/// <summary> /// Check re-use timers on skills, returns true if we can use the power /// </summary> /// <param name="power">The power.</param> /// <param name="recheck">if set to <c>true</c> check again.</param> /// <returns> /// Returns whether or not we can use a skill, or if it's on our own internal Trinity cooldown timer /// </returns> public static bool SNOPowerUseTimer(SNOPower power, bool recheck = false) { if (TimeSinceUse(power) >= CombatBase.GetSNOPowerUseDelay(power)) { return(true); } if (recheck && TimeSinceUse(power) >= 150 && TimeSinceUse(power) <= 600) { return(true); } return(false); }
public void MoveTowards(Vector3 vMoveToTarget) { if (Trinity.Settings.Advanced.DisableAllMovement) { return; } if (!ZetaDia.IsInGame || !ZetaDia.Me.IsValid || ZetaDia.Me.IsDead || ZetaDia.IsLoadingWorld) { return; } if (UISafetyCheck()) { return; } TimeLastUsedPlayerMover = DateTime.UtcNow; LastMoveToTarget = vMoveToTarget; // Set the public variable vMoveToTarget = WarnAndLogLongPath(vMoveToTarget); // Store player current position // Store distance to current moveto target float destinationDistance = MyPosition.Distance2D(vMoveToTarget); // Do unstuckery things if (Trinity.Settings.Advanced.UnstuckerEnabled) { // See if we can reset the 10-limit unstuck counter, if >120 seconds since we last generated an unstuck location // this is used if we're NOT stuck... if (TotalAntiStuckAttempts > 1 && DateTime.UtcNow.Subtract(LastGeneratedStuckPosition).TotalSeconds >= 120) { TotalAntiStuckAttempts = 1; TimesReachedStuckPoint = 0; vSafeMovementLocation = Vector3.Zero; NavHelper.UsedStuckSpots = new List <GridPoint>(); Logger.Log(TrinityLogLevel.Info, LogCategory.Movement, "Resetting unstuck timers", true); } // See if we need to, and can, generate unstuck actions // check if we're stuck bool isStuck = UnstuckChecker(); if (isStuck) { // Record the time we last apparently couldn't move for a brief period of time LastRecordedAnyStuck = DateTime.UtcNow; // See if there's any stuck position to try and navigate to generated by random mover vSafeMovementLocation = UnstuckHandler(MyPosition, vMoveToTarget); if (vSafeMovementLocation == Vector3.Zero) { Logger.Log(TrinityLogLevel.Info, LogCategory.Movement, "Unable to find Unstuck point!", vSafeMovementLocation); return; } Logger.Log(TrinityLogLevel.Verbose, LogCategory.Movement, "SafeMovement Location set to {0}", vSafeMovementLocation); } // See if we can clear the total unstuckattempts if we haven't been stuck in over 6 minutes. if (DateTime.UtcNow.Subtract(LastRecordedAnyStuck).TotalSeconds >= 360) { TimesReachedMaxUnstucks = 0; } // Did we have a safe point already generated (eg from last loop through), if so use it as our current location instead if (vSafeMovementLocation != Vector3.Zero) { // Set our current movement target to the safe point we generated last cycle vMoveToTarget = vSafeMovementLocation; destinationDistance = MyPosition.Distance2D(vMoveToTarget); } // Get distance to current destination // Remove the stuck position if it's been reached, this bit of code also creates multiple stuck-patterns in an ever increasing amount if (vSafeMovementLocation != Vector3.Zero && destinationDistance <= 3f) { vSafeMovementLocation = Vector3.Zero; TimesReachedStuckPoint++; // Do we want to immediately generate a 2nd waypoint to "chain" anti-stucks in an ever-increasing path-length? if (TimesReachedStuckPoint <= TotalAntiStuckAttempts) { vSafeMovementLocation = NavHelper.FindSafeZone(true, TotalAntiStuckAttempts, MyPosition); vMoveToTarget = vSafeMovementLocation; } else { if (Trinity.Settings.Advanced.LogCategories.HasFlag(LogCategory.Movement)) { Logger.Log(TrinityLogLevel.Info, LogCategory.UserInformation, "Clearing old route and trying new path find to: " + LastMoveToTarget.ToString()); } // Reset the path and allow a whole "New" unstuck generation next cycle TimesReachedStuckPoint = 0; // And cancel unstucking for 9 seconds so DB can try to navigate CancelUnstuckerForSeconds = (9 * TotalAntiStuckAttempts); if (CancelUnstuckerForSeconds < 20) { CancelUnstuckerForSeconds = 20; } LastCancelledUnstucker = DateTime.UtcNow; Navigator.Clear(); Navigator.MoveTo(LastMoveToTarget, "original destination", false); return; } } } // don't use special movement within 10 seconds of being stuck bool cancelSpecialMovementAfterStuck = DateTime.UtcNow.Subtract(LastGeneratedStuckPosition).TotalMilliseconds > 10000; // See if we can use abilities like leap etc. for movement out of combat, but not in town if (Trinity.Settings.Combat.Misc.AllowOOCMovement && !Trinity.Player.IsInTown && !Trinity.DontMoveMeIAmDoingShit && cancelSpecialMovementAfterStuck) { bool bTooMuchZChange = (Math.Abs(MyPosition.Z - vMoveToTarget.Z) >= 4f); // Whirlwind for a barb, special context only if (Trinity.Settings.Combat.Barbarian.SprintMode != BarbarianSprintMode.CombatOnly && Trinity.Hotbar.Contains(SNOPower.Barbarian_Whirlwind) && Trinity.ObjectCache.Any(u => u.IsUnit && MathUtil.IntersectsPath(u.Position, u.Radius + 5f, Trinity.Player.Position, vMoveToTarget)) && Trinity.Player.PrimaryResource >= V.F("Barbarian.Whirlwind.MinFury") && !Trinity.IsWaitingForSpecial && V.B("Barbarian.Whirlwind.UseForMovement")) { ZetaDia.Me.UsePower(SNOPower.Barbarian_Whirlwind, vMoveToTarget, Trinity.CurrentWorldDynamicId, -1); SpellHistory.RecordSpell(SNOPower.Barbarian_Whirlwind); if (Trinity.Settings.Advanced.LogCategories.HasFlag(LogCategory.Movement)) { Logger.Log(TrinityLogLevel.Debug, LogCategory.Movement, "Using Whirlwind for OOC movement, distance={0}", destinationDistance); } return; } // Leap movement for a barb if (Trinity.Settings.Combat.Barbarian.UseLeapOOC && Trinity.Hotbar.Contains(SNOPower.Barbarian_Leap) && PowerManager.CanCast(SNOPower.Barbarian_Leap) && !ShrinesInArea(vMoveToTarget)) { Vector3 vThisTarget = vMoveToTarget; if (destinationDistance > 35f) { vThisTarget = MathEx.CalculatePointFrom(vMoveToTarget, MyPosition, 35f); } ZetaDia.Me.UsePower(SNOPower.Barbarian_Leap, vThisTarget, Trinity.CurrentWorldDynamicId, -1); SpellHistory.RecordSpell(SNOPower.Barbarian_Leap); if (Trinity.Settings.Advanced.LogCategories.HasFlag(LogCategory.Movement)) { Logger.Log(TrinityLogLevel.Debug, LogCategory.Movement, "Using Leap for OOC movement, distance={0}", destinationDistance); } return; } // Furious Charge movement for a barb if (Trinity.Settings.Combat.Barbarian.UseChargeOOC && Trinity.Hotbar.Contains(SNOPower.Barbarian_FuriousCharge) && !bTooMuchZChange && destinationDistance >= 20f && PowerManager.CanCast(SNOPower.Barbarian_FuriousCharge) && !ShrinesInArea(vMoveToTarget)) { Vector3 vThisTarget = vMoveToTarget; if (destinationDistance > 35f) { vThisTarget = MathEx.CalculatePointFrom(vMoveToTarget, MyPosition, 35f); } ZetaDia.Me.UsePower(SNOPower.Barbarian_FuriousCharge, vThisTarget, Trinity.CurrentWorldDynamicId, -1); SpellHistory.RecordSpell(SNOPower.Barbarian_FuriousCharge); if (Trinity.Settings.Advanced.LogCategories.HasFlag(LogCategory.Movement)) { Logger.Log(TrinityLogLevel.Debug, LogCategory.Movement, "Using Furious Charge for OOC movement, distance={0}", destinationDistance); } return; } bool hasTacticalAdvantage = HotbarSkills.PassiveSkills.Any(s => s == SNOPower.DemonHunter_Passive_TacticalAdvantage); int vaultDelay = hasTacticalAdvantage ? 2000 : Trinity.Settings.Combat.DemonHunter.VaultMovementDelay; // DemonHunter Vault if (Trinity.Hotbar.Contains(SNOPower.DemonHunter_Vault) && !bTooMuchZChange && Trinity.Settings.Combat.DemonHunter.VaultMode != DemonHunterVaultMode.CombatOnly && CombatBase.TimeSincePowerUse(SNOPower.DemonHunter_Vault) > vaultDelay && destinationDistance >= 18f && PowerManager.CanCast(SNOPower.DemonHunter_Vault) && !ShrinesInArea(vMoveToTarget) && // Don't Vault into avoidance/monsters if we're kiting (CombatBase.PlayerKiteDistance <= 0 || (CombatBase.PlayerKiteDistance > 0 && (!CacheData.TimeBoundAvoidance.Any(a => a.Position.Distance(vMoveToTarget) <= CombatBase.PlayerKiteDistance) || (!CacheData.TimeBoundAvoidance.Any(a => MathEx.IntersectsPath(a.Position, a.Radius, Trinity.Player.Position, vMoveToTarget))) || !CacheData.MonsterObstacles.Any(a => a.Position.Distance(vMoveToTarget) <= CombatBase.PlayerKiteDistance)))) ) { Vector3 vThisTarget = vMoveToTarget; if (destinationDistance > 35f) { vThisTarget = MathEx.CalculatePointFrom(vMoveToTarget, MyPosition, 35f); } ZetaDia.Me.UsePower(SNOPower.DemonHunter_Vault, vThisTarget, Trinity.CurrentWorldDynamicId, -1); SpellHistory.RecordSpell(SNOPower.DemonHunter_Vault); if (Trinity.Settings.Advanced.LogCategories.HasFlag(LogCategory.Movement)) { Logger.Log(TrinityLogLevel.Debug, LogCategory.Movement, "Using Vault for OOC movement, distance={0}", destinationDistance); } return; } // Tempest rush for a monk if (Trinity.Hotbar.Contains(SNOPower.Monk_TempestRush) && (Trinity.Settings.Combat.Monk.TROption == TempestRushOption.MovementOnly || Trinity.Settings.Combat.Monk.TROption == TempestRushOption.Always || (Trinity.Settings.Combat.Monk.TROption == TempestRushOption.TrashOnly && !TargetUtil.AnyElitesInRange(40f)))) { Vector3 vTargetAimPoint = vMoveToTarget; bool canRayCastTarget = true; vTargetAimPoint = TargetUtil.FindTempestRushTarget(); if (!CanChannelTempestRush && ((Trinity.Player.PrimaryResource >= Trinity.Settings.Combat.Monk.TR_MinSpirit && destinationDistance >= Trinity.Settings.Combat.Monk.TR_MinDist) || DateTime.UtcNow.Subtract(CacheData.AbilityLastUsed[SNOPower.Monk_TempestRush]).TotalMilliseconds <= 150) && canRayCastTarget && PowerManager.CanCast(SNOPower.Monk_TempestRush)) { CanChannelTempestRush = true; } else if ((CanChannelTempestRush && (Trinity.Player.PrimaryResource < 10f)) || !canRayCastTarget) { CanChannelTempestRush = false; } double lastUse = DateTime.UtcNow.Subtract(CacheData.AbilityLastUsed[SNOPower.Monk_TempestRush]).TotalMilliseconds; if (CanChannelTempestRush) { if (Trinity.SNOPowerUseTimer(SNOPower.Monk_TempestRush)) { LastTempestRushPosition = vTargetAimPoint; ZetaDia.Me.UsePower(SNOPower.Monk_TempestRush, vTargetAimPoint, Trinity.CurrentWorldDynamicId, -1); SpellHistory.RecordSpell(SNOPower.Monk_TempestRush); // simulate movement speed of 30 SpeedSensor lastSensor = SpeedSensors.OrderByDescending(s => s.Timestamp).FirstOrDefault(); SpeedSensors.Add(new SpeedSensor() { Location = MyPosition, TimeSinceLastMove = new TimeSpan(0, 0, 0, 0, 1000), Distance = 5f, WorldID = Trinity.CurrentWorldDynamicId }); if (Trinity.Settings.Advanced.LogCategories.HasFlag(LogCategory.Movement)) { Logger.Log(TrinityLogLevel.Debug, LogCategory.Movement, "Using Tempest Rush for OOC movement, distance={0:0} spirit={1:0} cd={2} lastUse={3:0} V3={4} vAim={5}", destinationDistance, Trinity.Player.PrimaryResource, PowerManager.CanCast(SNOPower.Monk_TempestRush), lastUse, vMoveToTarget, vTargetAimPoint); } return; } else { return; } } else { if (Trinity.Settings.Advanced.LogCategories.HasFlag(LogCategory.Movement)) { Logger.Log(TrinityLogLevel.Debug, LogCategory.Movement, "Tempest rush failed!: {0:00.0} / {1} distance: {2:00.0} / {3} Raycast: {4} MS: {5:0.0} lastUse={6:0}", Trinity.Player.PrimaryResource, Trinity.Settings.Combat.Monk.TR_MinSpirit, destinationDistance, Trinity.Settings.Combat.Monk.TR_MinDist, canRayCastTarget, GetMovementSpeed(), lastUse); } Trinity.MaintainTempestRush = false; } // Always set this from PlayerMover Trinity.LastTempestRushLocation = vTargetAimPoint; } // Dashing Strike OOC if (CombatBase.CanCast(SNOPower.X1_Monk_DashingStrike) && Trinity.Settings.Combat.Monk.UseDashingStrikeOOC && destinationDistance > 15f) { ZetaDia.Me.UsePower(SNOPower.X1_Monk_DashingStrike, vMoveToTarget, Trinity.CurrentWorldDynamicId, -1); SpellHistory.RecordSpell(SNOPower.X1_Monk_DashingStrike); if (Trinity.Settings.Advanced.LogCategories.HasFlag(LogCategory.Movement)) { Logger.Log(TrinityLogLevel.Debug, LogCategory.Movement, "Using Dashing Strike for OOC movement, distance={0}", destinationDistance); } } bool hasWormHole = HotbarSkills.AssignedSkills.Any(s => s.Power == SNOPower.Wizard_Teleport && s.RuneIndex == 4); bool hasCalamity = HotbarSkills.AssignedSkills.Any(s => s.Power == SNOPower.Wizard_Teleport && s.RuneIndex == 0); // Teleport for a wizard if (!hasCalamity && CombatBase.CanCast(SNOPower.Wizard_Teleport, CombatBase.CanCastFlags.NoTimer) && CombatBase.TimeSincePowerUse(SNOPower.Wizard_Teleport) > 250 && destinationDistance >= 10f && !ShrinesInArea(vMoveToTarget)) { var maxTeleportRange = 75f; Vector3 vThisTarget = vMoveToTarget; if (destinationDistance > maxTeleportRange) { vThisTarget = MathEx.CalculatePointFrom(vMoveToTarget, MyPosition, maxTeleportRange); } ZetaDia.Me.UsePower(SNOPower.Wizard_Teleport, vThisTarget, Trinity.CurrentWorldDynamicId, -1); SpellHistory.RecordSpell(SNOPower.Wizard_Teleport); if (Trinity.Settings.Advanced.LogCategories.HasFlag(LogCategory.Movement)) { Logger.Log(TrinityLogLevel.Debug, LogCategory.Movement, "Using Teleport for OOC movement, distance={0}", destinationDistance); } return; } // Archon Teleport for a wizard if (Trinity.Hotbar.Contains(SNOPower.Wizard_Archon_Teleport) && DateTime.UtcNow.Subtract(CacheData.AbilityLastUsed[SNOPower.Wizard_Archon_Teleport]).TotalMilliseconds >= CombatBase.GetSNOPowerUseDelay(SNOPower.Wizard_Archon_Teleport) && destinationDistance >= 20f && PowerManager.CanCast(SNOPower.Wizard_Archon_Teleport) && !ShrinesInArea(vMoveToTarget)) { Vector3 vThisTarget = vMoveToTarget; if (destinationDistance > 35f) { vThisTarget = MathEx.CalculatePointFrom(vMoveToTarget, MyPosition, 35f); } ZetaDia.Me.UsePower(SNOPower.Wizard_Archon_Teleport, vThisTarget, Trinity.CurrentWorldDynamicId, -1); SpellHistory.RecordSpell(SNOPower.Wizard_Archon_Teleport); if (Trinity.Settings.Advanced.LogCategories.HasFlag(LogCategory.Movement)) { Logger.Log(TrinityLogLevel.Debug, LogCategory.Movement, "Using Archon Teleport for OOC movement, distance={0}", destinationDistance); } return; } } if (MyPosition.Distance2D(vMoveToTarget) > 1f) { // Default movement ZetaDia.Me.UsePower(SNOPower.Walk, vMoveToTarget, Trinity.CurrentWorldDynamicId, -1); if (Trinity.Settings.Advanced.LogCategories.HasFlag(LogCategory.Movement)) { Logger.Log(TrinityLogLevel.Debug, LogCategory.Movement, "Moved to:{0} dir:{1} Speed:{2:0.00} Dist:{3:0} ZDiff:{4:0} CanStand:{5} Raycast:{6}", NavHelper.PrettyPrintVector3(vMoveToTarget), MathUtil.GetHeadingToPoint(vMoveToTarget), MovementSpeed, MyPosition.Distance2D(vMoveToTarget), Math.Abs(MyPosition.Z - vMoveToTarget.Z), Trinity.MainGridProvider.CanStandAt(Trinity.MainGridProvider.WorldToGrid(vMoveToTarget.ToVector2())), !Navigator.Raycast(MyPosition, vMoveToTarget) ); } } else { if (Trinity.Settings.Advanced.LogCategories.HasFlag(LogCategory.Movement)) { Logger.Log(TrinityLogLevel.Debug, LogCategory.Movement, "Reached MoveTowards Destination {0} Current Speed: {1:0.0}", vMoveToTarget, MovementSpeed); } } }
/// <summary> /// This will find a safe place to stand in both Kiting and Avoidance situations /// </summary> /// <param name="isStuck"></param> /// <param name="stuckAttempts"></param> /// <param name="dangerPoint"></param> /// <param name="shouldKite"></param> /// <param name="avoidDeath"></param> /// <returns></returns> internal static Vector3 FindSafeZone(bool isStuck, int stuckAttempts, Vector3 dangerPoint, bool shouldKite = false, IEnumerable <TrinityCacheObject> monsterList = null, bool avoidDeath = false) { if (!isStuck) { if (shouldKite && DateTime.UtcNow.Subtract(lastFoundSafeSpot).TotalMilliseconds <= 1500 && lastSafeZonePosition != Vector3.Zero) { return(lastSafeZonePosition); } else if (DateTime.UtcNow.Subtract(lastFoundSafeSpot).TotalMilliseconds <= 800 && lastSafeZonePosition != Vector3.Zero) { return(lastSafeZonePosition); } hasEmergencyTeleportUp = ( // Leap is available (!PlayerStatus.IsIncapacitated && CombatBase.CanCast(SNOPower.Barbarian_Leap)) || // Whirlwind is available (!PlayerStatus.IsIncapacitated && CombatBase.CanCast(SNOPower.Barbarian_Whirlwind) && ((PlayerStatus.PrimaryResource >= 10 && !PlayerStatus.WaitingForReserveEnergy) || PlayerStatus.PrimaryResource >= Trinity.MinEnergyReserve)) || // Tempest rush is available (!PlayerStatus.IsIncapacitated && CombatBase.CanCast(SNOPower.Monk_TempestRush) && ((PlayerStatus.PrimaryResource >= 20 && !PlayerStatus.WaitingForReserveEnergy) || PlayerStatus.PrimaryResource >= Trinity.MinEnergyReserve)) || // Teleport is available (!PlayerStatus.IsIncapacitated && CombatBase.CanCast(SNOPower.Wizard_Teleport) && PlayerStatus.PrimaryResource >= 15) || // Archon Teleport is available (!PlayerStatus.IsIncapacitated && CombatBase.CanCast(SNOPower.Wizard_Archon_Teleport)) ); // Wizards can look for bee stings in range and try a wave of force to dispel them if (!shouldKite && PlayerStatus.ActorClass == ActorClass.Wizard && Hotbar.Contains(SNOPower.Wizard_WaveOfForce) && PlayerStatus.PrimaryResource >= 25 && DateTime.UtcNow.Subtract(CacheData.AbilityLastUsed[SNOPower.Wizard_WaveOfForce]).TotalMilliseconds >= CombatBase.GetSNOPowerUseDelay(SNOPower.Wizard_WaveOfForce) && !PlayerStatus.IsIncapacitated && CacheData.TimeBoundAvoidance.Count(u => u.ActorSNO == 5212 && u.Position.Distance(PlayerStatus.Position) <= 15f) >= 2 && ( //HotbarSkills.PassiveSkills.Contains(SNOPower.Wizard_Passive_CriticalMass) || PowerManager.CanCast(SNOPower.Wizard_WaveOfForce))) { ZetaDia.Me.UsePower(SNOPower.Wizard_WaveOfForce, Vector3.Zero, PlayerStatus.WorldDynamicID, -1); } } float highestWeight = 0f; if (monsterList == null) { monsterList = new List <TrinityCacheObject>(); } //Vector3 vBestLocation = FindSafeZone(dangerPoint, shouldKite, isStuck, monsterList, avoidDeath); Vector3 vBestLocation = MainFindSafeZone(dangerPoint, shouldKite, isStuck, monsterList, avoidDeath); highestWeight = 1; // Loop through distance-range steps if (highestWeight <= 0) { return(vBestLocation); } lastFoundSafeSpot = DateTime.UtcNow; lastSafeZonePosition = vBestLocation; return(vBestLocation); }