From d63efccb55723c8ff33d9ec0d9daf037d0bd66ca Mon Sep 17 00:00:00 2001 From: Quintine <15965533+Quintine@users.noreply.github.com> Date: Sat, 24 Oct 2026 18:03:08 +1100 Subject: [PATCH] Settle CHOAM charity claims after depleted funding Keep CHOAM's own poverty claim bank-funded. Prototype full other-faction claims using available CHOAM cash and an explicitly provisional bank residual; retain original eligibility, cancellation, Homeworld portions and claim limits. Connect persistent controls and genuine public setup/bribe evidence. Complete rules and deployed acceptance remain open. --- components/charity-claim.tsx | 53 +++++++ components/game-table.tsx | 31 +--- docs/CHOAM_CHARITY_FUNDING.md | 101 ++++++++++++ docs/CURRENT_STATUS.md | 19 +++ docs/IMPLEMENTATION_STATUS.md | 25 +++ docs/RULE_DECISIONS.md | 12 ++ game/charity.ts | 22 ++- game/engine.ts | 24 +-- game/reference.ts | 60 ++++++- tests/charity-claim-controls.test.tsx | 176 +++++++++++++++++++++ tests/charity-funding-quote.test.ts | 40 +++++ tests/choam-charity-funding-engine.test.ts | 55 +++++++ tests/choam-charity.test.ts | 48 +++++- tests/fixture-choam-charity-funding.ts | 169 ++++++++++++++++++++ tests/homeworld-charity-engine.test.ts | 21 ++- 15 files changed, 798 insertions(+), 58 deletions(-) create mode 100644 components/charity-claim.tsx create mode 100644 docs/CHOAM_CHARITY_FUNDING.md create mode 100644 tests/charity-claim-controls.test.tsx create mode 100644 tests/charity-funding-quote.test.ts create mode 100644 tests/choam-charity-funding-engine.test.ts create mode 100644 tests/fixture-choam-charity-funding.ts diff --git a/components/charity-claim.tsx b/components/charity-claim.tsx new file mode 100644 index 00000000..99f23b85 --- /dev/null +++ b/components/charity-claim.tsx @@ -0,0 +1,53 @@ +'use client'; + +import { ArrowRight } from 'lucide-react'; +import { Button } from '@/components/ui/button'; +import type { Action, GameView } from '@/game/engine'; + +export function CharityClaim({ + game, + act, + busy, +}: { + game: GameView; + act: (action: Action) => void; + busy: boolean; +}) { + const me = game.players.find((player) => player.id === game.me); + if (game.status !== 'playing' || game.phase !== 2 || !me) return null; + const disabled = busy || !!game.truthtrance || !!game.nexusTraitors?.pending || + !!me.charityClaimed || game.charity.incomePending || game.charity.amount === 0; + return ( + <> +

+ {game.charity.multiplier === 0 + ? 'Inflation cancels charity for every faction this turn.' + : game.charity.multiplier === 2 + ? 'Inflation doubles the amount of charity you receive. Ordinary eligibility still requires fewer than two spice; advanced Bene Gesserit receives four regardless of wealth.' + : game.advanced && me.faction === 'beneGesserit' + ? 'Receive two spice once per turn. If you already hold two or more, opponents may cancel the faction benefit with Karama.' + : 'If you have fewer than two spice, claim charity once to reach two.'} +

+

+ {game.charity.payer + ? `The ordinary claim is ${game.charity.ordinary} spice. CHOAM pays as much of the ordinary claim as it can; the Spice Bank supplies any shortfall. This funding rule is provisional.` + : 'Charity is paid by the Spice Bank.'} + {game.charity.homeworld > 0 && + ` Low Homeworld population adds ${game.charity.homeworld} spice directly from the bank${game.charity.multiplier === 2 ? ' after Inflation doubles the bonus' : ''}.`} +

+ + + ); +} diff --git a/components/game-table.tsx b/components/game-table.tsx index d28d51a7..deba4c8f 100644 --- a/components/game-table.tsx +++ b/components/game-table.tsx @@ -51,6 +51,7 @@ import { NexusAdvisors } from './nexus-advisors'; import { NexusSardaukar } from './nexus-sardaukar'; import { TerrorBoardMarkers } from './terror-board-markers'; import { AmbassadorBoardMarkers } from './ambassador-board-markers'; +import { CharityClaim } from './charity-claim'; import { homeworldRevivalActionBlock } from '@/game/homeworld-revival-deployment-options'; import { GuildHomeworldShipment } from './guild-homeworld-shipment'; import { JunctionTransport } from './junction-transport'; @@ -4204,35 +4205,7 @@ export function GameTable({ Ready the table to reveal the next spice blow.

)} - {g.phase === 2 && ( - <> -

- {g.charity.multiplier === 0 - ? 'Inflation cancels charity for every faction this turn.' - : g.charity.multiplier === 2 - ? 'Inflation doubles the amount of charity you receive. Ordinary eligibility still requires fewer than two spice; advanced Bene Gesserit receives four regardless of wealth.' - : g.advanced && me.faction === 'beneGesserit' - ? 'Receive two spice once per turn. If you already hold two or more, opponents may cancel the faction benefit with Karama.' - : 'If you have fewer than two spice, claim charity once to reach two.'} -

-

- {g.charity.payer - ? `The ordinary ${g.charity.ordinary} spice is paid from ${g.players.find((p) => p.id === g.charity.payer)?.name}’s spice.` - : 'Charity is paid by the Spice Bank.'} - {g.charity.homeworld > 0 && - ` Low Homeworld population adds ${g.charity.homeworld} spice directly from the bank${g.charity.multiplier === 2 ? ' after Inflation doubles the bonus' : ''}.`} -

- {actionButton( - g.charity.amount > 0 - ? `Claim ${g.charity.amount} charity spice` - : 'Claim charity', - { type: 'charity' }, - me.charityClaimed || - g.charity.incomePending || - g.charity.amount === 0, - )} - - )} + {g.phase === 2 && } {g.phase === 3 && g.richeseAuction && (