Compare commits
10
Commits
main
...
190c9dab9e
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
190c9dab9e | ||
|
|
bea9300d2b | ||
|
|
830aaa5a03 | ||
|
|
dd461f6f88 | ||
|
|
38319d6478 | ||
|
|
e1ed233f20 | ||
|
|
266a5cc7a9 | ||
|
|
18a69f1a17 | ||
|
|
c240d7322e | ||
|
|
70a2f0cfaa |
@@ -1,3 +1,4 @@
|
||||
# Godot 4+ specific ignores
|
||||
.godot/
|
||||
/android/
|
||||
/.vscode/
|
||||
|
||||
Vendored
-3
@@ -1,3 +0,0 @@
|
||||
{
|
||||
"godotTools.editorPath.godot4": "d:\\game\\Godot\\Godot_v4.7.1-stable_win64.exe\\Godot_v4.7.1-stable_win64_console.exe"
|
||||
}
|
||||
+141
@@ -0,0 +1,141 @@
|
||||
# P1 模块拆分
|
||||
|
||||
P1 目标:种田 → 交货 → 灵石 → 修炼 → 更强的种田 循环跑通。本文档定义 P1 的模块划分、职责、依赖、通信方式与测试方式。详见 `doc/Roadmap.md`、`doc/弟子管理.md`。
|
||||
|
||||
## 1. 架构原则:前后端分离
|
||||
|
||||
- **后端**:Manager 脚本(持有数据和逻辑),不依赖 UI。
|
||||
- **前端**:UI 场景(只管显示),不持有任何游戏状态,所有数据从后端读取。
|
||||
- **通信规则**:
|
||||
- UI → 后端:调用公开方法(如 `refine_pill()`)
|
||||
- 后端 → UI:信号推送状态变化(如 `balance_changed`)
|
||||
- **好处**:后端可脱离 UI 单独测试;UI 可随意更换;存档只序列化 Manager 状态。
|
||||
- **不抽象过度**:Manager 脚本本身就是后端,Godot 信号就是通信机制,不引入额外 Service/Interface 层。
|
||||
|
||||
## 2. 文件结构总览
|
||||
|
||||
```
|
||||
src/
|
||||
├── Core/ # 已有,不改
|
||||
│ ├── GameState.gd
|
||||
│ ├── TimeSystem.gd
|
||||
│ ├── SaveSystem.gd
|
||||
│ └── MainGame.gd # 挂载所有 P1 管理器
|
||||
│
|
||||
├── Data/
|
||||
│ └── Models/ # 数据模型(纯 RefCounted,无逻辑)
|
||||
│ ├── Disciple.gd # 弟子数据(见 弟子管理.md)
|
||||
│ ├── FieldData.gd # 灵田数据
|
||||
│ ├── PillRecipeData.gd # 丹方数据
|
||||
│ └── OrderData.gd # 订单数据
|
||||
│
|
||||
├── Character/
|
||||
│ ├── DiscipleManager.gd # 弟子管理器
|
||||
│ └── test_disciple.tscn # 独立测试场景
|
||||
│
|
||||
├── Production/
|
||||
│ ├── FieldManager.gd # 灵田管理器
|
||||
│ ├── AlchemyManager.gd # 炼丹管理器
|
||||
│ ├── test_field.tscn # 灵田测试
|
||||
│ └── test_alchemy.tscn # 炼丹测试
|
||||
│
|
||||
├── Sect/
|
||||
│ ├── InventoryManager.gd # 库存管理(灵药/丹药/凡粮)
|
||||
│ ├── EconomyManager.gd # 经济系统(灵石收支)
|
||||
│ ├── OrderManager.gd # 订单管理
|
||||
│ ├── FoodManager.gd # 口粮管理
|
||||
│ ├── test_economy.tscn # 经济测试
|
||||
│ ├── test_order.tscn # 订单测试
|
||||
│ └── test_food.tscn # 口粮测试
|
||||
│
|
||||
└── UI/
|
||||
└── HUD/
|
||||
└── MainSceneHud.gd # 已有,P1 加灵石显示(前端接入)
|
||||
```
|
||||
|
||||
## 3. 模块职责与依赖
|
||||
|
||||
| 模块 | 文件 | 职责 | 依赖 | 信号 |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| 弟子数据 | `Data/Models/Disciple.gd` | 弟子属性(境界/技艺/忠诚/修为) | 无 | — |
|
||||
| 灵田数据 | `Data/Models/FieldData.gd` | 内外田、作物、生长周期、品质 | 无 | — |
|
||||
| 丹方数据 | `Data/Models/PillRecipeData.gd` | 材料→成品、技艺要求、品质系数 | 无 | — |
|
||||
| 订单数据 | `Data/Models/OrderData.gd` | 客户、物品、数量、品质要求、期限、奖励 | 无 | — |
|
||||
| 弟子管理 | `Character/DiscipleManager.gd` | CRUD + 修炼结算 | Disciple | `disciple_added/removed/attr_changed` |
|
||||
| 灵田管理 | `Production/FieldManager.gd` | 种植/生长/收获 → 库存 | FieldData, InventoryManager | `field_harvested` |
|
||||
| 炼丹管理 | `Production/AlchemyManager.gd` | 消耗材料产出丹药 | PillRecipeData, InventoryManager | `pill_refined` |
|
||||
| 库存管理 | `Sect/InventoryManager.gd` | 物品存取(灵药/丹药/凡粮) | 无 | `inventory_changed` |
|
||||
| 口粮管理 | `Sect/FoodManager.gd` | 凡人弟子口粮消耗 | DiscipleManager, InventoryManager | `food_shortage` |
|
||||
| 订单管理 | `Sect/OrderManager.gd` | 订单生成/交货/过期 | OrderData, InventoryManager, EconomyManager | `order_fulfilled/expired` |
|
||||
| 经济系统 | `Sect/EconomyManager.gd` | 灵石收支、余额 | 无 | `balance_changed` |
|
||||
|
||||
## 4. 信号流(核心循环)
|
||||
|
||||
```
|
||||
TimeSystem.advance_month()
|
||||
→ month_passed
|
||||
→ FieldManager.settle_month() [灵田生长/收获入库存]
|
||||
→ DiscipleManager.settle_month() [修炼消耗灵石、修为增长]
|
||||
→ FoodManager.settle_month() [口粮消耗,凡人弟子数×口粮]
|
||||
→ OrderManager.settle_month() [订单过期检查]
|
||||
→ EconomyManager.settle_month() [香火供奉等固定收入]
|
||||
|
||||
玩家操作:
|
||||
FieldManager.plant_field() → field_harvested (收获灵药) → InventoryManager
|
||||
AlchemyManager.refine() → pill_refined (炼丹成功) → InventoryManager
|
||||
OrderManager.fulfill() → order_fulfilled (交货) → EconomyManager 加灵石
|
||||
```
|
||||
|
||||
- 各管理器暴露 `settle_month()`,由 MainGame 连接 `month_passed` 按顺序调用;管理器自身不依赖 TimeSystem autoload,测试时可直接调用。
|
||||
|
||||
## 5. 依赖注入方式
|
||||
|
||||
管理器不直接引用 autoload,通过 `setup()` 注入依赖:
|
||||
|
||||
```gdscript
|
||||
# MainGame._ready() 中:
|
||||
economy_manager.setup()
|
||||
field_manager.setup(inventory_manager)
|
||||
alchemy_manager.setup(inventory_manager)
|
||||
food_manager.setup(disciple_manager, inventory_manager)
|
||||
order_manager.setup(inventory_manager, economy_manager)
|
||||
```
|
||||
|
||||
好处:测试场景只需注入 Mock 依赖,模块可独立运行。
|
||||
|
||||
## 6. 独立测试方式
|
||||
|
||||
每个模块对应一个测试场景(`test_xxx.tscn`):
|
||||
|
||||
1. 实例化被测管理器 + 注入 Mock 依赖
|
||||
2. `_ready()` 中自动执行用例
|
||||
3. 用例覆盖:正常流程、边界条件(余额不足/库存不足/过期)、信号是否正确发出
|
||||
4. `print` 输出 PASS/FAIL,编辑器直接 F6 运行该场景验证
|
||||
|
||||
## 7. 开发顺序(依赖链自底向上)
|
||||
|
||||
| 步骤 | 模块 | 理由 |
|
||||
| --- | --- | --- |
|
||||
| 1 | 4 个数据模型(Disciple/FieldData/PillRecipeData/OrderData) | 无依赖,纯数据 |
|
||||
| 2 | InventoryManager | 无依赖,被 3 个模块使用 |
|
||||
| 3 | DiscipleManager + 测试 | 只依赖数据 |
|
||||
| 4 | FieldManager + 测试 | 依赖库存 |
|
||||
| 5 | AlchemyManager + 测试 | 依赖库存 |
|
||||
| 6 | FoodManager + 测试 | 依赖弟子+库存 |
|
||||
| 7 | OrderManager + 测试 | 依赖库存+经济 |
|
||||
| 8 | EconomyManager + 测试 | 无依赖(独立收支) |
|
||||
| 9 | MainGame 集成所有管理器 | 组装 |
|
||||
| 10 | HUD 灵石显示(前端接入) | UI 层 |
|
||||
|
||||
每完成一步都能单独测试验证,不会出现"全做完才能跑"的情况。
|
||||
|
||||
## 8. P1 简化决策
|
||||
|
||||
| 完整愿景 | P1 取舍 | 后续 |
|
||||
| --- | --- | --- |
|
||||
| 忠诚度系统 | 仅存字段,不参与结算 | P2 启用 |
|
||||
| 收徒 | 不做,初始弟子固定 | P2 |
|
||||
| 随机委托 | 不做 | P2 |
|
||||
| 香火供奉 | 简化为每月固定小额收入 | P2 完整化 |
|
||||
| 灵田品质 | 收获时按种植者技艺定品质,无随机 | 可加随机 |
|
||||
| 炼丹失败 | 材料足够即成功,品质随技艺浮动 | 可加失败率 |
|
||||
+100
@@ -0,0 +1,100 @@
|
||||
# 开发路线图(Roadmap)
|
||||
|
||||
愿景与世界观见 `doc/WorldDesign.md`。本文档把愿景拆分为里程碑,开发时只看当前里程碑,不做全量实现。
|
||||
|
||||
## 1. 开发原则
|
||||
|
||||
1. **分层交付**:每个里程碑独立可玩、可测试,P1 完成即验证核心乐趣。
|
||||
2. **先简后繁**:复杂系统先做能跑通的最小版本,验证后再加深。
|
||||
3. **战斗自动结算**:经营游戏中战斗是结果不是过程,P4/P5 冲突用抽象战力对比自动结算,不做回合制战斗。
|
||||
4. **宗主裁决用规则表**:价值/规矩/规模/面子四变量打分,不做复杂 AI。
|
||||
5. **天灾复用现有系统**:本质是事件链 + 数值压力,复用委托/订单/忠诚系统实现。
|
||||
6. **数值先纸面后调参**:前期用表格跑平衡,UI 只做展示。
|
||||
|
||||
## 2. 里程碑总览
|
||||
|
||||
| 里程碑 | 内容 | 对应 WorldDesign 章节 | 工作量 | 状态 |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| P0 框架 | 时间系统、存档系统、主菜单/HUD | — | — | 已完成 |
|
||||
| P1 核心循环 | 弟子 + 灵田 + 炼丹 + 灵石收支 + 基础订单 + 口粮 | 1、4、7 | ~35% | 待开发 |
|
||||
| P2 经营深度 | 忠诚/抽成、收徒、随机委托、香火 | 4、5、6 | ~15% | 规划中 |
|
||||
| P3 外交层 | 大客户名单、订单刷新、附庸三级、宗主裁决 | 3 | ~15% | 规划中 |
|
||||
| P4 秘境事件 | 勘探、发掘、走漏、冲突升级链 | 8 | ~20% | 规划中 |
|
||||
| P5 终局 | 毕业考、天灾事件链、结局结算 | 9、10 | ~15% | 规划中 |
|
||||
|
||||
## 3. P0 框架(已完成)
|
||||
|
||||
- 时间系统(TimeSystem):月推进回合、turn_started / month_passed / year_passed / season_changed 信号
|
||||
- 存档系统(SaveSystem):两级存档 + register_saver 数据注册
|
||||
- 主菜单、HUD、设置
|
||||
|
||||
## 4. P1 核心循环(下一步)
|
||||
|
||||
目标:种田 → 交货 → 灵石 → 修炼 → 更强的种田 循环跑通。
|
||||
|
||||
| 模块 | 内容 | 优先级 |
|
||||
| --- | --- | --- |
|
||||
| 弟子 | 基础属性:境界、种植技艺、炼丹技艺;修炼消耗灵石提升境界 | 必须 |
|
||||
| 灵田 | 内外田:外田种凡粮(口粮),内田种灵药(经济作物);生长周期、品质 | 必须 |
|
||||
| 炼丹 | 基础丹方:灵药材料 → 丹药成品;品质受技艺影响 | 必须 |
|
||||
| 灵石收支 | 挂 `month_passed` 月度结算:收入(订单/香火)、支出(弟子修炼消耗) | 必须 |
|
||||
| 基础订单 | 单一大客户,按季下订单,品质要求,交货结算灵石 | 必须 |
|
||||
| 口粮 | 凡人弟子每月消耗口粮,外田产量不足时入不敷出 | 必须 |
|
||||
| 忠诚度 | 仅存字段,不影响结算 | 可延后(P2 启用) |
|
||||
| 收徒 | 不做,初始弟子固定 | 可延后(P2) |
|
||||
| 随机委托 | 不做 | 可延后(P2) |
|
||||
| 香火供奉 | 可简化为每月固定小额收入,不做事件 | 可简化 |
|
||||
|
||||
P1 完成标准:新档开局 → 种植灵药 → 炼制丹药 → 按季交货 → 收到灵石 → 弟子修炼消耗灵石 → 境界提升后产量/品质提高,循环可持续 10 年不崩盘。
|
||||
|
||||
## 5. P2 经营深度
|
||||
|
||||
| 模块 | 内容 |
|
||||
| --- | --- |
|
||||
| 忠诚与抽成 | 弟子抽成率可调政策;抽成过高忠诚下降,低忠诚出走 |
|
||||
| 收徒 | 弟子招募:拜师礼一次性收入;新弟子品质随机 |
|
||||
| 随机委托 | 村镇除妖、散修代炼、家族看诊,随机刷新,主动风险收入 |
|
||||
| 香火 | 完整化:与声望挂钩的低保收入 |
|
||||
| 人口压力 | 凡人弟子多 → 口粮压力显现 |
|
||||
|
||||
## 6. P3 外交层
|
||||
|
||||
| 模块 | 内容 |
|
||||
| --- | --- |
|
||||
| 大客户名单 | 2~3 个大宗门/商会客户,各有订单偏好与性格(待定) |
|
||||
| 订单刷新 | 按季刷新订单池,品质/数量/期限随机 |
|
||||
| 附庸三级 | 普通供货商 / 附庸 / 内附,升级条件与权利义务 |
|
||||
| 宗主裁决 | 附庸冲突裁决:四变量打分规则表(价值/规矩/规模/面子) |
|
||||
| 庇护验证 | 玩家被袭击时宗主按等级实际出兵 |
|
||||
| 站队 | 给哪家供货 = 外交立场,影响附庸升级机会 |
|
||||
|
||||
## 7. P4 秘境事件
|
||||
|
||||
| 模块 | 内容 |
|
||||
| --- | --- |
|
||||
| 勘探 | 主动勘探后山:消耗人手/时间,概率发现秘境 |
|
||||
| 发掘 | 禁制分层解锁;功法有修炼门槛;残缺典籍需修复 |
|
||||
| 走漏 | 泄漏概率挂钩忠诚与对外接触;可派警戒/封锁延缓 |
|
||||
| 冲突升级 | 阶段 0~4:秘密发掘 → 小规模冲突 → 邻近宗门进攻 → 大宗门试探 → 大宗门下场 |
|
||||
| 战斗 | 自动结算:双方战力对比 + 战损/消耗计算 |
|
||||
| 试探期操作 | 藏拙 / 立威 / 贿赂 / 结交 事件选项 |
|
||||
|
||||
## 8. P5 终局
|
||||
|
||||
| 模块 | 内容 |
|
||||
| --- | --- |
|
||||
| 毕业考 | 大宗门亲自下场:成本收益式胜利(击退数波进攻使对方评估得不偿失);失败有条件投降 |
|
||||
| 天灾 | 首发魔道入侵:先兆 → 主力 分阶段事件链,威胁所有势力,外交关系重启 |
|
||||
| 天灾伏笔 | 秘境底层封印松动引发(主线贯穿) |
|
||||
| 结局结算 | 宗门传奇度评分 + "宗门史记"结局文本 + 自由沙盒 |
|
||||
|
||||
## 9. 全局简化决策(防范围膨胀)
|
||||
|
||||
| 完整愿景 | 实现取舍 | 理由 |
|
||||
| --- | --- | --- |
|
||||
| 回合制战斗 | 自动结算战力对比 | 经营游戏战斗是结果不是过程 |
|
||||
| 宗主裁决 AI | 四变量打分规则表 | 裁决本质是利益计算,规则表可表达 |
|
||||
| 天灾专属系统 | 事件链 + 复用现有系统 | 天灾 = 数值压力 + 外交重排 |
|
||||
| 多种天灾 | 先做魔道入侵一种 | 单一体验打磨深,后续追加 |
|
||||
| 一局固定时长 | 暂不定(60~100 年),数值测试后定 | 时长影响全盘平衡,不宜拍脑袋 |
|
||||
| 秘境探索玩法 | 概率 + 事件驱动,不做小游戏 | 保持经营主线,探索是节奏器 |
|
||||
+105
@@ -0,0 +1,105 @@
|
||||
# UI 界面清单
|
||||
|
||||
本文档按"前端只管显示,数据从后端 Manager 获取"原则(见 `doc/P1模块拆分.md`)整理全部界面规划。已有界面标注现状,规划界面按 P1 与中后期分列。
|
||||
|
||||
## 1. 目录结构约定
|
||||
|
||||
```
|
||||
src/UI/
|
||||
├── HUD/ 顶栏/底部条状 HUD(常驻)
|
||||
│ ├── MainSceneHud.tscn
|
||||
│ └── MainGameButtomHud.tscn
|
||||
└── Panels/ 全屏/大面积面板(弹层式)
|
||||
├── MainMenu/ 主菜单(已有)
|
||||
├── Management/ 管理主操作面板(P1 新增)
|
||||
│ ├── DisciplePanel/ 弟子面板
|
||||
│ ├── FieldPanel/ 建筑/灵田面板
|
||||
│ ├── AlchemyPanel/ 炼丹面板
|
||||
│ ├── OrderPanel/ 订单面板
|
||||
│ └── InventoryPanel/ 库存面板
|
||||
└── SavePannel.tscn 存档面板(已有,+SaveGroup/SaveSlot)
|
||||
```
|
||||
|
||||
约定:HUD 放常驻条状 UI;面板一律进 `Panels/`,同组面板建子文件夹(参照 MainMenu 先例)。弹窗类(事件/确认/通知)后续放 `Panels/` 下或单独 `Popups/`。
|
||||
|
||||
## 2. 已有界面(现状)
|
||||
|
||||
| 界面 | 文件 | 状态 |
|
||||
| --- | --- | --- |
|
||||
| 主菜单 | `src/UI/Panels/MainMenu/MainMenu.tscn` | 已有:新游戏/读档 |
|
||||
| 存档面板 | `src/UI/Panels/SavePannel.tscn`(+ SaveGroup/SaveSlot) | 已有:两级存档列表 |
|
||||
| 顶栏 HUD | `src/UI/HUD/MainSceneHud.tscn` | 已有:时间/灵石/声望/灵气 + 管理/保存/返回/下一回合按钮 |
|
||||
| 底部 HUD | `src/UI/HUD/MainGameButtomHud.tscn` | 已有但**未接入 MainGame 场景**(仅"继续"按钮) |
|
||||
|
||||
## 3. 主场景布局结构
|
||||
|
||||
```
|
||||
MainGame(场景)
|
||||
├── 顶栏 HUD(全局信息 + 入口按钮)
|
||||
│ ├── 时间 / 灵石 / 声望 / 灵气
|
||||
│ ├── 管理按钮(打开主操作面板) [灵田/炼丹/弟子/订单/库存 入口]
|
||||
│ └── 保存 / 返回
|
||||
├── 主操作区(当前主面板,全屏覆盖) ← 弹层式切换
|
||||
└── 弹窗层(事件/确认/通知,最高层)
|
||||
```
|
||||
|
||||
原则:一次只显示一个主操作面板,面板间通过顶栏"管理"按钮切换;弹窗层供事件、确认框、提示用。
|
||||
|
||||
## 4. P1 界面清单(对应各 Manager)
|
||||
|
||||
| 界面 | 数据源(后端) | 内容 | 操作 |
|
||||
| --- | --- | --- | --- |
|
||||
| 弟子面板 | DiscipleManager | 弟子列表:姓名/境界/技艺/忠诚/修为进度;详情区 | 查看;分配劳作类型(种田/炼丹/空闲) |
|
||||
| 灵田面板 | FieldManager | 内外田地块网格:作物/生长进度/品质 | 种植(选作物+分配弟子)、收获 |
|
||||
| 炼丹面板 | AlchemyManager | 丹方列表(材料需求/技艺要求)+ 可选炼丹弟子 | 炼制(扣材料→入库存) |
|
||||
| 订单面板 | OrderManager | 当前订单:客户/物品/数量/品质要求/期限/奖励 | 交货(校验库存→加灵石) |
|
||||
| 库存面板 | InventoryManager | 物品列表:灵药/丹药/凡粮 数量与品质 | 只读展示(可并入其他面板) |
|
||||
| 顶栏灵石/口粮 | EconomyManager / FoodManager | 灵石余额、口粮结余 | 只读 |
|
||||
|
||||
P1 简化:库存并入炼丹/订单面板分栏显示,不单开面板;弟子劳作分配暂只影响生产(对应弟子技艺),不做单独"空闲/忙碌"状态 UI。
|
||||
|
||||
## 5. 中后期界面规划(P2~P5)
|
||||
|
||||
### P2 经营深度
|
||||
|
||||
| 界面 | 数据源 | 内容 |
|
||||
| --- | --- | --- |
|
||||
| 收徒面板 | DiscipleManager(工厂) | 可收弟子候选:资质/拜师礼 |
|
||||
| 委托面板 | 委托系统 | 随机委托列表:奖励/风险,派遣弟子执行 |
|
||||
| 忠诚度视图 | DiscipleManager | 弟子忠诚/满意度,抽成政策调节(并入弟子面板) |
|
||||
| 香火/供奉 | 声望系统 | 村庄供奉信息 |
|
||||
|
||||
### P3 外交层
|
||||
|
||||
| 界面 | 数据源 | 内容 |
|
||||
| --- | --- | --- |
|
||||
| 外交面板 | 客户/宗门关系系统 | 大宗门列表:好感/附庸等级/订单偏好 |
|
||||
| 附庸管理 | 附庸系统 | 当前附庸等级、义务、宗主裁决记录 |
|
||||
|
||||
### P4 秘境事件
|
||||
|
||||
| 界面 | 数据源 | 内容 |
|
||||
| --- | --- | --- |
|
||||
| 勘探/发掘面板 | 秘境系统 | 后山勘探按钮、秘境层进度、功法获取 |
|
||||
| 警戒/封锁 | 走漏系统 | 消息封锁操作、泄露风险提示 |
|
||||
| 战报 | 冲突系统 | 小规模冲突事件、战斗自动结算结果 |
|
||||
|
||||
### P5 终局
|
||||
|
||||
| 界面 | 数据源 | 内容 |
|
||||
| --- | --- | --- |
|
||||
| 宗门史记/结算 | 评分系统 | 传奇度评分、结局文本、自由沙盒入口 |
|
||||
| 天灾面板 | 天灾系统 | 天灾进度、各方势力动态 |
|
||||
|
||||
## 6. UI 与后端绑定规则
|
||||
|
||||
1. 每个面板对应一个 Manager 或一组 Manager 的只读视图 + 操作方法。
|
||||
2. 面板 `_ready()` 时调 Manager getter 拉全量数据渲染,再连接信号增量刷新(观察者)。
|
||||
3. 玩家操作 → 调 Manager 方法 → Manager 发信号 → 面板刷新(UI 不写数据)。
|
||||
4. 弹窗(事件/确认)由事件系统驱动,可复用通用弹窗场景(标题/正文/按钮组)。
|
||||
|
||||
## 7. 待补充
|
||||
|
||||
- 通用弹窗场景、面板切换容器(P1 起步时实现)
|
||||
- 具体面板视觉风格(沿用现有 theme 体系:`resources/` 下按钮/Label theme)
|
||||
- 中后期面板在各自里程碑实现时细化(本文档只列清单)
|
||||
@@ -0,0 +1,257 @@
|
||||
# 世界观与经济设计讨论记录
|
||||
|
||||
本文档整理游戏设计讨论的过程与结论。结论部分为已共识方向,未敲死的开放问题记录在文末"待定决策"。
|
||||
|
||||
## 1. 宗门背景(已定稿)
|
||||
|
||||
宗门开局就是一个小宗门,并无显赫出身。家底:
|
||||
|
||||
- 一片灵田(种凡粮,供口粮)
|
||||
- 一片灵药园(种灵药,经济作物)
|
||||
- 一个旧丹炉
|
||||
- 初始人手:宗主 + 数名弟子,其中一人擅长种植灵药,同时会炼制基础丹药
|
||||
- 前期事件:一名落魄丹师来投,扩充炼丹产能
|
||||
|
||||
**中期核心事件**(详见第 8 节):玩家主动勘探后山,有概率发现上古宗门存放功法的秘境。发掘初期保密,风声走漏后冲突逐步升级,玩家须同时应付"发掘"与"防守"两条线。
|
||||
|
||||
核心张力分两阶段:
|
||||
|
||||
- **前期**:小作坊,能力单一(种药 + 基础炼丹),现金紧,靠订单吃饭;
|
||||
- **中期**:秘境发掘后"知识富",但兑现需要时间——禁制分层解锁、功法有修炼门槛、典籍残缺需修复。玩家每解锁一层底蕴都应获得成就感。
|
||||
|
||||
## 2. 市场与委托来源(无国家的世界)
|
||||
|
||||
修仙世界没有国家,但**没有国家 ≠ 没有市场**。委托不是行政任务,而是市场交易。市场参与者:
|
||||
|
||||
| 主体 | 委托/交易内容 | 支付方式 |
|
||||
| --- | --- | --- |
|
||||
| 大型宗门 | 收购灵药、定制丹药、外包采集/押运/除妖 | 灵石(大客户) |
|
||||
| 商会/坊市 | 收购丹药灵药、商队护卫、押镖 | 灵石 |
|
||||
| 修仙家族 | 上门炼丹、看风水、除家宅妖邪、族内子弟代培 | 灵石/资源 |
|
||||
| 凡人村镇 | 除妖驱邪、求雨治病、看护农田 | 灵石少,多以粮食/药材/子弟入宗抵偿 |
|
||||
| 散修 | 代炼一炉丹、修复法器、临时组队 | 灵石/以物易物 |
|
||||
|
||||
核心洞见:没有国家反而强化了信誉的地位——没有警察和法院,交易全靠口碑背书。这接上了"组织信誉 = 接委托资格"的设定:宗门能接到活,是因为它是可追责、可预期的组织。
|
||||
|
||||
## 3. 附庸关系与宗主裁决
|
||||
|
||||
### 3.1 三级绑定模型
|
||||
|
||||
大宗门的订单只是附庸关系的入场券,庇护才是核心。附庸关系的本质是**双向契约**:附庸交贡赋/专供/兵役,宗主给保护/司法/仲裁。
|
||||
|
||||
| 等级 | 大宗门提供 | 宗门义务 | 本质 |
|
||||
| --- | --- | --- | --- |
|
||||
| 普通供货商 | 订单(按市价) | 按时交货 | 纯商业,无保护 |
|
||||
| 附庸 | 订单 + 庇护(被袭击时宗主介入) | 专供权、按比例上贡、情报共享 | 庇护换义务 |
|
||||
| 内附/编外堂口 | 庇护 + 功法/资源扶持 | 兵役征调、重大决策受宗主约束 | 深度绑定,近似分部 |
|
||||
|
||||
"站队"因此有了重量:不依附 = 无保护但有自由;依附 = 有伞但受制于人。玩家可以在三个等级间进退,本身就是外交玩法。
|
||||
|
||||
### 3.2 宗主裁决的反应阶梯
|
||||
|
||||
大宗门的裁决**不是公正的,是利益计算的**。附庸都是它的资源,互斗 = 资源内耗 + 秩序受损,"乐见其成"只是特殊情况。
|
||||
|
||||
```
|
||||
默许坐视 → 口头申斥 → 强制调解 → 直接介入 → 废黜吞并
|
||||
```
|
||||
|
||||
裁决取决于四个变量:
|
||||
|
||||
1. **谁更有价值**——一个是大丹商一个是普通农户,偏袒哪个不言而喻;
|
||||
2. **谁坏了规矩**——先动手、先越界的,宗主为维护仲裁威信必须罚,否则庇护就贬值了;
|
||||
3. **冲突规模**——小摩擦装看不见,灭门级冲突必须出手;
|
||||
4. **宗主的面子**——连附庸都管不住,等于向外界示弱。
|
||||
|
||||
**"乐见其成"只在三种情况发生**:附庸欠贡不缴(借刀清除);双方产品同质(死一个,剩下的更依赖宗主);宗主想测试双方实力。大宗门嘴上永远是"口头警告",出不出力调解才是真实态度——**言行之间的落差就是玩家要读的信息**。
|
||||
|
||||
### 3.3 玩家策略
|
||||
|
||||
- **成为"不可替代的附庸"**:宗门擅长灵药丹药,而丹药是修炼刚需——产量、独有丹方、品质就是玩家在宗主面前的筹码。经济实力 → 政治地位,种田炼丹的基本盘与外交连成一体。
|
||||
- **附庸冲突是武器**:挑动竞争对手互斗(借刀杀人)、读宗主心思选时机,比直接打仗便宜得多。
|
||||
- **庇护要可验证**:玩家被袭击时宗主真的出兵(数值上触发),附庸等级的选择才是真实的风险决策。
|
||||
|
||||
## 4. 早期灵石来源(三层收入结构)
|
||||
|
||||
弟子修炼需要灵石,但小宗门没有灵石矿。**灵石矿是中期里程碑,而非开局必需品**。早期收入分三层:
|
||||
|
||||
| 层 | 性质 | 内容 |
|
||||
| --- | --- | --- |
|
||||
| 稳定线(主力) | 大客户订单 | 大型宗门/商会按季下单收购灵药、基础丹药,收入可预期,玩家玩的是排产与品控 |
|
||||
| 波动线(机会) | 零散委托 | 村镇除妖、散修代炼、家族看诊,随机刷新 |
|
||||
| 低保线(兜底) | 香火供奉 | 山脚村庄定期上供,数量少但稳定,保证玩家什么都不做也不破产 |
|
||||
|
||||
**大客户订单模式的设计价值**:
|
||||
|
||||
1. **身份清晰**——宗门核心能力 = 种植灵药 + 基础炼丹,是修仙世界的上游原料商。灵田灵药园因此有了"生产设备"的定位。
|
||||
2. **订单制**——按季交货,有品质要求(种植技艺影响),收入可规划。
|
||||
3. **权力关系**——大客户会压价、拖欠、提苛刻要求,形成"依附大客户 = 有庇护伞,但也受制于人"的张力。给哪家供货 = 站队选择,是外交维度的起点。
|
||||
4. **价值链爬升**——卖原料 → 卖基础丹药 → 卖高级丹药 → 秘境功法后自建高端线。经营核心叙事就是**在供应链里往上爬,从供应商变成品牌方**。
|
||||
|
||||
其他早期收入:拜师礼(一次)、宗主家底(开局一小笔灵石,只够撑过前几个月,逼迫玩家尽快打开收入)。
|
||||
|
||||
## 5. 宗门对弟子的价值(抽成的世界观基础)
|
||||
|
||||
弟子为何交抽成?因为宗门提供散修拿不到的东西,抽成 = 学费 + 洞府租金 + 保护费 + 渠道费 的打包价:
|
||||
|
||||
1. **功法传承**——修仙世界硬通货。完整功法几乎被宗门垄断,散修只能拿到残缺货。功法库就是宗门最大的卖点。
|
||||
2. **修炼设施**——洞府、聚灵阵、丹房、藏经阁(随宗门发展逐步解锁)。
|
||||
3. **庇护与身份**——宗门旗号是威慑力;宗门弟子有资格进坊市主街、参加交换会、报名秘境名额。
|
||||
4. **指点与护法**——师承指点突破、同门护法防走火入魔,散修突破十死无生。
|
||||
5. **资源渠道**——统一采购有渠道价,辅料内部调剂。
|
||||
|
||||
对应修仙文学"散修最惨"的设定:散修自由但什么都没有;交抽成是"用灵石换前程",是当时最优交易。
|
||||
|
||||
**机制含义**:抽成率做成玩家可调政策——定低宗门没钱发展,定高弟子不满甚至出走(需要忠诚度系统支撑)。宗门经营过程 = 提升服务价值,服务越好收高抽成越合理。
|
||||
|
||||
## 6. 小宗门早期形态
|
||||
|
||||
开局宗门几乎没有硬件资源(无藏经阁、无丹房、无灵脉),提供的是**无形资源**:
|
||||
|
||||
1. **宗主本人**——唯一的硬资产:一门完整功法、一身修为、随时可请教的师承指点。早期"抽成"本质是拜师费+学费。
|
||||
2. **"宗门"名头**——两人抱团即势力,挂宗门旗号下山,强盗要掂量报复代价。早期弟子最需要的是保命。
|
||||
3. **身份资格**——正式登记宗门可进坊市、报低阶秘境、接大客户订单。收入门路是宗门带来的。
|
||||
4. **宗主人脉**——旧友、故交、情报渠道,早期人脉 = 赚钱门路。
|
||||
5. **未来预期**——投奔小宗门是赌未来,相当于加入创业公司拿期权。
|
||||
|
||||
**游戏化结论**:早期宗门是"卖人的公司"——宗主拉单(订单/委托),弟子出工出力,灵石进账后抽成。早期循环:接单赚钱 → 攒灵石建设施 → 设施提升宗门价值 → 提高收徒门槛 → 更多灵石。
|
||||
|
||||
## 7. 前期经济线
|
||||
|
||||
两条收入线并存:
|
||||
|
||||
- **种田炼丹**(稳定被动收入):灵药园种药材 + 旧丹炉炼基础丹药,按大客户订单交货。
|
||||
- **零散委托**(主动风险收入):村镇除妖、散修代炼、家族看诊。
|
||||
|
||||
核心玩法张力:**弟子在"修炼"与"生产"之间分配人手**——种药炼丹需要弟子专注,占用修炼时间。
|
||||
|
||||
**灵田分级**:灵田种凡粮是浪费,分内外田——外田种凡粮(口粮),内田种灵药(收入)。凡粮半自给时,凡人弟子多则口粮压力显现,形成人口压力系统。
|
||||
|
||||
## 8. 秘境事件设计(中期核心事件)
|
||||
|
||||
### 8.1 触发
|
||||
|
||||
- 玩家主动勘探后山:消耗人手与时间,有一定概率发现上古宗门存放功法的秘境
|
||||
- 世界观自洽:后山本就是自家地盘,外人进不来;秘境因封印/禁制松动才可能被勘得
|
||||
|
||||
### 8.2 发掘节奏
|
||||
|
||||
- 秘境禁制分层,逐层解锁
|
||||
- 功法有修炼门槛(境界/悟性),部分典籍残缺需修复
|
||||
- "知识富"的兑现过程本身就是中期玩法,避免一次性全给导致中期崩盘
|
||||
|
||||
### 8.3 走漏与干预
|
||||
|
||||
- 风声走漏概率与弟子忠诚、对外接触频率挂钩(由忠诚度系统支撑)
|
||||
- 玩家可派警戒、封锁消息延缓走漏,形成"抢时间窗口"的策略层
|
||||
|
||||
### 8.4 冲突升级链
|
||||
|
||||
冲突是循序渐进的过程,**升级由"发掘进度"驱动,而非固定时间线**——玩家挖得越深,展露的功法价值越高,引来的人就越强。玩家自己控制危机的升级速度,风险与收益始终绑定,天然自平衡。
|
||||
|
||||
```
|
||||
阶段0 秘密发掘 风声未漏,玩家抢时间窗口
|
||||
阶段1 小规模冲突 散修、小家族:偷挖/渗透/袭扰商队,试探性攻击
|
||||
阶段2 邻近宗门进攻 有组织的围攻,真正的军事压力
|
||||
阶段3 大宗门试探 使者来访、假借收丹方、弟子切磋——明探暗察
|
||||
阶段4 大宗门亲自下场 毕业考:挺过去 = 区域强势宗门,正式进入后期
|
||||
```
|
||||
|
||||
**阶段 1 分维度打击**而非纯战斗:偷挖(威胁秘境)、渗透(威胁情报)、袭扰商队(断经济)、围攻山门(军事),逼迫玩家防御面面俱到。
|
||||
|
||||
**大宗门的态度演进有三重设计价值**:
|
||||
|
||||
1. **"不屑一顾"是合理的**——大宗门有自己的完整传承体系,挖出来的基础功法和残篇对它是边角料;谣言满天飞,它要先验证价值再出手。这段"不屑期"就是给玩家消化功法、培养弟子的**缓冲期**,伪装成剧情。
|
||||
2. **"派人试探"是玩家操作空间最大的阶段**——探子来"拜访"时,玩家可以藏拙、立威、贿赂、结交,外交操作直接决定试探结果。
|
||||
3. **"亲自下场"= 毕业考**,正好卡在玩家发育到有还手之力的时候。
|
||||
|
||||
### 8.5 毕业考(大宗门亲自下场)
|
||||
|
||||
- **胜利条件:成本收益式**——大宗门退兵不是因为被打败,而是评估"继续付出的代价 > 抢到功法的收益"后收手。玩家击退几波进攻、消耗对手实力、展示宗门战力,让大宗门觉得不值。符合经营游戏逻辑,也避免"小宗门硬吃掉大宗门"的战力膨胀。
|
||||
- **失败状态:有条件投降**——战败不死,可选择交出秘境控制权、成为大宗门附庸、割让部分收获,保留翻盘机会。
|
||||
|
||||
### 8.6 后遗症
|
||||
|
||||
- 打退大宗门后:名声大涨(招募吸引力提升)+ 结怨(外交隐患),正式成为区域强势宗门
|
||||
- 为后期更大势力的觊觎与天灾埋线
|
||||
|
||||
## 9. 游戏阶段与时间轴
|
||||
|
||||
借鉴群星"一局固定时长"的结构:固定时长让每局都有完整弧线,时间成为真正的资源,截止线制造戏剧张力。本游戏已有月推进回合制,事件挂时间轴做平衡容易。
|
||||
|
||||
| 阶段 | 时间点 | 事件 | 主轴 |
|
||||
| --- | --- | --- | --- |
|
||||
| 前期 | 第 1 年起 | 种田炼丹、大客户订单、附庸关系 | 经济 |
|
||||
| 中期 | 条件触发(勘探发现) | 秘境发掘、小规模冲突 | 秘密与防御 |
|
||||
| 中后期 | 随发掘进度升级 | 邻近宗门围攻、大宗门试探 | 外交与战争 |
|
||||
| 后期门槛 | 条件触发 | 大宗门亲自下场(毕业考) | 生存 |
|
||||
| 后期 | 毕业考通过后 | 区域强势宗门 | 版图与博弈 |
|
||||
| 终局 | 固定年份 + 条件 | 天灾爆发(见第 10 节) | 守护 |
|
||||
| 收尾 | 天灾解决后 | 结局结算 + 自由沙盒 | 历史定位 |
|
||||
|
||||
- **一局时长暂不定**(群星 300 年对 4X 合适;经营游戏倾向 60~100 年即 720~1200 回合,待数值测试后定)
|
||||
- **结算系统**:局末结算**宗门传奇度**——实力、版图、声望、善举综合评分,生成"宗门史记"结局文本。玩家追求的是更好的结局,而不是无限玩下去。结算后进入自由沙盒,可继续经营。
|
||||
- **循环闭环**:后期玩家自己成为"宗主",面对当年大宗门面对过的选择——对附庸冲突是公正裁决还是利益至上?玩家早年对大宗门的怨念,成为后期统治哲学的第一块试金石。
|
||||
|
||||
## 10. 终局天灾
|
||||
|
||||
### 10.1 借鉴群星天灾系统
|
||||
|
||||
1. **天灾威胁所有人,不止玩家**——终局危机爆发后全区域势力都要应对,昔日宿敌被迫合作。把后期"对抗"重构为"生存"。
|
||||
2. **强度挂钩实力**——天灾强度随玩家进程变化,始终有威胁性但可存活。
|
||||
3. **分阶段爆发**——先兆/初潮(零散入侵)→ 主力入侵,给玩家反应时间。
|
||||
4. **危机也是机遇**——趁火打劫、坐收渔利,天灾中依然有策略选择。
|
||||
|
||||
### 10.2 类型与实现顺序
|
||||
|
||||
**先做一种(魔道大举入侵),后续版本追加**:
|
||||
|
||||
| 天灾 | 原型 | 威胁维度 | 玩家角色 |
|
||||
| --- | --- | --- | --- |
|
||||
| 魔道大举入侵(首发) | 虫群/恶魔 | 军事 + 掠夺 | 抵抗军领袖 |
|
||||
| 上古巨凶解封 | 机械天灾觉醒 | 军事 + 地盘沦陷 | 守土者 |
|
||||
| 灵气异变/兽潮 | 灵能危机 | 经济 + 生存(灵田枯死、走火入魔潮) | 救济者 |
|
||||
| 上界势力插手 | 堕落帝国觉醒 | 外交 + 站队(投靠/抵抗) | 周旋者 |
|
||||
|
||||
### 10.3 天灾来源(秘境埋伏笔)
|
||||
|
||||
天灾由秘境剧情埋下伏笔:上古宗门当年为什么把功法封存起来?答案是——**它在封印某个东西**。玩家一路发掘,最后发现秘境底层是封印,自己的发掘正在松动它。
|
||||
|
||||
由此主线贯穿到底:种田起家 → 发掘遗产 → 引来觊觎 → 觉醒封印 → 天灾降临。前期每一层剧情都为终局服务。
|
||||
|
||||
### 10.4 天灾对后期玩法的重构
|
||||
|
||||
1. **外交关系重启**——天灾爆发后,此前所有"站队/结怨/附庸"的选择集体结算:帮过你的人来结盟,结过怨的宗门或抵抗或投敌。天灾是对玩家整个前期行为的终极检验。
|
||||
2. **角色转型**——玩家从"竞争者"变成"一方守土者/抵抗领袖",后期目标从扩张变成守护。
|
||||
3. **危机即机遇**——投敌或灭门的宗门留下势力真空供吞并;做"救世主"刷爆声望,做"渔翁"闷声发大财。
|
||||
4. **收尾**——挺过天灾 = 结局结算(守护一方、重建秩序的历史定位)+ 自由沙盒。
|
||||
|
||||
## 11. 待定决策(讨论过程记录,未敲死)
|
||||
|
||||
### 11.1 旧版背景(备选方案,已被替代)
|
||||
|
||||
远古超大型宗门因经营不善没落,仅剩一名宗主与数名弟子,传承了大量功法,保留灵药园、灵田、丹炉,"知识富、现金穷"。后被"小宗门 + 秘境发掘"方案取代。
|
||||
|
||||
原因:旧版需要额外解释两件事——
|
||||
|
||||
- **资产为何没被抢走**(原候选:A. 宗主实力背书——资产品质低劣、大宗门看不上;B. 位置偏僻——旧址从未被发现;C. 护山大阵残余——只防蟊贼,为后期修阵留伏笔)
|
||||
- **没落原因**(原候选:A. 大劫——大战/内乱/天灾;B. 经营不善——需另补解释资产未被蚕食;C. 两者结合)
|
||||
|
||||
新背景通过"秘境封存无人知晓"直接消除了资产保全漏洞,且天然提供中期事件结构。
|
||||
|
||||
### 11.2 "卖功法换快钱"道德决策点
|
||||
|
||||
宗门穷到揭不开锅时(秘境发掘后尤其相关),卖功法换钱该不该做?候选:
|
||||
|
||||
- **A. 纳入**:作为事件,短期获利但损失底蕴、声望、弟子忠诚,提供戏剧性选择。
|
||||
- **B. 不纳入**:功法库仅作招聘卖点与抽成底气,不做卖功法选项。
|
||||
|
||||
### 11.3 其他待讨论项
|
||||
|
||||
- 一局时长(暂不定,待数值测试)
|
||||
- 天灾后续类型(首发魔道入侵,其余按优先级追加)
|
||||
- 大宗门数量与性格(各具裁决风格)
|
||||
- 附庸等级升降机制(降级是否有惩罚期)
|
||||
- 秘境勘探的具体概率/人手消耗数值
|
||||
- 落魄丹师的入宗条件与剧情
|
||||
- 大客户(供货对象)的初始名单与站队影响
|
||||
- 毕业考难度平衡(成本收益式的具体数值)
|
||||
@@ -0,0 +1,88 @@
|
||||
# 主世界与建筑模块设计
|
||||
|
||||
本文档定义主世界(hex 地块)、建筑与弟子派工的关系。背景见 `doc/WorldDesign.md`、`doc/P1模块拆分.md`。
|
||||
|
||||
## 1. 主世界方案(已定)
|
||||
|
||||
**正六边形单元格地图**,玩家在单元格上建设建筑/灵田(类似文明的领地经营 + 模拟经营的指派产出)。
|
||||
|
||||
### 1.1 设计咬合点(为什么用 hex)
|
||||
|
||||
- **灵石矿是地块资源**:哪块地有灵脉 → 占矿/买矿成为位置决策
|
||||
- **秘境"在后山"是空间概念**:勘探发掘做成地图行为
|
||||
- **后期地盘争夺需要地图**:从 P1 引入空间维度,后期不返工
|
||||
- **成长可视化**:"从小宗门到大宗门"= 地图从一小块山头扩大的过程
|
||||
|
||||
### 1.2 落地节奏(已定:坐标预留,渲染后置)
|
||||
|
||||
| 阶段 | 数据层 | UI 层 |
|
||||
| --- | --- | --- |
|
||||
| P1 | 建筑数据带 hex 坐标(axial) | 卡片/列表渲染,不做地图 |
|
||||
| P2/P3 | 不变(存档天然带位置) | hex 主世界渲染(tile 美术 + 放置交互) |
|
||||
|
||||
前端后端分离下,UI 切换只影响显示层,后端零返工。
|
||||
|
||||
### 1.3 hex 成立的前提
|
||||
|
||||
1. **格子有位置价值**——灵脉格/水源格/肥沃地/岩石地,不同地块加成不同(纯装饰的格子不做)。
|
||||
2. **地图随机生成**——每局资源分布不同,防止最优布局模板一劳永逸,保证复玩性。
|
||||
3. **面积跟随成长**——开局只有山头几格,扩张需买地/开荒,地图与宗门成长绑定。
|
||||
|
||||
### 1.4 弟子与建筑的绑定深度(已定:派工单式)
|
||||
|
||||
- 弟子**指派**给建筑,月底结算产出,弟子不在地图上行走。
|
||||
- 位置只影响加成(如灵田靠近水源产量+)。
|
||||
- 明确不做实体行走式(寻路/队列/行程),与月回合结算节奏冲突,成本高。
|
||||
|
||||
## 2. 建筑实体模型
|
||||
|
||||
建筑(含灵田)是 hex 上的实体,**必须有弟子指派才有产出**:
|
||||
|
||||
```gdscript
|
||||
## BuildingData.gd(RefCounted,纯数据)
|
||||
var id: int
|
||||
var cell: Vector2i # hex axial 坐标(q, r)
|
||||
var building_type: int # 建筑类型枚举
|
||||
var assigned_disciple_id: int = -1 # 指派弟子,-1 = 无
|
||||
var progress: float = 0.0 # 生产/生长进度
|
||||
var level: int = 1 # 建筑等级(预留)
|
||||
```
|
||||
|
||||
## 3. 建筑类型
|
||||
|
||||
| 建筑 | 产出 | 指派要求 | 备注 |
|
||||
| --- | --- | --- | --- |
|
||||
| 外田(凡粮) | 灵稻 | 种植技艺 | 口粮来源 |
|
||||
| 内田(灵药) | 聚气草/凝神花 | 种植技艺 | 炼丹原料 |
|
||||
| 丹房 | 聚气丹/凝神丹 | 炼丹技艺 | 消耗灵药原料 |
|
||||
| 仓库(预留) | — | — | 存粮/存料上限,后期 |
|
||||
| 洞府/居所(预留) | — | — | 弟子数量上限,后期 |
|
||||
|
||||
P1 只做前三种(对应原 FieldManager + AlchemyManager 的职责,统一进建筑框架)。
|
||||
|
||||
## 4. 数据模型演进
|
||||
|
||||
原 `FieldData`(灵田)泛化为 `BuildingData`(建筑),灵田是建筑类型之一:
|
||||
|
||||
- 原 FieldManager → 建筑管理:地块放置、指派弟子、月度结算(生长/产出入库存)
|
||||
- 种植类建筑带"作物"字段(作物类型/生长月数/品质),收获式产出
|
||||
- 丹房是"即时加工"类建筑:有原料时弟子炼制入库存(也可保留 AlchemyManager 独立接口,两者皆可)
|
||||
|
||||
## 5. 月度结算信号流(不变)
|
||||
|
||||
```
|
||||
settle_month()
|
||||
→ 每个有弟子指派的建筑结算产出
|
||||
→ 收获/成品入 InventoryManager
|
||||
→ inventory_changed
|
||||
```
|
||||
|
||||
## 6. 简化决策
|
||||
|
||||
| 完整愿景 | P1 取舍 | 后续 |
|
||||
| --- | --- | --- |
|
||||
| hex 地图渲染 | 不做,卡片/列表 | P2/P3 |
|
||||
| 地图随机生成 | 固定初始地图(几块田+一丹房) | P3 |
|
||||
| 地块地形差异 | 初始全默认,无加成 | P3 引入水源/灵脉 |
|
||||
| 建筑多类型 | 只做外田/内田/丹房 | 逐里程碑加 |
|
||||
| 弟子行走 | 不做(派工单式) | 不评估升级 |
|
||||
+124
@@ -0,0 +1,124 @@
|
||||
# 弟子属性设计
|
||||
|
||||
本文件定义弟子(Disciple)的属性体系、境界规则与派生逻辑,是 `src/Data/Models/Disciple.gd` 的实现依据。关联:`doc/弟子管理.md`(模块设计)、`doc/模块联动.md`(联动规范)、`doc/Roadmap.md`(P1 核心循环)。
|
||||
|
||||
## 1. 属性列表(权威定义)
|
||||
|
||||
| 名称 | 范围 | 说明 |
|
||||
|:---|:---|:---|
|
||||
| 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_exp | [0, 2147483647] | 耕作技能经验 |
|
||||
| farming_level | [0, 10] | 耕作技能等级 |
|
||||
| herb_exp | [0, 2147483647] | 药草耕作技能经验 |
|
||||
| herb_level | [0, 10] | 药草耕作技能等级 |
|
||||
| alchemy_exp | [0, 2147483647] | 炼丹技能经验 |
|
||||
| alchemy_level | [0, 10] | 炼丹技能等级 |
|
||||
| loyalty | [0, 100] | 忠诚度 |
|
||||
|
||||
> 字段说明:`id` 只读,由管理器分配;`name` 普通字符串。原设计中的 `is_mortal`、`cultivation_skill`、`alchemy_skill` 单值字段由下述规则取代,不单独存字段。
|
||||
|
||||
## 2. 境界体系
|
||||
|
||||
大境界 `realm_level` 与显示名、小境界 `sub_realm_level` 数量:
|
||||
|
||||
| realm | 名称 | sub 范围 | 小境界显示 |
|
||||
| --- | --- | --- | --- |
|
||||
| 0 | 凡人 | 固定 1 | 凡人 |
|
||||
| 1 | 练气 | 1~13 | 练气X层(1~13 层) |
|
||||
| 2 | 筑基 | 1~4 | 筑基前期 / 中期 / 后期 / 大圆满 |
|
||||
| 3 | 金丹 | 1~4 | 金丹前期 / 中期 / 后期 / 大圆满 |
|
||||
| 4 | 元婴 | 1~4 | 元婴前期 / 中期 / 后期 / 大圆满 |
|
||||
| 5 | 化神 | 1~4 | 化神前期 / 中期 / 后期 / 大圆满 |
|
||||
|
||||
## 3. 灵根(五行推导,不存字段)
|
||||
|
||||
有灵根的凡人才能修炼破境。**任一五行属性 ≥ 阈值(占位 20,待调参)即具灵根**:
|
||||
|
||||
- 有灵根 → 可修炼:凡人期积累修炼经验,满后破境入练气
|
||||
- 无灵根 → 终生为凡,永不涨修炼经验(且大境界 0)
|
||||
|
||||
派生:`has_spiritual_root()`;凡俗判定 `is_mortal() = (realm_level == 0) 且 无灵根`。
|
||||
|
||||
## 4. 经验规则
|
||||
|
||||
### 4.1 修炼经验(cultivation_exp)
|
||||
|
||||
- 每个小境界有经验上限(曲线表配置,见 §5)
|
||||
- **跨小境界**:经验满自动突破 sub+1,溢出部分**继承**到下一个小境界
|
||||
- **跨大境界**:sub 到顶(练气 13 / 其他 4)再满,realm+1 且 sub=1,溢出部分**清零**
|
||||
- 化神大圆满封顶,不再获得修炼经验
|
||||
- 突破自动进行(回合结算时处理),暂不设额外条件
|
||||
|
||||
### 4.2 技能经验(farming / herb_farming / alchemy 三套相同规则)
|
||||
|
||||
- 每跨一级,溢出的经验**继承**到下一级,直到升满(10 级)为止
|
||||
- 满级后继续获得的经验**不再累积**(经验不会提升)
|
||||
- 每个等级所需经验由配置文件给出(曲线表,见 §5)
|
||||
|
||||
### 4.3 效率的作用
|
||||
|
||||
`efficiency` 决定修炼速度(隐藏属性),不直接改变经验上限;具体月收益公式由管理器结算实现,数值待调参。
|
||||
|
||||
## 5. 经验曲线表(放 Disciple.gd 内部 const,占位待调参)
|
||||
|
||||
> 存储决策:曲线表先以常量写在 `Disciple.gd` 内部,自包含易调;静态内容层方案(CSV/Resource)落地后再迁移。
|
||||
|
||||
### 5.1 修炼曲线 CULTIVATION_EXP_CAPS
|
||||
|
||||
键为 `"{realm}_{sub}"`,共 30 条(凡人 1 + 练气 13 + 筑基/金丹/元婴/化神 各 4)。凡人期条目(`0_1`)供有灵根凡人积累经验破境用;化神大圆满(`5_4`)无上限(哨兵)。
|
||||
|
||||
```
|
||||
{"0_1": 100, "1_1": 100, "1_2": 110, ... 占位递增,待调参}
|
||||
```
|
||||
|
||||
### 5.2 技能曲线 SKILL_EXP_CAPS
|
||||
|
||||
10 条,表示 level N → N+1 所需经验(N = 0~9)。三套技能暂共用一张表,将来可拆分。
|
||||
|
||||
```
|
||||
[100, 120, 140, ...] 占位,待调参
|
||||
```
|
||||
|
||||
## 6. 读写与派生接口(数据类 API)
|
||||
|
||||
数值属性读写统一走键名接口(RANGES 为合法键与夹取范围的唯一权威):
|
||||
|
||||
```gdscript
|
||||
func has_attr(key: String) -> bool # key 是否为登记表中的数值属性
|
||||
func get_attr(key: String) -> int # 读数值属性(未知键 push_error 返回 0)
|
||||
func set_attr(key: String, value: int) -> bool # 写数值属性(夹取 + 境界/小境界联动约束)
|
||||
```
|
||||
|
||||
> 说明:`id` 只读语义(由 `create()` 分配),`name` 为字符串不入 RANGES;两者直接字段访问。`total_amount` 为只读属性(实时由五行之和计算)。
|
||||
|
||||
派生只读方法(境界体系相关,实现时见 §2):
|
||||
|
||||
| 方法 | 逻辑 |
|
||||
| --- | --- |
|
||||
| `total_amount`(只读属性) | 五行之和(实时派生) |
|
||||
| `has_spiritual_root() -> bool` | 任一五行 ≥ 灵根阈值 |
|
||||
| `can_cultivate() -> bool` | 有灵根 且 未满级 |
|
||||
| `is_mortal() -> bool` | realm 0 且无灵根 |
|
||||
| `sub_realm_max() -> int` | 当前大境界的小境界上限(查 SUB_MAX_BY_REALM) |
|
||||
| `exp_cap() -> int` | 当前 (realm, sub) 修炼经验上限(查曲线);满级返哨兵 |
|
||||
| `realm_name() -> String` | 完整显示名:凡人 / 练气5层 / 筑基后期 / 化神大圆满 |
|
||||
|
||||
> 状态迁移(突破、升级、经验增减)属领域操作,归 DiscipleManager(见 `doc/弟子管理.md`),数据类只提供纯查询。
|
||||
|
||||
## 7. 夹取与写入约束
|
||||
|
||||
- `RANGES` 常量表为 17 个属性的合法范围唯一权威,`create()` 与管理器 `set_attr()` 共用
|
||||
- 属性全部普通字段(每属性 1 行声明),不带 setter 样板;夹取收口在两处写入口
|
||||
- 直接写字段不夹取,依赖 `doc/弟子管理.md` "修改只走管理器"纪律
|
||||
|
||||
## 8. 变更记录
|
||||
|
||||
- 2026-09-07:确立属性列表、境界体系(0~5 + sub)、灵根五行推导、经验曲线表结构;本文档由与 opencode 的讨论结论整理(本文件前身为占位草稿)
|
||||
+118
@@ -0,0 +1,118 @@
|
||||
# 弟子管理模块设计
|
||||
|
||||
模块位置: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[分配灵气利用率]
|
||||
```
|
||||
+158
@@ -0,0 +1,158 @@
|
||||
# 模块联动规范
|
||||
|
||||
本文档定义模块间联动逻辑的处理方案:谁调用谁、变更怎么传播、如何保证一致性。联动场景包括「建筑需要弟子才有产出」「炼丹需输入药材→产出丹药」「消耗药材→库存减少」等跨系统交互。
|
||||
|
||||
## 1. 方案组合结论
|
||||
|
||||
| 层 | 选择 | 理由 |
|
||||
| --- | --- | --- |
|
||||
| 主动操作联动(炼丹/交货/种植) | 直接调用 + 依赖注入 | 逻辑显式、可断点调试、单测零成本 |
|
||||
| 回合结算顺序 | 固定管线(MainGame 依次调用) | 消除时间耦合,顺序可预期 |
|
||||
| 状态变更通知(UI/统计/音效) | 信号广播(观察者,单向) | 不引入依赖,加监听者零改动 |
|
||||
| 全局事件总线 / 规则引擎 | 不做,数据形状预留 | P1~P3 联动类型仅个位数,通用引擎成本大于收益 |
|
||||
|
||||
**明确不用的方案**:
|
||||
|
||||
- 不用事件总线处理权威状态变更(如扣库存):请求-响应型联动需要双向信号,代码量反超直接调用,且结算顺序难追踪。事件广播只用于通知,不用于变更。
|
||||
- 不用规则引擎全量执行联动:为 4 类联动(种植收获/炼丹/交货/修炼)写通用执行器是净亏损。P4 功法/术法批量进场(联动类型持续增长)时再评估抽成引擎。
|
||||
|
||||
## 2. 联动场景的本质
|
||||
|
||||
| 场景 | 本质 | 参与方 |
|
||||
| --- | --- | --- |
|
||||
| 建筑需要弟子才有产出 | 前置条件检查(人手占用) | 生产系统 ↔ 弟子系统 |
|
||||
| 炼丹需输入药材→产出丹药 | 资源转换(事务) | 炼丹系统 ↔ 库存系统 |
|
||||
| 消耗药材→库存减少 | 库存变更 + 广播 | 调用方 → 库存 → UI/统计 |
|
||||
|
||||
统一模式:**A 向 B 提需求(检查)→ B 执行变更 → 变更通知所有关心者**。
|
||||
|
||||
## 3. 依赖管理铁律
|
||||
|
||||
### 3.1 依赖图无环(DAG)
|
||||
|
||||
库存、经济是叶子节点,谁也不依赖;生产类依赖库存/弟子;订单依赖库存+经济。**任何 Manager 不得依赖其上层**,出现环即设计错误。
|
||||
|
||||
```
|
||||
OrderManager ──→ InventoryManager(叶子)
|
||||
└──→ EconomyManager(叶子)
|
||||
AlchemyManager ─→ InventoryManager
|
||||
FieldManager ───→ InventoryManager
|
||||
DiscipleManager ─→ EconomyManager(修炼扣灵石)
|
||||
FoodManager ─────→ InventoryManager
|
||||
└─→ DiscipleManager
|
||||
```
|
||||
|
||||
**已知隐患**(P2 启动前必须复查):
|
||||
|
||||
- 忠诚/抽成:弟子抽成 → 经济结算方向仍是 弟子→经济,不构成环;但若经济系统反过来查询弟子(按忠诚发分红)会成环,届时改走信号通知。
|
||||
- 收徒拜师礼:一次性收入走 EconomyManager,同向,无环。
|
||||
|
||||
### 3.2 注入点唯一
|
||||
|
||||
所有 `setup()` 集中在 MainGame 一处,Manager 自身零 autoload 引用(TimeSystem/SaveSystem 除外按需)。这是依赖可管理的根源:
|
||||
|
||||
```gdscript
|
||||
# MainGame._ready() 中:
|
||||
economy_manager.setup()
|
||||
inventory_manager.setup()
|
||||
field_manager.setup(inventory_manager)
|
||||
alchemy_manager.setup(inventory_manager)
|
||||
food_manager.setup(disciple_manager, inventory_manager)
|
||||
order_manager.setup(inventory_manager, economy_manager)
|
||||
disciple_manager.setup(economy_manager)
|
||||
```
|
||||
|
||||
好处:测试场景只注入 Mock 依赖即可单独运行;依赖关系读一遍 MainGame 全貌可见。
|
||||
|
||||
### 3.3 结算顺序管线化
|
||||
|
||||
`month_passed` 固定按序调用各 Manager 的 `settle_month()`,消除时间耦合:
|
||||
|
||||
```
|
||||
灵田(收获入库存) → 炼丹(自动生产) → 弟子(修炼结算)
|
||||
→ 口粮(凡人弟子消耗) → 订单(过期检查) → 经济(固定收入)
|
||||
```
|
||||
|
||||
顺序依据:口粮必须在收获后扣(吃的是新粮);经济最后(汇总本月收支)。新增系统只插入管线,不改变既有顺序。
|
||||
|
||||
## 4. 原子事务约定(check-take-give)
|
||||
|
||||
联动变更必须原子:先检查所有前置条件与材料,全部通过后再执行扣除与产出,**不允许扣一半失败**。
|
||||
|
||||
### 4.1 收敛到库存层
|
||||
|
||||
「先检查后扣除」的手工纪律收敛为库存层的两个方法,调用方不可能写错:
|
||||
|
||||
```gdscript
|
||||
# InventoryManager
|
||||
func try_take(items: Dictionary) -> bool # 全量检查,够则返回 true
|
||||
func take(items: Dictionary) -> void # 实际扣除(try_take 通过后调用)
|
||||
func add_item(item_id: String, count: int) -> void
|
||||
func has_items(items: Dictionary) -> bool # 只查不扣(供 UI 显示可用性)
|
||||
```
|
||||
|
||||
### 4.2 调用方模板
|
||||
|
||||
```gdscript
|
||||
# AlchemyManager.refine() —— 主动操作联动标准写法
|
||||
func refine(recipe_id: String, disciple: Disciple) -> bool:
|
||||
var r: Dictionary = ContentDB.get_recipe(recipe_id)
|
||||
if disciple.alchemy_skill < r["skill_req"]:
|
||||
return false
|
||||
if not inventory.try_take(r["inputs"]):
|
||||
return false
|
||||
inventory.take(r["inputs"])
|
||||
inventory.add_item(r["output_id"], r["output_count"])
|
||||
pill_refined.emit(r["output_id"])
|
||||
return true
|
||||
```
|
||||
|
||||
## 5. 单一写入口
|
||||
|
||||
每种数据只允许其归属 Manager 变更:
|
||||
|
||||
| 数据 | 唯一写入口 | 外部只能 |
|
||||
| --- | --- | --- |
|
||||
| 库存物品 | `InventoryManager.take/add_item` | 调用这两个方法 |
|
||||
| 灵石余额 | `EconomyManager.spend/earn` | 调用这两个方法 |
|
||||
| 弟子属性 | `DiscipleManager.set_attr` | 调用该方法 |
|
||||
| 弟子增删 | `DiscipleManager.add/remove_disciple` | 调用该方法 |
|
||||
|
||||
违反此规则的直接后果:数据被谁改的不可追踪,信号漏发,UI 不同步。
|
||||
|
||||
## 6. 信号分工
|
||||
|
||||
| 信号类型 | 用途 | 方向 |
|
||||
| --- | --- | --- |
|
||||
| Manager 发出的领域信号(`inventory_changed`/`pill_refined`…) | UI 刷新、统计、音效 | 后端 → 前端/其他监听者,单向 |
|
||||
| TimeSystem 的 `month_passed` 等 | 回合结算管线入口 | 已定,不改 |
|
||||
| UI 调用 Manager 公开方法 | 玩家操作 | 前端 → 后端,直接调用不走信号 |
|
||||
|
||||
原则:**信号只通知,不携带状态变更职责**。UI 收到信号后从后端读最新值,不依赖信号参数里的数据(参数仅作提示)。
|
||||
|
||||
## 7. 规则引擎的预留(P4 退路)
|
||||
|
||||
P1 定义配方数据时,形状直接写成 `requires / consumes / produces` 三段(逻辑在代码手写):
|
||||
|
||||
```gdscript
|
||||
{
|
||||
"id": "refine_peiyuan",
|
||||
"requires": [{"type": "disciple_skill", "skill": "alchemy", "min": 3}],
|
||||
"consumes": [{"item": "herb_lingzhi", "count": 2}],
|
||||
"produces": [{"item": "pill_peiyuan", "count": 1}],
|
||||
}
|
||||
```
|
||||
|
||||
将来联动类型膨胀(P4 功法/术法、炼丹失败率、双产出等)时,把各 Manager 手写逻辑抽成通用执行器(检查 requires → 扣 consumes → 给 produces,天然原子),**数据文件一行不用改**。
|
||||
|
||||
评估时机:新增联动类型需要改多个 Manager 的代码时,即考虑抽取。当前(P1)不抽取。
|
||||
|
||||
## 8. 违背规范的常见症状
|
||||
|
||||
| 症状 | 根因 | 对应规范 |
|
||||
| --- | --- | --- |
|
||||
| 库存数量莫名变化 | 绕过 InventoryManager 直接改 | §5 单一写入口 |
|
||||
| 扣了一半材料失败 | 未先 try_take 全量检查 | §4 原子事务 |
|
||||
| 结算结果依赖按钮点击顺序 | 未走固定管线 | §3.3 结算顺序 |
|
||||
| Manager 互相调用成环 | 依赖方向失控 | §3.1 DAG |
|
||||
| 换 UI 后数据不刷新 | 变更未发信号 | §6 信号分工 |
|
||||
@@ -0,0 +1,10 @@
|
||||
[gd_resource type="LabelSettings" format=3 uid="uid://dur4uye3m0ffd"]
|
||||
|
||||
[ext_resource type="FontFile" uid="uid://duxymf2xy66em" path="res://assets/fonts/lxgw-wenkai-v1.522/LXGWWenKai-Regular.ttf" id="1_ks1xp"]
|
||||
|
||||
[resource]
|
||||
line_spacing = 5.0
|
||||
paragraph_spacing = 1.0
|
||||
font = ExtResource("1_ks1xp")
|
||||
font_size = 24
|
||||
font_color = Color(0, 0, 0, 1)
|
||||
@@ -0,0 +1,9 @@
|
||||
[gd_resource type="StyleBoxFlat" format=3 uid="uid://kxc0y8146vlk"]
|
||||
|
||||
[resource]
|
||||
bg_color = Color(0.70980394, 0.91764706, 0.84313726, 1)
|
||||
border_width_left = 10
|
||||
border_width_top = 10
|
||||
border_width_right = 10
|
||||
border_width_bottom = 10
|
||||
border_color = Color(0.763118, 0.67405456, 0.91787714, 1)
|
||||
@@ -0,0 +1,9 @@
|
||||
[gd_resource type="StyleBoxFlat" format=3 uid="uid://e86yw57h4v2s"]
|
||||
|
||||
[resource]
|
||||
bg_color = Color(0.34372595, 0.630709, 0.8494593, 1)
|
||||
border_width_left = 80
|
||||
border_width_top = 40
|
||||
border_width_right = 80
|
||||
border_width_bottom = 40
|
||||
border_color = Color(0.8, 0.8, 0.8, 0)
|
||||
@@ -0,0 +1,163 @@
|
||||
# 初始版本不提供添加弟子的接口
|
||||
|
||||
|
||||
class_name DiscipleManager
|
||||
extends Node
|
||||
|
||||
signal disciple_added(disciple: Disciple)
|
||||
signal disciple_removed(disciple: Disciple)
|
||||
signal disciple_attr_changed(disciple: Disciple, attr_name: String, old_value: Variant, new_value: Variant)
|
||||
|
||||
## 开局随机编队(一次性随机生成的数据,非程序化 RNG;占位,可手调)
|
||||
## 字段与 Disciple.create() 构造键一致;realm_level 用 int(0凡人/1练气,=Disciple.Realm 枚举值)
|
||||
const INITIAL_ROSTER := [
|
||||
# 1 玄真(宗主):修士·炼气9层,五行全高,炼丹3级
|
||||
{
|
||||
"name": "玄真",
|
||||
"metal": 45, "wood": 38, "water": 22, "fire": 31, "earth": 19,
|
||||
"efficiency": 85,
|
||||
"realm_level": 1, "sub_realm_level": 9,
|
||||
"farming_level": 1, "herb_level": 2, "alchemy_level": 3,
|
||||
"loyalty": 90,
|
||||
},
|
||||
# 2 凌霄:修士·炼气4层,金火旺
|
||||
{
|
||||
"name": "凌霄",
|
||||
"metal": 62, "wood": 12, "water": 15, "fire": 44, "earth": 20,
|
||||
"efficiency": 76,
|
||||
"realm_level": 1, "sub_realm_level": 4,
|
||||
"farming_level": 2, "herb_level": 1, "alchemy_level": 1,
|
||||
"loyalty": 55,
|
||||
},
|
||||
# 3 青禾:凡人带灵根(木47≥20 可修炼破境),药草天赋
|
||||
{
|
||||
"name": "青禾",
|
||||
"metal": 8, "wood": 47, "water": 25, "fire": 12, "earth": 10,
|
||||
"efficiency": 68,
|
||||
"realm_level": 0, "sub_realm_level": 1,
|
||||
"farming_level": 2, "herb_level": 3, "alchemy_level": 0,
|
||||
"loyalty": 60,
|
||||
},
|
||||
# 4 阿福:凡人杂役(无灵根,五行均<20)
|
||||
{
|
||||
"name": "阿福",
|
||||
"metal": 5, "wood": 12, "water": 8, "fire": 9, "earth": 10,
|
||||
"efficiency": 41,
|
||||
"realm_level": 0, "sub_realm_level": 1,
|
||||
"farming_level": 2, "herb_level": 1, "alchemy_level": 0,
|
||||
"loyalty": 50,
|
||||
},
|
||||
# 5 阿禄:凡人杂役(无灵根)
|
||||
{
|
||||
"name": "阿禄",
|
||||
"metal": 3, "wood": 6, "water": 5, "fire": 8, "earth": 15,
|
||||
"efficiency": 35,
|
||||
"realm_level": 0, "sub_realm_level": 1,
|
||||
"farming_level": 3, "herb_level": 1, "alchemy_level": 0,
|
||||
"loyalty": 45,
|
||||
},
|
||||
]
|
||||
|
||||
var _disciples: Array[Disciple] = [] # 弟子列表
|
||||
var _master_id: int = 0 # 管理器的发号计数,id 的唯一来源
|
||||
|
||||
# ---- 工厂 ----
|
||||
|
||||
# 初始编队(复用 _create_from_data)
|
||||
func spawn_initial_roster() -> Array[Disciple]:
|
||||
for data in INITIAL_ROSTER:
|
||||
var disciple = _create_from_data(data)
|
||||
if disciple != null:
|
||||
add_disciple(disciple)
|
||||
# _disciples.append(disciple)
|
||||
# emit_signal("disciple_added", disciple)
|
||||
else:
|
||||
push_error("Failed to spawn disciple from data: %s" % data)
|
||||
pass
|
||||
|
||||
# 补 id → Disciple.create()
|
||||
# 从数据字典创建 Disciple 实例,若失败返回 null
|
||||
func _create_from_data(data: Dictionary) -> Disciple:
|
||||
var disciple := Disciple.create(data)
|
||||
if disciple == null:
|
||||
push_error("Failed to create disciple from data: %s" % data)
|
||||
return null
|
||||
disciple.id = _master_id
|
||||
_master_id += 1
|
||||
return disciple
|
||||
pass
|
||||
|
||||
# ---- 仓储 ----
|
||||
|
||||
# 公开接口:添加弟子
|
||||
func add_disciple(d : Disciple) -> void:
|
||||
if d == null:
|
||||
push_error("Cannot add null disciple.")
|
||||
return
|
||||
_disciples.append(d)
|
||||
emit_signal("disciple_added", d)
|
||||
|
||||
# 移除弟子
|
||||
func remove_disciple(d : Disciple) -> bool:
|
||||
if d != null and _disciples.has(d):
|
||||
_disciples.erase(d)
|
||||
emit_signal("disciple_removed", d)
|
||||
return true
|
||||
return false
|
||||
|
||||
# 公开接口:通过id获取弟子
|
||||
func get_disciple(id : int) -> Disciple:
|
||||
for d in _disciples:
|
||||
if d.id == id:
|
||||
return d
|
||||
return null
|
||||
|
||||
# 公开接口:获取所有弟子(返回副本,避免外部修改原数组)
|
||||
func get_all() -> Array[Disciple]:
|
||||
return _disciples.duplicate() # 返回副本,避免外部修改原数组
|
||||
|
||||
# ---- 修改唯一入口 ----
|
||||
|
||||
# 调 d.set_attr + 发信号(name 特判)
|
||||
func set_attr(d :Disciple, attr: String, value: Variant) -> bool:
|
||||
if _disciples.has(d):
|
||||
var old_value = d.get_attr(attr)
|
||||
if d.set_attr(attr, value):
|
||||
emit_signal("disciple_attr_changed", d, attr, old_value, value)
|
||||
return true
|
||||
return false
|
||||
|
||||
# ---- 经验晋级(领域操作)----
|
||||
|
||||
# 跨小境界继承循环;跨大境界清零;满级不涨;无灵根拒绝
|
||||
func add_cultivation_exp(d : Disciple, amount : int) -> void:
|
||||
if _disciples.has(d):
|
||||
var old_realm = d.realm_level
|
||||
var old_sub_realm = d.sub_realm_level
|
||||
d.add_cultivation_exp(amount)
|
||||
if d.realm_level != old_realm or d.sub_realm_level != old_sub_realm:
|
||||
emit_signal("disciple_attr_changed", d, "realm_level", old_realm, d.realm_level)
|
||||
emit_signal("disciple_attr_changed", d, "sub_realm_level", old_sub_realm, d.sub_realm_level)
|
||||
|
||||
# skill∈{farming,herb,alchemy},查 SKILL_EXP_CAPS,溢出继承至满级
|
||||
func add_skill_exp(d : Disciple, skill: String, amount : int) -> void:
|
||||
if _disciples.has(d):
|
||||
var old_level = d.get_skill_level(skill)
|
||||
d.add_skill_exp(skill, amount)
|
||||
var new_level = d.get_skill_level(skill)
|
||||
if new_level != old_level:
|
||||
emit_signal("disciple_attr_changed", d, skill + "_level", old_level, new_level)
|
||||
|
||||
# ---- 月度结算(预留)----
|
||||
# 月收益 = f(efficiency),依赖经济扣灵石 —— 取决于下列问题
|
||||
func settle_month() -> void:
|
||||
pass
|
||||
|
||||
# ---- 存档 ----
|
||||
func to_dict():
|
||||
pass
|
||||
|
||||
func load_from_dict():
|
||||
pass
|
||||
|
||||
|
||||
@@ -2,6 +2,8 @@
|
||||
|
||||
[ext_resource type="Script" uid="uid://cr4by5k8vgkcq" path="res://src/Core/MainGame.gd" id="1_8bu23"]
|
||||
[ext_resource type="PackedScene" uid="uid://d3njijtx7ws1m" path="res://src/UI/HUD/MainSceneHud.tscn" id="2_8bu23"]
|
||||
[ext_resource type="StyleBox" uid="uid://e86yw57h4v2s" path="res://resources/manager_style_box_flat.tres" id="3_dl5bp"]
|
||||
[ext_resource type="PackedScene" uid="uid://d1mbwdtarjqof" path="res://src/UI/Panels/Manager/Manager.tscn" id="4_58g0x"]
|
||||
|
||||
[node name="MainGame" type="Node" unique_id=42551660]
|
||||
script = ExtResource("1_8bu23")
|
||||
@@ -10,4 +12,15 @@ script = ExtResource("1_8bu23")
|
||||
size_flags_horizontal = 3
|
||||
size_flags_vertical = 3
|
||||
|
||||
[node name="PanelContainer" type="PanelContainer" parent="." unique_id=1678471853]
|
||||
anchors_preset = 15
|
||||
anchor_right = 1.0
|
||||
anchor_bottom = 1.0
|
||||
grow_horizontal = 2
|
||||
grow_vertical = 2
|
||||
theme_override_styles/panel = ExtResource("3_dl5bp")
|
||||
|
||||
[node name="Manager" parent="PanelContainer" unique_id=360636457 instance=ExtResource("4_58g0x")]
|
||||
layout_mode = 2
|
||||
|
||||
[connection signal="sign_return_mainmenu" from="MainHud" to="." method="_on_top_bar_hud_sign_return_mainmenu"]
|
||||
|
||||
@@ -0,0 +1,253 @@
|
||||
## 弟子数据模型(纯数据,RefCounted,无业务逻辑)。
|
||||
##
|
||||
## 设计依据:doc/弟子属性.md(属性权威表)、doc/弟子管理.md(模块结构)。
|
||||
## - 持有弟子全部信息,运行时可变,不依赖场景树,可脱离场景单测
|
||||
## - 状态迁移(突破/升级/经验增减)属领域操作,归 DiscipleManager
|
||||
## - 属性写入一律经 set_attr()/create():RANGES 夹取在此收口,禁止外部裸写字段
|
||||
## ("修改只走管理器"纪律,见 doc/弟子管理.md §5)
|
||||
class_name Disciple
|
||||
extends RefCounted
|
||||
|
||||
## 大境界枚举(值即 realm_level 的存储值 0~5,与 doc/弟子属性.md §2 对应)。
|
||||
## 注意:值为存档数据,严禁改变成员顺序或插入成员;只可在末尾追加。
|
||||
enum Realm {
|
||||
MORTAL, # 0 凡人
|
||||
QI_REFINING, # 1 练气
|
||||
FOUNDATION, # 2 筑基
|
||||
GOLDEN_CORE, # 3 金丹
|
||||
NASCENT_SOUL, # 4 元婴
|
||||
SPIRIT_TRANSFORMATION # 5 化神
|
||||
}
|
||||
|
||||
## 每属性合法范围(唯一夹取权威,create/set_attr 共用)。
|
||||
## 与 doc/弟子属性.md §1 权威属性表一一对应;加新属性=加一行。
|
||||
## 注意:name 为字符串不在此表(直接字段读写);id 由 create() 分配(只读语义,见 §3)。
|
||||
const RANGES := {
|
||||
# ---- 五行属性 ----
|
||||
"metal": [0, 100], # 金
|
||||
"wood": [0, 100], # 木
|
||||
"water": [0, 100], # 水
|
||||
"fire": [0, 100], # 火
|
||||
"earth": [0, 100], # 土
|
||||
|
||||
# ---- 修炼属性 ----
|
||||
"efficiency": [1, 100], # 灵气吸收效率(隐藏)
|
||||
"realm_level": [Realm.MORTAL, Realm.SPIRIT_TRANSFORMATION], # 大境界(枚举 Realm)
|
||||
"sub_realm_level": [1, 13], # 小境界:凡人1 / 练气1~13 / 筑基+每境1~4
|
||||
"cultivation_exp": [0, 2147483647], # 修炼经验
|
||||
|
||||
# ---- 技能属性(每技能 = 等级 + 经验两字段,词干区分领域)----
|
||||
"farming_level": [0, 10], # 耕作等级(凡粮·外田)
|
||||
"farming_exp": [0, 2147483647], # 耕作经验
|
||||
"herb_level": [0, 10], # 药草种植等级(灵药·内田)
|
||||
"herb_exp": [0, 2147483647], # 药草种植经验
|
||||
"alchemy_level": [0, 10], # 炼丹等级
|
||||
"alchemy_exp": [0, 2147483647], # 炼丹经验
|
||||
|
||||
# ---- 其他属性 ----
|
||||
"loyalty": [0, 100], # 忠诚度
|
||||
}
|
||||
|
||||
# 五行属性字段(数据字典缺哪个就填 0)
|
||||
const ELEMENT_FIELDS: Array[String] = ["metal", "wood", "water", "fire", "earth"]
|
||||
|
||||
## 各境界的小境界上限(索引 = realm_level):凡人1 / 练气13 / 筑基~化神 各4
|
||||
const SUB_MAX_BY_REALM := [1, 13, 4, 4, 4, 4]
|
||||
|
||||
## 大境界显示名(索引 = realm_level)
|
||||
const REALM_NAMES := ["凡人", "练气", "筑基", "金丹", "元婴", "化神"]
|
||||
## 小境界显示名(索引 = sub_realm_level,0 占位不用):筑基起的 前/中/后/大圆满
|
||||
const SUB_NAMES := ["", "前期", "中期", "后期", "大圆满"]
|
||||
|
||||
## 灵根阈值(占位,待调参):任一五行 ≥ 此值即具灵根,可修炼破境
|
||||
const SPIRITUAL_ROOT_THRESHOLD := 20
|
||||
## 修炼经验存储上限(属性表定义),满级(化神大圆满)时的经验哨兵
|
||||
const EXP_CAP := 2147483647
|
||||
|
||||
## 修炼经验曲线(占位,待调参):键 "{realm}_{sub}" → 该小境界经验上限。
|
||||
## 共 29 条:凡人 1 + 练气 13 + 筑基/金丹/元婴/化神 各 4。
|
||||
## 化神大圆满(5_4)无上限不入表,exp_cap() 满级时返回 EXP_CAP。
|
||||
const CULTIVATION_EXP_CAPS := {
|
||||
# 凡人期:有灵根凡人在此积累经验,满则破境入练气
|
||||
"0_1": 100,
|
||||
# 练气 1~13 层
|
||||
"1_1": 100, "1_2": 120, "1_3": 140, "1_4": 160, "1_5": 180,
|
||||
"1_6": 200, "1_7": 220, "1_8": 240, "1_9": 260, "1_10": 280,
|
||||
"1_11": 300, "1_12": 320, "1_13": 340,
|
||||
# 筑基 前期/中期/后期/大圆满
|
||||
"2_1": 400, "2_2": 460, "2_3": 530, "2_4": 610,
|
||||
# 金丹
|
||||
"3_1": 700, "3_2": 810, "3_3": 930, "3_4": 1070,
|
||||
# 元婴
|
||||
"4_1": 1250, "4_2": 1450, "4_3": 1680, "4_4": 1950,
|
||||
# 化神(大圆满 5_4 封顶,不入表)
|
||||
"5_1": 2300, "5_2": 2700, "5_3": 3150,
|
||||
}
|
||||
|
||||
## 技能经验曲线(占位,待调参):索引 = 当前等级 N(0~9)→ 升到 N+1 所需经验。
|
||||
## 耕作/药草种植/炼丹三套技能共用一张表,将来可拆分(doc/弟子属性.md §5.2)。
|
||||
const SKILL_EXP_CAPS := [100, 150, 220, 320, 460, 660, 940, 1350, 1900, 2700]
|
||||
|
||||
## ---------- 字段 ----------
|
||||
## 各字段默认值即"无数据时的出生值";_init/create 只覆盖 data 中出现的键。
|
||||
## 直接读写仅限 id/name(id 应经 create 分配);数值属性读写必须走 get_attr/set_attr。
|
||||
|
||||
var id: int = 0 # 唯一标识,由 create() 从 data 分配(0 = 未分配)
|
||||
var name: String = "" # 姓名
|
||||
|
||||
## 五行:出生后可变,一律经 create()/DiscipleManager.set_attr 写入
|
||||
var metal: int = 0 # 金
|
||||
var wood: int = 0 # 木
|
||||
var water: int = 0 # 水
|
||||
var fire: int = 0 # 火
|
||||
var earth: int = 0 # 土
|
||||
|
||||
## 灵气吸收效率 1~100,决定修炼速度(隐藏属性)
|
||||
var efficiency: int = 1
|
||||
|
||||
## 大境界(枚举 Realm:MORTAL=凡人 QI_REFINING=练气 FOUNDATION=筑基 GOLDEN_CORE=金丹 NASCENT_SOUL=元婴 SPIRIT_TRANSFORMATION=化神)
|
||||
var realm_level: int = Realm.MORTAL
|
||||
## 小境界:凡人固定1,练气1~13,筑基起1~4
|
||||
var sub_realm_level: int = 1
|
||||
## 修炼经验(每小境界上限查曲线,跨小境界溢出继承,跨大境界清零)
|
||||
var cultivation_exp: int = 0
|
||||
|
||||
## 耕作等级 0~10(凡粮·外田)
|
||||
var farming_level: int = 0
|
||||
## 耕作经验
|
||||
var farming_exp: int = 0
|
||||
## 药草种植等级 0~10(灵药·内田)
|
||||
var herb_level: int = 0
|
||||
## 药草种植经验
|
||||
var herb_exp: int = 0
|
||||
## 炼丹等级 0~10
|
||||
var alchemy_level: int = 0
|
||||
## 炼丹经验
|
||||
var alchemy_exp: int = 0
|
||||
|
||||
## 忠诚度 0~100
|
||||
var loyalty: int = 50
|
||||
|
||||
## 属性总量(只读,实时由五行之和计算)
|
||||
var total_amount: int:
|
||||
get:
|
||||
return metal + wood + water + fire + earth
|
||||
|
||||
## ---------- 构造与工厂 ----------
|
||||
|
||||
## 构造:按 data 覆盖属性,数值属性经 RANGES 夹取;缺键保留字段默认值。
|
||||
## @param data: 键名与属性名一致,如 {"id": 1, "name": "玄真", "metal": 60}
|
||||
func _init(data: Dictionary[String, Variant] = {}) -> void:
|
||||
if data.has("id"):
|
||||
id = int(data["id"])
|
||||
if data.has("name"):
|
||||
name = str(data["name"])
|
||||
for key in RANGES:
|
||||
if data.has(key):
|
||||
set_attr(key, int(data[key]))
|
||||
|
||||
## 工厂:唯一创建入口。id 必填,缺失则报错返回 null(防止未分配 id 的孤儿对象)。
|
||||
## @param data: 构造字典(须含 "id")
|
||||
## @return: 新弟子;id 缺失返回 null
|
||||
static func create(data: Dictionary) -> Disciple:
|
||||
if not data.has("id"):
|
||||
push_error("Disciple.create: 缺少 id,放弃生成 Disciple")
|
||||
return null
|
||||
return Disciple.new(data)
|
||||
|
||||
## ---------- 读写接口 ----------
|
||||
|
||||
## 数值属性是否存在(RANGES 登记表中的键)。
|
||||
## @param key: 属性名
|
||||
## @return: true=是数值属性
|
||||
func has_attr(key: String) -> bool:
|
||||
return RANGES.has(key)
|
||||
|
||||
## 读数值属性。name/id 请直接字段访问(id 不经此函数,name 为字符串)。
|
||||
## 未知键名 push_error 并返回 0(防止拼错键名静默出错)。
|
||||
## @param key: RANGES 登记表中的属性名
|
||||
## @return: 当前值
|
||||
func get_attr(key: String) -> int:
|
||||
if not RANGES.has(key):
|
||||
push_error("Disciple.get_attr: 未知数值属性 \"%s\"" % key)
|
||||
return 0
|
||||
return int(get(key))
|
||||
|
||||
## 写数值属性(唯一数值写入入口):RANGES 夹取 + 境界/小境界联动约束。
|
||||
## 未知键名报错返回 false;不直接改 name/id。
|
||||
## @param key: RANGES 登记表中的属性名
|
||||
## @param value: 新值(越界自动夹取到合法范围)
|
||||
## @return: true=写入成功
|
||||
func set_attr(key: String, value: int) -> bool:
|
||||
if not RANGES.has(key):
|
||||
push_error("Disciple.set_attr: 未知数值属性 \"%s\"" % key)
|
||||
return false
|
||||
var range_: Array = RANGES[key]
|
||||
var v := clampi(value, int(range_[0]), int(range_[1]))
|
||||
if key == "realm_level":
|
||||
# 大境界变化后,小境界不得超过新境界的上限(如落入凡人则强制 1)
|
||||
set("realm_level", v)
|
||||
var max_sub := SUB_MAX_BY_REALM[v]
|
||||
if int(get("sub_realm_level")) > max_sub:
|
||||
set("sub_realm_level", max_sub)
|
||||
elif key == "sub_realm_level":
|
||||
# 小境界写入时按当前大境界上限夹取(凡人上限 1 / 练气 13 / 其余 4)
|
||||
var max_sub := SUB_MAX_BY_REALM[int(get("realm_level"))]
|
||||
set("sub_realm_level", clampi(v, 1, max_sub))
|
||||
else:
|
||||
set(key, v)
|
||||
return true
|
||||
|
||||
## ---------- 派生只读(不存储,由属性实时计算) ----------
|
||||
|
||||
## 当前大境界允许的小境界上限(凡人1 / 练气13 / 筑基起4)。
|
||||
func sub_realm_max() -> int:
|
||||
return SUB_MAX_BY_REALM[realm_level]
|
||||
|
||||
## 是否具灵根:任一五行 ≥ SPIRITUAL_ROOT_THRESHOLD。
|
||||
## 有灵根是修炼的前提(凡人无灵根则终生无法破境)。
|
||||
func has_spiritual_root() -> bool:
|
||||
for f in ELEMENT_FIELDS:
|
||||
if int(get(f)) >= SPIRITUAL_ROOT_THRESHOLD:
|
||||
return true
|
||||
return false
|
||||
|
||||
## 是否满级(化神大圆满):到达后不再获得修炼经验。
|
||||
func is_max_realm() -> bool:
|
||||
return realm_level == Realm.SPIRIT_TRANSFORMATION \
|
||||
and sub_realm_level == SUB_MAX_BY_REALM[Realm.SPIRIT_TRANSFORMATION]
|
||||
|
||||
## 能否修炼:有灵根 且 未满级。
|
||||
func can_cultivate() -> bool:
|
||||
return has_spiritual_root() and not is_max_realm()
|
||||
|
||||
## 是否凡俗:大境界为凡人且无灵根。
|
||||
func is_mortal() -> bool:
|
||||
return realm_level == Realm.MORTAL and not has_spiritual_root()
|
||||
|
||||
## 当前 (realm, sub) 的修炼经验上限;满级返回 EXP_CAP(不再积累)。
|
||||
func exp_cap() -> int:
|
||||
if is_max_realm():
|
||||
return EXP_CAP
|
||||
var key := "%d_%d" % [realm_level, sub_realm_level]
|
||||
if not CULTIVATION_EXP_CAPS.has(key):
|
||||
push_error("Disciple.exp_cap: 曲线表缺少条目 %s" % key)
|
||||
return EXP_CAP
|
||||
return int(CULTIVATION_EXP_CAPS[key])
|
||||
|
||||
## 完整境界显示名:凡人 / 练气5层 / 筑基后期 / 化神大圆满。
|
||||
func realm_name() -> String:
|
||||
if realm_level == Realm.MORTAL:
|
||||
return REALM_NAMES[Realm.MORTAL]
|
||||
if realm_level == Realm.QI_REFINING:
|
||||
return "练气%d层" % sub_realm_level
|
||||
return "%s%s" % [REALM_NAMES[realm_level], SUB_NAMES[sub_realm_level]]
|
||||
|
||||
## ---------- 序列化(存档网关) ----------
|
||||
|
||||
## 输出全属性字典(键名与构造一致),供管理器 to_dict 汇总存档。
|
||||
func to_dict() -> Dictionary:
|
||||
var result := {"id": id, "name": name}
|
||||
for key in RANGES:
|
||||
result[key] = get_attr(key)
|
||||
return result
|
||||
@@ -0,0 +1 @@
|
||||
uid://bttw23ffqifbv
|
||||
@@ -1,36 +0,0 @@
|
||||
[gd_scene format=3 uid="uid://xecv3j1wfxmv"]
|
||||
|
||||
[ext_resource type="Theme" uid="uid://bse6imt1ab1rx" path="res://resources/top_bar_hud_theme.tres" id="1_o3gra"]
|
||||
|
||||
[node name="MainGameButtomHud" type="Control" unique_id=12078477]
|
||||
layout_mode = 3
|
||||
anchors_preset = 15
|
||||
anchor_right = 1.0
|
||||
anchor_bottom = 1.0
|
||||
grow_horizontal = 2
|
||||
grow_vertical = 2
|
||||
size_flags_horizontal = 3
|
||||
size_flags_vertical = 3
|
||||
|
||||
[node name="VBoxContainer" type="VBoxContainer" parent="." unique_id=1423942750]
|
||||
layout_mode = 1
|
||||
anchors_preset = 15
|
||||
anchor_right = 1.0
|
||||
anchor_bottom = 1.0
|
||||
grow_horizontal = 2
|
||||
grow_vertical = 2
|
||||
|
||||
[node name="HBoxContainer" type="HBoxContainer" parent="VBoxContainer" unique_id=1433002269]
|
||||
layout_mode = 2
|
||||
size_flags_vertical = 10
|
||||
|
||||
[node name="Spacer" type="Control" parent="VBoxContainer/HBoxContainer" unique_id=990599719]
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
size_flags_stretch_ratio = 8.0
|
||||
|
||||
[node name="Button" type="Button" parent="VBoxContainer/HBoxContainer" unique_id=1159144698]
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
theme = ExtResource("1_o3gra")
|
||||
text = "继续"
|
||||
@@ -0,0 +1,99 @@
|
||||
[gd_scene format=3 uid="uid://c856oxmhhj5m2"]
|
||||
|
||||
[ext_resource type="StyleBox" uid="uid://kxc0y8146vlk" path="res://resources/manager_info_style_box_flat.tres" id="1_62fqa"]
|
||||
[ext_resource type="LabelSettings" uid="uid://dur4uye3m0ffd" path="res://resources/manager_info_label_settings.tres" id="1_j02a5"]
|
||||
|
||||
[node name="AlchemyPanel" type="Control" unique_id=729180406]
|
||||
custom_minimum_size = Vector2(180, 240)
|
||||
custom_maximum_size = Vector2(200, 240)
|
||||
layout_mode = 3
|
||||
anchors_preset = 15
|
||||
anchor_right = 1.0
|
||||
anchor_bottom = 1.0
|
||||
grow_horizontal = 2
|
||||
grow_vertical = 2
|
||||
|
||||
[node name="PanelContainer" type="PanelContainer" parent="." unique_id=1524260599]
|
||||
layout_mode = 1
|
||||
anchors_preset = 15
|
||||
anchor_right = 1.0
|
||||
anchor_bottom = 1.0
|
||||
grow_horizontal = 2
|
||||
grow_vertical = 2
|
||||
theme_override_styles/panel = ExtResource("1_62fqa")
|
||||
|
||||
[node name="HBoxContainer" type="VBoxContainer" parent="PanelContainer" unique_id=786291684]
|
||||
layout_mode = 2
|
||||
|
||||
[node name="HBoxContainer" type="HBoxContainer" parent="PanelContainer/HBoxContainer" unique_id=199601169]
|
||||
layout_mode = 2
|
||||
size_flags_vertical = 3
|
||||
|
||||
[node name="Label" type="Label" parent="PanelContainer/HBoxContainer/HBoxContainer" unique_id=748051878]
|
||||
layout_mode = 2
|
||||
size_flags_vertical = 1
|
||||
text = "配方:"
|
||||
label_settings = ExtResource("1_j02a5")
|
||||
horizontal_alignment = 1
|
||||
vertical_alignment = 1
|
||||
|
||||
[node name="Name" type="Label" parent="PanelContainer/HBoxContainer/HBoxContainer" unique_id=785682846]
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
size_flags_vertical = 1
|
||||
label_settings = ExtResource("1_j02a5")
|
||||
horizontal_alignment = 1
|
||||
vertical_alignment = 1
|
||||
|
||||
[node name="GridContainer" type="GridContainer" parent="PanelContainer/HBoxContainer" unique_id=1708801949]
|
||||
custom_minimum_size = Vector2(173, 173)
|
||||
custom_maximum_size = Vector2(173, 173)
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 4
|
||||
size_flags_vertical = 4
|
||||
columns = 3
|
||||
|
||||
[node name="Panel" type="Panel" parent="PanelContainer/HBoxContainer/GridContainer" unique_id=1131761795]
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
size_flags_vertical = 3
|
||||
|
||||
[node name="Panel2" type="Panel" parent="PanelContainer/HBoxContainer/GridContainer" unique_id=2037909469]
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
size_flags_vertical = 3
|
||||
|
||||
[node name="Panel3" type="Panel" parent="PanelContainer/HBoxContainer/GridContainer" unique_id=2146602649]
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
size_flags_vertical = 3
|
||||
|
||||
[node name="Panel4" type="Panel" parent="PanelContainer/HBoxContainer/GridContainer" unique_id=1937789898]
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
size_flags_vertical = 3
|
||||
|
||||
[node name="Panel5" type="Panel" parent="PanelContainer/HBoxContainer/GridContainer" unique_id=1747365489]
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
size_flags_vertical = 3
|
||||
|
||||
[node name="Panel6" type="Panel" parent="PanelContainer/HBoxContainer/GridContainer" unique_id=835591152]
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
size_flags_vertical = 3
|
||||
|
||||
[node name="Panel7" type="Panel" parent="PanelContainer/HBoxContainer/GridContainer" unique_id=1977341162]
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
size_flags_vertical = 3
|
||||
|
||||
[node name="Panel8" type="Panel" parent="PanelContainer/HBoxContainer/GridContainer" unique_id=1254657901]
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
size_flags_vertical = 3
|
||||
|
||||
[node name="Panel9" type="Panel" parent="PanelContainer/HBoxContainer/GridContainer" unique_id=1465325187]
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
size_flags_vertical = 3
|
||||
@@ -0,0 +1,159 @@
|
||||
[gd_scene format=3 uid="uid://swa7gtddn6fj"]
|
||||
|
||||
[ext_resource type="LabelSettings" uid="uid://dur4uye3m0ffd" path="res://resources/manager_info_label_settings.tres" id="1_abpqa"]
|
||||
[ext_resource type="StyleBox" uid="uid://kxc0y8146vlk" path="res://resources/manager_info_style_box_flat.tres" id="1_po3i5"]
|
||||
|
||||
[node name="DiscipleInformationPanel" type="PanelContainer" unique_id=2024414593]
|
||||
custom_minimum_size = Vector2(180, 240)
|
||||
custom_maximum_size = Vector2(180, 240)
|
||||
anchors_preset = 15
|
||||
anchor_right = 1.0
|
||||
anchor_bottom = 1.0
|
||||
grow_horizontal = 2
|
||||
grow_vertical = 2
|
||||
size_flags_horizontal = 3
|
||||
size_flags_vertical = 3
|
||||
theme_override_styles/panel = ExtResource("1_po3i5")
|
||||
|
||||
[node name="VBoxContainer" type="VBoxContainer" parent="." unique_id=1066350214]
|
||||
layout_mode = 2
|
||||
size_flags_vertical = 3
|
||||
|
||||
[node name="HBoxContainer" type="HBoxContainer" parent="VBoxContainer" unique_id=528093249]
|
||||
layout_mode = 2
|
||||
size_flags_vertical = 3
|
||||
|
||||
[node name="Label" type="Label" parent="VBoxContainer/HBoxContainer" unique_id=2078348112]
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
size_flags_vertical = 1
|
||||
text = "姓名"
|
||||
label_settings = ExtResource("1_abpqa")
|
||||
horizontal_alignment = 1
|
||||
vertical_alignment = 1
|
||||
|
||||
[node name="Name" type="Label" parent="VBoxContainer/HBoxContainer" unique_id=1124761822]
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
size_flags_vertical = 1
|
||||
label_settings = ExtResource("1_abpqa")
|
||||
|
||||
[node name="HBoxContainer2" type="HBoxContainer" parent="VBoxContainer" unique_id=1255356167]
|
||||
layout_mode = 2
|
||||
size_flags_vertical = 3
|
||||
|
||||
[node name="Label" type="Label" parent="VBoxContainer/HBoxContainer2" unique_id=233829191]
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
size_flags_vertical = 1
|
||||
text = "境界"
|
||||
label_settings = ExtResource("1_abpqa")
|
||||
horizontal_alignment = 1
|
||||
vertical_alignment = 1
|
||||
|
||||
[node name="Level" type="Label" parent="VBoxContainer/HBoxContainer2" unique_id=913472455]
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
size_flags_vertical = 1
|
||||
label_settings = ExtResource("1_abpqa")
|
||||
|
||||
[node name="HBoxContainer3" type="HBoxContainer" parent="VBoxContainer" unique_id=1358402714]
|
||||
layout_mode = 2
|
||||
size_flags_vertical = 3
|
||||
|
||||
[node name="Label" type="Label" parent="VBoxContainer/HBoxContainer3" unique_id=1781987484]
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
size_flags_vertical = 1
|
||||
text = "属性"
|
||||
label_settings = ExtResource("1_abpqa")
|
||||
horizontal_alignment = 1
|
||||
vertical_alignment = 1
|
||||
|
||||
[node name="VBoxContainer2" type="VBoxContainer" parent="VBoxContainer/HBoxContainer3" unique_id=251442021]
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
|
||||
[node name="Metal" type="Label" parent="VBoxContainer/HBoxContainer3/VBoxContainer2" unique_id=395220991]
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
size_flags_vertical = 3
|
||||
text = "金"
|
||||
label_settings = ExtResource("1_abpqa")
|
||||
vertical_alignment = 1
|
||||
|
||||
[node name="Wood" type="Label" parent="VBoxContainer/HBoxContainer3/VBoxContainer2" unique_id=1852801128]
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
size_flags_vertical = 3
|
||||
text = "木"
|
||||
label_settings = ExtResource("1_abpqa")
|
||||
vertical_alignment = 1
|
||||
|
||||
[node name="Water" type="Label" parent="VBoxContainer/HBoxContainer3/VBoxContainer2" unique_id=1864946709]
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
size_flags_vertical = 3
|
||||
text = "水"
|
||||
label_settings = ExtResource("1_abpqa")
|
||||
vertical_alignment = 1
|
||||
|
||||
[node name="Fire" type="Label" parent="VBoxContainer/HBoxContainer3/VBoxContainer2" unique_id=1916838020]
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
size_flags_vertical = 3
|
||||
text = "火"
|
||||
label_settings = ExtResource("1_abpqa")
|
||||
vertical_alignment = 1
|
||||
|
||||
[node name="Earth" type="Label" parent="VBoxContainer/HBoxContainer3/VBoxContainer2" unique_id=2106256122]
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
size_flags_vertical = 3
|
||||
text = "土"
|
||||
label_settings = ExtResource("1_abpqa")
|
||||
vertical_alignment = 1
|
||||
|
||||
[node name="VBoxContainer" type="VBoxContainer" parent="VBoxContainer/HBoxContainer3" unique_id=565242402]
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
|
||||
[node name="Metal" type="Label" parent="VBoxContainer/HBoxContainer3/VBoxContainer" unique_id=334471189]
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
size_flags_vertical = 3
|
||||
text = "1"
|
||||
label_settings = ExtResource("1_abpqa")
|
||||
vertical_alignment = 1
|
||||
|
||||
[node name="Wood" type="Label" parent="VBoxContainer/HBoxContainer3/VBoxContainer" unique_id=1099683329]
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
size_flags_vertical = 3
|
||||
text = "1"
|
||||
label_settings = ExtResource("1_abpqa")
|
||||
vertical_alignment = 1
|
||||
|
||||
[node name="Water" type="Label" parent="VBoxContainer/HBoxContainer3/VBoxContainer" unique_id=1520779703]
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
size_flags_vertical = 3
|
||||
text = "1"
|
||||
label_settings = ExtResource("1_abpqa")
|
||||
vertical_alignment = 1
|
||||
|
||||
[node name="Fire" type="Label" parent="VBoxContainer/HBoxContainer3/VBoxContainer" unique_id=66098798]
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
size_flags_vertical = 3
|
||||
text = "1"
|
||||
label_settings = ExtResource("1_abpqa")
|
||||
vertical_alignment = 1
|
||||
|
||||
[node name="Earth" type="Label" parent="VBoxContainer/HBoxContainer3/VBoxContainer" unique_id=170833691]
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
size_flags_vertical = 3
|
||||
text = "1"
|
||||
label_settings = ExtResource("1_abpqa")
|
||||
vertical_alignment = 1
|
||||
@@ -0,0 +1,43 @@
|
||||
[gd_scene format=3 uid="uid://bk5w5of88aysu"]
|
||||
|
||||
[ext_resource type="StyleBox" uid="uid://kxc0y8146vlk" path="res://resources/manager_info_style_box_flat.tres" id="1_rap8l"]
|
||||
[ext_resource type="LabelSettings" uid="uid://dur4uye3m0ffd" path="res://resources/manager_info_label_settings.tres" id="2_p6bv6"]
|
||||
|
||||
[node name="FieldInformationPanel" type="Control" unique_id=844204855]
|
||||
custom_minimum_size = Vector2(180, 240)
|
||||
custom_maximum_size = Vector2(180, 240)
|
||||
layout_mode = 3
|
||||
anchors_preset = 15
|
||||
anchor_right = 1.0
|
||||
anchor_bottom = 1.0
|
||||
grow_horizontal = 2
|
||||
grow_vertical = 2
|
||||
|
||||
[node name="PanelContainer" type="PanelContainer" parent="." unique_id=900478219]
|
||||
layout_mode = 1
|
||||
anchors_preset = 15
|
||||
anchor_right = 1.0
|
||||
anchor_bottom = 1.0
|
||||
grow_horizontal = 2
|
||||
grow_vertical = 2
|
||||
theme_override_styles/panel = ExtResource("1_rap8l")
|
||||
|
||||
[node name="VBoxContainer" type="VBoxContainer" parent="PanelContainer" unique_id=241079176]
|
||||
layout_mode = 2
|
||||
|
||||
[node name="Name" type="Label" parent="PanelContainer/VBoxContainer" unique_id=909840161]
|
||||
layout_mode = 2
|
||||
text = "AAA"
|
||||
label_settings = ExtResource("2_p6bv6")
|
||||
horizontal_alignment = 1
|
||||
vertical_alignment = 1
|
||||
|
||||
[node name="Info" type="Label" parent="PanelContainer/VBoxContainer" unique_id=1950109494]
|
||||
layout_mode = 2
|
||||
text = "BBB"
|
||||
label_settings = ExtResource("2_p6bv6")
|
||||
|
||||
[node name="GridContainer" type="GridContainer" parent="PanelContainer/VBoxContainer" unique_id=1107543260]
|
||||
layout_mode = 2
|
||||
size_flags_vertical = 3
|
||||
columns = 4
|
||||
@@ -0,0 +1,23 @@
|
||||
[gd_scene format=3 uid="uid://dxij6vexu5m1q"]
|
||||
|
||||
[node name="InventoryPanel" type="Control" unique_id=735363118]
|
||||
layout_mode = 3
|
||||
anchors_preset = 15
|
||||
anchor_right = 1.0
|
||||
anchor_bottom = 1.0
|
||||
grow_horizontal = 2
|
||||
grow_vertical = 2
|
||||
|
||||
[node name="ScrollContainer" type="ScrollContainer" parent="." unique_id=887058553]
|
||||
layout_mode = 1
|
||||
anchors_preset = 15
|
||||
anchor_right = 1.0
|
||||
anchor_bottom = 1.0
|
||||
grow_horizontal = 2
|
||||
grow_vertical = 2
|
||||
|
||||
[node name="Tree" type="Tree" parent="ScrollContainer" unique_id=1018260768]
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
size_flags_vertical = 3
|
||||
columns = 5
|
||||
@@ -0,0 +1,6 @@
|
||||
extends Control
|
||||
|
||||
|
||||
|
||||
func _ready() -> void:
|
||||
pass
|
||||
@@ -0,0 +1 @@
|
||||
uid://qjqb0jaqxuu2
|
||||
@@ -0,0 +1,49 @@
|
||||
[gd_scene format=3 uid="uid://d1mbwdtarjqof"]
|
||||
|
||||
[ext_resource type="Script" uid="uid://qjqb0jaqxuu2" path="res://src/UI/Panels/Manager/Manager.gd" id="1_qdh6a"]
|
||||
[ext_resource type="PackedScene" uid="uid://cbqydw1lerumr" path="res://src/UI/Panels/Manager/ManagerPanel.tscn" id="2_ieenf"]
|
||||
[ext_resource type="PackedScene" uid="uid://dxij6vexu5m1q" path="res://src/UI/Panels/Manager/InventoryPanel/InventoryPanel.tscn" id="3_k3wlu"]
|
||||
|
||||
[node name="Manager" type="Control" unique_id=360636457]
|
||||
layout_mode = 3
|
||||
anchors_preset = 15
|
||||
anchor_right = 1.0
|
||||
anchor_bottom = 1.0
|
||||
grow_horizontal = 2
|
||||
grow_vertical = 2
|
||||
size_flags_horizontal = 3
|
||||
size_flags_vertical = 3
|
||||
script = ExtResource("1_qdh6a")
|
||||
|
||||
[node name="TabContainer" type="TabContainer" parent="." unique_id=915058536]
|
||||
layout_mode = 1
|
||||
anchors_preset = 15
|
||||
anchor_right = 1.0
|
||||
anchor_bottom = 1.0
|
||||
grow_horizontal = 2
|
||||
grow_vertical = 2
|
||||
current_tab = 4
|
||||
|
||||
[node name="ManagerPanel" parent="TabContainer" unique_id=1475562434 instance=ExtResource("2_ieenf")]
|
||||
visible = false
|
||||
layout_mode = 2
|
||||
metadata/_tab_index = 0
|
||||
|
||||
[node name="ManagerPanel2" parent="TabContainer" unique_id=393118788 instance=ExtResource("2_ieenf")]
|
||||
visible = false
|
||||
layout_mode = 2
|
||||
metadata/_tab_index = 1
|
||||
|
||||
[node name="ManagerPanel3" parent="TabContainer" unique_id=609733971 instance=ExtResource("2_ieenf")]
|
||||
visible = false
|
||||
layout_mode = 2
|
||||
metadata/_tab_index = 2
|
||||
|
||||
[node name="ManagerPanel4" parent="TabContainer" unique_id=780944038 instance=ExtResource("2_ieenf")]
|
||||
visible = false
|
||||
layout_mode = 2
|
||||
metadata/_tab_index = 3
|
||||
|
||||
[node name="InventoryPanel" parent="TabContainer" unique_id=735363118 instance=ExtResource("3_k3wlu")]
|
||||
layout_mode = 2
|
||||
metadata/_tab_index = 4
|
||||
@@ -0,0 +1,21 @@
|
||||
[gd_scene format=3 uid="uid://cbqydw1lerumr"]
|
||||
|
||||
[node name="ManagerPanel" type="Control" unique_id=1475562434]
|
||||
layout_mode = 3
|
||||
anchors_preset = 15
|
||||
anchor_right = 1.0
|
||||
anchor_bottom = 1.0
|
||||
grow_horizontal = 2
|
||||
grow_vertical = 2
|
||||
|
||||
[node name="ScrollContainer" type="ScrollContainer" parent="." unique_id=129376527]
|
||||
layout_mode = 1
|
||||
anchors_preset = 15
|
||||
anchor_right = 1.0
|
||||
anchor_bottom = 1.0
|
||||
grow_horizontal = 2
|
||||
grow_vertical = 2
|
||||
|
||||
[node name="HFlowContainer" type="HFlowContainer" parent="ScrollContainer" unique_id=836450693]
|
||||
layout_mode = 2
|
||||
alignment = 1
|
||||
@@ -0,0 +1,34 @@
|
||||
[gd_scene format=3 uid="uid://6ui4v8yhobr4"]
|
||||
|
||||
[ext_resource type="StyleBox" uid="uid://kxc0y8146vlk" path="res://resources/manager_info_style_box_flat.tres" id="1_87463"]
|
||||
[ext_resource type="LabelSettings" uid="uid://dur4uye3m0ffd" path="res://resources/manager_info_label_settings.tres" id="2_sotk8"]
|
||||
|
||||
[node name="OrderPanel" type="Control" unique_id=1777280890]
|
||||
custom_minimum_size = Vector2(180, 240)
|
||||
custom_maximum_size = Vector2(180, 240)
|
||||
layout_mode = 3
|
||||
anchors_preset = 15
|
||||
anchor_right = 1.0
|
||||
anchor_bottom = 1.0
|
||||
grow_horizontal = 2
|
||||
grow_vertical = 2
|
||||
|
||||
[node name="PanelContainer" type="PanelContainer" parent="." unique_id=1940446491]
|
||||
layout_mode = 1
|
||||
anchors_preset = 15
|
||||
anchor_right = 1.0
|
||||
anchor_bottom = 1.0
|
||||
grow_horizontal = 2
|
||||
grow_vertical = 2
|
||||
theme_override_styles/panel = ExtResource("1_87463")
|
||||
|
||||
[node name="VBoxContainer" type="VBoxContainer" parent="PanelContainer" unique_id=1857403555]
|
||||
layout_mode = 2
|
||||
|
||||
[node name="Title" type="Label" parent="PanelContainer/VBoxContainer" unique_id=174596326]
|
||||
layout_mode = 2
|
||||
label_settings = ExtResource("2_sotk8")
|
||||
|
||||
[node name="Reward" type="Label" parent="PanelContainer/VBoxContainer" unique_id=835372898]
|
||||
layout_mode = 2
|
||||
label_settings = ExtResource("2_sotk8")
|
||||
@@ -46,7 +46,6 @@ func refresh_groups() -> void:
|
||||
item.group_selected.connect(_on_group_selected)
|
||||
item.delete_requested.connect(_on_group_delete_requested)
|
||||
group_container.add_child(item)
|
||||
print(group['id'])
|
||||
# 校验当前选中组:已被删除则复位并清空右列
|
||||
if _current_profile == 0 or _current_profile not in _get_profile_ids():
|
||||
_current_profile = 0
|
||||
@@ -70,8 +69,6 @@ func refresh_slots(profile_id: int) -> void:
|
||||
item.load_requested.connect(_on_slot_load_requested)
|
||||
item.delete_requested.connect(_on_slot_delete_requested)
|
||||
slot_list.add_child(item)
|
||||
print("--->");
|
||||
print("---------------------->");
|
||||
|
||||
|
||||
func _on_group_selected(profile_id: int) -> void:
|
||||
|
||||
Reference in New Issue
Block a user