mirror of
https://github.com/xivdev/Penumbra.git
synced 2025-12-13 20:24:17 +01:00
Use IndividualCollections in PathResolver.
This commit is contained in:
parent
f8c0702432
commit
6a6eac1c3b
10 changed files with 128 additions and 245 deletions
|
|
@ -2,7 +2,6 @@ using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
using Dalamud.Game.ClientState.Objects.Enums;
|
using Dalamud.Game.ClientState.Objects.Enums;
|
||||||
using FFXIVClientStructs.FFXIV.Client.Game.Character;
|
|
||||||
using Newtonsoft.Json.Linq;
|
using Newtonsoft.Json.Linq;
|
||||||
using Penumbra.String;
|
using Penumbra.String;
|
||||||
|
|
||||||
|
|
@ -38,7 +37,7 @@ public readonly struct ActorIdentifier : IEquatable<ActorIdentifier>
|
||||||
IdentifierType.Player => HomeWorld == other.HomeWorld && PlayerName.EqualsCi(other.PlayerName),
|
IdentifierType.Player => HomeWorld == other.HomeWorld && PlayerName.EqualsCi(other.PlayerName),
|
||||||
IdentifierType.Owned => HomeWorld == other.HomeWorld && PlayerName.EqualsCi(other.PlayerName) && Manager.DataIdEquals(this, other),
|
IdentifierType.Owned => HomeWorld == other.HomeWorld && PlayerName.EqualsCi(other.PlayerName) && Manager.DataIdEquals(this, other),
|
||||||
IdentifierType.Special => Special == other.Special,
|
IdentifierType.Special => Special == other.Special,
|
||||||
IdentifierType.Npc => Index == other.Index && DataId == other.DataId && Manager.DataIdEquals(this, other),
|
IdentifierType.Npc => Manager.DataIdEquals(this, other) && (Index == other.Index || Index == ushort.MaxValue || other.Index == ushort.MaxValue),
|
||||||
_ => false,
|
_ => false,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
@ -75,7 +74,7 @@ public readonly struct ActorIdentifier : IEquatable<ActorIdentifier>
|
||||||
IdentifierType.Player => HashCode.Combine(IdentifierType.Player, PlayerName, HomeWorld),
|
IdentifierType.Player => HashCode.Combine(IdentifierType.Player, PlayerName, HomeWorld),
|
||||||
IdentifierType.Owned => HashCode.Combine(IdentifierType.Owned, Kind, PlayerName, HomeWorld, DataId),
|
IdentifierType.Owned => HashCode.Combine(IdentifierType.Owned, Kind, PlayerName, HomeWorld, DataId),
|
||||||
IdentifierType.Special => HashCode.Combine(IdentifierType.Special, Special),
|
IdentifierType.Special => HashCode.Combine(IdentifierType.Special, Special),
|
||||||
IdentifierType.Npc => HashCode.Combine(IdentifierType.Npc, Kind, Index, DataId),
|
IdentifierType.Npc => HashCode.Combine(IdentifierType.Npc, Kind, DataId),
|
||||||
_ => 0,
|
_ => 0,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,5 @@
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Globalization;
|
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using Dalamud;
|
using Dalamud;
|
||||||
|
|
@ -70,7 +69,10 @@ public sealed partial class ActorManager : DataSharer
|
||||||
public unsafe ActorIdentifier GetCurrentPlayer()
|
public unsafe ActorIdentifier GetCurrentPlayer()
|
||||||
{
|
{
|
||||||
var address = (Character*)(_objects[0]?.Address ?? IntPtr.Zero);
|
var address = (Character*)(_objects[0]?.Address ?? IntPtr.Zero);
|
||||||
return address == null ? ActorIdentifier.Invalid : CreatePlayer(new ByteString(address->GameObject.Name), address->HomeWorld);
|
return address == null
|
||||||
|
? ActorIdentifier.Invalid
|
||||||
|
: CreateIndividualUnchecked(IdentifierType.Player, new ByteString(address->GameObject.Name), address->HomeWorld,
|
||||||
|
ObjectKind.None, uint.MaxValue);
|
||||||
}
|
}
|
||||||
|
|
||||||
public ActorIdentifier GetInspectPlayer()
|
public ActorIdentifier GetInspectPlayer()
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,6 @@ using System.Collections.Generic;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Penumbra.GameData.Actors;
|
|
||||||
|
|
||||||
namespace Penumbra.Collections;
|
namespace Penumbra.Collections;
|
||||||
|
|
||||||
|
|
@ -44,8 +43,8 @@ public partial class ModCollection
|
||||||
=> _characters;
|
=> _characters;
|
||||||
|
|
||||||
// If a name does not correspond to a character, return the default collection instead.
|
// If a name does not correspond to a character, return the default collection instead.
|
||||||
public ModCollection Individual( ActorIdentifier identifier )
|
public ModCollection Character( string name )
|
||||||
=> Individuals.Individuals.TryGetValue( identifier, out var c ) ? c : Default;
|
=> _characters.TryGetValue( name, out var c ) ? c : Default;
|
||||||
|
|
||||||
// Special Collections
|
// Special Collections
|
||||||
private readonly ModCollection?[] _specialCollections = new ModCollection?[Enum.GetValues< CollectionType >().Length - 4];
|
private readonly ModCollection?[] _specialCollections = new ModCollection?[Enum.GetValues< CollectionType >().Length - 4];
|
||||||
|
|
|
||||||
|
|
@ -149,7 +149,7 @@ public partial class ModCollection
|
||||||
|
|
||||||
foreach( var (characterName, _) in _characters.Where( c => c.Value.Index == idx ).ToList() )
|
foreach( var (characterName, _) in _characters.Where( c => c.Value.Index == idx ).ToList() )
|
||||||
{
|
{
|
||||||
SetCollection( Empty, CollectionType.Character, characterName );
|
SetCollection( Empty, CollectionType.Individual, characterName );
|
||||||
}
|
}
|
||||||
|
|
||||||
var collection = _collections[ idx ];
|
var collection = _collections[ idx ];
|
||||||
|
|
|
||||||
|
|
@ -2,8 +2,10 @@ using System.Collections;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Diagnostics.CodeAnalysis;
|
using System.Diagnostics.CodeAnalysis;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using Dalamud.Game.ClientState.Objects.Enums;
|
||||||
using Dalamud.Game.ClientState.Objects.Types;
|
using Dalamud.Game.ClientState.Objects.Types;
|
||||||
using Penumbra.GameData.Actors;
|
using Penumbra.GameData.Actors;
|
||||||
|
using Penumbra.String;
|
||||||
|
|
||||||
namespace Penumbra.Collections;
|
namespace Penumbra.Collections;
|
||||||
|
|
||||||
|
|
@ -34,8 +36,8 @@ public sealed partial class IndividualCollections : IReadOnlyList< (string Displ
|
||||||
}
|
}
|
||||||
|
|
||||||
// Handle generic NPC
|
// Handle generic NPC
|
||||||
var npcIdentifier = _manager.CreateNpc( identifier.Kind, identifier.DataId );
|
var npcIdentifier = _manager.CreateIndividualUnchecked( IdentifierType.Npc, ByteString.Empty, ushort.MaxValue, identifier.Kind, identifier.DataId );
|
||||||
if( npcIdentifier.IsValid && _individuals.TryGetValue( identifier, out collection ) )
|
if( npcIdentifier.IsValid && _individuals.TryGetValue( npcIdentifier, out collection ) )
|
||||||
{
|
{
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
@ -43,7 +45,7 @@ public sealed partial class IndividualCollections : IReadOnlyList< (string Displ
|
||||||
// Handle Ownership.
|
// Handle Ownership.
|
||||||
if( Penumbra.Config.UseOwnerNameForCharacterCollection )
|
if( Penumbra.Config.UseOwnerNameForCharacterCollection )
|
||||||
{
|
{
|
||||||
identifier = _manager.CreatePlayer( identifier.PlayerName, identifier.HomeWorld );
|
identifier = _manager.CreateIndividualUnchecked( IdentifierType.Player, identifier.PlayerName, identifier.HomeWorld, ObjectKind.None, uint.MaxValue );
|
||||||
return CheckWorlds( identifier, out collection );
|
return CheckWorlds( identifier, out collection );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -60,22 +62,9 @@ public sealed partial class IndividualCollections : IReadOnlyList< (string Displ
|
||||||
return CheckWorlds( _manager.GetCurrentPlayer(), out collection );
|
return CheckWorlds( _manager.GetCurrentPlayer(), out collection );
|
||||||
case SpecialActor.ExamineScreen:
|
case SpecialActor.ExamineScreen:
|
||||||
{
|
{
|
||||||
if( CheckWorlds( _manager.GetInspectPlayer(), out collection! ) )
|
return CheckWorlds( _manager.GetInspectPlayer(), out collection! )
|
||||||
{
|
|| CheckWorlds( _manager.GetCardPlayer(), out collection! )
|
||||||
return true;
|
|| CheckWorlds( _manager.GetGlamourPlayer(), out collection! );
|
||||||
}
|
|
||||||
|
|
||||||
if( CheckWorlds( _manager.GetCardPlayer(), out collection! ) )
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if( CheckWorlds( _manager.GetGlamourPlayer(), out collection! ) )
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -172,4 +172,7 @@ public partial class ModCollection
|
||||||
Save();
|
Save();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public override string ToString()
|
||||||
|
=> Name;
|
||||||
}
|
}
|
||||||
|
|
@ -1,141 +1,21 @@
|
||||||
using System;
|
using System;
|
||||||
using System.Diagnostics.CodeAnalysis;
|
using System.Collections;
|
||||||
using Dalamud.Utility.Signatures;
|
using System.Linq;
|
||||||
using FFXIVClientStructs.FFXIV.Client.Game.Character;
|
using Dalamud.Data;
|
||||||
using FFXIVClientStructs.FFXIV.Client.Game.Object;
|
using FFXIVClientStructs.FFXIV.Client.Graphics.Scene;
|
||||||
using FFXIVClientStructs.FFXIV.Client.UI.Agent;
|
|
||||||
using FFXIVClientStructs.FFXIV.Component.GUI;
|
|
||||||
using Lumina.Excel.GeneratedSheets;
|
using Lumina.Excel.GeneratedSheets;
|
||||||
|
using OtterGui;
|
||||||
using Penumbra.Collections;
|
using Penumbra.Collections;
|
||||||
|
using Penumbra.GameData.Actors;
|
||||||
using Penumbra.GameData.Enums;
|
using Penumbra.GameData.Enums;
|
||||||
using Penumbra.String;
|
using Character = FFXIVClientStructs.FFXIV.Client.Game.Character.Character;
|
||||||
using CustomizeData = Penumbra.GameData.Structs.CustomizeData;
|
using GameObject = FFXIVClientStructs.FFXIV.Client.Game.Object.GameObject;
|
||||||
using ObjectKind = Dalamud.Game.ClientState.Objects.Enums.ObjectKind;
|
using ObjectKind = Dalamud.Game.ClientState.Objects.Enums.ObjectKind;
|
||||||
|
|
||||||
namespace Penumbra.Interop.Resolver;
|
namespace Penumbra.Interop.Resolver;
|
||||||
|
|
||||||
public unsafe partial class PathResolver
|
public unsafe partial class PathResolver
|
||||||
{
|
{
|
||||||
[Signature( "0F B7 0D ?? ?? ?? ?? C7 85", ScanType = ScanType.StaticAddress )]
|
|
||||||
private static ushort* _inspectTitleId = null!;
|
|
||||||
|
|
||||||
// Obtain the name of the current player, if one exists.
|
|
||||||
private static string? GetPlayerName()
|
|
||||||
=> Dalamud.Objects[ 0 ]?.Name.ToString();
|
|
||||||
|
|
||||||
// Obtain the name of the inspect target from its window, if it exists.
|
|
||||||
private static string? GetInspectName()
|
|
||||||
{
|
|
||||||
if( !Penumbra.Config.UseCharacterCollectionInInspect )
|
|
||||||
{
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
var addon = Dalamud.GameGui.GetAddonByName( "CharacterInspect", 1 );
|
|
||||||
if( addon == IntPtr.Zero )
|
|
||||||
{
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
var ui = ( AtkUnitBase* )addon;
|
|
||||||
var nodeId = Dalamud.GameData.GetExcelSheet< Title >()?.GetRow( *_inspectTitleId )?.IsPrefix == true ? 7u : 6u;
|
|
||||||
|
|
||||||
var text = ( AtkTextNode* )ui->UldManager.SearchNodeById( nodeId );
|
|
||||||
return text != null && text->AtkResNode.Type == NodeType.Text ? text->NodeText.ToString() : null;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Obtain the name displayed in the Character Card from the agent.
|
|
||||||
private static string? GetCardName()
|
|
||||||
{
|
|
||||||
// TODO: Update to ClientStructs when merged.
|
|
||||||
if( !Penumbra.Config.UseCharacterCollectionsInCards )
|
|
||||||
{
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
var agent = AgentCharaCard.Instance();
|
|
||||||
if( agent == null )
|
|
||||||
{
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
var data = *( byte** )( ( byte* )agent + 0x28 );
|
|
||||||
if( data == null )
|
|
||||||
{
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
var block = data + 0x7A;
|
|
||||||
return new ByteString( block ).ToString();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Obtain the name of the player character if the glamour plate edit window is open.
|
|
||||||
private static string? GetGlamourName()
|
|
||||||
{
|
|
||||||
if( !Penumbra.Config.UseCharacterCollectionInTryOn )
|
|
||||||
{
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
var addon = Dalamud.GameGui.GetAddonByName( "MiragePrismMiragePlate", 1 );
|
|
||||||
return addon == IntPtr.Zero ? null : GetPlayerName();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Guesstimate whether an unnamed cutscene actor corresponds to the player or not,
|
|
||||||
// and if so, return the player name.
|
|
||||||
private static string? GetCutsceneName( GameObject* gameObject )
|
|
||||||
{
|
|
||||||
if( gameObject->Name[ 0 ] != 0 || gameObject->ObjectKind != ( byte )ObjectKind.Player )
|
|
||||||
{
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
var parent = Cutscenes[ gameObject->ObjectIndex ];
|
|
||||||
if( parent != null )
|
|
||||||
{
|
|
||||||
return parent.Name.ToString();
|
|
||||||
}
|
|
||||||
|
|
||||||
// should not really happen but keep it in as a emergency case.
|
|
||||||
var player = Dalamud.Objects[ 0 ];
|
|
||||||
if( player == null )
|
|
||||||
{
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
var customize1 = ( CustomizeData* )( ( Character* )gameObject )->CustomizeData;
|
|
||||||
var customize2 = ( CustomizeData* )( ( Character* )player.Address )->CustomizeData;
|
|
||||||
return customize1->Equals( *customize2 ) ? player.Name.ToString() : null;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Identify the owner of a companion, mount or monster and apply the corresponding collection.
|
|
||||||
// Companions and mounts get set to the actor before them in the table if it exists.
|
|
||||||
// Monsters with a owner use that owner if it exists.
|
|
||||||
private static string? GetOwnerName( GameObject* gameObject )
|
|
||||||
{
|
|
||||||
if( !Penumbra.Config.UseOwnerNameForCharacterCollection )
|
|
||||||
{
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
GameObject* owner = null;
|
|
||||||
if( ( ObjectKind )gameObject->GetObjectKind() is ObjectKind.Companion or ObjectKind.MountType && gameObject->ObjectIndex > 0 )
|
|
||||||
{
|
|
||||||
owner = ( GameObject* )Dalamud.Objects[ gameObject->ObjectIndex - 1 ]?.Address;
|
|
||||||
}
|
|
||||||
else if( gameObject->OwnerID != 0xE0000000 )
|
|
||||||
{
|
|
||||||
owner = ( GameObject* )( Dalamud.Objects.SearchById( gameObject->OwnerID )?.Address ?? IntPtr.Zero );
|
|
||||||
}
|
|
||||||
|
|
||||||
if( owner != null )
|
|
||||||
{
|
|
||||||
return new ByteString( owner->Name ).ToString();
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Identify the correct collection for a GameObject by index and name.
|
// Identify the correct collection for a GameObject by index and name.
|
||||||
private static ResolveData IdentifyCollection( GameObject* gameObject )
|
private static ResolveData IdentifyCollection( GameObject* gameObject )
|
||||||
{
|
{
|
||||||
|
|
@ -152,51 +32,18 @@ public unsafe partial class PathResolver
|
||||||
if( !Dalamud.ClientState.IsLoggedIn )
|
if( !Dalamud.ClientState.IsLoggedIn )
|
||||||
{
|
{
|
||||||
var collection = Penumbra.CollectionManager.ByType( CollectionType.Yourself )
|
var collection = Penumbra.CollectionManager.ByType( CollectionType.Yourself )
|
||||||
?? ( CollectionByActor( string.Empty, gameObject, out var c ) ? c : Penumbra.CollectionManager.Default );
|
?? CollectionByAttributes( gameObject )
|
||||||
|
?? Penumbra.CollectionManager.Default;
|
||||||
return collection.ToResolveData( gameObject );
|
return collection.ToResolveData( gameObject );
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// Housing Retainers
|
var identifier = Penumbra.Actors.FromObject( gameObject );
|
||||||
if( Penumbra.Config.UseDefaultCollectionForRetainers
|
var collection = CollectionByIdentifier( identifier )
|
||||||
&& gameObject->ObjectKind == ( byte )ObjectKind.EventNpc
|
?? CheckYourself( identifier, gameObject )
|
||||||
&& gameObject->DataID is 1011832 or 1011021 ) // cf. "E8 ?? ?? ?? ?? 0F B6 F8 88 45", male or female retainer
|
?? CollectionByAttributes( gameObject )
|
||||||
{
|
?? CheckOwnedCollection( identifier, gameObject )
|
||||||
return Penumbra.CollectionManager.Default.ToResolveData( gameObject );
|
?? Penumbra.CollectionManager.Default;
|
||||||
}
|
|
||||||
|
|
||||||
string? actorName = null;
|
|
||||||
if( Penumbra.Config.PreferNamedCollectionsOverOwners )
|
|
||||||
{
|
|
||||||
// Early return if we prefer the actors own name over its owner.
|
|
||||||
actorName = new ByteString( gameObject->Name ).ToString();
|
|
||||||
if( actorName.Length > 0
|
|
||||||
&& CollectionByActorName( actorName, out var actorCollection ) )
|
|
||||||
{
|
|
||||||
return actorCollection.ToResolveData( gameObject );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// All these special cases are relevant for an empty name, so never collide with the above setting.
|
|
||||||
// Only OwnerName can be applied to something with a non-empty name, and that is the specific case we want to handle.
|
|
||||||
var actualName = gameObject->ObjectIndex switch
|
|
||||||
{
|
|
||||||
240 => Penumbra.Config.UseCharacterCollectionInMainWindow ? GetPlayerName() : null, // character window
|
|
||||||
241 => GetInspectName() ?? GetCardName() ?? GetGlamourName(), // inspect, character card, glamour plate editor.
|
|
||||||
242 => Penumbra.Config.UseCharacterCollectionInTryOn ? GetPlayerName() : null, // try-on
|
|
||||||
243 => Penumbra.Config.UseCharacterCollectionInTryOn ? GetPlayerName() : null, // dye preview
|
|
||||||
244 => Penumbra.Config.UseCharacterCollectionsInCards ? GetPlayerName() : null, // portrait list and editor
|
|
||||||
>= CutsceneCharacters.CutsceneStartIdx and < CutsceneCharacters.CutsceneEndIdx => GetCutsceneName( gameObject ),
|
|
||||||
_ => null,
|
|
||||||
}
|
|
||||||
?? GetOwnerName( gameObject ) ?? actorName ?? new ByteString( gameObject->Name ).ToString();
|
|
||||||
|
|
||||||
// First check temporary character collections, then the own configuration, then special collections.
|
|
||||||
var collection = CollectionByActorName( actualName, out var c )
|
|
||||||
? c
|
|
||||||
: CollectionByActor( actualName, gameObject, out c )
|
|
||||||
? c
|
|
||||||
: Penumbra.CollectionManager.Default;
|
|
||||||
return collection.ToResolveData( gameObject );
|
return collection.ToResolveData( gameObject );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -211,67 +58,102 @@ public unsafe partial class PathResolver
|
||||||
// or the default collection if no player exists.
|
// or the default collection if no player exists.
|
||||||
public static ModCollection PlayerCollection()
|
public static ModCollection PlayerCollection()
|
||||||
{
|
{
|
||||||
var player = Dalamud.ClientState.LocalPlayer;
|
var player = Penumbra.Actors.GetCurrentPlayer();
|
||||||
if( player == null )
|
if( !player.IsValid )
|
||||||
{
|
{
|
||||||
return Penumbra.CollectionManager.Default;
|
return Penumbra.CollectionManager.Default;
|
||||||
}
|
}
|
||||||
|
|
||||||
var name = player.Name.TextValue;
|
return CollectionByIdentifier( player )
|
||||||
if( CollectionByActorName( name, out var c ) )
|
?? CollectionByAttributes( ( GameObject* )Dalamud.Objects[ 0 ]!.Address )
|
||||||
{
|
?? Penumbra.CollectionManager.Default;
|
||||||
return c;
|
|
||||||
}
|
|
||||||
|
|
||||||
if( CollectionByActor( name, ( GameObject* )player.Address, out c ) )
|
|
||||||
{
|
|
||||||
return c;
|
|
||||||
}
|
|
||||||
|
|
||||||
return Penumbra.CollectionManager.Default;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check both temporary and permanent character collections. Temporary first.
|
// Check both temporary and permanent character collections. Temporary first.
|
||||||
private static bool CollectionByActorName( string name, [NotNullWhen( true )] out ModCollection? collection )
|
private static ModCollection? CollectionByIdentifier( ActorIdentifier identifier )
|
||||||
=> Penumbra.TempMods.Collections.TryGetValue( name, out collection )
|
=> Penumbra.TempMods.Collections.TryGetValue( identifier.ToString(), out var collection )
|
||||||
|| Penumbra.CollectionManager.Characters.TryGetValue( name, out collection );
|
|| Penumbra.CollectionManager.Individuals.TryGetCollection( identifier, out collection )
|
||||||
|
? collection
|
||||||
|
: null;
|
||||||
|
|
||||||
// Check special collections given the actor.
|
|
||||||
private static bool CollectionByActor( string name, GameObject* actor, [NotNullWhen( true )] out ModCollection? collection )
|
// Check for the Yourself collection.
|
||||||
|
private static ModCollection? CheckYourself( ActorIdentifier identifier, GameObject* actor )
|
||||||
{
|
{
|
||||||
collection = null;
|
|
||||||
// Check for the Yourself collection.
|
|
||||||
if( actor->ObjectIndex == 0
|
if( actor->ObjectIndex == 0
|
||||||
|| Cutscenes.GetParentIndex( actor->ObjectIndex ) == 0
|
|| Cutscenes.GetParentIndex( actor->ObjectIndex ) == 0
|
||||||
|| name == Dalamud.ClientState.LocalPlayer?.Name.ToString() )
|
|| identifier.Equals( Penumbra.Actors.GetCurrentPlayer() ) )
|
||||||
{
|
{
|
||||||
collection = Penumbra.CollectionManager.ByType( CollectionType.Yourself );
|
return Penumbra.CollectionManager.ByType( CollectionType.Yourself );
|
||||||
if( collection != null )
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if( actor->IsCharacter() )
|
return null;
|
||||||
{
|
}
|
||||||
var character = ( Character* )actor;
|
|
||||||
// Only handle human models.
|
|
||||||
if( character->ModelCharaId == 0 )
|
|
||||||
{
|
|
||||||
var race = ( SubRace )character->CustomizeData[ 4 ];
|
|
||||||
var gender = ( Gender )( character->CustomizeData[ 1 ] + 1 );
|
|
||||||
var isNpc = actor->ObjectKind != ( byte )ObjectKind.Player;
|
|
||||||
|
|
||||||
var type = CollectionTypeExtensions.FromParts( race, gender, isNpc );
|
// Check special collections given the actor.
|
||||||
collection = Penumbra.CollectionManager.ByType( type );
|
private static ModCollection? CollectionByAttributes( GameObject* actor )
|
||||||
collection ??= Penumbra.CollectionManager.ByType( CollectionTypeExtensions.FromParts( gender, isNpc ) );
|
{
|
||||||
if( collection != null )
|
if( !actor->IsCharacter() )
|
||||||
{
|
{
|
||||||
return true;
|
return null;
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
// Only handle human models.
|
||||||
|
var character = ( Character* )actor;
|
||||||
|
if( character->ModelCharaId >= 0 && character->ModelCharaId < ValidHumanModels.Count && ValidHumanModels[ character->ModelCharaId ] )
|
||||||
|
{
|
||||||
|
var race = ( SubRace )character->CustomizeData[ 4 ];
|
||||||
|
var gender = ( Gender )( character->CustomizeData[ 1 ] + 1 );
|
||||||
|
var isNpc = actor->ObjectKind != ( byte )ObjectKind.Player;
|
||||||
|
|
||||||
|
var type = CollectionTypeExtensions.FromParts( race, gender, isNpc );
|
||||||
|
var collection = Penumbra.CollectionManager.ByType( type );
|
||||||
|
collection ??= Penumbra.CollectionManager.ByType( CollectionTypeExtensions.FromParts( gender, isNpc ) );
|
||||||
|
return collection;
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the collection applying to the owner if it is available.
|
||||||
|
private static ModCollection? CheckOwnedCollection( ActorIdentifier identifier, GameObject* obj )
|
||||||
|
{
|
||||||
|
if( identifier.Type != IdentifierType.Owned || !Penumbra.Config.UseOwnerNameForCharacterCollection )
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
var owner = identifier.Kind switch
|
||||||
|
{
|
||||||
|
ObjectKind.BattleNpc when obj->OwnerID != 0xE0000000 => ( GameObject* )( Dalamud.Objects.SearchById( obj->OwnerID )?.Address ?? IntPtr.Zero ),
|
||||||
|
ObjectKind.MountType when obj->ObjectIndex % 2 == 1 => ( GameObject* )Dalamud.Objects.GetObjectAddress( obj->ObjectIndex - 1 ),
|
||||||
|
ObjectKind.Companion when obj->ObjectIndex % 2 == 1 => ( GameObject* )Dalamud.Objects.GetObjectAddress( obj->ObjectIndex - 1 ),
|
||||||
|
( ObjectKind )15 when obj->ObjectIndex % 2 == 1 => ( GameObject* )Dalamud.Objects.GetObjectAddress( obj->ObjectIndex - 1 ), // TODO: CS Update
|
||||||
|
_ => null,
|
||||||
|
};
|
||||||
|
|
||||||
|
if( owner == null )
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
var id = Penumbra.Actors.CreateIndividualUnchecked( IdentifierType.Player, identifier.PlayerName, identifier.HomeWorld, ObjectKind.None, uint.MaxValue );
|
||||||
|
return CheckYourself( id, owner )
|
||||||
|
?? CollectionByAttributes( owner );
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Go through all ModelChara rows and return a bitfield of those that resolve to human models.
|
||||||
|
/// </summary>
|
||||||
|
private static BitArray GetValidHumanModels( DataManager gameData )
|
||||||
|
{
|
||||||
|
var sheet = gameData.GetExcelSheet< ModelChara >()!;
|
||||||
|
var ret = new BitArray( ( int )sheet.RowCount, false );
|
||||||
|
foreach( var (row, idx) in sheet.WithIndex().Where( p => p.Value.Type == ( byte )CharacterBase.ModelType.Human ) )
|
||||||
|
{
|
||||||
|
ret[ idx ] = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1,9 +1,14 @@
|
||||||
using System;
|
using System;
|
||||||
|
using System.Collections;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using Dalamud.Data;
|
||||||
using Dalamud.Utility.Signatures;
|
using Dalamud.Utility.Signatures;
|
||||||
using FFXIVClientStructs.FFXIV.Client.Game.Object;
|
using FFXIVClientStructs.FFXIV.Client.Game.Object;
|
||||||
using FFXIVClientStructs.FFXIV.Client.Graphics.Scene;
|
using FFXIVClientStructs.FFXIV.Client.Graphics.Scene;
|
||||||
using FFXIVClientStructs.FFXIV.Client.System.Resource;
|
using FFXIVClientStructs.FFXIV.Client.System.Resource;
|
||||||
|
using Lumina.Excel.GeneratedSheets;
|
||||||
|
using OtterGui;
|
||||||
using Penumbra.Collections;
|
using Penumbra.Collections;
|
||||||
using Penumbra.GameData.Enums;
|
using Penumbra.GameData.Enums;
|
||||||
using Penumbra.Interop.Loader;
|
using Penumbra.Interop.Loader;
|
||||||
|
|
@ -24,11 +29,15 @@ public partial class PathResolver : IDisposable
|
||||||
private readonly ResourceLoader _loader;
|
private readonly ResourceLoader _loader;
|
||||||
private static readonly CutsceneCharacters Cutscenes = new();
|
private static readonly CutsceneCharacters Cutscenes = new();
|
||||||
private static readonly DrawObjectState DrawObjects = new();
|
private static readonly DrawObjectState DrawObjects = new();
|
||||||
|
private static readonly BitArray ValidHumanModels;
|
||||||
private readonly AnimationState _animations;
|
private readonly AnimationState _animations;
|
||||||
private readonly PathState _paths;
|
private readonly PathState _paths;
|
||||||
private readonly MetaState _meta;
|
private readonly MetaState _meta;
|
||||||
private readonly MaterialState _materials;
|
private readonly MaterialState _materials;
|
||||||
|
|
||||||
|
static PathResolver()
|
||||||
|
=> ValidHumanModels = GetValidHumanModels( Dalamud.GameData );
|
||||||
|
|
||||||
public unsafe PathResolver( ResourceLoader loader )
|
public unsafe PathResolver( ResourceLoader loader )
|
||||||
{
|
{
|
||||||
SignatureHelper.Initialise( this );
|
SignatureHelper.Initialise( this );
|
||||||
|
|
|
||||||
|
|
@ -332,7 +332,7 @@ public class Penumbra : IDalamudPlugin
|
||||||
}
|
}
|
||||||
|
|
||||||
string? characterName = null;
|
string? characterName = null;
|
||||||
if( type is CollectionType.Character )
|
if( type is CollectionType.Individual )
|
||||||
{
|
{
|
||||||
var split = collectionName.Split( '|', 2, StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries );
|
var split = collectionName.Split( '|', 2, StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries );
|
||||||
if( split.Length < 2 || split[ 0 ].Length == 0 || split[ 1 ].Length == 0 )
|
if( split.Length < 2 || split[ 0 ].Length == 0 || split[ 1 ].Length == 0 )
|
||||||
|
|
@ -368,7 +368,7 @@ public class Penumbra : IDalamudPlugin
|
||||||
{
|
{
|
||||||
CollectionManager.CreateSpecialCollection( type );
|
CollectionManager.CreateSpecialCollection( type );
|
||||||
}
|
}
|
||||||
else if( type is CollectionType.Character )
|
else if( type is CollectionType.Individual )
|
||||||
{
|
{
|
||||||
CollectionManager.CreateCharacterCollection( characterName! );
|
CollectionManager.CreateCharacterCollection( characterName! );
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -126,7 +126,7 @@ public partial class ConfigWindow
|
||||||
{
|
{
|
||||||
var (name, collection) = Penumbra.CollectionManager.Individuals[ i ];
|
var (name, collection) = Penumbra.CollectionManager.Individuals[ i ];
|
||||||
using var id = ImRaii.PushId( i );
|
using var id = ImRaii.PushId( i );
|
||||||
DrawCollectionSelector( string.Empty, _window._inputTextWidth.X, CollectionType.Character, true, name );
|
DrawCollectionSelector( string.Empty, _window._inputTextWidth.X, CollectionType.Individual, true, name );
|
||||||
ImGui.SameLine();
|
ImGui.SameLine();
|
||||||
if( ImGuiUtil.DrawDisabledButton( FontAwesomeIcon.Trash.ToIconString(), _window._iconButtonSize, string.Empty,
|
if( ImGuiUtil.DrawDisabledButton( FontAwesomeIcon.Trash.ToIconString(), _window._iconButtonSize, string.Empty,
|
||||||
false, true ) )
|
false, true ) )
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue