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:
@@ -0,0 +1,105 @@
|
||||
extends CanvasLayer
|
||||
## "TAP TO PLAY" gate for the web/touch build (autoload `WebStartGate`). Browsers only allow
|
||||
## requestFullscreen / screen.orientation.lock from inside a user gesture, so instead of
|
||||
## piggybacking the first *gameplay* tap (which used to race the resize/orientation flip and
|
||||
## strand the joystick — a dropped touchend left the stick stuck), we capture a dedicated tap
|
||||
## on a full-screen panel BEFORE play. The tap drives Controls.request_fullscreen_landscape()
|
||||
## and the gate dismisses, so all the fullscreen/orientation churn happens with no in-flight
|
||||
## touch to lose.
|
||||
##
|
||||
## Re-armable, so the player is never stuck windowed: whenever we're NOT fullscreen (boot, or
|
||||
## after they left via Esc / Android back / edge swipe) the gate re-appears and its tap goes
|
||||
## back in — fullscreen otherwise persists across menu ↔ game, so tapping Play never lands you
|
||||
## windowed. Where fullscreen is impossible (iPhone Safari has no requestFullscreen) it shows
|
||||
## once and then stays out of the way instead of nagging. Only active on web touch; no-op else.
|
||||
|
||||
const _POLL: float = 0.4 # seconds between browser fullscreen-state polls
|
||||
|
||||
var _panel: ColorRect
|
||||
var _active: bool = false # web + touch: the only case this gate does anything
|
||||
var _fs_supported: bool = true
|
||||
var _dismissed_unsupported: bool = false # iPhone: tapped once, don't show again
|
||||
var _poll_accum: float = 0.0
|
||||
|
||||
|
||||
func _ready() -> void:
|
||||
layer = 127 # above HUD/touch UI, just below OrientationGuard (128) so "rotate" wins in portrait
|
||||
process_mode = Node.PROCESS_MODE_ALWAYS
|
||||
|
||||
_active = OS.has_feature("web") and Controls.use_touch_ui()
|
||||
if not _active:
|
||||
return
|
||||
_fs_supported = Controls.fullscreen_supported()
|
||||
|
||||
_panel = ColorRect.new()
|
||||
_panel.color = Color(0.05, 0.03, 0.02, 0.94)
|
||||
_panel.set_anchors_preset(Control.PRESET_FULL_RECT)
|
||||
_panel.mouse_filter = Control.MOUSE_FILTER_STOP # swallow the tap from the game behind it
|
||||
add_child(_panel)
|
||||
|
||||
var center := CenterContainer.new()
|
||||
center.set_anchors_preset(Control.PRESET_FULL_RECT)
|
||||
center.mouse_filter = Control.MOUSE_FILTER_IGNORE
|
||||
_panel.add_child(center)
|
||||
|
||||
var box := VBoxContainer.new()
|
||||
box.alignment = BoxContainer.ALIGNMENT_CENTER
|
||||
box.add_theme_constant_override("separation", 16)
|
||||
center.add_child(box)
|
||||
|
||||
var title := Label.new()
|
||||
title.text = "TAP TO PLAY"
|
||||
title.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER
|
||||
title.add_theme_font_size_override("font_size", 52)
|
||||
title.add_theme_color_override("font_color", Color(1.0, 0.78, 0.22))
|
||||
box.add_child(title)
|
||||
|
||||
var sub := Label.new()
|
||||
sub.text = "Fullscreen, landscape"
|
||||
sub.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER
|
||||
sub.add_theme_font_size_override("font_size", 22)
|
||||
sub.add_theme_color_override("font_color", Color(0.85, 0.85, 0.85))
|
||||
box.add_child(sub)
|
||||
|
||||
|
||||
func _input(event: InputEvent) -> void:
|
||||
if not _active or not _panel.visible:
|
||||
return
|
||||
var gesture := (event is InputEventScreenTouch and (event as InputEventScreenTouch).pressed) \
|
||||
or (event is InputEventMouseButton and (event as InputEventMouseButton).pressed)
|
||||
if gesture:
|
||||
# The native DOM listener (controls_manager.gd) enters fullscreen synchronously on this
|
||||
# same tap; this is the belt-and-suspenders explicit call.
|
||||
Controls.request_fullscreen_landscape()
|
||||
_panel.visible = false
|
||||
if not _fs_supported:
|
||||
_dismissed_unsupported = true # iPhone: don't reappear, there's nothing to enter
|
||||
get_viewport().set_input_as_handled()
|
||||
|
||||
|
||||
func _process(delta: float) -> void:
|
||||
if not _active:
|
||||
return
|
||||
_poll_accum += delta
|
||||
if _poll_accum < _POLL:
|
||||
return
|
||||
_poll_accum = 0.0
|
||||
|
||||
# Portrait: OrientationGuard owns the screen — stand down until they turn the device.
|
||||
if _is_portrait():
|
||||
_panel.visible = false
|
||||
return
|
||||
if not _fs_supported:
|
||||
# Can't ever enter fullscreen (iPhone) — offer the tap once, then get out of the way.
|
||||
_panel.visible = not _dismissed_unsupported
|
||||
return
|
||||
# Show the gate whenever we're not fullscreen, and arm so the next tap (re-)enters.
|
||||
var fs := Controls.is_browser_fullscreen()
|
||||
_panel.visible = not fs
|
||||
if not fs:
|
||||
Controls.arm_fullscreen()
|
||||
|
||||
|
||||
func _is_portrait() -> bool:
|
||||
var s := get_viewport().get_visible_rect().size
|
||||
return s.y > s.x
|
||||
Reference in New Issue
Block a user