90 lines
4.0 KiB
C#
90 lines
4.0 KiB
C#
|
using Dalamud.Game.ClientState.Objects.Types;
|
|||
|
using Dalamud.Hooking;
|
|||
|
using Dalamud.Logging;
|
|||
|
using Dalamud.Memory;
|
|||
|
using Dalamud.Utility.Signatures;
|
|||
|
using System;
|
|||
|
using System.Text;
|
|||
|
|
|||
|
namespace Pal.Client
|
|||
|
{
|
|||
|
internal unsafe class Hooks
|
|||
|
{
|
|||
|
#pragma warning disable CS0649
|
|||
|
private delegate nint ActorVfxCreateDelegate(char* a1, nint a2, nint a3, float a4, char a5, ushort a6, char a7);
|
|||
|
|
|||
|
[Signature("40 53 55 56 57 48 81 EC ?? ?? ?? ?? 0F 29 B4 24 ?? ?? ?? ?? 48 8B 05 ?? ?? ?? ?? 48 33 C4 48 89 84 24 ?? ?? ?? ?? 0F B6 AC 24 ?? ?? ?? ?? 0F 28 F3 49 8B F8", DetourName = nameof(ActorVfxCreate))]
|
|||
|
private Hook<ActorVfxCreateDelegate> ActorVfxCreateHook { get; init; } = null!;
|
|||
|
#pragma warning restore CS0649
|
|||
|
|
|||
|
public Hooks()
|
|||
|
{
|
|||
|
SignatureHelper.Initialise(this);
|
|||
|
ActorVfxCreateHook.Enable();
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// Even with a pomander of sight, the BattleChara's position for the trap remains at {0, 0, 0} until it is activated.
|
|||
|
/// Upon exploding, the trap's position is moved to the exact location that the pomander of sight would have revealed.
|
|||
|
///
|
|||
|
/// That exact position appears to be used for VFX playing when you walk into it - even if you barely walk into the
|
|||
|
/// outer ring of an otter/luring/impeding/landmine trap, the VFX plays at the exact center and not at your character's
|
|||
|
/// location.
|
|||
|
///
|
|||
|
/// Especially at higher floors, you're more likely to walk into an undiscovered trap compared to e.g. 51-60,
|
|||
|
/// and you probably don't want to/can't use sight on every floor - yet the trap location is still useful information.
|
|||
|
///
|
|||
|
/// Some (but not all) chests also count as BattleChara named 'Trap', however the effect upon opening isn't played via
|
|||
|
/// ActorVfxCreate even if they explode (but probably as a Vfx with static location, doesn't matter for here).
|
|||
|
///
|
|||
|
/// Landmines and luring traps also don't play a VFX attached to their BattleChara.
|
|||
|
///
|
|||
|
/// otter: vfx/common/eff/dk05th_stdn0t.avfx <br/>
|
|||
|
/// toading: vfx/common/eff/dk05th_stdn0t.avfx <br/>
|
|||
|
/// enfeebling: vfx/common/eff/dk05th_stdn0t.avfx <br/>
|
|||
|
/// landmine: none <br/>
|
|||
|
/// luring: none <br/>
|
|||
|
/// impeding: vfx/common/eff/dk05ht_ipws0t.avfx (one of silence/pacification) <br/>
|
|||
|
/// impeding: vfx/common/eff/dk05ht_slet0t.avfx (the other of silence/pacification) <br/>
|
|||
|
///
|
|||
|
/// It is of course annoying that, when testing, almost all traps are landmines.
|
|||
|
/// There's also vfx/common/eff/dk01gd_inv0h.avfx for e.g. impeding when you're invulnerable, but not sure if that
|
|||
|
/// has other trigger conditions.
|
|||
|
/// </summary>
|
|||
|
public nint ActorVfxCreate(char* a1, nint a2, nint a3, float a4, char a5, ushort a6, char a7)
|
|||
|
{
|
|||
|
try
|
|||
|
{
|
|||
|
if (Service.Plugin.IsInDeepDungeon())
|
|||
|
{
|
|||
|
var vfxPath = MemoryHelper.ReadString(new nint(a1), Encoding.ASCII, 256);
|
|||
|
var obj = Service.ObjectTable.CreateObjectReference(a2);
|
|||
|
|
|||
|
/*
|
|||
|
if (Service.Configuration.BetaKey == "VFX")
|
|||
|
Service.Chat.Print($"{vfxPath} on {obj}");
|
|||
|
*/
|
|||
|
|
|||
|
if (obj is BattleChara bc && (bc.NameId == /* potd */ 5042 || bc.NameId == /* hoh */ 7395))
|
|||
|
{
|
|||
|
if (vfxPath == "vfx/common/eff/dk05th_stdn0t.avfx" || vfxPath == "vfx/common/eff/dk05ht_ipws0t.avfx")
|
|||
|
{
|
|||
|
Service.Plugin.NextUpdateObjects.Enqueue(obj.Address);
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
catch (Exception e)
|
|||
|
{
|
|||
|
PluginLog.Error(e, "VFX Create Hook failed");
|
|||
|
}
|
|||
|
return ActorVfxCreateHook.Original(a1, a2, a3, a4, a5, a6, a7);
|
|||
|
}
|
|||
|
|
|||
|
public void Dispose()
|
|||
|
{
|
|||
|
ActorVfxCreateHook?.Dispose();
|
|||
|
}
|
|||
|
}
|
|||
|
}
|