diff --git a/project.godot b/project.godot index 11d90c8..2422925 100644 --- a/project.godot +++ b/project.godot @@ -23,12 +23,12 @@ run/max_fps=60 [autoload] +BeehaveGlobalMetrics="*res://addons/beehave/metrics/beehave_global_metrics.gd" +BeehaveGlobalDebugger="*res://addons/beehave/debug/global_debugger.gd" GameRuntime="*res://src/game/game_runtime.gd" ItemCatalog="*res://src/items/item_catalog.tscn" GameSettings="*res://src/game/game_settings.gd" GameManager="*res://src/game/game_manager.tscn" -BeehaveGlobalMetrics="*res://addons/beehave/metrics/beehave_global_metrics.gd" -BeehaveGlobalDebugger="*res://addons/beehave/debug/global_debugger.gd" [debug] diff --git a/src/world/grunk_beast/behaviors/actions/get_random_target.gd b/src/world/grunk_beast/behaviors/actions/get_random_target.gd new file mode 100644 index 0000000..7100d9d --- /dev/null +++ b/src/world/grunk_beast/behaviors/actions/get_random_target.gd @@ -0,0 +1,30 @@ +@tool +class_name GetRandomTarget extends ActionLeaf +## Picks a random navigable point near the actor and writes it to the blackboard. +## +## If the random point is not navigable, will return RUNNING and pick a new point next tick. + +## Blackboard key under which to store the random target. +@export var blackboard_key := "target" + +## Standard deviation of position in each direction, in game units. +@export var st_dev := Vector3(1, 0, 1) + +## Maximum path length considered navigable, in game units. +@export var max_path_len := 16.0 + + +func tick(actor: Node, blackboard: Blackboard) -> int: + var beast := actor as GrunkBeast + + var target := Vector3( + randfn(beast.global_position.x, st_dev.x), + randfn(beast.global_position.y, st_dev.y), + randfn(beast.global_position.z, st_dev.z) + ) + + if not beast.path_shorter_than(target, max_path_len): + return RUNNING + + blackboard.set_value(blackboard_key, target) + return SUCCESS diff --git a/src/world/grunk_beast/behaviors/actions/get_random_target.gd.uid b/src/world/grunk_beast/behaviors/actions/get_random_target.gd.uid new file mode 100644 index 0000000..e931852 --- /dev/null +++ b/src/world/grunk_beast/behaviors/actions/get_random_target.gd.uid @@ -0,0 +1 @@ +uid://ubcelage67hs diff --git a/src/world/grunk_beast/behaviors/actions/sniff.gd b/src/world/grunk_beast/behaviors/actions/sniff.gd new file mode 100644 index 0000000..fa96318 --- /dev/null +++ b/src/world/grunk_beast/behaviors/actions/sniff.gd @@ -0,0 +1,9 @@ +@tool +class_name Sniff extends ActionLeaf +## The wily grunkbeest sniffs for its prey! + + +func tick(_actor: Node, _blackboard: Blackboard) -> int: + # TODO + print_debug("SNIFF SNIFF...") + return SUCCESS diff --git a/src/world/grunk_beast/behaviors/actions/sniff.gd.uid b/src/world/grunk_beast/behaviors/actions/sniff.gd.uid new file mode 100644 index 0000000..9257b27 --- /dev/null +++ b/src/world/grunk_beast/behaviors/actions/sniff.gd.uid @@ -0,0 +1 @@ +uid://bksvlal4gjhfr diff --git a/src/world/grunk_beast/grunk_beast.gd b/src/world/grunk_beast/grunk_beast.gd index b9c542f..46deee8 100644 --- a/src/world/grunk_beast/grunk_beast.gd +++ b/src/world/grunk_beast/grunk_beast.gd @@ -44,18 +44,28 @@ func get_speed() -> float: func path_shorter_than(target: Vector3, limit: float) -> bool: - var limit_sq := limit * limit - var length_sq := 0.0 + var length := 0.0 var last_pos := global_position nav_probe.target_position = target - for waypoint: Vector3 in nav_probe.get_current_navigation_path().slice( + # Fail early if the target is unreachable. + # NOTE: this call also forces a navigation path refresh! Do not remove! + if not nav_probe.is_target_reachable(): + return false + + var path := nav_probe.get_current_navigation_path().slice( nav_probe.get_current_navigation_path_index() - ): - # Using distance squared to save a sqrt instruction - length_sq += last_pos.distance_squared_to(waypoint) - if length_sq > limit_sq: + ) + if not path: + # Shouldn't be possible, but if it is it would cause problems if we didn't fail here + print_debug("Target is reachable but has no path (tell the developer!)") + return false + + # Integrate along path + for waypoint: Vector3 in path: + length += last_pos.distance_to(waypoint) + if length > limit: return false last_pos = waypoint diff --git a/src/world/grunk_beast/grunk_beast.tscn b/src/world/grunk_beast/grunk_beast.tscn index ae618a0..7a9f99a 100644 --- a/src/world/grunk_beast/grunk_beast.tscn +++ b/src/world/grunk_beast/grunk_beast.tscn @@ -1,4 +1,4 @@ -[gd_scene load_steps=37 format=3 uid="uid://ehf5sg3ahvbf"] +[gd_scene load_steps=41 format=3 uid="uid://ehf5sg3ahvbf"] [ext_resource type="Script" uid="uid://gwwmqwixqqr5" path="res://src/world/grunk_beast/grunk_beast.gd" id="2_qqnhb"] [ext_resource type="Shader" uid="uid://ckxc0ngd37rtk" path="res://src/shaders/gunk.gdshader" id="4_0gxpq"] @@ -25,6 +25,9 @@ [ext_resource type="Script" uid="uid://wfd5nbethyw6" path="res://src/world/grunk_beast/behaviors/actions/blackboard_copy.gd" id="15_umoec"] [ext_resource type="Script" uid="uid://ykfqlqp7e8og" path="res://src/world/grunk_beast/behaviors/actions/start_timer.gd" id="16_asd50"] [ext_resource type="Script" uid="uid://h0cp58nswpml" path="res://src/world/grunk_beast/behaviors/conditions/timer_running.gd" id="16_oons1"] +[ext_resource type="Script" uid="uid://ubcelage67hs" path="res://src/world/grunk_beast/behaviors/actions/get_random_target.gd" id="24_asd50"] +[ext_resource type="Script" uid="uid://dvnmhlldp23hg" path="res://addons/beehave/nodes/composites/selector_random.gd" id="24_xs4mp"] +[ext_resource type="Script" uid="uid://bksvlal4gjhfr" path="res://src/world/grunk_beast/behaviors/actions/sniff.gd" id="26_lak6w"] [sub_resource type="NoiseTexture3D" id="NoiseTexture3D_faau1"] width = 256 @@ -96,6 +99,9 @@ radius = 20.0 [sub_resource type="SphereShape3D" id="SphereShape3D_oons1"] radius = 3.0 +[sub_resource type="SphereShape3D" id="SphereShape3D_lak6w"] +radius = 10.0 + [node name="GrunkBeast" type="CharacterBody3D"] collision_layer = 36 script = ExtResource("2_qqnhb") @@ -115,12 +121,15 @@ shape = SubResource("CapsuleShape3D_faau1") [node name="NavAgent" type="NavigationAgent3D" parent="."] unique_name_in_owner = true path_desired_distance = 0.75 -avoidance_enabled = true debug_enabled = true [node name="NavProbe" type="NavigationAgent3D" parent="."] unique_name_in_owner = true path_desired_distance = 0.75 +debug_enabled = true +debug_use_custom = true +debug_path_custom_color = Color(0, 1, 0, 1) +debug_path_custom_point_size = 8.0 [node name="GameSoundListener" type="StaticBody3D" parent="."] collision_layer = 16 @@ -157,7 +166,7 @@ collision_layer = 0 collision_mask = 8 [node name="CollisionShape3D" type="CollisionShape3D" parent="SniffRange"] -shape = SubResource("SphereShape3D_wffas") +shape = SubResource("SphereShape3D_lak6w") [node name="StalkingTimer" type="Timer" parent="."] unique_name_in_owner = true @@ -257,9 +266,38 @@ metadata/_custom_type_script = "uid://u1ntpwjwjqhj" script = ExtResource("14_4y64f") metadata/_custom_type_script = "uid://om57w2acvgb7" -[node name="SelectorReactiveComposite" type="Node" parent="GrunkBeastBehavior/StateSelector/StalkingSequence"] -script = ExtResource("7_vvw1q") -metadata/_custom_type_script = "uid://cw22yurt5l74k" +[node name="RandomDelay" type="Node" parent="GrunkBeastBehavior/StateSelector/StalkingSequence"] +script = ExtResource("11_mbqcc") +mean_time = 4.0 +st_dev_time = 0.6 +wait_time = 3.87336 +metadata/_custom_type_script = "uid://beyk2xtbjrsg4" + +[node name="RandomStalkingBehavior" type="Node" parent="GrunkBeastBehavior/StateSelector/StalkingSequence/RandomDelay"] +script = ExtResource("24_xs4mp") +use_weights = true +Weights/PickRandomStalkTarget = 10 +Weights/SniffSequence = 1 +metadata/_custom_type_script = "uid://dvnmhlldp23hg" + +[node name="PickRandomStalkTarget" type="Node" parent="GrunkBeastBehavior/StateSelector/StalkingSequence/RandomDelay/RandomStalkingBehavior"] +script = ExtResource("24_asd50") +blackboard_key = "stalking_target" +metadata/_custom_type_script = "uid://ubcelage67hs" + +[node name="SniffSequence" type="Node" parent="GrunkBeastBehavior/StateSelector/StalkingSequence/RandomDelay/RandomStalkingBehavior"] +script = ExtResource("8_0gxpq") +metadata/_custom_type_script = "uid://cg016dbe7gs1x" + +[node name="Sniff" type="Node" parent="GrunkBeastBehavior/StateSelector/StalkingSequence/RandomDelay/RandomStalkingBehavior/SniffSequence"] +script = ExtResource("26_lak6w") +metadata/_custom_type_script = "uid://bksvlal4gjhfr" + +[node name="SetTargetFromArea" type="Node" parent="GrunkBeastBehavior/StateSelector/StalkingSequence/RandomDelay/RandomStalkingBehavior/SniffSequence" node_paths=PackedStringArray("area")] +script = ExtResource("13_x8l6r") +blackboard_key = "stalking_target" +area = NodePath("../../../../../../../SniffRange") +metadata/_custom_type_script = "uid://b34l3v4sr8rmq" [node name="AlwaysFailDecorator2" type="Node" parent="GrunkBeastBehavior/StateSelector"] script = ExtResource("15_oons1") @@ -278,7 +316,7 @@ metadata/_custom_type_script = "uid://cg016dbe7gs1x" script = ExtResource("11_mbqcc") mean_time = 5.0 st_dev_time = 1.0 -wait_time = 4.6164 +wait_time = 5.5859 metadata/_custom_type_script = "uid://beyk2xtbjrsg4" [node name="PickRandomLurkTarget" type="Node" parent="GrunkBeastBehavior/StateSelector/LurkSequence/RandomDelay"]