feat(spawners): virtual OnTick; SpawnerDto carries the group flag (#2640)

## Summary

Two small additive changes a derived spawner needs.

- **`BaseSpawner.OnTick()` is now `virtual`.** A subclass that gates spawning on external state (time windows, event triggers) must gate *timer* spawns without gating the manual `Spawn()` API, and `OnTick` is the only place the two paths differ: it is the timer callback and `Spawn()` is both what it calls and what commands and scripts call. Cost: one virtual dispatch on the existing timer callback; no change to stock behaviour.
- **`group` in the JSON DTO.** `BaseSpawner.Group` (all dead, then respawn) is binary-persisted but was missing from `SpawnerDto`, so it did not survive export/import. Added to the abstract record after `spawnLocationIsHome`, assigned in `ApplyDto` after `InitSpawn` (which resets it), and exported by the three stock `ToDto` implementations.

## Test plan

- [x] A derived spawner overriding `OnTick` with a closed gate: `OnTick()` spawns nothing; manual `Spawn()` still spawns and does not pass through `OnTick`.
- [x] DTO round trip with `Group = true` carries `group` and restores it on `ToSpawner()`.
- [x] `UOContent.Tests` full suite green.
- [ ] CI
This commit is contained in:
Kamron Batman 2026-09-12 16:33:57 -07:00 • committed by GitHub
parent d16166591c
commit c02909e2c8
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
7 changed files with 99 additions and 6 deletions

View file

@ -822,7 +822,11 @@ public abstract partial class BaseSpawner : Item, ISpawner
return remove && Spawned.Remove(spawned);
}
public void OnTick()
/// <summary>
/// Timer callback. Override to gate or reorder tick work; manual <see cref="Spawn"/> does not
/// pass through here.
/// </summary>
public virtual void OnTick()
{
if (_group)
{