Merge pull request #116 from NotAdam/feature/conditions

add support for checking client conditions
This commit is contained in:
goaaats 2020-05-26 16:04:36 +02:00 committed by GitHub
commit 3fe162aebf
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 587 additions and 3 deletions

View file

@ -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

View file

@ -92,6 +92,11 @@ namespace Dalamud.Game.ClientState
/// </summary>
public KeyState KeyState;
/// <summary>
/// Provides access to client conditions/player state. Allows you to check if a player is in a duty, mounted, etc.
/// </summary>
public Condition Condition;
/// <summary>
/// Set up client state access.
/// </summary>
@ -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<SetupTerritoryTypeDelegate>(Address.SetupTerritoryType,

View file

@ -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");
}
}
}

View file

@ -0,0 +1,42 @@
using System;
using System.Runtime.CompilerServices;
using Dalamud.Hooking;
using Serilog;
namespace Dalamud.Game.ClientState
{
/// <summary>
/// Provides access to conditions (generally player state). You can check whether a player is in combat, mounted, etc.
/// </summary>
public class Condition
{
internal readonly IntPtr conditionArrayBase;
/// <summary>
/// The current max number of conditions. You can get this just by looking at the condition sheet and how many rows it has.
/// </summary>
public const int MaxConditionEntries = 100;
internal Condition( ClientStateAddressResolver resolver )
{
this.conditionArrayBase = resolver.ConditionFlags;
}
/// <summary>
/// Check the value of a specific condition/state flag.
/// </summary>
/// <param name="flag">The condition flag to check</param>
public unsafe bool this[ ConditionFlag flag ]
{
get
{
var idx = ( int )flag;
if( idx > MaxConditionEntries || idx < 0 )
return false;
return *( bool* )( this.conditionArrayBase + idx );
}
}
}
}

View file

@ -0,0 +1,455 @@
namespace Dalamud.Game.ClientState
{
/// <summary>
/// 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.
/// </summary>
public enum ConditionFlag {
/// <summary>
/// Unused.
/// </summary>
None = 0,
/// <summary>
/// Unable to execute command under normal conditions.
/// </summary>
NormalConditions = 1,
/// <summary>
/// Unable to execute command while unconscious.
/// </summary>
Unconscious = 2,
/// <summary>
/// Unable to execute command during an emote.
/// </summary>
Emoting = 3,
/// <summary>
/// Unable to execute command while mounted.
/// </summary>
/// <summary>
/// Unable to execute command while mounted.
/// </summary>
Mounted = 4,
/// <summary>
/// Unable to execute command while crafting.
/// </summary>
Crafting = 5,
/// <summary>
/// Unable to execute command while gathering.
/// </summary>
Gathering = 6,
/// <summary>
/// Unable to execute command while melding materia.
/// </summary>
MeldingMateria = 7,
/// <summary>
/// Unable to execute command while operating a siege machine.
/// </summary>
OperatingSiegeMachine = 8,
/// <summary>
/// Unable to execute command while carrying an object.
/// </summary>
CarryingObject = 9,
/// <summary>
/// Unable to execute command while mounted.
/// </summary>
Mounted2 = 10,
/// <summary>
/// Unable to execute command while in that position.
/// </summary>
InThatPosition = 11,
/// <summary>
/// Unable to execute command while chocobo racing.
/// </summary>
ChocoboRacing = 12,
/// <summary>
/// Unable to execute command while playing a mini-game.
/// </summary>
PlayingMiniGame = 13,
/// <summary>
/// Unable to execute command while playing Lord of Verminion.
/// </summary>
PlayingLordOfVerminion = 14,
/// <summary>
/// Unable to execute command while participating in a custom match.
/// </summary>
ParticipatingInCustomMatch = 15,
/// <summary>
/// Unable to execute command while performing.
/// </summary>
Performing = 16,
//Unknown17 = 17,
//Unknown18 = 18,
//Unknown19 = 19,
//Unknown20 = 20,
//Unknown21 = 21,
//Unknown22 = 22,
//Unknown23 = 23,
//Unknown24 = 24,
/// <summary>
/// Unable to execute command while occupied.
/// </summary>
Occupied = 25,
/// <summary>
/// Unable to execute command during combat.
/// </summary>
InCombat = 26,
/// <summary>
/// Unable to execute command while casting.
/// </summary>
Casting = 27,
/// <summary>
/// Unable to execute command while suffering status affliction.
/// </summary>
SufferingStatusAffliction = 28,
/// <summary>
/// Unable to execute command while suffering status affliction.
/// </summary>
SufferingStatusAffliction2 = 29,
/// <summary>
/// Unable to execute command while occupied.
/// </summary>
Occupied30 = 30,
/// <summary>
/// Unable to execute command while occupied.
/// </summary>
// todo: not sure if this is used for other event states/???
OccupiedInEvent = 31,
/// <summary>
/// Unable to execute command while occupied.
/// </summary>
OccupiedInQuestEvent = 32,
/// <summary>
/// Unable to execute command while occupied.
/// </summary>
Occupied33 = 33,
/// <summary>
/// Unable to execute command while bound by duty.
/// </summary>
BoundByDuty = 34,
/// <summary>
/// Unable to execute command while occupied.
/// </summary>
OccupiedInCutSceneEvent = 35,
/// <summary>
/// Unable to execute command while in a dueling area.
/// </summary>
InDuelingArea = 36,
/// <summary>
/// Unable to execute command while a trade is open.
/// </summary>
TradeOpen = 37,
/// <summary>
/// Unable to execute command while occupied.
/// </summary>
Occupied38 = 38,
/// <summary>
/// Unable to execute command while occupied.
/// </summary>
Occupied39 = 39,
/// <summary>
/// Unable to execute command while crafting.
/// </summary>
Crafting40 = 40,
/// <summary>
/// Unable to execute command while preparing to craft.
/// </summary>
PreparingToCraft = 41,
/// <summary>
/// Unable to execute command while gathering.
/// </summary>
Gathering42 = 42,
/// <summary>
/// Unable to execute command while fishing.
/// </summary>
Fishing = 43,
//Unknown44 = 44,
/// <summary>
/// Unable to execute command while between areas.
/// </summary>
BetweenAreas = 45,
/// <summary>
/// Unable to execute command while stealthed.
/// </summary>
Stealthed = 46,
//Unknown47 = 47,
/// <summary>
/// Unable to execute command while jumping.
/// </summary>
Jumping = 48,
/// <summary>
/// Unable to execute command while auto-run is active.
/// </summary>
AutorunActive = 49,
/// <summary>
/// Unable to execute command while occupied.
/// </summary>
// todo: used for other shits?
OccupiedSummoningBell = 50,
/// <summary>
/// Unable to execute command while between areas.
/// </summary>
BetweenAreas51 = 51,
/// <summary>
/// Unable to execute command due to system error.
/// </summary>
SystemError = 52,
/// <summary>
/// Unable to execute command while logging out.
/// </summary>
LoggingOut = 53,
/// <summary>
/// Unable to execute command at this location.
/// </summary>
ConditionLocation = 54,
/// <summary>
/// Unable to execute command while waiting for duty.
/// </summary>
WaitingForDuty = 55,
/// <summary>
/// Unable to execute command while bound by duty.
/// </summary>
BoundByDuty56 = 56,
/// <summary>
/// Unable to execute command at this time.
/// </summary>
Unknown57 = 57,
/// <summary>
/// Unable to execute command while watching a cutscene.
/// </summary>
WatchingCutscene = 58,
/// <summary>
/// Unable to execute command while waiting for Duty Finder.
/// </summary>
WaitingForDutyFinder = 59,
/// <summary>
/// Unable to execute command while creating a character.
/// </summary>
CreatingCharacter = 60,
/// <summary>
/// Unable to execute command while jumping.
/// </summary>
Jumping61 = 61,
/// <summary>
/// Unable to execute command while the PvP display is active.
/// </summary>
PvPDisplayActive = 62,
/// <summary>
/// Unable to execute command while suffering status affliction.
/// </summary>
SufferingStatusAffliction63 = 63,
/// <summary>
/// Unable to execute command while mounting.
/// </summary>
Mounting = 64,
/// <summary>
/// Unable to execute command while carrying an item.
/// </summary>
CarryingItem = 65,
/// <summary>
/// Unable to execute command while using the Party Finder.
/// </summary>
UsingPartyFinder = 66,
/// <summary>
/// Unable to execute command while using housing functions.
/// </summary>
UsingHousingFunctions = 67,
/// <summary>
/// Unable to execute command while transformed.
/// </summary>
Transformed = 68,
/// <summary>
/// Unable to execute command while on the free trial.
/// </summary>
OnFreeTrial = 69,
/// <summary>
/// Unable to execute command while being moved.
/// </summary>
BeingMoved = 70,
/// <summary>
/// Unable to execute command while mounting.
/// </summary>
Mounting71 = 71,
/// <summary>
/// Unable to execute command while suffering status affliction.
/// </summary>
SufferingStatusAffliction72 = 72,
/// <summary>
/// Unable to execute command while suffering status affliction.
/// </summary>
SufferingStatusAffliction73 = 73,
/// <summary>
/// Unable to execute command while registering for a race or match.
/// </summary>
RegisteringForRaceOrMatch = 74,
/// <summary>
/// Unable to execute command while waiting for a race or match.
/// </summary>
WaitingForRaceOrMatch = 75,
/// <summary>
/// Unable to execute command while waiting for a Triple Triad match.
/// </summary>
WaitingForTripleTriadMatch = 76,
/// <summary>
/// Unable to execute command while in flight.
/// </summary>
InFlight = 77,
/// <summary>
/// Unable to execute command while watching a cutscene.
/// </summary>
WatchingCutscene78 = 78,
/// <summary>
/// Unable to execute command while delving into a deep dungeon.
/// </summary>
InDeepDungeon = 79,
/// <summary>
/// Unable to execute command while swimming.
/// </summary>
Swimming = 80,
/// <summary>
/// Unable to execute command while diving.
/// </summary>
Diving = 81,
/// <summary>
/// Unable to execute command while registering for a Triple Triad match.
/// </summary>
RegisteringForTripleTriadMatch = 82,
/// <summary>
/// Unable to execute command while waiting for a Triple Triad match.
/// </summary>
WaitingForTripleTriadMatch83 = 83,
/// <summary>
/// Unable to execute command while participating in a cross-world party or alliance.
/// </summary>
ParticipatingInCrossWorldPartyOrAlliance = 84,
//Unknown85 = 85,
/// <summary>
/// Unable to execute command while playing duty record.
/// </summary>
DutyRecorderPlayback = 86,
/// <summary>
/// Unable to execute command while casting.
/// </summary>
Casting87 = 87,
/// <summary>
/// Unable to execute command in this state.
/// </summary>
InThisState88 = 88,
/// <summary>
/// Unable to execute command in this state.
/// </summary>
InThisState89 = 89,
/// <summary>
/// Unable to execute command while role-playing.
/// </summary>
RolePlaying = 90,
/// <summary>
/// Unable to execute command while bound by duty.
/// </summary>
BoundToDuty97 = 91,
/// <summary>
/// Unable to execute command while readying to visit another World.
/// </summary>
ReadyingVisitOtherWorld = 92,
/// <summary>
/// Unable to execute command while waiting to visit another World.
/// </summary>
WaitingToVisitOtherWorld = 93,
/// <summary>
/// Unable to execute command while using a parasol.
/// </summary>
UsingParasol = 94,
/// <summary>
/// Unable to execute command while bound by duty.
/// </summary>
BoundByDuty95 = 95,
}
}

View file

@ -182,7 +182,7 @@ namespace Dalamud.Game {
/// <param name="signature">The signature of the function using the data.</param>
/// <param name="offset">The offset from function start of the instruction using the data.</param>
/// <returns>An IntPtr to the static memory location.</returns>
public IntPtr GetStaticAddressFromSig(string signature, int offset)
public IntPtr GetStaticAddressFromSig(string signature, int offset = 0)
{
IntPtr instrAddr = ScanText(signature);
instrAddr = IntPtr.Add(instrAddr, offset);

View file

@ -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();
}
}
}