edc40f9008
- 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>
233 lines
11 KiB
GDScript
233 lines
11 KiB
GDScript
extends Node2D
|
|
|
|
# Minimal cockpit HUD — maximum screen space for the game.
|
|
# During play: only 4 corner L-brackets + tiny top-right status line.
|
|
# On gameover: full terminal overlay.
|
|
|
|
var W: float = 960.0
|
|
var H: float = 600.0
|
|
|
|
# Cockpit palette
|
|
const COL_PRIMARY := Color(0.0, 1.0, 0.533, 0.55) # phosphor green, semi-dim
|
|
const COL_ACCENT := Color(0.27, 1.0, 0.8, 0.80)
|
|
const COL_DIM := Color(0.0, 0.27, 0.13, 0.45)
|
|
const COL_WARNING := Color(1.0, 0.67, 0.0, 0.90)
|
|
const COL_DANGER := Color(1.0, 0.2, 0.0, 0.90)
|
|
const COL_WHITE := Color(1.0, 1.0, 1.0, 0.90)
|
|
const BRACKET_LEN := 14.0
|
|
const BRACKET_W := 1.5
|
|
const MARGIN := 10.0
|
|
|
|
var hud: Node = null
|
|
|
|
func _process(_delta: float) -> void:
|
|
queue_redraw()
|
|
|
|
func _draw() -> void:
|
|
if hud == null:
|
|
return
|
|
var vs: Vector2 = get_viewport_rect().size
|
|
W = vs.x; H = vs.y
|
|
var font := ThemeDB.fallback_font
|
|
var gw: Node = hud.game_world
|
|
|
|
# ── Countdown ────────────────────────────────────────────────────────────
|
|
if hud.show_countdown_flag and hud.countdown_value > 0:
|
|
_draw_corner_brackets()
|
|
var ct := str(hud.countdown_value)
|
|
_draw_text_centered(font, ct, W * 0.5, H * 0.5 - 36.0, 64, COL_ACCENT)
|
|
_draw_text_centered(font, Tr.t("hud_launching"), W * 0.5, H * 0.5 + 28.0, 10, COL_DIM)
|
|
return
|
|
|
|
if not hud.is_gameover and gw == null:
|
|
return
|
|
|
|
# ── Normal gameplay HUD ──────────────────────────────────────────────────
|
|
if not hud.is_gameover and gw != null:
|
|
_draw_corner_brackets()
|
|
|
|
# ── Wave timer bar ────────────────────────────────────────────────
|
|
var wave_dur: float = float(gw.get("wave_duration") if gw.get("wave_duration") != null else 60.0)
|
|
var wave_t: float = float(gw.get("wave_timer") if gw.get("wave_timer") != null else 0.0)
|
|
var wave_frac: float = clamp(wave_t / wave_dur, 0.0, 1.0)
|
|
var wave_remaining: float = max(0.0, wave_dur - wave_t)
|
|
var last_10: bool = wave_remaining <= 10.0 and gw.get("is_playing")
|
|
# Thin bar at top — shrinks from right as time passes
|
|
var bar_col: Color
|
|
if last_10:
|
|
bar_col = Color(COL_WARNING.r, COL_WARNING.g, COL_WARNING.b,
|
|
0.55 + 0.45 * sin(hud.blink_phase * 8.0))
|
|
else:
|
|
bar_col = Color(COL_PRIMARY.r, COL_PRIMARY.g, COL_PRIMARY.b, 0.35)
|
|
draw_rect(Rect2(0.0, 0.0, W * (1.0 - wave_frac), 2.0), bar_col)
|
|
# Wave label top-left: "W1 0:42"
|
|
var w_mins: int = int(wave_remaining / 60.0)
|
|
var w_secs: int = int(wave_remaining) % 60
|
|
var wave_label := "W%d %d:%02d" % [hud.wave_number, w_mins, w_secs]
|
|
var wlabel_col: Color = COL_WARNING if last_10 else COL_DIM
|
|
_draw_text(font, wave_label, MARGIN, 10.0, 9, wlabel_col)
|
|
|
|
# ── Wave cleared overlay ──────────────────────────────────────────
|
|
if hud.wave_cleared:
|
|
var wc_a: float = 0.7 + 0.3 * sin(hud.blink_phase * 5.0)
|
|
_draw_text_centered(font, Tr.t("hud_wave_clear") % (hud.wave_number - 1),
|
|
W * 0.5, H * 0.5 - 22.0, 16, Color(COL_ACCENT.r, COL_ACCENT.g, COL_ACCENT.b, wc_a))
|
|
_draw_text_centered(font, Tr.t("hud_to_shop"), W * 0.5, H * 0.5 + 8.0, 11,
|
|
Color(COL_PRIMARY.r, COL_PRIMARY.g, COL_PRIMARY.b, wc_a * 0.7))
|
|
|
|
# Top-right micro status — lives dots + credits + time + kills
|
|
var players: Array = gw.players
|
|
var lives_str := ""
|
|
for li in 3:
|
|
lives_str += ("● " if li < hud.lives else "○ ")
|
|
var cr_str := "CR:%d" % hud.credits
|
|
if not gw.is_multiplayer:
|
|
if players.size() > 0:
|
|
var p = players[0]
|
|
var mins: int = int(p.survival_time / 60.0)
|
|
var secs: int = int(p.survival_time) % 60
|
|
var time_str := "%02d:%02d" % [mins, secs]
|
|
var shields_str := ""
|
|
for _si in p.current_shields:
|
|
shields_str += "▣"
|
|
var status_str := "%s %s %s %s x%d" % [lives_str, cr_str, shields_str, time_str, p.kills]
|
|
_draw_text(font, status_str, W - 200.0, 10.0, 9, COL_PRIMARY)
|
|
else:
|
|
if players.size() >= 1:
|
|
var p1 = players[0]
|
|
var t1 := "%02d:%02d" % [int(p1.survival_time / 60.0), int(p1.survival_time) % 60]
|
|
_draw_text(font, "P1 %s x%d %s" % [t1, p1.kills, cr_str], 10.0, 10.0, 9, COL_PRIMARY)
|
|
if players.size() >= 2:
|
|
var p2 = players[1]
|
|
var t2 := "%02d:%02d" % [int(p2.survival_time / 60.0), int(p2.survival_time) % 60]
|
|
_draw_text(font, "P2 %s x%d" % [t2, p2.kills], W - 110.0, 10.0, 9,
|
|
Color(0.27, 1.0, 0.67, 0.55))
|
|
|
|
# ── Boost-Cooldown-Leisten (nur Schiffe mit has_boost) ────────────
|
|
for player in players:
|
|
var sp: Spaceship = player
|
|
if sp == null or sp.dead: continue
|
|
if sp.stats == null or not sp.stats.has_boost: continue
|
|
var ratio: float = sp.boost_ratio()
|
|
var bar_w := 40.0; var bar_h := 4.0
|
|
var bx2: float = sp.x - bar_w * 0.5
|
|
var by2: float = sp.y + 18.0
|
|
# Background
|
|
draw_rect(Rect2(bx2, by2, bar_w, bar_h), Color(0.0, 0.04, 0.02, 0.7))
|
|
# Fill — remaining cooldown shown as shrinking bar (ratio 1 = full cd, 0 = ready)
|
|
var fill_w: float = bar_w * (1.0 - ratio)
|
|
var is_ready: bool = ratio <= 0.0
|
|
var fill_col: Color
|
|
if is_ready:
|
|
fill_col = Color(1.0, 0.9, 0.3, 0.7 + 0.3 * sin(hud.blink_phase * 6.0))
|
|
else:
|
|
fill_col = Color(COL_WARNING.r, COL_WARNING.g, COL_WARNING.b, 0.75)
|
|
draw_rect(Rect2(bx2, by2, fill_w, bar_h), fill_col)
|
|
# Border
|
|
draw_rect(Rect2(bx2, by2, bar_w, bar_h),
|
|
Color(COL_DIM.r, COL_DIM.g, COL_DIM.b, 0.7), false, 1.0)
|
|
|
|
# Big wipe warning — bold centred flash, no decoration
|
|
if hud.wipe_warning:
|
|
if sin(hud.blink_phase * 8.0) > 0.0:
|
|
_draw_text_centered(font, Tr.hint("hud_wipe_key"), W * 0.5, H * 0.5 - 8.0, 13, COL_WARNING)
|
|
return
|
|
|
|
# ── Gameover / Returning ─────────────────────────────────────────────────
|
|
if hud.is_gameover or hud.is_returning:
|
|
# Dim overlay
|
|
draw_rect(get_viewport_rect(), Color(0.0, 0.0, 0.04, 0.62))
|
|
|
|
var sd: Dictionary = hud.score_data
|
|
var p1_time: float = sd.get("p1_time", 0.0)
|
|
var p1_kills: int = sd.get("p1_kills", 0)
|
|
var total: int = int(p1_time) + p1_kills * 50 + int(sd.get("p1_wipe", 0))
|
|
if sd.get("multiplayer", false):
|
|
total += int(sd.get("p2_time", 0.0)) + int(sd.get("p2_kills", 0)) * 50 + int(sd.get("p2_wipe", 0))
|
|
|
|
# Central terminal box — taller when asking for name
|
|
var bx := W * 0.5 - 160.0
|
|
var by := H * 0.5 - 100.0
|
|
var bw := 320.0
|
|
var bh2 := 215.0 if hud.awaiting_name else 200.0
|
|
_draw_terminal_box(bx, by, bw, bh2, COL_PRIMARY)
|
|
|
|
_draw_text_centered(font, Tr.t("hud_mission_over"), W * 0.5, by + 22.0, 12, COL_ACCENT)
|
|
_draw_hline(bx + 16.0, bx + bw - 16.0, by + 42.0, COL_DIM)
|
|
|
|
_draw_text_centered(font, Tr.t("hud_score") % total, W * 0.5, by + 60.0, 18, COL_WHITE)
|
|
|
|
var mins1: int = int(p1_time / 60.0)
|
|
var secs1: int = int(p1_time) % 60
|
|
_draw_text_centered(font, "P1 %02d:%02d KILLS %d" % [mins1, secs1, p1_kills],
|
|
W * 0.5, by + 92.0, 10, COL_PRIMARY)
|
|
if sd.get("multiplayer", false):
|
|
var p2t: float = sd.get("p2_time", 0.0)
|
|
var mins2: int = int(p2t / 60.0); var secs2: int = int(p2t) % 60
|
|
_draw_text_centered(font, "P2 %02d:%02d KILLS %d" % [mins2, secs2, sd.get("p2_kills", 0)],
|
|
W * 0.5, by + 112.0, 10, Color(0.27, 1.0, 0.67, 0.8))
|
|
|
|
_draw_hline(bx + 16.0, bx + bw - 16.0, by + 135.0, COL_DIM)
|
|
|
|
if hud.is_returning:
|
|
_draw_text_centered(font, Tr.t("hud_transfer"), W * 0.5, by + 155.0, 10, COL_WARNING)
|
|
elif hud.awaiting_name:
|
|
_draw_text_centered(font, Tr.t("lb_enter_name"), W * 0.5, by + 148.0, 10, COL_DIM)
|
|
var cursor_vis: bool = fmod(hud.blink_phase, 1.0) < 0.6
|
|
var field_str: String = hud.name_input + ("_" if cursor_vis else " ")
|
|
var display_str: String = field_str if field_str.strip_edges().length() > 0 else "_"
|
|
_draw_text_centered(font, display_str, W * 0.5, by + 163.0, 13, COL_ACCENT)
|
|
_draw_text_centered(font, Tr.hint("lb_name_hint"), W * 0.5, by + 186.0, 8, COL_DIM)
|
|
elif hud.name_saved:
|
|
_draw_text_centered(font, Tr.t("lb_saved"), W * 0.5, by + 155.0, 11, COL_ACCENT)
|
|
elif hud.gameover_blink:
|
|
_draw_text_centered(font, Tr.hint("hud_press_key"), W * 0.5, by + 155.0, 11, COL_ACCENT)
|
|
|
|
# ── Drawing helpers ───────────────────────────────────────────────────────────
|
|
|
|
func _draw_corner_brackets() -> void:
|
|
# Four L-shaped corner brackets as the only permanent chrome
|
|
var m := MARGIN
|
|
var bl := BRACKET_LEN
|
|
var bw := BRACKET_W
|
|
# Top-left
|
|
draw_line(Vector2(m, m), Vector2(m + bl, m), COL_DIM, bw)
|
|
draw_line(Vector2(m, m), Vector2(m, m + bl), COL_DIM, bw)
|
|
# Top-right
|
|
draw_line(Vector2(W-m, m), Vector2(W-m-bl, m), COL_DIM, bw)
|
|
draw_line(Vector2(W-m, m), Vector2(W-m, m+bl), COL_DIM, bw)
|
|
# Bottom-left
|
|
draw_line(Vector2(m, H-m), Vector2(m+bl, H-m), COL_DIM, bw)
|
|
draw_line(Vector2(m, H-m), Vector2(m, H-m-bl), COL_DIM, bw)
|
|
# Bottom-right
|
|
draw_line(Vector2(W-m, H-m), Vector2(W-m-bl, H-m), COL_DIM, bw)
|
|
draw_line(Vector2(W-m, H-m), Vector2(W-m, H-m-bl), COL_DIM, bw)
|
|
|
|
func _draw_terminal_box(bx: float, by: float, bw: float, bh2: float, col: Color) -> void:
|
|
# Filled dark panel
|
|
draw_rect(Rect2(bx, by, bw, bh2), Color(0.0, 0.04, 0.02, 0.88))
|
|
# Border lines
|
|
var bc := Color(col.r, col.g, col.b, col.a * 0.6)
|
|
draw_line(Vector2(bx, by), Vector2(bx+bw, by), bc, 1.0)
|
|
draw_line(Vector2(bx, by+bh2), Vector2(bx+bw, by+bh2), bc, 1.0)
|
|
draw_line(Vector2(bx, by), Vector2(bx, by+bh2), bc, 1.0)
|
|
draw_line(Vector2(bx+bw, by), Vector2(bx+bw, by+bh2), bc, 1.0)
|
|
# Corner L-brackets (bright)
|
|
var cl := 10.0
|
|
for cx in [bx, bx+bw]:
|
|
for cy in [by, by+bh2]:
|
|
var sx := 1.0 if cx == bx else -1.0
|
|
var sy := 1.0 if cy == by else -1.0
|
|
draw_line(Vector2(cx, cy), Vector2(cx + sx*cl, cy), col, 1.5)
|
|
draw_line(Vector2(cx, cy), Vector2(cx, cy + sy*cl), col, 1.5)
|
|
|
|
func _draw_hline(x1: float, x2: float, y: float, col: Color) -> void:
|
|
draw_line(Vector2(x1, y), Vector2(x2, y), col, 1.0)
|
|
|
|
func _draw_text(font: Font, text: String, x: float, y: float, size: int, col: Color) -> void:
|
|
draw_string(font, Vector2(x, y + size), text, HORIZONTAL_ALIGNMENT_LEFT, -1, size, col)
|
|
|
|
func _draw_text_centered(font: Font, text: String, x: float, y: float, size: int, col: Color) -> void:
|
|
var tw := font.get_string_size(text, HORIZONTAL_ALIGNMENT_LEFT, -1, size).x
|
|
draw_string(font, Vector2(x - tw * 0.5, y + size), text, HORIZONTAL_ALIGNMENT_LEFT, -1, size, col)
|