diff options
-rw-r--r-- | index.html | 7 | ||||
-rw-r--r-- | level.js | 112 | ||||
-rw-r--r-- | main.js | 187 | ||||
-rw-r--r-- | render.js | 107 | ||||
-rw-r--r-- | sounds/ebi-sea.mp3 | bin | 9303910 -> 0 bytes | |||
-rw-r--r-- | sounds/sfx/129655__perplessio__droplets-underwater.aiff (renamed from sounds/129655__perplessio__droplets-underwater.aiff) | bin | 8929022 -> 8929022 bytes | |||
-rw-r--r-- | sounds/sfx/318688__limitsnap_creations__rocket-thrust-effect.wav (renamed from sounds/318688__limitsnap_creations__rocket-thrust-effect.wav) | bin | 12612146 -> 12612146 bytes | |||
-rw-r--r-- | sounds/sfx/346573__inspectorj__train-door-beep-a.wav | bin | 0 -> 537698 bytes | |||
-rw-r--r-- | sounds/sfx/beep/_readme_and_license.txt | 60 | ||||
-rw-r--r-- | sounds/sfx/beep/alienxxx__noizdumpster-beep-sequences.zip | bin | 0 -> 642536 bytes | |||
-rw-r--r-- | sounds/sfx/beep/sequence_01.wav | bin | 0 -> 40570 bytes | |||
-rw-r--r-- | sounds/sfx/beep/sequence_02.wav | bin | 0 -> 167366 bytes | |||
-rw-r--r-- | sounds/sfx/beep/sequence_03.wav | bin | 0 -> 65982 bytes | |||
-rw-r--r-- | sounds/sfx/beep/sequence_04.wav | bin | 0 -> 48588 bytes | |||
-rw-r--r-- | sounds/sfx/beep/sequence_05.wav | bin | 0 -> 41490 bytes | |||
-rw-r--r-- | sounds/sfx/beep/sequence_06.wav | bin | 0 -> 51308 bytes | |||
-rw-r--r-- | sounds/sfx/beep/sequence_07.wav | bin | 0 -> 67420 bytes | |||
-rw-r--r-- | sounds/sfx/beep/sequence_08.wav | bin | 0 -> 17580 bytes | |||
-rw-r--r-- | sounds/sfx/beep/sequence_09.wav | bin | 0 -> 70884 bytes | |||
-rw-r--r-- | sounds/sfx/beep/sequence_10.wav | bin | 0 -> 67530 bytes | |||
-rw-r--r-- | sounds/sfx/droplet.mp3 (renamed from sounds/droplet.mp3) | bin | 10608 -> 10608 bytes | |||
-rw-r--r-- | sounds/sfx/rocket-thrust.wav (renamed from sounds/rocket-thrust.wav) | bin | 12612146 -> 12612146 bytes | |||
-rw-r--r-- | sounds/sfx/ship_move.mp3 | bin | 0 -> 3556 bytes | |||
-rw-r--r-- | sounds/sfx/ship_move2.mp3 | bin | 0 -> 23961 bytes |
24 files changed, 326 insertions, 147 deletions
@@ -7,6 +7,13 @@ <script src="mathjs/mathjs-expression-parser.js"></script> </head> <body> + <h1 id="status">⚠ WARNING</h1> + <audio id="music-starboard" src="sounds/music/starboard.mp3"></audio> + <audio id="music-minute" src="sounds/music/minute.mp3"></audio> + <audio id="music-cribwhistling" src="sounds/music/cribwhistling.mp3"></audio> + <audio id="music-swish" src="sounds/music/swish.mp3"></audio> + <audio id="music-aeroplane" src="sounds/music/aeroplane.mp3"></audio> + <audio id="sfx-rocket-thrust" src="sounds/sfx/ship_move.mp3" loop="true"></audio> <div id="root"></div> </body> </html> diff --git a/level.js b/level.js new file mode 100644 index 0000000..360b918 --- /dev/null +++ b/level.js @@ -0,0 +1,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}) ]); +} + + @@ -1,153 +1,46 @@ -const X_START = '8*cos(t)'; -const Y_START = '8*sin(t)'; -const equations = { - x: math.compile(X_START), - y: math.compile(Y_START), -} +import { setupLevel, setupLevelUi } from './level.js'; window.onload = () => { - const root = document.getElementById('root'); - - const launchButton = document.createElement('input'); - launchButton.type = 'button'; - launchButton.value = 'Launch'; - const xeq = document.createElement('input'); - xeq.value = X_START; - xeq.onchange = (e) => { - equations.x = math.compile(e.target.value); - running = false; - path = computePath(equations, 0, 100, 0.1); - render(ctx, path, 0); - }; - xeq.onkeydown = xeq.onchange; - xeq.onkeyup = xeq.onchange; - const yeq = document.createElement('input'); - yeq.value = Y_START; - yeq.onchange = (e) => { - equations.y = math.compile(e.target.value); - running = false; - path = computePath(equations, 0, 100, 0.1); - render(ctx, path, 0); - }; - yeq.onkeydown = yeq.onchange; - yeq.onkeyup = yeq.onchange; - - root.appendChild(xeq); - root.appendChild(yeq); - root.appendChild(launchButton); - const canvas = document.createElement('canvas'); - canvas.width = 600; - canvas.height = 600; - root.appendChild(canvas); - - const ctx = canvas.getContext('2d'); - ctx.translate(canvas.width/2, canvas.height/2) - ctx.scale(canvas.width/20, -canvas.height/20); - let path = computePath(equations, 0, 100, 0.1); + const root = document.getElementById('root'); - let running = false; - const step = (index) => { - render(ctx, path, index); - if (running && index < path.length-1) { - setTimeout(() => step(index+1), 10); - } else { - running = false; - render(ctx, path, 0); + const start = document.createElement('input'); + start.type = 'button'; + start.value = 'Start'; + start.onclick = () => { + const audio = new AudioContext(); + root.removeChild(start); + + const rocketThrustAudio = document.getElementById('sfx-rocket-thrust'); + const rocketThrustSource = audio.createMediaElementSource(rocketThrustAudio); + const rocketGain = audio.createGain(); + rocketThrustSource.connect(rocketGain).connect(audio.destination); + + const musicGain = audio.createGain(); + const addMusic = (music, id) => { + const element = document.getElementById(id); + const source = audio.createMediaElementSource(element); + source.connect(musicGain).connect(audio.destination); + return [ ...music, { element, source } ]; } - } - launchButton.onclick = () => { - running = false; - setTimeout(() => { running=true; step(0); }, 20); - } - render(ctx, path, 0); -} - - -function render(ctx, path, index) { - const { width, height } = ctx.canvas; - ctx.fillStyle = 'black'; - ctx.fillRect(-width, -height, 2*width, 2*height); - const [x0, y0] = path[index]; - const [x1, y1] = path[index+1] || path[index]; - const angle = Math.atan2(y1-y0, x1-x0); - drawGrid(ctx); - drawPath(ctx, path); - drawShip(ctx, [x0, y0], angle); -} - - -function drawShip(ctx, pos, angle) { - const transform = ctx.getTransform(); - const { width, height } = ctx.canvas; - const [x, y] = pos; - ctx.translate(x, y); - ctx.rotate(angle); - ctx.fillStyle = 'white'; - const STEP = 120/Math.min(width,height); - ctx.beginPath(); - ctx.moveTo(0, 0); - ctx.lineTo(-STEP, -STEP); - ctx.lineTo(2*STEP, 0); - ctx.lineTo(-STEP, STEP); - ctx.lineTo(0, 0); - ctx.fill(); - ctx.setTransform(transform); -} - - -function computePath(equations, start, end, step) { - const ts = [...Array(Math.floor((end-start)/step)).keys()].map(k => step * (start+k)) - return ts.map(t => [ equations.x.eval({t}), equations.y.eval({t}) ]); -} - -function drawGrid(ctx) { - ctx.scale(1, -1); - ctx.strokeStyle = 'white'; - ctx.fillStyle = 'white'; - const { width, height } = ctx.canvas; - ctx.font = `${200/Math.max(width, height)}px serif`; - ctx.lineWidth = 4 / Math.max(width, height); - const drawLine = (start, end) => { - ctx.beginPath(); - ctx.moveTo(start[0], start[1]); - ctx.lineTo(end[0], end[1]); - ctx.stroke(); - } - const drawXLabel = (x, y) => { - ctx.textAlign = 'right'; - ctx.textBaseline = 'bottom'; - ctx.fillText(`${x} `, x, y); - } - - const drawYLabel = (x, y) => { - ctx.rotate(Math.PI/2); - ctx.textAlign = 'right'; - ctx.textBaseline = 'bottom'; - ctx.fillText(`${y} `, y, x); - ctx.rotate(-Math.PI/2); - } - - const drawGrid = (start, end, step) => { - [...Array(1+Math.floor((end-start)/step)).keys()].forEach(z => { - z = (step*z)+start; - drawLine([z, start], [z, end]); - drawLine([start, z], [end, z]); - drawXLabel(z, end); - drawYLabel(end, z); - }); - } - drawGrid(-10, 10, 2); - ctx.scale(1, -1); -} - -function drawPath(ctx, path) { - const [ start, ...line ] = path; - const { width, height } = ctx.canvas; - ctx.strokeStyle = 'white'; - ctx.lineWidth = 8 / Math.max(width, height); - ctx.beginPath(); - ctx.moveTo(start[0], start[1]); - line.forEach(p => ctx.lineTo(p[0], p[1])); - ctx.stroke(); + musicGain.gain.value = 0.5; + const musicList = [ + 'music-minute', + 'music-starboard', + 'music-cribwhistling', + 'music-swish', + 'music-aeroplane', + ].reduce(addMusic, []); + musicList.forEach( + ({ element }, i) => element.addEventListener( + 'ended', + () => musicList[(i+1) % musicList.length].element.play(), + ) + ) + musicList[0].element.play(); + + const level = setupLevel([], []); + const ui = setupLevelUi(level, root, audio); + }; + root.appendChild(start); } diff --git a/render.js b/render.js new file mode 100644 index 0000000..46cc37b --- /dev/null +++ b/render.js @@ -0,0 +1,107 @@ +export function render(ctx, level, index) { + ctx.fillStyle = 'white'; + + const [x0, y0] = level.path[index]; + const [x1, y1] = level.path[index+1] || level.path[index]; + const angle = Math.atan2(y1-y0, x1-x0); + clearBlack(ctx); + drawGrid(ctx); + drawPath(ctx, level.path); + drawShip(ctx, [x0, y0], angle); + level.resources + .filter(({ collected }) => collected === false) + .forEach(({ pos }) => drawMark(ctx, '+', pos[0], pos[1])); +} + + +export function clearBlack(ctx) { + const { width, height } = ctx.canvas; + ctx.fillStyle = 'black'; + ctx.fillRect(-width, -height, 2*width, 2*height); +} + + +export function drawShip(ctx, pos, angle) { + const transform = ctx.getTransform(); + const { width, height } = ctx.canvas; + const [x, y] = pos; + ctx.translate(x, y); + ctx.rotate(angle); + ctx.fillStyle = 'white'; + const STEP = 0.2; + ctx.beginPath(); + ctx.moveTo(0, 0); + ctx.lineTo(-STEP, -STEP); + ctx.lineTo(2*STEP, 0); + ctx.lineTo(-STEP, STEP); + ctx.lineTo(0, 0); + ctx.fill(); + ctx.setTransform(transform); +} + + +export function drawGrid(ctx) { + ctx.scale(1, -1); + ctx.strokeStyle = 'white'; + ctx.fillStyle = 'white'; + const { width, height } = ctx.canvas; + ctx.font = `0.4px monospace`; + ctx.lineWidth = 4 / Math.max(width, height); + const drawLine = (start, end) => { + ctx.beginPath(); + ctx.moveTo(start[0], start[1]); + ctx.lineTo(end[0], end[1]); + ctx.stroke(); + } + const drawXLabel = (x, y) => { + ctx.textAlign = 'right'; + ctx.textBaseline = 'bottom'; + ctx.fillText(`${x} `, x, y); + } + + const drawYLabel = (x, y) => { + ctx.rotate(Math.PI/2); + ctx.textAlign = 'right'; + ctx.textBaseline = 'bottom'; + ctx.fillText(`${y} `, y, x); + ctx.rotate(-Math.PI/2); + } + + const drawGrid = (start, end, step) => { + [...Array(1+Math.floor((end-start)/step)).keys()].forEach(z => { + z = (step*z)+start; + drawLine([z, start], [z, end]); + drawLine([start, z], [end, z]); + drawXLabel(z, end); + drawYLabel(end, z); + }); + } + drawGrid(-10, 10, 2); + ctx.scale(1, -1); +} + +export function drawPath(ctx, path) { + const [ start, ...line ] = path; + ctx.strokeStyle = 'white'; + ctx.lineWidth = 0.02; + ctx.beginPath(); + ctx.moveTo(start[0], start[1]); + line.forEach(p => ctx.lineTo(p[0], p[1])); + ctx.stroke(); +} + + +export function drawMark(ctx, mark, x, y) { + ctx.strokeStyle = 'white'; + ctx.fillStyle = 'white'; + ctx.font = `0.5px monospace`; + ctx.lineWidth = 0.1; + + ctx.beginPath(); + ctx.arc(x, y, 0.4, 0, 2*Math.PI); + ctx.stroke(); + + ctx.textAlign = 'center'; + ctx.textBaseline = 'middle'; + ctx.fillText(mark, x-0.02, y-0.12); +} diff --git a/sounds/ebi-sea.mp3 b/sounds/ebi-sea.mp3 Binary files differdeleted file mode 100644 index 1c8e678..0000000 --- a/sounds/ebi-sea.mp3 +++ /dev/null diff --git a/sounds/129655__perplessio__droplets-underwater.aiff b/sounds/sfx/129655__perplessio__droplets-underwater.aiff Binary files differindex f08b7ed..f08b7ed 100644 --- a/sounds/129655__perplessio__droplets-underwater.aiff +++ b/sounds/sfx/129655__perplessio__droplets-underwater.aiff diff --git a/sounds/318688__limitsnap_creations__rocket-thrust-effect.wav b/sounds/sfx/318688__limitsnap_creations__rocket-thrust-effect.wav Binary files differindex ab45061..ab45061 100644 --- a/sounds/318688__limitsnap_creations__rocket-thrust-effect.wav +++ b/sounds/sfx/318688__limitsnap_creations__rocket-thrust-effect.wav diff --git a/sounds/sfx/346573__inspectorj__train-door-beep-a.wav b/sounds/sfx/346573__inspectorj__train-door-beep-a.wav Binary files differnew file mode 100644 index 0000000..e85be13 --- /dev/null +++ b/sounds/sfx/346573__inspectorj__train-door-beep-a.wav diff --git a/sounds/sfx/beep/_readme_and_license.txt b/sounds/sfx/beep/_readme_and_license.txt new file mode 100644 index 0000000..362e85f --- /dev/null +++ b/sounds/sfx/beep/_readme_and_license.txt @@ -0,0 +1,60 @@ +Sound pack downloaded from Freesound +---------------------------------------- + +"NoizDumpster Beep Sequences" + +This pack of sounds contains sounds by the following user: + - AlienXXX ( https://freesound.org/people/AlienXXX/ ) + +You can find this pack online at: https://freesound.org/people/AlienXXX/packs/16506/ + + +Pack description +---------------- + +Beep sequences made with The NoizDumpster VST (by The Lower Rhythm) back in Jun 2013. +You can find NoizDumpster here: +http://urthwurk.com/thelowerrhythm/ + + +Licenses in this pack (see below for individual sound licenses) +--------------------------------------------------------------- + +Attribution 4.0: https://creativecommons.org/licenses/by/4.0/ + + +Sounds in this pack +------------------- + + * 267561__alienxxx__beep-sequence-10.wav + * url: https://freesound.org/s/267561/ + * license: Attribution 4.0 + * 267560__alienxxx__beep-sequence-01.wav + * url: https://freesound.org/s/267560/ + * license: Attribution 4.0 + * 267559__alienxxx__beep-sequence-06.wav + * url: https://freesound.org/s/267559/ + * license: Attribution 4.0 + * 267558__alienxxx__beep-sequence-07.wav + * url: https://freesound.org/s/267558/ + * license: Attribution 4.0 + * 267557__alienxxx__beep-sequence-08.wav + * url: https://freesound.org/s/267557/ + * license: Attribution 4.0 + * 267556__alienxxx__beep-sequence-09.wav + * url: https://freesound.org/s/267556/ + * license: Attribution 4.0 + * 267555__alienxxx__beep-sequence-02.wav + * url: https://freesound.org/s/267555/ + * license: Attribution 4.0 + * 267554__alienxxx__beep-sequence-03.wav + * url: https://freesound.org/s/267554/ + * license: Attribution 4.0 + * 267553__alienxxx__beep-sequence-04.wav + * url: https://freesound.org/s/267553/ + * license: Attribution 4.0 + * 267552__alienxxx__beep-sequence-05.wav + * url: https://freesound.org/s/267552/ + * license: Attribution 4.0 + + diff --git a/sounds/sfx/beep/alienxxx__noizdumpster-beep-sequences.zip b/sounds/sfx/beep/alienxxx__noizdumpster-beep-sequences.zip Binary files differnew file mode 100644 index 0000000..665b01c --- /dev/null +++ b/sounds/sfx/beep/alienxxx__noizdumpster-beep-sequences.zip diff --git a/sounds/sfx/beep/sequence_01.wav b/sounds/sfx/beep/sequence_01.wav Binary files differnew file mode 100644 index 0000000..4eb8a58 --- /dev/null +++ b/sounds/sfx/beep/sequence_01.wav diff --git a/sounds/sfx/beep/sequence_02.wav b/sounds/sfx/beep/sequence_02.wav Binary files differnew file mode 100644 index 0000000..58bfc85 --- /dev/null +++ b/sounds/sfx/beep/sequence_02.wav diff --git a/sounds/sfx/beep/sequence_03.wav b/sounds/sfx/beep/sequence_03.wav Binary files differnew file mode 100644 index 0000000..24a76c4 --- /dev/null +++ b/sounds/sfx/beep/sequence_03.wav diff --git a/sounds/sfx/beep/sequence_04.wav b/sounds/sfx/beep/sequence_04.wav Binary files differnew file mode 100644 index 0000000..1259921 --- /dev/null +++ b/sounds/sfx/beep/sequence_04.wav diff --git a/sounds/sfx/beep/sequence_05.wav b/sounds/sfx/beep/sequence_05.wav Binary files differnew file mode 100644 index 0000000..81b2320 --- /dev/null +++ b/sounds/sfx/beep/sequence_05.wav diff --git a/sounds/sfx/beep/sequence_06.wav b/sounds/sfx/beep/sequence_06.wav Binary files differnew file mode 100644 index 0000000..7d34b92 --- /dev/null +++ b/sounds/sfx/beep/sequence_06.wav diff --git a/sounds/sfx/beep/sequence_07.wav b/sounds/sfx/beep/sequence_07.wav Binary files differnew file mode 100644 index 0000000..c5e89f1 --- /dev/null +++ b/sounds/sfx/beep/sequence_07.wav diff --git a/sounds/sfx/beep/sequence_08.wav b/sounds/sfx/beep/sequence_08.wav Binary files differnew file mode 100644 index 0000000..998b824 --- /dev/null +++ b/sounds/sfx/beep/sequence_08.wav diff --git a/sounds/sfx/beep/sequence_09.wav b/sounds/sfx/beep/sequence_09.wav Binary files differnew file mode 100644 index 0000000..a3f557e --- /dev/null +++ b/sounds/sfx/beep/sequence_09.wav diff --git a/sounds/sfx/beep/sequence_10.wav b/sounds/sfx/beep/sequence_10.wav Binary files differnew file mode 100644 index 0000000..7ab812d --- /dev/null +++ b/sounds/sfx/beep/sequence_10.wav diff --git a/sounds/droplet.mp3 b/sounds/sfx/droplet.mp3 Binary files differindex 1852466..1852466 100644 --- a/sounds/droplet.mp3 +++ b/sounds/sfx/droplet.mp3 diff --git a/sounds/rocket-thrust.wav b/sounds/sfx/rocket-thrust.wav Binary files differindex ab45061..ab45061 100644 --- a/sounds/rocket-thrust.wav +++ b/sounds/sfx/rocket-thrust.wav diff --git a/sounds/sfx/ship_move.mp3 b/sounds/sfx/ship_move.mp3 Binary files differnew file mode 100644 index 0000000..f0e0b3e --- /dev/null +++ b/sounds/sfx/ship_move.mp3 diff --git a/sounds/sfx/ship_move2.mp3 b/sounds/sfx/ship_move2.mp3 Binary files differnew file mode 100644 index 0000000..65f9c2a --- /dev/null +++ b/sounds/sfx/ship_move2.mp3 |