feat: 建筑面板——资源按钮入口+建造升级产出(D6 核心)
This commit is contained in:
@@ -2,7 +2,7 @@
|
||||
|
||||
> 对应开发计划表 D6(8/8):① 灵石矿场景 ② 建造→产出 ③ 升级。交付标准:建筑产资源,升级加速。
|
||||
> 本文档只描述设计,不包含代码。
|
||||
> 状态:步骤 1–2 已实现(食物资源/HUD 灵米显示/开局 500 灵石+100 灵米配置驱动),步骤 3+ 待实现
|
||||
> 状态:步骤 1–2、4、6 已实现(食物资源/HUD 灵米/开局赠送配置化;Building+BuildingManager 建造升级产出;BuildingsPanel 面板+资源按钮+暂停恢复),步骤 3 部分(建筑参数配置已加)、5 待实现
|
||||
|
||||
---
|
||||
|
||||
|
||||
@@ -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]
|
||||
|
||||
|
||||
@@ -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 ; 升级产出倍率
|
||||
|
||||
@@ -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] = []
|
||||
|
||||
@@ -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,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"]
|
||||
|
||||
@@ -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
|
||||
@@ -0,0 +1 @@
|
||||
uid://6hi0wog110up
|
||||
@@ -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
|
||||
@@ -0,0 +1 @@
|
||||
uid://dmfhbyjwngqh3
|
||||
@@ -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,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
|
||||
Reference in New Issue
Block a user