From e68d48c3a8db2ae665cd8f551807420deb3253e9 Mon Sep 17 00:00:00 2001 From: Aotumuri Date: Sat, 14 Jun 2025 01:21:22 +0900 Subject: [PATCH] Fixed quick chat text injection (#1144) ## Description: https://github.com/openfrontio/OpenFrontIO/issues/1035 Fixes #1035 ## Please complete the following: - [x] I have added screenshots for all UI updates - [x] I process any text displayed to the user through translateText() and I've added it to the en.json file - [x] 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 - [x] I understand that submitting code with bugs that could have been caught through manual testing blocks releases and new features for all contributors ## Please put your Discord username so you can be contacted if a bug or regression is found: .w. --------- Co-authored-by: Scott Anderson Co-authored-by: evanpelle --- src/client/Transport.ts | 4 +- src/client/graphics/layers/ChatIntegration.ts | 2 +- src/client/graphics/layers/ChatModal.ts | 42 +++++++------------ src/client/graphics/layers/EventsDisplay.ts | 18 ++++++-- src/core/Schemas.ts | 2 +- src/core/execution/ExecutionManager.ts | 2 +- src/core/execution/QuickChatExecution.ts | 13 +++--- src/core/game/Game.ts | 2 +- src/core/game/GameImpl.ts | 4 +- src/core/game/GameUpdates.ts | 2 +- 10 files changed, 44 insertions(+), 47 deletions(-) diff --git a/src/client/Transport.ts b/src/client/Transport.ts index b1e73fded..7d112c8ad 100644 --- a/src/client/Transport.ts +++ b/src/client/Transport.ts @@ -116,7 +116,7 @@ export class SendQuickChatEvent implements GameEvent { constructor( public readonly recipient: PlayerView, public readonly quickChatKey: string, - public readonly variables: { [key: string]: string }, + public readonly target?: PlayerID, ) {} } @@ -488,7 +488,7 @@ export class Transport { clientID: this.lobbyConfig.clientID, recipient: event.recipient.id(), quickChatKey: event.quickChatKey, - variables: event.variables, + target: event.target, }); } diff --git a/src/client/graphics/layers/ChatIntegration.ts b/src/client/graphics/layers/ChatIntegration.ts index cadabed58..b81e5caca 100644 --- a/src/client/graphics/layers/ChatIntegration.ts +++ b/src/client/graphics/layers/ChatIntegration.ts @@ -74,7 +74,7 @@ export class ChatIntegration { new SendQuickChatEvent( recipient, `${category.id}.${phrase.key}`, - {}, + undefined, ), ); } diff --git a/src/client/graphics/layers/ChatModal.ts b/src/client/graphics/layers/ChatModal.ts index 1118a027e..6c0413f59 100644 --- a/src/client/graphics/layers/ChatModal.ts +++ b/src/client/graphics/layers/ChatModal.ts @@ -29,16 +29,16 @@ export class ChatModal extends LitElement { return this; } - private players: string[] = []; + private players: PlayerView[] = []; private playerSearchQuery: string = ""; private previewText: string | null = null; private requiresPlayerSelection: boolean = false; private selectedCategory: string | null = null; private selectedPhraseText: string | null = null; - private selectedPlayer: string | null = null; private selectedPhraseTemplate: string | null = null; private selectedQuickChatKey: string | null = null; + private selectedPlayer: PlayerView | null = null; private recipient: PlayerView; private sender: PlayerView; @@ -143,7 +143,7 @@ export class ChatModal extends LitElement { : ""}" @click=${() => this.selectPlayer(player)} > - ${player} + ${player.name()} `, )} @@ -177,7 +177,6 @@ export class ChatModal extends LitElement { this.selectedPhraseText = null; this.previewText = null; this.requiresPlayerSelection = false; - this.selectedPlayer = null; this.requestUpdate(); } @@ -194,7 +193,6 @@ export class ChatModal extends LitElement { ); this.previewText = `chat.${this.selectedCategory}.${phrase.key}`; this.requiresPlayerSelection = phrase.requiresPlayer; - this.selectedPlayer = null; this.requestUpdate(); } @@ -202,10 +200,10 @@ export class ChatModal extends LitElement { return translateText(`chat.${this.selectedCategory}.${phrase.key}`); } - private selectPlayer(player: string) { + private selectPlayer(player: PlayerView) { if (this.previewText) { this.previewText = - this.selectedPhraseTemplate?.replace("[P1]", player) ?? null; + this.selectedPhraseTemplate?.replace("[P1]", player.name()) ?? null; this.selectedPlayer = player; this.requiresPlayerSelection = false; this.requestUpdate(); @@ -219,15 +217,11 @@ export class ChatModal extends LitElement { console.log("Key:", this.selectedQuickChatKey); if (this.sender && this.recipient && this.selectedQuickChatKey) { - const variables: Record = this.selectedPlayer - ? { P1: this.selectedPlayer } - : {}; - this.eventBus.emit( new SendQuickChatEvent( this.recipient, this.selectedQuickChatKey, - variables, + this.selectedPlayer?.id(), ), ); } @@ -246,13 +240,15 @@ export class ChatModal extends LitElement { this.requestUpdate(); } - private getSortedFilteredPlayers(): string[] { - const sorted = [...this.players].sort((a, b) => a.localeCompare(b)); + private getSortedFilteredPlayers(): PlayerView[] { + const sorted = [...this.players].sort((a, b) => + a.name().localeCompare(b.name()), + ); const filtered = sorted.filter((p) => - p.toLowerCase().includes(this.playerSearchQuery), + p.name().toLowerCase().includes(this.playerSearchQuery), ); const others = sorted.filter( - (p) => !p.toLowerCase().includes(this.playerSearchQuery), + (p) => !p.name().toLowerCase().includes(this.playerSearchQuery), ); return [...filtered, ...others]; } @@ -265,13 +261,10 @@ export class ChatModal extends LitElement { if (sender && recipient) { console.log("Sent message:", recipient); console.log("Sent message:", sender); - const alivePlayerNames = this.g + this.players = this.g .players() - .filter((p) => p.isAlive() && !(p.data.playerType === PlayerType.Bot)) - .map((p) => p.data.name); + .filter((p) => p.isAlive() && p.data.playerType !== PlayerType.Bot); - console.log("Alive player names:", alivePlayerNames); - this.players = alivePlayerNames; this.recipient = recipient; this.sender = sender; } @@ -284,7 +277,6 @@ export class ChatModal extends LitElement { this.selectedPhraseText = null; this.previewText = null; this.requiresPlayerSelection = false; - this.selectedPlayer = null; this.modalEl?.close(); } @@ -303,12 +295,10 @@ export class ChatModal extends LitElement { recipient?: PlayerView, ) { if (sender && recipient) { - const alivePlayerNames = this.g + this.players = this.g .players() - .filter((p) => p.isAlive() && !(p.data.playerType === PlayerType.Bot)) - .map((p) => p.data.name); + .filter((p) => p.isAlive() && p.data.playerType !== PlayerType.Bot); - this.players = alivePlayerNames; this.recipient = recipient; this.sender = sender; } diff --git a/src/client/graphics/layers/EventsDisplay.ts b/src/client/graphics/layers/EventsDisplay.ts index 39b535347..6861a9d05 100644 --- a/src/client/graphics/layers/EventsDisplay.ts +++ b/src/client/graphics/layers/EventsDisplay.ts @@ -305,10 +305,20 @@ export class EventsDisplay extends LitElement implements Layer { } const baseMessage = translateText(`chat.${event.category}.${event.key}`); - const translatedMessage = baseMessage.replace( - /\[([^\]]+)\]/g, - (_, key) => event.variables?.[key] || `[${key}]`, - ); + let translatedMessage = baseMessage; + if (event.target) { + try { + const targetPlayer = this.game.player(event.target); + const targetName = targetPlayer?.name() ?? event.target; + translatedMessage = baseMessage.replace("[P1]", targetName); + } catch (e) { + console.warn( + `Failed to resolve player for target ID '${event.target}'`, + e, + ); + return; + } + } this.addEvent({ description: translateText(event.isFrom ? "chat.from" : "chat.to", { diff --git a/src/core/Schemas.ts b/src/core/Schemas.ts index 094872c0a..a25b823b2 100644 --- a/src/core/Schemas.ts +++ b/src/core/Schemas.ts @@ -287,7 +287,7 @@ export const QuickChatIntentSchema = BaseIntentSchema.extend({ type: z.literal("quick_chat"), recipient: ID, quickChatKey: QuickChatKeySchema, - variables: z.record(SafeString).optional(), + target: ID.optional(), }); export const MarkDisconnectedIntentSchema = BaseIntentSchema.extend({ diff --git a/src/core/execution/ExecutionManager.ts b/src/core/execution/ExecutionManager.ts index 9640b8d5e..a50b9c270 100644 --- a/src/core/execution/ExecutionManager.ts +++ b/src/core/execution/ExecutionManager.ts @@ -122,7 +122,7 @@ export class Executor { player, intent.recipient, intent.quickChatKey, - intent.variables ?? {}, + intent.target, ); case "mark_disconnected": return new MarkDisconnectedExecution(player, intent.isDisconnected); diff --git a/src/core/execution/QuickChatExecution.ts b/src/core/execution/QuickChatExecution.ts index 4e545b6c5..b3f79830f 100644 --- a/src/core/execution/QuickChatExecution.ts +++ b/src/core/execution/QuickChatExecution.ts @@ -10,7 +10,7 @@ export class QuickChatExecution implements Execution { private sender: Player, private recipientID: PlayerID, private quickChatKey: string, - private variables: Record, + private target: PlayerID | undefined, ) {} init(mg: Game, ticks: number): void { @@ -27,12 +27,12 @@ export class QuickChatExecution implements Execution { } tick(ticks: number): void { - const message = this.getMessageFromKey(this.quickChatKey, this.variables); + const message = this.getMessageFromKey(this.quickChatKey); this.mg.displayChat( message[1], message[0], - this.variables, + this.target, this.recipient.id(), true, this.sender.name(), @@ -41,7 +41,7 @@ export class QuickChatExecution implements Execution { this.mg.displayChat( message[1], message[0], - this.variables, + this.target, this.sender.id(), false, this.recipient.name(), @@ -66,10 +66,7 @@ export class QuickChatExecution implements Execution { return false; } - private getMessageFromKey( - fullKey: string, - vars: Record, - ): string[] { + private getMessageFromKey(fullKey: string): string[] { const translated = fullKey.split("."); return translated; } diff --git a/src/core/game/Game.ts b/src/core/game/Game.ts index 8e30cdafb..1a0dcb411 100644 --- a/src/core/game/Game.ts +++ b/src/core/game/Game.ts @@ -606,7 +606,7 @@ export interface Game extends GameMap { displayChat( message: string, category: string, - variables: Record, + target: PlayerID | undefined, playerID: PlayerID | null, isFrom: boolean, recipient: string, diff --git a/src/core/game/GameImpl.ts b/src/core/game/GameImpl.ts index 17fb3a9db..0a2ecc918 100644 --- a/src/core/game/GameImpl.ts +++ b/src/core/game/GameImpl.ts @@ -630,7 +630,7 @@ export class GameImpl implements Game { displayChat( message: string, category: string, - variables: Record = {}, + target: PlayerID | undefined, playerID: PlayerID | null, isFrom: boolean, recipient: string, @@ -643,7 +643,7 @@ export class GameImpl implements Game { type: GameUpdateType.DisplayChatEvent, key: message, category: category, - variables: variables, + target: target, playerID: id, isFrom, recipient: recipient, diff --git a/src/core/game/GameUpdates.ts b/src/core/game/GameUpdates.ts index 3bcbf9ce8..48e8fbd1f 100644 --- a/src/core/game/GameUpdates.ts +++ b/src/core/game/GameUpdates.ts @@ -172,7 +172,7 @@ export type DisplayChatMessageUpdate = { type: GameUpdateType.DisplayChatEvent; key: string; category: string; - variables?: Record; + target: string | undefined; playerID: number | null; isFrom: boolean; recipient: string;