diff --git a/Scripts/Commands/Decorate.cs b/Scripts/Commands/Decorate.cs index 572f74176..2b2b44972 100644 --- a/Scripts/Commands/Decorate.cs +++ b/Scripts/Commands/Decorate.cs @@ -454,7 +454,7 @@ namespace Server.Commands int indexOf = m_Params[i].IndexOf( '=' ); if ( indexOf >= 0 ) - sp.SpawnNames.Add( m_Params[i].Substring( ++indexOf ) ); + sp.AddEntry( m_Params[i].Substring( ++indexOf ), 100, 1 ); } else if ( m_Params[i].StartsWith( "MinDelay" ) ) { @@ -482,7 +482,11 @@ namespace Server.Commands int indexOf = m_Params[i].IndexOf( '=' ); if ( indexOf >= 0 ) + { sp.Count = Utility.ToInt32( m_Params[i].Substring( ++indexOf ) ); + for ( int se = 0;se < sp.Entries.Count; se++ ) + sp.Entries[se].SpawnedMaxCount = sp.Count; + } } else if ( m_Params[i].StartsWith( "Team" ) ) { diff --git a/Scripts/Commands/DecorateMag.cs b/Scripts/Commands/DecorateMag.cs index 8025cbd78..8939f309d 100644 --- a/Scripts/Commands/DecorateMag.cs +++ b/Scripts/Commands/DecorateMag.cs @@ -451,7 +451,7 @@ namespace Server.Commands int indexOf = m_Params[i].IndexOf( '=' ); if ( indexOf >= 0 ) - sp.SpawnNames.Add( m_Params[i].Substring( ++indexOf ) ); + sp.AddEntry( m_Params[i].Substring( ++indexOf ), 100, 1 ); } else if ( m_Params[i].StartsWith( "MinDelay" ) ) { @@ -479,7 +479,11 @@ namespace Server.Commands int indexOf = m_Params[i].IndexOf( '=' ); if ( indexOf >= 0 ) + { sp.Count = Utility.ToInt32( m_Params[i].Substring( ++indexOf ) ); + for ( int se = 0;se < sp.Entries.Count; se++ ) + sp.Entries[se].SpawnedMaxCount = sp.Count; + } } else if ( m_Params[i].StartsWith( "Team" ) ) { diff --git a/Scripts/Commands/Handlers.cs b/Scripts/Commands/Handlers.cs index 99a270653..cf26b4550 100644 --- a/Scripts/Commands/Handlers.cs +++ b/Scripts/Commands/Handlers.cs @@ -74,8 +74,6 @@ namespace Server.Commands Register( "Light", AccessLevel.Counselor, new CommandEventHandler( Light_OnCommand ) ); Register( "Stats", AccessLevel.Counselor, new CommandEventHandler( Stats_OnCommand ) ); - Register( "ReplaceBankers", AccessLevel.Administrator, new CommandEventHandler( ReplaceBankers_OnCommand ) ); - Register( "SpeedBoost", AccessLevel.Counselor, new CommandEventHandler( SpeedBoost_OnCommand ) ); } @@ -334,46 +332,6 @@ namespace Server.Commands } } - public static void ReplaceBankers_OnCommand( CommandEventArgs e ) - { - List list = new List(); - - foreach ( Mobile m in World.Mobiles.Values ) - if ( (m is Banker) && !(m is BaseCreature) ) - list.Add( m ); - - foreach ( Mobile m in list ) - { - Map map = m.Map; - - if ( map != null ) - { - bool hasBankerSpawner = false; - - foreach ( Item item in m.GetItemsInRange( 0 ) ) - { - if ( item is Spawner ) - { - Spawner spawner = (Spawner)item; - - for ( int i = 0; !hasBankerSpawner && i < spawner.SpawnNames.Count; ++i ) - hasBankerSpawner = Insensitive.Equals( (string)spawner.SpawnNames[i], "banker" ); - - if ( hasBankerSpawner ) - break; - } - } - - if ( !hasBankerSpawner ) - { - Spawner spawner = new Spawner( 1, 1, 5, 0, 4, "banker" ); - - spawner.MoveToWorld( m.Location, map ); - } - } - } - } - private class ViewEqTarget : Target { public ViewEqTarget() : base( -1, false, TargetFlags.None ) @@ -1015,4 +973,4 @@ namespace Server.Commands e.Mobile.SendMessage( "Items: {0}", World.Items.Count ); } } -} \ No newline at end of file +} diff --git a/Scripts/Engines/Spawner/ProximitySpawner.cs b/Scripts/Engines/Spawner/ProximitySpawner.cs index 82ee3dd86..4d12af3fe 100644 --- a/Scripts/Engines/Spawner/ProximitySpawner.cs +++ b/Scripts/Engines/Spawner/ProximitySpawner.cs @@ -10,21 +10,21 @@ namespace Server.Mobiles private TextDefinition m_SpawnMessage; private bool m_InstantFlag; - [CommandProperty( AccessLevel.GameMaster )] + [CommandProperty( AccessLevel.Developer )] public int TriggerRange { get { return m_TriggerRange; } set { m_TriggerRange = value; } } - [CommandProperty( AccessLevel.GameMaster )] + [CommandProperty( AccessLevel.Developer )] public TextDefinition SpawnMessage { get { return m_SpawnMessage; } set { m_SpawnMessage = value; } } - [CommandProperty( AccessLevel.GameMaster )] + [CommandProperty( AccessLevel.Developer )] public bool InstantFlag { get { return m_InstantFlag; } @@ -49,7 +49,7 @@ namespace Server.Mobiles } [Constructible] - public ProximitySpawner( int amount, int minDelay, int maxDelay, int team, int homeRange, string spawnName, int triggerRange, string spawnMessage, bool instantFlag ) + public ProximitySpawner( int amount, int minDelay, int maxDelay, int team, int homeRange, int triggerRange, string spawnMessage, bool instantFlag, string spawnName ) : base( amount, minDelay, maxDelay, team, homeRange, spawnName ) { m_TriggerRange = triggerRange; @@ -57,13 +57,13 @@ namespace Server.Mobiles m_InstantFlag = instantFlag; } - public ProximitySpawner( int amount, TimeSpan minDelay, TimeSpan maxDelay, int team, int homeRange, List spawnNames ) - : base( amount, minDelay, maxDelay, team, homeRange, spawnNames ) + public ProximitySpawner( int amount, TimeSpan minDelay, TimeSpan maxDelay, int team, int homeRange, params string[] spawnedNames ) + : base( amount, minDelay, maxDelay, team, homeRange, spawnedNames ) { } - public ProximitySpawner( int amount, TimeSpan minDelay, TimeSpan maxDelay, int team, int homeRange, List spawnNames, int triggerRange, TextDefinition spawnMessage, bool instantFlag ) - : base( amount, minDelay, maxDelay, team, homeRange, spawnNames ) + public ProximitySpawner( int amount, TimeSpan minDelay, TimeSpan maxDelay, int team, int homeRange, int triggerRange, TextDefinition spawnMessage, bool instantFlag, params string[] spawnedNames ) + : base( amount, minDelay, maxDelay, team, homeRange, spawnedNames ) { m_TriggerRange = triggerRange; m_SpawnMessage = spawnMessage; @@ -85,42 +85,24 @@ namespace Server.Mobiles public override void Respawn() { - RemoveSpawned(); + RemoveSpawns(); End = DateTime.UtcNow; } - public override void Spawn() - { - for ( int i = 0; i < SpawnNamesCount; ++i ) - { - for ( int j = 0; j < Count; ++j ) - Spawn( i ); - } - } - - public override bool CheckSpawnerFull() - { - return false; - } - public override bool HandlesOnMovement { get { return true; } } public virtual bool ValidTrigger( Mobile m ) { if ( m is BaseCreature ) { - BaseCreature bc = (BaseCreature)m; + BaseCreature bc = m as BaseCreature; - if ( !bc.Controlled && !bc.Summoned ) + if ( bc.IsDeadBondedPet || !(bc.Controlled || bc.Summoned) ) return false; } - else if ( !m.Player ) - { - return false; - } - return ( m.Alive && !m.IsDeadBondedPet && m.CanBeDamaged() && !m.Hidden ); + return m.AccessLevel == AccessLevel.Player && ( m.Player || ( m.Alive && !m.Hidden && m.CanBeDamaged() ) ); } public override void OnMovement( Mobile m, Point3D oldLocation ) @@ -137,11 +119,9 @@ namespace Server.Mobiles if ( m_InstantFlag ) { - foreach ( ISpawnable spawned in Spawned ) - { + foreach ( ISpawnable spawned in Spawned.Keys ) if ( spawned is Mobile ) ((Mobile)spawned).Combatant = m; - } } } } @@ -155,7 +135,7 @@ namespace Server.Mobiles { base.Serialize( writer ); - writer.Write( (int) 0 ); // version + writer.WriteEncodedInt( (int) 0 ); // version writer.Write( m_TriggerRange ); TextDefinition.Serialize( writer, m_SpawnMessage ); @@ -166,7 +146,7 @@ namespace Server.Mobiles { base.Deserialize( reader ); - int version = reader.ReadInt(); + int version = reader.ReadEncodedInt(); m_TriggerRange = reader.ReadInt(); m_SpawnMessage = TextDefinition.Deserialize( reader ); diff --git a/Scripts/Engines/Spawner/Spawner.cs b/Scripts/Engines/Spawner/Spawner.cs index 443a89b44..58d3d3958 100644 --- a/Scripts/Engines/Spawner/Spawner.cs +++ b/Scripts/Engines/Spawner/Spawner.cs @@ -1,151 +1,76 @@ using System; -using System.Collections; -using System.Collections.Generic; using System.IO; using System.Reflection; -using System.Text; +using System.Collections; +using System.Collections.Generic; using Server; -using Server.Commands; using Server.Items; -using Server.Network; -using Server.Multis; +using Server.Commands; using CPA = Server.CommandPropertyAttribute; -/* - UsesSpawnerHome true causes normal behavior, while false will - cause the spawner to set the mobile's home to be its spawn - location, thus, not walking back to the spawner. This will - create a less artificial feel to mobiles attempting to return - to their home location. - - Also, the spawn area and home range work together. If the - area is not set, they will behave pretty much like they - always have. If the area is set, the mobile will spawn within - that rectangle. If both are set, the spawn location will be - based on the rectangle and allow an additional # of tiles, - which is the home range. - - Also, since the home does not necessarily equate to the spawner - location any longer, a gettersetter was added to the - BaseCreature. -*/ - namespace Server.Mobiles { public class Spawner : Item, ISpawner { private int m_Team; private int m_HomeRange; - private int m_WalkingRange; private int m_Count; + private int m_WalkingRange = -1; private TimeSpan m_MinDelay; private TimeSpan m_MaxDelay; - private List m_SpawnNames; - private List m_Spawned; + + private List m_Entries; + private Dictionary m_Spawned; + private DateTime m_End; private InternalTimer m_Timer; private bool m_Running; private bool m_Group; private WayPoint m_WayPoint; - private bool m_UsesSpawnerHome; - private Rectangle2D m_SpawnArea; - private bool m_IgnoreHousing; - private bool m_MobilesSeekHome; - public bool IsFull{ get{ return ( m_Spawned.Count >= m_Count ); } } - public bool IsEmpty{ get{ return ( m_Spawned.Count == 0 ); } } + public override string DefaultName{ get{ return "Spawner"; } } + public bool IsFull{ get{ return ( m_Spawned != null && m_Spawned.Count >= m_Count ); } } + public bool IsEmpty{ get{ return ( m_Spawned != null && m_Spawned.Count == 0 ); } } - public List SpawnNames + public Point3D HomeLocation{ get{ return Location; } } + public bool UnlinkOnTaming{ get{ return true; } } + public DateTime End{ get{ return m_End; } set{ m_End = value; } } + + public List Entries{ get{ return m_Entries; } } + public Dictionary Spawned{ get{ return m_Spawned; } } + + public override void OnAfterDuped( Item newItem ) { - get { return m_SpawnNames; } + if ( newItem is Spawner ) + { + Spawner newSpawner = newItem as Spawner; + for ( int i = 0; i < m_Entries.Count; i++ ) + newSpawner.AddEntry( m_Entries[i].SpawnedName, m_Entries[i].SpawnedProbability, m_Entries[i].SpawnedMaxCount, false ); + } + } + + [CommandProperty( AccessLevel.Developer )] + public int Count + { + get { return m_Count; } set { - m_SpawnNames = value; - if ( m_SpawnNames.Count < 1 ) - Stop(); + m_Count = value; + + if ( m_Timer != null ) + { + if ( ( !IsFull && !m_Timer.Running ) || IsFull && m_Timer.Running ) + DoTimer(); + } InvalidateProperties(); } } - public List Spawned - { - get { return m_Spawned; } - } + [CommandProperty( AccessLevel.Developer )] + public WayPoint WayPoint{ get{ return m_WayPoint; } set{ m_WayPoint = value; } } - public virtual int SpawnNamesCount { get { return m_SpawnNames.Count; } } - - public override void OnAfterDuped( Item newItem ) - { - Spawner s = newItem as Spawner; - - if ( s == null ) - return; - - s.m_SpawnNames = new List( m_SpawnNames ); - s.m_Spawned = new List(); - } - - [CommandProperty( AccessLevel.GameMaster )] - public bool IgnoreHousing - { - get - { - return m_IgnoreHousing; - } - set - { - m_IgnoreHousing = value; - } - } - - [CommandProperty( AccessLevel.GameMaster )] - public bool MobilesSeekHome - { - get - { - return m_MobilesSeekHome; - } - set - { - m_MobilesSeekHome = value; - } - } - - [CommandProperty( AccessLevel.GameMaster )] - public Rectangle2D SpawnArea - { - get - { - return m_SpawnArea; - } - set - { - m_SpawnArea = value; - } - } - - [CommandProperty( AccessLevel.GameMaster )] - public int Count - { - get { return m_Count; } - set { m_Count = value; InvalidateProperties(); } - } - - [CommandProperty( AccessLevel.GameMaster )] - public WayPoint WayPoint - { - get - { - return m_WayPoint; - } - set - { - m_WayPoint = value; - } - } - - [CommandProperty( AccessLevel.GameMaster )] + [CommandProperty( AccessLevel.Developer )] public bool Running { get { return m_Running; } @@ -160,66 +85,27 @@ namespace Server.Mobiles } } - [CommandProperty( AccessLevel.GameMaster )] - public bool UsesSpawnerHome - { - get - { - return m_UsesSpawnerHome; - } - set - { - m_UsesSpawnerHome = value; - } - } + [CommandProperty( AccessLevel.Developer )] + public int HomeRange{ get { return m_HomeRange; } set { m_HomeRange = value; InvalidateProperties(); } } - [CommandProperty( AccessLevel.GameMaster )] - public int HomeRange - { - get { return m_HomeRange; } - set { m_HomeRange = value; InvalidateProperties(); } - } + [CommandProperty( AccessLevel.Developer )] + public int WalkingRange{ get { return m_WalkingRange; } set { m_WalkingRange = value; InvalidateProperties(); } } - [CommandProperty( AccessLevel.GameMaster )] - public int WalkingRange - { - get { return m_WalkingRange; } - set { m_WalkingRange = value; InvalidateProperties(); } - } + [CommandProperty( AccessLevel.Developer )] + public int Team{ get { return m_Team; } set { m_Team = value; InvalidateProperties(); } } - [CommandProperty( AccessLevel.GameMaster )] - public int Team - { - get { return m_Team; } - set { m_Team = value; InvalidateProperties(); } - } + [CommandProperty( AccessLevel.Developer )] + public TimeSpan MinDelay{ get { return m_MinDelay; } set { m_MinDelay = value; InvalidateProperties(); } } - [CommandProperty( AccessLevel.GameMaster )] - public TimeSpan MinDelay - { - get { return m_MinDelay; } - set { m_MinDelay = value; InvalidateProperties(); } - } + [CommandProperty( AccessLevel.Developer )] + public TimeSpan MaxDelay{ get { return m_MaxDelay; } set { m_MaxDelay = value; InvalidateProperties(); } } - [CommandProperty( AccessLevel.GameMaster )] - public TimeSpan MaxDelay - { - get { return m_MaxDelay; } - set { m_MaxDelay = value; InvalidateProperties(); } - } - - public DateTime End - { - get { return m_End; } - set { m_End = value; } - } - - [CommandProperty( AccessLevel.GameMaster )] + [CommandProperty( AccessLevel.Developer )] public TimeSpan NextSpawn { get { - if ( m_Running ) + if ( m_Running && m_Timer != null && m_Timer.Running ) return m_End - DateTime.UtcNow; else return TimeSpan.FromSeconds( 0 ); @@ -231,55 +117,58 @@ namespace Server.Mobiles } } - [CommandProperty( AccessLevel.GameMaster )] + [CommandProperty( AccessLevel.Developer )] public bool Group { get { return m_Group; } set { m_Group = value; InvalidateProperties(); } } - [Constructible] - public Spawner() - : this(null) + Region ISpawner.Region{ get{ return Region.Find( Location, Map ); } } + +// [Constructible] + public Spawner( int amount, int minDelay, int maxDelay, int team, int homeRange, string spawnedNames ) : base( 0x1f13 ) { + InitSpawn( amount, TimeSpan.FromMinutes( minDelay ), TimeSpan.FromMinutes( maxDelay ), team, homeRange ); + AddEntry( spawnedNames, 100, amount, false ); } - [Constructible] - public Spawner( string spawnName ) - : this( 1, 5, 10, 0, 4, spawnName) +// [Constructible] + public Spawner( string spawnedName ) : base( 0x1f13 ) { + InitSpawn( 1, TimeSpan.FromMinutes( 5 ), TimeSpan.FromMinutes( 10 ), 0, 4 ); + AddEntry( spawnedName, 100, 1, false ); } - [Constructible] - public Spawner( int amount, int minDelay, int maxDelay, int team, int homeRange, string spawnName ) - : this( amount, TimeSpan.FromMinutes( minDelay ), TimeSpan.FromMinutes( maxDelay ), team, homeRange, spawnName ) + [Constructible( AccessLevel.Developer )] + public Spawner() : base( 0x1f13 ) { + InitSpawn( 1, TimeSpan.FromMinutes( 5 ), TimeSpan.FromMinutes( 10 ), 0, 4 ); } - [Constructible] - public Spawner(int amount, TimeSpan minDelay, TimeSpan maxDelay, int team, int homeRange, string spawnName) - : base(0x1f13) + public Spawner( int amount, TimeSpan minDelay, TimeSpan maxDelay, int team, int homeRange, params string[] spawnedNames ) : base( 0x1f13 ) { - List spawnNames = new List(); - - if (!String.IsNullOrEmpty(spawnName)) - spawnNames.Add(spawnName); - - InitSpawner(amount, minDelay, maxDelay, team, homeRange, spawnNames); + InitSpawn( amount, minDelay, maxDelay, team, homeRange ); + for ( int i = 0;i < spawnedNames.Length; i++ ) + AddEntry( spawnedNames[i], 100, amount, false ); } - public Spawner(int amount, TimeSpan minDelay, TimeSpan maxDelay, int team, int homeRange, List spawnNames) - : base(0x1f13) + public SpawnerEntry AddEntry( string creaturename, int probability, int amount ) { - InitSpawner(amount, minDelay, maxDelay, team, homeRange, spawnNames); + return AddEntry( creaturename, probability, amount, true ); } - public override string DefaultName + public SpawnerEntry AddEntry( string creaturename, int probability, int amount, bool dotimer ) { - get { return "Spawner"; } + SpawnerEntry entry = new SpawnerEntry( creaturename, probability, amount ); + m_Entries.Add( entry ); + if ( dotimer ) + DoTimer( TimeSpan.FromSeconds( 1 ) ); + + return entry; } - private void InitSpawner( int amount, TimeSpan minDelay, TimeSpan maxDelay, int team, int homeRange, List spawnNames ) + public void InitSpawn( int amount, TimeSpan minDelay, TimeSpan maxDelay, int team, int homeRange ) { Visible = false; Movable = false; @@ -290,9 +179,9 @@ namespace Server.Mobiles m_Count = amount; m_Team = team; m_HomeRange = homeRange; - m_WalkingRange = -1; - m_SpawnNames = spawnNames; - m_Spawned = new List(); + m_Entries = new List(); + m_Spawned = new Dictionary(); + DoTimer( TimeSpan.FromSeconds( 1 ) ); } @@ -302,11 +191,8 @@ namespace Server.Mobiles public override void OnDoubleClick( Mobile from ) { - if ( from.AccessLevel < AccessLevel.GameMaster ) - return; - - SpawnerGump g = new SpawnerGump( this ); - from.SendGump( g ); + if ( from.AccessLevel >= AccessLevel.Developer ) + from.SendGump( new SpawnerGump( this ) ); } public override void GetProperties( ObjectPropertyList list ) @@ -321,17 +207,15 @@ namespace Server.Mobiles list.Add( 1061169, m_HomeRange.ToString() ); // range ~1_val~ list.Add( 1060658, "walking range\t{0}", m_WalkingRange ); // ~1_val~: ~2_val~ - list.Add( 1060659, "group\t{0}", m_Group ); // ~1_val~: ~2_val~ - list.Add( 1060660, "team\t{0}", m_Team ); // ~1_val~: ~2_val~ - list.Add( 1060661, "speed\t{0} to {1}", m_MinDelay, m_MaxDelay ); // ~1_val~: ~2_val~ + list.Add( 1060658, "group\t{0}", m_Group ); // ~1_val~: ~2_val~ + list.Add( 1060659, "team\t{0}", m_Team ); // ~1_val~: ~2_val~ + list.Add( 1060660, "speed\t{0} to {1}", m_MinDelay, m_MaxDelay ); // ~1_val~: ~2_val~ - if ( m_SpawnNames.Count != 0 ) - list.Add( SpawnedStats() ); + for ( int i = 0; i < 3 && i < m_Entries.Count; ++i ) + list.Add( 1060661 + i, "{0}\t{1}", m_Entries[i].SpawnedName, CountSpawns( m_Entries[i] ) ); } else - { list.Add( 1060743 ); // inactive - } } public override void OnSingleClick( Mobile from ) @@ -348,7 +232,7 @@ namespace Server.Mobiles { if ( !m_Running ) { - if ( SpawnNamesCount > 0 ) + if ( m_Entries.Count > 0 ) { m_Running = true; DoTimer(); @@ -362,181 +246,121 @@ namespace Server.Mobiles { if ( m_Timer != null ) m_Timer.Stop(); - m_Running = false; } } - public static string ParseType( string s ) - { - return s.Split( null, 2 )[0]; - } - public void Defrag() { - bool removed = false; + if ( m_Entries == null ) + m_Entries = new List(); - for ( int i = 0; i < m_Spawned.Count; ++i ) - { - ISpawnable e = m_Spawned[i]; - - bool toRemove = false; - - if( e is Item ) - { - Item item = (Item)e; - - if (item.Deleted || item.Parent != null) - toRemove = true; - } - else if (e is Mobile) - { - Mobile m = (Mobile)e; - - if (m.Deleted) - { - toRemove = true; - } - else if (m is BaseCreature) - { - BaseCreature bc = (BaseCreature)m; - - if (bc.Controlled || bc.IsStabled) - { - toRemove = true; - } - } - } - - if (toRemove) - { - m_Spawned.RemoveAt(i); - --i; - removed = true; - } - } - - if ( removed ) - InvalidateProperties(); - } - - bool ISpawner.UnlinkOnTaming { get { return true; } } - - void ISpawner.Remove( ISpawnable spawn ) - { - m_Spawned.Remove( spawn ); - - InvalidateProperties(); + for ( int i = 0; i < m_Entries.Count; ++i ) + m_Entries[i].Defrag( this ); } public void OnTick() { - DoTimer(); +// DoTimer( m_Spawned.Count >= m_Count ); if ( m_Group ) { Defrag(); - if ( m_Spawned.Count == 0 ) - { - Respawn(); - } - else - { + if ( m_Spawned.Count > 0 ) return; - } + + Respawn(); } else - { Spawn(); +/* + if ( m_Running && m_Timer != null ) + { + if ( m_Spawned.Count >= m_Count && m_Timer.Running ) + DoTimer( true ); + else if ( m_Spawned.Count < m_Count && !m_Timer.Running ) + DoTimer( false ); } +*/ + DoTimer(); } public virtual void Respawn() { - RemoveSpawned(); + RemoveSpawns(); for ( int i = 0; i < m_Count; i++ ) Spawn(); + + DoTimer(); //Turn off the timer! } public virtual void Spawn() { - if ( SpawnNamesCount > 0 ) - Spawn( Utility.Random( SpawnNamesCount ) ); - } + Defrag(); - public void Spawn( string creatureName ) - { - for ( int i = 0; i < m_SpawnNames.Count; i++ ) + if ( m_Entries.Count > 0 && !IsFull ) { - if ( m_SpawnNames[i] == creatureName ) + int probsum = 0; + + for ( int i = 0; i < m_Entries.Count; i++ ) + if ( !m_Entries[i].IsFull ) + probsum += m_Entries[i].SpawnedProbability; + + if ( probsum > 0 ) { - Spawn( i ); - break; + int rand = Utility.RandomMinMax( 1, probsum ); + + for ( int i = 0; i < m_Entries.Count; i++ ) + { + SpawnerEntry entry = m_Entries[i]; + if ( !entry.IsFull ) + { + bool success = true; + + if ( rand <= entry.SpawnedProbability ) + { + EntryFlags flags; + success = Spawn( entry, out flags ); + entry.Valid = flags; + return; + } + + if ( success ) + rand -= entry.SpawnedProbability; + } + } } } } - protected virtual ISpawnable CreateSpawnedObject( int index ) + private static string[,] FormatProperties( string[] args ) { - if ( index >= m_SpawnNames.Count ) - return null; - - Type type = ScriptCompiler.FindTypeByName( ParseType( m_SpawnNames[index] ) ); - - if ( type != null ) - { - try - { - return Build( type, CommandSystem.Split( m_SpawnNames[index] ) ); - } - catch - { - } - } - - return null; - } - - public static ISpawnable Build( Type type, string[] args) - { - bool isISpawnable = typeof(ISpawnable).IsAssignableFrom( type ); - - if (!isISpawnable) - { - return null; - } - - Add.FixArgs( ref args ); - string[,] props = null; - for ( int i = 0; i < args.Length; ++i ) + int remains = args.Length; + + if ( remains >= 2 ) { - if ( Insensitive.Equals( args[i], "set" ) ) + props = new string[remains / 2, 2]; + + remains /= 2; + + for ( int j = 0; j < remains; ++j ) { - int remains = args.Length - i - 1; - - if ( remains >= 2 ) - { - props = new string[remains / 2, 2]; - - remains /= 2; - - for ( int j = 0; j < remains; ++j ) - { - props[j, 0] = args[i + (j * 2) + 1]; - props[j, 1] = args[i + (j * 2) + 2]; - } - - Add.FixSetString( ref args, i ); - } - - break; + props[j, 0] = args[j * 2]; + props[j, 1] = args[(j * 2) + 1]; } } + else + props = new string[0,0]; + return props; + } + + private static PropertyInfo[] GetTypeProperties( Type type, string[,] props ) + { PropertyInfo[] realProps = null; if ( props != null ) @@ -557,124 +381,204 @@ namespace Server.Mobiles thisProp = allProps[j]; } - if ( thisProp != null ) + if ( thisProp == null ) + return null; + else { CPA attr = Properties.GetCPA( thisProp ); - if ( attr != null && AccessLevel.GameMaster >= attr.WriteLevel && thisProp.CanWrite && !attr.ReadOnly ) + if ( attr == null || AccessLevel.Developer < attr.WriteLevel || !thisProp.CanWrite || attr.ReadOnly ) + return null; + else realProps[i] = thisProp; } } } - ConstructorInfo[] ctors = type.GetConstructors(); + return realProps; + } - for ( int i = 0; i < ctors.Length; ++i ) + public bool Spawn( int index, out EntryFlags flags ) + { + if ( index >= 0 && index < m_Entries.Count ) + return Spawn( m_Entries[index], out flags ); + else { - ConstructorInfo ctor = ctors[i]; + flags = EntryFlags.InvalidEntry; + return false; + } + } - if ( !Add.IsConstructible( ctor, AccessLevel.GameMaster ) ) - continue; + public bool Spawn( SpawnerEntry entry, out EntryFlags flags ) + { + Map map = GetSpawnMap(); + flags = EntryFlags.None; - ParameterInfo[] paramList = ctor.GetParameters(); + if ( map == null || map == Map.Internal || Parent != null ) + return false; - if ( args.Length == paramList.Length ) + //Defrag taken care of in Spawn(), beforehand + //Count check taken care of in Spawn(), beforehand + + Type type = SpawnerType.GetType( entry.SpawnedName ); + + if ( type != null ) + { + try { - object[] paramValues = Add.ParseValues( paramList, args ); + object o = null; + string[] paramargs; + string[] propargs; - if ( paramValues == null ) - continue; + if ( String.IsNullOrEmpty( entry.Properties ) ) + propargs = new string[0]; + else + propargs = CommandSystem.Split( entry.Properties.Trim() ); - object built = ctor.Invoke( paramValues ); + string[,] props = FormatProperties( propargs ); - if ( built != null && realProps != null ) + PropertyInfo[] realProps = GetTypeProperties( type, props ); + + if ( realProps == null ) { - for ( int j = 0; j < realProps.Length; ++j ) - { - if ( realProps[j] == null ) - continue; + flags = EntryFlags.InvalidProps; + return false; + } - string result = Properties.InternalSetValue( built, realProps[j], props[j, 1] ); + if ( String.IsNullOrEmpty( entry.Parameters ) ) + paramargs = new string[0]; + else + paramargs = entry.Parameters.Trim().Split( ' ' ); + + ConstructorInfo[] ctors = type.GetConstructors(); + + for ( int i = 0; i < ctors.Length; ++i ) + { + ConstructorInfo ctor = ctors[i]; + + if ( Add.IsConstructible( ctor, AccessLevel.Developer ) ) + { + ParameterInfo[] paramList = ctor.GetParameters(); + + if ( paramargs.Length == paramList.Length ) + { + object[] paramValues = Add.ParseValues( paramList, paramargs ); + + if ( paramValues != null ) + { + o = ctor.Invoke( paramValues ); + for ( int j = 0; j < realProps.Length; j++ ) + { + if ( realProps[j] != null ) + { + object toSet = null; + string result = Properties.ConstructFromString( realProps[j].PropertyType, o, props[j, 1], ref toSet ); + if ( result == null ) + realProps[j].SetValue( o, toSet, null ); + else + { + flags = EntryFlags.InvalidProps; + + if ( o is ISpawnable ) + ((ISpawnable)o).Delete(); + + return false; + } + } + } + break; + } + } } } - return (ISpawnable)built; + if ( o is Mobile ) + { + Mobile m = (Mobile)o; + + m_Spawned.Add( m, entry ); + entry.Spawned.Add( m ); + + Point3D loc = ( m is BaseVendor ? this.Location : GetSpawnPosition( m, map ) ); + + m.OnBeforeSpawn( loc, map ); + InvalidateProperties(); + + m.MoveToWorld( loc, map ); + + if ( m is BaseCreature ) + { + BaseCreature c = (BaseCreature)m; + + int walkrange = GetWalkingRange(); + + if( walkrange >= 0 ) + c.RangeHome = walkrange; + else + c.RangeHome = m_HomeRange; + + c.CurrentWayPoint = GetWayPoint(); + + if ( m_Team > 0 ) + c.Team = m_Team; + + c.Home = this.Location; + c.HomeMap = this.Map; + } + + m.Spawner = this; + m.OnAfterSpawn(); + } + else if ( o is Item ) + { + Item item = (Item)o; + + m_Spawned.Add( item, entry ); + entry.Spawned.Add( item ); + + Point3D loc = GetSpawnPosition( item, map ); + + item.OnBeforeSpawn( loc, map ); + + item.MoveToWorld( loc, map ); + + item.Spawner = this; + item.OnAfterSpawn(); + } + else + { + flags = EntryFlags.InvalidType | EntryFlags.InvalidParams; + return false; + } } + catch ( Exception e ) + { + Console.WriteLine( "EXCEPTION CAUGHT: {0:X}", Serial ); + Console.WriteLine( e ); + return false; + } + + InvalidateProperties(); + return true; } - return null; + flags = EntryFlags.InvalidType; + return false; } - public Point3D HomeLocation { get { return this.Location; } } - - public virtual bool CheckSpawnerFull() + public virtual int GetWalkingRange() { - return ( m_Spawned.Count >= m_Count ); + return m_WalkingRange; } - public void Spawn( int index ) + public virtual WayPoint GetWayPoint() { - Map map = Map; - - if ( map == null || map == Map.Internal || SpawnNamesCount == 0 || index >= SpawnNamesCount || Parent != null ) - return; - - Defrag(); - - if ( CheckSpawnerFull() ) - return; - - ISpawnable spawned = CreateSpawnedObject( index ); - - if ( spawned == null ) - return; - - spawned.Spawner = this; - m_Spawned.Add( spawned ); - - Point3D loc = ( spawned is BaseVendor ? this.Location : GetSpawnPosition( spawned ) ); - - spawned.OnBeforeSpawn( loc, map ); - spawned.MoveToWorld( loc, map ); - spawned.OnAfterSpawn(); - - if ( spawned is BaseCreature ) - { - BaseCreature bc = (BaseCreature)spawned; - - if( m_WalkingRange >= 0 ) - bc.RangeHome = m_WalkingRange; - else - bc.RangeHome = m_HomeRange; - - bc.CurrentWayPoint = m_WayPoint; - - bc.SeeksHome = m_MobilesSeekHome; - - if ( m_Team > 0 ) - bc.Team = m_Team; - - bc.Home = ( m_UsesSpawnerHome ) ? this.HomeLocation : bc.Location ; - } - - InvalidateProperties(); + return m_WayPoint; } - public Point3D GetSpawnPosition() + public virtual Point3D GetSpawnPosition( ISpawnable spawned, Map map ) { - return GetSpawnPosition( null ); - } - - private int GetAdjustedLocation( int range, int side, int coord, int coord_this ) - { - return ( ( ( coord > 0 ) ? coord : ( coord_this - range ) ) + ( Utility.Random( Math.Max( ( ( ( range * 2 ) + 1 ) + side ), 1 ) ) ) ); - } - - public Point3D GetSpawnPosition( ISpawnable spawned ) - { - Map map = Map; - - if ( map == null ) + if ( map == null || map == Map.Internal ) return Location; bool waterMob, waterOnlyMob; @@ -692,31 +596,29 @@ namespace Server.Mobiles waterOnlyMob = false; } - for( int i = 0; i < 10; ++i ) + // Try 10 times to find a Spawnable location. + for ( int i = 0; i < 10; i++ ) { - int x = GetAdjustedLocation( m_HomeRange, m_SpawnArea.Width, m_SpawnArea.X, X ); - int y = GetAdjustedLocation( m_HomeRange, m_SpawnArea.Height, m_SpawnArea.Y, Y ); + int x = Location.X + (Utility.Random( (m_HomeRange * 2) + 1 ) - m_HomeRange); + int y = Location.Y + (Utility.Random( (m_HomeRange * 2) + 1 ) - m_HomeRange); + int z = Map.GetAverageZ( x, y ); - int mapZ = map.GetAverageZ( x, y ); + int mapZ = map.GetAverageZ( x, y ); - if( m_IgnoreHousing || ( ( BaseHouse.FindHouseAt( new Point3D( x, y, mapZ ), Map, 16 ) == null && - BaseHouse.FindHouseAt( new Point3D( x, y, this.Z ), Map, 16 ) == null ) ) ) + if ( waterMob ) { - if( waterMob ) - { - if( IsValidWater( map, x, y, this.Z ) ) - return new Point3D( x, y, this.Z ); - else if( IsValidWater( map, x, y, mapZ ) ) - return new Point3D( x, y, mapZ ); - } + if ( IsValidWater( map, x, y, this.Z ) ) + return new Point3D( x, y, this.Z ); + else if ( IsValidWater( map, x, y, mapZ ) ) + return new Point3D( x, y, mapZ ); + } - if( !waterOnlyMob ) - { - if( map.CanSpawnMobile( x, y, this.Z ) ) - return new Point3D( x, y, this.Z ); - else if( map.CanSpawnMobile( x, y, mapZ ) ) - return new Point3D( x, y, mapZ ); - } + if ( !waterOnlyMob ) + { + if ( map.CanSpawnMobile( x, y, this.Z ) ) + return new Point3D( x, y, this.Z ); + else if ( map.CanSpawnMobile( x, y, mapZ ) ) + return new Point3D( x, y, mapZ ); } } @@ -746,6 +648,11 @@ namespace Server.Mobiles return false; } + public virtual Map GetSpawnMap() + { + return Map; + } + public void DoTimer() { if ( !m_Running ) @@ -769,7 +676,8 @@ namespace Server.Mobiles m_Timer.Stop(); m_Timer = new InternalTimer( this, delay ); - m_Timer.Start(); + if ( !IsFull ) + m_Timer.Start(); } private class InternalTimer : Timer @@ -794,78 +702,97 @@ namespace Server.Mobiles } } - public string SpawnedStats() + public int CountSpawns( SpawnerEntry entry ) { Defrag(); - Dictionary counts = new Dictionary( StringComparer.OrdinalIgnoreCase ); - - foreach ( string entry in m_SpawnNames ) - { - string name = ParseType( entry ); - Type type = ScriptCompiler.FindTypeByName( name ); - - if ( type == null ) - counts[name] = 0; - else - counts[type.Name] = 0; - } - - foreach ( ISpawnable spawned in m_Spawned ) - { - string name = spawned.GetType().Name; - - if ( counts.ContainsKey( name ) ) - ++counts[name]; - else - counts[name] = 1; - } - - List names = new List( counts.Keys ); - names.Sort(); - - StringBuilder result = new StringBuilder(); - - for ( int i = 0; i < names.Count; ++i ) - result.AppendFormat( "{0}{1}: {2}", ( i == 0 ) ? "" : "
", names[i], counts[names[i]] ); - - return result.ToString(); + return entry.Spawned.Count; } - public int CountCreatures( string creatureName ) + public void RemoveEntry( SpawnerEntry entry ) { Defrag(); - int count = 0; - - for ( int i = 0; i < m_Spawned.Count; ++i ) - if ( Insensitive.Equals( creatureName, m_Spawned[i].GetType().Name ) ) - ++count; - - return count; - } - - public void RemoveSpawned( string creatureName ) - { - Defrag(); - - for ( int i = m_Spawned.Count - 1; i >= 0; --i ) + for ( int i = entry.Spawned.Count-1; i >= 0; i-- ) { - IEntity e = m_Spawned[i]; - - if ( Insensitive.Equals( creatureName, e.GetType().Name ) ) + ISpawnable e = entry.Spawned[i]; + entry.Spawned.RemoveAt( i ); + if ( e != null ) e.Delete(); } + m_Entries.Remove( entry ); + + if ( m_Running && !IsFull && m_Timer != null && !m_Timer.Running ) + DoTimer(); + InvalidateProperties(); } - public void RemoveSpawned() + public void Remove( ISpawnable spawn ) { Defrag(); - for ( int i = m_Spawned.Count - 1; i >= 0; --i ) - m_Spawned[i].Delete(); + if ( spawn != null ) + { + SpawnerEntry entry; + m_Spawned.TryGetValue( spawn, out entry ); + + if ( entry != null ) + entry.Spawned.Remove( spawn ); + + m_Spawned.Remove( spawn ); + } + + if ( m_Running && !IsFull && m_Timer != null && !m_Timer.Running ) + DoTimer(); + } + + public void RemoveSpawn( int index ) //Entry + { + if ( index >= 0 && index < m_Entries.Count ) + RemoveSpawn( m_Entries[index] ); + } + + public void RemoveSpawn( SpawnerEntry entry ) + { + for ( int i = entry.Spawned.Count-1; i >= 0; i-- ) + { + ISpawnable e = entry.Spawned[i]; + + if ( e != null ) + { + entry.Spawned.RemoveAt( i ); + m_Spawned.Remove( e ); + + e.Delete(); + } + } + } + + public void RemoveSpawns() + { + Defrag(); + + for ( int i = 0;i < m_Entries.Count; i++ ) + { + SpawnerEntry entry = m_Entries[i]; + + for ( int j = entry.Spawned.Count - 1; j >= 0; j-- ) + { + ISpawnable e = entry.Spawned[j]; + + if ( e != null ) + { + m_Spawned.Remove( e ); + entry.Spawned.RemoveAt( j ); + e.Delete(); + } + } + } + + if ( m_Running && !IsFull && m_Timer != null && !m_Timer.Running ) + DoTimer(); InvalidateProperties(); } @@ -874,37 +801,29 @@ namespace Server.Mobiles { Defrag(); - for ( int i = 0; i < m_Spawned.Count; ++i ) - { - ISpawnable e = m_Spawned[i]; - - e.MoveToWorld( this.Location, this.Map ); - } + foreach( ISpawnable e in m_Spawned.Keys ) + if ( e != null ) + e.MoveToWorld( Location, Map ); } public override void OnDelete() { base.OnDelete(); - RemoveSpawned(); - - if ( m_Timer != null ) - m_Timer.Stop(); + Stop(); + RemoveSpawns(); } public override void Serialize( GenericWriter writer ) { base.Serialize( writer ); - writer.Write( (int) 6 ); // version + writer.Write( (int) 7 ); // version - writer.Write( m_MobilesSeekHome ); + writer.Write( m_Entries.Count ); - writer.Write( m_IgnoreHousing ); - - writer.Write( m_SpawnArea ); - - writer.Write( m_UsesSpawnerHome ); + for ( int i = 0; i < m_Entries.Count; ++i ) + m_Entries[i].Serialize( writer ); writer.Write( m_WalkingRange ); @@ -921,25 +840,6 @@ namespace Server.Mobiles if ( m_Running ) writer.WriteDeltaTime( m_End ); - - writer.Write( m_SpawnNames.Count ); - - for ( int i = 0; i < m_SpawnNames.Count; ++i ) - writer.Write( m_SpawnNames[i] ); - - writer.Write( m_Spawned.Count ); - - for ( int i = 0; i < m_Spawned.Count; ++i ) - { - IEntity e = m_Spawned[i]; - - if ( e is Item ) - writer.Write( (Item)e ); - else if ( e is Mobile ) - writer.Write( (Mobile)e ); - else - writer.Write( Serial.MinusOne ); - } } private static WarnTimer m_WarnTimer; @@ -950,18 +850,49 @@ namespace Server.Mobiles int version = reader.ReadInt(); + m_Spawned = new Dictionary(); + + if ( version < 7 ) + m_Entries = new List(); + switch ( version ) { + case 7: + { + int size = reader.ReadInt(); + + m_Entries = new List( size ); + + for ( int i = 0; i < size; ++i ) + m_Entries.Add( new SpawnerEntry( this, reader ) ); + + goto case 4; //Skip the other crap + } case 6: { - m_MobilesSeekHome = reader.ReadBool(); - m_UsesSpawnerHome = reader.ReadBool(); + int size = reader.ReadInt(); + + bool addentries = m_Entries.Count == 0; + + for ( int i = 0; i < size; ++i ) + if ( addentries ) + m_Entries.Add( new SpawnerEntry( String.Empty, 100, reader.ReadInt() ) ); + else + m_Entries[i].SpawnedMaxCount = reader.ReadInt(); + goto case 5; } case 5: { - m_SpawnArea = reader.ReadRect2D(); - m_UsesSpawnerHome = reader.ReadBool(); + int size = reader.ReadInt(); + + bool addentries = m_Entries.Count == 0; + + for ( int i = 0; i < size; ++i ) + if ( addentries ) + m_Entries.Add( new SpawnerEntry( String.Empty, reader.ReadInt(), 1 ) ); + else + m_Entries[i].SpawnedProbability = reader.ReadInt(); goto case 4; } @@ -1000,50 +931,84 @@ namespace Server.Mobiles if ( m_Running ) ts = reader.ReadDeltaTime() - DateTime.UtcNow; - int size = reader.ReadInt(); - - m_SpawnNames = new List( size ); - - for ( int i = 0; i < size; ++i ) + if ( version < 7 ) { - string creatureString = reader.ReadString(); + int size = reader.ReadInt(); - m_SpawnNames.Add( creatureString ); - string typeName = ParseType( creatureString ); + bool addentries = m_Entries.Count == 0; - if ( ScriptCompiler.FindTypeByName( typeName ) == null ) + for ( int i = 0; i < size; ++i ) { - if ( m_WarnTimer == null ) - m_WarnTimer = new WarnTimer(); + string typeName = reader.ReadString(); - m_WarnTimer.Add( Location, Map, typeName ); + if ( addentries ) + m_Entries.Add( new SpawnerEntry( typeName, 100, 1 ) ); + else + m_Entries[i].SpawnedName = typeName; + + if ( SpawnerType.GetType( typeName ) == null ) + { + if ( m_WarnTimer == null ) + m_WarnTimer = new WarnTimer(); + + m_WarnTimer.Add( Location, Map, typeName ); + } + } + + int count = reader.ReadInt(); + + for ( int i = 0; i < count; ++i ) + { + ISpawnable e = reader.ReadEntity() as ISpawnable; + + if ( e != null ) + { + if ( e is BaseCreature ) + ((BaseCreature)e).RemoveIfUntamed = true; + + e.Spawner = this; + + for ( int j = 0;j < m_Entries.Count; j++ ) + { + if ( SpawnerType.GetType( m_Entries[j].SpawnedName ) == e.GetType() ) + { + m_Entries[j].Spawned.Add( e ); + m_Spawned.Add( e, m_Entries[j] ); + break; + } + } + } } } - int count = reader.ReadInt(); - - m_Spawned = new List(count); - - for ( int i = 0; i < count; ++i ) - { - ISpawnable e = World.FindEntity(reader.ReadInt()) as ISpawnable; - - if (e != null) - { - e.Spawner = this; - m_Spawned.Add(e); - } - } - - if ( m_Running ) - DoTimer( ts ); + DoTimer( ts ); break; } } - if ( version < 3 && Weight == 0 ) - Weight = -1; + if ( version < 4 ) + m_WalkingRange = m_HomeRange; + } + + public static string ConvertTypes( string type ) + { + type = type.ToLower(); + switch ( type ) + { + case "wheat": return "WheatSheaf"; + case "noxxiousmage": return "NoxiousMage"; + case "noxxiousarcher": return "NoxiousArcher"; + case "noxxiouswarrior": return "NoxiousWarrior"; + case "noxxiouswarlord": return "NoxiousWarlord"; + case "obsidian": return "obsidianstatue"; + case "adeepwaterelemental": return "deepwaterelemental"; + case "noxskeleton": return "poisonskeleton"; + case "earthcaller": return "earthsummoner"; + case "bonedemon": return "bonedaemon"; + } + + return type; } private class WarnTimer : Timer @@ -1083,7 +1048,7 @@ namespace Server.Mobiles using ( StreamWriter op = new StreamWriter( "badspawn.log", true ) ) { - op.WriteLine( "# Bad spawns : {0}", DateTime.UtcNow ); + op.WriteLine( "# Bad spawns : {0}", DateTime.Now ); op.WriteLine( "# Format: X Y Z F Name" ); op.WriteLine(); @@ -1100,4 +1065,181 @@ namespace Server.Mobiles } } } -} \ No newline at end of file + + [Flags] + public enum EntryFlags + { + None = 0x000, + InvalidType = 0x001, + InvalidParams = 0x002, + InvalidProps = 0x004, + InvalidEntry = 0x008 + } + + public class SpawnerEntry + { + private string m_SpawnedName; + private int m_SpawnedProbability; + private List m_Spawned; + private int m_SpawnedMaxCount; + private string m_Properties; + private string m_Parameters; + private EntryFlags m_Valid; + + public int SpawnedProbability + { + get { return m_SpawnedProbability; } + set { m_SpawnedProbability = value; } + } + + public int SpawnedMaxCount + { + get { return m_SpawnedMaxCount; } + set { m_SpawnedMaxCount = value; } + } + + public string SpawnedName + { + get { return m_SpawnedName; } + set { m_SpawnedName = value; } + } + + public string Properties + { + get { return m_Properties; } + set { m_Properties = value; } + } + + public string Parameters + { + get { return m_Parameters; } + set { m_Parameters = value; } + } + + public EntryFlags Valid{ get{ return m_Valid; } set{ m_Valid = value; } } + + public List Spawned{ get { return m_Spawned; } } + public bool IsFull{ get{ return m_Spawned.Count >= m_SpawnedMaxCount; } } + + public SpawnerEntry( string name, int probability, int maxcount ) + { + m_SpawnedName = name; + m_SpawnedProbability = probability; + m_SpawnedMaxCount = maxcount; + m_Spawned = new List(); + } + + public void Serialize( GenericWriter writer ) + { + writer.Write( (int) 0 ); // version + + writer.Write( m_SpawnedName ); + writer.Write( m_SpawnedProbability ); + writer.Write( m_SpawnedMaxCount ); + + writer.Write( m_Properties ); + writer.Write( m_Parameters ); + + writer.Write( m_Spawned.Count ); + + for ( int i = 0; i < m_Spawned.Count; ++i ) + { + object o = m_Spawned[i]; + + if ( o is Item ) + writer.Write( (Item)o ); + else if ( o is Mobile ) + writer.Write( (Mobile)o ); + else + writer.Write( Serial.MinusOne ); + } + } + + public SpawnerEntry( Spawner parent, GenericReader reader ) + { + int version = reader.ReadInt(); + + m_SpawnedName = reader.ReadString(); + m_SpawnedProbability = reader.ReadInt(); + m_SpawnedMaxCount = reader.ReadInt(); + + m_Properties = reader.ReadString(); + m_Parameters = reader.ReadString(); + + int count = reader.ReadInt(); + + m_Spawned = new List( count ); + + for ( int i = 0; i < count; ++i ) + { + //IEntity e = World.FindEntity( reader.ReadInt() ); + ISpawnable e = reader.ReadEntity() as ISpawnable; + + if ( e != null ) + { + e.Spawner = parent; + + if ( e is BaseCreature ) + ((BaseCreature)e).RemoveIfUntamed = true; + + m_Spawned.Add( e ); + + if ( !parent.Spawned.ContainsKey( e ) ) + parent.Spawned.Add( e, this ); + } + } + } + + public void Defrag( Spawner parent ) + { + for ( int i = 0; i < m_Spawned.Count; ++i ) + { + ISpawnable e = m_Spawned[i]; + bool remove = false; + + if ( e is Item ) + { + Item item = (Item)e; + + if ( item.Deleted || item.RootParent is Mobile || item.IsLockedDown || item.IsSecure || item.Spawner == null ) + remove = true; + } + else if ( e is Mobile ) + { + Mobile m = (Mobile)e; + + if ( m.Deleted ) + remove = true; + else if ( m is BaseCreature ) + { + BaseCreature c = (BaseCreature)m; + + if ( c.Controlled || c.IsStabled ) + remove = true; +/* + else if ( c.Combatant == null && ( c.GetDistanceToSqrt( Location ) > (c.RangeHome * 4) ) ) + { + //m_Spawned[i].Delete(); + m_Spawned.RemoveAt( i ); + --i; + c.Delete(); + remove = true; + } +*/ + } + else if ( m.Spawner == null ) + remove = true; + } + else + remove = true; + + if ( remove ) + { + m_Spawned.RemoveAt( i-- ); + if ( parent.Spawned.ContainsKey( e ) ) + parent.Spawned.Remove( e ); + } + } + } + } +} diff --git a/Scripts/Engines/Spawner/SpawnerGump.cs b/Scripts/Engines/Spawner/SpawnerGump.cs index 777ad94f7..102d7e077 100644 --- a/Scripts/Engines/Spawner/SpawnerGump.cs +++ b/Scripts/Engines/Spawner/SpawnerGump.cs @@ -1,142 +1,304 @@ using System; using System.Collections; +using System.Collections.Generic; using Server.Network; using Server.Gumps; -using System.Collections.Generic; namespace Server.Mobiles { public class SpawnerGump : Gump { private Spawner m_Spawner; + private int m_Page; + private SpawnerEntry m_Entry; - public SpawnerGump( Spawner spawner ) : base( 50, 50 ) + public SpawnerGump( Spawner spawner ) : this( spawner, 0 ) + { + } + + public SpawnerGump( Spawner spawner, int page ) : this( spawner, null, page ) + { + } + + public SpawnerGump( Spawner spawner, SpawnerEntry focusentry, int page ) : base( 50, 50 ) { m_Spawner = spawner; + m_Entry = focusentry; + m_Page = page; AddPage( 0 ); - AddBackground( 0, 0, 410, 371, 5054 ); + AddBackground( 0, 0, 343, 371 + ( m_Entry != null ? 44 : 0 ), 5054 ); - AddLabel( 160, 1, 0, "Creatures List" ); + AddHtml( 95, 1, 250, 20, "Creatures List", false, false ); + AddHtml( 245, 1, 250, 20, "#", false, false ); + AddHtml( 282, 1, 250, 20, "Prb", false, false ); - AddButton( 5, 347, 0xFB1, 0xFB3, 0, GumpButtonType.Reply, 0 ); - AddLabel( 38, 347, 0x384, "Cancel" ); + //AddLabel( 95, 1, 0, "Creatures List" ); - AddButton( 5, 325, 0xFB7, 0xFB9, 1, GumpButtonType.Reply, 0 ); - AddLabel( 38, 325, 0x384, "Apply" ); + int offset = 0; - AddButton( 110, 325, 0xFB4, 0xFB6, 2, GumpButtonType.Reply, 0 ); - AddLabel( 143, 325, 0x384, "Bring to Home" ); - - AddButton( 110, 347, 0xFA8, 0xFAA, 3, GumpButtonType.Reply, 0 ); - AddLabel( 143, 347, 0x384, "Total Respawn" ); - - for ( int i = 0; i < 13; i++ ) + for ( int i = 0; i < 13; i++ ) { - AddButton( 5, ( 22 * i ) + 20, 0xFA5, 0xFA7, 4 + (i * 2), GumpButtonType.Reply, 0 ); - AddButton( 38, ( 22 * i ) + 20, 0xFA2, 0xFA4, 5 + (i * 2), GumpButtonType.Reply, 0 ); + int textindex = i * 5; + int entryindex = ( m_Page * 13 ) + i; - AddImageTiled( 71, ( 22 * i ) + 20, 309, 23, 0xA40 ); - AddImageTiled( 72, ( 22 * i ) + 21, 307, 21, 0xBBC ); + SpawnerEntry entry = null; - string str = ""; + if ( entryindex < spawner.Entries.Count ) + entry = m_Spawner.Entries[entryindex]; - if ( i < spawner.SpawnNames.Count ) + if ( entry == null || m_Entry != entry ) + AddButton( 5, ( 22 * i ) + 21 + offset, entry != null ? 0xFBA : 0xFA5, entry != null ? 0xFBC : 0xFA7, GetButtonID( 2, (i * 2) ), GumpButtonType.Reply, 0 ); //Expand + else + AddButton( 5, ( 22 * i ) + 21 + offset, entry != null ? 0xFBB : 0xFA5, entry != null ? 0xFBC : 0xFA7, GetButtonID( 2, (i * 2) ), GumpButtonType.Reply, 0 ); //Unexpand + + AddButton( 38, ( 22 * i ) + 21 + offset, 0xFA2, 0xFA4, GetButtonID( 2, 1 + (i * 2) ), GumpButtonType.Reply, 0 ); //Delete + + AddImageTiled( 71, (22 * i) + 20 + offset, 161, 23, 0xA40 ); //creature text box + AddImageTiled( 72, (22 * i) + 21 + offset, 159, 21, 0xBBC ); //creature text box + + AddImageTiled( 235, (22 * i) + 20 + offset, 35, 23, 0xA40 ); //maxcount text box + AddImageTiled( 236, (22 * i) + 21 + offset, 33, 21, 0xBBC ); //maxcount text box + + AddImageTiled( 273, (22 * i) + 20 + offset, 35, 23, 0xA40 ); //probability text box + AddImageTiled( 274, (22 * i) + 21 + offset, 33, 21, 0xBBC ); //probability text box + + string name = ""; + string probability = ""; + string maxcount = ""; + EntryFlags flags = EntryFlags.None; + + if ( entry != null ) { - str = (string)spawner.SpawnNames[i]; - int count = m_Spawner.CountCreatures( str ); + name = (string)entry.SpawnedName; + probability = entry.SpawnedProbability.ToString(); + maxcount = entry.SpawnedMaxCount.ToString(); + flags = entry.Valid; - AddLabel( 382, ( 22 * i ) + 20, 0, count.ToString() ); + AddLabel( 315, (22 * i) + 20 + offset, 0, spawner.CountSpawns( entry ).ToString() ); } - AddTextEntry( 75, ( 22 * i ) + 21, 304, 21, 0, i, str ); + AddTextEntry( 75, (22 * i) + 21 + offset, 156, 21, ( ( flags & EntryFlags.InvalidType ) != 0 ) ? 33 : 0, textindex, name ); //creature + AddTextEntry( 239, (22 * i) + 21 + offset, 30, 21, 0, textindex + 1, maxcount); //max count + AddTextEntry( 277, (22 * i) + 21 + offset, 30, 21, 0, textindex + 2, probability); //probability + + if ( entry != null && m_Entry == entry ) + { + AddLabel( 5, (22 * i) + 42, 0x384, "Params" ); + AddImageTiled( 55, (22 * i) + 42, 253, 23, 0xA40 ); //Parameters + AddImageTiled( 56, (22 * i) + 43, 251, 21, 0xBBC ); //Parameters + + AddLabel( 5, (22 * i) + 64, 0x384, "Props" ); + AddImageTiled( 55, (22 * i) + 64, 253, 23, 0xA40 ); //Properties + AddImageTiled( 56, (22 * i) + 65, 251, 21, 0xBBC ); //Properties + + AddTextEntry( 59, (22 * i) + 42, 248, 21, ( ( flags & EntryFlags.InvalidParams ) != 0 ) ? 33 : 0, textindex + 3, entry.Parameters ); //parameters + AddTextEntry( 59, (22 * i) + 62, 248, 21, ( ( flags & EntryFlags.InvalidProps ) != 0 ) ? 33 : 0, textindex + 4, entry.Properties ); //properties + + offset += 44; + } } + + AddButton( 5, 347 + offset, 0xFB1, 0xFB3, 0, GumpButtonType.Reply, 0 ); + AddLabel( 38, 347 + offset, 0x384, "Cancel" ); + + AddButton( 5, 325 + offset, 0xFB7, 0xFB9, GetButtonID( 1, 2 ), GumpButtonType.Reply, 0 ); + AddLabel( 38, 325 + offset, 0x384, "Okay" ); + + AddButton( 110, 325 + offset, 0xFB4, 0xFB6, GetButtonID( 1, 3 ), GumpButtonType.Reply, 0 ); + AddLabel( 143, 325 + offset, 0x384, "Bring to Home" ); + + AddButton( 110, 347 + offset, 0xFA8, 0xFAA, GetButtonID( 1, 4 ), GumpButtonType.Reply, 0 ); + AddLabel( 143, 347 + offset, 0x384, "Total Respawn" ); + + AddButton( 253, 325 + offset, 0xFB7, 0xFB9, GetButtonID( 1, 5 ), GumpButtonType.Reply, 0 ); + AddLabel( 286, 325 + offset, 0x384, "Apply" ); + + if ( m_Page > 0 ) + AddButton( 276, 308 + offset, 0x15E3, 0x15E7, GetButtonID( 1, 0 ), GumpButtonType.Reply, 0 ); + else + AddImage( 276, 308 + offset, 0x25EA ); + + if ( (m_Page + 1) * 13 <= m_Spawner.Entries.Count ) + AddButton( 293, 308 + offset, 0x15E1, 0x15E5, GetButtonID( 1, 1 ), GumpButtonType.Reply, 0 ); + else + AddImage( 293, 308 + offset, 0x25E6 ); } - public List CreateArray( RelayInfo info, Mobile from ) + public int GetButtonID( int type, int index ) { - List creaturesName = new List(); + return 1 + (index * 10) + type; + } - for ( int i = 0; i < 13; i++ ) + public void CreateArray( RelayInfo info, Mobile from, Spawner spawner ) + { + int ocount = spawner.Entries.Count; + + List rementries = new List(); + + for ( int i = 0;i < 13; i++ ) { - TextRelay te = info.GetTextEntry( i ); + int index = i * 5; + int entryindex = ( m_Page * 13 ) + i; - if ( te != null ) + TextRelay cte = info.GetTextEntry( index ); + TextRelay mte = info.GetTextEntry( index + 1 ); + TextRelay poste = info.GetTextEntry( index + 2 ); + TextRelay parmte = info.GetTextEntry( index + 3 ); + TextRelay propte = info.GetTextEntry( index + 4 ); + + if ( cte != null ) { - string str = te.Text; + string str = cte.Text.Trim().ToLower(); if ( str.Length > 0 ) { - str = str.Trim(); - - string t = Spawner.ParseType( str ); - - Type type = ScriptCompiler.FindTypeByName( t ); + Type type = SpawnerType.GetType( str ); if ( type != null ) - creaturesName.Add( str ); + { + SpawnerEntry entry = null; + + if ( entryindex < ocount ) + { + entry = spawner.Entries[entryindex]; + entry.SpawnedName = str; + + if ( mte != null ) + entry.SpawnedMaxCount = Utility.ToInt32( mte.Text.Trim() ); + + if ( poste != null ) + entry.SpawnedProbability = Utility.ToInt32( poste.Text.Trim() ); + } + else + { + int maxcount = 1; + int probcount = 100; + + if ( mte != null ) + maxcount = Utility.ToInt32( mte.Text.Trim() ); + + if ( poste != null ) + probcount = Utility.ToInt32( poste.Text.Trim() ); + + entry = spawner.AddEntry( str, probcount, maxcount ); + } + + if ( parmte != null ) + entry.Parameters = parmte.Text.Trim(); + + if ( propte != null ) + entry.Properties = propte.Text.Trim(); + } else - from.SendMessage( "{0} is not a valid type name.", t ); + from.SendMessage( "{0} is not a valid type name for entry #{1}.", str, i ); } + else if ( entryindex < ocount && spawner.Entries[entryindex] != null ) + rementries.Add( spawner.Entries[entryindex] ); } } - return creaturesName; + if ( rementries.Count > 0 ) + for ( int i = 0; i < rementries.Count; i++ ) + spawner.RemoveEntry( rementries[i] ); + + if ( ocount == 0 && spawner.Entries.Count > 0 ) + spawner.Start(); } - + public override void OnResponse( NetState state, RelayInfo info ) { - if ( m_Spawner.Deleted || state.Mobile.AccessLevel < AccessLevel.GameMaster ) + if ( m_Spawner.Deleted ) return; - switch ( info.ButtonID ) + Mobile from = state.Mobile; + + int val = info.ButtonID - 1; + + if ( val < 0 ) + return; + + int type = val % 10; + int index = val / 10; + + switch ( type ) { - case 0: // Closed - { + case 0: //Cancel return; - } - case 1: // Apply + case 1: { - m_Spawner.SpawnNames = CreateArray( info, state.Mobile ); - - break; - } - case 2: // Bring to Home - { - m_Spawner.BringToHome(); - - break; - } - case 3: // Total Respawn - { - m_Spawner.Respawn(); - - break; - } - default: - { - int buttonID = info.ButtonID - 4; - int index = buttonID / 2; - int type = buttonID % 2; - - TextRelay entry = info.GetTextEntry( index ); - - if ( entry != null && entry.Text.Length > 0 ) + switch ( index ) { - if ( type == 0 ) // Spawn creature - m_Spawner.Spawn( entry.Text ); - else // Remove creatures - m_Spawner.RemoveSpawned( entry.Text ); + case 0: + { + if ( m_Spawner.Entries != null && m_Page > 0 ) + { + m_Page--; + m_Entry = null; + } + break; + } + case 1: + { + if ( m_Spawner.Entries != null && (m_Page + 1) * 13 <= m_Spawner.Entries.Count ) + { + m_Page++; + m_Entry = null; + } + break; + } + case 2: //Okay + { + CreateArray(info, state.Mobile, m_Spawner); + return; + } + case 3: + { + m_Spawner.BringToHome(); + break; + } + case 4: // Complete respawn + { + m_Spawner.Respawn(); + break; + } + case 5: + { + CreateArray(info, state.Mobile, m_Spawner); + break; + } + } + break; + } + case 2: + { + int entryindex = ( index / 2 ) + ( m_Page * 13 ); + int buttontype = index % 2; - m_Spawner.SpawnNames = CreateArray( info, state.Mobile ); + if ( entryindex >= 0 && entryindex < m_Spawner.Entries.Count ) + { + SpawnerEntry entry = m_Spawner.Entries[entryindex]; + if ( buttontype == 0 ) // Spawn creature + { + if ( m_Entry != null && m_Entry == entry ) + m_Entry = null; + else + m_Entry = entry; + } + else // Remove creatures + m_Spawner.RemoveSpawn( entryindex ); } + CreateArray( info, state.Mobile, m_Spawner ); break; } } - state.Mobile.SendGump( new SpawnerGump( m_Spawner ) ); + if ( m_Entry != null && m_Spawner.Entries.Contains( m_Entry ) ) + state.Mobile.SendGump( new SpawnerGump( m_Spawner, m_Entry, m_Page ) ); + else + state.Mobile.SendGump( new SpawnerGump( m_Spawner, null, m_Page ) ); } } } diff --git a/Scripts/Engines/Spawner/SpawnerType.cs b/Scripts/Engines/Spawner/SpawnerType.cs index 2b19d6e34..198faf56e 100644 --- a/Scripts/Engines/Spawner/SpawnerType.cs +++ b/Scripts/Engines/Spawner/SpawnerType.cs @@ -11,4 +11,4 @@ namespace Server.Mobiles return ScriptCompiler.FindTypeByName( name ); } } -} \ No newline at end of file +} diff --git a/Scripts/Mobiles/BaseCreature.cs b/Scripts/Mobiles/BaseCreature.cs index ddcf53f71..9a73fe26d 100644 --- a/Scripts/Mobiles/BaseCreature.cs +++ b/Scripts/Mobiles/BaseCreature.cs @@ -190,7 +190,8 @@ namespace Server.Mobiles private double m_dPassiveSpeed; // Timer speed when not active private double m_dCurrentSpeed; // The current speed, lets say it could be changed by something; - private Point3D m_pHome; // The home position of the creature, used by some AI + private Point3D m_pHome; // The home position of the creature, used by some AI + private Map m_HomeMap; // Used by grim reaper and guards that follow across maps! private int m_iRangeHome = 10; // The home range of the creature List m_arSpellAttack; // List of attack spell/power @@ -1740,7 +1741,7 @@ namespace Server.Mobiles { base.Serialize( writer ); - writer.Write( (int) 18 ); // version + writer.Write( (int) 19 ); // version writer.Write( (int)m_CurrentAI ); writer.Write( (int)m_DefaultAI ); @@ -1859,6 +1860,9 @@ namespace Server.Mobiles // Version 18 writer.Write( m_CorpseNameOverride ); + + // Version 19 + writer.Write( m_HomeMap ); } private static double[] m_StandardActiveSpeeds = new double[] @@ -2083,6 +2087,9 @@ namespace Server.Mobiles if ( version >= 18 ) m_CorpseNameOverride = reader.ReadString(); + if ( version >= 19 ) + m_HomeMap = reader.ReadMap(); + if( version <= 14 && m_Paragon && Hue == 0x31 ) { Hue = Paragon.Hue; //Paragon hue fixed, should now be 0x501. @@ -2606,6 +2613,19 @@ namespace Server.Mobiles } } + [CommandProperty(AccessLevel.GameMaster)] + public Map HomeMap + { + get + { + return m_HomeMap; + } + set + { + m_HomeMap = value; + } + } + [CommandProperty( AccessLevel.GameMaster )] public bool Controlled { diff --git a/Scripts/Regions/Spawning/SpawnDefinition.cs b/Scripts/Regions/Spawning/SpawnDefinition.cs index 2820cc875..16314d4b6 100644 --- a/Scripts/Regions/Spawning/SpawnDefinition.cs +++ b/Scripts/Regions/Spawning/SpawnDefinition.cs @@ -112,7 +112,7 @@ namespace Server.Regions public override ISpawnable Spawn( SpawnEntry entry ) { - BaseRegion region = entry.Region; + Region region = entry.Region; Map map = region.Map; Point3D loc = entry.RandomSpawnLocation( this.Height, this.Land, this.Water ); @@ -184,6 +184,7 @@ namespace Server.Regions if ( creature != null ) { creature.Home = entry.HomeLocation; + creature.HomeMap = map; creature.RangeHome = entry.HomeRange; } @@ -406,4 +407,4 @@ namespace Server.Regions return false; } } -} \ No newline at end of file +} diff --git a/Scripts/Regions/Spawning/SpawnEntry.cs b/Scripts/Regions/Spawning/SpawnEntry.cs index 1b88e25bb..78f4ad31a 100644 --- a/Scripts/Regions/Spawning/SpawnEntry.cs +++ b/Scripts/Regions/Spawning/SpawnEntry.cs @@ -46,6 +46,7 @@ namespace Server.Regions public int ID{ get{ return m_ID; } } public BaseRegion Region{ get{ return m_Region; } } + Region ISpawner.Region{ get{ return m_Region; } } public Point3D HomeLocation{ get{ return m_Home; } } public int HomeRange{ get{ return m_Range; } } public Direction Direction{ get{ return m_Direction; } } diff --git a/Scripts/Regions/Spawning/SpawnPersistence.cs b/Scripts/Regions/Spawning/SpawnPersistence.cs index 9946950a2..0edb5c878 100644 --- a/Scripts/Regions/Spawning/SpawnPersistence.cs +++ b/Scripts/Regions/Spawning/SpawnPersistence.cs @@ -4,6 +4,7 @@ using Server; namespace Server.Regions { + [TypeAlias( "Server.Regions.SpawnPersistance" )] public class SpawnPersistence : Item { private static SpawnPersistence m_Instance; @@ -66,4 +67,4 @@ namespace Server.Regions } } } -} \ No newline at end of file +} diff --git a/Server/IEntity.cs b/Server/IEntity.cs index 1ae647683..c244165b2 100644 --- a/Server/IEntity.cs +++ b/Server/IEntity.cs @@ -27,6 +27,7 @@ namespace Server Serial Serial{ get; } Point3D Location{ get; } Map Map{ get; } + void MoveToWorld(Point3D location, Map map); bool Deleted { get; } void Delete(); @@ -105,6 +106,12 @@ namespace Server } } + public virtual void MoveToWorld(Point3D newLocation, Map map) + { + m_Location = newLocation; + m_Map = map; + } + public bool Deleted { get { return m_Deleted; @@ -119,4 +126,4 @@ namespace Server { } } -} \ No newline at end of file +} diff --git a/Server/Interfaces.cs b/Server/Interfaces.cs index c475b15b9..1c233b3ee 100644 --- a/Server/Interfaces.cs +++ b/Server/Interfaces.cs @@ -101,6 +101,7 @@ namespace Server bool UnlinkOnTaming { get; } Point3D HomeLocation { get; } int HomeRange { get; } + Region Region { get; } void Remove(ISpawnable spawn); } @@ -108,9 +109,8 @@ namespace Server public interface ISpawnable : IEntity { void OnBeforeSpawn(Point3D location, Map map); - void MoveToWorld(Point3D location, Map map); void OnAfterSpawn(); ISpawner Spawner { get; set; } } -} \ No newline at end of file +} diff --git a/Server/Serialization.cs b/Server/Serialization.cs index fb15bcf6d..f290dfea5 100644 --- a/Server/Serialization.cs +++ b/Server/Serialization.cs @@ -63,6 +63,7 @@ namespace Server public abstract Rectangle3D ReadRect3D(); public abstract Map ReadMap(); + public abstract IEntity ReadEntity(); public abstract Item ReadItem(); public abstract Mobile ReadMobile(); public abstract BaseGuild ReadGuild(); @@ -134,6 +135,7 @@ namespace Server public abstract void Write( Rectangle3D value ); public abstract void Write( Map value ); + public abstract void WriteEntity( IEntity value ); public abstract void Write( Item value ); public abstract void Write( Mobile value ); public abstract void Write( BaseGuild value ); @@ -189,7 +191,7 @@ namespace Server public abstract void WriteGuildSet( HashSet set ) where T : BaseGuild; public abstract void WriteGuildSet( HashSet set, bool tidy ) where T : BaseGuild; - // Compiler won't notice their 'where' to differentiate the generic methods. + // Compiler won't notice there 'where' to differentiate the generic methods. } public class BinaryFileWriter : GenericWriter @@ -591,6 +593,14 @@ namespace Server Write( (byte)0xFF ); } + public override void WriteEntity( IEntity value ) + { + if ( value == null || value.Deleted ) + Write( Serial.MinusOne ); + else + Write( value.Serial ); + } + public override void Write( Item value ) { if( value == null || value.Deleted ) @@ -1126,6 +1136,16 @@ namespace Server return Map.Maps[ReadByte()]; } + public override IEntity ReadEntity() + { + Serial serial = ReadInt(); + IEntity entity = World.FindEntity( serial ); + if ( entity == null ) + return new Entity( serial, new Point3D( 0, 0, 0 ), Map.Internal ); + else + return entity; + } + public override Item ReadItem() { return World.FindItem( ReadInt() ); @@ -1725,6 +1745,14 @@ namespace Server Write( (byte)0xFF ); } + public override void WriteEntity( IEntity value ) + { + if ( value == null || value.Deleted ) + Write( Serial.MinusOne ); + else + Write( value.Serial ); + } + public override void Write( Item value ) { if( value == null || value.Deleted )