## Description:

Center pop & gold information, and split up & remove the top bar.

Before:

<img width="1913" alt="Screenshot 2025-07-07 at 7 54 55 PM"
src="https://github.com/user-attachments/assets/8ab59102-1f0e-4023-905f-e0b1b2fa73a2"
/>


After:

<img width="1916" alt="Screenshot 2025-07-07 at 7 53 02 PM"
src="https://github.com/user-attachments/assets/06c885e3-a179-4447-9d1d-0da3849e726d"
/>


reference:

![image](https://github.com/user-attachments/assets/8c985a1a-e232-45ce-9af7-c1981df7fb15)

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

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

evan
This commit is contained in:
evanpelle
2025-07-07 20:06:14 -07:00
committed by GitHub
parent a387909262
commit 2cbe34ed79
8 changed files with 129 additions and 124 deletions
@@ -77,7 +77,7 @@ export class GameLeftSidebar extends LitElement implements Layer {
render() {
return html`
<aside
class=${`fixed top-[90px] left-0 z-[1000] flex flex-col max-h-[calc(100vh-80px)] overflow-y-auto p-2 bg-slate-800/40 backdrop-blur-sm shadow-xs rounded-tr-lg rounded-br-lg transition-transform duration-300 ease-out transform ${
class=${`fixed top-[20px] left-0 z-[1000] flex flex-col max-h-[calc(100vh-80px)] overflow-y-auto p-2 bg-slate-800/40 backdrop-blur-sm shadow-xs rounded-tr-lg rounded-br-lg transition-transform duration-300 ease-out transform ${
this.isVisible ? "translate-x-0" : "-translate-x-full"
}`}
>
+86 -54
View File
@@ -5,16 +5,21 @@ import pauseIcon from "../../../../resources/images/PauseIconWhite.svg";
import playIcon from "../../../../resources/images/PlayIconWhite.svg";
import replayRegularIcon from "../../../../resources/images/ReplayRegularIconWhite.svg";
import replaySolidIcon from "../../../../resources/images/ReplaySolidIconWhite.svg";
import settingsIcon from "../../../../resources/images/SettingIconWhite.svg";
import { EventBus } from "../../../core/EventBus";
import { GameType } from "../../../core/game/Game";
import { GameUpdateType } from "../../../core/game/GameUpdates";
import { GameView } from "../../../core/game/GameView";
import { PauseGameEvent } from "../../Transport";
import { Layer } from "./Layer";
import { ShowReplayPanelEvent } from "./ReplayPanel";
import { ShowSettingsModalEvent } from "./SettingsModal";
@customElement("game-right-sidebar")
export class GameRightSidebar extends LitElement implements Layer {
public game: GameView;
public eventBus: EventBus;
@state()
private _isSinglePlayer: boolean = false;
@@ -28,7 +33,9 @@ export class GameRightSidebar extends LitElement implements Layer {
private isPaused: boolean = false;
@state()
private isExistButtonVisible: boolean = true;
private timer: number = 0;
private hasWinner = false;
createRenderRoot() {
return this;
@@ -44,13 +51,31 @@ export class GameRightSidebar extends LitElement implements Layer {
}
tick() {
if (!this.game.inSpawnPhase()) {
this.isExistButtonVisible = false;
// Timer logic
const updates = this.game.updatesSinceLastTick();
if (updates) {
this.hasWinner = this.hasWinner || updates[GameUpdateType.Win].length > 0;
}
if (this.game.inSpawnPhase()) {
this.timer = 0;
} else if (!this.hasWinner && this.game.ticks() % 10 === 0) {
this.timer++;
}
}
private secondsToHms = (d: number): string => {
const h = Math.floor(d / 3600);
const m = Math.floor((d % 3600) / 60);
const s = Math.floor((d % 3600) % 60);
let time = d === 0 ? "-" : `${s}s`;
if (m > 0) time = `${m}m` + time;
if (h > 0) time = `${h}h` + time;
return time;
};
private toggleReplayPanel(): void {
this._isReplayVisible = !this._isReplayVisible;
this.eventBus.emit(new ShowReplayPanelEvent(this._isReplayVisible));
}
private onPauseButtonClick() {
@@ -68,10 +93,16 @@ export class GameRightSidebar extends LitElement implements Layer {
window.location.href = "/";
}
private onSettingsButtonClick() {
this.eventBus.emit(new ShowSettingsModalEvent(true));
}
render() {
if (this.game === undefined) return html``;
return html`
<aside
class=${`fixed top-[90px] right-0 z-[1000] flex flex-col max-h-[calc(100vh-80px)] overflow-y-auto p-2 bg-slate-800/40 backdrop-blur-sm shadow-xs rounded-tl-lg rounded-bl-lg transition-transform duration-300 ease-out transform ${
class=${`flex flex-col max-h-[calc(100vh-80px)] overflow-y-auto p-2 bg-gray-800/70 backdrop-blur-sm shadow-xs rounded-tl-lg rounded-bl-lg transition-transform duration-300 ease-out transform ${
this._isVisible ? "translate-x-0" : "translate-x-full"
}`}
@contextmenu=${(e: Event) => e.preventDefault()}
@@ -81,59 +112,60 @@ export class GameRightSidebar extends LitElement implements Layer {
this._isReplayVisible ? "mb-2" : ""
}`}
>
${this._isSinglePlayer || this.game?.config()?.isReplay()
? html`
<div
class="w-6 h-6 cursor-pointer"
@click=${this.toggleReplayPanel}
>
<img
src=${this._isReplayVisible
? replaySolidIcon
: replayRegularIcon}
alt="replay"
width="20"
height="20"
style="vertical-align: middle;"
/>
</div>
<div
class="w-6 h-6 cursor-pointer"
@click=${this.onPauseButtonClick}
>
<img
src=${this.isPaused ? playIcon : pauseIcon}
alt="play/pause"
width="20"
height="20"
style="vertical-align: middle;"
/>
</div>
${this.isExistButtonVisible
? html`
<div
class="w-6 h-6 cursor-pointer"
@click=${this.onExitButtonClick}
>
<img
src=${exitIcon}
alt="exit"
width="20"
height="20"
/>
</div>
`
: null}
`
: null}
${this.maybeRenderReplayButtons()}
<div
class="w-6 h-6 cursor-pointer"
@click=${this.onSettingsButtonClick}
>
<img
src=${settingsIcon}
alt="settings"
width="20"
height="20"
style="vertical-align: middle;"
/>
</div>
<div class="w-6 h-6 cursor-pointer" @click=${this.onExitButtonClick}>
<img src=${exitIcon} alt="exit" width="20" height="20" />
</div>
</div>
<div class="block lg:flex flex-wrap gap-2">
<replay-panel
.isSingleplayer="${this._isSinglePlayer}"
.visible="${this._isReplayVisible}"
></replay-panel>
<!-- Timer display below buttons -->
<div class="flex justify-center items-center mt-2">
<div
class="w-[70px] h-8 lg:w-24 lg:h-10 border border-slate-400 p-0.5 text-xs md:text-sm lg:text-base flex items-center justify-center text-white px-1"
>
${this.secondsToHms(this.timer)}
</div>
</div>
</aside>
`;
}
maybeRenderReplayButtons() {
if (this._isSinglePlayer || this.game?.config()?.isReplay()) {
return html` <div
class="w-6 h-6 cursor-pointer"
@click=${this.toggleReplayPanel}
>
<img
src=${this._isReplayVisible ? replaySolidIcon : replayRegularIcon}
alt="replay"
width="20"
height="20"
style="vertical-align: middle;"
/>
</div>
<div class="w-6 h-6 cursor-pointer" @click=${this.onPauseButtonClick}>
<img
src=${this.isPaused ? playIcon : pauseIcon}
alt="play/pause"
width="20"
height="20"
style="vertical-align: middle;"
/>
</div>`;
} else {
return html``;
}
}
}
+22 -62
View File
@@ -1,8 +1,7 @@
import { html, LitElement } from "lit";
import { customElement, state } from "lit/decorators.js";
import { customElement } from "lit/decorators.js";
import goldCoinIcon from "../../../../resources/images/GoldCoinIcon.svg";
import populationIcon from "../../../../resources/images/PopulationIconSolidWhite.svg";
import settingsIcon from "../../../../resources/images/SettingIconWhite.svg";
import troopIcon from "../../../../resources/images/TroopIconWhite.svg";
import workerIcon from "../../../../resources/images/WorkerIconWhite.svg";
import { EventBus } from "../../../core/EventBus";
@@ -11,7 +10,6 @@ import { GameView } from "../../../core/game/GameView";
import { UserSettings } from "../../../core/game/UserSettings";
import { renderNumber, renderTroops } from "../../Utils";
import { Layer } from "./Layer";
import { ShowSettingsModalEvent } from "./SettingsModal";
@customElement("game-top-bar")
export class GameTopBar extends LitElement implements Layer {
@@ -25,9 +23,6 @@ export class GameTopBar extends LitElement implements Layer {
private _popRateIsIncreasing = false;
private hasWinner = false;
@state()
private timer: number = 0;
createRenderRoot() {
return this;
}
@@ -46,18 +41,9 @@ export class GameTopBar extends LitElement implements Layer {
if (updates) {
this.hasWinner = this.hasWinner || updates[GameUpdateType.Win].length > 0;
}
if (this.game.inSpawnPhase()) {
this.timer = 0;
} else if (!this.hasWinner && this.game.ticks() % 10 === 0) {
this.timer++;
}
this.requestUpdate();
}
private onSettingsButtonClick() {
this.eventBus.emit(new ShowSettingsModalEvent(true));
}
private updatePopulationIncrease() {
const player = this.game?.myPlayer();
if (player === null) return;
@@ -69,16 +55,6 @@ export class GameTopBar extends LitElement implements Layer {
}
}
private secondsToHms = (d: number): string => {
const h = Math.floor(d / 3600);
const m = Math.floor((d % 3600) / 60);
const s = Math.floor((d % 3600) % 60);
let time = d === 0 ? "-" : `${s}s`;
if (m > 0) time = `${m}m` + time;
if (h > 0) time = `${h}h` + time;
return time;
};
render() {
const myPlayer = this.game?.myPlayer();
if (!this.game || !myPlayer || this.game.inSpawnPhase()) {
@@ -89,14 +65,8 @@ export class GameTopBar extends LitElement implements Layer {
if (isAlt) {
return html`
<div
class="fixed top-0 left-auto right-0 z-[1100] bg-slate-800/40 backdrop-blur-sm p-2 flex justify-center items-center"
>
<div
class="w-[70px] h-8 lg:w-24 lg:h-10 border border-slate-400 p-0.5 text-xs md:text-sm lg:text-base flex items-center text-white px-1"
>
${this.secondsToHms(this.timer)}
</div>
</div>
class="absolute top-4 left-1/2 transform -translate-x-1/2 flex justify-center items-center p-2"
></div>
`;
}
const popRate = myPlayer
@@ -109,19 +79,17 @@ export class GameTopBar extends LitElement implements Layer {
return html`
<div
class="fixed top-0 min-h-[50px] lg:min-h-[80px] z-[1100] flex flex-wrap bg-slate-800/40 backdrop-blur-sm shadow-xs text-white text-xs md:text-sm lg:text-base left-0 right-0 grid-cols-4 p-1 md:px-1.5 lg:px-4"
class="absolute top-4 left-1/2 transform -translate-x-1/2 flex justify-center items-center p-1 md:px-1.5 lg:px-4 z-[1100]"
>
<div
class="flex flex-1 basis-full justify-between items-center gap-1 w-full"
>
<div class="flex justify-center items-center gap-1">
${myPlayer?.isAlive() && !this.game.inSpawnPhase()
? html`
<div class="overflow-x-auto hide-scrollbar flex-1 max-w-[85vw]">
<div class="overflow-x-auto hide-scrollbar">
<div
class="grid gap-1 grid-cols-[80px_100px_80px] w-max md:gap-2 md:grid-cols-[90px_120px_90px]"
>
<div
class="flex flex-wrap gap-1 flex-col bg-slate-800/20 border border-slate-400 p-0.5 md:px-1 lg:px-2"
class="flex flex-wrap gap-1 flex-col bg-gray-800/70 border border-slate-400 p-0.5 md:px-1 lg:px-2"
>
<div class="flex gap-2 items-center justify-between">
<img
@@ -131,12 +99,16 @@ export class GameTopBar extends LitElement implements Layer {
height="20"
style="vertical-align: middle;"
/>
+${renderNumber(goldPerSecond)}
<span class="text-white"
>+${renderNumber(goldPerSecond)}</span
>
</div>
<div class="text-white">
${renderNumber(myPlayer.gold())}
</div>
<div>${renderNumber(myPlayer.gold())}</div>
</div>
<div
class="flex flex-wrap gap-1 flex-col bg-slate-800/20 border border-slate-400 p-0.5 md:px-1 lg:px-2"
class="flex flex-wrap gap-1 flex-col bg-gray-800/70 border border-slate-400 p-0.5 md:px-1 lg:px-2"
>
<div class="flex gap-2 items-center justify-between">
<img
@@ -155,13 +127,13 @@ export class GameTopBar extends LitElement implements Layer {
+${renderTroops(popRate)}
</span>
</div>
<div>
<div class="text-white">
${renderTroops(myPlayer.population())} /
${renderTroops(maxPop)}
</div>
</div>
<div
class="flex bg-slate-800/20 border border-slate-400 p-0.5 md:px-1 lg:px-2"
class="flex bg-gray-800/70 border border-slate-400 p-0.5 md:px-1 lg:px-2"
>
<div class="flex flex-col flex-grow gap-1 w-full ">
<div class="flex gap-1">
@@ -172,7 +144,9 @@ export class GameTopBar extends LitElement implements Layer {
height="20"
style="vertical-align: middle;"
/>
${renderTroops(this._troops)}
<span class="text-white"
>${renderTroops(this._troops)}</span
>
</div>
<div class="flex gap-1">
<img
@@ -182,7 +156,9 @@ export class GameTopBar extends LitElement implements Layer {
height="20"
style="vertical-align: middle;"
/>
${renderTroops(this._workers)}
<span class="text-white"
>${renderTroops(this._workers)}</span
>
</div>
</div>
</div>
@@ -190,22 +166,6 @@ export class GameTopBar extends LitElement implements Layer {
</div>
`
: html`<div></div>`}
<div class="flex gap-1 items-center">
<div
class="w-[70px] h-8 lg:w-24 lg:h-10 border border-slate-400 p-0.5 text-xs md:text-sm lg:text-base flex items-center px-1"
>
${this.secondsToHms(this.timer)}
</div>
<img
class="cursor-pointer bg-slate-800/20 border border-slate-400 p-0.5"
src=${settingsIcon}
alt="settings"
width="28"
height="28"
style="vertical-align: middle;"
@click=${this.onSettingsButtonClick}
/>
</div>
</div>
</div>
`;
+2 -2
View File
@@ -193,10 +193,10 @@ export class Leaderboard extends LitElement implements Layer {
</button>
<div
class="grid bg-slate-800/70 w-full text-xs md:text-sm lg:text-base"
class="grid bg-gray-800/70 w-full text-xs md:text-sm lg:text-base"
style="grid-template-columns: 35px 100px 85px 65px 65px;"
>
<div class="contents font-bold bg-slate-700/50">
<div class="contents font-bold bg-gray-700/50">
<div class="py-1.5 md:py-2.5 text-center border-b border-slate-500">
#
</div>
@@ -356,7 +356,7 @@ export class PlayerInfoOverlay extends LitElement implements Layer {
@contextmenu=${(e) => e.preventDefault()}
>
<div
class="bg-slate-800/40 backdrop-blur-sm shadow-xs rounded-lg shadow-lg transition-all duration-300 text-white text-lg md:text-base ${containerClasses}"
class="bg-gray-800/70 backdrop-blur-sm shadow-xs rounded-lg shadow-lg transition-all duration-300 text-white text-lg md:text-base ${containerClasses}"
>
${this.player !== null ? this.renderPlayerInfo(this.player) : ""}
${this.unit !== null ? this.renderUnitInfo(this.unit) : ""}
+12 -2
View File
@@ -10,6 +10,10 @@ import {
import { translateText } from "../../Utils";
import { Layer } from "./Layer";
export class ShowReplayPanelEvent {
constructor(public visible: boolean = true) {}
}
@customElement("replay-panel")
export class ReplayPanel extends LitElement implements Layer {
public game: GameView | undefined;
@@ -28,7 +32,13 @@ export class ReplayPanel extends LitElement implements Layer {
return this; // Enable Tailwind CSS
}
init() {}
init() {
if (this.eventBus) {
this.eventBus.on(ShowReplayPanelEvent, (event: ShowReplayPanelEvent) => {
this.visible = event.visible;
});
}
}
tick() {
if (!this.visible) return;
@@ -52,7 +62,7 @@ export class ReplayPanel extends LitElement implements Layer {
return html`
<div
class="bg-opacity-60 bg-gray-900 p-1 lg:p-2 rounded-es-sm lg:rounded-lg backdrop-blur-md"
class="flex-shrink-0 bg-opacity-60 bg-gray-900 p-1 lg:p-2 rounded-es-sm lg:rounded-lg backdrop-blur-md"
@contextmenu=${(e: Event) => e.preventDefault()}
>
<label class="block mb-1 text-white" translate="no">
+1 -1
View File
@@ -91,7 +91,7 @@ export class UnitDisplay extends LitElement implements Layer {
return html`
<div
class="fixed bottom-4 left-1/2 transform -translate-x-1/2 z-[1100] bg-slate-800/40 backdrop-blur-sm border border-slate-400 rounded-lg p-2 hidden lg:block"
class="fixed bottom-4 left-1/2 transform -translate-x-1/2 z-[1100] bg-gray-800/70 backdrop-blur-sm border border-slate-400 rounded-lg p-2 hidden lg:block"
>
<div class="grid grid-rows-1 auto-cols-max grid-flow-col gap-1">
${this.renderUnitItem(cityIcon, this._cities, UnitType.City, "city")}
+4 -1
View File
@@ -361,7 +361,10 @@
<game-starting-modal></game-starting-modal>
<game-top-bar></game-top-bar>
<unit-display></unit-display>
<game-right-sidebar></game-right-sidebar>
<div class="flex fixed top-[20px] right-[20px] z-[1000] items-start gap-2">
<replay-panel></replay-panel>
<game-right-sidebar></game-right-sidebar>
</div>
<settings-modal></settings-modal>
<player-panel></player-panel>
<help-modal></help-modal>