Convert Regions/Spawns to JSON (#138)

This commit is contained in:
Kamron Batman 2020-05-26 00:34:29 -07:00 • committed by GitHub
parent b8316e9202
commit 390f30e706
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
117 changed files with 97306 additions and 4848 deletions

View file

@ -1148,7 +1148,7 @@ namespace Server.Engines.ConPVP
if (!pm.CheckAlive())
{
}
else if (pm.Region.IsPartOf<Jail>())
else if (pm.Region.IsPartOf<JailRegion>())
{
}
else if (CheckCombat(pm))
@ -1942,7 +1942,7 @@ namespace Server.Engines.ConPVP
if (dp == null)
return "a slot is empty";
if (dp.Mobile.Region.IsPartOf<Jail>())
if (dp.Mobile.Region.IsPartOf<JailRegion>())
return $"{dp.Mobile.Name} is in jail";
if (Sigil.ExistsOn(dp.Mobile))

View file

@ -595,7 +595,7 @@ namespace Server.Engines.ConPVP
Mobile check = part.Players[j];
if (check.Deleted || check.Map == null || check.Map == Map.Internal || !check.Alive ||
Sigil.ExistsOn(check) || check.Region.IsPartOf<Jail>())
Sigil.ExistsOn(check) || check.Region.IsPartOf<JailRegion>())
{
bad = true;
break;
@ -776,7 +776,7 @@ namespace Server.Engines.ConPVP
Mobile check = part.Players[j];
if (check.Deleted || check.Map == null || check.Map == Map.Internal || !check.Alive ||
Sigil.ExistsOn(check) || check.Region.IsPartOf<Jail>())
Sigil.ExistsOn(check) || check.Region.IsPartOf<JailRegion>())
{
bad = true;
break;

View file

@ -254,7 +254,7 @@ namespace Server.Engines.Help
{
from.Location = house.BanLocation;
}
else if (from.Region.IsPartOf<Jail>())
else if (from.Region.IsPartOf<JailRegion>())
{
from.SendLocalizedMessage(1114345, "", 0x35); // You'll need a better jailbreak plan than that!
}
@ -313,7 +313,7 @@ namespace Server.Engines.Help
{
if (IsYoung(from))
{
if (from.Region.IsPartOf<Jail>())
if (from.Region.IsPartOf<JailRegion>())
from.SendLocalizedMessage(1114345, "", 0x35); // You'll need a better jailbreak plan than that!
else if (from.Region.IsPartOf("Haven Island"))
from.SendLocalizedMessage(1041529); // You're already in Haven

View file

@ -1,6 +1,5 @@
using System;
using System.Collections.Generic;
using Server.Configurations;
using Server.Misc;
using Server.Mobiles;
using Server.Network;

View file

@ -1,6 +1,7 @@
using System;
using Server.Engines.MLQuests.Objectives;
using Server.Engines.MLQuests.Rewards;
using Server.Engines.Spawners;
using Server.Items;
using Server.Mobiles;

View file

@ -1,5 +1,6 @@
using Server.Engines.MLQuests.Objectives;
using Server.Engines.MLQuests.Rewards;
using Server.Engines.Spawners;
using Server.Items;
using Server.Mobiles;

View file

@ -1,6 +1,7 @@
using System;
using Server.Engines.MLQuests.Objectives;
using Server.Engines.MLQuests.Rewards;
using Server.Engines.Spawners;
using Server.Items;
using Server.Mobiles;
@ -275,4 +276,4 @@ namespace Server.Engines.MLQuests.Definitions
reader.ReadInt();
}
}
}
}

View file

@ -1,6 +1,7 @@
using System;
using Server.Engines.MLQuests.Objectives;
using Server.Engines.MLQuests.Rewards;
using Server.Engines.Spawners;
using Server.Items;
using Server.Mobiles;

View file

@ -1,5 +1,6 @@
using Server.Engines.MLQuests.Objectives;
using Server.Engines.MLQuests.Rewards;
using Server.Engines.Spawners;
using Server.Items;
using Server.Mobiles;

View file

@ -1,6 +1,7 @@
using System;
using Server.Engines.MLQuests.Objectives;
using Server.Engines.MLQuests.Rewards;
using Server.Engines.Spawners;
using Server.Items;
using Server.Mobiles;

View file

@ -1,6 +1,7 @@
using System;
using Server.Engines.MLQuests.Objectives;
using Server.Engines.MLQuests.Rewards;
using Server.Engines.Spawners;
using Server.Items;
using Server.Mobiles;

View file

@ -1,6 +1,7 @@
using System;
using Server.Engines.MLQuests.Objectives;
using Server.Engines.MLQuests.Rewards;
using Server.Engines.Spawners;
using Server.Items;
using Server.Mobiles;

View file

@ -1,5 +1,6 @@
using Server.Engines.MLQuests.Objectives;
using Server.Engines.MLQuests.Rewards;
using Server.Engines.Spawners;
using Server.Items;
using Server.Mobiles;
@ -82,4 +83,4 @@ namespace Server.Engines.MLQuests.Definitions
int version = reader.ReadInt();
}
}
}
}

View file

@ -2,6 +2,7 @@ using System;
using Server.Engines.MLQuests.Items;
using Server.Engines.MLQuests.Objectives;
using Server.Engines.MLQuests.Rewards;
using Server.Engines.Spawners;
using Server.Items;
using Server.Mobiles;

View file

@ -1,6 +1,7 @@
using System.Collections.Generic;
using Server.Engines.MLQuests.Objectives;
using Server.Engines.MLQuests.Rewards;
using Server.Engines.Spawners;
using Server.Items;
using Server.Mobiles;
@ -2518,4 +2519,4 @@ namespace Server.Engines.MLQuests.Definitions
int version = reader.ReadInt();
}
}
}
}

View file

@ -1,5 +1,6 @@
using Server.Engines.MLQuests.Objectives;
using Server.Engines.MLQuests.Rewards;
using Server.Engines.Spawners;
using Server.Items;
using Server.Mobiles;

View file

@ -1,6 +1,7 @@
using System;
using Server.Engines.MLQuests.Objectives;
using Server.Engines.MLQuests.Rewards;
using Server.Engines.Spawners;
using Server.Items;
using Server.Mobiles;

View file

@ -1,6 +1,7 @@
using System;
using Server.Engines.MLQuests.Objectives;
using Server.Engines.MLQuests.Rewards;
using Server.Engines.Spawners;
using Server.Items;
using Server.Mobiles;

View file

@ -1,6 +1,7 @@
using System;
using Server.Engines.MLQuests.Objectives;
using Server.Engines.MLQuests.Rewards;
using Server.Engines.Spawners;
using Server.Items;
using Server.Mobiles;

View file

@ -1,6 +1,7 @@
using System;
using Server.Engines.MLQuests.Objectives;
using Server.Engines.MLQuests.Rewards;
using Server.Engines.Spawners;
using Server.Items;
using Server.Mobiles;

View file

@ -4,6 +4,7 @@ using System.Linq;
using Server.Engines.MLQuests.Gumps;
using Server.Engines.MLQuests.Objectives;
using Server.Engines.MLQuests.Rewards;
using Server.Engines.Spawners;
using Server.Mobiles;
namespace Server.Engines.MLQuests

View file

@ -162,7 +162,7 @@ namespace Server.Engines.Quests
public override void UseGate(Mobile m)
{
if (m.Region.IsPartOf<Jail>())
if (m.Region.IsPartOf<JailRegion>())
{
m.SendLocalizedMessage(1114345); // You'll need a better jailbreak plan than that!
}

View file

@ -1,41 +0,0 @@
using System;
using System.Xml;
using Server.Mobiles;
using Server.Regions;
namespace Server.Engines.Quests
{
public class CancelQuestRegion : BaseRegion
{
private readonly Type m_Quest;
public CancelQuestRegion(XmlElement xml, Map map, Region parent) : base(xml, map, parent)
{
ReadType(xml["quest"], "type", ref m_Quest);
}
public Type Quest => m_Quest;
public override bool OnMoveInto(Mobile m, Direction d, Point3D newLocation, Point3D oldLocation)
{
if (!base.OnMoveInto(m, d, newLocation, oldLocation))
return false;
if (m.AccessLevel > AccessLevel.Player)
return true;
if (m_Quest == null)
return true;
if (m is PlayerMobile player && player.Quest != null && player.Quest.GetType() == m_Quest)
{
if (!player.HasGump<QuestCancelGump>())
player.Quest.BeginCancelQuest();
return false;
}
return true;
}
}
}

View file

@ -1,38 +0,0 @@
using System;
using System.Xml;
using Server.Mobiles;
using Server.Regions;
namespace Server.Engines.Quests
{
public class QuestCompleteObjectiveRegion : BaseRegion
{
private readonly Type m_Objective;
private readonly Type m_Quest;
public QuestCompleteObjectiveRegion(XmlElement xml, Map map, Region parent) : base(xml, map, parent)
{
XmlElement questEl = xml["quest"];
ReadType(questEl, "type", ref m_Quest);
ReadType(questEl, "complete", ref m_Objective);
}
public Type Quest => m_Quest;
public Type Objective => m_Objective;
public override void OnEnter(Mobile m)
{
base.OnEnter(m);
if (m_Quest != null && m_Objective != null)
if (m is PlayerMobile player && player.Quest != null && player.Quest.GetType() == m_Quest)
{
QuestObjective obj = player.Quest.FindObjective(m_Objective);
if (obj?.Completed == false)
obj.Complete();
}
}
}
}

View file

@ -1,55 +0,0 @@
using System;
using System.Xml;
using Server.Mobiles;
using Server.Regions;
namespace Server.Engines.Quests
{
public class QuestNoEntryRegion : BaseRegion
{
private readonly Type m_MaxObjective;
private readonly int m_Message;
private readonly Type m_MinObjective;
private readonly Type m_Quest;
public QuestNoEntryRegion(XmlElement xml, Map map, Region parent) : base(xml, map, parent)
{
XmlElement questEl = xml["quest"];
ReadType(questEl, "type", ref m_Quest);
ReadType(questEl, "min", ref m_MinObjective, false);
ReadType(questEl, "max", ref m_MaxObjective, false);
ReadInt32(questEl, "message", ref m_Message, false);
}
public Type Quest => m_Quest;
public Type MinObjective => m_MinObjective;
public Type MaxObjective => m_MaxObjective;
public int Message => m_Message;
public override bool OnMoveInto(Mobile m, Direction d, Point3D newLocation, Point3D oldLocation)
{
if (!base.OnMoveInto(m, d, newLocation, oldLocation))
return false;
if (m.AccessLevel > AccessLevel.Player)
return true;
if (m is BaseCreature bc && !bc.Controlled && !bc.Summoned)
return true;
if (m_Quest == null)
return true;
if (m is PlayerMobile player && player.Quest != null && player.Quest.GetType() == m_Quest
&& (m_MinObjective == null || player.Quest.FindObjective(m_MinObjective) != null)
&& (m_MaxObjective == null || player.Quest.FindObjective(m_MaxObjective) == null))
return true;
if (m_Message != 0)
m.SendLocalizedMessage(m_Message);
return false;
}
}
}

View file

@ -1,39 +0,0 @@
using System;
using System.Xml;
using Server.Mobiles;
using Server.Regions;
using Server.Utilities;
namespace Server.Engines.Quests
{
public class QuestOfferRegion : BaseRegion
{
private readonly Type m_Quest;
public QuestOfferRegion(XmlElement xml, Map map, Region parent) : base(xml, map, parent)
{
ReadType(xml["quest"], "type", ref m_Quest);
}
public Type Quest => m_Quest;
public override void OnEnter(Mobile m)
{
base.OnEnter(m);
if (m_Quest == null)
return;
if (m is PlayerMobile player && player.Quest == null && QuestSystem.CanOfferQuest(m, m_Quest))
try
{
QuestSystem qs = (QuestSystem)ActivatorUtil.CreateInstance(m_Quest, player);
qs.SendOffer();
}
catch (Exception ex)
{
Console.WriteLine("Error creating quest {0}: {1}", m_Quest, ex);
}
}
}
}

View file

@ -1,9 +0,0 @@
using System;
namespace Server.Mobiles
{
public class SpawnerType
{
public static Type GetType(string name) => AssemblyHandler.FindFirstTypeForName(name);
}
}

View file

@ -1,49 +1,47 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Text.Json;
using Server.Commands;
using Server.Items;
using Server.Json;
using Server.Mobiles;
using Server.Utilities;
using CPA = Server.CommandPropertyAttribute;
namespace Server.Mobiles
namespace Server.Engines.Spawners
{
public class Spawner : Item, ISpawner
public abstract class BaseSpawner : Item, ISpawner
{
private static WarnTimer m_WarnTimer;
private int m_Count;
private bool m_Group;
private int m_HomeRange;
private int m_Team;
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;
// [Constructible]
public Spawner(int amount, int minDelay, int maxDelay, int team, int homeRange, string spawnedNames) : base(0x1f13)
public BaseSpawner() : this(1, TimeSpan.FromMinutes(5), TimeSpan.FromMinutes(10), 0, 4)
{
InitSpawn(amount, TimeSpan.FromMinutes(minDelay), TimeSpan.FromMinutes(maxDelay), team, homeRange);
AddEntry(spawnedNames, 100, amount, false);
}
// [Constructible]
public Spawner(string spawnedName) : base(0x1f13)
public BaseSpawner(string spawnedName) : this(1, TimeSpan.FromMinutes(5), TimeSpan.FromMinutes(10), 0, 4, spawnedName)
{
InitSpawn(1, TimeSpan.FromMinutes(5), TimeSpan.FromMinutes(10), 0, 4);
AddEntry(spawnedName, 100, 1, false);
}
[Constructible(AccessLevel.Developer)]
public Spawner() : base(0x1f13)
public BaseSpawner(int amount, int minDelay, int maxDelay, int team, int homeRange,
params string[] spawnedNames) : this(amount, TimeSpan.FromMinutes(minDelay), TimeSpan.FromMinutes(maxDelay),
team, homeRange, spawnedNames)
{
InitSpawn(1, TimeSpan.FromMinutes(5), TimeSpan.FromMinutes(10), 0, 4);
}
public Spawner(int amount, TimeSpan minDelay, TimeSpan maxDelay, int team, int homeRange,
public BaseSpawner(int amount, TimeSpan minDelay, TimeSpan maxDelay, int team, int homeRange,
params string[] spawnedNames) : base(0x1f13)
{
InitSpawn(amount, minDelay, maxDelay, team, homeRange);
@ -51,7 +49,25 @@ namespace Server.Mobiles
AddEntry(spawnedNames[i], 100, amount, false);
}
public Spawner(Serial serial) : base(serial)
public BaseSpawner(DynamicJson json, JsonSerializerOptions options) : base(0x1f13)
{
json.GetProperty("count", options, out int amount);
json.GetProperty("minDelay", options, out TimeSpan minDelay);
json.GetProperty("maxDelay", options, out TimeSpan maxDelay);
json.GetProperty("team", options, out int team);
json.GetProperty("homeRange", options, out int homeRange);
json.GetProperty("walkingRange", options, out int walkingRange);
m_WalkingRange = walkingRange;
InitSpawn(amount, minDelay, maxDelay, team, homeRange);
json.GetProperty("entries", options, out List<SpawnerEntry> entries);
foreach (var entry in entries)
AddEntry(entry.SpawnedName, entry.SpawnedProbability, entry.SpawnedMaxCount, false);
}
public BaseSpawner(Serial serial) : base(serial)
{
}
@ -64,6 +80,9 @@ namespace Server.Mobiles
public Dictionary<ISpawnable, SpawnerEntry> Spawned { get; private set; }
[CommandProperty(AccessLevel.Developer)]
public bool ReturnOnDeactivate { get; set; }
[CommandProperty(AccessLevel.Developer)]
public int Count
{
@ -72,9 +91,8 @@ namespace Server.Mobiles
{
m_Count = value;
if (m_Timer != null)
if ((!IsFull && !m_Timer.Running) || (IsFull && m_Timer.Running))
DoTimer();
if (m_Timer != null && (!IsFull && !m_Timer.Running || IsFull && m_Timer.Running))
DoTimer();
InvalidateProperties();
}
@ -164,7 +182,7 @@ namespace Server.Mobiles
}
}
public Point3D HomeLocation => Location;
public virtual Point3D HomeLocation => Location;
public bool UnlinkOnTaming => true;
[CommandProperty(AccessLevel.Developer)]
@ -199,7 +217,7 @@ namespace Server.Mobiles
public override void OnAfterDuped(Item newItem)
{
if (newItem is Spawner newSpawner)
if (newItem is BaseSpawner newSpawner)
for (int i = 0; i < Entries.Count; i++)
newSpawner.AddEntry(Entries[i].SpawnedName, Entries[i].SpawnedProbability, Entries[i].SpawnedMaxCount,
false);
@ -238,6 +256,10 @@ namespace Server.Mobiles
from.SendGump(new SpawnerGump(this));
}
public virtual void GetSpawnerProperties(ObjectPropertyList list)
{
}
public override void GetProperties(ObjectPropertyList list)
{
base.GetProperties(list);
@ -248,14 +270,16 @@ namespace Server.Mobiles
list.Add(1060656, m_Count.ToString()); // amount to make: ~1_val~
list.Add(1061169, m_HomeRange.ToString()); // range ~1_val~
list.Add(1060658, "walking range\t{0}", m_WalkingRange); // ~1_val~: ~2_val~
list.Add(1050039, "walking range:\t{0}", m_WalkingRange); // ~1_NUMBER~ ~2_ITEMNAME~
list.Add(1060658, "group\t{0}", m_Group); // ~1_val~: ~2_val~
list.Add(1060659, "team\t{0}", m_Team); // ~1_val~: ~2_val~
list.Add(1060660, "speed\t{0} to {1}", m_MinDelay, m_MaxDelay); // ~1_val~: ~2_val~
list.Add(1053099, "group:\t{0}", m_Group); // ~1_oretype~: ~2_armortype~
list.Add(1060847, "team:\t{0}", m_Team); // ~1_val~ ~2_val~
list.Add(1063483, "delay:\t{0} to {1}", m_MinDelay, m_MaxDelay); // ~1_MATERIAL~: ~2_ITEMNAME~
for (int i = 0; i < 3 && i < Entries.Count; ++i)
list.Add(1060661 + i, "{0}\t{1}", Entries[i].SpawnedName, CountSpawns(Entries[i]));
GetSpawnerProperties(list);
for (int i = 0; i < 6 && i < Entries.Count; ++i)
list.Add(1060658 + i, "\t{0}\t{1}", Entries[i].SpawnedName, CountSpawns(Entries[i]));
}
else
{
@ -300,6 +324,22 @@ namespace Server.Mobiles
Entries[i].Defrag(this);
}
public virtual bool OnDefragSpawn(ISpawnable spawned, bool remove)
{
if (!remove) // Override could have set it to true already
remove = spawned.Deleted || spawned.Spawner == null || spawned switch
{
Item item => item.RootParent is Mobile || item.IsLockedDown || item.IsSecure,
Mobile m => m is BaseCreature c && (c.Controlled || c.IsStabled),
_ => true
};
if (remove)
Spawned.Remove(spawned);
return remove;
}
public void OnTick()
{
if (m_Group)
@ -336,11 +376,7 @@ namespace Server.Mobiles
if (Entries.Count <= 0 || IsFull)
return;
int probsum = 0;
for (int i = 0; i < Entries.Count; i++)
if (!Entries[i].IsFull)
probsum += Entries[i].SpawnedProbability;
int probsum = Entries.Where(t => !t.IsFull).Sum(t => t.SpawnedProbability);
if (probsum <= 0)
return;
@ -443,7 +479,7 @@ namespace Server.Mobiles
// Defrag taken care of in Spawn(), beforehand
// Count check taken care of in Spawn(), beforehand
Type type = SpawnerType.GetType(entry.SpawnedName);
Type type = AssemblyHandler.FindFirstTypeForName(entry.SpawnedName);
if (type != null)
{
@ -453,10 +489,8 @@ namespace Server.Mobiles
string[] paramargs;
string[] propargs;
if (string.IsNullOrEmpty(entry.Properties))
propargs = Array.Empty<string>();
else
propargs = CommandSystem.Split(entry.Properties.Trim());
propargs = string.IsNullOrEmpty(entry.Properties) ?
Array.Empty<string>() : CommandSystem.Split(entry.Properties.Trim());
string[,] props = FormatProperties(propargs);
@ -468,10 +502,8 @@ namespace Server.Mobiles
return false;
}
if (string.IsNullOrEmpty(entry.Parameters))
paramargs = Array.Empty<string>();
else
paramargs = entry.Parameters.Trim().Split(' ');
paramargs = string.IsNullOrEmpty(entry.Parameters) ?
Array.Empty<string>() : entry.Parameters.Trim().Split(' ');
if (paramargs.Length == 0)
{
@ -504,7 +536,6 @@ namespace Server.Mobiles
}
for (int i = 0; i < realProps.Length; i++)
{
if (realProps[i] != null)
{
object toSet = null;
@ -523,7 +554,6 @@ namespace Server.Mobiles
return false;
}
}
}
if (o is Mobile m)
{
@ -591,75 +621,7 @@ namespace Server.Mobiles
public virtual int GetWalkingRange() => m_WalkingRange;
public virtual 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 Spawnable location.
for (int i = 0; i < 10; i++)
{
int x = Location.X + (Utility.Random(m_HomeRange * 2 + 1) - m_HomeRange);
int y = Location.Y + (Utility.Random(m_HomeRange * 2 + 1) - m_HomeRange);
int 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 Location;
}
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;
LandTile landTile = map.Tiles.GetLandTile(x, y);
if (landTile.Z == z && (TileData.LandTable[landTile.ID & TileData.MaxLandValue].Flags & TileFlag.Wet) != 0)
return true;
StaticTile[] staticTiles = map.Tiles.GetStaticTiles(x, y, true);
for (int i = 0; i < staticTiles.Length; ++i)
{
StaticTile staticTile = staticTiles[i];
if (staticTile.Z == z &&
(TileData.ItemTable[staticTile.ID & TileData.MaxItemValue].Flags & TileFlag.Wet) != 0)
return true;
}
return false;
}
public abstract Point3D GetSpawnPosition(ISpawnable spawned, Map map);
public virtual Map GetSpawnMap() => Map;
@ -783,7 +745,9 @@ namespace Server.Mobiles
{
base.Serialize(writer);
writer.Write(7); // version
writer.Write(8); // version
writer.Write(ReturnOnDeactivate);
writer.Write(Entries.Count);
@ -820,6 +784,11 @@ namespace Server.Mobiles
switch (version)
{
case 8:
{
ReturnOnDeactivate = reader.ReadBool();
goto case 7;
}
case 7:
{
int size = reader.ReadInt();
@ -909,7 +878,7 @@ namespace Server.Mobiles
else
Entries[i].SpawnedName = typeName;
if (SpawnerType.GetType(typeName) == null)
if (AssemblyHandler.FindFirstTypeForName(typeName) == null)
{
m_WarnTimer ??= new WarnTimer();
@ -928,7 +897,7 @@ namespace Server.Mobiles
e.Spawner = this;
for (int j = 0; j < Entries.Count; j++)
if (SpawnerType.GetType(Entries[j].SpawnedName) == e.GetType())
if (AssemblyHandler.FindFirstTypeForName(Entries[j].SpawnedName) == e.GetType())
{
Entries[j].Spawned.Add(e);
Spawned.Add(e, Entries[j]);
@ -947,30 +916,11 @@ namespace Server.Mobiles
m_WalkingRange = m_HomeRange;
}
public static string ConvertTypes(string type)
{
type = type.ToLower();
return type switch
{
"wheat" => "WheatSheaf",
"noxxiousmage" => "NoxiousMage",
"noxxiousarcher" => "NoxiousArcher",
"noxxiouswarrior" => "NoxiousWarrior",
"noxxiouswarlord" => "NoxiousWarlord",
"obsidian" => "obsidianstatue",
"adeepwaterelemental" => "deepwaterelemental",
"noxskeleton" => "poisonskeleton",
"earthcaller" => "earthsummoner",
"bonedemon" => "bonedaemon",
_ => type
};
}
private class InternalTimer : Timer
{
private readonly Spawner m_Spawner;
private readonly BaseSpawner m_Spawner;
public InternalTimer(Spawner spawner, TimeSpan delay) : base(delay)
public InternalTimer(BaseSpawner spawner, TimeSpan delay) : base(delay)
{
if (spawner.IsFull)
Priority = TimerPriority.FiveSeconds;
@ -1052,142 +1002,4 @@ namespace Server.Mobiles
InvalidProps = 0x004,
InvalidEntry = 0x008
}
public class SpawnerEntry
{
public SpawnerEntry(string name, int probability, int maxcount)
{
SpawnedName = name;
SpawnedProbability = probability;
SpawnedMaxCount = maxcount;
Spawned = new List<ISpawnable>();
}
public SpawnerEntry(Spawner parent, IGenericReader reader)
{
int version = reader.ReadInt();
SpawnedName = reader.ReadString();
SpawnedProbability = reader.ReadInt();
SpawnedMaxCount = reader.ReadInt();
Properties = reader.ReadString();
Parameters = reader.ReadString();
int count = reader.ReadInt();
Spawned = new List<ISpawnable>(count);
for (int i = 0; i < count; ++i)
// IEntity e = World.FindEntity( reader.ReadInt() );
if (reader.ReadEntity() is ISpawnable e)
{
e.Spawner = parent;
if (e is BaseCreature creature)
creature.RemoveIfUntamed = true;
Spawned.Add(e);
if (!parent.Spawned.ContainsKey(e))
parent.Spawned.Add(e, this);
}
}
public int SpawnedProbability { get; set; }
public int SpawnedMaxCount { get; set; }
public string SpawnedName { get; set; }
public string Properties { get; set; }
public string Parameters { get; set; }
public EntryFlags Valid { get; set; }
public List<ISpawnable> Spawned { get; }
public bool IsFull => Spawned.Count >= SpawnedMaxCount;
public void Serialize(IGenericWriter writer)
{
writer.Write(0); // version
writer.Write(SpawnedName);
writer.Write(SpawnedProbability);
writer.Write(SpawnedMaxCount);
writer.Write(Properties);
writer.Write(Parameters);
writer.Write(Spawned.Count);
for (int i = 0; i < Spawned.Count; ++i)
{
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(Spawner parent)
{
for (int i = 0; i < Spawned.Count; ++i)
{
ISpawnable e = Spawned[i];
bool remove = false;
if (e is Item item)
{
if (item.Deleted || item.RootParent is Mobile || item.IsLockedDown || item.IsSecure ||
item.Spawner == null)
remove = true;
}
else if (e is Mobile m)
{
if (m.Deleted)
{
remove = true;
}
else if (m is BaseCreature c)
{
if (c.Controlled || c.IsStabled)
remove = true;
/*
else if (c.Combatant == null && ( c.GetDistanceToSqrt( Location ) > (c.RangeHome * 4) ))
{
//m_Spawned[i].Delete();
m_Spawned.RemoveAt( i );
--i;
c.Delete();
remove = true;
}
*/
}
else if (m.Spawner == null)
{
remove = true;
}
}
else
{
remove = true;
}
if (remove)
{
Spawned.RemoveAt(i--);
if (parent.Spawned.ContainsKey(e))
parent.Spawned.Remove(e);
}
}
}
}
}

View file

@ -0,0 +1,116 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Text.Json;
using Server.Json;
using Server.Utilities;
namespace Server.Engines.Spawners
{
public static class GenerateSpawners
{
public static void Initialize()
{
CommandSystem.Register("GenerateSpawners", AccessLevel.Developer, GenerateSpawners_OnCommand);
}
private static void GenerateSpawners_OnCommand(CommandEventArgs e)
{
Mobile from = e.Mobile;
if (e.Arguments.Length == 0)
{
from.SendMessage("Usage: [GenerateSpawners <path|search pattern>");
return;
}
var di = new DirectoryInfo(Core.BaseDirectory);
var files = di.GetFiles(e.Arguments[0], SearchOption.AllDirectories);
if (files.Length == 0)
{
from.SendMessage("GenerateSpawners: No files found matching the pattern");
return;
}
JsonSerializerOptions options = new JsonSerializerOptions();
options.Converters.Add(new MapConverterFactory());
options.Converters.Add(new Point3DConverterFactory());
options.Converters.Add(new TimeSpanConverterFactory());
options.Converters.Add(new TextDefinitionConverterFactory());
for (int i = 0; i < files.Length; i++)
{
var file = files[i];
from.SendMessage("GenerateSpawners: Generating spawners for {0}...", file.Name);
List<DynamicJson> spawners = JsonConfig.Deserialize<List<DynamicJson>>(file.FullName);
ParseSpawnerList(from, spawners, options);
}
}
private static void ParseSpawnerList(Mobile from, List<DynamicJson> spawners, JsonSerializerOptions options)
{
Stopwatch watch = Stopwatch.StartNew();
List<string> failures = new List<string>();
int count = 0;
for (var i = 0; i < spawners.Count; i++)
{
var json = spawners[i];
Type type = AssemblyHandler.FindFirstTypeForName(json.Type);
if (type == null || !typeof(BaseSpawner).IsAssignableFrom(type))
{
string failure = $"GenerateSpawners: Invalid spawner type {json.Type ?? "(-null-)"} ({i})";
if (!failures.Contains(failure))
{
failures.Add(failure);
from.SendMessage(failure);
}
continue;
}
json.GetProperty("location", options, out Point3D location);
json.GetProperty("map", options, out Map map);
var eable = map.GetItemsInRange<BaseSpawner>(location, 0);
if (eable.Any(sp => sp.GetType() == type))
{
eable.Free();
continue;
}
eable.Free();
try
{
var spawner = ActivatorUtil.CreateInstance(type, json, options) as ISpawner;
spawner!.MoveToWorld(location, map);
spawner!.Respawn();
}
catch (Exception)
{
string failure = $"GenerateSpawners: Spawner {type} failed to construct";
if (!failures.Contains(failure))
{
failures.Add(failure);
from.SendMessage(failure);
}
continue;
}
count++;
}
watch.Stop();
from.SendMessage("GenerateSpawners: Generated {0} spawners ({1:F2} seconds, {2} failures)", count, watch.Elapsed.TotalSeconds, failures.Count);
}
}
}

View file

@ -1,27 +1,30 @@
using System;
using System.Text.Json;
using Server.Json;
using Server.Mobiles;
namespace Server.Mobiles
namespace Server.Engines.Spawners
{
public class ProximitySpawner : Spawner
{
[Constructible]
[Constructible(AccessLevel.Developer)]
public ProximitySpawner()
{
}
[Constructible]
[Constructible(AccessLevel.Developer)]
public ProximitySpawner(string spawnName)
: base(spawnName)
{
}
[Constructible]
[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]
[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)
@ -31,12 +34,14 @@ namespace Server.Mobiles
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)
@ -46,6 +51,17 @@ namespace Server.Mobiles
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)
{
@ -129,4 +145,4 @@ namespace Server.Mobiles
InstantFlag = reader.ReadBool();
}
}
}
}

View file

@ -0,0 +1,152 @@
using System;
using System.Text.Json;
using Server.Json;
using Server.Regions;
namespace Server.Engines.Spawners
{
public class RegionSpawner : Spawner
{
private BaseRegion m_SpawnRegion;
[CommandProperty(AccessLevel.Developer)]
public BaseRegion SpawnRegion
{
get => m_SpawnRegion;
set
{
m_SpawnRegion = value;
m_SpawnRegion?.InitRectangles();
InvalidateProperties();
}
}
[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);
m_SpawnRegion = Region.Find(spawnRegion, map) as BaseRegion;
m_SpawnRegion?.InitRectangles();
}
public RegionSpawner(Serial serial) : base(serial)
{
}
public override void GetSpawnerProperties(ObjectPropertyList list)
{
base.GetSpawnerProperties(list);
if (Running && m_SpawnRegion != null) list.Add(1076228, "region:\t{0}", 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 (int i = 0; i < 10; i++)
{
int rand = Utility.Random(m_SpawnRegion.TotalWeight);
int x = int.MinValue;
int y = int.MinValue;
for (int j = 0; j < m_SpawnRegion.RectangleWeights.Length; j++)
{
int curWeight = m_SpawnRegion.RectangleWeights[j];
if (rand < curWeight)
{
Rectangle3D rect = m_SpawnRegion.Rectangles[j];
x = rect.Start.X + rand % rect.Width;
y = rect.Start.Y + rand / rect.Width;
break;
}
rand -= curWeight;
}
int 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);
int 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);
}
}
}

View file

@ -0,0 +1,139 @@
using System;
using System.Text.Json;
using Server.Json;
namespace Server.Engines.Spawners
{
public class Spawner : BaseSpawner
{
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;
LandTile landTile = map.Tiles.GetLandTile(x, y);
if (landTile.Z == z && (TileData.LandTable[landTile.ID & TileData.MaxLandValue].Flags & TileFlag.Wet) != 0)
return true;
StaticTile[] staticTiles = map.Tiles.GetStaticTiles(x, y, true);
for (int i = 0; i < staticTiles.Length; ++i)
{
StaticTile staticTile = staticTiles[i];
if (staticTile.Z == z &&
(TileData.ItemTable[staticTile.ID & TileData.MaxItemValue].Flags & TileFlag.Wet) != 0)
return true;
}
return false;
}
[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 Spawner(Serial serial) : base(serial)
{
}
/*
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 (int i = 0; i < 10; i++)
{
int x = Location.X + (Utility.Random(HomeRange * 2 + 1) - HomeRange);
int y = Location.Y + (Utility.Random(HomeRange * 2 + 1) - HomeRange);
int 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);
int version = reader.ReadEncodedInt();
}
public override void Serialize(IGenericWriter writer)
{
base.Serialize(writer);
writer.WriteEncodedInt(0); // version
}
}
}

View file

@ -0,0 +1,113 @@
using System.Collections.Generic;
using System.Text.Json.Serialization;
using Server.Mobiles;
namespace Server.Engines.Spawners
{
public class SpawnerEntry
{
public SpawnerEntry() => Spawned = new List<ISpawnable>();
public SpawnerEntry(string name, int probability, int maxcount) : this()
{
SpawnedName = name;
SpawnedProbability = probability;
SpawnedMaxCount = maxcount;
}
public SpawnerEntry(BaseSpawner parent, IGenericReader reader)
{
int version = reader.ReadInt();
SpawnedName = reader.ReadString();
SpawnedProbability = reader.ReadInt();
SpawnedMaxCount = reader.ReadInt();
Properties = reader.ReadString();
Parameters = reader.ReadString();
int count = reader.ReadInt();
Spawned = new List<ISpawnable>(count);
for (int i = 0; i < count; ++i)
// IEntity e = World.FindEntity( reader.ReadInt() );
if (reader.ReadEntity() is ISpawnable e)
{
e.Spawner = parent;
if (e is BaseCreature creature)
creature.RemoveIfUntamed = true;
Spawned.Add(e);
if (!parent.Spawned.ContainsKey(e))
parent.Spawned.Add(e, this);
}
}
[JsonPropertyName("probability")]
public int SpawnedProbability { get; set; }
[JsonPropertyName("maxCount")]
public int SpawnedMaxCount { get; set; }
[JsonPropertyName("name")]
public string SpawnedName { get; set; }
[JsonPropertyName("properties")]
public string Properties { get; set; }
[JsonPropertyName("parameters")]
public string Parameters { get; set; }
[JsonIgnore]
public EntryFlags Valid { get; set; }
[JsonIgnore]
public List<ISpawnable> Spawned { get; }
public bool IsFull => Spawned.Count >= SpawnedMaxCount;
public void Serialize(IGenericWriter writer)
{
writer.Write(0); // version
writer.Write(SpawnedName);
writer.Write(SpawnedProbability);
writer.Write(SpawnedMaxCount);
writer.Write(Properties);
writer.Write(Parameters);
writer.Write(Spawned.Count);
for (int i = 0; i < Spawned.Count; ++i)
{
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 (int i = 0; i < Spawned.Count; ++i)
{
ISpawnable spawned = Spawned[i];
if (parent.OnDefragSpawn(spawned, false))
{
Spawned.RemoveAt(i--);
parent.Spawned.Remove(spawned);
}
}
}
}
}

View file

@ -3,15 +3,15 @@ using System.Collections.Generic;
using Server.Gumps;
using Server.Network;
namespace Server.Mobiles
namespace Server.Engines.Spawners
{
public class SpawnerGump : Gump
{
private SpawnerEntry m_Entry;
private int m_Page;
private readonly Spawner m_Spawner;
private readonly BaseSpawner m_Spawner;
public SpawnerGump(Spawner spawner, SpawnerEntry focusentry = null, int page = 0) : base(50, 50)
public SpawnerGump(BaseSpawner spawner, SpawnerEntry focusentry = null, int page = 0) : base(50, 50)
{
m_Spawner = spawner;
m_Entry = focusentry;
@ -124,7 +124,7 @@ namespace Server.Mobiles
public int GetButtonID(int type, int index) => 1 + index * 10 + type;
public void CreateArray(RelayInfo info, Mobile from, Spawner spawner)
public void CreateArray(RelayInfo info, Mobile from, BaseSpawner spawner)
{
int ocount = spawner.Entries.Count;
@ -148,47 +148,46 @@ namespace Server.Mobiles
if (str.Length > 0)
{
Type type = SpawnerType.GetType(str);
Type type = AssemblyHandler.FindFirstTypeForName(str);
if (type != null)
if (type == null)
{
SpawnerEntry entry;
from.SendMessage("{0} is not a valid type name for entry #{1}.", str, i);
return;
}
if (entryindex < ocount)
{
entry = spawner.Entries[entryindex];
entry.SpawnedName = str;
SpawnerEntry entry;
if (mte != null)
entry.SpawnedMaxCount = Utility.ToInt32(mte.Text.Trim());
if (entryindex < ocount)
{
entry = spawner.Entries[entryindex];
entry.SpawnedName = str;
if (poste != null)
entry.SpawnedProbability = Utility.ToInt32(poste.Text.Trim());
}
else
{
int maxcount = 1;
int probcount = 100;
if (mte != null)
entry.SpawnedMaxCount = Utility.ToInt32(mte.Text.Trim());
if (mte != null)
maxcount = Utility.ToInt32(mte.Text.Trim());
if (poste != null)
probcount = Utility.ToInt32(poste.Text.Trim());
entry = spawner.AddEntry(str, probcount, maxcount);
}
if (parmte != null)
entry.Parameters = parmte.Text.Trim();
if (propte != null)
entry.Properties = propte.Text.Trim();
if (poste != null)
entry.SpawnedProbability = Utility.ToInt32(poste.Text.Trim());
}
else
{
from.SendMessage("{0} is not a valid type name for entry #{1}.", str, i);
int maxcount = 1;
int probcount = 100;
if (mte != null)
maxcount = Utility.ToInt32(mte.Text.Trim());
if (poste != null)
probcount = Utility.ToInt32(poste.Text.Trim());
entry = spawner.AddEntry(str, probcount, maxcount);
}
if (parmte != null)
entry.Parameters = parmte.Text.Trim();
if (propte != null)
entry.Properties = propte.Text.Trim();
}
else if (entryindex < ocount && spawner.Entries[entryindex] != null)
{

View file

@ -120,7 +120,7 @@ namespace Server.Items
private void InternalCallback(Mobile from, object targeted)
{
if (Deleted || UsesRemaining <= 0 || !from.InRange(GetWorldLocation(), 3) ||
if (Deleted || UsesRemaining <= 0 || !from.InRange(GetWorldLocation(), 3) ||
!IsAccessibleTo(from))
return;