Initial commit — Godot space roguelite source
- Touch controls: direct InputEventScreenTouch in shop_ui (bypass relay) - ItemDB: static preload list instead of DirAccess scan (export fix) - All 18 items with EN localisation (name_en, desc_en, category_en) - Ship playstyles: NOVA-1 shield, INFERNO ram, AURORA agile/tank - Quasar: SMBH visual, jet boost, merge, push, BH-eating - Atlas & UI text updated EN+DE Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,193 @@
|
||||
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)
|
||||
Reference in New Issue
Block a user