284 lines
11 KiB
C#
284 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 System.Runtime.CompilerServices;
|
|
using Dalamud.Game;
|
|
using Dalamud.Plugin.Services;
|
|
using Dalamud.Utility;
|
|
using Lumina.Excel;
|
|
using Lumina.Excel.Sheets;
|
|
using Microsoft.Extensions.Logging;
|
|
using Questionable.Model.Questing;
|
|
using SmartNav;
|
|
|
|
namespace Questionable.Data;
|
|
|
|
internal sealed class TerritoryData : ITerritoryInfo
|
|
{
|
|
public readonly record struct ZoneAccessRequirement(byte RequirementType, uint QuestRowId, uint RequiredSequence);
|
|
|
|
public sealed record ContentFinderConditionData(uint ContentFinderConditionId, string Name, uint TerritoryId, ushort RequiredItemLevel, byte ClassJobLevelSync, bool AllowUndersized, bool IsDungeon)
|
|
{
|
|
public ContentFinderConditionData(ContentFinderCondition condition, ClientLanguage clientLanguage)
|
|
: this(condition.RowId, FixName(condition.Name.ToDalamudString().ToString(), clientLanguage), condition.TerritoryType.RowId, condition.ItemLevelRequired, condition.ClassJobLevelSync, condition.AllowUndersized, condition.ContentType.RowId == 2)
|
|
{
|
|
}
|
|
}
|
|
|
|
private readonly ImmutableDictionary<uint, string> _territoryNames;
|
|
|
|
private readonly ImmutableHashSet<uint> _territoriesWithMount;
|
|
|
|
private readonly ImmutableDictionary<uint, byte> _territoryExVersions;
|
|
|
|
private readonly ImmutableDictionary<uint, ZoneAccessRequirement> _territoryZoneAccessRequirements;
|
|
|
|
private readonly ImmutableDictionary<uint, uint> _dutyTerritories;
|
|
|
|
private readonly ImmutableDictionary<uint, string> _instanceNames;
|
|
|
|
private readonly ImmutableDictionary<uint, ContentFinderConditionData> _contentFinderConditions;
|
|
|
|
private readonly ImmutableDictionary<uint, uint> _territoryToContentFinderCondition;
|
|
|
|
private readonly ImmutableDictionary<(ElementId QuestId, byte Index), uint> _questBattlesToContentFinderCondition;
|
|
|
|
public TerritoryData(IDataManager dataManager, ILogger<TerritoryData> logger)
|
|
{
|
|
Dictionary<uint, ZoneAccessRequirement> dictionary = dataManager.GetSubrowExcelSheet<ZoneSharedGroup>().Flatten().Where(delegate(ZoneSharedGroup x)
|
|
{
|
|
bool flag = x.SubrowId == 0;
|
|
if (flag)
|
|
{
|
|
byte b = x.RequirementType[0];
|
|
bool flag2 = (uint)(b - 1) <= 1u;
|
|
flag = flag2;
|
|
}
|
|
return flag;
|
|
})
|
|
.ToDictionary((ZoneSharedGroup x) => x.RowId, (ZoneSharedGroup x) => new ZoneAccessRequirement(x.RequirementType[0], x.RequirementRow[0].RowId, x.RequirementQuestSequence[0]));
|
|
ImmutableDictionary<uint, string>.Builder builder = ImmutableDictionary.CreateBuilder<uint, string>();
|
|
ImmutableHashSet<uint>.Builder builder2 = ImmutableHashSet.CreateBuilder<uint>();
|
|
ImmutableDictionary<uint, byte>.Builder builder3 = ImmutableDictionary.CreateBuilder<uint, byte>();
|
|
ImmutableDictionary<uint, ZoneAccessRequirement>.Builder builder4 = ImmutableDictionary.CreateBuilder<uint, ZoneAccessRequirement>();
|
|
ImmutableDictionary<uint, uint>.Builder builder5 = ImmutableDictionary.CreateBuilder<uint, uint>();
|
|
List<uint> list = new List<uint>();
|
|
foreach (TerritoryType item in dataManager.GetExcelSheet<TerritoryType>())
|
|
{
|
|
if (item.RowId == 0)
|
|
{
|
|
continue;
|
|
}
|
|
string value = item.PlaceName.ValueNullable?.Name.ToString() ?? item.PlaceNameZone.ValueNullable?.Name.ToString();
|
|
if (!string.IsNullOrEmpty(value))
|
|
{
|
|
builder[item.RowId] = value;
|
|
}
|
|
if (item.Mount)
|
|
{
|
|
builder2.Add(item.RowId);
|
|
}
|
|
if (item.ExVersion.RowId != 0)
|
|
{
|
|
builder3[item.RowId] = (byte)item.ExVersion.RowId;
|
|
}
|
|
if (item.ZoneSharedGroup.RowId != 0 && dictionary.TryGetValue(item.ZoneSharedGroup.RowId, out var value2))
|
|
{
|
|
if ((dataManager.GetExcelSheet<Quest>().GetRowOrDefault(value2.QuestRowId)?.Expansion.RowId ?? 0) > item.ExVersion.RowId)
|
|
{
|
|
list.Add(item.RowId);
|
|
}
|
|
else
|
|
{
|
|
builder4[item.RowId] = value2;
|
|
}
|
|
}
|
|
RowRef<ContentFinderCondition> contentFinderCondition = item.ContentFinderCondition;
|
|
if (contentFinderCondition.RowId != 0)
|
|
{
|
|
ContentFinderCondition? valueNullable = contentFinderCondition.ValueNullable;
|
|
if (valueNullable.HasValue)
|
|
{
|
|
ContentFinderCondition valueOrDefault = valueNullable.GetValueOrDefault();
|
|
builder5[item.RowId] = valueOrDefault.ContentType.RowId;
|
|
}
|
|
}
|
|
}
|
|
_territoryNames = builder.ToImmutable();
|
|
_territoriesWithMount = builder2.ToImmutable();
|
|
_territoryExVersions = builder3.ToImmutable();
|
|
_territoryZoneAccessRequirements = builder4.ToImmutable();
|
|
_dutyTerritories = builder5.ToImmutable();
|
|
if (list.Count > 0)
|
|
{
|
|
logger.LogDebug("Ignoring later-expansion ZoneSharedGroup gate for {Count} walkable zones: {Zones}", list.Count, string.Join(", ", list.Select(GetNameAndId)));
|
|
}
|
|
_instanceNames = (from x in dataManager.GetExcelSheet<ContentFinderCondition>()
|
|
where x.RowId != 0 && x.Content.RowId != 0 && x.ContentLinkType == 1 && x.ContentType.RowId != 6
|
|
group x by x.Content.RowId).ToImmutableDictionary((IGrouping<uint, ContentFinderCondition> g) => g.Key, (IGrouping<uint, ContentFinderCondition> g) => g.First().Name.ToDalamudString().ToString());
|
|
_contentFinderConditions = (from x in dataManager.GetExcelSheet<ContentFinderCondition>().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);
|
|
_territoryToContentFinderCondition = (from x in dataManager.GetExcelSheet<ContentFinderCondition>()
|
|
where x.RowId != 0 && x.TerritoryType.RowId != 0
|
|
group x by x.TerritoryType.RowId).ToImmutableDictionary((IGrouping<uint, ContentFinderCondition> g) => g.Key, (IGrouping<uint, ContentFinderCondition> g) => g.First().RowId);
|
|
_questBattlesToContentFinderCondition = (from x in (from x in dataManager.GetExcelSheet<Quest>()
|
|
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(uint territoryId)
|
|
{
|
|
return _territoryNames.GetValueOrDefault(territoryId);
|
|
}
|
|
|
|
public string GetNameAndId(uint 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(uint territoryId)
|
|
{
|
|
return _territoriesWithMount.Contains(territoryId);
|
|
}
|
|
|
|
public byte GetExVersion(uint territoryId)
|
|
{
|
|
if (!_territoryExVersions.TryGetValue(territoryId, out var value))
|
|
{
|
|
return 0;
|
|
}
|
|
return value;
|
|
}
|
|
|
|
public ZoneAccessRequirement? GetZoneAccessRequirement(uint territoryId)
|
|
{
|
|
if (!_territoryZoneAccessRequirements.TryGetValue(territoryId, out var value))
|
|
{
|
|
return null;
|
|
}
|
|
return value;
|
|
}
|
|
|
|
public bool IsDutyInstance(uint territoryId)
|
|
{
|
|
return _dutyTerritories.ContainsKey(territoryId);
|
|
}
|
|
|
|
public bool IsQuestBattleInstance(uint territoryId)
|
|
{
|
|
if (_dutyTerritories.TryGetValue(territoryId, out var value))
|
|
{
|
|
return value == 7;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
public bool IsFieldOperation(uint territoryId)
|
|
{
|
|
uint value;
|
|
bool flag = _dutyTerritories.TryGetValue(territoryId, out value);
|
|
if (flag)
|
|
{
|
|
bool flag2 = ((value == 26 || value == 29 || value == 38) ? true : false);
|
|
flag = flag2;
|
|
}
|
|
return flag;
|
|
}
|
|
|
|
public string? GetInstanceName(uint 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 bool TryGetContentFinderConditionIdForTerritory(uint territoryId, out uint cfcId)
|
|
{
|
|
return _territoryToContentFinderCondition.TryGetValue(territoryId, out cfcId);
|
|
}
|
|
|
|
public IEnumerable<(ElementId QuestId, byte Index, ContentFinderConditionData Data)> GetAllQuestsWithQuestBattles()
|
|
{
|
|
return _questBattlesToContentFinderCondition.Select<KeyValuePair<(ElementId, byte), uint>, (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).AsSpan(), 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<InstanceContent>().GetRow(questBattleId).ContentFinderCondition.RowId;
|
|
}
|
|
return dataManager.GetExcelSheet<QuestBattleResident>().GetRow(questBattleId).SoloDuty.RowId;
|
|
}
|
|
}
|