fix: Codegens spawners (#1678)
This commit is contained in:
parent
48e47f9352
commit
5ada7a4e50
19 changed files with 1494 additions and 1604 deletions
25
Projects/Server/Json/SerializedJsonIgnore.cs
Normal file
25
Projects/Server/Json/SerializedJsonIgnore.cs
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
/*************************************************************************
|
||||
* ModernUO *
|
||||
* Copyright 2019-2024 - ModernUO Development Team *
|
||||
* Email: hi@modernuo.com *
|
||||
* File: SerializedJsonIgnore.cs *
|
||||
* *
|
||||
* This program is free software: you can redistribute it and/or modify *
|
||||
* it under the terms of the GNU General Public License as published by *
|
||||
* the Free Software Foundation, either version 3 of the License, or *
|
||||
* (at your option) any later version. *
|
||||
* *
|
||||
* You should have received a copy of the GNU General Public License *
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>. *
|
||||
*************************************************************************/
|
||||
|
||||
using System;
|
||||
using System.Text.Json.Serialization;
|
||||
using ModernUO.Serialization;
|
||||
|
||||
namespace Server.Json;
|
||||
|
||||
[AttributeUsage(AttributeTargets.Property | AttributeTargets.Field)]
|
||||
public class SerializedJsonIgnoreAttribute : SerializedPropertyAttrAttribute<JsonIgnoreAttribute>
|
||||
{
|
||||
}
|
||||
26
Projects/Server/Json/SerializedJsonPropertyName.cs
Normal file
26
Projects/Server/Json/SerializedJsonPropertyName.cs
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
/*************************************************************************
|
||||
* ModernUO *
|
||||
* Copyright 2019-2024 - ModernUO Development Team *
|
||||
* Email: hi@modernuo.com *
|
||||
* File: SerializedJsonPropertyName.cs *
|
||||
* *
|
||||
* This program is free software: you can redistribute it and/or modify *
|
||||
* it under the terms of the GNU General Public License as published by *
|
||||
* the Free Software Foundation, either version 3 of the License, or *
|
||||
* (at your option) any later version. *
|
||||
* *
|
||||
* You should have received a copy of the GNU General Public License *
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>. *
|
||||
*************************************************************************/
|
||||
|
||||
using System;
|
||||
using System.Text.Json.Serialization;
|
||||
using ModernUO.Serialization;
|
||||
|
||||
namespace Server.Json;
|
||||
|
||||
[AttributeUsage(AttributeTargets.Property | AttributeTargets.Field)]
|
||||
public class SerializedJsonPropertyNameAttribute(string name) : SerializedPropertyAttrAttribute<JsonPropertyNameAttribute>
|
||||
{
|
||||
public string Name { get; } = name;
|
||||
}
|
||||
|
|
@ -3,6 +3,7 @@ using System.Collections.Generic;
|
|||
using System.IO;
|
||||
using System.Reflection;
|
||||
using System.Text.Json;
|
||||
using ModernUO.Serialization;
|
||||
using Server.Commands;
|
||||
using Server.Items;
|
||||
using Server.Json;
|
||||
|
|
@ -12,21 +13,59 @@ using static Server.Attributes;
|
|||
|
||||
namespace Server.Engines.Spawners;
|
||||
|
||||
public abstract class BaseSpawner : Item, ISpawner
|
||||
[SerializationGenerator(10, false)]
|
||||
public abstract partial class BaseSpawner : Item, ISpawner
|
||||
{
|
||||
private static WarnTimer m_WarnTimer;
|
||||
|
||||
[SerializableField(0)]
|
||||
[SerializedCommandProperty(AccessLevel.Developer)]
|
||||
private Guid _guid;
|
||||
private int m_Count;
|
||||
private bool m_Group;
|
||||
private int m_HomeRange;
|
||||
private TimeSpan m_MaxDelay;
|
||||
private TimeSpan m_MinDelay;
|
||||
private bool m_Running;
|
||||
private int m_Team;
|
||||
|
||||
private InternalTimer m_Timer;
|
||||
private int m_WalkingRange = -1;
|
||||
[SerializableField(1)]
|
||||
[SerializedCommandProperty(AccessLevel.Developer)]
|
||||
private bool _returnOnDeactivate;
|
||||
|
||||
[SerializableField(2, setter: "private")]
|
||||
private List<SpawnerEntry> _entries;
|
||||
|
||||
[InvalidateProperties]
|
||||
[SerializableField(3)]
|
||||
[SerializedCommandProperty(AccessLevel.Developer)]
|
||||
private int _walkingRange = -1;
|
||||
|
||||
[SerializableField(4)]
|
||||
[SerializedCommandProperty(AccessLevel.Developer)]
|
||||
private WayPoint _wayPoint;
|
||||
|
||||
[InvalidateProperties]
|
||||
[SerializableField(5)]
|
||||
[SerializedCommandProperty(AccessLevel.Developer)]
|
||||
private bool _group;
|
||||
|
||||
[InvalidateProperties]
|
||||
[SerializableField(6)]
|
||||
[SerializedCommandProperty(AccessLevel.Developer)]
|
||||
private TimeSpan _minDelay;
|
||||
|
||||
[InvalidateProperties]
|
||||
[SerializableField(7)]
|
||||
[SerializedCommandProperty(AccessLevel.Developer)]
|
||||
private TimeSpan _maxDelay;
|
||||
|
||||
[InvalidateProperties]
|
||||
[SerializableField(9)]
|
||||
[SerializedCommandProperty(AccessLevel.Developer)]
|
||||
private int _team;
|
||||
|
||||
[InvalidateProperties]
|
||||
[SerializableField(10)]
|
||||
[SerializedCommandProperty(AccessLevel.Developer)]
|
||||
private int _homeRange;
|
||||
|
||||
[SerializableField(12)]
|
||||
[SerializedCommandProperty(AccessLevel.Developer)]
|
||||
private DateTime _end;
|
||||
|
||||
private InternalTimer _timer;
|
||||
|
||||
public BaseSpawner() : this(1, TimeSpan.FromMinutes(5), TimeSpan.FromMinutes(10), 0, 4)
|
||||
{
|
||||
|
|
@ -88,7 +127,7 @@ public abstract class BaseSpawner : Item, ISpawner
|
|||
json.GetProperty("team", options, out int team);
|
||||
json.GetProperty("homeRange", options, out int homeRange);
|
||||
json.GetProperty("walkingRange", options, out int walkingRange);
|
||||
m_WalkingRange = walkingRange;
|
||||
_walkingRange = walkingRange;
|
||||
|
||||
InitSpawn(amount, minDelay, maxDelay, team, homeRange);
|
||||
|
||||
|
|
@ -100,54 +139,40 @@ public abstract class BaseSpawner : Item, ISpawner
|
|||
}
|
||||
}
|
||||
|
||||
public BaseSpawner(Serial serial) : base(serial)
|
||||
{
|
||||
}
|
||||
|
||||
public override string DefaultName => "Spawner";
|
||||
public bool IsFull => Spawned?.Count >= m_Count;
|
||||
public bool IsFull => Spawned?.Count >= _count;
|
||||
public bool IsEmpty => Spawned?.Count == 0;
|
||||
public DateTime End { get; set; }
|
||||
|
||||
public List<SpawnerEntry> Entries { get; private set; }
|
||||
|
||||
public Dictionary<ISpawnable, SpawnerEntry> Spawned { get; private set; }
|
||||
|
||||
[CommandProperty(AccessLevel.Developer)]
|
||||
public Guid Guid
|
||||
{
|
||||
get => _guid;
|
||||
set => _guid = value;
|
||||
}
|
||||
|
||||
[SerializableProperty(8)]
|
||||
[CommandProperty(AccessLevel.Developer)]
|
||||
public int Count
|
||||
{
|
||||
get => m_Count;
|
||||
get => _count;
|
||||
set
|
||||
{
|
||||
m_Count = value;
|
||||
_count = value;
|
||||
|
||||
if (IsFull)
|
||||
{
|
||||
m_Timer?.Stop();
|
||||
_timer?.Stop();
|
||||
}
|
||||
else if (m_Timer?.Running != true)
|
||||
else if (_timer?.Running != true)
|
||||
{
|
||||
DoTimer();
|
||||
}
|
||||
|
||||
InvalidateProperties();
|
||||
this.MarkDirty();
|
||||
}
|
||||
}
|
||||
|
||||
[CommandProperty(AccessLevel.Developer)]
|
||||
public virtual WayPoint WayPoint { get; set; }
|
||||
|
||||
[SerializableProperty(11)]
|
||||
[CommandProperty(AccessLevel.Developer)]
|
||||
public bool Running
|
||||
{
|
||||
get => m_Running;
|
||||
get => _running;
|
||||
set
|
||||
{
|
||||
if (value)
|
||||
|
|
@ -160,57 +185,14 @@ public abstract class BaseSpawner : Item, ISpawner
|
|||
}
|
||||
|
||||
InvalidateProperties();
|
||||
}
|
||||
}
|
||||
|
||||
[CommandProperty(AccessLevel.Developer)]
|
||||
public int WalkingRange
|
||||
{
|
||||
get => m_WalkingRange;
|
||||
set
|
||||
{
|
||||
m_WalkingRange = value;
|
||||
InvalidateProperties();
|
||||
}
|
||||
}
|
||||
|
||||
[CommandProperty(AccessLevel.Developer)]
|
||||
public int Team
|
||||
{
|
||||
get => m_Team;
|
||||
set
|
||||
{
|
||||
m_Team = value;
|
||||
InvalidateProperties();
|
||||
}
|
||||
}
|
||||
|
||||
[CommandProperty(AccessLevel.Developer)]
|
||||
public TimeSpan MinDelay
|
||||
{
|
||||
get => m_MinDelay;
|
||||
set
|
||||
{
|
||||
m_MinDelay = value;
|
||||
InvalidateProperties();
|
||||
}
|
||||
}
|
||||
|
||||
[CommandProperty(AccessLevel.Developer)]
|
||||
public TimeSpan MaxDelay
|
||||
{
|
||||
get => m_MaxDelay;
|
||||
set
|
||||
{
|
||||
m_MaxDelay = value;
|
||||
InvalidateProperties();
|
||||
this.MarkDirty();
|
||||
}
|
||||
}
|
||||
|
||||
[CommandProperty(AccessLevel.Developer)]
|
||||
public TimeSpan NextSpawn
|
||||
{
|
||||
get => m_Running && m_Timer?.Running == true ? End - Core.Now : TimeSpan.Zero;
|
||||
get => _running && _timer?.Running == true ? End - Core.Now : TimeSpan.Zero;
|
||||
set
|
||||
{
|
||||
Start();
|
||||
|
|
@ -218,34 +200,9 @@ public abstract class BaseSpawner : Item, ISpawner
|
|||
}
|
||||
}
|
||||
|
||||
[CommandProperty(AccessLevel.Developer)]
|
||||
public bool Group
|
||||
{
|
||||
get => m_Group;
|
||||
set
|
||||
{
|
||||
m_Group = value;
|
||||
InvalidateProperties();
|
||||
}
|
||||
}
|
||||
|
||||
[CommandProperty(AccessLevel.Developer)]
|
||||
public bool ReturnOnDeactivate { get; set; }
|
||||
|
||||
public virtual Point3D HomeLocation => Location;
|
||||
public bool UnlinkOnTaming => true;
|
||||
|
||||
[CommandProperty(AccessLevel.Developer)]
|
||||
public int HomeRange
|
||||
{
|
||||
get => m_HomeRange;
|
||||
set
|
||||
{
|
||||
m_HomeRange = value;
|
||||
InvalidateProperties();
|
||||
}
|
||||
}
|
||||
|
||||
Region ISpawner.Region => Region.Find(Location, Map);
|
||||
|
||||
public void UpdateEntries(List<SpawnerEntry> entries)
|
||||
|
|
@ -266,7 +223,7 @@ public abstract class BaseSpawner : Item, ISpawner
|
|||
Spawned.Remove(spawn);
|
||||
}
|
||||
|
||||
if (m_Running && !IsFull && m_Timer?.Running == false)
|
||||
if (_running && !IsFull && _timer?.Running == false)
|
||||
{
|
||||
DoTimer();
|
||||
}
|
||||
|
|
@ -276,7 +233,7 @@ public abstract class BaseSpawner : Item, ISpawner
|
|||
{
|
||||
RemoveSpawns();
|
||||
|
||||
for (var i = 0; i < m_Count; i++)
|
||||
for (var i = 0; i < _count; i++)
|
||||
{
|
||||
Spawn();
|
||||
}
|
||||
|
|
@ -339,7 +296,7 @@ public abstract class BaseSpawner : Item, ISpawner
|
|||
string parameters = null
|
||||
)
|
||||
{
|
||||
var entry = new SpawnerEntry(creaturename, probability, amount, properties, parameters);
|
||||
var entry = new SpawnerEntry(this, creaturename, probability, amount, properties, parameters);
|
||||
Entries.Add(entry);
|
||||
if (dotimer)
|
||||
{
|
||||
|
|
@ -353,13 +310,13 @@ public abstract class BaseSpawner : Item, ISpawner
|
|||
{
|
||||
Visible = false;
|
||||
Movable = false;
|
||||
m_Running = true;
|
||||
m_Group = false;
|
||||
m_MinDelay = minDelay;
|
||||
m_MaxDelay = maxDelay;
|
||||
m_Count = amount;
|
||||
m_Team = team;
|
||||
m_HomeRange = homeRange;
|
||||
_running = true;
|
||||
_group = false;
|
||||
_minDelay = minDelay;
|
||||
_maxDelay = maxDelay;
|
||||
_count = amount;
|
||||
_team = team;
|
||||
_homeRange = homeRange;
|
||||
Entries = new List<SpawnerEntry>();
|
||||
Spawned = new Dictionary<ISpawnable, SpawnerEntry>();
|
||||
|
||||
|
|
@ -383,16 +340,16 @@ public abstract class BaseSpawner : Item, ISpawner
|
|||
{
|
||||
base.GetProperties(list);
|
||||
|
||||
if (m_Running)
|
||||
if (_running)
|
||||
{
|
||||
list.Add(1060742); // active
|
||||
|
||||
list.Add(1060656, m_Count); // amount to make: ~1_val~
|
||||
list.Add(1061169, m_HomeRange); // range ~1_val~
|
||||
list.Add(1050039, $"{"walking range:"}\t{m_WalkingRange}"); // ~1_NUMBER~ ~2_ITEMNAME~
|
||||
list.Add(1053099, $"{"group:"}\t{m_Group}"); // ~1_oretype~: ~2_armortype~
|
||||
list.Add(1060847, $"{"team:"}\t{m_Team}"); // ~1_val~ ~2_val~
|
||||
list.Add(1063483, $"{"delay:"}\t{m_MinDelay} to {m_MaxDelay}"); // ~1_MATERIAL~: ~2_ITEMNAME~
|
||||
list.Add(1060656, _count); // amount to make: ~1_val~
|
||||
list.Add(1061169, _homeRange); // range ~1_val~
|
||||
list.Add(1050039, $"{"walking range:"}\t{_walkingRange}"); // ~1_NUMBER~ ~2_ITEMNAME~
|
||||
list.Add(1053099, $"{"group:"}\t{_group}"); // ~1_oretype~: ~2_armortype~
|
||||
list.Add(1060847, $"{"team:"}\t{_team}"); // ~1_val~ ~2_val~
|
||||
list.Add(1063483, $"{"delay:"}\t{_minDelay} to {_maxDelay}"); // ~1_MATERIAL~: ~2_ITEMNAME~
|
||||
|
||||
GetSpawnerProperties(list);
|
||||
|
||||
|
|
@ -412,7 +369,7 @@ public abstract class BaseSpawner : Item, ISpawner
|
|||
{
|
||||
base.OnSingleClick(from);
|
||||
|
||||
if (m_Running)
|
||||
if (_running)
|
||||
{
|
||||
LabelTo(from, "[Running]");
|
||||
}
|
||||
|
|
@ -424,11 +381,11 @@ public abstract class BaseSpawner : Item, ISpawner
|
|||
|
||||
public void Start()
|
||||
{
|
||||
if (!m_Running)
|
||||
if (!_running)
|
||||
{
|
||||
if (Entries.Count > 0)
|
||||
{
|
||||
m_Running = true;
|
||||
_running = true;
|
||||
DoTimer();
|
||||
}
|
||||
}
|
||||
|
|
@ -436,8 +393,8 @@ public abstract class BaseSpawner : Item, ISpawner
|
|||
|
||||
public void Stop()
|
||||
{
|
||||
m_Timer?.Stop();
|
||||
m_Running = false;
|
||||
_timer?.Stop();
|
||||
_running = false;
|
||||
}
|
||||
|
||||
public void Defrag()
|
||||
|
|
@ -472,7 +429,7 @@ public abstract class BaseSpawner : Item, ISpawner
|
|||
|
||||
public void OnTick()
|
||||
{
|
||||
if (m_Group)
|
||||
if (_group)
|
||||
{
|
||||
Defrag();
|
||||
|
||||
|
|
@ -737,12 +694,12 @@ public abstract class BaseSpawner : Item, ISpawner
|
|||
{
|
||||
var walkrange = GetWalkingRange();
|
||||
|
||||
c.RangeHome = walkrange >= 0 ? walkrange : m_HomeRange;
|
||||
c.RangeHome = walkrange >= 0 ? walkrange : _homeRange;
|
||||
c.CurrentWayPoint = WayPoint;
|
||||
|
||||
if (m_Team > 0)
|
||||
if (_team > 0)
|
||||
{
|
||||
c.Team = m_Team;
|
||||
c.Team = _team;
|
||||
}
|
||||
|
||||
c.Home = Location;
|
||||
|
|
@ -784,19 +741,19 @@ public abstract class BaseSpawner : Item, ISpawner
|
|||
return true;
|
||||
}
|
||||
|
||||
public virtual int GetWalkingRange() => m_WalkingRange;
|
||||
public virtual int GetWalkingRange() => _walkingRange;
|
||||
|
||||
public virtual Map GetSpawnMap() => Map;
|
||||
|
||||
public void DoTimer()
|
||||
{
|
||||
if (!m_Running)
|
||||
if (!_running)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var min = (long)m_MinDelay.TotalMilliseconds;
|
||||
var max = (long)m_MaxDelay.TotalMilliseconds;
|
||||
var min = (long)_minDelay.TotalMilliseconds;
|
||||
var max = (long)_maxDelay.TotalMilliseconds;
|
||||
|
||||
var delay = TimeSpan.FromMilliseconds(Utility.RandomMinMax(min, max));
|
||||
DoTimer(delay);
|
||||
|
|
@ -804,26 +761,26 @@ public abstract class BaseSpawner : Item, ISpawner
|
|||
|
||||
public virtual void DoTimer(TimeSpan delay)
|
||||
{
|
||||
if (!m_Running)
|
||||
if (!_running)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
End = Core.Now + delay;
|
||||
|
||||
if (m_Timer == null)
|
||||
if (_timer == null)
|
||||
{
|
||||
m_Timer = new InternalTimer(this, delay);
|
||||
_timer = new InternalTimer(this, delay);
|
||||
}
|
||||
else
|
||||
{
|
||||
m_Timer.Stop();
|
||||
m_Timer.Delay = delay;
|
||||
_timer.Stop();
|
||||
_timer.Delay = delay;
|
||||
}
|
||||
|
||||
if (!IsFull)
|
||||
{
|
||||
m_Timer.Start();
|
||||
_timer.Start();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -847,7 +804,7 @@ public abstract class BaseSpawner : Item, ISpawner
|
|||
|
||||
Entries.Remove(entry);
|
||||
|
||||
if (m_Running && !IsFull && m_Timer?.Running == false)
|
||||
if (_running && !IsFull && _timer?.Running == false)
|
||||
{
|
||||
DoTimer();
|
||||
}
|
||||
|
|
@ -899,7 +856,7 @@ public abstract class BaseSpawner : Item, ISpawner
|
|||
}
|
||||
}
|
||||
|
||||
if (m_Running && !IsFull && m_Timer?.Running == false)
|
||||
if (_running && !IsFull && _timer?.Running == false)
|
||||
{
|
||||
DoTimer();
|
||||
}
|
||||
|
|
@ -925,294 +882,54 @@ public abstract class BaseSpawner : Item, ISpawner
|
|||
RemoveSpawns();
|
||||
}
|
||||
|
||||
public override void Serialize(IGenericWriter writer)
|
||||
private void Deserialize(IGenericReader reader, int version)
|
||||
{
|
||||
base.Serialize(writer);
|
||||
|
||||
writer.Write(9); // version
|
||||
|
||||
writer.Write(_guid);
|
||||
|
||||
writer.Write(ReturnOnDeactivate);
|
||||
|
||||
writer.Write(Entries.Count);
|
||||
|
||||
for (var i = 0; i < Entries.Count; ++i)
|
||||
{
|
||||
Entries[i].Serialize(writer);
|
||||
}
|
||||
|
||||
writer.Write(m_WalkingRange);
|
||||
|
||||
writer.Write(WayPoint);
|
||||
|
||||
writer.Write(m_Group);
|
||||
|
||||
writer.Write(m_MinDelay);
|
||||
writer.Write(m_MaxDelay);
|
||||
writer.Write(m_Count);
|
||||
writer.Write(m_Team);
|
||||
writer.Write(m_HomeRange);
|
||||
writer.Write(m_Running);
|
||||
|
||||
if (m_Running)
|
||||
{
|
||||
writer.WriteDeltaTime(End);
|
||||
}
|
||||
}
|
||||
|
||||
public override void Deserialize(IGenericReader reader)
|
||||
{
|
||||
base.Deserialize(reader);
|
||||
|
||||
var version = reader.ReadInt();
|
||||
|
||||
Spawned = new Dictionary<ISpawnable, SpawnerEntry>();
|
||||
|
||||
if (version < 7)
|
||||
_guid = reader.ReadGuid();
|
||||
_returnOnDeactivate = reader.ReadBool();
|
||||
_entries = new List<SpawnerEntry>(reader.ReadInt());
|
||||
|
||||
for (var i = 0; i < _entries.Count; ++i)
|
||||
{
|
||||
Entries = new List<SpawnerEntry>();
|
||||
var entry = new SpawnerEntry(this);
|
||||
entry.Deserialize(reader);
|
||||
_entries.Add(entry);
|
||||
}
|
||||
|
||||
switch (version)
|
||||
_walkingRange = reader.ReadInt();
|
||||
_wayPoint = reader.ReadEntity<WayPoint>();
|
||||
_group = reader.ReadBool();
|
||||
_minDelay = reader.ReadTimeSpan();
|
||||
_maxDelay = reader.ReadTimeSpan();
|
||||
_count = reader.ReadInt();
|
||||
_team = reader.ReadInt();
|
||||
_homeRange = reader.ReadInt();
|
||||
_running = reader.ReadBool();
|
||||
|
||||
_end = _running ? reader.ReadDeltaTime() : Core.Now;
|
||||
}
|
||||
|
||||
[AfterDeserialization(false)]
|
||||
private void AfterDeserialization()
|
||||
{
|
||||
if (_running && _end > Core.Now)
|
||||
{
|
||||
case 9:
|
||||
{
|
||||
_guid = reader.ReadGuid();
|
||||
goto case 8;
|
||||
}
|
||||
case 8:
|
||||
{
|
||||
ReturnOnDeactivate = reader.ReadBool();
|
||||
goto case 7;
|
||||
}
|
||||
case 7:
|
||||
{
|
||||
var size = reader.ReadInt();
|
||||
|
||||
Entries = new List<SpawnerEntry>(size);
|
||||
|
||||
for (var i = 0; i < size; ++i)
|
||||
{
|
||||
Entries.Add(new SpawnerEntry(this, reader));
|
||||
}
|
||||
|
||||
goto case 4; // Skip the other crap
|
||||
}
|
||||
case 6:
|
||||
{
|
||||
var size = reader.ReadInt();
|
||||
|
||||
var addentries = Entries.Count == 0;
|
||||
|
||||
for (var i = 0; i < size; ++i)
|
||||
{
|
||||
if (addentries)
|
||||
{
|
||||
Entries.Add(new SpawnerEntry(string.Empty, 100, reader.ReadInt()));
|
||||
}
|
||||
else
|
||||
{
|
||||
Entries[i].SpawnedMaxCount = reader.ReadInt();
|
||||
}
|
||||
}
|
||||
|
||||
goto case 5;
|
||||
}
|
||||
case 5:
|
||||
{
|
||||
var size = reader.ReadInt();
|
||||
|
||||
var addentries = Entries.Count == 0;
|
||||
|
||||
for (var i = 0; i < size; ++i)
|
||||
{
|
||||
if (addentries)
|
||||
{
|
||||
Entries.Add(new SpawnerEntry(string.Empty, reader.ReadInt(), 1));
|
||||
}
|
||||
else
|
||||
{
|
||||
Entries[i].SpawnedProbability = reader.ReadInt();
|
||||
}
|
||||
}
|
||||
|
||||
goto case 4;
|
||||
}
|
||||
case 4:
|
||||
{
|
||||
m_WalkingRange = reader.ReadInt();
|
||||
|
||||
goto case 3;
|
||||
}
|
||||
case 3:
|
||||
case 2:
|
||||
{
|
||||
WayPoint = reader.ReadEntity<WayPoint>();
|
||||
|
||||
goto case 1;
|
||||
}
|
||||
|
||||
case 1:
|
||||
{
|
||||
m_Group = reader.ReadBool();
|
||||
|
||||
goto case 0;
|
||||
}
|
||||
|
||||
case 0:
|
||||
{
|
||||
m_MinDelay = reader.ReadTimeSpan();
|
||||
m_MaxDelay = reader.ReadTimeSpan();
|
||||
m_Count = reader.ReadInt();
|
||||
m_Team = reader.ReadInt();
|
||||
m_HomeRange = reader.ReadInt();
|
||||
m_Running = reader.ReadBool();
|
||||
|
||||
var ts = m_Running ? reader.ReadDeltaTime() - Core.Now : TimeSpan.Zero;
|
||||
|
||||
if (version < 7)
|
||||
{
|
||||
var size = reader.ReadInt();
|
||||
|
||||
var addentries = Entries.Count == 0;
|
||||
|
||||
for (var i = 0; i < size; ++i)
|
||||
{
|
||||
var typeName = reader.ReadString();
|
||||
|
||||
if (addentries)
|
||||
{
|
||||
Entries.Add(new SpawnerEntry(typeName, 100, 1));
|
||||
}
|
||||
else
|
||||
{
|
||||
Entries[i].SpawnedName = typeName;
|
||||
}
|
||||
|
||||
if (AssemblyHandler.FindTypeByName(typeName) == null)
|
||||
{
|
||||
m_WarnTimer ??= new WarnTimer();
|
||||
|
||||
m_WarnTimer.Add(Location, Map, typeName);
|
||||
}
|
||||
}
|
||||
|
||||
var count = reader.ReadInt();
|
||||
|
||||
for (var i = 0; i < count; ++i)
|
||||
{
|
||||
var e = reader.ReadEntity<ISpawnable>();
|
||||
|
||||
if (e == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (e is BaseCreature creature)
|
||||
{
|
||||
creature.RemoveIfUntamed = true;
|
||||
}
|
||||
|
||||
e.Spawner = this;
|
||||
|
||||
for (var j = 0; j < Entries.Count; j++)
|
||||
{
|
||||
if (AssemblyHandler.FindTypeByName(Entries[j].SpawnedName) == e.GetType())
|
||||
{
|
||||
Entries[j].Spawned.Add(e);
|
||||
Spawned.Add(e, Entries[j]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
DoTimer(ts);
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (version < 4)
|
||||
{
|
||||
m_WalkingRange = m_HomeRange;
|
||||
DoTimer(_end - Core.Now);
|
||||
}
|
||||
}
|
||||
|
||||
private class InternalTimer : Timer
|
||||
{
|
||||
private readonly BaseSpawner m_Spawner;
|
||||
private readonly BaseSpawner _spawner;
|
||||
|
||||
public InternalTimer(BaseSpawner spawner, TimeSpan delay) : base(delay) => m_Spawner = spawner;
|
||||
public InternalTimer(BaseSpawner spawner, TimeSpan delay) : base(delay) => _spawner = spawner;
|
||||
|
||||
protected override void OnTick()
|
||||
{
|
||||
if (m_Spawner?.Deleted == false)
|
||||
if (_spawner?.Deleted == false)
|
||||
{
|
||||
m_Spawner.OnTick();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private class WarnTimer : Timer
|
||||
{
|
||||
private readonly List<WarnEntry> m_List;
|
||||
|
||||
public WarnTimer() : base(TimeSpan.FromSeconds(1.0))
|
||||
{
|
||||
m_List = new List<WarnEntry>();
|
||||
Start();
|
||||
}
|
||||
|
||||
public void Add(Point3D p, Map map, string name)
|
||||
{
|
||||
m_List.Add(new WarnEntry(p, map, name));
|
||||
}
|
||||
|
||||
protected override void OnTick()
|
||||
{
|
||||
try
|
||||
{
|
||||
Console.WriteLine("Warning: {0} bad spawns detected, logged: 'badspawn.log'", m_List.Count);
|
||||
|
||||
using var op = new StreamWriter("badspawn.log", true);
|
||||
op.WriteLine("# Bad spawns : {0}", DateTime.Now);
|
||||
op.WriteLine("# Format: X Y Z F Name");
|
||||
op.WriteLine();
|
||||
|
||||
foreach (var e in m_List)
|
||||
{
|
||||
op.WriteLine(
|
||||
"{0}\t{1}\t{2}\t{3}\t{4}",
|
||||
e.m_Point.X,
|
||||
e.m_Point.Y,
|
||||
e.m_Point.Z,
|
||||
e.m_Map,
|
||||
e.m_Name
|
||||
);
|
||||
}
|
||||
|
||||
op.WriteLine();
|
||||
op.WriteLine();
|
||||
}
|
||||
catch
|
||||
{
|
||||
// ignored
|
||||
}
|
||||
}
|
||||
|
||||
private class WarnEntry
|
||||
{
|
||||
public readonly Map m_Map;
|
||||
public readonly string m_Name;
|
||||
public Point3D m_Point;
|
||||
|
||||
public WarnEntry(Point3D p, Map map, string name)
|
||||
{
|
||||
m_Point = p;
|
||||
m_Map = map;
|
||||
m_Name = name;
|
||||
_spawner.OnTick();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -23,236 +23,235 @@ using Server.Json;
|
|||
using Server.Logging;
|
||||
using Server.Network;
|
||||
|
||||
namespace Server.Engines.Spawners
|
||||
namespace Server.Engines.Spawners;
|
||||
|
||||
public static class ConvertPremiumSpawners
|
||||
{
|
||||
public static class ConvertPremiumSpawners
|
||||
private static readonly ILogger logger = LogFactory.GetLogger(typeof(ConvertPremiumSpawners));
|
||||
|
||||
public static void Configure()
|
||||
{
|
||||
private static readonly ILogger logger = LogFactory.GetLogger(typeof(ConvertPremiumSpawners));
|
||||
CommandSystem.Register("ConvertPremiumSpawners", AccessLevel.Developer, ConvertPremiumSpawners_OnCommand);
|
||||
}
|
||||
|
||||
public static void Configure()
|
||||
[Usage("ConvertPremiumSpawners <relative search pattern to distribution> <output directory relative to distribution>")]
|
||||
[Description("Converts Nerun's Premium Spawners to JSON.")]
|
||||
private static void ConvertPremiumSpawners_OnCommand(CommandEventArgs args)
|
||||
{
|
||||
var from = args.Mobile;
|
||||
|
||||
if (args.Arguments.Length != 2)
|
||||
{
|
||||
CommandSystem.Register("ConvertPremiumSpawners", AccessLevel.Developer, ConvertPremiumSpawners_OnCommand);
|
||||
from.SendMessage("Usage: [ConvertPremiumSpawners <relative search pattern to distribution> <output directory relative to distribution>");
|
||||
return;
|
||||
}
|
||||
|
||||
[Usage("ConvertPremiumSpawners <relative search pattern to distribution> <output directory relative to distribution>")]
|
||||
[Description("Converts Nerun's Premium Spawners to JSON.")]
|
||||
private static void ConvertPremiumSpawners_OnCommand(CommandEventArgs args)
|
||||
var inputDi = new DirectoryInfo(Core.BaseDirectory);
|
||||
|
||||
var patternMatches = new Matcher()
|
||||
.AddInclude(args.Arguments[0])
|
||||
.Execute(new DirectoryInfoWrapper(inputDi))
|
||||
.Files;
|
||||
|
||||
List<(FileInfo, string)> files = [];
|
||||
foreach (var match in patternMatches)
|
||||
{
|
||||
var from = args.Mobile;
|
||||
|
||||
if (args.Arguments.Length != 2)
|
||||
{
|
||||
from.SendMessage("Usage: [ConvertPremiumSpawners <relative search pattern to distribution> <output directory relative to distribution>");
|
||||
return;
|
||||
}
|
||||
|
||||
var inputDi = new DirectoryInfo(Core.BaseDirectory);
|
||||
|
||||
var patternMatches = new Matcher()
|
||||
.AddInclude(args.Arguments[0])
|
||||
.Execute(new DirectoryInfoWrapper(inputDi))
|
||||
.Files;
|
||||
|
||||
List<(FileInfo, string)> files = new List<(FileInfo, string)>();
|
||||
foreach (var match in patternMatches)
|
||||
{
|
||||
files.Add((new FileInfo(match.Path), match.Stem));
|
||||
}
|
||||
|
||||
if (files.Count == 0)
|
||||
{
|
||||
from.SendMessage("ConvertPremiumSpawners: No files found.");
|
||||
return;
|
||||
}
|
||||
|
||||
var outputDir = Path.Combine(Core.BaseDirectory, args.Arguments[1]);
|
||||
var options = JsonConfig.GetOptions(new TextDefinitionConverterFactory());
|
||||
|
||||
for (var i = 0; i < files.Count; i++)
|
||||
{
|
||||
var (file, stem) = files[i];
|
||||
from.SendMessage($"ConvertPremiumSpawners: Converting spawners for {stem}...");
|
||||
|
||||
NetState.FlushAll();
|
||||
|
||||
try
|
||||
{
|
||||
var stemDir = stem[..^file.Name.Length];
|
||||
var fullOutputDir = Path.Combine(outputDir, stemDir);
|
||||
PathUtility.EnsureDirectory(fullOutputDir);
|
||||
|
||||
var outputFileName = $"{file.Name.AsSpan(0, file.Name.Length - file.Extension.Length)}.json";
|
||||
ParsePremiumSpawnerFile(file.FullName, Path.Combine(fullOutputDir, outputFileName), options);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
logger.Error(e.ToString());
|
||||
from.SendMessage($"ConvertPremiumSpawners: Exception parsing {stem}, file may not be in the correct format.");
|
||||
}
|
||||
}
|
||||
files.Add((new FileInfo(match.Path), match.Stem));
|
||||
}
|
||||
|
||||
private static void ParsePremiumSpawnerFile(string inputFile, string outputFile, JsonSerializerOptions options)
|
||||
if (files.Count == 0)
|
||||
{
|
||||
var lines = File.ReadAllLines(inputFile);
|
||||
from.SendMessage("ConvertPremiumSpawners: No files found.");
|
||||
return;
|
||||
}
|
||||
|
||||
TimeSpan minTime = TimeSpan.MinValue;
|
||||
TimeSpan maxTime = TimeSpan.MinValue;
|
||||
int mapId = -1;
|
||||
string spawnerId = null;
|
||||
var outputDir = Path.Combine(Core.BaseDirectory, args.Arguments[1]);
|
||||
var options = JsonConfig.GetOptions(new TextDefinitionConverterFactory());
|
||||
|
||||
var json = new List<DynamicJson>();
|
||||
for (var i = 0; i < files.Count; i++)
|
||||
{
|
||||
var (file, stem) = files[i];
|
||||
from.SendMessage($"ConvertPremiumSpawners: Converting spawners for {stem}...");
|
||||
|
||||
foreach (var line in lines)
|
||||
NetState.FlushAll();
|
||||
|
||||
try
|
||||
{
|
||||
if (line.StartsWithOrdinal("#"))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
var stemDir = stem[..^file.Name.Length];
|
||||
var fullOutputDir = Path.Combine(outputDir, stemDir);
|
||||
PathUtility.EnsureDirectory(fullOutputDir);
|
||||
|
||||
if (line.StartsWith('*'))
|
||||
var outputFileName = $"{file.Name.AsSpan(0, file.Name.Length - file.Extension.Length)}.json";
|
||||
ParsePremiumSpawnerFile(file.FullName, Path.Combine(fullOutputDir, outputFileName), options);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
logger.Error(e.ToString());
|
||||
from.SendMessage($"ConvertPremiumSpawners: Exception parsing {stem}, file may not be in the correct format.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void ParsePremiumSpawnerFile(string inputFile, string outputFile, JsonSerializerOptions options)
|
||||
{
|
||||
var lines = File.ReadAllLines(inputFile);
|
||||
|
||||
TimeSpan minTime = TimeSpan.MinValue;
|
||||
TimeSpan maxTime = TimeSpan.MinValue;
|
||||
int mapId = -1;
|
||||
string spawnerId = null;
|
||||
|
||||
var json = new List<DynamicJson>();
|
||||
|
||||
foreach (var line in lines)
|
||||
{
|
||||
if (line.StartsWithOrdinal("#"))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (line.StartsWith('*'))
|
||||
{
|
||||
var spawners = ParsePremiumSpawner(line.Split('|'), spawnerId, mapId, minTime, maxTime);
|
||||
foreach (var spawner in spawners)
|
||||
{
|
||||
var spawners = ParsePremiumSpawner(line.Split('|'), spawnerId, mapId, minTime, maxTime);
|
||||
foreach (var spawner in spawners)
|
||||
var dynamicJson = new DynamicJson
|
||||
{
|
||||
var dynamicJson = new DynamicJson
|
||||
{
|
||||
Type = "Spawner",
|
||||
Data = new Dictionary<string, JsonElement>()
|
||||
};
|
||||
Type = "Spawner",
|
||||
Data = new Dictionary<string, JsonElement>()
|
||||
};
|
||||
|
||||
spawner.ToJson(dynamicJson, options);
|
||||
json.Add(dynamicJson);
|
||||
spawner.Delete();
|
||||
}
|
||||
|
||||
spawners.Clear();
|
||||
continue;
|
||||
}
|
||||
|
||||
var over = line.Split(' ');
|
||||
switch (over[0].ToLowerInvariant())
|
||||
{
|
||||
case "overrideid":
|
||||
{
|
||||
spawnerId = over[1];
|
||||
break;
|
||||
}
|
||||
case "overridemap":
|
||||
{
|
||||
mapId = int.Parse(over[1]);
|
||||
break;
|
||||
}
|
||||
case "overridemintime":
|
||||
{
|
||||
minTime = GetTimeSpan(over[1]);
|
||||
break;
|
||||
}
|
||||
case "overridemaxtime":
|
||||
{
|
||||
maxTime = GetTimeSpan(over[1]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
JsonConfig.Serialize(outputFile, json, options);
|
||||
}
|
||||
|
||||
private static List<Spawner> ParsePremiumSpawner(
|
||||
string[] parts,
|
||||
string spawnerIdOverride,
|
||||
int mapIdOverride,
|
||||
TimeSpan minTimeOverride,
|
||||
TimeSpan maxTimeOverride
|
||||
)
|
||||
{
|
||||
var spawnersList = new List<Spawner>();
|
||||
var mapId = mapIdOverride != -1 ? mapIdOverride : int.Parse(parts[10]);
|
||||
Map[] maps = mapId == 0 ? new[] { Map.Felucca, Map.Trammel } : new[] { Map.Maps[mapId - 1] };
|
||||
|
||||
foreach (var map in maps)
|
||||
{
|
||||
var spawner = new Spawner
|
||||
{
|
||||
X = int.Parse(parts[7]),
|
||||
Y = int.Parse(parts[8]),
|
||||
Z = int.Parse(parts[9]),
|
||||
Map = map,
|
||||
MinDelay = minTimeOverride != TimeSpan.MinValue ? minTimeOverride : GetTimeSpan(parts[11]),
|
||||
MaxDelay = maxTimeOverride != TimeSpan.MinValue ? maxTimeOverride : GetTimeSpan(parts[12]),
|
||||
WalkingRange = int.Parse(parts[13]),
|
||||
HomeRange = int.Parse(parts[14]),
|
||||
Name = $"Spawner ({spawnerIdOverride ?? parts[15]})",
|
||||
Guid = Guid.NewGuid()
|
||||
};
|
||||
|
||||
var totalCount = 0;
|
||||
|
||||
for (var i = 0; i < 6; i++)
|
||||
{
|
||||
var count = int.Parse(parts[i + 16]);
|
||||
totalCount += count;
|
||||
spawner.Entries.AddRange(CreateSpawnerEntries(parts[i + 1], count));
|
||||
}
|
||||
|
||||
if (spawner.Entries.Count == 0)
|
||||
{
|
||||
spawner.ToJson(dynamicJson, options);
|
||||
json.Add(dynamicJson);
|
||||
spawner.Delete();
|
||||
continue;
|
||||
}
|
||||
|
||||
spawner.Count = totalCount;
|
||||
spawnersList.Add(spawner);
|
||||
spawners.Clear();
|
||||
continue;
|
||||
}
|
||||
|
||||
return spawnersList;
|
||||
var over = line.Split(' ');
|
||||
switch (over[0].ToLowerInvariant())
|
||||
{
|
||||
case "overrideid":
|
||||
{
|
||||
spawnerId = over[1];
|
||||
break;
|
||||
}
|
||||
case "overridemap":
|
||||
{
|
||||
mapId = int.Parse(over[1]);
|
||||
break;
|
||||
}
|
||||
case "overridemintime":
|
||||
{
|
||||
minTime = GetTimeSpan(over[1]);
|
||||
break;
|
||||
}
|
||||
case "overridemaxtime":
|
||||
{
|
||||
maxTime = GetTimeSpan(over[1]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static List<SpawnerEntry> CreateSpawnerEntries(string typeList, int maxCount)
|
||||
JsonConfig.Serialize(outputFile, json, options);
|
||||
}
|
||||
|
||||
private static List<Spawner> ParsePremiumSpawner(
|
||||
string[] parts,
|
||||
string spawnerIdOverride,
|
||||
int mapIdOverride,
|
||||
TimeSpan minTimeOverride,
|
||||
TimeSpan maxTimeOverride
|
||||
)
|
||||
{
|
||||
var spawnersList = new List<Spawner>();
|
||||
var mapId = mapIdOverride != -1 ? mapIdOverride : int.Parse(parts[10]);
|
||||
Map[] maps = mapId == 0 ? [Map.Felucca, Map.Trammel] : [Map.Maps[mapId - 1]];
|
||||
|
||||
foreach (var map in maps)
|
||||
{
|
||||
var list = new List<SpawnerEntry>();
|
||||
if (string.IsNullOrWhiteSpace(typeList))
|
||||
var spawner = new Spawner
|
||||
{
|
||||
return list;
|
||||
X = int.Parse(parts[7]),
|
||||
Y = int.Parse(parts[8]),
|
||||
Z = int.Parse(parts[9]),
|
||||
Map = map,
|
||||
MinDelay = minTimeOverride != TimeSpan.MinValue ? minTimeOverride : GetTimeSpan(parts[11]),
|
||||
MaxDelay = maxTimeOverride != TimeSpan.MinValue ? maxTimeOverride : GetTimeSpan(parts[12]),
|
||||
WalkingRange = int.Parse(parts[13]),
|
||||
HomeRange = int.Parse(parts[14]),
|
||||
Name = $"Spawner ({spawnerIdOverride ?? parts[15]})",
|
||||
Guid = Guid.NewGuid()
|
||||
};
|
||||
|
||||
var totalCount = 0;
|
||||
|
||||
for (var i = 0; i < 6; i++)
|
||||
{
|
||||
var count = int.Parse(parts[i + 16]);
|
||||
totalCount += count;
|
||||
spawner.Entries.AddRange(CreateSpawnerEntries(spawner, parts[i + 1], count));
|
||||
}
|
||||
|
||||
foreach (var spawnType in typeList.Split(':'))
|
||||
if (spawner.Entries.Count == 0)
|
||||
{
|
||||
var actualType = AssemblyHandler.FindTypeByName(ConvertType(spawnType))?.Name ?? spawnType;
|
||||
list.Add(new SpawnerEntry(actualType, 100, maxCount));
|
||||
spawner.Delete();
|
||||
continue;
|
||||
}
|
||||
|
||||
spawner.Count = totalCount;
|
||||
spawnersList.Add(spawner);
|
||||
}
|
||||
|
||||
return spawnersList;
|
||||
}
|
||||
|
||||
private static List<SpawnerEntry> CreateSpawnerEntries(BaseSpawner spawner, string typeList, int maxCount)
|
||||
{
|
||||
var list = new List<SpawnerEntry>();
|
||||
if (string.IsNullOrWhiteSpace(typeList))
|
||||
{
|
||||
return list;
|
||||
}
|
||||
|
||||
private static TimeSpan GetTimeSpan(string time)
|
||||
foreach (var spawnType in typeList.Split(':'))
|
||||
{
|
||||
if (string.IsNullOrEmpty(time))
|
||||
{
|
||||
return TimeSpan.MinValue;
|
||||
}
|
||||
|
||||
return char.ToLowerInvariant(time[^1]) switch
|
||||
{
|
||||
'h' => TimeSpan.FromHours(double.Parse(time.AsSpan(0, time.Length - 1))),
|
||||
'm' => TimeSpan.FromMinutes(double.Parse(time.AsSpan(0, time.Length - 1))),
|
||||
's' => TimeSpan.FromSeconds(double.Parse(time.AsSpan(0, time.Length - 1))),
|
||||
_ => TimeSpan.FromMinutes(double.Parse(time))
|
||||
};
|
||||
var actualType = AssemblyHandler.FindTypeByName(ConvertType(spawnType))?.Name ?? spawnType;
|
||||
list.Add(new SpawnerEntry(spawner, actualType, 100, maxCount));
|
||||
}
|
||||
|
||||
private static string ConvertType(string type)
|
||||
{
|
||||
return type.ToLowerInvariant() switch
|
||||
{
|
||||
"treasurelevel1" => "treasurechestlevel1",
|
||||
"treasurelevel2" => "treasurechestlevel2",
|
||||
"treasurelevel3" => "treasurechestlevel3",
|
||||
"treasurelevel4" => "treasurechestlevel4",
|
||||
"treasurelevel5" => "treasurechestlevel5",
|
||||
_ => type
|
||||
};
|
||||
}
|
||||
return list;
|
||||
}
|
||||
}
|
||||
|
||||
private static TimeSpan GetTimeSpan(string time)
|
||||
{
|
||||
if (string.IsNullOrEmpty(time))
|
||||
{
|
||||
return TimeSpan.MinValue;
|
||||
}
|
||||
|
||||
return char.ToLowerInvariant(time[^1]) switch
|
||||
{
|
||||
'h' => TimeSpan.FromHours(double.Parse(time.AsSpan(0, time.Length - 1))),
|
||||
'm' => TimeSpan.FromMinutes(double.Parse(time.AsSpan(0, time.Length - 1))),
|
||||
's' => TimeSpan.FromSeconds(double.Parse(time.AsSpan(0, time.Length - 1))),
|
||||
_ => TimeSpan.FromMinutes(double.Parse(time))
|
||||
};
|
||||
}
|
||||
|
||||
private static string ConvertType(string type)
|
||||
{
|
||||
return type.ToLowerInvariant() switch
|
||||
{
|
||||
"treasurelevel1" => "treasurechestlevel1",
|
||||
"treasurelevel2" => "treasurechestlevel2",
|
||||
"treasurelevel3" => "treasurechestlevel3",
|
||||
"treasurelevel4" => "treasurechestlevel4",
|
||||
"treasurelevel5" => "treasurechestlevel5",
|
||||
_ => type
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
@ -18,116 +18,114 @@ using System.Collections.Generic;
|
|||
using Server.Commands.Generic;
|
||||
using static Server.Types;
|
||||
|
||||
namespace Server.Engines.Spawners
|
||||
namespace Server.Engines.Spawners;
|
||||
|
||||
public class EditSpawnCommand : BaseCommand
|
||||
{
|
||||
|
||||
public class EditSpawnCommand : BaseCommand
|
||||
public static void Configure()
|
||||
{
|
||||
public static void Configure()
|
||||
TargetCommands.Register(new EditSpawnCommand());
|
||||
}
|
||||
|
||||
public EditSpawnCommand()
|
||||
{
|
||||
AccessLevel = AccessLevel.GameMaster;
|
||||
Supports = CommandSupport.Complex | CommandSupport.Simple;
|
||||
Commands = ["EditSpawner"];
|
||||
ObjectTypes = ObjectTypes.Items;
|
||||
Usage = "EditSpawner <type> <arguments> set <properties> where <properties>";
|
||||
Description = "Modifies spawners arguments and properties for the given type";
|
||||
ListOptimized = true;
|
||||
}
|
||||
|
||||
public override void ExecuteList(CommandEventArgs e, List<object> list)
|
||||
{
|
||||
var args = e.Arguments;
|
||||
|
||||
if (args.Length <= 1)
|
||||
{
|
||||
TargetCommands.Register(new EditSpawnCommand());
|
||||
LogFailure(Usage);
|
||||
return;
|
||||
}
|
||||
|
||||
public EditSpawnCommand()
|
||||
if (list.Count == 0)
|
||||
{
|
||||
AccessLevel = AccessLevel.GameMaster;
|
||||
Supports = CommandSupport.Complex | CommandSupport.Simple;
|
||||
Commands = new[] { "EditSpawner" };
|
||||
ObjectTypes = ObjectTypes.Items;
|
||||
Usage = "EditSpawner <type> <arguments> set <properties> where <properties>";
|
||||
Description = "Modifies spawners arguments and properties for the given type";
|
||||
ListOptimized = true;
|
||||
LogFailure("No matching objects found.");
|
||||
return;
|
||||
}
|
||||
|
||||
public override void ExecuteList(CommandEventArgs e, List<object> list)
|
||||
var name = args[0];
|
||||
|
||||
var type = AssemblyHandler.FindTypeByName(name);
|
||||
|
||||
if (!IsEntity(type))
|
||||
{
|
||||
var args = e.Arguments;
|
||||
|
||||
if (args.Length <= 1)
|
||||
{
|
||||
LogFailure(Usage);
|
||||
return;
|
||||
}
|
||||
|
||||
if (list.Count == 0)
|
||||
{
|
||||
LogFailure("No matching objects found.");
|
||||
return;
|
||||
}
|
||||
|
||||
var name = args[0];
|
||||
|
||||
var type = AssemblyHandler.FindTypeByName(name);
|
||||
|
||||
if (!IsEntity(type))
|
||||
{
|
||||
LogFailure("No type with that name was found.");
|
||||
return;
|
||||
}
|
||||
|
||||
var argSpan = e.ArgString.AsSpan(name.Length + 1);
|
||||
var setIndex = argSpan.InsensitiveIndexOf("set ");
|
||||
var where = argSpan.InsensitiveIndexOf("where ");
|
||||
|
||||
ReadOnlySpan<char> props = null, findmatch = null;
|
||||
|
||||
if (setIndex > -1 || where > -1)
|
||||
{
|
||||
var start = setIndex + 4;
|
||||
var len = where > -1 ? where : argSpan.Length;
|
||||
findmatch = argSpan.Slice(len < argSpan.Length ? len + 6 : argSpan.Length);
|
||||
if (setIndex > -1)
|
||||
{
|
||||
props = argSpan[start..^len];
|
||||
argSpan = argSpan[..setIndex];
|
||||
}
|
||||
else
|
||||
{
|
||||
argSpan = argSpan[..^len];
|
||||
}
|
||||
}
|
||||
|
||||
var argStr = argSpan.ToString().DefaultIfNullOrEmpty(null);
|
||||
var propsStr = props.ToString().DefaultIfNullOrEmpty(null);
|
||||
var whereStr = findmatch.ToString().DefaultIfNullOrEmpty(null);
|
||||
|
||||
e.Mobile.SendMessage("Updating spawners...");
|
||||
|
||||
foreach (var obj in list)
|
||||
{
|
||||
if (obj is BaseSpawner spawner)
|
||||
{
|
||||
UpdateSpawner(spawner, name, argStr, propsStr, whereStr);
|
||||
}
|
||||
}
|
||||
|
||||
e.Mobile.SendMessage("Update completed.");
|
||||
LogFailure("No type with that name was found.");
|
||||
return;
|
||||
}
|
||||
|
||||
public static void UpdateSpawner(BaseSpawner spawner, string name, string arguments, string properties, string find = null)
|
||||
var argSpan = e.ArgString.AsSpan(name.Length + 1);
|
||||
var setIndex = argSpan.InsensitiveIndexOf("set ");
|
||||
var where = argSpan.InsensitiveIndexOf("where ");
|
||||
|
||||
ReadOnlySpan<char> props = null, findmatch = null;
|
||||
|
||||
if (setIndex > -1 || where > -1)
|
||||
{
|
||||
foreach (var entry in spawner.Entries)
|
||||
var start = setIndex + 4;
|
||||
var len = where > -1 ? where : argSpan.Length;
|
||||
findmatch = argSpan.Slice(len < argSpan.Length ? len + 6 : argSpan.Length);
|
||||
if (setIndex > -1)
|
||||
{
|
||||
// TODO: Should cache spawn type on the entry
|
||||
if (!entry.SpawnedName.InsensitiveEquals(name))
|
||||
props = argSpan[start..^len];
|
||||
argSpan = argSpan[..setIndex];
|
||||
}
|
||||
else
|
||||
{
|
||||
argSpan = argSpan[..^len];
|
||||
}
|
||||
}
|
||||
|
||||
var argStr = argSpan.ToString().DefaultIfNullOrEmpty(null);
|
||||
var propsStr = props.ToString().DefaultIfNullOrEmpty(null);
|
||||
var whereStr = findmatch.ToString().DefaultIfNullOrEmpty(null);
|
||||
|
||||
e.Mobile.SendMessage("Updating spawners...");
|
||||
|
||||
foreach (var obj in list)
|
||||
{
|
||||
if (obj is BaseSpawner spawner)
|
||||
{
|
||||
UpdateSpawner(spawner, name, argStr, propsStr, whereStr);
|
||||
}
|
||||
}
|
||||
|
||||
e.Mobile.SendMessage("Update completed.");
|
||||
}
|
||||
|
||||
public static void UpdateSpawner(BaseSpawner spawner, string name, string arguments, string properties, string find = null)
|
||||
{
|
||||
foreach (var entry in spawner.Entries)
|
||||
{
|
||||
// TODO: Should cache spawn type on the entry
|
||||
if (!entry.SpawnedName.InsensitiveEquals(name))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
var found = find != null ? entry.Properties?.LastIndexOf(find, StringComparison.OrdinalIgnoreCase) : null;
|
||||
var shouldUpdate = find == null || found > -1 &&
|
||||
(found + find.Length == entry.Properties.Length ||
|
||||
char.IsWhiteSpace(entry.Properties[(int)found + find.Length]));
|
||||
|
||||
if (shouldUpdate)
|
||||
{
|
||||
if (arguments != null)
|
||||
{
|
||||
continue;
|
||||
entry.Parameters = arguments;
|
||||
}
|
||||
|
||||
var found = find != null ? entry.Properties?.LastIndexOf(find, StringComparison.OrdinalIgnoreCase) : null;
|
||||
var shouldUpdate = find == null || found > -1 &&
|
||||
(found + find.Length == entry.Properties.Length ||
|
||||
char.IsWhiteSpace(entry.Properties[(int)found + find.Length]));
|
||||
|
||||
if (shouldUpdate)
|
||||
{
|
||||
if (arguments != null)
|
||||
{
|
||||
entry.Parameters = arguments;
|
||||
}
|
||||
|
||||
entry.Properties = properties;
|
||||
}
|
||||
entry.Properties = properties;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -33,7 +33,7 @@ public class ExportSpawnersCommand : BaseCommand
|
|||
{
|
||||
AccessLevel = AccessLevel.GameMaster;
|
||||
Supports = CommandSupport.AllItems & ~CommandSupport.Contained;
|
||||
Commands = new[] { "ExportSpawners" };
|
||||
Commands = ["ExportSpawners"];
|
||||
ObjectTypes = ObjectTypes.Items;
|
||||
Usage = "ExportSpawners";
|
||||
Description = "Exports the given spawners to a file";
|
||||
|
|
|
|||
|
|
@ -11,194 +11,193 @@ using Server.Logging;
|
|||
using Server.Network;
|
||||
using Server.Utilities;
|
||||
|
||||
namespace Server.Engines.Spawners
|
||||
namespace Server.Engines.Spawners;
|
||||
|
||||
public static class GenerateSpawnersCommand
|
||||
{
|
||||
public static class GenerateSpawnersCommand
|
||||
private static readonly ILogger logger = LogFactory.GetLogger(typeof(GenerateSpawnersCommand));
|
||||
|
||||
public static void Configure()
|
||||
{
|
||||
private static readonly ILogger logger = LogFactory.GetLogger(typeof(GenerateSpawnersCommand));
|
||||
CommandSystem.Register("GenerateSpawners", AccessLevel.Developer, GenerateSpawners_OnCommand);
|
||||
}
|
||||
|
||||
public static void Configure()
|
||||
[Usage("GenerateSpawners <relative search pattern to distribution>")]
|
||||
[Aliases("ImportSpawners", "GenSpawners", "GenSpawns")]
|
||||
[Description("Generates spawners from JSON files. Supports basic globbing.")]
|
||||
private static void GenerateSpawners_OnCommand(CommandEventArgs e)
|
||||
{
|
||||
var from = e.Mobile;
|
||||
|
||||
if (e.Arguments.Length == 0)
|
||||
{
|
||||
CommandSystem.Register("GenerateSpawners", AccessLevel.Developer, GenerateSpawners_OnCommand);
|
||||
from.SendMessage("Usage: [GenerateSpawners <relative search pattern to distribution>");
|
||||
return;
|
||||
}
|
||||
|
||||
[Usage("GenerateSpawners <relative search pattern to distribution>")]
|
||||
[Aliases("ImportSpawners", "GenSpawners", "GenSpawns")]
|
||||
[Description("Generates spawners from JSON files. Supports basic globbing.")]
|
||||
private static void GenerateSpawners_OnCommand(CommandEventArgs e)
|
||||
var di = new DirectoryInfo(Core.BaseDirectory);
|
||||
|
||||
var patternMatches = new Matcher()
|
||||
.AddInclude(e.Arguments[0])
|
||||
.Execute(new DirectoryInfoWrapper(di))
|
||||
.Files;
|
||||
|
||||
List<FileInfo> files = [];
|
||||
foreach (var match in patternMatches)
|
||||
{
|
||||
var from = e.Mobile;
|
||||
|
||||
if (e.Arguments.Length == 0)
|
||||
{
|
||||
from.SendMessage("Usage: [GenerateSpawners <relative search pattern to distribution>");
|
||||
return;
|
||||
}
|
||||
|
||||
var di = new DirectoryInfo(Core.BaseDirectory);
|
||||
|
||||
var patternMatches = new Matcher()
|
||||
.AddInclude(e.Arguments[0])
|
||||
.Execute(new DirectoryInfoWrapper(di))
|
||||
.Files;
|
||||
|
||||
List<FileInfo> files = new List<FileInfo>();
|
||||
foreach (var match in patternMatches)
|
||||
{
|
||||
files.Add(new FileInfo(match.Path));
|
||||
}
|
||||
|
||||
if (files.Count == 0)
|
||||
{
|
||||
from.SendMessage("GenerateSpawners: No files found matching the pattern");
|
||||
return;
|
||||
}
|
||||
|
||||
var watch = Stopwatch.StartNew();
|
||||
|
||||
var allSpawners = new Dictionary<Guid, ISpawner>();
|
||||
foreach (var item in World.Items.Values)
|
||||
{
|
||||
if (item is ISpawner spawner)
|
||||
{
|
||||
allSpawners[spawner.Guid] = spawner;
|
||||
}
|
||||
}
|
||||
|
||||
var options = JsonConfig.GetOptions(new TextDefinitionConverterFactory());
|
||||
var totalGenerated = 0;
|
||||
var totalFailures = 0;
|
||||
|
||||
for (var i = 0; i < files.Count; i++)
|
||||
{
|
||||
var file = files[i];
|
||||
from.SendMessage($"GenerateSpawners: Generating spawners from {file.Name}...");
|
||||
logger.Information("{User} is generating spawners from {File}", from, file.FullName);
|
||||
|
||||
NetState.FlushAll();
|
||||
|
||||
try
|
||||
{
|
||||
var spawners = JsonConfig.Deserialize<List<DynamicJson>>(file.FullName);
|
||||
ParseSpawnerList(spawners, options, allSpawners, out var generated, out var failed);
|
||||
totalGenerated += generated;
|
||||
totalFailures += failed;
|
||||
}
|
||||
catch (JsonException)
|
||||
{
|
||||
from.SendMessage(
|
||||
$"GenerateSpawners: Exception parsing {file.FullName}, file may not be in the correct format."
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
watch.Stop();
|
||||
|
||||
logger.Information(
|
||||
"Generated {Count} spawners ({Duration:F2} seconds, {Failures} failures)",
|
||||
totalGenerated,
|
||||
watch.Elapsed.TotalSeconds,
|
||||
totalFailures
|
||||
);
|
||||
|
||||
from.SendMessage(
|
||||
$"GenerateSpawners: Generated {totalGenerated} spawners ({watch.Elapsed.TotalSeconds:F2} seconds, {totalFailures} failures)"
|
||||
);
|
||||
files.Add(new FileInfo(match.Path));
|
||||
}
|
||||
|
||||
private static void ParseSpawnerList(
|
||||
List<DynamicJson> spawners,
|
||||
JsonSerializerOptions options,
|
||||
Dictionary<Guid, ISpawner> allSpawners,
|
||||
out int totalGenerated,
|
||||
out int failureCount
|
||||
)
|
||||
if (files.Count == 0)
|
||||
{
|
||||
failureCount = 0;
|
||||
totalGenerated = 0;
|
||||
from.SendMessage("GenerateSpawners: No files found matching the pattern");
|
||||
return;
|
||||
}
|
||||
|
||||
using var queue = PooledRefQueue<Item>.Create();
|
||||
for (var i = 0; i < spawners.Count; i++)
|
||||
var watch = Stopwatch.StartNew();
|
||||
|
||||
var allSpawners = new Dictionary<Guid, ISpawner>();
|
||||
foreach (var item in World.Items.Values)
|
||||
{
|
||||
if (item is ISpawner spawner)
|
||||
{
|
||||
var json = spawners[i];
|
||||
var type = AssemblyHandler.FindTypeByName(json.Type);
|
||||
|
||||
if (type == null || !typeof(BaseSpawner).IsAssignableFrom(type))
|
||||
{
|
||||
logger.Error($"Invalid spawner type {json.Type ?? "(-null-)"} ({i}).");
|
||||
failureCount++;
|
||||
continue;
|
||||
}
|
||||
|
||||
json.GetProperty("location", options, out Point3D location);
|
||||
json.GetProperty("map", options, out Map map);
|
||||
|
||||
// Delete all spawners at this location.
|
||||
// Probably shouldn't do this outside of migrations? Is there a better way to find/fix spawners?
|
||||
foreach (var spawner in map.GetItemsAt<BaseSpawner>(location))
|
||||
{
|
||||
if (spawner.GetType() == type)
|
||||
{
|
||||
queue.Enqueue(spawner);
|
||||
allSpawners.Remove(spawner.Guid);
|
||||
}
|
||||
}
|
||||
|
||||
while (queue.Count > 0)
|
||||
{
|
||||
queue.Dequeue().Delete();
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
var spawner = type.CreateInstance<ISpawner>(json, options);
|
||||
|
||||
spawner!.MoveToWorld(location, map);
|
||||
spawner!.Respawn();
|
||||
|
||||
if (allSpawners.Remove(spawner.Guid, out var oldSpawner))
|
||||
{
|
||||
oldSpawner.Delete();
|
||||
}
|
||||
|
||||
allSpawners.Add(spawner.Guid, spawner);
|
||||
totalGenerated++;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
json.GetProperty("guid", options, out Guid guid);
|
||||
TraceException(ex, $"Failed to generate spawner {guid}.");
|
||||
|
||||
failureCount++;
|
||||
}
|
||||
allSpawners[spawner.Guid] = spawner;
|
||||
}
|
||||
}
|
||||
|
||||
private static void TraceException(Exception ex, string message = "")
|
||||
var options = JsonConfig.GetOptions(new TextDefinitionConverterFactory());
|
||||
var totalGenerated = 0;
|
||||
var totalFailures = 0;
|
||||
|
||||
for (var i = 0; i < files.Count; i++)
|
||||
{
|
||||
var file = files[i];
|
||||
from.SendMessage($"GenerateSpawners: Generating spawners from {file.Name}...");
|
||||
logger.Information("{User} is generating spawners from {File}", from, file.FullName);
|
||||
|
||||
NetState.FlushAll();
|
||||
|
||||
try
|
||||
{
|
||||
using var op = new StreamWriter("spawner-errors.log", true);
|
||||
op.WriteLine("# {0}", Core.Now);
|
||||
var spawners = JsonConfig.Deserialize<List<DynamicJson>>(file.FullName);
|
||||
ParseSpawnerList(spawners, options, allSpawners, out var generated, out var failed);
|
||||
totalGenerated += generated;
|
||||
totalFailures += failed;
|
||||
}
|
||||
catch (JsonException)
|
||||
{
|
||||
from.SendMessage(
|
||||
$"GenerateSpawners: Exception parsing {file.FullName}, file may not be in the correct format."
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(message))
|
||||
watch.Stop();
|
||||
|
||||
logger.Information(
|
||||
"Generated {Count} spawners ({Duration:F2} seconds, {Failures} failures)",
|
||||
totalGenerated,
|
||||
watch.Elapsed.TotalSeconds,
|
||||
totalFailures
|
||||
);
|
||||
|
||||
from.SendMessage(
|
||||
$"GenerateSpawners: Generated {totalGenerated} spawners ({watch.Elapsed.TotalSeconds:F2} seconds, {totalFailures} failures)"
|
||||
);
|
||||
}
|
||||
|
||||
private static void ParseSpawnerList(
|
||||
List<DynamicJson> spawners,
|
||||
JsonSerializerOptions options,
|
||||
Dictionary<Guid, ISpawner> allSpawners,
|
||||
out int totalGenerated,
|
||||
out int failureCount
|
||||
)
|
||||
{
|
||||
failureCount = 0;
|
||||
totalGenerated = 0;
|
||||
|
||||
using var queue = PooledRefQueue<Item>.Create();
|
||||
for (var i = 0; i < spawners.Count; i++)
|
||||
{
|
||||
var json = spawners[i];
|
||||
var type = AssemblyHandler.FindTypeByName(json.Type);
|
||||
|
||||
if (type == null || !typeof(BaseSpawner).IsAssignableFrom(type))
|
||||
{
|
||||
logger.Error($"Invalid spawner type {json.Type ?? "(-null-)"} ({i}).");
|
||||
failureCount++;
|
||||
continue;
|
||||
}
|
||||
|
||||
json.GetProperty("location", options, out Point3D location);
|
||||
json.GetProperty("map", options, out Map map);
|
||||
|
||||
// Delete all spawners at this location.
|
||||
// Probably shouldn't do this outside of migrations? Is there a better way to find/fix spawners?
|
||||
foreach (var spawner in map.GetItemsAt<BaseSpawner>(location))
|
||||
{
|
||||
if (spawner.GetType() == type)
|
||||
{
|
||||
op.WriteLine(message);
|
||||
queue.Enqueue(spawner);
|
||||
allSpawners.Remove(spawner.Guid);
|
||||
}
|
||||
}
|
||||
|
||||
while (queue.Count > 0)
|
||||
{
|
||||
queue.Dequeue().Delete();
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
var spawner = type.CreateInstance<ISpawner>(json, options);
|
||||
|
||||
spawner!.MoveToWorld(location, map);
|
||||
spawner!.Respawn();
|
||||
|
||||
if (allSpawners.Remove(spawner.Guid, out var oldSpawner))
|
||||
{
|
||||
oldSpawner.Delete();
|
||||
}
|
||||
|
||||
op.WriteLine(ex);
|
||||
|
||||
op.WriteLine();
|
||||
op.WriteLine();
|
||||
allSpawners.Add(spawner.Guid, spawner);
|
||||
totalGenerated++;
|
||||
}
|
||||
catch
|
||||
catch (Exception ex)
|
||||
{
|
||||
// ignored
|
||||
}
|
||||
json.GetProperty("guid", options, out Guid guid);
|
||||
TraceException(ex, $"Failed to generate spawner {guid}.");
|
||||
|
||||
#if DEBUG
|
||||
logger.Error(ex, message);
|
||||
#endif
|
||||
failureCount++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void TraceException(Exception ex, string message = "")
|
||||
{
|
||||
try
|
||||
{
|
||||
using var op = new StreamWriter("spawner-errors.log", true);
|
||||
op.WriteLine("# {0}", Core.Now);
|
||||
|
||||
if (!string.IsNullOrEmpty(message))
|
||||
{
|
||||
op.WriteLine(message);
|
||||
}
|
||||
|
||||
op.WriteLine(ex);
|
||||
|
||||
op.WriteLine();
|
||||
op.WriteLine();
|
||||
}
|
||||
catch
|
||||
{
|
||||
// ignored
|
||||
}
|
||||
|
||||
#if DEBUG
|
||||
logger.Error(ex, message);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -17,47 +17,46 @@ using System.Collections.Generic;
|
|||
using Server.Commands.Generic;
|
||||
using Server.Network;
|
||||
|
||||
namespace Server.Engines.Spawners
|
||||
namespace Server.Engines.Spawners;
|
||||
|
||||
public class RespawnCommand : BaseCommand
|
||||
{
|
||||
public class RespawnCommand : BaseCommand
|
||||
public static void Configure()
|
||||
{
|
||||
public static void Configure()
|
||||
TargetCommands.Register(new RespawnCommand());
|
||||
}
|
||||
|
||||
public RespawnCommand()
|
||||
{
|
||||
AccessLevel = AccessLevel.GameMaster;
|
||||
Supports = CommandSupport.Complex | CommandSupport.Simple;
|
||||
Commands = ["Respawn"];
|
||||
ObjectTypes = ObjectTypes.Items;
|
||||
Usage = "Respawn";
|
||||
Description = "Respawns the given the spawners.";
|
||||
ListOptimized = true;
|
||||
}
|
||||
|
||||
public override void ExecuteList(CommandEventArgs e, List<object> list)
|
||||
{
|
||||
if (list.Count == 0)
|
||||
{
|
||||
TargetCommands.Register(new RespawnCommand());
|
||||
LogFailure("No matching objects found.");
|
||||
return;
|
||||
}
|
||||
|
||||
public RespawnCommand()
|
||||
{
|
||||
AccessLevel = AccessLevel.GameMaster;
|
||||
Supports = CommandSupport.Complex | CommandSupport.Simple;
|
||||
Commands = new[] { "Respawn" };
|
||||
ObjectTypes = ObjectTypes.Items;
|
||||
Usage = "Respawn";
|
||||
Description = "Respawns the given the spawners.";
|
||||
ListOptimized = true;
|
||||
}
|
||||
e.Mobile.SendMessage("Respawning...");
|
||||
|
||||
public override void ExecuteList(CommandEventArgs e, List<object> list)
|
||||
NetState.FlushAll();
|
||||
|
||||
foreach (var obj in list)
|
||||
{
|
||||
if (list.Count == 0)
|
||||
if (obj is ISpawner spawner)
|
||||
{
|
||||
LogFailure("No matching objects found.");
|
||||
return;
|
||||
spawner.Respawn();
|
||||
}
|
||||
|
||||
e.Mobile.SendMessage("Respawning...");
|
||||
|
||||
NetState.FlushAll();
|
||||
|
||||
foreach (var obj in list)
|
||||
{
|
||||
if (obj is ISpawner spawner)
|
||||
{
|
||||
spawner.Respawn();
|
||||
}
|
||||
}
|
||||
|
||||
e.Mobile.SendMessage("Respawn completed.");
|
||||
}
|
||||
|
||||
e.Mobile.SendMessage("Respawn completed.");
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -20,54 +20,53 @@ using Server.Targeting;
|
|||
|
||||
using static Server.Types;
|
||||
|
||||
namespace Server.Engines.Spawners
|
||||
namespace Server.Engines.Spawners;
|
||||
|
||||
public class SpawnPropsCommand : BaseCommand
|
||||
{
|
||||
public class SpawnPropsCommand : BaseCommand
|
||||
public static void Configure()
|
||||
{
|
||||
public static void Configure()
|
||||
TargetCommands.Register(new SpawnPropsCommand());
|
||||
}
|
||||
|
||||
public SpawnPropsCommand()
|
||||
{
|
||||
AccessLevel = AccessLevel.GameMaster;
|
||||
Supports = CommandSupport.Complex | CommandSupport.Simple;
|
||||
Commands = ["SpawnProps"];
|
||||
ObjectTypes = ObjectTypes.Items;
|
||||
Usage = "SpawnProps";
|
||||
Description = "Shows a props gump that will modify the properties of spawn entries related to the chosen entity";
|
||||
ListOptimized = true;
|
||||
}
|
||||
|
||||
public override void ExecuteList(CommandEventArgs e, List<object> list)
|
||||
{
|
||||
if (list.Count == 0)
|
||||
{
|
||||
TargetCommands.Register(new SpawnPropsCommand());
|
||||
LogFailure("No matching objects found.");
|
||||
return;
|
||||
}
|
||||
|
||||
public SpawnPropsCommand()
|
||||
{
|
||||
AccessLevel = AccessLevel.GameMaster;
|
||||
Supports = CommandSupport.Complex | CommandSupport.Simple;
|
||||
Commands = new[] { "SpawnProps" };
|
||||
ObjectTypes = ObjectTypes.Items;
|
||||
Usage = "SpawnProps";
|
||||
Description = "Shows a props gump that will modify the properties of spawn entries related to the chosen entity";
|
||||
ListOptimized = true;
|
||||
}
|
||||
e.Mobile.SendMessage("Target the object you want to use as a template for modifying the spawner properties.");
|
||||
e.Mobile.Target = new InternalTarget(list);
|
||||
}
|
||||
|
||||
public override void ExecuteList(CommandEventArgs e, List<object> list)
|
||||
private class InternalTarget : Target
|
||||
{
|
||||
private readonly List<object> _list;
|
||||
|
||||
public InternalTarget(List<object> list) : base(-1, false, TargetFlags.None) => _list = list;
|
||||
|
||||
protected override void OnTarget(Mobile from, object targeted)
|
||||
{
|
||||
if (list.Count == 0)
|
||||
var type = targeted.GetType();
|
||||
if (!IsEntity(type))
|
||||
{
|
||||
LogFailure("No matching objects found.");
|
||||
return;
|
||||
from.SendMessage("No type with that name was found.");
|
||||
}
|
||||
|
||||
e.Mobile.SendMessage("Target the object you want to use as a template for modifying the spawner properties.");
|
||||
e.Mobile.Target = new InternalTarget(list);
|
||||
}
|
||||
|
||||
private class InternalTarget : Target
|
||||
{
|
||||
private readonly List<object> _list;
|
||||
|
||||
public InternalTarget(List<object> list) : base(-1, false, TargetFlags.None) => _list = list;
|
||||
|
||||
protected override void OnTarget(Mobile from, object targeted)
|
||||
{
|
||||
var type = targeted.GetType();
|
||||
if (!IsEntity(type))
|
||||
{
|
||||
from.SendMessage("No type with that name was found.");
|
||||
}
|
||||
|
||||
from.SendGump(new SpawnPropsGump(from, targeted, _list));
|
||||
}
|
||||
from.SendGump(new SpawnPropsGump(from, targeted, _list));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -15,174 +15,147 @@
|
|||
|
||||
using System;
|
||||
using System.Text.Json;
|
||||
using ModernUO.Serialization;
|
||||
using Server.Json;
|
||||
using Server.Mobiles;
|
||||
|
||||
namespace Server.Engines.Spawners
|
||||
namespace Server.Engines.Spawners;
|
||||
|
||||
[SerializationGenerator(0)]
|
||||
public partial class ProximitySpawner : Spawner
|
||||
{
|
||||
public class ProximitySpawner : Spawner
|
||||
[SerializableField(0)]
|
||||
[SerializedCommandProperty(AccessLevel.Developer)]
|
||||
private int _triggerRange;
|
||||
|
||||
[SerializableField(1)]
|
||||
[SerializedCommandProperty(AccessLevel.Developer)]
|
||||
private TextDefinition _spawnMessage;
|
||||
|
||||
[SerializableField(2)]
|
||||
[SerializedCommandProperty(AccessLevel.Developer)]
|
||||
private bool _instantFlag;
|
||||
|
||||
[Constructible(AccessLevel.Developer)]
|
||||
public ProximitySpawner()
|
||||
{
|
||||
[Constructible(AccessLevel.Developer)]
|
||||
public ProximitySpawner()
|
||||
}
|
||||
|
||||
[Constructible(AccessLevel.Developer)]
|
||||
public ProximitySpawner(string spawnName) : base(spawnName)
|
||||
{
|
||||
}
|
||||
|
||||
[Constructible(AccessLevel.Developer)]
|
||||
public ProximitySpawner(int amount, int minDelay, int maxDelay, int team, int homeRange, string spawnName)
|
||||
: base(amount, minDelay, maxDelay, team, homeRange, spawnName)
|
||||
{
|
||||
}
|
||||
|
||||
[Constructible(AccessLevel.Developer)]
|
||||
public ProximitySpawner(
|
||||
int amount, int minDelay, int maxDelay, int team, int homeRange, int triggerRange,
|
||||
string spawnMessage, bool instantFlag, string spawnName
|
||||
) : base(amount, minDelay, maxDelay, team, homeRange, spawnName)
|
||||
{
|
||||
TriggerRange = triggerRange;
|
||||
SpawnMessage = TextDefinition.Parse(spawnMessage);
|
||||
InstantFlag = instantFlag;
|
||||
}
|
||||
|
||||
[Constructible(AccessLevel.Developer)]
|
||||
public ProximitySpawner(
|
||||
int amount, TimeSpan minDelay, TimeSpan maxDelay, int team, int homeRange,
|
||||
params string[] spawnedNames
|
||||
) : base(amount, minDelay, maxDelay, team, homeRange, spawnedNames)
|
||||
{
|
||||
}
|
||||
|
||||
[Constructible(AccessLevel.Developer)]
|
||||
public ProximitySpawner(
|
||||
int amount, TimeSpan minDelay, TimeSpan maxDelay, int team, int homeRange, int triggerRange,
|
||||
TextDefinition spawnMessage, bool instantFlag, params string[] spawnedNames
|
||||
) : base(amount, minDelay, maxDelay, team, homeRange, spawnedNames)
|
||||
{
|
||||
TriggerRange = triggerRange;
|
||||
SpawnMessage = spawnMessage;
|
||||
InstantFlag = instantFlag;
|
||||
}
|
||||
|
||||
public ProximitySpawner(DynamicJson json, JsonSerializerOptions options) : base(json, options)
|
||||
{
|
||||
json.GetProperty("triggerRange", options, out int triggerRange);
|
||||
json.GetProperty("spawnMessage", options, out TextDefinition spawnMessage);
|
||||
json.GetProperty("instant", options, out bool instant);
|
||||
|
||||
TriggerRange = triggerRange;
|
||||
SpawnMessage = spawnMessage;
|
||||
InstantFlag = instant;
|
||||
}
|
||||
|
||||
public override string DefaultName => "Proximity Spawner";
|
||||
|
||||
public override bool HandlesOnMovement => true;
|
||||
|
||||
public override void ToJson(DynamicJson json, JsonSerializerOptions options)
|
||||
{
|
||||
json.SetProperty("triggerRange", options, TriggerRange);
|
||||
json.SetProperty("spawnMessage", options, SpawnMessage);
|
||||
json.SetProperty("instant", options, InstantFlag);
|
||||
}
|
||||
|
||||
public override void DoTimer(TimeSpan delay)
|
||||
{
|
||||
if (!Running)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
[Constructible(AccessLevel.Developer)]
|
||||
public ProximitySpawner(string spawnName)
|
||||
: base(spawnName)
|
||||
End = Core.Now + delay;
|
||||
}
|
||||
|
||||
public override void Respawn()
|
||||
{
|
||||
RemoveSpawns();
|
||||
|
||||
End = Core.Now;
|
||||
}
|
||||
|
||||
public virtual bool ValidTrigger(Mobile m)
|
||||
{
|
||||
if (m is BaseCreature bc && (bc.IsDeadBondedPet || !(bc.Controlled || bc.Summoned)))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
[Constructible(AccessLevel.Developer)]
|
||||
public ProximitySpawner(int amount, int minDelay, int maxDelay, int team, int homeRange, string spawnName)
|
||||
: base(amount, minDelay, maxDelay, team, homeRange, spawnName)
|
||||
return m.AccessLevel == AccessLevel.Player && (m.Player || m.Alive && !m.Hidden && m.CanBeDamaged());
|
||||
}
|
||||
|
||||
public override void OnMovement(Mobile m, Point3D oldLocation)
|
||||
{
|
||||
if (!Running)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
[Constructible(AccessLevel.Developer)]
|
||||
public ProximitySpawner(
|
||||
int amount, int minDelay, int maxDelay, int team, int homeRange, int triggerRange,
|
||||
string spawnMessage, bool instantFlag, string spawnName
|
||||
)
|
||||
: base(amount, minDelay, maxDelay, team, homeRange, spawnName)
|
||||
if (IsEmpty && End <= Core.Now && m.InRange(GetWorldLocation(), TriggerRange) &&
|
||||
m.Location != oldLocation && ValidTrigger(m))
|
||||
{
|
||||
TriggerRange = triggerRange;
|
||||
SpawnMessage = TextDefinition.Parse(spawnMessage);
|
||||
InstantFlag = instantFlag;
|
||||
}
|
||||
SpawnMessage.SendMessageTo(m);
|
||||
|
||||
[Constructible(AccessLevel.Developer)]
|
||||
public ProximitySpawner(
|
||||
int amount, TimeSpan minDelay, TimeSpan maxDelay, int team, int homeRange,
|
||||
params string[] spawnedNames
|
||||
)
|
||||
: base(amount, minDelay, maxDelay, team, homeRange, spawnedNames)
|
||||
{
|
||||
}
|
||||
DoTimer();
|
||||
Spawn();
|
||||
|
||||
[Constructible(AccessLevel.Developer)]
|
||||
public ProximitySpawner(
|
||||
int amount, TimeSpan minDelay, TimeSpan maxDelay, int team, int homeRange, int triggerRange,
|
||||
TextDefinition spawnMessage, bool instantFlag, params string[] spawnedNames
|
||||
)
|
||||
: base(amount, minDelay, maxDelay, team, homeRange, spawnedNames)
|
||||
{
|
||||
TriggerRange = triggerRange;
|
||||
SpawnMessage = spawnMessage;
|
||||
InstantFlag = instantFlag;
|
||||
}
|
||||
|
||||
public ProximitySpawner(DynamicJson json, JsonSerializerOptions options) : base(json, options)
|
||||
{
|
||||
json.GetProperty("triggerRange", options, out int triggerRange);
|
||||
json.GetProperty("spawnMessage", options, out TextDefinition spawnMessage);
|
||||
json.GetProperty("instant", options, out bool instant);
|
||||
|
||||
TriggerRange = triggerRange;
|
||||
SpawnMessage = spawnMessage;
|
||||
InstantFlag = instant;
|
||||
}
|
||||
|
||||
public ProximitySpawner(Serial serial)
|
||||
: base(serial)
|
||||
{
|
||||
}
|
||||
|
||||
[CommandProperty(AccessLevel.Developer)]
|
||||
public int TriggerRange { get; set; }
|
||||
|
||||
[CommandProperty(AccessLevel.Developer)]
|
||||
public TextDefinition SpawnMessage { get; set; }
|
||||
|
||||
[CommandProperty(AccessLevel.Developer)]
|
||||
public bool InstantFlag { get; set; }
|
||||
|
||||
public override string DefaultName => "Proximity Spawner";
|
||||
|
||||
public override bool HandlesOnMovement => true;
|
||||
|
||||
public override void ToJson(DynamicJson json, JsonSerializerOptions options)
|
||||
{
|
||||
json.SetProperty("triggerRange", options, TriggerRange);
|
||||
json.SetProperty("spawnMessage", options, SpawnMessage);
|
||||
json.SetProperty("instant", options, InstantFlag);
|
||||
}
|
||||
|
||||
public override void DoTimer(TimeSpan delay)
|
||||
{
|
||||
if (!Running)
|
||||
if (InstantFlag)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
End = Core.Now + delay;
|
||||
}
|
||||
|
||||
public override void Respawn()
|
||||
{
|
||||
RemoveSpawns();
|
||||
|
||||
End = Core.Now;
|
||||
}
|
||||
|
||||
public virtual bool ValidTrigger(Mobile m)
|
||||
{
|
||||
if (m is BaseCreature bc && (bc.IsDeadBondedPet || !(bc.Controlled || bc.Summoned)))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return m.AccessLevel == AccessLevel.Player && (m.Player || m.Alive && !m.Hidden && m.CanBeDamaged());
|
||||
}
|
||||
|
||||
public override void OnMovement(Mobile m, Point3D oldLocation)
|
||||
{
|
||||
if (!Running)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (IsEmpty && End <= Core.Now && m.InRange(GetWorldLocation(), TriggerRange) &&
|
||||
m.Location != oldLocation && ValidTrigger(m))
|
||||
{
|
||||
SpawnMessage.SendMessageTo(m);
|
||||
|
||||
DoTimer();
|
||||
Spawn();
|
||||
|
||||
if (InstantFlag)
|
||||
foreach (var spawned in Spawned.Keys)
|
||||
{
|
||||
foreach (var spawned in Spawned.Keys)
|
||||
if (spawned is Mobile mobile)
|
||||
{
|
||||
if (spawned is Mobile mobile)
|
||||
{
|
||||
mobile.Combatant = m;
|
||||
}
|
||||
mobile.Combatant = m;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public override void Serialize(IGenericWriter writer)
|
||||
{
|
||||
base.Serialize(writer);
|
||||
|
||||
writer.WriteEncodedInt(0); // version
|
||||
|
||||
writer.Write(TriggerRange);
|
||||
writer.Write(SpawnMessage);
|
||||
writer.Write(InstantFlag);
|
||||
}
|
||||
|
||||
public override void Deserialize(IGenericReader reader)
|
||||
{
|
||||
base.Deserialize(reader);
|
||||
|
||||
var version = reader.ReadEncodedInt();
|
||||
|
||||
TriggerRange = reader.ReadInt();
|
||||
SpawnMessage = reader.ReadTextDefinition();
|
||||
InstantFlag = reader.ReadBool();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -15,184 +15,172 @@
|
|||
|
||||
using System;
|
||||
using System.Text.Json;
|
||||
using ModernUO.Serialization;
|
||||
using Server.Json;
|
||||
using Server.Regions;
|
||||
|
||||
namespace Server.Engines.Spawners
|
||||
namespace Server.Engines.Spawners;
|
||||
|
||||
[SerializationGenerator(0)]
|
||||
public partial class RegionSpawner : Spawner
|
||||
{
|
||||
public class RegionSpawner : Spawner
|
||||
[SerializableField(0, getter: "private", setter: "private")]
|
||||
private string _spawnRegionName;
|
||||
|
||||
private BaseRegion _spawnRegion;
|
||||
|
||||
[Constructible(AccessLevel.Developer)]
|
||||
public RegionSpawner()
|
||||
{
|
||||
private BaseRegion m_SpawnRegion;
|
||||
}
|
||||
|
||||
[Constructible(AccessLevel.Developer)]
|
||||
public RegionSpawner()
|
||||
[Constructible(AccessLevel.Developer)]
|
||||
public RegionSpawner(string spawnedName) : base(spawnedName)
|
||||
{
|
||||
}
|
||||
|
||||
[Constructible(AccessLevel.Developer)]
|
||||
public RegionSpawner(
|
||||
int amount, int minDelay, int maxDelay, int team, int homeRange,
|
||||
params string[] spawnedNames
|
||||
) : this(
|
||||
amount,
|
||||
TimeSpan.FromMinutes(minDelay),
|
||||
TimeSpan.FromMinutes(maxDelay),
|
||||
team,
|
||||
homeRange,
|
||||
spawnedNames
|
||||
)
|
||||
{
|
||||
}
|
||||
|
||||
[Constructible(AccessLevel.Developer)]
|
||||
public RegionSpawner(
|
||||
int amount, TimeSpan minDelay, TimeSpan maxDelay, int team, int homeRange,
|
||||
params string[] spawnedNames
|
||||
) : base(amount, minDelay, maxDelay, team, homeRange, spawnedNames)
|
||||
{
|
||||
}
|
||||
|
||||
public RegionSpawner(DynamicJson json, JsonSerializerOptions options) : base(json, options)
|
||||
{
|
||||
json.GetProperty("map", options, out Map map);
|
||||
json.GetProperty("region", options, out string spawnRegion);
|
||||
|
||||
_spawnRegion = Region.Find(spawnRegion, map) as BaseRegion;
|
||||
_spawnRegion?.InitRectangles();
|
||||
}
|
||||
|
||||
[CommandProperty(AccessLevel.Developer)]
|
||||
public BaseRegion SpawnRegion
|
||||
{
|
||||
get => _spawnRegion;
|
||||
set
|
||||
{
|
||||
}
|
||||
|
||||
[Constructible(AccessLevel.Developer)]
|
||||
public RegionSpawner(string spawnedName) : base(spawnedName)
|
||||
{
|
||||
}
|
||||
|
||||
[Constructible(AccessLevel.Developer)]
|
||||
public RegionSpawner(
|
||||
int amount, int minDelay, int maxDelay, int team, int homeRange,
|
||||
params string[] spawnedNames
|
||||
) : this(
|
||||
amount,
|
||||
TimeSpan.FromMinutes(minDelay),
|
||||
TimeSpan.FromMinutes(maxDelay),
|
||||
team,
|
||||
homeRange,
|
||||
spawnedNames
|
||||
)
|
||||
{
|
||||
}
|
||||
|
||||
[Constructible(AccessLevel.Developer)]
|
||||
public RegionSpawner(
|
||||
int amount, TimeSpan minDelay, TimeSpan maxDelay, int team, int homeRange,
|
||||
params string[] spawnedNames
|
||||
) : base(amount, minDelay, maxDelay, team, homeRange, spawnedNames)
|
||||
{
|
||||
}
|
||||
|
||||
public RegionSpawner(DynamicJson json, JsonSerializerOptions options) : base(json, options)
|
||||
{
|
||||
json.GetProperty("map", options, out Map map);
|
||||
json.GetProperty("region", options, out string spawnRegion);
|
||||
|
||||
m_SpawnRegion = Region.Find(spawnRegion, map) as BaseRegion;
|
||||
m_SpawnRegion?.InitRectangles();
|
||||
}
|
||||
|
||||
public RegionSpawner(Serial serial) : base(serial)
|
||||
{
|
||||
}
|
||||
|
||||
[CommandProperty(AccessLevel.Developer)]
|
||||
public BaseRegion SpawnRegion
|
||||
{
|
||||
get => m_SpawnRegion;
|
||||
set
|
||||
{
|
||||
m_SpawnRegion = value;
|
||||
m_SpawnRegion?.InitRectangles();
|
||||
|
||||
InvalidateProperties();
|
||||
}
|
||||
}
|
||||
|
||||
public override void ToJson(DynamicJson json, JsonSerializerOptions options)
|
||||
{
|
||||
json.SetProperty("region", options, SpawnRegion.Name);
|
||||
}
|
||||
|
||||
public override void GetSpawnerProperties(IPropertyList list)
|
||||
{
|
||||
base.GetSpawnerProperties(list);
|
||||
|
||||
if (Running && m_SpawnRegion != null)
|
||||
{
|
||||
list.Add(1076228, $"{"region:"}\t{m_SpawnRegion.Name}"); // ~1_DUMMY~ ~2_DUMMY~
|
||||
}
|
||||
}
|
||||
|
||||
public override Point3D GetSpawnPosition(ISpawnable spawned, Map map)
|
||||
{
|
||||
if (m_SpawnRegion == null || map == null || map == Map.Internal || map != m_SpawnRegion.Map ||
|
||||
m_SpawnRegion.TotalWeight <= 0)
|
||||
{
|
||||
return Location;
|
||||
}
|
||||
|
||||
bool waterMob, waterOnlyMob;
|
||||
|
||||
if (spawned is Mobile mob)
|
||||
{
|
||||
waterMob = mob.CanSwim;
|
||||
waterOnlyMob = mob.CanSwim && mob.CantWalk;
|
||||
}
|
||||
else
|
||||
{
|
||||
waterMob = false;
|
||||
waterOnlyMob = false;
|
||||
}
|
||||
|
||||
// Try 10 times to find a valid location.
|
||||
for (var i = 0; i < 10; i++)
|
||||
{
|
||||
var rand = Utility.Random(m_SpawnRegion.TotalWeight);
|
||||
|
||||
var x = int.MinValue;
|
||||
var y = int.MinValue;
|
||||
|
||||
for (var j = 0; j < m_SpawnRegion.RectangleWeights.Length; j++)
|
||||
{
|
||||
var curWeight = m_SpawnRegion.RectangleWeights[j];
|
||||
|
||||
if (rand < curWeight)
|
||||
{
|
||||
var rect = m_SpawnRegion.Rectangles[j];
|
||||
|
||||
x = rect.Start.X + rand % rect.Width;
|
||||
y = rect.Start.Y + rand / rect.Width;
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
rand -= curWeight;
|
||||
}
|
||||
|
||||
var mapZ = map.GetAverageZ(x, y);
|
||||
|
||||
if (waterMob)
|
||||
{
|
||||
if (IsValidWater(map, x, y, Z))
|
||||
{
|
||||
return new Point3D(x, y, Z);
|
||||
}
|
||||
|
||||
if (IsValidWater(map, x, y, mapZ))
|
||||
{
|
||||
return new Point3D(x, y, mapZ);
|
||||
}
|
||||
}
|
||||
|
||||
if (!waterOnlyMob)
|
||||
{
|
||||
if (map.CanSpawnMobile(x, y, Z))
|
||||
{
|
||||
return new Point3D(x, y, Z);
|
||||
}
|
||||
|
||||
if (map.CanSpawnMobile(x, y, mapZ))
|
||||
{
|
||||
return new Point3D(x, y, mapZ);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return HomeLocation;
|
||||
}
|
||||
|
||||
public override void Deserialize(IGenericReader reader)
|
||||
{
|
||||
base.Deserialize(reader);
|
||||
|
||||
var version = reader.ReadEncodedInt();
|
||||
|
||||
m_SpawnRegion = Region.Find(reader.ReadString(), Map) as BaseRegion;
|
||||
m_SpawnRegion?.InitRectangles();
|
||||
}
|
||||
|
||||
public override void Serialize(IGenericWriter writer)
|
||||
{
|
||||
base.Serialize(writer);
|
||||
|
||||
writer.WriteEncodedInt(0); // version
|
||||
|
||||
writer.Write(m_SpawnRegion?.Name);
|
||||
_spawnRegion = value;
|
||||
SpawnRegionName = _spawnRegion?.Name;
|
||||
_spawnRegion?.InitRectangles();
|
||||
InvalidateProperties();
|
||||
}
|
||||
}
|
||||
|
||||
public override void ToJson(DynamicJson json, JsonSerializerOptions options)
|
||||
{
|
||||
json.SetProperty("region", options, SpawnRegion.Name);
|
||||
}
|
||||
|
||||
public override void GetSpawnerProperties(IPropertyList list)
|
||||
{
|
||||
base.GetSpawnerProperties(list);
|
||||
|
||||
if (Running && _spawnRegion != null)
|
||||
{
|
||||
list.Add(1076228, $"{"region:"}\t{_spawnRegion.Name}"); // ~1_DUMMY~ ~2_DUMMY~
|
||||
}
|
||||
}
|
||||
|
||||
public override Point3D GetSpawnPosition(ISpawnable spawned, Map map)
|
||||
{
|
||||
if (_spawnRegion == null || map == null || map == Map.Internal || map != _spawnRegion.Map ||
|
||||
_spawnRegion.TotalWeight <= 0)
|
||||
{
|
||||
return Location;
|
||||
}
|
||||
|
||||
bool waterMob, waterOnlyMob;
|
||||
|
||||
if (spawned is Mobile mob)
|
||||
{
|
||||
waterMob = mob.CanSwim;
|
||||
waterOnlyMob = mob.CanSwim && mob.CantWalk;
|
||||
}
|
||||
else
|
||||
{
|
||||
waterMob = false;
|
||||
waterOnlyMob = false;
|
||||
}
|
||||
|
||||
// Try 10 times to find a valid location.
|
||||
for (var i = 0; i < 10; i++)
|
||||
{
|
||||
var rand = Utility.Random(_spawnRegion.TotalWeight);
|
||||
|
||||
var x = int.MinValue;
|
||||
var y = int.MinValue;
|
||||
|
||||
for (var j = 0; j < _spawnRegion.RectangleWeights.Length; j++)
|
||||
{
|
||||
var curWeight = _spawnRegion.RectangleWeights[j];
|
||||
|
||||
if (rand < curWeight)
|
||||
{
|
||||
var rect = _spawnRegion.Rectangles[j];
|
||||
|
||||
x = rect.Start.X + rand % rect.Width;
|
||||
y = rect.Start.Y + rand / rect.Width;
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
rand -= curWeight;
|
||||
}
|
||||
|
||||
var mapZ = map.GetAverageZ(x, y);
|
||||
|
||||
if (waterMob)
|
||||
{
|
||||
if (IsValidWater(map, x, y, Z))
|
||||
{
|
||||
return new Point3D(x, y, Z);
|
||||
}
|
||||
|
||||
if (IsValidWater(map, x, y, mapZ))
|
||||
{
|
||||
return new Point3D(x, y, mapZ);
|
||||
}
|
||||
}
|
||||
|
||||
if (!waterOnlyMob)
|
||||
{
|
||||
if (map.CanSpawnMobile(x, y, Z))
|
||||
{
|
||||
return new Point3D(x, y, Z);
|
||||
}
|
||||
|
||||
if (map.CanSpawnMobile(x, y, mapZ))
|
||||
{
|
||||
return new Point3D(x, y, mapZ);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return HomeLocation;
|
||||
}
|
||||
|
||||
[AfterDeserialization(false)]
|
||||
private void AfterDeserialization()
|
||||
{
|
||||
_spawnRegion = Region.Find(_spawnRegionName, Map) as BaseRegion;
|
||||
_spawnRegion?.InitRectangles();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -20,134 +20,133 @@ using Server.Mobiles;
|
|||
using Server.Network;
|
||||
using Server.Text;
|
||||
|
||||
namespace Server.Gumps
|
||||
namespace Server.Gumps;
|
||||
|
||||
public class SpawnPropsGump : PropertiesGump
|
||||
{
|
||||
public class SpawnPropsGump : PropertiesGump
|
||||
public static readonly HashSet<string> MobileAttributes = new()
|
||||
{
|
||||
public static readonly HashSet<string> MobileAttributes = new()
|
||||
{
|
||||
nameof(Mobile.Hue),
|
||||
nameof(Mobile.Str),
|
||||
nameof(Mobile.Dex),
|
||||
nameof(Mobile.Int),
|
||||
nameof(BaseCreature.HitsMaxSeed),
|
||||
nameof(BaseCreature.Hits),
|
||||
nameof(BaseCreature.DamageMin),
|
||||
nameof(BaseCreature.DamageMax),
|
||||
nameof(BaseCreature.ActiveSpeed),
|
||||
nameof(BaseCreature.PassiveSpeed),
|
||||
nameof(BaseCreature.VirtualArmor)
|
||||
};
|
||||
nameof(Mobile.Hue),
|
||||
nameof(Mobile.Str),
|
||||
nameof(Mobile.Dex),
|
||||
nameof(Mobile.Int),
|
||||
nameof(BaseCreature.HitsMaxSeed),
|
||||
nameof(BaseCreature.Hits),
|
||||
nameof(BaseCreature.DamageMin),
|
||||
nameof(BaseCreature.DamageMax),
|
||||
nameof(BaseCreature.ActiveSpeed),
|
||||
nameof(BaseCreature.PassiveSpeed),
|
||||
nameof(BaseCreature.VirtualArmor)
|
||||
};
|
||||
|
||||
private List<object> _spawners;
|
||||
private List<object> _spawners;
|
||||
|
||||
public SpawnPropsGump(Mobile mobile, object o, List<object> spawners) : base(mobile, o)
|
||||
public SpawnPropsGump(Mobile mobile, object o, List<object> spawners) : base(mobile, o)
|
||||
{
|
||||
_spawners = spawners;
|
||||
}
|
||||
|
||||
public SpawnPropsGump(Mobile mobile, object o, Stack<StackEntry> stack, StackEntry parent, List<object> spawners) : base(
|
||||
mobile, o, stack, parent
|
||||
)
|
||||
{
|
||||
_spawners = spawners;
|
||||
}
|
||||
|
||||
public SpawnPropsGump(Mobile mobile, object o, Stack<StackEntry> stack, List<object> list, int page, List<object> spawners) : base(
|
||||
mobile, o, stack, list, page
|
||||
)
|
||||
{
|
||||
_spawners = spawners;
|
||||
}
|
||||
|
||||
protected override int TotalHeight => base.TotalHeight + PropsConfig.ApplySize;
|
||||
|
||||
protected override void Initialize(int page)
|
||||
{
|
||||
base.Initialize(page);
|
||||
var totalHeight = TotalHeight - PropsConfig.ApplySize;
|
||||
|
||||
AddButton(BackWidth / 3, PropsConfig.BorderSize + totalHeight + PropsConfig.BorderSize, 5204, 5205, 3);
|
||||
}
|
||||
|
||||
public override void SendPropertiesGump() =>
|
||||
m_Mobile.SendGump(new SpawnPropsGump(m_Mobile, m_Object, m_Stack, m_List, m_Page, _spawners));
|
||||
|
||||
public static object GetPropValue(object src, string propName) =>
|
||||
src.GetType().GetProperty(propName)?.GetValue(src, null);
|
||||
|
||||
public override void OnResponse(NetState state, RelayInfo info)
|
||||
{
|
||||
var from = state.Mobile;
|
||||
|
||||
if (!BaseCommand.IsAccessible(from, m_Object))
|
||||
{
|
||||
_spawners = spawners;
|
||||
from.SendMessage("You may no longer access their properties.");
|
||||
return;
|
||||
}
|
||||
|
||||
public SpawnPropsGump(Mobile mobile, object o, Stack<StackEntry> stack, StackEntry parent, List<object> spawners) : base(
|
||||
mobile, o, stack, parent
|
||||
)
|
||||
switch (info.ButtonID)
|
||||
{
|
||||
_spawners = spawners;
|
||||
}
|
||||
|
||||
public SpawnPropsGump(Mobile mobile, object o, Stack<StackEntry> stack, List<object> list, int page, List<object> spawners) : base(
|
||||
mobile, o, stack, list, page
|
||||
)
|
||||
{
|
||||
_spawners = spawners;
|
||||
}
|
||||
|
||||
protected override int TotalHeight => base.TotalHeight + PropsConfig.ApplySize;
|
||||
|
||||
protected override void Initialize(int page)
|
||||
{
|
||||
base.Initialize(page);
|
||||
var totalHeight = TotalHeight - PropsConfig.ApplySize;
|
||||
|
||||
AddButton(BackWidth / 3, PropsConfig.BorderSize + totalHeight + PropsConfig.BorderSize, 5204, 5205, 3);
|
||||
}
|
||||
|
||||
public override void SendPropertiesGump() =>
|
||||
m_Mobile.SendGump(new SpawnPropsGump(m_Mobile, m_Object, m_Stack, m_List, m_Page, _spawners));
|
||||
|
||||
public static object GetPropValue(object src, string propName) =>
|
||||
src.GetType().GetProperty(propName)?.GetValue(src, null);
|
||||
|
||||
public override void OnResponse(NetState state, RelayInfo info)
|
||||
{
|
||||
var from = state.Mobile;
|
||||
|
||||
if (!BaseCommand.IsAccessible(from, m_Object))
|
||||
{
|
||||
from.SendMessage("You may no longer access their properties.");
|
||||
return;
|
||||
}
|
||||
|
||||
switch (info.ButtonID)
|
||||
{
|
||||
default:
|
||||
{
|
||||
base.OnResponse(state, info);
|
||||
break;
|
||||
}
|
||||
case 3: // Apply
|
||||
{
|
||||
using var propsBuilder = ValueStringBuilder.Create();
|
||||
bool first = true;
|
||||
foreach (var attr in MobileAttributes)
|
||||
{
|
||||
var prop = GetPropValue(m_Object, attr);
|
||||
if (prop != null)
|
||||
{
|
||||
if (first)
|
||||
{
|
||||
first = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
propsBuilder.Append(' ');
|
||||
}
|
||||
|
||||
propsBuilder.Append(attr);
|
||||
propsBuilder.Append(' ');
|
||||
propsBuilder.Append(prop.ToString()); // TODO: Replace with ZString, or IFormatter code
|
||||
}
|
||||
}
|
||||
|
||||
var name = m_Object.GetType().Name;
|
||||
var props = propsBuilder.ToString();
|
||||
|
||||
m_Mobile.SendMessage("Updating spawners...");
|
||||
|
||||
foreach (var obj in _spawners)
|
||||
{
|
||||
if (obj is BaseSpawner spawner)
|
||||
{
|
||||
EditSpawnCommand.UpdateSpawner(spawner, name, null, props);
|
||||
}
|
||||
}
|
||||
|
||||
m_Mobile.SendMessage("Update completed.");
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected override bool ShowAttribute(string name)
|
||||
{
|
||||
foreach (var item in MobileAttributes)
|
||||
{
|
||||
if (item.InsensitiveEquals(name))
|
||||
default:
|
||||
{
|
||||
return true;
|
||||
base.OnResponse(state, info);
|
||||
break;
|
||||
}
|
||||
}
|
||||
case 3: // Apply
|
||||
{
|
||||
using var propsBuilder = ValueStringBuilder.Create();
|
||||
bool first = true;
|
||||
foreach (var attr in MobileAttributes)
|
||||
{
|
||||
var prop = GetPropValue(m_Object, attr);
|
||||
if (prop != null)
|
||||
{
|
||||
if (first)
|
||||
{
|
||||
first = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
propsBuilder.Append(' ');
|
||||
}
|
||||
|
||||
return false;
|
||||
propsBuilder.Append(attr);
|
||||
propsBuilder.Append(' ');
|
||||
propsBuilder.Append(prop.ToString()); // TODO: Replace with ZString, or IFormatter code
|
||||
}
|
||||
}
|
||||
|
||||
var name = m_Object.GetType().Name;
|
||||
var props = propsBuilder.ToString();
|
||||
|
||||
m_Mobile.SendMessage("Updating spawners...");
|
||||
|
||||
foreach (var obj in _spawners)
|
||||
{
|
||||
if (obj is BaseSpawner spawner)
|
||||
{
|
||||
EditSpawnCommand.UpdateSpawner(spawner, name, null, props);
|
||||
}
|
||||
}
|
||||
|
||||
m_Mobile.SendMessage("Update completed.");
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected override bool ShowAttribute(string name)
|
||||
{
|
||||
foreach (var item in MobileAttributes)
|
||||
{
|
||||
if (item.InsensitiveEquals(name))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,162 +1,145 @@
|
|||
using System;
|
||||
using System.Text.Json;
|
||||
using ModernUO.Serialization;
|
||||
using Server.Json;
|
||||
|
||||
namespace Server.Engines.Spawners
|
||||
namespace Server.Engines.Spawners;
|
||||
|
||||
[SerializationGenerator(0)]
|
||||
public partial class Spawner : BaseSpawner
|
||||
{
|
||||
public class Spawner : BaseSpawner
|
||||
[Constructible(AccessLevel.Developer)]
|
||||
public Spawner()
|
||||
{
|
||||
[Constructible(AccessLevel.Developer)]
|
||||
public Spawner()
|
||||
}
|
||||
|
||||
[Constructible(AccessLevel.Developer)]
|
||||
public Spawner(string spawnedName) : base(spawnedName)
|
||||
{
|
||||
}
|
||||
|
||||
[Constructible(AccessLevel.Developer)]
|
||||
public Spawner(
|
||||
int amount, int minDelay, int maxDelay, int team, int homeRange,
|
||||
params string[] spawnedNames
|
||||
) : this(
|
||||
amount,
|
||||
TimeSpan.FromMinutes(minDelay),
|
||||
TimeSpan.FromMinutes(maxDelay),
|
||||
team,
|
||||
homeRange,
|
||||
spawnedNames
|
||||
)
|
||||
{
|
||||
}
|
||||
|
||||
[Constructible(AccessLevel.Developer)]
|
||||
public Spawner(
|
||||
int amount, TimeSpan minDelay, TimeSpan maxDelay, int team, int homeRange,
|
||||
params string[] spawnedNames
|
||||
) : base(amount, minDelay, maxDelay, team, homeRange, spawnedNames)
|
||||
{
|
||||
}
|
||||
|
||||
public Spawner(DynamicJson json, JsonSerializerOptions options) : base(json, options)
|
||||
{
|
||||
}
|
||||
|
||||
public static bool IsValidWater(Map map, int x, int y, int z)
|
||||
{
|
||||
if (!Region.Find(new Point3D(x, y, z), map).AllowSpawn() || !map.CanFit(x, y, z, 16, false, true, false))
|
||||
{
|
||||
}
|
||||
|
||||
[Constructible(AccessLevel.Developer)]
|
||||
public Spawner(string spawnedName) : base(spawnedName)
|
||||
{
|
||||
}
|
||||
|
||||
[Constructible(AccessLevel.Developer)]
|
||||
public Spawner(
|
||||
int amount, int minDelay, int maxDelay, int team, int homeRange,
|
||||
params string[] spawnedNames
|
||||
) : this(
|
||||
amount,
|
||||
TimeSpan.FromMinutes(minDelay),
|
||||
TimeSpan.FromMinutes(maxDelay),
|
||||
team,
|
||||
homeRange,
|
||||
spawnedNames
|
||||
)
|
||||
{
|
||||
}
|
||||
|
||||
[Constructible(AccessLevel.Developer)]
|
||||
public Spawner(
|
||||
int amount, TimeSpan minDelay, TimeSpan maxDelay, int team, int homeRange,
|
||||
params string[] spawnedNames
|
||||
) : base(amount, minDelay, maxDelay, team, homeRange, spawnedNames)
|
||||
{
|
||||
}
|
||||
|
||||
public Spawner(DynamicJson json, JsonSerializerOptions options) : base(json, options)
|
||||
{
|
||||
}
|
||||
|
||||
public Spawner(Serial serial) : base(serial)
|
||||
{
|
||||
}
|
||||
|
||||
public static bool IsValidWater(Map map, int x, int y, int z)
|
||||
{
|
||||
if (!Region.Find(new Point3D(x, y, z), map).AllowSpawn() || !map.CanFit(x, y, z, 16, false, true, false))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
var landTile = map.Tiles.GetLandTile(x, y);
|
||||
|
||||
if (landTile.Z == z && (TileData.LandTable[landTile.ID & TileData.MaxLandValue].Flags & TileFlag.Wet) != 0)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
foreach (var staticTile in map.Tiles.GetStaticAndMultiTiles(x, y))
|
||||
{
|
||||
if (staticTile.Z == z && TileData.ItemTable[staticTile.ID & TileData.MaxItemValue].Wet)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
public override bool OnDefragSpawn(ISpawnable spawned, bool remove)
|
||||
{
|
||||
// To despawn a mob that was lured 4x away from its spawner
|
||||
// TODO: Move this to a config
|
||||
if (spawned is BaseCreature c && c.Combatant == null && c.GetDistanceToSqrt( Location ) > c.RangeHome * 4)
|
||||
{
|
||||
c.Delete();
|
||||
remove = true;
|
||||
}
|
||||
var landTile = map.Tiles.GetLandTile(x, y);
|
||||
|
||||
return base.OnDefragSpawn(entry, spawned, remove);
|
||||
if (landTile.Z == z && (TileData.LandTable[landTile.ID & TileData.MaxLandValue].Flags & TileFlag.Wet) != 0)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
*/
|
||||
|
||||
public override Point3D GetSpawnPosition(ISpawnable spawned, Map map)
|
||||
foreach (var staticTile in map.Tiles.GetStaticAndMultiTiles(x, y))
|
||||
{
|
||||
if (map == null || map == Map.Internal)
|
||||
if (staticTile.Z == z && TileData.ItemTable[staticTile.ID & TileData.MaxItemValue].Wet)
|
||||
{
|
||||
return Location;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
bool waterMob, waterOnlyMob;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (spawned is Mobile mob)
|
||||
/*
|
||||
public override bool OnDefragSpawn(ISpawnable spawned, bool remove)
|
||||
{
|
||||
// To despawn a mob that was lured 4x away from its spawner
|
||||
// TODO: Move this to a config
|
||||
if (spawned is BaseCreature c && c.Combatant == null && c.GetDistanceToSqrt( Location ) > c.RangeHome * 4)
|
||||
{
|
||||
c.Delete();
|
||||
remove = true;
|
||||
}
|
||||
|
||||
return base.OnDefragSpawn(entry, spawned, remove);
|
||||
}
|
||||
*/
|
||||
|
||||
public override Point3D GetSpawnPosition(ISpawnable spawned, Map map)
|
||||
{
|
||||
if (map == null || map == Map.Internal)
|
||||
{
|
||||
return Location;
|
||||
}
|
||||
|
||||
bool waterMob, waterOnlyMob;
|
||||
|
||||
if (spawned is Mobile mob)
|
||||
{
|
||||
waterMob = mob.CanSwim;
|
||||
waterOnlyMob = mob.CanSwim && mob.CantWalk;
|
||||
}
|
||||
else
|
||||
{
|
||||
waterMob = false;
|
||||
waterOnlyMob = false;
|
||||
}
|
||||
|
||||
// Try 10 times to find a valid location.
|
||||
for (var i = 0; i < 10; i++)
|
||||
{
|
||||
var x = Location.X + (Utility.Random(HomeRange * 2 + 1) - HomeRange);
|
||||
var y = Location.Y + (Utility.Random(HomeRange * 2 + 1) - HomeRange);
|
||||
|
||||
var mapZ = map.GetAverageZ(x, y);
|
||||
|
||||
if (waterMob)
|
||||
{
|
||||
waterMob = mob.CanSwim;
|
||||
waterOnlyMob = mob.CanSwim && mob.CantWalk;
|
||||
}
|
||||
else
|
||||
{
|
||||
waterMob = false;
|
||||
waterOnlyMob = false;
|
||||
}
|
||||
|
||||
// Try 10 times to find a valid location.
|
||||
for (var i = 0; i < 10; i++)
|
||||
{
|
||||
var x = Location.X + (Utility.Random(HomeRange * 2 + 1) - HomeRange);
|
||||
var y = Location.Y + (Utility.Random(HomeRange * 2 + 1) - HomeRange);
|
||||
|
||||
var mapZ = map.GetAverageZ(x, y);
|
||||
|
||||
if (waterMob)
|
||||
if (IsValidWater(map, x, y, Z))
|
||||
{
|
||||
if (IsValidWater(map, x, y, Z))
|
||||
{
|
||||
return new Point3D(x, y, Z);
|
||||
}
|
||||
|
||||
if (IsValidWater(map, x, y, mapZ))
|
||||
{
|
||||
return new Point3D(x, y, mapZ);
|
||||
}
|
||||
return new Point3D(x, y, Z);
|
||||
}
|
||||
|
||||
if (!waterOnlyMob)
|
||||
if (IsValidWater(map, x, y, mapZ))
|
||||
{
|
||||
if (map.CanSpawnMobile(x, y, Z))
|
||||
{
|
||||
return new Point3D(x, y, Z);
|
||||
}
|
||||
|
||||
if (map.CanSpawnMobile(x, y, mapZ))
|
||||
{
|
||||
return new Point3D(x, y, mapZ);
|
||||
}
|
||||
return new Point3D(x, y, mapZ);
|
||||
}
|
||||
}
|
||||
|
||||
return HomeLocation;
|
||||
if (!waterOnlyMob)
|
||||
{
|
||||
if (map.CanSpawnMobile(x, y, Z))
|
||||
{
|
||||
return new Point3D(x, y, Z);
|
||||
}
|
||||
|
||||
if (map.CanSpawnMobile(x, y, mapZ))
|
||||
{
|
||||
return new Point3D(x, y, mapZ);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public override void Deserialize(IGenericReader reader)
|
||||
{
|
||||
base.Deserialize(reader);
|
||||
|
||||
var version = reader.ReadEncodedInt();
|
||||
}
|
||||
|
||||
public override void Serialize(IGenericWriter writer)
|
||||
{
|
||||
base.Serialize(writer);
|
||||
|
||||
writer.WriteEncodedInt(0); // version
|
||||
}
|
||||
return HomeLocation;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,122 +1,104 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Text.Json.Serialization;
|
||||
using ModernUO.Serialization;
|
||||
using Server.Json;
|
||||
|
||||
namespace Server.Engines.Spawners
|
||||
namespace Server.Engines.Spawners;
|
||||
|
||||
[SerializationGenerator(1, false)]
|
||||
public partial class SpawnerEntry
|
||||
{
|
||||
public class SpawnerEntry
|
||||
[DirtyTrackingEntity]
|
||||
private BaseSpawner _parent;
|
||||
|
||||
[SerializableField(0)]
|
||||
[SerializedJsonPropertyName("name")]
|
||||
private string _spawnedName;
|
||||
|
||||
[SerializableField(1)]
|
||||
[SerializedJsonPropertyName("probability")]
|
||||
private int _spawnedProbability;
|
||||
|
||||
[SerializableField(2)]
|
||||
[SerializedJsonPropertyName("maxCount")]
|
||||
private int _spawnedMaxCount;
|
||||
|
||||
[SerializableField(3)]
|
||||
[SerializedJsonPropertyName("properties")]
|
||||
private string _properties;
|
||||
|
||||
[SerializableField(4)]
|
||||
[SerializedJsonPropertyName("parameters")]
|
||||
private string _parameters;
|
||||
|
||||
[SerializedJsonIgnore]
|
||||
[SerializableField(5)]
|
||||
private List<ISpawnable> _spawned;
|
||||
|
||||
public SpawnerEntry(BaseSpawner parent)
|
||||
{
|
||||
public SpawnerEntry() => Spawned = new List<ISpawnable>();
|
||||
_parent = parent;
|
||||
_spawned = new List<ISpawnable>();
|
||||
}
|
||||
|
||||
public SpawnerEntry(
|
||||
string name,
|
||||
int probability,
|
||||
int maxcount,
|
||||
string properties = null,
|
||||
string parameters = null
|
||||
) : this()
|
||||
public SpawnerEntry(
|
||||
BaseSpawner parent,
|
||||
string name,
|
||||
int probability,
|
||||
int maxcount,
|
||||
string properties = null,
|
||||
string parameters = null
|
||||
) : this(parent)
|
||||
{
|
||||
SpawnedName = name;
|
||||
SpawnedProbability = probability;
|
||||
SpawnedMaxCount = maxcount;
|
||||
Properties = properties;
|
||||
Parameters = parameters;
|
||||
}
|
||||
|
||||
private void Deserialize(IGenericReader reader, int version)
|
||||
{
|
||||
SpawnedName = reader.ReadString();
|
||||
SpawnedProbability = reader.ReadInt();
|
||||
SpawnedMaxCount = reader.ReadInt();
|
||||
|
||||
Properties = reader.ReadString();
|
||||
Parameters = reader.ReadString();
|
||||
|
||||
var count = reader.ReadInt();
|
||||
|
||||
Spawned = new List<ISpawnable>(count);
|
||||
|
||||
for (var i = 0; i < count; ++i)
|
||||
{
|
||||
SpawnedName = name;
|
||||
SpawnedProbability = probability;
|
||||
SpawnedMaxCount = maxcount;
|
||||
Properties = properties;
|
||||
Parameters = parameters;
|
||||
}
|
||||
var e = reader.ReadEntity<ISpawnable>();
|
||||
|
||||
public SpawnerEntry(BaseSpawner parent, IGenericReader reader)
|
||||
{
|
||||
var version = reader.ReadInt();
|
||||
|
||||
SpawnedName = reader.ReadString();
|
||||
SpawnedProbability = reader.ReadInt();
|
||||
SpawnedMaxCount = reader.ReadInt();
|
||||
|
||||
Properties = reader.ReadString();
|
||||
Parameters = reader.ReadString();
|
||||
|
||||
var count = reader.ReadInt();
|
||||
|
||||
Spawned = new List<ISpawnable>(count);
|
||||
|
||||
for (var i = 0; i < count; ++i)
|
||||
if (e != null)
|
||||
{
|
||||
var e = reader.ReadEntity<ISpawnable>();
|
||||
e.Spawner = _parent;
|
||||
|
||||
if (e != null)
|
||||
{
|
||||
e.Spawner = parent;
|
||||
|
||||
Spawned.Add(e);
|
||||
parent.Spawned.TryAdd(e, this);
|
||||
}
|
||||
Spawned.Add(e);
|
||||
_parent.Spawned.TryAdd(e, this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[JsonPropertyName("name")]
|
||||
public string SpawnedName { get; set; }
|
||||
[JsonIgnore]
|
||||
public EntryFlags Valid { get; set; }
|
||||
|
||||
[JsonPropertyName("parameters")]
|
||||
public string Parameters { get; set; }
|
||||
[JsonIgnore]
|
||||
public bool IsFull => Spawned.Count >= SpawnedMaxCount;
|
||||
|
||||
[JsonPropertyName("properties")]
|
||||
public string Properties { get; set; }
|
||||
|
||||
[JsonPropertyName("maxCount")]
|
||||
public int SpawnedMaxCount { get; set; }
|
||||
|
||||
[JsonPropertyName("probability")]
|
||||
public int SpawnedProbability { get; set; }
|
||||
|
||||
[JsonIgnore]
|
||||
public EntryFlags Valid { get; set; }
|
||||
|
||||
[JsonIgnore]
|
||||
public List<ISpawnable> Spawned { get; }
|
||||
|
||||
[JsonIgnore]
|
||||
public bool IsFull => Spawned.Count >= SpawnedMaxCount;
|
||||
|
||||
public void Serialize(IGenericWriter writer)
|
||||
public void Defrag(BaseSpawner parent)
|
||||
{
|
||||
for (var i = 0; i < Spawned.Count; ++i)
|
||||
{
|
||||
writer.Write(0); // version
|
||||
var spawned = Spawned[i];
|
||||
|
||||
writer.Write(SpawnedName);
|
||||
writer.Write(SpawnedProbability);
|
||||
writer.Write(SpawnedMaxCount);
|
||||
|
||||
writer.Write(Properties);
|
||||
writer.Write(Parameters);
|
||||
|
||||
writer.Write(Spawned.Count);
|
||||
|
||||
for (var i = 0; i < Spawned.Count; ++i)
|
||||
if (parent.OnDefragSpawn(spawned, false))
|
||||
{
|
||||
object o = Spawned[i];
|
||||
|
||||
if (o is Item item)
|
||||
{
|
||||
writer.Write(item);
|
||||
}
|
||||
else if (o is Mobile mobile)
|
||||
{
|
||||
writer.Write(mobile);
|
||||
}
|
||||
else
|
||||
{
|
||||
writer.Write(Serial.MinusOne);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void Defrag(BaseSpawner parent)
|
||||
{
|
||||
for (var i = 0; i < Spawned.Count; ++i)
|
||||
{
|
||||
var spawned = Spawned[i];
|
||||
|
||||
if (parent.OnDefragSpawn(spawned, false))
|
||||
{
|
||||
Spawned.RemoveAt(i--);
|
||||
}
|
||||
Spawned.RemoveAt(i--);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
100
Projects/UOContent/Migrations/Server.Engines.Spawners.BaseSpawner.v10.json
generated
Normal file
100
Projects/UOContent/Migrations/Server.Engines.Spawners.BaseSpawner.v10.json
generated
Normal file
|
|
@ -0,0 +1,100 @@
|
|||
{
|
||||
"version": 10,
|
||||
"type": "Server.Engines.Spawners.BaseSpawner",
|
||||
"properties": [
|
||||
{
|
||||
"name": "Guid",
|
||||
"type": "System.Guid",
|
||||
"rule": "PrimitiveTypeMigrationRule"
|
||||
},
|
||||
{
|
||||
"name": "ReturnOnDeactivate",
|
||||
"type": "bool",
|
||||
"rule": "PrimitiveTypeMigrationRule",
|
||||
"ruleArguments": [
|
||||
""
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "Entries",
|
||||
"type": "System.Collections.Generic.List\u003CServer.Engines.Spawners.SpawnerEntry\u003E",
|
||||
"rule": "ListMigrationRule",
|
||||
"ruleArguments": [
|
||||
"Server.Engines.Spawners.SpawnerEntry",
|
||||
"RawSerializableMigrationRule",
|
||||
"DeserializationRequiresParent"
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "WalkingRange",
|
||||
"type": "int",
|
||||
"rule": "PrimitiveTypeMigrationRule",
|
||||
"ruleArguments": [
|
||||
""
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "WayPoint",
|
||||
"type": "Server.Items.WayPoint",
|
||||
"rule": "SerializableInterfaceMigrationRule"
|
||||
},
|
||||
{
|
||||
"name": "Group",
|
||||
"type": "bool",
|
||||
"rule": "PrimitiveTypeMigrationRule",
|
||||
"ruleArguments": [
|
||||
""
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "MinDelay",
|
||||
"type": "System.TimeSpan",
|
||||
"rule": "PrimitiveTypeMigrationRule"
|
||||
},
|
||||
{
|
||||
"name": "MaxDelay",
|
||||
"type": "System.TimeSpan",
|
||||
"rule": "PrimitiveTypeMigrationRule"
|
||||
},
|
||||
{
|
||||
"name": "Count",
|
||||
"type": "int",
|
||||
"rule": "PrimitiveTypeMigrationRule",
|
||||
"ruleArguments": [
|
||||
""
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "Team",
|
||||
"type": "int",
|
||||
"rule": "PrimitiveTypeMigrationRule",
|
||||
"ruleArguments": [
|
||||
""
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "HomeRange",
|
||||
"type": "int",
|
||||
"rule": "PrimitiveTypeMigrationRule",
|
||||
"ruleArguments": [
|
||||
""
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "Running",
|
||||
"type": "bool",
|
||||
"rule": "PrimitiveTypeMigrationRule",
|
||||
"ruleArguments": [
|
||||
""
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "End",
|
||||
"type": "System.DateTime",
|
||||
"rule": "PrimitiveTypeMigrationRule",
|
||||
"ruleArguments": [
|
||||
""
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
30
Projects/UOContent/Migrations/Server.Engines.Spawners.ProximitySpawner.v0.json
generated
Normal file
30
Projects/UOContent/Migrations/Server.Engines.Spawners.ProximitySpawner.v0.json
generated
Normal file
|
|
@ -0,0 +1,30 @@
|
|||
{
|
||||
"version": 0,
|
||||
"type": "Server.Engines.Spawners.ProximitySpawner",
|
||||
"properties": [
|
||||
{
|
||||
"name": "TriggerRange",
|
||||
"type": "int",
|
||||
"rule": "PrimitiveTypeMigrationRule",
|
||||
"ruleArguments": [
|
||||
""
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "SpawnMessage",
|
||||
"type": "Server.TextDefinition",
|
||||
"rule": "PrimitiveUOTypeMigrationRule",
|
||||
"ruleArguments": [
|
||||
"TextDefinition"
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "InstantFlag",
|
||||
"type": "bool",
|
||||
"rule": "PrimitiveTypeMigrationRule",
|
||||
"ruleArguments": [
|
||||
""
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
14
Projects/UOContent/Migrations/Server.Engines.Spawners.RegionSpawner.v0.json
generated
Normal file
14
Projects/UOContent/Migrations/Server.Engines.Spawners.RegionSpawner.v0.json
generated
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
{
|
||||
"version": 0,
|
||||
"type": "Server.Engines.Spawners.RegionSpawner",
|
||||
"properties": [
|
||||
{
|
||||
"name": "SpawnRegionName",
|
||||
"type": "string",
|
||||
"rule": "PrimitiveTypeMigrationRule",
|
||||
"ruleArguments": [
|
||||
""
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
4
Projects/UOContent/Migrations/Server.Engines.Spawners.Spawner.v0.json
generated
Normal file
4
Projects/UOContent/Migrations/Server.Engines.Spawners.Spawner.v0.json
generated
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
{
|
||||
"version": 0,
|
||||
"type": "Server.Engines.Spawners.Spawner"
|
||||
}
|
||||
55
Projects/UOContent/Migrations/Server.Engines.Spawners.SpawnerEntry.v1.json
generated
Normal file
55
Projects/UOContent/Migrations/Server.Engines.Spawners.SpawnerEntry.v1.json
generated
Normal file
|
|
@ -0,0 +1,55 @@
|
|||
{
|
||||
"version": 1,
|
||||
"type": "Server.Engines.Spawners.SpawnerEntry",
|
||||
"properties": [
|
||||
{
|
||||
"name": "SpawnedName",
|
||||
"type": "string",
|
||||
"rule": "PrimitiveTypeMigrationRule",
|
||||
"ruleArguments": [
|
||||
""
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "SpawnedProbability",
|
||||
"type": "int",
|
||||
"rule": "PrimitiveTypeMigrationRule",
|
||||
"ruleArguments": [
|
||||
""
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "SpawnedMaxCount",
|
||||
"type": "int",
|
||||
"rule": "PrimitiveTypeMigrationRule",
|
||||
"ruleArguments": [
|
||||
""
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "Properties",
|
||||
"type": "string",
|
||||
"rule": "PrimitiveTypeMigrationRule",
|
||||
"ruleArguments": [
|
||||
""
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "Parameters",
|
||||
"type": "string",
|
||||
"rule": "PrimitiveTypeMigrationRule",
|
||||
"ruleArguments": [
|
||||
""
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "Spawned",
|
||||
"type": "System.Collections.Generic.List\u003CServer.ISpawnable\u003E",
|
||||
"rule": "ListMigrationRule",
|
||||
"ruleArguments": [
|
||||
"Server.ISpawnable",
|
||||
"SerializableInterfaceMigrationRule"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue