Interface.cs 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423
  1. using System.Linq;
  2. using System.Numerics;
  3. using System.Threading.Tasks;
  4. using Dalamud.Interface;
  5. using ImGuiNET;
  6. namespace HuntBuddy
  7. {
  8. public class Interface
  9. {
  10. private readonly Plugin plugin;
  11. public bool DrawInterface;
  12. private bool drawConfigurationInterface;
  13. public Interface(Plugin plugin)
  14. {
  15. this.plugin = plugin;
  16. }
  17. public unsafe bool Draw()
  18. {
  19. var draw = true;
  20. ImGui.SetNextWindowSize(new Vector2(400 * ImGui.GetIO().FontGlobalScale, 500), ImGuiCond.Once);
  21. var windowFlags = ImGuiWindowFlags.NoDocking;
  22. if (this.plugin.Configuration.LockWindowPositions)
  23. {
  24. windowFlags |= ImGuiWindowFlags.NoResize | ImGuiWindowFlags.NoMove;
  25. }
  26. if (!ImGui.Begin($"{this.plugin.Name}", ref draw, windowFlags))
  27. {
  28. return draw;
  29. }
  30. if (!this.plugin.MobHuntEntriesReady)
  31. {
  32. ImGui.Text("Reloading data ...");
  33. ImGui.End();
  34. return draw;
  35. }
  36. if (Interface.IconButton(FontAwesomeIcon.Redo, "Reload"))
  37. {
  38. ImGui.End();
  39. this.plugin.MobHuntEntriesReady = false;
  40. Task.Run(() => this.plugin.ReloadData());
  41. return draw;
  42. }
  43. if (ImGui.IsItemHovered())
  44. {
  45. ImGui.BeginTooltip();
  46. ImGui.Text("Click this button to reload daily hunt data");
  47. ImGui.EndTooltip();
  48. }
  49. ImGui.SameLine();
  50. if (Interface.IconButton(FontAwesomeIcon.Cog, "Config"))
  51. {
  52. this.drawConfigurationInterface = !this.drawConfigurationInterface;
  53. }
  54. foreach (var expansionEntry in this.plugin.MobHuntEntries.Where(
  55. expansionEntry =>
  56. ImGui.TreeNode(expansionEntry.Key)))
  57. {
  58. foreach (var entry in expansionEntry.Value.Where(
  59. entry =>
  60. {
  61. var treeOpen = ImGui.TreeNodeEx(entry.Key.Value, ImGuiTreeNodeFlags.AllowItemOverlap);
  62. ImGui.SameLine();
  63. var killedCount = entry.Value.Count(
  64. x =>
  65. this.plugin.MobHuntStruct->CurrentKills[x.CurrentKillsOffset] ==
  66. x.NeededKills);
  67. if (killedCount != entry.Value.Count)
  68. {
  69. ImGui.Text($"({killedCount}/{entry.Value.Count})");
  70. }
  71. else
  72. {
  73. ImGui.TextColored(
  74. new Vector4(0f, 1f, 0f, 1f),
  75. $"({killedCount}/{entry.Value.Count})");
  76. }
  77. return treeOpen;
  78. }))
  79. {
  80. //ImGui.Indent();
  81. foreach (var mobHuntEntry in entry.Value)
  82. {
  83. if (Location.Database.ContainsKey(mobHuntEntry.MobHuntId))
  84. {
  85. if (Interface.IconButton(FontAwesomeIcon.MapMarkerAlt, $"pin##{mobHuntEntry.MobHuntId}"))
  86. {
  87. Location.CreateMapMarker(
  88. mobHuntEntry.TerritoryType,
  89. mobHuntEntry.MapId,
  90. mobHuntEntry.MobHuntId,
  91. mobHuntEntry.Name,
  92. Location.OpenType.None);
  93. }
  94. if (ImGui.IsItemHovered())
  95. {
  96. ImGui.BeginTooltip();
  97. ImGui.Text("Place marker on the map");
  98. ImGui.EndTooltip();
  99. }
  100. ImGui.SameLine();
  101. if (Interface.IconButton(FontAwesomeIcon.MapMarkedAlt, $"open##{mobHuntEntry.MobHuntId}"))
  102. {
  103. var includeArea = this.plugin.Configuration.IncludeAreaOnMap;
  104. if (ImGui.IsKeyDown(ImGuiKey.ModShift))
  105. {
  106. includeArea = !includeArea;
  107. }
  108. Location.CreateMapMarker(
  109. mobHuntEntry.TerritoryType,
  110. mobHuntEntry.MapId,
  111. mobHuntEntry.MobHuntId,
  112. mobHuntEntry.Name,
  113. includeArea ? Location.OpenType.ShowOpen : Location.OpenType.MarkerOpen);
  114. }
  115. if (ImGui.IsItemHovered())
  116. {
  117. var color = ImGui.IsKeyDown(ImGuiKey.ModShift) ? new Vector4(0f, 0.7f, 0f, 1f) : new Vector4(0.7f, 0.7f, 0.7f, 1f);
  118. ImGui.BeginTooltip();
  119. if (this.plugin.Configuration.IncludeAreaOnMap)
  120. {
  121. ImGui.Text("Show hunt area on the map");
  122. ImGui.TextColored(
  123. color,
  124. "Hold [SHIFT] to show the location only");
  125. }
  126. else
  127. {
  128. ImGui.Text("Show hunt location on the map");
  129. ImGui.TextColored(
  130. color,
  131. "Hold [SHIFT] to include the area");
  132. }
  133. ImGui.EndTooltip();
  134. }
  135. ImGui.SameLine();
  136. if (Plugin.TeleportConsumer?.IsAvailable == true)
  137. {
  138. if (Interface.IconButton(FontAwesomeIcon.StreetView, $"t##{mobHuntEntry.MobHuntId}"))
  139. {
  140. Location.TeleportToNearestAetheryte(
  141. mobHuntEntry.TerritoryType,
  142. mobHuntEntry.MapId,
  143. mobHuntEntry.MobHuntId);
  144. }
  145. if (ImGui.IsItemHovered())
  146. {
  147. ImGui.BeginTooltip();
  148. ImGui.Text("Teleport to nearest aetheryte");
  149. ImGui.EndTooltip();
  150. }
  151. ImGui.SameLine();
  152. }
  153. }
  154. var currentKills = this.plugin.MobHuntStruct->CurrentKills[mobHuntEntry.CurrentKillsOffset];
  155. ImGui.Text(mobHuntEntry.Name);
  156. if (ImGui.IsItemHovered())
  157. {
  158. ImGui.PushStyleColor(ImGuiCol.PopupBg, Vector4.Zero);
  159. ImGui.BeginTooltip();
  160. this.DrawHuntIcon(mobHuntEntry);
  161. ImGui.PopStyleColor();
  162. ImGui.EndTooltip();
  163. }
  164. ImGui.SameLine();
  165. if (currentKills != mobHuntEntry.NeededKills)
  166. {
  167. ImGui.Text($"({currentKills}/{mobHuntEntry.NeededKills})");
  168. }
  169. else
  170. {
  171. ImGui.TextColored(
  172. new Vector4(0f, 1f, 0f, 1f),
  173. $"({currentKills}/{mobHuntEntry.NeededKills})");
  174. }
  175. }
  176. //ImGui.Unindent();
  177. ImGui.TreePop();
  178. }
  179. ImGui.TreePop();
  180. }
  181. ImGui.End();
  182. if (this.drawConfigurationInterface)
  183. {
  184. this.DrawConfiguration();
  185. }
  186. return draw;
  187. }
  188. public unsafe void DrawLocalHunts()
  189. {
  190. if (!this.plugin.Configuration.ShowLocalHunts ||
  191. this.plugin.CurrentAreaMobHuntEntries.IsEmpty ||
  192. this.plugin.CurrentAreaMobHuntEntries.Count(
  193. x =>
  194. this.plugin.MobHuntStruct->CurrentKills[x.CurrentKillsOffset] == x.NeededKills) ==
  195. this.plugin.CurrentAreaMobHuntEntries.Count)
  196. {
  197. return;
  198. }
  199. ImGui.SetNextWindowSize(Vector2.Zero, ImGuiCond.Always);
  200. var windowFlags = ImGuiWindowFlags.NoNavInputs | ImGuiWindowFlags.NoDocking;
  201. if (this.plugin.Configuration.HideLocalHuntBackground)
  202. {
  203. windowFlags |= ImGuiWindowFlags.NoBackground;
  204. }
  205. if (this.plugin.Configuration.LockWindowPositions)
  206. {
  207. windowFlags |= ImGuiWindowFlags.NoResize | ImGuiWindowFlags.NoMove;
  208. }
  209. if (!ImGui.Begin("Hunts in current area", windowFlags))
  210. {
  211. return;
  212. }
  213. foreach (var mobHuntEntry in this.plugin.CurrentAreaMobHuntEntries)
  214. {
  215. var currentKills = this.plugin.MobHuntStruct->CurrentKills[mobHuntEntry.CurrentKillsOffset];
  216. if (this.plugin.Configuration.HideCompletedHunts && currentKills == mobHuntEntry.NeededKills)
  217. {
  218. continue;
  219. }
  220. if (Location.Database.ContainsKey(mobHuntEntry.MobHuntId))
  221. {
  222. if (Interface.IconButton(FontAwesomeIcon.MapMarkerAlt, $"pin##{mobHuntEntry.MobHuntId}"))
  223. {
  224. Location.CreateMapMarker(
  225. mobHuntEntry.TerritoryType,
  226. mobHuntEntry.MapId,
  227. mobHuntEntry.MobHuntId,
  228. mobHuntEntry.Name,
  229. Location.OpenType.None);
  230. }
  231. if (ImGui.IsItemHovered())
  232. {
  233. ImGui.BeginTooltip();
  234. ImGui.Text("Place marker on the map");
  235. ImGui.EndTooltip();
  236. }
  237. ImGui.SameLine();
  238. if (Interface.IconButton(FontAwesomeIcon.MapMarkedAlt, $"open##{mobHuntEntry.MobHuntId}"))
  239. {
  240. var includeArea = this.plugin.Configuration.IncludeAreaOnMap;
  241. if (ImGui.IsKeyDown(ImGuiKey.ModShift))
  242. {
  243. includeArea = !includeArea;
  244. }
  245. Location.CreateMapMarker(
  246. mobHuntEntry.TerritoryType,
  247. mobHuntEntry.MapId,
  248. mobHuntEntry.MobHuntId,
  249. mobHuntEntry.Name,
  250. includeArea ? Location.OpenType.ShowOpen : Location.OpenType.MarkerOpen);
  251. }
  252. if (ImGui.IsItemHovered())
  253. {
  254. var color = ImGui.IsKeyDown(ImGuiKey.ModShift) ? new Vector4(0f, 0.7f, 0f, 1f) : new Vector4(0.7f, 0.7f, 0.7f, 1f);
  255. ImGui.BeginTooltip();
  256. if (this.plugin.Configuration.IncludeAreaOnMap)
  257. {
  258. ImGui.Text("Show hunt area on the map");
  259. ImGui.TextColored(
  260. color,
  261. "Hold [SHIFT] to show the location only");
  262. }
  263. else
  264. {
  265. ImGui.Text("Show hunt location on the map");
  266. ImGui.TextColored(
  267. color,
  268. "Hold [SHIFT] to include the area");
  269. }
  270. ImGui.EndTooltip();
  271. }
  272. ImGui.SameLine();
  273. }
  274. ImGui.Text($"{mobHuntEntry.Name} ({currentKills}/{mobHuntEntry.NeededKills})");
  275. if (!this.plugin.Configuration.ShowLocalHuntIcons)
  276. {
  277. continue;
  278. }
  279. this.DrawHuntIcon(mobHuntEntry);
  280. }
  281. ImGui.End();
  282. }
  283. private void DrawConfiguration()
  284. {
  285. ImGui.SetNextWindowSize(Vector2.Zero, ImGuiCond.Always);
  286. var windowFlags = ImGuiWindowFlags.NoDocking;
  287. if (this.plugin.Configuration.LockWindowPositions)
  288. {
  289. windowFlags |= ImGuiWindowFlags.NoResize | ImGuiWindowFlags.NoMove;
  290. }
  291. if (!ImGui.Begin($"{this.plugin.Name} settings", windowFlags))
  292. {
  293. return;
  294. }
  295. var save = false;
  296. save |= ImGui.Checkbox("Lock plugin window positions", ref this.plugin.Configuration.LockWindowPositions);
  297. save |= ImGui.Checkbox("Include hunt area on map by default", ref this.plugin.Configuration.IncludeAreaOnMap);
  298. save |= ImGui.Checkbox("Show hunts in local area", ref this.plugin.Configuration.ShowLocalHunts);
  299. save |= ImGui.Checkbox(
  300. "Show icons of hunts in local area",
  301. ref this.plugin.Configuration.ShowLocalHuntIcons);
  302. save |= ImGui.Checkbox(
  303. "Hide background of local hunts window",
  304. ref this.plugin.Configuration.HideLocalHuntBackground);
  305. save |= ImGui.Checkbox(
  306. "Hide completed targets in local hunts window",
  307. ref this.plugin.Configuration.HideCompletedHunts);
  308. save |= ImGui.SliderFloat("Hunt icon scale", ref this.plugin.Configuration.IconScale, 0.2f, 2f, "%.2f");
  309. if (ImGui.ColorEdit4("Hunt icon background colour", ref this.plugin.Configuration.IconBackgroundColour))
  310. {
  311. this.plugin.Configuration.IconBackgroundColourU32 =
  312. ImGui.ColorConvertFloat4ToU32(this.plugin.Configuration.IconBackgroundColour);
  313. save = true;
  314. }
  315. if (save)
  316. {
  317. this.plugin.Configuration.Save();
  318. }
  319. ImGui.End();
  320. }
  321. private static bool IconButton(FontAwesomeIcon icon, string? id = null)
  322. {
  323. ImGui.PushFont(UiBuilder.IconFont);
  324. var text = icon.ToIconString();
  325. if (id != null)
  326. {
  327. text += $"##{id}";
  328. }
  329. var result = ImGui.Button(text);
  330. ImGui.PopFont();
  331. return result;
  332. }
  333. private void DrawHuntIcon(MobHuntEntry mobHuntEntry)
  334. {
  335. var cursorPos = ImGui.GetCursorScreenPos();
  336. var imageSize = mobHuntEntry.ExpansionId < 3 ? new Vector2(192f, 128f) : new Vector2(210f);
  337. imageSize *= ImGui.GetIO().FontGlobalScale * this.plugin.Configuration.IconScale;
  338. ImGui.InvisibleButton("canvas", imageSize);
  339. var drawList = ImGui.GetWindowDrawList();
  340. if (mobHuntEntry is { ExpansionId: 4, IsEliteMark: false }) // Endwalker uses circle for non elite mobs
  341. {
  342. drawList.AddCircleFilled(
  343. cursorPos + (imageSize / 2f),
  344. imageSize.X / 2f,
  345. this.plugin.Configuration.IconBackgroundColourU32);
  346. }
  347. else
  348. {
  349. drawList.AddRectFilled(
  350. cursorPos,
  351. cursorPos + imageSize,
  352. this.plugin.Configuration.IconBackgroundColourU32);
  353. }
  354. drawList.AddImage(mobHuntEntry.Icon.ImGuiHandle, cursorPos, cursorPos + imageSize);
  355. }
  356. }
  357. }