separate optionsmenu from playerinfo menu

This commit is contained in:
Evan
2025-01-24 15:52:42 -08:00
parent 047d7ef147
commit 9b2a84cb74
5 changed files with 151 additions and 42 deletions
+16 -7
View File
@@ -21,6 +21,7 @@ import { RefreshGraphicsEvent as RedrawGraphicsEvent } from "../InputHandler";
import { GameView } from "../../core/game/GameView";
import { WinModal } from "./layers/WinModal";
import { SpawnTimer } from "./layers/SpawnTimer";
import { OptionsMenu } from "./layers/OptionsMenu";
export function createRenderer(canvas: HTMLCanvasElement, game: GameView, eventBus: EventBus, clientID: ClientID): GameRenderer {
@@ -77,11 +78,18 @@ export function createRenderer(canvas: HTMLCanvasElement, game: GameView, eventB
const winModel = document.querySelector('win-modal') as WinModal
if (!(playerInfo instanceof WinModal)) {
if (!(winModel instanceof WinModal)) {
console.error('win modal not found')
}
winModel.game = game
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),
@@ -95,7 +103,8 @@ export function createRenderer(canvas: HTMLCanvasElement, game: GameView, eventB
leaderboard,
controlPanel,
playerInfo,
winModel
winModel,
optionsMenu
]
return new GameRenderer(game, eventBus, canvas, transformHandler, uiState, layers)
@@ -148,16 +157,16 @@ export class GameRenderer {
this.transformHandler.handleTransform(this.context)
this.layers.forEach(l => {
if (l.shouldTransform()) {
l.renderLayer(this.context)
if (l.shouldTransform?.()) {
l.renderLayer?.(this.context)
}
})
this.context.restore()
this.layers.forEach(l => {
if (!l.shouldTransform()) {
l.renderLayer(this.context)
if (!l.shouldTransform?.()) {
l.renderLayer?.(this.context)
}
})
@@ -170,7 +179,7 @@ export class GameRenderer {
}
tick() {
this.layers.forEach(l => l.tick())
this.layers.forEach(l => l.tick?.())
}
resize(width: number, height: number): void {
+4 -4
View File
@@ -1,9 +1,9 @@
import { Game } from "../../../core/game/Game"
export interface Layer {
init()
tick()
renderLayer(context: CanvasRenderingContext2D)
shouldTransform(): boolean
init?()
tick?()
renderLayer?(context: CanvasRenderingContext2D)
shouldTransform?(): boolean
redraw?(): void
}
+119
View File
@@ -0,0 +1,119 @@
import { LitElement, html, css } from 'lit';
import { customElement, property, state } from 'lit/decorators.js';
import { EventBus } from '../../../core/EventBus';
import { PauseGameEvent } from '../../Transport';
import { GameType } from '../../../core/game/Game';
import { GameView } from '../../../core/game/GameView';
import { Layer } from './Layer';
@customElement('options-menu')
export class OptionsMenu extends LitElement implements Layer {
public game: GameView;
public eventBus: EventBus;
@state()
private showPauseButton: boolean = true;
@state()
private isPaused: boolean = false;
private isVisible = false
private onExitButtonClick() {
window.location.reload();
}
private onPauseButtonClick() {
this.isPaused = !this.isPaused;
this.eventBus.emit(new PauseGameEvent(this.isPaused));
}
init() {
console.log('init called from OptionsMenu')
this.showPauseButton = this.game.config().gameConfig().gameType == GameType.Singleplayer;
this.isVisible = true
this.requestUpdate()
}
tick() {
this.isVisible = true
this.requestUpdate()
}
render() {
if (!this.isVisible) {
return html``
}
return html`
<div class="controls">
<button
class="control-button pause-button ${!this.showPauseButton ? 'hidden' : ''}"
@click=${this.onPauseButtonClick}
aria-label="${this.isPaused ? 'Resume game' : 'Pause game'}"
>
${this.isPaused ? '▶' : '⏸'}
</button>
<button
class="control-button exit-button"
@click=${this.onExitButtonClick}
aria-label="Exit game"
>×</button>
</div>
`;
}
static styles = css`
:host {
position: fixed; /* Make sure it's fixed positioning */
top: 20px; /* Position it where you want */
right: 10px;
z-index: 1000; /* Make sure it's higher than canvas */
pointer-events: auto; /* Ensure it can receive clicks */
}
.controls {
display: flex;
gap: 8px;
}
.control-button {
background: rgba(30, 30, 30, 0.7);
border: none;
color: white;
font-size: 24px;
cursor: pointer;
padding: 4px 8px;
border-radius: 4px;
opacity: 0.7;
transition: opacity 0.2s, background-color 0.2s;
backdrop-filter: blur(5px);
}
.control-button:hover {
opacity: 1;
background: rgba(40, 40, 40, 0.8);
}
.pause-button {
font-size: 20px;
padding: 4px 10px;
}
.hidden {
opacity: 0;
visibility: hidden;
pointer-events: none;
}
@media (max-width: 768px) {
.control-button {
font-size: 16px;
padding: 3px 6px;
}
.pause-button {
font-size: 14px;
padding: 3px 8px;
}
}
`;
}
+11 -30
View File
@@ -8,7 +8,6 @@ import { TransformHandler } from '../TransformHandler';
import { MouseMoveEvent } from '../../InputHandler';
import { GameView, PlayerView, UnitView } from '../../../core/game/GameView';
import { TileRef } from '../../../core/game/GameMap';
import { PauseGameEvent } from '../../Transport';
import { renderNumber, renderTroops } from '../../Utils';
function euclideanDistWorld(coord: { x: number, y: number }, tileRef: TileRef, game: GameView): number {
@@ -50,15 +49,9 @@ export class PlayerInfoOverlay extends LitElement implements Layer {
@state()
private unit: UnitView | null = null;
@state()
private showPauseButton: boolean = true;
@state()
private _isInfoVisible: boolean = false;
@state()
private _isPaused: boolean = false;
private _isActive = false;
private lastMouseUpdate = 0
@@ -66,7 +59,6 @@ export class PlayerInfoOverlay extends LitElement implements Layer {
init() {
this.eventBus.on(MouseMoveEvent, (e: MouseMoveEvent) => this.onMouseEvent(e));
this._isActive = true;
this.showPauseButton = this.game.config().gameConfig().gameType == GameType.Singleplayer;
}
private onMouseEvent(event: MouseMoveEvent) {
@@ -109,15 +101,6 @@ export class PlayerInfoOverlay extends LitElement implements Layer {
}
}
private onExitButtonClick() {
window.location.reload();
}
private onPauseButtonClick() {
this._isPaused = !this._isPaused;
this.eventBus.emit(new PauseGameEvent(this._isPaused));
}
tick() {
this.requestUpdate();
}
@@ -201,19 +184,17 @@ export class PlayerInfoOverlay extends LitElement implements Layer {
return html``;
}
return html`
<div class="container">
<div class="controls">
<button class="control-button pause-button ${!this.showPauseButton ? 'hidden' : ''}" @click=${this.onPauseButtonClick}>
${this._isPaused ? '▶' : '⏸'}
</button>
<button class="control-button exit-button" @click=${this.onExitButtonClick}>×</button>
</div>
<div class="player-info ${!this._isInfoVisible ? 'hidden' : ''}">
${this.player != null ? this.renderPlayerInfo(this.player) : ''}
${this.unit != null ? this.renderUnitInfo(this.unit) : ''}
</div>
<div class="container">
<options-menu
.game=${this.game}
.eventBus=${this.eventBus}
></options-menu>
<div class="player-info ${!this._isInfoVisible ? 'hidden' : ''}">
${this.player != null ? this.renderPlayerInfo(this.player) : ''}
${this.unit != null ? this.renderUnitInfo(this.unit) : ''}
</div>
`;
</div>
`;
}
static styles = css`
@@ -223,7 +204,7 @@ export class PlayerInfoOverlay extends LitElement implements Layer {
.container {
position: fixed;
top: 10px;
top: 70px;
right: 10px;
z-index: 9999;
display: flex;
+1 -1
View File
@@ -125,9 +125,9 @@
<control-panel></control-panel>
<events-display></events-display>
<build-menu></build-menu>
<options-menu></options-menu>
<player-info-overlay></player-info-overlay>
<win-modal></win-modal>
<script>
window.addEventListener('DOMContentLoaded', (event) => {
document.body.style.visibility = 'visible';