Switch to ActorObjectManager provided by Penumbra.GameData
This commit is contained in:
@@ -1,46 +0,0 @@
|
||||
using Penumbra.GameData.Interop;
|
||||
|
||||
namespace CustomizePlus.GameData.Data;
|
||||
|
||||
/// <summary>
|
||||
/// A single actor with its label and the list of associated game objects.
|
||||
/// </summary>
|
||||
public readonly struct ActorData
|
||||
{
|
||||
public readonly List<Actor> Objects;
|
||||
public readonly string Label;
|
||||
|
||||
public bool Valid
|
||||
=> Objects.Count > 0;
|
||||
|
||||
public ActorData(Actor actor, string label)
|
||||
{
|
||||
Objects = new List<Actor> { actor };
|
||||
Label = label;
|
||||
}
|
||||
|
||||
public static readonly ActorData Invalid = new(false);
|
||||
|
||||
private ActorData(bool _)
|
||||
{
|
||||
Objects = new List<Actor>(0);
|
||||
Label = string.Empty;
|
||||
}
|
||||
|
||||
/*public LazyString ToLazyString(string invalid)
|
||||
{
|
||||
var objects = Objects;
|
||||
return Valid
|
||||
? new LazyString(() => string.Join(", ", objects.Select(o => o.ToString())))
|
||||
: new LazyString(() => invalid);
|
||||
}*/
|
||||
|
||||
private ActorData(List<Actor> objects, string label)
|
||||
{
|
||||
Objects = objects;
|
||||
Label = label;
|
||||
}
|
||||
|
||||
public ActorData OnlyGPose()
|
||||
=> new(Objects.Where(o => o.IsGPoseOrCutscene).ToList(), Label);
|
||||
}
|
||||
@@ -5,6 +5,7 @@ using FFXIVClientStructs.FFXIV.Client.Game.Character;
|
||||
using FFXIVClientStructs.FFXIV.Client.Game.Object;
|
||||
using OtterGui.Services;
|
||||
using Penumbra.GameData.Enums;
|
||||
using Penumbra.GameData.Interop;
|
||||
using Penumbra.String;
|
||||
using System.Diagnostics;
|
||||
|
||||
|
||||
@@ -1,247 +0,0 @@
|
||||
using CustomizePlus.GameData.Data;
|
||||
using Dalamud.Game.ClientState.Objects;
|
||||
using Dalamud.Plugin;
|
||||
using Dalamud.Plugin.Services;
|
||||
using FFXIVClientStructs.FFXIV.Client.Game.Control;
|
||||
using FFXIVClientStructs.FFXIV.Client.UI.Agent;
|
||||
using OtterGui.Log;
|
||||
using Penumbra.GameData.Actors;
|
||||
using Penumbra.GameData.Enums;
|
||||
using Penumbra.GameData.Interop;
|
||||
using Penumbra.String;
|
||||
|
||||
namespace CustomizePlus.GameData.Services;
|
||||
|
||||
public class ObjectManager(
|
||||
IFramework framework,
|
||||
IClientState clientState,
|
||||
IObjectTable objects,
|
||||
IDalamudPluginInterface pi,
|
||||
Logger log,
|
||||
ActorManager actors,
|
||||
ITargetManager targets)
|
||||
: global::Penumbra.GameData.Interop.ObjectManager(pi, log, framework, objects)
|
||||
{
|
||||
public DateTime LastUpdate
|
||||
=> LastFrame;
|
||||
|
||||
private DateTime _identifierUpdate;
|
||||
|
||||
public bool IsInGPose => clientState.IsGPosing;
|
||||
//c+ custom
|
||||
public bool IsInLobby { get; private set; }
|
||||
public ushort World { get; private set; }
|
||||
|
||||
private readonly Dictionary<ActorIdentifier, ActorData> _identifiers = new(200);
|
||||
private readonly Dictionary<ActorIdentifier, ActorData> _allWorldIdentifiers = new(200);
|
||||
private readonly Dictionary<ActorIdentifier, ActorData> _nonOwnedIdentifiers = new(200);
|
||||
|
||||
public IReadOnlyDictionary<ActorIdentifier, ActorData> Identifiers
|
||||
=> _identifiers;
|
||||
|
||||
public override bool Update()
|
||||
{
|
||||
if (!base.Update() && _identifierUpdate >= LastUpdate)
|
||||
return false;
|
||||
|
||||
_identifierUpdate = LastUpdate;
|
||||
World = (ushort)(Player.Valid ? Player.HomeWorld : 0);
|
||||
_identifiers.Clear();
|
||||
_allWorldIdentifiers.Clear();
|
||||
_nonOwnedIdentifiers.Clear();
|
||||
|
||||
foreach (var actor in BattleNpcs.Concat(CutsceneCharacters))
|
||||
{
|
||||
if (actor.Identifier(actors, out var identifier))
|
||||
HandleIdentifier(identifier, actor);
|
||||
}
|
||||
|
||||
void AddSpecial(ScreenActor idx, string label)
|
||||
{
|
||||
var actor = this[(int)idx];
|
||||
if (actor.Identifier(actors, out var ident))
|
||||
{
|
||||
var data = new ActorData(actor, label);
|
||||
_identifiers.Add(ident, data);
|
||||
}
|
||||
}
|
||||
|
||||
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");
|
||||
|
||||
foreach (var actor in EventNpcs)
|
||||
{
|
||||
if (actor.Identifier(actors, out var identifier))
|
||||
HandleIdentifier(identifier, actor);
|
||||
}
|
||||
|
||||
//C+ custom
|
||||
IsInLobby = AddLobbyCharacters();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private void HandleIdentifier(ActorIdentifier identifier, Actor character)
|
||||
{
|
||||
if (!identifier.IsValid)
|
||||
return;
|
||||
|
||||
if (!_identifiers.TryGetValue(identifier, out var data))
|
||||
{
|
||||
data = new ActorData(character, identifier.ToString());
|
||||
_identifiers[identifier] = data;
|
||||
}
|
||||
else
|
||||
{
|
||||
data.Objects.Add(character);
|
||||
}
|
||||
|
||||
if (identifier.Type is IdentifierType.Player or IdentifierType.Owned)
|
||||
{
|
||||
var allWorld = actors.CreateIndividualUnchecked(identifier.Type, identifier.PlayerName, ushort.MaxValue,
|
||||
identifier.Kind,
|
||||
identifier.DataId);
|
||||
|
||||
if (!_allWorldIdentifiers.TryGetValue(allWorld, out var allWorldData))
|
||||
{
|
||||
allWorldData = new ActorData(character, allWorld.ToString());
|
||||
_allWorldIdentifiers[allWorld] = allWorldData;
|
||||
}
|
||||
else
|
||||
{
|
||||
allWorldData.Objects.Add(character);
|
||||
}
|
||||
}
|
||||
|
||||
if (identifier.Type is IdentifierType.Owned)
|
||||
{
|
||||
var nonOwned = actors.CreateNpc(identifier.Kind, identifier.DataId);
|
||||
if (!_nonOwnedIdentifiers.TryGetValue(nonOwned, out var nonOwnedData))
|
||||
{
|
||||
nonOwnedData = new ActorData(character, nonOwned.ToString());
|
||||
_nonOwnedIdentifiers[nonOwned] = nonOwnedData;
|
||||
}
|
||||
else
|
||||
{
|
||||
nonOwnedData.Objects.Add(character);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public Actor GPosePlayer
|
||||
=> this[(int)ScreenActor.GPosePlayer];
|
||||
|
||||
public Actor Player
|
||||
=> this[0];
|
||||
|
||||
public unsafe Actor Target
|
||||
=> clientState.IsGPosing ? TargetSystem.Instance()->GPoseTarget : TargetSystem.Instance()->Target;
|
||||
|
||||
public Actor Focus
|
||||
=> targets.FocusTarget?.Address ?? nint.Zero;
|
||||
|
||||
public Actor MouseOver
|
||||
=> targets.MouseOverTarget?.Address ?? nint.Zero;
|
||||
|
||||
public (ActorIdentifier Identifier, ActorData Data) PlayerData
|
||||
{
|
||||
get
|
||||
{
|
||||
Update();
|
||||
return Player.Identifier(actors, out var ident) && _identifiers.TryGetValue(ident, out var data)
|
||||
? (ident, data)
|
||||
: (ident, ActorData.Invalid);
|
||||
}
|
||||
}
|
||||
|
||||
public (ActorIdentifier Identifier, ActorData Data) TargetData
|
||||
{
|
||||
get
|
||||
{
|
||||
Update();
|
||||
return Target.Identifier(actors, out var ident) && _identifiers.TryGetValue(ident, out var data)
|
||||
? (ident, data)
|
||||
: (ident, ActorData.Invalid);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary> Also handles All Worlds players and non-owned NPCs. </summary>
|
||||
public bool ContainsKey(ActorIdentifier key)
|
||||
=> Identifiers.ContainsKey(key) || _allWorldIdentifiers.ContainsKey(key) || _nonOwnedIdentifiers.ContainsKey(key);
|
||||
|
||||
public bool TryGetValue(ActorIdentifier key, out ActorData value)
|
||||
=> Identifiers.TryGetValue(key, out value);
|
||||
|
||||
public bool TryGetValueAllWorld(ActorIdentifier key, out ActorData value)
|
||||
=> _allWorldIdentifiers.TryGetValue(key, out value);
|
||||
|
||||
public bool TryGetValueNonOwned(ActorIdentifier key, out ActorData value)
|
||||
=> _nonOwnedIdentifiers.TryGetValue(key, out value);
|
||||
|
||||
public ActorData this[ActorIdentifier key]
|
||||
=> Identifiers[key];
|
||||
|
||||
public IEnumerable<ActorIdentifier> Keys
|
||||
=> Identifiers.Keys;
|
||||
|
||||
public IEnumerable<ActorData> Values
|
||||
=> Identifiers.Values;
|
||||
|
||||
public bool GetName(string lowerName, out Actor actor)
|
||||
{
|
||||
(actor, var ret) = lowerName switch
|
||||
{
|
||||
"" => (Actor.Null, true),
|
||||
"<me>" => (Player, true),
|
||||
"self" => (Player, true),
|
||||
"<t>" => (Target, true),
|
||||
"target" => (Target, true),
|
||||
"<f>" => (Focus, true),
|
||||
"focus" => (Focus, true),
|
||||
"<mo>" => (MouseOver, true),
|
||||
"mouseover" => (MouseOver, true),
|
||||
_ => (Actor.Null, false),
|
||||
};
|
||||
return ret;
|
||||
}
|
||||
|
||||
//c+ custom
|
||||
private unsafe bool AddLobbyCharacters()
|
||||
{
|
||||
if (clientState.IsLoggedIn)
|
||||
return false;
|
||||
|
||||
var agent = AgentLobby.Instance();
|
||||
if (agent == null)
|
||||
return false;
|
||||
|
||||
var span = agent->LobbyData.CharaSelectEntries.AsSpan();
|
||||
|
||||
// The lobby uses the first 8 cutscene actors.
|
||||
int cnt = 0;
|
||||
foreach (var actor in CutsceneCharacters.Take(8))
|
||||
{
|
||||
if (!actor.Valid) //shouldn't happen so should be safe to break?
|
||||
break;
|
||||
|
||||
if (cnt >= span.Length)
|
||||
break;
|
||||
|
||||
if (span[cnt].Value == null) //should mean the end of valid actors so should be safe to break?
|
||||
break;
|
||||
|
||||
var chara = span[cnt].Value;
|
||||
HandleIdentifier(actors.CreatePlayer(new ByteString(chara->Name), chara->HomeWorldId), actor);
|
||||
|
||||
cnt++;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
52
CustomizePlus.GameData/packages.lock.json
Normal file
52
CustomizePlus.GameData/packages.lock.json
Normal file
@@ -0,0 +1,52 @@
|
||||
{
|
||||
"version": 1,
|
||||
"dependencies": {
|
||||
"net9.0-windows7.0": {
|
||||
"DotNet.ReproducibleBuilds": {
|
||||
"type": "Direct",
|
||||
"requested": "[1.2.25, )",
|
||||
"resolved": "1.2.25",
|
||||
"contentHash": "xCXiw7BCxHJ8pF6wPepRUddlh2dlQlbr81gXA72hdk4FLHkKXas7EH/n+fk5UCA/YfMqG1Z6XaPiUjDbUNBUzg=="
|
||||
},
|
||||
"JetBrains.Annotations": {
|
||||
"type": "Transitive",
|
||||
"resolved": "2024.3.0",
|
||||
"contentHash": "ox5pkeLQXjvJdyAB4b2sBYAlqZGLh3PjSnP1bQNVx72ONuTJ9+34/+Rq91Fc0dG29XG9RgZur9+NcP4riihTug=="
|
||||
},
|
||||
"Microsoft.Extensions.DependencyInjection": {
|
||||
"type": "Transitive",
|
||||
"resolved": "9.0.2",
|
||||
"contentHash": "ZffbJrskOZ40JTzcTyKwFHS5eACSWp2bUQBBApIgGV+es8RaTD4OxUG7XxFr3RIPLXtYQ1jQzF2DjKB5fZn7Qg==",
|
||||
"dependencies": {
|
||||
"Microsoft.Extensions.DependencyInjection.Abstractions": "9.0.2"
|
||||
}
|
||||
},
|
||||
"Microsoft.Extensions.DependencyInjection.Abstractions": {
|
||||
"type": "Transitive",
|
||||
"resolved": "9.0.2",
|
||||
"contentHash": "MNe7GSTBf3jQx5vYrXF0NZvn6l7hUKF6J54ENfAgCO8y6xjN1XUmKKWG464LP2ye6QqDiA1dkaWEZBYnhoZzjg=="
|
||||
},
|
||||
"ottergui": {
|
||||
"type": "Project",
|
||||
"dependencies": {
|
||||
"JetBrains.Annotations": "[2024.3.0, )",
|
||||
"Microsoft.Extensions.DependencyInjection": "[9.0.2, )"
|
||||
}
|
||||
},
|
||||
"penumbra.api": {
|
||||
"type": "Project"
|
||||
},
|
||||
"penumbra.gamedata": {
|
||||
"type": "Project",
|
||||
"dependencies": {
|
||||
"OtterGui": "[1.0.0, )",
|
||||
"Penumbra.Api": "[5.6.1, )",
|
||||
"Penumbra.String": "[1.0.6, )"
|
||||
}
|
||||
},
|
||||
"penumbra.string": {
|
||||
"type": "Project"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user