extends CharacterBody2D @onready var player = get_tree().get_root().get_node("main/Player") var moves = ["slam", "wave", "water_rise", "splash"] @export var big_blob : PackedScene @onready var water : Water = get_tree().get_root().get_node("main/Water") # How often water has been risen. var risen = 0 # Managing idle behavior between attacks. var attack_ready = true var idle_dir : Vector2 = Vector2.ZERO var idle_dir_remaining = 0 var idle_move = true var target_pos = Vector2.ZERO var damage = 1 var dead = false signal grounded signal slam_step_finished func choose_next_move() -> String: # Water rises at 75% and 50% of remaining HP. if $EnemyHurtbox.hp <= 3 * $EnemyHurtbox.max_hp / 4 and risen == 0: risen += 1 return "water_rise" if $EnemyHurtbox.hp <= $EnemyHurtbox.max_hp / 2 and risen == 1: risen += 1 return "water_rise" var pool = ["splash"] # Heavily decrease slam probability if boss height is low. if not (position.length() - water.radius < 450 and randf()<0.75): pool.append("slam") # Heavily decrease wave probability if player is very high up. if not (player.position.length() > water.radius + 900 and randf()<0.75): pool.append("wave") return ["slam", "wave", "splash"].pick_random() func _process(_delta: float) -> void: if dead: return if attack_ready: attack_ready = false call(choose_next_move()) func _physics_process(delta: float) -> void: if dead: return up_direction = $EarthAligner.up if(is_on_floor()): grounded.emit() if idle_move: move_idle(delta) if($Hitbox.overlaps_body(player)): player.hurt(damage, self.position - player.position) move_and_slide() func move_idle(delta : float): # Pick a random target roughly above the player's head every 0.5 seconds. idle_dir_remaining -= delta if(idle_dir_remaining <= 0): target_pos = player.position + player.get_node("EarthAligner").up * 400 target_pos += randf_range(0, max(200, (target_pos - global_position).length())*0.25) * Vector2.from_angle(randf_range(0,TAU)) idle_dir = (target_pos - global_position).normalized()* max(200, (target_pos - global_position).length()) * 0.4 idle_dir_remaining = 0.5 velocity = idle_dir func slam(): # Move up, Slam Down, Repeat. Afterwards, linger for a moment. # The slam destroys buildings. idle_move = false velocity = up_direction * 500 await get_tree().create_timer(0.6).timeout await slam_step() damage = 1 velocity = up_direction * 500 await get_tree().create_timer(0.3).timeout await slam_step() velocity = up_direction * 35 await get_tree().create_timer(3).timeout idle_move = true attack_ready = true func slam_step(): # End a downslam after ground is reached or 1.5 seconds have passed. # Then destroy buildings hit. damage = 2 velocity = up_direction * -1500 grounded.connect(func(): slam_step_finished.emit()) get_tree().create_timer(1.5).timeout.connect(func(): slam_step_finished.emit()) await slam_step_finished $SoundSlam.play() destroy_below() damage = 1 func destroy_below(): if dead: return for body in $DestructionChecker.get_overlapping_bodies(): if(body.has_method("destroy")): body.destroy() func wave(): # Raise a wave from the water at the boundary of the screen and move it towards the center. var angle = player.position.angle() var dir = randi_range(0, 1) * 2 - 1 var speed = 3000 / water.radius_base water.create_tsunami(angle - speed * dir * TAU/30, dir, speed) await get_tree().create_timer(0.5).timeout $Wave.play() await get_tree().create_timer(3.5).timeout attack_ready = true func water_rise(): # Increase the water level by 1 room height. water.rise_water() await get_tree().create_timer(5).timeout attack_ready = true func splash(): # Form four small blobs around the player which merge into one. var blob_instance = big_blob.instantiate() get_tree().get_root().get_node("main").add_child(blob_instance) blob_instance.position = player.position blob_instance.rotation = randf_range(0, TAU) await get_tree().create_timer(5).timeout attack_ready = true func die(): # Clear everything else, then wait for Audio Players to make sure sounds are not cut off. dead = true for child in get_children(): if not child is AudioStreamPlayer2D: child.queue_free() $DeathSound.play() await $DeathSound.finished await get_tree().create_timer(1).timeout get_tree().change_scene_to_file("res://ui/victory_screen/victory_screen.tscn") queue_free() func _on_enemy_hurtbox_damage_taken(_damage, _dir, _id) -> void: if dead: return $AudioStreamPlayer2D.play()