diff --git a/doc/弟子管理.md b/doc/弟子管理.md index 3fa9b2a..cab430b 100644 --- a/doc/弟子管理.md +++ b/doc/弟子管理.md @@ -115,4 +115,10 @@ func load_from_dict(d) -> void ```mermaid graph TD A[分配属性总量,按正正态分布] --> B[随机分配5种属性,属性之和为1] --> C[分配灵气利用率] -``` \ No newline at end of file +``` + +## 修炼说明 + +每个小境界修炼需要的经验 = 基础经验值 * 属性总量 + +经验获取量 = 基础经验 * 灵气吸收效率 diff --git a/project.godot b/project.godot index 8fa90eb..192998b 100644 --- a/project.godot +++ b/project.godot @@ -18,7 +18,6 @@ config/icon="res://icon.svg" [autoload] GameState="*res://src/Core/GameState.gd" -TimeSystem="*res://src/Core/TimeSystem.gd" Settings="*res://src/Core/Settings.gd" SaveSystem="*res://src/Core/SaveSystem.gd" diff --git a/src/Character/DiscipleManager.gd b/src/Character/DiscipleManager.gd index ac559c5..c9a9581 100644 --- a/src/Character/DiscipleManager.gd +++ b/src/Character/DiscipleManager.gd @@ -1,6 +1,4 @@ # 初始版本不提供添加弟子的接口 - - class_name DiscipleManager extends Node @@ -14,6 +12,7 @@ const INITIAL_ROSTER := [ # 1 玄真(宗主):修士·炼气9层,五行全高,炼丹3级 { "name": "玄真", + "id" : 1, "metal": 45, "wood": 38, "water": 22, "fire": 31, "earth": 19, "efficiency": 85, "realm_level": 1, "sub_realm_level": 9, @@ -23,6 +22,7 @@ const INITIAL_ROSTER := [ # 2 凌霄:修士·炼气4层,金火旺 { "name": "凌霄", + "id" : 2, "metal": 62, "wood": 12, "water": 15, "fire": 44, "earth": 20, "efficiency": 76, "realm_level": 1, "sub_realm_level": 4, @@ -32,6 +32,7 @@ const INITIAL_ROSTER := [ # 3 青禾:凡人带灵根(木47≥20 可修炼破境),药草天赋 { "name": "青禾", + "id" : 3, "metal": 8, "wood": 47, "water": 25, "fire": 12, "earth": 10, "efficiency": 68, "realm_level": 0, "sub_realm_level": 1, @@ -41,6 +42,7 @@ const INITIAL_ROSTER := [ # 4 阿福:凡人杂役(无灵根,五行均<20) { "name": "阿福", + "id" : 4, "metal": 5, "wood": 12, "water": 8, "fire": 9, "earth": 10, "efficiency": 41, "realm_level": 0, "sub_realm_level": 1, @@ -50,6 +52,7 @@ const INITIAL_ROSTER := [ # 5 阿禄:凡人杂役(无灵根) { "name": "阿禄", + "id" : 5, "metal": 3, "wood": 6, "water": 5, "fire": 8, "earth": 15, "efficiency": 35, "realm_level": 0, "sub_realm_level": 1, @@ -61,103 +64,101 @@ const INITIAL_ROSTER := [ var _disciples: Array[Disciple] = [] # 弟子列表 var _master_id: int = 0 # 管理器的发号计数,id 的唯一来源 -# ---- 工厂 ---- +func _enter_tree() -> void: + pass +# ---- 工厂 ---- # 初始编队(复用 _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 +func spawn_initial_roster() -> void: + for data in INITIAL_ROSTER: + var disciple = _create_from_data(data) + if disciple != null: + add_disciple(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 + var copy := data.duplicate() # 可写副本(浅拷贝,顶层加键足够) + copy["id"] = _master_id + _master_id += 1 + var disciple := Disciple.create(copy) + if disciple == null: + push_error("Failed to create disciple from data: %s" % data) + return null + return disciple # ---- 仓储 ---- # 公开接口:添加弟子 func add_disciple(d : Disciple) -> void: - if d == null: - push_error("Cannot add null disciple.") - return - _disciples.append(d) - emit_signal("disciple_added", d) + 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 + 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 + for d in _disciples: + if d.id == id: + return d + return null # 公开接口:获取所有弟子(返回副本,避免外部修改原数组) func get_all() -> Array[Disciple]: - return _disciples.duplicate() # 返回副本,避免外部修改原数组 + 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 + 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) + 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) + 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 + pass # ---- 存档 ---- func to_dict(): - pass + pass func load_from_dict(): - pass - - + pass diff --git a/src/Character/DiscipleManager.gd.uid b/src/Character/DiscipleManager.gd.uid new file mode 100644 index 0000000..9074554 --- /dev/null +++ b/src/Character/DiscipleManager.gd.uid @@ -0,0 +1 @@ +uid://bstiea1nx61b8 diff --git a/src/Core/MainGame.gd b/src/Core/MainGame.gd index 99b2d39..1f5edaa 100644 --- a/src/Core/MainGame.gd +++ b/src/Core/MainGame.gd @@ -1,5 +1,17 @@ extends Node +var cnt := 0; +func _ready() -> void: + %DiscipleManager.spawn_initial_roster() + pass + +func _process(delta: float) -> void: + if cnt >= 1000: + var d : Disciple= %DiscipleManager.get_disciple(2); + %DiscipleManager.remove_disciple(d); + else: + cnt = cnt + 1; + pass func _on_top_bar_hud_sign_return_mainmenu() -> void: if GameState.mode == GameState.GameMode.NEW_GAME: @@ -8,3 +20,13 @@ func _on_top_bar_hud_sign_return_mainmenu() -> void: SaveSystem.save(GameState.profile_id) # 进游戏立即存首档,防开局就丢 get_tree().change_scene_to_file("res://src/UI/Panels/MainMenu/MainMenu.tscn") pass # Replace with function body. + +func _on_disciple_manager_disciple_added(disciple: Disciple) -> void: + $PanelContainer/Manager.add_disciple_card(disciple) + pass # Replace with function body. + + + +func _on_disciple_manager_disciple_removed(disciple: Disciple) -> void: + $PanelContainer/Manager.remove_disciple_card(disciple.id) + pass # Replace with function body. diff --git a/src/Core/MainGame.tscn b/src/Core/MainGame.tscn index ac9ad33..b17ba6e 100644 --- a/src/Core/MainGame.tscn +++ b/src/Core/MainGame.tscn @@ -1,12 +1,14 @@ [gd_scene format=3 uid="uid://c1exffg84ojqx"] -[ext_resource type="Script" uid="uid://cr4by5k8vgkcq" path="res://src/Core/MainGame.gd" id="1_8bu23"] +[ext_resource type="Script" uid="uid://cr4by5k8vgkcq" path="res://src/Core/MainGame.gd" id="1_2atgf"] +[ext_resource type="Script" uid="uid://bstiea1nx61b8" path="res://src/Character/DiscipleManager.gd" id="1_dl5bp"] [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"] +[ext_resource type="Script" uid="uid://b8f8t2phmhq2e" path="res://src/Core/TimeSystem.gd" id="5_58g0x"] [node name="MainGame" type="Node" unique_id=42551660] -script = ExtResource("1_8bu23") +script = ExtResource("1_2atgf") [node name="MainHud" parent="." unique_id=836125968 instance=ExtResource("2_8bu23")] size_flags_horizontal = 3 @@ -23,4 +25,16 @@ theme_override_styles/panel = ExtResource("3_dl5bp") [node name="Manager" parent="PanelContainer" unique_id=360636457 instance=ExtResource("4_58g0x")] layout_mode = 2 +[node name="DiscipleManager" type="Node" parent="." unique_id=1485476702] +unique_name_in_owner = true +script = ExtResource("1_dl5bp") +metadata/_custom_type_script = "uid://bstiea1nx61b8" + +[node name="TimeSystem" type="Node" parent="." unique_id=1280036358] +unique_name_in_owner = true +script = ExtResource("5_58g0x") +metadata/_custom_type_script = "uid://b8f8t2phmhq2e" + +[connection signal="disciple_added" from="DiscipleManager" to="." method="_on_disciple_manager_disciple_added"] +[connection signal="disciple_removed" from="DiscipleManager" to="." method="_on_disciple_manager_disciple_removed"] [connection signal="sign_return_mainmenu" from="MainHud" to="." method="_on_top_bar_hud_sign_return_mainmenu"] diff --git a/src/Core/TimeSystem.gd b/src/Core/TimeSystem.gd index 44a10d2..74e2a2a 100644 --- a/src/Core/TimeSystem.gd +++ b/src/Core/TimeSystem.gd @@ -1,6 +1,7 @@ ## 全局时间系统(Autoload 单例)。 ## 负责游戏时间的推进与播报,仅在世界场景中运行。 ## 历法:12 月 × 30 日 = 360 日/年。 +class_name TimeSystem extends Node const MONTHS_PER_YEAR := 12 diff --git a/src/Data/Models/Disciple.gd b/src/Data/Models/Disciple.gd index 50f86ad..2c70226 100644 --- a/src/Data/Models/Disciple.gd +++ b/src/Data/Models/Disciple.gd @@ -137,7 +137,7 @@ var total_amount: int: ## 构造:按 data 覆盖属性,数值属性经 RANGES 夹取;缺键保留字段默认值。 ## @param data: 键名与属性名一致,如 {"id": 1, "name": "玄真", "metal": 60} -func _init(data: Dictionary[String, Variant] = {}) -> void: +func _init(data: Dictionary = {}) -> void: if data.has("id"): id = int(data["id"]) if data.has("name"): @@ -187,12 +187,12 @@ func set_attr(key: String, value: int) -> bool: if key == "realm_level": # 大境界变化后,小境界不得超过新境界的上限(如落入凡人则强制 1) set("realm_level", v) - var max_sub := SUB_MAX_BY_REALM[v] + var max_sub : int = 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"))] + var max_sub : int = SUB_MAX_BY_REALM[int(get("realm_level"))] set("sub_realm_level", clampi(v, 1, max_sub)) else: set(key, v) diff --git a/src/UI/HUD/MainSceneHud.gd b/src/UI/HUD/MainSceneHud.gd index d64c4ee..be958d7 100644 --- a/src/UI/HUD/MainSceneHud.gd +++ b/src/UI/HUD/MainSceneHud.gd @@ -6,8 +6,8 @@ extends Control signal sign_return_mainmenu func _ready() -> void: - time_label.text = TimeSystem.get_date_text() - TimeSystem.month_passed.connect(_on_TimeMonthPassed) + time_label.text = %TimeSystem.get_date_text() + %TimeSystem.month_passed.connect(_on_TimeMonthPassed) pass func _on_ButtonEsc_ButtonUp() -> void: @@ -20,5 +20,5 @@ func _on_TimeMonthPassed(_year: int, _month: int) -> void: func _on_button_continue_button_up() -> void: - TimeSystem.advance_month() + %TimeSystem.advance_month() pass # Replace with function body. diff --git a/src/UI/Panels/Manager/DisciplePanel/DiscipleInformation.gd b/src/UI/Panels/Manager/DisciplePanel/DiscipleInformation.gd index dbab5cc..1e69d5e 100644 --- a/src/UI/Panels/Manager/DisciplePanel/DiscipleInformation.gd +++ b/src/UI/Panels/Manager/DisciplePanel/DiscipleInformation.gd @@ -1,11 +1,14 @@ extends Control +var disciple_id: int; + func _ready() -> void: custom_maximum_size = Vector2(180, 320); custom_minimum_size = Vector2(180, 240); pass; func set_info(info: Dictionary): + disciple_id = info['id']; $VBoxContainer/HBoxContainer/Name.text = info['name']; $VBoxContainer/HBoxContainer2/Level.text = info['level'] $VBoxContainer/HBoxContainer3/GridContainer/MetalVal.text = str(info['metal']) @@ -13,4 +16,6 @@ func set_info(info: Dictionary): $VBoxContainer/HBoxContainer3/GridContainer/WaterVal.text = str(info['water']) $VBoxContainer/HBoxContainer3/GridContainer/FireVal.text = str(info['fire']) $VBoxContainer/HBoxContainer3/GridContainer/EarthVal.text = str(info['earth']) + + #print(info) pass diff --git a/src/UI/Panels/Manager/DisciplePanel/DiscipleInformationPanel.tscn b/src/UI/Panels/Manager/DisciplePanel/DiscipleInformationPanel.tscn index 3aad7ed..6f39c65 100644 --- a/src/UI/Panels/Manager/DisciplePanel/DiscipleInformationPanel.tscn +++ b/src/UI/Panels/Manager/DisciplePanel/DiscipleInformationPanel.tscn @@ -15,6 +15,7 @@ grow_vertical = 2 size_flags_horizontal = 3 theme_override_styles/panel = ExtResource("1_po3i5") script = ExtResource("2_6fig8") +metadata/discipleId = 0 [node name="VBoxContainer" type="VBoxContainer" parent="." unique_id=1066350214] layout_mode = 2 diff --git a/src/UI/Panels/Manager/Manager.gd b/src/UI/Panels/Manager/Manager.gd index 150150e..a8c2c0a 100644 --- a/src/UI/Panels/Manager/Manager.gd +++ b/src/UI/Panels/Manager/Manager.gd @@ -1,6 +1,5 @@ extends Control -var test_disciple_table : Array[Dictionary] = [] # 弟子 var test_field_table : Array[Dictionary] = [] # 建筑 var test_order_table : Array[Dictionary] = [] # 订单 var test_alchemy_table : Array[Dictionary] = [] # 炼丹配方 @@ -22,7 +21,6 @@ func _ready() -> void: var manager_panel_scene := load("res://src/UI/Panels/Manager/ManagerPanel.tscn") as PackedScene var inventory_panel_scene := load("res://src/UI/Panels/Manager/InventoryPanel/InventoryPanel.tscn") as PackedScene - sect_resources["弟子"] = manager_panel_scene.instantiate(); sect_resources["弟子"].name = "弟子"; $TabContainer.add_child(sect_resources["弟子"]); @@ -42,12 +40,6 @@ func _ready() -> void: sect_resources["库房"] = inventory_panel_scene.instantiate(); sect_resources["库房"].name = "库房"; $TabContainer.add_child(sect_resources["库房"]); - - for e in test_disciple_table: - var d := disciple_info_scene.instantiate() - d.set_info(e) - sect_resources["弟子"].add_info_card(d) - pass for e in test_field_table: var d := field_info_scene.instantiate() @@ -66,25 +58,12 @@ func _ready() -> void: d.set_alchemy_info(e) sect_resources['炼丹'].add_info_card(d) pass - - - pass func init_test_data() -> void: - test_disciple_table.append_array([ - {"name": "赵一", "gender": "男", "level": "练气一层", "metal": 0.5, "wood": 0.9, "water": 0.1, "fire": 0.2, "earth": 0.15}, - {"name": "钱二", "gender": "男", "level": "练气一层", "metal": 0.5, "wood": 0.9, "water": 0.1, "fire": 0.2, "earth": 0.15}, - {"name": "孙三", "gender": "男", "level": "练气一层", "metal": 0.5, "wood": 0.9, "water": 0.1, "fire": 0.2, "earth": 0.15}, - {"name": "李四", "gender": "男", "level": "练气一层", "metal": 0.5, "wood": 0.9, "water": 0.1, "fire": 0.2, "earth": 0.15}, - {"name": "周五", "gender": "男", "level": "练气一层", "metal": 0.5, "wood": 0.9, "water": 0.1, "fire": 0.2, "earth": 0.15}, - {"name": "吴六", "gender": "男", "level": "练气一层", "metal": 0.5, "wood": 0.9, "water": 0.1, "fire": 0.2, "earth": 0.15}, - {"name": "郑七", "gender": "男", "level": "练气一层", "metal": 0.5, "wood": 0.9, "water": 0.1, "fire": 0.2, "earth": 0.15} - ]) - test_field_table.append_array([ {"name": "灵田", "弟子": 0}, {"name": "灵田", "弟子": 1}, @@ -129,3 +108,30 @@ func init_test_data() -> void: test_inventory.append_array([{"草药": test_sub_inventory1}, {"粮食": test_sub_inventory2}, {"丹药": test_sub_inventory3}]); pass + +func add_disciple_card(d : Disciple) -> void: + var ds := disciple_info_scene.instantiate() + var info := { + "id": d.id, + "name": d.name, + "gender" : "x", + "level" : d.realm_name(), + "metal" : int(d.get_attr("metal")), + "wood" : int(d.get_attr("wood")), + "water" : int(d.get_attr("water")), + "fire" : int(d.get_attr("fire")), + "earth" : int(d.get_attr("earth")) + } + ds.set_info(info) + sect_resources["弟子"].add_info_card(ds) + pass + +func remove_disciple_card(id: int) -> void: + var panel: Control = sect_resources["弟子"] # ManagerPanel 页 + var flow: Node = panel.get_node("ScrollContainer/HFlowContainer") + + for child in flow.get_children(): + if child.disciple_id == id: # 指定项匹配 + flow.remove_child(child) # 1. 先从树摘除 + child.queue_free() # 2. 再排队销毁 + return # 匹配唯一,删完即走