Compose selected native World independent cards
This commit is contained in:
1 parent
f5b7ebc8da
commit
cda9338f13
18 files changed
+1292
-40
No files matched your search
@@ -0,0 +1,97 @@
|
||||
import assert from 'node:assert/strict';
|
||||
import { viewGame, type Game } from '../game/engine';
|
||||
import { forceInventory } from '../game/force-inventory';
|
||||
import type { NativeReserveSelections } from '../game/homeworld-native-reserves';
|
||||
import {
|
||||
createIndependentCardModulesFixture, type IndependentCardModulesStep,
|
||||
} from './fixture-independent-card-modules';
|
||||
import {
|
||||
createRicheseReinforcementsFixture, RICHESE_HARASS, RICHESE_REINFORCEMENTS,
|
||||
type RicheseReinforcementsFixture, type RicheseReinforcementsOptions,
|
||||
type RicheseReinforcementsStep,
|
||||
} from './fixture-richese-reinforcements';
|
||||
|
||||
export type NativeWorldMarkerOptions = Omit<RicheseReinforcementsOptions, 'initial' | 'seatIds'>;
|
||||
export type NativeWorldMarkerFixture = RicheseReinforcementsFixture & {
|
||||
/** Ready but undealt: the original initializer seam for controls/policies. */
|
||||
readyLobby: Game;
|
||||
readySteps: IndependentCardModulesStep[];
|
||||
actors: { owner: string; opponent: string; observer: string };
|
||||
source: {
|
||||
homeworld: string;
|
||||
reservesBeforeReveal: number;
|
||||
materializes: number;
|
||||
reservesAfterReveal: number;
|
||||
feeChoices: NativeReserveSelections[] | undefined;
|
||||
};
|
||||
};
|
||||
|
||||
/** Genuine selected-family World programme. Only the original initializer's
|
||||
* randomness is scoped. Basic low budgets come from two reveals, real Spice
|
||||
* Blow collection and an ordinary paid shipment over four turns. Advanced uses
|
||||
* its funded turn-one route, never a staged low budget or frozen later Storm.
|
||||
* The inherited staging log explicitly labels conserved physical card moves;
|
||||
* none of those moves is represented as a natural purchase. */
|
||||
export function createNativeWorldMarkerFixture(
|
||||
options: NativeWorldMarkerOptions = {},
|
||||
): NativeWorldMarkerFixture {
|
||||
const family = options.family ?? 'choam';
|
||||
const selected = createIndependentCardModulesFixture({
|
||||
advanced: options.advanced ?? false,
|
||||
homeworlds: true,
|
||||
tech: false,
|
||||
strongholds: false,
|
||||
expansions: family === 'choam' ? ['choam'] : ['ix', 'choam'],
|
||||
factions: family === 'choam' ? ['richese', 'guild', 'atreides'] : ['richese', 'ixians', 'atreides'],
|
||||
});
|
||||
const readyLobby = selected.initial;
|
||||
const fixture = createRicheseReinforcementsFixture({ ...options, family, initial: readyLobby });
|
||||
const view = viewGame(fixture.game, fixture.richese);
|
||||
const homeworld = view.homeworlds?.worlds?.find(home => home.native === fixture.richese && !home.secondary);
|
||||
const preview = view.battle!.reinforcements!;
|
||||
assert.ok(homeworld && preview.noField);
|
||||
return {
|
||||
...fixture,
|
||||
readyLobby,
|
||||
readySteps: selected.steps.filter(step => step.action.type === 'ready'),
|
||||
actors: { owner: fixture.richese, opponent: fixture.opponent, observer: fixture.observer },
|
||||
source: {
|
||||
homeworld: homeworld.id,
|
||||
reservesBeforeReveal: homeworld.forces[fixture.richese].normal,
|
||||
materializes: preview.noField.materializes,
|
||||
reservesAfterReveal: preview.noField.reservesAfterReveal,
|
||||
feeChoices: structuredClone(preview.homeworldSources),
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
/** Original public inventory counts native Worlds within reserves exactly once,
|
||||
* keeps foreign visitors separate and preserves Ixian elite identity. */
|
||||
export function nativeWorldMarkerInventory(game: Game, actor: string) {
|
||||
const view = viewGame(game, actor);
|
||||
const player = view.players.find(seat => seat.id === actor);
|
||||
const worlds = view.homeworlds?.worlds;
|
||||
assert.ok(player && worlds);
|
||||
const inventory = forceInventory(player, worlds);
|
||||
assert.ok(inventory, `Original native force inventory is inconsistent for ${actor}`);
|
||||
return inventory;
|
||||
}
|
||||
|
||||
/** Both independent machines may occupy either actual battle-plan slot. For a
|
||||
* combined plan slot selects R's slot; H occupies the other. The fee allocation
|
||||
* is the owner's real preview quote, never an inferred native source split. */
|
||||
export function nativeWorldMarkerPlan(fixture: NativeWorldMarkerFixture,
|
||||
mode: 'reinforcements' | 'both' | 'harass' | 'plain' = 'reinforcements',
|
||||
slot: 'weapon' | 'defense' = 'weapon'): RicheseReinforcementsStep {
|
||||
const step = structuredClone(fixture.plans.plain);
|
||||
step.action.weapon = null;
|
||||
step.action.defense = null;
|
||||
if (mode === 'reinforcements' || mode === 'both') {
|
||||
step.action[slot] = RICHESE_REINFORCEMENTS;
|
||||
const sources = fixture.source.feeChoices?.[0];
|
||||
if (sources) step.action.reinforcementsSources = structuredClone(sources);
|
||||
}
|
||||
if (mode === 'harass') step.action[slot] = RICHESE_HARASS;
|
||||
if (mode === 'both') step.action[slot === 'weapon' ? 'defense' : 'weapon'] = RICHESE_HARASS;
|
||||
return step;
|
||||
}
|
||||
Reference in new issue
Block a user