Files
OpenFrontIO/src/client/graphics/layers/AlertFrame.ts
T
Scott Anderson 9163f0d710 Enable the @typescript-eslint/prefer-readonly eslint rule (#1859)
## Description:

Enable the `@typescript-eslint/prefer-readonly` eslint rule.

## 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
2025-08-18 20:09:21 -04:00

133 lines
2.9 KiB
TypeScript

import {
BrokeAllianceUpdate,
GameUpdateType,
} from "../../../core/game/GameUpdates";
import { LitElement, css, html } from "lit";
import { customElement, state } from "lit/decorators.js";
import { GameView } from "../../../core/game/GameView";
import { Layer } from "./Layer";
import { UserSettings } from "../../../core/game/UserSettings";
// Parameters for the alert animation
const ALERT_SPEED = 1.6;
const ALERT_COUNT = 2;
@customElement("alert-frame")
export class AlertFrame extends LitElement implements Layer {
public game: GameView;
private readonly userSettings: UserSettings = new UserSettings();
@state()
private isActive = false;
private animationTimeout: number | null = null;
static styles = css`
.alert-border {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
pointer-events: none;
border: 17px solid #ee0000;
box-sizing: border-box;
z-index: 40;
opacity: 0;
}
.alert-border.animate {
animation: alertBlink ${ALERT_SPEED}s ease-in-out ${ALERT_COUNT};
}
@keyframes alertBlink {
0% {
opacity: 0;
}
50% {
opacity: 1;
}
100% {
opacity: 0;
}
}
`;
constructor() {
super();
if (!document.querySelector("style[data-alert-frame]")) {
const styleEl = document.createElement("style");
styleEl.setAttribute("data-alert-frame", "");
styleEl.textContent = AlertFrame.styles.cssText;
document.head.appendChild(styleEl);
}
}
createRenderRoot() {
return this;
}
init() {
// Listen for BrokeAllianceUpdate events directly from game updates
}
tick() {
if (!this.game) {
return; // Game not initialized yet
}
// Check for BrokeAllianceUpdate events
this.game
.updatesSinceLastTick()
?.[GameUpdateType.BrokeAlliance]?.forEach((update) => {
this.onBrokeAllianceUpdate(update);
});
}
// The alert frame is not affected by the camera transform
shouldTransform(): boolean {
return false;
}
private onBrokeAllianceUpdate(update: BrokeAllianceUpdate) {
const myPlayer = this.game.myPlayer();
if (!myPlayer) return;
const betrayed = this.game.playerBySmallID(update.betrayedID);
// Only trigger alert if the current player is the betrayed one
if (betrayed === myPlayer) {
this.activateAlert();
}
}
private activateAlert() {
if (this.userSettings.alertFrame()) {
this.isActive = true;
this.requestUpdate();
}
}
public dismissAlert() {
this.isActive = false;
if (this.animationTimeout) {
clearTimeout(this.animationTimeout);
this.animationTimeout = null;
}
this.requestUpdate();
}
render() {
if (!this.isActive) {
return html``;
}
return html`
<div
class="alert-border animate"
@animationend=${() => this.dismissAlert()}
></div>
`;
}
}