mirror of
https://github.com/runuo/runuo
synced 2026-08-11 22:26:04 -04:00
Convert movement, actions (lift/use), combat, and spells to use Core.TickCount and avoid DateTime caveats (performance, system time dependency, etc) Refactor DateTime.Now to DateTime.UtcNow Add LOS check for Iron Maiden addon
465 lines
No EOL
12 KiB
C#
465 lines
No EOL
12 KiB
C#
using System;
|
|
using System.Collections;
|
|
using System.Collections.Generic;
|
|
using Server;
|
|
using Server.Mobiles;
|
|
using Server.Commands;
|
|
|
|
namespace Server.Regions
|
|
{
|
|
public class SpawnEntry : ISpawner
|
|
{
|
|
public static readonly TimeSpan DefaultMinSpawnTime = TimeSpan.FromMinutes( 2.0 );
|
|
public static readonly TimeSpan DefaultMaxSpawnTime = TimeSpan.FromMinutes( 5.0 );
|
|
|
|
private static Hashtable m_Table = new Hashtable();
|
|
|
|
public static Hashtable Table{ get{ return m_Table; } }
|
|
|
|
|
|
// When a creature's AI is deactivated (PlayerRangeSensitive optimization) does it return home?
|
|
public bool ReturnOnDeactivate{ get{ return true; } }
|
|
|
|
// Are creatures unlinked on taming (true) or should they also go out of the region (false)?
|
|
public bool UnlinkOnTaming{ get{ return false; } }
|
|
|
|
// Are unlinked and untamed creatures removed after 20 hours?
|
|
public bool RemoveIfUntamed{ get{ return true; } }
|
|
|
|
|
|
public static readonly Direction InvalidDirection = Direction.Running;
|
|
|
|
private int m_ID;
|
|
private BaseRegion m_Region;
|
|
private Point3D m_Home;
|
|
private int m_Range;
|
|
private Direction m_Direction;
|
|
private SpawnDefinition m_Definition;
|
|
private List<ISpawnable> m_SpawnedObjects;
|
|
private int m_Max;
|
|
private TimeSpan m_MinSpawnTime;
|
|
private TimeSpan m_MaxSpawnTime;
|
|
private bool m_Running;
|
|
|
|
private DateTime m_NextSpawn;
|
|
private Timer m_SpawnTimer;
|
|
|
|
public int ID{ get{ return m_ID; } }
|
|
public BaseRegion 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; } }
|
|
public SpawnDefinition Definition{ get{ return m_Definition; } }
|
|
public List<ISpawnable> SpawnedObjects{ get{ return m_SpawnedObjects; } }
|
|
public int Max{ get{ return m_Max; } }
|
|
public TimeSpan MinSpawnTime{ get{ return m_MinSpawnTime; } }
|
|
public TimeSpan MaxSpawnTime{ get{ return m_MaxSpawnTime; } }
|
|
public bool Running{ get{ return m_Running; } }
|
|
|
|
public bool Complete{ get{ return m_SpawnedObjects.Count >= m_Max; } }
|
|
public bool Spawning{ get{ return m_Running && !this.Complete; } }
|
|
|
|
public SpawnEntry( int id, BaseRegion region, Point3D home, int range, Direction direction, SpawnDefinition definition, int max, TimeSpan minSpawnTime, TimeSpan maxSpawnTime )
|
|
{
|
|
m_ID = id;
|
|
m_Region = region;
|
|
m_Home = home;
|
|
m_Range = range;
|
|
m_Direction = direction;
|
|
m_Definition = definition;
|
|
m_SpawnedObjects = new List<ISpawnable>();
|
|
m_Max = max;
|
|
m_MinSpawnTime = minSpawnTime;
|
|
m_MaxSpawnTime = maxSpawnTime;
|
|
m_Running = false;
|
|
|
|
if ( m_Table.Contains( id ) )
|
|
Console.WriteLine( "Warning: double SpawnEntry ID '{0}'", id );
|
|
else
|
|
m_Table[id] = this;
|
|
}
|
|
|
|
public Point3D RandomSpawnLocation( int spawnHeight, bool land, bool water )
|
|
{
|
|
return m_Region.RandomSpawnLocation( spawnHeight, land, water, m_Home, m_Range );
|
|
}
|
|
|
|
public void Start()
|
|
{
|
|
if ( m_Running )
|
|
return;
|
|
|
|
m_Running = true;
|
|
CheckTimer();
|
|
}
|
|
|
|
public void Stop()
|
|
{
|
|
if ( !m_Running )
|
|
return;
|
|
|
|
m_Running = false;
|
|
CheckTimer();
|
|
}
|
|
|
|
private void Spawn()
|
|
{
|
|
ISpawnable spawn = m_Definition.Spawn(this);
|
|
|
|
if ( spawn != null )
|
|
Add( spawn );
|
|
}
|
|
|
|
private void Add( ISpawnable spawn )
|
|
{
|
|
m_SpawnedObjects.Add( spawn );
|
|
|
|
spawn.Spawner = this;
|
|
|
|
if ( spawn is BaseCreature )
|
|
((BaseCreature)spawn).RemoveIfUntamed = this.RemoveIfUntamed;
|
|
}
|
|
|
|
void ISpawner.Remove( ISpawnable spawn )
|
|
{
|
|
m_SpawnedObjects.Remove( spawn );
|
|
|
|
CheckTimer();
|
|
}
|
|
|
|
private TimeSpan RandomTime()
|
|
{
|
|
int min = (int) m_MinSpawnTime.TotalSeconds;
|
|
int max = (int) m_MaxSpawnTime.TotalSeconds;
|
|
|
|
int rand = Utility.RandomMinMax( min, max );
|
|
return TimeSpan.FromSeconds( rand );
|
|
}
|
|
|
|
private void CheckTimer()
|
|
{
|
|
if ( this.Spawning )
|
|
{
|
|
if ( m_SpawnTimer == null )
|
|
{
|
|
TimeSpan time = RandomTime();
|
|
m_SpawnTimer = Timer.DelayCall( time, new TimerCallback( TimerCallback ) );
|
|
m_NextSpawn = DateTime.UtcNow + time;
|
|
}
|
|
}
|
|
else if ( m_SpawnTimer != null )
|
|
{
|
|
m_SpawnTimer.Stop();
|
|
m_SpawnTimer = null;
|
|
}
|
|
}
|
|
|
|
private void TimerCallback()
|
|
{
|
|
int amount = Math.Max( (m_Max - m_SpawnedObjects.Count) / 3, 1 );
|
|
|
|
for ( int i = 0; i < amount; i++ )
|
|
Spawn();
|
|
|
|
m_SpawnTimer = null;
|
|
CheckTimer();
|
|
}
|
|
|
|
public void DeleteSpawnedObjects()
|
|
{
|
|
InternalDeleteSpawnedObjects();
|
|
|
|
m_Running = false;
|
|
CheckTimer();
|
|
}
|
|
|
|
private void InternalDeleteSpawnedObjects()
|
|
{
|
|
foreach ( ISpawnable spawnable in m_SpawnedObjects )
|
|
{
|
|
spawnable.Spawner = null;
|
|
|
|
bool uncontrolled = !(spawnable is BaseCreature) || !((BaseCreature)spawnable).Controlled;
|
|
|
|
if( uncontrolled )
|
|
spawnable.Delete();
|
|
}
|
|
|
|
m_SpawnedObjects.Clear();
|
|
}
|
|
|
|
public void Respawn()
|
|
{
|
|
InternalDeleteSpawnedObjects();
|
|
|
|
for ( int i = 0; !this.Complete && i < m_Max; i++ )
|
|
Spawn();
|
|
|
|
m_Running = true;
|
|
CheckTimer();
|
|
}
|
|
|
|
public void Delete()
|
|
{
|
|
m_Max = 0;
|
|
InternalDeleteSpawnedObjects();
|
|
|
|
if ( m_SpawnTimer != null )
|
|
{
|
|
m_SpawnTimer.Stop();
|
|
m_SpawnTimer = null;
|
|
}
|
|
|
|
if ( m_Table[m_ID] == this )
|
|
m_Table.Remove( m_ID );
|
|
}
|
|
|
|
public void Serialize( GenericWriter writer )
|
|
{
|
|
writer.Write( (int) m_SpawnedObjects.Count );
|
|
|
|
for ( int i = 0; i < m_SpawnedObjects.Count; i++ )
|
|
{
|
|
ISpawnable spawn = m_SpawnedObjects[i];
|
|
|
|
int serial = spawn.Serial;
|
|
|
|
writer.Write( (int) serial );
|
|
}
|
|
|
|
writer.Write( (bool) m_Running );
|
|
|
|
if ( m_SpawnTimer != null )
|
|
{
|
|
writer.Write( true );
|
|
writer.WriteDeltaTime( (DateTime) m_NextSpawn );
|
|
}
|
|
else
|
|
{
|
|
writer.Write( false );
|
|
}
|
|
}
|
|
|
|
public void Deserialize( GenericReader reader, int version )
|
|
{
|
|
int count = reader.ReadInt();
|
|
|
|
for ( int i = 0; i < count; i++ )
|
|
{
|
|
int serial = reader.ReadInt();
|
|
ISpawnable spawnableEntity = World.FindEntity( serial ) as ISpawnable;
|
|
|
|
if (spawnableEntity != null)
|
|
Add(spawnableEntity);
|
|
}
|
|
|
|
m_Running = reader.ReadBool();
|
|
|
|
if ( reader.ReadBool() )
|
|
{
|
|
m_NextSpawn = reader.ReadDeltaTime();
|
|
|
|
if ( this.Spawning )
|
|
{
|
|
if ( m_SpawnTimer != null )
|
|
m_SpawnTimer.Stop();
|
|
|
|
TimeSpan delay = m_NextSpawn - DateTime.UtcNow;
|
|
m_SpawnTimer = Timer.DelayCall( delay > TimeSpan.Zero ? delay : TimeSpan.Zero, new TimerCallback( TimerCallback ) );
|
|
}
|
|
}
|
|
|
|
CheckTimer();
|
|
}
|
|
|
|
private static List<IEntity> m_RemoveList;
|
|
|
|
public static void Remove( GenericReader reader, int version )
|
|
{
|
|
int count = reader.ReadInt();
|
|
|
|
for ( int i = 0; i < count; i++ )
|
|
{
|
|
int serial = reader.ReadInt();
|
|
IEntity entity = World.FindEntity( serial );
|
|
|
|
if ( entity != null )
|
|
{
|
|
if ( m_RemoveList == null )
|
|
m_RemoveList = new List<IEntity>();
|
|
|
|
m_RemoveList.Add( entity );
|
|
}
|
|
}
|
|
|
|
reader.ReadBool(); // m_Running
|
|
|
|
if ( reader.ReadBool() )
|
|
reader.ReadDeltaTime(); // m_NextSpawn
|
|
}
|
|
|
|
public static void Initialize()
|
|
{
|
|
if ( m_RemoveList != null )
|
|
{
|
|
foreach ( IEntity ent in m_RemoveList )
|
|
{
|
|
ent.Delete();
|
|
}
|
|
|
|
m_RemoveList = null;
|
|
}
|
|
|
|
SpawnPersistence.EnsureExistence();
|
|
|
|
CommandSystem.Register( "RespawnAllRegions", AccessLevel.Administrator, new CommandEventHandler( RespawnAllRegions_OnCommand ) );
|
|
CommandSystem.Register( "RespawnRegion", AccessLevel.GameMaster, new CommandEventHandler( RespawnRegion_OnCommand ) );
|
|
CommandSystem.Register( "DelAllRegionSpawns", AccessLevel.Administrator, new CommandEventHandler( DelAllRegionSpawns_OnCommand ) );
|
|
CommandSystem.Register( "DelRegionSpawns", AccessLevel.GameMaster, new CommandEventHandler( DelRegionSpawns_OnCommand ) );
|
|
CommandSystem.Register( "StartAllRegionSpawns", AccessLevel.Administrator, new CommandEventHandler( StartAllRegionSpawns_OnCommand ) );
|
|
CommandSystem.Register( "StartRegionSpawns", AccessLevel.GameMaster, new CommandEventHandler( StartRegionSpawns_OnCommand ) );
|
|
CommandSystem.Register( "StopAllRegionSpawns", AccessLevel.Administrator, new CommandEventHandler( StopAllRegionSpawns_OnCommand ) );
|
|
CommandSystem.Register( "StopRegionSpawns", AccessLevel.GameMaster, new CommandEventHandler( StopRegionSpawns_OnCommand ) );
|
|
}
|
|
|
|
private static BaseRegion GetCommandData( CommandEventArgs args )
|
|
{
|
|
Mobile from = args.Mobile;
|
|
|
|
Region reg;
|
|
if ( args.Length == 0 )
|
|
{
|
|
reg = from.Region;
|
|
}
|
|
else
|
|
{
|
|
string name = args.GetString( 0 );
|
|
//reg = (Region) from.Map.Regions[name];
|
|
|
|
if ( !from.Map.Regions.TryGetValue( name, out reg ) )
|
|
{
|
|
from.SendMessage( "Could not find region '{0}'.", name );
|
|
return null;
|
|
}
|
|
}
|
|
|
|
BaseRegion br = reg as BaseRegion;
|
|
|
|
if ( br == null || br.Spawns == null )
|
|
{
|
|
from.SendMessage( "There are no spawners in region '{0}'.", reg );
|
|
return null;
|
|
}
|
|
|
|
return br;
|
|
}
|
|
|
|
[Usage( "RespawnAllRegions" )]
|
|
[Description( "Respawns all regions and sets the spawners as running." )]
|
|
private static void RespawnAllRegions_OnCommand( CommandEventArgs args )
|
|
{
|
|
foreach ( SpawnEntry entry in m_Table.Values )
|
|
{
|
|
entry.Respawn();
|
|
}
|
|
|
|
args.Mobile.SendMessage( "All regions have respawned." );
|
|
}
|
|
|
|
[Usage( "RespawnRegion [<region name>]" )]
|
|
[Description( "Respawns the region in which you are (or that you provided) and sets the spawners as running." )]
|
|
private static void RespawnRegion_OnCommand( CommandEventArgs args )
|
|
{
|
|
BaseRegion region = GetCommandData( args );
|
|
|
|
if ( region == null )
|
|
return;
|
|
|
|
for ( int i = 0; i < region.Spawns.Length; i++ )
|
|
region.Spawns[i].Respawn();
|
|
|
|
args.Mobile.SendMessage( "Region '{0}' has respawned.", region );
|
|
}
|
|
|
|
[Usage( "DelAllRegionSpawns" )]
|
|
[Description( "Deletes all spawned objects of every regions and sets the spawners as not running." )]
|
|
private static void DelAllRegionSpawns_OnCommand( CommandEventArgs args )
|
|
{
|
|
foreach ( SpawnEntry entry in m_Table.Values )
|
|
{
|
|
entry.DeleteSpawnedObjects();
|
|
}
|
|
|
|
args.Mobile.SendMessage( "All region spawned objects have been deleted." );
|
|
}
|
|
|
|
[Usage( "DelRegionSpawns [<region name>]" )]
|
|
[Description( "Deletes all spawned objects of the region in which you are (or that you provided) and sets the spawners as not running." )]
|
|
private static void DelRegionSpawns_OnCommand( CommandEventArgs args )
|
|
{
|
|
BaseRegion region = GetCommandData( args );
|
|
|
|
if ( region == null )
|
|
return;
|
|
|
|
for ( int i = 0; i < region.Spawns.Length; i++ )
|
|
region.Spawns[i].DeleteSpawnedObjects();
|
|
|
|
args.Mobile.SendMessage( "Spawned objects of region '{0}' have been deleted.", region );
|
|
}
|
|
|
|
[Usage( "StartAllRegionSpawns" )]
|
|
[Description( "Sets the region spawners of all regions as running." )]
|
|
private static void StartAllRegionSpawns_OnCommand( CommandEventArgs args )
|
|
{
|
|
foreach ( SpawnEntry entry in m_Table.Values )
|
|
{
|
|
entry.Start();
|
|
}
|
|
|
|
args.Mobile.SendMessage( "All region spawners have started." );
|
|
}
|
|
|
|
[Usage( "StartRegionSpawns [<region name>]" )]
|
|
[Description( "Sets the region spawners of the region in which you are (or that you provided) as running." )]
|
|
private static void StartRegionSpawns_OnCommand( CommandEventArgs args )
|
|
{
|
|
BaseRegion region = GetCommandData( args );
|
|
|
|
if ( region == null )
|
|
return;
|
|
|
|
for ( int i = 0; i < region.Spawns.Length; i++ )
|
|
region.Spawns[i].Start();
|
|
|
|
args.Mobile.SendMessage( "Spawners of region '{0}' have started.", region );
|
|
}
|
|
|
|
[Usage( "StopAllRegionSpawns" )]
|
|
[Description( "Sets the region spawners of all regions as not running." )]
|
|
private static void StopAllRegionSpawns_OnCommand( CommandEventArgs args )
|
|
{
|
|
foreach ( SpawnEntry entry in m_Table.Values )
|
|
{
|
|
entry.Stop();
|
|
}
|
|
|
|
args.Mobile.SendMessage( "All region spawners have stopped." );
|
|
}
|
|
|
|
[Usage( "StopRegionSpawns [<region name>]" )]
|
|
[Description( "Sets the region spawners of the region in which you are (or that you provided) as not running." )]
|
|
private static void StopRegionSpawns_OnCommand( CommandEventArgs args )
|
|
{
|
|
BaseRegion region = GetCommandData( args );
|
|
|
|
if ( region == null )
|
|
return;
|
|
|
|
for ( int i = 0; i < region.Spawns.Length; i++ )
|
|
region.Spawns[i].Stop();
|
|
|
|
args.Mobile.SendMessage( "Spawners of region '{0}' have stopped.", region );
|
|
}
|
|
}
|
|
} |