From b807e9897d4d42696066ee20c1d776b5e4b24ef2 Mon Sep 17 00:00:00 2001 From: gamer147 Date: Wed, 8 Apr 2026 18:44:58 -0400 Subject: [PATCH] Refactored unit --- prefabs/deployed_unit.tscn | 91 +++++++++++++++++++ prefabs/unit.tscn | 5 +- scenes/views/main_menu_view.tscn | 21 ++--- .../battle/combat_engine/combat_proposal.gd | 2 +- scripts/battle/combat_engine/combat_system.gd | 60 ++++++------ .../battle/combat_tactics/combat_tactic.gd | 2 +- .../unit_matching_combat_tactic_range.gd | 2 +- .../tactics/attack_combat_tactic.gd | 4 +- .../tactics/defend_combat_tactic.gd | 2 +- scripts/battle/combat_ui.gd | 34 +++---- .../battle/deployed_units/deployed_unit.gd | 47 +++++++++- .../deployed_units/deployed_unit_stats.gd | 14 +-- scripts/battle/map/combat_map.gd | 21 +++-- scripts/battle/player_controller.gd | 28 +++--- scripts/battle/strategy_phase.gd | 2 +- scripts/units/unit.gd | 58 +----------- 16 files changed, 239 insertions(+), 154 deletions(-) create mode 100644 prefabs/deployed_unit.tscn diff --git a/prefabs/deployed_unit.tscn b/prefabs/deployed_unit.tscn new file mode 100644 index 0000000..4f67ee2 --- /dev/null +++ b/prefabs/deployed_unit.tscn @@ -0,0 +1,91 @@ +[gd_scene format=3 uid="uid://b6a7nlnf58mc4"] + +[ext_resource type="Script" uid="uid://cmh4lphvboggy" path="res://scripts/battle/deployed_units/deployed_unit.gd" id="1_cq4v0"] +[ext_resource type="Texture2D" uid="uid://cw5su6lignryo" path="res://assets/sprites/flag.png" id="2_fhs1y"] +[ext_resource type="Shader" uid="uid://bd8ki8xwym5nc" path="res://shaders/chroma_key.gdshader" id="3_fhs1y"] +[ext_resource type="Texture2D" uid="uid://dyutp4m5d53gd" path="res://assets/sprites/CP002AA.BMP" id="3_on614"] + +[sub_resource type="GDScript" id="GDScript_on614"] +resource_name = "UnitSelectorHandler" +script/source = "extends ColorRect + +func _unit_selected_changed(_deployed: DeployedUnit, selected: bool) -> void: + visible = selected +" + +[sub_resource type="GDScript" id="GDScript_fhs1y"] +resource_name = "AllegianceIndicatorManager" +script/source = "extends Sprite2D + + +func _on_unit_unit_allegiance_changed(_deployed: DeployedUnit, allegiance: UnitAllegiance) -> void: + self_modulate = allegiance.color +" + +[sub_resource type="ShaderMaterial" id="ShaderMaterial_fhs1y"] +shader = ExtResource("3_fhs1y") +shader_parameter/key_color = Color(0, 1, 0, 1) +shader_parameter/threshold = 0.01 + +[sub_resource type="AtlasTexture" id="AtlasTexture_fhs1y"] +atlas = ExtResource("3_on614") +region = Rect2(0, 0, 40, 50) + +[sub_resource type="AtlasTexture" id="AtlasTexture_4j20j"] +atlas = ExtResource("3_on614") +region = Rect2(40, 0, 40, 50) + +[sub_resource type="AtlasTexture" id="AtlasTexture_v0xod"] +atlas = ExtResource("3_on614") +region = Rect2(0, 50, 40, 50) + +[sub_resource type="AtlasTexture" id="AtlasTexture_50p1h"] +atlas = ExtResource("3_on614") +region = Rect2(40, 50, 40, 50) + +[sub_resource type="SpriteFrames" id="SpriteFrames_7jqdg"] +animations = [{ +"frames": [{ +"duration": 1.0, +"texture": SubResource("AtlasTexture_fhs1y") +}, { +"duration": 1.0, +"texture": SubResource("AtlasTexture_4j20j") +}, { +"duration": 1.0, +"texture": SubResource("AtlasTexture_v0xod") +}, { +"duration": 1.0, +"texture": SubResource("AtlasTexture_50p1h") +}], +"loop": true, +"name": &"idle", +"speed": 5.0 +}] + +[node name="DeployedUnit" type="Node2D" unique_id=1893234933 groups=["deployed_units"]] +script = ExtResource("1_cq4v0") +metadata/_custom_type_script = "uid://cmh4lphvboggy" + +[node name="SelectionIndicator" type="ColorRect" parent="." unique_id=1313394023] +visible = false +offset_right = 100.0 +offset_bottom = 100.0 +color = Color(1, 1, 0.3019608, 0.36078432) +script = SubResource("GDScript_on614") + +[node name="AllegianceIndicator" type="Sprite2D" parent="." unique_id=1567439632] +z_index = 2 +texture = ExtResource("2_fhs1y") +offset = Vector2(24, 24) +script = SubResource("GDScript_fhs1y") + +[node name="AnimatedSprite2D" type="AnimatedSprite2D" parent="." unique_id=1796991032] +material = SubResource("ShaderMaterial_fhs1y") +position = Vector2(50, 50) +sprite_frames = SubResource("SpriteFrames_7jqdg") +animation = &"idle" +autoplay = "idle" + +[connection signal="unit_allegiance_changed" from="." to="AllegianceIndicator" method="_on_unit_unit_allegiance_changed"] +[connection signal="unit_selected_changed" from="." to="SelectionIndicator" method="_unit_selected_changed"] diff --git a/prefabs/unit.tscn b/prefabs/unit.tscn index 94e1379..0addf09 100644 --- a/prefabs/unit.tscn +++ b/prefabs/unit.tscn @@ -1,4 +1,4 @@ -[gd_scene format=3 uid="uid://b6a7nlnf58mc4"] +[gd_scene format=3 uid="uid://dy0s7rfs4i64y"] [ext_resource type="Script" uid="uid://c016mxgatcpse" path="res://scripts/units/unit.gd" id="1_cq4v0"] [ext_resource type="Texture2D" uid="uid://cw5su6lignryo" path="res://assets/sprites/flag.png" id="2_fhs1y"] @@ -86,6 +86,3 @@ position = Vector2(50, 50) sprite_frames = SubResource("SpriteFrames_7jqdg") animation = &"idle" autoplay = "idle" - -[connection signal="unit_allegiance_changed" from="." to="AllegianceIndicator" method="_on_unit_unit_allegiance_changed"] -[connection signal="unit_selected_changed" from="." to="SelectionIndicator" method="_unit_selected_changed"] diff --git a/scenes/views/main_menu_view.tscn b/scenes/views/main_menu_view.tscn index 280a2a0..c9b1338 100644 --- a/scenes/views/main_menu_view.tscn +++ b/scenes/views/main_menu_view.tscn @@ -76,7 +76,6 @@ resource_name = "StartButton" script/source = "extends TextureButton const COMBAT_SCENE = preload(\"res://scenes/views/battle_view.tscn\") -const UNIT_SCENE = preload(\"res://prefabs/unit.tscn\") const PLAYER_ALLEGIANCE = preload(\"res://resources/allegiance_types/player_allegiance.tres\") const ENEMY_ALLEGIANCE = preload(\"res://resources/allegiance_types/enemy_allegiance.tres\") @@ -85,18 +84,18 @@ func _pressed() -> void: var combat_instance := COMBAT_SCENE.instantiate() var combat_map: CombatMap = combat_instance.find_child(\"CombatMap\") - var player_unit: Unit = UNIT_SCENE.instantiate() - player_unit.stat_template = UnitStats.new() - player_unit.info_template = UnitInfo.new() - player_unit.info_template.name = \"Putit\" - player_unit.allegiance_template = PLAYER_ALLEGIANCE + var player_unit := Unit.new() + player_unit.stats = UnitStats.new() + player_unit.info = UnitInfo.new() + player_unit.info.name = \"Putit\" + player_unit.allegiance = PLAYER_ALLEGIANCE combat_map.deploy_unit(player_unit, Vector2i(3, 3)) - var enemy_unit: Unit = UNIT_SCENE.instantiate() - enemy_unit.stat_template = UnitStats.new() - enemy_unit.info_template = UnitInfo.new() - enemy_unit.info_template.name = \"Putit\" - enemy_unit.allegiance_template = ENEMY_ALLEGIANCE + var enemy_unit := Unit.new() + enemy_unit.stats = UnitStats.new() + enemy_unit.info = UnitInfo.new() + enemy_unit.info.name = \"Putit\" + enemy_unit.allegiance = ENEMY_ALLEGIANCE combat_map.deploy_unit(enemy_unit, Vector2i(6, 3)) var tree := get_tree() diff --git a/scripts/battle/combat_engine/combat_proposal.gd b/scripts/battle/combat_engine/combat_proposal.gd index b6e1998..007a352 100644 --- a/scripts/battle/combat_engine/combat_proposal.gd +++ b/scripts/battle/combat_engine/combat_proposal.gd @@ -1,7 +1,7 @@ class_name CombatProposal extends Resource class CombatantStats: - var unit: Unit + var deployed: DeployedUnit var max_hp: int var hp: int var sp: int diff --git a/scripts/battle/combat_engine/combat_system.gd b/scripts/battle/combat_engine/combat_system.gd index 1e05a1a..379a02f 100644 --- a/scripts/battle/combat_engine/combat_system.gd +++ b/scripts/battle/combat_engine/combat_system.gd @@ -1,6 +1,6 @@ class_name CombatSystem extends Node -func create_proposal(attacker: Unit, defender: Unit, distance: int) -> CombatProposal: +func create_proposal(attacker: DeployedUnit, defender: DeployedUnit, distance: int) -> CombatProposal: var proposal := CombatProposal.new() var atk_tactics := _filter_tactics(attacker, distance) @@ -19,10 +19,10 @@ func create_proposal(attacker: Unit, defender: Unit, distance: int) -> CombatPro return proposal -func _filter_tactics(unit: Unit, distance: int) -> Array[CombatTactic]: +func _filter_tactics(deployed: DeployedUnit, distance: int) -> Array[CombatTactic]: var valid: Array[CombatTactic] = [] - for tactic in unit.tactics: - if tactic.tactic_range and tactic.tactic_range.is_valid_range(distance, unit): + for tactic in deployed.tactics: + if tactic.tactic_range and tactic.tactic_range.is_valid_range(distance, deployed.unit): valid.append(tactic) return valid @@ -34,28 +34,28 @@ func _find_default_attack(tactics: Array[CombatTactic]) -> CombatTactic: return tactics[0] if tactics.size() > 0 else null -func _snapshot(unit: Unit, opponent: Unit, available: Array[CombatTactic], selected: CombatTactic, opponent_selected: CombatTactic) -> CombatProposal.CombatantStats: +func _snapshot(deployed: DeployedUnit, opponent: DeployedUnit, available: Array[CombatTactic], selected: CombatTactic, opponent_selected: CombatTactic) -> CombatProposal.CombatantStats: var stats := CombatProposal.CombatantStats.new() - stats.unit = unit - stats.max_hp = unit.current_stats.max_hp - stats.hp = unit.current_stats.current_hp - stats.sp = unit.current_stats.current_sp - stats.spd = unit.current_stats.spd + stats.deployed = deployed + stats.max_hp = deployed.unit.stats.max_hp + stats.hp = deployed.current_stats.current_hp + stats.sp = deployed.current_stats.current_sp + stats.spd = deployed.unit.stats.spd stats.available_tactics = available stats.selected_tactic = selected if selected and selected.deals_damage(): - var offensive: Dictionary = selected.get_offensive_stats(unit) + var offensive: Dictionary = selected.get_offensive_stats(deployed.unit) stats.atk = offensive["atk"] - stats.hit = offensive["hit"] - opponent.current_stats.eva + stats.hit = offensive["hit"] - opponent.unit.stats.eva else: stats.atk = 0 stats.hit = 0 if opponent_selected and opponent_selected.deals_damage(): - stats.def = opponent_selected.get_relevant_defense(unit) + stats.def = opponent_selected.get_relevant_defense(deployed.unit) else: - stats.def = unit.current_stats.phys_def + stats.def = deployed.unit.stats.phys_def return stats @@ -74,29 +74,29 @@ func update_tactic(proposal: CombatProposal, is_attacker: bool, tactic: CombatTa # Recalculate this side's offensive stats if tactic and tactic.deals_damage(): - var offensive: Dictionary = tactic.get_offensive_stats(self_stats.unit) + var offensive: Dictionary = tactic.get_offensive_stats(self_stats.deployed.unit) self_stats.atk = offensive["atk"] - self_stats.hit = offensive["hit"] - opp_stats.unit.current_stats.eva + self_stats.hit = offensive["hit"] - opp_stats.deployed.unit.stats.eva else: self_stats.atk = 0 self_stats.hit = 0 # Recalculate opponent's def based on this side's new tactic if tactic and tactic.deals_damage(): - opp_stats.def = tactic.get_relevant_defense(opp_stats.unit) + opp_stats.def = tactic.get_relevant_defense(opp_stats.deployed.unit) else: - opp_stats.def = opp_stats.unit.current_stats.phys_def + opp_stats.def = opp_stats.deployed.unit.stats.phys_def -func select_ai_tactic(unit: Unit, opponent: Unit, available_tactics: Array[CombatTactic]) -> CombatTactic: +func select_ai_tactic(deployed: DeployedUnit, opponent: DeployedUnit, available_tactics: Array[CombatTactic]) -> CombatTactic: var best_tactic: CombatTactic = null var best_damage := -1 for tactic in available_tactics: if not tactic.deals_damage(): continue - var offensive: Dictionary = tactic.get_offensive_stats(unit) - var defense: int = tactic.get_relevant_defense(opponent) + var offensive: Dictionary = tactic.get_offensive_stats(deployed.unit) + var defense: int = tactic.get_relevant_defense(opponent.unit) var damage := maxi(offensive["atk"] - defense, 0) if damage > best_damage: best_damage = damage @@ -114,10 +114,10 @@ func select_ai_tactic(unit: Unit, opponent: Unit, available_tactics: Array[Comba func apply_proposal(proposal: CombatProposal) -> void: var atk_stats := proposal.attacker var def_stats := proposal.defender - var atk_unit := atk_stats.unit - var def_unit := def_stats.unit + var atk_deployed := atk_stats.deployed + var def_deployed := def_stats.deployed - if not is_instance_valid(atk_unit) or not is_instance_valid(def_unit): + if not is_instance_valid(atk_deployed) or not is_instance_valid(def_deployed): return # Attacker strikes (if their tactic deals damage) @@ -125,17 +125,17 @@ func apply_proposal(proposal: CombatProposal) -> void: var atk_roll := randi_range(1, 100) if atk_roll <= atk_stats.hit: var damage := maxi(atk_stats.atk - def_stats.def, 0) - def_unit.take_damage(damage) + def_deployed.take_damage(damage) # Counterattack if defender survives and their tactic deals damage - if is_instance_valid(def_unit) and def_unit.is_alive() \ - and is_instance_valid(atk_unit) \ + if is_instance_valid(def_deployed) and def_deployed.is_alive() \ + and is_instance_valid(atk_deployed) \ and def_stats.selected_tactic and def_stats.selected_tactic.deals_damage(): var def_roll := randi_range(1, 100) if def_roll <= def_stats.hit: var damage := maxi(def_stats.atk - atk_stats.def, 0) - atk_unit.take_damage(damage) + atk_deployed.take_damage(damage) -func _is_player_controlled(unit: Unit) -> bool: - return unit.current_allegiance.type == UnitAllegiance.AllegianceType.PLAYER +func _is_player_controlled(deployed: DeployedUnit) -> bool: + return deployed.unit.allegiance.type == UnitAllegiance.AllegianceType.PLAYER diff --git a/scripts/battle/combat_tactics/combat_tactic.gd b/scripts/battle/combat_tactics/combat_tactic.gd index c1bb88e..0627085 100644 --- a/scripts/battle/combat_tactics/combat_tactic.gd +++ b/scripts/battle/combat_tactics/combat_tactic.gd @@ -7,7 +7,7 @@ func get_offensive_stats(_unit: Unit) -> Variant: return null func get_relevant_defense(unit: Unit) -> int: - return unit.current_stats.phys_def + return unit.stats.phys_def func deals_damage() -> bool: return false diff --git a/scripts/battle/combat_tactics/ranges/unit_matching_combat_tactic_range.gd b/scripts/battle/combat_tactics/ranges/unit_matching_combat_tactic_range.gd index da2d89e..b668e57 100644 --- a/scripts/battle/combat_tactics/ranges/unit_matching_combat_tactic_range.gd +++ b/scripts/battle/combat_tactics/ranges/unit_matching_combat_tactic_range.gd @@ -2,4 +2,4 @@ class_name UnitMatchingCombatTacticRange extends CombatTacticRange func is_valid_range(distance: int, unit: Unit) -> bool: - return distance <= unit.current_stats.atk_range + return distance <= unit.stats.atk_range diff --git a/scripts/battle/combat_tactics/tactics/attack_combat_tactic.gd b/scripts/battle/combat_tactics/tactics/attack_combat_tactic.gd index 5902029..d3758bc 100644 --- a/scripts/battle/combat_tactics/tactics/attack_combat_tactic.gd +++ b/scripts/battle/combat_tactics/tactics/attack_combat_tactic.gd @@ -1,10 +1,10 @@ class_name AttackCombatTactic extends CombatTactic func get_offensive_stats(unit: Unit) -> Variant: - return {"atk": unit.current_stats.phys_atk, "hit": unit.current_stats.hit} + return {"atk": unit.stats.phys_atk, "hit": unit.stats.hit} func get_relevant_defense(unit: Unit) -> int: - return unit.current_stats.phys_def + return unit.stats.phys_def func deals_damage() -> bool: return true diff --git a/scripts/battle/combat_tactics/tactics/defend_combat_tactic.gd b/scripts/battle/combat_tactics/tactics/defend_combat_tactic.gd index 5141847..9fc95cb 100644 --- a/scripts/battle/combat_tactics/tactics/defend_combat_tactic.gd +++ b/scripts/battle/combat_tactics/tactics/defend_combat_tactic.gd @@ -4,7 +4,7 @@ func get_offensive_stats(_unit: Unit) -> Variant: return null func get_relevant_defense(unit: Unit) -> int: - return unit.current_stats.phys_def + return unit.stats.phys_def func deals_damage() -> bool: return false diff --git a/scripts/battle/combat_ui.gd b/scripts/battle/combat_ui.gd index 6a065a2..78b211f 100644 --- a/scripts/battle/combat_ui.gd +++ b/scripts/battle/combat_ui.gd @@ -26,7 +26,7 @@ signal fight_cancelled @onready var atk_tactic_select: OptionButton = %AttackerTacticSelect @onready var def_tactic_select: OptionButton = %DefenderTacticSelect -var _selected_unit: Unit +var _selected_unit: DeployedUnit var _current_proposal: CombatProposal var combat_system: CombatSystem @@ -37,29 +37,29 @@ func _ready() -> void: cancel_button.pressed.connect(_on_cancel_pressed) atk_tactic_select.item_selected.connect(_on_atk_tactic_selected) def_tactic_select.item_selected.connect(_on_def_tactic_selected) - for unit: Unit in get_tree().get_nodes_in_group("units"): - unit.unit_selected_changed.connect(_on_unit_selected_changed) - unit.unit_died.connect(_on_unit_died) + for deployed: DeployedUnit in get_tree().get_nodes_in_group("deployed_units"): + deployed.unit_selected_changed.connect(_on_unit_selected_changed) + deployed.unit_died.connect(_on_unit_died) get_tree().node_added.connect(_on_node_added) func _on_node_added(node: Node) -> void: - if node is Unit and node.is_in_group("units"): + if node is DeployedUnit and node.is_in_group("deployed_units"): if not node.unit_selected_changed.is_connected(_on_unit_selected_changed): node.unit_selected_changed.connect(_on_unit_selected_changed) if not node.unit_died.is_connected(_on_unit_died): node.unit_died.connect(_on_unit_died) -func _on_unit_died(unit: Unit) -> void: - if _selected_unit == unit: +func _on_unit_died(deployed: DeployedUnit) -> void: + if _selected_unit == deployed: _selected_unit = null unit_panel.visible = false if _current_proposal: - if _current_proposal.attacker.unit == unit or _current_proposal.defender.unit == unit: + if _current_proposal.attacker.deployed == deployed or _current_proposal.defender.deployed == deployed: _hide_proposal() func _process(_delta: float) -> void: if _selected_unit and is_instance_valid(_selected_unit): - hp_bar.max_value = _selected_unit.current_stats.max_hp + hp_bar.max_value = _selected_unit.unit.stats.max_hp hp_bar.value = _selected_unit.current_stats.current_hp func _unhandled_input(event: InputEvent) -> void: @@ -68,12 +68,12 @@ func _unhandled_input(event: InputEvent) -> void: fight_cancelled.emit() get_viewport().set_input_as_handled() -func _on_unit_selected_changed(unit: Unit, selected: bool) -> void: +func _on_unit_selected_changed(deployed: DeployedUnit, selected: bool) -> void: if selected: - _selected_unit = unit - name_label.text = unit.current_info.name - hp_bar.max_value = unit.current_stats.max_hp - hp_bar.value = unit.current_stats.current_hp + _selected_unit = deployed + name_label.text = deployed.unit.info.name + hp_bar.max_value = deployed.unit.stats.max_hp + hp_bar.value = deployed.current_stats.current_hp unit_panel.visible = true else: _selected_unit = null @@ -112,21 +112,21 @@ func _populate_tactic_select(button: OptionButton, combatant: CombatProposal.Com if tactic == combatant.selected_tactic: selected_idx = i button.selected = selected_idx - var is_player := combatant.unit.current_allegiance.type == UnitAllegiance.AllegianceType.PLAYER + var is_player := combatant.deployed.unit.allegiance.type == UnitAllegiance.AllegianceType.PLAYER button.disabled = not is_player func _refresh_stats() -> void: var atk := _current_proposal.attacker var def := _current_proposal.defender - atk_name_label.text = atk.unit.current_info.name + atk_name_label.text = atk.deployed.unit.info.name atk_hp_bar.max_value = atk.max_hp atk_hp_bar.value = atk.hp atk_atk_label.text = "ATK: %d" % atk.atk atk_def_label.text = "DEF: %d" % atk.def atk_hit_label.text = "HIT: %d%%" % atk.hit atk_spd_label.text = "SPD: %d" % atk.spd - def_name_label.text = def.unit.current_info.name + def_name_label.text = def.deployed.unit.info.name def_hp_bar.max_value = def.max_hp def_hp_bar.value = def.hp def_atk_label.text = "ATK: %d" % def.atk diff --git a/scripts/battle/deployed_units/deployed_unit.gd b/scripts/battle/deployed_units/deployed_unit.gd index 63ffe5d..36ab0f2 100644 --- a/scripts/battle/deployed_units/deployed_unit.gd +++ b/scripts/battle/deployed_units/deployed_unit.gd @@ -1,4 +1,49 @@ class_name DeployedUnit extends Node2D -# The unit represented by this deployment +enum UnitState { ALIVE, DEAD } + @export var unit: Unit + +var current_stats: DeployedUnitStats +var tactics: Array[CombatTactic] = [] +var state: UnitState = UnitState.ALIVE + +signal unit_selected_changed(deployed: DeployedUnit, selected: bool) +signal unit_allegiance_changed(deployed: DeployedUnit, allegiance: UnitAllegiance) +signal unit_died(deployed: DeployedUnit) + +func _ready() -> void: + current_stats = DeployedUnitStats.from_unit_stats(unit.stats) + tactics = unit.tactics.duplicate() + _append_builtin_tactics() + unit_allegiance_changed.emit(self, unit.allegiance) + +func _append_builtin_tactics() -> void: + var attack := AttackCombatTactic.new() + attack.tactic_name = "Attack" + attack.tactic_range = UnitMatchingCombatTacticRange.new() + tactics.append(attack) + + var defend := DefendCombatTactic.new() + defend.tactic_name = "Defend" + defend.tactic_range = AnyCombatTacticRange.new() + tactics.append(defend) + +func set_selected(selected: bool) -> void: + unit_selected_changed.emit(self, selected) + +func is_alive() -> bool: + return state == UnitState.ALIVE + +func take_damage(amount: int) -> void: + if state != UnitState.ALIVE: + return + current_stats.current_hp -= amount + if current_stats.current_hp <= 0: + current_stats.current_hp = 0 + _die() + +func _die() -> void: + state = UnitState.DEAD + unit_died.emit(self) + queue_free() diff --git a/scripts/battle/deployed_units/deployed_unit_stats.gd b/scripts/battle/deployed_units/deployed_unit_stats.gd index 77e10a2..40d44e5 100644 --- a/scripts/battle/deployed_units/deployed_unit_stats.gd +++ b/scripts/battle/deployed_units/deployed_unit_stats.gd @@ -5,10 +5,10 @@ class_name DeployedUnitStats extends Resource @export var current_sp: int @export var current_fs: int -func _init() -> void: - _init_stats.call_deferred() - -func _init_stats() -> void: - current_hp = unit_stats.max_hp - current_sp = unit_stats.max_sp - current_fs = unit_stats.max_fs +static func from_unit_stats(source: UnitStats) -> DeployedUnitStats: + var stats := DeployedUnitStats.new() + stats.unit_stats = source + stats.current_hp = source.max_hp + stats.current_sp = source.max_sp + stats.current_fs = source.max_fs + return stats diff --git a/scripts/battle/map/combat_map.gd b/scripts/battle/map/combat_map.gd index e516b50..dd3d9c7 100644 --- a/scripts/battle/map/combat_map.gd +++ b/scripts/battle/map/combat_map.gd @@ -8,6 +8,7 @@ extends Node2D @onready var wall_renderer: WallRenderer = %WallRenderer @onready var fog_renderer: FogRenderer = %FogRenderer +const DEPLOYED_UNIT_SCENE = preload("res://prefabs/deployed_unit.tscn") const TILE_SIZE := 100.0 const SOURCE_ID: int = 0 @@ -19,7 +20,7 @@ func _ready() -> void: if _pending_layout: _apply_layout(_pending_layout) for entry in _pending_units: - _apply_deploy(entry.unit, entry.coords) + _apply_deploy(entry.deployed, entry.coords) _pending_units.clear() if map_layout: apply_layout(map_layout) @@ -51,10 +52,12 @@ func load_map(layout: String) -> void: func deploy_unit(unit: Unit, coords: Vector2i) -> void: + var deployed: DeployedUnit = DEPLOYED_UNIT_SCENE.instantiate() + deployed.unit = unit if is_node_ready(): - _apply_deploy(unit, coords) + _apply_deploy(deployed, coords) else: - _pending_units.append({unit = unit, coords = coords}) + _pending_units.append({deployed = deployed, coords = coords}) func _apply_layout(layout: String) -> void: @@ -69,14 +72,14 @@ func _apply_layout(layout: String) -> void: draw_floor(coords) -func _apply_deploy(unit: Unit, coords: Vector2i) -> void: - unit.position = coords_to_world(coords) - add_child(unit) +func _apply_deploy(deployed: DeployedUnit, coords: Vector2i) -> void: + deployed.position = coords_to_world(coords) + add_child(deployed) -func remove_unit(unit: Unit) -> void: - if unit.get_parent() == self: - remove_child(unit) +func remove_unit(deployed: DeployedUnit) -> void: + if deployed.get_parent() == self: + remove_child(deployed) func target_tile(coords: Vector2i) -> void: diff --git a/scripts/battle/player_controller.gd b/scripts/battle/player_controller.gd index 64140f7..83d5ea9 100644 --- a/scripts/battle/player_controller.gd +++ b/scripts/battle/player_controller.gd @@ -4,13 +4,13 @@ const SPEED = 192.0 @export var dl_map: CombatMap -signal combat_requested(attacker: Unit, defender: Unit) +signal combat_requested(attacker: DeployedUnit, defender: DeployedUnit) signal mouse_grid_changed(coords: Vector2i) signal camera_drag(delta: Vector2) var input_disabled := false -var _selected_unit: Unit = null +var _selected_unit: DeployedUnit = null var _target_pos: Vector2 var _goal_pos: Vector2 var _moving := false @@ -24,19 +24,19 @@ const DRAG_THRESHOLD := 8.0 func _ready() -> void: - for unit: Unit in get_tree().get_nodes_in_group("units"): - unit.unit_died.connect(_on_unit_died) + for deployed: DeployedUnit in get_tree().get_nodes_in_group("deployed_units"): + deployed.unit_died.connect(_on_unit_died) get_tree().node_added.connect(_on_node_added) func _on_node_added(node: Node) -> void: - if node is Unit and node.is_in_group("units"): + if node is DeployedUnit and node.is_in_group("deployed_units"): if not node.unit_died.is_connected(_on_unit_died): node.unit_died.connect(_on_unit_died) -func _on_unit_died(unit: Unit) -> void: - if _selected_unit == unit: +func _on_unit_died(deployed: DeployedUnit) -> void: + if _selected_unit == deployed: _selected_unit = null _moving = false @@ -146,22 +146,22 @@ func _handle_left_click(screen_pos: Vector2) -> void: get_viewport().set_input_as_handled() -func _select_unit(unit: Unit) -> void: +func _select_unit(deployed: DeployedUnit) -> void: if _selected_unit: _selected_unit.set_selected(false) - _selected_unit = unit + _selected_unit = deployed _selected_unit.set_selected(true) _goal_pos = _selected_unit.position _target_pos = _selected_unit.position _moving = false -func _get_unit_at(world_pos: Vector2) -> Unit: +func _get_unit_at(world_pos: Vector2) -> DeployedUnit: var snapped_coords := dl_map.snap_to_grid(world_pos) - for unit: Unit in get_tree().get_nodes_in_group("units"): - if not unit.is_alive(): + for deployed: DeployedUnit in get_tree().get_nodes_in_group("deployed_units"): + if not deployed.is_alive(): continue - var unit_snapped := dl_map.snap_to_grid(unit.global_position) + var unit_snapped := dl_map.snap_to_grid(deployed.global_position) if unit_snapped == snapped_coords: - return unit + return deployed return null diff --git a/scripts/battle/strategy_phase.gd b/scripts/battle/strategy_phase.gd index 7688414..a20fb28 100644 --- a/scripts/battle/strategy_phase.gd +++ b/scripts/battle/strategy_phase.gd @@ -45,7 +45,7 @@ func _ready() -> void: func _on_mouse_grid_changed(coords: Vector2i) -> void: combat_map.target_tile(coords) -func _on_combat_requested(attacker: Unit, defender: Unit) -> void: +func _on_combat_requested(attacker: DeployedUnit, defender: DeployedUnit) -> void: var atk_coords := combat_map.world_to_coords(attacker.position) var def_coords := combat_map.world_to_coords(defender.position) var distance := absi(atk_coords.x - def_coords.x) + absi(atk_coords.y - def_coords.y) diff --git a/scripts/units/unit.gd b/scripts/units/unit.gd index 817024c..87f8e20 100644 --- a/scripts/units/unit.gd +++ b/scripts/units/unit.gd @@ -1,56 +1,6 @@ -class_name Unit extends Node2D +class_name Unit extends Resource -enum UnitState { ALIVE, DEAD } - -#region Templates -@export var stat_template: UnitStats -@export var info_template: UnitInfo -@export var allegiance_template: UnitAllegiance +@export var stats: UnitStats +@export var info: UnitInfo +@export var allegiance: UnitAllegiance @export var tactics: Array[CombatTactic] = [] -#endregion - -var current_stats: UnitStats -var current_info: UnitInfo -var current_allegiance: UnitAllegiance -var state: UnitState = UnitState.ALIVE - -signal unit_selected_changed(unit: Unit, selected: bool) -signal unit_allegiance_changed(unit: Unit, allegiance: UnitAllegiance) -signal unit_died(unit: Unit) - -func _ready() -> void: - current_stats = stat_template.duplicate(true) - current_info = info_template.duplicate(true) - current_allegiance = allegiance_template.duplicate(true) - _append_builtin_tactics() - unit_allegiance_changed.emit(self, current_allegiance) - -func _append_builtin_tactics() -> void: - var attack := AttackCombatTactic.new() - attack.tactic_name = "Attack" - attack.tactic_range = UnitMatchingCombatTacticRange.new() - tactics.append(attack) - - var defend := DefendCombatTactic.new() - defend.tactic_name = "Defend" - defend.tactic_range = AnyCombatTacticRange.new() - tactics.append(defend) - -func set_selected(selected: bool) -> void: - unit_selected_changed.emit(self, selected) - -func is_alive() -> bool: - return state == UnitState.ALIVE - -func take_damage(amount: int) -> void: - if state != UnitState.ALIVE: - return - current_stats.current_hp -= amount - if current_stats.current_hp <= 0: - current_stats.current_hp = 0 - _die() - -func _die() -> void: - state = UnitState.DEAD - unit_died.emit(self) - queue_free()