510 lines
14 KiB
C#
510 lines
14 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.Linq;
|
|
using Dalamud.Game.ClientState.Objects.SubKinds;
|
|
using Dalamud.Plugin.Services;
|
|
using FFXIVClientStructs.FFXIV.Client.Game;
|
|
using Lumina.Excel;
|
|
using Lumina.Excel.Sheets;
|
|
using QuestionableCompanion.Data;
|
|
|
|
namespace QuestionableCompanion.Services;
|
|
|
|
public class MSQProgressionService
|
|
{
|
|
private readonly IDataManager dataManager;
|
|
|
|
private readonly IPluginLog log;
|
|
|
|
private readonly QuestDetectionService questDetectionService;
|
|
|
|
private readonly IObjectTable objectTable;
|
|
|
|
private readonly IFramework framework;
|
|
|
|
private List<Quest>? mainScenarioQuests;
|
|
|
|
private Dictionary<uint, string> questNameCache = new Dictionary<uint, string>();
|
|
|
|
private Dictionary<string, List<Quest>> questsByExpansion = new Dictionary<string, List<Quest>>();
|
|
|
|
private static readonly uint[] MSQ_JOURNAL_GENRE_IDS = new uint[14]
|
|
{
|
|
1u, 2u, 3u, 4u, 5u, 6u, 7u, 8u, 9u, 10u,
|
|
11u, 12u, 13u, 14u
|
|
};
|
|
|
|
private const uint LAST_ARR_QUEST_ID = 65964u;
|
|
|
|
private static readonly Dictionary<uint, MSQExpansionData.Expansion> JournalGenreToExpansion = new Dictionary<uint, MSQExpansionData.Expansion>
|
|
{
|
|
{
|
|
1u,
|
|
MSQExpansionData.Expansion.ARealmReborn
|
|
},
|
|
{
|
|
2u,
|
|
MSQExpansionData.Expansion.ARealmReborn
|
|
},
|
|
{
|
|
3u,
|
|
MSQExpansionData.Expansion.Heavensward
|
|
},
|
|
{
|
|
4u,
|
|
MSQExpansionData.Expansion.Heavensward
|
|
},
|
|
{
|
|
5u,
|
|
MSQExpansionData.Expansion.Heavensward
|
|
},
|
|
{
|
|
6u,
|
|
MSQExpansionData.Expansion.Stormblood
|
|
},
|
|
{
|
|
7u,
|
|
MSQExpansionData.Expansion.Stormblood
|
|
},
|
|
{
|
|
8u,
|
|
MSQExpansionData.Expansion.Shadowbringers
|
|
},
|
|
{
|
|
9u,
|
|
MSQExpansionData.Expansion.Shadowbringers
|
|
},
|
|
{
|
|
10u,
|
|
MSQExpansionData.Expansion.Shadowbringers
|
|
},
|
|
{
|
|
11u,
|
|
MSQExpansionData.Expansion.Endwalker
|
|
},
|
|
{
|
|
12u,
|
|
MSQExpansionData.Expansion.Endwalker
|
|
},
|
|
{
|
|
13u,
|
|
MSQExpansionData.Expansion.Dawntrail
|
|
},
|
|
{
|
|
14u,
|
|
MSQExpansionData.Expansion.Dawntrail
|
|
}
|
|
};
|
|
|
|
public MSQProgressionService(IDataManager dataManager, IPluginLog log, QuestDetectionService questDetectionService, IObjectTable objectTable, IFramework framework)
|
|
{
|
|
this.dataManager = dataManager;
|
|
this.log = log;
|
|
this.questDetectionService = questDetectionService;
|
|
this.objectTable = objectTable;
|
|
this.framework = framework;
|
|
InitializeMSQData();
|
|
framework.RunOnTick(delegate
|
|
{
|
|
DebugCurrentCharacterQuest();
|
|
}, default(TimeSpan), 60);
|
|
}
|
|
|
|
private void InitializeMSQData()
|
|
{
|
|
try
|
|
{
|
|
ExcelSheet<Quest> questSheet = dataManager.GetExcelSheet<Quest>();
|
|
if (questSheet == null)
|
|
{
|
|
return;
|
|
}
|
|
questSheet.Count();
|
|
int manualCount = 0;
|
|
foreach (Quest item in questSheet)
|
|
{
|
|
_ = item;
|
|
manualCount++;
|
|
}
|
|
List<Quest> highIdQuests = questSheet.Where((Quest q) => q.RowId > 66000).ToList();
|
|
if (highIdQuests.Count > 0)
|
|
{
|
|
highIdQuests.First();
|
|
}
|
|
foreach (IGrouping<uint, Quest> item2 in (from q in questSheet
|
|
where q.RowId != 0
|
|
group q by q.JournalGenre.RowId into g
|
|
orderby g.Key
|
|
select g).Take(10))
|
|
{
|
|
_ = item2;
|
|
}
|
|
mainScenarioQuests = (from q in questSheet
|
|
where ((ReadOnlySpan<uint>)MSQ_JOURNAL_GENRE_IDS).Contains(q.JournalGenre.RowId)
|
|
orderby q.RowId
|
|
select q).ToList();
|
|
if (mainScenarioQuests.Count == 0)
|
|
{
|
|
return;
|
|
}
|
|
foreach (Quest quest in mainScenarioQuests.Take(20))
|
|
{
|
|
try
|
|
{
|
|
quest.Expansion.Value.Name.ToString();
|
|
}
|
|
catch (Exception)
|
|
{
|
|
}
|
|
}
|
|
foreach (IGrouping<uint, Quest> group in from q in mainScenarioQuests
|
|
group q by q.JournalGenre.RowId into g
|
|
orderby g.Key
|
|
select g)
|
|
{
|
|
uint genreId = group.Key;
|
|
JournalGenreToExpansion.GetValueOrDefault(genreId, MSQExpansionData.Expansion.ARealmReborn);
|
|
group.First().JournalGenre.Value.Name.ToString();
|
|
string.Join(", ", from q in @group.Take(3)
|
|
select q.RowId);
|
|
}
|
|
foreach (IGrouping<MSQExpansionData.Expansion, Quest> item3 in from q in mainScenarioQuests
|
|
group q by JournalGenreToExpansion.GetValueOrDefault(q.JournalGenre.RowId, MSQExpansionData.Expansion.ARealmReborn) into g
|
|
orderby g.Key
|
|
select g)
|
|
{
|
|
_ = item3;
|
|
}
|
|
MSQExpansionData.ClearQuests();
|
|
foreach (Quest quest2 in mainScenarioQuests)
|
|
{
|
|
string name = quest2.Name.ToString();
|
|
if (!string.IsNullOrEmpty(name))
|
|
{
|
|
questNameCache[quest2.RowId] = name;
|
|
}
|
|
MSQExpansionData.Expansion expansion = JournalGenreToExpansion.GetValueOrDefault(quest2.JournalGenre.RowId, MSQExpansionData.Expansion.ARealmReborn);
|
|
if (quest2.JournalGenre.RowId != 2 || quest2.RowId <= 65964)
|
|
{
|
|
MSQExpansionData.RegisterQuest(quest2.RowId, expansion);
|
|
string shortName = MSQExpansionData.GetExpansionShortName(expansion);
|
|
if (!questsByExpansion.ContainsKey(shortName))
|
|
{
|
|
questsByExpansion[shortName] = new List<Quest>();
|
|
}
|
|
questsByExpansion[shortName].Add(quest2);
|
|
}
|
|
}
|
|
foreach (MSQExpansionData.Expansion allExpansion in MSQExpansionData.GetAllExpansions())
|
|
{
|
|
string shortName2 = MSQExpansionData.GetExpansionShortName(allExpansion);
|
|
List<Quest> quests = questsByExpansion.GetValueOrDefault(shortName2);
|
|
if ((quests?.Count ?? 0) > 0 && quests != null)
|
|
{
|
|
string.Join(", ", from q in quests.Take(5)
|
|
select q.RowId);
|
|
}
|
|
}
|
|
}
|
|
catch (Exception)
|
|
{
|
|
}
|
|
}
|
|
|
|
public (uint questId, string questName) GetLastCompletedMSQ(string characterName)
|
|
{
|
|
if (mainScenarioQuests == null || mainScenarioQuests.Count == 0)
|
|
{
|
|
return (questId: 0u, questName: "—");
|
|
}
|
|
try
|
|
{
|
|
List<uint> completedQuests = questDetectionService.GetAllCompletedQuestIds();
|
|
Quest lastMSQ = (from q in mainScenarioQuests
|
|
where completedQuests.Contains(q.RowId)
|
|
orderby q.RowId descending
|
|
select q).FirstOrDefault();
|
|
if (lastMSQ.RowId != 0)
|
|
{
|
|
string questName = questNameCache.GetValueOrDefault(lastMSQ.RowId, "Unknown Quest");
|
|
return (questId: lastMSQ.RowId, questName: questName);
|
|
}
|
|
}
|
|
catch (Exception)
|
|
{
|
|
}
|
|
return (questId: 0u, questName: "—");
|
|
}
|
|
|
|
public float GetMSQCompletionPercentage()
|
|
{
|
|
if (mainScenarioQuests == null || mainScenarioQuests.Count == 0)
|
|
{
|
|
return 0f;
|
|
}
|
|
try
|
|
{
|
|
List<uint> completedQuests = questDetectionService.GetAllCompletedQuestIds();
|
|
return (float)mainScenarioQuests.Count((Quest q) => completedQuests.Contains(q.RowId)) / (float)mainScenarioQuests.Count * 100f;
|
|
}
|
|
catch (Exception)
|
|
{
|
|
return 0f;
|
|
}
|
|
}
|
|
|
|
public int GetTotalMSQCount()
|
|
{
|
|
return mainScenarioQuests?.Count ?? 0;
|
|
}
|
|
|
|
public int GetCompletedMSQCount()
|
|
{
|
|
if (mainScenarioQuests == null || mainScenarioQuests.Count == 0)
|
|
{
|
|
return 0;
|
|
}
|
|
try
|
|
{
|
|
List<uint> completedQuests = questDetectionService.GetAllCompletedQuestIds();
|
|
return mainScenarioQuests.Count((Quest q) => completedQuests.Contains(q.RowId));
|
|
}
|
|
catch (Exception)
|
|
{
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
public string GetQuestName(uint questId)
|
|
{
|
|
return questNameCache.GetValueOrDefault(questId, "Unknown Quest");
|
|
}
|
|
|
|
public bool IsMSQ(uint questId)
|
|
{
|
|
return mainScenarioQuests?.Any((Quest q) => q.RowId == questId) ?? false;
|
|
}
|
|
|
|
public ExpansionInfo? GetExpansionForQuest(uint questId)
|
|
{
|
|
MSQExpansionData.Expansion expansion = MSQExpansionData.GetExpansionForQuest(questId);
|
|
return new ExpansionInfo
|
|
{
|
|
Name = MSQExpansionData.GetExpansionName(expansion),
|
|
ShortName = MSQExpansionData.GetExpansionShortName(expansion),
|
|
MinQuestId = 0u,
|
|
MaxQuestId = 0u,
|
|
ExpectedQuestCount = MSQExpansionData.GetExpectedQuestCount(expansion)
|
|
};
|
|
}
|
|
|
|
public List<ExpansionInfo> GetExpansions()
|
|
{
|
|
return (from exp in MSQExpansionData.GetAllExpansions()
|
|
select new ExpansionInfo
|
|
{
|
|
Name = MSQExpansionData.GetExpansionName(exp),
|
|
ShortName = MSQExpansionData.GetExpansionShortName(exp),
|
|
MinQuestId = 0u,
|
|
MaxQuestId = 0u,
|
|
ExpectedQuestCount = MSQExpansionData.GetExpectedQuestCount(exp)
|
|
}).ToList();
|
|
}
|
|
|
|
public (int completed, int total) GetExpansionProgress(string expansionShortName)
|
|
{
|
|
List<uint> completedQuests = questDetectionService.GetAllCompletedQuestIds();
|
|
List<Quest>? obj = questsByExpansion.GetValueOrDefault(expansionShortName) ?? new List<Quest>();
|
|
int completed = obj.Count((Quest q) => completedQuests.Contains(q.RowId));
|
|
int total = obj.Count;
|
|
return (completed: completed, total: total);
|
|
}
|
|
|
|
public ExpansionInfo? GetCurrentExpansion()
|
|
{
|
|
try
|
|
{
|
|
List<uint> completedQuests = questDetectionService.GetAllCompletedQuestIds();
|
|
(MSQExpansionData.Expansion expansion, string debugInfo) currentExpansionFromGameWithDebug = MSQExpansionData.GetCurrentExpansionFromGameWithDebug();
|
|
MSQExpansionData.Expansion gameExpansion = currentExpansionFromGameWithDebug.expansion;
|
|
string[] array = currentExpansionFromGameWithDebug.debugInfo.Split('\n');
|
|
for (int i = 0; i < array.Length; i++)
|
|
{
|
|
string.IsNullOrWhiteSpace(array[i]);
|
|
}
|
|
MSQExpansionData.Expansion analysisExpansion = MSQExpansionData.GetCurrentExpansion(completedQuests);
|
|
array = MSQExpansionData.GetExpansionDetectionDebugInfo(completedQuests).Split('\n');
|
|
for (int i = 0; i < array.Length; i++)
|
|
{
|
|
string.IsNullOrWhiteSpace(array[i]);
|
|
}
|
|
MSQExpansionData.Expansion finalExpansion = gameExpansion;
|
|
if (gameExpansion == MSQExpansionData.Expansion.ARealmReborn && analysisExpansion != MSQExpansionData.Expansion.ARealmReborn)
|
|
{
|
|
finalExpansion = analysisExpansion;
|
|
}
|
|
return new ExpansionInfo
|
|
{
|
|
Name = MSQExpansionData.GetExpansionName(finalExpansion),
|
|
ShortName = MSQExpansionData.GetExpansionShortName(finalExpansion),
|
|
MinQuestId = 0u,
|
|
MaxQuestId = 0u,
|
|
ExpectedQuestCount = MSQExpansionData.GetExpectedQuestCount(finalExpansion)
|
|
};
|
|
}
|
|
catch (Exception)
|
|
{
|
|
return GetExpansions().FirstOrDefault();
|
|
}
|
|
}
|
|
|
|
public Dictionary<string, (int completed, int total)> GetExpansionProgressForCharacter(List<uint> completedQuestIds)
|
|
{
|
|
Dictionary<string, (int, int)> result = new Dictionary<string, (int, int)>();
|
|
foreach (ExpansionInfo exp in GetExpansions())
|
|
{
|
|
List<Quest> expansionQuests = questsByExpansion.GetValueOrDefault(exp.ShortName) ?? new List<Quest>();
|
|
int completed = expansionQuests.Count((Quest q) => completedQuestIds.Contains(q.RowId));
|
|
result[exp.ShortName] = (completed, expansionQuests.Count);
|
|
}
|
|
return result;
|
|
}
|
|
|
|
public List<Quest> GetAllMSQQuests()
|
|
{
|
|
return mainScenarioQuests ?? new List<Quest>();
|
|
}
|
|
|
|
public Dictionary<string, ExpansionProgressInfo> GetExpansionProgress()
|
|
{
|
|
Dictionary<string, ExpansionProgressInfo> result = new Dictionary<string, ExpansionProgressInfo>();
|
|
List<uint> completedQuests = questDetectionService.GetAllCompletedQuestIds();
|
|
foreach (MSQExpansionData.Expansion expansion in MSQExpansionData.GetAllExpansions())
|
|
{
|
|
ExpansionProgress progress = MSQExpansionData.GetExpansionProgress(completedQuests, expansion);
|
|
result[progress.ExpansionName] = new ExpansionProgressInfo
|
|
{
|
|
ExpansionName = progress.ExpansionName,
|
|
ShortName = progress.ExpansionShortName,
|
|
TotalQuests = progress.ExpectedCount,
|
|
CompletedQuests = progress.CompletedCount,
|
|
Percentage = progress.Percentage
|
|
};
|
|
}
|
|
return result;
|
|
}
|
|
|
|
public Dictionary<string, ExpansionProgressInfo> GetExpansionProgressForCharacter(List<string> completedQuestIds)
|
|
{
|
|
Dictionary<string, ExpansionProgressInfo> result = new Dictionary<string, ExpansionProgressInfo>();
|
|
uint result2;
|
|
List<uint> completedQuestIdsUint = (from id in completedQuestIds
|
|
select uint.TryParse(id, out result2) ? result2 : 0u into id
|
|
where id != 0
|
|
select id).ToList();
|
|
foreach (MSQExpansionData.Expansion expansion in MSQExpansionData.GetAllExpansions())
|
|
{
|
|
ExpansionProgress progress = MSQExpansionData.GetExpansionProgress(completedQuestIdsUint, expansion);
|
|
result[progress.ExpansionName] = new ExpansionProgressInfo
|
|
{
|
|
ExpansionName = progress.ExpansionName,
|
|
ShortName = progress.ExpansionShortName,
|
|
TotalQuests = progress.ExpectedCount,
|
|
CompletedQuests = progress.CompletedCount,
|
|
Percentage = progress.Percentage
|
|
};
|
|
}
|
|
return result;
|
|
}
|
|
|
|
public ExpansionInfo? GetCurrentExpansion(uint lastCompletedQuestId)
|
|
{
|
|
MSQExpansionData.Expansion expansion = MSQExpansionData.GetExpansionForQuest(lastCompletedQuestId);
|
|
return new ExpansionInfo
|
|
{
|
|
Name = MSQExpansionData.GetExpansionName(expansion),
|
|
ShortName = MSQExpansionData.GetExpansionShortName(expansion),
|
|
MinQuestId = 0u,
|
|
MaxQuestId = 0u,
|
|
ExpectedQuestCount = MSQExpansionData.GetExpectedQuestCount(expansion)
|
|
};
|
|
}
|
|
|
|
private MSQExpansionData.Expansion ConvertLuminaExpansionToOurs(uint luminaExpansionId)
|
|
{
|
|
return luminaExpansionId switch
|
|
{
|
|
0u => MSQExpansionData.Expansion.ARealmReborn,
|
|
1u => MSQExpansionData.Expansion.Heavensward,
|
|
2u => MSQExpansionData.Expansion.Stormblood,
|
|
3u => MSQExpansionData.Expansion.Shadowbringers,
|
|
4u => MSQExpansionData.Expansion.Endwalker,
|
|
5u => MSQExpansionData.Expansion.Dawntrail,
|
|
_ => MSQExpansionData.Expansion.ARealmReborn,
|
|
};
|
|
}
|
|
|
|
public void DebugCurrentCharacterQuest()
|
|
{
|
|
try
|
|
{
|
|
IPlayerCharacter player = objectTable.LocalPlayer;
|
|
if (player == null)
|
|
{
|
|
framework.RunOnTick(delegate
|
|
{
|
|
DebugCurrentCharacterQuest();
|
|
}, default(TimeSpan), 60);
|
|
return;
|
|
}
|
|
_ = player.Name.TextValue;
|
|
player.HomeWorld.Value.Name.ToString();
|
|
ExcelSheet<Quest> questSheet = dataManager.GetExcelSheet<Quest>();
|
|
if (questSheet == null)
|
|
{
|
|
return;
|
|
}
|
|
List<Quest> completedMSQQuests = new List<Quest>();
|
|
foreach (Quest quest in questSheet)
|
|
{
|
|
if (((ReadOnlySpan<uint>)MSQ_JOURNAL_GENRE_IDS).Contains(quest.JournalGenre.RowId) && QuestManager.IsQuestComplete((ushort)quest.RowId))
|
|
{
|
|
completedMSQQuests.Add(quest);
|
|
}
|
|
}
|
|
if (completedMSQQuests.Count == 0)
|
|
{
|
|
return;
|
|
}
|
|
Quest questData = completedMSQQuests.OrderByDescending((Quest q) => q.RowId).First();
|
|
try
|
|
{
|
|
questData.JournalGenre.Value.Name.ToString();
|
|
}
|
|
catch
|
|
{
|
|
}
|
|
try
|
|
{
|
|
questData.Expansion.Value.Name.ToString();
|
|
}
|
|
catch
|
|
{
|
|
}
|
|
foreach (Quest item in completedMSQQuests.OrderByDescending((Quest q) => q.RowId).Take(10).ToList())
|
|
{
|
|
_ = item;
|
|
}
|
|
foreach (IGrouping<MSQExpansionData.Expansion, Quest> item2 in from q in completedMSQQuests
|
|
group q by JournalGenreToExpansion.GetValueOrDefault(q.JournalGenre.RowId, MSQExpansionData.Expansion.ARealmReborn) into g
|
|
orderby g.Key
|
|
select g)
|
|
{
|
|
_ = item2;
|
|
}
|
|
}
|
|
catch (Exception)
|
|
{
|
|
}
|
|
}
|
|
}
|