diff --git a/src/client/graphics/layers/EventsDisplay.ts b/src/client/graphics/layers/EventsDisplay.ts
index 2d86f7cbd..7991ba85c 100644
--- a/src/client/graphics/layers/EventsDisplay.ts
+++ b/src/client/graphics/layers/EventsDisplay.ts
@@ -1,10 +1,7 @@
-import { LitElement, html, css } from 'lit';
-import { customElement, property, state } from 'lit/decorators.js';
+import { LitElement, html } from "lit";
+import { customElement, property, state } from "lit/decorators.js";
import { EventBus } from "../../../core/EventBus";
-import {
- AllPlayers,
- MessageType,
-} from "../../../core/game/Game";
+import { AllPlayers, MessageType } from "../../../core/game/Game";
import { DisplayMessageUpdate } from "../../../core/game/GameUpdates";
import { EmojiUpdate } from "../../../core/game/GameUpdates";
import { TargetPlayerUpdate } from "../../../core/game/GameUpdates";
@@ -16,13 +13,13 @@ import { GameUpdateType } from "../../../core/game/GameUpdates";
import { ClientID } from "../../../core/Schemas";
import { Layer } from "./Layer";
import { SendAllianceReplyIntentEvent } from "../../Transport";
-import { unsafeHTML } from 'lit/directives/unsafe-html.js';
-import { onlyImages, sanitize } from '../../../core/Util';
-import { GameView, PlayerView } from '../../../core/game/GameView';
+import { unsafeHTML } from "lit/directives/unsafe-html.js";
+import { onlyImages, sanitize } from "../../../core/Util";
+import { GameView, PlayerView } from "../../../core/game/GameView";
interface Event {
description: string;
- unsafeDescription?: boolean
+ unsafeDescription?: boolean;
buttons?: {
text: string;
className: string;
@@ -34,7 +31,7 @@ interface Event {
onDelete?: () => void;
}
-@customElement('events-display')
+@customElement("events-display")
export class EventsDisplay extends LitElement implements Layer {
public eventBus: EventBus;
public game: GameView;
@@ -42,131 +39,32 @@ export class EventsDisplay extends LitElement implements Layer {
private events: Event[] = [];
- static styles = css`
- :host {
- display: block;
- position: fixed;
- bottom: 10px;
- right: 10px;
- z-index: 1000;
- max-width: 800px;
- }
-
- .events-table {
- width: 100%;
- border-collapse: collapse;
- background-color: rgba(0, 0, 0, 0.7);
- color: white;
- box-shadow: 0 0 20px rgba(0, 0, 0, 0.1);
- font-size: 0.9em;
- }
-
- .events-table th,
- .events-table td {
- padding: 8px 12px;
- text-align: left;
- border-bottom: 1px solid rgba(255, 255, 255, 0.0);
- z-index: 1000;
- }
-
- .events-table th {
- background-color: rgba(0, 0, 0, 0.0);
- font-size: 1em;
- text-transform: uppercase;
- }
-
- .events-table tr:hover {
- background-color: rgba(255, 255, 255, 0.0);
- }
-
- .btn {
- display: inline-block;
- padding: 4px 12px;
- margin: 3px 8px 3px 0;
- background-color: #4CAF50;
- color: white;
- text-decoration: none;
- border-radius: 4px;
- transition: background-color 0.3s;
- border: none;
- cursor: pointer;
- font-size: 0.9em;
- }
-
- .btn:hover {
- background-color: #45a049;
- }
-
- .btn-info {
- background-color: #2196F3;
- }
-
- .btn-info:hover {
- background-color: #0b7dda;
- }
-
- .success td { color: rgb(120, 255, 140); }
- .info td { color: rgb(230, 230, 230); }
- .warn td { color: rgb(255, 220, 80) }
- .error td { color: rgb(255, 100, 100); }
-
- .button-container {
- display: flex;
- gap: 6px;
- flex-wrap: wrap;
- }
-
- @media (max-width: 600px) {
- :host {
- bottom: auto;
- top: 10px;
- right: 10px;
- left: 10px;
- max-width: calc(100% - 20px);
- }
-
- .events-table th,
- .events-table td {
- padding: 6px 10px;
- }
-
- .btn {
- display: block;
- margin: 3px 0;
- width: 100%;
- }
-
- .button-container {
- flex-direction: column;
- }
- }
- `;
-
private updateMap = new Map([
- [GameUpdateType.DisplayEvent, u => this.onDisplayMessageEvent(u)],
- [GameUpdateType.AllianceRequest, u => this.onAllianceRequestEvent(u)],
- [GameUpdateType.AllianceRequestReply, u => this.onAllianceRequestReplyEvent(u)],
- [GameUpdateType.BrokeAlliance, u => this.onBrokeAllianceEvent(u)],
- [GameUpdateType.TargetPlayer, u => this.onTargetPlayerEvent(u)],
- [GameUpdateType.EmojiUpdate, u => this.onEmojiMessageEvent(u)]
- ])
+ [GameUpdateType.DisplayEvent, (u) => this.onDisplayMessageEvent(u)],
+ [GameUpdateType.AllianceRequest, (u) => this.onAllianceRequestEvent(u)],
+ [
+ GameUpdateType.AllianceRequestReply,
+ (u) => this.onAllianceRequestReplyEvent(u),
+ ],
+ [GameUpdateType.BrokeAlliance, (u) => this.onBrokeAllianceEvent(u)],
+ [GameUpdateType.TargetPlayer, (u) => this.onTargetPlayerEvent(u)],
+ [GameUpdateType.EmojiUpdate, (u) => this.onEmojiMessageEvent(u)],
+ ]);
constructor() {
super();
this.events = [];
}
- init() {
- }
+ init() {}
tick() {
- const updates = this.game.updatesSinceLastTick()
+ const updates = this.game.updatesSinceLastTick();
for (const [ut, fn] of this.updateMap) {
- updates[ut]?.forEach(u => fn(u))
+ updates[ut]?.forEach((u) => fn(u));
}
-
- let remainingEvents = this.events.filter(event => {
+ let remainingEvents = this.events.filter((event) => {
const shouldKeep = this.game.ticks() - event.createdAt < 80;
if (!shouldKeep && event.onDelete) {
event.onDelete();
@@ -180,19 +78,19 @@ export class EventsDisplay extends LitElement implements Layer {
if (this.events.length !== remainingEvents.length) {
this.events = remainingEvents;
- this.requestUpdate()
+ this.requestUpdate();
}
}
private addEvent(event: Event) {
this.events = [...this.events, event];
- this.requestUpdate()
+ this.requestUpdate();
}
private removeEvent(index: number) {
this.events = [
...this.events.slice(0, index),
- ...this.events.slice(index + 1)
+ ...this.events.slice(index + 1),
];
}
@@ -200,11 +98,14 @@ export class EventsDisplay extends LitElement implements Layer {
return false;
}
- renderLayer(): void { }
+ renderLayer(): void {}
onDisplayMessageEvent(event: DisplayMessageUpdate) {
const myPlayer = this.game.playerByClientID(this.clientID);
- if (event.playerID != null && (!myPlayer || myPlayer.smallID() !== event.playerID)) {
+ if (
+ event.playerID != null &&
+ (!myPlayer || myPlayer.smallID() !== event.playerID)
+ ) {
return;
}
@@ -223,8 +124,12 @@ export class EventsDisplay extends LitElement implements Layer {
return;
}
- const requestor = this.game.playerBySmallID(update.requestorID) as PlayerView
- const recipient = this.game.playerBySmallID(update.recipientID) as PlayerView
+ const requestor = this.game.playerBySmallID(
+ update.requestorID
+ ) as PlayerView;
+ const recipient = this.game.playerBySmallID(
+ update.recipientID
+ ) as PlayerView;
this.addEvent({
description: `${requestor.name()} requests an alliance!`,
@@ -232,24 +137,27 @@ export class EventsDisplay extends LitElement implements Layer {
{
text: "Accept",
className: "btn",
- action: () => this.eventBus.emit(
- new SendAllianceReplyIntentEvent(requestor, recipient, true)
- ),
+ action: () =>
+ this.eventBus.emit(
+ new SendAllianceReplyIntentEvent(requestor, recipient, true)
+ ),
},
{
text: "Reject",
- className: "btn btn-info",
- action: () => this.eventBus.emit(
- new SendAllianceReplyIntentEvent(requestor, recipient, false)
- ),
- }
+ className: "btn-info",
+ action: () =>
+ this.eventBus.emit(
+ new SendAllianceReplyIntentEvent(requestor, recipient, false)
+ ),
+ },
],
highlight: true,
type: MessageType.INFO,
createdAt: this.game.ticks(),
- onDelete: () => this.eventBus.emit(
- new SendAllianceReplyIntentEvent(requestor, recipient, false)
- )
+ onDelete: () =>
+ this.eventBus.emit(
+ new SendAllianceReplyIntentEvent(requestor, recipient, false)
+ ),
});
}
@@ -259,10 +167,14 @@ export class EventsDisplay extends LitElement implements Layer {
return;
}
- const recipient = this.game.playerBySmallID(update.request.recipientID) as PlayerView
+ const recipient = this.game.playerBySmallID(
+ update.request.recipientID
+ ) as PlayerView;
this.addEvent({
- description: `${recipient.name()} ${update.accepted ? "accepted" : "rejected"} your alliance request`,
+ description: `${recipient.name()} ${
+ update.accepted ? "accepted" : "rejected"
+ } your alliance request`,
type: update.accepted ? MessageType.SUCCESS : MessageType.ERROR,
highlight: true,
createdAt: this.game.ticks(),
@@ -273,8 +185,8 @@ export class EventsDisplay extends LitElement implements Layer {
const myPlayer = this.game.playerByClientID(this.clientID);
if (!myPlayer) return;
- const betrayed = this.game.playerBySmallID(update.betrayedID) as PlayerView
- const traitor = this.game.playerBySmallID(update.traitorID) as PlayerView
+ const betrayed = this.game.playerBySmallID(update.betrayedID) as PlayerView;
+ const traitor = this.game.playerBySmallID(update.traitorID) as PlayerView;
if (!betrayed.isTraitor() && traitor === myPlayer) {
this.addEvent({
@@ -297,8 +209,13 @@ export class EventsDisplay extends LitElement implements Layer {
const myPlayer = this.game.playerByClientID(this.clientID);
if (!myPlayer) return;
- const otherID = update.player1ID === myPlayer.smallID() ? update.player2ID : update.player2ID === myPlayer.smallID() ? update.player1ID : null;
- const other = this.game.playerBySmallID(otherID) as PlayerView
+ const otherID =
+ update.player1ID === myPlayer.smallID()
+ ? update.player2ID
+ : update.player2ID === myPlayer.smallID()
+ ? update.player1ID
+ : null;
+ const other = this.game.playerBySmallID(otherID) as PlayerView;
if (!other || !myPlayer.isAlive() || !other.isAlive()) return;
this.addEvent({
@@ -310,11 +227,11 @@ export class EventsDisplay extends LitElement implements Layer {
}
onTargetPlayerEvent(event: TargetPlayerUpdate) {
- const other = this.game.playerBySmallID(event.playerID) as PlayerView
- const myPlayer = this.game.playerByClientID(this.clientID) as PlayerView
+ const other = this.game.playerBySmallID(event.playerID) as PlayerView;
+ const myPlayer = this.game.playerByClientID(this.clientID) as PlayerView;
if (!myPlayer || !myPlayer.isAlliedWith(other)) return;
- const target = this.game.playerBySmallID(event.targetID) as PlayerView
+ const target = this.game.playerBySmallID(event.targetID) as PlayerView;
this.addEvent({
description: `${other.name()} requests you attack ${target.name()}`,
@@ -328,8 +245,13 @@ export class EventsDisplay extends LitElement implements Layer {
const myPlayer = this.game.playerByClientID(this.clientID);
if (!myPlayer) return;
- const recipient = update.emoji.recipientID == AllPlayers ? AllPlayers : this.game.playerBySmallID(update.emoji.recipientID)
- const sender = this.game.playerBySmallID(update.emoji.senderID) as PlayerView
+ const recipient =
+ update.emoji.recipientID == AllPlayers
+ ? AllPlayers
+ : this.game.playerBySmallID(update.emoji.recipientID);
+ const sender = this.game.playerBySmallID(
+ update.emoji.senderID
+ ) as PlayerView;
if (recipient == myPlayer) {
this.addEvent({
@@ -341,7 +263,9 @@ export class EventsDisplay extends LitElement implements Layer {
});
} else if (sender === myPlayer && recipient !== AllPlayers) {
this.addEvent({
- description: `Sent ${(recipient as PlayerView).displayName()}: ${update.emoji.message}`,
+ description: `Sent ${(recipient as PlayerView).displayName()}: ${
+ update.emoji.message
+ }`,
unsafeDescription: true,
type: MessageType.INFO,
highlight: true,
@@ -350,39 +274,79 @@ export class EventsDisplay extends LitElement implements Layer {
}
}
+ private getMessageTypeClasses(type: MessageType): string {
+ switch (type) {
+ case MessageType.SUCCESS:
+ return "text-green-300";
+ case MessageType.INFO:
+ return "text-gray-200";
+ case MessageType.WARN:
+ return "text-yellow-300";
+ case MessageType.ERROR:
+ return "text-red-300";
+ default:
+ return "text-white";
+ }
+ }
+
render() {
if (this.events.length === 0) {
return html``;
}
return html`
-
-
- ${this.events.map((event, index) => html`
-
- |
- ${event.unsafeDescription ? unsafeHTML(onlyImages(event.description)) : event.description}
- ${event.buttons ? html`
-
- ${event.buttons.map(btn => html`
-
- `)}
-
- ` : ''}
- |
-
- `)}
-
-
+
+
+
+ ${this.events.map(
+ (event, index) => html`
+
+ |
+ ${event.unsafeDescription
+ ? unsafeHTML(onlyImages(event.description))
+ : event.description}
+ ${event.buttons
+ ? html`
+
+ ${event.buttons.map(
+ (btn) => html`
+
+ `
+ )}
+
+ `
+ : ""}
+ |
+
+ `
+ )}
+
+
+
`;
}
-}
\ No newline at end of file
+
+ createRenderRoot() {
+ return this; // Required for Tailwind classes to work with Lit
+ }
+}
diff --git a/src/client/index.html b/src/client/index.html
index da675a0c7..98c3402b2 100644
--- a/src/client/index.html
+++ b/src/client/index.html
@@ -1,151 +1,209 @@
+
+
+
+
OpenFront (ALPHA)
-
-
-
-
OpenFront (ALPHA)
+
+
+
-
-
-
+
+
- /* Critical styles to prevent layout shift */
- .container {
- opacity: 1;
- transition: opacity 0.3s ease-in-out;
- }
-
+
+
-
-
+
+
+
+
-
-
-
-
+
+
+
+
+

+
+ (v0.14.0)
+
-
-
-
-
-

-
(v0.14.0)
+
+
+
-
-
-
+
-
+
+
+
+
-
-
-
-
+
+
+
-
-
-
+
+
+
-
-
-
+
+
+
+
-
-
-
-
+
+
+
+
+
+
+
+
+
+
-
-
-
-
-
-
-
-
-
-
-
-
+
-
-
+
+
-
-
-
-
-
\ No newline at end of file
+
+
+
+