This commit is contained in:
Gnarwhal 2024-07-14 22:58:08 +00:00
commit 607f09b75a
Signed by: Gnarwhal
GPG key ID: 0989A73D8C421174
3 changed files with 313 additions and 0 deletions

22
index.html Normal file
View 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">&gt;</div>
<input id="console" type="text" value=""></input>
<div id="under_cursor">&nbsp;</div>
</div>
</div>
</body>
</html>

205
script.js Normal file
View 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
View 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);
}