class_name MonsterSpawnpoint extends Node3D @onready var monster_tscn: PackedScene = preload("res://entities/monster/monster.tscn") # a spawnpoint that can spawn monsters # monsters are only spawned, if the player is not looking ## List that holds all spawnable monsters @export var spawnable_monsters: Array[MonsterSpawnpointProbability] ## Minimum Level the spawnable monsters have @export var min_level: int = 1 ## Maximum level the spawnable monsters can have @export var max_level: int = 1 ## Ignore spawning only if visible @export var ignore_visibility: bool = false ## The maximum amount of spawned monsters. ## No monsters will spawn, when this number is reached. @export var max_monsters: int = 5 # a monster is then deleted if the following conditions are met: # the monster has exceeded its maximum lifetime and it is not on screen # TODO: figure out how to check for the exceeded lifetime and delete ## The maximum lifetime a monster can be in the world @export var max_lifetime: float = 60.0 # seconds ## A monster can only spawn every x seconds @export var spawn_cooldown: float = 3.0 # seconds @onready var spawn_timer: Timer = Timer.new() @onready var time: float = 16.0 @onready var frequency: float = 1.0 @onready var amplitude: float = 0.5 @onready var spawn_point: Vector3 = Vector3(0, 0, 0) @onready var label: Label3D = $Label3D # a spawnpoint should keep track of spawned entities var active_monsters: Array[Monster] = [] # Called when the node enters the scene tree for the first time. func _ready() -> void: if spawnable_monsters.is_empty(): printerr("Spawnpoint (%s) does not have monsters to spawn." % self.name) spawn_timer.wait_time = spawn_cooldown spawn_timer.autostart = true spawn_timer.timeout.connect(spawn) add_child(spawn_timer) spawn_timer.start() spawn_point = position label.text = "%s / %s" % [len(active_monsters), max_monsters] func spawn() -> void: if len(active_monsters) >= max_monsters: #print("Spawnpoint reached maximum amount of monsters") return if ignore_visibility == false and $VisibleOnScreenNotifier3D.is_on_screen(): #print("Spawnpoint is visible on screen. Not spawning a monster.") return # TODO: Add a check to not spawn enemies X units near the player # instantiate a monster .tscn scene var monster_to_spawn: Node = monster_tscn.instantiate() # Create a new MonsterData for the monster to spawn var monster_data: MonsterData = MonsterData.new() # Get a random monster data as a template var random: int = randi() % spawnable_monsters.size() var template_data : MonsterData = spawnable_monsters[random].monster_data monster_data.set_values(template_data.unique_id, {}, randi_range(min_level, max_level)) ## Heal the wild monster fully monster_data.heal(9999) ## Set the data of the Node3D monster to spawn monster_to_spawn.data = monster_data # keep track of the current monster active_monsters.append(monster_to_spawn) label.text = "%s / %s" % [len(active_monsters), max_monsters] # calculate the position of the monster and add the offset of the spawnpoint itself var monster_position: Vector3 = Vector3(position.x, position.y + 1, position.z) monster_to_spawn.position = monster_position var debug_level_node: Node3D = get_parent_node_3d() debug_level_node.add_child(monster_to_spawn) #print("Spawning Mob \"%s\"" % monster_to_spawn.data.display_name) # Called every frame. 'delta' is the elapsed time since the previous frame. func _process(delta: float) -> void: time += delta var diamond_rotation: float = sin(time * frequency) * amplitude var diamond_height: float = sin(time * frequency) * amplitude $Diamond.rotation.y = diamond_rotation $Diamond.position.y = 2.0 + diamond_height