BeamNG.Earth
BeamNG.drive용 1:1 비율 실시간 지구, 실제 고도·위성 이미지·부동 원점·보조 서버에서 LOD 스트리밍
Valhalla MMORPG 기반, Rust 서버와 브라우저 클라이언트, 공유 PvE·채팅·영구 캐릭터
Valhalla MMORPG foundation with an authoritative Rust server, Windows/Linux browser client, shared PvE, chat, and persistent characters
게임JavaScript · PythonGitHub @sork01온라인운영자 미확인
써 보셨어요? 한 번만 눌러 주세요. 로그인은 필요 없어요.
아직 감지된 업데이트가 없습니다. GitHub 릴리스가 나오면 여기에 보입니다.
개인 계정의 공개 저장소 소유자는 GitHub로 로그인해 운영자를 확인하고 소개를 직접 관리할 수 있습니다. 관리자 승인은 필요하지 않습니다.
로그인한 뒤 확인 파일을 만들어 이 저장소의 기본 브랜치에 30분 안에 커밋해 주세요. 다른 브랜치나 포크의 파일은 인정하지 않습니다. 파일을 올린 뒤 아래에서 확인하면 관리자 승인 없이 관리할 수 있습니다. 자격을 유지하려면 파일을 남겨 두세요.
로그인이 필요하면 로그인 화면으로 이동합니다. 로그인 후 이곳으로 돌아와 원하는 확인 버튼을 다시 눌러 주세요.
프로젝트 폴더에서 /nomorevibe verify를 실행하면 소유권을 확인하고 소개를 직접 관리할 수 있습니다.
1분 만에 확인하기README 전체를 불러오는 중입니다. 아래는 저장된 발췌입니다.
A Rust multiplayer server and a browser client for Windows and Linux, built from Valhalla (../Valhalla in the original workspace). The existing splash art, animated character creation, Warrior/Mage/Assassin/Priest/Hunter sprites, equipment layers, isometric meadow, minimap, sound effects, and synthesised music are reused. This project is self-contained; the original game is unchanged.
Public repository: sork01/Valhallasc-AI.README 전문 보기 ↗A Rust multiplayer server and a browser client for Windows and Linux, built from Valhalla (../Valhalla in the original workspace). The existing splash art, animated character creation, Warrior/Mage/Assassin/Priest/Hunter sprites, equipment layers, isometric meadow, minimap, sound effects, and synthesised music are reused. This project is self-contained; the original game is unchanged.
Public repository: sork01/Valhallasc-AI.
The client runs in current Firefox, Chrome, or Edge on either OS. There is no client build step and players do not need Rust. Open the server's URL in a browser. The server serves the client and accepts WebSocket connections on the same origin.
Public client: https://gunning.se/Valhallasc/. Open it from Windows or Linux, click the title, create a character, and join. You can play from outside the server's network; no VPN, downloads, or local server are needed.
The public game is managed by the valhalla.service user service. Apache handles HTTPS and proxies the client and WSS connection to Rust on the private Docker bridge address 172.18.0.1:8080. The Rust port is not bound to the public interface.
systemctl --user status valhalla.service
systemctl --user restart valhalla.service
journalctl --user -u valhalla.service -n 50
For a separate local/development installation, use the instructions below. Do not start another world against the public service's database; use a separate VALHALLA_DB and port when developing alongside it.
Install stable Rust (1.88 or later), then run from this folder:
cargo run --release --locked
Open http://localhost:8080/. Create a character and join the world. Open another browser profile or private window to join as another player. A normal tab in the same profile resumes the same character and is refused while that character is already online.
On this machine a project-local Rust toolchain is installed under .tools/. Use:
bash scripts/cargo.sh run --release --locked
For friends on the same LAN, listen on all interfaces:
VALHALLA_BIND=0.0.0.0:8080 cargo run --release --locked
Players open http://<server-LAN-address>:8080/. Allow that port in the server's firewall. On PowerShell, set $env:VALHALLA_BIND = '0.0.0.0:8080' before running Cargo. The Rust server's bundled SQLite build needs a C compiler; the Linux build tools or Windows Visual Studio Build Tools provide it.
The level-99 Black Hour Foundry is a five-player challenge beneath Yearwheel's Equinox Crossing, with giant clockwork enemies, exclusive purple Nullforged equipment and a dark industrial techno score. Content, balance checks, rebuild commands and saved-instance migration notes: docs/blackhour.md. The source entrance is open. Public activation requires a separate server deployment and saved-instance migration; development does not restart the service.
Click ground to walk; click an enemy to chase and keep attacking.
Spark Travel: speak to the travel master in Alderhaven, Cinderwatch Camp, Rimeward Camp, Lanternmere, Skaldholm, Hollowmoot, Skuldwatch and Heimdall's Perch to discover that stop for your character. Choose a destination in the master's dialogue. Every intermediate master must also be discovered. Each leg costs 20 gold: Alderhaven → Cinderwatch → Rimeward costs 40 gold. The glacier network branches toward Lanternmere and Skaldholm; Skaldholm continues to Hollowmoot and on to Skuldwatch (a flight inside the Wyrdwood is a straight line over the river). You become an animated spark, fly over terrain at three times your class's walking speed, pass every intermediate master, and land at the destination without attracting enemies. The full fare is charged at departure; disconnects and server restarts resume the paid flight.
Riding horse: Stable-hand Greta in Skaldholm sells a permanent horse at level 50 for 500,000 gold. Press H or the HUD button to mount or dismount. Riding doubles normal outdoor travel speed, including in cities. Attacking, casting, Shadowstep, taking enemy aggro, starting Spark Travel, dying or entering a private dungeon dismounts you. Outdoor gates keep you mounted; ownership survives reconnects and restarts.
World map: press M for the painted world atlas based on the owner's newworldmap.png. Correct names, level ranges and interactive regions follow the supplied artwork; detailed maps use real zone data. Asterion is a city landmark inside Stormglass and the Canopy inside Veyrroot; Skaldholm and Nacrehold keep separate regions. Walk across outdoor paths while keeping movement held; purple entrance markers open dungeon/interior maps. Exploration fog is preserved. world/atlas-geography.txt and scripts/atlas_geography.py own the geography; scripts/compact_atlas.py applies the final crossings through node scripts/sync-world.cjs. Rebuild Rust and release it with the synchronized client. Existing zone IDs and saves stay valid. See docs/world-map.md.
Players can walk and Shadowstep through other players, NPCs, and enemies. Enemies stop at least one cell away from living players and move aside when a player enters that space; enemies also avoid stacking with each other.
Ironhide Beetles roam the outer meadow: 240 HP, 22 base damage, fast horn charges, and 10 gold (XP follows its level, see Enemy levels). Their steel shells and bronze horns distinguish them from slimes.
King Slime has 600 HP, 30 base damage, faster pursuit and charge recovery, and rewards 30 gold (XP follows its level). Only one king can be alive at a time. It first appears after a random 5–10 minutes of server uptime, then waits another random 5–10 minutes after each defeat. Its location is randomly chosen from two meadow spawn points, and the world receives an announcement. Dodge its windup or fight together.
Nacrehold is a peaceful 192×192 merfolk city reached through the Nacrehold Tideway east of Keelhaven in Rán’s Deep. Its six neighbourhoods hold 120 shell-roofed homes, coral gardens, a pearl fountain, a tide palace and an archive. Thirty NPCs include eighteen swimming residents, healing, starter fittings, food/potions/bags, loot sales and Travel Master Auralis (discover her stop to unlock Spark Travel to Keelhaven). Nine quests explore the districts, uncover the lost choir’s history and request materials from Rán’s Deep. Beside the mercenary-hiring Meeting Stone stand the three doors of The Drowned Cathedral. The city plays Waterglass Lullaby, an original calm F-major score at 64 BPM, with soft keys and warm chords, without percussion.
The Drowned Cathedral has three independently instanced five-player wings, each with four private copies. The left Flooded Cloister (level 40) winds through teal drowned gardens and bell courts; the right Coral Reliquary (level 45) follows rose-coral galleries and pearl tombs; the main Grand Nave (level 50) crosses broad violet-black marble transepts. Each door enforces its wing's minimum level. Each wing holds four packs and four unique bosses; all four bosses must fall before its final exit opens. Drowned Templars and Bound Cantors appear throughout, with Cloister Tide Leeches, Reliquary Crabs and Abyssal Zealots confined to their own wings. Party enemies, drops and clears stay private; an empty wing resets and logging back in from it returns you to Nacrehold. Bosses use the existing party loot rolls and guarantee exclusive blue gear at their wing's exact level, with 15% more attack/defense than ordinary same-level blue pieces. The left, right and main wings play three original looping scores: Stillwater Vespers, Pearls Behind Glass, and Heart of the Drowned Tide.
Asterion, the Seven Rings is a 180×180 glass-roof city reached from the southeastern Glassroot Causeway in Moonspore Canopy. Its 130 addressed homes orbit an animated astrolabe among moonlit obelisks and lantern courts. Twenty-one residents include ten people walking the rings, each with a distinct appearance and carried object. Archon Seraphine, Ixo, Mora, Umi, Voss and Nim offer six quests, including two green and two blue level-60 gear rewards; the city has healing, provisions, fittings, sales and a Travel Master linked to Lamplight. The Meeting Stone hires companions beside the stairs to The Moonwell, a level-60 five-player dungeon with four separate party copies. Its open-water garden has six petal islands, glass causeways, three original ordinary creatures and four bosses: the Tideglass Heron, Hourpetal Stag, Moonskein Weaver and Moonwell Echo. Each boss drops a named material and guaranteed level-60 blue gear; defeating all four opens the return portal and completes Voss's quest. The Moonwell uses the Moonspore score rather than the Undervault music. The city plays the bombastic 112-BPM dybase2 score Seven Rings of Light (editable project 5e715b4a47a849bc).
To rebuild the zone, run python3 scripts/generate_asterion.py, then node scripts/sync-world.cjs, then bash scripts/cargo.sh build. python3 scripts/make_moonwell_garden_sprites.py preview | export | verify creates the six new individual 34-frame monster atlases; their style and asset manifest are in docs/moonwell-style.md. The original Echo art is still managed by python3 scripts/make_moonwell_sprites.py preview | build [--replace] | export | verify: build --replace discards editor changes; export preserves them. scripts/create_asterion_song.sh creates a new editable dybase2 project, and python3 scripts/finish_asterion_music.py /tmp/valhallasc-asterion-dybase2.wav makes its game loop. Adding these two file zones moves the old appended private dungeon copy IDs; inspect saved character zone values and migrate any characters inside an old copy before deploying. The client files are served directly and the public server requires a rebuilt binary for the map, quests, items and boss rules.
Optional owner login pushes go through Surv: the service sets VALHALLA_SURV_NOTIFY_URL (Surv's notify endpoint, https://gunning.se/surv/api.php?action=notify) and VALHALLA_SURV_NOTIFY_ADDR (127.0.0.1:443, which sends the request to the local Apache while the certificate is still checked for gunning.se). Surv pushes to Gotify with its own token, so the game holds no secret, and records each push in its alarm log as [🎮 Valhalla login] Name · Class · level N · Zone. Setting VALHALLA_GOTIFY_URL and VALHALLA_GOTIFY_TOKEN instead posts straight to a Gotify application. Leave all unset to disable pushes. See login notification rules. Local test worlds suppress pushes.
The Rust server owns movement speed, map collision, AI, melee range/facing, attack cooldowns, swept projectile hits, damage, critical hits, equipment stats, XP, levels, gold pickups, deaths, and respawns. Clients send actions, never character positions, damage results, or rewards. Slimes are shared: a kill happens once, and the killer receives XP and owns its gold and item drops. This is cooperative PvE; players cannot damage each other.
The simulation runs at 20 ticks per second and sends snapshots at 10 per second. Each connection receives only the players, enemies, projectiles and drops of the zone its character is in, so a second zone adds nothing to the other zone's bandwidth. Clients interpolate positions, draw the original layered sprites, and display everyone on the minimap. Text chat is world-wide. Commands have payload/rate limits, inactive directional input expires, slow connections are dropped, and WebSocket origins are checked. The server allows up to 128 simultaneous connections; this is a guardrail, not a tested MMO capacity claim.
Characters are stored in data/valhalla.sqlite with SQLite WAL transactions. Saves run every five seconds, on disconnect, and at graceful shutdown. Item pickups, equipment changes, stat training, vendor services and quest acceptance/claims also save immediately. The browser retains a server-issued guest character key. Only the key hash is stored in SQLite; the name is not a credential. Reloading or reconnecting resumes the same character, including position, class, appearance, equipment, inventory, HP, level, XP, gold, kill count, trained attributes, unspent stat points, and quests. Reconnect happens automatically after a network/server interruption.
Keep the browser's site data and the database. Clearing site data loses access to guest characters (a guest has no recovery and no cross-device login); accounts below do not have that limit. HTTP is appropriate for local development; put public deployments behind HTTPS so character keys travel over WSS. Different server URLs have separate character-key collections.
An abrupt process/machine failure can lose the last five seconds of progress. Slimes, projectiles, and uncollected drops are transient and reset when the server restarts. Persistent characters remain. Keep SQLite backups outside the public web root; use SQLite's backup command/API or stop the server before copying the database and its WAL files.
| Environment variable | Default | Purpose |
|---|---|---|
VALHALLA_BIND | 127.0.0.1:8080 | Listening address |
VALHALLA_DB | data/valhalla.sqlite | Character database path |
VALHALLA_CLIENT_DIR | client | Static client folder |
VALHALLA_ORIGIN | Same as request host | Exact allowed browser origin behind a reverse proxy, e.g. https://gunning.se |
VALHALLA_START_LEVEL | 1 | New characters begin at this level (1–100) so tests can drive learned skills and high-level wings; saved characters are untouched. For automated tests only: never set it on the public service. |
VALHALLA_GOD_MODE | unset | 1 makes players take no damage (enemies still fight, levels still roll). For automated tests only: the MCP driver's private worlds and the browser suites set it; never set it on the public service. |
VALHALLA_LEVEL_SPREAD | 2 | Each enemy's level is its kind's default ± this many levels (0 pins every enemy to its default, maximum 5). Browser suites that exercise controls rather than combat set it to 0. |
VALHALLA_TEST_COMMANDS | unset | 1 lets players send debug shortcut messages (set level, give items, teleport, finish quests, kill enemies...). For automated tests only: the MCP driver's private worlds set it; never set it on the public service, which otherwise answers every debug message with an error. |
RUST_LOG | valhalla_server=info | Logging filter |
GET /health reports status, protocol version, tick, online players, and connection capacity. /ws is the game connection. All other HTTP routes serve only client/, never server source or character data. Opening client/index.html directly or serving just static files does not start multiplayer.
Dockerfile builds the Rust server and packages the browser client. deploy/compose.yaml persists saves in a Docker volume:
docker compose -f deploy/compose.yaml up -d --build
This alternative Docker deployment binds port 8080 to loopback by default. It is separate from the installed host user service; stop the host service before using Docker with the same database. Adjust deploy/apache.conf to the Docker backend's reachable address if changing deployment method. The client derives its WebSocket URL from its page URL, so both root and subpath hosting work when the whole path is proxied with the prefix stripped.
The installed deployment uses deploy/valhalla.service under ~/.config/systemd/user/, and includes deploy/apache.conf inside the main HTTPS virtual host in /home/serveperry/webserver/000-default.conf and the php8site container. www.gunning.se/Valhallasc/ redirects to the canonical gunning.se origin. Apache and WebSocket configuration are scoped to this game. deploy/install-public.py performed the installation with a baseline check, backup, syntax validation, and graceful Apache reload; configuration backups live in gitignored deploy/backups/. Do not rerun that first-install script on an existing installation.
client/ Browser UI, renderer, sprites, music, connection/reconnect layer
server/src/ Rust HTTP/WebSocket transport, world simulation, SQLite storage
world/map.txt Shared collision geometry, slime spawns, city layout and NPC services
scripts/ Local Cargo wrapper and client map sync
tests/ Browser, test driver, and MCP integration tests
deploy/ Compose, Apache and systemd examples
world/map.txt is the source of truth for spawn/collision geometry, NPCs, and quest definitions. The top level is zone 0 (Greenmeadow with Alderhaven) and its zones array holds further zones with the same fields plus name, theme, levels, paths and portals; a portal's to, tx, ty name the destination zone index and arrival point. python3 scripts/generate_crags.py writes zone 1 and the meadow gates (seeded, with a flood-fill check that every spawn is reachable) and keeps zones 2 and up; python3 scripts/generate_rimeveil.py writes zone 2 (the spiral glacier and Rimeward Camp) and adds the gate at the north end of the Crags, moving the golem spawns and scenery in front of it (idempotent; run it again after a full Crags rebuild). Then node scripts/sync-world.cjs and a Rust rebuild apply it. After editing it, run node scripts/sync-world.cjs, then rebuild Rust (the map is bundled into the binary). Terrain shading and scenery drawing use the original renderer, with original city artwork in client/city.js. Alderhaven takes visual inspiration from Prontera references: cobblestone plazas, a fountain, timber-framed shops, gardens, and stone gates. Reference images are not shipped as game assets.
python3 scripts/expand_meadow.py idempotently fills Greenmeadow's previously empty east and south with branching trails, an eastern pond, a southeastern stone circle, scenery and low-level hunting spawns. It preserves Alderhaven, the player start, the Crags gate, quests and other zones; only its greenmeadow_wide_v1-tagged objects/spawns are regenerated. Run it after a full Crags regeneration, then node scripts/sync-world.cjs and rebuild Rust. node tests/meadow-layout.cjs checks full-map scenery coverage and routes from the player start to the new edges and gate.
python3 scripts/generate_prismwaste.py rebuilds file zone 14 and its Starbreak entrance in Astralhollow. It preserves the older zones, writes byte-stable JSON, and checks that every monster, NPC and gate is reachable. Run node scripts/sync-world.cjs and rebuild Rust afterward. python3 scripts/make_prism_sprites.py preview | build [--replace] | export | verify | contact owns the six transparent enemy atlases; build uploads editable PixelFlow sprites and export preserves editor edits. client/prismart.js owns the mirror desert's scenery. The private MCP scenario prismwaste checks both gates, drops, quests and restart; tests/prism-ui.cjs checks the live browser view.
python3 scripts/generate_orrery.py rebuilds file zone 15, the reciprocal Hourglass Pass, the Stillpoint hub and its quests. Run it after generate_prismwaste.py, then node scripts/sync-world.cjs and rebuild Rust. Both generators preserve the later zone and reapply its gate on rerun. The generator flood-checks all NPCs, enemies and gates; python3 scripts/make_orrery_sprites.py preview | build [--replace] | export | verify | contact owns its six 34-frame editable PixelFlow atlases. node scripts/test-via-mcp.cjs run orrery and REAL_SPRITES=1 node tests/orrery-ui.cjs exercise the private world. Adding the public zone shifts the first private Undervault copy from 15 to 16, so inspect saved zone IDs before a production deployment.
python3 scripts/generate_moonspore.py rebuilds file zone 16, the reciprocal Moonfall Arch, Lamplight Refuge and twelve quests. Run it after generate_orrery.py, then node scripts/sync-world.cjs and rebuild Rust. Both generators preserve the Moonspore gate on rerun. python3 scripts/make_moonspore_sprites.py preview | build [--replace] | export | verify | contact owns seven editable 34-frame PixelFlow sprites. node scripts/test-via-mcp.cjs run moonspore and REAL_SPRITES=1 node tests/moonspore-ui.cjs exercise the private world. The public-zone append shifts the first private Undervault copy from 16 to 17; saved zone IDs were inspected before the 2026-10-11 release.
The four Emberfall Crags monsters follow the same workflow with python3 scripts/make_crag_sprites.py preview | build [kind...] [--replace] | export: shaded ellipsoids and capsules drawn from parameters (never by hand), one 34-frame 96×96 PixelFlow sprite per kind (valhallasc_crag_<kind>_all, opened at /pixelflow/?sprite=<id> using the ids in client/assets/crags.txt), exported to client/assets/crags_<kind>.png with the same five clips as the Ironhide. build --replace discards editor hand edits; export preserves them. PixelFlow caps an owner at 200 saved sprites, so free slots before building more.
The four Rimeveil Glacier monsters use the same contract with python3 scripts/make_rime_sprites.py preview | build [kind...] [--replace] | export | verify: the Crags toolkit is imported from make_crag_sprites.py; Rime Crab, Frostfang Wolf, Glacier Yeti and Rime Wyrm are each one 34-frame 96×96 PixelFlow sprite (valhallasc_rime_<kind>_all, ids in client/assets/rime.txt), exported to client/assets/rime_<kind>.png. preview also reports any frame that touches the canvas edge, and verify checks the exported atlas against the rendered frames pixel for pixel. The raw frames are kept in scripts/rime_<kind>_raw.npz.
Skaldholm has no monsters, so no sprites: its buildings are the city objects of client/city.js (house, chapel, stall, fountain, lamp, bench...) plus the landmarks in client/cityart.js (rampart, tower, grandfountain, meetingstone, banner, statue, well, barrels), each registered with City.art[kind] = { box, draw, animate? }. Its ground is painted from the zone's roads list (rectangles, discs and rings of type 1 cobbles, 2 marble, 3 brick), and scripts/generate_skaldholm.py (content in scripts/skaldholm_content.py) builds the whole zone and the second glacier summit gate. A person with route, speed, pause and phase walks a closed loop of waypoints; a look (skin, hair, style, hat, apron, beard, scale) draws a townsperson from data.
The four Gloamfen monsters use the same contract with python3 scripts/make_fen_sprites.py preview | build [kind...] [--replace] | export | verify (toolkit imported from make_crag_sprites.py; one 34-frame 96×96 PixelFlow sprite per kind, valhallasc_fen_<kind>_all, ids in client/assets/fen.txt, raw frames in scripts/fen_<kind>_raw.npz): Fen Toad (a hopping toad with a tongue lunge that dies belly-up), Mire Crocodile (side-on, jaws and tail lash), Drowned Knight (a corroded part rig with a sword, a shield and a kelp plume that collapses into an armour pile) and Mire Hydra (three necks, each outlined as its own chain, striking in turn). preview refuses frames that touch the canvas edge; verify compares the exported atlas with the render pixel for pixel.
The seven Undervault monsters use the same contract with python3 scripts/make_vault_sprites.py preview | build [kind...] [--replace] | export | verify: the skeletons (thrall, archer, acolyte, Hrolf, Valka, Haldor) share one rig (two-bone IK limbs, ribcage, skull; per-kind gear and poses) and Ironwake is a granite construct; one 34-frame sprite each (valhallasc_vault_<kind>_all, ids in client/assets/vault.txt). The dungeon is scripts/undervault_layout.py (rooms, packs, bosses, doors) turned into a zone by scripts/generate_undervault.py (stone wall pieces round every room, pillars, braziers, torches, spawns; idempotent; a flood fill checks every spawn and door); the stairs and the Stone's speaker are written by generate_skaldholm.py. Server: world/instances.rs (copies), world/pursuit.rs (shared dungeon routes), world/ranged.rs (missiles), slams, boss loot and the level gate in world.rs; client: client/vaultart.js (walls, pillars, fire, the stairways, the exit portal), the darkness pass and boss bar in field.js. Tests: cargo test vault_tests (17, the three balance sims take a minute in debug), node scripts/test-via-mcp.cjs run undervault, node tests/undervault-ui.cjs. The focused instance_pursuit MCP scenario checks pursuit beyond both old leash limits and mercenary dismissal after final-boss loot is created.
The Wyrdwood is scripts/wyrd_layout.py (constants and geometry) + scripts/wyrd_content.py (people, buildings, quests) turned into zone 6 by scripts/generate_wyrdwood.py (idempotent; it plots with --plot out.png, finds the escort route with A* over the real obstacle grid, places the ambushers, and proves with flood fills that the river is crossed only at the bridge, the first ridge only at its east gap and the second only at its west gap, that every person, enemy and place is reachable and that the route is clear). Its map fields are new: camps (further sanctuaries beside city), places (what visit objectives name), bridges (the plank deck the client draws over the gap in the river), NPC escort (class, level, speed, route, ambush triggers) and spawn ambush (a monster that sleeps until the named escort passes). Server: world/escort.rs (an escort is a Player with merc set to its owner and an escort state: never saved, never online, never in a party; World::update_escorts walks it each tick; ambushers are dormant slots with hunt set when released so they chase without a leash), model::sync_visits, and the new kinds in world.rs/items.rs. Client: client/wyrdart.js registers every Wyrdwood prop with City.art (oaks and pines, ferns and heather, rune-carved standing stones, crag blocks, the Elder Ash, beacons, gallows, giant ribcages); field.js has the wyrd theme (leaf-litter and moor ground blended by latitude, the river and its bridge, the storm sky, leaves, rain and lightning), several hubs, the escort's nameplate and the hiding of her stranded copy. Monster art is python3 scripts/make_wyrd_sprites.py preview | build | export | verify (a shared four-legged rig for boar, ram and stag; a shared two-legged rig for the troll and the giant; ids in client/assets/wyrd.txt). Tests: cargo test wyrd_tests (22 plus the ignored wyrd_balance_probe), node scripts/test-via-mcp.cjs run wyrdwood (24 checks, about a minute) and run wyrd_quests (every ordinary quest played end to end, the group quests and the escort, about two and a half minutes), node tests/wyrdwood-ui.cjs (37 checks, REAL_SPRITES=1 for the art) and node tests/music-crags.cjs.
Bifrost Reach is scripts/sky_layout.py (islands as lumpy ellipses, axis-aligned bridges, places, ward wave plans, spawn bands) + scripts/sky_content.py (people, buildings, quests) turned into zone 7 by scripts/generate_bifrost.py (idempotent and byte-stable with the Wyrdwood and Skaldholm generators; --plot out.png draws it; it proves with flood fills that every person, enemy, place and ambusher is reachable and that closing each bridge cuts off exactly the expected islands). Its new map fields: sky (the floor rows), void objects (rectangles the client skips drawing) and places[].waves ({at, tag}: the progress mark in seconds and the dormant ambushers it wakes, spawns with ambush: "<place>#<n>"). The server side is server/src/world/hold.rs (update_holds, once a tick: counts whole seconds inside a place for each accepted, unfinished hold objective, releases waves with the escort's release_ambush, resets on a fall within 30 tiles, sleeps the waves on completion or after twelve seconds without a hero near) and server/src/world/sky_tests.rs. The Wyrdwood's half of the gate (the clearing, seven standing stones, the portal) is open_stormrift in generate_wyrdwood.py, applied after every rebuild. The five monsters are python3 scripts/make_sky_sprites.py preview | build | export | verify (the Wyrdwood quadruped and biped rigs are imported from make_wyrd_sprites.py; the ray and the roc are drawn on their own). The client draws the zone through client/skyart.js (props registered with City.art), skyGround/drawSkySky/drawWardRings in client/field.js (a tile with a void neighbour gets a cliff face, so the islands float) and client/music_sky.js. The dungeon copies of the Undervault are zones 9-11 now.
Ran's Deep is scripts/deep_layout.py (the seeded depth-first maze with a two-door chamber, wall rectangles of at most 4 tiles, depth quartiles for the spawn bands, the chime chains) + scripts/deep_content.py (people, buildings, quests) turned into zone 8 by scripts/generate_deep.py (idempotent; --plot out.png; it proves with flood fills and a Dijkstra on the real collision grid that everything is reachable, that closing the Net's door cuts off the whole maze, that closing both Court doors cuts off only the Kraken, that each bell chain can be rung by the slowest class in 40-78 % of its window, and that the elites keep clear of quest places). The Eyrie's half of the gate is open_maelstrom (post-processing; spawns near it move once). New data: places[].chain and places[].burn, zone bounds, paths. The server side is server/src/world/chime.rs (update_chimes, once a tick; bells are held per hero and not saved; the snapshot lists the hero's own bells: [{place, left}]) and server/src/world/deep_tests.rs (mutation-checked). The eight monsters are python3 scripts/make_deep_sprites.py preview | build | export | verify (biped and quadruped rigs from make_wyrd_sprites.py; angler, moray, siren and kraken are bespoke; the captain and the Ghostmaw are recoloured draugr and moray). The client draws the zone through client/deepart.js (coral walls, kelp, tidebells, the Maelstrom) and deepGround/drawSkyDeep/drawDeepWeather/drawChimeRings in client/field.js, and plays client/music_deep.js.
The Ironhide Beetle follows the local makesprites/PixelFlow workflow. Run python3 scripts/make_ironhide_sprites.py preview to review all five clips, build to create its editable sprite, and export to read edits back into client/assets/ironhide.png and ironhide.txt. build --replace discards beetle hand edits. The original indexed frames are preserved in scripts/ironhide_raw.npz. PixelFlow's saved-sprite quota requires one 34-frame editor sprite; editorStart in the metadata records each clip's frame range. Open the sprite ID from the metadata at /pixelflow/?sprite=<id>.
After the title screen the player chooses how to enter. Guest is the original flow (a server-issued key kept in this browser). Game account: username (3–30 letters/digits/underscores, unique without case) and password (8–128 characters with a letter and a digit), hashed with Argon2id in accounts. gunning.se: the browser's sso_session cookie is checked against the site SSO (VALHALLA_SSO_URL, default https://gunning.se/sso/kriswe-session.php, reached by loopback via VALHALLA_SSO_RESOLVE, default 127.0.0.1:443, with the certificate verified for gunning.se); the account is kind=sso and separate from any game account of the same name. Both account kinds keep up to 5 characters on the server; the browser stores only a 30-day sliding session key (hashed in sessions) and per-character UI layouts.
HTTP endpoints (all POST, JSON, same-origin only, Authorization: Bearer <session> where needed): /api/register, /api/login, /api/sso, /api/session (validate + list characters), /api/logout, /api/character/delete (refused while that character is in the world). Failed logins are limited to 5 per address per 5 minutes and 10 per account per 15; registrations to 5 per address per hour. An account joins the world with {"type":"join","version":1,"session":"<key>","character":"<id>"} or, to create one, "character":null,"look":{...}; an expired session gets a fatal error with "code":"auth".
Authentication rate-limit responses include retryAfter seconds and a matching
Retry-After header. The sign-in screen shows a countdown through reloads;
see docs/login-rate-limit.md.
On WebSocket connection, send {"type":"join","version":1,"token":null,"look":{"name":"Freya","class":"mage","gender":"female"}} (gender is male or female and defaults to male; any other value is refused). The welcome packet returns an ID, a guest key for a newly created character, and a world snapshot. For resume, send the guest key with look:null; saved state takes precedence. Keep keys out of URLs and logs.
Subsequent messages: input {dx,dy}, stop, move {x,y}, target {id}, attack {fx,fy}, dash {dx,dy}, skill {id,fx,fy}, equip {armor,weapon}, allocate_stat {stat}, use_item {item}, interact {npc,offer?}, chat {text}, social {command:{op,...}}, ping {nonce}, and (test servers only) debug {ref?,command:{op,...}}; see Test shortcuts below. Direction vectors are normalized on the server. move chooses a destination, not a teleport. Quest acceptance and reward collection use interact with offer:"quest:accept:<id>" or offer:"quest:claim:<id>"; quests with class reward choices require offer:"quest:claim:<id>:<item-id>". The server validates the giver, distance, living player, prerequisite completions, recorded objectives, and any selected reward. Player snapshots and dialogue packets include the character’s quests progress. Unknown command fields are refused. Server messages: welcome, snapshot, event, chat, system, pong, dialogue, debug (the reply to a test shortcut), and error. See server/src/model.rs for the exact types.
The transport uses Axum WebSockets and Tower HTTP static serving. Cargo.lock pins the resolved versions.
The test driver starts a real Rust server on a free loopback port with a fresh SQLite database under test-results/driver-*. Bots use the same /ws actions as browser players. Combat, movement, NPC services, and rewards are calculated by the server. The driver overrides inherited bind/database settings and has no option to attach to the public world.
Build the server and install the Node 20+ development dependencies first:
bash scripts/cargo.sh build --locked
npm ci
npm run test:driver
node scripts/test-driver.cjs list
node scripts/test-driver.cjs run movement
node scripts/test-driver.cjs run ironhide
node scripts/test-driver.cjs run city
node scripts/test-via-mcp.cjs run inventory
node scripts/test-via-mcp.cjs run quests
node scripts/test-via-mcp.cjs run crags_quests
node scripts/test-via-mcp.cjs run rimeveil
node scripts/test-via-mcp.cjs run rime_quests
node scripts/test-via-mcp.cjs run gloamfen
node scripts/test-via-mcp.cjs run fen_quests
node scripts/test-via-mcp.cjs run skaldholm
node scripts/test-via-mcp.cjs run skaldholm_quests
node scripts/test-via-mcp.cjs run quest_combat
npm run test:scenarios
npm run test:scenarios runs every scenario through a real MCP SDK connection and the run_scenario tool. Use node scripts/test-via-mcp.cjs --registered run all to verify the launch configuration installed in Codex. movement checks three classes, overlapping players, authoritative movement, dormant King slots, enemy spacing, and character resume. ironhide walks a mage to an actual beetle, defeats it, collects its gold, and checks saved progress. city walks into Alderhaven and checks distance restrictions and healer/armorer services. quests covers the town introduction, partial progress, NPC/range checks, reward replay protection, and restarting the actual Rust process. quest_combat earns the patrol, Ironhide hunt, and repeatable bounty through real combat, checks progression and level-up, accepts the King challenge, resets the bounty, and verifies saved history after restart. inventory checks real loot, ownership, equipment, protected sales and persistence. rimeveil walks through the Rimeveil Gate, checks the zone's 27 monsters and per-zone views, walks the whole ice spiral to the summit through all four ring gaps, fights a Rime Crab, uses both gates and checks persistence across reconnect and restart; rime_quests accepts, completes and claims all thirteen glacier quests through real NPC offers. stats earns a level through actual combat and quests, trains Intellect/Dexterity/Accuracy, rejects overspending, and verifies saved allocations after restarting Rust. bags earns 500 gold through quests and combat, buys a six-slot expansion bag at Linden, checks range and funds restrictions, and verifies capacity and ownership after restarting the private server. This scenario takes about eight minutes; use a request timeout of at least 600 seconds for direct MCP calls. The provided runner already requests that timeout. King Slime defeat and the randomized timer also have Rust tests; the MCP scenario does not wait 5–10 minutes for a king spawn. Every observed snapshot is checked for enemy spacing. Each scenario prints JSON with pass/fail checks and an artifact path; failures produce a nonzero exit status. Reports contain the last snapshot and up to 500 recent events. Test guest keys remain in process memory and never appear in reports or tool results.
On this host, Node is at /home/serveperry/.nvm/versions/node/v20.20.2/bin/node. Use that absolute executable for the direct node commands above, or add its directory to PATH for npm scripts. VALHALLA_BINARY can select another locally built server binary; bind, database, and client directory remain controlled by the driver.
On this host, the MCP is registered in ~/.codex/config.toml as valhallasc-testing, with a 20-second startup timeout and a 180-second tool timeout. Restart Codex after registration to load its tools (the list is read from the server, so new tools need no re-registration). To register this checkout or verify an installation again:
bash scripts/cargo.sh build --locked
python3 scripts/setup-test-mcp.py --verify
The setup command checks dependencies, backs up the existing Codex configuration, preserves other settings, registers absolute launch paths, and runs the private driver/MCP integration checks. It requires permission to write user-level Codex settings and open a private test port. Once connected, ask the agent to “Use valhallasc-testing to run the movement, Ironhide, and city scenarios.”
The MCP wrapper uses the official SDK and stdio. Configure your MCP client to launch an absolute Node executable with the absolute script path as its argument, for example:
{
"mcpServers": {
"valhallasc-testing": {
"command": "/home/serveperry/.nvm/versions/node/v20.20.2/bin/node",
"args": ["/home/serveperry/webserver/www/Valhallasc/scripts/test-mcp.cjs"]
}
}
}
This is a launch example; the enclosing settings format depends on your MCP client. Launch the script directly so stdout contains only MCP messages. The script resolves the repository from its own location and does not depend on the client's working directory.
| Tool | Purpose |
|---|---|
start_world / stop_world | Start/stop a private world; stopping saves an interactive report |
connect_bot / disconnect_bot | Create a class (and a male or female body) or resume a disconnected bot by name |
send_action | Send movement, input, stop, target, attack, dash, skill, equipment, NPC interaction, chat, or ping |
inspect_world | Inspect the latest snapshot or one bot, recent events, and spacing checks |
wait_world | Wait up to 30 seconds of real simulation time and inspect again |
list_scenarios / run_scenario | Discover/run a named scenario in a fresh world |
describe_world | Zones, NPCs and offers, portals, quests, items, skills and enemy kinds with default levels, so no world file needs reading |
set_level / give_xp / set_gold / set_health | Level a bot (1-100; 40 unlocks every skill) with the real level-up event, grant XP through the normal rules, set gold, set or restore hp or defeat the bot |
give_item / take_item / drop_item | Add items (bags are fitted, capacity is honoured unless force), remove spare copies (worn ones stay), or drop loot at the bot's feet |
teleport / walk_to / talk_to | Jump to {x,y,zone}, {npc}, {portal}, {enemy}, {spawn} or {bot}; walk there like a player along a collision-aware route; use an NPC offer, walking into range first |
quest | accept, complete, claim, finish (prerequisites, accept, complete and claim in one go) or reset any quest |
cast_skill | Cast a class skill, optionally at nearest/an enemy/a point, raising the level to unlock it and clearing cooldowns first; reports whether it went off, cooldown, buffs and which enemies lost hp |
For interactive exploration: start_world, connect_bot with {"bot":"Mage","class":"mage"}, then send_action with {"bot":"Mage","action":{"type":"move","x":45,"y":48}}, wait_world, and inspect_world. A movement destination is an ordinary server action. The driver supports at most 16 bot identities, validates action schemas, and serializes MCP calls. Stop the interactive world before running a scenario. Allow at least 180 seconds for scenario calls; walking and combat happen in real time. Normal disconnect saves characters, and reusing a bot name resumes the same character and class within that world.
Snapshots refresh while bots are connected; with no connected bots inspection retains the last observed snapshot. Closing MCP stdin or sending SIGINT/SIGTERM disconnects bots and stops the child server. Test databases and reports remain local and gitignored for review. The default npm test runs driver/MCP integration checks, all MCP scenarios, then the focused quest, inventory, equipment/skillbar and social Chromium UI passes. The social UI pass runs three browsers: it checks the panel's keys and focus, friend and party prompts, the party frame, party chat, and that a player's name containing markup is shown as text. The quest UI pass checks journal controls, focus, cards, markers, travel buttons, partial progress display, and loaded artwork. The inventory UI pass checks actual browser combat/pickup, equipment ownership, vendor selling and reload, plus isolated display fixtures for rare cross-class gear and blue repeatable markers. The equipment/bags/skillbar pass checks the nine slots, item tooltips, drag-to-equip and unequip, bag tabs, sorting, search, saved cell positions, authoritative skill cooldowns, keyboard and click activation, assignment and dragging, per-character layout persistence, chat input, and small viewport fit. The original three-player browser regression suite remains available as npm run test:browser:full when controls or rendering need broader coverage. No public service restart or deployment is needed for these local tools.
{"type":"social","command":{"op":...}} with refresh, who, friend_request {id|name}, friend_accept {id}, friend_decline {id}, friend_remove {id}, party_invite {id|name}, party_accept {id}, party_decline {id}, party_leave, party_kick {id}, party_promote {id} and party_chat {text}. An id is a character id from a list or an invite; a name must match exactly one other online player. The server answers every command with {"type":"notice","ok":true|false,"text"} and pushes {"type":"social","friends":[...],"incoming":[...],"outgoing":[...],"party":null|{leader,max,members:[...]}} whenever a list changes, {"type":"party","party":{...}} twice a second while in a party (member health), and {"type":"who","players":[...]} for the online list. Party chat is {"type":"chat","channel":"party",...}. The rules and limits live in server/src/world/social.rs; Character.friends (character ids) is the only persisted part. In the MCP tools, social runs one command as a bot and takes bot: "Other" in place of an id.
Private worlds start Rust with VALHALLA_TEST_COMMANDS=1, so a bot may send {"type":"debug","ref":1,"command":{"op":"set_level","level":20}}. The server answers {"type":"debug","ref":1,"ok":true,"tick":...,"result":{...}} or ok:false with an error; any server without the variable answers every debug message with ok:false and changes nothing. The shortcut skips only the time it would take to reach a state: rewards, level-up events and skill unlocks, quest rules, loot, bag capacity, saves and zone filtering still run in the server (server/src/world/debug.rs). Operations: set_level, give_xp, set_gold, give_mount (mount: horse, raptor, lizard, sabertooth or gryphon; adds it to the owned set), set_resource (amount), set_hp, die, reset_stats, reset_cooldowns, set_god_mode, set_weather (kind, optional intensity), teleport, give_item, take_item, drop_item, quest (accept, complete, claim, finish, reset), kill_enemy, respawn_enemy, spawn_enemy (kind, x, y, optional level: puts an enemy on the nearest free ground in your zone), spawn_chest (optional distance: puts a treasure chest that many tiles in front of you, or on a placed tile) and summon_king. TestWorld.debug(bot, command) sends one and resolves once the bot's own snapshot shows the effect. start_world accepts startLevel, godMode and levelSpread (0 pins enemy levels). Delayed effects (a meteor landing, projectiles in flight) need a longer settleMs on cast_skill. The shortcuts scenario exercises the lot against a real server.
The rules are in docs/gm-invisibility.md. The GM character is hidden from other players and ignored by monsters: /vis reports the current state, /vis on|off|toggle changes it, /show makes the GM visible and /hide hides them. The default is hidden on every login and every zone change; the state is not saved and carries into private dungeon copies. A hidden GM must show themselves before joining a duel or arena match. The character HUD displays a Hidden or Visible badge; the GM sees their own hidden character as a faint gold outline.
Outdoor zones share server weather from a schedule seeded by the persistent world clock epoch. The type, intensity and time left arrive in each zone snapshot; private dungeons and enclosed maps report clear indoor weather. Rain, snow, fog, thunderstorm, wind variants and sandstorms have canvas effects; Settings has separate persistent sliders for weather effects and weather sound. The HUD still shows the weather when drawing is off. Rules and zone eligibility: docs/weather.md, generated by python3 scripts/weather_zones.py --write into world/weather.txt and docs/weather-zones.txt. A GM may use /weather <clear|rain|snow|fog|thunderstorm|wind|sandstorm> [light|medium|heavy] or /weather resume; private test worlds may send the same set_weather debug op. Overrides are transient and limited to types permitted in the current outdoor zone. node tests/weather-ui.cjs runs the focused browser check.
Settings also offers English, Swedish and Korean. The choice lives in valhallasc.save.v1 and reloads the UI when changed; it never changes server IDs or character saves. Swedish catalog, UI and system-message strings are checked in as client/sv-data.js and sourced from scripts/sv-translations.txt and scripts/sv-template-translations.txt. After editing English catalog or UI text, run python3 scripts/build_swedish.py to update the translation and node tests/swedish-ui.cjs to check the switch in a private browser. The builder contacts a translation service; the browser does not.
cargo test --locked
cargo clippy --locked --all-targets -- -D warnings
cargo build --locked
npm ci
npx playwright install chromium
npm run check
npm test
The optional full browser regression suite (npm run test:browser:full) starts its own isolated Rust server/database and uses three independent browser clients. It covers character creation, visible remote players, movement replication, player pass-through, enemy spacing in server snapshots and rendered combat, equipment, safe chat, invalid commands/keys, duplicate sessions, slime and Ironhide combat, shared kills, XP pacing, loot, page reload, server restart/reconnect, and switching between saved characters, walking through the city gate, NPC dialogue, sanctuary healing, armorer fitting, and shop affordability. Screenshots and disposable databases are written under gitignored test-results/. The existing sibling Playwright installation was used for testing in this workspace via NODE_PATH; no runtime dependency on the sibling project is required.
node tests/public-smoke.cjs verifies the actual public HTTPS/WSS deployment with two browser clients. It also checks canonical redirects, private-file protection, chat, movement replication, and saved-character reload. It records only the test character IDs in test-results/public-smoke-results.json; remove those specific test characters from SQLite after both clients disconnect.
This browser game runs one authoritative Rust world with public map templates, private dungeon copies and a private arena. It supports guests, game accounts, gunning.se SSO, parties, quests, player trading, PvP and persistent characters. Guilds, distributed world servers and native client packages are future work; the 128-connection ceiling is a guardrail, not a measured MMO capacity. Each connection receives snapshots for its own zone or private copy. Movement follows direct goals with collision sliding; ordinary ground clicks do not find routes around complex obstacles, while city/NPC travel follows the shared collision grid. Browser validation here covers Linux Chromium.
Run python3 scripts/generate_nacrehold.py --plot test-results/nacrehold-plan.png, then node scripts/sync-world.cjs and bash scripts/cargo.sh build. The generator proves that every home door, stationary NPC, quest landmark and swimmer route is reachable; repeated runs are byte-identical. generate_deep.py preserves Nacrehold and reapplies the Tideway. client/nacreart.js registers the original shell architecture and merfolk drawing with the existing city compositor; NPC positions, shops, quests and movement remain server-owned. Rebuild the original music with python3 scripts/make_nacre_music.py (numpy and ffmpeg).
Focused checks: bash scripts/cargo.sh test nacre_tests, node scripts/test-via-mcp.cjs run nacrehold, node scripts/test-via-mcp.cjs run nacre_quests and node tests/nacrehold-ui.cjs. These launch disposable private servers and databases.
The Cathedral adds file zones 10–12 and moves the Undervault’s additional copies to zones 13–15. Before any future deployment, back up the live executable and SQLite, check for characters saved in the prior Undervault copies (10–12), and move those characters to the Skaldholm stairs (zone 4, x 104.2, y 125.4) while nobody is online. Client files in this checkout are served directly and become visible before a server deployment.
Run python3 scripts/generate_cathedral.py --plot test-results/cathedral-plan.png, then node scripts/sync-world.cjs and bash scripts/cargo.sh build. The seeded generator preserves older zones, checks every spawn and exit against collision, and writes the three wings plus world/cathedral.txt. scripts/cathedral_content.py owns their names, levels, encounters and stats; Rust reads the generated catalog in server/src/cathedral.rs, and the client reads generated client/cathedral.js. Rebuilding Nacrehold reapplies the three entrances. Public templates are zones 10/11/12; snapshots use those numbers for every private copy. All private copies, including the Undervault, bring the internal map count to 25.
Enemy artwork uses python3 scripts/make_cathedral_sprites.py preview | build | export | verify: seventeen 34-frame PixelFlow sprites, original indexed art preserved, editor IDs in client/assets/cathedral.txt. export preserves editor changes; build --replace explicitly discards only these sprites' changes. Architecture is client/cathedralart.js. Regenerate the three original music loops with python3 scripts/make_cathedral_music.py (numpy and ffmpeg).
Focused checks: bash scripts/cargo.sh test cathedral, bash scripts/cargo.sh test --release cathedral_balance_probe -- --ignored --nocapture, node scripts/test-via-mcp.cjs run cathedral, and REAL_SPRITES=1 node tests/cathedral-ui.cjs. The balance probe stages actual combat with hired mercenaries and no god mode; browser/MCP worlds use separate ports and databases. Dungeon pursuit grids rasterise expanded obstacles instead of scanning every object for every cell, preventing a first-aggro pause in the larger nave. Driver inspection reads each bot's own snapshot when different private copies share a public zone.
python3 scripts/gear_levels.py builds the equipment catalog, followed by node scripts/sync-world.cjs and a Rust rebuild. Required levels are 1 and every five levels through 55. Each step has common, uncommon and rare gear; epic sets occur at levels 10, 20, 30, 40 and 50. Every set has chest, headgear, shoulders, gloves and a weapon for each of the five classes, plus shared pants, necklaces and rings. The 41 ordinary sets continue the existing stat and sale-price curve. Level-55 pieces use the level-50 silhouettes and icons with their own tints until a dedicated art pass. Existing item IDs, artwork, GM gear and consumables are preserved.
Each blue item type has one fixed attribute bonus; each purple item type has two distinct fixed bonuses. Each bonus follows the item's required-level ladder: +1 at levels 1–5, +3 at 10, +5 at 15, +7 at 20, +10 at 25, +15 at 30, +20 at 35, +30 at 40, +35 at 45 and +40 at 50. Future five-level steps interpolate from +40 at 50 to +100 at 90. The item ID determines which attributes it gets, so another copy of that item has identical stats and stacks normally. Class gear only uses attributes useful to that class; shared gear uses universally useful attributes. Amounts above Accuracy's 20-point or Dexterity's 70-point effective cap are assigned to other attributes instead. Bonuses apply while wearable gear is equipped, appear in tooltips and the character window, and never spend trained stat points. The existing 17.5% per-tier attack/defense scaling remains unchanged.
The Cathedral adds 28 exclusive rare pieces per wing: level 40 Cloister, level 45 Reliquary and level 50 Nave. They have their own names and colors, with 15% more attack/defense than the same-level ordinary blue gear. lootSource in the encounter catalog ties each boss to its wing's source in the item catalog. First bosses guarantee headgear/shoulders, second bosses gloves/pants/necklaces, third bosses weapons/rings; finales guarantee a chest and one other piece. All rewards use the normal party rolls, collection, level/class requirements and persistence. Ordinary enemy drop rolls cannot award source-specific gear. Undervault rewards remain level 20.
Ordinary level-25–60 gear is in every eligible enemy's level-capped drop pool (an enemy can roll gear through the next five-level step); level-50–54 enemies draw ordinary gear from levels 50 and 55, while level-55+ enemies draw from levels 55 and 60. Outdoor elites independently roll one guaranteed green piece, a blue piece at 33%, and a purple piece at 5% per kill; their previous gray and legendary odds remain. Instance enemies keep their previous ordinary drop table, while the Undervault and Cathedral final bosses each guarantee at least two exclusive blue pieces. Every non-instance quest hub has at least two one-time green gear rewards and a harder blue gear reward, including both Wyrdwood hubs. scripts/quest_gear_rewards.py owns the new assignments and can apply them to world/map.txt with --apply; the zone generators use the same table.
Checks: python3 tests/gear-redesign-integrity.py, npm run test:gear-generator, bash scripts/cargo.sh test items::tests, node tests/inventory-ui.cjs, node tests/sprites-real.cjs --redesign-only warrior, and the cathedral, inventory, bags and undervault MCP scenarios. The art suite saves both-body attack/death contact sheets under test-results/gear-25-50/.
python3 scripts/generate_stormglass.py creates file zone 19, a level 60–65 storm coast reached from Asterion's eastern causeway. Breakwater Camp has a bell court, coastal shelters, a healer, provisions, quests and reciprocal Spark Travel. Five original monsters roam the shore; the Maelstrom Heart is a five-player outdoor elite. The Eye Below has a Meeting Stone and a visible sealed entrance. It has no dungeon portal or instance map yet.
Run python3 scripts/generate_asterion.py, python3 scripts/generate_stormglass.py, then python3 scripts/generate_sunward.py to rebuild the full coast progression. Sunward is file zone 20, reached by the reciprocal Sunward Cut on Stormglass's eastern rim. Green meadow, a living river, regular oaks and birches, apple rows and two bridge routes lead from Millbridge to the sealed Elder Hollow beneath a giant split oak. The village has supplies, healing, quests and Travel Master Jori, linked to Breakwater. Eight NPCs and nine quests span the zone. The Elder Hollow is a visible future five-player entrance and survey objective; it has no dungeon portal or instance map yet.
Four ordinary kinds (Meadow Hare 65, Sunback Snail 66, Brush Fox 67 and Spadeclaw Badger 68) and two outdoor elites (Old Honeyjaw 69, three players; Wicker Warden 70, five players) have distinct materials. python3 scripts/gear_levels.py extends ordinary gear through level 70; the two elite quests award level-70 blue gear. After map or item generation run node scripts/sync-world.cjs and rebuild Rust. The generator is seeded and byte-stable, and Stormglass's generator preserves the Sunward gate when rerun.
PixelFlow owns nine editable 34-frame atlases, including Ada Bell, Rowan Alder and Travel Master Jori: python3 scripts/make_sunward_sprites.py preview | build | export | verify. build refuses existing sprites; export preserves editor edits. IDs are in client/assets/sunward.txt, indexed source frames in scripts/sunward_*_raw.npz. client/sunwardart.js paints the trees, village, bridges and giant oak entrance. The editable dybase2 project for Along the Millstream (G major, 104 BPM, 32 bars) is e59a59732cb42ace; sh scripts/create_sunward_song.sh creates a new project and python3 scripts/finish_sunward_music.py makes its seamless game loop. Use node scripts/test-via-mcp.cjs run sunward and REAL_SPRITES=1 node tests/sunward-ui.cjs for private validation.
Adding file zone 20 shifts every appended private copy id up one (the first extra Undervault copy becomes 21). Inspect saved character zone ids before any deployment and migrate affected private-copy saves to a safe public arrival while the service is stopped. Client files in this checkout are served directly before a server restart; the running Rust binary does not know the new map until rebuilt and deployed.
Run python3 scripts/generate_asterion.py, python3 scripts/generate_stormglass.py, python3 scripts/gear_levels.py, node scripts/sync-world.cjs, then bash scripts/cargo.sh build after changing the generators or catalog. Both zone generators preserve later zones and reapply the connecting gate. Run python3 scripts/make_stormglass_sprites.py with the preview, build, export and verify subcommands to create and round-trip five editable PixelFlow sprites; build refuses to replace them. The original indexed frames and PixelFlow IDs stay in scripts/stormglass_*_raw.npz and client/assets/stormglass.txt.
Private checks: bash scripts/cargo.sh test stormglass_tests, node scripts/test-via-mcp.cjs run stormglass, node tests/stormglass-ui.cjs, and REAL_SPRITES=1 node tests/stormglass-ui.cjs. This added public zone moves the first additional Undervault copy from zone 19 to 20; inspect saved character zones before later zone deployments. The 2026-10-09 release needed no save migration: all 18 characters were in zones 0, 1, 2 or 17. Backup deploy/backups/stormglass-20261009T211141Z contains the previous running executable, consistent live and stopped SQLite copies and the new release binary. Public/internal health, SQLite quick check, all twelve public client hashes and the running/release executable matched after the zero-player restart. Client files in this checkout are served directly, so keep future server and client rollouts close together.
python3 scripts/generate_ribbonwild.py adds file zone 21, a 136×136 four-color pinwheel of grass courts and radial parade roads. The reciprocal Parade Arch connects Sunward Vale. Jubilee Crossroads has eight NPCs, nine quests, services, a Meeting Stone and Travel Master Luma linked to Millbridge. Four ordinary kinds occupy the courts; the Parade Gryphon (three players) and Masquerade Colossus (five players) are outdoor elites with named materials and level-75 blue quest rewards. The immense striped Thousandfold Pavilion is now an open private instance entrance and survey objective, with a level-75 gate. Run python3 scripts/gear_levels.py, node scripts/sync-world.cjs and rebuild Rust after generation. The Sunward generator preserves and reapplies the Parade Arch on rerun.
Nine editable 34-frame PixelFlow sprites are managed by python3 scripts/make_ribbonwild_sprites.py preview | build | export | verify; editor IDs are in client/assets/ribbonwild.txt and indexed originals in scripts/ribbonwild_*_raw.npz. client/ribbonwildart.js paints the colorful courts, pinwheels, stalls and monumental Pavilion. dybase2 project 702081f8ef8eb02a is March of Many Colors (D major, 118 BPM, 32 bars); scripts/create_ribbonwild_song.sh creates a new editable composition and scripts/finish_ribbonwild_music.py turns its render into a seamless game loop. Run bash scripts/cargo.sh test ribbonwild, node scripts/test-via-mcp.cjs run ribbonwild and REAL_SPRITES=1 node tests/ribbonwild-ui.cjs for private validation. The new public zone shifts all appended private copies up one; saved zone IDs were inspected before the 2026-10-11 release. Deployed with the 2026-10-11 Yearwheel server release.
python3 scripts/render_ribbonwild_gallery.py <live-hub-capture> <live-pavilion-capture> makes the zone overview and monster sheet. The overview, monster sheet and full 65-second MP3 are in Sork's FileDrop (/filedrop/) as Valhalla_Ribbonwild_Zone_Overview.png, Valhalla_Ribbonwild_Monsters.png and Valhalla_Ribbonwild_March_of_Many_Colors.mp3.
python3 scripts/generate_tanglecrown.py adds file zone 22, a 144×144 rain-bright jungle reached from Ribbonwild's reciprocal Rootway. Braided waterways and four reconnecting trail loops wind through giant kapok trees, strangler figs, ferns and orchids. Banyan Landing has eight NPCs, twelve quests, supplies, healing, a Meeting Stone and Travel Master Suri linked to Jubilee Crossroads. Four ordinary creatures inhabit the canopy and basin; Jade Anaconda (three players) and Crown Jaguar (five players) are outdoor elites with named materials and level-80 blue quest rewards. The enormous Temple of the Rain Crown is an open level-80 private instance entrance and survey objective; its public portal leads to zone 32. The Ribbonwild generator preserves and reapplies the Rootway on rerun. After map/catalog generation, run python3 scripts/gear_levels.py, node scripts/sync-world.cjs and rebuild Rust.
Nine editable 34-frame PixelFlow atlases (six creatures and three NPCs) are managed by python3 scripts/make_tanglecrown_sprites.py preview | build | export | verify. Editor IDs are in client/assets/tanglecrown.txt; indexed originals are in scripts/tanglecrown_*_raw.npz. client/tanglecrownart.js draws the jungle structures and temple. The editable dybase2 song Beneath the Rain Crown (A minor, 104 BPM, 32 bars) is project f7cc1c7e9bc03c07; scripts/create_tanglecrown_song.sh creates the score, scripts/render_tanglecrown_song.sh renders it and scripts/finish_tanglecrown_music.py makes the game loop. Private focused checks: bash scripts/cargo.sh test tanglecrown, node scripts/test-via-mcp.cjs run tanglecrown, and REAL_SPRITES=1 node tests/tanglecrown-ui.cjs. scripts/render_tanglecrown_gallery.py builds the zone overview and creature sheet from real captures. The new public zone shifts appended private-copy IDs up one; saved character zone IDs were inspected before the 2026-10-11 release. Deployed with the 2026-10-11 Yearwheel server release.
Sork's FileDrop holds Valhalla_Tanglecrown_Zone_Overview.png, Valhalla_Tanglecrown_Monsters.png and Valhalla_Tanglecrown_Beneath_the_Rain_Crown.mp3.
python3 scripts/generate_worldheart.py adds two connected public maps: file zone 23 Veyrroot Canopy, an open city of circular branch decks, root bridges, hanging homes and markets around one enormous jungle tree; and file zone 24 Veyrroot Heartwood, the tree's inhabited hollow with amber archives, a living pith and chambers cut into the inner rings. The Living Root joins the Tanglecrown to the canopy, and the Hollow Arch joins the canopy to the heartwood. Both transitions are reciprocal and have safe arrival points. The immense living iris of The Seed Vault opens into a level-80 five-player private instance at zone 33. The Tanglecrown generator preserves and reapplies the Living Root on rerun.
The canopy quest hub has seven quests, services, a Meeting Stone and Travel Master Lio linked to Banyan Landing; the heartwood has two civic quests and four resident NPCs. Both city maps have zero enemy spawns. Sap Beetles, Lantern Hornets, Moss Lemurs and Bark Mimics have editable sprites, stats and materials reserved for the future Seed Vault instance; city quests use visits and conversations, with three level-80 gear rewards. Run node scripts/sync-world.cjs and rebuild Rust after changing either map. scripts/make_worldheart_sprites.py preview | build | export | verify manages eight editable 34-frame PixelFlow atlases: four creatures, Iria Sunbark, Oli Amberleaf, Veya of the Pith and Travel Master Lio. IDs are in client/assets/worldheart.txt, indexed originals in scripts/worldheart_*_raw.npz. client/worldheartart.js paints the exterior tree and the interior chambers.
The editable dybase2 project for Rooms in the Canopy (F major, 92 BPM, 32 bars) is f817d3fb5b57cbb8. scripts/create_worldheart_song.sh creates a new score, scripts/render_worldheart_song.sh PROJECT_ID renders it and scripts/finish_worldheart_music.py makes the seamless game MP3. Focused private checks: bash scripts/cargo.sh test worldheart, node scripts/test-via-mcp.cjs run worldheart and REAL_SPRITES=1 node tests/worldheart-ui.cjs. scripts/render_worldheart_gallery.py <browser-artifacts-dir> makes the outside/inside and NPC share images from real game captures. The two new public zones shift appended private-copy IDs up two; persisted character zones were inspected before the 2026-10-11 release. Deployed with the 2026-10-11 Yearwheel server release.
The peaceful-city revision passed two focused Rust tests, 15 private MCP scenario checks, quest reward catalog and JavaScript syntax; the original visual release passed 16 Chromium checks with real sprites. The world generator is byte stable. The city panorama, NPC portrait sheet and full MP3 were uploaded to Sork's FileDrop as Valhalla_Veyrroot_City_Outside_Inside.png, Valhalla_Veyrroot_NPCs.png and Valhalla_Veyrroot_Rooms_in_the_Canopy.mp3; container copies match local SHA-256 hashes.
python3 scripts/generate_lapis_erg.py adds file zone 25, a 160×160 ochre desert with three turquoise oases, date palms, blue salt ridges and looping caravan routes. The reciprocal Sunroad joins peaceful Veyrroot Canopy to the southern Azure Wells camp; generate_worldheart.py preserves the desert and reapplies the canopy gate on rerun. Azure Wells has nine quests, healing, trade, mercenaries, a Meeting Stone and Travel Master Nadir linked to Veyrroot. The giant rotating brass lens of The Sunken Astrolabe marks a sealed future five-player instance entrance and survey objective; no dungeon map or playable portal exists yet. Veyrroot's two city maps remain free of enemy spawns. After map or catalog changes, run python3 scripts/gear_levels.py, node scripts/sync-world.cjs and rebuild Rust.
Sandhopper (80), Dune Fennec (81), Glass Scorpion (82) and Sun Vulture (83) are ordinary enemies; Dune Colossus (84, three players) and Mirage Roc (85, five players) are outdoor elites. All six have distinct materials; elite quests award fixed level-85 blue gear. Eleven editable 34-frame PixelFlow atlases cover the six creatures and five named NPCs. Run python3 scripts/make_lapis_sprites.py preview | build | export | verify; build refuses existing names unless --replace is specified, while export preserves editor changes. Sprite IDs are in client/assets/lapis.txt, indexed originals in scripts/lapis_*_raw.npz. client/lapisart.js paints the wells, palms, caravan tents and astrolabe.
The editable dybase2 project Wells of Blue Glass is b1a0de52aae84cfb (D minor, 96 BPM, 32 bars). scripts/create_lapis_song.sh creates a fresh score, scripts/render_lapis_song.sh renders the saved project, and scripts/finish_lapis_music.py creates the seamless game MP3. Focused private checks: bash scripts/cargo.sh test lapis, node scripts/test-via-mcp.cjs run lapis, and REAL_SPRITES=1 node tests/lapis-ui.cjs. python3 scripts/render_lapis_gallery.py <browser-artifacts-dir> creates the share images from real game captures. This public zone moves appended private-copy IDs up one; saved character zones were inspected before the 2026-10-11 release. Deployed with the 2026-10-11 Yearwheel server release; client assets are served directly from this checkout.
Sork's FileDrop contains Valhalla_Lapis_Erg_Zone_Overview.png, Valhalla_Lapis_Erg_Monsters.png and Valhalla_Lapis_Erg_Wells_of_Blue_Glass.mp3; the container copies match local SHA-256 hashes. Focused validation passed two Rust tests, 17 private MCP scenario checks, 14 real-sprite Chromium checks, gear/quest generator checks, Rust formatting, JavaScript syntax and map/client byte stability. Broad suites were left unrun to conserve the requested test budget.
python3 scripts/generate_frostwake.py adds file zone 26, a 160×160 snow shelf with branching ice roads, two long blue fractures, frozen pools, firs and an aurora obelisk. The reciprocal Snowline connects the Lapis Erg's northern rim to the southern Frostwake Station camp. The hub has ten quests, healing, trade, mercenaries, a Meeting Stone and Travel Master Eira linked to Azure Wells. The map has four ordinary kinds—Snow Hare (85), Ice Otter (86), Frost Heron (87) and Glacier Crab (88)—and three separate outdoor elites: Bell Mammoth (88, three players), Aurora Manta (89, four players) and Crevasse Leviathan (90, five players). Each drops its own material; five quests grant fixed gear, including level-90 blue elite rewards. generate_lapis_erg.py preserves the new zone and reapplies its Snowline gate. Rebuild gear with python3 scripts/gear_levels.py, then run node scripts/sync-world.cjs and rebuild Rust after world edits.
python3 scripts/make_frostwake_sprites.py preview | build | export | verify manages thirteen editable 34-frame PixelFlow atlases: seven creatures and six named residents, including Eira. Editor IDs are in client/assets/frostwake.txt; indexed originals are in scripts/frostwake_*_raw.npz. client/frostwakeart.js draws ice fissures, snow pines, sled tents and the obelisk. The dybase2 score Lanterns in the Blue Ice is project 75951aa438eab92b (G minor, 88 BPM, 32 bars); scripts/create_frostwake_song.sh, scripts/render_frostwake_song.sh and scripts/finish_frostwake_music.py produce the seamless game loop. python3 scripts/render_frostwake_gallery.py <browser-artifacts-dir> makes the share images from real captures.
Focused private checks: bash scripts/cargo.sh test frostwake, node scripts/test-via-mcp.cjs run frostwake, REAL_SPRITES=1 node tests/frostwake-ui.cjs, gear/quest generator checks, sprite export verification, JavaScript syntax and generator byte stability. These passed with two Rust tests, 17 MCP checks and 13 real-sprite Chromium checks. Broad suites were left unrun to conserve the requested test budget. Adding this public zone shifts appended private-copy IDs up one; saved character zones were inspected before the 2026-10-11 release. Deployed with the 2026-10-11 Yearwheel server release. FileDrop contains Valhalla_Frostwake_Zone_Overview.png, Valhalla_Frostwake_Monsters.png and Valhalla_Frostwake_Lanterns_in_the_Blue_Ice.mp3.
python3 scripts/generate_cairnspine.py adds file zone 27, a 168×168 slate mountain crossed by four connected switchbacks and short cliff cuts. Boulderwatch has ten quests, healing, trade, mercenary support, a Meeting Stone and Travel Master Asta, linked to Frostwake through the reciprocal High Pass. Giant boulders and leafless white trees flank a singing cairn, a bare grove and the sealed Thunderheart Gate. The gate is an instance landmark and survey objective; its reserved private floor is sealed and has no inbound portal. The Frostwake generator preserves the later zone and reapplies the High Pass gate. Run python3 scripts/gear_levels.py, node scripts/sync-world.cjs and rebuild Rust after world edits.
Scree Ferret (90), Stone Ibex (91), Slate Raven (92) and Ridge Adder (93) are ordinary enemies. Cairn Behemoth (93, three players), Whitebark Titan (94, four players) and Storm Condor (95, five players) are outdoor elites with separate group quests. All seven have named material drops; five quests grant fixed gear, including level-95 blue elite rewards. python3 scripts/make_cairn_sprites.py preview | build | export | verify manages thirteen editable 34-frame PixelFlow atlases (seven monsters and six residents). Sprite IDs are in client/assets/cairn.txt, indexed sources in scripts/cairn_*_raw.npz; client/cairnart.js draws the mountain scenery and monumental gate.
The dybase2 project The High Pass Rings is 6132f99b6de18dce (E minor, 108 BPM, 32 bars). scripts/create_cairn_song.sh, scripts/render_cairn_song.sh PROJECT_ID and scripts/finish_cairn_music.py produce the seamless MP3. python3 scripts/render_cairn_gallery.py <browser-artifacts-dir> creates the zone and monster pictures from real Chromium captures. Focused private checks: bash scripts/cargo.sh test cairnspine, node scripts/test-via-mcp.cjs run cairn, REAL_SPRITES=1 node tests/cairn-ui.cjs, sprite export verification and generator byte stability. Adding this public zone shifts appended private dungeon copy IDs up one; saved character zone IDs were inspected before the 2026-10-11 release. Deployed with the 2026-10-11 Yearwheel server release.
Sork's FileDrop contains Valhalla_Cairnspine_Zone_Overview.png, Valhalla_Cairnspine_Monsters.png and Valhalla_Cairnspine_The_High_Pass_Rings.mp3; their container copies match the local SHA-256 hashes. The focused checks passed with two Rust tests, 17 private MCP scenario checks, 13 stub and 13 real-sprite Chromium checks, gear/quest generator checks, Rust formatting, JavaScript syntax, atlas edge/export verification and byte-stable zone and parent generators. Broad suites were left unrun to conserve the requested test budget.
python3 scripts/generate_yearwheel.py adds file zone 28, a 400×400 end zone, more than four times the area of any earlier zone. Springmere's blossoming groves, Solhaven's sunlit orchards, Harvestwatch's copper forest and Rimehold's snow birches circle the Equinox Crossing. Each quarter has its own quest hub, seven named residents, six outdoor quests and a distinct Travel Master on a Spark Travel ring. Springmere has two additional dungeon quests. A reciprocal Year's Ascent pass joins Boulderwatch in the Cairnspine. The four painted thresholds—Blooming Labyrinth, Solar Crucible, Amber Archive and Silent Solstice—are sealed landmarks and quest objectives. Their reserved private maps have no active public entrance; Blooming now has staged encounters and quests.
Four ordinary creatures and twelve outdoor elites have separate models, levels 95–99, attack profiles and material drops. scripts/make_yearwheel_sprites.py preview | build | export | verify manages 24 editable, 34-frame PixelFlow atlases: 16 creatures, four keepers and four travel masters. IDs are in client/assets/yearwheel.txt; indexed sources are scripts/yearwheel_*_raw.npz. client/yearwheelart.js draws the seasonal scenery and four different large gates. The current dybase2 MCP project c7ab959e2d451253, Four Horizons, is a 64-bar G-major score at 100 BPM, about 154 seconds long. Sork-60 Mellow Pad and bass carry the quieter passages; horn motifs, Juno Strings and sparse low drums build the peaks. scripts/create_yearwheel_four_horizons.sh, scripts/repair_yearwheel_pad.sh, scripts/render_yearwheel_song.sh PROJECT_ID and scripts/finish_yearwheel_music.py reproduce the client loop. The first 32-bar score, The Turning Sky, remains in dybase2 as project 310d37a0998293f5 and in the original FileDrop audio. scripts/render_yearwheel_gallery.py <browser-artifacts-dir> makes two share images from private real-game captures and the PixelFlow cast.
After editing map or catalog data run python3 scripts/generate_yearwheel.py, then node scripts/sync-world.cjs and rebuild Rust. The Cairnspine parent generator preserves the Yearwheel and reapplies its reciprocal pass. Focused private checks: bash scripts/cargo.sh test yearwheel, node scripts/test-via-mcp.cjs run yearwheel, and REAL_SPRITES=1 node tests/yearwheel-ui.cjs. This public zone shifts the IDs of appended private Undervault and Moonwell copies up one; saved character zones were inspected before the 2026-10-11 release. Deployed with the 2026-10-11 Yearwheel server release.
Sork's FileDrop contains Valhalla_Yearwheel_Zone_Overview.png, Valhalla_Yearwheel_Monsters.png, the current Valhalla_Yearwheel_Four_Horizons.mp3 and the archived Valhalla_Yearwheel_The_Turning_Sky.mp3. Local and container SHA-256 hashes matched. Focused validation passed with two Rust tests, 43 MCP scenario checks, 16 real-sprite Chromium checks, byte-stable Yearwheel and Cairnspine generators, Rust formatting, JavaScript syntax and PixelFlow export verification. Broad suites were left unrun at the user's request to limit testing.
2026-10-11 deployment: The release server was rebuilt with the Yearwheel and the previously private zones, then started on valhalla.service. Backup: deploy/backups/yearwheel-20261011T115102Z (live binary and SQLite snapshots before build and after graceful stop). The sole saved character in old private-copy zone 28, [GM]Sork, was moved to the Yearwheel arrival at (24,359) before restart. The public health check passed, 13 representative client file hashes matched, and /proc/<MainPID>/exe matched the new release binary. No public test guests were created.
Creature art correction: python3 scripts/make_yearwheel_sprites.py repair redraws the 16 existing creature sprites in PixelFlow and exports the full cast. Their feet now meet the (48,90) ground anchor in idle, walk, attack and hurt frames, and four-legged bodies have attached support limbs. The Yearwheel atlas URL has a new cache version. The revised monster sheet replaced the earlier FileDrop image under the same name. A targeted frame connectivity check, real-sprite private browser capture, public atlas hash and client JavaScript syntax check passed; no broad suite was rerun.
The follow-up art pass refined eight creatures whose repeated triangle spikes or stiff horns read poorly at game scale. Sundarter and Longnight Wyrm now have connected back sails; Glass Lion has a curled mane; Snow Giant has swept horns; Petal Chimera, Rime Saber and Amber Mantis have branched growths or antennae; Lantern Owl has shorter feather tufts. Pass specific creature names after repair to update only those PixelFlow sprites.
Music revision: Four Horizons replaces the shorter, bright Turning Sky loop in the live Yearwheel client. Its 64 bars run for 153.6 seconds and leave the horns and low drum out of the quieter stretches. After the pad issue was reported, the drifting OB-Xd Eight Voice Strings and untried Sork-60 Swelling Choir were replaced in the saved project with the wiki-proven Sork-60 Mellow Pad and Juno Strings. The finished MP3 is high-pass filtered at 25 Hz to remove the pad's DC offset and measures about −24.3 LUFS integrated and −13.0 dB true peak, with no clipping; decoded length is 153.6 seconds and the loop boundary is below 0.001 sample amplitude. The live loader uses four-horizons-2; local, public asset and FileDrop storage hashes match. The FileDrop download endpoint requires authentication. No server restart or broad test suite was needed for this client asset change.
python3 scripts/generate_sealed_dungeon_maps.py owns eleven private, four-copy floor plans at file zones 29–39. The Eye Below (29), Elder Hollow (30) and Thousandfold Pavilion (31) are open. Rain Crown Temple, Seed Vault, Sunken Astrolabe, Thunderheart Gate, Blooming Labyrinth, Solar Crucible, Amber Archive and Silent Solstice (32–39) retain sealed entrances. Each has connected halls, solid rock collision, a safe arrival and a return portal to its outdoor landmark. sealedTemplate prevents inbound portal use and hides unfinished maps on the world sheet. Run the generator after the outdoor zone generators, then python3 scripts/late_quests.py, node scripts/sync-world.cjs and rebuild Rust. Appended private-copy IDs shift; inspect saved zone IDs before any deployment. This dungeon work has not been deployed.
python3 scripts/generate_sealed_dungeon_maps.py --previews test-results/sealed-dungeons now makes eleven labeled PNG plans. The initial five sealed-plan snapshots were uploaded to Sork's FileDrop as Valhalla_Eye_Below_Sealed_Map.png, Valhalla_Elder_Hollow_Sealed_Map.png, Valhalla_Thousandfold_Pavilion_Sealed_Map.png, Valhalla_Temple_of_the_Rain_Crown_Sealed_Map.png and Valhalla_Seed_Vault_Sealed_Map.png; local and FileDrop hashes matched. The current Elder open-map and Silent Solstice sealed-map previews were uploaded separately. Focused checks passed: two Rust sealed-dungeon tests, the existing portal-arrival Rust test, 16 private Chromium checks including five real return journeys, generator byte stability, Rust formatting and JavaScript syntax. The complete test suite was not run.
Five real-sprite Chromium captures from inside the walkable floors were uploaded separately as Valhalla_<name>_Walkable_Instance.png for Eye Below, Elder Hollow, Thousandfold Pavilion, Temple of the Rain Crown and Seed Vault. They show the current shared vault renderer with the minimap hidden; unique dungeon scenery belongs to each SCENE task. The uploaded PNG hashes match the private captures.
Thousandfold Pavilion foes (2026-10-12): python3 scripts/thousandfold_content.py writes its six level-75 encounter definitions and material items; python3 scripts/generate_sealed_dungeon_maps.py places three Paper Foldlings, three Inkwing Moths, three Silkguards and one each of the Mirror Dancer, Thousandfold Weaver and Sovereign of the Last Fold on the zone-31 floor. Rust owns their stats, ranged/slam attacks, material rewards and final-boss clear flag. python3 scripts/make_thousandfold_sprites.py preview | build | export | verify manages six editable PixelFlow atlases (34 frames each); editor IDs are in client/assets/cathedral.txt and indexed sources are scripts/thousandfold_*_raw.npz. Run node scripts/sync-world.cjs and rebuild Rust after catalog/map changes. The entrance is open after I75-SCENE and I75-OPEN. Focused validation: cargo test thousandfold, cargo test sealed_dungeon, node scripts/test-via-mcp.cjs run thousandfold_foes, REAL_SPRITES=1 node tests/sealed-dungeon-maps-ui.cjs --pavilion-foes-only, sprite export verification and byte-stable generators. Sork's FileDrop lists the six atlas PNGs, Valhalla_Thousandfold_Pavilion_Enemies.png, Valhalla_Thousandfold_Pavilion_In_Game.png and Valhalla_Thousandfold_PixelFlow_Sources.zip with matching stored hashes. No broad suite or deployment was run.
Thousandfold Pavilion quests (2026-10-13): Iona Bell offers ribbon_folds (three of each ordinary kind) after The Last Curtain, then ribbon_final (all three bosses). Both are level-75 five-player quests with table-based XP and class choices. scripts/gear_levels.py generates 28 rare Thousandfold pieces from the level-75 catalog; the three bosses each guarantee one pool, and the final quest also awards a class-neutral level-75 blue accessory. The Pavilion quest and loot path runs in the opened private party copies. Regenerate with python3 scripts/gear_levels.py, python3 scripts/generate_ribbonwild.py, python3 scripts/late_quests.py, then node scripts/sync-world.cjs and rebuild Rust. Focused checks: cargo test thousandfold, node scripts/test-via-mcp.cjs run thousandfold_quests, quest reward catalog validation and generator byte stability. No new visual or audio assets were created, and no deployment was made.
The five additional sealed MAP tasks at zones 34–38 passed the focused Rust collision/private-copy check and private Chromium return-portal check. The generator is byte-stable, and Rust format, JavaScript syntax and diff whitespace checks pass. This addition creates map data only; it has no new art or music asset to upload to FileDrop. No full suite or deployment was run.
Elder Hollow and Silent Solstice (2026-10-12): scripts/elder_hollow_content.py defines six level-70 enemy kinds, their materials and boss profiles. scripts/make_elder_hollow_sprites.py preview | build | export | verify owns six editable 34-frame PixelFlow sprites, with IDs in client/assets/cathedral.txt. scripts/gear_levels.py makes 28 exclusive Oakheart rare pieces; each boss has a guaranteed slot pool. Sunward Vale now has an open level-70 entrance and two group quests. Ordinary and boss kills inside a party copy advance quests offered at the entrance; boss kills credit living members in that copy, and the final guardian unlocks its exit. client/elderhollowart.js draws root chambers, lanterns and portals. The dybase2 score The Roots Remember Spring is project 5c438fcb8e46f8c2; scripts/create_elder_hollow_song.sh, render_elder_hollow_song.sh and finish_elder_hollow_music.py reproduce its client MP3. Silent Solstice is a distinct sealed floor at zone 39 with no enemies or inbound gate yet.
The six Elder atlases, MP3, source bundle, real-browser capture, Elder open-map plan and Silent Solstice sealed plan are in Sork's FileDrop; local and storage hashes match. Focused Elder Rust, sealed-floor Rust, private MCP and real-sprite Chromium checks cover the new content. The full suite and public deployment were not run.
After regenerating a zone, run python3 scripts/late_quests.py before node scripts/sync-world.cjs and a Rust rebuild. The late quest pass restores the Undervault and Cathedral boss quests, level-40–95 automatic travel quests, class reward choices, and Yearwheel elite reward pools without regenerating terrain. It is byte-idempotent. Reward choice claims include the selected item ID; the server checks the character's class and grants one choice atomically with the quest's other rewards. python3 tests/quest-rewards.py checks the map/catalog contract. The 28 Moonwell source pieces are generated by python3 scripts/gear_levels.py; their art and drop switch remain separate tasks. world/orange_rules.txt is the 50-piece orange design manifest generated by python3 scripts/orange_rules.py; it does not enable orange drops. Merge, refinement, level-55 art and Undervault art decisions are in docs/equipment-design-decisions.md.
Pavilion opening and Rain Crown preparation (2026-10-13): scripts/generate_ribbonwild.py opens the Pavilion gate at zone 21 (119,65), with a safe landing at (113,65). scripts/generate_sealed_dungeon_maps.py makes zone 31 open with a post-clear exit, while zone 32 remains sealed. The Pavilion scene is client/pavilionart.js; its dybase2 score is project 391cfea30638ebd9 (112 BPM, 32 bars), rendered by scripts/create_pavilion_song.sh, render_pavilion_song.sh and finish_pavilion_music.py.
scripts/rain_crown_content.py defines nine ordinary spawns and three bosses on the zone-32 private floor. scripts/make_rain_crown_sprites.py preview | build | export | verify manages six editable 34-frame PixelFlow sprites; source grids are scripts/rain_crown_*_raw.npz, editor IDs are in client/assets/cathedral.txt. scripts/gear_levels.py makes 28 exclusive level-80 Rainbound pieces, one guaranteed boss pool per guardian. Oru Slate offers tangle_crown_trials and tangle_crown_final; party-copy kills credit those quests, and the final claim grants a level-80 blue accessory. client/raincrownart.js draws the jade halls, braziers, altar and rain. Its dybase2 score is project 05e0ff35de1c06b2 (76 BPM, 32 bars), with matching create/render/finish scripts. The public Rain Crown gate opens through I80T-OPEN.
Regenerate in order: python3 scripts/gear_levels.py, python3 scripts/rain_crown_content.py, python3 scripts/generate_ribbonwild.py, python3 scripts/generate_tanglecrown.py, python3 scripts/generate_sealed_dungeon_maps.py, python3 scripts/late_quests.py, then node scripts/sync-world.cjs and rebuild Rust. Focused checks: cargo test thousandfold, cargo test rain_crown, cargo test sealed_dungeon_floor_plans, node scripts/test-via-mcp.cjs run thousandfold_open, rain_crown, thousandfold_foes, thousandfold_quests, and REAL_SPRITES=1 node tests/sealed-dungeon-maps-ui.cjs --pavilion-rain-only. Generator output was byte stable. The complete suite and deployment were not run. New sprite/music/map/screenshot assets were uploaded to Sork's FileDrop.
The Temple of the Rain Crown (zone 32) opens from the Tanglecrown at (117,24); the Seed Vault (zone 33) opens from Veyrroot Heartwood at (48,17). Both are level-80, five-player, four-copy private instances with safe public return landings and a post-clear exit. The level gate, party copy selection and boss clear remain server authoritative. The Seed Vault has nine ordinary spawns, three bosses, two linked group quests from Mosslet and Veya, six materials and 28 exclusive level-80 rare pieces. Its final quest grants a fixed level-80 accessory. The Rain Crown gate now admits parties to its already populated floor.
For map changes, regenerate scripts/generate_tanglecrown.py, scripts/generate_worldheart.py, scripts/generate_sealed_dungeon_maps.py, then run node scripts/sync-world.cjs and rebuild Rust. scripts/seed_vault_content.py owns the encounter catalog; scripts/gear_levels.py owns the rare set. scripts/make_seed_vault_sprites.py preview | build | export | verify manages six editable 34-frame PixelFlow sprites (IDs in client/assets/cathedral.txt, indexed sources in scripts/seed_*_raw.npz). client/seedvaultart.js paints the interior and portal. The dybase2 score The First Heartbeat is project 668fd1f10e92837a (G minor, 84 BPM, 32 bars); scripts/create_seed_vault_song.sh, render_seed_vault_song.sh and finish_seed_vault_music.py are its score, render and loop workflow.
Focused checks: cargo test seed_vault, cargo test rain_crown, node scripts/test-via-mcp.cjs run seed_vault, node scripts/test-via-mcp.cjs run rain_crown_open, and node tests/sealed-dungeon-maps-ui.cjs --pavilion-rain-only. They verify the live gates, level 80 entry, party isolation, quest credit, loot, clear and client scene. No full suite or deployment is part of this work.
Sork's FileDrop holds the Seed Vault open map, enemy sheet, six exported atlases, interior and entrance captures, The First Heartbeat MP3, editable sprite and score source ZIPs, plus updated Rain Crown entrance/map/interior captures. Sixteen stored files were checked against local SHA-256 hashes and visible in the FileDrop listing.
The Sunken Astrolabe opens from the Lapis Erg at (140,26). Its zone-34 template is a level-85, five-player, four-copy brass observatory: twelve encounters, two linked group quests from Laleh of the Lens, a return portal and a final-boss-clear exit. The public return landing is (140,17), outside the entrance disc. Its three bosses guarantee level-85 Meridian rare gear from 28 class and shared-slot pieces. The final quest also grants a fixed level-85 accessory. The party copy owns kill credit, boss clear and drops; an outsider receives none of them.
Thunderheart Gate now opens from Cairnspine. Its zone-35 floor has nine ordinary foes and three bosses at level 95 with server-owned combat, level scaling, materials and exclusive loot.
Regenerate with python3 scripts/sunken_astrolabe_content.py, python3 scripts/thunderheart_gate_content.py, python3 scripts/gear_levels.py, python3 scripts/generate_lapis_erg.py, python3 scripts/generate_sealed_dungeon_maps.py, then node scripts/sync-world.cjs and rebuild Rust. scripts/make_astrolabe_sprites.py preview | build | export | verify manages twelve editable, 34-frame PixelFlow sprites; editor IDs are in client/assets/cathedral.txt, indexed sources in scripts/*_raw.npz. The Sunken Astrolabe score is dybase2 project 00fa43564098f94a (Meridian of Dust, F-sharp minor, 96 BPM, 32 bars); scripts/create_sunken_astrolabe_song.sh, scripts/render_sunken_astrolabe_song.sh PROJECT_ID, and scripts/finish_sunken_astrolabe_music.py manage its source and seamless MP3.
Focused checks: bash scripts/cargo.sh test sunken_astrolabe, bash scripts/cargo.sh test lapis, bash scripts/cargo.sh test sealed_dungeon, node scripts/test-via-mcp.cjs run sunken_astrolabe, node scripts/test-via-mcp.cjs run thunderheart_foes, and REAL_SPRITES=1 node tests/sealed-dungeon-maps-ui.cjs --astrolabe-only (with the project's Node/NODE_PATH settings). The 33 new atlas, preview, map, music and editable-source assets were uploaded to Sork's FileDrop with matching SHA-256 hashes. This work is local; deploy separately when ready.
scripts/generate_cairnspine.py opens the level-95 Thunderheart portal at (145,22), preserves cairn_onward, and offers linked group quests cairn_storm_trials and cairn_thunder_final. Zone 35 has four party copies, a safe public return at (151,20), and an exit after the final boss. Its three bosses guarantee 28 exclusive level-95 Stormbound pieces; the final quest grants a fixed level-95 accessory. client/thunderart.js paints the interior and entrance transition. The editable dybase2 project 73f494b1b6f2d4c0 is Storm in the Stone (D minor, 132 BPM, 32 bars); scripts/create_thunderheart_song.sh, scripts/render_thunderheart_song.sh PROJECT_ID and scripts/finish_thunderheart_music.py manage its audio.
scripts/blooming_labyrinth_content.py and scripts/generate_sealed_dungeon_maps.py stage nine ordinary enemies and three level-97 bosses in zone 36. scripts/generate_yearwheel.py offers linked yr_sp_bloom_trials and yr_sp_bloom_final quests while the public Blooming threshold stays sealed. The three bosses guarantee 28 exclusive level-95 Glassbloom pieces, the top five-level gear tier; the final quest grants a fixed level-95 necklace. scripts/make_blooming_sprites.py preview | build | export | verify manages six editable 34-frame PixelFlow sprites. IDs are in client/assets/cathedral.txt; indexed sources are scripts/bloom_*_raw.npz.
Regenerate Thunderheart and Blooming catalogs, gear_levels.py, Cairnspine and Yearwheel, then the dungeon maps and sync-world.cjs; rebuild Rust. Focused validation: cargo test thunderheart, cargo test blooming_labyrinth, cargo test sealed_dungeon, MCP scenarios thunderheart_gate, blooming_foes, blooming_quests, and REAL_SPRITES=1 node tests/sealed-dungeon-maps-ui.cjs --thunder-bloom-only with the project's Node/NODE_PATH settings. New map plans, PixelFlow atlases and sources, and the dybase2 score and MP3 are uploaded to Sork's FileDrop. Deployment remains separate.
The level-97 Blooming Labyrinth opens through Yearwheel's spring gate at (48,237). Its return landing (48,235) is separate from the entrance disc. Four private party copies, the level gate, twelve encounters, two group quests, exclusive boss gear and the final-boss-clear exit are authoritative. Zone 37, the level-98 Solar Crucible, remains sealed; its reserved floor now holds nine ordinary enemies and three bosses, two linked Solhaven group quests, six material drops and 28 exclusive level-95 Sunforged rare pieces. Its quest and boss clear credit stay within one party copy.
scripts/solar_crucible_content.py owns the Solar encounter catalog. scripts/make_solar_sprites.py preview | build | export | verify manages six editable 34-frame PixelFlow sprites; their editor IDs are in client/assets/cathedral.txt. client/seasonDungeonArt.js draws both interiors, lighting, details, entrances and weather. Two dybase2 projects supply the scores: Blooming Petals Beyond the Door (e670b440d35db1cb, D major, 84 BPM) and Solar The Furnace of Noon (aa58fa401b993395, E major, 112 BPM). scripts/create_season_dungeon_song.sh THEME and scripts/finish_season_dungeon_music.py THEME reproduce the loops.
Regenerate with python3 scripts/solar_crucible_content.py, python3 scripts/gear_levels.py, python3 scripts/generate_yearwheel.py, python3 scripts/generate_sealed_dungeon_maps.py, then node scripts/sync-world.cjs and rebuild Rust. Run only focused checks for these changes: cargo test blooming, cargo test solar_crucible, cargo test yearwheel; MCP blooming_open and solar_crucible; real-art Chromium tests/sealed-dungeon-maps-ui.cjs --season-only. The two MP3s, six Solar PixelFlow atlas exports, six contact sheets, two interior captures and two entrance captures were uploaded to Sork's FileDrop and checked against local SHA-256 hashes. No deployment is part of routine TODO work.
Solar Crucible (zone 37), Amber Archive (38) and Silent Solstice (39) now open from the summer, autumn and winter Yearwheel courts. Each has a level gate (98, 99 and 99), four isolated five-player copies, twelve encounters, three bosses, two linked group quests, level-95 exclusive boss gear, a safe return landing and an exit after the final boss. The spring Blooming Labyrinth remains open. scripts/yearwheel_dungeon_content.py owns the Amber and Solstice catalogs; scripts/generate_yearwheel.py owns their parent gates and quests; scripts/generate_sealed_dungeon_maps.py owns the floors. scripts/make_yearwheel_dungeon_sprites.py preview | build | export | verify manages twelve editable 34-frame PixelFlow sprites; IDs are in client/assets/cathedral.txt. client/seasonDungeonArt.js paints the four halls and entrance effects. Amber's dybase2 score is The Book of Falling Leaves (8067858ec763e6fb, G major, 90 BPM); Solstice's is The Longest Night (efd640bac8f96f8b, B minor, 72 BPM). Run scripts/create_season_dungeon_song.sh THEME and scripts/finish_season_dungeon_music.py THEME for their audio. Twenty-eight new atlas, preview, score and scene files were uploaded to Sork's FileDrop with SHA-256 verification.
Alda Copperhand, the gearwright at (36,91) in Alderhaven, offers merge and refinement. scripts/gear_services_catalog.py adds her and five matching crystal materials. A legacy stack becomes stable per-copy data at her first dialogue. Each copy stores its catalog ID, current rarity, rank, rank-zero attack/defense, fixed bonus stats and rolled stats. The server accepts two explicit, unequipped IDs of the same item and rarity, consumes both and creates one next-rarity copy; epic and orange cannot merge. The output gets 117.5% of each inputs' average stats and keeps the lower rank. A chosen copy can be moved to the front of that catalog item's equipped allocation. Refinement ranks 1–5 add 4% of rank-zero stats per rank and cost 100 × rank² × rarity factor gold plus rank matching crystals. Both actions use cloned character state and save on success; failed requests leave inventory, gold and copies intact. client/gearservices.js shows the selected copies, server preview, payment and a separate confirmation. The old catalog-ID inventory/equipment and bag layouts remain readable.
Regenerate catalogs, gear, Yearwheel, dungeon maps, late quests and gearwright data, then run node scripts/sync-world.cjs and rebuild Rust. Focused checks: bash scripts/cargo.sh test yearwheel, bash scripts/cargo.sh test sunken_astrolabe_tests, bash scripts/cargo.sh test gear_services, bash scripts/cargo.sh test world::trade::tests; private MCP scenarios solar_crucible, amber_archive, silent_solstice, gear_services; real-art tests/sealed-dungeon-maps-ui.cjs --season-only and tests/gear-services-ui.cjs. The map, item catalog and generated client files are byte-stable under this regeneration chain. These changes are local until a separate deployment is requested.
A private party dungeon beneath Wyrdwood’s Norn Stones, with three guardians, three quests, Fatewoven level-25 loot with original fitted equipment shapes, a root-and-loom interior, original PixelFlow enemies and a 5/4 chamber score. See docs/nornroot.md for generator commands, focused validation and the private-copy migration required before deployment.
A private party dungeon below Bifrost Reach, with three guardians, three quests, Bridgeward level-30 loot, original PixelFlow enemies and a 3/4 chamber score. See docs/skybridge.md for generator commands, focused validation and the private-copy migration required before deployment.
A five-person private wreck west of Keelhaven in Rán’s Deep (public zone 43). Its pointed hull, split cargo hatch, gangplank and copper ribs have their own collision and rendering. Six nautical foes guard three encounters; 28 original Keelbound equipment pieces use copper, navy sailcloth, rope and coral on both hero bodies. Editable PixelFlow sprites and a 6/8 dybase2 score accompany it. See docs/longship.md for generation, validation and the private-copy migration required before a server deployment.
A public volcanic approach beyond Asterion, with a basalt causeway over a physical molten river, four ordinary enemy kinds, three elites and five quests leading to Eldmarr's Nine-Chain Door. Seven original editable PixelFlow sprites and separate dub-techno day/night scores accompany it. See docs/emberwake.md for generation, focused checks, the raid-party dependency and the private-copy migration before release.
The open Chained Forge raid floor is generated with
python3 scripts/generate_chained_forge.py (or --check to validate existing data),
after generate_emberwake.py. Run node scripts/sync-world.cjs and rebuild the
server afterward. The generator preserves the released open entrance. Its original
addition of file zone 46 shifted old private-copy ids; the opening release adds no
zone and shifts no IDs. See docs/molten-raid.md before deploying. Focused checks:
cargo test chained_forge, MCP scenario chained_forge, and
REAL_SPRITES=1 node tests/chained-forge-ui.cjs.
The raid now has ten packs of four level-99 dungeon elites, four original
kinds and no separate elite encounters or side quests. The owner asked for
overpowered packs; clear-time tuning is deferred to a later fix. Its independent 3% purple
mob roll awards unique class-neutral Cinderwork pants, necklace or ring. The first
nine Wardens and Eldmarr have their own mechanics and guaranteed orange rewards. chained_forge_content.py owns the
pack catalog and loot pieces, while elite-power.txt owns combat tuning.
Enemy art: python3 scripts/make_chained_forge_sprites.py preview|build|export|verify,
then python3 scripts/zone_tops.py cathedral. Gear jobs:
cfpack-shared-pants, cfpack-shared-necklace, cfpack-shared-accessory through
make_progression_gear.py; use export for existing PixelFlow records.
Focused checks: Rust filter raid_pack, private MCP raid_packs,
tests/raid-packs-ui.cjs (real sprites), and tests/raid-pack-gear-art.cjs.
The entrance is open: level-99 players enter muster and assemble exactly ten living
fighters at its inner gate. Use MCP scenario raid_release for the focused
entry/hiring/hall/boss/reward/exit check. The owner evaluates encounter timing and
win rates; Eldmarr's timing/enrage remain provisional. Details and approved interim fixture:
docs/molten-raid.md.
Raid strength grants every human and mercenary +50% attack and 5% damage
reduction per other fighter in the same raid copy (maximum nine others: 5.5×
attack and 45% reduction). It updates on presence changes and appears in the
character panel. Solo heroes and ordinary dungeons get no bonus. Focused checks:
Rust raid_support, browser tests/raid-support-ui.cjs; details in
docs/molten-raid.md section 17. Pack clear-time tuning remains deferred.
The first three raid bosses are Kettil Linkbearer (paired chains and numbered
anvils), Ysolt Bellowsmaid (wind and three-person shelter posts), and Hallgrim
Anvilhand (three anvils needing three fighters each). Regenerate them through
generate_chained_forge.py; original editable art is maintained by
make_chained_forge_boss_sprites.py preview|build|export|verify. Focused checks:
Rust raid_boss, private real-client tests/raid-bosses-ui.cjs. See
docs/raid-bosses.md for exact rules, fixtures and measurements.
Wardens five through nine add Two Valves and Dagfinn's copy-local shortcut lift,
Brand Relay, Four on the Floor, Water Carriers and Mirror Plate. They use the
same generator and original sprite maker. Focused checks: Rust raid_warden,
tests/raid-bosses-5-9-ui.cjs with real sprites; exact rules and the owner's
accepted faster clear times are in docs/raid-bosses-5-9.md.
Every hero unlocks the Chained Forge through The Ember Key, offered by Iarn Emberkeeper at the Emberwake arrival camp at level 99. Ten grave crosses across older zones summon shared Firelords with personal Ember Shard rewards. The journal lists the zone names; the crosses have no map markers. The new remappable Interact action defaults to G. Every hero must complete this quest, including past raid visitors; hired fighters follow their hirer.
python3 scripts/raid_key.py owns the data and --check verifies reachable,
quiet cross sites. The affected zone generators reapply it, and
scripts/sync-world.cjs exports it. No zone IDs or saved copies shift. Focused
checks: Rust raid_key, MCP raid_key, real-sprite tests/raid-key-ui.cjs
(MUTATE=1 must fail). Rules and coordinates: docs/raid-key.md.
Enemy levels: every enemy has a level, shown as Lv N beside its name when you are near or targeting it (grey if far below you, orange from three levels above you, red from five). Each kind has a default level (Green Slime 2, Blue and Pink 3, Golden 4, Ironhide 5, King 6, Cinder Wisp 5, Magma Spider 7, Ash Wraith 8, Basalt Golem 10) and every individual enemy rolls a whole level within ±2 of it, never below 1. A new roll happens at each respawn. Per level away from the default an enemy's health and damage change by 12 %, and its gold by 10 %, so a level-7 Ironhide (default 5) has 24 % more health and damage and 20 % more gold. XP depends only on the enemy's own level: it pays 45 + 5 × level XP (level 1: 50, level 2: 55, level 5: 70, level 7: 80, level 10: 95), so a same-level kill is half a level at level 1 and about 4.5 % of a level at level 10.
Emberfall Crags is a second zone for levels 5–10, reached through the Emberfall Gate at the north end of Greenmeadow's dirt road. Walk between its two stone posts into the glowing plane and the server moves you to the Crags' camp; the Meadow Gate there takes you back. The zone is saved with your character, so you resume where you logged out, and dying in the Crags brings you back at the Crags' camp. You only see and fight enemies, players, drops and combat events in your own zone. The Crags are a 96×96 field of basalt, charred trees and obsidian spires cut by four lava rivers, each with a single ford, so the trail zigzags north through four bands: Cinder Wisps (8, level 5, fast fire spirits, 280 HP, 34 damage, 18 gold, Ember Core), Magma Spiders (7, level 7, 420 HP, 42 damage, 26 gold, Magma Fang), Ash Wraiths (6, level 8, scythe-wielding specters, 520 HP, 52 damage, 36 gold, Ash Veil) and Basalt Golems (5, level 10, slow, 1100 HP, 64 damage, 55 gold, Basalt Heart); all numbers are for a default-level monster. They drop rare gear more often than the meadow (8 %, 10 %, 14 % and 20 %) from the same pool of upgrades. Lava blocks movement. Warriors, mages and assassins at level 1 can enter, but the zone is deliberately dangerous.
Leveling from level L to L+1 takes the XP in world/levels.txt: 100 at level 1, 200 at level 2, 400 at level 3, 800 at level 5, 2,100 at level 10, 6,500 at level 20 and 118,300 at level 98 (the table's last entry; later levels add the last step of 2,300 each). Existing levels and earned XP are preserved, so a character part-way through a level may see a different bar.
WASD or arrow keys move relative to the screen. Space/J attacks or casts.
The bottom skillbar has twelve slots, keyed 1–9, 0, - and =, or click/tap. K opens Skills: select a slot and an ability, or drag to assign and swap. Layouts save per guest character in this browser. Each class learns one new skill every four levels from 4 through 40 (ten per class); the even levels between unlocks improve existing skills. world/skills.txt is the catalogue; node scripts/sync-world.cjs generates client/skills.js (then rebuild Rust). A newly learned skill goes into the first empty slot. Cooldowns, buffs, damage and unlock levels are all decided by the server (skill {id,fx,fy}). A level-up shows a pillar of light, rays, rings and sparks, a screen flash and shake, a fanfare and a banner naming the new skill or rank; prefers-reduced-motion drops the flash, shake and spin.
Class resources: skills spend the cost shown in the skill library and bar tooltip; insufficient resources refuse the cast without starting a cooldown. Basic attacks are free. Assassins and hunters have 100 Energy, start full and recover 10/second even in combat. Warriors have 100 Rage, start at zero and gain 0.5 per damage dealt and 1 per damage taken (actual HP lost, capped against overkill); rage fades at 5/second outside combat. Mages and priests start with full Mana, with capacity 100 + 10 per level after 1 + 5 per trained Intellect; mana persists across logout/restart and regenerates 5% of maximum/second after five seconds outside combat. Attacks, hostile pursuit and healing or buffing a fighting ally keep mana regeneration off. Death empties resources; respawn restores mana/energy and zero rage. Level-up refills mana. Rust owns all values and publishes resourceType, resource, maxResource and inCombat in player snapshots.
Shift uses Assassin Shadowstep (20 Energy).
Mana supplies: potion merchants in every town/camp sell a Mana Potion for 30 gold, restoring 100 mana immediately. C uses a mana potion for mages and priests; X keeps using a health potion and Z food. Health and mana potions share their persistent 60-second cooldown. Traveler's Stew restores both 100 HP and 100 mana over 8 seconds, and can be eaten at full HP when mana is missing. Mana potions are refused without consuming anything for classes without mana or when mana is full. Mana returns at 5% of the pool a second out of combat and, since 2026-10-03, at 1.5% a second in combat, so a healer is never completely dry in a long fight.
Priest: the healer, drawn in the Assassin's anime style (robes, halo, violet stole, mace; scripts/make_priest_sprites.py). 100 health, melee mace strike every 0.62 s, and Intellect as its specialty. Its ten skills unlock at the even levels: Mend (a heal for the most wounded party member within 10 m, or yourself when alone), Smite, Ward of Light, Prayer of Healing (up to five), Holy Nova (damages enemies and heals the party), Blessing of Might, Guardian Light, Searing Light, Divine Hymn and Heaven's Wrath. Heals scale with attack power (stats().0 times the skill's multiplier), reach only living party members in the same zone within range, never strangers, and a heal with nobody hurt is refused without spending its cooldown. Healed players see a green +N and a healed event; Ward of Light and Blessing also buff party members in range.
Hunter: the ranged class, a bow-and-quiver ranger in the same anime style (feathered headband or wolf hood, cape, short tunic; scripts/make_hunter_cel_sprites.py). 95 health, an arrow every 0.58 s that flies up to about 8 m (the Mage's bolt mechanics), and Agility as its specialty. Its ten skills unlock at the even levels: Power Shot, Multi-Shot, Roll Away, Piercing Arrow, Hunter's Mark, Explosive Arrow, Rapid Fire, Rain of Arrows, Eagle Sight and Arrow Storm.
E opens the character and equipment window beside your bags, with Headgear, Shoulders, Chest, Pants, Gloves, Hands, Necklace and two Accessories. Existing armor fills Chest and weapons fill Hands; other slots start empty and accept rare universal loot. Gear bonuses are authoritative, and equipped accessory copies are protected from sale. Esc opens the world menu. These stop your actions, but the shared world continues. Enemies can still hurt you.
Male or female: every class can be created with a male or a female body (the Body buttons on the creation screen; the choice is part of the look, stored by the server, shown to every other player and fixed once the character exists). The female sets are separate atlases with their own narrower shoulders, waist and hips, softer face and long hair, and the same gear, stats and skills: python3 scripts/make_<class>_cel_sprites.py preview | export --local --female (the Priest is make_priest_sprites.py ... --female) writes client/assets/<class>_f_*.png and <class>_f_sprites.txt. Existing characters stay male.
Layered gear: Warrior, Mage, Assassin and Hunter are drawn as eleven independent layers each: the body plus armor (Chest), weapon (Hands), shoulders, gloves and head, in two sets per slot (Warrior crimson/azure, Mage apprentice/runic, Assassin shadow/moon, Hunter scout/warden). Wear a class piece in the Headgear, Shoulders or Gloves slot and it shows on your character for everyone; other gear in those slots only changes stats. Each class starts with the first set of all three pieces in the bag, unworn; the second set drops as rare loot. The Priest does not have these three layers yet. scripts/cel_common.py is the shared renderer, and make_<class>_cel_sprites.py preview | build | export draws, edits in PixelFlow and exports each class (the Assassin's body, outfits and blades stay as they were; only its three new layers are drawn).
Attributes: Every level after level 1 grants three permanent training points in the E character window. Existing characters receive points for their earned levels. Strength adds 0.2 defense and 2 attack for warriors (0.5 for other classes); Agility adds 0.5 percentage points of critical chance and 2 attack for assassins (0.5 otherwise); Intellect adds 1% recovery reduction and 2 attack for mages (0.5 otherwise). Stamina adds 8 maximum/current HP, preserving existing damage. Dexterity adds 0.5 percentage points of dodge chance; Accuracy adds 0.5 percentage points of hit chance, starting at 90%. Crit caps at 60%, dodge at 35%, hit at 100%, and recovery reduction at 30%; attack recovery never undercuts the attack animation. Dodge avoids an entire incoming enemy hit. Missed attacks deal no damage and grant no rewards. Training is validated and saved immediately by Rust; all derived combat values are authoritative. The primary-stat/class choices draw on Blizzard’s stat overview and WoW’s game manual; the training budget and numerical effects are tuned for this game.
The quest tracker on the right lists every active quest with gold titles, indented objective counts and green completed objectives or turn-in prompts. Collapse it with the header toggle; Q or a quest title opens the journal. The journal's Track action places a quest first.
B or I opens bags. Items use icon cells with rarity borders, stack counts and hover/focus tooltips. Right-click or double-click gear to equip, drag it onto a matching character slot, and right-click equipped gear or drag it into a bag to unequip. Drag bag cells to reorder, search across all bags, or use Sort. Equipped copies leave the bag grid; extra copies stay stacked. Bag layouts save per guest character in this browser. The server enforces a 16-slot backpack and four expansion-bag slots. Bag traders sell ten tiers from a level-1 six-slot Linen Satchel (500 gold) through a level-90 24-slot Sovereign Pack (240,000 gold); the tier chooser shows requirements, owned copies and immediate capacity gain. A purchase fits immediately, and four full slots replace only the smallest strictly smaller bag. Existing fitted bags keep their capacity after a level loss. Every enemy has an independent 2% chance to drop the bag tier matching its level band; eligible party members take the shared drop in round-robin order and fit it on pickup. Each loose item stack uses one slot; equipped copies use none. Full bags still accept gold and existing loose stacks, while new item stacks stay on the ground until picked up or their normal 60-second expiry. Unequipping cannot overflow bags. Existing unlimited inventories receive only the expansion packs needed to preserve their loot when first migrated. Merchants use the same item artwork and protect equipped copies.
Consumables: the quick slots left of the skillbar move together in Edit UI. Pip sells Traveler's Stew (12 gold, 100 HP and 100 mana over 8 seconds; one meal at a time, shown in the effects bar). Mira sells health and mana potions (30 gold, 100 HP or mana instantly). Bags offer Use by button, right-click or double-click. Items are refused when none of their recovery is needed. The shared potion cooldown is saved as a wall-clock time; a meal ends when you leave. The catalog fields are heal, mana, duration (food) and cooldown (potions); an NPC offer sells an item with item.
Potion and food tiers (2026-10-03): a stronger potion and meal every ten levels, scripts/consumable_tiers.py (levels 1, 10, 20 ... 90; green at 10, blue at 20, purple at 30, orange from 40). Health potions heal 100, 210, 350, 490 ... 1,330 HP, mana potions restore 100, 170, 300, 430 ... 1,190 mana, and the stew gives both over 8 seconds (Traveler's Stew, Hearty Stew, Ranger's Feast, Warden's Feast ...). Every existing potion and food vendor sells the best tier your level allows (the offer shows its name, effect and price: 30 to 1,150 gold for potions, 12 to 500 for food); an item cannot be used below its level, and the X, C and Z quick slots pick the strongest one you own and may use. Mercenaries carry their level's health potions, and mana potions if they cast, and drink them when low.
Friends and parties: O opens the social panel (Friends, Party and Online tabs) and P opens it on the party tab. Add a friend or invite to a party by typing a player's exact name, or press the buttons in the Online list (names are not unique, so the list is the way to pick between two players called the same). The other player gets a prompt with Accept and Decline; requests and invites lapse after 60 seconds or when either player disconnects. Friendship is mutual and saved with both characters (up to 50 friends); the list shows who is online and where, and offline friends stay listed. A party holds at most 5 players. Whoever invites first becomes the leader; only the leader invites, promotes or removes members, and when the leader leaves the longest-standing member leads (a party of one ends). A party member who disconnects keeps their seat for 60 seconds. Parties are not saved and end when the server restarts. The party frame under your portrait shows each member's health, party members are green on the minimap and in the world, and /p message (or /party) talks to the party only. /friend name, /invite name and /leave are chat shortcuts. Nothing in combat, XP, loot or quests depends on a party yet.
Player trading: Choose Trade beside a nearby player in the Social panel's Friends or Online tab. The other player accepts the request, then each side offers items and/or gold and confirms after reviewing both offers. Changing either offer clears both confirmations. The server checks distance, ownership, worn gear, gold and bag space again at completion, then saves both characters in one transaction. Starter and GM gear cannot be traded. Requests expire after 30 seconds, open trades after 120 seconds, and disconnecting or walking apart ends the exchange.
PvP: The Social panel's PvP tab has a world PvP flag, incoming duel challenges and arena queues. Duel another nearby player from the Online list; both must be alive outside a sanctuary, and the other must accept within 45 seconds. The duel ring has a 25-tile radius around the acceptance point. Leaving it starts a visible 10-second return timer; returning clears it, while time running out forfeits the duel. A duel also ends at one HP without an XP penalty, or when either player cancels or leaves. World PvP requires both players' flags and is disabled in sanctuaries and between party members. The flag is session only and clears on reconnect or defeat. Select a player in the world or the Online list, then press Space or J for a server resolved basic attack. Arena queues support 1v1, 2v2 and 3v3; a team larger than one must be an online party of exactly that size, queued by its leader. Matches use private arena copies, disallow friendly fire, end when every member of a team reaches one HP, and return everyone to their prior location at full health. Forfeit and a five minute draw also end a match. Build the match-only zone with python3 scripts/generate_arena.py, then node scripts/sync-world.cjs and a Rust rebuild. Test only this feature with npm run test:pvp and bash scripts/cargo.sh test world::pvp::tests.
Autonomous adventurers: with VALHALLA_AMBIENT_PLAYERS=1, every shared outdoor zone or city containing a connected player gradually gains 3–6 temporary adventurers. They choose a unique name from world/ambient_names.txt (100 names), a random class, appearance and level inside that map's recommended span; cities use their quest-level span. New arrivals appear at varied hunting grounds near ordinary mobs, take compatible kill quests, follow collision-safe routes between fights, and return to a quest giver after a run. After death, the same adventurer respawns at that giver's hub, pauses to talk, then heads back into the field. Cities without mobs have adventurers walking between townspeople. They use ordinary combat, skills and potions, occasionally chat nearby, and appear in the online roster; they decline friend and party invitations. They leave when the last real player leaves that map, are never saved, never occupy connection slots or private dungeon instances, and do not count toward the real-user /health.online value (visibleOnline includes them). Isolated MCP worlds leave them off unless start_world receives ambientPlayers: true.
Enter focuses chat; Enter sends; Esc leaves the chat input. M toggles all sound.
Party XP and party level: a party shares the XP of a kill. Each living member in the zone near the body (60 m) gets a cut set by the party level (1 to 5): 40%, 55%, 70%, 85% and 100%, so a level-5 party earns each member as much as a lone hero. The level is earned by killing together: each kill shared by two or more members gives every member who took part fellowship points (ordinary enemy 1, elite 5, boss 15; enemies more than 6 levels below the strongest member give none), and the party's level comes from the average of its online members' points (reaching 40, 120, 260 and 450 gives levels 2 to 5). The points are saved on the character. The party tab shows the level and a progress bar, the party frame shows Lv N, and the system log reads "defeated by your party" for the shared XP. A hero alone, or with only hired fighters, keeps all the XP; mercenaries take none. server/src/world/partyxp.rs.
Settings (Esc, then Settings): separate Music and Sound effects sliders (0–100%, default 100%, a squared taper so 100% is the original loudness; releasing the effects slider plays a sample) and a Sound on checkbox that mirrors M. Edit UI hides the panel and puts a dashed handle over each movable piece: character frame, active effects, party frame, minimap, Visit Alderhaven button, quest tracker, Character, Bags and Social buttons, skillbar, chat and the controls hint. Drag a handle (mouse or touch), or Tab to one and use the arrow keys (Shift for bigger steps). Pieces stay inside the stage, the game receives no keys or clicks while editing, and Esc or Done returns to Settings. Reset layout restores every default. Volumes and the layout are stored per browser (not per character) under valhallasc.save.v1; positions are offsets in stage-width percent from each piece's default spot, so they follow the window size. Client only: no server or protocol change.
New characters receive their class’s starter armor and weapon. Equipment must be owned before it can be equipped. Existing characters keep their equipped gear when their inventory is first migrated.
Alderhaven is south of the meadow. Follow the path through the north gate or click Visit Alderhaven to walk there automatically.
Q opens the quest journal. Wren offers a town introduction; Captain Rowan offers a six-slime patrol, which unlocks Bram’s two-Ironhide hunt and Linden’s repeatable eight-enemy bounty. Bram’s quest unlocks Rowan’s King Slime challenge. Accept at the giver, complete the objectives, and return for XP and gold. Every quest has a recommended level, shown on its journal card (orange from three levels above you, red from five, grey from five below), and pays XP equal to a tenth of what that level needs to level up (level 5: 80 XP). Quest progress and completed rewards are saved with your character. The journal can walk you to a giver; gold markers show one-time quests (!) and ready turn-ins (?); repeatable quests use blue markers. Only kills after acceptance count, and the finishing blow earns credit.
Cinderwatch Camp is the safe quest hub beside the Crags arrival gate. Captain Sera commands the expedition, Scout Kael guides the lower hunts, Sister Iona offers free healing and Health Potions, and Quartermaster Dain buys loot and sells stew and six-slot satchels. Tents, a campfire, a contract board and minimap markers identify the camp. Outside its sanctuary, the travel button walks you back to camp. Update only the camp with python3 scripts/generate_crags.py --hub-only, then run node scripts/sync-world.cjs and rebuild Rust; the full generator also includes the camp.
Twelve Crags quests: A Foothold in the Ash (meet the crew); Lights Along the Ford (4 wisps); Quench the Cinders (8 wisps); Silk Across the Trail (4 spiders); Break the Brood (7 spiders); Voices in the Ash (3 wraiths); A Quiet Mountain (6 wraiths); Stone Sentinels (2 golems); Crack the Basalt (4 golems); Emberfall Vanguard (2 of each kind); Cinderwatch Patrol (10 Crags enemies, repeatable); Keep the Ash Road Open (3 wisps and 3 spiders, repeatable). The introduction unlocks the first hunt and patrol; claiming the wisp, spider, wraith and golem hunts unlocks successive expedition stages. Quests only credit actions in their own zone. The journal puts the current zone's quests first and routes to local givers.
Cinderlord elite: a horned warhammer tyrant in the northeastern Crags, default level 10 (normal ±2 spread), 2,400 HP and 180 base damage. Tuned for two trained level-10 adventurers using skills and healing; he respawns after three minutes and fully heals on evade. Captain Sera offers Two Against the Cinderlord, with 210 XP, 200 gold and one guaranteed green Reinforced Moonstone Necklace (level 10, all classes, +4.8 attack / +1.2 defense). Each player must accept the quest and damage him, then stay alive in the same zone within 12 units of the kill. This quest shares credit among contributors; ordinary quests, XP and loot keep killer ownership. Full bags refuse the entire turn-in until there is room. python3 scripts/make_crag_sprites.py preview cinderlord (then build cinderlord and export cinderlord) manages its 34-frame editable PixelFlow sprite; build cinderlord --replace discards only its editor edits. node scripts/test-via-mcp.cjs run cinderlord runs a real duo fight with god mode disabled.
Gloomroot Colossus elite: a drowned willow grown into a giant, in the far south-eastern corner of Gloamfen (past the third ridge gap), default level 20 (normal ±2 spread), 90,000 HP and 1,500 base damage (two hits fell a non-tank). It needs a healer: parties of one to five warriors without one all fall, while a warrior, Priest, Mage and Hunter (mercenaries included) beat it in about 2.5 minutes losing at most a couple, and a warrior with only a Priest cannot. Reeve Osric's Five Against the Gloomroot (fen_gloomroot, needs Many Heads) is a group quest: living, nearby contributors share the kill, and every one of them gets 1,500 gold, a tenth of level 20's XP and a guaranteed blue (rare) Celestial Amber Ring. It respawns after 300 s and heals fully when it evades.
Vendor weapons: merchants and armorers sell green weapons for your class at levels 10–90 in ten-level steps, showing the highest usable tier. Prices rise from 1,000 to 81,000 gold; stock, pricing and generation.
Mercenaries: Captain Sera (Two Against the Cinderlord) and Reeve Osric (Five Against the Gloomroot) rent fighters once you have taken their group quest: any class, for gold that rises with your level (250 at level 1, 1,200 at level 20, 115,000 at level 99), as many as it takes to fill your party to the number of players the quest wants (one for the Cinderlord, up to four for the Gloomroot, fewer if you already have a party). A mercenary joins your party at your level with the class's starter gear and all its stat points in its main stat, follows you across zones, fights what you or your party fight, uses its class skills (a Priest mends, a Mage casts from range), drinks a potion when hurt and is never saved. Its kills, XP, loot and quest credit are yours. It leaves as soon as its hired quest objective is complete (before turn-in), its instance is cleared, you log out, leave the party or choose Send my mercenaries away. Finished objectives cannot hire replacement fighters. Mercenaries are not counted as players online. Hired fighters route around walls and recover from a blocked route once both they and their hirer leave combat; see mercenary following.
Rimeveil Glacier is a third zone for levels 10–15, reached through the Rimeveil Gate at the north end of the Emberfall Crags' trail (the Crags Gate there leads back, to the foot of the new gate). It is a 128×128 glacier built as a spiral: four rings of ice walls (radius 48, 37, 26 and 15) each have a single gap, on the south, north, south and north side in turn, so the way in winds half a turn round the mountain per ring (about 300 units of walking for 67 units of distance) and everything in the next ring wakes at the gap. The packed-snow trail runs gap to gap. The outer shelf holds Rime Crabs (9, level 10, 900 HP, 60 damage, 60 gold, Rime Shell), the second ring Frostfang Wolves (8, level 12, fast pouncers, 1000 HP, 72 damage, 78 gold, Frost Pelt), the third Glacier Yetis (6, level 13, slow ground-pounders, 1900 HP, 88 damage, 105 gold, Yeti Horn) and the summit bowl, ringed by ice spires, four Rime Wyrms (level 15, 2200 HP, 100 damage, 150 gold, Wyrm Scale); all numbers are for a default-level monster and each rolls two levels either way. Outside the outer wall is a pine forest. The glacier has a polar-night sky with a slow aurora, falling snow and its own score, Rimeveil Spiral (F♯ minor, 100 BPM, 32 bars, written in dybase2; client/music_rime.js and assets/music_rime.mp3), which replaces Ashfall Run while you are there. Zone, level, loot and quest rules are the Crags' rules.
Rimeward Camp is the safe quest hub beside the glacier arrival gate: Warden Halvard commands it, Huntress Brynja guides the lower hunts, Brother Eirik offers free healing and Health Potions, Trader Skadi buys loot and sells stew and satchels, and Loremaster Ylva studies the summit. Update only the camp with python3 scripts/generate_rimeveil.py --hub-only, then node scripts/sync-world.cjs and a Rust rebuild.
Thirteen glacier quests (levels 10–15; a quest pays a tenth of its level's XP): Into the White (meet the four NPCs); Shells on the Ice (5 crabs); Crack the Shoreline (10 crabs); Howls in the Whiteout (4 wolves); Break the Pack (8 wolves); Thaw for the Lost (6 crabs and 4 wolves); Footprints the Size of Barrels (3 yetis); Thin the Herd (6 yetis); The Thing in the Summit (2 wyrms); Wyrmslayer (4 wyrms); Heart of the Glacier (2 of each kind); Rimeward Patrol (10 glacier enemies, repeatable); Keep the Sledge Road Open (3 crabs and 3 wolves, repeatable). The introduction unlocks the first hunt and the patrol; each hunt unlocks the next.
Gloamfen is a fourth zone for levels 15–20, a dusk fen reached through the Gloamfen Gate in the middle of the Rimeveil summit bowl (the Summit Gate there leads back). It is 128×128 and built as a C round a black lake: a bramble ridge closes the north strip except for one gap in the west, and three more ridges (each with one gap) cut the route into bands, so the way in is forced: the west bank (Fen Toads, level 15, ×10), the south shore (Mire Crocodiles, 17, ×9), the east bank among ruined columns (Drowned Knights, 18, ×8) and, across a causeway from the east shore, the island of the Mire Hydras (20, ×4). A short ridge joins the north ridge to the lake so the north fen cannot be used as a short cut. The trail is a boardwalk, the lake and willows are drawn in a violet-dusk sky with a pale moon and drifting fireflies, and the zone has its own score, Lanternmere Dusk (6/8 in D dorian, written in dybase2).
Lanternmere is a boardwalk town on the fen's north shore (a sanctuary with a fountain, a hall, an inn, houses, stalls and lamps): Reeve Osric is in charge, Lamplighter Wren trims the lanterns, Sister Maren heals (free blessing, Health Potions), Trader Torvald buys loot and sells stew and satchels, Ranger Tilda guides the hunts, Scholar Pell studies the ruins and Ferryman Odd waits for the causeway to be cleared. Update only the town with python3 scripts/generate_gloamfen.py --hub-only, then node scripts/sync-world.cjs and rebuild Rust; without the flag the generator rebuilds the whole fen and the summit gate (seeded, idempotent, checked by flood fill: every spawn and NPC reachable, and sealing each gap in turn cuts off exactly the bands beyond it).
Sixteen fen quests (levels 15–20; a quest pays a tenth of its level's XP): Lights on the Water (meet the six townspeople); The Choir in the Reeds (5 toads); Hush the Marsh (10 toads); Wicks and Fog (6 toads); Teeth in the Shallows (4 crocodiles); Hides for the Dike (8 crocodiles); The Ferry Line (5 toads and 3 crocodiles); The Garrison That Stayed (3 knights); Lay Them to Rest (6 knights); The Drowned Watch (4 crocodiles and 4 knights); Many Heads (2 hydras); Hydrabane (4 hydras); Heart of the Mere (2 of every kind); and three repeatable contracts, Boardwalk Patrol (10 fen enemies), Keep the Plank Road Open (3 toads and 3 crocodiles) and The Deep Patrol (4 knights and 1 hydra). Fen materials (Fen Toad Gland, Mire Crocodile Hide, Drowned Gauntlet, Hydra Fang) sell to Torvald.
Skaldholm is a fifth zone and a safe walled city with no enemies at all, reached through the Skaldholm Gate in the Rimeveil summit bowl (the Glacier Gate in its forecourt leads back). It is 160×160: a wall with eleven towers, one Great Gate in the south, a Grand Avenue and a Trade Road crossing at a round plaza with a huge marble fountain, four minor streets, about a hundred timber houses (each signed with its street and number), the Steward's Hall closing the avenue, a Cathedral, an Archive, the Merchants' Guildhall, a tavern with a stage, a smithy, stables, markets, a garden, an orchard and, in its own court near the gate, the Meeting Stone: a tall runed monolith in a ring of stone with glowing blue runes, after the one in World of Warcraft. Forty-two people live there. Twenty-eight stand about with a word for you; fourteen walk: guards on patrol, children running round the fountain, a couple strolling the ring road, a courier, a peddler, a pilgrim. A walker's place is a pure function of the server's clock (Npc::position_at in model.rs, City.routePoint in city.js), so everyone sees the same walk and talking works wherever the server says they are. The score is Skaldholm Square (E minor, 76 BPM, 32 bars = 101 s). Rebuild the city with python3 scripts/generate_skaldholm.py (after generate_rimeveil.py and generate_gloamfen.py when those are rebuilt), then node scripts/sync-world.cjs.
Eleven city quests (levels 11–20; a quest pays a tenth of its level's XP): The Great Gate Opens (meet five people in the city); Letters to the Four Hearths (speak to Captain Rowan, Captain Sera, Warden Halvard and Reeve Osric in the four earlier maps); Clear Gel for Clear Heads (8 Slime Gel and 4 Blue Slime Gel from the meadow); Cinders for the Forge (Ember Cores and Magma Fangs from the Crags); Winter Cloaks for the Watch (Frost Pelts and Yeti Horns from the glacier); The Song of Four Hearths (Pip, Scout Kael, Loremaster Ylva and Ferryman Odd); Marsh Medicine (Toad Glands and Crocodile Hides from the fen); The Drowned Archive (Scholar Pell, then 3 Drowned Gauntlets); Runes for the Meeting Stone (Hydra Fangs, Wyrm Scales, Basalt Hearts); Tribute of Four Lands (one gift from each land); and the repeatable Guild's Standing Order (3 Magma Fangs and 3 Rime Shells). Hand-in objectives ("kind": "bring") count what is in your bags right now (so materials gathered before accepting already count and selling them takes progress back) and the items are taken when you collect the reward. A talk objective may name someone in another map: speaking to them counts toward the quest wherever you accepted it.
The Undervault is a sixth zone and a dungeon, reached by the stairs beside the Meeting Stone in Skaldholm (its stairs up, and a portal that opens in the throne room, lead back). It is for five heroes of level 20: the stairs refuse anyone below level 20. It is an instance: every party gets a private copy (four exist; the party's own if a mate is already inside, else a free one), nobody outside sees it, its enemies and loot, and when the last hero leaves it resets (all enemies back, the exit shut again). A hero who logs out inside wakes at the stairs. 15 rooms and corridors in one long snake: the Stair Hall, the Barracks, Hrolf Bonegate's Court (boss 1, a horned jarl whose axe-slam rings the ground), the Ossuary, Valka, the Hollow Choir's Chapel (boss 2, a lich who sings volleys of three), the Cistern, Ironwake's Forge (boss 3, a granite colossus with a huge slam) and the Throne of Haldor, the Hollow King (boss 4: a slam up close, a fan of five missiles from afar). Fifty enemies: 25 Vault Thralls (melee), 12 Bone Archers and 9 Hollow Acolytes (the game's first ranged enemies: they stop at range, wind up and loose missiles that fly straight, stop at walls and are dodged by stepping aside). It is dark; torches and braziers cut pools of light out of the dark. Nothing respawns inside. Bosses pay 12 times an ordinary kill's XP and each boss drops one random blue piece across all classes (two from the king), shared through the same Need/Greed/Pass party rolls as world drops; solo heroes keep the drop: Undervault gear, a recoloured and renamed copy of the level-20 blue set that is 15% stronger (27 pieces, ids ending _l20_vault, source: undervault; they never drop anywhere else). The Meeting Stone itself speaks: click the monolith to hire fighters of any class for 250 gold each (no quest needed, up to a party of five); they follow you down the stairs and depart when the instance is cleared. Once aggroed, instance enemies keep pursuing throughout the dungeon, routing around walls without a distance or home leash; when a target dies or leaves, they pursue another living hero in that copy if one remains.
Click a townsperson or press F nearby to talk. The sanctuary heals for free; the bakery, apothecary, and inn offer healing services for gold. The armorer replaces and fits basic starter equipment for free. Starter gear cannot be sold. Food and tonics are consumed at the counter.
Every mob drops a sellable material alongside its gold: Slime Gel (3 gold), Blue/Pink Gel (5), Golden Gel (7), Ironhide Shell (15), or Royal Jelly (40). Move within 3 world units to attract your drops; only the living killer can collect them. Uncollected drops disappear after 60 seconds.
Gear rarity uses gray Common, green Uncommon, blue Rare, purple Epic and orange Legendary. Ordinary gear drops retain their level-aware pools and rates (common 2%, green 1.5%, blue 0.03%, purple 0.01% per ordinary kill); outdoor elites guarantee a green piece and roll blue and purple separately. Every credited monster death also gets an independent 0.1% total orange chance: one of 50 class-free, level-one pieces is chosen uniformly, and its attack, defense, stamina, accuracy and dexterity are rolled within that piece’s ranges. The rolled copy keeps those exact values through pickup, party voting, trade, equip and saving. All 50 orange pieces have distinct PixelFlow inventory icons and wear as fitted, animated class gear with family colors. The authoritative rules are in world/orange_rules.txt and server/src/items/orange.rs.
Sell loot to Linden (merchant) or Bram (armorer) in town. Sell one item, a spare stack, or all materials. Rare class gear sells for 30 gold and universal gear for 25 gold. All equipped copies are protected. Inventory, trained attributes and fitted bags persist with your character. world/items.txt owns item names, values and the shared UI catalog; run node scripts/sync-world.cjs after changing it, then rebuild Rust.
Slimes stay outside the city walls. NPC conversations stop your actions while the shared world keeps running.
New Character keeps your earlier character in Esc → Your Characters.
The Wyrdwood is a seventh zone for levels 20-30, reached through the new East Gate in Skaldholm's wall (the Trade Road now runs out of the city; the gate in the Wyrdwood's west edge leads back). It is a 192×192 autumn forest below a storm-wracked moor, with two quest hubs, each with its own sanctuary and travel master: Hollowmoot, a root-town beneath the colossal hollow Elder Ash (levels 20-25, seven people and twelve quests), and Skuldwatch, the hill-fort at the north end of the Troll Bridge (levels 25-30, seven people and thirteen quests). The river Skuld cuts the whole map; the bridge is the only dry crossing, and above Skuldwatch two ridges of dark crag, each with one gap (the first in the east, the second in the west), make a zigzag up the Highmoor to the summit court. Five monster kinds climb the levels: Rotfang Boars (21, a fast charge), Gallowcrows (23, ranged, two feathers, they back away and hover), Mosshide Trolls (25, ground-pound with a sapling growing from the shoulder), Wyrdweavers (27, ranged, three thread bolts) and Stormrams (29, the fastest charge in the game). Two elites: Oakhorn, the grove's stag (level 25, three players, a charge after a full second of warning, a guaranteed blue pair of Celestial Wayfarer Pants) and Hrungnir, a granite storm giant (level 30, five players, a fan of three lightning bolts from afar and a ground-pound up close, a guaranteed epic Mythic Moonstone Necklace); both givers hire the missing fighters. Wyrdwood monsters can drop the expanded level-25/30 gear through the ordinary level-aware loot pool; fixed quest equipment rewards retain their existing items.
visit: the Moonpetal Glade, Gallows Rock, the Norn Stones and three beacons that stay dark until your quest has lit them), a message to the other hub (the Warden's Seal; it also teaches the travel master), mixed hunts, repeatable patrols and bounties, the two elite group quests and the capstone. A progression quest, The Road East, arrives by itself at level 22.python3 scripts/generate_skaldholm.py (it writes the East Gate) then python3 scripts/generate_wyrdwood.py and node scripts/sync-world.cjs.Bifrost Reach is an eighth zone for levels 30-35, up in the sky. Climb the Stormrift, a ring of standing stones in the north-west corner of the Wyrdwood's summit court (a progression quest, Up Through the Rift, finds you at level 30 and sends you there) and you arrive on Heimdall's Perch, the one hub: a Valkyrie outpost on a floating island with seven people (Shieldmaiden Sigrun, Eira the Windhealer, Hrafn Skyfarer, Gale-Mistress Skadi, Prism-smith Brokk, Stormseer Veleda, Thorgrim Hallskald), a travel master (Spark Travel links it to Skuldwatch) and a gate back down. The zone is 160×160 tiles of eight floating islands joined by rainbow-glass bridges over open void: only the islands and bridges are ground, everything else is a drop (the zone lists its floor as 160 text rows, sky, and a ring of void rectangles two tiles thick that the server collides with, so nobody can walk or dash off an edge). A storm rolls far below under a gold-and-blue sky. Five new monster kinds climb the chain of islands: Galehounds (30, cloud-wolves, a very fast charge) on Windward Meadow, Prism Golems (31, ground-pounding crystal) on the Shardfields, Skyrays (32, hovering mantas that shoot two lightning bolts) on the Stormcrown, Hollow Einherjar (33, dead heroes with sword and shield) in the Hall of the Fallen and Thunderrocs (35, diving storm eagles) in the Roc's Eyrie; each drops its own material (Gale Fang, Prism Shard, Stormray Wing, Worn Rune Token, Thunderroc Quill). Closing a bridge cuts off exactly the islands beyond it (the generator and a Rust test prove it), so the bands come in order; Windward and the Shardfields form a ring that joins at the Stormcrown.
hold: hold the ward. Three ward stones (the Windward Ward, the Fallen's Vigil, the Last Ward) stand in rune circles. Take the quest, stand inside the circle and channel: every second inside adds one to the objective (the tracker and journal show it as 34/60 s; the circle fills with an amber arc and turns gold when done). Stepping out only pauses the count; falling near the ward loses all of it. While you channel, waves of sleeping storm-spawn wake at set marks (a galehound at 15 s and 40 s at the first ward, einherjar and a skyray at the vigil, thunderrocs and prism golems at the last) and hunt you; when the ward holds they dissolve, and if you simply walk away they go back to sleep after twelve seconds. The first ward (60 s, level 31) and the vigil (75 s, level 34, a blue Amber Ring) are solo content, the Last Ward (120 s, level 35) is a group quest for three whose giver hires the missing fighters (epic Mythic Wayfarer Pants). It is the only place the objective is used.python3 scripts/generate_bifrost.py (after generate_skaldholm.py, generate_wyrdwood.py) then node scripts/sync-world.cjs.Ran's Deep is a ninth zone for levels 35-40, on the sea floor under the storm. Step into the Maelstrom, a waterspout beside the end of the long bridge on Bifrost Reach's Roc's Eyrie (a progression quest, Down the Maelstrom, finds you at level 35), and you land in Keelhaven, a camp of overturned longships in a bell of air with seven people (Skipper Ylva Saltbeard, Brynja the Tidehealer, Ottar Netmender, Halla Deepdiver, Ketil Rustbane, Thora Drownedspeaker, Old Sigmund Bellwright), a travel master (Spark Travel links it to the Perch) and the Maelstrom back up. The zone is 176x176 tiles: the open Shallows round Keelhaven (the Wreck of Naglfar in the west, the Drowned Chapel in the east) and, to the north, the Net, a labyrinth of living coral (10 x 7 cells, walls 3 tiles thick, one door in its south wall) whose heart, Ran's Court, is a 2 x 2 chamber with exactly two doors. A trail of pale shell-sand marks the shortest way in; the rest is yours to find. The kinds follow the maze's depth: Drowned Draugr (35) in the Shallows, then, ever deeper, Lantern Anglers (36, a glowing lure that shoots one slow bead), Gnashing Morays (37, a fast charge), Sirens (38, three bolts of song) and Shellbacks (39, ground-pounding turtles). Three elites: Hvitserk the Drowned Captain (37, three heroes, west Shallows, has a quest), the Kraken (40, five heroes, in Ran's Court, has a quest) and the Ghostmaw (39, three heroes, a pale giant moray in a deep corridor, with a level-40 group quest from Thora). Each drops its own material; the Ghostmaw's tooth and the captain's signet are only worth selling.
Astralhollow is a level 40-45 overworld reached through the starway in Nacrehold. Its 128x128 impact basin has living starglass groves, grass and starflowers, crystal ridges, three connected trails, and a lantern-lit observatory sanctuary. The return starway stands on the arrival plaza; six residents offer healing, provisions and twelve quests, and the Travel Master links to Nacrehold. Void Moths, Crystalwyrms, Orbit Beetles, Eclipse Dryads and Meteor Golems have original animated atlases, combat stats and drops. Its original score, The Falling Garden, is a 5/4 F# minor chamber piece.
The Prismwaste is a level 45-50 salt and shattered-mirror desert beyond Astralhollow's northern Starbreak. Three broken mirror ridges lead from the Last Shade refuge to a northern crown. Its six enemy families are Mirage Jackals (45), Shard Scarabs (46), Glass Harriers (47), Prism Sentinels (48), the Mirror Queen (49, three-player elite) and the Sunshard (50, five-player elite). All six drop their own sellable materials; normal enemies roll the existing level-aware equipment pool, and outdoor elites use the guaranteed green, 33% blue and 5% purple rolls. Ten quests include a group hunt for each elite with a level-50 blue reward. A Travel Master links the refuge to Astralhollow. Its dybase2 score is Saltglass Mirage.
The Obsidian Orrery is a level 50-55 broken clock beyond the Prismwaste's eastern Hourglass Pass. Three open clock rings lead from the Stillpoint refuge to the northern Epoch Crown. Bronze Mantises (50), Gearlings (51), ranged Orbit Seers (52) and Chronoguards (53) drop distinct sellable materials. The Pendulum Matron (54, three-player elite) and Epoch Engine (55, five-player elite) guard separate courts and have separate group quests with level-55 blue rewards. The ten quests also pay two level-50 green pieces. Outdoor elite rarity rolls and party rules apply. A Travel Master connects the Stillpoint to the Last Shade. Six original animated enemy atlases and original scenery art render through the existing no-build client. Its dybase2 score is The Unwound Hour.
The Moonspore Canopy is a level 55-60 fungal forest beyond the Orrery's Moonfall Arch. Three luminous rings branch from Lamplight Refuge to separate courts for the Mireheart (58, three players), Silver Widow (59, three players) and Nightbloom (60, five players). Four ordinary kinds fill the ascent: Glowcap Grazers (55), Silkwings (56), Rootlurkers (57) and ranged Lantern Wraiths (58). All seven have distinct material drops, the ordinary gear pool includes level-60 equipment, and three separate elite quests give level-60 blue rewards. The refuge has twelve quests, healing, provisions, material sales and a Travel Master linked to the Stillpoint. Seven original editable 34-frame enemy atlases and original scenery art use the no-build client. Its dybase2 score is Lanterns in the Canopy.
chime: ring the tidebells. Twelve bronze bells hang in whalebone arches in three chains (the three Harbour Bells, the four Bells of the Net, the five Bells of Ran). Walk into a bell's circle and it rings for 35 / 45 / 50 seconds; walking in again restarts its time. The objective counts the bells of its chain that ring at this moment (the tracker shows 2/4 ringing) and is done when all of them ring at once, so you must learn where the bells are and then run the route faster than the first one goes silent (the generator proves that the slowest class can just manage it, at 45-80 % of the window). A fall silences every bell you rang. It is used by exactly three quests; the group quest against the Kraken needs the last chain first.python3 scripts/generate_deep.py (after the Bifrost generator; generate_bifrost.py re-applies the Maelstrom) then node scripts/sync-world.cjs.spawn_enemy | Put an enemy of a chosen kind and level a few steps from a bot (or at x/y), on free ground in its zone |
kill_enemies / respawn_enemy | Defeat enemies through the real kill path (XP, quest credit, gold and loot), revive one, or call the King now |
reset_character / set_god_mode | Clear cooldowns, refund stat points, toggle invulnerability mid-test |
setup_character | Reach a state in one call: level, gold, items, finished quests, then a teleport target |
wait_for_event / restart_world / debug_command | Wait for a new event, restart only the private server (to check persistence), or send a raw shortcut |