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);
}
}