Skip to content

Commit

Permalink
Fix preset placement in various content types
Browse files Browse the repository at this point in the history
  • Loading branch information
sourpuh committed Dec 1, 2024
1 parent b979616 commit 1aac106
Show file tree
Hide file tree
Showing 7 changed files with 94 additions and 68 deletions.
12 changes: 12 additions & 0 deletions WaymarkStudio/ContentType.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
namespace WaymarkStudio;
public enum ContentType
{
Dungeons = 2,
Guildhests = 3,
Trials = 4,
Raids = 5,
UltimateRaids = 28,
SavetheQueen = 29,
VCDungeonFinder = 30,
ChaoticAllianceRaid = 37,
}
5 changes: 3 additions & 2 deletions WaymarkStudio/Extensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -64,8 +64,9 @@ public static MarkerPresetPlacement ToMarkerPresetPlacement(this FieldMarkerPres
internal static ushort TerritoryIdForContendId(ushort contentId)
{
var contentSheet = Plugin.DataManager.GetExcelSheet<ContentFinderCondition>();
var row = contentSheet.GetRowOrDefault(contentId);
return (ushort)row?.TerritoryType.Value.RowId;
if (contentSheet.TryGetRow(contentId, out var row))
return (ushort)row.TerritoryType.Value.RowId;
return 0;
}

public static WaymarkPreset ToPreset(this FieldMarkerPreset preset, string name = "")
Expand Down
8 changes: 5 additions & 3 deletions WaymarkStudio/FieldMarkerAddon.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,10 @@ namespace WaymarkStudio;
*/
public unsafe class FieldMarkerAddon : IDisposable
{
const uint Pages = 6;
const uint EntriesPerPage = 5;
private readonly Hook<AgentFieldMarker.Delegates.Show>? show;

int lastHover = -1;
private int lastHover = -1;

public FieldMarkerAddon()
{
Expand Down Expand Up @@ -49,7 +50,8 @@ public void AddonPostDraw(AddonEvent type, AddonArgs args)
{
if (thisPtr->HoveredPresetIndex >= 0)
{
var gamePreset = Plugin.Storage.GetNativePreset(thisPtr->SelectedPage, (uint)thisPtr->HoveredPresetIndex);
uint hoveredIndex = (uint)(thisPtr->SelectedPage * EntriesPerPage + thisPtr->HoveredPresetIndex);
var gamePreset = Plugin.Storage.GetNativePreset(hoveredIndex);
Plugin.WaymarkManager.SetHoverPreview(gamePreset.ToPreset());
}
else
Expand Down
14 changes: 2 additions & 12 deletions WaymarkStudio/Plugin.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
using Dalamud.IoC;
using Dalamud.Plugin;
using Dalamud.Plugin.Services;
using FFXIVClientStructs.FFXIV.Client.Game.Event;
using Lumina.Excel.Sheets;
using WaymarkStudio.Windows;

Expand Down Expand Up @@ -86,19 +85,10 @@ private void OnCommand(string command, string args)
{
ToggleMainUI();
}


private void OnTerritoryChange(ushort mapId)
private void OnTerritoryChange(ushort id)
{
if (DataManager.GetExcelSheet<TerritoryType>().TryGetRow(mapId, out var territory))
if (DataManager.GetExcelSheet<TerritoryType>().TryGetRow(id, out var territory))
WaymarkManager.OnTerritoryChange(territory);

#if DEBUG
if (EventFramework.GetCurrentContentType() == FFXIVClientStructs.FFXIV.Client.Game.Event.ContentType.Party)
{
Chat.Print("This is party content! Check if waymarks can be saved then go update IsSafeToDirectPlacePreset");
}
#endif
}
private void DrawUI() => WindowSystem.Draw();
public void ToggleConfigUI() => ConfigWindow.Toggle();
Expand Down
33 changes: 22 additions & 11 deletions WaymarkStudio/PresetStorage.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,7 @@ namespace WaymarkStudio;
*/
internal class PresetStorage
{
const uint Pages = 6;
const uint EntriesPerPage = 5;
const uint MaxEntries = Pages * EntriesPerPage;
const uint MaxEntries = 30;

public int CountPresetsForTerritoryId(uint territoryId)
{
Expand All @@ -32,18 +30,14 @@ public unsafe FieldMarkerPreset GetNativePreset(uint index)
}
return FieldMarkerModule.Instance()->Presets[(int)index];
}
public unsafe FieldMarkerPreset GetNativePreset(uint page, uint pageIndex)
{
return GetNativePreset(page * EntriesPerPage + pageIndex);
}

public IEnumerable<(uint, FieldMarkerPreset)> NativePresets(ushort contentFinderIdFilter = 0)
public IEnumerable<(int, FieldMarkerPreset)> NativePresets(ushort contentFinderIdFilter = 0)
{
for (uint i = 0; i < MaxEntries; i++)
for (int i = 0; i < MaxEntries; i++)
{
var nativePreset = GetNativePreset(i);
var nativePreset = GetNativePreset((uint)i);
if (contentFinderIdFilter == 0 || nativePreset.ContentFinderConditionId == contentFinderIdFilter)
yield return (i, GetNativePreset(i));
yield return (i, nativePreset);
}
}

Expand All @@ -58,4 +52,21 @@ public unsafe bool SetNativePreset(int slotNum, FieldMarkerPreset preset)
*pointer = preset;
return true;
}

public IEnumerable<(int, WaymarkPreset)> SavedPresets(ushort territoryId = 0)
{
var presets = Plugin.Config.SavedPresets;
for (int i = 0; i < presets.Count; i++)
{
var preset = presets[i];
if (territoryId == 0 || preset.TerritoryId == territoryId)
yield return (i, preset);
}
}

public void DeleteSavedPreset(int index)
{
Plugin.Config.SavedPresets.RemoveAt(index);
Plugin.Config.Save();
}
}
60 changes: 40 additions & 20 deletions WaymarkStudio/WaymarkManager.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
using Dalamud.Game.ClientState.Conditions;
using FFXIVClientStructs.FFXIV.Client.Game.Event;
using FFXIVClientStructs.FFXIV.Client.Game.UI;
using FFXIVClientStructs.FFXIV.Client.UI.Misc;
using FFXIVClientStructs.FFXIV.Common.Component.BGCollision;
Expand All @@ -10,11 +9,7 @@
using System.Numerics;
using System.Runtime.InteropServices;


namespace WaymarkStudio;

using ContentType = FFXIVClientStructs.FFXIV.Client.Game.Event.ContentType;

/**
* Manager for drafting and interacting with native waymarks.
*/
Expand All @@ -27,20 +22,24 @@ internal class WaymarkManager
internal CircleGuide circleGuide;
internal ushort territoryId;
internal ushort contentFinderId;
internal ContentType contentType;
internal string mapName;
internal Dictionary<Waymark, Vector3> placeholders = new();
internal IReadOnlyDictionary<Waymark, Vector3> hoverPreviews = EmptyWaymarks;
private readonly Stopwatch lastPlacementTimer;

private unsafe delegate byte PlaceWaymark(MarkingController* markingController, uint marker, Vector3 wPos);
private readonly PlaceWaymark? placeWaymarkFn;
private readonly PlaceWaymark placeWaymarkFn;

private unsafe delegate byte ClearWaymarks(MarkingController* markingController);
private readonly ClearWaymarks? clearWaymarksFn;

private unsafe delegate byte WaymarkSafety();
private readonly WaymarkSafety? waymarkSafetyFn;

private unsafe delegate byte PlacePreset(MarkingController* markingController, MarkerPresetPlacement* placement);
private readonly PlacePreset? placePresetFn;

internal WaymarkPreset Preset { get { return new(mapName, territoryId, contentFinderId, new Dictionary<Waymark, Vector3>(placeholders)); } }
internal IReadOnlyDictionary<Waymark, Vector3> Placeholders => placeholders;
internal IReadOnlyDictionary<Waymark, Vector3> HoverPreviews => hoverPreviews;
Expand All @@ -53,16 +52,19 @@ public WaymarkManager()
placeWaymarkFn = Marshal.GetDelegateForFunctionPointer<PlaceWaymark>(Plugin.SigScanner.ScanText("E8 ?? ?? ?? ?? 84 C0 0F 85 ?? ?? ?? ?? EB 23"));
clearWaymarksFn = Marshal.GetDelegateForFunctionPointer<ClearWaymarks>(Plugin.SigScanner.ScanText("41 55 48 83 EC 50 4C 8B E9"));
waymarkSafetyFn = Marshal.GetDelegateForFunctionPointer<WaymarkSafety>(Plugin.SigScanner.ScanText("E8 ?? ?? ?? ?? 84 C0 74 0D B0 05"));
placePresetFn = Marshal.GetDelegateForFunctionPointer<PlacePreset>(Plugin.SigScanner.ScanText("E8 ?? ?? ?? ?? 84 C0 75 1B B0 01"));
}

internal void OnTerritoryChange(TerritoryType territory)
{
territoryId = (ushort)territory.RowId;
mapName = territory.PlaceName.Value.Name.ExtractText();
contentFinderId = (ushort)territory.ContentFinderCondition.RowId;
contentType = 0;
if (contentFinderId != 0)
{
mapName = territory.ContentFinderCondition.Value.Name.ExtractText();
contentType = (ContentType)territory.ContentFinderCondition.Value.ContentType.RowId;
}
placeholders.Clear();
hoverPreviews = EmptyWaymarks;
Expand Down Expand Up @@ -106,8 +108,8 @@ internal PlacementUnsafeReason GeneralWaymarkPlacementStatus()
{
if (Plugin.ClientState.LocalPlayer == null)
return PlacementUnsafeReason.NoLocalPlayer;
if (!IsSupportedZone())
return PlacementUnsafeReason.UnsupportedZone;
if (!IsWaymarksEnabled())
return PlacementUnsafeReason.UnsupportedArea;
if (Plugin.Condition[ConditionFlag.InCombat])
return PlacementUnsafeReason.InCombat;
if (Plugin.Condition[ConditionFlag.DutyRecorderPlayback])
Expand Down Expand Up @@ -166,7 +168,7 @@ internal async void PlaceWaymarkWithRetries(Waymark waymark, Vector3 wPos, bool
await Plugin.Framework.Run(async () =>
{
// TODO tidy
while (SafePlaceWaymark(waymark, wPos))
while (!SafePlaceWaymark(waymark, wPos))
{
await Plugin.Framework.DelayTicks(100);
if (territoryId != this.territoryId)
Expand All @@ -190,7 +192,7 @@ internal bool SafePlaceWaymark(Waymark waymark, Vector3 wPos)
return false;
}

internal unsafe bool IsSupportedZone()
internal unsafe bool IsWaymarksEnabled()
{
return (waymarkSafetyFn?.Invoke() ?? 0) == 0;
}
Expand All @@ -200,6 +202,7 @@ private unsafe bool UnsafeNativePlaceWaymark(Waymark waymark, Vector3 wPos)
var status = placeWaymarkFn?.Invoke(MarkingController.Instance(), (uint)waymark, wPos);
if (status != 0)
{
// return 2 too frequent
Plugin.Chat.Print("[Report to dev] Native placement failed with status " + status);
}
return status == 0;
Expand All @@ -212,12 +215,21 @@ internal bool IsSafeToDirectPlacePreset()

internal PlacementUnsafeReason DirectPlacementStatus()
{
if (Plugin.ClientState.LocalPlayer == null)
return PlacementUnsafeReason.NoLocalPlayer;
if (Plugin.Condition[ConditionFlag.InCombat])
return PlacementUnsafeReason.InCombat;
if (EventFramework.GetCurrentContentType() is not ContentType.Instance)
return PlacementUnsafeReason.NotInInstance;
var status = GeneralWaymarkPlacementStatus();
if (status != PlacementUnsafeReason.Safe)
return status;
if (!(contentType is
ContentType.Dungeons
or ContentType.Guildhests
or ContentType.Trials
or ContentType.Raids
or ContentType.UltimateRaids
or ContentType.SavetheQueen
or ContentType.VCDungeonFinder
or ContentType.ChaoticAllianceRaid))
return PlacementUnsafeReason.UnsupportedContentType;
if (contentType is ContentType.SavetheQueen && contentFinderId is not /*DR*/760 or /*DRS*/761)
return PlacementUnsafeReason.UnsupportedContentType;
if (Placeholders.Count == 0)
return PlacementUnsafeReason.NoWaymarksPlaced;
return PlacementUnsafeReason.Safe;
Expand All @@ -241,10 +253,18 @@ public void SafePlacePreset(WaymarkPreset preset, bool clearPlaceholder = true)
}
}

private unsafe void UnsafeNativePlacePreset(FieldMarkerPreset preset)
private unsafe bool UnsafeNativePlacePreset(FieldMarkerPreset preset)
{
var placementStruct = preset.ToMarkerPresetPlacement();
MarkingController.Instance()->PlacePreset(&placementStruct);
var status = placePresetFn?.Invoke(MarkingController.Instance(), &placementStruct);
if (status != 0)
{
// 7.15 qword_1427C6F00 && (*(_BYTE *)(qword_1427C6F00 + 9) & 8) != 0
// 7.05 qword_1427316F0 && (*(_BYTE *)(qword_1427316F0 + 9) & 8) != 0
// returns 6 unsupported area?
Plugin.Chat.Print("[Report to dev] Native preset placement failed with status " + status);
}
return status == 0;
}

internal enum PlacementUnsafeReason
Expand All @@ -253,11 +273,11 @@ internal enum PlacementUnsafeReason
NoLocalPlayer,
InCombat,
DutyRecorderPlayback,
NotInInstance,
UnsupportedContentType,
NoWaymarksPlaced,
TooFar,
NotGrounded,
TooFrequent,
UnsupportedZone,
UnsupportedArea,
}
}
30 changes: 10 additions & 20 deletions WaymarkStudio/Windows/StudioWindow.cs
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ internal class StudioWindow : Window, IDisposable
internal StudioWindow()
: base("Waymark Studio", ImGuiWindowFlags.NoScrollbar | ImGuiWindowFlags.NoScrollWithMouse)
{
Size = new(310, 440);
Size = new(560, 505);
SizeCondition = ImGuiCond.Once;
SizeConstraints = new WindowSizeConstraints
{
Expand All @@ -39,13 +39,13 @@ public unsafe override void Draw()
ImGui.TableNextRow();
ImGui.TableNextColumn();
if (ImGui.CollapsingHeader("Draft", ImGuiTreeNodeFlags.DefaultOpen))
using (ImRaii.Disabled(!Plugin.WaymarkManager.IsSupportedZone()))
using (ImRaii.Disabled(!Plugin.WaymarkManager.IsWaymarksEnabled()))
DrawDraftSection();

ImGui.Spacing();

if (ImGui.CollapsingHeader("Guide", ImGuiTreeNodeFlags.DefaultOpen))
using (ImRaii.Disabled(!Plugin.WaymarkManager.IsSupportedZone()))
using (ImRaii.Disabled(!Plugin.WaymarkManager.IsWaymarksEnabled()))
DrawGuideSection();

ImGui.Spacing();
Expand All @@ -64,7 +64,7 @@ internal void DrawDraftSection()
ImGui.Checkbox("Place real marker if possible", ref Plugin.Config.PlaceRealIfPossible);
ImGui.Checkbox("Snap to grid", ref Plugin.Config.SnapXZToGrid);

using (ImRaii.Disabled(!Plugin.WaymarkManager.IsSupportedZone()))
using (ImRaii.Disabled(!Plugin.WaymarkManager.IsWaymarksEnabled()))
{
WaymarkButton(Waymark.A); ImGui.SameLine();
WaymarkButton(Waymark.B); ImGui.SameLine();
Expand Down Expand Up @@ -239,30 +239,20 @@ internal void DrawSavedPresets()

var presets = Plugin.Config.SavedPresets;
deleteIndex = -1;
int i;
for (i = 0; i < presets.Count; i++)
{
var preset = presets[i];
if (preset.TerritoryId == Plugin.WaymarkManager.territoryId)
{
DrawPresetRow(i, preset);
}
}

foreach ((var i, var preset) in Plugin.Storage.SavedPresets(Plugin.WaymarkManager.territoryId))
DrawPresetRow(i, preset);

if (deleteIndex >= 0)
{
presets.RemoveAt(deleteIndex);
Plugin.Config.Save();
}
Plugin.Storage.DeleteSavedPreset(deleteIndex);

if (Plugin.WaymarkManager.contentFinderId > 0)
{
ImGui.TableNextRow();
ImGui.TableNextColumn();
ImGui.Text($"\nNative Presets");
foreach ((var j, var nativePreset) in Plugin.Storage.NativePresets(Plugin.WaymarkManager.contentFinderId))
{
DrawPresetRow(i++, nativePreset.ToPreset($"{j + 1}. Game Preset"), isReadOnly: true);
}
DrawPresetRow(j, nativePreset.ToPreset($"{j + 1}. Game Preset"), isReadOnly: true);
}
ImGui.EndTable();
}
Expand Down

0 comments on commit 1aac106

Please sign in to comment.