Gam
This commit is contained in:
commit
607f09b75a
3 changed files with 313 additions and 0 deletions
22
index.html
Normal file
22
index.html
Normal file
|
@ -0,0 +1,22 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<title>Ludum Dare 47 - Loops</title>
|
||||
<link href="./style.css" rel="stylesheet" />
|
||||
<link href="https://fonts.googleapis.com/css2?family=Ubuntu+Mono&display=swap" rel="stylesheet" />
|
||||
<script src="./script.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<div id="primary">
|
||||
<div id="width_dummy"></div>
|
||||
<div id="prompt"></div>
|
||||
<div id="entry_space">
|
||||
<div id="arrow">></div>
|
||||
<input id="console" type="text" value=""></input>
|
||||
<div id="under_cursor"> </div>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
|
205
script.js
Normal file
205
script.js
Normal file
|
@ -0,0 +1,205 @@
|
|||
var storyline = storyline || {};
|
||||
storyline.access = (path) => {
|
||||
path = path.split('.');
|
||||
let obj = storyline;
|
||||
for (const sub of path) {
|
||||
obj = obj[sub];
|
||||
}
|
||||
return obj;
|
||||
}
|
||||
|
||||
let widthDummy = null;
|
||||
function fitToText(element) {
|
||||
let elementStyle = window.getComputedStyle(element);
|
||||
widthDummy.style.fontSize = elementStyle.getPropertyValue('font-size');
|
||||
widthDummy.style.fontFamily = elementStyle.getPropertyValue('font-family');
|
||||
|
||||
widthDummy.textContent = '|';
|
||||
let pipeWidth = widthDummy.clientWidth;
|
||||
widthDummy.textContent = '|' + element.value + '|';
|
||||
element.style.width = widthDummy.clientWidth - 2 * pipeWidth + "px";
|
||||
}
|
||||
|
||||
/* https://davidwalsh.name/caret-end */
|
||||
function moveCursorToEnd(el) {
|
||||
if (typeof el.selectionStart == "number") {
|
||||
el.selectionStart = el.selectionEnd = el.value.length;
|
||||
} else if (typeof el.createTextRange != "undefined") {
|
||||
el.focus();
|
||||
var range = el.createTextRange();
|
||||
range.collapse(false);
|
||||
range.select();
|
||||
}
|
||||
}
|
||||
|
||||
function sleep(millis) {
|
||||
return new Promise(resolve => setTimeout(resolve, millis));
|
||||
}
|
||||
|
||||
let promptDiv = null;
|
||||
let responses = [];
|
||||
let currentId = 0;
|
||||
function reset(id) {
|
||||
if (id === currentId) {
|
||||
promptDiv.textContent = "";
|
||||
}
|
||||
}
|
||||
|
||||
function append(id, c) {
|
||||
if (id === currentId) {
|
||||
promptDiv.textContent += c;
|
||||
}
|
||||
}
|
||||
|
||||
async function promptUser(module) {
|
||||
const MIN_SLEEP = 25;
|
||||
const MAX_SLEEP = 50;
|
||||
const SPACE_SCALAR = 1.1;
|
||||
|
||||
const localId = ++currentId;
|
||||
module = storyline.access(module);
|
||||
for (const p of module.prompts) {
|
||||
responses = p.responses;
|
||||
reset(localId);
|
||||
for (let i = 0; i < p.text.length; ++i) {
|
||||
let c = p.text.charAt(i);
|
||||
append(localId, c);
|
||||
if (c === ' ') {
|
||||
await sleep((Math.random() * (MAX_SLEEP - MIN_SLEEP) + MIN_SLEEP) * SPACE_SCALAR);
|
||||
} else {
|
||||
await sleep((Math.random() * (MAX_SLEEP - MIN_SLEEP) + MIN_SLEEP));
|
||||
}
|
||||
}
|
||||
await sleep(p.wait);
|
||||
}
|
||||
if (localId === currentId && module.continuation) {
|
||||
promptUser(module.continuation);
|
||||
}
|
||||
}
|
||||
|
||||
window.addEventListener("load", () => {
|
||||
widthDummy = document.querySelector("#width_dummy");
|
||||
promptDiv = document.querySelector("#prompt");
|
||||
|
||||
let userConsole = document.querySelector("#console");
|
||||
let underCursor = document.querySelector("#under_cursor");
|
||||
userConsole.addEventListener("input", (event) => {
|
||||
fitToText(userConsole);
|
||||
});
|
||||
userConsole.addEventListener("keyup", (event) => {
|
||||
if (event.keyCode === 37) {
|
||||
moveCursorToEnd(userConsole);
|
||||
} else if (event.keyCode === 13) {
|
||||
const entry = userConsole.value;
|
||||
let matched = false;
|
||||
for (const responseOption of responses) {
|
||||
if (responseOption.matches(entry)) {
|
||||
matched = true;
|
||||
promptUser(responseOption.target);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (matched) {
|
||||
userConsole.value = "";
|
||||
fitToText(userConsole);
|
||||
}
|
||||
}
|
||||
});
|
||||
userConsole.addEventListener("click", (event) => {
|
||||
moveCursorToEnd(userConsole);
|
||||
});
|
||||
let timerId = 0;
|
||||
let state = 0;
|
||||
userConsole.addEventListener("focusin", (event) => {
|
||||
moveCursorToEnd(userConsole);
|
||||
timerId = window.setInterval((e) => {
|
||||
if (state === 0) {
|
||||
underCursor.style.visibility = "visible";
|
||||
state = 1;
|
||||
} else {
|
||||
underCursor.style.visibility = "hidden";
|
||||
state = 0;
|
||||
}
|
||||
}, 500);
|
||||
});
|
||||
userConsole.addEventListener("focusout", (event) => {
|
||||
state = 0;
|
||||
window.clearInterval(timerId);
|
||||
underCursor.style.visibility = "hidden";
|
||||
});
|
||||
|
||||
fitToText(userConsole);
|
||||
userConsole.focus();
|
||||
|
||||
let entrySpace = document.querySelector("#entry_space");
|
||||
entrySpace.addEventListener("click", (event) => {
|
||||
userConsole.focus();
|
||||
});
|
||||
|
||||
promptUser('welcome');
|
||||
});
|
||||
|
||||
function Regex(regex) {
|
||||
return text => regex.test(text);
|
||||
}
|
||||
|
||||
storyline.common = {};
|
||||
storyline.common.yes = Regex(/^(y(y*e+s+)?)|(sure)$/i);
|
||||
storyline.common.no = Regex(/^n(n*o+)?$/i);
|
||||
storyline.common.any = text => true;
|
||||
|
||||
storyline.welcome = {
|
||||
prompts: [
|
||||
{ text: "Welcome!", wait: 1000, responses: [] },
|
||||
],
|
||||
continuation: 'begin',
|
||||
};
|
||||
|
||||
storyline.begin = {
|
||||
prompts: [
|
||||
{ text: "Would you like to begin?", wait: 0, responses: [
|
||||
{ matches: storyline.common.yes, target: 'repeat', },
|
||||
{ matches: storyline.common.no, target: 'begin.no', },
|
||||
{ matches: storyline.common.any, target: 'begin.unclear', },
|
||||
]},
|
||||
],
|
||||
};
|
||||
|
||||
storyline.begin.no = {
|
||||
prompts: [
|
||||
{ text: "As you wish. Whenever you're ready, hit enter and we will begin.", wait: 3000, responses: [
|
||||
{ matches: storyline.common.any, target: 'repeat' },
|
||||
]},
|
||||
],
|
||||
};
|
||||
|
||||
storyline.begin.unclear = {
|
||||
prompts: [
|
||||
{ text: "My apologies, I am unable to parse the textual data you passed forth. I invite you to please try again.", wait: 3000, },
|
||||
],
|
||||
continuation: 'begin',
|
||||
};
|
||||
|
||||
storyline.repeat = {
|
||||
prompts: [
|
||||
{ text: "Pete and repeat went out on a boat. Pete fell off. Who was left?", wait: 2000, responses: [
|
||||
{ matches: Regex(/r[e3]p[3e][a4]t/i), target: 'repeat', },
|
||||
{ matches: Regex(/.*n[o0]t.*p[e3]t[e3].*/i), target: 'repeat.unsmort', },
|
||||
{ matches: storyline.common.any, target: 'repeat.wat', },
|
||||
]},
|
||||
],
|
||||
};
|
||||
|
||||
storyline.repeat.unsmort = {
|
||||
prompts: [
|
||||
{ text: "Ah! A self proclaimed intellectual I see. Listen here, I'm gonna ask you again and I want a NAME this time! Thank you in advance.", wait: 4000, responses: [], },
|
||||
],
|
||||
continuation: 'repeat',
|
||||
};
|
||||
|
||||
storyline.repeat.wat = {
|
||||
prompts: [
|
||||
{ text: "I do apologize but that is not the correct answer. I do hope this hasn't discouraged you and you will try again though!", wait: 3500, responses: [], },
|
||||
],
|
||||
continuation: 'repeat',
|
||||
};
|
86
style.css
Normal file
86
style.css
Normal file
|
@ -0,0 +1,86 @@
|
|||
:root {
|
||||
--background-color: #000000;
|
||||
--text-color: #00FF33;
|
||||
}
|
||||
|
||||
::selection {
|
||||
background-color: var(--text-color);
|
||||
color: var(--background-color);
|
||||
}
|
||||
|
||||
html {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
body {
|
||||
height: 100%;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
background-color: var(--background-color);
|
||||
}
|
||||
|
||||
|
||||
#primary {
|
||||
width: 100%;
|
||||
height: 50%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
#width_dummy {
|
||||
position: fixed;
|
||||
visibility: hidden;
|
||||
}
|
||||
|
||||
#prompt {
|
||||
width: 80%;
|
||||
height: max-content;
|
||||
|
||||
margin-bottom: 0.2em;
|
||||
|
||||
font-size: 3em;
|
||||
font-family: 'Ubuntu Mono', monospace;
|
||||
text-align: center;
|
||||
word-wrap: break-word;
|
||||
|
||||
color: var(--text-color);
|
||||
}
|
||||
|
||||
#entry_space {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
|
||||
}
|
||||
|
||||
#arrow {
|
||||
margin-right: 0.25em;
|
||||
|
||||
font-size: 2em;
|
||||
font-family: 'Ubuntu Mono', monospace;
|
||||
|
||||
color: var(--text-color);
|
||||
}
|
||||
|
||||
#console {
|
||||
border: 0px solid var(--background-color);
|
||||
padding: 0px;
|
||||
|
||||
font-size: 2em;
|
||||
font-family: 'Ubuntu Mono', monospace;
|
||||
text-overflow: hidden;
|
||||
caret-color: transparent;
|
||||
|
||||
background-color: var(--background-color);
|
||||
color: var(--text-color);
|
||||
}
|
||||
|
||||
#under_cursor {
|
||||
font-size: 2em;
|
||||
font-family: 'Ubuntu Mono', monospace;
|
||||
|
||||
background-color: var(--text-color);
|
||||
}
|
||||
|
Loading…
Reference in a new issue