punish v6.8.18.0

This commit is contained in:
alydev 2025-10-09 07:47:19 +10:00
commit e786325cda
322 changed files with 554232 additions and 0 deletions

View file

@ -0,0 +1,543 @@
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Numerics;
using System.Runtime.InteropServices;
using Dalamud.Game;
using Dalamud.Game.ClientState.Conditions;
using Dalamud.Game.ClientState.Objects;
using Dalamud.Game.ClientState.Objects.Enums;
using Dalamud.Game.ClientState.Objects.SubKinds;
using Dalamud.Game.ClientState.Objects.Types;
using Dalamud.Plugin.Services;
using FFXIVClientStructs.FFXIV.Client.Game;
using FFXIVClientStructs.FFXIV.Client.Game.Character;
using FFXIVClientStructs.FFXIV.Client.Game.Control;
using FFXIVClientStructs.FFXIV.Client.Game.Object;
using FFXIVClientStructs.FFXIV.Client.Game.UI;
using FFXIVClientStructs.FFXIV.Client.UI.Agent;
using FFXIVClientStructs.FFXIV.Component.GUI;
using LLib.GameUI;
using Lumina.Excel.Sheets;
using Microsoft.Extensions.Logging;
using Questionable.Model;
using Questionable.Model.Questing;
namespace Questionable.Functions;
internal sealed class GameFunctions
{
private delegate void AbandonDutyDelegate(bool a1);
private static class Signatures
{
internal const string AbandonDuty = "E8 ?? ?? ?? ?? 41 B2 01 EB 39";
}
private readonly QuestFunctions _questFunctions;
private readonly IDataManager _dataManager;
private readonly IObjectTable _objectTable;
private readonly ITargetManager _targetManager;
private readonly ICondition _condition;
private readonly IClientState _clientState;
private readonly IGameGui _gameGui;
private readonly Configuration _configuration;
private readonly ILogger<GameFunctions> _logger;
private readonly AbandonDutyDelegate _abandonDuty;
private readonly ReadOnlyDictionary<ushort, uint> _territoryToAetherCurrentCompFlgSet;
private readonly ReadOnlyDictionary<uint, uint> _contentFinderConditionToContentId;
public GameFunctions(QuestFunctions questFunctions, IDataManager dataManager, IObjectTable objectTable, ITargetManager targetManager, ICondition condition, IClientState clientState, IGameGui gameGui, Configuration configuration, ISigScanner sigScanner, ILogger<GameFunctions> logger)
{
_questFunctions = questFunctions;
_dataManager = dataManager;
_objectTable = objectTable;
_targetManager = targetManager;
_condition = condition;
_clientState = clientState;
_gameGui = gameGui;
_configuration = configuration;
_logger = logger;
_abandonDuty = Marshal.GetDelegateForFunctionPointer<AbandonDutyDelegate>(sigScanner.ScanText("E8 ?? ?? ?? ?? 41 B2 01 EB 39"));
_territoryToAetherCurrentCompFlgSet = (from x in dataManager.GetExcelSheet<TerritoryType>()
where x.RowId != 0
where x.AetherCurrentCompFlgSet.RowId != 0
select x).ToDictionary((TerritoryType x) => (ushort)x.RowId, (TerritoryType x) => x.AetherCurrentCompFlgSet.RowId).AsReadOnly();
_contentFinderConditionToContentId = (from x in dataManager.GetExcelSheet<ContentFinderCondition>()
where x.RowId != 0 && x.Content.RowId != 0
select x).ToDictionary((ContentFinderCondition x) => x.RowId, (ContentFinderCondition x) => x.Content.RowId).AsReadOnly();
}
public unsafe bool IsFlyingUnlocked(ushort territoryId)
{
if (_configuration.Advanced.NeverFly)
{
return false;
}
if (_questFunctions.IsQuestAccepted(new QuestId(3304)) && _condition[ConditionFlag.Mounted] && GetMountId() == 198)
{
return true;
}
PlayerState* ptr = PlayerState.Instance();
if (ptr != null && _territoryToAetherCurrentCompFlgSet.TryGetValue(territoryId, out var value))
{
return ptr->IsAetherCurrentZoneComplete(value);
}
return false;
}
public unsafe ushort? GetMountId()
{
BattleChara* ptr = (BattleChara*)(_clientState.LocalPlayer?.Address ?? 0);
if (ptr != null && ptr->Mount.MountId != 0)
{
return ptr->Mount.MountId;
}
return null;
}
public bool IsFlyingUnlockedInCurrentZone()
{
return IsFlyingUnlocked(_clientState.TerritoryType);
}
public unsafe bool IsAetherCurrentUnlocked(uint aetherCurrentId)
{
PlayerState* ptr = PlayerState.Instance();
if (ptr != null)
{
return ptr->IsAetherCurrentUnlocked(aetherCurrentId);
}
return false;
}
public IGameObject? FindObjectByDataId(uint dataId, Dalamud.Game.ClientState.Objects.Enums.ObjectKind? kind = null)
{
foreach (IGameObject item in _objectTable)
{
Dalamud.Game.ClientState.Objects.Enums.ObjectKind objectKind = item.ObjectKind;
bool flag = ((objectKind == Dalamud.Game.ClientState.Objects.Enums.ObjectKind.Player || objectKind - 8 <= Dalamud.Game.ClientState.Objects.Enums.ObjectKind.BattleNpc || objectKind == Dalamud.Game.ClientState.Objects.Enums.ObjectKind.Housing) ? true : false);
if (!flag && (item == null || item.ObjectKind != Dalamud.Game.ClientState.Objects.Enums.ObjectKind.GatheringPoint || item.IsTargetable) && item.DataId == dataId && (!kind.HasValue || kind.Value == item.ObjectKind))
{
return item;
}
}
_logger.LogWarning("Could not find GameObject with dataId {DataId}", dataId);
return null;
}
public bool InteractWith(uint dataId, Dalamud.Game.ClientState.Objects.Enums.ObjectKind? kind = null)
{
IGameObject gameObject = FindObjectByDataId(dataId, kind);
if (gameObject != null)
{
return InteractWith(gameObject);
}
_logger.LogDebug("Game object is null");
return false;
}
public unsafe bool InteractWith(IGameObject gameObject)
{
_logger.LogInformation("Setting target with {DataId} to {ObjectId}", gameObject.DataId, gameObject.EntityId);
_targetManager.Target = null;
_targetManager.Target = gameObject;
if (gameObject.ObjectKind == Dalamud.Game.ClientState.Objects.Enums.ObjectKind.GatheringPoint)
{
TargetSystem.Instance()->OpenObjectInteraction((GameObject*)gameObject.Address);
_logger.LogInformation("Interact result: (none) for GatheringPoint");
return true;
}
long num = (long)TargetSystem.Instance()->InteractWithObject((GameObject*)gameObject.Address, checkLineOfSight: false);
_logger.LogInformation("Interact result: {Result}", num);
if (num != 7)
{
return num > 0;
}
return false;
}
public unsafe bool UseItem(uint itemId)
{
long num = AgentInventoryContext.Instance()->UseItem(itemId, InventoryType.Invalid, 0u, 0);
_logger.LogInformation("UseItem result: {Result}", num);
return num == 0;
}
public unsafe bool UseItem(uint dataId, uint itemId)
{
IGameObject gameObject = FindObjectByDataId(dataId);
if (gameObject != null)
{
_targetManager.Target = gameObject;
long num = AgentInventoryContext.Instance()->UseItem(itemId, InventoryType.Invalid, 0u, 0);
_logger.LogInformation("UseItem result on {DataId}: {Result}", dataId, num);
if ((ulong)num <= 1uL)
{
return true;
}
return false;
}
return false;
}
public unsafe bool UseItemOnGround(uint dataId, uint itemId)
{
IGameObject gameObject = FindObjectByDataId(dataId);
if (gameObject != null)
{
Vector3 position = gameObject.Position;
return ActionManager.Instance()->UseActionLocation(ActionType.KeyItem, itemId, 3758096384uL, &position, 0u, 0);
}
return false;
}
public unsafe bool UseItemOnPosition(Vector3 position, uint itemId)
{
return ActionManager.Instance()->UseActionLocation(ActionType.KeyItem, itemId, 3758096384uL, &position, 0u, 0);
}
public unsafe bool UseAction(EAction action)
{
uint num = (uint)(action & (EAction)65535);
ActionType actionType = (((action & (EAction)65536) != (EAction)65536) ? ActionType.Action : ActionType.GeneralAction);
if (actionType == ActionType.Action)
{
num = ActionManager.Instance()->GetAdjustedActionId(num);
}
if (ActionManager.Instance()->GetActionStatus(actionType, num, 3758096384uL, checkRecastActive: true, checkCastingActive: true, null) == 0)
{
bool flag = ActionManager.Instance()->UseAction(actionType, num, 3758096384uL, 0u, ActionManager.UseActionMode.None, 0u, null);
_logger.LogInformation("UseAction {Action} (adjusted: {AdjustedActionId}) result: {Result}", action, num, flag);
return flag;
}
return false;
}
public unsafe bool UseAction(IGameObject gameObject, EAction action, bool checkCanUse = true)
{
uint actionId = (uint)(action & (EAction)65535);
ActionType actionType = (((action & (EAction)65536) != (EAction)65536) ? ActionType.Action : ActionType.GeneralAction);
if (actionType == ActionType.GeneralAction)
{
_logger.LogWarning("Can not use general action {Action} on target {Target}", action, gameObject);
return false;
}
actionId = ActionManager.Instance()->GetAdjustedActionId(actionId);
if (checkCanUse && !ActionManager.CanUseActionOnTarget(actionId, (GameObject*)gameObject.Address))
{
_logger.LogWarning("Can not use action {Action} (adjusted: {AdjustedActionId}) on target {Target}", action, actionId, gameObject);
return false;
}
Lumina.Excel.Sheets.Action row = _dataManager.GetExcelSheet<Lumina.Excel.Sheets.Action>().GetRow(actionId);
_targetManager.Target = gameObject;
if (ActionManager.Instance()->GetActionStatus(actionType, actionId, gameObject.GameObjectId, checkRecastActive: true, checkCastingActive: true, null) == 0)
{
bool flag;
if (row.TargetArea)
{
Vector3 position = gameObject.Position;
flag = ActionManager.Instance()->UseActionLocation(actionType, actionId, 3758096384uL, &position, 0u, 0);
_logger.LogInformation("UseAction {Action} (adjusted: {AdjustedActionId}) on target area {Target} result: {Result}", action, actionId, gameObject, flag);
}
else
{
flag = ActionManager.Instance()->UseAction(actionType, actionId, gameObject.GameObjectId, 0u, ActionManager.UseActionMode.None, 0u, null);
_logger.LogInformation("UseAction {Action} (adjusted: {AdjustedActionId}) on target {Target} result: {Result}", action, actionId, gameObject, flag);
}
return flag;
}
return false;
}
public bool IsObjectAtPosition(uint dataId, Vector3 position, float distance)
{
IGameObject gameObject = FindObjectByDataId(dataId);
if (gameObject != null)
{
return (gameObject.Position - position).Length() < distance;
}
return false;
}
public unsafe bool HasStatusPreventingMount()
{
if (_condition[ConditionFlag.Swimming] && !IsFlyingUnlockedInCurrentZone())
{
return true;
}
PlayerState* ptr = PlayerState.Instance();
if (ptr != null && !ptr->IsMountUnlocked(1u))
{
return true;
}
IPlayerCharacter localPlayer = _clientState.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 HasCharacterStatusPreventingMountOrSprint();
}
public bool HasStatusPreventingSprint()
{
return HasCharacterStatusPreventingMountOrSprint();
}
private unsafe bool HasCharacterStatusPreventingMountOrSprint()
{
IPlayerCharacter localPlayer = _clientState.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;
}
public unsafe bool HasStatus(EStatus statusId)
{
IPlayerCharacter localPlayer = _clientState.LocalPlayer;
if (localPlayer == null)
{
return false;
}
BattleChara* address = (BattleChara*)localPlayer.Address;
return address->GetStatusManager()->HasStatus((uint)statusId);
}
public static bool RemoveStatus(EStatus statusId)
{
return StatusManager.ExecuteStatusOff((uint)statusId);
}
public unsafe bool Mount()
{
if (_condition[ConditionFlag.Mounted])
{
return true;
}
PlayerState* ptr = PlayerState.Instance();
if (ptr != null && _configuration.General.MountId != 0 && ptr->IsMountUnlocked(_configuration.General.MountId))
{
if (ActionManager.Instance()->GetActionStatus(ActionType.Mount, _configuration.General.MountId, 3758096384uL, checkRecastActive: true, checkCastingActive: true, null) == 0)
{
_logger.LogDebug("Attempting to use preferred mount...");
if (ActionManager.Instance()->UseAction(ActionType.Mount, _configuration.General.MountId, 3758096384uL, 0u, ActionManager.UseActionMode.None, 0u, null))
{
_logger.LogInformation("Using preferred mount");
return true;
}
return false;
}
}
else if (ActionManager.Instance()->GetActionStatus(ActionType.GeneralAction, 9u, 3758096384uL, checkRecastActive: true, checkCastingActive: true, null) == 0)
{
_logger.LogDebug("Attempting to use mount roulette...");
if (ActionManager.Instance()->UseAction(ActionType.GeneralAction, 9u, 3758096384uL, 0u, ActionManager.UseActionMode.None, 0u, null))
{
_logger.LogInformation("Using mount roulette");
return true;
}
return false;
}
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("Attempting to unmount...");
if (ActionManager.Instance()->UseAction(ActionType.GeneralAction, 23u, 3758096384uL, 0u, ActionManager.UseActionMode.None, 0u, null))
{
_logger.LogInformation("Unmounted");
return true;
}
return false;
}
_logger.LogWarning("Can't unmount right now?");
return false;
}
public unsafe void OpenDutyFinder(uint contentFinderConditionId)
{
if (_contentFinderConditionToContentId.TryGetValue(contentFinderConditionId, out var value))
{
if (UIState.IsInstanceContentUnlocked(value))
{
AgentContentsFinder.Instance()->OpenRegularDuty(contentFinderConditionId);
return;
}
_logger.LogError("Trying to access a locked duty (cf: {ContentFinderId}, content: {ContentId})", contentFinderConditionId, value);
}
else
{
_logger.LogError("Could not find content for content finder condition (cf: {ContentFinderId})", contentFinderConditionId);
}
}
public static bool GameStringEquals(string? a, string? b)
{
if (a == null)
{
return b == null;
}
if (b == null)
{
return false;
}
return a.ReplaceLineEndings().Replace('', '-') == b.ReplaceLineEndings().Replace('', '-');
}
public unsafe bool IsOccupied()
{
if (!_clientState.IsLoggedIn || _clientState.LocalPlayer == null)
{
return true;
}
if (IsLoadingScreenVisible())
{
return true;
}
if (_condition[ConditionFlag.Crafting])
{
if (!AgentRecipeNote.Instance()->IsAgentActive())
{
return true;
}
if (!_condition[ConditionFlag.PreparingToCraft])
{
return true;
}
}
if (_condition[ConditionFlag.Unconscious] && _condition[ConditionFlag.SufferingStatusAffliction63] && _clientState.TerritoryType == 1052)
{
return false;
}
if (!_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.MountOrOrnamentTransition] && !_condition[ConditionFlag.BetweenAreas] && !_condition[ConditionFlag.BetweenAreas51] && !_condition[ConditionFlag.Jumping61] && !_condition[ConditionFlag.ExecutingGatheringAction])
{
return _condition[ConditionFlag.Jumping];
}
return true;
}
public unsafe bool IsOccupiedWithCustomDeliveryNpc(Questionable.Model.Quest? currentQuest)
{
if (currentQuest == null || !(currentQuest.Info is SatisfactionSupplyInfo))
{
return false;
}
if (_targetManager.Target == null || _targetManager.Target.DataId != currentQuest.Info.IssuerDataId)
{
return false;
}
if (!AgentSatisfactionSupply.Instance()->IsAgentActive())
{
return false;
}
HashSet<ConditionFlag> hashSet = _condition.AsReadOnlySet().ToHashSet();
hashSet.Remove(ConditionFlag.InDutyQueue);
if (hashSet.Count == 2 && hashSet.Contains(ConditionFlag.NormalConditions))
{
return hashSet.Contains(ConditionFlag.OccupiedInQuestEvent);
}
return false;
}
public unsafe bool IsLoadingScreenVisible()
{
if (_gameGui.TryGetAddonByName<AtkUnitBase>("FadeMiddle", out var addonPtr) && LAddon.IsAddonReady(addonPtr) && addonPtr->IsVisible)
{
return true;
}
if (_gameGui.TryGetAddonByName<AtkUnitBase>("FadeBack", out addonPtr) && LAddon.IsAddonReady(addonPtr) && addonPtr->IsVisible)
{
return true;
}
if (_gameGui.TryGetAddonByName<AtkUnitBase>("NowLoading", out addonPtr) && LAddon.IsAddonReady(addonPtr) && addonPtr->IsVisible)
{
return true;
}
return false;
}
public unsafe int GetFreeInventorySlots()
{
InventoryManager* ptr = InventoryManager.Instance();
if (ptr == null)
{
return 0;
}
int num = 0;
for (InventoryType inventoryType = InventoryType.Inventory1; inventoryType <= InventoryType.Inventory4; inventoryType++)
{
InventoryContainer* inventoryContainer = ptr->GetInventoryContainer(inventoryType);
if (inventoryContainer == null)
{
continue;
}
for (int i = 0; i < inventoryContainer->Size; i++)
{
InventoryItem* inventorySlot = inventoryContainer->GetInventorySlot(i);
if (inventorySlot == null || inventorySlot->ItemId == 0)
{
num++;
}
}
}
return num;
}
public void AbandonDuty()
{
_abandonDuty(a1: false);
}
public unsafe IReadOnlyList<uint> GetUnlockLinks()
{
UIState* ptr = UIState.Instance();
if (ptr == null)
{
_logger.LogError("Could not query unlock links");
return Array.Empty<uint>();
}
List<uint> list = new List<uint>();
for (uint num = 0u; num < ptr->UnlockLinkBitmask.Length * 8; num++)
{
if (ptr->IsUnlockLinkUnlocked(num))
{
list.Add(num);
}
}
_logger.LogInformation("Unlocked unlock links: {UnlockedUnlockLinks}", string.Join(", ", list));
return list;
}
}