diff --git a/assets/sprites/ui/BG1.png b/assets/sprites/ui/BG1.png deleted file mode 100644 index dda27bb..0000000 Binary files a/assets/sprites/ui/BG1.png and /dev/null differ diff --git a/assets/sprites/ui/图片大门.png b/assets/sprites/ui/图片大门.png new file mode 100644 index 0000000..760ba94 Binary files /dev/null and b/assets/sprites/ui/图片大门.png differ diff --git a/assets/sprites/ui/BG1.png.import b/assets/sprites/ui/图片大门.png.import similarity index 73% rename from assets/sprites/ui/BG1.png.import rename to assets/sprites/ui/图片大门.png.import index a0a01f5..26da856 100644 --- a/assets/sprites/ui/BG1.png.import +++ b/assets/sprites/ui/图片大门.png.import @@ -2,16 +2,16 @@ importer="texture" type="CompressedTexture2D" -uid="uid://caeqoc6wx8toy" -path="res://.godot/imported/BG1.png-6bf4b9c36da6bbdf71bd2c8203f4cb53.ctex" +uid="uid://c6bthhmao5fyh" +path="res://.godot/imported/图片大门.png-03ea66f8e1eb82ba7952374321f00064.ctex" metadata={ "vram_texture": false } [deps] -source_file="res://assets/sprites/ui/BG1.png" -dest_files=["res://.godot/imported/BG1.png-6bf4b9c36da6bbdf71bd2c8203f4cb53.ctex"] +source_file="res://assets/sprites/ui/图片大门.png" +dest_files=["res://.godot/imported/图片大门.png-03ea66f8e1eb82ba7952374321f00064.ctex"] [params] diff --git a/assets/sprites/ui/宗门全景.png b/assets/sprites/ui/宗门全景.png new file mode 100644 index 0000000..bed3a0e Binary files /dev/null and b/assets/sprites/ui/宗门全景.png differ diff --git a/assets/sprites/ui/宗门全景.png.import b/assets/sprites/ui/宗门全景.png.import new file mode 100644 index 0000000..06335a1 --- /dev/null +++ b/assets/sprites/ui/宗门全景.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://cewfc3vetgbfx" +path="res://.godot/imported/宗门全景.png-d969df84bf06c400df6e36c494864bbe.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://assets/sprites/ui/宗门全景.png" +dest_files=["res://.godot/imported/宗门全景.png-d969df84bf06c400df6e36c494864bbe.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/doc/design/修炼系统设计.md b/doc/design/修炼系统设计.md new file mode 100644 index 0000000..3b418d1 --- /dev/null +++ b/doc/design/修炼系统设计.md @@ -0,0 +1,146 @@ +# 修炼系统设计(D5:修炼循环) + +> 对应开发计划表 D5(8/7):① 修为随时间增长(资质×灵气)② 境界门槛 ③ 突破判定。 +> 交付标准:弟子自动修炼,可突破。本文档只描述设计,不包含代码。 +> 状态:设计稿,待审阅通过后实现 + +--- + +## 一、核心循环 + +``` +每日推进 → 修为增长(资质×天灵根×灵气浓度)→ 达到层门槛 → 升层/突破判定 → 境界推进 +``` + +- 纯挂机机制:弟子自动修炼,玩家无需操作 +- 修炼只在游戏时间推进时进行(TimeSystem.day_passed),暂停/弹窗期间冻结 + +--- + +## 二、修炼速度 + +**每日修为增量 = 基础速度 × 资质倍率 × 灵根数量倍率 × 天灵根加成 × 灵气浓度** + +| 因子 | 来源 | 说明 | +|------|------|------| +| 基础速度 | 配置 `base_speed`(默认 10) | 修为/游戏日 | +| 资质倍率 | 配置 `aptitude_speed`([0.6, 1.0, 1.5, 2.0]) | 按下品/中品/上品/天骄索引 | +| 灵根数量倍率 | 配置 `root_count_speed`([1.0, 0.8, 0.6]) | 按根数索引(1根/2根/3根),根数越少修炼越快 | +| 天灵根加成 | 配置 `heavenly_bonus`(1.5) | 仅在 is_heavenly 时乘入 | +| 灵气浓度 | 配置 `qi_concentration`(默认 1.0) | D5 固定值;D6 建筑系统接入后动态化 | + +> 所有修炼数值统一从 GameConfig 读取(Disciple 不内置倍率常量),避免双份数值漂移。 + +### 含义 + +- **资质定天赋上限**:决定倍率主体(0.6~2.0) +- **灵根数量定纯度**:根数越少越纯净(1根 ×1.0、2根 ×0.8、3根 ×0.6) +- **天灵根是稀缺加成**:强制单根 + 额外 ×1.5,三个维度独立掷骰 + +组合示例(天骄资质 + 三灵根 = 2.0 × 0.6 = 1.2,慢于 上品资质 + 单灵根 = 1.5 × 1.0 = 1.5) + +修为存储用 float(允许小数积累),显示时取整。 + +--- + +## 三、境界门槛(本期:练气 1–13 层) + +| 层 | 需求修为(配置 `level_thresholds`) | 是否瓶颈 | +|----|----------------------------------|---------| +| 1→2 | 100 | 否 | +| 2→3 | 200 | 否 | +| 3→4 | 350 | **是** | +| 4→5 | 550 | 否 | +| 5→6 | 800 | 否 | +| 6→7 | 1100 | **是** | +| 7→8 | 1500 | 否 | +| 8→9 | 2000 | 否 | +| 9→10 | 2600 | **是** | +| 10→11 | 3300 | 否 | +| 11→12 | 4100 | 否 | +| 12→13 | 5000 | **是** | + +- **非瓶颈层**:修为满门槛 → 自动升层(无失败) +- **瓶颈层**(3→4 / 6→7 / 9→10 / 12→13):修为满后进入突破判定 +- 13 层圆满:修为封顶,显示"练气圆满";筑基以上大境界需丹药/灵石资源,**本期不做**(后续 P2 接入) + +> 数值均为示例,正式数值在 D9 平衡调整,只改配置不动代码。 + +--- + +## 四、突破判定(瓶颈层) + +1. 修为 ≥ 瓶颈层门槛时,每日结算后自动尝试突破 +2. 掷骰:成功概率 = 配置 `breakthrough_chance`(默认 0.8) +3. **成功** → 升层,控制台打印"X 突破至 练气4层" +4. **失败** → 修为扣减 20%(冲关损耗),次日再尝试;控制台打印失败信息 +5. 失败不连续重试(每天最多判定一次) + +**设计意图**:挂机友好(全自动),但瓶颈制造节奏感与"卡关→等积累"的经营反馈;损耗惩罚避免无脑挂到顶。 + +--- + +## 五、CultivationManager(新 Autoload) + +存放位置:`src/Character/CultivationManager.gd`(与 DiscipleManager 同域,注册顺序在 DiscipleManager 之后、声明于 project.godot) + +| 接口 | 说明 | +|------|------| +| 监听 `TimeSystem.day_passed` | 每日结算全体弟子 | +| 私有 `_gain_exp(d)` | 按公式累计修为 | +| 私有 `_check_breakthrough(d)` | 门槛判定 + 瓶颈掷骰 | +| 信号 `realm_changed(d)` | 境界变化(详情面板刷新用) | +| 信号 `cultivation_log(text)` | 修炼日志文本(控制台打印/后续日志系统) | + +**注意**:只在 `disciples` 名录内结算,候选池不修炼。 + +--- + +## 六、配置节(game_config.cfg 新增 [cultivation]) + +```ini +[cultivation] +base_speed=10 ; 基础修为/日 +qi_concentration=1.0 ; 宗门灵气浓度(D6 动态化) +aptitude_speed=[0.6, 1.0, 1.5, 2.0] ; 资质倍率(下品/中品/上品/天骄) +root_count_speed=[1.0, 0.8, 0.6] ; 灵根数量倍率(1根/2根/3根) +heavenly_bonus=1.5 ; 天灵根额外倍率 +breakthrough_chance=0.8 ; 瓶颈突破成功率(待接入) +level_thresholds=[100, 200, 350, 550, 800, 1100, 1500, 2000, 2600, 3300, 4100, 5000] ; 待接入 +bottleneck_levels=[4, 7, 10, 13] ; 待接入 +``` + +- `aptitude_speed`:与资质枚举一一对应,必须 4 项 +- `level_thresholds`:练气 1→2 到 12→13 共 12 个门槛值 +- `bottleneck_levels`:升到这些层时需要突破判定 + +--- + +## 七、UI 接入(D4 预留区填充) + +- **详情弹窗**:信息区追加"修为:1234 / 2000"(当前/本层门槛;13 层显示 练气圆满) +- **名录列表**:境界列实时变化(realm_changed 后自动刷新,现有 disciples_changed 不触发,需详情面板监听 realm_changed) +- 修炼日志:控制台 print(本期不做日志界面) + +--- + +## 八、实现拆解(小步可验证) + +1. 配置节 [cultivation] + GameConfig 读取 +2. Disciple 加 `cultivation_exp: float` 字段 + 门槛查询辅助 +3. CultivationManager 骨架:每日结算修为 +4. 非瓶颈自动升层 +5. 瓶颈突破判定(成功/失败/损耗) +6. 详情面板修为显示 + realm_changed 刷新 +7. 挂机验证(加速时间观察多轮突破)+ 提交 + +--- + +## 九、验收清单(D5 交付标准) + +- [ ] 挂机时弟子修为逐日增长,速度随资质/天灵根不同(下品慢、天骄快) +- [ ] 非瓶颈层修为满自动升层,无失败 +- [ ] 瓶颈层(4/7/10/13 层)需突破判定,控制台有成败日志,失败扣 20% 修为 +- [ ] 练气 13 层圆满后修为封顶,显示"练气圆满" +- [ ] 详情面板显示修为进度,突破后实时刷新 +- [ ] 修改配置(速度/概率/门槛)重启生效 diff --git a/doc/design/弟子系统设计.md b/doc/design/弟子系统设计.md index e2f9d8d..f4c22af 100644 --- a/doc/design/弟子系统设计.md +++ b/doc/design/弟子系统设计.md @@ -32,7 +32,7 @@ | 2 | 上品 | 1.5x | 天赋尚可 | | 3 | 天骄 | 2.0x | 万中无一 | -> 倍率由 D5 修炼循环使用,此处只定义不消费。 +> 倍率由 D5 修炼循环使用,数值存于 GameConfig(配置 `aptitude_speed`),此处仅为参考展示。 ### 枚举二:灵根五行(SpiritRoot) @@ -44,12 +44,13 @@ | 项目 | 设定 | |------|------| -| 生成概率 | 3%(远低于四类资质中"天骄"的 5%,天灵根可叠加于任意资质之上) | +| 出现规则 | **仅天骄资质**(Aptitude.HEAVENLY)中再掷一次,非天骄弟子不可能有天灵根 | +| 天骄内概率 | 配置 `heavenly_root_chance`(默认 0.5 = 50%);总体概率 = 天骄概率 × 天骄内概率(普通难度 5% × 50% ≈ 2.5%) | | 灵根构成 | 仅一系灵根(五行之一),纯净单灵根 | -| 修炼速度 | 在资质倍率基础上**额外 +50%**(乘法叠加,D5 修炼循环消费) | +| 修炼速度 | 在资质倍率基础上**额外 +50%**(乘法叠加,数值存于 GameConfig `heavenly_bonus`,D5 修炼循环消费) | | 地位 | 宗门争抢的种子弟子,日后可挂靠特殊培养事件(W8 预留) | -> 天灵根与资质是两个独立维度:资质定天赋底子,天灵根是稀缺加成,二者随机独立。 +> 天灵根与资质是**嵌套关系**:先掷资质,天骄之后再判天灵根。天灵根 = 天骄 + 单灵根 + 1.5x 加成,是最稀缺组合。 ### 境界字段说明 @@ -84,7 +85,7 @@ ### 3. 灵根 - 普通弟子:数量 1~3 根均匀随机,元素从五行中随机抽取、互不重复 -- 天灵根:3% 概率判定为天灵根,灵根数量固定 1 根,修炼速度额外 +50% +- 天灵根:仅天骄资质再判(概率 `heavenly_root_chance`),灵根数量固定 1 根,修炼速度额外 +50% - 含义:单灵根修炼最快,三灵根最慢(对应修仙世界观) ### 4. 资质(入宗分流) @@ -119,7 +120,7 @@ ### 招募流程(文字时序) 1. 开局(MainGame 调用 `start_new_game()`)或倒计时归零(`_on_day_passed`) -2. 生成候选弟子(先判天灵根 3% → 生成灵根 → 按权重掷资质 → 组名字),进入候选池 +2. 生成候选弟子(按权重掷资质 → 天骄再判天灵根 → 生成灵根 → 组名字),进入候选池 3. `applicants_generated(is_opening)` 信号发出 → 弹窗显示候选列表 4. 玩家勾选(开局模式:确定按钮在选满 3 名前禁用) 5. 点"确定" → `finish_recruitment(selected)` 结算 → 恢复时间 @@ -162,6 +163,7 @@ opening_applicant_count=5 ; 开局候选人数 opening_pick_count=3 ; 开局必须选满数 recruit_interval_years=0.1 ; 定期招募间隔(游戏年) recruit_applicant_count=3 ; 定期弹窗候选人数 +heavenly_root_chance=0.5 ; 天灵根概率:仅天骄资质中再掷,50% [recruitment_normal] ; 普通难度资质权重(下品/中品/上品/天骄) aptitude_weights=[45, 35, 15, 5] diff --git a/resources/game_config.cfg b/resources/game_config.cfg index 2fe7993..70e8377 100644 --- a/resources/game_config.cfg +++ b/resources/game_config.cfg @@ -8,10 +8,19 @@ opening_applicant_count=5 opening_pick_count=3 recruit_interval_years=1 recruit_applicant_count=3 +heavenly_root_chance=0.5 ; 天灵根概率:仅天骄资质中再掷,50% -; 各难度资质权重(下品/中品/上品/天骄) +; 各难度资质权重表(下品/中品/上品/天骄,权重和=100 即百分比概率) +; 当前难度由 [recruitment] difficulty 指定 [recruitment_normal] -aptitude_weights=[45, 35, 15, 5] +aptitude_weights=[45, 35, 15, 5] ; 45% / 35% / 15% / 5% [recruitment_hard] -aptitude_weights=[60, 30, 8, 2] +aptitude_weights=[60, 30, 8, 2] ; 60% / 30% / 8% / 2% + +[cultivation] ; 修炼系统 +base_speed=10 ; 基础修为/日 +qi_concentration=1.0 ; 宗门灵气浓度 +aptitude_speed=[0.6, 1.0, 1.5, 2.0] ; 资质倍率(下品/中品/上品/天骄) +root_count_speed=[1.0, 0.8, 0.6] ; 灵根数量倍率(1根/2根/3根) +heavenly_bonus=1.5 ; 天灵根额外修炼速度倍率 diff --git a/src/Character/Disciple.gd b/src/Character/Disciple.gd index 69f56c7..2a10509 100644 --- a/src/Character/Disciple.gd +++ b/src/Character/Disciple.gd @@ -9,12 +9,12 @@ 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 用) + +# —— 修炼数值一律读 GameConfig(资质倍率/天灵根加成/速度等)—— # —— 实例字段 —— var id: int = 0 # 唯一编号(DiscipleManager 分配) @@ -22,7 +22,7 @@ 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 is_heavenly: bool = false # 天灵根标记(仅天骄资质,概率见配置) var join_year: int = 0 # 入宗年份(0 = 未入宗/候选),accept 时快照 var join_month: int = 0 # 入宗月份 var join_day: int = 0 # 入宗日 @@ -39,6 +39,12 @@ func get_roots_text() -> String: names.append(ROOT_NAMES[root]) return "、".join(names) +## 灵根数量修炼倍率(根数越少越快,D5 修炼循环消费)。 +## 数值来自 GameConfig.root_count_speed,按根数-1 索引,越界钳制。 +func get_root_count_speed() -> float: + var idx := clampi(roots.size() - 1, 0, GameConfig.root_count_speed.size() - 1) + return GameConfig.root_count_speed[idx] + func get_realm_text() -> String: if realm_index == 0: return "%s%d层" % [REALM_NAMES[realm_index], sub_realm_index + 1] diff --git a/src/Character/DiscipleManager.gd b/src/Character/DiscipleManager.gd index 5332a7d..143f731 100644 --- a/src/Character/DiscipleManager.gd +++ b/src/Character/DiscipleManager.gd @@ -14,9 +14,6 @@ var disciples: Array[Disciple] = [] ## 候选弟子池(未入宗,等待玩家选择)。 var applicants: Array[Disciple] = [] -## 天灵根生成概率(3%)。 -const HEAVENLY_CHANCE := 0.03 - ## 姓氏池。 const SURNAMES: Array[String] = [ "林", "苏", "叶", "萧", "顾", "沈", "楚", "白", @@ -118,15 +115,17 @@ func _get_interval_days() -> int: var days_per_year := TimeSystem.MONTHS_PER_YEAR * TimeSystem.DAYS_PER_MONTH return maxi(1, roundi(GameConfig.recruit_interval_years * days_per_year)) -## 创建一名候选弟子:随机生成属性(姓名/年龄/灵根/资质), +## 创建一名候选弟子:随机生成属性(姓名/年龄/资质/天灵根/灵根)。 +## 天灵根规则:仅天骄资质中再掷一次(概率来自 GameConfig.heavenly_root_chance)。 ## 不分配 ID——ID 在入宗时由 accept_applicant 分配,保证连续且无浪费。 func _create_applicant() -> Disciple: var d := Disciple.new() 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() + d.is_heavenly = d.aptitude == Disciple.Aptitude.HEAVENLY \ + and randf() < GameConfig.heavenly_root_chance + d.roots = _generate_roots(d.is_heavenly) return d ## 随机生成姓名:单姓 + 1~2 个名用字。 diff --git a/src/Core/GameConfig.gd b/src/Core/GameConfig.gd index a899a66..45b4992 100644 --- a/src/Core/GameConfig.gd +++ b/src/Core/GameConfig.gd @@ -17,6 +17,20 @@ var opening_pick_count := 3 var recruit_interval_years := 0.1 ## 定期弹窗候选人数。 var recruit_applicant_count := 3 +## 天灵根概率(仅天骄资质中再掷一次)。 +var heavenly_root_chance := 0.5 + +# —— 修炼系统(对应 cfg 的 [cultivation] 节)—— +## 基础修炼速度(修为/游戏日)。 +var cultivation_base_speed := 10.0 +## 宗门灵气浓度(修炼速度系数,D6 建筑系统接入后动态化)。 +var qi_concentration := 1.0 +## 资质修炼倍率(按下品/中品/上品/天骄索引)。 +var aptitude_speed: Array[float] = [0.6, 1.0, 1.5, 2.0] +## 灵根数量修炼倍率(按根数-1 索引:1根/2根/3根)。 +var root_count_speed: Array[float] = [1.0, 0.8, 0.6] +## 天灵根额外修炼速度倍率。 +var heavenly_bonus := 1.5 ## 各难度资质权重表(键 = 难度档位,值 = 下品/中品/上品/天骄 权重)。 var aptitude_weights_by_difficulty: Dictionary = { @@ -40,6 +54,23 @@ func _load_config() -> void: opening_pick_count = cfg.get_value("recruitment", "opening_pick_count", opening_pick_count) recruit_interval_years = cfg.get_value("recruitment", "recruit_interval_years", recruit_interval_years) recruit_applicant_count = cfg.get_value("recruitment", "recruit_applicant_count", recruit_applicant_count) + heavenly_root_chance = cfg.get_value("recruitment", "heavenly_root_chance", heavenly_root_chance) + # 修炼系统 + cultivation_base_speed = cfg.get_value("cultivation", "base_speed", cultivation_base_speed) + qi_concentration = cfg.get_value("cultivation", "qi_concentration", qi_concentration) + heavenly_bonus = cfg.get_value("cultivation", "heavenly_bonus", heavenly_bonus) + var apt_raw: Variant = cfg.get_value("cultivation", "aptitude_speed", []) + if apt_raw is Array and apt_raw.size() == aptitude_speed.size(): + var arr: Array[float] = [] + for v in apt_raw: + arr.append(float(v)) + aptitude_speed = arr + var root_raw: Variant = cfg.get_value("cultivation", "root_count_speed", []) + if root_raw is Array and root_raw.size() == root_count_speed.size(): + var arr2: Array[float] = [] + for v in root_raw: + arr2.append(float(v)) + root_count_speed = arr2 # 逐档读取难度权重(INI 数组字面量,如 [45, 35, 15, 5]) for key in aptitude_weights_by_difficulty.keys(): var raw: Variant = cfg.get_value("recruitment_" + key, "aptitude_weights", []) diff --git a/src/UI/Panels/MainMenu/MainMenu.tscn b/src/UI/Panels/MainMenu/MainMenu.tscn index dcd3e6d..373fc24 100644 --- a/src/UI/Panels/MainMenu/MainMenu.tscn +++ b/src/UI/Panels/MainMenu/MainMenu.tscn @@ -1,7 +1,7 @@ [gd_scene format=3 uid="uid://d1odycoaqhhyg"] [ext_resource type="Script" uid="uid://drfyh36my6gj2" path="res://src/UI/Panels/MainMenu/MainMenu.gd" id="1_cwvg5"] -[ext_resource type="Texture2D" uid="uid://caeqoc6wx8toy" path="res://assets/sprites/ui/BG1.png" id="1_hrti0"] +[ext_resource type="Texture2D" uid="uid://c6bthhmao5fyh" path="res://assets/sprites/ui/图片大门.png" id="2_q7typ"] [ext_resource type="FontFile" uid="uid://drt448qncx27p" path="res://assets/fonts/dingliezhuhaifont-20240831GengXinBan)-2.ttf" id="3_q7typ"] [ext_resource type="PackedScene" uid="uid://dj7b5vssydifa" path="res://src/UI/Panels/SavePanel.tscn" id="3_wrf65"] @@ -21,7 +21,9 @@ anchor_right = 1.0 anchor_bottom = 1.0 grow_horizontal = 2 grow_vertical = 2 -texture = ExtResource("1_hrti0") +expand_mode = 1 +stretch_mode = 6 +texture = ExtResource("2_q7typ") [node name="Logo" type="TextureRect" parent="." unique_id=1485724026] layout_mode = 1