×
@@ -476,7 +481,7 @@ export class SinglePlayerModal extends LitElement {
detail: {
gameType: GameType.Singleplayer,
lobby: {
- id: generateID(),
+ gameID: generateID(),
},
map: this.selectedMap,
difficulty: this.selectedDifficulty,
diff --git a/src/client/Transport.ts b/src/client/Transport.ts
index c4fd5f700..a1dcffd05 100644
--- a/src/client/Transport.ts
+++ b/src/client/Transport.ts
@@ -9,6 +9,7 @@ import {
Player,
PlayerID,
PlayerType,
+ Tick,
UnitType,
} from "../core/game/Game";
import {
@@ -23,6 +24,7 @@ import {
GameConfig,
ClientLogMessageSchema,
ClientSendWinnerSchema,
+ ClientMessageSchema,
} from "../core/Schemas";
import { LobbyConfig } from "./ClientGameRunner";
import { LocalServer } from "./LocalServer";
@@ -100,6 +102,14 @@ export class SendDonateIntentEvent implements GameEvent {
) {}
}
+export class SendEmbargoIntentEvent implements GameEvent {
+ constructor(
+ public readonly sender: PlayerView,
+ public readonly target: PlayerView,
+ public readonly action: "start" | "stop",
+ ) {}
+}
+
export class SendSetTargetTroopRatioEvent implements GameEvent {
constructor(public readonly ratio: number) {}
}
@@ -107,6 +117,12 @@ export class SendSetTargetTroopRatioEvent implements GameEvent {
export class SendWinnerEvent implements GameEvent {
constructor(public readonly winner: ClientID) {}
}
+export class SendHashEvent implements GameEvent {
+ constructor(
+ public readonly tick: Tick,
+ public readonly hash: number,
+ ) {}
+}
export class Transport {
private socket: WebSocket;
@@ -151,6 +167,9 @@ export class Transport {
);
this.eventBus.on(SendEmojiIntentEvent, (e) => this.onSendEmojiIntent(e));
this.eventBus.on(SendDonateIntentEvent, (e) => this.onSendDonateIntent(e));
+ this.eventBus.on(SendEmbargoIntentEvent, (e) =>
+ this.onSendEmbargoIntent(e),
+ );
this.eventBus.on(SendSetTargetTroopRatioEvent, (e) =>
this.onSendSetTargetTroopRatioEvent(e),
);
@@ -159,6 +178,7 @@ export class Transport {
this.eventBus.on(SendLogEvent, (e) => this.onSendLogEvent(e));
this.eventBus.on(PauseGameEvent, (e) => this.onPauseGameEvent(e));
this.eventBus.on(SendWinnerEvent, (e) => this.onSendWinnerEvent(e));
+ this.eventBus.on(SendHashEvent, (e) => this.onSendHashEvent(e));
}
private startPing() {
@@ -219,9 +239,10 @@ export class Transport {
) {
this.startPing();
this.maybeKillSocket();
- const wsHost = process.env.WEBSOCKET_URL || window.location.host;
+ const wsHost = window.location.host;
const wsProtocol = window.location.protocol === "https:" ? "wss:" : "ws:";
- this.socket = new WebSocket(`${wsProtocol}//${wsHost}`);
+ const workerPath = this.serverConfig.workerPath(this.lobbyConfig.gameID);
+ this.socket = new WebSocket(`${wsProtocol}//${wsHost}/${workerPath}`);
this.onconnect = onconnect;
this.onmessage = onmessage;
this.socket.onopen = () => {
@@ -237,7 +258,9 @@ export class Transport {
const serverMsg = ServerMessageSchema.parse(JSON.parse(event.data));
this.onmessage(serverMsg);
} catch (error) {
- console.error("Failed to process server message:", error);
+ console.error(
+ `Failed to process server message ${event.data}: ${error}`,
+ );
}
};
this.socket.onerror = (err) => {
@@ -397,6 +420,16 @@ export class Transport {
});
}
+ private onSendEmbargoIntent(event: SendEmbargoIntentEvent) {
+ this.sendIntent({
+ type: "embargo",
+ clientID: this.lobbyConfig.clientID,
+ playerID: this.lobbyConfig.playerID,
+ targetID: event.target.id(),
+ action: event.action,
+ });
+ }
+
private onSendSetTargetTroopRatioEvent(event: SendSetTargetTroopRatioEvent) {
this.sendIntent({
type: "troop_ratio",
@@ -448,6 +481,26 @@ export class Transport {
}
}
+ private onSendHashEvent(event: SendHashEvent) {
+ if (this.isLocal || this.socket.readyState === WebSocket.OPEN) {
+ const msg = ClientMessageSchema.parse({
+ type: "hash",
+ clientID: this.lobbyConfig.clientID,
+ persistentID: this.lobbyConfig.persistentID,
+ gameID: this.lobbyConfig.gameID,
+ tick: event.tick,
+ hash: event.hash,
+ });
+ this.sendMsg(JSON.stringify(msg));
+ } else {
+ console.log(
+ "WebSocket is not open. Current state:",
+ this.socket.readyState,
+ );
+ console.log("attempting reconnect");
+ }
+ }
+
private sendIntent(intent: Intent) {
if (this.isLocal || this.socket.readyState === WebSocket.OPEN) {
const msg = ClientIntentMessageSchema.parse({
diff --git a/src/client/components/ModalOverlay.ts b/src/client/components/ModalOverlay.ts
new file mode 100644
index 000000000..8dc0cbbcf
--- /dev/null
+++ b/src/client/components/ModalOverlay.ts
@@ -0,0 +1,26 @@
+import { LitElement, html, css } from "lit";
+import { customElement, property } from "lit/decorators";
+
+@customElement("modal-overlay")
+export class ModalOverlay extends LitElement {
+ @property({ reflect: true }) public visible: boolean = false;
+
+ static styles = css`
+ .overlay {
+ position: absolute;
+ left: 0px;
+ top: 0px;
+ width: 100%;
+ height: 100%;
+ }
+ `;
+
+ render() {
+ return html`
+
(this.visible = false)}
+ >
+ `;
+ }
+}
diff --git a/src/client/graphics/TransformHandler.ts b/src/client/graphics/TransformHandler.ts
index ae4371a6e..5ee69af27 100644
--- a/src/client/graphics/TransformHandler.ts
+++ b/src/client/graphics/TransformHandler.ts
@@ -5,7 +5,7 @@ import {
calculateBoundingBox,
calculateBoundingBoxCenter,
} from "../../core/Util";
-import { ZoomEvent, DragEvent } from "../InputHandler";
+import { ZoomEvent, DragEvent, CenterCameraEvent } from "../InputHandler";
import { GoToPlayerEvent } from "./layers/Leaderboard";
import { placeName } from "./NameBoxCalculator";
import { GameView } from "../../core/game/GameView";
@@ -27,6 +27,7 @@ export class TransformHandler {
this.eventBus.on(ZoomEvent, (e) => this.onZoom(e));
this.eventBus.on(DragEvent, (e) => this.onMove(e));
this.eventBus.on(GoToPlayerEvent, (e) => this.onGoToPlayer(e));
+ this.eventBus.on(CenterCameraEvent, () => this.centerCamera());
}
boundingRect(): DOMRect {
@@ -148,6 +149,14 @@ export class TransformHandler {
this.intervalID = setInterval(() => this.goTo(), 1);
}
+ centerCamera() {
+ this.clearTarget();
+ const player = this.game.myPlayer();
+ if (!player || !player.nameLocation()) return;
+ this.target = new Cell(player.nameLocation().x, player.nameLocation().y);
+ this.intervalID = setInterval(() => this.goTo(), 1);
+ }
+
private goTo() {
const { screenX, screenY } = this.screenCenter();
const screenMapCenter = new Cell(screenX, screenY);
diff --git a/src/client/graphics/layers/ControlPanel.ts b/src/client/graphics/layers/ControlPanel.ts
index aeb98896b..1286c97d2 100644
--- a/src/client/graphics/layers/ControlPanel.ts
+++ b/src/client/graphics/layers/ControlPanel.ts
@@ -190,7 +190,7 @@ export class ControlPanel extends LitElement implements Layer {
e.preventDefault()}
>
diff --git a/src/client/graphics/layers/EventsDisplay.ts b/src/client/graphics/layers/EventsDisplay.ts
index 223219210..a479fc5e1 100644
--- a/src/client/graphics/layers/EventsDisplay.ts
+++ b/src/client/graphics/layers/EventsDisplay.ts
@@ -25,6 +25,7 @@ import { unsafeHTML } from "lit/directives/unsafe-html.js";
import { onlyImages, sanitize } from "../../../core/Util";
import { GameView, PlayerView } from "../../../core/game/GameView";
import { renderTroops } from "../../Utils";
+import { GoToPlayerEvent } from "./Leaderboard";
interface Event {
description: string;
@@ -33,6 +34,7 @@ interface Event {
text: string;
className: string;
action: () => void;
+ preventClose?: boolean;
}[];
type: MessageType;
highlight?: boolean;
@@ -53,6 +55,15 @@ export class EventsDisplay extends LitElement implements Layer {
@state() private incomingAttacks: AttackUpdate[] = [];
@state() private outgoingAttacks: AttackUpdate[] = [];
@state() private _hidden: boolean = false;
+ @state() private newEvents: number = 0;
+
+ private toggleHidden() {
+ this._hidden = !this._hidden;
+ if (this._hidden) {
+ this.newEvents = 0;
+ }
+ this.requestUpdate();
+ }
private updateMap = new Map([
[GameUpdateType.DisplayEvent, (u) => this.onDisplayMessageEvent(u)],
@@ -63,7 +74,7 @@ export class EventsDisplay extends LitElement implements Layer {
],
[GameUpdateType.BrokeAlliance, (u) => this.onBrokeAllianceEvent(u)],
[GameUpdateType.TargetPlayer, (u) => this.onTargetPlayerEvent(u)],
- [GameUpdateType.EmojiUpdate, (u) => this.onEmojiMessageEvent(u)],
+ [GameUpdateType.Emoji, (u) => this.onEmojiMessageEvent(u)],
]);
constructor() {
@@ -119,6 +130,9 @@ export class EventsDisplay extends LitElement implements Layer {
private addEvent(event: Event) {
this.events = [...this.events, event];
+ if (this._hidden == true) {
+ this.newEvents++;
+ }
this.requestUpdate();
}
@@ -169,6 +183,12 @@ export class EventsDisplay extends LitElement implements Layer {
this.addEvent({
description: `${requestor.name()} requests an alliance!`,
buttons: [
+ {
+ text: "Focus",
+ className: "btn-gray",
+ action: () => this.eventBus.emit(new GoToPlayerEvent(requestor)),
+ preventClose: true,
+ },
{
text: "Accept",
className: "btn",
@@ -397,7 +417,7 @@ export class EventsDisplay extends LitElement implements Layer {
@@ -406,7 +426,7 @@ export class EventsDisplay extends LitElement implements Layer {
._hidden
? "hidden"
: ""}"
- @click=${() => (this._hidden = true)}
+ @click=${this.toggleHidden}
>
Hide
@@ -415,9 +435,15 @@ export class EventsDisplay extends LitElement implements Layer {
class="text-white cursor-pointer pointer-events-auto ${this._hidden
? ""
: "hidden"}"
- @click=${() => (this._hidden = false)}
+ @click=${this.toggleHidden}
>
Events
+
${this.newEvents}
{
btn.action();
- this.removeEvent(index);
+ if (!btn.preventClose) {
+ this.removeEvent(index);
+ }
this.requestUpdate();
}}
>
diff --git a/src/client/graphics/layers/Leaderboard.ts b/src/client/graphics/layers/Leaderboard.ts
index d2a146f29..b1cdc8317 100644
--- a/src/client/graphics/layers/Leaderboard.ts
+++ b/src/client/graphics/layers/Leaderboard.ts
@@ -1,17 +1,18 @@
-import { LitElement, html, css } from "lit";
-import { customElement, property, state } from "lit/decorators.js";
-import { Layer } from "./Layer";
-import { ClientID } from "../../../core/Schemas";
+import { LitElement, css, html } from "lit";
+import { customElement, state } from "lit/decorators.js";
import { unsafeHTML } from "lit/directives/unsafe-html.js";
import { EventBus, GameEvent } from "../../../core/EventBus";
-import { renderNumber } from "../../Utils";
import { GameView, PlayerView } from "../../../core/game/GameView";
+import { ClientID } from "../../../core/Schemas";
+import { renderNumber } from "../../Utils";
+import { Layer } from "./Layer";
interface Entry {
name: string;
position: number;
score: string;
gold: string;
+ troops: string;
isMyPlayer: boolean;
player: PlayerView;
}
@@ -26,6 +27,13 @@ export class Leaderboard extends LitElement implements Layer {
public clientID: ClientID;
public eventBus: EventBus;
+ players: Entry[] = [];
+
+ @state()
+ private _leaderboardHidden = true;
+ private _shownOnInit = false;
+ private showTopFive = true;
+
init() {}
tick() {
@@ -58,14 +66,25 @@ export class Leaderboard extends LitElement implements Layer {
const numTilesWithoutFallout =
this.game.numLandTiles() - this.game.numTilesWithFallout();
- this.players = sorted.slice(0, 5).map((player, index) => ({
- name: player.displayName(),
- position: index + 1,
- score: formatPercentage(player.numTilesOwned() / numTilesWithoutFallout),
- gold: renderNumber(player.gold()),
- isMyPlayer: player == myPlayer,
- player: player,
- }));
+ const playersToShow = this.showTopFive ? sorted.slice(0, 5) : sorted;
+
+ this.players = playersToShow.map((player, index) => {
+ let troops = player.troops() / 10;
+ if (!player.isAlive()) {
+ troops = 0;
+ }
+ return {
+ name: player.displayName(),
+ position: index + 1,
+ score: formatPercentage(
+ player.numTilesOwned() / numTilesWithoutFallout,
+ ),
+ gold: renderNumber(player.gold()),
+ troops: renderNumber(troops),
+ isMyPlayer: player == myPlayer,
+ player: player,
+ };
+ });
if (myPlayer != null && this.players.find((p) => p.isMyPlayer) == null) {
let place = 0;
@@ -76,6 +95,10 @@ export class Leaderboard extends LitElement implements Layer {
}
}
+ let myPlayerTroops = myPlayer.troops() / 10;
+ if (!myPlayer.isAlive()) {
+ myPlayerTroops = 0;
+ }
this.players.pop();
this.players.push({
name: myPlayer.displayName(),
@@ -84,6 +107,7 @@ export class Leaderboard extends LitElement implements Layer {
myPlayer.numTilesOwned() / this.game.numLandTiles(),
),
gold: renderNumber(myPlayer.gold()),
+ troops: renderNumber(myPlayerTroops),
isMyPlayer: true,
player: myPlayer,
});
@@ -119,10 +143,10 @@ export class Leaderboard extends LitElement implements Layer {
padding-top: 0px;
box-shadow: 0 0 20px rgba(0, 0, 0, 0.5);
border-radius: 10px;
- max-width: 300px;
- max-height: 80vh;
+ max-width: 500px;
+ max-height: 30vh;
overflow-y: auto;
- width: 300px;
+ width: 400px;
backdrop-filter: blur(5px);
}
table {
@@ -180,6 +204,13 @@ export class Leaderboard extends LitElement implements Layer {
cursor: pointer;
}
+ .leaderboard-top-five-button {
+ background: none;
+ border: none;
+ color: white;
+ cursor: pointer;
+ }
+
.player-name {
max-width: 10ch;
overflow: hidden;
@@ -188,7 +219,8 @@ export class Leaderboard extends LitElement implements Layer {
@media (max-width: 1000px) {
.leaderboard {
- top: 60px;
+ top: 70px;
+ left: 0px;
}
.leaderboard-button {
@@ -198,13 +230,6 @@ export class Leaderboard extends LitElement implements Layer {
}
`;
- players: Entry[] = [];
-
- @state()
- private _leaderboardHidden = true;
-
- private _shownOnInit = false;
-
render() {
return html`
+
@@ -232,6 +266,7 @@ export class Leaderboard extends LitElement implements Layer {
| Player |
Owned |
Gold |
+ Troops |
@@ -245,6 +280,7 @@ export class Leaderboard extends LitElement implements Layer {
${unsafeHTML(player.name)} |
${player.score} |
${player.gold} |
+ ${player.troops} |
`,
)}
diff --git a/src/client/graphics/layers/NameLayer.ts b/src/client/graphics/layers/NameLayer.ts
index 555627039..49b3f4f49 100644
--- a/src/client/graphics/layers/NameLayer.ts
+++ b/src/client/graphics/layers/NameLayer.ts
@@ -11,8 +11,10 @@ import { Layer } from "./Layer";
import { TransformHandler } from "../TransformHandler";
import traitorIcon from "../../../../resources/images/TraitorIcon.svg";
import allianceIcon from "../../../../resources/images/AllianceIcon.svg";
+import allianceRequestIcon from "../../../../resources/images/AllianceRequestIcon.svg";
import crownIcon from "../../../../resources/images/CrownIcon.svg";
import targetIcon from "../../../../resources/images/TargetIcon.svg";
+import embargoIcon from "../../../../resources/images/EmbargoIcon.svg";
import { ClientID } from "../../../core/Schemas";
import { GameView, PlayerView } from "../../../core/game/GameView";
import { createCanvas, renderTroops } from "../../Utils";
@@ -40,9 +42,11 @@ export class NameLayer implements Layer {
private renders: RenderInfo[] = [];
private seenPlayers: Set = new Set();
private traitorIconImage: HTMLImageElement;
+ private allianceRequestIconImage: HTMLImageElement;
private allianceIconImage: HTMLImageElement;
private targetIconImage: HTMLImageElement;
private crownIconImage: HTMLImageElement;
+ private embargoIconImage: HTMLImageElement;
private container: HTMLDivElement;
private myPlayer: PlayerView | null = null;
private firstPlace: PlayerView | null = null;
@@ -57,10 +61,14 @@ export class NameLayer implements Layer {
this.traitorIconImage.src = traitorIcon;
this.allianceIconImage = new Image();
this.allianceIconImage.src = allianceIcon;
+ this.allianceRequestIconImage = new Image();
+ this.allianceRequestIconImage.src = allianceRequestIcon;
this.crownIconImage = new Image();
this.crownIconImage.src = crownIcon;
this.targetIconImage = new Image();
this.targetIconImage.src = targetIcon;
+ this.embargoIconImage = new Image();
+ this.embargoIconImage.src = embargoIcon;
}
resizeCanvas() {
@@ -162,6 +170,7 @@ export class NameLayer implements Layer {
iconsDiv.style.justifyContent = "center";
iconsDiv.style.alignItems = "center";
iconsDiv.style.zIndex = "2";
+ iconsDiv.style.opacity = "0.8";
element.appendChild(iconsDiv);
const nameDiv = document.createElement("div");
@@ -314,6 +323,23 @@ export class NameLayer implements Layer {
existingAlliance.remove();
}
+ // Alliance request icon
+ const data = '[data-icon="alliance-request"]';
+ const existingRequestAlliance = iconsDiv.querySelector(data);
+ if (myPlayer != null && render.player.isRequestingAllianceWith(myPlayer)) {
+ if (!existingRequestAlliance) {
+ iconsDiv.appendChild(
+ this.createIconElement(
+ this.allianceRequestIconImage.src,
+ iconSize,
+ "alliance-request",
+ ),
+ );
+ }
+ } else if (existingRequestAlliance) {
+ existingRequestAlliance.remove();
+ }
+
// Target icon
const existingTarget = iconsDiv.querySelector('[data-icon="target"]');
if (
@@ -359,6 +385,24 @@ export class NameLayer implements Layer {
existingEmoji.remove();
}
+ const existingEmbargo = iconsDiv.querySelector('[data-icon="embargo"]');
+ const hasEmbargo =
+ render.player.hasEmbargoAgainst(myPlayer) ||
+ myPlayer.hasEmbargoAgainst(render.player);
+ if (myPlayer && hasEmbargo) {
+ if (!existingEmbargo) {
+ iconsDiv.appendChild(
+ this.createIconElement(
+ this.embargoIconImage.src,
+ iconSize,
+ "embargo",
+ ),
+ );
+ }
+ } else if (existingEmbargo) {
+ existingEmbargo.remove();
+ }
+
// Update all icon sizes
const icons = iconsDiv.getElementsByTagName("img");
for (const icon of icons) {
diff --git a/src/client/graphics/layers/OptionsMenu.ts b/src/client/graphics/layers/OptionsMenu.ts
index af105ce08..e52772870 100644
--- a/src/client/graphics/layers/OptionsMenu.ts
+++ b/src/client/graphics/layers/OptionsMenu.ts
@@ -107,7 +107,7 @@ export class OptionsMenu extends LitElement implements Layer {
tick() {
this.hasWinner =
this.hasWinner ||
- this.game.updatesSinceLastTick()[GameUpdateType.WinUpdate].length > 0;
+ this.game.updatesSinceLastTick()[GameUpdateType.Win].length > 0;
if (this.game.inSpawnPhase()) {
this.timer = 0;
} else if (!this.hasWinner && this.game.ticks() % 10 == 0) {
@@ -127,7 +127,7 @@ export class OptionsMenu extends LitElement implements Layer {
@contextmenu=${(e) => e.preventDefault()}
>
${button({
diff --git a/src/client/graphics/layers/PlayerPanel.ts b/src/client/graphics/layers/PlayerPanel.ts
index bfd0c4c19..fba06c7a1 100644
--- a/src/client/graphics/layers/PlayerPanel.ts
+++ b/src/client/graphics/layers/PlayerPanel.ts
@@ -4,7 +4,12 @@ import { EventBus } from "../../../core/EventBus";
import { GameView, PlayerView } from "../../../core/game/GameView";
import { Layer } from "./Layer";
import { MouseUpEvent } from "../../InputHandler";
-import { AllPlayers, Player, PlayerActions } from "../../../core/game/Game";
+import {
+ AllPlayers,
+ Player,
+ PlayerActions,
+ UnitType,
+} from "../../../core/game/Game";
import { TileRef } from "../../../core/game/GameMap";
import { renderNumber, renderTroops } from "../../Utils";
import targetIcon from "../../../../resources/images/TargetIconWhite.svg";
@@ -18,6 +23,7 @@ import {
SendDonateIntentEvent,
SendEmojiIntentEvent,
SendTargetPlayerIntentEvent,
+ SendEmbargoIntentEvent,
} from "../../Transport";
import { EmojiTable } from "./EmojiTable";
@@ -76,6 +82,26 @@ export class PlayerPanel extends LitElement implements Layer {
this.hide();
}
+ private handleEmbargoClick(
+ e: Event,
+ myPlayer: PlayerView,
+ other: PlayerView,
+ ) {
+ e.stopPropagation();
+ this.eventBus.emit(new SendEmbargoIntentEvent(myPlayer, other, "start"));
+ this.hide();
+ }
+
+ private handleStopEmbargoClick(
+ e: Event,
+ myPlayer: PlayerView,
+ other: PlayerView,
+ ) {
+ e.stopPropagation();
+ this.eventBus.emit(new SendEmbargoIntentEvent(myPlayer, other, "stop"));
+ this.hide();
+ }
+
private handleEmojiClick(e: Event, myPlayer: PlayerView, other: PlayerView) {
e.stopPropagation();
this.emojiTable.showTable((emoji: string) => {
@@ -107,6 +133,24 @@ export class PlayerPanel extends LitElement implements Layer {
this.requestUpdate();
}
+ getTotalNukesSent(): number {
+ const stats = this.actions.interaction?.stats;
+ if (!stats) {
+ return 0;
+ }
+ let sum = 0;
+ const nukes = stats.sentNukes[this.g.myPlayer().id()];
+ if (!nukes) {
+ return 0;
+ }
+ for (const nukeType in nukes) {
+ if (nukeType != UnitType.MIRVWarhead) {
+ sum += nukes[nukeType];
+ }
+ }
+ return sum;
+ }
+
render() {
if (!this.isVisible) {
return html``;
@@ -131,6 +175,7 @@ export class PlayerPanel extends LitElement implements Layer {
: this.actions.interaction?.canSendEmoji;
const canBreakAlliance = this.actions.interaction?.canBreakAlliance;
const canTarget = this.actions.interaction?.canTarget;
+ const canEmbargo = this.actions.interaction?.canEmbargo;
return html`
diff --git a/src/client/graphics/layers/TopBar.ts b/src/client/graphics/layers/TopBar.ts
index fdf8f32b5..682dc4934 100644
--- a/src/client/graphics/layers/TopBar.ts
+++ b/src/client/graphics/layers/TopBar.ts
@@ -8,6 +8,9 @@ import { renderNumber, renderTroops } from "../../Utils";
export class TopBar extends LitElement implements Layer {
public game: GameView;
private isVisible = false;
+ private _population = 0;
+ private _lastPopulationIncreaseRate = 0;
+ private _popRateIsIncreasing = false;
createRenderRoot() {
return this;
@@ -19,6 +22,15 @@ export class TopBar extends LitElement implements Layer {
}
tick() {
+ if (this.game?.myPlayer() !== null) {
+ const popIncreaseRate =
+ this.game.myPlayer().population() - this._population;
+ if (this.game.ticks() % 5 == 0) {
+ this._popRateIsIncreasing =
+ popIncreaseRate >= this._lastPopulationIncreaseRate;
+ this._lastPopulationIncreaseRate = popIncreaseRate;
+ }
+ }
this.requestUpdate();
}
@@ -38,7 +50,7 @@ export class TopBar extends LitElement implements Layer {
return html`
${renderTroops(myPlayer.population())} /
${renderTroops(maxPop)}
- (+${renderTroops(popRate)})
+ (+${renderTroops(popRate)})
-
${this._title}
-
${this.won ? this.supportHTML() : this.adsHTML()}
+
+
${this._title || ""}
+ ${this.won ? this.supportHTML() : this.adsHTML()}