vn-engine
Version:
A powerful, flexible TypeScript library for creating visual novels and interactive narratives
1,517 lines âĸ 92.1 kB
JavaScript
import d from "lodash";
import * as $ from "js-yaml";
class k {
constructor(e) {
this.state = {
currentScene: "",
currentInstruction: 0,
variables: /* @__PURE__ */ new Map(),
storyFlags: /* @__PURE__ */ 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: (/* @__PURE__ */ 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: /* @__PURE__ */ new Map(),
storyFlags: /* @__PURE__ */ 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: /* @__PURE__ */ new Map(),
storyFlags: /* @__PURE__ */ 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 H = class H {
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(H.CONDITION_ELSE_REGEX, (s, n, i, a) => this.evaluateCondition(n.trim(), t) ? i : a), e = e.replace(H.CONDITION_REGEX, (s, n, i) => this.evaluateCondition(n.trim(), t) ? i : ""), e;
}
processVariables(e, t) {
return e.replace(H.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, c] = n, A = this.evaluateExpression(o, t), x = this.parseValue(c.trim());
return this.compareValues(A, g, x);
}
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";
}
};
H.VARIABLE_REGEX = /\{\{([^}]+)\}\}/g, H.CONDITION_REGEX = /\{\{#if\s+([^}]+)\}\}(.*?)\{\{\/if\}\}/gs, H.CONDITION_ELSE_REGEX = /\{\{#if\s+([^}]+)\}\}(.*?)\{\{else\}\}(.*?)\{\{\/if\}\}/gs;
let F = H;
function N(r) {
return d.isArrayLike(r);
}
const m = {
first(r, e) {
if (N(r))
return typeof e == "number" ? d.take(r, e) : d.head(r);
},
last(r, e) {
if (N(r))
return typeof e == "number" ? d.takeRight(r, e) : d.last(r);
},
length(r) {
return d.size(r);
},
includes(r, e) {
return d.includes(r, e);
},
isEmpty(r) {
return d.isEmpty(r);
},
filter(r, e) {
return d.filter(r, e);
},
find(r, e) {
return d.find(r, e);
},
where(r, e) {
return d.filter(r, e);
},
map(r, e) {
return d.map(r, e);
},
pluck(r, e) {
return d.map(r, e);
},
join(r, e = ",") {
return Array.isArray(r) ? r.join(e) : "";
},
groupBy(r, e) {
return d.groupBy(r, e);
},
chunk(r, e) {
return d.chunk(r, Math.max(1, e));
},
unique(r) {
return d.uniq(r);
},
shuffle(r) {
return d.shuffle([...r]);
},
slice(r, e, t) {
return d.slice(r, e, t);
},
take(r, e) {
return d.take(r, Math.max(0, e));
},
sample(r) {
return d.sample(r);
},
sampleSize(r, e) {
return d.sampleSize(r, Math.max(0, e));
},
flatten(r) {
return d.flatten(r);
},
reverse(r) {
return [...r].reverse();
},
concat(...r) {
return d.concat([], ...r);
},
compact(r) {
return d.compact(r);
},
without(r, ...e) {
return d.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 d.find(r, { [e]: t });
}
};
function z(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) => d.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 I(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 w(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 w(r) > w(e);
},
gte(r, e) {
return w(r) >= w(e);
},
lt(r, e) {
return w(r) < w(e);
},
lte(r, e) {
return w(r) <= w(e);
},
and(...r) {
return r.every(I);
},
or(...r) {
return r.some(I);
},
not(r) {
return !I(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, w(t), w(arguments[3]));
case "typeof":
return typeof r === t;
default:
return !1;
}
},
between(r, e, t) {
const s = w(r), n = w(e), i = w(t);
return s >= n && s <= i;
},
ifx(r, e, t) {
return I(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 C(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 O(r) {
r.registerHelper("eq", C(p.eq)), r.registerHelper("ne", C(p.ne)), r.registerHelper("gt", C(p.gt)), r.registerHelper("gte", C(p.gte)), r.registerHelper("lt", C(p.lt)), r.registerHelper("lte", C(p.lte)), r.registerHelper("and", C(p.and)), r.registerHelper("or", C(p.or)), r.registerHelper("not", C(p.not)), r.registerHelper("contains", C(p.contains)), r.registerHelper("isEmpty", C(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 l(r, e = 0) {
const t = Number(r);
return isNaN(t) || !isFinite(t) ? e : t;
}
function V(r) {
return Array.isArray(r) ? r.map((e) => l(e)).filter((e) => !isNaN(e)) : [];
}
const f = {
add(r, e) {
return l(r) + l(e);
},
subtract(r, e) {
return l(r) - l(e);
},
multiply(r, e) {
return l(r) * l(e);
},
divide(r, e) {
const t = l(e);
return t === 0 ? 0 : l(r) / t;
},
remainder(r, e) {
const t = l(e);
return t === 0 ? 0 : l(r) % t;
},
abs(r) {
return Math.abs(l(r));
},
min(...r) {
const e = r.map(l).filter((t) => !isNaN(t));
return e.length > 0 ? Math.min(...e) : 0;
},
max(...r) {
const e = r.map(l).filter((t) => !isNaN(t));
return e.length > 0 ? Math.max(...e) : 0;
},
round(r, e = 0) {
const t = l(r), s = Math.pow(10, l(e));
return Math.round(t * s) / s;
},
ceil(r) {
return Math.ceil(l(r));
},
floor(r) {
return Math.floor(l(r));
},
random(r, e) {
const t = l(r), s = l(e);
return Math.random() * (s - t) + t;
},
randomInt(r, e) {
const t = Math.ceil(l(r)), s = Math.floor(l(e));
return Math.floor(Math.random() * (s - t + 1)) + t;
},
clamp(r, e, t) {
const s = l(r), n = l(e), i = l(t);
return Math.min(Math.max(s, n), i);
},
sum(r) {
return V(r).reduce((e, t) => e + t, 0);
},
average(r) {
const e = V(r);
return e.length > 0 ? e.reduce((t, s) => t + s, 0) / e.length : 0;
},
percentage(r, e) {
const t = l(e);
return t === 0 ? 0 : f.round(l(r) / t * 100, 2);
},
statCheck(r, e) {
const t = l(r), s = l(e), n = Math.random() * 100;
return t + n >= s;
},
rollDice(r, e = 1) {
const t = Math.max(1, Math.floor(l(r))), s = Math.max(1, Math.floor(l(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 = l(r), n = l(e), i = f.clamp(l(t), 0, 1);
return s + (n - s) * i;
},
normalizeValue(r, e, t) {
const s = l(r), n = l(e), i = l(t);
return i === n ? 0 : f.clamp((s - n) / (i - n), 0, 1);
},
formatNumber(r, e = 0) {
const t = l(r), s = Math.max(0, Math.floor(l(e)));
return t.toFixed(s);
}
};
function j(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 = l(e), a = l(t), o = l(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 = l(e) % 2 === 0;
return t.fn ? s ? t.fn(this) : t.inverse(this) : s;
}), r.registerHelper("isOdd", function(e, t) {
const s = l(e) % 2 !== 0;
return t.fn ? s ? t.fn(this) : t.inverse(this) : s;
});
}
function h(r) {
return typeof r == "string" ? r : r == null ? "" : String(r);
}
const u = {
uppercase(r) {
return h(r).toUpperCase();
},
lowercase(r) {
return h(r).toLowerCase();
},
capitalize(r) {
const e = h(r);
return e.charAt(0).toUpperCase() + e.slice(1).toLowerCase();
},
capitalizeFirst(r) {
const e = h(r);
return e.charAt(0).toUpperCase() + e.slice(1);
},
titleCase(r) {
return h(r).replace(
/\w\S*/g,
(e) => e.charAt(0).toUpperCase() + e.slice(1).toLowerCase()
);
},
trim(r) {
return h(r).trim();
},
truncate(r, e, t = "...") {
const s = h(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 h(r).split(e).join(t);
},
remove(r, e) {
return u.replace(r, e, "");
},
reverse(r) {
return h(r).split("").reverse().join("");
},
repeat(r, e) {
const t = Math.max(0, Math.floor(e));
return h(r).repeat(t);
},
padStart(r, e, t = " ") {
return h(r).padStart(e, t);
},
padEnd(r, e, t = " ") {
return h(r).padEnd(e, t);
},
center(r, e) {
const t = h(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 h(r).startsWith(e);
},
endsWith(r, e) {
return h(r).endsWith(e);
},
includes(r, e) {
return h(r).includes(e);
},
substring(r, e, t) {
return h(r).substring(e, t);
},
words(r, e) {
const t = h(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 h(r).toLowerCase().trim().replace(/[^\w\s-]/g, "").replace(/[\s_-]+/g, "-").replace(/^-+|-+$/g, "");
},
stripTags(r) {
return h(r).replace(/<[^>]*>/g, "");
},
typewriter(r, e = 50) {
const t = h(r);
return `<typewriter speed="${e}">${t}</typewriter>`;
},
nameTag(r) {
const e = h(r).trim();
return e ? `<name>${e}</name>` : "";
},
dialogueFormat(r, e) {
const t = h(r).trim(), s = h(e).trim();
return t ? `${u.nameTag(t)}
${s}` : s;
},
parseMarkdown(r) {
return h(r).replace(/\*\*(.*?)\*\*/g, "<strong>$1</strong>").replace(/\*(.*?)\*/g, "<em>$1</em>").replace(/__(.*?)__/g, "<u>$1</u>").replace(/~~(.*?)~~/g, "<del>$1</del>");
},
sanitizeInput(r) {
return h(r).replace(/[<>\"'&]/g, (e) => ({
"<": "<",
">": ">",
'"': """,
"'": "'",
"&": "&"
})[e] || e).trim();
},
colorText(r, e) {
const t = h(r), s = h(e).trim();
return s ? `<color="${s}">${t}</color>` : t;
}
};
function L(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) => h(e).charAt(Math.floor(t)));
}
let y = null;
function U(r) {
y = r;
}
function R(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 _(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 b(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 B(r) {
if (r == null) return "";
if (typeof r == "string") return r;
try {
return String(r);
} catch {
return "";
}
}
const v = {
hasFlag(r, e) {
return !r || typeof r != "string" ? !1 : y ? y.hasStoryFlag(r) : Array.isArray(e.storyFlags) && e.storyFlags.includes(r);
},
addFlag(r, e) {
!r || typeof r != "string" || (y && y.setStoryFlag(r), Array.isArray(e.storyFlags) || (e.storyFlags = []), e.storyFlags.includes(r) || e.storyFlags.push(r));
},
removeFlag(r, e) {
if (!r || typeof r != "string" || (y && y.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 : v.hasFlag(r, e) ? (v.removeFlag(r, e), !1) : (v.addFlag(r, e), !0);
},
getVar(r, e = void 0, t) {
if (!r || typeof r != "string")
return e;
if (y) {
const s = y.getVariable(r);
if (s !== void 0)
return s;
}
if (t != null && t.variables) {
const s = R(t.variables, r);
return s !== void 0 ? s : e;
}
return e;
},
setVar(r, e, t) {
!r || typeof r != "string" || !t || (y && y.setVariable(r, e), (!t.variables || typeof t.variables != "object") && (t.variables = {}), _(t.variables, r, e));
},
hasVar(r, e) {
return !r || typeof r != "string" ? !1 : y ? y.getVariable(r) !== void 0 : e != null && e.variables ? R(e.variables, r) !== void 0 : !1;
},
incrementVar(r, e, t) {
if (!r || typeof r != "string" || !t)
return;
const s = b(v.getVar(r, 0, t)), n = b(e), i = s + n;
v.setVar(r, i, t);
},
playerChose(r, e, t) {
return !r || typeof r != "string" ? !1 : y ? y.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 (y) {
const e = y.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 y ? y.getChoiceHistory().length : r != null && r.choiceHistory && Array.isArray(r.choiceHistory) ? r.choiceHistory.length : 0;
},
formatTime(r) {
const e = Math.floor(b(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 = 0.5) {
const e = b(r, 0.5), t = Math.max(0, Math.min(1, e));
return Math.random() < t;
},
exportFlags(r) {
if (y) {
const e = y.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 (y) {
const e = y.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 D(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 && v.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 = v.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 = v.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 = v.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 && v.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 && v.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 = v.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 ? v.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 = v.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 ? v.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 ? v.choiceCount(s) : 0;
} catch (s) {
return console.warn("Error in choiceCount helper:", s), 0;
}
}), r.registerHelper("formatTime", (e) => {
try {
return v.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 = 0.5);
const s = v.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 = B(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 (/* @__PURE__ */ new Date()).toLocaleDateString();
} catch (e) {
return console.warn("Error in currentDate helper:", e), "";
}
}), r.registerHelper("currentTime", () => {
try {
return (/* @__PURE__ */ new Date()).toLocaleTimeString();
} catch (e) {
return console.warn("Error in currentTime helper:", e), "";
}
});
} catch (e) {
console.error("Failed to register VN helpers:", e);
}
}
const S = {
hasAsset(r, e) {
if (!Array.isArray(e)) return !1;
const t = S.normalizeKey(r);
return e.some((s) => s ? [
s.id === r,
s.id === t,
s.name === r,
s.path === r,
S.normalizeKey(s.name || "") === t,
S.normalizeKey(s.path || "") === t
].some(Boolean) : !1);
},
getAsset(r, e) {
if (!Array.isArray(e)) return null;
const t = S.normalizeKey(r);
return e.find((s) => s ? s.id === r || s.id === t || s.name === r || s.path === r || S.normalizeKey(s.name || "") === t || S.normalizeKey(s.path || "") === t : !1) || null;
},
resolveAsset(r, e) {
const t = S.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 = S.getAsset(r, e);
return t ? {
type: S.getMediaType(t.name || t.path || ""),
size: t.size,
name: t.name || t.path
} : null;
},
validateAsset(r, e) {
return S.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 P(r) {
r.registerHelper("hasAsset", function(e, t, s) {
const n = S.hasAsset(e, t);
return s.fn ? n ? s.fn(this) : s.inverse(this) : n;
}), r.registerHelper("getAsset", S.getAsset), r.registerHelper("resolveAsset", S.resolveAsset), r.registerHelper("getAssetInfo", S.getAssetInfo), r.registerHelper("getMediaType", S.getMediaType), r.registerHelper("normalizeKey", S.normalizeKey), r.registerHelper("assetCount", S.assetCount), r.registerHelper("formatFileSize", S.formatFileSize), r.registerHelper("validateAsset", function(e, t, s) {
const n = S.validateAsset(e, t);
return s.fn ? n ? s.fn(this) : s.inverse(this) : n;
}), r.registerHelper("showImage", function(e, t, s, n) {
const i = S.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 = S.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 = S.resolveAsset(e, t);
if (!a) return "";
const o = s ? " autoplay" : "", g = n ? " loop" : "", c = typeof i == "string" ? ` class="${i}"` : "";
return new r.SafeString(`<video src="${a}" controls${o}${g}${c}></video>`);
});
}
const G = {
array: m,
comparison: p,
math: f,
string: u,
vn: v,
asset: S
};
function M(r) {
if (!r || typeof r.registerHelper != "function") {
console.warn("â ī¸ Invalid Handlebars instance provided to registerAllHelpers");
return;
}
try {
z(r), O(r), j(r), L(r), D(r), P(r);
} catch (e) {
throw console.error("â Error registering VN Engine helpers:", e), e;
}
}
function ae(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 oe() {
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 W {
constructor() {
this.handlebars = null, this.isHandlebarsAvailable = !1, this.helpersRegistered = !1, this.isTestMode = !1, this.gameStateManager = null, this.simpleEngine = new F(), this.isTestMode = typeof process < "u" && process.argv.some((e) => e.includes("test"));
}
setGameStateManager(e) {
this.gameStateManager = e, U(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 J {
constructor(e, t) {
this.gameState = e, this.templateManager = t, this.scenes = /* @__PURE__ */ 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.currentInstruct