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 <scottanderson@users.noreply.github.com>
Co-authored-by: evanpelle <evanpelle@gmail.com>
This commit is contained in:
Aotumuri
2025-06-14 01:21:22 +09:00
committed by GitHub
parent 9717c95c34
commit e68d48c3a8
10 changed files with 44 additions and 47 deletions
+2 -2
View File
@@ -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,
});
}
@@ -74,7 +74,7 @@ export class ChatIntegration {
new SendQuickChatEvent(
recipient,
`${category.id}.${phrase.key}`,
{},
undefined,
),
);
}
+16 -26
View File
@@ -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()}
</button>
`,
)}
@@ -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<string, string> = 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;
}
+14 -4
View File
@@ -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", {
+1 -1
View File
@@ -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({
+1 -1
View File
@@ -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);
+5 -8
View File
@@ -10,7 +10,7 @@ export class QuickChatExecution implements Execution {
private sender: Player,
private recipientID: PlayerID,
private quickChatKey: string,
private variables: Record<string, string>,
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, string>,
): string[] {
private getMessageFromKey(fullKey: string): string[] {
const translated = fullKey.split(".");
return translated;
}
+1 -1
View File
@@ -606,7 +606,7 @@ export interface Game extends GameMap {
displayChat(
message: string,
category: string,
variables: Record<string, string>,
target: PlayerID | undefined,
playerID: PlayerID | null,
isFrom: boolean,
recipient: string,
+2 -2
View File
@@ -630,7 +630,7 @@ export class GameImpl implements Game {
displayChat(
message: string,
category: string,
variables: Record<string, string> = {},
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,
+1 -1
View File
@@ -172,7 +172,7 @@ export type DisplayChatMessageUpdate = {
type: GameUpdateType.DisplayChatEvent;
key: string;
category: string;
variables?: Record<string, string>;
target: string | undefined;
playerID: number | null;
isFrom: boolean;
recipient: string;