Bläddra i källkod

Add IPC integration for XivEsp to find daily hunt mobs

This will, obviously, do nothing if you don't have XivEsp. But I'm at least reasonably sure it won't crash either. Call it like, 98% sure. And I'm not pushing a public update yet anyway, so fuck it, merge time.
Lilith 2 år sedan
förälder
incheckning
3d2c88eb2b

+ 2 - 0
HuntBuddy/Configuration.cs

@@ -11,6 +11,8 @@ public class Configuration: IPluginConfiguration {
 		set;
 	}
 
+	public bool EnableXivEspIntegration;
+	public bool AutoSetEspSearchOnNextHuntCommand;
 	public bool IncludeAreaOnMap;
 	public bool LockWindowPositions;
 	public bool ShowLocalHunts;

+ 2 - 2
HuntBuddy/DalamudPackager.targets

@@ -1,10 +1,10 @@
 <?xml version="1.0" encoding="utf-8"?>
 <Project>
-    <Target Name="PackagePlugin" AfterTargets="Build" Condition="'$(Configuration)' == 'Release'">
+    <Target Name="PackagePlugin" AfterTargets="Build">
         <DalamudPackager
                 ProjectDir="$(ProjectDir)"
                 OutputPath="$(OutputPath)"
                 AssemblyName="$(AssemblyName)"
                 MakeZip="true"/>
     </Target>
-</Project>
+</Project>

+ 38 - 0
HuntBuddy/Ipc/ConsumerBase.cs

@@ -0,0 +1,38 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace HuntBuddy.Ipc;
+public abstract class ConsumerBase {
+	public const int MS_PER_AVAILABILITY_RECHECK = 5000;
+
+	public abstract string RequiredPlugin { get; }
+
+	private bool isAvailable;
+	private long timeSinceLastCheck;
+
+	public bool IsAvailable {
+		get {
+			if (Environment.TickCount64 > this.timeSinceLastCheck + MS_PER_AVAILABILITY_RECHECK) {
+				if (Service.PluginInterface.InstalledPlugins.Any(p => p.IsLoaded && p.InternalName == this.RequiredPlugin)) {
+					try {
+						this.isAvailable = this.Validate();
+					}
+					catch {
+						this.isAvailable = false;
+					}
+				}
+				else {
+					this.isAvailable = false;
+				}
+				this.timeSinceLastCheck = Environment.TickCount64;
+			}
+
+			return this.isAvailable;
+		}
+	}
+
+	protected abstract bool Validate();
+}

+ 58 - 0
HuntBuddy/Ipc/EspConsumer.cs

@@ -0,0 +1,58 @@
+using System;
+
+using Dalamud.Plugin.Ipc;
+
+namespace HuntBuddy.Ipc;
+
+public class EspConsumer: ConsumerBase {
+	public override string RequiredPlugin { get; } = "XivEsp";
+	protected override bool Validate() {
+		this.getUnifiedSearch.InvokeFunc();
+		return true;
+	}
+
+	private ICallGateSubscriber<string> getUnifiedSearch = null!;
+	private ICallGateSubscriber<string, object> setSubstringSearch = null!;
+
+	private void Subscribe() {
+		try {
+			this.getUnifiedSearch = Service.PluginInterface.GetIpcSubscriber<string>("XivEsp.GetSearch");
+			this.setSubstringSearch = Service.PluginInterface.GetIpcSubscriber<string, object>("XivEsp.SetSubstring");
+		}
+		catch (Exception ex) {
+			Service.PluginLog.Debug($"Failed to subscribe to XivEsp\nReason: {ex}");
+		}
+	}
+
+	public EspConsumer() => this.Subscribe();
+
+	public bool CanSetSearch {
+		get {
+			try {
+				string current = this.getUnifiedSearch.InvokeFunc();
+				char type = current[0];
+				return type is 'N' or 'S';
+			}
+			catch {
+				return false;
+			}
+		}
+	}
+	public bool SearchFor(string target) {
+		try {
+			if (this.CanSetSearch) {
+				this.setSubstringSearch.InvokeAction(target);
+				return true;
+			}
+			else {
+				Service.Chat.Print("Cannot override complex (glob/regex) XivEsp search");
+				return false;
+			}
+		}
+		catch (Exception ex) {
+			Service.PluginLog.Error($"XivEsp is not responding to IPC: {ex}");
+			Service.Chat.PrintError("XivEsp plugin is not responding");
+			return false;
+		}
+	}
+}

+ 5 - 21
HuntBuddy/Ipc/TeleportConsumer.cs

@@ -4,27 +4,11 @@ using Dalamud.Plugin.Ipc;
 
 namespace HuntBuddy.Ipc;
 
-public class TeleportConsumer {
-	private bool isAvailable;
-	private long timeSinceLastCheck;
-
-	public bool IsAvailable {
-		get {
-			if (this.timeSinceLastCheck + 5000 > Environment.TickCount64) {
-				return this.isAvailable;
-			}
-
-			try {
-				this.consumerMessageSetting.InvokeFunc();
-				this.isAvailable = true;
-				this.timeSinceLastCheck = Environment.TickCount64;
-			}
-			catch {
-				this.isAvailable = false;
-			}
-
-			return this.isAvailable;
-		}
+public class TeleportConsumer: ConsumerBase {
+	public override string RequiredPlugin { get; } = "TeleporterPlugin";
+	protected override bool Validate() {
+		this.consumerMessageSetting.InvokeFunc();
+		return true;
 	}
 
 	private ICallGateSubscriber<bool> consumerMessageSetting = null!;

+ 20 - 17
HuntBuddy/Plugin.cs

@@ -1,9 +1,9 @@
 using System;
-using System.Numerics;
 using System.Collections.Concurrent;
 using System.Collections.Generic;
 using System.Globalization;
 using System.Linq;
+using System.Numerics;
 using System.Threading.Tasks;
 
 using Dalamud.Interface.Internal;
@@ -12,8 +12,6 @@ using Dalamud.Plugin;
 using Dalamud.Plugin.Services;
 using Dalamud.Utility;
 
-using Lumina.Excel.GeneratedSheets;
-
 using HuntBuddy.Attributes;
 using HuntBuddy.Ipc;
 using HuntBuddy.Structs;
@@ -23,6 +21,7 @@ using ImGuiNET;
 
 using Lumina.Data.Files;
 using Lumina.Excel;
+using Lumina.Excel.GeneratedSheets;
 using Lumina.Extensions;
 using Lumina.Text;
 
@@ -41,7 +40,8 @@ public class Plugin: IDalamudPlugin {
 	public bool MobHuntEntriesReady = true;
 	public readonly unsafe MobHuntStruct* MobHuntStruct;
 	public readonly Configuration Configuration;
-	public static TeleportConsumer? TeleportConsumer;
+	public static TeleportConsumer? TeleportConsumer { get; private set; }
+	public static EspConsumer? EspConsumer { get; private set; }
 
 	private WindowSystem WindowSystem {
 		get;
@@ -66,8 +66,8 @@ public class Plugin: IDalamudPlugin {
 		pluginInterface.Create<Service>();
 
 		this.commandManager = new PluginCommandManager<Plugin>(this, Service.Commands);
-		this.MobHuntEntries = new Dictionary<string, Dictionary<KeyValuePair<uint, string>, List<MobHuntEntry>>>();
-		this.CurrentAreaMobHuntEntries = new ConcurrentBag<MobHuntEntry>();
+		this.MobHuntEntries = [];
+		this.CurrentAreaMobHuntEntries = [];
 		this.Configuration = (Configuration)(Service.PluginInterface.GetPluginConfig() ?? new Configuration());
 		this.Configuration.IconBackgroundColourU32 =
 			ImGui.ColorConvertFloat4ToU32(this.Configuration.IconBackgroundColour);
@@ -87,6 +87,7 @@ public class Plugin: IDalamudPlugin {
 		this.WindowSystem.AddWindow(this.ConfigurationWindow);
 
 		Plugin.TeleportConsumer = new TeleportConsumer();
+		Plugin.EspConsumer = new EspConsumer();
 		Service.ClientState.TerritoryChanged += this.ClientStateOnTerritoryChanged;
 		Service.PluginInterface.UiBuilder.Draw += this.WindowSystem.Draw;
 		Service.PluginInterface.UiBuilder.OpenConfigUi += this.OpenConfigUi;
@@ -106,9 +107,9 @@ public class Plugin: IDalamudPlugin {
 		this.CurrentAreaMobHuntEntries.Clear();
 
 		foreach (MobHuntEntry mobHuntEntry in this.MobHuntEntries.SelectMany(
-			         expansionEntry => expansionEntry.Value
-				         .Where(entry => entry.Key.Key == Service.ClientState.TerritoryType)
-				         .SelectMany(entry => entry.Value))) {
+					 expansionEntry => expansionEntry.Value
+						 .Where(entry => entry.Key.Key == Service.ClientState.TerritoryType)
+						 .SelectMany(entry => entry.Value))) {
 			this.CurrentAreaMobHuntEntries.Add(mobHuntEntry);
 		}
 	}
@@ -149,7 +150,7 @@ public class Plugin: IDalamudPlugin {
 					break;
 				case "next":
 					if (this.MobHuntEntries.Count > 0) {
-						Func<MobHuntEntry, bool> filterPredicate = (MobHuntEntry entry) => entry.IsEliteMark ||
+						bool filterPredicate(MobHuntEntry entry) => entry.IsEliteMark ||
 							this.MobHuntStruct->CurrentKills[
 								entry.CurrentKillsOffset] <
 							entry.NeededKills;
@@ -181,7 +182,7 @@ public class Plugin: IDalamudPlugin {
 									.SelectMany(l => l)
 									.Where(filterPredicate)
 									.ToList()
-								: new List<MobHuntEntry>();
+								: [];
 							// if we didn't find any candidates, we try a different method to fill it
 							if (candidates.Count == 0) {
 								Service.PluginLog.Information(
@@ -209,7 +210,7 @@ public class Plugin: IDalamudPlugin {
 							}
 							else {
 								long remaining = chosen.NeededKills -
-								                 this.MobHuntStruct->CurrentKills[chosen.CurrentKillsOffset];
+												 this.MobHuntStruct->CurrentKills[chosen.CurrentKillsOffset];
 								Service.Chat.Print($"Hunting {remaining}x {chosen.Name} in {chosen.TerritoryName}");
 								Location.CreateMapMarker(
 									chosen.TerritoryType,
@@ -218,6 +219,8 @@ public class Plugin: IDalamudPlugin {
 									chosen.Name,
 									openType);
 							}
+							if (this.Configuration.EnableXivEspIntegration && this.Configuration.AutoSetEspSearchOnNextHuntCommand && Plugin.EspConsumer?.IsAvailable == true)
+								Plugin.EspConsumer.SearchFor(chosen.Name!);
 						}
 						else {
 							Service.PluginLog.Information("Unable to find a hunt mark to target");
@@ -253,7 +256,7 @@ public class Plugin: IDalamudPlugin {
 
 	public unsafe void ReloadData() {
 		this.MobHuntEntries.Clear();
-		List<MobHuntEntry> mobHuntList = new List<MobHuntEntry>();
+		List<MobHuntEntry> mobHuntList = [];
 		ExcelSheet<MobHuntOrder>? mobHuntOrderSheet = Service.DataManager.Excel.GetSheet<MobHuntOrder>()!;
 
 		foreach (BillEnum billNumber in Enum.GetValues<BillEnum>()) {
@@ -265,7 +268,7 @@ public class Plugin: IDalamudPlugin {
 				Service.DataManager.Excel.GetSheet<MobHuntOrderType>()!.GetRow((uint)billNumber)!;
 
 			uint rowId = mobHuntOrderTypeRow.OrderStart.Value!.RowId +
-			             (uint)(this.MobHuntStruct->BillOffset[mobHuntOrderTypeRow.RowId] - 1);
+						 (uint)(this.MobHuntStruct->BillOffset[mobHuntOrderTypeRow.RowId] - 1);
 
 			if (rowId > mobHuntOrderSheet.RowCount) {
 				continue;
@@ -309,14 +312,14 @@ public class Plugin: IDalamudPlugin {
 		foreach (MobHuntEntry entry in mobHuntList) {
 			string key = entry.ExpansionName ?? "Unknown";
 			KeyValuePair<uint, string> subKey =
-				new KeyValuePair<uint, string>(entry.TerritoryType, entry.TerritoryName ?? "Unknown");
+				new(entry.TerritoryType, entry.TerritoryName ?? "Unknown");
 
 			if (!this.MobHuntEntries.ContainsKey(key)) {
-				this.MobHuntEntries[key] = new Dictionary<KeyValuePair<uint, string>, List<MobHuntEntry>>();
+				this.MobHuntEntries[key] = [];
 			}
 
 			if (!this.MobHuntEntries[key].ContainsKey(subKey)) {
-				this.MobHuntEntries[key][subKey] = new List<MobHuntEntry>();
+				this.MobHuntEntries[key][subKey] = [];
 			}
 
 			this.MobHuntEntries[key][subKey].Add(entry);

+ 9 - 0
HuntBuddy/Windows/ConfigurationWindow.cs

@@ -32,6 +32,15 @@ public class ConfigurationWindow: Window {
 	public override void Draw() {
 		bool save = false;
 
+		ImGui.BeginDisabled(Plugin.EspConsumer?.IsAvailable == false);
+		save |= ImGui.Checkbox("Enable XivEsp plugin integration?", ref Plugin.Instance.Configuration.EnableXivEspIntegration);
+		ImGui.Indent();
+		save |= ImGui.Checkbox("Set XivEsp search when using '/phb next' command?", ref Plugin.Instance.Configuration.AutoSetEspSearchOnNextHuntCommand);
+		ImGui.Unindent();
+		ImGui.EndDisabled();
+
+		ImGui.Spacing();
+
 		save |= ImGui.Checkbox("Lock plugin window positions", ref Plugin.Instance.Configuration.LockWindowPositions);
 		save |= ImGui.Checkbox("Include hunt area on map by default",
 			ref Plugin.Instance.Configuration.IncludeAreaOnMap);

+ 19 - 7
HuntBuddy/Windows/LocalHuntsWindow.cs

@@ -14,7 +14,7 @@ namespace HuntBuddy.Windows;
 /// Local hunts window.
 /// </summary>
 public class LocalHuntsWindow: Window {
-	public LocalHuntsWindow(): base(
+	public LocalHuntsWindow() : base(
 		"Hunts in current area",
 		ImGuiWindowFlags.NoNavInputs | ImGuiWindowFlags.NoDocking,
 		true) {
@@ -46,12 +46,10 @@ public class LocalHuntsWindow: Window {
 		}
 	}
 
-	public override unsafe bool DrawConditions() =>
-		Plugin.Instance.Configuration.ShowLocalHunts &&
-		!Plugin.Instance.CurrentAreaMobHuntEntries.IsEmpty &&
-		Plugin.Instance.CurrentAreaMobHuntEntries.Count(
-			x => Plugin.Instance.MobHuntStruct->CurrentKills[x.CurrentKillsOffset] == x.NeededKills) !=
-		Plugin.Instance.CurrentAreaMobHuntEntries.Count;
+	public override unsafe bool DrawConditions() => Plugin.Instance.Configuration.ShowLocalHunts
+		&& !Plugin.Instance.CurrentAreaMobHuntEntries.IsEmpty
+		&& Plugin.Instance.CurrentAreaMobHuntEntries
+			.Count(x => Plugin.Instance.MobHuntStruct->CurrentKills[x.CurrentKillsOffset] == x.NeededKills) != Plugin.Instance.CurrentAreaMobHuntEntries.Count;
 
 	public override unsafe void Draw() {
 		foreach (MobHuntEntry? mobHuntEntry in Plugin.Instance.CurrentAreaMobHuntEntries) {
@@ -114,6 +112,20 @@ public class LocalHuntsWindow: Window {
 					ImGui.EndTooltip();
 				}
 
+				if (Plugin.Instance.Configuration.EnableXivEspIntegration && Plugin.EspConsumer?.IsAvailable == true) {
+
+					ImGui.SameLine();
+					if (InterfaceUtil.IconButton(FontAwesomeIcon.Search, $"esp##{mobHuntEntry.MobHuntId}")) {
+						Plugin.EspConsumer.SearchFor(mobHuntEntry.Name!);
+					}
+
+					if (ImGui.IsItemHovered()) {
+						ImGui.BeginTooltip();
+						ImGui.Text("Set XivEsp search to this target");
+						ImGui.EndTooltip();
+					}
+				}
+
 				ImGui.SameLine();
 			}
 

+ 38 - 28
HuntBuddy/Windows/MainWindow.cs

@@ -6,17 +6,17 @@ using System.Threading.Tasks;
 using Dalamud.Interface;
 using Dalamud.Interface.Windowing;
 
-using ImGuiNET;
-
 using HuntBuddy.Utils;
 
+using ImGuiNET;
+
 namespace HuntBuddy.Windows;
 
 /// <summary>
 /// Main plugin window.
 /// </summary>
 public class MainWindow: Window {
-	public MainWindow(): base(
+	public MainWindow() : base(
 		$"{Plugin.Instance.Name}",
 		ImGuiWindowFlags.NoDocking,
 		true) {
@@ -59,30 +59,26 @@ public class MainWindow: Window {
 			Plugin.Instance.OpenConfigUi();
 		}
 
-		foreach (KeyValuePair<string, Dictionary<KeyValuePair<uint, string>, List<MobHuntEntry>>> expansionEntry in
-		         Plugin.Instance.MobHuntEntries.Where(
-			         expansionEntry =>
-				         ImGui.TreeNode(expansionEntry.Key))) {
-			foreach (KeyValuePair<KeyValuePair<uint, string>, List<MobHuntEntry>> entry in expansionEntry.Value.Where(
-				         entry => {
-					         bool treeOpen = ImGui.TreeNodeEx(entry.Key.Value, ImGuiTreeNodeFlags.AllowItemOverlap);
-					         ImGui.SameLine();
-					         int killedCount = entry.Value.Count(
-						         x =>
-							         Plugin.Instance.MobHuntStruct->CurrentKills[x.CurrentKillsOffset] ==
-							         x.NeededKills);
-
-					         if (killedCount != entry.Value.Count) {
-						         ImGui.Text($"({killedCount}/{entry.Value.Count})");
-					         }
-					         else {
-						         ImGui.TextColored(
-							         new Vector4(0f, 1f, 0f, 1f),
-							         $"({killedCount}/{entry.Value.Count})");
-					         }
-
-					         return treeOpen;
-				         })) {
+		IEnumerable<KeyValuePair<string, Dictionary<KeyValuePair<uint, string>, List<MobHuntEntry>>>> expansionEntriesWithTreeNodes = Plugin.Instance
+			.MobHuntEntries
+			.Where(expansionEntry => ImGui.TreeNode(expansionEntry.Key));
+		foreach (KeyValuePair<string, Dictionary<KeyValuePair<uint, string>, List<MobHuntEntry>>> expansionEntry in expansionEntriesWithTreeNodes) {
+			IEnumerable<KeyValuePair<KeyValuePair<uint, string>, List<MobHuntEntry>>> mobEntriesWithTreeNodes = expansionEntry.Value
+				.Where(entry => {
+					bool treeOpen = ImGui.TreeNodeEx(entry.Key.Value, ImGuiTreeNodeFlags.AllowItemOverlap);
+					ImGui.SameLine();
+					int killedCount = entry.Value.Count(x => Plugin.Instance.MobHuntStruct->CurrentKills[x.CurrentKillsOffset] == x.NeededKills);
+					if (killedCount != entry.Value.Count) {
+						ImGui.Text($"({killedCount}/{entry.Value.Count})");
+					}
+					else {
+						ImGui.TextColored(
+							new Vector4(0f, 1f, 0f, 1f),
+							$"({killedCount}/{entry.Value.Count})");
+					}
+					return treeOpen;
+				});
+			foreach (KeyValuePair<KeyValuePair<uint, string>, List<MobHuntEntry>> entry in mobEntriesWithTreeNodes) {
 				foreach (MobHuntEntry? mobHuntEntry in entry.Value) {
 					if (Location.Database.ContainsKey(mobHuntEntry.MobHuntId)) {
 						if (InterfaceUtil.IconButton(FontAwesomeIcon.MapMarkerAlt, $"pin##{mobHuntEntry.MobHuntId}")) {
@@ -140,7 +136,7 @@ public class MainWindow: Window {
 						ImGui.SameLine();
 
 						if (Plugin.TeleportConsumer?.IsAvailable == true) {
-							if (InterfaceUtil.IconButton(FontAwesomeIcon.StreetView, $"t##{mobHuntEntry.MobHuntId}")) {
+							if (InterfaceUtil.IconButton(FontAwesomeIcon.StreetView, $"teleport##{mobHuntEntry.MobHuntId}")) {
 								Location.TeleportToNearestAetheryte(
 									mobHuntEntry.TerritoryType,
 									mobHuntEntry.MapId,
@@ -155,6 +151,20 @@ public class MainWindow: Window {
 
 							ImGui.SameLine();
 						}
+
+						if (Plugin.Instance.Configuration.EnableXivEspIntegration && Plugin.EspConsumer?.IsAvailable == true) {
+							if (InterfaceUtil.IconButton(FontAwesomeIcon.Search, $"esp##{mobHuntEntry.MobHuntId}")) {
+								Plugin.EspConsumer.SearchFor(mobHuntEntry.Name!);
+							}
+
+							if (ImGui.IsItemHovered()) {
+								ImGui.BeginTooltip();
+								ImGui.Text("Set XivEsp search to this target");
+								ImGui.EndTooltip();
+							}
+
+							ImGui.SameLine();
+						}
 					}
 
 					int currentKills = Plugin.Instance.MobHuntStruct->CurrentKills[mobHuntEntry.CurrentKillsOffset];