Refactored unit
This commit is contained in:
91
prefabs/deployed_unit.tscn
Normal file
91
prefabs/deployed_unit.tscn
Normal file
@@ -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"]
|
||||||
@@ -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="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"]
|
[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")
|
sprite_frames = SubResource("SpriteFrames_7jqdg")
|
||||||
animation = &"idle"
|
animation = &"idle"
|
||||||
autoplay = "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"]
|
|
||||||
|
|||||||
@@ -76,7 +76,6 @@ resource_name = "StartButton"
|
|||||||
script/source = "extends TextureButton
|
script/source = "extends TextureButton
|
||||||
|
|
||||||
const COMBAT_SCENE = preload(\"res://scenes/views/battle_view.tscn\")
|
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 PLAYER_ALLEGIANCE = preload(\"res://resources/allegiance_types/player_allegiance.tres\")
|
||||||
const ENEMY_ALLEGIANCE = preload(\"res://resources/allegiance_types/enemy_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_instance := COMBAT_SCENE.instantiate()
|
||||||
var combat_map: CombatMap = combat_instance.find_child(\"CombatMap\")
|
var combat_map: CombatMap = combat_instance.find_child(\"CombatMap\")
|
||||||
|
|
||||||
var player_unit: Unit = UNIT_SCENE.instantiate()
|
var player_unit := Unit.new()
|
||||||
player_unit.stat_template = UnitStats.new()
|
player_unit.stats = UnitStats.new()
|
||||||
player_unit.info_template = UnitInfo.new()
|
player_unit.info = UnitInfo.new()
|
||||||
player_unit.info_template.name = \"Putit\"
|
player_unit.info.name = \"Putit\"
|
||||||
player_unit.allegiance_template = PLAYER_ALLEGIANCE
|
player_unit.allegiance = PLAYER_ALLEGIANCE
|
||||||
combat_map.deploy_unit(player_unit, Vector2i(3, 3))
|
combat_map.deploy_unit(player_unit, Vector2i(3, 3))
|
||||||
|
|
||||||
var enemy_unit: Unit = UNIT_SCENE.instantiate()
|
var enemy_unit := Unit.new()
|
||||||
enemy_unit.stat_template = UnitStats.new()
|
enemy_unit.stats = UnitStats.new()
|
||||||
enemy_unit.info_template = UnitInfo.new()
|
enemy_unit.info = UnitInfo.new()
|
||||||
enemy_unit.info_template.name = \"Putit\"
|
enemy_unit.info.name = \"Putit\"
|
||||||
enemy_unit.allegiance_template = ENEMY_ALLEGIANCE
|
enemy_unit.allegiance = ENEMY_ALLEGIANCE
|
||||||
combat_map.deploy_unit(enemy_unit, Vector2i(6, 3))
|
combat_map.deploy_unit(enemy_unit, Vector2i(6, 3))
|
||||||
|
|
||||||
var tree := get_tree()
|
var tree := get_tree()
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
class_name CombatProposal extends Resource
|
class_name CombatProposal extends Resource
|
||||||
|
|
||||||
class CombatantStats:
|
class CombatantStats:
|
||||||
var unit: Unit
|
var deployed: DeployedUnit
|
||||||
var max_hp: int
|
var max_hp: int
|
||||||
var hp: int
|
var hp: int
|
||||||
var sp: int
|
var sp: int
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
class_name CombatSystem extends Node
|
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 proposal := CombatProposal.new()
|
||||||
|
|
||||||
var atk_tactics := _filter_tactics(attacker, distance)
|
var atk_tactics := _filter_tactics(attacker, distance)
|
||||||
@@ -19,10 +19,10 @@ func create_proposal(attacker: Unit, defender: Unit, distance: int) -> CombatPro
|
|||||||
return proposal
|
return proposal
|
||||||
|
|
||||||
|
|
||||||
func _filter_tactics(unit: Unit, distance: int) -> Array[CombatTactic]:
|
func _filter_tactics(deployed: DeployedUnit, distance: int) -> Array[CombatTactic]:
|
||||||
var valid: Array[CombatTactic] = []
|
var valid: Array[CombatTactic] = []
|
||||||
for tactic in unit.tactics:
|
for tactic in deployed.tactics:
|
||||||
if tactic.tactic_range and tactic.tactic_range.is_valid_range(distance, unit):
|
if tactic.tactic_range and tactic.tactic_range.is_valid_range(distance, deployed.unit):
|
||||||
valid.append(tactic)
|
valid.append(tactic)
|
||||||
return valid
|
return valid
|
||||||
|
|
||||||
@@ -34,28 +34,28 @@ func _find_default_attack(tactics: Array[CombatTactic]) -> CombatTactic:
|
|||||||
return tactics[0] if tactics.size() > 0 else null
|
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()
|
var stats := CombatProposal.CombatantStats.new()
|
||||||
stats.unit = unit
|
stats.deployed = deployed
|
||||||
stats.max_hp = unit.current_stats.max_hp
|
stats.max_hp = deployed.unit.stats.max_hp
|
||||||
stats.hp = unit.current_stats.current_hp
|
stats.hp = deployed.current_stats.current_hp
|
||||||
stats.sp = unit.current_stats.current_sp
|
stats.sp = deployed.current_stats.current_sp
|
||||||
stats.spd = unit.current_stats.spd
|
stats.spd = deployed.unit.stats.spd
|
||||||
stats.available_tactics = available
|
stats.available_tactics = available
|
||||||
stats.selected_tactic = selected
|
stats.selected_tactic = selected
|
||||||
|
|
||||||
if selected and selected.deals_damage():
|
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.atk = offensive["atk"]
|
||||||
stats.hit = offensive["hit"] - opponent.current_stats.eva
|
stats.hit = offensive["hit"] - opponent.unit.stats.eva
|
||||||
else:
|
else:
|
||||||
stats.atk = 0
|
stats.atk = 0
|
||||||
stats.hit = 0
|
stats.hit = 0
|
||||||
|
|
||||||
if opponent_selected and opponent_selected.deals_damage():
|
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:
|
else:
|
||||||
stats.def = unit.current_stats.phys_def
|
stats.def = deployed.unit.stats.phys_def
|
||||||
|
|
||||||
return stats
|
return stats
|
||||||
|
|
||||||
@@ -74,29 +74,29 @@ func update_tactic(proposal: CombatProposal, is_attacker: bool, tactic: CombatTa
|
|||||||
|
|
||||||
# Recalculate this side's offensive stats
|
# Recalculate this side's offensive stats
|
||||||
if tactic and tactic.deals_damage():
|
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.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:
|
else:
|
||||||
self_stats.atk = 0
|
self_stats.atk = 0
|
||||||
self_stats.hit = 0
|
self_stats.hit = 0
|
||||||
|
|
||||||
# Recalculate opponent's def based on this side's new tactic
|
# Recalculate opponent's def based on this side's new tactic
|
||||||
if tactic and tactic.deals_damage():
|
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:
|
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_tactic: CombatTactic = null
|
||||||
var best_damage := -1
|
var best_damage := -1
|
||||||
|
|
||||||
for tactic in available_tactics:
|
for tactic in available_tactics:
|
||||||
if not tactic.deals_damage():
|
if not tactic.deals_damage():
|
||||||
continue
|
continue
|
||||||
var offensive: Dictionary = tactic.get_offensive_stats(unit)
|
var offensive: Dictionary = tactic.get_offensive_stats(deployed.unit)
|
||||||
var defense: int = tactic.get_relevant_defense(opponent)
|
var defense: int = tactic.get_relevant_defense(opponent.unit)
|
||||||
var damage := maxi(offensive["atk"] - defense, 0)
|
var damage := maxi(offensive["atk"] - defense, 0)
|
||||||
if damage > best_damage:
|
if damage > best_damage:
|
||||||
best_damage = 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:
|
func apply_proposal(proposal: CombatProposal) -> void:
|
||||||
var atk_stats := proposal.attacker
|
var atk_stats := proposal.attacker
|
||||||
var def_stats := proposal.defender
|
var def_stats := proposal.defender
|
||||||
var atk_unit := atk_stats.unit
|
var atk_deployed := atk_stats.deployed
|
||||||
var def_unit := def_stats.unit
|
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
|
return
|
||||||
|
|
||||||
# Attacker strikes (if their tactic deals damage)
|
# Attacker strikes (if their tactic deals damage)
|
||||||
@@ -125,17 +125,17 @@ func apply_proposal(proposal: CombatProposal) -> void:
|
|||||||
var atk_roll := randi_range(1, 100)
|
var atk_roll := randi_range(1, 100)
|
||||||
if atk_roll <= atk_stats.hit:
|
if atk_roll <= atk_stats.hit:
|
||||||
var damage := maxi(atk_stats.atk - def_stats.def, 0)
|
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
|
# Counterattack if defender survives and their tactic deals damage
|
||||||
if is_instance_valid(def_unit) and def_unit.is_alive() \
|
if is_instance_valid(def_deployed) and def_deployed.is_alive() \
|
||||||
and is_instance_valid(atk_unit) \
|
and is_instance_valid(atk_deployed) \
|
||||||
and def_stats.selected_tactic and def_stats.selected_tactic.deals_damage():
|
and def_stats.selected_tactic and def_stats.selected_tactic.deals_damage():
|
||||||
var def_roll := randi_range(1, 100)
|
var def_roll := randi_range(1, 100)
|
||||||
if def_roll <= def_stats.hit:
|
if def_roll <= def_stats.hit:
|
||||||
var damage := maxi(def_stats.atk - atk_stats.def, 0)
|
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:
|
func _is_player_controlled(deployed: DeployedUnit) -> bool:
|
||||||
return unit.current_allegiance.type == UnitAllegiance.AllegianceType.PLAYER
|
return deployed.unit.allegiance.type == UnitAllegiance.AllegianceType.PLAYER
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ func get_offensive_stats(_unit: Unit) -> Variant:
|
|||||||
return null
|
return null
|
||||||
|
|
||||||
func get_relevant_defense(unit: Unit) -> int:
|
func get_relevant_defense(unit: Unit) -> int:
|
||||||
return unit.current_stats.phys_def
|
return unit.stats.phys_def
|
||||||
|
|
||||||
func deals_damage() -> bool:
|
func deals_damage() -> bool:
|
||||||
return false
|
return false
|
||||||
|
|||||||
@@ -2,4 +2,4 @@
|
|||||||
class_name UnitMatchingCombatTacticRange extends CombatTacticRange
|
class_name UnitMatchingCombatTacticRange extends CombatTacticRange
|
||||||
|
|
||||||
func is_valid_range(distance: int, unit: Unit) -> bool:
|
func is_valid_range(distance: int, unit: Unit) -> bool:
|
||||||
return distance <= unit.current_stats.atk_range
|
return distance <= unit.stats.atk_range
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
class_name AttackCombatTactic extends CombatTactic
|
class_name AttackCombatTactic extends CombatTactic
|
||||||
|
|
||||||
func get_offensive_stats(unit: Unit) -> Variant:
|
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:
|
func get_relevant_defense(unit: Unit) -> int:
|
||||||
return unit.current_stats.phys_def
|
return unit.stats.phys_def
|
||||||
|
|
||||||
func deals_damage() -> bool:
|
func deals_damage() -> bool:
|
||||||
return true
|
return true
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ func get_offensive_stats(_unit: Unit) -> Variant:
|
|||||||
return null
|
return null
|
||||||
|
|
||||||
func get_relevant_defense(unit: Unit) -> int:
|
func get_relevant_defense(unit: Unit) -> int:
|
||||||
return unit.current_stats.phys_def
|
return unit.stats.phys_def
|
||||||
|
|
||||||
func deals_damage() -> bool:
|
func deals_damage() -> bool:
|
||||||
return false
|
return false
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ signal fight_cancelled
|
|||||||
@onready var atk_tactic_select: OptionButton = %AttackerTacticSelect
|
@onready var atk_tactic_select: OptionButton = %AttackerTacticSelect
|
||||||
@onready var def_tactic_select: OptionButton = %DefenderTacticSelect
|
@onready var def_tactic_select: OptionButton = %DefenderTacticSelect
|
||||||
|
|
||||||
var _selected_unit: Unit
|
var _selected_unit: DeployedUnit
|
||||||
var _current_proposal: CombatProposal
|
var _current_proposal: CombatProposal
|
||||||
var combat_system: CombatSystem
|
var combat_system: CombatSystem
|
||||||
|
|
||||||
@@ -37,29 +37,29 @@ func _ready() -> void:
|
|||||||
cancel_button.pressed.connect(_on_cancel_pressed)
|
cancel_button.pressed.connect(_on_cancel_pressed)
|
||||||
atk_tactic_select.item_selected.connect(_on_atk_tactic_selected)
|
atk_tactic_select.item_selected.connect(_on_atk_tactic_selected)
|
||||||
def_tactic_select.item_selected.connect(_on_def_tactic_selected)
|
def_tactic_select.item_selected.connect(_on_def_tactic_selected)
|
||||||
for unit: Unit in get_tree().get_nodes_in_group("units"):
|
for deployed: DeployedUnit in get_tree().get_nodes_in_group("deployed_units"):
|
||||||
unit.unit_selected_changed.connect(_on_unit_selected_changed)
|
deployed.unit_selected_changed.connect(_on_unit_selected_changed)
|
||||||
unit.unit_died.connect(_on_unit_died)
|
deployed.unit_died.connect(_on_unit_died)
|
||||||
get_tree().node_added.connect(_on_node_added)
|
get_tree().node_added.connect(_on_node_added)
|
||||||
|
|
||||||
func _on_node_added(node: Node) -> void:
|
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):
|
if not node.unit_selected_changed.is_connected(_on_unit_selected_changed):
|
||||||
node.unit_selected_changed.connect(_on_unit_selected_changed)
|
node.unit_selected_changed.connect(_on_unit_selected_changed)
|
||||||
if not node.unit_died.is_connected(_on_unit_died):
|
if not node.unit_died.is_connected(_on_unit_died):
|
||||||
node.unit_died.connect(_on_unit_died)
|
node.unit_died.connect(_on_unit_died)
|
||||||
|
|
||||||
func _on_unit_died(unit: Unit) -> void:
|
func _on_unit_died(deployed: DeployedUnit) -> void:
|
||||||
if _selected_unit == unit:
|
if _selected_unit == deployed:
|
||||||
_selected_unit = null
|
_selected_unit = null
|
||||||
unit_panel.visible = false
|
unit_panel.visible = false
|
||||||
if _current_proposal:
|
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()
|
_hide_proposal()
|
||||||
|
|
||||||
func _process(_delta: float) -> void:
|
func _process(_delta: float) -> void:
|
||||||
if _selected_unit and is_instance_valid(_selected_unit):
|
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
|
hp_bar.value = _selected_unit.current_stats.current_hp
|
||||||
|
|
||||||
func _unhandled_input(event: InputEvent) -> void:
|
func _unhandled_input(event: InputEvent) -> void:
|
||||||
@@ -68,12 +68,12 @@ func _unhandled_input(event: InputEvent) -> void:
|
|||||||
fight_cancelled.emit()
|
fight_cancelled.emit()
|
||||||
get_viewport().set_input_as_handled()
|
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:
|
if selected:
|
||||||
_selected_unit = unit
|
_selected_unit = deployed
|
||||||
name_label.text = unit.current_info.name
|
name_label.text = deployed.unit.info.name
|
||||||
hp_bar.max_value = unit.current_stats.max_hp
|
hp_bar.max_value = deployed.unit.stats.max_hp
|
||||||
hp_bar.value = unit.current_stats.current_hp
|
hp_bar.value = deployed.current_stats.current_hp
|
||||||
unit_panel.visible = true
|
unit_panel.visible = true
|
||||||
else:
|
else:
|
||||||
_selected_unit = null
|
_selected_unit = null
|
||||||
@@ -112,21 +112,21 @@ func _populate_tactic_select(button: OptionButton, combatant: CombatProposal.Com
|
|||||||
if tactic == combatant.selected_tactic:
|
if tactic == combatant.selected_tactic:
|
||||||
selected_idx = i
|
selected_idx = i
|
||||||
button.selected = selected_idx
|
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
|
button.disabled = not is_player
|
||||||
|
|
||||||
|
|
||||||
func _refresh_stats() -> void:
|
func _refresh_stats() -> void:
|
||||||
var atk := _current_proposal.attacker
|
var atk := _current_proposal.attacker
|
||||||
var def := _current_proposal.defender
|
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.max_value = atk.max_hp
|
||||||
atk_hp_bar.value = atk.hp
|
atk_hp_bar.value = atk.hp
|
||||||
atk_atk_label.text = "ATK: %d" % atk.atk
|
atk_atk_label.text = "ATK: %d" % atk.atk
|
||||||
atk_def_label.text = "DEF: %d" % atk.def
|
atk_def_label.text = "DEF: %d" % atk.def
|
||||||
atk_hit_label.text = "HIT: %d%%" % atk.hit
|
atk_hit_label.text = "HIT: %d%%" % atk.hit
|
||||||
atk_spd_label.text = "SPD: %d" % atk.spd
|
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.max_value = def.max_hp
|
||||||
def_hp_bar.value = def.hp
|
def_hp_bar.value = def.hp
|
||||||
def_atk_label.text = "ATK: %d" % def.atk
|
def_atk_label.text = "ATK: %d" % def.atk
|
||||||
|
|||||||
@@ -1,4 +1,49 @@
|
|||||||
class_name DeployedUnit extends Node2D
|
class_name DeployedUnit extends Node2D
|
||||||
|
|
||||||
# The unit represented by this deployment
|
enum UnitState { ALIVE, DEAD }
|
||||||
|
|
||||||
@export var unit: Unit
|
@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()
|
||||||
|
|||||||
@@ -5,10 +5,10 @@ class_name DeployedUnitStats extends Resource
|
|||||||
@export var current_sp: int
|
@export var current_sp: int
|
||||||
@export var current_fs: int
|
@export var current_fs: int
|
||||||
|
|
||||||
func _init() -> void:
|
static func from_unit_stats(source: UnitStats) -> DeployedUnitStats:
|
||||||
_init_stats.call_deferred()
|
var stats := DeployedUnitStats.new()
|
||||||
|
stats.unit_stats = source
|
||||||
func _init_stats() -> void:
|
stats.current_hp = source.max_hp
|
||||||
current_hp = unit_stats.max_hp
|
stats.current_sp = source.max_sp
|
||||||
current_sp = unit_stats.max_sp
|
stats.current_fs = source.max_fs
|
||||||
current_fs = unit_stats.max_fs
|
return stats
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ extends Node2D
|
|||||||
@onready var wall_renderer: WallRenderer = %WallRenderer
|
@onready var wall_renderer: WallRenderer = %WallRenderer
|
||||||
@onready var fog_renderer: FogRenderer = %FogRenderer
|
@onready var fog_renderer: FogRenderer = %FogRenderer
|
||||||
|
|
||||||
|
const DEPLOYED_UNIT_SCENE = preload("res://prefabs/deployed_unit.tscn")
|
||||||
const TILE_SIZE := 100.0
|
const TILE_SIZE := 100.0
|
||||||
const SOURCE_ID: int = 0
|
const SOURCE_ID: int = 0
|
||||||
|
|
||||||
@@ -19,7 +20,7 @@ func _ready() -> void:
|
|||||||
if _pending_layout:
|
if _pending_layout:
|
||||||
_apply_layout(_pending_layout)
|
_apply_layout(_pending_layout)
|
||||||
for entry in _pending_units:
|
for entry in _pending_units:
|
||||||
_apply_deploy(entry.unit, entry.coords)
|
_apply_deploy(entry.deployed, entry.coords)
|
||||||
_pending_units.clear()
|
_pending_units.clear()
|
||||||
if map_layout:
|
if map_layout:
|
||||||
apply_layout(map_layout)
|
apply_layout(map_layout)
|
||||||
@@ -51,10 +52,12 @@ func load_map(layout: String) -> void:
|
|||||||
|
|
||||||
|
|
||||||
func deploy_unit(unit: Unit, coords: Vector2i) -> void:
|
func deploy_unit(unit: Unit, coords: Vector2i) -> void:
|
||||||
|
var deployed: DeployedUnit = DEPLOYED_UNIT_SCENE.instantiate()
|
||||||
|
deployed.unit = unit
|
||||||
if is_node_ready():
|
if is_node_ready():
|
||||||
_apply_deploy(unit, coords)
|
_apply_deploy(deployed, coords)
|
||||||
else:
|
else:
|
||||||
_pending_units.append({unit = unit, coords = coords})
|
_pending_units.append({deployed = deployed, coords = coords})
|
||||||
|
|
||||||
|
|
||||||
func _apply_layout(layout: String) -> void:
|
func _apply_layout(layout: String) -> void:
|
||||||
@@ -69,14 +72,14 @@ func _apply_layout(layout: String) -> void:
|
|||||||
draw_floor(coords)
|
draw_floor(coords)
|
||||||
|
|
||||||
|
|
||||||
func _apply_deploy(unit: Unit, coords: Vector2i) -> void:
|
func _apply_deploy(deployed: DeployedUnit, coords: Vector2i) -> void:
|
||||||
unit.position = coords_to_world(coords)
|
deployed.position = coords_to_world(coords)
|
||||||
add_child(unit)
|
add_child(deployed)
|
||||||
|
|
||||||
|
|
||||||
func remove_unit(unit: Unit) -> void:
|
func remove_unit(deployed: DeployedUnit) -> void:
|
||||||
if unit.get_parent() == self:
|
if deployed.get_parent() == self:
|
||||||
remove_child(unit)
|
remove_child(deployed)
|
||||||
|
|
||||||
|
|
||||||
func target_tile(coords: Vector2i) -> void:
|
func target_tile(coords: Vector2i) -> void:
|
||||||
|
|||||||
@@ -4,13 +4,13 @@ const SPEED = 192.0
|
|||||||
|
|
||||||
@export var dl_map: CombatMap
|
@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 mouse_grid_changed(coords: Vector2i)
|
||||||
signal camera_drag(delta: Vector2)
|
signal camera_drag(delta: Vector2)
|
||||||
|
|
||||||
var input_disabled := false
|
var input_disabled := false
|
||||||
|
|
||||||
var _selected_unit: Unit = null
|
var _selected_unit: DeployedUnit = null
|
||||||
var _target_pos: Vector2
|
var _target_pos: Vector2
|
||||||
var _goal_pos: Vector2
|
var _goal_pos: Vector2
|
||||||
var _moving := false
|
var _moving := false
|
||||||
@@ -24,19 +24,19 @@ const DRAG_THRESHOLD := 8.0
|
|||||||
|
|
||||||
|
|
||||||
func _ready() -> void:
|
func _ready() -> void:
|
||||||
for unit: Unit in get_tree().get_nodes_in_group("units"):
|
for deployed: DeployedUnit in get_tree().get_nodes_in_group("deployed_units"):
|
||||||
unit.unit_died.connect(_on_unit_died)
|
deployed.unit_died.connect(_on_unit_died)
|
||||||
get_tree().node_added.connect(_on_node_added)
|
get_tree().node_added.connect(_on_node_added)
|
||||||
|
|
||||||
|
|
||||||
func _on_node_added(node: Node) -> void:
|
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):
|
if not node.unit_died.is_connected(_on_unit_died):
|
||||||
node.unit_died.connect(_on_unit_died)
|
node.unit_died.connect(_on_unit_died)
|
||||||
|
|
||||||
|
|
||||||
func _on_unit_died(unit: Unit) -> void:
|
func _on_unit_died(deployed: DeployedUnit) -> void:
|
||||||
if _selected_unit == unit:
|
if _selected_unit == deployed:
|
||||||
_selected_unit = null
|
_selected_unit = null
|
||||||
_moving = false
|
_moving = false
|
||||||
|
|
||||||
@@ -146,22 +146,22 @@ func _handle_left_click(screen_pos: Vector2) -> void:
|
|||||||
get_viewport().set_input_as_handled()
|
get_viewport().set_input_as_handled()
|
||||||
|
|
||||||
|
|
||||||
func _select_unit(unit: Unit) -> void:
|
func _select_unit(deployed: DeployedUnit) -> void:
|
||||||
if _selected_unit:
|
if _selected_unit:
|
||||||
_selected_unit.set_selected(false)
|
_selected_unit.set_selected(false)
|
||||||
_selected_unit = unit
|
_selected_unit = deployed
|
||||||
_selected_unit.set_selected(true)
|
_selected_unit.set_selected(true)
|
||||||
_goal_pos = _selected_unit.position
|
_goal_pos = _selected_unit.position
|
||||||
_target_pos = _selected_unit.position
|
_target_pos = _selected_unit.position
|
||||||
_moving = false
|
_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)
|
var snapped_coords := dl_map.snap_to_grid(world_pos)
|
||||||
for unit: Unit in get_tree().get_nodes_in_group("units"):
|
for deployed: DeployedUnit in get_tree().get_nodes_in_group("deployed_units"):
|
||||||
if not unit.is_alive():
|
if not deployed.is_alive():
|
||||||
continue
|
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:
|
if unit_snapped == snapped_coords:
|
||||||
return unit
|
return deployed
|
||||||
return null
|
return null
|
||||||
|
|||||||
@@ -45,7 +45,7 @@ func _ready() -> void:
|
|||||||
func _on_mouse_grid_changed(coords: Vector2i) -> void:
|
func _on_mouse_grid_changed(coords: Vector2i) -> void:
|
||||||
combat_map.target_tile(coords)
|
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 atk_coords := combat_map.world_to_coords(attacker.position)
|
||||||
var def_coords := combat_map.world_to_coords(defender.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)
|
var distance := absi(atk_coords.x - def_coords.x) + absi(atk_coords.y - def_coords.y)
|
||||||
|
|||||||
@@ -1,56 +1,6 @@
|
|||||||
class_name Unit extends Node2D
|
class_name Unit extends Resource
|
||||||
|
|
||||||
enum UnitState { ALIVE, DEAD }
|
@export var stats: UnitStats
|
||||||
|
@export var info: UnitInfo
|
||||||
#region Templates
|
@export var allegiance: UnitAllegiance
|
||||||
@export var stat_template: UnitStats
|
|
||||||
@export var info_template: UnitInfo
|
|
||||||
@export var allegiance_template: UnitAllegiance
|
|
||||||
@export var tactics: Array[CombatTactic] = []
|
@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()
|
|
||||||
|
|||||||
Reference in New Issue
Block a user