diff --git a/Data/goat.png b/Data/goat.png deleted file mode 100644 index 4ca06ac..0000000 Binary files a/Data/goat.png and /dev/null differ diff --git a/SamplePlugin/Configuration.cs b/SamplePlugin/Configuration.cs deleted file mode 100644 index de783a4..0000000 --- a/SamplePlugin/Configuration.cs +++ /dev/null @@ -1,19 +0,0 @@ -using Dalamud.Configuration; -using System; - -namespace SamplePlugin; - -[Serializable] -public class Configuration : IPluginConfiguration -{ - public int Version { get; set; } = 0; - - public bool IsConfigWindowMovable { get; set; } = true; - public bool SomePropertyToBeSavedAndWithADefault { get; set; } = true; - - // The below exists just to make saving less cumbersome - public void Save() - { - Plugin.PluginInterface.SavePluginConfig(this); - } -} diff --git a/SamplePlugin/Plugin.cs b/SamplePlugin/Plugin.cs deleted file mode 100644 index d00c733..0000000 --- a/SamplePlugin/Plugin.cs +++ /dev/null @@ -1,86 +0,0 @@ -using Dalamud.Game.Command; -using Dalamud.IoC; -using Dalamud.Plugin; -using System.IO; -using Dalamud.Interface.Windowing; -using Dalamud.Plugin.Services; -using SamplePlugin.Windows; - -namespace SamplePlugin; - -public sealed class Plugin : IDalamudPlugin -{ - [PluginService] internal static IDalamudPluginInterface PluginInterface { get; private set; } = null!; - [PluginService] internal static ITextureProvider TextureProvider { get; private set; } = null!; - [PluginService] internal static ICommandManager CommandManager { get; private set; } = null!; - [PluginService] internal static IClientState ClientState { get; private set; } = null!; - [PluginService] internal static IPlayerState PlayerState { get; private set; } = null!; - [PluginService] internal static IDataManager DataManager { get; private set; } = null!; - [PluginService] internal static IPluginLog Log { get; private set; } = null!; - - private const string CommandName = "/pmycommand"; - - public Configuration Configuration { get; init; } - - public readonly WindowSystem WindowSystem = new("SamplePlugin"); - private ConfigWindow ConfigWindow { get; init; } - private MainWindow MainWindow { get; init; } - - public Plugin() - { - Configuration = PluginInterface.GetPluginConfig() as Configuration ?? new Configuration(); - - // You might normally want to embed resources and load them from the manifest stream - var goatImagePath = Path.Combine(PluginInterface.AssemblyLocation.Directory?.FullName!, "goat.png"); - - ConfigWindow = new ConfigWindow(this); - MainWindow = new MainWindow(this, goatImagePath); - - WindowSystem.AddWindow(ConfigWindow); - WindowSystem.AddWindow(MainWindow); - - CommandManager.AddHandler(CommandName, new CommandInfo(OnCommand) - { - HelpMessage = "A useful message to display in /xlhelp" - }); - - // Tell the UI system that we want our windows to be drawn through the window system - PluginInterface.UiBuilder.Draw += WindowSystem.Draw; - - // This adds a button to the plugin installer entry of this plugin which allows - // toggling the display status of the configuration ui - PluginInterface.UiBuilder.OpenConfigUi += ToggleConfigUi; - - // Adds another button doing the same but for the main ui of the plugin - PluginInterface.UiBuilder.OpenMainUi += ToggleMainUi; - - // Add a simple message to the log with level set to information - // Use /xllog to open the log window in-game - // Example Output: 00:57:54.959 | INF | [SamplePlugin] ===A cool log message from Sample Plugin=== - Log.Information($"===A cool log message from {PluginInterface.Manifest.Name}==="); - } - - public void Dispose() - { - // Unregister all actions to not leak anything during disposal of plugin - PluginInterface.UiBuilder.Draw -= WindowSystem.Draw; - PluginInterface.UiBuilder.OpenConfigUi -= ToggleConfigUi; - PluginInterface.UiBuilder.OpenMainUi -= ToggleMainUi; - - WindowSystem.RemoveAllWindows(); - - ConfigWindow.Dispose(); - MainWindow.Dispose(); - - CommandManager.RemoveHandler(CommandName); - } - - private void OnCommand(string command, string args) - { - // In response to the slash command, toggle the display status of our main ui - MainWindow.Toggle(); - } - - public void ToggleConfigUi() => ConfigWindow.Toggle(); - public void ToggleMainUi() => MainWindow.Toggle(); -} diff --git a/SamplePlugin/SamplePlugin.json b/SamplePlugin/SamplePlugin.json deleted file mode 100644 index c806554..0000000 --- a/SamplePlugin/SamplePlugin.json +++ /dev/null @@ -1,12 +0,0 @@ -{ - "Author": "your name here", - "Name": "Sample Plugin", - "Punchline": "A short one-liner that shows up in /xlplugins.", - "Description": "A description that shows up in /xlplugins. List any major slash-command(s).", - "ApplicableVersion": "any", - "Tags": [ - "sample", - "plugin", - "goats" - ] -} diff --git a/SamplePlugin/Windows/ConfigWindow.cs b/SamplePlugin/Windows/ConfigWindow.cs deleted file mode 100644 index 5a43c5f..0000000 --- a/SamplePlugin/Windows/ConfigWindow.cs +++ /dev/null @@ -1,59 +0,0 @@ -using System; -using System.Numerics; -using Dalamud.Bindings.ImGui; -using Dalamud.Interface.Windowing; - -namespace SamplePlugin.Windows; - -public class ConfigWindow : Window, IDisposable -{ - private readonly Configuration configuration; - - // We give this window a constant ID using ###. - // This allows for labels to be dynamic, like "{FPS Counter}fps###XYZ counter window", - // and the window ID will always be "###XYZ counter window" for ImGui - public ConfigWindow(Plugin plugin) : base("A Wonderful Configuration Window###With a constant ID") - { - Flags = ImGuiWindowFlags.NoResize | ImGuiWindowFlags.NoCollapse | ImGuiWindowFlags.NoScrollbar | - ImGuiWindowFlags.NoScrollWithMouse; - - Size = new Vector2(232, 90); - SizeCondition = ImGuiCond.Always; - - configuration = plugin.Configuration; - } - - public void Dispose() { } - - public override void PreDraw() - { - // Flags must be added or removed before Draw() is being called, or they won't apply - if (configuration.IsConfigWindowMovable) - { - Flags &= ~ImGuiWindowFlags.NoMove; - } - else - { - Flags |= ImGuiWindowFlags.NoMove; - } - } - - public override void Draw() - { - // Can't ref a property, so use a local copy - var configValue = configuration.SomePropertyToBeSavedAndWithADefault; - if (ImGui.Checkbox("Random Config Bool", ref configValue)) - { - configuration.SomePropertyToBeSavedAndWithADefault = configValue; - // Can save immediately on change if you don't want to provide a "Save and Close" button - configuration.Save(); - } - - var movable = configuration.IsConfigWindowMovable; - if (ImGui.Checkbox("Movable Config Window", ref movable)) - { - configuration.IsConfigWindowMovable = movable; - configuration.Save(); - } - } -} diff --git a/SamplePlugin/Windows/MainWindow.cs b/SamplePlugin/Windows/MainWindow.cs deleted file mode 100644 index d1e12ad..0000000 --- a/SamplePlugin/Windows/MainWindow.cs +++ /dev/null @@ -1,102 +0,0 @@ -using System; -using System.Numerics; -using Dalamud.Bindings.ImGui; -using Dalamud.Interface.Utility; -using Dalamud.Interface.Utility.Raii; -using Dalamud.Interface.Windowing; -using Lumina.Excel.Sheets; - -namespace SamplePlugin.Windows; - -public class MainWindow : Window, IDisposable -{ - private readonly string goatImagePath; - private readonly Plugin plugin; - - // We give this window a hidden ID using ##. - // The user will see "My Amazing Window" as window title, - // but for ImGui the ID is "My Amazing Window##With a hidden ID" - public MainWindow(Plugin plugin, string goatImagePath) - : base("My Amazing Window##With a hidden ID", ImGuiWindowFlags.NoScrollbar | ImGuiWindowFlags.NoScrollWithMouse) - { - SizeConstraints = new WindowSizeConstraints - { - MinimumSize = new Vector2(375, 330), - MaximumSize = new Vector2(float.MaxValue, float.MaxValue) - }; - - this.goatImagePath = goatImagePath; - this.plugin = plugin; - } - - public void Dispose() { } - - public override void Draw() - { - ImGui.Text($"The random config bool is {plugin.Configuration.SomePropertyToBeSavedAndWithADefault}"); - - if (ImGui.Button("Show Settings")) - { - plugin.ToggleConfigUi(); - } - - ImGui.Spacing(); - - // Normally a BeginChild() would have to be followed by an unconditional EndChild(), - // ImRaii takes care of this after the scope ends. - // This works for all ImGui functions that require specific handling, examples are BeginTable() or Indent(). - using (var child = ImRaii.Child("SomeChildWithAScrollbar", Vector2.Zero, true)) - { - // Check if this child is drawing - if (child.Success) - { - ImGui.Text("Have a goat:"); - var goatImage = Plugin.TextureProvider.GetFromFile(goatImagePath).GetWrapOrDefault(); - if (goatImage != null) - { - using (ImRaii.PushIndent(55f)) - { - ImGui.Image(goatImage.Handle, goatImage.Size); - } - } - else - { - ImGui.Text("Image not found."); - } - - ImGuiHelpers.ScaledDummy(20.0f); - - // Example for other services that Dalamud provides. - // PlayerState provides a wrapper filled with information about the player character. - - var playerState = Plugin.PlayerState; - if (!playerState.IsLoaded) - { - ImGui.Text("Our local player is currently not logged in."); - return; - } - - if (!playerState.ClassJob.IsValid) - { - ImGui.Text("Our current job is currently not valid."); - return; - } - - // If you want to see the Macro representation of this SeString use `.ToMacroString()` - // More info about SeStrings: https://dalamud.dev/plugin-development/sestring/ - ImGui.Text($"Our current job is ({playerState.ClassJob.RowId}) '{playerState.ClassJob.Value.Abbreviation}' with level {playerState.Level}"); - - // Example for querying Lumina, getting the name of our current area. - var territoryId = Plugin.ClientState.TerritoryType; - if (Plugin.DataManager.GetExcelSheet().TryGetRow(territoryId, out var territoryRow)) - { - ImGui.Text($"We are currently in ({territoryId}) '{territoryRow.PlaceName.Value.Name}'"); - } - else - { - ImGui.Text("Invalid territory."); - } - } - } - } -} diff --git a/SamplePlugin.sln b/Waypoints.sln similarity index 89% rename from SamplePlugin.sln rename to Waypoints.sln index 31c0066..ae963d1 100644 --- a/SamplePlugin.sln +++ b/Waypoints.sln @@ -3,7 +3,7 @@ Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio Version 16 VisualStudioVersion = 16.0.29709.97 MinimumVisualStudioVersion = 10.0.40219.1 -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SamplePlugin", "SamplePlugin\SamplePlugin.csproj", "{13C812E9-0D42-4B95-8646-40EEBF30636F}" +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Waypoints", "Waypoints\Waypoints.csproj", "{13C812E9-0D42-4B95-8646-40EEBF30636F}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution diff --git a/Waypoints/Configuration.cs b/Waypoints/Configuration.cs new file mode 100644 index 0000000..011a6f5 --- /dev/null +++ b/Waypoints/Configuration.cs @@ -0,0 +1,28 @@ +using Dalamud.Configuration; +using Dalamud.Plugin; +using System; +using System.Collections.Generic; +using System.Numerics; + +namespace Waypoints; + +[Serializable] +public class Configuration : IPluginConfiguration +{ + public int Version { get; set; } = 0; + + public Dictionary Waypoints = new(); + + [NonSerialized] + private IDalamudPluginInterface? PluginInterface; + + public void Initialize(IDalamudPluginInterface pluginInterface) + { + PluginInterface = pluginInterface; + } + + public void Save() + { + PluginInterface!.SavePluginConfig(this); + } +} diff --git a/Waypoints/MainWindow.cs b/Waypoints/MainWindow.cs new file mode 100644 index 0000000..c3cfad6 --- /dev/null +++ b/Waypoints/MainWindow.cs @@ -0,0 +1,58 @@ +using Dalamud.Bindings.ImGui; +using Dalamud.Interface.Windowing; +using System; +using System.Numerics; + +namespace Waypoints; + +public class MainWindow : Window, IDisposable +{ + private string newName = string.Empty; + + public MainWindow() + : base("Waypoints") + { + SizeConstraints = new WindowSizeConstraints + { + MinimumSize = new Vector2(375, 330), + MaximumSize = new Vector2(float.MaxValue, float.MaxValue) + }; + } + + public void Dispose() { } + + public override void Draw() + { + ImGui.InputText("", ref newName, 128); + ImGui.SameLine(); + if (ImGui.Button("Add Waypoint")) + { + if (newName.Length == 0) + { + return; + } + Plugin.Configuration.Waypoints.Add(newName, (Utils.CurrentTerritoryId(), Utils.CurrentMapId(), Utils.CurrentPosition() ?? Vector3.Zero)); + Plugin.Configuration.Save(); + Plugin.WotsitIpc.Update(); + newName = string.Empty; + } + + ImGui.Spacing(); + + ImGui.Text("Waypoints"); + foreach (var kvp in Plugin.Configuration.Waypoints) + { + if (ImGui.Button(kvp.Key)) + { + Utils.OpenMapAt(kvp.Value.Item1, kvp.Value.Item2, kvp.Value.Item3); + } + ImGui.SameLine(); + if (ImGui.Button($"Delete##{kvp.Key}")) + { + Plugin.Configuration.Waypoints.Remove(kvp.Key); + Plugin.Configuration.Save(); + Plugin.WotsitIpc.Update(); + } + } + } +} diff --git a/Waypoints/Plugin.cs b/Waypoints/Plugin.cs new file mode 100644 index 0000000..b8d574a --- /dev/null +++ b/Waypoints/Plugin.cs @@ -0,0 +1,37 @@ +using Dalamud.Interface.Windowing; +using Dalamud.IoC; +using Dalamud.Plugin; +using Dalamud.Plugin.Services; + +namespace Waypoints; + +public sealed class Plugin : IDalamudPlugin +{ + [PluginService] public static IDalamudPluginInterface PluginInterface { get; private set; } = null!; + [PluginService] public static IClientState ClientState { get; private set; } = null!; + [PluginService] public static IObjectTable ObjectTable { get; private set; } = null!; + public static Configuration Configuration { get; private set; } = null!; + public readonly WindowSystem WindowSystem = new("Waypoints"); + public static WotsitIpc WotsitIpc { get; private set; } = null!; + public static MainWindow MainWindow { get; private set; } = null!; + + public Plugin() + { + Configuration = PluginInterface.GetPluginConfig() as Configuration ?? new Configuration(); + Configuration.Initialize(PluginInterface); + + WotsitIpc = new(); + + MainWindow = new(); + WindowSystem.AddWindow(MainWindow); + PluginInterface.UiBuilder.Draw += WindowSystem.Draw; + PluginInterface.UiBuilder.OpenMainUi += MainWindow.Toggle; + } + + public void Dispose() + { + Configuration.Save(); + WindowSystem.RemoveAllWindows(); + WotsitIpc.Dispose(); + } +} diff --git a/Waypoints/Utils.cs b/Waypoints/Utils.cs new file mode 100644 index 0000000..5685886 --- /dev/null +++ b/Waypoints/Utils.cs @@ -0,0 +1,29 @@ +using FFXIVClientStructs.FFXIV.Client.UI.Agent; +using System.Numerics; + +namespace Waypoints +{ + public static class Utils + { + public static unsafe uint CurrentTerritoryId() + { + return AgentMap.Instance()->CurrentTerritoryId; + } + + public static unsafe uint CurrentMapId() + { + return AgentMap.Instance()->CurrentMapId; + } + + public static Vector3? CurrentPosition() + { + return Plugin.ObjectTable.LocalPlayer?.Position; + } + + public static unsafe void OpenMapAt(uint territoryId, uint mapId, Vector3 position) + { + AgentMap.Instance()->SetFlagMapMarker(territoryId, mapId, position); + AgentMap.Instance()->OpenMap(mapId); + } + } +} diff --git a/SamplePlugin/SamplePlugin.csproj b/Waypoints/Waypoints.csproj similarity index 65% rename from SamplePlugin/SamplePlugin.csproj rename to Waypoints/Waypoints.csproj index 5cad3cb..3f60da8 100644 --- a/SamplePlugin/SamplePlugin.csproj +++ b/Waypoints/Waypoints.csproj @@ -6,11 +6,4 @@ AGPL-3.0-or-later false - - - - PreserveNewest - false - - diff --git a/Waypoints/Waypoints.json b/Waypoints/Waypoints.json new file mode 100644 index 0000000..0ecdfeb --- /dev/null +++ b/Waypoints/Waypoints.json @@ -0,0 +1,11 @@ +{ + "Author": "SuspiciousActivity", + "Name": "Waypoints", + "Punchline": "Save Waypoints and access them using Wotsit.", + "Description": "Save Waypoints and access them using Wotsit.", + "ApplicableVersion": "any", + "Tags": [ + "waypoint", + "wotsit" + ] +} diff --git a/Waypoints/WotsitIpc.cs b/Waypoints/WotsitIpc.cs new file mode 100644 index 0000000..4fbb063 --- /dev/null +++ b/Waypoints/WotsitIpc.cs @@ -0,0 +1,97 @@ +using System; +using System.Collections.Generic; +using System.Numerics; +using System.Timers; +using Dalamud.Plugin.Ipc; + +namespace Waypoints +{ + public class WotsitIpc + { + private ICallGateSubscriber _registerWithSearch; + private ICallGateSubscriber _invoke; + private ICallGateSubscriber _unregisterAll; + private Timer _timer; + + private Dictionary _map = new(); + + public WotsitIpc() + { + _registerWithSearch = Plugin.PluginInterface.GetIpcSubscriber("FA.RegisterWithSearch"); + _unregisterAll = Plugin.PluginInterface.GetIpcSubscriber("FA.UnregisterAll"); + + _invoke = Plugin.PluginInterface.GetIpcSubscriber("FA.Invoke"); + _invoke.Subscribe(Invoke); + _timer = new Timer(5000); + _timer.Elapsed += (sender, e) => RetryInit(); + if (!Update()) + { + _timer.Start(); + } + } + + private void RetryInit() + { + _registerWithSearch = Plugin.PluginInterface.GetIpcSubscriber("FA.RegisterWithSearch"); + _unregisterAll = Plugin.PluginInterface.GetIpcSubscriber("FA.UnregisterAll"); + + _invoke = Plugin.PluginInterface.GetIpcSubscriber("FA.Invoke"); + _invoke.Subscribe(Invoke); + if (Update()) + { + _timer.Stop(); + } + } + + public void Dispose() + { + GC.SuppressFinalize(this); + + UnregisterAll(); + } + + public bool Update() + { + _map.Clear(); + if (!UnregisterAll()) + { + return false; + } + + foreach (var kvp in Plugin.Configuration.Waypoints) + { + string guid = _registerWithSearch.InvokeFunc( + Plugin.PluginInterface.InternalName, + $"Waypoint: {kvp.Key}", + $"Waypoint: {kvp.Key}", + 66472 + ); + + _map.Add(guid, (kvp.Value.Item1, kvp.Value.Item2, kvp.Value.Item3)); + } + return true; + } + + public void Invoke(string guid) + { + if (!_map.TryGetValue(guid, out var value)) + { + return; + } + Utils.OpenMapAt(value.Item1, value.Item2, value.Item3); + } + + public bool UnregisterAll() + { + try + { + _unregisterAll.InvokeFunc(Plugin.PluginInterface.InternalName); + return true; + } + catch + { + return false; + } + } + } +} diff --git a/SamplePlugin/packages.lock.json b/Waypoints/packages.lock.json similarity index 100% rename from SamplePlugin/packages.lock.json rename to Waypoints/packages.lock.json