qstbak/Questionable/Questionable.Controller.Steps.Shared/Craft.cs
2025-10-09 07:47:19 +10:00

139 lines
4.4 KiB
C#

using System;
using System.Collections.Generic;
using System.Linq;
using Dalamud.Plugin.Services;
using FFXIVClientStructs.FFXIV.Client.Game;
using FFXIVClientStructs.FFXIV.Client.UI.Agent;
using FFXIVClientStructs.FFXIV.Component.GUI;
using LLib.GameData;
using Lumina.Excel.Sheets;
using Microsoft.Extensions.Logging;
using Questionable.Controller.Steps.Common;
using Questionable.External;
using Questionable.Model;
using Questionable.Model.Questing;
namespace Questionable.Controller.Steps.Shared;
internal static class Craft
{
internal sealed class Factory : ITaskFactory
{
public IEnumerable<ITask> CreateAllTasks(Questionable.Model.Quest quest, QuestSequence sequence, QuestStep step)
{
if (step.InteractionType != EInteractionType.Craft)
{
return Array.Empty<ITask>();
}
ArgumentNullException.ThrowIfNull(step.ItemId, "step.ItemId");
ArgumentNullException.ThrowIfNull(step.ItemCount, "step.ItemCount");
return new global::_003C_003Ez__ReadOnlyArray<ITask>(new ITask[2]
{
new Questionable.Controller.Steps.Common.Mount.UnmountTask(),
new CraftTask(step.ItemId.Value, step.ItemCount.Value)
});
}
}
internal sealed record CraftTask(uint ItemId, int ItemCount) : ITask
{
public override string ToString()
{
return $"Craft {ItemCount}x {ItemId} (with Artisan)";
}
}
internal sealed class DoCraft(IDataManager dataManager, IClientState clientState, ArtisanIpc artisanIpc, ILogger<DoCraft> logger) : TaskExecutor<CraftTask>()
{
protected override bool Start()
{
if (HasRequestedItems())
{
logger.LogInformation("Already own {ItemCount}x {ItemId}", base.Task.ItemCount, base.Task.ItemId);
return false;
}
RecipeLookup? rowOrDefault = dataManager.GetExcelSheet<RecipeLookup>().GetRowOrDefault(base.Task.ItemId);
if (!rowOrDefault.HasValue)
{
throw new TaskException($"Item {base.Task.ItemId} is not craftable");
}
uint num = (EClassJob)clientState.LocalPlayer.ClassJob.RowId switch
{
EClassJob.Carpenter => rowOrDefault.Value.CRP.RowId,
EClassJob.Blacksmith => rowOrDefault.Value.BSM.RowId,
EClassJob.Armorer => rowOrDefault.Value.ARM.RowId,
EClassJob.Goldsmith => rowOrDefault.Value.GSM.RowId,
EClassJob.Leatherworker => rowOrDefault.Value.LTW.RowId,
EClassJob.Weaver => rowOrDefault.Value.WVR.RowId,
EClassJob.Alchemist => rowOrDefault.Value.ALC.RowId,
EClassJob.Culinarian => rowOrDefault.Value.CUL.RowId,
_ => 0u,
};
if (num == 0)
{
num = new uint[8]
{
rowOrDefault.Value.CRP.RowId,
rowOrDefault.Value.BSM.RowId,
rowOrDefault.Value.ARM.RowId,
rowOrDefault.Value.GSM.RowId,
rowOrDefault.Value.LTW.RowId,
rowOrDefault.Value.WVR.RowId,
rowOrDefault.Value.ALC.RowId,
rowOrDefault.Value.WVR.RowId
}.FirstOrDefault((uint x) => x != 0);
}
if (num == 0)
{
throw new TaskException($"Unable to determine recipe for item {base.Task.ItemId}");
}
int num2 = base.Task.ItemCount - GetOwnedItemCount();
logger.LogInformation("Starting craft for item {ItemId} with recipe {RecipeId} for {RemainingItemCount} items", base.Task.ItemId, num, num2);
if (!artisanIpc.CraftItem((ushort)num, num2))
{
throw new TaskException($"Failed to start Artisan craft for recipe {num}");
}
return true;
}
public unsafe override ETaskResult Update()
{
if (HasRequestedItems() && !artisanIpc.IsCrafting())
{
AgentRecipeNote* ptr = AgentRecipeNote.Instance();
if (ptr != null && ptr->IsAgentActive())
{
uint addonId = ptr->GetAddonId();
if (addonId == 0)
{
return ETaskResult.StillRunning;
}
AtkUnitBase* addonById = AtkStage.Instance()->RaptureAtkUnitManager->GetAddonById((ushort)addonId);
if (addonById != null)
{
logger.LogInformation("Closing crafting window");
addonById->FireCallbackInt(-1);
return ETaskResult.TaskComplete;
}
}
}
return ETaskResult.StillRunning;
}
private bool HasRequestedItems()
{
return GetOwnedItemCount() >= base.Task.ItemCount;
}
private unsafe int GetOwnedItemCount()
{
InventoryManager* ptr = InventoryManager.Instance();
return ptr->GetInventoryItemCount(base.Task.ItemId, isHq: false, checkEquipped: false, checkArmory: true, 0) + ptr->GetInventoryItemCount(base.Task.ItemId, isHq: true, checkEquipped: false, checkArmory: true, 0);
}
public override bool ShouldInterruptOnDamage()
{
return false;
}
}
}