Compose Ornithopter with unused Hajr movement
Replace one unused movement entitlement by the selected card mode and retain independent Hajr movement after a completed two-group flight. Preserve distinct cohorts and later legal reuse. Adopt supplied Advanced immediate-after-movement Hajr play timing with explicit provisional online/card-group interpretation; Basic timing remains. Validation: frozen11-file132/132, types/lint/build and12 original-action/rendered-table programmes; four minimal policies retain both continuations. Actual cards are acquired by original auctions and source groups use funded shipments. Multiple Hajr cards, advisors, wider combinations, final assurance and deployed acceptance remain open. No saved-game, reset, restart or deployment operation.
This commit is contained in:
1 parent
6ccfaa1b4d
commit
72e77d7f4d
16 files changed
+522
-31
No files matched your search
@@ -17,6 +17,9 @@ export type OrdinaryCardPlayer = {
|
||||
advisors?: Record<string, { lockedTurn?: number }>;
|
||||
hand?: readonly Card[];
|
||||
movesAllowed?: number;
|
||||
moved?: number;
|
||||
hajrUsed?: boolean;
|
||||
hajrAvailable?: boolean;
|
||||
};
|
||||
/** Only public state and the current viewer's own hand are inspected. */
|
||||
export type OrdinaryCardState = {
|
||||
@@ -102,9 +105,12 @@ export function ordinaryCardAvailability(
|
||||
if (
|
||||
state.phase !== 5 ||
|
||||
state.active !== state.me ||
|
||||
(state.nexusGuildCunning?.active ? !state.nexusGuildCunning.active.hajrAvailable : own.movesAllowed !== 1)
|
||||
(state.nexusGuildCunning?.active ? !state.nexusGuildCunning.active.hajrAvailable :
|
||||
own.hajrAvailable !== undefined ? !own.hajrAvailable :
|
||||
own.hajrUsed === undefined ? own.movesAllowed !== 1 :
|
||||
own.hajrUsed || (own.moved ?? 0) > (own.movesAllowed ?? 1))
|
||||
)
|
||||
return blocked('Use Hajr during your movement.');
|
||||
return blocked('Use one Hajr during your movement; Advanced requires the immediate opportunity after a completed Force Movement.');
|
||||
break;
|
||||
case 'harvester':
|
||||
if (state.phase !== 1 || !harvesterAvailable(state.spiceWindow))
|
||||
|
||||
+31
-10
@@ -400,6 +400,7 @@ import {
|
||||
} from './residual-poison';
|
||||
import {
|
||||
createRemainingCohort,
|
||||
ordinaryMovementAllowance,
|
||||
validateCohortSelection,
|
||||
type MovementCohort,
|
||||
type OrnithopterMode,
|
||||
@@ -681,6 +682,10 @@ export type Player = {
|
||||
ecazRevivalCohort?: EcazRevivalCohort;
|
||||
shipped: boolean;
|
||||
moved: number;
|
||||
/** Completed second groups replace one entitlement without consuming Hajr. */
|
||||
ornithopterExtraMoves?: number;
|
||||
/** Supplied Advanced Hajr timing: the owner's next voluntary action. */
|
||||
hajrWindow?: {turn: number; move: number};
|
||||
battleLosses: number;
|
||||
kwisatz?: { dead: boolean; usedAt?: string; revivalCycle?: number };
|
||||
prediction?: { faction: FactionId; turn: number };
|
||||
@@ -6085,6 +6090,8 @@ function ornithopterDiscardSignature(
|
||||
advisors: p.advisors,
|
||||
noField: p.noField,
|
||||
noFieldEvent: p.noFieldEvent,
|
||||
ornithopterExtraMoves: p.ornithopterExtraMoves,
|
||||
hajrWindow: p.hajrWindow,
|
||||
moved: p.moved,
|
||||
shipped: p.shipped,
|
||||
})),
|
||||
@@ -15568,9 +15575,11 @@ function movesAllowed(g: Game, p: Player) {
|
||||
const flight = g.ornithopter;
|
||||
return flight?.player === p.id && flight.turn === g.turn
|
||||
? flight.startingMove + (flight.mode === 'twoGroups' ? 2 : 1)
|
||||
: g.hajr.includes(p.id)
|
||||
? 2
|
||||
: 1;
|
||||
: ordinaryMovementAllowance(g.hajr.includes(p.id), p.ornithopterExtraMoves);
|
||||
}
|
||||
function hajrAvailable(g: Game, p: Player) {
|
||||
return !g.hajr.includes(p.id) && p.moved <= movesAllowed(g,p) &&
|
||||
(!g.advanced || (p.hajrWindow?.turn === g.turn && p.hajrWindow.move === p.moved));
|
||||
}
|
||||
function emperorHomeworldMoveBlock(g: Game, p: Player): string | null {
|
||||
if (!g.homeworlds?.custody || !g.advanced || p.faction !== 'emperor')
|
||||
@@ -15637,8 +15646,8 @@ function ornithopterBlock(
|
||||
if (g.ornithopter)
|
||||
return 'Finish the already played Ornithopter movement first.';
|
||||
if (currentGuildCunning(g,p.id)) return 'Guild Cunning combined with the physical Ornithopter card awaits its timing ruling.';
|
||||
if (p.moved || g.hajr.includes(p.id))
|
||||
return 'Ornithopter combined with prior moves or Hajr awaits its timing ruling.';
|
||||
if (p.moved >= movesAllowed(g,p))
|
||||
return 'Spend an unused ordinary or Hajr movement on Ornithopter.';
|
||||
// Provisional composition: Kulon augments normal movement, not the
|
||||
// Ornithopter card's explicit maximum of three territories.
|
||||
return null;
|
||||
@@ -16252,6 +16261,7 @@ function completeMove(g: Game, move: MovementOrder) {
|
||||
finishShipmentPromises(g, p, null);
|
||||
p.shipped = true;
|
||||
p.moved++;
|
||||
p.hajrWindow = {turn:g.turn,move:p.moved};
|
||||
if (move.discoveryFlight) {
|
||||
g.discoveries = nexusRule(() => completeDiscoveryFlight(g, move.discoveryFlight!, move));
|
||||
log(g, `${p.name} used the carried Discovery Ornithopter for this movement and removed its token.`);
|
||||
@@ -16271,6 +16281,7 @@ function completeMove(g: Game, move: MovementOrder) {
|
||||
if (flight?.player === id) {
|
||||
flight.completed++;
|
||||
if (flight.completed === (flight.mode === 'twoGroups' ? 2 : 1)) {
|
||||
if (flight.mode === 'twoGroups') p.ornithopterExtraMoves = (p.ornithopterExtraMoves ?? 0) + 1;
|
||||
finishOrnithopter(g, 'move', completed);
|
||||
return;
|
||||
}
|
||||
@@ -19640,6 +19651,8 @@ function commitMovementPhaseStart(g: Game, quote: MovementPhaseQuote) {
|
||||
for (const p of g.players) {
|
||||
p.shipped = false;
|
||||
p.moved = 0;
|
||||
delete p.ornithopterExtraMoves;
|
||||
delete p.hajrWindow;
|
||||
}
|
||||
applyAdvisorReleases(g, quote.released);
|
||||
g.decision = quote.decision;
|
||||
@@ -28354,7 +28367,7 @@ function projectedNexusGuildCunning(g: Game, owner: string) {
|
||||
else if (g.response || g.decision || g.truthtrance || g.phaseOpening || g.pendingKarama || g.pendingTreacheryDiscard ||
|
||||
g.pendingNullentropy || g.pendingShipment || g.pendingHomeworldShipment || g.nexusTraitorPending || g.nexusCards.phase?.stage === 'drawing')
|
||||
blocked = 'Finish the current interaction before declaring a second shipment.';
|
||||
else if (g.ornithopter || p.moved > (g.hajr.includes(owner) ? 2 : 1))
|
||||
else if (g.ornithopter || p.ornithopterExtraMoves || p.moved > (g.hajr.includes(owner) ? 2 : 1))
|
||||
blocked = 'Cunning combined with the physical Ornithopter movement card awaits its timing ruling.';
|
||||
else if (g.nexusGuildCunningHistory?.some(r => r.receipt.turn === g.turn && r.receipt.owner === owner))
|
||||
blocked = 'This turn’s Guild Cunning opportunity is already used.';
|
||||
@@ -30730,6 +30743,12 @@ function applyActionInner(
|
||||
);
|
||||
requireRule(g.status !== 'finished', 'This game has ended.');
|
||||
const t = action.type;
|
||||
// Mandatory movement reactions and other seats' actions do not close the
|
||||
// Advanced owner's immediate Hajr opportunity. Its next voluntary action does.
|
||||
if (g.advanced && p.hajrWindow && !g.response && !g.decision && !g.truthtrance &&
|
||||
!g.phaseOpening && !g.pendingTreacheryDiscard && t !== 'advanceBots' &&
|
||||
!(t === 'card' && p.hand.some(card => card.id === action.card && card.effect === 'hajr')))
|
||||
delete p.hajrWindow;
|
||||
requireRule(t !== 'nexusIxianReplacementPass' && t !== 'nexusIxianReplacementUse',
|
||||
'This purchased-card choice is no longer pending.');
|
||||
requireRule(t !== 'guildBetrayalPass' && t !== 'guildBetrayalUse',
|
||||
@@ -33995,6 +34014,7 @@ function applyActionInner(
|
||||
finishShipmentPromises(g, p, null);
|
||||
p.shipped = true;
|
||||
p.moved++;
|
||||
p.hajrWindow = {turn:g.turn,move:p.moved};
|
||||
const name = (world: string) =>
|
||||
world === 'homeworld:emperor' ? 'Kaitain' : 'Salusa Secundus';
|
||||
const shifts = quote.populations.after
|
||||
@@ -34592,6 +34612,8 @@ function applyActionInner(
|
||||
advisors: p.advisors,
|
||||
hand: p.hand,
|
||||
movesAllowed: movesAllowed(g, p),
|
||||
hajrUsed: g.hajr.includes(p.id),
|
||||
hajrAvailable: hajrAvailable(g,p),
|
||||
},
|
||||
],
|
||||
},
|
||||
@@ -34601,11 +34623,8 @@ function applyActionInner(
|
||||
if (availability && !availability.available)
|
||||
throw new RuleError(availability.reason);
|
||||
if (c.effect === 'hajr') {
|
||||
requireRule(
|
||||
g.ornithopter?.player !== id,
|
||||
'Hajr combined with Ornithopter awaits its timing ruling.',
|
||||
);
|
||||
g.hajr.push(id);
|
||||
delete p.hajrWindow;
|
||||
} else if (c.effect === 'ghola') {
|
||||
applyGholaEffect(g, p, action, c.id);
|
||||
} else if (c.effect === 'harvester') {
|
||||
@@ -35585,6 +35604,8 @@ export function viewGame(state: Game, id: string) {
|
||||
shipped: p.shipped,
|
||||
moved: p.moved,
|
||||
movesAllowed: movesAllowed(g, p),
|
||||
hajrUsed: g.hajr.includes(p.id),
|
||||
hajrAvailable: hajrAvailable(g,p),
|
||||
revived: p.revived,
|
||||
leaderRevived: p.leaderRevived,
|
||||
gholaBlocked: Object.entries(p.gholaBlocked ?? {})
|
||||
|
||||
@@ -7,6 +7,15 @@ export type MovementCohort = {
|
||||
};
|
||||
type Counts = Readonly<Record<string, number>>;
|
||||
|
||||
/** A two-group flight replaces one ordinary entitlement. Only its completed
|
||||
* extra group earns a physical-move credit; Hajr remains an independent move. */
|
||||
export function ordinaryMovementAllowance(hajrUsed: boolean, extraMoves = 0): number {
|
||||
const limit = 1 + Number(hajrUsed) + extraMoves;
|
||||
if (typeof hajrUsed !== 'boolean' || !Number.isSafeInteger(extraMoves) || extraMoves < 0 ||
|
||||
!Number.isSafeInteger(limit)) throw new RangeError('Movement credits must be nonnegative safe integers.');
|
||||
return limit;
|
||||
}
|
||||
|
||||
function validateCounts(counts: Counts, label: string) {
|
||||
if (!counts || typeof counts !== 'object' || Array.isArray(counts))
|
||||
throw new TypeError(`${label} must be a location count record.`);
|
||||
|
||||
+19
-12
@@ -901,7 +901,8 @@ export const RULE_TOPICS: RuleTopic[] = [
|
||||
'A concealed No-Field may move as a group member without exposing its value. A marker moved in the first group cannot also move in the second. Revealing an unmoved marker converts its remaining entitlement to its actual placed forces; revealing a moved marker does not give those forces another move.',
|
||||
'A validated declaration moves the physical card from your hand to the public played-card area. It remains there through genuine CHOAM, Ixian or other arrival decisions and is discarded once when the chosen use ends. Random hand effects act on cards still in hand. Ending your movement closes any remaining card movement without undoing a completed group.',
|
||||
'The saved event binds retries and the remaining original-group data. Refresh and reconnect preserve that event. After disposal, recovery preserves the completed forces and marker event before opening arrival decisions or advancing an early-ended turn; it does not move the group again. Missing saved group data cannot be reconstructed from merged forces and blocks the damaged continuation instead of allowing reuse.',
|
||||
'Kulon and the fixed-range card now coexist under a provisional policy: the one-group move stays capped at three territories, while each normal-range group receives Kulon’s bonus. Hajr/prior-move composition and advanced advisor use remain guarded; these development guards are not printed prohibitions. Other unfinished entry and optional-module interactions remain unfinished; full Richese starts are still disabled.',
|
||||
'Under the provisional composition, Ornithopter replaces one unused ordinary or Hajr movement. A prior move stays spent and cannot become the card’s first group retrospectively. One Hajr plus a two-group flight permits three group movements; the Hajr move may reuse a card-moved group. Ending early abandons the unused card group and ends your turn. Multiple physical Hajr cards remain unfinished.',
|
||||
'Advanced Hajr must be played immediately after a completed Force Movement and its required arrival decisions, before your next voluntary game action. A completed card group provisionally supplies this opportunity. Basic retains broader own-turn timing. Kulon leaves the fixed card maximum at three and extends both normal-range groups. Advisor/card use, Guild Cunning/card and wider optional combinations remain unfinished.',
|
||||
'All four AI profiles use their own projected movement choices and original-group limits. They can use a longer route, split distinct groups, finish interrupted movement and end optional movement when no legal second group remains. Complete Richese-game calibration is not yet certified.',
|
||||
],
|
||||
related: [
|
||||
@@ -915,32 +916,37 @@ export const RULE_TOPICS: RuleTopic[] = [
|
||||
area: 'Implementation',
|
||||
status: 'Partial',
|
||||
detail:
|
||||
'Both movement modes, played-card custody, exact event continuation, typed original-group quotas, concealed-marker conversion and separate disposal recovery are integrated. Kulon coexistence uses a labeled provisional three-territory cap; Hajr/prior moves and advisor composition remain guarded.',
|
||||
'Both modes retain original event/cohort accounting. One unused normal/Hajr entitlement can start a flight; completed two-group credit preserves unused Hajr afterward. Advanced uses supplied immediate-after-play timing with an explicit online/card-group convention. Multiple Hajr and advisor composition remain unfinished.',
|
||||
},
|
||||
{
|
||||
area: 'Player controls',
|
||||
status: 'Partial',
|
||||
detail:
|
||||
'Uses the existing board source/destination and multi-sector controls, a mode selector, an inspector and remaining-group guidance. Automatic disposal adds no confirmation.',
|
||||
'Existing source/destination, typed group, mode and inspector controls consume current shared movement availability. Persistent text explains Hajr composition and Advanced timing; automatic disposal adds no confirmation.',
|
||||
},
|
||||
{
|
||||
area: 'AI',
|
||||
status: 'Partial',
|
||||
detail:
|
||||
'Four profiles use projected ranges and remaining ordinary/elite/marker quotas without hidden-value access. Complete expansion strength calibration remains unfinished.',
|
||||
'Four profiles use projected ranges, distinct group quotas and earned Hajr movement. Actual owned second-flight and extra-move candidates are legal; strategic tuning remains deferred.',
|
||||
},
|
||||
{
|
||||
area: 'Documentation',
|
||||
status: 'Partial',
|
||||
detail:
|
||||
'Explains both choices, independent card range, current normal range, distinct groups, public played-card custody and explicit unresolved combinations.',
|
||||
'Explains unused entitlement replacement, per-flight distinctness, later Hajr reuse, fixed/normal range, Advanced source timing and explicit unfinished multiple-card/module combinations.',
|
||||
},
|
||||
{
|
||||
area: 'Verification',
|
||||
status: 'Partial',
|
||||
detail:
|
||||
'Pure quota and engine checks cover twelve holders, both game modes, merged groups, exact marker events, stale state and interrupted faction decisions. Full expansion acceptance remains unverified.',
|
||||
'Hajr checkpoint adds frozen11-file132/132, types/lint/build and12 original-action/rendered-table programmes: actual paid cards/shipments, Basic and Advanced timing, prior normal movement, typed groups, native Baliset, voluntary window closure/reopening and early end. Four minimal policies retain both continuations. Framework aliases/CSS stubs mean no visual/browser/deployed acceptance; full combinations/assurance/modes remain open.',
|
||||
evidence: [
|
||||
'game/ornithopter.ts',
|
||||
'game/card-availability.ts',
|
||||
'tests/fixture-ornithopter-hajr.ts',
|
||||
'tests/ornithopter-hajr-engine.test.ts',
|
||||
'docs/RICHESE_ORNITHOPTER_ENGINE_AUDIT.md',
|
||||
'tests/ornithopter.test.ts',
|
||||
'tests/ornithopter-engine.test.ts',
|
||||
'tests/ornithopter-engine-review.test.ts',
|
||||
@@ -1217,7 +1223,7 @@ export const RULE_TOPICS: RuleTopic[] = [
|
||||
: definition.card.effect === 'nullentropyBox'
|
||||
? 'Use the Nullentropy Box panel to pay two bank spice for a private discard search. Both modes provisionally permit full-hand retrieval as an atomic exchange that discards the Box and leaves the final hand within its current limit. Provisional Guild refund claims remain guarded.'
|
||||
: definition.card.effect === 'ornithopter'
|
||||
? 'Use the Ornithopter movement controls with a selected group and destination. Provisionally, Kulon coexists without raising the one-group maximum of three; both normal-range groups retain its bonus. Hajr/prior-move composition and advanced advisors remain guarded.'
|
||||
? 'Use the Ornithopter controls with one unused normal/Hajr movement and a selected group/destination. Two card groups stay distinct, while Hajr may reuse their forces afterward. Advanced Hajr follows immediate-after-movement timing. Kulon retains the fixed maximum three and extends both normal groups. Multiple Hajr, advisor/card and wider module combinations remain unfinished.'
|
||||
: definition.card.effect === 'residualPoison'
|
||||
? 'Use the Residual Poison panel before either combatant commits a leader. A shared preparation step provides an opportunity regardless of hand contents; advanced Harkonnen tables remain guarded pending the secret-captive ruling. In the explicit Semuta preview, a clean death with no readiness or inspection commitments pauses after public disposal for the neutral offer, then resumes the same preparation.'
|
||||
: definition.card.effect === 'stoneBurner'
|
||||
@@ -1251,7 +1257,7 @@ export const RULE_TOPICS: RuleTopic[] = [
|
||||
'The private semuta --discoveries entry composes original Richese/CHOAM and optional Ix Discovery setup, optionally unused Tech3+ and Advanced Strongholds, without Skills, Homeworlds, Nexus or E3. An entitled physical Truthtrance used as Karama offers its original fresh cost before the same price, cancellation, normal-auction or native special effect resumes. Emperor revival already happened and is not repeated. A physical Karama used as Truthtrance offers after its single definite answer. Recovery changes custody, never the printed face or original effect. Controlled Shrine setup evidence is not natural draw/entry or complete-mode acceptance.',
|
||||
'The separate fresh semuta --homeworlds entry retains original Richese/CHOAM and classic Homeworld occupation setup with exact CHOAM and no other modules. Paid Box search, clean Distrans transfer, definite Truthtrance answers, used ordinary specials, paid Kaitain disposal, Tupile cleanup and printed Karama canceling native Guild/Emperor/Richese-purchase income offer Semuta. Native printed Kulon, Baliset, La La La, Trip to Gamont and Jubba costs also pause before their original effect: claim/pass adds one movement bonus, denies free allowance without charging/moving forces, stops the uncommitted Baliset move, returns one original board counter before the same CHOAM close, or applies protection before the same recorded storm moves. Printed Karama canceling these powers pauses separately before denial; the retained Worthless fuel is not a candidate. A canceled reactive Baliset resumes its original declared move once; canceled Kulon/La La La/Gamont/Jubba do not apply their effects. A recovered card grants no other faction CHOAM power. Kaitain private groups require commitment before selection; each Tupile holder has a separate cost. Native income denial retains payment and the original occupied-share allocation. Basic unresolved epochs, Kull, other producers and public starts remain guarded.',
|
||||
'Legal completed CHOAM market sales also offer their used card in the Homeworld profile. Worthless cashout keeps its original two-spice credit; a non-Worthless duplicate keeps its three-spice credit and the original same-name witness stays held. The high-Tupile Worthless-sale prohibition still applies, including duplicate Worthless sales. Claim/pass cannot repeat payout, undo the actual reserve shipment or replace the original market close; no new bank-income interpretation is made.',
|
||||
'Final Ornithopter disposal also offers its used card in the Homeworld profile after the one-group range-three or original two-group movement completes, including an early end with no useful original group. Claim/pass cannot move those counters again or replay the paid native shipment; prior-move/Hajr, advisor and Homeworld-transfer boundaries remain. Clean Sapho Once Around and unstarted Movement first/last costs likewise offer after reordering the finite unacted queue. Earlier bids/funding and physical storm order stay committed, and the same next bidder or combined turn resumes once. Cyclic auctions, partly completed turns, first-versus-Guild and late battle timing are not opened.',
|
||||
'Final Ornithopter disposal also offers its used card in the Homeworld profile after the one-group range-three or original two-group movement completes, including an early end with no useful original group. Claim/pass cannot move those counters again or replay the paid native shipment; unused movement entitlements retain the shared Hajr composition, while advisor and Homeworld-transfer boundaries remain. Clean Sapho Once Around and unstarted Movement first/last costs likewise offer after reordering the finite unacted queue. Earlier bids/funding and physical storm order stay committed, and the same next bidder or combined turn resumes once. Cyclic auctions, partly completed turns, first-versus-Guild and late battle timing are not opened.',
|
||||
'Original Homeworld battle disposal offers accepted losing cards, later mandatory winning cards and optional winner-selected cards as separate fresh events. Commit and exact selection recover at most one other player’s newly discarded card; unselected cards stay in the pile. The original paid deployments, accepted result, leader deaths and casualty obligations remain committed, with the same native winner cleanup and rewards afterward. A recovered card cannot replay combat or consume another original opportunity.',
|
||||
'Clean Homeworld Residual Poison disposal offers its used card after exactly one native random eligible leader enters the Tanks, with no spice bounty or force loss; claim/pass resumes the original shared preparation and does not redraw the victim. Early Sapho aggressor disposal likewise keeps the original participants/chooser and changed ordinary tie priority, while clean between-battle first/last disposal keeps earlier resolved battles and only reorders remaining chooser opportunities. Physical storm order is unchanged. Readied or inspected Poison costs, late Sapho timing and Advanced Harkonnen captive disclosure remain outside these clean reactions.',
|
||||
'Printed Karama canceling original CHOAM opening income or native Inflation placement also offers its used card in the Homeworld profile. Claim/pass completes the same denial once: unpaid income stays withheld, and the original placement attempt remains spent with no token. Clean normal-lot printed Karama purchase and free winning-bid payment likewise offer their costs before exactly one original reserved card arrives without spice payment. Earlier bids, the normal pool, Richese cache, original wallets and later native controls stay committed. Richese lots, BG Worthless conversion, Kull and other cancellations remain separate.',
|
||||
@@ -2556,14 +2562,15 @@ export const RULE_TOPICS: RuleTopic[] = [
|
||||
id: 'card-hajr',
|
||||
title: 'Hajr',
|
||||
category: 'Cards',
|
||||
coverage: 'Implemented',
|
||||
coverage: 'Partial',
|
||||
summary: 'Make an additional force move during your movement turn.',
|
||||
steps: [
|
||||
'Play during your shipment and movement turn.',
|
||||
'Make a second legal group move. Normal source, range and destination restrictions still apply.',
|
||||
'In Basic, play during your shipment and movement turn. Advanced requires the immediate opportunity after a completed Force Movement and its required arrival decisions, before your next voluntary game action.',
|
||||
'Make one additional legal group move, using the same or another group. Normal source, range and destination restrictions still apply. A completed Ornithopter group provisionally supplies the Advanced timing opportunity.',
|
||||
'Ornithopter replaces one unused movement entitlement, so one Hajr and a two-group flight permit three group movements. Groups must differ within that flight, while the later Hajr move may reuse them. Multiple physical Hajr plays in one turn remain an unfinished prototype boundary; the supplied passage does not establish a once-per-turn prohibition.',
|
||||
'A saved play preserves the extra movement allowance. Recovery neither grants another move nor finishes your movement turn.',
|
||||
],
|
||||
related: ['movement'],
|
||||
related: ['movement', 'ornithopter-movement'],
|
||||
},
|
||||
{
|
||||
id: 'card-ghola',
|
||||
|
||||
Reference in new issue
Block a user