Files
hello-godot/scripts/boss.gd
T

119 lines
3.1 KiB
GDScript

class_name Boss
extends EnemyBase
## 关底 Boss:四发重型轰炸机。
## 进场悬停后左右巡逻;弹幕双模式交替(瞄准连发 / 扇形散布),
## 半血进入狂暴阶段(弹速、频率、弹幕密度提升)。
## 死亡时爆炸链 + 震屏 + 清除全场敌弹。
const TEX := preload("res://assets/svg/boss_plane.svg")
const CENTER_X := 270.0
const ENTER_Y := 150.0
const ENTER_SPEED := 100.0
const PATROL_AMP := 145.0
const MUZZLE := Vector2(0.0, 55.0)
var difficulty := 1.0
var max_hp := 120
var _t := 0.0
var _entered := false
var _pattern_index := 0
var _pattern_timer := 1.4
var _burst_left := 0
var _burst_tick := 0.0
func setup(p_difficulty: float) -> void:
difficulty = p_difficulty
hp = int(60.0 + 60.0 * difficulty)
max_hp = hp
score_value = 5000
drops_powerup = true
hit_radius = 62.0
position = Vector2(CENTER_X, -120.0)
func _ready() -> void:
collision_layer = 4
collision_mask = 0
var sprite := Sprite2D.new()
sprite.texture = TEX
add_child(sprite)
var shape := CollisionShape2D.new()
var rect := RectangleShape2D.new()
rect.size = Vector2(190.0, 84.0)
shape.shape = rect
add_child(shape)
func _process(delta: float) -> void:
if dead:
return
if not _entered:
position.y += ENTER_SPEED * delta
if position.y >= ENTER_Y:
position.y = ENTER_Y
_entered = true
return
_t += delta * (1.25 if _in_phase2() else 1.0)
position.x = CENTER_X + sin(_t * 0.55) * PATROL_AMP
position.y = ENTER_Y + sin(_t * 1.6) * 16.0
if _burst_left > 0:
_burst_tick -= delta
if _burst_tick <= 0.0:
_burst_tick = 0.14
_burst_left -= 1
_fire_bullet(_aim_direction() * _bullet_speed(), MUZZLE)
else:
_pattern_timer -= delta
if _pattern_timer <= 0.0:
_fire_pattern()
func _in_phase2() -> bool:
return hp <= max_hp / 2.0
func _bullet_speed() -> float:
return (235.0 + 40.0 * difficulty) * (1.12 if _in_phase2() else 1.0)
func _fire_pattern() -> void:
var phase2 := _in_phase2()
if _pattern_index % 2 == 0:
# 瞄准连发
_burst_left = 5 if phase2 else 3
_burst_tick = 0.0
_pattern_timer = 1.5 if phase2 else 2.0
else:
# 扇形散布
var count := 8 if phase2 else 5
var spread := 50.0 if phase2 else 36.0
var speed := _bullet_speed()
for i in count:
var angle := deg_to_rad(-spread + spread * 2.0 * float(i) / float(count - 1))
_fire_bullet(Vector2.DOWN.rotated(angle) * speed, MUZZLE)
_pattern_timer = 1.7 if phase2 else 2.2
_pattern_index += 1
func _die() -> void:
_spawn_drop(Powerup.Kind.POWER)
var game := get_tree().get_first_node_in_group("game") as Game
if game != null and is_instance_valid(game):
game.add_score(score_value)
game.add_bomb()
game.clear_enemy_bullets()
game.shake(9.0)
for i in 8:
var offset := Vector2(randf_range(-92.0, 92.0), randf_range(-56.0, 56.0))
if game != null and is_instance_valid(game):
game.spawn_explosion(global_position + offset, randf_range(1.3, 2.0))
await get_tree().create_timer(0.12).timeout
if game != null and is_instance_valid(game):
game.spawn_explosion(global_position, 3.0)
await get_tree().create_timer(0.3).timeout
queue_free()