The_Dark_Side_of_Earth/enemies/boss/boss.gd

145 lines
4.4 KiB
GDScript3
Raw Normal View History

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