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:

TriggerWhat happens
Tile unlockedCombat 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 levelMultiplierMax offline
0×1.08 h
5×1.512 h
10 (max)×2.016 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

PropertyValue
StorageCloud-only (Supabase backend) — no local save file
Slots5 per account (SAVE_SLOT_COUNT)
Save format version4 (SAVE_VERSION) — mismatched versions are rejected as incompatible on load
Autosave pollEvery 1 second, change-detecting
In-combat write throttleAt most once per 15 seconds
Out-of-combat writesDebounced (only when the save signature actually changes)
On tab closeA 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:

  1. 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.
  2. 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 superseded and 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.