From ab5b044362af8afd493d5ca4f5713eace854cf0f Mon Sep 17 00:00:00 2001 From: Arkadiusz Sygulski Date: Sat, 3 Jan 2026 03:53:24 +0100 Subject: [PATCH] Fix train was destroyed message spam (#2774) ## Description: Trains are made of a primary unit (`TrainExecution.train`) followed by 6 cars (`TrainExecution.cars`). Currently when any of the units is destroyed, a message "Your Train was destroyed" is shown. In worst case scenario, when all cars are blasted by a nuke, 7 messages are displayed to the user, but the train continues. Since the actual logic is unaffected as long as the primary unit stays alive, displaying messages is confusing. This PR fixes it. The message is only displayed when the primary unit is destroyed. The following cars are only there for fancy visuals. ## Screencast ##### Current - multiple messages for single train https://github.com/user-attachments/assets/3df04c71-d899-4f68-af83-36c9d0ffa730 First nuke was a test. Second nuke destroyed the entire train, prompting 7 messages. Third nuke destroyed one full train and then some. Prompting many too many messages. ##### Fixed - one message, only if train was fully destroyed https://github.com/user-attachments/assets/1f3840a7-6c62-487d-af3a-82de39dad9e8 First train was only partially destroyed, no message was shown, delivery mission completed A+. Following 2 trains had the front engine destroyed and therefore were promptly decommissioned. ## Please complete the following: - [x] I have added screenshots for all UI updates - [ ] I process any text displayed to the user through translateText() and I've added it to the en.json file - [ ] I have added relevant tests to the test directory - [x] I confirm I have thoroughly tested these changes and take full responsibility for any bugs introduced ## Please put your Discord username so you can be contacted if a bug or regression is found: moleole --- src/client/graphics/SpriteLoader.ts | 17 ++++++++++++----- src/core/execution/TrainExecution.ts | 7 ++++--- src/core/game/Game.ts | 1 + src/core/game/UnitImpl.ts | 26 ++++++++++++++++++++------ 4 files changed, 37 insertions(+), 14 deletions(-) diff --git a/src/client/graphics/SpriteLoader.ts b/src/client/graphics/SpriteLoader.ts index 8d71d7d53..36c4aca54 100644 --- a/src/client/graphics/SpriteLoader.ts +++ b/src/client/graphics/SpriteLoader.ts @@ -80,11 +80,18 @@ export const loadAllSprites = async (): Promise => { * The train sprites rely on the train attributes and not only on its type */ function trainTypeToSpriteType(unit: UnitView): TrainTypeSprite { - return unit.trainType() === TrainType.Engine - ? TrainTypeSprite.Engine - : unit.isLoaded() - ? TrainTypeSprite.LoadedCarriage - : TrainTypeSprite.Carriage; + const trainType = unit.trainType(); + + switch (trainType) { + case TrainType.Engine: + case TrainType.TailEngine: + return TrainTypeSprite.Engine; + case TrainType.Carriage: + default: + return unit.isLoaded() + ? TrainTypeSprite.LoadedCarriage + : TrainTypeSprite.Carriage; + } } const getSpriteForUnit = (unit: UnitView): ImageBitmap | null => { diff --git a/src/core/execution/TrainExecution.ts b/src/core/execution/TrainExecution.ts index 5e165067a..4872885e8 100644 --- a/src/core/execution/TrainExecution.ts +++ b/src/core/execution/TrainExecution.ts @@ -14,8 +14,8 @@ import { TrainStation } from "../game/TrainStation"; export class TrainExecution implements Execution { private active = true; private mg: Game | null = null; - private train: Unit | null = null; - private cars: Unit[] = []; + private train: Unit | null = null; // primary unit + private cars: Unit[] = []; // stored back to front private hasCargo: boolean = false; private currentTile: number = 0; private spacing = 2; @@ -69,6 +69,7 @@ export class TrainExecution implements Execution { if (this.train === null) { throw new Error("Not initialized"); } + if (!this.train.isActive() || !this.activeSourceOrDestination()) { this.deleteTrain(); return; @@ -113,7 +114,7 @@ export class TrainExecution implements Execution { this.cars.push( this.player.buildUnit(UnitType.Train, tile, { targetUnit: this.destination.unit, - trainType: TrainType.Engine, + trainType: TrainType.TailEngine, }), ); for (let i = 0; i < this.numCars; i++) { diff --git a/src/core/game/Game.ts b/src/core/game/Game.ts index a51f42723..c38b00300 100644 --- a/src/core/game/Game.ts +++ b/src/core/game/Game.ts @@ -217,6 +217,7 @@ export enum UnitType { export enum TrainType { Engine = "Engine", + TailEngine = "TailEngine", Carriage = "Carriage", } diff --git a/src/core/game/UnitImpl.ts b/src/core/game/UnitImpl.ts index 98344ec4b..fad1f02f0 100644 --- a/src/core/game/UnitImpl.ts +++ b/src/core/game/UnitImpl.ts @@ -265,13 +265,11 @@ export class UnitImpl implements Unit { this._active = false; this.mg.addUpdate(this.toUpdate()); this.mg.removeUnit(this); - if (displayMessage !== false && this._type !== UnitType.MIRVWarhead) { - this.mg.displayMessage( - `Your ${this._type} was destroyed`, - MessageType.UNIT_DESTROYED, - this.owner().id(), - ); + + if (displayMessage !== false) { + this.displayMessageOnDeleted(); } + if (destroyer !== undefined) { switch (this._type) { case UnitType.TransportShip: @@ -296,6 +294,22 @@ export class UnitImpl implements Unit { } } + private displayMessageOnDeleted(): void { + if (this._type === UnitType.MIRVWarhead) { + return; + } + + if (this._type === UnitType.Train && this._trainType !== TrainType.Engine) { + return; + } + + this.mg.displayMessage( + `Your ${this._type} was destroyed`, + MessageType.UNIT_DESTROYED, + this.owner().id(), + ); + } + isActive(): boolean { return this._active; }