feat: 建筑面板——资源按钮入口+建造升级产出(D6 核心)

This commit is contained in:
2026-08-09 16:13:34 +08:00
parent 8b31866d8e
commit 9e1bd055d5
15 changed files with 337 additions and 1 deletions
+1 -1
View File
@@ -2,7 +2,7 @@
> 对应开发计划表 D6(8/8):① 灵石矿场景 ② 建造→产出 ③ 升级。交付标准:建筑产资源,升级加速。
> 本文档只描述设计,不包含代码。
> 状态:步骤 1–2 已实现(食物资源/HUD 灵米显示/开局 500 灵石+100 灵米配置驱动),步骤 3+ 待实现
> 状态:步骤 12、4、6 已实现(食物资源/HUD 灵米/开局赠送配置化;Building+BuildingManager 建造升级产出;BuildingsPanel 面板+资源按钮+暂停恢复),步骤 3 部分(建筑参数配置已加)、5 待实现
---
+1
View File
@@ -23,6 +23,7 @@ SectManager="*res://src/Core/SectManager.gd"
GameConfig="*res://src/Core/GameConfig.gd"
DiscipleManager="*res://src/Character/DiscipleManager.gd"
CultivationManager="*res://src/Character/CultivationManager.gd"
BuildingManager="*res://src/Sect/BuildingManager.gd"
[display]
+13
View File
@@ -34,3 +34,16 @@ hud_font_size=24 ; HUD 字体大小
[building] ; 建筑系统通用
starting_spirit_stones=500 ; 开局赠送灵石
starting_food=100 ; 开局赠送灵米
max_level=10 ; 建筑等级上限
[building_mine] ; 灵石矿
cost=200 ; 建造价(灵石)
output=10 ; 日产出(灵石)
upgrade_cost_x=2.0 ; 升级价格倍率
upgrade_output_x=1.5 ; 升级产出倍率
[building_field] ; 灵田
cost=100 ; 建造价(灵石)
output=5 ; 日产出(灵米)
upgrade_cost_x=2.0 ; 升级价格倍率
upgrade_output_x=1.5 ; 升级产出倍率
+17
View File
@@ -47,6 +47,14 @@ var hud_font_size := 32
var starting_spirit_stones := 500
## 开局赠送灵米(新游戏发放)。
var starting_food := 100
## 建筑等级上限。
var building_max_level := 10
## 各建筑参数表(键 = 建筑类型名,值 = {cost, output, upgrade_cost_x, upgrade_output_x})。
var building_params: Dictionary = {
"mine": {"cost": 200, "output": 10, "upgrade_cost_x": 2.0, "upgrade_output_x": 1.5},
"field": {"cost": 100, "output": 5, "upgrade_cost_x": 2.0, "upgrade_output_x": 1.5},
}
## 各难度资质权重表(键 = 难度档位,值 = 下品/中品/上品/天骄 权重)。
var aptitude_weights_by_difficulty: Dictionary = {
@@ -104,6 +112,15 @@ func _load_config() -> void:
# 建筑系统
starting_spirit_stones = cfg.get_value("building", "starting_spirit_stones", starting_spirit_stones)
starting_food = cfg.get_value("building", "starting_food", starting_food)
building_max_level = cfg.get_value("building", "max_level", building_max_level)
for key in building_params.keys():
var p: Dictionary = building_params[key]
building_params[key] = {
"cost": cfg.get_value("building_" + key, "cost", p["cost"]),
"output": cfg.get_value("building_" + key, "output", p["output"]),
"upgrade_cost_x": cfg.get_value("building_" + key, "upgrade_cost_x", p["upgrade_cost_x"]),
"upgrade_output_x": cfg.get_value("building_" + key, "upgrade_output_x", p["upgrade_output_x"]),
}
var apt_raw: Variant = cfg.get_value("cultivation", "aptitude_speed", [])
if apt_raw is Array and apt_raw.size() == aptitude_speed.size():
var arr: Array[float] = []
+6
View File
@@ -9,6 +9,8 @@ func _ready() -> void:
TimeSystem.apply_default_speed()
# 开局招募:清空旧数据 → 生成 5 名候选 → 暂停时间等待选择(读档流程接入后由存档还原)
DiscipleManager.start_new_game()
# 开局建筑:清空旧建筑(读档流程接入后由存档还原)
BuildingManager.reset()
# 开局赠送资源(数量来自配置;读档流程接入后由存档还原)
SectManager.add_spirit_stones(GameConfig.starting_spirit_stones)
SectManager.add_food(GameConfig.starting_food)
@@ -20,3 +22,7 @@ func _on_top_bar_hud_sign_return_mainmenu() -> void:
## 顶部 HUD 的"管理"按钮请求:打开弟子名录面板。
func _on_top_bar_hud_sign_open_disciples() -> void:
$DisciplesPanel.show_panel()
## 顶部 HUD 的"资源"按钮请求:打开建筑面板。
func _on_top_bar_hud_sign_open_buildings() -> void:
$BuildingsPanel.show_panel()
+4
View File
@@ -4,6 +4,7 @@
[ext_resource type="PackedScene" uid="uid://cahux61qqblje" path="res://src/UI/Panels/DisciplesPanel/DisciplesPanel.tscn" id="1_d5cpm"]
[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" path="res://src/UI/Panels/BuildingsPanel/BuildingsPanel.tscn" id="1_b9dpm"]
[ext_resource type="Texture2D" uid="uid://cweu17mwblxmg" path="res://assets/sprites/ui/宗门后山.png" id="2_2atgf"]
[ext_resource type="Texture2D" uid="uid://dnjuv1btbxm4o" path="res://assets/sprites/ui/宗门全景.png" id="5_58g0x"]
@@ -35,5 +36,8 @@ layout_mode = 3
[node name="DisciplesPanel" parent="." unique_id=780000015 instance=ExtResource("1_d5cpm")]
[node name="BuildingsPanel" parent="." unique_id=795000012 instance=ExtResource("1_b9dpm")]
[connection signal="sign_open_buildings" from="TopBarHud" to="." method="_on_top_bar_hud_sign_open_buildings"]
[connection signal="sign_open_disciples" from="TopBarHud" to="." method="_on_top_bar_hud_sign_open_disciples"]
[connection signal="sign_return_mainmenu" from="TopBarHud" to="." method="_on_top_bar_hud_sign_return_mainmenu"]
+63
View File
@@ -0,0 +1,63 @@
## 建筑管理器(Autoload 单例)。
## 负责建筑建造/升级与每日产出结算,产出直接进入 SectManager 资源。
## 本期每种建筑至多一座(面板按类型一行展示);多实例留待后续扩展。
extends Node
## 建筑列表变化时发出(建造/升级/重置)。
signal buildings_changed
## 宗门建筑列表。
var buildings: Array[Building] = []
func _ready() -> void:
# 每日推进时结算各建筑产出
TimeSystem.day_passed.connect(_on_day_passed)
## 建造建筑:检查灵石足够 → 扣费入列。
## 同类型已存在时拒绝(本期一座一型)。
## @param type: 建筑种类
## @return 是否建造成功
func build(type: Building.Type) -> bool:
if _find(type) != null:
return false
var b := Building.new()
b.type = type
if SectManager.spirit_stones < b.get_build_cost():
return false
SectManager.remove_spirit_stones(b.get_build_cost())
buildings.append(b)
buildings_changed.emit()
return true
## 升级建筑:检查灵石足够 → 扣费提升等级。
## @param b: 要升级的建筑
## @return 是否升级成功
func upgrade(b: Building) -> bool:
if b.is_max_level() or SectManager.spirit_stones < b.get_upgrade_cost():
return false
SectManager.remove_spirit_stones(b.get_upgrade_cost())
b.level += 1
buildings_changed.emit()
return true
## 清空建筑列表(新游戏开局调用)。
func reset() -> void:
buildings.clear()
buildings_changed.emit()
## 每日结算:各建筑产出进入 SectManager 对应资源。
func _on_day_passed(_year: int, _month: int, _day: int) -> void:
for b in buildings:
match b.type:
Building.Type.MINE:
SectManager.add_spirit_stones(b.get_output())
Building.Type.FIELD:
SectManager.add_food(b.get_output())
## 查找指定类型的建筑,不存在返回 null。
func _find(type: Building.Type) -> Building:
for b in buildings:
if b.type == type:
return b
return null
+1
View File
@@ -0,0 +1 @@
uid://6hi0wog110up
+54
View File
@@ -0,0 +1,54 @@
## 建筑数据模型(纯数据,不挂在场景树上)。
## 价格与产出不在建筑上存副本,运行时按 type+level 从 GameConfig 计算。
class_name Building
extends Resource
## 建筑种类。
enum Type { MINE, FIELD } # 灵石矿 / 灵田
## 建筑类型。
var type: Type = Type.MINE
## 等级(1 起)。
var level: int = 1
## 建筑类型键(对应 GameConfig.building_params 字典键)。
static func get_type_key(t: Type) -> String:
match t:
Type.MINE:
return "mine"
Type.FIELD:
return "field"
return ""
## 建筑显示名称。
static func get_type_name(t: Type) -> String:
match t:
Type.MINE:
return "灵石矿"
Type.FIELD:
return "灵田"
return ""
## 建造价格(灵石)。
func get_build_cost() -> int:
return get_build_cost_for(type)
## 建造价格(静态查询,无需实例)。
static func get_build_cost_for(t: Type) -> int:
var p: Dictionary = GameConfig.building_params[get_type_key(t)]
return int(p["cost"])
## 日产出量 = 基础产出 × 升级产出倍率^(等级-1)。
func get_output() -> int:
var p: Dictionary = GameConfig.building_params[get_type_key(type)]
return int(p["output"] * pow(p["upgrade_output_x"], level - 1))
## 升级价格 = 建造价 × 升级价格倍率^等级。
func get_upgrade_cost() -> int:
var p: Dictionary = GameConfig.building_params[get_type_key(type)]
return int(p["cost"] * pow(p["upgrade_cost_x"], level))
## 是否已达等级上限。
func is_max_level() -> bool:
return level >= GameConfig.building_max_level
+1
View File
@@ -0,0 +1 @@
uid://dmfhbyjwngqh3
+1
View File
@@ -224,4 +224,5 @@ size_flags_stretch_ratio = 75.0
[connection signal="button_up" from="VBoxContainer/HBoxContainer2/SpeedGroup/Button_Pause" to="." method="_on_button_pause_button_up"]
[connection signal="button_up" from="VBoxContainer/HBoxContainer2/Button_StaffManagement" to="." method="_on_button_staff_management_button_up"]
[connection signal="button_up" from="VBoxContainer/HBoxContainer2/Button_ResourcesManagement" to="." method="_on_button_resources_management_button_up"]
[connection signal="button_up" from="VBoxContainer/HBoxContainer2/Button_Return" to="." method="_on_button_return_button_up"]
+6
View File
@@ -6,6 +6,8 @@ extends Control
signal sign_return_mainmenu
## 请求打开弟子名录面板。
signal sign_open_disciples
## 请求打开建筑面板。
signal sign_open_buildings
@onready var time_label: Label = %Value_Time
@onready var currency_label: Label = %Value_Currency
@@ -62,6 +64,10 @@ func _on_button_return_button_up() -> void:
func _on_button_staff_management_button_up() -> void:
sign_open_disciples.emit()
## 点击"资源"按钮:请求打开建筑面板(由 MainGame 处理)。
func _on_button_resources_management_button_up() -> void:
sign_open_buildings.emit()
## 点击暂停/继续按钮,切换后刷新按钮状态。
func _on_button_pause_button_up() -> void:
TimeSystem.toggle_pause()
@@ -0,0 +1,77 @@
[gd_scene format=3 uid="uid://b9k4v8q2x5n3m7p6"]
[ext_resource type="Script" path="res://src/UI/Panels/BuildingsPanel/buildings_panel.gd" id="1_script"]
[ext_resource type="FontFile" uid="uid://4su41gib4l5u" path="res://assets/fonts/lxgw-wenkai-v1.522/LXGWWenKaiMono-Medium.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="BuildingsPanel" type="Control" unique_id=795000001]
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=795000002]
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=795000003]
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=795000004]
theme_override_styles/panel = SubResource("StyleBoxFlat_dialog")
[node name="MarginContainer" type="MarginContainer" parent="CenterContainer/Panel" unique_id=795000005]
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=795000006]
theme_override_constants/separation = 12
[node name="TitleLabel" type="Label" parent="CenterContainer/Panel/MarginContainer/VBoxContainer" unique_id=795000007]
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="ListContainer" type="VBoxContainer" parent="CenterContainer/Panel/MarginContainer/VBoxContainer" unique_id=795000008]
unique_name_in_owner = true
theme_override_constants/separation = 10
[node name="ButtonRow" type="HBoxContainer" parent="CenterContainer/Panel/MarginContainer/VBoxContainer" unique_id=795000009]
[node name="Spacer" type="Control" parent="CenterContainer/Panel/MarginContainer/VBoxContainer/ButtonRow" unique_id=795000010]
layout_mode = 2
size_flags_horizontal = 3
[node name="Button_Close" type="Button" parent="CenterContainer/Panel/MarginContainer/VBoxContainer/ButtonRow" unique_id=795000011]
unique_name_in_owner = true
theme_override_fonts/font = ExtResource("2_font")
theme_override_font_sizes/font_size = 22
text = "关闭"
[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,91 @@
## 建筑面板(挂载于 BuildingsPanel.tscn)。
## 展示可建/已建建筑(灵石矿/灵田),提供建造与升级操作。
## 打开面板暂停时间,关闭恢复;监听建筑变化信号实时刷新。
extends Control
@onready var title_label: Label = %TitleLabel
@onready var list_container: VBoxContainer = %ListContainer
func _ready() -> void:
BuildingManager.buildings_changed.connect(_on_buildings_changed)
visible = false
## 打开面板并刷新列表(暂停时间推进)。
func show_panel() -> void:
_refresh()
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_buildings_changed() -> void:
if visible:
_refresh()
## 重建列表:按建筑类型逐行生成(未建显示建造按钮,已建显示等级/产出/升级按钮)。
func _refresh() -> void:
for child in list_container.get_children():
child.queue_free()
title_label.text = "宗门建筑"
for type in [Building.Type.MINE, Building.Type.FIELD]:
_add_row(type)
## 添加一行建筑条目。
## @param type: 建筑种类
func _add_row(type: Building.Type) -> void:
var b := BuildingManager._find(type)
var row := HBoxContainer.new()
row.add_theme_constant_override("separation", 12)
var name_label := Label.new()
name_label.text = Building.get_type_name(type)
name_label.custom_minimum_size = Vector2(140, 0)
row.add_child(name_label)
var info_label := Label.new()
info_label.size_flags_horizontal = Control.SIZE_EXPAND_FILL
if b == null:
info_label.text = "未建造"
else:
info_label.text = "等级 %d 日产出 %d" % [b.level, b.get_output()]
row.add_child(info_label)
var btn := Button.new()
if b == null:
var cost := Building.get_build_cost_for(type)
btn.text = "建造(%d 灵石)" % cost
btn.disabled = SectManager.spirit_stones < cost
btn.button_up.connect(_on_build_button_up.bind(type))
else:
if b.is_max_level():
btn.text = "已满级"
btn.disabled = true
else:
btn.text = "升级(%d 灵石)" % b.get_upgrade_cost()
btn.disabled = SectManager.spirit_stones < b.get_upgrade_cost()
btn.button_up.connect(_on_upgrade_button_up.bind(b))
row.add_child(btn)
list_container.add_child(row)
## 点击建造按钮。
## @param type: 建筑种类
func _on_build_button_up(type: Building.Type) -> void:
if BuildingManager.build(type):
_refresh()
## 点击升级按钮。
## @param b: 要升级的建筑
func _on_upgrade_button_up(b: Building) -> void:
if BuildingManager.upgrade(b):
_refresh()
@@ -0,0 +1 @@
uid://bhvcvw4ufjhxv