/// <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)); }
/// <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); }
public override bool ShouldForceGather(GatheringPointObject node) { return(!node.IsEphemeral() && !node.IsUnspoiled()); }
/// <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); }