1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
|
import Arena from './arena.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();
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);
|