#region References using System; using System.Collections.Generic; using System.IO; using System.Linq; using Server.ContextMenus; using Server.Engines.XmlSpawner2; using Server.Factions; using Server.Gumps; using Server.Items; using Server.Network; using Server.Regions; using Server.Spells; using Server.Targets; using MoveImpl = Server.Movement.MovementImpl; #endregion namespace Server.Mobiles { public enum AIType { AI_Use_Default, AI_Melee, AI_Animal, AI_Archer, AI_Healer, AI_Vendor, AI_Mage, AI_Berserk, AI_Predator, AI_Thief, AI_NecroMage, AI_OrcScout, AI_Spellbinder, AI_OmniAI, AI_Samurai, AI_Ninja, AI_Spellweaving, AI_Mystic, AI_Paladin, AI_Necro } public enum ActionType { Wander, Combat, Guard, Flee, Backoff, Interact } public abstract class BaseAI { public Timer m_Timer; protected ActionType m_Action; private long m_NextStopGuard; public BaseCreature m_Mobile; /// /// Prevent AI from altering the Mobile's Direction. /// This does not prevent direct modification of a Mobile's Direction. /// Can be useful when processing long sequence attacks for monsters /// where the effect requires Direction manipulation. /// public bool DirectionLocked { get; set; } public BaseAI(BaseCreature m) { m_Mobile = m; m_Timer = new AITimer(this); bool activate; if (!m.PlayerRangeSensitive) { activate = true; } else if (World.Loading) { activate = false; } else if (m.Map == null || m.Map == Map.Internal || !m.Map.GetSector(m).Active) { activate = false; } else { activate = true; } if (activate) { m_Timer.Start(); } Action = ActionType.Wander; } public bool CanRun { get { return m_Mobile.SupportsRunAnimation; } } public ActionType Action { get { return m_Action; } set { m_Action = value; OnActionChanged(); } } public virtual bool WasNamed(string speech) { var name = m_Mobile.Name; return (name != null && Insensitive.StartsWith(speech, name)); } private class InternalEntry : ContextMenuEntry { private readonly Mobile m_From; private readonly BaseCreature m_Mobile; private readonly BaseAI m_AI; private readonly OrderType m_Order; public InternalEntry(Mobile from, int number, int range, BaseCreature mobile, BaseAI ai, OrderType order) : base(number, range) { m_From = from; m_Mobile = mobile; m_AI = ai; m_Order = order; if (mobile.IsDeadPet && (order == OrderType.Guard || order == OrderType.Attack || order == OrderType.Transfer || order == OrderType.Drop)) { Enabled = false; } } public override void OnClick() { if (!m_Mobile.Deleted && m_Mobile.Controlled && m_From.CheckAlive()) { if (m_From.Hidden) { m_From.RevealingAction(); } if (m_Mobile.IsDeadPet && (m_Order == OrderType.Guard || m_Order == OrderType.Attack || m_Order == OrderType.Transfer || m_Order == OrderType.Drop)) { return; } var isOwner = (m_From == m_Mobile.ControlMaster); var isFriend = (!isOwner && m_Mobile.IsPetFriend(m_From)); if (!isOwner && !isFriend) { return; } if (isFriend && m_Order != OrderType.Follow && m_Order != OrderType.Stay && m_Order != OrderType.Stop) { return; } switch (m_Order) { case OrderType.Follow: case OrderType.Attack: case OrderType.Transfer: case OrderType.Friend: case OrderType.Unfriend: { if (m_Order == OrderType.Transfer && m_From.HasTrade) { m_From.SendLocalizedMessage(1010507); // You cannot transfer a pet with a trade pending } else if (m_Order == OrderType.Friend && m_From.HasTrade) { m_From.SendLocalizedMessage(1070947); // You cannot friend a pet with a trade pending } else { if (m_From.NetState != null && m_From.NetState.IsEnhancedClient) { m_AI.BeginPickTargetDelayed(m_From, m_Order); } else { m_AI.BeginPickTarget(m_From, m_Order); } } break; } case OrderType.Release: { if (m_Mobile.Summoned) { goto default; } m_From.SendGump(new ConfirmReleaseGump(m_From, m_Mobile)); break; } default: { if (m_Mobile.CheckControlChance(m_From)) { m_Mobile.ControlOrder = m_Order; } break; } } } } } public virtual void GetContextMenuEntries(Mobile from, List list) { if (from.Alive && m_Mobile.Controlled && from.InRange(m_Mobile, 14)) { if (from == m_Mobile.ControlMaster) { list.Add(new InternalEntry(from, 6111, 14, m_Mobile, this, OrderType.Attack)); // Command: Kill list.Add(new InternalEntry(from, 6108, 14, m_Mobile, this, OrderType.Follow)); // Command: Follow list.Add(new InternalEntry(from, 6107, 14, m_Mobile, this, OrderType.Guard)); // Command: Guard if (m_Mobile.IsBonded) { list.Add(new InternalEntry(from, 6109, 14, m_Mobile, this, OrderType.Drop)); // Command: Drop } list.Add(new InternalEntry(from, 6112, 14, m_Mobile, this, OrderType.Stop)); // Command: Stop list.Add(new InternalEntry(from, 6114, 14, m_Mobile, this, OrderType.Stay)); // Command: Stay if (!m_Mobile.Summoned && !(m_Mobile is GrizzledMare)) { list.Add(new InternalEntry(from, 6110, 14, m_Mobile, this, OrderType.Friend)); // Add Friend list.Add(new InternalEntry(from, 6099, 14, m_Mobile, this, OrderType.Unfriend)); // Remove Friend list.Add(new InternalEntry(from, 6113, 14, m_Mobile, this, OrderType.Transfer)); // Transfer } list.Add( new InternalEntry( from, m_Mobile is BaseHire ? 6129 : 6118, 14, m_Mobile, this, OrderType.Release)); // Dismiss / Release } else if (m_Mobile.IsPetFriend(from)) { list.Add(new InternalEntry(from, 6108, 14, m_Mobile, this, OrderType.Follow)); // Command: Follow list.Add(new InternalEntry(from, 6112, 14, m_Mobile, this, OrderType.Stop)); // Command: Stop list.Add(new InternalEntry(from, 6114, 14, m_Mobile, this, OrderType.Stay)); // Command: Stay } } } public virtual void BeginPickTargetDelayed(Mobile from, OrderType order) { Timer.DelayCall(TimeSpan.FromMilliseconds(100), () => BeginPickTarget(from, order)); } public virtual void BeginPickTarget(Mobile from, OrderType order) { if (m_Mobile.Deleted || !m_Mobile.Controlled || !from.InRange(m_Mobile, 14) || from.Map != m_Mobile.Map) { return; } var isOwner = (from == m_Mobile.ControlMaster); var isFriend = (!isOwner && m_Mobile.IsPetFriend(from)); if (!isOwner && !isFriend) { return; } if (isFriend && order != OrderType.Follow && order != OrderType.Stay && order != OrderType.Stop) { return; } if (from.Target == null) { if (order == OrderType.Transfer) { from.SendLocalizedMessage(502038); // Click on the person to transfer ownership to. } else if (order == OrderType.Friend) { from.SendLocalizedMessage(502020); // Click on the player whom you wish to make a co-owner. } else if (order == OrderType.Unfriend) { from.SendLocalizedMessage(1070948); // Click on the player whom you wish to remove as a co-owner. } from.Target = new AIControlMobileTarget(this, order); } else if (from.Target is AIControlMobileTarget) { var t = (AIControlMobileTarget)from.Target; if (t.Order == order) { t.AddAI(this); } } } public virtual void OnAggressiveAction(Mobile aggressor) { var currentCombat = m_Mobile.Combatant as Mobile; if (currentCombat != null && !aggressor.Hidden && currentCombat != aggressor && m_Mobile.GetDistanceToSqrt(currentCombat) > m_Mobile.GetDistanceToSqrt(aggressor)) { m_Mobile.Combatant = aggressor; } } public virtual void EndPickTarget(Mobile from, IDamageable target, OrderType order) { if (m_Mobile.Deleted || !m_Mobile.Controlled || !from.InRange(m_Mobile, 14) || from.Map != m_Mobile.Map || !from.CheckAlive()) { return; } var isOwner = (from == m_Mobile.ControlMaster); var isFriend = (!isOwner && m_Mobile.IsPetFriend(from)); if (!isOwner && !isFriend) { return; } if (isFriend && order != OrderType.Follow && order != OrderType.Stay && order != OrderType.Stop) { return; } if (order == OrderType.Attack) { if (target is BaseCreature) { var bc = (BaseCreature)target; if (bc.IsScaryToPets && m_Mobile.IsScaredOfScaryThings) { m_Mobile.SayTo(from, "Your pet refuses to attack this creature!"); return; } if ((bc is IBlackSolen && SolenHelper.CheckBlackFriendship(from)) || (bc is IRedSolen && SolenHelper.CheckRedFriendship(from))) { from.SendAsciiMessage("You can not force your pet to attack a creature you are protected from."); return; } if (bc is BaseFactionGuard) { m_Mobile.SayTo(from, "Your pet refuses to attack the guard."); return; } } } if (m_Mobile.CheckControlChance(from)) { m_Mobile.ControlTarget = target; m_Mobile.ControlOrder = order; } } public virtual bool HandlesOnSpeech(Mobile from) { if (from.AccessLevel >= AccessLevel.GameMaster) { return true; } if (from.Alive && m_Mobile.Controlled && m_Mobile.Commandable && (from == m_Mobile.ControlMaster || m_Mobile.IsPetFriend(from))) { return true; } return (from.Alive && from.InRange(m_Mobile.Location, 3) && m_Mobile.IsHumanInTown()); } private static readonly SkillName[] m_KeywordTable = { SkillName.Parry, SkillName.Healing, SkillName.Hiding, SkillName.Stealing, SkillName.Alchemy, SkillName.AnimalLore, SkillName.ItemID, SkillName.ArmsLore, SkillName.Begging, SkillName.Blacksmith, SkillName.Fletching, SkillName.Peacemaking, SkillName.Camping, SkillName.Carpentry, SkillName.Cartography, SkillName.Cooking, SkillName.DetectHidden, SkillName.Discordance, //?? SkillName.EvalInt, SkillName.Fishing, SkillName.Provocation, SkillName.Lockpicking, SkillName.Magery, SkillName.MagicResist, SkillName.Tactics, SkillName.Snooping, SkillName.RemoveTrap, SkillName.Musicianship, SkillName.Poisoning, SkillName.Archery, SkillName.SpiritSpeak, SkillName.Tailoring, SkillName.AnimalTaming, SkillName.TasteID, SkillName.Tinkering, SkillName.Veterinary, SkillName.Forensics, SkillName.Herding, SkillName.Tracking, SkillName.Stealth, SkillName.Inscribe, SkillName.Swords, SkillName.Macing, SkillName.Fencing, SkillName.Wrestling, SkillName.Lumberjacking, SkillName.Mining, SkillName.Meditation }; public virtual void OnSpeech(SpeechEventArgs e) { if (e.Mobile.Alive && e.Mobile.InRange(m_Mobile.Location, 3) && m_Mobile.IsHumanInTown()) { if (e.HasKeyword(0x9D) && WasNamed(e.Speech)) // *move* { if (m_Mobile.Combatant != null) { // I am too busy fighting to deal with thee! m_Mobile.PublicOverheadMessage(MessageType.Regular, 0x3B2, 501482); } else { // Excuse me? m_Mobile.PublicOverheadMessage(MessageType.Regular, 0x3B2, 501516); WalkRandomInHome(2, 2, 1); } } else if (e.HasKeyword(0x9E) && WasNamed(e.Speech)) // *time* { if (m_Mobile.Combatant != null) { // I am too busy fighting to deal with thee! m_Mobile.PublicOverheadMessage(MessageType.Regular, 0x3B2, 501482); } else { int generalNumber; string exactTime; Clock.GetTime(m_Mobile, out generalNumber, out exactTime); m_Mobile.PublicOverheadMessage(MessageType.Regular, 0x3B2, generalNumber); } } else if (e.HasKeyword(0x6C) && WasNamed(e.Speech)) // *train { if (m_Mobile.Combatant != null) { // I am too busy fighting to deal with thee! m_Mobile.PublicOverheadMessage(MessageType.Regular, 0x3B2, 501482); } else { var foundSomething = false; var ourSkills = m_Mobile.Skills; var theirSkills = e.Mobile.Skills; for (var i = 0; i < ourSkills.Length && i < theirSkills.Length; ++i) { var skill = ourSkills[i]; var theirSkill = theirSkills[i]; if (skill != null && theirSkill != null && skill.Base >= 60.0 && m_Mobile.CheckTeach(skill.SkillName, e.Mobile)) { var toTeach = skill.Base / 3.0; if (toTeach > 42.0) { toTeach = 42.0; } if (toTeach > theirSkill.Base) { var number = 1043059 + i; if (number > 1043107) { continue; } if (!foundSomething) { m_Mobile.Say(1043058); // I can train the following: } m_Mobile.Say(number); foundSomething = true; } } } if (!foundSomething) { m_Mobile.Say(501505); // Alas, I cannot teach thee anything. } } } else { var toTrain = (SkillName)(-1); for (var i = 0; toTrain == (SkillName)(-1) && i < e.Keywords.Length; ++i) { var keyword = e.Keywords[i]; if (keyword == 0x154) { toTrain = SkillName.Anatomy; } else if (keyword >= 0x6D && keyword <= 0x9C) { var index = keyword - 0x6D; if (index >= 0 && index < m_KeywordTable.Length) { toTrain = m_KeywordTable[index]; } } } if (toTrain != (SkillName)(-1) && WasNamed(e.Speech)) { if (m_Mobile.Combatant != null) { // I am too busy fighting to deal with thee! m_Mobile.PublicOverheadMessage(MessageType.Regular, 0x3B2, 501482); } else { var skills = m_Mobile.Skills; var skill = skills[toTrain]; if (skill == null || skill.Base < 60.0 || !m_Mobile.CheckTeach(toTrain, e.Mobile)) { m_Mobile.Say(501507); // 'Tis not something I can teach thee of. } else { m_Mobile.Teach(toTrain, e.Mobile, 0, false); } } } } } if (m_Mobile.Controlled && m_Mobile.Commandable) { m_Mobile.DebugSay("Listening..."); var isOwner = (e.Mobile == m_Mobile.ControlMaster); var isFriend = (!isOwner && m_Mobile.IsPetFriend(e.Mobile)); if (e.Mobile.Alive && (isOwner || isFriend)) { m_Mobile.DebugSay("It's from my master"); var keywords = e.Keywords; var speech = e.Speech; // First, check the all* for (var i = 0; i < keywords.Length; ++i) { var keyword = keywords[i]; switch (keyword) { case 0x164: // all come { if (!isOwner) { break; } if (m_Mobile.CheckControlChance(e.Mobile)) { m_Mobile.ControlTarget = null; m_Mobile.ControlOrder = OrderType.Come; } return; } case 0x165: // all follow { BeginPickTarget(e.Mobile, OrderType.Follow); return; } case 0x166: // all guard case 0x16B: // all guard me { if (!isOwner) { break; } if (m_Mobile.CheckControlChance(e.Mobile)) { m_Mobile.ControlOrder = OrderType.Guard; m_Mobile.ControlTarget = null; } return; } case 0x167: // all stop { if (m_Mobile.CheckControlChance(e.Mobile)) { m_Mobile.ControlTarget = null; m_Mobile.ControlOrder = OrderType.Stop; } return; } case 0x168: // all kill case 0x169: // all attack { if (!isOwner) { break; } BeginPickTarget(e.Mobile, OrderType.Attack); return; } case 0x16C: // all follow me { if (m_Mobile.CheckControlChance(e.Mobile)) { m_Mobile.ControlTarget = e.Mobile; m_Mobile.ControlOrder = OrderType.Follow; } return; } case 0x170: // all stay { if (m_Mobile.CheckControlChance(e.Mobile)) { m_Mobile.ControlTarget = null; m_Mobile.ControlOrder = OrderType.Stay; } return; } } } // No all*, so check *command for (var i = 0; i < keywords.Length; ++i) { var keyword = keywords[i]; switch (keyword) { case 0x155: // *come { if (!isOwner) { break; } if (WasNamed(speech) && m_Mobile.CheckControlChance(e.Mobile)) { m_Mobile.ControlTarget = null; m_Mobile.ControlOrder = OrderType.Come; } return; } case 0x156: // *drop { if (!isOwner) { break; } if (!m_Mobile.IsDeadPet && !m_Mobile.Summoned && WasNamed(speech) && m_Mobile.CheckControlChance(e.Mobile)) { m_Mobile.ControlTarget = null; m_Mobile.ControlOrder = OrderType.Drop; } return; } case 0x15A: // *follow { if (WasNamed(speech) && m_Mobile.CheckControlChance(e.Mobile)) { BeginPickTarget(e.Mobile, OrderType.Follow); } return; } case 0x15B: // *friend { if (!isOwner) { break; } if (WasNamed(speech) && m_Mobile.CheckControlChance(e.Mobile)) { if (m_Mobile.Summoned || (m_Mobile is GrizzledMare)) { e.Mobile.SendLocalizedMessage(1005481); // Summoned creatures are loyal only to their summoners. } else if (e.Mobile.HasTrade) { e.Mobile.SendLocalizedMessage(1070947); // You cannot friend a pet with a trade pending } else { BeginPickTarget(e.Mobile, OrderType.Friend); } } return; } case 0x15C: // *guard { if (!isOwner) { break; } if (!m_Mobile.IsDeadPet && WasNamed(speech) && m_Mobile.CheckControlChance(e.Mobile)) { m_Mobile.ControlOrder = OrderType.Guard; m_Mobile.ControlTarget = null; } return; } case 0x15D: // *kill case 0x15E: // *attack { if (!isOwner) { break; } if (!m_Mobile.IsDeadPet && WasNamed(speech) && m_Mobile.CheckControlChance(e.Mobile)) { BeginPickTarget(e.Mobile, OrderType.Attack); } return; } case 0x15F: // *patrol { if (!isOwner) { break; } if (WasNamed(speech) && m_Mobile.CheckControlChance(e.Mobile)) { m_Mobile.ControlTarget = null; m_Mobile.ControlOrder = OrderType.Patrol; } return; } case 0x161: // *stop { if (WasNamed(speech) && m_Mobile.CheckControlChance(e.Mobile)) { m_Mobile.ControlTarget = null; m_Mobile.ControlOrder = OrderType.Stop; } return; } case 0x163: // *follow me { if (WasNamed(speech) && m_Mobile.CheckControlChance(e.Mobile)) { m_Mobile.ControlTarget = e.Mobile; m_Mobile.ControlOrder = OrderType.Follow; } return; } case 0x16D: // *release { if (!isOwner) { break; } if (WasNamed(speech) && m_Mobile.CheckControlChance(e.Mobile)) { if (!m_Mobile.Summoned) { e.Mobile.SendGump(new ConfirmReleaseGump(e.Mobile, m_Mobile)); } else { m_Mobile.ControlTarget = null; m_Mobile.ControlOrder = OrderType.Release; } } return; } case 0x16E: // *transfer { if (!isOwner) { break; } if (!m_Mobile.IsDeadPet && WasNamed(speech) && m_Mobile.CheckControlChance(e.Mobile)) { if (m_Mobile.Summoned || (m_Mobile is GrizzledMare)) { e.Mobile.SendLocalizedMessage(1005487); // You cannot transfer ownership of a summoned creature. } else if (e.Mobile.HasTrade) { e.Mobile.SendLocalizedMessage(1010507); // You cannot transfer a pet with a trade pending } else { BeginPickTarget(e.Mobile, OrderType.Transfer); } } return; } case 0x16F: // *stay { if (WasNamed(speech) && m_Mobile.CheckControlChance(e.Mobile)) { m_Mobile.ControlTarget = null; m_Mobile.ControlOrder = OrderType.Stay; } return; } } } } } else { if (e.Mobile.AccessLevel >= AccessLevel.GameMaster) { m_Mobile.DebugSay("It's from a GM"); if (m_Mobile.FindMyName(e.Speech, true)) { var str = e.Speech.Split(' '); int i; for (i = 0; i < str.Length; i++) { var word = str[i]; if (Insensitive.Equals(word, "obey")) { m_Mobile.SetControlMaster(e.Mobile); if (m_Mobile.Summoned) { m_Mobile.SummonMaster = e.Mobile; } return; } } } } } } public virtual bool Think() { if (m_Mobile.Deleted) { return false; } if (CheckCharming()) return true; if (CheckFlee()) { return true; } switch (Action) { case ActionType.Wander: m_Mobile.OnActionWander(); return DoActionWander(); case ActionType.Combat: m_Mobile.OnActionCombat(); return DoActionCombat(); case ActionType.Guard: m_Mobile.OnActionGuard(); return DoActionGuard(); case ActionType.Flee: m_Mobile.OnActionFlee(); return DoActionFlee(); case ActionType.Interact: m_Mobile.OnActionInteract(); return DoActionInteract(); case ActionType.Backoff: m_Mobile.OnActionBackoff(); return DoActionBackoff(); default: return false; } } public virtual void OnActionChanged() { switch (Action) { case ActionType.Wander: m_Mobile.Warmode = false; m_Mobile.Combatant = null; m_Mobile.FocusMob = null; m_Mobile.CurrentSpeed = m_Mobile.PassiveSpeed; CheckNavPoint(); break; case ActionType.Combat: m_Mobile.Warmode = true; m_Mobile.FocusMob = null; m_Mobile.CurrentSpeed = m_Mobile.ActiveSpeed; break; case ActionType.Guard: m_Mobile.Warmode = true; m_Mobile.FocusMob = null; m_Mobile.Combatant = null; m_NextStopGuard = Core.TickCount + 10000; m_Mobile.CurrentSpeed = m_Mobile.ActiveSpeed; break; case ActionType.Flee: m_Mobile.Warmode = true; m_Mobile.FocusMob = null; m_Mobile.CurrentSpeed = m_Mobile.ActiveSpeed; break; case ActionType.Interact: m_Mobile.Warmode = false; m_Mobile.CurrentSpeed = m_Mobile.PassiveSpeed; break; case ActionType.Backoff: m_Mobile.Warmode = false; m_Mobile.CurrentSpeed = m_Mobile.PassiveSpeed; break; } } public virtual void CheckNavPoint() { var map = m_Mobile.Map; if (map != null && m_Mobile.NavPoints != null && m_Mobile.NavPoints.ContainsKey(map)) { if (m_Mobile.CurrentNavPoint >= 0 && m_Mobile.CurrentNavPoint < m_Mobile.NavPoints[map].Count - 1) { var next = m_Mobile.NavPoints[map][m_Mobile.CurrentNavPoint + 1]; if (m_Mobile.InRange(next, 15)) m_Mobile.CurrentNavPoint++; } } } public virtual bool OnAtWayPoint() { return true; } public virtual bool DoActionWander() { var followRange = m_Mobile.FollowRange; var map = m_Mobile.Map; if (CheckHerding()) { m_Mobile.DebugSay("Praise the shepherd!"); } else if (m_Mobile.NavPoints != null && m_Mobile.NavPoints.ContainsKey(map)) { if (m_Mobile.CurrentNavPoint >= 0 && m_Mobile.CurrentNavPoint < m_Mobile.NavPoints[map].Count) { var point = m_Mobile.NavPoints[map][m_Mobile.CurrentNavPoint]; if (point.X != m_Mobile.X || point.Y != m_Mobile.Y) { m_Mobile.DebugSay(String.Format("I will move towards my navpoint: {0}", point)); //DoMove(m_Mobile.GetDirectionTo(point)); var res = DoMoveImpl(m_Mobile.GetDirectionTo(point)); if (res == MoveResult.Blocked) { CheckNavPoint(); } } else if (OnAtWayPoint()) { if (m_Mobile.CurrentNavPoint + 1 >= m_Mobile.NavPoints[map].Count) m_Mobile.CurrentNavPoint = -1; else { m_Mobile.CurrentNavPoint++; m_Mobile.DebugSay( String.Format("I will go to the next navpoint: {0}", m_Mobile.NavPoints[map][m_Mobile.CurrentNavPoint])); } } } } else if (m_Mobile.CurrentWayPoint != null) { var point = m_Mobile.CurrentWayPoint; if ((point.X != m_Mobile.Location.X || point.Y != m_Mobile.Location.Y) && point.Map == map && point.Parent == null && !point.Deleted) { m_Mobile.DebugSay("I will move towards my waypoint."); DoMove(m_Mobile.GetDirectionTo(m_Mobile.CurrentWayPoint)); } else if (OnAtWayPoint()) { m_Mobile.DebugSay("I will go to the next waypoint"); m_Mobile.CurrentWayPoint = point.NextPoint; if (point.NextPoint != null && point.NextPoint.Deleted) { m_Mobile.CurrentWayPoint = point.NextPoint = point.NextPoint.NextPoint; } } } else if (m_Mobile.IsAnimatedDead || m_Mobile.FollowRange > 0) { // animated dead follow their master var master = m_Mobile.SummonMaster; if (master != null && master.Map == m_Mobile.Map && master.InRange(m_Mobile, m_Mobile.RangePerception + followRange)) { MoveTo(master, false, followRange); } else { WalkRandomInHome(2, 2, 1); } } else if (CheckMove()) { if (!m_Mobile.CheckIdle()) { WalkRandomInHome(2, 2, 1); } } if (!DirectionLocked && m_Mobile.Combatant != null && !m_Mobile.Combatant.Deleted && m_Mobile.Combatant.Alive && (!(m_Mobile.Combatant is Mobile) || !((Mobile)m_Mobile.Combatant).IsDeadBondedPet)) { m_Mobile.Direction = m_Mobile.GetDirectionTo(m_Mobile.Combatant); } return true; } public virtual bool DoActionCombat() { if (Core.AOS && CheckHerding()) { m_Mobile.DebugSay("Praise the shepherd!"); } else { var c = m_Mobile.Combatant; if (c == null || c.Deleted || c.Map != m_Mobile.Map || !c.Alive || (c is Mobile && ((Mobile)c).IsDeadBondedPet)) { Action = ActionType.Wander; } else if (!DirectionLocked) { m_Mobile.Direction = m_Mobile.GetDirectionTo(c); } } return true; } public virtual bool DoActionGuard() { if (Core.AOS && CheckHerding()) { m_Mobile.DebugSay("Praise the shepherd!"); } else if (Core.TickCount < m_NextStopGuard) { m_Mobile.DebugSay("I am on guard"); //m_Mobile.Turn( Utility.Random(0, 2) - 1 ); } else { m_Mobile.DebugSay("I stopped being on guard"); Action = ActionType.Wander; } return true; } public virtual bool DoActionFlee() { var c = m_Mobile.Combatant as Mobile; // We only want enemies we know want to attack us, not those we want to attack if (AcquireFocusMob(m_Mobile.RangePerception, FightMode.Aggressor, false, false, true) || c != null) { // If I found a new target, set it as my combatant if (m_Mobile.FocusMob != null) { m_Mobile.Combatant = m_Mobile.FocusMob; c = m_Mobile.Combatant as Mobile; m_Mobile.FocusMob = null; } // If my combatant is bad, guard if (c == null || c.Deleted || c.Map != m_Mobile.Map) { m_Mobile.DebugSay("I have lost him"); m_Mobile.Combatant = null; Action = ActionType.Guard; return true; } if (WalkMobileRange(c, 1, true, m_Mobile.RangePerception * 2, m_Mobile.RangePerception * 3)) { m_Mobile.DebugSay("I have fled"); Action = ActionType.Guard; return true; } m_Mobile.DebugSay("I am scared of {0}", c.Name); } else { m_Mobile.DebugSay("Area seems clear, but my guard is up"); Action = ActionType.Guard; } return true; } public virtual bool DoActionInteract() { return true; } public virtual bool DoActionBackoff() { return true; } public virtual bool Obey() { if (m_Mobile.Deleted) { return false; } switch (m_Mobile.ControlOrder) { case OrderType.None: return DoOrderNone(); case OrderType.Come: return DoOrderCome(); case OrderType.Drop: return DoOrderDrop(); case OrderType.Friend: return DoOrderFriend(); case OrderType.Unfriend: return DoOrderUnfriend(); case OrderType.Guard: return DoOrderGuard(); case OrderType.Attack: return DoOrderAttack(); case OrderType.Patrol: return DoOrderPatrol(); case OrderType.Release: return DoOrderRelease(); case OrderType.Stay: return DoOrderStay(); case OrderType.Stop: return DoOrderStop(); case OrderType.Follow: return DoOrderFollow(); case OrderType.Transfer: return DoOrderTransfer(); default: return false; } } public virtual void OnCurrentOrderChanged() { if (m_Mobile.Deleted || m_Mobile.ControlMaster == null || m_Mobile.ControlMaster.Deleted) { return; } switch (m_Mobile.ControlOrder) { case OrderType.None: m_Mobile.Home = m_Mobile.Location; m_Mobile.CurrentSpeed = m_Mobile.PassiveSpeed; m_Mobile.PlaySound(m_Mobile.GetIdleSound()); m_Mobile.Warmode = false; m_Mobile.Combatant = null; break; case OrderType.Come: m_Mobile.CurrentSpeed = m_Mobile.ActiveSpeed; m_Mobile.PlaySound(m_Mobile.GetIdleSound()); m_Mobile.Warmode = false; m_Mobile.Combatant = null; break; case OrderType.Drop: m_Mobile.CurrentSpeed = m_Mobile.PassiveSpeed; m_Mobile.PlaySound(m_Mobile.GetIdleSound()); m_Mobile.Warmode = true; m_Mobile.Combatant = null; break; case OrderType.Friend: case OrderType.Unfriend: break; case OrderType.Guard: m_Mobile.CurrentSpeed = m_Mobile.ActiveSpeed; m_Mobile.PlaySound(m_Mobile.GetIdleSound()); m_Mobile.Warmode = true; m_Mobile.Combatant = null; m_Mobile.ControlTarget = null; var petname = String.Format("{0}", m_Mobile.Name); m_Mobile.ControlMaster.SendLocalizedMessage(1049671, petname); //~1_PETNAME~ is now guarding you. break; case OrderType.Attack: m_Mobile.CurrentSpeed = m_Mobile.ActiveSpeed; m_Mobile.PlaySound(m_Mobile.GetIdleSound()); m_Mobile.Warmode = true; m_Mobile.Combatant = null; break; case OrderType.Patrol: m_Mobile.CurrentSpeed = m_Mobile.ActiveSpeed; m_Mobile.PlaySound(m_Mobile.GetIdleSound()); m_Mobile.Warmode = false; m_Mobile.Combatant = null; break; case OrderType.Release: m_Mobile.CurrentSpeed = m_Mobile.PassiveSpeed; m_Mobile.PlaySound(m_Mobile.GetIdleSound()); m_Mobile.Warmode = false; m_Mobile.Combatant = null; break; case OrderType.Stay: m_Mobile.CurrentSpeed = m_Mobile.PassiveSpeed; m_Mobile.PlaySound(m_Mobile.GetIdleSound()); m_Mobile.Warmode = false; m_Mobile.Combatant = null; break; case OrderType.Stop: m_Mobile.Home = m_Mobile.Location; m_Mobile.CurrentSpeed = m_Mobile.PassiveSpeed; m_Mobile.PlaySound(m_Mobile.GetIdleSound()); m_Mobile.Warmode = false; m_Mobile.Combatant = null; break; case OrderType.Follow: m_Mobile.PlaySound(m_Mobile.GetIdleSound()); m_Mobile.Warmode = false; m_Mobile.Combatant = null; m_Mobile.AdjustSpeeds(); m_Mobile.CurrentSpeed = m_Mobile.ActiveSpeed; break; case OrderType.Transfer: m_Mobile.CurrentSpeed = m_Mobile.PassiveSpeed; m_Mobile.PlaySound(m_Mobile.GetIdleSound()); m_Mobile.Warmode = false; m_Mobile.Combatant = null; break; } } public virtual bool DoOrderNone() { m_Mobile.DebugSay("I have no order"); WalkRandomInHome(3, 2, 1); if (m_Mobile.Combatant is Mobile && !m_Mobile.Combatant.Deleted && m_Mobile.Combatant.Alive && (!(m_Mobile.Combatant is Mobile) || !((Mobile)m_Mobile.Combatant).IsDeadBondedPet)) { m_Mobile.Warmode = true; //if (!DirectionLocked) // m_Mobile.Direction = m_Mobile.GetDirectionTo(m_Mobile.Combatant); } else { m_Mobile.Warmode = false; } return true; } public virtual bool DoOrderCome() { if (m_Mobile.ControlMaster != null && !m_Mobile.ControlMaster.Deleted) { var iCurrDist = (int)m_Mobile.GetDistanceToSqrt(m_Mobile.ControlMaster); if (iCurrDist > m_Mobile.RangePerception) { m_Mobile.DebugSay("I have lost my master. I stay here"); m_Mobile.ControlTarget = null; m_Mobile.ControlOrder = OrderType.None; } else { m_Mobile.DebugSay("My master told me come"); // Not exactly OSI style, but better than nothing. var bRun = CanRun && (iCurrDist > 5); if (WalkMobileRange(m_Mobile.ControlMaster, 1, bRun, 0, 1)) { if (m_Mobile.Combatant is Mobile && !m_Mobile.Combatant.Deleted && m_Mobile.Combatant.Alive && (!(m_Mobile.Combatant is Mobile) || !((Mobile)m_Mobile.Combatant).IsDeadBondedPet)) { m_Mobile.Warmode = true; //if (!DirectionLocked) // m_Mobile.Direction = m_Mobile.GetDirectionTo(m_Mobile.Combatant); } else { m_Mobile.Warmode = false; } } } } return true; } public virtual bool DoOrderDrop() { if (m_Mobile.IsDeadPet || !m_Mobile.IsBonded) { return true; } m_Mobile.DebugSay("I drop my stuff for my master"); var pack = m_Mobile.Backpack; if (pack != null) { var list = pack.Items; for (var i = list.Count - 1; i >= 0; --i) { if (i < list.Count) { list[i].MoveToWorld(m_Mobile.Location, m_Mobile.Map); } } } m_Mobile.ControlTarget = null; m_Mobile.ControlOrder = OrderType.None; return true; } public virtual bool CheckCharming() { var target = m_Mobile.CharmTarget; if (target == Point2D.Zero) return false; if (m_Mobile.GetDistanceToSqrt(target) >= 1) DoMove(m_Mobile.GetDirectionTo(target)); return true; } public virtual bool CheckHerding() { var target = m_Mobile.TargetLocation; if (target == null) { return false; // Creature is not being herded } var distance = m_Mobile.GetDistanceToSqrt(target); if (distance < 1 || distance > 15) { m_Mobile.TargetLocation = null; return false; // At the target or too far away } DoMove(m_Mobile.GetDirectionTo(target)); return true; } public virtual bool DoOrderFollow() { if (CheckHerding()) { m_Mobile.DebugSay("Praise the shepherd!"); } else if (m_Mobile.ControlTarget != null && !m_Mobile.ControlTarget.Deleted && m_Mobile.ControlTarget != m_Mobile) { var iCurrDist = (int)m_Mobile.GetDistanceToSqrt(m_Mobile.ControlTarget); if (iCurrDist > m_Mobile.RangePerception * 5) { m_Mobile.DebugSay("I have lost the one to follow. I stay here"); if (m_Mobile.Combatant is Mobile && !m_Mobile.Combatant.Deleted && m_Mobile.Combatant.Alive && (!(m_Mobile.Combatant is Mobile) || !((Mobile)m_Mobile.Combatant).IsDeadBondedPet)) { m_Mobile.Warmode = true; //if (!DirectionLocked) // m_Mobile.Direction = m_Mobile.GetDirectionTo(m_Mobile.Combatant); } else { m_Mobile.Warmode = false; } } else { m_Mobile.DebugSay("My master told me to follow: {0}", m_Mobile.ControlTarget.Name); // Not exactly OSI style, but better than nothing. var bRun = (iCurrDist > 5); if (WalkMobileRange(m_Mobile.ControlTarget, 1, bRun, 0, 1)) { if (m_Mobile.Combatant != null && !m_Mobile.Combatant.Deleted && m_Mobile.Combatant.Alive && (!(m_Mobile.Combatant is Mobile) || !((Mobile)m_Mobile.Combatant).IsDeadBondedPet)) { m_Mobile.Warmode = true; //if (!DirectionLocked) // m_Mobile.Direction = m_Mobile.GetDirectionTo(m_Mobile.Combatant); } else { m_Mobile.Warmode = false; if (Core.AOS) { m_Mobile.CurrentSpeed = m_Mobile.ActiveSpeed; } } } } } else { m_Mobile.DebugSay("I have nobody to follow"); m_Mobile.ControlTarget = null; m_Mobile.ControlOrder = OrderType.None; } return true; } public virtual bool DoOrderFriend() { var from = m_Mobile.ControlMaster; var to = m_Mobile.ControlTarget as Mobile; if (from == null || to == null || from == to || from.Deleted || to.Deleted || !to.Player) { m_Mobile.PublicOverheadMessage(MessageType.Regular, 0x3B2, 502039); // *looks confused* } else { var youngFrom = from is PlayerMobile ? ((PlayerMobile)from).Young : false; var youngTo = to is PlayerMobile ? ((PlayerMobile)to).Young : false; if (youngFrom && !youngTo) { from.SendLocalizedMessage(502040); // As a young player, you may not friend pets to older players. } else if (!youngFrom && youngTo) { from.SendLocalizedMessage(502041); // As an older player, you may not friend pets to young players. } else if (from.CanBeBeneficial(to, true)) { NetState fromState = from.NetState, toState = to.NetState; if (fromState != null && toState != null) { if (from.HasTrade) { from.SendLocalizedMessage(1070947); // You cannot friend a pet with a trade pending } else if (to.HasTrade) { to.SendLocalizedMessage(1070947); // You cannot friend a pet with a trade pending } else if (m_Mobile.IsPetFriend(to)) { from.SendLocalizedMessage(1049691); // That person is already a friend. } else if (!m_Mobile.AllowNewPetFriend) { from.SendLocalizedMessage(1005482); // Your pet does not seem to be interested in making new friends right now. } else if (m_Mobile.CanFriend(to)) { // ~1_NAME~ will now accept movement commands from ~2_NAME~. from.SendLocalizedMessage(1049676, String.Format("{0}\t{1}", m_Mobile.Name, to.Name)); /* ~1_NAME~ has granted you the ability to give orders to their pet ~2_PET_NAME~. * This creature will now consider you as a friend. */ to.SendLocalizedMessage(1043246, String.Format("{0}\t{1}", from.Name, m_Mobile.Name)); m_Mobile.AddPetFriend(to); m_Mobile.ControlTarget = to; m_Mobile.ControlOrder = OrderType.Follow; return true; } } } } m_Mobile.ControlTarget = from; m_Mobile.ControlOrder = OrderType.Follow; return true; } public virtual bool DoOrderUnfriend() { var from = m_Mobile.ControlMaster; var to = m_Mobile.ControlTarget as Mobile; if (from == null || to == null || from == to || from.Deleted || to.Deleted || !to.Player) { m_Mobile.PublicOverheadMessage(MessageType.Regular, 0x3B2, 502039); // *looks confused* } else if (!m_Mobile.IsPetFriend(to)) { from.SendLocalizedMessage(1070953); // That person is not a friend. } else { // ~1_NAME~ will no longer accept movement commands from ~2_NAME~. from.SendLocalizedMessage(1070951, String.Format("{0}\t{1}", m_Mobile.Name, to.Name)); /* ~1_NAME~ has no longer granted you the ability to give orders to their pet ~2_PET_NAME~. * This creature will no longer consider you as a friend. */ to.SendLocalizedMessage(1070952, String.Format("{0}\t{1}", from.Name, m_Mobile.Name)); m_Mobile.RemovePetFriend(to); } m_Mobile.ControlTarget = from; m_Mobile.ControlOrder = OrderType.Follow; return true; } public virtual bool DoOrderGuard() { if (m_Mobile.IsDeadPet) { return true; } var controlMaster = m_Mobile.ControlMaster; if (controlMaster == null || controlMaster.Deleted) { return true; } var combatant = m_Mobile.Combatant as Mobile; if (combatant != null && !ValidGuardTarget(combatant)) combatant = null; Mobile closestMob = combatant; var closestDist = combatant == null ? m_Mobile.RangePerception : combatant.GetDistanceToSqrt(controlMaster); foreach (var aggressor in controlMaster.Aggressors.Select(x => x.Attacker).Where(m => ValidGuardTarget(m))) { var dist = aggressor.GetDistanceToSqrt(controlMaster); if (closestMob == null || dist < closestDist) { closestMob = aggressor; closestDist = dist; } } foreach (var aggressed in controlMaster.Aggressed.Select(x => x.Defender).Where(m => ValidGuardTarget(m))) { var dist = aggressed.GetDistanceToSqrt(controlMaster); if (closestMob == null || dist < closestDist) { closestMob = aggressed; closestDist = dist; } } if (closestMob != null) { if (m_Mobile.Debug && closestMob != null && combatant != closestMob) { m_Mobile.DebugSay("Crap, my master has been attacked! I will attack one of those bastards!"); } combatant = closestMob; } if (combatant != null) { m_Mobile.DebugSay("Guarding from target..."); m_Mobile.Combatant = combatant; m_Mobile.FocusMob = combatant; Action = ActionType.Combat; m_Mobile.Direction = m_Mobile.GetDirectionTo(combatant); /* * We need to call Think() here or spell casting monsters will not use * spells when guarding because their target is never processed. */ Think(); } else { m_Mobile.DebugSay("Nothing to guard from"); m_Mobile.Warmode = false; if (Core.AOS) { m_Mobile.CurrentSpeed = m_Mobile.ActiveSpeed; } WalkMobileRange(controlMaster, 1, false, 0, 1); } return true; } public bool ValidGuardTarget(Mobile combatant) { return combatant != null && combatant != m_Mobile && combatant != m_Mobile.ControlMaster && !combatant.Deleted && m_Mobile.CanSee(combatant) && combatant.Alive && (!(combatant is Mobile) || !combatant.IsDeadBondedPet) && m_Mobile.CanBeHarmful(combatant, false) && combatant.Map == m_Mobile.Map && combatant.GetDistanceToSqrt(m_Mobile) <= m_Mobile.RangePerception; } public virtual bool DoOrderAttack() { if (m_Mobile.IsDeadPet) { return true; } if (m_Mobile.ControlTarget == null || m_Mobile.ControlTarget.Deleted || m_Mobile.ControlTarget.Map != m_Mobile.Map || !m_Mobile.ControlTarget.Alive || (m_Mobile.ControlTarget is Mobile && ((Mobile)m_Mobile.ControlTarget).IsDeadBondedPet)) { m_Mobile.DebugSay( "I think he might be dead. He's not anywhere around here at least. That's cool. I'm glad he's dead."); if (Core.AOS) { m_Mobile.ControlTarget = m_Mobile.ControlMaster; m_Mobile.ControlOrder = OrderType.Follow; } else { m_Mobile.ControlTarget = null; m_Mobile.ControlOrder = OrderType.None; } if (m_Mobile.FightMode == FightMode.Closest || m_Mobile.FightMode == FightMode.Aggressor) { Mobile newCombatant = null; var newScore = 0.0; var eable = m_Mobile.GetMobilesInRange(m_Mobile.RangePerception); foreach (var aggr in eable) { if (!m_Mobile.CanSee(aggr) || aggr.Combatant != m_Mobile) { continue; } if (aggr.IsDeadBondedPet || !aggr.Alive) { continue; } var aggrScore = m_Mobile.GetFightModeRanking(aggr, FightMode.Closest, false); if ((newCombatant == null || aggrScore > newScore || (aggrScore == newScore && !aggr.Player && newCombatant.Player)) && m_Mobile.InLOS(aggr)) { newCombatant = aggr; newScore = aggrScore; } } eable.Free(); if (newCombatant != null) { m_Mobile.ControlTarget = newCombatant; m_Mobile.ControlOrder = OrderType.Attack; m_Mobile.Combatant = newCombatant; m_Mobile.DebugSay("But -that- is not dead. Here we go again..."); Think(); } } } else { m_Mobile.DebugSay("Attacking target..."); if (m_Mobile.Combatant != null) m_Mobile.Direction = m_Mobile.GetDirectionTo(m_Mobile.Combatant); Think(); } return true; } public virtual bool DoOrderPatrol() { m_Mobile.DebugSay("This order is not yet coded"); return true; } public virtual bool DoOrderRelease() { m_Mobile.DebugSay("I have been released"); m_Mobile.PlaySound(m_Mobile.GetAngerSound()); var master = m_Mobile.ControlMaster; if (m_Mobile.DeleteOnRelease) { m_Mobile.PrivateOverheadMessage( MessageType.Regular, 0x3B2, 1043255, String.Format("{0}", m_Mobile.Name), master.NetState); // ~1_NAME~ appears to have decided that it is better off without a master! } m_Mobile.SetControlMaster(null); m_Mobile.SummonMaster = null; m_Mobile.BondingBegin = DateTime.MinValue; m_Mobile.OwnerAbandonTime = DateTime.MinValue; m_Mobile.IsBonded = false; var se = m_Mobile.Spawner as SpawnEntry; if (se != null && se.HomeLocation != Point3D.Zero) { m_Mobile.Home = se.HomeLocation; m_Mobile.RangeHome = se.HomeRange; } if (m_Mobile.DeleteOnRelease || m_Mobile.IsDeadPet) { Timer.DelayCall(TimeSpan.FromSeconds(2), m_Mobile.Delete); } m_Mobile.BeginDeleteTimer(); if (m_Mobile is BaseHire) { if (master != null) { m_Mobile.SayTo(master, 502034, 0x3B2); // I thank thee for thy kindness! m_Mobile.SayTo(master, 502005, 0x3B2); // I quit. } } else { m_Mobile.DropBackpack(); } return true; } public virtual bool DoOrderStay() { if (CheckHerding()) { m_Mobile.DebugSay("Praise the shepherd!"); } else { m_Mobile.DebugSay("My master told me to stay"); } return true; } public virtual bool DoOrderStop() { if (m_Mobile.ControlMaster == null || m_Mobile.ControlMaster.Deleted) { return true; } m_Mobile.DebugSay("My master told me to stop."); //if (!DirectionLocked) // m_Mobile.Direction = m_Mobile.GetDirectionTo(m_Mobile.ControlMaster); m_Mobile.Home = m_Mobile.Location; m_Mobile.ControlTarget = null; if (Core.ML) { WalkRandomInHome(3, 2, 1); } else { m_Mobile.ControlOrder = OrderType.None; } return true; } private class TransferItem : Item { public static bool IsInCombat(BaseCreature creature) { return (creature != null && (creature.Aggressors.Count > 0 || creature.Aggressed.Count > 0)); } private readonly BaseCreature m_Creature; public TransferItem(BaseCreature creature) : base(ShrinkTable.Lookup(creature)) { m_Creature = creature; Movable = false; if (!Core.AOS) { Name = creature.Name; } else if (ItemID == ShrinkTable.DefaultItemID || creature.GetType().IsDefined(typeof(FriendlyNameAttribute), false) || creature is Reptalon) { Name = FriendlyNameAttribute.GetFriendlyNameFor(creature.GetType()).ToString(); } //(As Per OSI)No name. Normally, set by the ItemID of the Shrink Item unless we either explicitly set it with an Attribute, or, no lookup found Hue = creature.Hue & 0x0FFF; } public TransferItem(Serial serial) : base(serial) { } public override void Serialize(GenericWriter writer) { base.Serialize(writer); writer.Write(0); // version } public override void Deserialize(GenericReader reader) { base.Deserialize(reader); var version = reader.ReadInt(); Delete(); } public override void GetProperties(ObjectPropertyList list) { base.GetProperties(list); list.Add(1041603); // This item represents a pet currently in consideration for trade list.Add(1041601, m_Creature.Name); // Pet Name: ~1_val~ if (m_Creature.ControlMaster != null) { list.Add(1041602, m_Creature.ControlMaster.Name); // Owner: ~1_val~ } } public override bool AllowSecureTrade(Mobile from, Mobile to, Mobile newOwner, bool accepted) { if (!base.AllowSecureTrade(from, to, newOwner, accepted)) { return false; } if (Deleted || m_Creature == null || m_Creature.Deleted || m_Creature.ControlMaster != from || !from.CheckAlive() || !to.CheckAlive()) { return false; } if (from.Map != m_Creature.Map || !from.InRange(m_Creature, 14)) { return false; } var youngFrom = from is PlayerMobile ? ((PlayerMobile)from).Young : false; var youngTo = to is PlayerMobile ? ((PlayerMobile)to).Young : false; if (accepted && youngFrom && !youngTo) { from.SendLocalizedMessage(502051); // As a young player, you may not transfer pets to older players. } else if (accepted && !youngFrom && youngTo) { from.SendLocalizedMessage(502052); // As an older player, you may not transfer pets to young players. } else if (accepted && !m_Creature.CanBeControlledBy(to)) { var args = String.Format("{0}\t{1}\t ", to.Name, from.Name); from.SendLocalizedMessage(1043248, args); // The pet refuses to be transferred because it will not obey ~1_NAME~.~3_BLANK~ to.SendLocalizedMessage(1043249, args); // The pet will not accept you as a master because it does not trust you.~3_BLANK~ return false; } else if (accepted && !m_Creature.CanBeControlledBy(from)) { var args = String.Format("{0}\t{1}\t ", to.Name, from.Name); from.SendLocalizedMessage(1043250, args); // The pet refuses to be transferred because it will not obey you sufficiently.~3_BLANK~ to.SendLocalizedMessage(1043251, args); // The pet will not accept you as a master because it does not trust ~2_NAME~.~3_BLANK~ } else if (accepted && (to.Followers + m_Creature.ControlSlots) > to.FollowersMax) { to.SendLocalizedMessage(1049607); // You have too many followers to control that creature. return false; } else if (accepted && IsInCombat(m_Creature)) { from.SendMessage("You may not transfer a pet that has recently been in combat."); to.SendMessage("The pet may not be transfered to you because it has recently been in combat."); return false; } return true; } public override void OnSecureTrade(Mobile from, Mobile to, Mobile newOwner, bool accepted) { if (Deleted) { return; } Delete(); if (m_Creature == null || m_Creature.Deleted || m_Creature.ControlMaster != from || !from.CheckAlive() || !to.CheckAlive()) { return; } if (from.Map != m_Creature.Map || !from.InRange(m_Creature, 14)) { return; } if (accepted) { if (m_Creature.SetControlMaster(to)) { if (m_Creature.Summoned) { m_Creature.SummonMaster = to; } m_Creature.ControlTarget = to; m_Creature.ControlOrder = OrderType.Follow; m_Creature.BondingBegin = DateTime.MinValue; m_Creature.OwnerAbandonTime = DateTime.MinValue; m_Creature.IsBonded = false; m_Creature.PlaySound(m_Creature.GetIdleSound()); var args = String.Format("{0}\t{1}\t{2}", from.Name, m_Creature.Name, to.Name); from.SendLocalizedMessage(1043253, args); // You have transferred your pet to ~3_GETTER~. to.SendLocalizedMessage(1043252, args); // ~1_NAME~ has transferred the allegiance of ~2_PET_NAME~ to you. } } } } public virtual bool DoOrderTransfer() { if (m_Mobile.IsDeadPet) { return true; } var from = m_Mobile.ControlMaster; var to = m_Mobile.ControlTarget as Mobile; if (from != to && from != null && !from.Deleted && to != null && !to.Deleted && to.Player) { m_Mobile.DebugSay("Begin transfer with {0}", to.Name); var youngFrom = from is PlayerMobile ? ((PlayerMobile)from).Young : false; var youngTo = to is PlayerMobile ? ((PlayerMobile)to).Young : false; if (youngFrom && !youngTo) { from.SendLocalizedMessage(502051); // As a young player, you may not transfer pets to older players. } else if (!youngFrom && youngTo) { from.SendLocalizedMessage(502052); // As an older player, you may not transfer pets to young players. } else if (!m_Mobile.CanBeControlledBy(to)) { var args = String.Format("{0}\t{1}\t ", to.Name, from.Name); from.SendLocalizedMessage(1043248, args); // The pet refuses to be transferred because it will not obey ~1_NAME~.~3_BLANK~ to.SendLocalizedMessage(1043249, args); // The pet will not accept you as a master because it does not trust you.~3_BLANK~ } else if (!m_Mobile.CanBeControlledBy(from)) { var args = String.Format("{0}\t{1}\t ", to.Name, from.Name); from.SendLocalizedMessage(1043250, args); // The pet refuses to be transferred because it will not obey you sufficiently.~3_BLANK~ to.SendLocalizedMessage(1043251, args); // The pet will not accept you as a master because it does not trust ~2_NAME~.~3_BLANK~ } else if (TransferItem.IsInCombat(m_Mobile)) { from.SendMessage("You may not transfer a pet that has recently been in combat."); to.SendMessage("The pet may not be transfered to you because it has recently been in combat."); } else if (m_Mobile.CanTransfer(from)) { NetState fromState = from.NetState, toState = to.NetState; if (fromState != null && toState != null) { if (from.HasTrade) { from.SendLocalizedMessage(1010507); // You cannot transfer a pet with a trade pending } else if (to.HasTrade) { to.SendLocalizedMessage(1010507); // You cannot transfer a pet with a trade pending } else if (to is PlayerMobile && ((PlayerMobile)to).RefuseTrades) { from.SendLocalizedMessage(1154111, to.Name); // ~1_NAME~ is refusing all trades. } else { Container c = fromState.AddTrade(toState); c.DropItem(new TransferItem(m_Mobile)); } } } } m_Mobile.ControlTarget = null; m_Mobile.ControlOrder = OrderType.Stay; return true; } public virtual bool DoBardPacified() { if (DateTime.UtcNow < m_Mobile.BardEndTime) { m_Mobile.DebugSay("I am pacified, I wait"); m_Mobile.Combatant = null; m_Mobile.Warmode = false; } else { m_Mobile.DebugSay("I'm not pacified any longer"); m_Mobile.BardPacified = false; } return true; } public virtual bool DoBardProvoked() { if (DateTime.UtcNow >= m_Mobile.BardEndTime && (m_Mobile.BardMaster == null || m_Mobile.BardMaster.Deleted || m_Mobile.BardMaster.Map != m_Mobile.Map || m_Mobile.GetDistanceToSqrt(m_Mobile.BardMaster) > m_Mobile.RangePerception)) { m_Mobile.DebugSay("I have lost my provoker"); m_Mobile.BardProvoked = false; m_Mobile.BardMaster = null; m_Mobile.BardTarget = null; m_Mobile.Combatant = null; m_Mobile.Warmode = false; } else { if (m_Mobile.BardTarget == null || m_Mobile.BardTarget.Deleted || m_Mobile.BardTarget.Map != m_Mobile.Map || m_Mobile.GetDistanceToSqrt(m_Mobile.BardTarget) > m_Mobile.RangePerception) { m_Mobile.DebugSay("I have lost my provoke target"); m_Mobile.BardProvoked = false; m_Mobile.BardMaster = null; m_Mobile.BardTarget = null; m_Mobile.Combatant = null; m_Mobile.Warmode = false; } else { m_Mobile.Combatant = m_Mobile.BardTarget; m_Action = ActionType.Combat; m_Mobile.OnThink(); Think(); } } return true; } public virtual void WalkRandom(int iChanceToNotMove, int iChanceToDir, int iSteps) { if (m_Mobile.Deleted || m_Mobile.DisallowAllMoves) { return; } if (m_Mobile.CanFly && Utility.Random(8 * iChanceToNotMove) <= 8) { var p = Point3D.Zero; for (int i = 0; i < 10; i++) { p.X = m_Mobile.X + Utility.RandomList(-3, -2, 2, 3); p.Y = m_Mobile.Y + Utility.RandomList(-3, -2, 2, 3); p.Z = m_Mobile.Map.GetAverageZ(p.X, p.Y); if (m_Mobile.Home != Point3D.Zero && m_Mobile.RangeHome > 0 && !Utility.InRange(p, m_Mobile.Home, m_Mobile.RangeHome)) { continue; } WalkMobileRange(p, 1, m_Mobile.CanFly, 0, 1); return; } } for (var i = 0; i < iSteps; i++) { if (Utility.Random(8 * iChanceToNotMove) <= 8) { var iRndMove = Utility.Random(0, 8 + (9 * iChanceToDir)); switch (iRndMove) { case 0: DoMove(Direction.Up); break; case 1: DoMove(Direction.North); break; case 2: DoMove(Direction.Left); break; case 3: DoMove(Direction.West); break; case 5: DoMove(Direction.Down); break; case 6: DoMove(Direction.South); break; case 7: DoMove(Direction.Right); break; case 8: DoMove(Direction.East); break; default: DoMove(m_Mobile.Direction); break; } } } } public virtual double TransformMoveDelay(double delay) { delay = SpeedInfo.TransformMoveDelay(m_Mobile, delay); if (double.IsNaN(delay)) { using (var op = new StreamWriter("nan_transform.txt", true)) { op.WriteLine( "NaN in TransformMoveDelay: {0}, {1}, {2}, {3}", DateTime.UtcNow, GetType(), m_Mobile == null ? "null" : m_Mobile.GetType().ToString(), m_Mobile.StamMax); } return 1.0; } return delay; } public long NextMove { get; set; } public virtual bool CheckMove() { return (Core.TickCount - NextMove >= 0); } public virtual bool DoMove(Direction d) { return DoMove(d, false); } public virtual bool DoMove(Direction d, bool badStateOk) { var res = DoMoveImpl(d); return (res == MoveResult.Success || res == MoveResult.SuccessAutoTurn || (badStateOk && res == MoveResult.BadState)); } private static readonly Queue m_Obstacles = new Queue(); public virtual MoveResult DoMoveImpl(Direction d) { if (m_Mobile.Deleted || m_Mobile.Frozen || m_Mobile.Paralyzed || !m_Mobile.CanMove || (m_Mobile.Spell != null && m_Mobile.Spell.IsCasting && m_Mobile.FreezeOnCast) || m_Mobile.DisallowAllMoves) { return MoveResult.BadState; } if (!CheckMove()) { return MoveResult.BadState; } var delay = (int)(TransformMoveDelay(m_Mobile.CurrentSpeed) * 1000); var mounted = (m_Mobile.Mounted || m_Mobile.Flying); var running = CanRun && (mounted ? (delay < Mobile.WalkMount) : (delay < Mobile.WalkFoot)); if (running) { d |= Direction.Running; } // This makes them always move one step, never any direction changes m_Mobile.Direction = d; NextMove += delay; if (Core.TickCount - NextMove > 0) { NextMove = Core.TickCount; } m_Mobile.Pushing = false; MoveImpl.IgnoreMovableImpassables = (m_Mobile.CanMoveOverObstacles && !m_Mobile.CanDestroyObstacles); if ((m_Mobile.Direction & Direction.Mask) != (d & Direction.Mask)) { var v = m_Mobile.Move(d); MoveImpl.IgnoreMovableImpassables = false; return (v ? MoveResult.Success : MoveResult.Blocked); } if (!m_Mobile.Move(d)) { var wasPushing = m_Mobile.Pushing; var blocked = true; var canOpenDoors = m_Mobile.CanOpenDoors; var canDestroyObstacles = m_Mobile.CanDestroyObstacles; if (canOpenDoors || canDestroyObstacles) { m_Mobile.DebugSay("My movement was blocked, I will try to clear some obstacles."); var map = m_Mobile.Map; if (map != null) { int x = m_Mobile.X, y = m_Mobile.Y; Movement.Movement.Offset(d, ref x, ref y); var destroyables = 0; IPooledEnumerable eable = map.GetItemsInRange(new Point3D(x, y, m_Mobile.Location.Z), 1); foreach (Item item in eable) { if (canOpenDoors && item is BaseDoor && (item.Z + item.ItemData.Height) > m_Mobile.Z && (m_Mobile.Z + 16) > item.Z) { if (item.X != x || item.Y != y) { continue; } var door = (BaseDoor)item; if (!door.Locked || !door.UseLocks()) { m_Obstacles.Enqueue(door); } if (!canDestroyObstacles) { break; } } else if (canDestroyObstacles && item.Movable && item.ItemData.Impassable && (item.Z + item.ItemData.Height) > m_Mobile.Z && (m_Mobile.Z + 16) > item.Z) { if (!m_Mobile.InRange(item.GetWorldLocation(), 1)) { continue; } m_Obstacles.Enqueue(item); ++destroyables; } } eable.Free(); if (destroyables > 0) { Effects.PlaySound(new Point3D(x, y, m_Mobile.Z), m_Mobile.Map, 0x3B3); } if (m_Obstacles.Count > 0) { blocked = false; // retry movement } while (m_Obstacles.Count > 0) { var item = m_Obstacles.Dequeue(); if (item is BaseDoor) { m_Mobile.DebugSay("Little do they expect, I've learned how to open doors. Didn't they read the script??"); m_Mobile.DebugSay("*twist*"); ((BaseDoor)item).Use(m_Mobile); } else { m_Mobile.DebugSay("Ugabooga. I'm so big and tough I can destroy it: {0}", item.GetType().Name); if (item is Container) { var cont = (Container)item; for (var i = 0; i < cont.Items.Count; ++i) { var check = cont.Items[i]; if (check.Movable && check.ItemData.Impassable && (item.Z + check.ItemData.Height) > m_Mobile.Z) { m_Obstacles.Enqueue(check); } } cont.Destroy(); } else { item.Delete(); } } } if (!blocked) { blocked = !m_Mobile.Move(d); } } } if (blocked) { var offset = (Utility.RandomDouble() >= 0.6 ? 1 : -1); for (var i = 0; i < 2; ++i) { m_Mobile.TurnInternal(offset); if (m_Mobile.Move(m_Mobile.Direction)) { MoveImpl.IgnoreMovableImpassables = false; return MoveResult.SuccessAutoTurn; } } MoveImpl.IgnoreMovableImpassables = false; return (wasPushing ? MoveResult.BadState : MoveResult.Blocked); } MoveImpl.IgnoreMovableImpassables = false; return MoveResult.Success; } MoveImpl.IgnoreMovableImpassables = false; return MoveResult.Success; } public virtual void WalkRandomInHome(int iChanceToNotMove, int iChanceToDir, int iSteps) { if (m_Mobile.Deleted || m_Mobile.DisallowAllMoves) { return; } if (m_Mobile.Home == Point3D.Zero) { if (m_Mobile.Spawner is SpawnEntry) { Region region = ((SpawnEntry)m_Mobile.Spawner).Region; if (m_Mobile.Region.AcceptsSpawnsFrom(region)) { m_Mobile.WalkRegion = region; WalkRandom(iChanceToNotMove, iChanceToDir, iSteps); m_Mobile.WalkRegion = null; } else { if (region.GoLocation != Point3D.Zero && Utility.Random(10) > 5) { DoMove(m_Mobile.GetDirectionTo(region.GoLocation)); } else { WalkRandom(iChanceToNotMove, iChanceToDir, 1); } } } else { WalkRandom(iChanceToNotMove, iChanceToDir, iSteps); } } else { for (var i = 0; i < iSteps; i++) { if (m_Mobile.RangeHome != 0) { var iCurrDist = (int)m_Mobile.GetDistanceToSqrt(m_Mobile.Home); if (iCurrDist < m_Mobile.RangeHome * 2 / 3) { WalkRandom(iChanceToNotMove, iChanceToDir, 1); } else if (iCurrDist > m_Mobile.RangeHome) { DoMove(m_Mobile.GetDirectionTo(m_Mobile.Home)); } else { if (Utility.Random(10) > 5) { DoMove(m_Mobile.GetDirectionTo(m_Mobile.Home)); } else { WalkRandom(iChanceToNotMove, iChanceToDir, 1); } } } else { if (m_Mobile.Location != m_Mobile.Home) { DoMove(m_Mobile.GetDirectionTo(m_Mobile.Home)); } } } } } public virtual bool CheckFlee() { if (m_Mobile.CheckFlee()) { var combatant = m_Mobile.Combatant as Mobile; if (combatant == null) { WalkRandom(1, 2, 1); } else { var d = combatant.GetDirectionTo(m_Mobile); d = (Direction)((int)d + Utility.RandomMinMax(-1, +1)); m_Mobile.Direction = d; m_Mobile.Move(d); } return true; } return false; } protected PathFollower m_Path; public virtual void OnTeleported() { if (m_Path != null) { m_Mobile.DebugSay("Teleported; repathing"); m_Path.ForceRepath(); } } public virtual bool MoveTo(IPoint3D p, bool run, int range) { if (m_Mobile.Deleted || m_Mobile.DisallowAllMoves || p == null || (p is IDamageable && ((IDamageable)p).Deleted)) { return false; } if (!m_Mobile.Controlled && m_Mobile.ForceStayHome) { var rangeHome = Math.Min(10, m_Mobile.RangeHome); if (!Utility.InRange(new Point3D(p), m_Mobile.Home, rangeHome) || 0.025 < Utility.RandomDouble()) { return false; } } if (m_Mobile.InRange(p, range)) { m_Path = null; return true; } if (m_Path != null && m_Path.Goal == p) { if (m_Path.Follow(run, 1)) { m_Path = null; return true; } } else if (!DoMove(m_Mobile.GetDirectionTo(p), true)) { m_Path = new PathFollower(m_Mobile, p); m_Path.Mover = DoMoveImpl; if (m_Path.Follow(run, 1)) { m_Path = null; return true; } } else { m_Path = null; return true; } return false; } /* * Walk at range distance from mobile * * iSteps : Number of steps * bRun : Do we run * iWantDistMin : The minimum distance we want to be * iWantDistMax : The maximum distance we want to be * */ public virtual bool WalkMobileRange(IPoint3D p, int iSteps, bool bRun, int iWantDistMin, int iWantDistMax) { if (m_Mobile.Deleted || m_Mobile.DisallowAllMoves) { return false; } if (p != null) { for (var i = 0; i < iSteps; i++) { // Get the curent distance var iCurrDist = (int)m_Mobile.GetDistanceToSqrt(p); if (iCurrDist < iWantDistMin || iCurrDist > iWantDistMax) { var needCloser = (iCurrDist > iWantDistMax); var needFurther = !needCloser; if (needCloser && m_Path != null && m_Path.Goal == p) { if (m_Path.Follow(bRun, 1)) { m_Path = null; } } else { Direction dirTo; if (iCurrDist > iWantDistMax) { dirTo = m_Mobile.GetDirectionTo(p); } else { dirTo = Utility.GetDirection(p, m_Mobile); } // Add the run flag if (bRun) { dirTo = dirTo | Direction.Running; } if (!DoMove(dirTo, true) && needCloser) { m_Path = new PathFollower(m_Mobile, p); m_Path.Mover = DoMoveImpl; if (m_Path.Follow(bRun, 1)) { m_Path = null; } } else { m_Path = null; } } } else { return true; } } // Get the curent distance var iNewDist = (int)m_Mobile.GetDistanceToSqrt(p); if (iNewDist >= iWantDistMin && iNewDist <= iWantDistMax) { return true; } return false; } return false; } /* * Here we check to acquire a target from our surronding * * iRange : The range * acqType : A type of acquire we want (closest, strongest, etc) * bPlayerOnly : Don't bother with other creatures or NPCs, want a player * bFacFriend : Check people in my faction * bFacFoe : Check people in other factions * * Note: Never use a differing acqType for enemy targeting! It only checks using creature's fightmode! */ public virtual bool AcquireFocusMob(int iRange, FightMode acqType, bool bPlayerOnly, bool bFacFriend, bool bFacFoe) { if (m_Mobile.Deleted) { return false; } if (m_Mobile.BardProvoked) { if (m_Mobile.BardTarget == null || m_Mobile.BardTarget.Deleted) { m_Mobile.FocusMob = null; return false; } m_Mobile.FocusMob = m_Mobile.BardTarget; return (m_Mobile.FocusMob != null); } if (m_Mobile.Controlled) { if (m_Mobile.ControlTarget == null || m_Mobile.ControlTarget.Deleted || (m_Mobile.ControlTarget is Mobile && ((Mobile)m_Mobile.ControlTarget).Hidden) || !m_Mobile.ControlTarget.Alive || (m_Mobile.ControlTarget is Mobile && ((Mobile)m_Mobile.ControlTarget).IsDeadBondedPet) || !m_Mobile.InRange(m_Mobile.ControlTarget, m_Mobile.RangePerception * 2)) { if (m_Mobile.ControlTarget != null && m_Mobile.ControlTarget != m_Mobile.ControlMaster) { m_Mobile.ControlTarget = null; } m_Mobile.FocusMob = null; return false; } m_Mobile.FocusMob = m_Mobile.ControlTarget; return (m_Mobile.FocusMob != null); } if (m_Mobile.ConstantFocus != null) { m_Mobile.DebugSay("Acquired my constant focus"); m_Mobile.FocusMob = m_Mobile.ConstantFocus; return true; } if (acqType == FightMode.None) { m_Mobile.FocusMob = null; return false; } if (acqType == FightMode.Aggressor && m_Mobile.Aggressors.Count == 0 && m_Mobile.Aggressed.Count == 0 && m_Mobile.FactionAllegiance == null && m_Mobile.EthicAllegiance == null) { if ((Core.TOL && m_Mobile.Tribe == TribeType.None) || (!Core.TOL && m_Mobile.OppositionGroup == null)) { if ((XmlIsEnemy)XmlAttach.FindAttachment(m_Mobile, typeof(XmlIsEnemy)) == null) { m_Mobile.FocusMob = null; return false; } } } if (m_Mobile.NextReacquireTime > Core.TickCount) { m_Mobile.FocusMob = null; return false; } m_Mobile.NextReacquireTime = Core.TickCount + (int)m_Mobile.ReacquireDelay.TotalMilliseconds; m_Mobile.DebugSay("Acquiring..."); var map = m_Mobile.Map; if (map != null) { Mobile newFocusMob = null; var val = double.MinValue; double theirVal; var eable = map.GetMobilesInRange(m_Mobile.Location, iRange); foreach (var m in eable) { if (m.Deleted || m.Blessed) { continue; } // Let's not target ourself... if (m == m_Mobile) { continue; } // Dead targets are invalid. if (!m.Alive || m.IsDeadBondedPet) { continue; } // Staff members cannot be targeted. if (m.IsStaff()) { continue; } // Does it have to be a player? if (bPlayerOnly && !m.Player) { continue; } // Can't acquire a target we can't see. if (!m_Mobile.CanSee(m) || !m_Mobile.InLOS(m)) { continue; } // If we only want faction friends if (bFacFriend && !bFacFoe) { // Ignore anyone who's not a friend if (!m_Mobile.IsFriend(m)) { continue; } } // Don't ignore friends we want to and can help else if (!bFacFriend || !m_Mobile.IsFriend(m)) { // Let's not target a familiar... if (m is BaseFamiliar) { continue; } if (m_Mobile.Summoned && m_Mobile.SummonMaster != null) { // If this is a summon, it can't target its controller. if (m == m_Mobile.SummonMaster) continue; // It also must abide by harmful spell rules if the master is a player. if (m_Mobile.SummonMaster is PlayerMobile && !SpellHelper.ValidIndirectTarget(m_Mobile.SummonMaster, m)) { continue; } // Players animated creatures cannot attack other players directly. if (m is PlayerMobile && m_Mobile.IsAnimatedDead && m_Mobile.SummonMaster is PlayerMobile) continue; } // Ignore anyone we can't hurt if (!m_Mobile.CanBeHarmful(m, false)) { continue; } // Don't ignore hostile mobiles if (!IsHostile(m, acqType)) { // Ignore anyone if we don't want enemies if (!bFacFoe) { continue; } // Ignore any non-enemy if (!m_Mobile.IsEnemy(m)) { continue; } } } theirVal = m_Mobile.GetFightModeRanking(m, acqType, bPlayerOnly); if (theirVal > val || (theirVal == val && newFocusMob.Player && !m.Player)) { newFocusMob = m; val = theirVal; } } eable.Free(); m_Mobile.FocusMob = newFocusMob; } return (m_Mobile.FocusMob != null); } public virtual bool IsHostile(Mobile from, FightMode mode) { if (m_Mobile.Combatant == from || from.Combatant == m_Mobile) { return true; } return false; } public virtual void Deactivate() { if (m_Mobile.PlayerRangeSensitive) { m_Timer.Stop(); var se = m_Mobile.Spawner as SpawnEntry; if (se != null && se.ReturnOnDeactivate && !m_Mobile.Controlled) { if (se.HomeLocation == Point3D.Zero) { if (!m_Mobile.Region.AcceptsSpawnsFrom(se.Region)) { Timer.DelayCall(TimeSpan.Zero, ReturnToHome); } } else if (!m_Mobile.InRange(se.HomeLocation, se.HomeRange)) { Timer.DelayCall(TimeSpan.Zero, ReturnToHome); } } } } private void ReturnToHome() { var se = m_Mobile.Spawner as SpawnEntry; if (se != null) { var loc = se.RandomSpawnLocation(16, !m_Mobile.CantWalk, m_Mobile.CanSwim); if (loc != Point3D.Zero) { m_Mobile.MoveToWorld(loc, se.Region.Map); } } } public virtual void Activate() { if (!m_Timer.Running) { m_Timer.Delay = TimeSpan.Zero; m_Timer.Start(); } } /* * The mobile changed it speed, we must ajust the timer */ public virtual void OnCurrentSpeedChanged() { m_Timer.Stop(); m_Timer.Delay = TimeSpan.FromSeconds(Utility.RandomDouble()); m_Timer.Interval = TimeSpan.FromSeconds(Math.Max(0.0, m_Mobile.CurrentSpeed)); m_Timer.Start(); } public virtual void AfterThink() { } /* * The Timer object */ private class AITimer : Timer { private readonly BaseAI m_Owner; public AITimer(BaseAI owner) : base( TimeSpan.FromSeconds(Utility.RandomDouble()), TimeSpan.FromSeconds(Math.Max(0.0, owner.m_Mobile.CurrentSpeed))) { m_Owner = owner; Priority = TimerPriority.FiftyMS; } protected override void OnTick() { if (m_Owner.m_Mobile.Deleted) { Stop(); return; } if (m_Owner.m_Mobile.Map == null || m_Owner.m_Mobile.Map == Map.Internal) { m_Owner.Deactivate(); return; } if (m_Owner.m_Mobile.PlayerRangeSensitive) //have to check this in the timer.... { var sect = m_Owner.m_Mobile.Map.GetSector(m_Owner.m_Mobile); if (!sect.Active) { m_Owner.Deactivate(); return; } } m_Owner.m_Mobile.OnThink(); if (m_Owner.m_Mobile.Deleted) { Stop(); return; } if (m_Owner.m_Mobile.Map == null || m_Owner.m_Mobile.Map == Map.Internal) { m_Owner.Deactivate(); return; } if (m_Owner.m_Mobile.BardPacified) { m_Owner.DoBardPacified(); } else if (m_Owner.m_Mobile.BardProvoked) { m_Owner.DoBardProvoked(); } else { if (!m_Owner.m_Mobile.Controlled) { if (!m_Owner.Think()) { Stop(); return; } } else { if (!m_Owner.Obey()) { Stop(); return; } } } m_Owner.AfterThink(); } } } }