Skip to content

RuntimeError 错误码参考

ADVMaker 定义了一个 RuntimeError 类,继承自 Error,用于在游戏运行时报告逻辑错误。所有运行时错误通过 Game.error(new RuntimeError(code, reason)) 统一抛出,并在控制台输出 [RuntimeError #xxx] 格式的日志。

错误码一览

错误码含义触发位置
101物品数量不能为负数state.tsobtainItem()
102找不到场景 / 对话 / 属性game.tsenter(), speak(), toNext()state.tsqryStatus()
103骰子错误dice.tsroll()
104存在重复 IDgame.tsdefineConfig()api.tscheckHasSameId()
105合成配方材料列表为空api.tsdefineRecipe(), removeRecipe()
106给 number 类型状态赋值 stringapi.tsAdv.status setter
107给 string 类型状态赋值 numberapi.tsAdv.status setter

各错误码详解

101 · 物品数量不能为负数

触发条件:调用 obtainItem() 时,当前持有数量 + 变化量 < 0。

typescript
// 假设当前拥有 3 个苹果
Adv.bag.apple = -5; // ❌ 3 + (-5) = -2 < 0 → RuntimeError(101)

常见原因

  • 剧情脚本中写错了扣除数量
  • 某个条件分支错误地重复扣除了物品

102 · 找不到场景 / 对话 / 属性

触发条件:引用了未定义的场景 ID、对话 ID 或状态 ID。

这是一个使用频率最高的错误,有 4 个触发点:

触发点示例
enter(sceneId)场景 ID 不存在于已注册的场景中
speak(dialogId)对话 ID 不存在于已注册的对话中
toNext()next 指向的场景或对话 ID 无法解析
qryStatus(id)查询的状态 ID 从未通过 defineConfig 注册
typescript
Adv.goto('secret_room'); // 如果 'secret_room' 未定义 → RuntimeError(102)

常见原因

  • 拼写错误:'bedroom' 写成了 'bedRoom'
  • 忘记注册场景/对话:在 game.config.ts 中漏写了某个 status
  • next 指向了一个已被删除的 ID

103 · 骰子错误

触发条件:传递给 dice() 的表达式格式不正确。

typescript
dice('2d6+abc'); // ❌ 无效的骰子表达式 → RuntimeError(103)
dice('hello'); // ❌ 完全无法解析 → 抛出普通 Error

注意:骰子数量和面数由正则 \d+ 保证为正整数,传入 'd20''3d6+2' 等合法格式不会触发此错误。此错误仅在正则匹配成功但后续解析异常时触发。


104 · 重复 ID

触发条件:注册的场景、对话或状态 ID 与已有的冲突。

typescript
Adv.appendScene('attic', { name: '阁楼' });
Adv.appendScene('attic', { name: '另一个阁楼' }); // ❌ ID 重复 → RuntimeError(104)

状态同理——在 game.config.ts 中定义了两个同 ID 的 status 也会触发:

typescript
Adv.defineConfig({
    status: {
        hp: {
            content: {
                hp: { name: '生命值', value: 100 }, // ❌ 重复了 'hp'
            },
        },
    },
});

常见原因

  • 复制粘贴后忘记改 ID
  • 多人协作时 ID 命名冲突

105 · 合成配方为空

触发条件:注册或操作合成配方时,need 列表为空。

typescript
Adv.defineRecipe('potion', {
    name: '治疗药水',
    need: [], // ❌ 空配方 → RuntimeError(105)
});

removeRecipe() 移除最后一个材料后也会触发:

typescript
const rc = Adv.recipeControl('potion');
rc.removeRecipe('herb'); // 如果只剩这一个材料
rc.removeRecipe('water'); // ❌ need 为空 → RuntimeError(105)

106 · Number 状态被赋值字符串

触发条件:通过 Adv.status 给数字类型状态赋值了字符串。

typescript
Adv.defineConfig({
    status: {
        hp: { content: { hp: { name: '生命值', value: 100 } } }, // number 类型
    },
});

Adv.status.hp = '满血'; // ❌ number 型被赋 string → RuntimeError(106)

这是数字专属的类型守卫。如果你想把一个状态改成字符串类型,请在 defineConfig 中将 value 设为字符串。


107 · String 状态被赋值数字

触发条件:通过 Adv.status 给字符串类型状态赋值了数字。

typescript
Adv.defineConfig({
    status: {
        reputation: { content: { reputation: { name: '声望', value: '默默无闻' } } }, // string 类型
    },
});

Adv.status.reputation = 100; // ❌ string 型被赋 number → RuntimeError(107)

调试技巧

  1. 打开控制台:所有 RuntimeError 在抛出前会通过 console.error 输出,即使被 try-catch 捕获也能看到日志。
  2. 搜索错误码:在源码中搜索 RuntimeError(X,(X 为子码)可快速定位触发点。
  3. 利用浏览器断点:在 Game.error() 处打断点,可以在错误抛出前检查调用栈。

与普通 Error 的区别

项目中还存在一些普通 throw 语句(如 throw new Error('无效的骰子表达式')),它们不是 RuntimeError 体系的成员:

类型用途示例
RuntimeError游戏逻辑层面的可预期错误找不到场景、状态类型错误
原生 Error / throw编程错误或未分类异常骰子正则不匹配、栈为空、内部断言