using Server.Accounting; using Server.Commands; using Server.Commands.Generic; using Server.ContextMenus; using Server.Items; using Server.Network; using Server.Targeting; using System; using System.Collections.Generic; using System.Data; using System.IO; using System.Reflection; using System.Xml; namespace Server.Mobiles { public class XmlSpawner : Item, ISpawner { #region Type declarations public enum TODModeType { Realtime, Gametime } public enum SpawnPositionType { Random, RowFill, ColFill, Perimeter, Player, Waypoint, RelXY, DeltaLocation, Location, Wet, Tiles, NoTiles, ItemID, NoItemID } public class SpawnPositionInfo { public SpawnPositionType positionType; public Mobile trigMob; public string[] positionArgs; public SpawnPositionInfo(SpawnPositionType positiontype, Mobile trigmob, string[] positionargs) { positionType = positiontype; trigMob = trigmob; positionArgs = positionargs; } } public class MovementInfo { public Mobile trigMob; public Point3D trigLocation = Point3D.Zero; public MovementInfo(Mobile m) { trigMob = m; if (m != null) trigLocation = m.Location; } } #endregion #region Constant declarations public const byte MaxLoops = 10; //maximum number of recursive calls from spawner to itself. this is to prevent stack overflow from xmlspawner scripting private const int ShowBoundsItemId = 14089; // 14089 Fire Column // 3555 Campfire // 8708 Skull Pole private const string SpawnDataSetName = "Spawns"; private const string SpawnTablePointName = "Points"; private const int SpawnFitSize = 16; // Normal wall/door height for a mobile is 20 to walk through private static int BaseItemId = 0x1F1C; // Purple Magic Crystal private static int ShowItemId = 0x3E57; // ships mast private static int defaultTriggerSound = 0x1F4; // click and sparkle sound by default (0x1F4) , click sound (0x3A4) public static string XmlSpawnDir = "XmlSpawner"; // default directory for saving/loading .xml files with [xmlload [xmlsave private const int MaxSmartSectorListSize = 1024; // maximum sector list size for use in smart spawning. This gives a 512x512 tile range. private static string defwaypointname = null; // default waypoint name will get assigned in Initialize private const string XmlTableName = "Properties"; private const string XmlDataSetName = "XmlSpawner"; public static AccessLevel DiskAccessLevel = AccessLevel.Administrator; // minimum access level required by commands that can access the disk such as XmlLoad, XmlSave, and the Save function of XmlEdit #if (RESTRICTCONSTRUCTABLE) public static AccessLevel ConstructableAccessLevel = AccessLevel.GameMaster; // only allow spawning of objects that have Constructable access restrictions at this level or lower. Must define RESTRICTCONSTRUCTABLE to enable this. #endif private static int MaxMoveCheck = 10; // limit number of players that can be checked for triggering in a single OnMovement tick #endregion #region Static variable declarations // specifies the level at which smartspawning will be triggered. Players with AccessLevel above this will not trigger smartspawning unless unhidden. public static AccessLevel SmartSpawnAccessLevel = AccessLevel.Player; // define the default values used in making spawners private static TimeSpan defMinDelay = TimeSpan.FromMinutes(5); private static TimeSpan defMaxDelay = TimeSpan.FromMinutes(10); private static TimeSpan defMinRefractory = TimeSpan.FromMinutes(0); private static TimeSpan defMaxRefractory = TimeSpan.FromMinutes(0); private static TimeSpan defTODStart = TimeSpan.FromMinutes(0); private static TimeSpan defTODEnd = TimeSpan.FromMinutes(0); private static TimeSpan defDuration = TimeSpan.FromMinutes(0); private static TimeSpan defDespawnTime = TimeSpan.FromHours(0); private static bool defIsGroup = false; private static int defTeam = 0; private static int defProximityTriggerSound = defaultTriggerSound; private static int defAmount = 1; private static bool defRelativeHome = true; private static int defSpawnRange = 5; private static int defHomeRange = 5; private static double defTriggerProbability = 1; private static int defProximityRange = -1; private static readonly int defKillReset = 1; private static TODModeType defTODMode = TODModeType.Realtime; private static Timer m_GlobalSectorTimer; private static bool SmartSpawningSystemEnabled = false; private static WarnTimer2 m_WarnTimer; // hash table for optimizing HoldSmartSpawning method invocation private static Dictionary holdSmartSpawningHash; public static int seccount; // sector hashtable for each map private static readonly Dictionary>[] GlobalSectorTable = new Dictionary>[6]; #endregion #region Variable declarations private string m_Name = string.Empty; private string m_UniqueId = string.Empty; private bool m_PlayerCreated = false; private bool m_HomeRangeIsRelative = false; private int m_Team; private int m_HomeRange; // added a amount parameter for stacked item spawns private int m_StackAmount; // this is actually redundant with the width height spec for spawning area // just an easier way of specifying it private int m_SpawnRange; private int m_Count; private TimeSpan m_MinDelay; private TimeSpan m_MaxDelay; // added a duration parameter for time-limited spawns private TimeSpan m_Duration; public List m_SpawnObjects = new List(); // List of objects to spawn private DateTime m_End; private DateTime m_RefractEnd; private DateTime m_DurEnd; private SpawnerTimer m_Timer; private InternalTimer m_DurTimer; private InternalTimer3 m_RefractoryTimer; private bool m_Running; private bool m_Group; private int m_X; private int m_Y; private int m_Width; private int m_Height; private WayPoint m_WayPoint; private Static m_ShowContainerStatic; private bool m_proximityActivated; private bool m_refractActivated; private bool m_durActivated; private TimeSpan m_TODStart; private TimeSpan m_TODEnd; // time after proximity activation when the spawn cannot be reactivated private TimeSpan m_MinRefractory; private TimeSpan m_MaxRefractory; private string m_ItemTriggerName; private string m_NoItemTriggerName; private Item m_ObjectPropertyItem; private string m_ObjectPropertyName; public string status_str; public int m_killcount; // added proximity range sensor private int m_ProximityRange; // sound played when a proximity triggered spawner is tripped by a player // set this to zero if you dont want to hear anything private int m_ProximityTriggerSound; private string m_ProximityTriggerMessage; private string m_SpeechTrigger; private bool m_speechTriggerActivated; private string m_MobPropertyName; private string m_MobTriggerName; private string m_PlayerPropertyName; private double m_TriggerProbability = defTriggerProbability; private Mobile m_mob_who_triggered; private Item m_SetPropertyItem; private bool m_skipped = false; private int m_KillReset = defKillReset; // number of spawn ticks that pass without kills before killcount gets reset to zero private int m_spawncheck = 0; private TODModeType m_TODMode = TODModeType.Realtime; private string m_GumpState; private bool m_ExternalTriggering; private bool m_ExternalTrigger; private int m_SequentialSpawning = -1; // off by default private DateTime m_SeqEnd; private Region m_Region; // 2004.02.08 :: Omega Red private string m_RegionName = string.Empty; // 2004.02.08 :: Omega Red private AccessLevel m_TriggerAccessLevel = AccessLevel.Player; public List m_TextEntryBook; private XmlSpawnerGump m_SpawnerGump; private bool m_AllowGhostTriggering = false; private bool m_AllowNPCTriggering = false; private string m_ConfigFile; private bool m_OnHold = false; private bool m_HoldSequence = false; private bool m_SpawnOnTrigger = false; private List m_MovementList; private MovementTimer m_MovementTimer; internal List m_KeywordTagList = new List(); public List RecentSpawnerSearchList = null; public List RecentItemSearchList = null; public List RecentMobileSearchList = null; private TimeSpan m_DespawnTime; private string m_SkillTrigger; private SkillName m_skill_that_triggered; private bool m_FreeRun = false; // override for all other triggering modes private Map currentmap; public bool m_IsInactivated = false; private bool m_SmartSpawning = false; private SectorTimer m_SectorTimer; private List m_ShowBoundsItems = new List(); public List PropertyInfoList = null; // used to optimize property info lookup used by set and get property methods. private Dictionary> spawnPositionWayTable = null; // used to optimize #waypoint lookup private bool inrespawn = false; private List sectorList = null; private bool m_DisableGlobalAutoReset; private Point3D mostRecentSpawnPosition = Point3D.Zero; #endregion #region Property Overrides // does not decay public override bool Decays => false; // is not counted in the normal item count public override bool IsVirtualItem => true; #endregion #region Properties public TimerPriority BasePriority { get; set; } = TimerPriority.OneSecond; public bool DebugThis { get; set; } = false; public int MovingPlayerCount { get; set; } = 0; public int FastestPlayerSpeed { get; set; } = 0; public int NearbyPlayerCount { get { int count = 0; if (ProximityRange >= 0) { IPooledEnumerable eable = GetMobilesInRange(ProximityRange); foreach (Mobile m in eable) { if (m != null && m.Player) count++; } eable.Free(); } return count; } } public Point3D MostRecentSpawnPosition { get { return mostRecentSpawnPosition; } set { mostRecentSpawnPosition = value; } } public TimeSpan GameTOD { get { int hours; int minutes; Clock.GetTime(Map, Location.X, Location.Y, out hours, out minutes); return (new DateTime(DateTime.UtcNow.Year, DateTime.UtcNow.Month, DateTime.UtcNow.Day, hours, minutes, 0).TimeOfDay); } } public TimeSpan RealTOD => DateTime.UtcNow.TimeOfDay; public int RealDay => DateTime.UtcNow.Day; public int RealMonth => DateTime.UtcNow.Month; public DayOfWeek RealDayOfWeek => DateTime.UtcNow.DayOfWeek; public MoonPhase MoonPhase => Clock.GetMoonPhase(Map, Location.X, Location.Y); public XmlSpawnerGump SpawnerGump { get { return m_SpawnerGump; } set { m_SpawnerGump = value; } } public bool DisableGlobalAutoReset { get { return m_DisableGlobalAutoReset; } set { m_DisableGlobalAutoReset = value; } } public bool DoDefrag { get { return false; } set { if (value) { Defrag(true); } } } private readonly bool sectorIsActive = false; private bool UseSectorActivate = false; public bool SingleSector => UseSectorActivate; public bool InActivationRange(Sector s1, Sector s2) { // check to see if the sectors are within +- 2 of one another if (s1 == null || s2 == null) return false; return (Math.Abs(s1.X - s2.X) < 3 && Math.Abs(s1.Y - s2.Y) < 3); } public bool HasDamagedOrDistantSpawns { get { Sector ssec = Map.GetSector(Location); // go through the spawn lists foreach (SpawnObject so in m_SpawnObjects) { for (int x = 0; x < so.SpawnedObjects.Count; x++) { object o = so.SpawnedObjects[x]; if (o is BaseCreature) { BaseCreature b = (BaseCreature)o; // if the mob is damaged or outside of smartspawning detection range then return true if ((b.Hits < b.HitsMax) || (b.Mana < b.ManaMax) || (b.Stam < b.StamMax) || (b.Map != Map)) { return true; } // if the spawn moves into a sector that is not activatable from a sector on the sector list then dont smartspawn if (b.Map != null && b.Map != Map.Internal) { Sector bsec = b.Map.GetSector(b.Location); if (UseSectorActivate) { // is it in activatable range of the sector the spawner is in if (!InActivationRange(bsec, ssec)) { return true; } } else { bool outofsec = true; if (sectorList != null) { foreach (Sector s in sectorList) { // is the creatures sector within activation range of any of the sectors in the list if (InActivationRange(bsec, s)) { outofsec = false; break; } } } if (outofsec) { return true; } } } } } } return false; } } private static int totalSectorsMonitored = 0; public bool HasActiveSectors { get { if (!SmartSpawning || Map == null || Map == Map.Internal) return false; // is this a region spawner? if (m_Region != null) { List players = m_Region.GetPlayers(); if (players == null || players.Count == 0) return false; // confirm that players with the proper access level are present foreach (Mobile m in players) { if (m != null && (m.AccessLevel <= SmartSpawnAccessLevel || !m.Hidden)) { return true; } } return false; } // is this a single sector spawner? if (UseSectorActivate) { return sectorIsActive; } // if there is no sector list made for this spawner then create one. if (sectorList == null) { Point3D loc = Location; sectorList = new List(); // is this container held? if (Parent != null) { if (RootParent is Mobile) loc = ((Mobile)(RootParent)).Location; else if (RootParent is Item) loc = ((Item)(RootParent)).Location; } // find the max detection range by examining both spawnrange // note, sectors will activate when within +-2 sectors int bufferzone = 2 * Map.SectorSize; int x1 = m_X - bufferzone; int width = m_Width + 2 * bufferzone; int y1 = m_Y - bufferzone; int height = m_Height + 2 * bufferzone; // go through all of the sectors within the SpawnRange of the spawner to see if any are active for (int x = x1; x <= x1 + width; x += Map.SectorSize) { for (int y = y1; y <= y1 + height; y += Map.SectorSize) { Sector s = Map.GetSector(new Point3D(x, y, loc.Z)); if (s == null) continue; // dont add any redundant sectors bool duplicate = false; foreach (Sector olds in sectorList) { if (olds == s) { duplicate = true; break; } } if (!duplicate) { sectorList.Add(s); if (GlobalSectorTable[Map.MapID] == null) { GlobalSectorTable[Map.MapID] = new Dictionary>(); } // add this sector and the spawner associated with it to the global sector table List spawnerlist; if (GlobalSectorTable[Map.MapID].TryGetValue(s, out spawnerlist))//.Contains(s)) { //List spawnerlist = GlobalSectorTable[Map.MapID][s]; if (spawnerlist == null) { //GlobalSectorTable[Map.MapID].Remove(s); spawnerlist = new List(); //GlobalSectorTable[Map.MapID].Add(s, spawnerlist); GlobalSectorTable[Map.MapID][s] = spawnerlist; } if (!spawnerlist.Contains(this)) { spawnerlist.Add(this); } } else { spawnerlist = new List(); spawnerlist.Add(this); // add a new entry to the table GlobalSectorTable[Map.MapID][s] = spawnerlist; } totalSectorsMonitored++; // add some sanity checking here if (sectorList.Count > MaxSmartSectorListSize) { SmartSpawning = false; // log it try { Console.WriteLine("SmartSpawning disabled at {0} {1} : Range too large.", loc, Map); using (StreamWriter op = new StreamWriter("badspawn.log", true)) { op.WriteLine("{0} SmartSpawning disabled at {1} {2} : Range too large.", DateTime.UtcNow, loc, Map); op.WriteLine(); } } catch (Exception e) { Diagnostics.ExceptionLogging.LogException(e); } return true; } } } } UseSectorActivate = false; } _TraceStart(2); // go through the sectorlist and see if any of the sectors are active foreach (Sector s in sectorList) { if (s != null && s.Active && s.Players != null && s.Players.Count > 0) { // confirm that players with the proper access level are present foreach (Mobile m in s.Players) { if (m != null && (m.AccessLevel <= SmartSpawnAccessLevel || !m.Hidden)) { return true; } } _TraceEnd(2); } seccount++; } _TraceEnd(2); return false; } } public int SecCount => seccount; public bool IsInactivated { get { return m_IsInactivated; } set { m_IsInactivated = value; } } public int ActiveSectorCount { get { if (sectorList != null) return sectorList.Count; return 0; } } public bool PlayerCreated { get { return m_PlayerCreated; } set { m_PlayerCreated = value; } } public bool OnHold { get { if (m_OnHold) return true; // determine whether there are any keywordtags with the hold flag if (m_KeywordTagList == null || m_KeywordTagList.Count == 0) return false; foreach (BaseXmlSpawner.KeywordTag sot in m_KeywordTagList) { // check for any keyword tag with the holdspawn flag if (sot != null && !sot.Deleted && ((sot.Flags & BaseXmlSpawner.KeywordFlags.HoldSpawn) != 0)) { return true; } } // no hold flags were set return false; } set { m_OnHold = value; } } public string AddSpawn { get { return null; } set { if (!string.IsNullOrEmpty(value)) { string str = value.Trim(); string typestr = BaseXmlSpawner.ParseObjectType(str); Type type = SpawnerType.GetType(typestr); if (type != null) m_SpawnObjects.Add(new SpawnObject(str, 1)); else { // check for special keywords if (typestr != null && (BaseXmlSpawner.IsTypeOrItemKeyword(typestr) || typestr.IndexOf("{") != -1 || typestr.StartsWith("*") || typestr.StartsWith("#"))) { m_SpawnObjects.Add(new SpawnObject(str, 1)); } else status_str = string.Format("{0} is not a valid type name.", str); } InvalidateProperties(); } } } public string UniqueId => m_UniqueId; // does not perform a defrag, so less accurate but can be used while looping through world object enums public int SafeCurrentCount => SafeTotalSpawnedObjects; public bool FreeRun { get { return m_FreeRun; } set { m_FreeRun = value; } } public bool CanFreeSpawn { get { // allow free spawning if proximity sensing is off and if all of the potential free-spawning triggers are disabled if (Running && m_ProximityRange == -1 && string.IsNullOrEmpty(m_ObjectPropertyName) && (string.IsNullOrEmpty(m_MobPropertyName) || m_MobTriggerName == null || m_MobTriggerName.Length == 0) && !m_ExternalTriggering) return true; else return false; } } public SpawnObject[] SpawnObjects { get { return m_SpawnObjects.ToArray(); } set { if ((value != null) && (value.Length > 0)) { foreach (SpawnObject so in value) { if (so == null) continue; bool AlreadyInList = false; // Check if the new array has an existing spawn object foreach (SpawnObject TheSpawn in m_SpawnObjects) { if (TheSpawn.TypeName.ToUpper() == so.TypeName.ToUpper()) { AlreadyInList = true; break; } } // Does this item need to be added if (!AlreadyInList) { // This is a new spawn object so add it to the array (deep copy) m_SpawnObjects.Add(new SpawnObject(so.TypeName, so.ActualMaxCount, so.SubGroup, so.SequentialResetTime, so.SequentialResetTo, so.KillsNeeded, so.RestrictKillsToSubgroup, so.ClearOnAdvance, so.MinDelay, so.MaxDelay, so.SpawnsPerTick, so.PackRange)); } } if (SpawnObjects.Length < 1) Stop(); InvalidateProperties(); } } } public bool HoldSequence { get { // check to see if any keyword tags have the holdsequence flag set, or whether the spawner holdsequence flag is set if (m_HoldSequence) return true; // determine whether there are any keywordtags with the hold flag if (m_KeywordTagList == null || m_KeywordTagList.Count == 0) return false; foreach (BaseXmlSpawner.KeywordTag sot in m_KeywordTagList) { // check for any keyword tag with the holdsequence flag if (sot != null && !sot.Deleted && ((sot.Flags & BaseXmlSpawner.KeywordFlags.HoldSequence) != 0)) { return true; } } // no hold flags were set return false; } set { m_HoldSequence = value; } } public bool CanSpawn { get { if (OnHold) return false; if (m_Group) { if (TotalSpawnedObjects <= 0) return true; else return false; } if (IsFull) return false; else return true; } } // test for a full spawner public bool IsFull { get { int nobj = TotalSpawnedObjects; return ((nobj >= m_Count) || (nobj >= TotalSpawnObjectCount)); } } // this can be used in loops over world objects since it will not defrag and potentially modify the world object lists public int SafeTotalSpawnedObjects { get { if (m_SpawnObjects == null) return 0; int count = 0; foreach (SpawnObject so in m_SpawnObjects) count += so.SpawnedObjects.Count; return count; } } public int TotalSpawnedObjects { get { if (m_SpawnObjects == null) return 0; // defrag so that accurately reflects currently active spawns Defrag(true); int count = 0; foreach (SpawnObject so in m_SpawnObjects) count += so.SpawnedObjects.Count; return count; } } public bool isEmpty() { if (m_SpawnObjects == null) return true; foreach (SpawnObject so in m_SpawnObjects) { if (so.SpawnedObjects != null && so.SpawnedObjects.Count > 0) { if (so.SpawnedObjects[0] is Mobile) return false; } } return true; } public int TotalSpawnObjectCount { get { int count = 0; foreach (SpawnObject so in m_SpawnObjects) count += so.MaxCount; return count; } } #endregion #region Command Properties [CommandProperty(AccessLevel.GameMaster)] public bool GumpReset { set { if (value) { m_SpawnerGump = null; } } } [CommandProperty(AccessLevel.GameMaster)] public Region SpawnRegion { get { return m_Region; } set { // force a re-update of the smart spawning sector list the next time it is accessed ResetSectorList(); m_Region = value; m_RegionName = m_Region?.Name; } } // 2004.02.08 :: Omega Red [CommandProperty(AccessLevel.GameMaster)] public string RegionName { get { return m_RegionName; } set { // force a re-update of the smart spawning sector list the next time it is accessed ResetSectorList(); m_RegionName = value; if (string.IsNullOrEmpty(value)) { m_Region = null; return; } if (Region.Regions.Count == 0) // after world load, before region load return; foreach (Region region in Region.Regions) { if (string.Compare(region.Name, m_RegionName, true) == 0) { m_Region = region; m_RegionName = region.Name; //InvalidateProperties(); return; } } status_str = "invalid region: " + value; m_Region = null; } } [CommandProperty(AccessLevel.GameMaster)] public Point3D X1_Y1 { get { return new Point3D(m_X, m_Y, Z); } set { // X1 and Y1 will initiate region specification m_Width = 0; m_Height = 0; m_X = value.X; m_Y = value.Y; // reset the sector list ResetSectorList(); m_SpawnRange = 0; if (ShowBounds) { ShowBounds = false; ShowBounds = true; } } } [CommandProperty(AccessLevel.GameMaster)] public Point3D X2_Y2 { get { return new Point3D((m_X + m_Width), (m_Y + m_Height), Z); } set { int X2; int Y2; int OriginalX2 = m_X + m_Width; int OriginalY2 = m_Y + m_Height; // reset the sector list ResetSectorList(); // now determine based upon the entered coordinate values what the lower left corner is // lower left will be the min x and min y // upper right will be max x max y if (value.X < OriginalX2) { // ok, this is the proper x value for the lower left m_X = value.X; X2 = OriginalX2; } else { m_X = OriginalX2; X2 = value.X; } if (value.Y < OriginalY2) { // ok, this is the proper y value for the lower left m_Y = value.Y; Y2 = OriginalY2; } else { m_Y = OriginalY2; Y2 = value.Y; } m_Width = X2 - m_X; m_Height = Y2 - m_Y; if (m_Width == m_Height) m_SpawnRange = m_Width / 2; else m_SpawnRange = -1; if (m_HomeRangeIsRelative == false) { int NewHomeRange = (m_Width > m_Height ? m_Height : m_Width); m_HomeRange = (NewHomeRange > 0 ? NewHomeRange : 0); } //original test was for less than 1, changed it to less than zero (zero is a valid width, its the default in fact) // Stop the spawner if the width or height is less than 1 if ((m_Width < 0) || (m_Height < 0)) Running = false; InvalidateProperties(); if (ShowBounds) { ShowBounds = false; ShowBounds = true; } } } // added the spawnrange property. It sets both the XY and width/height parameters automatically. // also doesnt mess with homerange like XY does [CommandProperty(AccessLevel.GameMaster)] public int SpawnRange { get { return (m_SpawnRange); } set { if (value < 0) return; // reset the sector list ResetSectorList(); m_SpawnRange = value; m_Width = m_SpawnRange * 2; m_Height = m_SpawnRange * 2; // dont set the bounding box locations if the initial location is 0,0 since this occurs when the item is just being made // because m_X and m_Y are restored on loading, it creates problems with OnLocationChange which has to avoid applying translational // adjustments to newly placed spawners (because the actual m_X and m_Y is associated with the original location, not the 0,0 location) // basically, before placement, dont set m_X or m_Y to anything that needs to be adjusted later on if (Location.X == 0 && Location.Y == 0) return; m_X = Location.X - m_SpawnRange; m_Y = Location.Y - m_SpawnRange; if (ShowBounds) { ShowBounds = false; ShowBounds = true; } } } [CommandProperty(AccessLevel.GameMaster)] public bool ShowBounds { get { return (m_ShowBoundsItems != null && m_ShowBoundsItems.Count > 0); } set { if (value && (ShowBounds == false)) { if (m_ShowBoundsItems == null) m_ShowBoundsItems = new List(); // Boundary lines int ValidX1 = m_X; int ValidX2 = m_X + m_Width; int ValidY1 = m_Y; int ValidY2 = m_Y + m_Height; for (int x = 0; x <= m_Width; x++) { int NewX = m_X + x; for (int y = 0; y <= m_Height; y++) { int NewY = m_Y + y; if (NewX == ValidX1 || NewX == ValidX2 || NewX == ValidY1 || NewX == ValidY2 || NewY == ValidX1 || NewY == ValidX2 || NewY == ValidY1 || NewY == ValidY2) { // Add an object to show the spawn area Static s = new Static(ShowBoundsItemId) { Visible = false }; s.MoveToWorld(new Point3D(NewX, NewY, Z), Map); m_ShowBoundsItems.Add(s); } } } } if (value == false && m_ShowBoundsItems != null) { // Remove all of the items from the array foreach (Static s in m_ShowBoundsItems) s.Delete(); m_ShowBoundsItems.Clear(); } } } [CommandProperty(AccessLevel.GameMaster)] public int MaxCount { get { return m_Count; } set { m_Count = value; InvalidateProperties(); } } [CommandProperty(AccessLevel.GameMaster)] public int CurrentCount => TotalSpawnedObjects; [CommandProperty(AccessLevel.GameMaster)] public WayPoint WayPoint { get { return m_WayPoint; } set { m_WayPoint = value; } } [CommandProperty(AccessLevel.GameMaster)] public bool ExternalTriggering { get { return m_ExternalTriggering; } set { m_ExternalTriggering = value; } } [CommandProperty(AccessLevel.GameMaster)] public bool ExtTrigState { get { return m_ExternalTrigger; } set { m_ExternalTrigger = value; } } [CommandProperty(AccessLevel.GameMaster)] public bool Running { get { return m_Running; } set { // Don't start the spawner unless the height and width are valid if (value && (m_Width >= 0) && (m_Height >= 0)) { Start(); } else Stop(); InvalidateProperties(); } } [CommandProperty(AccessLevel.GameMaster)] public int HomeRange { get { return m_HomeRange; } set { m_HomeRange = value; InvalidateProperties(); } } [CommandProperty(AccessLevel.GameMaster)] public bool HomeRangeIsRelative { get { return m_HomeRangeIsRelative; } set { m_HomeRangeIsRelative = value; } } [CommandProperty(AccessLevel.GameMaster)] public int Team { get { return m_Team; } set { m_Team = value; InvalidateProperties(); } } [CommandProperty(AccessLevel.GameMaster)] public int StackAmount { get { return m_StackAmount; } set { m_StackAmount = value; } } [CommandProperty(AccessLevel.GameMaster)] public TimeSpan MinDelay { get { return m_MinDelay; } set { m_MinDelay = value; // reset the spawn timer DoTimer(); InvalidateProperties(); } } [CommandProperty(AccessLevel.GameMaster)] public TimeSpan MaxDelay { get { return m_MaxDelay; } set { m_MaxDelay = value; // reset the spawn timer DoTimer(); InvalidateProperties(); } } [CommandProperty(AccessLevel.GameMaster)] public int KillCount { get { return m_killcount; } set { m_killcount = value; } } [CommandProperty(AccessLevel.GameMaster)] public int KillReset { get { return m_KillReset; } set { m_KillReset = value; } } [CommandProperty(AccessLevel.GameMaster)] public double TriggerProbability { get { return m_TriggerProbability; } set { m_TriggerProbability = value; } } //added refractory period support [CommandProperty(AccessLevel.GameMaster)] public TimeSpan RefractMin { get { return m_MinRefractory; } set { m_MinRefractory = value; } } [CommandProperty(AccessLevel.GameMaster)] public TimeSpan RefractMax { get { return m_MaxRefractory; } set { m_MaxRefractory = value; } } [CommandProperty(AccessLevel.GameMaster)] public TimeSpan RefractoryOver { get { if (m_refractActivated) return m_RefractEnd - DateTime.UtcNow; else return TimeSpan.FromSeconds(0); } set { DoTimer3(value); } } [CommandProperty(AccessLevel.GameMaster)] public string SetItemName { get { if (m_SetPropertyItem == null || m_SetPropertyItem.Deleted) return null; return m_SetPropertyItem.Name; } } [CommandProperty(AccessLevel.GameMaster)] public Item SetItem { get { return m_SetPropertyItem; } set { m_SetPropertyItem = value; } } [CommandProperty(AccessLevel.GameMaster)] public string MobTriggerProp { get { return m_MobPropertyName; } set { m_MobPropertyName = value; } } [CommandProperty(AccessLevel.GameMaster)] public string MobTriggerName { get { return m_MobTriggerName; } set { m_MobTriggerName = value; } } [CommandProperty(AccessLevel.GameMaster)] public Mobile MobTriggerId { get { if (m_MobTriggerName == null) return null; // try to parse out the type information if it has also been saved string[] typeargs = m_MobTriggerName.Split(",".ToCharArray(), 2); string typestr = null; string namestr = m_MobTriggerName; if (typeargs.Length > 1) { namestr = typeargs[0]; typestr = typeargs[1]; } return BaseXmlSpawner.FindMobileByName(this, namestr, typestr); } } [CommandProperty(AccessLevel.GameMaster)] public string PlayerTriggerProp { get { return m_PlayerPropertyName; } set { m_PlayerPropertyName = value; } } // time of day activation [CommandProperty(AccessLevel.GameMaster)] public TimeSpan TODStart { get { return m_TODStart; } set { m_TODStart = value; } } [CommandProperty(AccessLevel.GameMaster)] public TimeSpan TODEnd { get { return m_TODEnd; } set { m_TODEnd = value; } } [CommandProperty(AccessLevel.GameMaster)] public TimeSpan TOD { get { if (m_TODMode == TODModeType.Gametime) { int hours; int minutes; Clock.GetTime(Map, Location.X, Location.Y, out hours, out minutes); return (new DateTime(DateTime.UtcNow.Year, DateTime.UtcNow.Month, DateTime.UtcNow.Day, hours, minutes, 0).TimeOfDay); } return DateTime.UtcNow.TimeOfDay; } } [CommandProperty(AccessLevel.GameMaster)] public TODModeType TODMode { get { return m_TODMode; } set { m_TODMode = value; } } [CommandProperty(AccessLevel.GameMaster)] public bool TODInRange { get { if (m_TODStart == m_TODEnd) return true; DateTime now; if (m_TODMode == TODModeType.Gametime) { int hours; int minutes; Clock.GetTime(Map, Location.X, Location.Y, out hours, out minutes); now = new DateTime(DateTime.UtcNow.Year, DateTime.UtcNow.Month, DateTime.UtcNow.Day, hours, minutes, 0); } else { // calculate the time window now = DateTime.UtcNow; } var day_start = new DateTime(now.Year, now.Month, now.Day); // calculate the starting TOD window by adding the TODStart to day_start var TOD_start = day_start + m_TODStart; var TOD_end = day_start + m_TODEnd; // handle the case when TODstart is before midnight and end is after if (TOD_start > TOD_end) { if (now > TOD_start || now < TOD_end) return true; else return false; } if (now > TOD_start && now < TOD_end) return true; else return false; } } [CommandProperty(AccessLevel.GameMaster)] public TimeSpan DespawnTime { get { return m_DespawnTime; } set { m_DespawnTime = value; } } [CommandProperty(AccessLevel.GameMaster)] public TimeSpan Duration { get { return m_Duration; } set { m_Duration = value; InvalidateProperties(); } } [CommandProperty(AccessLevel.GameMaster)] public TimeSpan DurationOver { get { if (m_durActivated) return m_DurEnd - DateTime.UtcNow; else return TimeSpan.FromSeconds(0); } set { DoTimer2(value); } } // proximity range parameter [CommandProperty(AccessLevel.GameMaster)] public int ProximityRange { get { return m_ProximityRange; } set { m_ProximityRange = value; InvalidateProperties(); } } // proximity range activated? [CommandProperty(AccessLevel.GameMaster)] public bool ProximityActivated { get { return m_proximityActivated; } set { if (AllowTriggering) { ActivateTrigger(); } m_proximityActivated = value; } } // proximity trigger sound parameter [CommandProperty(AccessLevel.GameMaster)] public int ProximitySound { get { return m_ProximityTriggerSound; } set { m_ProximityTriggerSound = value; } } // proximity trigger message parameter [CommandProperty(AccessLevel.GameMaster)] public string ProximityMsg { get { return m_ProximityTriggerMessage; } set { m_ProximityTriggerMessage = value; } } [CommandProperty(AccessLevel.GameMaster)] public string SpeechTrigger { get { return m_SpeechTrigger; } set { m_SpeechTrigger = value; } } public string SkillTrigger { get { return m_SkillTrigger; } set { m_SkillTrigger = value; } } [CommandProperty(AccessLevel.GameMaster)] public TimeSpan NextSpawn { get { if (m_Running) return m_End - DateTime.UtcNow; else return TimeSpan.FromSeconds(0); } set { Start(); DoTimer(value); } } [CommandProperty(AccessLevel.GameMaster)] public bool SpawnOnTrigger { get { return m_SpawnOnTrigger; } set { m_SpawnOnTrigger = value; } } [CommandProperty(AccessLevel.GameMaster)] public bool Group { get { return m_Group; } set { m_Group = value; InvalidateProperties(); } } [CommandProperty(AccessLevel.GameMaster)] public string GumpState { get { return m_GumpState; } set { m_GumpState = value; } } [CommandProperty(AccessLevel.GameMaster)] public int SequentialSpawn { get { return m_SequentialSpawning; } set { m_SequentialSpawning = value; } } [CommandProperty(AccessLevel.GameMaster)] public TimeSpan NextSeqReset { get { if (m_Running && ((m_SeqEnd - DateTime.UtcNow) > TimeSpan.Zero)) return m_SeqEnd - DateTime.UtcNow; else return TimeSpan.FromSeconds(0); } set { m_SeqEnd = DateTime.UtcNow + value; } } [CommandProperty(AccessLevel.GameMaster)] public AccessLevel TriggerAccessLevel { get { return m_TriggerAccessLevel; } set { m_TriggerAccessLevel = value; } } [CommandProperty(AccessLevel.GameMaster)] public bool DoRespawn { get { return false; } set { // need to determine whether this is being set by the spawner during processing of a respawn entry // if so then dont do it, otherwise you will infinitely recurse and crash with a stack overflow if (value && !inrespawn) { Respawn(); } } } [CommandProperty(AccessLevel.GameMaster)] public bool DoReset { get { return false; } set { if (value) Reset(); } } [CommandProperty(AccessLevel.GameMaster)] public bool AllowGhostTrig { get { return m_AllowGhostTriggering; } set { m_AllowGhostTriggering = value; } } [CommandProperty(AccessLevel.GameMaster)] public bool AllowNPCTrig { get { return m_AllowNPCTriggering; } set { m_AllowNPCTriggering = value; } } [CommandProperty(AccessLevel.GameMaster)] public string ConfigFile { get { return m_ConfigFile; } set { m_ConfigFile = value; } } [CommandProperty(AccessLevel.GameMaster)] public bool LoadConfig { get { return false; } set { if (value) LoadXmlConfig(ConfigFile); } } [CommandProperty(AccessLevel.GameMaster)] public Mobile TriggerMob { get { return m_mob_who_triggered; } set { m_mob_who_triggered = value; } } [CommandProperty(AccessLevel.GameMaster)] public bool SmartSpawning { get { return m_SmartSpawning; } set { m_SmartSpawning = value; if (m_SmartSpawning) { // if any spawner is smartspawning, then the smartspawning system is enabled SmartSpawningSystemEnabled = true; // check to see if the global sector timer is running if (m_GlobalSectorTimer == null || !m_GlobalSectorTimer.Running) { // start the global smartspawning timer DoGlobalSectorTimer(TimeSpan.FromSeconds(1)); } } //IsInactivated = false; } } [CommandProperty(AccessLevel.GameMaster)] public bool IsEmpty => isEmpty(); #endregion #region ISpawner interface support public bool UnlinkOnTaming => true; public Point3D HomeLocation => Location; public int Range => HomeRange; public virtual void GetSpawnProperties(ISpawnable spawn, ObjectPropertyList list) { } public virtual void GetSpawnContextEntries(ISpawnable spawn, Mobile user, List list) { } public void Remove(ISpawnable spawn) { if (m_SpawnObjects == null) return; foreach (SpawnObject so in m_SpawnObjects) { for (int i = 0; i < so.SpawnedObjects.Count; ++i) { if (so.SpawnedObjects[i] == spawn) { so.SpawnedObjects.Remove(spawn); if (SequentialSpawn >= 0 && so.RestrictKillsToSubgroup) { if (so.SubGroup == SequentialSpawn) m_killcount++; } else m_killcount++; return; } } } } public void RestoreISpawner() { // restore the Spawner assignments to all spawned objects if (m_SpawnObjects == null) return; foreach (SpawnObject so in m_SpawnObjects) { for (int i = 0; i < so.SpawnedObjects.Count; ++i) { object o = so.SpawnedObjects[i]; if (o is Item) { ((Item)o).Spawner = this; } else if (o is Mobile) { ((Mobile)o).Spawner = this; } } } } #endregion #region Method Overrides public override void OnAfterDuped(Item newItem) { ((XmlSpawner)newItem).Running = false; // automatically turn off duped spawners base.OnAfterDuped(newItem); } public override void OnMapChange() { base.OnMapChange(); currentmap = Map; ResetSectorList(); // reset the sector list for smart spawning } public override void OnDoubleClick(Mobile from) { if (from == null || from.Deleted || from.AccessLevel < AccessLevel.GameMaster || (m_SpawnerGump != null && SomeOneHasGumpOpen)) return; DeleteTextEntryBook(); // clear any text entry books that might still be around int x = 0; int y = 0; Account acct = from.Account as Account; // read the text entries for default values if (acct != null) { XmlSpawnerDefaults.DefaultEntry defs = XmlSpawnerDefaults.GetDefaults(acct.ToString(), from.Name); if (defs != null) { x = defs.SpawnerGumpX; y = defs.SpawnerGumpY; } } XmlSpawnerGump g = new XmlSpawnerGump(this, x, y, 0, 0, 0); from.SendGump(g); } public override void GetProperties(ObjectPropertyList list) { base.GetProperties(list); list.Add(m_Running ? 1060742 : 1060743); // Active - Inactive // add whitespace to the beginning to avoid any problem with names that begin with # and are interpreted as cliloc ids list.Add(1042971, " " + Name); // ~1_val~ list.Add(1060656, m_Count.ToString()); // amount to make: ~1_val~ list.Add(1061169, m_HomeRange.ToString()); // range ~1_val~ int nlist_items = 6; if (m_Group) { list.Add(1060658 + 6 - nlist_items, "group\t{0}", m_Group); // ~1_val~: ~2_val~ nlist_items--; } if (m_Team != 0) { list.Add(1060658 + 6 - nlist_items, "team\t{0}", m_Team); // ~1_val~: ~2_val~ nlist_items--; } list.Add(1060658 + 6 - nlist_items, "speed\t{0} to {1}", m_MinDelay, m_MaxDelay); // ~1_val~: ~2_val~ nlist_items--; // display the duration parameter in the prop gump if it is non-zero if (m_Duration > TimeSpan.FromMinutes(0)) { list.Add(1060658 + 6 - nlist_items, "Duration\t{0}", m_Duration); nlist_items--; } // display the proximity range parameter in the prop gump if it is active if (m_ProximityRange != -1) { list.Add(1060658 + 6 - nlist_items, "ProximityRange\t{0}", m_ProximityRange); nlist_items--; } if (m_SpawnObjects != null) for (int i = 0; i < nlist_items && i < m_SpawnObjects.Count; ++i) { string typename = m_SpawnObjects[i].TypeName; if (typename != null && typename.Length > 20) { typename = typename.Substring(0, 20); } list.Add(1060658 + (6 - nlist_items) + i, " {0}\t{1}", typename, m_SpawnObjects[i].SpawnedObjects.Count); } } public override void OnDelete() { base.OnDelete(); if (ShowBounds) ShowBounds = false; RemoveSpawnObjects(); // remove any text entry books that might still be attached to the spawner DeleteTextEntryBook(); if (m_Timer != null) m_Timer.Stop(); if (m_DurTimer != null) m_DurTimer.Stop(); if (m_RefractoryTimer != null) m_RefractoryTimer.Stop(); // if statics were added for marking container held spawners, delete them if (m_ShowContainerStatic != null && !m_ShowContainerStatic.Deleted) m_ShowContainerStatic.Delete(); } static bool IgnoreLocationChange = false; public override void OnLocationChange(Point3D oldLocation) { if (IgnoreLocationChange) { IgnoreLocationChange = false; return; } // calculate the positional shift if (oldLocation.X > 0 && oldLocation.Y > 0) { int diffx = X - oldLocation.X; int diffy = Y - oldLocation.Y; m_X += diffx; m_Y += diffy; } else { // Keep the original dimensions the same (Width, Height), // just recalculate the new top left corner m_X = X - (m_Width / 2); m_Y = Y - (m_Height / 2); } // reset the sector list for smart spawning ResetSectorList(); // Check if the spawner is showing its bounds if (ShowBounds) { ShowBounds = false; ShowBounds = true; } } #endregion #region Gump support public bool SomeOneHasGumpOpen { get { // go through all online mobiles and see if any have xmlspawner gumps open List states = NetState.Instances; for (int i = 0; i < states.Count; ++i) { Mobile m = states[i].Mobile; if (m != null && m.HasGump(typeof(XmlSpawnerGump))) { return true; } } return false; } } public static void SpawnerGumpCallback(Mobile from, object invoker, string response) { // assign the response to the gumpstate if (invoker is XmlSpawner) { XmlSpawner xs = (XmlSpawner)invoker; xs.GumpState = response; } } public void DeleteTextEntryBook() { if (m_TextEntryBook != null) { foreach (XmlTextEntryBook s in m_TextEntryBook) s.Delete(); m_TextEntryBook = null; } } #endregion #region Utility Methods private static bool IsConstructable(ConstructorInfo ctor) { return ctor.IsDefined(typeof(ConstructableAttribute), false); } public static int ConvertToInt(string value) { if (value.StartsWith("0x")) { return Convert.ToInt32(value.Substring(2), 16); } return Convert.ToInt32(value); } public static void ExecuteAction(object attachedto, Mobile trigmob, string action) { Point3D loc = Point3D.Zero; Map map = null; if (attachedto is IEntity) { loc = ((IEntity)attachedto).Location; map = ((IEntity)attachedto).Map; } if (action == null || action.Length <= 0 || attachedto == null || map == null) return; SpawnObject TheSpawn = new SpawnObject(null, 0) { TypeName = action }; string substitutedtypeName = BaseXmlSpawner.ApplySubstitution(null, attachedto, action); string typeName = BaseXmlSpawner.ParseObjectType(substitutedtypeName); string status_str; if (BaseXmlSpawner.IsTypeOrItemKeyword(typeName)) { BaseXmlSpawner.SpawnTypeKeyword(attachedto, TheSpawn, typeName, substitutedtypeName, trigmob, map, out status_str); } else { // its a regular type descriptor so find out what it is Type type = SpawnerType.GetType(typeName); try { string[] arglist = BaseXmlSpawner.ParseString(substitutedtypeName, 3, "/"); object o = CreateObject(type, arglist[0]); if (o == null) { status_str = "invalid type specification: " + arglist[0]; } else if (o is Mobile) { Mobile m = (Mobile)o; if (m is BaseCreature) { BaseCreature c = (BaseCreature)m; c.Home = loc; // Spawners location is the home point } m.Location = loc; m.Map = map; BaseXmlSpawner.ApplyObjectStringProperties(null, substitutedtypeName, m, trigmob, attachedto, out status_str); } else if (o is Item) { Item item = (Item)o; BaseXmlSpawner.AddSpawnItem(null, attachedto, TheSpawn, item, loc, map, trigmob, false, substitutedtypeName, out status_str); } } catch (Exception e) { Diagnostics.ExceptionLogging.LogException(e); } } } private static void RemoveFromSectorTable(Sector s, XmlSpawner spawner) { if (s == null || s.Owner == null || s.Owner == Map.Internal || GlobalSectorTable[s.Owner.MapID] == null) return; // find the sector List spawnerlist; if (GlobalSectorTable[s.Owner.MapID].TryGetValue(s, out spawnerlist) && spawnerlist != null) { //List spawnerlist = GlobalSectorTable[s.Owner.MapID][s]; if (spawnerlist.Contains(spawner)) { spawnerlist.Remove(spawner); } } } private void ResetSectorList() { // remove the global sector entries if (sectorList != null) { foreach (Sector s in sectorList) { RemoveFromSectorTable(s, this); } } sectorList = null; UseSectorActivate = false; // force an update of the sector list bool sectorrefresh = HasActiveSectors; } public void LoadXmlConfig(string filename) { if (filename == null || filename.Length <= 0) return; // Check if the file exists if (File.Exists(filename)) { FileStream fs = null; try { fs = File.Open(filename, FileMode.Open, FileAccess.Read); } catch (Exception e) { Diagnostics.ExceptionLogging.LogException(e); } if (fs == null) { status_str = string.Format("Unable to open {0} for loading", filename); return; } // Create the data set DataSet ds = new DataSet(XmlDataSetName); // Read in the file bool fileerror = false; try { ds.ReadXml(fs); } catch { fileerror = true; } // close the file fs.Close(); if (fileerror) { Console.WriteLine("XmlSpawner: Error in XML config file '{0}'", filename); return; } // Check that at least a single table was loaded if (ds.Tables.Count > 0) { if (ds.Tables[XmlTableName] != null && ds.Tables[XmlTableName].Rows.Count > 0) { foreach (DataRow dr in ds.Tables[XmlTableName].Rows) { string strEntry = null; bool boolEntry = true; double doubleEntry = 0; int intEntry = 0; var valid_entry = true; try { strEntry = (string)dr["Name"]; } catch { valid_entry = false; } if (valid_entry) { Name = strEntry; } valid_entry = true; try { intEntry = int.Parse((string)dr["X"]); } catch { valid_entry = false; } if (valid_entry) { m_X = intEntry; } valid_entry = true; try { intEntry = int.Parse((string)dr["Y"]); } catch { valid_entry = false; } if (valid_entry) { m_Y = intEntry; } valid_entry = true; try { intEntry = int.Parse((string)dr["Width"]); } catch { valid_entry = false; } if (valid_entry) { m_Width = intEntry; } valid_entry = true; try { intEntry = int.Parse((string)dr["Height"]); } catch { valid_entry = false; } if (valid_entry) { m_Height = intEntry; } valid_entry = true; try { intEntry = int.Parse((string)dr["CentreX"]); } catch { valid_entry = false; } if (valid_entry) { X = intEntry; } valid_entry = true; try { intEntry = int.Parse((string)dr["CentreY"]); } catch { valid_entry = false; } if (valid_entry) { Y = intEntry; } valid_entry = true; try { intEntry = int.Parse((string)dr["CentreZ"]); } catch { valid_entry = false; } if (valid_entry) { Z = intEntry; } valid_entry = true; try { intEntry = int.Parse((string)dr["SequentialSpawning"]); } catch { valid_entry = false; } if (valid_entry) { m_SequentialSpawning = intEntry; } valid_entry = true; try { intEntry = int.Parse((string)dr["ProximityRange"]); } catch { valid_entry = false; } if (valid_entry) { m_ProximityRange = intEntry; } valid_entry = true; try { strEntry = (string)dr["ProximityTriggerMessage"]; } catch { valid_entry = false; } if (valid_entry) { m_ProximityTriggerMessage = strEntry; } valid_entry = true; try { strEntry = (string)dr["SpeechTrigger"]; } catch { valid_entry = false; } if (valid_entry) { m_SpeechTrigger = strEntry; } valid_entry = true; try { strEntry = (string)dr["SkillTrigger"]; } catch { valid_entry = false; } if (valid_entry) { m_SkillTrigger = strEntry; } valid_entry = true; try { intEntry = int.Parse((string)dr["ProximityTriggerSound"]); } catch { valid_entry = false; } if (valid_entry) { m_ProximityTriggerSound = intEntry; } valid_entry = true; try { strEntry = (string)dr["ItemTriggerName"]; } catch { valid_entry = false; } if (valid_entry) { m_ItemTriggerName = strEntry; } valid_entry = true; try { strEntry = (string)dr["NoItemTriggerName"]; } catch { valid_entry = false; } if (valid_entry) { m_NoItemTriggerName = strEntry; } // check for the delayinsec entry bool delayinsec = false; try { delayinsec = bool.Parse((string)dr["DelayInSec"]); } catch { } valid_entry = true; try { doubleEntry = double.Parse((string)dr["MinDelay"]); } catch { valid_entry = false; } if (valid_entry) { m_MinDelay = delayinsec ? TimeSpan.FromSeconds(doubleEntry) : TimeSpan.FromMinutes(doubleEntry); } valid_entry = true; try { doubleEntry = double.Parse((string)dr["MaxDelay"]); } catch { valid_entry = false; } if (valid_entry) { m_MaxDelay = delayinsec ? TimeSpan.FromSeconds(doubleEntry) : TimeSpan.FromMinutes(doubleEntry); } valid_entry = true; try { doubleEntry = double.Parse((string)dr["Duration"]); } catch { valid_entry = false; } if (valid_entry) { m_Duration = TimeSpan.FromMinutes(doubleEntry); } valid_entry = true; try { doubleEntry = double.Parse((string)dr["DespawnTime"]); } catch { valid_entry = false; } if (valid_entry) { m_DespawnTime = TimeSpan.FromHours(doubleEntry); } valid_entry = true; try { doubleEntry = double.Parse((string)dr["MinRefractory"]); } catch { valid_entry = false; } if (valid_entry) { m_MinRefractory = TimeSpan.FromMinutes(doubleEntry); } valid_entry = true; try { doubleEntry = double.Parse((string)dr["MaxRefractory"]); } catch { valid_entry = false; } if (valid_entry) { m_MaxRefractory = TimeSpan.FromMinutes(doubleEntry); } valid_entry = true; try { doubleEntry = double.Parse((string)dr["TODStart"]); } catch { valid_entry = false; } if (valid_entry) { m_TODStart = TimeSpan.FromMinutes(doubleEntry); } valid_entry = true; try { doubleEntry = double.Parse((string)dr["TODEnd"]); } catch { valid_entry = false; } if (valid_entry) { m_TODEnd = TimeSpan.FromMinutes(doubleEntry); } valid_entry = true; try { intEntry = int.Parse((string)dr["TODMode"]); } catch { valid_entry = false; } if (valid_entry) { m_TODMode = (TODModeType)intEntry; } valid_entry = true; try { intEntry = int.Parse((string)dr["Amount"]); } catch { valid_entry = false; } if (valid_entry) { m_StackAmount = intEntry; } valid_entry = true; try { intEntry = int.Parse((string)dr["MaxCount"]); } catch { valid_entry = false; } if (valid_entry) { m_Count = intEntry; } valid_entry = true; try { intEntry = int.Parse((string)dr["Range"]); } catch { valid_entry = false; } if (valid_entry) { m_HomeRange = intEntry; } valid_entry = true; try { intEntry = int.Parse((string)dr["Team"]); } catch { valid_entry = false; } if (valid_entry) { m_Team = intEntry; } valid_entry = true; try { strEntry = (string)dr["WayPoint"]; } catch { valid_entry = false; } if (valid_entry) { m_WayPoint = GetWaypoint(strEntry); } valid_entry = true; try { intEntry = int.Parse((string)dr["KillReset"]); } catch { valid_entry = false; } if (valid_entry) { m_KillReset = intEntry; } valid_entry = true; try { doubleEntry = double.Parse((string)dr["TriggerProbability"]); } catch { valid_entry = false; } if (valid_entry) { m_TriggerProbability = doubleEntry; } valid_entry = true; try { boolEntry = bool.Parse((string)dr["ExternalTriggering"]); } catch { valid_entry = false; } if (valid_entry) { m_ExternalTriggering = boolEntry; } valid_entry = true; try { boolEntry = bool.Parse((string)dr["IsGroup"]); } catch { valid_entry = false; } if (valid_entry) { m_Group = boolEntry; } valid_entry = true; try { boolEntry = bool.Parse((string)dr["IsHomeRangeRelative"]); } catch { valid_entry = false; } if (valid_entry) { m_HomeRangeIsRelative = boolEntry; } valid_entry = true; try { boolEntry = bool.Parse((string)dr["AllowGhostTriggering"]); } catch { valid_entry = false; } if (valid_entry) { m_AllowGhostTriggering = boolEntry; } valid_entry = true; try { boolEntry = bool.Parse((string)dr["AllowNPCTriggering"]); } catch { valid_entry = false; } if (valid_entry) { m_AllowNPCTriggering = boolEntry; } valid_entry = true; try { boolEntry = bool.Parse((string)dr["SpawnOnTrigger"]); } catch { valid_entry = false; } if (valid_entry) { m_SpawnOnTrigger = boolEntry; } valid_entry = true; try { boolEntry = bool.Parse((string)dr["SmartSpawning"]); } catch { valid_entry = false; } if (valid_entry) { m_SmartSpawning = boolEntry; } valid_entry = true; try { strEntry = (string)dr["RegionName"]; } catch { valid_entry = false; } if (valid_entry) { RegionName = strEntry; } valid_entry = true; try { strEntry = (string)dr["PlayerPropertyName"]; } catch { valid_entry = false; } if (valid_entry) { m_PlayerPropertyName = strEntry; } valid_entry = true; try { strEntry = (string)dr["MobPropertyName"]; } catch { valid_entry = false; } if (valid_entry) { m_MobPropertyName = strEntry; } valid_entry = true; try { strEntry = (string)dr["MobTriggerName"]; } catch { valid_entry = false; } if (valid_entry) { m_MobTriggerName = strEntry; } valid_entry = true; try { strEntry = (string)dr["ObjectPropertyName"]; } catch { valid_entry = false; } if (valid_entry) { m_ObjectPropertyName = strEntry; } valid_entry = true; try { strEntry = (string)dr["ObjectPropertyItemName"]; } catch { valid_entry = false; } if (valid_entry) { string[] typeargs = strEntry.Split(",".ToCharArray(), 2); string typestr = null; string namestr = strEntry; if (typeargs.Length > 1) { namestr = typeargs[0]; typestr = typeargs[1]; } m_ObjectPropertyItem = BaseXmlSpawner.FindItemByName(this, namestr, typestr); } valid_entry = true; try { strEntry = (string)dr["SetPropertyItemName"]; } catch { valid_entry = false; } if (valid_entry) { string[] typeargs = strEntry.Split(",".ToCharArray(), 2); string typestr = null; string namestr = strEntry; if (typeargs.Length > 1) { namestr = typeargs[0]; typestr = typeargs[1]; } m_SetPropertyItem = BaseXmlSpawner.FindItemByName(this, namestr, typestr); } valid_entry = true; try { strEntry = (string)dr["Name"]; } catch { valid_entry = false; } if (valid_entry) { Name = strEntry; } valid_entry = true; try { strEntry = (string)dr["Map"]; } catch { valid_entry = false; } if (valid_entry) { // Convert the xml map value to a real map object try { Map = Map.Parse(strEntry); } catch (Exception e) { Diagnostics.ExceptionLogging.LogException(e); } } // try loading the new spawn specifications first SpawnObject[] Spawns = new SpawnObject[0]; bool havenew = true; valid_entry = true; try { Spawns = SpawnObject.LoadSpawnObjectsFromString2((string)dr["Objects2"]); } catch { havenew = false; } if (!havenew) { // try loading the new spawn specifications try { Spawns = SpawnObject.LoadSpawnObjectsFromString((string)dr["Objects"]); } catch { valid_entry = false; } // can only have one of these defined } if (valid_entry) { // clear existing spawns RemoveSpawnObjects(); // Create the new array of spawned objects m_SpawnObjects = new List(); // Assign the list of objects to spawn SpawnObjects = Spawns; } } } } } } public void ReportStatus() { if (PropertyInfoList != null) { Console.WriteLine("PropertyInfoList: {0}", PropertyInfoList.Count); foreach (BaseXmlSpawner.TypeInfo to in PropertyInfoList) { Console.WriteLine("\t{0}", to.t); foreach (PropertyInfo p in to.plist) { Console.WriteLine("\t\t{0}", p); } } } ShowTagList(this); int count = 0; Console.WriteLine("Registered SkillsTotal = {0}", count); } #endregion #region Trace support #if (TRACE) readonly string setname1 = _traceName[1] = "XmlFind"; readonly string setname2 = _traceName[2] = "HasSector"; readonly string setname4 = _traceName[4] = "AttachSpeech"; readonly string setname5 = _traceName[5] = "HasHold"; readonly string setname8 = _traceName[8] = "OnTick"; readonly string setname9 = _traceName[9] = "Defrag"; readonly string setname10 = _traceName[10] = "Respawn"; readonly string setname11 = _traceName[11] = "SetProp"; readonly string setname12 = _traceName[12] = "AttachMovement"; readonly string setname13 = _traceName[13] = "ActiveSector"; readonly string setname15 = _traceName[15] = "DistroTick"; readonly string setname16 = _traceName[16] = "GetScaledFaction"; readonly string setname17 = _traceName[17] = "FactionOnKill"; readonly string setname18 = _traceName[18] = "CheckAcquire"; private const int MaxTraces = 20; private static readonly DateTime[] _traceStart = new DateTime[MaxTraces]; public static TimeSpan[] _traceTotal = new TimeSpan[MaxTraces]; public static string[] _traceName = new string[MaxTraces]; public static int[] _traceCount = new int[MaxTraces]; private static DateTime _traceStartTime = DateTime.UtcNow; private static double _startProcessTime = 0; public static void _TraceStart(int index) { if (index < MaxTraces) { _traceStart[index] = DateTime.UtcNow; //_traceStart[index] = Process.GetCurrentProcess().UserProcessorTime; } } public static void _TraceEnd(int index) { if (index < MaxTraces) { XmlSpawner._traceTotal[index] = XmlSpawner._traceTotal[index].Add(DateTime.UtcNow - _traceStart[index]); //XmlSpawner._traceTotal[index] = XmlSpawner._traceTotal[index].Add(Process.GetCurrentProcess().UserProcessorTime - _traceStart[index]); XmlSpawner._traceCount[index]++; } } #else public static void _TraceStart(int index) { } public static void _TraceEnd(int index) { } #endif #endregion #region Trigger Methods private bool ValidPlayerTrig(Mobile m) { if (m == null || m.Deleted) return false; return ((m.Player || m_AllowNPCTriggering) && (m.AccessLevel <= TriggerAccessLevel) && ((!m.Body.IsGhost && !m_AllowGhostTriggering) || (m.Body.IsGhost && m_AllowGhostTriggering))); } private bool AllowTriggering => m_Running && !m_refractActivated && TODInRange && CanSpawn; private void ActivateTrigger() { DoTimer(); // reset the timer // start the refractory timer to set proximity activated to false, thus enabling another activation if (m_MaxRefractory > TimeSpan.FromMinutes(0)) { int minSeconds = (int)m_MinRefractory.TotalSeconds; int maxSeconds = (int)m_MaxRefractory.TotalSeconds; DoTimer3(TimeSpan.FromSeconds(Utility.RandomMinMax(minSeconds, maxSeconds))); } // if the spawnontrigger flag is set, then spawn immediately if (m_SpawnOnTrigger) { NextSpawn = TimeSpan.Zero; ResetNextSpawnTimes(); } // reset speech triggering if it was set m_speechTriggerActivated = false; } public void CheckTriggers(Mobile m, Skill s, bool hasproximity) { if (AllowTriggering && !m_proximityActivated) // only proximity trigger when no spawns have already been triggered { bool needs_speech_trigger = false; bool needs_player_trigger = false; bool has_player_trigger = false; m_skipped = false; // test for the various triggering options in the order of increasing computational demand. No point checking a high demand test // if a low demand one has already failed. // check for external triggering if (m_ExternalTriggering && !m_ExternalTrigger) return; // if speech triggering is set then test for successful activation if (!string.IsNullOrEmpty(m_SpeechTrigger)) { needs_speech_trigger = true; } // check to see if we have to continue if (needs_speech_trigger && !m_speechTriggerActivated) return; // if player property triggering is set then look for the mob and test properties if (!string.IsNullOrEmpty(m_PlayerPropertyName)) { needs_player_trigger = true; string status_str; if (BaseXmlSpawner.TestMobProperty(this, m, m_PlayerPropertyName, out status_str)) { has_player_trigger = true; } if (!string.IsNullOrEmpty(status_str)) { this.status_str = status_str; } } // check to see if we have to continue if (needs_player_trigger && !has_player_trigger) return; // if this was called without being proximity triggered then check to see that the non-movement triggers were enabled. if (!hasproximity && !m_ExternalTriggering) return; // all of the necessary trigger conditions have been met so go ahead and trigger // after you make the probability check if (Utility.RandomDouble() < m_TriggerProbability) { // play a sound indicating the spawner has been triggered if (m_ProximityTriggerSound > 0 && m != null && !m.Deleted) m.PlaySound(m_ProximityTriggerSound); // display the trigger message if (!string.IsNullOrEmpty(m_ProximityTriggerMessage) && m != null && !m.Deleted) m.PublicOverheadMessage(MessageType.Regular, 0x3B2, false, m_ProximityTriggerMessage); // enable spawning at the next ontick // this will also start the refractory timer and send the triggering indicators ProximityActivated = true; // keep track of who triggered this m_mob_who_triggered = m; } else { m_skipped = true; // reset speech triggering if it was set m_speechTriggerActivated = false; } } } public override bool HandlesOnSpeech => (m_Running && !string.IsNullOrEmpty(m_SpeechTrigger)); public override void OnSpeech(SpeechEventArgs e) { if (m_Running && m_ProximityRange >= 0 && ValidPlayerTrig(e.Mobile) && CanSpawn && !m_refractActivated && TODInRange) { m_speechTriggerActivated = false; if (!Utility.InRange(e.Mobile.Location, Location, m_ProximityRange)) return; if (m_SpeechTrigger != null && e.Speech.ToLower().IndexOf(m_SpeechTrigger.ToLower()) >= 0) { e.Handled = true; // found the speech trigger so flag it for testing in the onmovement handler where the other proximity features are tested m_speechTriggerActivated = true; CheckTriggers(e.Mobile, null, true); } } } public override bool HandlesOnMovement => (m_Running && m_ProximityRange >= 0); public void AddToMovementList(Mobile m) { // go through the list and check for redundancy if (m_MovementList == null) { m_MovementList = new List(); } // check to see if the movement timer is running if (m_MovementTimer == null || !m_MovementTimer.Running) { DoMovementTimer(TimeSpan.FromSeconds(1)); } bool add = true; foreach (MovementInfo moveinfo in m_MovementList) { Mobile mtrig = moveinfo.trigMob; if (mtrig == m) { add = false; break; } } // wasnt on the list so add it if (add) { // is the list at max throttling length? if (m_MovementList.Count > MaxMoveCheck) { // replace a random entry in the current list with this one m_MovementList[Utility.Random(m_MovementList.Count)] = new MovementInfo(m); } else { m_MovementList.Add(new MovementInfo(m)); } } } public void DoMovementTimer(TimeSpan delay) { if (m_MovementTimer != null) m_MovementTimer.Stop(); m_MovementTimer = new MovementTimer(this, delay); m_MovementTimer.Start(); } private class MovementTimer : Timer { private readonly XmlSpawner m_Spawner; public MovementTimer(XmlSpawner spawner, TimeSpan delay) : base(delay) { Priority = TimerPriority.OneSecond; m_Spawner = spawner; } protected override void OnTick() { // check everyone on the movement list then clear the list if (m_Spawner != null && !m_Spawner.Deleted) { if (m_Spawner.m_Running && !m_Spawner.m_proximityActivated && !m_Spawner.m_refractActivated && m_Spawner.TODInRange && m_Spawner.CanSpawn) { int count = 0; int maxspeed = 0; foreach (MovementInfo moveinfo in m_Spawner.m_MovementList) { Mobile m = moveinfo.trigMob; if (m == null) continue; // additional throttling in here by limiting number of mobs that can be checked in a single ontick count++; if (count > MaxMoveCheck) break; int speed = (int)GetDistance(m.Location, moveinfo.trigLocation); if (speed > maxspeed) maxspeed = speed; m_Spawner.CheckTriggers(m, null, true); } m_Spawner.MovingPlayerCount = m_Spawner.m_MovementList.Count; m_Spawner.FastestPlayerSpeed = maxspeed; } m_Spawner.m_MovementList.Clear(); } } } public static double GetDistance(Point3D p1, Point3D p2) { int xDelta = p1.X - p2.X; int yDelta = p1.Y - p2.Y; return Math.Sqrt((xDelta * xDelta) + (yDelta * yDelta)); } public override void OnMovement(Mobile m, Point3D oldLocation) { if (m_Running && m_ProximityRange >= 0 && ValidPlayerTrig(m) && CanSpawn) { // check to see if player is within range of the spawner if ((Parent == null) && Utility.InRange(m.Location, Location, m_ProximityRange)) { // add some throttling code here. // add the player to a list that gets cleared every few seconds, checking for redundancy then trigger off of the list instead of off of // the actual movement stream AddToMovementList(m); } else { // clear any speech triggering m_speechTriggerActivated = false; } } base.OnMovement(m, oldLocation); } #endregion #region Initialization public static bool AssignSettings(string argname, string value) { switch (argname) { case "XmlSpawnDir": XmlSpawnDir = value; break; case "DiskAccessLevel": DiskAccessLevel = (AccessLevel)Enum.Parse(typeof(AccessLevel), value, true); break; case "SmartSpawnAccessLevel": SmartSpawnAccessLevel = (AccessLevel)Enum.Parse(typeof(AccessLevel), value, true); break; case "defaultTriggerSound": defaultTriggerSound = ConvertToInt(value); defProximityTriggerSound = defaultTriggerSound; break; case "BaseItemId": BaseItemId = ConvertToInt(value); break; case "ShowItemId": ShowItemId = ConvertToInt(value); break; case "MaxMoveCheck": MaxMoveCheck = ConvertToInt(value); break; case "defMinDelay": defMinDelay = TimeSpan.FromMinutes(ConvertToInt(value)); break; case "defMaxDelay": defMaxDelay = TimeSpan.FromMinutes(ConvertToInt(value)); break; case "defRelativeHome": defRelativeHome = bool.Parse(value); break; case "defSpawnRange": defSpawnRange = ConvertToInt(value); break; case "defHomeRange": defHomeRange = ConvertToInt(value); break; case "BlockKeyword": { // parse the keyword list and remove them from the keyword hashtables string[] keywordlist = value.Split(','); if (keywordlist.Length > 0) { for (int i = 0; i < keywordlist.Length; i++) { BaseXmlSpawner.RemoveKeyword(keywordlist[i]); } } break; } case "BlockCommand": case "ChangeCommand": // delay processing of these settings until after all commands have been registered in their Initialize methods Timer.DelayCall(TimeSpan.Zero, new TimerStateCallback(DelayedAssignSettings), new object[] { argname, value }); break; default: return false; } return true; } private static void DelayedAssignSettings(object state) { object[] args = (object[])state; string argname = (string)args[0]; string value = (string)args[1]; switch (argname) { case "BlockCommand": { // delay processing of this until after all commands have been registered in their Initialize methods // parse the command list and remove them from the command hashtables // the syntax is "commandname, commandname, etc." string[] keywordlist = value.Split(','); if (keywordlist.Length > 0) { for (int i = 0; i < keywordlist.Length; i++) { string commandname = keywordlist[i].Trim().ToLower(); try { CommandSystem.Entries.Remove(commandname); } catch { Console.WriteLine("{0}: invalid command {1}", argname, commandname); } } } break; } case "ChangeCommand": { // delay processing of this until after all commands have been registered in their Initialize methods // parse the command list and rehash them into the command hashtables // the syntax is "oldname:newname[:accesslevel], oldname:newname[:accesslevel], etc." string[] keywordlist = value.Split(','); if (keywordlist.Length > 0) { for (int i = 0; i < keywordlist.Length; i++) { string[] namelist = keywordlist[i].Split(':'); if (namelist.Length > 1) { string oldname = namelist[0].Trim().ToLower(); string newname = namelist[1].Trim(); if (newname.Length == 0) newname = oldname; AccessLevel access = AccessLevel.Player; bool validaccess = false; if (namelist.Length > 2) { // get the new accesslevel try { access = (AccessLevel)Enum.Parse(typeof(AccessLevel), namelist[2].Trim(), true); validaccess = true; } catch { Console.WriteLine("{0}: invalid accesslevel {1} for {2}", argname, namelist[2], newname); } } // find the command entry for the old name CommandEntry e = null; try { e = CommandSystem.Entries[oldname]; } catch { Console.WriteLine("{0}: invalid command {1}", argname, oldname); } if (e != null) { if (!validaccess) { // use the old accesslevel access = e.AccessLevel; } // remove the old command entry CommandSystem.Entries.Remove(oldname); // register the new command using the old handler CommandSystem.Register(newname, access, e.Handler); } // also look in the targetcommands list and adjust name and accesslevel there foreach (BaseCommand b in TargetCommands.AllCommands) { if (b.Commands != null) { for (int j = 0; j < b.Commands.Length; j++) { string commandname = b.Commands[j]; if (commandname.ToLower() == oldname) { // modify the basecommand with the new name and access b.Commands[j] = newname; if (validaccess) { b.AccessLevel = access; } // re-register it in the implementors hashtable List impls = BaseCommandImplementor.Implementors; for (int k = 0; k < impls.Count; ++k) { BaseCommandImplementor impl = impls[k]; if ((b.Supports & impl.SupportRequirement) != 0) { try { impl.Commands.Remove(commandname); } catch (Exception ex) { Diagnostics.ExceptionLogging.LogException(ex); } impl.Register(b); } } break; } } } } } } } break; } } } public delegate bool AssignSettingsHandler(string argname, string value); // load in settings from the xmlspawner2.cfg file in the Data directory public static void LoadSettings(AssignSettingsHandler settingshandler, string section) { // Check if the file exists string path = Path.Combine(Core.BaseDirectory, "Data/xmlspawner.cfg"); if (!File.Exists(path)) { return; } Console.WriteLine("Loading {0} configuration", section); using (StreamReader ip = new StreamReader(path)) { string line; string currentsection = null; int nsettings = 0; while ((line = ip.ReadLine()) != null) { line = line.Trim(); // skip comments if (line.Length == 0 || line.StartsWith("#")) continue; if (line.StartsWith("[")) { // parse the section name string[] args = line.Split("[]".ToCharArray(), 3); if (args.Length > 2) { currentsection = args[1].Trim(); } } // only process the matching classname section if (currentsection != section) continue; string[] split = line.Split('='); if (split.Length >= 2) { string argname = split[0].Trim(); string value = split[1].Trim(); if (argname.Length == 0 || value.Length == 0) continue; try { if (settingshandler(argname, value)) nsettings++; else Console.WriteLine("'{0}' setting is invalid in section [{1}]", argname, currentsection); } catch (Exception e) { Console.WriteLine("Config error '{0}'='{1}'", argname, value); Console.WriteLine("Error: {0}", e.Message); Diagnostics.ExceptionLogging.LogException(e); } } } if (nsettings > 0) { Console.WriteLine("{0} settings processed", nsettings); } } } public static void Initialize() { LoadSettings(AssignSettings, "XmlSpawner"); // initialize the default waypoint name WayPoint tmpwaypoint = new WayPoint(); defwaypointname = tmpwaypoint.Name; tmpwaypoint.Delete(); int count = 0; int regional = 0; foreach (Item item in World.Items.Values) { if (item is XmlSpawner) { count++; XmlSpawner spawner = ((XmlSpawner)item); if (!string.IsNullOrEmpty(spawner.RegionName)) { spawner.RegionName = spawner.RegionName; // invoke set(RegionName) regional++; } // check for smart spawning and restart timers after deser if needed // note, HasActiveSectors will recalculate the sector list and UseSectorActivate property bool recalc_sectors = spawner.HasActiveSectors; spawner.RestoreISpawner(); } } // start the global smartspawning timer if (SmartSpawningSystemEnabled) { DoGlobalSectorTimer(TimeSpan.FromSeconds(1)); } // standard commands CommandSystem.Register("XmlSpawnerShowAll", AccessLevel.Administrator, ShowSpawnPoints_OnCommand); CommandSystem.Register("XmlSpawnerHideAll", AccessLevel.Administrator, HideSpawnPoints_OnCommand); CommandSystem.Register("XmlSpawnerWipe", AccessLevel.Administrator, Wipe_OnCommand); CommandSystem.Register("XmlSpawnerWipeAll", AccessLevel.Administrator, WipeAll_OnCommand); CommandSystem.Register("XmlSpawnerLoad", DiskAccessLevel, Load_OnCommand); CommandSystem.Register("XmlSpawnerSave", DiskAccessLevel, Save_OnCommand); CommandSystem.Register("XmlSpawnerSaveAll", DiskAccessLevel, SaveAll_OnCommand); CommandSystem.Register("XmlSpawnerRespawn", AccessLevel.Seer, Respawn_OnCommand); CommandSystem.Register("XmlSpawnerRespawnAll", AccessLevel.Seer, RespawnAll_OnCommand); CommandSystem.Register("XmlShow", AccessLevel.Administrator, ShowSpawnPoints_OnCommand); CommandSystem.Register("XmlHide", AccessLevel.Administrator, HideSpawnPoints_OnCommand); CommandSystem.Register("XmlHome", AccessLevel.GameMaster, XmlHome_OnCommand); CommandSystem.Register("XmlUnLoad", DiskAccessLevel, UnLoad_OnCommand); CommandSystem.Register("XmlSpawnerUnLoad", DiskAccessLevel, UnLoad_OnCommand); CommandSystem.Register("XmlLoad", DiskAccessLevel, Load_OnCommand); CommandSystem.Register("XmlLoadHere", DiskAccessLevel, LoadHere_OnCommand); CommandSystem.Register("XmlNewLoad", DiskAccessLevel, NewLoad_OnCommand); CommandSystem.Register("XmlNewLoadHere", DiskAccessLevel, NewLoadHere_OnCommand); CommandSystem.Register("XmlSave", DiskAccessLevel, Save_OnCommand); CommandSystem.Register("XmlSaveAll", DiskAccessLevel, SaveAll_OnCommand); CommandSystem.Register("XmlSaveOld", DiskAccessLevel, SaveOld_OnCommand); CommandSystem.Register("XmlImportSpawners", DiskAccessLevel, XmlImportSpawners_OnCommand); CommandSystem.Register("XmlImportMSF", DiskAccessLevel, XmlImportMSF_OnCommand); CommandSystem.Register("XmlImportMap", DiskAccessLevel, XmlImportMap_OnCommand); CommandSystem.Register("XmlDefaults", AccessLevel.Administrator, XmlDefaults_OnCommand); CommandSystem.Register("XmlGet", AccessLevel.GameMaster, XmlGetValue_OnCommand); CommandSystem.Register("OptimalSmartSpawning", AccessLevel.Administrator, OptimalSmartSpawning_OnCommand); CommandSystem.Register("SmartStat", AccessLevel.GameMaster, SmartStat_OnCommand); CommandSystem.Register("XmlGo", AccessLevel.GameMaster, SpawnEditorGo_OnCommand); TargetCommands.Register(new XmlSetCommand()); TargetCommands.Register(new XmlSaveSingle()); #if (TRACE) CommandSystem.Register("XmlMake", AccessLevel.Administrator, new CommandEventHandler(XmlMake_OnCommand)); CommandSystem.Register("XmlTrace", AccessLevel.Administrator, new CommandEventHandler(XmlTrace_OnCommand)); CommandSystem.Register("XmlResetTrace", AccessLevel.Administrator, new CommandEventHandler(XmlResetTrace_OnCommand)); #endif } #endregion #region Commands [Usage("XmlGet property")] [Description("Returns value of the property on the targeted object.")] public static void XmlGetValue_OnCommand(CommandEventArgs e) { e.Mobile.Target = new GetValueTarget(e); } private class GetValueTarget : Target { private readonly CommandEventArgs m_e; public GetValueTarget(CommandEventArgs e) : base(30, false, TargetFlags.None) { m_e = e; } protected override void OnTarget(Mobile from, object targeted) { string pname = m_e.GetString(0); Type ptype; string result = BaseXmlSpawner.GetPropertyValue(null, targeted, pname, out ptype); // see if it was successful if (ptype == null) { return; } from.SendMessage("{0}", result); } } public class XmlSetCommand : BaseCommand { public XmlSetCommand() { AccessLevel = AccessLevel.Administrator; Supports = CommandSupport.All; Commands = new[] { "XmlSet" }; ObjectTypes = ObjectTypes.Both; Usage = "XmlSet "; Description = "Sets a property value by name of a targeted object. Provides access to all public properties."; } public override void Execute(CommandEventArgs e, object obj) { if (e.Length >= 2) { string result = BaseXmlSpawner.SetPropertyValue(null, obj, e.GetString(0), e.GetString(1)); if (result == "Property has been set.") AddResponse(result); else LogFailure(result); } else { LogFailure("Format: XmlSet "); } } } [Usage("TagList property")] [Description("Lists the keyword taglist for a spawner")] public static void ShowTagList_OnCommand(CommandEventArgs e) { e.Mobile.Target = new TagListTarget(e); } private class TagListTarget : Target { private readonly CommandEventArgs m_e; public TagListTarget(CommandEventArgs e) : base(30, false, TargetFlags.None) { m_e = e; } protected override void OnTarget(Mobile from, object targeted) { if (targeted is XmlSpawner) ((XmlSpawner)targeted).ShowTagList((XmlSpawner)targeted); } } public void ShowTagList(XmlSpawner spawner) { int count = 0; Console.WriteLine("{0} tags", spawner.m_KeywordTagList.Count); foreach (BaseXmlSpawner.KeywordTag tag in spawner.m_KeywordTagList) { count++; Console.WriteLine("tag {0} : {1}", count, BaseXmlSpawner.TagInfo(tag)); } } // added in targeting for the [xmlhome command private class XmlHomeTarget : Target { private readonly CommandEventArgs m_e; public XmlHomeTarget(CommandEventArgs e) : base(30, false, TargetFlags.None) { m_e = e; } protected override void OnTarget(Mobile from, object targeted) { XmlSpawner spawner = null; if (targeted is XmlSpawner) { if (m_e.GetString(0) == "status") { ((XmlSpawner)targeted).ReportStatus(); return; } } if (targeted is Mobile) { spawner = ((Mobile)targeted).Spawner as XmlSpawner; } else if (targeted is Item) { spawner = ((Item)targeted).Spawner as XmlSpawner; } if (spawner == null) { from.SendMessage("Unable to find spawner for this object"); return; } // check to make sure it is still on the spawner foreach (SpawnObject so in spawner.m_SpawnObjects) { for (int x = 0; x < so.SpawnedObjects.Count; x++) { object o = so.SpawnedObjects[x]; if (o == targeted) { from.SendMessage("{0}, {1}, {2}", spawner.X, spawner.Y, spawner.Z); if (m_e.GetString(0) == "go") { // make sure the spawner is not in a container. if (spawner.Parent == null) { from.Location = new Point3D(spawner.Location); from.Map = spawner.Map; } else { from.SendMessage("Spawner is in a container"); } } else if (m_e.GetString(0) == "send") { // make sure the spawner is not in a container. if (spawner.Parent == null) { if (o is Item) { ((Item)o).Location = new Point3D(spawner.Location); ((Item)o).Map = spawner.Map; } if (o is Mobile) { ((Mobile)o).Location = new Point3D(spawner.Location); ((Mobile)o).Map = spawner.Map; } } else { from.SendMessage("Spawner is in a container"); } } else if (m_e.GetString(0) == "gump") { spawner.OnDoubleClick(from); } return; } } } } } [Usage("XmlHome [go][gump][send]")] [Description("Returns the coordinates of the spawner for the targeted object. Args: 'go' teleports to spawner, 'gump' opens spawner gump, 'send' sends mob home")] public static void XmlHome_OnCommand(CommandEventArgs e) { e.Mobile.Target = new XmlHomeTarget(e); } private static void XmlSaveDefaults(string filePath, Mobile m) { if (string.IsNullOrEmpty(filePath)) return; using (StreamWriter op = new StreamWriter(filePath)) { XmlTextWriter xml = new XmlTextWriter(op) { Formatting = Formatting.Indented, IndentChar = '\t', Indentation = 1 }; xml.WriteStartDocument(true); xml.WriteStartElement("XmlDefaults"); xml.WriteStartElement("defProximityRange"); xml.WriteString(defProximityRange.ToString()); xml.WriteEndElement(); xml.WriteStartElement("defTriggerProbability"); xml.WriteString(defTriggerProbability.ToString()); xml.WriteEndElement(); xml.WriteStartElement("defProximityTriggerSound"); xml.WriteString(defProximityTriggerSound.ToString()); xml.WriteEndElement(); xml.WriteStartElement("defMinRefractory"); xml.WriteString(defMinRefractory.ToString()); xml.WriteEndElement(); xml.WriteStartElement("defMaxRefractory"); xml.WriteString(defMaxRefractory.ToString()); xml.WriteEndElement(); xml.WriteStartElement("defTODStart"); xml.WriteString(defTODStart.ToString()); xml.WriteEndElement(); xml.WriteStartElement("defTODEnd"); xml.WriteString(defTODEnd.ToString()); xml.WriteEndElement(); xml.WriteStartElement("defStackAmount"); xml.WriteString(defAmount.ToString()); xml.WriteEndElement(); xml.WriteStartElement("defDuration"); xml.WriteString(defDuration.ToString()); xml.WriteEndElement(); xml.WriteStartElement("defIsGroup"); xml.WriteString(defIsGroup.ToString()); xml.WriteEndElement(); xml.WriteStartElement("defTeam"); xml.WriteString(defTeam.ToString()); xml.WriteEndElement(); xml.WriteStartElement("defRelativeHome"); xml.WriteString(defRelativeHome.ToString()); xml.WriteEndElement(); xml.WriteStartElement("defSpawnRange"); xml.WriteString(defSpawnRange.ToString()); xml.WriteEndElement(); xml.WriteStartElement("defHomeRange"); xml.WriteString(defHomeRange.ToString()); xml.WriteEndElement(); xml.WriteStartElement("defMinDelay"); xml.WriteString(defMinDelay.ToString()); xml.WriteEndElement(); xml.WriteStartElement("defMaxDelay"); xml.WriteString(defMaxDelay.ToString()); xml.WriteEndElement(); xml.WriteStartElement("defTODMode"); xml.WriteString(defTODMode.ToString()); xml.WriteEndElement(); xml.WriteEndElement(); xml.Close(); } m.SendMessage("defaults saved to {0}", filePath); } public static void XmlLoadDefaults(string filePath, Mobile m) { if (m == null || m.Deleted) return; if (!string.IsNullOrEmpty(filePath)) { if (File.Exists(filePath)) { XmlDocument doc = new XmlDocument(); doc.Load(filePath); XmlElement root = doc["XmlDefaults"]; LoadDefaults(root); m.SendMessage("defaults loaded successfully from {0}", filePath); } else { m.SendMessage("File {0} does not exist.", filePath); } } } private static void LoadDefaults(XmlElement node) { try { defProximityRange = int.Parse(node["defProximityRange"].InnerText); } catch (Exception e) { Diagnostics.ExceptionLogging.LogException(e); } try { defTriggerProbability = double.Parse(node["defTriggerProbability"].InnerText); } catch (Exception e) { Diagnostics.ExceptionLogging.LogException(e); } try { defProximityTriggerSound = int.Parse(node["defProximityTriggerSound"].InnerText); } catch (Exception e) { Diagnostics.ExceptionLogging.LogException(e); } try { defMinRefractory = TimeSpan.Parse(node["defMinRefractory"].InnerText); } catch (Exception e) { Diagnostics.ExceptionLogging.LogException(e); } try { defMaxRefractory = TimeSpan.Parse(node["defMaxRefractory"].InnerText); } catch (Exception e) { Diagnostics.ExceptionLogging.LogException(e); } try { defTODStart = TimeSpan.Parse(node["defTODStart"].InnerText); } catch (Exception e) { Diagnostics.ExceptionLogging.LogException(e); } try { defTODEnd = TimeSpan.Parse(node["defTODEnd"].InnerText); } catch (Exception e) { Diagnostics.ExceptionLogging.LogException(e); } try { defAmount = int.Parse(node["defStackAmount"].InnerText); } catch (Exception e) { Diagnostics.ExceptionLogging.LogException(e); } try { defDuration = TimeSpan.Parse(node["defDuration"].InnerText); } catch (Exception e) { Diagnostics.ExceptionLogging.LogException(e); } try { defIsGroup = bool.Parse(node["defIsGroup"].InnerText); } catch (Exception e) { Diagnostics.ExceptionLogging.LogException(e); } try { defTeam = int.Parse(node["defTeam"].InnerText); } catch (Exception e) { Diagnostics.ExceptionLogging.LogException(e); } try { defRelativeHome = bool.Parse(node["defRelativeHome"].InnerText); } catch (Exception e) { Diagnostics.ExceptionLogging.LogException(e); } try { defSpawnRange = int.Parse(node["defSpawnRange"].InnerText); } catch (Exception e) { Diagnostics.ExceptionLogging.LogException(e); } try { defHomeRange = int.Parse(node["defHomeRange"].InnerText); } catch (Exception e) { Diagnostics.ExceptionLogging.LogException(e); } try { defMinDelay = TimeSpan.Parse(node["defMinDelay"].InnerText); } catch (Exception e) { Diagnostics.ExceptionLogging.LogException(e); } try { defMaxDelay = TimeSpan.Parse(node["defMaxDelay"].InnerText); } catch (Exception e) { Diagnostics.ExceptionLogging.LogException(e); } int todmode = 0; try { todmode = int.Parse(node["defTODMode"].InnerText); } catch (Exception e) { Diagnostics.ExceptionLogging.LogException(e); } switch (todmode) { case (int)TODModeType.Realtime: defTODMode = TODModeType.Realtime; break; case (int)TODModeType.Gametime: defTODMode = TODModeType.Gametime; break; } } [Usage("XmlDefaults [defaultpropertyname value]")] [Description("Returns or changes the default settings of the spawner.")] public static void XmlDefaults_OnCommand(CommandEventArgs e) { Mobile m = e.Mobile; if (m == null || m.Deleted) return; if (e.Arguments.Length >= 1) { // leave open the possibility of just requesting display of a single property if (e.Arguments.Length == 2) { if (e.Arguments[0].ToLower() == "save") { XmlSaveDefaults(e.Arguments[1], m); } else if (e.Arguments[0].ToLower() == "load") { XmlLoadDefaults(e.Arguments[1], m); } else if (e.Arguments[0].ToLower() == "maxdelay") { try { defMaxDelay = TimeSpan.FromMinutes(Convert.ToDouble(e.Arguments[1])); m.SendMessage("MaxDelay = {0}", defMaxDelay); } catch { m.SendMessage("invalid value : {0}", e.Arguments[1]); } } else if (e.Arguments[0].ToLower() == "mindelay") { try { defMinDelay = TimeSpan.FromMinutes(Convert.ToDouble(e.Arguments[1])); m.SendMessage("MinDelay = {0}", defMinDelay); } catch { m.SendMessage("invalid value : {0}", e.Arguments[1]); } } else if (e.Arguments[0].ToLower() == "spawnrange") { try { defSpawnRange = Convert.ToInt32(e.Arguments[1]); m.SendMessage("SpawnRange = {0}", defSpawnRange); } catch { m.SendMessage("invalid value : {0}", e.Arguments[1]); } } else if (e.Arguments[0].ToLower() == "homerange") { try { defHomeRange = Convert.ToInt32(e.Arguments[1]); m.SendMessage("HomeRange = {0}", defHomeRange); } catch { m.SendMessage("invalid value : {0}", e.Arguments[1]); } } else if (e.Arguments[0].ToLower() == "relativehome") { try { defRelativeHome = Convert.ToBoolean(e.Arguments[1]); m.SendMessage("RelativeHome = {0}", defRelativeHome); } catch { m.SendMessage("invalid value : {0}", e.Arguments[1]); } } else if (e.Arguments[0].ToLower() == "proximitytriggersound") { try { defProximityTriggerSound = Convert.ToInt32(e.Arguments[1]); m.SendMessage("ProximityTriggerSound = {0}", defProximityTriggerSound); } catch { m.SendMessage("invalid value : {0}", e.Arguments[1]); } } else if (e.Arguments[0].ToLower() == "proximityrange") { try { defProximityRange = Convert.ToInt32(e.Arguments[1]); m.SendMessage("ProximityRange = {0}", defProximityRange); } catch { m.SendMessage("invalid value : {0}", e.Arguments[1]); } } else if (e.Arguments[0].ToLower() == "triggerprobability") { try { defTriggerProbability = Convert.ToDouble(e.Arguments[1]); m.SendMessage("TriggerProbability = {0}", defTriggerProbability); } catch { m.SendMessage("invalid value : {0}", e.Arguments[1]); } } else if (e.Arguments[0].ToLower() == "todstart") { try { defTODStart = TimeSpan.FromMinutes(Convert.ToDouble(e.Arguments[1])); m.SendMessage("TODStart = {0}", defTODStart); } catch { m.SendMessage("invalid value : {0}", e.Arguments[1]); } } else if (e.Arguments[0].ToLower() == "todend") { try { defTODEnd = TimeSpan.FromMinutes(Convert.ToDouble(e.Arguments[1])); m.SendMessage("TODEnd = {0}", defTODEnd); } catch { m.SendMessage("invalid value : {0}", e.Arguments[1]); } } else if (e.Arguments[0].ToLower() == "stackamount") { try { defAmount = Convert.ToInt32(e.Arguments[1]); m.SendMessage("StackAmount = {0}", defAmount); } catch { m.SendMessage("invalid value : {0}", e.Arguments[1]); } } else if (e.Arguments[0].ToLower() == "duration") { try { defDuration = TimeSpan.FromMinutes(Convert.ToDouble(e.Arguments[1])); m.SendMessage("Duration = {0}", defDuration); } catch { m.SendMessage("invalid value : {0}", e.Arguments[1]); } } else if (e.Arguments[0].ToLower() == "group") { try { defIsGroup = Convert.ToBoolean(e.Arguments[1]); m.SendMessage("Group = {0}", defIsGroup); } catch { m.SendMessage("invalid value : {0}", e.Arguments[1]); } } else if (e.Arguments[0].ToLower() == "team") { try { defTeam = Convert.ToInt32(e.Arguments[1]); m.SendMessage("Team = {0}", defTeam); } catch { m.SendMessage("invalid value : {0}", e.Arguments[1]); } } else if (e.Arguments[0].ToLower() == "todmode") { try { int todmode = Convert.ToInt32(e.Arguments[1]); switch (todmode) { case (int)TODModeType.Gametime: defTODMode = TODModeType.Gametime; break; case (int)TODModeType.Realtime: defTODMode = TODModeType.Realtime; break; } m.SendMessage("TODMode = {0}", defTODMode); } catch { m.SendMessage("invalid value : {0}", e.Arguments[1]); } } else if (e.Arguments[0].ToLower() == "maxrefractory") { try { defMaxRefractory = TimeSpan.FromMinutes(Convert.ToDouble(e.Arguments[1])); m.SendMessage("MaxRefractory = {0}", defMaxRefractory); } catch { m.SendMessage("invalid value : {0}", e.Arguments[1]); } } else if (e.Arguments[0].ToLower() == "minrefractory") { try { defMinRefractory = TimeSpan.FromMinutes(Convert.ToDouble(e.Arguments[1])); m.SendMessage("MinRefractory = {0}", defMinRefractory); } catch { m.SendMessage("invalid value : {0}", e.Arguments[1]); } } else { m.SendMessage("{0} : no such default value.", e.Arguments[0]); } } } else { // just display the values m.SendMessage("TriggerProbability = {0}", defTriggerProbability); m.SendMessage("ProximityRange = {0}", defProximityRange); m.SendMessage("ProximityTriggerSound = {0}", defProximityTriggerSound); m.SendMessage("MinRefractory = {0}", defMinRefractory); m.SendMessage("MaxRefractory = {0}", defMaxRefractory); m.SendMessage("TODStart = {0}", defTODStart); m.SendMessage("TODEnd = {0}", defTODEnd); m.SendMessage("TODMode = {0}", defTODMode); m.SendMessage("StackAmount = {0}", defAmount); m.SendMessage("Duration = {0}", defDuration); m.SendMessage("Group = {0}", defIsGroup); m.SendMessage("Team = {0}", defTeam); m.SendMessage("RelativeHome = {0}", defRelativeHome); m.SendMessage("SpawnRange = {0}", defSpawnRange); m.SendMessage("HomeRange = {0}", defHomeRange); m.SendMessage("MinDelay = {0}", defMinDelay); m.SendMessage("MaxDelay = {0}", defMaxDelay); } } [Usage("XmlSpawnerShowAll")] [Aliases("XmlShow")] [Description("Makes all XmlSpawner objects movable and also changes the item id to a blue ships mast for easy identification.")] public static void ShowSpawnPoints_OnCommand(CommandEventArgs e) { List ToShow = new List(); foreach (Item item in World.Items.Values) { if (item is XmlSpawner) { //turned off visibility. Admins will still see masts but players will not. item.Visible = false; // set the spawn item visibility item.Movable = false; // Make the spawn item movable item.Hue = 88; // Bright blue colour so its easy to spot item.ItemID = ShowItemId; // Ship Mast (Very tall, easy to see if beneath other objects) // find container-held spawners to be marked with an external static if ((item.Parent != null) && (item.RootParent is Container)) { ToShow.Add(item); } } } // place the statics foreach (XmlSpawner xml_item in ToShow) { // does the spawner already have a static attached to it? could happen if two showall commands are issued in a row. // if so then dont add another if ((xml_item.m_ShowContainerStatic == null || xml_item.m_ShowContainerStatic.Deleted) && xml_item.RootParent is Container) { Container root_item = (Container)xml_item.RootParent; // calculate a world location for the static. Position it just above the container int x = root_item.Location.X; int y = root_item.Location.Y; int z = root_item.Location.Z + 10; Static s = new Static(ShowItemId) { Visible = false }; s.MoveToWorld(new Point3D(x, y, z), root_item.Map); xml_item.m_ShowContainerStatic = s; } } } [Usage("XmlSpawnerHideAll")] [Aliases("XmlHide")] [Description("Makes all XmlSpawner objects invisible and unmovable returns the object id to the default.")] public static void HideSpawnPoints_OnCommand(CommandEventArgs e) { List ToDelete = new List(); foreach (Item item in World.Items.Values) { if (item is XmlSpawner) { item.Visible = false; item.Movable = false; item.Hue = 0; item.ItemID = BaseItemId; // get rid of the external static marker for container-held spawners // check anything that might have been tagged with a container static XmlSpawner xml_item = (XmlSpawner)item; if (xml_item.m_ShowContainerStatic != null && !xml_item.m_ShowContainerStatic.Deleted) { ToDelete.Add(item); } } } foreach (XmlSpawner xml_item in ToDelete) { if (xml_item.m_ShowContainerStatic != null && !xml_item.m_ShowContainerStatic.Deleted) { xml_item.m_ShowContainerStatic.Delete(); } } } [Usage("XmlGo | [z]")] [Description("Go command used with spawn editor, takes the name of the map as the first parameter.")] private static void SpawnEditorGo_OnCommand(CommandEventArgs e) { if (e == null) return; Mobile from = e.Mobile; // Make sure a map name was given at least if (from != null && e.Length >= 1) { string MapName = e.Arguments[0]; // Get the map Map NewMap; // Convert the xml map value to a real map object if (string.Compare(MapName, Map.Trammel.Name, true) == 0) NewMap = Map.Trammel; else if (string.Compare(MapName, Map.Felucca.Name, true) == 0) NewMap = Map.Felucca; else if (string.Compare(MapName, Map.Ilshenar.Name, true) == 0) NewMap = Map.Ilshenar; else if (string.Compare(MapName, Map.Malas.Name, true) == 0) NewMap = Map.Malas; else if (string.Compare(MapName, Map.Tokuno.Name, true) == 0) NewMap = Map.Tokuno; else { from.SendMessage("Map '{0}' does not exist!", MapName); return; } // Now that the map has been determined, continue // Check if the request is to simply change maps if (e.Length == 1) { // Map Change ONLY from.Map = NewMap; } else if (e.Length == 3) { // Map & X Y ONLY if (NewMap != null) { int x = e.GetInt32(1); int y = e.GetInt32(2); int z = NewMap.GetAverageZ(x, y); from.Map = NewMap; from.Location = new Point3D(x, y, z); } } else if (e.Length == 4) { // Map & X Y Z from.Map = NewMap; from.Location = new Point3D(e.GetInt32(1), e.GetInt32(2), e.GetInt32(3)); } else { from.SendMessage("Format: XmlGo | [z]"); } } } [Usage("SmartStat [accesslevel Player/Counselor/GameMaster/Seer/Administrator]")] [Description("Returns the spawn reduction due to SmartSpawning.")] public static void SmartStat_OnCommand(CommandEventArgs e) { if (e == null || e.Mobile == null) return; if (e.Arguments.Length > 1 && e.Arguments[0].ToLower() == "accesslevel" && e.Mobile.AccessLevel >= AccessLevel.Administrator) { try { SmartSpawnAccessLevel = (AccessLevel)Enum.Parse(typeof(AccessLevel), e.Arguments[1], true); } catch (Exception ex) { Diagnostics.ExceptionLogging.LogException(ex); } } // handle the // number of spawners int count = 0; // number of actual spawns int currentcount = 0; int smartcount = 0; int inactivecount = 0; // maximum possible spawns int totalcount = 0; int maxcount = 0; // maximum possible of spawns that are currently inactivated int savings = 0; foreach (Item item in World.Items.Values) { if (item is XmlSpawner) { XmlSpawner spawner = (XmlSpawner)item; if (spawner.Deleted) continue; totalcount += spawner.MaxCount; // get the current count without defragging currentcount += spawner.SafeCurrentCount; count++; // check to see if smartspawning is set if (spawner.SmartSpawning) { smartcount++; maxcount += spawner.MaxCount; } if (spawner.IsInactivated) { inactivecount++; savings += spawner.MaxCount; } } } int percent = 0; int maxpercent = 0; if (totalcount > 0) { percent = 100 * savings / totalcount; maxpercent = 100 * maxcount / totalcount; } e.Mobile.SendMessage( "Smartspawning access level is {10}\n" + "--------------------------------\n" + "{0} XmlSpawners\n" + "{1} are configured for SmartSpawning\n" + "{2} are currently inactivated\n" + "{9} sectors being monitored\n" + "Maximum possible spawn count is {3}\n" + "Maximum possible spawn reduction is {4}\n" + "Current spawn count is {5}\n" + "Current spawn reduction is {6}\n" + "Maximum possible savings is {7}%\n" + "Current savings is {8}%", count, smartcount, inactivecount, totalcount, maxcount, currentcount, savings, maxpercent, percent, totalSectorsMonitored, SmartSpawnAccessLevel); } [Usage("OptimalSmartSpawning [max spawn/homerange diff]")] [Description("Activates SmartSpawning on XmlSpawners that are well-suited for use of this feature.")] public static void OptimalSmartSpawning_OnCommand(CommandEventArgs e) { int maxdiff = 1; if (e.Arguments.Length > 0) { try { maxdiff = int.Parse(e.Arguments[0]); } catch (Exception ex) { Diagnostics.ExceptionLogging.LogException(ex); } } int count = 0; int maxcount = 0; foreach (Item item in World.Items.Values) { if (item is XmlSpawner) { XmlSpawner spawner = (XmlSpawner)item; // determine whether this spawner is a good candidate if (spawner.Deleted) continue; // ignore spawners in towns //if (Region.Find(spawner.Location, spawner.Map) is Regions.TownRegion) continue; // dont bother setting it on triggered spawners if (spawner.ProximityRange >= 0) continue; // check the relative spawnrange and homerange. Dont set it on spawners with a larger homerange than spawnrange int width = spawner.m_Width; int height = spawner.m_Height; if (spawner.HomeRange * 2 > width + maxdiff * 2 || spawner.HomeRange * 2 > height + maxdiff * 2 && spawner.m_Region != null) continue; int nso = 0; if (spawner.m_SpawnObjects != null) nso = spawner.m_SpawnObjects.Count; // empty spawner so skip it if (nso == 0) continue; bool skipit = false; // check the spawn types for (int i = 0; i < nso; ++i) { SpawnObject so = spawner.m_SpawnObjects[i]; if (so == null) continue; string typestr = so.TypeName; Type type = SpawnerType.GetType(typestr); // if it has basevendors on it or invalid types, then skip it if (typestr == null || (type != null && (type == typeof(BaseVendor) || type.IsSubclassOf(typeof(BaseVendor)))) || ((type == null) && (!BaseXmlSpawner.IsTypeOrItemKeyword(typestr) && typestr.IndexOf('{') == -1 && !typestr.StartsWith("*") && !typestr.StartsWith("#")))) { skipit = true; break; } } if (!skipit) { count++; spawner.SmartSpawning = true; maxcount += spawner.MaxCount; } } } e.Mobile.SendMessage("Configured {0} XmlSpawners for SmartSpawning using maxdiff of {1}", count, maxdiff); e.Mobile.SendMessage("Estimated item/mob reduction is {0}", maxcount); } [Usage("XmlSpawnerWipe [SpawnerPrefixFilter]")] [Description("Removes all XmlSpawner objects from the current map.")] public static void Wipe_OnCommand(CommandEventArgs e) { WipeSpawners(e, false); } [Usage("XmlSpawnerWipeAll [SpawnerPrefixFilter]")] [Description("Removes all XmlSpawner objects from the entire world.")] public static void WipeAll_OnCommand(CommandEventArgs e) { WipeSpawners(e, true); } public static void XmlUnLoadFromFile(string filename, string SpawnerPrefix, Mobile from, out int processedmaps, out int processedspawners) { processedmaps = 0; processedspawners = 0; if (filename == null || filename.Length <= 0) return; int total_processed_maps = 0; int total_processed_spawners = 0; // Check if the file exists if (File.Exists(filename)) { FileStream fs = null; try { fs = File.Open(filename, FileMode.Open, FileAccess.Read); } catch { } if (fs == null) { if (from != null) from.SendMessage("Unable to open {0} for unloading", filename); return; } XmlUnLoadFromStream(fs, filename, SpawnerPrefix, from, out processedmaps, out processedspawners); } else // check to see if it is a directory if (Directory.Exists(filename)) { // if so then import all of the .xml files in the directory string[] files = null; try { files = Directory.GetFiles(filename, "*.xml"); } catch { } if (files != null && files.Length > 0) { if (from != null) from.SendMessage("UnLoading {0} .xml files from directory {1}", files.Length, filename); foreach (string file in files) { XmlUnLoadFromFile(file, SpawnerPrefix, from, out processedmaps, out processedspawners); total_processed_maps += processedmaps; total_processed_spawners += processedspawners; } } // recursively search subdirectories for more .xml files string[] dirs = null; try { dirs = Directory.GetDirectories(filename); } catch { } if (dirs != null && dirs.Length > 0) { foreach (string dir in dirs) { XmlUnLoadFromFile(dir, SpawnerPrefix, from, out processedmaps, out processedspawners); total_processed_maps += processedmaps; total_processed_spawners += processedspawners; } } if (from != null) from.SendMessage("UnLoaded a total of {0} .xml files and {2} spawners from directory {1}", total_processed_maps, filename, total_processed_spawners); processedmaps = total_processed_maps; processedspawners = total_processed_spawners; } else { if (from != null) from.SendMessage("{0} does not exist", filename); } } public static void XmlUnLoadFromStream(Stream fs, string filename, string SpawnerPrefix, Mobile from, out int processedmaps, out int processedspawners) { processedmaps = 0; processedspawners = 0; if (fs == null) return; int TotalCount = 0; int TrammelCount = 0; int FeluccaCount = 0; int IlshenarCount = 0; int MalasCount = 0; int TokunoCount = 0; int OtherCount = 0; int bad_spawner_count = 0; int spawners_deleted = 0; if (from != null) from.SendMessage(string.Format("UnLoading {0} objects{1} from file {2}.", "XmlSpawner", (!string.IsNullOrEmpty(SpawnerPrefix) ? " beginning with " + SpawnerPrefix : string.Empty), filename)); // Create the data set DataSet ds = new DataSet(SpawnDataSetName); // Read in the file //ds.ReadXml( e.Arguments[0].ToString() ); bool fileerror = false; try { ds.ReadXml(fs); } catch { if (from != null) from.SendMessage(33, "Error reading xml file {0}", filename); fileerror = true; } // close the file fs.Close(); if (fileerror) return; // Check that at least a single table was loaded if (ds.Tables.Count > 0) { // Add each spawn point to the current map if (ds.Tables[SpawnTablePointName] != null && ds.Tables[SpawnTablePointName].Rows.Count > 0) { foreach (DataRow dr in ds.Tables[SpawnTablePointName].Rows) { // load in the spawner info. Certain fields are required and therefore cannot be ignored // the exception handler for those will flag bad_spawner and the result will be logged // Each row makes up a single spawner string SpawnName = "Spawner"; try { SpawnName = (string)dr["Name"]; } catch { } // Check if there is any spawner name criteria specified on the unload if (SpawnerPrefix == null || (SpawnerPrefix.Length == 0) || SpawnName.StartsWith(SpawnerPrefix)) { bool bad_spawner = false; // Try load the GUID (might not work so create a new GUID) Guid SpawnId = Guid.NewGuid(); try { SpawnId = new Guid((string)dr["UniqueId"]); } catch { bad_spawner = true; } // have to have a GUID or no point in continuing if (bad_spawner) { bad_spawner_count++; continue; } // Get the map (default to the mobiles map) Map SpawnMap = Map.Internal; string XmlMapName = SpawnMap.Name; // Try to get the "map" field, but in case it doesn't exist, catch and discard the exception try { XmlMapName = (string)dr["Map"]; } catch { } // Convert the xml map value to a real map object if (string.Compare(XmlMapName, Map.Trammel.Name, true) == 0 || XmlMapName == "Trammel") { SpawnMap = Map.Trammel; TrammelCount++; } else if (string.Compare(XmlMapName, Map.Felucca.Name, true) == 0 || XmlMapName == "Felucca") { SpawnMap = Map.Felucca; FeluccaCount++; } else if (string.Compare(XmlMapName, Map.Ilshenar.Name, true) == 0 || XmlMapName == "Ilshenar") { SpawnMap = Map.Ilshenar; IlshenarCount++; } else if (string.Compare(XmlMapName, Map.Malas.Name, true) == 0 || XmlMapName == "Malas") { SpawnMap = Map.Malas; MalasCount++; } else if (string.Compare(XmlMapName, Map.Tokuno.Name, true) == 0 || XmlMapName == "Tokuno") { SpawnMap = Map.Tokuno; TokunoCount++; } else { try { SpawnMap = Map.Parse(XmlMapName); } catch { } OtherCount++; } // Check if this spawner already exists XmlSpawner OldSpawner = null; foreach (Item i in World.Items.Values) { if (i is XmlSpawner) { XmlSpawner CheckXmlSpawner = (XmlSpawner)i; // Check if the spawners GUID is the same as the one being unloaded // and that the spawners map is the same as the one being unloaded if ((CheckXmlSpawner.UniqueId == SpawnId.ToString()) /*&& ( CheckXmlSpawner.Map == SpawnMap )*/ ) { OldSpawner = (XmlSpawner)i; if (OldSpawner != null) { spawners_deleted++; OldSpawner.Delete(); } break; } } } } TotalCount++; } } } try { fs.Close(); } catch { } if (from != null) from.SendMessage("{0}/{8} spawner(s) were unloaded using file {1} [Trammel={2}, Felucca={3}, Ilshenar={4}, Malas={5}, Tokuno={6}, Other={7}].", spawners_deleted, filename, TrammelCount, FeluccaCount, IlshenarCount, MalasCount, TokunoCount, OtherCount, TotalCount); if (bad_spawner_count > 0) { if (from != null) from.SendMessage(33, "{0} bad spawners detected.", bad_spawner_count); } processedmaps = 1; processedspawners = TotalCount; } [Usage("XmlSpawnerUnLoad [SpawnerPrefixFilter]")] [Aliases("XmlUnload")] [Description("UnLoads XmlSpawner objects from the proper map as defined in the file supplied.")] public static void UnLoad_OnCommand(CommandEventArgs e) { if (e.Mobile.AccessLevel >= DiskAccessLevel) { if (e.Arguments.Length >= 1) { // Spawner unload criteria (if any) string SpawnerPrefix = string.Empty; // Check if there is an argument provided (load criteria) if (e.Arguments.Length > 1) SpawnerPrefix = e.Arguments[1]; string filename = LocateFile(e.Arguments[0]); int processedmaps; int processedspawners; XmlUnLoadFromFile(filename, SpawnerPrefix, e.Mobile, out processedmaps, out processedspawners); } else e.Mobile.SendMessage("Usage: {0} ", e.Command); } else e.Mobile.SendMessage("You do not have rights to perform this command."); } [Usage("XmlImportMap ")] [Description("Loads spawner definitions from a .map file")] public static void XmlImportMap_OnCommand(CommandEventArgs e) { if (e.Mobile.AccessLevel >= DiskAccessLevel) { if (e.Arguments.Length >= 1) { string filename = e.Arguments[0]; int processedmaps; int processedspawners; XmlImportMap(filename, e.Mobile, out processedmaps, out processedspawners); } else e.Mobile.SendMessage("Usage: {0} ", e.Command); } else e.Mobile.SendMessage("You do not have rights to perform this command."); } public static void XmlImportMap(string filename, Mobile from, out int processedmaps, out int processedspawners) { processedmaps = 0; processedspawners = 0; int total_processed_maps = 0; int total_processed_spawners = 0; if (filename == null || filename.Length <= 0 || from == null || from.Deleted) return; // Check if the file exists if (File.Exists(filename)) { int spawnercount = 0; int badspawnercount = 0; int linenumber = 0; // default is no map override, use the map spec from each spawn line int overridemap = -1; double overridemintime = -1; double overridemaxtime = -1; bool newformat = false; try { // Create an instance of StreamReader to read from a file. // The using statement also closes the StreamReader. using (StreamReader sr = new StreamReader(filename)) { string line; // Read and display lines from the file until the end of // the file is reached. while ((line = sr.ReadLine()) != null) { // the old format of each .map line is * Dragon:Wyvern 5209 965 -40 2 2 10 50 30 1 // * typename:typename:... x y z map mindelay maxdelay homerange spawnrange maxcount // * | typename:typename:... |s1 |s2 |s3 |s4 |s5 | x | y | z | map | mindelay maxdelay homerange spawnrange spawnid maxcount | maxcount1 | maxcount2 | maxcount3 | maxcount4 | maxcount5 // where s1-5 are additional spawn type entries with their own maxcounts // the new format of each .map line is * |Dragon:Wyvern| spawns:spawns| | | | | 5209 | 965 | -40 | 2 | 2 | 10 | 50 | 30 | 1 linenumber++; // is this the new format? string[] args; if (line.IndexOf('|') >= 0) { args = line.Trim().Split('|'); newformat = true; } else { args = line.Trim().Split(' '); } // determine the format of this line and parse accordingly if (newformat) { ParseNewMapFormat(from, filename, line, args, linenumber, ref spawnercount, ref badspawnercount, ref overridemap, ref overridemintime, ref overridemaxtime); } else { ParseOldMapFormat(from, filename, line, args, linenumber, ref spawnercount, ref badspawnercount, ref overridemap, ref overridemintime, ref overridemaxtime); } } sr.Close(); } } catch (Exception e) { // Let the user know what went wrong. from.SendMessage("The file could not be read: {0}", e.Message); } from.SendMessage("Imported {0} spawners from {1}", spawnercount, filename); from.SendMessage("{0} bad spawners detected", badspawnercount); processedmaps = 1; processedspawners = spawnercount; } else // check to see if it is a directory if (Directory.Exists(filename)) { // if so then import all of the .map files in the directory string[] files = null; try { files = Directory.GetFiles(filename, "*.map"); } catch { } if (files != null && files.Length > 0) { from.SendMessage("Importing {0} .map files from directory {1}", files.Length, filename); foreach (string file in files) { XmlImportMap(file, from, out processedmaps, out processedspawners); total_processed_maps += processedmaps; total_processed_spawners += processedspawners; } } // recursively search subdirectories for more .map files string[] dirs = null; try { dirs = Directory.GetDirectories(filename); } catch { } if (dirs != null && dirs.Length > 0) { foreach (string dir in dirs) { XmlImportMap(dir, from, out processedmaps, out processedspawners); total_processed_maps += processedmaps; total_processed_spawners += processedspawners; } } from.SendMessage("Imported a total of {0} .map files and {2} spawners from directory {1}", total_processed_maps, filename, total_processed_spawners); processedmaps = total_processed_maps; processedspawners = total_processed_spawners; } else { from.SendMessage("{0} does not exist", filename); } } private static void ParseNewMapFormat(Mobile from, string filename, string line, string[] args, int linenumber, ref int spawnercount, ref int badspawnercount, ref int overridemap, ref double overridemintime, ref double overridemaxtime) { // format of each .map line is * Dragon:Wyvern 5209 965 -40 2 2 10 50 30 1 // * typename:typename:... x y z map mindelay maxdelay homerange spawnrange maxcount // or // * typename:typename:... x y z map mindelay maxdelay homerange spawnrange spawnid maxcount // ## are comments // overridemap mapnumber // map 0 is tram+fel // map 1 is fel // map 2 is tram // map 3 is ilsh // map 4 is mal // map 5 is tokuno // // * | typename:typename:... | | | | | | x | y | z | map | mindelay maxdelay homerange spawnrange spawnid maxcount1 | maxcount2 | maxcount2 | maxcount3 | maxcount4 | maxcount5 | maxcount6 // the new format of each .map line is * |Dragon:Wyvern| spawns:spawns| | | | | 5209 | 965 | -40 | 2 | 2 | 10 | 50 | 30 | 1 if (args == null || from == null) return; // look for the override keyword if (args.Length == 2 && (args[0].ToLower() == "overridemap")) { try { overridemap = int.Parse(args[1]); } catch { } } else if (args.Length == 2 && (args[0].ToLower() == "overridemintime")) { try { overridemintime = double.Parse(args[1]); } catch { } } else if (args.Length == 2 && (args[0].ToLower() == "overridemaxtime")) { try { overridemaxtime = double.Parse(args[1]); } catch { } } else // look for a spawn spec line if (args.Length > 0 && (args[0] == "*")) { bool badspawn = false; int x = 0; int y = 0; int z = 0; int map = 0; double mindelay = 0; double maxdelay = 0; int homerange = 0; int spawnrange = 0; string[][] typenames = new string[6][]; int[] maxcount = new int[6]; // parse the main args try { // get the list of spawns for (int k = 0; k < 6; k++) typenames[k] = args[k + 1].Split(':'); x = int.Parse(args[7]); y = int.Parse(args[8]); z = int.Parse(args[9]); map = int.Parse(args[10]); mindelay = double.Parse(args[11]); maxdelay = double.Parse(args[12]); homerange = int.Parse(args[13]); spawnrange = int.Parse(args[14]); int spawnid = int.Parse(args[15]); for (int k = 0; k < 6; k++) maxcount[k] = int.Parse(args[k + 16]); } catch { from.SendMessage("Parsing error at line {0}", linenumber); badspawn = true; } // compute the total number of spawns int totalspawns = 0; int totalmaxcount = 0; for (int k = 0; k < 6; k++) { if (typenames[k] == null) continue; for (int i = 0; i < typenames[k].Length; i++) { if (typenames[k][i] == null || typenames[k][i].Length == 0) continue; totalspawns++; } totalmaxcount += maxcount[k]; } // apply min/maxdelay overrides if (overridemintime != -1) { mindelay = overridemintime; } if (overridemaxtime != -1) { maxdelay = overridemaxtime; } if (mindelay > maxdelay) maxdelay = mindelay; if (!badspawn && totalspawns > 0) { // everything seems ok so go ahead and make the spawner // check for map override if (overridemap >= 0) map = overridemap; Map spawnmap = Map.Internal; switch (map) { case 0: spawnmap = Map.Felucca; // note it also does trammel break; case 1: spawnmap = Map.Felucca; break; case 2: spawnmap = Map.Trammel; break; case 3: spawnmap = Map.Ilshenar; break; case 4: spawnmap = Map.Malas; break; case 5: spawnmap = Map.Tokuno; break; } if (!IsValidMapLocation(x, y, spawnmap)) { // invalid so dont spawn it badspawnercount++; from.SendMessage("Invalid map/location at line {0}", linenumber); from.SendMessage("Bad spawn at line {1}: {0}", line, linenumber); return; } // allow it to make an xmlspawner instead // first add all of the creatures on the list SpawnObject[] so = new SpawnObject[totalspawns]; int count = 0; bool hasvendor = true; for (int k = 0; k < 6; k++) { if (typenames[k] == null) continue; for (int i = 0; i < typenames[k].Length; i++) { if (typenames[k][i] == null || typenames[k][i].Length == 0 || count > totalspawns) continue; so[count++] = new SpawnObject(typenames[k][i], maxcount[k]); // check the type to see if there are vendors on it Type type = SpawnerType.GetType(typenames[k][i]); // check for vendor-only spawners which get special spawnrange treatment if (type != null && (type != typeof(BaseVendor) && !type.IsSubclassOf(typeof(BaseVendor)))) { hasvendor = false; } } } // assign it a unique id Guid SpawnId = Guid.NewGuid(); // and give it a name based on the spawner count and file string spawnername = string.Format("{0}#{1}", Path.GetFileNameWithoutExtension(filename), spawnercount); // Create the new xml spawner XmlSpawner spawner = new XmlSpawner(SpawnId, x, y, 0, 0, spawnername, totalmaxcount, TimeSpan.FromMinutes(mindelay), TimeSpan.FromMinutes(maxdelay), TimeSpan.FromMinutes(0), -1, defaultTriggerSound, 1, 0, homerange, false, so, TimeSpan.FromMinutes(0), TimeSpan.FromMinutes(0), TimeSpan.FromMinutes(0), TimeSpan.FromMinutes(0), null, null, null, null, null, null, null, null, null, 1, null, false, defTODMode, defKillReset, false, -1, null, false, false, false, null, TimeSpan.FromHours(0), null, false, null); spawner.SpawnRange = hasvendor ? 0 : spawnrange; spawner.m_PlayerCreated = true; spawner.MoveToWorld(new Point3D(x, y, z), spawnmap); if (spawner.Map == Map.Internal) { badspawnercount++; spawner.Delete(); from.SendMessage("Invalid map at line {0}", linenumber); from.SendMessage("Bad spawn at line {1}: {0}", line, linenumber); return; } spawnercount++; // handle the special case of map 0 that also needs to do trammel if (map == 0) { spawnmap = Map.Trammel; // assign it a unique id SpawnId = Guid.NewGuid(); // Create the new xml spawner spawner = new XmlSpawner(SpawnId, x, y, 0, 0, spawnername, totalmaxcount, TimeSpan.FromMinutes(mindelay), TimeSpan.FromMinutes(maxdelay), TimeSpan.FromMinutes(0), -1, defaultTriggerSound, 1, 0, homerange, false, so, TimeSpan.FromMinutes(0), TimeSpan.FromMinutes(0), TimeSpan.FromMinutes(0), TimeSpan.FromMinutes(0), null, null, null, null, null, null, null, null, null, 1, null, false, defTODMode, defKillReset, false, -1, null, false, false, false, null, TimeSpan.FromHours(0), null, false, null) { SpawnRange = spawnrange, m_PlayerCreated = true }; spawner.MoveToWorld(new Point3D(x, y, z), spawnmap); if (spawner.Map == Map.Internal) { badspawnercount++; spawner.Delete(); from.SendMessage("Bad spawn at line {1}: {0}", line, linenumber); return; } spawnercount++; } } else { badspawnercount++; from.SendMessage("Bad spawn at line {1}: {0}", line, linenumber); } } } private static void ParseOldMapFormat(Mobile from, string filename, string line, string[] args, int linenumber, ref int spawnercount, ref int badspawnercount, ref int overridemap, ref double overridemintime, ref double overridemaxtime) { // format of each .map line is * Dragon:Wyvern 5209 965 -40 2 2 10 50 30 1 // * typename:typename:... x y z map mindelay maxdelay homerange spawnrange maxcount // or // * typename:typename:... x y z map mindelay maxdelay homerange spawnrange spawnid maxcount // ## are comments // overridemap mapnumber // map 0 is tram+fel // map 1 is fel // map 2 is tram // map 3 is ilsh // map 4 is mal // map 5 is tokuno // // * | typename:typename:... | | | | | | x | y | z | map | mindelay maxdelay homerange spawnrange spawnid maxcount | maxcount2 | maxcount2 | maxcount3 | maxcount4 | maxcount5 // the new format of each .map line is * |Dragon:Wyvern| spawns:spawns| | | | | 5209 | 965 | -40 | 2 | 2 | 10 | 50 | 30 | 1 if (args == null || from == null) return; // look for the override keyword if (args.Length == 2 && (args[0].ToLower() == "overridemap")) { try { overridemap = int.Parse(args[1]); } catch { } } else if (args.Length == 2 && (args[0].ToLower() == "overridemintime")) { try { overridemintime = double.Parse(args[1]); } catch { } } else if (args.Length == 2 && (args[0].ToLower() == "overridemaxtime")) { try { overridemaxtime = double.Parse(args[1]); } catch { } } else // look for a spawn spec line if (args.Length > 0 && (args[0] == "*")) { bool badspawn = false; int x = 0; int y = 0; int z = 0; int map = 0; double mindelay = 0; double maxdelay = 0; int homerange = 0; int spawnrange = 0; int maxcount = 0; string[] typenames = null; if (args.Length != 11 && args.Length != 12) { badspawn = true; from.SendMessage("Invalid arg count {1} at line {0}", linenumber, args.Length); } else { // get the list of spawns typenames = args[1].Split(':'); // parse the rest of the args if (args.Length == 11) { try { x = int.Parse(args[2]); y = int.Parse(args[3]); z = int.Parse(args[4]); map = int.Parse(args[5]); mindelay = double.Parse(args[6]); maxdelay = double.Parse(args[7]); homerange = int.Parse(args[8]); spawnrange = int.Parse(args[9]); maxcount = int.Parse(args[10]); } catch { from.SendMessage("Parsing error at line {0}", linenumber); badspawn = true; } } else if (args.Length == 12) { try { x = int.Parse(args[2]); y = int.Parse(args[3]); z = int.Parse(args[4]); map = int.Parse(args[5]); mindelay = double.Parse(args[6]); maxdelay = double.Parse(args[7]); homerange = int.Parse(args[8]); spawnrange = int.Parse(args[9]); int spawnid = int.Parse(args[10]); maxcount = int.Parse(args[11]); } catch { from.SendMessage("Parsing error at line {0}", linenumber); badspawn = true; } } } // apply mi/maxdelay overrides if (overridemintime != -1) { mindelay = overridemintime; } if (overridemaxtime != -1) { maxdelay = overridemaxtime; } if (mindelay > maxdelay) maxdelay = mindelay; if (!badspawn && typenames.Length > 0) { // everything seems ok so go ahead and make the spawner // check for map override if (overridemap >= 0) map = overridemap; Map spawnmap = Map.Internal; switch (map) { case 0: spawnmap = Map.Felucca; // note it also does trammel break; case 1: spawnmap = Map.Felucca; break; case 2: spawnmap = Map.Trammel; break; case 3: spawnmap = Map.Ilshenar; break; case 4: spawnmap = Map.Malas; break; case 5: spawnmap = Map.Tokuno; break; } if (!IsValidMapLocation(x, y, spawnmap)) { // invalid so dont spawn it badspawnercount++; from.SendMessage("Invalid map/location at line {0}", linenumber); from.SendMessage("Bad spawn at line {1}: {0}", line, linenumber); return; } // allow it to make an xmlspawner instead // first add all of the creatures on the list SpawnObject[] so = new SpawnObject[typenames.Length]; bool hasvendor = true; for (int i = 0; i < typenames.Length; i++) { so[i] = new SpawnObject(typenames[i], maxcount); // check the type to see if there are vendors on it Type type = SpawnerType.GetType(typenames[i]); // check for vendor-only spawners which get special spawnrange treatment if (type != null && (type != typeof(BaseVendor) && !type.IsSubclassOf(typeof(BaseVendor)))) { hasvendor = false; } } // assign it a unique id Guid SpawnId = Guid.NewGuid(); // and give it a name based on the spawner count and file string spawnername = string.Format("{0}#{1}", Path.GetFileNameWithoutExtension(filename), spawnercount); // Create the new xml spawner XmlSpawner spawner = new XmlSpawner(SpawnId, x, y, 0, 0, spawnername, maxcount, TimeSpan.FromMinutes(mindelay), TimeSpan.FromMinutes(maxdelay), TimeSpan.FromMinutes(0), -1, defaultTriggerSound, 1, 0, homerange, false, so, TimeSpan.FromMinutes(0), TimeSpan.FromMinutes(0), TimeSpan.FromMinutes(0), TimeSpan.FromMinutes(0), null, null, null, null, null, null, null, null, null, 1, null, false, defTODMode, defKillReset, false, -1, null, false, false, false, null, TimeSpan.FromHours(0), null, false, null); spawner.SpawnRange = hasvendor ? 0 : spawnrange; spawner.m_PlayerCreated = true; spawner.MoveToWorld(new Point3D(x, y, z), spawnmap); if (spawner.Map == Map.Internal) { badspawnercount++; spawner.Delete(); from.SendMessage("Invalid map at line {0}", linenumber); from.SendMessage("Bad spawn at line {1}: {0}", line, linenumber); return; } spawnercount++; // handle the special case of map 0 that also needs to do trammel if (map == 0) { spawnmap = Map.Trammel; // assign it a unique id SpawnId = Guid.NewGuid(); // Create the new xml spawner spawner = new XmlSpawner(SpawnId, x, y, 0, 0, spawnername, maxcount, TimeSpan.FromMinutes(mindelay), TimeSpan.FromMinutes(maxdelay), TimeSpan.FromMinutes(0), -1, defaultTriggerSound, 1, 0, homerange, false, so, TimeSpan.FromMinutes(0), TimeSpan.FromMinutes(0), TimeSpan.FromMinutes(0), TimeSpan.FromMinutes(0), null, null, null, null, null, null, null, null, null, 1, null, false, defTODMode, defKillReset, false, -1, null, false, false, false, null, TimeSpan.FromHours(0), null, false, null) { SpawnRange = spawnrange, m_PlayerCreated = true }; spawner.MoveToWorld(new Point3D(x, y, z), spawnmap); if (spawner.Map == Map.Internal) { badspawnercount++; spawner.Delete(); from.SendMessage("Bad spawn at line {1}: {0}", line, linenumber); return; } spawnercount++; } } else { badspawnercount++; from.SendMessage("Bad spawn at line {1}: {0}", line, linenumber); } } } [Usage("XmlImportSpawners filename")] [Description("Loads xml files created by Sno's xml exporter as xmlspawners.")] public static void XmlImportSpawners_OnCommand(CommandEventArgs e) { if (e.Arguments.Length >= 1) { string filename = e.GetString(0); string filePath = Path.Combine("Saves/Spawners", filename); if (File.Exists(filePath)) { XmlDocument doc = new XmlDocument(); try { doc.Load(filePath); } catch { e.Mobile.SendMessage("unable to load file {0}.", filePath); return; } XmlElement root = doc["spawners"]; int successes = 0, failures = 0; if (root?.GetElementsByTagName("spawner") != null) { foreach (XmlElement spawner in root.GetElementsByTagName("spawner")) { try { ImportSpawner(spawner, e.Mobile); successes++; } catch (Exception ex) { e.Mobile.SendMessage(33, "{0} {1}", ex.Message, spawner.InnerText); failures++; } } } e.Mobile.SendMessage("{0} spawners loaded successfully from {1}, {2} failures.", successes, filePath, failures); } else e.Mobile.SendMessage("File {0} does not exist.", filePath); } else e.Mobile.SendMessage("Usage: [XmlImportSpawners "); } private static string GetText(XmlElement node, string defaultValue) { if (node == null) return defaultValue; return node.InnerText; } private static void ImportSpawner(XmlElement node, Mobile from) { int count = int.Parse(GetText(node["count"], "1")); int homeRange = int.Parse(GetText(node["homerange"], "4")); int walkingRange = int.Parse(GetText(node["walkingrange"], "-1")); // width of the spawning area int spawnwidth = homeRange * 2; if (walkingRange >= 0) spawnwidth = walkingRange * 2; int team = int.Parse(GetText(node["team"], "0")); bool group = bool.Parse(GetText(node["group"], "False")); TimeSpan maxDelay = TimeSpan.Parse(GetText(node["maxdelay"], "10:00")); TimeSpan minDelay = TimeSpan.Parse(GetText(node["mindelay"], "05:00")); List creaturesName = LoadCreaturesName(node["creaturesname"]); string name = GetText(node["name"], "Spawner"); Point3D location = Point3D.Parse(GetText(node["location"], "Error")); Map map = Map.Parse(GetText(node["map"], "Error")); // allow it to make an xmlspawner instead // first add all of the creatures on the list SpawnObject[] so = new SpawnObject[creaturesName.Count]; bool hasvendor = false; for (int i = 0; i < creaturesName.Count; i++) { so[i] = new SpawnObject(creaturesName[i], count); // check the type to see if there are vendors on it Type type = SpawnerType.GetType(creaturesName[i]); // if it has basevendors on it or invalid types, then skip it if (type != null && (type == typeof(BaseVendor) || type.IsSubclassOf(typeof(BaseVendor)))) { hasvendor = true; } } // assign it a unique id Guid SpawnId = Guid.NewGuid(); // Create the new xml spawner XmlSpawner spawner = new XmlSpawner(SpawnId, location.X, location.Y, spawnwidth, spawnwidth, name, count, minDelay, maxDelay, TimeSpan.FromMinutes(0), -1, defaultTriggerSound, 1, team, homeRange, false, so, TimeSpan.FromMinutes(0), TimeSpan.FromMinutes(0), TimeSpan.FromMinutes(0), TimeSpan.FromMinutes(0), null, null, null, null, null, null, null, null, null, 1, null, group, defTODMode, defKillReset, false, -1, null, false, false, false, null, defDespawnTime, null, false, null); spawner.SpawnRange = hasvendor ? 0 : homeRange; spawner.m_PlayerCreated = true; spawner.MoveToWorld(location, map); if (!IsValidMapLocation(location, spawner.Map)) { spawner.Delete(); throw new Exception("Invalid spawner location."); } } private static List LoadCreaturesName(XmlElement node) { List names = new List(); if (node != null) { foreach (XmlElement ele in node.GetElementsByTagName("creaturename")) { if (ele != null) names.Add(ele.InnerText); } } return names; } [Usage("XmlImportMSF filename")] [Description("Loads msf files created by Morxeton's megaspawner as xmlspawners.")] public static void XmlImportMSF_OnCommand(CommandEventArgs e) { if (e.Arguments.Length >= 1) { /* // I'm not sure what the default location for .msf files is string filename = e.GetString( 0 ); string filePath = Path.Combine( "Data/Megaspawner", filename ); */ string filePath = e.GetString(0); if (File.Exists(filePath)) { XmlDocument doc = new XmlDocument(); doc.Load(filePath); XmlElement root = doc["MegaSpawners"]; if (root != null) { int successes = 0, failures = 0; foreach (XmlElement spawner in root.GetElementsByTagName("MegaSpawner")) { try { ImportMegaSpawner(e.Mobile, spawner); successes++; } catch (Exception ex) { e.Mobile.SendMessage(33, "{0} {1}", ex.Message, spawner.InnerText); failures++; } } e.Mobile.SendMessage("{0} megaspawners loaded successfully from {1}, {2} failures.", successes, filePath, failures); } else { e.Mobile.SendMessage("Invalid .msf file. No MegaSpawners node found"); } } else e.Mobile.SendMessage("File {0} does not exist.", filePath); } else e.Mobile.SendMessage("Usage: [XmlImportMSF "); } private static void ImportMegaSpawner(Mobile from, XmlElement node) { string name = GetText(node["Name"], "MegaSpawner"); bool running = bool.Parse(GetText(node["Active"], "True")); Point3D location = Point3D.Parse(GetText(node["Location"], "Error")); Map map = Map.Parse(GetText(node["Map"], "Error")); int team = 0; bool group = false; int maxcount = 0; // default maxcount of the spawner int homeRange = 4; // default homerange int spawnRange = 4; // default homerange TimeSpan maxDelay = TimeSpan.FromMinutes(10); TimeSpan minDelay = TimeSpan.FromMinutes(5); XmlElement listnode = node["EntryLists"]; int nentries = 0; SpawnObject[] so = null; if (listnode != null) { // get the number of entries if (listnode.HasAttributes) { XmlAttributeCollection attr = listnode.Attributes; nentries = int.Parse(attr.GetNamedItem("count").Value); } if (nentries > 0) { so = new SpawnObject[nentries]; int entrycount = 0; bool diff = false; foreach (XmlElement entrynode in listnode.GetElementsByTagName("EntryList")) { // go through each entry and add a spawn object for it if (entrynode != null) { if (entrycount == 0) { // get the spawner defaults from the first entry // dont handle the individually specified entry attributes group = bool.Parse(GetText(entrynode["GroupSpawn"], "False")); maxDelay = TimeSpan.FromSeconds(int.Parse(GetText(entrynode["MaxDelay"], "10:00"))); minDelay = TimeSpan.FromSeconds(int.Parse(GetText(entrynode["MinDelay"], "05:00"))); homeRange = int.Parse(GetText(entrynode["WalkRange"], "10")); spawnRange = int.Parse(GetText(entrynode["SpawnRange"], "4")); } else { // just check for consistency with other entries and report discrepancies if (group != bool.Parse(GetText(entrynode["GroupSpawn"], "False"))) { diff = true; // log it try { using (StreamWriter op = new StreamWriter("badimport.log", true)) { op.WriteLine("MSFimport : individual group entry difference: {0} vs {1}", GetText(entrynode["GroupSpawn"], "False"), group); } } catch { } } if (minDelay != TimeSpan.FromSeconds(int.Parse(GetText(entrynode["MinDelay"], "05:00")))) { diff = true; // log it try { using (StreamWriter op = new StreamWriter("badimport.log", true)) { op.WriteLine("MSFimport : individual mindelay entry difference: {0} vs {1}", GetText(entrynode["MinDelay"], "05:00"), minDelay); } } catch { } } if (maxDelay != TimeSpan.FromSeconds(int.Parse(GetText(entrynode["MaxDelay"], "10:00")))) { diff = true; // log it try { using (StreamWriter op = new StreamWriter("badimport.log", true)) { op.WriteLine("MSFimport : individual maxdelay entry difference: {0} vs {1}", GetText(entrynode["MaxDelay"], "10:00"), maxDelay); } } catch { } } if (homeRange != int.Parse(GetText(entrynode["WalkRange"], "10"))) { diff = true; // log it try { using (StreamWriter op = new StreamWriter("badimport.log", true)) { op.WriteLine("MSFimport : individual homerange entry difference: {0} vs {1}", GetText(entrynode["WalkRange"], "10"), homeRange); } } catch { } } if (spawnRange != int.Parse(GetText(entrynode["SpawnRange"], "4"))) { diff = true; // log it try { using (StreamWriter op = new StreamWriter("badimport.log", true)) { op.WriteLine("MSFimport : individual spawnrange entry difference: {0} vs {1}", GetText(entrynode["SpawnRange"], "4"), spawnRange); } } catch { } } } // these apply to individual entries int amount = int.Parse(GetText(entrynode["Amount"], "1")); string entryname = GetText(entrynode["EntryType"], ""); // keep track of the maxcount for the spawner by adding the individual amounts maxcount += amount; // add the creature entry so[entrycount] = new SpawnObject(entryname, amount); entrycount++; if (entrycount > nentries) { // log it try { using (StreamWriter op = new StreamWriter("badimport.log", true)) { op.WriteLine("{0} MSFImport Error; inconsistent entry count {1} {2}", DateTime.UtcNow, location, map); op.WriteLine(); } } catch { } from.SendMessage("Inconsistent entry count detected at {0} {1}.", location, map); break; } } } if (diff) { from.SendMessage("Individual entry setting detected at {0} {1}.", location, map); // log it try { using (StreamWriter op = new StreamWriter("badimport.log", true)) { op.WriteLine("{0} MSFImport: Individual entry setting differences listed above from spawner at {1} {2}", DateTime.UtcNow, location, map); op.WriteLine(); } } catch { } } } } // assign it a unique id Guid SpawnId = Guid.NewGuid(); // Create the new xml spawner XmlSpawner spawner = new XmlSpawner(SpawnId, location.X, location.Y, 0, 0, name, maxcount, minDelay, maxDelay, TimeSpan.FromMinutes(0), -1, defaultTriggerSound, 1, team, homeRange, false, so, TimeSpan.FromMinutes(0), TimeSpan.FromMinutes(0), TimeSpan.FromMinutes(0), TimeSpan.FromMinutes(0), null, null, null, null, null, null, null, null, null, 1, null, group, defTODMode, defKillReset, false, -1, null, false, false, false, null, defDespawnTime, null, false, null) { SpawnRange = spawnRange, m_PlayerCreated = true }; // Try to find a valid Z height if required (Z == -999) if (location.Z == -999) { int NewZ = map.GetAverageZ(location.X, location.Y); if (map.CanFit(location.X, location.Y, NewZ, SpawnFitSize) == false) { for (int x = 1; x <= 39; x++) { if (map.CanFit(location.X, location.Y, NewZ + x, SpawnFitSize)) { NewZ += x; break; } } } location.Z = NewZ; } spawner.MoveToWorld(location, map); if (!IsValidMapLocation(location, spawner.Map)) { spawner.Delete(); throw new Exception("Invalid spawner location."); } } public static void XmlLoadFromFile(string filename, string SpawnerPrefix, Mobile from, Point3D fromloc, Map frommap, bool loadrelative, int maxrange, bool loadnew, out int processedmaps, out int processedspawners) { processedmaps = 0; processedspawners = 0; int total_processed_maps = 0; int total_processed_spawners = 0; if (filename == null || filename.Length <= 0) return; // Check if the file exists if (File.Exists(filename)) { FileStream fs = null; try { fs = File.Open(filename, FileMode.Open, FileAccess.Read); } catch { } if (fs == null) { if (from != null) from.SendMessage("Unable to open {0} for loading", filename); return; } // load the file XmlLoadFromStream(fs, filename, SpawnerPrefix, from, fromloc, frommap, loadrelative, maxrange, loadnew, out processedmaps, out processedspawners); } else if (Directory.Exists(filename)) { // if so then load all of the .xml files in the directory string[] files = null; try { files = Directory.GetFiles(filename, "*.xml"); } catch { } if (files != null && files.Length > 0) { if (from != null) from.SendMessage("Loading {0} .xml files from directory {1}", files.Length, filename); foreach (string file in files) { XmlLoadFromFile(file, SpawnerPrefix, from, fromloc, frommap, loadrelative, maxrange, loadnew, out processedmaps, out processedspawners); total_processed_maps += processedmaps; total_processed_spawners += processedspawners; } } // recursively search subdirectories for more .xml files string[] dirs = null; try { dirs = Directory.GetDirectories(filename); } catch { } if (dirs != null && dirs.Length > 0) { foreach (string dir in dirs) { XmlLoadFromFile(dir, SpawnerPrefix, from, fromloc, frommap, loadrelative, maxrange, loadnew, out processedmaps, out processedspawners); total_processed_maps += processedmaps; total_processed_spawners += processedspawners; } } if (from != null) from.SendMessage("Loaded a total of {0} .xml files and {2} spawners from directory {1}", total_processed_maps, filename, total_processed_spawners); processedmaps = total_processed_maps; processedspawners = total_processed_spawners; } else { if (from != null) from.SendMessage("{0} does not exist", filename); } } public static void XmlLoadFromFile(string filename, string SpawnerPrefix, Mobile from, bool loadrelative, int maxrange, bool loadnew, out int processedmaps, out int processedspawners) { processedmaps = 0; processedspawners = 0; if (from == null) return; XmlLoadFromFile(filename, SpawnerPrefix, from, from.Location, from.Map, loadrelative, maxrange, loadnew, out processedmaps, out processedspawners); } public static void XmlLoadFromFile(string filename, string SpawnerPrefix, Point3D fromloc, Map frommap, bool loadrelative, int maxrange, bool loadnew, out int processedmaps, out int processedspawners) { XmlLoadFromFile(filename, SpawnerPrefix, null, fromloc, frommap, loadrelative, maxrange, loadnew, out processedmaps, out processedspawners); } public static void XmlLoadFromFile(string filename, string SpawnerPrefix, bool loadnew, out int processedmaps, out int processedspawners) { XmlLoadFromFile(filename, SpawnerPrefix, null, Point3D.Zero, Map.Internal, false, 0, loadnew, out processedmaps, out processedspawners); } public static void XmlLoadFromStream(Stream fs, string filename, string SpawnerPrefix, Mobile from, Point3D fromloc, Map frommap, bool loadrelative, int maxrange, bool loadnew, out int processedmaps, out int processedspawners) { XmlLoadFromStream(fs, filename, SpawnerPrefix, from, fromloc, frommap, loadrelative, maxrange, loadnew, out processedmaps, out processedspawners, false); } public static void XmlLoadFromStream(Stream fs, string filename, string SpawnerPrefix, Mobile from, Point3D fromloc, Map frommap, bool loadrelative, int maxrange, bool loadnew, out int processedmaps, out int processedspawners, bool verbose) { processedmaps = 0; processedspawners = 0; if (fs == null) return; // assign an id that will be used to distinguish the newly loaded spawners by appending it to their name Guid newloadid = Guid.NewGuid(); int TotalCount = 0; int TrammelCount = 0; int FeluccaCount = 0; int IlshenarCount = 0; int MalasCount = 0; int TokunoCount = 0; int OtherCount = 0; bool questionable_spawner = false; bool bad_spawner = false; int badcount = 0; int questionablecount = 0; int failedobjectitemcount = 0; int failedsetitemcount = 0; int relativex = -1; int relativey = -1; int relativez = 0; Map relativemap = null; if (from != null) from.SendMessage(string.Format("Loading {0} objects{1} from file {2}.", "XmlSpawner", (!string.IsNullOrEmpty(SpawnerPrefix) ? " beginning with " + SpawnerPrefix : string.Empty), filename)); // Create the data set DataSet ds = new DataSet(SpawnDataSetName); // Read in the file bool fileerror = false; try { ds.ReadXml(fs); } catch { if (from != null) from.SendMessage(33, "Error reading xml file {0}", filename); fileerror = true; } // close the file fs.Close(); if (fileerror) return; // Check that at least a single table was loaded if (ds.Tables.Count > 0) { // Add each spawn point to the current map if (ds.Tables[SpawnTablePointName] != null && ds.Tables[SpawnTablePointName].Rows.Count > 0) foreach (DataRow dr in ds.Tables[SpawnTablePointName].Rows) { // load in the spawner info. Certain fields are required and therefore cannot be ignored // the exception handler for those will flag bad_spawner and the result will be logged // Each row makes up a single spawner string SpawnName = "Spawner"; try { SpawnName = (string)dr["Name"]; } catch { questionable_spawner = true; } if (loadnew) { // append the new id to the name SpawnName = string.Format("{0}-{1}", SpawnName, newloadid); } // Check if there is any spawner name criteria specified on the load if ((SpawnerPrefix == null) || (SpawnerPrefix.Length == 0) || SpawnName.StartsWith(SpawnerPrefix)) { // Try load the GUID (might not work so create a new GUID) Guid SpawnId = Guid.NewGuid(); if (!loadnew) { try { SpawnId = new Guid((string)dr["UniqueId"]); } catch { } } else { // change the dataset guid to the newly created one when new loading try { dr["UniqueId"] = SpawnId; } catch { Console.WriteLine("unable to set UniqueId"); } } int SpawnCentreX = fromloc.X; int SpawnCentreY = fromloc.Y; int SpawnCentreZ = fromloc.Z; try { SpawnCentreX = int.Parse((string)dr["CentreX"]); } catch { bad_spawner = true; } try { SpawnCentreY = int.Parse((string)dr["CentreY"]); } catch { bad_spawner = true; } try { SpawnCentreZ = int.Parse((string)dr["CentreZ"]); } catch { bad_spawner = true; } int SpawnX = SpawnCentreX; int SpawnY = SpawnCentreY; int SpawnWidth = 0; int SpawnHeight = 0; try { SpawnX = int.Parse((string)dr["X"]); } catch { questionable_spawner = true; } try { SpawnY = int.Parse((string)dr["Y"]); } catch { questionable_spawner = true; } try { SpawnWidth = int.Parse((string)dr["Width"]); } catch { questionable_spawner = true; } try { SpawnHeight = int.Parse((string)dr["Height"]); } catch { questionable_spawner = true; } // Try load the InContainer (default to false) bool InContainer = false; int ContainerX = 0; int ContainerY = 0; int ContainerZ = 0; try { InContainer = bool.Parse((string)dr["InContainer"]); } catch { } if (InContainer) { try { ContainerX = int.Parse((string)dr["ContainerX"]); } catch { } try { ContainerY = int.Parse((string)dr["ContainerY"]); } catch { } try { ContainerZ = int.Parse((string)dr["ContainerZ"]); } catch { } } // Get the map (default to the mobiles map) if the relative distance is too great, then use the defined map Map SpawnMap = frommap; string XmlMapName = frommap.Name; //if(!loadrelative && !loadnew) { // Try to get the "map" field, but in case it doesn't exist, catch and discard the exception try { XmlMapName = (string)dr["Map"]; } catch { questionable_spawner = true; } // Convert the xml map value to a real map object if (string.Compare(XmlMapName, Map.Trammel.Name, true) == 0 || XmlMapName == "Trammel") { SpawnMap = Map.Trammel; TrammelCount++; } else if (string.Compare(XmlMapName, Map.Felucca.Name, true) == 0 || XmlMapName == "Felucca") { SpawnMap = Map.Felucca; FeluccaCount++; } else if (string.Compare(XmlMapName, Map.Ilshenar.Name, true) == 0 || XmlMapName == "Ilshenar") { SpawnMap = Map.Ilshenar; IlshenarCount++; } else if (string.Compare(XmlMapName, Map.Malas.Name, true) == 0 || XmlMapName == "Malas") { SpawnMap = Map.Malas; MalasCount++; } else if (string.Compare(XmlMapName, Map.Tokuno.Name, true) == 0 || XmlMapName == "Tokuno") { SpawnMap = Map.Tokuno; TokunoCount++; } else { try { SpawnMap = Map.Parse(XmlMapName); } catch { } OtherCount++; } } // test to see whether the distance between the relative center point and the spawner is too great. If so then dont do relative if (relativex == -1 && relativey == -1) { // the first xml entry in the file will determine the origin relativex = SpawnCentreX; relativey = SpawnCentreY; relativez = SpawnCentreZ; // and also the relative map to relocate from relativemap = SpawnMap; } int SpawnRelZ = 0; int OrigZ = SpawnCentreZ; if (loadrelative && (Math.Abs(relativex - SpawnCentreX) <= maxrange) && (Math.Abs(relativey - SpawnCentreY) <= maxrange) && (SpawnMap == relativemap)) { // its within range so shift it SpawnCentreX -= relativex - fromloc.X; SpawnCentreY -= relativey - fromloc.Y; SpawnX -= relativex - fromloc.X; SpawnY -= relativey - fromloc.Y; // force it to autosearch for Z when it places it but hold onto relative Z info just in case it can be placed there SpawnRelZ = relativez - fromloc.Z; SpawnCentreZ = short.MinValue; } // if relative loading has been specified, see if the loaded map is the same as the relativemap and relocate. // if it doesnt match then just leave it if (loadrelative && (relativemap == SpawnMap)) { SpawnMap = frommap; } if (SpawnMap == Map.Internal) bad_spawner = true; // Try load the IsRelativeHomeRange (default to true) bool SpawnIsRelativeHomeRange = true; try { SpawnIsRelativeHomeRange = bool.Parse((string)dr["IsHomeRangeRelative"]); } catch { } int SpawnHomeRange = 5; try { SpawnHomeRange = int.Parse((string)dr["Range"]); } catch { questionable_spawner = true; } int SpawnMaxCount = 1; try { SpawnMaxCount = int.Parse((string)dr["MaxCount"]); } catch { questionable_spawner = true; } //deal with double format for delay. default is the old minute format bool delay_in_sec = false; try { delay_in_sec = bool.Parse((string)dr["DelayInSec"]); } catch { } TimeSpan SpawnMinDelay = TimeSpan.FromMinutes(5); TimeSpan SpawnMaxDelay = TimeSpan.FromMinutes(10); if (delay_in_sec) { try { SpawnMinDelay = TimeSpan.FromSeconds(int.Parse((string)dr["MinDelay"])); } catch { } try { SpawnMaxDelay = TimeSpan.FromSeconds(int.Parse((string)dr["MaxDelay"])); } catch { } } else { try { SpawnMinDelay = TimeSpan.FromMinutes(int.Parse((string)dr["MinDelay"])); } catch { } try { SpawnMaxDelay = TimeSpan.FromMinutes(int.Parse((string)dr["MaxDelay"])); } catch { } } TimeSpan SpawnMinRefractory = TimeSpan.FromMinutes(0); try { SpawnMinRefractory = TimeSpan.FromMinutes(double.Parse((string)dr["MinRefractory"])); } catch { } TimeSpan SpawnMaxRefractory = TimeSpan.FromMinutes(0); try { SpawnMaxRefractory = TimeSpan.FromMinutes(double.Parse((string)dr["MaxRefractory"])); } catch { } TimeSpan SpawnTODStart = TimeSpan.FromMinutes(0); try { SpawnTODStart = TimeSpan.FromMinutes(double.Parse((string)dr["TODStart"])); } catch { } TimeSpan SpawnTODEnd = TimeSpan.FromMinutes(0); try { SpawnTODEnd = TimeSpan.FromMinutes(double.Parse((string)dr["TODEnd"])); } catch { } int todmode = (int)TODModeType.Realtime; TODModeType SpawnTODMode = TODModeType.Realtime; try { todmode = int.Parse((string)dr["TODMode"]); } catch { } switch (todmode) { case (int)TODModeType.Gametime: SpawnTODMode = TODModeType.Gametime; break; case (int)TODModeType.Realtime: SpawnTODMode = TODModeType.Realtime; break; } int SpawnKillReset = defKillReset; try { SpawnKillReset = int.Parse((string)dr["KillReset"]); } catch { } string SpawnProximityMessage = null; // proximity message try { SpawnProximityMessage = (string)dr["ProximityTriggerMessage"]; } catch { } string SpawnItemTriggerName = null; try { SpawnItemTriggerName = (string)dr["ItemTriggerName"]; } catch { } string SpawnNoItemTriggerName = null; try { SpawnNoItemTriggerName = (string)dr["NoItemTriggerName"]; } catch { } string SpawnSpeechTrigger = null; try { SpawnSpeechTrigger = (string)dr["SpeechTrigger"]; } catch { } string SpawnSkillTrigger = null; try { SpawnSkillTrigger = (string)dr["SkillTrigger"]; } catch { } string SpawnMobTriggerName = null; try { SpawnMobTriggerName = (string)dr["MobTriggerName"]; } catch { } string SpawnMobPropertyName = null; try { SpawnMobPropertyName = (string)dr["MobPropertyName"]; } catch { } string SpawnPlayerPropertyName = null; try { SpawnPlayerPropertyName = (string)dr["PlayerPropertyName"]; } catch { } double SpawnTriggerProbability = 1; try { SpawnTriggerProbability = double.Parse((string)dr["TriggerProbability"]); } catch { } int SpawnSequentialSpawning = -1; try { SpawnSequentialSpawning = int.Parse((string)dr["SequentialSpawning"]); } catch { } string SpawnRegionName = null; try { SpawnRegionName = (string)dr["RegionName"]; } catch { } string SpawnConfigFile = null; try { SpawnConfigFile = (string)dr["ConfigFile"]; } catch { } bool SpawnAllowGhost = false; try { SpawnAllowGhost = bool.Parse((string)dr["AllowGhostTriggering"]); } catch { } bool SpawnAllowNPC = false; try { SpawnAllowNPC = bool.Parse((string)dr["AllowNPCTriggering"]); } catch { } bool SpawnSpawnOnTrigger = false; try { SpawnSpawnOnTrigger = bool.Parse((string)dr["SpawnOnTrigger"]); } catch { } bool SpawnSmartSpawning = false; try { SpawnSmartSpawning = bool.Parse((string)dr["SmartSpawning"]); } catch { } bool TickReset = false; try { TickReset = bool.Parse((string)dr["TickReset"]); } catch { } string SpawnObjectPropertyName = null; try { SpawnObjectPropertyName = (string)dr["ObjectPropertyName"]; } catch { } // we will assign this during the self-reference resolution pass Item SpawnSetPropertyItem = null; // we will assign this during the self-reference resolution pass Item SpawnObjectPropertyItem = null; // read the duration parameter from the xml file // but older files wont have it so deal with that condition and set it to the default of "0", i.e. infinite duration // Try to get the "Duration" field, but in case it doesn't exist, catch and discard the exception TimeSpan SpawnDuration = TimeSpan.FromMinutes(0); try { SpawnDuration = TimeSpan.FromMinutes(double.Parse((string)dr["Duration"])); } catch { } TimeSpan SpawnDespawnTime = TimeSpan.FromHours(0); try { SpawnDespawnTime = TimeSpan.FromHours(double.Parse((string)dr["DespawnTime"])); } catch { } int SpawnProximityRange = -1; // Try to get the "ProximityRange" field, but in case it doesn't exist, catch and discard the exception try { SpawnProximityRange = int.Parse((string)dr["ProximityRange"]); } catch { } int SpawnProximityTriggerSound = 0; // Try to get the "ProximityTriggerSound" field, but in case it doesn't exist, catch and discard the exception try { SpawnProximityTriggerSound = int.Parse((string)dr["ProximityTriggerSound"]); } catch { } int SpawnAmount = 1; try { SpawnAmount = int.Parse((string)dr["Amount"]); } catch { } bool SpawnExternalTriggering = false; try { SpawnExternalTriggering = bool.Parse((string)dr["ExternalTriggering"]); } catch { } string waypointstr = null; try { waypointstr = (string)dr["Waypoint"]; } catch { } WayPoint SpawnWaypoint = GetWaypoint(waypointstr); int SpawnTeam = 0; try { SpawnTeam = int.Parse((string)dr["Team"]); } catch { questionable_spawner = true; } bool SpawnIsGroup = false; try { SpawnIsGroup = bool.Parse((string)dr["IsGroup"]); } catch { questionable_spawner = true; } bool SpawnIsRunning = false; try { SpawnIsRunning = bool.Parse((string)dr["IsRunning"]); } catch { questionable_spawner = true; } // try loading the new spawn specifications first SpawnObject[] Spawns = new SpawnObject[0]; bool havenew = true; try { Spawns = SpawnObject.LoadSpawnObjectsFromString2((string)dr["Objects2"]); } catch { havenew = false; } if (!havenew) { // try loading the new spawn specifications try { Spawns = SpawnObject.LoadSpawnObjectsFromString((string)dr["Objects"]); } catch { questionable_spawner = true; } // can only have one of these defined } // do a check on the location of the spawner if (!IsValidMapLocation(SpawnCentreX, SpawnCentreY, SpawnMap)) { if (from != null) from.SendMessage(33, "Invalid location '{0}' at [{1} {2}] in {3}", SpawnName, SpawnCentreX, SpawnCentreY, XmlMapName); bad_spawner = true; } // Check if this spawner already exists XmlSpawner OldSpawner = null; bool found_container = false; bool found_spawner = false; Container spawn_container = null; if (!bad_spawner) { foreach (Item i in World.Items.Values) { if (i is XmlSpawner) { XmlSpawner CheckXmlSpawner = (XmlSpawner)i; // Check if the spawners GUID is the same as the one being loaded // and that the spawners map is the same as the one being loaded if ((CheckXmlSpawner.UniqueId == SpawnId.ToString()) /* && ( CheckXmlSpawner.Map == SpawnMap || loadrelative)*/ ) { OldSpawner = (XmlSpawner)i; found_spawner = true; } } //look for containers with the spawn coordinates if the incontainer flag is set if (InContainer && !found_container && (i is Container) && (SpawnCentreX == i.Location.X) && (SpawnCentreY == i.Location.Y) && (SpawnCentreZ == i.Location.Z || SpawnCentreZ == short.MinValue)) { // assume this is the container that the spawner was in found_container = true; spawn_container = i as Container; } // ok we can break if we have handled both the spawner and any containers if (found_spawner && (found_container || !InContainer)) break; } } // test to see whether the spawner specification was valid, bad, or questionable if (bad_spawner) { badcount++; if (from != null) from.SendMessage(33, "Invalid spawner"); // log it long fileposition = -1; try { fileposition = fs.Position; } catch { } try { using (StreamWriter op = new StreamWriter("badxml.log", true)) { op.WriteLine("# Invalid spawner : {0}: Fileposition {1} {2}", DateTime.UtcNow, fileposition, filename); op.WriteLine(); } } catch { } } else if (questionable_spawner) { questionablecount++; if (from != null) from.SendMessage(33, "Questionable spawner '{0}' at [{1} {2}] in {3}", SpawnName, SpawnCentreX, SpawnCentreY, XmlMapName); // log it long fileposition = -1; try { fileposition = fs.Position; } catch { } try { using (StreamWriter op = new StreamWriter("badxml.log", true)) { op.WriteLine("# Questionable spawner : {0}: Format: X Y Z Map SpawnerName Fileposition Xmlfile", DateTime.UtcNow); op.WriteLine("{0}\t{1}\t{2}\t{3}\t{4}\t{5}\t{6}", SpawnCentreX, SpawnCentreY, SpawnCentreZ, XmlMapName, SpawnName, fileposition, filename); op.WriteLine(); } } catch { } } if (!bad_spawner) { // Delete the old spawner if it exists if (OldSpawner != null) OldSpawner.Delete(); // Create the new spawner XmlSpawner TheSpawn = new XmlSpawner(SpawnId, SpawnX, SpawnY, SpawnWidth, SpawnHeight, SpawnName, SpawnMaxCount, SpawnMinDelay, SpawnMaxDelay, SpawnDuration, SpawnProximityRange, SpawnProximityTriggerSound, SpawnAmount, SpawnTeam, SpawnHomeRange, SpawnIsRelativeHomeRange, Spawns, SpawnMinRefractory, SpawnMaxRefractory, SpawnTODStart, SpawnTODEnd, SpawnObjectPropertyItem, SpawnObjectPropertyName, SpawnProximityMessage, SpawnItemTriggerName, SpawnNoItemTriggerName, SpawnSpeechTrigger, SpawnMobTriggerName, SpawnMobPropertyName, SpawnPlayerPropertyName, SpawnTriggerProbability, SpawnSetPropertyItem, SpawnIsGroup, SpawnTODMode, SpawnKillReset, SpawnExternalTriggering, SpawnSequentialSpawning, SpawnRegionName, SpawnAllowGhost, SpawnAllowNPC, SpawnSpawnOnTrigger, SpawnConfigFile, SpawnDespawnTime, SpawnSkillTrigger, SpawnSmartSpawning, SpawnWaypoint) { m_DisableGlobalAutoReset = TickReset }; // Try to find a valid Z height if required (SpawnCentreZ = short.MinValue) int NewZ = 0; // Check if relative loading is set. If so then try loading at the z-offset position first with no surface requirement, then try auto /*if(loadrelative && SpawnMap.CanFit( SpawnCentreX, SpawnCentreY, OrigZ - SpawnRelZ, SpawnFitSize,true, false,false )) */ if (loadrelative && HasTileSurface(SpawnMap, SpawnCentreX, SpawnCentreY, OrigZ - SpawnRelZ)) { NewZ = OrigZ - SpawnRelZ; } else if (SpawnCentreZ == short.MinValue) { NewZ = SpawnMap.GetAverageZ(SpawnCentreX, SpawnCentreY); if (SpawnMap.CanFit(SpawnCentreX, SpawnCentreY, NewZ, SpawnFitSize) == false) { for (int x = 1; x <= 39; x++) { if (SpawnMap.CanFit(SpawnCentreX, SpawnCentreY, NewZ + x, SpawnFitSize)) { NewZ += x; break; } } } } else { // This spawn point already has a defined Z location, so use it NewZ = SpawnCentreZ; } // if this is a container held spawner, drop it in the container if (found_container && (spawn_container != null) && !spawn_container.Deleted) { TheSpawn.Location = (new Point3D(ContainerX, ContainerY, ContainerZ)); spawn_container.AddItem(TheSpawn); } else { // disable the X_Y adjustments in OnLocationChange IgnoreLocationChange = true; TheSpawn.MoveToWorld(new Point3D(SpawnCentreX, SpawnCentreY, NewZ), SpawnMap); } // reset the spawner TheSpawn.Reset(); TheSpawn.Running = SpawnIsRunning; // update subgroup-specific next spawn times TheSpawn.NextSpawn = TimeSpan.Zero; TheSpawn.ResetNextSpawnTimes(); // Send a message to the client that the spawner is created if (from != null && verbose) from.SendMessage(188, "Created '{0}' in {1} at {2}", TheSpawn.Name, TheSpawn.Map.Name, TheSpawn.Location.ToString()); // Do a total respawn //TheSpawn.Respawn(); // Increment the count TotalCount++; } bad_spawner = false; questionable_spawner = false; } } if (from != null) from.SendMessage("Resolving spawner self references"); if (ds.Tables[SpawnTablePointName] != null && ds.Tables[SpawnTablePointName].Rows.Count > 0) foreach (DataRow dr in ds.Tables[SpawnTablePointName].Rows) { // Try load the GUID bool badid = false; Guid SpawnId = Guid.NewGuid(); try { SpawnId = new Guid((string)dr["UniqueId"]); } catch { badid = true; } if (badid) continue; // Get the map Map SpawnMap = frommap; string XmlMapName = frommap.Name; if (!loadrelative) { try { XmlMapName = (string)dr["Map"]; } catch { } // Convert the xml map value to a real map object try { SpawnMap = Map.Parse(XmlMapName); } catch { } } bool found_spawner = false; XmlSpawner OldSpawner = null; foreach (Item i in World.Items.Values) { if (i is XmlSpawner) { XmlSpawner CheckXmlSpawner = (XmlSpawner)i; // Check if the spawners GUID is the same as the one being loaded // and that the spawners map is the same as the one being loaded if ((CheckXmlSpawner.UniqueId == SpawnId.ToString()) /* && ( CheckXmlSpawner.Map == SpawnMap || loadrelative) */) { OldSpawner = (XmlSpawner)i; found_spawner = true; } } if (found_spawner) break; } if (found_spawner && OldSpawner != null && !OldSpawner.Deleted) { // resolve item name references since they may have referred to spawners that were just created string setObjectName = null; try { setObjectName = (string)dr["SetPropertyItemName"]; } catch { } if (!string.IsNullOrEmpty(setObjectName)) { // try to parse out the type information if it has also been saved string[] typeargs = setObjectName.Split(",".ToCharArray(), 2); string typestr = null; string namestr = setObjectName; if (typeargs.Length > 1) { namestr = typeargs[0]; typestr = typeargs[1]; } // if this is a new load then assume that it will be referring to another newly loaded object so append the newloadid if (loadnew) { string tmpsetObjectName = string.Format("{0}-{1}", namestr, newloadid); OldSpawner.m_SetPropertyItem = BaseXmlSpawner.FindItemByName(null, tmpsetObjectName, typestr); } // if this fails then try the original if (OldSpawner.m_SetPropertyItem == null) { OldSpawner.m_SetPropertyItem = BaseXmlSpawner.FindItemByName(null, namestr, typestr); } if (OldSpawner.m_SetPropertyItem == null) { failedsetitemcount++; if (from != null) from.SendMessage(33, "Failed to initialize SetItemProperty Object '{0}' on ' '{1}' at [{2} {3}] in {4}", setObjectName, OldSpawner.Name, OldSpawner.Location.X, OldSpawner.Location.Y, OldSpawner.Map); // log it try { using (StreamWriter op = new StreamWriter("badxml.log", true)) { op.WriteLine("# Failed SetItemProperty Object initialization : {0}: Format: ObjectName X Y Z Map SpawnerName Xmlfile", DateTime.UtcNow); op.WriteLine("{0}\t{1}\t{2}\t{3}\t{4}\t{5}\t{6}", setObjectName, OldSpawner.Location.X, OldSpawner.Location.Y, OldSpawner.Location.Z, OldSpawner.Map, OldSpawner.Name, filename); op.WriteLine(); } } catch { } } } string triggerObjectName = null; try { triggerObjectName = (string)dr["ObjectPropertyItemName"]; } catch { } if (!string.IsNullOrEmpty(triggerObjectName)) { string[] typeargs = triggerObjectName.Split(",".ToCharArray(), 2); string typestr = null; string namestr = triggerObjectName; if (typeargs.Length > 1) { namestr = typeargs[0]; typestr = typeargs[1]; } // if this is a new load then assume that it will be referring to another newly loaded object so append the newloadid if (loadnew) { string tmptriggerObjectName = string.Format("{0}-{1}", namestr, newloadid); OldSpawner.m_ObjectPropertyItem = BaseXmlSpawner.FindItemByName(null, tmptriggerObjectName, typestr); } // if this fails then try the original if (OldSpawner.m_ObjectPropertyItem == null) { OldSpawner.m_ObjectPropertyItem = BaseXmlSpawner.FindItemByName(null, namestr, typestr); } if (OldSpawner.m_ObjectPropertyItem == null) { failedobjectitemcount++; if (from != null) from.SendMessage(33, "Failed to initialize TriggerObject '{0}' on ' '{1}' at [{2} {3}] in {4}", triggerObjectName, OldSpawner.Name, OldSpawner.Location.X, OldSpawner.Location.Y, OldSpawner.Map); // log it try { using (StreamWriter op = new StreamWriter("badxml.log", true)) { op.WriteLine("# Failed TriggerObject initialization : {0}: Format: ObjectName X Y Z Map SpawnerName Xmlfile", DateTime.UtcNow); op.WriteLine("{0}\t{1}\t{2}\t{3}\t{4}\t{5}\t{6}", triggerObjectName, OldSpawner.Location.X, OldSpawner.Location.Y, OldSpawner.Location.Z, OldSpawner.Map, OldSpawner.Name, filename); op.WriteLine(); } } catch { } } } } } } // close the file try { fs.Close(); } catch { } if (from != null) from.SendMessage("{0} spawner(s) were created from file {1} [Trammel={2}, Felucca={3}, Ilshenar={4}, Malas={5}, Tokuno={6} Other={7}].", TotalCount, filename, TrammelCount, FeluccaCount, IlshenarCount, MalasCount, TokunoCount, OtherCount); if (failedobjectitemcount > 0) { if (from != null) from.SendMessage(33, "Failed to initialize TriggerObjects in {0} spawners. Saved to 'badxml.log'", failedobjectitemcount); } if (failedsetitemcount > 0) { if (from != null) from.SendMessage(33, "Failed to initialize SetItemProperty Objects in {0} spawners. Saved to 'badxml.log'", failedsetitemcount); } if (badcount > 0) { if (from != null) from.SendMessage(33, "{0} bad spawners detected. Saved to 'badxml.log'", badcount); } if (questionablecount > 0) { if (from != null) from.SendMessage(33, "{0} questionable spawners detected. Saved to 'badxml.log'", questionablecount); } processedmaps = 1; processedspawners = TotalCount; } public static string LocateFile(string filename) { bool found = false; string dirname = null; if (Directory.Exists(XmlSpawnDir)) { // get it from the defaults directory if it exists dirname = string.Format("{0}/{1}", XmlSpawnDir, filename); found = File.Exists(dirname) || Directory.Exists(dirname); } if (!found) { // otherwise just get it from the main installation dir dirname = filename; } return dirname; } [Usage("XmlNewLoad [SpawnerPrefixFilter]")] [Description("Loads new XmlSpawner objects with new GUIDs (no replacement) into the current map of the player.")] public static void NewLoad_OnCommand(CommandEventArgs e) { if (e.Mobile.AccessLevel >= DiskAccessLevel) { if (e.Arguments.Length >= 1) { string filename = LocateFile(e.Arguments[0]); // Spawner load criteria (if any) string SpawnerPrefix = string.Empty; // Check if there is an argument provided (load criteria) if (e.Arguments.Length > 1) SpawnerPrefix = e.Arguments[1]; int processedmaps; int processedspawners; XmlLoadFromFile(filename, SpawnerPrefix, e.Mobile, false, 0, true, out processedmaps, out processedspawners); } else e.Mobile.SendMessage("Usage: {0} [SpawnerPrefixFilter]", e.Command); } else e.Mobile.SendMessage("You do not have rights to perform this command."); } [Usage("XmlLoad [SpawnerPrefixFilter]")] [Description("Loads XmlSpawner objects (replacing existing spawners with matching GUIDs) into the proper map as defined in the file supplied.")] public static void Load_OnCommand(CommandEventArgs e) { var m = e.Mobile; if (m == null || m.AccessLevel >= DiskAccessLevel) { if (e.Arguments.Length >= 1) { string filename = LocateFile(e.Arguments[0]); // Spawner load criteria (if any) string SpawnerPrefix = string.Empty; // Check if there is an argument provided (load criteria) if (e.Arguments.Length > 1) SpawnerPrefix = e.Arguments[1]; int processedmaps; int processedspawners; XmlLoadFromFile(filename, SpawnerPrefix, m, false, 0, false, out processedmaps, out processedspawners); } else if (m != null) { e.Mobile.SendMessage("Usage: {0} [SpawnerPrefixFilter]", e.Command); } } else { e.Mobile.SendMessage("You do not have rights to perform this command."); } } [Usage("XmlNewLoadHere [SpawnerPrefixFilter][-maxrange range]")] [Description("Loads new XmlSpawner objects with new GUIDs (no replacement) to the current map and location of the player. Spawners beyond maxrange (default=48 tiles) are not moved relative to the player")] public static void NewLoadHere_OnCommand(CommandEventArgs e) { if (e.Mobile.AccessLevel >= DiskAccessLevel) { if (e.Arguments.Length >= 1) { string filename = LocateFile(e.Arguments[0]); // Spawner load criteria (if any) string SpawnerPrefix = string.Empty; bool badargs = false; int maxrange = 48; // Check if there is an argument provided (load criteria) try { // Check if there is an argument provided (load criteria) for (int nxtarg = 1; nxtarg < e.Arguments.Length; nxtarg++) { // is it a maxrange option? if (e.Arguments[nxtarg].ToLower() == "-maxrange") { maxrange = int.Parse(e.Arguments[++nxtarg]); } else { SpawnerPrefix = e.Arguments[nxtarg]; } } } catch { e.Mobile.SendMessage("Usage: {0} [SpawnerPrefixFilter][-maxrange range]", e.Command); badargs = true; } if (!badargs) { int processedmaps; int processedspawners; XmlLoadFromFile(filename, SpawnerPrefix, e.Mobile, true, maxrange, true, out processedmaps, out processedspawners); } } else e.Mobile.SendMessage("Usage: {0} [SpawnerPrefixFilter][-maxrange range]", e.Command); } else e.Mobile.SendMessage("You do not have rights to perform this command."); } [Usage("XmlLoadHere [SpawnerPrefixFilter][-maxrange range]")] [Description("Loads XmlSpawner objects to the current map and location of the player. Spawners beyond maxrange (default=48 tiles) are not moved relative to the player")] public static void LoadHere_OnCommand(CommandEventArgs e) { if (e.Mobile.AccessLevel >= DiskAccessLevel) { if (e.Arguments.Length >= 1) { string filename = LocateFile(e.Arguments[0]); // Spawner load criteria (if any) string SpawnerPrefix = string.Empty; bool badargs = false; int maxrange = 48; try { // Check if there is an argument provided (load criteria) for (int nxtarg = 1; nxtarg < e.Arguments.Length; nxtarg++) { // is it a maxrange option? if (e.Arguments[nxtarg].ToLower() == "-maxrange") { maxrange = int.Parse(e.Arguments[++nxtarg]); } else { SpawnerPrefix = e.Arguments[nxtarg]; } } } catch { e.Mobile.SendMessage("Usage: {0} [SpawnerPrefixFilter][-maxrange range]", e.Command); badargs = true; } if (!badargs) { int processedmaps; int processedspawners; XmlLoadFromFile(filename, SpawnerPrefix, e.Mobile, true, maxrange, false, out processedmaps, out processedspawners); } } else e.Mobile.SendMessage("Usage: {0} [SpawnerPrefixFilter][-maxrange range]", e.Command); } else e.Mobile.SendMessage("You do not have rights to perform this command."); } [Usage("XmlSaveOld [SpawnerPrefixFilter]")] [Description("Saves all XmlSpawner objects from the current map into the file supplied in the old xmlspawner format.")] public static void SaveOld_OnCommand(CommandEventArgs e) { SaveSpawns(e, false, true); } [Usage("XmlSpawnerSave [SpawnerPrefixFilter]")] [Description("Saves all XmlSpawner objects from the current map into the file supplied.")] public static void Save_OnCommand(CommandEventArgs e) { SaveSpawns(e, false, false); } [Usage("XmlSpawnerSaveAll [SpawnerPrefixFilter]")] [Description("Saves ALL XmlSpawner objects from the entire world into the file supplied.")] public static void SaveAll_OnCommand(CommandEventArgs e) { SaveSpawns(e, true, false); } public class XmlSaveSingle : BaseCommand { public XmlSaveSingle() { AccessLevel = DiskAccessLevel; Supports = CommandSupport.Single; Commands = new[] { "XmlSaveSingle" }; ObjectTypes = ObjectTypes.Items; Usage = "XmlSaveSingle "; Description = "Saves single xmlspawner to specified file."; } public override void Execute(CommandEventArgs e, object obj) { if (e == null || e.Mobile == null || e.Arguments == null) return; if (e.Arguments.Length < 1) { e.Mobile.SendMessage("Usage: {0} (without spaces!!)", e.Command); return; } string filename = e.Arguments[0]; XmlSpawner xmlspawner = obj as XmlSpawner; if (xmlspawner == null) { e.Mobile.SendMessage("You can select only XmlSpawner objects!"); return; } Mobile m = e.Mobile; CommandLogging.WriteLine(m, "{0} {1} Saving XmlSpawner {2} on file {3}", m.AccessLevel, CommandLogging.Format(m), CommandLogging.Format(xmlspawner), CommandLogging.Format(filename)); SaveSpawns(m, xmlspawner, filename); } } private static void SaveSpawns(Mobile m, XmlSpawner xmlspawner, string filename) { if (m.AccessLevel < DiskAccessLevel) { m.SendMessage("You do not have rights to perform this command."); return; } string dirname; if (Directory.Exists(XmlSpawnDir) && filename != null && !filename.StartsWith("/") && !filename.StartsWith("\\")) { // put it in the defaults directory if it exists dirname = string.Format("{0}/{1}", XmlSpawnDir, filename); } else { // otherwise just put it in the main installation dir dirname = filename; } m.SendMessage("Saving object in folder {0} - file {1} - spawner {2}.", dirname, filename, xmlspawner); List saveslist = new List(1); saveslist.Add(xmlspawner); SaveSpawnList(m, saveslist, dirname, false, true); } private static void SaveSpawns(CommandEventArgs e, bool SaveAllMaps, bool oldformat) { if (e == null || e.Mobile == null || e.Arguments == null || e.Arguments.Length < 1) return; if (e.Mobile.AccessLevel < DiskAccessLevel) { e.Mobile.SendMessage("You do not have rights to perform this command."); return; } if (e.Arguments != null && e.Arguments.Length < 1) { e.Mobile.SendMessage("Usage: {0} [SpawnerPrefixFilter]", e.Command); return; } // Spawner save criteria (if any) string SpawnerPrefix = string.Empty; // Check if there is an argument provided (save criteria) if (e.Arguments.Length > 1) SpawnerPrefix = e.Arguments[1]; string filename = e.Arguments[0]; string dirname; if (Directory.Exists(XmlSpawnDir) && filename != null && !filename.StartsWith("/") && !filename.StartsWith("\\")) { // put it in the defaults directory if it exists dirname = string.Format("{0}/{1}", XmlSpawnDir, filename); } else { // otherwise just put it in the main installation dir dirname = filename; } if (SaveAllMaps) e.Mobile.SendMessage(string.Format("Saving {0} objects{1} to file {2} from {3}.", "XmlSpawner", !string.IsNullOrEmpty(SpawnerPrefix) ? " beginning with " + SpawnerPrefix : string.Empty, dirname, e.Mobile.Map)); else e.Mobile.SendMessage(string.Format("Saving {0} obejcts{1} to file {2} from the entire world.", "XmlSpawner", !string.IsNullOrEmpty(SpawnerPrefix) ? " beginning with " + SpawnerPrefix : string.Empty, dirname)); List saveslist = new List(); // Add each spawn point to the list foreach (Item i in World.Items.Values) { if (i is XmlSpawner && !i.Deleted && (SaveAllMaps || (i.Map == e.Mobile.Map)) //check for mob carried spawners and ignore them && !(i.RootParent is Mobile) && (SpawnerPrefix == null || (SpawnerPrefix.Length == 0) || (i.Name != null && i.Name.StartsWith(SpawnerPrefix)))) { saveslist.Add((XmlSpawner)i); } } // save the list SaveSpawnList(e.Mobile, saveslist, dirname, oldformat, true); } public static bool SaveSpawnList(List savelist, Stream stream) { return SaveSpawnList(null, savelist, null, stream, false, false); } public static bool SaveSpawnList(Mobile from, List savelist, string dirname, bool oldformat, bool verbose) { if (string.IsNullOrEmpty(dirname)) return false; bool save_ok = true; FileStream fs = null; try { // Create the FileStream to write with. fs = new FileStream(dirname, FileMode.Create); } catch { if (from != null) from.SendMessage("Error creating file {0}", dirname); save_ok = false; } // so far so good if (save_ok) { save_ok = SaveSpawnList(from, savelist, dirname, fs, oldformat, verbose); } if (!save_ok && from != null) { from.SendMessage("Unable to complete save operation."); } return save_ok; } public static bool SaveSpawnList(Mobile from, List savelist, string dirname, Stream stream, bool oldformat, bool verbose) { if (savelist == null || stream == null) return false; int TotalCount = 0; int TrammelCount = 0; int FeluccaCount = 0; int IlshenarCount = 0; int MalasCount = 0; int TokunoCount = 0; int OtherCount = 0; // Create the data set DataSet ds = new DataSet(SpawnDataSetName); // Load the data set up ds.Tables.Add(SpawnTablePointName); // Create spawn point schema ds.Tables[SpawnTablePointName].Columns.Add("Name"); ds.Tables[SpawnTablePointName].Columns.Add("UniqueId"); ds.Tables[SpawnTablePointName].Columns.Add("Map"); ds.Tables[SpawnTablePointName].Columns.Add("X"); ds.Tables[SpawnTablePointName].Columns.Add("Y"); ds.Tables[SpawnTablePointName].Columns.Add("Width"); ds.Tables[SpawnTablePointName].Columns.Add("Height"); ds.Tables[SpawnTablePointName].Columns.Add("CentreX"); ds.Tables[SpawnTablePointName].Columns.Add("CentreY"); ds.Tables[SpawnTablePointName].Columns.Add("CentreZ"); ds.Tables[SpawnTablePointName].Columns.Add("Range"); ds.Tables[SpawnTablePointName].Columns.Add("MaxCount"); ds.Tables[SpawnTablePointName].Columns.Add("MinDelay"); ds.Tables[SpawnTablePointName].Columns.Add("MaxDelay"); // deal with the double format for delay. old format stored them as minutes in int format. that meant that short delays were lost // proper solution would simply be to store as doubles, but older progs still assume int format (like spawneditor) // so this is the solution. add a flag and do it both ways. ds.Tables[SpawnTablePointName].Columns.Add("DelayInSec"); // add the duration and proximity range and sound parameters, and in container flag and coords inside the container ds.Tables[SpawnTablePointName].Columns.Add("Duration"); ds.Tables[SpawnTablePointName].Columns.Add("DespawnTime"); ds.Tables[SpawnTablePointName].Columns.Add("ProximityRange"); ds.Tables[SpawnTablePointName].Columns.Add("ProximityTriggerSound"); ds.Tables[SpawnTablePointName].Columns.Add("ProximityTriggerMessage"); ds.Tables[SpawnTablePointName].Columns.Add("ObjectPropertyName"); ds.Tables[SpawnTablePointName].Columns.Add("ObjectPropertyItemName"); ds.Tables[SpawnTablePointName].Columns.Add("SetPropertyItemName"); ds.Tables[SpawnTablePointName].Columns.Add("ItemTriggerName"); ds.Tables[SpawnTablePointName].Columns.Add("NoItemTriggerName"); ds.Tables[SpawnTablePointName].Columns.Add("MobTriggerName"); ds.Tables[SpawnTablePointName].Columns.Add("MobPropertyName"); ds.Tables[SpawnTablePointName].Columns.Add("PlayerPropertyName"); ds.Tables[SpawnTablePointName].Columns.Add("TriggerProbability"); ds.Tables[SpawnTablePointName].Columns.Add("SpeechTrigger"); ds.Tables[SpawnTablePointName].Columns.Add("SkillTrigger"); ds.Tables[SpawnTablePointName].Columns.Add("InContainer"); ds.Tables[SpawnTablePointName].Columns.Add("ContainerX"); ds.Tables[SpawnTablePointName].Columns.Add("ContainerY"); ds.Tables[SpawnTablePointName].Columns.Add("ContainerZ"); ds.Tables[SpawnTablePointName].Columns.Add("MinRefractory"); ds.Tables[SpawnTablePointName].Columns.Add("MaxRefractory"); ds.Tables[SpawnTablePointName].Columns.Add("TODStart"); ds.Tables[SpawnTablePointName].Columns.Add("TODEnd"); ds.Tables[SpawnTablePointName].Columns.Add("TODMode"); ds.Tables[SpawnTablePointName].Columns.Add("KillReset"); ds.Tables[SpawnTablePointName].Columns.Add("ExternalTriggering"); ds.Tables[SpawnTablePointName].Columns.Add("SequentialSpawning"); ds.Tables[SpawnTablePointName].Columns.Add("RegionName"); ds.Tables[SpawnTablePointName].Columns.Add("AllowGhostTriggering"); ds.Tables[SpawnTablePointName].Columns.Add("AllowNPCTriggering"); ds.Tables[SpawnTablePointName].Columns.Add("SpawnOnTrigger"); ds.Tables[SpawnTablePointName].Columns.Add("ConfigFile"); ds.Tables[SpawnTablePointName].Columns.Add("SmartSpawning"); ds.Tables[SpawnTablePointName].Columns.Add("TickReset"); ds.Tables[SpawnTablePointName].Columns.Add("WayPoint"); ds.Tables[SpawnTablePointName].Columns.Add("Team"); // amount for stacked item spawns ds.Tables[SpawnTablePointName].Columns.Add("Amount"); ds.Tables[SpawnTablePointName].Columns.Add("IsGroup"); ds.Tables[SpawnTablePointName].Columns.Add("IsRunning"); ds.Tables[SpawnTablePointName].Columns.Add("IsHomeRangeRelative"); ds.Tables[SpawnTablePointName].Columns.Add(oldformat ? "Objects" : "Objects2"); // Always export sorted by UUID to help diffs savelist.Sort((a, b) => { return a.UniqueId.CompareTo(b.UniqueId); }); // Add each spawn point to the new table foreach (XmlSpawner sp in savelist) { if (sp == null || sp.Map == null || sp.Deleted) continue; if (verbose && from != null) // Send a message to the client that the spawner is being saved from.SendMessage(68, "Saving '{0}' in {1} at {2}", sp.Name, sp.Map.Name, sp.Location.ToString()); // Create a new data row DataRow dr = ds.Tables[SpawnTablePointName].NewRow(); // Populate the data dr["Name"] = sp.Name; // Set the unqiue id dr["UniqueId"] = sp.m_UniqueId; // Get the map name dr["Map"] = sp.Map.Name; // Convert the xml map value to a real map object if (string.Compare(sp.Map.Name, Map.Trammel.Name, true) == 0) TrammelCount++; else if (string.Compare(sp.Map.Name, Map.Felucca.Name, true) == 0) FeluccaCount++; else if (string.Compare(sp.Map.Name, Map.Ilshenar.Name, true) == 0) IlshenarCount++; else if (string.Compare(sp.Map.Name, Map.Malas.Name, true) == 0) MalasCount++; else if (string.Compare(sp.Map.Name, Map.Tokuno.Name, true) == 0) TokunoCount++; else OtherCount++; dr["X"] = sp.m_X; dr["Y"] = sp.m_Y; dr["Width"] = sp.m_Width; dr["Height"] = sp.m_Height; // check to see if this is in a container if (sp.RootParent is Container) { dr["CentreX"] = ((Container)(sp.RootParent)).Location.X; dr["CentreY"] = ((Container)(sp.RootParent)).Location.Y; dr["CentreZ"] = ((Container)(sp.RootParent)).Location.Z; dr["ContainerX"] = sp.Location.X; dr["ContainerY"] = sp.Location.Y; dr["ContainerZ"] = sp.Location.Z; dr["InContainer"] = true; } else { dr["CentreX"] = sp.Location.X; dr["CentreY"] = sp.Location.Y; dr["CentreZ"] = sp.Location.Z; //dr["ContainerX"] = 0; //dr["ContainerY"] = 0; //dr["ContainerZ"] = 0; dr["InContainer"] = false; } dr["Range"] = sp.m_HomeRange; dr["MaxCount"] = sp.m_Count; // need to deal with the fact that the old xmlspawner xml format only saved delays in minutes as ints, so shorter spawn times // are lost // flag it then on reading it can be properly handled and still // maintain backward compatibility with older xml files if (((int)sp.m_MinDelay.TotalSeconds - 60 * (int)sp.m_MinDelay.TotalMinutes) > 0 || ((int)sp.m_MaxDelay.TotalSeconds - 60 * (int)sp.m_MaxDelay.TotalMinutes) > 0) { dr["DelayInSec"] = true; dr["MinDelay"] = (int)sp.m_MinDelay.TotalSeconds; dr["MaxDelay"] = (int)sp.m_MaxDelay.TotalSeconds; } else { dr["DelayInSec"] = false; dr["MinDelay"] = (int)sp.m_MinDelay.TotalMinutes; dr["MaxDelay"] = (int)sp.m_MaxDelay.TotalMinutes; } // additional parameters dr["TODStart"] = sp.m_TODStart.TotalMinutes; dr["TODEnd"] = sp.m_TODEnd.TotalMinutes; dr["TODMode"] = (int)sp.m_TODMode; dr["KillReset"] = sp.m_KillReset; dr["MinRefractory"] = sp.m_MinRefractory.TotalMinutes; dr["MaxRefractory"] = sp.m_MaxRefractory.TotalMinutes; dr["Duration"] = sp.m_Duration.TotalMinutes; dr["DespawnTime"] = sp.m_DespawnTime.TotalHours; dr["ExternalTriggering"] = sp.m_ExternalTriggering; dr["ProximityRange"] = sp.m_ProximityRange; dr["ProximityTriggerSound"] = sp.m_ProximityTriggerSound; dr["ProximityTriggerMessage"] = sp.m_ProximityTriggerMessage; if (sp.m_ObjectPropertyItem != null && !sp.m_ObjectPropertyItem.Deleted) dr["ObjectPropertyItemName"] = string.Format("{0},{1}", sp.m_ObjectPropertyItem.Name, sp.m_ObjectPropertyItem.GetType().Name); else dr["ObjectPropertyItemName"] = null; dr["ObjectPropertyName"] = sp.m_ObjectPropertyName; if (sp.m_SetPropertyItem != null && !sp.m_SetPropertyItem.Deleted) dr["SetPropertyItemName"] = string.Format("{0},{1}", sp.m_SetPropertyItem.Name, sp.m_SetPropertyItem.GetType().Name); else dr["SetPropertyItemName"] = null; dr["ItemTriggerName"] = sp.m_ItemTriggerName; dr["NoItemTriggerName"] = sp.m_NoItemTriggerName; dr["MobTriggerName"] = sp.m_MobTriggerName; dr["MobPropertyName"] = sp.m_MobPropertyName; dr["PlayerPropertyName"] = sp.m_PlayerPropertyName; dr["TriggerProbability"] = sp.m_TriggerProbability; dr["SequentialSpawning"] = sp.m_SequentialSpawning; dr["RegionName"] = sp.m_RegionName; dr["AllowGhostTriggering"] = sp.m_AllowGhostTriggering; dr["AllowNPCTriggering"] = sp.m_AllowNPCTriggering; dr["SpawnOnTrigger"] = sp.m_SpawnOnTrigger; dr["ConfigFile"] = sp.m_ConfigFile; dr["SmartSpawning"] = sp.m_SmartSpawning; dr["TickReset"] = sp.m_DisableGlobalAutoReset; dr["SpeechTrigger"] = sp.m_SpeechTrigger; dr["SkillTrigger"] = sp.m_SkillTrigger; dr["Amount"] = sp.m_StackAmount; dr["Team"] = sp.m_Team; // assign the waypoint based on the waypoint name if it deviates from the default waypoint name, otherwise do it by serial string waystr = null; if (sp.m_WayPoint != null) { if ((sp.m_WayPoint.Name != defwaypointname) && !string.IsNullOrEmpty(sp.m_WayPoint.Name)) { waystr = sp.m_WayPoint.Name; } else { waystr = string.Format("SERIAL,{0}", sp.m_WayPoint.Serial); } } dr["WayPoint"] = waystr; dr["IsGroup"] = sp.m_Group; dr["IsRunning"] = sp.m_Running; dr["IsHomeRangeRelative"] = sp.m_HomeRangeIsRelative; if (oldformat) { dr["Objects"] = sp.GetSerializedObjectList(); } else { dr["Objects2"] = sp.GetSerializedObjectList2(); } // Add the row the the table ds.Tables[SpawnTablePointName].Rows.Add(dr); // Increment the count TotalCount++; } // Write out the file bool file_error = false; if (TotalCount > 0) { try { ds.WriteXml(stream); } catch { file_error = true; } if (file_error) { return false; } } try { stream.Close(); } catch { } // Indicate how many spawners were written if (from != null) { from.SendMessage("{0} spawner(s) were saved to file {1} [Trammel={2}, Felucca={3}, Ilshenar={4}, Malas={5}, Tokuno={6}, Other={7}].", TotalCount, dirname, TrammelCount, FeluccaCount, IlshenarCount, MalasCount, TokunoCount, OtherCount); } return true; } private static void WipeSpawners(CommandEventArgs e, bool WipeAll) { if (e == null || e.Mobile == null) return; if (e.Mobile.AccessLevel >= AccessLevel.Administrator) { // Spawner delete criteria (if any) string SpawnerPrefix = string.Empty; // Check if there is an argument provided (delete criteria) if (e.Arguments != null && e.Arguments.Length > 0) SpawnerPrefix = e.Arguments[0]; if (WipeAll) e.Mobile.SendMessage("Removing ALL XmlSpawner objects from the world{0}.", !string.IsNullOrEmpty(SpawnerPrefix) ? " beginning with " + SpawnerPrefix : string.Empty); else e.Mobile.SendMessage("Removing ALL XmlSpawner objects from {0}{1}.", e.Mobile.Map, !string.IsNullOrEmpty(SpawnerPrefix) ? " beginning with " + SpawnerPrefix : string.Empty); // Delete Xml spawner's in the world based on the mobiles current map int Count = 0; List ToDelete = new List(); foreach (Item i in World.Items.Values) { if ((i is XmlSpawner) && (WipeAll || i.Map == e.Mobile.Map) && (i.Deleted == false)) { // Check if there is a delete condition if (SpawnerPrefix == null || (SpawnerPrefix.Length == 0) || (i.Name.StartsWith(SpawnerPrefix))) { // Send a message to the client that the spawner is being deleted //e.Mobile.SendMessage(33, "Removing '{0}' in {1} at {2}", i.Name, i.Map.Name, i.Location.ToString()); ToDelete.Add(i); Count++; } } } // Delete the items in the array list foreach (Item i in ToDelete) i.Delete(); if (WipeAll) e.Mobile.SendMessage("Removed {0} XmlSpawner objects from the world.", Count); else e.Mobile.SendMessage("Removed {0} XmlSpawner objects from {1}.", Count, e.Mobile.Map); } else e.Mobile.SendMessage("You do not have rights to perform this command."); } [Usage("XmlSpawnerRespawn [SpawnerPrefixFilter]")] [Description("Respawns all XmlSpawner objects from the current map.")] public static void Respawn_OnCommand(CommandEventArgs e) { RespawnSpawners(e, false); } [Usage("XmlSpawnerRespawnAll [SpawnerPrefixFilter]")] [Description("Respawns all XmlSpawner objects from the entire world.")] public static void RespawnAll_OnCommand(CommandEventArgs e) { RespawnSpawners(e, true); } private static void RespawnSpawners(CommandEventArgs e, bool RespawnAll) { if (e == null || e.Mobile == null) return; if (e.Mobile.AccessLevel >= AccessLevel.Administrator) { // Spawner Respawn criteria (if any) string SpawnerPrefix = string.Empty; // Check if there is an argument provided (respawn criteria) if (e.Arguments != null && e.Arguments.Length > 0) SpawnerPrefix = e.Arguments[0]; if (RespawnAll) e.Mobile.SendMessage("Respawning ALL XmlSpawner objects from the world{0}.", !string.IsNullOrEmpty(SpawnerPrefix) ? " beginning with " + SpawnerPrefix : string.Empty); else e.Mobile.SendMessage("Respawning ALL XmlSpawner objects from {0}{1}.", e.Mobile.Map, !string.IsNullOrEmpty(SpawnerPrefix) ? " beginning with " + SpawnerPrefix : string.Empty); // Respawn Xml spawner's in the world based on the mobiles current map int Count = 0; List ToRespawn = new List(); foreach (Item i in World.Items.Values) { try { if ((i is XmlSpawner) && (RespawnAll || i.Map == e.Mobile.Map) && (i.Deleted == false)) { // Check if there is a respawn condition if ((SpawnerPrefix == null) || (SpawnerPrefix.Length == 0) || (i.Name != null && i.Name.StartsWith(SpawnerPrefix))) { ToRespawn.Add(i); Count++; } } } catch (Exception ex) { Console.WriteLine("Error attempting to add {0}, {1}", i, ex.Message); } } // Respawn the items in the array list foreach (Item i in ToRespawn) { // Send a message to the client that the spawner is being respawned e.Mobile.SendMessage(33, "Respawning '{0}' in {1} at {2}", i.Name, i.Map.Name, i.Location.ToString()); XmlSpawner CheckXmlSpawner = (XmlSpawner)i; CheckXmlSpawner.Respawn(); } if (RespawnAll) e.Mobile.SendMessage("Respawned {0} XmlSpawner objects from the world.", Count); else e.Mobile.SendMessage("Respawned {0} XmlSpawner objects from {1}.", Count, e.Mobile.Map); } else e.Mobile.SendMessage("You do not have rights to perform this command."); } #if (TRACE) public static void XmlMake_OnCommand(CommandEventArgs e) { if (e.Arguments.Length > 0) { int count = 0; try { count = Convert.ToInt32(e.Arguments[0], 10); } catch (Exception ex) { Server.Diagnostics.ExceptionLogging.LogException(ex); } for (int i = 0; i < count; i++) { if (e.Arguments.Length > 2) { Spawner x = new Spawner(10, 1, 1, 0, 2, e.Arguments[1]); x.Location = new Point3D(5400 + Utility.Random(700), 1090 + Utility.Random(180), 0); x.Map = Map.Trammel; } else if (e.Arguments.Length > 1) { XmlSpawner x = new XmlSpawner(10, 1, 1, 0, 2, e.Arguments[1]); x.Location = new Point3D(5400 + Utility.Random(700), 1090 + Utility.Random(180), 0); x.Map = Map.Trammel; //x.MinDelay = TimeSpan.FromSeconds(1); //x.MaxDelay = TimeSpan.FromSeconds(1); //x.ProximityRange = 0; } } if (e.Arguments.Length > 2) { e.Mobile.SendMessage("Created {0} Spawner objects.", count); } else { e.Mobile.SendMessage("Created {0} XmlSpawner objects.", count); } } } public static void XmlTrace_OnCommand() { XmlTrace_OnCommand(null); } public static void XmlTrace_OnCommand(CommandEventArgs e) { Process currentprocess = Process.GetCurrentProcess(); TimeSpan runningtime = DateTime.UtcNow - XmlSpawner._traceStartTime; double processtime = currentprocess.UserProcessorTime.TotalMilliseconds - _startProcessTime; double sysload = 0; if (runningtime.TotalMilliseconds > 0) { sysload = processtime / runningtime.TotalMilliseconds; } Console.WriteLine("______________"); Console.WriteLine("Active Traces:"); Console.WriteLine("Running Time = {0}", runningtime); Console.WriteLine("Adjusted Process Time = {0:####.####} secs", processtime / 1000); Console.WriteLine("Processor Time = {0} ({1:p3} avg sys load)", currentprocess.UserProcessorTime, sysload); for (int i = 0; i < MaxTraces; i++) { if (XmlSpawner._traceCount[i] > 0) { double load = 0; if (processtime > 0) { load = _traceTotal[i].TotalMilliseconds / processtime; } Console.WriteLine("{0} ({4}) {1,21} / {2} calls = {3:####.####} ms/call, {5:p3}", i, XmlSpawner._traceTotal[i], XmlSpawner._traceCount[i], _traceTotal[i].TotalMilliseconds / XmlSpawner._traceCount[i], XmlSpawner._traceName[i], load); } } } public static void XmlResetTrace_OnCommand(CommandEventArgs e) { if (e.Arguments.Length >= 0) { for (int i = 0; i < MaxTraces; i++) { XmlSpawner._traceCount[i] = 0; XmlSpawner._traceTotal[i] = TimeSpan.Zero; } XmlSpawner._traceStartTime = DateTime.UtcNow; Process currentprocess = Process.GetCurrentProcess(); _startProcessTime = currentprocess.UserProcessorTime.TotalMilliseconds; Console.WriteLine("Traces reset"); } } #endif #endregion #region Constructors [Constructable] public XmlSpawner() : base(BaseItemId) { m_PlayerCreated = true; m_UniqueId = Guid.NewGuid().ToString(); SpawnRange = defSpawnRange; InitSpawn(0, 0, m_Width, m_Height, string.Empty, 0, defMinDelay, defMaxDelay, defDuration, defProximityRange, defProximityTriggerSound, defAmount, defTeam, defHomeRange, defRelativeHome, new SpawnObject[0], defMinRefractory, defMaxRefractory, defTODStart, defTODEnd, null, null, null, null, null, null, null, null, null, defTriggerProbability, null, defIsGroup, defTODMode, defKillReset, false, -1, null, false, false, false, null, defDespawnTime, null, false, null); } [Constructable] public XmlSpawner(int amount, int minDelay, int maxDelay, int team, int homeRange, string creatureName) : base(BaseItemId) { m_PlayerCreated = true; m_UniqueId = Guid.NewGuid().ToString(); SpawnRange = homeRange; SpawnObject[] so = new SpawnObject[1]; so[0] = new SpawnObject(creatureName, amount); InitSpawn(0, 0, m_Width, m_Height, string.Empty, amount, TimeSpan.FromMinutes(minDelay), TimeSpan.FromMinutes(maxDelay), defDuration, defProximityRange, defProximityTriggerSound, defAmount, team, homeRange, defRelativeHome, so, defMinRefractory, defMaxRefractory, defTODStart, defTODEnd, null, null, null, null, null, null, null, null, null, defTriggerProbability, null, defIsGroup, defTODMode, defKillReset, false, -1, null, false, false, false, null, defDespawnTime, null, false, null); } [Constructable] public XmlSpawner(int amount, int minDelay, int maxDelay, int team, int homeRange, int spawnRange, string creatureName) : base(BaseItemId) { m_PlayerCreated = true; m_UniqueId = Guid.NewGuid().ToString(); SpawnRange = spawnRange; SpawnObject[] so = new SpawnObject[1]; so[0] = new SpawnObject(creatureName, amount); InitSpawn(0, 0, m_Width, m_Height, string.Empty, amount, TimeSpan.FromMinutes(minDelay), TimeSpan.FromMinutes(maxDelay), defDuration, defProximityRange, defProximityTriggerSound, defAmount, team, homeRange, defRelativeHome, so, defMinRefractory, defMaxRefractory, defTODStart, defTODEnd, null, null, null, null, null, null, null, null, null, defTriggerProbability, null, defIsGroup, defTODMode, defKillReset, false, -1, null, false, false, false, null, defDespawnTime, null, false, null); } [Constructable] public XmlSpawner(string creatureName) : base(BaseItemId) { m_PlayerCreated = true; m_UniqueId = Guid.NewGuid().ToString(); SpawnObject[] so = new SpawnObject[1]; so[0] = new SpawnObject(creatureName, 1); SpawnRange = defSpawnRange; InitSpawn(0, 0, m_Width, m_Height, string.Empty, 1, defMinDelay, defMaxDelay, defDuration, defProximityRange, defProximityTriggerSound, defAmount, defTeam, defHomeRange, defRelativeHome, so, defMinRefractory, defMaxRefractory, defTODStart, defTODEnd, null, null, null, null, null, null, null, null, null, defTriggerProbability, null, defIsGroup, defTODMode, defKillReset, false, -1, null, false, false, false, null, defDespawnTime, null, false, null); } public XmlSpawner(Guid uniqueId, int x, int y, int width, int height, string name, int maxCount, TimeSpan minDelay, TimeSpan maxDelay, TimeSpan duration, int proximityRange, int proximityTriggerSound, int amount, int team, int homeRange, bool isRelativeHomeRange, SpawnObject[] spawnObjects, TimeSpan minRefractory, TimeSpan maxRefractory, TimeSpan todstart, TimeSpan todend, Item objectPropertyItem, string objectPropertyName, string proximityMessage, string itemTriggerName, string noitemTriggerName, string speechTrigger, string mobTriggerName, string mobPropertyName, string playerPropertyName, double triggerProbability, Item setPropertyItem, bool isGroup, TODModeType todMode, int killReset, bool externalTriggering, int sequentialSpawning, string regionName, bool allowghost, bool allownpc, bool spawnontrigger, string configfile, TimeSpan despawnTime, string skillTrigger, bool smartSpawning, WayPoint wayPoint) : base(BaseItemId) { m_UniqueId = uniqueId.ToString(); InitSpawn(x, y, width, height, name, maxCount, minDelay, maxDelay, duration, proximityRange, proximityTriggerSound, amount, team, homeRange, isRelativeHomeRange, spawnObjects, minRefractory, maxRefractory, todstart, todend, objectPropertyItem, objectPropertyName, proximityMessage, itemTriggerName, noitemTriggerName, speechTrigger, mobTriggerName, mobPropertyName, playerPropertyName, triggerProbability, setPropertyItem, isGroup, todMode, killReset, externalTriggering, sequentialSpawning, regionName, allowghost, allownpc, spawnontrigger, configfile, despawnTime, skillTrigger, smartSpawning, wayPoint); } public void InitSpawn(int x, int y, int width, int height, string name, int maxCount, TimeSpan minDelay, TimeSpan maxDelay, TimeSpan duration, int proximityRange, int proximityTriggerSound, int amount, int team, int homeRange, bool isRelativeHomeRange, SpawnObject[] objectsToSpawn, TimeSpan minRefractory, TimeSpan maxRefractory, TimeSpan todstart, TimeSpan todend, Item objectPropertyItem, string objectPropertyName, string proximityMessage, string itemTriggerName, string noitemTriggerName, string speechTrigger, string mobTriggerName, string mobPropertyName, string playerPropertyName, double triggerProbability, Item setPropertyItem, bool isGroup, TODModeType todMode, int killReset, bool externalTriggering, int sequentialSpawning, string regionName, bool allowghost, bool allownpc, bool spawnontrigger, string configfile, TimeSpan despawnTime, string skillTrigger, bool smartSpawning, WayPoint wayPoint) { Visible = false; Movable = false; m_X = x; m_Y = y; m_Width = width; m_Height = height; // init spawn range if compatible if (width == height) m_SpawnRange = width / 2; else m_SpawnRange = -1; m_Running = true; m_Group = isGroup; Name = !string.IsNullOrEmpty(name) ? name : "Spawner"; m_MinDelay = minDelay; m_MaxDelay = maxDelay; // duration and proximity range parameter m_MinRefractory = minRefractory; m_MaxRefractory = maxRefractory; m_TODStart = todstart; m_TODEnd = todend; m_TODMode = todMode; m_KillReset = killReset; m_Duration = duration; m_DespawnTime = despawnTime; m_ProximityRange = proximityRange; m_ProximityTriggerSound = proximityTriggerSound; m_proximityActivated = false; m_durActivated = false; m_refractActivated = false; m_Count = maxCount; m_Team = team; m_StackAmount = amount; m_HomeRange = homeRange; m_HomeRangeIsRelative = isRelativeHomeRange; m_ObjectPropertyItem = objectPropertyItem; m_ObjectPropertyName = objectPropertyName; m_ProximityTriggerMessage = proximityMessage; m_ItemTriggerName = itemTriggerName; m_NoItemTriggerName = noitemTriggerName; m_SpeechTrigger = speechTrigger; SkillTrigger = skillTrigger; // note this will register the skill as well m_MobTriggerName = mobTriggerName; m_MobPropertyName = mobPropertyName; m_PlayerPropertyName = playerPropertyName; m_TriggerProbability = triggerProbability; m_SetPropertyItem = setPropertyItem; m_ExternalTriggering = externalTriggering; m_ExternalTrigger = false; m_SequentialSpawning = sequentialSpawning; RegionName = regionName; m_AllowGhostTriggering = allowghost; m_AllowNPCTriggering = allownpc; m_SpawnOnTrigger = spawnontrigger; m_SmartSpawning = smartSpawning; ConfigFile = configfile; m_WayPoint = wayPoint; // set the totalitem property to -1 so that it doesnt show up in the item count of containers //TotalItems = -1; //UpdateTotal(this, TotalType.Items, -1); // Create the array of spawned objects m_SpawnObjects = new List(); // Assign the list of objects to spawn SpawnObjects = objectsToSpawn; // Kick off the process DoTimer(TimeSpan.FromSeconds(1)); } public XmlSpawner(Serial serial) : base(serial) { } #endregion #region Defrag methods public void Defrag(bool killtest) { if (m_SpawnObjects == null) return; bool removed = false; int total_removed = 0; List deleteilist = new List(); List deletemlist = new List(); foreach (SpawnObject so in m_SpawnObjects) { for (int x = 0; x < so.SpawnedObjects.Count; x++) { object o = so.SpawnedObjects[x]; if (o is Item) { Item item = (Item)o; bool despawned = false; // check to see if the despawn time has elapsed. If so, then delete it if it hasnt been picked up or stolen. if (DespawnTime.TotalHours > 0 && !item.Deleted && (item.LastMoved < DateTime.UtcNow - DespawnTime) && (item.Parent == Parent) && (!ItemFlags.GetTaken(item) || (item.Parent != null && (item.Parent == Parent)))) // can despawn if just moved within the same container { //item.Delete(); deleteilist.Add(item); despawned = true; } // Check if the items has been deleted or // if something else now owns the item (picked it up for example) // also check the stolen/placed in container flag. If any of those are true then the spawner doesnt own it any more so take it off the list. // the stolen/container flag prevents spawns from being left on the list when players take them and lock them back down on the ground. // If you have made the changes to stealing.cs and container.cs described in xmlspawner2.txt then just uncomment the line below to // enable this check if (item.Deleted || despawned || (item.Parent != Parent) // different container || (ItemFlags.GetTaken(item) && (item.Parent == null || (item.Parent != Parent)))) // taken and in the world, or a different container { so.SpawnedObjects.Remove(o); x--; removed = true; // if sequential spawning is active and the RestrictKillsToSubgroup flag is set, then check to see if // the object is in the current subgroup before adding to the total if (SequentialSpawn >= 0 && so.RestrictKillsToSubgroup) { if (so.SubGroup == SequentialSpawn) total_removed++; } else { // just add it total_removed++; } } } else if (o is Mobile) { Mobile m = (Mobile)o; bool despawned = false; // check to see if the despawn time has elapsed. If so, and the sector is not active then delete it. if (DespawnTime.TotalHours > 0 && !m.Deleted && (m.CreationTime < DateTime.UtcNow - DespawnTime) && m.Map != null && m.Map != Map.Internal && !m.Map.GetSector(m.Location).Active) { //m.Delete(); deletemlist.Add(m); despawned = true; } if (m.Deleted || despawned) { // Remove the delete mobile from the list so.SpawnedObjects.Remove(o); x--; removed = true; // if sequential spawning is active and the RestrictKillsToSubgroup flag is set, then check to see if // the object is in the current subgroup before adding to the total if (SequentialSpawn >= 0 && so.RestrictKillsToSubgroup) { if (so.SubGroup == SequentialSpawn) total_removed++; } else { // just add it total_removed++; } } else if (m is BaseCreature) { BaseCreature b = (BaseCreature)m; // Check if the creature has been tamed or previously tamed and released // and if it is, remove it from the list of spawns if (b.Controlled || b.IsStabled || (b.Owners != null && b.Owners.Count > 0)) { so.SpawnedObjects.Remove(o); x--; removed = true; // if sequential spawning is active and the RestrictKillsToSubgroup flag is set, then check to see if // the object is in the current subgroup before adding to the total if (SequentialSpawn >= 0 && so.RestrictKillsToSubgroup) { if (so.SubGroup == SequentialSpawn) total_removed++; } else { // just add it total_removed++; } } } } else if (o is BaseXmlSpawner.KeywordTag) { BaseXmlSpawner.KeywordTag tag = (BaseXmlSpawner.KeywordTag)o; if (tag.Deleted) { so.SpawnedObjects.Remove(o); x--; removed = true; } } else { // Don't know what this is, so remove it Console.WriteLine("removing unknown {0} from spawnlist", so); so.SpawnedObjects.Remove(o); x--; removed = true; } } } DeleteFromList(deleteilist, deletemlist); // Check if anything has been removed if (removed) InvalidateProperties(); // increment the killcount based upon the number of items that were removed from the spawnlist (i.e. were spawned but now are gone, presumed killed) if (killtest) m_killcount += total_removed; } // special defrag pass to remove GOTO keyword tags public void ClearGOTOTags() { if (m_SpawnObjects == null) return; List ToDelete = new List(); foreach (SpawnObject so in m_SpawnObjects) { for (int x = 0; x < so.SpawnedObjects.Count; x++) { object o = so.SpawnedObjects[x]; if (o is BaseXmlSpawner.KeywordTag) { BaseXmlSpawner.KeywordTag sot = (BaseXmlSpawner.KeywordTag)o; // clear the tags except for gump and delay tags if (sot.Type == 2) { ToDelete.Add(sot); so.SpawnedObjects.Remove(o); x--; } } } } for (int x = ToDelete.Count - 1; x >= 0; --x)//BaseXmlSpawner.KeywordTag i in ToDelete) { BaseXmlSpawner.KeywordTag i = ToDelete[x]; if (i != null && !i.Deleted) { i.Delete(); } } } // special defrag pass to remove spawn object tags, which are placeholders for the special keyword spawn spec entries public void ClearTags(bool all) { if (m_SpawnObjects == null) return; bool removed = false; List ToDelete = new List(); foreach (SpawnObject so in m_SpawnObjects) { for (int x = 0; x < so.SpawnedObjects.Count; x++) { object o = so.SpawnedObjects[x]; if (o is BaseXmlSpawner.KeywordTag) { BaseXmlSpawner.KeywordTag sot = (BaseXmlSpawner.KeywordTag)o; // clear the tags except for gump and delay tags if ((all || ((sot.Flags & BaseXmlSpawner.KeywordFlags.Defrag) != 0))) { ToDelete.Add(sot); so.SpawnedObjects.Remove(o); x--; removed = true; } } } } for (int x = ToDelete.Count - 1; x >= 0; --x)//each (BaseXmlSpawner.KeywordTag i in ToDelete) { BaseXmlSpawner.KeywordTag i = ToDelete[x]; if (i != null && !i.Deleted) { i.Delete(); } } // full clear of the taglist if (all) m_KeywordTagList.Clear(); // Check if anything has been removed if (removed) InvalidateProperties(); } public void DeleteGumpTags() { if (m_SpawnObjects == null) return; bool removed = false; List ToDelete = new List(); foreach (SpawnObject so in m_SpawnObjects) { for (int x = 0; x < so.SpawnedObjects.Count; x++) { object o = so.SpawnedObjects[x]; if (o is BaseXmlSpawner.KeywordTag) { BaseXmlSpawner.KeywordTag sot = (BaseXmlSpawner.KeywordTag)o; // clear the gump tags if (sot.Type == 1) { ToDelete.Add(sot); so.SpawnedObjects.Remove(o); x--; removed = true; } } } } for (int x = ToDelete.Count - 1; x >= 0; --x)//BaseXmlSpawner.KeywordTag i in ToDelete) { BaseXmlSpawner.KeywordTag i = ToDelete[x]; if (i != null && !i.Deleted) { i.Delete(); } } // Check if anything has been removed if (removed) InvalidateProperties(); } public void DeleteTag(BaseXmlSpawner.KeywordTag tag) { if (m_SpawnObjects == null) return; bool removed = false; List ToDelete = new List(); foreach (SpawnObject so in m_SpawnObjects) { for (int x = 0; x < so.SpawnedObjects.Count; x++) { object o = so.SpawnedObjects[x]; if (o is BaseXmlSpawner.KeywordTag) { BaseXmlSpawner.KeywordTag sot = (BaseXmlSpawner.KeywordTag)o; // clear the matching tags if (sot == tag) { ToDelete.Add(sot); so.SpawnedObjects.Remove(o); x--; removed = true; } } } } for (int x = ToDelete.Count - 1; x >= 0; --x)//BaseXmlSpawner.KeywordTag i in ToDelete) { BaseXmlSpawner.KeywordTag i = ToDelete[x]; if (i != null && !i.Deleted) { i.Delete(); } } // Check if anything has been removed if (removed) InvalidateProperties(); } #endregion #region SequentialSpawning methods private int SubGroupCount(int sgroup) { if (m_SpawnObjects == null) return (0); int nsub = 0; for (int i = 0; i < m_SpawnObjects.Count; i++) { SpawnObject s = m_SpawnObjects[i]; if (s.SubGroup == sgroup) nsub++; } return nsub; } private int RandomAvailableSpawnIndex() { // get spawn indices randomly from all available spawns independent of group return (RandomAvailableSpawnIndex(-1)); } // get spawn indices randomly from all available spawns of a group private int RandomAvailableSpawnIndex(int sgroup) { if (m_SpawnObjects == null) return (-1); int maxrange = 0; List sgrouplist = null; int totalcount = 0; // make a pass to determine which subgroups are available for spawning // by finding any subgroups that do not have available spawns for (int i = 0; i < m_SpawnObjects.Count; i++) { SpawnObject s = m_SpawnObjects[i]; if (s.SubGroup > 0 && (s.Ignore || s.Disabled)) continue; totalcount += s.SpawnedObjects.Count; if (s.SubGroup > 0 && s.SpawnedObjects.Count >= s.MaxCount) { // this subgroup is not available so add it to the list if (sgrouplist == null) { sgrouplist = new List(); } sgrouplist.Add(s.SubGroup); } } for (int i = 0; i < m_SpawnObjects.Count; i++) { SpawnObject s = m_SpawnObjects[i]; if (s.SubGroup > 0 && (s.Ignore || s.Disabled)) continue; if ((s.MaxCount > s.SpawnedObjects.Count) && (sgroup < 0 || sgroup == s.SubGroup) && (sgrouplist == null || !sgrouplist.Contains(s.SubGroup)) && (s.SubGroup <= 0 || SubGroupCount(s.SubGroup) + totalcount <= MaxCount)) { // keep track of the number of spawn objects that are not at max (hence available for spawning) // this will be used to compute the probabilistic weighting function based on the relative // maxcounts of each entry maxrange += s.MaxCount; s.Available = true; } else { s.Available = false; } } // now generate a random number over the available spawnobjects // but only if the entire subgroup is available for spawning // note, subgroup zero is exempt from this check. if (maxrange > 0) { int randindex = Utility.Random(maxrange); // and map it into the avail spawns int currentrange = 0; for (int i = 0; i < m_SpawnObjects.Count; i++) { SpawnObject s = m_SpawnObjects[i]; if (s.SubGroup > 0 && (s.Ignore || s.Disabled)) continue; // keep track of the number of spawn objects that are not at max (hence available for spawning) if (s.Available) { // check to see if the random value maps into the range of the current index if (randindex >= currentrange && randindex < currentrange + s.MaxCount) { return (i); } currentrange += s.MaxCount; } } // should never get here return (-1); } // no spawns are available return (-1); } // get spawn indices randomly from all available spawns of a group private int RandomSpawnIndex(int sgroup) { if (m_SpawnObjects == null) return (-1); int avail = 0; int maxrange = 0; for (int i = 0; i < m_SpawnObjects.Count; i++) { SpawnObject s = m_SpawnObjects[i]; // keep track of the number of spawn objects that are not at max (hence available for spawning) if (sgroup < 0 || (sgroup == s.SubGroup)) { avail++; maxrange += s.MaxCount; } } // now generate a random number over the available spawnobjects if (avail > 0 && maxrange > 0) { int randindex = Utility.Random(maxrange); // and map it into the avail spawns int currentrange = 0; for (int i = 0; i < m_SpawnObjects.Count; i++) { SpawnObject s = m_SpawnObjects[i]; // keep track of the number of spawn objects that are not at max (hence available for spawning) if (sgroup < 0 || (sgroup == s.SubGroup)) { if (randindex >= currentrange && randindex < currentrange + s.MaxCount) return (i); currentrange += s.MaxCount; } } // should never get here return (-1); } // no spawns are available return (-1); } // return the next subgroup in the sequence. public int NextSequentialIndex(int sgroup) { if (m_SpawnObjects == null || m_SpawnObjects.Count == 0) return (0); int finddirection = 1; int largergroup = -1; //find the next subgroup that is greater than the current one for (int j = 0; j < m_SpawnObjects.Count; j++) { SpawnObject s = m_SpawnObjects[j]; if (s.SubGroup > 0 && (s.Ignore || s.Disabled)) continue; int thisgroup = s.SubGroup; // start off by finding a subgroup that is larger if (finddirection == 1) { if (thisgroup > sgroup) { largergroup = thisgroup; // then work backward to find the group that is less than this but still larger than the current finddirection = -1; } } else { if ((thisgroup > sgroup) && (thisgroup < largergroup)) { largergroup = thisgroup; finddirection = -1; } } } // if couldnt find one larger, then it is time to wraparound if (largergroup < 0 && sgroup >= 0) return (NextSequentialIndex(-1)); else return (largergroup); } // returns the spawn index of a spawn entry in the current sequential subgroup public int GetCurrentAvailableSequentialSpawnIndex(int sgroup) { if (sgroup < 0) return (-1); if (m_SpawnObjects == null) return (-1); if (sgroup == 0) return (RandomAvailableSpawnIndex(0)); //return the first instance of a spawn object that is an available member of the requested subgroup for (int j = 0; j < m_SpawnObjects.Count; j++) { SpawnObject s = m_SpawnObjects[j]; if (s.SubGroup == sgroup && s.MaxCount > s.SpawnedObjects.Count) { return (j); } } // failed to find any spawn entry of the requested subgroup return (-1); } // returns the spawn index of a spawn entry in the current sequential subgroup public int GetCurrentSequentialSpawnIndex(int sgroup) { if (sgroup < 0) return (-1); if (m_SpawnObjects == null) return (-1); if (sgroup == 0) return (RandomSpawnIndex(0)); //return the first instance of a spawn object that is an available member of the requested subgroup for (int j = 0; j < m_SpawnObjects.Count; j++) { if (m_SpawnObjects[j].SubGroup == sgroup) { return (j); } } // failed to find any spawn entry of the requested subgroup return (-1); } private void SeqResetTo(int sgroup) { // check the SequentialResetTo on the subgroup // cant do resets on subgroup 0 if (sgroup == 0) return; // this will get the index of the first spawn entry in the subgroup // it will have the subgroup timer settings int spawnindex = GetCurrentSequentialSpawnIndex(sgroup); if (spawnindex >= 0) { // if it is greater than zero then initiate reset SpawnObject s = m_SpawnObjects[spawnindex]; m_SequentialSpawning = s.SequentialResetTo; InitiateSequentialReset(sgroup); // clear the spawns //RemoveSpawnObjects(); ClearSubgroup(s.SubGroup); // and reset the kill count KillCount = 0; } } private bool CheckForSequentialReset() { // check the SequentialResetTime on the subgroup // cant do resets on subgroup 0 if (m_SequentialSpawning == 0) return false; // this will get the index of the first spawn entry in the subgroup // it will have the subgroup timer settings int spawnindex = GetCurrentSequentialSpawnIndex(m_SequentialSpawning); if (spawnindex >= 0) { // check the reset time on it SpawnObject s = m_SpawnObjects[spawnindex]; // if it is greater than zero then resetting is possible if (s.SequentialResetTime > 0) { // so check the reset timer if (NextSeqReset <= TimeSpan.Zero) { // it has expired so time to reset return true; } } } return false; } private void InitiateSequentialReset(int sgroup) { // check the SequentialResetTime on the subgroup // cant do resets on subgroup 0 if (sgroup == 0) return; // this will get the index of the first spawn entry in the subgroup // it will have the subgroup timer settings int spawnindex = GetCurrentSequentialSpawnIndex(sgroup); if (spawnindex >= 0) { // if it is greater than zero then initiate reset SpawnObject s = m_SpawnObjects[spawnindex]; NextSeqReset = TimeSpan.FromMinutes(s.SequentialResetTime); } } public void ResetSequential() { // go back to the lowest level if (m_SequentialSpawning >= 0) { m_SequentialSpawning = NextSequentialIndex(-1); } // reset the nextspawn times ResetNextSpawnTimes(); // and reset the kill count KillCount = 0; } public bool AdvanceSequential() { // check for a sequence hold if (HoldSequence) return false; // check for triggering if (!((m_proximityActivated || CanFreeSpawn) && TODInRange)) return false; // if kills needed is greater than zero then check the killcount as well int spawnindex = GetCurrentSequentialSpawnIndex(m_SequentialSpawning); int killsneeded = 0; int subgroup = -1; bool clearedobjects = false; if (spawnindex >= 0) { SpawnObject s = m_SpawnObjects[spawnindex]; subgroup = s.SubGroup; killsneeded = s.KillsNeeded; } // advance the sequential spawn index if it is enabled and kills needed have been satisfied if (m_SequentialSpawning >= 0 && (killsneeded == 0 || KillCount >= killsneeded)) { m_SequentialSpawning = NextSequentialIndex(m_SequentialSpawning); // set the sequential reset based on the current sequence state // this will be checked in the spawner OnTick to determine whether to Reset the sequential state InitiateSequentialReset(m_SequentialSpawning); // clear the spawns if there is a killcount on the level if (killsneeded >= 0) { ClearSubgroup(subgroup); clearedobjects = true; } // and reset the kill count KillCount = 0; } // returning true will indicate that all spawns have been cleared and therefore a new spawn can be initiated in the same OnTick return (clearedobjects); } #endregion #region Spawn methods int killcount_held = 0; public void OnTick() { _TraceStart(8); // start up the timer again for the next Ontick DoTimer(); // reset the protection against runaway looping ClearSpawnedThisTick = true; // if regional spawning is enabled, update the region in case new regions were added after the initialization pass CheckRegionAssignment = true; // reset the killcount whenever a spawntick goes by in which it could have spawned, ie the spawner is full, or proximity triggered // spawns were not activated. Note that killcount gets incremented within Defrag whenever a spawn is that had been generated is removed from the active list. // Check the count before and then after the spawn passes. // if the spawner is still refractory then dont do a reset of the killcount. //int startcount = this.m_killcount; int startcount = killcount_held; if (!m_skipped) { killcount_held = m_killcount; } // killcount will be updated in Defrag Defrag(true); // remove any keyword tags that were made // note, tags only last a single ontick except for WAIT type ClearTags(false); if (!m_DisableGlobalAutoReset && startcount == m_killcount && !m_refractActivated && !m_skipped) { m_spawncheck--; } m_skipped = false; // allow for some slack in the killcount reset by resetting after a certain number of spawn ticks without kills pass if (m_spawncheck <= 0) { m_killcount = 0; m_spawncheck = m_KillReset; // wait for 1 spawn ticks to pass before resetting. This can be set to anything you like } // check for smart spawning if (SmartSpawning && IsFull && !HasActiveSectors && !HasDamagedOrDistantSpawns /*&& !HasHoldSmartSpawning */ ) { IsInactivated = true; // for multiple sector spawning ranges use the sector timer, otherwise just rely on OnSectorActivate to detect sector activation //if(!UseSectorActivate) //DoSectorTimer(TimeSpan.FromSeconds(1)); SmartRemoveSpawnObjects(); } // dont process spawn ticks while inactivated if smart spawning is enabled if (SmartSpawning && IsInactivated) { _TraceEnd(8); return; } IsInactivated = false; // check to see if spawning is on hold due to a WAIT keyword if (!OnHold) { // look for triggers that are not player activated. if (m_ProximityRange == -1 && CanSpawn) { CheckTriggers(null, null, false); } // check for proximity triggers without movement activation if (m_ProximityRange >= 0 && CanSpawn) { // check all nearby players IPooledEnumerable eable = GetMobilesInRange(m_ProximityRange); foreach (Mobile p in eable) { if (ValidPlayerTrig(p)) CheckTriggers(p, null, true); } eable.Free(); } if (m_Group) { // check the seq reset time on the current subgroup // if the reset time is greater than zero then check the timer // if it has expired then reset the sequential subgroup // only do this if it can actually spawn if (CheckForSequentialReset()) { // it has expired so reset the sequential spawn level SeqResetTo(m_SequentialSpawning); bool triedtospawn = Respawn(); if (triedtospawn) ClearGOTOTags(); // dont advance if the spawn isnt triggered after resetting if (!triedtospawn) HoldSequence = true; } else if (TotalSpawnedObjects <= 0) { // advance the sequential spawn index if it is enabled AdvanceSequential(); //bool hadhold = HoldSequence; //HoldSequence = false; bool triedtospawn = Respawn(); if (triedtospawn) ClearGOTOTags(); //if(!triedtospawn) HoldSequence = hadhold; } } else { if (CheckForSequentialReset()) { // it has expired so reset the sequential spawn level SeqResetTo(m_SequentialSpawning); // dont advance if the spawn isnt triggered after resetting HoldSequence = true; } else { // advance the sequence before spawning AdvanceSequential(); } // keep track of the hold flag before trying to spawn in case no spawn attempt is made //bool hadhold = HoldSequence; // clear the hold flag to see if any of the spawned entries try to set it //HoldSequence = false; // try to spawn. If spawning conditions such as triggering or TOD are not met, then it returns false bool triedtospawn = Spawn(false, 0); if (triedtospawn) ClearGOTOTags(); // this will maintain any sequential holds if spawning was suppressed due to triggering if (!FreeRun) { m_mob_who_triggered = null; } } // remove any keyword tags that were made except for WAIT type ClearTags(false); // and clear triggering flags if (!OnHold && !FreeRun) { m_proximityActivated = false; } } if (FreeRun && SpawnOnTrigger && m_proximityActivated) { // if it is in free run and was triggered, then just keep spawning as though it was triggered immediately NextSpawn = TimeSpan.Zero; ResetNextSpawnTimes(); } //this.m_ExternalTrigger = false; // if it is out of the TOD range then delete the spawns if (!TODInRange) { RemoveSpawnObjects(); ResetAllFlags(); } _TraceEnd(8); } public bool ClearSpawnedThisTick { set { if (m_SpawnObjects == null || value == false) return; for (int i = 0; i < m_SpawnObjects.Count; i++) { SpawnObject sobj = m_SpawnObjects[i]; if (sobj != null) { sobj.SpawnedThisTick = false; } } } } // select and spawn something // return false if it cannot spawn, e.g. there is nothing to spawn or it is a triggerable spawner and has not been triggered public bool Spawn(bool smartspawn, byte loops) { if (m_SpawnObjects != null && m_SpawnObjects.Count > 0 && (m_proximityActivated || CanFreeSpawn) && TODInRange) { m_HoldSequence = false; // if the spawner is full then dont bother if (IsFull) { ResetProximityActivated(); return true; } // Pick a spawn object to spawn int SpawnIndex; // see if sequential spawning has been selected SpawnIndex = m_SequentialSpawning >= 0 ? GetCurrentAvailableSequentialSpawnIndex(m_SequentialSpawning) : RandomAvailableSpawnIndex(); // no spawns are available so no point in continuing if (SpawnIndex < 0) { ResetProximityActivated(); return true; } SpawnObject sobj = m_SpawnObjects[SpawnIndex]; int sgroup = sobj.SubGroup; // if this is part of a non-zero group, then spawn all of the group members as well if (sgroup != 0) { SpawnSubGroup(sgroup, smartspawn, loops); } else { // Found a valid spawn object so spawn it and see if it successful if (Spawn(SpawnIndex, smartspawn, sobj.SpawnsPerTick, loops)) { if (!smartspawn) RefreshNextSpawnTime(sobj); } } ResetProximityActivated(); return true; } ResetProximityActivated(); return false; } // spawn an individual entry by index up to count times public bool Spawn(int index, bool smartspawn, int count, int packrange, Point3D packcoord, bool ignoreloopprotection, byte loops) { if (m_SpawnObjects == null || index >= m_SpawnObjects.Count) return false; bool didspawn = false; SpawnObject so = m_SpawnObjects[index]; if (so == null) return false; Defrag(false); // make sure you dont go over the individual entry maxcount int somax = so.MaxCount; int socnt = so.SpawnedObjects.Count; int nspawn = so.SpawnsPerTick; int scnt = SafeCurrentCount; for (int k = 0; (k < nspawn) && (k + socnt < somax) && (k + scnt < MaxCount); k++) { if (packrange >= 0 && so.SubGroup > 0 && packcoord == Point3D.Zero) { packcoord = GetPackCoord(so.SubGroup); } if (Spawn(index, smartspawn, packrange, packcoord, ignoreloopprotection, loops)) { // if any of the attempts were successful then flag it as having spawned didspawn = true; } } return didspawn; } // spawn an individual entry by index up to count times public bool Spawn(int index, bool smartspawn, int count, byte loops) { return Spawn(index, smartspawn, count, false, loops); } // spawn an individual entry by index up to count times public bool Spawn(int index, bool smartspawn, int count, bool ignoreloopprotection, byte loops) { return Spawn(index, smartspawn, count, -1, Point3D.Zero, ignoreloopprotection, loops); } // spawn an individual entry by spawn object public void Spawn(string SpawnObjectTypeName, bool smartspawn, int packrange, Point3D packcoord, byte loops) { if (m_SpawnObjects == null) return; for (int i = 0; i < m_SpawnObjects.Count; i++) { if (m_SpawnObjects[i].TypeName.ToUpper() == SpawnObjectTypeName.ToUpper()) { if (Spawn(i, smartspawn, packrange, packcoord, loops)) { RefreshNextSpawnTime(m_SpawnObjects[i]); } break; } } } // spawn an individual entry by index public void Spawn(string SpawnObjectTypeName, bool smartspawn, byte loops) { Spawn(SpawnObjectTypeName, smartspawn, -1, Point3D.Zero, loops); } // spawn an individual entry by index public bool Spawn(int index, bool smartspawn, int packrange, Point3D packcoord, byte loops) { return Spawn(index, smartspawn, packrange, packcoord, false, loops); } // spawn an individual entry by index public bool Spawn(int index, bool smartspawn, int packrange, Point3D packcoord, bool ignoreloopprotection, byte loops) { Map map = Map; // Make sure everything is ok to spawn an object if ((map == null) || (map == Map.Internal) || (m_SpawnObjects == null) || (m_SpawnObjects.Count == 0) || (index < 0) || (index >= m_SpawnObjects.Count) ) return false; // Remove any spawns that don't belong to the spawner any more. Defrag(false); // Get the spawn object at the required index SpawnObject TheSpawn = m_SpawnObjects[index]; // Check if the object retrieved is a valid SpawnObject if (TheSpawn != null) { // dont allow an entry to be spawned more than once per tick // this protects against runaway recursive looping if (TheSpawn.SpawnedThisTick && !ignoreloopprotection) return false; // check the nextspawn time to see if it is available if (TheSpawn.NextSpawn > DateTime.UtcNow) return false; int CurrentCreatureMax = TheSpawn.MaxCount; int CurrentCreatureCount = TheSpawn.SpawnedObjects.Count; // Check that the current object to be spawned has not reached its maximum allowed // and make sure that the maximum spawner count has not been exceeded as well if ((CurrentCreatureCount >= CurrentCreatureMax) || (TotalSpawnedObjects >= m_Count)) { return false; } // check for string substitions string substitutedtypeName = BaseXmlSpawner.ApplySubstitution(this, this, TheSpawn.TypeName); // random positioning is the default List spawnpositioning = null; // require valid surfaces by default bool requiresurface = true; // parse the # function specification for the entry while (substitutedtypeName.StartsWith("#")) { string[] args = BaseXmlSpawner.ParseSemicolonArgs(substitutedtypeName, 2); if (args.Length > 0) { if (spawnpositioning == null) { spawnpositioning = new List(); } // parse any comma args string[] keyvalueargs = BaseXmlSpawner.ParseCommaArgs(args[0], 10); if (keyvalueargs.Length > 0) { switch (keyvalueargs[0]) { case "#NOITEMID": spawnpositioning.Add(new SpawnPositionInfo(SpawnPositionType.NoItemID, m_mob_who_triggered, keyvalueargs)); break; case "#ITEMID": spawnpositioning.Add(new SpawnPositionInfo(SpawnPositionType.ItemID, m_mob_who_triggered, keyvalueargs)); break; case "#NOTILES": spawnpositioning.Add(new SpawnPositionInfo(SpawnPositionType.NoTiles, m_mob_who_triggered, keyvalueargs)); break; case "#TILES": spawnpositioning.Add(new SpawnPositionInfo(SpawnPositionType.Tiles, m_mob_who_triggered, keyvalueargs)); break; case "#WET": spawnpositioning.Add(new SpawnPositionInfo(SpawnPositionType.Wet, m_mob_who_triggered, keyvalueargs)); break; case "#XFILL": spawnpositioning.Add(new SpawnPositionInfo(SpawnPositionType.RowFill, m_mob_who_triggered, keyvalueargs)); break; case "#YFILL": spawnpositioning.Add(new SpawnPositionInfo(SpawnPositionType.ColFill, m_mob_who_triggered, keyvalueargs)); break; case "#EDGE": spawnpositioning.Add(new SpawnPositionInfo(SpawnPositionType.Perimeter, m_mob_who_triggered, keyvalueargs)); break; case "#PLAYER": spawnpositioning.Add(new SpawnPositionInfo(SpawnPositionType.Player, m_mob_who_triggered, keyvalueargs)); break; case "#WAYPOINT": spawnpositioning.Add(new SpawnPositionInfo(SpawnPositionType.Waypoint, m_mob_who_triggered, keyvalueargs)); break; case "#RELXY": spawnpositioning.Add(new SpawnPositionInfo(SpawnPositionType.RelXY, m_mob_who_triggered, keyvalueargs)); break; case "#DXY": spawnpositioning.Add(new SpawnPositionInfo(SpawnPositionType.DeltaLocation, m_mob_who_triggered, keyvalueargs)); break; case "#XY": spawnpositioning.Add(new SpawnPositionInfo(SpawnPositionType.Location, m_mob_who_triggered, keyvalueargs)); break; case "#CONDITION": // test the specified condition string // syntax is #CONDITION,proptest // reparse with only one arg after the comma, this allows property tests that use commas as well string[] ckeyvalueargs = BaseXmlSpawner.ParseCommaArgs(args[0], 2); if (ckeyvalueargs.Length > 1) { // dont spawn if it fails the test if (!BaseXmlSpawner.CheckPropertyString(this, this, ckeyvalueargs[1], out status_str)) return false; } else { status_str = "invalid #CONDITION specification: " + args[0]; } break; default: status_str = "invalid # specification: " + args[0]; break; } } } // get the rest of the spawn entry substitutedtypeName = args.Length > 1 ? args[1].Trim() : string.Empty; } if (substitutedtypeName.StartsWith("*")) { requiresurface = false; substitutedtypeName = substitutedtypeName.TrimStart('*'); } TheSpawn.RequireSurface = requiresurface; string typeName = BaseXmlSpawner.ParseObjectType(substitutedtypeName); if (BaseXmlSpawner.IsTypeOrItemKeyword(typeName)) { string status_str = null; bool completedtypespawn = BaseXmlSpawner.SpawnTypeKeyword(this, TheSpawn, typeName, substitutedtypeName, m_mob_who_triggered, Map, out status_str, loops); if (status_str != null) { this.status_str = status_str; } if (completedtypespawn) { // successfully spawned the keyword // note that returning true means that Spawn will assume that it worked and will not try to respawn something else // added the duration timer that begins on spawning DoTimer2(m_Duration); InvalidateProperties(); return true; } return false; } // its a regular type descriptor so find out what it is Type type = SpawnerType.GetType(typeName); // dont try to spawn invalid types, or Mobile type spawns in containers if (type != null && !(Parent != null && (type == typeof(Mobile) || type.IsSubclassOf(typeof(Mobile))))) { string[] arglist = BaseXmlSpawner.ParseString(substitutedtypeName, 3, "/"); object o = CreateObject(type, arglist[0]); if (o == null) { status_str = "invalid type specification: " + arglist[0]; return true; } try { if (o is Mobile) { // if this is in any container such as a pack the xyz values are invalid as map coords so dont spawn the mob if (Parent is Container) { ((Mobile)o).Delete(); return true; } Mobile m = (Mobile)o; // add the mobile to the spawned list TheSpawn.SpawnedObjects.Add(m); m.Spawner = this; var loc = GetSpawnPosition(requiresurface, packrange, packcoord, spawnpositioning, m); if (!smartspawn) { m.OnBeforeSpawn(loc, map); } m.MoveToWorld(loc, map); if (m is BaseCreature) { BaseCreature c = (BaseCreature)m; c.RangeHome = m_HomeRange; c.CurrentWayPoint = m_WayPoint; if (m_Team > 0) c.Team = m_Team; // Check if this spawner uses absolute (from spawnER location) // or relative (from spawnED location) as the mobiles home point c.Home = m_HomeRangeIsRelative ? m.Location : Location; } // if the object has an OnSpawned method, then invoke it if (!smartspawn) { m.OnAfterSpawn(); } // apply the parsed arguments from the typestring using setcommand // be sure to do this after setting map and location so that errors dont place the mob on the internal map string status_str; BaseXmlSpawner.ApplyObjectStringProperties(this, substitutedtypeName, m, m_mob_who_triggered, this, out status_str); if (status_str != null) { this.status_str = status_str; } InvalidateProperties(); // added the duration timer that begins on spawning DoTimer2(m_Duration); return true; } if (o is Item) { Item item = (Item)o; string status_str; BaseXmlSpawner.AddSpawnItem(this, TheSpawn, item, Location, map, m_mob_who_triggered, requiresurface, spawnpositioning, substitutedtypeName, smartspawn, out status_str); if (status_str != null) { this.status_str = status_str; } InvalidateProperties(); // added the duration timer that begins on spawning DoTimer2(m_Duration); return true; } } catch (Exception ex) { Console.WriteLine("When spawning {0}, {1}", o, ex); } } else { status_str = "invalid type specification: " + typeName; return true; } } return false; } public bool SpawnSubGroup(int sgroup, byte loops) { return SpawnSubGroup(sgroup, false, loops); } public bool SpawnSubGroup(int sgroup, bool smartspawn, byte loops) { return SpawnSubGroup(sgroup, false, false, loops); } public bool SpawnSubGroup(int sgroup, bool smartspawn, bool ignoreloopprotection, byte loops) { if (m_SpawnObjects == null) return false; if (sgroup >= 0) { bool didspawn = false; Point3D packcoord = Point3D.Zero; for (int j = 0; j < m_SpawnObjects.Count; j++) { SpawnObject so = m_SpawnObjects[j]; if (so != null && so.SubGroup == sgroup) { // find the first subgroup spawn to determine the packspawning reference coordinates if (so.PackRange >= 0 && packcoord == Point3D.Zero) { packcoord = GetPackCoord(sgroup); } // get the SpawnsPerTick count and spawn up to that number bool success = Spawn(j, smartspawn, so.SpawnsPerTick, so.PackRange, packcoord, ignoreloopprotection, loops); if (success) didspawn = true; if (success && !smartspawn) RefreshNextSpawnTime(so); } } // success if any of the subgroup spawned if (didspawn) { return (true); } } return (false); } #endregion #region Spawn support methods public Point3D GetPackCoord(int sgroup) { for (int j = 0; j < m_SpawnObjects.Count; j++) { SpawnObject so = m_SpawnObjects[j]; if (so != null && so.SubGroup == sgroup && so.SpawnedObjects.Count > 0 && so.PackRange >= 0) { // if pack spawning is enabled for this subgroup, then get the // the origin for pack spawning using the first existing pack spawn // in the subgroup for (int i = 0; i < so.SpawnedObjects.Count; ++i) { object o = so.SpawnedObjects[i]; if (o is Item) { return ((Item)o).Location; } if (o is Mobile) { return ((Mobile)o).Location; } } } } return Point3D.Zero; } //used by the reset button in the gump public void ResetAllFlags() { m_proximityActivated = false; m_ExternalTrigger = false; m_durActivated = false; m_refractActivated = false; m_mob_who_triggered = null; m_killcount = 0; m_GumpState = null; FreeRun = false; } public bool BringHome { set { if (value) BringToHome(); } } public void BringToHome() { if (m_SpawnObjects == null) return; Defrag(false); foreach (SpawnObject so in m_SpawnObjects) { for (int i = 0; i < so.SpawnedObjects.Count; ++i) { object o = so.SpawnedObjects[i]; if (o is Mobile) { Mobile m = (Mobile)o; m.Map = Map; m.Location = new Point3D(Location); } else if (o is Item) { Item item = (Item)o; item.MoveToWorld(Location, Map); } } } } public bool CheckRegionAssignment { get { return false; } set { if (value) { // see if a region definition needs updating if (m_Region == null && m_RegionName != null && RegionName != string.Empty) { RegionName = RegionName; if (SpawnRegion != null) { // clear the status if successful status_str = null; } } } } } public void Start() { if (m_Running == false) { if (m_SpawnObjects != null && m_SpawnObjects.Count > 0) { m_Running = true; DoTimer(); } } } public void Stop() { if (m_Running) { // turn off all timers if (m_Timer != null) m_Timer.Stop(); if (m_DurTimer != null) m_DurTimer.Stop(); if (m_RefractoryTimer != null) m_RefractoryTimer.Stop(); m_Running = false; m_proximityActivated = false; m_ExternalTrigger = false; m_mob_who_triggered = null; } } public void Reset() { Stop(); // reset the protection against runaway looping ClearSpawnedThisTick = true; RemoveSpawnObjects(); ClearTags(true); ResetAllFlags(); status_str = ""; m_killcount = 0; OnHold = false; mostRecentSpawnPosition = Point3D.Zero; spawnPositionWayTable = null; // dont advance before the next spawn HoldSequence = true; IsInactivated = false; ResetSequential(); } public bool Respawn() { inrespawn = true; IsInactivated = false; // reset the protection against runaway looping ClearSpawnedThisTick = true; // Delete all currently spawned objects RemoveSpawnObjects(); // added the explicit start. Previously it relied on the automatic start that occurred when the spawnobject list was updated. Start(); ResetNextSpawnTimes(); // Respawn all objects up to the spawners current maximum allowed // note that by default, for proximity sensing, the spawner will only trigger once, but for respawns allow them all bool keepProximityActivated = m_proximityActivated; bool triedtospawn = false; // attempt to spawn up to the MaxCount of the spawner for (int x = 0; x < m_Count; x++) { triedtospawn = Spawn(false, 0); if (x < m_Count - 1 || OnHold) m_proximityActivated = keepProximityActivated; } if (!FreeRun) { m_mob_who_triggered = null; } ClearTags(true); inrespawn = false; return triedtospawn; } // used to optimize smartspawning use of hasholdsmartspawning public void SmartRespawn() { inrespawn = true; IsInactivated = false; // reset the protection against runaway looping ClearSpawnedThisTick = true; // Delete all currently spawned objects SmartRemoveSpawnObjects(); // added the explicit start. Previously it relied on the automatic start that occurred when the spawnobject list was updated. Start(); ResetNextSpawnTimes(); // Respawn all objects up to the spawners current maximum allowed // note that by default, for proximity sensing, the spawner will only trigger once, but for respawns allow them all bool keepProximityActivated = m_proximityActivated; // attempt to spawn up to the MaxCount of the spawner for (int x = 0; x < m_Count; x++) { Spawn(true, 0); if (x < m_Count - 1 || OnHold) m_proximityActivated = keepProximityActivated; } if (!FreeRun) { m_mob_who_triggered = null; } ClearTags(true); inrespawn = false; } public void SortSpawns() { if (m_SpawnObjects == null) return; // establish the entry order int count = 0; foreach (SpawnObject so in m_SpawnObjects) { so.EntryOrder = count++; } m_SpawnObjects.Sort(new SubgroupSorter()); } private class SubgroupSorter : IComparer { public int Compare(SpawnObject a, SpawnObject b) { if (a.SubGroup == b.SubGroup) { // use the entry order as the secondary sort factor return a.EntryOrder - b.EntryOrder; } return a.SubGroup - b.SubGroup; } } public static SpawnObject GetSpawnObject(XmlSpawner spawner, int sgroup) { if (spawner == null || spawner.m_SpawnObjects == null) return (null); for (int i = 0; i < spawner.m_SpawnObjects.Count; i++) { // find the first entry with matching subgroup id if (spawner.m_SpawnObjects[i].SubGroup == sgroup) { return spawner.m_SpawnObjects[i]; } } return null; } public static object GetSpawned(XmlSpawner spawner, int sgroup) { if (spawner == null || spawner.m_SpawnObjects == null) return (null); for (int i = 0; i < spawner.m_SpawnObjects.Count; i++) { // find the first entry with matching subgroup id if (spawner.m_SpawnObjects[i].SubGroup == sgroup) { // find the first spawned object in the entry if (spawner.m_SpawnObjects[i].SpawnedObjects.Count > 0) { return spawner.m_SpawnObjects[i].SpawnedObjects[0]; } } } return null; } public static List GetSpawnedList(XmlSpawner spawner, int sgroup) { List newlist = new List(); if (spawner == null || spawner.m_SpawnObjects == null) return (null); for (int i = 0; i < spawner.m_SpawnObjects.Count; i++) { // find the first entry with matching subgroup id if (spawner.m_SpawnObjects[i].SubGroup == sgroup) { // find the first spawned object in the entry if (spawner.m_SpawnObjects[i].SpawnedObjects.Count > 0) { for (int j = 0; j < spawner.m_SpawnObjects[i].SpawnedObjects.Count; j++) newlist.Add(spawner.m_SpawnObjects[i].SpawnedObjects[j]); } } } return newlist; } public bool HasSubGroups() { if (m_SpawnObjects == null) return (false); for (int j = 0; j < m_SpawnObjects.Count; j++) { if (m_SpawnObjects[j].SubGroup > 0) return true; } return false; } private void ResetProximityActivated() { // dont reset triggering if free run mode has been selected if (!FreeRun) { m_proximityActivated = false; } } public bool HasIndividualSpawnTimes() { if (m_SpawnObjects != null && m_SpawnObjects.Count > 0) { for (int i = 0; i < m_SpawnObjects.Count; i++) { SpawnObject so = m_SpawnObjects[i]; if (so.MinDelay != -1 || so.MaxDelay != -1) return true; } } return false; } private void ResetNextSpawnTimes() { if (m_SpawnObjects != null && m_SpawnObjects.Count > 0) { for (int i = 0; i < m_SpawnObjects.Count; i++) { SpawnObject so = m_SpawnObjects[i]; so.NextSpawn = DateTime.UtcNow; } } } public void RefreshNextSpawnTime(SpawnObject so) { if (so == null) return; int mind = (int)(so.MinDelay * 60); int maxd = (int)(so.MaxDelay * 60); if (mind < 0 || maxd < 0) { so.NextSpawn = DateTime.UtcNow; } else { TimeSpan delay = TimeSpan.FromSeconds(Utility.RandomMinMax(mind, maxd)); so.NextSpawn = DateTime.UtcNow + delay; } } public static bool IsValidMapLocation(int X, int Y, Map map) { if (map == null || map == Map.Internal) return false; // check the location relative to the current map to make sure it is valid if (X < 0 || X > map.Width || Y < 0 || Y > map.Height) { return false; } return true; } public static bool IsValidMapLocation(Point3D location, Map map) { if (map == null || map == Map.Internal) return false; // check the location relative to the current map to make sure it is valid if (location.X < 0 || location.X > map.Width || location.Y < 0 || location.Y > map.Height) { return false; } return true; } public static bool IsValidMapLocation(Point2D location, Map map) { if (map == null || map == Map.Internal) return false; // check the location relative to the current map to make sure it is valid if (location.X < 0 || location.X > map.Width || location.Y < 0 || location.Y > map.Height) { return false; } return true; } private static WayPoint GetWaypoint(string waypointstr) { WayPoint waypoint = null; // try parsing the waypoint name to determine the waypoint. object syntax is "SERIAL,sernumber" or "waypointname" if (!string.IsNullOrEmpty(waypointstr)) { string[] wayargs = BaseXmlSpawner.ParseString(waypointstr, 2, ","); if (wayargs != null && wayargs.Length > 0) { // is this a SERIAL specification? if (wayargs[0] == "SERIAL") { // look it up by serial if (wayargs.Length > 1) { int sernum = -1; try { sernum = (int)Convert.ToUInt64(wayargs[1].Substring(2), 16); } catch { } if (sernum > -1) { IEntity e = World.FindEntity(sernum); if (e is WayPoint) waypoint = e as WayPoint; } } } else { // just look it up by name Item wayitem = BaseXmlSpawner.FindItemByName(null, wayargs[0], "WayPoint"); if (wayitem is WayPoint) { waypoint = wayitem as WayPoint; } } } } return waypoint; } private static bool HasTileSurface(Map map, int X, int Y, int Z) { if (map == null) return false; StaticTile[] tiles = map.Tiles.GetStaticTiles(X, Y, true); if (tiles == null) return false; // go through the tiles and see if any are at the Z location foreach (StaticTile o in tiles) { StaticTile i = o; if ((i.Z + i.Height) == Z) { return true; } } return false; } private bool CheckHoldSmartSpawning(object o) { if (o == null) return false; // try looking this up in the lookup table if (holdSmartSpawningHash == null) { holdSmartSpawningHash = new Dictionary(); } PropertyInfo prop; if (!holdSmartSpawningHash.TryGetValue(o.GetType(), out prop)) { prop = o.GetType().GetProperty("HoldSmartSpawning"); // check to make sure the HoldSmartSpawning property for this object has the right type if (prop != null && (!prop.CanRead || prop.PropertyType != typeof(bool))) { prop = null; } holdSmartSpawningHash[o.GetType()] = prop; } if (prop != null) { try { return (bool)(prop.GetValue(o, null)); } catch { } } return false; } public bool HasHoldSmartSpawning { get { // go through the spawn lists foreach (SpawnObject so in m_SpawnObjects) { for (int x = 0; x < so.SpawnedObjects.Count; x++) { object o = so.SpawnedObjects[x]; if (CheckHoldSmartSpawning(o)) { return true; } } } return false; } } // if a non-null mob argument is passed, then check the canswim and cantwalk props to determine valid placement public bool CanFit(int x, int y, int z, int height, bool checkBlocksFit, bool checkMobiles, bool requireSurface, Mobile mob) { Map map = Map; if (DebugThis) { Console.WriteLine("CanFit mob {0}, map={1}", mob, map); } if (map == null || map == Map.Internal) return false; if (x < 0 || y < 0 || x >= map.Width || y >= map.Height) return false; bool hasSurface = false; bool checkmob = false; bool canswim = false; bool cantwalk = false; if (mob != null) { checkmob = true; canswim = mob.CanSwim; cantwalk = mob.CantWalk; } if (DebugThis) { Console.WriteLine("fitting mob {0} checkmob={1} swim={2} walk={3}", mob, checkmob, canswim, cantwalk); } LandTile lt = map.Tiles.GetLandTile(x, y); int lowZ = 0, avgZ = 0, topZ = 0; bool surface; bool wet = false; map.GetAverageZ(x, y, ref lowZ, ref avgZ, ref topZ); TileFlag landFlags = TileData.LandTable[lt.ID & TileData.MaxLandValue].Flags; if (DebugThis) { Console.WriteLine("landtile at {0},{1},{2} lowZ={3} avgZ={4} topZ={5}", x, y, z, lowZ, avgZ, topZ); } var impassable = (landFlags & TileFlag.Impassable) != 0; if (checkmob) { wet = (landFlags & TileFlag.Wet) != 0; // dont allow wateronly creatures on land if (cantwalk && !wet) impassable = true; // allow water creatures on water if (canswim && wet) { impassable = false; } } if (impassable && avgZ > z && (z + height) > lowZ) return false; if (!impassable && z == avgZ && !lt.Ignored) hasSurface = true; if (DebugThis) { Console.WriteLine("landtile at {0},{1},{2} wet={3} impassable={4} hassurface={5}", x, y, z, wet, impassable, hasSurface); } StaticTile[] staticTiles = map.Tiles.GetStaticTiles(x, y, true); for (int i = 0; i < staticTiles.Length; ++i) { ItemData id = TileData.ItemTable[staticTiles[i].ID & TileData.MaxItemValue]; surface = id.Surface; impassable = id.Impassable; if (checkmob) { wet = (id.Flags & TileFlag.Wet) != 0; // dont allow wateronly creatures on land if (cantwalk && !wet) impassable = true; // allow water creatures on water if (canswim && wet) { surface = true; impassable = false; } } if ((surface || impassable) && (staticTiles[i].Z + id.CalcHeight) > z && (z + height) > staticTiles[i].Z) return false; if (surface && !impassable && z == (staticTiles[i].Z + id.CalcHeight)) hasSurface = true; } if (DebugThis) { Console.WriteLine("statics hassurface={0}", hasSurface); } Sector sector = map.GetSector(x, y); List items = sector.Items; List mobs = sector.Mobiles; for (int i = 0; i < items.Count; ++i) { Item item = items[i]; if (item.ItemID < 0x4000 && item.AtWorldPoint(x, y)) { ItemData id = item.ItemData; surface = id.Surface; impassable = id.Impassable; if (checkmob) { wet = (id.Flags & TileFlag.Wet) != 0; // dont allow wateronly creatures on land if (cantwalk && !wet) impassable = true; // allow water creatures on water if (canswim && wet) { surface = true; impassable = false; } } if ((surface || impassable || (checkBlocksFit && item.BlocksFit)) && (item.Z + id.CalcHeight) > z && (z + height) > item.Z) return false; if (surface && !impassable && !item.Movable && z == (item.Z + id.CalcHeight)) hasSurface = true; } } if (DebugThis) { Console.WriteLine("items hassurface={0}", hasSurface); } if (checkMobiles) { for (int i = 0; i < mobs.Count; ++i) { Mobile m = mobs[i]; if (m.Location.X == x && m.Location.Y == y && (m.AccessLevel == AccessLevel.Player || !m.Hidden)) if ((m.Z + 16) > z && (z + height) > m.Z) return false; } } if (DebugThis) { Console.WriteLine("return requiresurface={0} hassurface={1}", requireSurface, hasSurface); } return !requireSurface || hasSurface; } public bool CanSpawnMobile(int x, int y, int z, Mobile mob) { if (DebugThis) { Console.WriteLine("CanSpawnMobile mob {0}", mob); } if (!Region.Find(new Point3D(x, y, z), Map).AllowSpawn()) return false; return Map.CanFit(x, y, z, 16, false, true, true, mob); } public bool HasRegionPoints(Region r) { if (r != null && r.Area.Length > 0) return true; else return false; } public Rectangle2D SpawnerBounds => new Rectangle2D(m_X, m_Y, m_Width + 1, m_Height + 1); private void FindTileLocations(ref List locations, Map map, int startx, int starty, int width, int height, List includetilelist, List excludetilelist, TileFlag tileflag, bool checkitems, int spawnerZ) { if (width < 0 || height < 0 || map == null) return; if (locations == null) locations = new List(); bool includetile; bool excludetile; for (int x = startx; x <= startx + width; x++) { for (int y = starty; y <= starty + height; y++) { bool allok = false; Point3D p = Point3D.Zero; // go through all of the tiles at the location and find those that are in the allowed tiles list LandTile ltile = map.Tiles.GetLandTile(x, y); TileFlag lflags = TileData.LandTable[ltile.ID & TileData.MaxLandValue].Flags; // check the land tile if (includetilelist != null && includetilelist.Count > 0) { includetile = includetilelist.Contains(ltile.ID & TileData.MaxLandValue); } else { includetile = true; } // non-excluded tiles must also be passable if (excludetilelist != null && excludetilelist.Count > 0) { // also require the tile to be passable excludetile = ((lflags & TileFlag.Impassable) != 0) || excludetilelist.Contains(ltile.ID & TileData.MaxLandValue); } else { excludetile = false; } if (includetile && !excludetile && ((lflags & tileflag) == tileflag)) { p = new Point3D(x, y, ltile.Z + ltile.Height); allok = true; } StaticTile[] statictiles = map.Tiles.GetStaticTiles(x, y, true); // check the static tiles for (int i = 0; i < statictiles.Length; ++i) { StaticTile stile = statictiles[i]; TileFlag sflags = TileData.ItemTable[stile.ID & TileData.MaxItemValue].Flags; if (includetilelist != null && includetilelist.Count > 0) { includetile = includetilelist.Contains(stile.ID & TileData.MaxItemValue); } else { includetile = true; } // non-excluded tiles must also be passable if (excludetilelist != null && excludetilelist.Count > 0) { excludetile = ((sflags & TileFlag.Impassable) != 0) || excludetilelist.Contains(stile.ID & TileData.MaxItemValue); } else { excludetile = false; } if (includetile && !excludetile && ((sflags & tileflag) == tileflag)) { //Console.WriteLine("found statictile {0}/{1} at {2},{3},{4}", stile.ID, stile.ID & 0x3fff, x, y, stile.Z + stile.Height); if (p == Point3D.Zero) p = new Point3D(x, y, stile.Z + stile.Height); else if (!allok && (p.Z - spawnerZ) > Math.Abs(stile.Z - spawnerZ)) p = new Point3D(x, y, stile.Z + stile.Height); else if (Math.Abs(ltile.Z - spawnerZ) > Math.Abs(stile.Z - spawnerZ)) //maggiore distanza rispetto allo statico dallo spawner p = new Point3D(x, y, stile.Z + stile.Height); allok = true; //locations.Add(new Point3D(x, y, stile.Z + stile.Height)); //break; } } if (checkitems) { IPooledEnumerable itemslist = map.GetItemsInRange(new Point3D(x, y, 0), 0); // check the itemsid foreach (Item i in itemslist) { if (i.ItemData.Impassable) excludetile = true; TileFlag iflags = TileData.ItemTable[i.ItemID & TileData.MaxItemValue].Flags; if (includetilelist != null && includetilelist.Count > 0) { includetile = includetilelist.Contains(i.ItemID & TileData.MaxItemValue); } else { includetile = true; } if (excludetilelist != null && excludetilelist.Count > 0) { excludetile = excludetilelist.Contains(i.ItemID & TileData.MaxItemValue); } else { excludetile = false; } if (includetile && !excludetile && ((iflags & tileflag) == tileflag)) { p = new Point3D(x, y, i.Z + i.ItemData.Height); allok = true; } } itemslist.Free(); } if (allok && !excludetile) locations.Add(p); } } } private void FindRegionTileLocations(ref List locations, Region r, List includetilelist, List excludetilelist, TileFlag tileflag, bool checkitems, int spawnerZ) { if (r == null || r.Area == null) return; int count = r.Area.Length; if (locations == null) locations = new List(); // calculate fields of all rectangles (for probability calculating) for (int n = 0; n < count; n++) { Rectangle3D ra = r.Area[n]; int sx = ra.Start.X; int sy = ra.Start.Y; int w = ra.Width; int h = ra.Height; // find all of the valid tile locations in the area FindTileLocations(ref locations, r.Map, sx, sy, w, h, includetilelist, excludetilelist, tileflag, checkitems, spawnerZ); } } public Point2D GetRandomRegionPoint(Region r) { int count = r.Area.Length; int[] FieldArray = new int[count]; int total = 0; // calculate fields of all rectangles (for probability calculating) for (int i = 0; i < count; i++) { Rectangle3D ra = r.Area[i]; total += (FieldArray[i] = (ra.Width * ra.Height)); } int sum = 0; int rnd = 0; if (total > 0) rnd = Utility.Random(total); int x = 0; int y = 0; for (int i = 0; i < count; i++) { sum += FieldArray[i]; if (sum > rnd) { Rectangle3D r3d = r.Area[i]; if (r3d.Width >= 0) x = r3d.Start.X + Utility.Random(r3d.Width); if (r3d.Height >= 0) y = r3d.Start.Y + Utility.Random(r3d.Height); break; } } return new Point2D(x, y); } // used for getting non-mobile spawn positions public Point3D GetSpawnPosition(bool requiresurface) { // no pack spawning return GetSpawnPosition(requiresurface, -1, Point3D.Zero, null, null); } // used for getting mobile spawn positions public Point3D GetSpawnPosition(bool requiresurface, Mobile mob) { // no pack spawning return GetSpawnPosition(requiresurface, -1, Point3D.Zero, null, mob); } // used for getting non-mobile spawn positions public Point3D GetSpawnPosition(bool requiresurface, int packrange, Point3D packcoord, List spawnpositioning) { return GetSpawnPosition(requiresurface, packrange, packcoord, spawnpositioning, null); } public Point3D GetSpawnPosition(bool requiresurface, int packrange, Point3D packcoord, List spawnpositioning, Mobile mob) { Map map = Map; if (map == null) return Location; // random positioning by default SpawnPositionType positioning = SpawnPositionType.Random; Mobile trigmob = null; List includetilelist = null; List excludetilelist = null; bool checkitems = false; // restrictions on tile flags TileFlag tileflag = TileFlag.None; List locations = null; int fillinc = 1; int positionrange = 0; string prefix = null; List WayList = null; int xinc = 0; int yinc = 0; int zinc = 0; if (spawnpositioning != null) { foreach (SpawnPositionInfo s in spawnpositioning) { if (s == null) continue; trigmob = s.trigMob; string[] positionargs = s.positionArgs; // parse the possible args to the spawn position control keywords switch (s.positionType) { case SpawnPositionType.Wet: // syntax Wet // find all of the wet tiles tileflag |= TileFlag.Wet; requiresurface = false; break; case SpawnPositionType.ItemID: checkitems = true; goto case SpawnPositionType.Tiles; case SpawnPositionType.NoItemID: checkitems = true; goto case SpawnPositionType.NoTiles; case SpawnPositionType.Tiles: { // syntax Tiles,start[,end] // get the tiles in the range requiresurface = false; int start = -1; int end = -1; if (positionargs != null && positionargs.Length > 1) { try { start = int.Parse(positionargs[1]); } catch { } } if (positionargs != null && positionargs.Length > 2) { try { end = int.Parse(positionargs[2]); } catch { } } if (includetilelist == null) { includetilelist = new List(); } // add the tiles to the list if (start > -1 && end < 0) { includetilelist.Add(start); } else if (start > -1 && end > -1) { for (int j = start; j <= end; j++) { includetilelist.Add(j); } } break; } case SpawnPositionType.NoTiles: { // syntax Tiles,start[,end] // get the tiles in the range requiresurface = false; int start = -1; int end = -1; if (positionargs != null && positionargs.Length > 1) { try { start = int.Parse(positionargs[1]); } catch { } } if (positionargs != null && positionargs.Length > 2) { try { end = int.Parse(positionargs[2]); } catch { } } if (excludetilelist == null) { excludetilelist = new List(); } // add the tiles to the list if (start > -1 && end < 0) { excludetilelist.Add(start); } else if (start > -1 && end > -1) { for (int j = start; j <= end; j++) { excludetilelist.Add(j); } } break; } case SpawnPositionType.RowFill: case SpawnPositionType.ColFill: case SpawnPositionType.Perimeter: // syntax XFILL[,inc] // syntax YFILL[,inc] // syntax EDGE[,inc] positioning = s.positionType; if (positionargs != null && positionargs.Length > 1) { try { fillinc = int.Parse(positionargs[1]); } catch { } } break; case SpawnPositionType.RelXY: case SpawnPositionType.DeltaLocation: case SpawnPositionType.Location: // syntax RELXY,xinc,yinc[,zinc] // syntax XY,x,y[,z] // syntax DXY,dx,dy[,dz] positioning = s.positionType; if (positionargs != null && positionargs.Length > 2) { try { xinc = int.Parse(positionargs[1]); yinc = int.Parse(positionargs[2]); } catch { } } if (positionargs != null && positionargs.Length > 3) { try { zinc = int.Parse(positionargs[3]); } catch { } } break; case SpawnPositionType.Waypoint: // syntax WAYPOINT,prefix[,range] positioning = s.positionType; if (positionargs != null && positionargs.Length > 1) { prefix = positionargs[1]; } if (positionargs != null && positionargs.Length > 2) { try { positionrange = int.Parse(positionargs[2]); } catch { } } // find a list of items that match the waypoint prefix if (prefix != null) { // see if there is an existing hashtable for the waypoint lists if (spawnPositionWayTable == null) { spawnPositionWayTable = new Dictionary>(); } // no existing list so create a new one if (!spawnPositionWayTable.TryGetValue(prefix, out WayList) || WayList == null) { WayList = new List(); foreach (Item i in World.Items.Values) { if (i is WayPoint && !string.IsNullOrEmpty(i.Name) && i.Map == Map && i.Name == prefix) { // add it to the list of items WayList.Add(i); } } // add the new list to the local table spawnPositionWayTable[prefix] = WayList; } } break; case SpawnPositionType.Player: // syntax PLAYER[,range] positioning = s.positionType; if (positionargs != null && positionargs.Length > 1) { try { positionrange = int.Parse(positionargs[1]); } catch { } } break; } } } // precalculate tile locations if they have been specified if (includetilelist != null || excludetilelist != null || tileflag != TileFlag.None) { if (m_Region != null && HasRegionPoints(m_Region)) { FindRegionTileLocations(ref locations, m_Region, includetilelist, excludetilelist, tileflag, checkitems, Z); } else if (positioning == SpawnPositionType.Random) { FindTileLocations(ref locations, Map, m_X, m_Y, m_Width, m_Height, includetilelist, excludetilelist, tileflag, checkitems, Z); } } // Try 10 times to find a Spawnable location. // trace profiling indicates that this is a major bottleneck for (int i = 0; i < 10; i++) { int x = X; int y = Y; int z = Z; int defaultZ = Z; if (packrange >= 0 && packcoord != Point3D.Zero) { defaultZ = packcoord.Z; } if (packrange >= 0 && packcoord != Point3D.Zero) { // find a random coord relative to the packcoord x = packcoord.X - packrange + Utility.Random(packrange * 2 + 1); y = packcoord.Y - packrange + Utility.Random(packrange * 2 + 1); } else if (m_Region != null && HasRegionPoints(m_Region)) { // if region spawning is selected then use that to find an x,y loc instead of the spawn box if (includetilelist != null || excludetilelist != null || tileflag != TileFlag.None) { // use the precalculated tile locations if (locations != null && locations.Count > 0) { Point3D p = locations[Utility.Random(locations.Count)]; x = p.X; y = p.Y; defaultZ = p.Z; } } else { Point2D p = GetRandomRegionPoint(m_Region); x = p.X; y = p.Y; } } else { switch (positioning) { case SpawnPositionType.Random: if (includetilelist != null || excludetilelist != null || tileflag != TileFlag.None) { if (locations != null && locations.Count > 0) { Point3D p = locations[Utility.Random(locations.Count)]; x = p.X; y = p.Y; defaultZ = p.Z; } } else { if (m_Width > 0) x = m_X + Utility.Random(m_Width + 1); if (m_Height > 0) y = m_Y + Utility.Random(m_Height + 1); } break; case SpawnPositionType.RelXY: x = mostRecentSpawnPosition.X + xinc; y = mostRecentSpawnPosition.Y + yinc; defaultZ = mostRecentSpawnPosition.Z + zinc; break; case SpawnPositionType.DeltaLocation: x = X + xinc; y = Y + yinc; defaultZ = Z + zinc; break; case SpawnPositionType.Location: x = xinc; y = yinc; defaultZ = zinc; break; case SpawnPositionType.RowFill: x = mostRecentSpawnPosition.X + fillinc; y = mostRecentSpawnPosition.Y; if (x < m_X) { x = m_X; } if (y < m_Y) { y = m_Y; } if (x > m_X + m_Width) { x = m_X + (x - m_X - m_Width - 1); y++; } if (y > m_Y + m_Height) { y = m_Y; } break; case SpawnPositionType.ColFill: x = mostRecentSpawnPosition.X; y = mostRecentSpawnPosition.Y + fillinc; if (x < m_X) { x = m_X; } if (y < m_Y) { y = m_Y; } if (y > m_Y + m_Height) { y = m_Y + (y - m_Y - m_Height - 1); x++; } if (x > m_X + m_Width) { x = m_X; } break; case SpawnPositionType.Perimeter: x = mostRecentSpawnPosition.X; y = mostRecentSpawnPosition.Y; // if the point is not on the perimeter, reset it to the corner if (x != m_X && x != m_X + m_Width && y != m_Y && y != m_Y + m_Height) { x = m_X; y = m_Y; } if (y == m_Y && x < m_X + m_Width) x += fillinc; else if (y == m_Y + m_Height && x > m_X) x -= fillinc; else if (x == m_X && y > m_Y) y -= fillinc; else if (x == m_X + m_Width && y < m_Y + m_Height) y += fillinc; if (x > m_X + m_Width) { x = m_X + m_Width; } if (y > m_Y + m_Height) { y = m_Y + m_Height; } if (x < m_X) { x = m_X; } if (y < m_Y) { y = m_Y; } break; case SpawnPositionType.Player: if (trigmob != null) { x = trigmob.Location.X; y = trigmob.Location.Y; if (positionrange > 0) { x += Utility.Random(positionrange * 2 + 1) - positionrange; y += Utility.Random(positionrange * 2 + 1) - positionrange; } } break; case SpawnPositionType.Waypoint: // pick an item randomly from the waylist if (WayList != null && WayList.Count > 0) { int index = Utility.Random(WayList.Count); Item waypoint = WayList[index]; if (waypoint != null) { x = waypoint.Location.X; y = waypoint.Location.Y; defaultZ = waypoint.Location.Z; if (positionrange > 0) { x += Utility.Random(positionrange * 2 + 1) - positionrange; y += Utility.Random(positionrange * 2 + 1) - positionrange; } } } break; } mostRecentSpawnPosition = new Point3D(x, y, defaultZ); } // skip invalid points if (x < 0 || y < 0 || (x == 0 && y == 0)) continue; // try to find a valid spawn location using the z coord of the spawner // relax the normal surface requirement for mobiles if the flag is set var fit = requiresurface ? CanSpawnMobile(x, y, defaultZ, mob) : Map.CanFit(x, y, defaultZ, SpawnFitSize, true, false, false); // if that fails then try to find a valid z coord if (fit) { return new Point3D(x, y, defaultZ); } z = Map.GetAverageZ(x, y); fit = requiresurface ? CanSpawnMobile(x, y, z, mob) : Map.CanFit(x, y, z, SpawnFitSize, true, false, false); if (fit) { return new Point3D(x, y, z); } } if (packrange >= 0 && packcoord != Point3D.Zero) { return packcoord; } return Location; } public int GetCreatureMax(int index) { Defrag(false); if (m_SpawnObjects == null) return 0; return m_SpawnObjects[index].MaxCount; } private void DeleteFromList(List list) { if (list == null) return; foreach (object o in list) { if (o is Item) ((Item)o).Delete(); else if (o is Mobile) ((Mobile)o).Delete(); } } private void DeleteFromList(List listi, List listm) { if (listi != null) { int i = listi.Count; while (--i >= 0) { if (i < listi.Count && listi[i] != null) { try { listi[i].Delete(); } catch { } } } listi.Clear(); } if (listm != null) { int i = listm.Count; while (--i >= 0) { if (i < listm.Count && listm[i] != null) { try { listm[i].Delete(); } catch { } } } listm.Clear(); } } public void RemoveSpawnObjects() { if (m_SpawnObjects == null) return; Defrag(false); ClearTags(true); List deletelist = new List(); foreach (SpawnObject so in m_SpawnObjects) { for (int i = 0; i < so.SpawnedObjects.Count; ++i) { object o = so.SpawnedObjects[i]; if (o is Item || o is Mobile) deletelist.Add(o); } } DeleteFromList(deletelist); // Defrag again Defrag(false); } public void RemoveSpawnObjects(SpawnObject so) { if (so == null) return; Defrag(false); List deletelist = new List(); for (int i = 0; i < so.SpawnedObjects.Count; ++i) { object o = so.SpawnedObjects[i]; if (o is Item || o is Mobile) deletelist.Add(o); } DeleteFromList(deletelist); // Defrag again Defrag(false); } public void ClearSubgroup(int subgroup) { if (m_SpawnObjects == null) return; Defrag(false); ClearTags(true); List deletelist = new List(); foreach (SpawnObject so in m_SpawnObjects) { if (so.SubGroup != subgroup || !so.ClearOnAdvance) continue; for (int i = 0; i < so.SpawnedObjects.Count; ++i) { object o = so.SpawnedObjects[i]; if (o is Item || o is Mobile) deletelist.Add(o); } } DeleteFromList(deletelist); // Defrag again Defrag(false); } // used to optimize smart spawning by removing all objects except those that have hold smartspawning public void SmartRemoveSpawnObjects() { if (m_SpawnObjects == null) return; Defrag(false); ClearTags(true); List deletelist = new List(); foreach (SpawnObject so in m_SpawnObjects) { for (int i = 0; i < so.SpawnedObjects.Count; ++i) { object o = so.SpawnedObjects[i]; // new optimization for smart spawning to remove all objects except those with hold smartspawning enabled if (CheckHoldSmartSpawning(o)) { continue; } if (o is Item || o is Mobile) deletelist.Add(o); } } DeleteFromList(deletelist); // Defrag again Defrag(false); } public void AddSpawnObject(string SpawnObjectName) { if (m_SpawnObjects == null) return; Defrag(false); // Find the spawn object and increment its count by one foreach (SpawnObject so in m_SpawnObjects) { if (so.TypeName.ToUpper() == SpawnObjectName.ToUpper()) { // Add one to the total count m_Count++; // Increment the max count for the current creature so.ActualMaxCount++; //only spawn them immediately if the spawner is running if (Running) Spawn(SpawnObjectName, false, 0); } } InvalidateProperties(); } public void DeleteSpawnObject(Mobile from, string SpawnObjectName) { bool WasRunning = m_Running; try { // Stop spawning for a moment Stop(); // Clean up any spawns marked as deleted Defrag(false); // Keep a reference to the spawn object SpawnObject TheSpawn = null; // Find the spawn object and increment its count by one foreach (SpawnObject so in m_SpawnObjects) { if (so.TypeName.ToUpper() == SpawnObjectName.ToUpper()) { // Set the spawn TheSpawn = so; break; } } // Was the spawn object found if (TheSpawn != null) { bool delete_this_entry = false; // Decrement the max count for the current creature TheSpawn.ActualMaxCount--; // Make sure the spawn count does not go negative if (TheSpawn.MaxCount < 0) { TheSpawn.MaxCount = 0; delete_this_entry = true; } if (!delete_this_entry) { // Subtract one to the total count m_Count--; } // Make sure the count does not go negative if (m_Count < 0) { m_Count = 0; } List deletelist = new List(); // Remove any spawns over the count while (TheSpawn.SpawnedObjects != null && TheSpawn.SpawnedObjects.Count > 0 && TheSpawn.SpawnedObjects.Count > TheSpawn.MaxCount) { object o = TheSpawn.SpawnedObjects[0]; // Delete the object if (o is Item || o is Mobile) deletelist.Add(o); TheSpawn.SpawnedObjects.Remove(o); } DeleteFromList(deletelist); // Check if the spawn object should be removed if (delete_this_entry) { m_SpawnObjects.Remove(TheSpawn); if (from != null) CommandLogging.WriteLine(from, "{0} {1} removed from XmlSpawner {2} '{3}' [{4}, {5}] ({6}) : {7}", from.AccessLevel, CommandLogging.Format(from), Serial, Name, GetWorldLocation().X, GetWorldLocation().Y, Map, SpawnObjectName); } } InvalidateProperties(); } finally { if (WasRunning) Start(); } } public void RemoveSpawnObject(SpawnObject so) { if (m_SpawnObjects.Contains(so)) { m_SpawnObjects.Remove(so); } } #endregion #region Object Creation public static object CreateObject(Type type, string itemtypestring) { return CreateObject(type, itemtypestring, true); } public static object CreateObject(Type type, string itemtypestring, bool requireconstructable) { // look for constructor arguments to be passed to it with the syntax type,arg1,arg2,.../ string[] typewordargs = BaseXmlSpawner.ParseObjectArgs(itemtypestring); return CreateObject(type, typewordargs, requireconstructable); } public static object CreateObject(Type type, string[] typewordargs, bool requireconstructable) { if (type == null) return null; object o = null; int typearglen = 0; if (typewordargs != null) typearglen = typewordargs.Length; // ok, there are args in the typename, so we need to invoke the proper constructor ConstructorInfo[] ctors = type.GetConstructors(); // go through all the constructors for this type for (int i = 0; i < ctors.Length; ++i) { ConstructorInfo ctor = ctors[i]; // if requireconstructable is true, then allow either condition #if (RESTRICTCONSTRUCTABLE) if (!(requireconstructable && Add.IsConstructable(ctor,requester))) continue; #else if (!(requireconstructable && IsConstructable(ctor))) continue; #endif // check the parameter list of the constructor ParameterInfo[] paramList = ctor.GetParameters(); // and compare with the argument list provided if (typearglen == paramList.Length) { // this is a constructor that takes args and matches the number of args passed in to CreateObject if (paramList.Length > 0) { object[] paramValues = null; try { paramValues = Add.ParseValues(paramList, typewordargs); } catch { } if (paramValues == null) continue; // ok, have a match on args, so try to construct it try { o = Activator.CreateInstance(type, paramValues); } catch { } } else { // zero argument constructor try { o = Activator.CreateInstance(type); } catch { } } // successfully constructed the object, otherwise try another matching constructor if (o != null) break; } } return o; } #endregion #region Timers private static void DoGlobalSectorTimer(TimeSpan delay) { if (m_GlobalSectorTimer != null) m_GlobalSectorTimer.Stop(); m_GlobalSectorTimer = new GlobalSectorTimer(delay); m_GlobalSectorTimer.Start(); } private class GlobalSectorTimer : Timer { public GlobalSectorTimer(TimeSpan delay) : base(delay, delay) { Priority = TimerPriority.OneSecond; } protected override void OnTick() { // check the sectors // check all active players if (NetState.Instances != null) { foreach (NetState state in NetState.Instances) { Mobile m = state.Mobile; if (m != null && (m.AccessLevel <= SmartSpawnAccessLevel || !m.Hidden)) { // activate any spawner in the sector they are in if (m.Map != null && m.Map != Map.Internal) { Sector s = m.Map.GetSector(m.Location); if (s != null && GlobalSectorTable[m.Map.MapID] != null) { List spawnerlist;// = GlobalSectorTable[m.Map.MapID][s]; if (GlobalSectorTable[m.Map.MapID].TryGetValue(s, out spawnerlist) && spawnerlist != null) { foreach (XmlSpawner spawner in spawnerlist) { if (spawner != null && !spawner.Deleted && spawner.Running && spawner.SmartSpawning && spawner.IsInactivated) { spawner.SmartRespawn(); } } } } } } } } } } public void DoSectorTimer(TimeSpan delay) { if (m_SectorTimer != null) m_SectorTimer.Stop(); m_SectorTimer = new SectorTimer(this, delay); m_SectorTimer.Start(); } private class SectorTimer : Timer { private readonly XmlSpawner m_Spawner; public SectorTimer(XmlSpawner spawner, TimeSpan delay) : base(delay, delay) { Priority = TimerPriority.OneSecond; m_Spawner = spawner; } protected override void OnTick() { // check the sectors if (m_Spawner != null && !m_Spawner.Deleted && m_Spawner.Running && m_Spawner.IsInactivated) { if (m_Spawner.SmartSpawning) { if (m_Spawner.HasActiveSectors) { Stop(); m_Spawner.SmartRespawn(); } } else { Stop(); m_Spawner.IsInactivated = false; } } else { Stop(); } } } private class WarnTimer2 : Timer { private readonly List m_List; private class WarnEntry2 { public Point3D m_Point; public Map m_Map; public string m_Name; public WarnEntry2(Point3D p, Map map, string name) { m_Point = p; m_Map = map; m_Name = name; } } public WarnTimer2() : base(TimeSpan.FromSeconds(1.0)) { m_List = new List(); Start(); } public void Add(Point3D p, Map map, string name) { m_List.Add(new WarnEntry2(p, map, name)); } protected override void OnTick() { try { Console.WriteLine("Warning: {0} bad spawns detected, logged: 'badspawn.log'", m_List.Count); using (StreamWriter op = new StreamWriter("badspawn.log", true)) { op.WriteLine("# Bad spawns : {0}", DateTime.UtcNow); op.WriteLine("# Format: X Y Z F Name"); op.WriteLine(); foreach (WarnEntry2 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 { } } } public void DoTimer() { if (!m_Running) return; int minSeconds = (int)m_MinDelay.TotalSeconds; int maxSeconds = (int)m_MaxDelay.TotalSeconds; TimeSpan delay = TimeSpan.FromSeconds(Utility.RandomMinMax(minSeconds, maxSeconds)); DoTimer(delay); } public void DoTimer(TimeSpan delay) { if (!m_Running) return; m_End = DateTime.UtcNow + delay; if (m_Timer != null) m_Timer.Stop(); m_Timer = new SpawnerTimer(this, delay); m_Timer.Start(); } public void DoTimer2(TimeSpan delay) { m_DurEnd = DateTime.UtcNow + delay; if (m_Duration > TimeSpan.FromMinutes(0) || m_durActivated) { if (m_DurTimer != null) m_DurTimer.Stop(); m_DurTimer = new InternalTimer(this, delay); m_DurTimer.Start(); m_durActivated = true; } } public void DoTimer3(TimeSpan delay) { m_RefractEnd = DateTime.UtcNow + delay; m_refractActivated = true; if (m_RefractoryTimer != null) m_RefractoryTimer.Stop(); m_RefractoryTimer = new InternalTimer3(this, delay); m_RefractoryTimer.Start(); } // added the duration timer that begins on spawning private class InternalTimer : Timer { private readonly XmlSpawner m_spawner; public InternalTimer(XmlSpawner spawner, TimeSpan delay) : base(delay) { Priority = TimerPriority.OneSecond; m_spawner = spawner; } protected override void OnTick() { if (m_spawner != null && !m_spawner.Deleted) { m_spawner.RemoveSpawnObjects(); m_spawner.m_durActivated = false; } } } private class SpawnerTimer : Timer { private readonly XmlSpawner m_Spawner; public SpawnerTimer(XmlSpawner spawner, TimeSpan delay) : base(delay) { if (spawner.IsInactivated || spawner.CurrentCount == spawner.MaxCount) // reduce timer priority if spawner is inactivated or spawner is maxed { Priority = TimerPriority.FiveSeconds; } else { Priority = spawner.BasePriority; } m_Spawner = spawner; } protected override void OnTick() { if (m_Spawner != null && !m_Spawner.Deleted) { m_Spawner.OnTick(); } } } // added the refractory timer that begins on proximity triggering private class InternalTimer3 : Timer { private readonly XmlSpawner m_spawner; public InternalTimer3(XmlSpawner spawner, TimeSpan delay) : base(delay) { Priority = TimerPriority.OneSecond; m_spawner = spawner; } protected override void OnTick() { if (m_spawner != null && !m_spawner.Deleted) { // reenable triggering m_spawner.m_refractActivated = false; } } } #endregion #region Serialization public override void Serialize(GenericWriter writer) { base.Serialize(writer); writer.Write(32); // version // version 31 writer.Write(m_DisableGlobalAutoReset); // Version 30 writer.Write(m_AllowNPCTriggering); // Version 29 if (m_SpawnObjects != null) { writer.Write(m_SpawnObjects.Count); for (int i = 0; i < m_SpawnObjects.Count; ++i) { // Write the spawns per tick value writer.Write(m_SpawnObjects[i].SpawnsPerTick); } } else { // empty spawner writer.Write(0); } // Version 28 if (m_SpawnObjects != null) { for (int i = 0; i < m_SpawnObjects.Count; ++i) { // Write the pack range value writer.Write(m_SpawnObjects[i].PackRange); } } // Version 27 if (m_SpawnObjects != null) { for (int i = 0; i < m_SpawnObjects.Count; ++i) { // Write the disable spawn flag writer.Write(m_SpawnObjects[i].Disabled); } } // Version 26 writer.Write(m_SpawnOnTrigger); // Version 24 if (m_SpawnObjects != null) { for (int i = 0; i < m_SpawnObjects.Count; ++i) { SpawnObject so = m_SpawnObjects[i]; // Write the restrict kills flag writer.Write(so.RestrictKillsToSubgroup); // Write the clear on advance flag writer.Write(so.ClearOnAdvance); // Write the mindelay writer.Write(so.MinDelay); // Write the maxdelay writer.Write(so.MaxDelay); // write the next spawn time for the subgrop writer.WriteDeltaTime(so.NextSpawn); } } if (m_ShowBoundsItems != null && m_ShowBoundsItems.Count > 0) { writer.Write(true); writer.WriteItemList(m_ShowBoundsItems); } else { // empty showbounds item list writer.Write(false); } // Version 23 writer.Write(IsInactivated); writer.Write(m_SmartSpawning); // Version 22 writer.Write(m_SkillTrigger); writer.Write((int)m_skill_that_triggered); writer.Write(m_FreeRun); writer.Write(m_mob_who_triggered); // Version 21 writer.Write(m_DespawnTime); // Version 20 if (m_SpawnObjects != null) { for (int i = 0; i < m_SpawnObjects.Count; ++i) { // Write the requiresurface flag writer.Write(m_SpawnObjects[i].RequireSurface); } } // Version 19 writer.Write(m_ConfigFile); writer.Write(m_OnHold); writer.Write(m_HoldSequence); // compute the number of tags to save int tagcount = 0; for (int i = 0; i < m_KeywordTagList.Count; i++) { // only save WAIT type keywords or other keywords that have the save flag set if ((m_KeywordTagList[i].Flags & BaseXmlSpawner.KeywordFlags.Serialize) != 0) tagcount++; } writer.Write(tagcount); // and write them out for (int i = 0; i < m_KeywordTagList.Count; i++) { if ((m_KeywordTagList[i].Flags & BaseXmlSpawner.KeywordFlags.Serialize) != 0) { m_KeywordTagList[i].Serialize(writer); } } // Version 18 writer.Write(m_AllowGhostTriggering); // Version 17 // removed in version 25 //writer.Write( m_TextEntryBook); // Version 16 writer.Write(m_SequentialSpawning); // write out the remaining time until sequential reset writer.Write(NextSeqReset); // Write the spawn object list if (m_SpawnObjects != null) { for (int i = 0; i < m_SpawnObjects.Count; ++i) { SpawnObject so = m_SpawnObjects[i]; // Write the subgroup and sequential reset time writer.Write(so.SubGroup); writer.Write(so.SequentialResetTime); writer.Write(so.SequentialResetTo); writer.Write(so.KillsNeeded); } } writer.Write(m_RegionName); // Version 15 writer.Write(m_ExternalTriggering); writer.Write(m_ExternalTrigger); // Version 14 writer.Write(m_NoItemTriggerName); // Version 13 writer.Write(m_GumpState); // Version 12 int todtype = (int)m_TODMode; writer.Write(todtype); // Version 11 writer.Write(m_KillReset); writer.Write(m_skipped); writer.Write(m_spawncheck); // Version 10 writer.Write(m_SetPropertyItem); // Version 9 writer.Write(m_TriggerProbability); // Version 8 writer.Write(m_MobPropertyName); writer.Write(m_MobTriggerName); writer.Write(m_PlayerPropertyName); // Version 7 writer.Write(m_SpeechTrigger); // Version 6 writer.Write(m_ItemTriggerName); // Version 5 writer.Write(m_ProximityTriggerMessage); writer.Write(m_ObjectPropertyItem); writer.Write(m_ObjectPropertyName); writer.Write(m_killcount); // Version 4 writer.Write(m_ProximityRange); writer.Write(m_ProximityTriggerSound); writer.Write(m_proximityActivated); writer.Write(m_durActivated); writer.Write(m_refractActivated); writer.Write(m_StackAmount); writer.Write(m_TODStart); writer.Write(m_TODEnd); writer.Write(m_MinRefractory); writer.Write(m_MaxRefractory); if (m_refractActivated) writer.Write(m_RefractEnd - DateTime.UtcNow); if (m_durActivated) writer.Write(m_DurEnd - DateTime.UtcNow); // Version 3 writer.Write(m_ShowContainerStatic); // Version 2 writer.Write(m_Duration); // Version 1 writer.Write(m_UniqueId); writer.Write(m_HomeRangeIsRelative); // Version 0 writer.Write(m_Name); writer.Write(m_X); writer.Write(m_Y); writer.Write(m_Width); writer.Write(m_Height); writer.Write(m_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.Write(m_End - DateTime.UtcNow); // Write the spawn object list int nso = 0; if (m_SpawnObjects != null) nso = m_SpawnObjects.Count; writer.Write(nso); for (int i = 0; i < nso; ++i) { SpawnObject so = m_SpawnObjects[i]; // Write the type and maximum count writer.Write(so.TypeName); writer.Write(so.ActualMaxCount); // Write the spawned object information writer.Write(so.SpawnedObjects.Count); for (int x = 0; x < so.SpawnedObjects.Count; ++x) { object o = so.SpawnedObjects[x]; if (o is Item) writer.Write((Item)o); else if (o is Mobile) writer.Write((Mobile)o); else { // if this is a keyword tag then add some more info if (o is BaseXmlSpawner.KeywordTag) { writer.Write(-1 * ((BaseXmlSpawner.KeywordTag)o).Serial - 2); } else { writer.Write(Serial.MinusOne); } } } } } public override void Deserialize(GenericReader reader) { base.Deserialize(reader); int version = reader.ReadInt(); bool haveproximityrange = false; bool hasnewobjectinfo = false; int tmpSpawnListSize = 0; List tmpSubGroup = null; List tmpSequentialResetTime = null; List tmpSequentialResetTo = null; List tmpKillsNeeded = null; List tmpRequireSurface = null; List tmpRestrictKillsToSubgroup = null; List tmpClearOnAdvance = null; List tmpMinDelay = null; List tmpMaxDelay = null; List tmpNextSpawn = null; List tmpDisableSpawn = null; List tmpPackRange = null; List tmpSpawnsPer = null; switch (version) { case 32: case 31: { m_DisableGlobalAutoReset = reader.ReadBool(); goto case 30; } case 30: { m_AllowNPCTriggering = reader.ReadBool(); goto case 29; } case 29: { tmpSpawnListSize = reader.ReadInt(); tmpSpawnsPer = new List(tmpSpawnListSize); for (int i = 0; i < tmpSpawnListSize; ++i) { int spawnsper = reader.ReadInt(); tmpSpawnsPer.Add(spawnsper); } goto case 28; } case 28: { tmpPackRange = new List(tmpSpawnListSize); for (int i = 0; i < tmpSpawnListSize; ++i) { int packrange = reader.ReadInt(); tmpPackRange.Add(packrange); } goto case 27; } case 27: { tmpDisableSpawn = new List(tmpSpawnListSize); for (int i = 0; i < tmpSpawnListSize; ++i) { bool disablespawn = reader.ReadBool(); tmpDisableSpawn.Add(disablespawn); } goto case 26; } case 26: { m_SpawnOnTrigger = reader.ReadBool(); if (version < 32) { // Delete First & Last Modified reader.ReadDateTime(); reader.ReadDateTime(); } goto case 25; } case 25: { goto case 24; } case 24: { tmpRestrictKillsToSubgroup = new List(tmpSpawnListSize); tmpClearOnAdvance = new List(tmpSpawnListSize); tmpMinDelay = new List(tmpSpawnListSize); tmpMaxDelay = new List(tmpSpawnListSize); tmpNextSpawn = new List(tmpSpawnListSize); for (int i = 0; i < tmpSpawnListSize; ++i) { bool restrictkills = reader.ReadBool(); bool clearadvance = reader.ReadBool(); double mind = reader.ReadDouble(); double maxd = reader.ReadDouble(); DateTime nextspawn = reader.ReadDeltaTime(); tmpRestrictKillsToSubgroup.Add(restrictkills); tmpClearOnAdvance.Add(clearadvance); tmpMinDelay.Add(mind); tmpMaxDelay.Add(maxd); tmpNextSpawn.Add(nextspawn); } bool hasitems = reader.ReadBool(); if (hasitems) { m_ShowBoundsItems = reader.ReadStrongItemList(); } goto case 23; } case 23: { IsInactivated = reader.ReadBool(); SmartSpawning = reader.ReadBool(); goto case 22; } case 22: { SkillTrigger = reader.ReadString(); // note this will also register the skill m_skill_that_triggered = (SkillName)reader.ReadInt(); m_FreeRun = reader.ReadBool(); m_mob_who_triggered = reader.ReadMobile(); goto case 21; } case 21: { m_DespawnTime = reader.ReadTimeSpan(); goto case 20; } case 20: { tmpRequireSurface = new List(tmpSpawnListSize); for (int i = 0; i < tmpSpawnListSize; ++i) { bool requiresurface = reader.ReadBool(); tmpRequireSurface.Add(requiresurface); } goto case 19; } case 19: { m_ConfigFile = reader.ReadString(); m_OnHold = reader.ReadBool(); m_HoldSequence = reader.ReadBool(); if (version < 32) { // // Delete First & Last Modified By // // Delete First & Last Modified By reader.ReadString(); reader.ReadString(); } // deserialize the keyword tag list int tagcount = reader.ReadInt(); m_KeywordTagList = new List(tagcount); for (int i = 0; i < tagcount; i++) { BaseXmlSpawner.KeywordTag tag = new BaseXmlSpawner.KeywordTag(null, this); tag.Deserialize(reader); } goto case 18; } case 18: { m_AllowGhostTriggering = reader.ReadBool(); goto case 17; } case 17: { goto case 16; } case 16: { hasnewobjectinfo = true; m_SequentialSpawning = reader.ReadInt(); TimeSpan seqdelay = reader.ReadTimeSpan(); m_SeqEnd = DateTime.UtcNow + seqdelay; tmpSubGroup = new List(tmpSpawnListSize); tmpSequentialResetTime = new List(tmpSpawnListSize); tmpSequentialResetTo = new List(tmpSpawnListSize); tmpKillsNeeded = new List(tmpSpawnListSize); for (int i = 0; i < tmpSpawnListSize; ++i) { int subgroup = reader.ReadInt(); double resettime = reader.ReadDouble(); int resetto = reader.ReadInt(); int killsneeded = reader.ReadInt(); tmpSubGroup.Add(subgroup); tmpSequentialResetTime.Add(resettime); tmpSequentialResetTo.Add(resetto); tmpKillsNeeded.Add(killsneeded); } m_RegionName = reader.ReadString(); goto case 15; } case 15: { m_ExternalTriggering = reader.ReadBool(); m_ExternalTrigger = reader.ReadBool(); goto case 14; } case 14: { m_NoItemTriggerName = reader.ReadString(); goto case 13; } case 13: { m_GumpState = reader.ReadString(); goto case 12; } case 12: { int todtype = reader.ReadInt(); switch (todtype) { case (int)TODModeType.Gametime: m_TODMode = TODModeType.Gametime; break; case (int)TODModeType.Realtime: m_TODMode = TODModeType.Realtime; break; } goto case 11; } case 11: { m_KillReset = reader.ReadInt(); m_skipped = reader.ReadBool(); m_spawncheck = reader.ReadInt(); goto case 10; } case 10: { m_SetPropertyItem = reader.ReadItem(); goto case 9; } case 9: { m_TriggerProbability = reader.ReadDouble(); goto case 8; } case 8: { m_MobPropertyName = reader.ReadString(); m_MobTriggerName = reader.ReadString(); m_PlayerPropertyName = reader.ReadString(); goto case 7; } case 7: { m_SpeechTrigger = reader.ReadString(); goto case 6; } case 6: { m_ItemTriggerName = reader.ReadString(); goto case 5; } case 5: { m_ProximityTriggerMessage = reader.ReadString(); m_ObjectPropertyItem = reader.ReadItem(); m_ObjectPropertyName = reader.ReadString(); m_killcount = reader.ReadInt(); goto case 4; } case 4: { haveproximityrange = true; m_ProximityRange = reader.ReadInt(); m_ProximityTriggerSound = reader.ReadInt(); m_proximityActivated = reader.ReadBool(); m_durActivated = reader.ReadBool(); m_refractActivated = reader.ReadBool(); m_StackAmount = reader.ReadInt(); m_TODStart = reader.ReadTimeSpan(); m_TODEnd = reader.ReadTimeSpan(); m_MinRefractory = reader.ReadTimeSpan(); m_MaxRefractory = reader.ReadTimeSpan(); if (m_refractActivated) { TimeSpan delay = reader.ReadTimeSpan(); DoTimer3(delay); } if (m_durActivated) { TimeSpan delay = reader.ReadTimeSpan(); DoTimer2(delay); } goto case 3; } case 3: { m_ShowContainerStatic = reader.ReadItem() as Static; goto case 2; } case 2: { m_Duration = reader.ReadTimeSpan(); goto case 1; } case 1: { m_UniqueId = reader.ReadString(); m_HomeRangeIsRelative = reader.ReadBool(); goto case 0; } case 0: { m_Name = reader.ReadString(); // backward compatibility with old name storage if (!string.IsNullOrEmpty(m_Name)) Name = m_Name; m_X = reader.ReadInt(); m_Y = reader.ReadInt(); m_Width = reader.ReadInt(); m_Height = reader.ReadInt(); //we HAVE to check if the area is even or if coordinates point to the original spawner, otherwise it's custom area! if (m_Width == m_Height && (m_Width % 2) == 0 && (m_X + m_Width / 2) == X && (m_Y + m_Height / 2) == Y) m_SpawnRange = m_Width / 2; else m_SpawnRange = -1; if (!haveproximityrange) { m_ProximityRange = -1; } m_WayPoint = reader.ReadItem() as WayPoint; m_Group = reader.ReadBool(); m_MinDelay = reader.ReadTimeSpan(); m_MaxDelay = reader.ReadTimeSpan(); m_Count = reader.ReadInt(); m_Team = reader.ReadInt(); m_HomeRange = reader.ReadInt(); m_Running = reader.ReadBool(); if (m_Running) { TimeSpan delay = reader.ReadTimeSpan(); DoTimer(delay); } // Read in the size of the spawn object list int SpawnListSize = reader.ReadInt(); m_SpawnObjects = new List(SpawnListSize); for (int i = 0; i < SpawnListSize; ++i) { string TypeName = reader.ReadString(); int TypeMaxCount = reader.ReadInt(); SpawnObject TheSpawnObject = new SpawnObject(TypeName, TypeMaxCount); m_SpawnObjects.Add(TheSpawnObject); string typeName = BaseXmlSpawner.ParseObjectType(TypeName); if (typeName == null || ((SpawnerType.GetType(typeName) == null) && (!BaseXmlSpawner.IsTypeOrItemKeyword(typeName) && typeName.IndexOf('{') == -1 && !typeName.StartsWith("*") && !typeName.StartsWith("#")))) { if (m_WarnTimer == null) m_WarnTimer = new WarnTimer2(); m_WarnTimer.Add(Location, Map, TypeName); status_str = "invalid type: " + typeName; } // Read in the number of spawns already int SpawnedCount = reader.ReadInt(); TheSpawnObject.SpawnedObjects = new List(SpawnedCount); for (int x = 0; x < SpawnedCount; ++x) { int serial = reader.ReadInt(); if (serial < -1) { // minusone is reserved for unknown types by default // minustwo on is used for referencing keyword tags int tagserial = -1 * (serial + 2); // get the tag with that serial and add it BaseXmlSpawner.KeywordTag t = BaseXmlSpawner.GetFromTagList(this, tagserial); if (t != null) { TheSpawnObject.SpawnedObjects.Add(t); } } else { IEntity e = World.FindEntity(serial); if (e != null) TheSpawnObject.SpawnedObjects.Add(e); } } } // now have to reintegrate the later version spawnobject information into the earlier version desered objects if (hasnewobjectinfo && tmpSpawnListSize == SpawnListSize) { for (int i = 0; i < SpawnListSize; ++i) { SpawnObject so = m_SpawnObjects[i]; so.SubGroup = tmpSubGroup[i]; so.SequentialResetTime = tmpSequentialResetTime[i]; so.SequentialResetTo = tmpSequentialResetTo[i]; so.KillsNeeded = tmpKillsNeeded[i]; if (version > 19) so.RequireSurface = tmpRequireSurface[i]; bool restrictkills = false; bool clearadvance = true; double mind = -1; double maxd = -1; DateTime nextspawn = DateTime.MinValue; if (version > 23) { restrictkills = tmpRestrictKillsToSubgroup[i]; clearadvance = tmpClearOnAdvance[i]; mind = tmpMinDelay[i]; maxd = tmpMaxDelay[i]; nextspawn = tmpNextSpawn[i]; } so.RestrictKillsToSubgroup = restrictkills; so.ClearOnAdvance = clearadvance; so.MinDelay = mind; so.MaxDelay = maxd; so.NextSpawn = nextspawn; bool disablespawn = false; if (version > 26) { disablespawn = tmpDisableSpawn[i]; } so.Disabled = disablespawn; int packrange = -1; if (version > 27) { packrange = tmpPackRange[i]; } so.PackRange = packrange; int spawnsper = 1; if (version > 28) { spawnsper = tmpSpawnsPer[i]; } so.SpawnsPerTick = spawnsper; } } break; } } if (m_RegionName != null) { Timer.DelayCall(delegate { if (!Deleted && m_RegionName != null) RegionName = m_RegionName; }); } } internal string GetSerializedObjectList() { System.Text.StringBuilder sb = new System.Text.StringBuilder(); foreach (SpawnObject so in m_SpawnObjects) { if (sb.Length > 0) sb.Append(':'); // ':' Separates multiple object types sb.AppendFormat("{0}={1}", so.TypeName, so.ActualMaxCount); // '=' separates object name from maximum amount } return sb.ToString(); } internal string GetSerializedObjectList2() { System.Text.StringBuilder sb = new System.Text.StringBuilder(); foreach (SpawnObject so in m_SpawnObjects) { if (sb.Length > 0) sb.Append(":OBJ="); // Separates multiple object types sb.AppendFormat("{0}:MX={1}:SB={2}:RT={3}:TO={4}:KL={5}:RK={6}:CA={7}:DN={8}:DX={9}:SP={10}:PR={11}", so.TypeName, so.ActualMaxCount, so.SubGroup, so.SequentialResetTime, so.SequentialResetTo, so.KillsNeeded, so.RestrictKillsToSubgroup ? 1 : 0, so.ClearOnAdvance ? 1 : 0, so.MinDelay, so.MaxDelay, so.SpawnsPerTick, so.PackRange); } return sb.ToString(); } #endregion #region Spawn classes public class SpawnObject { private int m_MaxCount; // temporary variable used to calculate weighted spawn probabilities public bool Available; public List SpawnedObjects; public string[] PropertyArgs; public double SequentialResetTime; public int EntryOrder; // used for sorting public bool RequireSurface = true; public DateTime NextSpawn; public bool SpawnedThisTick; // these are externally accessible to the SETONSPAWNENTRY keyword public string TypeName { get; set; } public int MaxCount { get { if (Disabled) { return 0; } return m_MaxCount; } set { m_MaxCount = value; } } public int ActualMaxCount { get { return m_MaxCount; } set { m_MaxCount = value; } } public int SubGroup { get; set; } public int SpawnsPerTick { get; set; } = 1; public int SequentialResetTo { get; set; } public int KillsNeeded { get; set; } public bool RestrictKillsToSubgroup { get; set; } = false; public bool ClearOnAdvance { get; set; } = true; public double MinDelay { get; set; } = -1; public double MaxDelay { get; set; } = -1; public bool Disabled { get; set; } = false; public bool Ignore { get; set; } = false; public int PackRange { get; set; } = -1; // command loggable constructor public SpawnObject(Mobile from, XmlSpawner spawner, string name, int maxamount) { if (from != null && spawner != null) { bool found = false; // go through the current spawner objects and see if this is a new entry if (spawner.m_SpawnObjects != null) { for (int i = 0; i < spawner.m_SpawnObjects.Count; i++) { SpawnObject s = spawner.m_SpawnObjects[i]; if (s != null && s.TypeName == name) { found = true; break; } } } if (!found) { CommandLogging.WriteLine(from, "{0} {1} added to XmlSpawner {2} '{3}' [{4}, {5}] ({6}) : {7}", from.AccessLevel, CommandLogging.Format(from), spawner.Serial, spawner.Name, spawner.GetWorldLocation().X, spawner.GetWorldLocation().Y, spawner.Map, name); } } TypeName = name; MaxCount = maxamount; SubGroup = 0; SequentialResetTime = 0; SequentialResetTo = 0; KillsNeeded = 0; RestrictKillsToSubgroup = false; ClearOnAdvance = true; SpawnedObjects = new List(); } public SpawnObject(string name, int maxamount) { TypeName = name; MaxCount = maxamount; SubGroup = 0; SequentialResetTime = 0; SequentialResetTo = 0; KillsNeeded = 0; RestrictKillsToSubgroup = false; ClearOnAdvance = true; SpawnedObjects = new List(); } public SpawnObject(string name, int maxamount, int subgroup, double sequentialresettime, int sequentialresetto, int killsneeded, bool restrictkills, bool clearadvance, double mindelay, double maxdelay, int spawnsper, int packrange) { TypeName = name; MaxCount = maxamount; SubGroup = subgroup; SequentialResetTime = sequentialresettime; SequentialResetTo = sequentialresetto; KillsNeeded = killsneeded; RestrictKillsToSubgroup = restrictkills; ClearOnAdvance = clearadvance; MinDelay = mindelay; MaxDelay = maxdelay; SpawnsPerTick = spawnsper; PackRange = packrange; SpawnedObjects = new List(); } internal static string GetParm(string str, string separator) { // find the parm separator in the string // then look for the termination at the ':' or end of string // and return the stuff between string[] arg = BaseXmlSpawner.SplitString(str, separator); //should be 2 args if (arg.Length > 1) { // look for the end of parm terminator (could also be eol) string[] parm = arg[1].Split(':'); if (parm.Length > 0) { return (parm[0]); } } return (null); } internal static SpawnObject[] LoadSpawnObjectsFromString(string ObjectList) { // Clear the spawn object list List NewSpawnObjects = new List(); if (!string.IsNullOrEmpty(ObjectList)) { // Split the string based on the object separator first ':' string[] SpawnObjectList = ObjectList.Split(':'); // Parse each item in the array foreach (string s in SpawnObjectList) { // Split the single spawn object item by the max count '=' string[] SpawnObjectDetails = s.Split('='); // Should be two entries if (SpawnObjectDetails.Length == 2) { // Validate the information // Make sure the spawn object name part has a valid length if (SpawnObjectDetails[0].Length > 0) { // Make sure the max count part has a valid length if (SpawnObjectDetails[1].Length > 0) { int maxCount = 1; try { maxCount = int.Parse(SpawnObjectDetails[1]); } catch (Exception) { // Something went wrong, leave the default amount } } // Create the spawn object and store it in the array list SpawnObject so = new SpawnObject(SpawnObjectDetails[0], maxCount); NewSpawnObjects.Add(so); } } } } } return NewSpawnObjects.ToArray(); } internal static SpawnObject[] LoadSpawnObjectsFromString2(string ObjectList) { // Clear the spawn object list List NewSpawnObjects = new List(); // spawn object definitions will take the form typestring:MX=int:SB=int:RT=double:TO=int:KL=int // or typestring:MX=int:SB=int:RT=double:TO=int:KL=int:OBJ=typestring... if (!string.IsNullOrEmpty(ObjectList)) { string[] SpawnObjectList = BaseXmlSpawner.SplitString(ObjectList, ":OBJ="); // Parse each item in the array foreach (string s in SpawnObjectList) { // at this point each spawn string will take the form typestring:MX=int:SB=int:RT=double:TO=int:KL=int // Split the single spawn object item by the max count to get the typename and the remaining parms string[] SpawnObjectDetails = BaseXmlSpawner.SplitString(s, ":MX="); // Should be two entries if (SpawnObjectDetails.Length == 2) { // Validate the information // Make sure the spawn object name part has a valid length if (SpawnObjectDetails[0].Length > 0) { // Make sure the parm part has a valid length if (SpawnObjectDetails[1].Length > 0) { // now parse out the parms // MaxCount string parmstr = GetParm(s, ":MX="); int maxCount = 1; try { maxCount = int.Parse(parmstr); } catch { } // SubGroup parmstr = GetParm(s, ":SB="); int subGroup = 0; try { subGroup = int.Parse(parmstr); } catch { } // SequentialSpawnResetTime parmstr = GetParm(s, ":RT="); double resetTime = 0; try { resetTime = double.Parse(parmstr); } catch { } // SequentialSpawnResetTo parmstr = GetParm(s, ":TO="); int resetTo = 0; try { resetTo = int.Parse(parmstr); } catch { } // KillsNeeded parmstr = GetParm(s, ":KL="); int killsNeeded = 0; try { killsNeeded = int.Parse(parmstr); } catch { } // RestrictKills parmstr = GetParm(s, ":RK="); bool restrictKills = false; if (parmstr != null) try { restrictKills = (int.Parse(parmstr) == 1); } catch { } // ClearOnAdvance parmstr = GetParm(s, ":CA="); bool clearAdvance = true; // if kills needed is zero, then set CA to false by default. This maintains consistency with the // previous default behavior for old spawn specs that havent specified CA if (killsNeeded == 0) clearAdvance = false; if (parmstr != null) try { clearAdvance = (int.Parse(parmstr) == 1); } catch { } // MinDelay parmstr = GetParm(s, ":DN="); double minD = -1; try { minD = double.Parse(parmstr); } catch { } // MaxDelay parmstr = GetParm(s, ":DX="); double maxD = -1; try { maxD = double.Parse(parmstr); } catch { } // SpawnsPerTick parmstr = GetParm(s, ":SP="); int spawnsPer = 1; try { spawnsPer = int.Parse(parmstr); } catch { } // PackRange parmstr = GetParm(s, ":PR="); int packRange = -1; try { packRange = int.Parse(parmstr); } catch { } // Create the spawn object and store it in the array list SpawnObject so = new SpawnObject(SpawnObjectDetails[0], maxCount, subGroup, resetTime, resetTo, killsNeeded, restrictKills, clearAdvance, minD, maxD, spawnsPer, packRange); NewSpawnObjects.Add(so); } } } } } return NewSpawnObjects.ToArray(); } } #endregion } }