Debug markers, client implementation
This commit is contained in:
parent
74d18c95c6
commit
b959355121
89
Pal.Client/Hooks.cs
Normal file
89
Pal.Client/Hooks.cs
Normal file
@ -0,0 +1,89 @@
|
||||
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();
|
||||
}
|
||||
}
|
||||
}
|
@ -62,7 +62,7 @@ namespace Pal.Client
|
||||
|
||||
localState = new LocalState(territoryType)
|
||||
{
|
||||
Markers = new ConcurrentBag<Marker>(save.Markers),
|
||||
Markers = new ConcurrentBag<Marker>(save.Markers.Where(o => o.Type != Marker.EType.Debug)),
|
||||
};
|
||||
version = save.Version;
|
||||
}
|
||||
|
@ -53,7 +53,7 @@ namespace Pal.Client
|
||||
/// <summary>
|
||||
/// To make rollbacks of local data easier, keep track of the version which was used to write the marker initially.
|
||||
/// </summary>
|
||||
public string SinceVersion { get; set; }
|
||||
public string? SinceVersion { get; set; }
|
||||
|
||||
[JsonIgnore]
|
||||
public Element? SplatoonElement { get; set; }
|
||||
@ -86,7 +86,7 @@ namespace Pal.Client
|
||||
}
|
||||
|
||||
|
||||
public bool IsPermanent() => Type == EType.Trap || Type == EType.Hoard;
|
||||
public bool IsPermanent() => Type == EType.Trap || Type == EType.Hoard || Type == EType.Debug;
|
||||
|
||||
public enum EType
|
||||
{
|
||||
@ -95,6 +95,7 @@ namespace Pal.Client
|
||||
#region Permanent Markers
|
||||
Trap = ObjectType.Trap,
|
||||
Hoard = ObjectType.Hoard,
|
||||
Debug = ObjectType.Debug,
|
||||
#endregion
|
||||
|
||||
# region Markers that only show up if they're currently visible
|
||||
|
@ -3,7 +3,7 @@
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net7.0-windows</TargetFramework>
|
||||
<LangVersion>11.0</LangVersion>
|
||||
<Version>2.3</Version>
|
||||
<Version>2.4</Version>
|
||||
<Nullable>enable</Nullable>
|
||||
</PropertyGroup>
|
||||
|
||||
|
@ -41,6 +41,7 @@ namespace Pal.Client
|
||||
{ Marker.EType.Trap, new MarkerConfig { Radius = 1.7f } },
|
||||
{ Marker.EType.Hoard, new MarkerConfig { Radius = 1.7f, OffsetY = -0.03f } },
|
||||
{ Marker.EType.SilverCoffer, new MarkerConfig { Radius = 1f } },
|
||||
{ Marker.EType.Debug, new MarkerConfig { Radius = 1.5f } },
|
||||
};
|
||||
private LocalizedChatMessages _localizedChatMessages = new();
|
||||
|
||||
@ -53,6 +54,7 @@ namespace Pal.Client
|
||||
public string? DebugMessage { get; set; }
|
||||
internal Queue<IQueueOnFrameworkThread> EarlyEventQueue { get; } = new();
|
||||
internal Queue<IQueueOnFrameworkThread> LateEventQueue { get; } = new();
|
||||
internal ConcurrentQueue<nint> NextUpdateObjects { get; } = new();
|
||||
|
||||
public string Name => "Palace Pal";
|
||||
|
||||
@ -79,6 +81,7 @@ namespace Pal.Client
|
||||
Service.Plugin = this;
|
||||
Service.Configuration = (Configuration?)pluginInterface.GetPluginConfig() ?? pluginInterface.Create<Configuration>()!;
|
||||
Service.Configuration.Migrate();
|
||||
Service.Hooks = new Hooks();
|
||||
|
||||
var agreementWindow = pluginInterface.Create<AgreementWindow>();
|
||||
if (agreementWindow is not null)
|
||||
@ -174,6 +177,10 @@ namespace Pal.Client
|
||||
DebugNearest(m => m.Type == Marker.EType.Hoard);
|
||||
break;
|
||||
|
||||
case "dnear":
|
||||
DebugNearest(m => m.Type == Marker.EType.Debug);
|
||||
break;
|
||||
|
||||
default:
|
||||
Service.Chat.PrintError($"[Palace Pal] Unknown sub-command '{arguments}' for '{command}'.");
|
||||
break;
|
||||
@ -199,6 +206,7 @@ namespace Pal.Client
|
||||
Service.WindowSystem.RemoveAllWindows();
|
||||
|
||||
Service.RemoteApi.Dispose();
|
||||
Service.Hooks.Dispose();
|
||||
|
||||
try
|
||||
{
|
||||
@ -274,6 +282,7 @@ namespace Pal.Client
|
||||
{
|
||||
LastTerritory = Service.ClientState.TerritoryType;
|
||||
TerritorySyncState = SyncState.NotAttempted;
|
||||
NextUpdateObjects.Clear();
|
||||
|
||||
if (IsInDeepDungeon())
|
||||
GetFloorMarkers(LastTerritory);
|
||||
@ -414,6 +423,12 @@ namespace Pal.Client
|
||||
marker.SplatoonElement = element;
|
||||
elements.Add(element);
|
||||
}
|
||||
else if (marker.Type == Marker.EType.Debug && Service.Configuration.BetaKey == "VFX")
|
||||
{
|
||||
var element = CreateSplatoonElement(marker.Type, marker.Position, DetermineColor(marker, visibleMarkers));
|
||||
marker.SplatoonElement = element;
|
||||
elements.Add(element);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -444,13 +459,15 @@ namespace Pal.Client
|
||||
else
|
||||
return COLOR_INVISIBLE;
|
||||
}
|
||||
else
|
||||
else if (marker.Type == Marker.EType.Hoard)
|
||||
{
|
||||
if (PomanderOfIntuition == PomanderState.Inactive || !Service.Configuration.OnlyVisibleHoardAfterPomander || visibleMarkers.Any(x => x == marker))
|
||||
return ImGui.ColorConvertFloat4ToU32(Service.Configuration.HoardColor);
|
||||
else
|
||||
return COLOR_INVISIBLE;
|
||||
}
|
||||
else
|
||||
return ImGui.ColorConvertFloat4ToU32(new Vector4(1, 0.5f, 1, 0.4f));
|
||||
}
|
||||
|
||||
private void HandleEphemeralMarkers(IList<Marker> visibleMarkers, bool recreateLayout)
|
||||
@ -596,7 +613,7 @@ namespace Pal.Client
|
||||
var playerPosition = Service.ClientState.LocalPlayer?.Position;
|
||||
if (playerPosition == null)
|
||||
return;
|
||||
Service.Chat.Print($"[Pal] {playerPosition}");
|
||||
Service.Chat.Print($"[Palace Pal] {playerPosition}");
|
||||
|
||||
var nearbyMarkers = state.Markers
|
||||
.Where(m => predicate(m))
|
||||
@ -641,6 +658,13 @@ namespace Pal.Client
|
||||
}
|
||||
}
|
||||
|
||||
while (NextUpdateObjects.TryDequeue(out nint address))
|
||||
{
|
||||
var obj = Service.ObjectTable.FirstOrDefault(x => x.Address == address);
|
||||
if (obj != null && obj.Position.Length() > 0.1)
|
||||
result.Add(new Marker(Marker.EType.Debug, obj.Position) { Seen = true });
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
@ -27,5 +27,6 @@ namespace Pal.Client
|
||||
public static WindowSystem WindowSystem { get; set; } = new(typeof(Service).AssemblyQualifiedName);
|
||||
internal static RemoteApi RemoteApi { get; set; } = new RemoteApi();
|
||||
public static Configuration Configuration { get; set; } = null!;
|
||||
internal static Hooks Hooks { get; set; } = null!;
|
||||
}
|
||||
}
|
||||
|
@ -306,6 +306,11 @@ namespace Pal.Client.Windows
|
||||
int silverCoffers = plugin.EphemeralMarkers.Count(x => x.Type == Marker.EType.SilverCoffer);
|
||||
ImGui.Text($"{silverCoffers} silver coffer{(silverCoffers == 1 ? "" : "s")} visible on current floor");
|
||||
}
|
||||
if (Service.Configuration.BetaKey == "VFX")
|
||||
{
|
||||
int debugMarkers = currentFloor.Markers.Count(x => x.Type == Marker.EType.Debug);
|
||||
ImGui.Text($"{debugMarkers} debug marker{(debugMarkers == 1 ? "" : "s")}");
|
||||
}
|
||||
|
||||
ImGui.Text($"Pomander of Sight: {plugin.PomanderOfSight}");
|
||||
ImGui.Text($"Pomander of Intuition: {plugin.PomanderOfIntuition}");
|
||||
|
Loading…
Reference in New Issue
Block a user