3c6f31b503
Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent) Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
119 lines
3.1 KiB
GDScript
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()
|