Skip to content

Instantly share code, notes, and snippets.

@JoshuaGrams
Last active May 5, 2021 12:50
Show Gist options
  • Save JoshuaGrams/073e02468a5fe3addb8cdbfdc6f199d3 to your computer and use it in GitHub Desktop.
Save JoshuaGrams/073e02468a5fe3addb8cdbfdc6f199d3 to your computer and use it in GitHub Desktop.
Extremely basic storylets in Ink
(function(storyContent) {
var story = new inkjs.Story(storyContent);
var storyContainer = document.querySelectorAll('#story')[0];
function showAfter(delay, el) {
setTimeout(function() { el.classList.add("show") }, delay);
}
function choose(list, n) {
const last = list.length - 1;
for(let i=0; i<Math.min(n, list.length - 2); ++i) {
const j = i + Math.floor((last - i)*Math.random());
const t = list[i]; list[i] = list[j]; list[j] = t;
}
return list.slice(0, n);
}
function scrollToBottom() {
var progress = 0.0;
var start = window.pageYOffset || document.documentElement.scrollTop || document.body.scrollTop || 0;
var dist = document.body.scrollHeight - window.innerHeight - start;
if( dist < 0 ) return;
var duration = 300 + 300*dist/100;
var startTime = null;
function step(time) {
if( startTime == null ) startTime = time;
var t = (time-startTime) / duration;
var lerp = 3*t*t - 2*t*t*t;
window.scrollTo(0, start + lerp*dist);
if( t < 1 ) requestAnimationFrame(step);
}
requestAnimationFrame(step);
}
function continueStory() {
var paragraphIndex = 0;
var delay = 0.0;
// Generate story text - loop through available content
while(story.canContinue) {
// Get ink to generate the next paragraph
var paragraphText = story.Continue();
// Create paragraph element
var paragraphElement = document.createElement('p');
paragraphElement.innerHTML = paragraphText;
storyContainer.appendChild(paragraphElement);
// Fade in paragraph after a short delay
showAfter(delay, paragraphElement);
delay += 200.0;
}
// Create HTML choices from ink choices
const limit = story.variablesState.num_storylets || story.currentChoices.length;
choose(story.currentChoices, limit).forEach(function(choice) {
// Create paragraph with anchor element
var choiceParagraphElement = document.createElement('p');
choiceParagraphElement.classList.add("choice");
choiceParagraphElement.innerHTML = `<a href='#'>${choice.text}</a>`
storyContainer.appendChild(choiceParagraphElement);
// Fade choice in after a short delay
showAfter(delay, choiceParagraphElement);
delay += 200.0;
// Click on choice
var choiceAnchorEl = choiceParagraphElement.querySelectorAll("a")[0];
choiceAnchorEl.addEventListener("click", function(event) {
// Don't follow <a> link
event.preventDefault();
// Remove all existing choices
var existingChoices = storyContainer.querySelectorAll('p.choice');
for(var i=0; i<existingChoices.length; i++) {
var c = existingChoices[i];
c.parentNode.removeChild(c);
}
// Tell the story where to go next
story.ChooseChoiceIndex(choice.index);
// Aaand loop
continueStory();
});
});
scrollToBottom();
}
continueStory();
})(storyContent);
VAR num_storylets = 0 // 0 means no limit.
VAR location = "home"
VAR driving = false
-> home
== choose_storylet ==
// This is your "deck of cards": a list of conditional choices, one for each
// storylet. Each choice will show up only when the storylet is available.
//
// It's a little inconvenient having the conditions separate from the content,
// but you can have little groups of conditions near the content and use the
// threading operator <- to include them into a bigger "deck" knot.
//
// If you want to show only 1 or 3 (or however many) of the available
// storylets instead of all of them, I think that's better handled in the
// presentation layer (randomly select a few choices instead of showing all
// of them), though I've seen people do it purely in Ink with lists.
+ {driving && location != "home"} [Home] -> home
+ {driving && location != "fairweather"} [Fairweather Farm] -> fairweather
+ {driving && location != "anderson"} [Anderson Farm] -> anderson
+ {driving && location != "docks"} [Docks] -> docks
== home ==
~ location = "home"
Where the heart is.
+ [Drive] -> car
== fairweather ==
~ location = "fairweather"
Organic vegetables, eggs from happy hens.
+ [Drive] -> car
== anderson ==
~ location = "anderson"
Meat and dairy products for three generations.
+ [Drive] -> car
== docks ==
~ location = "docks"
A bustling wharf.
+ [Drive] -> car
== car ==
You get behind the wheel.
~ driving = true
<- choose_storylet
~ driving = false
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment