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
+123
View File
@@ -0,0 +1,123 @@
extends SceneTree
## Guardrail for the on-screen joystick's self-healing. The reported bug is a stick that
## "disappears / goes non-responsive": a touchend dropped during a resize/orientation flip (or
## an app backgrounding) leaves _stick_active stuck true, so the ghost hint hides and no new
## stick can start — and the bull keeps coasting on the held move actions. touch_controls.gd
## now resets on relayout + focus-out, prunes orphaned touches, and lets the latest left-zone
## touch re-acquire the stick. This drives synthetic touch events and asserts the stick never
## stays stranded and never leaves movement held.
## Run: godot --headless --script res://tests/touch_controls_test.gd
const _MOVE := [&"move_forward", &"move_back", &"move_left", &"move_right"]
var _pass := 0
var _fail := 0
var _tc: CanvasLayer
func _init() -> void:
_run.call_deferred()
func _ok(c: bool, m: String) -> void:
if c:
_pass += 1
print(" PASS: ", m)
else:
_fail += 1
print(" FAIL: ", m)
func _press(index: int, pos: Vector2) -> void:
var e := InputEventScreenTouch.new()
e.index = index
e.position = pos
e.pressed = true
_tc._input(e)
func _release(index: int, pos: Vector2) -> void:
var e := InputEventScreenTouch.new()
e.index = index
e.position = pos
e.pressed = false
_tc._input(e)
func _drag(index: int, pos: Vector2) -> void:
var e := InputEventScreenDrag.new()
e.index = index
e.position = pos
_tc._input(e)
func _any_move_held() -> bool:
for a: StringName in _MOVE:
if Input.is_action_pressed(a):
return true
return false
# A point safely inside the left-hand joystick zone (independent of viewport size), plus the
# same point pushed a full radius "up" so the stick engages move_forward.
func _stick_origin() -> Vector2:
return _tc._stick_zone.position + _tc._stick_zone.size * Vector2(0.25, 0.5)
func _run() -> void:
var dp: Node = root.get_node("/root/DP")
var controls: Node = root.get_node("/root/Controls")
dp.call("set_value", "force_touch_controls", true)
_ok(controls.call("use_touch_ui"), "force flag makes use_touch_ui() true (touch UI live)")
# Headless boots a 64x64 root viewport, which collapses every UI rect on top of each other.
# Give it a real phone-ish landscape size so the joystick zone / buttons lay out sanely.
root.size = Vector2i(1152, 648)
_tc = (load("res://touch_controls.gd") as Script).new()
root.add_child(_tc)
await process_frame # _ready builds _surface and runs the first _relayout
_tc._relayout() # ensure the layout reflects the size set above
var o := _stick_origin()
var up := o + Vector2(0.0, -_tc._stick_radius)
# (a) A resize mid-drag (dropped touchend) must not strand the stick or keep the bull moving.
_tc._reset_touch_state()
_press(0, o)
_drag(0, up)
_ok(_tc._stick_active and _any_move_held(), "drag engages the stick and holds movement")
_tc._relayout() # the resize/orientation flip that used to swallow the release
_ok(not _tc._stick_active, "a relayout mid-drag stands the stick down (not stranded)")
_ok(not _any_move_held(), "movement is released on relayout (bull doesn't coast)")
# (b) App backgrounded / focus lost — same self-heal via the notification hook.
_tc._reset_touch_state()
_press(0, o)
_drag(0, up)
_tc._notification(Node.NOTIFICATION_APPLICATION_FOCUS_OUT)
_ok(not _tc._stick_active and not _any_move_held(), "focus-out clears the stick and movement")
# (c) A stranded stick self-heals: with finger 0's release lost, a fresh finger 1 in the zone
# re-acquires the stick; finger 0's late release must NOT kill finger 1's stick.
_tc._reset_touch_state()
_press(0, o) # finger 0 owns the stick
_press(1, o + Vector2(20, 10)) # finger 1 lands while 0 is still (wrongly) held → takes over
_ok(_tc._stick_active and _tc._stick_index == 1, "latest left-zone touch re-acquires the stick")
_release(0, o) # the stranded finger finally reports up
_ok(_tc._stick_active and _tc._stick_index == 1, "a superseded finger's release doesn't end the stick")
_release(1, o)
_ok(not _tc._stick_active, "releasing the owning finger ends the stick")
# (d) Watchdog: a touch that vanishes from tracking (no release event at all) is pruned.
_tc._reset_touch_state()
_press(0, o)
_tc._touches.erase(0) # simulate the finger silently disappearing
_tc._process(0.016) # prune runs each frame
_ok(not _tc._stick_active and not _any_move_held(), "watchdog prunes a vanished touch and frees the stick")
_finish()
func _finish() -> void:
print("Results: %d passed, %d failed" % [_pass, _fail])
quit(1 if _fail > 0 else 0)