master
5 Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
61680d00d7 |
Style the rest of the interface: panels, sliders, scrollbars, cards
ci / verify (push) Successful in 48s
Buttons were the easy half. This is everything else. Dialogs sit on panels now rather than being text over a dimmed world -- the pause menu, character select, upgrades, settings and credits all root through UiTheme.dialog_panel(). The panel stylebox is the pack's frame MODULATED DARK. The set is cream throughout, which is right for buttons and wrong for a dialog over a dark dungeon; tinting keeps the pixel border and the corner shape and lets every label the game already draws in light colours stay readable, instead of recolouring every label in five screens to suit the art. Sliders and scrollbars are the pack's too, section headings sit on its banner ribbon, and an upgrade card's rarity is now its frame rather than a word on it -- three choices are compared at a glance and a colour reads faster than a label. Two bugs of the same shape, and neither was findable without looking at the screen. A Slider and a ScrollBar take their THICKNESS from the stylebox's minimum size, which for a StyleBoxTexture is its content margins. Mine were zero, so both resolved the correct stylebox, reported the correct texture, and drew a groove zero pixels tall. A probe confirmed the theme was resolving perfectly while the track was invisible. Tests now assert every slider and scrollbar stylebox has a non-zero minimum, and it is gotcha 7 in CLAUDE.md. The first attempt at the slider groove also used the pack's HOLLOW bar sprite, whose middle is transparent -- tinting it dark left an outline and nothing else. It uses the solid one. tools/screenshot.tscn gained the upgrade screen, which it has to stage: the game scene owns that screen's visibility and re-asserts it every frame, so setting `visible` lasted exactly one frame, and standing the player at the NPC client-side lasted until reconciliation pulled them back. It now moves the player on both sides and holds it until the shot. That tool has caught four bugs the automated gates all passed. check.sh clean, 468 tests, SMOKE PASS, all four diagnostics green. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
132646f6c3 |
Rework the UI on Crusenho's pack; credit it in the game, not just the repo
ci / verify (push) Successful in 48s
Crusenho's Complete UI Essential Pack is CC BY 4.0 -- redistributable and
commercial-friendly, confirmed from the License.txt the pack itself ships --
so unlike the two bdragon packs a subset is committed: twelve PNGs, 48 KB,
under assets/sprites/ui/. Only what is used, because each committed PNG costs
a Godot .import sidecar and a directory nothing references is one nobody
prunes.
UiTheme builds a Theme in code from it -- button states, panels, line edits --
and every screen roots itself through UiTheme.themed_root(). The HUD's bars are
the pack's frame with a tinted fill, drawn as three horizontal slices because
Godot's nine-patch lives on nodes and the HUD is drawn rather than built from
controls. Inventory slots use the pack's slot art at exactly twice the source
size; a non-integer scale on a 1px border reads as a wobble along every edge.
The credits screen is the other half of the request and it is a licence
obligation, not a nicety: two packs are now CC BY, which asks for attribution
"in any reasonable manner", and a markdown file in a source repo is not
reasonable for someone who downloaded a build. Settings -> Credits shows every
source with its terms and a link to the licence text. test_credits.gd asserts
CREDITS.md and docs/ASSETS.md name every entry, so the three cannot drift.
Two things found by actually looking at the screen, which is the point:
- The FIRST version of this styled nothing. A Control inherits its theme from
Control ANCESTORS only, and the chain breaks at the first plain Node or
CanvasLayer -- which is every screen here. get_window().theme set the
property, changed nothing, and read as correct. check.sh, 458 tests and a
clean smoke run all passed with the entire interface unstyled. The theme
test now instantiates every screen and asks what its buttons resolve.
- The settings screen showed Fire bound to the right mouse button, because
the test suite was writing the player's real user://settings.cfg --
rebinding calls save() and nothing had redirected the path. Settings.path
is now redirectable, the fixture points it at a scratch file, and a test
asserts the default is still the player's own.
tools/screenshot.tscn is what found both. It boots the client windowed and
saves the menus, the HUD, settings and credits. Manual, needs a display, and
the only thing in the project that can tell you the interface rendered.
check.sh clean, 460 tests, SMOKE PASS, all four diagnostics green.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
||
|
|
872922e9e2 |
Fix boss jitter; add a settings screen for controls and volume
ci / verify (push) Successful in 48s
The jitter had two causes, and the larger one is embarrassing: boss_state() handed back the newest snapshot raw while players and enemies both went through the interpolator. The boss therefore stepped at the 20Hz snapshot rate instead of the frame rate. Invisible for as long as every boss stood still, and the first one that moved looked broken. It is interpolated now -- but not across an instance change, where the previous snapshot describes a different fight in a different room and lerping to it would fling the new boss across the map for a frame. The smaller cause was server-side: a CHASE boss corrects by the SIGN of its distance error, so at the standoff the sign flipped every tick and the boss vibrated a couple of pixels at 60Hz. It has a dead band now. The settings screen covers rebindable controls and volume, reachable from both the main menu and the in-game menu. Bindings are stored as physical keycodes -- following key position, the choice setup_input_map.gd already made -- and labelled back through the active layout so an AZERTY player reads the letter on the key their fingers are on. A rebind replaces every event on the action rather than the first, because an action that kept its alternates would still answer to the key you just moved away from. A key already in use is refused and the clash is named. Reset restores what the PROJECT shipped, captured once before anything overrides it -- captured later it would restore the last session's choice, which is the thing being undone. Effects play on an SFX bus created at runtime, so both sliders are real mixer settings rather than a number multiplied into every play() call. Worth recording: the test suite AND the smoke test both passed while a client logged twelve engine errors on every startup. ConfigFile.get_value(s, k, null) does not mean "no default" -- it means the key is absent and no default was given, and the engine logs an error per action. Nothing caught it because the smoke refutations matched SCRIPT ERROR and friends, and a plain ERROR: is none of those. It surfaced from running the client and reading the output. smoke.sh now asserts no plain engine errors either, excluding by name the one line Godot prints on every clean exit, and reintroducing the bug makes it fail. One mutation caught nothing and should not have: the early return in linear_to_db_clamped was dead code, since the clamp beneath it already prevents negative infinity. Removed rather than left looking tested. check.sh clean, 439 tests, SMOKE PASS (23 assertions), all four diagnostics green, and a real client boots with zero engine errors. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
ded7bf96d5 |
Documentation pass before context compaction
ci / verify (push) Successful in 47s
The roadmap was actively misleading: an earlier stage renumbering left Stage 2's completed work sitting under a "Stage 3 -- todo" heading, and the inventory table orphaned with no heading at all. Since that file is the primary handoff document, a fresh session would have started by re-implementing accounts and characters. Rewritten. Captured in full, from the original brief rather than from memory, the two stages not yet built: - Stage 3 (inventory and loot): 4 slots, potions rare from trash and guaranteed from bosses, world-shared loot, and the player-instanced food item -- with a note that two loot visibilities must exist from the start, because proving the instanced path works is the food item's entire purpose. - Stage 4 (upgrades): every upgrade with its exact stated effect, plus the two constraints it will collide with -- sniper's 2x bullet speed against the tunnelling threshold, and per-player bullet travel against the interest radius that test_interest.gd currently derives from static content. Ten open questions are listed as explicitly do-not-guess, seven of them blocking Stage 4. ARCHITECTURE.md still claimed "the arena is a rectangle" and "no tilemap collision", both untrue since Stage 1. Rewritten around MapGrid, with what the grid costs (axis-aligned, 32px-quantised, bullets under a tile per tick) rather than only what it buys. Added a "verification traps" section to WORKFLOW.md recording six mistakes made during this work, each of which cost a round trip of reporting something fixed that was not: verifying the artefact rather than the behaviour, dumping the wrong channel, measuring a configuration where the bug cannot exist, a test whose setup silently invalidated it, git checkout reverting real work alongside a probe, and a pattern edit matching in two files. They are specific enough to be actionable. Also documented the diagnostics in CLAUDE.md -- they were undiscoverable -- and recorded the current verification surface so "everything passes" has a stated meaning. 206 tests, 15 smoke assertions, both diagnostics pass. |
||
|
|
c4beeae38f |
Initial commit: Transcience MVP
Top-down twin-stick bullet-hell, Godot 4.7, server-authoritative dedicated server with client-side prediction. Clients send input only; the server resolves every hit for both players and enemies (no PvP). - SimWorld: whole simulation as plain RefCounted objects (no nodes, no physics server), ~0.24ms/tick at peak load -- runs headless for free and drives 78 tests in under a second - BulletPool: struct-of-arrays bullet storage, replicated as spawn/despawn events rather than per-tick state - Emitter framework (Ring/AimedSpread/WallGap/ArcSweep) shared by trash enemies and bosses -- a new boss is data in src/content/content.gd, no simulation changes - The Warden of the Fold: stationary 4-phase boss built entirely on that format - Lobby hub with a portal into on-demand dungeon instances; one process hosts the hub plus every concurrent dungeon - Emergency escape: 3s server-owned channel, cancelled by damage - tools/check.sh, test.sh (GUT), smoke.sh (real server + bot clients over ENet), bench.gd; git hooks wired to the same scripts - docs/ARCHITECTURE.md, NETCODE.md, WORKFLOW.md, ROADMAP.md |