mirror of
https://github.com/Steffo99/hella-farm.git
synced 2024-11-21 23:54:23 +00:00
Add state_holder
This commit is contained in:
parent
c1a1a024cf
commit
6853154a54
12 changed files with 237 additions and 64 deletions
|
@ -6,29 +6,38 @@ signal move(movement: Vector2)
|
|||
signal dragged
|
||||
signal dropped
|
||||
|
||||
@export var state_holder: StateHolder
|
||||
|
||||
@onready var mover: Node2D = $"MatchMousePosition"
|
||||
|
||||
var being_dragged: bool:
|
||||
var is_being_dragged: bool:
|
||||
get:
|
||||
return being_dragged
|
||||
set(value):
|
||||
being_dragged = value
|
||||
mover.set_process(being_dragged)
|
||||
mover.set_physics_process(being_dragged)
|
||||
return state_holder.state == 'dragged'
|
||||
|
||||
|
||||
func drag():
|
||||
being_dragged = true
|
||||
if not state_holder.propose_state('dragged', 100, _on_dropped):
|
||||
return false
|
||||
|
||||
_update_mover_process()
|
||||
dragged.emit()
|
||||
|
||||
func drop():
|
||||
being_dragged = false
|
||||
state_holder.remove_state('dragged')
|
||||
|
||||
func _on_dropped():
|
||||
dropped.emit()
|
||||
|
||||
func _update_mover_process():
|
||||
var value = is_being_dragged
|
||||
mover.set_process(value)
|
||||
mover.set_physics_process(value)
|
||||
|
||||
func _ready():
|
||||
being_dragged = false
|
||||
if state_holder == null:
|
||||
state_holder = StateHolder.get_default(self)
|
||||
_update_mover_process()
|
||||
|
||||
func _on_move(movement: Vector2):
|
||||
move.emit(movement)
|
||||
if is_being_dragged:
|
||||
move.emit(movement)
|
||||
|
|
|
@ -16,19 +16,24 @@ signal move(movement: Vector2)
|
|||
eater.tag = value
|
||||
|
||||
|
||||
@export var state_holder: StateHolder
|
||||
|
||||
@onready var hunt_target: HuntTarget = $"HuntTarget"
|
||||
@onready var eater: Eater = $"Eater"
|
||||
@onready var move_towards: MoveTowardsTarget = $"MoveTowardsTarget"
|
||||
|
||||
func _ready():
|
||||
if state_holder == null:
|
||||
state_holder = StateHolder.get_default(self)
|
||||
hunt_target.tag = tag
|
||||
eater.tag = tag
|
||||
|
||||
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:
|
||||
move_towards.target = null
|
||||
state_holder.remove_state('hunt')
|
||||
|
||||
func _on_eater_eaten(edible):
|
||||
eaten.emit(edible)
|
||||
|
|
|
@ -11,10 +11,13 @@ signal captured
|
|||
@export var speed: float = 100.0
|
||||
@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():
|
||||
return game.camera.get_global_mouse_position()
|
||||
|
@ -26,21 +29,27 @@ func get_followed_mouse_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:
|
||||
match state:
|
||||
State.CAPTURED:
|
||||
var relative_followed_position: Vector2 = get_followed_mouse_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 movement: Vector2 = direction * actual_speed
|
||||
move.emit(movement)
|
||||
if is_captured:
|
||||
var relative_followed_position: Vector2 = get_followed_mouse_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 movement: Vector2 = direction * actual_speed
|
||||
move.emit(movement)
|
||||
|
||||
func _on_capture_area_mouse_entered() -> void:
|
||||
state = State.CAPTURED
|
||||
captured.emit()
|
||||
if state_holder.propose_state(state_name, 70, func (): detached.emit()):
|
||||
captured.emit()
|
||||
|
||||
func _on_capture_area_mouse_exited() -> void:
|
||||
if can_detach:
|
||||
state = State.DETACHED
|
||||
detached.emit()
|
||||
state_holder.remove_state(state_name)
|
||||
|
||||
|
||||
|
|
56
behaviours/random_walk.gd
Normal file
56
behaviours/random_walk.gd
Normal 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()
|
11
behaviours/random_walk.tscn
Normal file
11
behaviours/random_walk.tscn
Normal 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
|
|
@ -6,7 +6,7 @@ signal move(movement: Vector2)
|
|||
signal scared
|
||||
signal calmed
|
||||
|
||||
|
||||
@export var state_holder: StateHolder;
|
||||
@export_range(1, 500, 1) var speed: float = 300.0
|
||||
@export_range(0.1, 5, 0.1) var scare_secs: float = 0.2
|
||||
@export var directions: Array[Vector2] = []
|
||||
|
@ -14,25 +14,26 @@ signal calmed
|
|||
@onready var calm_timer: Timer = $"CalmTimer"
|
||||
@onready var scare_area: HoverDetector = $"ScareArea"
|
||||
|
||||
|
||||
enum State { CALM, SCARED }
|
||||
|
||||
var state := State.CALM
|
||||
var direction := Vector2.ZERO
|
||||
|
||||
const SCARED_PRIORITY = 50
|
||||
|
||||
|
||||
func recheck():
|
||||
calm_timer.stop()
|
||||
if scare_area.mouse_inside:
|
||||
scare()
|
||||
else:
|
||||
calm()
|
||||
|
||||
func calm():
|
||||
state = State.CALM
|
||||
calmed.emit()
|
||||
if state_holder.state == 'scared':
|
||||
state_holder.reset_state()
|
||||
calmed.emit()
|
||||
|
||||
func scare():
|
||||
state = State.SCARED
|
||||
if not state_holder.propose_state('scared', SCARED_PRIORITY, recheck):
|
||||
return
|
||||
# Pick a direction
|
||||
var idx := Random.rng.randi_range(0, len(directions) - 1)
|
||||
direction = directions[idx]
|
||||
|
@ -42,15 +43,16 @@ func scare():
|
|||
|
||||
|
||||
func _ready():
|
||||
if state_holder == null:
|
||||
state_holder = StateHolder.get_default(self)
|
||||
if not directions:
|
||||
Log.w(self, "No directions defined, object won't skitter.")
|
||||
|
||||
func _physics_process(delta: float) -> void:
|
||||
match state:
|
||||
State.SCARED:
|
||||
if len(directions) > 0:
|
||||
var movement: Vector2 = direction * delta * speed
|
||||
move.emit(movement)
|
||||
if state_holder.state == 'scared':
|
||||
if len(directions) > 0:
|
||||
var movement: Vector2 = direction * delta * speed
|
||||
move.emit(movement)
|
||||
|
||||
func _on_calm_timer_timeout() -> void:
|
||||
recheck()
|
||||
|
|
68
behaviours/state_holder.gd
Normal file
68
behaviours/state_holder.gd
Normal 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
|
||||
|
||||
|
6
behaviours/state_holder.tscn
Normal file
6
behaviours/state_holder.tscn
Normal 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")
|
|
@ -13,13 +13,6 @@ func _on_eat_target_eaten(target: Edible):
|
|||
if Random.rng.randf() < skull_chance:
|
||||
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):
|
||||
if not draggable.being_dragged:
|
||||
move_and_collide(movement)
|
||||
sprite.handle_move(movement)
|
||||
move_and_collide(movement)
|
||||
sprite.handle_move(movement)
|
||||
|
|
|
@ -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="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="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="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://d31h5lcitnlsd" path="res://behaviours/state_holder.tscn" id="9_myo4m"]
|
||||
|
||||
[sub_resource type="CircleShape2D" id="CircleShape2D_ide4n"]
|
||||
radius = 8.0
|
||||
|
@ -27,8 +28,9 @@ right_texture = ExtResource("4_0sckn")
|
|||
scale = Vector2(4, 4)
|
||||
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)
|
||||
state_holder = NodePath("../StateHolder")
|
||||
|
||||
[node name="DragSound" type="AudioStreamPlayer2D" parent="Draggable"]
|
||||
scale = Vector2(0.5, 0.5)
|
||||
|
@ -36,13 +38,16 @@ scale = Vector2(0.5, 0.5)
|
|||
[node name="DropSound" type="AudioStreamPlayer2D" parent="Draggable"]
|
||||
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)
|
||||
tag = &"Sheep"
|
||||
state_holder = NodePath("../StateHolder")
|
||||
|
||||
[node name="SkullSpawner" parent="." instance=ExtResource("4_d8lgm")]
|
||||
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="move" from="EatTarget" to="." method="_on_move"]
|
||||
|
|
|
@ -9,14 +9,8 @@ class_name Sheep
|
|||
|
||||
|
||||
func _on_move(movement: Vector2) -> void:
|
||||
if not draggable.being_dragged:
|
||||
move_and_collide(movement)
|
||||
sprite.handle_move(movement)
|
||||
|
||||
func _on_drag_move(movement: Vector2) -> void:
|
||||
if draggable.being_dragged:
|
||||
move_and_collide(movement)
|
||||
sprite.handle_move(movement)
|
||||
move_and_collide(movement)
|
||||
sprite.handle_move(movement)
|
||||
|
||||
func _on_draggable_dragged() -> void:
|
||||
drag_sound.play()
|
||||
|
|
|
@ -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="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://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://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"]
|
||||
radius = 8.0
|
||||
|
@ -28,13 +30,23 @@ right_texture = ExtResource("4_5qoof")
|
|||
scale = Vector2(3, 3)
|
||||
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)
|
||||
speed = -30.0
|
||||
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)
|
||||
state_holder = NodePath("../StateHolder")
|
||||
|
||||
[node name="DragSound" type="AudioStreamPlayer2D" parent="Draggable"]
|
||||
scale = Vector2(0.5, 0.5)
|
||||
|
@ -47,7 +59,10 @@ stream = ExtResource("4_nxjnl")
|
|||
[node name="Edible" parent="." instance=ExtResource("6_3odsh")]
|
||||
tag = &"Sheep"
|
||||
|
||||
[node name="StateHolder" parent="." instance=ExtResource("10_mm8v8")]
|
||||
|
||||
[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="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"]
|
||||
|
|
Loading…
Reference in a new issue