feat: 场景与核心玩法脚本(双机型/波次/Boss/道具/炸弹/地面单位/浮游炮激光)
Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent) Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
This commit is contained in:
+118
@@ -0,0 +1,118 @@
|
||||
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()
|
||||
Reference in New Issue
Block a user