Appearance
How to Build: Death & Level Reset
Death is inevitable in an auto-battler — especially when you're pushing into content that's slightly too hard. The question is: how punishing should it feel? In Endless Idle, the answer is "not very." Player death is a soft reset that preserves everything meaningful while giving you a fresh map to explore. Enemy death is the engine that drives progression forward.
Player death: a fresh start, not a punishment
When the player's HP hits zero, three things happen in order:
- Progress is saved. Experience, gold, inventory, skill levels, checkpoint unlocks — all preserved. The player never loses progress from dying.
- The level regenerates. A new random seed is rolled and the entire level is rebuilt from scratch using the WFC pipeline. Same level number, completely new layout.
- The player restarts. Health resets to full, combat state clears, position resets to the start of the level.
typescript
function onPlayerDeath(world: World): voidThe key design decision here is that the player entity is never tagged with IsDead. Death is detected by checking Health.current <= 0 and handled immediately — save, regenerate, reset. This avoids a whole class of edge cases where other systems might try to interact with a "dead" player entity during the same tick.
Notice how the auto-save fires before level regeneration. If the player closes the browser mid-regeneration, they'll still have their progress when they come back.
Enemy death: gold and a timer
When an enemy dies, the handler is simpler but just as important for game feel.
typescript
function onEnemyDeath(enemy: Entity, world: World): voidThe handler does three things:
- Grants gold to the player, reading the pre-rolled amount from
EnemyData.goldDrop. - Clears combat state so the enemy stops being considered in aggro and targeting queries.
- Starts the respawn timer by setting
RespawnTimer.ticksRemainingto the enemy'srespawnTicksvalue.
The respawn system (covered in the enemy spawning guide) handles the countdown and resurrection.
Boss death: level complete
When the dying enemy has the IsLevelBoss tag, an additional step fires after the gold grant.
typescript
function completeLevel(world: World): voidThis marks the current level as complete, unlocks the next level checkpoint in the player's progression data, and triggers whatever transition the game needs — whether that's an immediate advance to the next level or a UI prompt celebrating the victory.
The boss doesn't respawn. Once it's dead, the level is done. This gives the level a clear arc: fight through clusters, optionally tackle camps, reach the boss, win.
The save-before-reset pattern
One subtle but important detail: saveGame runs before startLevel. This matters because startLevel destroys all enemy entities, regenerates terrain, and repositions the player. If the save happened after regeneration, we'd be saving the new level state instead of the completed progress.
The save captures:
- Current gold balance (including the gold from the fight that killed the player)
- All skill levels and experience
- Inventory contents
- Checkpoint unlocks
- Consumable settings
Where death detection lives
Death checking happens inside the combat tick system, after all attacks for the current tick have resolved. This ensures that a player and enemy can kill each other on the same tick — both deaths are detected in the same sweep. The player death handler fires after enemy deaths are processed, so the player still gets gold and experience from enemies they killed on the same tick they died.
Relevant files
src/core/systems/handle-death.ts — Death detection and response handlersWe now have a complete life-and-death cycle. Enemies die, drop gold, and come back. The player dies, keeps everything, and gets a new world to explore. Next, let's look at exactly how those gold drops work.