Interface.cs 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433
  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. if (includeArea)
  109. {
  110. Location.CreateMapMarker(
  111. mobHuntEntry.TerritoryType,
  112. mobHuntEntry.MapId,
  113. mobHuntEntry.MobHuntId,
  114. mobHuntEntry.Name,
  115. Location.OpenType.ShowOpen);
  116. }
  117. else
  118. {
  119. Location.CreateMapMarker(
  120. mobHuntEntry.TerritoryType,
  121. mobHuntEntry.MapId,
  122. mobHuntEntry.MobHuntId,
  123. mobHuntEntry.Name);
  124. }
  125. }
  126. if (ImGui.IsItemHovered())
  127. {
  128. var color = ImGui.IsKeyDown(ImGuiKey.ModShift) ? new Vector4(0f, 0.7f, 0f, 1f) : new Vector4(0.7f, 0.7f, 0.7f, 1f);
  129. ImGui.BeginTooltip();
  130. if (this.plugin.Configuration.IncludeAreaOnMap)
  131. {
  132. ImGui.Text("Show hunt area on the map");
  133. ImGui.TextColored(
  134. color,
  135. "Hold [SHIFT] to show the location only");
  136. }
  137. else
  138. {
  139. ImGui.Text("Show hunt location on the map");
  140. ImGui.TextColored(
  141. color,
  142. "Hold [SHIFT] to include the area");
  143. }
  144. ImGui.EndTooltip();
  145. }
  146. ImGui.SameLine();
  147. if (Plugin.TeleportConsumer?.Subscribed == true)
  148. {
  149. if (Interface.IconButton(FontAwesomeIcon.StreetView, $"t##{mobHuntEntry.MobHuntId}"))
  150. {
  151. Location.TeleportToNearestAetheryte(
  152. mobHuntEntry.TerritoryType,
  153. mobHuntEntry.MapId,
  154. mobHuntEntry.MobHuntId);
  155. }
  156. if (ImGui.IsItemHovered())
  157. {
  158. ImGui.BeginTooltip();
  159. ImGui.Text("Teleport to nearest aetheryte");
  160. ImGui.EndTooltip();
  161. }
  162. ImGui.SameLine();
  163. }
  164. }
  165. var currentKills = this.plugin.MobHuntStruct->CurrentKills[mobHuntEntry.CurrentKillsOffset];
  166. ImGui.Text(mobHuntEntry.Name);
  167. if (ImGui.IsItemHovered())
  168. {
  169. ImGui.PushStyleColor(ImGuiCol.PopupBg, Vector4.Zero);
  170. ImGui.BeginTooltip();
  171. this.DrawHuntIcon(mobHuntEntry);
  172. ImGui.PopStyleColor();
  173. ImGui.EndTooltip();
  174. }
  175. ImGui.SameLine();
  176. if (currentKills != mobHuntEntry.NeededKills)
  177. {
  178. ImGui.Text($"({currentKills}/{mobHuntEntry.NeededKills})");
  179. }
  180. else
  181. {
  182. ImGui.TextColored(
  183. new Vector4(0f, 1f, 0f, 1f),
  184. $"({currentKills}/{mobHuntEntry.NeededKills})");
  185. }
  186. }
  187. //ImGui.Unindent();
  188. ImGui.TreePop();
  189. }
  190. ImGui.TreePop();
  191. }
  192. ImGui.End();
  193. if (this.drawConfigurationInterface)
  194. {
  195. this.DrawConfiguration();
  196. }
  197. return draw;
  198. }
  199. public unsafe void DrawLocalHunts()
  200. {
  201. if (!this.plugin.Configuration.ShowLocalHunts ||
  202. this.plugin.CurrentAreaMobHuntEntries.IsEmpty ||
  203. this.plugin.CurrentAreaMobHuntEntries.Count(
  204. x =>
  205. this.plugin.MobHuntStruct->CurrentKills[x.CurrentKillsOffset] == x.NeededKills) ==
  206. this.plugin.CurrentAreaMobHuntEntries.Count)
  207. {
  208. return;
  209. }
  210. ImGui.SetNextWindowSize(Vector2.Zero, ImGuiCond.Always);
  211. var windowFlags = ImGuiWindowFlags.NoNavInputs | ImGuiWindowFlags.NoDocking;
  212. if (this.plugin.Configuration.HideLocalHuntBackground)
  213. {
  214. windowFlags |= ImGuiWindowFlags.NoBackground;
  215. }
  216. if (this.plugin.Configuration.LockWindowPositions)
  217. {
  218. windowFlags |= ImGuiWindowFlags.NoResize | ImGuiWindowFlags.NoMove;
  219. }
  220. if (!ImGui.Begin("Hunts in current area", windowFlags))
  221. {
  222. return;
  223. }
  224. foreach (var mobHuntEntry in this.plugin.CurrentAreaMobHuntEntries)
  225. {
  226. var currentKills = this.plugin.MobHuntStruct->CurrentKills[mobHuntEntry.CurrentKillsOffset];
  227. if (this.plugin.Configuration.HideCompletedHunts && currentKills == mobHuntEntry.NeededKills)
  228. {
  229. continue;
  230. }
  231. if (Location.Database.ContainsKey(mobHuntEntry.MobHuntId))
  232. {
  233. if (Interface.IconButton(FontAwesomeIcon.MapMarkerAlt, $"pin##{mobHuntEntry.MobHuntId}"))
  234. {
  235. Location.CreateMapMarker(
  236. mobHuntEntry.TerritoryType,
  237. mobHuntEntry.MapId,
  238. mobHuntEntry.MobHuntId,
  239. mobHuntEntry.Name,
  240. Location.OpenType.None);
  241. }
  242. if (ImGui.IsItemHovered())
  243. {
  244. ImGui.BeginTooltip();
  245. ImGui.Text("Place marker on the map");
  246. ImGui.EndTooltip();
  247. }
  248. ImGui.SameLine();
  249. if (Interface.IconButton(FontAwesomeIcon.Compass, $"openRadius##{mobHuntEntry.MobHuntId}"))
  250. {
  251. Location.CreateMapMarker(
  252. mobHuntEntry.TerritoryType,
  253. mobHuntEntry.MapId,
  254. mobHuntEntry.MobHuntId,
  255. mobHuntEntry.Name,
  256. Location.OpenType.ShowOpen);
  257. }
  258. if (ImGui.IsItemHovered())
  259. {
  260. ImGui.BeginTooltip();
  261. ImGui.Text("Show hunt area on the map");
  262. ImGui.EndTooltip();
  263. }
  264. ImGui.SameLine();
  265. if (Interface.IconButton(FontAwesomeIcon.MapMarkedAlt, $"open##{mobHuntEntry.MobHuntId}"))
  266. {
  267. Location.CreateMapMarker(
  268. mobHuntEntry.TerritoryType,
  269. mobHuntEntry.MapId,
  270. mobHuntEntry.MobHuntId,
  271. mobHuntEntry.Name);
  272. }
  273. if (ImGui.IsItemHovered())
  274. {
  275. ImGui.BeginTooltip();
  276. ImGui.Text("Show hunt location on the map");
  277. ImGui.EndTooltip();
  278. }
  279. ImGui.SameLine();
  280. }
  281. ImGui.Text($"{mobHuntEntry.Name} ({currentKills}/{mobHuntEntry.NeededKills})");
  282. if (!this.plugin.Configuration.ShowLocalHuntIcons)
  283. {
  284. continue;
  285. }
  286. this.DrawHuntIcon(mobHuntEntry);
  287. }
  288. ImGui.End();
  289. }
  290. private void DrawConfiguration()
  291. {
  292. ImGui.SetNextWindowSize(Vector2.Zero, ImGuiCond.Always);
  293. var windowFlags = ImGuiWindowFlags.NoDocking;
  294. if (this.plugin.Configuration.LockWindowPositions)
  295. {
  296. windowFlags |= ImGuiWindowFlags.NoResize | ImGuiWindowFlags.NoMove;
  297. }
  298. if (!ImGui.Begin($"{this.plugin.Name} settings", windowFlags))
  299. {
  300. return;
  301. }
  302. var save = false;
  303. save |= ImGui.Checkbox("Lock plugin window positions", ref this.plugin.Configuration.LockWindowPositions);
  304. save |= ImGui.Checkbox("Include hunt area on map by default", ref this.plugin.Configuration.IncludeAreaOnMap);
  305. save |= ImGui.Checkbox("Show hunts in local area", ref this.plugin.Configuration.ShowLocalHunts);
  306. save |= ImGui.Checkbox(
  307. "Show icons of hunts in local area",
  308. ref this.plugin.Configuration.ShowLocalHuntIcons);
  309. save |= ImGui.Checkbox(
  310. "Hide background of local hunts window",
  311. ref this.plugin.Configuration.HideLocalHuntBackground);
  312. save |= ImGui.Checkbox(
  313. "Hide completed targets in local hunts window",
  314. ref this.plugin.Configuration.HideCompletedHunts);
  315. save |= ImGui.SliderFloat("Hunt icon scale", ref this.plugin.Configuration.IconScale, 0.2f, 2f, "%.2f");
  316. if (ImGui.ColorEdit4("Hunt icon background colour", ref this.plugin.Configuration.IconBackgroundColour))
  317. {
  318. this.plugin.Configuration.IconBackgroundColourU32 =
  319. ImGui.ColorConvertFloat4ToU32(this.plugin.Configuration.IconBackgroundColour);
  320. save = true;
  321. }
  322. if (save)
  323. {
  324. this.plugin.Configuration.Save();
  325. }
  326. ImGui.End();
  327. }
  328. private static bool IconButton(FontAwesomeIcon icon, string? id = null)
  329. {
  330. ImGui.PushFont(UiBuilder.IconFont);
  331. var text = icon.ToIconString();
  332. if (id != null)
  333. {
  334. text += $"##{id}";
  335. }
  336. var result = ImGui.Button(text);
  337. ImGui.PopFont();
  338. return result;
  339. }
  340. private void DrawHuntIcon(MobHuntEntry mobHuntEntry)
  341. {
  342. var cursorPos = ImGui.GetCursorScreenPos();
  343. var imageSize = mobHuntEntry.ExpansionId < 3 ? new Vector2(192f, 128f) : new Vector2(210f);
  344. imageSize *= ImGui.GetIO().FontGlobalScale * this.plugin.Configuration.IconScale;
  345. ImGui.InvisibleButton("canvas", imageSize);
  346. var drawList = ImGui.GetWindowDrawList();
  347. if (mobHuntEntry is { ExpansionId: 4, IsEliteMark: false }) // Endwalker uses circle for non elite mobs
  348. {
  349. drawList.AddCircleFilled(
  350. cursorPos + (imageSize / 2f),
  351. imageSize.X / 2f,
  352. this.plugin.Configuration.IconBackgroundColourU32);
  353. }
  354. else
  355. {
  356. drawList.AddRectFilled(
  357. cursorPos,
  358. cursorPos + imageSize,
  359. this.plugin.Configuration.IconBackgroundColourU32);
  360. }
  361. drawList.AddImage(mobHuntEntry.Icon.ImGuiHandle, cursorPos, cursorPos + imageSize);
  362. }
  363. }
  364. }