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]