Questionable/Questionable/GameFunctions.cs

688 lines
26 KiB
C#
Raw Normal View History

2024-05-25 21:51:37 +00:00
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
2024-07-15 22:18:10 +00:00
using System.Diagnostics.CodeAnalysis;
2024-05-25 21:51:37 +00:00
using System.Linq;
2024-05-26 19:45:26 +00:00
using System.Numerics;
2024-05-27 19:54:34 +00:00
using Dalamud.Game.ClientState.Conditions;
2024-05-26 19:45:26 +00:00
using Dalamud.Game.ClientState.Objects;
2024-07-03 19:00:04 +00:00
using Dalamud.Game.ClientState.Objects.Types;
2024-06-19 11:00:14 +00:00
using Dalamud.Memory;
2024-05-25 21:51:37 +00:00
using Dalamud.Plugin.Services;
2024-06-06 16:49:49 +00:00
using Dalamud.Utility;
using FFXIVClientStructs.FFXIV.Application.Network.WorkDefinitions;
2024-05-25 21:51:37 +00:00
using FFXIVClientStructs.FFXIV.Client.Game;
2024-05-26 19:45:26 +00:00
using FFXIVClientStructs.FFXIV.Client.Game.Control;
2024-05-27 19:54:34 +00:00
using FFXIVClientStructs.FFXIV.Client.Game.Object;
2024-05-25 21:51:37 +00:00
using FFXIVClientStructs.FFXIV.Client.Game.UI;
using FFXIVClientStructs.FFXIV.Client.UI.Agent;
using FFXIVClientStructs.FFXIV.Component.GUI;
using LLib.GameUI;
using Lumina.Excel.CustomSheets;
2024-06-12 16:03:48 +00:00
using Lumina.Excel.GeneratedSheets2;
2024-06-08 19:16:57 +00:00
using Microsoft.Extensions.Logging;
using Questionable.Controller;
using Questionable.Data;
using Questionable.Model;
2024-05-26 19:45:26 +00:00
using Questionable.Model.V1;
2024-07-12 21:58:48 +00:00
using Action = Lumina.Excel.GeneratedSheets2.Action;
2024-05-27 19:54:34 +00:00
using BattleChara = FFXIVClientStructs.FFXIV.Client.Game.Character.BattleChara;
2024-06-12 16:03:48 +00:00
using ContentFinderCondition = Lumina.Excel.GeneratedSheets.ContentFinderCondition;
using ContentTalk = Lumina.Excel.GeneratedSheets.ContentTalk;
using EventPathMove = Lumina.Excel.GeneratedSheets.EventPathMove;
using GrandCompany = FFXIVClientStructs.FFXIV.Client.UI.Agent.GrandCompany;
using ObjectKind = Dalamud.Game.ClientState.Objects.Enums.ObjectKind;
using Quest = Questionable.Model.Quest;
2024-06-12 16:03:48 +00:00
using TerritoryType = Lumina.Excel.GeneratedSheets.TerritoryType;
2024-05-25 21:51:37 +00:00
namespace Questionable;
internal sealed unsafe class GameFunctions
{
private readonly ReadOnlyDictionary<ushort, byte> _territoryToAetherCurrentCompFlgSet;
2024-06-01 12:30:20 +00:00
private readonly ReadOnlyDictionary<uint, ushort> _contentFinderConditionToContentId;
2024-05-25 21:51:37 +00:00
private readonly IDataManager _dataManager;
2024-05-26 19:45:26 +00:00
private readonly IObjectTable _objectTable;
private readonly ITargetManager _targetManager;
2024-05-27 19:54:34 +00:00
private readonly ICondition _condition;
2024-05-31 23:26:46 +00:00
private readonly IClientState _clientState;
2024-06-08 19:16:57 +00:00
private readonly QuestRegistry _questRegistry;
private readonly QuestData _questData;
private readonly IGameGui _gameGui;
2024-06-12 16:03:48 +00:00
private readonly Configuration _configuration;
2024-06-08 19:16:57 +00:00
private readonly ILogger<GameFunctions> _logger;
2024-05-26 19:45:26 +00:00
public GameFunctions(IDataManager dataManager,
IObjectTable objectTable,
ITargetManager targetManager,
ICondition condition,
IClientState clientState,
QuestRegistry questRegistry,
QuestData questData,
IGameGui gameGui,
Configuration configuration,
ILogger<GameFunctions> logger)
2024-05-25 21:51:37 +00:00
{
_dataManager = dataManager;
2024-05-26 19:45:26 +00:00
_objectTable = objectTable;
_targetManager = targetManager;
2024-05-27 19:54:34 +00:00
_condition = condition;
2024-05-31 23:26:46 +00:00
_clientState = clientState;
2024-06-08 19:16:57 +00:00
_questRegistry = questRegistry;
_questData = questData;
_gameGui = gameGui;
2024-06-12 16:03:48 +00:00
_configuration = configuration;
2024-06-08 19:16:57 +00:00
_logger = logger;
2024-05-25 21:51:37 +00:00
_territoryToAetherCurrentCompFlgSet = dataManager.GetExcelSheet<TerritoryType>()!
.Where(x => x.RowId > 0)
.Where(x => x.Unknown32 > 0)
.ToDictionary(x => (ushort)x.RowId, x => x.Unknown32)
.AsReadOnly();
2024-06-01 20:01:50 +00:00
_contentFinderConditionToContentId = dataManager.GetExcelSheet<ContentFinderCondition>()!
2024-06-01 12:30:20 +00:00
.Where(x => x.RowId > 0 && x.Content > 0)
.ToDictionary(x => x.RowId, x => x.Content)
.AsReadOnly();
2024-05-25 21:51:37 +00:00
}
public DateTime ReturnRequestedAt { get; set; } = DateTime.MinValue;
2024-05-26 19:45:26 +00:00
public (ushort CurrentQuest, byte Sequence) GetCurrentQuest()
{
var (currentQuest, sequence) = GetCurrentQuestInternal();
QuestManager* questManager = QuestManager.Instance();
PlayerState* playerState = PlayerState.Instance();
if (currentQuest == 0)
{
if (_clientState.TerritoryType == 181) // Starting in Limsa
return (107, 0);
if (_clientState.TerritoryType == 183) // Starting in Gridania
return (39, 0);
return default;
}
else if (currentQuest == 681)
{
// if we have already picked up the GC quest, just return the progress for it
if (questManager->IsQuestAccepted(currentQuest) || QuestManager.IsQuestComplete(currentQuest))
return (currentQuest, sequence);
// The company you keep...
return _configuration.General.GrandCompany switch
{
GrandCompany.TwinAdder => (680, 0),
GrandCompany.Maelstrom => (681, 0),
_ => default
};
}
else if (currentQuest == 3856 && !playerState->IsMountUnlocked(1)) // we come in peace
{
ushort chocoboQuest = (GrandCompany)playerState->GrandCompany switch
{
GrandCompany.TwinAdder => 700,
GrandCompany.Maelstrom => 701,
_ => 0
};
if (chocoboQuest != 0 && !QuestManager.IsQuestComplete(chocoboQuest))
return (chocoboQuest, QuestManager.GetQuestSequence(chocoboQuest));
}
return (currentQuest, sequence);
}
public (ushort CurrentQuest, byte Sequence) GetCurrentQuestInternal()
2024-05-25 21:51:37 +00:00
{
var questManager = QuestManager.Instance();
if (questManager != null)
2024-05-25 21:51:37 +00:00
{
// always prioritize accepting MSQ quests, to make sure we don't turn in one MSQ quest and then go off to do
// side quests until the end of time.
2024-06-19 11:00:14 +00:00
var msqQuest = GetMainScenarioQuest(questManager);
if (msqQuest.CurrentQuest != 0 && !questManager->IsQuestAccepted(msqQuest.CurrentQuest))
return msqQuest;
// Use the quests in the same order as they're shown in the to-do list, e.g. if the MSQ is the first item,
// do the MSQ; if a side quest is the first item do that side quest.
//
// If no quests are marked as 'priority', accepting a new quest adds it to the top of the list.
2024-07-03 19:00:04 +00:00
for (int i = questManager->TrackedQuests.Length - 1; i >= 0; --i)
{
ushort currentQuest;
2024-07-03 19:00:04 +00:00
var trackedQuest = questManager->TrackedQuests[i];
switch (trackedQuest.QuestType)
{
default:
continue;
case 1: // normal quest
2024-07-03 19:00:04 +00:00
currentQuest = questManager->NormalQuests[trackedQuest.Index].QuestId;
break;
}
2024-06-08 19:16:57 +00:00
if (_questRegistry.IsKnownQuest(currentQuest))
return (currentQuest, QuestManager.GetQuestSequence(currentQuest));
}
// if we know no quest of those currently in the to-do list, just do MSQ
return msqQuest;
2024-05-25 21:51:37 +00:00
}
return default;
}
2024-06-19 11:00:14 +00:00
private (ushort CurrentQuest, byte Sequence) GetMainScenarioQuest(QuestManager* questManager)
{
2024-06-19 11:00:14 +00:00
if (QuestManager.IsQuestComplete(3759)) // Memories Rekindled
{
AgentInterface* questRedoHud = AgentModule.Instance()->GetAgentByInternalId(AgentId.QuestRedoHud);
if (questRedoHud != null && questRedoHud->IsAgentActive())
{
// there's surely better ways to check this, but the one in the OOB Plugin was even less reliable
if (_gameGui.TryGetAddonByName<AtkUnitBase>("QuestRedoHud", out var addon) &&
addon->AtkValuesCount == 4 &&
// 0 seems to be active,
// 1 seems to be paused,
// 2 is unknown, but it happens e.g. before the quest 'Alzadaal's Legacy'
// 3 seems to be having /ng+ open while active,
// 4 seems to be when (a) suspending the chapter, or (b) having turned in a quest
addon->AtkValues[0].UInt is 0 or 2 or 3 or 4)
{
// redoHud+44 is chapter
// redoHud+46 is quest
ushort questId = MemoryHelper.Read<ushort>((nint)questRedoHud + 46);
return (questId, QuestManager.GetQuestSequence(questId));
}
}
}
var scenarioTree = AgentScenarioTree.Instance();
if (scenarioTree == null)
return default;
2024-05-25 21:51:37 +00:00
if (scenarioTree->Data == null)
return default;
2024-05-25 21:51:37 +00:00
ushort currentQuest = scenarioTree->Data->CurrentScenarioQuest;
2024-05-25 21:51:37 +00:00
if (currentQuest == 0)
return default;
// if the MSQ is hidden, we generally ignore it
if (questManager->IsQuestAccepted(currentQuest) && questManager->GetQuestById(currentQuest)->IsHidden)
return default;
// if we're not at a high enough level to continue, we also ignore it
var currentLevel = _clientState.LocalPlayer?.Level ?? 0;
if (currentLevel != 0 &&
_questRegistry.TryGetQuest(currentQuest, out Quest? quest)
&& quest.Info.Level > currentLevel)
return default;
return (currentQuest, QuestManager.GetQuestSequence(currentQuest));
}
public QuestWork? GetQuestEx(ushort questId)
{
QuestWork* questWork = QuestManager.Instance()->GetQuestById(questId);
return questWork != null ? *questWork : null;
2024-05-26 19:45:26 +00:00
}
public bool IsReadyToAcceptQuest(ushort questId)
{
_questRegistry.TryGetQuest(questId, out var quest);
if (quest is { Info.IsRepeatable: true })
{
if (IsQuestAccepted(questId))
return false;
}
else
{
if (IsQuestAcceptedOrComplete(questId))
return false;
}
// if we're not at a high enough level to continue, we also ignore it
var currentLevel = _clientState.LocalPlayer?.Level ?? 0;
if (currentLevel != 0 && quest != null && quest.Info.Level > currentLevel)
return false;
return true;
}
public bool IsQuestAcceptedOrComplete(ushort questId)
{
return IsQuestComplete(questId) || IsQuestAccepted(questId);
}
public bool IsQuestAccepted(ushort questId)
{
QuestManager* questManager = QuestManager.Instance();
return questManager->IsQuestAccepted(questId);
}
2024-07-15 22:18:10 +00:00
[SuppressMessage("Performance", "CA1822")]
public bool IsQuestComplete(ushort questId)
{
return QuestManager.IsQuestComplete(questId);
}
public bool IsQuestLocked(ushort questId, ushort? extraCompletedQuest = null)
{
var questInfo = _questData.GetQuestInfo(questId);
if (questInfo.QuestLocks.Count > 0)
{
var completedQuests = questInfo.QuestLocks.Count(x => IsQuestComplete(x) || x == extraCompletedQuest);
if (questInfo.QuestLockJoin == QuestInfo.QuestJoin.All && questInfo.QuestLocks.Count == completedQuests)
return true;
else if (questInfo.QuestLockJoin == QuestInfo.QuestJoin.AtLeastOne && completedQuests > 0)
return true;
}
if (questInfo.PreviousQuests.Count > 0)
{
var completedQuests = questInfo.PreviousQuests.Count(x => IsQuestComplete(x) || x == extraCompletedQuest);
if (questInfo.PreviousQuestJoin == QuestInfo.QuestJoin.All &&
questInfo.PreviousQuests.Count == completedQuests)
return false;
else if (questInfo.PreviousQuestJoin == QuestInfo.QuestJoin.AtLeastOne && completedQuests > 0)
return false;
else
return true;
}
return false;
}
2024-05-26 19:45:26 +00:00
public bool IsAetheryteUnlocked(uint aetheryteId, out byte subIndex)
{
subIndex = 0;
var uiState = UIState.Instance();
return uiState != null && uiState->IsAetheryteUnlocked(aetheryteId);
2024-05-26 19:45:26 +00:00
}
public bool IsAetheryteUnlocked(EAetheryteLocation aetheryteLocation)
=> IsAetheryteUnlocked((uint)aetheryteLocation, out _);
public bool CanTeleport(EAetheryteLocation aetheryteLocation)
{
if ((ushort)aetheryteLocation == PlayerState.Instance()->HomeAetheryteId &&
ActionManager.Instance()->GetActionStatus(ActionType.GeneralAction, 8) == 0)
return true;
return ActionManager.Instance()->GetActionStatus(ActionType.Action, 5) == 0;
}
2024-05-26 19:45:26 +00:00
public bool TeleportAetheryte(uint aetheryteId)
{
_logger.LogDebug("Attempting to teleport to aetheryte {AetheryteId}", aetheryteId);
2024-05-26 19:45:26 +00:00
if (IsAetheryteUnlocked(aetheryteId, out var subIndex))
{
if (aetheryteId == PlayerState.Instance()->HomeAetheryteId &&
ActionManager.Instance()->GetActionStatus(ActionType.GeneralAction, 8) == 0)
{
ReturnRequestedAt = DateTime.Now;
if (ActionManager.Instance()->UseAction(ActionType.GeneralAction, 8))
{
_logger.LogInformation("Using 'return' for home aetheryte");
return true;
}
}
if (ActionManager.Instance()->GetActionStatus(ActionType.Action, 5) == 0)
{
// fallback if return isn't available or (more likely) on a different aetheryte
_logger.LogInformation("Teleporting to aetheryte {AetheryteId}", aetheryteId);
return Telepo.Instance()->Teleport(aetheryteId, subIndex);
}
2024-05-26 19:45:26 +00:00
}
return false;
2024-05-25 21:51:37 +00:00
}
2024-05-26 19:45:26 +00:00
public bool TeleportAetheryte(EAetheryteLocation aetheryteLocation)
=> TeleportAetheryte((uint)aetheryteLocation);
2024-05-25 21:51:37 +00:00
public bool IsFlyingUnlocked(ushort territoryId)
{
if (_configuration.Advanced.NeverFly)
return false;
2024-07-17 18:40:38 +00:00
if (IsQuestAccepted(3304) && _condition[ConditionFlag.Mounted])
{
BattleChara* battleChara = (BattleChara*)(_clientState.LocalPlayer?.Address ?? 0);
if (battleChara != null && battleChara->Mount.MountId == 198) // special quest amaro, not the normal one
return true;
}
2024-05-25 21:51:37 +00:00
var playerState = PlayerState.Instance();
return playerState != null &&
_territoryToAetherCurrentCompFlgSet.TryGetValue(territoryId, out byte aetherCurrentCompFlgSet) &&
playerState->IsAetherCurrentZoneComplete(aetherCurrentCompFlgSet);
}
2024-06-08 19:16:57 +00:00
public bool IsFlyingUnlockedInCurrentZone() => IsFlyingUnlocked(_clientState.TerritoryType);
2024-05-27 19:54:34 +00:00
public bool IsAetherCurrentUnlocked(uint aetherCurrentId)
{
var playerState = PlayerState.Instance();
return playerState != null &&
playerState->IsAetherCurrentUnlocked(aetherCurrentId);
}
public IGameObject? FindObjectByDataId(uint dataId, ObjectKind? kind = null)
2024-05-26 19:45:26 +00:00
{
foreach (var gameObject in _objectTable)
{
if (gameObject.ObjectKind is ObjectKind.Player or ObjectKind.Companion or ObjectKind.MountType
or ObjectKind.Retainer or ObjectKind.Housing)
continue;
if (gameObject.DataId == dataId && (kind == null || kind.Value == gameObject.ObjectKind))
2024-05-26 19:45:26 +00:00
{
2024-05-27 19:54:34 +00:00
return gameObject;
2024-05-26 19:45:26 +00:00
}
}
2024-05-27 19:54:34 +00:00
2024-06-08 19:16:57 +00:00
_logger.LogWarning("Could not find GameObject with dataId {DataId}", dataId);
2024-05-27 19:54:34 +00:00
return null;
}
public bool InteractWith(uint dataId, ObjectKind? kind = null)
2024-05-27 19:54:34 +00:00
{
IGameObject? gameObject = FindObjectByDataId(dataId, kind);
2024-05-27 19:54:34 +00:00
if (gameObject != null)
{
2024-07-03 19:00:04 +00:00
_logger.LogInformation("Setting target with {DataId} to {ObjectId}", dataId, gameObject.EntityId);
_targetManager.Target = null;
2024-05-27 19:54:34 +00:00
_targetManager.Target = gameObject;
2024-07-03 19:00:04 +00:00
long result = (long)TargetSystem.Instance()->InteractWithObject((GameObject*)gameObject.Address, false);
_logger.LogInformation("Interact result: {Result}", result);
return result != 7 && result > 0;
2024-05-27 19:54:34 +00:00
}
_logger.LogDebug("Game object is null");
return false;
2024-05-27 19:54:34 +00:00
}
2024-06-12 16:03:48 +00:00
public bool UseItem(uint itemId)
{
long result = AgentInventoryContext.Instance()->UseItem(itemId);
_logger.LogInformation("UseItem result: {Result}", result);
return result == 0;
}
2024-06-12 16:03:48 +00:00
public bool UseItem(uint dataId, uint itemId)
2024-05-27 19:54:34 +00:00
{
2024-07-03 19:00:04 +00:00
IGameObject? gameObject = FindObjectByDataId(dataId);
2024-05-27 19:54:34 +00:00
if (gameObject != null)
{
_targetManager.Target = gameObject;
long result = AgentInventoryContext.Instance()->UseItem(itemId);
_logger.LogInformation("UseItem result on {DataId}: {Result}", dataId, result);
return result == 0;
2024-05-27 19:54:34 +00:00
}
2024-06-12 16:03:48 +00:00
return false;
2024-05-27 19:54:34 +00:00
}
2024-06-12 16:03:48 +00:00
public bool UseItemOnGround(uint dataId, uint itemId)
2024-05-28 22:17:19 +00:00
{
2024-07-03 19:00:04 +00:00
IGameObject? gameObject = FindObjectByDataId(dataId);
2024-05-28 22:17:19 +00:00
if (gameObject != null)
{
2024-07-03 19:00:04 +00:00
Vector3 position = gameObject.Position;
2024-06-12 16:03:48 +00:00
return ActionManager.Instance()->UseActionLocation(ActionType.KeyItem, itemId, location: &position);
2024-05-28 22:17:19 +00:00
}
2024-06-12 16:03:48 +00:00
return false;
2024-05-28 22:17:19 +00:00
}
2024-07-12 21:58:48 +00:00
public bool UseAction(EAction action)
{
if (ActionManager.Instance()->GetActionStatus(ActionType.Action, (uint)action) == 0)
{
bool result = ActionManager.Instance()->UseAction(ActionType.Action, (uint)action);
_logger.LogInformation("UseAction {Action} result: {Result}", action, result);
return result;
}
return false;
}
public bool UseAction(IGameObject gameObject, EAction action)
{
2024-07-12 21:58:48 +00:00
var actionRow = _dataManager.GetExcelSheet<Action>()!.GetRow((uint)action)!;
if (!ActionManager.CanUseActionOnTarget((uint)action, (GameObject*)gameObject.Address))
{
_logger.LogWarning("Can not use action {Action} on target {Target}", action, gameObject);
return false;
}
_targetManager.Target = gameObject;
if (ActionManager.Instance()->GetActionStatus(ActionType.Action, (uint)action, gameObject.GameObjectId) == 0)
{
2024-07-12 21:58:48 +00:00
bool result;
if (actionRow.TargetArea)
{
Vector3 position = gameObject.Position;
result = ActionManager.Instance()->UseActionLocation(ActionType.Action, (uint)action,
location: &position);
_logger.LogInformation("UseAction {Action} on target area {Target} result: {Result}", action,
gameObject,
2024-07-12 21:58:48 +00:00
result);
}
else
{
2024-07-12 21:58:48 +00:00
result = ActionManager.Instance()->UseAction(ActionType.Action, (uint)action, gameObject.GameObjectId);
_logger.LogInformation("UseAction {Action} on target {Target} result: {Result}", action, gameObject,
result);
}
return result;
}
return false;
}
public bool IsObjectAtPosition(uint dataId, Vector3 position, float distance)
2024-05-27 19:54:34 +00:00
{
2024-07-03 19:00:04 +00:00
IGameObject? gameObject = FindObjectByDataId(dataId);
return gameObject != null && (gameObject.Position - position).Length() < distance;
2024-05-27 19:54:34 +00:00
}
public bool HasStatusPreventingMount()
2024-05-27 19:54:34 +00:00
{
2024-06-08 19:16:57 +00:00
if (_condition[ConditionFlag.Swimming] && !IsFlyingUnlockedInCurrentZone())
2024-05-31 23:26:46 +00:00
return true;
// company chocobo is locked
var playerState = PlayerState.Instance();
if (playerState != null && !playerState->IsMountUnlocked(1))
return true;
return HasCharacterStatusPreventingMountOrSprint();
}
public bool HasStatusPreventingSprint() => HasCharacterStatusPreventingMountOrSprint();
private bool HasCharacterStatusPreventingMountOrSprint()
{
2024-07-03 19:00:04 +00:00
var localPlayer = _clientState.LocalPlayer;
if (localPlayer == null)
return false;
2024-05-27 19:54:34 +00:00
2024-07-03 19:00:04 +00:00
var battleChara = (BattleChara*)localPlayer.Address;
StatusManager* statusManager = battleChara->GetStatusManager();
return statusManager->HasStatus(565) ||
statusManager->HasStatus(404) ||
statusManager->HasStatus(2729) ||
statusManager->HasStatus(2730);
2024-05-27 19:54:34 +00:00
}
public bool Mount()
{
if (_condition[ConditionFlag.Mounted])
return true;
var playerState = PlayerState.Instance();
2024-06-12 16:03:48 +00:00
if (playerState != null && _configuration.General.MountId != 0 &&
playerState->IsMountUnlocked(_configuration.General.MountId))
{
2024-06-12 16:03:48 +00:00
if (ActionManager.Instance()->GetActionStatus(ActionType.Mount, _configuration.General.MountId) == 0)
{
_logger.LogDebug("Attempting to use preferred mount...");
2024-06-12 16:03:48 +00:00
if (ActionManager.Instance()->UseAction(ActionType.Mount, _configuration.General.MountId))
{
2024-06-12 16:03:48 +00:00
_logger.LogInformation("Using preferred mount");
return true;
}
return false;
}
}
else
{
if (ActionManager.Instance()->GetActionStatus(ActionType.GeneralAction, 9) == 0)
{
_logger.LogDebug("Attempting to use mount roulette...");
if (ActionManager.Instance()->UseAction(ActionType.GeneralAction, 9))
{
_logger.LogInformation("Using mount roulette");
return true;
}
return false;
}
}
return false;
}
2024-05-27 19:54:34 +00:00
public bool Unmount()
{
if (!_condition[ConditionFlag.Mounted])
2024-06-10 17:56:13 +00:00
return true;
2024-05-27 19:54:34 +00:00
if (ActionManager.Instance()->GetActionStatus(ActionType.GeneralAction, 23) == 0)
{
_logger.LogDebug("Attempting to unmount...");
if (ActionManager.Instance()->UseAction(ActionType.GeneralAction, 23))
{
_logger.LogInformation("Unmounted");
return true;
}
return false;
2024-05-27 19:54:34 +00:00
}
else
2024-06-10 17:56:13 +00:00
{
_logger.LogWarning("Can't unmount right now?");
2024-06-10 17:56:13 +00:00
return false;
}
2024-05-26 19:45:26 +00:00
}
2024-06-01 12:30:20 +00:00
public void OpenDutyFinder(uint contentFinderConditionId)
{
if (_contentFinderConditionToContentId.TryGetValue(contentFinderConditionId, out ushort contentId))
{
if (UIState.IsInstanceContentUnlocked(contentId))
AgentContentsFinder.Instance()->OpenRegularDuty(contentFinderConditionId);
else
2024-06-08 19:16:57 +00:00
_logger.LogError(
"Trying to access a locked duty (cf: {ContentFinderId}, content: {ContentId})",
contentFinderConditionId, contentId);
2024-06-01 12:30:20 +00:00
}
else
_logger.LogError("Could not find content for content finder condition (cf: {ContentFinderId})",
contentFinderConditionId);
2024-06-01 12:30:20 +00:00
}
2024-06-06 16:49:49 +00:00
public string? GetDialogueText(Quest currentQuest, string? excelSheetName, string key)
{
if (excelSheetName == null)
{
var questRow =
_dataManager.GetExcelSheet<Lumina.Excel.GeneratedSheets2.Quest>()!.GetRow((uint)currentQuest.QuestId +
0x10000);
if (questRow == null)
{
2024-06-08 19:16:57 +00:00
_logger.LogError("Could not find quest row for {QuestId}", currentQuest.QuestId);
return null;
}
excelSheetName = $"quest/{(currentQuest.QuestId / 100):000}/{questRow.Id}";
}
var excelSheet = _dataManager.Excel.GetSheet<QuestDialogueText>(excelSheetName);
if (excelSheet == null)
{
2024-06-08 19:16:57 +00:00
_logger.LogError("Unknown excel sheet '{SheetName}'", excelSheetName);
return null;
}
2024-06-06 16:49:49 +00:00
return excelSheet.FirstOrDefault(x => x.Key == key)?.Value?.ToDalamudString().ToString();
}
2024-06-12 16:03:48 +00:00
public string? GetDialogueTextByRowId(string? excelSheet, uint rowId)
2024-06-06 16:49:49 +00:00
{
2024-06-12 16:03:48 +00:00
if (excelSheet == "GimmickYesNo")
{
var questRow = _dataManager.GetExcelSheet<GimmickYesNo>()!.GetRow(rowId);
return questRow?.Unknown0?.ToString();
}
else if (excelSheet == "Warp")
{
var questRow = _dataManager.GetExcelSheet<Warp>()!.GetRow(rowId);
return questRow?.Name?.ToString();
}
else if (excelSheet is "Addon")
{
var questRow = _dataManager.GetExcelSheet<Addon>()!.GetRow(rowId);
return questRow?.Text?.ToString();
}
else if (excelSheet is "EventPathMove")
{
var questRow = _dataManager.GetExcelSheet<EventPathMove>()!.GetRow(rowId);
return questRow?.Unknown10?.ToString();
}
2024-06-12 16:03:48 +00:00
else if (excelSheet is "ContentTalk" or null)
{
var questRow = _dataManager.GetExcelSheet<ContentTalk>()!.GetRow(rowId);
return questRow?.Text?.ToString();
}
else
throw new ArgumentOutOfRangeException(nameof(excelSheet), $"Unsupported excel sheet {excelSheet}");
}
public bool IsOccupied()
{
if (!_clientState.IsLoggedIn || _clientState.LocalPlayer == null)
return true;
if (IsLoadingScreenVisible())
return true;
return _condition[ConditionFlag.Occupied] || _condition[ConditionFlag.Occupied30] ||
_condition[ConditionFlag.Occupied33] || _condition[ConditionFlag.Occupied38] ||
_condition[ConditionFlag.Occupied39] || _condition[ConditionFlag.OccupiedInEvent] ||
_condition[ConditionFlag.OccupiedInQuestEvent] || _condition[ConditionFlag.OccupiedInCutSceneEvent] ||
_condition[ConditionFlag.Casting] || _condition[ConditionFlag.Unknown57] ||
_condition[ConditionFlag.BetweenAreas] || _condition[ConditionFlag.BetweenAreas51] ||
_condition[ConditionFlag.Jumping61];
}
public bool IsLoadingScreenVisible()
{
return _gameGui.TryGetAddonByName("FadeMiddle", out AtkUnitBase* fade) &&
LAddon.IsAddonReady(fade) &&
fade->IsVisible;
}
2024-05-25 21:51:37 +00:00
}