From df52a0b22fbd3f7b56c3c49b997bfafc8011d425 Mon Sep 17 00:00:00 2001 From: chen Date: Wed, 5 Aug 2026 23:50:30 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E5=BC=9F=E5=AD=90=E5=AE=9A=E6=97=B6?= =?UTF-8?q?=E6=8B=9B=E5=8B=9F+=E5=85=A5=E5=AE=97=E5=88=86=E6=B5=81?= =?UTF-8?q?=EF=BC=8C=E5=AE=8C=E6=88=90=20D3=EF=BC=88Disciple/DiscipleManag?= =?UTF-8?q?er=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- doc/design/弟子系统设计.md | 166 +++++++++++++++++++++++++++ project.godot | 2 + src/Character/Disciple.gd | 42 +++++++ src/Character/Disciple.gd.uid | 1 + src/Character/DiscipleManager.gd | 116 +++++++++++++++++++ src/Character/DiscipleManager.gd.uid | 1 + src/Core/MainGame.gd | 2 + 7 files changed, 330 insertions(+) create mode 100644 doc/design/弟子系统设计.md create mode 100644 src/Character/Disciple.gd create mode 100644 src/Character/Disciple.gd.uid create mode 100644 src/Character/DiscipleManager.gd create mode 100644 src/Character/DiscipleManager.gd.uid diff --git a/doc/design/弟子系统设计.md b/doc/design/弟子系统设计.md new file mode 100644 index 0000000..c0ac3b5 --- /dev/null +++ b/doc/design/弟子系统设计.md @@ -0,0 +1,166 @@ +# 弟子系统设计(D3:生成 + 入宗分流) + +> 对应开发计划表 D3(8/5)。本文档只描述设计,不包含代码。 +> 状态:设计稿,待审阅通过后实现 + +--- + +## 一、数据模型(Disciple) + +弟子用一条数据记录表示,不挂在场景树上。 + +| 字段 | 类型 | 说明 | +|------|------|------| +| `id` | int | 唯一编号,由 DiscipleManager 自增分配 | +| `name` | String | 随机生成的姓名 | +| `age` | int | 入宗年龄(12~18) | +| `aptitude` | 枚举 Aptitude | 资质(入宗分流结果) | +| `roots` | 枚举数组 SpiritRoot | 灵根列表(普通弟子 1~3 根,互不重复) | +| `is_heavenly` | bool | 天灵根标记(稀有灵根,见枚举二) | +| `realm_index` | int | 大境界索引(0=练气),D5 修炼系统推进 | +| `sub_realm_index` | int | 小境界索引(练气层数-1),D5 修炼系统推进 | + +### 枚举一:资质四类(Aptitude) + +| 值 | 名称 | 修炼速度倍率 | 说明 | +|----|------|-------------|------| +| 0 | 下品 | 0.6x | 根基平庸 | +| 1 | 中品 | 1.0x | 资质平平 | +| 2 | 上品 | 1.5x | 天赋尚可 | +| 3 | 天骄 | 2.0x | 万中无一 | + +> 倍率由 D5 修炼循环使用,此处只定义不消费。 + +### 枚举二:灵根五行(SpiritRoot) + +金、木、水、火、土 五种。灵根数量影响修炼速度(根数越少越快),元素种类影响日后生产效率匹配(D6 建筑/职能系统使用),此处只生成不消费。 + +### 特殊灵根:天灵根 + +稀有灵根,独立于五行枚举之外,用 `is_heavenly` 标记: + +| 项目 | 设定 | +|------|------| +| 生成概率 | 3%(远低于四类资质中"天骄"的 5%,天灵根可叠加于任意资质之上) | +| 灵根构成 | 仅一系灵根(五行之一),纯净单灵根 | +| 修炼速度 | 在资质倍率基础上**额外 +50%**(乘法叠加,D5 修炼循环消费) | +| 地位 | 宗门争抢的种子弟子,日后可挂靠特殊培养事件(W8 预留) | + +> 天灵根与资质是两个独立维度:资质定天赋底子,天灵根是稀缺加成,二者随机独立。 + +### 境界字段说明 + +新弟子统一为练气一层(realm_index=0, sub_realm_index=0)。境界显示文本规则: + +- 练气期:练气 N 层 +- 筑基及以上:大境界 + 初/中/后/圆满 + +境界的成长、突破门槛、寿元计算全部留给 D5 修炼循环,D3 只负责初始状态。 + +--- + +## 二、生成规则(随机性) + +### 1. 姓名 + +- 姓氏池:24 个常用修仙风单姓(林、苏、叶、萧、顾、沈、楚、白……) +- 名用字池:24 个(清、玄、墨、尘、风、澜、霜、珏……) +- 规则:随机 1 个姓 + 1~2 个字(50% 概率双字名) +- 不保证唯一(同名弟子可共存,以 id 区分) + +### 2. 年龄 + +12~18 岁均匀随机。寿元消耗、大限判断留给 D5。 + +### 3. 灵根 + +- 普通弟子:数量 1~3 根均匀随机,元素从五行中随机抽取、互不重复 +- 天灵根:3% 概率判定为天灵根,灵根数量固定 1 根,修炼速度额外 +50% +- 含义:单灵根修炼最快,三灵根最慢(对应修仙世界观) + +### 4. 资质(入宗分流) + +按权重表掷骰: + +| 资质 | 权重 | 概率 | +|------|------|------| +| 下品 | 45 | 45% | +| 中品 | 35 | 35% | +| 上品 | 15 | 15% | +| 天骄 | 5 | 5% | + +> 概率说明:开发阶段固定此分布,后期根据游戏难度测试调整权重即可,不改代码结构。 + +--- + +## 三、入宗分流 + +"入宗分流"的含义:**按资质将弟子分为四类**,这是生成时的一次性判定,不是持续运行的机制。四类资质弟子日后可能享受不同的培养路线(D4+ 扩展),本期只完成分类本身。 + +### 招募机制:定时自动招募 + +招募**不是玩家点击按钮随时进行**,而是按游戏时间自动进行: + +- 间隔:每 **30 游戏日**(1 个月)自动招募一名弟子 +- 驱动:监听 `TimeSystem.day_passed` 信号倒计时,满 30 天触发一次 +- 期间玩家无需任何操作,挂机即可收弟子(与挂机修炼节奏一致) +- 间隔常量可调(后期按宗门声望/规模动态调整,本期固定) + +### 招募流程(文字时序) + +1. DiscipleManager 倒计时归零(每 30 游戏日) +2. 生成一名弟子(先判天灵根 3% → 生成灵根 → 按权重掷资质 → 组名字) +3. 弟子加入宗门名录 +4. 名录变化信号发出(供 UI 后续刷新,D4 列表 UI 消费) +5. 控制台打印该弟子完整信息(姓名/年龄/资质/灵根/境界)—— 本期验收方式 + +--- + +## 四、DiscipleManager 接口 + +| 接口 | 签名 | 说明 | +|------|------|------| +| `recruit()` | 返回新弟子 | 生成 + 分流 + 入名录,发信号(内部由定时器调用,也允许调试手动调用) | +| `remove_disciple(d)` | 入参弟子 | 移出名录,发信号(逐出/陨落) | +| `reset()` | 无 | 清空名录、ID 归零、招募倒计时复位(新游戏开局) | +| 信号 `disciples_changed` | 无参数 | 名录增删/重置时发出 | + +内部私有方法(不对外):生成姓名、生成灵根(含天灵根判定)、掷资质。 + +内部定时机制: + +- 字段 `_days_until_recruit`:距下次招募的剩余游戏天数,初始 30 +- 监听 `TimeSystem.day_passed`,每日减 1,归零 → 招募一名 → 重置为 30 + +存放位置:`src/Character/`(与数据模型同目录),注册为 Autoload 单例,供全局访问。 + +--- + +## 五、UI 接入点 + +- 本期**不加任何按钮**:招募完全自动,顶栏无需改动 +- 弟子信息通过控制台打印展示(验收方式) +- 名录的可视化列表属于 D4(弟子列表 UI),届时由 `disciples_changed` 信号驱动 + +--- + +## 六、与后续任务衔接 + +| 后续任务 | 衔接方式 | +|----------|----------| +| D4 弟子列表 UI | 监听 `disciples_changed` 信号 + 遍历 `disciples` 数组 | +| D5 修炼循环 | 消费 `aptitude` 倍率与 `realm_index/sub_realm_index` 字段 | +| D6 建筑/职能系统 | 消费 `roots` 元素与生产效率匹配 | +| D8 存档接入 | Disciple 数据模型为纯数据,可直接序列化 | + +--- + +## 七、验收清单(D3 交付标准) + +- [ ] 项目能正常启动,无脚本报错 +- [ ] 进入游戏后**无需操作**,每 30 游戏日(挂机约 15 秒 @1x)自动招募一名弟子 +- [ ] 控制台每次招募打印弟子完整信息(姓名/年龄/资质/灵根/境界) +- [ ] 连续观察多个游戏月,每次生成不同弟子,名录持续增长 +- [ ] 资质四类均可能出现(多次招募后抽样观察分布) +- [ ] 多次招募后能观察到天灵根弟子(约 3% 概率,控制台有标记) +- [ ] 返回主菜单再进入,名录已清空(reset 生效) diff --git a/project.godot b/project.godot index 9f1a31a..3e06c1b 100644 --- a/project.godot +++ b/project.godot @@ -20,6 +20,8 @@ GameState="*res://src/Core/GameState.gd" TimeSystem="*res://src/Core/TimeSystem.gd" Settings="*res://src/Core/Settings.gd" SectManager="*res://src/Core/SectManager.gd" +DiscipleManager="*res://src/Character/DiscipleManager.gd" +DiscipleManager="*res://src/Character/DiscipleManager.gd" [display] diff --git a/src/Character/Disciple.gd b/src/Character/Disciple.gd new file mode 100644 index 0000000..d2b9cdb --- /dev/null +++ b/src/Character/Disciple.gd @@ -0,0 +1,42 @@ +## 弟子数据模型(纯数据,不挂在场景树上)。 +## 生成时决定的属性(姓名/资质/灵根)不可变,成长属性(境界)由 D5 修炼系统推进。 +class_name Disciple # 全局类名:全项目可直接用 Disciple.new() +extends Resource # Resource = 轻量纯数据对象,可序列化(D8 存档用) + +## 资质四类(入宗分流结果)。 +enum Aptitude { ORDINARY, MEDIUM, SUPERIOR, HEAVENLY } # 下品/中品/上品/天骄 + +## 灵根五行。 +enum SpiritRoot { GOLD, WOOD, WATER, FIRE, EARTH } + +# —— 常量表:枚举值 → 显示文本 / 倍率(按索引一一对应)—— +const APTITUDE_NAMES: Array[String] = ["下品", "中品", "上品", "天骄"] +const APTITUDE_SPEED: Array[float] = [0.6, 1.0, 1.5, 2.0] # D5 用 +const ROOT_NAMES: Array[String] = ["金", "木", "水", "火", "土"] +const REALM_NAMES: Array[String] = ["练气", "筑基", "金丹", "元婴", "化神"] # 按 realm_index 索引 +const HEAVENLY_BONUS := 1.5 # 天灵根额外修炼速度倍率(D5 用) + +# —— 实例字段 —— +var id: int = 0 # 唯一编号(DiscipleManager 分配) +var name: String = "" +var age: int = 0 +var aptitude: Aptitude = Aptitude.ORDINARY +var roots: Array[SpiritRoot] = [] # 普通弟子 1~3 根;天灵根固定 1 根 +var is_heavenly: bool = false # 天灵根标记(3% 概率) +var realm_index: int = 0 # 大境界索引(0=练气) +var sub_realm_index: int = 0 # 小境界索引(练气层数-1) + +# —— 辅助函数:把数据翻译成给人看的文本 —— +func get_aptitude_name() -> String: + return APTITUDE_NAMES[aptitude] + +func get_roots_text() -> String: + var names: Array[String] = [] + for root in roots: + names.append(ROOT_NAMES[root]) + return "、".join(names) + +func get_realm_text() -> String: + if realm_index == 0: + return "%s%d层" % [REALM_NAMES[realm_index], sub_realm_index + 1] + return "%s%s" % [REALM_NAMES[realm_index], ["初期", "中期", "后期", "圆满"][sub_realm_index]] diff --git a/src/Character/Disciple.gd.uid b/src/Character/Disciple.gd.uid new file mode 100644 index 0000000..0c238b9 --- /dev/null +++ b/src/Character/Disciple.gd.uid @@ -0,0 +1 @@ +uid://cuybveud5w308 diff --git a/src/Character/DiscipleManager.gd b/src/Character/DiscipleManager.gd new file mode 100644 index 0000000..a5c307c --- /dev/null +++ b/src/Character/DiscipleManager.gd @@ -0,0 +1,116 @@ +## 弟子管理器(Autoload 单例)。 +## 负责弟子定时招募、入宗分流与宗门名录维护。 +extends Node + +## 名录变化时发出(增删/重置)。 +signal disciples_changed + +## 宗门弟子名录。 +var disciples: Array[Disciple] = [] + +## 招募间隔(游戏天数,每满间隔自动招募一名弟子)。 +const RECRUIT_INTERVAL_DAYS := 30 + +## 入宗分流权重(按资质四类):下品 / 中品 / 上品 / 天骄。 +const APTITUDE_WEIGHTS: Array[int] = [45, 35, 15, 5] + +## 天灵根生成概率(3%)。 +const HEAVENLY_CHANCE := 0.03 + +## 姓氏池。 +const SURNAMES: Array[String] = [ + "林", "苏", "叶", "萧", "顾", "沈", "楚", "白", + "陆", "云", "柳", "秦", "江", "韩", "洛", "慕", + "夜", "凤", "凌", "姜", "谢", "赵", "魏", "许", +] + +## 名用字池(修仙风)。 +const GIVEN_CHARS: Array[String] = [ + "清", "玄", "墨", "尘", "风", "澜", "霜", "珏", + "澈", "衍", "离", "微", "昭", "明", "月", "宁", + "舒", "歌", "翎", "屿", "珩", "璃", "幽", "衡", +] + +## 距下次招募的剩余游戏天数。 +var _days_until_recruit := RECRUIT_INTERVAL_DAYS +## 下一位弟子的 ID(自增,重置归零)。 +var _next_id := 1 + + +func _ready() -> void: + # 挂到时间系统上:每日推进时倒计时,满间隔自动招募 + TimeSystem.day_passed.connect(_on_day_passed) + +## 每日推进:招募倒计时减 1,归零时自动招募一名弟子。 +func _on_day_passed(_year: int, _month: int, _day: int) -> void: + _days_until_recruit -= 1 + if _days_until_recruit <= 0: + _days_until_recruit = RECRUIT_INTERVAL_DAYS + recruit() + +## 招募一名弟子:随机生成属性、按资质分流后加入名录,返回该弟子。 +func recruit() -> Disciple: + var d := Disciple.new() + d.id = _next_id + _next_id += 1 + d.name = _generate_name() + d.age = randi_range(12, 18) + d.is_heavenly = randf() < HEAVENLY_CHANCE + d.roots = _generate_roots(d.is_heavenly) + d.aptitude = _roll_aptitude() + disciples.append(d) + disciples_changed.emit() + print("弟子入宗:%s %d岁 | %s资质%s | %s灵根 | %s" % [ + d.name, d.age, d.get_aptitude_name(), + "·天灵根" if d.is_heavenly else "", + d.get_roots_text(), d.get_realm_text(), + ]) + return d + +## 移除弟子(逐出/陨落)。 +## @param d: 要移除的弟子 +func remove_disciple(d: Disciple) -> void: + disciples.erase(d) + disciples_changed.emit() + +## 清空名录(新游戏开局调用),招募倒计时复位。 +func reset() -> void: + disciples.clear() + _next_id = 1 + _days_until_recruit = RECRUIT_INTERVAL_DAYS + disciples_changed.emit() + + +## 随机生成姓名:单姓 + 1~2 个名用字。 +func _generate_name() -> String: + var surname := SURNAMES[randi() % SURNAMES.size()] + var given := GIVEN_CHARS[randi() % GIVEN_CHARS.size()] + if randf() < 0.5: + given += GIVEN_CHARS[randi() % GIVEN_CHARS.size()] + return surname + given + +## 随机生成灵根。 +## 天灵根:固定 1 根五行;普通弟子:1~3 根互不重复(根数越少修炼越快)。 +## @param heavenly: 是否天灵根 +func _generate_roots(heavenly: bool) -> Array[Disciple.SpiritRoot]: + if heavenly: + return [randi() % Disciple.SpiritRoot.size() as Disciple.SpiritRoot] + var count := randi_range(1, 3) + var pool := Disciple.SpiritRoot.values() + pool.shuffle() + var result: Array[Disciple.SpiritRoot] = [] + for i in count: + result.append(pool[i]) + return result + +## 按权重掷资质(入宗分流)。 +func _roll_aptitude() -> Disciple.Aptitude: + var total := 0 + for w in APTITUDE_WEIGHTS: + total += w + var roll := randi() % total + for i in APTITUDE_WEIGHTS.size(): + if roll < APTITUDE_WEIGHTS[i]: + return i as Disciple.Aptitude + roll -= APTITUDE_WEIGHTS[i] + return Disciple.Aptitude.ORDINARY diff --git a/src/Character/DiscipleManager.gd.uid b/src/Character/DiscipleManager.gd.uid new file mode 100644 index 0000000..dbaccd4 --- /dev/null +++ b/src/Character/DiscipleManager.gd.uid @@ -0,0 +1 @@ +uid://d2aj0gbvb6p4w diff --git a/src/Core/MainGame.gd b/src/Core/MainGame.gd index 28cb79e..e096563 100644 --- a/src/Core/MainGame.gd +++ b/src/Core/MainGame.gd @@ -7,6 +7,8 @@ func _ready() -> void: add_to_group("game_scene") # 应用用户设置中的默认流速 TimeSystem.apply_default_speed() + # 新游戏清空弟子名录与招募倒计时(读档流程接入后由存档还原) + DiscipleManager.reset() ## 顶部 HUD 的返回主菜单请求。 func _on_top_bar_hud_sign_return_mainmenu() -> void: