using System; using System.Numerics; using System.Runtime.InteropServices; using Dalamud.Hooking; namespace Tourist { public class GameFunctions : IDisposable { private delegate nint VistaUnlockedDelegate(ushort index, int a2, int a3); private delegate nint CreateVfxDelegate(string name); private delegate nint PlayVfxDelegate(nint vfx, float idk, int idk2); internal delegate nint RemoveVfxDelegate(nint vfx); private Plugin Plugin { get; } private nint SightseeingMaskPointer { get; } private CreateVfxDelegate CreateVfx { get; } private PlayVfxDelegate PlayVfxInternal { get; } internal RemoveVfxDelegate RemoveVfx { get; } private Hook VistaUnlockedHook { get; } public GameFunctions(Plugin plugin) { this.Plugin = plugin; var vistaUnlockedPtr = this.Plugin.SigScanner.ScanText("E8 ?? ?? ?? ?? E9 ?? ?? ?? ?? 8B CE E8 ?? ?? ?? ?? E9 ?? ?? ?? ?? 44 8B 7C 24"); this.VistaUnlockedHook = this.Plugin.GameInteropProvider.HookFromAddress(vistaUnlockedPtr, this.OnVistaUnlock); this.VistaUnlockedHook.Enable(); var maskPtr = this.Plugin.SigScanner.GetStaticAddressFromSig("8B EA 48 8D 0D ?? ?? ?? ?? 8B D3"); this.SightseeingMaskPointer = maskPtr; var createVfxPtr = this.Plugin.SigScanner.ScanText("E8 ?? ?? ?? ?? F3 0F 10 35 ?? ?? ?? ?? 48 89 43 08"); this.CreateVfx = Marshal.GetDelegateForFunctionPointer(createVfxPtr); var playVfxPtr = this.Plugin.SigScanner.ScanText("E8 ?? ?? ?? ?? 8B 4B 7C 85 C9"); this.PlayVfxInternal = Marshal.GetDelegateForFunctionPointer(playVfxPtr); var removeVfxPtr = this.Plugin.SigScanner.ScanText("40 53 48 83 EC 20 48 8B D9 48 8B 89 ?? ?? ?? ?? 48 85 C9 74 28 33 D2"); this.RemoveVfx = Marshal.GetDelegateForFunctionPointer(removeVfxPtr); } public void Dispose() { this.VistaUnlockedHook.Dispose(); } private nint OnVistaUnlock(ushort index, int a2, int a3) { try { this.Plugin.Markers.RemoveVfx(index); } catch (Exception ex) { Plugin.Log.Error(ex, "Exception in vista unlock"); } return this.VistaUnlockedHook.Original(index, a2, a3); } public unsafe nint SpawnVfx(string name, Vector3 position) { var vfx = this.CreateVfx(name); var pos = (float*) (vfx + 80); *pos = position.X; *(pos + 1) = position.Z; *(pos + 2) = position.Y; pos = (float*) (vfx + 640); *pos = 0; *(pos + 1) = 0; *(pos + 2) = 0; *(long*) (vfx + 56) |= 2; *(int*) (vfx + 652) = 0; return vfx; } internal bool PlayVfx(nint vfx) { return this.PlayVfxInternal(vfx, 0.0f, -1) == nint.Zero; } public bool HasVistaUnlocked(short index) { if (this.SightseeingMaskPointer == nint.Zero) { return false; } var byteToCheck = index >> 3; var bitToCheck = 1 << (index - 8 * byteToCheck); var maskPart = Marshal.ReadByte(this.SightseeingMaskPointer + byteToCheck); return (maskPart & bitToCheck) != 0; } } }