qstbak/Questionable/Questionable.Data/ClassJobUtils.cs
2025-10-09 07:47:19 +10:00

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;
}
}