mirror of
https://github.com/xivdev/Penumbra.git
synced 2025-12-12 18:27:24 +01:00
Some improvements of the cutscene service.
This commit is contained in:
parent
b5a71ed7b3
commit
363b220613
1 changed files with 69 additions and 5 deletions
|
|
@ -1,8 +1,10 @@
|
||||||
using Dalamud.Plugin.Services;
|
using Dalamud.Plugin.Services;
|
||||||
using FFXIVClientStructs.FFXIV.Client.Game.Character;
|
using FFXIVClientStructs.FFXIV.Client.Game.Character;
|
||||||
|
using FFXIVClientStructs.FFXIV.Client.Game.Object;
|
||||||
using OtterGui.Services;
|
using OtterGui.Services;
|
||||||
using Penumbra.GameData.Enums;
|
using Penumbra.GameData.Enums;
|
||||||
using Penumbra.Interop.Hooks.Objects;
|
using Penumbra.Interop.Hooks.Objects;
|
||||||
|
using Penumbra.String;
|
||||||
|
|
||||||
namespace Penumbra.Interop.PathResolving;
|
namespace Penumbra.Interop.PathResolving;
|
||||||
|
|
||||||
|
|
@ -22,15 +24,19 @@ public sealed class CutsceneService : IService, IDisposable
|
||||||
.Where(i => _objects[i] != null)
|
.Where(i => _objects[i] != null)
|
||||||
.Select(i => KeyValuePair.Create(i, this[i] ?? _objects[i]!));
|
.Select(i => KeyValuePair.Create(i, this[i] ?? _objects[i]!));
|
||||||
|
|
||||||
public unsafe CutsceneService(IObjectTable objects, CopyCharacter copyCharacter, CharacterDestructor characterDestructor)
|
public unsafe CutsceneService(IObjectTable objects, CopyCharacter copyCharacter, CharacterDestructor characterDestructor,
|
||||||
|
IClientState clientState)
|
||||||
{
|
{
|
||||||
_objects = objects;
|
_objects = objects;
|
||||||
_copyCharacter = copyCharacter;
|
_copyCharacter = copyCharacter;
|
||||||
_characterDestructor = characterDestructor;
|
_characterDestructor = characterDestructor;
|
||||||
_copyCharacter.Subscribe(OnCharacterCopy, CopyCharacter.Priority.CutsceneService);
|
_copyCharacter.Subscribe(OnCharacterCopy, CopyCharacter.Priority.CutsceneService);
|
||||||
_characterDestructor.Subscribe(OnCharacterDestructor, CharacterDestructor.Priority.CutsceneService);
|
_characterDestructor.Subscribe(OnCharacterDestructor, CharacterDestructor.Priority.CutsceneService);
|
||||||
|
if (clientState.IsGPosing)
|
||||||
|
RecoverGPoseActors();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Get the related actor to a cutscene actor.
|
/// Get the related actor to a cutscene actor.
|
||||||
/// Does not check for valid input index.
|
/// Does not check for valid input index.
|
||||||
|
|
@ -66,11 +72,28 @@ public sealed class CutsceneService : IService, IDisposable
|
||||||
|
|
||||||
private unsafe void OnCharacterDestructor(Character* character)
|
private unsafe void OnCharacterDestructor(Character* character)
|
||||||
{
|
{
|
||||||
if (character->GameObject.ObjectIndex is < CutsceneStartIdx or >= CutsceneEndIdx)
|
if (character->GameObject.ObjectIndex < CutsceneStartIdx)
|
||||||
return;
|
{
|
||||||
|
// Remove all associations for now non-existing actor.
|
||||||
|
for (var i = 0; i < _copiedCharacters.Length; ++i)
|
||||||
|
{
|
||||||
|
if (_copiedCharacters[i] == character->GameObject.ObjectIndex)
|
||||||
|
{
|
||||||
|
// A hack to deal with GPose actors leaving and thus losing the link, we just set the home world instead.
|
||||||
|
// I do not think this breaks anything?
|
||||||
|
var address = (GameObject*)_objects.GetObjectAddress(i + CutsceneStartIdx);
|
||||||
|
if (address != null && address->GetObjectKind() is (byte)ObjectKind.Pc)
|
||||||
|
((Character*)address)->HomeWorld = character->HomeWorld;
|
||||||
|
|
||||||
var idx = character->GameObject.ObjectIndex - CutsceneStartIdx;
|
_copiedCharacters[i] = -1;
|
||||||
_copiedCharacters[idx] = -1;
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (character->GameObject.ObjectIndex < CutsceneEndIdx)
|
||||||
|
{
|
||||||
|
var idx = character->GameObject.ObjectIndex - CutsceneStartIdx;
|
||||||
|
_copiedCharacters[idx] = -1;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private unsafe void OnCharacterCopy(Character* target, Character* source)
|
private unsafe void OnCharacterCopy(Character* target, Character* source)
|
||||||
|
|
@ -81,4 +104,45 @@ public sealed class CutsceneService : IService, IDisposable
|
||||||
var idx = target->GameObject.ObjectIndex - CutsceneStartIdx;
|
var idx = target->GameObject.ObjectIndex - CutsceneStartIdx;
|
||||||
_copiedCharacters[idx] = (short)(source != null ? source->GameObject.ObjectIndex : -1);
|
_copiedCharacters[idx] = (short)(source != null ? source->GameObject.ObjectIndex : -1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary> Try to recover GPose actors on reloads into a running game. </summary>
|
||||||
|
/// <remarks> This is not 100% accurate due to world IDs, minions etc., but will be mostly sane. </remarks>
|
||||||
|
private unsafe void RecoverGPoseActors()
|
||||||
|
{
|
||||||
|
Dictionary<ByteString, short>? actors = null;
|
||||||
|
|
||||||
|
for (var i = CutsceneStartIdx; i < CutsceneEndIdx; ++i)
|
||||||
|
{
|
||||||
|
if (!TryGetName(i, out var name))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if ((actors ??= CreateActors()).TryGetValue(name, out var idx))
|
||||||
|
_copiedCharacters[i - CutsceneStartIdx] = idx;
|
||||||
|
}
|
||||||
|
|
||||||
|
return;
|
||||||
|
|
||||||
|
bool TryGetName(int idx, out ByteString name)
|
||||||
|
{
|
||||||
|
name = ByteString.Empty;
|
||||||
|
var address = (GameObject*)_objects.GetObjectAddress(idx);
|
||||||
|
if (address == null)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
name = new ByteString(address->Name);
|
||||||
|
return !name.IsEmpty;
|
||||||
|
}
|
||||||
|
|
||||||
|
Dictionary<ByteString, short> CreateActors()
|
||||||
|
{
|
||||||
|
var ret = new Dictionary<ByteString, short>();
|
||||||
|
for (short i = 0; i < CutsceneStartIdx; ++i)
|
||||||
|
{
|
||||||
|
if (TryGetName(i, out var name))
|
||||||
|
ret.TryAdd(name, i);
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue