v0.4.2 fixed passenger trains not stopping at office. changeed revenue for train completing run - 1 point to every player when train exits division
This commit is contained in:
+47
-10
@@ -457,12 +457,35 @@ function refusesThisOffice(s: GameState, player: PlayerIndex, tray: CrewTray): b
|
||||
* this works out whether a card is the reason. Without it a Military train standing at the platform
|
||||
* reported "no train at the Office", which is both wrong and unhelpful.
|
||||
*/
|
||||
function passengerRefusal(s: GameState, player: PlayerIndex): RejectionCode {
|
||||
function passengerRefusal(
|
||||
s: GameState,
|
||||
player: PlayerIndex,
|
||||
at: GridCoord,
|
||||
dir: 'board' | 'detrain',
|
||||
): RejectionCode {
|
||||
const area = areaOf(s, player);
|
||||
const trains = area.adOccupancy.map((id) => s.trays.get(id)).filter((t): t is CrewTray => !!t);
|
||||
if (trains.length > 0 && trains.every((t) => refusesThisOffice(s, player, t))) return 'NOT_A_TERMINAL';
|
||||
if (trains.length > 0 && trains.every(refusesPassengers)) return 'NO_PASSENGER_WORK';
|
||||
return 'NO_TRAIN_AT_OFFICE';
|
||||
if (trains.length === 0) return 'NO_TRAIN_AT_OFFICE';
|
||||
|
||||
/**
|
||||
* A TRAIN IS STANDING THERE, so say what is actually missing.
|
||||
*
|
||||
* This used to fall through to `NO_TRAIN_AT_OFFICE` — told to a player looking straight at a train
|
||||
* on their own A/D track, which reads as a broken game rather than a rule. Measured over 60
|
||||
* solitaire games it fired 51 times with a coach train in front of the player: 27 with nobody
|
||||
* waiting to travel, 24 with passengers waiting and every coach already full.
|
||||
*/
|
||||
const f = facilityAt(s, player, at);
|
||||
if (!f || f.kind !== 'passenger') return 'NO_SUCH_FACILITY';
|
||||
if (dir === 'board') {
|
||||
if (!f.outboundBox.some((c) => c.type === 'coach' && c.loaded)) return 'NO_PASSENGERS_WAITING';
|
||||
return 'NO_EMPTY_COACH';
|
||||
}
|
||||
if (f.inboundBox.length >= f.capacity.inbound) return 'INBOUND_BOX_FULL';
|
||||
if (!s.yards.divisionYard.some((c) => c.type === 'coach' && !c.loaded)) return 'NO_EMPTY_COACH_IN_YARD';
|
||||
return 'NO_LOADED_COACH';
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
@@ -790,17 +813,31 @@ export function check(s: GameState, player: PlayerIndex, i: Intent): RejectionCo
|
||||
}
|
||||
|
||||
// -- Load / Unload --------------------------------------------------------
|
||||
case 'porter.board':
|
||||
/**
|
||||
* A WHISTLE POST HAS NO PORTERS AT ALL, which is not the same as having used them.
|
||||
*
|
||||
* The Office card carries a passenger facility at every tier so that an upgrade is a property
|
||||
* change rather than a card swap — but a Whistle Post's has `porters: 0`, so this fell into
|
||||
* `RESOURCE_SPENT`, "all Porters already used this Stage". A player who had used nothing was
|
||||
* told they had spent it all, when the answer was to upgrade the Office. 15 times in 60 games.
|
||||
*/
|
||||
case 'porter.board': {
|
||||
if (!inPhase(s, 'loadUnload')) return 'WRONG_PHASE';
|
||||
if (!facilityAt(s, player, i.at)) return 'NO_SUCH_FACILITY';
|
||||
if (portersLeft(facilityAt(s, player, i.at)!) < 1) return 'RESOURCE_SPENT';
|
||||
return canBoard(s, player, i.at) ? null : passengerRefusal(s, player);
|
||||
const f = facilityAt(s, player, i.at);
|
||||
if (!f) return 'NO_SUCH_FACILITY';
|
||||
if (f.porters < 1) return 'NO_PORTERS_HERE';
|
||||
if (portersLeft(f) < 1) return 'RESOURCE_SPENT';
|
||||
return canBoard(s, player, i.at) ? null : passengerRefusal(s, player, i.at, 'board');
|
||||
}
|
||||
|
||||
case 'porter.detrain':
|
||||
case 'porter.detrain': {
|
||||
if (!inPhase(s, 'loadUnload')) return 'WRONG_PHASE';
|
||||
if (!facilityAt(s, player, i.at)) return 'NO_SUCH_FACILITY';
|
||||
if (portersLeft(facilityAt(s, player, i.at)!) < 1) return 'RESOURCE_SPENT';
|
||||
return canDetrain(s, player, i.at) ? null : passengerRefusal(s, player);
|
||||
const f = facilityAt(s, player, i.at);
|
||||
if (!f) return 'NO_SUCH_FACILITY';
|
||||
if (f.porters < 1) return 'NO_PORTERS_HERE';
|
||||
if (portersLeft(f) < 1) return 'RESOURCE_SPENT';
|
||||
return canDetrain(s, player, i.at) ? null : passengerRefusal(s, player, i.at, 'detrain');
|
||||
}
|
||||
|
||||
case 'laborer.startLoad': {
|
||||
if (!inPhase(s, 'loadUnload')) return 'WRONG_PHASE';
|
||||
|
||||
Reference in New Issue
Block a user