1
0
Fork 0
forked from aly/qstbak
qstbak/Questionable/Questionable.Data/DutyUnlockData.cs
2026-01-19 08:31:23 +10:00

384 lines
11 KiB
C#

using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Diagnostics.CodeAnalysis;
using System.Globalization;
using System.Linq;
using Dalamud.Game;
using Dalamud.Plugin.Services;
using Dalamud.Utility;
using FFXIVClientStructs.FFXIV.Client.Game.UI;
using Lumina.Excel;
using Lumina.Excel.Sheets;
using Microsoft.Extensions.Logging;
using Questionable.Model;
using Questionable.Model.Questing;
namespace Questionable.Data;
internal sealed class DutyUnlockData
{
private static class ContentTypeIds
{
public const uint Dungeon = 2u;
public const uint Guildhest = 3u;
public const uint Trial = 4u;
public const uint Raid = 5u;
public const uint PvP = 6u;
public const uint QuestBattle = 7u;
public const uint DeepDungeon = 21u;
public const uint AllianceRaid = 26u;
public const uint Ultimate = 28u;
public const uint VCDungeonFinder = 29u;
public const uint Criterion = 30u;
public const uint CriterionSavage = 31u;
}
private static class ContentMemberTypeIds
{
public const byte LightParty = 2;
public const byte FullParty = 3;
public const byte Alliance = 4;
}
private readonly ImmutableDictionary<uint, DutyInfo> _duties;
private readonly ImmutableDictionary<QuestId, ImmutableList<uint>> _questToDuties;
private readonly ImmutableDictionary<uint, ImmutableList<QuestId>> _dutyToQuests;
private readonly ILogger<DutyUnlockData> _logger;
private static readonly HashSet<uint> VariantDungeonCfcIds = new HashSet<uint> { 868u, 945u, 961u };
public DutyUnlockData(IDataManager dataManager, ILogger<DutyUnlockData> logger)
{
_logger = logger;
ExcelSheet<ContentFinderCondition> excelSheet = dataManager.GetExcelSheet<ContentFinderCondition>();
ExcelSheet<QuestEx> excelSheet2 = dataManager.GetExcelSheet<QuestEx>();
ImmutableDictionary<uint, uint> immutableDictionary = (from c in excelSheet
where c.RowId != 0 && c.Content.RowId != 0 && c.ContentLinkType == 1
group c by c.Content.RowId).ToImmutableDictionary((IGrouping<uint, ContentFinderCondition> g) => g.Key, (IGrouping<uint, ContentFinderCondition> g) => g.First().RowId);
Dictionary<QuestId, List<uint>> dictionary = new Dictionary<QuestId, List<uint>>();
Dictionary<uint, List<QuestId>> dictionary2 = new Dictionary<uint, List<QuestId>>();
foreach (QuestEx item in excelSheet2.Where((QuestEx q) => q.RowId != 0))
{
uint rowId = item.InstanceContentUnlock.RowId;
if (rowId != 0 && immutableDictionary.TryGetValue(rowId, out var value))
{
QuestId questId = QuestId.FromRowId(item.RowId);
if (!dictionary.ContainsKey(questId))
{
dictionary[questId] = new List<uint>();
}
dictionary[questId].Add(value);
if (!dictionary2.ContainsKey(value))
{
dictionary2[value] = new List<QuestId>();
}
dictionary2[value].Add(questId);
}
}
_questToDuties = dictionary.ToImmutableDictionary((KeyValuePair<QuestId, List<uint>> x) => x.Key, (KeyValuePair<QuestId, List<uint>> x) => x.Value.ToImmutableList());
_dutyToQuests = dictionary2.ToImmutableDictionary((KeyValuePair<uint, List<QuestId>> x) => x.Key, (KeyValuePair<uint, List<QuestId>> x) => x.Value.ToImmutableList());
Dictionary<uint, DutyInfo> dictionary3 = new Dictionary<uint, DutyInfo>();
foreach (ContentFinderCondition item2 in excelSheet.Where((ContentFinderCondition x) => x.RowId != 0 && x.Content.RowId != 0))
{
if (item2.ContentLinkType != 1)
{
continue;
}
ContentType? valueNullable = item2.ContentType.ValueNullable;
if (!valueNullable.HasValue)
{
continue;
}
uint rowId2 = valueNullable.Value.RowId;
if (rowId2 - 6 > 1)
{
string text = FixName(item2.Name.ToDalamudString().ToString(), dataManager.Language);
if (!string.IsNullOrWhiteSpace(text) && !IsExcludedContent(text, item2.RowId))
{
List<QuestId> value2;
ImmutableList<QuestId> unlockQuests = (dictionary2.TryGetValue(item2.RowId, out value2) ? value2.ToImmutableList() : ImmutableList<QuestId>.Empty);
uint rowId3 = item2.ContentMemberType.RowId;
EDutyCategory dutyCategory = DetermineDutyCategory(rowId2, text, item2.HighEndDuty, item2.RowId, (byte)rowId3);
DutyInfo value3 = new DutyInfo(item2.RowId, item2.Content.RowId, text, rowId2, valueNullable.Value.Name.ToDalamudString().ToString(), item2.ClassJobLevelRequired, item2.ItemLevelRequired, unlockQuests, item2.HighEndDuty, dutyCategory);
dictionary3[item2.RowId] = value3;
}
}
}
_duties = dictionary3.ToImmutableDictionary();
_logger.LogInformation("Loaded {DutyCount} duties with {QuestMappings} quest unlock mappings", _duties.Count, _questToDuties.Values.Sum((ImmutableList<uint> x) => x.Count));
}
private static bool IsExcludedContent(string name, uint cfcId)
{
if (cfcId - 69 <= 2)
{
return true;
}
return false;
}
private static EDutyCategory DetermineDutyCategory(uint contentTypeId, string name, bool isHighEndDuty, uint cfcId, byte contentMemberType)
{
if (VariantDungeonCfcIds.Contains(cfcId))
{
return EDutyCategory.VariantDungeon;
}
switch (contentTypeId)
{
case 2u:
if (name.EndsWith("(Hard)", StringComparison.OrdinalIgnoreCase))
{
return EDutyCategory.HardDungeon;
}
return EDutyCategory.Dungeon;
case 3u:
return EDutyCategory.Guildhest;
case 4u:
if (name.EndsWith("(Unreal)", StringComparison.OrdinalIgnoreCase))
{
return EDutyCategory.UnrealTrial;
}
if (name.EndsWith("(Extreme)", StringComparison.OrdinalIgnoreCase) || name.StartsWith("The Minstrel's Ballad:", StringComparison.OrdinalIgnoreCase) || isHighEndDuty)
{
return EDutyCategory.ExtremeTrial;
}
if (name.EndsWith("(Hard)", StringComparison.OrdinalIgnoreCase))
{
return EDutyCategory.HardTrial;
}
return EDutyCategory.Trial;
case 5u:
if (contentMemberType == 4)
{
if (isHighEndDuty)
{
return EDutyCategory.ChaoticAllianceRaid;
}
return EDutyCategory.AllianceRaid;
}
if (name.EndsWith("(Savage)", StringComparison.OrdinalIgnoreCase) || isHighEndDuty)
{
return EDutyCategory.SavageRaid;
}
return EDutyCategory.NormalRaid;
case 21u:
return EDutyCategory.DeepDungeon;
case 26u:
if (isHighEndDuty)
{
return EDutyCategory.ChaoticAllianceRaid;
}
return EDutyCategory.AllianceRaid;
case 28u:
return EDutyCategory.Ultimate;
case 29u:
if (name.EndsWith("(Savage)", StringComparison.OrdinalIgnoreCase) || isHighEndDuty)
{
return EDutyCategory.CriterionSavage;
}
return EDutyCategory.CriterionDungeon;
case 30u:
if (name.EndsWith("(Savage)", StringComparison.OrdinalIgnoreCase) || isHighEndDuty)
{
return EDutyCategory.CriterionSavage;
}
return EDutyCategory.CriterionDungeon;
case 31u:
return EDutyCategory.CriterionSavage;
default:
if (name.EndsWith("(Savage)", StringComparison.OrdinalIgnoreCase))
{
return EDutyCategory.SavageRaid;
}
if (name.EndsWith("(Extreme)", StringComparison.OrdinalIgnoreCase) || name.StartsWith("The Minstrel's Ballad:", StringComparison.OrdinalIgnoreCase))
{
return EDutyCategory.ExtremeTrial;
}
return EDutyCategory.Other;
}
}
private static string FixName(string name, ClientLanguage language)
{
if (string.IsNullOrEmpty(name) || language != ClientLanguage.English)
{
return name;
}
return string.Concat(name[0].ToString().ToUpper(CultureInfo.InvariantCulture).AsSpan(), name.AsSpan(1));
}
public IEnumerable<DutyInfo> GetAllDuties()
{
return _duties.Values;
}
public IEnumerable<DutyInfo> GetDutiesByContentType(uint contentTypeId)
{
return _duties.Values.Where((DutyInfo d) => d.ContentTypeId == contentTypeId);
}
public IEnumerable<DutyInfo> GetDutiesByCategory(EDutyCategory category)
{
return _duties.Values.Where((DutyInfo d) => d.DutyCategory == category);
}
public IEnumerable<DutyInfo> GetDungeons()
{
return GetDutiesByCategory(EDutyCategory.Dungeon);
}
public IEnumerable<DutyInfo> GetHardDungeons()
{
return GetDutiesByCategory(EDutyCategory.HardDungeon);
}
public IEnumerable<DutyInfo> GetGuildhests()
{
return GetDutiesByCategory(EDutyCategory.Guildhest);
}
public IEnumerable<DutyInfo> GetTrials()
{
return GetDutiesByCategory(EDutyCategory.Trial);
}
public IEnumerable<DutyInfo> GetHardTrials()
{
return GetDutiesByCategory(EDutyCategory.HardTrial);
}
public IEnumerable<DutyInfo> GetExtremeTrials()
{
return GetDutiesByCategory(EDutyCategory.ExtremeTrial);
}
public IEnumerable<DutyInfo> GetUnrealTrials()
{
return GetDutiesByCategory(EDutyCategory.UnrealTrial);
}
public IEnumerable<DutyInfo> GetNormalRaids()
{
return GetDutiesByCategory(EDutyCategory.NormalRaid);
}
public IEnumerable<DutyInfo> GetSavageRaids()
{
return GetDutiesByCategory(EDutyCategory.SavageRaid);
}
public IEnumerable<DutyInfo> GetUltimateRaids()
{
return GetDutiesByCategory(EDutyCategory.Ultimate);
}
public IEnumerable<DutyInfo> GetAllianceRaids()
{
return GetDutiesByCategory(EDutyCategory.AllianceRaid);
}
public IEnumerable<DutyInfo> GetChaoticAllianceRaids()
{
return GetDutiesByCategory(EDutyCategory.ChaoticAllianceRaid);
}
public IEnumerable<DutyInfo> GetDeepDungeons()
{
return GetDutiesByCategory(EDutyCategory.DeepDungeon);
}
public IEnumerable<DutyInfo> GetVariantDungeons()
{
return GetDutiesByCategory(EDutyCategory.VariantDungeon);
}
public IEnumerable<DutyInfo> GetCriterionDungeons()
{
return GetDutiesByCategory(EDutyCategory.CriterionDungeon);
}
public IEnumerable<DutyInfo> GetCriterionSavageDungeons()
{
return GetDutiesByCategory(EDutyCategory.CriterionSavage);
}
public IEnumerable<DutyInfo> GetDutiesUnlockedByQuest(QuestId questId)
{
if (!_questToDuties.TryGetValue(questId, out ImmutableList<uint> value))
{
yield break;
}
foreach (uint item in value)
{
if (_duties.TryGetValue(item, out DutyInfo value2))
{
yield return value2;
}
}
}
public IEnumerable<QuestId> GetUnlockQuestsForDuty(uint cfcId)
{
if (_dutyToQuests.TryGetValue(cfcId, out ImmutableList<QuestId> value))
{
return value;
}
return Array.Empty<QuestId>();
}
public bool IsDutyUnlocked(uint cfcId)
{
if (!_duties.TryGetValue(cfcId, out DutyInfo value))
{
return false;
}
return UIState.IsInstanceContentUnlocked(value.ContentId);
}
public static bool IsContentUnlocked(uint contentId)
{
return UIState.IsInstanceContentUnlocked(contentId);
}
public bool TryGetDuty(uint cfcId, [NotNullWhen(true)] out DutyInfo? duty)
{
return _duties.TryGetValue(cfcId, out duty);
}
public DutyInfo? GetDuty(uint cfcId)
{
return _duties.GetValueOrDefault(cfcId);
}
public IEnumerable<DutyInfo> GetUnlockedDuties()
{
return _duties.Values.Where((DutyInfo d) => UIState.IsInstanceContentUnlocked(d.ContentId));
}
public IEnumerable<DutyInfo> GetLockedDuties()
{
return _duties.Values.Where((DutyInfo d) => !UIState.IsInstanceContentUnlocked(d.ContentId));
}
public IEnumerable<DutyInfo> GetDutiesWithUnlockQuests()
{
return _duties.Values.Where((DutyInfo d) => d.UnlockQuests.Count > 0);
}
}