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 _classJobToExpArrayIndex; public readonly ReadOnlyCollection<(EClassJob ClassJob, int Category)> SortedClassJobs; public ClassJobUtils(Configuration configuration, IDataManager dataManager) { _configuration = configuration; _classJobToExpArrayIndex = (from x in dataManager.GetExcelSheet() 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() select (ClassJob: (EClassJob)x.RowId, Priority: x.UIPriority) into x orderby x.Priority select (ClassJob: x.ClassJob, x.Priority / 10)).ToList().AsReadOnly(); } public IEnumerable AsIndividualJobs(EExtendedClassJob classJob, ElementId? referenceQuest) { switch (classJob) { case EExtendedClassJob.Gladiator: return new global::_003C_003Ez__ReadOnlySingleElementList(EClassJob.Gladiator); case EExtendedClassJob.Pugilist: return new global::_003C_003Ez__ReadOnlySingleElementList(EClassJob.Pugilist); case EExtendedClassJob.Marauder: return new global::_003C_003Ez__ReadOnlySingleElementList(EClassJob.Marauder); case EExtendedClassJob.Lancer: return new global::_003C_003Ez__ReadOnlySingleElementList(EClassJob.Lancer); case EExtendedClassJob.Archer: return new global::_003C_003Ez__ReadOnlySingleElementList(EClassJob.Archer); case EExtendedClassJob.Conjurer: return new global::_003C_003Ez__ReadOnlySingleElementList(EClassJob.Conjurer); case EExtendedClassJob.Thaumaturge: return new global::_003C_003Ez__ReadOnlySingleElementList(EClassJob.Thaumaturge); case EExtendedClassJob.Carpenter: return new global::_003C_003Ez__ReadOnlySingleElementList(EClassJob.Carpenter); case EExtendedClassJob.Blacksmith: return new global::_003C_003Ez__ReadOnlySingleElementList(EClassJob.Blacksmith); case EExtendedClassJob.Armorer: return new global::_003C_003Ez__ReadOnlySingleElementList(EClassJob.Armorer); case EExtendedClassJob.Goldsmith: return new global::_003C_003Ez__ReadOnlySingleElementList(EClassJob.Goldsmith); case EExtendedClassJob.Leatherworker: return new global::_003C_003Ez__ReadOnlySingleElementList(EClassJob.Leatherworker); case EExtendedClassJob.Weaver: return new global::_003C_003Ez__ReadOnlySingleElementList(EClassJob.Weaver); case EExtendedClassJob.Alchemist: return new global::_003C_003Ez__ReadOnlySingleElementList(EClassJob.Alchemist); case EExtendedClassJob.Culinarian: return new global::_003C_003Ez__ReadOnlySingleElementList(EClassJob.Culinarian); case EExtendedClassJob.Miner: return new global::_003C_003Ez__ReadOnlySingleElementList(EClassJob.Miner); case EExtendedClassJob.Botanist: return new global::_003C_003Ez__ReadOnlySingleElementList(EClassJob.Botanist); case EExtendedClassJob.Fisher: return new global::_003C_003Ez__ReadOnlySingleElementList(EClassJob.Fisher); case EExtendedClassJob.Paladin: return new global::_003C_003Ez__ReadOnlySingleElementList(EClassJob.Paladin); case EExtendedClassJob.Monk: return new global::_003C_003Ez__ReadOnlySingleElementList(EClassJob.Monk); case EExtendedClassJob.Warrior: return new global::_003C_003Ez__ReadOnlySingleElementList(EClassJob.Warrior); case EExtendedClassJob.Dragoon: return new global::_003C_003Ez__ReadOnlySingleElementList(EClassJob.Dragoon); case EExtendedClassJob.Bard: return new global::_003C_003Ez__ReadOnlySingleElementList(EClassJob.Bard); case EExtendedClassJob.WhiteMage: return new global::_003C_003Ez__ReadOnlySingleElementList(EClassJob.WhiteMage); case EExtendedClassJob.BlackMage: return new global::_003C_003Ez__ReadOnlySingleElementList(EClassJob.BlackMage); case EExtendedClassJob.Arcanist: return new global::_003C_003Ez__ReadOnlySingleElementList(EClassJob.Arcanist); case EExtendedClassJob.Summoner: return new global::_003C_003Ez__ReadOnlySingleElementList(EClassJob.Summoner); case EExtendedClassJob.Scholar: return new global::_003C_003Ez__ReadOnlySingleElementList(EClassJob.Scholar); case EExtendedClassJob.Rogue: return new global::_003C_003Ez__ReadOnlySingleElementList(EClassJob.Rogue); case EExtendedClassJob.Ninja: return new global::_003C_003Ez__ReadOnlySingleElementList(EClassJob.Ninja); case EExtendedClassJob.Machinist: return new global::_003C_003Ez__ReadOnlySingleElementList(EClassJob.Machinist); case EExtendedClassJob.DarkKnight: return new global::_003C_003Ez__ReadOnlySingleElementList(EClassJob.DarkKnight); case EExtendedClassJob.Astrologian: return new global::_003C_003Ez__ReadOnlySingleElementList(EClassJob.Astrologian); case EExtendedClassJob.Samurai: return new global::_003C_003Ez__ReadOnlySingleElementList(EClassJob.Samurai); case EExtendedClassJob.RedMage: return new global::_003C_003Ez__ReadOnlySingleElementList(EClassJob.RedMage); case EExtendedClassJob.BlueMage: return new global::_003C_003Ez__ReadOnlySingleElementList(EClassJob.BlueMage); case EExtendedClassJob.Gunbreaker: return new global::_003C_003Ez__ReadOnlySingleElementList(EClassJob.Gunbreaker); case EExtendedClassJob.Dancer: return new global::_003C_003Ez__ReadOnlySingleElementList(EClassJob.Dancer); case EExtendedClassJob.Reaper: return new global::_003C_003Ez__ReadOnlySingleElementList(EClassJob.Reaper); case EExtendedClassJob.Sage: return new global::_003C_003Ez__ReadOnlySingleElementList(EClassJob.Sage); case EExtendedClassJob.Viper: return new global::_003C_003Ez__ReadOnlySingleElementList(EClassJob.Viper); case EExtendedClassJob.Pictomancer: return new global::_003C_003Ez__ReadOnlySingleElementList(EClassJob.Pictomancer); case EExtendedClassJob.DoW: return from x in Enum.GetValues() where x.DealsPhysicalDamage() select x; case EExtendedClassJob.DoM: return from x in Enum.GetValues() where x.DealsMagicDamage() select x; case EExtendedClassJob.DoH: return from x in Enum.GetValues() where x.IsCrafter() select x; case EExtendedClassJob.DoL: return from x in Enum.GetValues() where x.IsGatherer() select x; case EExtendedClassJob.ConfiguredCombatJob: { EClassJob eClassJob2 = LookupConfiguredCombatJob(); IEnumerable result2; if (eClassJob2 == EClassJob.Adventurer) { IEnumerable enumerable = Array.Empty(); result2 = enumerable; } else { IEnumerable enumerable = new global::_003C_003Ez__ReadOnlySingleElementList(eClassJob2); result2 = enumerable; } return result2; } case EExtendedClassJob.QuestStartJob: { EClassJob eClassJob = LookupQuestStartJob(referenceQuest); IEnumerable result; if (eClassJob == EClassJob.Adventurer) { IEnumerable enumerable = Array.Empty(); result = enumerable; } else { IEnumerable enumerable = new global::_003C_003Ez__ReadOnlySingleElementList(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 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().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; } }