From 27094c3be82a77f51348091fae538f678468d88a Mon Sep 17 00:00:00 2001 From: tnhnblgl <51187395+tnhnblgl@users.noreply.github.com> Date: Sun, 18 May 2025 18:06:46 +0300 Subject: [PATCH 01/10] Update Game.ts --- src/core/game/Game.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/core/game/Game.ts b/src/core/game/Game.ts index 61f4051d5b..dea576ffec 100644 --- a/src/core/game/Game.ts +++ b/src/core/game/Game.ts @@ -609,6 +609,7 @@ export interface PlayerInteraction { canTarget: boolean; canDonate: boolean; canEmbargo: boolean; + allianceCreatedAtTick?: Tick; } export interface EmojiMessage { From f689eae7531c441b135d2778c412a36af0fc2cc4 Mon Sep 17 00:00:00 2001 From: tnhnblgl <51187395+tnhnblgl@users.noreply.github.com> Date: Sun, 18 May 2025 18:16:36 +0300 Subject: [PATCH 02/10] Update GameRunner.ts --- src/core/GameRunner.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/core/GameRunner.ts b/src/core/GameRunner.ts index e64909307f..58631aaddf 100644 --- a/src/core/GameRunner.ts +++ b/src/core/GameRunner.ts @@ -201,6 +201,10 @@ export class GameRunner { canDonate: player.canDonate(other), canEmbargo: !player.hasEmbargoAgainst(other), }; + const alliance = player.allianceWith(other as Player); + if (alliance) { + actions.interaction.allianceCreatedAtTick = alliance.createdAt(); + } } return actions; From 5755ccfad26e1a7889f04cd3446a2aa6bf00e7a1 Mon Sep 17 00:00:00 2001 From: tnhnblgl <51187395+tnhnblgl@users.noreply.github.com> Date: Sun, 18 May 2025 18:42:16 +0300 Subject: [PATCH 03/10] Update PlayerPanel.ts --- src/client/graphics/layers/PlayerPanel.ts | 45 ++++++++++++++++++++++- 1 file changed, 44 insertions(+), 1 deletion(-) diff --git a/src/client/graphics/layers/PlayerPanel.ts b/src/client/graphics/layers/PlayerPanel.ts index 547bd54fd4..680e620420 100644 --- a/src/client/graphics/layers/PlayerPanel.ts +++ b/src/client/graphics/layers/PlayerPanel.ts @@ -13,6 +13,7 @@ import { AllPlayers, PlayerActions, PlayerID, + Tick, UnitType, } from "../../../core/game/Game"; import { TileRef } from "../../../core/game/GameMap"; @@ -45,6 +46,9 @@ export class PlayerPanel extends LitElement implements Layer { @state() private isVisible: boolean = false; + @state() + private allianceExpiryText: string | null = null; + public show(actions: PlayerActions, tile: TileRef) { this.actions = actions; this.tile = tile; @@ -169,12 +173,39 @@ export class PlayerPanel extends LitElement implements Layer { if (this.isVisible && this.tile) { const myPlayer = this.g.myPlayer(); if (myPlayer !== null && myPlayer.isAlive()) { - this.actions = await myPlayer.actions(this.tile); + const currentActions = await myPlayer.actions(this.tile); + this.actions = currentActions; + + if (currentActions?.interaction?.allianceCreatedAtTick !== undefined) { + const createdAt = currentActions.interaction.allianceCreatedAtTick; + const durationTicks = this.g.config().allianceDuration(); + const expiryTick = createdAt + durationTicks; + const remainingTicks = expiryTick - this.g.ticks(); + + if (remainingTicks > 0) { + const remainingSeconds = Math.max(0, Math.floor(remainingTicks / 10)); // 10 ticks per second + this.allianceExpiryText = this.formatDuration(remainingSeconds); + } else { + this.allianceExpiryText = translateText("player_panel.alliance_expired_status"); + } + } else { + this.allianceExpiryText = null; + } this.requestUpdate(); } } } + private formatDuration(totalSeconds: number): string { + if (totalSeconds <= 0) return "0s"; + const minutes = Math.floor(totalSeconds / 60); + const seconds = totalSeconds % 60; + let time = ""; + if (minutes > 0) time += `${minutes}m `; + time += `${seconds}s`; + return time.trim(); + } + getTotalNukesSent(otherId: PlayerID): number { const stats = this.g.player(otherId).stats(); if (!stats) { @@ -305,6 +336,18 @@ export class PlayerPanel extends LitElement implements Layer { + ${this.allianceExpiryText !== null + ? html` +