extends RefCounted class_name BossShip # ═══════════════════════════════════════════════════════════════════════════ # BossShip — Mini-Boss "WRAITH" (Welle 5) & Boss "LEVIATHAN" (Welle 8) # Orbitet elliptisch um die Bildschirmmitte, feuert Spread-Geschosse. # Wird von game_world.gd verwaltet (update/draw/take_hit). # ═══════════════════════════════════════════════════════════════════════════ var x: float = 0.0 var y: float = 0.0 var heading: float = 0.0 var orbit_angle: float = 0.0 var hp: int = 20 var max_hp: int = 20 var dead: bool = false var fire_timer: int = 0 var is_miniboss: bool = true # false = Boss var phase: int = 1 # 1 oder 2 (nur Boss) var _phase2_triggered: bool = false var _just_entered_phase2: bool = false var pixel_size: int = 5 # Miniboss=5, Boss=7 const HULL_PIXELS: Array = [ [6, 0, "nose"], [4, -2, "mid"], [4, 2, "mid"], [2, 0, "dim"], [0, -4, "accent"], [0, 4, "accent"], [0, 0, "bright"], [-2, -2, "dim"], [-2, 2, "dim"], [-4, -4, "shadow"], [-4, 4, "shadow"], [-4, 0, "edge"], ] # Mini-Boss WRAITH: Magenta/Lila const PAL_MINI: Dictionary = { "nose": Color(1.0, 0.15, 0.9, 1.0), "bright": Color(1.0, 0.5, 1.0, 1.0), "mid": Color(0.75, 0.05, 0.7, 1.0), "dim": Color(0.45, 0.02, 0.38, 1.0), "accent": Color(1.0, 0.0, 0.85, 1.0), "edge": Color(0.28, 0.0, 0.28, 1.0), "shadow": Color(0.12, 0.0, 0.12, 1.0), } # Boss LEVIATHAN: Feuer-Orange const PAL_BOSS: Dictionary = { "nose": Color(1.0, 0.65, 0.0, 1.0), "bright": Color(1.0, 0.9, 0.25, 1.0), "mid": Color(0.8, 0.38, 0.0, 1.0), "dim": Color(0.5, 0.18, 0.0, 1.0), "accent": Color(1.0, 0.72, 0.0, 1.0), "edge": Color(0.28, 0.08, 0.0, 1.0), "shadow": Color(0.12, 0.03, 0.0, 1.0), } # ── Initialisierung ──────────────────────────────────────────────────────── func init_miniboss(cx: float, cy: float) -> void: x = cx y = cy - 120.0 orbit_angle = -PI * 0.5 # startet oben hp = 20; max_hp = 20 is_miniboss = true pixel_size = 5 fire_timer = 25 # kurze Verzögerung vor dem ersten Schuss func init_boss(cx: float, cy: float) -> void: x = cx y = cy - 140.0 orbit_angle = -PI * 0.5 hp = 50; max_hp = 50 is_miniboss = false phase = 1 pixel_size = 7 fire_timer = 25 # ── Update ───────────────────────────────────────────────────────────────── # Gibt Array[Bullet] zurück. Wird von game_world._tick() pro Frame aufgerufen. func update(delta: float, cx: float, cy: float) -> Array: if dead: return [] # Orbitalbewegung (elliptisch, um Spannung zu erzeugen) var orbit_speed: float if is_miniboss: orbit_speed = 0.55 elif phase == 1: orbit_speed = 0.44 else: orbit_speed = 0.72 # Phase 2 schneller → gefährlicher orbit_angle += orbit_speed * delta var orbit_r: float = 185.0 if is_miniboss else 215.0 x = cx + cos(orbit_angle) * orbit_r y = cy + sin(orbit_angle) * orbit_r * 0.58 # leicht elliptisch # Heading zeigt immer zur Bildschirmmitte (schießt nach innen) heading = atan2(cy - y, cx - x) # Feuer-Intervall fire_timer += 1 var fire_interval: int if is_miniboss: fire_interval = 72 elif phase == 1: fire_interval = 60 else: fire_interval = 42 var result: Array = [] if fire_timer >= fire_interval: fire_timer = 0 result = _fire() # Phase-2-Übergang (Boss, < 50% HP) if not is_miniboss and not _phase2_triggered and hp <= max_hp / 2: phase = 2 _phase2_triggered = true _just_entered_phase2 = true return result func _fire() -> Array: var ways: int if is_miniboss: ways = 3 elif phase == 1: ways = 5 else: ways = 8 var bullet_speed: float = 4.2 if is_miniboss else 5.0 var spread: float = PI / float(ways + 2) var bullets: Array = [] for i in ways: var angle: float = heading + (float(i) - float(ways - 1) * 0.5) * spread var b := Bullet.new() b.init(x, y, angle, "boss") # Geschwindigkeit überschreiben (langsamer als Spielerkugeln für Ausweichen) b.vx = cos(angle) * bullet_speed b.vy = sin(angle) * bullet_speed if is_miniboss: b.color = Color(1.0, 0.15, 0.9, 1.0) # Magenta elif phase == 1: b.color = Color(1.0, 0.5, 0.0, 1.0) # Orange else: b.color = Color(1.0, 0.1, 0.0, 1.0) # Dunkelrot bullets.append(b) return bullets func take_hit() -> void: hp -= 1 if hp <= 0: hp = 0 dead = true # ── Zeichnen ─────────────────────────────────────────────────────────────── func draw(canvas: CanvasItem) -> void: if dead: return var pal: Dictionary = PAL_MINI if is_miniboss else PAL_BOSS var ps: float = float(pixel_size) var hs: float = ps * 0.5 var ch: float = cos(heading) var sh: float = sin(heading) # Rumpf — gleiche Offset-Tabelle wie EnemyShip, skaliert mit pixel_size for pix: Array in HULL_PIXELS: var lx: float = float(pix[0]) * ps var ly: float = float(pix[1]) * ps var wx: float = x + lx * ch - ly * sh var wy: float = y + lx * sh + ly * ch canvas.draw_rect(Rect2(wx - hs, wy - hs, ps, ps), pal[pix[2]]) # Thruster-Glühen (hinter dem Schiff) var glow_x: float = x + cos(heading + PI) * (ps * 4.2) var glow_y: float = y + sin(heading + PI) * (ps * 4.2) var glow_r: float = ps + 2.0 var pulse: float = 0.48 + 0.38 * sin(float(Time.get_ticks_msec()) * 0.012) var glow_col: Color if is_miniboss: glow_col = Color(1.0, 0.0, 0.85, pulse) elif phase == 1: glow_col = Color(1.0, 0.5, 0.0, pulse) else: glow_col = Color(1.0, 0.15, 0.0, pulse + 0.15) # Phase 2 heller canvas.draw_circle(Vector2(glow_x, glow_y), glow_r, glow_col)