summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--site/arena.mjs4
-rw-r--r--site/game.mjs143
-rw-r--r--site/index.html4
-rw-r--r--site/main.css4
-rw-r--r--site/main.mjs103
-rw-r--r--site/robo.mjs1
6 files changed, 152 insertions, 107 deletions
diff --git a/site/arena.mjs b/site/arena.mjs
index c76f7dc..83f0f05 100644
--- a/site/arena.mjs
+++ b/site/arena.mjs
@@ -30,10 +30,10 @@ export default class extends HTMLElement {
return [xx, yy];
}
- randStart(robo) {
+ randStart() {
const [x, y] = [this.#canvas.width, this.#canvas.height].map(len =>
this.constructor.radius + Math.floor(Math.random() * (len - 2*this.constructor.radius)));
- return {...robo, x, y};
+ return {x, y};
}
renderFPS(now) {
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);
+ }
+}
diff --git a/site/index.html b/site/index.html
index ace176c..a935339 100644
--- a/site/index.html
+++ b/site/index.html
@@ -10,7 +10,7 @@
<body>
<h1>automathon</h1>
- <div class='flex-col'>
+ <x-game>
<section>
<h2>arena</h2>
<x-arena>
@@ -97,7 +97,7 @@
</details> <!-- scope -->
</x-inspector>
</section>
- </div>
+ </x-game>
<script src='./main.mjs' type='module'></script>
diff --git a/site/main.css b/site/main.css
index 7e7ed18..4f00319 100644
--- a/site/main.css
+++ b/site/main.css
@@ -5,12 +5,12 @@ html {
box-sizing: inherit;
}
-.flex-col {
+x-game {
display: flex;
flex-direction: row;
}
-.flex-col section {
+x-game section {
flex: 1;
}
diff --git a/site/main.mjs b/site/main.mjs
index e2cdc3e..0d79e7c 100644
--- a/site/main.mjs
+++ b/site/main.mjs
@@ -1,110 +1,11 @@
import Arena from './arena.mjs';
+import Game from './game.mjs';
import Inspector from './inspector.mjs';
-// one per page
-const TICK_BUTTON_SELECTOR = '#tick';
-const TICK_RATE_SELECTOR = '#tick-rate-select';
-const RUN_BUTTON_SELECTOR = '#run';
-
async function loaded() {
Arena.register();
+ Game.register();
Inspector.register();
-
- const robos = [{
- worker: new Worker('robo.mjs', { type: 'module' }),
- lastTick: undefined,
- heading: 0,
- speed: 0,
- speedx: 0,
- speedy: 0,
- }].map(r => document.querySelector(Arena.name).randStart(r));
- robos.forEach((robo, i) => {
- robo.worker.onmessage = e => {
- const { kind, res, trans } = e.data;
- switch (kind) {
- case 'compile':
- if (res) {
- document.querySelector(Arena.name).render(robos);
- document.querySelectorAll(Inspector.name)[i].render(trans, true);
- }
- break;
- case 'tick':
- robo.lastTick = document.timeline.currentTime;
-
- [robo.x, robo.y] = document.querySelector(Arena.name).clamp(Arena.radius, robo.x + robo.speedx, robo.y + robo.speedy);
- document.querySelector(Arena.name).render(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);
-
- document.querySelectorAll(Inspector.name)[i].render(trans);
- break;
- default:
- console.error('invalid message from robo worker', e.data);
- }
- };
- robo.worker.onerror = e => {
- console.error('error in roboWorker', e);
- };
- });
-
- document.querySelectorAll(Inspector.name).forEach((elt, i) => {
- elt.addEventListener(Inspector.compileRequest, e => {
- console.debug('compiling', e.detail.text);
- robos[i].worker.postMessage({ kind: 'compile', text: e.detail.text });
- });
- });
-
- document.querySelector(TICK_BUTTON_SELECTOR).onclick = e => {
- console.debug('tick clicked', e);
- robos.forEach(robo => robo.worker.postMessage({ kind: 'tick' }));
- };
-
- document.querySelector(TICK_RATE_SELECTOR).onchange = e => {
- console.debug('tick rate changed', e, Number(e.target.value));
- document.querySelector(Arena.name).tickMS = 1_000 / e.target.value;
- }
- document.querySelectorAll(TICK_RATE_SELECTOR).forEach(elt => elt.onchange({ target: elt }));
-
- let blinkenRun = false;
- function renderFrame(t) {
- if (!blinkenRun) {
- return;
- }
- self.requestAnimationFrame(renderFrame);
- document.querySelector(Arena.name).render(robos, t);
- }
-
- function timeout() {
- if (!blinkenRun) {
- return
- }
- self.setTimeout(timeout, document.querySelector(Arena.name).tickMS);
-
- robos.forEach(robo => robo.worker.postMessage({ kind: 'tick' }));
- }
-
- document.querySelector(RUN_BUTTON_SELECTOR).onclick = e => {
- console.debug('blinken clicked', e);
-
- blinkenRun = !blinkenRun;
- if (blinkenRun) {
- e.currentTarget.classList.remove('halten');
- e.currentTarget.classList.add('blinken');
- e.currentTarget.querySelector('title').textContent = 'halten';
- setTimeout(timeout);
- renderFrame(document.timeline.currentTime);
- } else {
- e.currentTarget.classList.remove('blinken');
- e.currentTarget.classList.add('halten');
- e.currentTarget.querySelector('title').textContent = 'blinken';
- document.querySelector(Arena.name).invalidateFPS();
- }
- }
}
document.addEventListener('DOMContentLoaded', loaded);
diff --git a/site/robo.mjs b/site/robo.mjs
index d80baad..ba879fa 100644
--- a/site/robo.mjs
+++ b/site/robo.mjs
@@ -18,6 +18,7 @@ function tick() {
}
async function messageHandler(e) {
+ console.debug('worker messageHandler')
const { kind } = e.data;
switch (kind) {
case 'compile':