private static void OnMarkQuestItem(PlayerMobile pm, Item item, Type type) { MLQuestContext context = GetContext(pm); if (context == null) { return; } List <MLQuestInstance> instances = context.QuestInstances; // We don't foreach because CheckComplete() can potentially modify the MLQuests list for (int i = instances.Count - 1; i >= 0; --i) { MLQuestInstance instance = instances[i]; if (instance.ClaimReward) { continue; } foreach (BaseObjectiveInstance objective in instance.Objectives) { if (!objective.Expired && objective.AllowsQuestItem(item, type)) { objective.CheckComplete(); // yes, this can happen multiple times (for multiple quests) break; } } } }
public static void HandleSkillGain(PlayerMobile pm, SkillName skill) { MLQuestContext context = GetContext(pm); if (context == null) { return; } List <MLQuestInstance> instances = context.QuestInstances; for (int i = instances.Count - 1; i >= 0; --i) { MLQuestInstance instance = instances[i]; if (instance.ClaimReward) { continue; } foreach (BaseObjectiveInstance objective in instance.Objectives) { if (!objective.Expired && objective is GainSkillObjectiveInstance objectiveInstance && objectiveInstance.Handles(skill)) { objectiveInstance.CheckComplete(); break; } } } }
public static void HandleDeath(PlayerMobile pm) { MLQuestContext context = GetContext(pm); if (context != null) { context.HandleDeath(); } }
public static MLQuestContext GetOrCreateContext(PlayerMobile pm) { if (!Contexts.TryGetValue(pm, out MLQuestContext context)) { Contexts[pm] = context = new MLQuestContext(pm); } return(context); }
public static void HandleDeletion(PlayerMobile pm) { MLQuestContext context = GetContext(pm); if (context != null) { context.HandleDeletion(); Contexts.Remove(pm); } }
private static bool FindQuest( IQuestGiver quester, PlayerMobile pm, MLQuestContext context, out MLQuest quest, out MLQuestInstance entry ) { quest = null; entry = null; var quests = quester.MLQuests; var questerType = quester.GetType(); // 1. Check quests in progress with this NPC (overriding deliveries is intended) if (context != null) { foreach (var questEntry in quests) { var instance = context.FindInstance(questEntry); if (instance != null && (instance.Quester == quester || !questEntry.IsEscort && instance.QuesterType == questerType)) { entry = instance; quest = questEntry; return(true); } } } // 2. Check deliveries (overriding chain offers is intended) if ((entry = HandleDelivery(pm, quester, questerType)) != null) { quest = entry.Quest; return(true); } // 3. Check chain quest offers if (context != null) { foreach (var questEntry in quests) { if (questEntry.IsChainTriggered && context.ChainOffers.Contains(questEntry)) { quest = questEntry; return(true); } } } // 4. Random quest quest = RandomStarterQuest(quester, pm, context); return(quest != null); }
public static MLQuestInstance HandleDelivery(PlayerMobile pm, IQuestGiver quester, Type questerType) { MLQuestContext context = GetContext(pm); if (context == null) { return(null); } List <MLQuestInstance> instances = context.QuestInstances; MLQuestInstance deliverInstance = null; for (int i = instances.Count - 1; i >= 0; --i) { MLQuestInstance instance = instances[i]; // Do NOT skip quests on ClaimReward, because the quester still needs the quest ref! //if ( instance.ClaimReward ) // continue; foreach (BaseObjectiveInstance objective in instance.Objectives) { // Note: On OSI, expired deliveries can still be completed. Bug? if (!objective.Expired && objective is DeliverObjectiveInstance) { DeliverObjectiveInstance deliver = (DeliverObjectiveInstance)objective; if (deliver.IsDestination(quester, questerType)) { if (!deliver.HasCompleted) // objective completes only once { deliver.HasCompleted = true; deliver.CheckComplete(); // The quest is continued with this NPC (important for chains) instance.Quester = quester; } if (deliverInstance == null) { deliverInstance = instance; } break; // don't return, we may have to complete more deliveries } } } } return(deliverInstance); }
public static bool CanMarkQuestItem(PlayerMobile pm, Item item, Type type) { MLQuestContext context = GetContext(pm); if (context != null) { foreach (MLQuestInstance quest in context.QuestInstances) { if (!quest.ClaimReward && quest.AllowsQuestItem(item, type)) { return(true); } } } return(false); }
public static void HandleKill(PlayerMobile pm, Mobile mob) { MLQuestContext context = GetContext(pm); if (context == null) { return; } List <MLQuestInstance> instances = context.QuestInstances; Type type = null; for (int i = instances.Count - 1; i >= 0; --i) { MLQuestInstance instance = instances[i]; if (instance.ClaimReward) { continue; } /* A kill only counts for a single objective within a quest, * but it can count for multiple quests. This is something not * currently observable on OSI, so it is assumed behavior. */ foreach (BaseObjectiveInstance objective in instance.Objectives) { if (!objective.Expired && objective is KillObjectiveInstance) { KillObjectiveInstance kill = (KillObjectiveInstance)objective; if (type == null) { type = mob.GetType(); } if (kill.AddKill(mob, type)) { kill.CheckComplete(); break; } } } } }
public static void OnDoubleClick(IQuestGiver quester, PlayerMobile pm) { if (quester.Deleted || !pm.Alive) { return; } MLQuestContext context = GetContext(pm); MLQuest quest; MLQuestInstance entry; if (!FindQuest(quester, pm, context, out quest, out entry)) { Tell(quester, pm, 1080107); // I'm sorry, I have nothing for you at this time. return; } if (entry != null) { TurnToFace(quester, pm); if (entry.Failed) { return; // Note: OSI sends no gump at all for failed quests, they have to be cancelled in the quest overview } else if (entry.ClaimReward) { entry.SendRewardOffer(); } else if (entry.IsCompleted()) { entry.SendReportBackGump(); } else { entry.SendProgressGump(); } } else if (quest.CanOffer(quester, pm, context, true)) { TurnToFace(quester, pm); quest.SendOffer(quester, pm); } }
public override void Deserialize( GenericReader reader ) { base.Deserialize( reader ); int version = reader.ReadInt(); int contexts = reader.ReadInt(); for ( int i = 0; i < contexts; ++i ) { MLQuestContext context = new MLQuestContext( reader, version ); if ( context.Owner != null ) MLQuestSystem.Contexts[context.Owner] = context; } int quests = reader.ReadInt(); for ( int i = 0; i < quests; ++i ) MLQuest.Deserialize( reader, version ); }
public override void Deserialize(GenericReader reader) { base.Deserialize(reader); int version = reader.ReadInt(); int contexts = reader.ReadInt(); for (int i = 0; i < contexts; ++i) { MLQuestContext context = new MLQuestContext(reader, version); if (context.Owner != null) { MLQuestSystem.Contexts[context.Owner] = context; } } int quests = reader.ReadInt(); for (int i = 0; i < quests; ++i) { MLQuest.Deserialize(reader, version); } }
public static MLQuest RandomStarterQuest( IQuestGiver quester, PlayerMobile pm, MLQuestContext context ) { List<MLQuest> quests = quester.MLQuests; if ( quests.Count == 0 ) return null; m_EligiblePool.Clear(); MLQuest fallback = null; foreach ( MLQuest quest in quests ) { if ( quest.IsChainTriggered || ( context != null && context.IsDoingQuest( quest ) ) ) continue; /* * Save first quest that reaches the CanOffer call. * If no quests are valid at all, return this quest for displaying the CanOffer error message. */ if ( fallback == null ) fallback = quest; if ( quest.CanOffer( quester, pm, context, false ) ) m_EligiblePool.Add( quest ); } if ( m_EligiblePool.Count == 0 ) return fallback; return m_EligiblePool[Utility.Random( m_EligiblePool.Count )]; }
public static MLQuestContext GetOrCreateContext( PlayerMobile pm ) { MLQuestContext context; if ( !m_Contexts.TryGetValue( pm, out context ) ) m_Contexts[pm] = context = new MLQuestContext( pm ); return context; }
public void ClaimRewards() { if (m_Quest == null || m_Player == null || m_Player.Deleted || !ClaimReward || Removed) { return; } List <Item> rewards = new List <Item>(); foreach (BaseReward reward in m_Quest.Rewards) { reward.AddRewardItems(m_Player, rewards); } if (rewards.Count != 0) { // On OSI a more naive method of checking is used. // For containers, only the actual container item counts. bool canFit = true; foreach (Item rewardItem in rewards) { if (!m_Player.AddToBackpack(rewardItem)) { canFit = false; break; } } if (!canFit) { foreach (Item rewardItem in rewards) { rewardItem.Delete(); } m_Player.SendLocalizedMessage(1078524); // Your backpack is full. You cannot complete the quest and receive your reward. return; } foreach (Item rewardItem in rewards) { string rewardName = (rewardItem.Name != null) ? rewardItem.Name : String.Concat("#", rewardItem.LabelNumber); if (rewardItem.Stackable) { m_Player.SendLocalizedMessage(1115917, String.Concat(rewardItem.Amount, "\t", rewardName)); // You receive a reward: ~1_QUANTITY~ ~2_ITEM~ } else { m_Player.SendLocalizedMessage(1074360, rewardName); // You receive a reward: ~1_REWARD~ } } } foreach (BaseObjectiveInstance objective in m_ObjectiveInstances) { objective.OnRewardClaimed(); } m_Quest.OnRewardClaimed(this); MLQuestContext context = PlayerContext; if (m_Quest.RecordCompletion && !m_Quest.HasRestartDelay) // Quests with restart delays are logged earlier as per OSI { context.SetDoneQuest(m_Quest); } if (m_Quest.IsChainTriggered) { context.ChainOffers.Remove(m_Quest); } Type nextQuestType = m_Quest.NextQuest; if (nextQuestType != null) { MLQuest nextQuest = MLQuestSystem.FindQuest(nextQuestType); if (nextQuest != null && !context.ChainOffers.Contains(nextQuest)) { context.ChainOffers.Add(nextQuest); } } Remove(); }
public static void HandleDeath(PlayerMobile pm) { MLQuestContext context = GetContext(pm); context?.HandleDeath(); }
public virtual bool CanOffer(IQuestGiver quester, PlayerMobile pm, MLQuestContext context, bool message) { if (!m_Activated || quester.Deleted) { return(false); } if (context != null) { if (context.IsFull) { if (message) { MLQuestSystem.Tell(quester, pm, 1080107); // I'm sorry, I have nothing for you at this time. } return(false); } MLQuest checkQuest = this; while (checkQuest != null) { DateTime nextAvailable; if (context.HasDoneQuest(checkQuest, out nextAvailable)) { if (checkQuest.OneTimeOnly) { if (message) { MLQuestSystem.Tell(quester, pm, 1075454); // I cannot offer you the quest again. } return(false); } else if (nextAvailable > DateTime.UtcNow) { if (message) { MLQuestSystem.Tell(quester, pm, 1075575); // I'm sorry, but I don't have anything else for you right now. Could you check back with me in a few minutes? } return(false); } } if (checkQuest.NextQuest == null) { break; } checkQuest = MLQuestSystem.FindQuest(checkQuest.NextQuest); } } foreach (BaseObjective obj in m_Objectives) { if (!obj.CanOffer(quester, pm, message)) { return(false); } } return(true); }
public static MLQuest RandomStarterQuest(IQuestGiver quester, PlayerMobile pm, MLQuestContext context) { List <MLQuest> quests = quester.MLQuests; if (quests.Count == 0) { return(null); } m_EligiblePool.Clear(); MLQuest fallback = null; foreach (MLQuest quest in quests) { if (quest.IsChainTriggered || context?.IsDoingQuest(quest) == true) { continue; } /* * Save first quest that reaches the CanOffer call. * If no quests are valid at all, return this quest for displaying the CanOffer error message. */ fallback ??= quest; if (quest.CanOffer(quester, pm, context, false)) { m_EligiblePool.Add(quest); } } return(m_EligiblePool.Count == 0 ? fallback : m_EligiblePool[Utility.Random(m_EligiblePool.Count)]); }
private static bool FindQuest( IQuestGiver quester, PlayerMobile pm, MLQuestContext context, out MLQuest quest, out MLQuestInstance entry ) { quest = null; entry = null; List<MLQuest> quests = quester.MLQuests; Type questerType = quester.GetType(); // 1. Check quests in progress with this NPC (overriding deliveries is intended) if ( context != null ) { foreach ( MLQuest questEntry in quests ) { MLQuestInstance instance = context.FindInstance( questEntry ); if ( instance != null && ( instance.Quester == quester || ( !questEntry.IsEscort && instance.QuesterType == questerType ) ) ) { entry = instance; quest = questEntry; return true; } } } // 2. Check deliveries (overriding chain offers is intended) if ( ( entry = HandleDelivery( pm, quester, questerType ) ) != null ) { quest = entry.Quest; return true; } // 3. Check chain quest offers if ( context != null ) { foreach ( MLQuest questEntry in quests ) { if ( questEntry.IsChainTriggered && context.ChainOffers.Contains( questEntry ) ) { quest = questEntry; return true; } } } // 4. Random quest quest = RandomStarterQuest( quester, pm, context ); return ( quest != null ); }