644 lines
18 KiB
C#
644 lines
18 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.Runtime.CompilerServices;
|
|
using Dalamud.Game.ClientState.Conditions;
|
|
using Dalamud.Game.ClientState.Objects.SubKinds;
|
|
using Dalamud.Game.ClientState.Objects.Types;
|
|
using Dalamud.Plugin.Services;
|
|
using FFXIVClientStructs.FFXIV.Client.Game;
|
|
using FFXIVClientStructs.FFXIV.Client.UI.Misc;
|
|
using FFXIVClientStructs.Interop;
|
|
using LLib.GameData;
|
|
using LLib.Gear;
|
|
using LLib.Inventory;
|
|
using Lumina.Excel.Sheets;
|
|
using Questionable.Model;
|
|
using Questionable.Model.Questing;
|
|
|
|
namespace Questionable.Controller.Steps.Interactions;
|
|
|
|
internal static class EquipRecommended
|
|
{
|
|
internal sealed class Factory : SimpleTaskFactory
|
|
{
|
|
public override ITask? CreateTask(Questionable.Model.Quest quest, QuestSequence sequence, QuestStep step)
|
|
{
|
|
if (step.InteractionType != EInteractionType.EquipRecommended)
|
|
{
|
|
return null;
|
|
}
|
|
return new EquipTask();
|
|
}
|
|
}
|
|
|
|
internal sealed class BeforeDutyOrInstance : SimpleTaskFactory
|
|
{
|
|
public override ITask? CreateTask(Questionable.Model.Quest quest, QuestSequence sequence, QuestStep step)
|
|
{
|
|
if (step.InteractionType != EInteractionType.Duty && step.InteractionType != EInteractionType.SinglePlayerDuty && step.InteractionType != EInteractionType.Combat)
|
|
{
|
|
return null;
|
|
}
|
|
return new EquipTask();
|
|
}
|
|
}
|
|
|
|
internal sealed class EquipTask : ITask
|
|
{
|
|
public override string ToString()
|
|
{
|
|
return "EquipRecommended";
|
|
}
|
|
}
|
|
|
|
internal sealed class DoEquipRecommended(IObjectTable objectTable, IChatGui chatGui, ICondition condition, IDataManager dataManager, Configuration configuration, BestItemFinder bestItemFinder) : TaskExecutor<EquipTask>()
|
|
{
|
|
private readonly record struct PendingMove(InventoryType SourceContainer, int SourceSlot, uint SourceItemId, InventoryType DestinationContainer, int DestinationSlot);
|
|
|
|
private static readonly (string Name, uint[] Categories)[] SlotMap = new(string, uint[])[13]
|
|
{
|
|
("MainHand", new uint[2] { 1u, 13u }),
|
|
("OffHand", new uint[1] { 2u }),
|
|
("Head", new uint[1] { 3u }),
|
|
("Body", new uint[1] { 4u }),
|
|
("Hands", new uint[1] { 5u }),
|
|
("Belt", Array.Empty<uint>()),
|
|
("Legs", new uint[1] { 7u }),
|
|
("Feet", new uint[1] { 8u }),
|
|
("Ears", new uint[1] { 9u }),
|
|
("Neck", new uint[1] { 10u }),
|
|
("Wrists", new uint[1] { 11u }),
|
|
("Ring1", new uint[1] { 12u }),
|
|
("Ring2", new uint[1] { 12u })
|
|
};
|
|
|
|
private const long EquipApplyTimeoutMs = 5000L;
|
|
|
|
private const long MoveApplyTimeoutMs = 2000L;
|
|
|
|
private bool _useSmart;
|
|
|
|
private bool _checkedOrTriggeredEquipmentUpdate;
|
|
|
|
private long _timeoutAt;
|
|
|
|
private List<(int Slot, BestItemRef Item)>? _smartUpgrades;
|
|
|
|
private List<PendingMove>? _smartMoves;
|
|
|
|
private int _smartMoveCursor;
|
|
|
|
private bool _currentMoveStarted;
|
|
|
|
private bool _smartApplied;
|
|
|
|
private bool _useDirectEquip;
|
|
|
|
private uint _smartClassJobId;
|
|
|
|
private int _directEquipCursor;
|
|
|
|
private bool _directEquipStarted;
|
|
|
|
protected unsafe override bool Start()
|
|
{
|
|
if (configuration.General.EquipMode == Configuration.EEquipMode.Disabled)
|
|
{
|
|
return false;
|
|
}
|
|
if (condition[ConditionFlag.InCombat])
|
|
{
|
|
return false;
|
|
}
|
|
IPlayerCharacter localPlayer = objectTable.LocalPlayer;
|
|
if (localPlayer == null)
|
|
{
|
|
return false;
|
|
}
|
|
_useSmart = configuration.General.EquipMode == Configuration.EEquipMode.Smart;
|
|
if (_useSmart && TrySmartStart(localPlayer))
|
|
{
|
|
return true;
|
|
}
|
|
_useSmart = false;
|
|
RecommendEquipModule.Instance()->SetupForClassJob((byte)localPlayer.ClassJob.RowId);
|
|
return true;
|
|
}
|
|
|
|
public override ETaskResult Update()
|
|
{
|
|
if (_useSmart)
|
|
{
|
|
return SmartUpdate();
|
|
}
|
|
return GameUpdate();
|
|
}
|
|
|
|
private unsafe bool TrySmartStart(ICharacter localPlayer)
|
|
{
|
|
uint rowId = localPlayer.ClassJob.RowId;
|
|
if (!Enum.IsDefined(typeof(EClassJob), rowId))
|
|
{
|
|
return false;
|
|
}
|
|
RaptureGearsetModule* ptr = RaptureGearsetModule.Instance();
|
|
if (ptr == null)
|
|
{
|
|
return false;
|
|
}
|
|
int currentGearsetIndex = ptr->CurrentGearsetIndex;
|
|
if (currentGearsetIndex < 0 || !ptr->IsValidGearset(currentGearsetIndex))
|
|
{
|
|
return false;
|
|
}
|
|
EClassJob job = (EClassJob)rowId;
|
|
StatPriority priority = StatPriority.ForJob(job);
|
|
byte playerLevel = GetPlayerLevel(job);
|
|
_smartClassJobId = rowId;
|
|
_useDirectEquip = configuration.General.PreserveGearset || !CurrentGearsetMatchesEquippedItems();
|
|
if (!TryComputeUpgradesAndMoves(job, priority, playerLevel, out _smartUpgrades, out _smartMoves))
|
|
{
|
|
_smartUpgrades = null;
|
|
_smartMoves = null;
|
|
return false;
|
|
}
|
|
_smartMoveCursor = 0;
|
|
_currentMoveStarted = false;
|
|
return true;
|
|
}
|
|
|
|
private ETaskResult SmartUpdate()
|
|
{
|
|
List<PendingMove> smartMoves = _smartMoves;
|
|
if (smartMoves != null && smartMoves.Count > 0 && _smartMoveCursor < smartMoves.Count)
|
|
{
|
|
PendingMove pendingMove = smartMoves[_smartMoveCursor];
|
|
if (!_currentMoveStarted)
|
|
{
|
|
if (!TryStartMove(pendingMove))
|
|
{
|
|
chatGui.Print($"Equip move {_smartMoveCursor + 1}/{smartMoves.Count} rejected; skipping equip step.", "Questionable", 576);
|
|
return ETaskResult.TaskComplete;
|
|
}
|
|
_currentMoveStarted = true;
|
|
_timeoutAt = Environment.TickCount64 + 2000;
|
|
return ETaskResult.StillRunning;
|
|
}
|
|
if (IsMoveApplied(pendingMove))
|
|
{
|
|
if (_smartUpgrades != null)
|
|
{
|
|
NotifyStagingMoveApplied(_smartUpgrades, pendingMove);
|
|
}
|
|
_smartMoveCursor++;
|
|
_currentMoveStarted = false;
|
|
return ETaskResult.StillRunning;
|
|
}
|
|
if (Environment.TickCount64 >= _timeoutAt)
|
|
{
|
|
chatGui.Print($"Equip move {_smartMoveCursor + 1}/{smartMoves.Count} timed out; skipping equip step.", "Questionable", 576);
|
|
return ETaskResult.TaskComplete;
|
|
}
|
|
return ETaskResult.StillRunning;
|
|
}
|
|
if (!_useDirectEquip && !CurrentGearsetMatchesEquippedItems())
|
|
{
|
|
_useDirectEquip = true;
|
|
}
|
|
if (_useDirectEquip)
|
|
{
|
|
return DirectEquipPhase();
|
|
}
|
|
if (!_smartApplied)
|
|
{
|
|
List<(int, BestItemRef)> smartUpgrades = _smartUpgrades;
|
|
if (smartUpgrades != null && smartUpgrades.Count > 0)
|
|
{
|
|
chatGui.Print($"Equipping {smartUpgrades.Count} gear upgrade(s) (smart).", "Questionable", 576);
|
|
ApplyUpgrades(smartUpgrades);
|
|
_timeoutAt = Environment.TickCount64 + 5000;
|
|
_smartApplied = true;
|
|
return ETaskResult.StillRunning;
|
|
}
|
|
return ETaskResult.TaskComplete;
|
|
}
|
|
List<(int, BestItemRef)> smartUpgrades2 = _smartUpgrades;
|
|
if (smartUpgrades2 != null && AreUpgradesApplied(smartUpgrades2))
|
|
{
|
|
return ETaskResult.TaskComplete;
|
|
}
|
|
if (Environment.TickCount64 < _timeoutAt)
|
|
{
|
|
return ETaskResult.StillRunning;
|
|
}
|
|
return ETaskResult.TaskComplete;
|
|
}
|
|
|
|
private ETaskResult DirectEquipPhase()
|
|
{
|
|
List<(int, BestItemRef)> smartUpgrades = _smartUpgrades;
|
|
if (smartUpgrades == null)
|
|
{
|
|
return ETaskResult.TaskComplete;
|
|
}
|
|
if (_directEquipCursor >= smartUpgrades.Count)
|
|
{
|
|
if (AreUpgradesApplied(smartUpgrades))
|
|
{
|
|
return ETaskResult.TaskComplete;
|
|
}
|
|
if (Environment.TickCount64 < _timeoutAt)
|
|
{
|
|
return ETaskResult.StillRunning;
|
|
}
|
|
return ETaskResult.TaskComplete;
|
|
}
|
|
var (num, item) = smartUpgrades[_directEquipCursor];
|
|
if (!_directEquipStarted)
|
|
{
|
|
if (!TryStartDirectEquip(item, num))
|
|
{
|
|
chatGui.Print($"Direct equip for slot {num} rejected, skipping.", "Questionable", 576);
|
|
_directEquipCursor++;
|
|
return ETaskResult.StillRunning;
|
|
}
|
|
_directEquipStarted = true;
|
|
_timeoutAt = Environment.TickCount64 + 2000;
|
|
return ETaskResult.StillRunning;
|
|
}
|
|
if (IsDirectEquipApplied(item, num))
|
|
{
|
|
_directEquipCursor++;
|
|
_directEquipStarted = false;
|
|
return ETaskResult.StillRunning;
|
|
}
|
|
if (Environment.TickCount64 >= _timeoutAt)
|
|
{
|
|
_directEquipCursor++;
|
|
_directEquipStarted = false;
|
|
}
|
|
return ETaskResult.StillRunning;
|
|
}
|
|
|
|
private unsafe bool TryStartDirectEquip(BestItemRef item, int gearsetSlot)
|
|
{
|
|
InventoryManager* ptr = InventoryManager.Instance();
|
|
if (ptr == null)
|
|
{
|
|
return false;
|
|
}
|
|
return ptr->MoveItemSlot(item.Container, (ushort)item.Slot, InventoryType.EquippedItems, (ushort)gearsetSlot) == 0;
|
|
}
|
|
|
|
private unsafe bool IsDirectEquipApplied(BestItemRef item, int gearsetSlot)
|
|
{
|
|
InventoryManager* ptr = InventoryManager.Instance();
|
|
if (ptr == null)
|
|
{
|
|
return false;
|
|
}
|
|
InventoryContainer* inventoryContainer = ptr->GetInventoryContainer(InventoryType.EquippedItems);
|
|
if (inventoryContainer == null)
|
|
{
|
|
return false;
|
|
}
|
|
InventoryItem* inventorySlot = inventoryContainer->GetInventorySlot(gearsetSlot);
|
|
if (inventorySlot == null)
|
|
{
|
|
return false;
|
|
}
|
|
return ItemHandle.FromInventorySlot(inventorySlot).Id == ((ItemHandle)item.ItemId).Id;
|
|
}
|
|
|
|
private static void NotifyStagingMoveApplied(List<(int Slot, BestItemRef Item)> upgrades, PendingMove appliedMove)
|
|
{
|
|
for (int i = 0; i < upgrades.Count; i++)
|
|
{
|
|
var (item, bestItemRef) = upgrades[i];
|
|
if (bestItemRef.Container == appliedMove.SourceContainer && bestItemRef.Slot == appliedMove.SourceSlot)
|
|
{
|
|
upgrades[i] = (item, bestItemRef with
|
|
{
|
|
Container = appliedMove.DestinationContainer,
|
|
Slot = appliedMove.DestinationSlot
|
|
});
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
private unsafe bool TryStartMove(PendingMove move)
|
|
{
|
|
InventoryManager* ptr = InventoryManager.Instance();
|
|
if (ptr == null)
|
|
{
|
|
return false;
|
|
}
|
|
return ptr->MoveItemSlot(move.SourceContainer, (ushort)move.SourceSlot, move.DestinationContainer, (ushort)move.DestinationSlot) == 0;
|
|
}
|
|
|
|
private unsafe bool IsMoveApplied(PendingMove move)
|
|
{
|
|
InventoryManager* ptr = InventoryManager.Instance();
|
|
if (ptr == null)
|
|
{
|
|
return false;
|
|
}
|
|
InventoryContainer* inventoryContainer = ptr->GetInventoryContainer(move.DestinationContainer);
|
|
if (inventoryContainer == null)
|
|
{
|
|
return false;
|
|
}
|
|
InventoryItem* inventorySlot = inventoryContainer->GetInventorySlot(move.DestinationSlot);
|
|
if (inventorySlot == null)
|
|
{
|
|
return false;
|
|
}
|
|
return ItemHandle.FromInventorySlot(inventorySlot).Id == ((ItemHandle)move.SourceItemId).Id;
|
|
}
|
|
|
|
private unsafe ETaskResult GameUpdate()
|
|
{
|
|
RecommendEquipModule* ptr = RecommendEquipModule.Instance();
|
|
if (ptr->IsUpdating)
|
|
{
|
|
return ETaskResult.StillRunning;
|
|
}
|
|
if (!_checkedOrTriggeredEquipmentUpdate)
|
|
{
|
|
if (IsAllRecommendedGearEquipped())
|
|
{
|
|
return ETaskResult.TaskComplete;
|
|
}
|
|
chatGui.Print("Equipping recommended gear.", "Questionable", 576);
|
|
ptr->EquipRecommendedGear();
|
|
_timeoutAt = Environment.TickCount64 + 5000;
|
|
_checkedOrTriggeredEquipmentUpdate = true;
|
|
return ETaskResult.StillRunning;
|
|
}
|
|
if (IsAllRecommendedGearEquipped())
|
|
{
|
|
return ETaskResult.TaskComplete;
|
|
}
|
|
if (Environment.TickCount64 < _timeoutAt)
|
|
{
|
|
return ETaskResult.StillRunning;
|
|
}
|
|
return ETaskResult.TaskComplete;
|
|
}
|
|
|
|
private unsafe bool AreUpgradesApplied(IReadOnlyList<(int Slot, BestItemRef Item)> upgrades)
|
|
{
|
|
InventoryManager* ptr = InventoryManager.Instance();
|
|
if (ptr == null)
|
|
{
|
|
return false;
|
|
}
|
|
InventoryContainer* inventoryContainer = ptr->GetInventoryContainer(InventoryType.EquippedItems);
|
|
if (inventoryContainer == null)
|
|
{
|
|
return false;
|
|
}
|
|
foreach (var upgrade in upgrades)
|
|
{
|
|
int item = upgrade.Slot;
|
|
BestItemRef item2 = upgrade.Item;
|
|
InventoryItem* inventorySlot = inventoryContainer->GetInventorySlot(item);
|
|
if (inventorySlot == null || ItemHandle.FromInventorySlot(inventorySlot).Id != ((ItemHandle)item2.ItemId).Id)
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
private unsafe bool TryComputeUpgradesAndMoves(EClassJob job, StatPriority priority, byte playerLevel, out List<(int Slot, BestItemRef Item)> upgrades, out List<PendingMove> moves)
|
|
{
|
|
upgrades = new List<(int, BestItemRef)>();
|
|
moves = new List<PendingMove>();
|
|
InventoryManager* ptr = InventoryManager.Instance();
|
|
if (ptr == null)
|
|
{
|
|
return false;
|
|
}
|
|
InventoryContainer* inventoryContainer = ptr->GetInventoryContainer(InventoryType.EquippedItems);
|
|
if (inventoryContainer == null)
|
|
{
|
|
return false;
|
|
}
|
|
BestItemRef? bestItemRef = null;
|
|
bool flag = job.IsCrafter() || job.IsGatherer();
|
|
Dictionary<InventoryType, int> cursors = new Dictionary<InventoryType, int>();
|
|
for (int i = 0; i < SlotMap.Length; i++)
|
|
{
|
|
uint[] item = SlotMap[i].Categories;
|
|
if (item.Length == 0)
|
|
{
|
|
continue;
|
|
}
|
|
BestItemRef? excludeContainerSlot = ((i == 12 && bestItemRef.HasValue) ? bestItemRef : ((BestItemRef?)null));
|
|
bool flag2 = !flag;
|
|
if (flag2)
|
|
{
|
|
bool flag3 = (uint)i <= 1u;
|
|
flag2 = flag3;
|
|
}
|
|
EItemRankingMode rankingMode = ((!flag2) ? EItemRankingMode.ScoreFirst : EItemRankingMode.DamageFirst);
|
|
BestItemRef? bestItemRef2 = bestItemFinder.FindBest(job, item, priority, BestItemFinder.ArmoryEquippedAndBags, playerLevel, rankingMode, excludeContainerSlot);
|
|
if (!bestItemRef2.HasValue)
|
|
{
|
|
continue;
|
|
}
|
|
if (i == 11)
|
|
{
|
|
bestItemRef = bestItemRef2;
|
|
}
|
|
InventoryItem* inventorySlot = inventoryContainer->GetInventorySlot(i);
|
|
if (((inventorySlot != null) ? ItemHandle.FromInventorySlot(inventorySlot).Id : 0) == ((ItemHandle)bestItemRef2.Value.ItemId).Id)
|
|
{
|
|
continue;
|
|
}
|
|
if (IsBagContainer(bestItemRef2.Value.Container))
|
|
{
|
|
InventoryType? inventoryType = ResolveArmoryContainer(item);
|
|
if (!inventoryType.HasValue)
|
|
{
|
|
continue;
|
|
}
|
|
int nextFreeSlot = GetNextFreeSlot(inventoryType.Value, cursors);
|
|
if (nextFreeSlot < 0)
|
|
{
|
|
return false;
|
|
}
|
|
moves.Add(new PendingMove(bestItemRef2.Value.Container, bestItemRef2.Value.Slot, bestItemRef2.Value.ItemId, inventoryType.Value, nextFreeSlot));
|
|
}
|
|
upgrades.Add((i, bestItemRef2.Value));
|
|
}
|
|
return true;
|
|
}
|
|
|
|
private unsafe bool CurrentGearsetMatchesEquippedItems()
|
|
{
|
|
RaptureGearsetModule* ptr = RaptureGearsetModule.Instance();
|
|
if (ptr == null)
|
|
{
|
|
return false;
|
|
}
|
|
int currentGearsetIndex = ptr->CurrentGearsetIndex;
|
|
if (currentGearsetIndex < 0 || !ptr->IsValidGearset(currentGearsetIndex))
|
|
{
|
|
return false;
|
|
}
|
|
RaptureGearsetModule.GearsetEntry* ptr2 = (RaptureGearsetModule.GearsetEntry*)Unsafe.AsPointer(in ptr->Entries[currentGearsetIndex]);
|
|
if (ptr2->ClassJob != _smartClassJobId)
|
|
{
|
|
return false;
|
|
}
|
|
InventoryManager* ptr3 = InventoryManager.Instance();
|
|
if (ptr3 == null)
|
|
{
|
|
return false;
|
|
}
|
|
InventoryContainer* inventoryContainer = ptr3->GetInventoryContainer(InventoryType.EquippedItems);
|
|
if (inventoryContainer == null)
|
|
{
|
|
return false;
|
|
}
|
|
for (int i = 0; i <= 13; i++)
|
|
{
|
|
if (i != 5)
|
|
{
|
|
InventoryItem* inventorySlot = inventoryContainer->GetInventorySlot(i);
|
|
uint num = ((inventorySlot != null) ? ItemHandle.FromInventorySlot(inventorySlot).Id : 0u);
|
|
if (((ItemHandle)ptr2->GetItem((RaptureGearsetModule.GearsetItemIndex)i).ItemId).Id != num)
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
private static bool IsBagContainer(InventoryType type)
|
|
{
|
|
if (type <= InventoryType.Inventory4)
|
|
{
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
private static InventoryType? ResolveArmoryContainer(uint[] acceptedCategories)
|
|
{
|
|
for (int i = 0; i < acceptedCategories.Length; i++)
|
|
{
|
|
InventoryType? armoryContainer = ((EEquipSlotCategory)acceptedCategories[i]).GetArmoryContainer();
|
|
if (armoryContainer.HasValue)
|
|
{
|
|
return armoryContainer;
|
|
}
|
|
}
|
|
return null;
|
|
}
|
|
|
|
private unsafe static int GetNextFreeSlot(InventoryType container, Dictionary<InventoryType, int> cursors)
|
|
{
|
|
InventoryManager* ptr = InventoryManager.Instance();
|
|
if (ptr == null)
|
|
{
|
|
return -1;
|
|
}
|
|
InventoryContainer* inventoryContainer = ptr->GetInventoryContainer(container);
|
|
if (inventoryContainer == null)
|
|
{
|
|
return -1;
|
|
}
|
|
int value;
|
|
for (int i = (cursors.TryGetValue(container, out value) ? value : 0); i < inventoryContainer->Size; i++)
|
|
{
|
|
InventoryItem* inventorySlot = inventoryContainer->GetInventorySlot(i);
|
|
if (inventorySlot != null && inventorySlot->ItemId == 0)
|
|
{
|
|
cursors[container] = i + 1;
|
|
return i;
|
|
}
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
private unsafe void ApplyUpgrades(IReadOnlyList<(int Slot, BestItemRef Item)> upgrades)
|
|
{
|
|
RaptureGearsetModule* ptr = RaptureGearsetModule.Instance();
|
|
if (ptr == null)
|
|
{
|
|
return;
|
|
}
|
|
int currentGearsetIndex = ptr->CurrentGearsetIndex;
|
|
if (currentGearsetIndex < 0 || !ptr->IsValidGearset(currentGearsetIndex))
|
|
{
|
|
return;
|
|
}
|
|
RaptureGearsetModule.GearsetEntry* ptr2 = (RaptureGearsetModule.GearsetEntry*)Unsafe.AsPointer(in ptr->Entries[currentGearsetIndex]);
|
|
InventoryManager* ptr3 = InventoryManager.Instance();
|
|
if (ptr3 == null)
|
|
{
|
|
return;
|
|
}
|
|
foreach (var upgrade in upgrades)
|
|
{
|
|
int item = upgrade.Slot;
|
|
BestItemRef item2 = upgrade.Item;
|
|
InventoryContainer* inventoryContainer = ptr3->GetInventoryContainer(item2.Container);
|
|
if (inventoryContainer == null)
|
|
{
|
|
continue;
|
|
}
|
|
InventoryItem* inventorySlot = inventoryContainer->GetInventorySlot(item2.Slot);
|
|
if (inventorySlot != null && inventorySlot->ItemId != 0)
|
|
{
|
|
RaptureGearsetModule.GearsetItem* ptr4 = (RaptureGearsetModule.GearsetItem*)Unsafe.AsPointer(in ptr2->Items[item]);
|
|
ptr4->ItemId = inventorySlot->ItemId;
|
|
ptr4->GlamourId = inventorySlot->GlamourId;
|
|
ptr4->Stain0Id = inventorySlot->Stains[0];
|
|
ptr4->Stain1Id = inventorySlot->Stains[1];
|
|
for (int i = 0; i < 5; i++)
|
|
{
|
|
ptr4->Materia[i] = inventorySlot->Materia[i];
|
|
ptr4->MateriaGrades[i] = inventorySlot->MateriaGrades[i];
|
|
}
|
|
}
|
|
}
|
|
ptr->EquipGearset(currentGearsetIndex, 0);
|
|
}
|
|
|
|
private byte GetPlayerLevel(EClassJob job)
|
|
{
|
|
short classJobLevel = PlayerStateHelper.GetClassJobLevel(dataManager.GetExcelSheet<ClassJob>().GetRow((uint)job).ExpArrayIndex);
|
|
if (classJobLevel <= 0)
|
|
{
|
|
return 0;
|
|
}
|
|
return (byte)classJobLevel;
|
|
}
|
|
|
|
private unsafe bool IsAllRecommendedGearEquipped()
|
|
{
|
|
Span<Pointer<InventoryItem>> recommendedItems = RecommendEquipModule.Instance()->RecommendedItems;
|
|
for (int i = 0; i < recommendedItems.Length; i++)
|
|
{
|
|
Pointer<InventoryItem> pointer = recommendedItems[i];
|
|
InventoryItem* value = pointer.Value;
|
|
if (value != null && value->ItemId != 0 && !InventoryHelper.IsItemEquipped(value->ItemId))
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
public override bool ShouldInterruptOnDamage()
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
}
|