Alert frame: add to in-game settings, orange for attack instead of red (#2561)

## Description:

The red alert frame for betrayals was added in
https://github.com/openfrontio/OpenFrontIO/pull/1195. It also flashes
red for incoming land attacks since
https://github.com/openfrontio/OpenFrontIO/pull/2358.

The same color for betrayals and attacks leads to confusion. And
possibly red alert fatigue. But when players find themselves fatigued
and want to shut it off for awhile, they can't because the setting
doesn't exist in-game. Also, the setting description on the homepage
settings didn't yet reflect that the alert frame flashes for attacks
too.

This PR fixes this by:

- making the color for land attacks orange. This is well discernable
from red for various colorblindness types, while still looking alarming.
- adding the setting to in-game SettingsModal 
- adding land attack to setting description

Reference to comments on it on Dev Discord:

https://discord.com/channels/1359946986937258015/1381347989464809664/1441232666065240064


https://discord.com/channels/1359946986937258015/1360078040222142564/1434574256704061523

Orange alert frame on being attacked over land:

https://github.com/user-attachments/assets/e0772d62-5b25-4213-a393-dd5af13e8bc9

Settings description change and addition to in-game toggles:
<img width="560" height="160" alt="Added to description what was added
in PR 2358"
src="https://github.com/user-attachments/assets/bc6e2206-b7ac-498d-9009-d2b6e302d3cf"
/>

<img width="665" height="425" alt="In SettingsModal and with attacks
added to description"
src="https://github.com/user-attachments/assets/d489830c-e359-4a5f-8eb4-3caa7d0c21b2"
/>

## 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

## Please put your Discord username so you can be contacted if a bug or
regression is found:

tryout33
This commit is contained in:
VariableVince
2025-12-04 22:26:55 +01:00
committed by GitHub
parent ec0bf079ef
commit 6ca81211ea
4 changed files with 52 additions and 3 deletions
+11
View File
@@ -0,0 +1,11 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" width="32" height="32">
<path d="M0 0 C1.0725 -0.01675781 2.145 -0.03351562 3.25 -0.05078125 C4.1575 0.06910156 5.065 0.18898438 6 0.3125 C8.35579986 3.84619979 8.65736711 6.90012694 9.125 11 C9.41375 13.413125 9.7025 15.82625 10 18.3125 C3.4 18.3125 -3.2 18.3125 -10 18.3125 C-9.71125 15.71375 -9.4225 13.115 -9.125 10.4375 C-9.03879395 9.62039551 -8.95258789 8.80329102 -8.86376953 7.96142578 C-8.05470453 1.117728 -6.75802103 0.02436786 0 0 Z " fill="#FFFFFF" transform="translate(16,6.6875)"/>
<path d="M0 0 C8.58 0 17.16 0 26 0 C26 1.98 26 3.96 26 6 C17.42 6 8.84 6 0 6 C0 4.02 0 2.04 0 0 Z " fill="#FFFFFF" transform="translate(3,26)"/>
<path d="M0 0 C-0.33 0.99 -0.66 1.98 -1 3 C-2.32 3 -3.64 3 -5 3 C-5 2.34 -5 1.68 -5 1 C-3 0 -3 0 0 0 Z " fill="#FFFFFF" transform="translate(32,7)"/>
<path d="M0 0 C2.475 0.495 2.475 0.495 5 1 C5 1.66 5 2.32 5 3 C3.35 2.67 1.7 2.34 0 2 C0 1.34 0 0.68 0 0 Z " fill="#FFFFFF" transform="translate(0,7)"/>
<path d="M0 0 C1.32 0.66 2.64 1.32 4 2 C4 2.66 4 3.32 4 4 C1.525 3.01 1.525 3.01 -1 2 C-0.67 1.34 -0.34 0.68 0 0 Z " fill="#FFFFFF" transform="translate(28,16)"/>
<path d="M0 0 C0.66 0.66 1.32 1.32 2 2 C0.35 2.66 -1.3 3.32 -3 4 C-2.67 3.01 -2.34 2.02 -2 1 C-1.34 0.67 -0.68 0.34 0 0 Z " fill="#FFFFFF" transform="translate(3,16)"/>
<path d="M0 0 C0.66 0 1.32 0 2 0 C1.34 1.65 0.68 3.3 0 5 C-0.66 4.34 -1.32 3.68 -2 3 C-1.34 2.01 -0.68 1.02 0 0 Z " fill="#FFFFFF" transform="translate(22,0)"/>
<path d="M0 0 C0.99 0.33 1.98 0.66 3 1 C3.33 1.99 3.66 2.98 4 4 C3.01 4 2.02 4 1 4 C0.67 2.68 0.34 1.36 0 0 Z " fill="#FFFFFF" transform="translate(8,0)"/>
</svg>

After

Width:  |  Height:  |  Size: 1.7 KiB

+1 -1
View File
@@ -336,7 +336,7 @@
"emojis_label": "Emojis",
"emojis_desc": "Toggle whether emojis are shown in game",
"alert_frame_label": "Alert Frame",
"alert_frame_desc": "Toggle the alert frame. When enabled, the frame will be displayed when you are betrayed.",
"alert_frame_desc": "Toggle the alert frame. When enabled, the frame will be displayed when you are betrayed or attacked over land.",
"special_effects_label": "Special effects",
"special_effects_desc": "Toggle special effects. Deactivate to improve performances",
"structure_sprites_label": "Structure Sprites",
+14 -2
View File
@@ -21,6 +21,8 @@ export class AlertFrame extends LitElement implements Layer {
@state()
private isActive = false;
@state()
private alertType: "betrayal" | "land-attack" = "betrayal";
private animationTimeout: number | null = null;
private seenAttackIds: Set<string> = new Set();
@@ -36,12 +38,20 @@ export class AlertFrame extends LitElement implements Layer {
width: 100%;
height: 100%;
pointer-events: none;
border: 17px solid #ee0000;
border: 17px solid;
box-sizing: border-box;
z-index: 40;
opacity: 0;
}
.alert-border.betrayal {
border-color: #ee0000;
}
.alert-border.land-attack {
border-color: #ffa500;
}
.alert-border.animate {
animation: alertBlink ${ALERT_SPEED}s ease-in-out ${ALERT_COUNT};
}
@@ -119,6 +129,7 @@ export class AlertFrame extends LitElement implements Layer {
// Only trigger alert if the current player is the betrayed one
if (betrayed === myPlayer) {
this.alertType = "betrayal";
this.activateAlert();
}
}
@@ -202,6 +213,7 @@ export class AlertFrame extends LitElement implements Layer {
// 3. The attack is too small (less than 1/5 of our troops)
if (!inCooldown && !isRetaliation && !isSmallAttack) {
this.seenAttackIds.add(attack.id);
this.alertType = "land-attack";
this.activateAlert();
} else {
// Still mark as seen so we don't alert later
@@ -237,7 +249,7 @@ export class AlertFrame extends LitElement implements Layer {
return html`
<div
class="alert-border animate"
class=${`alert-border animate ${this.alertType}`}
@animationend=${() => this.dismissAlert()}
></div>
`;
@@ -8,6 +8,7 @@ import explosionIcon from "../../../../resources/images/ExplosionIconWhite.svg";
import mouseIcon from "../../../../resources/images/MouseIconWhite.svg";
import ninjaIcon from "../../../../resources/images/NinjaIconWhite.svg";
import settingsIcon from "../../../../resources/images/SettingIconWhite.svg";
import sirenIcon from "../../../../resources/images/SirenIconWhite.svg";
import treeIcon from "../../../../resources/images/TreeIconWhite.svg";
import musicIcon from "../../../../resources/images/music.svg";
import { EventBus } from "../../../core/EventBus";
@@ -130,6 +131,11 @@ export class SettingsModal extends LitElement implements Layer {
this.requestUpdate();
}
private onToggleAlertFrameButtonClick() {
this.userSettings.toggleAlertFrame();
this.requestUpdate();
}
private onToggleDarkModeButtonClick() {
this.userSettings.toggleDarkMode();
this.eventBus.emit(new RefreshGraphicsEvent());
@@ -346,6 +352,26 @@ export class SettingsModal extends LitElement implements Layer {
</div>
</button>
<button
class="flex gap-3 items-center w-full text-left p-3 hover:bg-slate-700 rounded text-white transition-colors"
@click="${this.onToggleAlertFrameButtonClick}"
>
<img src=${sirenIcon} alt="alertFrame" width="20" height="20" />
<div class="flex-1">
<div class="font-medium">
${translateText("user_setting.alert_frame_label")}
</div>
<div class="text-sm text-slate-400">
${translateText("user_setting.alert_frame_desc")}
</div>
</div>
<div class="text-sm text-slate-400">
${this.userSettings.alertFrame()
? translateText("user_setting.on")
: translateText("user_setting.off")}
</div>
</button>
<button
class="flex gap-3 items-center w-full text-left p-3 hover:bg-slate-700 rounded text-white transition-colors"
@click="${this.onToggleStructureSpritesButtonClick}"