mirror of
https://github.com/openfrontio/OpenFrontIO.git
synced 2026-06-28 21:34:14 +00:00
9fb9be142a
## Description: Extend the winner schema to support multi-player wins (vote for peace, teams). ## 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
294 lines
7.1 KiB
TypeScript
294 lines
7.1 KiB
TypeScript
import { LitElement, css, html } from "lit";
|
|
import { customElement, state } from "lit/decorators.js";
|
|
import logo from "../../../../resources/images/ofm/logo_MASTER_2025.png";
|
|
import { translateText } from "../../../client/Utils";
|
|
import { EventBus } from "../../../core/EventBus";
|
|
import { GameUpdateType } from "../../../core/game/GameUpdates";
|
|
import { GameView } from "../../../core/game/GameView";
|
|
import { SendWinnerEvent } from "../../Transport";
|
|
import { GutterAdModalEvent } from "./GutterAdModal";
|
|
import { Layer } from "./Layer";
|
|
|
|
@customElement("win-modal")
|
|
export class WinModal extends LitElement implements Layer {
|
|
public game: GameView;
|
|
public eventBus: EventBus;
|
|
|
|
private hasShownDeathModal = false;
|
|
|
|
@state()
|
|
isVisible = false;
|
|
|
|
@state()
|
|
showButtons = false;
|
|
|
|
@state()
|
|
private showSteamContent = Math.random() > 0.5;
|
|
|
|
private _title: string;
|
|
|
|
// Override to prevent shadow DOM creation
|
|
createRenderRoot() {
|
|
return this;
|
|
}
|
|
|
|
static styles = css`
|
|
.win-modal {
|
|
display: none;
|
|
position: fixed;
|
|
top: 50%;
|
|
left: 50%;
|
|
transform: translate(-50%, -50%);
|
|
background-color: rgba(30, 30, 30, 0.7);
|
|
padding: 25px;
|
|
border-radius: 10px;
|
|
z-index: 9999;
|
|
box-shadow: 0 0 20px rgba(0, 0, 0, 0.5);
|
|
backdrop-filter: blur(5px);
|
|
color: white;
|
|
width: 350px;
|
|
transition:
|
|
opacity 0.3s ease-in-out,
|
|
visibility 0.3s ease-in-out;
|
|
}
|
|
|
|
.win-modal.visible {
|
|
display: block;
|
|
animation: fadeIn 0.3s ease-out;
|
|
}
|
|
|
|
@keyframes fadeIn {
|
|
from {
|
|
opacity: 0;
|
|
transform: translate(-50%, -48%);
|
|
}
|
|
to {
|
|
opacity: 1;
|
|
transform: translate(-50%, -50%);
|
|
}
|
|
}
|
|
|
|
.win-modal h2 {
|
|
margin: 0 0 15px 0;
|
|
font-size: 26px;
|
|
text-align: center;
|
|
color: white;
|
|
}
|
|
|
|
.win-modal p {
|
|
margin: 0 0 20px 0;
|
|
text-align: center;
|
|
background-color: rgba(0, 0, 0, 0.3);
|
|
padding: 10px;
|
|
border-radius: 5px;
|
|
}
|
|
|
|
.button-container {
|
|
display: flex;
|
|
justify-content: space-between;
|
|
gap: 10px;
|
|
}
|
|
|
|
.win-modal button {
|
|
flex: 1;
|
|
padding: 12px;
|
|
font-size: 16px;
|
|
cursor: pointer;
|
|
background: rgba(0, 150, 255, 0.6);
|
|
color: white;
|
|
border: none;
|
|
border-radius: 5px;
|
|
transition:
|
|
background-color 0.2s ease,
|
|
transform 0.1s ease;
|
|
}
|
|
|
|
.win-modal button:hover {
|
|
background: rgba(0, 150, 255, 0.8);
|
|
transform: translateY(-1px);
|
|
}
|
|
|
|
.win-modal button:active {
|
|
transform: translateY(1px);
|
|
}
|
|
|
|
@media (max-width: 768px) {
|
|
.win-modal {
|
|
width: 90%;
|
|
max-width: 300px;
|
|
padding: 20px;
|
|
}
|
|
|
|
.win-modal h2 {
|
|
font-size: 26px;
|
|
}
|
|
|
|
.win-modal button {
|
|
padding: 10px;
|
|
font-size: 14px;
|
|
}
|
|
}
|
|
`;
|
|
|
|
constructor() {
|
|
super();
|
|
// Add styles to document
|
|
const styleEl = document.createElement("style");
|
|
styleEl.textContent = WinModal.styles.toString();
|
|
document.head.appendChild(styleEl);
|
|
}
|
|
|
|
render() {
|
|
return html`
|
|
<div class="win-modal ${this.isVisible ? "visible" : ""}">
|
|
<h2>${this._title || ""}</h2>
|
|
${this.showSteamContent
|
|
? this.steamWishlist()
|
|
: this.openfrontMasters()}
|
|
<div
|
|
class="button-container ${this.showButtons ? "visible" : "hidden"}"
|
|
>
|
|
<button @click=${this._handleExit}>
|
|
${translateText("win_modal.exit")}
|
|
</button>
|
|
<button @click=${this.hide}>
|
|
${translateText("win_modal.keep")}
|
|
</button>
|
|
</div>
|
|
</div>
|
|
`;
|
|
}
|
|
|
|
steamWishlist() {
|
|
return html`<p>
|
|
<a
|
|
href="https://store.steampowered.com/app/3560670"
|
|
target="_blank"
|
|
rel="noopener noreferrer"
|
|
style="
|
|
color: #4a9eff;
|
|
text-decoration: underline;
|
|
font-weight: 500;
|
|
transition: color 0.2s ease;
|
|
font-size: 24px;
|
|
"
|
|
onmouseover="this.style.color='#6db3ff'"
|
|
onmouseout="this.style.color='#4a9eff'"
|
|
>
|
|
${translateText("win_modal.wishlist")}
|
|
</a>
|
|
</p>`;
|
|
}
|
|
|
|
openfrontMasters() {
|
|
return html`<p>
|
|
<img
|
|
src="${logo}"
|
|
alt="OpenFront Masters"
|
|
style="max-width: 100%; height: auto; margin-bottom: 16px;"
|
|
/>
|
|
<a
|
|
href="https://discord.gg/gStsGh5vWR"
|
|
target="_blank"
|
|
rel="noopener noreferrer"
|
|
style="
|
|
color: #4a9eff;
|
|
text-decoration: underline;
|
|
font-weight: 500;
|
|
transition: color 0.2s ease;
|
|
font-size: 24px;
|
|
"
|
|
onmouseover="this.style.color='#6db3ff'"
|
|
onmouseout="this.style.color='#4a9eff'"
|
|
>
|
|
Watch the best compete in the
|
|
<span style="font-weight: bold;">OpenFront Masters</span>
|
|
</a>
|
|
</p>`;
|
|
}
|
|
|
|
show() {
|
|
this.eventBus.emit(new GutterAdModalEvent(true));
|
|
setTimeout(() => {
|
|
this.isVisible = true;
|
|
this.requestUpdate();
|
|
}, 1500);
|
|
setTimeout(() => {
|
|
this.showButtons = true;
|
|
this.requestUpdate();
|
|
}, 3000);
|
|
}
|
|
|
|
hide() {
|
|
this.eventBus.emit(new GutterAdModalEvent(false));
|
|
this.isVisible = false;
|
|
this.showButtons = false;
|
|
this.requestUpdate();
|
|
}
|
|
|
|
private _handleExit() {
|
|
this.hide();
|
|
window.location.href = "/";
|
|
}
|
|
|
|
init() {}
|
|
|
|
tick() {
|
|
const myPlayer = this.game.myPlayer();
|
|
if (
|
|
!this.hasShownDeathModal &&
|
|
myPlayer &&
|
|
!myPlayer.isAlive() &&
|
|
!this.game.inSpawnPhase() &&
|
|
myPlayer.hasSpawned()
|
|
) {
|
|
this.hasShownDeathModal = true;
|
|
this._title = translateText("win_modal.died");
|
|
this.show();
|
|
}
|
|
const updates = this.game.updatesSinceLastTick();
|
|
const winUpdates = updates !== null ? updates[GameUpdateType.Win] : [];
|
|
winUpdates.forEach((wu) => {
|
|
if (wu.winner === undefined) {
|
|
// ...
|
|
} else if (wu.winner[0] === "team") {
|
|
this.eventBus.emit(new SendWinnerEvent(wu.winner, wu.allPlayersStats));
|
|
if (wu.winner[1] === this.game.myPlayer()?.team()) {
|
|
this._title = translateText("win_modal.your_team");
|
|
} else {
|
|
this._title = translateText("win_modal.other_team", {
|
|
team: wu.winner[1],
|
|
});
|
|
}
|
|
this.show();
|
|
} else {
|
|
const winner = this.game.playerByClientID(wu.winner[1]);
|
|
if (!winner?.isPlayer()) return;
|
|
const winnerClient = winner.clientID();
|
|
if (winnerClient !== null) {
|
|
this.eventBus.emit(
|
|
new SendWinnerEvent(["player", winnerClient], wu.allPlayersStats),
|
|
);
|
|
}
|
|
if (
|
|
winnerClient !== null &&
|
|
winnerClient === this.game.myPlayer()?.clientID()
|
|
) {
|
|
this._title = translateText("win_modal.you_won");
|
|
} else {
|
|
this._title = translateText("win_modal.other_won", {
|
|
player: winner.name(),
|
|
});
|
|
}
|
|
this.show();
|
|
}
|
|
});
|
|
}
|
|
|
|
renderLayer(/* context: CanvasRenderingContext2D */) {}
|
|
|
|
shouldTransform(): boolean {
|
|
return false;
|
|
}
|
|
}
|