From c6d46f9aea636517c9c0f79c0638fa910f1b1d1b Mon Sep 17 00:00:00 2001 From: NotAdam Date: Tue, 26 May 2020 23:13:39 +1000 Subject: [PATCH] add support for checking client conditions --- Dalamud/Dalamud.cs | 18 + Dalamud/Game/ClientState/ClientState.cs | 7 + .../ClientState/ClientStateAddressResolver.cs | 8 +- Dalamud/Game/ClientState/Condition.cs | 42 ++ Dalamud/Game/ClientState/ConditionFlag.cs | 455 ++++++++++++++++++ Dalamud/Game/SigScanner.cs | 2 +- Dalamud/Interface/ConditionDebugWindow.cs | 58 +++ 7 files changed, 587 insertions(+), 3 deletions(-) create mode 100644 Dalamud/Game/ClientState/Condition.cs create mode 100644 Dalamud/Game/ClientState/ConditionFlag.cs create mode 100644 Dalamud/Interface/ConditionDebugWindow.cs diff --git a/Dalamud/Dalamud.cs b/Dalamud/Dalamud.cs index 9dbaded45..bcb6832f7 100644 --- a/Dalamud/Dalamud.cs +++ b/Dalamud/Dalamud.cs @@ -145,6 +145,8 @@ namespace Dalamud { IsReady = true; }); + + this.conditionDebugWindow = new ConditionDebugWindow( this ); } public void Start() { @@ -209,6 +211,7 @@ namespace Dalamud { private DalamudCreditsWindow creditsWindow; private DalamudSettingsWindow settingsWindow; private PluginInstallerWindow pluginWindow; + private ConditionDebugWindow conditionDebugWindow; private void BuildDalamudUi() { @@ -268,6 +271,16 @@ namespace Dalamud { ImGui.EndMenu(); } + if( ImGui.BeginMenu( "Game" ) ) + { + if( ImGui.MenuItem( "Condition Debug" ) ) + { + this.conditionDebugWindow.Enabled = !this.conditionDebugWindow.Enabled; + } + + ImGui.EndMenu(); + } + if (ImGui.BeginMenu("Plugins")) { if (ImGui.MenuItem("Open Plugin installer")) @@ -372,6 +385,11 @@ namespace Dalamud { if (this.isImguiDrawDemoWindow) ImGui.ShowDemoWindow(); + + if( this.conditionDebugWindow.Enabled ) + { + this.conditionDebugWindow.Draw(); + } } #endregion diff --git a/Dalamud/Game/ClientState/ClientState.cs b/Dalamud/Game/ClientState/ClientState.cs index 96b0b40f1..b37dc2651 100644 --- a/Dalamud/Game/ClientState/ClientState.cs +++ b/Dalamud/Game/ClientState/ClientState.cs @@ -92,6 +92,11 @@ namespace Dalamud.Game.ClientState /// public KeyState KeyState; + /// + /// Provides access to client conditions/player state. Allows you to check if a player is in a duty, mounted, etc. + /// + public Condition Condition; + /// /// Set up client state access. /// @@ -114,6 +119,8 @@ namespace Dalamud.Game.ClientState this.KeyState = new KeyState(Address, scanner.Module.BaseAddress); + this.Condition = new Condition( Address ); + Log.Verbose("SetupTerritoryType address {SetupTerritoryType}", Address.SetupTerritoryType); this.setupTerritoryTypeHook = new Hook(Address.SetupTerritoryType, diff --git a/Dalamud/Game/ClientState/ClientStateAddressResolver.cs b/Dalamud/Game/ClientState/ClientStateAddressResolver.cs index dd559b623..e858ac6fc 100644 --- a/Dalamud/Game/ClientState/ClientStateAddressResolver.cs +++ b/Dalamud/Game/ClientState/ClientStateAddressResolver.cs @@ -15,14 +15,16 @@ namespace Dalamud.Game.ClientState public IntPtr SetupTerritoryType { get; private set; } //public IntPtr SomeActorTableAccess { get; private set; } public IntPtr PartyListUpdate { get; private set; } + + public IntPtr ConditionFlags { get; private set; } protected override void Setup64Bit(SigScanner sig) { // We don't need those anymore, but maybe someone else will - let's leave them here for good measure //ViewportActorTable = sig.GetStaticAddressFromSig("48 8D 0D ?? ?? ?? ?? 85 ED", 0) + 0x148; //SomeActorTableAccess = sig.ScanText("E8 ?? ?? ?? ?? 48 8D 55 A0 48 8D 8E ?? ?? ?? ??"); - ActorTable = sig.GetStaticAddressFromSig("88 91 ?? ?? ?? ?? 48 8D 3D ?? ?? ?? ??", 0x0); + ActorTable = sig.GetStaticAddressFromSig("88 91 ?? ?? ?? ?? 48 8D 3D ?? ?? ?? ??"); - LocalContentId = sig.GetStaticAddressFromSig("48 8B 05 ?? ?? ?? ?? 48 89 86 ?? ?? ?? ??", 0); + LocalContentId = sig.GetStaticAddressFromSig("48 8B 05 ?? ?? ?? ?? 48 89 86 ?? ?? ?? ??"); JobGaugeData = sig.GetStaticAddressFromSig("E8 ?? ?? ?? ?? FF C6 48 8D 5B 0C", 0xB9) + 0x10; SetupTerritoryType = sig.ScanText("48 89 5C 24 ?? 48 89 74 24 ?? 57 48 83 EC 20 48 8B F9 66 89 91 ?? ?? ?? ??"); @@ -31,6 +33,8 @@ namespace Dalamud.Game.ClientState KeyboardState = sig.ScanText("48 8D 0C 85 ?? ?? ?? ?? 8B 04 31 85 C2 0F 85") + 0x4; PartyListUpdate = sig.ScanText("E8 ?? ?? ?? ?? 49 8B D4 4C 8D 87 ?? ?? ?? ??"); + + ConditionFlags = sig.GetStaticAddressFromSig("48 8D 0D ?? ?? ?? ?? BA ?? ?? ?? ?? 45 33 C0"); } } } diff --git a/Dalamud/Game/ClientState/Condition.cs b/Dalamud/Game/ClientState/Condition.cs new file mode 100644 index 000000000..c886c6143 --- /dev/null +++ b/Dalamud/Game/ClientState/Condition.cs @@ -0,0 +1,42 @@ +using System; +using System.Runtime.CompilerServices; +using Dalamud.Hooking; +using Serilog; + +namespace Dalamud.Game.ClientState +{ + /// + /// Provides access to conditions (generally player state). You can check whether a player is in combat, mounted, etc. + /// + public class Condition + { + internal readonly IntPtr conditionArrayBase; + + /// + /// The current max number of conditions. You can get this just by looking at the condition sheet and how many rows it has. + /// + public const int MaxConditionEntries = 100; + + internal Condition( ClientStateAddressResolver resolver ) + { + this.conditionArrayBase = resolver.ConditionFlags; + } + + /// + /// Check the value of a specific condition/state flag. + /// + /// The condition flag to check + public unsafe bool this[ ConditionFlag flag ] + { + get + { + var idx = ( int )flag; + + if( idx > MaxConditionEntries || idx < 0 ) + return false; + + return *( bool* )( this.conditionArrayBase + idx ); + } + } + } +} diff --git a/Dalamud/Game/ClientState/ConditionFlag.cs b/Dalamud/Game/ClientState/ConditionFlag.cs new file mode 100644 index 000000000..4f1e3800e --- /dev/null +++ b/Dalamud/Game/ClientState/ConditionFlag.cs @@ -0,0 +1,455 @@ +namespace Dalamud.Game.ClientState +{ + /// + /// Possible state flags (or conditions as they're called internally) that can be set on the local client. + /// + /// These come from LogMessage (somewhere) and directly map to each state field managed by the client. As of 5.25, it maps to + /// LogMessage row 7700 and onwards, which can be checked by looking at the Condition sheet and looking at what column 2 maps to. + /// + public enum ConditionFlag { + /// + /// Unused. + /// + None = 0, + + /// + /// Unable to execute command under normal conditions. + /// + AbnormalConditions = 1, + + /// + /// Unable to execute command while unconscious. + /// + Unconscious = 2, + + /// + /// Unable to execute command during an emote. + /// + Emoting = 3, + + /// + /// Unable to execute command while mounted. + /// + /// + /// Unable to execute command while mounted. + /// + Mounted = 4, + + /// + /// Unable to execute command while crafting. + /// + Crafting = 5, + + /// + /// Unable to execute command while gathering. + /// + Gathering = 6, + + /// + /// Unable to execute command while melding materia. + /// + MeldingMateria = 7, + + /// + /// Unable to execute command while operating a siege machine. + /// + OperatingSiegeMachine = 8, + + /// + /// Unable to execute command while carrying an object. + /// + CarryingObject = 9, + + /// + /// Unable to execute command while mounted. + /// + Mounted2 = 10, + + /// + /// Unable to execute command while in that position. + /// + InThatPosition = 11, + + /// + /// Unable to execute command while chocobo racing. + /// + ChocoboRacing = 12, + + /// + /// Unable to execute command while playing a mini-game. + /// + PlayingMiniGame = 13, + + /// + /// Unable to execute command while playing Lord of Verminion. + /// + PlayingLordOfVerminion = 14, + + /// + /// Unable to execute command while participating in a custom match. + /// + ParticipatingInCustomMatch = 15, + + /// + /// Unable to execute command while performing. + /// + Performing = 16, + + //Unknown17 = 17, + //Unknown18 = 18, + //Unknown19 = 19, + //Unknown20 = 20, + //Unknown21 = 21, + //Unknown22 = 22, + //Unknown23 = 23, + //Unknown24 = 24, + + /// + /// Unable to execute command while occupied. + /// + Occupied = 25, + + /// + /// Unable to execute command during combat. + /// + InCombat = 26, + + /// + /// Unable to execute command while casting. + /// + Casting = 27, + + /// + /// Unable to execute command while suffering status affliction. + /// + SufferingStatusAffliction = 28, + + /// + /// Unable to execute command while suffering status affliction. + /// + SufferingStatusAffliction2 = 29, + + /// + /// Unable to execute command while occupied. + /// + Occupied30 = 30, + + /// + /// Unable to execute command while occupied. + /// + // todo: not sure if this is used for other event states/??? + OccupiedInEvent = 31, + + /// + /// Unable to execute command while occupied. + /// + OccupiedInQuestEvent = 32, + + /// + /// Unable to execute command while occupied. + /// + Occupied33 = 33, + + /// + /// Unable to execute command while bound by duty. + /// + BoundByDuty = 34, + + /// + /// Unable to execute command while occupied. + /// + OccupiedInCutSceneEvent = 35, + + /// + /// Unable to execute command while in a dueling area. + /// + InDuelingArea = 36, + + /// + /// Unable to execute command while a trade is open. + /// + TradeOpen = 37, + + /// + /// Unable to execute command while occupied. + /// + Occupied38 = 38, + + /// + /// Unable to execute command while occupied. + /// + Occupied39 = 39, + + /// + /// Unable to execute command while crafting. + /// + Crafting40 = 40, + + /// + /// Unable to execute command while preparing to craft. + /// + PreparingToCraft = 41, + + /// + /// Unable to execute command while gathering. + /// + Gathering42 = 42, + + /// + /// Unable to execute command while fishing. + /// + Fishing = 43, + + //Unknown44 = 44, + + /// + /// Unable to execute command while between areas. + /// + BetweenAreas = 45, + + /// + /// Unable to execute command while stealthed. + /// + Stealthed = 46, + + //Unknown47 = 47, + + /// + /// Unable to execute command while jumping. + /// + Jumping = 48, + + /// + /// Unable to execute command while auto-run is active. + /// + AutorunActive = 49, + + /// + /// Unable to execute command while occupied. + /// + // todo: used for other shits? + OccupiedSummoningBell = 50, + + /// + /// Unable to execute command while between areas. + /// + BetweenAreas51 = 51, + + /// + /// Unable to execute command due to system error. + /// + SystemError = 52, + + /// + /// Unable to execute command while logging out. + /// + LoggingOut = 53, + + /// + /// Unable to execute command at this location. + /// + ConditionLocation = 54, + + /// + /// Unable to execute command while waiting for duty. + /// + WaitingForDuty = 55, + + /// + /// Unable to execute command while bound by duty. + /// + BoundByDuty56 = 56, + + /// + /// Unable to execute command at this time. + /// + Unknown57 = 57, + + /// + /// Unable to execute command while watching a cutscene. + /// + WatchingCutscene = 58, + + /// + /// Unable to execute command while waiting for Duty Finder. + /// + WaitingForDutyFinder = 59, + + /// + /// Unable to execute command while creating a character. + /// + CreatingCharacter = 60, + + /// + /// Unable to execute command while jumping. + /// + Jumping61 = 61, + + /// + /// Unable to execute command while the PvP display is active. + /// + PvPDisplayActive = 62, + + /// + /// Unable to execute command while suffering status affliction. + /// + SufferingStatusAffliction63 = 63, + + /// + /// Unable to execute command while mounting. + /// + Mounting = 64, + + /// + /// Unable to execute command while carrying an item. + /// + CarryingItem = 65, + + /// + /// Unable to execute command while using the Party Finder. + /// + UsingPartyFinder = 66, + + /// + /// Unable to execute command while using housing functions. + /// + UsingHousingFunctions = 67, + + /// + /// Unable to execute command while transformed. + /// + Transformed = 68, + + /// + /// Unable to execute command while on the free trial. + /// + OnFreeTrial = 69, + + /// + /// Unable to execute command while being moved. + /// + BeingMoved = 70, + + /// + /// Unable to execute command while mounting. + /// + Mounting71 = 71, + + /// + /// Unable to execute command while suffering status affliction. + /// + SufferingStatusAffliction72 = 72, + + /// + /// Unable to execute command while suffering status affliction. + /// + SufferingStatusAffliction73 = 73, + + /// + /// Unable to execute command while registering for a race or match. + /// + RegisteringForRaceOrMatch = 74, + + /// + /// Unable to execute command while waiting for a race or match. + /// + WaitingForRaceOrMatch = 75, + + /// + /// Unable to execute command while waiting for a Triple Triad match. + /// + WaitingForTripleTriadMatch = 76, + + /// + /// Unable to execute command while in flight. + /// + InFlight = 77, + + /// + /// Unable to execute command while watching a cutscene. + /// + WatchingCutscene78 = 78, + + /// + /// Unable to execute command while delving into a deep dungeon. + /// + InDeepDungeon = 79, + + /// + /// Unable to execute command while swimming. + /// + Swimming = 80, + + /// + /// Unable to execute command while diving. + /// + Diving = 81, + + /// + /// Unable to execute command while registering for a Triple Triad match. + /// + RegisteringForTripleTriadMatch = 82, + + /// + /// Unable to execute command while waiting for a Triple Triad match. + /// + WaitingForTripleTriadMatch83 = 83, + + /// + /// Unable to execute command while participating in a cross-world party or alliance. + /// + ParticipatingInCrossWorldPartyOrAlliance = 84, + + //Unknown85 = 85, + + /// + /// Unable to execute command while playing duty record. + /// + DutyRecorderPlayback = 86, + + /// + /// Unable to execute command while casting. + /// + Casting87 = 87, + + /// + /// Unable to execute command in this state. + /// + InThisState88 = 88, + + /// + /// Unable to execute command in this state. + /// + InThisState89 = 89, + + /// + /// Unable to execute command while role-playing. + /// + RolePlaying = 90, + + /// + /// Unable to execute command while bound by duty. + /// + BoundToDuty97 = 91, + + /// + /// Unable to execute command while readying to visit another World. + /// + ReadyingVisitOtherWorld = 92, + + /// + /// Unable to execute command while waiting to visit another World. + /// + WaitingToVisitOtherWorld = 93, + + /// + /// Unable to execute command while using a parasol. + /// + UsingParasol = 94, + + /// + /// Unable to execute command while bound by duty. + /// + BoundByDuty95 = 95, + } +} diff --git a/Dalamud/Game/SigScanner.cs b/Dalamud/Game/SigScanner.cs index a9508ad47..9cd9b7694 100644 --- a/Dalamud/Game/SigScanner.cs +++ b/Dalamud/Game/SigScanner.cs @@ -182,7 +182,7 @@ namespace Dalamud.Game { /// The signature of the function using the data. /// The offset from function start of the instruction using the data. /// An IntPtr to the static memory location. - public IntPtr GetStaticAddressFromSig(string signature, int offset) + public IntPtr GetStaticAddressFromSig(string signature, int offset = 0) { IntPtr instrAddr = ScanText(signature); instrAddr = IntPtr.Add(instrAddr, offset); diff --git a/Dalamud/Interface/ConditionDebugWindow.cs b/Dalamud/Interface/ConditionDebugWindow.cs new file mode 100644 index 000000000..daa86bbdd --- /dev/null +++ b/Dalamud/Interface/ConditionDebugWindow.cs @@ -0,0 +1,58 @@ +using System.Numerics; +using Dalamud.Game.ClientState; +using ImGuiNET; + +namespace Dalamud.Interface +{ + internal class ConditionDebugWindow + { + private Condition condition; + + internal bool Enabled = false; + + private static readonly Vector2 DefaultWindowSize = new Vector2( 375, 500 ); + + internal ConditionDebugWindow( Dalamud dalamud ) + { + this.condition = dalamud.ClientState.Condition; + } + + internal void Draw() + { + ImGui.SetNextWindowSize( DefaultWindowSize, ImGuiCond.FirstUseEver ); + + ImGui.Begin( "Condition Debug", ref Enabled ); + + #if DEBUG + ImGui.Text($"ptr: {this.condition.conditionArrayBase.ToString("X16")}" ); + #endif + + ImGui.Text( "Current Conditions:" ); + ImGui.Separator(); + + bool didAny = false; + + for( var i = 0; i < Condition.MaxConditionEntries; i++ ) + { + var typedCondition = ( ConditionFlag )i; + var cond = this.condition[ typedCondition ]; + + if( !cond ) + { + continue; + } + + didAny = true; + + ImGui.Text( $"ID: {i} Enum: {typedCondition}" ); + } + + if( !didAny ) + { + ImGui.Text( "None. Talk to a shop NPC or visit a market board to find out more!!!!!!!" ); + } + + ImGui.End(); + } + } +}