541 lines
19 KiB
TypeScript
541 lines
19 KiB
TypeScript
import test from 'node:test';
|
|
import assert from 'node:assert/strict';
|
|
import {
|
|
applyAction,
|
|
normalizeAutomaticGame,
|
|
viewGame,
|
|
type Game,
|
|
type Action,
|
|
} from '../game/engine';
|
|
import { passNativeGuildTransportPermission } from './fixture-guild-ambassador-income-arrival';
|
|
import {
|
|
nexusGuildCunningFixture,
|
|
originalGuildCunningTurn,
|
|
nexusGuildCunningRequest,
|
|
nexusGuildCunningAllow,
|
|
nexusGuildCunningReload,
|
|
nexusGuildCunningInventory,
|
|
settleGuildCunningShipment,
|
|
holdGuildCunningCard,
|
|
nexusGuildCunningAllianceFixture,
|
|
} from './fixture-nexus-guild-cunning';
|
|
function rejected(g: Game, id: string, action: Action) {
|
|
const before = structuredClone(g);
|
|
assert.throws(() => applyAction(g, id, action));
|
|
assert.deepEqual(g, before);
|
|
}
|
|
function stable(g: Game) {
|
|
nexusGuildCunningInventory(g);
|
|
for (const p of g.players)
|
|
assert.deepEqual(
|
|
viewGame(nexusGuildCunningReload(g), p.id),
|
|
viewGame(g, p.id),
|
|
);
|
|
assert.deepEqual(
|
|
normalizeAutomaticGame(nexusGuildCunningReload(g)),
|
|
nexusGuildCunningReload(g),
|
|
);
|
|
}
|
|
|
|
void test('an actual Moritani alliance formed on the second arrival preserves the earned Guild Hajr continuation', () => {
|
|
const f = nexusGuildCunningAllianceFixture();
|
|
const hajr = holdGuildCunningCard(f.g, f.owner, 'hajr');
|
|
let g = originalGuildCunningTurn(f);
|
|
g = nexusGuildCunningAllow(
|
|
applyAction(g, f.owner, nexusGuildCunningRequest(f, g)),
|
|
);
|
|
g = settleGuildCunningShipment(
|
|
applyAction(g, f.owner, {
|
|
type: 'ship',
|
|
territory: 'carthag',
|
|
sector: 11,
|
|
amount: 3,
|
|
}),
|
|
);
|
|
assert.equal(g.pendingTerrorEntry?.stage, 'offer');
|
|
g = nexusGuildCunningAllow(
|
|
applyAction(g, f.target, { type: 'decision', alliance: true }),
|
|
);
|
|
stable(g);
|
|
const before = structuredClone(g.players[0]);
|
|
g = applyAction(nexusGuildCunningReload(g), f.owner, {
|
|
type: 'decision',
|
|
accept: true,
|
|
});
|
|
assert.equal(g.players[0].ally, f.target);
|
|
assert.equal(g.players[1].ally, f.owner);
|
|
assert.equal(g.players[0].spice, before.spice);
|
|
assert.equal(g.players[0].reserves, before.reserves);
|
|
stable(g);
|
|
g = applyAction(g, f.owner, { type: 'card', card: hajr.id });
|
|
g = applyAction(g, f.owner, {
|
|
type: 'move',
|
|
from: 'carthag:11',
|
|
territory: 'hagga_basin',
|
|
sector: 12,
|
|
amount: 3,
|
|
});
|
|
assert.equal(g.players[0].forces['hagga_basin:12'], 8);
|
|
assert.equal(g.players[0].spice, before.spice);
|
|
if (g.active === f.owner)
|
|
g = applyAction(g, f.owner, { type: 'endMovement' });
|
|
assert.notEqual(g.active, f.owner);
|
|
stable(g);
|
|
});
|
|
|
|
void test('Guild special Karama stops its own second shipment without payment or arrival and preserves only the unused Hajr move', () => {
|
|
for (const homeworlds of [false, true]) {
|
|
const f = nexusGuildCunningFixture({ advanced: true, homeworlds });
|
|
const karama = holdGuildCunningCard(f.g, f.owner, 'karama');
|
|
const hajr = holdGuildCunningCard(f.g, f.owner, 'hajr');
|
|
let g = originalGuildCunningTurn(f, false);
|
|
g = nexusGuildCunningAllow(
|
|
applyAction(g, f.owner, nexusGuildCunningRequest(f, g)),
|
|
);
|
|
const before = structuredClone(g.players[0]);
|
|
const shipment: Action = homeworlds
|
|
? {
|
|
type: 'homeworldShip',
|
|
event: viewGame(g, f.owner).homeworldShipment!.event,
|
|
destination: 'homeworld:harkonnen',
|
|
sources: { 'homeworld:guild': { normal: 3, elite: 0 } },
|
|
}
|
|
: { type: 'ship', territory: 'carthag', sector: 11, amount: 3 };
|
|
g = applyAction(g, f.owner, shipment);
|
|
assert.equal(
|
|
g.decision?.kind,
|
|
homeworlds ? 'homeworldShipmentGuild' : 'guildShipment',
|
|
);
|
|
stable(g);
|
|
g = applyAction(nexusGuildCunningReload(g), f.owner, {
|
|
type: 'card',
|
|
mode: 'special',
|
|
card: karama.id,
|
|
});
|
|
assert.equal(
|
|
g.nexusGuildCunningHistory!.at(-1)!.shipment!.stage,
|
|
'stopped',
|
|
);
|
|
assert.equal(g.players[0].spice, before.spice);
|
|
assert.equal(g.players[0].reserves, before.reserves);
|
|
assert.deepEqual(g.players[0].forces, before.forces);
|
|
assert.equal(g.nexusCards!.cards!.hands[f.owner], null);
|
|
assert.equal(
|
|
g.nexusCards!.cards!.discard.filter((c) => c === 'guild').length,
|
|
1,
|
|
);
|
|
if (homeworlds)
|
|
assert.equal(
|
|
g.homeworlds!.custody!.visitors['homeworld:harkonnen']?.[f.owner],
|
|
undefined,
|
|
);
|
|
const move: Action = {
|
|
type: 'move',
|
|
from: 'arrakeen:10',
|
|
territory: 'hagga_basin',
|
|
sector: 12,
|
|
amount: 5,
|
|
};
|
|
rejected(g, f.owner, move);
|
|
rejected(g, f.owner, shipment);
|
|
g = applyAction(nexusGuildCunningReload(g), f.owner, {
|
|
type: 'card',
|
|
card: hajr.id,
|
|
});
|
|
g = applyAction(g, f.owner, move);
|
|
rejected(g, f.owner, {
|
|
type: 'move',
|
|
from: 'hagga_basin:12',
|
|
territory: 'arsunt',
|
|
sector: 12,
|
|
amount: 5,
|
|
});
|
|
assert.equal(g.players[0].spice, before.spice);
|
|
stable(g);
|
|
}
|
|
});
|
|
|
|
void test('native and Cunning cross-shipments cannot manufacture a shipment within the same territory', () => {
|
|
const f = nexusGuildCunningFixture();
|
|
const native = nexusGuildCunningFixture();
|
|
native.g.players[0].reserves -= 5;
|
|
native.g.players[0].forces['arrakeen:10'] = 5;
|
|
nexusGuildCunningInventory(native.g);
|
|
let g = originalGuildCunningTurn(f, false);
|
|
const same: Action = {
|
|
type: 'guildShip',
|
|
from: 'arrakeen:10',
|
|
territory: 'arrakeen',
|
|
sector: 10,
|
|
amount: 1,
|
|
};
|
|
rejected(native.g, native.owner, same);
|
|
g = nexusGuildCunningAllow(
|
|
applyAction(g, f.owner, nexusGuildCunningRequest(f, g)),
|
|
);
|
|
rejected(g, f.owner, same);
|
|
stable(g);
|
|
});
|
|
|
|
void test('Guild Cunning adds a paid native reserve, cross or return shipment after the actual original movement without resetting flags', () => {
|
|
for (const advanced of [false, true])
|
|
for (const route of ['reserve', 'cross', 'return'] as const) {
|
|
const f = nexusGuildCunningFixture({ advanced, karama: true });
|
|
let g = originalGuildCunningTurn(f);
|
|
const before = nexusGuildCunningReload(g);
|
|
const request = nexusGuildCunningRequest(f, g);
|
|
g = applyAction(g, f.owner, request);
|
|
assert.equal(g.response?.kind, 'nexusGuildCunning');
|
|
assert.equal(g.players[0].shipped, true);
|
|
assert.equal(g.players[0].moved, 1);
|
|
assert.equal(g.nexusCards!.cards!.hands[f.owner], null);
|
|
stable(g);
|
|
g = nexusGuildCunningAllow(nexusGuildCunningReload(g));
|
|
assert.equal(
|
|
viewGame(g, f.owner).nexusGuildCunning!.active!.stage,
|
|
'secondShipment',
|
|
);
|
|
const action: Action =
|
|
route === 'reserve'
|
|
? { type: 'ship', territory: 'carthag', sector: 11, amount: 5 }
|
|
: {
|
|
type: 'guildShip',
|
|
from: 'hagga_basin:12',
|
|
territory: route === 'return' ? 'reserves' : 'carthag',
|
|
sector: route === 'return' ? 0 : 11,
|
|
amount: 5,
|
|
};
|
|
g = settleGuildCunningShipment(applyAction(g, f.owner, action));
|
|
assert.equal(g.players[0].spice, before.players[0].spice - 3);
|
|
assert.equal(g.players[0].moved, 1);
|
|
assert.equal(g.players[0].shipped, true);
|
|
assert.equal(
|
|
g.players[0].reserves,
|
|
route === 'reserve' ? 10 : route === 'return' ? 20 : 15,
|
|
);
|
|
if (route !== 'return')
|
|
assert.equal(g.players[0].forces['carthag:11'], 5);
|
|
rejected(g, f.owner, action);
|
|
rejected(g, f.owner, {
|
|
type: 'move',
|
|
from: 'carthag:11',
|
|
territory: 'hagga_basin',
|
|
sector: 12,
|
|
amount: 1,
|
|
});
|
|
assert.equal(
|
|
g.nexusCards!.cards!.discard.filter((c) => c === 'guild').length,
|
|
1,
|
|
);
|
|
stable(g);
|
|
}
|
|
});
|
|
|
|
void test('canceling Guild Cunning preserves the first paid shipment and movement and does not reset the real turn queue', () => {
|
|
for (const advanced of [false, true]) {
|
|
const f = nexusGuildCunningFixture({ advanced, karama: true });
|
|
let g = originalGuildCunningTurn(f);
|
|
const before = nexusGuildCunningReload(g),
|
|
request = nexusGuildCunningRequest(f, g);
|
|
g = applyAction(g, f.owner, request);
|
|
g = applyAction(nexusGuildCunningReload(g), f.target, {
|
|
type: 'card',
|
|
mode: 'cancel',
|
|
card: f.karama,
|
|
});
|
|
assert.deepEqual(g.players[0].forces, before.players[0].forces);
|
|
assert.equal(g.players[0].spice, before.players[0].spice);
|
|
assert.equal(g.players[0].moved, 1);
|
|
assert.equal(g.players[0].shipped, true);
|
|
assert.equal(g.movementRemaining!.includes(f.owner), false);
|
|
assert.notEqual(g.active, f.owner);
|
|
assert.equal(g.discard.filter((c) => c.id === f.karama).length, 1);
|
|
rejected(g, f.owner, request);
|
|
rejected(g, f.owner, {
|
|
type: 'ship',
|
|
territory: 'carthag',
|
|
sector: 11,
|
|
amount: 1,
|
|
});
|
|
stable(g);
|
|
}
|
|
});
|
|
|
|
void test('declining ordinary movement does not leave a free movement after the second Guild shipment', () => {
|
|
for (const originalShip of [false, true]) {
|
|
const f = nexusGuildCunningFixture();
|
|
let g = originalShip ? originalGuildCunningTurn(f, false) : f.g;
|
|
g = nexusGuildCunningAllow(
|
|
applyAction(g, f.owner, nexusGuildCunningRequest(f, g)),
|
|
);
|
|
g = settleGuildCunningShipment(
|
|
applyAction(g, f.owner, {
|
|
type: 'ship',
|
|
territory: 'carthag',
|
|
sector: 11,
|
|
amount: 3,
|
|
}),
|
|
);
|
|
assert.equal(g.players[0].moved, 0);
|
|
rejected(g, f.owner, {
|
|
type: 'move',
|
|
from: 'carthag:11',
|
|
territory: 'hagga_basin',
|
|
sector: 12,
|
|
amount: 3,
|
|
});
|
|
stable(g);
|
|
}
|
|
});
|
|
|
|
void test('Hajr grants only its remaining extra movement before or after the second shipment and cannot create a third move', () => {
|
|
for (const mode of ['late', 'early-unspent', 'early-used'] as const) {
|
|
const f = nexusGuildCunningFixture();
|
|
const hajr = holdGuildCunningCard(f.g, f.owner, 'hajr');
|
|
let g = originalGuildCunningTurn(f);
|
|
if (mode !== 'late')
|
|
g = applyAction(g, f.owner, { type: 'card', card: hajr.id });
|
|
if (mode === 'early-used')
|
|
g = applyAction(g, f.owner, {
|
|
type: 'move',
|
|
from: 'hagga_basin:12',
|
|
territory: 'arsunt',
|
|
sector: 12,
|
|
amount: 5,
|
|
});
|
|
g = nexusGuildCunningAllow(
|
|
applyAction(g, f.owner, nexusGuildCunningRequest(f, g)),
|
|
);
|
|
g = settleGuildCunningShipment(
|
|
applyAction(g, f.owner, {
|
|
type: 'ship',
|
|
territory: 'carthag',
|
|
sector: 11,
|
|
amount: 3,
|
|
}),
|
|
);
|
|
if (mode === 'late')
|
|
g = applyAction(g, f.owner, { type: 'card', card: hajr.id });
|
|
const move: Action = {
|
|
type: 'move',
|
|
from: 'carthag:11',
|
|
territory: 'hagga_basin',
|
|
sector: 12,
|
|
amount: 3,
|
|
};
|
|
if (mode === 'early-used') rejected(g, f.owner, move);
|
|
else {
|
|
g = applyAction(g, f.owner, move);
|
|
assert.equal(g.players[0].moved, 2);
|
|
rejected(g, f.owner, {
|
|
type: 'move',
|
|
from: 'hagga_basin:12',
|
|
territory: 'arrakeen',
|
|
sector: 10,
|
|
amount: 1,
|
|
});
|
|
}
|
|
assert.equal(g.discard.filter((c) => c.id === hajr.id).length, 1);
|
|
stable(g);
|
|
}
|
|
});
|
|
|
|
void test('declining the second shipment preserves paid first forces and permits only an unused Hajr move after declining ordinary movement', () => {
|
|
const f = nexusGuildCunningFixture();
|
|
const hajr = holdGuildCunningCard(f.g, f.owner, 'hajr');
|
|
let g = originalGuildCunningTurn(f, false);
|
|
g = nexusGuildCunningAllow(
|
|
applyAction(g, f.owner, nexusGuildCunningRequest(f, g)),
|
|
);
|
|
const skip: Action = {
|
|
type: 'nexusGuildSkipShipment',
|
|
event: viewGame(g, f.owner).nexusGuildCunning!.active!.event,
|
|
};
|
|
rejected(g, f.target, skip);
|
|
rejected(g, f.owner, { ...skip, event: 'stale' });
|
|
g = applyAction(g, f.owner, skip);
|
|
assert.equal(g.players[0].spice, 17);
|
|
assert.equal(g.players[0].reserves, 15);
|
|
assert.equal(g.players[0].shipped, true);
|
|
assert.equal(g.players[0].moved, 0);
|
|
stable(g);
|
|
const move: Action = {
|
|
type: 'move',
|
|
from: 'arrakeen:10',
|
|
territory: 'hagga_basin',
|
|
sector: 12,
|
|
amount: 5,
|
|
};
|
|
rejected(g, f.owner, move);
|
|
rejected(g, f.owner, skip);
|
|
g = applyAction(nexusGuildCunningReload(g), f.owner, {
|
|
type: 'card',
|
|
card: hajr.id,
|
|
});
|
|
g = applyAction(g, f.owner, move);
|
|
assert.equal(g.players[0].forces['hagga_basin:12'], 5);
|
|
assert.equal(g.players[0].spice, 17);
|
|
rejected(g, f.owner, {
|
|
type: 'move',
|
|
from: 'hagga_basin:12',
|
|
territory: 'arsunt',
|
|
sector: 12,
|
|
amount: 5,
|
|
});
|
|
assert.equal(g.discard.filter((c) => c.id === hajr.id).length, 1);
|
|
stable(g);
|
|
});
|
|
|
|
void test('Guild Cunning keeps declared ownership private before play and rejects stale, foreign or duplicate declarations', () => {
|
|
const f = nexusGuildCunningFixture();
|
|
let g = originalGuildCunningTurn(f);
|
|
const request = nexusGuildCunningRequest(f, g);
|
|
for (const id of [f.target, f.observer])
|
|
assert.equal(viewGame(g, id).nexusGuildCunning, null);
|
|
rejected(g, f.owner, { ...request, nexus: 'old' });
|
|
rejected(g, f.target, request);
|
|
g = nexusGuildCunningAllow(applyAction(g, f.owner, request));
|
|
rejected(g, f.owner, request);
|
|
for (const p of g.players)
|
|
assert.equal(
|
|
Object.hasOwn(viewGame(g, p.id), 'nexusGuildCunningHistory'),
|
|
false,
|
|
);
|
|
stable(g);
|
|
});
|
|
|
|
void test('a second Guild shipment can return actual Arrakis counters to Junction through the typed native Homeworld route', () => {
|
|
const f = nexusGuildCunningFixture({ homeworlds: true });
|
|
let g = originalGuildCunningTurn(f);
|
|
g = nexusGuildCunningAllow(
|
|
applyAction(g, f.owner, nexusGuildCunningRequest(f, g)),
|
|
);
|
|
const option = viewGame(g, f.owner).guildHomeworldShipment!;
|
|
assert.equal(option.blocked, null);
|
|
g = applyAction(g, f.owner, {
|
|
type: 'guildHomeworldShip',
|
|
event: option.event,
|
|
destination: 'homeworld:guild',
|
|
sources: { 'hagga_basin:12': { normal: 5, elite: 0 } },
|
|
});
|
|
assert.equal(g.players[0].reserves, 20);
|
|
assert.equal(g.players[0].forces['hagga_basin:12'], undefined);
|
|
assert.equal(g.players[0].spice, 14);
|
|
stable(g);
|
|
const outward = nexusGuildCunningFixture({ homeworlds: true });
|
|
let next = originalGuildCunningTurn(outward);
|
|
next = nexusGuildCunningAllow(
|
|
applyAction(next, outward.owner, nexusGuildCunningRequest(outward, next)),
|
|
);
|
|
const outwardOption = viewGame(next, outward.owner).homeworldShipment!;
|
|
assert.equal(outwardOption.blocked, null);
|
|
next = applyAction(next, outward.owner, {
|
|
type: 'homeworldShip',
|
|
event: outwardOption.event,
|
|
destination: 'homeworld:harkonnen',
|
|
sources: { 'homeworld:guild': { normal: 2, elite: 0 } },
|
|
});
|
|
assert.equal(next.players[0].reserves, 13);
|
|
assert.equal(
|
|
next.homeworlds!.custody!.visitors['homeworld:harkonnen'][outward.owner]
|
|
.normal,
|
|
2,
|
|
);
|
|
assert.equal(next.players[0].spice, 16);
|
|
stable(next);
|
|
});
|
|
|
|
void test('Advanced Guild Cunning finishes within a genuinely granted out-of-order combined turn', () => {
|
|
const f = nexusGuildCunningFixture({ advanced: true, karama: true });
|
|
let g = f.g;
|
|
// The first phase-five position is only fixture staging. Enter its native
|
|
// turn queue through an actual completed Revival boundary before acting.
|
|
Object.assign(g, {
|
|
phase: 4,
|
|
active: null,
|
|
ready: [],
|
|
order: [f.target, f.observer, f.owner],
|
|
});
|
|
for (const p of g.players) g = applyAction(g, p.id, { type: 'ready' });
|
|
assert.equal(g.decision?.kind, 'guildTiming');
|
|
g = applyAction(g, f.owner, { type: 'decision', take: true });
|
|
g = nexusGuildCunningAllow(g);
|
|
assert.equal(g.active, f.owner);
|
|
assert.equal(g.guildTimingGranted, true);
|
|
g = originalGuildCunningTurn(f, true, g);
|
|
g = nexusGuildCunningAllow(
|
|
applyAction(g, f.owner, nexusGuildCunningRequest(f, g)),
|
|
);
|
|
g = settleGuildCunningShipment(
|
|
applyAction(g, f.owner, {
|
|
type: 'ship',
|
|
territory: 'carthag',
|
|
sector: 11,
|
|
amount: 3,
|
|
}),
|
|
);
|
|
if (g.active === f.owner)
|
|
g = applyAction(g, f.owner, { type: 'endMovement' });
|
|
assert.equal(g.movementRemaining!.includes(f.owner), false);
|
|
assert.equal(g.active, f.target);
|
|
assert.equal(new Set(g.movementRemaining).size, g.movementRemaining!.length);
|
|
stable(g);
|
|
});
|
|
|
|
for (const route of ['reserve', 'transport'] as const) {
|
|
for (const outcome of ['allow', 'cancel', 'unaffordable'] as const) {
|
|
void test(`Guild Cunning ${route} preserves its second allowance through native rate ${outcome}`, () => {
|
|
// Existing controlled final-setup fixture; distinct from the original
|
|
// action-only expansion/Nexus producer in native-guild-rate-karama.
|
|
const f = nexusGuildCunningFixture({ advanced: true, karama: true });
|
|
let g = originalGuildCunningTurn(f, false);
|
|
g = nexusGuildCunningAllow(
|
|
applyAction(g, f.owner, nexusGuildCunningRequest(f, g)),
|
|
);
|
|
if (outcome === 'unaffordable')
|
|
g = applyAction(g, f.owner, { type: 'bribe', target: f.observer,
|
|
amount: g.players[0].spice - 2 });
|
|
const before = structuredClone(g.players[0]);
|
|
const shipment: Action = route === 'reserve'
|
|
? { type: 'ship', territory: 'carthag', sector: 11, amount: 3 }
|
|
: { type: 'guildShip', from: 'arrakeen:10', territory: 'carthag', sector: 11, amount: 3 };
|
|
g = applyAction(g, f.owner, shipment);
|
|
if (route === 'reserve') {
|
|
assert.equal(g.decision?.kind, 'guildShipment');
|
|
g = applyAction(g, f.owner, { type: 'decision', allow: true });
|
|
}
|
|
g = passNativeGuildTransportPermission(g).game;
|
|
assert.equal(g.response?.kind, 'guildRate');
|
|
assert.equal(g.players[0].shipped, true);
|
|
assert.equal(g.nexusGuildCunningLast?.stage, 'secondShipment');
|
|
assert.equal(g.players[0].spice, before.spice);
|
|
assert.equal(g.players[0].reserves, before.reserves);
|
|
assert.deepEqual(g.players[0].forces, before.forces);
|
|
g = outcome === 'allow' ? nexusGuildCunningAllow(g)
|
|
: applyAction(g, f.target, { type: 'card', mode: 'cancel', card: f.karama! });
|
|
let amount = 3, cost = outcome === 'allow' ? 2 : 3;
|
|
if (outcome === 'unaffordable') {
|
|
assert.equal(g.nexusGuildCunningLast?.stage, 'secondShipment');
|
|
assert.ok(!g.nexusGuildCunningHistory!.at(-1)!.shipment);
|
|
assert.equal(g.players[0].spice, before.spice);
|
|
assert.equal(g.players[0].reserves, before.reserves);
|
|
assert.deepEqual(g.players[0].forces, before.forces);
|
|
assert.equal(viewGame(g, f.owner).guildRateCanceled, true);
|
|
g = settleGuildCunningShipment(applyAction(g, f.owner, { ...shipment, amount: 2 }));
|
|
amount = cost = 2;
|
|
}
|
|
assert.equal(g.nexusGuildCunningLast?.stage, 'extraMove');
|
|
assert.equal(g.nexusGuildCunningHistory!.at(-1)!.shipment!.stage, 'complete');
|
|
assert.equal(g.players[0].spice, before.spice - cost);
|
|
assert.equal(g.players[0].reserves, before.reserves - (route === 'reserve' ? amount : 0));
|
|
assert.equal(g.players[0].forces['arrakeen:10'],
|
|
before.forces['arrakeen:10'] - (route === 'transport' ? amount : 0));
|
|
assert.equal(g.players[0].forces['carthag:11'], amount);
|
|
assert.equal(g.discard.filter(card => card.id === f.karama).length, outcome === 'allow' ? 0 : 1);
|
|
g = applyAction(g, f.owner, { type: 'endMovement' });
|
|
assert.notEqual(g.active, f.owner);
|
|
assert.equal(g.nexusGuildCunningLast?.stage, 'completed');
|
|
});
|
|
}
|
|
}
|