diff --git a/Influx/Influx.csproj b/Influx/Influx.csproj index 455bd8e..ee08837 100644 --- a/Influx/Influx.csproj +++ b/Influx/Influx.csproj @@ -22,8 +22,8 @@ - - + + @@ -58,10 +58,10 @@ - + - + diff --git a/Influx/Influx/InfluxStatisticsClient.cs b/Influx/Influx/InfluxStatisticsClient.cs index 8960b35..e5a2ea6 100644 --- a/Influx/Influx/InfluxStatisticsClient.cs +++ b/Influx/Influx/InfluxStatisticsClient.cs @@ -102,8 +102,19 @@ internal class InfluxStatisticsClient : IDisposable 11 => 90_000, _ => 0, }) - .Field("squadron_unlocked", localStats?.SquadronUnlocked == true ? 1 : 0) + .Field("squadron_unlocked", localStats.SquadronUnlocked == true ? 1 : 0) .Timestamp(date, WritePrecision.S)); + + if (localStats.MsqCount != -1) + { + values.Add(PointData.Measurement("quests") + .Tag("id", character.CharacterId.ToString()) + .Tag("player_name", character.Name) + .Tag("msq_name", localStats.MsqName) + .Field("msq_count", localStats.MsqCount) + .Field("msq_genre", localStats.MsqGenre) + .Timestamp(date, WritePrecision.S)); + } } } else if (character.CharacterType == CharacterType.Retainer) diff --git a/Influx/InfluxPlugin.cs b/Influx/InfluxPlugin.cs index 12a45cd..3c0110d 100644 --- a/Influx/InfluxPlugin.cs +++ b/Influx/InfluxPlugin.cs @@ -47,7 +47,7 @@ public class InfluxPlugin : IDalamudPlugin _commandManager = commandManager; _allaganToolsIpc = new AllaganToolsIpc(pluginInterface, chatGui, _configuration); _submarineTrackerIpc = new SubmarineTrackerIpc(chatGui); - _localStatsCalculator = new LocalStatsCalculator(pluginInterface, clientState, chatGui); + _localStatsCalculator = new LocalStatsCalculator(pluginInterface, clientState, chatGui, dataManager); _influxStatisticsClient = new InfluxStatisticsClient(chatGui, _configuration, dataManager); _windowSystem = new WindowSystem(typeof(InfluxPlugin).FullName); diff --git a/Influx/LocalStatistics/LocalStats.cs b/Influx/LocalStatistics/LocalStats.cs index 5a8da88..40f08d1 100644 --- a/Influx/LocalStatistics/LocalStats.cs +++ b/Influx/LocalStatistics/LocalStats.cs @@ -10,4 +10,8 @@ public record LocalStats public bool SquadronUnlocked { get; init; } public byte MaxLevel { get; init; } = 90; public List ClassJobLevels { get; set; } = new(); + public byte StartingTown { get; init; } + public int MsqCount { get; set; } = -1; + public string MsqName { get; set; } + public uint MsqGenre { get; set; } } diff --git a/Influx/LocalStatistics/LocalStatsCalculator.cs b/Influx/LocalStatistics/LocalStatsCalculator.cs index f36c895..2f1c891 100644 --- a/Influx/LocalStatistics/LocalStatsCalculator.cs +++ b/Influx/LocalStatistics/LocalStatsCalculator.cs @@ -1,29 +1,50 @@ using System; using System.Collections.Generic; using System.IO; +using System.Linq; +using System.Threading.Tasks; using Dalamud.Data; using Dalamud.Game.ClientState; using Dalamud.Game.Gui; using Dalamud.Logging; using Dalamud.Plugin; +using ECommons.Schedulers; using FFXIVClientStructs.FFXIV.Client.Game; using FFXIVClientStructs.FFXIV.Client.Game.UI; -using FFXIVClientStructs.FFXIV.Client.UI.Agent; +using Lumina.Excel.GeneratedSheets; using Newtonsoft.Json; +using GrandCompany = FFXIVClientStructs.FFXIV.Client.UI.Agent.GrandCompany; namespace Influx.LocalStatistics; public class LocalStatsCalculator : IDisposable { + private const uint ComingToGridania = 65575; + private const uint ComingToLimsa = 65643; + private const uint ComingToUldah = 66130; + + private const uint EnvoyGridania = 66043; + private const uint EnvoyLimsa = 66082; + private const uint EnvoyUldah = 66064; + + private const uint JointQuest = 65781; + private readonly DalamudPluginInterface _pluginInterface; private readonly ClientState _clientState; private readonly ChatGui _chatGui; private readonly Dictionary _cache = new(); + private IReadOnlyList? _gridaniaStart; + private IReadOnlyList? _limsaStart; + private IReadOnlyList? _uldahStart; + private IReadOnlyList? _msqQuests; + + public LocalStatsCalculator( DalamudPluginInterface pluginInterface, ClientState clientState, - ChatGui chatGui) + ChatGui chatGui, + DataManager dataManager) { _pluginInterface = pluginInterface; _clientState = clientState; @@ -33,6 +54,41 @@ public class LocalStatsCalculator : IDisposable _clientState.Logout += UpdateStatistics; _clientState.TerritoryChanged += UpdateStatistics; + Task.Run(() => + { + List msq = new(); + foreach (var quest in dataManager.GetExcelSheet()!.Where(x => x.JournalGenre.Row is >= 1 and <= 12)) + { + var previousQuests = quest.PreviousQuest?.Select(x => x.Row).Where(x => x != 0).ToList(); + msq.Add(new QuestInfo + { + RowId = quest.RowId, + Name = quest.Name.ToString(), + PreviousQuestIds = previousQuests ?? new(), + Genre = quest.JournalGenre.Row, + }); + } + + _gridaniaStart = PopulateStartingCities(msq, EnvoyGridania, ComingToGridania, false); + _limsaStart = PopulateStartingCities(msq, EnvoyLimsa, ComingToLimsa, true); + _uldahStart = PopulateStartingCities(msq, EnvoyUldah, ComingToUldah, true); + + List sortedQuests = new(); + sortedQuests.Add(msq.First(x => x.RowId == JointQuest)); + msq.Remove(sortedQuests[0]); + + QuestInfo? qq = msq.FirstOrDefault(); + while ((qq = msq.FirstOrDefault(quest => quest.PreviousQuestIds.Count == 0 || + quest.PreviousQuestIds.All( + x => sortedQuests.Any(y => x == y.RowId)))) != null) + { + sortedQuests.Add(qq); + msq.Remove(qq); + } + + _msqQuests = sortedQuests.AsReadOnly(); + }); + foreach (var file in _pluginInterface.ConfigDirectory.GetFiles("l.*.json")) { try @@ -53,6 +109,38 @@ public class LocalStatsCalculator : IDisposable UpdateStatistics(); } + private IReadOnlyList PopulateStartingCities(List quests, uint envoyQuestId, uint startingQuestId, bool popCallOfTheSea) + { + QuestInfo callOfTheSea = quests.First(x => x.PreviousQuestIds.Contains(envoyQuestId)); + if (popCallOfTheSea) + quests.Remove(callOfTheSea); + + List startingCityQuests = new List{ callOfTheSea }; + uint? questId = envoyQuestId; + QuestInfo? quest; + + do + { + quest = quests.First(x => x.RowId == questId); + quests.Remove(quest); + + if (quest.Name == "Close to Home") + { + quest = new QuestInfo + { + RowId = startingQuestId, + Name = "Coming to ...", + PreviousQuestIds = new(), + }; + } + + startingCityQuests.Add(quest); + questId = quest.PreviousQuestIds.FirstOrDefault(); + } while (questId != null && questId != 0); + + return Enumerable.Reverse(startingCityQuests).ToList().AsReadOnly(); + } + public void Dispose() { _clientState.Login -= UpdateStatistics; @@ -93,8 +181,43 @@ public class LocalStatsCalculator : IDisposable }, MaxLevel = playerState->MaxLevel, ClassJobLevels = ExtractClassJobLevels(playerState), + StartingTown = playerState->StartTown, }; + if (_msqQuests != null) + { + if (QuestManager.IsQuestComplete(JointQuest)) + { + var quests = _msqQuests.Where(x => QuestManager.IsQuestComplete(x.RowId)).ToList(); + localStats.MsqCount = 24 + quests.Count; + localStats.MsqName = quests.Last().Name; + localStats.MsqGenre = quests.Last().Genre; + } + else + { + PluginLog.Information($"XX → {playerState->StartTown}"); + IReadOnlyList cityQuests = playerState->StartTown switch + { + 1 => _limsaStart!, + 2 => _gridaniaStart!, + 3 => _uldahStart!, + _ => new List(), + }; + var quests = cityQuests.Where(x => QuestManager.IsQuestComplete(x.RowId)).ToList(); + localStats.MsqCount = quests.Count; + localStats.MsqName = quests.LastOrDefault()?.Name ?? string.Empty; + localStats.MsqGenre = quests.LastOrDefault()?.Genre ?? 0; + } + } + else + { + localStats.MsqCount = -1; + localStats.MsqName = string.Empty; + localStats.MsqGenre = 0; + } + + PluginLog.Information($"ls → {localStats.MsqCount}, {localStats.MsqName}"); + if (_cache.TryGetValue(localContentId, out var existingStats)) { if (existingStats != localStats) diff --git a/Influx/LocalStatistics/QuestInfo.cs b/Influx/LocalStatistics/QuestInfo.cs new file mode 100644 index 0000000..7687bbe --- /dev/null +++ b/Influx/LocalStatistics/QuestInfo.cs @@ -0,0 +1,11 @@ +using System.Collections.Generic; + +namespace Influx.LocalStatistics; + +public class QuestInfo +{ + public uint RowId { get; set; } + public string Name { get; set; } + public List PreviousQuestIds { get; set; } + public uint Genre { get; set; } +}