UNPKG

vn-engine

Version:

A powerful, flexible TypeScript library for creating visual novels and interactive narratives

2 lines â€ĸ 67.8 kB
(function(y,h){typeof exports=="object"&&typeof module<"u"?h(exports,require("lodash"),require("js-yaml")):typeof define=="function"&&define.amd?define(["exports","lodash","js-yaml"],h):(y=typeof globalThis<"u"?globalThis:y||self,h(y.VNEngine={},y._,y.yaml))})(this,function(y,h,D){"use strict";function B(r){const e=Object.create(null,{[Symbol.toStringTag]:{value:"Module"}});if(r){for(const t in r)if(t!=="default"){const s=Object.getOwnPropertyDescriptor(r,t);Object.defineProperty(e,t,s.get?s:{enumerable:!0,get:()=>r[t]})}}return e.default=r,Object.freeze(e)}const P=B(D);class R{constructor(e){this.state={currentScene:"",currentInstruction:0,variables:new Map,storyFlags:new Set,choiceHistory:[],...e}}setCurrentScene(e){this.state.currentScene=e}getCurrentScene(){return this.state.currentScene}setCurrentInstruction(e){this.state.currentInstruction=e}getCurrentInstruction(){return this.state.currentInstruction}setStoryFlag(e){this.state.storyFlags.add(e)}clearStoryFlag(e){this.state.storyFlags.delete(e)}hasStoryFlag(e){return this.state.storyFlags.has(e)}setVariable(e,t){this.state.variables.set(e,t)}getVariable(e){return this.state.variables.get(e)}addToVariable(e,t){if(e.includes(".")){const s=e.split("."),n=s[0],i=s.slice(1).join(".");let a=this.getVariable(n);(!a||typeof a!="object")&&(a={});const o=this.getNestedProperty(a,i)||0;this.setNestedProperty(a,i,o+t),this.setVariable(n,a)}else{const s=this.getVariable(e)||0;this.setVariable(e,s+t)}}getNestedProperty(e,t){if(!(!e||!t))return t.split(".").reduce((s,n)=>s&&s[n]!==void 0?s[n]:void 0,e)}setNestedProperty(e,t,s){const n=t.split(".");let i=e;for(let a=0;a<n.length-1;a++)(!i[n[a]]||typeof i[n[a]]!="object")&&(i[n[a]]={}),i=i[n[a]];i[n[n.length-1]]=s}addChoice(e){this.state.choiceHistory.push(e)}getChoiceHistory(){return[...this.state.choiceHistory]}playerChose(e,t){return this.state.choiceHistory.some(s=>s.choiceText===e&&(!t||s.scene===t))}getList(e){const t=this.getVariable(e);return Array.isArray(t)?t:[]}setList(e,t){this.setVariable(e,t)}addToList(e,t){const s=this.getList(e);s.push(t),this.setList(e,s)}addTime(e){const t=this.getVariable("gameTime")||0;this.setVariable("gameTime",t+e),this.setVariable("currentTime",t+e)}getCurrentTime(){return this.getVariable("gameTime")||this.getVariable("currentTime")||0}getState(){return{currentScene:this.state.currentScene,currentInstruction:this.state.currentInstruction,variables:new Map(this.state.variables),storyFlags:new Set(this.state.storyFlags),choiceHistory:[...this.state.choiceHistory]}}serialize(){return{currentScene:this.state.currentScene,currentInstruction:this.state.currentInstruction,variables:Array.from(this.state.variables.entries()),storyFlags:Array.from(this.state.storyFlags),choiceHistory:[...this.state.choiceHistory],schemaVersion:"1.0.0",saveDate:new Date().toISOString()}}deserialize(e){try{this.state={currentScene:e.currentScene||"",currentInstruction:e.currentInstruction||0,variables:new Map(e.variables||[]),storyFlags:new Set(e.storyFlags||[]),choiceHistory:e.choiceHistory||[]}}catch(t){console.error("Failed to deserialize game state:",t),this.state={currentScene:"",currentInstruction:0,variables:new Map,storyFlags:new Set,choiceHistory:[]}}}setBulkVariables(e){try{Object.entries(e).forEach(([t,s])=>{this.setVariable(t,s)})}catch(t){console.error("Error setting bulk variables:",t)}}setBulkFlags(e){try{e.forEach(t=>{typeof t=="string"&&t.length>0&&this.setStoryFlag(t)})}catch(t){console.error("Error setting bulk flags:",t)}}reset(){this.state={currentScene:"",currentInstruction:0,variables:new Map,storyFlags:new Set,choiceHistory:[]}}validateState(){const e=[];return typeof this.state.currentScene!="string"&&e.push("Current scene must be a string"),(typeof this.state.currentInstruction!="number"||this.state.currentInstruction<0)&&e.push("Current instruction must be a non-negative number"),this.state.variables instanceof Map||e.push("Variables must be a Map"),this.state.storyFlags instanceof Set||e.push("Story flags must be a Set"),Array.isArray(this.state.choiceHistory)||e.push("Choice history must be an array"),{valid:e.length===0,errors:e}}}const A=class A{render(e,t){try{let s=e;return s=this.processConditionals(s,t),s=this.processVariables(s,t),s}catch(s){return console.error("Simple template rendering error:",s),`[Template Error: ${s.message}]`}}processConditionals(e,t){return e=e.replace(A.CONDITION_ELSE_REGEX,(s,n,i,a)=>this.evaluateCondition(n.trim(),t)?i:a),e=e.replace(A.CONDITION_REGEX,(s,n,i)=>this.evaluateCondition(n.trim(),t)?i:""),e}processVariables(e,t){return e.replace(A.VARIABLE_REGEX,(s,n)=>{const i=this.evaluateExpression(n.trim(),t);return this.formatValue(i)})}evaluateCondition(e,t){try{const s=e.match(/hasFlag\s*\(\s*['"]([^'"]+)['"]\s*\)/);if(s)return t.computed.hasFlag(s[1]);if(/^[a-zA-Z_][a-zA-Z0-9_.]*$/.test(e)){const o=this.evaluateExpression(e,t);return this.isTruthy(o)}const n=e.match(/^([a-zA-Z_][a-zA-Z0-9_.]*)\s+(eq|ne|gt|lt|gte|lte)\s+(.+)$/);if(n){const[,o,g,l]=n,I=this.evaluateExpression(o,t),he=this.parseValue(l.trim());return this.compareValues(I,g,he)}const i=e.match(/playerChose\s*\(\s*['"]([^'"]+)['"]\s*\)/);if(i)return t.computed.playerChose(i[1]);const a=this.evaluateExpression(e,t);return this.isTruthy(a)}catch(s){return console.warn(`Failed to evaluate condition: ${e}`,s),!1}}evaluateExpression(e,t){const s=e.split(".");let n={...t.variables,...t};for(const i of s)if(n&&typeof n=="object")if(i in n)n=n[i];else{n=void 0;break}else{n=void 0;break}return n}parseValue(e){if(e.startsWith('"')&&e.endsWith('"')||e.startsWith("'")&&e.endsWith("'"))return e.slice(1,-1);if(/^-?\d+$/.test(e))return parseInt(e,10);if(/^-?\d*\.\d+$/.test(e))return parseFloat(e);if(e==="true")return!0;if(e==="false")return!1;if(e==="null")return null;if(e!=="undefined")return e}compareValues(e,t,s){switch(t){case"eq":return e==s;case"ne":return e!=s;case"gt":return Number(e)>Number(s);case"gte":return Number(e)>=Number(s);case"lt":return Number(e)<Number(s);case"lte":return Number(e)<=Number(s);default:return!1}}isTruthy(e){return e==null?!1:typeof e=="boolean"?e:typeof e=="number"?e!==0&&!isNaN(e):typeof e=="string"||Array.isArray(e)?e.length>0:typeof e=="object"?Object.keys(e).length>0:!!e}formatValue(e){return e==null?"":typeof e=="string"?e:typeof e=="number"||typeof e=="boolean"?e.toString():Array.isArray(e)?e.join(", "):typeof e=="object"?JSON.stringify(e):String(e)}getSupportedFeatures(){return{variables:!0,conditionals:!0,helpers:!1,loops:!1,partials:!1}}getEngineType(){return"simple"}};A.VARIABLE_REGEX=/\{\{([^}]+)\}\}/g,A.CONDITION_REGEX=/\{\{#if\s+([^}]+)\}\}(.*?)\{\{\/if\}\}/gs,A.CONDITION_ELSE_REGEX=/\{\{#if\s+([^}]+)\}\}(.*?)\{\{else\}\}(.*?)\{\{\/if\}\}/gs;let b=A;function $(r){return h.isArrayLike(r)}const m={first(r,e){if($(r))return typeof e=="number"?h.take(r,e):h.head(r)},last(r,e){if($(r))return typeof e=="number"?h.takeRight(r,e):h.last(r)},length(r){return h.size(r)},includes(r,e){return h.includes(r,e)},isEmpty(r){return h.isEmpty(r)},filter(r,e){return h.filter(r,e)},find(r,e){return h.find(r,e)},where(r,e){return h.filter(r,e)},map(r,e){return h.map(r,e)},pluck(r,e){return h.map(r,e)},join(r,e=","){return Array.isArray(r)?r.join(e):""},groupBy(r,e){return h.groupBy(r,e)},chunk(r,e){return h.chunk(r,Math.max(1,e))},unique(r){return h.uniq(r)},shuffle(r){return h.shuffle([...r])},slice(r,e,t){return h.slice(r,e,t)},take(r,e){return h.take(r,Math.max(0,e))},sample(r){return h.sample(r)},sampleSize(r,e){return h.sampleSize(r,Math.max(0,e))},flatten(r){return h.flatten(r)},reverse(r){return[...r].reverse()},concat(...r){return h.concat([],...r)},compact(r){return h.compact(r)},without(r,...e){return h.without(r,...e)},randomChoice(r){if(!(!Array.isArray(r)||r.length===0))return r[Math.floor(Math.random()*r.length)]},weightedChoice(r,e){if(!Array.isArray(r)||!Array.isArray(e)||r.length!==e.length)return;const t=e.reduce((n,i)=>n+Math.max(0,i),0);if(t===0)return;let s=Math.random()*t;for(let n=0;n<r.length;n++)if(s-=Math.max(0,e[n]),s<=0)return r[n];return r[r.length-1]},cycleNext(r,e){if(!Array.isArray(r)||r.length===0)return;const t=(e+1)%r.length;return r[t]},findByProperty(r,e,t){return h.find(r,{[e]:t})}};function G(r){r.registerHelper("first",(e,t)=>m.first(e,t)),r.registerHelper("last",(e,t)=>m.last(e,t)),r.registerHelper("length",e=>m.length(e)),r.registerHelper("size",e=>m.length(e)),r.registerHelper("includes",(e,t)=>m.includes(e,t)),r.registerHelper("isEmpty",e=>m.isEmpty(e)),r.registerHelper("where",(e,t)=>m.where(e,t)),r.registerHelper("pluck",(e,t)=>m.pluck(e,t)),r.registerHelper("join",(e,t)=>m.join(e,t)),r.registerHelper("groupBy",(e,t)=>m.groupBy(e,t)),r.registerHelper("chunk",(e,t)=>m.chunk(e,t)),r.registerHelper("unique",e=>m.unique(e)),r.registerHelper("shuffle",e=>m.shuffle(e)),r.registerHelper("slice",(e,t,s)=>m.slice(e,t,s)),r.registerHelper("take",(e,t)=>m.take(e,t)),r.registerHelper("sample",e=>m.sample(e)),r.registerHelper("flatten",e=>m.flatten(e)),r.registerHelper("reverse",e=>m.reverse(e)),r.registerHelper("compact",e=>m.compact(e)),r.registerHelper("without",(e,...t)=>{const s=t.slice(0,-1);return m.without(e,...s)}),r.registerHelper("randomChoice",e=>m.randomChoice(e)),r.registerHelper("weightedChoice",(e,t)=>m.weightedChoice(e,t)),r.registerHelper("cycleNext",(e,t)=>m.cycleNext(e,t)),r.registerHelper("findByProperty",(e,t,s)=>m.findByProperty(e,t,s)),r.registerHelper("array",(...e)=>(e.pop(),e)),r.registerHelper("range",(e,t,s)=>h.range(e,t,s)),r.registerHelper("times",function(e,t){let s="";for(let n=0;n<e;n++)s+=t.fn({index:n,first:n===0,last:n===e-1});return s})}function T(r){return r==null?!1:typeof r=="boolean"?r:typeof r=="number"?r!==0&&!isNaN(r):typeof r=="string"||Array.isArray(r)?r.length>0:typeof r=="object"?Object.keys(r).length>0:!!r}function C(r,e=0){const t=Number(r);return isNaN(t)?e:t}const p={eq(r,e){return r===e},ne(r,e){return r!==e},gt(r,e){return C(r)>C(e)},gte(r,e){return C(r)>=C(e)},lt(r,e){return C(r)<C(e)},lte(r,e){return C(r)<=C(e)},and(...r){return r.every(T)},or(...r){return r.some(T)},not(r){return!T(r)},contains(r,e){return Array.isArray(r)||typeof r=="string"?r.includes(e):r&&typeof r=="object"?e in r:!1},isEmpty(r){return r==null?!0:Array.isArray(r)||typeof r=="string"?r.length===0:typeof r=="object"?Object.keys(r).length===0:!1},isString(r){return typeof r=="string"},isNumber(r){return typeof r=="number"&&!isNaN(r)},isArray(r){return Array.isArray(r)},isObject(r){return r!==null&&typeof r=="object"&&!Array.isArray(r)},isBoolean(r){return typeof r=="boolean"},compare(r,e,t){switch(e){case"==":case"eq":return r==t;case"===":case"eqStrict":return r===t;case"!=":case"ne":return r!=t;case"!==":case"neStrict":return r!==t;case">":case"gt":return p.gt(r,t);case">=":case"gte":return p.gte(r,t);case"<":case"lt":return p.lt(r,t);case"<=":case"lte":return p.lte(r,t);case"contains":return p.contains(r,t);case"between":return p.between(r,C(t),C(arguments[3]));case"typeof":return typeof r===t;default:return!1}},between(r,e,t){const s=C(r),n=C(e),i=C(t);return s>=n&&s<=i},ifx(r,e,t){return T(r)?e:t},coalesce(...r){for(const e of r)if(e!=null)return e},defaultTo(r,e){return r!=null&&r!==""?r:e}};function H(r){return function(...e){const t=e[e.length-1],s=r(...e.slice(0,-1));return t.fn?s?t.fn(this):t.inverse(this):s}}function _(r){r.registerHelper("eq",H(p.eq)),r.registerHelper("ne",H(p.ne)),r.registerHelper("gt",H(p.gt)),r.registerHelper("gte",H(p.gte)),r.registerHelper("lt",H(p.lt)),r.registerHelper("lte",H(p.lte)),r.registerHelper("and",H(p.and)),r.registerHelper("or",H(p.or)),r.registerHelper("not",H(p.not)),r.registerHelper("contains",H(p.contains)),r.registerHelper("isEmpty",H(p.isEmpty)),r.registerHelper("isString",e=>p.isString(e)),r.registerHelper("isNumber",e=>p.isNumber(e)),r.registerHelper("isArray",e=>p.isArray(e)),r.registerHelper("isObject",e=>p.isObject(e)),r.registerHelper("isBoolean",e=>p.isBoolean(e)),r.registerHelper("compare",function(e,t,s,n){const i=p.compare(e,t,s);return n.fn?i?n.fn(this):n.inverse(this):i}),r.registerHelper("between",function(e,t,s,n){const i=p.between(e,t,s);return n.fn?i?n.fn(this):n.inverse(this):i}),r.registerHelper("ifx",p.ifx),r.registerHelper("ternary",p.ifx),r.registerHelper("coalesce",function(...e){return e.pop(),p.coalesce(...e)}),r.registerHelper("default",function(...e){return e.pop(),p.defaultTo(e[0],e[1])}),r.registerHelper("eqw",function(e,t,s){const n=e==t;return s.fn?n?s.fn(this):s.inverse(this):n}),r.registerHelper("neqw",function(e,t,s){const n=e!=t;return s.fn?n?s.fn(this):s.inverse(this):n})}function c(r,e=0){const t=Number(r);return isNaN(t)||!isFinite(t)?e:t}function k(r){return Array.isArray(r)?r.map(e=>c(e)).filter(e=>!isNaN(e)):[]}const f={add(r,e){return c(r)+c(e)},subtract(r,e){return c(r)-c(e)},multiply(r,e){return c(r)*c(e)},divide(r,e){const t=c(e);return t===0?0:c(r)/t},remainder(r,e){const t=c(e);return t===0?0:c(r)%t},abs(r){return Math.abs(c(r))},min(...r){const e=r.map(c).filter(t=>!isNaN(t));return e.length>0?Math.min(...e):0},max(...r){const e=r.map(c).filter(t=>!isNaN(t));return e.length>0?Math.max(...e):0},round(r,e=0){const t=c(r),s=Math.pow(10,c(e));return Math.round(t*s)/s},ceil(r){return Math.ceil(c(r))},floor(r){return Math.floor(c(r))},random(r,e){const t=c(r),s=c(e);return Math.random()*(s-t)+t},randomInt(r,e){const t=Math.ceil(c(r)),s=Math.floor(c(e));return Math.floor(Math.random()*(s-t+1))+t},clamp(r,e,t){const s=c(r),n=c(e),i=c(t);return Math.min(Math.max(s,n),i)},sum(r){return k(r).reduce((e,t)=>e+t,0)},average(r){const e=k(r);return e.length>0?e.reduce((t,s)=>t+s,0)/e.length:0},percentage(r,e){const t=c(e);return t===0?0:f.round(c(r)/t*100,2)},statCheck(r,e){const t=c(r),s=c(e),n=Math.random()*100;return t+n>=s},rollDice(r,e=1){const t=Math.max(1,Math.floor(c(r))),s=Math.max(1,Math.floor(c(e)));let n=0;for(let i=0;i<s;i++)n+=Math.floor(Math.random()*t)+1;return n},lerp(r,e,t){const s=c(r),n=c(e),i=f.clamp(c(t),0,1);return s+(n-s)*i},normalizeValue(r,e,t){const s=c(r),n=c(e),i=c(t);return i===n?0:f.clamp((s-n)/(i-n),0,1)},formatNumber(r,e=0){const t=c(r),s=Math.max(0,Math.floor(c(e)));return t.toFixed(s)}};function W(r){r.registerHelper("add",(e,t)=>f.add(e,t)),r.registerHelper("subtract",(e,t)=>f.subtract(e,t)),r.registerHelper("multiply",(e,t)=>f.multiply(e,t)),r.registerHelper("divide",(e,t)=>f.divide(e,t)),r.registerHelper("remainder",(e,t)=>f.remainder(e,t)),r.registerHelper("mod",(e,t)=>f.remainder(e,t)),r.registerHelper("abs",e=>f.abs(e)),r.registerHelper("min",(...e)=>{const t=e.slice(0,-1);return f.min(...t)}),r.registerHelper("max",(...e)=>{const t=e.slice(0,-1);return f.max(...t)}),r.registerHelper("round",(e,t)=>f.round(e,t)),r.registerHelper("ceil",e=>f.ceil(e)),r.registerHelper("floor",e=>f.floor(e)),r.registerHelper("random",(e,t)=>f.random(e,t)),r.registerHelper("randomInt",(e,t)=>f.randomInt(e,t)),r.registerHelper("clamp",(e,t,s)=>f.clamp(e,t,s)),r.registerHelper("sum",e=>f.sum(e)),r.registerHelper("average",e=>f.average(e)),r.registerHelper("percentage",(e,t)=>f.percentage(e,t)),r.registerHelper("statCheck",function(e,t,s){const n=f.statCheck(e,t);return s.fn?n?s.fn(this):s.inverse(this):n}),r.registerHelper("rollDice",(e,t)=>f.rollDice(e,t)),r.registerHelper("lerp",(e,t,s)=>f.lerp(e,t,s)),r.registerHelper("normalize",(e,t,s)=>f.normalizeValue(e,t,s)),r.registerHelper("formatNumber",(e,t)=>f.formatNumber(e,t)),r.registerHelper("inRange",function(e,t,s,n){const i=c(e),a=c(t),o=c(s),g=i>=a&&i<=o;return n.fn?g?n.fn(this):n.inverse(this):g}),r.registerHelper("isEven",function(e,t){const s=c(e)%2===0;return t.fn?s?t.fn(this):t.inverse(this):s}),r.registerHelper("isOdd",function(e,t){const s=c(e)%2!==0;return t.fn?s?t.fn(this):t.inverse(this):s})}function d(r){return typeof r=="string"?r:r==null?"":String(r)}const u={uppercase(r){return d(r).toUpperCase()},lowercase(r){return d(r).toLowerCase()},capitalize(r){const e=d(r);return e.charAt(0).toUpperCase()+e.slice(1).toLowerCase()},capitalizeFirst(r){const e=d(r);return e.charAt(0).toUpperCase()+e.slice(1)},titleCase(r){return d(r).replace(/\w\S*/g,e=>e.charAt(0).toUpperCase()+e.slice(1).toLowerCase())},trim(r){return d(r).trim()},truncate(r,e,t="..."){const s=d(r);return s.length<=e?s:s.slice(0,e-t.length)+t},ellipsis(r,e){return u.truncate(r,e,"â€Ļ")},replace(r,e,t){return d(r).split(e).join(t)},remove(r,e){return u.replace(r,e,"")},reverse(r){return d(r).split("").reverse().join("")},repeat(r,e){const t=Math.max(0,Math.floor(e));return d(r).repeat(t)},padStart(r,e,t=" "){return d(r).padStart(e,t)},padEnd(r,e,t=" "){return d(r).padEnd(e,t)},center(r,e){const t=d(r),s=Math.max(0,e-t.length),n=Math.floor(s/2),i=s-n;return" ".repeat(n)+t+" ".repeat(i)},startsWith(r,e){return d(r).startsWith(e)},endsWith(r,e){return d(r).endsWith(e)},includes(r,e){return d(r).includes(e)},substring(r,e,t){return d(r).substring(e,t)},words(r,e){const t=d(r).trim().split(/\s+/).filter(s=>s.length>0);return typeof e=="number"?t.slice(0,e):t},wordCount(r){return u.words(r).length},slugify(r){return d(r).toLowerCase().trim().replace(/[^\w\s-]/g,"").replace(/[\s_-]+/g,"-").replace(/^-+|-+$/g,"")},stripTags(r){return d(r).replace(/<[^>]*>/g,"")},typewriter(r,e=50){const t=d(r);return`<typewriter speed="${e}">${t}</typewriter>`},nameTag(r){const e=d(r).trim();return e?`<name>${e}</name>`:""},dialogueFormat(r,e){const t=d(r).trim(),s=d(e).trim();return t?`${u.nameTag(t)} ${s}`:s},parseMarkdown(r){return d(r).replace(/\*\*(.*?)\*\*/g,"<strong>$1</strong>").replace(/\*(.*?)\*/g,"<em>$1</em>").replace(/__(.*?)__/g,"<u>$1</u>").replace(/~~(.*?)~~/g,"<del>$1</del>")},sanitizeInput(r){return d(r).replace(/[<>\"'&]/g,e=>({"<":"&lt;",">":"&gt;",'"':"&quot;","'":"&#39;","&":"&amp;"})[e]||e).trim()},colorText(r,e){const t=d(r),s=d(e).trim();return s?`<color="${s}">${t}</color>`:t}};function q(r){r.registerHelper("uppercase",e=>u.uppercase(e)),r.registerHelper("lowercase",e=>u.lowercase(e)),r.registerHelper("capitalize",e=>u.capitalize(e)),r.registerHelper("capitalizeFirst",e=>u.capitalizeFirst(e)),r.registerHelper("titleCase",e=>u.titleCase(e)),r.registerHelper("trim",e=>u.trim(e)),r.registerHelper("truncate",(e,t,s)=>u.truncate(e,t,s)),r.registerHelper("ellipsis",(e,t)=>u.ellipsis(e,t)),r.registerHelper("replace",(e,t,s)=>u.replace(e,t,s)),r.registerHelper("remove",(e,t)=>u.remove(e,t)),r.registerHelper("reverse",e=>u.reverse(e)),r.registerHelper("repeat",(e,t)=>u.repeat(e,t)),r.registerHelper("padStart",(e,t,s)=>u.padStart(e,t,s)),r.registerHelper("padEnd",(e,t,s)=>u.padEnd(e,t,s)),r.registerHelper("center",(e,t)=>u.center(e,t)),r.registerHelper("startsWith",function(e,t,s){const n=u.startsWith(e,t);return s.fn?n?s.fn(this):s.inverse(this):n}),r.registerHelper("endsWith",function(e,t,s){const n=u.endsWith(e,t);return s.fn?n?s.fn(this):s.inverse(this):n}),r.registerHelper("includes",function(e,t,s){const n=u.includes(e,t);return s.fn?n?s.fn(this):s.inverse(this):n}),r.registerHelper("substring",(e,t,s)=>u.substring(e,t,s)),r.registerHelper("words",(e,t)=>u.words(e,t)),r.registerHelper("wordCount",e=>u.wordCount(e)),r.registerHelper("slugify",e=>u.slugify(e)),r.registerHelper("stripTags",e=>u.stripTags(e)),r.registerHelper("typewriter",(e,t)=>u.typewriter(e,t)),r.registerHelper("nameTag",e=>u.nameTag(e)),r.registerHelper("dialogueFormat",(e,t)=>u.dialogueFormat(e,t)),r.registerHelper("parseMarkdown",e=>u.parseMarkdown(e)),r.registerHelper("sanitizeInput",e=>u.sanitizeInput(e)),r.registerHelper("colorText",(e,t)=>u.colorText(e,t)),r.registerHelper("charAt",(e,t)=>d(e).charAt(Math.floor(t)))}let S=null;function J(r){S=r}function V(r,e){if(!(!r||!e||typeof e!="string"))return e.split(".").reduce((t,s)=>t!=null&&typeof t=="object"&&s in t?t[s]:void 0,r)}function x(r,e,t){if(!r||!e||typeof e!="string")return;const s=e.split(".");let n=r;for(let i=0;i<s.length-1;i++){const a=s[i];(n[a]==null||typeof n[a]!="object"||Array.isArray(n[a]))&&(n[a]={}),n=n[a]}n[s[s.length-1]]=t}function F(r,e=0){if(typeof r=="number"&&!isNaN(r)&&isFinite(r))return r;const t=Number(r);return isNaN(t)||!isFinite(t)?e:t}function K(r){if(r==null)return"";if(typeof r=="string")return r;try{return String(r)}catch{return""}}const w={hasFlag(r,e){return!r||typeof r!="string"?!1:S?S.hasStoryFlag(r):Array.isArray(e.storyFlags)&&e.storyFlags.includes(r)},addFlag(r,e){!r||typeof r!="string"||(S&&S.setStoryFlag(r),Array.isArray(e.storyFlags)||(e.storyFlags=[]),e.storyFlags.includes(r)||e.storyFlags.push(r))},removeFlag(r,e){if(!r||typeof r!="string"||(S&&S.clearStoryFlag(r),!Array.isArray(e.storyFlags)))return;const t=e.storyFlags.indexOf(r);t>-1&&e.storyFlags.splice(t,1)},toggleFlag(r,e){return!r||typeof r!="string"?!1:w.hasFlag(r,e)?(w.removeFlag(r,e),!1):(w.addFlag(r,e),!0)},getVar(r,e=void 0,t){if(!r||typeof r!="string")return e;if(S){const s=S.getVariable(r);if(s!==void 0)return s}if(t!=null&&t.variables){const s=V(t.variables,r);return s!==void 0?s:e}return e},setVar(r,e,t){!r||typeof r!="string"||!t||(S&&S.setVariable(r,e),(!t.variables||typeof t.variables!="object")&&(t.variables={}),x(t.variables,r,e))},hasVar(r,e){return!r||typeof r!="string"?!1:S?S.getVariable(r)!==void 0:e!=null&&e.variables?V(e.variables,r)!==void 0:!1},incrementVar(r,e,t){if(!r||typeof r!="string"||!t)return;const s=F(w.getVar(r,0,t)),n=F(e),i=s+n;w.setVar(r,i,t)},playerChose(r,e,t){return!r||typeof r!="string"?!1:S?S.playerChose(r,e):t!=null&&t.choiceHistory&&Array.isArray(t.choiceHistory)?t.choiceHistory.some(s=>{if(!s||typeof s!="object")return!1;const n=s.choiceText===r,i=!e||s.scene===e;return n&&i}):!1},getLastChoice(r){if(S){const e=S.getChoiceHistory();if(e.length>0)return e[e.length-1]}if(r!=null&&r.choiceHistory&&Array.isArray(r.choiceHistory)&&r.choiceHistory.length>0)return r.choiceHistory[r.choiceHistory.length-1]},choiceCount(r){return S?S.getChoiceHistory().length:r!=null&&r.choiceHistory&&Array.isArray(r.choiceHistory)?r.choiceHistory.length:0},formatTime(r){const e=Math.floor(F(r));if(e<0)return"0m";if(e<60)return`${e}m`;const t=Math.floor(e/60),s=e%60;return s>0?`${t}h ${s}m`:`${t}h`},randomBool(r=.5){const e=F(r,.5),t=Math.max(0,Math.min(1,e));return Math.random()<t},exportFlags(r){if(S){const e=S.getState();return Array.from(e.storyFlags)}return r!=null&&r.storyFlags&&Array.isArray(r.storyFlags)?r.storyFlags.filter(e=>typeof e=="string"&&e.length>0):[]},exportVariables(r){if(S){const e=S.getState();return Object.fromEntries(e.variables.entries())}if(r!=null&&r.variables&&typeof r.variables=="object")try{return JSON.parse(JSON.stringify(r.variables))}catch{const e={};for(const[t,s]of Object.entries(r.variables))try{JSON.stringify(s),e[t]=s}catch{e[t]="[Non-serializable value]"}return e}return{}}};function X(r){if(!r||typeof r.registerHelper!="function"){console.warn("Invalid Handlebars instance provided to registerVNHelpers");return}try{r.registerHelper("setVar",function(e,t,s){var n;try{const i=(n=s==null?void 0:s.data)==null?void 0:n.root;return i&&w.setVar(e,t,i),""}catch(i){return console.warn("Error in setVar helper:",i),""}}),r.registerHelper("getVar",function(e,t,s){var n;try{typeof t=="object"&&(t!=null&&t.fn)&&(s=t,t="");const i=(n=s==null?void 0:s.data)==null?void 0:n.root,a=w.getVar(e,t,i);if(typeof a=="string"||typeof a=="number"||typeof a=="boolean")return a;if(Array.isArray(a))return a.join(", ");if(typeof a=="object"&&a!==null)try{return JSON.stringify(a)}catch{return String(a)}return String(a)}catch(i){return console.warn("Error in getVar helper:",i),t||""}}),r.registerHelper("hasVar",function(e,t){var s;try{const n=(s=t==null?void 0:t.data)==null?void 0:s.root,i=w.hasVar(e,n);return t.fn?i?t.fn(n):t.inverse(n):i}catch(n){return console.warn("Error in hasVar helper:",n),!1}}),r.registerHelper("hasFlag",function(e,t){var s;try{const n=(s=t==null?void 0:t.data)==null?void 0:s.root,i=w.hasFlag(e,n);return t.fn?i?t.fn(n):t.inverse(n):i}catch(n){return console.warn("Error in hasFlag helper:",n),!1}}),r.registerHelper("addFlag",function(e,t){var s;try{const n=(s=t==null?void 0:t.data)==null?void 0:s.root;return n&&w.addFlag(e,n),""}catch(n){return console.warn("Error in addFlag helper:",n),""}}),r.registerHelper("removeFlag",function(e,t){var s;try{const n=(s=t==null?void 0:t.data)==null?void 0:s.root;return n&&w.removeFlag(e,n),""}catch(n){return console.warn("Error in removeFlag helper:",n),""}}),r.registerHelper("toggleFlag",function(e,t){var s;try{const n=(s=t==null?void 0:t.data)==null?void 0:s.root;if(!n)return!1;const i=w.toggleFlag(e,n);return t.fn?i?t.fn(n):t.inverse(n):i}catch(n){return console.warn("Error in toggleFlag helper:",n),!1}}),r.registerHelper("incrementVar",function(e,t,s){var n;try{const i=(n=s==null?void 0:s.data)==null?void 0:n.root;return i?w.incrementVar(e,t,i):0}catch(i){return console.warn("Error in incrementVar helper:",i),0}}),r.registerHelper("playerChose",function(...e){var t;try{const s=e[e.length-1],n=e[0],i=e.length>2?e[1]:void 0,a=(t=s==null?void 0:s.data)==null?void 0:t.root;if(!a)return!1;const o=w.playerChose(n,i,a);return s.fn?o?s.fn(a):s.inverse(a):o}catch(s){return console.warn("Error in playerChose helper:",s),!1}}),r.registerHelper("getLastChoice",function(e){var t;try{const s=(t=e==null?void 0:e.data)==null?void 0:t.root;return s?w.getLastChoice(s):void 0}catch(s){console.warn("Error in getLastChoice helper:",s);return}}),r.registerHelper("choiceCount",function(e){var t;try{const s=(t=e==null?void 0:e.data)==null?void 0:t.root;return s?w.choiceCount(s):0}catch(s){return console.warn("Error in choiceCount helper:",s),0}}),r.registerHelper("formatTime",e=>{try{return w.formatTime(e)}catch(t){return console.warn("Error in formatTime helper:",t),"0m"}}),r.registerHelper("randomBool",function(e,t){try{typeof e=="object"&&(e!=null&&e.fn)&&(t=e,e=.5);const s=w.randomBool(e);return t!=null&&t.fn?s?t.fn(this):t.inverse(this):s}catch(s){return console.warn("Error in randomBool helper:",s),!1}}),r.registerHelper("debug",function(e,t,s){try{t&&typeof t=="object"&&"fn"in t&&(s=t,t="DEBUG");const n=K(t||"DEBUG");if(console.log(`🐛 ${n}:`,e),e&&typeof e=="object")if(console.log(" Type:",Array.isArray(e)?"Array":"Object"),Array.isArray(e))console.log(" Length:",e.length);else try{console.log(" Keys:",Object.keys(e))}catch{console.log(" Keys: [Unable to enumerate]")}return""}catch(n){return console.warn("Error in debug helper:",n),""}}),r.registerHelper("timestamp",()=>{try{return Date.now()}catch(e){return console.warn("Error in timestamp helper:",e),0}}),r.registerHelper("currentDate",()=>{try{return new Date().toLocaleDateString()}catch(e){return console.warn("Error in currentDate helper:",e),""}}),r.registerHelper("currentTime",()=>{try{return new Date().toLocaleTimeString()}catch(e){return console.warn("Error in currentTime helper:",e),""}})}catch(e){console.error("Failed to register VN helpers:",e)}}const v={hasAsset(r,e){if(!Array.isArray(e))return!1;const t=v.normalizeKey(r);return e.some(s=>s?[s.id===r,s.id===t,s.name===r,s.path===r,v.normalizeKey(s.name||"")===t,v.normalizeKey(s.path||"")===t].some(Boolean):!1)},getAsset(r,e){if(!Array.isArray(e))return null;const t=v.normalizeKey(r);return e.find(s=>s?s.id===r||s.id===t||s.name===r||s.path===r||v.normalizeKey(s.name||"")===t||v.normalizeKey(s.path||"")===t:!1)||null},resolveAsset(r,e){const t=v.getAsset(r,e);return t&&(t.data||t.url||t.path||t.src)||null},getMediaType(r){var t;if(!r)return"unknown";const e=((t=r.split(".").pop())==null?void 0:t.toLowerCase())||"";return["jpg","jpeg","png","gif","webp","svg","bmp"].includes(e)?"image":["mp3","wav","ogg","m4a","aac","flac"].includes(e)?"audio":["mp4","webm","avi","mov","wmv","flv"].includes(e)?"video":"unknown"},getAssetInfo(r,e){const t=v.getAsset(r,e);return t?{type:v.getMediaType(t.name||t.path||""),size:t.size,name:t.name||t.path}:null},validateAsset(r,e){return v.resolveAsset(r,e)!==null},assetCount(r){return Array.isArray(r)?r.length:0},formatFileSize(r){if(typeof r!="number"||r===0)return"0 B";const e=1024,t=["B","KB","MB","GB"],s=Math.floor(Math.log(r)/Math.log(e));return parseFloat((r/Math.pow(e,s)).toFixed(1))+" "+t[s]},normalizeKey(r){return r?r.replace(/\.[^/.]+$/,"").toLowerCase().replace(/[^a-z0-9]/g,"_"):""}};function Z(r){r.registerHelper("hasAsset",function(e,t,s){const n=v.hasAsset(e,t);return s.fn?n?s.fn(this):s.inverse(this):n}),r.registerHelper("getAsset",v.getAsset),r.registerHelper("resolveAsset",v.resolveAsset),r.registerHelper("getAssetInfo",v.getAssetInfo),r.registerHelper("getMediaType",v.getMediaType),r.registerHelper("normalizeKey",v.normalizeKey),r.registerHelper("assetCount",v.assetCount),r.registerHelper("formatFileSize",v.formatFileSize),r.registerHelper("validateAsset",function(e,t,s){const n=v.validateAsset(e,t);return s.fn?n?s.fn(this):s.inverse(this):n}),r.registerHelper("showImage",function(e,t,s,n){const i=v.resolveAsset(e,t);if(!i)return"";const a=typeof s=="string"?s:e,o=typeof n=="string"?` class="${n}"`:"";return new r.SafeString(`<img src="${i}" alt="${a}"${o}>`)}),r.registerHelper("playAudio",function(e,t,s=!1,n=!1){const i=v.resolveAsset(e,t);if(!i)return"";const a=s?" autoplay":"",o=n?" loop":"";return new r.SafeString(`<audio src="${i}" controls${a}${o}></audio>`)}),r.registerHelper("playVideo",function(e,t,s=!1,n=!1,i){const a=v.resolveAsset(e,t);if(!a)return"";const o=s?" autoplay":"",g=n?" loop":"",l=typeof i=="string"?` class="${i}"`:"";return new r.SafeString(`<video src="${a}" controls${o}${g}${l}></video>`)})}const j={array:m,comparison:p,math:f,string:u,vn:w,asset:v};function M(r){if(!r||typeof r.registerHelper!="function"){console.warn("âš ī¸ Invalid Handlebars instance provided to registerAllHelpers");return}try{G(r),_(r),W(r),q(r),X(r),Z(r)}catch(e){throw console.error("❌ Error registering VN Engine helpers:",e),e}}function Q(r){if(r&&typeof r.registerHelper=="function")try{return M(r),{registered:!0,engine:"handlebars",message:"All helpers registered with Handlebars"}}catch(e){return{registered:!1,engine:"handlebars",message:`Failed to register helpers: ${e instanceof Error?e.message:"Unknown error"}`}}else return{registered:!1,engine:"simple",message:"Handlebars not available - using simple template engine without helpers"}}function Y(){return{handlebarsRequired:["first","last","length","includes","isEmpty","where","pluck","join","groupBy","chunk","unique","shuffle","slice","take","sample","flatten","reverse","compact","without","randomChoice","weightedChoice","array","range","times","eq","ne","gt","gte","lt","lte","and","or","not","contains","isEmpty","compare","between","ifx","ternary","coalesce","default","add","subtract","multiply","divide","mod","abs","min","max","round","ceil","floor","random","randomInt","clamp","sum","average","percentage","statCheck","rollDice","lerp","normalize","formatNumber","uppercase","lowercase","capitalize","titleCase","trim","truncate","replace","repeat","padStart","padEnd","center","substring","words","wordCount","slugify","typewriter","nameTag","dialogueFormat","parseMarkdown","colorText","hasFlag","getVar","setVar","hasVar","playerChose","formatTime","randomBool","debug","hasAsset","getAsset","resolveAsset","getAssetInfo","getMediaType","normalizeKey","assetCount","formatFileSize","validateAsset","showImage","playAudio","playVideo"],standalone:["All helper functions are available as JavaScript functions in the helpers object","Use helpers.array.first(), helpers.math.add(), helpers.string.capitalize(), helpers.asset.hasAsset(), etc."],description:"When Handlebars is not available, all helper functions can still be used directly from the helpers object in JavaScript code."}}class O{constructor(){this.handlebars=null,this.isHandlebarsAvailable=!1,this.helpersRegistered=!1,this.isTestMode=!1,this.gameStateManager=null,this.simpleEngine=new b,this.isTestMode=typeof process<"u"&&process.argv.some(e=>e.includes("test"))}setGameStateManager(e){this.gameStateManager=e,J(e)}async initialize(){await this.detectHandlebars(),this.isHandlebarsAvailable&&await this.setupHelpers()}async detectHandlebars(){try{if(typeof require<"u")try{const t=require("handlebars");if(this.handlebars=t.default||t,this.handlebars&&typeof this.handlebars.create=="function"){this.handlebars=this.handlebars.create(),this.isHandlebarsAvailable=!0,this.isTestMode||console.log("✅ Handlebars detected (sync) - Full template functionality available");return}}catch{}const e=await import("handlebars");if(this.handlebars=e.default||e,this.handlebars&&typeof this.handlebars.create=="function")this.handlebars=this.handlebars.create(),this.isHandlebarsAvailable=!0,this.isTestMode||console.log("✅ Handlebars detected (async) - Full template functionality available");else throw new Error("Invalid Handlebars module")}catch{this.isTestMode||console.log("â„šī¸ Handlebars not available - Using simple template engine"),this.isHandlebarsAvailable=!1}}async setupHelpers(){if(!(!this.isHandlebarsAvailable||this.helpersRegistered))try{if(M&&this.handlebars)M(this.handlebars),this.helpersRegistered=!0,this.isTestMode||console.log("✅ VN Engine helpers registered with Handlebars");else throw new Error("registerAllHelpers function not available")}catch(e){this.isTestMode||console.warn("Failed to register VN Engine helpers:",e)}}render(e,t){return this.isHandlebarsAvailable&&this.handlebars&&this.helpersRegistered?this.renderWithHandlebars(e,t):this.simpleEngine.render(e,t)}renderStrict(e,t){return this.isHandlebarsAvailable&&this.handlebars&&this.helpersRegistered?this.handlebars.compile(e,{strict:!0,noEscape:!1,compat:!0})(t)||"":this.simpleEngine.render(e,t)}renderWithHandlebars(e,t){try{return this.handlebars.compile(e,{strict:!1,noEscape:!1,compat:!0})(t)||""}catch(s){if(this.isTestMode||console.error("Handlebars template rendering error:",s),s.message.includes("Missing helper")){const n=s.message.match(/Missing helper: "([^"]+)"/);if(n)return`[Missing Helper: ${n[1]}]`}return`[Template Error: ${s.message}]`}}getEngineInfo(){return this.isHandlebarsAvailable?{type:"handlebars",isHandlebarsAvailable:!0,helpersRegistered:this.helpersRegistered,supportedFeatures:{variables:!0,conditionals:!0,helpers:this.helpersRegistered,loops:!0,partials:!0}}:{type:"simple",isHandlebarsAvailable:!1,helpersRegistered:!1,supportedFeatures:this.simpleEngine.getSupportedFeatures()}}registerHelper(e,t){return this.isHandlebarsAvailable&&this.handlebars?(this.handlebars.registerHelper(e,t),!0):(this.isTestMode||console.warn(`Cannot register helper "${e}" - Handlebars not available`),!1)}getHandlebarsInstance(){return this.isHandlebarsAvailable?this.handlebars:null}validateTemplate(e){const t=this.getEngineInfo();try{return this.isHandlebarsAvailable&&this.handlebars?(this.handlebars.compile(e),{valid:!0,engine:"handlebars",supportedFeatures:Object.keys(t.supportedFeatures).filter(s=>t.supportedFeatures[s])}):(this.simpleEngine.render(e,{storyFlags:[],variables:{},choiceHistory:[],computed:{gameTime:"00:00",hasFlag:()=>!1,getVar:()=>"",playerChose:()=>!1}}),{valid:!0,engine:"simple",supportedFeatures:Object.keys(t.supportedFeatures).filter(s=>t.supportedFeatures[s])})}catch(s){return{valid:!1,error:s.message,engine:this.isHandlebarsAvailable?"handlebars":"simple",supportedFeatures:[]}}}supportsFeature(e){return this.getEngineInfo().supportedFeatures[e]}isReady(){return!this.isHandlebarsAvailable||this.helpersRegistered}}class z{constructor(e,t){this.gameState=e,this.templateManager=t,this.scenes=new Map,this.currentScene="",this.currentInstructionIndex=0,this.pendingChoices=null}loadScenes(e){this.scenes.clear(),e.forEach(t=>{this.scenes.set(t.name,t)})}startScene(e,t=0){const s=this.scenes.get(e);return s?(this.currentScene=e,this.currentInstructionIndex=t,this.pendingChoices=null,this.gameState.setCurrentScene(e),this.gameState.setCurrentInstruction(t),t>=s.instructions.length?{type:"scene_complete"}:this.executeCurrentInstruction()):{type:"error",error:`Scene "${e}" not found`}}continue(){return this.pendingChoices?{type:"error",error:"Cannot continue while choices are pending. Make a choice first."}:(this.currentInstructionIndex++,this.gameState.setCurrentInstruction(this.currentInstructionIndex),this.executeCurrentInstruction())}makeChoice(e){if(!this.pendingChoices)return{type:"error",error:"No choices are currently available"};if(e==null||typeof e!="number"||isNaN(e))return{type:"error",error:"Choice index must be a valid number"};if(e<0||e>=this.pendingChoices.length)return{type:"error",error:`Invalid choice index: ${e}`};const t=this.pendingChoices[e],s={scene:this.currentScene,instruction:this.currentInstructionIndex,choiceIndex:e,choiceText:t.text,timestamp:Date.now()};if(this.gameState.addChoice(s),t.actions&&this.executeActions(t.actions),t.goto){this.pendingChoices=null;const n=this.renderTemplate(t.goto);return this.startScene(n)}return this.pendingChoices=null,this.currentInstructionIndex++,this.gameState.setCurrentInstruction(this.currentInstructionIndex),this.executeCurrentInstruction()}executeCurrentInstruction(){const e=this.scenes.get(this.currentScene);if(!e)return{type:"error",error:`Current scene "${this.currentScene}" not found`};if(this.currentInstructionIndex>=e.instructions.length)return{type:"scene_complete"};const t=e.instructions[this.currentInstructionIndex];return this.executeInstruction(t)}executeInstruction(e){try{switch(e.type){case"dialogue":return this.executeDialogue(e);case"action":return this.executeActionInstruction(e);case"conditional":return this.executeConditional(e);case"jump":return this.executeJump(e);default:return{type:"error",error:`Unknown instruction type: ${e.type}`}}}catch(t){return{type:"error",error:`Execution error: ${t.message}`}}}executeDialogue(e){if(e.actions&&this.executeActions(e.actions),e.choices){const t=this.filterAvailableChoices(e.choices);return t.length===0?(this.currentInstructionIndex++,this.gameState.setCurrentInstruction(this.currentInstructionIndex),this.executeCurrentInstruction()):(this.pendingChoices=t,{type:"show_choices",content:e.text?this.renderTemplate(e.text):void 0,speaker:e.speaker?this.renderTemplate(e.speaker):void 0,choices:t.map(s=>({...s,text:this.renderTemplate(s.text)}))})}return e.text?{type:"display_dialogue",content:this.renderTemplate(e.text),speaker:e.speaker?this.renderTemplate(e.speaker):void 0,canContinue:!0}:(this.currentInstructionIndex++,this.gameState.setCurrentInstruction(this.currentInstructionIndex),this.executeCurrentInstruction())}executeActionInstruction(e){return this.executeActions(e.actions),this.currentInstructionIndex++,this.gameState.setCurrentInstruction(this.currentInstructionIndex),this.executeCurrentInstruction()}executeConditional(e){const s=this.evaluateCondition(e.condition)?e.then:e.else;if(!s||s.length===0)return this.currentInstructionIndex++,this.gameState.setCurrentInstruction(this.currentInstructionIndex),this.executeCurrentInstruction();const n=this.executeInstruction(s[0]);if(s.length>1)for(let i=1;i<s.length;i++)this.executeInstruction(s[i]);return n.type==="display_dialogue"&&n.canContinue?n:(this.currentInstructionIndex++,this.gameState.setCurrentInstruction(this.currentInstructionIndex),this.executeCurrentInstruction())}executeJump(e){const t=this.renderTemplate(e.target);return this.startScene(t)}executeActions(e){e.forEach(t=>this.executeAction(t))}executeAction(e){try{switch(e.type){case"setVar":const t=this.renderTemplate(e.key.toString()),s=this.renderActionValue(e.value);this.gameState.setVariable(t,s);break;case"addVar":const n=this.renderTemplate(e.key.toString()),i=this.renderActionValue(e.value);this.gameState.addToVariable(n,i);break;case"setFlag":const a=this.renderTemplate(e.flag.toString());this.gameState.setStoryFlag(a);break;case"clearFlag":const o=this.renderTemplate(e.flag.toString());this.gameState.clearStoryFlag(o);break;case"addToList":const g=this.renderTemplate(e.list.toString()),l=this.renderActionValue(e.item);this.gameState.addToList(g,l);break;case"addTime":const I=this.renderActionValue(e.minutes);this.gameState.addTime(Number(I));break;case"helper":this.executeHelperAction(e);break;default:console.warn(`Unknown action type: ${e.type}`)}}catch(t){console.error(`Error executing action ${e.type}:`,t)}}renderActionValue(e){if(typeof e=="string"){if(e.includes("{{")&&e.includes("}}")){const t=this.renderTemplate(e);return/^\-?\d+(\.\d+)?$/.test(t)?Number(t):t==="true"?!0:t==="false"?!1:t}return e}return e}executeHelperAction(e){var a;const{helper:t}=e,s=((a=e.args)==null?void 0:a.map(o=>this.renderActionValue(o)))||[],n=e.result?this.renderTemplate(e.result.toString()):null,i=this.callHelper(t,s);n&&i!==void 0&&this.gameState.setVariable(n,i)}callHelper(e,t){const s=e.split(".");if(s.length!==2)throw new Error(`Invalid helper reference: ${e}`);const[n,i]=s,a=j[n];if(!a)throw new Error(`Helper category not found: ${n}`);const o=a[i];if(!o||typeof o!="function")throw new Error(`Helper method not found: ${e}`);return o(...t)}filterAvailableChoices(e){return e.filter(t=>t.condition?this.evaluateCondition(t.condition):!0)}evaluateCondition(e){try{return this.renderTemplate(e)==="true"}catch(t){return console.error("Condition evaluation error:",t),!1}}renderTemplate(e){try{const t=this.createTemplateContext();return this.templateManager.render(e,t)}catch(t){return console.error("Template rendering error:",t),e}}createTemplateContext(){const e=this.gameState.getState(),t=Object.fromEntries(e.variables.entries()),s=Array.from(e.storyFlags);return{...t,storyFlags:s,variables:t,choiceHistory:e.choiceHistory,gameTime:this.gameState.getCurrentTime(),computed:{hasFlag:n=>this.gameState.hasStoryFlag(n),getVar:(n,i="")=>{const a=this.getNestedProperty(t,n);return a!==void 0?a:i},playerChose:(n,i)=>this.gameState.playerChose(n,i)}}}getNestedProperty(e,t){if(!(!e||!t))return t.split(".").reduce((s,n)=>s==null?void 0:s[n],e)}}class N extends Error{constructor(e,t,s){super(e),this.code=t,this.context=s,this.name="VNEngineError"}}class ee extends N{constructor(e,t,s,n){super(e,"ACTION_VALIDATION_ERROR",{invalidAction:t,suggestion:s,sourceLocation:n}),this.invalidAction=t,this.suggestion=s,this.sourceLocation=n}}class E extends N{constructor(e,t){super(e,"SCRIPT_PARSE_ERROR",{sourceLocation:t}),this.sourceLocation=t}}class te extends N{constructor(e,t,s){super(e,"TEMPLATE_RENDER_ERROR",{template:t,originalError:s}),this.template=t,this.originalError=s}}class L{parse(e,t="script.yaml"){try{const s=P.load(e);if(!s||typeof s!="object")throw new E("Invalid YAML format",{file:t,line:1,scene:"root"});const n=[];for(const[i,a]of Object.entries(s)){const o=this.parseScene(i,a,t);n.push(o)}return n}catch(s){throw s instanceof E?s:new E(`Failed to parse script: ${s.message}`,{file:t,line:0,scene:"root"})}}parseScene(e,t,s){if(!Array.isArray(t))throw new E(`Scene "${e}" must be an array of instructions`,{file:s,scene:e,line:1});const n=[];return t.forEach((i,a)=>{const o={file:s,line:a+1,scene:e},g=this.parseInstruction(i,o);n.push(g)}),{name:e,instructions:n}}parseInstruction(e,t){if(typeof e=="string")return{type:"dialogue",text:e,_sourceLocation:t};if(typeof e=="object"&&e!==null){if(e.action||e.actions)return{type:"action",actions:Array.isArray(e.actions)?e.actions:[e.action],_sourceLocation:t};if(e.if)return{type:"conditional",condition:e.if,then:Array.isArray(e.then)?e.then.map(n=>this.parseInstruction(n,t)):[this.parseInstruction(e.then,t)],else:e.else?Array.isArray(e.else)?e.else.map(n=>this.parseInstruction(n,t)):[this.parseInstruction(e.else,t)]:void 0,_sourceLocation:t};if(e.goto||e.jump)return{type:"jump",target:e.goto||e.jump,_sourceLocation:t};if(e.say||e.text||e.speaker||e.choices||e.choice)return{type:"dialogue",speaker:e.speaker,text:e.say||e.text,actions:e.actions,choices:e.choices||e.choice,_sourceLocation:t}}throw new E("Invalid instruction format",t)}}class re{constructor(){this.listeners={}}on(e,t){return this.listeners[e]||(this.listeners[e]=[]),this.listeners[e].push(t),()=>{const s=this.listeners[e];if(s){const n=s.indexOf(t);n>-1&&s.splice(n,1)}}}emit(e,t){const s=this.listeners[e];s&&s.forEach(n=>{try{n(t)}catch(i){console.error(`Error in ${String(e)} listener:`,i)}})}removeAllListeners(){this.listeners={}}}class se{constructor(e){this.gameState=e}validateSceneConflicts(e,t,s){const n=new Set(e.map(l=>l.name)),i=t.map(l=>l.name),a=new Set(s.allowOverwrite||[]),o=i.filter(l=>n.has(l)),g=s.mode==="replace"?o.filter(l=>!a.has(l)):o;return{hasConflicts:o.length>0,conflicts:o,unauthorizedOverwrites:g}}validateSceneReferences(e){const t=new Set(e.map(a=>a.name)),s=[],n=[],i=[];for(const a of e){const o=this.validateSceneInstructions(a.instructions,t);(o.invalidJumps.length>0||o.invalidChoices.length>0)&&(i.push(a.name),s.push(...o.invalidJumps),n.push(...o.invalidChoices))}return{valid:s.length===0&&n.length===0,invalidJumpTargets:[...new Set(s)],invalidChoiceTargets:[...new Set(n)],affectedScenes:[...new Set(i)]}}validateCurrentState(e){if(!this.gameState)return{valid:!0,issues:[]};const t=[],s=new Set(e.map(i=>i.name)),n=this.gameState.getCurrentScene();if(n&&!s.has(n)&&t.push(`Current scene '${n}' will no longer exist after upgrade`),n&&s.has(n)){const i=e.find(o=>o.name===n),a=this.gameState.getCurrentInstruction();i&&a>=i.instructions.length&&t.push(`Current instruction index ${a} exceeds new scene length ${i.instructions.length}`)}return{valid:t.length===0,issues:t}}createUpgradeError(e,t,s){var o,g,l;const n=[],i={};if(e.unauthorizedOverwrites.length>0&&(n.push(`Cannot overwrite scenes: ${e.unauthorizedOverwrites.join(", ")}`),i.unauthorizedOverwrites=e.unauthorizedOverwrites),t.valid||(t.invalidJumpTargets.length>0&&(n.push(`Invalid jump targets: ${t.invalidJumpTargets.join(", ")}`),i.invalidReferences=t.invalidJumpTargets),t.invalidChoiceTargets.length>0&&(n.push(`Invalid choice targets: ${t.invalidChoiceTargets.join(", ")}`),i.invalidReferences=[...i.invalidReferences||[],...t.invalidChoiceTargets])),s.valid||(n.push(`Current game state would become invalid: ${s.issues.join(", ")}`),i.affectedState=s.issues),n.length===0)return null;let a="SCENE_CONFLICT";return(o=i.unauthorizedOverwrites)!=null&&o.length?a="UNAUTHORIZED_OVERWRITE":(g=i.invalidReferences)!=null&&g.length?a="INVALID_REFERENCE":(l=i.affectedState)!=null&&l.length&&(a="STATE_INVALID"),{code:a,message:n.join("; "),details:i}}validateSceneInstructions(e,t){const s=[],n=[];for(const i of e)switch(i.type){case"jump":const a=i;t.has(a.target)||s.push(a.target);break;case"dialogue":const o=i;if(o.choices)for(const l of o.choices)l.goto&&!t.has(l.goto)&&n.push(l.goto);break;case"conditional":const g=i;if(g.then){const l=this.validateSceneInstructions(g.then,t);s.push(...l.invalidJumps),n.push(...l.invalidChoices)}if(g.else){const l=this.validateSceneInstructions(g.else,t);s.push(...l.invalidJumps),n.push(...l.invalidChoices)}break}return{invalidJumps:s,invalidChoices:n}}generateWarnings(e,t,s){const n=[];if(s.namespace){const a=t.map(l=>`${s.namespace}_${l.name}`),o=new Set(e.map(l=>l.name)),g=a.filter(l=>o.has(l));g.length>0&&n.push(`Namespaced scenes still conflict with existing scenes: ${g.join(", ")}`)}t.length>50&&n.push(`Adding ${t.length} scenes - this is a large upgrade`);const i=e.length+t.length;return i>200&&n.push(`Total scenes after upgrade: ${i} - consider performance impact`),n}}class ne{constructor(e,t){this.gameState=e,this.scriptEngine=t}backup(e){return{scenes:this.deepCloneScenes(e),currentScene:this.gameState.getCurrentScene(),currentInstruction:this.gameState.getCurrentInstruction(),gameState:this.gameState.serialize(),timestamp:Date.now()}}restore(e){try{this.scriptEngine.loadScenes(e.scenes),this.gameState.deserialize(e.gameState),this.gameState.setCurrentScene(e.currentScene),this.gameState.setCurrentInstruction(e.currentInstruction)}catch(t){throw console.error("Failed to restore state from backup:",t),new Error(`State restoration failed: ${t instanceof Error?t.message:"Unknown error"}`)}}validateCurrentPosition(e){const t=this.gameState.getCurrentScene(),s=this.gameState.getCurrentInstruction();if(!t)return{valid:!0};const n=e.find(i=>i.name===t);return n?s>=n.instructions.length?{valid:!1,reason:`Current instruction index ${s} exceeds scene length ${n.instructions.length}`}:{valid:!0}:{valid:!1,reason:`Current scene '${t}' no longer exists`}}restoreExecutionState(e){const t=this.validateCurrentPosition(e);if(!t.valid)return{success:!1,message:t.reason};try{return this.scriptEngine.loadScenes(e),{success:!0}}catch(s){return{success:!1,message:`Failed to load new scenes: ${s instanceof Error?s.message:"Unknown error"}`}}}validateBackup(e){const t=[];(!e.scenes||!Array.isArray(e.scenes))&&t.push("Backup scenes are missing or invalid"),e.gameState||t.push("Backup game state is missing"),typeof e.currentScene!="string"&&t.push("Backup current scene is invalid"),typeof e.currentInstruction!="number"&&t.push("Backup current instruction is invalid"),(!e.timestamp||typeof e.timestamp!="number")&&t.push("Backup timestamp is missing or invalid");const s=Date.now()-60*60*1e3;return e.timestamp<s&&t.push("Backup is older than 1 hour and may be stale"),{valid:t.length===0,issues:t}}getCurrentContext(){const e=this.gameState.getState();return{scene:e.currentScene,instruction:e.currentInstruction,hasState:e.variables.size>0||e.storyFlags.size>0,stateSize:e.variables.size+e.storyFlags.size+e.choiceHistory.length}}deepCloneScenes(e){try{return JSON.parse(JSON.stringify(e))}catch(t){return console.error("Failed to clone scenes, using shallow copy:",t),e.map(s=>({...s}))}}createQuickBackup(){return{currentScene:this.gameState.getCurrentScene(),currentInstruction:this.gameState.getCurrentInstruction(),timestamp:Date.now()}}restoreQuickBackup(e){this.gameState.setCurrentScene(e.currentScene),this.gameState.setCurrentInstruction(e.currentInstruction)}}class ie{constructor(e,t,s){this.gameState=e,this.scriptEngine=t,this.scriptParser=s,this.validator=new se(e),this.statePreserver=new ne(e,t)}upgrade(e,t,s={}){const n={mode:"additive",namespace:"",allowOverwrite:[],validateS