479 lines
15 KiB
C#
479 lines
15 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.Linq;
|
|
using System.Text;
|
|
using FFXIVClientStructs.FFXIV.Client.UI.Agent;
|
|
|
|
namespace QuestionableCompanion.Data;
|
|
|
|
public static class MSQExpansionData
|
|
{
|
|
public enum Expansion
|
|
{
|
|
ARealmReborn,
|
|
Heavensward,
|
|
Stormblood,
|
|
Shadowbringers,
|
|
Endwalker,
|
|
Dawntrail
|
|
}
|
|
|
|
private static readonly Dictionary<Expansion, HashSet<uint>> ExpansionQuests = new Dictionary<Expansion, HashSet<uint>>
|
|
{
|
|
{
|
|
Expansion.ARealmReborn,
|
|
new HashSet<uint>()
|
|
},
|
|
{
|
|
Expansion.Heavensward,
|
|
new HashSet<uint>()
|
|
},
|
|
{
|
|
Expansion.Stormblood,
|
|
new HashSet<uint>()
|
|
},
|
|
{
|
|
Expansion.Shadowbringers,
|
|
new HashSet<uint>()
|
|
},
|
|
{
|
|
Expansion.Endwalker,
|
|
new HashSet<uint>()
|
|
},
|
|
{
|
|
Expansion.Dawntrail,
|
|
new HashSet<uint>()
|
|
}
|
|
};
|
|
|
|
private static readonly Dictionary<Expansion, int> ExpectedQuestCounts = new Dictionary<Expansion, int>
|
|
{
|
|
{
|
|
Expansion.ARealmReborn,
|
|
200
|
|
},
|
|
{
|
|
Expansion.Heavensward,
|
|
100
|
|
},
|
|
{
|
|
Expansion.Stormblood,
|
|
100
|
|
},
|
|
{
|
|
Expansion.Shadowbringers,
|
|
100
|
|
},
|
|
{
|
|
Expansion.Endwalker,
|
|
100
|
|
},
|
|
{
|
|
Expansion.Dawntrail,
|
|
100
|
|
}
|
|
};
|
|
|
|
private static readonly Dictionary<Expansion, string> ExpansionNames = new Dictionary<Expansion, string>
|
|
{
|
|
{
|
|
Expansion.ARealmReborn,
|
|
"A Realm Reborn"
|
|
},
|
|
{
|
|
Expansion.Heavensward,
|
|
"Heavensward"
|
|
},
|
|
{
|
|
Expansion.Stormblood,
|
|
"Stormblood"
|
|
},
|
|
{
|
|
Expansion.Shadowbringers,
|
|
"Shadowbringers"
|
|
},
|
|
{
|
|
Expansion.Endwalker,
|
|
"Endwalker"
|
|
},
|
|
{
|
|
Expansion.Dawntrail,
|
|
"Dawntrail"
|
|
}
|
|
};
|
|
|
|
private static readonly Dictionary<Expansion, string> ExpansionShortNames = new Dictionary<Expansion, string>
|
|
{
|
|
{
|
|
Expansion.ARealmReborn,
|
|
"ARR"
|
|
},
|
|
{
|
|
Expansion.Heavensward,
|
|
"HW"
|
|
},
|
|
{
|
|
Expansion.Stormblood,
|
|
"SB"
|
|
},
|
|
{
|
|
Expansion.Shadowbringers,
|
|
"ShB"
|
|
},
|
|
{
|
|
Expansion.Endwalker,
|
|
"EW"
|
|
},
|
|
{
|
|
Expansion.Dawntrail,
|
|
"DT"
|
|
}
|
|
};
|
|
|
|
public static void RegisterQuest(uint questId, Expansion expansion)
|
|
{
|
|
if (ExpansionQuests.TryGetValue(expansion, out HashSet<uint> quests))
|
|
{
|
|
quests.Add(questId);
|
|
}
|
|
}
|
|
|
|
public static void ClearQuests()
|
|
{
|
|
foreach (HashSet<uint> value in ExpansionQuests.Values)
|
|
{
|
|
value.Clear();
|
|
}
|
|
}
|
|
|
|
public static Expansion GetExpansionForQuest(uint questId)
|
|
{
|
|
foreach (var (expansion2, hashSet2) in ExpansionQuests)
|
|
{
|
|
if (hashSet2.Contains(questId))
|
|
{
|
|
return expansion2;
|
|
}
|
|
}
|
|
return Expansion.ARealmReborn;
|
|
}
|
|
|
|
public static IReadOnlySet<uint> GetQuestsForExpansion(Expansion expansion)
|
|
{
|
|
if (!ExpansionQuests.TryGetValue(expansion, out HashSet<uint> quests))
|
|
{
|
|
return new HashSet<uint>();
|
|
}
|
|
return quests;
|
|
}
|
|
|
|
public static int GetExpectedQuestCount(Expansion expansion)
|
|
{
|
|
if (!ExpectedQuestCounts.TryGetValue(expansion, out var count))
|
|
{
|
|
return 0;
|
|
}
|
|
return count;
|
|
}
|
|
|
|
public static string GetExpansionName(Expansion expansion)
|
|
{
|
|
if (!ExpansionNames.TryGetValue(expansion, out string name))
|
|
{
|
|
return "Unknown";
|
|
}
|
|
return name;
|
|
}
|
|
|
|
public static string GetExpansionShortName(Expansion expansion)
|
|
{
|
|
if (!ExpansionShortNames.TryGetValue(expansion, out string name))
|
|
{
|
|
return "???";
|
|
}
|
|
return name;
|
|
}
|
|
|
|
public static IEnumerable<Expansion> GetAllExpansions()
|
|
{
|
|
return from e in Enum.GetValues<Expansion>()
|
|
orderby (int)e
|
|
select e;
|
|
}
|
|
|
|
public static int GetCompletedQuestCountForExpansion(IEnumerable<uint> completedQuestIds, Expansion expansion)
|
|
{
|
|
IReadOnlySet<uint> expansionQuests = GetQuestsForExpansion(expansion);
|
|
return completedQuestIds.Count((uint qId) => expansionQuests.Contains(qId));
|
|
}
|
|
|
|
public unsafe static (Expansion expansion, string debugInfo) GetCurrentExpansionFromGameWithDebug()
|
|
{
|
|
StringBuilder debug = new StringBuilder();
|
|
debug.AppendLine("=== AGENT SCENARIO TREE DEBUG ===");
|
|
try
|
|
{
|
|
AgentScenarioTree* agentScenarioTree = AgentScenarioTree.Instance();
|
|
StringBuilder stringBuilder = debug;
|
|
StringBuilder stringBuilder2 = stringBuilder;
|
|
StringBuilder.AppendInterpolatedStringHandler handler = new StringBuilder.AppendInterpolatedStringHandler(30, 1, stringBuilder);
|
|
handler.AppendLiteral("AgentScenarioTree.Instance(): ");
|
|
handler.AppendFormatted((agentScenarioTree != null) ? "OK" : "NULL");
|
|
stringBuilder2.AppendLine(ref handler);
|
|
if (agentScenarioTree == null)
|
|
{
|
|
debug.AppendLine("ERROR: AgentScenarioTree is NULL!");
|
|
return (expansion: Expansion.ARealmReborn, debugInfo: debug.ToString());
|
|
}
|
|
stringBuilder = debug;
|
|
StringBuilder stringBuilder3 = stringBuilder;
|
|
handler = new StringBuilder.AppendInterpolatedStringHandler(25, 1, stringBuilder);
|
|
handler.AppendLiteral("AgentScenarioTree->Data: ");
|
|
handler.AppendFormatted((agentScenarioTree->Data != null) ? "OK" : "NULL");
|
|
stringBuilder3.AppendLine(ref handler);
|
|
if (agentScenarioTree->Data == null)
|
|
{
|
|
debug.AppendLine("ERROR: AgentScenarioTree->Data is NULL!");
|
|
return (expansion: Expansion.ARealmReborn, debugInfo: debug.ToString());
|
|
}
|
|
ushort currentQuest = agentScenarioTree->Data->CurrentScenarioQuest;
|
|
ushort completedQuest = agentScenarioTree->Data->CompleteScenarioQuest;
|
|
stringBuilder = debug;
|
|
StringBuilder stringBuilder4 = stringBuilder;
|
|
handler = new StringBuilder.AppendInterpolatedStringHandler(33, 2, stringBuilder);
|
|
handler.AppendLiteral("CurrentScenarioQuest (raw): ");
|
|
handler.AppendFormatted(currentQuest);
|
|
handler.AppendLiteral(" (0x");
|
|
handler.AppendFormatted(currentQuest, "X4");
|
|
handler.AppendLiteral(")");
|
|
stringBuilder4.AppendLine(ref handler);
|
|
stringBuilder = debug;
|
|
StringBuilder stringBuilder5 = stringBuilder;
|
|
handler = new StringBuilder.AppendInterpolatedStringHandler(34, 2, stringBuilder);
|
|
handler.AppendLiteral("CompleteScenarioQuest (raw): ");
|
|
handler.AppendFormatted(completedQuest);
|
|
handler.AppendLiteral(" (0x");
|
|
handler.AppendFormatted(completedQuest, "X4");
|
|
handler.AppendLiteral(")");
|
|
stringBuilder5.AppendLine(ref handler);
|
|
ushort questToCheck = ((currentQuest != 0) ? currentQuest : completedQuest);
|
|
stringBuilder = debug;
|
|
StringBuilder stringBuilder6 = stringBuilder;
|
|
handler = new StringBuilder.AppendInterpolatedStringHandler(25, 2, stringBuilder);
|
|
handler.AppendLiteral("Quest to check: ");
|
|
handler.AppendFormatted(questToCheck);
|
|
handler.AppendLiteral(" (using ");
|
|
handler.AppendFormatted((currentQuest != 0) ? "Current" : "Completed");
|
|
handler.AppendLiteral(")");
|
|
stringBuilder6.AppendLine(ref handler);
|
|
if (questToCheck == 0)
|
|
{
|
|
debug.AppendLine("WARNING: Both CurrentScenarioQuest and CompleteScenarioQuest are 0!");
|
|
return (expansion: Expansion.ARealmReborn, debugInfo: debug.ToString());
|
|
}
|
|
uint questId = (uint)(questToCheck | 0x10000);
|
|
stringBuilder = debug;
|
|
StringBuilder stringBuilder7 = stringBuilder;
|
|
handler = new StringBuilder.AppendInterpolatedStringHandler(25, 2, stringBuilder);
|
|
handler.AppendLiteral("Converted Quest ID: ");
|
|
handler.AppendFormatted(questId);
|
|
handler.AppendLiteral(" (0x");
|
|
handler.AppendFormatted(questId, "X8");
|
|
handler.AppendLiteral(")");
|
|
stringBuilder7.AppendLine(ref handler);
|
|
Expansion expansion = GetExpansionForQuest(questId);
|
|
stringBuilder = debug;
|
|
StringBuilder stringBuilder8 = stringBuilder;
|
|
handler = new StringBuilder.AppendInterpolatedStringHandler(22, 2, stringBuilder);
|
|
handler.AppendLiteral("Expansion for Quest ");
|
|
handler.AppendFormatted(questId);
|
|
handler.AppendLiteral(": ");
|
|
handler.AppendFormatted(GetExpansionName(expansion));
|
|
stringBuilder8.AppendLine(ref handler);
|
|
IReadOnlySet<uint> expansionQuests = GetQuestsForExpansion(expansion);
|
|
bool isRegistered = expansionQuests.Contains(questId);
|
|
stringBuilder = debug;
|
|
StringBuilder stringBuilder9 = stringBuilder;
|
|
handler = new StringBuilder.AppendInterpolatedStringHandler(23, 3, stringBuilder);
|
|
handler.AppendLiteral("Quest ");
|
|
handler.AppendFormatted(questId);
|
|
handler.AppendLiteral(" registered in ");
|
|
handler.AppendFormatted(expansion);
|
|
handler.AppendLiteral(": ");
|
|
handler.AppendFormatted(isRegistered);
|
|
stringBuilder9.AppendLine(ref handler);
|
|
if (!isRegistered)
|
|
{
|
|
stringBuilder = debug;
|
|
StringBuilder stringBuilder10 = stringBuilder;
|
|
handler = new StringBuilder.AppendInterpolatedStringHandler(48, 1, stringBuilder);
|
|
handler.AppendLiteral("WARNING: Quest ");
|
|
handler.AppendFormatted(questId);
|
|
handler.AppendLiteral(" is NOT in our registered quests!");
|
|
stringBuilder10.AppendLine(ref handler);
|
|
stringBuilder = debug;
|
|
StringBuilder stringBuilder11 = stringBuilder;
|
|
handler = new StringBuilder.AppendInterpolatedStringHandler(30, 2, stringBuilder);
|
|
handler.AppendLiteral("Total registered quests for ");
|
|
handler.AppendFormatted(expansion);
|
|
handler.AppendLiteral(": ");
|
|
handler.AppendFormatted(expansionQuests.Count);
|
|
stringBuilder11.AppendLine(ref handler);
|
|
foreach (Expansion exp in GetAllExpansions())
|
|
{
|
|
if (GetQuestsForExpansion(exp).Contains(questId))
|
|
{
|
|
stringBuilder = debug;
|
|
StringBuilder stringBuilder12 = stringBuilder;
|
|
handler = new StringBuilder.AppendInterpolatedStringHandler(10, 1, stringBuilder);
|
|
handler.AppendLiteral("FOUND in ");
|
|
handler.AppendFormatted(exp);
|
|
handler.AppendLiteral("!");
|
|
stringBuilder12.AppendLine(ref handler);
|
|
expansion = exp;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
stringBuilder = debug;
|
|
StringBuilder stringBuilder13 = stringBuilder;
|
|
handler = new StringBuilder.AppendInterpolatedStringHandler(25, 1, stringBuilder);
|
|
handler.AppendLiteral(">>> FINAL EXPANSION: ");
|
|
handler.AppendFormatted(GetExpansionName(expansion));
|
|
handler.AppendLiteral(" <<<");
|
|
stringBuilder13.AppendLine(ref handler);
|
|
return (expansion: expansion, debugInfo: debug.ToString());
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
StringBuilder stringBuilder = debug;
|
|
StringBuilder stringBuilder14 = stringBuilder;
|
|
StringBuilder.AppendInterpolatedStringHandler handler = new StringBuilder.AppendInterpolatedStringHandler(11, 1, stringBuilder);
|
|
handler.AppendLiteral("EXCEPTION: ");
|
|
handler.AppendFormatted(ex.Message);
|
|
stringBuilder14.AppendLine(ref handler);
|
|
stringBuilder = debug;
|
|
StringBuilder stringBuilder15 = stringBuilder;
|
|
handler = new StringBuilder.AppendInterpolatedStringHandler(7, 1, stringBuilder);
|
|
handler.AppendLiteral("Stack: ");
|
|
handler.AppendFormatted(ex.StackTrace);
|
|
stringBuilder15.AppendLine(ref handler);
|
|
return (expansion: Expansion.ARealmReborn, debugInfo: debug.ToString());
|
|
}
|
|
}
|
|
|
|
public static Expansion GetCurrentExpansionFromGame()
|
|
{
|
|
return GetCurrentExpansionFromGameWithDebug().expansion;
|
|
}
|
|
|
|
public static Expansion GetCurrentExpansion(IEnumerable<uint> completedQuestIds)
|
|
{
|
|
List<uint> questList = completedQuestIds.ToList();
|
|
if (questList.Count == 0)
|
|
{
|
|
return Expansion.ARealmReborn;
|
|
}
|
|
foreach (Expansion expansion in GetAllExpansions().Reverse().ToList())
|
|
{
|
|
IReadOnlySet<uint> expansionQuests = GetQuestsForExpansion(expansion);
|
|
if (questList.Where((uint qId) => expansionQuests.Contains(qId)).ToList().Count > 0)
|
|
{
|
|
return expansion;
|
|
}
|
|
}
|
|
return Expansion.ARealmReborn;
|
|
}
|
|
|
|
public static string GetExpansionDetectionDebugInfo(IEnumerable<uint> completedQuestIds)
|
|
{
|
|
List<uint> questList = completedQuestIds.ToList();
|
|
StringBuilder result = new StringBuilder();
|
|
result.AppendLine("=== EXPANSION DETECTION DEBUG ===");
|
|
StringBuilder stringBuilder = result;
|
|
StringBuilder stringBuilder2 = stringBuilder;
|
|
StringBuilder.AppendInterpolatedStringHandler handler = new StringBuilder.AppendInterpolatedStringHandler(24, 1, stringBuilder);
|
|
handler.AppendLiteral("Total completed quests: ");
|
|
handler.AppendFormatted(questList.Count);
|
|
stringBuilder2.AppendLine(ref handler);
|
|
result.AppendLine("");
|
|
result.AppendLine("Checking expansions from highest to lowest:");
|
|
result.AppendLine("");
|
|
foreach (Expansion expansion in GetAllExpansions().Reverse())
|
|
{
|
|
IReadOnlySet<uint> expansionQuests = GetQuestsForExpansion(expansion);
|
|
List<uint> completedInExpansion = questList.Where((uint qId) => expansionQuests.Contains(qId)).ToList();
|
|
float percentage = ((expansionQuests.Count > 0) ? ((float)completedInExpansion.Count / (float)expansionQuests.Count * 100f) : 0f);
|
|
stringBuilder = result;
|
|
StringBuilder stringBuilder3 = stringBuilder;
|
|
handler = new StringBuilder.AppendInterpolatedStringHandler(4, 2, stringBuilder);
|
|
handler.AppendFormatted(GetExpansionName(expansion));
|
|
handler.AppendLiteral(" (");
|
|
handler.AppendFormatted(GetExpansionShortName(expansion));
|
|
handler.AppendLiteral("):");
|
|
stringBuilder3.AppendLine(ref handler);
|
|
stringBuilder = result;
|
|
StringBuilder stringBuilder4 = stringBuilder;
|
|
handler = new StringBuilder.AppendInterpolatedStringHandler(28, 1, stringBuilder);
|
|
handler.AppendLiteral(" - Total MSQ in expansion: ");
|
|
handler.AppendFormatted(expansionQuests.Count);
|
|
stringBuilder4.AppendLine(ref handler);
|
|
stringBuilder = result;
|
|
StringBuilder stringBuilder5 = stringBuilder;
|
|
handler = new StringBuilder.AppendInterpolatedStringHandler(32, 2, stringBuilder);
|
|
handler.AppendLiteral(" - Completed by character: ");
|
|
handler.AppendFormatted(completedInExpansion.Count);
|
|
handler.AppendLiteral(" (");
|
|
handler.AppendFormatted(percentage, "F1");
|
|
handler.AppendLiteral("%)");
|
|
stringBuilder5.AppendLine(ref handler);
|
|
if (completedInExpansion.Count > 0)
|
|
{
|
|
string samples = string.Join(", ", completedInExpansion.OrderByDescending((uint x) => x).Take(5));
|
|
stringBuilder = result;
|
|
StringBuilder stringBuilder6 = stringBuilder;
|
|
handler = new StringBuilder.AppendInterpolatedStringHandler(22, 1, stringBuilder);
|
|
handler.AppendLiteral(" - Sample Quest IDs: ");
|
|
handler.AppendFormatted(samples);
|
|
stringBuilder6.AppendLine(ref handler);
|
|
result.AppendLine(" >>> HAS COMPLETED QUESTS - WOULD SELECT THIS EXPANSION <<<");
|
|
}
|
|
else
|
|
{
|
|
result.AppendLine(" - No quests completed in this expansion");
|
|
}
|
|
result.AppendLine("");
|
|
}
|
|
Expansion currentExpansion = GetCurrentExpansion(questList);
|
|
result.AppendLine("===========================================");
|
|
stringBuilder = result;
|
|
StringBuilder stringBuilder7 = stringBuilder;
|
|
handler = new StringBuilder.AppendInterpolatedStringHandler(34, 1, stringBuilder);
|
|
handler.AppendLiteral(">>> FINAL DETECTED EXPANSION: ");
|
|
handler.AppendFormatted(GetExpansionName(currentExpansion));
|
|
handler.AppendLiteral(" <<<");
|
|
stringBuilder7.AppendLine(ref handler);
|
|
result.AppendLine("===========================================");
|
|
return result.ToString();
|
|
}
|
|
|
|
public static ExpansionProgress GetExpansionProgress(IEnumerable<uint> completedQuestIds, Expansion expansion)
|
|
{
|
|
int completed = GetCompletedQuestCountForExpansion(completedQuestIds, expansion);
|
|
int expected = GetExpectedQuestCount(expansion);
|
|
return new ExpansionProgress
|
|
{
|
|
Expansion = expansion,
|
|
CompletedCount = completed,
|
|
ExpectedCount = expected,
|
|
Percentage = ((expected > 0) ? ((float)completed / (float)expected * 100f) : 0f),
|
|
IsComplete = (completed >= expected)
|
|
};
|
|
}
|
|
|
|
public static List<ExpansionProgress> GetAllExpansionProgress(IEnumerable<uint> completedQuestIds)
|
|
{
|
|
return (from exp in GetAllExpansions()
|
|
select GetExpansionProgress(completedQuestIds, exp)).ToList();
|
|
}
|
|
}
|