Compose Homeworld storm and native Worthless counter costs with Semuta
This commit is contained in:
1 parent
506bf25bc6
commit
7004977e75
12 files changed
+776
-13
No files matched your search
@@ -0,0 +1,83 @@
|
||||
import assert from 'node:assert/strict';
|
||||
import { viewGame, type Action, type Game } from '../game/engine';
|
||||
import { semutaEmperorPlayer } from './fixture-semuta-emperor-share';
|
||||
import { applySemutaHomeworldChoamStep, holdSemutaHomeworldChoamCard } from './fixture-semuta-homeworld-choam';
|
||||
import {
|
||||
createSemutaHomeworldChoamMovementFixture,
|
||||
type SemutaHomeworldChoamMovementFixture,
|
||||
} from './fixture-semuta-homeworld-choam-movement';
|
||||
import {
|
||||
createSemutaHomeworldChoamReturnsFixture,
|
||||
type SemutaHomeworldChoamReturnsFixture,
|
||||
} from './fixture-semuta-homeworld-choam-returns';
|
||||
import type { RicheseSkillsTechStep } from './fixture-richese-skills-tech';
|
||||
|
||||
export type SemutaHomeworldChoamKaramaPower = 'kulon' | 'baliset' | 'laLaLa' | 'gamont';
|
||||
export type SemutaHomeworldChoamKaramaFixture = {
|
||||
setup: Game;
|
||||
beforeDeclaration: Game;
|
||||
/** Exact original declaration response, before Emperor spends its Karama. */
|
||||
game: Game;
|
||||
owner: string;
|
||||
holder: string;
|
||||
target: string;
|
||||
canceller: string;
|
||||
card: string;
|
||||
karama: string;
|
||||
power: SemutaHomeworldChoamKaramaPower;
|
||||
action: Action;
|
||||
cancelAction: Action;
|
||||
actions: RicheseSkillsTechStep[];
|
||||
movement?: SemutaHomeworldChoamMovementFixture;
|
||||
returns?: SemutaHomeworldChoamReturnsFixture;
|
||||
};
|
||||
|
||||
/** Genuine native prefix and printed declaration. Only conserved original
|
||||
* physical cards are positioned; the movement fixture already holds Karama. */
|
||||
export function createSemutaHomeworldChoamKaramaFixture(
|
||||
power: SemutaHomeworldChoamKaramaPower, advanced = false, fullHolder = false,
|
||||
): SemutaHomeworldChoamKaramaFixture {
|
||||
const movement = power === 'kulon' || power === 'baliset'
|
||||
? createSemutaHomeworldChoamMovementFixture(power, advanced, fullHolder) : undefined;
|
||||
const returns = power === 'laLaLa' || power === 'gamont'
|
||||
? createSemutaHomeworldChoamReturnsFixture(power, advanced) : undefined;
|
||||
const native = movement ?? returns;
|
||||
assert.ok(native);
|
||||
const beforeDeclaration = structuredClone(native.game);
|
||||
const canceller = movement?.canceller
|
||||
?? beforeDeclaration.players.find(player => player.faction === 'emperor')!.id;
|
||||
assert.equal(semutaEmperorPlayer(beforeDeclaration, canceller).faction, 'emperor');
|
||||
const held = semutaEmperorPlayer(beforeDeclaration, canceller).hand.find(card => card.effect === 'karama');
|
||||
const karama = held?.id ?? holdSemutaHomeworldChoamCard(beforeDeclaration, canceller, 'Karama');
|
||||
const actions = structuredClone(native.actions);
|
||||
const game = applySemutaHomeworldChoamStep(beforeDeclaration, native.owner, native.action, actions);
|
||||
assert.equal(game.response?.kind, 'choamWorthless');
|
||||
assert.equal(game.response!.owner, native.owner);
|
||||
assert.ok(viewGame(game, canceller).responseControls?.cancelCards.includes(karama));
|
||||
return { setup: native.setup, beforeDeclaration, game,
|
||||
owner: native.owner, holder: native.holder, target: native.target, canceller,
|
||||
card: native.card, karama, power, action: native.action,
|
||||
cancelAction: { type: 'card', mode: 'cancel', card: karama }, actions, movement, returns };
|
||||
}
|
||||
|
||||
export function cancelSemutaHomeworldChoamKarama(
|
||||
fixture: SemutaHomeworldChoamKaramaFixture, state = fixture.game, actions?: RicheseSkillsTechStep[],
|
||||
): Game {
|
||||
return applySemutaHomeworldChoamStep(state, fixture.canceller, fixture.cancelAction, actions);
|
||||
}
|
||||
|
||||
/** Singleton printed Karama only; pass every still-entitled neutral participant. */
|
||||
export function reactSemutaHomeworldChoamKarama(
|
||||
state: Game, holder: string, claim: boolean, actions?: RicheseSkillsTechStep[],
|
||||
): Game {
|
||||
const pending = state.pendingTreacheryDiscard;
|
||||
assert.ok(pending);
|
||||
const reaction = pending.reaction;
|
||||
if (reaction?.stage !== 'offer') throw Error('Expected the fresh native Karama discard offer');
|
||||
const event = pending.batch.event;
|
||||
if (claim) return applySemutaHomeworldChoamStep(state, holder, { type: 'semutaCommit', event }, actions);
|
||||
let game = state;
|
||||
for (const player of state.players.filter(player => !reaction.passed.includes(player.id)))
|
||||
game = applySemutaHomeworldChoamStep(game, player.id, { type: 'semutaPass', event }, actions);
|
||||
return game;
|
||||
}
|
||||
Reference in new issue
Block a user