2025-04-10 18:29:00 -06:00
|
|
|
class_name SpookManager extends Resource
|
|
|
|
## A strategy for handling horror elements through the level.
|
|
|
|
|
2025-04-17 18:47:15 -06:00
|
|
|
const SPAWN_GROUP := "BeastSpawnPoint"
|
2025-04-20 02:15:46 -06:00
|
|
|
const BEAST_GROUP := "GrunkBeast"
|
2025-04-17 18:47:15 -06:00
|
|
|
|
|
|
|
@export var grunkbeast_scene: PackedScene
|
|
|
|
|
2025-04-10 18:29:00 -06:00
|
|
|
var debug_set_alert_level: int:
|
2025-04-21 16:48:08 -06:00
|
|
|
set = on_alert_raised
|
2025-04-10 18:29:00 -06:00
|
|
|
|
|
|
|
|
2025-04-17 18:47:15 -06:00
|
|
|
func _spawn_beast_at_point(spawn_point: Node3D) -> void:
|
|
|
|
print_debug("Spawning active beast at ", spawn_point.global_position)
|
|
|
|
var beast: GrunkBeast = grunkbeast_scene.instantiate()
|
|
|
|
spawn_point.add_child(beast)
|
|
|
|
|
|
|
|
|
|
|
|
## Spawn a beast somewhere the player isn't looking.
|
|
|
|
func spawn_beast() -> void:
|
|
|
|
var spawn_point := SceneTools.pick_unseen_from_group(SPAWN_GROUP)
|
|
|
|
if not spawn_point:
|
|
|
|
print_debug("Couldn't find a hidden spawn point... Picking one at random.")
|
2025-04-21 16:48:08 -06:00
|
|
|
var nodes := World.instance.get_tree().get_nodes_in_group(SPAWN_GROUP)
|
2025-04-17 18:47:15 -06:00
|
|
|
if not nodes:
|
|
|
|
print_debug("Oh that's why. There aren't any spawn points. Complain to a developer.")
|
|
|
|
return
|
2025-04-18 09:53:21 -06:00
|
|
|
@warning_ignore("unsafe_cast")
|
2025-04-17 18:47:15 -06:00
|
|
|
spawn_point = nodes.pick_random() as Node3D
|
|
|
|
|
|
|
|
_spawn_beast_at_point(spawn_point)
|
|
|
|
|
|
|
|
|
|
|
|
## Spawn beasts at _every_ spawn point the player can't see.
|
|
|
|
func spawn_many_beasts() -> void:
|
2025-04-21 16:48:08 -06:00
|
|
|
var nodes := World.instance.get_tree().get_nodes_in_group(SPAWN_GROUP)
|
2025-04-17 18:47:15 -06:00
|
|
|
for node: Node in nodes:
|
|
|
|
var target := node as Node3D
|
|
|
|
if is_instance_valid(target) and not SceneTools.player_can_see(target.global_position):
|
|
|
|
_spawn_beast_at_point(target)
|
|
|
|
|
|
|
|
|
2025-04-21 16:48:08 -06:00
|
|
|
func on_alert_raised(new_level: int) -> void:
|
2025-04-10 18:29:00 -06:00
|
|
|
match new_level:
|
|
|
|
0:
|
|
|
|
# LEVEL 0: UNAWARE
|
|
|
|
pass
|
|
|
|
1:
|
|
|
|
# LEVEL 1: PASSIVE
|
|
|
|
# Beast avoids the player.
|
|
|
|
# Set up meet-spook.
|
|
|
|
# Get closest MeetSpook point to player.
|
|
|
|
if Player.instance:
|
2025-04-22 12:08:09 -06:00
|
|
|
var closest := (
|
|
|
|
SceneTools.closest_in_group(Player.instance, MeetSpook.GROUP) as MeetSpook
|
|
|
|
)
|
|
|
|
if closest:
|
|
|
|
closest.prepare()
|
2025-04-10 18:29:00 -06:00
|
|
|
2:
|
|
|
|
# LEVEL 2: AGGRESSIVE
|
|
|
|
# Beast pursues player on sight.
|
2025-04-17 18:47:15 -06:00
|
|
|
spawn_beast()
|
2025-04-10 18:29:00 -06:00
|
|
|
3:
|
|
|
|
# LEVEL 3: PREDATORY
|
|
|
|
# Beast pursues player relentlessly.
|
2025-04-17 18:47:15 -06:00
|
|
|
# The logic for this behavior change is handled directly in the GrunkBeast AI.
|
|
|
|
pass
|
2025-04-10 18:29:00 -06:00
|
|
|
4:
|
|
|
|
# LEVEL 4: SWARMING
|
|
|
|
# Many beasts spawn, overwhelming the player.
|
2025-04-17 18:47:15 -06:00
|
|
|
spawn_many_beasts()
|
2025-04-10 18:29:00 -06:00
|
|
|
5:
|
|
|
|
# LEVEL 5: FUN
|
|
|
|
# Just kill that fool!
|
|
|
|
pass # TODO
|
|
|
|
|
|
|
|
|
2025-04-21 16:48:08 -06:00
|
|
|
func on_alert_cleared() -> void:
|
2025-04-22 15:26:55 -06:00
|
|
|
# Destroy all grunk beasts
|
|
|
|
for b: Node in World.instance.get_tree().get_nodes_in_group(BEAST_GROUP):
|
|
|
|
b.queue_free()
|
|
|
|
|
|
|
|
# And spawn one back in, if needed
|
|
|
|
if World.instance.manager.alert_level >= 2:
|
|
|
|
spawn_beast()
|
|
|
|
|
|
|
|
|
|
|
|
## Set the state of spooky effects after a level is initialized & any save state is loaded.
|
|
|
|
func after_level_init() -> void:
|
|
|
|
on_alert_cleared()
|