using System; using System.Collections.Generic; using System.Collections.Immutable; using System.Diagnostics.CodeAnalysis; using System.Globalization; using System.Linq; using System.Runtime.CompilerServices; using Dalamud.Game; using Dalamud.Plugin.Services; using Dalamud.Utility; using Lumina.Excel.Sheets; using Questionable.Model.Questing; namespace Questionable.Data; internal sealed class TerritoryData { public sealed record ContentFinderConditionData(uint ContentFinderConditionId, string Name, uint TerritoryId, ushort RequiredItemLevel) { public ContentFinderConditionData(ContentFinderCondition condition, ClientLanguage clientLanguage) : this(condition.RowId, FixName(condition.Name.ToDalamudString().ToString(), clientLanguage), condition.TerritoryType.RowId, condition.ItemLevelRequired) { } } private readonly ImmutableDictionary _territoryNames; private readonly ImmutableHashSet _territoriesWithMount; private readonly ImmutableDictionary _dutyTerritories; private readonly ImmutableDictionary _instanceNames; private readonly ImmutableDictionary _contentFinderConditions; private readonly ImmutableDictionary<(ElementId QuestId, byte Index), uint> _questBattlesToContentFinderCondition; public TerritoryData(IDataManager dataManager) { _territoryNames = (from x in dataManager.GetExcelSheet() where x.RowId != 0 select new { RowId = x.RowId, Name = (x.PlaceName.ValueNullable?.Name.ToString() ?? x.PlaceNameZone.ValueNullable?.Name.ToString()) } into x where !string.IsNullOrEmpty(x.Name) select x).ToImmutableDictionary(x => x.RowId, x => x.Name); _territoriesWithMount = (from x in dataManager.GetExcelSheet() where x.RowId != 0 && x.Mount select (ushort)x.RowId).ToImmutableHashSet(); _dutyTerritories = (from x in dataManager.GetExcelSheet() where x.RowId != 0 && x.ContentFinderCondition.RowId != 0 select x).ToImmutableDictionary((TerritoryType x) => (ushort)x.RowId, (TerritoryType x) => x.ContentFinderCondition.Value.ContentType.RowId); _instanceNames = (from x in dataManager.GetExcelSheet() where x.RowId != 0 && x.Content.RowId != 0 && x.ContentLinkType == 1 && x.ContentType.RowId != 6 select x).ToImmutableDictionary((ContentFinderCondition x) => x.Content.RowId, (ContentFinderCondition x) => x.Name.ToDalamudString().ToString()); _contentFinderConditions = (from x in dataManager.GetExcelSheet().Where(delegate(ContentFinderCondition x) { bool flag = x.RowId != 0 && x.Content.RowId != 0; if (flag) { byte contentLinkType = x.ContentLinkType; bool flag2 = ((contentLinkType == 1 || contentLinkType == 5) ? true : false); flag = flag2; } return flag && x.ContentType.RowId != 6; }) select new ContentFinderConditionData(x, dataManager.Language)).ToImmutableDictionary((ContentFinderConditionData x) => x.ContentFinderConditionId, (ContentFinderConditionData x) => x); _questBattlesToContentFinderCondition = (from x in (from x in dataManager.GetExcelSheet() where x.RowId != 0 && x.IssuerLocation.RowId != 0 select x).SelectMany(GetQuestBattles) select (QuestId: x.QuestId, Index: x.Index, CfcId: LookupContentFinderConditionForQuestBattle(dataManager, x.QuestBattleId))).ToImmutableDictionary<(ElementId, byte, uint), (ElementId, byte), uint>(((ElementId QuestId, byte Index, uint CfcId) x) => (QuestId: x.QuestId, Index: x.Index), ((ElementId QuestId, byte Index, uint CfcId) x) => x.CfcId); } public string? GetName(ushort territoryId) { return _territoryNames.GetValueOrDefault(territoryId); } public string GetNameAndId(ushort territoryId) { string name = GetName(territoryId); if (name != null) { IFormatProvider invariantCulture = CultureInfo.InvariantCulture; DefaultInterpolatedStringHandler handler = new DefaultInterpolatedStringHandler(3, 2, invariantCulture); handler.AppendFormatted(name); handler.AppendLiteral(" ("); handler.AppendFormatted(territoryId); handler.AppendLiteral(")"); return string.Create(invariantCulture, ref handler); } return territoryId.ToString(CultureInfo.InvariantCulture); } public bool CanUseMount(ushort territoryId) { return _territoriesWithMount.Contains(territoryId); } public bool IsDutyInstance(ushort territoryId) { return _dutyTerritories.ContainsKey(territoryId); } public bool IsQuestBattleInstance(ushort territoryId) { if (_dutyTerritories.TryGetValue(territoryId, out var value)) { return value == 7; } return false; } public string? GetInstanceName(ushort instanceId) { return _instanceNames.GetValueOrDefault(instanceId); } public ContentFinderConditionData? GetContentFinderCondition(uint cfcId) { return _contentFinderConditions.GetValueOrDefault(cfcId); } public bool TryGetContentFinderCondition(uint cfcId, [NotNullWhen(true)] out ContentFinderConditionData? contentFinderConditionData) { return _contentFinderConditions.TryGetValue(cfcId, out contentFinderConditionData); } public bool TryGetContentFinderConditionForSoloInstance(ElementId questId, byte index, [NotNullWhen(true)] out ContentFinderConditionData? contentFinderConditionData) { if (_questBattlesToContentFinderCondition.TryGetValue((questId, index), out var value)) { return _contentFinderConditions.TryGetValue(value, out contentFinderConditionData); } contentFinderConditionData = null; return false; } public IEnumerable<(ElementId QuestId, byte Index, ContentFinderConditionData Data)> GetAllQuestsWithQuestBattles() { return _questBattlesToContentFinderCondition.Select, (ElementId, byte, ContentFinderConditionData)>((KeyValuePair<(ElementId QuestId, byte Index), uint> x) => (QuestId: x.Key.QuestId, Index: x.Key.Index, _contentFinderConditions[x.Value])); } 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), name.AsSpan(1)); } private static IEnumerable<(ElementId QuestId, byte Index, uint QuestBattleId)> GetQuestBattles(Quest quest) { foreach (Quest.QuestParamsStruct questParam in quest.QuestParams) { if (questParam.ScriptInstruction == "QUESTBATTLE0") { yield return (QuestId: QuestId.FromRowId(quest.RowId), Index: 0, QuestBattleId: questParam.ScriptArg); } else if (questParam.ScriptInstruction == "QUESTBATTLE1") { yield return (QuestId: QuestId.FromRowId(quest.RowId), Index: 1, QuestBattleId: questParam.ScriptArg); } else if (questParam.ScriptInstruction.IsEmpty) { break; } } } private static uint LookupContentFinderConditionForQuestBattle(IDataManager dataManager, uint questBattleId) { if (questBattleId >= 5000) { return dataManager.GetExcelSheet().GetRow(questBattleId).ContentFinderCondition.RowId; } return dataManager.GetExcelSheet().GetRow(questBattleId).Unknown0; } }