mirror of
https://github.com/Ottermandias/Glamourer.git
synced 2026-02-18 21:47:44 +01:00
.
This commit is contained in:
parent
145b64bb7a
commit
4cf082aa19
37 changed files with 1362 additions and 1255 deletions
|
|
@ -66,6 +66,9 @@ public unsafe partial struct Actor : IEquatable<Actor>, IDesignable
|
|||
public bool Valid
|
||||
=> Pointer != null;
|
||||
|
||||
public int Index
|
||||
=> Pointer->GameObject.ObjectIndex;
|
||||
|
||||
public uint ModelId
|
||||
{
|
||||
get => (uint)Pointer->ModelCharaId;
|
||||
|
|
|
|||
|
|
@ -1,22 +1,15 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using Dalamud.Game.ClientState.Objects.Enums;
|
||||
using Lumina.Excel.GeneratedSheets;
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using Dalamud.Game;
|
||||
using Dalamud.Game.ClientState;
|
||||
using Dalamud.Game.ClientState.Objects;
|
||||
using Penumbra.GameData.Actors;
|
||||
using static Glamourer.Interop.Actor;
|
||||
|
||||
namespace Glamourer.Interop;
|
||||
|
||||
public static class ObjectManager
|
||||
public class ObjectManager : IReadOnlyDictionary<ActorIdentifier, ObjectManager.ActorData>
|
||||
{
|
||||
private const int CutsceneIndex = 200;
|
||||
private const int GPosePlayerIndex = 201;
|
||||
private const int CharacterScreenIndex = 240;
|
||||
private const int ExamineScreenIndex = 241;
|
||||
private const int FittingRoomIndex = 242;
|
||||
private const int DyePreviewIndex = 243;
|
||||
private const int PortraitIndex = 244;
|
||||
|
||||
public readonly struct ActorData
|
||||
{
|
||||
public readonly List<Actor> Objects;
|
||||
|
|
@ -40,28 +33,22 @@ public static class ObjectManager
|
|||
}
|
||||
}
|
||||
|
||||
public static bool IsInGPose { get; private set; }
|
||||
public static ushort World { get; private set; }
|
||||
public DateTime LastUpdate { get; private set; }
|
||||
|
||||
public static IReadOnlyDictionary<ActorIdentifier, ActorData> Actors
|
||||
=> Identifiers;
|
||||
public bool IsInGPose { get; private set; }
|
||||
public ushort World { get; private set; }
|
||||
|
||||
public static IReadOnlyList<(ActorIdentifier, ActorData)> List
|
||||
=> ListData;
|
||||
private readonly Dictionary<ActorIdentifier, ActorData> _identifiers = new(200);
|
||||
|
||||
private static readonly Dictionary<ActorIdentifier, ActorData> Identifiers = new(200);
|
||||
private static readonly List<(ActorIdentifier, ActorData)> ListData = new(Dalamud.Objects.Length);
|
||||
|
||||
private static void HandleIdentifier(ActorIdentifier identifier, Actor character)
|
||||
private void HandleIdentifier(ActorIdentifier identifier, Actor character)
|
||||
{
|
||||
if (!character.DrawObject || !identifier.IsValid)
|
||||
return;
|
||||
|
||||
if (!Identifiers.TryGetValue(identifier, out var data))
|
||||
if (!_identifiers.TryGetValue(identifier, out var data))
|
||||
{
|
||||
data = new ActorData(character, identifier.ToString());
|
||||
Identifiers[identifier] = data;
|
||||
ListData.Add((identifier, data));
|
||||
data = new ActorData(character, identifier.ToString());
|
||||
_identifiers[identifier] = data;
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
@ -69,88 +56,100 @@ public static class ObjectManager
|
|||
}
|
||||
}
|
||||
|
||||
public static void Update()
|
||||
{
|
||||
World = (ushort)(Dalamud.ClientState.LocalPlayer?.CurrentWorld.Id ?? 0u);
|
||||
Identifiers.Clear();
|
||||
ListData.Clear();
|
||||
private readonly Framework _framework;
|
||||
private readonly ClientState _clientState;
|
||||
private readonly ObjectTable _objects;
|
||||
|
||||
for (var i = 0; i < CutsceneIndex; ++i)
|
||||
public ObjectManager(Framework framework, ClientState clientState, ObjectTable objects)
|
||||
{
|
||||
_framework = framework;
|
||||
_clientState = clientState;
|
||||
_objects = objects;
|
||||
}
|
||||
|
||||
public void Update()
|
||||
{
|
||||
var lastUpdate = _framework.LastUpdate;
|
||||
if (lastUpdate <= LastUpdate)
|
||||
return;
|
||||
|
||||
LastUpdate = lastUpdate;
|
||||
World = (ushort)(_clientState.LocalPlayer?.CurrentWorld.Id ?? 0u);
|
||||
_identifiers.Clear();
|
||||
|
||||
for (var i = 0; i < (int)ScreenActor.CutsceneStart; ++i)
|
||||
{
|
||||
Actor character = Dalamud.Objects.GetObjectAddress(i);
|
||||
Actor character = _objects.GetObjectAddress(i);
|
||||
if (character.Identifier(out var identifier))
|
||||
HandleIdentifier(identifier, character);
|
||||
}
|
||||
|
||||
for (var i = CutsceneIndex; i < CharacterScreenIndex; ++i)
|
||||
for (var i = (int)ScreenActor.CutsceneStart; i < (int)ScreenActor.CutsceneEnd; ++i)
|
||||
{
|
||||
Actor character = Dalamud.Objects.GetObjectAddress(i);
|
||||
Actor character = _objects.GetObjectAddress(i);
|
||||
if (!character.Identifier(out var identifier))
|
||||
break;
|
||||
|
||||
HandleIdentifier(identifier, character);
|
||||
}
|
||||
|
||||
void AddSpecial(int idx, string label)
|
||||
void AddSpecial(ScreenActor idx, string label)
|
||||
{
|
||||
Actor actor = Dalamud.Objects.GetObjectAddress(idx);
|
||||
Actor actor = _objects.GetObjectAddress((int)idx);
|
||||
if (actor.Identifier(out var ident))
|
||||
{
|
||||
var data = new ActorData(actor, label);
|
||||
Identifiers.Add(ident, data);
|
||||
ListData.Add((ident, data));
|
||||
_identifiers.Add(ident, data);
|
||||
}
|
||||
}
|
||||
|
||||
AddSpecial(CharacterScreenIndex, "Character Screen Actor");
|
||||
AddSpecial(ExamineScreenIndex, "Examine Screen Actor");
|
||||
AddSpecial(FittingRoomIndex, "Fitting Room Actor");
|
||||
AddSpecial(DyePreviewIndex, "Dye Preview Actor");
|
||||
AddSpecial(PortraitIndex, "Portrait Actor");
|
||||
AddSpecial(ScreenActor.CharacterScreen, "Character Screen Actor");
|
||||
AddSpecial(ScreenActor.ExamineScreen, "Examine Screen Actor");
|
||||
AddSpecial(ScreenActor.FittingRoom, "Fitting Room Actor");
|
||||
AddSpecial(ScreenActor.DyePreview, "Dye Preview Actor");
|
||||
AddSpecial(ScreenActor.Portrait, "Portrait Actor");
|
||||
AddSpecial(ScreenActor.Card6, "Card Actor 6");
|
||||
AddSpecial(ScreenActor.Card7, "Card Actor 7");
|
||||
AddSpecial(ScreenActor.Card8, "Card Actor 8");
|
||||
|
||||
for (var i = PortraitIndex + 1; i < Dalamud.Objects.Length; ++i)
|
||||
for (var i = (int)ScreenActor.ScreenEnd; i < Dalamud.Objects.Length; ++i)
|
||||
{
|
||||
Actor character = Dalamud.Objects.GetObjectAddress(i);
|
||||
Actor character = _objects.GetObjectAddress(i);
|
||||
if (character.Identifier(out var identifier))
|
||||
HandleIdentifier(identifier, character);
|
||||
}
|
||||
|
||||
|
||||
Actor gPose = Dalamud.Objects.GetObjectAddress(GPosePlayerIndex);
|
||||
var gPose = GPosePlayer;
|
||||
IsInGPose = gPose && gPose.Utf8Name.Length > 0;
|
||||
}
|
||||
|
||||
public static Actor GPosePlayer
|
||||
=> Dalamud.Objects.GetObjectAddress(GPosePlayerIndex);
|
||||
public Actor GPosePlayer
|
||||
=> _objects.GetObjectAddress((int)ScreenActor.GPosePlayer);
|
||||
|
||||
public static Actor Player
|
||||
=> Dalamud.Objects.GetObjectAddress(0);
|
||||
public Actor Player
|
||||
=> _objects.GetObjectAddress(0);
|
||||
|
||||
private static unsafe string GetLabel(Actor player, string playerName, int num, bool gPose)
|
||||
{
|
||||
var sb = new StringBuilder(64);
|
||||
sb.Append(playerName);
|
||||
public IEnumerator<KeyValuePair<ActorIdentifier, ActorData>> GetEnumerator()
|
||||
=> _identifiers.GetEnumerator();
|
||||
|
||||
if (gPose)
|
||||
{
|
||||
sb.Append(" (GPose");
|
||||
IEnumerator IEnumerable.GetEnumerator()
|
||||
=> GetEnumerator();
|
||||
|
||||
if (player.ObjectKind == ObjectKind.Player)
|
||||
sb.Append(')');
|
||||
else
|
||||
sb.Append(player.ModelId == 0 ? ", NPC)" : ", Monster)");
|
||||
}
|
||||
else if (player.ObjectKind != ObjectKind.Player)
|
||||
{
|
||||
sb.Append(player.ModelId == 0 ? " (NPC)" : " (Monster)");
|
||||
}
|
||||
public int Count
|
||||
=> _identifiers.Count;
|
||||
|
||||
if (num > 1)
|
||||
{
|
||||
sb.Append(" #");
|
||||
sb.Append(num);
|
||||
}
|
||||
public bool ContainsKey(ActorIdentifier key)
|
||||
=> _identifiers.ContainsKey(key);
|
||||
|
||||
return sb.ToString();
|
||||
}
|
||||
public bool TryGetValue(ActorIdentifier key, out ActorData value)
|
||||
=> _identifiers.TryGetValue(key, out value);
|
||||
|
||||
public ActorData this[ActorIdentifier key]
|
||||
=> _identifiers[key];
|
||||
|
||||
public IEnumerable<ActorIdentifier> Keys
|
||||
=> _identifiers.Keys;
|
||||
|
||||
public IEnumerable<ActorData> Values
|
||||
=> _identifiers.Values;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,35 +8,6 @@ namespace Glamourer.Interop;
|
|||
|
||||
public unsafe partial class RedrawManager
|
||||
{
|
||||
// Update
|
||||
public delegate bool ChangeCustomizeDelegate(Human* human, byte* data, byte skipEquipment);
|
||||
|
||||
[Signature("E8 ?? ?? ?? ?? 41 0F B6 C5 66 41 89 86")]
|
||||
private readonly ChangeCustomizeDelegate _changeCustomize = null!;
|
||||
|
||||
|
||||
|
||||
public bool UpdateCustomize(Actor actor, Customize customize)
|
||||
{
|
||||
if (!actor.Valid || !actor.DrawObject.Valid)
|
||||
return false;
|
||||
|
||||
var d = actor.DrawObject;
|
||||
if (NeedsRedraw(d.Customize, customize))
|
||||
{
|
||||
Glamourer.Penumbra.RedrawObject(actor.Character, RedrawType.Redraw);
|
||||
return true;
|
||||
}
|
||||
|
||||
return _changeCustomize(d.Pointer, (byte*)customize.Data, 1);
|
||||
}
|
||||
|
||||
public static bool NeedsRedraw(Customize lhs, Customize rhs)
|
||||
=> lhs.Race != rhs.Race
|
||||
|| lhs.Gender != rhs.Gender
|
||||
|| lhs.BodyType != rhs.BodyType
|
||||
|| lhs.Face != rhs.Face
|
||||
|| lhs.Race == Race.Hyur && lhs.Clan != rhs.Clan;
|
||||
|
||||
|
||||
public static void SetVisor(Human* data, bool on)
|
||||
|
|
|
|||
|
|
@ -69,16 +69,4 @@ public unsafe partial class RedrawManager
|
|||
//
|
||||
//return _flagSlotForUpdateHook.Original(drawObject, slotIdx, data);
|
||||
}
|
||||
|
||||
private void HandleEquipUpdate(nint drawObject, EquipSlot slot, ref CharacterArmor data, bool manual)
|
||||
{
|
||||
var actor = Glamourer.Penumbra.GameObjectFromDrawObject(drawObject);
|
||||
var identifier = actor.GetIdentifier();
|
||||
|
||||
if (!_currentManipulations.TryGetDesign(identifier, out var design))
|
||||
return;
|
||||
|
||||
var flag = slot.ToFlag();
|
||||
var stainFlag = slot.ToStainFlag();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -45,7 +45,7 @@ public unsafe partial class RedrawManager
|
|||
_ => weapon,
|
||||
};
|
||||
}
|
||||
else if (redrawOnEquality == 1 && _currentManipulations.TryGetDesign(identifier, out var save2))
|
||||
else if (redrawOnEquality == 1 && _stateManager.TryGetValue(identifier, out var save2))
|
||||
{
|
||||
PluginLog.Information($"Loaded weapon from current design for {identifier}.");
|
||||
//switch (slot)
|
||||
|
|
|
|||
|
|
@ -1,31 +1,104 @@
|
|||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using Dalamud.Hooking;
|
||||
using Dalamud.Logging;
|
||||
using Dalamud.Utility.Signatures;
|
||||
using FFXIVClientStructs.FFXIV.Client.Graphics.Scene;
|
||||
using Glamourer.Customization;
|
||||
using Glamourer.State;
|
||||
using Glamourer.Structs;
|
||||
using Penumbra.GameData.Data;
|
||||
using Penumbra.GameData.Enums;
|
||||
using Penumbra.GameData.Structs;
|
||||
using CustomizeData = Penumbra.GameData.Structs.CustomizeData;
|
||||
|
||||
namespace Glamourer.Interop;
|
||||
|
||||
public class DesignBaseValidator
|
||||
public partial class Interop : IDisposable
|
||||
{
|
||||
private readonly CustomizationManager _manager;
|
||||
private readonly RestrictedGear _restrictedGear;
|
||||
|
||||
public DesignBaseValidator(CustomizationManager manager, RestrictedGear restrictedGear)
|
||||
public Interop()
|
||||
{
|
||||
_manager = manager;
|
||||
_restrictedGear = restrictedGear;
|
||||
SignatureHelper.Initialise(this);
|
||||
_changeJobHook.Enable();
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
_changeJobHook.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
public unsafe partial class Interop
|
||||
{
|
||||
private delegate ulong FlagSlotForUpdateDelegateIntern(nint drawObject, uint slot, CharacterArmor* data);
|
||||
public delegate void FlagSlotForUpdateDelegate(DrawObject drawObject, EquipSlot slot, ref CharacterArmor item);
|
||||
|
||||
public unsafe partial class RedrawManager
|
||||
// This gets called when one of the ten equip items of an existing draw object gets changed.
|
||||
[Signature(Sigs.FlagSlotForUpdate, DetourName = nameof(FlagSlotForUpdateDetour))]
|
||||
private readonly Hook<FlagSlotForUpdateDelegateIntern> _flagSlotForUpdateHook = null!;
|
||||
|
||||
public event FlagSlotForUpdateDelegate? EquipUpdate;
|
||||
|
||||
public ulong FlagSlotForUpdateInterop(DrawObject drawObject, EquipSlot slot, CharacterArmor armor)
|
||||
=> _flagSlotForUpdateHook.Original(drawObject.Address, slot.ToIndex(), &armor);
|
||||
|
||||
public void UpdateSlot(DrawObject drawObject, EquipSlot slot, CharacterArmor data)
|
||||
{
|
||||
InvokeFlagSlotEvent(drawObject, slot, ref data);
|
||||
FlagSlotForUpdateInterop(drawObject, slot, data);
|
||||
}
|
||||
|
||||
public void UpdateStain(DrawObject drawObject, EquipSlot slot, StainId stain)
|
||||
{
|
||||
var armor = drawObject.Equip[slot] with { Stain = stain };
|
||||
UpdateSlot(drawObject, slot, armor);
|
||||
}
|
||||
|
||||
private ulong FlagSlotForUpdateDetour(nint drawObject, uint slotIdx, CharacterArmor* data)
|
||||
{
|
||||
var slot = slotIdx.ToEquipSlot();
|
||||
InvokeFlagSlotEvent(drawObject, slot, ref *data);
|
||||
return _flagSlotForUpdateHook.Original(drawObject, slotIdx, data);
|
||||
}
|
||||
|
||||
private void InvokeFlagSlotEvent(DrawObject drawObject, EquipSlot slot, ref CharacterArmor armor)
|
||||
{
|
||||
if (EquipUpdate == null)
|
||||
return;
|
||||
|
||||
foreach (var del in EquipUpdate.GetInvocationList().OfType<FlagSlotForUpdateDelegate>())
|
||||
{
|
||||
try
|
||||
{
|
||||
del(drawObject, slot, ref armor);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Glamourer.Log.Error($"Could not invoke {nameof(EquipUpdate)} Subscriber:\n{ex}");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public unsafe partial class Interop
|
||||
{
|
||||
public delegate bool ChangeCustomizeDelegate(Human* human, byte* data, byte skipEquipment);
|
||||
|
||||
[Signature(Sigs.ChangeCustomize)]
|
||||
private readonly ChangeCustomizeDelegate _changeCustomize = null!;
|
||||
|
||||
public bool UpdateCustomize(Actor actor, CustomizeData customize)
|
||||
{
|
||||
Debug.Assert(customize.Data != null, "Customize was invalid.");
|
||||
|
||||
if (!actor.Valid || !actor.DrawObject.Valid)
|
||||
return false;
|
||||
|
||||
return _changeCustomize(actor.DrawObject.Pointer, customize.Data, 1);
|
||||
}
|
||||
}
|
||||
|
||||
public partial class Interop
|
||||
{
|
||||
private delegate void ChangeJobDelegate(IntPtr data, uint job);
|
||||
|
||||
|
|
@ -44,27 +117,21 @@ public unsafe partial class RedrawManager
|
|||
public unsafe partial class RedrawManager : IDisposable
|
||||
{
|
||||
private readonly FixedDesigns _fixedDesigns;
|
||||
private readonly CurrentManipulations _currentManipulations;
|
||||
private readonly ActiveDesign.Manager _stateManager;
|
||||
|
||||
public RedrawManager(FixedDesigns fixedDesigns, CurrentManipulations currentManipulations)
|
||||
public RedrawManager(FixedDesigns fixedDesigns, ActiveDesign.Manager stateManager)
|
||||
{
|
||||
SignatureHelper.Initialise(this);
|
||||
Glamourer.Penumbra.CreatingCharacterBase.Event += OnCharacterRedraw;
|
||||
Glamourer.Penumbra.CreatedCharacterBase.Event += OnCharacterRedrawFinished;
|
||||
_fixedDesigns = fixedDesigns;
|
||||
_currentManipulations = currentManipulations;
|
||||
_fixedDesigns = fixedDesigns;
|
||||
_stateManager = stateManager;
|
||||
_flagSlotForUpdateHook.Enable();
|
||||
_loadWeaponHook.Enable();
|
||||
_changeJobHook.Enable();
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
_flagSlotForUpdateHook.Dispose();
|
||||
_loadWeaponHook.Dispose();
|
||||
_changeJobHook.Dispose();
|
||||
Glamourer.Penumbra.CreatingCharacterBase.Event -= OnCharacterRedraw;
|
||||
Glamourer.Penumbra.CreatedCharacterBase.Event -= OnCharacterRedrawFinished;
|
||||
}
|
||||
|
||||
private void OnCharacterRedraw(Actor actor, uint* modelId, Customize customize, CharacterEquip equip)
|
||||
|
|
@ -76,7 +143,7 @@ public unsafe partial class RedrawManager : IDisposable
|
|||
|
||||
// Check if we have a current design in use, or if not if the actor has a fixed design.
|
||||
var identifier = actor.GetIdentifier();
|
||||
if (!(_currentManipulations.TryGetDesign(identifier, out var save) || _fixedDesigns.TryGetDesign(identifier, out var save2)))
|
||||
if (!(_stateManager.TryGetValue(identifier, out var save) || _fixedDesigns.TryGetDesign(identifier, out var save2)))
|
||||
return;
|
||||
|
||||
// Compare game object customize data against draw object customize data for transformations.
|
||||
|
|
@ -116,4 +183,4 @@ public unsafe partial class RedrawManager : IDisposable
|
|||
{
|
||||
//SetVisor((Human*)drawObject, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue