初次提交

This commit is contained in:
2026-07-20 10:56:52 +08:00
commit 7bcc0026e0
462 changed files with 50191 additions and 0 deletions
+252
View File
@@ -0,0 +1,252 @@
# 构建核心 (Core) 设计文档
## 0. 概念定位
**Core(构建核心)** 是玩家持有的"武器主体"。它定义了:
1. **插槽拓扑 (Slot Topology)**:有多少个插槽、如何排列(线性/矩阵/电路板)。
2. **基础属性 (Base Stats)**:法杖自带的充能速度、容量、基础蓝耗。
3. **特殊规则 (Core Rules)**:部分高阶 Core 拥有改变执行规则的特性(如持久内存、双流并行)。
> Core 本身不执行法术,它是 SpellDeck 的"容器",决定了 SpellEvaluator 的执行环境。
---
## 1. 数据结构 (GDScript)
```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 特性标签 (Array[String]),驱动特殊执行规则
## 例如: ["persistent_memory", "dual_stream", "shuffle_deck"]
@export var feature_tags: Array[String] = []
## 图标路径
@export var icon: Texture2D
```
---
## 2. 插槽拓扑类型
### 2.1 LINEAR(线性)
```
[ 0 ] -> [ 1 ] -> [ 2 ] -> [ 3 ] -> [ 4 ]
```
- 最基础的类型,执行流从左到右。
- 新手友好,等同于标准 Noita 法杖。
- **代表 Core**: `wand_basic`5槽)、`staff_long`10槽)
### 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_2x4`8槽)
**邻接加成效果表(P6-N13 权威定义)**
预编译阶段 `_flatten_matrix` 检测以下组合,注入对应隐式 MODIFIER 节点:
| 行 A 法术类型 | 行 B 法术类型 | 触发条件 | 注入效果 | 说明 |
| :--- | :--- | :--- | :--- | :--- |
| **ACTION** | **ACTION**(同 ID) | 完全相同的 ACTION 对齐 | 伤害 ×1.5`damage_mult +0.5` | "强化同类弹" |
| **ACTION** | **MODIFIER** | 任意 | 该 MODIFIER 效果额外再施加一次(等效双倍加成) | "增幅器加倍" |
| **MODIFIER** | **MODIFIER**(同 ID | 完全相同的 MODIFIER 对齐 | 修正值 ×2(平直叠加而非乘算) | "共鸣修正" |
| **ACTION** | **TRIGGER** | 任意 | 该 TRIGGER 的 `proc_rate ×1.5`(最大 1.0 | "增压触发" |
| **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_fork`7槽,含1个分叉点)
**CIRCUIT Core 的 `CoreDefinition` 资源格式**
`edges` 列表通过 `CoreDefinition.edges``@export var edges: Array = []`)存储,
`slot_configs` 仅存储每个槽的类型约束与标签;不再在 slot_configs 内部嵌套 `"edges"` key。
```json
{
"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 如何执行,最后一个插槽的法术在一轮结束时**必定**执行一次 | 稀有 |
> **实现约束说明(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)` 个槽(0~2),后向流取后 `floor(slot_count / 2)` 个槽(4~6),**中间槽(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。
> - 若最后一个插槽为 **TRIGGER** 或 **LOGIC**:同样静默跳过(无 ACTION 上下文,执行无意义)。
> - UI 提示:最后一个插槽为非 ACTION 时,卡牌展示灰色小锁图标(∅无效提示),防止玩家误以为 MODIFIER 也会被额外执行而混淡。
> **Feature Tag 引用规范(ADR-R5-N2**:业务代码中必须通过 `CoreFeatureTag.PERSISTENT_MEMORY`、`CoreFeatureTag.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。
```json
{
"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` 获取:
```gdscript
# spell_evaluator.gd 伪代码
# SpellEvaluator 采用两阶段架构:
# - compile_wand():法杖装备/换牌时调用,结果缓存为 CompiledDeck
# - execute_compiled():每次施法时执行缓存的线性化节点序列
# ── 阶段 1:编译(法杖装备/换牌时调用,结果缓存于 CompiledDeck)──────────────────
# 参数类型:raw_deck: SpellDeck(通过 deck.nodes[i] 访问节点序列,禁止传入 Array[SpellNode]
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 不存在)。
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` 时变黄/变红。
+387
View File
@@ -0,0 +1,387 @@
# 游戏设计说明书:魔法工匠 (Arcane Artificer)
## 1. 游戏概述 (Game Overview)
**《魔法工匠》** (暂定名) 是一款融合了 **Roguelite 割草动作****深度法术编程** 的快节奏射击游戏。
玩家扮演一名被困在异次元的“土豆工匠”,必须利用手中的核心(法杖)和收集到的法术芯片,组装出毁天灭地的魔法武器,抵御无穷无尽的怪物潮。
本游戏的核心差异点在于:**你捡到的不是武器,而是逻辑与弹头的碎片。你必须像程序员一样“编写”你的子弹。**
---
## 2. 核心体验与特色 (Core Experience)
### 2.1 "Noita" 遇上 "Brotato"
* **快节奏战斗**:继承 Brotato 的短短波次(每波 60-90 秒),自动射击,高密度怪物,让玩家肾上腺素飙升。
* **深深度构建**:继承 Noita 的法杖编辑深度。战斗之余的商店阶段,是冷静思考、调整“代码”的时间。
### 2.2 真正的模块化 (True Modularity)
不像传统游戏那样只是“加一个火属性配件”,本作允许玩家构建复杂的逻辑链。
* *例如*:“当子弹击中墙壁时,生成一个炮台,炮台每秒发射三枚追踪冰弹。”
### 2.3 性能怪兽
得益于底层的 **ECS-Lite (Autoload Manager-based)****Spatial Grid** 技术,游戏支持同屏 **1000+** 敌人和 **2000+** 独立运算的弹幕,每一次爆炸都不仅是贴图,而是真实的物理/逻辑判定。
---
## 3. 武器系统详解:法术管道 (The Spell Pipeline)
这是游戏最核心的玩法。武器由 **“核心 (Core)”** 和 **“法术节点 (Spell Node)”** 组成。
### 3.1 核心 (The Core)
核心相当于“枪身”或“主板”。它决定了基础属性:
* **插槽数 (Capacity)**: 能插多少个法术芯片。
* **回蓝速度 (Mana Charge)**: 每秒恢复多少法力。
* **最大魔力 (Mana Max)**: 蓝条上限。
* **施法频率 (Cast Rate)**: 每秒执行多少次主循环。
> **术语说明**:技术文档(`core_wand_design.md`、`numerical_design.md`)统一使用
> **Cast Delay(施法后摇)** 表示同一概念的倒数形式(`cast_delay = 1 / cast_rate`)。
> 设计文档(GDD)用 Cast Rate"快慢"直觉更好);实现层用 Cast Delay("乘算系数"更方便计算)。
> 两者映射关系:`base_cast_delay = 0.25s` → `cast_rate = 4 次/秒`(默认)。
* **拓扑结构**: 所有的核心都有一个 Main 入口,但高级核心可能有额外的 "OnKill"(击杀时)或 "OnHit"(受击时)入口插槽。
### 3.1.A 多 Core 持有与激活规则 (Multi-Core Rules)
> 这是最基础的游戏规则,所有构建策略以此为前提。
* **同时激活数量上限**: 玩家在战斗中**同时激活 1 个 Core**(激活的 Core 持续执行其主循环自动射击)。
* **背包携带上限**: 背包最多同时持有 **3 个 Core**(含当前激活的 1 个)。超出时商店购买会提示选择丢弃或出售。
* **切换时机**: 仅可在 **商店阶段(非战斗期间)** 切换激活的 Core;战斗中不可切换。
* **插槽独立**: 每个 Core 的法术插槽互相独立,切换 Core 时旧 Core 插槽内容保留,不适配的卡移入等待区(规则见 §5.G5)。
* **特殊机制 `dual_stream` Core**(史诗级 Feature Tag): 此类 Core 自身内部拥有两条并行执行流(从头尾各执行一次),**并非同时激活两个 Core**,不突破"同时激活 1 个"的规则。
* *设计意图*:限制同时激活数量避免 Brotato 式"堆满武器"的感官疲劳,同时背包存 3 个让玩家有转型预备的空间。
### 3.2 法术芯片 (Spell Chips)
法术分为四大类,通过排列组合产生无限可能。
#### A. 动作类 (Actions) - 实际产生的飞行物
* **魔法弹 (Spark Bolt)**: 基础伤害,低蓝耗。
* **黑洞 (Black Hole)**: 吸附敌人,无伤害。
* **治疗雾 (Healing Mist)**: 范围回血。
#### B. 修正类 (Modifiers) - 改变下一个动作的属性
* **伤害增强 (Damage+)**: 蓝耗+10,伤害+20。
* **弹道折射 (Bounce)**: 子弹遇墙反弹。
* **多重施法 (Multicast)**: 同时发射后面 2 个动作。
* **甚至有“数学修正”**: 比如 `Sin(Time)` 让子弹走正弦波轨迹。
#### C. 触发器类 (Triggers) - 逻辑的传递
这是构建“子母弹”的关键。
* **击中触发 (Trigger On Hit)**: 这个子弹本身伤害很低,但它击中敌人时,会以敌人为原点,**施放**这个芯片后面的法术。
* **定时触发 (Timer)**: 子弹飞行 0.5秒 后,施放后面的法术。
#### D. 逻辑门类 (Logic Gates) - 自动化的灵魂
这是超越 Noita 的设计,让武器变智能。
* **IF_HP_LOW**: 如果玩家血量 < 30%,执行后续法术,否则跳过。
* **IF_ENEMY_CLOSE**: 如果 2米内 有敌人,执行后续法术。
* **LOOP**: 循环执行后续 3 个法术 5 次(适合做激光加特林)。
### 3.3 扩展玩法设计 (Extended Gameplay Mechanics)
为了让构建过程不仅是“堆数值”,我们引入了三个深度维度:
#### A. 拓扑与空间构建 (Topology & Spatial Building)
核心(Core)不只是一个列表,而是一个**2D 电路板**。
* **物理邻接 (Adjacency)**:每个插槽可能有特殊的加成方向。
* *示例*:一个“增压核心”中间有一个红色插槽,标明“右侧插槽获得双倍效果”。
* **电路分流 (Circuit Splitting)**:电路板上可能有分支。
* *玩法*:使用 [分流器] 法术,将“电爆”效果只输送给上面的路线,将“冰冻”效果只输送给下面的路线。
#### B. 逻辑与自动化 (Logic & Automation)
引入变量寄存器,让法杖拥有简单的“图灵完备性”。
* **Global Registers (全局寄存器)**:允许在这个核心内读写变量 `A`, `B`
* **构建示例 - "蓄力逻辑"**:
* `[Set A = A + 1]` -> `[IF A < 100 THEN END]` -> `[Set A = 0]` -> `[终极核弹]`
* *效果*:这个法杖平时只会空转(每帧 A+1),直到第 100 帧时,才会发射一枚核弹。相当于自动蓄力 1.6秒。
* **玩家呈现策略(P5-C1**:寄存器 `A/B` 的读写操作**不直接作为可购买法术卡暴露给普通玩家**。游戏将寄存器操作封装为具体功能的法术卡:
* 「蓄力计数器」卡:内置完整的计数→阈值判断→重置→执行逻辑,卡面属性为"蓄力阶数 N",玩家感知为"蓄力 N 次后释放一次"。
* 「击杀连击」卡:内置 ON_KILL 计数→阈值→爆发→重置,玩家感知为"积累 5 击杀触发爆发"。
* 「裸寄存器模式」仅通过 **Tier 3+ 的「DEBUG 核心」**解锁,作为面向硬核玩家的高阶自定义工具,不作为默认游戏体验推送。
#### C. 炼金共鸣 (Alchemical Resonance)
这是隐藏的合成系统。当某些特定属性的法术在执行链中直接相邻时,会发生**质变**。
* `[水属性波]` + `[闪电链]` -> **自动合成** -> `[等离子风暴 (Plasma Storm)]`
* *效果*:原本是两个独立的法术,现在融合成了一个全新的强力法术,无需消耗额外的插槽。
* 这鼓励玩家去尝试各种元素的排列组合,寻找隐藏配方。
* **触发范围权威规则**:绝大多数共鸣配方要求两张法术卡 **直接相邻**`match: "adjacent"`)。
少数传说级配方允许跨 Deck 任意位置触发(`match: "anywhere_in_deck"`),但此类配方
`resonance_recipes.json` 中标注 `"rarity": 3`,且性能开销较高(O(N²) 全局扫描),
数量会严格控制。详见 `architecture_design.md §3.4.C`
* **配方发现机制(P5-C2)**:共鸣配方默认隐藏。玩家**首次实际触发某配方**后,图鉴自动解锁并展示该配方的完整描述(触发效果、参与元素、法术 ID)。触发前,图鉴仅展示"某两种元素似乎能产生反应…"的模糊提示(仅显示参与元素的属性标签,不透露具体法术 ID),引导探索而非强制查询外部攻略。
### 3.4 构建示例 (Build Examples)
1. **“霰弹枪”构建**:
* `[三双重施法]` -> `[扩散修正]` -> `[子弹]` -> `[子弹]` -> `[子弹]`
* *效果*:一次射出三发散开的子弹。
2. **“运载核弹”构建**:
* `[定时触发(1s)]` -> `[速度为0修正]` -> `[伤害增强]` -> `[伤害增强]` -> `[巨型爆炸]`
* *效果*:射出一个普通子弹,1秒后在空中停滞,然后引发核爆。
3. **“吸血炮台”构建**:
* `[击中触发]` -> `[召唤静止炮台]` -> `[炮台指令: 击中触发]` -> `[连锁闪电]` -> `[吸血修正]`
* *效果*:你射中地板,地板上长出一个炮台,炮台射出的闪电会连环电击敌人并把血吸给你。
---
## 4. 数值策划 (Numeric Planning)
为了支撑如此复杂的构建,数值系统必须严谨且具备极其宽广的边界。
### 4.1 核心资源:魔力 (Mana)
为了防止无限“加特林核弹”,限制资源是 **魔力**
* 强力修正器(如伤害+100)会有巨大的 **Delay (施法后摇)** 和 **Mana Cost (蓝耗)**
* **平衡点**:高射速 = 低单发伤害;高爆发 = 便秘的手感(打一发回蓝3秒)。
### 4.2 伤害公式 (The Formula)
> 完整权威公式定义该文件 `docs/design/numerical_design.md §3.1`。
$$ FinalDamage = ((Base + \sum Mod_{flat}) \times \prod Mod_{mult}) \times (1 - Res) - Armor $$
* **Base**: 法术动作的基础伤害 (e.g., 火球 10点)。
* **Mod_flat**: 修正器的数值加成 (e.g., +5伤害)。
* **Mod_mult**: 玩家天赋/暴击带来的百分比乘区 (e.g., 火焰伤害 +50%)。
* **Res**: 敌人元素抗性 (0.0~1.0)。
* **Armor**: 敌人护甲平铺扣除值(固定数值,不受百分比影响)。最终伤害最小为 1。
### 4.3 属性词条池 (Stats Pool) — *MVP 层*
玩家在升级或商店中购买的属性,直接影响构建的上限。
> ⚠️ **MVP 说明**:以下 5 条属性为 MVP 阶段实现目标。后续扩充方向:
> 第二批(防御类):护甲穿透、元素抗性降低、格挡概率。
> 第三批(资源类):击杀回血(吸血)、金币掉落增益、法力吸取。
> 第四批(移动类):冲刺距离、鬼影残影、落身位置标记。
| 属性名 | 影响 | 备注 |
| :--- | :--- | :--- |
| **施法速度 (Cast Speed)** | 减少每个法术块的执行时间 | 堆高了可以把“狙击枪”变成“冲锋枪” |
| **魔力虹吸 (Mana Leech)** | 击杀回复魔力 | 续航流派的核心 |
| **散射控制 (Accuracy)** | 减少弹道随机偏转角 | 修正“多重施法”带来的精准度惩罚 |
| **运算力 (CPU)** | 增加核心的插槽上限 | 决定了你能组多复杂的法术 |
| **元素精通 (Attunement)** | 增加特定元素(火/冰/电/毒)的触发概率和伤害 | 每点 Attunement 使对应元素伤害 +3% 且元素状态触发概率 +2%;详见 `numerical_design.md §1.1` |
### 4.4 怪物成长曲线
* **波次 1-5**: 纯数值验证(血量低,无抗性)。
* **波次 6-10**: 机制验证(出现 **反弹护盾怪**、**高护甲怪**)。这迫使玩家必须构建“穿透”或“元素伤害”来应对。
* **波次 11-20**: 弹幕地狱。怪物的数量呈指数上升,考验玩家构建的 **AOE 覆盖率****FPS 优化**
### 4.5 Boss 设计框架 (Boss Design)
Boss 出现于特定里程碑波次,强制验证玩家构建的适应性。
#### W10 精英 Boss (第 10 波)
* **功能**: 展示某一核心机制的利用方式。
* **典型设计示例**:
* **镜像法师**: 反射所有正面 PHYSICAL 投射物,逼迫玩家必须配备至少一个元素伤害 ACTION 或使用 MODIFIER: pierce。
* **重甲领主**: 自身有层层叠加的护甲,每次受到指定元素伤害时降低一层,验证玩家 **Stacking** 构建的强度。
#### W20 最终 Boss(第 20 波)
* **功能**: 综合检验所有核心玩法的适应性。
* **阶段设计**:
1. **Phase 1** (全血): 高层抗性,对【共鸣效果】或特定元素伤害几乎无抵抗(正常受击)。纯物理构建并非硬性免疫,但每次物理命中触发 Boss 的"虚空再生"(每次物理命中恢复 `regen_on_physical_hit = 0.5%` 最大血量),形成动态 DPS 门槛——高 DPS 物理构建仍可磨血,但效率远不如元素/共鸣流,引导玩家主动考虑构建多样性。
2. **Phase 2** (50% 血量): 开始弹幕攻势,玩家必须在走位的同时输出足够 DPS。
3. **Phase 3** (杀死前): 爆发全屏投射物,考验玩家现有弹道规避构建的极限。
* **设计原则**: Boss 无法被"无脑通关"(一种构建就能轻松将其秒杀);每个阶段应着重考察一个核心机制。
#### Boss 行为数据格式 (Boss Behavior Schema — P5-E1)
Boss 阶段数据完全由 JSON 驱动(`res://resources/bosses/boss_XX.json`),关卡设计师无需改代码即可调整 Boss 行为:
```json
{
"boss_id": "void_lord_w20",
"display_name": "虚空领主",
"wave": 20,
"phases": [
{
"phase": 1,
"hp_threshold_enter": 1.0,
"hp_threshold_exit": 0.5,
"resistances": { "PHYSICAL": 0.8, "FIRE": 0.1, "ICE": 0.1, "LIGHTNING": 0.1 },
"regen_on_physical_hit": 0.005,
"behavior_tags": ["stationary", "spread_projectiles"],
"projectile_pattern": "spread_8"
},
{
"phase": 2,
"hp_threshold_enter": 0.5,
"hp_threshold_exit": 0.1,
"resistances": { "PHYSICAL": 0.3, "FIRE": 0.1, "ICE": 0.1, "LIGHTNING": 0.1 },
"regen_on_physical_hit": 0.0,
"behavior_tags": ["chase_player", "barrage_mode"],
"barrage_interval": 3.0
},
{
"phase": 3,
"hp_threshold_enter": 0.1,
"hp_threshold_exit": 0.0,
"resistances": {},
"behavior_tags": ["enrage", "fullscreen_burst"],
"burst_interval": 5.0
}
],
"ng_plus_phase": {
"phase": 4,
"unlock_condition": "ng_plus",
"hp_threshold_enter": 0.0,
"hp_threshold_exit": -0.3,
"note": "NG+ 专属第四阶段:Boss 在 0% HP 时触发'最后意志',暂时无敌 5 秒,HP 回复至 30%,进入超狂暴状态",
"resistances": { "PHYSICAL": 0.5 },
"regen_on_physical_hit": 0.0,
"behavior_tags": ["last_will", "hyper_enrage", "clone_summon"],
"clone_count": 2,
"invincible_duration": 5.0,
"hp_restore_ratio": 0.3,
"burst_interval": 2.0
}
}
```
> **字段说明**`resistances` 为各元素抗性(0.0 = 无抵抗,1.0 = 完全免疫);`regen_on_physical_hit` 为每次物理命中触发的生命恢复比例(Phase 1 动态门槛的数据实现);`behavior_tags` 驱动 `BossAI` 状态机的行为策略选择。`ng_plus_phase` 字段仅在 NG+ 模式下由 `BossAI` 读取——普通模式下此字段存在但不生效(`unlock_condition` 检查决定是否激活)。Phase 4 的 `hp_threshold_exit: -0.3` 表示 Boss 在"回复后血量"归零后才真正死亡,防止普通模式下意外触发。
---
## 5. 游戏循环 (Gameplay Loop)
1. **准备 (The Lab)**:
* 界面左侧是背包(存放法术卡),右侧是核心(插槽)。
* 玩家将卡牌拖入插槽,点击”测试按钮”,在一个安全的靶场内测试射击效果,还有 DPS 统计面板。
* **防呆机制**:点击”进入战斗”时,系统先对 Deck 进行验证:若全部插槽均为空或仅含 MODIFIER/TRIGGER 而无任何 ACTION 类型法术,展示警告弹窗”法术塔无法开火!需要至少一个行动类法术。”并阻止进入。
* **深度玩法**:调整卡牌顺序,比如把 [伤害增强] 放在 [多重施法] 之前还是之后,效果截然不同。
2. **战斗 (The Arena)**:
* 倒计时 60 秒。
* 玩家自动使用构建好的武器射击。
* 主要操作:走位躲避弹幕,拾取掉落的”法力核心”(钱)。
* **走位-构建互动 (G4)**:部分法术对玩家的移动状态有响应,让走位不只是”跑步”:
* **移动增益型法术**(如”电冲弹”):玩家处于运动状态时该法术伤害 ×1.5,静止时恢复基准值。
* **冲刺波(Dash Wave)型核心**:特定 Core 属性,玩家触发冲刺动作时自动在冲刺轨迹上释放一次附加法术(消耗独立冷却,不走主 Deck 队列)。
* **停步蓄力型法术**(如”引力奇点”):玩家静止超过 1.5 秒时触发蓄力,停止移动后立即释放一次超载版本(DMG ×3);一旦移动则蓄力清零。
* **UI 反馈**:静止后 0.3 秒开始显示蓄力进度环(天空蓝,展示 1.5 秒倒计时);蓄力满时进度环闪烁并播放充能音效;移动就进度环消失。进度环由 `UIManager` 经 EventBus `CHARGE_STATE_CHANGED` 事件驱动,不得在 `SpellEvaluator` 中直接操作 UI 节点。
* *设计意图*:玩家可以通过选择法术类型来主动决定”走位风险收益比”,使操作与构建选择相互影响。
3. **购物 (The Shop)**:
* 战斗结束,进入商店。
* **货架 A**: 出售新的法术卡片(随机池)。
* **货架 B**: 出售新的”核心”(更牛的主板)。
* **货架 C**: 基础属性提升(生命值、暴击率)。
* **出售与转型规则 (G5)**
* 玩家可在商店界面将背包中的法术卡**出售**,回收该卡购入价格的 **50%**(下限 10G)。
* 更换 Core 时,原 Core 不适配的法术卡(插槽类型不匹配)将自动移入**等待区**(上限 5 张);超出 5 张的部分,玩家必须手动选择出售或丢弃。
* 等待区中的卡可在下次商店阶段重新插入新 Core,或继续出售。
* *设计意图*:转型有真实代价——金币折损 + 等待区占位;鼓励玩家谨慎决策而非随意重构。
* *经济策略*:是买一个强力的”黑洞”法术?还是把钱用来升级”回蓝速度”以支撑现有的构建?
## 6. 新手引导设计 (Onboarding)
Roguelite 的"第一把"体验至关重要。系统复杂度必须渐进式解锁,避免一次性呈现所有概念。
### 6.1 渐进式解锁顺序
| 第几局 | 开放内容 | 目的 |
| :--- | :--- | :--- |
| 局 1 | 预设法杖(只有 3 个 Spark Bolt),只教走位 | 零门槛上手,感受割草爽感 |
| 局 2 | 解锁"法杖编辑",仅开放 Tier 1 法术 | 体验最简单的换弹 |
| 局 3 | 引入第一张 Modifier(伤害增强)+ Tooltip 教学 | 理解 Modifier 放在 Action 之前生效 |
| 局 4 | 引入第一张 Trigger(击中触发)+ 靶场演示 | 看到"子母弹"视觉效果 |
| 局 5+ | Tier 2 法术、Logic 类法术逐步开放 | 自由探索 |
### 6.2 情境提示 (Contextual Tips)
* 当玩家第一次持有 Trigger 但没有配套 Action 时,显示提示:"触发器需要在其后面放置动作才能生效 →"。
* 当玩家 `cpu_limit` 占用超过 80% 时,运算力进度条变红并显示:"运算力不足,法杖将无法完整执行"。
* 商店中首次出现特殊 Core(如 `matrix_board`)时,显示简短动画演示邻接加成效果。
### 6.3 靶场 (Training Range)
在**每次**进入商店阶段时,靶场始终可用:
* 右侧显示实时 DPS 数字。
* 点击"慢放"按钮,以 0.1x 速度演示法术链的执行顺序(调试教学用)。
---
## 7. 元进展设计 (Meta-Progression)
元进展是 Roguelite 的长期粘性来源。玩家每次游玩都在为"总体库"做出贡献,即使失败也有收获感。
### 7.1 局外货币:以太碎片 (Aether Fragments)
* **来源**: 每次游玩结束时,按波次数量奖励(W1=0, W5=3, W10=8, W20=20)。
* **永久保留**,不随死亡重置。
### 7.2 局外解锁树 (Unlock Tree)
用以太碎片解锁**永久加入掉落池**的内容,不直接增强玩家数值:
| 花费 | 解锁内容 | 说明 |
| :--- | :--- | :--- |
| 5 碎片 | 法术:连锁闪电 | 加入商店掉落池 |
| 10 碎片 | 核心:构造板 (matrix_board) | 加入商店掉落池 |
| 15 碎片 | 法术:战术核弹 | 加入商店掉落池 |
| 30 碎片 | 核心:记忆法杖 (persistent_memory) | 加入商店掉落池 |
| 50 碎片 | 挑战模式:诅咒波次 | 解锁额外难度模式 |
### 7.3 图鉴系统 (Codex)
* 玩家每次拾取/购买新法术,自动解锁该法术的图鉴词条(含详细数值和设计意图)。
* 图鉴中显示**元素亲和性标签**,暗示可能的共鸣组合(如"此法术具有 💧 水属性亲和")。
* 成就:首次触发每种元素反应,解锁对应的"炼金日志"风味文本。
### 7.4 设计原则
* 解锁内容**只扩展可能性**(让玩家看到更多法术),而非直接增强初始属性(避免 P2W 感)。
* 通过 20 局可解锁全部核心内容,给硬核玩家明确的完成感。
---
## 8. 失败、通关与里程碑 (Failure, Victory & Checkpoints)
### 8.1 死亡与失败机制 (G1)
* **触发条件**:玩家 HP 降至 0 时死亡,当局立即结束。
* **死亡流程**
1. 播放死亡动画与音效。
2. 统计数据冻结(本局最高波次、总输出伤害、最高单次伤害、击杀总数)。
3. 显示 **本局总结屏 (Run Summary)**,含构建快照(自动截图保存至 `user://runs/YYYY-MM-DD_run_N.png`)。
4. 给予以太碎片奖励(按波次阶梯),不因死亡扣减。
* **唯一例外**:天赋"最后意志 (Last Will)"——允许一次以 1 HP 存活,每局限触发一次,触发后天赋失效。
* **死亡不回退**:已解锁法术图鉴词条、成就、Meta 解锁树进度,均在死亡后保留。
### 8.2 通关条件与通关后内容 (G2)
* **通关条件**:击败 W20 终波 Boss(三阶段精英怪),触发通关演出。
* **首通奖励**
* 给予 30 以太碎片(固定)。
* 解锁 **New Game+ (NG+)** 模式:全局敌人属性 ×1.5、弹幕速度 +30%、Boss 新增第四阶段。
* **NG+ 递增**:每次 NG+ 通关再给予 20 碎片(递减 20%,下限 5)。
* **无限模式 (Endless)**:W20 通关后可选择继续,每 5 波难度系数 +10%(无上限),用于挑战竞速排行榜,不计入 Meta 进度。
* **排行榜技术实现(P6-N19**:以**到达波次 (Wave Reached)** 为主排名维度,同波次按**总经过时间 (Elapsed Seconds)** 升序排。
- **本地存储**`user://endless_records.json`Schema`{ "records": [{ "wave": int, "elapsed_sec": float, "build_snapshot": String, "timestamp": int }] }`,保留最近 20 条。
- **在线排行榜(P2 可选)**:上传分数 = `wave * 100000 + (86400 - elapsed_sec)`,单整数实现双维度排序(波次高优先,同波次用剩余秒数打分)。
- **防作弊**:本地记录含 `build_snapshot`Deck checksum),便于审核异常成绩。
* **新机制注入 (G7)**:Endless 模式不只是纯数值膨胀,每 10 波(W30/W40/W50...)随机激活一条**全局词条**(每次 1 条,不与前轮叠加,仅保留最新一条):
| 词条 | 效果 |
| :--- | :--- |
| 镜子世界 | 所有弹道被首次命中后反弹回发射者方向 |
| 怪物分裂 | 敌人死亡时以 50% HP 分裂为 2 个小型版本(仅分裂 1 次)|
| 地图缩小 | 可活动区域半径减少 20%(最小 50% 原始大小)|
| 魔力反转 | 法术 Delay 变为正向加成,原 Delay 越长攻速越快 |
| 精英泛滥 | 所有普通敌人升级为精英,HP ×2、奖励 ×2 |
* 每 20 波(W40/W60/W80...)触发 **Boss Rush 片段**:连续出现 3 个 Boss(随机抽取已出现过的 Boss 变体),全部击败后获得大量碎片与特殊法术卡(每次 Boss Rush 保证至少 1 张 Tier 3 卡)。
### 8.3 里程碑与 Checkpoint (G3)
到达 **W5 / W10 / W15** 触发里程碑事件:
| 里程碑 | 即时奖励 | 解锁内容 |
| :--- | :--- | :--- |
| W5 | 以太碎片 ×2 | 解锁 W5 练习模式起点 |
| W8 | — | **Mini Boss 首现警告波**(非里程碑;仅首次到达时弹出提示,Boss 数据进入图鉴)|
| W10 | 以太碎片 ×3 | 解锁 W10 练习模式起点;W10 精英 Boss 激活 |
| W15 | 以太碎片 ×4 | 解锁 W15 练习模式起点 |
* **里程碑碎片加成**缓解高波次死亡的挫败感(玩家不会"一无所获")。
* **练习模式 (Practice Mode)**:从任意已到达的里程碑波次开始,无限时间商店,不计入 Meta 进度也不消耗里程碑次数;用于测试新构建。
---
## 9. 总结
《魔法工匠》不只是一款“幸存者”游戏,它是一个**带有动作外壳的逻辑解谜游戏**。每一个通过第 20 波的玩家,都是一名合格的“魔法软件工程师”。
+248
View File
@@ -0,0 +1,248 @@
# 数值策划设计案:魔法工匠 (Arcane Artificer)
## 0. 设计理念 (Design Philosophy)
本游戏的数值体系服务于 **“高自由度构建”** 这一核心目标。
数值设计不应教玩家怎么玩,而应提供足够宽广的边界(Boundary)和明确的权衡(Trade-off)。
**核心公式**:
`构建强度 = (基础数值 * 修正系数) ^ 逻辑复杂度`
## 1. 基础资源模型 (Resource Model)
### 1.1 玩家属性 (Player Stats)
这些属性是全局的,会修正所有法杖的输出。
| 属性 ID | 名称 | 基准值 | 软上限 | 硬上限 | 说明 |
| :--- | :--- | :--- | :--- | :--- | :--- |
| `hp_max` | 最大生命 | 100 | 2000 | - | |
| `mana_max` | 最大魔力 | 100 | 1000 | - | 全局蓝条上限,法杖也有自己的上限,取 Min 值 |
| `move_speed` | 移动速度 | 300 | 600 | 800 | 像素/秒 |
| `cast_delay_mod` | 施法延迟修正 | 1.0 (100%) | 0.1 | 0.01 | 越低越快,乘算系数 |
| `recharge_speed_mod` | 充能速度修正 | 1.0 (100%) | 5.0 | - | 越低越快 |
| `luck` | 幸运 | 0 | 100 | - | 影响高阶法术掉率、暴击率 |
| `cpu_limit` | 运算力上限 | 5 | 20 | 50 | `SpellEvaluator` 实际 MAX_OPS = `cpu_limit × 40`(默认 5 → 200 步;软上限 20 → 800 步;硬上限 50 → 2000 步)。升级此属性可执行更复杂的法术链,与 `implementation_plan.md §2.2``MAX_OPS_PER_CPU = 40` 常量对应。 |
| `attunement_fire` | 火焰精通 | 0 | 50 | 100 | 每点:火焰伤害 +3%(乘算),火焰状态(点燃/爆燃)触发概率 +2%。影响 `damage_type=FIRE` 的所有投射物。 |
| `attunement_ice` | 冰霜精通 | 0 | 50 | 100 | 每点:冰霜伤害 +3%,冰冻/减速触发概率 +2%。 |
| `attunement_lightning` | 雷电精通 | 0 | 50 | 100 | 每点:雷电伤害 +3%,麻痹/连锁导电触发概率 +2%。 |
| `attunement_poison` | 毒素精通 | 0 | 50 | 100 | 每点:毒素 DoT +3%,中毒叠层上限 +1(上限 20)。 |
### 1.2 经济系统 (Economy)
* **金币 (Gold)** (代号 `G`)
* **来源**: 击杀怪物 (1-5G),波次结算 (100G + 10%利息)。
* **消耗**: 购买法术 (50-500G),购买法杖 (200-2000G),刷新商店 (20G*)。
* **利息上限**: 10% 利息计算时,单次波次奖励上限为 **100G**(即持有 1000G 以上部分不再产生额外利息)。防止后期经济失控膨胀。
* **膨胀控制**: 商店刷新价格每次增加,波次结束后重置。金币不捡 5 秒后消失,逼迫玩家移动拾取。
- **刷新价格公式(F11 补充,P6-N23)**`刷新费用 = 20 + (本波已刷新次数 × 10)`(单位:G)。
即第 1 次刷新 20G,第 2 次 30G,第 3 次 40G,以此类推;波次结算后重置为 20G。
设计意图:前 1-2 次刷新成本低,鼓励灵活换货;反复刷新代价指数感,抑制"无脑刷新"。
实现:`ShopManager` 维护 `_refresh_count: int`(波次内),`WAVE_COMPLETE` 事件后清零。
* **经验值 (XP)**
* **升级曲线(权威公式 P6-N16**
$$XP\_required(level) = \lfloor 10 \times 1.4^{level-1} \rfloor$$
| 等级 | 所需 XP | 累计 XP |
| :--- | :--- | :--- |
| 1→2 | 10 | 10 |
| 5→6 | 54 | 234 |
| 10→11 | 289 | 1397 |
| 15→16 | 1551 | 7174 |
| 20→21 | 8322 | 39,343 |
- 系数 `1.4` 使早期升级快速(前 5 级约 4 波内完成),后期升级需要持续 3-5 波,节奏符合 Roguelite 期望。
- `ProfileManager` 在关卡加载时预计算前 50 级的阈值表并缓存,避免运行时浮点指数运算。
- **⚠️ F10 可行性验证(P6-N22**Level 21 累计 XP 需求 39,343。20 波 × 60 秒/波 = 1200 秒游戏时长。
设平均每秒击杀 2 只敌人,每只掉 15 XP(Wave 10+ 标准怪),则 1200 × 2 × 15 = 36,000 XP——
**略低于 39,343**,意味着理论上最高只能达到 Level 20 左右(不满 21 级)。
若期望玩家在通关时达到 20 级,需将 `XP_required` 公式中系数从 `1.4` 调整为约 `1.38`
或将波次奖励 XP 提高(每波结算奖励 200 XP)。**当前设计定为 Level 20 通关上限,不强制要求 Level 21**;
Level 21+ 曲线仅服务于 Endless 模式。敌人 XP 掉落参考值:Wave 1 杂鱼 = 5 XPWave 10 精英 = 30 XPBoss = 500 XP。
---
## 2. 法术系统数值 (Spell System Metrics)
### 2.1 核心参数定义
每个法术卡片 (Godot Resource / Json) 包含以下关键数值:
1. **Mana Cost (魔耗)**: 执行此节点消耗的魔力。
* *原则*: 越强力的效果,蓝耗越高,或者有其他负面代价。
2. **Cast Delay (施法后摇)**: 执行此节点后,给法杖增加的“冷却时间”。
* *原则*: 强大的单发法术通常有高延迟(如核弹 +2.0s)。
3. **Recharge Time (充能延迟)**: 这是一个特殊的延迟,只有当完整的一轮(Deck空了)打完正在重置时,才会计算。
* *原则*: 只有极强的终极技能才会增加此值。
### 2.2 基础法术参考表 (Spell Database)
> **⚠️ 注意**Projectile/Action 类型法术必须在 `.tres` 中显式设置 `damage_type`DamageType 枚举值)。
> 未填写时 `ProjectileDef.reset()` 默认 `DamageType.PHYSICAL`,但设计意图应在此表中明示,
> 避免内容制作者遗漏火/雷等元素属性导致构建伤害系统无法正常触发。
#### Tier 1 (新手/平民)
| ID | 名称 | 类型 | Mana | Delay | 伤害类型 | 效果/修正 | 备注 |
| :--- | :--- | :--- | :--- | :--- | :--- | :--- | :--- |
| `spark_bolt` | 魔法飞弹 | Projectile | 5 | +0.05s | PHYSICAL | Dmg: 3, Speed: 600 | 最基础的突突突 |
| `energy_orb` | 能量球 | Projectile | 20 | +0.20s | PHYSICAL | Dmg: 15, Speed: 300 | 慢速高伤 |
| `double_cast` | 双重施法 | Multicast | 2 | +0.00s | — | Draw: 2 | 必备插件 |
| `spread_mod` | 散射修正 | Modifier | 0 | -0.10s | — | Spread: +30°, Speed: +10% | 这一发打不准,但射得快 |
#### Tier 2 (进阶/功能)
| ID | 名称 | 类型 | Mana | Delay | 伤害类型 | 效果/修正 | 备注 |
| :--- | :--- | :--- | :--- | :--- | :--- | :--- | :--- |
| `shotgun_blast` | 霰弹爆破 | Projectile | 25 | +0.40s | PHYSICAL | 发射3枚 4dmg 的弹片 | 自带多重效果 |
| `damage_plus` | 伤害增强 | Modifier | 15 | +0.10s | — | Dmg: +10 | |
| `speed_up` | 加速推进 | Modifier | 5 | -0.05s | — | Speed: +400, Range: +20% | 狙击流必备 |
| `trigger_hit` | 击中触发 | Trigger | 10 | +0.00s | — | Payload Dmg: 0.1x | 子母弹核心 |
#### Tier 3 (史诗/质变)
| ID | 名称 | 类型 | Mana | Delay | 伤害类型 | 效果/修正 | 备注 |
| :--- | :--- | :--- | :--- | :--- | :--- | :--- | :--- |
| `nuke` | 战术核弹 | Projectile | 200 | +3.00s | PHYSICAL | Dmg: 500, Radius: 300 | 屏幕清空,可能会炸死自己 |
| `homing` | 自动追踪 | Modifier | 40 | +0.10s | — | Homing Force: 5.0 | 只有这一个修正就够改变玩法 |
| `heavy_cost` | 鲜血献祭 | Modifier | 0 | -0.50s | — | Dmg: +50, Cost: 10 HP | 卖血流 |
| `chain_bolt` | 连锁闪电 | Projectile | 60 | +0.80s | LIGHTNING | 弹射 5 次 | 清怪神器 |
---
## 3. 敌人与战斗数值 (Combat Metrics)
### 3.1 伤害计算管线
> **⚠️ 权威公式(Authoritative Formula**:所有文档均应引用此处定义,不得出现矛盾版本。
为了支持复杂的 Buff/Debuff,伤害计算必须分步:
1. **Base Damage**: 法术面板值 (e.g. 10)
2. **Add Modifiers**: 累加修正器 (e.g. +5 +5 -> 20)
3. **Mult Modifiers**: 乘法修正器 (e.g. Critical 2.0x, Element Weakness 1.5x)
4. **Flat Reduction**: 敌人护甲 (Armor -2)
5. **Percent Reduction**: 敌人抗性 (Fire Res 50%)
```
Damage = ((Base + Add) × Mult) × (1 - Res) - Armor
```
*(最小伤害为 1*
### 3.2 怪物设计标准
我们设定 **“标准单位时间伤害 (DPS)”** 为基准。
* 玩家 Lvl 1 预计 DPS: 20
* 玩家 Lvl 20 预计 DPS: 5000+
| 怪物等级 | 怪物类型 | HP | 移动速度 | 攻击力 | 特殊词条 |
| :--- | :--- | :--- | :--- | :--- | :--- |
| Wave 1 | 杂鱼土豆 | 15 | 100 | 5 | - |
| Wave 5 | 冲锋甲虫 | 80 | 250 | 12 | 击退抗性 50% |
| Wave 10| 坦克巨兽 | 2000 | 50 | 30 | 护甲 5 (免疫机枪) |
| Wave 20| 虚空领主 | 50000| 150 | 100 | 弹幕反射盾 |
### 3.3 波次强度控制 (Pacing)
游戏设计为 20 波 (Wave),每波 60秒。
* **W1-3**: 爽局。怪物血量 < 玩家单发伤害。
* **W4-7**: 压力局。怪物密度增加,玩家必须拥有至少一个 AOE 手段(穿透/爆炸)。
* **W8**: **首领战 (Mini Boss)**。考验单体 DPS。
* **W9-15**: 资源局。怪物掉落增加,但伴随高护甲怪,考验玩家的“破甲/元素”构建。
* **W20**: **最终 Boss**
---
## 4. 元素反应矩阵 (Elemental Matrix)
为了增加构建深度,引入简单的元素克制/反应。
| 攻击 \ 如果目标状态 | 无 | 潮湿 (Wet) | 油腻 (Oily) | 燃烧 (Burning) | 冰冻 (Frozen) |
| :--- | :--- | :--- | :--- | :--- | :--- |
| **火 (Fire)** | 点燃 | 蒸发 (AOE) | 爆燃 (x3 Dmg) | - | 融化 (解冻) |
| **冰 (Ice)** | 减速 | 冻结 (硬控) | - | 熄灭 | - |
| **雷 (Elec)** | 麻痹 | 连锁导电 (AOE) | - | 过载 (爆炸) | 碎冰 (物理Dmg) |
*数值策划备注:*
* **潮湿**:受到雷电伤害 +100%。
* **油腻**:受到火焰伤害 +200%,且持续时间翻倍。
* **冻结**:无法移动,受到物理撞击伤害 +300% (即碎冰机制)。
---
## 5. 平衡性隐患与对策 (Balancing Risks)
### A. 无限蓝机枪 (Infinite Mana Gun)
* **问题**: 玩家堆积大量 `-Delay``Mana Regen`,导致射速突破帧率限制,且蓝不掉。
* **对策**:
* **最小帧限制**: 设定射击间隔至少为 1 帧 (0.016s)。
* **过热机制 (Overheat)**: 连续射击超过 5秒,法杖进入“过热”状态,强制冷却 2秒 或 增加散布。
* **递增蓝耗**: 连续快速射击时,每发子弹蓝耗增加 1%,停止射击后快速衰减。
### B. 显卡杀手 (GPU Killer)
* **问题**: 玩家构建出 `无限分裂` + `无限粒子`
* **对策**:
* **最大弹道数 (Max Projectiles)**: 全局限制 2000 个弹道。超过时,新的子弹不再生成(或顶掉旧的)。
* **双重限制机制**
* **MAX_OPS**= `cpu_limit × MAX_OPS_PER_CPU`,默认 `5 × 40 = 200`,软上限 `20 × 40 = 800`):`SpellEvaluator` 每帧单次施法最多执行对应条指令(包括 ACTION/MODIFIER/LOGIC 各类节点)。主要防止线性/循环构建死循环。与 `implementation_plan.md §2.2``MAX_OPS_PER_CPU = 40` 常量对应。
* **嵌套深度锁 MAX_TRIGGER_DEPTH = 3**`SpellEvaluator.execute_sub` 调用时传入 `depth` 参数,超过 3 层不再触发新的 SubPayload(即 TRIGGER 内嵌套 TRIGGER 最多 3 层)。**两者分工**:MAX_OPS 管局内指令数量,MAX_TRIGGER_DEPTH 管跨子弹的调用深度。
### C. 站桩输出 (AFK Build)
* **问题**: 比如”自动追踪吸血光环”,玩家站着不动就能通关。
* **对策**:
* **反挂机怪**: 设计一种怪物,会向玩家当前位置投掷”毒圈”,逼迫玩家移动。
* **资源衰减**: 掉落的金币如果不捡,5秒后消失(或变少),逼迫玩家去捡钱。
### D. W20 Boss Phase 1 DPS 门槛(P6-N17
* **问题**: Phase 1 的 `regen_on_physical_hit = 0.005`(每次物理命中恢复 Boss 最大 HP 的 0.5%)。
Boss 最大 HP = 50,000,即每次物理命中回血 **250 HP**
若玩家攻速 10 发/秒(纯物理构建),Boss 每秒净回血 2,500 HP。
* **DPS 突破阈值(最低要求)**:物理构建需满足以下条件才能有效磨血:
$$DPS_{physical} > \frac{250 \times hit\_rate}{1} \Rightarrow DPS > 2500 \text{10发/秒时)}$$
等价条件:每发伤害 > 250(单发狙击流),或总 DPS > 2500(高频流)。
- **设计意图**:并非阻止物理构建,而是要求玩家必须达到中后期构建强度(DPS 5000+)才能高效磨血;低配物理构建可用更长时间磨过,但效率不如元素/共鸣流。
- **数值调整入口**`res://resources/bosses/boss_w20.json` 中的 `regen_on_physical_hit` 字段,平衡迭代时直接修改数据而无需改代码。
### E. `infinite_spells` 与 `heavy_cost` 叠加交互(P6-N18
* **问题**`infinite_spells` 特性(法力耗尽时每发+1 HP 代价)与 `heavy_cost` 修正器(每次施法-10 HP)叠加时,HP 代价是否累加?
* **规则定义**:两者**独立计算、顺序执行**
1. `SpellEvaluator` 先执行 `heavy_cost` MODIFIER(扣 10 HP,归类为”构建设计主动代价”)。
2. **之后**,若 mana 不足,`infinite_spells` 机制额外扣 1 HP(归类为”资源溢出代价”)。
3. 两者同帧触发时,单发最大 HP 代价 = `heavy_cost 值 + 1`(如 10+1=11 HP/发)。
* **UI 提示**:法杖编辑界面在检测到 `infinite_spells Core` + `heavy_cost` 组合时,显示橙色警告”⚠ 双重 HP 消耗”,并提示预估每秒 HP 消耗量。
* **上限保护**:单次施法 HP 代价上限 = `player.hp_max × 0.15`(15% 上限),防止极端组合一发即死。
---
## 6. 波次生成配置格式 (SpawnConfig Schema — E5 权威定义)
波次内容**完全由数据驱动**,关卡设计师仅需编辑 JSON 无需改代码。文件位于 `res://resources/waves/wave_XX.json`
```json
{
"wave": 5,
"duration_sec": 60,
"budget": 200,
"spawn_groups": [
{
"enemy_id": "bug_charger",
"weight": 3,
"count_min": 2,
"count_max": 5,
"spawn_interval": 3.0,
"spawn_radius_from_player": [400, 700]
},
{
"enemy_id": "tank_brute",
"weight": 1,
"count_min": 1,
"count_max": 1,
"spawn_interval": 15.0,
"spawn_radius_from_player": [500, 800]
}
],
"elite_overrides": [
{ "at_sec": 45, "enemy_id": "elite_bug_charger", "count": 1 }
],
"boss": null
}
```
* `budget`:本波总"刷怪点数",每个怪物有对应的 `cost`;实际刷新量 = `floor(budget / cost)`
* `weight`:同时存在多个 group 时,按权重随机选组,而非轮流刷新。
* `elite_overrides`:精确时间点强制刷出精英怪,不消耗 budget。
* `boss`:非 null 时在波末触发 BossSpawn(见 §6 Boss 设计框架)。
+284
View File
@@ -0,0 +1,284 @@
# FTUE / 新手引导系统架构设计
# Tutorial & Onboarding System Architecture
> **范围**:本文档涵盖游戏的首次用户体验(FTUE)、上下文提示、引导状态机,以及
> `TutorialManager` Autoload 的完整设计规范。
> 本文档以 `architecture_design.md` 为基础,所有运行时接口遵循 §6.1 语言分区原则。
---
## 一、设计目标
| 目标 | 度量标准 |
| :--- | :--- |
| 前 3 分钟玩家留存 | 90%+ 玩家完成首波(Wave 1)且尝试第一次施法 |
| 零文字强制阅读 | 教学信息通过上下文提示传达,不弹出强制阅读对话框 |
| 可跳过原则 | 所有引导步骤可在设置中永久关闭,或在进行中跳过 |
| 渐进式复杂度 | W1–W3 仅展示核心移动+射击;W4+ 逐步解锁进阶提示 |
---
## 二、TutorialManager Autoload
### 2.1 职责边界
- **GDScript 层**`tutorial_manager.gd`):状态读写、EventBus 订阅、提示节点显示/隐藏。
- **不含热路径**:提示触发频率极低(每场景级别),全部保留在 GDScript。
```gdscript
# scripts/autoloads/tutorial_manager.gd
extends Node
## 教学步骤枚举(顺序即解锁顺序)
enum TutorialStep {
MOVE = 0, # WASD 移动
CAST = 1, # 首次自动施法(Wand 冷却后自动提示)
PICK_SPELL = 2, # 首次拾取法术卡
OPEN_SHOP = 3, # 首次进入商店
EQUIP_CORE = 4, # 首次装备 Core
KILL_10 = 5, # 击杀 10 个敌人
WAVE_CLEARED = 6, # 通过 Wave 1
RESONANCE_HINT = 7, # 首次触发共鸣(S3 解锁)
COMPLETED = 99, # 引导全部完成
}
## 已完成步骤的持久化集合(存入 ProfileManager
var _completed_steps: PackedInt32Array = PackedInt32Array()
## 当前激活提示步骤(-1 = 无)
var _active_step: int = -1
func _ready() -> void:
# 从 ProfileManager 恢复已完成状态
_completed_steps = ProfileManager.get_int_array(
"tutorial_completed", PackedInt32Array())
# 订阅进度事件
EventBus.subscribe(EventID.PLAYER_SPAWNED, _on_player_spawned)
EventBus.subscribe(EventID.SPELL_CAST, _on_spell_cast)
EventBus.subscribe(EventID.SHOP_OPENED, _on_shop_opened)
EventBus.subscribe(EventID.ENEMY_KILLED, _on_enemy_killed)
EventBus.subscribe(EventID.WAVE_CLEARED, _on_wave_cleared)
EventBus.subscribe(EventID.RESONANCE_TRIGGERED, _on_resonance)
```
### 2.2 状态机定义
```
[初始状态]
MOVE ──────────────── 检测:玩家首帧输入 WASD 后标记完成
CAST ──────────────── 检测:EventID.SPELL_CAST 首次触发
PICK_SPELL ─────────── 检测:拾取 loot 掉落的法术卡
OPEN_SHOP ──────────── 检测:Wave 1 通关后自动弹出商店提示
EQUIP_CORE ─────────── 检测:首次从商店装备任意 Core
KILL_10 ────────────── 检测:ENEMY_KILLED 事件累计 10 次
WAVE_CLEARED ───────── 检测:WAVE_CLEAREDwave_num=1
RESONANCE_HINT ─────── 检测:RESONANCE_TRIGGEREDS3 起激活)
COMPLETED ──────────── 持久化到 ProfileManager,停止所有提示
```
- **前向跳跃**:若玩家自行完成某步(无提示出现),步骤自动标记完成,无需显示提示。
- **后向保护**:已完成步骤不会因新 Run 而重置(存 ProfileManager 而非 RunData)。
### 2.3 核心接口
```gdscript
# 查询某步骤是否已完成
func is_completed(step: TutorialStep) -> bool:
return step in _completed_steps
# 手动标记完成(商店系统 / 法杖编辑器调用)
func mark_complete(step: TutorialStep) -> void:
if is_completed(step): return
_completed_steps.append(int(step))
ProfileManager.set_int_array("tutorial_completed", _completed_steps)
_hide_hint()
EventBus.emit(EventID.TUTORIAL_STEP_COMPLETED, step)
# 显示上下文提示(锚定到 UI 节点旁)
func show_hint(step: TutorialStep, anchor: Control,
offset: Vector2 = Vector2.ZERO) -> void:
if is_completed(step): return
_active_step = step
# 委托给 UIManager 渲染气泡提示
UIManager.show_tutorial_hint(
TutorialHintData.build(step), anchor, offset)
func _hide_hint() -> void:
if _active_step == -1: return
UIManager.hide_tutorial_hint()
_active_step = -1
```
---
## 三、上下文提示系统(Contextual Hints
### 3.1 提示数据结构
```gdscript
# resources/tutorial/tutorial_hint_data.gd
class_name TutorialHintData
extends Resource
@export var step: int # TutorialStep 枚举值
@export var icon_key: String # 对应 UIAtlas 中的图标键(如 "wasd_icon"
@export var text_key: String # i18n 键(tr(text_key) 使用)
@export var arrow_dir: int # 0=上, 1=右, 2=下, 3=左(箭头指向目标)
@export var duration: float = 0.0 # 0 = 手动关闭,>0 = 自动消失秒数
static func build(step: TutorialManager.TutorialStep) -> TutorialHintData:
return _HINT_TABLE[step]
# 提示内容表(运行时常量,避免 JSON 热加载)
const _HINT_TABLE: Dictionary = {
0: preload("res://resources/tutorial/hint_move.tres"),
1: preload("res://resources/tutorial/hint_cast.tres"),
2: preload("res://resources/tutorial/hint_pick_spell.tres"),
3: preload("res://resources/tutorial/hint_shop.tres"),
4: preload("res://resources/tutorial/hint_equip_core.tres"),
5: preload("res://resources/tutorial/hint_kill10.tres"),
6: preload("res://resources/tutorial/hint_wave_cleared.tres"),
7: preload("res://resources/tutorial/hint_resonance.tres"),
}
```
### 3.2 提示触发时机
| 步骤 | 触发时机 | 自动消失 | 跳过方式 |
| :--- | :--- | :--- | :--- |
| MOVE | 游戏开始后 1s(玩家还未移动) | 首次移动后 | 任意移动 |
| CAST | 法杖冷却恢复后 2s(玩家未施法) | 首次施法后 | 施法 |
| PICK_SPELL | 首个法术 loot 落地后 3s | 拾取后 | 走到 loot 上 |
| OPEN_SHOP | Wave 1 通关时自动进入商店流程 | 商店打开后 | 自动 |
| EQUIP_CORE | 商店内有 Core 可购买时 | 购买后 | 购买 |
| KILL_10 | 击杀计数达 5 时预显提示 | 计数达 10 | 击杀 |
| WAVE_CLEARED | Wave 1 通关瞬间 | 5s 后 | 点击 |
| RESONANCE_HINT | 首次编辑法杖时检测邻接 | 触发共鸣后 | 触发共鸣 |
### 3.3 提示 UI 节点结构
```
CombatScene
└── UILayer (CanvasLayer z_index=100)
└── TutorialHintContainer # UIManager 管理的专用节点
├── HintBubble (Panel)
│ ├── Icon (TextureRect)
│ └── Label (text = tr(hint.text_key))
└── Arrow (TextureRect, 根据 arrow_dir 旋转)
```
- `TutorialHintContainer``anchor` 节点世界坐标定位(`force_update_transform` 每帧同步)。
- 气泡使用 `AnimationPlayer` 播放 0.2s 淡入动画,消失时 0.15s 淡出。
- 同一时刻最多显示 1 个提示(显示新提示时先隐藏旧提示)。
---
## 四、首 3 分钟玩家流(First 3 Minutes Flow
```
T=0s 游戏载入 → CombatScene 初始化
T=1s [提示] WASD 移动(气泡锚定玩家位置右侧,arrow→左)
T=~5s 玩家完成移动 → MOVE 标记完成 → 提示消失
T=~8s 法杖冷却结束 → [提示] 观察法术已自动施发(施法为自动)
T=~15s 首个敌人被击杀 → loot 掉落 → [提示] 拾取法术卡
T=~30s 玩家拾取法术卡 → PICK_SPELL 标记完成
T=~60s Wave 1 清场 → 自动进入商店
T=商店 [提示] 尝试装备 Core(若商店有 Corearrow→Core 格)
T=~90s 购买/关闭商店 → Wave 2 开始
T=~120s 已掌握基本循环,FTUE 完成度 ~70%
T=Wave 4+ 触发共鸣时 → 最终提示显示
```
---
## 五、关键 EventID 扩展
> 以下 EventID 需追加到 `event_id.gd`(当前最大 ID = 17):
| ID | 常量名 | 负载 | 触发方 | 监听方 |
| :--- | :--- | :--- | :--- | :--- |
| 18 | `TUTORIAL_STEP_COMPLETED` | `step: int` | TutorialManager | UIManager(显示解锁提示)|
| 19 | `SHOP_OPENED` | — | ShopManager | TutorialManager |
| 20 | `RESONANCE_TRIGGERED` | `recipe_id: String` | SpellEvaluator | TutorialManager, VFXManager |
| 21 | `ACHIEVEMENT_UNLOCKED` | `achievement_id: String` | AchievementManager | Steam 插件层 |
> **注意**ID 0 为 `CRASH_DETECTED`CrashReporter 专用,已定义)。
---
## 六、可跳过 & 关闭机制
```gdscript
# settings_manager.gd 新增项
const KEY_TUTORIAL_DISABLED: String = "tutorial_disabled"
# TutorialManager._ready() 中检查
func _ready() -> void:
if ProfileManager.get_bool(SettingsManager.KEY_TUTORIAL_DISABLED, false):
# 将所有步骤标记为已完成,彻底关闭引导
for s in TutorialStep.values():
_completed_steps.append(s)
return
# 正常初始化...
```
设置菜单提供「关闭新手提示」开关,写入 `ProfileManager`(持久化跨 Run)。
**重置入口**`SettingsManager.reset_tutorial()` → 清空 `tutorial_completed` 并重置标志,
下次启动重新触发完整 FTUE 流程(适用于练习账号 / QA 测试)。
---
## 七、文件清单
```
scripts/autoloads/
tutorial_manager.gd # 核心 Autoload(本文档 §2 实现)
resources/tutorial/
hint_move.tres # TutorialHintDataMOVE 步骤)
hint_cast.tres
hint_pick_spell.tres
hint_shop.tres
hint_equip_core.tres
hint_kill10.tres
hint_wave_cleared.tres
hint_resonance.tres
scenes/ui/
tutorial_hint_bubble.tscn # HintBubble + Arrow 节点树
```
**Autoload 注册顺序**`project.godot`):
> `TutorialManager` 在 `EventBus` 和 `ProfileManager` 之后注册,
> 确保 `_ready()` 时可正常读取持久化数据并订阅事件。
---
## 八、与其他系统的接口约定
| 系统 | 接口 | 说明 |
| :--- | :--- | :--- |
| `UIManager` | `show_tutorial_hint(data, anchor, offset)` / `hide_tutorial_hint()` | 渲染和动画委托给 UIManager |
| `ProfileManager` | `get_int_array / set_int_array("tutorial_completed", ...)` | 跨 Run 持久化已完成步骤 |
| `EventBus` | 订阅 ID 1/6/11/18~21 等 | 只读订阅,不 emit 游戏逻辑事件 |
| `SpellEvaluator` | 无直接依赖 | 共鸣触发通过 `RESONANCE_TRIGGERED` 事件解耦 |
| `SettingsManager` | `KEY_TUTORIAL_DISABLED` 常量 | 关闭引导的持久化开关 |