Offline Progress & Saves
How offline catch-up replays your run, the offline cap, death handling, and cloud saves.
On this page
Leave with combat running and the game replays the time you were away the next time you load. This is not a statistical estimate — it is the real engine ticked forward, so the outcome is exactly what would have happened if you had left the tab open. That includes finding loot, gaining levels, clearing dungeons, using potions, taking durability damage — and dying.
When offline catch-up runs
The replay runs only if combat was active when you exited. If you stopped combat, sat in town, or were on a safe tile, offline time produces zero progress — you come back exactly as you left.
if (offlineSeconds <= 0 || !combatWasActive) → zero-progress summary, no replay
How the replay works
Catch-up steps the simulation in fixed 1/20-second ticks (20 tps) — the same rate as live play:
steps = floor(offlineSeconds / (1 / 20)) // = offlineSeconds × 20
Each tick runs the full stepSimulation, so every mechanic behaves normally: attacks, respawns,
potions, DoTs, buff expiry, drops, auto-sell, crafting completion, and death. Because it is the real
engine, the result is exact and can be dangerous — a long absence in a fight you can’t sustain will
replay your death, not hand-wave it.
Early-stop conditions
The replay stops fighting early on either of these, so you can react instead of replaying past a decision:
| Trigger | What happens |
|---|---|
| Tile unlocked | Combat is suspended so the unlock modal can show; it is running again when you take over. |
| Permanent death (Mediumcore / Hardcore) | Combat ends at the death so the new save state is captured and you can respond. |
A Softcore death does not halt the replay — you respawn and the catch-up keeps running. See Death Modes for what each mode loses on death.
Winding down to full health
Neither trigger ends the replay outright. Once combat is suspended, the remaining offline time keeps
ticking as out-of-combat regeneration (HERO_OUT_OF_COMBAT_REGEN_RATE = 1/60, so a full bar takes 60
seconds) until your health is back at 100%, and only then does the replay finish. You never come back
standing on a corpse or on the sliver of health a tile clear left you with — as long as enough offline
time was left to heal it. The same applies to a Softcore death: the replay idles through the respawn
regeneration before it stops.
A Hardcore lockout is the one exception — the save is dead, so there is nothing left to regenerate.
The recap prompt
When you load, an offline-progress recap summarizes kills, levels, gold, XP, dungeon clears, deaths,
and found items. It is suppressed for absences of 5 minutes or less (OFFLINE_PROMPT_MIN_SECONDS = 5 × 60), and also skipped if nothing meaningful changed:
show recap ⇔ offlineSeconds > 300 AND combatWasActive AND (any of:
kills, levels, gold≠0, dungeon clears, xp, deaths, or found items)
The offline cap
Offline time is clamped so it can’t accrue indefinitely:
maxOffline = 8 hours × (1 + 0.10 × offlineCapLevel)
The base cap is 8 hours (MAX_OFFLINE_SECONDS). The Offline Cap Bedroom upgrade
adds +10% per level up to level 10, doubling the cap to 16 hours at max.
| Offline Cap level | Multiplier | Max offline |
|---|---|---|
| 0 | ×1.0 | 8 h |
| 5 | ×1.5 | 12 h |
| 10 (max) | ×2.0 | 16 h |
Worked example — level 10: 8 h × (1 + 0.10 × 10) = 8 × 2.0 = 16 hours. Anything beyond 16 hours
of real absence is discarded; the replay only ever simulates up to the cap.
Saves
| Property | Value |
|---|---|
| Storage | Cloud-only (Supabase backend) — no local save file |
| Slots | 5 per account (SAVE_SLOT_COUNT) |
| Save format version | 4 (SAVE_VERSION) — mismatched versions are rejected as incompatible on load |
| Autosave poll | Every 1 second, change-detecting |
| In-combat write throttle | At most once per 15 seconds |
| Out-of-combat writes | Debounced (only when the save signature actually changes) |
| On tab close | A final guarded save fires on beforeunload |
A change-detecting poll runs every second; idle regen never changes the save signature, so it never schedules a redundant write. In-combat changes are throttled to one write per 15 seconds, and out-of-combat changes are debounced.
Single active session per character
Each character can only be actively played in one session at a time. When a session loads a character it claims the slot, and there are two layers enforcing exclusivity:
- Instant broadcast — a realtime channel (
char:<user>:<slot>) tells any older session that a new one has claimed the character, bouncing it to the menu. - Guarded write — every autosave is a conditional update that only succeeds while this session
still owns the slot. If another session has claimed it, the write returns
supersededand the losing session returns to the menu instead of clobbering the newer save.
The last session to load a character always wins. The guarded write is the source of truth, so correctness holds even if the instant broadcast is never delivered (e.g. a suspended mobile tab).
Anonymous guest sessions are supported (a real backend user with no email), and can later be
upgraded in place to a full account without migrating any save data. A leaderboard is served by the
get_leaderboard RPC.
See also
- Bedroom Upgrades — the Offline Cap upgrade that raises the 8 h → 16 h ceiling.
- Death Modes — how Softcore, Mediumcore, and Hardcore deaths differ, including during an offline replay.