Apply supplied Advanced Ixian Technology Richese-lot exclusion
This commit is contained in:
1 parent
0315917987
commit
4ed6beb33a
16 files changed
+40
-1356
No files matched your search
@@ -65,7 +65,6 @@ const decisions = {
|
||||
ixSetup: true,
|
||||
ixAuction: true,
|
||||
ixTechnology: true,
|
||||
ixRicheseTechnology: true,
|
||||
ixAllyCard: true,
|
||||
mobileStronghold: true,
|
||||
ixSubstitution: true,
|
||||
|
||||
@@ -1877,8 +1877,6 @@ function policyActions(g: GameView): Action[] {
|
||||
const card = cards.find((c) => c.kind === 'worthless') ?? cards[0];
|
||||
return card ? [{ type: 'decision', card: card.id }] : [];
|
||||
}
|
||||
if (d.kind === 'ixRicheseTechnology')
|
||||
return [{ type: 'decision', event: d.event, decline: true }];
|
||||
if (d.kind === 'richeseBlackMarket')
|
||||
return [
|
||||
{ type: 'decision', event: g.richeseBidding!.event, decline: true },
|
||||
|
||||
+1
-118
@@ -852,7 +852,6 @@ export type Decision =
|
||||
| { kind: 'ixSetup'; player: string }
|
||||
| { kind: 'ixAuction'; player: string }
|
||||
| { kind: 'ixTechnology'; player: string }
|
||||
| { kind: 'ixRicheseTechnology'; player: string; event: string; source: 'cache' | 'blackMarket' }
|
||||
| { kind: 'ixAllyCard'; player: string }
|
||||
| { kind: 'nexusIxianReplacement'; player: string; event: string }
|
||||
| { kind: 'mobileStronghold'; player: string; placement: boolean }
|
||||
@@ -1562,22 +1561,6 @@ export type Game = {
|
||||
richesePeekKnown?: boolean;
|
||||
richeseOfferedCard?: Card | null;
|
||||
richeseClaim?: string | null;
|
||||
/** Private pre-lot offer. The public Ixian choice contains no Black Market card identity. */
|
||||
pendingIxRicheseTechnology?: {
|
||||
event: string;
|
||||
player: string;
|
||||
owner: string;
|
||||
round: string;
|
||||
turn: number;
|
||||
source: 'cache' | 'blackMarket';
|
||||
card: Card;
|
||||
method: RicheseAuction['method'];
|
||||
direction: 'clockwise' | 'counterclockwise' | null;
|
||||
claim: string | null;
|
||||
frame: string;
|
||||
signature: string;
|
||||
};
|
||||
ixRicheseTechnologyEvent?: string;
|
||||
currentAuctionSale?: {
|
||||
occupiedEvent?: string;
|
||||
occupiedContributions?: { payer: string; amount: number }[];
|
||||
@@ -4314,8 +4297,6 @@ function savedTransferResponses(g: Game) {
|
||||
];
|
||||
}
|
||||
function transferCardBlock(g: Game, owner: Player, card: Card) {
|
||||
if (g.pendingIxRicheseTechnology?.owner === owner.id && g.pendingIxRicheseTechnology.card.id === card.id)
|
||||
return 'This card is reserved for the pending Richese lot and Ixian choice.';
|
||||
if (isPortableSnooper(card) && homeworldSavedDecisions(g).some((d) => d.kind === 'homeworldDefense' && d.player === owner.id))
|
||||
return 'Resolve the Homeworld late-defense choice before transferring its Portable Snooper.';
|
||||
|
||||
@@ -4844,8 +4825,6 @@ function draw(g: Game) {
|
||||
return g.deck.shift();
|
||||
}
|
||||
function discard(g: Game, p: Player, id: string, role?: Omit<EcazPoisonDiscard, 'card'>) {
|
||||
requireRule(g.pendingIxRicheseTechnology?.card.id !== id,
|
||||
'The card reserved for the pending Richese lot cannot be discarded.');
|
||||
requireRule(
|
||||
!g.pendingTreacheryDiscard,
|
||||
'Finish the committed discard continuation before another discard.',
|
||||
@@ -10943,7 +10922,7 @@ function richeseBetrayalSourcePreflight(g: Game, quote: Extract<RicheseSettlemen
|
||||
!g.auction && !g.currentAuctionSale && !g.response && !g.decision && !g.phaseOpening &&
|
||||
!g.truthtrance && !g.pendingKarama && !g.pendingNullentropy && !g.pendingTreacheryDiscard &&
|
||||
!g.pendingKull && !g.pendingChoamWorthless && !g.choamMarket && !g.pendingIxAlly &&
|
||||
!g.pendingRicheseGift && !g.pendingRichesePurchaseIncome && !g.pendingIxRicheseTechnology &&
|
||||
!g.pendingRicheseGift && !g.pendingRichesePurchaseIncome &&
|
||||
!g.biddingEnd && !g.bureaucratPayments?.pending &&
|
||||
!pendingNexusTraitors(g),
|
||||
'Richese Betrayal requires a clean ordinary Richese lot before payment, without another effect overlay.');
|
||||
@@ -11159,53 +11138,10 @@ function settleRicheseSoldLot(g: Game, quote: Extract<RicheseSettlementQuote,{ki
|
||||
g.decision = { kind: 'ixAllyCard', player: winner.id };
|
||||
} else continueAuctionSale(g, false);
|
||||
}
|
||||
function richeseOfferBlock(g: Game): string | null {
|
||||
const ixians = byFaction(g, 'ixians');
|
||||
return g.advanced && ixians?.hand.length && g.ixTechnologyTurn !== g.turn
|
||||
? 'Ixian Technology on Richese lots is awaiting a ruling on replacement-card custody.'
|
||||
: null;
|
||||
}
|
||||
function ixRicheseTechnologyFrame(g: Game): string {
|
||||
return JSON.stringify({ round: g.richeseBidding, order: g.order, positions: normalizedPlayerPositions(g) });
|
||||
}
|
||||
function ixRicheseTechnologySignature(pending: NonNullable<Game['pendingIxRicheseTechnology']>): string {
|
||||
return JSON.stringify({ ...pending, signature: undefined });
|
||||
}
|
||||
function ixRicheseTechnologyIntegrity(g: Game) {
|
||||
const pending = g.pendingIxRicheseTechnology;
|
||||
const decisions = homeworldSavedDecisions(g).filter(d => d.kind === 'ixRicheseTechnology');
|
||||
if (pending === undefined && g.ixRicheseTechnologyEvent === undefined && !decisions.length) return;
|
||||
const round = g.richeseBidding, ixians = byFaction(g, 'ixians'), owner = byFaction(g, 'richese');
|
||||
requireRule(pending && g.status === 'playing' && g.phase === 3 && g.advanced &&
|
||||
ixians && g.ixTechnologyTurn !== g.turn && owner && round &&
|
||||
pending.player === ixians.id && pending.owner === owner.id && pending.round === round.event &&
|
||||
pending.turn === g.turn && round.turn === g.turn && round.owner === owner.id &&
|
||||
typeof pending.event === 'string' && pending.event.length > 0 && pending.event === g.ixRicheseTechnologyEvent &&
|
||||
['cache', 'blackMarket'].includes(pending.source) &&
|
||||
round.stage === (pending.source === 'cache' ? 'cacheOffer' : 'blackMarketOffer') &&
|
||||
!g.auction && !g.richeseAuction && !g.ixAuction && !g.currentAuctionSale && !g.richeseOfferedCard &&
|
||||
pending.frame === ixRicheseTechnologyFrame(g) && pending.signature === ixRicheseTechnologySignature(pending),
|
||||
'The saved Ixian Richese-lot choice lost or changed its original offer, round or event.');
|
||||
requireRule(['normal', 'onceAround', 'silent'].includes(pending.method) &&
|
||||
(pending.source !== 'cache' || pending.method !== 'normal') &&
|
||||
(pending.direction === null || ['clockwise', 'counterclockwise'].includes(pending.direction)) &&
|
||||
(pending.method !== 'onceAround' || pending.direction !== null) &&
|
||||
(pending.source === 'cache' ? pending.claim === null : pending.claim === null ||
|
||||
typeof pending.claim === 'string' && pending.claim === pending.claim.trim() && pending.claim.length <= 300),
|
||||
'The saved Ixian Richese-lot choice has invalid auction terms.');
|
||||
const held = (pending.source === 'cache' ? g.richeseCache : owner.hand)?.filter(c => c.id === pending.card?.id);
|
||||
requireRule(held?.length === 1 && JSON.stringify(held[0]) === JSON.stringify(pending.card) &&
|
||||
physicalTreacheryCards(g).filter(c => c.id === pending.card.id).length === 1,
|
||||
'The pending Richese offer must retain its exact unique card in the original custody.');
|
||||
requireRule(decisions.length === 1 && decisions[0].player === pending.player && decisions[0].event === pending.event &&
|
||||
decisions[0].source === pending.source && Object.keys(decisions[0]).every(key => ['kind', 'player', 'event', 'source'].includes(key)),
|
||||
'The saved Ixian Richese-lot choice lost or duplicated its original decision.');
|
||||
}
|
||||
function beginRicheseLot(
|
||||
g: Game,
|
||||
source: 'cache' | 'blackMarket',
|
||||
action: Action,
|
||||
technologyDeclined = false,
|
||||
) {
|
||||
const round = g.richeseBidding!,
|
||||
owner = getPlayer(g, round.owner);
|
||||
@@ -11232,25 +11168,6 @@ function beginRicheseLot(
|
||||
action.method !== 'onceAround' || action.direction !== undefined,
|
||||
'Choose a physical bidding direction.',
|
||||
);
|
||||
if (richeseOfferBlock(g) && !technologyDeclined) {
|
||||
requireRule(!g.pendingIxRicheseTechnology && g.ixRicheseTechnologyEvent === undefined,
|
||||
'Finish the existing Ixian choice before declaring another Richese offer.');
|
||||
const event = crypto.randomUUID(), player = byFaction(g, 'ixians')!.id;
|
||||
const pending: NonNullable<Game['pendingIxRicheseTechnology']> = {
|
||||
event, player, owner: owner.id, round: round.event, turn: g.turn, source, card: structuredClone(card),
|
||||
method: action.method as RicheseAuction['method'],
|
||||
direction: (action.direction as 'clockwise' | 'counterclockwise' | undefined) ?? null,
|
||||
claim: source === 'blackMarket' && typeof action.claim === 'string' ? action.claim.trim().slice(0, 300) : null,
|
||||
frame: ixRicheseTechnologyFrame(g), signature: '',
|
||||
};
|
||||
pending.signature = ixRicheseTechnologySignature(pending);
|
||||
g.pendingIxRicheseTechnology = pending;
|
||||
g.ixRicheseTechnologyEvent = event;
|
||||
g.decision = { kind: 'ixRicheseTechnology', player, event, source };
|
||||
g.active = player;
|
||||
log(g, `${getPlayer(g, player).name} must choose whether to continue without exchanging this Richese ${source === 'cache' ? 'cache' : 'Black Market'} lot. The exchange is not yet implemented; Technology remains available for later lots.`);
|
||||
return;
|
||||
}
|
||||
const positions = normalizedPlayerPositions(g);
|
||||
let order = [...g.order];
|
||||
if (action.method === 'onceAround') {
|
||||
@@ -12157,8 +12074,6 @@ function karamaSpendingBlock(g: Game, p: Player, card: Card): string | null {
|
||||
(g.pendingKull.card === card.id || g.pendingChoamWorthless?.card === card.id ||
|
||||
g.pendingKull.worthless?.card === card.id))
|
||||
return 'This physical card is reserved for the interrupted Kull transaction.';
|
||||
if (g.pendingIxRicheseTechnology?.card.id === card.id)
|
||||
return 'This card is reserved for the pending Richese lot and Ixian choice.';
|
||||
if (g.battle?.lateDefense?.[p.id] === card.id)
|
||||
return 'This Portable Snooper is already played and reserved for battle cleanup.';
|
||||
if (giftReserved(g, p.id, card.id))
|
||||
@@ -26900,8 +26815,6 @@ export function prepareSpecialKaramaIntent(
|
||||
'Resolve the previous purchase income first.',
|
||||
);
|
||||
const acquire = stringField(action.acquire);
|
||||
requireRule(g.pendingIxRicheseTechnology?.card.id !== acquire,
|
||||
'The card reserved for the pending Richese lot cannot be acquired by another effect.');
|
||||
const chosen = g.richeseCache?.find((c) => c.id === acquire);
|
||||
requireRule(
|
||||
chosen && richeseCardDefinition(chosen),
|
||||
@@ -27097,11 +27010,6 @@ export function prepareSpecialKaramaIntent(
|
||||
g.pendingRicheseGift?.intent.owner !== target.id,
|
||||
'Resolve the reserved Richese gift before randomly exchanging that hand.',
|
||||
);
|
||||
requireRule(
|
||||
g.pendingIxRicheseTechnology?.source !== 'blackMarket' ||
|
||||
g.pendingIxRicheseTechnology.owner !== target.id,
|
||||
'Resolve the pending Black Market Ixian choice before randomly exchanging that hand.',
|
||||
);
|
||||
const count = integer(
|
||||
action.amount,
|
||||
1,
|
||||
@@ -27566,7 +27474,6 @@ export function applyAction(state: Game, id: string, action: Action): Game {
|
||||
harkonnenExchangeIntegrity(state);
|
||||
validateEcazLoyalty(state);
|
||||
ecazTreacheryIntegrity(state);
|
||||
ixRicheseTechnologyIntegrity(state);
|
||||
leaderSkillsIntegrity(state);
|
||||
moritaniExtortionIntegrity(state);
|
||||
guildRateIntegrity(state);
|
||||
@@ -27774,7 +27681,6 @@ export function applyAction(state: Game, id: string, action: Action): Game {
|
||||
homeworldSubstitutionIntegrity(g);
|
||||
homeworldDefenseIntegrity(g);
|
||||
homeworldShipmentIntegrity(g);
|
||||
ixRicheseTechnologyIntegrity(g);
|
||||
harkonnenExchangeIntegrity(g);
|
||||
moritaniExtortionIntegrity(g);
|
||||
guildRateIntegrity(g);
|
||||
@@ -27950,7 +27856,6 @@ export function normalizeAutomaticGame(state: Game): Game {
|
||||
harkonnenExchangeIntegrity(state);
|
||||
validateEcazLoyalty(state);
|
||||
ecazTreacheryIntegrity(state);
|
||||
ixRicheseTechnologyIntegrity(state);
|
||||
leaderSkillsIntegrity(state);
|
||||
moritaniExtortionIntegrity(state);
|
||||
guildRateIntegrity(state);
|
||||
@@ -28023,7 +27928,6 @@ export function normalizeAutomaticGame(state: Game): Game {
|
||||
homeworldSubstitutionIntegrity(g);
|
||||
homeworldDefenseIntegrity(g);
|
||||
homeworldShipmentIntegrity(g);
|
||||
ixRicheseTechnologyIntegrity(g);
|
||||
harkonnenExchangeIntegrity(g);
|
||||
moritaniExtortionIntegrity(g);
|
||||
guildRateIntegrity(g);
|
||||
@@ -28600,19 +28504,6 @@ function applyActionInner(
|
||||
return g;
|
||||
}
|
||||
if (decision.kind === 'bureaucratPayment') {actBureaucratPayment(g,action);return g;}
|
||||
if (decision.kind === 'ixRicheseTechnology') {
|
||||
const pending = g.pendingIxRicheseTechnology!;
|
||||
requireRule(action.event === pending.event && action.decline === true &&
|
||||
Object.keys(action).every(key => ['type', 'event', 'decline'].includes(key)),
|
||||
'Ixian exchange custody for Richese lots is not implemented. Explicitly continue without this exchange.');
|
||||
delete g.pendingIxRicheseTechnology;
|
||||
delete g.ixRicheseTechnologyEvent;
|
||||
log(g, `${p.name} continued without exchanging this Richese lot; Technology remains available for later lots.`);
|
||||
beginRicheseLot(g, pending.source, { type: 'decision', card: pending.card.id, method: pending.method,
|
||||
...(pending.direction !== null ? { direction: pending.direction } : {}),
|
||||
...(pending.claim !== null ? { claim: pending.claim } : {}) }, true);
|
||||
return g;
|
||||
}
|
||||
if (decision.kind === 'homeworldRevivalDeployment') {
|
||||
decideHomeworldRevivalReturn(g, p, action);
|
||||
return g;
|
||||
@@ -32005,7 +31896,6 @@ export function viewGame(state: Game, id: string) {
|
||||
harkonnenExchangeIntegrity(state);
|
||||
validateEcazLoyalty(state);
|
||||
ecazTreacheryIntegrity(state);
|
||||
ixRicheseTechnologyIntegrity(state);
|
||||
leaderSkillsIntegrity(state);
|
||||
moritaniExtortionIntegrity(state);
|
||||
guildRateIntegrity(state);
|
||||
@@ -33024,13 +32914,6 @@ export function viewGame(state: Game, id: string) {
|
||||
? { ecazSpecialKarama: ecazSpecialKaramaView(g, me) } : {}),
|
||||
...(g.moritaniSpecialKarama?.source.owner === me.id && g.moritaniSpecialKarama.stage === 'offer'
|
||||
? { moritaniSpecialKarama: currentMoritaniSpecialKaramaQuote(g) } : {}),
|
||||
ixRicheseTechnology: g.pendingIxRicheseTechnology ? {
|
||||
event: g.pendingIxRicheseTechnology.event,
|
||||
player: g.pendingIxRicheseTechnology.player,
|
||||
owner: g.pendingIxRicheseTechnology.owner,
|
||||
source: g.pendingIxRicheseTechnology.source,
|
||||
exchangeBlocked: 'Ixian exchange custody for Richese lots is not implemented. You can explicitly continue without this exchange; Technology remains available for later lots.',
|
||||
} : null,
|
||||
richeseBidding:
|
||||
g.richeseBidding?.turn === g.turn
|
||||
? {
|
||||
|
||||
@@ -12,7 +12,7 @@ export function nexusCleanPlayBlocked(g: Game, automaticPending = false): boolea
|
||||
g.pendingChoamWorthless || g.pendingChoamBattleIncome ||
|
||||
g.pendingChoamMarketGhola || g.pendingAuditor || g.pendingFaceDance ||
|
||||
g.pendingTech || g.pendingIxTechnology || g.pendingIxAlly ||
|
||||
g.pendingIxSubstitution || g.pendingIxRicheseTechnology ||
|
||||
g.pendingIxSubstitution ||
|
||||
g.pendingWinnerDiscards || g.pendingSukRescue || g.ornithopter ||
|
||||
g.summonedWorm || g.battle || g.auction || g.richeseAuction ||
|
||||
g.choamMarket || g.nexusTraitorPending ||
|
||||
|
||||
+7
-7
@@ -4634,7 +4634,7 @@ export const RULE_TOPICS: RuleTopic[] = [
|
||||
'A selected surviving Planetologist may use a canonical printed green Richese Special or Special - Movement in its weapon slot for two strength. Exact physical identity is required. Stone Burner, Mirror Weapon and Portable Snooper do not qualify. The green card is discarded once without its native effect, fee, search or early leader kill.',
|
||||
'The normal Smuggler own companion is one separate force alongside the marker, not added to its hidden value. Ordinary allied No-Field cancellation and Guild stages retain their exact owner/recipient/token frame; an allied free companion is not granted.',
|
||||
'The authorized Advanced recipient cutover keeps Richese ally contributions distinct: buyer four goes to Richese, funded Richese four goes to Emperor or bank. This also applies when Emperor is the buyer. Cancellation redirects only the original Emperor share, not unrelated buyer credit, with no second donor debit or card transfer. Basic keeps its publisher receipt.',
|
||||
'Separate normal Banker income uses only actual bank-routed payer legs and remains unavailable until actual Mentat. Allowed Emperor income and ordinary seller credit are not bank payments. Native Ixian Technology on Richese lots, exhausted-cache arithmetic, positive Black Market self-bidding, Bureaucrat split funding and other module/ruling boundaries remain explicit.',
|
||||
'Separate normal Banker income uses only actual bank-routed payer legs and remains unavailable until actual Mentat. Allowed Emperor income and ordinary seller credit are not bank payments. Supplied Advanced p34 expressly excludes Richese auctions from Ixian Technology, so no per-lot exchange or decline exists; exhausted-cache arithmetic, positive Black Market self-bidding, Bureaucrat split funding and other module boundaries remain explicit.',
|
||||
],
|
||||
related: ['leader-skills', 'richese-cards', 'richese-no-field', 'spice-banker-income', 'implementation-checklist'],
|
||||
checklist: [
|
||||
@@ -4709,7 +4709,7 @@ export const RULE_TOPICS: RuleTopic[] = [
|
||||
steps: [
|
||||
'Fresh all14 Leader Skills preserves original classic, supported E1/E2 or standalone Ecaz OR Moritani setup with Homeworlds/Discovery/Tech3+/Advanced Strongholds under each envelope. Explicit --nexus-cards additionally admits single/paired one-family E1/E2, BOTH selected Ix+CHOAM native families/union47 and standalone OR paired Ecaz/Moritani/ecaz33 with classics. Basic/Advanced2–6, original Ix pre-training offer, ordinary native discs and actual hands remain; Auditor/shared Duke never train. Winning normal/trained Suk uses source-labelled native, visitor or nested fixed-loss pools; native saved counters stay at that home and external Emperor returns allocate original homes. Original phase-end industry, held benefits, cleanup/Tech before Face Dance and native assassination remain; unreconciled bands, mixed E3/three-family, public starts and played-save conversion stay separate.',
|
||||
'Basic Moritani retains base opponents including Harkonnen. Its separate bounded Advanced classic/non-Harkonnen roster now composes the original assassination preview and all fourteen skills: winner Suk rescue and Rihani choice finish first, then an eligible different trained disc may be assassinated with its exact card return and printed bounty. No casualties repeat; original set-aside and one private Mentat replacement remain. See Moritani with Leader Skills for the exact scope.',
|
||||
'Fresh Advanced profiles compose native Ixians, Tleilaxu, CHOAM and/or Richese with classic opponents and distinct required family decks. All fourteen skills and native starts remain. Auditor stays unskilled; cyborg casualties, original CHOAM support and Richese capped marker revelation remain physical. Foreign revival creates no new skill. Native Ixian Technology on Richese lots, own mixed No-Field, captured replacement, Advanced Atreides Suk/KH and unrelated overlays retain their guards.',
|
||||
'Fresh Advanced profiles compose native Ixians, Tleilaxu, CHOAM and/or Richese with classic opponents and distinct required family decks. All fourteen skills and native starts remain. Auditor stays unskilled; cyborg casualties, original CHOAM support and Richese capped marker revelation remain physical. Foreign revival creates no new skill. Supplied Advanced p34 excludes Richese auctions from Ixian Technology; own mixed No-Field, captured replacement, Advanced Atreides Suk/KH and unrelated overlays retain their guards.',
|
||||
'Fresh three-through-six-seat Basic/Advanced development tables may compose all fourteen Skills with original Tech Tokens: classic factions, supported E1/E2 native families, or standalone Ecaz/Moritani with the exact ecaz deck and original roster exclusions. Starting cards/cache, skill/Traitor choices and printed/first-Storm assignment stay genuine. Existing leader-skills/banker-income entry preserves unused selected tokens; mixed E3 families, other overlays, public activation and saved-game conversion remain separate.',
|
||||
'Fresh Advanced classic or supported native two-through-six-seat skill tables may retain six original Stronghold Cards, with Tech at three or more seats. Original end-Mentat/local held effects remain. Arrakeen changes actual payer cost, not rescue or Banker eligibility; native CHOAM income retains bank support. Physical defenses, normal disciplines, holder ties and original dial/Worthless earnings remain. Standalone Ecaz Duke and Moritani assassination compose original rules; mixed E3, allied Occupy skills, copied Diplomat defenses, active skill/Stone and other guards stay separate.',
|
||||
'Fresh classic Leader Skills may explicitly add Nexus through local leader-skills --nexus-cards, Basic/Advanced two through six players, retaining selected original Tech at three or more seats and/or Advanced Strongholds. --discoveries additionally composes original Discovery7/8 in this classic profile. Saved hands/offers remain authoritative; the actual end-Spice closing draw follows Maker votes/typed rides, both Advanced piles and settled alliance. Free-return ledgers, bank purchase and Traitor Deck declarations stay distinct from training. Original phase-end industry, held payer support/physical defenses, physical Suk rescue and subsequent mandatory winner Tech rewards retain their own timing. Homeworlds, Banker/Mentat previews, borrowed Smuggler arithmetic and unresolved interactions remain separate.',
|
||||
@@ -4829,18 +4829,18 @@ export const RULE_TOPICS: RuleTopic[] = [
|
||||
'Ecaz places six starting forces in Imperial Basin, allocated among sectors 9, 10 and 11, and keeps fourteen in reserve. Finish Fremen placement first; Bene Gesserit then places its Advanced starting advisor. Moritani places its six forces in an unoccupied territory after all other setup is complete.',
|
||||
'Ixians keep their private starting-card selection and physical cyborg/suboid forces. Tleilaxu receives three private Face Dancers. Advanced CHOAM receives its Auditor. Richese has a separate ten-card cache and three private No-Fields. Ecaz and Moritani receive their Ambassador and Terror inventories and share one Duke Vidal disc.',
|
||||
'Without Ix, CHOAM adds Poison Tooth and Artillery Strike to the 33-card ordinary deck; Ix or combined decks contain47. Ecaz faction selection adds no cards. Explicit independent variant adds Recruits/Harass/Reinforcements for36/50/38, with classic/paired controls and a separately verified native Advanced Occupy combined-army path.',
|
||||
'Advanced Ixians may explicitly decline Technology for one Richese special lot. Their once-per-round use remains available later. The special-lot exchange itself is unfinished pending replacement-card custody interpretation.',
|
||||
'Supplied Advanced p34 expressly excludes Richese auctions from Ixian Technology, so a Richese special lot begins directly with no Ixian exchange or decline prompt; the once-per-round Technology use remains available for ordinary lots.',
|
||||
'Fresh unsupported movement arrivals still reject before opening prevention choices or reserving a new movement card. A saved CHOAM allow choice can return an uncommitted unsupported move for another choice, preserving its existing reserved Ornithopter. The bounded Ecaz/Moritani same-entry Ambassador/Terror overlap instead snapshots both eligible owners in storm order without replaying the original move.',
|
||||
'Ordinary shipment checks unfinished arrival combinations before saving the Guild interception choice. An already saved ordinary shipment may return unspent when its still-gated arrival cannot commit; older No-Field, Smuggler, Nexus and Ambassador shipments retain their separate recovery boundaries. Classic plus Ecaz/Moritani Ambassador/Terror overlap has a provisional saved storm-order sequence, while other simultaneous priorities remain unfinished.',
|
||||
'Remaining abilities and combined timing can still encounter explicit development guards. Setup and saved-continuation evidence does not certify complete expansion play.',
|
||||
],
|
||||
related: ['setup', 'ix-modules', 'choam-modules', 'ecaz-modules', 'implementation-checklist'],
|
||||
checklist: [
|
||||
{ area: 'Implementation', status: 'Partial', detail: 'Genuine setup for seven expansion selections reuses existing faction effects and corrects Ecaz starting placement. Saved per-lot Ixian decline resumes Richese bidding without changing card custody or consuming Technology. Optional variants and unfinished interactions remain separate.', evidence: ['game/engine.ts', 'game/cards.ts', 'game/ecaz-setup.ts', 'tools/prototype-room.ts'] },
|
||||
{ area: 'Player controls', status: 'Partial', detail: 'Existing faction choices and private hands are reused. Ecaz has an owned sector-allocation control and setup waiting messages; Ixians have an explicit per-lot decline control. Public starts and full acceptance remain gated.', evidence: ['components/ecaz-setup.tsx', 'components/ix-richese-technology.tsx', 'components/game-table.tsx'] },
|
||||
{ area: 'AI', status: 'Partial', detail: 'All four profiles use genuine setup choices and the shared Ecaz allocation quote, and can decline this Richese lot without using Technology. Combined gameplay strategy, guarded interactions and full-game reliability remain unfinished.', evidence: ['game/bots.ts', 'game/ecaz-setup.ts'] },
|
||||
{ area: 'Implementation', status: 'Partial', detail: 'Genuine setup for seven expansion selections reuses existing faction effects and corrects Ecaz starting placement. Supplied Advanced p34 excludes Richese auctions from Ixian Technology, so a Richese special lot begins directly. Optional variants and unfinished interactions remain separate.', evidence: ['game/engine.ts', 'game/cards.ts', 'game/ecaz-setup.ts', 'tools/prototype-room.ts'] },
|
||||
{ area: 'Player controls', status: 'Partial', detail: 'Existing faction choices and private hands are reused. Ecaz has an owned sector-allocation control and setup waiting messages; Richese special lots open directly with no Ixian prompt. Public starts and full acceptance remain gated.', evidence: ['components/ecaz-setup.tsx', 'components/game-table.tsx'] },
|
||||
{ area: 'AI', status: 'Partial', detail: 'All four profiles use genuine setup choices and the shared Ecaz allocation quote; no Ixian Richese-lot decision exists to answer. Combined gameplay strategy, guarded interactions and full-game reliability remain unfinished.', evidence: ['game/bots.ts', 'game/ecaz-setup.ts'] },
|
||||
{ area: 'Documentation', status: 'Partial', detail: 'The faction profile records ordinary deck composition, independent variants, Ecaz territory allocation and deterministic setup-order interpretations, with unfinished play explicit.', evidence: ['docs/EXPANSION_FACTIONS_PROTOTYPE.md'] },
|
||||
{ area: 'Verification', status: 'Partial', detail: 'Setup, inventory, private views, JSON/SQLite continuation, concurrent start and dirty-lobby rejection have focused checks. Ixian per-lot decline has ownership, stale/corrupt decision, private card custody and concurrent recovery checks. Sample gameplay and browser evidence are recorded separately, with remaining guards retained. Deferred movement and ordinary shipment checks cover arrival preflight, retained resources and saved concurrent recovery; special shipment receipts keep their separate guards.', evidence: ['tests/deferred-movement-arrival.test.ts', 'tests/deferred-movement-arrival-recovery.test.ts', 'tests/deferred-shipment-arrival.test.ts', 'tests/deferred-shipment-arrival-recovery.test.ts', 'docs/DEFERRED_SHIPMENT_ARRIVAL.md', 'tools/faction-games.ts', 'tests/expansion-factions-prototype.test.ts', 'tests/expansion-factions-recovery.test.ts', 'tests/ecaz-setup-controls.test.tsx', 'tests/ecaz-cards.test.ts', 'tests/ix-richese-technology.test.ts', 'tests/ix-richese-technology-controls.test.tsx', 'tests/ix-richese-technology-recovery.test.ts'] },
|
||||
{ area: 'Verification', status: 'Partial', detail: 'Setup, inventory, private views, JSON/SQLite continuation, concurrent start and dirty-lobby rejection have focused checks. Sample gameplay and browser evidence are recorded separately, with remaining guards retained. Deferred movement and ordinary shipment checks cover arrival preflight, retained resources and saved concurrent recovery; special shipment receipts keep their separate guards.', evidence: ['tests/deferred-movement-arrival.test.ts', 'tests/deferred-movement-arrival-recovery.test.ts', 'tests/deferred-shipment-arrival.test.ts', 'tests/deferred-shipment-arrival-recovery.test.ts', 'docs/DEFERRED_SHIPMENT_ARRIVAL.md', 'tools/faction-games.ts', 'tests/expansion-factions-prototype.test.ts', 'tests/expansion-factions-recovery.test.ts', 'tests/ecaz-setup-controls.test.tsx', 'tests/ecaz-cards.test.ts'] },
|
||||
],
|
||||
},
|
||||
...[
|
||||
|
||||
@@ -51,7 +51,6 @@ const DECISIONS = {
|
||||
ixSetup: true,
|
||||
ixAuction: true,
|
||||
ixTechnology: true,
|
||||
ixRicheseTechnology: true,
|
||||
ixAllyCard: true,
|
||||
mobileStronghold: true,
|
||||
ixSubstitution: true,
|
||||
|
||||
Reference in new issue
Block a user