diff --git a/src/client/Utils.ts b/src/client/Utils.ts index a842a42af..ff9e068c6 100644 --- a/src/client/Utils.ts +++ b/src/client/Utils.ts @@ -189,9 +189,10 @@ export function getMessageTypeClasses(type: MessageType): string { case MessageType.HYDROGEN_BOMB_INBOUND: case MessageType.SAM_MISS: case MessageType.ALLIANCE_EXPIRED: - case MessageType.NAVAL_INVASION_INBOUND: case MessageType.RENEW_ALLIANCE: return severityColors["warn"]; + case MessageType.NAVAL_INVASION_INBOUND: + return severityColors["fail"]; case MessageType.CHAT: case MessageType.ALLIANCE_REQUEST: return severityColors["info"]; diff --git a/src/client/graphics/layers/EventsDisplay.ts b/src/client/graphics/layers/EventsDisplay.ts index 1141643ef..bfd3b0f0a 100644 --- a/src/client/graphics/layers/EventsDisplay.ts +++ b/src/client/graphics/layers/EventsDisplay.ts @@ -705,12 +705,38 @@ export class EventsDisplay extends LitElement implements Layer { highlight: true, createdAt: this.game.ticks(), unitView: unitView, + shouldDelete: (game) => { + // Delete the message if the unit doesn't exist or is no longer active + // (destroyed, canceled, or landed) + if (!unitView) { + return true; + } + const currentUnitView = game.unit(event.unitID); + if (!currentUnitView || !currentUnitView.isActive()) { + return true; + } + return false; + }, }); } private getEventDescription( event: GameEvent, ): string | DirectiveResult { + // Add "(retreating)" for boat attacks when the boat is retreating + if (event.type === MessageType.NAVAL_INVASION_INBOUND && event.unitView) { + // Get fresh unit view to ensure we have current retreating status + const currentUnitView = this.game.unit(event.unitView.id()); + if (currentUnitView && currentUnitView.retreating()) { + const baseDescription = event.description; + const retreatingText = ` (${translateText("events_display.retreating")})`; + if (event.unsafeDescription) { + return unsafeHTML(onlyImages(baseDescription + retreatingText)); + } + return baseDescription + retreatingText; + } + } + return event.unsafeDescription ? unsafeHTML(onlyImages(event.description)) : event.description; diff --git a/src/core/execution/TransportShipExecution.ts b/src/core/execution/TransportShipExecution.ts index 913abb817..3028374f9 100644 --- a/src/core/execution/TransportShipExecution.ts +++ b/src/core/execution/TransportShipExecution.ts @@ -1,3 +1,4 @@ +import { renderTroops } from "../../client/Utils"; import { Execution, Game, @@ -144,7 +145,7 @@ export class TransportShipExecution implements Execution { mg.displayIncomingUnit( this.boat.id(), // TODO TranslateText - `Naval invasion incoming from ${this.attacker.displayName()}`, + `Boat: ${renderTroops(this.boat.troops())} ${this.attacker.displayName()}`, MessageType.NAVAL_INVASION_INBOUND, this.targetID, ); diff --git a/src/core/game/UnitImpl.ts b/src/core/game/UnitImpl.ts index 917d0af67..5c985cda3 100644 --- a/src/core/game/UnitImpl.ts +++ b/src/core/game/UnitImpl.ts @@ -269,6 +269,18 @@ export class UnitImpl implements Unit { this.mg .stats() .boatDestroyTroops(destroyer, this._owner, this._troops); + // Notify the defender if the boat was targeting them + if (this.targetTile() !== undefined) { + const targetTile = this.targetTile()!; + const targetOwner = this.mg.owner(targetTile); + if (targetOwner.isPlayer() && targetOwner !== this._owner) { + this.mg.displayMessage( + `${this._owner.displayName()} boat was destroyed`, + MessageType.UNIT_DESTROYED, + targetOwner.id(), + ); + } + } break; case UnitType.TradeShip: this.mg.stats().boatDestroyTrade(destroyer, this._owner);