diff --git a/content/yarn-spinner.md b/content/yarn-spinner.md index 5dfef30..dbc158c 100644 --- a/content/yarn-spinner.md +++ b/content/yarn-spinner.md @@ -21,7 +21,8 @@ title: 节点名称 ``` 对于`随机`遭遇而言,使用多个`title`相同的节点。 -可以使用`when: `来为节点触发添加条件。 +使用`when: `来为节点触发添加条件。 +如果没有条件,则使用`when: always`。 若需要节点只触发一次,可以结合`变量`。 ```yarn-spinner @@ -43,30 +44,29 @@ when: $villager_encountered == false 随机遭遇可以使用`<>`来返回主线。 -## 休息与睡觉 - -在合适的地方可以休息。如果时间在晚0点到早6点之间,可以睡觉。 +在玩家可以反复尝试的遭遇中,在结尾添加`<>```,跳转到自身的开头。 ## 命令和函数 使用命令读写时间。不要创建变量。 -- ```<>```:时间流逝,若休息则使用`<>`。若在夜间休息则休息至第二天6点,否则休息1小时。 -- ```<>```:读取24小时制当前时间。午夜为0点。 -- ```<= 72>>```:总共流逝的时间。 +- `<>``<>``<>`:触发时间流逝。 +- `$time_day``$time_hour``$time_minute`:读取24小时制当前时间。 +- `$hour_total`:总共流逝的时间。 使用以下命令操作角色属性: -- ```<>```:造成角色属性损伤。若可耐受,则使用```<>```。 +- ```<>```:造成角色属性损伤。 - ```<>```:解除属性创伤,并恢复角色属性损伤。 -- ```<>```:施加属性修改值,持续一定小时数。 - ```<> ```:检定角色属性。成功返回正值代表成功进度,失败返回负值代表失败进度。 - ```<= 3>>```:处理检定成功进度。 - ```<>```:处理检定失败进度。 -- ```< 0>>```:如果既没有成功也没有失败,暗示玩家应该继续尝试。 +- ```< 0>>```:如果既没有达成成功进度也没有达成失败进度,暗示玩家应该继续尝试。 +- 检定后总是在文本中描述玩家当前的进度(但不要描述难度和机会) -使用以下命令操作角色物品: -- ```<>```:添加物品。 -- ```<>```:消耗物品。 -- ```<>```:检查物品。 +直接使用变量来记录玩家物品。没有背包限制。 +- ```<>```:添加物品。 +- ```<= 100)>>```:检查物品。 + +直接使用变量来记录势力计划、地点、事件的进度。 ## 文件分割 diff --git a/src/components/stores/ttrpgRunner.ts b/src/components/stores/ttrpgRunner.ts new file mode 100644 index 0000000..dc4a523 --- /dev/null +++ b/src/components/stores/ttrpgRunner.ts @@ -0,0 +1,108 @@ +type IRunner = { + getVariable(key: string): unknown; + setVariable(key: string, value: unknown): void; + setSmartVariable(name: string, expression: string): void; +} +export function getTtrpgFunctions(runner: IRunner){ + /* time related */ + function ensure_time(){ + if(!runner.getVariable("time_day")){ + runner.setVariable("time_day", 1); + runner.setVariable("time_hour", 0); + runner.setVariable("time_minute", 0); + runner.setSmartVariable("hour_total", "$time_day * 24 + $time_hour"); + } + } + // midnight - 6am is night time + function is_night(){ + ensure_time(); + const hour = runner.getVariable("time_hour") as number; + return hour < 6; + } + function add_minute(delta: number){ + ensure_time(); + const min = (runner.getVariable("time_minute") as number) + delta; + runner.setVariable("time_minute", min % 60); + if(min >= 60) add_hour(Math.floor(min / 60)); + } + function add_hour(delta: number){ + ensure_time(); + const hour = (runner.getVariable("time_hour") as number) + delta; + runner.setVariable("time_hour", hour % 24); + if(hour >= 24) add_day(Math.floor(hour / 24)); + } + function add_day(delta: number){ + ensure_time(); + const day = (runner.getVariable("time_day") as number) + delta; + runner.setVariable("time_day", day); + } + /* stat related */ + function ensure_stat(stat: string){ + if(!runner.getVariable(stat)){ + const statBase = `${stat}_base`; + const statMod = `${stat}_mod`; + const statDamage = `${stat}_damage`; + const statWound = `${stat}_wound`; + runner.setVariable(statBase, 10); + runner.setVariable(statMod, 0); + runner.setVariable(statDamage, 0); + runner.setVariable(statWound, false); + runner.setSmartVariable(stat, `$${statBase} - $${statDamage} + $${statMod}`); + } + } + function get_stat(stat: string){ + ensure_stat(stat); + return runner.getVariable(stat) as number || 0; + } + function damage(stat: string, amount: number){ + const current = get_stat(stat); + if(amount * 2 >= current) + runner.setVariable(`${stat}_wound`, true); + const damage = get_stat(`${stat}_damage`); + runner.setVariable(`${stat}_damage`, damage + amount); + } + function heal(stat: string, amount = 0){ + ensure_stat(stat); + runner.setVariable(`${stat}_wound`, false); + const damage = get_stat(`${stat}_damage`); + runner.setVariable(`${stat}_damage`, Math.max(0, damage - amount)); + } + function check(id: string, stat: string): number { + const statVal = get_stat(stat); + 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; + } + function rollStat(){ + const index = Math.floor(Math.random() * 6); + return ['str', 'dex', 'con', 'int', 'wis', 'cha'][index]; + } + function fatigue(){ + damage(rollStat(), 1); + } + function regen(){ + let stat = rollStat(); + if(runner.getVariable(`${stat}_wound`) || get_stat(`${stat}_damage`) <= 0) { + stat = rollStat(); + } + if(runner.getVariable(`${stat}_wound`) || get_stat(`${stat}_damage`) <= 0) { + return; + } + heal(stat, 1); + } + return { + add_minutes: add_minute, + add_hours: add_hour, + add_days: add_day, + + damage, + heal, + check, + + fatigue, + regen, + }; +} \ No newline at end of file diff --git a/src/yarn-spinner/runtime/runner.ts b/src/yarn-spinner/runtime/runner.ts index a60237c..627731c 100644 --- a/src/yarn-spinner/runtime/runner.ts +++ b/src/yarn-spinner/runtime/runner.ts @@ -609,5 +609,9 @@ export class YarnRunner { this.variables[name] = value; this.evaluator.setVariable(name, value); } + + setSmartVariable(name: string, expression: string): void { + this.evaluator.setSmartVariable(name, expression); + } }