// CreateBehavior supplied by QuestBehaviorBase. // Instead, provide CreateMainBehavior definition. // Dispose provided by QuestBehaviorBase. // IsDone provided by QuestBehaviorBase. // Call the QuestBehaviorBase.BehaviorDone() method when you want to indicate your behavior is complete. // OnFinished provided by QuestBehaviorBase. public override void OnStart() { // Acquisition and checking of any sub-elements go here. // A common example: // HuntingGrounds = HuntingGroundsType.GetOrCreate(Element, "HuntingGrounds", HuntingGroundCenter); // IsAttributeProblem |= HuntingGrounds.IsAttributeProblem; // Let QuestBehaviorBase do basic initialization of the behavior, deal with bad or deprecated attributes, // capture configuration state, install BT hooks, etc. This will also update the goal text. var isBehaviorShouldRun = OnStart_QuestBehaviorCore(); // 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. if (isBehaviorShouldRun) { // The BotStop handler will remove the "use when" activities... // Note, we only want to hook BotStopped once for this behavior. if (!s_persistedIsOnBotStopHooked) { BotEvents.OnBotStopped += BotEvents_OnBotStopped; s_persistedIsOnBotStopHooked = true; } if (!s_persistedIsOnNewProfileLoadedHooked) { BotEvents.Profile.OnNewProfileLoaded += BotEvents_OnNewProfileLoaded; s_persistedIsOnNewProfileLoadedHooked = true; } switch (Command) { case CommandType.Disable: ActionSetEnableState(false); break; case CommandType.Enable: ActionSetEnableState(true); break; case CommandType.ShowActivities: ActionShowActivities(); break; case CommandType.Remove: ActionRemove(); break; case CommandType.Update: IUseWhenPredicate useWhenPredicate = (UseAtInterval > TimeSpan.Zero) ? (IUseWhenPredicate) new UseWhenPredicate_TimeElapse(UseAtInterval, AllowUseDuringCombat, AllowUseInVehicle, AllowUseWhileFlying, AllowUseWhileMounted) : (IUseWhenPredicate) new UseWhenPredicate_FuncEval(UseWhen, AllowUseDuringCombat, AllowUseInVehicle, AllowUseWhileFlying, AllowUseWhileMounted); ActionUpdate(useWhenPredicate, StopMovingToConductActivity); break; default: QBCLog.MaintenanceError("Unhandled action type of '{0}'.", Command); TreeRoot.Stop(); return; } // Install or remove behavior as needed... // We need to install the hook when its not present, AND there is something to execute. // We remove the hook if there is nothing left to execute. This betters the user's experience, // by maximizing performance. // NB: We cannot simply override the methods provided by QuestBehaviorBase, because this behavior // is unusual. We want these hooks to remain after this behavior terminates. If we use the // QuestBehaviorBase-provide facilities (e.g., override the methods), then the hooks would be // cleaned up (e.g., removed) when this behavior exits. if (s_persistedActivities.Count > 0) { DoWhenHookInstall(); } if (s_persistedActivities.Count <= 0) { DoWhenHookRemove(); } BehaviorDone(); } }
private void BotEvents_OnNewProfileLoaded(EventArgs args) { QBCLog.DeveloperInfo(CfbContextForHook, "OnNewProfileLoaded cleanup..."); // Uninstall the behavior from the tree... DoWhenHookRemove(); }
public void Loopstuff() { while (true) { ObjectManager.Update(); if (Me.IsQuestComplete(QuestId)) { _isBehaviorDone = true; break; } try { if (!Query.IsInVehicle()) { var turret = GetTurret(); if (turret != null) { if (turret.DistanceSqr > 5 * 5) { //Navigator.MoveTo(turret.Location); } else { turret.Interact(); } } else { QBCLog.Info("Unable to find turret"); } } else { if (Me.CurrentTarget != null && (Me.CurrentTarget.Distance < 60 || Me.CurrentTarget.InLineOfSight)) { WoWMovement.ClickToMove(Me.CurrentTarget.Location); //WoWMovement.ClickToMove(Me.CurrentTarget.Location.RayCast(Me.CurrentTarget.Rotation, 20)); var x = ObjectManager.GetObjectsOfType <WoWUnit>().FirstOrDefault(z => z.CharmedByUnit == Me); Tripper.Tools.Math.Vector3 v = Me.CurrentTarget.Location - Me.Location; v.Normalize(); Lua.DoString( string.Format( "VehicleAimIncrement(({0} - VehicleAimGetAngle())); CastPetAction(1);CastPetAction(2);", Math.Asin(v.Z).ToString())); } else { if (!Me.IsQuestObjectiveComplete(QuestId, 1)) { if (Marksmen != null) { Marksmen.Target(); } } else if (!Me.IsQuestObjectiveComplete(QuestId, 2)) { if (Cannoner != null) { Cannoner.Target(); } } else if (!Me.IsQuestObjectiveComplete(QuestId, 3)) { if (Cannon != null) { Cannon.Target(); } } } } } catch (Exception except) { QBCLog.Exception(except); } } }
private async Task <bool> MoveToEnd(bool exitVehicle, WoWUnit passenger = null) { if (!Query.IsInVehicle()) { return(false); } if (Vehicle.Location.DistanceSqr(EndLocation) >= PrecisionSqr) { await UseSpeedBuff(); Flightor.MoveTo(EndLocation); return(true); } if (exitVehicle) { Lua.DoString("VehicleExit()"); await Coroutine.Sleep(2000); await Coroutine.Wait(20000, () => !Me.IsFalling); if (Me.Combat) { QBCLog.Info("Getting in vehicle to drop combat"); return(await GetInVehicleLogic()); } return(true); } if (!Query.IsViable(passenger)) { return(false); } if (Vehicle.IsMoving) { await CommonCoroutines.StopMoving("Dropping off passenger."); await CommonCoroutines.SleepForLagDuration(); } await Coroutine.Sleep(StyxWoW.Random.Next(5000, 6000)); UseVehicleButton(DropPassengerButton); await CommonCoroutines.SleepForLagDuration(); if (!await Coroutine.Wait(10000, () => !Query.IsViable(passenger) || !UnitIsRidingMyVehicle(passenger))) { QBCLog.Warning("Failed to drop passenger off"); return(false); } if (Query.IsViable(passenger)) { Blacklist.Add(passenger, BlacklistFlags.Interact, TimeSpan.FromMinutes(10), "Rescued"); } // pause a sec to see if quest completes. if (await Coroutine.Wait(2000, () => Quest.IsCompleted)) { return(true); } CycleToNearestPointInPath(); return(true); }
public Composite CreateMainBehavior() { // NB: We need to allow lower BT nodes to run when the behavior is finished; otherwise, HB will not // process the change of _isBehaviorDone state. return(new PrioritySelector( // If quest is done, behavior is done... new Decorator(context => IsDone, new Action(context => { _isBehaviorDone = true; QBCLog.Info("Finished"); })), // If using cannon, start spanking targets... new Decorator(context => Query.IsInVehicle(), // Ready, Aim, Fire! new Action(context => { // If ejected from vehicle, try to re-locate it... if ((CannonVehicle == null) || !CannonVehicle.IsValid) { CannonVehicle = FindUnitsFromId(VehicleId_NurongsCannon).FirstOrDefault(); return; } // If target is no longer valid, select another... if (!IsViableTarget(CannonVehicle, SelectedTarget)) { SelectedTarget = ChooseTarget(CannonVehicle, SelectedTarget); if (SelectedTarget == null) { return; } SelectedTarget.Target(); } AimAndFireCannon(CannonVehicle, SelectedTarget); })), // If not using cannon, get in cannon vehicle... new Decorator(context => !Query.IsInVehicle(), new PrioritySelector(cannonContext => FindUnitsFromId(MobId_NurongsCannon).FirstOrDefault(), // If unable to locate cannon, warn user and stop... new Decorator(cannonContext => cannonContext == null, new PrioritySelector( // The Wait is a defensive bumper against a WoWclient/HBcore race condition... // Sometimes, the toon is ejected from the vehicle before the quest is marked as 'complete'. // We don't want this situation to cause the profile to stop, so we wait for a short while // before declaring a profile problem. new Wait(TimeSpan.FromMilliseconds(5000), cannonContext => IsDone, new ActionAlwaysSucceed()), new Action(cannonContext => { QBCLog.Error("PROFILE ERROR: Nurong's Cannon is not in the area--please repair profile"); TreeRoot.Stop(); _isBehaviorDone = true; }) )), // Move close enough, and interact with cannon... new Decorator(cannonContext => ((WoWUnit)cannonContext).Distance > ((WoWUnit)cannonContext).InteractRange, new Action(cannonContext => { Navigator.MoveTo(((WoWUnit)cannonContext).Location); })), new Decorator(cannonContext => !Me.IsFacing((WoWUnit)cannonContext), new Action(cannonContext => { ((WoWUnit)cannonContext).Face(); })), new Decorator(cannonContext => Me.IsMoving, new Action(cannonContext => { WoWMovement.MoveStop(); })), new Decorator(cannonContext => !Query.IsInVehicle(), new Action(cannonContext => { ((WoWUnit)cannonContext).Interact(); CannonVehicle = null; })), new Wait(TimeSpan.FromMilliseconds(5000), cannonContext => Query.IsInVehicle(), new ActionAlwaysSucceed()) )) )); }
public ChangeSet(Dictionary <string, object> changes) { var changeSet = new List <Tuple <SettingDescriptor, object> >(); var isProblemAttribute = false; foreach (var change in changes) { try { var name = change.Key; var value = change.Value; // Setting name cannot be null or empty... if (string.IsNullOrEmpty(name)) { QBCLog.Error("Name may not be null or empty"); isProblemAttribute = true; continue; } // Check that setting exists... var settingDescriptor = RecognizedSettings.FirstOrDefault(s => s.Name == name); if (settingDescriptor == null) { QBCLog.Error("Unable to locate setting for '{0}'.", name); isProblemAttribute = true; continue; } // Is changing attribute allowed? if (settingDescriptor.IsAccessDisallowed) { QBCLog.Error("Accessing attribute '{0}' is not allowed.", name); isProblemAttribute = true; continue; } // Check that setting doesn't already exist in the changeset... if (changeSet.Any(t => t.Item1.Name == name)) { QBCLog.Error("Setting '{0}' already exists in the changeset.", name); isProblemAttribute = true; continue; } // If user specified 'original' value, go look it up and substitute it for 'value'... if ((value is string) && ((string)value == "original")) { object originalValue; if (!OriginalConfiguration.TryGetValue(settingDescriptor.Name, out originalValue)) { // A missing 'original configuration' is a maintenance issue, not a user error... QBCLog.MaintenanceError("For setting '{0}', there is no original configuration value.", settingDescriptor.Name); isProblemAttribute = true; continue; } value = originalValue; } // Check that setting is an appropriate type... var newValue = settingDescriptor.ToCongruentObject(value); if (!settingDescriptor.ConstraintChecker.IsWithinConstraints(newValue)) { QBCLog.Error("For setting '{0}', the provided value '{1}' is not within the required constraints of {2}.", name, value, settingDescriptor.ConstraintChecker.Description); isProblemAttribute = true; continue; } // Setting change is acceptable... changeSet.Add(Tuple.Create(settingDescriptor, value)); } catch (Exception ex) { QBCLog.Exception(ex, "MAINTENANCE ERROR: Error processing attribute '{0}.'", change.Key); isProblemAttribute = true; } } // If problem encountered with any change, we're unable to build the ChangeSet... if (isProblemAttribute) { _changeSet = null; throw new ArgumentException("Problems encountered with provided argument"); } Count = changeSet.Count; _changeSet = new ReadOnlyCollection <Tuple <SettingDescriptor, object> >(changeSet); }
private Composite CreateBehavior_Antistuck() { return(new PrioritySelector( new Decorator(context => _stuckTimer.IsFinished, new Sequence(context => _antiStuckMyLoc = WoWMovement.ActiveMover.Location, // Check if stuck... new DecoratorContinue(context => _antiStuckMyLoc.DistanceSqr(_antiStuckPrevPosition) < (3 * 3), new Sequence(context => _antiStuckPerformSimpleSequence = _antiStuckStuckSucceedTimer.IsFinished, new DecoratorContinue(context => Me.IsMounted() && !Me.IsFlying, new ActionRunCoroutine(context => CommonCoroutines.Dismount("Stuck"))), // Perform simple unstuck proceedure... new DecoratorContinue(context => _antiStuckPerformSimpleSequence, new Sequence( new Action(context => QBCLog.Debug("Stuck. Trying to jump")), new Action(context => { // ensure bot is moving forward when jumping (Wow will sometimes automatically // stop moving if running against a wall) if (ShouldPerformCTM) { WoWMovement.ClickToMove(Destination); } WoWMovement.Move(WoWMovement.MovementDirection.JumpAscend); }), new Sleep(1000), new Action(context => WoWMovement.MoveStop(WoWMovement.MovementDirection.JumpAscend)) )), // perform less simple unstuck proceedure new DecoratorContinue(context => !_antiStuckPerformSimpleSequence, new Sequence(context => _antiStuckMoveDirection = GetRandomMovementDirection(), new Action(context => QBCLog.Debug("Stuck. Movement Directions: {0}", _antiStuckMoveDirection)), new Action(context => WoWMovement.Move(_antiStuckMoveDirection)), new Sleep(2000), new Action(context => WoWMovement.MoveStop(_antiStuckMoveDirection)))), new Action(context => _antiStuckStuckSucceedTimer.Reset()))), new Action(context => _antiStuckPrevPosition = _antiStuckMyLoc), new Action(context => _stuckTimer.Reset()) )))); }
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()) ))) )); }
public TwoBySea(Dictionary <string, string> args) : base(args) { QBCLog.BehaviorLoggingContext = this; try { // Quest handling... QuestId = 14382; // http://wowhead.com/quest=14382 QuestRequirementComplete = QuestCompleteRequirement.NotComplete; QuestRequirementInLog = QuestInLogRequirement.InLog; Task_CaptainAnson = new TaskDetail( "Captain Anson", 36397, // Captain Anson: http://wowhead.com/npc=36397 new Vector3(-2073.466f, 2632.036f, 2.717113f), // Launch Position new Vector3(-2124.181f, 2662.547f, 8.256202f), // Target Position 0.22, // Needed Azimuth (in radians) new Vector3(-2105.5f, 2655.504f, 0.5987438f), // Jump down off boat point c => IsQuestObjectiveComplete(QuestId, 1) ); Task_CaptainMorris = new TaskDetail( "Captain Morris", 36399, // Captain Morris: http://wowhead/npc=36399 new Vector3(-2182.197f, 2549.495f, 2.720596f), // Launch Position new Vector3(-2225.435f, 2565.901f, 8.664543f), // Target Position 0.18, // Needed Azimuth (in radians) new Vector3(-2207.448f, 2558.94f, 0.950241f), // Jump down off boat point c => IsQuestObjectiveComplete(QuestId, 2)); MobId_ForsakenMachinist = 36292; // http://wowhead.com/npc=36292 VehicleId_ForsakenCatapult = 36283; // http://www.wowhead.com/npc=36283 Location_CatapultFarm = new Vector3(-2052.313f, 2577.324f, 1.39316f).FanOutRandom(20.0); Lua_LaunchCommand = "if GetPetActionCooldown(1) == 0 then CastPetAction(1) end"; // http://www.wowhead.com/spell=66251 // Tunables... CombatMaxEngagementRangeDistance = 23.0; NonCompeteDistanceForCatapults = 25.0; VehicleLocationPathPrecision = 3.5; // Blackspots... Blackspots = new List <Blackspot>() { new Blackspot(new Vector3(-2126.297f, 2536.12f, 7.228605f), 12.0f, 1.0f) }; // Semantic coherency / covariant dependency checks -- } 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; } }
protected override Composite CreateBehavior_CombatMain() { return(new PrioritySelector( new Decorator(context => !IsDone && !Me.IsActuallyInCombat, new PrioritySelector( // Update Location if relative coord is used... // N.B. Relative locations are only used while on transports and because // transports are usually moving around 'Location' needs to be updated on every frame. new Decorator(context => UseRelativeLocation, new Action(context => Destination = CalculateRelativeLocation(OrigDestination))), // Initialize the timer... new Decorator(context => _runTimer == null, new Action(context => { _runTimer = new WaitTimer(Destination.MaximumTraversalTime(2.5, TimeSpan.FromSeconds(20), UpperLimitOnMovementTime)); QBCLog.DeveloperInfo("Maximum allowed time to reach destination: {0} seconds", _runTimer.WaitTime.TotalSeconds); _runTimer.Reset(); return RunStatus.Failure; })), // Stop HB if _runTimer finishes... new Decorator(context => _runTimer.IsFinished, new Action(context => { WoWMovement.MoveStop(); // N.B. set the runtimer to null so if player manually correct // problem and starts bot up it restarts the timer. _runTimer = null; QBCLog.Fatal("MyCTM is not able to reach {0} from {1}", DestinationName, WoWMovement.ActiveMover.Location); })), // Run stuckhandler CreateBehavior_Antistuck(), // Default anti-stuck has issues. // new Decorator(context => Navigator.NavigationProvider.StuckHandler.IsStuck(), // new Action(context => Navigator.NavigationProvider.StuckHandler.Unstick())), // check if bot has reached the destination. new Decorator(context => Destination.DistanceSqr(Me.Location) <= (3 * 3), new Action(context => { BehaviorDone(string.Format("Finished moving to {0}", DestinationName)); // Drop down to 'CreateBehavior_PerformCTM' to ensure ctm is performed // at least once if start and destination locations are very close on start return RunStatus.Failure; })), CreateBehavior_PerformCTM() )), // _runTimer needs to be recalculated after combat is over and stuck timer needs to rest. new Decorator(context => Me.IsActuallyInCombat, new Action(context => { _runTimer = null; _stuckTimer.Reset(); return RunStatus.Failure; })) )); }
private Composite CreateMainBehavior() { return(new PrioritySelector( // If a mob targets us, kill it... // We don't want to blindly move to destination and drag a bunch of mobs behind us... new Decorator(context => (SelectedTarget = FindMobTargetingMeOrPet()) != null, UtilityBehavior_SpankMob(context => SelectedTarget)), // Stateful Operation: new Switch <StateType_MainBehavior>(context => State_MainBehavior, #region State: DEFAULT new Action(context => // default case { QBCLog.Error("BEHAVIOR MAINTENANCE PROBLEM: StateType_MainBehavior({0}) is unhandled", State_MainBehavior); TreeRoot.Stop(); _isBehaviorDone = true; }), #endregion #region State: Assigning Task new SwitchArgument <StateType_MainBehavior>(StateType_MainBehavior.AssigningTask, new PrioritySelector( // Captain Anson... new Decorator(context => !Task_CaptainAnson.IsTaskComplete(context), new Action(context => { CurrentTask = Task_CaptainAnson; State_MainBehavior = StateType_MainBehavior.AcquiringCatapult; })), // Captain Morris... new Decorator(context => !Task_CaptainMorris.IsTaskComplete(context), new Action(context => { CurrentTask = Task_CaptainMorris; State_MainBehavior = StateType_MainBehavior.AcquiringCatapult; })), // Done with all tasks, move back to sane position to continue profile... new Decorator(context => !Navigator.AtLocation(Location_CatapultFarm), new Action(context => { Navigator.MoveTo(Location_CatapultFarm); })), new Action(context => { QBCLog.Info("Finished"); _isBehaviorDone = true; }) )), #endregion #region State: Acquiring Catapult new SwitchArgument <StateType_MainBehavior>(StateType_MainBehavior.AcquiringCatapult, new PrioritySelector( // If task complete, go get next task... new Decorator(context => CurrentTask.IsTaskComplete(context), new Action(context => { State_MainBehavior = StateType_MainBehavior.AssigningTask; })), // If we're in the catapult, start using it... new Decorator(context => Query.IsInVehicle(), new Action(context => { State_MainBehavior = StateType_MainBehavior.UsingCatapultToBoardBoat; })), // Notify user... new Action(context => { QBCLog.Info("Appropriating a Catapult"); return RunStatus.Failure; }), // If available catapult, take advantage of it... new Decorator(context => IsViable(SelectedCatapult) && (FindPlayersNearby(SelectedCatapult.Location, NonCompeteDistanceForCatapults).Count() <= 0), UtilityBehavior_InteractWithMob(context => SelectedCatapult)), // Otherwise, spank machinist and take his catapult... new Decorator(context => IsViable(SelectedMachinist) && (FindPlayersNearby(SelectedMachinist.Location, NonCompeteDistanceForCatapults).Count() <= 0), UtilityBehavior_SpankMob(context => SelectedMachinist)), // Find next catapult or machinist... // NB: Since it takes a couple of seconds for the catapult to appear after // we kill the machinist, we want to wait briefly. Without this delay, // the toon will run off to another machinist, and come back when the Catapult // spawns from the machinist we just killed. This makes us look very bottish, // and the delay prevents that. new Wait(TimeSpan.FromSeconds(3), context => ((SelectedCatapult = FindCatapult()) != null), new ActionAlwaysSucceed()), new Decorator(context => (SelectedMachinist = FindMachinist()) != null, new ActionAlwaysSucceed()), // No catapults to be had, move to center of catapult farm and wait for respawns... new Decorator(context => !Navigator.AtLocation(Location_CatapultFarm), new Action(context => { Navigator.MoveTo(Location_CatapultFarm); })), new Action(context => { QBCLog.Info("Waiting on more Catapults to respawn"); }) )), #endregion #region State: Using Catapult to Board Boat new SwitchArgument <StateType_MainBehavior>(StateType_MainBehavior.UsingCatapultToBoardBoat, new PrioritySelector( // If task complete, go fetch another... new Decorator(context => CurrentTask.IsTaskComplete(context), new Action(context => { State_MainBehavior = StateType_MainBehavior.AssigningTask; })), // If we're no longer in catapult, either launch succeeded or we need to fetch another Catapult... new Decorator(context => !Query.IsInVehicle(), new PrioritySelector( // Allow time for Launch completion, and toon to land on boat... new Wait(TimeSpan.FromSeconds(5), // TODO: Rewrite to use something else (probably IsOnTransport?) context => true /*Navigator.CanNavigateFully(Me.Location, CurrentTask.PositionToLand)*/, new ActionAlwaysFail()), new Action(context => { // If we can navigate to intended landing spot, we successfully boarded boat... // TODO: Rewrite to use something else (probably IsOnTransport?) if (/*Navigator.CanNavigateFully(Me.Location, CurrentTask.PositionToLand)*/ true) { State_MainBehavior = StateType_MainBehavior.KillingCaptain; return; } // Otherwise, we missed boarding boat, and need to try again... QBCLog.Warning("Failed in boarding {0}'s boat--trying again", CurrentTask.MobName); State_MainBehavior = StateType_MainBehavior.AcquiringCatapult; }) )), // If Catapult no longer viable, find a new one... new Decorator(context => !IsViable(SelectedCatapult), new Decorator(context => (SelectedCatapult = FindCatapult()) == null, new Action(context => { State_MainBehavior = StateType_MainBehavior.AcquiringCatapult; }))), // Try to board boat... new ActionRunCoroutine(ctx => UtilityCoroutine_MoveAndUseCatapult()) )), #endregion #region State: Kill the Captain new SwitchArgument <StateType_MainBehavior>(StateType_MainBehavior.KillingCaptain, new PrioritySelector( // If task complete, exit boat... new Decorator(context => CurrentTask.IsTaskComplete(context), new Action(context => { State_MainBehavior = StateType_MainBehavior.ExitingBoat; })), // Kill the Captain... new PrioritySelector(captainContext => FindUnitsFromIds(CurrentTask.MobId).FirstOrDefault(), new Decorator(captainContext => captainContext != null, UtilityBehavior_SpankMob(captainContext => (WoWUnit)captainContext)), new Decorator(captainContext => captainContext == null, new Action(captainContext => { QBCLog.Info("Waiting for {0} to respawn", CurrentTask.MobName); })) ) )), #endregion #region State: Exiting Boat new SwitchArgument <StateType_MainBehavior>(StateType_MainBehavior.ExitingBoat, new PrioritySelector( new Action(context => { QBCLog.Info("Exiting {0}'s boat", CurrentTask.MobName); return RunStatus.Failure; }), new Decorator(context => !Navigator.AtLocation(CurrentTask.PositionToLand), new Action(context => { Navigator.MoveTo(CurrentTask.PositionToLand); })), new Action(context => { State_MainBehavior = StateType_MainBehavior.ExitBoatJumpDown; }) )), #endregion #region State: Exit Boat Jump Down new SwitchArgument <StateType_MainBehavior>(StateType_MainBehavior.ExitBoatJumpDown, new PrioritySelector( new Action(context => { QBCLog.Info("Jumping down off of {0}'s boat", CurrentTask.MobName); return RunStatus.Failure; }), // NB: There appear to be no mesh "jump links" in the mesh to get off boat. // So, we're left with using ClickToMove to jump down from boat decks. new Decorator(context => !Navigator.AtLocation(CurrentTask.PositionToJumpDownOffBoat), new Action(context => { WoWMovement.ClickToMove(CurrentTask.PositionToJumpDownOffBoat); })), new Action(context => { State_MainBehavior = StateType_MainBehavior.AssigningTask; }) )) #endregion ))); }
protected override Composite CreateBehavior() { return(_Root ?? (_Root = new PrioritySelector(context => !s_isBehaviorDone, #region MyHotSpot // Store our current location. new Decorator(context => MyHotSpot == Vector3.Zero, new Sequence( new DecoratorContinue(context => Me.IsMoving, new WaitContinue(TimeSpan.FromMilliseconds(2000), context => false, new ActionAlwaysSucceed()) ), new DecoratorContinue(context => !Me.IsMoving, new Action(context => MyHotSpot = Me.Location) ) ) ), #endregion #region MinLevel // Should we check for partymember minumum level ? new Decorator(context => (MinLevel > 0), new Sequence( // Someone is below MinLevel. new DecoratorContinue(context => !CheckLevel(), new Sequence( new Action(context => QBCLog.Info("Someone in your party is below level {0}.", MinLevel)), new Action(context => s_isBehaviorDone = true) ) ), // Everyone is equal or above MinLevel. new DecoratorContinue(context => CheckLevel(), new Action(context => MinLevel = 0) ) ) ), #endregion #region CheckRange // Should we wait for party members to be in range ? new Decorator(context => (CheckRange != 0), new Sequence( // Everyone isn't within interact range, lets wait abit before checking again. new DecoratorContinue(context => !CheckPartyRange(), new Sequence( new DecoratorContinue(context => !Navigator.AtLocation(MyHotSpot), new Sequence( new Action(context => Navigator.MoveTo(MyHotSpot)) ) ), new WaitContinue(TimeSpan.FromMilliseconds(300), context => false, new ActionAlwaysSucceed()) ) ), // Everyone is within interact range. new DecoratorContinue(context => CheckPartyRange(), new Sequence( new Action(context => QBCLog.Info("Everyone is within range.")), new Action(context => CheckRange = 0) ) ) ) ), #endregion #region ChkExp // Disabled until I can find out a safer way to to it. /* * new Decorator(context => (ChkExp != 0), * new Sequence( * new DecoratorContinue(context => !AreWeDone(), * new Action(context => CheckExpansions()) * ), * new DecoratorContinue(context => AreWeDone(), * new Sequence( * new DecoratorContinue(context => !DoAllHaveExp(), * new Sequence( * new Action(context => QBCLog.Info("Everyone in your group doesn't have ExpansionLevel '{0}'", ChkExp)), * new Action(context => _isBehaviorDone = true) * ) * ), * new DecoratorContinue(context => DoAllHaveExp(), * new Sequence( * new Action(context => QBCLog.Info("Everyone has atleast ExpansionLevel '{0}'", ChkExp)), * new Action(context => ChkExp = 0) * ) * ) * ) * ) * ) * ), */ #endregion #region RemotePath // Load the remote profile... new Decorator(context => RemotePath != "", new Sequence( // You have included a RemotePath but not a ProfileName. new DecoratorContinue(context => ProfileName == "", new Sequence( new Action(context => QBCLog.Error("You need to include a ProfileName.")), new Action(context => s_isBehaviorDone = true) ) ), // Remote Profile doesn't exist. new DecoratorContinue(context => (ProfileName != "" && !UrlExists(NewRemoteProfilePath)), new Sequence( new Action(context => QBCLog.Error("Profile '{0}' does not exist.", ProfileName)), new Action(context => s_isBehaviorDone = true) ) ), // Everything is ok, Load the remote Profile new DecoratorContinue(context => (ProfileName != "" && UrlExists(NewRemoteProfilePath)), new Sequence( new Action(context => TreeRoot.StatusText = "Loading profile '" + ProfileName + "'"), new Action(context => QBCLog.Info("Loading profile '{0}'", ProfileName)), new Action(context => ProfileManager.LoadNew(new MemoryStream(new WebClient().DownloadData(NewRemoteProfilePath)))), new WaitContinue(TimeSpan.FromMilliseconds(300), context => false, new ActionAlwaysSucceed()), new Action(context => s_isBehaviorDone = true) ) ) ) ), #endregion #region ProfileName // Load the local profile... new Decorator(context => (ProfileName != "" && RemotePath == ""), new PrioritySelector( // Local Profile doesn't exist. new Decorator(context => !IsStoreProfile && !File.Exists(NewLocalProfilePath), new Sequence( new Action(context => QBCLog.Error("Profile '{0}' does not exist.", ProfileName)), new Action(context => s_isBehaviorDone = true) ) ), // Everything is ok, Load the local Profile. new Sequence( new Action(context => TreeRoot.StatusText = "Loading profile '" + ProfileName + "'"), new Action(context => QBCLog.Error("Loading profile '{0}'", ProfileName)), new Action(context => ProfileManager.LoadNew(NewLocalProfilePath, false)), new WaitContinue(TimeSpan.FromMilliseconds(300), context => false, new ActionAlwaysSucceed()), new Action(context => s_isBehaviorDone = true) ) ) ), #endregion #region Behavior Done // Everyone is within interact range and we shouldn't load a profile, then end the Quest Behavior. new Decorator(context => !s_isBehaviorDone, new Action(context => s_isBehaviorDone = true) ) #endregion ) )); }
private Composite StateBehaviorPS_PathIngressing() { return(new PrioritySelector( // If no Ingress path exists, build it... new Decorator(context => Path_Ingress == null, new Action(context => { Path_Ingress = FollowPath.FindPath_Ingress(); })), // If we've consumed our Ingress path (or the one we initially built is empty), we're done... new Decorator(context => !Path_Ingress.Any(), new Action(context => { State_MainBehavior = StateType_MainBehavior.DestinationReached; })), // If Mob_ToAvoid is too close or we get in combat, abandon current ingress, and retreat back to safespot... new Decorator(context => Query.IsViable(Mob_ToAvoid) && ((Mob_ToAvoid.Distance < FollowPath.EgressDistance) || Me.Combat), new Action(context => { Path_Ingress = null; Path_Egress = null; State_MainBehavior = StateType_MainBehavior.PathRetreating; })), new Switch <SafePathType.StrategyType>(context => FollowPath.Strategy, new Action(context => // default case { var message = string.Format("FollowPathStrategyType({0}) is unhandled", FollowPath.Strategy); QBCLog.MaintenanceError(message); TreeRoot.Stop(); BehaviorDone(message); }), new SwitchArgument <SafePathType.StrategyType>(SafePathType.StrategyType.StalkMobAtAvoidDistance, new Decorator(context => Query.IsViable(Mob_ToAvoid) && (Mob_ToAvoid.Distance < AvoidDistance), new PrioritySelector( new ActionRunCoroutine(context => CommonCoroutines.StopMoving()), new ActionAlwaysSucceed() ))), new SwitchArgument <SafePathType.StrategyType>(SafePathType.StrategyType.WaitForAvoidDistance, new PrioritySelector( // No addition action needed to implement strategy for now )) ), // If we've arrived at the current ingress waypoint, dequeue it... new Decorator(context => Navigator.AtLocation(Path_Ingress.Peek().Location), new Action(context => { FollowPath.DismissPetIfNeeded(); Path_Ingress.Dequeue(); })), // Follow the prescribed ingress path, if its still safe to proceed... new Decorator(context => IsSafeToMoveToDestination(Mob_ToAvoid), new ActionRunCoroutine( context => UtilityCoroutine.MoveTo( Path_Ingress.Peek().Location, "follow ingress path", MovementBy))), // If mob is heading our direction, hold position... new Decorator(context => !IsSafeToMoveToDestination(Mob_ToAvoid), new Sequence( new Action(context => { TreeRoot.StatusText = string.Format("Holding position to evaluate {0}'s actions.", Mob_ToAvoid.SafeName); }), new ActionRunCoroutine(context => CommonCoroutines.StopMoving()) )) )); }
public PursuitListType(XElement xElement) : base(xElement) { try { PursueObjects = new List <PursueObjectTypeBase>(); if (xElement != null) { var pursueObjectElementsQuery = from element in xElement.Elements() where (element.Name == "PursueObject") || (element.Name == "PursueUnit") || (element.Name == "PursueGameObject") || (element.Name == "PursueSelf") select element; foreach (XElement childElement in pursueObjectElementsQuery) { PursueObjectTypeBase pursueObj; if (childElement.Name == "PursueObject") { pursueObj = new PursueObjectType <WoWObject>(childElement); } else if (childElement.Name == "PursueUnit") { pursueObj = new PursueObjectType <WoWUnit>(childElement); } else if (childElement.Name == "PursueGameObject") { pursueObj = new PursueObjectType <WoWGameObject>(childElement); } else if (childElement.Name == "PursueSelf") { pursueObj = new PursueObjectType <LocalPlayer>(childElement); } else { throw new InvalidDataException(string.Format("{0} is not a recognized type", childElement.Name)); } if (!pursueObj.IsAttributeProblem) { PursueObjects.Add(pursueObj); } IsAttributeProblem |= pursueObj.IsAttributeProblem; } } HandleAttributeProblem(); } catch (Exception except) { if (Query.IsExceptionReportingNeeded(except)) { QBCLog.Exception(except, "PROFILE PROBLEM with \"{0}\"", xElement.ToString()); } IsAttributeProblem = true; } }
private object UtilGetAttributeAsWoWPoints(string attributeName, bool isAttributeRequired, string[] attributeNameAliases) { bool isError = false; string keyName = UtilLocateKey(isAttributeRequired, attributeName, attributeNameAliases); List <WoWPoint> pointList = new List <WoWPoint>(); char[] separatorCoordinate = { ' ', ',' }; char[] separatorTriplet = { '|', ';' }; if ((keyName == null) || !Attributes.ContainsKey(keyName)) { pointList.Clear(); return(pointList.ToArray()); } foreach (string tripletAsString in Attributes[keyName].Split(separatorTriplet, StringSplitOptions.RemoveEmptyEntries)) { string[] coordinatesAsString = tripletAsString.Split(separatorCoordinate, StringSplitOptions.RemoveEmptyEntries); if (coordinatesAsString.Length != 3) { QBCLog.Error(QBCLog.BuildMessageWithContext(Element, "The '{1}' attribute's value contribution (saw '{2}')" + " doesn't have three coordinates (counted {3}).{0}" + "Expect entries of the form \"x1,y1,z1 | x2,y2,z2 | x3,...\", or \"x1,y1,z1; x2,y2,z2; x3,...\"", Environment.NewLine, keyName, tripletAsString, coordinatesAsString.Length)); isError = true; continue; } double?tmpValueX = null; try { tmpValueX = UtilTo <double>(keyName, coordinatesAsString[0]); } catch (Exception) { isError = true; } double?tmpValueY = null; try { tmpValueY = UtilTo <double>(keyName, coordinatesAsString[1]); } catch (Exception) { isError = true; } double?tmpValueZ = null; try { tmpValueZ = UtilTo <double>(keyName, coordinatesAsString[2]); } catch (Exception) { isError = true; } if (tmpValueX.HasValue && tmpValueY.HasValue && tmpValueZ.HasValue) { pointList.Add(new WoWPoint(tmpValueX.Value, tmpValueY.Value, tmpValueZ.Value)); } } if (isError) { pointList.Clear(); IsAttributeProblem = true; } return(pointList.ToArray()); }
public CombatUseItemOnV2(Dictionary <string, string> args) : base(args) { try { // NB: Core attributes are parsed by QuestBehaviorBase parent (e.g., QuestId, NonCompeteDistance, etc) // Primary attributes... ItemId = GetAttributeAsNullable <int>("ItemId", true, ConstrainAs.ItemId, null) ?? 0; string itemUseAlwaysSucceeds = GetAttributeAs <string>("ItemAppliesAuraId", false, ConstrainAs.StringNonEmpty, null) ?? string.Empty; if (itemUseAlwaysSucceeds == "AssumeItemUseAlwaysSucceeds") { ItemAppliesAuraId = 0; } else { ItemAppliesAuraId = GetAttributeAsNullable <int>("ItemAppliesAuraId", false, ConstrainAs.AuraId, null) ?? 0; } MobIds = GetNumberedAttributesAsArray <int>("MobId", 1, ConstrainAs.MobId, null); UseWhenMeHasAuraId = GetAttributeAsNullable <int>("UseWhenMeHasAuraId", false, ConstrainAs.AuraId, null) ?? 0; UseWhenMeMissingAuraId = GetAttributeAsNullable <int>("UseWhenMeMissingAuraId", false, ConstrainAs.AuraId, null) ?? 0; UseWhenMobCastingSpellId = GetAttributeAsNullable <int>("UseWhenMobCastingSpellId", false, ConstrainAs.SpellId, null) ?? 0; UseWhenMobHasAuraId = GetAttributeAsNullable <int>("UseWhenMobHasAuraId", false, ConstrainAs.AuraId, null) ?? 0; UseWhenMobMissingAuraId = GetAttributeAsNullable <int>("UseWhenMobMissingAuraId", false, ConstrainAs.AuraId, null) ?? 0; UseWhenMobHasHealthPercent = GetAttributeAsNullable <double>("UseWhenMobHasHealthPercent", false, ConstrainAs.Percent, null) ?? 0; // Either HuntingGroundCenter or <HuntingGrounds> subelement must be provided... // The sanity check for this is done in OnStart() since that's where we must do // all sub-element processing due to the way CustomForcedBehavior is architected. HuntingGroundCenter = GetAttributeAsNullable <Vector3>("", false, ConstrainAs.Vector3NonEmpty, null); // Tunables... CollectionDistance = GetAttributeAsNullable <double>("CollectionDistance", false, ConstrainAs.Range, null) ?? 100; InteractBlacklistTimeInSeconds = GetAttributeAsNullable <int>("InteractBlacklistTimeInSeconds", false, ConstrainAs.CollectionCount, null) ?? 180; MaxRangeToUseItem = GetAttributeAsNullable <double>("MaxRangeToUseItem", false, ConstrainAs.Range, null) ?? 25.0; NumOfTimes = GetAttributeAsNullable <int>("NumOfTimesToUseItem", false, ConstrainAs.RepeatCount, null) ?? 1; RecallPetAtMobPercentHealth = GetAttributeAsNullable <double>("RecallPetAtMobPercentHealth", false, ConstrainAs.Percent, null) ?? UseWhenMobHasHealthPercent; UseItemStrategy = GetAttributeAsNullable <UseItemStrategyType>("UseItemStrategy", false, null, null) ?? UseItemStrategyType.UseItemOncePerTarget; WaitTimeAfterItemUse = GetAttributeAsNullable <int>("WaitTimeAfterItemUse", false, ConstrainAs.Milliseconds, null) ?? 0; // Hunting ground processing... HuntingGrounds = HuntingGroundsType.GetOrCreate(Element, "HuntingGrounds", (HuntingGroundCenter.HasValue ? new WaypointType(HuntingGroundCenter.Value, "hunting ground center") : null)); IsAttributeProblem |= HuntingGrounds.IsAttributeProblem; } 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; } }
private T UtilTo <T>(string attributeName, string attributeValueAsString) { Type concreteType = typeof(T); // Booleans require special handling... if (concreteType == typeof(bool)) { int tmpInt; if (int.TryParse(attributeValueAsString, NumberStyles.Integer, CultureInfo.InvariantCulture, out tmpInt)) { attributeValueAsString = (tmpInt != 0) ? "true" : "false"; QBCLog.Warning(QBCLog.BuildMessageWithContext(Element, "Attribute's '{1}' value was provided as an integer (saw '{2}')--a boolean was expected.{0}" + "The integral value '{2}' was converted to Boolean({3}).{0}" + "Please update to provide '{3}' for this value.", Environment.NewLine, attributeName, tmpInt, attributeValueAsString)); } // Fall through for normal boolean conversion } // Enums require special handling... else if (concreteType.IsEnum) { T tmpValue = default(T); try { tmpValue = (T)Enum.Parse(concreteType, attributeValueAsString); if (!Enum.IsDefined(concreteType, tmpValue)) { throw new ArgumentException(); } // If the provided value is a number instead of Enum name, ask the profile writer to fix it... // This is not fatal, so we let it go without flagging IsAttributeProblem. int tmpInt; if (int.TryParse(attributeValueAsString, NumberStyles.Integer, CultureInfo.InvariantCulture, out tmpInt)) { QBCLog.Warning(QBCLog.BuildMessageWithContext(Element, "The '{1}' attribute's value '{2}' has been implicitly converted" + " to the corresponding enumeration '{3}'.{0}" + "Please use the enumeration name '{3}' instead of a number.", Environment.NewLine, attributeName, tmpInt, tmpValue.ToString())); } } catch (Exception) { QBCLog.Error(QBCLog.BuildMessageWithContext(Element, "The value '{1}' is not a member of the {2} enumeration." + " Allowed values: {3}", Environment.NewLine, attributeValueAsString, concreteType.Name, string.Join(", ", Enum.GetNames(concreteType)))); throw; } return(tmpValue); } try { return((T)Convert.ChangeType(attributeValueAsString, concreteType, CultureInfo.InvariantCulture)); } catch (Exception except) { QBCLog.Error(QBCLog.BuildMessageWithContext(Element, "The '{1}' attribute's value (saw '{2}') is malformed. ({3})", Environment.NewLine, attributeName, attributeValueAsString, except.GetType().Name)); throw; } }
/// <summary> /// Behavior for forcing train/mail/vendor/repair /// Example usage: <CustomBehavior QuestId="14324" File="ForceSetVendor" VendorType="Train" /> /// QuestId is optional, if you don't use it make sure you put this tag inside an 'If' /// </summary> public ForceSetVendor(Dictionary <string, string> args) : base(args) { QBCLog.BehaviorLoggingContext = this; try { // Deprecation warnings... if (args.ContainsKey("VendorType")) { QBCLog.Warning("The VendorType attribute has been deprecated.\n" + "Please replace it with DoMail/DoRepair/DoSell/DoTrain='true'"); } // QuestRequirement* attributes are explained here... // http://www.thebuddyforum.com/mediawiki/index.php?title=Honorbuddy_Programming_Cookbook:_QuestId_for_Custom_Behaviors // ...and also used for IsDone processing. DoMail = GetAttributeAsNullable <bool>("DoMail", false, null, null) ?? false; DoRepair = GetAttributeAsNullable <bool>("DoRepair", false, null, null) ?? false; DoSell = GetAttributeAsNullable <bool>("DoSell", false, null, null) ?? false; DoTrain = GetAttributeAsNullable <bool>("DoTrain", false, null, null) ?? false; QuestId = GetAttributeAsNullable <int>("QuestId", false, ConstrainAs.QuestId(this), null) ?? 0; QuestRequirementComplete = GetAttributeAsNullable <QuestCompleteRequirement>("QuestCompleteRequirement", false, null, null) ?? QuestCompleteRequirement.NotComplete; QuestRequirementInLog = GetAttributeAsNullable <QuestInLogRequirement>("QuestInLogRequirement", false, null, null) ?? QuestInLogRequirement.InLog; // "VendorType" attribute is required if no Do* attribute is specified VendorType?type = GetAttributeAsNullable <VendorType>("VendorType", !(DoMail || DoRepair || DoSell || DoTrain), null, null); if (type.HasValue) { switch (type.Value) { case VendorType.Mail: DoMail = true; break; case VendorType.Repair: DoRepair = true; break; case VendorType.Sell: DoSell = true; break; case VendorType.Train: DoTrain = true; break; default: IsAttributeProblem = true; throw (new NotImplementedException("Unexpected VendorType")); } } } 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; } }
private string UtilLocateKey(bool isAttributeRequired, string primaryName, string[] aliasNames) { // Register keys as recognized UtilRecognizeAttributeNames(primaryName, aliasNames); // Make sure the key was only specified once -- // The 'dictionary' nature of Args assures that a key name will only be in the dictionary once. // However, if the key has been renamed, and an alias maintained for backward-compatibility, // then the user could specify the primary key name and one or more aliases as attributes. // If all the aliases provided the same value, then it is harmless, but we don't make the // distinction. Instead, we encourage the user to use the preferred name of the key. This // eliminates any possibility of the user specifying conflicting values for the 'same' attribute. if (UtilCountKeyNames(primaryName, aliasNames) > 1) { var keyNames = new List <string> { primaryName }; keyNames.AddRange(aliasNames); keyNames.Sort(); QBCLog.Error(QBCLog.BuildMessageWithContext(Element, "The attributes [{1}] are aliases for each other, and thus mutually exclusive.{0}" + "Please specify the attribute by its preferred name '{2}'.", Environment.NewLine, ("'" + string.Join("', '", keyNames.ToArray()) + "'"), primaryName)); IsAttributeProblem = true; return(null); } // Prefer the primary name... if (!string.IsNullOrEmpty(primaryName) && Attributes.ContainsKey(primaryName)) { return(primaryName); } if (aliasNames != null) { string keyName = (from aliasName in aliasNames where !string.IsNullOrEmpty(aliasName) && Attributes.ContainsKey(aliasName) select aliasName).FirstOrDefault(); if (!string.IsNullOrEmpty(keyName)) { QBCLog.Warning(QBCLog.BuildMessageWithContext(Element, "Found attribute via its alias name '{1}'.{0}" + "Please update to use its primary name '{2}', instead.", Environment.NewLine, keyName, primaryName)); return(keyName); } } // Attribute is required, but cannot be located... if (isAttributeRequired) { QBCLog.Error(QBCLog.BuildMessageWithContext(Element, "Attribute '{1}' is required, but was not provided.", Environment.NewLine, primaryName)); IsAttributeProblem = true; } return(null); }
public override void OnStart() { // Let QuestBehaviorBase do basic initialization of the behavior, deal with bad or deprecated attributes, // capture configuration state, install BT hooks, etc. This will also update the goal text. var isBehaviorShouldRun = OnStart_QuestBehaviorCore(); // 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. if (isBehaviorShouldRun) { var logInfo = new StringBuilder(); var logDeveloperInfo = new StringBuilder(); // The BotStop handler will put the original configuration settings back in place... // Note, we only want to hook it once for this behavior. if (!s_persistedIsBotStopHooked) { BotEvents.OnBotStopped += BotEvents_OnBotStopped; s_persistedIsBotStopHooked = true; } // First, process Preset request, if any... if (!string.IsNullOrEmpty(PresetName)) { var presetChangeSet = (from preset in _presetChangeSets where preset.Key == PresetName select preset.Value) .FirstOrDefault(); if (presetChangeSet == null) { QBCLog.Error("Unable to locate any preset named '{0}'", PresetName); TreeRoot.Stop(); BehaviorDone(); return; } var appliedChanges = presetChangeSet.Apply(" "); var appliedChangesBuilder = s_persistedDebugShowChangesApplied ? logInfo : logDeveloperInfo; appliedChangesBuilder.AppendFormat("Using preset '{0}'...{1}", PresetName, appliedChanges); appliedChangesBuilder.Append(Environment.NewLine); } // Second, apply any change requests... if (_userChangeRequest.Count > 0) { string appliedChanges = _userChangeRequest.Apply(" "); var appliedChangesBuilder = s_persistedDebugShowChangesApplied ? logInfo : logDeveloperInfo; appliedChangesBuilder.AppendFormat("Applied changes...{0}", appliedChanges); appliedChangesBuilder.Append(Environment.NewLine); } // Third, show state, if requested... if (DebugShowDetails) { var currentConfiguration = ChangeSet.FromCurrentConfiguration(); logInfo.AppendFormat("Details...{0}", currentConfiguration.BuildDetails(" ")); logInfo.Append(Environment.NewLine); } var diffBuilder = DebugShowDiff ? logInfo : logDeveloperInfo; diffBuilder.AppendFormat("Difference from user's original settings...{0}", ChangeSet.BuildDifferencesFromOriginalSettings(" ")); diffBuilder.Append(Environment.NewLine); // Forth, stop the bot, if requested... if (IsStopBot) { const string message = "Stopping the bot per profile request."; logInfo.AppendFormat(message); logInfo.Append(Environment.NewLine); var logInfoString = logInfo.ToString(); if (!string.IsNullOrEmpty(logInfoString)) { QBCLog.Info(logInfoString); } var logDeveloperString = logDeveloperInfo.ToString(); if (!string.IsNullOrEmpty(logDeveloperString)) { QBCLog.DeveloperInfo(logDeveloperString); } TreeRoot.Stop(message); BehaviorDone(); return; } else { var logInfoString = logInfo.ToString(); if (!string.IsNullOrEmpty(logInfoString)) { QBCLog.Info(logInfoString); } var logDeveloperString = logDeveloperInfo.ToString(); if (!string.IsNullOrEmpty(logDeveloperString)) { QBCLog.DeveloperInfo(logDeveloperString); } } BehaviorDone(); } }
private async Task TargetLogic(WoWUnit target) { if (!Query.IsViable(target)) { return; } var targetDistSqr = target.Location.DistanceSqr(Vehicle.Location); if (PickUpPassengerButton == 0) { TreeRoot.StatusText = string.Format("Blowing stuff up. {0} mins before resummon is required", _flightTimer.TimeLeft.TotalMinutes); if (HealButton > 0 && targetDistSqr < 60 * 60 && (Vehicle.HealthPercent <= HealPercent || Vehicle.ManaPercent <= HealPercent) && UseVehicleButton(HealButton)) { QBCLog.Info("Used heal button {0} on NPC:{1}", HealButton, target.SafeName); return; } // return when a button is used. foreach (var button in Buttons) { if (UseVehicleButton(button)) { return; } } return; } TreeRoot.StatusText = string.Format("Rescuing {0}", target.SafeName); var pickTimer = new WaitTimer(TimeSpan.FromSeconds(20)); pickTimer.Reset(); while (target.IsValid && target.IsAlive && !UnitIsRidingMyVehicle(target) && Query.IsInVehicle() && !pickTimer.IsFinished) { WoWPoint clickLocation = target.Location.RayCast(target.Rotation, 6); clickLocation.Z += 3; if (Vehicle.Location.DistanceSqr(clickLocation) > 3 * 3) { Flightor.MoveTo(clickLocation); } else { if (Vehicle.IsMoving) { await CommonCoroutines.StopMoving(string.Format("Picking up {0}", target.SafeName)); } UseVehicleButton(PickUpPassengerButton); if (await Coroutine.Wait(4000, () => UnitIsRidingMyVehicle(target))) { QBCLog.Info("Successfully picked up passenger {0}", target.SafeName); return; } QBCLog.Info("Failed to picked up passenger {0}", target.SafeName); } await Coroutine.Yield(); } }
protected async Task <bool> CombatMainLogic() { if (IsDone) { return(false); } _combatContext.Update(MobId_Kobai, MobId_MalevolentFury); if (Me.Combat) { // If the Blind Rage Trap is not on cooldown, move right next to Kobai and use it... // NB: We don't want to drop the trap unless we're pounding on Kobai if ((_combatContext.Kobai != null) && (Me.CurrentTarget == _combatContext.Kobai) && (_combatContext.BlindingRageTrap != null) && (_combatContext.BlindingRageTrap.CooldownTimeLeft <= TimeSpan.Zero)) { QBCLog.Info("Using Blinding Rage Trap"); if (!_combatContext.Kobai.IsWithinMeleeRange) { return(await UtilityCoroutine.MoveTo(_combatContext.Kobai.Location, "Kobai", MovementByType.NavigatorOnly)); } if (Me.IsMoving) { await CommonCoroutines.StopMoving(); } if (!Me.IsSafelyFacing(_combatContext.Kobai)) { _combatContext.Kobai.Face(); await Coroutine.Sleep(Delay.LagDuration.Milliseconds); } await Coroutine.Sleep(Delay.BeforeButtonClick.Milliseconds); _combatContext.BlindingRageTrap.Use(); await Coroutine.Sleep(Delay.AfterItemUse.Milliseconds); return(true); } // "Steal Mask" aura... // If Kobai is blinded by rage, and the Malevolent Fury is not on the battlefield, // move right next to Kobai, and steal the mask... // NB: We only want to cause one Malevolet Fury to spawn. If we click multiple times // then we get more. So, only click if Fury is not already up. if ((_combatContext.Kobai != null) && Me.HasAura(AuraId_StealMask) && (_combatContext.MalevolentFury == null)) { QBCLog.Info("Pilfering Mask"); if (!_combatContext.Kobai.IsWithinMeleeRange) { return(await UtilityCoroutine.MoveTo(_combatContext.Kobai.Location, "Kobai", MovementByType.NavigatorOnly)); } if (Me.CurrentTargetGuid != _combatContext.Kobai.Guid) { _combatContext.Kobai.Target(); if (!await Coroutine.Wait( 2000, () => _combatContext.Kobai.IsValid && Me.CurrentTarget == _combatContext.Kobai)) { return(false); } } Lua.DoString("ExtraActionButton1:Click()"); await Coroutine.Sleep(Delay.AfterItemUse.Milliseconds); return(true); } } // If we're not in combat, but have found Kobai, move to engage him... else { if (_combatContext.Kobai != null) { // If Kobai is not in kill zone... if (_combatContext.Kobai.Location.Distance(KobaiSafePullAreaAnchor) > KobaiSafePullAreaRadius) { if (await UtilityCoroutine_MoveToStartPosition()) { return(true); } // Wait for Kobai to arrive... QBCLog.Info("Waiting for Kobai to move into kill zone (dist: {0:F1})", Math.Max(_combatContext.Kobai.Location.Distance(KobaiSafePullAreaAnchor) - KobaiSafePullAreaRadius, 0.0)); await Coroutine.Wait(5000, () => Me.Combat); return(true); } // Kobai in kill zone, pull him... if (_combatContext.Kobai.Location.Distance(KobaiSafePullAreaAnchor) <= KobaiSafePullAreaRadius) { if (BotPoi.Current.Type != PoiType.Kill) { QBCLog.Info("Engaging Kobai"); BotPoi.Current = new BotPoi(_combatContext.Kobai, PoiType.Kill); if (Me.CurrentTarget != _combatContext.Kobai) { _combatContext.Kobai.Target(); await Coroutine.Sleep(Delay.LagDuration.Milliseconds); return(true); } } return(false); } // Can't find Kobai--must've just been killed--wait for repop... if (_combatContext.Kobai == null && Navigator.AtLocation(WaitPoint)) { QBCLog.Info("Waiting for Kobai to respawn"); await Coroutine.Wait(5000, () => Me.Combat); return(true); } } if (!UtilIsProgressRequirementsMet(QuestId, QuestRequirementInLog, QuestRequirementComplete)) { BehaviorDone("Finished"); return(true); } // Move to start position, if needed... return(await UtilityCoroutine_MoveToStartPosition()); } return(false); }
public void DLog(string format, params object[] args) { // following linecount hack is to stop dup suppression of Log window QBCLog.DeveloperInfo(format + (++s_lineCount % 2 == 0 ? "" : " "), args); }