summaryrefslogtreecommitdiffstats
path: root/site/game.mjs
diff options
context:
space:
mode:
Diffstat (limited to 'site/game.mjs')
-rw-r--r--site/game.mjs143
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);
+ }
+}