public JadeForestTakeNoPrisoners(Dictionary <string, string> args) : base(args) { QBCLog.BehaviorLoggingContext = this; try { VariantQuestIds = VariantQuestIds.Any() ? VariantQuestIds : new HashSet <int> { 29727 }; QuestRequirementComplete = GetAttributeAsNullable <QuestCompleteRequirement>("QuestCompleteRequirement", false, null, null) ?? QuestCompleteRequirement.NotComplete; QuestRequirementInLog = GetAttributeAsNullable <QuestInLogRequirement>("QuestInLogRequirement", false, null, null) ?? QuestInLogRequirement.InLog; } catch (Exception except) { // Maintenance problems occur for a number of reasons. The primary two are... // * Changes were made to the behavior, and boundary conditions weren't properly tested. // * The Honorbuddy core was changed, and the behavior wasn't adjusted for the new changes. // In any case, we pinpoint the source of the problem area here, and hopefully it // can be quickly resolved. QBCLog.Exception(except); IsAttributeProblem = true; } }
/// <summary> /// Searches player's quest log and list of completed quests for a quest specified by /// QuestId and <see cref="VariantQuestIds"/>, /// and returns the id if found; otherwise returns first quest provided by QuestId/VariantQuestIds /// </summary> /// <returns></returns> protected int GetQuestId() { var questInLog = GetQuestInLog(); if (questInLog != null) { return((int)questInLog.Id); } var completedQuests = new HashSet <uint>(StyxWoW.Me.QuestLog.GetCompletedQuests()); var completedQuestId = VariantQuestIds .FirstOrDefault(id => completedQuests.Contains((uint)id)); if (completedQuestId != 0) { return(completedQuestId); } return(VariantQuestIds.Any() ? VariantQuestIds.Min() : 0); }
protected override void EvaluateUsage_SemanticCoherency(XElement xElement) { UsageCheck_SemanticCoherency(xElement, ((UseWhenMeHasAuraId <= 0) && (UseWhenMeMissingAuraId <= 0) && (UseWhenMobCastingSpellId <= 0) && (UseWhenMobHasAuraId <= 0) && (UseWhenMobMissingAuraId <= 0) && (UseWhenMobHasHealthPercent <= 0)), context => "One or more of the following attributes must be specified:\n" + "UseWhenMeHasAuraId, UseWhenMeMissingAuraId, UseWhenMobCastingSpellId," + " UseWhenMobHasAuraId, UseWhenMobMissingAuraId, UseWhenMobHasHealthPercent"); UsageCheck_SemanticCoherency(xElement, ((ItemAppliesAuraId <= 0) && (!ItemUseAlwaysSucceeds) && ((UseItemStrategy == UseItemStrategyType.UseItemOncePerTarget) || (UseItemStrategy == UseItemStrategyType.UseItemOncePerTargetDontDefend) || (!VariantQuestIds.Any()))), context => string.Format("For a UseItemStrategy of {0}, ItemAppliesAuraId must be specified", UseItemStrategy)); }
private async Task <bool> MainCoroutine() { // break if we are done or we are not in combat and targting is not empty, we want the botbase to clear path for us. if (IsDone || (!Me.Combat && Targeting.Instance.FirstUnit != null) || !Me.IsAlive) { return(false); } if (!Query.IsViable(SelectedNpc)) { SelectedNpc = GetNpc(); } if (!Query.IsViable(SelectedNpc) || !Me.IsActuallyInCombat && Targeting.Instance.FirstUnit == null) { // move to search area if (SearchLocation != Vector3.Zero && !Navigator.AtLocation(SearchLocation)) { await UtilityCoroutine.MoveTo(SearchLocation, "Search Area", MovementBy); } // Dismount after reaching search location. else if ((SearchLocation == Vector3.Zero || Navigator.AtLocation(SearchLocation)) && Me.Mounted) { await UtilityCoroutine.ExecuteMountStrategy(MountStrategyType.Dismount); } else { TreeRoot.StatusText = "Waiting for NPC to spawn"; } return(true); } if (SelectedNpc.IsDead && SelectedNpc.TaggedByMe && !VariantQuestIds.Any()) { BehaviorDone(); return(true); } if (SelectedNpc.HasAura(ImmunityAuraId)) { if (BotPoi.Current.AsObject == SelectedNpc) { BotPoi.Clear("Mob is immune"); } var targetedMob = Targeting.Instance.FirstUnit; if (targetedMob != null && ImmunityBreakingMobIds.Contains((int)targetedMob.Entry)) { if (targetedMob.IsTargetingMeOrPet) { // move close enough to shielded NPC so that the exploding mobs will hit it when killed. var myMinDistance = Math.Max(2, MaxRange - targetedMob.MeleeRange); if (SelectedNpc.DistanceSqr > myMinDistance * myMinDistance) { TreeRoot.StatusText = string.Format("Moving closer to {0} before killing {1}", SelectedNpc.SafeName, targetedMob.SafeName); Navigator.MoveTo(SelectedNpc.Location); return(true); } // wait for exploding mob to get within range of shielded mob. if (targetedMob.Location.DistanceSquared(SelectedNpc.Location) > MaxRange * MaxRange) { TreeRoot.StatusText = string.Format( "Waiting for {0} to move withing range of {1}", targetedMob.SafeName, SelectedNpc.SafeName); return(true); } } } } return(false); }
//{ // // EXAMPLE: // UsageCheck_SemanticCoherency(xElement, // (!MobIds.Any() && !FactionIds.Any()), // context => "You must specify one or more MobIdN, one or more FactionIdN, or both."); // // const double rangeEpsilon = 3.0; // UsageCheck_SemanticCoherency(xElement, // ((RangeMax - RangeMin) < rangeEpsilon), // context => string.Format("Range({0}) must be at least {1} greater than MinRange({2}).", // RangeMax, rangeEpsilon, RangeMin)); //} protected PlayerQuest GetQuestInLog() { return(VariantQuestIds.Select(id => StyxWoW.Me.QuestLog.GetQuestById((uint)id)) .FirstOrDefault(q => q != null)); }
/// <summary> /// <para>This reports problems, and stops BT processing if there was a problem with attributes... /// We had to defer this action, as the 'profile line number' is not available during the element's /// constructor call.</para> /// <para>It also captures the user's configuration, and installs Behavior Tree hooks. The items will /// be restored when the behavior terminates, or Honorbuddy is stopped.</para> /// </summary> /// <return>true, if the behavior should run; false, if it should not.</return> /// <param name="extraGoalTextDescription"></param> protected bool OnStart_QuestBehaviorCore(string extraGoalTextDescription = null) { // Semantic coherency / covariant dependency checks... UsageCheck_SemanticCoherency(Element, QuestObjectiveIndex > 0 && !VariantQuestIds.Any(), context => $"QuestObjectiveIndex of '{QuestObjectiveIndex}' specified, but no corresponding QuestId provided"); UsageCheck_SemanticCoherency(Element, _providedQuestIdAndQuestVariantIds, context => "Cannot provide both a QuestId and VariantQuestIds at same time."); EvaluateUsage_SemanticCoherency(Element); // Deprecated attributes... EvaluateUsage_DeprecatedAttributes(Element); // This reports problems, and stops BT processing if there was a problem with attributes... // We had to defer this action, as the 'profile line number' is not available during the element's // constructor call. OnStart_HandleAttributeProblem(); var questId = GetQuestId(); // If the quest is complete, this behavior is already done... // So we don't want to falsely inform the user of things that will be skipped. // NB: Since the IsDone property may skip checking the 'progress conditions', we need to explicltly // check them here to see if we even need to start the behavior. if (!(IsDone || !UtilIsProgressRequirementsMet(questId, QuestRequirementInLog, QuestRequirementComplete))) { this.UpdateGoalText(questId, extraGoalTextDescription); // Start the timer to measure the behavior run time... _behaviorRunTimer.Restart(); // Monitored Behaviors... if (QuestBehaviorCoreSettings.Instance.MonitoredBehaviors.Contains(GetType().Name)) { QBCLog.Debug("MONITORED BEHAVIOR: {0}", GetType().Name); AudibleNotifyOn(true); } _configMemento = CreateConfigMemento(); if (Targeting.Instance != null) { Targeting.Instance.IncludeTargetsFilter += TargetFilter_IncludeTargets; Targeting.Instance.RemoveTargetsFilter += TargetFilter_RemoveTargets; Targeting.Instance.WeighTargetsFilter += TargetFilter_WeighTargets; } Query.InCompetitionReset(); Utility.BlacklistsReset(); _behaviorTreeHook_CombatMain = BehaviorHookInstall("Combat_Main", CreateBehavior_CombatMain()); _behaviorTreeHook_CombatOnly = BehaviorHookInstall("Combat_Only", CreateBehavior_CombatOnly()); _behaviorTreeHook_DeathMain = BehaviorHookInstall("Death_Main", CreateBehavior_DeathMain()); _behaviorTreeHook_QuestbotMain = BehaviorHookInstall("Questbot_Main", CreateBehavior_QuestbotMain()); BlackspotManager.AddBlackspots(_temporaryBlackspots.GetBlackspots()); if (_temporaryAvoidMobs != null) { foreach (var avoidMobId in _temporaryAvoidMobs.GetAvoidMobIds()) { // NB: ProfileManager.CurrentProfile.AvoidMobs will never be null if (!ProfileManager.CurrentProfile.AvoidMobs.Contains(avoidMobId)) { ProfileManager.CurrentProfile.AvoidMobs.Add(avoidMobId); } } } return(true); // behavior should run } return(false); // behavior should NOT run }
private Composite SubBehavior_CombatWithViableMob() { return(new PrioritySelector(context => SelectedTarget = Me.CurrentTarget, // Recall pet, if necessary... new Decorator(context => (SelectedTarget.HealthPercent < RecallPetAtMobPercentHealth) && (Me.GotAlivePet && Me.Pet.GotTarget), new ActionFail(context => { QBCLog.Info("Recalling Pet from '{0}' (health: {1:F1})", SelectedTarget.SafeName, SelectedTarget.HealthPercent); PetControl.SetStance_Passive(); PetControl.Follow(); })), // If we are beyond the max range allowed to use the item, move within range... new Decorator(context => SelectedTarget.Distance > MaxRangeToUseItem, new ActionRunCoroutine( interactUnitContext => UtilityCoroutine.MoveTo( SelectedTarget.Location, string.Format("within {0} feet of {1}", MaxRangeToUseItem, SelectedTarget.SafeName), MovementBy, (float)MaxRangeToUseItem))), // If time to use the item, do so... new Decorator(context => IsUseItemNeeded(SelectedTarget), new PrioritySelector( new ActionRunCoroutine(context => CommonCoroutines.StopMoving()), // Halt combat until we are able to use the item... new Decorator(context => ((UseItemStrategy == UseItemStrategyType.UseItemContinuouslyOnTargetDontDefend) || (UseItemStrategy == UseItemStrategyType.UseItemOncePerTargetDontDefend)), new ActionFail(context => { // We use LUA to stop casting, since SpellManager.StopCasting() doesn't seem to work... if (Me.IsCasting) { Lua.DoString("SpellStopCasting()"); } if (Me.IsAutoAttacking) { Lua.DoString("StopAttack()"); } TreeRoot.StatusText = string.Format("Combat halted--waiting for {0} to become usable.", Utility.GetItemNameFromId(ItemId)); })), new Sequence( new ActionRunCoroutine(ctx => UtilityCoroutine.UseItemOnTarget( ItemId, SelectedTarget, () => BehaviorDone(string.Format("Terminating behavior due to missing {0}", Utility.GetItemNameFromId(ItemId))))), // Allow a brief time for WoWclient to apply aura to mob... new WaitContinue(TimeSpan.FromMilliseconds(5000), context => ItemUseAlwaysSucceeds || SelectedTarget.HasAura(ItemAppliesAuraId), new ActionAlwaysSucceed()), new ActionFail(context => { _waitTimerAfterUsingItem.Reset(); if (ItemUseAlwaysSucceeds || SelectedTarget.HasAura(ItemAppliesAuraId)) { // Count our success if no associated quest... if (!VariantQuestIds.Any()) { ++Counter; } // If we can only use the item once per target, blacklist this target from subsequent selection... if ((UseItemStrategy == UseItemStrategyType.UseItemOncePerTarget) || (UseItemStrategy == UseItemStrategyType.UseItemOncePerTargetDontDefend)) { SelectedTarget.BlacklistForInteracting(TimeSpan.FromSeconds(InteractBlacklistTimeInSeconds)); } // If we can't defend ourselves from the target, blacklist it for combat and move on... if (Query.IsViable(SelectedTarget) && ((UseItemStrategy == UseItemStrategyType.UseItemContinuouslyOnTargetDontDefend) || (UseItemStrategy == UseItemStrategyType.UseItemOncePerTargetDontDefend))) { SelectedTarget.BlacklistForCombat(TimeSpan.FromSeconds(InteractBlacklistTimeInSeconds)); BotPoi.Clear(); Me.ClearTarget(); SelectedTarget = null; } } if ((ItemAppliesAuraId > 0) && !SelectedTarget.HasAura(ItemAppliesAuraId)) { var auraNamesOnMob = ((SelectedTarget.Auras.Keys.Count > 0) ? string.Join(", ", SelectedTarget.Auras.Keys) : "none"); QBCLog.Warning("{1} did not acquire expected AuraId, \"{2}\"--retrying.{0}" + " Auras on {1}: {3}", Environment.NewLine, SelectedTarget.SafeName, Utility.GetSpellNameFromId(ItemAppliesAuraId), auraNamesOnMob); } }), // Prevent combat, if we're not supposed to defend... new Decorator(context => ((UseItemStrategy == UseItemStrategyType.UseItemContinuouslyOnTargetDontDefend) || (UseItemStrategy == UseItemStrategyType.UseItemOncePerTargetDontDefend)), new ActionAlwaysSucceed()) ))) )); }