mirror of
https://github.com/xivdev/Penumbra.git
synced 2026-01-03 06:13:45 +01:00
Extract Strings to separate submodule.
This commit is contained in:
parent
bc901f3ff6
commit
35baba18bf
75 changed files with 751 additions and 1657 deletions
152
Penumbra.GameData/Actors/ActorIdentifier.cs
Normal file
152
Penumbra.GameData/Actors/ActorIdentifier.cs
Normal file
|
|
@ -0,0 +1,152 @@
|
|||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
using Dalamud.Game.ClientState.Objects.Enums;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using Penumbra.String;
|
||||
|
||||
namespace Penumbra.GameData.Actors;
|
||||
|
||||
[StructLayout( LayoutKind.Explicit )]
|
||||
public readonly struct ActorIdentifier : IEquatable< ActorIdentifier >
|
||||
{
|
||||
public static ActorManager? Manager;
|
||||
|
||||
public static readonly ActorIdentifier Invalid = new(IdentifierType.Invalid, 0, 0, 0, ByteString.Empty);
|
||||
|
||||
// @formatter:off
|
||||
[FieldOffset( 0 )] public readonly IdentifierType Type; // All
|
||||
[FieldOffset( 1 )] public readonly ObjectKind Kind; // Npc, Owned
|
||||
[FieldOffset( 2 )] public readonly ushort HomeWorld; // Player, Owned
|
||||
[FieldOffset( 2 )] public readonly ushort Index; // NPC
|
||||
[FieldOffset( 2 )] public readonly SpecialActor Special; // Special
|
||||
[FieldOffset( 4 )] public readonly uint DataId; // Owned, NPC
|
||||
[FieldOffset( 8 )] public readonly ByteString PlayerName; // Player, Owned
|
||||
// @formatter:on
|
||||
|
||||
public ActorIdentifier CreatePermanent()
|
||||
=> new(Type, Kind, Index, DataId, PlayerName.Clone());
|
||||
|
||||
public bool Equals( ActorIdentifier other )
|
||||
{
|
||||
if( Type != other.Type )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return Type switch
|
||||
{
|
||||
IdentifierType.Player => HomeWorld == other.HomeWorld && PlayerName.EqualsCi( other.PlayerName ),
|
||||
IdentifierType.Owned => HomeWorld == other.HomeWorld && PlayerName.EqualsCi( other.PlayerName ) && Manager.DataIdEquals( this, other ),
|
||||
IdentifierType.Special => Special == other.Special,
|
||||
IdentifierType.Npc => Index == other.Index && DataId == other.DataId && Manager.DataIdEquals( this, other ),
|
||||
_ => false,
|
||||
};
|
||||
}
|
||||
|
||||
public override bool Equals( object? obj )
|
||||
=> obj is ActorIdentifier other && Equals( other );
|
||||
|
||||
public bool IsValid
|
||||
=> Type != IdentifierType.Invalid;
|
||||
|
||||
public override string ToString()
|
||||
=> Manager?.ToString( this )
|
||||
?? Type switch
|
||||
{
|
||||
IdentifierType.Player => $"{PlayerName} ({HomeWorld})",
|
||||
IdentifierType.Owned => $"{PlayerName}s {Kind} {DataId} ({HomeWorld})",
|
||||
IdentifierType.Special => ActorManager.ToName( Special ),
|
||||
IdentifierType.Npc =>
|
||||
Index == ushort.MaxValue
|
||||
? $"{Kind} #{DataId}"
|
||||
: $"{Kind} #{DataId} at {Index}",
|
||||
_ => "Invalid",
|
||||
};
|
||||
|
||||
public override int GetHashCode()
|
||||
=> Type switch
|
||||
{
|
||||
IdentifierType.Player => HashCode.Combine( IdentifierType.Player, PlayerName, HomeWorld ),
|
||||
IdentifierType.Owned => HashCode.Combine( IdentifierType.Owned, Kind, PlayerName, HomeWorld, DataId ),
|
||||
IdentifierType.Special => HashCode.Combine( IdentifierType.Special, Special ),
|
||||
IdentifierType.Npc => HashCode.Combine( IdentifierType.Npc, Kind, Index, DataId ),
|
||||
_ => 0,
|
||||
};
|
||||
|
||||
internal ActorIdentifier( IdentifierType type, ObjectKind kind, ushort index, uint data, ByteString playerName )
|
||||
{
|
||||
Type = type;
|
||||
Kind = kind;
|
||||
Special = ( SpecialActor )index;
|
||||
HomeWorld = Index = index;
|
||||
DataId = data;
|
||||
PlayerName = playerName;
|
||||
}
|
||||
|
||||
|
||||
public JObject ToJson()
|
||||
{
|
||||
var ret = new JObject { { nameof( Type ), Type.ToString() } };
|
||||
switch( Type )
|
||||
{
|
||||
case IdentifierType.Player:
|
||||
ret.Add( nameof( PlayerName ), PlayerName.ToString() );
|
||||
ret.Add( nameof( HomeWorld ), HomeWorld );
|
||||
return ret;
|
||||
case IdentifierType.Owned:
|
||||
ret.Add( nameof( PlayerName ), PlayerName.ToString() );
|
||||
ret.Add( nameof( HomeWorld ), HomeWorld );
|
||||
ret.Add( nameof( Kind ), Kind.ToString() );
|
||||
ret.Add( nameof( DataId ), DataId );
|
||||
return ret;
|
||||
case IdentifierType.Special:
|
||||
ret.Add( nameof( Special ), Special.ToString() );
|
||||
return ret;
|
||||
case IdentifierType.Npc:
|
||||
ret.Add( nameof( Kind ), Kind.ToString() );
|
||||
ret.Add( nameof( Index ), Index );
|
||||
ret.Add( nameof( DataId ), DataId );
|
||||
return ret;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
public static class ActorManagerExtensions
|
||||
{
|
||||
public static bool DataIdEquals( this ActorManager? manager, ActorIdentifier lhs, ActorIdentifier rhs )
|
||||
{
|
||||
if( lhs.Kind != rhs.Kind )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if( lhs.DataId == rhs.DataId )
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
if( manager == null )
|
||||
{
|
||||
return lhs.Kind == rhs.Kind && lhs.DataId == rhs.DataId || lhs.DataId == uint.MaxValue || rhs.DataId == uint.MaxValue;
|
||||
}
|
||||
|
||||
return lhs.Kind switch
|
||||
{
|
||||
ObjectKind.MountType => manager.Mounts.TryGetValue( lhs.DataId, out var lhsName )
|
||||
&& manager.Mounts.TryGetValue( rhs.DataId, out var rhsName )
|
||||
&& lhsName.Equals( rhsName, StringComparison.OrdinalIgnoreCase ),
|
||||
ObjectKind.Companion => manager.Companions.TryGetValue( lhs.DataId, out var lhsName )
|
||||
&& manager.Companions.TryGetValue( rhs.DataId, out var rhsName )
|
||||
&& lhsName.Equals( rhsName, StringComparison.OrdinalIgnoreCase ),
|
||||
ObjectKind.BattleNpc => manager.BNpcs.TryGetValue( lhs.DataId, out var lhsName )
|
||||
&& manager.BNpcs.TryGetValue( rhs.DataId, out var rhsName )
|
||||
&& lhsName.Equals( rhsName, StringComparison.OrdinalIgnoreCase ),
|
||||
ObjectKind.EventNpc => manager.ENpcs.TryGetValue( lhs.DataId, out var lhsName )
|
||||
&& manager.ENpcs.TryGetValue( rhs.DataId, out var rhsName )
|
||||
&& lhsName.Equals( rhsName, StringComparison.OrdinalIgnoreCase ),
|
||||
_ => false,
|
||||
};
|
||||
}
|
||||
}
|
||||
352
Penumbra.GameData/Actors/ActorManager.cs
Normal file
352
Penumbra.GameData/Actors/ActorManager.cs
Normal file
|
|
@ -0,0 +1,352 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using Dalamud.Data;
|
||||
using Dalamud.Game.ClientState;
|
||||
using Dalamud.Game.ClientState.Objects;
|
||||
using Dalamud.Game.ClientState.Objects.Enums;
|
||||
using Dalamud.Game.ClientState.Objects.Types;
|
||||
using Dalamud.Utility;
|
||||
using Lumina.Excel.GeneratedSheets;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using Penumbra.String;
|
||||
|
||||
namespace Penumbra.GameData.Actors;
|
||||
|
||||
public class ActorManager
|
||||
{
|
||||
private readonly ObjectTable _objects;
|
||||
private readonly ClientState _clientState;
|
||||
|
||||
public readonly IReadOnlyDictionary< ushort, string > Worlds;
|
||||
public readonly IReadOnlyDictionary< uint, string > Mounts;
|
||||
public readonly IReadOnlyDictionary< uint, string > Companions;
|
||||
public readonly IReadOnlyDictionary< uint, string > BNpcs;
|
||||
public readonly IReadOnlyDictionary< uint, string > ENpcs;
|
||||
|
||||
public IEnumerable< KeyValuePair< ushort, string > > AllWorlds
|
||||
=> Worlds.OrderBy( kvp => kvp.Key ).Prepend( new KeyValuePair< ushort, string >( ushort.MaxValue, "Any World" ) );
|
||||
|
||||
private readonly Func< ushort, short > _toParentIdx;
|
||||
|
||||
public ActorManager( ObjectTable objects, ClientState state, DataManager gameData, Func< ushort, short > toParentIdx )
|
||||
{
|
||||
_objects = objects;
|
||||
_clientState = state;
|
||||
Worlds = gameData.GetExcelSheet< World >()!
|
||||
.Where( w => w.IsPublic && !w.Name.RawData.IsEmpty )
|
||||
.ToDictionary( w => ( ushort )w.RowId, w => w.Name.ToString() );
|
||||
|
||||
Mounts = gameData.GetExcelSheet< Mount >()!
|
||||
.Where( m => m.Singular.RawData.Length > 0 && m.Order >= 0 )
|
||||
.ToDictionary( m => m.RowId, m => CultureInfo.InvariantCulture.TextInfo.ToTitleCase( m.Singular.ToDalamudString().ToString() ) );
|
||||
Companions = gameData.GetExcelSheet< Companion >()!
|
||||
.Where( c => c.Singular.RawData.Length > 0 && c.Order < ushort.MaxValue )
|
||||
.ToDictionary( c => c.RowId, c => CultureInfo.InvariantCulture.TextInfo.ToTitleCase( c.Singular.ToDalamudString().ToString() ) );
|
||||
|
||||
BNpcs = gameData.GetExcelSheet< BNpcName >()!
|
||||
.Where( n => n.Singular.RawData.Length > 0 )
|
||||
.ToDictionary( n => n.RowId, n => CultureInfo.InvariantCulture.TextInfo.ToTitleCase( n.Singular.ToDalamudString().ToString() ) );
|
||||
|
||||
ENpcs = gameData.GetExcelSheet< ENpcResident >()!
|
||||
.Where( e => e.Singular.RawData.Length > 0 )
|
||||
.ToDictionary( e => e.RowId, e => CultureInfo.InvariantCulture.TextInfo.ToTitleCase( e.Singular.ToDalamudString().ToString() ) );
|
||||
|
||||
_toParentIdx = toParentIdx;
|
||||
|
||||
ActorIdentifier.Manager = this;
|
||||
}
|
||||
|
||||
public ActorIdentifier FromJson( JObject data )
|
||||
{
|
||||
var type = data[ nameof( ActorIdentifier.Type ) ]?.Value< IdentifierType >() ?? IdentifierType.Invalid;
|
||||
switch( type )
|
||||
{
|
||||
case IdentifierType.Player:
|
||||
{
|
||||
var name = ByteString.FromStringUnsafe( data[ nameof( ActorIdentifier.PlayerName ) ]?.Value< string >(), false );
|
||||
var homeWorld = data[ nameof( ActorIdentifier.HomeWorld ) ]?.Value< ushort >() ?? 0;
|
||||
return CreatePlayer( name, homeWorld );
|
||||
}
|
||||
case IdentifierType.Owned:
|
||||
{
|
||||
var name = ByteString.FromStringUnsafe( data[ nameof( ActorIdentifier.PlayerName ) ]?.Value< string >(), false );
|
||||
var homeWorld = data[ nameof( ActorIdentifier.HomeWorld ) ]?.Value< ushort >() ?? 0;
|
||||
var kind = data[ nameof( ActorIdentifier.Kind ) ]?.Value< ObjectKind >() ?? ObjectKind.CardStand;
|
||||
var dataId = data[ nameof( ActorIdentifier.DataId ) ]?.Value< uint >() ?? 0;
|
||||
return CreateOwned( name, homeWorld, kind, dataId );
|
||||
}
|
||||
case IdentifierType.Special:
|
||||
{
|
||||
var special = data[ nameof( ActorIdentifier.Special ) ]?.Value< SpecialActor >() ?? 0;
|
||||
return CreateSpecial( special );
|
||||
}
|
||||
case IdentifierType.Npc:
|
||||
{
|
||||
var index = data[ nameof( ActorIdentifier.Index ) ]?.Value< ushort >() ?? 0;
|
||||
var kind = data[ nameof( ActorIdentifier.Kind ) ]?.Value< ObjectKind >() ?? ObjectKind.CardStand;
|
||||
var dataId = data[ nameof( ActorIdentifier.DataId ) ]?.Value< uint >() ?? 0;
|
||||
return CreateNpc( kind, index, dataId );
|
||||
}
|
||||
case IdentifierType.Invalid:
|
||||
default:
|
||||
return ActorIdentifier.Invalid;
|
||||
}
|
||||
}
|
||||
|
||||
public string ToString( ActorIdentifier id )
|
||||
{
|
||||
return id.Type switch
|
||||
{
|
||||
IdentifierType.Player => id.HomeWorld != _clientState.LocalPlayer?.HomeWorld.Id
|
||||
? $"{id.PlayerName} ({Worlds[ id.HomeWorld ]})"
|
||||
: id.PlayerName.ToString(),
|
||||
IdentifierType.Owned => id.HomeWorld != _clientState.LocalPlayer?.HomeWorld.Id
|
||||
? $"{id.PlayerName} ({Worlds[ id.HomeWorld ]})'s {ToName( id.Kind, id.DataId )}"
|
||||
: $"{id.PlayerName}s {ToName( id.Kind, id.DataId )}",
|
||||
IdentifierType.Special => ToName( id.Special ),
|
||||
IdentifierType.Npc =>
|
||||
id.Index == ushort.MaxValue
|
||||
? ToName( id.Kind, id.DataId )
|
||||
: $"{ToName( id.Kind, id.DataId )} at {id.Index}",
|
||||
_ => "Invalid",
|
||||
};
|
||||
}
|
||||
|
||||
public static string ToName( SpecialActor actor )
|
||||
=> actor switch
|
||||
{
|
||||
SpecialActor.CharacterScreen => "Character Screen Actor",
|
||||
SpecialActor.ExamineScreen => "Examine Screen Actor",
|
||||
SpecialActor.FittingRoom => "Fitting Room Actor",
|
||||
SpecialActor.DyePreview => "Dye Preview Actor",
|
||||
SpecialActor.Portrait => "Portrait Actor",
|
||||
_ => "Invalid",
|
||||
};
|
||||
|
||||
public string ToName( ObjectKind kind, uint dataId )
|
||||
=> TryGetName( kind, dataId, out var ret ) ? ret : "Invalid";
|
||||
|
||||
public bool TryGetName( ObjectKind kind, uint dataId, [NotNullWhen( true )] out string? name )
|
||||
{
|
||||
name = null;
|
||||
return kind switch
|
||||
{
|
||||
ObjectKind.MountType => Mounts.TryGetValue( dataId, out name ),
|
||||
ObjectKind.Companion => Companions.TryGetValue( dataId, out name ),
|
||||
ObjectKind.BattleNpc => BNpcs.TryGetValue( dataId, out name ),
|
||||
ObjectKind.EventNpc => ENpcs.TryGetValue( dataId, out name ),
|
||||
_ => false,
|
||||
};
|
||||
}
|
||||
|
||||
public unsafe ActorIdentifier FromObject( FFXIVClientStructs.FFXIV.Client.Game.Object.GameObject* actor )
|
||||
{
|
||||
if( actor == null )
|
||||
{
|
||||
return ActorIdentifier.Invalid;
|
||||
}
|
||||
|
||||
var idx = actor->ObjectIndex;
|
||||
if( idx is >= ( ushort )SpecialActor.CutsceneStart and < ( ushort )SpecialActor.CutsceneEnd )
|
||||
{
|
||||
var parentIdx = _toParentIdx( idx );
|
||||
if( parentIdx >= 0 )
|
||||
{
|
||||
return FromObject( ( FFXIVClientStructs.FFXIV.Client.Game.Object.GameObject* )_objects.GetObjectAddress( parentIdx ) );
|
||||
}
|
||||
}
|
||||
else if( idx is >= ( ushort )SpecialActor.CharacterScreen and <= ( ushort )SpecialActor.Portrait )
|
||||
{
|
||||
return CreateSpecial( ( SpecialActor )idx );
|
||||
}
|
||||
|
||||
switch( ( ObjectKind )actor->ObjectKind )
|
||||
{
|
||||
case ObjectKind.Player:
|
||||
{
|
||||
var name = new ByteString( actor->Name );
|
||||
var homeWorld = ( ( FFXIVClientStructs.FFXIV.Client.Game.Character.Character* )actor )->HomeWorld;
|
||||
return CreatePlayer( name, homeWorld );
|
||||
}
|
||||
case ObjectKind.BattleNpc:
|
||||
{
|
||||
var ownerId = actor->OwnerID;
|
||||
if( ownerId != 0xE0000000 )
|
||||
{
|
||||
var owner = ( FFXIVClientStructs.FFXIV.Client.Game.Character.Character* )( _objects.SearchById( ownerId )?.Address ?? IntPtr.Zero );
|
||||
if( owner == null )
|
||||
{
|
||||
return ActorIdentifier.Invalid;
|
||||
}
|
||||
|
||||
var name = new ByteString( owner->GameObject.Name );
|
||||
var homeWorld = owner->HomeWorld;
|
||||
return CreateOwned( name, homeWorld, ObjectKind.BattleNpc, ( ( FFXIVClientStructs.FFXIV.Client.Game.Character.Character* )actor )->NameID );
|
||||
}
|
||||
|
||||
return CreateNpc( ObjectKind.BattleNpc, actor->ObjectIndex, ( ( FFXIVClientStructs.FFXIV.Client.Game.Character.Character* )actor )->NameID );
|
||||
}
|
||||
case ObjectKind.EventNpc: return CreateNpc( ObjectKind.EventNpc, actor->ObjectIndex, actor->DataID );
|
||||
case ObjectKind.MountType:
|
||||
case ObjectKind.Companion:
|
||||
{
|
||||
if( actor->ObjectIndex % 2 == 0 )
|
||||
{
|
||||
return ActorIdentifier.Invalid;
|
||||
}
|
||||
|
||||
var owner = ( FFXIVClientStructs.FFXIV.Client.Game.Character.Character* )_objects.GetObjectAddress( actor->ObjectIndex - 1 );
|
||||
if( owner == null )
|
||||
{
|
||||
return ActorIdentifier.Invalid;
|
||||
}
|
||||
|
||||
var dataId = GetCompanionId( actor, &owner->GameObject );
|
||||
return CreateOwned( new ByteString( owner->GameObject.Name ), owner->HomeWorld, ( ObjectKind )actor->ObjectKind, dataId );
|
||||
}
|
||||
default: return ActorIdentifier.Invalid;
|
||||
}
|
||||
}
|
||||
|
||||
private unsafe uint GetCompanionId( FFXIVClientStructs.FFXIV.Client.Game.Object.GameObject* actor, FFXIVClientStructs.FFXIV.Client.Game.Object.GameObject* owner )
|
||||
{
|
||||
return ( ObjectKind )actor->ObjectKind switch
|
||||
{
|
||||
ObjectKind.MountType => *( ushort* )( ( byte* )owner + 0x668 ),
|
||||
ObjectKind.Companion => *( ushort* )( ( byte* )actor + 0x1AAC ),
|
||||
_ => actor->DataID,
|
||||
};
|
||||
}
|
||||
|
||||
public unsafe ActorIdentifier FromObject( GameObject? actor )
|
||||
=> FromObject( ( FFXIVClientStructs.FFXIV.Client.Game.Object.GameObject* )( actor?.Address ?? IntPtr.Zero ) );
|
||||
|
||||
|
||||
public ActorIdentifier CreatePlayer( ByteString name, ushort homeWorld )
|
||||
{
|
||||
if( !VerifyWorld( homeWorld ) || !VerifyPlayerName( name ) )
|
||||
{
|
||||
return ActorIdentifier.Invalid;
|
||||
}
|
||||
|
||||
return new ActorIdentifier( IdentifierType.Player, ObjectKind.Player, homeWorld, 0, name );
|
||||
}
|
||||
|
||||
public ActorIdentifier CreateSpecial( SpecialActor actor )
|
||||
{
|
||||
if( !VerifySpecial( actor ) )
|
||||
{
|
||||
return ActorIdentifier.Invalid;
|
||||
}
|
||||
|
||||
return new ActorIdentifier( IdentifierType.Special, ObjectKind.Player, ( ushort )actor, 0, ByteString.Empty );
|
||||
}
|
||||
|
||||
public ActorIdentifier CreateNpc( ObjectKind kind, ushort index = ushort.MaxValue, uint data = uint.MaxValue )
|
||||
{
|
||||
if( !VerifyIndex( index ) || !VerifyNpcData( kind, data ) )
|
||||
{
|
||||
return ActorIdentifier.Invalid;
|
||||
}
|
||||
|
||||
return new ActorIdentifier( IdentifierType.Npc, kind, index, data, ByteString.Empty );
|
||||
}
|
||||
|
||||
public ActorIdentifier CreateOwned( ByteString ownerName, ushort homeWorld, ObjectKind kind, uint dataId )
|
||||
{
|
||||
if( !VerifyWorld( homeWorld ) || !VerifyPlayerName( ownerName ) || !VerifyOwnedData( kind, dataId ) )
|
||||
{
|
||||
return ActorIdentifier.Invalid;
|
||||
}
|
||||
|
||||
return new ActorIdentifier( IdentifierType.Owned, kind, homeWorld, dataId, ownerName );
|
||||
}
|
||||
|
||||
|
||||
/// <summary> Checks SE naming rules. </summary>
|
||||
private static bool VerifyPlayerName( ByteString name )
|
||||
{
|
||||
// Total no more than 20 characters + space.
|
||||
if( name.Length is < 5 or > 21 )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
var split = name.Split( ( byte )' ' );
|
||||
|
||||
// Forename and surname, no more spaces.
|
||||
if( split.Count != 2 )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool CheckNamePart( ByteString part )
|
||||
{
|
||||
// Each name part at least 2 and at most 15 characters.
|
||||
if( part.Length is < 2 or > 15 )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Each part starting with capitalized letter.
|
||||
if( part[ 0 ] is < ( byte )'A' or > ( byte )'Z' )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Every other symbol needs to be lowercase letter, hyphen or apostrophe.
|
||||
if( part.Skip( 1 ).Any( c => c != ( byte )'\'' && c != ( byte )'-' && c is < ( byte )'a' or > ( byte )'z' ) )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
var hyphens = part.Split( ( byte )'-' );
|
||||
// Apostrophes can not be used in succession, after or before apostrophes.
|
||||
return !hyphens.Any( p => p.Length == 0 || p[ 0 ] == ( byte )'\'' || p.Last() == ( byte )'\'' );
|
||||
}
|
||||
|
||||
return CheckNamePart( split[ 0 ] ) && CheckNamePart( split[ 1 ] );
|
||||
}
|
||||
|
||||
/// <summary> Checks if the world is a valid public world or ushort.MaxValue (any world). </summary>
|
||||
private bool VerifyWorld( ushort worldId )
|
||||
=> Worlds.ContainsKey( worldId );
|
||||
|
||||
/// <summary> Verify that the enum value is a specific actor and return the name if it is. </summary>
|
||||
private static bool VerifySpecial( SpecialActor actor )
|
||||
=> actor is >= SpecialActor.CharacterScreen and <= SpecialActor.Portrait;
|
||||
|
||||
/// <summary> Verify that the object index is a valid index for an NPC. </summary>
|
||||
private static bool VerifyIndex( ushort index )
|
||||
{
|
||||
return index switch
|
||||
{
|
||||
< 200 => index % 2 == 0,
|
||||
> ( ushort )SpecialActor.Portrait => index < 426,
|
||||
_ => false,
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary> Verify that the object kind is a valid owned object, and the corresponding data Id. </summary>
|
||||
private bool VerifyOwnedData( ObjectKind kind, uint dataId )
|
||||
{
|
||||
return kind switch
|
||||
{
|
||||
ObjectKind.MountType => Mounts.ContainsKey( dataId ),
|
||||
ObjectKind.Companion => Companions.ContainsKey( dataId ),
|
||||
ObjectKind.BattleNpc => BNpcs.ContainsKey( dataId ),
|
||||
_ => false,
|
||||
};
|
||||
}
|
||||
|
||||
private bool VerifyNpcData( ObjectKind kind, uint dataId )
|
||||
=> kind switch
|
||||
{
|
||||
ObjectKind.BattleNpc => BNpcs.ContainsKey( dataId ),
|
||||
ObjectKind.EventNpc => ENpcs.ContainsKey( dataId ),
|
||||
_ => false,
|
||||
};
|
||||
}
|
||||
10
Penumbra.GameData/Actors/IdentifierType.cs
Normal file
10
Penumbra.GameData/Actors/IdentifierType.cs
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
namespace Penumbra.GameData.Actors;
|
||||
|
||||
public enum IdentifierType : byte
|
||||
{
|
||||
Invalid,
|
||||
Player,
|
||||
Owned,
|
||||
Special,
|
||||
Npc,
|
||||
};
|
||||
12
Penumbra.GameData/Actors/SpecialActor.cs
Normal file
12
Penumbra.GameData/Actors/SpecialActor.cs
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
namespace Penumbra.GameData.Actors;
|
||||
|
||||
public enum SpecialActor : ushort
|
||||
{
|
||||
CutsceneStart = 200,
|
||||
CutsceneEnd = 240,
|
||||
CharacterScreen = 240,
|
||||
ExamineScreen = 241,
|
||||
FittingRoom = 242,
|
||||
DyePreview = 243,
|
||||
Portrait = 244,
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue