Globetrotter/Globetrotter/TreasureMaps.cs

220 lines
7.2 KiB
C#
Raw Normal View History

2021-04-05 18:56:44 +00:00
using Dalamud.Hooking;
2020-08-01 07:43:23 +00:00
using Lumina.Excel.GeneratedSheets;
using System;
using System.Collections.Generic;
2021-04-16 12:01:48 +00:00
using System.Linq;
2020-08-01 07:43:23 +00:00
using System.Runtime.InteropServices;
2021-04-05 18:56:44 +00:00
using Dalamud.Game.Text.SeStringHandling.Payloads;
2020-08-01 07:43:23 +00:00
namespace Globetrotter {
2021-03-19 20:06:45 +00:00
internal sealed class TreasureMaps : IDisposable {
private const uint TreasureMapsCode = 0x54;
2020-08-12 04:14:59 +00:00
2021-04-16 12:01:48 +00:00
private static Dictionary<uint, uint>? _mapToRow;
2021-03-19 20:06:45 +00:00
private Dictionary<uint, uint> MapToRow {
get {
if (_mapToRow != null) {
return _mapToRow;
}
2021-03-19 20:06:45 +00:00
var mapToRow = new Dictionary<uint, uint>();
2021-08-24 00:44:03 +00:00
foreach (var rank in this.Plugin.DataManager.GetExcelSheet<TreasureHuntRank>()!) {
2021-03-19 20:06:45 +00:00
var unopened = rank.ItemName.Value;
2020-08-02 02:38:47 +00:00
if (unopened == null) {
continue;
}
2021-04-16 12:01:48 +00:00
EventItem? opened;
2020-12-09 03:26:14 +00:00
// FIXME: remove this try/catch when lumina is fixed
try {
opened = rank.KeyItemName.Value;
} catch (NullReferenceException) {
opened = null;
}
2021-03-19 20:06:45 +00:00
2020-08-02 02:38:47 +00:00
if (opened == null) {
continue;
}
2021-10-24 00:56:59 +00:00
mapToRow[opened.RowId] = rank.RowId;
}
_mapToRow = mapToRow;
return _mapToRow;
}
}
2020-08-01 07:43:23 +00:00
2023-09-29 01:02:45 +00:00
private Plugin Plugin { get; }
2021-04-16 12:01:48 +00:00
private TreasureMapPacket? _lastMap;
private delegate char HandleActorControlSelfDelegate(long a1, long a2, IntPtr dataPtr);
2021-03-19 20:06:45 +00:00
2021-04-16 12:01:48 +00:00
private delegate IntPtr ShowTreasureMapDelegate(IntPtr manager, ushort rowId, ushort subRowId, byte a4);
2021-03-19 20:06:45 +00:00
private readonly Hook<HandleActorControlSelfDelegate> _acsHook;
2021-04-16 12:01:48 +00:00
private readonly Hook<ShowTreasureMapDelegate> _showMapHook;
2020-08-01 07:43:23 +00:00
2023-09-29 01:02:45 +00:00
public TreasureMaps(Plugin plugin) {
2021-08-23 01:48:54 +00:00
this.Plugin = plugin;
2021-08-23 01:48:54 +00:00
var acsPtr = this.Plugin.SigScanner.ScanText("48 89 5C 24 ?? 48 89 74 24 ?? 57 48 83 EC 30 48 8B D9 49 8B F8 41 0F B7 08");
2023-09-29 01:02:45 +00:00
this._acsHook = this.Plugin.GameInteropProvider.HookFromAddress<HandleActorControlSelfDelegate>(acsPtr, this.OnACS);
2021-03-19 20:06:45 +00:00
this._acsHook.Enable();
2021-04-16 12:01:48 +00:00
2021-12-06 22:35:41 +00:00
var showMapPtr = this.Plugin.SigScanner.ScanText("E8 ?? ?? ?? ?? 40 84 FF 0F 85 ?? ?? ?? ?? 48 8B 0D");
2023-09-29 01:02:45 +00:00
this._showMapHook = this.Plugin.GameInteropProvider.HookFromAddress<ShowTreasureMapDelegate>(showMapPtr, this.OnShowMap);
2021-04-16 12:01:48 +00:00
this._showMapHook.Enable();
}
public void Dispose() {
this._acsHook.Dispose();
this._showMapHook.Dispose();
2020-08-01 07:43:23 +00:00
}
2021-08-23 01:48:54 +00:00
public void OnHover(object? sender, ulong id) {
if (!this.Plugin.Config.ShowOnHover || this._lastMap == null || this._lastMap.EventItemId != id) {
2020-08-01 07:43:23 +00:00
return;
}
this.OpenMapLocation();
}
2021-04-16 12:01:48 +00:00
private IntPtr OnShowMap(IntPtr manager, ushort rowId, ushort subRowId, byte a4) {
try {
if (!this.OnShowMapInner(rowId, subRowId)) {
return IntPtr.Zero;
}
} catch (Exception ex) {
2023-09-29 01:02:45 +00:00
Plugin.Log.Error(ex, "Exception on show map");
2021-04-16 12:01:48 +00:00
}
return this._showMapHook.Original(manager, rowId, subRowId, a4);
}
private bool OnShowMapInner(ushort rowId, ushort subRowId) {
if (this._lastMap == null) {
try {
var eventItemId = this.MapToRow.First(entry => entry.Value == rowId);
this._lastMap = new TreasureMapPacket(eventItemId.Key, subRowId, false);
} catch (InvalidOperationException) {
// no-op
}
}
2021-08-23 01:48:54 +00:00
if (!this.Plugin.Config.ShowOnOpen && (!this.Plugin.Config.ShowOnDecipher || this._lastMap?.JustOpened != true)) {
2021-04-16 12:01:48 +00:00
return true;
}
this.OpenMapLocation();
return false;
}
private char OnACS(long a1, long a2, IntPtr dataPtr) {
2021-04-13 13:13:35 +00:00
try {
this.OnACSInner(dataPtr);
} catch (Exception ex) {
2023-09-29 01:02:45 +00:00
Plugin.Log.Error(ex, "Exception on ACS");
2021-04-13 13:13:35 +00:00
}
return this._acsHook.Original(a1, a2, dataPtr);
}
private void OnACSInner(IntPtr dataPtr) {
2021-03-19 20:06:45 +00:00
var packet = ParsePacket(dataPtr);
2020-08-01 07:43:23 +00:00
if (packet == null) {
2021-04-13 13:13:35 +00:00
return;
2020-08-01 07:43:23 +00:00
}
2021-03-19 20:06:45 +00:00
this._lastMap = packet;
2020-08-01 07:43:23 +00:00
}
public void OpenMapLocation() {
2021-03-19 20:06:45 +00:00
var packet = this._lastMap;
2020-08-01 07:43:23 +00:00
if (packet == null) {
return;
}
2021-03-19 20:06:45 +00:00
if (!this.MapToRow.TryGetValue(packet.EventItemId, out var rowId)) {
2020-08-01 07:43:23 +00:00
return;
}
2021-08-24 00:44:03 +00:00
var spot = this.Plugin.DataManager.GetExcelSheet<TreasureSpot>()!.GetRow(rowId, packet.SubRowId);
2020-08-01 07:43:23 +00:00
2021-03-19 20:06:45 +00:00
var loc = spot?.Location?.Value;
var map = loc?.Map?.Value;
var terr = map?.TerritoryType?.Value;
2020-08-01 07:43:23 +00:00
2021-03-19 20:06:45 +00:00
if (terr == null) {
2020-08-01 07:43:23 +00:00
return;
}
2021-08-23 01:48:54 +00:00
var x = ToMapCoordinate(loc!.X, map!.SizeFactor);
2021-03-19 20:06:45 +00:00
var y = ToMapCoordinate(loc.Z, map.SizeFactor);
var mapLink = new MapLinkPayload(
2020-08-01 07:43:23 +00:00
terr.RowId,
map.RowId,
ConvertMapCoordinateToRawPosition(x, map.SizeFactor),
ConvertMapCoordinateToRawPosition(y, map.SizeFactor)
);
2021-08-23 01:48:54 +00:00
this.Plugin.GameGui.OpenMapWithMapLink(mapLink);
2021-04-16 12:01:48 +00:00
if (this._lastMap != null) {
this._lastMap.JustOpened = false;
}
2020-08-01 07:43:23 +00:00
}
2021-04-16 12:01:48 +00:00
private static TreasureMapPacket? ParsePacket(IntPtr dataPtr) {
2020-12-09 03:26:14 +00:00
uint category = Marshal.ReadByte(dataPtr);
2021-03-19 20:06:45 +00:00
if (category != TreasureMapsCode) {
2020-08-01 07:43:23 +00:00
return null;
}
dataPtr += 4; // skip padding
2021-03-19 20:06:45 +00:00
var param1 = (uint) Marshal.ReadInt32(dataPtr);
2020-08-01 07:43:23 +00:00
dataPtr += 4;
2021-03-19 20:06:45 +00:00
var param2 = (uint) Marshal.ReadInt32(dataPtr);
2020-08-01 07:43:23 +00:00
dataPtr += 4;
2021-03-19 20:06:45 +00:00
var param3 = (uint) Marshal.ReadInt32(dataPtr);
2020-08-01 07:43:23 +00:00
2021-03-19 20:06:45 +00:00
var eventItemId = param1;
var subRowId = param2;
var justOpened = param3 == 1;
2020-08-01 07:43:23 +00:00
return new TreasureMapPacket(eventItemId, subRowId, justOpened);
}
private static int ConvertMapCoordinateToRawPosition(float pos, float scale) {
var c = scale / 100.0f;
2020-08-25 11:07:44 +00:00
var scaledPos = (((pos - 1.0f) * c / 41.0f * 2048.0f) - 1024.0f) / c;
2020-08-01 07:43:23 +00:00
scaledPos *= 1000.0f;
2021-03-19 20:06:45 +00:00
return (int) scaledPos;
2020-08-01 07:43:23 +00:00
}
2021-03-19 20:06:45 +00:00
2020-08-01 07:43:23 +00:00
private static float ToMapCoordinate(float val, float scale) {
var c = scale / 100f;
val *= c;
2020-08-25 11:07:44 +00:00
return (41f / c * ((val + 1024f) / 2048f)) + 1;
2020-08-01 07:43:23 +00:00
}
}
2021-03-19 20:06:45 +00:00
internal class TreasureMapPacket {
public uint EventItemId { get; }
public uint SubRowId { get; }
2021-04-16 12:01:48 +00:00
public bool JustOpened { get; set; }
2020-08-01 07:43:23 +00:00
public TreasureMapPacket(uint eventItemId, uint subRowId, bool justOpened) {
this.EventItemId = eventItemId;
this.SubRowId = subRowId;
this.JustOpened = justOpened;
}
}
}