--- id: annot-ondespawn name: @OnDespawn category: annotations kind: annotation tokens: @OnDespawn sig: @OnDespawn(Model, reason: r) handler Name { … } tip: Run a handler when a model instance is torn down, optionally knowing why. order: 50 --- @OnDespawn(Model) is the teardown counterpart to @OnSpawn: it fires once for each instance of the named model as it is removed, whether by an in-world despawn or at program shutdown, and its body still reads the instance's outgoing field values before they are gone. With the optional reason: r binding it becomes reason-carrying teardown — r is bound to an EndReason the compiler passes at each teardown site (an in-world despawn passes EndReason.Despawned, program exit passes EndReason.Quit) so one hook can branch on why the instance is ending, the way Unreal's EndPlay(reason) does. Every still-live instance's hook fires at quit, which makes "no silent deaths" real: drop loot on a real death but skip it when the app is simply closing. Add @Public to also emit a model_<Model>_despawn event. ```ludic program DespawnHook { property Health { current: int = 0 } property Loot { gold: int = 0 } model Enemy { Health, Loot } @OnDespawn(Enemy, reason: teardown_reason) handler DropLoot { match teardown_reason { EndReason.Quit => { } # app closing — do not drop loot _ => { print(Loot.gold) } # died in-world — award the gold } } handler SpawnWave phase Start { spawn Enemy { Health { current: 10 }, Loot { gold: 25 } } } handler KillOne phase Render { for (Health) in query [Health, {Enemy}] { despawn self() } # prints 25 quit() } } ```