mirror of
https://github.com/xivdev/Penumbra.git
synced 2025-12-12 10:17:22 +01:00
Try to identify actors in banners correctly.
This commit is contained in:
parent
23919d8083
commit
6b558c5940
4 changed files with 83 additions and 72 deletions
|
|
@ -14,6 +14,7 @@ using Dalamud.Utility.Signatures;
|
||||||
using FFXIVClientStructs.FFXIV.Client.Game.Group;
|
using FFXIVClientStructs.FFXIV.Client.Game.Group;
|
||||||
using FFXIVClientStructs.FFXIV.Client.Game.Object;
|
using FFXIVClientStructs.FFXIV.Client.Game.Object;
|
||||||
using FFXIVClientStructs.FFXIV.Client.UI.Agent;
|
using FFXIVClientStructs.FFXIV.Client.UI.Agent;
|
||||||
|
using FFXIVClientStructs.FFXIV.Component.GUI;
|
||||||
using Lumina.Excel.GeneratedSheets;
|
using Lumina.Excel.GeneratedSheets;
|
||||||
using Lumina.Text;
|
using Lumina.Text;
|
||||||
using Penumbra.GameData.Data;
|
using Penumbra.GameData.Data;
|
||||||
|
|
@ -227,7 +228,7 @@ public sealed partial class ActorManager : IDisposable
|
||||||
private unsafe bool SearchPlayerCustomize(Character* character, int idx, out ActorIdentifier id)
|
private unsafe bool SearchPlayerCustomize(Character* character, int idx, out ActorIdentifier id)
|
||||||
{
|
{
|
||||||
var other = (Character*)_objects.GetObjectAddress(idx);
|
var other = (Character*)_objects.GetObjectAddress(idx);
|
||||||
if (other == null || !CustomizeData.Equals((CustomizeData*)character->CustomizeData, (CustomizeData*)other->CustomizeData))
|
if (other == null || !CustomizeData.ScreenActorEquals((CustomizeData*)character->CustomizeData, (CustomizeData*)other->CustomizeData))
|
||||||
{
|
{
|
||||||
id = ActorIdentifier.Invalid;
|
id = ActorIdentifier.Invalid;
|
||||||
return false;
|
return false;
|
||||||
|
|
@ -246,14 +247,23 @@ public sealed partial class ActorManager : IDisposable
|
||||||
|
|
||||||
private unsafe ActorIdentifier SearchPlayersCustomize(Character* gameObject)
|
private unsafe ActorIdentifier SearchPlayersCustomize(Character* gameObject)
|
||||||
{
|
{
|
||||||
|
static bool Compare(Character* a, Character* b)
|
||||||
|
{
|
||||||
|
var data1 = (CustomizeData*)a->CustomizeData;
|
||||||
|
var data2 = (CustomizeData*)b->CustomizeData;
|
||||||
|
var equals = CustomizeData.ScreenActorEquals(data1, data2);
|
||||||
|
return equals;
|
||||||
|
}
|
||||||
|
|
||||||
for (var i = 0; i < (int)ScreenActor.CutsceneStart; i += 2)
|
for (var i = 0; i < (int)ScreenActor.CutsceneStart; i += 2)
|
||||||
{
|
{
|
||||||
var obj = (GameObject*)_objects.GetObjectAddress(i);
|
var obj = (GameObject*)_objects.GetObjectAddress(i);
|
||||||
if (obj != null
|
if (obj != null
|
||||||
&& obj->ObjectKind is (byte)ObjectKind.Player
|
&& obj->ObjectKind is (byte)ObjectKind.Player
|
||||||
&& CustomizeData.Equals((CustomizeData*)gameObject->CustomizeData, (CustomizeData*)((Character*)obj)->CustomizeData))
|
&& Compare(gameObject, (Character*)obj))
|
||||||
return FromObject(obj, out _, false, true);
|
return FromObject(obj, out _, false, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
return ActorIdentifier.Invalid;
|
return ActorIdentifier.Invalid;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -281,15 +291,18 @@ public sealed partial class ActorManager : IDisposable
|
||||||
public unsafe bool ResolvePvPBannerPlayer(ScreenActor type, out ActorIdentifier id)
|
public unsafe bool ResolvePvPBannerPlayer(ScreenActor type, out ActorIdentifier id)
|
||||||
{
|
{
|
||||||
id = ActorIdentifier.Invalid;
|
id = ActorIdentifier.Invalid;
|
||||||
var addon = _gameGui.GetAddonByName("PvPMKSIntroduction");
|
if (!_clientState.IsPvPExcludingDen)
|
||||||
if (addon == IntPtr.Zero)
|
return false;
|
||||||
|
|
||||||
|
var addon = (AtkUnitBase*)_gameGui.GetAddonByName("PvPMap");
|
||||||
|
if (addon == null || addon->IsVisible)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
var obj = (Character*)_objects.GetObjectAddress((int)type);
|
var obj = (Character*)_objects.GetObjectAddress((int)type);
|
||||||
if (obj == null)
|
if (obj == null)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
var identifier = type switch
|
id = type switch
|
||||||
{
|
{
|
||||||
ScreenActor.CharacterScreen => SearchPlayersCustomize(obj),
|
ScreenActor.CharacterScreen => SearchPlayersCustomize(obj),
|
||||||
ScreenActor.ExamineScreen => SearchPlayersCustomize(obj),
|
ScreenActor.ExamineScreen => SearchPlayersCustomize(obj),
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,11 @@
|
||||||
using System;
|
using System;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
using System.Text;
|
||||||
using Penumbra.String.Functions;
|
using Penumbra.String.Functions;
|
||||||
|
|
||||||
namespace Penumbra.GameData.Structs;
|
namespace Penumbra.GameData.Structs;
|
||||||
|
|
||||||
|
[StructLayout(LayoutKind.Sequential, Size = Size)]
|
||||||
public unsafe struct CustomizeData : IEquatable< CustomizeData >
|
public unsafe struct CustomizeData : IEquatable< CustomizeData >
|
||||||
{
|
{
|
||||||
public const int Size = 26;
|
public const int Size = 26;
|
||||||
|
|
@ -40,11 +43,17 @@ public unsafe struct CustomizeData : IEquatable< CustomizeData >
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public override bool Equals( object? obj )
|
||||||
|
=> obj is CustomizeData other && Equals( other );
|
||||||
|
|
||||||
public static bool Equals(CustomizeData* lhs, CustomizeData* rhs)
|
public static bool Equals(CustomizeData* lhs, CustomizeData* rhs)
|
||||||
=> MemoryUtility.MemCmpUnchecked(lhs, rhs, Size) == 0;
|
=> MemoryUtility.MemCmpUnchecked(lhs, rhs, Size) == 0;
|
||||||
|
|
||||||
public override bool Equals( object? obj )
|
/// <remarks>Compare Gender and then only from Height onwards, because all screen actors are set to Height 50,
|
||||||
=> obj is CustomizeData other && Equals( other );
|
/// the Race is implicitly included in the subrace (after height),
|
||||||
|
/// and the body type is irrelevant for players.</remarks>>
|
||||||
|
public static bool ScreenActorEquals(CustomizeData* lhs, CustomizeData* rhs)
|
||||||
|
=> lhs->Data[1] == rhs->Data[1] && MemoryUtility.MemCmpUnchecked(lhs->Data + 4, rhs->Data + 4, Size - 4) == 0;
|
||||||
|
|
||||||
public override int GetHashCode()
|
public override int GetHashCode()
|
||||||
{
|
{
|
||||||
|
|
@ -65,6 +74,17 @@ public unsafe struct CustomizeData : IEquatable< CustomizeData >
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public string WriteBytes()
|
||||||
|
{
|
||||||
|
var sb = new StringBuilder(Size * 3);
|
||||||
|
for (var i = 0; i < Size - 1; ++i)
|
||||||
|
{
|
||||||
|
sb.Append($"{Data[i]:X2} ");
|
||||||
|
}
|
||||||
|
sb.Append($"{Data[Size - 1]:X2}");
|
||||||
|
return sb.ToString();
|
||||||
|
}
|
||||||
|
|
||||||
public bool LoadBase64( string base64 )
|
public bool LoadBase64( string base64 )
|
||||||
{
|
{
|
||||||
var buffer = stackalloc byte[Size];
|
var buffer = stackalloc byte[Size];
|
||||||
|
|
|
||||||
|
|
@ -72,51 +72,73 @@ public sealed partial class IndividualCollections : IReadOnlyList< (string Displ
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
case IdentifierType.Npc: return _individuals.TryGetValue( identifier, out collection );
|
case IdentifierType.Npc: return _individuals.TryGetValue( identifier, out collection );
|
||||||
case IdentifierType.Special: return CheckWorlds( ConvertSpecialIdentifier( identifier ), out collection );
|
case IdentifierType.Special: return CheckWorlds( ConvertSpecialIdentifier( identifier ).Item1, out collection );
|
||||||
}
|
}
|
||||||
|
|
||||||
collection = null;
|
collection = null;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
public ActorIdentifier ConvertSpecialIdentifier( ActorIdentifier identifier )
|
public enum SpecialResult
|
||||||
|
{
|
||||||
|
PartyBanner,
|
||||||
|
PvPBanner,
|
||||||
|
Mahjong,
|
||||||
|
CharacterScreen,
|
||||||
|
FittingRoom,
|
||||||
|
DyePreview,
|
||||||
|
Portrait,
|
||||||
|
Inspect,
|
||||||
|
Card,
|
||||||
|
Glamour,
|
||||||
|
Invalid,
|
||||||
|
}
|
||||||
|
|
||||||
|
public (ActorIdentifier, SpecialResult) ConvertSpecialIdentifier( ActorIdentifier identifier )
|
||||||
{
|
{
|
||||||
if( identifier.Type != IdentifierType.Special )
|
if( identifier.Type != IdentifierType.Special )
|
||||||
{
|
{
|
||||||
return identifier;
|
return ( identifier, SpecialResult.Invalid );
|
||||||
}
|
}
|
||||||
|
|
||||||
if( _actorManager.ResolvePartyBannerPlayer( identifier.Special, out var id )
|
if( _actorManager.ResolvePartyBannerPlayer( identifier.Special, out var id ) )
|
||||||
|| _actorManager.ResolvePvPBannerPlayer( identifier.Special, out id )
|
|
||||||
|| _actorManager.ResolveMahjongPlayer( identifier.Special, out id ) )
|
|
||||||
{
|
{
|
||||||
return identifier;
|
return ( id, SpecialResult.PartyBanner );
|
||||||
|
}
|
||||||
|
|
||||||
|
if( _actorManager.ResolvePvPBannerPlayer( identifier.Special, out id ) )
|
||||||
|
{
|
||||||
|
return ( id, SpecialResult.PvPBanner );
|
||||||
|
}
|
||||||
|
|
||||||
|
if( _actorManager.ResolveMahjongPlayer( identifier.Special, out id ) )
|
||||||
|
{
|
||||||
|
return ( id, SpecialResult.Mahjong );
|
||||||
}
|
}
|
||||||
|
|
||||||
switch( identifier.Special )
|
switch( identifier.Special )
|
||||||
{
|
{
|
||||||
case ScreenActor.CharacterScreen when Penumbra.Config.UseCharacterCollectionInMainWindow:
|
case ScreenActor.CharacterScreen when Penumbra.Config.UseCharacterCollectionInMainWindow: return ( _actorManager.GetCurrentPlayer(), SpecialResult.CharacterScreen );
|
||||||
case ScreenActor.FittingRoom when Penumbra.Config.UseCharacterCollectionInTryOn:
|
case ScreenActor.FittingRoom when Penumbra.Config.UseCharacterCollectionInTryOn: return ( _actorManager.GetCurrentPlayer(), SpecialResult.FittingRoom );
|
||||||
case ScreenActor.DyePreview when Penumbra.Config.UseCharacterCollectionInTryOn:
|
case ScreenActor.DyePreview when Penumbra.Config.UseCharacterCollectionInTryOn: return ( _actorManager.GetCurrentPlayer(), SpecialResult.DyePreview );
|
||||||
case ScreenActor.Portrait when Penumbra.Config.UseCharacterCollectionsInCards:
|
case ScreenActor.Portrait when Penumbra.Config.UseCharacterCollectionsInCards: return ( _actorManager.GetCurrentPlayer(), SpecialResult.Portrait );
|
||||||
return _actorManager.GetCurrentPlayer();
|
|
||||||
case ScreenActor.ExamineScreen:
|
case ScreenActor.ExamineScreen:
|
||||||
{
|
{
|
||||||
identifier = _actorManager.GetInspectPlayer();
|
identifier = _actorManager.GetInspectPlayer();
|
||||||
if( identifier.IsValid )
|
if( identifier.IsValid )
|
||||||
{
|
{
|
||||||
return Penumbra.Config.UseCharacterCollectionInInspect ? identifier : ActorIdentifier.Invalid;
|
return ( Penumbra.Config.UseCharacterCollectionInInspect ? identifier : ActorIdentifier.Invalid, SpecialResult.Inspect );
|
||||||
}
|
}
|
||||||
|
|
||||||
identifier = _actorManager.GetCardPlayer();
|
identifier = _actorManager.GetCardPlayer();
|
||||||
if( identifier.IsValid )
|
if( identifier.IsValid )
|
||||||
{
|
{
|
||||||
return Penumbra.Config.UseCharacterCollectionInInspect ? identifier : ActorIdentifier.Invalid;
|
return ( Penumbra.Config.UseCharacterCollectionInInspect ? identifier : ActorIdentifier.Invalid, SpecialResult.Card );
|
||||||
}
|
}
|
||||||
|
|
||||||
return Penumbra.Config.UseCharacterCollectionInTryOn ? _actorManager.GetGlamourPlayer() : ActorIdentifier.Invalid;
|
return ( Penumbra.Config.UseCharacterCollectionInTryOn ? _actorManager.GetGlamourPlayer() : ActorIdentifier.Invalid, SpecialResult.Glamour );
|
||||||
}
|
}
|
||||||
default: return identifier;
|
default: return ( identifier, SpecialResult.Invalid );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,6 @@ using Penumbra.GameData.Actors;
|
||||||
using Penumbra.GameData.Enums;
|
using Penumbra.GameData.Enums;
|
||||||
using Penumbra.Util;
|
using Penumbra.Util;
|
||||||
using Character = FFXIVClientStructs.FFXIV.Client.Game.Character.Character;
|
using Character = FFXIVClientStructs.FFXIV.Client.Game.Character.Character;
|
||||||
using CustomizeData = Penumbra.GameData.Structs.CustomizeData;
|
|
||||||
using GameObject = FFXIVClientStructs.FFXIV.Client.Game.Object.GameObject;
|
using GameObject = FFXIVClientStructs.FFXIV.Client.Game.Object.GameObject;
|
||||||
using ObjectKind = Dalamud.Game.ClientState.Objects.Enums.ObjectKind;
|
using ObjectKind = Dalamud.Game.ClientState.Objects.Enums.ObjectKind;
|
||||||
|
|
||||||
|
|
@ -18,48 +17,6 @@ namespace Penumbra.Interop.Resolver;
|
||||||
|
|
||||||
public unsafe partial class PathResolver
|
public unsafe partial class PathResolver
|
||||||
{
|
{
|
||||||
private static ResolveData IdentifyMahjong( GameObject* gameObject )
|
|
||||||
{
|
|
||||||
static bool SearchPlayer( Character* character, int idx, out ActorIdentifier id )
|
|
||||||
{
|
|
||||||
var other = ( Character* )Dalamud.Objects.GetObjectAddress( idx );
|
|
||||||
if( other == null || !CustomizeData.Equals( ( CustomizeData* )character->CustomizeData, ( CustomizeData* )other->CustomizeData ) )
|
|
||||||
{
|
|
||||||
id = ActorIdentifier.Invalid;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
id = Penumbra.Actors.FromObject( &other->GameObject, out _, false, true );
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
static ActorIdentifier SearchPlayers( Character* gameObject, int idx1, int idx2, int idx3 )
|
|
||||||
=> SearchPlayer( gameObject, idx1, out var id ) || SearchPlayer( gameObject, idx2, out id ) || SearchPlayer( gameObject, idx3, out id )
|
|
||||||
? id
|
|
||||||
: ActorIdentifier.Invalid;
|
|
||||||
|
|
||||||
var identifier = gameObject->ObjectIndex switch
|
|
||||||
{
|
|
||||||
0 => Penumbra.Actors.GetCurrentPlayer(),
|
|
||||||
2 => Penumbra.Actors.FromObject( gameObject, out _, false, true ),
|
|
||||||
4 => Penumbra.Actors.FromObject( gameObject, out _, false, true ),
|
|
||||||
6 => Penumbra.Actors.FromObject( gameObject, out _, false, true ),
|
|
||||||
240 => Penumbra.Actors.GetCurrentPlayer(),
|
|
||||||
241 => SearchPlayers( ( Character* )gameObject, 2, 4, 6 ),
|
|
||||||
242 => SearchPlayers( ( Character* )gameObject, 4, 2, 6 ),
|
|
||||||
243 => SearchPlayers( ( Character* )gameObject, 6, 2, 4 ),
|
|
||||||
_ => ActorIdentifier.Invalid,
|
|
||||||
};
|
|
||||||
|
|
||||||
var collection = ( identifier.IsValid ? CollectionByIdentifier( identifier ) : null )
|
|
||||||
?? CheckYourself( identifier, gameObject )
|
|
||||||
?? CollectionByAttributes( gameObject )
|
|
||||||
?? Penumbra.CollectionManager.Default;
|
|
||||||
|
|
||||||
return IdentifiedCache.Set( collection, identifier, gameObject );
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// Identify the correct collection for a GameObject by index and name.
|
// Identify the correct collection for a GameObject by index and name.
|
||||||
public static ResolveData IdentifyCollection( GameObject* gameObject, bool useCache )
|
public static ResolveData IdentifyCollection( GameObject* gameObject, bool useCache )
|
||||||
{
|
{
|
||||||
|
|
@ -89,7 +46,7 @@ public unsafe partial class PathResolver
|
||||||
}
|
}
|
||||||
|
|
||||||
// Aesthetician. The relevant actor is yourself, so use player collection when possible.
|
// Aesthetician. The relevant actor is yourself, so use player collection when possible.
|
||||||
if( Dalamud.GameGui.GetAddonByName( "ScreenLog", 1 ) == IntPtr.Zero )
|
if( Dalamud.GameGui.GetAddonByName( "ScreenLog" ) == IntPtr.Zero )
|
||||||
{
|
{
|
||||||
var player = Penumbra.Actors.GetCurrentPlayer();
|
var player = Penumbra.Actors.GetCurrentPlayer();
|
||||||
var collection2 = ( player.IsValid ? CollectionByIdentifier( player ) : null )
|
var collection2 = ( player.IsValid ? CollectionByIdentifier( player ) : null )
|
||||||
|
|
@ -102,12 +59,11 @@ public unsafe partial class PathResolver
|
||||||
var identifier = Penumbra.Actors.FromObject( gameObject, out var owner, true, false );
|
var identifier = Penumbra.Actors.FromObject( gameObject, out var owner, true, false );
|
||||||
if( identifier.Type is IdentifierType.Special )
|
if( identifier.Type is IdentifierType.Special )
|
||||||
{
|
{
|
||||||
if( Penumbra.Config.UseNoModsInInspect && identifier.Special == ScreenActor.ExamineScreen )
|
( identifier, var type ) = Penumbra.CollectionManager.Individuals.ConvertSpecialIdentifier( identifier );
|
||||||
|
if( Penumbra.Config.UseNoModsInInspect && type == IndividualCollections.SpecialResult.Inspect )
|
||||||
{
|
{
|
||||||
return IdentifiedCache.Set( ModCollection.Empty, identifier, gameObject );
|
return IdentifiedCache.Set( ModCollection.Empty, identifier, gameObject );
|
||||||
}
|
}
|
||||||
|
|
||||||
identifier = Penumbra.CollectionManager.Individuals.ConvertSpecialIdentifier( identifier );
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var collection = CollectionByIdentifier( identifier )
|
var collection = CollectionByIdentifier( identifier )
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue