Files
spellforge/docs
joywayerandClaude Opus 5 1aeae19187 docs: 订正 move_speed 300→200 与 cpu_limit「从不读」告警;路线图 E3-① 勾除并订正现状
move_speed 300 从未被任何代码读取,是纸面孤值;200 自 S0 沿用并已围绕它调校
20 波内容与 Boss 弹幕密度,以既成事实为准。cpu_limit 行的「代码硬编码 40×5、
从不读 cpu_limit」告警随本次接线失效,改记生效值构成(玩家基准 0 + 法杖 3–8)
与 hard(50) 作用于加总后的总值;基准值一并由 5 改为 0,否则重蹈 move_speed 的
「纸面值与实现长期脱节」。

路线图 E3 现状原写「player_stats.gd 有 resistance 等占位属性(4/5 stats 未接线)」
与事实不符——权威属性表是 11 个,armor/resistance 根本不在表内,是实现先于设计的
孤儿字段。E3-① 勾除并标注实际范围(只做框架 + 三条死数据),attunement_×4 /
luck / recharge_speed_mod 三项延后各附理由;顺带记 cast_delay_mod 的饱和区待人定。
E1 现状里「玩家侧抗性仍占位」一句同因失效,一并订正。

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-31 12:54:22 +08:00
..

魔法工匠 (Arcane Artificer) - 文档索引

Roguelite 动作射击游戏 | Godot 4.x + GDScript


目录结构

docs/                     产品文档(设计规范 / 架构参考)
  README.md               本文件,文档导航索引
  design/                 游戏设计文档
  technical/              技术架构文档
  mechanics/              机制细节设计
  handbook/               开发者 / 策划手册

docs_dev/                 开发过程 / 归档文档(工程根目录,见下)
  development_plan.md          垂直切片开发计划
  certification_checklist.md   平台认证清单
  archived_cocos_architecture_draft.md  早期 Cocos 架构草案(已废弃)

工程计划与归档 (../docs_dev/)

开发过程/追踪与归档类文档已移出 docs/,统一存放于工程根目录的 docs_dev/

文档 内容简述
development_plan.md S0–S6 骨架垂直切片、风险登记、验收与出口检查、跨切片约束、参考文档地图(与 technical/ 架构对齐)
certification_checklist.md Steam / Switch / 无障碍 / 发布前检查项,并映射到切片
archived_cocos_architecture_draft.md 早期 Cocos Creator / TypeScript 架构草案(已废弃,权威架构见 technical/

游戏设计 (design/)

文档 内容简述
game_design.md 核心玩法 GDD:法术管道系统、游戏循环、构建示例、Endless 排行榜
numerical_design.md 数值策划:资源模型、XP 曲线公式、法术参数表、怪物成长曲线、Boss DPS 门槛、平衡性对策
core_wand_design.md Core 设计:插槽拓扑、MATRIX 邻接加成效果表、Feature Tags 常量规范、dual_stream 奇数槽行为

技术架构 (technical/)

文档 内容简述
architecture_design.md 系统分层架构(含 ZoneManager/VFXManager/CoreFeatureTag)、HMWS 法术系统设计、ECS-Lite、超大弹体 SpatialGrid 豁免、Feature Tags 常量 ADR、DamageType 枚举、自动瞄准算法、consume_inputs 掩码机制、技术栈选型
implementation_plan.md 各模块详细实现方案(含 VFXManager 正式定义)、EventBus 完整事件目录、SubPayloadRegistry 竞争条件保证、DamageContextPool 定义、PlayerManager 移动状态追踪、DPS 面板实现、开发阶段规划、关键技术难点预案

机制细节 (mechanics/)

文档 内容简述
combat_mechanics_depth.md 投射物溯源、连锁弹射、状态效果、伤害计算管线
combat_mechanics_extensions_v2.md 物理运动学、环境场、触发链、属性快照、Proc 系数
weapon_system_expansion.md 轨迹修正器(正弦/回旋镖/环绕-双写优化)、触发器系统、元素交互
consecutive_hits_stacking.md 连击窗口、叠加收益、Combo Tracker 数据结构设计、COMBO_MARK tick_interval 语义说明
advanced_mechanics_summons_and_environment.md 召唤体系(炮台/随从/卫星)、环境传播、地面效果、StatusID Autoload 常量规范

核心设计理念

  • "Noita 遇上 Brotato":快节奏割草 × 深度法术编程
  • 法术管道 (Spell Pipeline)ACTION → MODIFIER → TRIGGER → LOGIC GATE 四类节点组合,实现图灵完备的武器逻辑
  • 数值哲学构建强度 = (基础数值 × 修正系数) ^ 逻辑复杂度,提供宽广边界而非限制玩家策略

关键规范速查 (Design Rules Quick Reference)

规范 ID 位置 内容摘要
P6-N2 architecture_design.md §4.2 Homing 弹共用敌人位置快照 ⚠️ 已退役(2026-07-30:归航改为按 entity_id 锁定目标,目标有效时零查询、仅失效时重选,不再需要共用快照。见 spec 2026-07-30-bullet-homing-design.md §2.4 / §2.7
P6-N3 architecture_design.md §4.1 SpellContext 池大小 = 32
P6-N5 implementation_plan.md §2.2 CompiledDeck 多项式滚动哈希(顺序敏感,种子含 grid_cols,防换位误判)
P6-N6 implementation_plan.md §2.1 EventID 12 MINION_EXPIRED
P6-N10 implementation_plan.md §2.2 SubPayloadRegistry 竞争条件安全保证
P6-N11 architecture_design.md §4.3 超大弹体(radius>64px)豁免 SpatialGrid
P6-N12 core_wand_design.md §3 dual_stream 奇数槽前流多得中间槽
P6-N13 core_wand_design.md §2.2 MATRIX 邻接加成效果表
P6-N14 consecutive_hits_stacking.md COMBO_MARK tick_interval 语义重载说明
P6-N15 weapon_system_expansion.md §1.3 环绕弹先判断 is_orbiting 再决定是否积分
P6-N16 numerical_design.md §1.2 XP 曲线:⌊10 × 1.4^(level-1)⌋
P6-N17 numerical_design.md §5.D W20 Boss 物理构建 DPS 门槛
P6-N18 numerical_design.md §5.E infinite_spells + heavy_cost 叠加规则
P6-N19 game_design.md §8.2 Endless 排行榜单整数双维度评分方案
P6-N20 architecture_design.md §3.4.A _flatten_matrix 仅遍历 Row ARow B 不追加为执行节点
P6-N21 architecture_design.md §8 ADR-R5-N1 ZoneManager 首元素删除用循环前移,不用 remove_at()
P6-N22 numerical_design.md §1.2 XP 可行性验证:Level 20 为 20 波通关上限,Level 21+ 服务 Endless
P6-N23 numerical_design.md §1.2 商店刷新费用公式:20 + (本波刷新次数 × 10)G,波次结束重置
P6-N24 architecture_design.md §4.2 ProjectileDef(生成时模板)vs _bullet_contexts(运行时可变冷状态)职责分离
P6-N25 architecture_design.md §4.2 _enemy_pos_snapshot 必须声明为类成员(预分配复用,禁止每帧 var 分配) ⚠️ 已退役(2026-07-30:约束对象 _enemy_pos_snapshot 已删除 —— 归航改用按 entity_id 锁定目标,而该快照按槽位存位置、不含 entity_id,支撑不了锁定语义。见 spec 2026-07-30-bullet-homing-design.md §2.7
P6-N26 architecture_design.md §8 ADR-R5-N1 ZoneManager ZONE_STRIDE=8(含 tick_interval+tick_accum),spawn_zone 需 tick_interval 参数
P6-N27 implementation_plan.md §2.3.C _visible_flags 类成员 PackedByteArrayhas_point() 赋值须显式 int() 转换
P6-N28 implementation_plan.md §2.1 DamageContextPool Autoload 定义(acquire/get_context/release
P6-N29 architecture_design.md §3.4.C consume_inputs 运行时通过 _consumed 掩码实现,CompiledDeck 只读不修改
P6-N30 advanced_mechanics §3.2 StatusID Autoload 常量规范(禁止 StatusType.XXX 枚举写法)
P6-N31 architecture_design.md §3.2 ProjectileDef.reset() 必须用 DamageType.PHYSICAL(禁止裸整数 0
P6-N32 architecture_design.md §3.2 SpellContext 必须提供 reset() 方法;registers 不在 reset() 中清零(由 SpellEvaluator 按持久化策略决定)
P6-N33 architecture_design.md §3.4.A _flatten_circuit() 正式算法:Kahn 拓扑排序 + splitter 展开为 SubPayload LOGIC_FORK;环路时返回空 CompiledDeck
P6-N34 combat_mechanics_depth.md §5 BulletContext 禁止持有 calc_damage() 等伤害计算逻辑(SRP);弹射衰减由 EnemyManager.apply_damage() 用 bounce_count 计算
P6-N35 combat_mechanics_depth.md §4 / implementation_plan.md §2.1 DamageContext 权威字段:base_damage/mult/damage_type/owner_id/source_tags/is_crit/pierce_rateDamageContextPool.acquire() 含 is_crit+pierce_rate 参数;reset() 必须重置 mult=1.0
P6-N36 implementation_plan.md §2.1 EventID.DASH_TRIGGERED = 13;负载: caster_id+stationary_time;发出方 PlayerManager,订阅方 SpellEvaluator+UIManager
P6-N37 implementation_plan.md §2.2 LOGIC_EVERY_N_SHOTS 必须读写 SpellContext.registers(跨帧持久),严禁使用 CastState.registers(每帧重置)
P6-N38 implementation_plan.md §2.5.E DPS 环形缓冲区:PackedFloat64Array+PackedFloat32Array_RB_SIZE=256O(1) 写入,消除 pop_front() O(N) 移位
P6-N39 consecutive_hits_stacking.md / advanced_mechanics §3.2 StatusID.VULNERABILITY=8StatusID.COMBO_MARK=7;新增 ID 从 9 开始递增
P6-N40 architecture_design.md §3.2 SpellContext.stats 必须声明时初始化(= CastStats.new()),防止 reset() 空指针
P6-N41 architecture_design.md §3.2 ProjectileDef.reset() 必须重置 spawn_position(防止池化复用携带上帧发射原点)
P6-N42 core_wand_design.md §1 CoreDefinition 新增 @export var edges: Array = []CIRCUIT 有向边顶层字段);_flatten_circuit() 从 core.edges 读取,不在 slot_configs 内嵌 "edges"
P6-N43 architecture_design.md §3.4.A _flatten_circuit() 降级为 LINEAR 时使用 CompiledDeck.new(deck.nodes) 内联(_flatten_linear() 函数不存在)
P6-N44 architecture_design.md §3.4.A LOGIC_FORK 节点:type=LOGIC, id="LOGIC_FORK"meta "fork_branch_ids"_execute_logic 并行触发所有分支 SubPayload
P6-N45 implementation_plan.md §2.3.C ENEMY_STRIDE: int = 8 命名常量,与 BULLET_STRIDE=12 规范对齐
P6-N46 architecture_design.md §8 ADR-R5-N1 ZoneManager swap-and-pop 移除时必须调用 VFXManager.play("zone_expire", ...)
P6-N47 implementation_plan.md §2.3.A MOVE_THRESHOLD_NORM = 0.067(归一化输入幅度,非像素/秒);DPS get_dps() 懒加载 _recalc_windowrecord_damage() 仅做 O(1) 写入
P6-N48 architecture_design.md §3.2 SpellContext.registers 必须声明时初始化 = PackedFloat32Array([0.0, 0.0, 0.0, 0.0])(长度 4);未初始化则 registers[0] 触发 out-of-bounds 崩溃,fill(0.0) 对空数组无操作
P6-N49 implementation_plan.md §2.1 DamageContext 必须独立为 damage_context.gdclass_name DamageContext);DamageContextPool 为独立 Autoload 文件,不声明 class_name;两者混于同一文件会导致 _pool: Array[DamageContext] 存入具有管理方法的池管理器实例(语义错误)
P6-N50 numerical_design.md §2.2 Spell Database 新增 damage_type 列;Projectile 类型法术必须显式指定 DamageType(如 chain_bolt=LIGHTNING, nuke=PHYSICAL);Modifier/Trigger/Multicast 填 —
P6-N51 architecture_design.md §3.2 SpellNode.type 声明为 SpellType = SpellType.ACTION(枚举类型注解);原 int 类型丢失编译期检查
P6-N52 architecture_design.md §3.4.A _flatten_circuit() LOGIC_FORK 构造:每条出边必须单独 SubPayloadRegistry.register([entry_node]),禁止将所有分支入口节点合并到同一 SubPayload(否则并行分支退化为顺序执行)
P6-N53 architecture_design.md §3.2 SpellNode 新增 element_tags: Array[String] = [];共鸣系统 _check_resonance() 通过此字段匹配 resonance_recipes.jsonpattern 数组的 "tag:xxx" 元素
P6-N54 implementation_plan.md §2.3.C EnemyManager LOD 循环必须使用 ENEMY_STRIDE= 8),禁止使用旧常量名 STRIDE
P6-N55 core_wand_design.md §6 SpellEvaluator 采用两阶段架构:compile_wand() 编译(法杖装备/换牌时) + execute_compiled() 执行(每次施法);_flatten_circuit() 必须传 core.edges,不是 core.slot_configs
P6-N56 implementation_plan.md §2.2 SpellDeck.pop() 简化版须含 null 安全边界;权威完整实现(含 _consumed 掩码)见 architecture_design.md §3.4.C
P6-N57 architecture_design.md §3.4.A _flatten_circuit() 分叉检测须用出度(adj[slot_idx].size() > 1),禁止依赖 "splitter" tagtag 漏写时静默失效);"splitter" tag 仅保留为 UI 标注
P6-N58 architecture_design.md §3.4.A LOGIC_FORK SubPayload 须用 _collect_branch_path() 收集完整分支路径(多节点链),禁止仅注册入口节点(否则后续节点全被丢弃);分叉节点自身法术先追加再插入 LOGIC_FORK
P6-N59 implementation_plan.md §2.5.A DASH_TRIGGERED 事件负载须包含 stationary_time: floatP6-N36 要求),代码中 EventBus.emit 必须同时传 caster_id + stationary_time,冲刺后立即清零 _stationary_time
P6-N60 implementation_plan.md §2.5.D VFXManager 必须维护 _active_count: int 计数器;_pool_pop() 时 +1_pool_return() 时 -1play() 中以 O(1) 方式检查上限,禁止遍历 pool 字典统计(O(N))
P6-N61 core_wand_design.md §6 execute_compiled(compiled, ctx, core) 必须显式接收 core: CoreDefinition 参数;通过 core.feature_tags 判断持久内存策略;禁止使用 ctx.core_feature_tagsSpellContext 无此字段)
P6-N62 combat_mechanics_depth.md §4 DamageContext 新增 mult: float = 1.0 字段(伤害乘算系数);reset() 必须重置 mult = 1.0,否则池化复用时暴击系数残留
P6-N63 architecture_design.md §3.4.A _collect_branch_path()in_degree 参数必须传 Kahn 循环的原始入度副本(orig_in_degree = in_degree.duplicate(),在建立 adj 表之后、Kahn BFS 之前);Kahn 结束后工作数组全为 0,用其做 in_degree[nxt] <= 1 汇聚检测恒成立,导致分支路径越界纳入主链节点
P6-N64 architecture_design.md §3.4.A _flatten_circuit() 必须维护 in_branch_payload: Dictionary_collect_branch_path() 每访问一个槽索引即写入;主链 topo_order 循环在追加节点前检查此字典并跳过,防止分支节点在 SubPayloadLOGIC_FORK 路径)和主链(topo_order 直接追加)中各执行一次
P6-N65 core_wand_design.md §6 compile_wand() 参数类型修正:slot_spells: Array[SpellNode]raw_deck: SpellDeck_flatten_matrix()_flatten_circuit() 均通过 deck.nodes[i] 访问节点,传入裸 Array 会触发"Invalid get index 'nodes'"运行时错误;LINEAR 路径同步改为 CompiledDeck.new(raw_deck.nodes)
P6-N66 architecture_design.md §3.4.A _collect_branch_path() 内若遇到嵌套分叉(adj[cur].size() > 1),必须递归为每条子边调用自身并注册 SubPayload,再注入嵌套 LOGIC_FORK 节点后 break;直接 break 会丢弃子分支全部节点
P6-N67 implementation_plan.md §2.1 DamageContextPool.acquire() 必须在赋值任何字段前先调用 ctx.reset(),防止池化复用时残留 source_tags(如 TRIGGERED=2)污染新的伤害上下文
P6-N68 mechanics/consecutive_hits_stacking.md StatusTypeDef.can_catalyze 类型为 Array[int]COMBO_MARK 的正确值为 [](空数组),写 falsebool)在 GDScript 4 严格类型模式下触发类型赋值错误
P6-N69 implementation_plan.md §2.5.A 停步蓄力采用边沿检测:PlayerManager 维护 _charge_triggered: bool;首次 _stationary_time >= CHARGE_TRIGGER_TIME 时置 true 并发出 CHARGE_FIRED(ID=14,一次性);移动/冲刺后重置为 falseSpellEvaluator 订阅 CHARGE_FIRED禁止订阅 CHARGE_STATE_CHANGED(后者每帧 60Hz 发送,订阅将导致法术每帧重复释放)
P6-N70 architecture_design.md §3.4.A SpellEvaluator 预编译入口必须命名为 compile_wand(core: CoreDefinition, raw_deck: SpellDeck),权威定义见 core_wand_design.md §6
P6-N71 architecture_design.md §8 ADR-R5-N1 ZoneManager tick 累积器必须使用 while + -= tick_interval(同 StatusManager 模式),防止大帧余量丢失。SpatialGrid.query_circle 仅调用一次,置于 if 守卫内、while 循环外,避免重复空间查询
P6-N72 implementation_plan.md §2.5.D VFXManager _instantiate_vfx 必须设置 node.one_shot = true;否则 GPUParticles2D 无限循环,finished 信号永不触发,_pool_return 永远不被调用,_active_count 只增不减。节点挂载到 VFXManager Autoload 自身,跨场景持久存活
ADR-R5-N1 architecture_design.md §8 ZoneManager 架构定义(PackedFloat32Array SoAZONE_STRIDE=8MAX_ZONES=64,无 Area2D
ADR-R5-N2 architecture_design.md §8 Feature Tags 字符串常量规范(CoreFeatureTag Autoload
V-N1 implementation_plan.md §2.5.D VFXManager MAX_ACTIVE_VFX = 200