1
Fork 0
mirror of https://github.com/Steffo99/hella-farm.git synced 2024-11-25 09:34:24 +00:00

Add state_holder

This commit is contained in:
Lorenzo Rossi 2024-04-16 01:00:49 +02:00
parent c1a1a024cf
commit 6853154a54
12 changed files with 237 additions and 64 deletions

View file

@ -6,29 +6,38 @@ signal move(movement: Vector2)
signal dragged signal dragged
signal dropped signal dropped
@export var state_holder: StateHolder
@onready var mover: Node2D = $"MatchMousePosition" @onready var mover: Node2D = $"MatchMousePosition"
var being_dragged: bool: var is_being_dragged: bool:
get: get:
return being_dragged return state_holder.state == 'dragged'
set(value):
being_dragged = value
mover.set_process(being_dragged)
mover.set_physics_process(being_dragged)
func drag(): func drag():
being_dragged = true if not state_holder.propose_state('dragged', 100, _on_dropped):
return false
_update_mover_process()
dragged.emit() dragged.emit()
func drop(): func drop():
being_dragged = false state_holder.remove_state('dragged')
func _on_dropped():
dropped.emit() dropped.emit()
func _update_mover_process():
var value = is_being_dragged
mover.set_process(value)
mover.set_physics_process(value)
func _ready(): func _ready():
being_dragged = false if state_holder == null:
state_holder = StateHolder.get_default(self)
_update_mover_process()
func _on_move(movement: Vector2): func _on_move(movement: Vector2):
move.emit(movement) if is_being_dragged:
move.emit(movement)

View file

@ -16,19 +16,24 @@ signal move(movement: Vector2)
eater.tag = value eater.tag = value
@export var state_holder: StateHolder
@onready var hunt_target: HuntTarget = $"HuntTarget" @onready var hunt_target: HuntTarget = $"HuntTarget"
@onready var eater: Eater = $"Eater" @onready var eater: Eater = $"Eater"
@onready var move_towards: MoveTowardsTarget = $"MoveTowardsTarget" @onready var move_towards: MoveTowardsTarget = $"MoveTowardsTarget"
func _ready(): func _ready():
if state_holder == null:
state_holder = StateHolder.get_default(self)
hunt_target.tag = tag hunt_target.tag = tag
eater.tag = tag eater.tag = tag
func _on_target_selected(body: Node2D) -> void: func _on_target_selected(body: Node2D) -> void:
move_towards.target = body if state_holder.propose_state('hunt', 60, func(): move_towards.target = null):
move_towards.target = body
func _on_target_abandoned(_body: Node2D) -> void: func _on_target_abandoned(_body: Node2D) -> void:
move_towards.target = null state_holder.remove_state('hunt')
func _on_eater_eaten(edible): func _on_eater_eaten(edible):
eaten.emit(edible) eaten.emit(edible)

View file

@ -11,10 +11,13 @@ signal captured
@export var speed: float = 100.0 @export var speed: float = 100.0
@export var can_detach: bool = false @export var can_detach: bool = false
@export var state_holder: StateHolder
@export var state_name: StringName = ''
enum State { DETACHED, CAPTURED }
var state: State = State.DETACHED var is_captured: bool :
get:
return state_holder.state == state_name
func get_followed_global_position(): func get_followed_global_position():
return game.camera.get_global_mouse_position() return game.camera.get_global_mouse_position()
@ -26,21 +29,27 @@ func get_followed_mouse_position():
return relative_followed_position return relative_followed_position
func _ready():
if state_holder == null:
if state_name == '':
state_holder = StateHolder.new()
else:
state_holder = StateHolder.get_default(self)
func _physics_process(delta: float) -> void: func _physics_process(delta: float) -> void:
match state: if is_captured:
State.CAPTURED: var relative_followed_position: Vector2 = get_followed_mouse_position()
var relative_followed_position: Vector2 = get_followed_mouse_position() var direction: Vector2 = position.direction_to(relative_followed_position)
var direction: Vector2 = position.direction_to(relative_followed_position) var actual_speed: float = min(delta * speed, relative_followed_position.length()) # Don't overshoot.
var actual_speed: float = min(delta * speed, relative_followed_position.length()) # Don't overshoot. var movement: Vector2 = direction * actual_speed
var movement: Vector2 = direction * actual_speed move.emit(movement)
move.emit(movement)
func _on_capture_area_mouse_entered() -> void: func _on_capture_area_mouse_entered() -> void:
state = State.CAPTURED if state_holder.propose_state(state_name, 70, func (): detached.emit()):
captured.emit() captured.emit()
func _on_capture_area_mouse_exited() -> void: func _on_capture_area_mouse_exited() -> void:
if can_detach: if can_detach:
state = State.DETACHED state_holder.remove_state(state_name)
detached.emit()

56
behaviours/random_walk.gd Normal file
View file

@ -0,0 +1,56 @@
extends Node2D
class_name RandomWalk
signal move(movement: Vector2)
@export var speed: float = 100.0
@export var wait_time_mean: float = 4
@export var wait_time_dev: float = 1.5
@export var wander_time_mean: float = 5.0
@export var wander_time_dev: float = 1.0
@export var state_holder: StateHolder
@onready var rand_walk_timer: Timer = Timer.new()
var walk_dir: Vector2 = Vector2.ZERO
func _ready():
if state_holder == null:
state_holder = StateHolder.get_default(self)
add_child(rand_walk_timer)
rand_walk_timer.one_shot = true;
rand_walk_timer.connect("timeout", _on_timer_timeout)
state_holder.on_changed.connect(init_timer)
init_timer()
func _physics_process(delta: float) -> void:
if state_holder.state != 'wander':
return
var movement: Vector2 = speed * walk_dir * delta
move.emit(movement)
func init_timer():
match state_holder.state:
'idle':
var rand_time = max(0, Random.rng.randfn(wait_time_mean, wait_time_dev))
rand_walk_timer.start(rand_time)
'wander':
var rand_time = max(0, Random.rng.randfn(wander_time_mean, wander_time_dev))
rand_walk_timer.start(rand_time)
_:
rand_walk_timer.stop()
func _on_timer_timeout():
match state_holder.state:
'idle':
state_holder.set_state('wander', 10)
walk_dir = Vector2.from_angle(Random.rng.randf_range(0, 2*PI))
'wander':
state_holder.reset_state()

View file

@ -0,0 +1,11 @@
[gd_scene load_steps=2 format=3 uid="uid://dqlhv4j3b62hd"]
[ext_resource type="Script" path="res://behaviours/random_walk.gd" id="1_hs213"]
[node name="RandomWalk" type="Node2D"]
script = ExtResource("1_hs213")
speed = null
wait_time_mean = null
wait_time_dev = null
wander_time_mean = null
wander_time_dev = null

View file

@ -6,7 +6,7 @@ signal move(movement: Vector2)
signal scared signal scared
signal calmed signal calmed
@export var state_holder: StateHolder;
@export_range(1, 500, 1) var speed: float = 300.0 @export_range(1, 500, 1) var speed: float = 300.0
@export_range(0.1, 5, 0.1) var scare_secs: float = 0.2 @export_range(0.1, 5, 0.1) var scare_secs: float = 0.2
@export var directions: Array[Vector2] = [] @export var directions: Array[Vector2] = []
@ -14,25 +14,26 @@ signal calmed
@onready var calm_timer: Timer = $"CalmTimer" @onready var calm_timer: Timer = $"CalmTimer"
@onready var scare_area: HoverDetector = $"ScareArea" @onready var scare_area: HoverDetector = $"ScareArea"
enum State { CALM, SCARED }
var state := State.CALM
var direction := Vector2.ZERO var direction := Vector2.ZERO
const SCARED_PRIORITY = 50
func recheck(): func recheck():
calm_timer.stop()
if scare_area.mouse_inside: if scare_area.mouse_inside:
scare() scare()
else: else:
calm() calm()
func calm(): func calm():
state = State.CALM if state_holder.state == 'scared':
calmed.emit() state_holder.reset_state()
calmed.emit()
func scare(): func scare():
state = State.SCARED if not state_holder.propose_state('scared', SCARED_PRIORITY, recheck):
return
# Pick a direction # Pick a direction
var idx := Random.rng.randi_range(0, len(directions) - 1) var idx := Random.rng.randi_range(0, len(directions) - 1)
direction = directions[idx] direction = directions[idx]
@ -42,15 +43,16 @@ func scare():
func _ready(): func _ready():
if state_holder == null:
state_holder = StateHolder.get_default(self)
if not directions: if not directions:
Log.w(self, "No directions defined, object won't skitter.") Log.w(self, "No directions defined, object won't skitter.")
func _physics_process(delta: float) -> void: func _physics_process(delta: float) -> void:
match state: if state_holder.state == 'scared':
State.SCARED: if len(directions) > 0:
if len(directions) > 0: var movement: Vector2 = direction * delta * speed
var movement: Vector2 = direction * delta * speed move.emit(movement)
move.emit(movement)
func _on_calm_timer_timeout() -> void: func _on_calm_timer_timeout() -> void:
recheck() recheck()

View file

@ -0,0 +1,68 @@
extends Node2D
class_name StateHolder
signal on_changed(state: StringName, priority: int)
const DEFAULT_STATE: StringName = "idle"
const DEFAULT_PRIORITY: int = 0
var _state: StringName = DEFAULT_STATE
var _priority: int
var _exit_callback: Callable = no_callback
var state: StringName :
get:
return _state
var priority: int :
get:
return _priority
func set_state(new_state: StringName, new_priority: int, exit_callback: Callable = no_callback) -> void:
# Priority should be checked outside otherwise we never decrease it
# Prevent recursion problems
var prev_callback = _exit_callback
_state = new_state
_priority = new_priority
_exit_callback = exit_callback
prev_callback.call()
on_changed.emit(new_state, new_priority)
func propose_state(new_state: StringName, new_priority: int, exit_callback: Callable = no_callback) -> bool:
if new_priority > _priority:
set_state(new_state, new_priority, exit_callback)
return true
return false
func reset_state() -> void:
set_state(DEFAULT_STATE, DEFAULT_PRIORITY)
func remove_state(previous_state: StringName) -> void:
if _state == previous_state:
reset_state()
func no_callback():
pass
static func get_default(current: Node2D) -> StateHolder:
# Try some default paths
for path in [^"../StateHolder", ^"../State"]:
var node = current.get_node_or_null(path)
if node != null:
return node
# Try to get a StateHolder from the parent's children
for child in current.get_parent().get_children():
if child is StateHolder:
return child
# Create one (hard way)
Log.w(current, "No StateHolder found, please create one or connect one")
var holder = StateHolder.new()
holder.set_name("StateHolder")
current.get_parent().add_child(holder)
return holder

View file

@ -0,0 +1,6 @@
[gd_scene load_steps=2 format=3 uid="uid://d31h5lcitnlsd"]
[ext_resource type="Script" path="res://behaviours/state_holder.gd" id="1_md0rn"]
[node name="StateHolder" type="Node2D"]
script = ExtResource("1_md0rn")

View file

@ -13,13 +13,6 @@ func _on_eat_target_eaten(target: Edible):
if Random.rng.randf() < skull_chance: if Random.rng.randf() < skull_chance:
skull_spawner.spawn() skull_spawner.spawn()
func _on_draggable_move(movement:Vector2):
if draggable.being_dragged:
move_and_collide(movement)
sprite.handle_move(movement)
func _on_move(movement:Vector2): func _on_move(movement:Vector2):
if not draggable.being_dragged: move_and_collide(movement)
move_and_collide(movement) sprite.handle_move(movement)
sprite.handle_move(movement)

View file

@ -1,4 +1,4 @@
[gd_scene load_steps=10 format=3 uid="uid://4d3ksr3171x4"] [gd_scene load_steps=11 format=3 uid="uid://4d3ksr3171x4"]
[ext_resource type="Script" path="res://entities/imp.gd" id="1_dixpc"] [ext_resource type="Script" path="res://entities/imp.gd" id="1_dixpc"]
[ext_resource type="PackedScene" uid="uid://bxbjfev0lhwws" path="res://behaviours/sprite_left_right.tscn" id="2_eqcdi"] [ext_resource type="PackedScene" uid="uid://bxbjfev0lhwws" path="res://behaviours/sprite_left_right.tscn" id="2_eqcdi"]
@ -6,8 +6,9 @@
[ext_resource type="PackedScene" uid="uid://b7bdlh5akhi8s" path="res://behaviours/eat_target.tscn" id="3_iybf3"] [ext_resource type="PackedScene" uid="uid://b7bdlh5akhi8s" path="res://behaviours/eat_target.tscn" id="3_iybf3"]
[ext_resource type="Texture2D" uid="uid://crhwsob76ieya" path="res://entities/imp_left.png" id="3_qda0k"] [ext_resource type="Texture2D" uid="uid://crhwsob76ieya" path="res://entities/imp_left.png" id="3_qda0k"]
[ext_resource type="Texture2D" uid="uid://bubehid53q8h1" path="res://entities/imp_right.png" id="4_0sckn"] [ext_resource type="Texture2D" uid="uid://bubehid53q8h1" path="res://entities/imp_right.png" id="4_0sckn"]
[ext_resource type="PackedScene" path="res://behaviours/spawner.tscn" id="4_d8lgm"] [ext_resource type="PackedScene" uid="uid://cyrg770fsetyu" path="res://behaviours/spawner.tscn" id="4_d8lgm"]
[ext_resource type="PackedScene" uid="uid://uoxwjpmgg27a" path="res://entities/gold.tscn" id="5_yrfoq"] [ext_resource type="PackedScene" uid="uid://uoxwjpmgg27a" path="res://entities/gold.tscn" id="5_yrfoq"]
[ext_resource type="PackedScene" uid="uid://d31h5lcitnlsd" path="res://behaviours/state_holder.tscn" id="9_myo4m"]
[sub_resource type="CircleShape2D" id="CircleShape2D_ide4n"] [sub_resource type="CircleShape2D" id="CircleShape2D_ide4n"]
radius = 8.0 radius = 8.0
@ -27,8 +28,9 @@ right_texture = ExtResource("4_0sckn")
scale = Vector2(4, 4) scale = Vector2(4, 4)
shape = SubResource("CircleShape2D_ide4n") shape = SubResource("CircleShape2D_ide4n")
[node name="Draggable" parent="." instance=ExtResource("3_4528r")] [node name="Draggable" parent="." node_paths=PackedStringArray("state_holder") instance=ExtResource("3_4528r")]
scale = Vector2(4, 4) scale = Vector2(4, 4)
state_holder = NodePath("../StateHolder")
[node name="DragSound" type="AudioStreamPlayer2D" parent="Draggable"] [node name="DragSound" type="AudioStreamPlayer2D" parent="Draggable"]
scale = Vector2(0.5, 0.5) scale = Vector2(0.5, 0.5)
@ -36,13 +38,16 @@ scale = Vector2(0.5, 0.5)
[node name="DropSound" type="AudioStreamPlayer2D" parent="Draggable"] [node name="DropSound" type="AudioStreamPlayer2D" parent="Draggable"]
scale = Vector2(0.5, 0.5) scale = Vector2(0.5, 0.5)
[node name="EatTarget" parent="." instance=ExtResource("3_iybf3")] [node name="EatTarget" parent="." node_paths=PackedStringArray("state_holder") instance=ExtResource("3_iybf3")]
scale = Vector2(2, 2) scale = Vector2(2, 2)
tag = &"Sheep" tag = &"Sheep"
state_holder = NodePath("../StateHolder")
[node name="SkullSpawner" parent="." instance=ExtResource("4_d8lgm")] [node name="SkullSpawner" parent="." instance=ExtResource("4_d8lgm")]
scene = ExtResource("5_yrfoq") scene = ExtResource("5_yrfoq")
[connection signal="move" from="Draggable" to="." method="_on_draggable_move"] [node name="StateHolder" parent="." instance=ExtResource("9_myo4m")]
[connection signal="move" from="Draggable" to="." method="_on_move"]
[connection signal="eaten" from="EatTarget" to="." method="_on_eat_target_eaten"] [connection signal="eaten" from="EatTarget" to="." method="_on_eat_target_eaten"]
[connection signal="move" from="EatTarget" to="." method="_on_move"] [connection signal="move" from="EatTarget" to="." method="_on_move"]

View file

@ -9,14 +9,8 @@ class_name Sheep
func _on_move(movement: Vector2) -> void: func _on_move(movement: Vector2) -> void:
if not draggable.being_dragged: move_and_collide(movement)
move_and_collide(movement) sprite.handle_move(movement)
sprite.handle_move(movement)
func _on_drag_move(movement: Vector2) -> void:
if draggable.being_dragged:
move_and_collide(movement)
sprite.handle_move(movement)
func _on_draggable_dragged() -> void: func _on_draggable_dragged() -> void:
drag_sound.play() drag_sound.play()

View file

@ -1,4 +1,4 @@
[gd_scene load_steps=11 format=3 uid="uid://bc2bm8lbol18w"] [gd_scene load_steps=13 format=3 uid="uid://bc2bm8lbol18w"]
[ext_resource type="Script" path="res://entities/sheep.gd" id="1_4dmll"] [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"] [ext_resource type="Texture2D" uid="uid://iljp5yn3ehfk" path="res://entities/sheep_left.png" id="2_t13f5"]
@ -9,6 +9,8 @@
[ext_resource type="AudioStream" uid="uid://buxgivpkh8dyl" path="res://entities/drop.wav" id="4_nxjnl"] [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="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://dfdr3e32lohq" path="res://behaviours/edible.tscn" id="6_3odsh"]
[ext_resource type="PackedScene" uid="uid://dqlhv4j3b62hd" path="res://behaviours/random_walk.tscn" id="6_ekxsq"]
[ext_resource type="PackedScene" uid="uid://d31h5lcitnlsd" path="res://behaviours/state_holder.tscn" id="10_mm8v8"]
[sub_resource type="CircleShape2D" id="CircleShape2D_c5tcn"] [sub_resource type="CircleShape2D" id="CircleShape2D_c5tcn"]
radius = 8.0 radius = 8.0
@ -28,13 +30,23 @@ right_texture = ExtResource("4_5qoof")
scale = Vector2(3, 3) scale = Vector2(3, 3)
shape = SubResource("CircleShape2D_c5tcn") shape = SubResource("CircleShape2D_c5tcn")
[node name="MoveTowardsMouse" parent="." instance=ExtResource("2_tfd2i")] [node name="MoveTowardsMouse" parent="." node_paths=PackedStringArray("state_holder") instance=ExtResource("2_tfd2i")]
scale = Vector2(30, 30) scale = Vector2(30, 30)
speed = -30.0 speed = -30.0
can_detach = true can_detach = true
state_holder = NodePath("../StateHolder")
state_name = &"scared"
[node name="Draggable" parent="." instance=ExtResource("3_8ku7r")] [node name="RandomWalk" parent="." instance=ExtResource("6_ekxsq")]
speed = 10.0
wait_time_mean = 4.0
wait_time_dev = 1.5
wander_time_mean = 5.0
wander_time_dev = 1.0
[node name="Draggable" parent="." node_paths=PackedStringArray("state_holder") instance=ExtResource("3_8ku7r")]
scale = Vector2(3, 3) scale = Vector2(3, 3)
state_holder = NodePath("../StateHolder")
[node name="DragSound" type="AudioStreamPlayer2D" parent="Draggable"] [node name="DragSound" type="AudioStreamPlayer2D" parent="Draggable"]
scale = Vector2(0.5, 0.5) scale = Vector2(0.5, 0.5)
@ -47,7 +59,10 @@ stream = ExtResource("4_nxjnl")
[node name="Edible" parent="." instance=ExtResource("6_3odsh")] [node name="Edible" parent="." instance=ExtResource("6_3odsh")]
tag = &"Sheep" tag = &"Sheep"
[node name="StateHolder" parent="." instance=ExtResource("10_mm8v8")]
[connection signal="move" from="MoveTowardsMouse" to="." method="_on_move"] [connection signal="move" from="MoveTowardsMouse" to="." method="_on_move"]
[connection signal="move" from="RandomWalk" to="." method="_on_move"]
[connection signal="dragged" from="Draggable" to="." method="_on_draggable_dragged"] [connection signal="dragged" from="Draggable" to="." method="_on_draggable_dragged"]
[connection signal="dropped" from="Draggable" to="." method="_on_draggable_dropped"] [connection signal="dropped" from="Draggable" to="." method="_on_draggable_dropped"]
[connection signal="move" from="Draggable" to="." method="_on_drag_move"] [connection signal="move" from="Draggable" to="." method="_on_move"]