Files
spellforge/docs/design/core_wand_design.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

14 KiB
Raw Blame History

构建核心 (Core) 设计文档

0. 概念定位

Core(构建核心) 是玩家持有的"武器主体"。它定义了:

  1. 插槽拓扑 (Slot Topology):有多少个插槽、如何排列(线性/矩阵/电路板)。
  2. 基础属性 (Base Stats):法杖自带的充能速度、容量、基础蓝耗。
  3. 特殊规则 (Core Rules):部分高阶 Core 拥有改变执行规则的特性(如持久内存、双流并行)。

Core 本身不执行法术,它是 SpellDeck 的"容器",决定了 SpellEvaluator 的执行环境。


1. 数据结构 (GDScript)

# core_definition.gd
# 存储于 res://resources/cores/ 目录下的 .tres Resource 文件

class_name CoreDefinition
extends Resource

## 核心唯一 ID(如 "wand_basic", "staff_matrix"
@export var id: String

## 显示名称
@export var name: String

## 稀有度 (0=普通, 1=稀有, 2=史诗, 3=传说)
@export var rarity: int = 0

## 插槽拓扑类型
@export_enum("LINEAR", "MATRIX_2X4", "CIRCUIT") var topology: int = 0

## 插槽总数量
@export var slot_count: int = 5

## 矩阵布局尺寸(仅 MATRIX / CIRCUIT 类型有效)
@export var grid_cols: int = 4
@export var grid_rows: int = 2

## 特殊插槽配置(每个插槽的约束信息)
## Array[Dictionary]: { "index": int, "allowed_types": Array[int], "tags": Array[String] }
@export var slot_configs: Array = []

## CIRCUIT 拓扑的有向边列表(LINEAR/MATRIX 留空即可)
## Array[Dictionary]: { "from": int, "to": int }
## _flatten_circuit() 从 core.edges 读取边表;禁止在 slot_configs 内嵌 "edges" key。
@export var edges: Array = []

## 法杖基础属性
@export var base_mana_capacity: float = 50.0    # 法杖蓝上限
@export var base_mana_regen: float = 5.0         # 每秒回蓝
@export var base_cast_delay: float = 0.25        # 基础施法后摇(乘算基准)
@export var base_recharge_time: float = 0.5      # 完整一轮打完后的充能时间

## Core 特性标签,驱动特殊执行规则
## ⚠️ 实现现状(2026-07-20):代码实为 `@export var feature_tags: int = 0`int 位掩码,非 Array[String]);
##    CoreFeatureTag 常量为 int 1..5 并用 `&` 判定——值非 2 的幂有位冲突 bug(见审计 D 节)。
@export var feature_tags: Array[String] = []

## 图标路径
@export var icon: Texture2D

2. 插槽拓扑类型

2.1 LINEAR(线性)

[ 0 ] -> [ 1 ] -> [ 2 ] -> [ 3 ] -> [ 4 ]
  • 最基础的类型,执行流从左到右。
  • 新手友好,等同于标准 Noita 法杖。
  • 代表 Core: wand_basic5槽)、staff_long10槽)

2.2 MATRIX(矩阵)

行 A: [ 0 ] [ 1 ] [ 2 ] [ 3 ]
行 B: [ 4 ] [ 5 ] [ 6 ] [ 7 ]
  • 执行流默认沿行 A 执行,但行 B 中的法术在 邻接行 A 中对应位置的法术 执行时同时触发(邻接加成)。
  • 邻接规则: 将 slot[i]slot[i + grid_cols] 视为"竖向相邻",两者同类时触发 adjacency_bonus
  • 代表 Core: matrix_board_2x48槽)

邻接加成效果表(P6-N13 权威定义)

预编译阶段 _flatten_matrix 检测以下组合,注入对应隐式 MODIFIER 节点:

行 A 法术类型 行 B 法术类型 触发条件 注入效果 说明
ACTION ACTION(同 ID 完全相同的 ACTION 对齐 伤害 ×1.5damage_mult +0.5 "强化同类弹"
ACTION MODIFIER 任意 该 MODIFIER 效果额外再施加一次(等效双倍加成) "增幅器加倍"
MODIFIER MODIFIER(同 ID 完全相同的 MODIFIER 对齐 修正值 ×2(平直叠加而非乘算) "共鸣修正"
ACTION TRIGGER 任意 该 TRIGGER 的 proc_rate ×1.5(最大 1.0 "增压触发" ⚠️未实现(代码只实现 ACTION+ACTION / ACTION+MODIFIER / MODIFIER+MODIFIER 三行)
LOGIC 任意 任意 无加成(LOGIC 节点不参与邻接计算) 设计原则:逻辑层不受物理拓扑影响
空槽 任意 行 A 为空 无加成 空槽不传递邻接效果

实现说明_flatten_matrix 在预编译时查表,按上表注入 ImplicitModifierNode;运行时 SpellEvaluator 不感知矩阵结构,仅执行线性化后的节点序列。邻接加成不累叠(同一个 ACTION 最多受益于一次邻接加成)。

2.3 CIRCUIT(电路板)

         [ 2 ]
          ↑
[ 0 ] -> [ 1 ] -> [ 3 ] -> [ 4 ]
          ↓
         [ 5 ] -> [ 6 ]
  • 插槽有明确的 有向边(Edge 连接,执行流可分叉。
  • 玩家可在 slot_configs 中配置分叉点(Splitter)和合并点(Merger)。
  • 代表 Core: circuit_fork7槽,含1个分叉点)

CIRCUIT Core 的 CoreDefinition 资源格式 edges 列表通过 CoreDefinition.edges@export var edges: Array = [])存储, slot_configs 仅存储每个槽的类型约束与标签;不再在 slot_configs 内部嵌套 "edges" key。

{
  "topology": "CIRCUIT",
  "slot_configs": [
    { "index": 0, "allowed_types": ["ACTION","MODIFIER","TRIGGER","LOGIC"], "tags": [] },
    { "index": 1, "allowed_types": ["ACTION","MODIFIER","TRIGGER","LOGIC"], "tags": ["splitter"] },
    { "index": 2, "allowed_types": ["ACTION","MODIFIER"], "tags": ["branch_a"] },
    { "index": 3, "allowed_types": ["ACTION","MODIFIER","TRIGGER","LOGIC"], "tags": [] },
    { "index": 4, "allowed_types": ["ACTION","MODIFIER","TRIGGER","LOGIC"], "tags": [] },
    { "index": 5, "allowed_types": ["ACTION","MODIFIER"], "tags": ["branch_b"] },
    { "index": 6, "allowed_types": ["ACTION","MODIFIER"], "tags": ["branch_b"] }
  ],
  "edges": [
    { "from": 0, "to": 1 },
    { "from": 1, "to": 2 },
    { "from": 1, "to": 3 },
    { "from": 1, "to": 5 },
    { "from": 3, "to": 4 },
    { "from": 5, "to": 6 }
  ]
}

CoreDefinition.edges_flatten_circuit() 做 Kahn 算法拓扑排序的唯一边表来源。 edges 为空则视为 LINEAR 拓扑(降级处理)。LINEAR/MATRIX Core 的 edges 保持空数组即可。


3. 特性标签 (Feature Tags)

标签 效果 稀有度要求
persistent_memory 法杖的寄存器 (R1-R4) 在两次施法之间保留数值(跨帧持久化) 稀有+
dual_stream 同时从插槽序列头尾各执行一次,两条流的弹头同时发射 史诗
shuffle_deck 每次充能完成后,随机打乱插槽执行顺序 稀有
infinite_spells 法力耗尽时不停止执行,但每发增加 1 点 HP 代价(需配合 heavy_cost 法术) 传说
always_cast_last 无论 Deck 如何执行,最后一个插槽的法术在一轮结束时必定执行一次 稀有

⚠️ 实现现状 (2026-07-20 审计)5 个特性标签中只有 persistent_memory 实现dual_stream / shuffle_deck / infinite_spells / always_cast_last 在代码里除常量定义外无任何逻辑引用core_feature_tag.gd:6 自注"S6 P1 实现")。下文对这 4 个标签的详细行为规范(P5-N2/P6-N7/P6-N12 等)均为目标设计。详见 审计报告

实现约束说明(P5-N2

  • shuffle_deck:仅随机化独立 ACTION 节点的执行顺序;MODIFIER 节点始终与其紧接的 ACTION 视为原子单元整体移动,防止修正器与错误的动作配对,保持构建意图的确定性。UI 上以"卡组"为单位展示被打乱的顺序,而非单张卡牌。
  • dual_stream:两条执行流各自独立计算 ops 消耗,每条流的 MAX_OPS 上限 = cpu_limit × MAX_OPS_PER_CPU / 2(平均分配)。双流模式下单帧总指令数与单流相同,不额外增加运算力消耗;实际可用插槽从序列头尾各取一半,两条流不可互相访问对方的槽位。 奇数插槽行为(P6-N12 规范):当 slot_count 为奇数时(如 7 槽),前向流取前 floor(slot_count / 2) 个槽(02),后向流取后 floor(slot_count / 2) 个槽(46),中间槽(index = slot_count / 2,此处为 index 3)由前向流额外执行(不计入后向流)。 UI 上奇数情况下中间槽显示"▶ 仅前流"标注,防止玩家困惑。 示例(7 槽):[0前][1前][2前] | [3前专属] | [6后][5后][4后]
  • always_cast_last:无论 Deck 如何执行,一轮结束时对最后一个插槽的法术额外执行一次。 边界行为(P6-N7 补充)
    • 若最后一个插槽为 ACTION:正常发射一颗子弹(使用一轮结束时的当前 CastStats,所有 MODIFIER 已在上一轮累积)。
    • 若最后一个插槽为 MODIFIER:静默跳过(MODIFIER 必须有后续 ACTION 才能生效,单独执行没有效果),此次额外执行不消耗 mana。
    • 若最后一个插槽为 TRIGGERLOGIC:同样静默跳过(无 ACTION 上下文,执行无意义)。
    • UI 提示:最后一个插槽为非 ACTION 时,卡牌展示灰色小锁图标(∅无效提示),防止玩家误以为 MODIFIER 也会被额外执行而混淡。

Feature Tag 引用规范(ADR-R5-N2:业务代码中必须通过 CoreFeatureTag.PERSISTENT_MEMORYCoreFeatureTag.DUAL_STREAM 等常量引用标签,禁止使用裸字符串。详见 architecture_design.md §ADR-R5-N2


4. Core 实例(参考设计)

ID 名称 稀有度 拓扑 插槽数 特性 定位
wand_basic 朴素法杖 普通 LINEAR 5 初始装备,入门用
wand_fast 急促法杖 普通 LINEAR 4 基础施法延迟 ×0.7,容量小
staff_long 长卷轴 稀有 LINEAR 10 插槽多,回蓝慢
matrix_board 构造板 稀有 MATRIX_2X4 8 解锁邻接加成玩法
wand_memory 记忆法杖 史诗 LINEAR 6 persistent_memory 寄存器跨帧,启用计数器构建
circuit_fork 分叉回路 史诗 CIRCUIT 7 双分支执行,高复杂度
wand_eternal 永恒法杖 传说 LINEAR 8 infinite_spells 卖血流终极武器

5. 序列化格式 (SaveData)

法杖的存档需要记录:Core 类型 + 每个插槽内填的法术 ID。

{
  "schema_version": 1,
  "wands": [
    {
      "core_id": "wand_basic",
      "slots": ["double_cast", "spark_bolt", "damage_plus", null, null]
    },
    {
      "core_id": "matrix_board",
      "slots": ["trigger_hit", "chain_bolt", null, null, "homing", "damage_plus", null, null]
    }
  ]
}

注意

  • null 表示该插槽为空。
  • 加载时如果 core_id 或法术 ID 不存在于注册表,记录警告日志并跳过,不崩溃(向前兼容)。
  • schema_version 字段用于未来迁移,初始值为 1。

6. 与 SpellEvaluator 的接口

SpellEvaluator 在执行前需要从 Core 获取:

# spell_evaluator.gd 伪代码
# SpellEvaluator 采用两阶段架构:
#   - compile_wand():法杖装备/换牌时调用,结果缓存为 CompiledDeck
#   - execute_compiled():每次施法时执行缓存的线性化节点序列

# ── 阶段 1:编译(法杖装备/换牌时调用,结果缓存于 CompiledDeck)──────────────────
# 参数类型:raw_deck: SpellDeck(通过 deck.nodes[i] 访问节点序列,禁止传入 Array[SpellNode]
# ⚠️ 实现现状(2026-07-20):真实签名 compile_wand(core, raw_nodes: Array)——第 2 参是 Array 而非 SpellDeck
func compile_wand(core: CoreDefinition, raw_deck: SpellDeck) -> CompiledDeck:
    match core.topology:
        CoreDefinition.LINEAR:
            return CompiledDeck.new(raw_deck.nodes)
        CoreDefinition.MATRIX_2X4:
            return _flatten_matrix(raw_deck, core)        # 注入邻接 MODIFIER
        CoreDefinition.CIRCUIT:
            # 传 core.edges(顶层有向边表),core.slot_configs 仅存槽约束/标签,不含拓扑边
            return _flatten_circuit(raw_deck, core)       # Kahn 拓扑排序 + LOGIC_FORK 注入
    return CompiledDeck.new([])  # 未知拓扑降级为空 Deck

# ── 阶段 2:执行(每次施法时调用缓存的 CompiledDeck)────────────────────────────
# 必须接受 core: CoreDefinition 参数,用于读取 core.feature_tags 判断寄存器持久策略。
# 禁止将 feature_tags 复制到 SpellContextSpellContext 不持有 Core 引用;ctx.core_feature_tags 不存在)。
# ⚠️ 实现现状(2026-07-20):真实签名 execute_compiled(compiled, caster_id: int, spawn_pos: Vector2)——不传 ctx/corefeature_tags 从 CompiledDeck 编译期快照读取
func execute_compiled(compiled: CompiledDeck, ctx: SpellContext, core: CoreDefinition) -> void:
    _run_deck(compiled, ctx)  # SpellEvaluator 不感知拓扑,仅执行线性化节点序列

    # 执行完毕后处理特性标签(直接访问传入的 CoreDefinition
    if CoreFeatureTag.PERSISTENT_MEMORY not in core.feature_tags:  # ✅ 使用常量(ADR-R5-N2
        ctx.registers.fill(0.0)  # 非持久内存,帧结束后清零(registers 即 "memory_bank",见 architecture_design.md §3.2

7. UI 集成要点

  • 背包界面:根据 topology 渲染不同的插槽布局组件(LINEAR 显示横排,MATRIX 显示网格)。
  • 拓扑提示:悬停插槽时显示该插槽的 allowed_types 约束(如"此槽仅接受 Trigger 类型")。
  • 测试靶场:在商店界面提供靶人,玩家可立即测试当前法杖效果,无需进入战斗
  • cpu_limit 可视化:在法杖编辑 UI 显示当前构建消耗的"运算力"进度条,接近 cpu_limit 时变黄/变红。