diff --git a/behaviours/move.gd b/behaviours/move.gd index ae3e420..38787cd 100644 --- a/behaviours/move.gd +++ b/behaviours/move.gd @@ -4,3 +4,6 @@ class_name Move signal move(norm: Vector2) + +## Whether this component should emit [signal move]. +@export var enabled: bool = true diff --git a/behaviours/move_straight.gd b/behaviours/move_straight.gd index 67ddc10..761b7fd 100644 --- a/behaviours/move_straight.gd +++ b/behaviours/move_straight.gd @@ -1,11 +1,18 @@ extends Move class_name MoveStraight - ## A [Move] that moves in a fixed direction. -@export var direction: Vector2 +signal changed_direction(new: Vector2) + + +@export var direction: Vector2: + get: + return direction + set(value): + direction = value + changed_direction.emit(direction) func randomize_direction() -> void: @@ -13,4 +20,5 @@ func randomize_direction() -> void: func _physics_process(_delta: float) -> void: - move.emit(direction) + if enabled: + move.emit(direction) diff --git a/behaviours/move_towards.gd b/behaviours/move_towards.gd index dcb20ad..c2f8ca0 100644 --- a/behaviours/move_towards.gd +++ b/behaviours/move_towards.gd @@ -5,7 +5,15 @@ class_name MoveTowards ## A [Move] that moves towards the [field position] of a [field target]. -@export var target: Node2D = null +signal changed_target(target: Node2D) + + +@export var target: Node2D = null: + get: + return target + set(value): + value = target + changed_target.emit() func set_target(value: Node2D) -> void: @@ -15,14 +23,11 @@ func clear_target() -> void: target = null -func _ready() -> void: - if target == null: - Log.w(self, "No target is set, no signals will be emitted.") - func _physics_process(_delta: float) -> void: - if target: - var gap = target.global_position - global_position - var norm = gap.normalized() - move.emit(norm) - else: - move.emit(Vector2.ZERO) + if enabled: + if target: + var gap = target.global_position - global_position + var norm = gap.normalized() + move.emit(norm) + else: + move.emit(Vector2.ZERO) diff --git a/behaviours/priority.gd b/behaviours/priority.gd index e1e9e39..6be6430 100644 --- a/behaviours/priority.gd +++ b/behaviours/priority.gd @@ -3,8 +3,31 @@ extends Node class_name Priority +## Keeps track of the object's priority + + +signal priority_changed(new: int, old: int) +signal priority_changed_no_args + + @export var priority: int = 0 +func set_priority(value: int): + var old = priority + priority = value + priority_changed.emit(priority, old) + +func set_priority_if_truthy(variant: Variant, truthy: int, falsy: int = 0): + if variant: + set_priority(truthy) + else: + set_priority(falsy) + func get_ref() -> Node: return get_parent() + + +func _on_priority_changed(new: int, _old: int) -> void: + Log.p(self, "Priority changed to: %s" % new) + priority_changed_no_args.emit() diff --git a/behaviours/priority.tscn b/behaviours/priority.tscn index 1c489db..e240944 100644 --- a/behaviours/priority.tscn +++ b/behaviours/priority.tscn @@ -4,3 +4,5 @@ [node name="Priority" type="Node"] script = ExtResource("1_8u7ji") + +[connection signal="priority_changed" from="." to="." method="_on_priority_changed"] diff --git a/behaviours/sampler.gd b/behaviours/sampler.gd index 1c63eea..4e08e7f 100644 --- a/behaviours/sampler.gd +++ b/behaviours/sampler.gd @@ -6,7 +6,24 @@ class_name Sampler ## Abstract base class for sampling a certain reference among multiple. +signal notify_selected(node: Node) +signal notify_deselected(node: Node) + + +@export var possibilities: Array[Node] = [] + + ## Get a reference. -func sample() -> Object: +func sample() -> Node: Log.e(self, "Not implemented.") return null + +## Get all possible nodes referenced by [field possibilities]. +func get_refs() -> Array[Node]: + return possibilities + +## Set the "enabled" property on +func enable() -> void: + var selected = sample() + for possibility in get_refs(): + possibility.enabled = (selected == possibility) diff --git a/behaviours/sampler_priority.gd b/behaviours/sampler_priority.gd index ca3ea44..e2d36bd 100644 --- a/behaviours/sampler_priority.gd +++ b/behaviours/sampler_priority.gd @@ -5,9 +5,6 @@ class_name SamplerPriority ## Always sample the object with the highest priority in the queue. -@export var possibilities: Array[Priority] = [] - - ## Get a reference. func sample() -> Priority: if len(possibilities) == 0: @@ -16,10 +13,17 @@ func sample() -> Priority: # FIXME: Change this to something more efficient when needed var highest_possibility: Priority = null for possibility in possibilities: - if possibility.priority > highest_possibility.priority: + if highest_possibility == null or possibility.priority > highest_possibility.priority: highest_possibility = possibility if highest_possibility == null: return null + Log.p(self, "Sampled: %s" % highest_possibility) return highest_possibility.get_ref() + +func get_refs() -> Array[Node]: + var refs: Array[Node] = [] + for possibility in possibilities: + refs.append(possibility.get_ref()) + return refs diff --git a/behaviours/sampler_random.gd b/behaviours/sampler_random.gd index 5380d3e..88e4e82 100644 --- a/behaviours/sampler_random.gd +++ b/behaviours/sampler_random.gd @@ -5,10 +5,7 @@ class_name SamplerRandom ## Sample a random reference from the array. -@export var possibilities: Array = [] - - -func sample() -> Object: +func sample() -> Node: if len(possibilities) == 0: return null return Random.sample(possibilities) diff --git a/behaviours/sampler_weighted.gd b/behaviours/sampler_weighted.gd index cc81bc0..84bbc0d 100644 --- a/behaviours/sampler_weighted.gd +++ b/behaviours/sampler_weighted.gd @@ -5,11 +5,10 @@ class_name SamplerWeighted ## Sample a random reference from the array, considering the given weights. -@export var possibilities: Array = [] @export var weights: Array[int] = [] -func sample() -> Object: +func sample() -> Node: var total = compute_total_weight() if total == 0: return null diff --git a/behaviours/tracker.gd b/behaviours/tracker.gd index 111072f..deaf1a9 100644 --- a/behaviours/tracker.gd +++ b/behaviours/tracker.gd @@ -1,3 +1,4 @@ +@icon("res://behaviours/tracker.svg") extends Area2D class_name Tracker @@ -15,6 +16,9 @@ func track(body: Node2D) -> bool: if act: tracking.push_back(body) tracked.emit(body) + # Handle TrackerTracker + for tracker_tracker in body.find_children("*", "TrackerTracker", false, false): + tracker_tracker.track(self) return act func untrack(body: Node2D) -> bool: @@ -22,6 +26,9 @@ func untrack(body: Node2D) -> bool: if act: tracking.erase(body) untracked.emit(body) + # Handle TrackerTracker + for tracker_tracker in body.find_children("*", "TrackerTracker", false, false): + tracker_tracker.untrack(self) return act func log_tracked(body: Node2D) -> void: diff --git a/behaviours/tracker.svg b/behaviours/tracker.svg new file mode 100644 index 0000000..327b1f2 --- /dev/null +++ b/behaviours/tracker.svg @@ -0,0 +1,39 @@ + + + + + + + diff --git a/behaviours/tracker_edible.svg.import b/behaviours/tracker.svg.import similarity index 71% rename from behaviours/tracker_edible.svg.import rename to behaviours/tracker.svg.import index 3d51dc1..32be8d3 100644 --- a/behaviours/tracker_edible.svg.import +++ b/behaviours/tracker.svg.import @@ -2,16 +2,16 @@ importer="texture" type="CompressedTexture2D" -uid="uid://8443tt517pkv" -path="res://.godot/imported/tracker_edible.svg-fb108b51d33883549e8e498a16701c4f.ctex" +uid="uid://b3myvaqud7w0p" +path="res://.godot/imported/tracker.svg-98e9b55805c6d630e7da524610510efd.ctex" metadata={ "vram_texture": false } [deps] -source_file="res://behaviours/tracker_edible.svg" -dest_files=["res://.godot/imported/tracker_edible.svg-fb108b51d33883549e8e498a16701c4f.ctex"] +source_file="res://behaviours/tracker.svg" +dest_files=["res://.godot/imported/tracker.svg-98e9b55805c6d630e7da524610510efd.ctex"] [params] diff --git a/behaviours/tracker_edible.gd b/behaviours/tracker_edible.gd index a2aed3b..da5ece4 100644 --- a/behaviours/tracker_edible.gd +++ b/behaviours/tracker_edible.gd @@ -1,4 +1,3 @@ -@icon("res://behaviours/tracker_edible.svg") extends Tracker class_name EdibleTracker @@ -9,7 +8,7 @@ class_name EdibleTracker func check_diet_then_track(body: Node2D) -> void: - var edibles: Array = body.find_children("Edible", "Edible", false, false) + var edibles: Array = body.find_children("*", "Edible", false, false) for edible in edibles: if edible.tag in acceptable_diets: track(body) diff --git a/behaviours/tracker_edible.svg b/behaviours/tracker_edible.svg deleted file mode 100644 index 34115c7..0000000 --- a/behaviours/tracker_edible.svg +++ /dev/null @@ -1,39 +0,0 @@ - - - - - - - diff --git a/behaviours/tracker_tracker.gd b/behaviours/tracker_tracker.gd new file mode 100644 index 0000000..98c08c7 --- /dev/null +++ b/behaviours/tracker_tracker.gd @@ -0,0 +1,6 @@ +extends Tracker +class_name TrackerTracker + +## Tracks [Tracker]s tracking the parent. +## +## Shape is ignored. diff --git a/behaviours/tracker_tracker.tscn b/behaviours/tracker_tracker.tscn new file mode 100644 index 0000000..beadebe --- /dev/null +++ b/behaviours/tracker_tracker.tscn @@ -0,0 +1,10 @@ +[gd_scene load_steps=2 format=3 uid="uid://c5pyp5hvthdof"] + +[ext_resource type="Script" path="res://behaviours/tracker_tracker.gd" id="1_7b21r"] + +[node name="TrackerTracker" type="Area2D"] +collision_layer = 0 +collision_mask = 0 +monitoring = false +monitorable = false +script = ExtResource("1_7b21r") diff --git a/entities/sheep.tscn b/entities/sheep.tscn index d29f4ba..065360d 100644 --- a/entities/sheep.tscn +++ b/entities/sheep.tscn @@ -1,4 +1,4 @@ -[gd_scene load_steps=14 format=3 uid="uid://bc2bm8lbol18w"] +[gd_scene load_steps=21 format=3 uid="uid://bc2bm8lbol18w"] [ext_resource type="Script" path="res://entities/sheep.gd" id="1_4dmll"] [ext_resource type="Texture2D" uid="uid://iljp5yn3ehfk" path="res://entities/sheep_left.png" id="2_t13f5"] @@ -8,6 +8,12 @@ [ext_resource type="AudioStream" uid="uid://buxgivpkh8dyl" path="res://entities/drop.wav" id="4_nxjnl"] [ext_resource type="AudioStream" uid="uid://bmfscpnugaejk" path="res://entities/sheep_drag.wav" id="4_oalqu"] [ext_resource type="PackedScene" uid="uid://dfdr3e32lohq" path="res://behaviours/edible.tscn" id="6_3odsh"] +[ext_resource type="PackedScene" uid="uid://djcwis8ycrq85" path="res://behaviours/sampler_priority.tscn" id="9_s5lod"] +[ext_resource type="PackedScene" uid="uid://dk1ipq7dhkhf3" path="res://behaviours/move_straight.tscn" id="10_05kcd"] +[ext_resource type="PackedScene" uid="uid://cm67ko1k6kn4u" path="res://behaviours/priority.tscn" id="11_0jlmk"] +[ext_resource type="PackedScene" uid="uid://cml7rqvyfuagx" path="res://behaviours/move_towards.tscn" id="12_x2g3x"] +[ext_resource type="PackedScene" uid="uid://cbg5kgwxusvxf" path="res://behaviours/cursor_detector.tscn" id="13_5fkdr"] +[ext_resource type="PackedScene" uid="uid://c5pyp5hvthdof" path="res://behaviours/tracker_tracker.tscn" id="14_eqowb"] [sub_resource type="CircleShape2D" id="CircleShape2D_c5tcn"] radius = 8.0 @@ -70,6 +76,9 @@ _data = { "wobble": SubResource("Animation_lxieb") } +[sub_resource type="CircleShape2D" id="CircleShape2D_etpf6"] +radius = 144.0 + [node name="Sheep" type="CharacterBody2D"] collision_layer = 8 collision_mask = 14 @@ -97,12 +106,66 @@ stream = ExtResource("4_oalqu") scale = Vector2(0.5, 0.5) stream = ExtResource("4_nxjnl") -[node name="Edible" parent="." instance=ExtResource("6_3odsh")] - [node name="Animator" type="AnimationPlayer" parent="."] libraries = { "": SubResource("AnimationLibrary_6mutq") } +[node name="Edible" parent="." instance=ExtResource("6_3odsh")] + +[node name="Movement" parent="." node_paths=PackedStringArray("possibilities") instance=ExtResource("9_s5lod")] +possibilities = [NodePath("Idle/IdlePriority"), NodePath("Wander/WanderPriority"), NodePath("RunFromMouse/RunFromMousePriority"), NodePath("RunFromHunter/RunFromHunterPriority")] + +[node name="Idle" parent="Movement" instance=ExtResource("10_05kcd")] +enabled = false + +[node name="IdlePriority" parent="Movement/Idle" instance=ExtResource("11_0jlmk")] +priority = 10 + +[node name="BoredTimer" type="Timer" parent="Movement/Idle"] +one_shot = true +autostart = true + +[node name="Wander" parent="Movement" instance=ExtResource("10_05kcd")] +enabled = false + +[node name="WanderPriority" parent="Movement/Wander" instance=ExtResource("11_0jlmk")] + +[node name="TiredTimer" type="Timer" parent="Movement/Wander"] +one_shot = true + +[node name="RunFromMouse" parent="Movement" instance=ExtResource("12_x2g3x")] +enabled = false + +[node name="RunFromMousePriority" parent="Movement/RunFromMouse" instance=ExtResource("11_0jlmk")] + +[node name="CursorDetector" parent="Movement/RunFromMouse" instance=ExtResource("13_5fkdr")] + +[node name="Shape" type="CollisionShape2D" parent="Movement/RunFromMouse/CursorDetector"] +shape = SubResource("CircleShape2D_etpf6") +debug_color = Color(1, 0, 0, 0.0470588) + +[node name="RunFromHunter" parent="Movement" instance=ExtResource("12_x2g3x")] +enabled = false + +[node name="RunFromHunterPriority" parent="Movement/RunFromHunter" instance=ExtResource("11_0jlmk")] + +[node name="TrackerTracker" parent="Movement/RunFromHunter" instance=ExtResource("14_eqowb")] + [connection signal="dragged" from="Draggable" to="." method="_on_draggable_dragged"] [connection signal="dropped" from="Draggable" to="." method="_on_draggable_dropped"] +[connection signal="ready" from="Movement" to="Movement" method="enable"] +[connection signal="priority_changed_no_args" from="Movement/Idle/IdlePriority" to="Movement" method="enable"] +[connection signal="timeout" from="Movement/Idle/BoredTimer" to="Movement/Wander" method="randomize_direction"] +[connection signal="changed_direction" from="Movement/Wander" to="Movement/Wander/WanderPriority" method="set_priority_if_truthy" binds= [20]] +[connection signal="changed_direction" from="Movement/Wander" to="Movement/Wander/TiredTimer" method="start" unbinds=1] +[connection signal="priority_changed_no_args" from="Movement/Wander/WanderPriority" to="Movement" method="enable"] +[connection signal="timeout" from="Movement/Wander/TiredTimer" to="Movement/Wander/WanderPriority" method="set_priority" binds= [0]] +[connection signal="changed_target" from="Movement/RunFromMouse" to="Movement/RunFromMouse/RunFromMousePriority" method="set_priority_if_truthy" binds= [30]] +[connection signal="priority_changed_no_args" from="Movement/RunFromMouse/RunFromMousePriority" to="Movement" method="enable"] +[connection signal="cursor_entered" from="Movement/RunFromMouse/CursorDetector" to="Movement/RunFromMouse" method="set_target"] +[connection signal="cursor_exited" from="Movement/RunFromMouse/CursorDetector" to="Movement/RunFromMouse" method="clear_target" unbinds=1] +[connection signal="changed_target" from="Movement/RunFromHunter" to="Movement/RunFromHunter/RunFromHunterPriority" method="set_priority_if_truthy" binds= [40]] +[connection signal="priority_changed_no_args" from="Movement/RunFromHunter/RunFromHunterPriority" to="Movement" method="enable"] +[connection signal="tracked" from="Movement/RunFromHunter/TrackerTracker" to="Movement/RunFromHunter" method="set_target"] +[connection signal="untracked" from="Movement/RunFromHunter/TrackerTracker" to="Movement/RunFromHunter" method="clear_target" unbinds=1]