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..f7c866a96
--- /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.
+ ///
+ NormalConditions = 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();
+ }
+ }
+}