From 2d2d0e8b5ac341d18b06e3dfaf126c27a66249b9 Mon Sep 17 00:00:00 2001 From: scamiv <6170744+scamiv@users.noreply.github.com> Date: Fri, 21 Nov 2025 16:19:14 +0100 Subject: [PATCH] Add dynamic railroad fares based on segment length and congestion - Track train counts per railroad segment - expose a fare API on segments - charge players when entering each segment with visual feedback --- src/core/execution/TrainExecution.ts | 29 ++++++++++++++++++++++++++++ src/core/game/Railroad.ts | 29 ++++++++++++++++++++++++++++ 2 files changed, 58 insertions(+) diff --git a/src/core/execution/TrainExecution.ts b/src/core/execution/TrainExecution.ts index 501f599b5..9048c8cd4 100644 --- a/src/core/execution/TrainExecution.ts +++ b/src/core/execution/TrainExecution.ts @@ -1,6 +1,7 @@ import { Execution, Game, + MessageType, Player, TrainType, Unit, @@ -107,6 +108,29 @@ export class TrainExecution implements Execution { }; } + private enterRailroad(railroad: OrientedRailroad) { + const rail = railroad.getRailroad(); + rail.incrementTrainCount(); + const fare = rail.getFare(); + this.player.addGold(-fare, railroad.getStart().tile()); + if (this.mg) { + this.mg.displayMessage( + "Paid railroad fare", + MessageType.RECEIVED_GOLD_FROM_TRADE, + this.player.id(), + -fare, + ); + } + } + + private leaveRailroad() { + if (!this.currentRailroad) { + return; + } + const rail = this.currentRailroad.getRailroad(); + rail.decrementTrainCount(); + } + init(mg: Game, ticks: number): void { this.mg = mg; @@ -215,6 +239,8 @@ export class TrainExecution implements Execution { private deleteTrain() { this.active = false; + this.leaveRailroad(); + if (this.train?.isActive()) { this.train.delete(false); } @@ -337,8 +363,11 @@ export class TrainExecution implements Execution { // This should happen after arrival processing but before departure this.journeyHopCount++; + // Move to the next station and railroad, updating fare/usage tracking this.currentStation = nextHop; + this.leaveRailroad(); this.currentRailroad = railroad; + this.enterRailroad(railroad); this.currentTile = 0; } diff --git a/src/core/game/Railroad.ts b/src/core/game/Railroad.ts index 8b0f3086a..51c474eb2 100644 --- a/src/core/game/Railroad.ts +++ b/src/core/game/Railroad.ts @@ -4,6 +4,8 @@ import { GameUpdateType, RailTile, RailType } from "./GameUpdates"; import { TrainStation } from "./TrainStation"; export class Railroad { + private trainCount: number = 0; + constructor( public from: TrainStation, public to: TrainStation, @@ -23,6 +25,29 @@ export class Railroad { this.from.removeRailroad(this); this.to.removeRailroad(this); } + + incrementTrainCount(): void { + this.trainCount++; + } + + decrementTrainCount(): void { + this.trainCount = Math.max(0, this.trainCount - 1); + } + + getLength(): number { + return this.tiles.length; + } + + /** + * Dynamic fare based on railroad length and current busyness. + * Longer railroads cost more, but overcrowded edges become less profitable. + * Uses integer math to keep amounts precise. + */ + getFare(): bigint { + const baseFare = BigInt(this.getLength() * 100); // Base fare proportional to length + const busynessFactor = Math.max(1, 10 - this.trainCount); // 10,9,...,1 (capped) + return (baseFare * BigInt(busynessFactor)) / 10n; + } } export function getOrientedRailroad( @@ -54,6 +79,10 @@ export class OrientedRailroad { return this.tiles; } + getRailroad(): Railroad { + return this.railroad; + } + getStart(): TrainStation { return this.forward ? this.railroad.from : this.railroad.to; }