### Scene Navigation with SceneNavigator
Source: https://github.com/dfabulich/choicescript/blob/main/doc/index.html
Configure scene transitions and starting statistics for your game using SceneNavigator in mygame.js. This example sets up the order of scenes and initial player stats.
```javascript
// Specify the list of scenes here, separated by commas, with no final comma
nav = new SceneNavigator([
"startup"
,"animal"
,"variables"
,"ending"
,"death"
]);
// Specify the default starting stats here
stats = {
leadership: 50
,strength: 50
};
// Specify the stats to use in debug mode
debugStats = {
leadership: 50
,strength: 50
};
// or just use defaults
// debugStats = stats
```
--------------------------------
### Display Game Menu
Source: https://github.com/dfabulich/choicescript/blob/main/web/mygame/index.html
Starts the loading process for the game. This function should be called when the game begins loading.
```javascript
startLoading();
```
--------------------------------
### Legacy Game Configuration with SceneNavigator (mygame.js)
Source: https://context7.com/dfabulich/choicescript/llms.txt
Older ChoiceScript games used `mygame.js` for scene order and starting stats. Modern games prefer `startup.txt` with `*scene_list` and `*create`.
```javascript
// Legacy mygame.js approach (still supported):
nav = new SceneNavigator([
"startup",
"forest",
"castle",
"ending",
"death"
]);
stats = {
leadership: 50,
strength: 50,
gold: 0
};
debugStats = {
leadership: 99,
strength: 99,
gold: 999
};
```
--------------------------------
### Initialize Editor on Load
Source: https://github.com/dfabulich/choicescript/blob/main/editor/index.html
Sets up the editor when the window loads. It gets references to the textarea and highlight elements, binds necessary events, and performs an initial size adjustment.
```javascript
window.onload = function() {
window.text = document.getElementById("t");
window.highlight = document.getElementById("highlight");
bindEvent(text, "keydown", keyDown);
bindEvent(text, "change", change);
bindEvent(text, "keypress", change);
bindEvent(text, "keyup", change);
change();
}
```
--------------------------------
### Initialize Scene Navigator
Source: https://github.com/dfabulich/choicescript/blob/main/quicktest.html
Initializes the SceneNavigator for game testing. Sets the starting scene and an empty object for initial stats.
```javascript
nav = new SceneNavigator(["startup"])
nav.setStartingStatsClone({});
stats = {};
```
--------------------------------
### Start Randomtest Button Handler
Source: https://github.com/dfabulich/choicescript/blob/main/randomtest.html
Handles the click event for the 'Start Randomtest' button. Gathers configuration from form elements, parses URL arguments, and prepares for test execution. Includes logic for file uploads if running locally.
```javascript
var clicked = false;
var autoScroll;
document.getElementById("start").onclick = function() {
if (clicked) return;
clicked = true;
iterations = document.getElementById("iterations").value;
randomSeed = document.getElementById("seed").value*1;
var avoidUsedOptions = document.getElementById("avoidUsedOptions").checked;
var showText = document.getElementById("showText").checked;
var showCoverage = document.getElementById("showCoverage").checked;
var showChoices = document.getElementById("showChoices").checked;
highlightGenderPronouns = document.getElementById("highlightGenderPronouns").checked;
var recordBalance = document.getElementById("recordBalance").checked;
autoScroll = document.getElementById("autoScroll").checked;
var isFile = "file:" === window.location.protocol;
if (isFile) {
var numFiles = uploadDirectory.files.length;
var startupCandidates = [];
var randomtestJsCandidates = [];
var relativePathMap = {};
for (var i = 0; i < numFiles; i++) {
var file = uploadDirectory.files[i];
r
```
--------------------------------
### ChoiceScript Indentation Example
Source: https://github.com/dfabulich/choicescript/blob/main/doc/index.html
Demonstrates correct and incorrect indentation in ChoiceScript. Indentation is mandatory and must be consistent for nested choices.
```choicescript
*choice
#Hold 'em.
He calls; you win!
*finish
#Fold 'em.
Better luck next time.
*finish
```
--------------------------------
### Get user input with *input_text in ChoiceScript
Source: https://github.com/dfabulich/choicescript/blob/main/doc/more.html
Use *input_text to provide a text box for the user to specify a variable's value, such as their name.
```choicescript
Please enter your name.
*input_text name
Your name is ${name}
```
--------------------------------
### Get Player Text Input with *input_text
Source: https://context7.com/dfabulich/choicescript/llms.txt
Use *input_text to display a text input box and store the player's response in a named variable. Allows players to enter freeform text like names or titles.
```choicescript
Before your adventure begins, tell us your name.
*input_text hero_name
And what is your title?
*input_text hero_title
Welcome, ${hero_title} ${hero_name}! Your legend starts today.
*finish
```
--------------------------------
### Determine Root and Image Directories
Source: https://github.com/dfabulich/choicescript/blob/main/quicktest.html
Uses the 'startup.txt' file's path to create regular expressions for identifying scene files and image files within the project directory.
```javascript
var startup = startupCandidates[0]; var rootDirTest = new RegExp("^" + startup.webkitRelativePath.replace(/\\/startup.txt$/, "/\\S+$")); var imageDir = startup.webkitRelativePath.replace(/\\/[^/]+\\//startup.txt$/, "/"); var imageDirTest = new RegExp("^" + imageDir);
```
--------------------------------
### Validate and Collect Candidate Files
Source: https://github.com/dfabulich/choicescript/blob/main/quicktest.html
Iterates through selected input files to identify 'startup.txt' and 'quicktest.html'. This is a prerequisite for the quicktest functionality.
```javascript
var numFiles; i++) { var file = input.files[i]; if (file.name === "startup.txt") { startupCandidates.push(file); } if (file.name === "quicktest.html") { quicktestHtmlCandidates.push(file); } }
```
--------------------------------
### Get Editor Content
Source: https://github.com/dfabulich/choicescript/blob/main/editor/index.html
Retrieves the current text content from the editor's textarea element.
```javascript
function getContent() {
return text.value;
}
```
--------------------------------
### Initialize Game and Load Content
Source: https://github.com/dfabulich/choicescript/blob/main/editor/blankgame.html
Sets up the game environment on page load, retrieves content from the editor, and initializes debugging if enabled.
```javascript
window.version="DEVELOPMENT"
window.alreadyLoaded = true;
window.onload = function() {
window.main = document.getElementById("main");
var editor = window.opener;
window.debug = false;
try {
debug = editor.document.getElementById('debug').checked;
} catch (e) {}
loadLines(editor.getContent(), debug);
};
```
--------------------------------
### Startup File Configuration in ChoiceScript
Source: https://context7.com/dfabulich/choicescript/llms.txt
Defines game title, author, scene order, and initializes persistent variables. Use `*create` for variables that persist across all scenes.
```choicescript
*title My Adventure Game
*author Jane Smith
*scene_list
startup
forest
castle
ending
death
*create leadership 50
*create strength 50
*create gold 0
*create hero_name "Unnamed Hero"
Welcome, adventurer! Your journey begins now.
*page_break
What is your name?
*input_text hero_name
Hello, ${hero_name}! Your adventure begins.
*finish
```
--------------------------------
### Initialize Store
Source: https://github.com/dfabulich/choicescript/blob/main/quicktest.html
Initializes the store functionality. Returns false, indicating that store initialization failed or is not supported.
```javascript
function initStore() {
return false;
}
```
--------------------------------
### Get Content from Editor
Source: https://github.com/dfabulich/choicescript/blob/main/editor/contenteditable.html
Retrieves the raw text content from the contenteditable div, converting HTML elements like `
` and entities back into their plain text representations.
```javascript
function getContent() {
var ih = text.innerHTML;
ih = ih.replace(/<\/?div>/g, "");
ih = ih.replace(/
/g, "\n");
ih = ih.replace(/>/g, ">");
ih = ih.replace(/</g, "<");
ih = ih.replace(/&/g, "&");
return ih;
}
```
--------------------------------
### Add Initial Game Files
Source: https://github.com/dfabulich/choicescript/blob/main/quicktest.html
Adds essential ChoiceScript game files to the list of known scenes.
```javascript
addFile("startup.txt");
addFile("choicescript_stats.txt");
addFile("choicescript_screenshots.txt");
addFile("choicescript_upgrade.txt");
```
--------------------------------
### Evaluate Candidate Files for Uniqueness
Source: https://github.com/dfabulich/choicescript/blob/main/quicktest.html
Checks if the expected number of candidate files (e.g., 'startup.txt', 'quicktest.html') were found and if there are any duplicates. Displays alerts for errors and throws an exception.
```javascript
function evaluateCandidates(fileName, candidates) { if (!candidates.length) { alert("We couldn't find "+fileName+" in the folder you chose. Please try again. (Note that quicktest requires access to the entire choicescript directory, not just the mygame folder"); clicked = false; throw new Error(); } if (candidates.length > 1) { alert("There were multiple files called "+fileName+" in the folder you chose. Please try again.\n" + candidates.map(function(file) {return "\u2022 " + file.webkitRelativePath}).join("\n")); clicked = false; throw new Error(); } }
```
--------------------------------
### Generate random numbers with *rand in ChoiceScript
Source: https://github.com/dfabulich/choicescript/blob/main/doc/more.html
Use *rand to set a variable to a random number within a specified minimum and maximum range. For example, *rand die_roll 1 6 sets die_roll to a value between 1 and 6.
```choicescript
*rand die_roll 1 6
```
--------------------------------
### Initialize Game Variables
Source: https://github.com/dfabulich/choicescript/blob/main/web/mygame/index.html
Sets initial game variables like version and store name. The store name is disabled by default.
```javascript
window.version="UNKNOWN" // INSERT store name; disabled by default because it's confusing for newbie authors window.storeName = null; //Scene.generatedFast = true; var rootDir = "../";
```
--------------------------------
### Initialize and Test ChoiceScript
Source: https://github.com/dfabulich/choicescript/blob/main/editor/index.html
This function attempts to run the current ChoiceScript content in a new window. It includes error handling to highlight the line where an error occurred.
```javascript
function tryIt() {
window.highlight.style.display = "none";
try {
autotester(getContent(), null, "startup");
window.open("blankgame.html", "choicescript_testWindow");
} catch (e) {
console.log(e);
console.log(e.stack);
var x = e;
if (e.message) {
var match = e.message.match(/^line (\\d+)/);
if (match) {
highlightLine(match[1]);
}
}
alert(x);
}
}
```
--------------------------------
### Implement Player Choices in ChoiceScript
Source: https://github.com/dfabulich/choicescript/blob/main/web/mygame/scenes/variables.txt
Use the '*choice' command to present players with options. Each option ('#') can lead to different game paths or variable changes.
```choicescript
What do you prefer?
*choice
#Leadership
*set leadership +10
*goto action
#Strength
*set strength +10
```
--------------------------------
### Automated Testing with quicktest and randomtest
Source: https://context7.com/dfabulich/choicescript/llms.txt
ChoiceScript provides `quicktest` for syntax and logic errors, and `randomtest` for runtime errors in probabilistic paths by running thousands of playthroughs.
```bash
# Run quicktest via Node.js:
node quicktest.js web/
# Run randomtest (1000 random playthroughs):
node randomtest.js web/
# Via the browser: open quicktest.html or randomtest.html
# On macOS: double-click quicktest.command or randomtest.command
# On Windows: run run-quicktest.bat or run-randomtest.bat
# Autotest a single scene via Apache Ant:
ant autotest -Dvig=startup
```
--------------------------------
### Local Development Tools Check
Source: https://github.com/dfabulich/choicescript/blob/main/web/mygame/index.html
Checks if the game is running on localhost and dynamically loads testing and compilation links if available. It fetches links to ensure they are valid before displaying them.
```javascript
if (window.location.hostname === 'localhost') { const links =
This game requires JavaScript; please enable JavaScript and refresh this page.
``` -------------------------------- ### Try It Functionality Source: https://github.com/dfabulich/choicescript/blob/main/editor/contenteditable.html Attempts to run the autotester with the current content and opens a new window for testing. It includes error handling to catch and highlight specific line errors. ```javascript function tryIt() { if (window.highlighted) { highlighted.style.backgroundColor = "#ccc"; } try { autotester(getContent()); window.open("blankgame.html", "choicescript_testWindow"); } catch (e) { var x = e; if (e.message) { var match = e.message.match(/^line (\d+)/); if (match) { highlightLine(match[1]); } } alert(x); } } ``` -------------------------------- ### ChoiceScript Goto and Label Navigation Source: https://github.com/dfabulich/choicescript/blob/main/doc/index.html Illustrates how to use '*goto' to jump to a '*label' within a ChoiceScript scene for non-linear navigation. ```choicescript *choice #Lion *goto claws #Tiger *label claws In that case, you'll have powerful claws and a mighty roar! *finish #Elephant Well, elephants are interesting animals, too. *finish ``` -------------------------------- ### Using Gosub for Subroutines in ChoiceScript Source: https://github.com/dfabulich/choicescript/blob/main/web/mygame/scenes/gosub.txt Demonstrates how to use the *gosub command to call a subroutine named 'feelings' from different choice outcomes. The *return command within the subroutine brings execution back to the point after the *gosub call. ```ChoiceScript *choice #Stand up to him. He's a lot tougher than you are; you hit him, but he barely even notices. *gosub feelings He beats you up and takes your lunch money. *finish #Give him my lunch money. He takes your money and laughs at you. *gosub feelings He leaves you alone for the rest of the week. *finish *label feelings How do you feel about this? *choice #Angry. Maybe you can get even some day. *return #Sad. You do your best, but you think he can probably see your shame. *return ``` -------------------------------- ### Set Initial Variables in ChoiceScript Source: https://github.com/dfabulich/choicescript/blob/main/web/mygame/scenes/variables.txt Use the '*set' command to initialize variables at the beginning of a ChoiceScript game. These variables can track player stats or game state. ```choicescript *set leadership 10 *set strength 10 ``` -------------------------------- ### ChoiceScript Basic Scene Structure Source: https://github.com/dfabulich/choicescript/blob/main/doc/index.html Demonstrates the fundamental `*choice` and `*finish` commands to create branching narratives. Indentation is crucial for defining choices and their outcomes. ```choicescript Your majesty, your people are starving in the streets, and threaten revolution. Our enemies to the west are weak, but they threaten soon to invade. What will you do? *choice #Make pre-emptive war on the western lands. If you can seize their territory, your kingdom will flourish. But your army's morale is low and the kingdom's armory is empty. How will you win the war? *choice #Drive the peasants like slaves; if we work hard enough, we'll win. Unfortunately, morale doesn't work like that. Your army soon turns against you and the kingdom falls to the western barbarians. *finish #Appoint charismatic knights and give them land, peasants, and resources. Your majesty's people are eminently resourceful. Your knights win the day, but take care: they may soon demand a convention of parliament. *finish #Steal food and weapons from the enemy in the dead of night. A cunning plan. Soon your army is a match for the westerners; they choose not to invade for now, but how long can your majesty postpone the inevitable? *finish #Beat swords to plowshares and trade food to the westerners for protection. The westerners have you at the point of a sword. They demand unfair terms from you. *choice #Accept the terms for now. Eventually, the barbarian westerners conquer you anyway, destroying their bread basket, and the entire region starves. *finish #Threaten to salt our fields if they don't offer better terms. They blink. Your majesty gets a fair price for wheat. *finish #Abdicate the throne. I have clearly mismanaged this kingdom! The kingdom descends into chaos, but you manage to escape with your own hide. Perhaps in time you can return to restore order to this fair land. *finish ``` -------------------------------- ### Scene Initialization and Execution Source: https://github.com/dfabulich/choicescript/blob/main/editor/blankgame.html Defines a function to load game lines into a new scene and safely execute it, setting the initial scene name. ```javascript function loadLines(lines, debug) { window.stats.sceneName = "startup"; var scene = new Scene("startup", window.stats, window.nav, debug); safeCall(scene, scene.execute); } ``` -------------------------------- ### Initialize Editor and Bind Events Source: https://github.com/dfabulich/choicescript/blob/main/editor/contenteditable.html Sets up the editor environment on page load, including initializing variables, printing line numbers, setting margins, and binding various input events to the contenteditable area. ```javascript window.onload = function() { window.text = document.getElementById("t"); window.numbers = document.getElementById("numbers"); window.numberMask = document.getElementById("numberMask"); window.isWebKit = /WebKit/.test(navigator.userAgent) currentLines = 1; var lineNumbers = 1500; printLineNumbers(lineNumbers); text.style.marginLeft = (numbers.offsetWidth + 2) + "px"; bindEvent(text, "keydown", keyDown); bindEvent(text, "change", change); bindEvent(text, "keypress", change); bindEvent(text, "keyup", change); change(); } ``` -------------------------------- ### Initialize Upload Prompt Source: https://github.com/dfabulich/choicescript/blob/main/randomtest.html Conditionally displays the upload prompt if the page is loaded from a local file. ```javascript if ("file:" === window.location.protocol) { document.getElementById("uploadPrompt").style.display = ""; } ``` -------------------------------- ### Subroutine Calls with *gosub and *return Source: https://context7.com/dfabulich/choicescript/llms.txt Implement subroutines by calling a labeled block with *gosub and resuming after the call with *return. This helps in avoiding duplicated code passages across different narrative branches. ```choicescript The school bully confronts you. *choice #Stand up to him. He barely flinches. He beats you up. *gosub feelings He takes your lunch money anyway. *finish #Give him my lunch money. He laughs at you. *gosub feelings He leaves you alone for the rest of the week. *finish *label feelings How do you feel about this? *choice #Angry. Maybe you can get even someday. *return #Sad. You hold back tears as best you can. *return ``` -------------------------------- ### Scene Navigation with *goto and *label Source: https://context7.com/dfabulich/choicescript/llms.txt Use *label to mark points in a scene and *goto to jump execution to those labels, useful for merging branches or avoiding code repetition within the same scene. ```choicescript What kind of animal will you be? *choice #Lion *goto claws #Tiger *label claws You have powerful claws and a mighty roar! *finish #Elephant Elephants are wise and powerful in their own way. *finish *label battle_end You survived the encounter. *finish ``` -------------------------------- ### Variable Declaration with *create and *temp in ChoiceScript Source: https://context7.com/dfabulich/choicescript/llms.txt Use `*create` in `startup.txt` for persistent variables and `*temp` in scene files for temporary variables. Variables can be numbers, strings (quoted), or booleans. ```choicescript *comment In startup.txt — persistent variables: *create gold 100 *create player_name "Hero" *create is_knight false *comment In any scene file — temporary variable: *temp roll 0 *rand roll 1 20 *if roll >= 15 You rolled ${roll} — a critical success! *finish *else You rolled ${roll} — better luck next time. *finish ``` -------------------------------- ### Scene Loading Logic Source: https://github.com/dfabulich/choicescript/blob/main/quicktest.html Handles the loading of scenes based on the provided game data and scene number. Includes logic for handling local file loading and checks for uploaded files. ```javascript var uncoveredScenes = []; var success = true; var gameTitle, authorIncluded; var gotoSceneLabels = {}; function loadScenes(game, sceneNum) { if (!game) game = {}; var fileName = knownScenes[sceneNum]; var sceneName = fileName.replace(/.txt$/, ""); var skip = false; var loadFailed = false; var sceneText; if ("file:" === window.location.protocol) { if (!window.uploadedFiles) { document.body.innerHTML = "Please \"upload\" your choicescript folder (including quicktest.html).
"; var input = document.createElement("input"); input.type = 'file'; input.webkitdirectory = true; input.multiple = true; input.addEventListener('change', function searchForStartup(e) { var numFiles = input.files.length; var startupCandidates = []; var quicktestHtmlCandidates = []; for (var i = 0; i < ``` -------------------------------- ### Show Full Screen Advertisement Button Source: https://github.com/dfabulich/choicescript/blob/main/quicktest.html Displays a full-screen advertisement button and executes a callback function upon interaction. In this implementation, the callback is always executed immediately. ```javascript showFullScreenAdvertisementButton = function(message, callback) { callback(); } ``` -------------------------------- ### ChoiceScript Quicktest Core JavaScript Logic Source: https://github.com/dfabulich/choicescript/blob/main/quicktest.html This comprehensive JavaScript code defines the core functions, `loadScenes` and `testScene`, which orchestrate the entire quicktest process. It manages file loading, scene parsing, error handling, and generates reports on code coverage, metadata compliance, and achievement usage. ```javascript meters; print warning? } else { addFile(match[1]+".txt"); if (match[2] !== "") { if (!gotoSceneLabels[match[1]]) gotoSceneLabels[match[1]] = []; gotoSceneLabels[match[1]].push({origin:sceneName, originLine:j, label:match[2]}); } } } } } else if (command == "save_game") { if (data !== null) addFile(data+".txt"); } else if (command == "scene_list" && sceneNum === 0) { var parsedSceneList = parseSceneList(sceneLines, j); j = parsedSceneList.lineNum; for (var k = 0; k < parsedSceneList.scenes.length; k++) { addFile(parsedSceneList.scenes[k]+".txt"); sceneList.push(parsedSceneList.scenes[k]); } } } } catch (e) { console.log("Error parsing "+sceneName+" line "+(j+1)+"; we'll flag the error in detail later\n"); console.log(e); } } if (++sceneNum < knownScenes.length) return setTimeout(function(){loadScenes(game, sceneNum)}, 0); testScene(game, 0); } function testScene(game, sceneNum, progressCallback) { var fileName = knownScenes[sceneNum]; var sceneName = fileName.replace(/.txt$/, ""); var sceneText = game[fileName]; var skip = false; if (!sceneText) { if (/^choicescript_(stats|upgrade|screenshots)\\.txt$/.test(fileName)) { skip = true; } else { doneLoading(); var errorMessage = "ERROR: couldn't open " + fileName; if (progressCallback) progressCallback(sceneNum, knownScenes.length, errorMessage); console.log("QUICKTEST FAILED"); console.log(errorMessage); success = false; return; } } if (!skip) { console.log(sceneName); try { var uncovered = autotester(sceneText, nav, sceneName, gotoSceneLabels[sceneName])[1]; if (uncovered) { uncoveredScenes.push({name:sceneName, lines:uncovered}); } if (sceneNum === 0) { var match = /^\\\*title (.*)/m.exec(sceneText); if (match) { gameTitle = match[1]; } authorIncluded = /^\\\*author /m.test(sceneText); } } catch (x) { if (progressCallback) progressCallback(sceneNum, knownScenes.length, x); console.log("QUICKTEST FAILED"); console.log(x); console.error(x.stack); success = false; return; } } ++sceneNum; if (progressCallback) progressCallback(sceneNum, knownScenes.length, !"error"); if (sceneNum < knownScenes.length) { return setTimeout(function(){testScene(game, sceneNum, progressCallback)}, 0); } if (success) { var allLinesTested = true; for (var i = 0; i < uncoveredScenes.length; i++) { allLinesTested = false; var uncoveredScene = uncoveredScenes[i]; for (var j = 0; j < uncoveredScene.lines.length; j++) { console.log("UNTESTED " + uncoveredScene.name + " " + uncoveredScene.lines[j]); }; } (function() { if (nav.achievementList && nav.achievementList.length) { for (var i = 0; i < nav.achievementList.length; i++) { var name = nav.achievementList[i]; if (!nav.achieved[name]) { console.log("UNUSED achievement: " + name); } } } })(); if (!allLinesTested) console.log("SOME LINES UNTESTED"); if (typeof gameTitle === "undefined") { console.log("MISSING \*TITLE COMMAND"); } else if (gameTitle.length > 30) { console.log("TITLE TOO LONG (" + gameTitle.length + " out of 30 characters): " + gameTitle); } if (!authorIncluded) console.log("MISSING \*AUTHOR COMMAND"); for (var i = 0; i < warnings.length; i++) { console.log(warnings[i]); } console.log("QUICKTEST PASSED"); } } Loading... if (!window.location.search) loadScenes(null, 0); ``` -------------------------------- ### Custom Scene Loading Logic Source: https://github.com/dfabulich/choicescript/blob/main/editor/blankgame.html Extends the Scene prototype to handle custom loading logic, particularly for the 'startup' scene, and integrates with the editor's content. ```javascript Scene.prototype.oldLoadScene = Scene.prototype.loadScene; Scene.prototype.loadScene = function () { if (this.name == "startup") { this.loadLines(window.opener.getContent()); this.debugMode = window.debug; if (this.executing) { doneLoading(); this.execute(); } } else { this.oldLoadScene(); } } ``` -------------------------------- ### Evaluate Game File Candidates Source: https://github.com/dfabulich/choicescript/blob/main/randomtest.html Evaluates lists of candidate files for specific game files like 'randomtest.js' and 'startup.txt'. It alerts the user if no files are found or if multiple files with the same name exist, preventing ambiguous test runs. ```javascript elativePathMap[file.webkitRelativePath] = file; if (file.name === "startup.txt") { startupCandidates.push(file); } if (file.name === "randomtest.js") { randomtestJsCandidates.push(file); } } function evaluateCandidates(fileName, candidates) { if (!candidates.length) { alert("We couldn't find "+fileName+" in the folder you chose. Please try again. (Note that randomtest requires access to the entire choicescript directory, not just the mygame folder"); clicked = false; throw new Error(); } if (candidates.length > 1) { alert("There were multiple files called "+fileName+" in the folder you chose. Please try again.\n" + candidates.map(function(file) {return "\u2022 " + file.webkitRelativePath}).join("\n")); clicked = false; throw new Error(); } } ``` -------------------------------- ### Println Placeholder Source: https://github.com/dfabulich/choicescript/blob/main/quicktest.html Placeholder function for println. Currently does nothing. ```javascript function println() {} ``` -------------------------------- ### Verify Scene File in Browser Source: https://github.com/dfabulich/choicescript/blob/main/quicktest.html Extends the Scene prototype to verify scene files in a web environment by adding them to the known scenes list. ```javascript Scene.prototype.verifySceneFile = function webVerifySceneFile(name) { addFile(name+".txt"); } ``` -------------------------------- ### Jumping to a scene with *goto_scene Source: https://github.com/dfabulich/choicescript/blob/main/doc/index.html Use *goto_scene to immediately jump to a different scene. This is useful for creating common outcomes like death or failure without repeating code. ```choicescript #Lift weights *if strength > 15 You lift the weights. *finish You drop the weights and hurt yourself badly. You never recover. *goto_scene death ``` -------------------------------- ### Process Scene Commands with Parameters Source: https://github.com/dfabulich/choicescript/blob/main/quicktest.html Extracts commands and their associated data from scene files. This snippet specifically handles commands like 'goto_scene' or 'gosub_scene' when they have parameters. ```javascript var sceneLines = sceneText.split("\n"); for (var j = 0; j < sceneLines.length; j++) { var line = sceneLines[j]; var words; var result = /^\s*\*(\w+)(.*)/.exec(line); if (!result) continue; var command = result[1].toLowerCase(); var data = trim(result[2]); if (command == "goto_scene" || command == "gosub_scene") { if (data === null) data = ""; if (/[\[\{\]/.test(data)) { // print warning? } else { var match = (\S+)\s*(\S*)\s*(.*)/.exec(data); if (match) { if (match[3] !== "") { // para ``` -------------------------------- ### Cross-Scene Navigation with *goto_scene Source: https://context7.com/dfabulich/choicescript/llms.txt Transfer execution to a different scene file using *goto_scene. This is ideal for shared outcomes like death sequences or reusable story segments. ```choicescript *comment In variables.txt: *if strength <= 5 You drop the weights and injure yourself fatally. *goto_scene death *comment In death.txt: You have died. *goto_scene ending *comment In ending.txt: This is the last scene! The game is over. *ending ``` -------------------------------- ### Compile ChoiceScript Game with compile.js Source: https://context7.com/dfabulich/choicescript/llms.txt The `compile.js` script bundles game scenes and assets into a single HTML file for distribution. Can be run via Node.js or a browser interface. ```bash # Command-line compilation with Node.js: # Usage: node compile.js [output.html] [game-directory/] node compile.js my-game.html web/ # On macOS, double-click compile.command (opens in browser for folder upload) # On Windows, run run-compile.bat # Output: my-game.html — a fully standalone game file # Expected output: Generated /path/to/my-game.html ``` -------------------------------- ### ChoiceScript Variable Declaration and Setting Source: https://github.com/dfabulich/choicescript/blob/main/doc/index.html Shows how to declare a temporary variable using '*temp' and set its initial value using '*set'. Temporary variables only last for the current scene. ```choicescript *temp leadership *set leadership 20 ``` -------------------------------- ### Compile and Finish Game Export Source: https://github.com/dfabulich/choicescript/blob/main/compile.html Compiles the ChoiceScript game and then creates an HTML file for download. It generates a Blob URL for the compiled content and provides a link for the user to download the game, named based on the compiled result's title. ```javascript function finish(compiledResult) { var blob = new Blob([compiledResult.content], {type: "text/html"}); var bloburl = URL.createObjectURL(blob); var title = compiledResult.title || 'output'; var fileName = title.replace(/[^a-zA-Z0-9\-]/g, '_') + '.html'; console.log("Export Complete!"); document.body.insertAdjacentHTML("beforeend", "Click here to download your compiled game."); } var compiledResult = compile(); if (compiledResult) finish(compiledResult); ``` -------------------------------- ### Initialize Randomtest Worker Source: https://github.com/dfabulich/choicescript/blob/main/randomtest.html Initializes the randomtest worker with game configuration and content. This code path is used when the game files are not uploaded as a directory, but rather individually. ```javascript else { document.body.innerHTML = ""; var worker = new Worker("randomtest.js"); worker.onmessage = function(x) { var msg = x.data.msg; messages.push(msg); console.log(msg); } worker.postMessage({iterations:iterations, randomSeed:randomSeed, showText:showText, showCoverage:showCoverage, highlightGenderPronouns:highlightGenderPronouns, showChoices:showChoices, avoidUsedOptions:avoidUsedOptions, recordBalance:recordBalance}); } ``` -------------------------------- ### Load Scene Files and Images Source: https://github.com/dfabulich/choicescript/blob/main/quicktest.html Reads scene files, processes them into text, and stores them in `uploadedFiles`. It also identifies and stores image file information. ```javascript var sceneFiles = []; for (var i = 0; i < numFiles; i++) { var file = input.files[i]; if (rootDirTest.test(file.webkitRelativePath)) { sceneFiles.push(file); } else if (imageDirTest.test(file.webkitRelativePath)) { knownImages[file.name] = 1; knownImages[file.webkitRelativePath.replace(imageDir, "")] = 1; } } var _uploadedFiles = {}; document.body.innerHTML = ""; Promise.all([].map.call(sceneFiles, function(file) { return new Response(file).text();})).then(function(results) { for (var i = 0; i < sceneFiles.length; i++) { var file = sceneFiles[i]; _uploadedFiles[file.name] = results[i]; } window.uploadedFiles = _uploadedFiles; loadScenes(game, sceneNum); }); }); document.body.appendChild(input); return; ``` -------------------------------- ### Dynamic Label Navigation Source: https://github.com/dfabulich/choicescript/blob/main/doc/more.html Use `*gotoref` to navigate to a label specified by its name stored in a variable. Note: This feature is currently not implemented. ```choicescript *gotoref "claws" ``` -------------------------------- ### Parse Scene List Commands Source: https://github.com/dfabulich/choicescript/blob/main/quicktest.html Parses lines within a scene file to identify scene transitions (goto_scene, gosub_scene) and potential purchase commands. It handles indentation and extracts scene names and product types. ```javascript function parseSceneList(lines, lineNum) { var nextIndent = null; var scenes = []; var purchases = {}; var line; while(typeof (line = lines[++lineNum]) != "undefined") { if (!line.trim()) continue; var indent = /^(\s*)/.exec(line)[1].length; if (nextIndent === null || nextIndent === undefined) { // initialize nextIndent with whatever indentation the line turns out to be // ...unless it's not indented at all if (indent === 0) throw new Error("invalid scene_list indent, expected at least one row"); this.indent = nextIndent = indent; } if (indent === 0) break; if (indent != this.indent) { // all scenes are supposed to be at the same indentation level throw new Error("invalid scene_list indent, expected "+this.indent+", was " + indent); } line = line.trim(); var purchaseMatch = /^\$(\w*)\s+(.*)/.exec(line); if (purchaseMatch) { line = purchaseMatch[2]; var product = purchaseMatch[1].trim() || "adfree"; purchases[line] = product; } if (!scenes.length && "startup" != line) scenes.push("startup"); scenes.push(line); } return {scenes:scenes, purchases:purchases, lineNum:lineNum-1}; } ``` -------------------------------- ### Clear Screen with Callback Source: https://github.com/dfabulich/choicescript/blob/main/quicktest.html Clears the screen by executing a provided callback function. This is used to reset the display before rendering new content. ```javascript clearScreen = function clearScreen(code) { code.call(); }; ``` -------------------------------- ### Check Registration Status Source: https://github.com/dfabulich/choicescript/blob/main/quicktest.html Returns false, indicating that registration is not supported or not applicable. ```javascript isRegistered = function() {return false;}; isRegisterAllowed = function() {return false;}; isRestorePurchasesSupported = function() {return false;}; isFullScreenAdvertisingSupported = function() {return false;}; areSaveSlotsSupported = function() {return false;}; printDiscount = function() {}; isFullScreenAdvertisingSupported = function() {return false;}; isAdvertisingSupported = function() {return false;}; isPrerelease = function() {return false;}; ``` -------------------------------- ### Basic CSS for Editor Source: https://github.com/dfabulich/choicescript/blob/main/editor/index.html Provides fundamental styling for the editor's body, line numbers, and the main textarea. ```css body { margin: 0; padding: 0; font-family: monospace; font-size: 10pt; background-color: #f0f0f0; } #numbers { background-image: url('line-numbers.png'); background-repeat:repeat-y; padding: 12px 0 0 26px; } textarea { margin: 0; padding: 0; width: 100%; height: 100%; border: 0; overflow: visible; word-wrap: normal; font-family: monospace; font-size: 10pt; line-height: 16px; } #highlight { display: none; position: absolute; width: 25px; left: 0; top: 0; background-color: red; opacity: 0.5; } ``` -------------------------------- ### Process Scene Files and Load Worker Source: https://github.com/dfabulich/choicescript/blob/main/randomtest.html Processes scene files from a directory, reads their content, and then loads required ChoiceScript game logic into a Web Worker. This allows for efficient, background execution of game tests. ```javascript evaluateCandidates("randomtest.js", randomtestJsCandidates); evaluateCandidates("startup.txt", startupCandidates); var startup = startupCandidates[0]; var randomtestJs = randomtestJsCandidates[0]; var rootDirTest = new RegExp("^" + startup.webkitRelativePath.replace(/\/startup.txt$/, "/"[^/]+$)); var sceneFiles = []; for (var i = 0; i < numFiles; i++) { var file = uploadDirectory.files[i]; if (rootDirTest.test(file.webkitRelativePath)) { sceneFiles.push(file); } } var sceneContent = {}; document.body.innerHTML = ""; Promise.all([].map.call(sceneFiles, function(file) { return new Response(file).text();})).then(function(results) { for (var i = 0; i < sceneFiles.length; i++) { var file = sceneFiles[i]; sceneContent[file.name] = results[i]; } var randomtestRoot = randomtestJs.webkitRelativePath.replace(/\/randomtest.js$/, "/"); var requiredFiles = ["web/scene.js", "web/navigator.js", "web/util.js", "web/mygame/mygame.js", "seedrandom.js", "randomtest.js"]; Promise.all(requiredFiles.map(function (file) { return new Response(relativePathMap[randomtestRoot+file]).text() })).then(function (results) { worker = new Worker(URL.createObjectURL(new Blob([results.join('\n;\n')], {type: 'text/javascript'}))); worker.onmessage = function(x) { var msg = x.data.msg; messages.push(msg); console.log(msg); } worker.postMessage({iterations:iterations, randomSeed:randomSeed, showText:showText, showCoverage:showCoverage, highlightGenderPronouns:highlightGenderPronouns, showChoices:showChoices, avoidUsedOptions:avoidUsedOptions, recordBalance:recordBalance, sceneContent:sceneContent}); }); }); } ``` -------------------------------- ### Use *fake_choice for choices without sub-commands Source: https://github.com/dfabulich/choicescript/blob/main/doc/more.html The *fake_choice command functions like *choice but does not allow commands within its body, eliminating the need for *goto or *finish. ```choicescript What color do you prefer? *fake_choice #Red Red is the color of roses. #Blue Blue is the color of the sea. #Green Green is the color of spring. What an excellent choice! And what flavor of ice cream would you like? *fake_choice #Vanilla #Chocolate #Strawberry Mmm, delicious! *finish ``` -------------------------------- ### Cosmetic Branching with *fake_choice in ChoiceScript Source: https://context7.com/dfabulich/choicescript/llms.txt Displays choices cosmetically without affecting game flow. No commands are allowed within `*fake_choice` branches, and execution continues to the next line automatically. ```choicescript What is your favourite colour? *fake_choice #Red Red is the colour of courage. #Blue Blue is the colour of wisdom. #Green Green is the colour of nature. No matter your answer, your quest continues regardless. *finish ``` -------------------------------- ### ChoiceScript Text Variable Assignment Source: https://github.com/dfabulich/choicescript/blob/main/doc/index.html Illustrates how to assign text strings to variables using quotation marks. This allows for dynamic text in the game. ```choicescript *set lover_name "Jamie" ``` -------------------------------- ### Branching Narrative with *choice in ChoiceScript Source: https://context7.com/dfabulich/choicescript/llms.txt Presents players with options using `#` and executes indented commands based on selection. Branches must end with `*finish`, `*goto`, or `*goto_scene`. Nested choices are supported. ```choicescript Your path splits in two. What do you do? *choice #Take the forest path. The trees close in around you, dark and quiet. *choice #Press on bravely. You find a hidden village and rest safely. *finish #Turn back. You return to the crossroads, heart pounding. *finish #Take the mountain road. The wind howls as you climb the rocky slope. *finish #Make camp and rest. You sleep soundly and recover your strength. *set strength +10 *finish ``` -------------------------------- ### Manage Known Scenes Source: https://github.com/dfabulich/choicescript/blob/main/quicktest.html Adds a scene file name to the list of known scenes if it's not already present. Used to track all game scenes. ```javascript function addFile(name) { for (var i = 0; i < knownScenes.length; i++) { if (knownScenes[i] == name) return; } knownScenes.push(name); } ``` -------------------------------- ### ChoiceScript Conditional Logic with Variables Source: https://github.com/dfabulich/choicescript/blob/main/doc/index.html Demonstrates checking a variable's value using '*if' to control the narrative flow. The player's choices can affect story outcomes. ```choicescript #Run for class president *if leadership > 15 You win the election. *finish You lose the election. *finish ``` -------------------------------- ### Display Stats with *stat_chart Source: https://context7.com/dfabulich/choicescript/llms.txt Define variables to appear on the stats screen (`choicescript_stats.txt`). Supports 'percent' for progress bars, 'opposed_pair' for bipolar bars, and 'text' for raw values. ```choicescript *comment In web/mygame/scenes/choicescript_stats.txt: Your current statistics: *stat_chart percent Leadership percent Strength opposed_pair Courage Cowardice text gold text hero_name ``` -------------------------------- ### Ending a scene with *ending Source: https://github.com/dfabulich/choicescript/blob/main/doc/index.html Use the *ending command in the last scene to display a 'Play Again' button. This allows players to restart the game from the beginning. ```choicescript This is the last scene! The game is over! *ending ``` -------------------------------- ### Set Base URL for Scenes Source: https://github.com/dfabulich/choicescript/blob/main/editor/blankgame.html Configures the base URL for loading game scenes, typically used in web-based game environments. ```javascript Scene.baseUrl = "../web/mygame/scenes"; rootDir = "../web/" ``` -------------------------------- ### Placeholder UI Functions Source: https://github.com/dfabulich/choicescript/blob/main/quicktest.html Placeholder functions for various UI elements and interactions, returning default or no-op behavior. ```javascript printFooter = function() {}; printShareLinks = function() {}; printLink = function() {}; printButton = function() {}; printImage = function() {}; showPassword = function() {}; achieve = function() {}; loginForm = function() {}; ``` -------------------------------- ### Conditional Logic Based on Variables in ChoiceScript Source: https://github.com/dfabulich/choicescript/blob/main/web/mygame/scenes/variables.txt Use the '*if' command to create branching narratives based on variable values. Ensure conditions are met before proceeding to specific outcomes. ```choicescript *label action What will you do today? *choice #Run for class president *if leadership > 15 You win the election. *finish You lose the election. *finish #Lift weights *if strength > 15 You lift the weights. *finish You drop the weights and hurt yourself badly. You never recover. *goto_scene death ```