qstbak/Questionable/Questionable.Controller/GearRepairService.cs
2026-08-17 20:29:32 +10:00

143 lines
5.1 KiB
C#

using System;
using Dalamud.Game.ClientState.Conditions;
using Dalamud.Game.ClientState.Objects.SubKinds;
using Dalamud.Plugin.Services;
using LLib.GameData;
using LLib.Gear;
using Lumina.Excel;
using Lumina.Excel.Sheets;
using Microsoft.Extensions.Logging;
using Questionable.Controller.Steps;
using Questionable.Controller.Steps.Common;
using Questionable.Data;
using Questionable.Functions;
using Questionable.Model.Questing;
using Questionable.Navigation;
namespace Questionable.Controller;
internal sealed class GearRepairService(Configuration configuration, ICondition condition, IClientState clientState, IObjectTable objectTable, IDataManager dataManager, MenderDataService menderDataService, SmartNavRouteEnqueuer routeEnqueuer, AetheryteFunctions aetheryteFunctions, IChatGui chatGui, ILogger<GearRepairService> logger)
{
private const int CriticalConditionPercent = 10;
private const long AttemptCooldownMs = 300000L;
private const long WarningCooldownMs = 300000L;
private long _nextAttemptAtMs;
private long _nextWarningAtMs;
public void ResetAttemptCooldown()
{
_nextAttemptAtMs = 0L;
_nextWarningAtMs = 0L;
}
public ERepairDecision EvaluateAndEnqueue(EInteractionType upcomingType, TaskQueue taskQueue)
{
bool flag = (uint)(upcomingType - 19) <= 1u;
bool flag2 = flag;
flag = (uint)(upcomingType - 30) <= 1u;
bool flag3 = flag;
if (!flag2 && !flag3)
{
return ERepairDecision.NotNeeded;
}
if (!configuration.General.RepairGear)
{
return ERepairDecision.NotNeeded;
}
if (condition[ConditionFlag.InCombat])
{
return ERepairDecision.NotNeeded;
}
int? lowestConditionPercent = GearConditionHelper.GetLowestConditionPercent();
if (!lowestConditionPercent.HasValue || lowestConditionPercent.Value >= configuration.General.RepairThresholdPercent)
{
return ERepairDecision.NotNeeded;
}
bool flag4 = flag2 && lowestConditionPercent.Value < 10;
if (Environment.TickCount64 < _nextAttemptAtMs)
{
if (flag4)
{
logger.LogError("Gear still critically damaged ({Condition}%) after recent repair attempt", lowestConditionPercent.Value);
return ERepairDecision.BlockDuty;
}
return ERepairDecision.NotNeeded;
}
if (TryEnqueueRepair(taskQueue, lowestConditionPercent.Value, out string failureReason))
{
return ERepairDecision.Enqueued;
}
if (flag4)
{
logger.LogError("Gear critically damaged ({Condition}%) and unrepairable: {Reason}", lowestConditionPercent.Value, failureReason);
return ERepairDecision.BlockDuty;
}
if (Environment.TickCount64 >= _nextWarningAtMs)
{
_nextWarningAtMs = Environment.TickCount64 + 300000;
chatGui.Print($"Gear is at {lowestConditionPercent.Value}% condition but can't be repaired: {failureReason}", "Questionable", 576);
}
return ERepairDecision.NotNeeded;
}
private bool TryEnqueueRepair(TaskQueue taskQueue, int lowestCondition, out string failureReason)
{
if (CanSelfRepair())
{
logger.LogInformation("Lowest gear condition {Condition}%, self-repairing", lowestCondition);
taskQueue.Enqueue(new Questionable.Controller.Steps.Common.Mount.UnmountTask());
taskQueue.Enqueue(new RepairGear.SelfRepairTask());
MarkAttempt();
failureReason = string.Empty;
return true;
}
IPlayerCharacter localPlayer = objectTable.LocalPlayer;
if (localPlayer != null)
{
MenderNpc menderNpc = menderDataService.FindNearestInTerritory((ushort)clientState.TerritoryType, localPlayer.Position);
if ((object)menderNpc != null)
{
logger.LogInformation("Lowest gear condition {Condition}%, visiting mender {DataId} in current zone", lowestCondition, menderNpc.DataId);
routeEnqueuer.Enqueue(taskQueue, menderNpc.TerritoryId, menderNpc.Position, 3f);
taskQueue.Enqueue(new Questionable.Controller.Steps.Common.Mount.UnmountTask());
taskQueue.Enqueue(new RepairGear.MenderRepairTask(menderNpc));
MarkAttempt();
failureReason = string.Empty;
return true;
}
}
if (aetheryteFunctions.IsTeleportUnlocked())
{
foreach (MenderNpc cityFallback in MenderDataService.CityFallbacks)
{
if (routeEnqueuer.TryEnqueueRouted(taskQueue, cityFallback.TerritoryId, cityFallback.Position))
{
logger.LogInformation("Lowest gear condition {Condition}%, no zone mender - routing to city mender {DataId}", lowestCondition, cityFallback.DataId);
taskQueue.Enqueue(new Questionable.Controller.Steps.Common.Mount.UnmountTask());
taskQueue.Enqueue(new RepairGear.MenderRepairTask(cityFallback));
MarkAttempt();
failureReason = string.Empty;
return true;
}
}
}
failureReason = "no dark matter or crafter levels for self-repair, and no reachable mender";
return false;
}
private bool CanSelfRepair()
{
ExcelSheet<ClassJob> classJobSheet = dataManager.GetExcelSheet<ClassJob>();
ClassJob row;
return RepairActions.CanSelfRepairAny(dataManager, (uint classJobId) => (byte)(classJobSheet.TryGetRow(classJobId, out row) ? ((byte)PlayerStateHelper.GetClassJobLevel(row.ExpArrayIndex)) : 0), configuration.General.RepairThresholdPercent);
}
private void MarkAttempt()
{
_nextAttemptAtMs = Environment.TickCount64 + 300000;
}
}