using System; using System.Collections.Generic; using Server; using Server.Gumps; using Server.Network; using Server.Spells; using Server.Mobiles; using Server.Items; using Server.Regions; 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 m_Table = new Dictionary(); 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 ); 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 { 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 list = new List(); foreach ( Mobile m in from.GetMobilesInRange( range ) ) { if ( ( m.Map == Map.Underworld || m.Region is GraveRegion ) && m is BaseCreature && m.Hidden ) list.Add( m ); else if ( m != from && (!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 ) { return true; } 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 m_List; private TrackWhoGump( Mobile from, List 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]; string name = m.Name; int icon = Utility.RandomList( 0x208E, 0x208F, 0x2090 ); // general icon if ( m is PlayerMobile ) { if ( m.Female ) icon = Utility.RandomList( 0x208C, 0x208D ); else icon = Utility.RandomList( 0x208A, 0x208B ); } else { if ( m.Body.IsHuman ) { if ( m.Female ) icon = Utility.RandomList( 0x209C, 0x209D, 0x209E ); else icon = Utility.RandomList( 0x2099, 0x209A, 0x209B ); } else if ( IsGiant( m ) ){ icon = 0x208F; } else if ( IsOgre( m ) ){ icon = 0x2092; } else if ( IsOrc( m ) ){ icon = 0x2093; } else if ( IsTroll( m ) ){ icon = 0x2092; } else if ( IsUndead( m ) ){ icon = 0x2098; } else if ( IsNature( m ) ){ icon = 0x2094; } else if ( IsElemental( m ) ){ icon = 0x2088; } else if ( IsDemon( m ) ){ icon = 0x2082; } else if ( IsArachnids( m ) ){ icon = 0x2097; } else if ( IsPlant( m ) ){ icon = 0x20A0; } else if ( IsGoblin( m ) ){ icon = 0x209F; } else if ( IsDragon( m ) ){ icon = 0x2087; } else if ( IsSnake( m ) || IsSerpent( m ) ){ icon = 0x2096; } else if ( IsReptile( m ) ){ icon = 0x2091; } else if ( IsOcean( m ) ){ icon = 0x2095; } else if ( m is Slime || m is Sludge ){ icon = 0x20A1; } else if ( m is EvilMage || m is EvilMageLord ){ icon = 0x2089; } else if ( m is BaseCreature && ((BaseCreature)m).Feathers > 0 ){ icon = 0x2081; } else if ( m.Body.IsAnimal ){ icon = 0x2080; } } AddItem( 20 + ((i % 4) * 100), 26 + ((i / 4) * 155), icon ); AddButton( 20 + ((i % 4) * 100), 130 + ((i / 4) * 155), 4005, 4007, i + 1, GumpButtonType.Reply, 0 ); if ( m.Name != null ) { if ( m.HiddenTitle != null ) name = name + " " + m.HiddenTitle; else if ( m.Title != null ) name = name + " " + m.Title; } AddHtml( 20 + ((i % 4) * 100), 90 + ((i / 4) * 155), 90, 40, name, false, false ); } } private static bool IsOgre( Mobile m ){ return (SlayerGroup.GetEntryByName( SlayerName.OgreTrashing )).Slays(m); } private static bool IsOrc( Mobile m ){ return (SlayerGroup.GetEntryByName( SlayerName.OrcSlaying )).Slays(m); } private static bool IsTroll( Mobile m ){ return (SlayerGroup.GetEntryByName( SlayerName.TrollSlaughter )).Slays(m); } private static bool IsUndead( Mobile m ){ return (SlayerGroup.GetEntryByName( SlayerName.UndeadDoom )).Slays(m); } private static bool IsNature( Mobile m ){ return (SlayerGroup.GetEntryByName( SlayerName.NaturesFury )).Slays(m); } private static bool IsElemental( Mobile m ){ return (SlayerGroup.GetEntryByName( SlayerName.ElementalBan )).Slays(m); } private static bool IsDemon( Mobile m ){ return (SlayerGroup.GetEntryByName( SlayerName.Exorcism )).Slays(m); } private static bool IsArachnids( Mobile m ){ return (SlayerGroup.GetEntryByName( SlayerName.BugButcher )).Slays(m); } private static bool IsReptile( Mobile m ){ return (SlayerGroup.GetEntryByName( SlayerName.ReptilianDeath )).Slays(m); } private static bool IsDragon( Mobile m ){ return (SlayerGroup.GetEntryByName( SlayerName.DragonSlaying )).Slays(m); } private static bool IsSnake( Mobile m ){ return (SlayerGroup.GetEntryByName( SlayerName.SerpentBane )).Slays(m); } private static bool IsSerpent( Mobile m ){ return (SlayerGroup.GetEntryByName( SlayerName.SerpentoidMassacre )).Slays(m); } private static bool IsOcean( Mobile m ){ return (SlayerGroup.GetEntryByName( SlayerName.SeaSlaughter )).Slays(m); } private static bool IsGoblin( Mobile m ){ return (SlayerGroup.GetEntryByName( SlayerName.GoblinoidHunter )).Slays(m); } private static bool IsPlant( Mobile m ){ return (SlayerGroup.GetEntryByName( SlayerName.WeedWrecker )).Slays(m); } private static bool IsGiant( Mobile m ){ return (SlayerGroup.GetEntryByName( SlayerName.GiantsFall )).Slays(m); } 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 ); } } } 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_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(); } } } }