mirror of
https://github.com/xivdev/Penumbra.git
synced 2025-12-12 10:17:22 +01:00
Allow player watcher to watch NPCs with human model.
This commit is contained in:
parent
6a024ba5d1
commit
ba7dc6fda7
2 changed files with 98 additions and 13 deletions
62
Penumbra.PlayerWatch/CharacterFactory.cs
Normal file
62
Penumbra.PlayerWatch/CharacterFactory.cs
Normal file
|
|
@ -0,0 +1,62 @@
|
||||||
|
using System;
|
||||||
|
using System.Reflection;
|
||||||
|
using Dalamud.Game.ClientState.Objects.Enums;
|
||||||
|
using Dalamud.Game.ClientState.Objects.SubKinds;
|
||||||
|
using Dalamud.Game.ClientState.Objects.Types;
|
||||||
|
|
||||||
|
namespace Penumbra.PlayerWatch
|
||||||
|
{
|
||||||
|
public static class CharacterFactory
|
||||||
|
{
|
||||||
|
private static ConstructorInfo? _characterConstructor = null;
|
||||||
|
|
||||||
|
private static void Initialize()
|
||||||
|
{
|
||||||
|
_characterConstructor ??= typeof( Character ).GetConstructor( BindingFlags.NonPublic | BindingFlags.Instance, null, new[]
|
||||||
|
{
|
||||||
|
typeof( IntPtr ),
|
||||||
|
}, null )!;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Character Character( IntPtr address )
|
||||||
|
{
|
||||||
|
Initialize();
|
||||||
|
return ( Character )_characterConstructor?.Invoke( new object[]
|
||||||
|
{
|
||||||
|
address,
|
||||||
|
} )!;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Character? Convert( GameObject? actor )
|
||||||
|
{
|
||||||
|
if( actor == null )
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return actor switch
|
||||||
|
{
|
||||||
|
PlayerCharacter p => p,
|
||||||
|
BattleChara b => b,
|
||||||
|
_ => actor.ObjectKind switch
|
||||||
|
{
|
||||||
|
ObjectKind.BattleNpc => Character( actor.Address ),
|
||||||
|
ObjectKind.Companion => Character( actor.Address ),
|
||||||
|
ObjectKind.EventNpc => Character( actor.Address ),
|
||||||
|
_ => null,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class GameObjectExtensions
|
||||||
|
{
|
||||||
|
private const int ModelTypeOffset = 0x01B4;
|
||||||
|
|
||||||
|
public static unsafe int ModelType( this GameObject actor )
|
||||||
|
=> *( int* )( actor.Address + ModelTypeOffset );
|
||||||
|
|
||||||
|
public static unsafe void SetModelType( this GameObject actor, int value )
|
||||||
|
=> *( int* )( actor.Address + ModelTypeOffset ) = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -15,7 +15,7 @@ namespace Penumbra.PlayerWatch
|
||||||
{
|
{
|
||||||
public const int GPosePlayerIdx = 201;
|
public const int GPosePlayerIdx = 201;
|
||||||
public const int GPoseTableEnd = GPosePlayerIdx + 48;
|
public const int GPoseTableEnd = GPosePlayerIdx + 48;
|
||||||
private const int ObjectsPerFrame = 8;
|
private const int ObjectsPerFrame = 32;
|
||||||
|
|
||||||
private readonly Framework _framework;
|
private readonly Framework _framework;
|
||||||
private readonly ClientState _clientState;
|
private readonly ClientState _clientState;
|
||||||
|
|
@ -172,11 +172,11 @@ namespace Penumbra.PlayerWatch
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private Character CheckGPoseObject( GameObject player )
|
private Character? CheckGPoseObject( GameObject player )
|
||||||
{
|
{
|
||||||
if( !_inGPose )
|
if( !_inGPose )
|
||||||
{
|
{
|
||||||
return ( Character )player;
|
return CharacterFactory.Convert( player );
|
||||||
}
|
}
|
||||||
|
|
||||||
for( var i = GPosePlayerIdx; i < GPoseTableEnd; ++i )
|
for( var i = GPosePlayerIdx; i < GPoseTableEnd; ++i )
|
||||||
|
|
@ -184,16 +184,16 @@ namespace Penumbra.PlayerWatch
|
||||||
var a = _objects[ i ];
|
var a = _objects[ i ];
|
||||||
if( a == null )
|
if( a == null )
|
||||||
{
|
{
|
||||||
return ( Character )player;
|
return CharacterFactory.Convert( player);
|
||||||
}
|
}
|
||||||
|
|
||||||
if( a.Name == player.Name )
|
if( a.Name == player.Name )
|
||||||
{
|
{
|
||||||
return ( Character )a;
|
return CharacterFactory.Convert( a );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return ( Character )player;
|
return CharacterFactory.Convert(player)!;
|
||||||
}
|
}
|
||||||
|
|
||||||
private bool TryGetPlayer( GameObject gameObject, out (CharacterEquipment, HashSet< PlayerWatcher >) equip )
|
private bool TryGetPlayer( GameObject gameObject, out (CharacterEquipment, HashSet< PlayerWatcher >) equip )
|
||||||
|
|
@ -203,6 +203,29 @@ namespace Penumbra.PlayerWatch
|
||||||
return name.Length != 0 && Equip.TryGetValue( name, out equip );
|
return name.Length != 0 && Equip.TryGetValue( name, out equip );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static bool InvalidObjectKind( ObjectKind kind )
|
||||||
|
{
|
||||||
|
return kind switch
|
||||||
|
{
|
||||||
|
ObjectKind.BattleNpc => false,
|
||||||
|
ObjectKind.EventNpc => false,
|
||||||
|
ObjectKind.Player => false,
|
||||||
|
_ => true,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private GameObject? GetNextObject()
|
||||||
|
{
|
||||||
|
if( _frameTicker == GPosePlayerIdx - 1 )
|
||||||
|
_frameTicker = GPoseTableEnd;
|
||||||
|
else if( _frameTicker == _objects.Length - 1 )
|
||||||
|
_frameTicker = 0;
|
||||||
|
else
|
||||||
|
++_frameTicker;
|
||||||
|
|
||||||
|
return _objects[ _frameTicker ];
|
||||||
|
}
|
||||||
|
|
||||||
private void OnFrameworkUpdate( object framework )
|
private void OnFrameworkUpdate( object framework )
|
||||||
{
|
{
|
||||||
var newInGPose = _objects[ GPosePlayerIdx ] != null;
|
var newInGPose = _objects[ GPosePlayerIdx ] != null;
|
||||||
|
|
@ -223,26 +246,26 @@ namespace Penumbra.PlayerWatch
|
||||||
|
|
||||||
for( var i = 0; i < ObjectsPerFrame; ++i )
|
for( var i = 0; i < ObjectsPerFrame; ++i )
|
||||||
{
|
{
|
||||||
_frameTicker = _frameTicker < GPosePlayerIdx - 2
|
var actor = GetNextObject();
|
||||||
? _frameTicker + 2
|
|
||||||
: 0;
|
|
||||||
|
|
||||||
var actor = _objects[ _frameTicker ];
|
|
||||||
if( actor == null
|
if( actor == null
|
||||||
|| actor.ObjectKind != ObjectKind.Player
|
|| InvalidObjectKind(actor.ObjectKind)
|
||||||
|| !TryGetPlayer( actor, out var equip ) )
|
|| !TryGetPlayer( actor, out var equip ) )
|
||||||
{
|
{
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
var character = CheckGPoseObject( actor );
|
var character = CheckGPoseObject( actor );
|
||||||
|
|
||||||
if( _cancel )
|
if( _cancel )
|
||||||
{
|
{
|
||||||
_cancel = false;
|
_cancel = false;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if( character == null || character.ModelType() != 0 )
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
PluginLog.Verbose( "Comparing Gear for {PlayerName} at {Address}...", character.Name, character.Address );
|
PluginLog.Verbose( "Comparing Gear for {PlayerName} at {Address}...", character.Name, character.Address );
|
||||||
if( !equip.Item1.CompareAndUpdate( character ) )
|
if( !equip.Item1.CompareAndUpdate( character ) )
|
||||||
{
|
{
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue