e.preventDefault()}
>
${this.getMessage()}
diff --git a/src/client/graphics/layers/Leaderboard.ts b/src/client/graphics/layers/Leaderboard.ts
index 3204d1e56..5dd8793f3 100644
--- a/src/client/graphics/layers/Leaderboard.ts
+++ b/src/client/graphics/layers/Leaderboard.ts
@@ -4,7 +4,7 @@ import { repeat } from "lit/directives/repeat.js";
import { renderTroops, translateText } from "../../../client/Utils";
import { EventBus, GameEvent } from "../../../core/EventBus";
import { GameView, PlayerView, UnitView } from "../../../core/game/GameView";
-import { renderNumber } from "../../Utils";
+import { formatPercentage, renderNumber } from "../../Utils";
import { Layer } from "./Layer";
interface Entry {
@@ -274,9 +274,3 @@ export class Leaderboard extends LitElement implements Layer {
`;
}
}
-
-function formatPercentage(value: number): string {
- const perc = value * 100;
- if (Number.isNaN(perc)) return "0%";
- return perc.toFixed(1) + "%";
-}
diff --git a/src/client/graphics/layers/SettingsModal.ts b/src/client/graphics/layers/SettingsModal.ts
index 4c1c2c461..92d8f1fe4 100644
--- a/src/client/graphics/layers/SettingsModal.ts
+++ b/src/client/graphics/layers/SettingsModal.ts
@@ -187,7 +187,7 @@ export class SettingsModal extends LitElement implements Layer {
return html`
e.preventDefault()}
>
{
diff --git a/src/core/execution/SAMMissileExecution.ts b/src/core/execution/SAMMissileExecution.ts
index e313acd8c..a8d0862f6 100644
--- a/src/core/execution/SAMMissileExecution.ts
+++ b/src/core/execution/SAMMissileExecution.ts
@@ -61,9 +61,11 @@ export class SAMMissileExecution implements Execution {
);
if (result === true) {
this.mg.displayMessage(
- `Missile intercepted ${this.target.type()}`,
+ "events_display.missile_intercepted",
MessageType.SAM_HIT,
this._owner.id(),
+ undefined,
+ { unit: this.target.type() },
);
this.active = false;
this.target.delete(true, this._owner);
diff --git a/src/core/execution/TradeShipExecution.ts b/src/core/execution/TradeShipExecution.ts
index f1230c6f4..c421e17aa 100644
--- a/src/core/execution/TradeShipExecution.ts
+++ b/src/core/execution/TradeShipExecution.ts
@@ -142,10 +142,14 @@ export class TradeShipExecution implements Execution {
if (this.wasCaptured) {
this.tradeShip!.owner().addGold(gold, this._dstPort.tile());
this.mg.displayMessage(
- `Received ${renderNumber(gold)} gold from ship captured from ${this.origOwner.displayName()}`,
+ "events_display.received_gold_from_captured_ship",
MessageType.CAPTURED_ENEMY_UNIT,
this.tradeShip!.owner().id(),
gold,
+ {
+ gold: renderNumber(gold),
+ name: this.origOwner.displayName(),
+ },
);
// Record stats
this.mg
@@ -155,16 +159,24 @@ export class TradeShipExecution implements Execution {
this.srcPort.owner().addGold(gold);
this._dstPort.owner().addGold(gold, this._dstPort.tile());
this.mg.displayMessage(
- `Received ${renderNumber(gold)} gold from trade with ${this.srcPort.owner().displayName()}`,
+ "events_display.received_gold_from_trade",
MessageType.RECEIVED_GOLD_FROM_TRADE,
this._dstPort.owner().id(),
gold,
+ {
+ gold: renderNumber(gold),
+ name: this.srcPort.owner().displayName(),
+ },
);
this.mg.displayMessage(
- `Received ${renderNumber(gold)} gold from trade with ${this._dstPort.owner().displayName()}`,
+ "events_display.received_gold_from_trade",
MessageType.RECEIVED_GOLD_FROM_TRADE,
this.srcPort.owner().id(),
gold,
+ {
+ gold: renderNumber(gold),
+ name: this._dstPort.owner().displayName(),
+ },
);
// Record stats
this.mg
diff --git a/src/core/execution/TransportShipExecution.ts b/src/core/execution/TransportShipExecution.ts
index 776644aa2..b5a8c2d2f 100644
--- a/src/core/execution/TransportShipExecution.ts
+++ b/src/core/execution/TransportShipExecution.ts
@@ -77,9 +77,11 @@ export class TransportShipExecution implements Execution {
mg.config().boatMaxNumber()
) {
mg.displayMessage(
- `No boats available, max ${mg.config().boatMaxNumber()}`,
+ "events_display.no_boats_available",
MessageType.ATTACK_FAILED,
this.attacker.id(),
+ undefined,
+ { max: mg.config().boatMaxNumber() },
);
this.active = false;
return;
@@ -270,9 +272,11 @@ export class TransportShipExecution implements Execution {
.boatArriveTroops(this.attacker, this.target, survivors);
if (deaths) {
this.mg.displayMessage(
- `Attack cancelled, ${renderTroops(deaths)} soldiers killed during retreat.`,
+ "events_display.attack_cancelled_retreat",
MessageType.ATTACK_CANCELLED,
this.attacker.id(),
+ undefined,
+ { troops: renderTroops(deaths) },
);
}
return;
diff --git a/src/core/game/Game.ts b/src/core/game/Game.ts
index 3cb288559..8d732c271 100644
--- a/src/core/game/Game.ts
+++ b/src/core/game/Game.ts
@@ -114,7 +114,7 @@ export enum GameMapType {
StraitOfHormuz = "Strait of Hormuz",
Surrounded = "Surrounded",
Didier = "Didier",
- DidierFrance = "Didier (France)",
+ DidierFrance = "Didier France",
AmazonRiver = "Amazon River",
}
diff --git a/src/core/game/PlayerImpl.ts b/src/core/game/PlayerImpl.ts
index 09c02c5a7..b11c09354 100644
--- a/src/core/game/PlayerImpl.ts
+++ b/src/core/game/PlayerImpl.ts
@@ -665,14 +665,18 @@ export class PlayerImpl implements Player {
this.sentDonations.push(new Donation(recipient, this.mg.ticks()));
this.mg.displayMessage(
- `Sent ${renderTroops(troops)} troops to ${recipient.name()}`,
+ "events_display.sent_troops_to_player",
MessageType.SENT_TROOPS_TO_PLAYER,
this.id(),
+ undefined,
+ { troops: renderTroops(troops), name: recipient.name() },
);
this.mg.displayMessage(
- `Received ${renderTroops(troops)} troops from ${this.name()}`,
+ "events_display.received_troops_from_player",
MessageType.RECEIVED_TROOPS_FROM_PLAYER,
recipient.id(),
+ undefined,
+ { troops: renderTroops(troops), name: this.name() },
);
return true;
}
@@ -685,15 +689,18 @@ export class PlayerImpl implements Player {
this.sentDonations.push(new Donation(recipient, this.mg.ticks()));
this.mg.displayMessage(
- `Sent ${renderNumber(gold)} gold to ${recipient.name()}`,
+ "events_display.sent_gold_to_player",
MessageType.SENT_GOLD_TO_PLAYER,
this.id(),
+ undefined,
+ { gold: renderNumber(gold), name: recipient.name() },
);
this.mg.displayMessage(
- `Received ${renderNumber(gold)} gold from ${this.name()}`,
+ "events_display.received_gold_from_player",
MessageType.RECEIVED_GOLD_FROM_PLAYER,
recipient.id(),
gold,
+ { gold: renderNumber(gold), name: this.name() },
);
return true;
}
diff --git a/src/core/game/UnitImpl.ts b/src/core/game/UnitImpl.ts
index fad1f02f0..67791b458 100644
--- a/src/core/game/UnitImpl.ts
+++ b/src/core/game/UnitImpl.ts
@@ -206,14 +206,18 @@ export class UnitImpl implements Unit {
this._owner._units.push(this);
this.mg.addUpdate(this.toUpdate());
this.mg.displayMessage(
- `Your ${this.type()} was captured by ${newOwner.displayName()}`,
+ "events_display.unit_captured_by_enemy",
MessageType.UNIT_CAPTURED_BY_ENEMY,
this._lastOwner.id(),
+ undefined,
+ { unit: this.type(), name: newOwner.displayName() },
);
this.mg.displayMessage(
- `Captured ${this.type()} from ${this._lastOwner.displayName()}`,
+ "events_display.captured_enemy_unit",
MessageType.CAPTURED_ENEMY_UNIT,
newOwner.id(),
+ undefined,
+ { unit: this.type(), name: this._lastOwner.displayName() },
);
}
@@ -304,9 +308,11 @@ export class UnitImpl implements Unit {
}
this.mg.displayMessage(
- `Your ${this._type} was destroyed`,
+ "events_display.unit_destroyed",
MessageType.UNIT_DESTROYED,
this.owner().id(),
+ undefined,
+ { unit: this._type },
);
}
diff --git a/src/server/MapPlaylist.ts b/src/server/MapPlaylist.ts
index 39bebe93b..35a218a47 100644
--- a/src/server/MapPlaylist.ts
+++ b/src/server/MapPlaylist.ts
@@ -61,7 +61,7 @@ const frequency: Partial> = {
TwoLakes: 6,
StraitOfHormuz: 4,
Surrounded: 4,
- DidierFrance: 2,
+ DidierFrance: 1,
AmazonRiver: 3,
};
diff --git a/tests/InputHandler.test.ts b/tests/InputHandler.test.ts
index 118d0ce68..e77c4cda6 100644
--- a/tests/InputHandler.test.ts
+++ b/tests/InputHandler.test.ts
@@ -431,7 +431,7 @@ describe("InputHandler AutoUpgrade", () => {
expect((inputHandler as any).keybinds.moveUp).toBe("KeyX");
});
- test("ignores non-string and 'Null' values and preserves defaults", () => {
+ test("ignores non-string values and preserves defaults, but keeps 'Null' for unbound keys", () => {
const mixed = {
moveUp: { key: "moveUp", value: null },
moveLeft: "Null",
@@ -440,9 +440,9 @@ describe("InputHandler AutoUpgrade", () => {
inputHandler.initialize();
- // defaults from InputHandler should remain
expect((inputHandler as any).keybinds.moveUp).toBe("KeyW");
- expect((inputHandler as any).keybinds.moveLeft).toBe("KeyA");
+ // "Null" is preserved to indicate unbound keybind
+ expect((inputHandler as any).keybinds.moveLeft).toBe("Null");
});
test("handles invalid JSON gracefully and warns", () => {