217 lines
6.5 KiB
C#
217 lines
6.5 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.Linq;
|
|
using System.Runtime.InteropServices;
|
|
using Dalamud.Game.Gui.ContextMenu;
|
|
using Dalamud.Game.Text;
|
|
using Dalamud.Plugin.Services;
|
|
using FFXIVClientStructs.FFXIV.Client.UI;
|
|
using FFXIVClientStructs.FFXIV.Client.UI.Agent;
|
|
using LLib.GameData;
|
|
using LLib.GameUI;
|
|
using Microsoft.Extensions.Logging;
|
|
using Questionable.Data;
|
|
using Questionable.Functions;
|
|
using Questionable.GameStructs;
|
|
using Questionable.Model;
|
|
using Questionable.Model.Gathering;
|
|
using Questionable.Model.Questing;
|
|
|
|
namespace Questionable.Controller;
|
|
|
|
internal sealed class ContextMenuController : IDisposable
|
|
{
|
|
private readonly IContextMenu _contextMenu;
|
|
|
|
private readonly QuestController _questController;
|
|
|
|
private readonly GatheringPointRegistry _gatheringPointRegistry;
|
|
|
|
private readonly GatheringData _gatheringData;
|
|
|
|
private readonly QuestRegistry _questRegistry;
|
|
|
|
private readonly QuestData _questData;
|
|
|
|
private readonly GameFunctions _gameFunctions;
|
|
|
|
private readonly QuestFunctions _questFunctions;
|
|
|
|
private readonly IGameGui _gameGui;
|
|
|
|
private readonly IChatGui _chatGui;
|
|
|
|
private readonly IClientState _clientState;
|
|
|
|
private readonly ILogger<ContextMenuController> _logger;
|
|
|
|
public ContextMenuController(IContextMenu contextMenu, QuestController questController, GatheringPointRegistry gatheringPointRegistry, GatheringData gatheringData, QuestRegistry questRegistry, QuestData questData, GameFunctions gameFunctions, QuestFunctions questFunctions, IGameGui gameGui, IChatGui chatGui, IClientState clientState, ILogger<ContextMenuController> logger)
|
|
{
|
|
_contextMenu = contextMenu;
|
|
_questController = questController;
|
|
_gatheringPointRegistry = gatheringPointRegistry;
|
|
_gatheringData = gatheringData;
|
|
_questRegistry = questRegistry;
|
|
_questData = questData;
|
|
_gameFunctions = gameFunctions;
|
|
_questFunctions = questFunctions;
|
|
_gameGui = gameGui;
|
|
_chatGui = chatGui;
|
|
_clientState = clientState;
|
|
_logger = logger;
|
|
_contextMenu.OnMenuOpened += MenuOpened;
|
|
}
|
|
|
|
private void MenuOpened(IMenuOpenedArgs args)
|
|
{
|
|
if (args.AddonName != null)
|
|
{
|
|
return;
|
|
}
|
|
uint num = GetHoveredSatisfactionSupplyItemId();
|
|
if (num == 0)
|
|
{
|
|
_logger.LogTrace("Ignoring context menu, no item hovered");
|
|
return;
|
|
}
|
|
if (num > 1000000)
|
|
{
|
|
num -= 1000000;
|
|
}
|
|
if (num >= 500000)
|
|
{
|
|
num -= 500000;
|
|
}
|
|
if (_gatheringData.TryGetCustomDeliveryNpc(num, out var npcId))
|
|
{
|
|
AddContextMenuEntry(args, num, npcId, EClassJob.Miner, "Mine");
|
|
AddContextMenuEntry(args, num, npcId, EClassJob.Botanist, "Harvest");
|
|
}
|
|
else
|
|
{
|
|
_logger.LogDebug("No custom delivery NPC found for item {ItemId}.", num);
|
|
}
|
|
}
|
|
|
|
private unsafe uint GetHoveredSatisfactionSupplyItemId()
|
|
{
|
|
AgentSatisfactionSupply* ptr = AgentSatisfactionSupply.Instance();
|
|
if (ptr == null || !ptr->IsAgentActive())
|
|
{
|
|
return 0u;
|
|
}
|
|
if (_gameGui.TryGetAddonByName<AddonSatisfactionSupply>("SatisfactionSupply", out var addonPtr) && LAddon.IsAddonReady(&addonPtr->AtkUnitBase))
|
|
{
|
|
int hoveredElementIndex = addonPtr->HoveredElementIndex;
|
|
if (hoveredElementIndex >= 0 && hoveredElementIndex <= 2)
|
|
{
|
|
return ptr->Items[addonPtr->HoveredElementIndex].Id;
|
|
}
|
|
}
|
|
return 0u;
|
|
}
|
|
|
|
private unsafe void AddContextMenuEntry(IMenuOpenedArgs args, uint itemId, uint npcId, EClassJob classJob, string verb)
|
|
{
|
|
EClassJob rowId = (EClassJob)_clientState.LocalPlayer.ClassJob.RowId;
|
|
bool flag = classJob != rowId;
|
|
if (flag)
|
|
{
|
|
bool flag2 = rowId - 16 <= EClassJob.Gladiator;
|
|
flag = flag2;
|
|
}
|
|
if (flag)
|
|
{
|
|
return;
|
|
}
|
|
if (!_gatheringPointRegistry.TryGetGatheringPointId(itemId, classJob, out GatheringPointId _))
|
|
{
|
|
_logger.LogInformation("No gathering point found for {ClassJob}.", classJob);
|
|
return;
|
|
}
|
|
ushort collectability = _gatheringData.GetRecommendedCollectability(itemId);
|
|
int quantityToGather = ((collectability > 0) ? 6 : int.MaxValue);
|
|
if (collectability != 0)
|
|
{
|
|
AgentSatisfactionSupply* ptr = AgentSatisfactionSupply.Instance();
|
|
if (ptr->IsAgentActive())
|
|
{
|
|
int maxTurnIns = ((ptr->NpcInfo.SatisfactionRank == 1) ? 3 : 6);
|
|
quantityToGather = Math.Min(ptr->NpcData.RemainingAllowances, ((AgentSatisfactionSupply2*)ptr)->CalculateTurnInsToNextRank(maxTurnIns));
|
|
}
|
|
string text = string.Empty;
|
|
if (!_questFunctions.IsClassJobUnlocked(classJob))
|
|
{
|
|
text = $"{classJob} not unlocked";
|
|
}
|
|
else if (quantityToGather == 0)
|
|
{
|
|
text = "No allowances";
|
|
}
|
|
else if (quantityToGather > _gameFunctions.GetFreeInventorySlots())
|
|
{
|
|
text = "Inventory full";
|
|
}
|
|
else if (_gameFunctions.IsOccupied())
|
|
{
|
|
text = "Can't be used while interacting";
|
|
}
|
|
string text2 = verb + " with Questionable";
|
|
if (!string.IsNullOrEmpty(text))
|
|
{
|
|
text2 = text2 + " (" + text + ")";
|
|
}
|
|
args.AddMenuItem(new MenuItem
|
|
{
|
|
Prefix = SeIconChar.Hyadelyn,
|
|
PrefixColor = 52,
|
|
Name = text2,
|
|
OnClicked = delegate
|
|
{
|
|
StartGathering(npcId, itemId, quantityToGather, collectability, classJob);
|
|
},
|
|
IsEnabled = string.IsNullOrEmpty(text)
|
|
});
|
|
}
|
|
}
|
|
|
|
private void StartGathering(uint npcId, uint itemId, int quantity, ushort collectability, EClassJob classJob)
|
|
{
|
|
SatisfactionSupplyInfo satisfactionSupplyInfo = (SatisfactionSupplyInfo)_questData.GetAllByIssuerDataId(npcId).Single((IQuestInfo x) => x is SatisfactionSupplyInfo);
|
|
if (_questRegistry.TryGetQuest(satisfactionSupplyInfo.QuestId, out Quest quest))
|
|
{
|
|
QuestSequence questSequence = quest.FindSequence(0);
|
|
QuestStep questStep = questSequence.Steps.Single((QuestStep x) => x.InteractionType == EInteractionType.SwitchClass);
|
|
questStep.TargetClass = classJob switch
|
|
{
|
|
EClassJob.Miner => EExtendedClassJob.Miner,
|
|
EClassJob.Botanist => EExtendedClassJob.Botanist,
|
|
_ => throw new ArgumentOutOfRangeException("classJob", classJob, null),
|
|
};
|
|
QuestStep questStep2 = questSequence.Steps.Single((QuestStep x) => x.InteractionType == EInteractionType.Gather);
|
|
int num = 1;
|
|
List<GatheredItem> list = new List<GatheredItem>(num);
|
|
CollectionsMarshal.SetCount(list, num);
|
|
Span<GatheredItem> span = CollectionsMarshal.AsSpan(list);
|
|
int index = 0;
|
|
span[index] = new GatheredItem
|
|
{
|
|
ItemId = itemId,
|
|
ItemCount = quantity,
|
|
Collectability = collectability
|
|
};
|
|
questStep2.ItemsToGather = list;
|
|
_questController.SetGatheringQuest(quest);
|
|
_questController.StartGatheringQuest("SatisfactionSupply prepare gathering");
|
|
}
|
|
else
|
|
{
|
|
_chatGui.PrintError($"No associated quest ({satisfactionSupplyInfo.QuestId}).", "Questionable");
|
|
}
|
|
}
|
|
|
|
public void Dispose()
|
|
{
|
|
_contextMenu.OnMenuOpened -= MenuOpened;
|
|
}
|
|
}
|