diff --git a/src/components/md-yarn-spinner.tsx b/src/components/md-yarn-spinner.tsx index 001e3b0..bba73a7 100644 --- a/src/components/md-yarn-spinner.tsx +++ b/src/components/md-yarn-spinner.tsx @@ -2,9 +2,8 @@ import { customElement, noShadowDOM } from 'solid-element'; import { For, Show, createEffect } from 'solid-js'; import type {TextResult, RuntimeResult, OptionsResult} from '../yarn-spinner/runtime/results'; import { createYarnStore } from './stores/yarnStore'; -import {RunnerOptions} from "../yarn-spinner/runtime/runner"; -customElement('md-yarn-spinner', {startAt: 'start'}, (props, { element }) => { +customElement<{start: string}>('md-yarn-spinner', {start: 'start'}, (props, { element }) => { noShadowDOM(); let historyContainer: HTMLDivElement | undefined; diff --git a/src/components/stores/ttrpgRunner.ts b/src/components/stores/ttrpgRunner.ts index dc4a523..d59b1ab 100644 --- a/src/components/stores/ttrpgRunner.ts +++ b/src/components/stores/ttrpgRunner.ts @@ -38,7 +38,7 @@ export function getTtrpgFunctions(runner: IRunner){ } /* stat related */ function ensure_stat(stat: string){ - if(!runner.getVariable(stat)){ + if(runner.getVariable(stat) === undefined){ const statBase = `${stat}_base`; const statMod = `${stat}_mod`; const statDamage = `${stat}_damage`; @@ -74,6 +74,7 @@ export function getTtrpgFunctions(runner: IRunner){ const progress = runner.getVariable(key) as number || 0; const newProgress = progress + Math.ceil(Math.random() * 4); runner.setVariable(key, newProgress); + console.log(`check ${stat}(${statVal}) ${pass ? 'pass' : 'fail'}, now ${newProgress}`); return pass ? newProgress : -newProgress; } function rollStat(){ @@ -94,15 +95,17 @@ export function getTtrpgFunctions(runner: IRunner){ heal(stat, 1); } return { - add_minutes: add_minute, - add_hours: add_hour, - add_days: add_day, - - damage, - heal, - check, - - fatigue, - regen, - }; + commands: { + add_minute, + add_hour, + add_day, + damage, + heal, + fatigue, + regen + } as Recordany>, + functions: { + check + } as Recordany> + } } \ No newline at end of file diff --git a/src/components/stores/yarnStore.ts b/src/components/stores/yarnStore.ts index 5e27acb..4c0c2fd 100644 --- a/src/components/stores/yarnStore.ts +++ b/src/components/stores/yarnStore.ts @@ -4,6 +4,7 @@ import {compile, parseYarn, YarnRunner} from "../../yarn-spinner"; import {loadElementSrc, resolvePath} from "../utils/path"; import {createStore} from "solid-js/store"; import {RunnerOptions} from "../../yarn-spinner/runtime/runner"; +import {getTtrpgFunctions} from "./ttrpgRunner"; type YarnSpinnerStore = { dialogueHistory: (RuntimeResult | OptionsResult['options'][0])[], @@ -12,7 +13,7 @@ type YarnSpinnerStore = { runnerInstance: YarnRunner | null, } -export function createYarnStore(element: HTMLElement, props: RunnerOptions){ +export function createYarnStore(element: HTMLElement, props: {start: string}){ const [store, setStore] = createStore({ dialogueHistory: [], currentOptions: null, @@ -39,19 +40,11 @@ export function createYarnStore(element: HTMLElement, props: RunnerOptions){ const program = compile(ast); const runner = new YarnRunner(program, { - functions: { - check(id: string, stat: string): number { - const statVal = runner.getVariable(stat) as number || 10; - const pass = Math.ceil(Math.random() * 20) <= statVal; - const key = `${id}_${pass ? 'pass' : 'fail'}`; - const progress = runner.getVariable(key) as number || 0; - const newProgress = progress + Math.ceil(Math.random() * 4); - runner.setVariable(key, newProgress); - return pass ? newProgress : -newProgress; - } - }, - startAt: props.startAt, + startAt: props.start, }); + const {commands, functions} = getTtrpgFunctions(runner); + runner.registerFunctions(functions); + runner.registerCommands(commands); return runner; } catch (error) { console.error('Failed to initialize YarnRunner:', error); diff --git a/src/yarn-spinner/runtime/evaluator.ts b/src/yarn-spinner/runtime/evaluator.ts index 1e48b66..7c65128 100644 --- a/src/yarn-spinner/runtime/evaluator.ts +++ b/src/yarn-spinner/runtime/evaluator.ts @@ -499,4 +499,11 @@ export class ExpressionEvaluator { getVariable(name: string): unknown { return this.variables[name]; } + + registerFunctions(functions: Record unknown>){ + this.functions = { + ...this.functions, + ...functions + } + } } diff --git a/src/yarn-spinner/runtime/runner.ts b/src/yarn-spinner/runtime/runner.ts index 627731c..b54f8fb 100644 --- a/src/yarn-spinner/runtime/runner.ts +++ b/src/yarn-spinner/runtime/runner.ts @@ -28,7 +28,6 @@ type CompiledOption = { export class YarnRunner { private readonly program: IRProgram; private readonly variables: Record; - private readonly functions: Record unknown>; private readonly handleCommand?: (command: string, parsed?: ReturnType) => void; private readonly commandHandler: CommandHandler; private readonly evaluator: ExpressionEvaluator; @@ -59,7 +58,7 @@ export class YarnRunner { this.variables[normalizedKey] = value; } } - this.functions = { + let functions = { // Default conversion helpers string: (v: unknown) => String(v ?? ""), number: (v: unknown) => Number(v), @@ -115,12 +114,25 @@ export class YarnRunner { } as Record unknown>; this.handleCommand = opts.handleCommand; this.onStoryEnd = opts.onStoryEnd; - this.evaluator = new ExpressionEvaluator(this.variables, this.functions, this.program.enums); + this.evaluator = new ExpressionEvaluator(this.variables, functions, this.program.enums); this.commandHandler = opts.commandHandler ?? new CommandHandler(this.variables); this.nodeTitle = opts.startAt; this.step(); } + + public registerFunctions(functions: Record unknown>){ + this.evaluator.registerFunctions(functions); + } + + public registerCommands(commands: Record void | Promise>) { + for(const key in commands){ + this.commandHandler.register(key, (args, evaluator) => { + if(!evaluator) return; + commands[key].call(this, args.map(arg => evaluator.evaluateExpression(arg))); + }); + } + }; /** * Resolve a node title to an actual node (handling node groups). @@ -599,6 +611,8 @@ export class YarnRunner { * Get variable value. */ getVariable(name: string): unknown { + if(this.evaluator.isSmartVariable(name)) + return this.evaluator.evaluateExpression(`$${name}`); return this.variables[name]; }