Add blood decals, gore, mobile HUD, web start gate + touch/perf tests

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
This commit is contained in:
2026-09-04 14:07:57 +03:00
parent 01d6ecf475
commit 981ebf1910
32 changed files with 1536 additions and 113 deletions
+51 -10
View File
@@ -95,6 +95,7 @@ func _ready() -> void:
_build_damage_vignette()
_build_game_over()
_build_touch_controls()
_build_mobile_hud()
_update_control_hints()
_find_spawner.call_deferred()
_watch_barrels.call_deferred()
@@ -107,6 +108,14 @@ func _build_touch_controls() -> void:
add_child((load("res://touch_controls.gd") as Script).new())
# Mobile-only health readouts (bull pips top-left + bear boss bar top-centre). On touch the
# bottom pip cluster is hidden (see _build_health), so this is the only health display there;
# on desktop it's never created.
func _build_mobile_hud() -> void:
if Controls.use_touch_ui():
add_child((load("res://mobile_hud.gd") as Script).new())
# Bottom-centre column that groups the gameplay HUD (abilities on top, bull health
# directly beneath) so both read as one cluster instead of scattered corners.
func _build_bottom_hud() -> void:
@@ -171,18 +180,29 @@ func _position_bull_bg() -> void:
vp_h - bh)
# Unobtrusive early-build tag in the bottom-right corner.
# Early-build tag. Bottom-right on desktop; on touch it moves to the bottom centre, clear of
# the ability cluster + joystick that own the bottom corners, and reads with an outline.
func _build_version_label() -> void:
var lbl := Label.new()
lbl.text = GameVersion.full()
lbl.add_theme_color_override("font_color", _MUTED)
lbl.add_theme_font_size_override("font_size", 12)
lbl.set_anchors_preset(Control.PRESET_BOTTOM_RIGHT)
lbl.grow_horizontal = Control.GROW_DIRECTION_BEGIN
lbl.add_theme_color_override("font_color", Color(0.90, 0.86, 0.72))
lbl.add_theme_color_override("font_outline_color", Color(0, 0, 0, 0.85))
lbl.add_theme_constant_override("outline_size", 4)
lbl.add_theme_font_size_override("font_size", 16)
lbl.grow_vertical = Control.GROW_DIRECTION_BEGIN
lbl.offset_right = -14.0
lbl.offset_bottom = -10.0
lbl.mouse_filter = Control.MOUSE_FILTER_IGNORE
if Controls.use_touch_ui():
lbl.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER
lbl.anchor_left = 0.5
lbl.anchor_right = 0.5
lbl.anchor_top = 1.0
lbl.anchor_bottom = 1.0
lbl.grow_horizontal = Control.GROW_DIRECTION_BOTH
else:
lbl.set_anchors_preset(Control.PRESET_BOTTOM_RIGHT)
lbl.grow_horizontal = Control.GROW_DIRECTION_BEGIN
lbl.offset_right = -20.0
add_child(lbl)
@@ -494,7 +514,7 @@ func _build_game_over() -> void:
# Buttons overlaid at the bottom of the clip.
var row := HBoxContainer.new()
row.alignment = BoxContainer.ALIGNMENT_CENTER
row.add_theme_constant_override("separation", 16)
row.add_theme_constant_override("separation", 32 if Controls.use_touch_ui() else 16)
row.anchor_left = 0.5
row.anchor_right = 0.5
row.anchor_top = 1.0
@@ -504,13 +524,13 @@ func _build_game_over() -> void:
row.offset_bottom = -48.0
_over_root.add_child(row)
# Win → Continue to the run map; loss → Go Again from the top. Shown per-result in
# _show_game_over; both share the Enter shortcut.
# Win → continue to the run map; loss → restart from the top. Shown per-result in
# _show_game_over; both share the Enter shortcut and label.
_over_continue = _make_button("Continue (Enter)")
_over_continue.pressed.connect(_continue_to_lobby)
row.add_child(_over_continue)
_over_again = _make_button("Go Again (Enter)")
_over_again = _make_button("Continue (Enter)")
_over_again.pressed.connect(_restart)
row.add_child(_over_again)
@@ -518,6 +538,11 @@ func _build_game_over() -> void:
menu.pressed.connect(_return_to_menu)
row.add_child(menu)
# Thumbs need a far bigger target than a mouse pointer, and the outcome clip has room
# for it — grow the whole row on touch only.
for btn: Button in [_over_continue, _over_again, menu]:
_grow_for_touch(btn)
# ── Bull health ───────────────────────────────────────────────────────────────
# A row of gold-socketed pips beneath the ability bar — one pip per HP, so raising or
# lowering max HP visibly grows or shrinks the row. Lit pips run red and shade toward
@@ -540,6 +565,9 @@ func _build_health() -> void:
"panel", UiTheme.plaque(UiTheme.LEATHER, UiTheme.BORDER, 4, 12.0, 6.0))
panel.size_flags_horizontal = Control.SIZE_SHRINK_CENTER
panel.mouse_filter = Control.MOUSE_FILTER_IGNORE
# On touch the bottom edge is crowded by the joystick + ability cluster and mobile_hud.gd
# paints health up top instead, so drop this bottom pip cluster there.
panel.visible = not Controls.use_touch_ui()
_bottom_col.add_child(panel)
var row := HBoxContainer.new()
@@ -799,6 +827,10 @@ func _build_controls_panel() -> void:
vbox.add_child(_toggle_btn)
const _TOUCH_BTN_FONT: int = 34
const _TOUCH_BTN_MIN: Vector2 = Vector2(320.0, 110.0)
func _make_button(label_text: String) -> Button:
var btn := Button.new()
btn.text = label_text
@@ -819,6 +851,15 @@ func _make_button(label_text: String) -> Button:
return btn
# Scales a game-over button up to a comfortable thumb target on touch devices; a no-op
# with mouse/gamepad, where the desktop sizing already reads fine.
func _grow_for_touch(btn: Button) -> void:
if not Controls.use_touch_ui():
return
btn.add_theme_font_size_override("font_size", _TOUCH_BTN_FONT)
btn.custom_minimum_size = _TOUCH_BTN_MIN
func _row(parent: VBoxContainer, key: String, desc: String, desc_color: Color) -> void:
var row := HBoxContainer.new()
row.add_theme_constant_override("separation", 10)