summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--index.html10
-rw-r--r--level.js107
-rw-r--r--levelSelect.js40
-rw-r--r--main.js43
-rw-r--r--render.js2
-rw-r--r--style.css64
6 files changed, 200 insertions, 66 deletions
diff --git a/index.html b/index.html
index d724516..fba6177 100644
--- a/index.html
+++ b/index.html
@@ -2,11 +2,16 @@
<html>
<head>
<meta charset="utf-8">
+ <meta name="viewport" content="width=device-width, initial-scale=1">
<title>robotic</title>
<script type="module" src="main.js"></script>
<script src="mathjs/mathjs-expression-parser.js"></script>
+ <link rel="preconnect" href="https://fonts.googleapis.com">
+ <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
+ <link href="https://fonts.googleapis.com/css2?family=Jura:wght@300..700&display=swap" rel="stylesheet">
+ <link href="style.css" rel="stylesheet">
</head>
- <body>
+ <body style="font-family: Jura;">
<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>
@@ -17,8 +22,9 @@
<audio id="sfx-wrong" src="sounds/sfx/gui-beep/33789__jobro__5-beep-c.wav"></audio>
<audio id="sfx-buttonenter" src="sounds/sfx/gui-beep/33777__jobro__1-beep-c.wav"></audio>
- <audio id="sfx-buttonclick" src="sounds/sfx/gui-beep/33778__jobro__2-beep-a.wav"></audio>
+ <audio id="sfx-buttonclick" src="sounds/sfx/gui-beep/33780__jobro__2-beep-c.wav"></audio>
<div id="root"></div>
+ <div id="overlay"></div>
</body>
</html>
diff --git a/level.js b/level.js
index 5671c4c..ba1032f 100644
--- a/level.js
+++ b/level.js
@@ -20,7 +20,9 @@ export function setupLevel(resources, home) {
completed: false,
index: 0,
equations: {
+ xStr: X_START,
x: math.compile(X_START),
+ yStr: Y_START,
y: math.compile(Y_START),
},
};
@@ -29,45 +31,31 @@ export function setupLevel(resources, home) {
}
-export function setupLevelUi(level, root, audio) {
- const ui = { audio };
-
- const buttonEnterAudio = document.getElementById('sfx-buttonenter');
- const buttonClickAudio = document.getElementById('sfx-buttonclick');
- audio.createMediaElementSource(buttonEnterAudio).connect(audio.destination);
- audio.createMediaElementSource(buttonClickAudio).connect(audio.destination);
-
- 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);
+export function setupLevelUi(level, root, sfx) {
+ const ui = { sfx };
+ root.classList.add('center');
ui.launchButton = document.createElement('input');
ui.launchButton.type = 'button';
ui.launchButton.value = 'Launch';
ui.launchButton.onmouseenter = () => {
- buttonEnterAudio.load();
- buttonEnterAudio.play();
+ sfx.buttonEnterAudio.currentTime = 0;
+ sfx.buttonEnterAudio.play();
}
ui.launchButton.onclick = () => {
- buttonClickAudio.load();
- buttonClickAudio.play();
+ sfx.buttonClickAudio.currentTime = 0;
+ sfx.buttonClickAudio.play();
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.id = 'xeq';
+ ui.xeq.value = level.equations.xStr;
ui.xeq.onchange = (e) => {
try {
+ level.equations.xStr = e.target.value;
level.equations.x = math.compile(e.target.value);
} catch(err) { }
level.running = false;
@@ -76,11 +64,15 @@ export function setupLevelUi(level, root, audio) {
};
ui.xeq.onkeydown = ui.xeq.onchange;
ui.xeq.onkeyup = ui.xeq.onchange;
+ ui.xeqLabel = document.createElement('label');
+ ui.xeqLabel.htmlFor = 'xeq';
+ ui.xeqLabel.innerText = 'x(t) = ';
ui.yeq = document.createElement('input');
- ui.yeq.value = Y_START;
+ ui.yeq.value = level.equations.yStr;
ui.yeq.onchange = (e) => {
try {
+ level.equations.yStr = e.target.value;
level.equations.y = math.compile(e.target.value);
} catch(err) { }
level.running = false;
@@ -89,40 +81,54 @@ export function setupLevelUi(level, root, audio) {
};
ui.yeq.onkeydown = ui.yeq.onchange;
ui.yeq.onkeyup = ui.yeq.onchange;
+ ui.yeqLabel = document.createElement('label');
+ ui.yeqLabel.htmlFor = 'yeq';
+ ui.yeqLabel.innerText = 'y(t) = ';
ui.message = document.createElement('div');
ui.id = "level-message";
- root.appendChild(ui.launchButton);
+ root.appendChild(ui.xeqLabel);
root.appendChild(ui.xeq);
+ root.appendChild(document.createElement('br'));
+ root.appendChild(ui.yeqLabel);
root.appendChild(ui.yeq);
+ root.appendChild(document.createElement('br'));
+ root.appendChild(ui.launchButton);
root.appendChild(ui.message);
ui.canvas = document.createElement('canvas');
- ui.canvas.width = 600;
- ui.canvas.height = 600;
+ const setCanvasSize = () => {
+ const size = Math.min(0.7*window.innerHeight, 0.9*root.offsetWidth);
+ ui.canvas.width = size;
+ ui.canvas.height = size;
+ 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);
+ }
+ setCanvasSize();
+ window.onresize = setCanvasSize;
root.appendChild(ui.canvas);
+ root.appendChild(document.createElement('br'));
ui.returnBtn = document.createElement('input');
ui.returnBtn.type = 'button';
- ui.returnBtn.value = 'Return';
+ ui.returnBtn.value = '↲ Return';
ui.returnBtn.onmouseenter = () => {
- buttonEnterAudio.load();
- buttonEnterAudio.play();
+ sfx.buttonEnterAudio.currentTime = 0;
+ sfx.buttonEnterAudio.play();
}
ui.returnBtn.onclick = () => {
- buttonClickAudio.load();
- buttonClickAudio.play();
+ sfx.buttonClickAudio.currentTime = 0;
+ sfx.buttonClickAudio.play();
root.innerText = '';
- setupLevelSelectUi(root, audio);
+ root.classList.remove('center');
+ window.onresize = () => {};
+ setupLevelSelectUi(root, sfx);
}
root.appendChild(ui.returnBtn);
- 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;
}
@@ -137,8 +143,8 @@ function stepLevel(ui, level, index) {
.map(([x, i]) => [ distance(pos, x.position), i ])
.forEach(([d, i]) => {
if (d < 0.4) {
- ui.resourceAudio.load();
- ui.resourceAudio.play();
+ ui.sfx.resourceAudio.currentTime = 0;
+ ui.sfx.resourceAudio.play();
level.resources[i].collected = true;
}
});
@@ -147,11 +153,12 @@ function stepLevel(ui, level, index) {
if (distance(pos, level.home) < 0.4) {
level.running = false;
- finishLevel(ui, level);
+ finishLevel(ui, level, false);
render(ui.ctx, level, 0);
} else if (level.running && index < level.path.length-1) {
setTimeout(() => stepLevel(ui, level, index+1), 1);
} else {
+ finishLevel(ui, level, true);
level.running = false;
render(ui.ctx, level, 0);
}
@@ -175,15 +182,19 @@ function distance(p1, p2) {
}
-function finishLevel(ui, level) {
+function finishLevel(ui, level, fail) {
const uncollected = level.resources.reduce((acc, { collected }) => acc + (collected ? 0 : 1), 0);
- if (uncollected > 0) {
- ui.wrongAudio.load();
- ui.wrongAudio.play();
+ if (fail) {
+ ui.sfx.wrongAudio.currentTime = 0;
+ ui.sfx.wrongAudio.play();
+ ui.message.innerText = 'did not arrive at destination';
+ } else if (uncollected > 0) {
+ ui.sfx.wrongAudio.currentTime = 0;
+ ui.sfx.wrongAudio.play();
ui.message.innerText = 'resources remaining!';
} else {
- ui.doneAudio.load();
- ui.doneAudio.play();
+ ui.sfx.doneAudio.currentTime = 0;
+ ui.sfx.doneAudio.play();
ui.message.innerText = 'mission complete!';
level.completed = true;
}
diff --git a/levelSelect.js b/levelSelect.js
index b1f6073..0fbdbf0 100644
--- a/levelSelect.js
+++ b/levelSelect.js
@@ -3,38 +3,54 @@ import{ setupLevel, setupLevelUi } from './level.js';
const levels = {
'Initial Jump': [ [], setupLevel([], [8, 0]) ],
'Monopole Mining': [ ['Initial Jump'], setupLevel([[3, 3]], [8,8]) ],
+ 'Parabolic': [ ['Initial Jump', 'Monopole Mining'], setupLevel([[-2, 4], [0,0], [2, 4]], [3,9]) ],
+ 'Sinusoidal': [
+ ['Initial Jump', 'Monopole Mining'],
+ setupLevel([
+ [-4, 4*Math.sin(0)],
+ [0.5*Math.PI - 4, 4*Math.sin(0.5*Math.PI)],
+ [1.0*Math.PI - 4, 4*Math.sin(1.0*Math.PI)],
+ [1.5*Math.PI - 4, 4*Math.sin(1.5*Math.PI)],
+ ], [2.0*Math.PI - 4, 4*Math.sin(2.0*Math.PI)]),
+ ],
};
-export function setupLevelSelectUi(root, audio) {
- const buttonEnterAudio = document.getElementById('sfx-buttonenter');
- const buttonClickAudio = document.getElementById('sfx-buttonclick');
- audio.createMediaElementSource(buttonEnterAudio).connect(audio.destination);
- audio.createMediaElementSource(buttonClickAudio).connect(audio.destination);
-
+export function setupLevelSelectUi(root, sfx) {
const levelList = document.createElement('ol');
const levelPicker = name => {
const [ dependencies, level ] = levels[name];
const allDependenciesSatisfied = dependencies.map(x => levels[x][1].completed).reduce((acc, x) => acc && x, true);
- if (allDependenciesSatisfied) {
+ // if (allDependenciesSatisfied) {
+ if (true) {
const button = document.createElement('input');
button.type = 'button';
button.value = name;
button.onmouseenter = () => {
- buttonEnterAudio.load();
- buttonEnterAudio.play();
+ sfx.buttonEnterAudio.currentTime = 0;
+ sfx.buttonEnterAudio.play();
}
button.onclick = () => {
- buttonClickAudio.load();
- buttonClickAudio.play();
+ sfx.buttonClickAudio.currentTime = 0;
+ sfx.buttonClickAudio.play();
root.innerText = '';
- setupLevelUi(level, root, audio);
+ setupLevelUi(level, root, sfx);
}
const li = document.createElement('li');
li.appendChild(button);
+ if (level.completed) {
+ li.appendChild(document.createTextNode('[complete]'));
+ }
levelList.appendChild(li);
}
}
[...Object.keys(levels)].forEach(levelPicker);
+ const header = document.createElement('h1');
+ header.classList.add('center');
+ header.innerText = 'AVAILABLE CONTRACTS';
+ root.appendChild(header);
+ const p = document.createElement('p');
+ p.innerText = 'Please select a General Products AMH hyperspace navigation contract from the list below.';
+ root.appendChild(p);
root.appendChild(levelList);
}
diff --git a/main.js b/main.js
index 3ca2f27..a052d74 100644
--- a/main.js
+++ b/main.js
@@ -4,13 +4,32 @@ import { setupLevel, setupLevelUi } from './level.js';
window.onload = () => {
const root = document.getElementById('root');
+ root.classList.add('center');
+
+ const title = document.createElement('h1');
+ title.innerText = 'GENERAL PRODUCTS';
+ root.appendChild(title);
+
+ const logo = document.createElement('h1');
+ logo.classList.add('logo');
+ logo.innerText = '※';
+ root.appendChild(logo);
+
+ const title2 = document.createElement('h2');
+ title2.innerText = 'AUTOMATED MONOPOLE HARVESTER (AMH) HYPERSPACE NAVIGATIONAL SERVICES';
+ root.appendChild(title2);
+
+ const paragraph = document.createElement('p');
+ paragraph.innerText = 'Greetings, navigator! Thank you for helping us to program the hyperspace engines of our Automated Monopole Harvester (AMH) platforms. By doing so, you help ensure stable antimatter production throughout the Nine Worlds.';
+ root.appendChild(paragraph);
const start = document.createElement('input');
start.type = 'button';
- start.value = 'Start';
+ start.value = 'Connect to AMH';
start.onclick = () => {
+ root.classList.remove('center');
+ root.innerText = '';
const audio = new AudioContext();
- root.removeChild(start);
const musicGain = audio.createGain();
const addMusic = (music, id) => {
@@ -35,9 +54,27 @@ window.onload = () => {
)
musicList[0].element.play();
+ const sfx = {};
+ sfx.buttonEnterAudio = document.getElementById('sfx-buttonenter');
+ sfx.buttonClickAudio = document.getElementById('sfx-buttonclick');
+ audio.createMediaElementSource(sfx.buttonEnterAudio).connect(audio.destination);
+ audio.createMediaElementSource(sfx.buttonClickAudio).connect(audio.destination);
+
+ sfx.resourceAudio = document.getElementById('sfx-resource');
+ sfx.resourceSource = audio.createMediaElementSource(sfx.resourceAudio);
+ sfx.resourceSource.connect(audio.destination);
+
+ sfx.doneAudio = document.getElementById('sfx-done');
+ sfx.doneSource = audio.createMediaElementSource(sfx.doneAudio);
+ sfx.doneSource.connect(audio.destination);
+
+ sfx.wrongAudio = document.getElementById('sfx-wrong');
+ sfx.wrongSource = audio.createMediaElementSource(sfx.wrongAudio);
+ sfx.wrongSource.connect(audio.destination);
+
// const level = setupLevel([[2, 2], [3,3]], [7, 7]);
// const ui = setupLevelUi(level, root, audio);
- setupLevelSelectUi(root, audio);
+ setupLevelSelectUi(root, sfx);
};
root.appendChild(start);
}
diff --git a/render.js b/render.js
index be3bc8e..5fde9d1 100644
--- a/render.js
+++ b/render.js
@@ -84,7 +84,7 @@ export function drawGrid(ctx) {
export function drawPath(ctx, path) {
const [ start, ...line ] = path;
ctx.strokeStyle = 'white';
- ctx.lineWidth = 0.02;
+ ctx.lineWidth = 0.04;
ctx.beginPath();
ctx.moveTo(start[0], start[1]);
line.forEach(p => ctx.lineTo(p[0], p[1]));
diff --git a/style.css b/style.css
new file mode 100644
index 0000000..e0c968d
--- /dev/null
+++ b/style.css
@@ -0,0 +1,64 @@
+body {
+ color: white;
+ background-color: black;
+ font-family: "Jura", monospace;
+ font-variant-caps: small-caps;
+ position: relative;
+ overflow-x: hidden;
+}
+
+.logo {
+ margin-top: 0em;
+ margin-bottom: 0em;
+ font-size: 10em;
+}
+
+#overlay {
+ position: absolute;
+ top: 0;
+ right: 0;
+ left: 0;
+ height: 100%;
+ background: orange;
+ mix-blend-mode: multiply;
+ pointer-events: none;
+ overflow: none;
+}
+
+#root {
+ width: min(90vw, 50em);
+ margin: auto;
+ border-left: 1px solid white;
+ border-right: 1px solid white;
+ padding: 0.5em;
+ min-height: 95vh;
+}
+
+.center {
+ text-align: center;
+ margin: auto;
+}
+
+input {
+ font-family: "Jura", monospace;
+ font-variant-caps: small-caps;
+ color: white;
+ background-color: black;
+ font-size: 1em;
+ padding: 0.5em;
+ margin: 0.5em;
+ font-weight: bold;
+ border: 2px solid;
+}
+input[type=button] {
+ padding: 0.6em;
+ font-size: 1.3em;
+}
+input[type=button]:hover {
+ color: black;
+ background-color: white;
+}
+input[type=button]:active {
+ color: black;
+ background-color: grey;
+}