/// <summary> /// Mounts a vehicle. /// </summary> /// <param name="vehicleId">The vehicle identifier.</param> /// <param name="searchLocation">The search location.</param> /// <param name="movementBy">The movement by.</param> /// <param name="extraVehicleQualifiers">The extra vehicle qualifiers.</param> /// <returns></returns> public static async Task <bool> MountVehicle( int vehicleId, Vector3 searchLocation, MovementByType movementBy = MovementByType.FlightorPreferred, Func <WoWUnit, bool> extraVehicleQualifiers = null) { return(await MountVehicle(searchLocation, movementBy, extraVehicleQualifiers, vehicleId)); }
// 30May2013-08:11UTC chinajade public static bool IsStateMatch_MeshNavigable(WoWObject wowObject, MovementByType movementBy) { Contract.Requires(wowObject != null, context => "wowObject != null"); return ((movementBy != MovementByType.NavigatorOnly) || Navigator.CanNavigateFully(StyxWoW.Me.Location, wowObject.Location)); }
// 30May2013-08:11UTC chinajade public static bool IsStateMatch_MeshNavigable(WoWObject wowObject, MovementByType movementBy) { Contract.Requires(wowObject != null, context => "wowObject != null"); return ((movementBy != MovementByType.NavigatorOnly) // TODO: Rewrite usages to examine paths after generation instead || /*Navigator.CanNavigateFully(StyxWoW.Me.Location, wowObject.Location)*/ true); }
public static async Task <bool> MoveTo( HuntingGroundsType huntingGrounds, MovementByType movementBy = MovementByType.FlightorPreferred) { Contract.Requires(huntingGrounds != null, context => "huntingGrounds may not be null"); var destination = huntingGrounds.CurrentWaypoint().Location; var destinationName = String.Format("hunting ground waypoint '{0}'", huntingGrounds.CurrentWaypoint().Name); return(await MoveTo(destination, destinationName, movementBy)); }
/// <summary>Buys an item from the specified wow object.</summary> /// <param name="wowObject">The wow object. Navigates to <paramref name="searchLocation" /> null</param> /// <param name="searchLocation">The search location of <paramref name="wowObject" />.</param> /// <param name="itemId">The item identifier.</param> /// <param name="quantity">The quantity to buy.</param> /// <param name="movementBy">The movement type to use.</param> /// <param name="navigationFailedAction"> /// The action to take if <paramref name="wowObject" /> or /// <paramref name="searchLocation" /> cant be navigated to /// </param> /// <param name="notFoundAction"> /// The action to take if /// <paramref name="wowObject" /> is not found at /// <paramref name="searchLocation" />. /// </param> /// <param name="noVendorFrameAction"> /// The action to take if interaction with /// <paramref name="wowObject" /> didn't open a vendor frame. /// </param> /// <param name="itemNotFoundAction">The action to take if <paramref name="wowObject"/> does not offer <paramref name="itemId"/> </param> /// <param name="insufficientFundsAction">The action to take if toon doesn't have enough funds to buy <paramref name="itemId"/> </param> /// <returns></returns> /// <exception cref="Exception">A delegate callback throws an exception.</exception> public static async Task <bool> BuyItem( WoWObject wowObject, Vector3 searchLocation, int itemId, int quantity, MovementByType movementBy = MovementByType.FlightorPreferred, Action navigationFailedAction = null, Action notFoundAction = null, Action noVendorFrameAction = null, Action itemNotFoundAction = null, Action insufficientFundsAction = null) { if (!MerchantFrame.Instance.IsVisible) { return(await Gossip( wowObject, searchLocation, movementBy, navigationFailedAction, notFoundAction, null, noVendorFrameAction, GossipEntry.GossipEntryType.Vendor)); } var item = MerchantFrame.Instance.GetAllMerchantItems().FirstOrDefault(i => i.ItemId == itemId); if (item == null) { if (itemNotFoundAction != null) { itemNotFoundAction(); } return(false); } if (!MerchantFrame.Instance.BuyItem(item.Index, quantity)) { if (insufficientFundsAction != null) { insufficientFundsAction(); } return(false); } await CommonCoroutines.SleepForRandomUiInteractionTime(); MerchantFrame.Instance.Close(); await CommonCoroutines.SleepForLagDuration(); return(true); }
/// <summary>Turns in a quest at object </summary> /// <param name="wowObject"> The turnin object. </param> /// <param name="questId"> The quest Id. If 0 (default) then first completed quest is turned in. </param> /// <param name="searchLocation">The search location of <paramref name="wowObject" />.</param> /// <param name="movementBy">The movement type to use.</param> /// <param name="navigationFailedAction"> /// The action to take if <paramref name="wowObject" /> or <paramref name="searchLocation"/> cant be navigated to /// </param> /// <param name="notFoundAction"> /// The action to take if <paramref name="wowObject" /> is not found at /// <paramref name="searchLocation" />. /// </param> /// <returns><c>true</c> if an action was taken; <c>false</c> otherwise</returns> /// <exception cref="Exception">A delegate callback throws an exception.</exception> public static async Task <bool> TurninQuest( WoWObject wowObject, WoWPoint searchLocation, uint questId = 0, MovementByType movementBy = MovementByType.FlightorPreferred, Action navigationFailedAction = null, Action notFoundAction = null) { if (wowObject == null) { if (!Navigator.AtLocation(searchLocation)) { if (await MoveTo(searchLocation, "Quest turnin search area", movementBy)) { return(true); } if (navigationFailedAction != null) { navigationFailedAction(); } return(false); } if (notFoundAction != null) { notFoundAction(); } else { TreeRoot.StatusText = "Waiting for the WoW object selected for quest turnin to spawn"; } return(true); } if (!wowObject.WithinInteractRange) { if (await MoveTo(wowObject.Location, wowObject.SafeName, movementBy)) { return(true); } if (navigationFailedAction != null) { navigationFailedAction(); } return(false); } return(await ScriptHelpers.TurninQuest(wowObject, questId)); }
/// <summary>Turns in a quest at object </summary> /// <param name="wowObjectId"> The turnin object. </param> /// <param name="questId"> The quest Id. If 0 (default) then first completed quest is turned in. </param> /// <param name="searchLocation">The search location of <paramref name="wowObjectId" />.</param> /// <param name="movementBy">The movement type to use.</param> /// <param name="navigationFailedAction"> /// The action to take if <paramref name="wowObjectId" /> or <paramref name="searchLocation"/> cant be navigated to /// </param> /// <param name="notFoundAction"> /// The action to take if <paramref name="wowObjectId" /> is not found at /// <paramref name="searchLocation" />. /// </param> /// <returns><c>true</c> if an action was taken; <c>false</c> otherwise</returns> /// <exception cref="Exception">A delegate callback throws an exception.</exception> public static async Task <bool> TurninQuest( int wowObjectId, WoWPoint searchLocation, uint questId = 0, MovementByType movementBy = MovementByType.FlightorPreferred, Action navigationFailedAction = null, Action notFoundAction = null) { return(await TurninQuest( ObjectManager.ObjectList.Where(o => o.Entry == wowObjectId).OrderBy(o => o.DistanceSqr).FirstOrDefault(), searchLocation, questId, movementBy, navigationFailedAction, notFoundAction)); }
/// <summary> /// Mounts a vehicle /// </summary> /// <param name="searchLocation">The search location.</param> /// <param name="movementBy">The movement type.</param> /// <param name="extraVehicleQualifiers">The extra vehicle qualifiers.</param> /// <param name="vehicleIds">The vehicle ids.</param> /// <returns> /// <c>true</c> if any action was taken; <c>false</c> otherwise /// </returns> public static async Task <bool> MountVehicle( Vector3 searchLocation, MovementByType movementBy = MovementByType.FlightorPreferred, Func <WoWUnit, bool> extraVehicleQualifiers = null, params int[] vehicleIds) { if (Query.IsInVehicle()) { return(false); } var vehicle = Query.FindUnoccupiedVehicles(vehicleIds, extraVehicleQualifiers).FirstOrDefault(); if (vehicle == null) { if (!Navigator.AtLocation(searchLocation)) { return(await MoveTo(searchLocation, "Vehicle search area", movementBy)); } await (s_mountVehicleUserUpdateThrottle ?? (s_mountVehicleUserUpdateThrottle = new ThrottleCoroutineTask( TimeSpan.FromSeconds(10), async() => QBCLog.Info("Waiting for a vehicle to become available")))); return(true); } if (!vehicle.WithinInteractRange) { return(await MoveTo(vehicle.Location, vehicle.SafeName, movementBy, vehicle.InteractRange)); } if (await CommonCoroutines.Dismount("Getting inside vehicle")) { await Coroutine.Sleep(Delay.BeforeButtonClick); } vehicle.Interact(); await Coroutine.Sleep(Delay.AfterInteraction); return(true); }
/// <summary> /// <para>Gossips with the specified wow object. </para> /// <para>Hearthstone bind popups are automatically accepted</para> /// </summary> /// <param name="wowObjectId">The wow object identifier.</param> /// <param name="searchLocation">The search location of <paramref name="wowObjectId" />.</param> /// <param name="movementBy">The movement type to use.</param> /// <param name="navigationFailedAction"> /// The action to take if <paramref name="wowObjectId" /> or <paramref name="searchLocation"/> cant be navigated to /// </param> /// <param name="notFoundAction">The action to take if /// <paramref name="wowObjectId" /> is not found at /// <paramref name="searchLocation" />.</param> /// <param name="noGossipFrameAction">The action to take if interaction with /// <paramref name="wowObjectId" /> didn't open a gossip frame.</param> /// <param name="noMatchingGossipOptionAction"> /// <para>The action to take if the passed in gossip type and/or gossip indices </para> /// <para>doesn't match what was offered by <paramref name="wowObjectId" />.</para> /// </param> /// <param name="gossipEntryType"> /// <para>Type gossip entry type to select. If none of this type are found on current page then</para> /// <para> normal gossip types are clicked through in hopes of ending on a page with this gossip type</para> /// </param> /// <param name="gossipIndexes">The gossip indexes to follow through. Has precedence over /// <paramref name="gossipEntryType" />.</param> /// <returns></returns> /// <exception cref="Exception">A delegate callback throws an exception.</exception> public static async Task <bool> Gossip( int wowObjectId, WoWPoint searchLocation, MovementByType movementBy = MovementByType.FlightorPreferred, Action navigationFailedAction = null, Action notFoundAction = null, Action noGossipFrameAction = null, Action noMatchingGossipOptionAction = null, GossipEntry.GossipEntryType gossipEntryType = GossipEntry.GossipEntryType.Unknown, params int[] gossipIndexes) { return(await Gossip( ObjectManager.ObjectList .Where(o => o.Entry == wowObjectId) .OrderBy(o => o.DistanceSqr).FirstOrDefault(), searchLocation, movementBy, navigationFailedAction, notFoundAction, noGossipFrameAction, noMatchingGossipOptionAction, gossipEntryType, gossipIndexes)); }
/// <summary>Gossips with the specified wow object. Hearthstone bind popups are automatically accepted</summary> /// <param name="wowObjectId">The wow object identifier.</param> /// <param name="searchLocation">The search location of <paramref name="wowObjectId" />.</param> /// <param name="itemId">The item identifier.</param> /// <param name="quantity">The quantity to buy.</param> /// <param name="movementBy">The movement type to use.</param> /// <param name="navigationFailedAction"> /// The action to take if <paramref name="wowObjectId" /> or <paramref name="searchLocation"/> cant be navigated to /// </param> /// <param name="notFoundAction">The action to take if /// <paramref name="wowObjectId" /> is not found at /// <paramref name="searchLocation" />.</param> /// <param name="noVendorFrameAction">The action to take if interaction with /// <paramref name="wowObjectId" /> didn't open a vendor frame. /// </param> /// <param name="itemNotFoundAction">The action to take if <paramref name="wowObjectId"/> does not offer <paramref name="itemId"/> </param> /// <param name="insufficientFundsAction">The action to take if toon doesn't have enough funds to buy <paramref name="itemId"/> </param> /// <returns></returns> /// <exception cref="Exception">A delegate callback throws an exception.</exception> public static async Task <bool> BuyItem( int wowObjectId, Vector3 searchLocation, int itemId, int quantity, MovementByType movementBy = MovementByType.FlightorPreferred, Action navigationFailedAction = null, Action notFoundAction = null, Action noVendorFrameAction = null, Action itemNotFoundAction = null, Action insufficientFundsAction = null) { return(await BuyItem( ObjectManager.ObjectList.FirstOrDefault(o => o.Entry == wowObjectId), searchLocation, itemId, quantity, movementBy, navigationFailedAction, notFoundAction, noVendorFrameAction, itemNotFoundAction, insufficientFundsAction)); }
/// <summary>Gossips with the specified wow object. Hearthstone bind popups are automatically accepted</summary> /// <param name="wowObject">The wow object. Navigates to <paramref name="searchLocation" /> null </param> /// <param name="searchLocation">The search location of <paramref name="wowObject" />.</param> /// <param name="movementBy">The movement type to use.</param> /// <param name="navigationFailedAction"> /// The action to take if <paramref name="wowObject" /> or <paramref name="searchLocation"/> cant be navigated to /// </param> /// <param name="notFoundAction"> /// The action to take if <paramref name="wowObject" /> is not found at /// <paramref name="searchLocation" />. /// </param> /// <param name="noGossipFrameAction"> /// The action to take if interaction with <paramref name="wowObject" /> didn't open a /// gossip frame. /// </param> /// <param name="noMatchingGossipOptionAction"> /// <para>The action to take if the passed in gossip type and/or gossip indices </para> /// <para>doesn't match what was offered by <paramref name="wowObject" />.</para> /// </param> /// <param name="gossipEntryType"> /// <para>Type gossip entry type to select. Ignored if set to Unknown.</para> /// <para>If none of this type are found on current page then</para> /// <para> normal gossip types are clicked through in hopes of ending on a page with this gossip type</para> /// </param> /// <param name="gossipIndexes"> /// The gossip indexes to follow through. Has precedence over /// <paramref name="gossipEntryType" />. /// </param> /// <exception cref="Exception">A delegate callback throws an exception.</exception> public static async Task <bool> Gossip( WoWObject wowObject, WoWPoint searchLocation, MovementByType movementBy = MovementByType.FlightorPreferred, Action navigationFailedAction = null, Action notFoundAction = null, Action noGossipFrameAction = null, Action noMatchingGossipOptionAction = null, GossipEntry.GossipEntryType gossipEntryType = GossipEntry.GossipEntryType.Unknown, params int[] gossipIndexes) { if (wowObject == null) { if (!Navigator.AtLocation(searchLocation)) { if (await MoveTo(searchLocation, "Gossip object search area", movementBy)) { return(true); } navigationFailedAction?.Invoke(); return(false); } if (notFoundAction != null) { notFoundAction(); } else { TreeRoot.StatusText = "Waiting for the WoW object selected for gossip to spawn"; } return(true); } if (!wowObject.WithinInteractRange) { if (await MoveTo(wowObject.Location, wowObject.SafeName, movementBy)) { return(true); } navigationFailedAction?.Invoke(); return(false); } if (await CommonCoroutines.Dismount("Gossiping with " + wowObject.SafeName)) { await Coroutine.Sleep(Delay.BeforeButtonClick); } // If gossip frame is open then we must assume that it doesn't belong to the selected gossip object at this point if (GossipFrame.Instance.IsVisible) { GossipFrame.Instance.Close(); return(true); } Func <bool> isFrameReadyForInput = () => GossipFrame.Instance.IsVisible && (GossipFrame.Instance.GossipOptionEntries != null || (!gossipIndexes.Any() && gossipEntryType == GossipEntry.GossipEntryType.Unknown)); wowObject.Interact(); var openedGossipFrame = await Coroutine.Wait(3000, isFrameReadyForInput); if (!openedGossipFrame) { QBCLog.Warning("No gossip frame was opened after interacting with {0}", wowObject.SafeName); noGossipFrameAction?.Invoke(); return(false); } int gossipPage = 1; // Click through all the gossip indices for (var i = 0; i < gossipIndexes.Length; i++) { var index = gossipIndexes[i] - 1; var gossipEntry = GossipFrame.Instance.GossipOptionEntries.Where(g => g.Index == index) .Select(g => (GossipEntry?)g) .FirstOrDefault(); if (!gossipEntry.HasValue || gossipEntry.Value.Type == GossipEntry.GossipEntryType.Unknown) { QBCLog.Warning("{0} does not provide a gossip at index {1} on page {2}", wowObject.SafeName, index + 1, gossipPage); noMatchingGossipOptionAction?.Invoke(); return(false); } await ClickGossipOption(gossipEntry.Value, gossipPage); // make sure frame didn't close before we're done. if (!isFrameReadyForInput() && (i < gossipIndexes.Length - 1 || gossipEntryType != GossipEntry.GossipEntryType.Unknown)) { // This can happen if some external event causes object to stop offering gossip frame, such as NPC getting into combat. // Usually this can be fixed by interacting with object again at a later time. We let the caller handle this. QBCLog.Warning("Gossip frame for {0} closed unexpectedly.", wowObject.SafeName); return(true); } gossipPage++; } if (gossipEntryType != GossipEntry.GossipEntryType.Unknown) { if (!gossipIndexes.Any()) { while (true) { var gossipEntry = GossipFrame.Instance.GossipOptionEntries.FirstOrDefault(g => g.Type == gossipEntryType); // If no gossip indices were specified then we just click through more gossip, // hopefully it leads to the final gossip type if (gossipEntry.Type != gossipEntryType) { gossipEntry = GossipFrame.Instance.GossipOptionEntries.FirstOrDefault(g => g.Type == GossipEntry.GossipEntryType.Gossip); } if (gossipEntry.Type == GossipEntry.GossipEntryType.Unknown) { QBCLog.Warning("{0} does not provide a {0} gossip type", wowObject.SafeName, gossipEntryType); noMatchingGossipOptionAction?.Invoke(); return(false); } await ClickGossipOption(gossipEntry, gossipPage); if (!isFrameReadyForInput() && gossipEntry.Type != gossipEntryType) { // This can happen if some external event causes object to stop offering gossip frame, such as NPC getting into combat. // Usually this can be fixed by interacting with object again at a later time. We let the caller handle this. QBCLog.Warning("Gossip frame for {0} closed unexpectedly.", wowObject.SafeName); return(true); } if (gossipEntry.Type == gossipEntryType) { break; } gossipPage++; } } } // Set hearthstone automatically const string setHsPopupName = "CONFIRM_BINDER"; if (Lua.GetReturnVal <bool>($"return StaticPopup_Visible('{setHsPopupName}')", 0)) { uint hsId = StyxWoW.Me.HearthstoneAreaId; Lua.DoString( $"local _,frame = StaticPopup_Visible('{setHsPopupName}') if frame then StaticPopup_OnClick(frame, 1) end"); if (await Coroutine.Wait(5000, () => StyxWoW.Me.HearthstoneAreaId != hsId)) { await CommonCoroutines.SleepForRandomReactionTime(); var boundLocation = Lua.GetReturnVal <string>("return GetBindLocation()", 0); QBCLog.Info( "You are now bound at {0} Inn in {1}({2})", (Query.IsViable(wowObject) ? wowObject.SafeName : "the"), boundLocation, Me.HearthstoneAreaId); } } return(true); }
public static async Task <bool> MoveTo( WoWPoint destination, string destinationName, MovementByType movementBy = MovementByType.FlightorPreferred) { Contract.Requires(destinationName != null, context => "destinationName may not be null"); if (movementBy == MovementByType.None) { return(false); } var activeMover = WoWMovement.ActiveMover; if (activeMover == null) { return(false); } if (!IsMoveToMessageThrottled) { if (string.IsNullOrEmpty(destinationName)) { destinationName = destination.ToString(); } TreeRoot.StatusText = "Moving to " + destinationName; } switch (movementBy) { case MovementByType.FlightorPreferred: if (await TryFlightor(destination)) { return(true); } if (await TryNavigator(destination, destinationName)) { return(true); } if (await TryClickToMove(destination, NavType.Fly)) { return(true); } break; case MovementByType.NavigatorPreferred: if (await TryNavigator(destination, destinationName)) { return(true); } if (await TryClickToMove(destination, NavType.Run)) { return(true); } break; case MovementByType.NavigatorOnly: if (await TryNavigator(destination, destinationName)) { return(true); } break; case MovementByType.ClickToMoveOnly: var navType = activeMover.MovementInfo.CanFly ? NavType.Fly : NavType.Run; if (await TryClickToMove(destination, navType)) { return(true); } break; case MovementByType.None: break; default: QBCLog.MaintenanceError("Unhandled MovementByType of {0}", movementBy); break; } return(false); }
/// <summary> /// Uses the transport. /// </summary> /// <param name="transportId">The transport identifier.</param> /// <param name="transportStartLoc">The start location.</param> /// <param name="transportEndLoc">The end location.</param> /// <param name="waitAtLoc">The wait at location.</param> /// <param name="boardAtLoc">The stand at location.</param> /// <param name="getOffLoc">The get off location.</param> /// <param name="movement">The movement.</param> /// <param name="destination">The destination.</param> /// <param name="navigationFailedAction"> /// The action to take if <paramref name="waitAtLoc" /> cant be navigated to /// </param> /// <returns>returns <c>true</c> until done</returns> /// <exception cref="Exception">A delegate callback throws an exception. </exception> public static async Task <bool> UseTransport( int transportId, Vector3 transportStartLoc, Vector3 transportEndLoc, Vector3 waitAtLoc, Vector3 boardAtLoc, Vector3 getOffLoc, MovementByType movement = MovementByType.FlightorPreferred, string destination = null, Action navigationFailedAction = null) { if (getOffLoc != Vector3.Zero && Me.Location.DistanceSquared(getOffLoc) < 2 * 2) { return(false); } var transportLocation = GetTransportLocation(transportId); if (transportLocation != Vector3.Zero && transportLocation.DistanceSquared(transportStartLoc) < 1.5 * 1.5 && waitAtLoc.DistanceSquared(Me.Location) < 2 * 2) { TreeRoot.StatusText = "Moving inside transport"; Navigator.PlayerMover.MoveTowards(boardAtLoc); await CommonCoroutines.SleepForLagDuration(); // wait for bot to get on boat. await Coroutine.Wait(12000, () => !Me.IsMoving || Navigator.AtLocation(boardAtLoc)); } // loop while on transport to prevent bot from doing anything else while (Me.Transport != null && Me.Transport.Entry == transportId) { if (transportLocation != Vector3.Zero && transportLocation.DistanceSquared(transportEndLoc) < 1.5 * 1.5) { TreeRoot.StatusText = "Moving out of transport"; Navigator.PlayerMover.MoveTowards(getOffLoc); await CommonCoroutines.SleepForLagDuration(); // Sleep until we stop moving. await Coroutine.Wait(12000, () => !Me.IsMoving || Navigator.AtLocation(getOffLoc)); return(true); } // Exit loop if in combat or dead. if (Me.Combat || !Me.IsAlive) { return(false); } TreeRoot.StatusText = "Waiting for the end location"; await Coroutine.Yield(); // update transport location. transportLocation = GetTransportLocation(transportId); } if (waitAtLoc.DistanceSquared(Me.Location) > 2 * 2) { if (!await MoveTo(waitAtLoc, destination ?? waitAtLoc.ToString(), movement)) { navigationFailedAction?.Invoke(); } return(true); } await CommonCoroutines.LandAndDismount(); TreeRoot.StatusText = "Waiting for transport"; return(true); }