More stuff.

This commit is contained in:
Ottermandias 2022-11-06 18:28:20 +01:00
parent dad146d043
commit 1a4672a901
23 changed files with 616 additions and 1005 deletions

View file

@ -1,375 +0,0 @@
using System;
using Dalamud.Game.ClientState.Objects.Enums;
using Dalamud.Utility;
using Lumina.Excel.GeneratedSheets;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using Penumbra.GameData.ByteString;
namespace Glamourer.Interop;
public unsafe partial struct Actor
{
public interface IIdentifier : IEquatable<IIdentifier>
{
Utf8String Name { get; }
public bool IsValid { get; }
public IIdentifier CreatePermanent();
public static readonly InvalidIdentifier Invalid = new();
public void ToJson(JsonTextWriter j);
public static IIdentifier? FromJson(JObject j)
{
switch (j["Type"]?.Value<string>() ?? string.Empty)
{
case nameof(PlayerIdentifier):
{
var name = j[nameof(Name)]?.Value<string>();
if (name.IsNullOrEmpty())
return null;
var serverId = j[nameof(PlayerIdentifier.HomeWorld)]?.Value<ushort>() ?? ushort.MaxValue;
return new PlayerIdentifier(Utf8String.FromStringUnsafe(name, false), serverId);
}
case nameof(SpecialIdentifier):
{
var index = j[nameof(SpecialIdentifier.Index)]?.Value<ushort>() ?? ushort.MaxValue;
return new SpecialIdentifier(index);
}
case nameof(OwnedIdentifier):
{
var name = j[nameof(Name)]?.Value<string>();
if (name.IsNullOrEmpty())
return null;
var ownerName = j[nameof(OwnedIdentifier.OwnerName)]?.Value<string>();
if (ownerName.IsNullOrEmpty())
return null;
var ownerHomeWorld = j[nameof(OwnedIdentifier.OwnerHomeWorld)]?.Value<ushort>() ?? ushort.MaxValue;
var dataId = j[nameof(OwnedIdentifier.DataId)]?.Value<ushort>() ?? ushort.MaxValue;
var kind = j[nameof(OwnedIdentifier.Kind)]?.Value<ObjectKind>() ?? ObjectKind.Player;
return new OwnedIdentifier(Utf8String.FromStringUnsafe(name, false), Utf8String.FromStringUnsafe(ownerName, false),
ownerHomeWorld, dataId, kind);
}
case nameof(NpcIdentifier):
{
var name = j[nameof(Name)]?.Value<string>();
if (name.IsNullOrEmpty())
return null;
var dataId = j[nameof(NpcIdentifier.DataId)]?.Value<uint>() ?? uint.MaxValue;
return new NpcIdentifier(Utf8String.FromStringUnsafe(name, false), ushort.MaxValue, dataId);
}
default: return null;
}
}
}
public class InvalidIdentifier : IIdentifier
{
public Utf8String Name
=> Utf8String.Empty;
public bool IsValid
=> false;
public bool Equals(IIdentifier? other)
=> false;
public override int GetHashCode()
=> 0;
public override string ToString()
=> "Invalid";
public IIdentifier CreatePermanent()
=> this;
public void ToJson(JsonTextWriter j)
{ }
}
public class PlayerIdentifier : IIdentifier, IEquatable<PlayerIdentifier>
{
public Utf8String Name { get; }
public readonly ushort HomeWorld;
public bool IsValid
=> true;
public PlayerIdentifier(Utf8String name, ushort homeWorld)
{
Name = name;
HomeWorld = homeWorld;
}
public bool Equals(IIdentifier? other)
=> Equals(other as PlayerIdentifier);
public bool Equals(PlayerIdentifier? other)
=> other?.HomeWorld == HomeWorld && other.Name.Equals(Name);
public override int GetHashCode()
=> HashCode.Combine(Name.Crc32, HomeWorld);
public override string ToString()
=> $"{Name} ({HomeWorld})";
public IIdentifier CreatePermanent()
=> new PlayerIdentifier(Name.Clone(), HomeWorld);
public void ToJson(JsonTextWriter j)
{
j.WriteStartObject();
j.WritePropertyName("Type");
j.WriteValue(GetType().Name);
j.WritePropertyName(nameof(Name));
j.WriteValue(Name);
j.WritePropertyName(nameof(HomeWorld));
j.WriteValue(HomeWorld);
j.WriteEndObject();
}
}
public class SpecialIdentifier : IIdentifier, IEquatable<SpecialIdentifier>
{
public Utf8String Name
=> Utf8String.Empty;
public readonly ushort Index;
public bool IsValid
=> true;
public SpecialIdentifier(ushort index)
=> Index = index;
public bool Equals(IIdentifier? other)
=> Equals(other as SpecialIdentifier);
public bool Equals(SpecialIdentifier? other)
=> other?.Index == Index;
public override int GetHashCode()
=> Index;
public override string ToString()
=> $"Special Actor {Index}";
public IIdentifier CreatePermanent()
=> this;
public void ToJson(JsonTextWriter j)
{
j.WriteStartObject();
j.WritePropertyName("Type");
j.WriteValue(GetType().Name);
j.WritePropertyName(nameof(Index));
j.WriteValue(Index);
j.WriteEndObject();
}
}
public class OwnedIdentifier : IIdentifier, IEquatable<OwnedIdentifier>
{
public Utf8String Name { get; }
public readonly Utf8String OwnerName;
public readonly uint DataId;
public readonly ushort OwnerHomeWorld;
public readonly ObjectKind Kind;
public bool IsValid
=> true;
public OwnedIdentifier(Utf8String actorName, Utf8String ownerName, ushort ownerHomeWorld, uint dataId, ObjectKind kind)
{
OwnerName = ownerName;
OwnerHomeWorld = ownerHomeWorld;
DataId = dataId;
Kind = kind;
Name = actorName;
switch (Kind)
{
case ObjectKind.MountType:
var mount = Dalamud.GameData.GetExcelSheet<Mount>()!.GetRow(dataId);
if (mount != null)
Name = Utf8String.FromSpanUnsafe(mount.Singular.RawData, false).AsciiToMixed();
break;
case ObjectKind.Companion:
var companion = Dalamud.GameData.GetExcelSheet<Companion>()!.GetRow(dataId);
if (companion != null)
Name = Utf8String.FromSpanUnsafe(companion.Singular.RawData, false).AsciiToMixed();
break;
}
}
public bool Equals(IIdentifier? other)
=> Equals(other as OwnedIdentifier);
public bool Equals(OwnedIdentifier? other)
=> other?.DataId == DataId
&& other.OwnerHomeWorld == OwnerHomeWorld
&& other.Kind == Kind
&& other.OwnerName.Equals(OwnerName);
public override int GetHashCode()
=> HashCode.Combine(OwnerName.Crc32, OwnerHomeWorld, DataId, Kind);
public override string ToString()
=> $"{OwnerName}s {Name}";
public IIdentifier CreatePermanent()
=> new OwnedIdentifier(Name.Clone(), OwnerName.Clone(), OwnerHomeWorld, DataId, Kind);
public void ToJson(JsonTextWriter j)
{
j.WriteStartObject();
j.WritePropertyName("Type");
j.WriteValue(GetType().Name);
j.WritePropertyName(nameof(Name));
j.WriteValue(Name);
j.WritePropertyName(nameof(OwnerName));
j.WriteValue(OwnerName);
j.WritePropertyName(nameof(OwnerHomeWorld));
j.WriteValue(OwnerHomeWorld);
j.WritePropertyName(nameof(Kind));
j.WriteValue(Kind);
j.WritePropertyName(nameof(DataId));
j.WriteValue(DataId);
j.WriteEndObject();
}
}
public class NpcIdentifier : IIdentifier, IEquatable<NpcIdentifier>
{
public Utf8String Name { get; }
public readonly uint DataId;
public readonly ushort ObjectIndex;
public bool IsValid
=> true;
public NpcIdentifier(Utf8String actorName, ushort objectIndex = ushort.MaxValue, uint dataId = uint.MaxValue)
{
Name = actorName;
ObjectIndex = objectIndex;
DataId = dataId;
}
public bool Equals(IIdentifier? other)
=> Equals(other as NpcIdentifier);
public bool Equals(NpcIdentifier? other)
=> (other?.Name.Equals(Name) ?? false)
&& (other.DataId == uint.MaxValue || DataId == uint.MaxValue || other.DataId == DataId)
&& (other.ObjectIndex == ushort.MaxValue || ObjectIndex == ushort.MaxValue || other.ObjectIndex == ObjectIndex);
public override int GetHashCode()
=> Name.Crc32;
public override string ToString()
=> DataId == uint.MaxValue ? ObjectIndex == ushort.MaxValue ? Name.ToString() : $"{Name} at {ObjectIndex}" :
ObjectIndex == ushort.MaxValue ? $"{Name} ({DataId})" : $"{Name} ({DataId}) at {ObjectIndex}";
public IIdentifier CreatePermanent()
=> new NpcIdentifier(Name.Clone(), ObjectIndex, DataId);
public void ToJson(JsonTextWriter j)
{
j.WriteStartObject();
j.WritePropertyName("Type");
j.WriteValue(GetType().Name);
j.WritePropertyName(nameof(Name));
j.WriteValue(Name);
j.WritePropertyName(nameof(DataId));
j.WriteValue(DataId);
j.WriteEndObject();
}
}
private static IIdentifier CreateIdentifier(Actor actor)
{
if (!actor.Valid)
return IIdentifier.Invalid;
var objectIdx = actor.Pointer->GameObject.ObjectIndex;
if (objectIdx is >= 200 and < 240)
{
var parentIdx = Glamourer.Penumbra.CutsceneParent(objectIdx);
if (parentIdx >= 0)
{
var parent = (Actor)Dalamud.Objects.GetObjectAddress(parentIdx);
if (!parent)
return IIdentifier.Invalid;
return CreateIdentifier(parent);
}
}
switch (actor.ObjectKind)
{
case ObjectKind.Player:
{
var name = actor.Utf8Name;
if (name.Length > 0 && actor.Pointer->HomeWorld is > 0 and < ushort.MaxValue)
return new PlayerIdentifier(actor.Utf8Name, actor.Pointer->HomeWorld);
return IIdentifier.Invalid;
}
case ObjectKind.BattleNpc:
{
var ownerId = actor.Pointer->GameObject.OwnerID;
if (ownerId != 0xE0000000)
{
var owner = (Actor)Dalamud.Objects.SearchById(ownerId)?.Address;
if (!owner)
return new InvalidIdentifier();
return new OwnedIdentifier(actor.Utf8Name, owner.Utf8Name, owner.Pointer->HomeWorld,
actor.Pointer->GameObject.DataID, ObjectKind.BattleNpc);
}
return new NpcIdentifier(actor.Utf8Name, actor.Pointer->GameObject.ObjectIndex,
actor.Pointer->GameObject.DataID);
}
case ObjectKind.Retainer:
case ObjectKind.EventNpc:
return new NpcIdentifier(actor.Utf8Name, actor.Pointer->GameObject.ObjectIndex,
actor.Pointer->GameObject.DataID);
case ObjectKind.MountType:
case ObjectKind.Companion:
{
var idx = actor.Pointer->GameObject.ObjectIndex;
if (idx % 2 == 0)
return new InvalidIdentifier();
var owner = (Actor)Dalamud.Objects[idx - 1]?.Address;
if (!owner)
return new InvalidIdentifier();
var dataId = actor.ObjectKind switch
{
ObjectKind.MountType => owner.UsedMountId,
ObjectKind.Companion => actor.CompanionId,
_ => actor.Pointer->GameObject.DataID,
};
var name = actor.Utf8Name;
if (name.IsEmpty && dataId == 0)
return new InvalidIdentifier();
return new OwnedIdentifier(name, owner.Utf8Name, owner.Pointer->HomeWorld,
dataId, actor.ObjectKind);
}
default: return new InvalidIdentifier();
}
}
}

View file

@ -1,9 +1,11 @@
using System;
using Dalamud.Game.ClientState.Objects.Enums;
using Dalamud.Game.ClientState.Objects.Types;
using FFXIVClientStructs.FFXIV.Client.System.String;
using Glamourer.Customization;
using Penumbra.GameData.ByteString;
using Penumbra.GameData.Actors;
using Penumbra.GameData.Structs;
using Penumbra.String;
using CustomizeData = Penumbra.GameData.Structs.CustomizeData;
namespace Glamourer.Interop;
@ -23,10 +25,10 @@ public unsafe partial struct Actor : IEquatable<Actor>, IDesignable
public static implicit operator IntPtr(Actor actor)
=> actor.Pointer == null ? IntPtr.Zero : (IntPtr)actor.Pointer;
public IIdentifier GetIdentifier()
=> CreateIdentifier(this);
public ActorIdentifier GetIdentifier()
=> Glamourer.Actors.FromObject((FFXIVClientStructs.FFXIV.Client.Game.Object.GameObject*)Pointer);
public bool Identifier(out IIdentifier ident)
public bool Identifier(out ActorIdentifier ident)
{
if (Valid)
{
@ -34,7 +36,7 @@ public unsafe partial struct Actor : IEquatable<Actor>, IDesignable
return true;
}
ident = IIdentifier.Invalid;
ident = ActorIdentifier.Invalid;
return false;
}
@ -53,7 +55,7 @@ public unsafe partial struct Actor : IEquatable<Actor>, IDesignable
set => Pointer->GameObject.ObjectKind = (byte)value;
}
public Utf8String Utf8Name
public ByteString Utf8Name
=> new(Pointer->GameObject.Name);
public byte Job
@ -123,6 +125,8 @@ public unsafe partial struct Actor : IEquatable<Actor>, IDesignable
}
}
public bool IsWet { get; set; }
public void SetModelId(int value)
{

View file

@ -25,6 +25,9 @@ public unsafe partial struct DrawObject : IEquatable<DrawObject>, IDesignable
public uint ModelId
=> 0;
public bool IsWet
=> false;
public uint Type
=> (*(delegate* unmanaged<Human*, uint>**)Pointer)[50](Pointer);

View file

@ -13,4 +13,5 @@ public interface IDesignable
public CharacterWeapon OffHand { get; }
public bool VisorEnabled { get; }
public bool WeaponEnabled { get; }
public bool IsWet { get; }
}

View file

@ -2,6 +2,7 @@
using System.Text;
using Dalamud.Game.ClientState.Objects.Enums;
using Lumina.Excel.GeneratedSheets;
using Penumbra.GameData.Actors;
using static Glamourer.Interop.Actor;
namespace Glamourer.Interop;
@ -42,67 +43,29 @@ public static class ObjectManager
public static bool IsInGPose { get; private set; }
public static ushort World { get; private set; }
public static IReadOnlyDictionary<IIdentifier, ActorData> Actors
public static IReadOnlyDictionary<ActorIdentifier, ActorData> Actors
=> Identifiers;
public static IReadOnlyList<(IIdentifier, ActorData)> List
public static IReadOnlyList<(ActorIdentifier, ActorData)> List
=> ListData;
private static readonly Dictionary<IIdentifier, ActorData> Identifiers = new(200);
private static readonly List<(IIdentifier, ActorData)> ListData = new(Dalamud.Objects.Length);
private static readonly Dictionary<ActorIdentifier, ActorData> Identifiers = new(200);
private static readonly List<(ActorIdentifier, ActorData)> ListData = new(Dalamud.Objects.Length);
private static void HandleIdentifier(IIdentifier identifier, Actor character)
private static void HandleIdentifier(ActorIdentifier identifier, Actor character)
{
if (!character.DrawObject)
if (!character.DrawObject || !identifier.IsValid)
return;
switch (identifier)
if (!Identifiers.TryGetValue(identifier, out var data))
{
case PlayerIdentifier p:
if (!Identifiers.TryGetValue(p, out var data))
{
data = new ActorData(character,
World != p.HomeWorld
? $"{p.Name} ({Dalamud.GameData.GetExcelSheet<World>()!.GetRow(p.HomeWorld)!.Name})"
: p.Name.ToString());
Identifiers[p] = data;
ListData.Add((p, data));
}
else
{
data.Objects.Add(character);
}
break;
case NpcIdentifier n when !n.Name.IsEmpty:
if (!Identifiers.TryGetValue(n, out data))
{
data = new ActorData(character, $"{n.Name} (at {n.ObjectIndex})");
Identifiers[n] = data;
ListData.Add((n, data));
}
else
{
data.Objects.Add(character);
}
break;
case OwnedIdentifier o:
if (!Identifiers.TryGetValue(o, out data))
{
data = new ActorData(character,
World != o.OwnerHomeWorld
? $"{o.OwnerName}s {o.Name} ({Dalamud.GameData.GetExcelSheet<World>()!.GetRow(o.OwnerHomeWorld)!.Name})"
: $"{o.OwnerName}s {o.Name}");
Identifiers[o] = data;
ListData.Add((o, data));
}
else
{
data.Objects.Add(character);
}
break;
data = new ActorData(character, identifier.ToString());
Identifiers[identifier] = data;
ListData.Add((identifier, data));
}
else
{
data.Objects.Add(character);
}
}