Prototype one through three own spice as a separate sealed battle commitment. Reserve funds alongside actual force support, add strength only for the surviving skilled leader, and settle once through existing versioned writes. Apply the documented card/core-rule interpretation: payment survives leader death or loss, except for the sole successful traitor caller. Keep printed disc strength and bounty unchanged. Normal income awaits its deferred-custody ruling; combined modules remain gated. Validation: 54 focused tests; typecheck, lint, 4490 offline tests, production build and 43 HTTP tests passed. Independent rules, privacy, persistence and UI review completed. SQLite tests cover reserved-spice diversion, concurrent settlement, traitor waiver and legacy plans. Browser exercised a three-spice commitment, pending refresh, actual +3 strength and payment, and restored private cards and the next decision. Two genuine four-profile Basic/Advanced samples completed without rejected candidates but did not use Banker spending. Following another reported outage, the server remained healthy. Preserved all 461 opening rooms and original seat/recovery rows (495 rooms after QA/HTTP). SQLite integrity and foreign keys passed. A fresh online backup captured all 495 rooms, and browser refresh restored the QA seat. No restart or reset was needed. Verified source tree: 208bf9324ab994b5e59ed50d78998b4dbf8810b3b6799b1a577ef3b84479335f
73 lines
1.9 KiB
TypeScript
73 lines
1.9 KiB
TypeScript
import {
|
|
usesSurvivingSkilledLeader,
|
|
type BattleLeaderSkill,
|
|
} from './leader-skill-combat';
|
|
|
|
export class SpiceBankerError extends Error {}
|
|
|
|
/** The same development boundary applies to declarations, saved plans, controls and bots. */
|
|
export function spiceBankerModeSupported(game: {
|
|
expansions: readonly string[];
|
|
homeworlds?: unknown;
|
|
nexusCards?: unknown;
|
|
discoveries?: unknown;
|
|
discoveryEnabled?: boolean;
|
|
strongholdCards?: unknown;
|
|
techTokens?: unknown;
|
|
}): boolean {
|
|
return (
|
|
!game.expansions.length &&
|
|
!game.homeworlds &&
|
|
!game.nexusCards &&
|
|
!game.discoveries &&
|
|
!game.discoveryEnabled &&
|
|
!game.strongholdCards &&
|
|
!game.techTokens
|
|
);
|
|
}
|
|
|
|
export function spiceBankerBattleMaximum(
|
|
assignments: readonly BattleLeaderSkill[],
|
|
leader: string | null | undefined,
|
|
ownSpice: number,
|
|
ownSupport = 0,
|
|
): number {
|
|
if (!usesSurvivingSkilledLeader(assignments, 'spice-banker', leader, true))
|
|
return 0;
|
|
if (
|
|
!Number.isSafeInteger(ownSpice) ||
|
|
!Number.isSafeInteger(ownSupport) ||
|
|
ownSpice < 0 ||
|
|
ownSupport < 0
|
|
)
|
|
return 0;
|
|
return Math.max(0, Math.min(3, ownSpice - ownSupport));
|
|
}
|
|
|
|
/** Banker funds are separate from force support and remain behind the shield until settlement. */
|
|
export function validateSpiceBankerSpend(
|
|
assignments: readonly BattleLeaderSkill[],
|
|
leader: string | null | undefined,
|
|
amount: unknown,
|
|
ownSpice: number,
|
|
ownSupport = 0,
|
|
): number {
|
|
const spend = amount === undefined ? 0 : amount;
|
|
if (
|
|
typeof spend !== 'number' ||
|
|
!Number.isSafeInteger(spend) ||
|
|
spend < 0 ||
|
|
spend > 3
|
|
)
|
|
throw new SpiceBankerError(
|
|
'Choose zero to decline, or one through three spice for Spice Banker.',
|
|
);
|
|
if (
|
|
spend > spiceBankerBattleMaximum(assignments, leader, ownSpice, ownSupport)
|
|
)
|
|
throw new SpiceBankerError(
|
|
'Spice Banker needs the selected skilled leader and enough own spice after force support.',
|
|
);
|
|
return spend;
|
|
}
|