981ebf1910
Remove tools/fstest.html scratch page used to probe browser fullscreen/orientation APIs. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EznnY8rH2dXhtono1kwsXg
165 lines
5.8 KiB
GDScript
165 lines
5.8 KiB
GDScript
extends CanvasLayer
|
|
## Mobile-only health readouts (instanced by hud.gd only when Controls.use_touch_ui()).
|
|
## The desktop HUD's bull pips are tiny and, on a phone, the on-screen thumb cluster crowds
|
|
## the bottom edge — so on touch we hide that cluster (hud.gd) and paint health up top where
|
|
## it's clear of the joystick and abilities:
|
|
## • bull health — a big pip row, top-left
|
|
## • boss health — a wide Souls-style bar, top-centre, shown only while a bear is alive
|
|
##
|
|
## Everything is drawn in one IGNORE-filtered Control (never eats a touch), same immediate-mode
|
|
## approach as touch_controls.gd. Cached values are refreshed from each character's
|
|
## health_changed signal; the bear is discovered by group (&"bear") because it spawns after the
|
|
## HUD, and a level swap frees it so we re-acquire the next one.
|
|
|
|
const _HP_FULL: Color = Color(0.82, 0.16, 0.12)
|
|
const _HP_BONUS: Color = Color(1.00, 0.84, 0.18)
|
|
const _SOCKET: Color = Color(0.05, 0.02, 0.02, 0.92)
|
|
const _PANEL_BG: Color = Color(0.06, 0.04, 0.02, 0.82)
|
|
|
|
const _PIP_W: float = 42.0
|
|
const _PIP_H: float = 28.0
|
|
const _PIP_GAP: float = 7.0
|
|
|
|
var _surface: Control
|
|
|
|
var _player: Node = null
|
|
var _bull_cur: int = 0
|
|
var _bull_max: int = 0
|
|
var _bull_bonus: int = 0
|
|
|
|
var _boss: Node = null
|
|
var _boss_alive: bool = false
|
|
var _boss_cur: int = 0
|
|
var _boss_max: int = 1
|
|
|
|
|
|
func _ready() -> void:
|
|
layer = 9 # below touch UI (10) and the HUD/game-over (11) so the outcome screen covers it
|
|
process_mode = Node.PROCESS_MODE_ALWAYS
|
|
|
|
_surface = Control.new()
|
|
_surface.set_anchors_preset(Control.PRESET_FULL_RECT)
|
|
_surface.mouse_filter = Control.MOUSE_FILTER_IGNORE
|
|
_surface.draw.connect(_draw_surface)
|
|
add_child(_surface)
|
|
|
|
get_viewport().size_changed.connect(_surface.queue_redraw)
|
|
|
|
|
|
func _process(_delta: float) -> void:
|
|
if not Controls.use_touch_ui():
|
|
return
|
|
|
|
if _player == null:
|
|
var players := get_tree().get_nodes_in_group(&"player")
|
|
if not players.is_empty():
|
|
_player = players[0]
|
|
_player.health_changed.connect(_on_bull_health)
|
|
_on_bull_health(_player.call(&"get_hp"), _player.call(&"get_max_hp"))
|
|
|
|
# A level swap frees the old bear; drop the stale ref so the next level's bear is picked up.
|
|
if _boss != null and not is_instance_valid(_boss):
|
|
_boss = null
|
|
_boss_alive = false
|
|
_surface.queue_redraw()
|
|
if _boss == null:
|
|
var bears := get_tree().get_nodes_in_group(&"bear")
|
|
if not bears.is_empty():
|
|
_boss = bears[0]
|
|
_boss_alive = true
|
|
_boss.health_changed.connect(_on_boss_health)
|
|
if _boss.has_signal(&"killed"):
|
|
_boss.killed.connect(_on_boss_killed)
|
|
_boss_cur = int(_boss.call(&"get_hp"))
|
|
_boss_max = maxi(_boss_cur, int(DP.f("bear_max_hp")))
|
|
_surface.queue_redraw()
|
|
|
|
|
|
func _on_bull_health(current: int, max_hp: int) -> void:
|
|
_bull_cur = maxi(current, 0)
|
|
_bull_max = maxi(max_hp, 0)
|
|
_bull_bonus = 0
|
|
if _player != null and _player.has_method(&"get_bonus_hp"):
|
|
_bull_bonus = int(_player.call(&"get_bonus_hp"))
|
|
_surface.queue_redraw()
|
|
|
|
|
|
func _on_boss_health(current: int, max_hp: int) -> void:
|
|
_boss_cur = maxi(current, 0)
|
|
_boss_max = maxi(max_hp, maxi(_boss_cur, 1))
|
|
if _boss_cur <= 0:
|
|
_boss_alive = false
|
|
_surface.queue_redraw()
|
|
|
|
|
|
func _on_boss_killed() -> void:
|
|
_boss_alive = false
|
|
_surface.queue_redraw()
|
|
|
|
|
|
# ── Drawing ─────────────────────────────────────────────────────────────────────
|
|
|
|
func _draw_surface() -> void:
|
|
if not Controls.use_touch_ui():
|
|
return
|
|
var boss_showing := _boss != null and is_instance_valid(_boss) and _boss_alive and _boss_cur > 0
|
|
# The boss bar is centred at the very top; the bull pip row is top-left. On a narrow landscape
|
|
# phone a long pip row reaches the centre and collides with the boss bar, so when the bar is up
|
|
# the bull row drops to a second line just beneath it (guaranteed clear, whatever the HP count).
|
|
var bull_top := 22.0
|
|
if boss_showing:
|
|
bull_top = _draw_boss_bar() + 12.0
|
|
_draw_bull_health(bull_top)
|
|
|
|
|
|
# Big pip row in the top-left. Bonus pips (barrel reward) sit at the high end and light gold,
|
|
# matching the desktop HUD; base pips light red; spent pips read as dark sockets.
|
|
func _draw_bull_health(top: float) -> void:
|
|
var font := UiFonts.body()
|
|
var x := 24.0
|
|
var y := top
|
|
if font != null:
|
|
var label := "BULL"
|
|
var fs := 20
|
|
_surface.draw_string(
|
|
font, Vector2(x, y + _PIP_H * 0.85), label, HORIZONTAL_ALIGNMENT_LEFT, -1, fs,
|
|
UiTheme.GOLD)
|
|
x += font.get_string_size(label, HORIZONTAL_ALIGNMENT_LEFT, -1, fs).x + 12.0
|
|
|
|
var base_max := _bull_max - _bull_bonus
|
|
for i: int in _bull_max:
|
|
var r := Rect2(x + i * (_PIP_W + _PIP_GAP), y, _PIP_W, _PIP_H)
|
|
_surface.draw_rect(r, _SOCKET)
|
|
if i < _bull_cur:
|
|
var c := _HP_BONUS if i >= base_max else _HP_FULL
|
|
_surface.draw_rect(r.grow(-2.0), c)
|
|
_surface.draw_rect(r, Color(UiTheme.GOLD.r, UiTheme.GOLD.g, UiTheme.GOLD.b, 0.45), false, 2.0)
|
|
|
|
|
|
# Wide boss bar centred at the top: dark trough, red fill scaled to remaining HP, gold trim,
|
|
# name centred above it. Returns the bar's bottom Y so the bull pip row can sit clear beneath it.
|
|
func _draw_boss_bar() -> float:
|
|
var vp := get_viewport().get_visible_rect().size
|
|
var bw := clampf(vp.x * 0.55, 280.0, 620.0)
|
|
var bh := 30.0
|
|
var bx := (vp.x - bw) * 0.5
|
|
var by := 20.0
|
|
var bar := Rect2(bx, by, bw, bh)
|
|
|
|
_surface.draw_rect(bar, _PANEL_BG)
|
|
var frac := clampf(float(_boss_cur) / float(_boss_max), 0.0, 1.0)
|
|
var fill := bar.grow(-3.0)
|
|
fill.size.x *= frac
|
|
_surface.draw_rect(fill, _HP_FULL)
|
|
_surface.draw_rect(bar, UiTheme.BORDER, false, 2.0)
|
|
|
|
var font := UiFonts.body()
|
|
if font != null:
|
|
var boss_name := "BEAR"
|
|
var fs := 20
|
|
var tw := font.get_string_size(boss_name, HORIZONTAL_ALIGNMENT_LEFT, -1, fs)
|
|
_surface.draw_string(
|
|
font, Vector2(bx + (bw - tw.x) * 0.5, by - 6.0), boss_name,
|
|
HORIZONTAL_ALIGNMENT_LEFT, -1, fs, UiTheme.GOLD)
|
|
return by + bh
|