using System; using Dalamud.Game.ClientState.Conditions; using Dalamud.Plugin; using Dalamud.Plugin.Ipc; using Dalamud.Plugin.Ipc.Exceptions; using Dalamud.Plugin.Services; using Microsoft.Extensions.Logging; using Questionable.Controller; namespace Questionable.External; internal sealed class TextAdvanceIpc : IDisposable { public sealed class ExternalTerritoryConfig { public bool? EnableQuestAccept; public bool? EnableQuestComplete; public bool? EnableRewardPick; public bool? EnableRequestHandin; public bool? EnableCutsceneEsc; public bool? EnableCutsceneSkipConfirm; public bool? EnableTalkSkip; public bool? EnableRequestFill; public bool? EnableAutoInteract; } private bool _isExternalControlActivated; private long _ipcRetryAfter; private bool _lastCinemaModeState; private bool _lastInCutsceneState; private bool _lastSuppressRequestState; private readonly QuestController _questController; private readonly FateController _fateController; private readonly SeasonalDutyController _seasonalDutyController; private readonly AttunementController _attunementController; private readonly CustomDeliveryController _customDeliveryController; private readonly Configuration _configuration; private readonly ICondition _condition; private readonly IFramework _framework; private readonly ICallGateSubscriber _isInExternalControl; private readonly ICallGateSubscriber _enableExternalControl; private readonly ICallGateSubscriber _disableExternalControl; private readonly string _pluginName; private readonly ILogger _logger; public TextAdvanceIpc(IDalamudPluginInterface pluginInterface, IFramework framework, QuestController questController, FateController fateController, SeasonalDutyController seasonalDutyController, AttunementController attunementController, CustomDeliveryController customDeliveryController, Configuration configuration, ICondition condition, ILogger logger) { _framework = framework; _questController = questController; _fateController = fateController; _seasonalDutyController = seasonalDutyController; _attunementController = attunementController; _customDeliveryController = customDeliveryController; _configuration = configuration; _condition = condition; _logger = logger; _isInExternalControl = pluginInterface.GetIpcSubscriber("TextAdvance.IsInExternalControl"); _enableExternalControl = pluginInterface.GetIpcSubscriber("TextAdvance.EnableExternalControl"); _disableExternalControl = pluginInterface.GetIpcSubscriber("TextAdvance.DisableExternalControl"); _pluginName = pluginInterface.InternalName; _framework.Update += OnUpdate; } public void Dispose() { _framework.Update -= OnUpdate; if (_isExternalControlActivated) { try { _disableExternalControl.InvokeFunc(_pluginName); } catch (IpcError exception) { _logger.LogDebug(exception, "Failed to disable TextAdvance external control during dispose"); } } } private void OnUpdate(IFramework framework) { if (Environment.TickCount64 < _ipcRetryAfter) { return; } try { OnUpdateInner(); } catch (IpcError exception) { _logger.LogDebug(exception, "TextAdvance IPC call failed"); _ipcRetryAfter = Environment.TickCount64 + 30000; } } private void OnUpdateInner() { bool flag = _questController.IsRunning || _questController.AutomationType != QuestController.EAutomationType.Manual || _fateController.IsRunning || _seasonalDutyController.IsRunning || _attunementController.IsRunning || _customDeliveryController.IsRunning; if (!_configuration.General.ConfigureTextAdvance || !flag) { if (!_isExternalControlActivated) { return; } try { if (_disableExternalControl.InvokeFunc(_pluginName) || !_isInExternalControl.InvokeFunc()) { _isExternalControlActivated = false; } return; } catch (IpcError) { _isExternalControlActivated = false; return; } } bool cinemaMode = _configuration.General.CinemaMode; bool flag2 = _condition[ConditionFlag.OccupiedInCutSceneEvent] || _condition[ConditionFlag.WatchingCutscene] || _condition[ConditionFlag.WatchingCutscene78]; bool flag3 = _customDeliveryController.IsRunning && !_questController.IsRunning && _questController.AutomationType == QuestController.EAutomationType.Manual && !_fateController.IsRunning && !_seasonalDutyController.IsRunning && !_attunementController.IsRunning; bool flag4 = cinemaMode != _lastCinemaModeState || (cinemaMode && flag2 != _lastInCutsceneState) || flag3 != _lastSuppressRequestState; if (!_isExternalControlActivated) { if (!_isInExternalControl.InvokeFunc()) { ExternalTerritoryConfig arg = CreateExternalTerritoryConfig(cinemaMode, flag2, flag3); if (_enableExternalControl.InvokeFunc(_pluginName, arg)) { _isExternalControlActivated = true; _lastCinemaModeState = cinemaMode; _lastInCutsceneState = flag2; _lastSuppressRequestState = flag3; } } } else if (flag4) { ExternalTerritoryConfig arg2 = CreateExternalTerritoryConfig(cinemaMode, flag2, flag3); _enableExternalControl.InvokeFunc(_pluginName, arg2); _lastCinemaModeState = cinemaMode; _lastInCutsceneState = flag2; _lastSuppressRequestState = flag3; } } private static ExternalTerritoryConfig CreateExternalTerritoryConfig(bool cinemaMode, bool inCutscene, bool suppressRequestHandling) { bool flag = cinemaMode && inCutscene; return new ExternalTerritoryConfig { EnableQuestAccept = true, EnableQuestComplete = true, EnableRewardPick = true, EnableRequestHandin = !suppressRequestHandling, EnableCutsceneEsc = !cinemaMode, EnableCutsceneSkipConfirm = !cinemaMode, EnableTalkSkip = !flag, EnableRequestFill = !suppressRequestHandling, EnableAutoInteract = false }; } }