Offer Semuta after clean private Distrans transfer
This commit is contained in:
1 parent
2ec0422b79
commit
162f01c160
9 files changed
+243
-40
No files matched your search
+67
-2
@@ -1134,6 +1134,15 @@ export type Game = {
|
||||
resume: NonNullable<Game['pendingNullentropy']>['resume'];
|
||||
stateSignature: string;
|
||||
}
|
||||
| {
|
||||
kind: 'distransDiscard';
|
||||
owner: string;
|
||||
recipient: string;
|
||||
transferred: string;
|
||||
card: string;
|
||||
resume: NonNullable<Game['pendingNullentropy']>['resume'];
|
||||
stateSignature: string;
|
||||
}
|
||||
| {
|
||||
kind: 'truthtranceDiscard';
|
||||
consumed: TruthQueueEntry;
|
||||
@@ -4658,6 +4667,20 @@ function stageOrdinaryCardDiscard(g: Game, p: Player, card: Card) {
|
||||
next,
|
||||
);
|
||||
}
|
||||
type DistransDiscardContinuation = Extract<
|
||||
NonNullable<Game['pendingTreacheryDiscard']>['continuation'],
|
||||
{ kind: 'distransDiscard' }
|
||||
>;
|
||||
function distransDiscardSignature(g: Game, c: DistransDiscardContinuation) {
|
||||
const owner = getPlayer(g, c.owner), recipient = getPlayer(g, c.recipient);
|
||||
return JSON.stringify({
|
||||
owner: c.owner, recipient: c.recipient, transferred: c.transferred,
|
||||
card: c.card, parent: nullentropyParentSignature(g, c.resume, `distrans:${c.card}`),
|
||||
turn: g.turn, phase: g.phase, active: g.active,
|
||||
ownerHand: owner.hand.map(card => card.id),
|
||||
recipientHand: recipient.hand.map(card => card.id),
|
||||
});
|
||||
}
|
||||
type TruthDiscardContinuation = Extract<
|
||||
NonNullable<Game['pendingTreacheryDiscard']>['continuation'],
|
||||
{ kind: 'truthtranceDiscard' }
|
||||
@@ -5053,6 +5076,25 @@ function treacheryDiscardIntegrity(g: Game) {
|
||||
: true,
|
||||
'The completed ordinary-card outcome has changed.',
|
||||
);
|
||||
} else if (continuation?.kind === 'distransDiscard') {
|
||||
const c = continuation, entry = batch.entries[0];
|
||||
const owner = g.players.find(p => p.id === c.owner);
|
||||
const recipient = g.players.find(p => p.id === c.recipient);
|
||||
requireRule(
|
||||
owner && recipient && owner !== recipient &&
|
||||
batch.cause === 'distrans' && batch.entries.length === 1 &&
|
||||
entry.publicFace && entry.discardedBy === c.owner &&
|
||||
entry.card.id === c.card &&
|
||||
richeseCardDefinition(entry.card)?.card.effect === 'distrans' &&
|
||||
recipient.hand.filter(card => card.id === c.transferred).length === 1 &&
|
||||
!owner.hand.some(card => card.id === c.transferred || card.id === c.card) &&
|
||||
outside.filter(card => card.id === c.transferred).length === 1 &&
|
||||
!g.discard.some(card => card.id === c.transferred) &&
|
||||
!c.resume.response && !c.resume.decision &&
|
||||
!c.resume.pendingKarama && !c.resume.phaseOpening &&
|
||||
c.stateSignature === distransDiscardSignature(g, c),
|
||||
'The completed private Distrans transfer no longer matches its fresh discard.',
|
||||
);
|
||||
} else if (continuation?.kind === 'truthtranceDiscard') {
|
||||
const c = continuation,
|
||||
entry = batch.entries[0],
|
||||
@@ -5643,8 +5685,8 @@ function treacheryDiscardIntegrity(g: Game) {
|
||||
} else throw new RuleError('Unknown saved discard continuation.');
|
||||
}
|
||||
/** Bounded public single-card reaction after a completed ordinary effect, paid
|
||||
* Box search, or early-ended Ornithopter flight. A completed flight's arrival
|
||||
* may open further entry windows, so that continuation remains automatic. */
|
||||
* Box search, early-ended Ornithopter flight or clean Distrans transfer. A
|
||||
* completed flight's arrival may open further entry windows and stays automatic. */
|
||||
function semutaOfferSupported(
|
||||
g: Game,
|
||||
continuation: NonNullable<Game['pendingTreacheryDiscard']>['continuation'],
|
||||
@@ -5652,7 +5694,11 @@ function semutaOfferSupported(
|
||||
) {
|
||||
if (continuation.kind !== 'ordinaryCardDiscard' &&
|
||||
continuation.kind !== 'nullentropyDiscard' &&
|
||||
continuation.kind !== 'distransDiscard' &&
|
||||
!(continuation.kind === 'ornithopterDiscard' && continuation.source === 'end')) return false;
|
||||
if (continuation.kind === 'distransDiscard' &&
|
||||
(g.pendingChoamMove || g.pendingIxMove || g.pendingFremenMove ||
|
||||
g.ornithopter || g.summonedWorm || g.wormRides.length > 0)) return false;
|
||||
return g.semutaPreview === true && g.status === 'playing' &&
|
||||
g.players.some(p => p.faction === 'richese') &&
|
||||
entries.length === 1 && entries[0].publicFace &&
|
||||
@@ -5815,6 +5861,11 @@ function finishTreacheryDiscard(g: Game, claim?: { player: string; card: string
|
||||
g.pendingKarama = next.resume.pendingKarama;
|
||||
g.phaseOpening = next.resume.phaseOpening;
|
||||
g.truthtrance = next.remaining;
|
||||
} else if (next.kind === 'distransDiscard') {
|
||||
g.response = next.resume.response;
|
||||
g.decision = next.resume.decision;
|
||||
g.pendingKarama = next.resume.pendingKarama;
|
||||
g.phaseOpening = next.resume.phaseOpening;
|
||||
} else if (next.kind === 'ornithopterDiscard') {
|
||||
g.response = next.resume.response;
|
||||
g.decision = next.resume.decision;
|
||||
@@ -23697,6 +23748,20 @@ function applyActionInner(
|
||||
`${p.name} played Distrans to give ${recipient.name} one private Treachery Card, then discarded Distrans. No spice, purchase bonus or recipient confirmation is required.`,
|
||||
{ faction: p.faction, name: 'Distrans' },
|
||||
);
|
||||
if (g.semutaPreview && !g.pendingTreacheryDiscard) {
|
||||
const continuation: DistransDiscardContinuation = {
|
||||
kind: 'distransDiscard', owner: p.id, recipient: recipient.id,
|
||||
transferred: result.transferred.id, card: result.discarded.id,
|
||||
resume: { response: g.response, decision: g.decision,
|
||||
pendingKarama: g.pendingKarama, phaseOpening: g.phaseOpening },
|
||||
stateSignature: '',
|
||||
};
|
||||
const entries = [{ card: result.discarded, discardedBy: p.id, publicFace: true }];
|
||||
if (semutaOfferSupported(g, continuation, entries)) {
|
||||
continuation.stateSignature = distransDiscardSignature(g, continuation);
|
||||
stageTreacheryDiscard(g, 'distrans', entries, continuation);
|
||||
}
|
||||
}
|
||||
return g;
|
||||
}
|
||||
if (t === 'richeseGift') {
|
||||
|
||||
Reference in new issue
Block a user