mirror of
https://github.com/openfrontio/OpenFrontIO.git
synced 2026-06-30 10:12:14 +00:00
0c7da790f1
## Description: - **Dynamic sidebar offset for top bars** - GameLeftSidebar, GameRightSidebar, and PlayerInfoOverlay now shift down when SpawnTimer and/or ImmunityTimer bars are visible (7px per bar). Implemented via events. - **Fixed text overflow** in HeadsUpMessage.ts (Random spawn message is long) - **Fixed inconsistent text sizing** in EventsDisplay - **Alliance icon horizontal** in PlayerInfoOverlay so the size of the overlay doesn't change if there is an alliance - **Nation relation coloring** - Nation player names are now colored based on their relation - **Background & Blur Unification** - **Border Radius & Page Edge Gap Standardization** - **EventsDisplay collapsed button:** Fixed badge hidden / inline-block CSS conflict (conditional rendering), added gap-2 between text and badge - **Right panel spacing:** Changed right container from sm:w-1/2 to sm:flex-1 to fill remaining space - **Leaderboard**: Rounded grid corners (rounded-lg overflow-hidden), removed last-row border, added `willUpdate` for auto-refresh on hide/show click, plus button styled to match toggle buttons - Other little CSS fixes (margins etc) Showcase: (Note the red mexico name on betrayal) https://github.com/user-attachments/assets/f0ed91de-3a07-4564-a209-3d7723edee55 Two progress bars at the top, mobile UI not cut off: https://github.com/user-attachments/assets/83f1fd64-ceab-4a74-8d16-6e1eeea1709d HeadsUpMessage text overflow fixed, SpawnTimer does not cut off the PlayerInfoOverlay: <img width="516" height="929" alt="Screenshot 2026-02-14 214410" src="https://github.com/user-attachments/assets/74f0edea-8c01-4394-a3d0-a3245922e0da" /> Previous: <img width="306" height="118" alt="Screenshot 2026-02-14 213705" src="https://github.com/user-attachments/assets/a7c7e8f3-f0e8-4213-8a8f-4f3677e9fc98" /> Smaller event panel text: <img width="594" height="975" alt="Screenshot 2026-02-14 215738" src="https://github.com/user-attachments/assets/33e80570-9260-40b0-b810-c71eda4861fc" /> ## 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: FloPinguin
165 lines
5.2 KiB
TypeScript
165 lines
5.2 KiB
TypeScript
import { LitElement, html } from "lit";
|
|
import { customElement, state } from "lit/decorators.js";
|
|
import { GameType } from "../../../core/game/Game";
|
|
import { GameUpdateType } from "../../../core/game/GameUpdates";
|
|
import { GameView } from "../../../core/game/GameView";
|
|
import { translateText } from "../../Utils";
|
|
import { Layer } from "./Layer";
|
|
|
|
@customElement("heads-up-message")
|
|
export class HeadsUpMessage extends LitElement implements Layer {
|
|
public game: GameView;
|
|
|
|
@state()
|
|
private isVisible = false;
|
|
|
|
@state()
|
|
private isPaused = false;
|
|
|
|
@state()
|
|
private isImmunityActive = false;
|
|
|
|
@state()
|
|
private toastMessage: string | import("lit").TemplateResult | null = null;
|
|
@state()
|
|
private toastColor: "green" | "red" = "green";
|
|
private toastTimeout: number | null = null;
|
|
|
|
createRenderRoot() {
|
|
return this;
|
|
}
|
|
|
|
connectedCallback() {
|
|
super.connectedCallback();
|
|
window.addEventListener(
|
|
"show-message",
|
|
this.handleShowMessage as EventListener,
|
|
);
|
|
}
|
|
|
|
disconnectedCallback() {
|
|
super.disconnectedCallback();
|
|
window.removeEventListener(
|
|
"show-message",
|
|
this.handleShowMessage as EventListener,
|
|
);
|
|
if (this.toastTimeout) {
|
|
clearTimeout(this.toastTimeout);
|
|
}
|
|
}
|
|
|
|
private handleShowMessage = (event: CustomEvent) => {
|
|
const { message, duration, color } = event.detail ?? {};
|
|
if (
|
|
typeof message === "string" ||
|
|
(message && typeof message.values === "object")
|
|
) {
|
|
this.toastMessage = message;
|
|
this.toastColor = color === "red" ? "red" : "green";
|
|
this.requestUpdate();
|
|
if (this.toastTimeout) {
|
|
clearTimeout(this.toastTimeout);
|
|
}
|
|
this.toastTimeout = window.setTimeout(
|
|
() => {
|
|
this.toastMessage = null;
|
|
this.requestUpdate();
|
|
},
|
|
typeof duration === "number" ? (duration ?? 2000) : 2000,
|
|
);
|
|
}
|
|
};
|
|
|
|
init() {
|
|
this.isVisible = true;
|
|
this.requestUpdate();
|
|
}
|
|
|
|
tick() {
|
|
const updates = this.game.updatesSinceLastTick();
|
|
if (updates && updates[GameUpdateType.GamePaused].length > 0) {
|
|
const pauseUpdate = updates[GameUpdateType.GamePaused][0];
|
|
this.isPaused = pauseUpdate.paused;
|
|
}
|
|
|
|
const showImmunityHudDuration = 10 * 10;
|
|
const spawnEnd = this.game.config().numSpawnPhaseTurns();
|
|
const ticksSinceSpawnEnd = this.game.ticks() - spawnEnd;
|
|
|
|
this.isImmunityActive =
|
|
this.game.config().hasExtendedSpawnImmunity() &&
|
|
!this.game.inSpawnPhase() &&
|
|
this.game.isSpawnImmunityActive() &&
|
|
ticksSinceSpawnEnd < showImmunityHudDuration;
|
|
|
|
this.isVisible =
|
|
this.game.inSpawnPhase() || this.isPaused || this.isImmunityActive;
|
|
this.requestUpdate();
|
|
}
|
|
|
|
private getMessage(): string {
|
|
if (this.isPaused) {
|
|
if (this.game.config().gameConfig().gameType === GameType.Singleplayer) {
|
|
return translateText("heads_up_message.singleplayer_game_paused");
|
|
} else {
|
|
return translateText("heads_up_message.multiplayer_game_paused");
|
|
}
|
|
}
|
|
if (this.isImmunityActive) {
|
|
return translateText("heads_up_message.pvp_immunity_active", {
|
|
seconds: Math.round(this.game.config().spawnImmunityDuration() / 10),
|
|
});
|
|
}
|
|
return this.game.config().isRandomSpawn()
|
|
? translateText("heads_up_message.random_spawn")
|
|
: translateText("heads_up_message.choose_spawn");
|
|
}
|
|
|
|
render() {
|
|
return html`
|
|
<div style="pointer-events: none;">
|
|
${this.toastMessage
|
|
? html`
|
|
<div
|
|
class="fixed top-6 left-1/2 -translate-x-1/2 z-[11001] px-6 py-4 rounded-xl transition-all duration-300 animate-fade-in-out"
|
|
style="max-width: 90vw; min-width: 200px; text-align: center;
|
|
background: ${this.toastColor === "red"
|
|
? "rgba(239,68,68,0.1)"
|
|
: "rgba(34,197,94,0.1)"};
|
|
border: 1px solid ${this.toastColor === "red"
|
|
? "rgba(239,68,68,0.5)"
|
|
: "rgba(34,197,94,0.5)"};
|
|
color: white;
|
|
box-shadow: 0 0 30px 0 ${this.toastColor === "red"
|
|
? "rgba(239,68,68,0.3)"
|
|
: "rgba(34,197,94,0.3)"};
|
|
backdrop-filter: blur(12px);"
|
|
@contextmenu=${(e: MouseEvent) => e.preventDefault()}
|
|
>
|
|
${typeof this.toastMessage === "string"
|
|
? html`<span class="font-medium">${this.toastMessage}</span>`
|
|
: this.toastMessage}
|
|
</div>
|
|
`
|
|
: null}
|
|
${this.isVisible
|
|
? html`
|
|
<div
|
|
class="fixed top-[15%] left-1/2 -translate-x-1/2 z-[11000]
|
|
inline-flex items-center justify-center min-h-8 lg:min-h-10
|
|
w-fit max-w-[90vw]
|
|
bg-gray-800/70 rounded-md lg:rounded-lg
|
|
backdrop-blur-xs text-white text-md lg:text-xl px-3 lg:px-4 py-1
|
|
text-center break-words"
|
|
style="word-wrap: break-word; hyphens: auto;"
|
|
@contextmenu=${(e: MouseEvent) => e.preventDefault()}
|
|
>
|
|
${this.getMessage()}
|
|
</div>
|
|
`
|
|
: null}
|
|
</div>
|
|
`;
|
|
}
|
|
}
|