Patch Notes · Dispatches from the Dungeon

The Chronicle

What we've built, what we've broken, what we've quietly buried on floor 4. Newest first.

◀Page 8 of 11 · v0.51.0 — v0.42.0▶
v0.51.0#May 3, 2026Feature

There Is No Eighth Gate

Or rather: there is one. Most maps disagree.

Behind the memorial stone at the centre of the hub, on a tile that's been bare grass since the overworld got its biomes, a new gate has started flickering into existence. You won't see it on your first visit. You won't see it on your tenth. It shows up only when you're carrying the right key — and the key only drops from bosses in the seven regular dungeons.

The drop is rare on purpose. One percent on Easy, two on Normal, five on Hard. Featured Dungeon active during the kill adds a small bonus on top. Drops never trigger inside the tower itself, so the loop has a definite end: get a key, walk into the gate, and the key dissolves on entry. Want another run? Earn another key.

What's behind the gate is still small for now — a five-floor descent ending on the Lich. The deeper version is two weeks away. Consider this part one of three.

If you find a key, the gate is in the patch of grass behind the memorial. You can't miss it.

v0.50.0#May 3, 2026Polish

Your Profile Looks Better When Shared

Paste your aot-game.com/u/<you> link into Discord, Slack, or anywhere with link previews, and the preview used to be the generic site favicon and a one-line meta description. Not exactly inviting.

Now the preview is a proper share card: 1200×630 pixels of black gradient, your username in oversized type, your featured character's class emblem on the right with their level and deepest floor, and a strip of stats underneath — deepest, top level, character count, hardcore clears. The same numbers your profile page shows, just compressed into a banner.

Profiles that haven't run anything yet still get a clean fallback card. Private and missing slugs render a "Profile not found" card so the share doesn't lie about whether the user exists.

If you've claimed a username, your link looks better today than it did yesterday. If you haven't, claim one in Settings.

v0.49.0#May 3, 2026Content

The Bestiary Got Filled In

The bestiary has been a half-empty encyclopedia. Six monsters had a paragraph of backstory; sixteen had nothing but their HP, their attack, and the suggestion that someone would write about them eventually. This is that eventually.

Every enemy in the game now has a write-up. The Bedroom Harpy roosts on a bed she shouldn't have access to, on a floor that shouldn't have beds. The Second-Shift Cultist worships something the dungeon hasn't agreed to. The Lich has read the patch notes. The Intern Necromancer is doing it wrong but doing it. The Retired Mummy still hits hard, but the bandages are coming off in a way they shouldn't be.

Every entry exists in English and Polish. The Polish names lean harder into the bureaucratic-fantasy register — Goblin-Harcerz, Mumia-Emerytka, Licz Ostatniego Patcha — so the prose follows.

Open the lore stone in the hub or visit /lore and read your way through. There are jokes hidden in the longer entries.

v0.48.0#May 3, 2026Feature

A Mysterious Stranger

Sprint 44 stretch — the third hub NPC. The Featured Dungeon rotates weekly, the Traveling Merchant shows up to sell, and now a cyan- tinted Mysterious Stranger posts up near the memorial twice a day with a job offer.

How it works

Same deterministic-window pattern as the merchant: two slots a day, 2 hours each, offset 6 h from the merchant so the two NPCs rarely overlap. Walk up, press [E], and pick the offered job — or decline and let it expire.

Three quest types so far, picked deterministically per window:

  • Slay N enemies of type X (8–15 of a specific bestiary entry)
  • Clear N floors (2–4)
  • Spend N gold in shops (100–299)

Reward is gold + a guaranteed rare or epic item. Rare quests pay 100g, epic quests 200g.

Quest UI

Once accepted, a cyan progress panel docks on the right side of the HUD with the task description, a fill bar, and a n / target label.

  • Kills tick when an enemy of the right type drops.
  • Floor clears tick on the existing floor-cleared event.
  • Gold-spend ticks every time useGold succeeds in the shop.

When the bar fills, a yellow Claim reward button appears — click delivers the gold + a random item of the rolled rarity, then the panel disappears.

Persistence

The active quest lives in the save blob — survives floor changes, death, scene-restart, and reloads. autoSave + loadState carry it through the same path that handles the dungeon seed bundle. Old saves without the field default to null.

Banner stack

Hub event banner now shows three rows when everything is up at once:

  • gold strip — Featured Dungeon
  • purple strip — Traveling Merchant
  • cyan strip — Mysterious Stranger

Each row repolls every 30 s so countdowns stay accurate without spamming render work.

Side notes

  • getQuestGiver reuses the merchant's daily-hours derivation (dailyMerchantHours) and just shifts by 6 h, so adding more hub events later is a one-line offset change.
  • Quest rewards filter out potions — only gear pool, so the player doesn't get "+1 minor health potion" as a quest payoff.
  • Dropping a quest mid-run is intentional — the ✕ in the panel header drops it without a confirmation. The next stranger window can offer something fresher.
v0.47.0#May 3, 2026Feature

The Hub Comes Alive (Again)

Sprint 37 made the hub a place — biomes, gates, river, memorial, lore stone. Then it sat there. Sprint 44 makes it a place that changes: a featured dungeon every week, a merchant who shows up twice a day for an hour, a banner that tells you what's live.

Featured Dungeon — rotating weekly buff

Every Monday at 00:00 UTC one of the seven dungeons becomes the Featured run for the week. The matching gate gets a pulsing gold ★ FEATURED ★ banner, the gate-card popover sprouts a gold strip explaining the buff, and the run picks up one of:

  • +50% gold — every coin pickup pays out 1.5×
  • +50% XP — every kill levels you faster
  • ×2 legendary drop chance — the rarity slot doubles its weight

Picks are deterministic from an FNV-1a hash of the ISO week — same dungeon and buff for every player on the planet, in MP and SP. The two hashes (dungeon + buff) are independent, so consecutive weeks rarely look identical.

This week (W18): Forest + ×2 legendary.

Traveling Merchant — twice a day, never twice the same

A purple-tinted NPC appears in the hub for two 60-minute windows per UTC day. He stocks 3-4 rare/epic items (~30% of windows include a legendary too) priced 3-5× their normal value. You miss the window, you miss the deal — same windowKey on every client means MP teammates see the same offers.

The merchant rotates between three spots in the hub (memorial side, north path, west path). Walk up, press [E] (or the on-screen button), buy or browse. The modal counts down MM:SS to the window close and auto-shuts when he leaves.

Hub event banner

Top-center on the overworld HUD: a single strip listing what's live right now. Always shows the Featured Dungeon + countdown to next rotation; stacks a purple strip below when the merchant is in town. Repolls every 30 s — ms-precision isn't useful for a once-an-hour event.

What landed

  • lib/hub/events.ts — pure helpers: getFeaturedDungeon(), getTravelingMerchant(), getTravelingMerchantStock(), msUntilNextWeek(), isoWeekKey(). FNV-1a + tiny LCG, no shared state, no server round-trips. Client + server can both call them and never disagree.
  • GameScene getFeaturedMults() stacks the buff into gainXp, the gold-pickup multiplier chain, and pickEquipmentByRarity (legendary slot weight). Non-featured runs see {1, 1, 1}, so SP balance stays unchanged outside the featured dungeon.
  • OverworldScene draws the gold ★ FEATURED ★ text banner above the matching gate, runs a 1 Hz pulse via tween. Spawns / despawns the merchant sprite (npc_shopkeeper tinted #a78bfa) on a 5-second poll so window-edge transitions land cleanly.
  • GateCardPopover gets a .oh-featured-strip band between the accent bar and the header when the dungeon is the week's featured pick — gold gradient with brightness pulse.
  • New <HubEventBanner /> component, mounted on OverworldScene-active state only. Stacks a second strip for the merchant when active.
  • New <MerchantModal /> reuses ModalShell + ShopBuyRow chrome — buy-only, no sell/identify tabs. Live countdown in the accent slot, auto-closes on window expiry, SOLD badge per-purchase.

Side notes

  • Buff stacking: featured XP buff layers ON TOP of class xpMult + difficulty xpMult — a Mage with Mystic Eye on a featured-XP Easy run gets a multiplicative party.
  • Merchant inventory rolls always include a rare and an epic even on unlucky LCG draws (filtered pool fallback). The wildcard slot never duplicates an already-picked item.
  • Future Sprint 44 stretches (hidden 8th dungeon trigger, quest giver NPC) will hook into the same banner — third + fourth strip slots when active.
v0.46.0#May 1, 2026Fix

Multiplayer, Patched

A multi-player run surfaced six bugs at once. None of them were giant on their own, but together they made co-op feel like single-player with confused teammates standing around in the dark.

What was wrong

  • The roster in the top-right showed Playroom Kit's auto-generated profile names ("RogueDuck", "PrettyZebra"), not the player's picked class.
  • Killing a single monster credited XP to everyone in the party. The XP-update tick fired locally on every client when the authoritative state showed an enemy at HP 0.
  • Other players cast no light. The lighting recompute ran FOV at the local player only — teammates were invisible flashlights.
  • The minimap and the multiplayer roster both anchored top-right on coarse-pointer devices and painted on top of each other.
  • Remote arrows were invisible. The attack broadcast carried only the melee fields (dir + damage); ranged hits applied damage but no visual.
  • Music started stuttering 1-2 minutes into a session. Each synth SFX call was creating + closing a fresh AudioContext, and Chrome's audio thread fell behind under MP combat load (4 players ⇒ ~10–30 contexts/sec).

What changed

  • Class names ship as displayName. Each peer broadcasts its picked class via setMyState after handleClassConfirm. The game-shell roster + the in-canvas nametag both prefer displayName over the Playroom profile name. Roster also polls every second so it updates as peers finish class-select.

  • XP attribution. New optional awardXpToLocal flag on killEnemy — defaults to true so SP behaviour stays. MP visualisation paths (applyRemoteEnemyState) pass false. When the host's processRemoteAttack lands a kill on a peer's behalf it broadcasts a kill-credit-<peerId> payload; the peer reads it on the next sync tick and adds XP locally. Kills attributed to whoever actually swung.

  • Remote-player FOV in the lighting OR. updateLighting now runs computeFov at each remote peer's tile and ORs the result into this.visibility. Peer crosses a tile boundary → lighting marked dirty so the recompute fires. Bounded by 4 players, so worst case is 3 extra computeFov calls per dirty frame.

  • MP roster moved to the upper-left under the status panel. Top-right column now reads as one consistent stack of icon buttons (📊 stats / 🎒 inventory / ⏸ pause / minimap) instead of competing with the roster chrome.

  • Cosmetic remote arrows. New rangedFire peer state carries spawn xy + velocity + range. Each client reads peer fires and tweens an arrow sprite along the same trajectory — pure visual, no physics, destroys itself after range / speed. Damage stays on the host's authoritative path.

  • Single shared synth AudioContext. Cached per scene and closed only on Phaser.Scenes.Events.SHUTDOWN. The per-call setTimeout(() => ctx.close(), N) everywhere in playSfxSynth is gone; only the connector teardown stays. BGM no longer competes with a churn of short-lived contexts on the same audio thread.

Side notes

  • creditKillsLastSeen is a monotonic counter — re-applying old credit after a reconnect can't double-pay because the host always strictly increases the total.
  • Remote arrow visual reads textures.exists("arrow") defensively so a peer joining mid-floor (asset still loading) silently drops the visual instead of crashing.
v0.45.0#April 29, 2026Feature

Pocket Trials

The game shipped a PWA manifest a sprint ago and an Android phone made a fresh point: the rest of the experience wasn't built for a phone. Joystick spawned in the wrong spot. Action buttons slid under the gesture bar. Modals overflowed. Half the time the hero swung in the direction of the joystick instead of at the closest enemy.

This week the mobile build catches up. New dungeons too, and a denser dungeon generator behind both.

Mobile / PWA polish

  • React virtual joystick replaces the rex joystick that was drifting on the overworld (the hub camera zooms 2× and rex's base/thumb GameObjects scale with it, so a tap at screen X=200 spawned the joystick at X=400). New component lives outside Phaser entirely — pointer events caught on the canvas, vector pushed to window.__AOT_JOYSTICK__ for both scenes to read.
  • Auto-aim closest enemy on touch. chooseCombatDir skips the "face the cursor" branch on coarse-pointer devices — the most recent pointer position on a phone is almost always the joystick drag, not an aimed cursor. Melee now lands toward whoever is closest. Ranged got the same treatment with a new free-aim angle helper, so arrows fly at the actual enemy instead of being snapped to a cardinal direction.
  • Tap-collapse HUD. Status panel toggles between full and compact (three thin gauges only — HP / stamina / XP, no text, no badges). Persisted via aot_hud_collapsed localStorage.
  • Tap-toggle minimap. Tapping the minimap collapses it to a 36×36 pin in the same corner; tapping the pin restores it. Reuses the [M] keypress wiring + lost_no_more achievement bridge.
  • Icon HudBtns matched to the pin chrome. STATS / INV / ⏸ render as 36×36 icon pills on any (pointer: coarse) device (was triggering off Tailwind's md: breakpoint, which leaked the labelled desktop variant onto landscape phones).
  • Action button cluster lifted. Bottom anchor is now max(env(safe-area-inset-bottom), 56px) + 24px — Android Chrome reports safe-area-inset-bottom: 0 even when the gesture nav paints over the page bottom, so the 56px floor guarantees ATK and Q stay above the system overlay.
  • Site nav hidden in play phase, PlayWindowChrome bars flattened on coarse pointer, modals clamped to 100dvh, html/body pinned to 100dvh so iOS PWA can't grow the document past the visible viewport mid-load.
  • Overworld now has a joystick — the hub had no movement input on touch after the on-screen D-pad was retired.
  • Mobile-input listener on OverworldScene. Tapping the on-screen E button now opens the gate-card popover; before it only fired in GameScene.

Two new dungeons

  • Mire Warrens (sewer, 5 floors, tier 2) — south-west pocket under the river. Plague swarm bestiary: rats, slimes, plague- bearers, drowned. Brick-arch gate with a 3-frame drip and a green sludge channel at the threshold.
  • Frozen Reach (ice, 10 floors, tier 3) — north-east pocket along the mountain ridge. Undead-knight bestiary: skeletons, wraiths, iron knights, void hounds, cultists. Frosted blue arch flanked by ice columns with a 4-frame shimmer drifting across the tunnel.

Both fill the two themes whose tile / BGM packs were already shipping but had no overworld gate. The hub now has gates for all seven dungeon themes. Two new exploration achievements come along for the ride: cleared_sewer (Plumber) and cleared_ice (Frozen Reach Cleared).

Denser dungeon generation

Sprint 40 doubled the map to 100×80 but the BSP generator still ran with split(root, 4) regardless of map size, capping out at ~12-16 rooms — most of the playable area was bare 30-tile corridor.

  • Split depth scales with map area — aim for ~one leaf per 144 tiles, so a 100×80 plans for ~55 leaves (depth ≈ 6) where it used to cap at 16. Legacy 50×40 still resolves to depth 4, so older callers don't change.
  • Closest-pair connectors. connectTree picks the Manhattan-closest leaf room across each split instead of the first match found in each subtree — top-level connectors stop snaking across half the map.
  • Cross-connection pass. Each room has a 25% chance to carve an extra L-corridor to its nearest non-adjacent room. Adds loops, lets enemies flank, breaks up the linear backbone. Probability is low so layouts stay legible (mostly tree-shaped), not mazey.

A 100×80 boss-test seed produced 36 rooms (32 monster / 1 shop / 1 treasure / 1 boss / 1 start), floor:corridor ratio 2.1:1 — was inverted before this change.

Side notes

  • Status / action panels softened from bg-black/65 to bg-black/40 so the world reads through the chrome. Minimap trim matches.
  • New themes.overworld i18n key (EN: Overworld, PL: Hub) drives the HUD floor chip when the player is in the hub instead of parroting the last dungeon's "P-1 · FOREST".
  • OverworldScene.cleanupBossRoom and the joystick state both live on window.__AOT_* globals — non-React components that need to talk to scenes (or vice versa) keep using the bridge pattern instead of importing through tangled chains.
v0.44.0#April 29, 2026Feature

Greater Depths

Every dungeon used to be three floors. Forest, Ruined, Crypt, Void — three floors each. Hellpit got five as a token gesture. Doesn't matter if you're a tutorial player or have cleared the game ten times: same length, same shape, same pacing. Nothing to commit to.

This week the dungeons branch into real tiers. Forest stays at three floors so the tutorial onboard doesn't change. Ruined stretches to five. Crypt is ten. Void is fifteen. Hellpit is twenty — a marathon dive that's an hour-plus session if you finish it. Pick your poison.

To make any of those longer dungeons playable, every floor also doubled in each linear dimension — 50×40 tiles became 100×80 tiles. Four times the explorable area, ten rooms per BSP instead of four, BFS corridors that actually let you wander.

To make a 100×80 floor navigable without losing your bearings, there's now a minimap in the top-right corner. Explored walls go gray, the corridor you can currently see lights brighter, the player's a gold blip with a halo, stairs and chests show as colored dots, and enemies in your line-of-sight turn into red dots. Press [M] to toggle it; the preference sticks across reloads.

What landed

  • Floor counts: Forest 3, Ruined 5, Crypt 10, Void 15, Hellpit 20. DUNGEON_TYPES.floorCount was already the source of truth — anti-cheat, boss-floor detection, and the HUD floor counter all picked up the new values automatically.
  • Map size 2× linear (4× area): 50×40 → 100×80 tiles. BSP generator's minRooms 4→10 and maxRoomSize 14→18 so the bigger canvas fills with rooms instead of empty corridors.
  • Dungeon minimap HUD (DungeonMinimap.tsx): canvas-based, 5 fps poll, reads the live visibility Uint8Array from the scene. Explored / visible / hidden tile states, plus markers for stairs (up green, down blue), chests (yellow/dark yellow), locked doors (red bar), and FOV-visible enemies (red dots). Default visible.
  • [M] toggle: keypress flips minimap visibility, persisted to localStorage.aot_minimap_visible. Toggle count tracked for the new lost_no_more achievement.
  • Cartographer ratio computation: floor-cleared event now carries exploredRatio = touched_walkable_tiles / total_walkable_tiles. Drives the new cartographer achievement.
  • Achievements:
    • marathoner — clear all 20 floors of the Hellpit
    • cartographer — explore ≥95% of a floor's walkable tiles
    • lost_no_more (target 50) — toggle the minimap with [M] 50 times across all your runs

Side notes

  • The chrome-style minimap art (gold rivets, skull/rune endcaps) is queued as Pack A but not blocking — the CSS-rendered border + dark inset reads as part of the same HUD family without a sprite asset.
  • The __ACHIEVEMENT_BRIDGE__ window hook lets non-React components (the minimap's [M] keypress handler) fire achievement unlocks without importing the tracker module through a tangled dependency chain.

Coming soon

  • Sprint 41 — Deep Floor Difficulty Curve is a planning stub for the inevitable balance pass. With Hellpit's 20-floor run live, we need to see how the existing per-floor enemy HP/damage scaling holds up at floor 15-20 — too easy and it's a slog, too hard and it's unreachable. We'll know after a couple of playthroughs.
v0.43.0#April 28, 2026Feature

They Shoot Back

Every enemy in this game used to walk up to you and swing. Goblin, skeleton, slime, harpy, imp — all melee, all the time. Player has had a bow and a fireball forever; enemies have had nothing. This week the imps spit fire, the harpies fan three feathers at once, the necromancers lob shadow bolts, and the cultists hurl blood spikes that knock you back two tiles.

Every ranged attack telegraphs. A red ! icon pulses over the enemy's head for 350-600ms before the shot fires — long enough to dash, sidestep, or break line-of-sight by ducking behind a wall. Walls and closed doors block projectiles so cover plays the way it should: walk into the doorway, the boulder hits stone, you walk out and finish them.

Each ranged enemy has its own glass-cannon balance. -30% HP vs the same-tier melee mob, +50% damage per shot, kite-back AI when you close in. Some types are hybrid (imp, harpy) — kite at range but bite if you corner them. The pure-ranged types (necromancer, cultist, plaguebearer) actively run from melee instead of swapping. Mixed encounters force flanking; ranged-only rooms become little puzzle boxes where you have to read the room and pick angles.

The boss-fight pipeline from last week (telegraphs, projectiles, line-of-sight) carried over cleanly — the same raycast util that stops a death beam now also stops a fireball spit, the same projectile spawner serves boss boulders and imp fireballs.

Cast

  • Imp (Forest, Hellpit): fast small fireballs, 1.5s cadence, hybrid — bite if cornered.
  • Harpy (Forest, Hellpit): 3-feather fan in a 30° spread, 2.0s cadence, hybrid.
  • Necromancer (Crypt, Void): heavy slow shadow bolt, 2.5s cadence, pure-ranged. Runs from melee.
  • Cultist (Crypt, Void): blood spike with 80px knockback, 1.8s cadence, pure-ranged.
  • Plaguebearer (Crypt, Hellpit): pus glob, 3.0s cadence, pure-ranged.

Achievements

  • Sniper Supreme (target 5) — kill 5 ranged enemies on a single floor before any of them fires.
  • Out of Range — clear a floor without taking damage from any projectile.
  • Glass Hunter (target 25) — defeat 25 ranged enemies cumulative.
  • Flank Attack — kill a ranged enemy from behind, breaking its line-of-sight on the way in.

Plus the per-dungeon clear achievements (Forest Cleared, Ruined Cleared, Crypt Master, Survived the Void, Hellpit Conqueror) now actually unlock when you complete a themed dungeon — they were defined but never wired to a fire.

What landed

  • lib/phaser/combat/los.ts — shared Bresenham raycast util. Walls + closed doors block. Boss specials (Sprint 38) and enemy ranged paths (this sprint) both call this.
  • lib/phaser/enemies/RangedAI.ts — state machine (idle / chasing / kiting / aiming / shooting / recovering) with mobile windup ×1.3 multiplier and hybrid melee fallback.
  • Per-enemy ranged config — 5 entries (imp, harpy, necromancer, cultist, plaguebearer) wired via resolveRangedAttackForType. Glass-cannon HP scale (-30%) at spawn time.
  • Aim alert sprite — 16×16 ! icon over enemy head during the aim state, 200ms-on/200ms-off frame swap (cheap, no tween).
  • Pack A — Ranged Projectile VFX (6 sprite sheets): fireball / feather / shadow bolt / blood spike / pus glob / alert. Animated at intended fps via existing texture-swap path.
  • 17 Suno SFX — per-enemy charge + release pairs, per-impact flesh hit variants, projectile-on-stone bounce, aim alert ping.
  • Per-dungeon clear achievements wired — Forest Cleared, Ruined Cleared, Crypt Master, Survived the Void, Hellpit Conqueror trigger off the boss-defeated event with the active dungeon snapshot.

Bugs killed

  • Dungeon BGM bled into the overworld — GameScene's SHUTDOWN handler dropped the i18n bridge but never stopped the active BGM Sound instance. After a successful run, the boss theme kept playing into OverworldScene's forest theme — two tracks at once. Now the SHUTDOWN handler explicitly .stop()s the current bgm.
  • Per-dungeon clear achievements never unlocked — the five cleared_* entries were defined in the achievements module but no caller ever called unlock(). Wired into the boss-defeated handler in GameHUD, snapshotting the active dungeon from the store before endActiveRun() clears it.
v0.42.0#April 28, 2026Feature

Bosses With Teeth

The three boss types in this game used to fight like every other mob. Bigger HP, bigger numbers, same chase-and-melee AI. The Stone Golem couldn't slam, the Trialmaster couldn't summon phantom blades, and the Lich didn't even cast a fireball. This week each boss becomes its own dance.

Every boss now has 2-3 special attacks, every special is telegraphed 600-1500ms before impact (so a dash window exists), and at half HP each one transitions into a phase 2 with extra moves and a faster tempo. There's a Souls-like HP banner across the bottom of the screen with the boss's name, a damage smear that flashes orange when you take a chunk off, and a phase pip that lights red when the boss enrages. The fight gets an intro stinger + camera shake + zoom punch when you walk in, and a death cinematic with white flash, slow-mo, gold rain, and a guaranteed reward chest when it falls.

The roster:

The Stone Golem (Forest, Ruined, Hellpit) ground-slams a 96px ring around itself, lobs boulders that arc across the room, and at half HP starts erupting earth-spike lines that walk toward you in sequence. The slam has the longest telegraph in the game — a growing red ring you can sprint out of if you read it.

The Trialmaster (Crypt, Void) phases between three quick spectral slashes that fan out 120° each, summons four phantom blades that home onto your last position, and at half HP starts vanishing into purple smoke and reappearing behind you for a heavy strike. The teleport has no telegraph at the destination — only the vanish puff, so you have to predict.

The Lich (Crypt, Void, Hellpit) fans five fireballs in a 90° arc, charges a death beam that takes 1.5s to fire and stretches across the room with a 3s burn DoT on hit, and at half HP starts summoning skeleton minions in pairs (max 4 on the field). The Death Beam telegraph is the most dangerous — line of red, the longest windup, but also the most damage if you eat it.

The hub got a save/resume fix too. If you save mid-run on floor 1 and come back, you can now use the stairs-up to drop your run and return to the overworld. Previously you were stuck in the dungeon until you cleared the boss or died. The new prompt reads "[E] Return to Overworld" — your XP, gold, and gear stay; only the dungeon progress is lost.

Achievement haul: ten new boss-themed unlocks, including Trinity Slayer (kill all three boss types on one character), Boss Master (10 cumulative kills), Untouchable (zero damage from specials in a fight), Lightning Round (60-second clear), Dance of Blades (5 phantom-blade dodges in a Trialmaster fight), Earth-Shaker Survivor (3 ground slams dodged in a Golem fight), Calm in the Storm (zero damage during phase 2), and per-boss slayer entries.

For dev / play-testing, three boss-arena URLs let you skip the overworld and drop straight into a 1v1 with the chosen boss:

  • /game?bossTest=golem
  • /game?bossTest=trial
  • /game?bossTest=lich

Single floor, no minor enemies, full-HP boss in the center room. First load is slow (~15s — the engine pre-fetches all 21 boss SFX

  • 4 BGM tracks + 13 telegraph/special VFX); subsequent fights in the same session boot instantly.

What landed

  • BossController + telegraph helpers + per-boss configs — new lib/phaser/bosses/ module. Controller handles weighted- random scheduling with cooldowns, phase transition at <50% HP, mobile windup ×1.3 multiplier, host-authoritative MP scheduling.
  • Boss HP HUD banner — BossHpBar mounts on boss-engaged via Phaser registry polling (chosen over event-emitter binding to dodge a HMR/StrictMode race that was eating the one-shot setdata emit), three-layer fill composite, lag-bar trails 700ms, damage smear flash, phase pip pulse.
  • Per-boss specials wired — Golem ground slam / boulder / earth spike, Trialmaster triple slash / phantom volley / teleport strike, Lich fireball volley / death beam / summon skeletons. Damage paths reuse the regular dodge + class reduction pipeline.
  • Encounter cinematic — camera shake + zoom pulse + intro stinger on engage, BGM crossfade to per-boss theme, boss-room cleanup despawns minor enemies on engagement.
  • Phase 2 transition — sprite tint shift gold→red, ember aura emitter trailing the boss, BGM rate ×1.15, dramatic synth swell stinger.
  • Death cinematic — white flash, 600ms screen shake, slow-mo (timeScale 0.3) for 800ms, 8-frame radial particle burst, 24-coin gold rain, reward chest spawn 1.2s delay. Skipable via Space/Enter/E/click.
  • 10 new achievements — boss_trinity boss_master untouchable_boss speedrun_boss dance_of_blades earth_shaker_survivor phase2_no_damage plus per-boss slayer trio (already existed). EN+PL i18n.
  • Boss test arena URLs — ?bossTest=<id> skips menu / character select / overworld, sets warrior default, picks matching themed dungeon for theme + bestiary, forces floor 0 as the boss floor, skips minor enemies.
  • Floor-0 stairs-up exits to overworld — [E] Return to Overworld prompt + endActiveRun() flush + scene start. The hand-built escape hatch for "I loaded a save and just want to back out of this run."
  • Pack A — Boss Telegraphs & Specials VFX (9 sheets, public/assets/vfx/{telegraphs,specials,status}/)
  • Pack B — Boss HP HUD Banner (7 layers, public/assets/hud/boss/)
  • Pack C — Boss Encounter Cinematic (6 assets, public/assets/vfx/boss_death/ + public/assets/props/)
  • 4 Suno BGM tracks + 21 SFX files (public/assets/audio/bgm/ + public/assets/audio/sfx/boss/)

Bugs killed

  • Boss specials passing through walls — projectiles (boulder, fireball, phantom blade) had no wall collider; line/cone damage (death beam, earth spike, triple slash) used pure geometry with no LOS check. Projectiles now collide+destroy on wallGroup / doorGroup, line/cone damage uses a Bresenham raycast over the tile grid before applying.
  • Boss HP HUD never appearing — Phaser's setdata-<key> is a one-shot emit on first set; React Strict Mode + HMR mount/unmount cycles meant our listener bound after the registry was already populated, missing the event. Replaced event-emitter subscription with 100ms polling of registry.get — bulletproof but a few cycles slower; latency invisible to the player.
  • Boss test arena spawning random mob enemies — spawnEnemies and loadMap had local isBossLevel calculations that didn't see the bossTestMode override (only the helper did). Routed both through isCurrentFloorBossLevel().
  • Floor-0 save softlock — saving on floor 1 (display) of a themed dungeon and reloading dropped the player back at floor 0 with no way to leave: stairs-up was gated currentFloor > 0, stairs-down only progresses deeper. Fixed via the new floor-0 exit-to-overworld path above.
▼ Older dispatches · Page 9