Implement nose coupling so run-arounds work, and stop coupling wiping the district
This commit is contained in:
+40
-5
@@ -689,7 +689,26 @@ function execute(s: GameState, player: PlayerIndex, i: Intent): GameEvent[] {
|
||||
},
|
||||
];
|
||||
if (dest.couples.length > 0) {
|
||||
events.push({ type: 'carsCoupled', trayId: i.trayId, at: i.to, stock: dest.couples });
|
||||
// Name the cards the cars came from. The reducer used to clear every card in the district
|
||||
// instead, so one coupling anywhere deleted every standing car and every industry track in
|
||||
// the Office Area — loads worked over several Stages vanished when a crew picked up a single
|
||||
// boxcar somewhere else entirely.
|
||||
const lifted = [
|
||||
...dest.path.map((step) => step.coord),
|
||||
i.to,
|
||||
].filter((c) => carsOn(areaOf(s, player).grid.get(coordKey(c)) ?? emptyCard()).length > 0);
|
||||
// §A.3 — "engines also have couplers on the front end, so a train can pick cars up onto
|
||||
// its nose". Running forward the engine meets cars head-on and takes them in front; backing
|
||||
// up, they couple behind. Which end they land on is the whole point of a run-around: it
|
||||
// decides which car is next to come off.
|
||||
events.push({
|
||||
type: 'carsCoupled',
|
||||
trayId: i.trayId,
|
||||
at: i.to,
|
||||
stock: dest.couples,
|
||||
from: lifted,
|
||||
toNose: !i.reverse,
|
||||
});
|
||||
}
|
||||
return events;
|
||||
}
|
||||
@@ -992,10 +1011,14 @@ export function reduce(s: GameState, e: GameEvent): void {
|
||||
case 'carsCoupled': {
|
||||
const tray = s.trays.get(e.trayId)!;
|
||||
const area = areaOf(s, tray.position.at === 'grid' ? tray.position.owner : 0);
|
||||
tray.consist.push(...e.stock);
|
||||
// Every card along the path is cleared; the event carries what was taken.
|
||||
for (const card of area.grid.values()) {
|
||||
if (card.standing.length > 0) card.standing = [];
|
||||
if (e.toNose) tray.consist.unshift(...e.stock);
|
||||
else tray.consist.push(...e.stock);
|
||||
// ONLY the cards the crew ran over. Clearing the whole grid emptied industry tracks the crew
|
||||
// never went near.
|
||||
for (const coord of e.from) {
|
||||
const card = area.grid.get(coordKey(coord));
|
||||
if (!card) continue;
|
||||
card.standing = [];
|
||||
if (card.facility) card.facility.industryTrack.cars = [];
|
||||
}
|
||||
break;
|
||||
@@ -1485,6 +1508,18 @@ export function checkEnhancementPlacement(
|
||||
}
|
||||
}
|
||||
|
||||
/** A stand-in with nothing on it, so a missing card reads as "no cars here" rather than throwing. */
|
||||
function emptyCard(): TrackCard {
|
||||
return {
|
||||
geometry: { kind: 'limits' },
|
||||
baseOperationalRail: false,
|
||||
standing: [],
|
||||
facility: null,
|
||||
modifiers: [],
|
||||
enhancements: [],
|
||||
};
|
||||
}
|
||||
|
||||
/** Removes a played card from its owner's hand and sends it to the Salvage Yard. */
|
||||
function spendCard(s: GameState, player: PlayerIndex, cardId: CardId): void {
|
||||
s.decks.hands.set(player, (s.decks.hands.get(player) ?? []).filter((c) => c !== cardId));
|
||||
|
||||
Reference in New Issue
Block a user