Merge remote-tracking branch 'origin/main' into ipc_new
# Conflicts: # CustomizePlus/Core/ServiceManagerBuilder.cs # CustomizePlus/Game/Services/GameObjectService.cs # CustomizePlus/Profiles/ProfileManager.cs
This commit is contained in:
2
.github/workflows/release.yml
vendored
2
.github/workflows/release.yml
vendored
@@ -15,7 +15,7 @@ jobs:
|
|||||||
- name: Setup .NET
|
- name: Setup .NET
|
||||||
uses: actions/setup-dotnet@v1
|
uses: actions/setup-dotnet@v1
|
||||||
with:
|
with:
|
||||||
dotnet-version: '7.x.x'
|
dotnet-version: '8.x.x'
|
||||||
- name: Restore dependencies
|
- name: Restore dependencies
|
||||||
run: dotnet restore
|
run: dotnet restore
|
||||||
- name: Download Dalamud
|
- name: Download Dalamud
|
||||||
|
|||||||
2
.github/workflows/test_release.yml
vendored
2
.github/workflows/test_release.yml
vendored
@@ -15,7 +15,7 @@ jobs:
|
|||||||
- name: Setup .NET
|
- name: Setup .NET
|
||||||
uses: actions/setup-dotnet@v1
|
uses: actions/setup-dotnet@v1
|
||||||
with:
|
with:
|
||||||
dotnet-version: '7.x.x'
|
dotnet-version: '8.x.x'
|
||||||
- name: Restore dependencies
|
- name: Restore dependencies
|
||||||
run: dotnet restore
|
run: dotnet restore
|
||||||
- name: Download Dalamud
|
- name: Download Dalamud
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
<Project Sdk="Microsoft.NET.Sdk">
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFramework>net7.0-windows</TargetFramework>
|
<TargetFramework>net8.0-windows</TargetFramework>
|
||||||
<ImplicitUsings>enable</ImplicitUsings>
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
<Nullable>enable</Nullable>
|
<Nullable>enable</Nullable>
|
||||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||||
|
|||||||
@@ -1,116 +0,0 @@
|
|||||||
using Penumbra.GameData.Actors;
|
|
||||||
using System;
|
|
||||||
using FFXIVClientStructs.FFXIV.Client.Game.Character;
|
|
||||||
using FFXIVClientStructs.FFXIV.Client.Game.Object;
|
|
||||||
using Penumbra.GameData.Enums;
|
|
||||||
using Penumbra.GameData.Structs;
|
|
||||||
using Penumbra.String;
|
|
||||||
|
|
||||||
namespace CustomizePlus.GameData.Data;
|
|
||||||
|
|
||||||
public readonly unsafe struct Actor : IEquatable<Actor>
|
|
||||||
{
|
|
||||||
private Actor(nint address)
|
|
||||||
=> Address = address;
|
|
||||||
|
|
||||||
public static readonly Actor Null = new(nint.Zero);
|
|
||||||
|
|
||||||
public readonly nint Address;
|
|
||||||
|
|
||||||
public GameObject* AsObject
|
|
||||||
=> (GameObject*)Address;
|
|
||||||
|
|
||||||
public Character* AsCharacter
|
|
||||||
=> (Character*)Address;
|
|
||||||
|
|
||||||
public bool Valid
|
|
||||||
=> Address != nint.Zero;
|
|
||||||
|
|
||||||
public bool IsCharacter
|
|
||||||
=> Valid && AsObject->IsCharacter();
|
|
||||||
|
|
||||||
public static implicit operator Actor(nint? pointer)
|
|
||||||
=> new(pointer ?? nint.Zero);
|
|
||||||
|
|
||||||
public static implicit operator Actor(GameObject* pointer)
|
|
||||||
=> new((nint)pointer);
|
|
||||||
|
|
||||||
public static implicit operator Actor(Character* pointer)
|
|
||||||
=> new((nint)pointer);
|
|
||||||
|
|
||||||
public static implicit operator nint(Actor actor)
|
|
||||||
=> actor.Address;
|
|
||||||
|
|
||||||
public bool IsGPoseOrCutscene
|
|
||||||
=> Index.Index is >= (int)ScreenActor.CutsceneStart and < (int)ScreenActor.CutsceneEnd;
|
|
||||||
|
|
||||||
public ActorIdentifier GetIdentifier(ActorManager actors)
|
|
||||||
=> actors.FromObject(AsObject, out _, true, true, false);
|
|
||||||
|
|
||||||
public ByteString Utf8Name
|
|
||||||
=> Valid ? new ByteString(AsObject->Name) : ByteString.Empty;
|
|
||||||
|
|
||||||
public bool Identifier(ActorManager actors, out ActorIdentifier ident)
|
|
||||||
{
|
|
||||||
if (Valid)
|
|
||||||
{
|
|
||||||
ident = GetIdentifier(actors);
|
|
||||||
return ident.IsValid;
|
|
||||||
}
|
|
||||||
|
|
||||||
ident = ActorIdentifier.Invalid;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
public ObjectIndex Index
|
|
||||||
=> Valid ? AsObject->ObjectIndex : ObjectIndex.AnyIndex;
|
|
||||||
|
|
||||||
public Model Model
|
|
||||||
=> Valid ? AsObject->DrawObject : null;
|
|
||||||
|
|
||||||
public byte Job
|
|
||||||
=> IsCharacter ? AsCharacter->CharacterData.ClassJob : (byte)0;
|
|
||||||
|
|
||||||
public static implicit operator bool(Actor actor)
|
|
||||||
=> actor.Address != nint.Zero;
|
|
||||||
|
|
||||||
public static bool operator true(Actor actor)
|
|
||||||
=> actor.Address != nint.Zero;
|
|
||||||
|
|
||||||
public static bool operator false(Actor actor)
|
|
||||||
=> actor.Address == nint.Zero;
|
|
||||||
|
|
||||||
public static bool operator !(Actor actor)
|
|
||||||
=> actor.Address == nint.Zero;
|
|
||||||
|
|
||||||
public bool Equals(Actor other)
|
|
||||||
=> Address == other.Address;
|
|
||||||
|
|
||||||
public override bool Equals(object? obj)
|
|
||||||
=> obj is Actor other && Equals(other);
|
|
||||||
|
|
||||||
public override int GetHashCode()
|
|
||||||
=> Address.GetHashCode();
|
|
||||||
|
|
||||||
public static bool operator ==(Actor lhs, Actor rhs)
|
|
||||||
=> lhs.Address == rhs.Address;
|
|
||||||
|
|
||||||
public static bool operator !=(Actor lhs, Actor rhs)
|
|
||||||
=> lhs.Address != rhs.Address;
|
|
||||||
/*
|
|
||||||
/// <summary> Only valid for characters. </summary>
|
|
||||||
public CharacterArmor GetArmor(EquipSlot slot)
|
|
||||||
=> ((CharacterArmor*)&AsCharacter->DrawData.Head)[slot.ToIndex()];
|
|
||||||
|
|
||||||
public CharacterWeapon GetMainhand()
|
|
||||||
=> new(AsCharacter->DrawData.Weapon(DrawDataContainer.WeaponSlot.MainHand).ModelId.Value);
|
|
||||||
|
|
||||||
public CharacterWeapon GetOffhand()
|
|
||||||
=> new(AsCharacter->DrawData.Weapon(DrawDataContainer.WeaponSlot.OffHand).ModelId.Value);
|
|
||||||
|
|
||||||
public Customize GetCustomize()
|
|
||||||
=> *(Customize*)&AsCharacter->DrawData.CustomizeData;
|
|
||||||
*/
|
|
||||||
public override string ToString()
|
|
||||||
=> $"0x{Address:X}";
|
|
||||||
}
|
|
||||||
@@ -1,4 +1,6 @@
|
|||||||
namespace CustomizePlus.GameData.Data;
|
using Penumbra.GameData.Interop;
|
||||||
|
|
||||||
|
namespace CustomizePlus.GameData.Data;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// A single actor with its label and the list of associated game objects.
|
/// A single actor with its label and the list of associated game objects.
|
||||||
|
|||||||
@@ -1,199 +0,0 @@
|
|||||||
using System;
|
|
||||||
using FFXIVClientStructs.FFXIV.Client.Game.Character;
|
|
||||||
using FFXIVClientStructs.FFXIV.Client.Graphics.Scene;
|
|
||||||
using Penumbra.GameData.Enums;
|
|
||||||
using Penumbra.GameData.Structs;
|
|
||||||
using Object = FFXIVClientStructs.FFXIV.Client.Graphics.Scene.Object;
|
|
||||||
using ObjectType = FFXIVClientStructs.FFXIV.Client.Graphics.Scene.ObjectType;
|
|
||||||
|
|
||||||
namespace CustomizePlus.GameData.Data;
|
|
||||||
|
|
||||||
public readonly unsafe struct Model : IEquatable<Model>
|
|
||||||
{
|
|
||||||
private Model(nint address)
|
|
||||||
=> Address = address;
|
|
||||||
|
|
||||||
public readonly nint Address;
|
|
||||||
|
|
||||||
public static readonly Model Null = new(0);
|
|
||||||
|
|
||||||
public DrawObject* AsDrawObject
|
|
||||||
=> (DrawObject*)Address;
|
|
||||||
|
|
||||||
public CharacterBase* AsCharacterBase
|
|
||||||
=> (CharacterBase*)Address;
|
|
||||||
|
|
||||||
public Weapon* AsWeapon
|
|
||||||
=> (Weapon*)Address;
|
|
||||||
|
|
||||||
public Human* AsHuman
|
|
||||||
=> (Human*)Address;
|
|
||||||
|
|
||||||
public static implicit operator Model(nint? pointer)
|
|
||||||
=> new(pointer ?? nint.Zero);
|
|
||||||
|
|
||||||
public static implicit operator Model(Object* pointer)
|
|
||||||
=> new((nint)pointer);
|
|
||||||
|
|
||||||
public static implicit operator Model(DrawObject* pointer)
|
|
||||||
=> new((nint)pointer);
|
|
||||||
|
|
||||||
public static implicit operator Model(Human* pointer)
|
|
||||||
=> new((nint)pointer);
|
|
||||||
|
|
||||||
public static implicit operator Model(CharacterBase* pointer)
|
|
||||||
=> new((nint)pointer);
|
|
||||||
|
|
||||||
public static implicit operator nint(Model model)
|
|
||||||
=> model.Address;
|
|
||||||
|
|
||||||
public bool Valid
|
|
||||||
=> Address != nint.Zero;
|
|
||||||
|
|
||||||
public bool IsCharacterBase
|
|
||||||
=> Valid && AsDrawObject->Object.GetObjectType() == ObjectType.CharacterBase;
|
|
||||||
|
|
||||||
public bool IsHuman
|
|
||||||
=> IsCharacterBase && AsCharacterBase->GetModelType() == CharacterBase.ModelType.Human;
|
|
||||||
|
|
||||||
public bool IsWeapon
|
|
||||||
=> IsCharacterBase && AsCharacterBase->GetModelType() == CharacterBase.ModelType.Weapon;
|
|
||||||
|
|
||||||
public static implicit operator bool(Model actor)
|
|
||||||
=> actor.Address != nint.Zero;
|
|
||||||
|
|
||||||
public static bool operator true(Model actor)
|
|
||||||
=> actor.Address != nint.Zero;
|
|
||||||
|
|
||||||
public static bool operator false(Model actor)
|
|
||||||
=> actor.Address == nint.Zero;
|
|
||||||
|
|
||||||
public static bool operator !(Model actor)
|
|
||||||
=> actor.Address == nint.Zero;
|
|
||||||
|
|
||||||
public bool Equals(Model other)
|
|
||||||
=> Address == other.Address;
|
|
||||||
|
|
||||||
public override bool Equals(object? obj)
|
|
||||||
=> obj is Model other && Equals(other);
|
|
||||||
|
|
||||||
public override int GetHashCode()
|
|
||||||
=> Address.GetHashCode();
|
|
||||||
|
|
||||||
public static bool operator ==(Model lhs, Model rhs)
|
|
||||||
=> lhs.Address == rhs.Address;
|
|
||||||
|
|
||||||
public static bool operator !=(Model lhs, Model rhs)
|
|
||||||
=> lhs.Address != rhs.Address;
|
|
||||||
/*
|
|
||||||
/// <summary> Only valid for humans. </summary>
|
|
||||||
public CharacterArmor GetArmor(EquipSlot slot)
|
|
||||||
=> ((CharacterArmor*)&AsHuman->Head)[slot.ToIndex()];
|
|
||||||
|
|
||||||
public Customize GetCustomize()
|
|
||||||
=> *(Customize*)&AsHuman->Customize;
|
|
||||||
|
|
||||||
public (Model Address, CharacterWeapon Data) GetMainhand()
|
|
||||||
{
|
|
||||||
Model weapon = AsDrawObject->Object.ChildObject;
|
|
||||||
return !weapon.IsWeapon
|
|
||||||
? (Null, CharacterWeapon.Empty)
|
|
||||||
: (weapon, new CharacterWeapon(weapon.AsWeapon->ModelSetId, weapon.AsWeapon->SecondaryId, (Variant)weapon.AsWeapon->Variant,
|
|
||||||
(StainId)weapon.AsWeapon->ModelUnknown));
|
|
||||||
}
|
|
||||||
|
|
||||||
public (Model Address, CharacterWeapon Data) GetOffhand()
|
|
||||||
{
|
|
||||||
var mainhand = AsDrawObject->Object.ChildObject;
|
|
||||||
if (mainhand == null)
|
|
||||||
return (Null, CharacterWeapon.Empty);
|
|
||||||
|
|
||||||
Model offhand = mainhand->NextSiblingObject;
|
|
||||||
if (offhand == mainhand || !offhand.IsWeapon)
|
|
||||||
return (Null, CharacterWeapon.Empty);
|
|
||||||
|
|
||||||
return (offhand, new CharacterWeapon(offhand.AsWeapon->ModelSetId, offhand.AsWeapon->SecondaryId, (Variant)offhand.AsWeapon->Variant,
|
|
||||||
(StainId)offhand.AsWeapon->ModelUnknown));
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary> Obtain the mainhand and offhand and their data by guesstimating which child object is which. </summary>
|
|
||||||
public (Model Mainhand, Model Offhand, CharacterWeapon MainData, CharacterWeapon OffData) GetWeapons()
|
|
||||||
{
|
|
||||||
var (first, second, count) = GetChildrenWeapons();
|
|
||||||
switch (count)
|
|
||||||
{
|
|
||||||
case 0: return (Null, Null, CharacterWeapon.Empty, CharacterWeapon.Empty);
|
|
||||||
case 1:
|
|
||||||
return (first, Null, new CharacterWeapon(first.AsWeapon->ModelSetId, first.AsWeapon->SecondaryId,
|
|
||||||
(Variant)first.AsWeapon->Variant,
|
|
||||||
(StainId)first.AsWeapon->ModelUnknown), CharacterWeapon.Empty);
|
|
||||||
default:
|
|
||||||
var (main, off) = DetermineMainhand(first, second);
|
|
||||||
var mainData = new CharacterWeapon(main.AsWeapon->ModelSetId, main.AsWeapon->SecondaryId, (Variant)main.AsWeapon->Variant,
|
|
||||||
(StainId)main.AsWeapon->ModelUnknown);
|
|
||||||
var offData = new CharacterWeapon(off.AsWeapon->ModelSetId, off.AsWeapon->SecondaryId, (Variant)off.AsWeapon->Variant,
|
|
||||||
(StainId)off.AsWeapon->ModelUnknown);
|
|
||||||
return (main, off, mainData, offData);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary> Obtain the mainhand and offhand and their data by using the drawdata container from the corresponding actor. </summary>
|
|
||||||
public (Model Mainhand, Model Offhand, CharacterWeapon MainData, CharacterWeapon OffData) GetWeapons(Actor actor)
|
|
||||||
{
|
|
||||||
if (!Valid || !actor.IsCharacter || actor.Model.Address != Address)
|
|
||||||
return (Null, Null, CharacterWeapon.Empty, CharacterWeapon.Empty);
|
|
||||||
|
|
||||||
Model main = actor.AsCharacter->DrawData.Weapon(DrawDataContainer.WeaponSlot.MainHand).DrawObject;
|
|
||||||
var mainData = CharacterWeapon.Empty;
|
|
||||||
if (main.IsWeapon)
|
|
||||||
mainData = new CharacterWeapon(main.AsWeapon->ModelSetId, main.AsWeapon->SecondaryId, (Variant)main.AsWeapon->Variant,
|
|
||||||
(StainId)main.AsWeapon->ModelUnknown);
|
|
||||||
else
|
|
||||||
main = Null;
|
|
||||||
Model off = actor.AsCharacter->DrawData.Weapon(DrawDataContainer.WeaponSlot.OffHand).DrawObject;
|
|
||||||
var offData = CharacterWeapon.Empty;
|
|
||||||
if (off.IsWeapon)
|
|
||||||
offData = new CharacterWeapon(off.AsWeapon->ModelSetId, off.AsWeapon->SecondaryId, (Variant)off.AsWeapon->Variant,
|
|
||||||
(StainId)off.AsWeapon->ModelUnknown);
|
|
||||||
else
|
|
||||||
off = Null;
|
|
||||||
return (main, off, mainData, offData);
|
|
||||||
}
|
|
||||||
|
|
||||||
private (Model, Model, int) GetChildrenWeapons()
|
|
||||||
{
|
|
||||||
Span<Model> weapons = stackalloc Model[2];
|
|
||||||
weapons[0] = Null;
|
|
||||||
weapons[1] = Null;
|
|
||||||
var count = 0;
|
|
||||||
|
|
||||||
if (!Valid || AsDrawObject->Object.ChildObject == null)
|
|
||||||
return (weapons[0], weapons[1], count);
|
|
||||||
|
|
||||||
Model starter = AsDrawObject->Object.ChildObject;
|
|
||||||
var iterator = starter;
|
|
||||||
do
|
|
||||||
{
|
|
||||||
if (iterator.IsWeapon)
|
|
||||||
weapons[count++] = iterator;
|
|
||||||
if (count == 2)
|
|
||||||
return (weapons[0], weapons[1], count);
|
|
||||||
|
|
||||||
iterator = iterator.AsDrawObject->Object.NextSiblingObject;
|
|
||||||
} while (iterator.Address != starter.Address);
|
|
||||||
|
|
||||||
return (weapons[0], weapons[1], count);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary> I don't know a safe way to do this but in experiments this worked.
|
|
||||||
/// The first uint at +0x8 was set to non-zero for the mainhand and zero for the offhand. </summary>
|
|
||||||
private static (Model Mainhand, Model Offhand) DetermineMainhand(Model first, Model second)
|
|
||||||
{
|
|
||||||
var discriminator1 = *(ulong*)(first.Address + 0x10);
|
|
||||||
var discriminator2 = *(ulong*)(second.Address + 0x10);
|
|
||||||
return discriminator1 == 0 && discriminator2 != 0 ? (second, first) : (first, second);
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
public override string ToString()
|
|
||||||
=> $"0x{Address:X}";
|
|
||||||
}
|
|
||||||
@@ -5,6 +5,7 @@ using FFXIVClientStructs.FFXIV.Client.Game.Control;
|
|||||||
using FFXIVClientStructs.FFXIV.Client.UI.Agent;
|
using FFXIVClientStructs.FFXIV.Client.UI.Agent;
|
||||||
using Penumbra.GameData.Actors;
|
using Penumbra.GameData.Actors;
|
||||||
using Penumbra.GameData.Enums;
|
using Penumbra.GameData.Enums;
|
||||||
|
using Penumbra.GameData.Interop;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections;
|
using System.Collections;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
@@ -14,25 +15,11 @@ using System.Threading.Tasks;
|
|||||||
|
|
||||||
namespace CustomizePlus.GameData.Services;
|
namespace CustomizePlus.GameData.Services;
|
||||||
|
|
||||||
public class ObjectManager : IReadOnlyDictionary<ActorIdentifier, ActorData>
|
public class ObjectManager(IFramework framework, IClientState clientState, global::Penumbra.GameData.Interop.ObjectManager objects, ActorManager actorManager, ITargetManager targetManager)
|
||||||
|
: IReadOnlyDictionary<ActorIdentifier, ActorData>
|
||||||
{
|
{
|
||||||
private readonly IFramework _framework;
|
public global::Penumbra.GameData.Interop.ObjectManager Objects
|
||||||
private readonly IClientState _clientState;
|
=> objects;
|
||||||
private readonly IObjectTable _objects;
|
|
||||||
private readonly ActorManager _actorManager;
|
|
||||||
private readonly ITargetManager _targets;
|
|
||||||
|
|
||||||
public IObjectTable Objects
|
|
||||||
=> _objects;
|
|
||||||
|
|
||||||
public ObjectManager(IFramework framework, IClientState clientState, IObjectTable objects, ActorManager actorManager, ITargetManager targets)
|
|
||||||
{
|
|
||||||
_framework = framework;
|
|
||||||
_clientState = clientState;
|
|
||||||
_objects = objects;
|
|
||||||
_actorManager = actorManager;
|
|
||||||
_targets = targets;
|
|
||||||
}
|
|
||||||
|
|
||||||
public DateTime LastUpdate { get; private set; }
|
public DateTime LastUpdate { get; private set; }
|
||||||
|
|
||||||
@@ -48,39 +35,39 @@ public class ObjectManager : IReadOnlyDictionary<ActorIdentifier, ActorData>
|
|||||||
|
|
||||||
public void Update()
|
public void Update()
|
||||||
{
|
{
|
||||||
var lastUpdate = _framework.LastUpdate;
|
var lastUpdate = framework.LastUpdate;
|
||||||
if (lastUpdate <= LastUpdate)
|
if (lastUpdate <= LastUpdate)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
LastUpdate = lastUpdate;
|
LastUpdate = lastUpdate;
|
||||||
World = (ushort)(_clientState.LocalPlayer?.CurrentWorld.Id ?? 0u);
|
World = (ushort)(clientState.LocalPlayer?.CurrentWorld.Id ?? 0u);
|
||||||
_identifiers.Clear();
|
_identifiers.Clear();
|
||||||
_allWorldIdentifiers.Clear();
|
_allWorldIdentifiers.Clear();
|
||||||
_nonOwnedIdentifiers.Clear();
|
_nonOwnedIdentifiers.Clear();
|
||||||
|
|
||||||
for (var i = 0; i < (int)ScreenActor.CutsceneStart; ++i)
|
for (var i = 0; i < (int)ScreenActor.CutsceneStart; ++i)
|
||||||
{
|
{
|
||||||
Actor character = _objects.GetObjectAddress(i);
|
Actor character = objects[i];
|
||||||
if (character.Identifier(_actorManager, out var identifier))
|
if (character.Identifier(actorManager, out var identifier))
|
||||||
HandleIdentifier(identifier, character);
|
HandleIdentifier(identifier, character);
|
||||||
}
|
}
|
||||||
|
|
||||||
for (var i = (int)ScreenActor.CutsceneStart; i < (int)ScreenActor.CutsceneEnd; ++i)
|
for (var i = (int)ScreenActor.CutsceneStart; i < (int)ScreenActor.CutsceneEnd; ++i)
|
||||||
{
|
{
|
||||||
Actor character = _objects.GetObjectAddress(i);
|
Actor character = objects[i];
|
||||||
// Technically the game does not create holes in cutscenes or GPose.
|
// Technically the game does not create holes in cutscenes or GPose.
|
||||||
// But for Brio compatibility, we allow holes in GPose.
|
// But for Brio compatibility, we allow holes in GPose.
|
||||||
// Since GPose always has the event actor in the first cutscene slot, we can still optimize in this case.
|
// Since GPose always has the event actor in the first cutscene slot, we can still optimize in this case.
|
||||||
if (!character.Valid && i == (int)ScreenActor.CutsceneStart)
|
if (!character.Valid && i == (int)ScreenActor.CutsceneStart)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
HandleIdentifier(character.GetIdentifier(_actorManager), character);
|
HandleIdentifier(character.GetIdentifier(actorManager), character);
|
||||||
}
|
}
|
||||||
|
|
||||||
void AddSpecial(ScreenActor idx, string label)
|
void AddSpecial(ScreenActor idx, string label)
|
||||||
{
|
{
|
||||||
Actor actor = _objects.GetObjectAddress((int)idx);
|
Actor actor = objects[(int)idx];
|
||||||
if (actor.Identifier(_actorManager, out var ident))
|
if (actor.Identifier(actorManager, out var ident))
|
||||||
{
|
{
|
||||||
var data = new ActorData(actor, label);
|
var data = new ActorData(actor, label);
|
||||||
_identifiers.Add(ident, data);
|
_identifiers.Add(ident, data);
|
||||||
@@ -96,10 +83,10 @@ public class ObjectManager : IReadOnlyDictionary<ActorIdentifier, ActorData>
|
|||||||
AddSpecial(ScreenActor.Card7, "Card Actor 7");
|
AddSpecial(ScreenActor.Card7, "Card Actor 7");
|
||||||
AddSpecial(ScreenActor.Card8, "Card Actor 8");
|
AddSpecial(ScreenActor.Card8, "Card Actor 8");
|
||||||
|
|
||||||
for (var i = (int)ScreenActor.ScreenEnd; i < _objects.Length; ++i)
|
for (var i = (int)ScreenActor.ScreenEnd; i < objects.Count; ++i)
|
||||||
{
|
{
|
||||||
Actor character = _objects.GetObjectAddress(i);
|
Actor character = objects[i];
|
||||||
if (character.Identifier(_actorManager, out var identifier))
|
if (character.Identifier(actorManager, out var identifier))
|
||||||
HandleIdentifier(identifier, character);
|
HandleIdentifier(identifier, character);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -124,7 +111,7 @@ public class ObjectManager : IReadOnlyDictionary<ActorIdentifier, ActorData>
|
|||||||
|
|
||||||
if (identifier.Type is IdentifierType.Player or IdentifierType.Owned)
|
if (identifier.Type is IdentifierType.Player or IdentifierType.Owned)
|
||||||
{
|
{
|
||||||
var allWorld = _actorManager.CreateIndividualUnchecked(identifier.Type, identifier.PlayerName, ushort.MaxValue,
|
var allWorld = actorManager.CreateIndividualUnchecked(identifier.Type, identifier.PlayerName, ushort.MaxValue,
|
||||||
identifier.Kind,
|
identifier.Kind,
|
||||||
identifier.DataId);
|
identifier.DataId);
|
||||||
|
|
||||||
@@ -141,7 +128,7 @@ public class ObjectManager : IReadOnlyDictionary<ActorIdentifier, ActorData>
|
|||||||
|
|
||||||
if (identifier.Type is IdentifierType.Owned)
|
if (identifier.Type is IdentifierType.Owned)
|
||||||
{
|
{
|
||||||
var nonOwned = _actorManager.CreateNpc(identifier.Kind, identifier.DataId);
|
var nonOwned = actorManager.CreateNpc(identifier.Kind, identifier.DataId);
|
||||||
if (!_nonOwnedIdentifiers.TryGetValue(nonOwned, out var nonOwnedData))
|
if (!_nonOwnedIdentifiers.TryGetValue(nonOwned, out var nonOwnedData))
|
||||||
{
|
{
|
||||||
nonOwnedData = new ActorData(character, nonOwned.ToString());
|
nonOwnedData = new ActorData(character, nonOwned.ToString());
|
||||||
@@ -155,26 +142,26 @@ public class ObjectManager : IReadOnlyDictionary<ActorIdentifier, ActorData>
|
|||||||
}
|
}
|
||||||
|
|
||||||
public Actor GPosePlayer
|
public Actor GPosePlayer
|
||||||
=> _objects.GetObjectAddress((int)ScreenActor.GPosePlayer);
|
=> objects[(int)ScreenActor.GPosePlayer];
|
||||||
|
|
||||||
public Actor Player
|
public Actor Player
|
||||||
=> _objects.GetObjectAddress(0);
|
=> objects[0];
|
||||||
|
|
||||||
public unsafe Actor Target
|
public unsafe Actor Target
|
||||||
=> _clientState.IsGPosing ? TargetSystem.Instance()->GPoseTarget : TargetSystem.Instance()->Target;
|
=> clientState.IsGPosing ? TargetSystem.Instance()->GPoseTarget : TargetSystem.Instance()->Target;
|
||||||
|
|
||||||
public Actor Focus
|
public Actor Focus
|
||||||
=> _targets.FocusTarget?.Address ?? nint.Zero;
|
=> targetManager.FocusTarget?.Address ?? nint.Zero;
|
||||||
|
|
||||||
public Actor MouseOver
|
public Actor MouseOver
|
||||||
=> _targets.MouseOverTarget?.Address ?? nint.Zero;
|
=> targetManager.MouseOverTarget?.Address ?? nint.Zero;
|
||||||
|
|
||||||
public (ActorIdentifier Identifier, ActorData Data) PlayerData
|
public (ActorIdentifier Identifier, ActorData Data) PlayerData
|
||||||
{
|
{
|
||||||
get
|
get
|
||||||
{
|
{
|
||||||
Update();
|
Update();
|
||||||
return Player.Identifier(_actorManager, out var ident) && _identifiers.TryGetValue(ident, out var data)
|
return Player.Identifier(actorManager, out var ident) && _identifiers.TryGetValue(ident, out var data)
|
||||||
? (ident, data)
|
? (ident, data)
|
||||||
: (ident, ActorData.Invalid);
|
: (ident, ActorData.Invalid);
|
||||||
}
|
}
|
||||||
@@ -185,7 +172,7 @@ public class ObjectManager : IReadOnlyDictionary<ActorIdentifier, ActorData>
|
|||||||
get
|
get
|
||||||
{
|
{
|
||||||
Update();
|
Update();
|
||||||
return Target.Identifier(_actorManager, out var ident) && _identifiers.TryGetValue(ident, out var data)
|
return Target.Identifier(actorManager, out var ident) && _identifiers.TryGetValue(ident, out var data)
|
||||||
? (ident, data)
|
? (ident, data)
|
||||||
: (ident, ActorData.Invalid);
|
: (ident, ActorData.Invalid);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ using CustomizePlus.Core.Extensions;
|
|||||||
using CustomizePlus.Armatures.Events;
|
using CustomizePlus.Armatures.Events;
|
||||||
using CustomizePlus.Armatures.Data;
|
using CustomizePlus.Armatures.Data;
|
||||||
using CustomizePlus.GameData.Extensions;
|
using CustomizePlus.GameData.Extensions;
|
||||||
|
using Penumbra.GameData.Interop;
|
||||||
|
|
||||||
namespace CustomizePlus.Api.Compatibility;
|
namespace CustomizePlus.Api.Compatibility;
|
||||||
|
|
||||||
|
|||||||
@@ -1,26 +1,25 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using Dalamud.Plugin.Services;
|
|
||||||
using OtterGui.Log;
|
|
||||||
using OtterGui.Classes;
|
|
||||||
using Penumbra.GameData.Actors;
|
|
||||||
using System.Numerics;
|
using System.Numerics;
|
||||||
using CustomizePlus.Core.Data;
|
|
||||||
using CustomizePlus.Armatures.Events;
|
|
||||||
using CustomizePlus.Armatures.Data;
|
using CustomizePlus.Armatures.Data;
|
||||||
|
using CustomizePlus.Armatures.Events;
|
||||||
|
using CustomizePlus.Core.Data;
|
||||||
|
using CustomizePlus.Core.Extensions;
|
||||||
|
using CustomizePlus.Game.Services;
|
||||||
|
using CustomizePlus.GameData.Extensions;
|
||||||
|
using CustomizePlus.GameData.Services;
|
||||||
using CustomizePlus.Profiles;
|
using CustomizePlus.Profiles;
|
||||||
using CustomizePlus.Profiles.Data;
|
using CustomizePlus.Profiles.Data;
|
||||||
using CustomizePlus.Game.Services;
|
|
||||||
using CustomizePlus.Templates.Events;
|
|
||||||
using CustomizePlus.Profiles.Events;
|
using CustomizePlus.Profiles.Events;
|
||||||
using CustomizePlus.Core.Extensions;
|
using CustomizePlus.Templates.Events;
|
||||||
using CustomizePlus.GameData.Data;
|
using Dalamud.Plugin.Services;
|
||||||
using CustomizePlus.GameData.Services;
|
using OtterGui.Classes;
|
||||||
using CustomizePlus.GameData.Extensions;
|
using OtterGui.Log;
|
||||||
using FFXIVClientStructs.FFXIV.Client.Graphics.Scene;
|
using Penumbra.GameData.Actors;
|
||||||
using System.Drawing;
|
|
||||||
using Penumbra.GameData.Enums;
|
using Penumbra.GameData.Enums;
|
||||||
|
using Penumbra.GameData.Interop;
|
||||||
|
using ObjectManager = CustomizePlus.GameData.Services.ObjectManager;
|
||||||
|
|
||||||
namespace CustomizePlus.Armatures.Services;
|
namespace CustomizePlus.Armatures.Services;
|
||||||
|
|
||||||
|
|||||||
@@ -33,6 +33,7 @@ using Penumbra.GameData.Actors;
|
|||||||
using Penumbra.GameData.Enums;
|
using Penumbra.GameData.Enums;
|
||||||
using Penumbra.GameData.Structs;
|
using Penumbra.GameData.Structs;
|
||||||
using OtterGui;
|
using OtterGui;
|
||||||
|
using OtterGui.Raii;
|
||||||
using CustomizePlus.Api;
|
using CustomizePlus.Api;
|
||||||
|
|
||||||
namespace CustomizePlus.Core;
|
namespace CustomizePlus.Core;
|
||||||
@@ -62,7 +63,7 @@ public static class ServiceManagerBuilder
|
|||||||
services.AddIServices(typeof(EquipItem).Assembly);
|
services.AddIServices(typeof(EquipItem).Assembly);
|
||||||
services.AddIServices(typeof(Plugin).Assembly);
|
services.AddIServices(typeof(Plugin).Assembly);
|
||||||
services.AddIServices(typeof(ObjectManager).Assembly);
|
services.AddIServices(typeof(ObjectManager).Assembly);
|
||||||
services.AddIServices(typeof(ImGuiUtil).Assembly);
|
services.AddIServices(typeof(ImRaii).Assembly);
|
||||||
|
|
||||||
services.CreateProvider();
|
services.CreateProvider();
|
||||||
|
|
||||||
|
|||||||
@@ -25,6 +25,8 @@ public class DalamudServices
|
|||||||
.AddDalamudService<IKeyState>(pi)
|
.AddDalamudService<IKeyState>(pi)
|
||||||
.AddDalamudService<IDataManager>(pi)
|
.AddDalamudService<IDataManager>(pi)
|
||||||
.AddDalamudService<IPluginLog>(pi)
|
.AddDalamudService<IPluginLog>(pi)
|
||||||
.AddDalamudService<ITargetManager>(pi);
|
.AddDalamudService<ITargetManager>(pi)
|
||||||
|
.AddDalamudService<INotificationManager>(pi)
|
||||||
|
.AddDalamudService<IContextMenu>(pi);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -10,6 +10,7 @@ using CustomizePlus.Configuration.Data;
|
|||||||
using CustomizePlus.Profiles;
|
using CustomizePlus.Profiles;
|
||||||
using CustomizePlus.Armatures.Services;
|
using CustomizePlus.Armatures.Services;
|
||||||
using CustomizePlus.GameData.Data;
|
using CustomizePlus.GameData.Data;
|
||||||
|
using Penumbra.GameData.Interop;
|
||||||
|
|
||||||
namespace CustomizePlus.Core.Services;
|
namespace CustomizePlus.Core.Services;
|
||||||
|
|
||||||
|
|||||||
@@ -13,7 +13,7 @@
|
|||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFramework>net7.0-windows</TargetFramework>
|
<TargetFramework>net8.0-windows</TargetFramework>
|
||||||
<Platforms>x64</Platforms>
|
<Platforms>x64</Platforms>
|
||||||
<Nullable>enable</Nullable>
|
<Nullable>enable</Nullable>
|
||||||
<LangVersion>preview</LangVersion>
|
<LangVersion>preview</LangVersion>
|
||||||
@@ -24,12 +24,6 @@
|
|||||||
<OutputPath>bin\$(Configuration)\</OutputPath>
|
<OutputPath>bin\$(Configuration)\</OutputPath>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
|
||||||
<Compile Remove="Util\**" />
|
|
||||||
<EmbeddedResource Remove="Util\**" />
|
|
||||||
<None Remove="Util\**" />
|
|
||||||
</ItemGroup>
|
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Content Include="..\Data\icon.png">
|
<Content Include="..\Data\icon.png">
|
||||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||||
@@ -44,7 +38,7 @@
|
|||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<!--<PackageReference Include="DalamudPackager" Version="2.1.12" />-->
|
<!--<PackageReference Include="DalamudPackager" Version="2.1.12" />-->
|
||||||
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="7.0.0" />
|
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="8.0.0" />
|
||||||
<PackageReference Include="StyleCop.Analyzers" Version="1.1.118">
|
<PackageReference Include="StyleCop.Analyzers" Version="1.1.118">
|
||||||
<PrivateAssets>all</PrivateAssets>
|
<PrivateAssets>all</PrivateAssets>
|
||||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
using Dalamud.Plugin.Services;
|
using System.Collections.Generic;
|
||||||
using Penumbra.GameData.Actors;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using CustomizePlus.Core.Data;
|
using CustomizePlus.Core.Data;
|
||||||
using CustomizePlus.GameData.Data;
|
|
||||||
using CustomizePlus.GameData.Services;
|
|
||||||
using CustomizePlus.GameData.Extensions;
|
using CustomizePlus.GameData.Extensions;
|
||||||
|
using Dalamud.Plugin.Services;
|
||||||
|
using Penumbra.GameData.Actors;
|
||||||
using Penumbra.GameData.Enums;
|
using Penumbra.GameData.Enums;
|
||||||
|
using Penumbra.GameData.Interop;
|
||||||
|
using ObjectManager = CustomizePlus.GameData.Services.ObjectManager;
|
||||||
using DalamudGameObject = Dalamud.Game.ClientState.Objects.Types.GameObject;
|
using DalamudGameObject = Dalamud.Game.ClientState.Objects.Types.GameObject;
|
||||||
|
|
||||||
namespace CustomizePlus.Game.Services;
|
namespace CustomizePlus.Game.Services;
|
||||||
|
|||||||
@@ -22,8 +22,9 @@ using CustomizePlus.GameData.Data;
|
|||||||
using CustomizePlus.GameData.Services;
|
using CustomizePlus.GameData.Services;
|
||||||
using CustomizePlus.GameData.Extensions;
|
using CustomizePlus.GameData.Extensions;
|
||||||
using CustomizePlus.Profiles.Enums;
|
using CustomizePlus.Profiles.Enums;
|
||||||
using Penumbra.GameData.Enums;
|
|
||||||
using CustomizePlus.Profiles.Exceptions;
|
using CustomizePlus.Profiles.Exceptions;
|
||||||
|
using Penumbra.GameData.Enums;
|
||||||
|
using Penumbra.GameData.Interop;
|
||||||
|
|
||||||
namespace CustomizePlus.Profiles;
|
namespace CustomizePlus.Profiles;
|
||||||
|
|
||||||
@@ -303,7 +304,6 @@ public class ProfileManager : IDisposable
|
|||||||
else
|
else
|
||||||
throw new ProfileNotFoundException();
|
throw new ProfileNotFoundException();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void SetLimitLookupToOwned(Profile profile, bool value)
|
public void SetLimitLookupToOwned(Profile profile, bool value)
|
||||||
{
|
{
|
||||||
if (profile.LimitLookupToOwnedObjects != value)
|
if (profile.LimitLookupToOwnedObjects != value)
|
||||||
|
|||||||
@@ -30,7 +30,7 @@ public abstract class TemplateComboBase : FilterComboCache<Tuple<Template, strin
|
|||||||
TemplateChanged templateChanged,
|
TemplateChanged templateChanged,
|
||||||
//TabSelected tabSelected,
|
//TabSelected tabSelected,
|
||||||
PluginConfiguration configuration)
|
PluginConfiguration configuration)
|
||||||
: base(generator, logger)
|
: base(generator, MouseWheelType.Control, logger)
|
||||||
{
|
{
|
||||||
_templateChanged = templateChanged;
|
_templateChanged = templateChanged;
|
||||||
//TabSelected = tabSelected;
|
//TabSelected = tabSelected;
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
"Punchline": "Customize your character beyond FFXIV's limitations.",
|
"Punchline": "Customize your character beyond FFXIV's limitations.",
|
||||||
"InternalName": "CustomizePlus",
|
"InternalName": "CustomizePlus",
|
||||||
"AssemblyVersion": "2.0.1.1",
|
"AssemblyVersion": "2.0.1.1",
|
||||||
"TestingAssemblyVersion": "2.0.1.1",
|
"TestingAssemblyVersion": "2.0.2.0",
|
||||||
"RepoUrl": "https://github.com/Aether-Tools/CustomizePlus",
|
"RepoUrl": "https://github.com/Aether-Tools/CustomizePlus",
|
||||||
"ApplicableVersion": "any",
|
"ApplicableVersion": "any",
|
||||||
"DalamudApiLevel": 9,
|
"DalamudApiLevel": 9,
|
||||||
@@ -17,7 +17,7 @@
|
|||||||
"LoadPriority": 0,
|
"LoadPriority": 0,
|
||||||
"DownloadLinkInstall": "https://github.com/Aether-Tools/CustomizePlus/releases/download/2.0.1.1/CustomizePlus.zip",
|
"DownloadLinkInstall": "https://github.com/Aether-Tools/CustomizePlus/releases/download/2.0.1.1/CustomizePlus.zip",
|
||||||
"DownloadLinkUpdate": "https://github.com/Aether-Tools/CustomizePlus/releases/download/2.0.1.1/CustomizePlus.zip",
|
"DownloadLinkUpdate": "https://github.com/Aether-Tools/CustomizePlus/releases/download/2.0.1.1/CustomizePlus.zip",
|
||||||
"DownloadLinkTesting": "https://github.com/Aether-Tools/CustomizePlus/releases/download/2.0.1.1/CustomizePlus.zip",
|
"DownloadLinkTesting": "https://github.com/Aether-Tools/CustomizePlus/releases/download/testing_2.0.2.0/CustomizePlus.zip",
|
||||||
"IconUrl": "https://raw.githubusercontent.com/Aether-Tools/CustomizePlus/main/Data/icon.png",
|
"IconUrl": "https://raw.githubusercontent.com/Aether-Tools/CustomizePlus/main/Data/icon.png",
|
||||||
"Tags": [
|
"Tags": [
|
||||||
"Anamnesis",
|
"Anamnesis",
|
||||||
|
|||||||
Submodule submodules/OtterGui updated: f8f3e0b9bd...2bbb9b2a8a
Submodule submodules/Penumbra.Api updated: cfc51714f7...d2a1406bc3
Submodule submodules/Penumbra.GameData updated: 260ac69cd6...529e181150
Submodule submodules/Penumbra.String updated: 620a7edf00...14e00f77d4
Reference in New Issue
Block a user