ModernUO/Scripts/Engines/Spawner/Spawner.cs
2018-09-14 13:22:16 -07:00

1234 lines
28 KiB
C#

using System;
using System.IO;
using System.Reflection;
using System.Collections.Generic;
using Server.Items;
using Server.Commands;
using CPA = Server.CommandPropertyAttribute;
namespace Server.Mobiles
{
public class Spawner : Item, ISpawner
{
private int m_Team;
private int m_HomeRange;
private int m_Count;
private int m_WalkingRange = -1;
private TimeSpan m_MinDelay;
private TimeSpan m_MaxDelay;
private List<SpawnerEntry> m_Entries;
private Dictionary<ISpawnable, SpawnerEntry> m_Spawned;
private DateTime m_End;
private InternalTimer m_Timer;
private bool m_Running;
private bool m_Group;
private WayPoint m_WayPoint;
public override string DefaultName => "Spawner";
public bool IsFull => ( m_Spawned != null && m_Spawned.Count >= m_Count );
public bool IsEmpty => ( m_Spawned != null && m_Spawned.Count == 0 );
public Point3D HomeLocation => Location;
public bool UnlinkOnTaming => true;
public DateTime End{ get => m_End;
set => m_End = value;
}
public List<SpawnerEntry> Entries => m_Entries;
public Dictionary<ISpawnable, SpawnerEntry> Spawned => m_Spawned;
public override void OnAfterDuped( Item newItem )
{
if ( newItem is Spawner newSpawner )
{
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 => m_Count;
set
{
m_Count = value;
if ( m_Timer != null )
{
if ( ( !IsFull && !m_Timer.Running ) || IsFull && m_Timer.Running )
DoTimer();
}
InvalidateProperties();
}
}
[CommandProperty( AccessLevel.Developer )]
public WayPoint WayPoint{ get => m_WayPoint;
set => m_WayPoint = value;
}
[CommandProperty( AccessLevel.Developer )]
public bool Running
{
get => m_Running;
set
{
if ( value )
Start();
else
Stop();
InvalidateProperties();
}
}
[CommandProperty( AccessLevel.Developer )]
public int HomeRange{ get => m_HomeRange;
set { m_HomeRange = value; InvalidateProperties(); } }
[CommandProperty( AccessLevel.Developer )]
public int WalkingRange{ get => m_WalkingRange;
set { m_WalkingRange = value; InvalidateProperties(); } }
[CommandProperty( AccessLevel.Developer )]
public int Team{ get => m_Team;
set { m_Team = value; InvalidateProperties(); } }
[CommandProperty( AccessLevel.Developer )]
public TimeSpan MinDelay{ get => m_MinDelay;
set { m_MinDelay = value; InvalidateProperties(); } }
[CommandProperty( AccessLevel.Developer )]
public TimeSpan MaxDelay{ get => m_MaxDelay;
set { m_MaxDelay = value; InvalidateProperties(); } }
[CommandProperty( AccessLevel.Developer )]
public TimeSpan NextSpawn
{
get
{
if ( m_Running && m_Timer != null && m_Timer.Running )
return m_End - DateTime.UtcNow;
else
return TimeSpan.FromSeconds( 0 );
}
set
{
Start();
DoTimer( value );
}
}
[CommandProperty( AccessLevel.Developer )]
public bool Group
{
get => m_Group;
set { m_Group = value; InvalidateProperties(); }
}
Region ISpawner.Region => 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 spawnedName ) : base( 0x1f13 )
{
InitSpawn( 1, TimeSpan.FromMinutes( 5 ), TimeSpan.FromMinutes( 10 ), 0, 4 );
AddEntry( spawnedName, 100, 1, false );
}
[Constructible( AccessLevel.Developer )]
public Spawner() : base( 0x1f13 )
{
InitSpawn( 1, TimeSpan.FromMinutes( 5 ), TimeSpan.FromMinutes( 10 ), 0, 4 );
}
public Spawner( int amount, TimeSpan minDelay, TimeSpan maxDelay, int team, int homeRange, params string[] spawnedNames ) : base( 0x1f13 )
{
InitSpawn( amount, minDelay, maxDelay, team, homeRange );
for ( int i = 0;i < spawnedNames.Length; i++ )
AddEntry( spawnedNames[i], 100, amount, false );
}
public SpawnerEntry AddEntry( string creaturename, int probability, int amount )
{
return AddEntry( creaturename, probability, amount, true );
}
public SpawnerEntry AddEntry( string creaturename, int probability, int amount, bool dotimer )
{
SpawnerEntry entry = new SpawnerEntry( creaturename, probability, amount );
m_Entries.Add( entry );
if ( dotimer )
DoTimer( TimeSpan.FromSeconds( 1 ) );
return entry;
}
public void InitSpawn( int amount, TimeSpan minDelay, TimeSpan maxDelay, int team, int homeRange )
{
Visible = false;
Movable = false;
m_Running = true;
m_Group = false;
m_MinDelay = minDelay;
m_MaxDelay = maxDelay;
m_Count = amount;
m_Team = team;
m_HomeRange = homeRange;
m_Entries = new List<SpawnerEntry>();
m_Spawned = new Dictionary<ISpawnable, SpawnerEntry>();
DoTimer( TimeSpan.FromSeconds( 1 ) );
}
public Spawner( Serial serial ) : base( serial )
{
}
public override void OnDoubleClick( Mobile from )
{
if ( from.AccessLevel >= AccessLevel.Developer )
from.SendGump( new SpawnerGump( this ) );
}
public override void GetProperties( ObjectPropertyList list )
{
base.GetProperties( list );
if ( m_Running )
{
list.Add( 1060742 ); // active
list.Add( 1060656, m_Count.ToString() ); // amount to make: ~1_val~
list.Add( 1061169, m_HomeRange.ToString() ); // range ~1_val~
list.Add( 1060658, "walking range\t{0}", m_WalkingRange ); // ~1_val~: ~2_val~
list.Add( 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~
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 )
{
base.OnSingleClick( from );
if ( m_Running )
LabelTo( from, "[Running]" );
else
LabelTo( from, "[Off]" );
}
public void Start()
{
if ( !m_Running )
{
if ( m_Entries.Count > 0 )
{
m_Running = true;
DoTimer();
}
}
}
public void Stop()
{
if ( m_Running )
{
if ( m_Timer != null )
m_Timer.Stop();
m_Running = false;
}
}
public void Defrag()
{
if ( m_Entries == null )
m_Entries = new List<SpawnerEntry>();
for ( int i = 0; i < m_Entries.Count; ++i )
m_Entries[i].Defrag( this );
}
public void OnTick()
{
// DoTimer( m_Spawned.Count >= m_Count );
if ( m_Group )
{
Defrag();
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()
{
RemoveSpawns();
for ( int i = 0; i < m_Count; i++ )
Spawn();
DoTimer(); //Turn off the timer!
}
public virtual void Spawn()
{
Defrag();
if ( m_Entries.Count > 0 && !IsFull )
{
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 )
{
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;
}
}
}
}
}
private static string[,] FormatProperties( string[] args )
{
string[,] props = null;
int remains = args.Length;
if ( remains >= 2 )
{
props = new string[remains / 2, 2];
remains /= 2;
for ( int j = 0; j < remains; ++j )
{
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 )
{
realProps = new PropertyInfo[props.GetLength( 0 )];
PropertyInfo[] allProps = type.GetProperties( BindingFlags.Static | BindingFlags.Instance | BindingFlags.Public );
for ( int i = 0; i < realProps.Length; ++i )
{
PropertyInfo thisProp = null;
string propName = props[i, 0];
for ( int j = 0; thisProp == null && j < allProps.Length; ++j )
{
if ( Insensitive.Equals( propName, allProps[j].Name ) )
thisProp = allProps[j];
}
if ( thisProp == null )
return null;
else
{
CPA attr = Properties.GetCPA( thisProp );
if ( attr == null || AccessLevel.Developer < attr.WriteLevel || !thisProp.CanWrite || attr.ReadOnly )
return null;
else
realProps[i] = thisProp;
}
}
}
return realProps;
}
public bool Spawn( int index, out EntryFlags flags )
{
if ( index >= 0 && index < m_Entries.Count )
return Spawn( m_Entries[index], out flags );
else
{
flags = EntryFlags.InvalidEntry;
return false;
}
}
public bool Spawn( SpawnerEntry entry, out EntryFlags flags )
{
Map map = GetSpawnMap();
flags = EntryFlags.None;
if ( map == null || map == Map.Internal || Parent != null )
return false;
//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 o = null;
string[] paramargs;
string[] propargs;
if ( string.IsNullOrEmpty( entry.Properties ) )
propargs = new string[0];
else
propargs = CommandSystem.Split( entry.Properties.Trim() );
string[,] props = FormatProperties( propargs );
PropertyInfo[] realProps = GetTypeProperties( type, props );
if ( realProps == null )
{
flags = EntryFlags.InvalidProps;
return false;
}
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 spawnable )
spawnable.Delete();
return false;
}
}
}
break;
}
}
}
}
if ( o is Mobile m )
{
m_Spawned.Add( m, entry );
entry.Spawned.Add( m );
Point3D loc = ( m is BaseVendor ? Location : GetSpawnPosition( m, map ) );
m.OnBeforeSpawn( loc, map );
InvalidateProperties();
m.MoveToWorld( loc, map );
if ( m is BaseCreature c )
{
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 = Location;
c.HomeMap = Map;
}
m.Spawner = this;
m.OnAfterSpawn();
}
else if ( o is Item item )
{
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;
}
flags = EntryFlags.InvalidType;
return false;
}
public virtual int GetWalkingRange()
{
return m_WalkingRange;
}
public virtual WayPoint GetWayPoint()
{
return m_WayPoint;
}
public virtual Point3D GetSpawnPosition( ISpawnable spawned, Map map )
{
if ( map == null || map == Map.Internal )
return Location;
bool waterMob, waterOnlyMob;
if ( spawned is Mobile mob )
{
waterMob = mob.CanSwim;
waterOnlyMob = ( mob.CanSwim && mob.CantWalk );
}
else
{
waterMob = false;
waterOnlyMob = false;
}
// Try 10 times to find a Spawnable location.
for ( int i = 0; i < 10; i++ )
{
int x = Location.X + (Utility.Random( (m_HomeRange * 2) + 1 ) - m_HomeRange);
int y = Location.Y + (Utility.Random( (m_HomeRange * 2) + 1 ) - m_HomeRange);
int z = Map.GetAverageZ( x, y );
int mapZ = map.GetAverageZ( x, y );
if ( waterMob )
{
if ( IsValidWater( map, x, y, Z ) )
return new Point3D( x, y, Z );
else if ( IsValidWater( map, x, y, mapZ ) )
return new Point3D( x, y, mapZ );
}
if ( !waterOnlyMob )
{
if ( map.CanSpawnMobile( x, y, Z ) )
return new Point3D( x, y, Z );
else if ( map.CanSpawnMobile( x, y, mapZ ) )
return new Point3D( x, y, mapZ );
}
}
return Location;
}
public static bool IsValidWater( Map map, int x, int y, int z )
{
if ( !Region.Find( new Point3D( x, y, z ), map ).AllowSpawn() || !map.CanFit( x, y, z, 16, false, true, false ) )
return false;
LandTile landTile = map.Tiles.GetLandTile( x, y );
if ( landTile.Z == z && ( TileData.LandTable[landTile.ID & TileData.MaxLandValue].Flags & TileFlag.Wet ) != 0 )
return true;
StaticTile[] staticTiles = map.Tiles.GetStaticTiles( x, y, true );
for ( int i = 0; i < staticTiles.Length; ++i )
{
StaticTile staticTile = staticTiles[i];
if ( staticTile.Z == z && ( TileData.ItemTable[staticTile.ID & TileData.MaxItemValue].Flags & TileFlag.Wet ) != 0 )
return true;
}
return false;
}
public virtual Map GetSpawnMap()
{
return Map;
}
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 virtual void DoTimer( TimeSpan delay )
{
if ( !m_Running )
return;
m_End = DateTime.UtcNow + delay;
m_Timer?.Stop();
m_Timer = new InternalTimer( this, delay );
if ( !IsFull )
m_Timer.Start();
}
private class InternalTimer : Timer
{
private Spawner m_Spawner;
public InternalTimer( Spawner spawner, TimeSpan delay ) : base( delay )
{
if ( spawner.IsFull )
Priority = TimerPriority.FiveSeconds;
else
Priority = TimerPriority.OneSecond;
m_Spawner = spawner;
}
protected override void OnTick()
{
if ( m_Spawner != null )
if ( !m_Spawner.Deleted )
m_Spawner.OnTick();
}
}
public int CountSpawns( SpawnerEntry entry )
{
Defrag();
return entry.Spawned.Count;
}
public void RemoveEntry( SpawnerEntry entry )
{
Defrag();
for ( int i = entry.Spawned.Count-1; i >= 0; i-- )
{
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 Remove( ISpawnable spawn )
{
Defrag();
if ( spawn != null )
{
m_Spawned.TryGetValue( spawn, out SpawnerEntry 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();
}
public void BringToHome()
{
Defrag();
foreach( ISpawnable e in m_Spawned.Keys )
if ( e != null )
e.MoveToWorld( Location, Map );
}
public override void OnDelete()
{
base.OnDelete();
Stop();
RemoveSpawns();
}
public override void Serialize( GenericWriter writer )
{
base.Serialize( writer );
writer.Write( (int) 7 ); // version
writer.Write( m_Entries.Count );
for ( int i = 0; i < m_Entries.Count; ++i )
m_Entries[i].Serialize( writer );
writer.Write( m_WalkingRange );
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.WriteDeltaTime( m_End );
}
private static WarnTimer m_WarnTimer;
public override void Deserialize( GenericReader reader )
{
base.Deserialize( reader );
int version = reader.ReadInt();
m_Spawned = new Dictionary<ISpawnable, SpawnerEntry>();
if ( version < 7 )
m_Entries = new List<SpawnerEntry>();
switch ( version )
{
case 7:
{
int size = reader.ReadInt();
m_Entries = new List<SpawnerEntry>( size );
for ( int i = 0; i < size; ++i )
m_Entries.Add( new SpawnerEntry( this, reader ) );
goto case 4; //Skip the other crap
}
case 6:
{
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:
{
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;
}
case 4:
{
m_WalkingRange = reader.ReadInt();
goto case 3;
}
case 3:
case 2:
{
m_WayPoint = reader.ReadItem() as WayPoint;
goto case 1;
}
case 1:
{
m_Group = reader.ReadBool();
goto case 0;
}
case 0:
{
m_MinDelay = reader.ReadTimeSpan();
m_MaxDelay = reader.ReadTimeSpan();
m_Count = reader.ReadInt();
m_Team = reader.ReadInt();
m_HomeRange = reader.ReadInt();
m_Running = reader.ReadBool();
TimeSpan ts = TimeSpan.Zero;
if ( m_Running )
ts = reader.ReadDeltaTime() - DateTime.UtcNow;
if ( version < 7 )
{
int size = reader.ReadInt();
bool addentries = m_Entries.Count == 0;
for ( int i = 0; i < size; ++i )
{
string typeName = reader.ReadString();
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 )
{
if ( reader.ReadEntity() is ISpawnable e )
{
if ( e is BaseCreature creature )
creature.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;
}
}
}
}
}
DoTimer( ts );
break;
}
}
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
{
private List<WarnEntry> m_List;
private class WarnEntry
{
public Point3D m_Point;
public Map m_Map;
public string m_Name;
public WarnEntry( Point3D p, Map map, string name )
{
m_Point = p;
m_Map = map;
m_Name = name;
}
}
public WarnTimer() : base( TimeSpan.FromSeconds( 1.0 ) )
{
m_List = new List<WarnEntry>();
Start();
}
public void Add( Point3D p, Map map, string name )
{
m_List.Add( new WarnEntry( p, map, name ) );
}
protected override void OnTick()
{
try
{
Console.WriteLine( "Warning: {0} bad spawns detected, logged: 'badspawn.log'", m_List.Count );
using ( StreamWriter op = new StreamWriter( "badspawn.log", true ) )
{
op.WriteLine( "# Bad spawns : {0}", DateTime.Now );
op.WriteLine( "# Format: X Y Z F Name" );
op.WriteLine();
foreach ( WarnEntry 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
{
}
}
}
}
[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<ISpawnable> m_Spawned;
private int m_SpawnedMaxCount;
private string m_Properties;
private string m_Parameters;
private EntryFlags m_Valid;
public int SpawnedProbability
{
get => m_SpawnedProbability;
set => m_SpawnedProbability = value;
}
public int SpawnedMaxCount
{
get => m_SpawnedMaxCount;
set => m_SpawnedMaxCount = value;
}
public string SpawnedName
{
get => m_SpawnedName;
set => m_SpawnedName = value;
}
public string Properties
{
get => m_Properties;
set => m_Properties = value;
}
public string Parameters
{
get => m_Parameters;
set => m_Parameters = value;
}
public EntryFlags Valid{ get => m_Valid;
set => m_Valid = value;
}
public List<ISpawnable> Spawned => m_Spawned;
public bool IsFull => 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<ISpawnable>();
}
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 item )
writer.Write( item );
else if ( o is Mobile mobile )
writer.Write( mobile );
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<ISpawnable>( count );
for ( int i = 0; i < count; ++i )
{
//IEntity e = World.FindEntity( reader.ReadInt() );
if ( reader.ReadEntity() is ISpawnable e )
{
e.Spawner = parent;
if ( e is BaseCreature creature )
creature.RemoveIfUntamed = true;
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 )
{
if ( item.Deleted || item.RootParent is Mobile || item.IsLockedDown || item.IsSecure || item.Spawner == null )
remove = true;
}
else if ( e is Mobile m )
{
if ( m.Deleted )
remove = true;
else if ( m is BaseCreature c )
{
if ( c.Controlled || c.IsStabled )
remove = true;
/*
else if ( c.Combatant == null && ( c.GetDistanceToSqrt( Location ) > (c.RangeHome * 4) ) )
{
//m_Spawned[i].Delete();
m_Spawned.RemoveAt( i );
--i;
c.Delete();
remove = true;
}
*/
}
else if ( m.Spawner == null )
remove = true;
}
else
remove = true;
if ( remove )
{
m_Spawned.RemoveAt( i-- );
if ( parent.Spawned.ContainsKey( e ) )
parent.Spawned.Remove( e );
}
}
}
}
}