From 72c4dc6018ce1c8b741752f71f31fe30eb88c677 Mon Sep 17 00:00:00 2001 From: eos Date: Sun, 12 Feb 2012 01:59:30 +0000 Subject: [PATCH] - PreventInaccess class, moves staff characters to jail or green acres in Felucca or Trammel randomly if they disconnect during the login process. - Added a [TileAvg command, which tiles items/mobiles on the average Z of the map instead of a fixed Z. - For all the tile commands ([Tile, [TileRXYZ, [TileXYZ, [TileZ, [TileAvg), added outline commands to only put items/mobiles on the outside of the area ([Outline, [OutlineRXYZ, [OutlineXYZ, [OutlineZ, [OutlineAvg). - Removed the [OpenBrowser confirmation messages when using the command on multiple mobiles at the same time. - Added a label showing the type of the object currently being viewed to the top of the properties gump. - Added SA skills (Mysticism, Throwing and Imbuing) to the [Skills gump. - Fixed DeathRobe decay when automatically dropped by self looting your corpse. - Removed the (summoned) property on mouseover from clones summoned through Mirror Image. - The Z of the Magincia destination of the public moongates is now calculated when the server starts, so both old and new maps will have the correct Z (34 and 31 respectively). - KeywordTeleporters no longer teleport if you're not within the trigger range when the delay ends. - Added WaitTeleporter, which is a KeywordTeleporter supporting adding messages when a character starts the process and showing the remaining time. When a character logs and times out, any running teleport timer is stopped. - Fixed BaseWaterContainer single click behavior. - Fixed MageAI reveal behavior. - House placement will now return the correct message when targeting inside a TempNoHousingRegion. - Tracking will no longer be able to track characters with higher access after they hide. - Added CorpseNameOverride to BaseCreature to override the name used for the generated corpse. - Pet ghosts will no longer be deleted when (auto)stabled. --- Scripts/Commands/Add.cs | 208 ++++++++++++++---- Scripts/Commands/Generic/Commands/Commands.cs | 31 ++- Scripts/Gumps/Properties/PropsGump.cs | 25 ++- Scripts/Gumps/SkillsGump.cs | 9 +- Scripts/Items/Clothing/OuterTorso.cs | 31 +-- Scripts/Items/Misc/Corpses/Corpse.cs | 11 + Scripts/Items/Misc/PublicMoongate.cs | 6 +- Scripts/Items/Misc/Teleporter.cs | 202 ++++++++++++++++- .../Rares/Containers/BaseWaterContainer.cs | 24 +- Scripts/Mobiles/AI/MageAI.cs | 174 +++++++++------ Scripts/Mobiles/BaseCreature.cs | 20 +- Scripts/Multis/Deeds.cs | 2 + Scripts/Multis/HousePlacementTool.cs | 2 + Scripts/Skills/Tracking.cs | 2 +- .../SpecialSystems/Engines/PreventInaccess.cs | 93 ++++++++ 15 files changed, 685 insertions(+), 155 deletions(-) create mode 100644 Scripts/SpecialSystems/Engines/PreventInaccess.cs diff --git a/Scripts/Commands/Add.cs b/Scripts/Commands/Add.cs index 9de5d3943..24fdf914a 100644 --- a/Scripts/Commands/Add.cs +++ b/Scripts/Commands/Add.cs @@ -18,14 +18,26 @@ namespace Server.Commands CommandSystem.Register( "TileRXYZ", AccessLevel.GameMaster, new CommandEventHandler( TileRXYZ_OnCommand ) ); CommandSystem.Register( "TileXYZ", AccessLevel.GameMaster, new CommandEventHandler( TileXYZ_OnCommand ) ); CommandSystem.Register( "TileZ", AccessLevel.GameMaster, new CommandEventHandler( TileZ_OnCommand ) ); + CommandSystem.Register( "TileAvg", AccessLevel.GameMaster, new CommandEventHandler( TileAvg_OnCommand ) ); + + CommandSystem.Register( "Outline", AccessLevel.GameMaster, new CommandEventHandler( Outline_OnCommand ) ); + CommandSystem.Register( "OutlineRXYZ", AccessLevel.GameMaster, new CommandEventHandler( OutlineRXYZ_OnCommand ) ); + CommandSystem.Register( "OutlineXYZ", AccessLevel.GameMaster, new CommandEventHandler( OutlineXYZ_OnCommand ) ); + CommandSystem.Register( "OutlineZ", AccessLevel.GameMaster, new CommandEventHandler( OutlineZ_OnCommand ) ); + CommandSystem.Register( "OutlineAvg", AccessLevel.GameMaster, new CommandEventHandler( OutlineAvg_OnCommand ) ); } public static void Invoke( Mobile from, Point3D start, Point3D end, string[] args ) { - Invoke( from, start, end, args, null ); + Invoke( from, start, end, args, null, false, false ); } public static void Invoke( Mobile from, Point3D start, Point3D end, string[] args, List packs ) + { + Invoke( from, start, end, args, packs, false, false ); + } + + public static void Invoke( Mobile from, Point3D start, Point3D end, string[] args, List packs, bool outline, bool mapAvg ) { StringBuilder sb = new StringBuilder(); @@ -83,7 +95,7 @@ namespace Server.Commands DateTime time = DateTime.Now; - int built = BuildObjects( from, type, start, end, args, props, packs ); + int built = BuildObjects( from, type, start, end, args, props, packs, outline, mapAvg ); if ( built > 0 ) from.SendMessage( "{0} object{1} generated in {2:F1} seconds.", built, built != 1 ? "s" : "", (DateTime.Now - time).TotalSeconds ); @@ -108,6 +120,11 @@ namespace Server.Commands } public static int BuildObjects( Mobile from, Type type, Point3D start, Point3D end, string[] args, string[,] props, List packs ) + { + return BuildObjects( from, type, start, end, args, props, packs, false, false ); + } + + public static int BuildObjects( Mobile from, Type type, Point3D start, Point3D end, string[] args, string[,] props, List packs, bool outline, bool mapAvg ) { Utility.FixPoints( ref start, ref end ); @@ -169,7 +186,7 @@ namespace Server.Commands if ( paramValues == null ) continue; - int built = Build( from, start, end, ctor, paramValues, props, realProps, packs ); + int built = Build( from, start, end, ctor, paramValues, props, realProps, packs, outline, mapAvg ); if ( built > 0 ) return built; @@ -270,12 +287,30 @@ namespace Server.Commands } public static int Build( Mobile from, Point3D start, Point3D end, ConstructorInfo ctor, object[] values, string[,] props, PropertyInfo[] realProps, List packs ) + { + return Build( from, start, end, ctor, values, props, realProps, packs, false, false ); + } + + public static int Build( Mobile from, Point3D start, Point3D end, ConstructorInfo ctor, object[] values, string[,] props, PropertyInfo[] realProps, List packs, bool outline, bool mapAvg ) { try { Map map = from.Map; - int objectCount = ( packs == null ? (((end.X - start.X) + 1) * ((end.Y - start.Y) + 1)) : packs.Count ); + int width = end.X - start.X + 1; + int height = end.Y - start.Y + 1; + + if ( outline && ( width < 3 || height < 3 ) ) + outline = false; + + int objectCount; + + if ( packs != null ) + objectCount = packs.Count; + else if ( outline ) + objectCount = ( width + height - 2 ) * 2; + else + objectCount = width * height; if ( objectCount >= 20 ) from.SendMessage( "Constructing {0} objects, please wait.", objectCount ); @@ -292,7 +327,7 @@ namespace Server.Commands IEntity built = Build( from, ctor, values, props, realProps, ref sendError ); sb.AppendFormat( "0x{0:X}; ", built.Serial.Value ); - + if ( built is Item ) { Container pack = packs[i]; pack.DropItem( (Item)built ); @@ -305,21 +340,29 @@ namespace Server.Commands } else { + int z = start.Z; + for ( int x = start.X; x <= end.X; ++x ) { for ( int y = start.Y; y <= end.Y; ++y ) { + if ( outline && x != start.X && x != end.X && y != start.Y && y != end.Y ) + continue; + + if ( mapAvg ) + z = map.GetAverageZ( x, y ); + IEntity built = Build( from, ctor, values, props, realProps, ref sendError ); sb.AppendFormat( "0x{0:X}; ", built.Serial.Value ); if ( built is Item ) { Item item = (Item)built; - item.MoveToWorld( new Point3D( x, y, start.Z ), map ); + item.MoveToWorld( new Point3D( x, y, z ), map ); } else if ( built is Mobile ) { Mobile m = (Mobile)built; - m.MoveToWorld( new Point3D( x, y, start.Z ), map ); + m.MoveToWorld( new Point3D( x, y, z ), map ); } } } @@ -410,51 +453,60 @@ namespace Server.Commands } } + private enum TileZType + { + Start, + Fixed, + MapAverage + } + private class TileState { - public bool m_UseFixedZ; + public TileZType m_ZType; public int m_FixedZ; public string[] m_Args; + public bool m_Outline; - public TileState( string[] args ) : this( false, 0, args ) + public TileState( TileZType zType, int fixedZ, string[] args, bool outline ) { - } - - public TileState( int fixedZ, string[] args ) : this( true, fixedZ, args ) - { - } - - public TileState( bool useFixedZ, int fixedZ, string[] args ) - { - m_UseFixedZ = useFixedZ; + m_ZType = zType; m_FixedZ = fixedZ; m_Args = args; + m_Outline = outline; } } private static void TileBox_Callback( Mobile from, Map map, Point3D start, Point3D end, object state ) { TileState ts = (TileState)state; + bool mapAvg = false; - if ( ts.m_UseFixedZ ) - start.Z = end.Z = ts.m_FixedZ; + switch ( ts.m_ZType ) + { + case TileZType.Fixed: + { + start.Z = end.Z = ts.m_FixedZ; + break; + } + case TileZType.MapAverage: + { + mapAvg = true; + break; + } + } - Invoke( from, start, end, ts.m_Args ); + Invoke( from, start, end, ts.m_Args, null, ts.m_Outline, mapAvg ); } - [Usage( "Tile [params] [set { ...}]" )] - [Description( "Tiles an item or npc by name into a targeted bounding box. Optional constructor parameters. Optional set property list." )] - public static void Tile_OnCommand( CommandEventArgs e ) + private static void Internal_OnCommand( CommandEventArgs e, bool outline ) { if ( e.Length >= 1 ) - BoundingBoxPicker.Begin( e.Mobile, new BoundingBoxCallback( TileBox_Callback ), new TileState( e.Arguments ) ); + BoundingBoxPicker.Begin( e.Mobile, new BoundingBoxCallback( TileBox_Callback ), new TileState( TileZType.Start, 0, e.Arguments, outline ) ); else - e.Mobile.SendMessage( "Format: Add [params] [set { ...}]" ); + e.Mobile.SendMessage( "Format: {0} [params] [set {{ ...}}]", outline ? "Outline" : "Tile" ); } - [Usage( "TileRXYZ [params] [set { ...}]" )] - [Description( "Tiles an item or npc by name into a given bounding box, (x, y) parameters are relative to your characters position. Optional constructor parameters. Optional set property list." )] - public static void TileRXYZ_OnCommand( CommandEventArgs e ) + private static void InternalRXYZ_OnCommand( CommandEventArgs e, bool outline ) { if ( e.Length >= 6 ) { @@ -466,17 +518,15 @@ namespace Server.Commands for ( int i = 0; i < subArgs.Length; ++i ) subArgs[i] = e.Arguments[i + 5]; - Add.Invoke( e.Mobile, p, p2, subArgs ); + Add.Invoke( e.Mobile, p, p2, subArgs, null, outline, false ); } else { - e.Mobile.SendMessage( "Format: TileRXYZ [params] [set { ...}]" ); + e.Mobile.SendMessage( "Format: {0}RXYZ [params] [set {{ ...}}]", outline ? "Outline" : "Tile" ); } } - [Usage( "TileXYZ [params] [set { ...}]" )] - [Description( "Tiles an item or npc by name into a given bounding box. Optional constructor parameters. Optional set property list." )] - public static void TileXYZ_OnCommand( CommandEventArgs e ) + private static void InternalXYZ_OnCommand( CommandEventArgs e, bool outline ) { if ( e.Length >= 6 ) { @@ -488,17 +538,15 @@ namespace Server.Commands for ( int i = 0; i < subArgs.Length; ++i ) subArgs[i] = e.Arguments[i + 5]; - Add.Invoke( e.Mobile, p, p2, subArgs ); + Add.Invoke( e.Mobile, p, p2, subArgs, null, outline, false ); } else { - e.Mobile.SendMessage( "Format: TileXYZ [params] [set { ...}]" ); + e.Mobile.SendMessage( "Format: {0}XYZ [params] [set {{ ...}}]", outline ? "Outline" : "Tile" ); } } - [Usage( "TileZ [params] [set { ...}]" )] - [Description( "Tiles an item or npc by name into a targeted bounding box at a fixed Z location. Optional constructor parameters. Optional set property list." )] - public static void TileZ_OnCommand( CommandEventArgs e ) + private static void InternalZ_OnCommand( CommandEventArgs e, bool outline ) { if ( e.Length >= 2 ) { @@ -507,14 +555,92 @@ namespace Server.Commands for ( int i = 0; i < subArgs.Length; ++i ) subArgs[i] = e.Arguments[i + 1]; - BoundingBoxPicker.Begin( e.Mobile, new BoundingBoxCallback( TileBox_Callback ), new TileState( e.GetInt32( 0 ), subArgs ) ); + BoundingBoxPicker.Begin( e.Mobile, new BoundingBoxCallback( TileBox_Callback ), new TileState( TileZType.Fixed, e.GetInt32( 0 ), subArgs, outline ) ); } else { - e.Mobile.SendMessage( "Format: TileZ [params] [set { ...}]" ); + e.Mobile.SendMessage( "Format: {0}Z [params] [set {{ ...}}]", outline ? "Outline" : "Tile" ); } } + private static void InternalAvg_OnCommand( CommandEventArgs e, bool outline ) + { + if ( e.Length >= 1 ) + BoundingBoxPicker.Begin( e.Mobile, new BoundingBoxCallback( TileBox_Callback ), new TileState( TileZType.MapAverage, 0, e.Arguments, outline ) ); + else + e.Mobile.SendMessage( "Format: {0}Avg [params] [set {{ ...}}]", outline ? "Outline" : "Tile" ); + } + + [Usage( "Tile [params] [set { ...}]" )] + [Description( "Tiles an item or npc by name into a targeted bounding box. Optional constructor parameters. Optional set property list." )] + public static void Tile_OnCommand( CommandEventArgs e ) + { + Internal_OnCommand( e, false ); + } + + [Usage( "TileRXYZ [params] [set { ...}]" )] + [Description( "Tiles an item or npc by name into a given bounding box, (x, y) parameters are relative to your characters position. Optional constructor parameters. Optional set property list." )] + public static void TileRXYZ_OnCommand( CommandEventArgs e ) + { + InternalRXYZ_OnCommand( e, false ); + } + + [Usage( "TileXYZ [params] [set { ...}]" )] + [Description( "Tiles an item or npc by name into a given bounding box. Optional constructor parameters. Optional set property list." )] + public static void TileXYZ_OnCommand( CommandEventArgs e ) + { + InternalXYZ_OnCommand( e, false ); + } + + [Usage( "TileZ [params] [set { ...}]" )] + [Description( "Tiles an item or npc by name into a targeted bounding box at a fixed Z location. Optional constructor parameters. Optional set property list." )] + public static void TileZ_OnCommand( CommandEventArgs e ) + { + InternalZ_OnCommand( e, false ); + } + + [Usage( "TileAvg [params] [set { ...}]" )] + [Description( "Tiles an item or npc by name into a targeted bounding box on the map's average Z elevation. Optional constructor parameters. Optional set property list." )] + public static void TileAvg_OnCommand( CommandEventArgs e ) + { + InternalAvg_OnCommand( e, false ); + } + + [Usage( "Outline [params] [set { ...}]" )] + [Description( "Tiles an item or npc by name around a targeted bounding box. Optional constructor parameters. Optional set property list." )] + public static void Outline_OnCommand( CommandEventArgs e ) + { + Internal_OnCommand( e, true ); + } + + [Usage( "OutlineRXYZ [params] [set { ...}]" )] + [Description( "Tiles an item or npc by name around a given bounding box, (x, y) parameters are relative to your characters position. Optional constructor parameters. Optional set property list." )] + public static void OutlineRXYZ_OnCommand( CommandEventArgs e ) + { + InternalRXYZ_OnCommand( e, true ); + } + + [Usage( "OutlineXYZ [params] [set { ...}]" )] + [Description( "Tiles an item or npc by name around a given bounding box. Optional constructor parameters. Optional set property list." )] + public static void OutlineXYZ_OnCommand( CommandEventArgs e ) + { + InternalXYZ_OnCommand( e, true ); + } + + [Usage( "OutlineZ [params] [set { ...}]" )] + [Description( "Tiles an item or npc by name around a targeted bounding box at a fixed Z location. Optional constructor parameters. Optional set property list." )] + public static void OutlineZ_OnCommand( CommandEventArgs e ) + { + InternalZ_OnCommand( e, true ); + } + + [Usage( "OutlineAvg [params] [set { ...}]" )] + [Description( "Tiles an item or npc by name around a targeted bounding box on the map's average Z elevation. Optional constructor parameters. Optional set property list." )] + public static void OutlineAvg_OnCommand( CommandEventArgs e ) + { + InternalAvg_OnCommand( e, true ); + } + private static Type m_EntityType = typeof( IEntity ); public static bool IsEntity( Type t ) diff --git a/Scripts/Commands/Generic/Commands/Commands.cs b/Scripts/Commands/Generic/Commands/Commands.cs index 0e9368354..a03307920 100644 --- a/Scripts/Commands/Generic/Commands/Commands.cs +++ b/Scripts/Commands/Generic/Commands/Commands.cs @@ -213,20 +213,25 @@ namespace Server.Commands.Generic object[] states = (object[])state; Mobile gm = (Mobile)states[0]; string url = (string)states[1]; + bool echo = (bool)states[2]; if ( okay ) { - gm.SendMessage( "{0} : has opened their web browser to : {1}", from.Name, url ); + if ( echo ) + gm.SendMessage( "{0} : has opened their web browser to : {1}", from.Name, url ); + from.LaunchBrowser( url ); } else { + if ( echo ) + gm.SendMessage( "{0} : has chosen not to open their web browser to : {1}", from.Name, url ); + from.SendMessage( "You have chosen not to open your web browser." ); - gm.SendMessage( "{0} : has chosen not to open their web browser to : {1}", from.Name, url ); } } - public override void Execute( CommandEventArgs e, object obj ) + public void Execute( CommandEventArgs e, object obj, bool echo ) { if ( e.Length == 1 ) { @@ -246,8 +251,13 @@ namespace Server.Commands.Generic string url = e.GetString( 0 ); CommandLogging.WriteLine( from, "{0} {1} requesting to open web browser of {2} to {3}", from.AccessLevel, CommandLogging.Format( from ), CommandLogging.Format( mob ), url ); - AddResponse( "Awaiting user confirmation..." ); - mob.SendGump( new WarningGump( 1060637, 30720, String.Format( "A game master is requesting to open your web browser to the following URL:
{0}", url ), 0xFFC000, 320, 240, new WarningGumpCallback( OpenBrowser_Callback ), new object[]{ from, url } ) ); + + if ( echo ) + AddResponse( "Awaiting user confirmation..." ); + else + AddResponse( "Open web browser request sent." ); + + mob.SendGump( new WarningGump( 1060637, 30720, String.Format( "A game master is requesting to open your web browser to the following URL:
{0}", url ), 0xFFC000, 320, 240, new WarningGumpCallback( OpenBrowser_Callback ), new object[]{ from, url, echo } ) ); } } else @@ -260,6 +270,17 @@ namespace Server.Commands.Generic LogFailure( "Format: OpenBrowser " ); } } + + public override void Execute( CommandEventArgs e, object obj ) + { + Execute( e, obj, true ); + } + + public override void ExecuteList( CommandEventArgs e, ArrayList list ) + { + for ( int i = 0; i < list.Count; ++i ) + Execute( e, list[i], false ); + } } public class IncreaseCommand : BaseCommand diff --git a/Scripts/Gumps/Properties/PropsGump.cs b/Scripts/Gumps/Properties/PropsGump.cs index 4cc1a6f1d..1e0fcc52b 100644 --- a/Scripts/Gumps/Properties/PropsGump.cs +++ b/Scripts/Gumps/Properties/PropsGump.cs @@ -17,6 +17,7 @@ namespace Server.Gumps private int m_Page; private Mobile m_Mobile; private object m_Object; + private Type m_Type; private Stack m_Stack; public static readonly bool OldStyle = PropsConfig.OldStyle; @@ -54,6 +55,7 @@ namespace Server.Gumps public static readonly int BorderSize = PropsConfig.BorderSize; private static bool PrevLabel = OldStyle, NextLabel = OldStyle; + private static bool TypeLabel = !OldStyle; private static readonly int PrevLabelOffsetX = PrevWidth + 1; private static readonly int PrevLabelOffsetY = 0; @@ -78,6 +80,7 @@ namespace Server.Gumps { m_Mobile = mobile; m_Object = o; + m_Type = o.GetType(); m_List = BuildList(); Initialize( 0 ); @@ -87,6 +90,7 @@ namespace Server.Gumps { m_Mobile = mobile; m_Object = o; + m_Type = o.GetType(); m_Stack = stack; m_List = BuildList(); @@ -105,6 +109,10 @@ namespace Server.Gumps { m_Mobile = mobile; m_Object = o; + + if ( o != null ) + m_Type = o.GetType(); + m_List = list; m_Stack = stack; @@ -155,7 +163,10 @@ namespace Server.Gumps x += PrevWidth + OffsetSize; if ( !OldStyle ) - AddImageTiled( x - (OldStyle ? OffsetSize : 0), y, emptyWidth + (OldStyle ? OffsetSize * 2 : 0), EntryHeight, HeaderGumpID ); + AddImageTiled( x, y, emptyWidth, EntryHeight, HeaderGumpID ); + + if ( TypeLabel && m_Type != null ) + AddHtml( x, y, emptyWidth, EntryHeight, String.Format( "
{0}
", m_Type.Name ), false, false ); x += emptyWidth + OffsetSize; @@ -506,13 +517,15 @@ namespace Server.Gumps private ArrayList BuildList() { - Type type = m_Object.GetType(); - - PropertyInfo[] props = type.GetProperties( BindingFlags.Static | BindingFlags.Instance | BindingFlags.Public ); - - ArrayList groups = GetGroups( type, props ); ArrayList list = new ArrayList(); + if ( m_Type == null ) + return list; + + PropertyInfo[] props = m_Type.GetProperties( BindingFlags.Static | BindingFlags.Instance | BindingFlags.Public ); + + ArrayList groups = GetGroups( m_Type, props ); + for ( int i = 0; i < groups.Count; ++i ) { DictionaryEntry de = (DictionaryEntry)groups[i]; diff --git a/Scripts/Gumps/SkillsGump.cs b/Scripts/Gumps/SkillsGump.cs index 436114c8a..6709f3b2c 100644 --- a/Scripts/Gumps/SkillsGump.cs +++ b/Scripts/Gumps/SkillsGump.cs @@ -471,7 +471,8 @@ namespace Server.Gumps SkillName.Fletching, SkillName.Inscribe, SkillName.Tailoring, - SkillName.Tinkering + SkillName.Tinkering, + SkillName.Imbuing } ), new SkillsGumpGroup( "Bardic", new SkillName[] { @@ -491,7 +492,8 @@ namespace Server.Gumps SkillName.SpiritSpeak, SkillName.Ninjitsu, SkillName.Bushido, - SkillName.Spellweaving + SkillName.Spellweaving, + SkillName.Mysticism } ), new SkillsGumpGroup( "Miscellaneous", new SkillName[] { @@ -514,7 +516,8 @@ namespace Server.Gumps SkillName.Parry, SkillName.Swords, SkillName.Tactics, - SkillName.Wrestling + SkillName.Wrestling, + SkillName.Throwing } ), new SkillsGumpGroup( "Actions", new SkillName[] { diff --git a/Scripts/Items/Clothing/OuterTorso.cs b/Scripts/Items/Clothing/OuterTorso.cs index 9488e81ab..e08b118c3 100644 --- a/Scripts/Items/Clothing/OuterTorso.cs +++ b/Scripts/Items/Clothing/OuterTorso.cs @@ -102,9 +102,9 @@ namespace Server.Items { private Timer m_DecayTimer; private DateTime m_DecayTime; - + private static TimeSpan m_DefaultDecayTime = TimeSpan.FromMinutes(1.0); - + public override bool DisplayLootType { get{ return false; } @@ -123,25 +123,30 @@ namespace Server.Items from.SendLocalizedMessage( 502440 ); // Scissors can not be used on that to produce anything. return false; } - - public void BeginDecay( TimeSpan delay ) + + public void BeginDecay() + { + BeginDecay( m_DefaultDecayTime ); + } + + private void BeginDecay( TimeSpan delay ) { if ( m_DecayTimer != null ) m_DecayTimer.Stop(); - + m_DecayTime = DateTime.Now + delay; - + m_DecayTimer = new InternalTimer( this, delay ); m_DecayTimer.Start(); } - + public override bool OnDroppedToWorld( Mobile from, Point3D p ) { BeginDecay( m_DefaultDecayTime ); - + return true; } - + public override bool OnDroppedToMobile( Mobile from, Mobile target ) { if (m_DecayTimer != null ) @@ -189,7 +194,7 @@ namespace Server.Items base.Serialize( writer ); writer.Write( (int) 2 ); // version - + writer.Write( m_DecayTimer != null ); if( m_DecayTimer != null ) @@ -201,7 +206,7 @@ namespace Server.Items base.Deserialize( reader ); int version = reader.ReadInt(); - + switch ( version ) { case 2: @@ -603,7 +608,7 @@ namespace Server.Items public MonkRobe() : this( 0x21E ) { } - + [Constructable] public MonkRobe( int hue ) : base( 0x2687, hue ) { @@ -634,7 +639,7 @@ namespace Server.Items int version = reader.ReadInt(); } } - + [Flipable( 0x1f01, 0x1f02 )] public class PlainDress : BaseOuterTorso { diff --git a/Scripts/Items/Misc/Corpses/Corpse.cs b/Scripts/Items/Misc/Corpses/Corpse.cs index 82024ef90..2c5de4c83 100644 --- a/Scripts/Items/Misc/Corpses/Corpse.cs +++ b/Scripts/Items/Misc/Corpses/Corpse.cs @@ -438,6 +438,14 @@ namespace Server.Items public static string GetCorpseName( Mobile m ) { + if ( m is BaseCreature ) + { + BaseCreature bc = (BaseCreature)m; + + if ( bc.CorpseNameOverride != null ) + return bc.CorpseNameOverride; + } + Type t = m.GetType(); object[] attrs = t.GetCustomAttributes( typeof( CorpseNameAttribute ), true ); @@ -1040,7 +1048,10 @@ namespace Server.Items Map map = from.Map; if ( map != null && map != Map.Internal ) + { robe.MoveToWorld( from.Location, map ); + robe.BeginDecay(); + } } Container pack = from.Backpack; diff --git a/Scripts/Items/Misc/PublicMoongate.cs b/Scripts/Items/Misc/PublicMoongate.cs index 414ce3403..d33aa9cf6 100644 --- a/Scripts/Items/Misc/PublicMoongate.cs +++ b/Scripts/Items/Misc/PublicMoongate.cs @@ -237,7 +237,8 @@ namespace Server.Items new PMEntry( new Point3D( 2701, 692, 5 ), 1012007 ), // Minoc new PMEntry( new Point3D( 1828, 2948,-20), 1012008 ), // Trinsic new PMEntry( new Point3D( 643, 2067, 5 ), 1012009 ), // Skara Brae - new PMEntry( new Point3D( 3563, 2139, 34), 1012010 ), // Magincia + /* Dynamic Z for Magincia to support both old and new maps. */ + new PMEntry( new Point3D( 3563, 2139, Map.Trammel.GetAverageZ( 3563, 2139 ) ), 1012010 ), // (New) Magincia new PMEntry( new Point3D( 3450, 2677, 25), 1078098 ) // New Haven } ); @@ -251,7 +252,8 @@ namespace Server.Items new PMEntry( new Point3D( 2701, 692, 5 ), 1012007 ), // Minoc new PMEntry( new Point3D( 1828, 2948,-20), 1012008 ), // Trinsic new PMEntry( new Point3D( 643, 2067, 5 ), 1012009 ), // Skara Brae - new PMEntry( new Point3D( 3563, 2139, 34), 1012010 ), // Magincia + /* Dynamic Z for Magincia to support both old and new maps. */ + new PMEntry( new Point3D( 3563, 2139, Map.Felucca.GetAverageZ( 3563, 2139 ) ), 1012010 ), // (New) Magincia new PMEntry( new Point3D( 2711, 2234, 0 ), 1019001 ) // Buccaneer's Den } ); diff --git a/Scripts/Items/Misc/Teleporter.cs b/Scripts/Items/Misc/Teleporter.cs index 5ae4e84e0..aebb7782a 100644 --- a/Scripts/Items/Misc/Teleporter.cs +++ b/Scripts/Items/Misc/Teleporter.cs @@ -1,4 +1,5 @@ using System; +using System.Collections.Generic; using Server; using Server.Network; using Server.Spells; @@ -147,12 +148,7 @@ namespace Server.Items if ( m_Delay == TimeSpan.Zero ) DoTeleport( m ); else - Timer.DelayCall( m_Delay, new TimerStateCallback( DoTeleport_Callback ), m ); - } - - private void DoTeleport_Callback( object state ) - { - DoTeleport( (Mobile) state ); + Timer.DelayCall( m_Delay, DoTeleport, m ); } public virtual void DoTeleport( Mobile m ) @@ -192,7 +188,7 @@ namespace Server.Items else if ( m_CombatCheck && SpellHelper.CheckCombat( m ) ) { m.SendLocalizedMessage( 1005564, "", 0x22 ); // Wouldst thou flee during the heat of battle?? - return true; + return true; } StartTeleport( m ); @@ -455,6 +451,14 @@ namespace Server.Items } } + public override void DoTeleport( Mobile m ) + { + if ( !m.InRange( GetWorldLocation(), m_Range ) || m.Map != Map ) + return; + + base.DoTeleport( m ); + } + public override bool OnMoveOver( Mobile m ) { return true; @@ -514,4 +518,188 @@ namespace Server.Items } } } + + public class WaitTeleporter : KeywordTeleporter + { + private static Dictionary m_Table; + + public static void Initialize() + { + m_Table = new Dictionary(); + + EventSink.Logout += new LogoutEventHandler( EventSink_Logout ); + } + + public static void EventSink_Logout( LogoutEventArgs e ) + { + Mobile from = e.Mobile; + TeleportingInfo info; + + if ( from == null || !m_Table.TryGetValue( from, out info ) ) + return; + + info.Timer.Stop(); + m_Table.Remove( from ); + } + + private int m_StartNumber; + private string m_StartMessage; + private int m_ProgressNumber; + private string m_ProgressMessage; + private bool m_ShowTimeRemaining; + + [CommandProperty( AccessLevel.GameMaster )] + public int StartNumber + { + get { return m_StartNumber; } + set { m_StartNumber = value; } + } + + [CommandProperty( AccessLevel.GameMaster )] + public string StartMessage + { + get { return m_StartMessage; } + set { m_StartMessage = value; } + } + + [CommandProperty( AccessLevel.GameMaster )] + public int ProgressNumber + { + get { return m_ProgressNumber; } + set { m_ProgressNumber = value; } + } + + [CommandProperty( AccessLevel.GameMaster )] + public string ProgressMessage + { + get { return m_ProgressMessage; } + set { m_ProgressMessage = value; } + } + + [CommandProperty( AccessLevel.GameMaster )] + public bool ShowTimeRemaining + { + get { return m_ShowTimeRemaining; } + set { m_ShowTimeRemaining = value; } + } + + [Constructable] + public WaitTeleporter() + { + } + + public static string FormatTime( TimeSpan ts ) + { + if ( ts.TotalHours >= 1 ) + { + int h = (int)ts.TotalHours; + return String.Format( "{0} hour{1}", h, ( h == 1 ) ? "" : "s" ); + } + else if ( ts.TotalMinutes >= 1 ) + { + int m = (int)ts.TotalMinutes; + return String.Format( "{0} minute{1}", m, ( m == 1 ) ? "" : "s" ); + } + + int s = Math.Max( (int)ts.TotalSeconds, 0 ); + return String.Format( "{0} second{1}", s, ( s == 1 ) ? "" : "s" ); + } + + private void EndLock( Mobile m ) + { + m.EndAction( this ); + } + + public override void StartTeleport( Mobile m ) + { + TeleportingInfo info; + + if ( m_Table.TryGetValue( m, out info ) ) + { + if ( info.Teleporter == this ) + { + if ( m.BeginAction( this ) ) + { + if ( m_ProgressMessage != null ) + m.SendMessage( m_ProgressMessage ); + else if ( m_ProgressNumber != 0 ) + m.SendLocalizedMessage( m_ProgressNumber ); + + if ( m_ShowTimeRemaining ) + m.SendMessage( "Time remaining: {0}", FormatTime( m_Table[m].Timer.Next - DateTime.Now ) ); + + Timer.DelayCall( TimeSpan.FromSeconds( 5 ), EndLock, m ); + } + + return; + } + else + { + info.Timer.Stop(); + } + } + + if ( m_StartMessage != null ) + m.SendMessage( m_StartMessage ); + else if ( m_StartNumber != 0 ) + m.SendLocalizedMessage( m_StartNumber ); + + if ( Delay == TimeSpan.Zero ) + DoTeleport( m ); + else + m_Table[m] = new TeleportingInfo( this, Timer.DelayCall( Delay, DoTeleport, m ) ); + } + + public override void DoTeleport( Mobile m ) + { + m_Table.Remove( m ); + + base.DoTeleport( m ); + } + + public WaitTeleporter( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + + writer.Write( m_StartNumber ); + writer.Write( m_StartMessage ); + writer.Write( m_ProgressNumber ); + writer.Write( m_ProgressMessage ); + writer.Write( m_ShowTimeRemaining ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + + m_StartNumber = reader.ReadInt(); + m_StartMessage = reader.ReadString(); + m_ProgressNumber = reader.ReadInt(); + m_ProgressMessage = reader.ReadString(); + m_ShowTimeRemaining = reader.ReadBool(); + } + + private class TeleportingInfo + { + private WaitTeleporter m_Teleporter; + private Timer m_Timer; + + public WaitTeleporter Teleporter { get { return m_Teleporter; } } + public Timer Timer { get { return m_Timer; } } + + public TeleportingInfo( WaitTeleporter tele, Timer t ) + { + m_Teleporter = tele; + m_Timer = t; + } + } + } } \ No newline at end of file diff --git a/Scripts/Items/Special/Rares/Containers/BaseWaterContainer.cs b/Scripts/Items/Special/Rares/Containers/BaseWaterContainer.cs index 48c2d9943..492bdfd0b 100644 --- a/Scripts/Items/Special/Rares/Containers/BaseWaterContainer.cs +++ b/Scripts/Items/Special/Rares/Containers/BaseWaterContainer.cs @@ -64,7 +64,29 @@ { if( IsEmpty ) { - base.OnDoubleClick( from ); + base.OnSingleClick( from ); + } + else + { + if( Name == null ) + LabelTo( from, LabelNumber ); + else + LabelTo( from, Name ); + } + } + + public override void OnAosSingleClick( Mobile from ) + { + if( IsEmpty ) + { + base.OnAosSingleClick( from ); + } + else + { + if( Name == null ) + LabelTo( from, LabelNumber ); + else + LabelTo( from, Name ); } } diff --git a/Scripts/Mobiles/AI/MageAI.cs b/Scripts/Mobiles/AI/MageAI.cs index 29f7bd90f..751476006 100644 --- a/Scripts/Mobiles/AI/MageAI.cs +++ b/Scripts/Mobiles/AI/MageAI.cs @@ -357,75 +357,66 @@ namespace Server.Mobiles if( spell != null ) return spell; - if (m_Mobile.Combatant.Hidden && Utility.RandomDouble() < .25) + switch (Utility.Random(16)) { - spell = new RevealSpell(m_Mobile, null); - } - else - { - switch (Utility.Random(16)) - { - case 0: - case 1: // Poison them - { - //m_Mobile.DebugSay( "Attempting to poison" ); + case 0: + case 1: // Poison them + { + //m_Mobile.DebugSay( "Attempting to poison" ); - if (!c.Poisoned) - spell = new PoisonSpell(m_Mobile, null); + if (!c.Poisoned) + spell = new PoisonSpell(m_Mobile, null); - break; - } - case 2: // Bless ourselves. - { - //m_Mobile.DebugSay( "Blessing myself" ); + break; + } + case 2: // Bless ourselves. + { + //m_Mobile.DebugSay( "Blessing myself" ); - spell = new BlessSpell(m_Mobile, null); - break; - } - case 3: - case 4: // Curse them. - { - //m_Mobile.DebugSay( "Attempting to curse" ); + spell = new BlessSpell(m_Mobile, null); + break; + } + case 3: + case 4: // Curse them. + { + //m_Mobile.DebugSay( "Attempting to curse" ); - spell = GetRandomCurse(); - break; - } - case 5: // Paralyze them. - { - //m_Mobile.DebugSay( "Attempting to paralyze" ); + spell = GetRandomCurse(); + break; + } + case 5: // Paralyze them. + { + //m_Mobile.DebugSay( "Attempting to paralyze" ); - if (m_Mobile.Skills[SkillName.Magery].Value > 50.0) - spell = new ParalyzeSpell(m_Mobile, null); + if (m_Mobile.Skills[SkillName.Magery].Value > 50.0) + spell = new ParalyzeSpell(m_Mobile, null); - break; - } - case 6: // Drain mana - { - //m_Mobile.DebugSay( "Attempting to drain mana" ); + break; + } + case 6: // Drain mana + { + //m_Mobile.DebugSay( "Attempting to drain mana" ); - spell = GetRandomManaDrainSpell(); - break; - } - case 7: - { - //m_Mobile.DebugSay( "Attempting to Invis" ); + spell = GetRandomManaDrainSpell(); + break; + } + case 7: + { + //m_Mobile.DebugSay( "Attempting to Invis" ); - if (spell == null && Utility.RandomBool()) - { - spell = new InvisibilitySpell(m_Mobile, null); - } + if (Utility.RandomBool()) + spell = new InvisibilitySpell(m_Mobile, null); - break; - } + break; + } - default: // Damage them. - { - //m_Mobile.DebugSay( "Just doing damage" ); + default: // Damage them. + { + //m_Mobile.DebugSay( "Just doing damage" ); - spell = GetRandomDamage(); - break; - } - } + spell = GetRandomDamage(); + break; + } } return spell; @@ -551,15 +542,25 @@ namespace Server.Mobiles return spell; } - private TimeSpan GetDelay() + private TimeSpan GetDelay( Spell spell ) { - double del = ScaleByMagery( 3.0 ); - double min = 6.0 - ( del * 0.75 ); - double max = 6.0 - ( del * 1.25 ); + if( SmartAI || ( spell is DispelSpell ) ) + { + return TimeSpan.FromSeconds( m_Mobile.ActiveSpeed ); + } + else + { + double del = ScaleByMagery( 3.0 ); + double min = 6.0 - ( del * 0.75 ); + double max = 6.0 - ( del * 1.25 ); - return TimeSpan.FromSeconds( min + ( ( max - min ) * Utility.RandomDouble() ) ); + return TimeSpan.FromSeconds( min + ( ( max - min ) * Utility.RandomDouble() ) ); + } } + private Mobile m_LastTarget; + private Point3D m_LastTargetLoc; + public override bool DoActionCombat() { Mobile c = m_Mobile.Combatant; @@ -592,7 +593,7 @@ namespace Server.Mobiles if( AcquireFocusMob( m_Mobile.RangePerception, m_Mobile.FightMode, false, false, true ) ) { - m_Mobile.DebugSay( "Nobody else is around" ); + m_Mobile.DebugSay( "I will switch to {0}", m_Mobile.FocusMob.Name ); m_Mobile.Combatant = c = m_Mobile.FocusMob; m_Mobile.FocusMob = null; } @@ -708,25 +709,45 @@ namespace Server.Mobiles if( spell != null ) spell.Cast(); - TimeSpan delay; - - if( SmartAI || ( spell is DispelSpell ) ) - delay = TimeSpan.FromSeconds( m_Mobile.ActiveSpeed ); - else - delay = GetDelay(); - - m_NextCastTime = DateTime.Now; + m_NextCastTime = DateTime.Now + GetDelay( spell ); } else if( m_Mobile.Spell == null || !m_Mobile.Spell.IsCasting ) { RunTo( c ); } + m_LastTarget = c; + m_LastTargetLoc = c.Location; + return true; } + private LandTarget m_RevealTarget; + public override bool DoActionGuard() { + if( m_LastTarget != null && m_LastTarget.Hidden ) + { + Map map = m_Mobile.Map; + + if( map == null || !m_Mobile.InRange( m_LastTargetLoc, Core.ML ? 10 : 12 ) ) + { + m_LastTarget = null; + } + else if( m_Mobile.Spell == null && DateTime.Now > m_NextCastTime ) + { + m_Mobile.DebugSay( "I am going to reveal my last target" ); + + m_RevealTarget = new LandTarget( m_LastTargetLoc, map ); + Spell spell = new RevealSpell( m_Mobile, null ); + + if( spell.Cast() ) + m_LastTarget = null; // only do it once + + m_NextCastTime = DateTime.Now + GetDelay( spell ); + } + } + if( AcquireFocusMob( m_Mobile.RangePerception, m_Mobile.FightMode, false, false, true ) ) { m_Mobile.DebugSay( "I am going to attack {0}", m_Mobile.FocusMob.Name ); @@ -934,7 +955,7 @@ namespace Server.Mobiles if( targ == null ) return false; - bool isReveal = (targ is RevealSpell.InternalTarget); + bool isReveal = ( targ is RevealSpell.InternalTarget ); bool isDispel = ( targ is DispelSpell.InternalTarget ); bool isParalyze = ( targ is ParalyzeSpell.InternalTarget ); bool isTeleport = ( targ is TeleportSpell.InternalTarget ); @@ -956,7 +977,7 @@ namespace Server.Mobiles else if( toTarget != null && m_Mobile.InRange( toTarget, 10 ) ) RunFrom( toTarget ); } - else if( SmartAI && ( isParalyze || isTeleport || isReveal ) ) + else if( SmartAI && ( isParalyze || isTeleport ) ) { toTarget = FindDispelTarget( true ); @@ -1000,6 +1021,13 @@ namespace Server.Mobiles { targ.Invoke( m_Mobile, m_Mobile ); } + else if( isReveal && m_RevealTarget != null ) + { + targ.Invoke( m_Mobile, m_RevealTarget ); + + if( SmartAI ) + m_Mobile.NextReacquireTime = DateTime.Now; + } else if( isTeleport && toTarget != null ) { Map map = m_Mobile.Map; diff --git a/Scripts/Mobiles/BaseCreature.cs b/Scripts/Mobiles/BaseCreature.cs index a31badfdf..3e1dc37bd 100644 --- a/Scripts/Mobiles/BaseCreature.cs +++ b/Scripts/Mobiles/BaseCreature.cs @@ -243,10 +243,18 @@ namespace Server.Mobiles private bool m_IsPrisoner; + private string m_CorpseNameOverride; #endregion public virtual InhumanSpeech SpeechType{ get{ return null; } } + [CommandProperty( AccessLevel.GameMaster )] + public string CorpseNameOverride + { + get { return m_CorpseNameOverride; } + set { m_CorpseNameOverride = value; } + } + [CommandProperty( AccessLevel.GameMaster, AccessLevel.Administrator )] public bool IsStabled { @@ -1560,7 +1568,7 @@ namespace Server.Mobiles { base.Serialize( writer ); - writer.Write( (int) 17 ); // version + writer.Write( (int) 18 ); // version writer.Write( (int)m_CurrentAI ); writer.Write( (int)m_DefaultAI ); @@ -1676,6 +1684,9 @@ namespace Server.Mobiles writer.Write( TimeSpan.Zero ); else writer.Write( DeleteTimeLeft ); + + // Version 18 + writer.Write( m_CorpseNameOverride ); } private static double[] m_StandardActiveSpeeds = new double[] @@ -1897,6 +1908,9 @@ namespace Server.Mobiles m_DeleteTimer.Start(); } + if ( version >= 18 ) + m_CorpseNameOverride = reader.ReadString(); + if( version <= 14 && m_Paragon && Hue == 0x31 ) { Hue = Paragon.Hue; //Paragon hue fixed, should now be 0x501. @@ -4186,7 +4200,7 @@ namespace Server.Mobiles list.Add( 1080078 ); // guarding } - if ( Summoned && !IsAnimatedDead && !IsNecroFamiliar ) + if ( Summoned && !IsAnimatedDead && !IsNecroFamiliar && !( this is Clone ) ) list.Add( 1049646 ); // (summoned) else if ( Controlled && Commandable ) { @@ -5314,7 +5328,7 @@ namespace Server.Mobiles { Mobile owner = c.ControlMaster; - if ( owner == null || owner.Deleted || owner.Map != c.Map || !owner.InRange( c, 12 ) || !c.CanSee( owner ) || !c.InLOS( owner ) ) + if ( !c.IsStabled && ( owner == null || owner.Deleted || owner.Map != c.Map || !owner.InRange( c, 12 ) || !c.CanSee( owner ) || !c.InLOS( owner ) ) ) { if ( c.OwnerAbandonTime == DateTime.MinValue ) c.OwnerAbandonTime = DateTime.Now; diff --git a/Scripts/Multis/Deeds.cs b/Scripts/Multis/Deeds.cs index 1300debc3..a5f2a5ef9 100644 --- a/Scripts/Multis/Deeds.cs +++ b/Scripts/Multis/Deeds.cs @@ -32,6 +32,8 @@ namespace Server.Multis.Deeds if ( from.AccessLevel >= AccessLevel.GameMaster || reg.AllowHousing( from, p ) ) m_Deed.OnPlacement( from, p ); + else if ( reg.IsPartOf( typeof( TempNoHousingRegion ) ) ) + from.SendLocalizedMessage( 501270 ); // Lord British has decreed a 'no build' period, thus you cannot build this house at this time. else if ( reg.IsPartOf( typeof( TreasureRegion ) ) ) from.SendLocalizedMessage( 1043287 ); // The house could not be created here. Either something is blocking the house, or the house would not be on valid terrain. else if ( reg.IsPartOf( typeof( HouseRaffleRegion ) ) ) diff --git a/Scripts/Multis/HousePlacementTool.cs b/Scripts/Multis/HousePlacementTool.cs index dcf25c21b..9fddf058a 100644 --- a/Scripts/Multis/HousePlacementTool.cs +++ b/Scripts/Multis/HousePlacementTool.cs @@ -248,6 +248,8 @@ namespace Server.Items if ( from.AccessLevel >= AccessLevel.GameMaster || reg.AllowHousing( from, p ) ) m_Placed = m_Entry.OnPlacement( from, p ); + else if ( reg.IsPartOf( typeof( TempNoHousingRegion ) ) ) + from.SendLocalizedMessage( 501270 ); // Lord British has decreed a 'no build' period, thus you cannot build this house at this time. else if ( reg.IsPartOf( typeof( TreasureRegion ) ) ) from.SendLocalizedMessage( 1043287 ); // The house could not be created here. Either something is blocking the house, or the house would not be on valid terrain. else if ( reg.IsPartOf( typeof( HouseRaffleRegion ) ) ) diff --git a/Scripts/Skills/Tracking.cs b/Scripts/Skills/Tracking.cs index 1b50af044..c4e39c7fb 100644 --- a/Scripts/Skills/Tracking.cs +++ b/Scripts/Skills/Tracking.cs @@ -381,7 +381,7 @@ namespace Server.SkillHandlers Stop(); return; } - else if ( m_From.NetState == null || m_From.Deleted || m_Target.Deleted || m_From.Map != m_Target.Map || !m_From.InRange( m_Target, m_Range ) ) + else if ( m_From.NetState == null || m_From.Deleted || m_Target.Deleted || m_From.Map != m_Target.Map || !m_From.InRange( m_Target, m_Range ) || ( m_Target.Hidden && m_Target.AccessLevel > m_From.AccessLevel ) ) { m_Arrow.Stop(); Stop(); diff --git a/Scripts/SpecialSystems/Engines/PreventInaccess.cs b/Scripts/SpecialSystems/Engines/PreventInaccess.cs new file mode 100644 index 000000000..7f9a46cf0 --- /dev/null +++ b/Scripts/SpecialSystems/Engines/PreventInaccess.cs @@ -0,0 +1,93 @@ +using System; +using System.Collections.Generic; +using Server; + +namespace Server.Misc +{ + /* + * This system prevents the inability for server staff to + * access their server due to data overflows during login. + * + * Whenever a staff character's NetState is disposed right after + * the login process, the character is moved to and logged out + * at a "safe" alternative. + * + * The location the character was moved from will be reported + * to the player upon the next successful login. + * + * This system does not affect non-staff players. + */ + public static class PreventInaccess + { + public static readonly bool Enabled = true; + + private static readonly LocationInfo[] m_Destinations = new LocationInfo[] + { + new LocationInfo( new Point3D( 5275, 1163, 0 ), Map.Felucca ), // Jail + new LocationInfo( new Point3D( 5275, 1163, 0 ), Map.Trammel ), + new LocationInfo( new Point3D( 5445, 1153, 0 ), Map.Felucca ), // Green acres + new LocationInfo( new Point3D( 5445, 1153, 0 ), Map.Trammel ) + }; + + private static Dictionary m_MoveHistory; + + public static void Initialize() + { + m_MoveHistory = new Dictionary(); + + if ( Enabled ) + EventSink.Login += new LoginEventHandler( OnLogin ); + } + + public static void OnLogin( LoginEventArgs e ) + { + Mobile from = e.Mobile; + + if ( from == null || from.AccessLevel < AccessLevel.Counselor ) + return; + + if ( HasDisconnected( from ) ) + { + if ( !m_MoveHistory.ContainsKey( from ) ) + m_MoveHistory[from] = new LocationInfo( from.Location, from.Map ); + + LocationInfo dest = GetRandomDestination(); + + from.Location = dest.Location; + from.Map = dest.Map; + } + else if ( m_MoveHistory.ContainsKey( from ) ) + { + LocationInfo orig = m_MoveHistory[from]; + from.SendMessage( "Your character was moved from {0} ({1}) due to a detected client crash.", orig.Location, orig.Map ); + + m_MoveHistory.Remove( from ); + } + } + + private static bool HasDisconnected( Mobile m ) + { + return ( m.NetState == null || m.NetState.Socket == null ); + } + + private static LocationInfo GetRandomDestination() + { + return m_Destinations[Utility.Random( m_Destinations.Length )]; + } + + private class LocationInfo + { + private Point3D m_Location; + private Map m_Map; + + public Point3D Location { get { return m_Location; } } + public Map Map { get { return m_Map; } } + + public LocationInfo( Point3D loc, Map map ) + { + m_Location = loc; + m_Map = map; + } + } + } +}