Castle Crawler - hand-off brief for the next session
Point the next session at this file. It says where the project stands on
2026-10-18 and what is left to do; nothing here depends on earlier chat
history. Project rules and conventions are in CLAUDE.md; how everything
works (and every test) is in wiki/game/game-prototype.md.
1. The project in one paragraph
An original 2D side-scrolling co-op action game, a spiritual successor to Castlevania: Harmony of Despair: 1-6 players online (host-authoritative), 30-minute chapters that each end in a boss, no XP (power only from loot and skill mastery), a camera that zooms from one room to the whole chapter. Godot 4.7.1 (GDScript), art modelled in Blender and pre-rendered to sprite sheets. Original IP only.
2. Where it stands (everything below is merged in main and live)
- Stages (
Main.STAGES): Prologue: The Outer Ward (the built-in level, 6 named rooms, Warden boss; most headless tests run here), Chapter 1: Hollow Keep (22 rooms), Chapter 2: The Clock Tower (31 rooms, clockwork traps and puzzles, Midnight Engine boss), Boss: The Colossus Pit, and the hub, The Forge Hall. - Hub: forge (upgrade +1..+5, reforge affixes, salvage for gold), chapter board (host picks, everyone moves; clients mark themselves READY).
- Loot: rarities, affixes and secondary stats (crit, lifesteal, MP/s,
subweapon power, luck), two 3-piece sets, gold (chapter rewards, coins from
enemies and chests, salvage). Gear, mastery and gold are saved per player
(
GearSave, version 3). - Screens: gear screen (EQUIPMENT / MASTERY / FORGE tabs), chapter map and minimap with shared fog of war, chapter-end screen (player cards, finds, badges, play again or back to the hub).
- Online: join handshake (version
Net.VERSION, currently 0.8.1, and a - Online: join handshake (version
Net.VERSION, currently 0.9.0, and a name), clear refusal/error messages, hub lobby with READY, quick chat and emotes (T / L3, bubbles and a chat log). Desktop plays over ENet (LAN, open port or Tailscale; step-by-step guide inwiki/game/game-prototype.md, "Tester en ligne avec des amis"). The web build plays co-op too, over WebRTC with a 5-letter room code (web_lobby.gd; signaling on the site's/signal, a Cloudflare Durable Object per code,deploy/web/worker.js). - Characters: eleven playable characters on the start screen: Aurel,
Silas and the Wanderer (PR #20), Brunhal the shield tank (#24), Ansel the
censer whip (#26), Kestrel the wallrunner (#27), Coralie the healer (#28),
Orsolya the werewolf (#29), Nemo the mask collector (#30), Ysolde the mage
(#31) and Tobias the summoner (#32). Kits, controls and tests are in
wiki/game/game-prototype.md("Playable characters"); the design doc "Castle Crawler - Playable Characters" has a Roster tab (all 11: stats, skills, gear) and one tab per archetype A-H, all built. Each new character is a block-out on the shared Meshy rig (Orsolya's wolf: a copy of the enemy wolf). - Co-op death (PR #21): a downed player with teammates rises as a ghost after 2 s (flies, untouchable) and is revived by a teammate's interact next to it; Phials of Return (pickup, two carried, Down + Use) revive the nearest fallen teammate from anywhere; Aurel's Last Rite scroll raises everyone in reach. Solo keeps the 3 s team-wipe respawn.
- Enemies (PR #21): the Ossuary Sentinel mini-boss (shield block, guard
break, bash / cleave / shards, enrage, its own HUD bar,
minibossloot), the leaper and the wisp, all placed in Hollow Keep. - The bestiary (branch
feature/silent-abbey, 2026-10-18): ten more enemies (Rampart Husk, Cinder Lobber, Veil Shade, Ceiling Dripper, Powder Thrall, Bone Chanter, Rolling Grinder, Lantern Archer, Gilded Mimic, Crypt Burrower), two mini-bosses (the Iron Jailer, the Glass Cantor) and two three-phase bosses (the Pale Huntsman, the Glass Saint), each with several attacks. All code-drawn block-outs. Met in Bestiary: The Menagerie (--stage bestiary) and the arenashuntsman_gladeandsaint_nave; the full table iswiki/game/enemies.md, the checksbestiarytest,jailertest,cantortest,huntsmantest,sainttest(game/scripts/test_bestiary.gd). Projectiles can now arc (Level.spawn_projectile(..., {"gravity": ...}));Net.VERSIONis 0.9.0. - Adaptive music (PR #21):
make_audio.pywrites stems (base loops plus same-length layers);Audio.music(track, layers)plays them sample-locked and the HUD raisescombatnear enemies / after a hit and the boss phase layers. - Accessibility (PR #21, options menu): screen-shake intensity, a
colour-blind filter (daltonization shader), text size;
Accessibilityautoload. - Networking (PR #21): rarely changing synced properties replicate on
change, enemies far from every player sync at 5 Hz, ENet range-coder
compression.
--loadtest(host + five bot clients) measured on localhost: host ~2.3 Mbit/s up for a full party, clients ~0.5 Mbit/s down, 1.7 ms physics per frame. - Steam hooks (PR #21):
SteamLobbyautoload opens a friends-only lobby, invites from the options menu, joins on+connect_lobby; only live with the GodotSteam extension (untested without it: no account yet) and the game still travels over ENet. - Live web build: https://castle.veridianresonance.com (deploy with
deploy/web/deploy.sh;--prefix betaputs a build under/beta/without touching the live one). - Windows build for friends: CI artifact
CastleCrawler-windowson every push tomain(zip with a French README for players), ordeploy/desktop/build_windows.shonce the Windows export templates are installed locally.
3. How to work on it
- Godot:
C:\Users\rd_d\Downloads\Godot_v4.7.1-stable_win64.exe\Godot_v4.7.1-stable_win64_console.exe(not on PATH). After pulling, import once:godot --headless --path game --import. - Tests:
GODOT=<path> python tools/ci/run_tests.py --smokeis the express suite (nine fast tests, what every PR runs in CI); label a PRfull-testsfor the regression suite below.GODOT=<path> python tools/ci/run_tests.pyruns every headless scenario (42, solo, host + client, and the load test with five clients) against the values inwiki/game/game-prototype.md; name tests to run only those,--listlists them. The co-op features' scenarios live ingame/scripts/test_coop.gd;--coopshot <path>(windowed,--stage hollow_keep) saves screenshots of the new HUD pieces. CI (.github/workflows/ci.yml) runs the same on every PR plus the web and Windows exports. - Two-peer tests use port 7777; if another Godot is hosting on it (another
session, the editor), hosting fails and the tests hang. Close it, or use
--port Nfor manual runs. - The older tests turn off newer systems so their seeded numbers hold:
Items.rich_loot,Items.gold_drops(seetest_scenarios.gd,run()). A new system that changes drops or damage should do the same (the Phial of Return rolls on its ownSubweapons.phial_rngfor that reason). - GDScript lambdas copy plain locals: a test that counts from inside a
lambda or a signal callback must keep the counter in a Dictionary (see
test_coop.gd); the old pattern silently stays at zero. - Bump
Net.VERSIONwhenever anything that crosses the network changes. - Several sessions have worked in the main checkout at once. Work in a git
worktree on a branch, one PR per feature, and merge
mainin before merging (CI must be green; the repo does not allow auto-merge).
4. Backlog
Features
- Ghost art and the new enemies' art: the ghost reuses the fall frame (pale blue, see-through); the Sentinel, leaper and wisp are code-drawn block-outs like the older enemies.
- Bandwidth: a full party costs the host ~2.3 Mbit/s up (see the load
test in
wiki/game/game-prototype.md). Next steps if that is too much for home connections: player state at 30 Hz instead of 60 (the interpolator copes; re-check the prediction-error expectations),net_animas an index instead of a StringName. - The bestiary in the chapters (placed 2026-10-19: Hollow Keep seven,
Clock Tower eight plus the Iron Jailer, Silent Abbey seven plus the Glass
Cantor; see
wiki/game/enemies.md, "Where to meet the new ones"). Left: the Prologue has no mini-boss, the Cinder Lobber is only in the Menagerie, and the Pale Huntsman and the Glass Saint have arenas of their own but no chapter leading to them. Hollow Keep's JSON has no generator:game/levels/add_bestiary_hollow_keep.pyappends to it. - Art for the bestiary: all fourteen are ColorRect block-outs
(
game/scenes/<type>.tscn); they need Blender sources and sheets like the wolf. - Brunhal, next steps (her kit is checked solo and on a client by bulwarktest and bulwarknettest): shields as loot in her off hand (today the tower shield is fixed); sets with one-handed weapon pieces (the two sets' weapons are two-handed, so no new archetype can finish one); armour restricted per character.
- Kestrel, next steps: level design that uses her (tall shafts and ledges only she reaches with a lever or a chest at the top, the co-op role the design doc gives her); a human animation pass on her six moves (they are scripted poses); her test wall is built by the test, the stages have no wall-run tutorial yet.
- Coralie, next steps (her kit is checked solo by bindertest): a
two-peer test (the book and Leaping Verse predicted on a client, a capture
and Sanctuary on another player's character); spells for the bosses and
the enemies without one (Hound, Tickwraith, Windup Knight); an icon per
spell and a spell page on the gear screen (slots are picked by turning the
page with the book open); the tome bonus in the gear screen's ATK; a Meshy
model instead of the block-out.
weapons.blendand the weapons sheet are binary and every archetype branch adds to them: after merging another one, re-runrender_weapons.py(it adds the missing collections) and sync. - Orsolya, next steps (checked by moonbloodtest and moonbloodnettest):
her art is a block-out on the shared rig and a recoloured, de-tentacled
copy of the enemy wolf (a Meshy model, prompt in the design doc, would
replace the human block-out); the wolf keeps the human body box (52 px
tall), so it fits wherever she stood and never gets stuck turning back;
Pounce aims at the enemy where the predicting client sees it, so it can
drift a few px on a client when the target moves (not covered by the
two-peer test); armour restriction per character now exists
(
"armor"inCharacters.DEFS) but only Orsolya uses it. - Nemo, next steps (his kit is checked solo by masktest; nothing on a client yet): a two-peer test (the glide, the shield and the blink are predicted like Brunhal's guard and need the same check); the Warden's mask (to be designed) and masks for the hound, the Colossus and the Midnight Engine; the worn mask as a head-socket layer (one look today); a mask icon sheet instead of the drawn placeholder; per-mask cooldowns if the shared one feels wrong.
- Ysolde, next steps (her kit is checked solo by ysoldetest and on a client by ysoldenettest): robes as armour (she wears only the Leather Coat and the Hunter's Garb); a charging pose while attack is held for the Star Bolt; the enemy slow and pull are not shown on clients (no tint); the gear screen's ATK preview leaves out her staff's spell-rank bonus.
- Tobias, next steps (checked solo by beastwardentest, on a client by
beastwardennettest, and at six players by tobiasloadtest): totems (his own
charms that change a familiar, e.g. a slower wolf that takes hits); a
real crow and moth sprite instead of the code drawings; the hound only
follows the floor and platforms (no path-finding round walls, it reappears
at his side past 22 tiles); the crow brings loot only to him, not to a
teammate in danger (the doc's coop note); his armour rule
(
Items.TOBIAS_ARMOR) is the first per-character armour restriction and could become a general"armor"list inCharacters.DEFS. - Shared kit work: per-character armour has grown several mechanisms
(
Items.WALLRUNNER_GEAR,BINDER_ARMOR,STARWRIGHT_ARMOR,TOBIAS_ARMOR, Nemo'sMASKED_NO_ARMORand the general"armor"list read byCharacters.armor_of): fold them into the"armor"list. No archetype has a co-op super (the Dual Crushes of the reference). The eight merges were resolved by hand; a fresh look atplayer.gd's_step(where every kit starts its moves) would be worth it.
Online
- A real game between two PCs with the Windows build and Tailscale (the guide exists; never tried across machines yet).
- Steam (GodotSteam and its relay) once there is a Steamworks account:
the lobby and invite hooks exist (
steam_lobby.gd, app id 480 until the game has its own); what is left is carrying the game over Steam's relay (SteamMultiplayerPeerinNet.host/Net.join) and testing it with the extension installed. Replaces Tailscale and the room codes on desktop. - TURN relay for the web, for strict networks (company, school) where peer-to-peer WebRTC fails.
- Web and desktop cross-play (needs the webrtc-native GDExtension on desktop).
Content and polish
- Make the Prologue a real intro chapter: story text or an arrival scene, a room-by-room controls tutorial, the Warden introduced as the first boss.
- Art: Clock Tower enemies, boss and tiles; the forge and chapter board; an item icon sheet. All are drawn in code for now.
Technical debt
- Client prediction drifts up to ~5 px during the axe throw (the CI reports
it as a flaky check of
subtest). clocknettesttiming race: cog Q4 stops right when both peers print (also a reported-only flaky check).- A stray empty
castle_crawler-goldfolder next to the repo could not be deleted (locked by a process); remove it by hand.