feat: Changes Spawner HomeRange to SpawnBounds (#2290)
### Summary This PR transitions the spawner system from a simple radius-based model to a flexible 3D boundary system. ### Core Changes * **Replaced `HomeRange` with `SpawnBounds`**: Spawners now use a `Rectangle3D` to define spawn areas instead of a circular integer range. * **Backward Compatibility**: * The `HomeRange` property remains as a helper that generates square `SpawnBounds` centered on the spawner. * Included a migration path (v10 to v11) that automatically converts old range data into new bounds during deserialization. * **Dynamic Bounds Shifting**: If a spawner is moved, its `SpawnBounds` will automatically shift with it, provided the bounds are currently configured as a centered square. * **New Spawn Logic**: Added `SpawnLocationIsHome` toggle. If enabled, spawned mobiles treat their exact spawn coordinates as their "Home" rather than the spawner's location. ### Implementation Details * **Interface Updates**: Updated `ISpawner` to include `WalkingRange`, `SpawnBounds`, and `IsInSpawnBounds()`. * **UI Enhancements**: The Spawner Controller Gump now displays "Custom" for complex bounds and allows copying of the new boundary properties between spawners. * **Refactored Constructors**: Streamlined `BaseSpawner`, `ProximitySpawner`, and `RegionSpawner` constructors to support the new data types.
This commit is contained in:
parent
bde072f81c
commit
0b34cc4417
16 changed files with 502 additions and 196 deletions
|
|
@ -0,0 +1,67 @@
|
|||
using System.Collections.Generic;
|
||||
using Server.Items;
|
||||
|
||||
namespace Server.Engines.Spawners;
|
||||
|
||||
public abstract partial class BaseSpawner
|
||||
{
|
||||
// Static dictionary to track pending HomeRange migrations from v10
|
||||
// Keyed by spawner instance, stores the HomeRange value to convert to SpawnBounds
|
||||
private static readonly Dictionary<BaseSpawner, int> _pendingHomeRangeMigrations = new();
|
||||
|
||||
private void MigrateFrom(V10Content content)
|
||||
{
|
||||
_guid = content.Guid;
|
||||
_returnOnDeactivate = content.ReturnOnDeactivate;
|
||||
_entries = content.Entries;
|
||||
_walkingRange = content.WalkingRange;
|
||||
_wayPoint = content.WayPoint;
|
||||
_group = content.Group;
|
||||
_minDelay = content.MinDelay;
|
||||
_maxDelay = content.MaxDelay;
|
||||
_team = content.Team;
|
||||
_end = content.End;
|
||||
|
||||
// Defer SpawnBounds calculation to AfterDeserialization when Location is available
|
||||
if (content.HomeRange > 0)
|
||||
{
|
||||
_pendingHomeRangeMigrations[this] = content.HomeRange;
|
||||
}
|
||||
|
||||
_spawnLocationIsHome = false;
|
||||
}
|
||||
|
||||
private void Deserialize(IGenericReader reader, int version)
|
||||
{
|
||||
_guid = reader.ReadGuid();
|
||||
_returnOnDeactivate = reader.ReadBool();
|
||||
|
||||
var count = reader.ReadInt();
|
||||
_entries = new List<SpawnerEntry>(count);
|
||||
|
||||
for (var i = 0; i < count; ++i)
|
||||
{
|
||||
var entry = new SpawnerEntry(this);
|
||||
entry.Deserialize(reader);
|
||||
_entries.Add(entry);
|
||||
}
|
||||
|
||||
_walkingRange = reader.ReadInt();
|
||||
_wayPoint = reader.ReadEntity<WayPoint>();
|
||||
_group = reader.ReadBool();
|
||||
_minDelay = reader.ReadTimeSpan();
|
||||
_maxDelay = reader.ReadTimeSpan();
|
||||
_count = reader.ReadInt();
|
||||
_team = reader.ReadInt();
|
||||
|
||||
// Store homeRange for migration in AfterDeserialization when Location is available
|
||||
var homeRange = reader.ReadInt();
|
||||
if (homeRange > 0)
|
||||
{
|
||||
_pendingHomeRangeMigrations[this] = homeRange;
|
||||
}
|
||||
|
||||
_running = reader.ReadBool();
|
||||
_end = _running ? reader.ReadDeltaTime() : Core.Now;
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue