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.resourceAudio = document.getElementById('sfx-resource'); ui.resourceSource = audio.createMediaElementSource(ui.resourceAudio); ui.resourceSource.connect(audio.destination); ui.doneAudio = document.getElementById('sfx-done'); ui.doneSource = audio.createMediaElementSource(ui.doneAudio); ui.doneSource.connect(audio.destination); ui.wrongAudio = document.getElementById('sfx-wrong'); ui.wrongSource = audio.createMediaElementSource(ui.wrongAudio); ui.wrongSource.connect(audio.destination); ui.launchButton = document.createElement('input'); ui.launchButton.type = 'button'; ui.launchButton.value = 'Launch'; ui.launchButton.onclick = () => { ui.message.innerText = ''; level.running = false; setTimeout(() => { level.running=true; stepLevel(ui, level, 0); }, 20); } ui.xeq = document.createElement('input'); ui.xeq.value = X_START; ui.xeq.onchange = (e) => { try { level.equations.x = math.compile(e.target.value); } catch(err) { } level.running = false; level.path = computePath(level.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) => { try { level.equations.y = math.compile(e.target.value); } catch(err) { } level.running = false; level.path = computePath(level.equations); render(ui.ctx, level, 0); }; ui.yeq.onkeydown = ui.yeq.onchange; ui.yeq.onkeyup = ui.yeq.onchange; ui.message = document.createElement('div'); ui.id = "level-message"; root.appendChild(ui.launchButton); root.appendChild(ui.xeq); root.appendChild(ui.yeq); root.appendChild(ui.message); 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); const pos = level.path[index]; const distances = level.resources .map((x, i) => [x, i]) .filter(([x, _]) => x.collected === false) .map(([x, i]) => [ distance(pos, x.position), i ]) .forEach(([d, i]) => { if (d < 0.4) { ui.resourceAudio.load(); ui.resourceAudio.play(); level.resources[i].collected = true; } }); if (distance(pos, level.home) < 0.4) { level.running = false; finishLevel(ui, level); render(ui.ctx, level, 0); } else if (level.running && index < level.path.length-1) { setTimeout(() => stepLevel(ui, level, index+1), 1); } else { level.running = false; render(ui.ctx, level, 0); } } function computePath(equations, start, end, step) { try { 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}) ]); } catch (err) { return [ [0, 0] ]; } } function distance(p1, p2) { const [x1, y1] = p1; const [x2, y2] = p2; return Math.sqrt((x2-x1)**2 + (y2-y1)**2); } function finishLevel(ui, level) { const uncollected = level.resources.reduce((acc, { collected }) => acc + (collected ? 0 : 1), 0); if (uncollected > 0) { ui.wrongAudio.load(); ui.wrongAudio.play(); ui.message.innerText = 'resources remaining!'; } else { ui.doneAudio.load(); ui.doneAudio.play(); ui.message.innerText = 'mission complete!'; level.completed = true; } level.resources.forEach(x => x.collected = false); }