using System; using System.Collections.Generic; using ModernUO.Serialization; namespace Server.Engines.Spawners; public abstract partial class BaseSpawner { /// /// The entries this spawner cycles through, owned by the concrete subclass so it can use its own /// entry type. Cold read-only view; loops inside BaseSpawner use . /// [IgnoreDupe] public abstract IReadOnlyList Entries { get; } /// Zero-cost span over the owner's list for hot loops (no interface dispatch, no allocation). protected abstract ReadOnlySpan EntrySpan { get; } /// Creates an entry of the owner's entry type, parented to this spawner. Not added. protected abstract SpawnerEntry CreateEntry( string name, int probability, int maxCount, string properties, string parameters ); protected abstract void AddEntryCore(SpawnerEntry entry); protected abstract bool RemoveEntryCore(SpawnerEntry entry); protected abstract void ClearEntriesCore(); /// /// Takes ownership of entries built elsewhere (a legacy save, a DTO import). The owner stores /// them, re-parents them, and converts foreign entry types if it must. Replaces the current list. /// /// /// An implementer that converts a foreign entry into its own entry type must carry the live spawns /// across with ; deliberately does not copy /// them, so a conversion that only clones orphans every creature the adopted entry owns. /// protected abstract void AdoptEntries(IReadOnlyList entries); /// /// Moves the live spawns of onto . Use when an owner converts /// an adopted entry into its own entry type; deliberately does not copy spawns. /// protected static void TransferSpawned(SpawnerEntry source, SpawnerEntry target) { var spawned = source.Spawned; for (var i = 0; i < spawned.Count; i++) { target.AddToSpawned(spawned[i]); } source.ClearSpawned(); } /// Deep-copies an entry into this spawner's entry type. Override to carry subtype fields. protected virtual SpawnerEntry CloneEntry(SpawnerEntry source) { var entry = CreateEntry( source.SpawnedName, source.SpawnedProbability, source.SpawnedMaxCount, source.Properties, source.Parameters ); entry.Disabled = source.Disabled; return entry; } public SpawnerEntry AddEntry( string creaturename, int probability = 100, int amount = 1, bool dotimer = true, string properties = null, string parameters = null ) { var entry = CreateEntry(creaturename, probability, amount, properties, parameters); AddEntryCore(entry); if (dotimer) { DoTimer(TimeSpan.FromSeconds(1)); } return entry; } public void RemoveEntry(SpawnerEntry entry) { Defrag(); if (!RemoveEntryCore(entry)) { return; } RemoveSpawn(entry); if (_running && !IsFull && _timer?.Running != true) { DoTimer(); } InvalidateProperties(); } /// /// Deletes every live spawn and removes every entry. Named for the deletion: before entry ownership /// moved to the owner, the generator emitted a ClearEntries() here that only emptied the list. /// public void RemoveAllEntries() { RemoveSpawns(); ClearEntriesCore(); InvalidateProperties(); } /// Replaces 's entries with clones of this spawner's entries. public void CopyEntriesTo(BaseSpawner target) { // A self-copy would clear the source. if (ReferenceEquals(target, this)) { return; } target.RemoveAllEntries(); var entries = EntrySpan; for (var i = 0; i < entries.Length; i++) { target.AddEntryCore(target.CloneEntry(entries[i])); } target.InvalidateProperties(); } /// /// Rebuilds the entity -> entry registry from the owner's entries and re-arms the timer. /// The owner calls this from its own [AfterDeserialization] once its list is loaded; the base /// hook runs before derived fields exist and must not touch entries. /// /// /// calls this for its own entry list only. A subclass that owns a different /// list (its own entry type, or an extra list) must call it again from its own /// [AfterDeserialization]: the base class's runs before the derived fields have been read, /// so the load would otherwise finish with an empty registry even though the /// entries themselves carry their spawns. /// protected void RebuildSpawned() { Spawned = new Dictionary(); var entries = EntrySpan; for (var i = 0; i < entries.Length; i++) { var entry = entries[i]; var spawned = entry.Spawned; for (var j = 0; j < spawned.Count; j++) { Spawned.TryAdd(spawned[j], entry); } } DoTimer(_end - Core.Now); } }