narrat 0.9.3 → 0.11.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (39) hide show
  1. package/lib/app.vue.d.ts +2 -1
  2. package/lib/components/MainMenu.vue.d.ts +1 -0
  3. package/lib/components/Skills.vue.d.ts +4 -0
  4. package/lib/demo/bitsy/bitsy.d.ts +27 -0
  5. package/lib/dialog-box.vue.d.ts +1 -0
  6. package/lib/display.d.ts +2 -0
  7. package/lib/index.d.ts +11 -0
  8. package/lib/index.esm.js +1323 -892
  9. package/lib/index.js +1324 -890
  10. package/lib/plugins/NarratPlugin.d.ts +11 -0
  11. package/lib/plugins.d.ts +9 -0
  12. package/lib/utils/error-handling.d.ts +1 -1
  13. package/lib/utils/logger.d.ts +9 -5
  14. package/lib/utils/skillchecks.d.ts +18 -0
  15. package/lib/vm/commands/add.d.ts +3 -0
  16. package/lib/vm/commands/add_level.d.ts +2 -0
  17. package/lib/vm/commands/add_stat.d.ts +3 -0
  18. package/lib/vm/commands/add_xp.d.ts +2 -0
  19. package/lib/vm/commands/choice.d.ts +6 -0
  20. package/lib/vm/commands/clear_dialog.d.ts +3 -0
  21. package/lib/vm/commands/command-plugin.d.ts +22 -0
  22. package/lib/vm/commands/if.d.ts +2 -0
  23. package/lib/vm/commands/index.d.ts +2 -0
  24. package/lib/vm/commands/jump.d.ts +2 -0
  25. package/lib/vm/commands/notify.d.ts +2 -0
  26. package/lib/vm/commands/pause.d.ts +2 -0
  27. package/lib/vm/commands/play.d.ts +3 -0
  28. package/lib/vm/commands/set.d.ts +2 -0
  29. package/lib/vm/commands/set_button.d.ts +2 -0
  30. package/lib/vm/commands/set_screen.d.ts +3 -0
  31. package/lib/vm/commands/set_stat.d.ts +3 -0
  32. package/lib/vm/commands/stop.d.ts +2 -0
  33. package/lib/vm/commands/talk.d.ts +2 -0
  34. package/lib/vm/commands/text.d.ts +2 -0
  35. package/lib/vm/commands/wait.d.ts +2 -0
  36. package/lib/vm/vm-helpers.d.ts +17 -0
  37. package/lib/vm/vm-parser.d.ts +9 -0
  38. package/lib/vm/vm.d.ts +20 -0
  39. package/package.json +2 -1
package/lib/index.esm.js CHANGED
@@ -1,6 +1,6 @@
1
- // Version: 0.9.3 - June 5, 2022 20:22:00
1
+ // Version: 0.11.0 - June 15, 2022 15:25:13
2
2
  import 'es6-promise/auto';
3
- import { ref, reactive, readonly, defineComponent, openBlock, createElementBlock, normalizeStyle, createElementVNode, createCommentVNode, Fragment, renderList, createBlock, Transition, withCtx, normalizeClass, renderSlot, createTextVNode, resolveComponent, withDirectives, vModelText, toDisplayString, TransitionGroup, createVNode, createApp } from 'vue';
3
+ import { ref, reactive, readonly, defineComponent, openBlock, createElementBlock, normalizeStyle, createElementVNode, createCommentVNode, Fragment, renderList, normalizeClass, createBlock, Transition, withCtx, renderSlot, createTextVNode, resolveComponent, withDirectives, vModelText, toDisplayString, TransitionGroup, createVNode, createApp } from 'vue';
4
4
  import { createLogger, createStore } from 'vuex';
5
5
 
6
6
  function styleInject(css, ref) {
@@ -30,7 +30,7 @@ function styleInject(css, ref) {
30
30
  }
31
31
  }
32
32
 
33
- var css_248z = "/*! @import */\n\n/*! modern-normalize v1.0.0 | MIT License | https://github.com/sindresorhus/modern-normalize */\n\n/*\nDocument\n========\n*/\n\n/**\nUse a better box model (opinionated).\n*/\n\n*,\n*::before,\n*::after {\n box-sizing: border-box;\n}\n\n/**\nUse a more readable tab size (opinionated).\n*/\n\n:root {\n -moz-tab-size: 4;\n -o-tab-size: 4;\n tab-size: 4;\n}\n\n/**\n1. Correct the line height in all browsers.\n2. Prevent adjustments of font size after orientation changes in iOS.\n*/\n\nhtml {\n line-height: 1.15; /* 1 */\n -webkit-text-size-adjust: 100%; /* 2 */\n}\n\n/*\nSections\n========\n*/\n\n/**\nRemove the margin in all browsers.\n*/\n\nbody {\n margin: 0;\n}\n\n/**\nImprove consistency of default fonts in all browsers. (https://github.com/sindresorhus/modern-normalize/issues/3)\n*/\n\nbody {\n font-family:\n\t\tsystem-ui,\n\t\t-apple-system, /* Firefox supports this but not yet `system-ui` */\n\t\t'Segoe UI',\n\t\tRoboto,\n\t\tHelvetica,\n\t\tArial,\n\t\tsans-serif,\n\t\t'Apple Color Emoji',\n\t\t'Segoe UI Emoji';\n}\n\n/*\nGrouping content\n================\n*/\n\n/**\n1. Add the correct height in Firefox.\n2. Correct the inheritance of border color in Firefox. (https://bugzilla.mozilla.org/show_bug.cgi?id=190655)\n*/\n\nhr {\n height: 0; /* 1 */\n color: inherit; /* 2 */\n}\n\n/*\nText-level semantics\n====================\n*/\n\n/**\nAdd the correct text decoration in Chrome, Edge, and Safari.\n*/\n\nabbr[title] {\n -webkit-text-decoration: underline dotted;\n text-decoration: underline dotted;\n}\n\n/**\nAdd the correct font weight in Edge and Safari.\n*/\n\nb,\nstrong {\n font-weight: bolder;\n}\n\n/**\n1. Improve consistency of default fonts in all browsers. (https://github.com/sindresorhus/modern-normalize/issues/3)\n2. Correct the odd 'em' font sizing in all browsers.\n*/\n\ncode,\nkbd,\nsamp,\npre {\n font-family:\n\t\tui-monospace,\n\t\tSFMono-Regular,\n\t\tConsolas,\n\t\t'Liberation Mono',\n\t\tMenlo,\n\t\tmonospace; /* 1 */\n font-size: 1em; /* 2 */\n}\n\n/**\nAdd the correct font size in all browsers.\n*/\n\nsmall {\n font-size: 80%;\n}\n\n/**\nPrevent 'sub' and 'sup' elements from affecting the line height in all browsers.\n*/\n\nsub,\nsup {\n font-size: 75%;\n line-height: 0;\n position: relative;\n vertical-align: baseline;\n}\n\nsub {\n bottom: -0.25em;\n}\n\nsup {\n top: -0.5em;\n}\n\n/*\nTabular data\n============\n*/\n\n/**\n1. Remove text indentation from table contents in Chrome and Safari. (https://bugs.chromium.org/p/chromium/issues/detail?id=999088, https://bugs.webkit.org/show_bug.cgi?id=201297)\n2. Correct table border color inheritance in all Chrome and Safari. (https://bugs.chromium.org/p/chromium/issues/detail?id=935729, https://bugs.webkit.org/show_bug.cgi?id=195016)\n*/\n\ntable {\n text-indent: 0; /* 1 */\n border-color: inherit; /* 2 */\n}\n\n/*\nForms\n=====\n*/\n\n/**\n1. Change the font styles in all browsers.\n2. Remove the margin in Firefox and Safari.\n*/\n\nbutton,\ninput,\noptgroup,\nselect,\ntextarea {\n font-family: inherit; /* 1 */\n font-size: 100%; /* 1 */\n line-height: 1.15; /* 1 */\n margin: 0; /* 2 */\n}\n\n/**\nRemove the inheritance of text transform in Edge and Firefox.\n1. Remove the inheritance of text transform in Firefox.\n*/\n\nbutton,\nselect { /* 1 */\n text-transform: none;\n}\n\n/**\nCorrect the inability to style clickable types in iOS and Safari.\n*/\n\nbutton,\n[type='button'],\n[type='reset'] {\n -webkit-appearance: button;\n}\n\n/**\nRemove the inner border and padding in Firefox.\n*/\n\n/**\nRestore the focus styles unset by the previous rule.\n*/\n\n/**\nRemove the additional ':invalid' styles in Firefox.\nSee: https://github.com/mozilla/gecko-dev/blob/2f9eacd9d3d995c937b4251a5557d95d494c9be1/layout/style/res/forms.css#L728-L737\n*/\n\n/**\nRemove the padding so developers are not caught out when they zero out 'fieldset' elements in all browsers.\n*/\n\nlegend {\n padding: 0;\n}\n\n/**\nAdd the correct vertical alignment in Chrome and Firefox.\n*/\n\nprogress {\n vertical-align: baseline;\n}\n\n/**\nCorrect the cursor style of increment and decrement buttons in Safari.\n*/\n\n/**\n1. Correct the odd appearance in Chrome and Safari.\n2. Correct the outline style in Safari.\n*/\n\n[type='search'] {\n -webkit-appearance: textfield; /* 1 */\n outline-offset: -2px; /* 2 */\n}\n\n/**\nRemove the inner padding in Chrome and Safari on macOS.\n*/\n\n/**\n1. Correct the inability to style clickable types in iOS and Safari.\n2. Change font properties to 'inherit' in Safari.\n*/\n\n/*\nInteractive\n===========\n*/\n\n/*\nAdd the correct display in Chrome and Safari.\n*/\n\nsummary {\n display: list-item;\n}\n\n/**\n * Manually forked from SUIT CSS Base: https://github.com/suitcss/base\n * A thin layer on top of normalize.css that provides a starting point more\n * suitable for web applications.\n */\n\n/**\n * Removes the default spacing and border for appropriate elements.\n */\n\nblockquote,\ndl,\ndd,\nh1,\nh2,\nh3,\nh4,\nh5,\nh6,\nhr,\nfigure,\np,\npre {\n margin: 0;\n}\n\nbutton {\n background-color: transparent;\n background-image: none;\n}\n\n/**\n * Work around a Firefox/IE bug where the transparent `button` background\n * results in a loss of the default `button` focus styles.\n */\n\nbutton:focus {\n outline: 1px dotted;\n outline: 5px auto -webkit-focus-ring-color;\n}\n\nfieldset {\n margin: 0;\n padding: 0;\n}\n\nol,\nul {\n list-style: none;\n margin: 0;\n padding: 0;\n}\n\n/**\n * Tailwind custom reset styles\n */\n\n/**\n * 1. Use the user's configured `sans` font-family (with Tailwind's default\n * sans-serif font stack as a fallback) as a sane default.\n * 2. Use Tailwind's default \"normal\" line-height so the user isn't forced\n * to override it to ensure consistency even when using the default theme.\n */\n\nhtml {\n font-family: ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, \"Helvetica Neue\", Arial, \"Noto Sans\", sans-serif, \"Apple Color Emoji\", \"Segoe UI Emoji\", \"Segoe UI Symbol\", \"Noto Color Emoji\"; /* 1 */\n line-height: 1.5; /* 2 */\n}\n\n/**\n * Inherit font-family and line-height from `html` so users can set them as\n * a class directly on the `html` element.\n */\n\nbody {\n font-family: inherit;\n line-height: inherit;\n}\n\n/**\n * 1. Prevent padding and border from affecting element width.\n *\n * We used to set this in the html element and inherit from\n * the parent element for everything else. This caused issues\n * in shadow-dom-enhanced elements like <details> where the content\n * is wrapped by a div with box-sizing set to `content-box`.\n *\n * https://github.com/mozdevs/cssremedy/issues/4\n *\n *\n * 2. Allow adding a border to an element by just adding a border-width.\n *\n * By default, the way the browser specifies that an element should have no\n * border is by setting it's border-style to `none` in the user-agent\n * stylesheet.\n *\n * In order to easily add borders to elements by just setting the `border-width`\n * property, we change the default border-style for all elements to `solid`, and\n * use border-width to hide them instead. This way our `border` utilities only\n * need to set the `border-width` property instead of the entire `border`\n * shorthand, making our border utilities much more straightforward to compose.\n *\n * https://github.com/tailwindcss/tailwindcss/pull/116\n */\n\n*,\n::before,\n::after {\n box-sizing: border-box; /* 1 */\n border-width: 0; /* 2 */\n border-style: solid; /* 2 */\n border-color: #e5e7eb; /* 2 */\n}\n\n/*\n * Ensure horizontal rules are visible by default\n */\n\nhr {\n border-top-width: 1px;\n}\n\n/**\n * Undo the `border-style: none` reset that Normalize applies to images so that\n * our `border-{width}` utilities have the expected effect.\n *\n * The Normalize reset is unnecessary for us since we default the border-width\n * to 0 on all elements.\n *\n * https://github.com/tailwindcss/tailwindcss/issues/362\n */\n\nimg {\n border-style: solid;\n}\n\ntextarea {\n resize: vertical;\n}\n\ninput::-moz-placeholder, textarea::-moz-placeholder {\n color: #9ca3af;\n}\n\ninput:-ms-input-placeholder, textarea:-ms-input-placeholder {\n color: #9ca3af;\n}\n\ninput::placeholder,\ntextarea::placeholder {\n color: #9ca3af;\n}\n\nbutton {\n cursor: pointer;\n}\n\ntable {\n border-collapse: collapse;\n}\n\nh1,\nh2,\nh3,\nh4,\nh5,\nh6 {\n font-size: inherit;\n font-weight: inherit;\n}\n\n/**\n * Reset links to optimize for opt-in styling instead of\n * opt-out.\n */\n\na {\n color: inherit;\n text-decoration: inherit;\n}\n\n/**\n * Reset form element properties that are easy to forget to\n * style explicitly so you don't inadvertently introduce\n * styles that deviate from your design system. These styles\n * supplement a partial reset that is already applied by\n * normalize.css.\n */\n\nbutton,\ninput,\noptgroup,\nselect,\ntextarea {\n padding: 0;\n line-height: inherit;\n color: inherit;\n}\n\n/**\n * Use the configured 'mono' font family for elements that\n * are expected to be rendered with a monospace font, falling\n * back to the system monospace stack if there is no configured\n * 'mono' font family.\n */\n\npre,\ncode,\nkbd,\nsamp {\n font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, \"Liberation Mono\", \"Courier New\", monospace;\n}\n\n/**\n * Make replaced elements `display: block` by default as that's\n * the behavior you want almost all of the time. Inspired by\n * CSS Remedy, with `svg` added as well.\n *\n * https://github.com/mozdevs/cssremedy/issues/14\n */\n\nimg,\nsvg,\nvideo,\ncanvas,\naudio,\niframe,\nembed,\nobject {\n display: block;\n vertical-align: middle;\n}\n\n/**\n * Constrain images and videos to the parent width and preserve\n * their instrinsic aspect ratio.\n *\n * https://github.com/mozdevs/cssremedy/issues/14\n */\n\nimg,\nvideo {\n max-width: 100%;\n height: auto;\n}\n\n.container {\n width: 100%;\n}\n\n@media (min-width: 640px) {\n .container {\n max-width: 640px;\n }\n}\n\n@media (min-width: 768px) {\n .container {\n max-width: 768px;\n }\n}\n\n@media (min-width: 1024px) {\n .container {\n max-width: 1024px;\n }\n}\n\n@media (min-width: 1280px) {\n .container {\n max-width: 1280px;\n }\n}\n\n@media (min-width: 1536px) {\n .container {\n max-width: 1536px;\n }\n}\n\n.bg-gray-800 {\n --tw-bg-opacity: 1;\n background-color: rgba(31, 41, 55, var(--tw-bg-opacity));\n}\n\n.border {\n border-width: 1px;\n}\n\n.flex {\n display: flex;\n}\n\n.table {\n display: table;\n}\n\n.grid {\n display: grid;\n}\n\n.hidden {\n display: none;\n}\n\n.flex-row {\n flex-direction: row;\n}\n\n.flex-col {\n flex-direction: column;\n}\n\n.flex-grow {\n flex-grow: 1;\n}\n\n.flex-shrink {\n flex-shrink: 1;\n}\n\n.list-disc {\n list-style-type: disc;\n}\n\n.absolute {\n position: absolute;\n}\n\n.resize {\n resize: both;\n}\n\n* {\n --tw-shadow: 0 0 #0000;\n}\n\n* {\n --tw-ring-inset: var(--tw-empty,/*!*/ /*!*/);\n --tw-ring-offset-width: 0px;\n --tw-ring-offset-color: #fff;\n --tw-ring-color: rgba(59, 130, 246, 0.5);\n --tw-ring-offset-shadow: 0 0 #0000;\n --tw-ring-shadow: 0 0 #0000;\n}\n\n.table-auto {\n table-layout: auto;\n}\n\n.line-through {\n text-decoration: line-through;\n}\n\n.w-full {\n width: 100%;\n}\n\n.gap-4 {\n gap: 1rem;\n}\n\n.grid-cols-3 {\n grid-template-columns: repeat(3, minmax(0, 1fr));\n}\n\n.transform {\n --tw-translate-x: 0;\n --tw-translate-y: 0;\n --tw-rotate: 0;\n --tw-skew-x: 0;\n --tw-skew-y: 0;\n --tw-scale-x: 1;\n --tw-scale-y: 1;\n transform: translateX(var(--tw-translate-x)) translateY(var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y));\n}\n\n.transition {\n transition-property: background-color, border-color, color, fill, stroke, opacity, box-shadow, transform;\n transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);\n transition-duration: 150ms;\n}\n\n@-webkit-keyframes spin {\n to {\n transform: rotate(360deg);\n }\n}\n\n@keyframes spin {\n to {\n transform: rotate(360deg);\n }\n}\n\n@-webkit-keyframes ping {\n 75%, 100% {\n transform: scale(2);\n opacity: 0;\n }\n}\n\n@keyframes ping {\n 75%, 100% {\n transform: scale(2);\n opacity: 0;\n }\n}\n\n@-webkit-keyframes pulse {\n 50% {\n opacity: .5;\n }\n}\n\n@keyframes pulse {\n 50% {\n opacity: .5;\n }\n}\n\n@-webkit-keyframes bounce {\n 0%, 100% {\n transform: translateY(-25%);\n -webkit-animation-timing-function: cubic-bezier(0.8,0,1,1);\n animation-timing-function: cubic-bezier(0.8,0,1,1);\n }\n\n 50% {\n transform: none;\n -webkit-animation-timing-function: cubic-bezier(0,0,0.2,1);\n animation-timing-function: cubic-bezier(0,0,0.2,1);\n }\n}\n\n@keyframes bounce {\n 0%, 100% {\n transform: translateY(-25%);\n -webkit-animation-timing-function: cubic-bezier(0.8,0,1,1);\n animation-timing-function: cubic-bezier(0.8,0,1,1);\n }\n\n 50% {\n transform: none;\n -webkit-animation-timing-function: cubic-bezier(0,0,0.2,1);\n animation-timing-function: cubic-bezier(0,0,0.2,1);\n }\n}\n\n:root {\n --bg-color: #131720;\n --text-color: #d9e1f2;\n --primary: hsl(255, 30%, 55%);\n --focus: hsl(210, 90%, 50%);\n --secondary: #42b983;\n --border-color: hsla(0, 0%, 100%, 0.2);\n --light-1: hsl(210, 30%, 40%);\n --light-2: hsl(255, 30%, 50%);\n --light-background: linear-gradient(to right, var(--light-1), var(--light-2));\n --shadow-1: hsla(236, 50%, 50%, 0.3);\n --shadow-2: hsla(236, 50%, 50%, 0.4);\n --hud-background: rgba(0, 0, 0, 0.4);\n --hud-text-color: var(--text-color);\n --notifications-bg: darkslateblue;\n --skills-text-background: rgba(0, 0, 0, 0.5);\n --skills-text-color: var(--text-color);\n --skills-level-background: rgba(0, 0, 0, 0.5);\n --skills-level-color: orange;\n}\n\n.list-item {\n display: inline-block;\n margin-right: 10px;\n}\n\n.list-enter-active,\r\n.list-leave-active {\n transition: all 0.3s ease;\n}\n\n/* .list-move {\r\n transition: transform 0.3s ease;\r\n} */\n\n.list-enter-from,\r\n.list-leave-to {\n opacity: 0;\n transform: translateX(300px);\n}\n\n.notification-item {\n display: inline-block;\n margin-right: 10px;\n}\n\n.notification-enter-active,\r\n.notification-leave-active {\n transition: all 0.3s ease;\n}\n\n/* .notification-move {\r\n transition: transform 0.3s ease;\r\n} */\n\n.notification-enter-from,\r\n.notification-leave-to {\n opacity: 0;\n transform: translateY(-300px);\n}\n\n.fade-enter-active,\r\n.fade-leave-active {\n transition: opacity 0.3s ease;\n}\n\n.fade-enter-from,\r\n.fade-leave-to {\n opacity: 0;\n}\n\n.fade-in-enter-active {\n transition: opacity 0.1s ease;\n}\n\n.fade-in-enter-from {\n opacity: 0;\n}\n\nbody {\n padding: 0;\n margin: 0;\n font-family: Arial, sans-serif;\n background-color: black;\n}\n\n.select {\n background: var(--light-background);\n padding: 10px;\n}\n\n.option {\n background-color: var(--light-2);\n padding: 5px;\n color: var(--text-color);\n}\n\n.button {\n background: var(--light-background);\n color: var(--text-color);\n box-shadow: 0.4rem 0.4rem 2.4rem 0.2rem var(--shadow-1);\n border-radius: 100px;\n padding: 10px;\n font-weight: 800;\n font-size: 16px;\n margin: 10px;\n transition: 0.2s;\n}\n\n.button:focus,\r\n.button:hover {\n transform: translateY(-0.2rem);\n box-shadow: 0 0 2.4rem 0.2rem var(--shadow-2);\n}\n\n.input {\n background: var(--light-background);\n color: var(--text-color);\n box-shadow: 0.4rem 0.4rem 2.4rem 0.2rem var(--shadow-1);\n border-radius: 100px;\n padding: 10px;\n font-weight: 800;\n font-size: 16px;\n margin: 10px;\n transition: 0.2s;\n}\n\n.input:focus,\r\n.input:hover {\n transform: translateY(-0.2rem);\n box-shadow: 0 0 2.4rem 0.2rem var(--shadow-2);\n}\n\na {\n color: pink;\n text-decoration: underline;\n}\n\nth,\r\ntd {\n padding: 4px;\n border: 1px solid var(--text-color);\n text-align: center;\n}\n\n#game-holder {\n width: 100vw;\n height: 100vh;\n padding: 0;\n margin: 0;\n top: 0;\n left: 0;\n background-color: black;\n display: flex;\n align-items: center;\n justify-content: center;\n min-height: -webkit-fill-available;\n}\n\n.title {\n font-size: 30px;\n font-weight: 700;\n text-align: center;\n}\n\n.container {\n padding: 20px;\n}\n\nh1,\r\nh2,\r\nh3,\r\nh4 {\n font-weight: 700;\n}\n\nh1 {\n font-size: 30px;\n}\n\nh2 {\n font-size: 26px;\n}\n\nh3 {\n font-size: 24px;\n}\n\nhr.solid {\n border: 1px solid var(--text-color);\n margin-top: 30px;\n margin-bottom: 30px;\n}\n\n.card-1 {\n box-shadow: 0 1px 3px rgba(0, 0, 0, 0.12), 0 1px 2px rgba(0, 0, 0, 0.24);\n transition: all 0.3s cubic-bezier(0.25, 0.8, 0.25, 1);\n}\n\n.card-1:hover {\n box-shadow: 0 14px 28px rgba(0, 0, 0, 0.25), 0 10px 10px rgba(0, 0, 0, 0.22);\n}\n\n.card-2 {\n box-shadow: 0 3px 6px rgba(0, 0, 0, 0.16), 0 3px 6px rgba(0, 0, 0, 0.23);\n}\n\n.card-3 {\n box-shadow: 0 10px 20px rgba(0, 0, 0, 0.19), 0 6px 6px rgba(0, 0, 0, 0.23);\n}\n\n.card-4 {\n box-shadow: 0 14px 28px rgba(0, 0, 0, 0.25), 0 10px 10px rgba(0, 0, 0, 0.22);\n}\n\n.card-5 {\n box-shadow: 0 19px 38px rgba(0, 0, 0, 0.3), 0 15px 12px rgba(0, 0, 0, 0.22);\n}\n\n@media (min-width: 640px) {\n}\n\n@media (min-width: 768px) {\n}\n\n@media (min-width: 1024px) {\n}\n\n@media (min-width: 1280px) {\n}\n\n@media (min-width: 1536px) {\n}\r\n";
33
+ var css_248z = "/*! @import */\n\n/*! modern-normalize v1.0.0 | MIT License | https://github.com/sindresorhus/modern-normalize */\n\n/*\nDocument\n========\n*/\n\n/**\nUse a better box model (opinionated).\n*/\n\n*,\n*::before,\n*::after {\n box-sizing: border-box;\n}\n\n/**\nUse a more readable tab size (opinionated).\n*/\n\n:root {\n -moz-tab-size: 4;\n -o-tab-size: 4;\n tab-size: 4;\n}\n\n/**\n1. Correct the line height in all browsers.\n2. Prevent adjustments of font size after orientation changes in iOS.\n*/\n\nhtml {\n line-height: 1.15; /* 1 */\n -webkit-text-size-adjust: 100%; /* 2 */\n}\n\n/*\nSections\n========\n*/\n\n/**\nRemove the margin in all browsers.\n*/\n\nbody {\n margin: 0;\n}\n\n/**\nImprove consistency of default fonts in all browsers. (https://github.com/sindresorhus/modern-normalize/issues/3)\n*/\n\nbody {\n font-family:\n\t\tsystem-ui,\n\t\t-apple-system, /* Firefox supports this but not yet `system-ui` */\n\t\t'Segoe UI',\n\t\tRoboto,\n\t\tHelvetica,\n\t\tArial,\n\t\tsans-serif,\n\t\t'Apple Color Emoji',\n\t\t'Segoe UI Emoji';\n}\n\n/*\nGrouping content\n================\n*/\n\n/**\n1. Add the correct height in Firefox.\n2. Correct the inheritance of border color in Firefox. (https://bugzilla.mozilla.org/show_bug.cgi?id=190655)\n*/\n\nhr {\n height: 0; /* 1 */\n color: inherit; /* 2 */\n}\n\n/*\nText-level semantics\n====================\n*/\n\n/**\nAdd the correct text decoration in Chrome, Edge, and Safari.\n*/\n\nabbr[title] {\n -webkit-text-decoration: underline dotted;\n text-decoration: underline dotted;\n}\n\n/**\nAdd the correct font weight in Edge and Safari.\n*/\n\nb,\nstrong {\n font-weight: bolder;\n}\n\n/**\n1. Improve consistency of default fonts in all browsers. (https://github.com/sindresorhus/modern-normalize/issues/3)\n2. Correct the odd 'em' font sizing in all browsers.\n*/\n\ncode,\nkbd,\nsamp,\npre {\n font-family:\n\t\tui-monospace,\n\t\tSFMono-Regular,\n\t\tConsolas,\n\t\t'Liberation Mono',\n\t\tMenlo,\n\t\tmonospace; /* 1 */\n font-size: 1em; /* 2 */\n}\n\n/**\nAdd the correct font size in all browsers.\n*/\n\nsmall {\n font-size: 80%;\n}\n\n/**\nPrevent 'sub' and 'sup' elements from affecting the line height in all browsers.\n*/\n\nsub,\nsup {\n font-size: 75%;\n line-height: 0;\n position: relative;\n vertical-align: baseline;\n}\n\nsub {\n bottom: -0.25em;\n}\n\nsup {\n top: -0.5em;\n}\n\n/*\nTabular data\n============\n*/\n\n/**\n1. Remove text indentation from table contents in Chrome and Safari. (https://bugs.chromium.org/p/chromium/issues/detail?id=999088, https://bugs.webkit.org/show_bug.cgi?id=201297)\n2. Correct table border color inheritance in all Chrome and Safari. (https://bugs.chromium.org/p/chromium/issues/detail?id=935729, https://bugs.webkit.org/show_bug.cgi?id=195016)\n*/\n\ntable {\n text-indent: 0; /* 1 */\n border-color: inherit; /* 2 */\n}\n\n/*\nForms\n=====\n*/\n\n/**\n1. Change the font styles in all browsers.\n2. Remove the margin in Firefox and Safari.\n*/\n\nbutton,\ninput,\noptgroup,\nselect,\ntextarea {\n font-family: inherit; /* 1 */\n font-size: 100%; /* 1 */\n line-height: 1.15; /* 1 */\n margin: 0; /* 2 */\n}\n\n/**\nRemove the inheritance of text transform in Edge and Firefox.\n1. Remove the inheritance of text transform in Firefox.\n*/\n\nbutton,\nselect { /* 1 */\n text-transform: none;\n}\n\n/**\nCorrect the inability to style clickable types in iOS and Safari.\n*/\n\nbutton,\n[type='button'],\n[type='reset'] {\n -webkit-appearance: button;\n}\n\n/**\nRemove the inner border and padding in Firefox.\n*/\n\n/**\nRestore the focus styles unset by the previous rule.\n*/\n\n/**\nRemove the additional ':invalid' styles in Firefox.\nSee: https://github.com/mozilla/gecko-dev/blob/2f9eacd9d3d995c937b4251a5557d95d494c9be1/layout/style/res/forms.css#L728-L737\n*/\n\n/**\nRemove the padding so developers are not caught out when they zero out 'fieldset' elements in all browsers.\n*/\n\nlegend {\n padding: 0;\n}\n\n/**\nAdd the correct vertical alignment in Chrome and Firefox.\n*/\n\nprogress {\n vertical-align: baseline;\n}\n\n/**\nCorrect the cursor style of increment and decrement buttons in Safari.\n*/\n\n/**\n1. Correct the odd appearance in Chrome and Safari.\n2. Correct the outline style in Safari.\n*/\n\n[type='search'] {\n -webkit-appearance: textfield; /* 1 */\n outline-offset: -2px; /* 2 */\n}\n\n/**\nRemove the inner padding in Chrome and Safari on macOS.\n*/\n\n/**\n1. Correct the inability to style clickable types in iOS and Safari.\n2. Change font properties to 'inherit' in Safari.\n*/\n\n/*\nInteractive\n===========\n*/\n\n/*\nAdd the correct display in Chrome and Safari.\n*/\n\nsummary {\n display: list-item;\n}\n\n/**\n * Manually forked from SUIT CSS Base: https://github.com/suitcss/base\n * A thin layer on top of normalize.css that provides a starting point more\n * suitable for web applications.\n */\n\n/**\n * Removes the default spacing and border for appropriate elements.\n */\n\nblockquote,\ndl,\ndd,\nh1,\nh2,\nh3,\nh4,\nh5,\nh6,\nhr,\nfigure,\np,\npre {\n margin: 0;\n}\n\nbutton {\n background-color: transparent;\n background-image: none;\n}\n\n/**\n * Work around a Firefox/IE bug where the transparent `button` background\n * results in a loss of the default `button` focus styles.\n */\n\nbutton:focus {\n outline: 1px dotted;\n outline: 5px auto -webkit-focus-ring-color;\n}\n\nfieldset {\n margin: 0;\n padding: 0;\n}\n\nol,\nul {\n list-style: none;\n margin: 0;\n padding: 0;\n}\n\n/**\n * Tailwind custom reset styles\n */\n\n/**\n * 1. Use the user's configured `sans` font-family (with Tailwind's default\n * sans-serif font stack as a fallback) as a sane default.\n * 2. Use Tailwind's default \"normal\" line-height so the user isn't forced\n * to override it to ensure consistency even when using the default theme.\n */\n\nhtml {\n font-family: ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, \"Helvetica Neue\", Arial, \"Noto Sans\", sans-serif, \"Apple Color Emoji\", \"Segoe UI Emoji\", \"Segoe UI Symbol\", \"Noto Color Emoji\"; /* 1 */\n line-height: 1.5; /* 2 */\n}\n\n/**\n * Inherit font-family and line-height from `html` so users can set them as\n * a class directly on the `html` element.\n */\n\nbody {\n font-family: inherit;\n line-height: inherit;\n}\n\n/**\n * 1. Prevent padding and border from affecting element width.\n *\n * We used to set this in the html element and inherit from\n * the parent element for everything else. This caused issues\n * in shadow-dom-enhanced elements like <details> where the content\n * is wrapped by a div with box-sizing set to `content-box`.\n *\n * https://github.com/mozdevs/cssremedy/issues/4\n *\n *\n * 2. Allow adding a border to an element by just adding a border-width.\n *\n * By default, the way the browser specifies that an element should have no\n * border is by setting it's border-style to `none` in the user-agent\n * stylesheet.\n *\n * In order to easily add borders to elements by just setting the `border-width`\n * property, we change the default border-style for all elements to `solid`, and\n * use border-width to hide them instead. This way our `border` utilities only\n * need to set the `border-width` property instead of the entire `border`\n * shorthand, making our border utilities much more straightforward to compose.\n *\n * https://github.com/tailwindcss/tailwindcss/pull/116\n */\n\n*,\n::before,\n::after {\n box-sizing: border-box; /* 1 */\n border-width: 0; /* 2 */\n border-style: solid; /* 2 */\n border-color: #e5e7eb; /* 2 */\n}\n\n/*\n * Ensure horizontal rules are visible by default\n */\n\nhr {\n border-top-width: 1px;\n}\n\n/**\n * Undo the `border-style: none` reset that Normalize applies to images so that\n * our `border-{width}` utilities have the expected effect.\n *\n * The Normalize reset is unnecessary for us since we default the border-width\n * to 0 on all elements.\n *\n * https://github.com/tailwindcss/tailwindcss/issues/362\n */\n\nimg {\n border-style: solid;\n}\n\ntextarea {\n resize: vertical;\n}\n\ninput::-moz-placeholder, textarea::-moz-placeholder {\n color: #9ca3af;\n}\n\ninput:-ms-input-placeholder, textarea:-ms-input-placeholder {\n color: #9ca3af;\n}\n\ninput::placeholder,\ntextarea::placeholder {\n color: #9ca3af;\n}\n\nbutton {\n cursor: pointer;\n}\n\ntable {\n border-collapse: collapse;\n}\n\nh1,\nh2,\nh3,\nh4,\nh5,\nh6 {\n font-size: inherit;\n font-weight: inherit;\n}\n\n/**\n * Reset links to optimize for opt-in styling instead of\n * opt-out.\n */\n\na {\n color: inherit;\n text-decoration: inherit;\n}\n\n/**\n * Reset form element properties that are easy to forget to\n * style explicitly so you don't inadvertently introduce\n * styles that deviate from your design system. These styles\n * supplement a partial reset that is already applied by\n * normalize.css.\n */\n\nbutton,\ninput,\noptgroup,\nselect,\ntextarea {\n padding: 0;\n line-height: inherit;\n color: inherit;\n}\n\n/**\n * Use the configured 'mono' font family for elements that\n * are expected to be rendered with a monospace font, falling\n * back to the system monospace stack if there is no configured\n * 'mono' font family.\n */\n\npre,\ncode,\nkbd,\nsamp {\n font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, \"Liberation Mono\", \"Courier New\", monospace;\n}\n\n/**\n * Make replaced elements `display: block` by default as that's\n * the behavior you want almost all of the time. Inspired by\n * CSS Remedy, with `svg` added as well.\n *\n * https://github.com/mozdevs/cssremedy/issues/14\n */\n\nimg,\nsvg,\nvideo,\ncanvas,\naudio,\niframe,\nembed,\nobject {\n display: block;\n vertical-align: middle;\n}\n\n/**\n * Constrain images and videos to the parent width and preserve\n * their instrinsic aspect ratio.\n *\n * https://github.com/mozdevs/cssremedy/issues/14\n */\n\nimg,\nvideo {\n max-width: 100%;\n height: auto;\n}\n\n.container {\n width: 100%;\n}\n\n@media (min-width: 640px) {\n .container {\n max-width: 640px;\n }\n}\n\n@media (min-width: 768px) {\n .container {\n max-width: 768px;\n }\n}\n\n@media (min-width: 1024px) {\n .container {\n max-width: 1024px;\n }\n}\n\n@media (min-width: 1280px) {\n .container {\n max-width: 1280px;\n }\n}\n\n@media (min-width: 1536px) {\n .container {\n max-width: 1536px;\n }\n}\n\n.bg-gray-800 {\n --tw-bg-opacity: 1;\n background-color: rgba(31, 41, 55, var(--tw-bg-opacity));\n}\n\n.border {\n border-width: 1px;\n}\n\n.flex {\n display: flex;\n}\n\n.table {\n display: table;\n}\n\n.grid {\n display: grid;\n}\n\n.hidden {\n display: none;\n}\n\n.flex-row {\n flex-direction: row;\n}\n\n.flex-col {\n flex-direction: column;\n}\n\n.flex-grow {\n flex-grow: 1;\n}\n\n.flex-shrink {\n flex-shrink: 1;\n}\n\n.list-disc {\n list-style-type: disc;\n}\n\n.absolute {\n position: absolute;\n}\n\n.resize {\n resize: both;\n}\n\n* {\n --tw-shadow: 0 0 #0000;\n}\n\n* {\n --tw-ring-inset: var(--tw-empty,/*!*/ /*!*/);\n --tw-ring-offset-width: 0px;\n --tw-ring-offset-color: #fff;\n --tw-ring-color: rgba(59, 130, 246, 0.5);\n --tw-ring-offset-shadow: 0 0 #0000;\n --tw-ring-shadow: 0 0 #0000;\n}\n\n.table-auto {\n table-layout: auto;\n}\n\n.line-through {\n text-decoration: line-through;\n}\n\n.w-full {\n width: 100%;\n}\n\n.gap-4 {\n gap: 1rem;\n}\n\n.grid-cols-3 {\n grid-template-columns: repeat(3, minmax(0, 1fr));\n}\n\n.transform {\n --tw-translate-x: 0;\n --tw-translate-y: 0;\n --tw-rotate: 0;\n --tw-skew-x: 0;\n --tw-skew-y: 0;\n --tw-scale-x: 1;\n --tw-scale-y: 1;\n transform: translateX(var(--tw-translate-x)) translateY(var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y));\n}\n\n.transition {\n transition-property: background-color, border-color, color, fill, stroke, opacity, box-shadow, transform;\n transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);\n transition-duration: 150ms;\n}\n\n@-webkit-keyframes spin {\n to {\n transform: rotate(360deg);\n }\n}\n\n@keyframes spin {\n to {\n transform: rotate(360deg);\n }\n}\n\n@-webkit-keyframes ping {\n 75%, 100% {\n transform: scale(2);\n opacity: 0;\n }\n}\n\n@keyframes ping {\n 75%, 100% {\n transform: scale(2);\n opacity: 0;\n }\n}\n\n@-webkit-keyframes pulse {\n 50% {\n opacity: .5;\n }\n}\n\n@keyframes pulse {\n 50% {\n opacity: .5;\n }\n}\n\n@-webkit-keyframes bounce {\n 0%, 100% {\n transform: translateY(-25%);\n -webkit-animation-timing-function: cubic-bezier(0.8,0,1,1);\n animation-timing-function: cubic-bezier(0.8,0,1,1);\n }\n\n 50% {\n transform: none;\n -webkit-animation-timing-function: cubic-bezier(0,0,0.2,1);\n animation-timing-function: cubic-bezier(0,0,0.2,1);\n }\n}\n\n@keyframes bounce {\n 0%, 100% {\n transform: translateY(-25%);\n -webkit-animation-timing-function: cubic-bezier(0.8,0,1,1);\n animation-timing-function: cubic-bezier(0.8,0,1,1);\n }\n\n 50% {\n transform: none;\n -webkit-animation-timing-function: cubic-bezier(0,0,0.2,1);\n animation-timing-function: cubic-bezier(0,0,0.2,1);\n }\n}\n\n:root {\n --bg-color: #131720;\n --text-color: #d9e1f2;\n --primary: hsl(255, 30%, 55%);\n --focus: hsl(210, 90%, 50%);\n --secondary: #42b983;\n --border-color: hsla(0, 0%, 100%, 0.2);\n --light-1: hsl(210, 30%, 40%);\n --light-2: hsl(255, 30%, 50%);\n --light-background: linear-gradient(to right, var(--light-1), var(--light-2));\n --shadow-1: hsla(236, 50%, 50%, 0.3);\n --shadow-2: hsla(236, 50%, 50%, 0.4);\n --hud-background: rgba(0, 0, 0, 0.4);\n --hud-text-color: var(--text-color);\n --notifications-bg: darkslateblue;\n --skills-text-background: rgba(0, 0, 0, 0.5);\n --skills-text-color: var(--text-color);\n --skills-level-background: rgba(0, 0, 0, 0.5);\n --skills-level-color: orange;\n --skills-xp-bar-height: 40px;\n --skill-check-name-color: orange;\n --skill-check-difficulty: orange;\n --skill-check-success: green;\n --skill-check-failed: red;\n --skill-check-color: orange;\n --dialog-choice-color: orange;\n --dialog-choice-hover-color: var(--text-color);\n}\n\n.list-item {\n display: inline-block;\n margin-right: 10px;\n}\n\n.list-enter-active,\r\n.list-leave-active {\n transition: all 0.3s ease;\n}\n\n/* .list-move {\r\n transition: transform 0.3s ease;\r\n} */\n\n.list-enter-from,\r\n.list-leave-to {\n opacity: 0;\n transform: translateX(300px);\n}\n\n.notification-item {\n display: inline-block;\n margin-right: 10px;\n}\n\n.notification-enter-active,\r\n.notification-leave-active {\n transition: all 0.3s ease;\n}\n\n/* .notification-move {\r\n transition: transform 0.3s ease;\r\n} */\n\n.notification-enter-from,\r\n.notification-leave-to {\n opacity: 0;\n transform: translateY(-300px);\n}\n\n.fade-enter-active,\r\n.fade-leave-active {\n transition: opacity 0.3s ease;\n}\n\n.fade-enter-from,\r\n.fade-leave-to {\n opacity: 0;\n}\n\n.fade-in-enter-active {\n transition: opacity 0.1s ease;\n}\n\n.fade-in-enter-from {\n opacity: 0;\n}\n\nbody {\n padding: 0;\n margin: 0;\n font-family: Arial, sans-serif;\n background-color: black;\n}\n\n.select {\n background: var(--light-background);\n padding: 10px;\n}\n\n.option {\n background-color: var(--light-2);\n padding: 5px;\n color: var(--text-color);\n}\n\n.button {\n background: var(--light-background);\n color: var(--text-color);\n box-shadow: 0.4rem 0.4rem 2.4rem 0.2rem var(--shadow-1);\n border-radius: 100px;\n padding: 10px;\n font-weight: 800;\n font-size: 16px;\n margin: 10px;\n transition: 0.2s;\n}\n\n.button:focus,\r\n.button:hover {\n transform: translateY(-0.2rem);\n box-shadow: 0 0 2.4rem 0.2rem var(--shadow-2);\n}\n\n.input {\n background: var(--light-background);\n color: var(--text-color);\n box-shadow: 0.4rem 0.4rem 2.4rem 0.2rem var(--shadow-1);\n border-radius: 100px;\n padding: 10px;\n font-weight: 800;\n font-size: 16px;\n margin: 10px;\n transition: 0.2s;\n}\n\n.input:focus,\r\n.input:hover {\n transform: translateY(-0.2rem);\n box-shadow: 0 0 2.4rem 0.2rem var(--shadow-2);\n}\n\na {\n color: pink;\n text-decoration: underline;\n}\n\nth,\r\ntd {\n padding: 4px;\n border: 1px solid var(--text-color);\n text-align: center;\n}\n\n#game-holder {\n width: 100vw;\n height: 100vh;\n padding: 0;\n margin: 0;\n top: 0;\n left: 0;\n background-color: black;\n display: flex;\n align-items: center;\n justify-content: center;\n min-height: -webkit-fill-available;\n}\n\n.title {\n font-size: 30px;\n font-weight: 700;\n text-align: center;\n}\n\n.container {\n padding: 20px;\n}\n\nh1,\r\nh2,\r\nh3,\r\nh4 {\n font-weight: 700;\n}\n\nh1 {\n font-size: 30px;\n}\n\nh2 {\n font-size: 26px;\n}\n\nh3 {\n font-size: 24px;\n}\n\nhr.solid {\n border: 1px solid var(--text-color);\n margin-top: 30px;\n margin-bottom: 30px;\n}\n\n.card-1 {\n box-shadow: 0 1px 3px rgba(0, 0, 0, 0.12), 0 1px 2px rgba(0, 0, 0, 0.24);\n transition: all 0.3s cubic-bezier(0.25, 0.8, 0.25, 1);\n}\n\n.card-1:hover {\n box-shadow: 0 14px 28px rgba(0, 0, 0, 0.25), 0 10px 10px rgba(0, 0, 0, 0.22);\n}\n\n.card-2 {\n box-shadow: 0 3px 6px rgba(0, 0, 0, 0.16), 0 3px 6px rgba(0, 0, 0, 0.23);\n}\n\n.card-3 {\n box-shadow: 0 10px 20px rgba(0, 0, 0, 0.19), 0 6px 6px rgba(0, 0, 0, 0.23);\n}\n\n.card-4 {\n box-shadow: 0 14px 28px rgba(0, 0, 0, 0.25), 0 10px 10px rgba(0, 0, 0, 0.22);\n}\n\n.card-5 {\n box-shadow: 0 19px 38px rgba(0, 0, 0, 0.3), 0 15px 12px rgba(0, 0, 0, 0.22);\n}\n\n.dialog-choice {\n transition: 0.2s;\n}\n\n.dialog-choice:hover {\n transform: scale(1.05, 1.05);\n transform-origin: center;\n}\n\n/* Somewhat arcane CSS to force the hover color to override the child style.\r\nOtherwise hovering choices doesn't change the color of skill check prompts. */\n\n.dialog-choice:not(:hover) > .skill-check-name,\r\n.passive-skill-check > .skill-check-name {\n color: var(--skill-check-name-color);\n}\n\n.dialog-choice:not(:hover) > .skill-check-difficulty,\r\n.passive-skill-check > .skill-check-difficulty {\n color: var(--skill-check-difficulty);\n}\n\n.skill-check-difficulty {\n font-weight: 700;\n}\n\n.dialog-choice:not(:hover) > .skill-check-success,\r\n.passive-skill-check > .skill-check-success {\n color: var(--skill-check-success);\n}\n\n.dialog-choice:not(:hover) > .skill-check-failed,\r\n.passive-skill-check > .skill-check-failed {\n color: var(--skill-check-failed);\n}\n\n.dialog-choice:not(:hover) > .skill-check,\r\n.passive-skill-check.skill-check {\n color: var(--skill-check-color);\n}\n\n.narrat-canvas {\n position: absolute;\n height: 100%;\n top: 0;\n left: 0;\n}\n\n/* Looks ugly */\n\n/* @keyframes strike-anim {\r\n 0% {\r\n width: 0;\r\n }\r\n 100% {\r\n width: 100%;\r\n }\r\n}\r\n.strike-anim {\r\n position: relative;\r\n}\r\n.strike-anim::after {\r\n content: ' ';\r\n position: absolute;\r\n top: 50%;\r\n left: 0;\r\n width: 100%;\r\n height: 1px;\r\n background: var(--text-color);\r\n animation-name: strike-anim;\r\n animation-duration: 0.5s;\r\n animation-timing-function: linear;\r\n animation-iteration-count: 1;\r\n animation-fill-mode: forwards;\r\n} */\n\n#touchTrigger {\n pointer-events: none;\n}\n\n@media (min-width: 640px) {\n}\n\n@media (min-width: 768px) {\n}\n\n@media (min-width: 1024px) {\n}\n\n@media (min-width: 1280px) {\n}\n\n@media (min-width: 1536px) {\n}\r\n";
34
34
  styleInject(css_248z);
35
35
 
36
36
  const f=ref([]),v=ref(null),m=ref(null),g=ref(null),h=reactive({current:""}),y=[],b=ref(!1),k=readonly(f),w=readonly(v),M=readonly(m),q=readonly(g),x=readonly(h),$=(e=w.value)=>{h.current=e;const t=k.value.findIndex((t=>t.name===e)),n=k.value.map((e=>e.name));for(let a=0;a<n.length;a++){if(a>0&&a<n.length-1){const e=n[a]+"Minus",r=n[a]+"Plus";h[e]=t<=a,h[r]=t>=a;}h[n[a]]=n[a]===e;}},V=(e=M.value)=>{h.orientation=e,h.isLandscape="landscape"===e,h.isPortrait="portrait"===e;},O=(e=q.value||"light")=>{h.theme=e,h.isDark="dark"===e,h.isLight="light"===e;};function j(e,t){if("undefined"==typeof window||!window.matchMedia)return !1;if("undefined"!=typeof window&&!window.matchMedia)return console.error("Vue3 Mq: No MatchMedia support detected in this browser. Responsive breakpoints not available."),!1;{b.value=!0;const n=window.matchMedia(e),a=({matches:e})=>{e&&t();};y.push({mql:n,cb:a});n.addEventListener&&"function"==typeof n.addEventListener?n.addEventListener("change",a):n.addListener(a),a(n);}}var L=Object.freeze({__proto__:null,[Symbol.toStringTag]:"Module",bootstrap5:{xs:0,sm:576,md:768,lg:992,xl:1200,xxl:1400},bootstrap4:{xs:0,sm:576,md:768,lg:992,xl:1200},bootstrap3:{xs:0,sm:768,md:992,lg:1200},vuetify:{xs:0,sm:600,md:960,lg:1264,xl:1904},tailwind:{xs:0,sm:640,md:768,lg:1024,xl:1280,xxl:1536},devices:{phone:0,tablet:768,laptop:1370,desktop:1906}});const T=e=>{if(!e||"object"!=typeof e)return !1;const t=[];for(let n in e){const a=parseFloat(e[n]);n&&"string"==typeof n?/^[^a-z]/i.test(n)||/[^a-zA-Z0-9_]/.test(n)?console.warn(`Vue3 Mq: "${n}" is an invalid breakpoint key. Breakpoint keys must start with a letter and contain only alphanumeric characters and underscores. Skipping.`):!a&&0!==a||isNaN(a)||a<0?console.warn(`Vue3 Mq: "${n}: ${e[n]}" is not a valid breakpoint. Breakpoints should be a number of zero or above. Skipping.`):t.push({name:n,min:a}):console.warn(`Vue3 Mq: Invalid or missing breakpoint key (${JSON.stringify(n)}). Skipping.`);}t.some((e=>0===e.min))||console.warn("Vue3 Mq: You have not declared a breakpoint with a minimum value of 0. There may be screen sizes to which Vue3Mq does not respond.");return new Set(t.map((e=>e.min))).size<t.length&&console.warn("Vue3 Mq: Your breakpoint configuration contains duplicate values. Behaviour may be unpredictable."),0!==t.length&&t.sort(((e,t)=>e.min-t.min))};function R({breakpoints:e,preset:t}){const n=(e=>{if("string"==typeof e&&L[e])return L[e];{const t=Object.keys(L);return console.error(`Vue3 Mq: "${e}" is not a valid preset. Available options are: ${t.join(", ")}`),!1}})(t),a=T(e);if(!1===n&&!a)throw new TypeError("Vue3 Mq: You must provide a valid preset, or valid breakpoint settings.");var r;r=a||T(n),f.value=r,function(){for(;y.length>0;){const e=y.shift();if(e&&"object"==typeof e){const{mql:t,cb:n}=e;t.addEventListener&&"function"==typeof t.addEventListener?t.removeEventListener("change",n):t.removeListener(n);}}}(),(()=>{const e=Object.keys(h);for(let t of e)delete h[t];$(),V(),O();})();const o=k.value.reduce(((e,t,n,a)=>{const r=`(min-width: ${t.min}px)`,o=n<a.length-1?`(max-width: ${a[n+1].min-1}px)`:null,i=r+(o?" and "+o:"");return Object.assign(e,{[t.name]:i})}),{});for(const i in o){j(o[i],(()=>{$(i);}));}["portrait","landscape"].forEach((e=>{j(`(orientation: ${e})`,(()=>{V(e);}));})),["light","dark"].forEach((e=>{j(`(prefers-color-scheme: ${e})`,(()=>{O(e);}));}));}var N={install:(e,{preset:t="bootstrap5",breakpoints:n,defaultBreakpoint:a,defaultOrientation:r="landscape",defaultTheme:o}={})=>{try{const l=!1===["landscape","portrait"].includes(s=r)?(console.error(`Vue3 Mq: "${s}" is not a valid default orientation. Reverting to unset value.`),null):s,u=((e=null)=>!1===["dark","light"].includes(e)&&null!==e?(console.error(`Vue3 Mq: "${e}" is not a valid default theme. Reverting to unset value.`),null):e)(o);i=a,v.value=i,(e=>{m.value=e;})(l),(e=>{g.value=e;})(u),e.provide("mq",x),e.provide("updateBreakpoints",R),R({breakpoints:n,preset:t});}catch(l){console.error(l);}var i,s;}};
@@ -135,7 +135,7 @@ var script = defineComponent({
135
135
  dialogBoxStyle() {
136
136
  const style = getCharacterStyle(this.options.styleId);
137
137
  const css = {
138
- opacity: this.options.old ? '0.5' : '1',
138
+ opacity: this.options.old ? '0.7' : '1',
139
139
  };
140
140
  if (!this.options.title) {
141
141
  css.marginTop = '-20px';
@@ -158,7 +158,10 @@ var script = defineComponent({
158
158
  return undefined;
159
159
  },
160
160
  canInteract() {
161
- return this.active && !this.passed && this.options.interactive;
161
+ return (this.active &&
162
+ !this.passed &&
163
+ this.options.interactive &&
164
+ !this.$store.state.paused);
162
165
  },
163
166
  },
164
167
  methods: {
@@ -174,6 +177,11 @@ var script = defineComponent({
174
177
  }
175
178
  return style;
176
179
  },
180
+ dialogClass(choice) {
181
+ if (!choice.allowed) {
182
+ return 'strike-anim';
183
+ }
184
+ },
177
185
  },
178
186
  });
179
187
 
@@ -221,10 +229,10 @@ function render(_ctx, _cache, $props, $setup, $data, $options) {
221
229
  return (openBlock(), createElementBlock("p", {
222
230
  key: index,
223
231
  style: normalizeStyle(_ctx.dialogStyle(choice)),
232
+ class: normalizeClass([_ctx.dialogClass(choice), "dialog-choice override"]),
224
233
  onClick: $event => (_ctx.chooseOption(choice)),
225
- class: "dialog-choice override",
226
234
  innerHTML: `${index + 1}. –  ${choice.choice}`
227
- }, null, 12, _hoisted_6))
235
+ }, null, 14, _hoisted_6))
228
236
  }), 128))
229
237
  ]))
230
238
  : (_ctx.canInteract)
@@ -239,7 +247,7 @@ function render(_ctx, _cache, $props, $setup, $data, $options) {
239
247
  ], 4))
240
248
  }
241
249
 
242
- var css_248z$1 = ".dialog-title {\n font-size: 20px;\n font-weight: bold;\n}\n\n.dialog-text {\n font-size: 16px;\n}\n\n.dialog-box {\n /* border-radius: 10px; */\n /* border: 1px solid #a8a8a8; */\n color: var(--text-color);\n /* background-color: #2e2e2e; */\n padding: 10px;\n padding-left: 2em;\n margin-bottom: 10px;\n}\n\n.dialog-choice {\n color: orange;\n}\n\n.dialog-choice:hover {\n color: var(--text-color);\n cursor: pointer;\n}\n\n.buttons-container {\n width: 100%;\n padding: 10px;\n display: flex;\n justify-content: space-evenly;\n align-items: stretch;\n box-sizing: border-box;\n}\n\n.interact-button {\n cursor: pointer;\n -webkit-user-select: none;\n -moz-user-select: none;\n -ms-user-select: none;\n user-select: none;\n height: 50px;\n color: var(--text-color);\n border: 1px solid black;\n font-weight: bold;\n font-size: 24px;\n text-align: center;\n flex-grow: 2;\n display: flex;\n align-items: center;\n justify-content: center;\n box-sizing: border-box;\n}\n\n.interact-button:not(:last-child) {\n margin-right: 10px;\n}\r\n";
250
+ var css_248z$1 = ".dialog-title {\n font-size: 20px;\n font-weight: bold;\n}\n\n.dialog-text {\n font-size: 16px;\n}\n\n.dialog-box {\n /* border-radius: 10px; */\n /* border: 1px solid #a8a8a8; */\n color: var(--text-color);\n /* background-color: #2e2e2e; */\n padding: 10px;\n padding-left: 2em;\n margin-bottom: 10px;\n}\n\n.dialog-choice {\n color: var(--dialog-choice-color);\n}\n\n.dialog-choice:hover {\n color: var(--dialog-choice-hover-color);\n cursor: pointer;\n}\n\n.buttons-container {\n width: 100%;\n padding: 10px;\n display: flex;\n justify-content: space-evenly;\n align-items: stretch;\n box-sizing: border-box;\n}\n\n.interact-button {\n cursor: pointer;\n -webkit-user-select: none;\n -moz-user-select: none;\n -ms-user-select: none;\n user-select: none;\n height: 50px;\n color: var(--text-color);\n border: 1px solid black;\n font-weight: bold;\n font-size: 24px;\n text-align: center;\n flex-grow: 2;\n display: flex;\n align-items: center;\n justify-content: center;\n box-sizing: border-box;\n}\n\n.interact-button:not(:last-child) {\n margin-right: 10px;\n}\r\n";
243
251
  styleInject(css_248z$1);
244
252
 
245
253
  script.render = render;
@@ -275,6 +283,9 @@ const defaultConfig = {
275
283
  },
276
284
  buttons: {},
277
285
  skills: {},
286
+ skillOptions: {
287
+ xpPerLevel: 10,
288
+ },
278
289
  skillChecks: {
279
290
  rollRange: 100,
280
291
  skillMultiplier: 10,
@@ -319,8 +330,22 @@ function setConfig(conf) {
319
330
  }
320
331
  function getConfig() {
321
332
  return config$1;
333
+ }
334
+ function getSkillConfig(commit, id) {
335
+ const skill = config$1.skills[id];
336
+ if (!skill) {
337
+ error(commit, `Skill config for skill ${id} doesn't exist`);
338
+ }
339
+ return skill;
322
340
  }
323
341
 
342
+ var config$2 = /*#__PURE__*/Object.freeze({
343
+ __proto__: null,
344
+ setConfig: setConfig,
345
+ getConfig: getConfig,
346
+ getSkillConfig: getSkillConfig
347
+ });
348
+
324
349
  var script$1 = defineComponent({
325
350
  props: {
326
351
  pictureUrl: String,
@@ -367,21 +392,20 @@ styleInject(css_248z$2);
367
392
 
368
393
  script$1.render = render$1;
369
394
 
370
- class Logger {
395
+ class LogManager {
371
396
  debug;
372
- constructor(debug) {
373
- this.debug = debug;
374
- }
375
- setDebug(debug) {
397
+ logger = {
398
+ log: () => { },
399
+ };
400
+ setupDebugger(debug) {
376
401
  this.debug = debug;
377
- }
378
- log(...args) {
379
- if (this.debug) {
380
- console.log(...args);
402
+ if (debug) {
403
+ this.logger.log = console.log.bind(window.console);
381
404
  }
382
405
  }
383
406
  }
384
- const logger = new Logger(false);
407
+ const logManager = new LogManager();
408
+ const logger = logManager.logger;
385
409
 
386
410
  function getPlayTime(startedAt, previousTime) {
387
411
  return Date.now() - startedAt + previousTime;
@@ -2353,19 +2377,21 @@ var script$3 = defineComponent({
2353
2377
  open() {
2354
2378
  this.showDebug = true;
2355
2379
  this.$nextTick(() => {
2356
- const variablesEditor = new xJ({
2380
+ // eslint-disable-next-line no-unused-vars
2381
+ const _variablesEditor = new xJ({
2357
2382
  target: this.$refs.variablesViewer,
2358
2383
  props: {
2359
2384
  content: {
2360
2385
  text: undefined,
2361
2386
  json: this.variables,
2362
2387
  },
2363
- onChange: (updatedContent, previousContent, patchResult) => {
2388
+ onChange: (updatedContent) => {
2364
2389
  this.$store.commit('overrideData', updatedContent.json);
2365
2390
  },
2366
2391
  },
2367
2392
  });
2368
- const stateEditor = new xJ({
2393
+ // eslint-disable-next-line no-unused-vars
2394
+ const _stateEditor = new xJ({
2369
2395
  target: this.$refs.stateViewer,
2370
2396
  props: {
2371
2397
  content: {
@@ -2596,9 +2622,10 @@ function render$3(_ctx, _cache, $props, $setup, $data, $options) {
2596
2622
  (openBlock(true), createElementBlock(Fragment, null, renderList(_ctx.matches, (match, index) => {
2597
2623
  return (openBlock(), createElementBlock("div", {
2598
2624
  class: "search-result",
2599
- style: normalizeStyle(_ctx.getMatchResultStyle(index))
2625
+ style: normalizeStyle(_ctx.getMatchResultStyle(index)),
2626
+ key: index
2600
2627
  }, toDisplayString(match), 5))
2601
- }), 256))
2628
+ }), 128))
2602
2629
  ]))
2603
2630
  : (openBlock(), createElementBlock("div", _hoisted_8, _hoisted_10))
2604
2631
  ]),
@@ -6262,6 +6289,7 @@ howler.Howler.volume(0.5);
6262
6289
  async function loadAudioAssets(config) {
6263
6290
  logger.log(`Loading audio`);
6264
6291
  const loadingPromises = [];
6292
+ howler.Howler.volume(config.audioOptions.volume);
6265
6293
  for (const key in config.music) {
6266
6294
  // Backward compatibility with old music list
6267
6295
  const musicConf = {
@@ -6310,6 +6338,9 @@ async function changeMusic(ctx, newMusic) {
6310
6338
  if (ctx.state.audio.currentMusic) {
6311
6339
  if (oldHowler) {
6312
6340
  oldMusic.fade(oldMusic.volume(), 0, audioOptions.musicFadeOutTime * 1000, oldHowler);
6341
+ setTimeout(() => {
6342
+ oldMusic.stop(oldHowler);
6343
+ }, audioOptions.musicFadeOutTime * 1000);
6313
6344
  }
6314
6345
  }
6315
6346
  if (oldMusic) {
@@ -6342,18 +6373,12 @@ function stopAudio(commit, key) {
6342
6373
  if (sound) {
6343
6374
  sound.stop();
6344
6375
  }
6345
- else {
6346
- error(commit, `Sound effect ${key} not found!`);
6347
- }
6348
6376
  }
6349
6377
  function pauseAudio(commit, key) {
6350
6378
  const sound = getAudio(key);
6351
6379
  if (sound) {
6352
6380
  sound.pause();
6353
6381
  }
6354
- else {
6355
- error(commit, `Sound effect ${key} not found!`);
6356
- }
6357
6382
  }
6358
6383
 
6359
6384
  function debounce(func, waitMilliseconds = 50, options = {}) {
@@ -6408,127 +6433,448 @@ function debounce(func, waitMilliseconds = 50, options = {}) {
6408
6433
  return debouncedFunction;
6409
6434
  }
6410
6435
 
6411
- var script$8 = defineComponent({
6412
- components: {},
6413
- props: {},
6414
- data() {
6415
- return {
6416
- activeMenu: false,
6417
- };
6418
- },
6419
- mounted() {
6420
- document.addEventListener('keydown', (event) => {
6421
- const key = event.key;
6422
- if (key === 'Escape') {
6423
- this.toggleMenu();
6424
- }
6436
+ function createSkillCheckState() {
6437
+ const skillCheck = {
6438
+ passed: false,
6439
+ available: true,
6440
+ };
6441
+ return skillCheck;
6442
+ }
6443
+ function getSkillCheckState(ctx, skillCheckId) {
6444
+ let skillCheck = ctx.state.skillChecks[skillCheckId];
6445
+ if (!skillCheck) {
6446
+ skillCheck = createSkillCheckState();
6447
+ ctx.commit('setupSkillCheck', {
6448
+ skillCheck,
6449
+ skillCheckId,
6425
6450
  });
6426
- },
6427
- methods: {
6428
- openMenu() {
6429
- this.$store.commit('openModal', 'menu');
6430
- },
6431
- openSkills() {
6432
- this.$store.commit('openModal', 'skills');
6433
- },
6434
- closeMenu() {
6435
- this.$store.commit('closeModal');
6436
- },
6437
- toggleMenu() {
6438
- this.$store.commit('toggleMenu');
6439
- },
6440
- },
6441
- computed: {
6442
- showSkills() {
6443
- if (Object.entries(getConfig().skills).length > 0) {
6444
- return true;
6451
+ }
6452
+ return skillCheck;
6453
+ }
6454
+ function getSkillCheckDifficultyScore(value, level) {
6455
+ return value - level * getConfig().skillChecks.skillMultiplier;
6456
+ }
6457
+ function getSkillCheckDifficultyText(value, level) {
6458
+ const difficultyScore = getSkillCheckDifficultyScore(value, level);
6459
+ const checks = getConfig().skillChecks;
6460
+ let found = false;
6461
+ let i = 0;
6462
+ let checkText = checks.difficultyText[0][1];
6463
+ while (!found) {
6464
+ if (checks.difficultyText.length > i) {
6465
+ if (difficultyScore >= checks.difficultyText[i][0]) {
6466
+ checkText = checks.difficultyText[i][1];
6445
6467
  }
6446
- return false;
6447
- },
6448
- },
6449
- });
6450
-
6451
- const _hoisted_1$7 = { class: "menu-container" };
6452
-
6453
- function render$8(_ctx, _cache, $props, $setup, $data, $options) {
6454
- return (openBlock(), createElementBlock("div", _hoisted_1$7, [
6455
- createElementVNode("button", {
6456
- class: "button menu-toggle-button",
6457
- id: "menu-button",
6458
- onClick: _cache[0] || (_cache[0] = (...args) => (_ctx.openMenu && _ctx.openMenu(...args)))
6459
- }, " Menu "),
6460
- (_ctx.showSkills)
6461
- ? (openBlock(), createElementBlock("button", {
6462
- key: 0,
6463
- class: "button menu-toggle-button",
6464
- id: "skills-menu-button",
6465
- onClick: _cache[1] || (_cache[1] = (...args) => (_ctx.openSkills && _ctx.openSkills(...args)))
6466
- }, " Skills "))
6467
- : createCommentVNode("", true)
6468
- ]))
6468
+ else {
6469
+ found = true;
6470
+ }
6471
+ }
6472
+ else {
6473
+ found = true;
6474
+ }
6475
+ i++;
6476
+ }
6477
+ return checkText;
6478
+ }
6479
+ function getSkillCheckText({ context, skill, skillCheckId, value, }) {
6480
+ const skillCheckState = getSkillCheckState(context, skillCheckId);
6481
+ const skillConfig = getSkillConfig(context.commit, skill);
6482
+ const level = context.state.skills[skill].level;
6483
+ const difficultyText = getSkillCheckDifficultyText(value, level);
6484
+ let allowed = true;
6485
+ let text = `<span class='skill-check'>[<span class='skill-check-name'>${skillConfig.name}</span> - `;
6486
+ if (skillCheckState.available) {
6487
+ text += ` <span class='skill-check-difficulty'>${difficultyText}</span>]</span>`;
6488
+ }
6489
+ else if (skillCheckState.passed) {
6490
+ text = '';
6491
+ }
6492
+ else {
6493
+ allowed = false;
6494
+ text += ` <span class='skill-check-difficulty'>${difficultyText}</span> - <span class='skill-check-failed'>FAILED</span>]</span>`;
6495
+ }
6496
+ return {
6497
+ difficultyText: text,
6498
+ allowed,
6499
+ };
6500
+ }
6501
+ function getPassiveSkillCheckText(ctx, success, params) {
6502
+ const skillConf = getSkillConfig(ctx.commit, params.skill);
6503
+ const difficultyText = getSkillCheckDifficultyText(params.value, ctx.state.skills[params.skill].level);
6504
+ return `<span class='passive-skill-check skill-check'>[<span class='skill-check-name'>${skillConf.name}</span> - <span class='skill-check-difficulty'>${difficultyText}</span> - ${success
6505
+ ? '<span class="skill-check-success">Success</span>'
6506
+ : '<span class="skill-check-failed">Failure</span>'}]</span>`;
6507
+ }
6508
+ function calculateSkillCheckRoll(state, skill) {
6509
+ const { skillChecks } = getConfig();
6510
+ const unmodifiedRoll = Math.floor(Math.random() * skillChecks.rollRange);
6511
+ const rollModifier = state.skills[skill].level * skillChecks.skillMultiplier;
6512
+ const roll = unmodifiedRoll + rollModifier;
6513
+ logger.log(`[SKILL CHECK] Roll: ${roll}. (Base roll: ${unmodifiedRoll}, modifier: ${rollModifier} - Skill level: ${state.skills[skill].level})`);
6514
+ return {
6515
+ roll,
6516
+ unmodifiedRoll,
6517
+ };
6518
+ }
6519
+ function resolveSkillCheck(state, params) {
6520
+ const { skills, skillChecks } = getConfig();
6521
+ let success = true;
6522
+ const { roll } = calculateSkillCheckRoll(state, params.skill);
6523
+ if (roll <= skillChecks.failureChance - 1) {
6524
+ success = false;
6525
+ }
6526
+ const skill = skills[params.skill];
6527
+ if (roll < params.value) {
6528
+ success = false;
6529
+ }
6530
+ logger.log(`[SKILL CHECK ${skill.name}]: ${success ? '✅' : '❌'}`, `(${params.id}) - ${roll}/${params.value}`);
6531
+ return success;
6469
6532
  }
6470
6533
 
6471
- var css_248z$9 = ".menu-content {\n text-align: center;\n}\n\n.menu-toggle-button {\n margin: 0;\n padding: 2px;\n border-radius: 5px;\n}\n\n.menu-toggle-button:not(:last-child) {\n margin-right: 10px;\n}\n\n.menu-modal {\n width: 500px;\n}\r\n";
6472
- styleInject(css_248z$9);
6473
-
6474
- script$8.render = render$8;
6475
-
6476
- var script$9 = defineComponent({
6477
- components: {
6478
- Modal: script$2,
6479
- VolumeControls: script$4,
6480
- },
6481
- props: {},
6482
- data() { },
6483
- mounted() { },
6484
- methods: {
6485
- quit() {
6486
- window.close();
6487
- // quit
6488
- },
6489
- mainMenu() {
6490
- this.$store.commit('reset');
6491
- this.$store.commit('setFlowState', 'menu');
6492
- },
6493
- getPlayTimeString() {
6494
- const time = getPlayTime(this.$store.state.playTime.start, this.$store.state.playTime.previousPlaytime);
6495
- return toHHMMSS(time / 1000);
6534
+ function processSkillCheck(ctx, skillcheck) {
6535
+ return runSkillCheck(ctx, {
6536
+ skill: skillcheck.skill,
6537
+ value: skillcheck.value,
6538
+ id: skillcheck.id,
6539
+ success: skillcheck.success.text,
6540
+ failure: skillcheck.failure.text,
6541
+ });
6542
+ }
6543
+ function runSkillCheck(ctx, params) {
6544
+ const { state } = ctx;
6545
+ const success = resolveSkillCheck(state, params);
6546
+ writeText(ctx, getPassiveSkillCheckText(ctx, success, params));
6547
+ if (success) {
6548
+ ctx.commit('passSkillCheck', params.id);
6549
+ return true;
6550
+ }
6551
+ ctx.commit('failSkillCheck', params.id);
6552
+ return false;
6553
+ }
6554
+ function runConditionCommand(ctx, command) {
6555
+ const options = command.options;
6556
+ const result = runCondition(ctx, options.condition);
6557
+ logger.log(result);
6558
+ if (result) {
6559
+ return options.success;
6560
+ }
6561
+ if (!result && options.failure) {
6562
+ return options.failure;
6563
+ }
6564
+ return undefined;
6565
+ }
6566
+ function writeText(ctx, text) {
6567
+ const dialog = {
6568
+ speaker: 'game',
6569
+ text,
6570
+ interactive: false,
6571
+ };
6572
+ ctx.commit('addDialog', {
6573
+ dialog,
6574
+ });
6575
+ }
6576
+ function runCondition(ctx, condition) {
6577
+ return conditionFunction(ctx, condition);
6578
+ }
6579
+ function conditionFunction(ctx, condition) {
6580
+ const { state } = ctx;
6581
+ const context = {
6582
+ data: state.machine.data,
6583
+ skills: state.skills,
6584
+ skillChecks: state.skillChecks,
6585
+ stats: state.hudStats,
6586
+ roll: (checkId, skill, value) => {
6587
+ const skillCheckState = getSkillCheckState(ctx, checkId);
6588
+ if (skillCheckState) {
6589
+ if (skillCheckState.passed) {
6590
+ return true;
6591
+ }
6592
+ if (!skillCheckState.available) {
6593
+ return false;
6594
+ }
6595
+ }
6596
+ return runSkillCheck(ctx, {
6597
+ skill,
6598
+ value,
6599
+ id: checkId,
6600
+ });
6496
6601
  },
6497
- },
6498
- computed: {},
6499
- });
6500
-
6501
- const _hoisted_1$8 = /*#__PURE__*/createElementVNode("h3", { class: "title" }, "Menu", -1);
6502
- const _hoisted_2$6 = { class: "menu-content" };
6503
-
6504
- function render$9(_ctx, _cache, $props, $setup, $data, $options) {
6505
- const _component_VolumeControls = resolveComponent("VolumeControls");
6506
- const _component_modal = resolveComponent("modal");
6602
+ };
6603
+ return evalInContext(`(${condition})`, context);
6604
+ }
6605
+ function evalInContext(script, context) {
6606
+ // # Return the results of the in-line anonymous function we .call with the passed context
6607
+ return function () {
6608
+ return eval(script);
6609
+ }.call(context);
6610
+ }
6611
+ async function textCommand(commit, dialog) {
6612
+ commit('addDialog', {
6613
+ dialog,
6614
+ });
6615
+ }
6616
+ function getLine(lines, index) {
6617
+ if (index < lines.length)
6618
+ return lines[index];
6619
+ }
6507
6620
 
6508
- return (openBlock(), createBlock(_component_modal, {
6509
- class: "menu",
6510
- onClose: _cache[2] || (_cache[2] = $event => (_ctx.$emit('close'))),
6511
- containerCssClass: "menu-modal"
6512
- }, {
6513
- header: withCtx(() => [
6514
- _hoisted_1$8
6515
- ]),
6516
- body: withCtx(() => [
6517
- createElementVNode("div", _hoisted_2$6, [
6518
- createElementVNode("h3", null, "Play time: " + toDisplayString(_ctx.getPlayTimeString()), 1),
6519
- createVNode(_component_VolumeControls),
6520
- createElementVNode("button", {
6521
- class: "button title quit-button",
6522
- onClick: _cache[0] || (_cache[0] = (...args) => (_ctx.mainMenu && _ctx.mainMenu(...args)))
6523
- }, " Main Menu "),
6524
- createElementVNode("button", {
6621
+ async function runLine(context) {
6622
+ const cmd = context.getters.currentLine;
6623
+ await runCommand(context, cmd);
6624
+ }
6625
+ class VM$1 {
6626
+ plugins = [];
6627
+ commands = {};
6628
+ addCommand(command) {
6629
+ this.commands[command.keyword] = command;
6630
+ }
6631
+ addPlugin(plugin) {
6632
+ this.plugins.push(plugin);
6633
+ if (plugin.customCommands) {
6634
+ for (const command of plugin.customCommands) {
6635
+ this.addCommand(command);
6636
+ }
6637
+ }
6638
+ }
6639
+ callHook(hookName, ...args) {
6640
+ for (const plugin of this.plugins) {
6641
+ if (typeof plugin[hookName] === 'function') {
6642
+ plugin[hookName](...args);
6643
+ }
6644
+ }
6645
+ }
6646
+ }
6647
+ const vm$1 = new VM$1();
6648
+ async function runCommand(context, cmd, choices) {
6649
+ const { state, commit } = context;
6650
+ try {
6651
+ const commandPlugin = vm$1.commands[cmd.commandType];
6652
+ if (commandPlugin) {
6653
+ commandPlugin.runner(context, cmd, choices);
6654
+ }
6655
+ }
6656
+ catch (err) {
6657
+ logger.log(`Error at: `, state.machine.stack[state.machine.stack.length - 1].label);
6658
+ console.error(err);
6659
+ error(commit, `Narrat script runtime error at <span class="error-filename">${cmd.fileName}:${cmd.line + 1}</span>
6660
+ <b>${err}</b>
6661
+ Script: ${cmd.code}
6662
+ Label: ${state.machine.stack[state.machine.stack.length - 1].label}`);
6663
+ }
6664
+ }
6665
+ async function playerAnswered(context, choiceIndex) {
6666
+ const { commit, dispatch } = context;
6667
+ const cmd = context.getters.currentLine;
6668
+ switch (cmd.commandType) {
6669
+ case 'choice':
6670
+ const options = cmd.options;
6671
+ const choice = options.choices[choiceIndex];
6672
+ let playerText = choice.choice;
6673
+ let newBranch;
6674
+ const skillcheck = choice.skillCheck;
6675
+ if (skillcheck) {
6676
+ const skillCheckState = getSkillCheckState(context, choice.skillCheck.id);
6677
+ if (skillCheckState.passed) {
6678
+ newBranch = skillcheck.success.branch;
6679
+ playerText = skillcheck.success.text;
6680
+ }
6681
+ else {
6682
+ const result = processSkillCheck(context, skillcheck);
6683
+ const winner = result ? skillcheck.success : skillcheck.failure;
6684
+ newBranch = winner.branch;
6685
+ playerText = undefined;
6686
+ }
6687
+ }
6688
+ else {
6689
+ newBranch = choice.branch;
6690
+ }
6691
+ if (playerText) {
6692
+ // If the choice involves printing a player dialog, show it
6693
+ const dialog = {
6694
+ speaker: 'player',
6695
+ text: playerText,
6696
+ interactive: false,
6697
+ };
6698
+ commit('addDialog', { dialog });
6699
+ }
6700
+ if (newBranch) {
6701
+ const newStack = {
6702
+ currentIndex: 0,
6703
+ branch: newBranch,
6704
+ };
6705
+ commit('addStack', newStack);
6706
+ dispatch('runLine');
6707
+ }
6708
+ else {
6709
+ dispatch('nextLine');
6710
+ }
6711
+ break;
6712
+ default:
6713
+ dispatch('nextLine');
6714
+ }
6715
+ }
6716
+ async function nextLine(ctx) {
6717
+ const { state, getters, dispatch, commit } = ctx;
6718
+ if (state.machine.stack.length === 0) {
6719
+ finishGame(ctx);
6720
+ return;
6721
+ }
6722
+ const machineHead = getters.machineHead;
6723
+ if (machineHead.currentIndex < machineHead.branch.length - 1) {
6724
+ commit('nextLine');
6725
+ }
6726
+ else {
6727
+ // This branch is finished, go back to previous stack
6728
+ commit('previousStack');
6729
+ return dispatch('nextLine');
6730
+ }
6731
+ if (state.machine.stack.length === 0) {
6732
+ finishGame(ctx);
6733
+ }
6734
+ else {
6735
+ return dispatch('runLine');
6736
+ }
6737
+ }
6738
+ function finishGame({ commit, state }) {
6739
+ if (state.options.debug) {
6740
+ commit('addDialog', {
6741
+ dialog: {
6742
+ speaker: 'game',
6743
+ text: '[DEBUG] Game Script is finished. This is the end of the game flow. This message only appears in debug mode.',
6744
+ },
6745
+ });
6746
+ }
6747
+ }
6748
+
6749
+ var script$8 = defineComponent({
6750
+ components: {},
6751
+ props: {},
6752
+ data() {
6753
+ return {
6754
+ activeMenu: false,
6755
+ };
6756
+ },
6757
+ mounted() {
6758
+ document.addEventListener('keydown', (event) => {
6759
+ const key = event.key;
6760
+ if (key === 'Escape') {
6761
+ this.toggleMenu();
6762
+ }
6763
+ });
6764
+ vm$1.callHook('onGameMounted');
6765
+ },
6766
+ unmounted() {
6767
+ vm$1.callHook('onGameUnmounted');
6768
+ },
6769
+ methods: {
6770
+ openMenu() {
6771
+ this.$store.commit('openModal', 'menu');
6772
+ },
6773
+ openSkills() {
6774
+ this.$store.commit('openModal', 'skills');
6775
+ },
6776
+ closeMenu() {
6777
+ this.$store.commit('closeModal');
6778
+ },
6779
+ toggleMenu() {
6780
+ this.$store.commit('toggleMenu');
6781
+ },
6782
+ },
6783
+ computed: {
6784
+ showSkills() {
6785
+ if (Object.entries(getConfig().skills).length > 0) {
6786
+ return true;
6787
+ }
6788
+ return false;
6789
+ },
6790
+ },
6791
+ });
6792
+
6793
+ const _hoisted_1$7 = { class: "menu-container" };
6794
+
6795
+ function render$8(_ctx, _cache, $props, $setup, $data, $options) {
6796
+ return (openBlock(), createElementBlock("div", _hoisted_1$7, [
6797
+ createElementVNode("button", {
6798
+ class: "button menu-toggle-button",
6799
+ id: "menu-button",
6800
+ onClick: _cache[0] || (_cache[0] = (...args) => (_ctx.openMenu && _ctx.openMenu(...args)))
6801
+ }, " Menu "),
6802
+ (_ctx.showSkills)
6803
+ ? (openBlock(), createElementBlock("button", {
6804
+ key: 0,
6805
+ class: "button menu-toggle-button",
6806
+ id: "skills-menu-button",
6807
+ onClick: _cache[1] || (_cache[1] = (...args) => (_ctx.openSkills && _ctx.openSkills(...args)))
6808
+ }, " Skills "))
6809
+ : createCommentVNode("", true)
6810
+ ]))
6811
+ }
6812
+
6813
+ var css_248z$9 = ".menu-content {\n text-align: center;\n}\n\n.menu-toggle-button {\n margin: 0;\n padding: 2px;\n border-radius: 5px;\n}\n\n.menu-toggle-button:not(:last-child) {\n margin-right: 10px;\n}\n\n.menu-modal {\n width: 500px;\n}\r\n";
6814
+ styleInject(css_248z$9);
6815
+
6816
+ script$8.render = render$8;
6817
+
6818
+ var script$9 = defineComponent({
6819
+ components: {
6820
+ Modal: script$2,
6821
+ VolumeControls: script$4,
6822
+ },
6823
+ props: {},
6824
+ data() { },
6825
+ mounted() { },
6826
+ methods: {
6827
+ quit() {
6828
+ window.close();
6829
+ // quit
6830
+ },
6831
+ close() {
6832
+ this.$emit('close');
6833
+ },
6834
+ mainMenu() {
6835
+ this.$store.dispatch('menuReturn');
6836
+ this.$store.commit('setFlowState', 'menu');
6837
+ this.close();
6838
+ },
6839
+ getPlayTimeString() {
6840
+ const time = getPlayTime(this.$store.state.playTime.start, this.$store.state.playTime.previousPlaytime);
6841
+ return toHHMMSS(time / 1000);
6842
+ },
6843
+ },
6844
+ computed: {},
6845
+ });
6846
+
6847
+ const _hoisted_1$8 = /*#__PURE__*/createElementVNode("h3", { class: "title" }, "Menu", -1);
6848
+ const _hoisted_2$6 = { class: "menu-content" };
6849
+
6850
+ function render$9(_ctx, _cache, $props, $setup, $data, $options) {
6851
+ const _component_VolumeControls = resolveComponent("VolumeControls");
6852
+ const _component_modal = resolveComponent("modal");
6853
+
6854
+ return (openBlock(), createBlock(_component_modal, {
6855
+ class: "menu",
6856
+ onClose: _ctx.close,
6857
+ containerCssClass: "menu-modal"
6858
+ }, {
6859
+ header: withCtx(() => [
6860
+ _hoisted_1$8
6861
+ ]),
6862
+ body: withCtx(() => [
6863
+ createElementVNode("div", _hoisted_2$6, [
6864
+ createElementVNode("h3", null, "Play time: " + toDisplayString(_ctx.getPlayTimeString()), 1),
6865
+ createVNode(_component_VolumeControls),
6866
+ createElementVNode("button", {
6867
+ class: "button title quit-button",
6868
+ onClick: _cache[0] || (_cache[0] = (...args) => (_ctx.mainMenu && _ctx.mainMenu(...args)))
6869
+ }, " Main Menu "),
6870
+ createElementVNode("button", {
6525
6871
  class: "button title quit-button",
6526
6872
  onClick: _cache[1] || (_cache[1] = (...args) => (_ctx.quit && _ctx.quit(...args)))
6527
6873
  }, "Exit")
6528
6874
  ])
6529
6875
  ]),
6530
6876
  _: 1
6531
- }))
6877
+ }, 8, ["onClose"]))
6532
6878
  }
6533
6879
 
6534
6880
  var css_248z$a = ".quit-button {\n margin: 20px;\n text-align: center;\n}\r\n";
@@ -6565,6 +6911,11 @@ var script$a = defineComponent({
6565
6911
  closeSkill() {
6566
6912
  this.chosenSkill = false;
6567
6913
  },
6914
+ xpBarWidth(xp) {
6915
+ return {
6916
+ width: `${Math.floor((xp / this.xpPerLevel) * 100)}%`,
6917
+ };
6918
+ },
6568
6919
  },
6569
6920
  computed: {
6570
6921
  skillsToDisplay() {
@@ -6584,6 +6935,9 @@ var script$a = defineComponent({
6584
6935
  skillConf() {
6585
6936
  return getConfig().skills;
6586
6937
  },
6938
+ xpPerLevel() {
6939
+ return getConfig().skillOptions.xpPerLevel;
6940
+ },
6587
6941
  },
6588
6942
  });
6589
6943
 
@@ -6595,14 +6949,16 @@ const _hoisted_3$4 = {
6595
6949
  };
6596
6950
  const _hoisted_4$3 = ["onClick"];
6597
6951
  const _hoisted_5$3 = { class: "skill-title" };
6598
- const _hoisted_6$3 = { class: "skill-level" };
6599
- const _hoisted_7$2 = {
6952
+ const _hoisted_6$3 = { class: "skill-xp-container" };
6953
+ const _hoisted_7$2 = { class: "skill-xp-text" };
6954
+ const _hoisted_8$1 = { class: "skill-level" };
6955
+ const _hoisted_9$1 = {
6600
6956
  key: 1,
6601
6957
  class: "flex flex-row skill-description-container"
6602
6958
  };
6603
- const _hoisted_8$1 = { class: "flex skill-left" };
6604
- const _hoisted_9$1 = { class: "flex skill-right" };
6605
- const _hoisted_10$1 = /*#__PURE__*/createElementVNode("hr", { class: "hr-solid" }, null, -1);
6959
+ const _hoisted_10$1 = { class: "flex skill-left" };
6960
+ const _hoisted_11$1 = { class: "flex skill-right" };
6961
+ const _hoisted_12$1 = /*#__PURE__*/createElementVNode("hr", { class: "hr-solid" }, null, -1);
6606
6962
 
6607
6963
  function render$a(_ctx, _cache, $props, $setup, $data, $options) {
6608
6964
  const _component_modal = resolveComponent("modal");
@@ -6623,16 +6979,24 @@ function render$a(_ctx, _cache, $props, $setup, $data, $options) {
6623
6979
  return (openBlock(), createElementBlock("button", {
6624
6980
  onClick: () => _ctx.clickSkill(key),
6625
6981
  class: "skill-display",
6626
- style: normalizeStyle(_ctx.getSkillStyle(key))
6982
+ style: normalizeStyle(_ctx.getSkillStyle(key)),
6983
+ key: key
6627
6984
  }, [
6628
6985
  createElementVNode("h3", _hoisted_5$3, toDisplayString(_ctx.getSkillName(key)), 1),
6629
- createElementVNode("h3", _hoisted_6$3, toDisplayString(skill.level), 1)
6986
+ createElementVNode("div", _hoisted_6$3, [
6987
+ createElementVNode("div", {
6988
+ class: "skill-xp-bar",
6989
+ style: normalizeStyle(_ctx.xpBarWidth(skill.xp))
6990
+ }, null, 4),
6991
+ createElementVNode("h3", _hoisted_7$2, toDisplayString(skill.xp) + " / " + toDisplayString(_ctx.xpPerLevel) + " XP ", 1)
6992
+ ]),
6993
+ createElementVNode("h3", _hoisted_8$1, toDisplayString(skill.level), 1)
6630
6994
  ], 12, _hoisted_4$3))
6631
- }), 256))
6995
+ }), 128))
6632
6996
  ]))
6633
6997
  : (typeof _ctx.chosenSkill === 'string')
6634
- ? (openBlock(), createElementBlock("div", _hoisted_7$2, [
6635
- createElementVNode("div", _hoisted_8$1, [
6998
+ ? (openBlock(), createElementBlock("div", _hoisted_9$1, [
6999
+ createElementVNode("div", _hoisted_10$1, [
6636
7000
  createElementVNode("button", {
6637
7001
  class: "button",
6638
7002
  onClick: _cache[0] || (_cache[0] = (...args) => (_ctx.closeSkill && _ctx.closeSkill(...args)))
@@ -6642,9 +7006,9 @@ function render$a(_ctx, _cache, $props, $setup, $data, $options) {
6642
7006
  style: normalizeStyle(_ctx.getSkillStyle(_ctx.chosenSkill))
6643
7007
  }, null, 4)
6644
7008
  ]),
6645
- createElementVNode("div", _hoisted_9$1, [
7009
+ createElementVNode("div", _hoisted_11$1, [
6646
7010
  createElementVNode("h2", null, toDisplayString(_ctx.getSkillName(_ctx.chosenSkill)), 1),
6647
- _hoisted_10$1,
7011
+ _hoisted_12$1,
6648
7012
  createElementVNode("h3", null, "Level: " + toDisplayString(_ctx.skills[_ctx.chosenSkill].level), 1),
6649
7013
  createElementVNode("p", null, toDisplayString(_ctx.skillConf[_ctx.chosenSkill].description), 1)
6650
7014
  ])
@@ -6656,7 +7020,7 @@ function render$a(_ctx, _cache, $props, $setup, $data, $options) {
6656
7020
  }, 8, ["onClose"]))
6657
7021
  }
6658
7022
 
6659
- var css_248z$b = ".skills-modal {\n width: 800px;\n}\n\n.skills-container {\n display: grid;\n grid-auto-rows: auto;\n grid-template-columns: repeat(3, 1fr);\n grid-gap: 20px 20px;\n}\n\n.skill-display {\n width: 200px;\n height: 300px;\n position: relative;\n background-size: cover;\n}\n\n.skill-title {\n position: absolute;\n bottom: 0px;\n text-align: center;\n width: 100%;\n color: var(--skills-text-color);\n background: var(--skills-text-background);\n}\n\n.skill-level {\n position: absolute;\n top: 0;\n right: 0;\n font-weight: 700;\n font-size: 25px;\n color: var(--skills-level-color);\n width: 40px;\n height: 40px;\n background-color: var(--skills-level-background);\n}\n\n.skill-description-container {\n justify-content: space-between;\n align-items: center;\n}\n\n.skill-left {\n flex-direction: column;\n}\n\n.skill-right {\n flex-direction: column;\n align-items: baseline;\n}\r\n";
7023
+ var css_248z$b = ".skills-modal {\n width: 800px;\n}\n\n.skills-container {\n display: grid;\n grid-auto-rows: auto;\n grid-template-columns: repeat(3, 1fr);\n grid-gap: 20px 20px;\n}\n\n.skill-display {\n width: 200px;\n height: 300px;\n position: relative;\n background-size: cover;\n}\n\n.skill-title {\n position: absolute;\n bottom: 0px;\n text-align: center;\n width: 100%;\n color: var(--skills-text-color);\n background: var(--skills-text-background);\n}\n\n.skill-level {\n position: absolute;\n top: 0;\n right: 0;\n font-weight: 700;\n font-size: 25px;\n color: var(--skills-level-color);\n width: var(--skills-xp-bar-height);\n height: var(--skills-xp-bar-height);\n background-color: var(--skills-level-background);\n}\n\n.skill-description-container {\n justify-content: space-between;\n align-items: center;\n}\n\n.skill-left {\n flex-direction: column;\n}\n\n.skill-right {\n flex-direction: column;\n align-items: baseline;\n}\n\n.skill-xp-container {\n position: absolute;\n top: 0;\n left: 0;\n height: var(--skills-xp-bar-height);\n width: calc(100% - var(--skills-xp-bar-height));\n background-color: rgba(0, 0, 0, 0.5);\n}\n\n.skill-xp-bar {\n position: absolute;\n top: 0;\n left: 0;\n width: 50%;\n height: 100%;\n background-color: rgba(0, 0, 250, 0.5);\n}\n\n.skill-xp-text {\n z-index: 2;\n}\r\n";
6660
7024
  styleInject(css_248z$b);
6661
7025
 
6662
7026
  script$a.render = render$a;
@@ -6684,6 +7048,7 @@ var script$b = defineComponent({
6684
7048
  gameLoaded: false,
6685
7049
  loadingStep: 'Loading',
6686
7050
  loadingPercentage: 0.1,
7051
+ hasSave: false,
6687
7052
  };
6688
7053
  },
6689
7054
  props: {
@@ -6691,6 +7056,7 @@ var script$b = defineComponent({
6691
7056
  options: Object,
6692
7057
  },
6693
7058
  async mounted() {
7059
+ vm$1.callHook('onAppMounted');
6694
7060
  this.loadingStep = 'Characters';
6695
7061
  const charsFile = await getFile('data/characters.json');
6696
7062
  await setCharactersConfig(JSON.parse(charsFile));
@@ -6702,11 +7068,17 @@ var script$b = defineComponent({
6702
7068
  this.loadingPercentage = 0.7;
6703
7069
  this.loadingStep = 'Audio';
6704
7070
  await loadAudioAssets(getConfig());
7071
+ vm$1.callHook('onAssetsLoaded');
6705
7072
  this.loadingPercentage = 0.95;
6706
7073
  this.loadingStep = 'Starting...';
6707
7074
  await this.setupMachine();
7075
+ vm$1.callHook('onGameSetup');
6708
7076
  this.loadingPercentage = 0.1;
6709
7077
  this.gameLoaded = true;
7078
+ const save = this.getSaveFile();
7079
+ if (save) {
7080
+ this.hasSave = true;
7081
+ }
6710
7082
  window.addEventListener('resize', debounce(() => {
6711
7083
  this.updateScreenSize();
6712
7084
  }, 100, {
@@ -6750,10 +7122,6 @@ var script$b = defineComponent({
6750
7122
  }
6751
7123
  return undefined;
6752
7124
  },
6753
- saveFile() {
6754
- const saveString = localStorage.getItem(SAVE_FILE);
6755
- return saveString;
6756
- },
6757
7125
  backgroundStyle() {
6758
7126
  let height;
6759
7127
  if (this.layoutMode === 'vertical') {
@@ -6899,15 +7267,19 @@ var script$b = defineComponent({
6899
7267
  config: getConfig(),
6900
7268
  });
6901
7269
  },
7270
+ getSaveFile() {
7271
+ return localStorage.getItem(SAVE_FILE);
7272
+ },
6902
7273
  async startGame() {
6903
7274
  this.$store.dispatch('startMachine');
6904
7275
  await this.$store.dispatch('runLine');
6905
7276
  this.$store.commit('setFlowState', 'playing');
7277
+ this.hasSave = true;
6906
7278
  this.$store.dispatch('saveGame');
6907
7279
  },
6908
7280
  async loadGame() {
6909
7281
  this.$store.dispatch('startMachine');
6910
- await this.$store.dispatch('loadGame', this.saveFile);
7282
+ await this.$store.dispatch('loadGame', this.getSaveFile());
6911
7283
  this.$store.commit('setFlowState', 'playing');
6912
7284
  },
6913
7285
  isDialogActive(i) {
@@ -7028,7 +7400,8 @@ function render$b(_ctx, _cache, $props, $setup, $data, $options) {
7028
7400
  createElementVNode("canvas", {
7029
7401
  width: _ctx.layoutWidth,
7030
7402
  height: _ctx.layoutHeight,
7031
- id: "background-canvas"
7403
+ id: "background-canvas",
7404
+ class: "narrat-canvas"
7032
7405
  }, null, 8, _hoisted_1$a)
7033
7406
  ], 4))
7034
7407
  : createCommentVNode("", true),
@@ -7070,7 +7443,7 @@ function render$b(_ctx, _cache, $props, $setup, $data, $options) {
7070
7443
  class: "button menu-button start-button override",
7071
7444
  onClick: _cache[0] || (_cache[0] = (...args) => (_ctx.startGame && _ctx.startGame(...args)))
7072
7445
  }, " Start Game "),
7073
- (_ctx.saveFile)
7446
+ (_ctx.hasSave)
7074
7447
  ? (openBlock(), createElementBlock("button", {
7075
7448
  key: 0,
7076
7449
  class: "button menu-button continue-button override",
@@ -7097,633 +7470,15 @@ styleInject(css_248z$c);
7097
7470
 
7098
7471
  script$b.render = render$b;
7099
7472
 
7100
- function createSkillCheckState() {
7101
- const skillCheck = {
7102
- passed: false,
7103
- available: true,
7104
- };
7105
- return skillCheck;
7106
- }
7107
- function getSkillCheckState(ctx, skillCheckId) {
7108
- let skillCheck = ctx.state.skillChecks[skillCheckId];
7109
- if (!skillCheck) {
7110
- skillCheck = createSkillCheckState();
7111
- ctx.commit('setupSkillCheck', {
7112
- skillCheck,
7113
- skillCheckId,
7114
- });
7115
- }
7116
- return skillCheck;
7117
- }
7118
-
7119
- function processSkillCheck(ctx, skillcheck) {
7120
- return runSkillCheck(ctx, {
7121
- skill: skillcheck.skill,
7122
- value: skillcheck.value,
7123
- id: skillcheck.id,
7124
- success: skillcheck.success.text,
7125
- failure: skillcheck.failure.text,
7126
- });
7127
- }
7128
- function runSkillCheck(ctx, params) {
7129
- const { state } = ctx;
7130
- const { skills, skillChecks } = getConfig();
7131
- let success = true;
7132
- let roll = Math.floor(Math.random() * skillChecks.rollRange);
7133
- if (roll <= skillChecks.failureChance - 1) {
7134
- success = false;
7135
- }
7136
- roll += state.skills[params.skill].level * skillChecks.skillMultiplier;
7137
- const skill = skills[params.skill];
7138
- logger.log(`roll `, roll, params.value);
7139
- if (roll < params.value) {
7140
- success = false;
7141
- }
7142
- if (success) {
7143
- ctx.commit('passSkillCheck', params.id);
7144
- writeText(ctx, `[${skill.name} - Success] ${params.success || ''}`);
7145
- return true;
7146
- }
7147
- ctx.commit('failSkillCheck', params.id);
7148
- writeText(ctx, `[${skill.name} - Failure] ${params.failure || ''}`);
7149
- return false;
7150
- }
7151
- function runConditionCommand(ctx, command) {
7152
- const options = command.options;
7153
- const result = runCondition(ctx, options.condition);
7154
- logger.log(result);
7155
- if (result) {
7156
- return options.success;
7157
- }
7158
- if (!result && options.failure) {
7159
- return options.failure;
7160
- }
7161
- return undefined;
7162
- }
7163
- function writeText(ctx, text) {
7164
- const dialog = {
7165
- speaker: 'game',
7166
- text,
7167
- interactive: false,
7168
- };
7169
- ctx.commit('addDialog', {
7170
- dialog,
7171
- });
7172
- }
7173
- function runCondition(ctx, condition) {
7174
- return conditionFunction(ctx, condition);
7175
- }
7176
- function conditionFunction(ctx, condition) {
7177
- const { state } = ctx;
7178
- const context = {
7179
- data: state.machine.data,
7180
- skills: state.skills,
7181
- skillChecks: state.skillChecks,
7182
- stats: state.hudStats,
7183
- roll: (checkId, skill, value) => {
7184
- const skillCheckState = getSkillCheckState(ctx, checkId);
7185
- if (skillCheckState) {
7186
- if (skillCheckState.passed) {
7187
- return true;
7188
- }
7189
- if (!skillCheckState.available) {
7190
- return false;
7191
- }
7192
- }
7193
- return runSkillCheck(ctx, {
7194
- skill,
7195
- value,
7196
- id: checkId,
7197
- });
7198
- },
7199
- };
7200
- return evalInContext(`(${condition})`, context);
7201
- }
7202
- function evalInContext(script, context) {
7203
- // # Return the results of the in-line anonymous function we .call with the passed context
7204
- return function () {
7205
- return eval(script);
7206
- }.call(context);
7207
- }
7208
-
7209
- async function runLine(context) {
7210
- const cmd = context.getters.currentLine;
7211
- await runCommand(context, cmd);
7212
- }
7213
- async function runCommand(context, cmd, choices) {
7214
- const { state, commit, dispatch } = context;
7215
- try {
7216
- switch (cmd.commandType) {
7217
- case 'jump':
7218
- const branch = cmd.args[0];
7219
- const newStack = {
7220
- branch: state.machine.script[branch],
7221
- label: branch,
7222
- currentIndex: 0,
7223
- };
7224
- commit('setStack', newStack);
7225
- await dispatch('saveGame');
7226
- await dispatch('runLine');
7227
- break;
7228
- case 'text':
7229
- await textCommand(commit, {
7230
- speaker: 'game',
7231
- text: cmd.options.text,
7232
- choices,
7233
- interactive: true,
7234
- });
7235
- break;
7236
- case 'set':
7237
- const key = cmd.args[0];
7238
- const value = cmd.args[1];
7239
- commit('setData', { path: key, value });
7240
- return dispatch('nextLine');
7241
- case 'add':
7242
- const addKey = cmd.args[0];
7243
- const addValue = cmd.args[1];
7244
- commit('addInstruction', { path: addKey, value: addValue });
7245
- return dispatch('nextLine');
7246
- case 'if':
7247
- const newBranch = runConditionCommand(context, cmd);
7248
- if (newBranch) {
7249
- const newStack = {
7250
- branch: newBranch,
7251
- currentIndex: 0,
7252
- };
7253
- commit('addStack', newStack);
7254
- return dispatch('runLine');
7255
- }
7256
- return dispatch('nextLine');
7257
- case 'talk':
7258
- await textCommand(commit, {
7259
- speaker: cmd.args[0],
7260
- pose: cmd.args[1],
7261
- text: `"${cmd.args[2]}"`,
7262
- choices,
7263
- interactive: true,
7264
- });
7265
- break;
7266
- case 'choice':
7267
- await runChoice(context, cmd);
7268
- break;
7269
- case 'set_screen':
7270
- commit('setScreen', cmd.options.screen);
7271
- return dispatch('nextLine');
7272
- case 'clear_dialog':
7273
- commit('clearDialog');
7274
- return dispatch('nextLine');
7275
- case 'set_button':
7276
- commit('changeButton', {
7277
- button: cmd.args[0],
7278
- enabled: cmd.args[1],
7279
- });
7280
- return dispatch('nextLine');
7281
- case 'play':
7282
- const playOptions = cmd.options;
7283
- if (playOptions.mode === 'music') {
7284
- changeMusic(context, playOptions.audio);
7285
- }
7286
- else {
7287
- playAudio(context.commit, playOptions.audio);
7288
- }
7289
- return dispatch('nextLine');
7290
- case 'stop':
7291
- const stopOptions = cmd.options;
7292
- if (stopOptions.mode === 'music') {
7293
- stopAudio(commit, context.state.audio.currentMusic);
7294
- }
7295
- else if (stopOptions.mode === 'sound' && stopOptions.audio) {
7296
- stopAudio(commit, stopOptions.audio);
7297
- }
7298
- else {
7299
- error(commit, `stop option needs to either be in music mode, or if stopping a sound needs to have the sound name supplied as second argument.`);
7300
- }
7301
- return dispatch('nextLine');
7302
- case 'pause':
7303
- const pauseOptions = cmd.options;
7304
- if (pauseOptions.mode === 'music') {
7305
- pauseAudio(commit, context.state.audio.currentMusic);
7306
- }
7307
- else if (stopOptions.mode === 'sound' && stopOptions.audio) {
7308
- pauseAudio(commit, stopOptions.audio);
7309
- }
7310
- else {
7311
- error(commit, `pause first option needs to either be in music mode, or if stopping a sound needs to have the sound name supplied as second argument.`);
7312
- }
7313
- return dispatch('nextLine');
7314
- case 'wait':
7315
- await timeout(cmd.options.duration);
7316
- return dispatch('nextLine');
7317
- case 'add_level':
7318
- const skillKey = cmd.args[0];
7319
- const levelToAdd = cmd.args[1];
7320
- if (!skillKey || !levelToAdd) {
7321
- error(commit, `add_level command needs a skill id and a value as parameters`);
7322
- }
7323
- commit('incrementSkill', {
7324
- skill: skillKey,
7325
- amount: levelToAdd,
7326
- });
7327
- const skillName = getConfig().skills[skillKey].name;
7328
- const skillLevel = state.skills[skillKey].level;
7329
- dispatch('addNotification', `Your skill in ${skillName} is now level ${skillLevel}`);
7330
- return dispatch('nextLine');
7331
- case 'add_stat':
7332
- const statKey = cmd.args[0];
7333
- const amountToAdd = cmd.args[1];
7334
- if (!statKey || !amountToAdd) {
7335
- error(commit, `add_stat command needs a stat id and a value as parameters`);
7336
- }
7337
- commit('addStat', {
7338
- stat: statKey,
7339
- amount: amountToAdd,
7340
- });
7341
- return dispatch('nextLine');
7342
- case 'set_stat':
7343
- const setStatKey = cmd.args[0];
7344
- const setAmount = cmd.args[1];
7345
- if (!setStatKey || !setAmount) {
7346
- error(commit, `set_stat command needs a stat id and a value as parameters`);
7347
- }
7348
- commit('setStat', {
7349
- stat: setStatKey,
7350
- amount: setAmount,
7351
- });
7352
- return dispatch('nextLine');
7353
- case 'notify':
7354
- const text = cmd.args[0];
7355
- dispatch('addNotification', text);
7356
- return dispatch('nextLine');
7357
- default:
7358
- break;
7359
- }
7360
- }
7361
- catch (err) {
7362
- logger.log(`Error at: `, state.machine.stack[state.machine.stack.length - 1].label);
7363
- console.error(err);
7364
- error(commit, `Narrat script runtime error at <span class="error-filename">${cmd.fileName}:${cmd.line + 1}</span>
7365
- <b>${err}</b>
7366
- Script: ${cmd.code}
7367
- Label: ${state.machine.stack[state.machine.stack.length - 1].label}`);
7368
- }
7369
- }
7370
- async function playerAnswered(context, choiceIndex) {
7371
- const { commit, dispatch } = context;
7372
- const cmd = context.getters.currentLine;
7373
- switch (cmd.commandType) {
7374
- case 'choice':
7375
- const options = cmd.options;
7376
- const choice = options.choices[choiceIndex];
7377
- let playerText = choice.choice;
7378
- let newBranch;
7379
- const skillcheck = choice.skillCheck;
7380
- if (skillcheck) {
7381
- const skillCheckState = getSkillCheckState(context, choice.skillCheck.id);
7382
- if (skillCheckState.passed) {
7383
- newBranch = skillcheck.success.branch;
7384
- playerText = skillcheck.success.text;
7385
- }
7386
- else {
7387
- const result = processSkillCheck(context, skillcheck);
7388
- const winner = result ? skillcheck.success : skillcheck.failure;
7389
- newBranch = winner.branch;
7390
- playerText = undefined;
7391
- }
7392
- }
7393
- else {
7394
- newBranch = choice.branch;
7395
- }
7396
- if (playerText) {
7397
- // If the choice involves printing a player dialog, show it
7398
- const dialog = {
7399
- speaker: 'player',
7400
- text: playerText,
7401
- interactive: false,
7402
- };
7403
- commit('addDialog', { dialog });
7404
- }
7405
- if (newBranch) {
7406
- const newStack = {
7407
- currentIndex: 0,
7408
- branch: newBranch,
7409
- };
7410
- commit('addStack', newStack);
7411
- dispatch('runLine');
7412
- }
7413
- else {
7414
- dispatch('nextLine');
7415
- }
7416
- break;
7417
- default:
7418
- dispatch('nextLine');
7419
- }
7420
- }
7421
- async function runChoice(context, cmd) {
7422
- const options = cmd.options;
7423
- const prompt = options.prompt;
7424
- const choices = options.choices
7425
- .filter((choice) => {
7426
- // Delete the choice if it fails a condition
7427
- if (choice.condition) {
7428
- return runCondition(context, choice.condition);
7429
- }
7430
- return true;
7431
- })
7432
- .map((choice) => {
7433
- let text = choice.choice;
7434
- let choiceAllowed = true;
7435
- if (choice.skillCheck) {
7436
- const check = choice.skillCheck;
7437
- const config = getConfig();
7438
- const skill = config.skills[check.skill];
7439
- const level = context.state.skills[check.skill].level;
7440
- const difficultyScore = check.value - level * config.skillChecks.skillMultiplier;
7441
- const skillCheckState = getSkillCheckState(context, choice.skillCheck.id);
7442
- let found = false;
7443
- let i = 0;
7444
- let checkText = config.skillChecks.difficultyText[0][1];
7445
- while (!found) {
7446
- if (config.skillChecks.difficultyText.length > i) {
7447
- if (difficultyScore >= config.skillChecks.difficultyText[i][0]) {
7448
- checkText = config.skillChecks.difficultyText[i][1];
7449
- }
7450
- else {
7451
- found = true;
7452
- }
7453
- }
7454
- else {
7455
- found = true;
7456
- }
7457
- i++;
7458
- }
7459
- if (!skillCheckState.available) {
7460
- choiceAllowed = false;
7461
- text = `[${skill.name} - Failed] ${text}`;
7462
- }
7463
- else if (!skillCheckState.passed) {
7464
- text = `[${skill.name} - ${checkText}] ${text}`;
7465
- }
7466
- }
7467
- const result = {
7468
- choice: text,
7469
- originalIndex: choice.index,
7470
- allowed: choiceAllowed,
7471
- };
7472
- return result;
7473
- });
7474
- runCommand(context, prompt, choices);
7475
- }
7476
- async function textCommand(commit, dialog) {
7477
- commit('addDialog', {
7478
- dialog,
7479
- });
7480
- }
7481
- async function nextLine(ctx) {
7482
- const { state, getters, dispatch, commit } = ctx;
7483
- if (state.machine.stack.length === 0) {
7484
- finishGame(ctx);
7485
- return;
7486
- }
7487
- const machineHead = getters.machineHead;
7488
- if (machineHead.currentIndex < machineHead.branch.length - 1) {
7489
- commit('nextLine');
7490
- }
7491
- else {
7492
- // This branch is finished, go back to previous stack
7493
- commit('previousStack');
7494
- return dispatch('nextLine');
7495
- }
7496
- if (state.machine.stack.length === 0) {
7497
- finishGame(ctx);
7498
- }
7499
- else {
7500
- return dispatch('runLine');
7501
- }
7502
- }
7503
- function finishGame({ commit, state }) {
7504
- if (state.options.debug) {
7505
- commit('addDialog', {
7506
- dialog: {
7507
- speaker: 'game',
7508
- text: '[DEBUG] Game Script is finished. This is the end of the game flow. This message only appears in debug mode.',
7509
- },
7510
- });
7511
- }
7512
- }
7513
-
7514
- function jump(ctx) {
7515
- ctx.command.commandType = 'jump';
7516
- ctx.currentLine++;
7517
- }
7518
- function choice(ctx) {
7519
- const { line, command } = ctx;
7520
- if (!line.branch || line.branch.length < 2) {
7521
- ctx.parserContext.error(line.line, `Choice menu needs to have at least one option`);
7522
- }
7523
- const prompt = line.branch[0];
7524
- const choices = line.branch.slice(1);
7525
- const prompts = choices.map((choice, index) => {
7526
- if (!choice.branch) {
7527
- ctx.parserContext.error(choice.line, `Choice option doesn't have any branch to go to (${choice.code})`);
7528
- }
7529
- return parseChoiceOption(ctx, choice, index);
7530
- });
7531
- command.options = {
7532
- prompt: ctx.processCommandsFunction(ctx.parserContext, [prompt], line)[0],
7533
- choices: prompts,
7534
- };
7535
- command.commandType = 'choice';
7536
- ctx.currentLine++;
7537
- }
7538
- function set(ctx) {
7539
- ctx.command.commandType = 'set';
7540
- ctx.currentLine++;
7541
- }
7542
- // eslint-disable-next-line camelcase
7543
- function add_level(ctx) {
7544
- ctx.command.commandType = 'add_level';
7545
- ctx.currentLine++;
7546
- }
7547
- function set_stat(ctx) {
7548
- ctx.command.commandType = 'set_stat';
7549
- ctx.currentLine++;
7550
- }
7551
- function add_stat(ctx) {
7552
- ctx.command.commandType = 'add_stat';
7553
- ctx.currentLine++;
7554
- }
7555
- function add(ctx) {
7556
- ctx.command.commandType = 'add';
7557
- ctx.currentLine++;
7558
- }
7559
- function talk(ctx) {
7560
- const { command, line } = ctx;
7561
- command.commandType = 'talk';
7562
- if (command.args.length < 3) {
7563
- ctx.parserContext.error(line.line, `Talk command needs 3 arguments!`);
7564
- }
7565
- ctx.currentLine++;
7566
- }
7567
- function ifCommand(ctx) {
7568
- const { command, lines, currentLine, line } = ctx;
7569
- command.commandType = 'if';
7570
- let failure;
7571
- const nextLine = getLine(lines, currentLine + 1);
7572
- if (nextLine && nextLine.operator === 'else') {
7573
- failure = ctx.processCommandsFunction(ctx.parserContext, nextLine.branch, line);
7574
- ctx.currentLine++;
7575
- }
7576
- command.options = {
7577
- condition: command.args[0],
7578
- success: ctx.processCommandsFunction(ctx.parserContext, line.branch, line),
7579
- failure,
7580
- };
7581
- ctx.currentLine++;
7582
- }
7583
- function setScreen(ctx) {
7584
- const { command } = ctx;
7585
- command.commandType = 'set_screen';
7586
- command.options = {
7587
- screen: command.args[0],
7588
- };
7589
- ctx.currentLine++;
7590
- }
7591
- function setButton(ctx) {
7592
- const { command, line } = ctx;
7593
- command.commandType = 'set_button';
7594
- if (command.args.length !== 2) {
7595
- ctx.parserContext.error(line.line, `set_button command should have 2 arguments`);
7596
- }
7597
- ctx.currentLine++;
7598
- }
7599
- function clearDialog(ctx) {
7600
- const { command } = ctx;
7601
- command.commandType = 'clear_dialog';
7602
- ctx.currentLine++;
7603
- }
7604
- function play(ctx) {
7605
- const { command } = ctx;
7606
- command.commandType = 'play';
7607
- command.options = {
7608
- mode: command.args[0],
7609
- audio: command.args[1],
7610
- };
7611
- ctx.currentLine++;
7612
- }
7613
- function stop(ctx) {
7614
- const { command } = ctx;
7615
- command.commandType = 'stop';
7616
- command.options = {
7617
- mode: command.args[0],
7618
- audio: command.args[1],
7619
- };
7620
- ctx.currentLine++;
7621
- }
7622
- function pause(ctx) {
7623
- const { command } = ctx;
7624
- command.commandType = 'pause';
7625
- command.options = {
7626
- mode: command.args[0],
7627
- audio: command.args[1],
7628
- };
7629
- ctx.currentLine++;
7630
- }
7631
- function wait(ctx) {
7632
- const { command } = ctx;
7633
- command.commandType = 'wait';
7634
- command.options = {
7635
- duration: parseInt(command.args[0], 10),
7636
- };
7637
- ctx.currentLine++;
7638
- }
7639
- function text(ctx) {
7640
- const { command, line } = ctx;
7641
- command.commandType = 'text';
7642
- command.options = {
7643
- text: line.operator,
7644
- };
7645
- ctx.currentLine++;
7646
- }
7647
- function notify(ctx) {
7648
- ctx.command.commandType = 'notify';
7649
- ctx.currentLine++;
7650
- }
7651
- const parserFunctions = {
7652
- jump,
7653
- choice,
7654
- set,
7655
- add,
7656
- talk,
7657
- if: ifCommand,
7658
- set_screen: setScreen,
7659
- set_button: setButton,
7660
- clear_dialog: clearDialog,
7661
- play,
7662
- stop,
7663
- pause,
7664
- wait,
7665
- text,
7666
- add_level,
7667
- notify,
7668
- set_stat,
7669
- add_stat,
7670
- };
7671
- function getLine(lines, index) {
7672
- if (index < lines.length)
7673
- return lines[index];
7674
- }
7675
- function parseChoiceOption(ctx, choice, index) {
7676
- let choiceText = choice.operator;
7677
- let condition;
7678
- let skillCheck;
7679
- if (choice.operator === 'roll') {
7680
- if (choice.args.length < 4) {
7681
- ctx.parserContext.error(choice.line, `Skillchecks need 4 arguments!`);
7682
- }
7683
- choiceText = choice.args[3];
7684
- const successBranch = choice.branch[0];
7685
- const failureBranch = choice.branch[1];
7686
- const success = {
7687
- text: successBranch.args[0],
7688
- branch: ctx.processCommandsFunction(ctx.parserContext, successBranch.branch, choice),
7689
- };
7690
- let failedBranch;
7691
- if (failureBranch.branch) {
7692
- failedBranch = ctx.processCommandsFunction(ctx.parserContext, failureBranch.branch, choice);
7693
- }
7694
- const failure = {
7695
- text: failureBranch.args[0],
7696
- branch: failedBranch,
7697
- };
7698
- skillCheck = {
7699
- id: choice.args[0],
7700
- skill: choice.args[1],
7701
- value: choice.args[2],
7702
- success,
7703
- failure,
7704
- };
7705
- }
7706
- if (choice.args[0] === 'if') {
7707
- condition = choice.args[1];
7708
- }
7709
- return {
7710
- choice: choiceText,
7711
- condition,
7712
- skillCheck,
7713
- branch: ctx.processCommandsFunction(ctx.parserContext, choice.branch, choice),
7714
- index,
7715
- };
7716
- }
7717
-
7718
- function parseRenpyScript(errorHandler, code, fileName) {
7719
- const ctx = {
7720
- fileName,
7721
- error: (line, text) => errorHandler(ctx, line, text),
7722
- processCommandsFunction: processRenpyCommands,
7723
- indentSize: 0, // Will be overriden soon
7473
+ function parseScript(errorHandler, code, fileName) {
7474
+ const ctx = {
7475
+ fileName,
7476
+ error: (line, text) => errorHandler(ctx, line, text),
7477
+ processCommandsFunction: processCommands,
7478
+ indentSize: 0, // Will be overriden soon
7724
7479
  };
7725
7480
  ctx.indentSize = detectIndentation(ctx, code);
7726
- const lines = findRenpyLines(ctx, code);
7481
+ const lines = findLines(ctx, code);
7727
7482
  logger.log(lines);
7728
7483
  const script = {};
7729
7484
  for (const line of lines) {
@@ -7734,13 +7489,13 @@ function parseRenpyScript(errorHandler, code, fileName) {
7734
7489
  if (!line.branch) {
7735
7490
  ctx.error(line.line, `This line should have a branch but doesn't`);
7736
7491
  }
7737
- script[labelName] = processRenpyCommands(ctx, line.branch, undefined);
7492
+ script[labelName] = processCommands(ctx, line.branch, undefined);
7738
7493
  }
7739
7494
  return script;
7740
7495
  }
7741
- function processRenpyCommands(ctx, lines, parentLine) {
7496
+ function processCommands(ctx, lines, parentLine) {
7742
7497
  const branchContext = {
7743
- processCommandsFunction: processRenpyCommands,
7498
+ processCommandsFunction: processCommands,
7744
7499
  parserContext: ctx,
7745
7500
  lines,
7746
7501
  currentLine: 0,
@@ -7767,10 +7522,11 @@ function processRenpyCommands(ctx, lines, parentLine) {
7767
7522
  };
7768
7523
  branchContext.line = line;
7769
7524
  branchContext.command = command;
7770
- let parseFunction = parserFunctions[operator];
7525
+ const commandPlugin = vm$1.commands[operator];
7526
+ let parseFunction = commandPlugin?.parser;
7771
7527
  if (!parseFunction) {
7772
7528
  // default to text function
7773
- parseFunction = parserFunctions.text;
7529
+ parseFunction = vm$1.commands.text.parser;
7774
7530
  }
7775
7531
  parseFunction(branchContext);
7776
7532
  branch.push(command);
@@ -7835,7 +7591,7 @@ function parseCodeLine(codeToProcess) {
7835
7591
  words.forEach((word, index) => (words[index] = parseValue(word)));
7836
7592
  return words;
7837
7593
  }
7838
- function findRenpyLines(ctx, data) {
7594
+ function findLines(ctx, data) {
7839
7595
  const code = data.split(/\r?\n|$/).map((line) => {
7840
7596
  const commentIndex = line.search(/ *\/\//g);
7841
7597
  if (commentIndex !== -1) {
@@ -7843,10 +7599,10 @@ function findRenpyLines(ctx, data) {
7843
7599
  }
7844
7600
  return line;
7845
7601
  });
7846
- const lines = findRenpyBranches(ctx, code, 0, 0);
7602
+ const lines = findBranches(ctx, code, 0, 0);
7847
7603
  return lines.lines;
7848
7604
  }
7849
- function findRenpyBranches(ctx, code, startLine, indentLevel) {
7605
+ function findBranches(ctx, code, startLine, indentLevel) {
7850
7606
  let stillInBranch = true;
7851
7607
  let currentLine = startLine;
7852
7608
  const lines = [];
@@ -7869,7 +7625,7 @@ function findRenpyBranches(ctx, code, startLine, indentLevel) {
7869
7625
  if (lines.length === 0 || lineIndent - indentLevel !== 1) {
7870
7626
  ctx.error(currentLine, `Wrong double indentation`);
7871
7627
  }
7872
- const branchLines = findRenpyBranches(ctx, code, currentLine, lineIndent);
7628
+ const branchLines = findBranches(ctx, code, currentLine, lineIndent);
7873
7629
  lines[lines.length - 1].branch = branchLines.lines;
7874
7630
  currentLine = branchLines.endLine;
7875
7631
  }
@@ -7914,6 +7670,138 @@ function randomId() {
7914
7670
  return `${Date.now() - Math.floor(Math.random() * 99999999)}`;
7915
7671
  }
7916
7672
 
7673
+ var isMergeableObject = function isMergeableObject(value) {
7674
+ return isNonNullObject(value)
7675
+ && !isSpecial(value)
7676
+ };
7677
+
7678
+ function isNonNullObject(value) {
7679
+ return !!value && typeof value === 'object'
7680
+ }
7681
+
7682
+ function isSpecial(value) {
7683
+ var stringValue = Object.prototype.toString.call(value);
7684
+
7685
+ return stringValue === '[object RegExp]'
7686
+ || stringValue === '[object Date]'
7687
+ || isReactElement(value)
7688
+ }
7689
+
7690
+ // see https://github.com/facebook/react/blob/b5ac963fb791d1298e7f396236383bc955f916c1/src/isomorphic/classic/element/ReactElement.js#L21-L25
7691
+ var canUseSymbol = typeof Symbol === 'function' && Symbol.for;
7692
+ var REACT_ELEMENT_TYPE = canUseSymbol ? Symbol.for('react.element') : 0xeac7;
7693
+
7694
+ function isReactElement(value) {
7695
+ return value.$$typeof === REACT_ELEMENT_TYPE
7696
+ }
7697
+
7698
+ function emptyTarget(val) {
7699
+ return Array.isArray(val) ? [] : {}
7700
+ }
7701
+
7702
+ function cloneUnlessOtherwiseSpecified(value, options) {
7703
+ return (options.clone !== false && options.isMergeableObject(value))
7704
+ ? deepmerge(emptyTarget(value), value, options)
7705
+ : value
7706
+ }
7707
+
7708
+ function defaultArrayMerge(target, source, options) {
7709
+ return target.concat(source).map(function(element) {
7710
+ return cloneUnlessOtherwiseSpecified(element, options)
7711
+ })
7712
+ }
7713
+
7714
+ function getMergeFunction(key, options) {
7715
+ if (!options.customMerge) {
7716
+ return deepmerge
7717
+ }
7718
+ var customMerge = options.customMerge(key);
7719
+ return typeof customMerge === 'function' ? customMerge : deepmerge
7720
+ }
7721
+
7722
+ function getEnumerableOwnPropertySymbols(target) {
7723
+ return Object.getOwnPropertySymbols
7724
+ ? Object.getOwnPropertySymbols(target).filter(function(symbol) {
7725
+ return target.propertyIsEnumerable(symbol)
7726
+ })
7727
+ : []
7728
+ }
7729
+
7730
+ function getKeys(target) {
7731
+ return Object.keys(target).concat(getEnumerableOwnPropertySymbols(target))
7732
+ }
7733
+
7734
+ function propertyIsOnObject(object, property) {
7735
+ try {
7736
+ return property in object
7737
+ } catch(_) {
7738
+ return false
7739
+ }
7740
+ }
7741
+
7742
+ // Protects from prototype poisoning and unexpected merging up the prototype chain.
7743
+ function propertyIsUnsafe(target, key) {
7744
+ return propertyIsOnObject(target, key) // Properties are safe to merge if they don't exist in the target yet,
7745
+ && !(Object.hasOwnProperty.call(target, key) // unsafe if they exist up the prototype chain,
7746
+ && Object.propertyIsEnumerable.call(target, key)) // and also unsafe if they're nonenumerable.
7747
+ }
7748
+
7749
+ function mergeObject(target, source, options) {
7750
+ var destination = {};
7751
+ if (options.isMergeableObject(target)) {
7752
+ getKeys(target).forEach(function(key) {
7753
+ destination[key] = cloneUnlessOtherwiseSpecified(target[key], options);
7754
+ });
7755
+ }
7756
+ getKeys(source).forEach(function(key) {
7757
+ if (propertyIsUnsafe(target, key)) {
7758
+ return
7759
+ }
7760
+
7761
+ if (propertyIsOnObject(target, key) && options.isMergeableObject(source[key])) {
7762
+ destination[key] = getMergeFunction(key, options)(target[key], source[key], options);
7763
+ } else {
7764
+ destination[key] = cloneUnlessOtherwiseSpecified(source[key], options);
7765
+ }
7766
+ });
7767
+ return destination
7768
+ }
7769
+
7770
+ function deepmerge(target, source, options) {
7771
+ options = options || {};
7772
+ options.arrayMerge = options.arrayMerge || defaultArrayMerge;
7773
+ options.isMergeableObject = options.isMergeableObject || isMergeableObject;
7774
+ // cloneUnlessOtherwiseSpecified is added to `options` so that custom arrayMerge()
7775
+ // implementations can use it. The caller may not replace it.
7776
+ options.cloneUnlessOtherwiseSpecified = cloneUnlessOtherwiseSpecified;
7777
+
7778
+ var sourceIsArray = Array.isArray(source);
7779
+ var targetIsArray = Array.isArray(target);
7780
+ var sourceAndTargetTypesMatch = sourceIsArray === targetIsArray;
7781
+
7782
+ if (!sourceAndTargetTypesMatch) {
7783
+ return cloneUnlessOtherwiseSpecified(source, options)
7784
+ } else if (sourceIsArray) {
7785
+ return options.arrayMerge(target, source, options)
7786
+ } else {
7787
+ return mergeObject(target, source, options)
7788
+ }
7789
+ }
7790
+
7791
+ deepmerge.all = function deepmergeAll(array, options) {
7792
+ if (!Array.isArray(array)) {
7793
+ throw new Error('first argument should be an array')
7794
+ }
7795
+
7796
+ return array.reduce(function(prev, next) {
7797
+ return deepmerge(prev, next, options)
7798
+ }, {})
7799
+ };
7800
+
7801
+ var deepmerge_1 = deepmerge;
7802
+
7803
+ var cjs = deepmerge_1;
7804
+
7917
7805
  let key = Symbol('Store Injection Key');
7918
7806
  let store;
7919
7807
  function setupStore(options) {
@@ -7924,7 +7812,6 @@ function setupStore(options) {
7924
7812
  }
7925
7813
  // define injection key
7926
7814
  key = Symbol('Store Injection Key');
7927
- logger.setDebug(options.debug);
7928
7815
  logger.log('setup store');
7929
7816
  store = createStore({
7930
7817
  state: {
@@ -7965,6 +7852,7 @@ function setupStore(options) {
7965
7852
  options,
7966
7853
  flowState: 'menu',
7967
7854
  openModal: false,
7855
+ paused: false,
7968
7856
  },
7969
7857
  getters: {
7970
7858
  machineHead(state) {
@@ -7980,7 +7868,11 @@ function setupStore(options) {
7980
7868
  },
7981
7869
  },
7982
7870
  actions: {
7983
- async setupMachine({ commit, dispatch }, payload) {
7871
+ async setupMachine(ctx, payload) {
7872
+ const { commit } = ctx;
7873
+ if (payload.config.audioOptions.defaultMusic) {
7874
+ changeMusic(ctx, payload.config.audioOptions.defaultMusic);
7875
+ }
7984
7876
  const { scriptPaths } = payload;
7985
7877
  const filePromises = [];
7986
7878
  for (const path of scriptPaths) {
@@ -7993,7 +7885,7 @@ function setupStore(options) {
7993
7885
  const file = files[index];
7994
7886
  scripts = {
7995
7887
  ...scripts,
7996
- ...parseRenpyScript((ctx, line, error) => parserError(commit, ctx, line, error), file, scriptPaths[index]),
7888
+ ...parseScript((ctx, line, error) => parserError(commit, ctx, line, error), file, scriptPaths[index]),
7997
7889
  };
7998
7890
  }
7999
7891
  const end = Date.now();
@@ -8001,6 +7893,7 @@ function setupStore(options) {
8001
7893
  commit('setScript', scripts);
8002
7894
  },
8003
7895
  async startMachine({ commit, dispatch, state }) {
7896
+ dispatch('stopMusic');
8004
7897
  const config = getConfig();
8005
7898
  commit('setButtons', config.buttons);
8006
7899
  commit('setupSkills', config.skills);
@@ -8014,6 +7907,7 @@ function setupStore(options) {
8014
7907
  ];
8015
7908
  state.ready = true;
8016
7909
  commit('startPlaying');
7910
+ vm$1.callHook('onGameStart');
8017
7911
  },
8018
7912
  runLabel({ state, commit }, label) {
8019
7913
  const branch = state.machine.script[label];
@@ -8067,26 +7961,59 @@ function setupStore(options) {
8067
7961
  dispatch('runLabel', save.lastLabel);
8068
7962
  }
8069
7963
  },
8070
- async addNotification({ commit }, text) {
7964
+ stopMusic(ctx) {
7965
+ stopAudio(ctx.commit, ctx.state.audio.currentMusic);
7966
+ },
7967
+ menuReturn(ctx) {
7968
+ ctx.commit('reset');
7969
+ ctx.dispatch('stopMusic');
7970
+ ctx.commit('setFlowState', 'menu');
7971
+ if (getConfig().audioOptions.defaultMusic) {
7972
+ changeMusic(ctx, getConfig().audioOptions.defaultMusic);
7973
+ }
7974
+ },
7975
+ async addNotification(ctx, text) {
7976
+ const { commit } = ctx;
8071
7977
  const id = `${Date.now()}-${Math.random() * 10000}`;
8072
7978
  const notification = {
8073
7979
  text,
8074
7980
  };
8075
7981
  commit('addNotification', { id, notification });
7982
+ if (getConfig().notifications.alsoPrintInDialogue) {
7983
+ writeText(ctx, `[NOTIFICATION] ${text}`);
7984
+ }
8076
7985
  await timeout(getConfig().notifications.timeOnScreen * 1000);
8077
7986
  commit('deleteNotification', id);
8078
7987
  },
7988
+ incrementSkill({ dispatch, state }, { skill, amount }) {
7989
+ state.skills[skill].level += amount;
7990
+ dispatch('levelledUp', skill);
7991
+ },
7992
+ levelledUp({ dispatch, state }, skill) {
7993
+ const skillName = getConfig().skills[skill].name;
7994
+ const skillLevel = state.skills[skill].level;
7995
+ dispatch('addNotification', `Your skill in ${skillName} is now level ${skillLevel}`);
7996
+ },
7997
+ addXp({ state, dispatch }, { skill, amount }) {
7998
+ const skillState = state.skills[skill];
7999
+ skillState.xp += amount;
8000
+ if (skillState.xp > getConfig().skillOptions.xpPerLevel) {
8001
+ skillState.level += 1;
8002
+ skillState.xp = 0;
8003
+ dispatch('levelledUp', skill);
8004
+ }
8005
+ },
8079
8006
  },
8080
8007
  mutations: {
8081
8008
  setLoadedData(state, save) {
8082
8009
  state.machine.data = save.data;
8083
- state.skills = save.skills;
8010
+ state.skills = cjs(state.skillChecks, save.skills);
8084
8011
  state.dialog = save.dialog;
8085
- state.buttons = save.buttons;
8012
+ state.buttons = cjs(state.buttons, save.buttons);
8086
8013
  state.lastLabel = save.lastLabel;
8087
- state.skillChecks = save.skillChecks;
8014
+ state.skillChecks = cjs(state.skillChecks, save.skillChecks);
8088
8015
  state.playTime.previousPlaytime = save.playTime;
8089
- state.hudStats = save.hudStats;
8016
+ state.hudStats = cjs(state.hudStats, save.hudStats);
8090
8017
  state.currentScreen = save.currentScreen;
8091
8018
  state.audio.currentMusic = save.audio.currentMusic;
8092
8019
  },
@@ -8124,6 +8051,7 @@ function setupStore(options) {
8124
8051
  for (const skill in skills) {
8125
8052
  state.skills[skill] = {
8126
8053
  level: skills[skill].startingLevel || 0,
8054
+ xp: 0,
8127
8055
  };
8128
8056
  }
8129
8057
  },
@@ -8132,9 +8060,6 @@ function setupStore(options) {
8132
8060
  state.hudStats[stat] = stats[stat].startingValue;
8133
8061
  }
8134
8062
  },
8135
- incrementSkill(state, { skill, amount }) {
8136
- state.skills[skill].level += amount;
8137
- },
8138
8063
  setStat(state, { stat, amount }) {
8139
8064
  state.hudStats[stat] = amount;
8140
8065
  },
@@ -8248,112 +8173,607 @@ function setupStore(options) {
8248
8173
  state.openModal = 'menu';
8249
8174
  }
8250
8175
  },
8176
+ pause(state) {
8177
+ state.paused = true;
8178
+ },
8179
+ unpause(state) {
8180
+ state.paused = false;
8181
+ },
8251
8182
  },
8252
8183
  plugins,
8253
8184
  });
8254
8185
  return {
8255
- store,
8256
- key,
8186
+ store,
8187
+ key,
8188
+ };
8189
+ }
8190
+
8191
+ let canvas;
8192
+ let ctx;
8193
+ let store$1;
8194
+ const mousePos = {
8195
+ x: 0,
8196
+ y: 0,
8197
+ };
8198
+ function startGameLoop(appStore) {
8199
+ store$1 = appStore;
8200
+ document.addEventListener('mousemove', (e) => {
8201
+ mousePos.x = e.clientX;
8202
+ mousePos.y = e.clientY;
8203
+ });
8204
+ document.addEventListener('click', debounce(mouseclick, 100, { maxWait: 200 }));
8205
+ gameLoop();
8206
+ }
8207
+ function gameLoop() {
8208
+ if (store$1.state.playing) {
8209
+ if (!canvas) {
8210
+ canvas = document.querySelector('#background-canvas');
8211
+ if (canvas && !ctx) {
8212
+ ctx = canvas.getContext('2d');
8213
+ }
8214
+ }
8215
+ else {
8216
+ ctx.fillStyle = 'white';
8217
+ ctx.fillRect(0, 0, canvas.width, canvas.height);
8218
+ const currentScreen = store$1.state.currentScreen;
8219
+ const screen = getConfig().screens[currentScreen];
8220
+ const bg = screen.background;
8221
+ ctx.drawImage(images[bg], 0, 0);
8222
+ let foundCollision = false;
8223
+ const scaledMousePos = screenToCanvas(mousePos.x, mousePos.y, canvas);
8224
+ if (screen.buttons) {
8225
+ for (const buttonName of screen.buttons) {
8226
+ if (store$1.state.buttons[buttonName].enabled) {
8227
+ const button = getConfig().buttons[buttonName];
8228
+ const image = images[button.background];
8229
+ ctx.drawImage(image, button.position.left, button.position.top);
8230
+ if (aabb(scaledMousePos.x, scaledMousePos.y, 1, 1, button.position.left, button.position.top, button.position.width, button.position.height)) {
8231
+ foundCollision = true;
8232
+ }
8233
+ }
8234
+ }
8235
+ }
8236
+ if (foundCollision) {
8237
+ document.body.style.cursor = 'pointer';
8238
+ }
8239
+ else {
8240
+ document.body.style.cursor = 'default';
8241
+ }
8242
+ ctx.fillStyle = 'black';
8243
+ ctx.textAlign = 'left';
8244
+ // ctx.fillText(`x: ${scaledMousePos.x}, y: ${scaledMousePos.y}`, 0, 20);
8245
+ }
8246
+ }
8247
+ else {
8248
+ canvas = undefined;
8249
+ ctx = undefined;
8250
+ }
8251
+ window.requestAnimationFrame(gameLoop);
8252
+ }
8253
+ function mouseclick(e) {
8254
+ if (!canvas) {
8255
+ return;
8256
+ }
8257
+ mousePos.x = e.clientX;
8258
+ mousePos.y = e.clientY;
8259
+ const scaledMousePos = screenToCanvas(mousePos.x, mousePos.y, canvas);
8260
+ const currentScreen = store$1.state.currentScreen;
8261
+ const screen = getConfig().screens[currentScreen];
8262
+ if (screen.buttons) {
8263
+ for (const buttonName of screen.buttons) {
8264
+ if (store$1.state.buttons[buttonName].enabled) {
8265
+ const button = getConfig().buttons[buttonName];
8266
+ if (aabb(scaledMousePos.x, scaledMousePos.y, 1, 1, button.position.left, button.position.top, button.position.width, button.position.height)) {
8267
+ // clicked button
8268
+ const scriptToRun = button.action;
8269
+ const newStack = {
8270
+ branch: store$1.state.machine.script[scriptToRun],
8271
+ currentIndex: 0,
8272
+ label: scriptToRun,
8273
+ };
8274
+ store$1.commit('setStack', newStack);
8275
+ store$1.dispatch('runLine');
8276
+ }
8277
+ }
8278
+ }
8279
+ }
8280
+ }
8281
+
8282
+ class CommandPlugin {
8283
+ keyword;
8284
+ runner;
8285
+ parser;
8286
+ constructor(keyword, runner, parser) {
8287
+ this.keyword = keyword;
8288
+ this.runner = runner;
8289
+ this.parser = parser;
8290
+ }
8291
+ }
8292
+ function generateParser(keyword) {
8293
+ return (ctx) => {
8294
+ ctx.command.commandType = keyword;
8295
+ ctx.currentLine++;
8296
+ };
8297
+ }
8298
+
8299
+ const add = async (context, cmd, choices) => {
8300
+ const { commit, dispatch } = context;
8301
+ const addKey = cmd.args[0];
8302
+ const addValue = cmd.args[1];
8303
+ commit('addInstruction', { path: addKey, value: addValue });
8304
+ return dispatch('nextLine');
8305
+ };
8306
+ const addPlugin = new CommandPlugin('add', add, generateParser('add'));
8307
+
8308
+ const addLevelRunner = async (context, cmd) => {
8309
+ const { commit, dispatch } = context;
8310
+ const skillKey = cmd.args[0];
8311
+ const levelToAdd = cmd.args[1];
8312
+ if (!skillKey || !levelToAdd) {
8313
+ error(commit, `add_level command needs a skill id and a value as parameters`);
8314
+ }
8315
+ dispatch('incrementSkill', {
8316
+ skill: skillKey,
8317
+ amount: levelToAdd,
8318
+ });
8319
+ return dispatch('nextLine');
8320
+ };
8321
+ const addLevelParser = (ctx) => {
8322
+ ctx.command.commandType = 'add_level';
8323
+ ctx.currentLine++;
8324
+ };
8325
+ const addLevelPlugin = new CommandPlugin('add_level', addLevelRunner, addLevelParser);
8326
+
8327
+ const addStatRunner = async (context, cmd) => {
8328
+ const { commit, dispatch } = context;
8329
+ const statKey = cmd.args[0];
8330
+ const amountToAdd = cmd.args[1];
8331
+ if (!statKey || !amountToAdd) {
8332
+ error(commit, `add_stat command needs a stat id and a value as parameters`);
8333
+ }
8334
+ commit('addStat', {
8335
+ stat: statKey,
8336
+ amount: amountToAdd,
8337
+ });
8338
+ return dispatch('nextLine');
8339
+ };
8340
+ const addStatParser = (ctx) => {
8341
+ ctx.command.commandType = 'add_stat';
8342
+ ctx.currentLine++;
8343
+ };
8344
+ const addStatPlugin = new CommandPlugin('add_stat', addStatRunner, addStatParser);
8345
+
8346
+ const addXp = async (context, cmd) => {
8347
+ const { commit, dispatch } = context;
8348
+ const xpKey = cmd.args[0];
8349
+ const xpToAdd = cmd.args[1];
8350
+ if (!xpKey || !xpToAdd) {
8351
+ error(commit, `add_xp command needs a skill id and a value as parameters`);
8352
+ }
8353
+ dispatch('addXp', {
8354
+ skill: xpKey,
8355
+ amount: xpToAdd,
8356
+ });
8357
+ return dispatch('nextLine');
8358
+ };
8359
+ const addXpPlugin = new CommandPlugin('add_xp', addXp, generateParser('add_xp'));
8360
+
8361
+ const choice = async (context, cmd) => {
8362
+ await runChoice(context, cmd);
8363
+ };
8364
+ async function runChoice(context, cmd) {
8365
+ const options = cmd.options;
8366
+ const prompt = options.prompt;
8367
+ const choices = options.choices
8368
+ .filter((choice) => {
8369
+ // Delete the choice if it fails a condition
8370
+ if (choice.condition) {
8371
+ return runCondition(context, choice.condition);
8372
+ }
8373
+ if (choice.skillCheck) {
8374
+ if (!getSkillCheckState(context, choice.skillCheck.id).available &&
8375
+ choice.skillCheck.hideAfterRoll) {
8376
+ return false;
8377
+ }
8378
+ }
8379
+ return true;
8380
+ })
8381
+ .map((choice) => {
8382
+ let text = choice.choice;
8383
+ let choiceAllowed = true;
8384
+ if (choice.skillCheck) {
8385
+ const check = choice.skillCheck;
8386
+ const { difficultyText, allowed } = getSkillCheckText({
8387
+ context,
8388
+ skill: check.skill,
8389
+ skillCheckId: check.id,
8390
+ value: check.value,
8391
+ });
8392
+ text = `${difficultyText} ${text}`;
8393
+ choiceAllowed = allowed;
8394
+ }
8395
+ const result = {
8396
+ choice: text,
8397
+ originalIndex: choice.index,
8398
+ allowed: choiceAllowed,
8399
+ };
8400
+ return result;
8401
+ });
8402
+ runCommand(context, prompt, choices);
8403
+ }
8404
+ const choiceParser = (ctx) => {
8405
+ const { line, command } = ctx;
8406
+ if (!line.branch || line.branch.length < 2) {
8407
+ ctx.parserContext.error(line.line, `Choice menu needs to have at least one option`);
8408
+ }
8409
+ const prompt = line.branch[0];
8410
+ const choices = line.branch.slice(1);
8411
+ const prompts = choices.map((choice, index) => {
8412
+ if (!choice.branch) {
8413
+ ctx.parserContext.error(choice.line, `Choice option doesn't have any branch to go to (${choice.code})`);
8414
+ }
8415
+ return parseChoiceOption(ctx, choice, index);
8416
+ });
8417
+ command.options = {
8418
+ prompt: ctx.processCommandsFunction(ctx.parserContext, [prompt], line)[0],
8419
+ choices: prompts,
8420
+ };
8421
+ command.commandType = 'choice';
8422
+ ctx.currentLine++;
8423
+ };
8424
+ function parseChoiceOption(ctx, choice, index) {
8425
+ let choiceText = choice.operator;
8426
+ let condition;
8427
+ let skillCheck;
8428
+ if (choice.operator === 'roll') {
8429
+ if (choice.args.length < 4) {
8430
+ ctx.parserContext.error(choice.line, `Skillchecks need 4 arguments!`);
8431
+ }
8432
+ choiceText = choice.args[3];
8433
+ const hideAfterRoll = choice.args.length >= 5;
8434
+ const successBranch = choice.branch[0];
8435
+ const failureBranch = choice.branch[1];
8436
+ const success = {
8437
+ text: successBranch.args[0],
8438
+ branch: ctx.processCommandsFunction(ctx.parserContext, successBranch.branch, choice),
8439
+ };
8440
+ let failedBranch;
8441
+ if (failureBranch.branch) {
8442
+ failedBranch = ctx.processCommandsFunction(ctx.parserContext, failureBranch.branch, choice);
8443
+ }
8444
+ const failure = {
8445
+ text: failureBranch.args[0],
8446
+ branch: failedBranch,
8447
+ };
8448
+ skillCheck = {
8449
+ id: choice.args[0],
8450
+ skill: choice.args[1],
8451
+ value: choice.args[2],
8452
+ hideAfterRoll,
8453
+ success,
8454
+ failure,
8455
+ };
8456
+ }
8457
+ if (choice.args[0] === 'if') {
8458
+ condition = choice.args[1];
8459
+ }
8460
+ return {
8461
+ choice: choiceText,
8462
+ condition,
8463
+ skillCheck,
8464
+ branch: ctx.processCommandsFunction(ctx.parserContext, choice.branch, choice),
8465
+ index,
8257
8466
  };
8258
- }
8467
+ }
8468
+ const choicePlugin = new CommandPlugin('choice', choice, choiceParser);
8259
8469
 
8260
- let canvas;
8261
- let ctx;
8262
- let store$1;
8263
- const mousePos = {
8264
- x: 0,
8265
- y: 0,
8470
+ const clearDialog = async (context, cmd) => {
8471
+ const { commit, dispatch } = context;
8472
+ commit('clearDialog');
8473
+ return dispatch('nextLine');
8266
8474
  };
8267
- function startGameLoop(appStore) {
8268
- store$1 = appStore;
8269
- document.addEventListener('mousemove', (e) => {
8270
- mousePos.x = e.clientX;
8271
- mousePos.y = e.clientY;
8272
- });
8273
- document.addEventListener('click', debounce(mouseclick, 100, { maxWait: 200 }));
8274
- gameLoop();
8275
- }
8276
- function gameLoop() {
8277
- if (store$1.state.playing) {
8278
- if (!canvas) {
8279
- canvas = document.querySelector('#background-canvas');
8280
- if (canvas && !ctx) {
8281
- ctx = canvas.getContext('2d');
8282
- }
8283
- }
8284
- else {
8285
- ctx.fillStyle = 'white';
8286
- ctx.fillRect(0, 0, canvas.width, canvas.height);
8287
- const currentScreen = store$1.state.currentScreen;
8288
- const screen = getConfig().screens[currentScreen];
8289
- const bg = screen.background;
8290
- ctx.drawImage(images[bg], 0, 0);
8291
- let foundCollision = false;
8292
- const scaledMousePos = screenToCanvas(mousePos.x, mousePos.y, canvas);
8293
- if (screen.buttons) {
8294
- for (const buttonName of screen.buttons) {
8295
- if (store$1.state.buttons[buttonName].enabled) {
8296
- const button = getConfig().buttons[buttonName];
8297
- const image = images[button.background];
8298
- ctx.drawImage(image, button.position.left, button.position.top);
8299
- if (aabb(scaledMousePos.x, scaledMousePos.y, 1, 1, button.position.left, button.position.top, button.position.width, button.position.height)) {
8300
- foundCollision = true;
8301
- }
8302
- }
8303
- }
8304
- }
8305
- if (foundCollision) {
8306
- document.body.style.cursor = 'pointer';
8307
- }
8308
- else {
8309
- document.body.style.cursor = 'default';
8310
- }
8311
- ctx.fillStyle = 'black';
8312
- ctx.textAlign = 'left';
8313
- // ctx.fillText(`x: ${scaledMousePos.x}, y: ${scaledMousePos.y}`, 0, 20);
8314
- }
8475
+ const clearDialogPlugin = new CommandPlugin('clear_dialog', clearDialog, generateParser('clear_dialog'));
8476
+
8477
+ const ifFunction = async (context, cmd) => {
8478
+ const { commit, dispatch } = context;
8479
+ const newBranch = runConditionCommand(context, cmd);
8480
+ if (newBranch) {
8481
+ const newStack = {
8482
+ branch: newBranch,
8483
+ currentIndex: 0,
8484
+ };
8485
+ commit('addStack', newStack);
8486
+ return dispatch('runLine');
8487
+ }
8488
+ return dispatch('nextLine');
8489
+ };
8490
+ const ifParser = (ctx) => {
8491
+ const { command, lines, currentLine, line } = ctx;
8492
+ command.commandType = 'if';
8493
+ let failure;
8494
+ const nextLine = getLine(lines, currentLine + 1);
8495
+ if (nextLine && nextLine.operator === 'else') {
8496
+ failure = ctx.processCommandsFunction(ctx.parserContext, nextLine.branch, line);
8497
+ ctx.currentLine++;
8498
+ }
8499
+ command.options = {
8500
+ condition: command.args[0],
8501
+ success: ctx.processCommandsFunction(ctx.parserContext, line.branch, line),
8502
+ failure,
8503
+ };
8504
+ ctx.currentLine++;
8505
+ };
8506
+ const ifCommand = new CommandPlugin('if', ifFunction, ifParser);
8507
+
8508
+ const jump = async (context, cmd, choices) => {
8509
+ const { state, commit, dispatch } = context;
8510
+ const branch = cmd.args[0];
8511
+ const newStack = {
8512
+ branch: state.machine.script[branch],
8513
+ label: branch,
8514
+ currentIndex: 0,
8515
+ };
8516
+ commit('setStack', newStack);
8517
+ await dispatch('saveGame');
8518
+ await dispatch('runLine');
8519
+ };
8520
+ const jumpCommand = new CommandPlugin('jump', jump, generateParser('jump'));
8521
+
8522
+ const notify = async (context, cmd) => {
8523
+ const { dispatch } = context;
8524
+ const text = cmd.args[0];
8525
+ dispatch('addNotification', text);
8526
+ return dispatch('nextLine');
8527
+ };
8528
+ const notifyPlugin = new CommandPlugin('notify', notify, generateParser('notify'));
8529
+
8530
+ const pause = async (context, cmd) => {
8531
+ const { commit, dispatch } = context;
8532
+ const pauseOptions = cmd.options;
8533
+ if (pauseOptions.mode === 'music') {
8534
+ pauseAudio(commit, context.state.audio.currentMusic);
8535
+ }
8536
+ else if (pauseOptions.mode === 'sound' && pauseOptions.audio) {
8537
+ pauseAudio(commit, pauseOptions.audio);
8315
8538
  }
8316
8539
  else {
8317
- canvas = undefined;
8318
- ctx = undefined;
8540
+ error(commit, `pause first option needs to either be in music mode, or if stopping a sound needs to have the sound name supplied as second argument.`);
8319
8541
  }
8320
- window.requestAnimationFrame(gameLoop);
8321
- }
8322
- function mouseclick(e) {
8323
- if (!canvas) {
8324
- return;
8542
+ return dispatch('nextLine');
8543
+ };
8544
+ const pauseParser = (ctx) => {
8545
+ const { command } = ctx;
8546
+ command.commandType = 'pause';
8547
+ command.options = {
8548
+ mode: command.args[0],
8549
+ audio: command.args[1],
8550
+ };
8551
+ ctx.currentLine++;
8552
+ };
8553
+ const pauseCommand = new CommandPlugin('pause', pause, pauseParser);
8554
+
8555
+ const play = async (context, cmd) => {
8556
+ const { dispatch } = context;
8557
+ const playOptions = cmd.options;
8558
+ if (playOptions.mode === 'music') {
8559
+ changeMusic(context, playOptions.audio);
8325
8560
  }
8326
- mousePos.x = e.clientX;
8327
- mousePos.y = e.clientY;
8328
- const scaledMousePos = screenToCanvas(mousePos.x, mousePos.y, canvas);
8329
- const currentScreen = store$1.state.currentScreen;
8330
- const screen = getConfig().screens[currentScreen];
8331
- if (screen.buttons) {
8332
- for (const buttonName of screen.buttons) {
8333
- if (store$1.state.buttons[buttonName].enabled) {
8334
- const button = getConfig().buttons[buttonName];
8335
- if (aabb(scaledMousePos.x, scaledMousePos.y, 1, 1, button.position.left, button.position.top, button.position.width, button.position.height)) {
8336
- // clicked button
8337
- const scriptToRun = button.action;
8338
- const newStack = {
8339
- branch: store$1.state.machine.script[scriptToRun],
8340
- currentIndex: 0,
8341
- label: scriptToRun,
8342
- };
8343
- store$1.commit('setStack', newStack);
8344
- store$1.dispatch('runLine');
8345
- }
8346
- }
8347
- }
8561
+ else {
8562
+ playAudio(context.commit, playOptions.audio);
8563
+ }
8564
+ return dispatch('nextLine');
8565
+ };
8566
+ const playParser = (ctx) => {
8567
+ const { command } = ctx;
8568
+ command.commandType = 'play';
8569
+ command.options = {
8570
+ mode: command.args[0],
8571
+ audio: command.args[1],
8572
+ };
8573
+ ctx.currentLine++;
8574
+ };
8575
+ const playCommand = new CommandPlugin('play', play, playParser);
8576
+
8577
+ const set = async (context, cmd, choices) => {
8578
+ const { commit, dispatch } = context;
8579
+ const key = cmd.args[0];
8580
+ const value = cmd.args[1];
8581
+ commit('setData', { path: key, value });
8582
+ return dispatch('nextLine');
8583
+ };
8584
+ const setCommand = new CommandPlugin('set', set, generateParser('set'));
8585
+
8586
+ const setButton = async (context, cmd) => {
8587
+ const { commit, dispatch } = context;
8588
+ commit('changeButton', {
8589
+ button: cmd.args[0],
8590
+ enabled: cmd.args[1],
8591
+ });
8592
+ return dispatch('nextLine');
8593
+ };
8594
+ const setButtonParser = (ctx) => {
8595
+ const { command, line } = ctx;
8596
+ command.commandType = 'set_button';
8597
+ if (command.args.length !== 2) {
8598
+ ctx.parserContext.error(line.line, `set_button command should have 2 arguments`);
8599
+ }
8600
+ ctx.currentLine++;
8601
+ };
8602
+ const setButtonCommand = new CommandPlugin('set_button', setButton, setButtonParser);
8603
+
8604
+ const setScreen = async (context, cmd) => {
8605
+ const { commit, dispatch } = context;
8606
+ commit('setScreen', cmd.options.screen);
8607
+ return dispatch('nextLine');
8608
+ };
8609
+ const setScreenParser = (ctx) => {
8610
+ const { command } = ctx;
8611
+ command.commandType = 'set_screen';
8612
+ command.options = {
8613
+ screen: command.args[0],
8614
+ };
8615
+ ctx.currentLine++;
8616
+ };
8617
+ const setScreenCommand = new CommandPlugin('set_screen', setScreen, setScreenParser);
8618
+
8619
+ const setStat = async (context, cmd) => {
8620
+ const { commit, dispatch } = context;
8621
+ const setStatKey = cmd.args[0];
8622
+ const setAmount = cmd.args[1];
8623
+ if (!setStatKey || !setAmount) {
8624
+ error(commit, `set_stat command needs a stat id and a value as parameters`);
8625
+ }
8626
+ commit('setStat', {
8627
+ stat: setStatKey,
8628
+ amount: setAmount,
8629
+ });
8630
+ return dispatch('nextLine');
8631
+ };
8632
+ const setStatCommand = new CommandPlugin('set_stat', setStat, generateParser('set_stat'));
8633
+
8634
+ const stop = async (context, cmd) => {
8635
+ const { commit, dispatch } = context;
8636
+ const stopOptions = cmd.options;
8637
+ if (stopOptions.mode === 'music') {
8638
+ stopAudio(commit, context.state.audio.currentMusic);
8639
+ }
8640
+ else if (stopOptions.mode === 'sound' && stopOptions.audio) {
8641
+ stopAudio(commit, stopOptions.audio);
8642
+ }
8643
+ else {
8644
+ error(commit, `stop option needs to either be in music mode, or if stopping a sound needs to have the sound name supplied as second argument.`);
8645
+ }
8646
+ return dispatch('nextLine');
8647
+ };
8648
+ const stopParser = (ctx) => {
8649
+ const { command } = ctx;
8650
+ command.commandType = 'stop';
8651
+ command.options = {
8652
+ mode: command.args[0],
8653
+ audio: command.args[1],
8654
+ };
8655
+ ctx.currentLine++;
8656
+ };
8657
+ const stopCommand = new CommandPlugin('stop', stop, stopParser);
8658
+
8659
+ const talk = async (context, cmd, choices) => {
8660
+ const { commit } = context;
8661
+ await textCommand(commit, {
8662
+ speaker: cmd.args[0],
8663
+ pose: cmd.args[1],
8664
+ text: `"${cmd.args[2]}"`,
8665
+ choices,
8666
+ interactive: true,
8667
+ });
8668
+ };
8669
+ const talkParser = (ctx) => {
8670
+ const { command, line } = ctx;
8671
+ command.commandType = 'talk';
8672
+ if (command.args.length < 3) {
8673
+ ctx.parserContext.error(line.line, `Talk command needs 3 arguments!`);
8348
8674
  }
8675
+ ctx.currentLine++;
8676
+ };
8677
+ const talkCommand = new CommandPlugin('talk', talk, talkParser);
8678
+
8679
+ const text = async (context, cmd, choices) => {
8680
+ const { commit } = context;
8681
+ await textCommand(commit, {
8682
+ speaker: 'game',
8683
+ text: cmd.options.text,
8684
+ choices,
8685
+ interactive: true,
8686
+ });
8687
+ };
8688
+ const textParser = (ctx) => {
8689
+ const { command, line } = ctx;
8690
+ command.commandType = 'text';
8691
+ command.options = {
8692
+ text: line.operator,
8693
+ };
8694
+ ctx.currentLine++;
8695
+ };
8696
+ const textCommandPlugin = new CommandPlugin('text', text, textParser);
8697
+
8698
+ const wait = async (context, cmd) => {
8699
+ const { dispatch } = context;
8700
+ await timeout(cmd.options.duration);
8701
+ return dispatch('nextLine');
8702
+ };
8703
+ const waitParser = (ctx) => {
8704
+ const { command } = ctx;
8705
+ command.commandType = 'wait';
8706
+ command.options = {
8707
+ duration: parseInt(command.args[0], 10),
8708
+ };
8709
+ ctx.currentLine++;
8710
+ };
8711
+ const waitCommand = new CommandPlugin('wait', wait, waitParser);
8712
+
8713
+ function registerBaseCommands(vm) {
8714
+ vm.addCommand(addLevelPlugin);
8715
+ vm.addCommand(addStatPlugin);
8716
+ vm.addCommand(addXpPlugin);
8717
+ vm.addCommand(addPlugin);
8718
+ vm.addCommand(choicePlugin);
8719
+ vm.addCommand(clearDialogPlugin);
8720
+ vm.addCommand(ifCommand);
8721
+ vm.addCommand(jumpCommand);
8722
+ vm.addCommand(notifyPlugin);
8723
+ vm.addCommand(pauseCommand);
8724
+ vm.addCommand(playCommand);
8725
+ vm.addCommand(setButtonCommand);
8726
+ vm.addCommand(setScreenCommand);
8727
+ vm.addCommand(setStatCommand);
8728
+ vm.addCommand(setCommand);
8729
+ vm.addCommand(stopCommand);
8730
+ vm.addCommand(talkCommand);
8731
+ vm.addCommand(textCommandPlugin);
8732
+ vm.addCommand(waitCommand);
8733
+ }
8734
+
8735
+ class NarratPlugin {
8736
+ onPageLoaded() { }
8737
+ onNarratSetup() { }
8738
+ onAppMounted() { }
8739
+ onAssetsLoaded() { }
8740
+ onGameSetup() { }
8741
+ onGameStart() { }
8742
+ onGameMounted() { }
8743
+ onGameDismounted() { }
8744
+ }
8745
+
8746
+ function registerPlugin(plugin) {
8747
+ vm$1.addPlugin(plugin);
8748
+ }
8749
+ function addCommand(command) {
8750
+ vm$1.addCommand(command);
8349
8751
  }
8350
8752
 
8753
+ var plugins = /*#__PURE__*/Object.freeze({
8754
+ __proto__: null,
8755
+ CommandPlugin: CommandPlugin,
8756
+ NarratPlugin: NarratPlugin,
8757
+ registerPlugin: registerPlugin,
8758
+ addCommand: addCommand,
8759
+ generateParser: generateParser
8760
+ });
8761
+
8762
+ var display = /*#__PURE__*/Object.freeze({
8763
+ __proto__: null,
8764
+ aspectRatioFit: aspectRatioFit
8765
+ });
8766
+
8351
8767
  let app;
8352
8768
  let store$2;
8769
+ vm$1.callHook('onPageLoaded');
8353
8770
  async function startApp(config, options) {
8771
+ registerBaseCommands(vm$1);
8772
+ logManager.setupDebugger(options.debug);
8354
8773
  const configFile = await getFile('data/config.json');
8355
8774
  setConfig(JSON.parse(configFile));
8356
- console.log('%c Narrat game engine – 0.9.3 - June 5, 2022 20:22:00', 'background: #222; color: #bada55');
8775
+ console.log('%c Narrat game engine – 0.11.0 - June 15, 2022 15:25:13', 'background: #222; color: #bada55');
8776
+ vm$1.callHook('onNarratSetup');
8357
8777
  const storeSetup = setupStore(options);
8358
8778
  store$2 = storeSetup.store;
8359
8779
  app = createApp(script$b, {
@@ -8375,6 +8795,10 @@ async function startApp(config, options) {
8375
8795
  store: store$2,
8376
8796
  app,
8377
8797
  state: store$2.state,
8798
+ vm: vm$1,
8799
+ jump: (label) => {
8800
+ store$2.dispatch('runLabel', label);
8801
+ },
8378
8802
  };
8379
8803
  window.narrat = narrat;
8380
8804
  }
@@ -8387,7 +8811,14 @@ async function startApp(config, options) {
8387
8811
  });
8388
8812
  }
8389
8813
  startGameLoop(store$2);
8390
- }
8814
+ }
8815
+ var index = {
8816
+ startApp,
8817
+ plugins,
8818
+ display,
8819
+ config: config$2,
8820
+ };
8391
8821
 
8392
- export { startApp };
8822
+ export default index;
8823
+ export { config$2 as config, display, plugins, startApp };
8393
8824
  //# sourceMappingURL=index.esm.js.map