using System.Collections.Generic; using System.Collections.ObjectModel; using System.Linq; using Dalamud.Game.ClientState.Conditions; using Dalamud.Game.ClientState.Objects.SubKinds; using Dalamud.Plugin.Services; using FFXIVClientStructs.FFXIV.Client.Game; using FFXIVClientStructs.FFXIV.Client.Game.Character; using FFXIVClientStructs.FFXIV.Component.GUI; using LLib.GameUI; using Lumina.Excel.Sheets; using Microsoft.Extensions.Logging; namespace LLib.GameData; public sealed class GameStateHelper { private readonly IDataManager _dataManager; private readonly IClientState _clientState; private readonly ICondition _condition; private readonly IObjectTable _objectTable; private readonly IGameGui _gameGui; private readonly ILogger _logger; private readonly ReadOnlyDictionary _territoryToAetherCurrentCompFlgSet; public GameStateHelper(IDataManager dataManager, IClientState clientState, ICondition condition, IObjectTable objectTable, IGameGui gameGui, ILogger logger) { _dataManager = dataManager; _clientState = clientState; _condition = condition; _objectTable = objectTable; _gameGui = gameGui; _logger = logger; _territoryToAetherCurrentCompFlgSet = (from x in dataManager.GetExcelSheet() where x.RowId != 0 where x.AetherCurrentCompFlgSet.RowId != 0 select x).ToDictionary((TerritoryType x) => x.RowId, (TerritoryType x) => x.AetherCurrentCompFlgSet.RowId).AsReadOnly(); } public bool IsFlyingUnlocked(uint territoryId) { if (_territoryToAetherCurrentCompFlgSet.TryGetValue(territoryId, out var value)) { return PlayerStateHelper.IsAetherCurrentZoneComplete(value); } return false; } public bool IsFlyingUnlockedInCurrentZone() { return IsFlyingUnlocked(_clientState.TerritoryType); } public unsafe ushort? GetMountId() { IPlayerCharacter localPlayer = _objectTable.LocalPlayer; if (localPlayer == null) { return null; } BattleChara* address = (BattleChara*)localPlayer.Address; if (address == null || address->Mount.MountId == 0) { return null; } return address->Mount.MountId; } public unsafe bool HasStatusPreventingMount() { if (_condition[ConditionFlag.Swimming] && !IsFlyingUnlockedInCurrentZone()) { return true; } if (!PlayerStateHelper.IsAnyMountUnlocked()) { return true; } IPlayerCharacter localPlayer = _objectTable.LocalPlayer; if (localPlayer == null) { return false; } BattleChara* address = (BattleChara*)localPlayer.Address; StatusManager* statusManager = address->GetStatusManager(); if (statusManager->HasStatus(1151u) || statusManager->HasStatus(1945u)) { return true; } return HasStatusPreventingMountOrSprint(); } public bool HasStatusPreventingSprint() { return HasStatusPreventingMountOrSprint(); } public unsafe bool Mount(uint preferredMountId = 0u) { if (_condition[ConditionFlag.Mounted]) { return true; } if (HasStatusPreventingMount()) { _logger.LogDebug("Cannot mount: status or condition prevents it"); return false; } if (preferredMountId != 0 && PlayerStateHelper.IsMountUnlocked(preferredMountId)) { if (ActionManager.Instance()->GetActionStatus(ActionType.Mount, preferredMountId, 3758096384uL, checkRecastActive: true, checkCastingActive: true, null) == 0) { _logger.LogDebug("Attempting preferred mount {Id}", preferredMountId); if (ActionManager.Instance()->UseAction(ActionType.Mount, preferredMountId, 3758096384uL, 0u, ActionManager.UseActionMode.None, 0u, null)) { _logger.LogInformation("Mounted (preferred {Id})", preferredMountId); return true; } } return false; } if (ActionManager.Instance()->GetActionStatus(ActionType.GeneralAction, 9u, 3758096384uL, checkRecastActive: true, checkCastingActive: true, null) == 0) { _logger.LogDebug("Attempting mount roulette"); if (ActionManager.Instance()->UseAction(ActionType.GeneralAction, 9u, 3758096384uL, 0u, ActionManager.UseActionMode.None, 0u, null)) { _logger.LogInformation("Mounted (roulette)"); return true; } } return false; } public unsafe bool Unmount() { if (!_condition[ConditionFlag.Mounted]) { return true; } if (ActionManager.Instance()->GetActionStatus(ActionType.GeneralAction, 23u, 3758096384uL, checkRecastActive: true, checkCastingActive: true, null) != 0) { _logger.LogDebug("Cannot unmount: action on cooldown"); return false; } _logger.LogDebug("Attempting dismount"); if (ActionManager.Instance()->UseAction(ActionType.GeneralAction, 23u, 3758096384uL, 0u, ActionManager.UseActionMode.None, 0u, null)) { _logger.LogInformation("Unmounted"); return true; } return false; } public unsafe int? GetFreeInventorySlots() { InventoryManager* ptr = InventoryManager.Instance(); if (ptr == null) { return null; } int num = 0; for (InventoryType inventoryType = InventoryType.Inventory1; inventoryType <= InventoryType.Inventory4; inventoryType++) { InventoryContainer* inventoryContainer = ptr->GetInventoryContainer(inventoryType); if (inventoryContainer == null) { return null; } for (int i = 0; i < inventoryContainer->Size; i++) { InventoryItem* inventorySlot = inventoryContainer->GetInventorySlot(i); if (inventorySlot == null || inventorySlot->ItemId == 0) { num++; } } } return num; } public uint GetItemLevel(uint itemId) { return _dataManager.GetExcelSheet().GetRowOrDefault(itemId)?.LevelItem.RowId ?? 0; } public unsafe bool IsLoadingScreenVisible() { if (_gameGui.TryGetAddonByName("FadeMiddle", out var addonPtr) && LAddon.IsAddonReady(addonPtr) && addonPtr->IsVisible) { return true; } if (_gameGui.TryGetAddonByName("FadeBack", out addonPtr) && LAddon.IsAddonReady(addonPtr) && addonPtr->IsVisible) { return true; } if (_gameGui.TryGetAddonByName("NowLoading", out addonPtr) && LAddon.IsAddonReady(addonPtr) && addonPtr->IsVisible) { return true; } return false; } private unsafe bool HasStatusPreventingMountOrSprint() { IPlayerCharacter localPlayer = _objectTable.LocalPlayer; if (localPlayer == null) { return false; } BattleChara* address = (BattleChara*)localPlayer.Address; StatusManager* statusManager = address->GetStatusManager(); if (!statusManager->HasStatus(565u) && !statusManager->HasStatus(404u) && !statusManager->HasStatus(416u) && !statusManager->HasStatus(2729u)) { return statusManager->HasStatus(2730u); } return true; } }