From 2b94671759dccd9ff16a4ba4991d5d69dbbf4bbf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C3=ABl=20Lemaire?= Date: Wed, 26 Oct 2022 20:29:17 +0200 Subject: [PATCH] Add defend command --- core/battlefield.gd | 19 +++++++++++-- core/spawner.gd | 10 +++++-- core/tools.gd | 19 +++++++++++++ core/ui/command.gd | 10 +++---- core/ui/command.tscn | 1 - core/ui/commands.gd | 11 +++++--- core/unit.gd | 10 +++++-- main.tscn | 9 ++++-- project.godot | 6 ++++ tac/assets/commands/defend.png | Bin 0 -> 4372 bytes tac/assets/commands/defend.png.import | 34 ++++++++++++++++++++++ tac/commands/defend.tscn | 11 ++++++++ tac/units/factory.gd | 17 ++++------- tac/units/fighter.gd | 39 ++++++++++++++++++-------- 14 files changed, 153 insertions(+), 43 deletions(-) create mode 100644 core/tools.gd create mode 100644 tac/assets/commands/defend.png create mode 100644 tac/assets/commands/defend.png.import create mode 100644 tac/commands/defend.tscn diff --git a/core/battlefield.gd b/core/battlefield.gd index d4bf3d8..4e28c26 100644 --- a/core/battlefield.gd +++ b/core/battlefield.gd @@ -1,8 +1,14 @@ extends Node2D class_name BattleField +@export var commands: Node + var _ticker1 := 0.0 +func _ready(): + if not commands: + push_warning("No commands manager set to battlefield") + func _physics_process(delta): _ticker1 += delta if _ticker1 > 1.0: @@ -12,5 +18,14 @@ func _physics_process(delta): func ticker(group_name: String): get_tree().call_group_flags(SceneTree.GROUP_CALL_DEFERRED, group_name, "tick") -func _on_commands_changed(commander, commands): - get_tree().call_group("unit", "_on_commands_changed", commander, commands) +func _on_commands_changed(comms): + get_tree().call_group("unit", "_on_commands_changed", comms) + +func _on_child_entered_tree(node): + if node is Unit: + node.promoted.connect(_send_commands_to_unit.bind(node)) + _send_commands_to_unit(node) + +func _send_commands_to_unit(unit: Unit): + if commands: + unit._on_commands_changed(commands.get_active()) diff --git a/core/spawner.gd b/core/spawner.gd index b9559fb..c7b42e3 100644 --- a/core/spawner.gd +++ b/core/spawner.gd @@ -30,13 +30,19 @@ func _physics_process(delta): if _spawning_since > spawn_duration: spawn() +func get_battlefield() -> BattleField: + return find_parent("battlefield") as BattleField + func spawn(): if unit_type: var unit := unit_type.instantiate() if unit is Unit: _spawning_since = min(0.0, _spawning_since - spawn_duration) - # FIXME add to battlefield unit.player = player - add_child(unit) + var battlefield := get_battlefield() + if battlefield: + battlefield.add_child(unit) + else: + add_child(unit) unit.global_position = global_position unit.move_to(delivery) diff --git a/core/tools.gd b/core/tools.gd new file mode 100644 index 0000000..060f026 --- /dev/null +++ b/core/tools.gd @@ -0,0 +1,19 @@ +class_name Tools + +# Get the nearest node from a list, satisfying a predicate +static func get_nearest(from: Node2D, nodes: Array[Node], predicate: Callable) -> Node2D: + var from_position := from.global_position + var nearest: Node2D = null + var distance := INF + for node in nodes: + if predicate.call(node): + var d := from_position.distance_to(node.global_position) + if d < distance: + distance = d + nearest = node + return nearest + +# Get a random position around a fixed position, in a given radius +static func jitter(pos: Vector2, min_dist = 0.0, max_dist = 100.0) -> Vector2: + # TODO uniform distribution + return pos + Vector2.from_angle(randf_range(0.0, PI * 2.0)) * randf_range(min_dist, max_dist) diff --git a/core/ui/command.gd b/core/ui/command.gd index e19c815..d153e6b 100644 --- a/core/ui/command.gd +++ b/core/ui/command.gd @@ -12,19 +12,19 @@ signal clicked icon = PlaceholderTexture2D.new() _check_compose() -@export var color: Color: +@export var player: Node: set(val): - if val is Color: - color = val + if val is Player: + player = val else: - color = Color.BLACK + player = null _check_compose() @export var code: String func _compose(): %icon.texture = icon - %badge.self_modulate = color + %badge.self_modulate = player.color if player else Color.GRAY super._compose() func apply(container: Node2D, pos: Vector2): diff --git a/core/ui/command.tscn b/core/ui/command.tscn index 280a148..a08daaa 100644 --- a/core/ui/command.tscn +++ b/core/ui/command.tscn @@ -16,7 +16,6 @@ fill_to = Vector2(0.5, 0) [node name="command" type="Node2D"] script = ExtResource("1_bwbsk") -color = Color(0, 0, 0, 1) code = "" [node name="badge" type="TextureButton" parent="."] diff --git a/core/ui/commands.gd b/core/ui/commands.gd index ace8cf9..42163ba 100644 --- a/core/ui/commands.gd +++ b/core/ui/commands.gd @@ -11,7 +11,7 @@ extends Node2D @export var available_commands: Array[PackedScene] -signal commands_changed(commander, commands) +signal commands_changed(commands) func _ready(): %stock.visible = false @@ -20,6 +20,7 @@ func _ready(): if available_commands: for command_scene in available_commands: var command := command_scene.instantiate() + command.player = player %stock.add_child(command) func _show_command_wheel(pos: Vector2): @@ -37,7 +38,6 @@ func _show_command_wheel(pos: Vector2): for command in %stock.get_children(): # TODO only available command = command.duplicate() - command.color = player.color command.position = Vector2.from_angle(inc * idx) * 200.0 command.connect("clicked", _on_wheel_command_clicked.bind(command), CONNECT_ONE_SHOT) var line := Line2D.new() @@ -55,11 +55,11 @@ func _on_wheel_command_clicked(command: Command): command.apply(%active, %wheel.position) command.scale *= 0.5 command.connect("clicked", _on_active_command_clicked.bind(command)) - commands_changed.emit(player, %active.get_children()) + commands_changed.emit(%active.get_children()) func _on_active_command_clicked(command: Command): command.queue_free() - commands_changed.emit(player, %active.get_children()) + commands_changed.emit(%active.get_children()) func background_clicked(pos): if %wheel.visible: @@ -68,3 +68,6 @@ func background_clicked(pos): if camera: pos = get_viewport().canvas_transform.affine_inverse() * pos _show_command_wheel(pos) + +func get_active(): + return %active.get_children() diff --git a/core/unit.gd b/core/unit.gd index 8029761..2cf94c6 100644 --- a/core/unit.gd +++ b/core/unit.gd @@ -2,6 +2,8 @@ class_name Unit extends Composer +signal promoted + @export var sprite: Texture2D: set(val): sprite = val @@ -68,9 +70,11 @@ func die(): queue_free() func promote_chief(): - is_chief = true - if player: - add_to_group("chief:" + player.code) + if not is_chief: + is_chief = true + if player: + add_to_group("chief:" + player.code) + promoted.emit() func set_chief(chief): if chief is Unit and chief.is_chief and chief != self: diff --git a/main.tscn b/main.tscn index 30fb7ca..eccaa42 100644 --- a/main.tscn +++ b/main.tscn @@ -1,4 +1,4 @@ -[gd_scene load_steps=8 format=3 uid="uid://c6omib6txy3qh"] +[gd_scene load_steps=9 format=3 uid="uid://c6omib6txy3qh"] [ext_resource type="PackedScene" uid="uid://ck41r2je85sm3" path="res://core/ui/camera.tscn" id="1_7evrt"] [ext_resource type="PackedScene" uid="uid://brbtq46uk18gg" path="res://core/battlefield.tscn" id="1_x63ik"] @@ -7,6 +7,7 @@ [ext_resource type="PackedScene" uid="uid://b8uik6q4v35o3" path="res://tac/units/factory.tscn" id="2_wnc50"] [ext_resource type="PackedScene" uid="uid://xoup4vukp3ni" path="res://core/ui/commands.tscn" id="4_1gkbi"] [ext_resource type="PackedScene" uid="uid://dr1e0h27nuam" path="res://tac/commands/delivery.tscn" id="5_fi2mi"] +[ext_resource type="PackedScene" uid="uid://cmaup0f5ud8vr" path="res://tac/commands/defend.tscn" id="5_fmegp"] [node name="main" type="Node2D"] @@ -17,7 +18,7 @@ [node name="commands" parent="." node_paths=PackedStringArray("player", "camera") instance=ExtResource("4_1gkbi")] player = NodePath("../world/player1") camera = NodePath("../camera") -available_commands = [ExtResource("5_fi2mi")] +available_commands = [ExtResource("5_fi2mi"), ExtResource("5_fmegp")] [node name="world" type="Node2D" parent="."] @@ -29,7 +30,8 @@ code = "p1" color = Color(1, 0, 0, 1) code = "p2" -[node name="battlefield" parent="world" instance=ExtResource("1_x63ik")] +[node name="battlefield" parent="world" node_paths=PackedStringArray("commands") instance=ExtResource("1_x63ik")] +commands = NodePath("../../commands") [node name="factory1" parent="world/battlefield" node_paths=PackedStringArray("player") instance=ExtResource("2_wnc50")] position = Vector2(270, 222) @@ -43,3 +45,4 @@ player = NodePath("../../player2") [connection signal="scrolled" from="inputs" to="camera" method="scroll"] [connection signal="zoomed" from="inputs" to="camera" method="change_zoom"] [connection signal="commands_changed" from="commands" to="world/battlefield" method="_on_commands_changed"] +[connection signal="child_entered_tree" from="world/battlefield" to="world/battlefield" method="_on_child_entered_tree"] diff --git a/project.godot b/project.godot index eef998f..20188fc 100644 --- a/project.godot +++ b/project.godot @@ -34,6 +34,11 @@ _global_script_classes=[{ "language": &"GDScript", "path": "res://core/spawner.gd" }, { +"base": "RefCounted", +"class": &"Tools", +"language": &"GDScript", +"path": "res://core/tools.gd" +}, { "base": "Composer", "class": &"Unit", "language": &"GDScript", @@ -50,6 +55,7 @@ _global_script_class_icons={ "Composer": "", "Player": "", "Spawner": "", +"Tools": "", "Unit": "", "Weapon": "" } diff --git a/tac/assets/commands/defend.png b/tac/assets/commands/defend.png new file mode 100644 index 0000000000000000000000000000000000000000..873665032c6183cc5d87810927c5c38543545fde GIT binary patch literal 4372 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 O0000 100.0: + # go near current command + move_to(Tools.jitter(command.global_position, 50.0, 100.0)) + return + else: + var chief = get_chief() + if chief and chief.global_position.distance_to(global_position) > 100.0: + # go near chief + move_to(Tools.jitter(chief.global_position, 50.0, 100.0)) + return + + var enemy := Tools.get_nearest(self, get_tree().get_nodes_in_group("unit"), + func filter(unit): return unit is Unit and unit.player != player) if enemy: - move_to(enemy.global_position + Vector2.from_angle(randf_range(0.0, PI * 2.0)) * randf_range(100.0, 300.0)) + # go near current target enemy + move_to(Tools.jitter(enemy.global_position, 100.0, 300.0)) $turret.set_target(enemy) func chief_maintenance(): @@ -31,7 +40,15 @@ func chief_maintenance(): var chiefs = list_chiefs() # TODO sort by distance, experience and subordinate count if chiefs.size() > 0: - chief = chiefs[0] + set_chief(chiefs[0]) else: promote_chief() _compose() + +func _on_commands_changed(commands): + if is_chief: + var defend := Tools.get_nearest(self, commands, + func filter(command): return command.player == player and command.code == "defend") + + if defend: + current_command = weakref(defend)