Commit graph spaceship/tests/test_icon_button_press_fires.gd
Author SHA1 Message Date
Loïc Wisniewski
2f161621c3 #84: move 215 root files into a folder hierarchy
Everything lived in one flat directory, so a diff gave no clue what area it
touched and finding the file you wanted meant knowing its exact name. Twelve
folders now, grouped by what a thing IS rather than what type it is - a
vessel's definition, its component scenes and its upgrade resources sit
together, because that is the set you read when you change one of them.

  buildings  core  controllers  enemies  logistics  planets
  resources  shaders  surface  ui  vessels  vfx  world

332 files moved and every res:// reference rewritten - 306 files touched,
including the autoload and main-scene paths in project.godot. Nothing renamed:
this is purely where files sit, so the diff is a pile of pure moves and no
behaviour is buried in it. ui_theme.tres, icon.svg and web_shell.html stay at
the root, where Godot and the export preset expect them.

Verified three ways, because a suite alone would not have caught all of it:
the full 117 pass, the web export builds clean, and the game renders.

The export is why that matters. The FIRST export after the move failed with
"Attempt to open script 'res://GameState.gd'" - the old path - while
project.godot on disk plainly said res://core/GameState.gd. That is this
project's documented Android FUSE mtime trap: Godot served a cached copy of a
file whose content had changed. A `touch` fixed it. Nothing in the test suite
would have found that, because the suite runs from a copy on a native
filesystem - the one place the bug cannot happen.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-21 19:44:14 +02:00
Loïc Wisniewski
137e4e528d Fix the web build: a wasm trap on integer % 0, and buttons that never fired
The web export reached the main menu and went no further. Two separate bugs,
one hiding the other.

BUTTONS NEVER FIRED. A browser on a phone delivers the touch DOWN and never
the release. IconButton emits `pressed` only on release, deliberately, so
that dragging off cancels - so on the web it never emitted at all, on every
button in the game. fire_on_press (default OS.has_feature("web")) completes
the press there instead, and does not latch, since nothing is coming to
unlatch it. The cost is drag-off-to-cancel, which every other platform keeps.

Measured on the device rather than guessed: an on-screen probe reported
`touch=1 mouse=1 held=y` after one tap - one down, one emulated mouse down,
the button latched through ordinary GUI routing, then nothing.

THE ENGINE THEN TRAPPED. With the button working, pressing Play loaded and
built Main.tscn and the whole engine stopped mid-frame, page still alive.
An error overlay in a custom HTML shell finally showed why:

    Uncaught RuntimeError: remainder by zero

An integer % 0. Wasm TRAPS on that; native builds silently tolerate it. So
the bug was equally present on Android and in all 100 tests, doing nothing
in both - which is why nothing runnable here could ever have caught it, and
why reasoning from frozen screenshots got nowhere for several rounds. Every
variable modulo is now guarded: the hangar grid's `i % columns`, planet
naming's `% names.size()`, and two in the map editor.

WHAT IS KEPT, AND WHY

- web_shell.html mirrors console errors onto the page, hidden until one
  occurs. Godot's errors go to the browser console, which is unreachable on
  a phone; this found in one shot what inference had failed to find all
  evening. It costs nothing when there is nothing to report.
- MainMenu._start does load -> instantiate -> add_child by hand instead of
  change_scene_to_file. That call does all three opaquely, so a failure is
  indistinguishable from an unpressed button; split, each step reports.
- PlanetAtmosphereController defers the forced world populate out of the
  frame that builds Main.tscn. It did not fix the hang - the trap did - but
  25 chunks of asteroids and 49 of planets do not belong in the one frame
  that already constructs 197 nodes, and nothing can paint until it ends.

Removed the direct-input-routing experiment and its test: it was written on
the theory that touches were not reaching Controls, which `held=y` later
disproved, and its set_input_as_handled() swallowed the very events the
probe was reading.

Full suite green at 100/100.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-19 01:08:09 +02:00
Loïc Wisniewski
00455dc6ba Fire buttons on press where the browser never delivers a release
Every button in the web build was dead - reported as "Play does nothing".

Diagnosed on the deployed build with a temporary on-screen input probe,
because none of it reproduces here: the suite passes, and pressing Play under
the real gl_compatibility renderer (the same path WebGL2 takes) reaches
Main.tscn fine. The probe reported, from a phone browser:

    #6 touch down @ 362,781
    play rect 296,725 128x128
    hit=YES

Three taps, six events, every one a DOWN and not a single UP - and the button
left drawn in its bright held state, pressed_down stuck true because nothing
ever came to clear it. So touches arrive, their coordinates are correct, and
they land inside the button; the browser simply never delivers the release,
in _gui_input or in raw _input.

IconButton emits `pressed` only on release, deliberately, so that dragging
off a button cancels it. With no release that means it never emits at all -
on every button in the game, not just Play. Which also explains why the
earlier suspects went nowhere: the scene change was never reached, so it was
never the scene change, and hit=YES ruled out coordinate mapping.

fire_on_press defaults to OS.has_feature("web"). There, a press completes the
tap itself and does NOT latch, since nothing is coming to unlatch it. Every
other platform is untouched and keeps drag-off-to-cancel.

The cost, stated plainly: a web tap can no longer be cancelled by sliding off
before lifting, and that applies to destructive buttons too. The alternative -
keeping release-firing and treating a new press as completing the previous
one - preserves cancellation but leaves the very first tap dead, which is
worse.

tests/test_icon_button_press_fires.gd drives the flag directly, since a
headless Linux run cannot BE a web build: one tap emits exactly once, a late
release does not emit again, the button does not latch, and with the flag off
the release still drives the press and dragging off still cancels.

MainMenu keeps the load/start bisection introduced while chasing this - a
failed change_scene_to_packed otherwise leaves the menu up, which looks
exactly like an unpressed button.

Full suite green at 100/100.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-18 18:02:20 +02:00