Repository: dastergon/wheel-of-misfortune Branch: master Commit: 973b65d48b2c Files: 16 Total size: 158.0 KB Directory structure: gitextract_7tkj533i/ ├── .github/ │ ├── FUNDING.yml │ └── workflows/ │ └── deploy_to_gh_pages.yml ├── .gitignore ├── LICENSE ├── README.md ├── incidents/ │ ├── general_incidents.json.sample │ ├── general_incidents.jsonnet.sample │ └── redis-story.json.sample ├── index.html ├── instructions.html └── static/ ├── ink.js ├── mainink.js ├── site.webmanifest ├── stopwatch.js ├── styles.css └── wheel.js ================================================ FILE CONTENTS ================================================ ================================================ FILE: .github/FUNDING.yml ================================================ github: dastergon ================================================ FILE: .github/workflows/deploy_to_gh_pages.yml ================================================ name: Deploy Wheel of Misfortune to GitHub Pages on: push: branches: - master jobs: deploy: runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 - name: Copy needed files and dirs into the public/ directory run: | mkdir -p public/incidents for source in *.html static; do cp -r $source public/; done ls incidents/ | while read i; do cp incidents/$i public/incidents/${i/.sample/}; done - uses: crazy-max/ghaction-github-pages@v2 with: commit_message: Deploy from ${{ github.ref }}@${{ github.sha }} build_dir: public env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} ================================================ FILE: .gitignore ================================================ incidents/*.json incidents/*.jsonnet ================================================ FILE: LICENSE ================================================ MIT License Copyright (c) 2018 Pavlos Ratis Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ================================================ FILE: README.md ================================================ # Wheel of Misfortune **Wheel of Misfortune** is a game that aims to build confidence in on-call engineers via simulated outage scenarios. With the game, you practice problem debugging under stress, understanding the incident management protocol, and effective communication with other engineers of your team and organization. It is a great way to train new hires, interns, and seasoned engineers to become well-rounded on-call engineers. The game is inspired by the [Site Reliability Engineering](https://landing.google.com/sre/book/chapters/accelerating-sre-on-call.html#xref_training_disaster-rpg) book. [Demo website](https://dastergon.gr/wheel-of-misfortune) ## Instructions ### Terminology * **Scenario**: A past or fictional incident case. * **Game Master**: The host-coordinator of the session. * **Volunteer**: The trainee on-call engineer. Feel free to fork the [repository](https://github.com/dastergon/wheel-of-misfortune) or [download](https://github.com/dastergon/wheel-of-misfortune/releases) the stable release. Copy the [general\_incidents.json.sample](incidents/general\_incidents.json.sample) file to *general_incidents.json*, inside the [incidents/](incidents/) directory, and insert your incident scenarios into it. To run the game locally on your computer please navigate the the main directory of the downloaded project i.e. `wheel-of-misfortune-5.0/` and from here start a web server i.e. `python -m SimpleHTTPServer` after that open the http://localhost:8000 using your web browser. The file has the following format: - **ID**: the unique ID of the outage (you can just auto-increment). - **title**: the title of the incident. - **scenario**: the description of the incident. It is useful to include URLs from monitoring systems, dashboards, time-series databases and playbooks. - **inkstory**: the path to an [Ink](https://www.inklestudios.com/ink/) story file in JSON format. You can also use [general\_incidents.jsonnet](incidents/general_incidents.jsonnet.sample) as an example, in case you want to generate your incident scenarios using [Jsonnet](https://jsonnet.org/). ### Ink [Ink](https://github.com/inkle/ink) is a scripting language for writing interactive narrative stories. It enables us to write interactive incident response narratives for team or individual trainings. You can use [Inky](https://github.com/inkle/inky) to write an interactive narrative for an incident and then export the story as JSON. Then, you can store the story file inside the [incidents/](incidents/) folder and associate the Ink story file with an Incident scenario using the **inkstory** key. You can read an example incident narrative [here](https://github.com/dastergon/wheel-of-misfortune/tree/master/incidents/redis-story.json). ### Role Playing #### Game Master 1. Choose a volunteer to be the primary on-call engineer in front of the group. 2. Find a balance between the volunteer's experience and the incident's difficulty. 3. Assist volunteer by answering questions that may arise in each theoretical action or dashboard observation. * Engage with the rest of the team and ask for different ways to debug the problem following the volunteer's explanation. * Team members may be made available over time for assistance in various topics. 5. At the end, have a debrief on the learnings of the session. #### Volunteer 1. Spin the wheel and attempt to fix the theoretical outage scenario. 2. Explain to the Game Master and the rest of the group what actions you would take (lookup queries, checks in dashboards, etc.) to find the root causes, and eventually solve the incident. 3. Always keep an eye on the time, since it is a simulated incident response scenario and not a routine troubleshooting process. During a real incident, you might have an SLA or SLO breach and therefore you should take timing into account. 4. Engage with the rest of the group. Keep them in the loop. Ask questions to different members depending on their expertise. Most importantly, **have fun!** You can [read](https://landing.google.com/sre/book/chapters/accelerating-sre-on-call.html#xref_training_disaster-rpg) a comprehensive example on how to conduct the exercise in the Google SRE book. ### Featured The Wheel of Misfortune was established as a practice in [Open Practice Library](https://openpracticelibrary.com/) and this project was [featured](https://openpracticelibrary.com/practice/wheel-of-misfortune/) there. ### Resources * [Disaster Role Playing](https://landing.google.com/sre/book/chapters/accelerating-sre-on-call.html#xref_training_disaster-rpg) * [Managing Misfortune for Best Results](https://www.usenix.org/conference/srecon18europe/presentation/barry) * [Postmortem Culture: Learning from Failure](https://landing.google.com/sre/book/chapters/postmortem-culture.html) * [Postmortem Templates](https://github.com/dastergon/postmortem-templates) * [Postmortems Metadata Index](https://postmortems.app) * [Site Reliability Engineering Resources](https://github.com/dastergon/awesome-sre) ================================================ FILE: incidents/general_incidents.json.sample ================================================ [ { "ID": "1", "title": "Incident 1", "scenario": "You received alerts that your MySQL master does not accept writes..." }, { "ID": "2", "title": "Incident 2", "scenario": "You've received alerts that your load balancer is down..." }, { "ID": "3", "title": "Incident 3", "scenario": "You've received an alert that your Blob storage system is unable to store objects..." }, { "ID": "4", "title": "Incident 4", "scenario": "You've received alerts that your DC eu-west-1 has a power outage..." }, { "ID": "5", "title": "Incident 5", "inkstory": "incidents/redis-story.json", "scenario": "You've received an alert that your Redis server is killed..." }, { "ID": "6", "title": "Incident 6", "scenario": "You've received alerts of high HTTP 5xx error rate..." }, { "ID": "7", "title": "Incident 7", "scenario": "You've received alerts that there is high query latency in the RPC servers..." }, { "ID": "8", "title": "Incident 8", "scenario": "You've received an alert that an LDAP client cannot query LDAP server" } ] ================================================ FILE: incidents/general_incidents.jsonnet.sample ================================================ local incidents = [ { scenario: "You've received an alert that your " + database + ' server is killed...' } for database in [ 'Redis', 'MySQL', 'MongoDB', ] ] + [ // Generates DC is down incidents { scenario: "You've received alerts that your DC " + dc + ' has a power outage...' } for dc in [ 'eu-west-1', 'eu-north-1', 'us-east-2', ] ] + [ // Static incidents { scenario: 'You received alerts that your MySQL master does not accept writes...', }, { scenario: "You've received alerts that your load balancer is down...", }, { scenario: "You've received an alert that your Blob storage system is unable to store objects...", }, { scenario: "You've received an alert that your Redis server is killed...", }, { scenario: "You've received alerts of high HTTP 5xx error rate...", }, { scenario: "You've received alerts that there is high query latency in the RPC servers...", }, { scenario: "You've received an alert that an LDAP client cannot query LDAP server", }, ]; # Generates incident title and id [incidents[i] { id: '%d' % [i + 1], title: 'Incident %d' % [i + 1] } for i in std.range(0, std.length(incidents) - 1)] ================================================ FILE: incidents/redis-story.json.sample ================================================ {"inkVersion":19,"root":[[["^You've received an alert that your Redis server is killed...","\n",["ev",{"^->":"0.g-0.2.$r1"},{"temp=":"$r"},"str",{"->":".^.s"},[{"#n":"$r1"}],"/str","/ev",{"*":".^.^.c-0","flg":18},{"s":["^Acknowledge the alert",{"->":"$r","var":true},null]}],"ev","str","^Wait for the incident to autoresolve","/str","/ev",{"*":".^.c-1","flg":4},"ev","str","^Do Nothing","/str","/ev",{"*":".^.c-2","flg":4},{"c-0":["ev",{"^->":"0.g-0.c-0.$r2"},"/ev",{"temp=":"$r"},{"->":".^.^.2.s"},[{"#n":"$r2"}],"\n","^How do you proceed?","\n",[["ev",{"^->":"0.g-0.c-0.9.0.$r1"},{"temp=":"$r"},"str",{"->":".^.s"},[{"#n":"$r1"}],"/str","/ev",{"*":".^.^.c-0","flg":18},{"s":["^Restart the service",{"->":"$r","var":true},null]}],["ev",{"^->":"0.g-0.c-0.9.1.$r1"},{"temp=":"$r"},"str",{"->":".^.s"},[{"#n":"$r1"}],"/str","/ev",{"*":".^.^.c-1","flg":18},{"s":["^Check Grafana ",{"->":"$r","var":true},null]}],{"c-0":["ev",{"^->":"0.g-0.c-0.9.c-0.$r2"},"/ev",{"temp=":"$r"},{"->":".^.^.0.s"},[{"#n":"$r2"}],"\n","^\"It looks like the restart didn't help the situation\"","\n",[["^And now, what?","\n",["ev",{"^->":"0.g-0.c-0.9.c-0.9.g-0.2.$r1"},{"temp=":"$r"},"str",{"->":".^.s"},[{"#n":"$r1"}],"/str","/ev",{"*":".^.^.c-0","flg":18},{"s":["^Restart again",{"->":"$r","var":true},null]}],["ev",{"^->":"0.g-0.c-0.9.c-0.9.g-0.3.$r1"},{"temp=":"$r"},"str",{"->":".^.s"},[{"#n":"$r1"}],"/str","/ev",{"*":".^.^.c-1","flg":18},{"s":["^Scale out",{"->":"$r","var":true},null]}],{"c-0":["ev",{"^->":"0.g-0.c-0.9.c-0.9.g-0.c-0.$r2"},"/ev",{"temp=":"$r"},{"->":".^.^.2.s"},[{"#n":"$r2"}],"\n",{"->":".^.^.^.g-1"},{"#f":5}],"c-1":["ev",{"^->":"0.g-0.c-0.9.c-0.9.g-0.c-1.$r2"},"/ev",{"temp=":"$r"},{"->":".^.^.3.s"},[{"#n":"$r2"}],"\n",{"->":".^.^.^.g-1"},{"#f":5}],"#f":5,"#n":"g-0"}],{"g-1":["^\"It seems that the alert is not getting resolved\"","\n",["ev",{"^->":"0.g-0.c-0.9.c-0.9.g-1.2.$r1"},{"temp=":"$r"},"str",{"->":".^.s"},[{"#n":"$r1"}],"/str","/ev",{"*":".^.^.c-2","flg":18},{"s":["^Check Dashboard for service X",{"->":"$r","var":true},null]}],["ev",{"^->":"0.g-0.c-0.9.c-0.9.g-1.3.$r1"},{"temp=":"$r"},"str",{"->":".^.s"},[{"#n":"$r1"}],"/str","/ev",{"*":".^.^.c-3","flg":18},{"s":["^Check Dashboard for service Y",{"->":"$r","var":true},null]}],{"c-2":["ev",{"^->":"0.g-0.c-0.9.c-0.9.g-1.c-2.$r2"},"/ev",{"temp=":"$r"},{"->":".^.^.2.s"},[{"#n":"$r2"}],"\n","^\"There's the URL for service X\" ",{"->":"inspect"},"\n",{"->":"0.g-1"},{"#f":5}],"c-3":["ev",{"^->":"0.g-0.c-0.9.c-0.9.g-1.c-3.$r2"},"/ev",{"temp=":"$r"},{"->":".^.^.3.s"},[{"#n":"$r2"}],"\n","^\"There's the URL for service Y\" ",{"->":"inspect"},"\n",{"->":"0.g-1"},{"#f":5}],"#f":5}]}],{"#f":5}],"c-1":["ev",{"^->":"0.g-0.c-0.9.c-1.$r2"},"/ev",{"temp=":"$r"},{"->":".^.^.1.s"},[{"#n":"$r2"}],{"->":"grafana"},"\n",{"->":"0.g-1"},{"#f":5}]}],{"#f":5}],"c-1":["^ ",{"->":"chill"},"\n",{"->":"0.g-1"},{"#f":5}],"c-2":["^ ",{"->":"chill"},"\n",{"->":"0.g-1"},{"#f":5}],"#f":5,"#n":"g-0"}],{"g-1":["end",["done",{"#f":5,"#n":"g-2"}],{"#f":5}]}],"done",{"grafana":["^There's the dashboard for Redis: URL ",{"->":"inspect"},"\n",{"#f":1}],"inspect":[[["^Do you see anything suspicious?","\n",[["ev",{"^->":"inspect.0.g-0.2.0.$r1"},{"temp=":"$r"},"str",{"->":".^.s"},[{"#n":"$r1"}],"/str","/ev",{"*":".^.^.c-0","flg":18},{"s":["^Nope ",{"->":"$r","var":true},null]}],["ev",{"^->":"inspect.0.g-0.2.1.$r1"},{"temp=":"$r"},"str",{"->":".^.s"},[{"#n":"$r1"}],"/str","/ev",{"*":".^.^.c-1","flg":18},{"s":["^Too many open connections",{"->":"$r","var":true},null]}],["ev",{"^->":"inspect.0.g-0.2.2.$r1"},{"temp=":"$r"},"str",{"->":".^.s"},[{"#n":"$r1"}],"/str","/ev",{"*":".^.^.c-2","flg":18},{"s":["^System is running out of memory",{"->":"$r","var":true},null]}],{"c-0":["ev",{"^->":"inspect.0.g-0.2.c-0.$r2"},"/ev",{"temp=":"$r"},{"->":".^.^.0.s"},[{"#n":"$r2"}],{"->":"grafana"},"\n",{"->":".^.^.g-0"},{"#f":5}],"c-1":["ev",{"^->":"inspect.0.g-0.2.c-1.$r2"},"/ev",{"temp=":"$r"},{"->":".^.^.1.s"},[{"#n":"$r2"}],"\n",{"->":".^.^.g-0"},{"#f":5}],"c-2":["ev",{"^->":"inspect.0.g-0.2.c-2.$r2"},"/ev",{"temp=":"$r"},{"->":".^.^.2.s"},[{"#n":"$r2"}],"\n",{"->":".^.^.g-0"},{"#f":5}],"g-0":["^And now what?","\n",["ev",{"^->":"inspect.0.g-0.2.g-0.2.$r1"},{"temp=":"$r"},"str",{"->":".^.s"},[{"#n":"$r1"}],"/str","/ev",{"*":".^.^.c-3","flg":18},{"s":["^Restart the service",{"->":"$r","var":true},null]}],["ev",{"^->":"inspect.0.g-0.2.g-0.3.$r1"},{"temp=":"$r"},"str",{"->":".^.s"},[{"#n":"$r1"}],"/str","/ev",{"*":".^.^.c-4","flg":18},{"s":["^Restart service X (that connects to Redis) ",{"->":"$r","var":true},null]}],["ev",{"^->":"inspect.0.g-0.2.g-0.4.$r1"},{"temp=":"$r"},"str",{"->":".^.s"},[{"#n":"$r1"}],"/str","/ev",{"*":".^.^.c-5","flg":18},{"s":["^Call your secondary to ask for help ","<>","^ ",{"->":"$r","var":true},null]}],["ev",{"^->":"inspect.0.g-0.2.g-0.5.$r1"},{"temp=":"$r"},"str",{"->":".^.s"},[{"#n":"$r1"}],"/str","/ev",{"*":".^.^.c-6","flg":18},{"s":["^Call your boss ",{"->":"$r","var":true},null]}],{"c-3":["ev",{"^->":"inspect.0.g-0.2.g-0.c-3.$r2"},"/ev",{"temp=":"$r"},{"->":".^.^.2.s"},[{"#n":"$r2"}],"\n","^\"It looks like the restart didn't help the situation\" ",{"->":"grafana"},"\n",{"#f":5}],"c-4":["ev",{"^->":"inspect.0.g-0.2.g-0.c-4.$r2"},"/ev",{"temp=":"$r"},{"->":".^.^.3.s"},[{"#n":"$r2"}],"\n","^\" ",{"->":"resolve"},"\n",{"#f":5}],"c-5":["ev",{"^->":"inspect.0.g-0.2.g-0.c-5.$r2"},"/ev",{"temp=":"$r"},{"->":".^.^.4.s"},[{"#n":"$r2"}],{"->":"chill"},"\n",{"#f":5}],"c-6":["ev",{"^->":"inspect.0.g-0.2.g-0.c-6.$r2"},"/ev",{"temp=":"$r"},{"->":".^.^.5.s"},[{"#n":"$r2"}],{"->":"escalate"},"\n",{"#f":5}],"#f":5}]}],{"#f":5,"#n":"g-0"}],null],{"#f":1}],"resolve":[["^\"Congrats, the Redis started to get back to normal\"","\n","^How do you proceed?","\n",["ev",{"^->":"resolve.0.4.$r1"},{"temp=":"$r"},"str",{"->":".^.s"},[{"#n":"$r1"}],"/str","/ev",{"*":".^.^.c-0","flg":18},{"s":["^Did you forget to open a ticket?",{"->":"$r","var":true},null]}],["ev",{"^->":"resolve.0.5.$r1"},{"temp=":"$r"},"str",{"->":".^.s"},[{"#n":"$r1"}],"/str","/ev",{"*":".^.^.c-1","flg":18},{"s":["^Mark the open ticket as resolved",{"->":"$r","var":true},null]}],{"c-0":["ev",{"^->":"resolve.0.c-0.$r2"},"/ev",{"temp=":"$r"},{"->":".^.^.4.s"},[{"#n":"$r2"}],"\n",[["ev",{"^->":"resolve.0.c-0.7.0.$r1"},{"temp=":"$r"},"str",{"->":".^.s"},[{"#n":"$r1"}],"/str","/ev",{"*":".^.^.c-0","flg":18},{"s":["^Create a new ticket, fill out the details and mark it as resolved ",{"->":"$r","var":true},null]}],{"c-0":["ev",{"^->":"resolve.0.c-0.7.c-0.$r2"},"/ev",{"temp=":"$r"},{"->":".^.^.0.s"},[{"#n":"$r2"}],"end","\n",{"#f":5}]}],{"#f":5}],"c-1":["ev",{"^->":"resolve.0.c-1.$r2"},"/ev",{"temp=":"$r"},{"->":".^.^.5.s"},[{"#n":"$r2"}],"\n","^\"Awesome, nice work!\"","\n","end",{"#f":5}]}],{"#f":1}],"escalate":["^\"Good luck with that\" ","end","\n",{"#f":1}],"chill":["^\"Have fun, doing nothing.\" ","end","\n",{"#f":1}],"#f":1}],"listDefs":{}} ================================================ FILE: index.html ================================================ Wheel of Misfortune

Wheel of Misfortune

A role-playing game for incident management training

Inspired by the Site Reliability Engineering book

Spin the Wheel!

📟

    Incident Actions

    📋

Incident Progress

📊
0%

Timing Controls


    ================================================ FILE: instructions.html ================================================ Wheel of Misfortune

    Wheel of Misfortune

    A role-playing game for incident management training

    Inspired by the Site Reliability Engineering book

    Instructions


    Wheel of Misfortune is a game that aims to build confidence to oncall engineers via simulated outage scenarios. With the game, you practice problem debugging under stress, the understanding of the incident management protocol, and effective communication with other engineers of your team and organization. It is a great way to train new hires, interns, and seasoned engineers to become well-rounded oncall engineers.

    Terminology

    Feel free to fork the repository or download the stable release.
    Insert your incident scenarios into the general_incidents.json file inside the incidents/ folder. The file has the following format:
    title the title of the incident.
    scenario the description of the incident. It is useful to include URLs from monitoring systems, dashboards, time-series databases and playbooks.
    ID the unique ID of the outage (you can just auto-increment).
    inkstory the path to an Ink story file in JSON format.

    You could use general_incidents.jsonnet as an example in case you want to generate your incident scenarios using Jsonnet.

    Wheel of Misfortune also supports the Ink scripting language for writing incident response narratives, for team and invdividual training. Ink is a scripting language for writing interactive narrative stories. It enables us to write interactive incident response narratives for team or individual trainings. You can use Inky to write an interactive narrative for an incident and then export the story as JSON. Then, you can store the story file inside the incidents/ folder and associate the Ink story file with an Incident scenario using the inkstory key. You can have a look at the incident narrative example.

    Game Master

    1. Choose a volunteer to be the primary oncall engineer in front of the group.
    2. Find a balance between volunteer's experience and incident's difficulty.
    3. Assist volunteer by answering questions that may arise in each theoretical action or dashboard observation.
      • Engage with the rest of the team and ask for different ways to debug the problem following the volunteer's explanation.
      • Team members may be made available over time for assistance in various topics.
    4. At the end, have a debrief on the learnings of the session.

    Volunteer

    1. Spin the wheel and attempt to fix the theoretical outage scenario.
    2. Explain to the Game Master and the rest of the group what actions you would take (lookup queries, checks in dashboards, etc.) to find the root causes, and eventually solve the incident.
    3. Always keep an eye on the time, since it is simulated incident response scenario and not a routine troubleshooting process. During a real incident you might have an SLA or SLO breach and therefore you should take timing into account.
    4. Engage with the rest of the group. Keep them in the loop. Ask questions to different members depending on their expertise.

    Most importantly, have fun!

    You can read a comprehensive example on how to conduct the exercise here.

    Resources

    ================================================ FILE: static/ink.js ================================================ !function(t,e){"object"==typeof exports&&"undefined"!=typeof module?e(exports):"function"==typeof define&&define.amd?define(["exports"],e):e((t=t||self).inkjs={})}(this,function(t){"use strict";class e{constructor(){if(this._components=[],this._componentsString=null,this._isRelative=!1,"string"==typeof arguments[0]){let t=arguments[0];this.componentsString=t}else if(arguments[0]instanceof e.Component&&arguments[1]instanceof e){let t=arguments[0],e=arguments[1];this._components.push(t),this._components=this._components.concat(e._components)}else if(arguments[0]instanceof Array){let t=arguments[0],e=!!arguments[1];this._components=this._components.concat(t),this._isRelative=e}}get isRelative(){return this._isRelative}get componentCount(){return this._components.length}get head(){return this._components.length>0?this._components[0]:null}get tail(){if(this._components.length>=2){let t=this._components.slice(1,this._components.length);return new e(t)}return e.self}get length(){return this._components.length}get lastComponent(){let t=this._components.length-1;return t>=0?this._components[t]:null}get containsNamedComponent(){for(let t=0,e=this._components.length;t=0}get isParent(){return this.name==t.parentId}static ToParent(){return new e(t.parentId)}toString(){return this.isIndex?this.index.toString():this.name}Equals(t){return null!=t&&t.isIndex==this.isIndex&&(this.isIndex?this.index==t.index:this.name==t.name)}}t.Component=e}(e||(e={})),function(t){function e(t,e){if(!t)throw void 0!==e&&console.warn(e),console.trace&&console.trace(),""}t.AssertType=function(t,n,i){e(t instanceof n,i)},t.Assert=e}(n||(n={}));class c extends Error{}function d(t){throw new c(`${t} is null or undefined`)}class p{constructor(){this.parent=null,this._debugMetadata=null,this._path=null}get debugMetadata(){return null===this._debugMetadata&&this.parent?this.parent.debugMetadata:this._debugMetadata}set debugMetadata(t){this._debugMetadata=t}get ownDebugMetadata(){return this._debugMetadata}DebugLineNumberOfPath(t){if(null===t)return null;let e=this.rootContentContainer;if(e){let n=e.ContentAtPath(t).obj;if(n){let t=n.debugMetadata;if(null!==t)return t.startLineNumber}}return null}get path(){if(null==this._path)if(null==this.parent)this._path=new e;else{let t=[],n=this,i=r(n.parent,N);for(;null!==i;){let a=o(n);null!=a&&a.hasValidName?t.unshift(new e.Component(a.name)):t.unshift(new e.Component(i.content.indexOf(n))),n=i,i=r(i.parent,N)}this._path=new e(t)}return this._path}ResolvePath(t){if(null===t)return d("path");if(t.isRelative){let e=r(this,N);return null===e&&(n.Assert(null!==this.parent,"Can't resolve relative path because we don't have a parent"),e=r(this.parent,N),n.Assert(null!==e,"Expected parent to be a container"),n.Assert(t.GetComponent(0).isParent),t=t.tail),null===e?d("nearestContainer"):e.ContentAtPath(t)}{let e=this.rootContentContainer;return null===e?d("contentContainer"):e.ContentAtPath(t)}}ConvertPathToRelative(t){let n=this.path,i=Math.min(t.length,n.length),a=-1;for(let e=0;evoid 0!==e[n]?e[n]:t)}toString(){return this.string}}class f{constructor(){if(this.originName=null,this.itemName=null,void 0!==arguments[1]){let t=arguments[0],e=arguments[1];this.originName=t,this.itemName=e}else if(arguments[0]){let t=arguments[0].toString().split(".");this.originName=t[0],this.itemName=t[1]}}static get Null(){return new f(null,null)}get isNull(){return null==this.originName&&null==this.itemName}get fullName(){return(null!==this.originName?this.originName:"?")+"."+this.itemName}toString(){return this.fullName}Equals(t){if(t instanceof f){let e=t;return e.itemName==this.itemName&&e.originName==this.originName}return!1}copy(){return new f(this.originName,this.itemName)}serialized(){return JSON.stringify({originName:this.originName,itemName:this.itemName})}static fromSerializedKey(t){let e=JSON.parse(t);if(!f.isLikeInkListItem(e))return f.Null;let n=e;return new f(n.originName,n.itemName)}static isLikeInkListItem(t){return"object"==typeof t&&(!(!t.hasOwnProperty("originName")||!t.hasOwnProperty("itemName"))&&(("string"==typeof t.originName||null===typeof t.originName)&&("string"==typeof t.itemName||null===typeof t.itemName)))}}class g extends Map{constructor(){if(super((()=>arguments[0]instanceof g?arguments[0]:void 0)()),this.origins=null,this._originNames=[],arguments[0]instanceof g){let t=arguments[0];t._originNames&&(this._originNames=t._originNames.slice())}else if("string"==typeof arguments[0]){let t=arguments[0],e=arguments[1];this.SetInitialOriginName(t);let n=e.listDefinitions.TryListGetDefinition(t,null);if(!n.exists)throw new Error("InkList origin could not be found in story when constructing new list: "+t);this.origins=[n.result]}else if("object"==typeof arguments[0]&&arguments[0].hasOwnProperty("Key")&&arguments[0].hasOwnProperty("Value")){let t=arguments[0];this.Add(t.Key,t.Value)}}AddItem(t){if(t instanceof f){let e=t;if(null==e.originName)return void this.AddItem(e.itemName);if(null===this.origins)return d("this.origins");for(let t of this.origins)if(t.name==e.originName){let n=t.TryGetValueForItem(e,0);if(n.exists)return void this.Add(e,n.result);throw new Error("Could not add the item "+e+" to this list because it doesn't exist in the original list definition in ink.")}throw new Error("Failed to add item to list because the item was from a new list definition that wasn't previously known to this list. Only items from previously known lists can be used, so that the int value can be found.")}{let e=t,n=null;if(null===this.origins)return d("this.origins");for(let t of this.origins){if(null===e)return d("itemName");if(t.ContainsItemWithName(e)){if(null!=n)throw new Error("Could not add the item "+e+" to this list because it could come from either "+t.name+" or "+n.name);n=t}}if(null==n)throw new Error("Could not add the item "+e+" to this list because it isn't known to any list definitions previously associated with this list.");let i=new f(n.name,e),a=n.ValueForItem(i);this.Add(i,a)}}ContainsItemNamed(t){for(let[e,n]of this){if(f.fromSerializedKey(e).itemName==t)return!0}return!1}ContainsKey(t){return this.has(t.serialized())}Add(t,e){let n=t.serialized();if(this.has(n))throw new Error(`The Map already contains an entry for ${t}`);this.set(n,e)}Remove(t){return this.delete(t.serialized())}get Count(){return this.size}get originOfMaxItem(){if(null==this.origins)return null;let t=this.maxItem.Key.originName,e=null;return this.origins.every(n=>n.name!=t||(e=n,!1)),e}get originNames(){if(this.Count>0){null==this._originNames&&this.Count>0?this._originNames=[]:(this._originNames||(this._originNames=[]),this._originNames.length=0);for(let[t,e]of this){let e=f.fromSerializedKey(t);if(null===e.originName)return d("item.originName");this._originNames.push(e.originName)}}return this._originNames}SetInitialOriginName(t){this._originNames=[t]}SetInitialOriginNames(t){this._originNames=null==t?null:t.slice()}get maxItem(){let t={Key:f.Null,Value:0};for(let[e,n]of this){let i=f.fromSerializedKey(e);(t.Key.isNull||n>t.Value)&&(t={Key:i,Value:n})}return t}get minItem(){let t={Key:f.Null,Value:0};for(let[e,n]of this){let i=f.fromSerializedKey(e);(t.Key.isNull||nt.maxItem.Value)}GreaterThanOrEquals(t){return 0!=this.Count&&(0==t.Count||this.minItem.Value>=t.minItem.Value&&this.maxItem.Value>=t.maxItem.Value)}LessThan(t){return 0!=t.Count&&(0==this.Count||this.maxItem.Value0?new g(this.maxItem):new g}MinAsList(){return this.Count>0?new g(this.minItem):new g}ListWithSubRange(t,e){if(0==this.Count)return new g;let n=this.orderedItems,i=0,a=Number.MAX_SAFE_INTEGER;Number.isInteger(t)?i=t:t instanceof g&&t.Count>0&&(i=t.minItem.Value),Number.isInteger(e)?a=e:t instanceof g&&t.Count>0&&(a=e.maxItem.Value);let r=new g;r.SetInitialOriginNames(this.originNames);for(let t of n)t.Value>=i&&t.Value<=a&&r.Add(t.Key,t.Value);return r}Equals(t){if(t instanceof g==!1)return!1;if(t.Count!=this.Count)return!1;for(let[e,n]of this)if(!t.has(e))return!1;return!0}get orderedItems(){let t=new Array;for(let[e,n]of this){let i=f.fromSerializedKey(e);t.push({Key:i,Value:n})}return t.sort((t,e)=>null===t.Key.originName?d("x.Key.originName"):null===e.Key.originName?d("y.Key.originName"):t.Value==e.Value?t.Key.originName.localeCompare(e.Key.originName):t.Valuee.Value?1:0),t}toString(){let t=this.orderedItems,e=new m;for(let n=0;n0&&e.Append(", ");let i=t[n].Key;if(null===i.itemName)return d("item.itemName");e.Append(i.itemName)}return e.toString()}valueOf(){return NaN}}class C extends Error{constructor(t){super(t),this.useEndLineNumber=!1,this.message=t,this.name="StoryException"}}function v(t,e,n){if(null===t)return{result:n,exists:!1};let i=t.get(e);return i?{result:i,exists:!0}:{result:n,exists:!1}}class S extends p{static Create(t){if("boolean"==typeof t){t=!!t?1:0}return Number.isInteger(Number(t))?new T(Number(t)):isNaN(t)?"string"==typeof t?new _(String(t)):t instanceof e?new w(s(t,e)):t instanceof g?new O(s(t,g)):null:new b(Number(t))}Copy(){return s(S.Create(this),p)}BadCastException(t){return new C("Can't cast "+this.valueObject+" from "+this.valueType+" to "+t)}}class y extends S{constructor(t){super(),this.value=t}get valueObject(){return this.value}toString(){return null===this.value?d("Value.value"):this.value.toString()}}class T extends y{constructor(t){super(t||0)}get isTruthy(){return 0!=this.value}get valueType(){return i.Int}Cast(t){if(null===this.value)return d("Value.value");if(t==this.valueType)return this;if(t==i.Float)return new b(this.value);if(t==i.String)return new _(""+this.value);throw this.BadCastException(t)}}class b extends y{constructor(t){super(t||0)}get isTruthy(){return 0!=this.value}get valueType(){return i.Float}Cast(t){if(null===this.value)return d("Value.value");if(t==this.valueType)return this;if(t==i.Int)return new T(this.value);if(t==i.String)return new _(""+this.value);throw this.BadCastException(t)}}class _ extends y{constructor(t){if(super(t||""),this._isNewline="\n"==this.value,this._isInlineWhitespace=!0,null===this.value)return d("Value.value");this.value.length>0&&this.value.split("").every(t=>" "==t||"\t"==t||(this._isInlineWhitespace=!1,!1))}get valueType(){return i.String}get isTruthy(){return null===this.value?d("Value.value"):this.value.length>0}get isNewline(){return this._isNewline}get isInlineWhitespace(){return this._isInlineWhitespace}get isNonWhitespace(){return!this.isNewline&&!this.isInlineWhitespace}Cast(t){if(t==this.valueType)return this;if(t==i.Int){let e=function(t,e=0){let n=parseInt(t);return Number.isNaN(n)?{result:e,exists:!1}:{result:n,exists:!0}}(this.value);if(e.exists)return new T(e.result);throw this.BadCastException(t)}if(t==i.Float){let e=function(t,e=0){let n=parseFloat(t);return Number.isNaN(n)?{result:e,exists:!1}:{result:n,exists:!0}}(this.value);if(e.exists)return new b(e.result);throw this.BadCastException(t)}throw this.BadCastException(t)}}class w extends y{constructor(t){super(t)}get valueType(){return i.DivertTarget}get targetPath(){return null===this.value?d("Value.value"):this.value}set targetPath(t){this.value=t}get isTruthy(){throw new Error("Shouldn't be checking the truthiness of a divert target")}Cast(t){if(t==this.valueType)return this;throw this.BadCastException(t)}toString(){return"DivertTargetValue("+this.targetPath+")"}}class E extends y{constructor(t,e=-1){super(t),this._contextIndex=e}get contextIndex(){return this._contextIndex}set contextIndex(t){this._contextIndex=t}get variableName(){return null===this.value?d("Value.value"):this.value}set variableName(t){this.value=t}get valueType(){return i.VariablePointer}get isTruthy(){throw new Error("Shouldn't be checking the truthiness of a variable pointer")}Cast(t){if(t==this.valueType)return this;throw this.BadCastException(t)}toString(){return"VariablePointerValue("+this.variableName+")"}Copy(){return new E(this.variableName,this.contextIndex)}}class O extends y{get isTruthy(){return null===this.value?d("this.value"):this.value.Count>0}get valueType(){return i.List}Cast(t){if(null===this.value)return d("Value.value");if(t==i.Int){let t=this.value.maxItem;return t.Key.isNull?new T(0):new T(t.Value)}if(t==i.Float){let t=this.value.maxItem;return t.Key.isNull?new b(0):new b(t.Value)}if(t==i.String){let t=this.value.maxItem;return t.Key.isNull?new _(""):new _(t.Key.toString())}if(t==this.valueType)return this;throw this.BadCastException(t)}constructor(t,e){super(null),t||e?t instanceof g?this.value=new g(t):t instanceof f&&"number"==typeof e&&(this.value=new g({Key:t,Value:e})):this.value=new g}static RetainListOriginsForAssignment(t,e){let n=r(t,O),i=r(e,O);return i&&null===i.value?d("newList.value"):n&&null===n.value?d("oldList.value"):void(n&&i&&0==i.value.Count&&i.value.SetInitialOriginNames(n.value.originNames))}}!function(t){t[t.Int=0]="Int",t[t.Float=1]="Float",t[t.List=2]="List",t[t.String=3]="String",t[t.DivertTarget=4]="DivertTarget",t[t.VariablePointer=5]="VariablePointer"}(i||(i={}));class x{constructor(){this.obj=null,this.approximate=!1}get correctObj(){return this.approximate?null:this.obj}get container(){return this.obj instanceof N?this.obj:null}copy(){let t=new x;return t.obj=this.obj,t.approximate=this.approximate,t}}class N extends p{constructor(){super(...arguments),this.name="",this._content=[],this.namedContent=new Map,this.visitsShouldBeCounted=!1,this.turnIndexShouldBeCounted=!1,this.countingAtStartOnly=!1,this._pathToFirstLeafContent=null}get hasValidName(){return null!=this.name&&this.name.length>0}get content(){return this._content}set content(t){this.AddContent(t)}get namedOnlyContent(){let t=new Map;for(let[e,n]of this.namedContent){let i=s(n,p);t.set(e,i)}for(let e of this.content){let n=o(e);null!=n&&n.hasValidName&&t.delete(n.name)}return 0==t.size&&(t=null),t}set namedOnlyContent(t){let e=this.namedOnlyContent;if(null!=e)for(let[t,n]of e)this.namedContent.delete(t);if(null!=t)for(let[e,n]of t){let t=o(n);null!=t&&this.AddToNamedContentOnly(t)}}get countFlags(){let t=0;return this.visitsShouldBeCounted&&(t|=N.CountFlags.Visits),this.turnIndexShouldBeCounted&&(t|=N.CountFlags.Turns),this.countingAtStartOnly&&(t|=N.CountFlags.CountStartOnly),t==N.CountFlags.CountStartOnly&&(t=0),t}set countFlags(t){let e=t;(e&N.CountFlags.Visits)>0&&(this.visitsShouldBeCounted=!0),(e&N.CountFlags.Turns)>0&&(this.turnIndexShouldBeCounted=!0),(e&N.CountFlags.CountStartOnly)>0&&(this.countingAtStartOnly=!0)}get pathToFirstLeafContent(){return null==this._pathToFirstLeafContent&&(this._pathToFirstLeafContent=this.path.PathByAppendingPath(this.internalPathToFirstLeafContent)),this._pathToFirstLeafContent}get internalPathToFirstLeafContent(){let t=[],n=this;for(;n instanceof N;)n.content.length>0&&(t.push(new e.Component(0)),n=n.content[0]);return new e(t)}AddContent(t){if(t instanceof Array){let e=t;for(let t of e)this.AddContent(t)}else{let e=t;if(this._content.push(e),e.parent)throw new Error("content is already in "+e.parent);e.parent=this,this.TryAddNamedContent(e)}}TryAddNamedContent(t){let e=o(t);null!=e&&e.hasValidName&&this.AddToNamedContentOnly(e)}AddToNamedContentOnly(t){n.AssertType(t,p,"Can only add Runtime.Objects to a Runtime.Container"),s(t,p).parent=this,this.namedContent.set(t.name,t)}ContentAtPath(t,e=0,n=-1){-1==n&&(n=t.length);let i=new x;i.approximate=!1;let a=this,s=this;for(let l=e;l=0&&t.index=0||r.set(t,e);if(r.size>0){a(),t.AppendLine("-- named: --");for(let[a,s]of r){n.AssertType(s,N,"Can only print out named Containers"),s.BuildStringOfHierarchy(t,e,i),t.AppendLine()}}e--,a(),t.Append("]")}}!function(t){let e;!function(t){t[t.Visits=1]="Visits",t[t.Turns=2]="Turns",t[t.CountStartOnly=4]="CountStartOnly"}(e=t.CountFlags||(t.CountFlags={}))}(N||(N={}));class P extends p{toString(){return"Glue"}}class A extends p{get commandType(){return this._commandType}constructor(t=A.CommandType.NotSet){super(),this._commandType=t}Copy(){return new A(this.commandType)}static EvalStart(){return new A(A.CommandType.EvalStart)}static EvalOutput(){return new A(A.CommandType.EvalOutput)}static EvalEnd(){return new A(A.CommandType.EvalEnd)}static Duplicate(){return new A(A.CommandType.Duplicate)}static PopEvaluatedValue(){return new A(A.CommandType.PopEvaluatedValue)}static PopFunction(){return new A(A.CommandType.PopFunction)}static PopTunnel(){return new A(A.CommandType.PopTunnel)}static BeginString(){return new A(A.CommandType.BeginString)}static EndString(){return new A(A.CommandType.EndString)}static NoOp(){return new A(A.CommandType.NoOp)}static ChoiceCount(){return new A(A.CommandType.ChoiceCount)}static Turns(){return new A(A.CommandType.Turns)}static TurnsSince(){return new A(A.CommandType.TurnsSince)}static ReadCount(){return new A(A.CommandType.ReadCount)}static Random(){return new A(A.CommandType.Random)}static SeedRandom(){return new A(A.CommandType.SeedRandom)}static VisitIndex(){return new A(A.CommandType.VisitIndex)}static SequenceShuffleIndex(){return new A(A.CommandType.SequenceShuffleIndex)}static StartThread(){return new A(A.CommandType.StartThread)}static Done(){return new A(A.CommandType.Done)}static End(){return new A(A.CommandType.End)}static ListFromInt(){return new A(A.CommandType.ListFromInt)}static ListRange(){return new A(A.CommandType.ListRange)}static ListRandom(){return new A(A.CommandType.ListRandom)}toString(){return this.commandType.toString()}}!function(t){let e;!function(t){t[t.NotSet=-1]="NotSet",t[t.EvalStart=0]="EvalStart",t[t.EvalOutput=1]="EvalOutput",t[t.EvalEnd=2]="EvalEnd",t[t.Duplicate=3]="Duplicate",t[t.PopEvaluatedValue=4]="PopEvaluatedValue",t[t.PopFunction=5]="PopFunction",t[t.PopTunnel=6]="PopTunnel",t[t.BeginString=7]="BeginString",t[t.EndString=8]="EndString",t[t.NoOp=9]="NoOp",t[t.ChoiceCount=10]="ChoiceCount",t[t.Turns=11]="Turns",t[t.TurnsSince=12]="TurnsSince",t[t.Random=13]="Random",t[t.SeedRandom=14]="SeedRandom",t[t.VisitIndex=15]="VisitIndex",t[t.SequenceShuffleIndex=16]="SequenceShuffleIndex",t[t.StartThread=17]="StartThread",t[t.Done=18]="Done",t[t.End=19]="End",t[t.ListFromInt=20]="ListFromInt",t[t.ListRange=21]="ListRange",t[t.ListRandom=22]="ListRandom",t[t.ReadCount=23]="ReadCount",t[t.TOTAL_VALUES=24]="TOTAL_VALUES"}(e=t.CommandType||(t.CommandType={}))}(A||(A={})),function(t){t[t.Tunnel=0]="Tunnel",t[t.Function=1]="Function",t[t.FunctionEvaluationFromGame=2]="FunctionEvaluationFromGame"}(a||(a={}));class k{constructor(){this.container=null,this.index=-1,2===arguments.length&&(this.container=arguments[0],this.index=arguments[1])}Resolve(){return this.index<0?this.container:null==this.container?null:0==this.container.content.length?this.container:this.index>=this.container.content.length?null:this.container.content[this.index]}get isNull(){return null==this.container}get path(){return this.isNull?null:this.index>=0?this.container.path.PathByAppendingComponent(new e.Component(this.index)):this.container.path}toString(){return this.container?"Ink Pointer -> "+this.container.path.toString()+" -- index "+this.index:"Ink Pointer (null)"}copy(){return new k(this.container,this.index)}static StartOf(t){return new k(t,0)}static get Null(){return new k(null,-1)}}class I extends p{constructor(t){super(),this._targetPath=null,this._targetPointer=k.Null,this.variableDivertName=null,this.pushesToStack=!1,this.stackPushType=0,this.isExternal=!1,this.externalArgs=0,this.isConditional=!1,this.pushesToStack=!1,void 0!==t&&(this.pushesToStack=!0,this.stackPushType=t)}get targetPath(){if(null!=this._targetPath&&this._targetPath.isRelative){let t=this.targetPointer.Resolve();t&&(this._targetPath=t.path)}return this._targetPath}set targetPath(t){this._targetPath=t,this._targetPointer=k.Null}get targetPointer(){if(this._targetPointer.isNull){let t=this.ResolvePath(this._targetPath).obj;if(null===this._targetPath)return d("this._targetPath");if(null===this._targetPath.lastComponent)return d("this._targetPath.lastComponent");if(this._targetPath.lastComponent.isIndex){if(null===t)return d("targetObj");this._targetPointer.container=t.parent instanceof N?t.parent:null,this._targetPointer.index=this._targetPath.lastComponent.index}else this._targetPointer=k.StartOf(t instanceof N?t:null)}return this._targetPointer.copy()}get targetPathString(){return null==this.targetPath?null:this.CompactPathString(this.targetPath)}set targetPathString(t){this.targetPath=null==t?null:new e(t)}get hasVariableTarget(){return null!=this.variableDivertName}Equals(t){let e=t;return e instanceof I&&this.hasVariableTarget==e.hasVariableTarget&&(this.hasVariableTarget?this.variableDivertName==e.variableDivertName:null===this.targetPath?d("this.targetPath"):this.targetPath.Equals(e.targetPath))}toString(){if(this.hasVariableTarget)return"Divert(variable: "+this.variableDivertName+")";if(null==this.targetPath)return"Divert(null)";{let t=new m,e=this.targetPath.toString();return t.Append("Divert"),this.isConditional&&t.Append("?"),this.pushesToStack&&(this.stackPushType==a.Function?t.Append(" function"):t.Append(" tunnel")),t.Append(" -> "),t.Append(this.targetPathString),t.Append(" ("),t.Append(e),t.Append(")"),t.toString()}}}class F extends p{constructor(t=!0){super(),this._pathOnChoice=null,this.hasCondition=!1,this.hasStartContent=!1,this.hasChoiceOnlyContent=!1,this.isInvisibleDefault=!1,this.onceOnly=!0,this.onceOnly=t}get pathOnChoice(){if(null!=this._pathOnChoice&&this._pathOnChoice.isRelative){let t=this.choiceTarget;t&&(this._pathOnChoice=t.path)}return this._pathOnChoice}set pathOnChoice(t){this._pathOnChoice=t}get choiceTarget(){return null===this._pathOnChoice?d("ChoicePoint._pathOnChoice"):this.ResolvePath(this._pathOnChoice).container}get pathStringOnChoice(){return null===this.pathOnChoice?d("ChoicePoint.pathOnChoice"):this.CompactPathString(this.pathOnChoice)}set pathStringOnChoice(t){this.pathOnChoice=new e(t)}get flags(){let t=0;return this.hasCondition&&(t|=1),this.hasStartContent&&(t|=2),this.hasChoiceOnlyContent&&(t|=4),this.isInvisibleDefault&&(t|=8),this.onceOnly&&(t|=16),t}set flags(t){this.hasCondition=(1&t)>0,this.hasStartContent=(2&t)>0,this.hasChoiceOnlyContent=(4&t)>0,this.isInvisibleDefault=(8&t)>0,this.onceOnly=(16&t)>0}toString(){if(null===this.pathOnChoice)return d("ChoicePoint.pathOnChoice");return"Choice: -> "+this.pathOnChoice.toString()}}class V extends p{constructor(t=null){super(),this.pathForCount=null,this.name=t}get containerForCount(){return null===this.pathForCount?null:this.ResolvePath(this.pathForCount).container}get pathStringForCount(){return null===this.pathForCount?null:this.CompactPathString(this.pathForCount)}set pathStringForCount(t){this.pathForCount=null===t?null:new e(t)}toString(){if(null!=this.name)return"var("+this.name+")";return"read_count("+this.pathStringForCount+")"}}class L extends p{constructor(t,e){super(),this.variableName=t||null,this.isNewDeclaration=!!e,this.isGlobal=!1}toString(){return"VarAssign to "+this.variableName}}class R extends p{}class D extends p{constructor(){if(super(),this._name=null,this._numberOfParameters=0,this._prototype=null,this._isPrototype=!1,this._operationFuncs=null,0===arguments.length)D.GenerateNativeFunctionsIfNecessary();else if(1===arguments.length){let t=arguments[0];D.GenerateNativeFunctionsIfNecessary(),this.name=t}else if(2===arguments.length){let t=arguments[0],e=arguments[1];this._isPrototype=!0,this.name=t,this.numberOfParameters=e}}static CallWithName(t){return new D(t)}static CallExistsWithName(t){return this.GenerateNativeFunctionsIfNecessary(),this._nativeFunctions.get(t)}get name(){return null===this._name?d("NativeFunctionCall._name"):this._name}set name(t){this._name=t,this._isPrototype||(null===D._nativeFunctions?d("NativeFunctionCall._nativeFunctions"):this._prototype=D._nativeFunctions.get(this._name)||null)}get numberOfParameters(){return this._prototype?this._prototype.numberOfParameters:this._numberOfParameters}set numberOfParameters(t){this._numberOfParameters=t}Call(t){if(this._prototype)return this._prototype.Call(t);if(this.numberOfParameters!=t.length)throw new Error("Unexpected number of parameters");let e=!1;for(let n of t){if(n instanceof R)throw new C('Attempting to perform operation on a void value. Did you forget to "return" a value from a function you called here?');n instanceof O&&(e=!0)}if(2==t.length&&e)return this.CallBinaryListOperation(t);let n=this.CoerceValuesToSingleType(t),a=n[0].valueType;return a==i.Int?this.CallType(n):a==i.Float?this.CallType(n):a==i.String?this.CallType(n):a==i.DivertTarget?this.CallType(n):a==i.List?this.CallType(n):null}CallType(t){let e=s(t[0],y),n=e.valueType,i=e,a=t.length;if(2==a||1==a){if(null===this._operationFuncs)return d("NativeFunctionCall._operationFuncs");let e=this._operationFuncs.get(n);if(!e)throw new C("Cannot perform operation "+this.name+" on "+n);if(2==a){let n=s(t[1],y),a=e;if(null===i.value||null===n.value)return d("NativeFunctionCall.Call BinaryOp values");let r=a(i.value,n.value);return y.Create(r)}{let t=e;if(null===i.value)return d("NativeFunctionCall.Call UnaryOp value");let n=t(i.value);return y.Create(n)}}throw new Error("Unexpected number of parameters to NativeFunctionCall: "+t.length)}CallBinaryListOperation(t){if(("+"==this.name||"-"==this.name)&&t[0]instanceof O&&t[1]instanceof T)return this.CallListIncrementOperation(t);let e=s(t[0],y),n=s(t[1],y);if(!("&&"!=this.name&&"||"!=this.name||e.valueType==i.List&&n.valueType==i.List)){if(null===this._operationFuncs)return d("NativeFunctionCall._operationFuncs");let t=this._operationFuncs.get(i.Int);if(null===t)return d("NativeFunctionCall.CallBinaryListOperation op");let a=t(e.isTruthy?1:0,n.isTruthy?1:0);return new T(a)}if(e.valueType==i.List&&n.valueType==i.List)return this.CallType([e,n]);throw new C("Can not call use "+this.name+" operation on "+e.valueType+" and "+n.valueType)}CallListIncrementOperation(t){let e=s(t[0],O),n=s(t[1],T),a=new g;if(null===e.value)return d("NativeFunctionCall.CallListIncrementOperation listVal.value");for(let[t,r]of e.value){let s=f.fromSerializedKey(t);if(null===this._operationFuncs)return d("NativeFunctionCall._operationFuncs");let l=this._operationFuncs.get(i.Int);if(null===n.value)return d("NativeFunctionCall.CallListIncrementOperation intVal.value");let o=l(r,n.value),u=null;if(null===e.value.origins)return d("NativeFunctionCall.CallListIncrementOperation listVal.value.origins");for(let t of e.value.origins)if(t.name==s.originName){u=t;break}if(null!=u){let t=u.TryGetItemWithValue(o,f.Null);t.exists&&a.Add(t.result,o)}}return new O(a)}CoerceValuesToSingleType(t){let e=i.Int,n=null;for(let a of t){let t=s(a,y);t.valueType>e&&(e=t.valueType),t.valueType==i.List&&(n=r(t,O))}let a=[];if(i[e]==i[i.List])for(let e of t){let t=s(e,y);if(t.valueType==i.List)a.push(t);else{if(t.valueType!=i.Int)throw new C("Cannot mix Lists and "+t.valueType+" values in this operation");{let e=parseInt(t.valueObject);if(null===(n=s(n,O)).value)return d("NativeFunctionCall.CoerceValuesToSingleType specialCaseList.value");let i=n.value.originOfMaxItem;if(null===i)return d("NativeFunctionCall.CoerceValuesToSingleType list");let r=i.TryGetItemWithValue(e,f.Null);if(!r.exists)throw new C("Could not find List item with the value "+e+" in "+i.name);{let t=new O(r.result,e);a.push(t)}}}}else for(let n of t){let t=s(n,y).Cast(e);a.push(t)}return a}static Identity(t){return t}static GenerateNativeFunctionsIfNecessary(){if(null==this._nativeFunctions){this._nativeFunctions=new Map,this.AddIntBinaryOp(this.Add,(t,e)=>t+e),this.AddIntBinaryOp(this.Subtract,(t,e)=>t-e),this.AddIntBinaryOp(this.Multiply,(t,e)=>t*e),this.AddIntBinaryOp(this.Divide,(t,e)=>Math.round(t/e)),this.AddIntBinaryOp(this.Mod,(t,e)=>t%e),this.AddIntUnaryOp(this.Negate,t=>-t),this.AddIntBinaryOp(this.Equal,(t,e)=>t==e?1:0),this.AddIntBinaryOp(this.Greater,(t,e)=>t>e?1:0),this.AddIntBinaryOp(this.Less,(t,e)=>tt>=e?1:0),this.AddIntBinaryOp(this.LessThanOrEquals,(t,e)=>t<=e?1:0),this.AddIntBinaryOp(this.NotEquals,(t,e)=>t!=e?1:0),this.AddIntUnaryOp(this.Not,t=>0==t?1:0),this.AddIntBinaryOp(this.And,(t,e)=>0!=t&&0!=e?1:0),this.AddIntBinaryOp(this.Or,(t,e)=>0!=t||0!=e?1:0),this.AddIntBinaryOp(this.Max,(t,e)=>Math.max(t,e)),this.AddIntBinaryOp(this.Min,(t,e)=>Math.min(t,e)),this.AddIntBinaryOp(this.Pow,(t,e)=>Math.pow(t,e)),this.AddIntUnaryOp(this.Floor,D.Identity),this.AddIntUnaryOp(this.Ceiling,D.Identity),this.AddIntUnaryOp(this.Int,D.Identity),this.AddIntUnaryOp(this.Float,t=>t),this.AddFloatBinaryOp(this.Add,(t,e)=>t+e),this.AddFloatBinaryOp(this.Subtract,(t,e)=>t-e),this.AddFloatBinaryOp(this.Multiply,(t,e)=>t*e),this.AddFloatBinaryOp(this.Divide,(t,e)=>t/e),this.AddFloatBinaryOp(this.Mod,(t,e)=>t%e),this.AddFloatUnaryOp(this.Negate,t=>-t),this.AddFloatBinaryOp(this.Equal,(t,e)=>t==e?1:0),this.AddFloatBinaryOp(this.Greater,(t,e)=>t>e?1:0),this.AddFloatBinaryOp(this.Less,(t,e)=>tt>=e?1:0),this.AddFloatBinaryOp(this.LessThanOrEquals,(t,e)=>t<=e?1:0),this.AddFloatBinaryOp(this.NotEquals,(t,e)=>t!=e?1:0),this.AddFloatUnaryOp(this.Not,t=>0==t?1:0),this.AddFloatBinaryOp(this.And,(t,e)=>0!=t&&0!=e?1:0),this.AddFloatBinaryOp(this.Or,(t,e)=>0!=t||0!=e?1:0),this.AddFloatBinaryOp(this.Max,(t,e)=>Math.max(t,e)),this.AddFloatBinaryOp(this.Min,(t,e)=>Math.min(t,e)),this.AddFloatBinaryOp(this.Pow,(t,e)=>Math.pow(t,e)),this.AddFloatUnaryOp(this.Floor,t=>Math.floor(t)),this.AddFloatUnaryOp(this.Ceiling,t=>Math.ceil(t)),this.AddFloatUnaryOp(this.Int,t=>Math.floor(t)),this.AddFloatUnaryOp(this.Float,D.Identity),this.AddStringBinaryOp(this.Add,(t,e)=>t+e),this.AddStringBinaryOp(this.Equal,(t,e)=>t===e?1:0),this.AddStringBinaryOp(this.NotEquals,(t,e)=>t!==e?1:0),this.AddStringBinaryOp(this.Has,(t,e)=>t.includes(e)?1:0),this.AddStringBinaryOp(this.Hasnt,(t,e)=>t.includes(e)?0:1),this.AddListBinaryOp(this.Add,(t,e)=>t.Union(e)),this.AddListBinaryOp(this.Subtract,(t,e)=>t.Without(e)),this.AddListBinaryOp(this.Has,(t,e)=>t.Contains(e)?1:0),this.AddListBinaryOp(this.Hasnt,(t,e)=>t.Contains(e)?0:1),this.AddListBinaryOp(this.Intersect,(t,e)=>t.Intersect(e)),this.AddListBinaryOp(this.Equal,(t,e)=>t.Equals(e)?1:0),this.AddListBinaryOp(this.Greater,(t,e)=>t.GreaterThan(e)?1:0),this.AddListBinaryOp(this.Less,(t,e)=>t.LessThan(e)?1:0),this.AddListBinaryOp(this.GreaterThanOrEquals,(t,e)=>t.GreaterThanOrEquals(e)?1:0),this.AddListBinaryOp(this.LessThanOrEquals,(t,e)=>t.LessThanOrEquals(e)?1:0),this.AddListBinaryOp(this.NotEquals,(t,e)=>t.Equals(e)?0:1),this.AddListBinaryOp(this.And,(t,e)=>t.Count>0&&e.Count>0?1:0),this.AddListBinaryOp(this.Or,(t,e)=>t.Count>0||e.Count>0?1:0),this.AddListUnaryOp(this.Not,t=>0==t.Count?1:0),this.AddListUnaryOp(this.Invert,t=>t.inverse),this.AddListUnaryOp(this.All,t=>t.all),this.AddListUnaryOp(this.ListMin,t=>t.MinAsList()),this.AddListUnaryOp(this.ListMax,t=>t.MaxAsList()),this.AddListUnaryOp(this.Count,t=>t.Count),this.AddListUnaryOp(this.ValueOfList,t=>t.maxItem.Value);let t=(t,e)=>t.Equals(e)?1:0,e=(t,e)=>t.Equals(e)?0:1;this.AddOpToNativeFunc(this.Equal,2,i.DivertTarget,t),this.AddOpToNativeFunc(this.NotEquals,2,i.DivertTarget,e)}}AddOpFuncForType(t,e){null==this._operationFuncs&&(this._operationFuncs=new Map),this._operationFuncs.set(t,e)}static AddOpToNativeFunc(t,e,n,i){if(null===this._nativeFunctions)return d("NativeFunctionCall._nativeFunctions");let a=this._nativeFunctions.get(t);a||(a=new D(t,e),this._nativeFunctions.set(t,a)),a.AddOpFuncForType(n,i)}static AddIntBinaryOp(t,e){this.AddOpToNativeFunc(t,2,i.Int,e)}static AddIntUnaryOp(t,e){this.AddOpToNativeFunc(t,1,i.Int,e)}static AddFloatBinaryOp(t,e){this.AddOpToNativeFunc(t,2,i.Float,e)}static AddFloatUnaryOp(t,e){this.AddOpToNativeFunc(t,1,i.Float,e)}static AddStringBinaryOp(t,e){this.AddOpToNativeFunc(t,2,i.String,e)}static AddListBinaryOp(t,e){this.AddOpToNativeFunc(t,2,i.List,e)}static AddListUnaryOp(t,e){this.AddOpToNativeFunc(t,1,i.List,e)}toString(){return'Native "'+this.name+'"'}}D.Add="+",D.Subtract="-",D.Divide="/",D.Multiply="*",D.Mod="%",D.Negate="_",D.Equal="==",D.Greater=">",D.Less="<",D.GreaterThanOrEquals=">=",D.LessThanOrEquals="<=",D.NotEquals="!=",D.Not="!",D.And="&&",D.Or="||",D.Min="MIN",D.Max="MAX",D.Pow="POW",D.Floor="FLOOR",D.Ceiling="CEILING",D.Int="INT",D.Float="FLOAT",D.Has="?",D.Hasnt="!?",D.Intersect="^",D.ListMin="LIST_MIN",D.ListMax="LIST_MAX",D.All="LIST_ALL",D.Count="LIST_COUNT",D.ValueOfList="LIST_VALUE",D.Invert="LIST_INVERT",D._nativeFunctions=null;class M extends p{constructor(t){super(),this.text=t.toString()||""}toString(){return"# "+this.text}}class G extends p{constructor(){super(...arguments),this.text="",this.index=0,this.threadAtGeneration=null,this.sourcePath="",this.targetPath=null,this.isInvisibleDefault=!1,this.originalThreadIndex=0}get pathStringOnChoice(){return null===this.targetPath?d("Choice.targetPath"):this.targetPath.toString()}set pathStringOnChoice(t){this.targetPath=new e(t)}}class B{constructor(t,e){this._name=t||"",this._items=null,this._itemNameToValues=e||new Map}get name(){return this._name}get items(){if(null==this._items){this._items=new Map;for(let[t,e]of this._itemNameToValues){let n=new f(this.name,t);this._items.set(n.serialized(),e)}}return this._items}ValueForItem(t){if(!t.itemName)return 0;let e=this._itemNameToValues.get(t.itemName);return void 0!==e?e:0}ContainsItem(t){return!!t.itemName&&(t.originName==this.name&&this._itemNameToValues.has(t.itemName))}ContainsItemWithName(t){return this._itemNameToValues.has(t)}TryGetItemWithValue(t,e){for(let[e,n]of this._itemNameToValues)if(n==t)return{result:new f(this.name,e),exists:!0};return{result:f.Null,exists:!1}}TryGetValueForItem(t,e){if(!t.itemName)return{result:0,exists:!1};let n=this._itemNameToValues.get(t.itemName);return n?{result:n,exists:!0}:{result:0,exists:!1}}}class W{constructor(t){this._lists=new Map,this._allUnambiguousListValueCache=new Map;for(let e of t){this._lists.set(e.name,e);for(let[t,n]of e.items){let e=f.fromSerializedKey(t),i=new O(e,n);if(!e.itemName)throw new Error("item.itemName is null or undefined.");this._allUnambiguousListValueCache.set(e.itemName,i),this._allUnambiguousListValueCache.set(e.fullName,i)}}}get lists(){let t=[];for(let[e,n]of this._lists)t.push(n);return t}TryListGetDefinition(t,e){if(null===t)return{result:e,exists:!1};let n=this._lists.get(t);return n?{result:n,exists:!0}:{result:e,exists:!1}}FindSingleItemListWithName(t){if(null===t)return d("name");let e=this._allUnambiguousListValueCache.get(t);return void 0!==e?e:null}}class j{static ListToJArray(t){let e=[];for(let n of t)e.push(this.RuntimeObjectToJToken(n));return e}static JArrayToRuntimeObjList(t,e=!1){let n=t.length;e&&n--;let i=[];for(let e=0;e"==e)return new P;for(let t=0;t->"==e)return A.PopTunnel();if("~ret"==e)return A.PopFunction();if("void"==e)return new R}if("object"==typeof t&&!Array.isArray(t)){let n,i=t;if(i["^->"])return n=i["^->"],new w(new e(n.toString()));if(i["^var"]){n=i["^var"];let t=new E(n.toString());return"ci"in i&&(n=i.ci,t.contextIndex=parseInt(n)),t}let r=!1,s=!1,l=a.Function,o=!1;if((n=i["->"])?r=!0:(n=i["f()"])?(r=!0,s=!0,l=a.Function):(n=i["->t->"])?(r=!0,s=!0,l=a.Tunnel):(n=i["x()"])&&(r=!0,o=!0,s=!1,l=a.Function),r){let t=new I;t.pushesToStack=s,t.stackPushType=l,t.isExternal=o;let e=n.toString();return(n=i.var)?t.variableDivertName=e:t.targetPathString=e,t.isConditional=!!i.c,o&&(n=i.exArgs)&&(t.externalArgs=parseInt(n)),t}if(n=i["*"]){let t=new F;return t.pathStringOnChoice=n.toString(),(n=i.flg)&&(t.flags=parseInt(n)),t}if(n=i["VAR?"])return new V(n.toString());if(n=i["CNT?"]){let t=new V;return t.pathStringForCount=n.toString(),t}let u=!1,h=!1;if((n=i["VAR="])?(u=!0,h=!0):(n=i["temp="])&&(u=!0,h=!1),u){let t=n.toString(),e=!i.re,a=new L(t,e);return a.isGlobal=h,a}if(void 0!==i["#"])return n=i["#"],new M(n.toString());if(n=i.list){let t=n,e=new g;if(n=i.origins){let t=n;e.SetInitialOriginNames(t)}for(let n in t)if(t.hasOwnProperty(n)){let i=t[n],a=new f(n),r=parseInt(i);e.Add(a,r)}return new O(e)}if(null!=i.originalChoicePath)return this.JObjectToChoice(i)}if(Array.isArray(t))return this.JArrayToContainer(t);if(null==t)return null;throw new Error("Failed to convert token to runtime object: "+JSON.stringify(t))}static RuntimeObjectToJToken(t){let e=r(t,N);if(e)return this.ContainerToJArray(e);let n=r(t,I);if(n){let t,e="->";n.isExternal?e="x()":n.pushesToStack&&(n.stackPushType==a.Function?e="f()":n.stackPushType==a.Tunnel&&(e="->t->")),t=n.hasVariableTarget?n.variableDivertName:n.targetPathString;let i={};return i[e]=t,n.hasVariableTarget&&(i.var=!0),n.isConditional&&(i.c=!0),n.externalArgs>0&&(i.exArgs=n.externalArgs),i}let i=r(t,F);if(i){let t={};return t["*"]=i.pathStringOnChoice,t.flg=i.flags,t}let s=r(t,T);if(s)return s.value;let l=r(t,b);if(l)return l.value;let o=r(t,_);if(o)return o.isNewline?"\n":"^"+o.value;let u=r(t,O);if(u)return this.InkListToJObject(u);let h=r(t,w);if(h){let t={};return null===h.value?d("divTargetVal.value"):(t["^->"]=h.value.componentsString,t)}let c=r(t,E);if(c){let t={};return t["^var"]=c.value,t.ci=c.contextIndex,t}if(r(t,P))return"<>";let p=r(t,A);if(p)return j._controlCommandNames[p.commandType];let m=r(t,D);if(m){let t=m.name;return"^"==t&&(t="L^"),t}let f=r(t,V);if(f){let t={},e=f.pathStringForCount;return null!=e?t["CNT?"]=e:t["VAR?"]=f.name,t}let g=r(t,L);if(g){let t={};return t[g.isGlobal?"VAR=":"temp="]=g.variableName,g.isNewDeclaration||(t.re=!0),t}if(r(t,R))return"void";let C=r(t,M);if(C){let t={};return t["#"]=C.text,t}let v=r(t,G);if(v)return this.ChoiceToJObject(v);throw new Error("Failed to convert runtime object to Json token: "+t)}static ContainerToJArray(t){let e=this.ListToJArray(t.content),n=t.namedOnlyContent,i=t.countFlags;if(null!=n&&n.size>0||i>0||null!=t.name){let a;if(null!=n){a=this.DictionaryRuntimeObjsToJObject(n);for(let t in a)if(a.hasOwnProperty(t)){let e=a[t];if(null!=e){let t=e[e.length-1];null!=t&&(delete t["#n"],0==Object.keys(t).length&&(e[e.length-1]=null))}}}else a={};i>0&&(a["#f"]=i),null!=t.name&&(a["#n"]=t.name),e.push(a)}else e.push(null);return e}static JArrayToContainer(t){let e=new N;e.content=this.JArrayToRuntimeObjList(t,!0);let n=t[t.length-1];if(null!=n){let t=new Map;for(let i in n)if("#f"==i)e.countFlags=parseInt(n[i]);else if("#n"==i)e.name=n[i].toString();else{let e=this.JTokenToRuntimeObject(n[i]),a=r(e,N);a&&(a.name=i),t.set(i,e)}e.namedOnlyContent=t}return e}static JObjectToChoice(t){let e=new G;return e.text=t.text.toString(),e.index=parseInt(t.index),e.sourcePath=t.originalChoicePath.toString(),e.originalThreadIndex=parseInt(t.originalThreadIndex),e.pathStringOnChoice=t.targetPath.toString(),e}static ChoiceToJObject(t){let e={};return e.text=t.text,e.index=t.index,e.originalChoicePath=t.sourcePath,e.originalThreadIndex=t.originalThreadIndex,e.targetPath=t.pathStringOnChoice,e}static InkListToJObject(t){let e=t.value;if(null===e)return d("rawList");let n={},i={};for(let[t,n]of e){i[f.fromSerializedKey(t).toString()]=n}return n.list=i,0==e.Count&&null!=e.originNames&&e.originNames.length>0&&(n.origins=e.originNames),n}static ListDefinitionsToJToken(t){let e={};for(let n of t.lists){let t={};for(let[e,i]of n.items){let n=f.fromSerializedKey(e);if(null===n.itemName)return d("item.itemName");t[n.itemName]=i}e[n.name]=t}return e}static JTokenToListDefinitions(t){let e=t,n=[];for(let t in e)if(e.hasOwnProperty(t)){let i=t.toString(),a=e[t],r=new Map;for(let n in a)if(e.hasOwnProperty(t)){let t=a[n];r.set(n,parseInt(t))}let s=new B(i,r);n.push(s)}return new W(n)}}j._controlCommandNames=(()=>{let t=[];t[A.CommandType.EvalStart]="ev",t[A.CommandType.EvalOutput]="out",t[A.CommandType.EvalEnd]="/ev",t[A.CommandType.Duplicate]="du",t[A.CommandType.PopEvaluatedValue]="pop",t[A.CommandType.PopFunction]="~ret",t[A.CommandType.PopTunnel]="->->",t[A.CommandType.BeginString]="str",t[A.CommandType.EndString]="/str",t[A.CommandType.NoOp]="nop",t[A.CommandType.ChoiceCount]="choiceCnt",t[A.CommandType.Turns]="turn",t[A.CommandType.TurnsSince]="turns",t[A.CommandType.ReadCount]="readc",t[A.CommandType.Random]="rnd",t[A.CommandType.SeedRandom]="srnd",t[A.CommandType.VisitIndex]="visit",t[A.CommandType.SequenceShuffleIndex]="seq",t[A.CommandType.StartThread]="thread",t[A.CommandType.Done]="done",t[A.CommandType.End]="end",t[A.CommandType.ListFromInt]="listInt",t[A.CommandType.ListRange]="range",t[A.CommandType.ListRandom]="lrnd";for(let e=0;e1}Reset(){this._threads=[],this._threads.push(new J.Thread),this._threads[0].callstack.push(new J.Element(a.Tunnel,this._startOfRoot))}SetJsonToken(t,e){this._threads.length=0;let n=t.threads;for(let t of n){let n=t,i=new J.Thread(n,e);this._threads.push(i)}this._threadCounter=parseInt(t.threadCounter),this._startOfRoot=k.StartOf(e.rootContentContainer)}GetJsonToken(){let t={},e=[];for(let t of this._threads)e.push(t.jsonToken);return t.threads=e,t.threadCounter=this._threadCounter,t}PushThread(){let t=this.currentThread.Copy();this._threadCounter++,t.threadIndex=this._threadCounter,this._threads.push(t)}ForkThread(){let t=this.currentThread.Copy();return this._threadCounter++,t.threadIndex=this._threadCounter,t}PopThread(){if(!this.canPopThread)throw new Error("Can't pop thread");this._threads.splice(this._threads.indexOf(this.currentThread),1)}get canPopThread(){return this._threads.length>1&&!this.elementIsEvaluateFromGame}get elementIsEvaluateFromGame(){return this.currentElement.type==a.FunctionEvaluationFromGame}Push(t,e=0,n=0){let i=new J.Element(t,this.currentElement.currentPointer,!1);i.evaluationStackHeightWhenPushed=e,i.functionStartInOutputStream=n,this.callStack.push(i)}CanPop(t=null){return!!this.canPop&&(null==t||this.currentElement.type==t)}Pop(t=null){if(!this.CanPop(t))throw new Error("Mismatched push/pop in Callstack");this.callStack.pop()}GetTemporaryVariableWithName(t,e=-1){-1==e&&(e=this.currentElementIndex+1);let n=v(this.callStack[e-1].temporaryVariables,t,null);return n.exists?n.result:null}SetTemporaryVariable(t,e,n,i=-1){-1==i&&(i=this.currentElementIndex+1);let a=this.callStack[i-1];if(!n&&!a.temporaryVariables.get(t))throw new C("Could not find temporary variable to set: "+t);let r=v(a.temporaryVariables,t,null);r.exists&&O.RetainListOriginsForAssignment(r.result,e),a.temporaryVariables.set(t,e)}ContextForVariableNamed(t){return this.currentElement.temporaryVariables.get(t)?this.currentElementIndex+1:0}ThreadWithIndex(t){return this._threads.filter(e=>{if(e.threadIndex==t)return e})[0]}get callStack(){return this.currentThread.callstack}get callStackTrace(){let t=new m;for(let e=0;e")}}}return t.toString()}}!function(t){class n{constructor(t,e,n=!1){this.evaluationStackHeightWhenPushed=0,this.functionStartInOutputStream=0,this.currentPointer=e.copy(),this.inExpressionEvaluation=n,this.temporaryVariables=new Map,this.type=t}Copy(){let t=new n(this.type,this.currentPointer,this.inExpressionEvaluation);return t.temporaryVariables=new Map(this.temporaryVariables),t.evaluationStackHeightWhenPushed=this.evaluationStackHeightWhenPushed,t.functionStartInOutputStream=this.functionStartInOutputStream,t}}t.Element=n;class i{constructor(){if(this.threadIndex=0,this.previousPointer=k.Null,this.callstack=[],arguments[0]&&arguments[1]){let t=arguments[0],i=arguments[1];this.threadIndex=parseInt(t.threadIndex);let a=t.callstack;for(let t of a){let a,r=t,s=parseInt(r.type),l=k.Null,o=r.cPath;if(void 0!==o){a=o.toString();let t=i.ContentAtPath(new e(a));if(l.container=t.container,l.index=parseInt(r.idx),null==t.obj)throw new Error("When loading state, internal story location couldn't be found: "+a+". Has the story changed since this save data was created?");if(t.approximate){if(null===l.container)return d("pointer.container");i.Warning("When loading state, exact internal story location couldn't be found: '"+a+"', so it was approximated to '"+l.container.path.toString()+"' to recover. Has the story changed since this save data was created?")}}let u=!!r.exp,h=new n(s,l,u),c=r.temp;h.temporaryVariables=j.JObjectToDictionaryRuntimeObjs(c),this.callstack.push(h)}let r=t.previousContentObject;if(void 0!==r){let t=new e(r.toString());this.previousPointer=i.PointerAtPath(t)}}}Copy(){let t=new i;t.threadIndex=this.threadIndex;for(let e of this.callstack)t.callstack.push(e.Copy());return t.previousPointer=this.previousPointer.copy(),t}get jsonToken(){let t={},e=[];for(let t of this.callstack){let n={};if(!t.currentPointer.isNull){if(null===t.currentPointer.container)return d("el.currentPointer.container");n.cPath=t.currentPointer.container.path.componentsString,n.idx=t.currentPointer.index}n.exp=t.inExpressionEvaluation,n.type=t.type,n.temp=j.DictionaryRuntimeObjsToJObject(t.temporaryVariables),e.push(n)}if(t.callstack=e,t.threadIndex=this.threadIndex,!this.previousPointer.isNull){let e=this.previousPointer.Resolve();if(null===e)return d("this.previousPointer.Resolve()");t.previousContentObject=e.path.toString()}return t}}t.Thread=i}(J||(J={}));class q{constructor(t,e){this.variableChangedEventCallbacks=[],this._batchObservingVariableChanges=!1,this._defaultGlobalVariables=new Map,this._changedVariables=new Set,this._globalVariables=new Map,this._callStack=t,this._listDefsOrigin=e;try{return new Proxy(this,{get:(t,e)=>e in t?t[e]:t.$(e),set:(t,e,n)=>(e in t?t[e]=n:t.$(e,n),!0)})}catch(t){}}variableChangedEvent(t,e){for(let n of this.variableChangedEventCallbacks)n(t,e)}get batchObservingVariableChanges(){return this._batchObservingVariableChanges}set batchObservingVariableChanges(t){if(this._batchObservingVariableChanges=t,t)this._changedVariables=new Set;else if(null!=this._changedVariables)for(let t of this._changedVariables){let e=this._globalVariables.get(t);e?this.variableChangedEvent(t,e):d("currentValue")}}get callStack(){return this._callStack}set callStack(t){this._callStack=t}$(t,e){if(void 0===e){let e=this._globalVariables.get(t);return void 0===e&&(e=this._defaultGlobalVariables.get(t)),void 0!==e?e.valueObject:null}{if(void 0===this._defaultGlobalVariables.get(t))throw new C("Cannot assign to a variable ("+t+") that hasn't been declared in the story");let n=y.Create(e);if(null==n)throw new C(null==e?"Cannot pass null to VariableState":"Invalid value passed to VariableState: "+e.toString());this.SetGlobal(t,n)}}CopyFrom(t){if(this._globalVariables=new Map(t._globalVariables),this._defaultGlobalVariables=new Map(t._defaultGlobalVariables),this.variableChangedEvent=t.variableChangedEvent,t.batchObservingVariableChanges!=this.batchObservingVariableChanges)if(t.batchObservingVariableChanges){if(this._batchObservingVariableChanges=!0,null===t._changedVariables)return d("toCopy._changedVariables");this._changedVariables=new Set(t._changedVariables)}else this._batchObservingVariableChanges=!1,this._changedVariables=null}get jsonToken(){return j.DictionaryRuntimeObjsToJObject(this._globalVariables)}set jsonToken(t){this._globalVariables=j.JObjectToDictionaryRuntimeObjs(t)}TryGetDefaultVariableValue(t){let e=v(this._defaultGlobalVariables,t,null);return e.exists?e.result:null}GlobalVariableExistsWithName(t){return this._globalVariables.has(t)}GetVariableWithName(t,e=-1){let n=this.GetRawVariableWithName(t,e),i=r(n,E);return null!==i&&(n=this.ValueAtVariablePointer(i)),n}GetRawVariableWithName(t,e){let n=null;if(0==e||-1==e){let e=v(this._globalVariables,t,null);if(e.exists)return e.result;if(null===this._listDefsOrigin)return d("VariablesState._listDefsOrigin");let n=this._listDefsOrigin.FindSingleItemListWithName(t);if(n)return n}return n=this._callStack.GetTemporaryVariableWithName(t,e)}ValueAtVariablePointer(t){return this.GetVariableWithName(t.variableName,t.contextIndex)}Assign(t,e){let n=t.variableName;if(null===n)return d("name");let i=-1,a=!1;if(a=t.isNewDeclaration?t.isGlobal:this._globalVariables.has(n),t.isNewDeclaration){let t=r(e,E);if(null!==t){e=this.ResolveVariablePointer(t)}}else{let t=null;do{null!=(t=r(this.GetRawVariableWithName(n,i),E))&&(n=t.variableName,a=0==(i=t.contextIndex))}while(null!=t)}a?this.SetGlobal(n,e):this._callStack.SetTemporaryVariable(n,e,t.isNewDeclaration,i)}SnapshotDefaultGlobals(){this._defaultGlobalVariables=new Map(this._globalVariables)}RetainListOriginsForAssignment(t,e){let n=s(t,O),i=s(e,O);n.value&&i.value&&0==i.value.Count&&i.value.SetInitialOriginNames(n.value.originNames)}SetGlobal(t,e){let n=v(this._globalVariables,t,null);if(n.exists&&O.RetainListOriginsForAssignment(n.result,e),null===t)return d("variableName");if(this._globalVariables.set(t,e),null!=this.variableChangedEvent&&e!==n.result)if(this.batchObservingVariableChanges){if(null===this._changedVariables)return d("this._changedVariables");this._changedVariables.add(t)}else this.variableChangedEvent(t,e)}ResolveVariablePointer(t){let e=t.contextIndex;-1==e&&(e=this.GetContextIndexOfVariableNamed(t.variableName));let n=r(this.GetRawVariableWithName(t.variableName,e),E);return null!=n?n:new E(t.variableName,e)}GetContextIndexOfVariableNamed(t){return this._globalVariables.get(t)?0:this._callStack.currentElementIndex}ObserveVariableChange(t){this.variableChangedEventCallbacks.push(t)}}class K{constructor(t){this.seed=t%2147483647,this.seed<=0&&(this.seed+=2147483646)}next(){return this.seed=16807*this.seed%2147483647}nextFloat(){return(this.next()-1)/2147483646}}class U{constructor(t){this.kInkSaveStateVersion=8,this.kMinCompatibleLoadVersion=8,this._currentErrors=null,this._currentWarnings=null,this.divertedPointer=k.Null,this._currentTurnIndex=0,this.storySeed=0,this.previousRandom=0,this.didSafeExit=!1,this._currentText=null,this._currentTags=null,this._outputStreamTextDirty=!0,this._outputStreamTagsDirty=!0,this.story=t,this._outputStream=[],this.OutputStreamDirty(),this._evaluationStack=[],this.callStack=new J(t),this._variablesState=new q(this.callStack,t.listDefinitions),this._visitCounts=new Map,this._turnIndices=new Map,this._currentTurnIndex=-1;let e=(new Date).getTime();this.storySeed=new K(e).next()%100,this.previousRandom=0,this._currentChoices=[],this.GoToStart()}ToJson(t=!1){return JSON.stringify(this.jsonToken,null,t?2:0)}toJson(t=!1){return this.ToJson(t)}LoadJson(t){this.jsonToken=JSON.parse(t)}VisitCountAtPathString(t){let e=v(this.visitCounts,t,null);return e.exists?e.result:0}get callstackDepth(){return this.callStack.depth}get outputStream(){return this._outputStream}get currentChoices(){return this.canContinue?[]:this._currentChoices}get generatedChoices(){return this._currentChoices}get currentErrors(){return this._currentErrors}get currentWarnings(){return this._currentWarnings}get variablesState(){return this._variablesState}get evaluationStack(){return this._evaluationStack}get visitCounts(){return this._visitCounts}get turnIndices(){return this._turnIndices}get currentTurnIndex(){return this._currentTurnIndex}get currentPathString(){let t=this.currentPointer;return t.isNull?null:null===t.path?d("pointer.path"):t.path.toString()}get currentPointer(){return this.callStack.currentElement.currentPointer.copy()}set currentPointer(t){this.callStack.currentElement.currentPointer=t.copy()}get previousPointer(){return this.callStack.currentThread.previousPointer.copy()}set previousPointer(t){this.callStack.currentThread.previousPointer=t.copy()}get canContinue(){return!this.currentPointer.isNull&&!this.hasError}get hasError(){return null!=this.currentErrors&&this.currentErrors.length>0}get hasWarning(){return null!=this.currentWarnings&&this.currentWarnings.length>0}get currentText(){if(this._outputStreamTextDirty){let t=new m;for(let e of this._outputStream){let n=r(e,_);null!==n&&t.Append(n.value)}this._currentText=this.CleanOutputWhitespace(t.toString()),this._outputStreamTextDirty=!1}return this._currentText}CleanOutputWhitespace(t){let e=new m,n=-1,i=0;for(let a=0;a0&&n!=i&&e.Append(" "),n=-1),"\n"==r&&(i=a+1),s||e.Append(r)}return e.toString()}get currentTags(){if(this._outputStreamTagsDirty){this._currentTags=[];for(let t of this._outputStream){let e=r(t,M);null!==e&&this._currentTags.push(e.text)}this._outputStreamTagsDirty=!1}return this._currentTags}get inExpressionEvaluation(){return this.callStack.currentElement.inExpressionEvaluation}set inExpressionEvaluation(t){this.callStack.currentElement.inExpressionEvaluation=t}GoToStart(){this.callStack.currentElement.currentPointer=k.StartOf(this.story.mainContentContainer)}Copy(){let t=new U(this.story);return t.outputStream.push.apply(t.outputStream,this._outputStream),this.OutputStreamDirty(),t._currentChoices.push.apply(t._currentChoices,this._currentChoices),this.hasError&&(t._currentErrors=[],t._currentErrors.push.apply(t._currentErrors,this.currentErrors||[])),this.hasWarning&&(t._currentWarnings=[],t._currentWarnings.push.apply(t._currentWarnings,this.currentWarnings||[])),t.callStack=new J(this.callStack),t._variablesState=new q(t.callStack,this.story.listDefinitions),t.variablesState.CopyFrom(this.variablesState),t.evaluationStack.push.apply(t.evaluationStack,this.evaluationStack),this.divertedPointer.isNull||(t.divertedPointer=this.divertedPointer.copy()),t.previousPointer=this.previousPointer.copy(),t._visitCounts=new Map(this.visitCounts),t._turnIndices=new Map(this.turnIndices),t._currentTurnIndex=this.currentTurnIndex,t.storySeed=this.storySeed,t.previousRandom=this.previousRandom,t.didSafeExit=this.didSafeExit,t}get jsonToken(){let t,e={};for(let e of this._currentChoices){if(null===e.threadAtGeneration)return d("c.threadAtGeneration");e.originalThreadIndex=e.threadAtGeneration.threadIndex,null==this.callStack.ThreadWithIndex(e.originalThreadIndex)&&(null==t&&(t=new Map),t[e.originalThreadIndex.toString()]=e.threadAtGeneration.jsonToken)}if(null!=t&&(e.choiceThreads=t),e.callstackThreads=this.callStack.GetJsonToken(),e.variablesState=this.variablesState.jsonToken,e.evalStack=j.ListToJArray(this.evaluationStack),e.outputStream=j.ListToJArray(this._outputStream),e.currentChoices=j.ListToJArray(this._currentChoices),!this.divertedPointer.isNull){if(null===this.divertedPointer.path)return d("this.divertedPointer.path");e.currentDivertTarget=this.divertedPointer.path.componentsString}return e.visitCounts=j.IntDictionaryToJObject(this.visitCounts),e.turnIndices=j.IntDictionaryToJObject(this.turnIndices),e.turnIdx=this.currentTurnIndex,e.storySeed=this.storySeed,e.previousRandom=this.previousRandom,e.inkSaveVersion=this.kInkSaveStateVersion,e.inkFormatVersion=this.story.inkVersionCurrent,e}set jsonToken(t){let n=t,i=n.inkSaveVersion;if(null==i)throw new C("ink save format incorrect, can't load.");if(parseInt(i)0){let t=new _(e.substring(0,n));s.push(t)}s.push(new _("\n")),l=i+1}if(-1!=a&&(o=r),o>l){let t=e.substring(l,o-l);s.push(new _(t))}if(-1!=a&&r>i&&(s.push(new _("\n")),a=0;e--){let n=this._outputStream[e],i=n instanceof A?n:null;if(null!=(n instanceof P?n:null)){r=e;break}if(null!=i&&i.commandType==A.CommandType.BeginString){e>=t&&(t=-1);break}}let s=-1;if(-1!=(s=-1!=r&&-1!=t?Math.min(t,r):-1!=r?r:t)){if(n.isNewline)i=!1;else if(n.isNonWhitespace&&(r>-1&&this.RemoveExistingGlue(),t>-1)){let t=this.callStack.elements;for(let e=t.length-1;e>=0;e--){let n=t[e];if(n.type!=a.Function)break;n.functionStartInOutputStream=-1}}}else n.isNewline&&(!this.outputStreamEndsInNewline&&this.outputStreamContainsContent||(i=!1))}if(i){if(null===t)return d("obj");this._outputStream.push(t),this.OutputStreamDirty()}}TrimNewlinesFromOutputStream(){let t=-1,e=this._outputStream.length-1;for(;e>=0;){let n=this._outputStream[e],i=r(n,A),a=r(n,_);if(null!=i||null!=a&&a.isNonWhitespace)break;null!=a&&a.isNewline&&(t=e),e--}if(t>=0)for(e=t;e=0;t--){let e=this._outputStream[t];if(e instanceof P)this._outputStream.splice(t,1);else if(e instanceof A)break}this.OutputStreamDirty()}get outputStreamEndsInNewline(){if(this._outputStream.length>0)for(let t=this._outputStream.length-1;t>=0;t--){if(this._outputStream[t]instanceof A)break;let e=this._outputStream[t];if(e instanceof _){if(e.isNewline)return!0;if(e.isNonWhitespace)break}}return!1}get outputStreamContainsContent(){for(let t=0;t=0;t--){let e=r(this._outputStream[t],A);if(e instanceof A&&e.commandType==A.CommandType.BeginString)return!0}return!1}PushEvaluationStack(t){let e=r(t,O);if(e){let t=e.value;if(null===t)return d("rawList");if(null!=t.originNames){t.origins||(t.origins=[]),t.origins.length=0;for(let e of t.originNames){if(null===this.story.listDefinitions)return d("StoryState.story.listDefinitions");let n=this.story.listDefinitions.TryListGetDefinition(e,null);if(null===n.result)return d("StoryState def.result");t.origins.indexOf(n.result)<0&&t.origins.push(n.result)}}}if(null===t)return d("obj");this.evaluationStack.push(t)}PopEvaluationStack(t){if(void 0===t){return u(this.evaluationStack.pop())}if(t>this.evaluationStack.length)throw new Error("trying to pop too many objects");return u(this.evaluationStack.splice(this.evaluationStack.length-t,t))}PeekEvaluationStack(){return this.evaluationStack[this.evaluationStack.length-1]}ForceEnd(){this.callStack.Reset(),this._currentChoices.length=0,this.currentPointer=k.Null,this.previousPointer=k.Null,this.didSafeExit=!0}TrimWhitespaceFromFunctionEnd(){n.Assert(this.callStack.currentElement.type==a.Function);let t=this.callStack.currentElement.functionStartInOutputStream;-1==t&&(t=0);for(let e=this._outputStream.length-1;e>=t;e--){let t=this._outputStream[e],n=r(t,_),i=r(t,A);if(null!=n){if(i)break;if(!n.isNewline&&!n.isInlineWhitespace)break;this._outputStream.splice(e,1),this.OutputStreamDirty()}}}PopCallStack(t=null){this.callStack.currentElement.type==a.Function&&this.TrimWhitespaceFromFunctionEnd(),this.callStack.Pop(t)}SetChosenPath(t,e){this._currentChoices.length=0;let n=this.story.PointerAtPath(t);n.isNull||-1!=n.index||(n.index=0),this.currentPointer=n,e&&this._currentTurnIndex++}StartFunctionEvaluationFromGame(t,e){this.callStack.Push(a.FunctionEvaluationFromGame,this.evaluationStack.length),this.callStack.currentElement.currentPointer=k.StartOf(t),this.PassArgumentsToEvaluationStack(e)}PassArgumentsToEvaluationStack(t){if(null!=t)for(let e=0;et;){let t=this.PopEvaluationStack();null===e&&(e=t)}if(this.PopCallStack(a.FunctionEvaluationFromGame),e){if(e instanceof R)return null;let t=s(e,y);return t.valueType==i.DivertTarget?t.valueObject.toString():t.valueObject}return null}AddError(t,e){e?(null==this._currentWarnings&&(this._currentWarnings=[]),this._currentWarnings.push(t)):(null==this._currentErrors&&(this._currentErrors=[]),this._currentErrors.push(t))}OutputStreamDirty(){this._outputStreamTextDirty=!0,this._outputStreamTagsDirty=!0}}class z{constructor(){this.startTime=void 0}get ElapsedMilliseconds(){return void 0===this.startTime?0:(new Date).getTime()-this.startTime}Start(){this.startTime=(new Date).getTime()}Stop(){this.startTime=void 0}}Number.isInteger||(Number.isInteger=function(t){return"number"==typeof t&&isFinite(t)&&t>-9007199254740992&&t<9007199254740992&&Math.floor(t)===t});class H extends p{constructor(){let t;super(),this.inkVersionCurrent=19,this.inkVersionMinimumCompatible=18,this._prevContainers=[],this.allowExternalFunctionFallbacks=!1,this._listDefinitions=null,this._variableObservers=null,this._hasValidatedExternals=!1,this._temporaryEvaluationContainer=null,this._asyncContinueActive=!1,this._stateAtLastNewline=null,this._recursiveContinueCount=0,this._profiler=null;let e=null,n=null;if(arguments[0]instanceof N)t=arguments[0],void 0!==arguments[1]&&(e=arguments[1]),this._mainContentContainer=t;else if("string"==typeof arguments[0]){let t=arguments[0];n=JSON.parse(t)}else n=arguments[0];if(null!=e&&(this._listDefinitions=new W(e)),this._externals=new Map,null!==n){let t=n,e=t.inkVersion;if(null==e)throw new Error("ink version number not found. Are you sure it's a valid .ink.json file?");let i=parseInt(e);if(i>this.inkVersionCurrent)throw new Error("Version of ink used to build story was newer than the current version of the engine");if(i0;if(this._recursiveContinueCount++,!this._asyncContinueActive){if(this._asyncContinueActive=e,!this.canContinue)throw new C("Can't continue - should check canContinue before calling Continue");this._state.didSafeExit=!1,this._state.ResetOutput(),1==this._recursiveContinueCount&&(this._state.variablesState.batchObservingVariableChanges=!0)}let n=new z;n.Start();let i=!1;do{try{i=this.ContinueSingleStep()}catch(t){if(!(t instanceof C))throw t;this.AddError(t.message,void 0,t.useEndLineNumber);break}if(i)break;if(this._asyncContinueActive&&n.ElapsedMilliseconds>t)break}while(this.canContinue);n.Stop(),!i&&this.canContinue||(null!=this._stateAtLastNewline&&(this.RestoreStateSnapshot(this._stateAtLastNewline),this._stateAtLastNewline=null),this.canContinue||(this.state.callStack.canPopThread&&this.AddError("Thread available to pop, threads should always be flat by the end of evaluation?"),0!=this.state.generatedChoices.length||this.state.didSafeExit||null!=this._temporaryEvaluationContainer||(this.state.callStack.CanPop(a.Tunnel)?this.AddError("unexpectedly reached end of content. Do you need a '->->' to return from a tunnel?"):this.state.callStack.CanPop(a.Function)?this.AddError("unexpectedly reached end of content. Do you need a '~ return'?"):this.state.callStack.canPop?this.AddError("unexpectedly reached end of content for unknown reason. Please debug compiler!"):this.AddError("ran out of content. Do you need a '-> DONE' or '-> END'?"))),this.state.didSafeExit=!1,1==this._recursiveContinueCount&&(this._state.variablesState.batchObservingVariableChanges=!1),this._asyncContinueActive=!1),this._recursiveContinueCount--,null!=this._profiler&&this._profiler.PostContinue()}ContinueSingleStep(){if(null!=this._profiler&&this._profiler.PreStep(),this.Step(),null!=this._profiler&&this._profiler.PostStep(),this.canContinue||this.state.callStack.elementIsEvaluateFromGame||this.TryFollowDefaultInvisibleChoice(),null!=this._profiler&&this._profiler.PreSnapshot(),!this.state.inStringEvaluation){if(null!=this._stateAtLastNewline){if(null===this._stateAtLastNewline.currentTags)return d("this._stateAtLastNewline.currentTags");if(null===this.state.currentTags)return d("this.state.currentTags");let t=this.CalculateNewlineOutputStateChange(this._stateAtLastNewline.currentText,this.state.currentText,this._stateAtLastNewline.currentTags.length,this.state.currentTags.length);if(t==H.OutputStateChange.ExtendedBeyondNewline)return this.RestoreStateSnapshot(this._stateAtLastNewline),!0;t==H.OutputStateChange.NewlineRemoved&&(this._stateAtLastNewline=null)}this.state.outputStreamEndsInNewline&&(this.canContinue?null==this._stateAtLastNewline&&(this._stateAtLastNewline=this.StateSnapshot()):this._stateAtLastNewline=null)}return null!=this._profiler&&this._profiler.PostSnapshot(),!1}CalculateNewlineOutputStateChange(t,e,n,i){if(null===t)return d("prevText");if(null===e)return d("currText");let a=e.length>=t.length&&"\n"==e.charAt(t.length-1);if(n==i&&t.length==e.length&&a)return H.OutputStateChange.NoChange;if(!a)return H.OutputStateChange.NewlineRemoved;if(i>n)return H.OutputStateChange.ExtendedBeyondNewline;for(let n=t.length;n0?this.Error("Failed to find content at path '"+t+"', and no approximation of it was possible."):i.approximate&&this.Warning("Failed to find content at path '"+t+"', so it was approximated to: '"+i.obj.path+"'."),e)}StateSnapshot(){return this.state.Copy()}RestoreStateSnapshot(t){this._state=t}Step(){let t=!0,e=this.state.currentPointer.copy();if(e.isNull)return;let n=r(e.Resolve(),N);for(;n&&(this.VisitContainer(n,!0),0!=n.content.length);)n=r((e=k.StartOf(n)).Resolve(),N);this.state.currentPointer=e.copy(),null!=this._profiler&&this._profiler.Step(this.state.callStack);let i=e.Resolve(),a=this.PerformLogicAndFlowControl(i);if(this.state.currentPointer.isNull)return;a&&(t=!1);let s=r(i,F);if(s){let e=this.ProcessChoice(s);e&&this.state.generatedChoices.push(e),i=null,t=!1}if(i instanceof N&&(t=!1),t){let t=r(i,E);if(t&&-1==t.contextIndex){let e=this.state.callStack.ContextForVariableNamed(t.variableName);i=new E(t.variableName,e)}this.state.inExpressionEvaluation?this.state.PushEvaluationStack(i):this.state.PushToOutputStream(i)}this.NextContent();let l=r(i,A);l&&l.commandType==A.CommandType.StartThread&&this.state.callStack.PushThread()}VisitContainer(t,e){t.countingAtStartOnly&&!e||(t.visitsShouldBeCounted&&this.IncrementVisitCountForContainer(t),t.turnIndexShouldBeCounted&&this.RecordTurnIndexVisitToContainer(t))}VisitChangedContainersDueToDivert(){let t=this.state.previousPointer.copy(),e=this.state.currentPointer.copy();if(e.isNull||-1==e.index)return;if(this._prevContainers.length=0,!t.isNull){let e=r(t.Resolve(),N)||r(t.container,N);for(;e;)this._prevContainers.push(e),e=r(e.parent,N)}let n=e.Resolve();if(null==n)return;let i=r(n.parent,N);for(;i&&(this._prevContainers.indexOf(i)<0||i.countingAtStartOnly);){let t=i.content.length>0&&n==i.content[0];this.VisitContainer(i,t),n=i,i=r(i.parent,N)}}ProcessChoice(t){let e=!0;if(t.hasCondition){let t=this.state.PopEvaluationStack();this.IsTruthy(t)||(e=!1)}let n="",i="";if(t.hasChoiceOnlyContent){i=s(this.state.PopEvaluationStack(),_).value||""}if(t.hasStartContent){n=s(this.state.PopEvaluationStack(),_).value||""}if(t.onceOnly){this.VisitCountForContainer(t.choiceTarget)>0&&(e=!1)}if(!e)return null;let a=new G;return a.targetPath=t.pathOnChoice,a.sourcePath=t.path.toString(),a.isInvisibleDefault=t.isInvisibleDefault,a.threadAtGeneration=this.state.callStack.ForkThread(),a.text=(n+i).replace(/^[ \t]+|[ \t]+$/g,""),a}IsTruthy(t){if(t instanceof y){let e=t;if(e instanceof w){let t=e;return this.Error("Shouldn't use a divert target (to "+t.targetPath+") as a conditional value. Did you intend a function call 'likeThis()' or a read count check 'likeThis'? (no arrows)"),!1}return e.isTruthy}return!1}PerformLogicAndFlowControl(t){if(null==t)return!1;if(t instanceof I){let e=t;if(e.isConditional){let t=this.state.PopEvaluationStack();if(!this.IsTruthy(t))return!0}if(e.hasVariableTarget){let t=e.variableDivertName,n=this.state.variablesState.GetVariableWithName(t);if(null==n)this.Error("Tried to divert using a target from a variable that could not be found ("+t+")");else if(!(n instanceof w)){let e=r(n,T),i="Tried to divert to a target from a variable, but the variable ("+t+") didn't contain a divert target, it ";e instanceof T&&0==e.value?i+="was empty/null (the value 0).":i+="contained '"+n+"'.",this.Error(i)}let i=s(n,w);this.state.divertedPointer=this.PointerAtPath(i.targetPath)}else{if(e.isExternal)return this.CallExternalFunction(e.targetPathString,e.externalArgs),!0;this.state.divertedPointer=e.targetPointer.copy()}return e.pushesToStack&&this.state.callStack.Push(e.stackPushType,void 0,this.state.outputStream.length),this.state.divertedPointer.isNull&&!e.isExternal&&(e&&e.debugMetadata&&null!=e.debugMetadata.sourceName?this.Error("Divert target doesn't exist: "+e.debugMetadata.sourceName):this.Error("Divert resolution failed: "+e)),!0}if(t instanceof A){let e=t;switch(e.commandType){case A.CommandType.EvalStart:this.Assert(!1===this.state.inExpressionEvaluation,"Already in expression evaluation?"),this.state.inExpressionEvaluation=!0;break;case A.CommandType.EvalEnd:this.Assert(!0===this.state.inExpressionEvaluation,"Not in expression evaluation mode"),this.state.inExpressionEvaluation=!1;break;case A.CommandType.EvalOutput:if(this.state.evaluationStack.length>0){let t=this.state.PopEvaluationStack();if(!(t instanceof R)){let e=new _(t.toString());this.state.PushToOutputStream(e)}}break;case A.CommandType.NoOp:break;case A.CommandType.Duplicate:this.state.PushEvaluationStack(this.state.PeekEvaluationStack());break;case A.CommandType.PopEvaluatedValue:this.state.PopEvaluationStack();break;case A.CommandType.PopFunction:case A.CommandType.PopTunnel:let t=e.commandType==A.CommandType.PopFunction?a.Function:a.Tunnel,n=null;if(t==a.Tunnel){let t=this.state.PopEvaluationStack();null===(n=r(t,w))&&this.Assert(t instanceof R,"Expected void if ->-> doesn't override target")}if(this.state.TryExitFunctionEvaluationFromGame())break;if(this.state.callStack.currentElement.type==t&&this.state.callStack.canPop)this.state.PopCallStack(),n&&(this.state.divertedPointer=this.PointerAtPath(n.targetPath));else{let e=new Map;e.set(a.Function,"function return statement (~ return)"),e.set(a.Tunnel,"tunnel onwards statement (->->)");let n=e.get(this.state.callStack.currentElement.type);this.state.callStack.canPop||(n="end of flow (-> END or choice)");let i="Found "+e.get(t)+", when expected "+n;this.Error(i)}break;case A.CommandType.BeginString:this.state.PushToOutputStream(e),this.Assert(!0===this.state.inExpressionEvaluation,"Expected to be in an expression when evaluating a string"),this.state.inExpressionEvaluation=!1;break;case A.CommandType.EndString:let i=[],l=0;for(let t=this.state.outputStream.length-1;t>=0;--t){let e=this.state.outputStream[t];l++;let n=r(e,A);if(n&&n.commandType==A.CommandType.BeginString)break;e instanceof _&&i.push(e)}this.state.PopFromOutputStream(l),i=i.reverse();let o=new m;for(let t of i)o.Append(t.toString());this.state.inExpressionEvaluation=!0,this.state.PushEvaluationStack(new _(o.toString()));break;case A.CommandType.ChoiceCount:let u=this.state.generatedChoices.length;this.state.PushEvaluationStack(new T(u));break;case A.CommandType.Turns:this.state.PushEvaluationStack(new T(this.state.currentTurnIndex+1));break;case A.CommandType.TurnsSince:case A.CommandType.ReadCount:let h=this.state.PopEvaluationStack();if(!(h instanceof w)){let t="";h instanceof T&&(t=". Did you accidentally pass a read count ('knot_name') instead of a target ('-> knot_name')?"),this.Error("TURNS_SINCE / READ_COUNT expected a divert target (knot, stitch, label name), but saw "+h+t);break}let c,p=s(h,w),v=r(this.ContentAtPath(p.targetPath).correctObj,N);null!=v?c=e.commandType==A.CommandType.TurnsSince?this.TurnsSinceForContainer(v):this.VisitCountForContainer(v):(c=e.commandType==A.CommandType.TurnsSince?-1:0,this.Warning("Failed to find container for "+e.toString()+" lookup at "+p.targetPath.toString())),this.state.PushEvaluationStack(new T(c));break;case A.CommandType.Random:{let t=r(this.state.PopEvaluationStack(),T),e=r(this.state.PopEvaluationStack(),T);if(null==e||e instanceof T==!1)return this.Error("Invalid value for minimum parameter of RANDOM(min, max)");if(null==t||e instanceof T==!1)return this.Error("Invalid value for maximum parameter of RANDOM(min, max)");if(null===t.value)return d("maxInt.value");if(null===e.value)return d("minInt.value");let n=t.value-e.value+1;n<=0&&this.Error("RANDOM was called with minimum as "+e.value+" and maximum as "+t.value+". The maximum must be larger");let i=this.state.storySeed+this.state.previousRandom,a=new K(i).next(),s=a%n+e.value;this.state.PushEvaluationStack(new T(s)),this.state.previousRandom=a;break}case A.CommandType.SeedRandom:let S=r(this.state.PopEvaluationStack(),T);if(null==S||S instanceof T==!1)return this.Error("Invalid value passed to SEED_RANDOM");if(null===S.value)return d("minInt.value");this.state.storySeed=S.value,this.state.previousRandom=0,this.state.PushEvaluationStack(new R);break;case A.CommandType.VisitIndex:let b=this.VisitCountForContainer(this.state.currentPointer.container)-1;this.state.PushEvaluationStack(new T(b));break;case A.CommandType.SequenceShuffleIndex:let E=this.NextSequenceShuffleIndex();this.state.PushEvaluationStack(new T(E));break;case A.CommandType.StartThread:break;case A.CommandType.Done:this.state.callStack.canPopThread?this.state.callStack.PopThread():(this.state.didSafeExit=!0,this.state.currentPointer=k.Null);break;case A.CommandType.End:this.state.ForceEnd();break;case A.CommandType.ListFromInt:let x=r(this.state.PopEvaluationStack(),T),P=s(this.state.PopEvaluationStack(),_);if(null===x)throw new C("Passed non-integer when creating a list element from a numerical value.");let I=null;if(null===this.listDefinitions)return d("this.listDefinitions");let F=this.listDefinitions.TryListGetDefinition(P.value,null);if(!F.exists)throw new C("Failed to find LIST called "+P.value);{if(null===x.value)return d("minInt.value");let t=F.result.TryGetItemWithValue(x.value,f.Null);t.exists&&(I=new O(t.result,x.value))}null==I&&(I=new O),this.state.PushEvaluationStack(I);break;case A.CommandType.ListRange:let V=r(this.state.PopEvaluationStack(),y),L=r(this.state.PopEvaluationStack(),y),D=r(this.state.PopEvaluationStack(),O);if(null===D||null===L||null===V)throw new C("Expected list, minimum and maximum for LIST_RANGE");if(null===D.value)return d("targetList.value");let M=D.value.ListWithSubRange(L.valueObject,V.valueObject);this.state.PushEvaluationStack(new O(M));break;case A.CommandType.ListRandom:{let t=this.state.PopEvaluationStack();if(null===t)throw new C("Expected list for LIST_RANDOM");let e=t.value,n=null;if(null===e)throw d("list");if(0==e.Count)n=new g;else{let t=this.state.storySeed+this.state.previousRandom,i=new K(t).next(),a=i%e.Count,r=e.entries();for(let t=0;t<=a-1;t++)r.next();let s=r.next().value,l={Key:f.fromSerializedKey(s[0]),Value:s[1]};if(null===l.Key.originName)return d("randomItem.Key.originName");(n=new g(l.Key.originName,this)).Add(l.Key,l.Value),this.state.previousRandom=i}this.state.PushEvaluationStack(new O(n));break}default:this.Error("unhandled ControlCommand: "+e)}return!0}if(t instanceof L){let e=t,n=this.state.PopEvaluationStack();return this.state.variablesState.Assign(e,n),!0}if(t instanceof V){let e=t,n=null;if(null!=e.pathForCount){let t=e.containerForCount,i=this.VisitCountForContainer(t);n=new T(i)}else if(null==(n=this.state.variablesState.GetVariableWithName(e.name))){let t=this.state.variablesState.TryGetDefaultVariableValue(e.name);null!=t?(this.Warning("Variable not found in save state: '"+e.name+"', but seems to have been newly created. Assigning value from latest ink's declaration: "+t),n=t,this.state.variablesState.SetGlobal(e.name,n)):(this.Warning("Variable not found: '"+e.name+"'. Using default value of 0 (false). This can happen with temporary variables if the declaration hasn't yet been hit."),n=new T(0))}return this.state.PushEvaluationStack(n),!0}if(t instanceof D){let e=t,n=this.state.PopEvaluationStack(e.numberOfParameters),i=e.Call(n);return this.state.PushEvaluationStack(i),!0}return!1}ChoosePathString(t,n=!0,i=[]){if(this.IfAsyncWeCant("call ChoosePathString right now"),n)this.ResetCallstack();else if(this.state.callStack.currentElement.type==a.Function){let e="",n=this.state.callStack.currentElement.currentPointer.container;throw null!=n&&(e="("+n.path.toString()+") "),new Error("Story was running a function "+e+"when you called ChoosePathString("+t+") - this is almost certainly not not what you want! Full stack trace: \n"+this.state.callStack.callStackTrace)}this.state.PassArgumentsToEvaluationStack(i),this.ChoosePath(new e(t))}IfAsyncWeCant(t){if(this._asyncContinueActive)throw new Error("Can't "+t+". Story is in the middle of a ContinueAsync(). Make more ContinueAsync() calls or a single Continue() call beforehand.")}ChoosePath(t,e=!0){this.state.SetChosenPath(t,e),this.VisitChangedContainersDueToDivert()}ChooseChoiceIndex(t){t=t;let e=this.currentChoices;this.Assert(t>=0&&te&&this.state.PopCallStack(),this.state.evaluationStack.length>n?this.state.PopEvaluationStack():null}CallExternalFunction(t,e){if(null===t)return d("funcName");let n=this._externals.get(t),i=null;if(!(void 0!==n)){if(this.allowExternalFunctionFallbacks)return i=this.KnotContainerWithName(t),this.Assert(null!==i,"Trying to call EXTERNAL function '"+t+"' which has not been bound, and fallback ink function could not be found."),this.state.callStack.Push(a.Function,void 0,this.state.outputStream.length),void(this.state.divertedPointer=k.StartOf(i));this.Assert(!1,"Trying to call EXTERNAL function '"+t+"' which has not been bound (and ink fallbacks disabled).")}let r=[];for(let t=0;t{this.Assert(t.length>=e.length,"External function expected "+e.length+" arguments");let n=[];for(let e=0,i=t.length;e1?"s":"",t+=": '",t+=Array.from(n).join("', '"),t+="' ",t+=this.allowExternalFunctionFallbacks?", and no fallback ink function found.":" (ink fallbacks disabled)",this.Error(t)}else if(null!=t){for(let e of t.content){let t=e;null!=t&&t.hasValidName||this.ValidateExternalBindings(e,n)}for(let[e,i]of t.namedContent)this.ValidateExternalBindings(r(i,p),n)}else if(null!=e){let t=r(e,I);if(t&&t.isExternal){let e=t.targetPathString;if(null===e)return d("name");if(!this._externals.has(e))if(this.allowExternalFunctionFallbacks){this.mainContentContainer.namedContent.has(e)||n.add(e)}else n.add(e)}}}ObserveVariable(t,e){if(this.IfAsyncWeCant("observe a new variable"),null===this._variableObservers&&(this._variableObservers=new Map),!this.state.variablesState.GlobalVariableExistsWithName(t))throw new C("Cannot observe variable '"+t+"' because it wasn't declared in the ink story.");this._variableObservers.has(t)?this._variableObservers.get(t).push(e):this._variableObservers.set(t,[e])}ObserveVariables(t,e){for(let n=0,i=t.length;n=e.container.content.length;){t=!1;let n=r(e.container.parent,N);if(n instanceof N==!1)break;let i=n.content.indexOf(e.container);if(-1==i)break;if((e=new k(n,i)).index++,t=!0,null===e.container)return d("pointer.container")}return t||(e=k.Null),this.state.callStack.currentElement.currentPointer=e.copy(),t}TryFollowDefaultInvisibleChoice(){let t=this._state.currentChoices,e=t.filter(t=>t.isInvisibleDefault);if(0==e.length||t.length>e.length)return!1;let n=e[0];return null===n.targetPath?d("choice.targetPath"):null===n.threadAtGeneration?d("choice.threadAtGeneration"):(this.state.callStack.currentThread=n.threadAtGeneration,this.ChoosePath(n.targetPath,!1),!0)}VisitCountForContainer(t){if(null===t)return d("container");if(!t.visitsShouldBeCounted)return console.warn("Read count for target ("+t.name+" - on "+t.debugMetadata+") unknown. The story may need to be compiled with countAllVisits flag (-c)."),0;let e=0,n=t.path.toString();return e=this.state.visitCounts.get(n)||e}IncrementVisitCountForContainer(t){let e=0,n=t.path.toString();this.state.visitCounts.has(n)&&(e=this.state.visitCounts.get(n)),e++,this.state.visitCounts.set(n,e)}RecordTurnIndexVisitToContainer(t){let e=t.path.toString();this.state.turnIndices.set(e,this.state.currentTurnIndex)}TurnsSinceForContainer(t){t.turnIndexShouldBeCounted||this.Error("TURNS_SINCE() for target ("+t.name+" - on "+t.debugMetadata+") unknown. The story may need to be compiled with countAllVisits flag (-c).");let e=t.path.toString(),n=this.state.turnIndices.get(e);return void 0!==n?this.state.currentTurnIndex-n:-1}NextSequenceShuffleIndex(){let t=r(this.state.PopEvaluationStack(),T);if(!(t instanceof T))return this.Error("expected number of elements in sequence for shuffle index"),0;let e=this.state.currentPointer.container;if(null===e)return d("seqContainer");if(null===t.value)return d("numElementsIntVal.value");let n=t.value,i=s(this.state.PopEvaluationStack(),T).value;if(null===i)return d("seqCount");let a=i/n,l=i%n,o=e.path.toString(),u=0;for(let t=0,e=o.length;t=0;--n)if(!(e=this.state.callStack.elements[n].currentPointer).isNull&&null!==e.Resolve()&&null!==(t=e.Resolve().debugMetadata))return t;for(let e=this.state.outputStream.length-1;e>=0;--e){if(null!==(t=this.state.outputStream[e].debugMetadata))return t}return null}get mainContentContainer(){return this._temporaryEvaluationContainer?this._temporaryEvaluationContainer:this._mainContentContainer}}!function(t){let e;!function(t){t[t.NoChange=0]="NoChange",t[t.ExtendedBeyondNewline=1]="ExtendedBeyondNewline",t[t.NewlineRemoved=2]="NewlineRemoved"}(e=t.OutputStateChange||(t.OutputStateChange={}))}(H||(H={})),t.InkList=g,t.Story=H,Object.defineProperty(t,"__esModule",{value:!0})}); //# sourceMappingURL=ink-es2015.js.map ================================================ FILE: static/mainink.js ================================================ var story; var storyContainer = document.querySelectorAll('#incident')[0]; function loadStory(storyName) { fetch(storyName) .then(function (response) { return response.text(); }) .then(function (storyContent) { story = new inkjs.Story(storyContent); continueStory(); }); } function showAfter(delay, el) { setTimeout(function () { el.classList.add("show") }, delay); } 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 story.currentChoices.forEach(function (choice) { // Create paragraph with anchor element var choiceParagraphElement = document.createElement('p'); choiceParagraphElement.classList.add("choice"); choiceParagraphElement.innerHTML = `${choice.text}` 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 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(); } ================================================ FILE: static/site.webmanifest ================================================ {"name":"","short_name":"","icons":[{"src":"/android-chrome-192x192.png","sizes":"192x192","type":"image/png"},{"src":"/android-chrome-512x512.png","sizes":"512x512","type":"image/png"}],"theme_color":"#ffffff","background_color":"#ffffff","display":"standalone"} ================================================ FILE: static/stopwatch.js ================================================ /* Kudos to Billy Brown: https://codepen.io/_Billy_Brown/pen/dbJeh */ var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } var Stopwatch = function () { function Stopwatch(display, results) { _classCallCheck(this, Stopwatch); this.running = false; this.display = display; this.results = results; this.laps = []; this.reset(); this.print(this.times); } _createClass(Stopwatch, [{ key: 'reset', value: function reset() { this.times = [0, 0, 0]; } }, { key: 'start', value: function start() { if (!this.time) this.time = performance.now(); if (!this.running) { this.running = true; requestAnimationFrame(this.step.bind(this)); } } }, { key: 'lap', value: function lap() { if (!this.running) return; var times = this.times; var li = document.createElement('li'); li.innerText = this.format(times); this.results.appendChild(li); } }, { key: 'stop', value: function stop() { this.running = false; this.time = null; } }, { key: 'restart', value: function restart() { if (!this.time) this.time = performance.now(); if (!this.running) { this.running = true; requestAnimationFrame(this.step.bind(this)); } this.reset(); } }, { key: 'clear', value: function clear() { clearChildren(this.results); } }, { key: 'step', value: function step( timestamp) { if (!this.running) return; this.calculate(timestamp); this.time = timestamp; this.print(); requestAnimationFrame(this.step.bind(this)); } }, { key: 'calculate', value: function calculate( timestamp) { var diff = timestamp - this.time; // Hundredths of a second are 100 ms this.times[2] += diff / 10; // Seconds are 100 hundredths of a second if (this.times[2] >= 100) { this.times[1] += 1; this.times[2] -= 100; } // Minutes are 60 seconds if (this.times[1] >= 60) { this.times[0] += 1; this.times[1] -= 60; } } }, { key: 'print', value: function print() { this.display.innerText = this.format(this.times); } }, { key: 'format', value: function format( times) { return ( pad0(times[0], 2) + ':' + pad0(times[1], 2) + ':' + pad0(Math.floor(times[2]), 2)); } }]); return Stopwatch; }(); function pad0(value, count) { var result = value.toString(); for (; result.length < count; --count) { result = '0' + result; } return result; } function clearChildren(node) { while (node.lastChild) { node.removeChild(node.lastChild); } } var stopwatch = new Stopwatch( document.querySelector('.stopwatch'), document.querySelector('.results')); ================================================ FILE: static/styles.css ================================================ html, body { width: 100%; margin: 0; padding: 0; } body { font-family: "Roboto", Helvetica, sans-serif; font-weight: 350; background-color: #f5f5f5; } button { border:solid 2px #590F20; border-radius:5px; background-color: #e7e7e7; letter-spacing: 1px; border: none; margin: 3px 1px; text-align: center; text-decoration: none; display: inline-block; cursor: pointer; font-size: 15px; padding: 12px 12px; } button:first-child { margin-left: 0; } button:last-child { margin-right: 0; } button:hover { color: rgb(255, 127, 14); } table { border-collapse: collapse; background-color: white; } td, th { border: 1px solid #dddddd; text-align: left; padding: 5px; } li a { text-decoration: none; } li a:hover { text-decoration:underline; } header p { color: #0a0a0a; } header li { color: #0a0a0a; } small a { color: white; text-decoration: underline; } small a:hover { text-decoration: underline; } ul { margin: 0; padding: 0; list-style-type: none; } h1 { margin-bottom: 5px; } h2 { margin-bottom: 10px; color: rgba(0,0,0,.54); } h3, h4 { margin-top: 5px; margin-bottom: 5px; color: rgba(0,0,0,.54); } label { display: inline-block; max-width: 100%; padding: 5px; font-weight: bold; } text { pointer-events:none; } .center { margin-right: auto; margin-left: auto; } .results { font-size: 20px; margin: 15px; padding: 0; bottom: 0; left: 50%; } .results li { color: #666; position: relative; border-bottom: 0.5px solid #333; } .result li::before { visibility: hidden; color: inherit; line-height: 3em; position: absolute; left: 0; top: 0; } a { text-decoration: none; } a:hover { text-decoration: underline; color: rgb(255, 127, 14); } p a { color: rgb(255,64,129);; } ul a { color: rgb(255,64,129);; } .br3 { border-radius: .5rem; box-shadow: 0 4px 8px 0 rgba(0,0,0,0.2); } .br-100 { border-radius: 100%; } .bw1 { border-width: .125rem; } .cf:before, .cf:after { content: " "; display: table; } .cf:after { clear: both; } .cf { *zoom: 1; } .fl { float: left; _display: inline; } .w-100 { width: 100%; } .tc { text-align: center; } @media screen and (min-width: 50em) { .w-50-ns { width: 50%; } } @media screen and (min-width: 50em) { .w-30-ns { width: 30%; } } @media screen and (min-width: 50em) { .w-40-ns { width: 40%; } } .f2 { font-size: 2.2rem; color: #721329; } @media screen and (min-width: 40em) and (max-width: 60em) { .f2-m { font-size: 2.2rem; } } @media screen and (min-width: 60em) { .f-headline-l2 { font-size: 2.2rem; } } .f3 { font-size: 1.5rem; } @media screen and (min-width: 50em) and (max-width: 60em) { .f1-m { font-size: 1.5rem; } } @media screen and (min-width: 60em) { .f-headline-l { font-size: 1.5rem; } } .white-ft { color: white; } .athelas { font-family: athelas, georgia, serif; } .pa4 { padding: 1rem; } .i { font-style: italic; } .fw1 { font-weight: 100; } .fw4 { font-weight: 400; } .f5 { font-size: 1rem; } .h2 { height: 2rem; } .black-80 { color: rgba(0, 0, 0, .8); } .pv4 { padding-top: 1rem; padding-bottom: 1rem; } .f1 { font-size: 2rem; } .ff2 { font-size: 1.15rem; } .db { display: block; } .mid-gray { color: #555; } .ph3 { padding-left: 1rem; padding-right: 1rem; } .container { position: relative; margin: 40px auto; font-size: 16px; } .ba { border-style: solid; border-width: 1px; } .bg-blue { background-color: rgb(63,81,181); } .bg-white { background-color: #fff; } .br3 { border-radius: .5rem; } .br-100 { border-radius: 100%; } .bw1 { border-width: .125rem; } .b--black-10 { border-color: rgba(0, 0, 0, .1); } .b--black-30 { border-color: rgba(0, 0, 0, .3); } .b--dotted { border-style: dotted; } .bt-0 { border-top-width: 0; } .br-0 { border-right-width: 0; } .bl-0 { border-left-width: 0; } .mw3 { max-width: 4rem; } .mw5 { max-width: 16rem; } .lh-copy { line-height: 1.5; } .list { display:list-item; list-style-type: none; } .list label { display:list-item; list-style-type: none; } .pa3 { padding: 1rem; } .pv3 { padding-top: 1rem; padding-bottom: 0.5rem; } .pl0 { padding-left: 0; } .f4 { font-size: 1rem; } .mw6 { max-width: 32rem; } @media screen and (min-width: 30em) { .pa4-ns { padding: 2rem; } } .mb { margin-bottom: 20px; } .pv5 { padding-top: 4rem; padding-bottom: 4rem; } .ph4 { padding-left: 2rem; padding-right: 2rem; } .mv4 { margin-top: 2rem; margin-bottom: 0.5rem; } .mv3 { margin-top: 0.5rem; margin-bottom: 2rem; } .mbr { margin-right: 1rem; } @media screen and (min-width: 60em) { .pv6-l { padding-top: 8rem; padding-bottom: 8rem; } } .mw-h { max-width: 35rem; } @media screen and (min-width: 50em) { .mw { max-width: 55rem; } } .flex { display: flex; } .items-center { align-items: center; } .justify-center { justify-content: center; } .w1 { width: 1rem; } .navy { color: #001b44; } .bg-lightest-blue { background-color: #cdecff; } .pa4 { padding: 2rem; } .ml3 { margin-left: .5rem; } .timing { font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; font-weight: 300; text-align: center; } .timing ul { list-style-type: none; } .timing button { padding: 12px 23px; cursor: pointer; font-size: 16px; border: 1px solid #353535; background-color: #353535; color: #ccc; border-radius: 100%; text-align: center; line-height: 35px; } .stopwatch { font-size: 2rem; } #start { background-color: #4caf50; color:white } #start:hover { background-color: #81c784; color:white } #stop { background-color: #f44336; color:white } #stop:hover { background-color: #e57373; color:white } #restart { background-color: #2196f3; color:white } #restart:hover { background-color: #b9d9f3; color:white } #wheel { width: 100%; margin-left: 5px; } #incident { margin-top: 10px; word-wrap: break-word; line-height: 1.5; } #incident p { font-size: .875rem } .link { text-decoration: none; color: white; transition: color .15s ease-in; } .link:link, .link:visited { transition: color .15s ease-in; } .link:hover { transition: color .15s ease-in; color: #f44336; } .link:active { transition: color .15s ease-in; } .link:focus { transition: color .15s ease-in; outline: 1px dotted currentColor; } @media screen and (min-width: 30em) and (max-width: 60em) { .ph5-m { padding-left: 4rem; padding-right: 4rem; } } @media screen and (min-width: 60em) { .ph6-l { padding-left: 8rem; padding-right: 8rem; } } @media screen and (min-width: 30em) { .w-40-ns { width: 40%; } } @media screen and (min-width: 30em) { .w-33-ns { width: 33.33333%; } } @media screen and (min-width: 30em) { .w-20-ns { width: 20%; } } @media screen and (min-width: 30em) { .w-10-ns { width: 10%; } } /* Slight modifications from: * https://www.cssscript.com/beautiful-checkbox-and-radio-button-replacement-with-pure-css-magic-check/ */ @keyframes hover-color { from { border-color: #c0c0c0; } to { border-color: #81c784; } } .magic-checkbox { position: absolute; display: none; } .magic-checkbox[disabled] { cursor: not-allowed; } .magic-checkbox + label { position: relative; display: block; cursor: pointer; } .magic-checkbox + label:hover:before { animation-duration: 0.4s; animation-fill-mode: both; animation-name: hover-color; } .magic-checkbox + label:before { position: absolute; top: 0; left: 0; display: inline-block; width: 30px; height: 30px; content: ''; border: 1px solid #c0c0c0; } .magic-checkbox + label:after { position: absolute; display: none; content: ''; } .magic-checkbox[disabled] + label { cursor: not-allowed; color: #e4e4e4; } .magic-checkbox[disabled] + label:hover, .magic-checkbox[disabled] + label:before, .magic-checkbox[disabled] + label:after { cursor: not-allowed; } .magic-checkbox[disabled] + label:hover:before { border: 1px solid #e4e4e4; animation-name: none; } .magic-checkbox[disabled] + label:before { border-color: #e4e4e4; } .magic-checkbox:checked + label:before { animation-name: none; } .magic-checkbox:checked + label:after { display: block; } .magic-checkbox + label:before { border-radius: 3px; } .magic-checkbox + label:after { top: 2px; left: 11px; box-sizing: border-box; width: 10px; height: 20px; transform: rotate(45deg); border-width: 2px; border-style: solid; border-color: #fff; border-top: 0; border-left: 0; } .magic-checkbox:checked + label:before { background: #388E3C; } .magic-checkbox:checked[disabled] + label:before { border: #388E3C; background: #388E3C; } .magic-checkbox + label { color: rgb(255,64,129); font-weight: bold; } .magic-checkbox + label:hover{ color: #353535; -moz-transition: all 0.2s ease-in; -o-transition: all 0.2s ease-in; -webkit-transition: all 0.2s ease-in; transition: all 0.2s ease-in; } .magic-checkbox:checked + label { color: #353535; } /* Modified: https://codepen.io/jo-asakura/pen/stFHi */ .pie-wrapper { height: 1em; width: 1em; float: left; position: relative; } .pie-wrapper:nth-child(3n + 1) { clear: both; } .pie-wrapper .pie { height: 100%; width: 100%; clip: rect(0, 1em, 1em, 0.5em); left: 0; position: absolute; top: 0; } .pie-wrapper .pie .half-circle { height: 100%; width: 100%; border: 0.1em solid #3498db; border-radius: 50%; clip: rect(0, 0.5em, 1em, 0); left: 0; position: absolute; top: 0; } .pie-wrapper .label { border-radius: 50%; bottom: 0.4em; background: none; color: #7f8c8d; cursor: default; display: block; font-size: 0.25em; left: 0.4em; line-height: 2.8em; position: absolute; right: 0.4em; text-align: center; top: 0.4em; } .pie-wrapper .label .smaller { color: #bdc3c7; font-size: .45em; padding-bottom: 20px; vertical-align: super; } .pie-wrapper .shadow { height: 100%; width: 100%; border: 0.1em solid #bdc3c7; border-radius: 50%; } .pie-wrapper.progress-25 .pie .half-circle { border-color: #e65100; } .pie-wrapper.progress-25 .pie .left-side { -webkit-transform: rotate(90deg); transform: rotate(90deg); } .pie-wrapper.progress-25 .pie .right-side { display: none; } .pie-wrapper.progress-50 .pie .half-circle { border-color: #ffc107; } .pie-wrapper.progress-50 .pie .left-side { -webkit-transform: rotate(180deg); transform: rotate(180deg); } .pie-wrapper.progress-50 .pie .right-side { display: none; } .pie-wrapper.progress-75 .pie { clip: rect(auto, auto, auto, auto); } .pie-wrapper.progress-75 .pie .half-circle { border-color: #ffd54f; } .pie-wrapper.progress-75 .pie .left-side { -webkit-transform: rotate(270deg); transform: rotate(270deg); } .pie-wrapper.progress-75 .pie .right-side { -webkit-transform: rotate(180deg); transform: rotate(180deg); } .pie-wrapper.progress-100 .pie { clip: rect(auto, auto, auto, auto); } .pie-wrapper.progress-100 .pie .half-circle { border-color: #4caf50; } .pie-wrapper.progress-100 .pie .left-side { -webkit-transform: rotate(360deg); transform: rotate(360deg); } .pie-wrapper.progress-100 .pie .right-side { -webkit-transform: rotate(180deg); transform: rotate(180deg); } *, *:before, *:after { box-sizing: border-box; } .sz { font-size: 10em; display: inline-block; } footer { color: #9e9e9e; background-color: #424242; margin-top: 2rem; } ================================================ FILE: static/wheel.js ================================================ /* This is a modified version of Jeremy Rue's Wheel of Fortune * http://bl.ocks.org/jrue/a2aaf36b3c096925ccbf */ function custom_colors(n) { var colors = ["#388E3C", "#1976D2", "#D32F2F", "#FFA000", "#388E3C", "#990099", "#0099c6", "#C2185B", "#81C784", "#D32F2F", "#1976D2", "#994499", "#4DD0E1", "#AED581", "#536DFE", "#FBC02D", "#C62828", "#AB47BC", "#66BB6A", "#90A4AE", "#0D47A1"]; return colors[n % colors.length]; } var padding = { top: 20, right: 40, bottom: 0, left: 0 }, w = 500 - padding.left - padding.right, h = 500 - padding.top - padding.bottom, r = Math.min(w, h) / 2, rotation = 0, oldrotation = 0, picked = 100000, oldpick = []; d3.json("./incidents/general_incidents.json", function (error, data) { if (error) throw error; var svg = d3.select("#wheel") .append("svg") .data([data]) .attr("viewBox", "0 0 500 500"); var container = svg.append("g") .attr("transform", "translate(" + (w / 2 + padding.left) + "," + (h / 2 + padding.top) + ")"); var vis = container .append("g"); var pie = d3.layout.pie().sort(null).value(function (d) { return 1; }); // declare an arc generator function var arc = d3.svg.arc().outerRadius(r); // select paths, use arc generator to draw var arcs = vis.selectAll("g.slice") .data(pie) .enter() .append("g") .attr("class", "slice"); arcs.append("path") .attr("fill", function (d, i) { return custom_colors(i); }) .attr("d", function (d) { return arc(d); }); // add the text arcs.append("text").attr("transform", function (d) { d.innerRadius = 0; d.outerRadius = r; d.angle = (d.startAngle + d.endAngle) / 2; return "rotate(" + (d.angle * 180 / Math.PI - 90) + ")translate(" + (d.outerRadius - 10) + ")"; }) .attr("text-anchor", "end") .text(function (d, i) { return data[i].title; }); container.on("click", spin); function spin(d) { container.on("click", null); //all slices have been seen, all done if (oldpick.length == data.length) { container.on("click", null); return; } var ps = 360 / data.length, pieslice = Math.round(1440 / data.length), rng = Math.floor((Math.random() * 1440) + 360); rotation = (Math.round(rng / ps) * ps); picked = Math.round(data.length - (rotation % 360) / ps); picked = picked >= data.length ? (picked % data.length) : picked; if (oldpick.indexOf(picked) !== -1) { d3.select(this).call(spin); return; } else { oldpick.push(picked); } rotation += 90 - Math.round(ps / 2); vis.transition() .duration(3000) .attrTween("transform", rotTween) .each("end", function () { //mark incident as seen d3.select(".slice:nth-child(" + (picked + 1) + ") path") .attr("fill", "#111"); // if Ink story is provided, override the actual scenario. if (data[picked].inkstory != undefined) { //populate incident d3.select("#incident p") .html("

    " + data[picked].title + "

    " + "
      "); container.on("click", spin, stopwatch.reset(), stopwatch.start(), changeControls(), loadStory(data[picked].inkstory)); } else { //populate incident d3.select("#incident p") .html("

      " + data[picked].title + "

      " + data[picked].scenario); container.on("click", spin, stopwatch.reset(), stopwatch.start(), changeControls()); } oldrotation = rotation; }); } //make arrow svg.append("g") .attr("transform", "translate(" + (w + padding.left + padding.right) + "," + ((h / 2) + padding.top) + ")") .append("path") .attr("d", "M-" + (r * .15) + ",0L0," + (r * .05) + "L0,-" + (r * .05) + "Z") .style({ "fill": "black" }); //draw spin circle container.append("circle") .attr("cx", 0) .attr("cy", 0) .attr("r", 30) .style({ "fill": "white", "cursor": "pointer" }); //spin text container.append("text") .attr("x", 0) .attr("y", 10) .attr("text-anchor", "middle") .text("SPIN") .style({ "color": "white", "font-weight": "bold", "font-size": "18px" }); function rotTween(to) { var i = d3.interpolate(oldrotation % 360, rotation); return function (t) { return "rotate(" + i(t) + ")"; }; } });