diff --git a/resources/lang/en.json b/resources/lang/en.json
index 1f1f0674ad..9d0d8056ee 100644
--- a/resources/lang/en.json
+++ b/resources/lang/en.json
@@ -536,6 +536,8 @@
"cursor_cost_label_desc": "Show a cost pill under the build cursor icon",
"anonymous_names_label": "Hidden Names",
"anonymous_names_desc": "Hide real player names with random ones on your screen.",
+ "player_name_opacity_label": "Player Name Opacity",
+ "player_name_opacity_desc": "Adjust on-map player label opacity. At 0, names and flags are hidden.",
"lobby_id_visibility_label": "Hidden Lobby IDs",
"lobby_id_visibility_desc": "Hide Lobby ID in private lobby creation",
"toggle_visibility": "Toggle Visibility",
@@ -626,6 +628,7 @@
"unbind": "Unbind",
"on": "On",
"off": "Off",
+ "hidden": "Hidden",
"toggle_terrain": "Toggle Terrain",
"exit_game_label": "Exit Game",
"exit_game_info": "Return to main menu",
diff --git a/src/client/UserSettingModal.ts b/src/client/UserSettingModal.ts
index cb47ee3207..2e54c63f96 100644
--- a/src/client/UserSettingModal.ts
+++ b/src/client/UserSettingModal.ts
@@ -366,6 +366,15 @@ export class UserSettingModal extends BaseModal {
}
}
+ private sliderPlayerNameOpacity(e: CustomEvent<{ value: number }>) {
+ const value = e.detail?.value;
+ if (typeof value !== "number") {
+ console.warn("Slider event missing detail.value", e);
+ return;
+ }
+ this.userSettings.setPlayerNameOpacity(value);
+ }
+
private changeAttackRatioIncrement(
e: CustomEvent<{ value: number | string }>,
) {
@@ -921,6 +930,16 @@ export class UserSettingModal extends BaseModal {
@change=${this.toggleAnonymousNames}
>
+
+
${this.value}%${valueText}
diff --git a/src/client/graphics/layers/NameLayer.ts b/src/client/graphics/layers/NameLayer.ts
index 2cee0dbe0d..e0d1ca6427 100644
--- a/src/client/graphics/layers/NameLayer.ts
+++ b/src/client/graphics/layers/NameLayer.ts
@@ -304,17 +304,26 @@ export class NameLayer implements Layer {
}
render.lastRenderCalc = now + this.rand.nextInt(0, 100);
- // Update text sizes
+ // Update text sizes and opacity
render.fontSize = Math.max(4, Math.floor(baseSize * 0.4));
render.fontColor = this.theme.textColor(render.player);
+ const nameOpacityPercent = this.userSettings.playerNameOpacity();
+ const nameOpacity = nameOpacityPercent / 100;
+ const hideFlag = nameOpacityPercent === 0;
render.nameDiv.style.fontSize = `${render.fontSize}px`;
render.nameDiv.style.lineHeight = `${render.fontSize}px`;
render.nameDiv.style.color = render.fontColor;
+ if (render.nameSpan) {
+ render.nameSpan.textContent = render.player.displayName();
+ render.nameSpan.style.opacity = `${nameOpacity}`;
+ }
if (render.flagDiv) {
render.flagDiv.style.height = `${render.fontSize}px`;
+ render.flagDiv.style.display = hideFlag ? "none" : "";
}
render.troopsDiv.style.fontSize = `${render.fontSize}px`;
+ render.troopsDiv.style.opacity = `${nameOpacity}`;
render.troopsDiv.style.color = render.fontColor;
render.troopsDiv.textContent = renderTroops(render.player.troops());
diff --git a/src/client/graphics/layers/SettingsModal.ts b/src/client/graphics/layers/SettingsModal.ts
index 2e3c9f532f..b965c9041d 100644
--- a/src/client/graphics/layers/SettingsModal.ts
+++ b/src/client/graphics/layers/SettingsModal.ts
@@ -194,6 +194,12 @@ export class SettingsModal extends LitElement implements Layer {
this.requestUpdate();
}
+ private onPlayerNameOpacityChange(event: Event) {
+ const opacity = parseFloat((event.target as HTMLInputElement).value);
+ this.userSettings.setPlayerNameOpacity(opacity);
+ this.requestUpdate();
+ }
+
render() {
if (!this.isVisible) {
return null;
@@ -482,6 +488,38 @@ export class SettingsModal extends LitElement implements Layer {
+
+

+
+
+ ${translateText("user_setting.player_name_opacity_label")}
+
+
+ ${translateText("user_setting.player_name_opacity_desc")}
+
+
+
+
+ ${this.userSettings.playerNameOpacity() === 0
+ ? translateText("user_setting.hidden")
+ : `${this.userSettings.playerNameOpacity()}%`}
+
+
+