1
0
Fork 0
forked from aly/qstbak
qstbak/Questionable/Questionable.Controller.Utils/PartyWatchDog.cs
2026-08-17 20:29:32 +10:00

166 lines
5.7 KiB
C#

using System;
using Dalamud.Plugin.Services;
using FFXIVClientStructs.FFXIV.Client.Game;
using FFXIVClientStructs.FFXIV.Client.Game.Group;
using LLib.GameData;
using Microsoft.Extensions.Logging;
using Questionable.Data;
using Questionable.Model.Questing;
namespace Questionable.Controller.Utils;
internal sealed class PartyWatchdog : IDisposable
{
private readonly QuestController _questController;
private readonly Configuration _configuration;
private readonly TerritoryData _territoryData;
private readonly IClientState _clientState;
private readonly IChatGui _chatGui;
private readonly ILogger<PartyWatchdog> _logger;
private uint? _uncheckedTerritoryId;
public PartyWatchdog(QuestController questController, Configuration configuration, TerritoryData territoryData, IClientState clientState, IChatGui chatGui, ILogger<PartyWatchdog> logger)
{
_questController = questController;
_configuration = configuration;
_territoryData = territoryData;
_clientState = clientState;
_chatGui = chatGui;
_logger = logger;
_clientState.TerritoryChanged += TerritoryChanged;
}
private unsafe void TerritoryChanged(uint newTerritoryId)
{
if (!_configuration.Advanced.DisablePartyWatchdog)
{
switch ((ETerritoryIntendedUse)GameMain.Instance()->CurrentTerritoryIntendedUseId)
{
case ETerritoryIntendedUse.MordionGaol:
case ETerritoryIntendedUse.Frontline:
case ETerritoryIntendedUse.LordOfVerminion:
case ETerritoryIntendedUse.ExploratoryMissions:
case ETerritoryIntendedUse.CrystallineConflict:
case ETerritoryIntendedUse.DeepDungeon:
case ETerritoryIntendedUse.TreasureMapInstance:
case ETerritoryIntendedUse.TripleTriadBattlehall:
case ETerritoryIntendedUse.CrystallineConflictCustomMatch:
case ETerritoryIntendedUse.DiademHuntingGrounds:
case ETerritoryIntendedUse.RivalWings:
case ETerritoryIntendedUse.Eureka:
case ETerritoryIntendedUse.LeapOfFaith:
case ETerritoryIntendedUse.OceanFishing:
case ETerritoryIntendedUse.Diadem:
case ETerritoryIntendedUse.Bozja:
case ETerritoryIntendedUse.TripleTriadOpenTournament:
case ETerritoryIntendedUse.TripleTriadInvitationalParlor:
case ETerritoryIntendedUse.DelubrumReginae:
case ETerritoryIntendedUse.DelubrumReginaeSavage:
case ETerritoryIntendedUse.Blunderville:
StopIfRunning($"Unsupported Area entered ({newTerritoryId})");
break;
case ETerritoryIntendedUse.Dungeon:
case ETerritoryIntendedUse.VariantDungeon:
case ETerritoryIntendedUse.AllianceRaid:
case ETerritoryIntendedUse.Trial:
case ETerritoryIntendedUse.Raid1:
case ETerritoryIntendedUse.Raid2:
case ETerritoryIntendedUse.Seasonal:
case ETerritoryIntendedUse.TheCalamityRetold:
case ETerritoryIntendedUse.CriterionDungeon:
case ETerritoryIntendedUse.CriterionDungeonSavage:
_uncheckedTerritoryId = newTerritoryId;
_logger.LogDebug("Will check territory {TerritoryId} after loading", newTerritoryId);
break;
case ETerritoryIntendedUse.OpeningArea:
case ETerritoryIntendedUse.BeforeTrialDung:
case ETerritoryIntendedUse.PreEwOverworldQuestBattle:
case ETerritoryIntendedUse.Unknown11:
case ETerritoryIntendedUse.WaitingRoom:
case ETerritoryIntendedUse.HousingOutdoor:
case ETerritoryIntendedUse.HousingIndoor:
case ETerritoryIntendedUse.SoloOverworldInstances:
case ETerritoryIntendedUse.ChocoboSquareOld:
case ETerritoryIntendedUse.ChocoboRacing:
case ETerritoryIntendedUse.Firmament:
case ETerritoryIntendedUse.SanctumOfTheTwelve:
case ETerritoryIntendedUse.GoldSaucer:
case ETerritoryIntendedUse.OriginalStepsOfFaith:
case ETerritoryIntendedUse.HallOfTheNovice:
case ETerritoryIntendedUse.SoloDuty:
case ETerritoryIntendedUse.GrandCompanyBarracks:
case ETerritoryIntendedUse.SeasonalInstancedArea:
case ETerritoryIntendedUse.ChaoticRaid:
case ETerritoryIntendedUse.Unknown40:
case ETerritoryIntendedUse.Unknown42:
case ETerritoryIntendedUse.MaskedCarnival:
case ETerritoryIntendedUse.IslandSanctuary:
case ETerritoryIntendedUse.EndwalkerMsqSoloOverworld:
case ETerritoryIntendedUse.Unknown55:
case ETerritoryIntendedUse.Elysion:
break;
}
}
}
public unsafe void Update()
{
if (_configuration.Advanced.DisablePartyWatchdog || _uncheckedTerritoryId != _clientState.TerritoryType || GameMain.Instance()->TerritoryLoadState != 2)
{
return;
}
GroupManager* ptr = GroupManager.Instance();
if (ptr == null)
{
return;
}
byte memberCount = ptr->MainGroup.MemberCount;
bool isAlliance = ptr->MainGroup.IsAlliance;
_logger.LogDebug("Territory {TerritoryId} with {MemberCount} members, alliance: {IsInAlliance}", _uncheckedTerritoryId, memberCount, isAlliance);
if (memberCount > 1 || isAlliance)
{
if (!IsDutyConfiguredForParty(_uncheckedTerritoryId.Value))
{
StopIfRunning("Other party members present");
}
else
{
_logger.LogDebug("Party detected but duty is configured for Unsync (Party) mode, continuing");
}
}
_uncheckedTerritoryId = null;
}
private bool IsDutyConfiguredForParty(uint territoryId)
{
if (!_territoryData.TryGetContentFinderConditionIdForTerritory(territoryId, out var cfcId))
{
return false;
}
if (_configuration.Duties.DutyModeOverrides.TryGetValue(cfcId, out var value))
{
return value == EDutyMode.UnsyncParty;
}
return _configuration.Duties.DefaultDutyMode == EDutyMode.UnsyncParty;
}
private void StopIfRunning(string reason)
{
if (_questController.IsRunning || _questController.AutomationType != QuestController.EAutomationType.Manual)
{
_chatGui.PrintError("Stopping Questionable: " + reason + ". If you believe this to be correct, please restart Questionable manually.", "Questionable", 576);
_questController.Stop(reason);
}
}
public void Dispose()
{
_clientState.TerritoryChanged -= TerritoryChanged;
}
}