create topbar for mobile

This commit is contained in:
Evan
2025-01-30 09:45:24 -08:00
parent 0d75765b5c
commit 5a1295a0da
4 changed files with 246 additions and 137 deletions
+177 -135
View File
@@ -22,170 +22,212 @@ import { GameView } from "../../core/game/GameView";
import { WinModal } from "./layers/WinModal";
import { SpawnTimer } from "./layers/SpawnTimer";
import { OptionsMenu } from "./layers/OptionsMenu";
import { TopBar } from "./layers/TopBar";
export function createRenderer(
canvas: HTMLCanvasElement,
game: GameView,
eventBus: EventBus,
clientID: ClientID
): GameRenderer {
const transformHandler = new TransformHandler(game, eventBus, canvas);
export function createRenderer(canvas: HTMLCanvasElement, game: GameView, eventBus: EventBus, clientID: ClientID): GameRenderer {
const transformHandler = new TransformHandler(game, eventBus, canvas)
const uiState = { attackRatio: 20 };
const uiState = { attackRatio: 20 }
// TODO maybe append this to dcoument instead of querying for them?
const emojiTable = document.querySelector("emoji-table") as EmojiTable;
if (!emojiTable || !(emojiTable instanceof EmojiTable)) {
consolex.error("EmojiTable element not found in the DOM");
}
const buildMenu = document.querySelector("build-menu") as BuildMenu;
if (!buildMenu || !(buildMenu instanceof BuildMenu)) {
consolex.error("BuildMenu element not found in the DOM");
}
buildMenu.game = game;
buildMenu.eventBus = eventBus;
// TODO maybe append this to dcoument instead of querying for them?
const emojiTable = document.querySelector('emoji-table') as EmojiTable;
if (!emojiTable || !(emojiTable instanceof EmojiTable)) {
consolex.error('EmojiTable element not found in the DOM');
}
const buildMenu = document.querySelector('build-menu') as BuildMenu;
if (!buildMenu || !(buildMenu instanceof BuildMenu)) {
consolex.error('BuildMenu element not found in the DOM')
}
buildMenu.game = game
buildMenu.eventBus = eventBus
const leaderboard = document.querySelector("leader-board") as Leaderboard;
if (!emojiTable || !(leaderboard instanceof Leaderboard)) {
consolex.error("EmojiTable element not found in the DOM");
}
leaderboard.clientID = clientID;
leaderboard.eventBus = eventBus;
leaderboard.game = game;
const leaderboard = document.querySelector('leader-board') as Leaderboard;
if (!emojiTable || !(leaderboard instanceof Leaderboard)) {
consolex.error('EmojiTable element not found in the DOM');
}
leaderboard.clientID = clientID
leaderboard.eventBus = eventBus
leaderboard.game = game
const controlPanel = document.querySelector("control-panel") as ControlPanel;
if (!(controlPanel instanceof ControlPanel)) {
consolex.error("ControlPanel element not found in the DOM");
}
controlPanel.clientID = clientID;
controlPanel.eventBus = eventBus;
controlPanel.uiState = uiState;
controlPanel.game = game;
const eventsDisplay = document.querySelector(
"events-display"
) as EventsDisplay;
if (!(eventsDisplay instanceof EventsDisplay)) {
consolex.error("events display not found");
}
eventsDisplay.eventBus = eventBus;
eventsDisplay.game = game;
eventsDisplay.clientID = clientID;
const controlPanel = document.querySelector('control-panel') as ControlPanel;
if (!(controlPanel instanceof ControlPanel)) {
consolex.error('ControlPanel element not found in the DOM');
}
controlPanel.clientID = clientID
controlPanel.eventBus = eventBus
controlPanel.uiState = uiState
controlPanel.game = game
const playerInfo = document.querySelector(
"player-info-overlay"
) as PlayerInfoOverlay;
if (!(playerInfo instanceof PlayerInfoOverlay)) {
consolex.error("player info overlay not found");
}
playerInfo.eventBus = eventBus;
playerInfo.clientID = clientID;
playerInfo.transform = transformHandler;
playerInfo.game = game;
const eventsDisplay = document.querySelector('events-display') as EventsDisplay;
if (!(eventsDisplay instanceof EventsDisplay)) {
consolex.error('events display not found')
}
eventsDisplay.eventBus = eventBus
eventsDisplay.game = game
eventsDisplay.clientID = clientID
const winModel = document.querySelector("win-modal") as WinModal;
if (!(winModel instanceof WinModal)) {
console.error("win modal not found");
}
winModel.eventBus = eventBus;
winModel.game = game;
const playerInfo = document.querySelector('player-info-overlay') as PlayerInfoOverlay
if (!(playerInfo instanceof PlayerInfoOverlay)) {
consolex.error('player info overlay not found')
}
playerInfo.eventBus = eventBus
playerInfo.clientID = clientID
playerInfo.transform = transformHandler
playerInfo.game = game
const optionsMenu = document.querySelector("options-menu") as OptionsMenu;
if (!(optionsMenu instanceof OptionsMenu)) {
console.error("options menu not found");
}
optionsMenu.eventBus = eventBus;
optionsMenu.game = game;
const topBar = document.querySelector("top-bar") as TopBar;
if (!(topBar instanceof TopBar)) {
console.error("top bar not found");
}
topBar.game = game;
const winModel = document.querySelector('win-modal') as WinModal
if (!(winModel instanceof WinModal)) {
console.error('win modal not found')
}
winModel.eventBus = eventBus
winModel.game = game
const layers: Layer[] = [
new TerrainLayer(game),
new TerritoryLayer(game, eventBus),
new StructureLayer(game, eventBus),
new UnitLayer(game, eventBus, clientID),
new NameLayer(game, game.config().theme(), transformHandler, clientID),
eventsDisplay,
new RadialMenu(
eventBus,
game,
transformHandler,
clientID,
emojiTable as EmojiTable,
buildMenu,
uiState
),
new SpawnTimer(game, transformHandler),
leaderboard,
controlPanel,
playerInfo,
winModel,
optionsMenu,
topBar,
];
const optionsMenu = document.querySelector('options-menu') as OptionsMenu
if (!(optionsMenu instanceof OptionsMenu)) {
console.log('options menu not found')
}
optionsMenu.eventBus = eventBus
optionsMenu.game = game
const layers: Layer[] = [
new TerrainLayer(game),
new TerritoryLayer(game, eventBus),
new StructureLayer(game, eventBus),
new UnitLayer(game, eventBus, clientID),
new NameLayer(game, game.config().theme(), transformHandler, clientID),
eventsDisplay,
new RadialMenu(eventBus, game, transformHandler, clientID, emojiTable as EmojiTable, buildMenu, uiState),
new SpawnTimer(game, transformHandler),
leaderboard,
controlPanel,
playerInfo,
winModel,
optionsMenu
]
return new GameRenderer(game, eventBus, canvas, transformHandler, uiState, layers)
return new GameRenderer(
game,
eventBus,
canvas,
transformHandler,
uiState,
layers
);
}
export class GameRenderer {
private context: CanvasRenderingContext2D;
private context: CanvasRenderingContext2D
constructor(
private game: GameView,
private eventBus: EventBus,
private canvas: HTMLCanvasElement,
public transformHandler: TransformHandler,
public uiState: UIState,
private layers: Layer[]
) {
this.context = canvas.getContext("2d");
}
constructor(private game: GameView, private eventBus: EventBus, private canvas: HTMLCanvasElement, public transformHandler: TransformHandler, public uiState: UIState, private layers: Layer[]) {
this.context = canvas.getContext("2d")
}
initialize() {
this.eventBus.on(RedrawGraphicsEvent, (e) => {
this.layers.forEach((l) => {
if (l.redraw) {
l.redraw();
}
});
});
initialize() {
this.eventBus.on(RedrawGraphicsEvent, (e) => {
this.layers.forEach(l => {
if (l.redraw) {
l.redraw()
}
})
})
this.layers.forEach((l) => l.init());
this.layers.forEach(l => l.init())
document.body.appendChild(this.canvas);
window.addEventListener("resize", () => this.resizeCanvas());
this.resizeCanvas();
document.body.appendChild(this.canvas);
window.addEventListener('resize', () => this.resizeCanvas());
this.resizeCanvas();
this.transformHandler = new TransformHandler(
this.game,
this.eventBus,
this.canvas
);
this.transformHandler = new TransformHandler(this.game, this.eventBus, this.canvas)
requestAnimationFrame(() => this.renderGame());
}
requestAnimationFrame(() => this.renderGame());
}
resizeCanvas() {
this.canvas.width = window.innerWidth;
this.canvas.height = window.innerHeight;
//this.redraw()
}
resizeCanvas() {
this.canvas.width = window.innerWidth;
this.canvas.height = window.innerHeight;
//this.redraw()
}
renderGame() {
const start = performance.now();
// Set background
this.context.fillStyle = this.game
.config()
.theme()
.backgroundColor()
.toHex();
this.context.fillRect(0, 0, this.canvas.width, this.canvas.height);
renderGame() {
const start = performance.now()
// Set background
this.context.fillStyle = this.game.config().theme().backgroundColor().toHex();
this.context.fillRect(0, 0, this.canvas.width, this.canvas.height);
// Save the current context state
this.context.save();
// Save the current context state
this.context.save();
this.transformHandler.handleTransform(this.context);
this.transformHandler.handleTransform(this.context)
this.layers.forEach((l) => {
if (l.shouldTransform?.()) {
l.renderLayer?.(this.context);
}
});
this.layers.forEach(l => {
if (l.shouldTransform?.()) {
l.renderLayer?.(this.context)
}
})
this.context.restore();
this.context.restore()
this.layers.forEach((l) => {
if (!l.shouldTransform?.()) {
l.renderLayer?.(this.context);
}
});
this.layers.forEach(l => {
if (!l.shouldTransform?.()) {
l.renderLayer?.(this.context)
}
})
requestAnimationFrame(() => this.renderGame());
requestAnimationFrame(() => this.renderGame());
const duration = performance.now() - start;
if (duration > 50) {
console.warn(
`tick ${this.game.ticks()} took ${duration}ms to render frame`
);
}
}
const duration = performance.now() - start
if (duration > 50) {
console.warn(`tick ${this.game.ticks()} took ${duration}ms to render frame`)
}
}
tick() {
this.layers.forEach((l) => l.tick?.());
}
tick() {
this.layers.forEach(l => l.tick?.())
}
resize(width: number, height: number): void {
this.canvas.width = Math.ceil(width / window.devicePixelRatio);
this.canvas.height = Math.ceil(height / window.devicePixelRatio);
}
}
resize(width: number, height: number): void {
this.canvas.width = Math.ceil(width / window.devicePixelRatio);
this.canvas.height = Math.ceil(height / window.devicePixelRatio);
}
}
+1 -1
View File
@@ -114,7 +114,7 @@ export class ControlPanel extends LitElement implements Layer {
return html`
<div
class="${this._isVisible
? "w-full lg:w-72 bg-gray-800/70 p-2 pr-3 lg:p-4 shadow-lg rounded-lg backdrop-blur"
? "w-full text-sm lg:text-m lg:w-72 bg-gray-800/70 p-2 pr-3 lg:p-4 shadow-lg rounded-lg backdrop-blur"
: "hidden"}"
>
<div class="hidden lg:block bg-black/30 text-white mb-4 p-2 rounded">
+66
View File
@@ -0,0 +1,66 @@
import { LitElement, html } from "lit";
import { customElement, property, state } from "lit/decorators.js";
import { GameView } from "../../../core/game/GameView";
import { Layer } from "./Layer";
import { renderNumber, renderTroops } from "../../Utils";
@customElement("top-bar")
export class TopBar extends LitElement implements Layer {
public game: GameView;
private isVisible = false;
createRenderRoot() {
return this;
}
init() {
this.isVisible = true;
this.requestUpdate();
}
tick() {
this.requestUpdate();
}
render() {
if (!this.isVisible) {
return html``;
}
const myPlayer = this.game?.myPlayer();
if (!myPlayer?.isAlive() || this.game?.inSpawnPhase()) {
return html``;
}
console.log("rendering top bar");
const popRate = this.game.config().populationIncreaseRate(myPlayer) * 10;
const maxPop = this.game.config().maxPopulation(myPlayer);
const goldPerSecond = this.game.config().goldAdditionRate(myPlayer) * 10;
return html`
<div
class="fixed top-0 z-50 bg-black/90 text-white text-sm p-1 rounded grid grid-cols-1 sm:grid-cols-2 w-2/3 sm:w-2/3 md:w-1/2 lg:hidden"
>
<!-- Pop section (takes 2 columns on desktop) -->
<div
class="sm:col-span-1 flex items-center space-x-1 overflow-x-auto whitespace-nowrap"
>
<span class="font-bold shrink-0">Pop:</span>
<span
>${renderTroops(myPlayer.population())} /
${renderTroops(maxPop)}</span
>
<span>(+${renderTroops(popRate)})</span>
</div>
<!-- Gold section (takes 1 column on desktop) -->
<div
class="flex items-center space-x-2 overflow-x-auto whitespace-nowrap"
>
<span class="font-bold shrink-0">Gold:</span>
<span
>${renderNumber(myPlayer.gold())}
(+${renderNumber(goldPerSecond)})</span
>
</div>
</div>
`;
}
}
+2 -1
View File
@@ -179,12 +179,13 @@
<options-menu></options-menu>
<player-info-overlay></player-info-overlay>
<win-modal></win-modal>
<top-bar></top-bar>
<div class="fixed bottom-0 flex w-full flex-col-reverse sm:flex-row z-50">
<div class="w-full sm:w-1/3">
<control-panel></control-panel>
</div>
<div class="w-full sm:w-2/3 sm:fixed sm:right-0 sm:bottom-0 sm:flex sm:justify-end">
<div class="w-full sm:w-2/3 sm:fixed sm:right-0 sm:bottom-0 sm:flex justify-end">
<events-display></events-displayflex >
</div>
</div>