diff options
| author | brian cully <bjc@spork.org> | 2025-12-24 11:55:01 -0500 |
|---|---|---|
| committer | brian cully <bjc@spork.org> | 2025-12-24 11:55:01 -0500 |
| commit | 826e3d959d2339af285cc3417315efe16a8ff07d (patch) | |
| tree | 5409c0d823b81ae0a4349090d9380615cbded830 /site/game.mjs | |
| parent | fdd715f4e610ae68dcb5310c37cebfbf383b1a4c (diff) | |
| download | automathon-826e3d959d2339af285cc3417315efe16a8ff07d.tar.gz automathon-826e3d959d2339af285cc3417315efe16a8ff07d.zip | |
part 2: move core logic to game module, which owns robos
Diffstat (limited to 'site/game.mjs')
| -rw-r--r-- | site/game.mjs | 143 |
1 files changed, 143 insertions, 0 deletions
diff --git a/site/game.mjs b/site/game.mjs new file mode 100644 index 0000000..0b1de90 --- /dev/null +++ b/site/game.mjs @@ -0,0 +1,143 @@ +import Arena from './arena.mjs'; +import Inspector from './inspector.mjs'; + +const ARENA_SELECTOR = 'x-arena'; +const TICK_BUTTON_SELECTOR = '#tick'; +const TICK_RATE_SELECTOR = '#tick-rate-select'; +const RUN_BUTTON_SELECTOR = '#run'; + +export default class extends HTMLElement { + static name = 'x-game'; + static register() { + console.debug('registering custom element', this.name, this); + self.customElements.define(this.name, this); + } + + #running = false; + #robos = []; + #inspectors = []; + #arena; + #tickButton; + #runButton; + #tickRate; + + constructor() { + super(); + } + + connectedCallback() { + console.debug('connectedCallback()', this); + this.#arena = this.querySelector(ARENA_SELECTOR); + this.#tickButton = this.querySelector(TICK_BUTTON_SELECTOR); + this.#runButton = this.querySelector(RUN_BUTTON_SELECTOR); + this.#tickRate = this.querySelector(TICK_RATE_SELECTOR); + this.#inspectors = this.querySelectorAll(Inspector.name); + + this.#tickButton.onclick = this.#tickHandler.bind(this); + this.#runButton.onclick = this.#blinkenHandler.bind(this); + this.#tickRate.onchange = this.#tickRateHandler.bind(this); + + this.#tickRate.onchange({ target: this.#tickRate }); + this.addRobo(); + } + + addRobo() { + const { x, y } = this.#arena.randStart(); + const robo = { + worker: new Worker('robo.mjs', { type: 'module' }), + lastTick: undefined, + heading: 0, + speed: 0, + speedx: 0, + speedy: 0, + x, + y, + } + robo.worker.onmessage = msg => this.#messageHandler(i, msg); + robo.worker.onerror = msg => this.#errorHander(i, msg); + this.#robos.push(robo); + const i = this.#robos.length - 1; + this.#inspectors[i].addEventListener(Inspector.compileRequest, e => { + console.debug('compiling for worker', i, e.detail.text, this.#robos[i].worker); + this.#robos[i].worker.postMessage({ kind: 'compile', text: e.detail.text }); + }); + } + + #messageHandler(i, e) { + const { kind, res, trans } = e.data; + switch (kind) { + case 'compile': + if (res) { + this.#arena.render(this.#robos); + this.#inspectors[i].render(trans, true); + } + break; + case 'tick': + const robo = this.#robos[i]; + robo.lastTick = document.timeline.currentTime; + + [robo.x, robo.y] = this.#arena.clamp(Arena.radius, robo.x + robo.speedx, robo.y + robo.speedy); + this.#arena.render(this.#robos, robo.lastTick); + + robo.heading = trans.vars.heading; + robo.speed = trans.vars.speed; + [robo.speedx, robo.speedy] = [ + Math.cos(2 * Math.PI * robo.heading / 360), + Math.sin(2 * Math.PI * robo.heading / 360) + ].map(x => robo.speed * x); + + this.#inspectors[i].render(trans); + break; + default: + console.error('invalid message from robo worker', e); + } + } + + #errorHander(i, e) { + console.error('error in roboWorker', this, i, e); + } + + #tickHandler(e) { + console.debug('tick clicked', this, e); + this.#robos.forEach(robo => robo.worker.postMessage({ kind: 'tick' })); + } + + #blinkenHandler(e) { + console.debug('blinken clicked', this, e); + + this.#running = !this.#running; + if (this.#running) { + e.currentTarget.classList.remove('halten'); + e.currentTarget.classList.add('blinken'); + e.currentTarget.querySelector('title').textContent = 'halten'; + this.#timeoutHandler(); + this.#renderFrame(document.timeline.currentTime); + } else { + e.currentTarget.classList.remove('blinken'); + e.currentTarget.classList.add('halten'); + e.currentTarget.querySelector('title').textContent = 'blinken'; + this.#arena.invalidateFPS(); + } + } + + #tickRateHandler(e) { + console.debug('tick rate changed', this, e, Number(e.target.value)); + this.#arena.tickMS = 1_000 / e.target.value; + } + + #timeoutHandler() { + if (!this.#running) { + return; + } + self.setTimeout(this.#timeoutHandler.bind(this), this.#arena.tickMS); + this.#robos.forEach(robo => robo.worker.postMessage({ kind: 'tick' })); + } + + #renderFrame(t) { + if (!this.#running) { + return; + } + self.requestAnimationFrame(this.#renderFrame.bind(this)); + this.#arena.render(this.#robos, t); + } +} |
