qstcompanion v1.0.5

This commit is contained in:
alydev 2025-12-04 04:40:50 +10:00
parent 52daefcfd7
commit 5e1e1decc5
9 changed files with 304 additions and 200 deletions

View file

@ -202,7 +202,7 @@ public class ChauffeurModeService : IDisposable
return; return;
} }
} }
if (clientState.LocalPlayer == null) if (objectTable.LocalPlayer == null)
{ {
return; return;
} }
@ -266,7 +266,7 @@ public class ChauffeurModeService : IDisposable
{ {
return; return;
} }
IPlayerCharacter localPlayer = clientState.LocalPlayer; IPlayerCharacter localPlayer = objectTable.LocalPlayer;
if (localPlayer == null) if (localPlayer == null)
{ {
return; return;
@ -574,7 +574,7 @@ public class ChauffeurModeService : IDisposable
private async void SummonHelper(Vector3 targetPos, uint zoneId) private async void SummonHelper(Vector3 targetPos, uint zoneId)
{ {
IPlayerCharacter localPlayer = clientState.LocalPlayer; IPlayerCharacter localPlayer = objectTable.LocalPlayer;
if (localPlayer == null) if (localPlayer == null)
{ {
return; return;
@ -789,7 +789,7 @@ public class ChauffeurModeService : IDisposable
this.questerName = $"{questerName}@{questerWorld}"; this.questerName = $"{questerName}@{questerWorld}";
targetZoneId = zoneId; targetZoneId = zoneId;
targetPosition = targetPos; targetPosition = targetPos;
IPlayerCharacter localPlayer = clientState.LocalPlayer; IPlayerCharacter localPlayer = objectTable.LocalPlayer;
if (localPlayer != null) if (localPlayer != null)
{ {
string myName = localPlayer.Name.ToString(); string myName = localPlayer.Name.ToString();
@ -881,7 +881,7 @@ public class ChauffeurModeService : IDisposable
{ {
try try
{ {
IPlayerCharacter localPlayer2 = clientState.LocalPlayer; IPlayerCharacter localPlayer2 = objectTable.LocalPlayer;
if (localPlayer2 == null) if (localPlayer2 == null)
{ {
log.Error("[ChauffeurMode] [WORKFLOW] LocalPlayer is null!"); log.Error("[ChauffeurMode] [WORKFLOW] LocalPlayer is null!");
@ -1180,7 +1180,7 @@ public class ChauffeurModeService : IDisposable
{ {
try try
{ {
IPlayerCharacter localPlayer2 = clientState.LocalPlayer; IPlayerCharacter localPlayer2 = objectTable.LocalPlayer;
if (localPlayer2 != null) if (localPlayer2 != null)
{ {
float result = Vector3.Distance(localPlayer2.Position, questerPos); float result = Vector3.Distance(localPlayer2.Position, questerPos);
@ -1271,7 +1271,7 @@ public class ChauffeurModeService : IDisposable
log.Information("[ChauffeurMode] [HELPER] ========================================"); log.Information("[ChauffeurMode] [HELPER] ========================================");
log.Information($"[ChauffeurMode] [HELPER] Sending mount ready signal to: {questerName}@{questerWorld}"); log.Information($"[ChauffeurMode] [HELPER] Sending mount ready signal to: {questerName}@{questerWorld}");
log.Information($"[ChauffeurMode] [HELPER] Helper is mounted: {IsMounted()}"); log.Information($"[ChauffeurMode] [HELPER] Helper is mounted: {IsMounted()}");
log.Information($"[ChauffeurMode] [HELPER] Helper position: ({clientState.LocalPlayer?.Position.X:F2}, {clientState.LocalPlayer?.Position.Y:F2}, {clientState.LocalPlayer?.Position.Z:F2})"); log.Information($"[ChauffeurMode] [HELPER] Helper position: ({objectTable.LocalPlayer?.Position.X:F2}, {objectTable.LocalPlayer?.Position.Y:F2}, {objectTable.LocalPlayer?.Position.Z:F2})");
crossProcessIPC.SendChauffeurMountReady(questerName, questerWorld); crossProcessIPC.SendChauffeurMountReady(questerName, questerWorld);
log.Information("[ChauffeurMode] [HELPER] Mount ready signal sent via IPC"); log.Information("[ChauffeurMode] [HELPER] Mount ready signal sent via IPC");
log.Information("[ChauffeurMode] [WORKFLOW] Waiting 8 seconds for quester to mount..."); log.Information("[ChauffeurMode] [WORKFLOW] Waiting 8 seconds for quester to mount...");
@ -1323,7 +1323,7 @@ public class ChauffeurModeService : IDisposable
config.AssignedQuester = ""; config.AssignedQuester = "";
config.CurrentHelperStatus = HelperStatus.Available; config.CurrentHelperStatus = HelperStatus.Available;
config.Save(); config.Save();
IPlayerCharacter localPlayer = clientState.LocalPlayer; IPlayerCharacter localPlayer = objectTable.LocalPlayer;
if (localPlayer != null) if (localPlayer != null)
{ {
string helperName = localPlayer.Name.ToString(); string helperName = localPlayer.Name.ToString();
@ -1363,7 +1363,7 @@ public class ChauffeurModeService : IDisposable
if (isAttuneAetheryteTask && targetPosition.HasValue) if (isAttuneAetheryteTask && targetPosition.HasValue)
{ {
log.Information("[ChauffeurMode] [HELPER] AttuneAetheryte detected - flying 10 yalms away from target before dismount"); log.Information("[ChauffeurMode] [HELPER] AttuneAetheryte detected - flying 10 yalms away from target before dismount");
Vector3 direction = Vector3.Normalize(await framework.RunOnFrameworkThread(() => clientState.LocalPlayer?.Position ?? Vector3.Zero) - targetPosition.Value); Vector3 direction = Vector3.Normalize(await framework.RunOnFrameworkThread(() => objectTable.LocalPlayer?.Position ?? Vector3.Zero) - targetPosition.Value);
Vector3 flyAwayPosition = targetPosition.Value + direction * 10f; Vector3 flyAwayPosition = targetPosition.Value + direction * 10f;
log.Information($"[ChauffeurMode] [HELPER] Flying to position 10 yalms away: ({flyAwayPosition.X:F2}, {flyAwayPosition.Y:F2}, {flyAwayPosition.Z:F2})"); log.Information($"[ChauffeurMode] [HELPER] Flying to position 10 yalms away: ({flyAwayPosition.X:F2}, {flyAwayPosition.Y:F2}, {flyAwayPosition.Z:F2})");
await framework.RunOnFrameworkThread(delegate await framework.RunOnFrameworkThread(delegate
@ -1382,7 +1382,7 @@ public class ChauffeurModeService : IDisposable
DateTime timeout = DateTime.Now.AddSeconds(10.0); DateTime timeout = DateTime.Now.AddSeconds(10.0);
while (DateTime.Now < timeout) while (DateTime.Now < timeout)
{ {
float distanceToTarget = Vector3.Distance(await framework.RunOnFrameworkThread(() => clientState.LocalPlayer?.Position ?? Vector3.Zero), targetPosition.Value); float distanceToTarget = Vector3.Distance(await framework.RunOnFrameworkThread(() => objectTable.LocalPlayer?.Position ?? Vector3.Zero), targetPosition.Value);
if (distanceToTarget >= 10f) if (distanceToTarget >= 10f)
{ {
log.Information($"[ChauffeurMode] [HELPER] Successfully flew away (distance: {distanceToTarget:F2} yalms)"); log.Information($"[ChauffeurMode] [HELPER] Successfully flew away (distance: {distanceToTarget:F2} yalms)");
@ -1535,7 +1535,7 @@ public class ChauffeurModeService : IDisposable
{ {
try try
{ {
if (clientState.LocalPlayer == null) if (objectTable.LocalPlayer == null)
{ {
return false; return false;
} }
@ -1621,7 +1621,7 @@ public class ChauffeurModeService : IDisposable
{ {
try try
{ {
IPlayerCharacter localPlayer = clientState.LocalPlayer; IPlayerCharacter localPlayer = objectTable.LocalPlayer;
posTask.SetResult(localPlayer?.Position); posTask.SetResult(localPlayer?.Position);
} }
catch (Exception ex2) catch (Exception ex2)
@ -1737,7 +1737,7 @@ public class ChauffeurModeService : IDisposable
{ {
try try
{ {
Vector3? result = clientState.LocalPlayer?.Position; Vector3? result = objectTable.LocalPlayer?.Position;
helperStartPosTask.SetResult(result); helperStartPosTask.SetResult(result);
} }
catch catch
@ -1759,7 +1759,7 @@ public class ChauffeurModeService : IDisposable
{ {
try try
{ {
Vector3? result = clientState.LocalPlayer?.Position; Vector3? result = objectTable.LocalPlayer?.Position;
helperCurrentPosTask.SetResult(result); helperCurrentPosTask.SetResult(result);
} }
catch catch
@ -1868,7 +1868,7 @@ public class ChauffeurModeService : IDisposable
{ {
try try
{ {
Vector3? result = clientState.LocalPlayer?.Position; Vector3? result = objectTable.LocalPlayer?.Position;
posTask.SetResult(result); posTask.SetResult(result);
} }
catch catch
@ -1965,7 +1965,7 @@ public class ChauffeurModeService : IDisposable
log.Debug("[ChauffeurMode] [QUESTER] RidePillion already executed this session, ignoring"); log.Debug("[ChauffeurMode] [QUESTER] RidePillion already executed this session, ignoring");
return; return;
} }
IPlayerCharacter localPlayer = clientState.LocalPlayer; IPlayerCharacter localPlayer = objectTable.LocalPlayer;
if (localPlayer == null) if (localPlayer == null)
{ {
return; return;
@ -2046,7 +2046,7 @@ public class ChauffeurModeService : IDisposable
{ {
try try
{ {
IPlayerCharacter localPlayer2 = clientState.LocalPlayer; IPlayerCharacter localPlayer2 = objectTable.LocalPlayer;
if (localPlayer2 == null) if (localPlayer2 == null)
{ {
log.Error("[ChauffeurMode] [QUESTER] Local player is null"); log.Error("[ChauffeurMode] [QUESTER] Local player is null");
@ -2270,7 +2270,7 @@ public class ChauffeurModeService : IDisposable
} }
return; return;
} }
IPlayerCharacter localPlayer = clientState.LocalPlayer; IPlayerCharacter localPlayer = objectTable.LocalPlayer;
if (localPlayer != null) if (localPlayer != null)
{ {
try try
@ -2312,7 +2312,7 @@ public class ChauffeurModeService : IDisposable
{ {
return; return;
} }
IPlayerCharacter localPlayer = clientState.LocalPlayer; IPlayerCharacter localPlayer = objectTable.LocalPlayer;
if (localPlayer == null) if (localPlayer == null)
{ {
return; return;
@ -2428,7 +2428,7 @@ public class ChauffeurModeService : IDisposable
if (config.IsQuester && !((DateTime.Now - lastZoneUpdate).TotalSeconds < 5.0)) if (config.IsQuester && !((DateTime.Now - lastZoneUpdate).TotalSeconds < 5.0))
{ {
lastZoneUpdate = DateTime.Now; lastZoneUpdate = DateTime.Now;
IPlayerCharacter localPlayer = clientState.LocalPlayer; IPlayerCharacter localPlayer = objectTable.LocalPlayer;
if (localPlayer != null) if (localPlayer != null)
{ {
string zoneName = GetZoneName(territoryId); string zoneName = GetZoneName(territoryId);
@ -2493,7 +2493,7 @@ public class ChauffeurModeService : IDisposable
config.AssignedQuester = ""; config.AssignedQuester = "";
config.CurrentHelperStatus = HelperStatus.Available; config.CurrentHelperStatus = HelperStatus.Available;
config.Save(); config.Save();
IPlayerCharacter localPlayer = clientState.LocalPlayer; IPlayerCharacter localPlayer = objectTable.LocalPlayer;
if (localPlayer != null) if (localPlayer != null)
{ {
string helperName = localPlayer.Name.ToString(); string helperName = localPlayer.Name.ToString();
@ -2533,7 +2533,7 @@ public class ChauffeurModeService : IDisposable
config.CurrentHelperStatus = HelperStatus.Available; config.CurrentHelperStatus = HelperStatus.Available;
config.Save(); config.Save();
log.Information("[ChauffeurMode] [HELPER] Status: Available"); log.Information("[ChauffeurMode] [HELPER] Status: Available");
IPlayerCharacter localPlayer = clientState.LocalPlayer; IPlayerCharacter localPlayer = objectTable.LocalPlayer;
if (localPlayer != null) if (localPlayer != null)
{ {
string helperName = localPlayer.Name.ToString(); string helperName = localPlayer.Name.ToString();
@ -2614,7 +2614,7 @@ public class ChauffeurModeService : IDisposable
return; return;
} }
lastFollowCheck = now; lastFollowCheck = now;
IPlayerCharacter localPlayer = clientState.LocalPlayer; IPlayerCharacter localPlayer = objectTable.LocalPlayer;
if (localPlayer == null) if (localPlayer == null)
{ {
return; return;
@ -2746,7 +2746,7 @@ public class ChauffeurModeService : IDisposable
log.Information($"[ChauffeurMode] [HelperFollowing] Territory Load State: Zone load complete ({timeSinceZoneChange:F1}s) - resuming position broadcasts"); log.Information($"[ChauffeurMode] [HelperFollowing] Territory Load State: Zone load complete ({timeSinceZoneChange:F1}s) - resuming position broadcasts");
lastZoneChangeTime = null; lastZoneChangeTime = null;
} }
IPlayerCharacter localPlayer = clientState.LocalPlayer; IPlayerCharacter localPlayer = objectTable.LocalPlayer;
if (localPlayer != null) if (localPlayer != null)
{ {
string questerName = localPlayer.Name.ToString(); string questerName = localPlayer.Name.ToString();
@ -2791,7 +2791,7 @@ public class ChauffeurModeService : IDisposable
{ {
return null; return null;
} }
IPlayerCharacter localPlayer = clientState.LocalPlayer; IPlayerCharacter localPlayer = objectTable.LocalPlayer;
if (localPlayer == null) if (localPlayer == null)
{ {
return null; return null;

View file

@ -267,7 +267,6 @@ public class AutoRetainerIPC : IDisposable
{ {
resolvedName = name2 + "@" + world2; resolvedName = name2 + "@" + world2;
characterCache[cid] = resolvedName; characterCache[cid] = resolvedName;
log.Debug($"[AutoRetainerIPC] CID {cid} resolved to {resolvedName} (via properties)");
return resolvedName; return resolvedName;
} }
} }
@ -277,7 +276,6 @@ public class AutoRetainerIPC : IDisposable
if (!string.IsNullOrEmpty(resolvedName)) if (!string.IsNullOrEmpty(resolvedName))
{ {
characterCache[cid] = resolvedName; characterCache[cid] = resolvedName;
log.Debug($"[AutoRetainerIPC] CID {cid} resolved to {resolvedName} (via JSON)");
return resolvedName; return resolvedName;
} }
} }

View file

@ -55,8 +55,6 @@ public class DungeonAutomationService : IDisposable
private bool isAutomationActive; private bool isAutomationActive;
private int originalDutyMode;
private bool hasSentAtY; private bool hasSentAtY;
public bool IsWaitingForParty => isWaitingForParty; public bool IsWaitingForParty => isWaitingForParty;

View file

@ -18,7 +18,7 @@ public class MSQProgressionService
private readonly QuestDetectionService questDetectionService; private readonly QuestDetectionService questDetectionService;
private readonly IClientState clientState; private readonly IObjectTable objectTable;
private readonly IFramework framework; private readonly IFramework framework;
@ -96,12 +96,12 @@ public class MSQProgressionService
} }
}; };
public MSQProgressionService(IDataManager dataManager, IPluginLog log, QuestDetectionService questDetectionService, IClientState clientState, IFramework framework) public MSQProgressionService(IDataManager dataManager, IPluginLog log, QuestDetectionService questDetectionService, IObjectTable objectTable, IFramework framework)
{ {
this.dataManager = dataManager; this.dataManager = dataManager;
this.log = log; this.log = log;
this.questDetectionService = questDetectionService; this.questDetectionService = questDetectionService;
this.clientState = clientState; this.objectTable = objectTable;
this.framework = framework; this.framework = framework;
InitializeMSQData(); InitializeMSQData();
framework.RunOnTick(delegate framework.RunOnTick(delegate
@ -114,95 +114,68 @@ public class MSQProgressionService
{ {
try try
{ {
log.Information("[MSQProgression] === INITIALIZING MSQ DATA ===");
ExcelSheet<Quest> questSheet = dataManager.GetExcelSheet<Quest>(); ExcelSheet<Quest> questSheet = dataManager.GetExcelSheet<Quest>();
if (questSheet == null) if (questSheet == null)
{ {
log.Error("[MSQProgression] Failed to load Quest sheet from Lumina!");
return; return;
} }
int totalQuests = questSheet.Count(); questSheet.Count();
log.Information($"[MSQProgression] ✓ Lumina Quest Sheet loaded: {totalQuests} total quests");
int manualCount = 0; int manualCount = 0;
foreach (Quest item in questSheet) foreach (Quest item in questSheet)
{ {
_ = item; _ = item;
manualCount++; manualCount++;
} }
log.Information($"[MSQProgression] Manual iteration count: {manualCount} quests");
List<Quest> highIdQuests = questSheet.Where((Quest q) => q.RowId > 66000).ToList(); List<Quest> highIdQuests = questSheet.Where((Quest q) => q.RowId > 66000).ToList();
log.Information($"[MSQProgression] Quests with RowId > 66000: {highIdQuests.Count}");
if (highIdQuests.Count > 0) if (highIdQuests.Count > 0)
{ {
Quest firstHighId = highIdQuests.First(); highIdQuests.First();
log.Information($"[MSQProgression] First High ID Quest: {firstHighId.RowId}");
log.Information($"[MSQProgression] - Name: {firstHighId.Name}");
log.Information($"[MSQProgression] - Expansion.RowId: {firstHighId.Expansion.RowId}");
log.Information($"[MSQProgression] - JournalGenre.RowId: {firstHighId.JournalGenre.RowId}");
} }
log.Information("[MSQProgression] Analyzing JournalGenre distribution..."); foreach (IGrouping<uint, Quest> item2 in (from q in questSheet
foreach (IGrouping<uint, Quest> group in (from q in questSheet
where q.RowId != 0 where q.RowId != 0
group q by q.JournalGenre.RowId into g group q by q.JournalGenre.RowId into g
orderby g.Key orderby g.Key
select g).Take(10)) select g).Take(10))
{ {
log.Information($"[MSQProgression] Genre {group.Key}: {group.Count()} quests"); _ = item2;
} }
log.Information("[MSQProgression] Filtering MSQ quests by JournalGenre categories (1-14)...");
mainScenarioQuests = (from q in questSheet mainScenarioQuests = (from q in questSheet
where ((ReadOnlySpan<uint>)MSQ_JOURNAL_GENRE_IDS).Contains(q.JournalGenre.RowId) where ((ReadOnlySpan<uint>)MSQ_JOURNAL_GENRE_IDS).Contains(q.JournalGenre.RowId)
orderby q.RowId orderby q.RowId
select q).ToList(); select q).ToList();
log.Information($"[MSQProgression] ✓ Found {mainScenarioQuests.Count} total MSQ quests across all expansions!");
if (mainScenarioQuests.Count == 0) if (mainScenarioQuests.Count == 0)
{ {
log.Error("[MSQProgression] No MSQ quests found! JournalGenre filter may be incorrect.");
return; return;
} }
log.Information("[MSQProgression] === DETAILED MSQ QUEST ANALYSIS (First 20) ===");
foreach (Quest quest in mainScenarioQuests.Take(20)) foreach (Quest quest in mainScenarioQuests.Take(20))
{ {
log.Information($"[MSQProgression] Quest {quest.RowId}:");
log.Information($"[MSQProgression] - Name: {quest.Name}");
log.Information($"[MSQProgression] - Expansion.RowId: {quest.Expansion.RowId}");
try try
{ {
string expansionName = quest.Expansion.Value.Name.ToString(); quest.Expansion.Value.Name.ToString();
log.Information("[MSQProgression] - Expansion.Name: " + expansionName);
} }
catch (Exception ex) catch (Exception)
{ {
log.Information("[MSQProgression] - Expansion.Name: ERROR - " + ex.Message);
} }
log.Information($"[MSQProgression] - JournalGenre.RowId: {quest.JournalGenre.RowId}");
} }
log.Information("[MSQProgression] === MSQ QUESTS BY JOURNALGENRE (EXPANSION) ==="); foreach (IGrouping<uint, Quest> group in from q in mainScenarioQuests
foreach (IGrouping<uint, Quest> group2 in from q in mainScenarioQuests
group q by q.JournalGenre.RowId into g group q by q.JournalGenre.RowId into g
orderby g.Key orderby g.Key
select g) select g)
{ {
uint genreId = group2.Key; uint genreId = group.Key;
MSQExpansionData.Expansion expansion = JournalGenreToExpansion.GetValueOrDefault(genreId, MSQExpansionData.Expansion.ARealmReborn); JournalGenreToExpansion.GetValueOrDefault(genreId, MSQExpansionData.Expansion.ARealmReborn);
string genreName = group2.First().JournalGenre.Value.Name.ToString(); group.First().JournalGenre.Value.Name.ToString();
string sampleQuests = string.Join(", ", from q in group2.Take(3) string.Join(", ", from q in @group.Take(3)
select q.RowId); select q.RowId);
log.Information($"[MSQProgression] JournalGenre {genreId} ({expansion}):");
log.Information("[MSQProgression] - Name: " + genreName);
log.Information($"[MSQProgression] - Count: {group2.Count()} quests");
log.Information("[MSQProgression] - Samples: " + sampleQuests + "...");
} }
log.Information("[MSQProgression] === MSQ QUESTS BY EXPANSION (GROUPED) ==="); foreach (IGrouping<MSQExpansionData.Expansion, Quest> item3 in from q in mainScenarioQuests
foreach (IGrouping<MSQExpansionData.Expansion, Quest> group3 in from q in mainScenarioQuests
group q by JournalGenreToExpansion.GetValueOrDefault(q.JournalGenre.RowId, MSQExpansionData.Expansion.ARealmReborn) into g group q by JournalGenreToExpansion.GetValueOrDefault(q.JournalGenre.RowId, MSQExpansionData.Expansion.ARealmReborn) into g
orderby g.Key orderby g.Key
select g) select g)
{ {
log.Information($"[MSQProgression] {group3.Key}: {group3.Count()} quests total"); _ = item3;
} }
MSQExpansionData.ClearQuests(); MSQExpansionData.ClearQuests();
log.Information("[MSQProgression] Building expansion quest mappings...");
foreach (Quest quest2 in mainScenarioQuests) foreach (Quest quest2 in mainScenarioQuests)
{ {
string name = quest2.Name.ToString(); string name = quest2.Name.ToString();
@ -210,11 +183,11 @@ public class MSQProgressionService
{ {
questNameCache[quest2.RowId] = name; questNameCache[quest2.RowId] = name;
} }
MSQExpansionData.Expansion expansion2 = JournalGenreToExpansion.GetValueOrDefault(quest2.JournalGenre.RowId, MSQExpansionData.Expansion.ARealmReborn); MSQExpansionData.Expansion expansion = JournalGenreToExpansion.GetValueOrDefault(quest2.JournalGenre.RowId, MSQExpansionData.Expansion.ARealmReborn);
if (quest2.JournalGenre.RowId != 2 || quest2.RowId <= 65964) if (quest2.JournalGenre.RowId != 2 || quest2.RowId <= 65964)
{ {
MSQExpansionData.RegisterQuest(quest2.RowId, expansion2); MSQExpansionData.RegisterQuest(quest2.RowId, expansion);
string shortName = MSQExpansionData.GetExpansionShortName(expansion2); string shortName = MSQExpansionData.GetExpansionShortName(expansion);
if (!questsByExpansion.ContainsKey(shortName)) if (!questsByExpansion.ContainsKey(shortName))
{ {
questsByExpansion[shortName] = new List<Quest>(); questsByExpansion[shortName] = new List<Quest>();
@ -222,29 +195,19 @@ public class MSQProgressionService
questsByExpansion[shortName].Add(quest2); questsByExpansion[shortName].Add(quest2);
} }
} }
log.Information("[MSQProgression] === EXPANSION BREAKDOWN ==="); foreach (MSQExpansionData.Expansion allExpansion in MSQExpansionData.GetAllExpansions())
foreach (MSQExpansionData.Expansion exp in MSQExpansionData.GetAllExpansions())
{ {
string shortName2 = MSQExpansionData.GetExpansionShortName(exp); string shortName2 = MSQExpansionData.GetExpansionShortName(allExpansion);
List<Quest> quests = questsByExpansion.GetValueOrDefault(shortName2); List<Quest> quests = questsByExpansion.GetValueOrDefault(shortName2);
int count = quests?.Count ?? 0; if ((quests?.Count ?? 0) > 0 && quests != null)
if (count > 0 && quests != null)
{ {
string sampleIds = string.Join(", ", from q in quests.Take(5) string.Join(", ", from q in quests.Take(5)
select q.RowId); select q.RowId);
log.Information($"[MSQProgression] ✓ {MSQExpansionData.GetExpansionName(exp)} ({shortName2}): {count} quests (IDs: {sampleIds}...)");
}
else
{
log.Warning($"[MSQProgression] ⚠ {MSQExpansionData.GetExpansionName(exp)} ({shortName2}): {count} quests (EMPTY!)");
} }
} }
log.Information("[MSQProgression] === MSQ DATA INITIALIZATION COMPLETE ===");
} }
catch (Exception ex2) catch (Exception)
{ {
log.Error("[MSQProgression] EXCEPTION during MSQ data initialization: " + ex2.Message);
log.Error("[MSQProgression] Stack trace: " + ex2.StackTrace);
} }
} }
@ -267,9 +230,8 @@ public class MSQProgressionService
return (questId: lastMSQ.RowId, questName: questName); return (questId: lastMSQ.RowId, questName: questName);
} }
} }
catch (Exception ex) catch (Exception)
{ {
log.Error("[MSQProgression] Failed to get last completed MSQ: " + ex.Message);
} }
return (questId: 0u, questName: "—"); return (questId: 0u, questName: "—");
} }
@ -285,9 +247,8 @@ public class MSQProgressionService
List<uint> completedQuests = questDetectionService.GetAllCompletedQuestIds(); List<uint> completedQuests = questDetectionService.GetAllCompletedQuestIds();
return (float)mainScenarioQuests.Count((Quest q) => completedQuests.Contains(q.RowId)) / (float)mainScenarioQuests.Count * 100f; return (float)mainScenarioQuests.Count((Quest q) => completedQuests.Contains(q.RowId)) / (float)mainScenarioQuests.Count * 100f;
} }
catch (Exception ex) catch (Exception)
{ {
log.Error("[MSQProgression] Failed to calculate MSQ completion: " + ex.Message);
return 0f; return 0f;
} }
} }
@ -308,9 +269,8 @@ public class MSQProgressionService
List<uint> completedQuests = questDetectionService.GetAllCompletedQuestIds(); List<uint> completedQuests = questDetectionService.GetAllCompletedQuestIds();
return mainScenarioQuests.Count((Quest q) => completedQuests.Contains(q.RowId)); return mainScenarioQuests.Count((Quest q) => completedQuests.Contains(q.RowId));
} }
catch (Exception ex) catch (Exception)
{ {
log.Error("[MSQProgression] Failed to get completed MSQ count: " + ex.Message);
return 0; return 0;
} }
} }
@ -364,49 +324,25 @@ public class MSQProgressionService
{ {
try try
{ {
log.Information("[MSQProgression] ========================================");
log.Information("[MSQProgression] === DETECTING CURRENT EXPANSION ===");
log.Information("[MSQProgression] ========================================");
List<uint> completedQuests = questDetectionService.GetAllCompletedQuestIds(); List<uint> completedQuests = questDetectionService.GetAllCompletedQuestIds();
log.Information($"[MSQProgression] Total completed quests: {completedQuests.Count}");
log.Information("[MSQProgression] METHOD 1: Using AgentScenarioTree (Game Data)");
log.Information("[MSQProgression] ------------------------------------------------");
(MSQExpansionData.Expansion expansion, string debugInfo) currentExpansionFromGameWithDebug = MSQExpansionData.GetCurrentExpansionFromGameWithDebug(); (MSQExpansionData.Expansion expansion, string debugInfo) currentExpansionFromGameWithDebug = MSQExpansionData.GetCurrentExpansionFromGameWithDebug();
MSQExpansionData.Expansion gameExpansion = currentExpansionFromGameWithDebug.expansion; MSQExpansionData.Expansion gameExpansion = currentExpansionFromGameWithDebug.expansion;
string[] array = currentExpansionFromGameWithDebug.debugInfo.Split('\n'); string[] array = currentExpansionFromGameWithDebug.debugInfo.Split('\n');
foreach (string line in array) for (int i = 0; i < array.Length; i++)
{ {
if (!string.IsNullOrWhiteSpace(line)) string.IsNullOrWhiteSpace(array[i]);
{
log.Information("[MSQProgression] " + line);
}
} }
log.Information("[MSQProgression] Game Data Result: " + MSQExpansionData.GetExpansionName(gameExpansion));
log.Information("[MSQProgression] METHOD 2: Using Completed Quests Analysis");
log.Information("[MSQProgression] ------------------------------------------------");
MSQExpansionData.Expansion analysisExpansion = MSQExpansionData.GetCurrentExpansion(completedQuests); MSQExpansionData.Expansion analysisExpansion = MSQExpansionData.GetCurrentExpansion(completedQuests);
log.Information("[MSQProgression] Analysis Result: " + MSQExpansionData.GetExpansionName(analysisExpansion));
array = MSQExpansionData.GetExpansionDetectionDebugInfo(completedQuests).Split('\n'); array = MSQExpansionData.GetExpansionDetectionDebugInfo(completedQuests).Split('\n');
foreach (string line2 in array) for (int i = 0; i < array.Length; i++)
{ {
if (!string.IsNullOrWhiteSpace(line2)) string.IsNullOrWhiteSpace(array[i]);
{
log.Debug("[MSQProgression] " + line2);
}
} }
log.Information("[MSQProgression] COMPARISON:");
log.Information("[MSQProgression] Game Data: " + MSQExpansionData.GetExpansionName(gameExpansion));
log.Information("[MSQProgression] Analysis: " + MSQExpansionData.GetExpansionName(analysisExpansion));
MSQExpansionData.Expansion finalExpansion = gameExpansion; MSQExpansionData.Expansion finalExpansion = gameExpansion;
if (gameExpansion == MSQExpansionData.Expansion.ARealmReborn && analysisExpansion != MSQExpansionData.Expansion.ARealmReborn) if (gameExpansion == MSQExpansionData.Expansion.ARealmReborn && analysisExpansion != MSQExpansionData.Expansion.ARealmReborn)
{ {
log.Warning("[MSQProgression] Game data returned ARR but analysis found higher expansion!");
log.Warning("[MSQProgression] Using analysis result: " + MSQExpansionData.GetExpansionName(analysisExpansion));
finalExpansion = analysisExpansion; finalExpansion = analysisExpansion;
} }
log.Information("[MSQProgression] ========================================");
log.Information("[MSQProgression] >>> FINAL EXPANSION: " + MSQExpansionData.GetExpansionName(finalExpansion) + " <<<");
log.Information("[MSQProgression] ========================================");
return new ExpansionInfo return new ExpansionInfo
{ {
Name = MSQExpansionData.GetExpansionName(finalExpansion), Name = MSQExpansionData.GetExpansionName(finalExpansion),
@ -416,10 +352,8 @@ public class MSQProgressionService
ExpectedQuestCount = MSQExpansionData.GetExpectedQuestCount(finalExpansion) ExpectedQuestCount = MSQExpansionData.GetExpectedQuestCount(finalExpansion)
}; };
} }
catch (Exception ex) catch (Exception)
{ {
log.Error("[MSQProgression] Error detecting expansion: " + ex.Message);
log.Error("[MSQProgression] Stack: " + ex.StackTrace);
return GetExpansions().FirstOrDefault(); return GetExpansions().FirstOrDefault();
} }
} }
@ -514,28 +448,23 @@ public class MSQProgressionService
{ {
try try
{ {
log.Information("[MSQProgression] === DEBUG CURRENT CHARACTER QUEST ==="); IPlayerCharacter player = objectTable.LocalPlayer;
IPlayerCharacter player = clientState.LocalPlayer;
if (player == null) if (player == null)
{ {
log.Warning("[MSQProgression] LocalPlayer is null - not logged in yet?");
framework.RunOnTick(delegate framework.RunOnTick(delegate
{ {
DebugCurrentCharacterQuest(); DebugCurrentCharacterQuest();
}, default(TimeSpan), 60); }, default(TimeSpan), 60);
return; return;
} }
string characterName = player.Name.TextValue; _ = player.Name.TextValue;
string worldName = player.HomeWorld.Value.Name.ToString(); player.HomeWorld.Value.Name.ToString();
log.Information("[MSQProgression] Character: " + characterName + " @ " + worldName);
ExcelSheet<Quest> questSheet = dataManager.GetExcelSheet<Quest>(); ExcelSheet<Quest> questSheet = dataManager.GetExcelSheet<Quest>();
if (questSheet == null) if (questSheet == null)
{ {
log.Error("[MSQProgression] Failed to load Quest sheet!");
return; return;
} }
List<Quest> completedMSQQuests = new List<Quest>(); List<Quest> completedMSQQuests = new List<Quest>();
log.Information("[MSQProgression] Checking MSQ quest completion...");
foreach (Quest quest in questSheet) foreach (Quest quest in questSheet)
{ {
if (((ReadOnlySpan<uint>)MSQ_JOURNAL_GENRE_IDS).Contains(quest.JournalGenre.RowId) && QuestManager.IsQuestComplete((ushort)quest.RowId)) if (((ReadOnlySpan<uint>)MSQ_JOURNAL_GENRE_IDS).Contains(quest.JournalGenre.RowId) && QuestManager.IsQuestComplete((ushort)quest.RowId))
@ -543,60 +472,39 @@ public class MSQProgressionService
completedMSQQuests.Add(quest); completedMSQQuests.Add(quest);
} }
} }
log.Information($"[MSQProgression] Character has {completedMSQQuests.Count} completed MSQ quests");
if (completedMSQQuests.Count == 0) if (completedMSQQuests.Count == 0)
{ {
log.Warning("[MSQProgression] No completed MSQ quests found!");
return; return;
} }
Quest latestMSQQuest = completedMSQQuests.OrderByDescending((Quest quest2) => quest2.RowId).First(); Quest questData = completedMSQQuests.OrderByDescending((Quest q) => q.RowId).First();
log.Information($"[MSQProgression] Latest completed MSQ quest ID: {latestMSQQuest.RowId}");
Quest questData = latestMSQQuest;
log.Information("[MSQProgression] === LATEST MSQ QUEST DETAILS ===");
log.Information($"[MSQProgression] Quest ID: {questData.RowId}");
log.Information($"[MSQProgression] Quest Name: {questData.Name}");
log.Information($"[MSQProgression] JournalGenre.RowId: {questData.JournalGenre.RowId}");
try try
{ {
string genreName = questData.JournalGenre.Value.Name.ToString(); questData.JournalGenre.Value.Name.ToString();
log.Information("[MSQProgression] JournalGenre.Name: " + genreName);
} }
catch catch
{ {
log.Information("[MSQProgression] JournalGenre.Name: ERROR");
} }
log.Information($"[MSQProgression] Expansion.RowId: {questData.Expansion.RowId}");
try try
{ {
string expansionName = questData.Expansion.Value.Name.ToString(); questData.Expansion.Value.Name.ToString();
log.Information("[MSQProgression] Expansion.Name: " + expansionName);
} }
catch catch
{ {
log.Information("[MSQProgression] Expansion.Name: ERROR");
} }
log.Information("[MSQProgression] === CHARACTER IS IN THIS EXPANSION ==="); foreach (Quest item in completedMSQQuests.OrderByDescending((Quest q) => q.RowId).Take(10).ToList())
log.Information($"[MSQProgression] Character is at Quest {questData.RowId} which is in:");
log.Information($"[MSQProgression] - JournalGenre: {questData.JournalGenre.RowId}");
log.Information($"[MSQProgression] - Expansion: {questData.Expansion.RowId}");
log.Information("[MSQProgression] === RECENT COMPLETED MSQ QUESTS (Last 10) ===");
foreach (Quest q in completedMSQQuests.OrderByDescending((Quest quest2) => quest2.RowId).Take(10).ToList())
{ {
log.Information($"[MSQProgression] Quest {q.RowId}: {q.Name} (Genre: {q.JournalGenre.RowId}, Exp: {q.Expansion.RowId})"); _ = item;
} }
log.Information("[MSQProgression] === COMPLETED MSQ QUESTS BY EXPANSION ==="); foreach (IGrouping<MSQExpansionData.Expansion, Quest> item2 in from q in completedMSQQuests
foreach (IGrouping<MSQExpansionData.Expansion, Quest> group in from quest2 in completedMSQQuests group q by JournalGenreToExpansion.GetValueOrDefault(q.JournalGenre.RowId, MSQExpansionData.Expansion.ARealmReborn) into g
group quest2 by JournalGenreToExpansion.GetValueOrDefault(quest2.JournalGenre.RowId, MSQExpansionData.Expansion.ARealmReborn) into g
orderby g.Key orderby g.Key
select g) select g)
{ {
log.Information($"[MSQProgression] {group.Key}: {group.Count()} quests completed"); _ = item2;
} }
} }
catch (Exception ex) catch (Exception)
{ {
log.Error("[MSQProgression] ERROR in DebugCurrentCharacterQuest: " + ex.Message);
log.Error("[MSQProgression] Stack trace: " + ex.StackTrace);
} }
} }
} }

View file

@ -48,6 +48,8 @@ public class QuestRotationExecutionService : IDisposable
private StepsOfFaithHandler? stepsOfFaithHandler; private StepsOfFaithHandler? stepsOfFaithHandler;
private ErrorRecoveryService? errorRecoveryService;
private readonly List<StopPoint> stopPoints = new List<StopPoint>(); private readonly List<StopPoint> stopPoints = new List<StopPoint>();
private RotationState currentState = new RotationState(); private RotationState currentState = new RotationState();
@ -64,10 +66,6 @@ public class QuestRotationExecutionService : IDisposable
private bool waitingForQuestAcceptForSubmarines; private bool waitingForQuestAcceptForSubmarines;
private uint? lastDungeonQuestId;
private int? lastDungeonSequence;
private uint? lastSoloDutyQuestId; private uint? lastSoloDutyQuestId;
private const double CharacterLoginTimeoutSeconds = 60.0; private const double CharacterLoginTimeoutSeconds = 60.0;
@ -480,6 +478,12 @@ public class QuestRotationExecutionService : IDisposable
log.Information("[QuestRotation] Steps of Faith Handler service linked"); log.Information("[QuestRotation] Steps of Faith Handler service linked");
} }
public void SetErrorRecoveryService(ErrorRecoveryService service)
{
errorRecoveryService = service;
log.Information("[QuestRotation] Error Recovery service linked");
}
private void MarkQuestCompleted(uint questId, string characterName) private void MarkQuestCompleted(uint questId, string characterName)
{ {
if (!questCompletionByCharacter.ContainsKey(questId)) if (!questCompletionByCharacter.ContainsKey(questId))
@ -626,6 +630,24 @@ public class QuestRotationExecutionService : IDisposable
return; return;
} }
lastCheckTime = now; lastCheckTime = now;
if (isRotationActive && errorRecoveryService != null && errorRecoveryService.IsErrorDisconnect)
{
string charToRelog = errorRecoveryService.LastDisconnectedCharacter ?? currentState.CurrentCharacter;
if (!string.IsNullOrEmpty(charToRelog))
{
log.Warning("[ErrorRecovery] Disconnect detected for " + charToRelog);
log.Information("[ErrorRecovery] Automatically relogging to " + charToRelog + "...");
errorRecoveryService.Reset();
currentState.Phase = RotationPhase.WaitingForCharacterLogin;
currentState.CurrentCharacter = charToRelog;
currentState.PhaseStartTime = DateTime.Now;
autoRetainerIpc.SwitchCharacter(charToRelog);
log.Information("[ErrorRecovery] Relog initiated for " + charToRelog);
return;
}
log.Warning("[ErrorRecovery] Disconnect detected but no character to relog to");
errorRecoveryService.Reset();
}
if (deathHandler != null && combatDutyDetection != null && !combatDutyDetection.IsInDuty) if (deathHandler != null && combatDutyDetection != null && !combatDutyDetection.IsInDuty)
{ {
deathHandler.Update(); deathHandler.Update();
@ -1037,7 +1059,19 @@ public class QuestRotationExecutionService : IDisposable
private unsafe void HandleQuestMonitoring() private unsafe void HandleQuestMonitoring()
{ {
uint questId = currentState.CurrentStopQuestId; uint questId = currentState.CurrentStopQuestId;
if (!submarineManager.IsSubmarinePaused && !submarineManager.IsSubmarineCooldownActive()) bool hasReachedStopSequence = false;
StopPoint stopPointForSubmarine = stopPoints.FirstOrDefault((StopPoint sp) => sp.QuestId == questId && sp.IsActive);
if (stopPointForSubmarine != null && stopPointForSubmarine.Sequence.HasValue)
{
string currentQuestIdStr = questionableIPC.GetCurrentQuestId();
byte? currentSequence = questionableIPC.GetCurrentSequence();
if (!string.IsNullOrEmpty(currentQuestIdStr) && currentSequence.HasValue && uint.TryParse(currentQuestIdStr, out var currentQuestId) && currentQuestId == questId && currentSequence.Value >= stopPointForSubmarine.Sequence.Value)
{
hasReachedStopSequence = true;
log.Debug($"[QuestRotation] Stop sequence reached (Quest {questId} Seq {currentSequence.Value} >= {stopPointForSubmarine.Sequence.Value}) - skipping submarine check");
}
}
if (!hasReachedStopSequence && !submarineManager.IsSubmarinePaused && !submarineManager.IsSubmarineCooldownActive())
{ {
TimeSpan submarineCheckInterval = TimeSpan.FromSeconds(configuration.SubmarineCheckInterval); TimeSpan submarineCheckInterval = TimeSpan.FromSeconds(configuration.SubmarineCheckInterval);
if (DateTime.Now - lastSubmarineCheckTime >= submarineCheckInterval) if (DateTime.Now - lastSubmarineCheckTime >= submarineCheckInterval)
@ -1128,16 +1162,16 @@ public class QuestRotationExecutionService : IDisposable
bool shouldRotate = false; bool shouldRotate = false;
if (activeStopPoint != null && activeStopPoint.Sequence.HasValue) if (activeStopPoint != null && activeStopPoint.Sequence.HasValue)
{ {
string currentQuestIdStr = questionableIPC.GetCurrentQuestId(); string currentQuestIdStr2 = questionableIPC.GetCurrentQuestId();
byte? currentSequence = questionableIPC.GetCurrentSequence(); byte? currentSequence2 = questionableIPC.GetCurrentSequence();
if (!string.IsNullOrEmpty(currentQuestIdStr) && currentSequence.HasValue && uint.TryParse(currentQuestIdStr, out var currentQuestId)) if (!string.IsNullOrEmpty(currentQuestIdStr2) && currentSequence2.HasValue && uint.TryParse(currentQuestIdStr2, out var currentQuestId2))
{ {
if (currentQuestId == questId) if (currentQuestId2 == questId)
{ {
if (currentSequence.Value >= activeStopPoint.Sequence.Value) if (currentSequence2.Value >= activeStopPoint.Sequence.Value)
{ {
log.Information($"[QuestRotation] ✓ Quest {questId} Sequence {activeStopPoint.Sequence.Value} reached by {currentState.CurrentCharacter}!"); log.Information($"[QuestRotation] ✓ Quest {questId} Sequence {activeStopPoint.Sequence.Value} reached by {currentState.CurrentCharacter}!");
log.Information($"[QuestRotation] Current Sequence: {currentSequence.Value} (reached {activeStopPoint.Sequence.Value})"); log.Information($"[QuestRotation] Current Sequence: {currentSequence2.Value} (reached {activeStopPoint.Sequence.Value})");
shouldRotate = true; shouldRotate = true;
} }
} }

View file

@ -1,6 +1,7 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO; using System.IO;
using System.Linq;
using System.Text.RegularExpressions; using System.Text.RegularExpressions;
using Dalamud.Plugin.Services; using Dalamud.Plugin.Services;
using Newtonsoft.Json.Linq; using Newtonsoft.Json.Linq;
@ -228,7 +229,32 @@ public class SubmarineManager : IDisposable
try try
{ {
JObject json = JObject.Parse(jsonContent); JObject json = JObject.Parse(jsonContent);
FindReturnTimes(json, returnTimes); HashSet<string> enabledSubs = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
if (json.SelectTokens("$..EnabledSubs").FirstOrDefault() is JArray enabledSubsArray)
{
foreach (JToken item in enabledSubsArray)
{
string subName = item.Value<string>();
if (!string.IsNullOrEmpty(subName))
{
enabledSubs.Add(subName);
}
}
if (enabledSubs.Count > 0)
{
log.Information($"[SubmarineManager] Found {enabledSubs.Count} enabled submarines: {string.Join(", ", enabledSubs)}");
}
else
{
log.Information("[SubmarineManager] EnabledSubs array found but empty - NO submarines will be checked");
}
FindReturnTimes(json, returnTimes, enabledSubs);
}
else
{
log.Information("[SubmarineManager] No EnabledSubs found in config - checking all submarines");
FindReturnTimes(json, returnTimes);
}
} }
catch catch
{ {
@ -244,21 +270,27 @@ public class SubmarineManager : IDisposable
return returnTimes; return returnTimes;
} }
private void FindReturnTimes(JToken token, List<long> returnTimes) private void FindReturnTimes(JToken token, List<long> returnTimes, HashSet<string>? enabledSubs = null)
{ {
if (token is JObject obj) if (token is JObject obj)
{ {
if (obj.TryGetValue("Name", out JToken nameToken) && obj.TryGetValue("ReturnTime", out JToken returnTimeToken))
{
string submarineName = nameToken.Value<string>();
if ((enabledSubs == null || (submarineName != null && enabledSubs.Contains(submarineName))) && returnTimeToken.Type == JTokenType.Integer)
{
long returnTime = returnTimeToken.Value<long>();
returnTimes.Add(returnTime);
if (enabledSubs != null)
{
log.Debug($"[SubmarineManager] Including submarine '{submarineName}' (ReturnTime: {returnTime})");
}
}
}
{ {
foreach (JProperty property in obj.Properties()) foreach (JProperty property in obj.Properties())
{ {
if (property.Name == "ReturnTime" && property.Value.Type == JTokenType.Integer) FindReturnTimes(property.Value, returnTimes, enabledSubs);
{
returnTimes.Add(property.Value.Value<long>());
}
else
{
FindReturnTimes(property.Value, returnTimes);
}
} }
return; return;
} }
@ -269,7 +301,7 @@ public class SubmarineManager : IDisposable
} }
foreach (JToken item in array) foreach (JToken item in array)
{ {
FindReturnTimes(item, returnTimes); FindReturnTimes(item, returnTimes, enabledSubs);
} }
} }

View file

@ -50,6 +50,8 @@ public class NewMainWindow : Window, IDisposable
private readonly MSQProgressionService msqProgressionService; private readonly MSQProgressionService msqProgressionService;
private readonly Configuration configuration;
private readonly IPluginLog log; private readonly IPluginLog log;
private readonly IUiBuilder uiBuilder; private readonly IUiBuilder uiBuilder;
@ -128,12 +130,6 @@ public class NewMainWindow : Window, IDisposable
private string selectedWorldFilter = "All"; private string selectedWorldFilter = "All";
private bool showWorldActionDropdown;
private uint inputStopQuestId;
private int inputStopSequence = -1;
private string selectedEventQuestId = ""; private string selectedEventQuestId = "";
private List<(string QuestId, string QuestName)> availableEventQuests = new List<(string, string)>(); private List<(string QuestId, string QuestName)> availableEventQuests = new List<(string, string)>();
@ -314,8 +310,8 @@ public class NewMainWindow : Window, IDisposable
} }
} }
public NewMainWindow(Plugin plugin, AutoRetainerIPC autoRetainerIpc, QuestTrackingService questTrackingService, QuestRotationExecutionService questRotationService, EventQuestExecutionService eventQuestService, AlliedSocietyRotationService alliedSocietyRotationService, AlliedSocietyPriorityWindow alliedSocietyPriorityWindow, DataCenterService dataCenterService, MSQProgressionService msqProgressionService, IPluginLog log, IUiBuilder uiBuilder, IDataManager dataManager) public NewMainWindow(Plugin plugin, AutoRetainerIPC autoRetainerIpc, QuestTrackingService questTrackingService, QuestRotationExecutionService questRotationService, EventQuestExecutionService eventQuestService, AlliedSocietyRotationService alliedSocietyRotationService, AlliedSocietyPriorityWindow alliedSocietyPriorityWindow, DataCenterService dataCenterService, MSQProgressionService msqProgressionService, Configuration configuration, IPluginLog log, IUiBuilder uiBuilder, IDataManager dataManager)
: base("Questionable Companion##NewMainWindow", ImGuiWindowFlags.NoTitleBar | ImGuiWindowFlags.NoScrollbar | ImGuiWindowFlags.NoScrollWithMouse | ImGuiWindowFlags.NoBackground) : base("Questionable Companion##NewMainWindow", ImGuiWindowFlags.NoTitleBar | ImGuiWindowFlags.NoScrollbar | ImGuiWindowFlags.NoScrollWithMouse | ImGuiWindowFlags.NoCollapse | ImGuiWindowFlags.NoBackground)
{ {
this.plugin = plugin; this.plugin = plugin;
this.autoRetainerIpc = autoRetainerIpc; this.autoRetainerIpc = autoRetainerIpc;
@ -326,6 +322,7 @@ public class NewMainWindow : Window, IDisposable
this.alliedSocietyPriorityWindow = alliedSocietyPriorityWindow; this.alliedSocietyPriorityWindow = alliedSocietyPriorityWindow;
this.dataCenterService = dataCenterService; this.dataCenterService = dataCenterService;
this.msqProgressionService = msqProgressionService; this.msqProgressionService = msqProgressionService;
this.configuration = configuration;
this.log = log; this.log = log;
this.uiBuilder = uiBuilder; this.uiBuilder = uiBuilder;
this.dataManager = dataManager; this.dataManager = dataManager;
@ -431,7 +428,6 @@ public class NewMainWindow : Window, IDisposable
if (registeredCharacters.Count > 0) if (registeredCharacters.Count > 0)
{ {
initialCharacterLoadComplete = true; initialCharacterLoadComplete = true;
log.Information("[NewMainWindow] ✅ Initial character load successful");
} }
charactersByDataCenter = dataCenterService.GroupCharactersByDataCenter(registeredCharacters); charactersByDataCenter = dataCenterService.GroupCharactersByDataCenter(registeredCharacters);
availableWorlds = (from w in (from c in registeredCharacters availableWorlds = (from w in (from c in registeredCharacters
@ -447,6 +443,13 @@ public class NewMainWindow : Window, IDisposable
characterSelection[character] = false; characterSelection[character] = false;
} }
} }
foreach (string savedChar in configuration.SelectedCharactersForUI)
{
if (characterSelection.ContainsKey(savedChar))
{
characterSelection[savedChar] = true;
}
}
} }
catch (Exception ex) catch (Exception ex)
{ {
@ -613,7 +616,7 @@ public class NewMainWindow : Window, IDisposable
uint rightColor = ImGui.ColorConvertFloat4ToU32(new Vector4(colorSecondary.X * 0.3f, colorSecondary.Y * 0.3f, colorSecondary.Z * 0.3f, 1f)); uint rightColor = ImGui.ColorConvertFloat4ToU32(new Vector4(colorSecondary.X * 0.3f, colorSecondary.Y * 0.3f, colorSecondary.Z * 0.3f, 1f));
drawList.AddRectFilledMultiColor(windowPos, windowPos + new Vector2(windowSize.X, height), leftColor, rightColor, rightColor, leftColor); drawList.AddRectFilledMultiColor(windowPos, windowPos + new Vector2(windowSize.X, height), leftColor, rightColor, rightColor, leftColor);
Vector2 titlePos = windowPos + new Vector2(10f, 7f); Vector2 titlePos = windowPos + new Vector2(10f, 7f);
drawList.AddText(titlePos, ImGui.ColorConvertFloat4ToU32(new Vector4(1f, 1f, 1f, 0.9f)), "Questionable Companion V.1.0.4"); drawList.AddText(titlePos, ImGui.ColorConvertFloat4ToU32(new Vector4(1f, 1f, 1f, 0.9f)), "Questionable Companion V.1.0.5");
Vector2 minimizeButtonPos = windowPos + new Vector2(windowSize.X - 60f, 3f); Vector2 minimizeButtonPos = windowPos + new Vector2(windowSize.X - 60f, 3f);
Vector2 minimizeButtonSize = new Vector2(24f, 24f); Vector2 minimizeButtonSize = new Vector2(24f, 24f);
if (ImGui.IsMouseHoveringRect(minimizeButtonPos, minimizeButtonPos + minimizeButtonSize)) if (ImGui.IsMouseHoveringRect(minimizeButtonPos, minimizeButtonPos + minimizeButtonSize))
@ -624,7 +627,22 @@ public class NewMainWindow : Window, IDisposable
isMinimized = !isMinimized; isMinimized = !isMinimized;
} }
} }
drawList.AddText(minimizeButtonPos + new Vector2(8f, 2f), ImGui.ColorConvertFloat4ToU32(new Vector4(1f, 1f, 1f, 1f)), "_"); uint arrowColor = ImGui.ColorConvertFloat4ToU32(new Vector4(1f, 1f, 1f, 1f));
Vector2 arrowCenter = minimizeButtonPos + new Vector2(12f, 12f);
if (isMinimized)
{
Vector2 p1 = arrowCenter + new Vector2(-4f, -5f);
Vector2 p2 = arrowCenter + new Vector2(-4f, 5f);
Vector2 p3 = arrowCenter + new Vector2(4f, 0f);
drawList.AddTriangleFilled(p1, p2, p3, arrowColor);
}
else
{
Vector2 p4 = arrowCenter + new Vector2(-5f, -4f);
Vector2 p5 = arrowCenter + new Vector2(5f, -4f);
Vector2 p6 = arrowCenter + new Vector2(0f, 4f);
drawList.AddTriangleFilled(p4, p5, p6, arrowColor);
}
Vector2 closeButtonPos = windowPos + new Vector2(windowSize.X - 30f, 3f); Vector2 closeButtonPos = windowPos + new Vector2(windowSize.X - 30f, 3f);
Vector2 closeButtonSize = new Vector2(24f, 24f); Vector2 closeButtonSize = new Vector2(24f, 24f);
if (ImGui.IsMouseHoveringRect(closeButtonPos, closeButtonPos + closeButtonSize)) if (ImGui.IsMouseHoveringRect(closeButtonPos, closeButtonPos + closeButtonSize))
@ -1909,6 +1927,10 @@ public class NewMainWindow : Window, IDisposable
{ {
characterSelection[character] = true; characterSelection[character] = true;
} }
configuration.SelectedCharactersForUI = (from kvp in characterSelection
where kvp.Value
select kvp.Key).ToList();
configuration.Save();
} }
ImGui.SameLine(); ImGui.SameLine();
if (ImGui.Button("Deselect All")) if (ImGui.Button("Deselect All"))
@ -1917,6 +1939,10 @@ public class NewMainWindow : Window, IDisposable
{ {
characterSelection[character2] = false; characterSelection[character2] = false;
} }
configuration.SelectedCharactersForUI = (from kvp in characterSelection
where kvp.Value
select kvp.Key).ToList();
configuration.Save();
} }
ImGui.SameLine(); ImGui.SameLine();
ImGui.SetNextItemWidth(150f); ImGui.SetNextItemWidth(150f);
@ -1967,6 +1993,10 @@ public class NewMainWindow : Window, IDisposable
if (ImGui.Checkbox("##Select", ref isSelected)) if (ImGui.Checkbox("##Select", ref isSelected))
{ {
characterSelection[character3] = isSelected; characterSelection[character3] = isSelected;
configuration.SelectedCharactersForUI = (from kvp in characterSelection
where kvp.Value
select kvp.Key).ToList();
configuration.Save();
} }
} }
ImGui.SetCursorScreenPos(cursorPos + new Vector2(40f, 8f)); ImGui.SetCursorScreenPos(cursorPos + new Vector2(40f, 8f));

View file

@ -42,6 +42,8 @@ public class Configuration : IPluginConfiguration
public List<string> SelectedCharactersForRotation { get; set; } = new List<string>(); public List<string> SelectedCharactersForRotation { get; set; } = new List<string>();
public List<string> SelectedCharactersForUI { get; set; } = new List<string>();
public Dictionary<uint, List<string>> QuestCompletionByCharacter { get; set; } = new Dictionary<uint, List<string>>(); public Dictionary<uint, List<string>> QuestCompletionByCharacter { get; set; } = new Dictionary<uint, List<string>>();
public Dictionary<string, List<string>> EventQuestCompletionByCharacter { get; set; } = new Dictionary<string, List<string>>(); public Dictionary<string, List<string>> EventQuestCompletionByCharacter { get; set; } = new Dictionary<string, List<string>>();

View file

@ -1,5 +1,6 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Numerics;
using System.Reflection; using System.Reflection;
using Dalamud.Game.ClientState.Objects.SubKinds; using Dalamud.Game.ClientState.Objects.SubKinds;
using Dalamud.Game.Command; using Dalamud.Game.Command;
@ -126,6 +127,8 @@ public sealed class Plugin : IDalamudPlugin, IDisposable
private AlliedSocietyPriorityWindow AlliedSocietyPriorityWindow { get; init; } private AlliedSocietyPriorityWindow AlliedSocietyPriorityWindow { get; init; }
private ErrorRecoveryService ErrorRecoveryService { get; init; }
private ConfigWindow ConfigWindow { get; init; } private ConfigWindow ConfigWindow { get; init; }
private NewMainWindow NewMainWindow { get; init; } private NewMainWindow NewMainWindow { get; init; }
@ -182,7 +185,7 @@ public sealed class Plugin : IDalamudPlugin, IDisposable
Log.Debug("[Plugin] Initializing StepsOfFaithHandler..."); Log.Debug("[Plugin] Initializing StepsOfFaithHandler...");
StepsOfFaithHandler = new StepsOfFaithHandler(Condition, Log, ClientState, CommandManager, Framework, Configuration); StepsOfFaithHandler = new StepsOfFaithHandler(Condition, Log, ClientState, CommandManager, Framework, Configuration);
Log.Debug("[Plugin] Initializing MSQProgressionService..."); Log.Debug("[Plugin] Initializing MSQProgressionService...");
MSQProgressionService = new MSQProgressionService(DataManager, Log, QuestDetection, ClientState, Framework); MSQProgressionService = new MSQProgressionService(DataManager, Log, QuestDetection, ObjectTable, Framework);
Log.Debug("[Plugin] Initializing ChauffeurMode..."); Log.Debug("[Plugin] Initializing ChauffeurMode...");
ChauffeurMode = new ChauffeurModeService(Configuration, Log, ClientState, Condition, Framework, CommandManager, DataManager, PartyList, ObjectTable, QuestionableIPC, CrossProcessIPC, PartyInviteService, PartyInviteAutoAccept, PluginInterface, MemoryHelper, MovementMonitor); ChauffeurMode = new ChauffeurModeService(Configuration, Log, ClientState, Condition, Framework, CommandManager, DataManager, PartyList, ObjectTable, QuestionableIPC, CrossProcessIPC, PartyInviteService, PartyInviteAutoAccept, PluginInterface, MemoryHelper, MovementMonitor);
MovementMonitor.SetChauffeurMode(ChauffeurMode); MovementMonitor.SetChauffeurMode(ChauffeurMode);
@ -194,6 +197,9 @@ public sealed class Plugin : IDalamudPlugin, IDisposable
AlliedSocietyQuestSelector = new AlliedSocietyQuestSelector(QuestionableIPC, Log); AlliedSocietyQuestSelector = new AlliedSocietyQuestSelector(QuestionableIPC, Log);
AlliedSocietyRotationService = new AlliedSocietyRotationService(QuestionableIPC, AlliedSocietyDatabase, AlliedSocietyQuestSelector, AutoRetainerIPC, Configuration, Log, Framework, CommandManager, Condition, ClientState); AlliedSocietyRotationService = new AlliedSocietyRotationService(QuestionableIPC, AlliedSocietyDatabase, AlliedSocietyQuestSelector, AutoRetainerIPC, Configuration, Log, Framework, CommandManager, Condition, ClientState);
AlliedSocietyPriorityWindow = new AlliedSocietyPriorityWindow(Configuration, AlliedSocietyDatabase); AlliedSocietyPriorityWindow = new AlliedSocietyPriorityWindow(Configuration, AlliedSocietyDatabase);
Log.Debug("[Plugin] Initializing Error Recovery Service...");
ErrorRecoveryService = new ErrorRecoveryService(Log, GameInterop, ClientState, AutoRetainerIPC, Framework, GameGui);
QuestRotationService.SetErrorRecoveryService(ErrorRecoveryService);
MultiClientIPC.OnChatMessageReceived += OnMultiClientChatReceived; MultiClientIPC.OnChatMessageReceived += OnMultiClientChatReceived;
CrossProcessIPC.OnChatMessageReceived += OnMultiClientChatReceived; CrossProcessIPC.OnChatMessageReceived += OnMultiClientChatReceived;
CrossProcessIPC.OnCommandReceived += OnCommandReceived; CrossProcessIPC.OnCommandReceived += OnCommandReceived;
@ -218,7 +224,7 @@ public sealed class Plugin : IDalamudPlugin, IDisposable
} }
Log.Debug("[Plugin] Initializing windows..."); Log.Debug("[Plugin] Initializing windows...");
ConfigWindow = new ConfigWindow(this); ConfigWindow = new ConfigWindow(this);
NewMainWindow = new NewMainWindow(this, AutoRetainerIPC, QuestTrackingService, QuestRotationService, EventQuestService, AlliedSocietyRotationService, AlliedSocietyPriorityWindow, dataCenterService, MSQProgressionService, Log, PluginInterface.UiBuilder, DataManager); NewMainWindow = new NewMainWindow(this, AutoRetainerIPC, QuestTrackingService, QuestRotationService, EventQuestService, AlliedSocietyRotationService, AlliedSocietyPriorityWindow, dataCenterService, MSQProgressionService, Configuration, Log, PluginInterface.UiBuilder, DataManager);
DebugWindow = new DebugWindow(this, CombatDutyDetection, DeathHandler, DungeonAutomation); DebugWindow = new DebugWindow(this, CombatDutyDetection, DeathHandler, DungeonAutomation);
WindowSystem.AddWindow(ConfigWindow); WindowSystem.AddWindow(ConfigWindow);
WindowSystem.AddWindow(NewMainWindow); WindowSystem.AddWindow(NewMainWindow);
@ -228,6 +234,14 @@ public sealed class Plugin : IDalamudPlugin, IDisposable
{ {
HelpMessage = "Open the Quest Sequence Manager" HelpMessage = "Open the Quest Sequence Manager"
}); });
string[] array = new string[2] { "/qstc", "/qstcompanion" };
foreach (string alias in array)
{
CommandManager.AddHandler(alias, new CommandInfo(OnCommand)
{
HelpMessage = "Open the Quest Sequence Manager"
});
}
CommandManager.AddHandler("/qsthelper", new CommandInfo(OnHelperCommand) CommandManager.AddHandler("/qsthelper", new CommandInfo(OnHelperCommand)
{ {
HelpMessage = "Helper commands: /qsthelper reset - Reset helper status to Available" HelpMessage = "Helper commands: /qsthelper reset - Reset helper status to Available"
@ -393,6 +407,7 @@ public sealed class Plugin : IDalamudPlugin, IDisposable
LifestreamIPC?.Dispose(); LifestreamIPC?.Dispose();
AutoRetainerIPC?.Dispose(); AutoRetainerIPC?.Dispose();
QuestionableIPC?.Dispose(); QuestionableIPC?.Dispose();
ErrorRecoveryService?.Dispose();
Log.Debug("[Plugin] Services disposed"); Log.Debug("[Plugin] Services disposed");
} }
catch (Exception ex3) catch (Exception ex3)
@ -418,6 +433,11 @@ public sealed class Plugin : IDalamudPlugin, IDisposable
{ {
CommandManager.RemoveHandler("/qstcomp"); CommandManager.RemoveHandler("/qstcomp");
CommandManager.RemoveHandler("/qsthelper"); CommandManager.RemoveHandler("/qsthelper");
string[] array = new string[2] { "/qstc", "/qstcompanion" };
foreach (string alias in array)
{
CommandManager.RemoveHandler(alias);
}
Log.Debug("[Plugin] Command handlers removed"); Log.Debug("[Plugin] Command handlers removed");
} }
catch (Exception ex5) catch (Exception ex5)
@ -487,6 +507,9 @@ public sealed class Plugin : IDalamudPlugin, IDisposable
case "mounts": case "mounts":
TestListMounts(); TestListMounts();
break; break;
case "aetheryte":
TestFindNearestAetheryte();
break;
default: default:
NewMainWindow.Toggle(); NewMainWindow.Toggle();
break; break;
@ -802,6 +825,85 @@ public sealed class Plugin : IDalamudPlugin, IDisposable
Log.Information("========================================"); Log.Information("========================================");
} }
private void TestFindNearestAetheryte()
{
Log.Information("========================================");
Log.Information("[TEST] Finding Nearest Aetheryte (Map Data)");
Log.Information("========================================");
IPlayerCharacter player = ClientState.LocalPlayer;
if (player == null)
{
Log.Error("[TEST] Player not logged in!");
Log.Information("========================================");
return;
}
Vector3 playerPos = player.Position;
ushort territoryId = ClientState.TerritoryType;
Log.Information($"[TEST] Player Position: ({playerPos.X:F2}, {playerPos.Y:F2}, {playerPos.Z:F2})");
Log.Information($"[TEST] Territory ID: {territoryId}");
ExcelSheet<Aetheryte> aetheryteSheet = DataManager.GetExcelSheet<Aetheryte>();
ExcelSheet<Map> mapSheet = DataManager.GetExcelSheet<Map>();
if (aetheryteSheet == null || mapSheet == null)
{
Log.Error("[TEST] Failed to load sheets!");
Log.Information("========================================");
return;
}
float minDistance = float.MaxValue;
string nearestName = "Unknown";
uint nearestId = 0u;
int aetheryteCount = 0;
foreach (Aetheryte aetheryte in aetheryteSheet)
{
if (aetheryte.Territory.RowId != territoryId || !aetheryte.IsAetheryte)
{
continue;
}
aetheryteCount++;
PlaceName? placeName = aetheryte.PlaceName.ValueNullable;
string nameStr = (placeName.HasValue ? placeName.Value.Name.ExtractText() : $"Aetheryte #{aetheryte.RowId}");
Map? map = aetheryte.Map.ValueNullable;
if (map.HasValue)
{
float mapX = ConvertMapCoordinateToRawPosition(aetheryte.AetherstreamX, map.Value.SizeFactor);
float mapY = ConvertMapCoordinateToRawPosition(aetheryte.AetherstreamY, map.Value.SizeFactor);
Vector3 aethPos = new Vector3(mapX, 0f, mapY);
float num = playerPos.X - aethPos.X;
float dz = playerPos.Z - aethPos.Z;
float distance = (float)Math.Sqrt(num * num + dz * dz);
Log.Information($"[TEST] Aetheryte #{aetheryteCount}: {nameStr}");
Log.Information($"[TEST] Position: ({aethPos.X:F2}, ?, {aethPos.Z:F2})");
Log.Information($"[TEST] Distance (2D): {distance:F2} yalms");
Log.Information($"[TEST] Aetheryte ID: {aetheryte.RowId}");
if (distance < minDistance)
{
minDistance = distance;
nearestName = nameStr;
nearestId = aetheryte.RowId;
}
}
}
if (aetheryteCount == 0)
{
Log.Warning("[TEST] No aetherytes found in this territory!");
}
else
{
Log.Information("========================================");
Log.Information("[TEST] NEAREST AETHERYTE: " + nearestName);
Log.Information($"[TEST] Distance (2D): {minDistance:F2} yalms");
Log.Information($"[TEST] Aetheryte ID: {nearestId}");
Log.Information("========================================");
}
Log.Information("========================================");
}
private float ConvertMapCoordinateToRawPosition(int coordinate, ushort scale)
{
float c = (float)(int)scale / 100f;
return ((((float)coordinate - 1024f) / 2048f + 1f) * c / 2f - 1f) * (2048f / c) * 1000f / 2048f;
}
private void TestAlliedSociety() private void TestAlliedSociety()
{ {
Log.Information("========================================"); Log.Information("========================================");