fix: stop items from insta-decaying when decay eligibility is restored without a move (#2583)
## Summary A GM flipping `Movable` back on for a long-frozen item made it vanish within one scheduler tick. The setter registered the item with a deadline computed from its stale `LastMoved`, so `ProcessActiveQueue` deleted it almost immediately. The pre-#2311 save-time sweep had the same semantics, just hidden behind the save cadence. The same failure existed for `Visible` and `Spawner` transitions. `LastMoved` is deliberately left meaning actual movement — it feeds vendor inventory expiry and house moving-crate checks — so the fix does not rewrite it for state changes. ## Changes - **`DecayResetTime`** (CompactInfo-backed): the decay countdown runs from the later of `LastMoved` and this stamp. `RestartDecay()` stamps it only when the item can decay and the stamp extends the current deadline, so hot paths with a fresh `LastMoved` allocate nothing. - **`Movable`/`Visible`/`Spawner` setters** call `RestartDecay()` instead of registering a stale deadline. - **Region-refusal retry** in `DecayScheduler` uses `RestartDecay()` instead of rewriting `LastMoved`. - **Persistence**: the stamp survives save/load as a `WriteDeltaTime` delta under `SaveFlag.DecayReset` (to become `WriteAnchoredTime` once the save-time anchor is ported) (Item serialization v10), so a restart mid-window no longer deletes the item. - **`LastMoved` setter** drops a superseded stamp so the `CompactInfo` can collapse instead of being held (~40 bytes) forever. - **Raw `Map` setter** now counts as a move for parentless items: it stamps `LastMoved` and updates decay registration, closing the gap where an item moved out of `Map.Internal` via the setter never decayed. - **`LiftItemDupe`**: the remainder of a partially lifted *ground* stack was placed via raw `Location`/`Map` assignments and never enrolled for decay (lingering-trash leak since #2311) — now enrolled via the Map setter. Parented remainders get their map from `AddItem` (parent first, then map), so container splits never transit the scheduler.
This commit is contained in:
parent
fd27b7a3c9
commit
971d7b6a77
4 changed files with 397 additions and 10 deletions
|
|
@ -5250,7 +5250,13 @@ public partial class Mobile : IHued, IComparable<Mobile>, ISpawnable, IObjectPro
|
|||
|
||||
item.PlayerConstructed = oldItem.PlayerConstructed;
|
||||
item.Amount = oldAmount - amount;
|
||||
item.Map = oldItem.Map;
|
||||
|
||||
// A parented remainder gets its map from AddItem (parent first, then map), keeping the
|
||||
// split off the decay scheduler; a ground remainder is placed and enrolled here.
|
||||
if (oldItem.Parent == null)
|
||||
{
|
||||
item.Map = oldItem.Map;
|
||||
}
|
||||
|
||||
oldItem.OnAfterDuped(item);
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue