/// <summary> /// Finds an optimal position for using Monk Tempest Rush out of combat /// </summary> /// <returns></returns> internal static Vector3 FindTempestRushTarget() { Vector3 target = PlayerMover.LastMoveToTarget; Vector3 myPos = ZetaDia.Me.Position; if (Trinity.CurrentTarget != null && NavHelper.CanRayCast(myPos, target)) { target = Trinity.CurrentTarget.Position; } float distance = target.Distance2D(myPos); if (distance < 30f) { double direction = MathUtil.FindDirectionRadian(myPos, target); target = MathEx.GetPointAt(myPos, 40f, (float)direction); } return(target); }
/// <summary> /// Special handling for whether or not we want to cache an object that's not in LoS /// </summary> /// <param name="c_diaObject"></param> /// <param name="AddToCache"></param> /// <returns></returns> private static bool RefreshStepIgnoreLoS(bool AddToCache = false) { try { if (CurrentCacheObject.Type == GObjectType.Item || CurrentCacheObject.Type == GObjectType.Gold) { return(true); } if (!DataDictionary.AlwaysRaycastWorlds.Contains(Trinity.Player.WorldID)) { // Bounty Objectives should always be on the weight list if (CurrentCacheObject.IsBountyObjective) { return(true); } // Quest Monsters should get LoS white-listed if (CurrentCacheObject.IsQuestMonster) { return(true); } // Always LoS Units during events if (CurrentCacheObject.Type == GObjectType.Unit && Player.InActiveEvent) { return(true); } } // Everything except items and the current target if (CurrentCacheObject.RActorGuid != LastTargetRactorGUID && CurrentCacheObject.Type != GObjectType.Unknown) { if (CurrentCacheObject.Distance < 95) { using (new PerformanceLogger("RefreshLoS.2")) { // Get whether or not this RActor has ever been in a path line with AllowWalk. If it hasn't, don't add to cache and keep rechecking if (!CacheData.HasBeenRayCasted.TryGetValue(CurrentCacheObject.RActorGuid, out c_HasBeenRaycastable) || DataDictionary.AlwaysRaycastWorlds.Contains(Trinity.Player.WorldID)) { if (CurrentCacheObject.Distance >= 1f && CurrentCacheObject.Distance <= 5f) { c_HasBeenRaycastable = true; if (!CacheData.HasBeenRayCasted.ContainsKey(CurrentCacheObject.RActorGuid)) { CacheData.HasBeenRayCasted.Add(CurrentCacheObject.RActorGuid, c_HasBeenRaycastable); } } else if (Settings.Combat.Misc.UseNavMeshTargeting) { Vector3 myPos = new Vector3(Player.Position.X, Player.Position.Y, Player.Position.Z + 8f); Vector3 cPos = new Vector3(CurrentCacheObject.Position.X, CurrentCacheObject.Position.Y, CurrentCacheObject.Position.Z + 8f); cPos = MathEx.CalculatePointFrom(cPos, myPos, CurrentCacheObject.Radius + 1f); if (Single.IsNaN(cPos.X) || Single.IsNaN(cPos.Y) || Single.IsNaN(cPos.Z)) { cPos = CurrentCacheObject.Position; } if (!NavHelper.CanRayCast(myPos, cPos)) { AddToCache = false; c_IgnoreSubStep = "UnableToRayCast"; } else { c_HasBeenRaycastable = true; if (!CacheData.HasBeenRayCasted.ContainsKey(CurrentCacheObject.RActorGuid)) { CacheData.HasBeenRayCasted.Add(CurrentCacheObject.RActorGuid, c_HasBeenRaycastable); } } } else { if (c_ZDiff > 14f) { AddToCache = false; c_IgnoreSubStep = "LoS.ZDiff"; } else { c_HasBeenRaycastable = true; if (!CacheData.HasBeenRayCasted.ContainsKey(CurrentCacheObject.RActorGuid)) { CacheData.HasBeenRayCasted.Add(CurrentCacheObject.RActorGuid, c_HasBeenRaycastable); } } } } } using (new PerformanceLogger("RefreshLoS.3")) { // Get whether or not this RActor has ever been in "Line of Sight" (as determined by Demonbuddy). If it hasn't, don't add to cache and keep rechecking if (!CacheData.HasBeenInLoS.TryGetValue(CurrentCacheObject.RActorGuid, out c_HasBeenInLoS) || DataDictionary.AlwaysRaycastWorlds.Contains(Trinity.Player.WorldID)) { // Ignore units not in LoS except bosses if (!CurrentCacheObject.IsBoss && !c_diaObject.InLineOfSight) { AddToCache = false; c_IgnoreSubStep = "NotInLoS"; } else { c_HasBeenInLoS = true; if (!CacheData.HasBeenInLoS.ContainsKey(CurrentCacheObject.RActorGuid)) { CacheData.HasBeenInLoS.Add(CurrentCacheObject.RActorGuid, c_HasBeenInLoS); } } } } } else { AddToCache = false; c_IgnoreSubStep = "LoS-OutOfRange"; } // always set true for bosses nearby if (CurrentCacheObject.IsBoss || CurrentCacheObject.IsQuestMonster || CurrentCacheObject.IsBountyObjective) { AddToCache = true; c_IgnoreSubStep = ""; } // always take the current target even if not in LoS if (CurrentCacheObject.RActorGuid == LastTargetRactorGUID) { AddToCache = true; c_IgnoreSubStep = ""; } } // Simple whitelist for LoS if (DataDictionary.LineOfSightWhitelist.Contains(CurrentCacheObject.ActorSNO)) { AddToCache = true; c_IgnoreSubStep = ""; } // Always pickup Infernal Keys whether or not in LoS if (DataDictionary.ForceToItemOverrideIds.Contains(CurrentCacheObject.ActorSNO)) { AddToCache = true; c_IgnoreSubStep = ""; } } catch (Exception ex) { AddToCache = true; c_IgnoreSubStep = "IgnoreLoSException"; Logger.Log(TrinityLogLevel.Debug, LogCategory.CacheManagement, "{0}", ex); } return(AddToCache); }
private static void Monk_MaintainTempestRush() { if (!Monk_TempestRushReady()) { return; } if (Player.IsInTown || Zeta.Bot.Logic.BrainBehavior.IsVendoring) { return; } if (TownRun.IsTryingToTownPortal()) { return; } if (TimeSinceUse(SNOPower.Monk_TempestRush) > 150) { return; } bool shouldMaintain = false; bool nullTarget = CurrentTarget == null; if (!nullTarget) { // maintain for everything except items, doors, interactables... stuff we have to "click" on switch (CurrentTarget.Type) { case GObjectType.Unit: case GObjectType.Gold: case GObjectType.Avoidance: case GObjectType.Barricade: case GObjectType.Destructible: case GObjectType.HealthGlobe: case GObjectType.PowerGlobe: { if (Settings.Combat.Monk.TROption == TempestRushOption.TrashOnly && (TargetUtil.AnyElitesInRange(40f) || CurrentTarget.IsBossOrEliteRareUnique)) { shouldMaintain = false; } else { shouldMaintain = true; } } break; } } else { shouldMaintain = true; } if (Settings.Combat.Monk.TROption != TempestRushOption.MovementOnly && SNOPowerUseTimer(SNOPower.Monk_TempestRush) && shouldMaintain) { Vector3 target = LastTempestRushLocation; const string locationSource = "LastLocation"; if (target.Distance2D(ZetaDia.Me.Position) <= 1f) { // rrrix edit: we can't maintain here return; } if (target == Vector3.Zero) { return; } float DestinationDistance = target.Distance2D(ZetaDia.Me.Position); target = TargetUtil.FindTempestRushTarget(); if (DestinationDistance > 10f && NavHelper.CanRayCast(ZetaDia.Me.Position, target)) { Monk_TempestRushStatus(String.Format("Using Tempest Rush to maintain channeling, source={0}, V3={1} dist={2:0}", locationSource, target, DestinationDistance)); var usePowerResult = ZetaDia.Me.UsePower(SNOPower.Monk_TempestRush, target, CurrentWorldDynamicId, -1); if (usePowerResult) { CacheData.AbilityLastUsed[SNOPower.Monk_TempestRush] = DateTime.UtcNow; } } } }
// Special Zig-Zag movement for whirlwind/tempest /// <summary> /// Finds an optimal position for Barbarian Whirlwind, Monk Tempest Rush, or Demon Hunter Strafe /// </summary> /// <param name="origin"></param> /// <param name="ringDistance"></param> /// <param name="randomizeDistance"></param> /// <returns></returns> internal static Vector3 GetZigZagTarget(Vector3 origin, float ringDistance, bool randomizeDistance = false) { var minDistance = 20f; Vector3 myPos = Player.Position; float distanceToTarget = origin.Distance2D(myPos); Vector3 zigZagPoint = origin; bool useTargetBasedZigZag = false; float maxDistance = 25f; int minTargets = 2; if (Trinity.Player.ActorClass == ActorClass.Monk) { maxDistance = 20f; minTargets = 3; useTargetBasedZigZag = Trinity.Settings.Combat.Monk.TargetBasedZigZag; } if (Trinity.Player.ActorClass == ActorClass.Barbarian) { useTargetBasedZigZag = Trinity.Settings.Combat.Barbarian.TargetBasedZigZag; } int eliteCount = ObjectCache.Count(u => u.IsUnit && u.IsBossOrEliteRareUnique); bool shouldZigZagElites = ((Trinity.CurrentTarget.IsBossOrEliteRareUnique && eliteCount > 1) || eliteCount == 0); if (useTargetBasedZigZag && shouldZigZagElites && !AnyTreasureGoblinsPresent && ObjectCache.Count(o => o.IsUnit) >= minTargets) { bool attackInAoe = Trinity.Settings.Combat.Misc.KillMonstersInAoE; var clusterPoint = TargetUtil.GetBestClusterPoint(ringDistance, ringDistance, false, attackInAoe); if (clusterPoint.Distance2D(Player.Position) >= minDistance) { Logger.Log(LogCategory.Movement, "Returning ZigZag: BestClusterPoint {0} r-dist={1} t-dist={2}", clusterPoint, ringDistance, clusterPoint.Distance2D(Player.Position)); return(clusterPoint); } var zigZagTargetList = new List <TrinityCacheObject>(); if (attackInAoe) { zigZagTargetList = (from u in ObjectCache where u.IsUnit && u.Distance < maxDistance select u).ToList(); } else { zigZagTargetList = (from u in ObjectCache where u.IsUnit && u.Distance < maxDistance && !UnitOrPathInAoE(u) select u).ToList(); } if (zigZagTargetList.Count() >= minTargets) { zigZagPoint = zigZagTargetList.OrderByDescending(u => u.Distance).FirstOrDefault().Position; if (NavHelper.CanRayCast(zigZagPoint) && zigZagPoint.Distance2D(Player.Position) >= minDistance) { Logger.Log(LogCategory.Movement, "Returning ZigZag: TargetBased {0} r-dist={1} t-dist={2}", zigZagPoint, ringDistance, zigZagPoint.Distance2D(Player.Position)); return(zigZagPoint); } } } Random rndNum = new Random(int.Parse(Guid.NewGuid().ToString().Substring(0, 8), NumberStyles.HexNumber)); float highestWeightFound = float.NegativeInfinity; Vector3 bestLocation = origin; // the unit circle always starts at 0 :) double min = 0; // the maximum size of a unit circle double max = 2 * Math.PI; // the number of times we will iterate around the circle to find points double piSlices = 16; // We will do several "passes" to make sure we can get a point that we can least zig-zag to // The total number of points tested will be piSlices * distancePasses.Count List <float> distancePasses = new List <float>(); distancePasses.Add(ringDistance * 1 / 2); // Do one loop at 1/2 distance distancePasses.Add(ringDistance * 3 / 4); // Do one loop at 3/4 distance distancePasses.Add(ringDistance); // Do one loop at exact distance foreach (float distance in distancePasses) { for (double direction = min; direction < max; direction += (Math.PI / piSlices)) { // Starting weight is 1 float pointWeight = 1f; // Find a new XY zigZagPoint = MathEx.GetPointAt(origin, distance, (float)direction); // Get the Z zigZagPoint.Z = Trinity.MainGridProvider.GetHeight(zigZagPoint.ToVector2()); // Make sure we're actually zig-zagging our target, except if we're kiting float targetCircle = CurrentTarget.Radius; if (targetCircle <= 5f) { targetCircle = 5f; } if (targetCircle > 10f) { targetCircle = 10f; } bool intersectsPath = MathUtil.IntersectsPath(CurrentTarget.Position, targetCircle, myPos, zigZagPoint); if (CombatBase.PlayerKiteDistance <= 0 && !intersectsPath) { continue; } // if we're kiting, lets not actualy run through monsters if (CombatBase.PlayerKiteDistance > 0 && CacheData.MonsterObstacles.Any(m => m.Position.Distance(zigZagPoint) <= CombatBase.PlayerKiteDistance)) { continue; } // Ignore point if any AoE in this point position if (CacheData.TimeBoundAvoidance.Any(m => m.Position.Distance(zigZagPoint) <= m.Radius && Player.CurrentHealthPct <= AvoidanceManager.GetAvoidanceHealthBySNO(m.ActorSNO, 1))) { continue; } // Make sure this point is in LoS/walkable (not around corners or into a wall) bool canRayCast = !Navigator.Raycast(Player.Position, zigZagPoint); if (!canRayCast) { continue; } float distanceToPoint = zigZagPoint.Distance2D(myPos); float distanceFromTargetToPoint = zigZagPoint.Distance2D(origin); // Lots of weight for points further away from us (e.g. behind our CurrentTarget) pointWeight *= distanceToPoint; // Add weight for any units in this point int monsterCount = ObjectCache.Count(u => u.IsUnit && u.Position.Distance2D(zigZagPoint) <= Math.Max(u.Radius, 10f)); if (monsterCount > 0) { pointWeight *= monsterCount; } //Logger.Log(LogCategory.Movement, "ZigZag Point: {0} distance={1:0} distaceFromTarget={2:0} intersectsPath={3} weight={4:0} monsterCount={5}", // zigZagPoint, distanceToPoint, distanceFromTargetToPoint, intersectsPath, pointWeight, monsterCount); // Use this one if it's more weight, or we haven't even found one yet, or if same weight as another with a random chance if (pointWeight > highestWeightFound) { highestWeightFound = pointWeight; if (Trinity.Settings.Combat.Misc.UseNavMeshTargeting) { bestLocation = new Vector3(zigZagPoint.X, zigZagPoint.Y, Trinity.MainGridProvider.GetHeight(zigZagPoint.ToVector2())); } else { bestLocation = new Vector3(zigZagPoint.X, zigZagPoint.Y, zigZagPoint.Z + 4); } } } } Logger.Log(LogCategory.Movement, "Returning ZigZag: RandomXY {0} r-dist={1} t-dist={2}", bestLocation, ringDistance, bestLocation.Distance2D(Player.Position)); return(bestLocation); }