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
111
112
|
import { render } from './render.js';
const X_START = '0.7t+sin(t)-6';
const Y_START = '0.7t-6';
const PATH_LEN = 20;
const PATH_STEP = PATH_LEN/1000;
const SLOW_RAMP = 1;
const FAST_RAMP = 0.01;
export function setupLevel(resources, home) {
const level = {
resources: resources.map(position => ({ position, collected: false })),
home,
running: false,
completed: false,
index: 0,
equations: {
x: math.compile(X_START),
y: math.compile(Y_START),
},
};
level.path = computePath(level.equations);
return level;
}
export function setupLevelUi(level, root, audio) {
const ui = { audio };
ui.rocketThrustAudio = document.getElementById('sfx-rocket-thrust');
ui.rocketThrustSource = audio.createMediaElementSource(ui.rocketThrustAudio);
ui.rocketGain = audio.createGain();
ui.rocketGain.gain.value = 0;
ui.rocketThrustSource.connect(ui.rocketGain).connect(audio.destination);
ui.launchButton = document.createElement('input');
ui.launchButton.type = 'button';
ui.launchButton.value = 'Launch';
ui.launchButton.onclick = () => {
level.running = false;
ui.rocketThrustAudio.play();
ui.rocketGain.gain.setTargetAtTime(1, audio.currentTime, FAST_RAMP);
setTimeout(() => { level.running=true; stepLevel(ui, level, 0); }, 20);
}
ui.xeq = document.createElement('input');
ui.xeq.value = X_START;
ui.xeq.onchange = (e) => {
ui.rocketGain.gain.setTargetAtTime(0, audio.currentTime, FAST_RAMP);
setTimeout(() => ui.rocketThrustAudio.pause(), 50);
level.equations.x = math.compile(e.target.value);
level.running = false;
level.path = computePath(equations);
render(ui.ctx, level, 0);
};
ui.xeq.onkeydown = ui.xeq.onchange;
ui.xeq.onkeyup = ui.xeq.onchange;
ui.yeq = document.createElement('input');
ui.yeq.value = Y_START;
ui.yeq.onchange = (e) => {
ui.rocketGain.gain.setTargetAtTime(0, audio.currentTime, FAST_RAMP);
setTimeout(() => ui.rocketThrustAudio.pause(), 50);
level.equations.y = math.compile(e.target.value);
level.running = false;
level.path = computePath(equations);
render(ui.ctx, level, 0);
};
ui.yeq.onkeydown = ui.yeq.onchange;
ui.yeq.onkeyup = ui.yeq.onchange;
root.appendChild(ui.xeq);
root.appendChild(ui.yeq);
root.appendChild(ui.launchButton);
ui.canvas = document.createElement('canvas');
ui.canvas.width = 600;
ui.canvas.height = 600;
root.appendChild(ui.canvas);
ui.ctx = ui.canvas.getContext('2d');
ui.ctx.translate(ui.canvas.width/2, ui.canvas.height/2)
ui.ctx.scale(ui.canvas.width/20, -ui.canvas.height/20);
render(ui.ctx, level, 0);
return ui;
}
function stepLevel(ui, level, index) {
render(ui.ctx, level, index);
if (level.running && index < level.path.length-1) {
setTimeout(() => stepLevel(ui, level, index+1), 1);
} else {
level.running = false;
ui.rocketGain.gain.setTargetAtTime(0, ui.audio.currentTime, FAST_RAMP);
setTimeout(() => ui.rocketThrustAudio.pause(), 50);
render(ui.ctx, level, 0);
}
}
function computePath(equations, start, end, step) {
const ts = [...Array(Math.floor((PATH_LEN)/PATH_STEP)).keys()].map(k => PATH_STEP * k);
return ts.map(t => [ equations.x.eval({t}), equations.y.eval({t}) ]);
}
|