3586555aea3959817ce13c462936e3e5a9606618
7 Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
3586555aea |
Remove non-redistributable art from the repo; record licence terms
Researched the licence at each of the four itch.io sources. Two of the four
forbid redistribution, and publishing a repository containing them is exactly
that, so they are out of the tree.
0x72 DungeonTileset II CC0 1.0 commercial ok, no credit required
Helton Yan Pixel Combat CC BY 4.0 commercial ok, CREDIT REQUIRED
Fire Pixel Bullet 16x16 custom non-commercial, NO REDISTRIBUTION
750+ Effect and FX Pixel custom non-commercial, NO REDISTRIBUTION
Both bdragon1727 packs state verbatim: "You cannot do: Resell / redistribute
this asset." The FX pack's paid tier is no better for this purpose -- "You can
NOT re-distribute the file, no matter how much you modify it" -- so paying does
not unlock it and neither does editing the art.
bullets.png and impact.png therefore move to assets/local/, gitignored.
Art.bullets_texture() / impact_texture() load them at runtime and return null
when absent; the bullet renderer falls back to the generated dot it used before,
tinted per kind so bullet types stay distinguishable. Nothing there may be
preload()ed -- preload resolves at compile time and would fail the build on
every machine without the files, which is every clone.
Verified both ways rather than assumed: with the files present the game uses
them and no fallback triggers; with them moved aside, 156/156 tests pass and the
game runs clean on the generated dots.
Added CREDITS.md, because Helton Yan's CC BY 4.0 makes attribution a licence
obligation rather than a courtesy. A file in the source tree does not discharge
it once the game ships -- an in-game credits screen is now a tracked todo, and
flagged as a licence requirement rather than polish.
NOT addressed here, and it needs a decision: these files remain in git history
at
|
||
|
|
9fa2e260f2 |
Replace placeholder rendering with real sprites and audio
Terrain, actors, boss and bullets are sprites now, and four sounds play off server events. 548KB total across seven files, out of 3.9GB of source packs. - src/view/art.gd holds every atlas rect and sound path in one table, copied from the 0x72 pack's own tile_list. Scattering coordinates through draw calls would make re-cutting the atlas a hunt, and a wrong rect invisible. - Bullets get one MultiMesh per kind rather than one overall: a MultiMeshInstance2D carries a single texture and each kind needs a different region of the sheet. Four draw calls, no custom shader passing UVs through per-instance data. - src/view/sfx.gd is a 16-voice round-robin pool. Bullet-hell fire rates mean sounds overlap constantly, and identical sounds landing within two frames are collapsed so a ring hitting eight bullets is a bang rather than clipping. Every sound is triggered by a server event, never a local guess, so what you hear matches what happened. Audio was converted, not copied: the pack ships 24-bit/96kHz masters averaging 2MB. Downsampled to 16-bit/44.1kHz mono, silence-trimmed, and the shoot sound hard-capped to 0.30s -- it came out at 2.03s, against a fire cooldown of 0.12s, which would have smeared held fire into noise. tests/unit/test_art.gd checks what cannot be eyeballed here: every atlas rect lands inside its texture, every animation frame of a strip fits (the last frame is what runs off the sheet, not the first), there is a sprite per bullet kind and per enemy visual, and no sound is long enough to stack badly. A wrong atlas coordinate does not error -- it silently draws the wrong pixels. Licence sources recorded in docs/ASSETS.md now that they are known. All four are free versions and four rows still say "confirm at source": free itch packs vary on credit and commercial use, and there is no credits screen yet. 155 tests (was 146). check.sh, test.sh and smoke.sh pass. |
||
|
|
7ee6c8e761 |
Close Stage 1: per-peer actor interest, boss room confinement
ci / verify (push) Successful in 46s
The gap Stage 1 left open: snapshots were encoded once and broadcast to every peer in an instance, so clients were handed enemies the fog then hid. Fog is a rendering rule -- a modified client draws whatever it holds -- so that was no defence at all. Snapshots are now encoded per peer, and actors beyond ACTOR_INTEREST_RADIUS (800u) are never sent. Measured cost of four filtered encodes against one shared: 95.6us vs 24.9us, or 0.032 ms/tick amortised over the snapshot interval. Two asymmetries worth keeping: - The observer's own player record is never filtered, however far out the arithmetic puts it. The client reconciles its prediction against that record, so dropping it breaks the player's own movement rather than hiding someone. - Only bullet SPAWNS are filtered, never despawns. A client told about a bullet must always be told it died, or it keeps a phantom. Bullet spawns use a much wider radius (2200u) than actors, deliberately. An enemy appearing at the edge of sight is cosmetic; a bullet withheld at spawn that later flies into view is invisible damage. The floor is longest bullet travel + fog radius -- 1500 for the Warden's Collapse snipe -- and test_interest.gd recomputes that from live content, so adding a faster or longer-lived bullet fails a test instead of producing bullets that wink into existence. Also clamped bosses to their room. A no-op today since every boss is stationary, which is exactly when the invariant is cheap to establish: boss rooms have no door that locks, so the only thing keeping a fight in the boss room is the boss, and Stage 5's movement work would otherwise break it quietly. 146 tests (was 137). check.sh, test.sh and smoke.sh pass. Stage 1 has no partials left; docs/ROADMAP.md now records the three interest radii and the floor each must respect. |
||
|
|
20dd6bc502 |
Keep asset packs out of git and out of Godot's importer
ci / verify (push) Successful in 46s
The packs total 3.9GB, almost all of it Helton Yan's SFX shipped as 24-bit / 96kHz studio masters -- ~2MB for a single gunshot. Two guards, because they solve different problems: - .gitignore excludes the contents. The directory and its guard files stay tracked, so a fresh clone has the .gdignore in place before anyone drops packs back in. - assetpacks/.gdignore stops Godot descending into the tree at all. This is the urgent half: tools/check.sh runs --import, which walks all of res://, so the next check would have imported several thousand PNGs and 2,100 of those WAVs, and every run after it. Verified by controlled test rather than assumed -- an unguarded PNG under res:// imports and gets a .import file beside it; with .gdignore neither happens. check.sh stays at 3.3s and .godot/imported at 1.2MB. The trade the .gdignore buys is that nothing in assetpacks/ can be referenced from a scene or script. That is the intended shape: raw source on one side, game-ready files copied into res://assets/ on the other, so adding a huge pack costs the repo and the import step nothing. docs/ASSETS.md records attribution, because gitignoring the packs also gitignores their licence files. Worth flagging: four of the six packs shipped with no licence text at all, and two of the undecided ones are demo versions, which are often more restricted than the paid packs. Only RF Catacombs has explicit terms (public domain, no resale). Those rows need a source URL before anything ships. Also removed __MACOSX and .DS_Store extraction artefacts, and left considering_dont_use_yet/ untouched as the user's own "not chosen" marker. |
||
|
|
de48afcbd9 |
Fix aiming under a scrolling camera; add status and decisions docs
ci / verify (push) Successful in 46s
The aim bug was collateral from the camera work. "Mouse relative to the centre of the screen" WAS the cursor's world position while the world was drawn fixed at the origin, so subtracting the player position gave the right vector. Once the camera scrolled, that expression became the aim vector itself, and subtracting the player position again made the ship aim at a fixed world location -- walking around swung the crosshair with the mouse held still. Fixed by inverting the transform the view actually draws with (world = screen - world_view.position, published by the game scene each frame) rather than assuming the player is centred, so it still holds if the camera later clamps at map edges or gets shake or look-ahead. tests/unit/test_aim.gd pins it, including the regression directly: moving the player must not move the crosshair. Documentation, for other sessions picking this up cold: - docs/ROADMAP.md rewritten as the status map -- every feature in the brief against its state and the file implementing it, the known gaps called out (actor interest management is the notable one), and the ten design questions that are genuinely unspecified and should not be guessed at. - docs/DECISIONS.md, new: settled decisions with their reasoning, so a session does not re-litigate or re-ask. Several are not the obvious default -- no i-frames, no contact damage, non-interruptible escape, and never sending the map seed. - CLAUDE.md and README point at both. 137 tests; check.sh, test.sh and smoke.sh pass. |
||
|
|
005679f1b5 |
Fix bullet/ship desync; rework death, escape, arrival and hub awareness
(1) Bullets appeared to trail the ship. Two independent causes, measured with
the new tools/diag_prediction.gd rather than guessed at:
- ServerRuntime ticked before ClientRuntime, so input sampled on frame N was
not consumed until frame N+1, leaving the drawn ship a constant one tick
(4.00px at 240 u/s) ahead of the authoritative one that bullets spawn from.
ClientRuntime now sets process_physics_priority = -10. Gap on a listen
server: 4.00px -> 0.10px mean, 0.30px worst.
- PLAYER_MUZZLE_OFFSET was PLAYER_RADIUS + 6 = 12px against a 13px drawn
ship, so bullets were born inside the sprite. Regression from the previous
commit's hitbox shrink; it now derives from PLAYER_VISUAL_RADIUS.
(2) No more timed respawn. A downed player stays down until they ask for the
hub (E), which is an ordinary input -- the server has no "revive me" message.
(3) Escape channel 3s -> 1s, and damage no longer cancels it. An interruptible
channel makes killing the process strictly better than using the button, so a
dropped connection now runs the same channel: the player stays in the world as
linkdead, still killable, and is only released once it completes. Instances
refuse to close while a linkdead body is resolving, or a solo drop would delete
it on the next tick and hand the exploit straight back.
(4) Escape opens an in-game menu: return to hub (routed through the same held-
escape channel, not a new message), disconnect, quit.
(5) Server pushes a roster so the hub shows who is online and which dungeon
they are in. Entering a dungeon grants 2s arrival protection -- invulnerable
AND weapons-cold, since invulnerability alone would make the spawn a free
firing position -- flagged in the snapshot and drawn on every protected ship.
(6) Cleared dungeons hold the party 30s (was 5s) with a visible countdown.
(7) The hub's grey circle was a 100k-HP target dummy that read as scenery. Now
drawn as a bullseye so its purpose is legible.
Protocol version 1 -> 2. 91 tests (was 78); smoke.sh gains a bot that is
SIGKILLed mid-dungeon to prove the disconnect path end to end. check.sh,
test.sh and smoke.sh all 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 |