Compare commits

...
4 Commits
14 changed files with 431 additions and 10 deletions
+93 -5
View File
@@ -1,7 +1,7 @@
# 弟子系统设计(D3:生成 + 入宗分流) # 弟子系统设计(D3:生成 + 入宗分流 / D4:名录 UI
> 对应开发计划表 D3(8/5)。本文档只描述设计,不包含代码。 > 对应开发计划表 D38/5与 D48/6。本文档只描述设计,不包含代码。
> 状态:设计稿,待审阅通过后实现 > 状态:D3 已实现;D4 设计稿,待审阅通过后实现
--- ---
@@ -17,6 +17,9 @@
| `aptitude` | 枚举 Aptitude | 资质(入宗分流结果) | | `aptitude` | 枚举 Aptitude | 资质(入宗分流结果) |
| `roots` | 枚举数组 SpiritRoot | 灵根列表(普通弟子 1~3 根,互不重复) | | `roots` | 枚举数组 SpiritRoot | 灵根列表(普通弟子 1~3 根,互不重复) |
| `is_heavenly` | bool | 天灵根标记(稀有灵根,见枚举二) | | `is_heavenly` | bool | 天灵根标记(稀有灵根,见枚举二) |
| `join_year` | int | 入宗年份(0 = 未入宗/候选),accept 时快照 GameState |
| `join_month` | int | 入宗月份 |
| `join_day` | int | 入宗日 |
| `realm_index` | int | 大境界索引(0=练气),D5 修炼系统推进 | | `realm_index` | int | 大境界索引(0=练气),D5 修炼系统推进 |
| `sub_realm_index` | int | 小境界索引(练气层数-1),D5 修炼系统推进 | | `sub_realm_index` | int | 小境界索引(练气层数-1),D5 修炼系统推进 |
@@ -57,6 +60,12 @@
境界的成长、突破门槛、寿元计算全部留给 D5 修炼循环,D3 只负责初始状态。 境界的成长、突破门槛、寿元计算全部留给 D5 修炼循环,D3 只负责初始状态。
### 入宗时间字段说明
- `join_year/month/day` 在**入宗瞬间**accept_applicant)快照 GameState 当前日期
- 候选池弟子不设(0),拒绝入宗不产生记录
- 本期只存不消费;入门天数换算(D5 修炼结算)、按入宗排序(列表)、资历事件(W8)后续使用
--- ---
## 二、生成规则(随机性) ## 二、生成规则(随机性)
@@ -173,7 +182,7 @@ aptitude_weights=[60, 30, 8, 2]
- 每个候选一行:姓名/年龄/资质(天灵根标记)/灵根/境界 + "入宗"勾选框 - 每个候选一行:姓名/年龄/资质(天灵根标记)/灵根/境界 + "入宗"勾选框
- 底部:全部接受 / 全部拒绝 / 确定 - 底部:全部接受 / 全部拒绝 / 确定
- 开局模式确定按钮在选满 3 名前禁用;确定后关闭弹窗并恢复时间 - 开局模式确定按钮在选满 3 名前禁用;确定后关闭弹窗并恢复时间
- 名录的可视化列表属于 D4弟子列表 UI,届时由 `disciples_changed` 信号驱动 - 名录的可视化列表见**第七节(D4 弟子名录 UI**
--- ---
@@ -188,7 +197,86 @@ aptitude_weights=[60, 30, 8, 2]
--- ---
## 七、验收清单(D3 交付标准 ## 七、弟子名录 UID4
> 对应开发计划表 D4(8/6):① 列表条目场景 ② 姓名/资质/境界显示 ③ 点击详情。交付标准:列表可浏览弟子。
### 1. 入口
- TopBar 顶部栏"**管理**"按钮(Button_StaffManagement)→ 打开弟子名录面板
- 打开面板时**暂停时间**(管理操作不打断挂机节奏,避免浏览时弟子入宗/时间流逝;关闭面板恢复推进)
### 2. 面板结构(两层弹窗,复用青绿仙侠配色)
**第一层:名录列表面板(DisciplesPanel**
| 元素 | 说明 |
|------|------|
| 标题 | "弟子名录(N 人)"N = 当前名录人数 |
| 列表区 | ScrollContainer 动态条目,一个弟子一行,按入宗顺序 |
| 条目内容 | 姓名 / 资质(含天灵根标记)/ 灵根 / 境界 + "详情"按钮 |
| 空状态 | 名录为空时显示"暂无弟子"提示 |
| 底部 | "关闭"按钮(也可点遮罩关闭) |
**第二层:弟子详情弹窗(DiscipleDetailPanel**
| 元素 | 说明 |
|------|------|
| 标题 | 弟子姓名 + 天灵根标记 |
| 信息区 | 静态展示:资质 / 年龄 / 灵根 / 境界 / 编号 / **入宗时间** |
| 预留区 | 修炼进度、属性面板等 D5+ 扩展位置(本期留白) |
| 底部 | "关闭"按钮 |
### 3. 数据驱动
- 面板监听 `DiscipleManager.disciples_changed` → 重建列表
- 打开面板时也主动刷新一次(保证与名录同步)
- 条目与弟子数据一一对应:动态生成时把 Disciple 引用绑定到条目上,点"详情"直接取用
### 4. 文件与场景结构
```
src/UI/Panels/DisciplesPanel/
├── DisciplesPanel.tscn # 名录列表弹窗
├── disciples_panel.gd
├── DiscipleDetailPanel.tscn # 弟子详情弹窗
└── disciple_detail_panel.gd
```
- 两个弹窗都挂在 MainGame 下(默认隐藏),详情弹窗作为列表面板的子层(显示时盖在列表之上)
- 条目在代码中动态生成(与 RecruitPanel 同套路),不单独做条目场景(D4 拆解的"列表条目场景"用动态行实现即可)
- TopBar"管理"按钮在 `top_bar_hud.gd` 接线,调 `DisciplesPanel.show_panel()`
### 5. 交互细节
- 详情弹窗从列表面板打开;关闭详情回到列表;关闭列表回到游戏
- 列表浏览期间名录变化(如定期招募入宗)→ 列表实时刷新
- 资质文字可用颜色区分(天骄金色、上品亮青),其余默认——视觉提示,本期可选
---
## 八、验收清单(D4 交付标准)
- [ ] 点顶部"管理"按钮弹出弟子名录面板,显示当前已入宗弟子
- [ ] 列表每行显示:姓名 / 资质(天灵根有标记)/ 灵根 / 境界
- [ ] 名录为空时显示"暂无弟子";招募入宗后列表实时刷新
- [ ] 点条目"详情"弹出详情弹窗,显示姓名/资质/年龄/灵根/境界/编号
- [ ] 关闭详情回到列表,关闭列表回到游戏,无报错
- [ ] 打开面板期间时间**暂停**,关闭面板后时间恢复推进
---
## 九、与后续任务衔接(D4 补充)
| 后续任务 | 衔接方式 |
|----------|----------|
| D5 修炼循环 | 详情弹窗预留区填充:修为进度、突破按钮、寿元 |
| D6 建筑/职能系统 | 列表条目可扩展"职位/职能"列 |
| D8 存档接入 | 名录数据来源不变,仅序列化方式接入 |
---
## 十、验收清单(D3 交付标准)
- [ ] 项目能正常启动,无脚本报错 - [ ] 项目能正常启动,无脚本报错
- [ ] **开局**:进入游戏即暂停,弹出"弟子入宗(开局招募)",显示 5 名候选 - [ ] **开局**:进入游戏即暂停,弹出"弟子入宗(开局招募)",显示 5 名候选
+2 -2
View File
@@ -1,7 +1,7 @@
# 开发计划表 # 开发计划表
> 开始日期:2026-08-03(周一) 每日有效开发约 2 小时 > 开始日期:2026-08-03(周一) 每日有效开发约 2 小时
> 进度更新:2026-08-06  D1D3 完成D3 含弹窗招募与配置系统),D4 进行 > 进度更新:2026-08-06  D1D4 完成D5(修炼循环)明日进行
## 一、每日作息模板(工作日) ## 一、每日作息模板(工作日)
@@ -22,7 +22,7 @@
| 8/3 一 | D1 | TimeSystem | ① 日期变量(年月日) ② tick 推进 ③ 暂停/加速 | HUD 显示年月日,可调速度 | ✅ 完成(含月/年信号、Settings 持久化) | | 8/3 一 | D1 | TimeSystem | ① 日期变量(年月日) ② tick 推进 ③ 暂停/加速 | HUD 显示年月日,可调速度 | ✅ 完成(含月/年信号、Settings 持久化) |
| 8/4 二 | D2 | SectManager 数据模型 | ① 单例 ② 灵石/灵气/声望 ③ 增减接口 | 控制台可读写资源 | ✅ 完成(灵石/声望增减+信号,HUD 实时绑定) | | 8/4 二 | D2 | SectManager 数据模型 | ① 单例 ② 灵石/灵气/声望 ③ 增减接口 | 控制台可读写资源 | ✅ 完成(灵石/声望增减+信号,HUD 实时绑定) |
| 8/5 三 | D3 | 弟子生成 + 入宗分流 | ① 随机名字/资质/灵根 ② 按资质分四类 ③ 招募流程 | 开局 5 选 3 必选满,定期弹窗可全拒/全收 | ✅ 完成(弹窗招募:开局5选3+定期3候选,配置驱动含天灵根3%;新增 GameConfig 配置系统) | | 8/5 三 | D3 | 弟子生成 + 入宗分流 | ① 随机名字/资质/灵根 ② 按资质分四类 ③ 招募流程 | 开局 5 选 3 必选满,定期弹窗可全拒/全收 | ✅ 完成(弹窗招募:开局5选3+定期3候选,配置驱动含天灵根3%;新增 GameConfig 配置系统) |
| 8/6 四 | D4 | 弟子列表 UI | ① 列表条目场景 ② 姓名/资质/境界显示 ③ 点击详情 | 列表可浏览弟子 | | | 8/6 四 | D4 | 弟子列表 UI | ① 列表条目场景 ② 姓名/资质/境界显示 ③ 点击详情 | 列表可浏览弟子 | ✅ 完成(管理按钮入口,名录面板+详情弹窗,天灵根标记,打开面板暂停时间) |
| 8/7 五 | D5 | 修炼循环 | ① 修为随时间增长(资质×灵气) ② 境界门槛 ③ 突破判定 | 弟子自动修炼,可突破 | ⬜ | | 8/7 五 | D5 | 修炼循环 | ① 修为随时间增长(资质×灵气) ② 境界门槛 ③ 突破判定 | 弟子自动修炼,可突破 | ⬜ |
| 8/8 六 | D6 | 建筑系统基础 | ① 灵石矿场景 ② 建造→每时辰产出 ③ 升级 | 建筑产灵石,升级加速 | ⬜ | | 8/8 六 | D6 | 建筑系统基础 | ① 灵石矿场景 ② 建造→每时辰产出 ③ 升级 | 建筑产灵石,升级加速 | ⬜ |
| 8/9 日 | D7 | 休息 / 补漏 | 修本周 bug,整理代码 | git 干净 | ⬜ | | 8/9 日 | D7 | 休息 / 补漏 | 修本周 bug,整理代码 | git 干净 | ⬜ |
+9
View File
@@ -23,6 +23,9 @@ var age: int = 0
var aptitude: Aptitude = Aptitude.ORDINARY var aptitude: Aptitude = Aptitude.ORDINARY
var roots: Array[SpiritRoot] = [] # 普通弟子 1~3 根;天灵根固定 1 根 var roots: Array[SpiritRoot] = [] # 普通弟子 1~3 根;天灵根固定 1 根
var is_heavenly: bool = false # 天灵根标记(3% 概率) var is_heavenly: bool = false # 天灵根标记(3% 概率)
var join_year: int = 0 # 入宗年份(0 = 未入宗/候选),accept 时快照
var join_month: int = 0 # 入宗月份
var join_day: int = 0 # 入宗日
var realm_index: int = 0 # 大境界索引(0=练气) var realm_index: int = 0 # 大境界索引(0=练气)
var sub_realm_index: int = 0 # 小境界索引(练气层数-1 var sub_realm_index: int = 0 # 小境界索引(练气层数-1
@@ -40,3 +43,9 @@ func get_realm_text() -> String:
if realm_index == 0: if realm_index == 0:
return "%s%d" % [REALM_NAMES[realm_index], sub_realm_index + 1] return "%s%d" % [REALM_NAMES[realm_index], sub_realm_index + 1]
return "%s%s" % [REALM_NAMES[realm_index], ["初期", "中期", "后期", "圆满"][sub_realm_index]] return "%s%s" % [REALM_NAMES[realm_index], ["初期", "中期", "后期", "圆满"][sub_realm_index]]
## 入宗时间文本,如 "1年2月3日";未入宗返回空串。
func get_join_date_text() -> String:
if join_year <= 0:
return ""
return "%d%d%d" % [join_year, join_month, join_day]
+5 -1
View File
@@ -22,6 +22,7 @@ const SURNAMES: Array[String] = [
"", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "",
"", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "",
"", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "",
"", "", "", "", "", "", "", "",
] ]
## 名用字池(修仙风)。 ## 名用字池(修仙风)。
@@ -65,7 +66,7 @@ func generate_applicants(count: int, is_opening := false) -> void:
applicants.append(_create_applicant()) applicants.append(_create_applicant())
applicants_generated.emit(is_opening) applicants_generated.emit(is_opening)
## 接受一名候选弟子:分配 ID 并移入名录(候选池 → 名录)。 ## 接受一名候选弟子:分配 ID、快照入宗时间并移入名录(候选池 → 名录)。
## @param d: 要接受的候选 ## @param d: 要接受的候选
func accept_applicant(d: Disciple) -> void: func accept_applicant(d: Disciple) -> void:
if not applicants.has(d): if not applicants.has(d):
@@ -73,6 +74,9 @@ func accept_applicant(d: Disciple) -> void:
applicants.erase(d) applicants.erase(d)
d.id = _next_id d.id = _next_id
_next_id += 1 _next_id += 1
d.join_year = GameState.year
d.join_month = GameState.month
d.join_day = GameState.day
disciples.append(d) disciples.append(d)
disciples_changed.emit() disciples_changed.emit()
print("弟子入宗:%s %d岁 | %s资质%s | %s灵根 | %s" % [ print("弟子入宗:%s %d岁 | %s资质%s | %s灵根 | %s" % [
+4
View File
@@ -13,3 +13,7 @@ func _ready() -> void:
## 顶部 HUD 的返回主菜单请求。 ## 顶部 HUD 的返回主菜单请求。
func _on_top_bar_hud_sign_return_mainmenu() -> void: func _on_top_bar_hud_sign_return_mainmenu() -> void:
get_tree().change_scene_to_file("res://src/UI/Panels/MainMenu/MainMenu.tscn") get_tree().change_scene_to_file("res://src/UI/Panels/MainMenu/MainMenu.tscn")
## 顶部 HUD 的"管理"按钮请求:打开弟子名录面板。
func _on_top_bar_hud_sign_open_disciples() -> void:
$DisciplesPanel.show_panel()
+6 -2
View File
@@ -2,14 +2,18 @@
[ext_resource type="Script" uid="uid://centrk8l54j7c" path="res://src/Core/MainGame.gd" id="1_8bu23"] [ext_resource type="Script" uid="uid://centrk8l54j7c" path="res://src/Core/MainGame.gd" id="1_8bu23"]
[ext_resource type="PackedScene" uid="uid://dl0tb68vsw3c6" path="res://src/UI/HUD/TopBarHUD.tscn" id="1_g6xmk"] [ext_resource type="PackedScene" uid="uid://dl0tb68vsw3c6" path="res://src/UI/HUD/TopBarHUD.tscn" id="1_g6xmk"]
[ext_resource type="PackedScene" uid="uid://cahbpjpnmbcfv" path="res://src/UI/Panels/RecruitPanel/RecruitPanel.tscn" id="1_k5nrm"] [ext_resource type="PackedScene" uid="uid://d4y7q3m6k2x5n7w2" path="res://src/UI/Panels/RecruitPanel/RecruitPanel.tscn" id="1_k5nrm"]
[ext_resource type="PackedScene" path="res://src/UI/Panels/DisciplesPanel/DisciplesPanel.tscn" id="1_d5cpm"]
[node name="MainGame" type="Node" unique_id=2053561265] [node name="MainGame" type="Node" unique_id=2053561265]
groups = ["game_scene"]
script = ExtResource("1_8bu23") script = ExtResource("1_8bu23")
[node name="TopBarHud" parent="." unique_id=1115394694 instance=ExtResource("1_g6xmk")] [node name="TopBarHud" parent="." unique_id=1115394694 instance=ExtResource("1_g6xmk")]
[node name="RecruitPanel" parent="." unique_id=770000016 instance=ExtResource("1_k5nrm")] [node name="RecruitPanel" parent="." unique_id=770000016 instance=ExtResource("1_k5nrm")]
layout_mode = 3
[node name="DisciplesPanel" parent="." unique_id=780000015 instance=ExtResource("1_d5cpm")]
[connection signal="sign_return_mainmenu" from="TopBarHud" to="." method="_on_top_bar_hud_sign_return_mainmenu"] [connection signal="sign_return_mainmenu" from="TopBarHud" to="." method="_on_top_bar_hud_sign_return_mainmenu"]
[connection signal="sign_open_disciples" from="TopBarHud" to="." method="_on_top_bar_hud_sign_open_disciples"]
+1
View File
@@ -163,4 +163,5 @@ theme_override_font_sizes/font_size = 32
text = "返回" text = "返回"
[connection signal="button_up" from="HBoxContainer/SpeedGroup/Button_Pause" to="." method="_on_button_pause_button_up"] [connection signal="button_up" from="HBoxContainer/SpeedGroup/Button_Pause" to="." method="_on_button_pause_button_up"]
[connection signal="button_up" from="HBoxContainer/Button_StaffManagement" to="." method="_on_button_staff_management_button_up"]
[connection signal="button_up" from="HBoxContainer/Button_Return" to="." method="_on_button_return_button_up"] [connection signal="button_up" from="HBoxContainer/Button_Return" to="." method="_on_button_return_button_up"]
+6
View File
@@ -4,6 +4,8 @@ extends Control
## 请求返回主菜单。 ## 请求返回主菜单。
signal sign_return_mainmenu signal sign_return_mainmenu
## 请求打开弟子名录面板。
signal sign_open_disciples
@onready var time_label: Label = %Value_Time @onready var time_label: Label = %Value_Time
@onready var currency_label: Label = %Value_Currency @onready var currency_label: Label = %Value_Currency
@@ -44,6 +46,10 @@ func _on_reputation_changed(new_value: int, _delta: int) -> void:
func _on_button_return_button_up() -> void: func _on_button_return_button_up() -> void:
sign_return_mainmenu.emit() sign_return_mainmenu.emit()
## 点击"管理"按钮:请求打开弟子名录面板(由 MainGame 处理)。
func _on_button_staff_management_button_up() -> void:
sign_open_disciples.emit()
## 点击暂停/继续按钮,切换后刷新按钮状态。 ## 点击暂停/继续按钮,切换后刷新按钮状态。
func _on_button_pause_button_up() -> void: func _on_button_pause_button_up() -> void:
TimeSystem.toggle_pause() TimeSystem.toggle_pause()
@@ -0,0 +1,79 @@
[gd_scene format=3 uid="uid://b3i7xibxjq303"]
[ext_resource type="Script" path="res://src/UI/Panels/DisciplesPanel/disciple_detail_panel.gd" id="1_script"]
[ext_resource type="FontFile" uid="uid://drt448qncx27p" path="res://assets/fonts/dingliezhuhaifont-20240831GengXinBan)-2.ttf" id="2_font"]
[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_dialog"]
bg_color = Color(0.07, 0.13, 0.12, 0.97)
border_width_left = 2
border_width_top = 2
border_width_right = 2
border_width_bottom = 2
border_color = Color(0.4, 0.72, 0.6, 1)
corner_radius_top_left = 8
corner_radius_top_right = 8
corner_radius_bottom_right = 8
corner_radius_bottom_left = 8
[node name="DiscipleDetailPanel" type="Control" unique_id=781000001]
anchors_preset = 15
anchor_right = 1.0
anchor_bottom = 1.0
grow_horizontal = 2
grow_vertical = 2
script = ExtResource("1_script")
[node name="Backdrop" type="ColorRect" parent="." unique_id=781000002]
anchors_preset = 15
anchor_right = 1.0
anchor_bottom = 1.0
grow_horizontal = 2
grow_vertical = 2
color = Color(0.02, 0.05, 0.05, 0.65)
[node name="CenterContainer" type="CenterContainer" parent="." unique_id=781000003]
anchors_preset = 15
anchor_right = 1.0
anchor_bottom = 1.0
grow_horizontal = 2
grow_vertical = 2
[node name="Panel" type="PanelContainer" parent="CenterContainer" unique_id=781000004]
theme_override_styles/panel = SubResource("StyleBoxFlat_dialog")
[node name="MarginContainer" type="MarginContainer" parent="CenterContainer/Panel" unique_id=781000005]
theme_override_constants/margin_left = 32
theme_override_constants/margin_top = 24
theme_override_constants/margin_right = 32
theme_override_constants/margin_bottom = 24
[node name="VBoxContainer" type="VBoxContainer" parent="CenterContainer/Panel/MarginContainer" unique_id=781000006]
theme_override_constants/separation = 16
[node name="TitleLabel" type="Label" parent="CenterContainer/Panel/MarginContainer/VBoxContainer" unique_id=781000007]
unique_name_in_owner = true
theme_override_fonts/font = ExtResource("2_font")
theme_override_font_sizes/font_size = 30
theme_override_colors/font_color = Color(0.82, 0.95, 0.88, 1)
text = "弟子详情"
[node name="InfoLabel" type="Label" parent="CenterContainer/Panel/MarginContainer/VBoxContainer" unique_id=781000008]
unique_name_in_owner = true
theme_override_fonts/font = ExtResource("2_font")
theme_override_font_sizes/font_size = 22
theme_override_colors/font_color = Color(0.82, 0.95, 0.88, 1)
text = ""
[node name="ButtonRow" type="HBoxContainer" parent="CenterContainer/Panel/MarginContainer/VBoxContainer" unique_id=781000009]
[node name="Spacer" type="Control" parent="CenterContainer/Panel/MarginContainer/VBoxContainer/ButtonRow" unique_id=781000010]
layout_mode = 2
size_flags_horizontal = 3
[node name="Button_Close" type="Button" parent="CenterContainer/Panel/MarginContainer/VBoxContainer/ButtonRow" unique_id=781000011]
unique_name_in_owner = true
theme_override_fonts/font = ExtResource("2_font")
theme_override_font_sizes/font_size = 22
text = "关闭"
[connection signal="button_up" from="CenterContainer/Panel/MarginContainer/VBoxContainer/ButtonRow/Button_Close" to="." method="_on_button_close_button_up"]
@@ -0,0 +1,104 @@
[gd_scene format=3 uid="uid://cahux61qqblje"]
[ext_resource type="Script" path="res://src/UI/Panels/DisciplesPanel/disciples_panel.gd" id="1_script"]
[ext_resource type="FontFile" uid="uid://drt448qncx27p" path="res://assets/fonts/dingliezhuhaifont-20240831GengXinBan)-2.ttf" id="2_font"]
[ext_resource type="PackedScene" uid="uid://b3i7xibxjq303" path="res://src/UI/Panels/DisciplesPanel/DiscipleDetailPanel.tscn" id="3_detail"]
[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_dialog"]
bg_color = Color(0.07, 0.13, 0.12, 0.97)
border_width_left = 2
border_width_top = 2
border_width_right = 2
border_width_bottom = 2
border_color = Color(0.4, 0.72, 0.6, 1)
corner_radius_top_left = 8
corner_radius_top_right = 8
corner_radius_bottom_right = 8
corner_radius_bottom_left = 8
[node name="DisciplesPanel" type="Control" unique_id=1362665771]
layout_mode = 3
anchors_preset = 15
anchor_right = 1.0
anchor_bottom = 1.0
grow_horizontal = 2
grow_vertical = 2
script = ExtResource("1_script")
[node name="Backdrop" type="ColorRect" parent="." unique_id=1162009235]
layout_mode = 1
anchors_preset = 15
anchor_right = 1.0
anchor_bottom = 1.0
grow_horizontal = 2
grow_vertical = 2
color = Color(0.02, 0.05, 0.05, 0.65)
[node name="CenterContainer" type="CenterContainer" parent="." unique_id=47958673]
layout_mode = 1
anchors_preset = 15
anchor_right = 1.0
anchor_bottom = 1.0
grow_horizontal = 2
grow_vertical = 2
[node name="Panel" type="PanelContainer" parent="CenterContainer" unique_id=1341680090]
layout_mode = 2
theme_override_styles/panel = SubResource("StyleBoxFlat_dialog")
[node name="MarginContainer" type="MarginContainer" parent="CenterContainer/Panel" unique_id=1155028384]
layout_mode = 2
theme_override_constants/margin_left = 28
theme_override_constants/margin_top = 20
theme_override_constants/margin_right = 28
theme_override_constants/margin_bottom = 20
[node name="VBoxContainer" type="VBoxContainer" parent="CenterContainer/Panel/MarginContainer" unique_id=523691907]
layout_mode = 2
theme_override_constants/separation = 12
[node name="TitleLabel" type="Label" parent="CenterContainer/Panel/MarginContainer/VBoxContainer" unique_id=780000007]
unique_name_in_owner = true
layout_mode = 2
theme_override_fonts/font = ExtResource("2_font")
theme_override_font_sizes/font_size = 30
theme_override_colors/font_color = Color(0.82, 0.95, 0.88, 1)
text = "弟子名录"
[node name="ScrollContainer" type="ScrollContainer" parent="CenterContainer/Panel/MarginContainer/VBoxContainer" unique_id=780000008]
custom_minimum_size = Vector2(640, 340)
layout_mode = 2
size_flags_vertical = 3
[node name="ListContainer" type="VBoxContainer" parent="CenterContainer/Panel/MarginContainer/VBoxContainer/ScrollContainer" unique_id=780000009]
unique_name_in_owner = true
layout_mode = 2
size_flags_horizontal = 3
[node name="EmptyLabel" type="Label" parent="CenterContainer/Panel/MarginContainer/VBoxContainer" unique_id=780000010]
unique_name_in_owner = true
layout_mode = 2
theme_override_fonts/font = ExtResource("2_font")
theme_override_font_sizes/font_size = 22
theme_override_colors/font_color = Color(0.6, 0.78, 0.7, 1)
text = "暂无弟子"
[node name="ButtonRow" type="HBoxContainer" parent="CenterContainer/Panel/MarginContainer/VBoxContainer" unique_id=780000011]
layout_mode = 2
[node name="Spacer" type="Control" parent="CenterContainer/Panel/MarginContainer/VBoxContainer/ButtonRow" unique_id=780000012]
layout_mode = 2
size_flags_horizontal = 3
[node name="Button_Close" type="Button" parent="CenterContainer/Panel/MarginContainer/VBoxContainer/ButtonRow" unique_id=780000013]
unique_name_in_owner = true
layout_mode = 2
theme_override_fonts/font = ExtResource("2_font")
theme_override_font_sizes/font_size = 22
text = "关闭"
[node name="DiscipleDetailPanel" parent="." unique_id=780000014 instance=ExtResource("3_detail")]
unique_name_in_owner = true
[connection signal="gui_input" from="Backdrop" to="." method="_on_backdrop_gui_input"]
[connection signal="button_up" from="CenterContainer/Panel/MarginContainer/VBoxContainer/ButtonRow/Button_Close" to="." method="hide_panel"]
@@ -0,0 +1,29 @@
## 弟子详情弹窗(挂载于 DiscipleDetailPanel.tscn)。
## 静态展示弟子信息(资质/年龄/灵根/境界/编号/入宗时间),
## 修炼进度等动态内容留待 D5 修炼循环填充。
extends Control
@onready var title_label: Label = %TitleLabel
@onready var info_label: Label = %InfoLabel
func _ready() -> void:
visible = false
## 显示指定弟子的详情。
## @param d: 要展示的弟子
func show_detail(d: Disciple) -> void:
title_label.text = d.name + ("(天灵根)" if d.is_heavenly else "")
info_label.text = "名字:%s\n资质:%s\n年龄:%d\n灵根:%s\n境界:%s\n入宗时间:%s" % [
d.name,
d.get_aptitude_name(),
d.age,
d.get_roots_text(),
d.get_realm_text(),
d.get_join_date_text(),
]
visible = true
## 点击"关闭"按钮。
func _on_button_close_button_up() -> void:
visible = false
@@ -0,0 +1 @@
uid://da0d24brddyxk
@@ -0,0 +1,91 @@
## 弟子名录列表面板(挂载于 DisciplesPanel.tscn)。
## 展示宗门已入宗弟子名录,点条目"详情"打开详情弹窗。
## 监听名录变化信号实时刷新,打开面板不暂停时间。
extends Control
## 资质颜色提示(天骄金色、上品亮青,其余默认)。
const APTITUDE_COLORS := {
Disciple.Aptitude.HEAVENLY: Color(1.0, 0.84, 0.35),
Disciple.Aptitude.SUPERIOR: Color(0.55, 0.9, 0.8),
}
@onready var title_label: Label = %TitleLabel
@onready var list_container: VBoxContainer = %ListContainer
@onready var empty_label: Label = %EmptyLabel
@onready var detail_panel: Control = %DiscipleDetailPanel
func _ready() -> void:
# 名录变化时刷新(面板打开期间也同步)
DiscipleManager.disciples_changed.connect(_on_disciples_changed)
visible = false
## 打开面板并刷新列表(暂停时间推进)。
func show_panel() -> void:
_refresh_list()
TimeSystem.set_paused(true)
visible = true
## 关闭面板(恢复时间推进;子层详情弹窗随父隐藏)。
func hide_panel() -> void:
visible = false
TimeSystem.set_paused(false)
## 点击遮罩关闭面板。
func _on_backdrop_gui_input(event: InputEvent) -> void:
if event is InputEventMouseButton and event.pressed and event.button_index == MOUSE_BUTTON_LEFT:
hide_panel()
## 名录变化时刷新列表。
func _on_disciples_changed() -> void:
if visible:
_refresh_list()
## 重建列表:清空旧条目,按名录顺序逐条生成。
func _refresh_list() -> void:
for child in list_container.get_children():
child.queue_free()
for d in DiscipleManager.disciples:
_add_entry(d)
empty_label.visible = DiscipleManager.disciples.is_empty()
title_label.text = "弟子名录(%d 人)" % DiscipleManager.disciples.size()
## 添加一名弟子的行条目:姓名/资质/灵根/境界 + 详情按钮。
## @param d: 已入宗弟子
func _add_entry(d: Disciple) -> void:
var row := HBoxContainer.new()
row.add_theme_constant_override("separation", 12)
var name_label := Label.new()
name_label.text = d.name
name_label.custom_minimum_size = Vector2(90, 0)
row.add_child(name_label)
var aptitude_label := Label.new()
aptitude_label.text = d.get_aptitude_name() + ("·天灵根" if d.is_heavenly else "")
if APTITUDE_COLORS.has(d.aptitude):
aptitude_label.add_theme_color_override("font_color", APTITUDE_COLORS[d.aptitude])
aptitude_label.custom_minimum_size = Vector2(130, 0)
row.add_child(aptitude_label)
var roots_label := Label.new()
roots_label.text = d.get_roots_text() + "灵根"
roots_label.custom_minimum_size = Vector2(160, 0)
row.add_child(roots_label)
var realm_label := Label.new()
realm_label.text = d.get_realm_text()
realm_label.size_flags_horizontal = Control.SIZE_EXPAND_FILL
row.add_child(realm_label)
var detail_button := Button.new()
detail_button.text = "详情"
detail_button.button_up.connect(_on_detail_button_up.bind(d))
row.add_child(detail_button)
list_container.add_child(row)
## 点击条目"详情"按钮:在子层弹窗中展示该弟子信息。
## @param d: 对应弟子
func _on_detail_button_up(d: Disciple) -> void:
detail_panel.show_detail(d)
@@ -0,0 +1 @@
uid://cfqhusp28ku5x