Connect Advanced allied reserve transit and legal departure
This commit is contained in:
1 parent
869412bebd
commit
7cef1a3412
10 files changed
+118
-44
No files matched your search
@@ -177,7 +177,7 @@ import { nexusGuildSecretAllyAction, nexusGuildSecretAllyQuote } from '@/game/ne
|
||||
import { nexusRicheseAction, nexusRicheseQuote } from '@/game/nexus-richese-options';
|
||||
import { guildTransportQuote } from '@/game/transport-quote';
|
||||
import { fremenReserveEntry, botGroundMoveAllowed } from '@/game/bot-mobility';
|
||||
import { isBasicAlliedShipmentVisit } from '@/game/allied-separation';
|
||||
import { isAlliedShipmentVisit } from '@/game/allied-separation';
|
||||
import { location as boardLocation } from '@/game/board';
|
||||
import { fighterCount, isAdvisor } from '@/game/advisors';
|
||||
import { useState } from 'react';
|
||||
@@ -4396,12 +4396,12 @@ export function GameTable({
|
||||
</div>
|
||||
)}
|
||||
{destination}
|
||||
{isBasicAlliedShipmentVisit(g, me, selected) && (
|
||||
{isAlliedShipmentVisit(g, me, selected) && (
|
||||
<p className="notice">
|
||||
Basic allied transit: ship ordinary forces here, then move the visiting group out
|
||||
before finishing your turn. Storm and stronghold capacity still apply.
|
||||
Provisional fallback: new visitors left behind go to Tanks, even if departure is prevented.
|
||||
This does not waive the older allied co-occupation deadline.
|
||||
{g.advanced
|
||||
? 'Advanced allied transit: ship ordinary forces here, then depart before finishing. Supplied p15 sends all your forces left sharing with an ally to Tanks, including old forces; advisor, Polar Sink and native Ecaz coexistence exemptions remain.'
|
||||
: 'Basic allied transit: ship ordinary forces here, then move the visiting group out before finishing your turn. Provisional fallback: new visitors left behind go to Tanks, even if departure is prevented. This does not waive the older allied co-occupation deadline.'}
|
||||
{' '}Storm and stronghold capacity still apply.
|
||||
</p>
|
||||
)}
|
||||
{amountInput}
|
||||
|
||||
@@ -2,6 +2,33 @@
|
||||
|
||||
Audit: 7 September 2026. Read-only source and code audit, retained by the coordinating agent. Scope is classic GF9 base rules and the applicable November 2020 FAQ, not tournament or movie-game rules.
|
||||
|
||||
## Advanced ordinary allied shipment follow-up — 8 October 2026
|
||||
|
||||
Ordinary physical reserve shipment may now enter an ally's territory on the
|
||||
shipper's own unused turn before movement, under the same scoped entry
|
||||
permission as Basic. This composes the November FAQ's shipment/departure
|
||||
permission with the authorized supplied **physical p15** ending constraint.
|
||||
Stronghold capacity, storm, price, counters and movement range still apply.
|
||||
No-Fields, allied No-Field offers, Homeworld and mobile transports keep their
|
||||
separate guards; normal ground-movement endpoints are not broadened.
|
||||
|
||||
Advanced creates no Basic visitor receipt. At ending, the existing native
|
||||
Advanced quote removes **all the ending player's shared forces**, including
|
||||
old counters, not just newcomers. Advisor, Polar Sink and native Ecaz
|
||||
coexistence exemptions remain. Basic keeps its visibly provisional excess-
|
||||
visitor policy and older co-occupation deadline. No extra move or refund
|
||||
is granted when departure is unavailable.
|
||||
|
||||
The pre-shipment notice explains the mode-specific consequence; the
|
||||
existing ending warning names the shared territories. Minimal legal AI
|
||||
prioritizes a native departure and does not leave a city sentinel behind.
|
||||
Three direct smokes cover paid normal1/special1 arrival and Medium departure,
|
||||
ending with old5 plus new2 (only the actor's7 go to Tanks), and third-faction
|
||||
capacity rejection. Chromium observed native GameTable SSR pre-arrival and
|
||||
ending warnings with enabled finish. No broad suite, recovery campaign or
|
||||
deployed acceptance was run.
|
||||
|
||||
|
||||
## Basic ordinary allied shipment prototype — 8 October 2026
|
||||
|
||||
The content-first Basic pass connects the FAQ's ordinary physical
|
||||
@@ -29,10 +56,10 @@ co-occupation deadlines still resolve separately. AI prioritizes available
|
||||
native departures and does not retain a stronghold sentinel from a
|
||||
visiting group.
|
||||
|
||||
Concealed and allied No-Field arrivals, mobile/homeworld transports,
|
||||
optional arrival compositions and Advanced entry permission are not
|
||||
expanded by this prototype. Refinement of interrupted and mixed-cohort
|
||||
interactions remains later work.
|
||||
Concealed and allied No-Field arrivals, mobile/homeworld transports and
|
||||
optional arrival compositions are not expanded by the Basic prototype.
|
||||
Advanced ordinary physical entry is connected in the follow-up above.
|
||||
Refinement of interrupted and mixed-cohort interactions remains later work.
|
||||
|
||||
**Brief smoke evidence:** paid Basic allied arrival followed by Medium's
|
||||
native departure and clean ending; ending without departure removes two
|
||||
@@ -69,13 +96,13 @@ the existing later-player and formation-turn conditions, including its
|
||||
non-exempt advisor treatment.
|
||||
|
||||
This resolves the **Advanced** timing/advisor and end-turn consequence
|
||||
questions below. It does not remove the current allied-entry guard, grant
|
||||
new shipments or extra moves, alter alliance formation/breaking windows,
|
||||
or settle every optional arrival composition. A forced end consequence
|
||||
does not require a new loss confirmation; the existing `endMovement`
|
||||
action remains legal even when no escape is available. The native force,
|
||||
elite and No-Field custody paths remove the ending player's group once,
|
||||
not its ally's group.
|
||||
questions below. The 8 October follow-up also connects scoped ordinary
|
||||
allied reserve entry, without granting new shipments or extra moves,
|
||||
altering alliance formation/breaking windows or settling optional arrivals.
|
||||
A forced end consequence needs no new loss confirmation; `endMovement`
|
||||
remains legal even when no escape is available. Native custody removes the
|
||||
ending player's group once, not its ally's group; concealed No-Field removal
|
||||
retains its separate custody guard.
|
||||
|
||||
The own clean-turn warning uses the canonical engine quote via
|
||||
`GameView.advancedAllySeparation: { territories: string[] } | null`.
|
||||
|
||||
+16
-4
@@ -20,10 +20,14 @@ open mode gates.
|
||||
requests usable first versions for every faction before refinement. Administration
|
||||
is deferred. Broad suites and capture-resumption campaigns are deferred; rule
|
||||
delivery uses brief changed-path smoke only, with batched compiler feedback.
|
||||
**Active pass — Advanced first versions.** Basic standalone core-function
|
||||
inventory is closed at prototype level, with its stated provisional policies.
|
||||
This does not certify Basic timing variants, combinations or public expansion
|
||||
starts. Those remain after the Advanced function pass.
|
||||
**Active pass — rule interactions and remaining optional paths.** Basic and
|
||||
Advanced standalone core-function inventories are closed at prototype level.
|
||||
Bounded classic/expansion mapping plus the connected follow-ups below found
|
||||
no further absent standalone faction primitive in the examined paths.
|
||||
This is not complete-mode acceptance: exceptional custody, timing variants,
|
||||
optional skill bands, combinations and public expansion starts remain open.
|
||||
Rules implementation continues; comprehensive assurance and administration
|
||||
remain deferred until all rules are implemented.
|
||||
|
||||
|
||||
**Richese cache cancellation connected.** The printed Karama cancellation
|
||||
@@ -120,6 +124,14 @@ Two native Easy smokes, two cycle regression cases, owned GameTable SSR
|
||||
Chromium choices and types/lint pass. No broad suite or deployed acceptance
|
||||
was run; prevention, price, death history and one-leader allowance remain.
|
||||
|
||||
**Advanced ordinary allied arrival connected.** Scoped physical reserve
|
||||
shipment can enter an ally's territory before movement. Native supplied
|
||||
p15 ending removes all shared actor forces, including old counters; Basic
|
||||
keeps its separate provisional visitor policy. Medium departs typed arrivals
|
||||
without a city sentinel. Three direct scenarios, one cross-mode arrival/ending
|
||||
regression, types/lint and native GameTable SSR pre-arrival/ending controls
|
||||
pass; no broad suite or deployed proof was run.
|
||||
|
||||
**7 October 2026 — five pending rulings resolved by the user.** The user
|
||||
answered five recorded questions; each is now recorded in
|
||||
[the decision index](RULE_DECISIONS.md#user-rulings--7-october-2026) and
|
||||
|
||||
+10
-3
@@ -25,9 +25,10 @@ rulings below remain distinct from these prototype choices.
|
||||
|
||||
## Advanced prototype pass — 8 October 2026
|
||||
|
||||
The Basic standalone core-function inventory is closed at first-version
|
||||
level, not complete-mode acceptance; Advanced function delivery now precedes
|
||||
timing/combination refinement. Ecaz's supplied physical PDF **p30, A**
|
||||
Basic and Advanced standalone core-function inventories are closed at
|
||||
first-version level, not complete-mode acceptance. Exceptional custody,
|
||||
timing variants and optional combinations remain active rule work.
|
||||
Ecaz's supplied physical PDF **p30, A**
|
||||
requires an available Duke grant after accepted Ambassador alliance consent.
|
||||
The accepting action now performs that same-disc transfer automatically,
|
||||
without a new loan acknowledgement. Basic keeps its optional prototype.
|
||||
@@ -57,6 +58,12 @@ The separate five-ordinary-disc cycle exclusion is a visibly provisional
|
||||
implementation policy, not a numerical user ruling or publisher answer.
|
||||
[Source, policy and runtime evidence](CHOAM_AUDITOR.md).
|
||||
|
||||
Advanced ordinary allied reserve shipment now uses the FAQ's scoped
|
||||
shipment-then-departure permission and supplied **p15** end-turn constraint.
|
||||
Its ending removes all shared actor forces, not the Basic prototype's
|
||||
excess newcomers. Storm/capacity and advisor/Polar/Ecaz exemptions remain.
|
||||
No-Field and optional transports are separate. [Allied transit](ALLIED_TRANSIT_RULES.md).
|
||||
|
||||
## User rulings — 7 October 2026
|
||||
|
||||
Five previously pending questions were answered directly by the user. These are
|
||||
|
||||
@@ -12,10 +12,9 @@ export type BasicAlliedShipment = Readonly<{
|
||||
baseline: Readonly<{ normal: number; elite: number }>;
|
||||
}>;
|
||||
|
||||
/** Ordinary Basic reserve arrivals only; movement endpoints keep their guard. */
|
||||
export function isBasicAlliedShipmentVisit(
|
||||
/** Ordinary reserve arrivals only; movement endpoints keep their guard. */
|
||||
export function isAlliedShipmentVisit(
|
||||
context: Readonly<{
|
||||
advanced: boolean;
|
||||
phase: number;
|
||||
active: string | null;
|
||||
players: readonly OccupancySeat[];
|
||||
@@ -23,7 +22,7 @@ export function isBasicAlliedShipmentVisit(
|
||||
player: OccupancySeat & Readonly<{ shipped?: boolean; moved?: number }>,
|
||||
to: string,
|
||||
): boolean {
|
||||
if (context.advanced || context.phase !== 5 || context.active !== player.id ||
|
||||
if (context.phase !== 5 || context.active !== player.id ||
|
||||
player.shipped !== false || player.moved !== 0 || to === 'polar_sink' ||
|
||||
to === MOBILE_STRONGHOLD) return false;
|
||||
const ally = context.players.find(seat => seat.id === player.ally);
|
||||
|
||||
@@ -9,7 +9,7 @@ import {
|
||||
type PlanetologistMoveMode,
|
||||
} from './planetologist-movement';
|
||||
import { territoryEntryBlock, strongholdPathBlocked } from './occupancy';
|
||||
import { isBasicAlliedShipmentVisit } from './allied-separation';
|
||||
import { isAlliedShipmentVisit } from './allied-separation';
|
||||
import {
|
||||
distance,
|
||||
gameDistance,
|
||||
@@ -67,7 +67,7 @@ export function botEntryAllowed(
|
||||
return false;
|
||||
try {
|
||||
return !territoryEntryBlock(g.players, p.id, to, advisors,
|
||||
kind === 'ship' && isBasicAlliedShipmentVisit(g, p, to));
|
||||
kind === 'ship' && isAlliedShipmentVisit(g, p, to));
|
||||
} catch (error) {
|
||||
if (error instanceof EcazOccupancyError) return false;
|
||||
throw error;
|
||||
|
||||
+8
-5
@@ -3485,6 +3485,7 @@ function policyActions(g: GameView): Action[] {
|
||||
const retained =
|
||||
level > 0 &&
|
||||
g.basicAlliedShipment?.territory !== territory(origin).name &&
|
||||
!g.advancedAllySeparation?.territories.includes(territory(origin).name) &&
|
||||
territory(origin).type === 'stronghold' &&
|
||||
!isAdvisor(me, origin)
|
||||
? 1
|
||||
@@ -4128,13 +4129,15 @@ export function botActions(g: GameView): Action[] {
|
||||
return sourced && botHomeworldShipmentPaymentAllowed(g, sourced) && !homeworldRevivalActionBlock(g, sourced) && !botArrivalBlock(g, sourced) ? [sourced] : [];
|
||||
});
|
||||
const me = g.players.find((p) => p.id === g.me)!;
|
||||
if (g.basicAlliedShipment && g.phase === 5 && g.active === me.id &&
|
||||
me.shipped && me.moved === 0) {
|
||||
// Do not choose an unrelated move or retain a city sentinel while visitors
|
||||
// owe departure. Native response/decision branches do not offer these moves.
|
||||
const advancedDepartures = g.advancedAllySeparation?.territories;
|
||||
if ((g.basicAlliedShipment || advancedDepartures?.length) &&
|
||||
g.phase === 5 && g.active === me.id && me.shipped && me.moved === 0) {
|
||||
// Depart from shared groups before unrelated moves; do not retain a
|
||||
// sentinel that the ending alliance constraint would send to Tanks.
|
||||
const departures = actions.filter(action => action.type === 'move' &&
|
||||
typeof action.from === 'string' &&
|
||||
territory(splitLocation(action.from).territory).name === g.basicAlliedShipment!.territory);
|
||||
(territory(splitLocation(action.from).territory).name === g.basicAlliedShipment?.territory ||
|
||||
advancedDepartures?.includes(territory(splitLocation(action.from).territory).name)));
|
||||
if (departures.length) return departures;
|
||||
}
|
||||
const ghola = standaloneGholaAction(g, actions);
|
||||
|
||||
+4
-4
@@ -567,7 +567,7 @@ import {
|
||||
settleAdvisors,
|
||||
arrivalAsAdvisor,
|
||||
} from './advisors';
|
||||
import { quoteAlliedSeparation, isBasicAlliedShipmentVisit, alliedShipmentPhysicalPool,
|
||||
import { quoteAlliedSeparation, isAlliedShipmentVisit, alliedShipmentPhysicalPool,
|
||||
type BasicAlliedShipment } from './allied-separation';
|
||||
import { casualtyOptions, maxCombatDial, maxCombatSupport, validCombatForces, type Casualties, type CombatForces } from './combat';
|
||||
import { FACTIONS, faction, type FactionId } from './catalog';
|
||||
@@ -25095,7 +25095,7 @@ function validatePhysicalShipment(g: Game, shipment: PendingShipment) {
|
||||
shipment.sector,
|
||||
!shipment.guildSecretEvent && g.advanced && p.faction === 'fremen',
|
||||
shipment.advisors,
|
||||
!shipment.noField && !shipment.alliedNoField && isBasicAlliedShipmentVisit(g, p, shipment.territory),
|
||||
!shipment.noField && !shipment.alliedNoField && isAlliedShipmentVisit(g, p, shipment.territory),
|
||||
);
|
||||
if (p.faction === 'fremen' && !shipment.guildSecretEvent)
|
||||
requireRule(
|
||||
@@ -25594,7 +25594,7 @@ function commitShipment(g: Game, shipment: PendingShipment, settlement?: {recipi
|
||||
}
|
||||
let homeworldOrigins = '';
|
||||
if (!shipment.noField) {
|
||||
if (!shipment.alliedNoField && n > 0 && isBasicAlliedShipmentVisit(g, p, to))
|
||||
if (!g.advanced && !shipment.alliedNoField && n > 0 && isAlliedShipmentVisit(g, p, to))
|
||||
g.basicAlliedShipment = {
|
||||
player: p.id, ally: p.ally!, turn: g.turn, territory: to,
|
||||
baseline: alliedShipmentPhysicalPool(p, to),
|
||||
@@ -30940,7 +30940,7 @@ function applyActionInner(
|
||||
'Only Ixians may ship directly into the mobile stronghold.',
|
||||
);
|
||||
allowedEntry(g, p, to, s, !guildSecretEvent && g.advanced && p.faction === 'fremen', advisors,
|
||||
action.noField === undefined && isBasicAlliedShipmentVisit(g, p, to));
|
||||
action.noField === undefined && isAlliedShipmentVisit(g, p, to));
|
||||
let noField: PendingShipment['noField'];
|
||||
if (action.noField !== undefined) {
|
||||
requireRule(
|
||||
|
||||
+7
-5
@@ -145,7 +145,7 @@ const phaseDetails = [
|
||||
'For movement, select the source forces. Enable Combine sectors to include several groups from the same territory.',
|
||||
'A Fremen move using the two-territory advantage has a Karama response. Cancellation leaves the forces in place and limits this move to one territory; city ornithopters and a later extra move keep their normal rules.',
|
||||
'Guild transport can move one exact physical group across the planet or return it to reserves. In classic games its normal half-price rate can be canceled separately from its transport permission. Ordinary movement is checked against range, storm and stronghold occupancy.',
|
||||
'You may pass through an ally’s territory on a legal route that ends elsewhere. Basic ordinary physical reserve shipments may also enter allied territory before movement, provided the visiting group departs during that turn. Storm and stronghold capacity remain binding. The table visibly labels its provisional no-exit policy: excess new visitors still present when the turn ends go to Tanks, without destroying the ally or granting an extra move. Concealed No-Fields, optional arrivals and Advanced entry remain separate.',
|
||||
'You may pass through an ally’s territory on a legal route that ends elsewhere. Ordinary physical reserve shipments in both modes may enter allied territory before movement, then depart during that turn. Storm and stronghold capacity remain binding. Basic visibly labels its provisional no-exit policy: excess new visitors still present go to Tanks. Advanced follows supplied p15: all the ending player’s shared forces go to Tanks, including old forces, subject to advisor/Polar/Ecaz exemptions. Neither grants an extra move. Concealed No-Fields and optional arrivals remain separate.',
|
||||
'Ecaz and its reciprocal ally may share territories and count as one faction toward stronghold capacity, without extra movement or storm/Homeworld exceptions. Their three-joint-stronghold victory and desert allocation are connected. Fremen/Ecaz cooccupation permits the Fremen special win in Sietch Tabr in Basic; supplied Advanced p32 permits both Tabr and Habbanya Sietch. Other victory conditions and exceptional combined interactions remain separate.',
|
||||
'A restored pending physical shipment is checked again before forces or spice are committed. If its saved turn, force allocation, destination, price or authorized contribution no longer matches the table, it is rejected without changing the saved declaration.',
|
||||
'Finish shipment and movement explicitly. Any Bene Gesserit free-shipment decision and its response must finish first.',
|
||||
@@ -3144,18 +3144,20 @@ export const RULE_TOPICS: RuleTopic[] = [
|
||||
title: 'Advanced alliances: ending shared occupation',
|
||||
category: 'Advanced & expansions',
|
||||
coverage: 'Partial',
|
||||
developmentStage: 'Prototyped',
|
||||
summary: 'Each ending player loses its own shared fighters; advisor coexistence, Polar Sink and native Ecaz coexistence are exempt.',
|
||||
steps: [
|
||||
'Under the adopted unofficial Advanced revision, finish each player’s Shipment and Movement separately. Your own fighters still sharing a territory with your ally go to Tanks when your turn ends. Your ally’s fighters stay; its later turn does not postpone your loss. An alliance formed this turn has no Advanced grace period.',
|
||||
'Polar Sink is exempt. If either allied group is Bene Gesserit advisors, coexistence sends neither group to Tanks. Bene Gesserit fighters have no advisor exemption. Native Ecaz peaceful coexistence keeps its own conditions, including applicable Karama effects.',
|
||||
'Before an ordinary finish, the table warns which public territories would lose your forces. It does not reveal force amounts or concealed No-Field values. Finish movement uses the existing action, without another loss confirmation. If no legal escape is available, you may still finish and accept the mandatory consequence.',
|
||||
'Complete any pending shipment, movement, arrival or response before ordinary finish controls become available. Extra-move and deferred-turn endings use the same separation rule; they do not replay the arrival or charge shipment again.',
|
||||
'Basic still uses its existing later-player deadline and formation-turn exemption, without this advisor exception. Homeworlds are outside this Dune-territory rule and keep their native allied-entry restrictions. The rule grants no new allied-entry permission or alliance formation window, and completed turns are not reevaluated.',
|
||||
'An ordinary physical reserve shipment may enter an allied territory before your movement on your own unused turn. Ship, then move out; stronghold capacity and storm still apply. The ending consequence covers your entire shared group, not only new visitors. No-Fields and optional Homeworld/mobile transport keep their separate legality.',
|
||||
'Basic still uses its older later-player deadline and formation-turn exemption, plus its visibly provisional new-visitor consequence. Homeworlds keep their native allied-entry restrictions. The rule grants no extra shipment, movement or alliance formation window; completed turns are not reevaluated.',
|
||||
],
|
||||
checklist: [
|
||||
{ area: 'Implementation', status: 'Partial', detail: 'One canonical separation quote distinguishes Basic from Advanced timing and exceptions; native ending-group custody remains responsible for physical losses.', evidence: ['game/allied-separation.ts', 'game/engine.ts'] },
|
||||
{ area: 'Player controls', status: 'Partial', detail: 'The own clean-turn warning contains public loss territories only; forced loss adds no action or confirmation and shared pending-control locks retain priority.', evidence: ['components/game-table.tsx'] },
|
||||
{ area: 'AI', status: 'Partial', detail: 'All four legal policies use native projected movement/ending availability and can finish with mandatory loss; no duplicate separation calculator or strategic alliance calibration was added.', evidence: ['game/bots.ts', 'tests/bot-advanced-source.test.ts'] },
|
||||
{ area: 'Implementation', status: 'Partial', detail: 'A shared ordinary reserve-entry quote connects native preflight and commit. Canonical separation distinguishes Basic from Advanced timing and exceptions; native ending-group custody removes all Advanced shared forces.', evidence: ['game/allied-separation.ts', 'game/engine.ts'] },
|
||||
{ area: 'Player controls', status: 'Partial', detail: 'Pre-shipment notice explains the mode-specific consequence; own clean-turn warning contains public loss territories only. Mandatory loss adds no confirmation and pending-control locks retain priority.', evidence: ['components/game-table.tsx'] },
|
||||
{ area: 'AI', status: 'Partial', detail: 'All four legal policies use projected movement/ending availability. Native departure takes priority, without retaining a doomed city sentinel; a Medium typed-arrival/departure smoke passes. No separate separation calculator or strategic calibration was added.', evidence: ['game/bots.ts', 'game/bot-mobility.ts', 'tests/bot-advanced-source.test.ts'] },
|
||||
{ area: 'Documentation', status: 'Implemented', detail: 'The unofficial per-player/advisor departures are distinguished from the earlier publisher/FAQ and unchanged Basic policy.', evidence: ['docs/ALLIED_TRANSIT_RULES.md', 'docs/RULE_DECISIONS.md', 'docs/BASE_ADVANCED_READINESS_20260907.md'] },
|
||||
{ area: 'Verification', status: 'Partial', detail: 'Genuine native overlaps cover first/later newly allied ends, exact normal/elite custody, Hajr, advisor/Ecaz/Polar exceptions and unchanged Basic policy. Authenticated SQLite covers restart, races, privacy and immutable rejects. Actual phone ending moved only the ending group to Tanks and refresh retained it; wider combinations, full modes and deployment remain open.', evidence: ['tests/allied-separation.test.ts', 'tests/advanced-source-engine.test.ts', 'tests/advanced-source-recovery.test.ts', 'tests/bot-advanced-source.test.ts', 'docs/ALLIED_TRANSIT_RULES.md'] },
|
||||
],
|
||||
|
||||
@@ -4,6 +4,7 @@ import {
|
||||
quoteAlliedSeparation,
|
||||
type AlliedSeparationQuoteContext,
|
||||
} from '../game/allied-separation';
|
||||
import { applyAction, createGame, newPlayer } from '../game/engine';
|
||||
|
||||
type TerritoryFacts = AlliedSeparationQuoteContext['territories'][number];
|
||||
|
||||
@@ -196,3 +197,26 @@ void test('quoting frozen canonical facts does not change them or retain a mutab
|
||||
assert.deepEqual(quoteAlliedSeparation(input), ['arrakeen']);
|
||||
assert.deepEqual(input, before);
|
||||
});
|
||||
|
||||
void test('ordinary allied reserve arrival retains distinct Basic visitor and Advanced whole-group ending losses', () => {
|
||||
for (const advanced of [false, true]) {
|
||||
let g = createGame('ALLIEDARRIVAL', newPlayer('e', 'Emperor', 'emperor'), advanced);
|
||||
g.players.push(newPlayer('a', 'Atreides', 'atreides'));
|
||||
g.status = 'playing'; g.phase = 5; g.turn = 2; g.storm = 18;
|
||||
g.active = 'e'; g.order = ['e', 'a']; g.movementRemaining = ['e', 'a'];
|
||||
for (const p of g.players) {
|
||||
p.forces = {}; p.reserves = 20; p.tanks = 0; p.spice = 10;
|
||||
p.hand = []; p.traitors = []; p.shipped = false; p.moved = 0;
|
||||
}
|
||||
g.players[0].ally = 'a'; g.players[1].ally = 'e';
|
||||
g.players[0].forces = { 'arrakeen:10': 5 }; g.players[0].reserves = 15;
|
||||
g.players[1].forces = { 'arrakeen:10': 3 }; g.players[1].reserves = 17;
|
||||
g = applyAction(g, 'e', { type: 'ship', amount: 2, territory: 'arrakeen', sector: 10 });
|
||||
assert.equal(g.players[0].spice, 8);
|
||||
g = applyAction(g, 'e', { type: 'endMovement' });
|
||||
assert.equal(g.players[0].tanks, advanced ? 7 : 2);
|
||||
assert.equal(g.players[0].forces['arrakeen:10'] ?? 0, advanced ? 0 : 5);
|
||||
assert.equal(g.players[1].forces['arrakeen:10'], 3);
|
||||
assert.equal(g.players[1].tanks, 0);
|
||||
}
|
||||
});
|
||||
Reference in new issue
Block a user