Files
spellforge/docs/handbook/02_content_authoring.md
T
joywayerandClaude Opus 4.8 ad2f4c0bc6 docs: 整理文档目录并对齐代码现状
- 将开发过程/归档文档迁至 docs_dev/(development_plan、certification_checklist、
  已废弃的 Cocos 架构草案 archived_cocos_architecture_draft),并修正全部跨引用
- 新增根 README.md(项目介绍,暂定名 Spellforge)与 docs_dev/README.md 索引
- 新增 docs_dev/doc_code_audit_2026-07-20.md:文档 vs 代码交叉审计报告(经 6
  路对抗性复核,零证伪),含「代码更优 / 文档更优 / 中性」判定汇总
- 在 docs/ 各设计·技术·机制文档就地加「实现现状 (2026-07-20)」callout:
  追认代码更优实现(纯 JSON 数据驱动、SpatialGrid-only 碰撞、MultiMesh 单档、
  存档选最新槽等),订正陈旧/矛盾内容(.tres→JSON、Boss HP/阈值/波次、EventID、
  StatusManager.apply 签名等),标记未实现功能(C# 热路径、Mana、元进展、
  Boss 阶段/抗性、Tutorial、轨迹/连锁/催化等)与 latent bug(CoreFeatureTag 位运算、
  pierce 空操作、MAX_OPS 不读 cpu_limit)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-20 14:35:55 +08:00

9.0 KiB
Raw Blame History

02 内容创作指南 (Content Authoring)

策划和程序共同维护的内容文件。所有数值修改无需改架构代码,只改指定位置。

⚠️ 实现现状 (2026-07-20 审计):本文多个「改硬编码」的步骤已过期作废——项目内容早已全面 JSON 数据驱动,法术/波次/敌人/共鸣/Core 均从 data/*.json 加载,相关硬编码工厂函数与常量表已从代码中删除。当前唯一权威的内容创作工作流是 07 游戏设计器(编辑器内插件面板,全部经 data/*.json + 编辑器,零代码)。本文中标注 OBSOLETE 的小节仅保留作历史参考,请勿据其修改代码。详见审计报告 ../../docs_dev/doc_code_audit_2026-07-20.md。

策划/美术优先用可视化工具07 游戏设计器 提供编辑器内一站式面板(法术/敌人/波次/Core/平衡),表单填数值即可,无需写代码。本文的代码方式已大部分作废(见上方现状说明)。


A. 添加/修改法术

⚠️ 实现现状 (2026-07-20 审计):法术定义唯一来源是 res://data/spells.json,由 SpellRegistry 启动时加载。wand_preset.gd 已不再定义任何法术(文件头注:「法术已迁至 SpellRegistry」),下文 make_frost_bolt / make_spell_by_id / get_all_spell_ids 等工厂函数在代码中已不存在,请勿添加。详见审计报告 ../../docs_dev/doc_code_audit_2026-07-20.md。

💡 策划优先用可视化工具:在 07 游戏设计器 的「法术」页签(或独立的 06 法术编辑器)用表单新增/编辑法术,保存到 data/spells.json无需写代码

法术定义位置

  • 唯一来源res://data/spells.json(由游戏设计器/法术编辑器维护,启动时由 SpellRegistry 加载)。
  • 手工编辑时,每条法术是一个对象,键与旧工厂函数字段对应:id / type / display_nametr() 键)/ description / element_tags(如 ["tag:ice"]/ metabase_damage / speed / lifetime / radius / damage_type / apply_status_id / shop_cost 等)。推荐用编辑器面板生成,避免手写 JSON 出错。

步骤:添加一个新 ACTION 法术(当前正确流程)

  1. 打开游戏设计器「法术」页签,新增一条法术,填写上述字段(data/spells.json 会自动写入)。
  2. translations/*.po 四个文件中添加对应翻译键(见本地化指南)。
  3. 无需改动任何 .gd 代码;SpellRegistry 启动即加载,商店会自动抽取新法术。

OBSOLETE(历史参考,代码已删除):早期版本需在 wand_preset.gd 添加 make_frost_bolt() 工厂函数并注册到 make_spell_by_id / get_all_spell_ids。这些函数已随 SpellRegistry 迁移删除,请勿参照。


支持的法术类型

SpellNode.SpellType 说明 meta 必填键
ACTION 发射弹道 base_damage, speed, lifetime, radius
ACTION(zone) 地面效果 action_kind:"zone", radius, status_id, duration, tick_interval
ACTION(summon) 召唤物 action_kind:"summon", lifetime, range, fire_interval, base_damage
MODIFIER 修改施法参数 至少一个:damage_add, damage_mult, spread_add, speed_mult, multicast
TRIGGER 命中触发子弹 base_damage, speed, lifetime, radius(触发物自身弹道参数)
LOGIC 条件/循环 logic_op(见下表)

LOGIC meta 参数

logic_op 额外 meta 效果
"every_n_shots" n:int, reg:0-3 每 N 次施法触发
"if_hp_below" threshold:float(0-1) HP < 阈值时触发
"if_enemy_nearby" range:float 范围内有敌时触发
"loop" count:int(1-16) 后续法术重复 N 次

B. 修改波次配置

波次配置位置

scripts/autoloads/wave_manager.gdWAVE_CONFIG: Array(数组索引 0 = Wave 1

配置字段说明

{"count": 20, "hp": 15.0, "type": 0, "elite": 0, "gold": 10, "xp": 20, "boss": -1}
#  count  : 本波生成的杂兵数量
#  hp     : 杂兵基础血量(受难度乘子影响)
#  type   : 敌人原型(见下方原型表)
#  elite  : W15+ 带 NavigationAgent2D 寻路的精英数量
#  gold   : 波次结算奖励金币
#  xp     : 波次结算奖励 XP
#  boss   : -1=无;4=Mini Boss5=Final Boss

敌人原型(EnemyManager.Type

名称 速度 护甲 颜色 大小
0 BASIC 80 0 14px
1 FAST 140 0 11px
2 ARMORED 60 5 16px
3 ELITE 90 3 22px
4 MINIBOSS 55 8 品红 48px
5 BOSS 45 14 深红 76px

示例:修改 W8 Mini Boss 血量

# wave_manager.gd line ~50
{"count": 12, "hp": 30.0, "type": 0, "elite": 0, "gold": 80, "xp": 120, "boss": 4},
# ↑ boss:4 → _BOSS_HP[4] = 450.0 控制 Boss 血量

# 修改 Boss 血量,找到下面这行:
const _BOSS_HP: Dictionary = {4: 450.0, 5: 1800.0}
# 改成你想要的数值

C. 修改敌人原型数值

scripts/autoloads/enemy_manager.gd 顶部常量:

const _SPEED: Dictionary = {0: 80.0, 1: 140.0, 2: 60.0, 3: 90.0, 4: 55.0, 5: 45.0}
const _ARMOR: Dictionary = {0: 0.0,  1: 0.0,   2: 5.0,  3: 3.0,  4: 8.0,  5: 14.0}

直接修改对应值即可,无需改其他代码。


D. 修改难度乘子

scripts/autoloads/settings_manager.gd

func enemy_hp_mult()        -> float: return [0.7, 1.0, 1.0][difficulty]
func player_dmg_taken_mult()-> float: return [0.7, 1.0, 1.0][difficulty]
func wave_count_mult()      -> float: return [1.0, 1.0, 1.2][difficulty]
func boss_hp_mult()         -> float: return [1.0, 1.0, 1.3][difficulty]
# 数组格式:[初学者, 标准, 挑战]

E. 添加共鸣配方

scripts/domain/spell_system/spell_evaluator.gd_ready()_resonance_recipes

_resonance_recipes = [
    {
        "id":             "plasma_storm",          # 配方 ID
        "pattern":        ["tag:water", "tag:lightning"],  # 元素标签组合
        "match":          "adjacent",              # "adjacent"=相邻;"anywhere_in_deck"=任意位置
        "result_spell_id":"action_plasma_storm",   # 产物法术 id(须在 WandPreset 中注册)
        "consume_inputs": true,                    # true=消耗原始法术插槽
    },
    # 新增配方直接追加到这里
    {
        "id":             "ice_nova",
        "pattern":        ["tag:ice", "tag:water"],
        "match":          "adjacent",
        "result_spell_id":"action_ice_nova",
        "consume_inputs": false,                   # false=原法术保留,额外注入产物
    },
]

注意:产物法术必须同时在 WandPreset.make_spell_by_id() 中注册,才能被 SpellRegistry 识别。


F. 添加/修改状态效果

现有状态 IDscripts/autoloads/status_id.gd

const BURN = 1    # 燃烧 DoT(火系)
const FREEZE = 2  # 冻结(硬控)
const POISON = 3  # 中毒(可叠层 DoT
const WET = 4     # 潮湿
const OILY = 5    # 油腻
const STUN = 6    # 麻痹
const COMBO_MARK = 7
const VULNERABILITY = 8
# 新增从 9 开始递增

状态数值定义(.tres 资源,目前占位代码生成)

当前 StatusRegistry_ready() 扫描 res://resources/status_types/ 目录。若不存在资源文件,会尝试从 WandPreset 或内置回退。添加新状态的完整流程:

  1. status_id.gd 追加常量(从 9 起)
  2. StatusTypeDef 对象中配置 tick_interval / stack_mode / max_stacks
  3. 在状态数值生效的 can_catalyze 字段填写对应状态 ID 列表
  4. 在四个 .po 文件中添加 STATUS_新状态_NAME 等翻译键

G. 添加 Core(法杖类型)

# wand_preset.gd 中添加:
func make_staff_long() -> CoreDefinition:
    var c := CoreDefinition.new()
    c.id            = "staff_long"
    c.display_name  = "CORE_STAFF_LONG_NAME"   # tr() 键
    c.slot_count    = 10                        # 插槽数
    c.topology      = CoreDefinition.Topology.LINEAR
    c.cpu_limit     = 10
    c.cast_interval = 0.7
    return c

然后在 CombatManager._CORE_ROSTER 中追加 "staff_long",以及在 _make_core_by_id()_default_deck_for() 中添加对应分支。


H. 调试技巧

在运行中检查任意系统状态

利用 MCP 游戏脚本执行器(或 Godot 调试器表达式):

# 查看当前法术 VM 编译结果
var ids = []
for nd in SpellEvaluator._equipped_compiled.nodes: ids.append(nd.id)
print(ids)

# 立即杀死所有敌人(测试波次结算)
for i in range(EnemyManager.get_active_count()-1, -1, -1):
    var eid = EnemyManager._slot_entity_map.get(i, -1)
    if eid >= 0: EnemyManager.apply_damage(eid, 99999.0, -1, false)

# 给玩家大量金币
PlayerStats.gain_gold(9999)