PalacePal/Pal.Client/DependencyInjection/GameHooks.cs

107 lines
4.7 KiB
C#
Raw Permalink Normal View History

2023-02-22 22:58:05 +00:00
using System;
using System.Text;
using Dalamud.Game.ClientState.Objects;
using Dalamud.Game.ClientState.Objects.Types;
2023-02-05 03:21:24 +00:00
using Dalamud.Hooking;
using Dalamud.Memory;
2023-10-03 09:08:38 +00:00
using Dalamud.Plugin.Services;
2023-02-05 03:21:24 +00:00
using Dalamud.Utility.Signatures;
using Microsoft.Extensions.Logging;
2023-02-22 22:58:05 +00:00
using Pal.Client.Floors;
2023-02-05 03:21:24 +00:00
2023-03-30 20:01:43 +00:00
namespace Pal.Client.DependencyInjection;
internal sealed unsafe class GameHooks : IDisposable
2023-02-05 03:21:24 +00:00
{
2023-03-30 20:01:43 +00:00
private readonly ILogger<GameHooks> _logger;
2023-10-03 09:08:38 +00:00
private readonly IObjectTable _objectTable;
2023-03-30 20:01:43 +00:00
private readonly TerritoryState _territoryState;
private readonly FrameworkService _frameworkService;
2023-02-15 22:17:19 +00:00
2023-02-05 03:21:24 +00:00
#pragma warning disable CS0649
2023-03-30 20:01:43 +00:00
private delegate nint ActorVfxCreateDelegate(char* a1, nint a2, nint a3, float a4, char a5, ushort a6, char a7);
2023-02-05 03:21:24 +00:00
2023-03-30 20:01:43 +00:00
[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!;
2023-02-05 03:21:24 +00:00
#pragma warning restore CS0649
2023-10-03 09:08:38 +00:00
public GameHooks(ILogger<GameHooks> logger, IObjectTable objectTable, TerritoryState territoryState, FrameworkService frameworkService, IGameInteropProvider gameInteropProvider)
2023-03-30 20:01:43 +00:00
{
_logger = logger;
_objectTable = objectTable;
_territoryState = territoryState;
_frameworkService = frameworkService;
2023-02-15 22:17:19 +00:00
2023-03-30 20:01:43 +00:00
_logger.LogDebug("Initializing game hooks");
2023-10-03 09:08:38 +00:00
gameInteropProvider.InitializeFromAttributes(this);
2023-03-30 20:01:43 +00:00
ActorVfxCreateHook.Enable();
2023-03-30 20:01:43 +00:00
_logger.LogDebug("Game hooks initialized");
}
2023-02-05 03:21:24 +00:00
2023-03-30 20:01:43 +00:00
/// <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.
///
2023-10-03 09:08:38 +00:00
/// Some (but not all) chests also count as BattleChara named 'Trap', however the effect upon opening isn't played via
2023-03-30 20:01:43 +00:00
/// ActorVfxCreate even if they explode (but probably as a Vfx with static location, doesn't matter for here).
2023-10-03 09:08:38 +00:00
///
2023-03-30 20:01:43 +00:00
/// Landmines and luring traps also don't play a VFX attached to their BattleChara.
2023-10-03 09:08:38 +00:00
///
2023-03-30 20:01:43 +00:00
/// 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/>
2023-10-03 09:08:38 +00:00
///
2023-03-30 20:01:43 +00:00
/// 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
2023-02-05 03:21:24 +00:00
{
2023-03-30 20:01:43 +00:00
if (_territoryState.IsInDeepDungeon())
2023-02-05 03:21:24 +00:00
{
2023-03-30 20:01:43 +00:00
var vfxPath = MemoryHelper.ReadString(new nint(a1), Encoding.ASCII, 256);
var obj = _objectTable.CreateObjectReference(a2);
2023-02-05 03:21:24 +00:00
2023-03-30 20:01:43 +00:00
/*
if (Service.Configuration.BetaKey == "VFX")
_chat.PalPrint($"{vfxPath} on {obj}");
*/
2023-02-05 03:21:24 +00:00
2023-03-30 20:01:43 +00:00
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")
2023-02-05 03:21:24 +00:00
{
2023-03-30 20:01:43 +00:00
_logger.LogDebug("VFX '{Path}' playing at {Location}", vfxPath, obj.Position);
_frameworkService.NextUpdateObjects.Enqueue(obj.Address);
2023-02-05 03:21:24 +00:00
}
}
}
}
2023-03-30 20:01:43 +00:00
catch (Exception e)
2023-02-05 03:21:24 +00:00
{
2023-03-30 20:01:43 +00:00
_logger.LogError(e, "VFX Create Hook failed");
2023-02-05 03:21:24 +00:00
}
2023-03-30 20:01:43 +00:00
return ActorVfxCreateHook.Original(a1, a2, a3, a4, a5, a6, a7);
}
public void Dispose()
{
_logger.LogDebug("Disposing game hooks");
ActorVfxCreateHook.Dispose();
2023-02-05 03:21:24 +00:00
}
}