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; }
+}