/// <summary> /// Calculates and returns the expected real world seconds until the node despawns /// </summary> /// <param name="node"></param> /// <returns></returns> public static NodeLifespan GetNodeLifespan(GatheringPointObject node) { var eorzeaMinutesTillDespawn = (int)byte.MaxValue; if (node.IsUnspoiled()) { if (WorldManager.ZoneId > 350) { eorzeaMinutesTillDespawn = 55 - WorldManager.EorzaTime.Minute; } else { // We really don't know how much time is left on the node, but it does have at least the 5 more EM. eorzeaMinutesTillDespawn = 60 - WorldManager.EorzaTime.Minute; } } if (node.IsEphemeral()) { var hoursFromNow = WorldManager.EorzaTime.AddHours(4); var rounded = new DateTime( hoursFromNow.Year, hoursFromNow.Month, hoursFromNow.Day, hoursFromNow.Hour - hoursFromNow.Hour % 4, 0, 0); eorzeaMinutesTillDespawn = (int)(rounded - WorldManager.EorzaTime).TotalMinutes; } return(new NodeLifespan(eorzeaMinutesTillDespawn * 35 / 12)); }
internal void ResetInternal() { interactedWithNode = false; GatherSpot = null; Node = null; GatherItem = null; CollectableItem = null; }
/// <summary> /// Executes the strategy logic in charge of regenerating GP after the gather. /// </summary> /// <returns></returns> public async Task <GpRegenStrategyResult> RegenerateGp(GatheringPointObject node, IGatheringRotation gatherRotation, GatherStrategy gatherStrategy, CordialTime cordialTime, CordialType cordialType) { var rtn = new GpRegenStrategyResult() { StrategyState = GpRegenStrategyResult.GpRegenStrategyResultState.OK, EffectiveCordialType = cordialType, OriginalCordialType = cordialType, }; var useCordial = cordialTime.HasFlag(CordialTime.AfterGather) && this.cordialStock.HasStock() && this.cordialStock.GetCordialCooldown() == TimeSpan.Zero; var currentGp = ExProfileBehavior.Me.CurrentGP; var maxGp = ExProfileBehavior.Me.MaxGP; var missingGp = maxGp - currentGp; var cordial = this.cordialStock.GetBestCordial(missingGp, cordialType); // Return OK if there is no cordial to use if (cordial == null) { rtn.UseState = InventoryItem.UseResult.OK; return(rtn); } // Use the cordial rtn.UseState = await cordial.Use( ExProfileBehavior.Me ); // Log the result this.LogCordialResult( rtn.UseState ?? InventoryItem.UseResult.CantUse, cordial, currentGp ); return(rtn); }
/// <summary> /// Returns true if the node is unspoiled or legendary /// </summary> public static bool IsUnspoiled(this GatheringPointObject node) { // Temporary until we decide if legendary have any diff properties or if we should treat them the same. return(node.EnglishName.IndexOf("unspoiled", StringComparison.InvariantCultureIgnoreCase) >= 0 || node.EnglishName.IndexOf("legendary", StringComparison.InvariantCultureIgnoreCase) >= 0); }
/// <summary> /// Returns true if the node is concealed /// </summary> public static bool IsConcealed(this GatheringPointObject node) { return(node.EnglishName.IndexOf("concealed", StringComparison.InvariantCultureIgnoreCase) >= 0); }
/// <summary> /// Returns true if the node is an ephemeral node /// </summary> public static bool IsEphemeral(this GatheringPointObject node) { return(node.EnglishName.IndexOf("ephemeral", StringComparison.InvariantCultureIgnoreCase) >= 0); }
public override bool ShouldForceGather(GatheringPointObject node) { return(!node.IsEphemeral() && !node.IsUnspoiled()); }
/// <summary> /// Executes the strategy logic in charge of regenerating GP before the gather. /// </summary> /// <returns></returns> public async Task <GpRegenStrategyResult> RegenerateGp(GatheringPointObject node, IGatheringRotation gatherRotation, GatherStrategy gatherStrategy, CordialTime cordialTime, CordialType cordialType) { var rtn = new GpRegenStrategyResult() { EffectiveCordialType = this.effectiveCordialType, OriginalCordialType = this.requestedCordialType, }; // Return OK immediately if there is no node if (node == null) { this.logger.GatheringNodeIsGone(); rtn.StrategyState = GpRegenStrategyResult.GpRegenStrategyResultState.NodeGone; return(rtn); } // Configure the strategy and report to the log this.Configure(node, gatherRotation, gatherStrategy, cordialTime, cordialType); this.logger.LogReport(this); // Return not enough time if player has less than 3 seconds to gather if (this.EffectiveTimeTillGather.TotalSeconds < 3) { this.logger.GatheringNotEnoughTime(); rtn.StrategyState = GpRegenStrategyResult.GpRegenStrategyResultState.NotEnoughTime; return(rtn); } if (this.gatherStrategy == GatherStrategy.GatherOrCollect) { // Return not enough GP if we cannot meet the target breakpoint for the rotation if (this.BreakpointGp > this.TargetGp) { this.logger.RegeneratingNotEnoughGp(); rtn.StrategyState = GpRegenStrategyResult.GpRegenStrategyResultState.NotEnoughGp; return(rtn); } } // Use the cordial if one was selected if (this.Cordial != null) { rtn.UseState = await this.Cordial.Use( ExProfileBehavior.Me, maxTimeout : this.EffectiveTimeTillGather, dismount : true ); this.LogCordialResult(rtn.UseState.Value); // Recalculate player's ability to gather after cordial use failure if (rtn.UseState != InventoryItem.UseResult.OK) { this.effectiveCordialType = CordialType.None; this.CalculateTargetAndCordialSelection(); this.logger.LogReport(this); // Return not enough GP if we cannot meet the target breakpoint for the rotation if (this.BreakpointGp > this.TargetGp) { this.logger.RegeneratingNotEnoughGp(); rtn.StrategyState = GpRegenStrategyResult.GpRegenStrategyResultState.NotEnoughGp; return(rtn); } } } // Handle TouchAndGo overrides if (this.gatherStrategy == GatherStrategy.TouchAndGo) { // Do nothing if this is not ephemeral if (!node.IsEphemeral()) { this.logger.GatheringNodeSkippedNotEphemeral(); rtn.StrategyState = GpRegenStrategyResult.GpRegenStrategyResultState.OK; return(rtn); } if (this.EffectiveTimeToRegenerate.TotalSeconds > MAX_TOUCHANDGO_WAIT) { this.logger.RegeneratingSkippedExceedsEphemeralMaxWait( this.EffectiveTimeToRegenerate.TotalSeconds, MAX_TOUCHANDGO_WAIT ); rtn.StrategyState = GpRegenStrategyResult.GpRegenStrategyResultState.OK; return(rtn); } } // Return OK immediately if the rotation forcefully gathers the item if (gatherRotation.ShouldForceGather(node)) { rtn.StrategyState = GpRegenStrategyResult.GpRegenStrategyResultState.OK; return(rtn); } // Execute wait logic rtn.StrategyState = await this.WaitForGpRegeneration(); return(rtn); }
/// <summary> /// Configures the strategy for use /// </summary> protected void Configure(GatheringPointObject node, IGatheringRotation gatherRotation, GatherStrategy gatherStrategy, CordialTime cordialTime, CordialType cordialType) { if (gatherRotation == null) { throw new ArgumentNullException("gatherRotation"); } // Set gathering parameters this.gatherRotation = gatherRotation; this.gatherStrategy = gatherStrategy; // Set cordial parameters this.cordialTime = cordialTime; this.requestedCordialType = this.effectiveCordialType = cordialType; // Override cordial type if (this.requestedCordialType > CordialType.None) { // Turn off cordial when there is no stock if (!this.cordialStockManager.HasStock()) { this.effectiveCordialType = CordialType.None; this.logger.RegeneratingCordialUseDisabledNoStock(); } // Turn off cordial when node is not ephemeral in TnG strategies if (this.gatherStrategy == GatherStrategy.TouchAndGo && !node.IsEphemeral()) { this.effectiveCordialType = CordialType.None; } } else { this.effectiveCordialType = this.requestedCordialType; } // Set node lifespan parameters this.NodeLifespan = new EffectiveNodeLifespan(NodeHelper.GetNodeLifespan(node), this.gatherRotation.Attributes.RequiredTimeInSeconds); if (this.gatherStrategy == GatherStrategy.TouchAndGo) { this.NodeLifespan.Clamp(TimeSpan.Zero, TimeSpan.FromSeconds(MAX_TOUCHANDGO_WAIT)); } this.EffectiveTimeTillGather = this.NodeLifespan.DeSpawn; // Set GP parameters this.StartingGp = this.CurrentGp; this.MaxGp = ExProfileBehavior.Me.MaxGP; this.EffectiveGp = CharacterResource.GetEffectiveGp(this.NodeLifespan.DeSpawnTicks); // Set breakpoint and cordial parameters this.CalculateTargetAndCordialSelection(); // Calculate regeneration parameters this.RegeneratedGp = (ushort)(this.TargetGp - this.CordialGp - this.StartingGp); if (this.RegeneratedGp < 0) { this.RegeneratedGp = 0; } this.EffectiveTimeToRegenerate = CharacterResource.EstimateExpectedRegenerationTime(this.RegeneratedGp); }
private async Task <bool> FindNode(bool retryCenterHotspot = true) { if (Node != null) { return(false); } StatusText = Localization.Localization.ExGather_SearchForNodes; while (Behaviors.ShouldContinue) { IEnumerable <GatheringPointObject> nodes = GameObjectManager.GetObjectsOfType <GatheringPointObject>().Where(gpo => gpo.CanGather).ToArray(); if (GatherStrategy == GatherStrategy.TouchAndGo && HotSpots != null) { if (GatherObjects != null) { nodes = nodes.Where(gpo => GatherObjects.Contains(gpo.EnglishName, StringComparer.InvariantCultureIgnoreCase)); } foreach (var node in nodes.Where(gpo => HotSpots.CurrentOrDefault.WithinHotSpot2D(gpo.Location)) .OrderBy(gpo => gpo.Location.Distance2D(ExProfileBehavior.Me.Location)) .Skip(1)) { if (!Blacklist.Contains(node.ObjectId, BlacklistFlags.Interact)) { Blacklist.Add( node, BlacklistFlags.Interact, TimeSpan.FromSeconds(18), Localization.Localization.ExGather_SkipFurthestNodes); } } } nodes = nodes.Where(gpo => !Blacklist.Contains(gpo.ObjectId, BlacklistFlags.Interact)); if (FreeRange) { nodes = nodes.Where(gpo => gpo.Distance2D(ExProfileBehavior.Me.Location) < Radius); } else { if (HotSpots != null) { nodes = nodes.Where(gpo => HotSpots.CurrentOrDefault.WithinHotSpot2D(gpo.Location)); } } // ReSharper disable once ConvertIfStatementToConditionalTernaryExpression if (GatherObjects != null) { Node = nodes.OrderBy( gpo => GatherObjects.FindIndex(i => string.Equals(gpo.EnglishName, i, StringComparison.InvariantCultureIgnoreCase))) .ThenBy(gpo => gpo.Location.Distance2D(ExProfileBehavior.Me.Location)) .FirstOrDefault(gpo => GatherObjects.Contains(gpo.EnglishName, StringComparer.InvariantCultureIgnoreCase)); } else { Node = nodes.OrderBy(gpo => gpo.Location.Distance2D(ExProfileBehavior.Me.Location)).FirstOrDefault(); } if (Node == null) { if (HotSpots != null) { var myLocation = ExProfileBehavior.Me.Location; var distanceToFurthestVisibleGameObject = GameObjectManager.GameObjects.Select(o => o.Location.Distance2D(myLocation)) .OrderByDescending(o => o) .FirstOrDefault(); var distanceToFurthestVectorInHotspot = myLocation.Distance2D(HotSpots.CurrentOrDefault) + HotSpots.CurrentOrDefault.Radius; if (myLocation.Distance2D(HotSpots.CurrentOrDefault) > Radius && GatherStrategy == GatherStrategy.GatherOrCollect && retryCenterHotspot && distanceToFurthestVisibleGameObject <= distanceToFurthestVectorInHotspot) { Logger.Verbose(Localization.Localization.ExGather_DistanceObject + distanceToFurthestVisibleGameObject); Logger.Verbose(Localization.Localization.ExGather_DistanceHotSpot + distanceToFurthestVectorInHotspot); Logger.Warn( Localization.Localization.ExGather_NoAvailableNode); await HotSpots.CurrentOrDefault.XYZ.MoveTo(radius : Radius, name : HotSpots.CurrentOrDefault.Name); retryCenterHotspot = false; await Coroutine.Yield(); continue; } if (!await ChangeHotSpot()) { retryCenterHotspot = false; await Coroutine.Yield(); continue; } } if (FreeRange && !FreeRangeConditional()) { await Coroutine.Yield(); isDone = true; return(true); } return(true); } var entry = Blacklist.GetEntry(Node.ObjectId); if (entry != null && entry.Flags.HasFlag(BlacklistFlags.Interact)) { Logger.Warn(Localization.Localization.ExGather_NodeBlacklist); if (await Coroutine.Wait(entry.Length, () => entry.IsFinished || Node.Location.Distance2D(ExProfileBehavior.Me.Location) > Radius) || Core.Player.IsDead) { if (!entry.IsFinished) { Node = null; Logger.Info(Localization.Localization.ExGather_NodeReset); return(false); } } Logger.Info(Localization.Localization.ExGather_NodeBlacklistRemoved); } Logger.Info(Localization.Localization.ExGather_NodeSet + Node); if (HotSpots == null) { MovementManager.SetFacing2D(Node.Location); } if (Poi.Current.Unit != Node) { Poi.Current = new Poi(Node, PoiType.Gather); } return(true); } return(true); }
public virtual bool ShouldForceGather(GatheringPointObject node) { return(false); }
private async Task<bool> FindNode(bool retryCenterHotspot = true) { if (Node != null) { return false; } StatusText = "Searching for nodes"; while (Behaviors.ShouldContinue) { IEnumerable<GatheringPointObject> nodes = GameObjectManager.GetObjectsOfType<GatheringPointObject>().Where(gpo => gpo.CanGather).ToArray(); if (GatherStrategy == GatherStrategy.TouchAndGo && HotSpots != null) { if (GatherObjects != null) { nodes = nodes.Where(gpo => GatherObjects.Contains(gpo.EnglishName, StringComparer.InvariantCultureIgnoreCase)); } foreach ( var node in nodes.OrderBy(gpo => gpo.Location.Distance2D(Me.Location)) .Where(gpo => HotSpots.CurrentOrDefault.WithinHotSpot2D(gpo.Location)) .Skip(1)) { if (!Blacklist.Contains(node.ObjectId, BlacklistFlags.Interact)) { Blacklist.Add( node, BlacklistFlags.Interact, TimeSpan.FromSeconds(18), "Skip furthest nodes in hotspot. We only want 1."); } } } nodes = nodes.Where(gpo => !Blacklist.Contains(gpo.ObjectId, BlacklistFlags.Interact)); if (FreeRange) { nodes = nodes.Where(gpo => gpo.Distance2D(Me.Location) < Radius); } else { if (HotSpots != null) { nodes = nodes.Where(gpo => HotSpots.CurrentOrDefault.WithinHotSpot2D(gpo.Location)); } } // ReSharper disable once ConvertIfStatementToConditionalTernaryExpression if (GatherObjects != null) { Node = nodes.OrderBy( gpo => GatherObjects.FindIndex(i => string.Equals(gpo.EnglishName, i, StringComparison.InvariantCultureIgnoreCase))) .ThenBy(gpo => gpo.Location.Distance2D(Me.Location)) .FirstOrDefault(gpo => GatherObjects.Contains(gpo.EnglishName, StringComparer.InvariantCultureIgnoreCase)); } else { Node = nodes.OrderBy(gpo => gpo.Location.Distance2D(Me.Location)).FirstOrDefault(); } if (Node == null) { if (HotSpots != null) { var myLocation = Me.Location; var distanceToFurthestVisibleGameObject = GameObjectManager.GameObjects.Select(o => o.Location.Distance2D(myLocation)) .OrderByDescending(o => o) .FirstOrDefault(); var distanceToFurthestVectorInHotspot = myLocation.Distance2D(HotSpots.CurrentOrDefault) + HotSpots.CurrentOrDefault.Radius; if (GatherStrategy == GatherStrategy.GatherOrCollect && retryCenterHotspot && distanceToFurthestVisibleGameObject <= distanceToFurthestVectorInHotspot) { Logger.Verbose("Distance to furthest visible game object -> " + distanceToFurthestVisibleGameObject); Logger.Verbose("Distance to furthest vector in hotspot -> " + distanceToFurthestVectorInHotspot); Logger.Warn( "Could not find any nodes and can not confirm hotspot is empty via object detection, trying again from center of hotspot."); await Behaviors.MoveTo(HotSpots.CurrentOrDefault, radius: Radius, name: HotSpots.CurrentOrDefault.Name); retryCenterHotspot = false; await Coroutine.Yield(); continue; } if (!ChangeHotSpot()) { retryCenterHotspot = false; await Coroutine.Yield(); continue; } } if (FreeRange && !FreeRangeConditional()) { await Coroutine.Yield(); isDone = true; return true; } return true; } var entry = Blacklist.GetEntry(Node.ObjectId); if (entry != null && entry.Flags.HasFlag(BlacklistFlags.Interact)) { Logger.Warn("Node on blacklist, waiting until we move out of range or it clears."); if (await Coroutine.Wait(entry.Length, () => entry.IsFinished || Node.Location.Distance2D(Me.Location) > Radius)) { if (!entry.IsFinished) { Node = null; Logger.Info("Node Reset, Reason: Ran out of range"); return false; } } Logger.Info("Node removed from blacklist."); } Logger.Info("Node set -> " + Node); if (HotSpots == null) { MovementManager.SetFacing2D(Node.Location); } if (Poi.Current.Unit != Node) { Poi.Current = new Poi(Node, PoiType.Gather); } return true; } return true; }
private async Task<bool> FindNode(bool retryCenterHotspot = true) { if (Node != null) { return false; } StatusText = Localization.Localization.ExGather_SearchForNodes; while (Behaviors.ShouldContinue) { IEnumerable<GatheringPointObject> nodes = GameObjectManager.GetObjectsOfType<GatheringPointObject>().Where(gpo => gpo.CanGather).ToArray(); if (GatherStrategy == GatherStrategy.TouchAndGo && HotSpots != null) { if (GatherObjects != null) { nodes = nodes.Where(gpo => GatherObjects.Contains(gpo.EnglishName, StringComparer.InvariantCultureIgnoreCase)); } foreach (var node in nodes.Where(gpo => HotSpots.CurrentOrDefault.WithinHotSpot2D(gpo.Location)) .OrderBy(gpo => gpo.Location.Distance2D(ExProfileBehavior.Me.Location)) .Skip(1)) { if (!Blacklist.Contains(node.ObjectId, BlacklistFlags.Interact)) { Blacklist.Add( node, BlacklistFlags.Interact, TimeSpan.FromSeconds(18), Localization.Localization.ExGather_SkipFurthestNodes); } } } nodes = nodes.Where(gpo => !Blacklist.Contains(gpo.ObjectId, BlacklistFlags.Interact)); if (FreeRange) { nodes = nodes.Where(gpo => gpo.Distance2D(ExProfileBehavior.Me.Location) < Radius); } else { if (HotSpots != null) { nodes = nodes.Where(gpo => HotSpots.CurrentOrDefault.WithinHotSpot2D(gpo.Location)); } } // ReSharper disable once ConvertIfStatementToConditionalTernaryExpression if (GatherObjects != null) { Node = nodes.OrderBy( gpo => GatherObjects.FindIndex(i => string.Equals(gpo.EnglishName, i, StringComparison.InvariantCultureIgnoreCase))) .ThenBy(gpo => gpo.Location.Distance2D(ExProfileBehavior.Me.Location)) .FirstOrDefault(gpo => GatherObjects.Contains(gpo.EnglishName, StringComparer.InvariantCultureIgnoreCase)); } else { Node = nodes.OrderBy(gpo => gpo.Location.Distance2D(ExProfileBehavior.Me.Location)).FirstOrDefault(); } if (Node == null) { if (HotSpots != null) { var myLocation = ExProfileBehavior.Me.Location; var distanceToFurthestVisibleGameObject = GameObjectManager.GameObjects.Select(o => o.Location.Distance2D(myLocation)) .OrderByDescending(o => o) .FirstOrDefault(); var distanceToFurthestVectorInHotspot = myLocation.Distance2D(HotSpots.CurrentOrDefault) + HotSpots.CurrentOrDefault.Radius; if (myLocation.Distance2D(HotSpots.CurrentOrDefault) > Radius && GatherStrategy == GatherStrategy.GatherOrCollect && retryCenterHotspot && distanceToFurthestVisibleGameObject <= distanceToFurthestVectorInHotspot) { Logger.Verbose(Localization.Localization.ExGather_DistanceObject + distanceToFurthestVisibleGameObject); Logger.Verbose(Localization.Localization.ExGather_DistanceHotSpot + distanceToFurthestVectorInHotspot); Logger.Warn( Localization.Localization.ExGather_NoAvailableNode); await HotSpots.CurrentOrDefault.XYZ.MoveTo(radius: Radius, name: HotSpots.CurrentOrDefault.Name); retryCenterHotspot = false; await Coroutine.Yield(); continue; } if (!await ChangeHotSpot()) { retryCenterHotspot = false; await Coroutine.Yield(); continue; } } if (FreeRange && !FreeRangeConditional()) { await Coroutine.Yield(); isDone = true; return true; } return true; } var entry = Blacklist.GetEntry(Node.ObjectId); if (entry != null && entry.Flags.HasFlag(BlacklistFlags.Interact)) { Logger.Warn(Localization.Localization.ExGather_NodeBlacklist); if (await Coroutine.Wait(entry.Length, () => entry.IsFinished || Node.Location.Distance2D(ExProfileBehavior.Me.Location) > Radius) || Core.Player.IsDead) { if (!entry.IsFinished) { Node = null; Logger.Info(Localization.Localization.ExGather_NodeReset); return false; } } Logger.Info(Localization.Localization.ExGather_NodeBlacklistRemoved); } Logger.Info(Localization.Localization.ExGather_NodeSet + Node); if (HotSpots == null) { MovementManager.SetFacing2D(Node.Location); } if (Poi.Current.Unit != Node) { Poi.Current = new Poi(Node, PoiType.Gather); } return true; } return true; }
internal static int Base(this GatheringPointObject node) { return((int)Core.Memory.Read <uint>(node.Pointer + 0x80)); }