JSFiddle - React, Tailwind, and code Playground
by Gwyn Milcote
HTML
<div id='cyoa-container'>
<div id='cyoa-topbar'>
<div><i>Scene #<span id='cyoa-scene-id'></span></i></div>
<div>
<b><div id='cyoa-game-title'>[Game title here]</div></b>
<i><div id='cyoa-game-author'>by Author</div></i>
</div>
<div id='cyoa-restart'><i>Restart?</i></div>
</div>
<hr>
<div id='cyoa-image'></div>
<div id='cyoa-text'></div>
<div id='cyoa-choices'></div>
<hr>
<div id='cyoa-stats'></div>
</div>
<!--
<p>Simple CYOA game, in vanilla JS. The <b>db</b> object could be an actual database (add AJAX requests to load from server) or included as own .js file, for each game. The <b>CYOA</b> class contains the logic, and can be shared across many games.</p>
<p><b>Tip:</b> use a free flowchart tool like <a href='https://app.diagrams.net/'>this</a> to map out scenes, where each choice goes, and which stats get checked or updated between scenes. Or you might find it hard to keep track of ID numbers!</p>
<hr>
<p>A <b>scene</b> must have some text, and at least 1 choice leading away from it. Optionally, you can set an URL for an image to display.</p>
<p>The only exceptions are 'game over' scenes. They don't need any choices.</p>
<ul>
<li><b>id</b> - The ID number, for looking up this scene.</li>
<li><b>text</b> - The text that player sees.</li>
<li><b>image</b> - (optional) URL of an image to display in the #scene_image div.</li>
<li><b>result</b> - (optional) What to do with the value that was passed here from the previous choice. See below.</li>
<li><b>effects</b> - (optional) Array of actions to do, like changing player's stats. See below.</li>
<li><b>gameOver</b> - (optional) Only include this property for game over scenes. Doesn't matter what the value is; just its presence will count.</li>
</ul>
<p><b>result</b> is a single object that tells what to do with the previous choice's value.</p>
<ul>
<li><b>{stat: "hp",...
CSS
div, img, span, button, hr {
box-sizing: border-box;
}
/* Variables */
:root {
--gold_vdark: #635B4D;
--gold_dark: #BAA47D;
--gold_medium: #DEC497;
--gold_light: #F5E8D2;
}
#cyoa-container {
background-color: var(--gold_light);
padding: 1.5em;
}
#cyoa-topbar {
display: flex;
justify-content: space-between;
}
#cyoa-restart {
cursor: pointer;
}
#cyoa-container hr {
border: 1px solid var(--gold_dark);
margin: 20px auto;
}
#cyoa-image {
display: none;
border: 1px solid var(--gold_dark);
border-radius: 2px;
background-size: cover;
background-position: center;
margin: 20px auto;
}
#cyoa-text {
margin: 20px auto;
}
#cyoa-choices .choice {
padding: 5px 10px;
border: 1px solid var(--gold_dark);
background-color: var(--gold_medium);
transition: 0.2s;
margin: 5px auto;
border-radius: 2px;
}
#cyoa-choices .choice:hover {
cursor: pointer;
background-color: white;
transition: 0.2s;
}
#cyoa-stats {
display: flex;
flex-flow: row wrap;
justify-content: space-between;
}
#cyoa-stats div {
width: calc(33.33% - 5px);
margin: 5px 0px;
border: 1px solid var(--gold_dark);
background-color: var(--gold_medium);
padding: 5px 10px;
border-radius: 2px;
}
#cyoa-game-title, #cyoa-game-author {
text-align: center;
}
JavaScript
let db2 = {
settings: {
title: "Griffin CYOA Example",
author: "Gwyn Milcote",
storageName: "milcote_cyoa",
saveLocally: true
},
scenes: [
{
id: 1,
text: "You're a griffin. What colour are your feathers?"
},{
id: 2,
text: "Of course! Your feathers are [RESULT]. Can you fly?",
result: {stat: "colour", type: "set"},
},{
id: 3,
text: "[RESULT] Now, how about magic?",
result: {stat: "flight", type: "set"},
},{
id: 4,
text: "[RESULT] So that's your magic skill.",
result: {stat: "magic", type: "set"}
},{
id: 5,
text: "Magic isn't everyone's cup of tea. It's fine. Did you know that a lack of magic makes you very physically strong instead?",
effects: [
{stat: "magic", type: "set", value: 3},
{stat: "strength", type: "set", value: 15}
]
},{
id: 6,
text: "Let's decide your role. What do you do in your flock?",
},{
id: 7,
text: "Oh dear, a solitary griffin! That's unusual. May I ask the reason...?",
result: {stat: "role", type: "set"}
},{
id: 8,
text: "[RESULT]<br>Let's move on.",
},{
id: 9,
text: "I see! [RESULT] Very cool!<p>This scene also gives you +5 flight and +2 magic, just because. Did you notice that in the stats below?</p>Now let's try a status check. You'll go to scene 10 if successful, or scene 11 if it fails.",
result: {stat: "role", type: "set"},
effects: [
{stat: "magic", type: "increase", value: 2},
{stat: "flight", type: "increase", value: 5}
]
},{
id: 10,
text: "You have at least 10 strength! Yay! You might have slayed a...