262 lines
11 KiB
C#
262 lines
11 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.Collections.ObjectModel;
|
|
using System.Linq;
|
|
using Dalamud.Plugin.Services;
|
|
using FFXIVClientStructs.FFXIV.Application.Network.WorkDefinitions;
|
|
using FFXIVClientStructs.FFXIV.Client.Game;
|
|
using FFXIVClientStructs.FFXIV.Client.Game.UI;
|
|
using FFXIVClientStructs.FFXIV.Client.UI.Misc;
|
|
using LLib.GameData;
|
|
using Lumina.Excel.Sheets;
|
|
using Questionable.Model.Questing;
|
|
|
|
namespace Questionable.Data;
|
|
|
|
internal sealed class ClassJobUtils
|
|
{
|
|
private readonly Configuration _configuration;
|
|
|
|
private readonly ReadOnlyDictionary<EClassJob, sbyte> _classJobToExpArrayIndex;
|
|
|
|
public readonly ReadOnlyCollection<(EClassJob ClassJob, int Category)> SortedClassJobs;
|
|
|
|
public ClassJobUtils(Configuration configuration, IDataManager dataManager)
|
|
{
|
|
_configuration = configuration;
|
|
_classJobToExpArrayIndex = (from x in dataManager.GetExcelSheet<ClassJob>()
|
|
where x.RowId != 0 && x.ExpArrayIndex >= 0
|
|
select x).ToDictionary((ClassJob x) => (EClassJob)x.RowId, (ClassJob x) => x.ExpArrayIndex).AsReadOnly();
|
|
SortedClassJobs = (from x in dataManager.GetExcelSheet<ClassJob>()
|
|
select (ClassJob: (EClassJob)x.RowId, Priority: x.UIPriority) into x
|
|
orderby x.Priority
|
|
select (ClassJob: x.ClassJob, x.Priority / 10)).ToList().AsReadOnly();
|
|
}
|
|
|
|
public IEnumerable<EClassJob> AsIndividualJobs(EExtendedClassJob classJob, ElementId? referenceQuest)
|
|
{
|
|
switch (classJob)
|
|
{
|
|
case EExtendedClassJob.Gladiator:
|
|
return new global::_003C_003Ez__ReadOnlySingleElementList<EClassJob>(EClassJob.Gladiator);
|
|
case EExtendedClassJob.Pugilist:
|
|
return new global::_003C_003Ez__ReadOnlySingleElementList<EClassJob>(EClassJob.Pugilist);
|
|
case EExtendedClassJob.Marauder:
|
|
return new global::_003C_003Ez__ReadOnlySingleElementList<EClassJob>(EClassJob.Marauder);
|
|
case EExtendedClassJob.Lancer:
|
|
return new global::_003C_003Ez__ReadOnlySingleElementList<EClassJob>(EClassJob.Lancer);
|
|
case EExtendedClassJob.Archer:
|
|
return new global::_003C_003Ez__ReadOnlySingleElementList<EClassJob>(EClassJob.Archer);
|
|
case EExtendedClassJob.Conjurer:
|
|
return new global::_003C_003Ez__ReadOnlySingleElementList<EClassJob>(EClassJob.Conjurer);
|
|
case EExtendedClassJob.Thaumaturge:
|
|
return new global::_003C_003Ez__ReadOnlySingleElementList<EClassJob>(EClassJob.Thaumaturge);
|
|
case EExtendedClassJob.Carpenter:
|
|
return new global::_003C_003Ez__ReadOnlySingleElementList<EClassJob>(EClassJob.Carpenter);
|
|
case EExtendedClassJob.Blacksmith:
|
|
return new global::_003C_003Ez__ReadOnlySingleElementList<EClassJob>(EClassJob.Blacksmith);
|
|
case EExtendedClassJob.Armorer:
|
|
return new global::_003C_003Ez__ReadOnlySingleElementList<EClassJob>(EClassJob.Armorer);
|
|
case EExtendedClassJob.Goldsmith:
|
|
return new global::_003C_003Ez__ReadOnlySingleElementList<EClassJob>(EClassJob.Goldsmith);
|
|
case EExtendedClassJob.Leatherworker:
|
|
return new global::_003C_003Ez__ReadOnlySingleElementList<EClassJob>(EClassJob.Leatherworker);
|
|
case EExtendedClassJob.Weaver:
|
|
return new global::_003C_003Ez__ReadOnlySingleElementList<EClassJob>(EClassJob.Weaver);
|
|
case EExtendedClassJob.Alchemist:
|
|
return new global::_003C_003Ez__ReadOnlySingleElementList<EClassJob>(EClassJob.Alchemist);
|
|
case EExtendedClassJob.Culinarian:
|
|
return new global::_003C_003Ez__ReadOnlySingleElementList<EClassJob>(EClassJob.Culinarian);
|
|
case EExtendedClassJob.Miner:
|
|
return new global::_003C_003Ez__ReadOnlySingleElementList<EClassJob>(EClassJob.Miner);
|
|
case EExtendedClassJob.Botanist:
|
|
return new global::_003C_003Ez__ReadOnlySingleElementList<EClassJob>(EClassJob.Botanist);
|
|
case EExtendedClassJob.Fisher:
|
|
return new global::_003C_003Ez__ReadOnlySingleElementList<EClassJob>(EClassJob.Fisher);
|
|
case EExtendedClassJob.Paladin:
|
|
return new global::_003C_003Ez__ReadOnlySingleElementList<EClassJob>(EClassJob.Paladin);
|
|
case EExtendedClassJob.Monk:
|
|
return new global::_003C_003Ez__ReadOnlySingleElementList<EClassJob>(EClassJob.Monk);
|
|
case EExtendedClassJob.Warrior:
|
|
return new global::_003C_003Ez__ReadOnlySingleElementList<EClassJob>(EClassJob.Warrior);
|
|
case EExtendedClassJob.Dragoon:
|
|
return new global::_003C_003Ez__ReadOnlySingleElementList<EClassJob>(EClassJob.Dragoon);
|
|
case EExtendedClassJob.Bard:
|
|
return new global::_003C_003Ez__ReadOnlySingleElementList<EClassJob>(EClassJob.Bard);
|
|
case EExtendedClassJob.WhiteMage:
|
|
return new global::_003C_003Ez__ReadOnlySingleElementList<EClassJob>(EClassJob.WhiteMage);
|
|
case EExtendedClassJob.BlackMage:
|
|
return new global::_003C_003Ez__ReadOnlySingleElementList<EClassJob>(EClassJob.BlackMage);
|
|
case EExtendedClassJob.Arcanist:
|
|
return new global::_003C_003Ez__ReadOnlySingleElementList<EClassJob>(EClassJob.Arcanist);
|
|
case EExtendedClassJob.Summoner:
|
|
return new global::_003C_003Ez__ReadOnlySingleElementList<EClassJob>(EClassJob.Summoner);
|
|
case EExtendedClassJob.Scholar:
|
|
return new global::_003C_003Ez__ReadOnlySingleElementList<EClassJob>(EClassJob.Scholar);
|
|
case EExtendedClassJob.Rogue:
|
|
return new global::_003C_003Ez__ReadOnlySingleElementList<EClassJob>(EClassJob.Rogue);
|
|
case EExtendedClassJob.Ninja:
|
|
return new global::_003C_003Ez__ReadOnlySingleElementList<EClassJob>(EClassJob.Ninja);
|
|
case EExtendedClassJob.Machinist:
|
|
return new global::_003C_003Ez__ReadOnlySingleElementList<EClassJob>(EClassJob.Machinist);
|
|
case EExtendedClassJob.DarkKnight:
|
|
return new global::_003C_003Ez__ReadOnlySingleElementList<EClassJob>(EClassJob.DarkKnight);
|
|
case EExtendedClassJob.Astrologian:
|
|
return new global::_003C_003Ez__ReadOnlySingleElementList<EClassJob>(EClassJob.Astrologian);
|
|
case EExtendedClassJob.Samurai:
|
|
return new global::_003C_003Ez__ReadOnlySingleElementList<EClassJob>(EClassJob.Samurai);
|
|
case EExtendedClassJob.RedMage:
|
|
return new global::_003C_003Ez__ReadOnlySingleElementList<EClassJob>(EClassJob.RedMage);
|
|
case EExtendedClassJob.BlueMage:
|
|
return new global::_003C_003Ez__ReadOnlySingleElementList<EClassJob>(EClassJob.BlueMage);
|
|
case EExtendedClassJob.Gunbreaker:
|
|
return new global::_003C_003Ez__ReadOnlySingleElementList<EClassJob>(EClassJob.Gunbreaker);
|
|
case EExtendedClassJob.Dancer:
|
|
return new global::_003C_003Ez__ReadOnlySingleElementList<EClassJob>(EClassJob.Dancer);
|
|
case EExtendedClassJob.Reaper:
|
|
return new global::_003C_003Ez__ReadOnlySingleElementList<EClassJob>(EClassJob.Reaper);
|
|
case EExtendedClassJob.Sage:
|
|
return new global::_003C_003Ez__ReadOnlySingleElementList<EClassJob>(EClassJob.Sage);
|
|
case EExtendedClassJob.Viper:
|
|
return new global::_003C_003Ez__ReadOnlySingleElementList<EClassJob>(EClassJob.Viper);
|
|
case EExtendedClassJob.Pictomancer:
|
|
return new global::_003C_003Ez__ReadOnlySingleElementList<EClassJob>(EClassJob.Pictomancer);
|
|
case EExtendedClassJob.DoW:
|
|
return from x in Enum.GetValues<EClassJob>()
|
|
where x.DealsPhysicalDamage()
|
|
select x;
|
|
case EExtendedClassJob.DoM:
|
|
return from x in Enum.GetValues<EClassJob>()
|
|
where x.DealsMagicDamage()
|
|
select x;
|
|
case EExtendedClassJob.DoH:
|
|
return from x in Enum.GetValues<EClassJob>()
|
|
where x.IsCrafter()
|
|
select x;
|
|
case EExtendedClassJob.DoL:
|
|
return from x in Enum.GetValues<EClassJob>()
|
|
where x.IsGatherer()
|
|
select x;
|
|
case EExtendedClassJob.ConfiguredCombatJob:
|
|
{
|
|
EClassJob eClassJob2 = LookupConfiguredCombatJob();
|
|
IEnumerable<EClassJob> result2;
|
|
if (eClassJob2 == EClassJob.Adventurer)
|
|
{
|
|
IEnumerable<EClassJob> enumerable = Array.Empty<EClassJob>();
|
|
result2 = enumerable;
|
|
}
|
|
else
|
|
{
|
|
IEnumerable<EClassJob> enumerable = new global::_003C_003Ez__ReadOnlySingleElementList<EClassJob>(eClassJob2);
|
|
result2 = enumerable;
|
|
}
|
|
return result2;
|
|
}
|
|
case EExtendedClassJob.QuestStartJob:
|
|
{
|
|
EClassJob eClassJob = LookupQuestStartJob(referenceQuest);
|
|
IEnumerable<EClassJob> result;
|
|
if (eClassJob == EClassJob.Adventurer)
|
|
{
|
|
IEnumerable<EClassJob> enumerable = Array.Empty<EClassJob>();
|
|
result = enumerable;
|
|
}
|
|
else
|
|
{
|
|
IEnumerable<EClassJob> enumerable = new global::_003C_003Ez__ReadOnlySingleElementList<EClassJob>(eClassJob);
|
|
result = enumerable;
|
|
}
|
|
return result;
|
|
}
|
|
default:
|
|
throw new ArgumentOutOfRangeException("classJob", classJob, null);
|
|
}
|
|
}
|
|
|
|
private EClassJob LookupConfiguredCombatJob()
|
|
{
|
|
EClassJob configuredJob = _configuration.General.CombatJob;
|
|
ReadOnlyCollection<(EClassJob, short, short)> combatJobGearSets = GetCombatJobGearSets();
|
|
HashSet<EClassJob> hashSet = combatJobGearSets.Select<(EClassJob, short, short), EClassJob>(((EClassJob ClassJob, short Level, short ItemLevel) x) => x.ClassJob).Distinct().ToHashSet();
|
|
if (configuredJob != EClassJob.Adventurer)
|
|
{
|
|
if (hashSet.Contains(configuredJob))
|
|
{
|
|
return configuredJob;
|
|
}
|
|
EClassJob eClassJob = Enum.GetValues<EClassJob>().SingleOrDefault((EClassJob x) => x.IsClass() && x.AsJob() == configuredJob);
|
|
if (eClassJob != EClassJob.Adventurer && hashSet.Contains(eClassJob))
|
|
{
|
|
return eClassJob;
|
|
}
|
|
}
|
|
return (from x in (from x in combatJobGearSets
|
|
orderby x.Level descending, x.ItemLevel descending
|
|
select x).ThenByDescending<(EClassJob, short, short), int>(delegate((EClassJob ClassJob, short Level, short ItemLevel) x)
|
|
{
|
|
if (x.ClassJob.IsCaster())
|
|
{
|
|
return 50;
|
|
}
|
|
if (x.ClassJob.IsPhysicalRanged())
|
|
{
|
|
return 40;
|
|
}
|
|
if (x.ClassJob.IsMelee())
|
|
{
|
|
return 30;
|
|
}
|
|
if (x.ClassJob.IsTank())
|
|
{
|
|
return 20;
|
|
}
|
|
return x.ClassJob.IsHealer() ? 10 : 0;
|
|
})
|
|
select x.ClassJob).DefaultIfEmpty(EClassJob.Adventurer).FirstOrDefault();
|
|
}
|
|
|
|
private unsafe ReadOnlyCollection<(EClassJob ClassJob, short Level, short ItemLevel)> GetCombatJobGearSets()
|
|
{
|
|
List<(EClassJob, short, short)> list = new List<(EClassJob, short, short)>();
|
|
PlayerState* ptr = PlayerState.Instance();
|
|
RaptureGearsetModule* ptr2 = RaptureGearsetModule.Instance();
|
|
if (ptr == null || ptr2 == null)
|
|
{
|
|
return list.AsReadOnly();
|
|
}
|
|
for (int i = 0; i < 100; i++)
|
|
{
|
|
RaptureGearsetModule.GearsetEntry* gearset = ptr2->GetGearset(i);
|
|
if (!gearset->Flags.HasFlag(RaptureGearsetModule.GearsetFlag.Exists))
|
|
{
|
|
continue;
|
|
}
|
|
EClassJob classJob = (EClassJob)gearset->ClassJob;
|
|
if (!classJob.IsCrafter() && !classJob.IsGatherer())
|
|
{
|
|
short num = ptr->ClassJobLevels[_classJobToExpArrayIndex[classJob]];
|
|
if (num != 0)
|
|
{
|
|
short itemLevel = gearset->ItemLevel;
|
|
list.Add((classJob, num, itemLevel));
|
|
}
|
|
}
|
|
}
|
|
return list.AsReadOnly();
|
|
}
|
|
|
|
private unsafe EClassJob LookupQuestStartJob(ElementId? elementId)
|
|
{
|
|
ArgumentNullException.ThrowIfNull(elementId, "elementId");
|
|
if (elementId is QuestId questId)
|
|
{
|
|
QuestWork* questById = QuestManager.Instance()->GetQuestById(questId.Value);
|
|
if (questById->AcceptClassJob != 0)
|
|
{
|
|
return (EClassJob)questById->AcceptClassJob;
|
|
}
|
|
}
|
|
return EClassJob.Adventurer;
|
|
}
|
|
}
|