using System; using System.Collections.Generic; using System.Linq; using Server.ContextMenus; using Server.Mobiles; using Server.Services.Virtues; namespace Server.Engines.Quests { public class BaseEscort : MondainQuester { private static readonly TimeSpan m_EscortDelay = TimeSpan.FromMinutes(5.0); private static readonly Dictionary m_EscortTable = new Dictionary(); private Timer m_DeleteTimer; private bool m_Checked; public BaseQuest Quest { get; set; } public DateTime LastSeenEscorter { get; set; } public BaseEscort() : base() { AI = AIType.AI_Melee; FightMode = FightMode.Aggressor; RangePerception = 22; RangeFight = 1; ActiveSpeed = 0.2; PassiveSpeed = 1.0; ControlSlots = 0; } public BaseEscort(Serial serial) : base(serial) { } public override bool InitialInnocent { get { return true; } } public override bool IsInvulnerable { get { return false; } } public override bool Commandable { get { return false; } } public override Type[] Quests { get { return null; } } public override bool CanAutoStable { get { return false; } } public override bool CanDetectHidden { get { return false; } } public override void OnTalk(PlayerMobile player) { if (AcceptEscorter(player)) base.OnTalk(player); } public override bool CanBeRenamedBy(Mobile from) { return (from.AccessLevel >= AccessLevel.GameMaster); } public override void AddCustomContextEntries(Mobile from, List list) { if (from.Alive && from == ControlMaster) list.Add(new AbandonEscortEntry(this)); base.AddCustomContextEntries(from, list); } public override void OnAfterDelete() { if (Quest != null) { Quest.RemoveQuest(); if (Quest.Owner != null) m_EscortTable.Remove(Quest.Owner); } base.OnAfterDelete(); } public override void OnThink() { base.OnThink(); CheckAtDestination(); } public override bool CanBeDamaged() { return true; } public override void InitBody() { SetStr(90, 100); SetDex(90, 100); SetInt(15, 25); Hue = Utility.RandomSkinHue(); Female = Utility.RandomBool(); Name = NameList.RandomName(Female ? "female" : "male"); Race = Race.Human; Utility.AssignRandomHair(this); Utility.AssignRandomFacialHair(this); } public override void Serialize(GenericWriter writer) { base.Serialize(writer); writer.Write((int)0); // version writer.Write(m_DeleteTimer != null); if (m_DeleteTimer != null) writer.WriteDeltaTime(m_DeleteTimer.Next); } public override void Deserialize(GenericReader reader) { base.Deserialize(reader); int version = reader.ReadInt(); if (reader.ReadBool()) { DateTime deleteTime = reader.ReadDeltaTime(); m_DeleteTimer = Timer.DelayCall(deleteTime - DateTime.UtcNow, new TimerCallback(Delete)); } } public void AddHash(PlayerMobile player) { m_EscortTable[player] = this; } public virtual void StartFollow() { StartFollow(ControlMaster); } public virtual void StartFollow(Mobile escorter) { ActiveSpeed = 0.1; PassiveSpeed = 0.2; ControlOrder = OrderType.Follow; ControlTarget = escorter; CurrentSpeed = 0.1; } public virtual void StopFollow() { ActiveSpeed = 0.2; PassiveSpeed = 1.0; ControlOrder = OrderType.None; ControlTarget = null; CurrentSpeed = 1.0; SetControlMaster(null); } public virtual void BeginDelete(Mobile m) { StopFollow(); if (m != null) m_EscortTable.Remove(m); m_DeleteTimer = Timer.DelayCall(TimeSpan.FromSeconds(45.0), new TimerCallback(Delete)); } public virtual bool AcceptEscorter(Mobile m) { if (!m.Alive) { return false; } else if (m_DeleteTimer != null) { Say(500898); // I am sorry, but I do not wish to go anywhere. return false; } else if (Controlled) { if (m == ControlMaster) m.SendGump(new MondainQuestGump(Quest, MondainQuestGump.Section.InProgress, false)); else Say(500897); // I am already being led! return false; } else if (!m.InRange(Location, 5)) { Say(500348); // I am too far away to do that. return false; } else if (m_EscortTable.ContainsKey(m)) { Say(500896); // I see you already have an escort. return false; } else if (m is PlayerMobile && (((PlayerMobile)m).LastEscortTime + m_EscortDelay) >= DateTime.UtcNow) { int minutes = (int)Math.Ceiling(((((PlayerMobile)m).LastEscortTime + m_EscortDelay) - DateTime.UtcNow).TotalMinutes); Say("You must rest {0} minute{1} before we set out on this journey.", minutes, minutes == 1 ? "" : "s"); return false; } return true; } public virtual EscortObjective GetObjective() { if (Quest != null) { for (int i = 0; i < Quest.Objectives.Count; i++) { EscortObjective escort = Quest.Objectives[i] as EscortObjective; if (escort != null && !escort.Completed && !escort.Failed) return escort; } } return null; } public virtual Mobile GetEscorter() { Mobile master = ControlMaster; if (master == null || !Controlled) { return master; } else if (master.Map != Map || !master.InRange(Location, 30) || !master.Alive) { TimeSpan lastSeenDelay = DateTime.UtcNow - LastSeenEscorter; if (lastSeenDelay >= TimeSpan.FromMinutes(2.0)) { EscortObjective escort = GetObjective(); if (escort != null) { master.SendLocalizedMessage(1071194); // You have failed your escort quest… master.PlaySound(0x5B3); escort.Fail(); } master.SendLocalizedMessage(1042473); // You have lost the person you were escorting. Say(1005653); // Hmmm. I seem to have lost my master. StopFollow(); m_EscortTable.Remove(master); m_DeleteTimer = Timer.DelayCall(TimeSpan.FromSeconds(5.0), new TimerCallback(Delete)); return null; } else { ControlOrder = OrderType.Stay; } } else { if (ControlOrder != OrderType.Follow) StartFollow(master); LastSeenEscorter = DateTime.UtcNow; } return master; } public virtual Region GetDestination() { return null; } public virtual bool CheckAtDestination() { if (Quest != null) { EscortObjective escort = GetObjective(); if (escort == null) return false; Mobile escorter = GetEscorter(); if (escorter == null) return false; if (escort.Region != null && escort.Region.Contains(Location)) { Say(1042809, escorter.Name); // We have arrived! I thank thee, ~1_PLAYER_NAME~! I have no further need of thy services. Here is thy pay. escort.Complete(); if (Quest.Completed) { escorter.SendLocalizedMessage(1046258, null, 0x23); // Your quest is complete. if (QuestHelper.AnyRewards(Quest)) escorter.SendGump(new MondainQuestGump(Quest, MondainQuestGump.Section.Rewards, false, true)); else Quest.GiveRewards(); escorter.PlaySound(Quest.CompleteSound); StopFollow(); m_EscortTable.Remove(escorter); m_DeleteTimer = Timer.DelayCall(TimeSpan.FromSeconds(5.0), new TimerCallback(Delete)); // fame Misc.Titles.AwardFame(escorter, escort.Fame, true); // compassion bool gainedPath = false; PlayerMobile pm = escorter as PlayerMobile; if (pm != null) { if (pm.CompassionGains > 0 && DateTime.UtcNow > pm.NextCompassionDay) { pm.NextCompassionDay = DateTime.MinValue; pm.CompassionGains = 0; } if (pm.CompassionGains >= 5) // have already gained 5 times in one day, can gain no more { pm.SendLocalizedMessage(1053004); // You must wait about a day before you can gain in compassion again. } else if (VirtueHelper.Award(pm, VirtueName.Compassion, escort.Compassion, ref gainedPath)) { pm.SendLocalizedMessage(1074949, null, 0x2A); // You have demonstrated your compassion! Your kind actions have been noted. if (gainedPath) pm.SendLocalizedMessage(1053005); // You have achieved a path in compassion! else pm.SendLocalizedMessage(1053002); // You have gained in compassion. pm.NextCompassionDay = DateTime.UtcNow + TimeSpan.FromDays(1.0); // in one day CompassionGains gets reset to 0 ++pm.CompassionGains; } else { pm.SendLocalizedMessage(1053003); // You have achieved the highest path of compassion and can no longer gain any further. } } } else { escorter.PlaySound(Quest.UpdateSound); } return true; } } else if (!m_Checked) { Region region = GetDestination(); if (region != null && region.Contains(Location)) { m_DeleteTimer = Timer.DelayCall(TimeSpan.FromSeconds(5.0), new TimerCallback(Delete)); m_Checked = true; } } return false; } private class AbandonEscortEntry : ContextMenuEntry { private readonly BaseEscort m_Mobile; public AbandonEscortEntry(BaseEscort m) : base(6102, 3) { m_Mobile = m; } public override void OnClick() { Owner.From.SendLocalizedMessage(1071194); // You have failed your escort quest… Owner.From.PlaySound(0x5B3); m_Mobile.Delete(); } } public static void DeleteEscort(Mobile owner) { PlayerMobile pm = owner as PlayerMobile; foreach (var escortquest in pm.Quests.Where(x => x.Quester is BaseEscort)) { BaseEscort escort = (BaseEscort)escortquest.Quester; Timer.DelayCall(TimeSpan.FromSeconds(3), new TimerCallback( delegate { escort.Say(500901); // Ack! My escort has come to haunt me! owner.SendLocalizedMessage(1071194); // You have failed your escort quest… owner.PlaySound(0x5B3); escort.Delete(); })); } } } }