Open Tleilaxu special Karama throughout Revival
Permit native Advanced quiet any-player declarations alongside the original target-bound pending request. Preserve completed returns and charges. Adopt explicit provisional Ghola inclusion during the remaining Revival and phase expiry; keep printed Emperor special immunity and the existing low-Homeworld guard. Connect persistent controls and minimal legal bots through the shared quote. Validation: 14 related files148/148, types/lint/build, six actual native control/engine/GameTable SSR journeys and four existing-profile continuations. Private-driver-only feedback reuses passed source-identical checks. Native111/113-action setup, paid shipment and battle sources earn originals. No saved-room/D1/restart/deployment operation; full modes and browser/deployed acceptance remain open.
This commit is contained in:
1 parent
6ea691255a
commit
1a505525a8
18 files changed
+977
-59
No files matched your search
+40
-20
@@ -11,6 +11,7 @@ import { classicDiscoveryLeaderSkillsProfile, classicDiscoveryNexusProfile, home
|
||||
import { advancedMoritaniLeaderSkillsProfile, selectedDeckLeaderSkillsProfile, homeworldLeaderSkillsProfile, independentTreacheryLeaderSkillsProfile, classicNexusLeaderSkillsProfile, classicTechLeaderSkillsProfile, guildBetrayalNexusLeaderSkillsProfile, mixedE1E2NexusLeaderSkillsProfile, mixedE3NexusLeaderSkillsProfile, nativeDiscoveryLeaderSkillsProfile, nativeEcazLeaderSkillsProfile, nativeExpansionLeaderSkillsProfile, nativeTechLeaderSkillsProfile, ordinaryLeaderSkillModeSupported, pairedE3LeaderSkillsProfile, pairedE3NexusLeaderSkillsProfile, pairedNexusLeaderSkillsProfile, singleE1E2NexusLeaderSkillsProfile, standaloneE3NexusLeaderSkillsProfile, strongholdLeaderSkillsProfile } from './leader-skill-profile';
|
||||
import { bribeTimingBlock, maximumBribe, type BribeOptions } from './bribe-options';
|
||||
import { quoteAtreidesFullPlan } from './atreides-full-plan';
|
||||
import { quoteTleilaxuSpecialKarama } from './tleilaxu-special-karama';
|
||||
import { quoteSpicePlacement, stormExposesTerritory, stormSectorAfter, wormConsumesForces } from './disaster-rules';
|
||||
import { isStormCardDistance, type StormCardComponent } from './storm-cards';
|
||||
import { createLeaderSkills, validateLeaderSkills, dealLeaderSkills, chooseLeaderSkill, returnDeadLeaderSkills, offerRevivedLeaderSkill, drawRevivedLeaderSkills, declineRevivedLeaderSkill, LeaderSkillError, type LeaderSkillsState, type LeaderSkillsView } from './leader-skills';
|
||||
@@ -23289,6 +23290,8 @@ function playMarketGhola(g: Game, p: Player, action: Action) {
|
||||
stageOrdinaryCardDiscard(g, p, used);
|
||||
}
|
||||
function applyGholaEffect(g: Game, p: Player, action: Action, cardId?: string) {
|
||||
requireRule(!revivalPrevented(g, p.id),
|
||||
'Tleilaxu prevented this player’s revival, including Ghola, for the remaining Revival Phase.');
|
||||
if (action.leader === 'kwisatz') {
|
||||
requireRule(
|
||||
g.advanced && p.faction === 'atreides' && p.kwisatz?.dead,
|
||||
@@ -30811,6 +30814,27 @@ function atreidesFullPlanView(g: Game, p: Player) {
|
||||
return { ...quote, cards,
|
||||
blocked: quote.blocked ?? (cards.length ? null : 'A spendable Karama card is required.') };
|
||||
}
|
||||
function tleilaxuSpecialKaramaQuote(g: Game, p: Player) {
|
||||
const ownStop = g.decision?.kind === 'revivalStop' && g.decision.player === p.id &&
|
||||
g.pendingRevival?.player === g.decision.recipient;
|
||||
return quoteTleilaxuSpecialKarama({
|
||||
status: g.status, phase: g.phase, advanced: g.advanced, owner: p,
|
||||
players: g.players,
|
||||
cards: p.hand.filter(card => canUseAsKaramaRole(g, p, card) &&
|
||||
!karamaSpendingBlock(g, p, card, kullSpecialPurpose(p.faction))).map(card => card.id),
|
||||
pendingPlayer: g.pendingRevival?.player ?? null,
|
||||
blocked: g.truthtrance || g.response || (g.decision && !ownStop) ||
|
||||
(g.pendingRevival && !ownStop) || g.phaseOpening || g.pendingKarama ||
|
||||
g.choamMarket || g.pendingTreacheryDiscard || g.pendingNullentropy || g.pendingKull
|
||||
? 'Resolve the current response, decision or card effect before declaring revival prevention.' : null,
|
||||
targetBlocks: Object.fromEntries(g.players.map(player =>
|
||||
[player.id, homeworldRevivalKaramaBlock(g, player.id)])),
|
||||
});
|
||||
}
|
||||
function tleilaxuSpecialKaramaView(g: Game, p: Player) {
|
||||
return g.status === 'playing' && g.advanced && p.faction === 'tleilaxu' && g.phase === 4
|
||||
? tleilaxuSpecialKaramaQuote(g, p) : null;
|
||||
}
|
||||
function ecazSpecialKaramaContext(g: Game, p: Player) {
|
||||
const b = g.battle;
|
||||
return { status: g.status, phase: g.phase, advanced: g.advanced, owner: p,
|
||||
@@ -30867,7 +30891,7 @@ export type SpecialKaramaIntent = {
|
||||
| { kind: 'choam'; cards: string[] }
|
||||
| { kind: 'richese'; acquire: string; nexusEvent?: string }
|
||||
| { kind: 'ixians'; route: string[]; collect: boolean; sandmasterPiles?: string[] }
|
||||
| { kind: 'tleilaxu'; target: string; revival: PendingRevival }
|
||||
| { kind: 'tleilaxu'; target: string; revival: PendingRevival | null }
|
||||
| { kind: 'fremen'; territory: string }
|
||||
| {
|
||||
kind: 'atreides';
|
||||
@@ -31037,29 +31061,21 @@ export function prepareSpecialKaramaIntent(
|
||||
...(action.sandmasterPiles !== undefined ? { sandmasterPiles } : {}),
|
||||
};
|
||||
} else if (p.faction === 'tleilaxu') {
|
||||
const quote = tleilaxuSpecialKaramaQuote(g, p);
|
||||
requireRule(!quote.blocked, quote.blocked ?? 'Revival prevention is unavailable.');
|
||||
const target = action.target === undefined && g.pendingRevival
|
||||
? g.pendingRevival.player : stringField(action.target);
|
||||
const selection = quote.targets.find(candidate => candidate.player === target);
|
||||
requireRule(
|
||||
g.phase === 4 &&
|
||||
!g.response &&
|
||||
g.decision?.kind === 'revivalStop' &&
|
||||
g.decision.player === p.id &&
|
||||
g.pendingRevival,
|
||||
'Wait for a normal revival declaration before preventing it.',
|
||||
);
|
||||
const target = g.pendingRevival.player;
|
||||
const homeworldBlock = homeworldRevivalKaramaBlock(g, target);
|
||||
requireRule(
|
||||
!homeworldBlock,
|
||||
homeworldBlock ?? '',
|
||||
);
|
||||
requireRule(
|
||||
action.target === undefined || action.target === target,
|
||||
'This decision applies to the faction currently reviving.',
|
||||
selection,
|
||||
g.pendingRevival ? 'This decision applies to the faction currently reviving.' : 'Choose a player to prevent during Revival.',
|
||||
);
|
||||
requireRule(!selection.blocked, selection.blocked ?? 'This target is unavailable.');
|
||||
return {
|
||||
...base,
|
||||
kind: 'tleilaxu',
|
||||
target,
|
||||
revival: structuredClone(g.pendingRevival),
|
||||
revival: g.pendingRevival ? structuredClone(g.pendingRevival) : null,
|
||||
};
|
||||
} else if (p.faction === 'fremen') {
|
||||
requireRule(
|
||||
@@ -31265,7 +31281,8 @@ export function executeSpecialKaramaIntent(
|
||||
if (intent.kind === 'tleilaxu' && prepared.kind === 'tleilaxu')
|
||||
requireRule(
|
||||
intent.target === prepared.target &&
|
||||
sameDeclaration(intent.revival, prepared.revival),
|
||||
(intent.revival === null ? prepared.revival === null :
|
||||
prepared.revival !== null && sameDeclaration(intent.revival, prepared.revival)),
|
||||
'The original revival declaration is no longer pending.',
|
||||
);
|
||||
if (intent.kind === 'guild' && prepared.kind === 'guild')
|
||||
@@ -31409,7 +31426,7 @@ function resolveSpecialKaramaEffect(g: Game, p: Player, card: Card, intent: Spec
|
||||
if (g.revivalRequests) delete g.revivalRequests[target];
|
||||
log(
|
||||
g,
|
||||
`${p.name} used special Karama to prevent ${getPlayer(g, target).name}’s normal force and leader revivals for this turn.`,
|
||||
`${p.name} used special Karama to prevent ${getPlayer(g, target).name}’s force and leader revivals, including Ghola, for the remaining Revival Phase. Completed returns stay unchanged; the Emperor’s special Karama remains independent.`,
|
||||
);
|
||||
} else if (intent.kind === 'fremen') {
|
||||
const t = intent.territory;
|
||||
@@ -36003,6 +36020,8 @@ function applyActionInner(
|
||||
{
|
||||
status: g.status,
|
||||
phase: g.phase,
|
||||
turn: g.turn,
|
||||
revivalPrevention: g.revivalPrevention,
|
||||
active: g.active,
|
||||
me: id,
|
||||
nexusGuildCunning: projectedNexusGuildCunning(g,id),
|
||||
@@ -37176,6 +37195,7 @@ export function viewGame(state: Game, id: string) {
|
||||
: null,
|
||||
richeseSpecialKarama: richeseSpecialKaramaView(g, me),
|
||||
atreidesFullPlan: atreidesFullPlanView(g, me),
|
||||
tleilaxuSpecialKarama: tleilaxuSpecialKaramaView(g, me),
|
||||
...(me.faction === 'ecaz' && g.advanced && g.battle
|
||||
? { ecazSpecialKarama: ecazSpecialKaramaView(g, me) } : {}),
|
||||
...(g.moritaniSpecialKarama?.source.owner === me.id && g.moritaniSpecialKarama.stage === 'offer'
|
||||
|
||||
Reference in new issue
Block a user