# 弟子管理模块设计 模块位置:P1 核心循环(见 `doc/Roadmap.md`)。本文档描述弟子数据模型与弟子管理器的设计。 ## 1. 结构:数据类 + 管理器 弟子模块拆分为两层: | 层 | 文件 | 类型 | 职责 | | --- | --- | --- | --- | | 弟子数据类 | `src/Data/Models/Disciple.gd` | RefCounted | 持有弟子的所有信息,纯数据,无逻辑 | | 弟子管理器 | `src/Character/DiscipleManager.gd` | Node | 添加、删除、查询、修改弟子属性 + 领域操作(修炼结算) | ## 2. 设计模式 | 角色 | 模式 | 说明 | | --- | --- | --- | | 弟子数据类 | 纯数据模型 | RefCounted,运行时可变数据,不依赖场景树,可脱离场景单测 | | 弟子管理器 | 仓储模式(Repository) | CRUD + 查询的集中入口,是弟子数据的唯一管理者 | | 变化通知 | 观察者模式(Observer) | 管理器发信号,UI/其他系统订阅实时刷新 | | 弟子创建 | 工厂方法(Factory) | 初始弟子/收徒的创建逻辑收在管理器内 | **明确不用的方案**: - 不用 ECS:弟子属性固定(境界/技艺/忠诚…),无动态组件需求,是过度设计。 - 不用 Resource/.tres:弟子是运行时可变数据,RefCounted 类最轻量,不走序列化资源。 - 不把所有逻辑塞进数据类:领域操作归管理器,保证修改只有单一入口,信号好通知,测试好做。 ## 3. Disciple 数据类(RefCounted,纯数据) ```gdscript var name: String var is_mortal: bool # 凡人弟子 var realm_level: int = 1 # 境界 1-9 var cultivation_skill: int = 1 # 种植技艺 1-10 var alchemy_skill: int = 1 # 炼丹技艺 1-10 var loyalty: int = 50 # 忠诚 0-100(P1 仅存字段) var cultivation_exp: int = 0 # 修为进度 ``` 只读派生(不存储,由属性计算): - `monthly_cost() -> int`:每月修炼灵石消耗(凡人弟子为 0) - `realm_name() -> String`:境界显示名(凡人 / 炼气X层…) - `exp_to_next() -> int`:升到下一境界所需修为 ## 4. DiscipleManager 管理器(Node,仓储) ```gdscript signal disciple_added(disciple) signal disciple_removed(disciple) signal disciple_attr_changed(disciple, attr_name, old_value, new_value) func add_disciple(d) -> void func remove_disciple(d) -> void func get_disciple(id) -> Disciple func get_all() -> Array[Disciple] func get_by_realm(level) -> Array[Disciple] func count_mortals() -> int func set_attr(d, attr, value) -> void # 修改唯一入口,内部发信号 func settle_month() -> void # 修炼结算(领域操作) func to_dict() -> Dictionary # 存档网关 func load_from_dict(d) -> void ``` ## 5. 关键设计决策 1. **修改只走管理器**——`set_attr()` 是唯一变更入口,内部统一发 `disciple_attr_changed`。UI 订阅它实时刷新,也为以后忠诚度/事件系统留监听点。 2. **查询返回引用**——性能好(P1 规模无碍),但禁止外部直接写属性;要写必须走管理器。 3. **管理器是 Node,数据类是 RefCounted**——管理器可挂 MainGame/测试场景;数据类脱离场景树也能单测。 4. **领域操作在管理器**——`settle_month()`(修炼消耗灵石、增加修为、境界提升)归管理器,数据类不碰业务逻辑。 5. **存档网关(Unit of Work)**——序列化/恢复只经过管理器 `to_dict()/load_from_dict()`,为 SaveSystem 注册预留。 ## 6. 测试方式 独立测试场景 `src/Character/test_disciple.tscn`: 1. 实例化 DiscipleManager(不依赖其他模块) 2. `_ready()` 中自动执行用例: - 添加/删除弟子 → 信号是否正确发出 - 查询(get_by_realm / count_mortals) - `set_attr` → 信号带旧值/新值 - `settle_month` → 境界提升/灵石消耗正确 - `to_dict / load_from_dict` 往返一致 3. `print` 输出 PASS/FAIL,编辑器直接运行场景验证 # 弟子属性 ## 属性列表 |名称|范围|说明| |:---|:---|:---| |metal|[0, 100]|金属性值| |wood|[0, 100]|木属性值| |water|[0, 100]|水属性值| |fire|[0, 100]|火属性值| |earth|[0, 100]|土属性值| |efficiency|[1, 100]|灵气吸收效率,决定修炼速度,隐藏属性| |realm_level|[0, 5]|大境界,凡人、练气、筑基、金丹、元婴、化神| |sub_realm_level|[1, 13]|凡人只有一个小境界;练气13个小境界,其他各有前、中、后、大圆满四个小境界| |cultivation_exp|[0, 2147483647]|修炼经验| |farming_skill_exp|[0, 2147483647]|耕作技能经验| |farming_skill_level|[0, 10]|炼丹技能等级| |herb_farming_skill_exp|[0, 2147483647]|药草耕作技能经验| |herb_farming_skill_level|[0, 10]|炼丹技能等级| |alchemy_skill_exp|[0, 2147483647]|炼丹技能经验| |alchemy_skill_level|[0, 10]|炼丹技能等级| |loyalty|[0, 100]|忠诚度| - **修炼经验说明**:每个小境界都有一个经验上限,跨越小境界,溢出部分会继承到下一个小境界。跨越大境界,溢出部分清零。 - **技能经验说明**:耕作技能经验、药草耕作技能经验、炼丹技能经验,每跨越一个等级,溢出的经验会继承到下一个等级,直到升满为止,不会有任何经验提升。每个等级所需的经验为需要在文件中配置。 ## 初始分配方式 ```mermaid graph TD A[分配属性总量,按正正态分布] --> B[随机分配5种属性,属性之和为1] --> C[分配灵气利用率] ``` ## 修炼说明 每个小境界修炼需要的经验 = 基础经验值 * 属性总量 经验获取量 = 基础经验 * 灵气吸收效率