runuo/Scripts/Skills/Tracking.cs
eos 72c4dc6018 - 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.
2012-02-12 01:59:30 +00:00

400 lines
No EOL
10 KiB
C#

using System;
using System.Collections.Generic;
using Server;
using Server.Gumps;
using Server.Network;
using Server.Spells.Necromancy;
using Server.Spells;
namespace Server.SkillHandlers
{
public class Tracking
{
public static void Initialize()
{
SkillInfo.Table[(int)SkillName.Tracking].Callback = new SkillUseCallback( OnUse );
}
public static TimeSpan OnUse( Mobile m )
{
m.SendLocalizedMessage( 1011350 ); // What do you wish to track?
m.CloseGump( typeof( TrackWhatGump ) );
m.CloseGump( typeof( TrackWhoGump ) );
m.SendGump( new TrackWhatGump( m ) );
return TimeSpan.FromSeconds( 10.0 ); // 10 second delay before beign able to re-use a skill
}
public class TrackingInfo
{
public Mobile m_Tracker;
public Mobile m_Target;
public Point2D m_Location;
public Map m_Map;
public TrackingInfo( Mobile tracker, Mobile target )
{
m_Tracker = tracker;
m_Target = target;
m_Location = new Point2D( target.X, target.Y );
m_Map = target.Map;
}
}
private static Dictionary<Mobile, TrackingInfo> m_Table = new Dictionary<Mobile, TrackingInfo>();
public static void AddInfo( Mobile tracker, Mobile target )
{
TrackingInfo info = new TrackingInfo( tracker, target );
m_Table[tracker] = info;
}
public static double GetStalkingBonus( Mobile tracker, Mobile target )
{
TrackingInfo info = null;
m_Table.TryGetValue( tracker, out info );
if ( info == null || info.m_Target != target || info.m_Map != target.Map )
return 0.0;
int xDelta = info.m_Location.X - target.X;
int yDelta = info.m_Location.Y - target.Y;
double bonus = Math.Sqrt( (xDelta * xDelta) + (yDelta * yDelta) );
m_Table.Remove( tracker ); //Reset as of Pub 40, counting it as bug for Core.SE.
if( Core.ML )
return Math.Min( bonus, 10 + tracker.Skills.Tracking.Value/10 );
return bonus;
}
public static void ClearTrackingInfo( Mobile tracker )
{
m_Table.Remove( tracker );
}
}
public class TrackWhatGump : Gump
{
private Mobile m_From;
private bool m_Success;
public TrackWhatGump( Mobile from ) : base( 20, 30 )
{
m_From = from;
m_Success = from.CheckSkill( SkillName.Tracking, 0.0, 21.1 );
AddPage( 0 );
AddBackground( 0, 0, 440, 135, 5054 );
AddBackground( 10, 10, 420, 75, 2620 );
AddBackground( 10, 85, 420, 25, 3000 );
AddItem( 20, 20, 9682 );
AddButton( 20, 110, 4005, 4007, 1, GumpButtonType.Reply, 0 );
AddHtmlLocalized( 20, 90, 100, 20, 1018087, false, false ); // Animals
AddItem( 120, 20, 9607 );
AddButton( 120, 110, 4005, 4007, 2, GumpButtonType.Reply, 0 );
AddHtmlLocalized( 120, 90, 100, 20, 1018088, false, false ); // Monsters
AddItem( 220, 20, 8454 );
AddButton( 220, 110, 4005, 4007, 3, GumpButtonType.Reply, 0 );
AddHtmlLocalized( 220, 90, 100, 20, 1018089, false, false ); // Human NPCs
AddItem( 320, 20, 8455 );
AddButton( 320, 110, 4005, 4007, 4, GumpButtonType.Reply, 0 );
AddHtmlLocalized( 320, 90, 100, 20, 1018090, false, false ); // Players
}
public override void OnResponse( NetState state, RelayInfo info )
{
if ( info.ButtonID >= 1 && info.ButtonID <= 4 )
TrackWhoGump.DisplayTo( m_Success, m_From, info.ButtonID - 1 );
}
}
public delegate bool TrackTypeDelegate( Mobile m );
public class TrackWhoGump : Gump
{
private Mobile m_From;
private int m_Range;
private static TrackTypeDelegate[] m_Delegates = new TrackTypeDelegate[]
{
new TrackTypeDelegate( IsAnimal ),
new TrackTypeDelegate( IsMonster ),
new TrackTypeDelegate( IsHumanNPC ),
new TrackTypeDelegate( IsPlayer )
};
private class InternalSorter : IComparer<Mobile>
{
private Mobile m_From;
public InternalSorter( Mobile from )
{
m_From = from;
}
public int Compare( Mobile x, Mobile y )
{
if ( x == null && y == null )
return 0;
else if ( x == null )
return -1;
else if ( y == null )
return 1;
return m_From.GetDistanceToSqrt( x ).CompareTo( m_From.GetDistanceToSqrt( y ) );
}
}
public static void DisplayTo( bool success, Mobile from, int type )
{
if ( !success )
{
from.SendLocalizedMessage( 1018092 ); // You see no evidence of those in the area.
return;
}
Map map = from.Map;
if ( map == null )
return;
TrackTypeDelegate check = m_Delegates[type];
from.CheckSkill( SkillName.Tracking, 21.1, 100.0 ); // Passive gain
int range = 10 + (int)(from.Skills[SkillName.Tracking].Value / 10);
List<Mobile> list = new List<Mobile>();
foreach ( Mobile m in from.GetMobilesInRange( range ) )
{
// Ghosts can no longer be tracked
if ( m != from && (!Core.AOS || m.Alive) && (!m.Hidden || m.AccessLevel == AccessLevel.Player || from.AccessLevel > m.AccessLevel) && check( m ) && CheckDifficulty( from, m ) )
list.Add( m );
}
if ( list.Count > 0 )
{
list.Sort( new InternalSorter( from ) );
from.SendGump( new TrackWhoGump( from, list, range ) );
from.SendLocalizedMessage( 1018093 ); // Select the one you would like to track.
}
else
{
if ( type == 0 )
from.SendLocalizedMessage( 502991 ); // You see no evidence of animals in the area.
else if ( type == 1 )
from.SendLocalizedMessage( 502993 ); // You see no evidence of creatures in the area.
else
from.SendLocalizedMessage( 502995 ); // You see no evidence of people in the area.
}
}
// Tracking players uses tracking and detect hidden vs. hiding and stealth
private static bool CheckDifficulty( Mobile from, Mobile m )
{
if ( !Core.AOS || !m.Player )
return true;
int tracking = from.Skills[SkillName.Tracking].Fixed;
int detectHidden = from.Skills[SkillName.DetectHidden].Fixed;
if( Core.ML && m.Race == Race.Elf )
tracking /= 2; //The 'Guide' says that it requires twice as Much tracking SKILL to track an elf. Not the total difficulty to track.
int hiding = m.Skills[SkillName.Hiding].Fixed;
int stealth = m.Skills[SkillName.Stealth].Fixed;
int divisor = hiding + stealth;
// Necromancy forms affect tracking difficulty
if ( TransformationSpellHelper.UnderTransformation( m, typeof( HorrificBeastSpell ) ) )
divisor -= 200;
else if ( TransformationSpellHelper.UnderTransformation( m, typeof( VampiricEmbraceSpell ) ) && divisor < 500 )
divisor = 500;
else if ( TransformationSpellHelper.UnderTransformation( m, typeof( WraithFormSpell ) ) && divisor <= 2000 )
divisor += 200;
int chance;
if ( divisor > 0 )
{
if ( Core.SE )
chance = 50 * (tracking * 2 + detectHidden) / divisor;
else
chance = 50 * (tracking + detectHidden + 10 * Utility.RandomMinMax( 1, 20 )) / divisor;
}
else
chance = 100;
return chance > Utility.Random( 100 );
}
private static bool IsAnimal( Mobile m )
{
return ( !m.Player && m.Body.IsAnimal );
}
private static bool IsMonster( Mobile m )
{
return ( !m.Player && m.Body.IsMonster );
}
private static bool IsHumanNPC( Mobile m )
{
return ( !m.Player && m.Body.IsHuman );
}
private static bool IsPlayer( Mobile m )
{
return m.Player;
}
private List<Mobile> m_List;
private TrackWhoGump( Mobile from, List<Mobile> list, int range ) : base( 20, 30 )
{
m_From = from;
m_List = list;
m_Range = range;
AddPage( 0 );
AddBackground( 0, 0, 440, 155, 5054 );
AddBackground( 10, 10, 420, 75, 2620 );
AddBackground( 10, 85, 420, 45, 3000 );
if ( list.Count > 4 )
{
AddBackground( 0, 155, 440, 155, 5054 );
AddBackground( 10, 165, 420, 75, 2620 );
AddBackground( 10, 240, 420, 45, 3000 );
if ( list.Count > 8 )
{
AddBackground( 0, 310, 440, 155, 5054 );
AddBackground( 10, 320, 420, 75, 2620 );
AddBackground( 10, 395, 420, 45, 3000 );
}
}
for ( int i = 0; i < list.Count && i < 12; ++i )
{
Mobile m = list[i];
AddItem( 20 + ((i % 4) * 100), 20 + ((i / 4) * 155), ShrinkTable.Lookup( m ) );
AddButton( 20 + ((i % 4) * 100), 130 + ((i / 4) * 155), 4005, 4007, i + 1, GumpButtonType.Reply, 0 );
if ( m.Name != null )
AddHtml( 20 + ((i % 4) * 100), 90 + ((i / 4) * 155), 90, 40, m.Name, false, false );
}
}
public override void OnResponse( NetState state, RelayInfo info )
{
int index = info.ButtonID - 1;
if ( index >= 0 && index < m_List.Count && index < 12 )
{
Mobile m = m_List[index];
m_From.QuestArrow = new TrackArrow( m_From, m, m_Range * 2 );
if ( Core.SE )
Tracking.AddInfo( m_From, m );
}
}
}
public class TrackArrow : QuestArrow
{
private Mobile m_From;
private Timer m_Timer;
public TrackArrow( Mobile from, Mobile target, int range ) : base( from, target )
{
m_From = from;
m_Timer = new TrackTimer( from, target, range, this );
m_Timer.Start();
}
public override void OnClick( bool rightClick )
{
if ( rightClick )
{
Tracking.ClearTrackingInfo( m_From );
m_From = null;
Stop();
}
}
public override void OnStop()
{
m_Timer.Stop();
if ( m_From != null )
{
Tracking.ClearTrackingInfo( m_From );
m_From.SendLocalizedMessage( 503177 ); // You have lost your quarry.
}
}
}
public class TrackTimer : Timer
{
private Mobile m_From, m_Target;
private int m_Range;
private int m_LastX, m_LastY;
private QuestArrow m_Arrow;
public TrackTimer( Mobile from, Mobile target, int range, QuestArrow arrow ) : base( TimeSpan.FromSeconds( 0.25 ), TimeSpan.FromSeconds( 2.5 ) )
{
m_From = from;
m_Target = target;
m_Range = range;
m_Arrow = arrow;
}
protected override void OnTick()
{
if ( !m_Arrow.Running )
{
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 ) || ( m_Target.Hidden && m_Target.AccessLevel > m_From.AccessLevel ) )
{
m_Arrow.Stop();
Stop();
return;
}
if ( m_LastX != m_Target.X || m_LastY != m_Target.Y )
{
m_LastX = m_Target.X;
m_LastY = m_Target.Y;
m_Arrow.Update();
}
}
}
}