From 3b0d2302968fa3c78ea43086ce6bddc1f60bc529 Mon Sep 17 00:00:00 2001 From: SheepGoMeh Date: Tue, 26 Dec 2023 08:16:48 +0200 Subject: [PATCH 1/2] Introduce .editorconfig --- HuntBuddy/.editorconfig | 210 ++++++++++++++++++++++++++++++++++++ HuntBuddy/settings.stylecop | Bin 50516 -> 0 bytes 2 files changed, 210 insertions(+) create mode 100644 HuntBuddy/.editorconfig delete mode 100644 HuntBuddy/settings.stylecop diff --git a/HuntBuddy/.editorconfig b/HuntBuddy/.editorconfig new file mode 100644 index 0000000..633ac18 --- /dev/null +++ b/HuntBuddy/.editorconfig @@ -0,0 +1,210 @@ +# Remove the line below if you want to inherit .editorconfig settings from higher directories +root = true + +[*] + +# Indentation and spacing +indent_size = 4 +indent_style = tab + +# New line preferences +end_of_line = crlf +insert_final_newline = true + +# C# files +[*.cs] + +# Organize usings +dotnet_separate_import_directive_groups = true +dotnet_sort_system_directives_first = true +file_header_template = unset + +# this. and Me. preferences +dotnet_style_qualification_for_event = true:warning +dotnet_style_qualification_for_field = true:warning +dotnet_style_qualification_for_method = true:warning +dotnet_style_qualification_for_property = true:warning + +# Language keywords vs BCL types preferences +dotnet_style_predefined_type_for_locals_parameters_members = true:warning +dotnet_style_predefined_type_for_member_access = true:warning + +# Parentheses preferences +dotnet_style_parentheses_in_arithmetic_binary_operators = always_for_clarity:silent +dotnet_style_parentheses_in_other_binary_operators = always_for_clarity:silent +dotnet_style_parentheses_in_other_operators = never_if_unnecessary:silent +dotnet_style_parentheses_in_relational_binary_operators = always_for_clarity:silent + +# Modifier preferences +dotnet_style_require_accessibility_modifiers = for_non_interface_members:warning + +# Expression-level preferences +dotnet_style_coalesce_expression = true:warning +dotnet_style_collection_initializer = true:warning +dotnet_style_explicit_tuple_names = true:warning +dotnet_style_null_propagation = true:error +dotnet_style_object_initializer = true:warning +dotnet_style_operator_placement_when_wrapping = beginning_of_line +dotnet_style_prefer_auto_properties = true:warning +dotnet_style_prefer_compound_assignment = true:warning +dotnet_style_prefer_conditional_expression_over_assignment = true:suggestion +dotnet_style_prefer_conditional_expression_over_return = true:suggestion +dotnet_style_prefer_inferred_anonymous_type_member_names = true:warning +dotnet_style_prefer_inferred_tuple_names = true:warning +dotnet_style_prefer_is_null_check_over_reference_equality_method = true:suggestion +dotnet_style_prefer_simplified_boolean_expressions = true:suggestion +dotnet_style_prefer_simplified_interpolation = true:suggestion + +# Field preferences +dotnet_style_readonly_field = true:suggestion + +# Parameter preferences +dotnet_code_quality_unused_parameters = all:suggestion + +# Suppression preferences +dotnet_remove_unnecessary_suppression_exclusions = none + +# var preferences +csharp_style_var_elsewhere = false:warning +csharp_style_var_for_built_in_types = false:warning +csharp_style_var_when_type_is_apparent = false:warning + +# Expression-bodied members +csharp_style_expression_bodied_accessors = when_on_single_line:suggestion +csharp_style_expression_bodied_constructors = when_on_single_line:suggestion +csharp_style_expression_bodied_indexers = when_on_single_line:suggestion +csharp_style_expression_bodied_lambdas = when_on_single_line:suggestion +csharp_style_expression_bodied_local_functions = when_on_single_line:suggestion +csharp_style_expression_bodied_methods = when_on_single_line:suggestion +csharp_style_expression_bodied_operators = when_on_single_line:suggestion +csharp_style_expression_bodied_properties = when_on_single_line:suggestion + +# Pattern matching preferences +csharp_style_pattern_matching_over_as_with_null_check = true:warning +csharp_style_pattern_matching_over_is_with_cast_check = true:warning +csharp_style_prefer_extended_property_pattern = true:warning +csharp_style_prefer_not_pattern = true:warning +csharp_style_prefer_pattern_matching = true:warning +csharp_style_prefer_switch_expression = true:warning + +# Null-checking preferences +csharp_style_conditional_delegate_call = true:warning + +# Modifier preferences +csharp_prefer_static_local_function = true:warning +csharp_preferred_modifier_order = public,private,protected,internal,static,extern,new,virtual,abstract,sealed,override,readonly,unsafe,required,volatile,async:silent +csharp_prefer_readonly_struct = true:warning + +# Code-block preferences +csharp_prefer_braces = when_multiline:warning +csharp_prefer_simple_using_statement = true:suggestion +csharp_style_namespace_declarations = file_scoped:error +csharp_style_prefer_top_level_statements = false:error + +# Expression-level preferences +csharp_prefer_simple_default_expression = true:suggestion +csharp_style_deconstructed_variable_declaration = true:suggestion +csharp_style_inlined_variable_declaration = true:suggestion +csharp_style_pattern_local_over_anonymous_function = true:suggestion +csharp_style_prefer_local_over_anonymous_function = true:suggestion +csharp_style_prefer_index_operator = true:suggestion +csharp_style_prefer_range_operator = true:suggestion +csharp_style_prefer_tuple_swap = true:error +csharp_style_prefer_null_check_over_type_check = true:error +csharp_style_throw_expression = true:suggestion +csharp_style_unused_value_assignment_preference = discard_variable:silent +csharp_style_unused_value_expression_statement_preference = discard_variable:silent + +# 'using' directive preferences +csharp_using_directive_placement = outside_namespace:suggestion + +# New line preferences +csharp_new_line_before_catch = true +csharp_new_line_before_else = true +csharp_new_line_before_finally = true +csharp_new_line_before_members_in_anonymous_types = true +csharp_new_line_before_members_in_object_initializers = true +csharp_new_line_before_open_brace = none +csharp_new_line_between_query_expression_clauses = true + +# Indentation preferences +csharp_indent_block_contents = true +csharp_indent_braces = false +csharp_indent_case_contents = true +csharp_indent_case_contents_when_block = true +csharp_indent_labels = one_less_than_current +csharp_indent_switch_labels = true + +# Space preferences +csharp_space_after_cast = false +csharp_space_after_colon_in_inheritance_clause = true +csharp_space_after_comma = true +csharp_space_after_dot = false +csharp_space_after_keywords_in_control_flow_statements = true +csharp_space_after_semicolon_in_for_statement = true +csharp_space_around_binary_operators = before_and_after +csharp_space_around_declaration_statements = false +csharp_space_before_colon_in_inheritance_clause = false +csharp_space_before_comma = false +csharp_space_before_dot = false +csharp_space_before_open_square_brackets = false +csharp_space_before_semicolon_in_for_statement = false +csharp_space_between_empty_square_brackets = false +csharp_space_between_method_call_empty_parameter_list_parentheses = false +csharp_space_between_method_call_name_and_opening_parenthesis = false +csharp_space_between_method_call_parameter_list_parentheses = false +csharp_space_between_method_declaration_empty_parameter_list_parentheses = false +csharp_space_between_method_declaration_name_and_open_parenthesis = false +csharp_space_between_method_declaration_parameter_list_parentheses = false +csharp_space_between_parentheses = false +csharp_space_between_square_brackets = false + +# Wrapping preferences +csharp_preserve_single_line_blocks = true +csharp_preserve_single_line_statements = false + +#### Naming styles #### + +# Style Definitions +dotnet_naming_style.pascal_case_style.capitalization = pascal_case + +dotnet_naming_style.I_prefix_style.required_prefix = I +dotnet_naming_style.I_prefix_style.capitalization = pascal_case + +dotnet_naming_style.camel_case_underscore_style.capitalization = camel_case + +# Use PascalCase for constant fields +dotnet_naming_rule.constant_fields_should_be_pascal_case.severity = warning +dotnet_naming_rule.constant_fields_should_be_pascal_case.symbols = constant_fields +dotnet_naming_rule.constant_fields_should_be_pascal_case.style = pascal_case_style +dotnet_naming_symbols.constant_fields.applicable_kinds = field +dotnet_naming_symbols.constant_fields.applicable_accessibilities = * +dotnet_naming_symbols.constant_fields.required_modifiers = const + +# Use PascalCase for public fields +dotnet_naming_rule.pascal_case_for_public_fields.severity = warning +dotnet_naming_rule.pascal_case_for_public_fields.symbols = public_fields +dotnet_naming_rule.pascal_case_for_public_fields.style = pascal_case_style +dotnet_naming_symbols.public_fields.applicable_kinds = field +dotnet_naming_symbols.public_fields.applicable_accessibilities = public + +# internal and private fields should be camelCase +dotnet_naming_rule.camel_case_for_private_internal_fields.severity = warning +dotnet_naming_rule.camel_case_for_private_internal_fields.symbols = private_internal_fields +dotnet_naming_rule.camel_case_for_private_internal_fields.style = camel_case_underscore_style +dotnet_naming_symbols.private_internal_fields.applicable_kinds = field +dotnet_naming_symbols.private_internal_fields.applicable_accessibilities = private, internal + +# Interfaces must be PascalCase and have an I prefix +dotnet_naming_rule.interfaces_start_with_I.severity = warning +dotnet_naming_rule.interfaces_start_with_I.symbols = any_interface +dotnet_naming_rule.interfaces_start_with_I.style = I_prefix_style +dotnet_naming_symbols.any_interface.applicable_kinds = interface +dotnet_naming_symbols.any_interface.applicable_accessibilities = * + +# Classes, structs, methods, enums, events, properties, namespaces, delegates must be PascalCase +dotnet_naming_rule.general_naming.severity = warning +dotnet_naming_rule.general_naming.symbols = general +dotnet_naming_rule.general_naming.style = pascal_case_style +dotnet_naming_symbols.general.applicable_kinds = class,struct,enum,property,method,event,namespace,delegate +dotnet_naming_symbols.general.applicable_accessibilities = * \ No newline at end of file diff --git a/HuntBuddy/settings.stylecop b/HuntBuddy/settings.stylecop deleted file mode 100644 index 31faa7418ad30d4dd0d45ab983fe9f2f0b0bfd01..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 50516 zcmeHQO>Y}F5al^Q{=>re$nB+vLQUW#4eAz2jl>0dl`SPvVabLix6z+(`-ZCtzm{Bb zEh%2_ph#D;xa4s7-pp{w<^KDh8mkxTi(05FRjI$#Ox>!LnyYKIR6nUB^}QOY?{KZ~ z%Tit9?^FD`RG;*(cWSLZs%I$m1IoNm&(%})1T~(i34U4Y+LoxV+O|NSsn$ERvQ$6f z+ok%9QVWz{p`O3+d95z+>qNIgf1@@RD7(<*E_9vGP-2F^-$ELaxR&Udy7AgQ+DcSG z0{U;P&eaB`W}bwVdW||(_~i;UY}7mapV}i?-)UX=Elwci6-rc)x2|!8vUUHtHNM7L z9jUikUeZDHou-E0(aUrhQ}zV9oo1Fccm1ZdI7N?6x1)55dg?L!2stm&Tf)I{an1R% zvow4it6$LaN}WKCwI1gSV25O?oH6=O%mU#&+tu+s8t4D|+&c8=_V`1)xgu}>~gH0N#__)%qjD6RJ$8A;o z9@k#QtYFIaeo=pbyK{`s2v-`v3i8b~*DLi(Tk93R(O-_V?_sXRuQaX;j4b&&`uqv? zleeoq(+TRl+N&kMFHcObbH^d%DxL->X<4Kk^d>_RWC4C9& z&hrv~YQIpU>5cYiYxK$YEV(%HrHFqg|3Xm?#VO>wraG=T$M-vYJ9N*_wJc=WHa+cq zxwkpq#u54GZ)+oN_2QP25i(Xk0}&L@Y+%pgktaneYhdI?Tjd!LOY!ct`UiJch#d`k z;<0B_o9zXc6`p2+lq;q4r?_^VjY96aBg5roze%_ z;a@rW3BA3`!rTZUJ|ce0{Jo0=ZJs(`3PNnoSh&bOB(FK+(%vUu#{2P=YHObHxV-K% zd!ZTaMrVo0I^BTDZk+}j%@UO8pR8eX?c54qZ5zs&p00C>kg45g@nKzIUM7e8G@@~B z#K*f`+qu^E46zMm2Pwluvs8|GCfisbh66J~U3{y#JBR69ny<~Ma3#;tKG(-riqkH) znKsG;GvaI=mDEe)m(xEg*F}r=7=JghvuloZzKQm4!ZnV%`5K>_(MnZZZ^ArbUJ3KWr^S7m z;hC^XSk=ZVJFoZ#HhP9!$W*TprE|Y-Rx5Ycq3tJjv!l?+T!MC6j;5@KEEzgR*49V28BnVGjo}_dotm)I97Q(t2O~H8#=Yr~fq1w$OE%A1XMUT#$qc;qY#3iWX zXm)t}i@HWOoNFoHJ9oeL_n49D`X&j+N1tZ5yGOLlWEn~3+UK1U{&-R}B9{9I?|t{@ z?Y^c;{KVs(sfs-}cA)ta#Rj`M6_=S=f;^Tf7NH2LSwrU9XL~O8yM8}=XunHXBlenR zp6;9R4jX^FIg)TR1?L9mCYKcINl@Rf9EzVAlvuou#SO*<(fvZSofWL@(+zf*(fi!# zy%Y~Mn-YIBu!$3!KQ*1g@AiAsDaXjIHPwHD%;j3YJDO&#tlw_!kl!!weQbVVKZPpH zCFaUb?f9CoKO3#Kc^F2tSd6FY37&2%`>$UX!^ePF4Y3;O^KHXfkB=d-BXz{pEh1oR z(=R*Hjta3XVp&Qpi&zq|B-5h#+vCk8%S*w zV{G#EQ&P7uinTi=Muj;RA4B4?hJ7|_*Jsua7u_*$`@I9*_6g$2Lc1isOlIxrS$q5H zfpE+}<=f;%g>mdh?5pfNOlLOi^*V_Z!${HaOZ=DkueAT7h?(EmwEqY&%sJcb=b+Wi zk&ckroS+An$Za;~OWCg4c~i+a8?D$+4mNAh)?CUiztUM~vPOq^RwP7{XGh;@?LlWrT(XY+J|31^?;!C?%RL3&&#`7m&+B{(zt3&wAk^1c f8!p`Pu2-Yqxa~7NhBS(|V0c}Qes}-AQqTVfdp|LJ From ae5492bc805fa4b9bea487ff45849dec7df8ae86 Mon Sep 17 00:00:00 2001 From: SheepGoMeh Date: Tue, 26 Dec 2023 08:17:24 +0200 Subject: [PATCH 2/2] Apply style changes to project --- HuntBuddy/Attributes/AliasesAttribute.cs | 19 +- HuntBuddy/Attributes/CommandAttribute.cs | 19 +- .../Attributes/DoNotShowInHelpAttribute.cs | 11 +- HuntBuddy/Attributes/HelpMessageAttribute.cs | 19 +- HuntBuddy/Configuration.cs | 39 +- HuntBuddy/Ipc/TeleportConsumer.cs | 90 +- HuntBuddy/Location.cs | 1070 ++++++++--------- HuntBuddy/MobHuntEntry.cs | 38 +- HuntBuddy/Plugin.cs | 553 ++++----- HuntBuddy/PluginCommandManager.cs | 141 +-- HuntBuddy/Service.cs | 75 +- HuntBuddy/Structs/MobHuntStruct.cs | 108 +- HuntBuddy/Utils/InterfaceUtil.cs | 105 +- HuntBuddy/Windows/ConfigurationWindow.cs | 98 +- HuntBuddy/Windows/LocalHuntsWindow.cs | 252 ++-- HuntBuddy/Windows/MainWindow.cs | 376 +++--- 16 files changed, 1475 insertions(+), 1538 deletions(-) diff --git a/HuntBuddy/Attributes/AliasesAttribute.cs b/HuntBuddy/Attributes/AliasesAttribute.cs index a86baae..0045ea2 100644 --- a/HuntBuddy/Attributes/AliasesAttribute.cs +++ b/HuntBuddy/Attributes/AliasesAttribute.cs @@ -1,15 +1,12 @@ using System; -namespace HuntBuddy.Attributes -{ - [AttributeUsage(AttributeTargets.Method)] - public class AliasesAttribute : Attribute - { - public string[] Aliases { get; } +namespace HuntBuddy.Attributes; - public AliasesAttribute(params string[] aliases) - { - this.Aliases = aliases; - } +[AttributeUsage(AttributeTargets.Method)] +public class AliasesAttribute: Attribute { + public string[] Aliases { + get; } -} \ No newline at end of file + + public AliasesAttribute(params string[] aliases) => this.Aliases = aliases; +} diff --git a/HuntBuddy/Attributes/CommandAttribute.cs b/HuntBuddy/Attributes/CommandAttribute.cs index 92c496e..14cbc2d 100644 --- a/HuntBuddy/Attributes/CommandAttribute.cs +++ b/HuntBuddy/Attributes/CommandAttribute.cs @@ -1,15 +1,10 @@ using System; -namespace HuntBuddy.Attributes -{ - [AttributeUsage(AttributeTargets.Method)] - public class CommandAttribute : Attribute - { - public string Command { get; } +namespace HuntBuddy.Attributes; - public CommandAttribute(string command) - { - this.Command = command; - } - } -} \ No newline at end of file +[AttributeUsage(AttributeTargets.Method)] +public class CommandAttribute: Attribute { + public string Command { get; } + + public CommandAttribute(string command) => this.Command = command; +} diff --git a/HuntBuddy/Attributes/DoNotShowInHelpAttribute.cs b/HuntBuddy/Attributes/DoNotShowInHelpAttribute.cs index 88f3930..9f07351 100644 --- a/HuntBuddy/Attributes/DoNotShowInHelpAttribute.cs +++ b/HuntBuddy/Attributes/DoNotShowInHelpAttribute.cs @@ -1,9 +1,6 @@ using System; -namespace HuntBuddy.Attributes -{ - [AttributeUsage(AttributeTargets.Method)] - public class DoNotShowInHelpAttribute : Attribute - { - } -} \ No newline at end of file +namespace HuntBuddy.Attributes; + +[AttributeUsage(AttributeTargets.Method)] +public class DoNotShowInHelpAttribute: Attribute; diff --git a/HuntBuddy/Attributes/HelpMessageAttribute.cs b/HuntBuddy/Attributes/HelpMessageAttribute.cs index 72f4acd..50a992d 100644 --- a/HuntBuddy/Attributes/HelpMessageAttribute.cs +++ b/HuntBuddy/Attributes/HelpMessageAttribute.cs @@ -1,15 +1,12 @@ using System; -namespace HuntBuddy.Attributes -{ - [AttributeUsage(AttributeTargets.Method)] - public class HelpMessageAttribute : Attribute - { - public string HelpMessage { get; } +namespace HuntBuddy.Attributes; - public HelpMessageAttribute(string helpMessage) - { - this.HelpMessage = helpMessage; - } +[AttributeUsage(AttributeTargets.Method)] +public class HelpMessageAttribute: Attribute { + public string HelpMessage { + get; } -} \ No newline at end of file + + public HelpMessageAttribute(string helpMessage) => this.HelpMessage = helpMessage; +} diff --git a/HuntBuddy/Configuration.cs b/HuntBuddy/Configuration.cs index 6a046c3..f52c1c7 100644 --- a/HuntBuddy/Configuration.cs +++ b/HuntBuddy/Configuration.cs @@ -1,27 +1,26 @@ using System.Numerics; using System.Text.Json.Serialization; + using Dalamud.Configuration; -namespace HuntBuddy -{ - public class Configuration : IPluginConfiguration - { - public int Version { get; set; } +namespace HuntBuddy; + +public class Configuration: IPluginConfiguration { + public int Version { + get; + set; + } - public bool IncludeAreaOnMap; - public bool LockWindowPositions; - public bool ShowLocalHunts; - public bool ShowLocalHuntIcons; - public bool HideLocalHuntBackground; - public bool HideCompletedHunts; - public float IconScale = 1f; - public Vector4 IconBackgroundColour = new (0.76f, 0.75f, 0.76f, 0.8f); + public bool IncludeAreaOnMap; + public bool LockWindowPositions; + public bool ShowLocalHunts; + public bool ShowLocalHuntIcons; + public bool HideLocalHuntBackground; + public bool HideCompletedHunts; + public float IconScale = 1f; + public Vector4 IconBackgroundColour = new(0.76f, 0.75f, 0.76f, 0.8f); - [JsonIgnore] public uint IconBackgroundColourU32; + [JsonIgnore] public uint IconBackgroundColourU32; - public void Save() - { - Service.PluginInterface.SavePluginConfig(this); - } - } -} \ No newline at end of file + public void Save() => Service.PluginInterface.SavePluginConfig(this); +} diff --git a/HuntBuddy/Ipc/TeleportConsumer.cs b/HuntBuddy/Ipc/TeleportConsumer.cs index c346c58..49387a1 100644 --- a/HuntBuddy/Ipc/TeleportConsumer.cs +++ b/HuntBuddy/Ipc/TeleportConsumer.cs @@ -1,66 +1,54 @@ using System; -using Dalamud.Plugin.Ipc; -namespace HuntBuddy.Ipc -{ - public class TeleportConsumer - { - private bool isAvailable; - private long timeSinceLastCheck; +using Dalamud.Plugin.Ipc; - public bool IsAvailable - { - get - { - if (this.timeSinceLastCheck + 5000 > Environment.TickCount64) - { - return this.isAvailable; - } +namespace HuntBuddy.Ipc; - try - { - this.consumerMessageSetting.InvokeFunc(); - this.isAvailable = true; - this.timeSinceLastCheck = Environment.TickCount64; - } - catch - { - this.isAvailable = false; - } +public class TeleportConsumer { + private bool isAvailable; + private long timeSinceLastCheck; + public bool IsAvailable { + get { + if (this.timeSinceLastCheck + 5000 > Environment.TickCount64) { return this.isAvailable; } - } - - private ICallGateSubscriber consumerMessageSetting = null!; - private ICallGateSubscriber consumerTeleport = null!; - private void Subscribe() - { - try - { - this.consumerTeleport = Service.PluginInterface.GetIpcSubscriber("Teleport"); - this.consumerMessageSetting = Service.PluginInterface.GetIpcSubscriber("Teleport.ChatMessage"); + try { + this.consumerMessageSetting.InvokeFunc(); + this.isAvailable = true; + this.timeSinceLastCheck = Environment.TickCount64; } - catch (Exception ex) - { - Service.PluginLog.Debug($"Failed to subscribe to Teleporter\nReason: {ex}"); + catch { + this.isAvailable = false; } + + return this.isAvailable; } + } - public TeleportConsumer() => this.Subscribe(); + private ICallGateSubscriber consumerMessageSetting = null!; + private ICallGateSubscriber consumerTeleport = null!; - public bool Teleport(uint aetheryteId) - { - try - { - return this.consumerTeleport.InvokeFunc(aetheryteId, 0); - } - catch - { - Service.Chat.PrintError("Teleporter plugin is not responding"); - return false; - } + private void Subscribe() { + try { + this.consumerTeleport = Service.PluginInterface.GetIpcSubscriber("Teleport"); + this.consumerMessageSetting = Service.PluginInterface.GetIpcSubscriber("Teleport.ChatMessage"); + } + catch (Exception ex) { + Service.PluginLog.Debug($"Failed to subscribe to Teleporter\nReason: {ex}"); + } + } + + public TeleportConsumer() => this.Subscribe(); + + public bool Teleport(uint aetheryteId) { + try { + return this.consumerTeleport.InvokeFunc(aetheryteId, 0); + } + catch { + Service.Chat.PrintError("Teleporter plugin is not responding"); + return false; } } -} \ No newline at end of file +} diff --git a/HuntBuddy/Location.cs b/HuntBuddy/Location.cs index ed4e190..73ed8d8 100644 --- a/HuntBuddy/Location.cs +++ b/HuntBuddy/Location.cs @@ -2,556 +2,552 @@ using System.Collections.Generic; using System.Linq; using System.Numerics; + +using FFXIVClientStructs.FFXIV.Client.UI.Agent; + using Lumina.Excel.GeneratedSheets; -using MapType = FFXIVClientStructs.FFXIV.Client.UI.Agent.MapType; -namespace HuntBuddy -{ - public static class Location - { - public class PositionInfo - { - public float X { get; init; } +using MapType = FFXIVClientStructs.FFXIV.Client.UI.Agent.MapType; - public float Y { get; init; } +namespace HuntBuddy; - public Vector2 Coordinate => new (this.X, this.Y); +public static class Location { + public class PositionInfo { + public float X { + get; + init; } - // MobHuntId as key - public static readonly Dictionary Database = new () - { - // Heavensward - // Coerthas Western Highlands - { 03481, new PositionInfo { X = 15.0f, Y = 12.0f } }, // Archaeornis - { 03472, new PositionInfo { X = 32.0f, Y = 24.0f } }, // Bergthurs - { 03471, new PositionInfo { X = 30.0f, Y = 31.0f } }, // Deepeye - { 03476, new PositionInfo { X = 28.0f, Y = 12.0f } }, // Frost Grenade - { 03480, new PositionInfo { X = 11.0f, Y = 17.0f } }, // Gelato - { 03484, new PositionInfo { X = 10.0f, Y = 14.0f } }, // Ice Commander - { 03475, new PositionInfo { X = 23.0f, Y = 16.0f } }, // Icetrap - { 03487, new PositionInfo { X = 28.0f, Y = 09.0f } }, // Inland Tursus - { 03483, new PositionInfo { X = 19.0f, Y = 29.0f } }, // Lone Yeti - { 03485, new PositionInfo { X = 22.0f, Y = 21.0f } }, // Polar Bear - { 03482, new PositionInfo { X = 16.0f, Y = 20.0f } }, // Rheum - { 03473, new PositionInfo { X = 26.0f, Y = 24.0f } }, // Silver Wolf - { 03490, new PositionInfo { X = 25.0f, Y = 32.0f } }, // Slate Yeti - { 03478, new PositionInfo { X = 25.0f, Y = 12.0f } }, // Slush Zoblyn - { 03470, new PositionInfo { X = 30.0f, Y = 32.0f } }, // Steinbock - { 03474, new PositionInfo { X = 31.0f, Y = 20.0f } }, // Upland Mylodon - { 03493, new PositionInfo { X = 09.0f, Y = 09.0f } }, // Vindthurs - { 03479, new PositionInfo { X = 15.0f, Y = 17.0f } }, // Wooly Yak - - // The Sea of Clouds - { 03524, new PositionInfo { X = 21.0f, Y = 06.0f } }, // Anzu - { 03498, new PositionInfo { X = 28.0f, Y = 29.0f } }, // Cloudworm - { 03496, new PositionInfo { X = 27.0f, Y = 30.0f } }, // Conodont - { 03505, new PositionInfo { X = 19.0f, Y = 30.0f } }, // Dhalmel - { 03511, new PositionInfo { X = 17.0f, Y = 10.0f } }, // Endymion - { 03494, new PositionInfo { X = 11.0f, Y = 33.0f } }, // Gaelicat - { 03495, new PositionInfo { X = 16.0f, Y = 36.0f } }, // Gastornis - { 03512, new PositionInfo { X = 23.0f, Y = 09.0f } }, // Groundskeeper - { 03506, new PositionInfo { X = 20.0f, Y = 30.0f } }, // Korrigan - { 03501, new PositionInfo { X = 36.0f, Y = 24.0f } }, // Lan'laii Gundu - { 03502, new PositionInfo { X = 36.0f, Y = 20.0f } }, // Nat'laii Gundu - { 03516, new PositionInfo { X = 28.0f, Y = 10.0f } }, // Nat'laii Vundu - { 03497, new PositionInfo { X = 29.0f, Y = 30.0f } }, // Obdella - { 03499, new PositionInfo { X = 20.0f, Y = 34.0f } }, // Paissa - { 03500, new PositionInfo { X = 36.0f, Y = 24.0f } }, // Sanuwa - { 03514, new PositionInfo { X = 30.0f, Y = 14.0f } }, // Sanuwa Vundu - { 03525, new PositionInfo { X = 21.0f, Y = 07.0f } }, // Toco Toco - { 03523, new PositionInfo { X = 14.0f, Y = 07.0f } }, // Tsanahale - { 03503, new PositionInfo { X = 35.0f, Y = 25.0f } }, // Vuk'laii Gundu - { 03513, new PositionInfo { X = 18.0f, Y = 17.0f } }, // Vundu Totem - { 03509, new PositionInfo { X = 09.0f, Y = 16.0f } }, // Window Wamoura - { 03510, new PositionInfo { X = 10.0f, Y = 17.0f } }, // Window Wamouracampa - { 03504, new PositionInfo { X = 20.0f, Y = 38.0f } }, // Wisent - - // The Dravanian Forelands - { 03565, new PositionInfo { X = 30.0f, Y = 16.0f } }, // Bandersnatch - { 03566, new PositionInfo { X = 26.0f, Y = 11.0f } }, // Brown Bear - { 03570, new PositionInfo { X = 28.0f, Y = 22.0f } }, // Clearwater Nanka - { 03569, new PositionInfo { X = 27.0f, Y = 25.0f } }, // Clearwater Ninki Nanka - { 03572, new PositionInfo { X = 28.0f, Y = 32.0f } }, // Dragonfly Watcher - { 03567, new PositionInfo { X = 27.0f, Y = 22.0f } }, // Dravanian Aevis - { 03576, new PositionInfo { X = 16.0f, Y = 33.0f } }, // Dravanian Wyvern - { 03563, new PositionInfo { X = 36.0f, Y = 24.0f } }, // Feather Flea - { 03578, new PositionInfo { X = 17.0f, Y = 26.0f } }, // Forelands Hippocerf - { 03579, new PositionInfo { X = 18.0f, Y = 12.0f } }, // Gallimimus - { 03571, new PositionInfo { X = 25.0f, Y = 29.0f } }, // Loaghtan - { 03592, new PositionInfo { X = 27.0f, Y = 35.0f } }, // Loth Cultivator - { 03590, new PositionInfo { X = 27.0f, Y = 35.0f } }, // Loth Firedrone - { 03591, new PositionInfo { X = 29.0f, Y = 36.0f } }, // Loth Steeldrone - { 03568, new PositionInfo { X = 28.0f, Y = 25.0f } }, // Melia - { 03577, new PositionInfo { X = 18.0f, Y = 31.0f } }, // Miacid - { 03555, new PositionInfo { X = 18.0f, Y = 12.0f } }, // Syricta - { 03586, new PositionInfo { X = 31.0f, Y = 08.0f } }, // Thunder Dragon - { 03582, new PositionInfo { X = 13.0f, Y = 15.0f } }, // Tyrannosaur - { 03581, new PositionInfo { X = 13.0f, Y = 14.0f } }, // Vinegaroon - { 03564, new PositionInfo { X = 35.0f, Y = 21.0f } }, // Wild Chocobo - - // The Churning Mists - { 03619, new PositionInfo { X = 17.0f, Y = 27.0f } }, // Amphiptere - { 03620, new PositionInfo { X = 24.0f, Y = 26.0f } }, // Archaeosaur - { 03625, new PositionInfo { X = 18.0f, Y = 24.0f } }, // Bladed Vinegaroon - { 03630, new PositionInfo { X = 25.0f, Y = 10.0f } }, // Blood Dragon - { 03631, new PositionInfo { X = 09.0f, Y = 36.0f } }, // Cloud Aevis - { 03629, new PositionInfo { X = 08.0f, Y = 19.0f } }, // Diresaur - { 03623, new PositionInfo { X = 20.0f, Y = 12.0f } }, // Dragonet - { 03626, new PositionInfo { X = 34.0f, Y = 21.0f } }, // Elder Syricta - { 03628, new PositionInfo { X = 25.0f, Y = 30.0f } }, // Elder Wyvern - { 03668, new PositionInfo { X = 10.0f, Y = 20.0f } }, // Gnarled Melia - { 03614, new PositionInfo { X = 34.0f, Y = 28.0f } }, // Hropken - { 03621, new PositionInfo { X = 09.0f, Y = 12.0f } }, // Limestone Golem - { 03618, new PositionInfo { X = 20.0f, Y = 28.0f } }, // Lower Skylord - { 03627, new PositionInfo { X = 33.0f, Y = 31.0f } }, // Mists Biast - { 03622, new PositionInfo { X = 10.0f, Y = 18.0f } }, // Mists Drake - { 03617, new PositionInfo { X = 23.0f, Y = 25.0f } }, // Moss Dragon - { 03613, new PositionInfo { X = 28.0f, Y = 32.0f } }, // Sankchinni - { 03615, new PositionInfo { X = 32.0f, Y = 15.0f } }, // Tulihand - { 03624, new PositionInfo { X = 38.0f, Y = 17.7f } }, // Vouivre - { 03616, new PositionInfo { X = 26.0f, Y = 20.0f } }, // Wadjet - - // The Dravanian Hinterlands - { 03612, new PositionInfo { X = 25.0f, Y = 37.0f } }, // Bifericeras - { 03609, new PositionInfo { X = 18.0f, Y = 36.0f } }, // Cockatrice - { 03603, new PositionInfo { X = 12.0f, Y = 16.0f } }, // Crawler - { 03594, new PositionInfo { X = 24.0f, Y = 21.0f } }, // Damselfly - { 03598, new PositionInfo { X = 31.0f, Y = 22.0f } }, // Goblin Brandisher - { 03601, new PositionInfo { X = 31.0f, Y = 22.0f } }, // Goblin Glider - { 03600, new PositionInfo { X = 31.0f, Y = 22.0f } }, // Goblin Sharpshooter - { 03599, new PositionInfo { X = 31.0f, Y = 22.0f } }, // Goblin Tinkerer - { 03605, new PositionInfo { X = 10.0f, Y = 21.0f } }, // Great Morbol - { 03597, new PositionInfo { X = 37.0f, Y = 24.0f } }, // Narbrooi - { 03610, new PositionInfo { X = 17.0f, Y = 33.0f } }, // Okeanis - { 03608, new PositionInfo { X = 12.0f, Y = 33.0f } }, // Opken - { 03604, new PositionInfo { X = 11.0f, Y = 27.0f } }, // Orn Kite - { 03607, new PositionInfo { X = 09.0f, Y = 34.0f } }, // Poroggo - { 03595, new PositionInfo { X = 28.0f, Y = 27.0f } }, // Ratel - { 03611, new PositionInfo { X = 12.0f, Y = 32.0f } }, // Sun Leech - { 03593, new PositionInfo { X = 21.0f, Y = 16.0f } }, // Tarantula Hawk - { 03596, new PositionInfo { X = 34.0f, Y = 19.0f } }, // Wildebeest - - // Azys Lla - { 03545, new PositionInfo { X = 35.0f, Y = 24.0f } }, // 6th Legion Vanguard - { 03552, new PositionInfo { X = 27.0f, Y = 33.0f } }, // Adamantite Claw - { 03540, new PositionInfo { X = 31.0f, Y = 06.0f } }, // Allagan Chimera - { 03534, new PositionInfo { X = 15.0f, Y = 13.0f } }, // Clockwork Engineer - { 03536, new PositionInfo { X = 13.0f, Y = 08.0f } }, // Clockwork Harvestman - { 03535, new PositionInfo { X = 18.0f, Y = 13.0f } }, // Clockwork Paladin - { 03542, new PositionInfo { X = 35.0f, Y = 09.0f } }, // Corpse Flower - { 03541, new PositionInfo { X = 29.5f, Y = 12.0f } }, // Empuse - { 03537, new PositionInfo { X = 13.0f, Y = 17.0f } }, // Enforcement Droid - { 03539, new PositionInfo { X = 27.0f, Y = 11.0f } }, // Lamia Cybrid - { 03538, new PositionInfo { X = 28.0f, Y = 13.0f } }, // Lamia Thelytoke - { 03580, new PositionInfo { X = 13.0f, Y = 33.0f } }, // Lesser Hydra - { 03559, new PositionInfo { X = 18.0f, Y = 31.0f } }, // Meracydian Amphiptere - { 03557, new PositionInfo { X = 08.0f, Y = 32.0f } }, // Meracydian Brobinyak - { 03560, new PositionInfo { X = 08.0f, Y = 27.0f } }, // Meracydian Dragon - { 03558, new PositionInfo { X = 06.0f, Y = 35.0f } }, // Meracydian Dragonet - { 03554, new PositionInfo { X = 15.0f, Y = 29.0f } }, // Meracydian Falak - { 03556, new PositionInfo { X = 14.0f, Y = 35.0f } }, // Meracydian Vouivre - { 03533, new PositionInfo { X = 12.0f, Y = 15.0f } }, // Owlbear - { 03543, new PositionInfo { X = 35.0f, Y = 08.0f } }, // Proto-naga - { 03544, new PositionInfo { X = 33.0f, Y = 13.0f } }, // Reptoid - { 03532, new PositionInfo { X = 09.0f, Y = 12.0f } }, // Snapper-rook - - // Stormblood - // The Fringes - { 05685, new PositionInfo { X = 10.0f, Y = 27.0f } }, // Diakka - { 05674, new PositionInfo { X = 22.0f, Y = 16.0f } }, // Foper - { 05697, new PositionInfo { X = 25.0f, Y = 27.0f } }, // Gazelle - { 05676, new PositionInfo { X = 11.6f, Y = 12.0f } }, // Gazelle Hawk - { 05679, new PositionInfo { X = 25.0f, Y = 15.0f } }, // Gelid Bhoot - { 05686, new PositionInfo { X = 10.0f, Y = 27.0f } }, // Goosefish - { 05671, new PositionInfo { X = 11.0f, Y = 11.0f } }, // Leshy - { 05687, new PositionInfo { X = 11.0f, Y = 17.0f } }, // Mossling - { 05683, new PositionInfo { X = 12.0f, Y = 17.0f } }, // Mountain Grizzly - { 05691, new PositionInfo { X = 35.0f, Y = 25.0f } }, // Qalyana Brahmin - { 05689, new PositionInfo { X = 35.0f, Y = 25.0f } }, // Qalyana Kshatriya - { 05690, new PositionInfo { X = 35.0f, Y = 25.0f } }, // Qalyana Shudra - { 05688, new PositionInfo { X = 35.0f, Y = 25.0f } }, // Sacred Marid - { 05675, new PositionInfo { X = 10.0f, Y = 12.0f } }, // Sapria - { 05677, new PositionInfo { X = 22.0f, Y = 11.0f } }, // Spinner - { 05693, new PositionInfo { X = 29.0f, Y = 24.0f } }, // Teleoceras - { 05678, new PositionInfo { X = 28.0f, Y = 15.0f } }, // Velodyna Pugil - { 05680, new PositionInfo { X = 17.0f, Y = 10.0f } }, // Velodyna Sarcosuchus - - // The Peaks - { 05705, new PositionInfo { X = 25.0f, Y = 11.0f } }, // Crag Claw - { 05701, new PositionInfo { X = 18.7f, Y = 12.9f } }, // Bloodglider - { 05702, new PositionInfo { X = 14.0f, Y = 08.0f } }, // Fluturini - { 05703, new PositionInfo { X = 12.0f, Y = 08.0f } }, // Gyr Abanian Hornbill - { 05713, new PositionInfo { X = 25.0f, Y = 33.0f } }, // Highland Eruca - { 05712, new PositionInfo { X = 24.0f, Y = 29.0f } }, // Jhammel - { 05714, new PositionInfo { X = 15.0f, Y = 27.0f } }, // Kongamato - { 05707, new PositionInfo { X = 34.0f, Y = 09.0f } }, // Marble Urolith - { 05715, new PositionInfo { X = 09.0f, Y = 26.0f } }, // Pantera - { 05708, new PositionInfo { X = 24.0f, Y = 14.0f } }, // Scarab Beetle - { 05711, new PositionInfo { X = 24.0f, Y = 24.0f } }, // True Griffin - - // The Ruby Sea - { 05737, new PositionInfo { X = 31.0f, Y = 35.0f } }, // Bombfish - { 05736, new PositionInfo { X = 34.0f, Y = 05.0f } }, // Coralshell - { 05740, new PositionInfo { X = 26.0f, Y = 30.0f } }, // Flying Shark - { 05742, new PositionInfo { X = 23.0f, Y = 33.0f } }, // Gasame - { 05734, new PositionInfo { X = 14.0f, Y = 10.0f } }, // Gyuki - { 05751, new PositionInfo { X = 25.0f, Y = 25.0f } }, // Naked Yumemi - { 05743, new PositionInfo { X = 07.0f, Y = 30.0f } }, // Red Bukan - { 05745, new PositionInfo { X = 08.0f, Y = 28.0f } }, // Red Honkan - { 05744, new PositionInfo { X = 09.5f, Y = 25.2f } }, // Red Hyoe - { 05738, new PositionInfo { X = 33.0f, Y = 11.0f } }, // Sea Serpent - { 05739, new PositionInfo { X = 26.0f, Y = 06.0f } }, // Shiranui - { 05746, new PositionInfo { X = 07.0f, Y = 27.0f } }, // Striped Ray - { 05733, new PositionInfo { X = 29.0f, Y = 37.0f } }, // Tatsunoko - { 05735, new PositionInfo { X = 35.0f, Y = 21.0f } }, // Unkiu - { 05750, new PositionInfo { X = 25.0f, Y = 25.0f } }, // Yumemi - - // Yanxia - { 05761, new PositionInfo { X = 18.0f, Y = 31.0f } }, // Bi Fang - { 05769, new PositionInfo { X = 28.0f, Y = 08.0f } }, // Ebisu Catfish - { 05752, new PositionInfo { X = 27.0f, Y = 34.0f } }, // Lupin Bladehand - { 05754, new PositionInfo { X = 24.0f, Y = 32.0f } }, // Lupin Bowhand - { 05753, new PositionInfo { X = 23.0f, Y = 28.0f } }, // Lupin Spearhand - { 05768, new PositionInfo { X = 19.0f, Y = 11.0f } }, // Magatsu Kiyofusa - { 05763, new PositionInfo { X = 33.0f, Y = 17.0f } }, // Minobi - { 05757, new PositionInfo { X = 30.0f, Y = 23.0f } }, // Rhino Beetle - { 05765, new PositionInfo { X = 30.0f, Y = 34.0f } }, // Taoquan - { 05755, new PositionInfo { X = 24.0f, Y = 32.0f } }, // Tenaga - { 05764, new PositionInfo { X = 23.0f, Y = 30.0f } }, // Vanara - { 05762, new PositionInfo { X = 25.0f, Y = 26.0f } }, // Water Serpent - - // The Azim Steppe - { 05785, new PositionInfo { X = 15.0f, Y = 19.0f } }, // Baras - { 05788, new PositionInfo { X = 17.0f, Y = 26.0f } }, // Chaochu - { 05777, new PositionInfo { X = 23.0f, Y = 15.0f } }, // Halgai - { 05778, new PositionInfo { X = 16.0f, Y = 11.0f } }, // Khun Chuluu - { 05781, new PositionInfo { X = 31.0f, Y = 17.0f } }, // Mammoth - { 05783, new PositionInfo { X = 12.0f, Y = 29.0f } }, // Manzasiri - { 05775, new PositionInfo { X = 28.0f, Y = 13.0f } }, // Matamata - { 05782, new PositionInfo { X = 09.0f, Y = 21.0f } }, // Matanga - { 05779, new PositionInfo { X = 23.0f, Y = 10.0f } }, // Muu Shuwuu - { 05780, new PositionInfo { X = 34.0f, Y = 18.0f } }, // Purbol - { 05776, new PositionInfo { X = 26.0f, Y = 29.0f } }, // Steppe Dhole - { 05773, new PositionInfo { X = 31.0f, Y = 32.0f } }, // Steppe Dzo - - // The Lochs - { 05723, new PositionInfo { X = 18.0f, Y = 32.0f } }, // Abaddon - { 05725, new PositionInfo { X = 26.0f, Y = 11.0f } }, // Abalathian Minotaur - { 05720, new PositionInfo { X = 25.0f, Y = 18.0f } }, // Chelone - { 05727, new PositionInfo { X = 29.0f, Y = 15.0f } }, // Creeping Edila - { 05729, new PositionInfo { X = 05.7f, Y = 26.7f } }, // Dark Clay Beast - { 05732, new PositionInfo { X = 23.0f, Y = 10.0f } }, // Guard Bhoot - { 05716, new PositionInfo { X = 08.0f, Y = 17.0f } }, // Kaluk - { 05724, new PositionInfo { X = 16.0f, Y = 12.0f } }, // Loch Leech - { 05730, new PositionInfo { X = 17.0f, Y = 16.0f } }, // Loch Nanka - { 05717, new PositionInfo { X = 20.0f, Y = 18.0f } }, // Phoebad - { 05721, new PositionInfo { X = 16.0f, Y = 21.0f } }, // Soblyn - { 05722, new PositionInfo { X = 22.0f, Y = 23.0f } }, // Salt Dhruva - { 05728, new PositionInfo { X = 17.0f, Y = 08.0f } }, // Specter - { 05726, new PositionInfo { X = 25.0f, Y = 29.0f } }, // Vepar - { 05719, new PositionInfo { X = 20.0f, Y = 25.0f } }, // Yabby - // Shadowbringers - // Lakeland - { 08498, new PositionInfo { X = 19.0f, Y = 09.0f } }, // Chiliad Cama - { 08502, new PositionInfo { X = 28.0f, Y = 23.2f } }, // Violet Triffid - { 08503, new PositionInfo { X = 14.0f, Y = 16.5f } }, // Gnole - { 08504, new PositionInfo { X = 24.4f, Y = 23.9f } }, // Wetland Warg - { 08505, new PositionInfo { X = 33.2f, Y = 10.0f } }, // White Gremlin - { 08507, new PositionInfo { X = 25.8f, Y = 23.3f } }, // Hoptrap - { 08508, new PositionInfo { X = 28.5f, Y = 36.7f } }, // Wolverine - { 08511, new PositionInfo { X = 11.3f, Y = 11.0f } }, // Smilodon - { 08514, new PositionInfo { X = 34.2f, Y = 17.0f } }, // Ya-te-veo - { 08515, new PositionInfo { X = 29.0f, Y = 17.6f } }, // Proterosuchus - { 08786, new PositionInfo { X = 20.5f, Y = 25.3f } }, // Lake Viper - - // Kholusia - { 08517, new PositionInfo { X = 31.9f, Y = 18.9f } }, // Ironbeard - { 08518, new PositionInfo { X = 36.4f, Y = 28.7f } }, // Hobgoblin - { 08520, new PositionInfo { X = 17.0f, Y = 18.0f } }, // Defective Talos - { 08522, new PositionInfo { X = 34.8f, Y = 10.5f } }, // Sulfur Byrgen - { 08523, new PositionInfo { X = 35.4f, Y = 29.2f } }, // Maultasche - { 08524, new PositionInfo { X = 14.3f, Y = 11.4f } }, // Huldu - { 08525, new PositionInfo { X = 14.3f, Y = 27.1f } }, // Island Rail - { 08527, new PositionInfo { X = 17.0f, Y = 11.0f } }, // Cliffkite - { 08528, new PositionInfo { X = 27.1f, Y = 13.8f } }, // Cliffmole - { 08529, new PositionInfo { X = 08.0f, Y = 18.0f } }, // Scree Gnome - { 08532, new PositionInfo { X = 17.8f, Y = 26.5f } }, // Wood Eyes - { 08533, new PositionInfo { X = 25.0f, Y = 23.5f } }, // Island Wolf - { 08534, new PositionInfo { X = 10.1f, Y = 29.6f } }, // Kholusian Bison - { 08536, new PositionInfo { X = 32.5f, Y = 26.2f } }, // Whiptail - { 08538, new PositionInfo { X = 22.5f, Y = 09.6f } }, // Highland Hyssop - { 08539, new PositionInfo { X = 19.9f, Y = 33.0f } }, // Tragopan - { 08540, new PositionInfo { X = 13.0f, Y = 15.0f } }, // Saichania - { 08541, new PositionInfo { X = 21.0f, Y = 08.7f } }, // Gulgnu - { 08542, new PositionInfo { X = 21.6f, Y = 32.0f } }, // Germinant - - // Amh Araeng - { 08544, new PositionInfo { X = 11.4f, Y = 30.4f } }, // Masterless Talos - { 08545, new PositionInfo { X = 19.1f, Y = 20.9f } }, // Evil Weapon - { 08547, new PositionInfo { X = 30.4f, Y = 12.3f } }, // Gigantender - { 08550, new PositionInfo { X = 29.4f, Y = 25.4f } }, // Ancient Lizard - { 08556, new PositionInfo { X = 29.4f, Y = 21.7f } }, // Sand Mole - { 08557, new PositionInfo { X = 12.7f, Y = 19.0f } }, // Thistle Mole - { 08558, new PositionInfo { X = 30.9f, Y = 27.3f } }, // Scissorjaws - { 08559, new PositionInfo { X = 21.5f, Y = 09.7f } }, // Gnome - { 08561, new PositionInfo { X = 13.9f, Y = 18.2f } }, // Debitage - { 08562, new PositionInfo { X = 27.1f, Y = 29.6f } }, // Ghilman - { 08563, new PositionInfo { X = 25.0f, Y = 34.3f } }, // Flame Zonure - { 08565, new PositionInfo { X = 15.2f, Y = 16.7f } }, // Phorusrhacos - { 08566, new PositionInfo { X = 21.7f, Y = 09.8f } }, // Desert Coyote - { 08567, new PositionInfo { X = 23.9f, Y = 31.8f } }, // Molamander - - // Il Mheg - { 08155, new PositionInfo { X = 08.4f, Y = 30.0f } }, // Flower Basket - { 08569, new PositionInfo { X = 18.0f, Y = 31.0f } }, // Echevore - { 08574, new PositionInfo { X = 31.0f, Y = 14.3f } }, // Garden Porxie - { 08575, new PositionInfo { X = 19.9f, Y = 16.3f } }, // Phooka - { 08576, new PositionInfo { X = 11.1f, Y = 26.0f } }, // Etainmoth - { 08577, new PositionInfo { X = 29.4f, Y = 12.7f } }, // Green Glider - { 08578, new PositionInfo { X = 21.0f, Y = 08.8f } }, // Moss Fungus - { 08581, new PositionInfo { X = 07.8f, Y = 18.7f } }, // Hawker - { 08582, new PositionInfo { X = 25.0f, Y = 11.0f } }, // Rainbow Lorikeet - { 08583, new PositionInfo { X = 29.5f, Y = 11.4f } }, // Tot Aevis - { 08584, new PositionInfo { X = 30.4f, Y = 10.6f } }, // Rabbit's Tail - { 08585, new PositionInfo { X = 19.0f, Y = 32.0f } }, // Rosebear - { 08586, new PositionInfo { X = 31.6f, Y = 06.4f } }, // Garden Crocota - { 08587, new PositionInfo { X = 32.0f, Y = 05.8f } }, // Werewood - { 08590, new PositionInfo { X = 09.4f, Y = 15.0f } }, // Killer Bee - - // The Rak'tika Greatwood - { 08596, new PositionInfo { X = 08.8f, Y = 35.6f } }, // Tomatl - { 08597, new PositionInfo { X = 27.3f, Y = 25.6f } }, // Forest Echo - { 08598, new PositionInfo { X = 25.1f, Y = 14.2f } }, // Cracked Ronkan Doll - { 08599, new PositionInfo { X = 23.0f, Y = 14.0f } }, // Cracked Ronkan Thorn - { 08600, new PositionInfo { X = 16.0f, Y = 32.0f } }, // Vampire Vine - { 08601, new PositionInfo { X = 23.4f, Y = 07.6f } }, // Greatwood Rail - { 08603, new PositionInfo { X = 29.4f, Y = 21.7f } }, // Snapweed - { 08604, new PositionInfo { X = 12.0f, Y = 34.0f } }, // Atrociraptor - { 08606, new PositionInfo { X = 27.7f, Y = 23.2f } }, // Gizamaluk - { 08609, new PositionInfo { X = 16.9f, Y = 33.3f } }, // Helm Beetle - { 08610, new PositionInfo { X = 34.1f, Y = 16.5f } }, // Floor Mandrill - { 08611, new PositionInfo { X = 15.0f, Y = 19.4f } }, // Wild Swine - { 08612, new PositionInfo { X = 24.9f, Y = 30.2f } }, // Caracal - { 08614, new PositionInfo { X = 25.0f, Y = 07.2f } }, // Woodbat - { 08616, new PositionInfo { X = 27.9f, Y = 21.2f } }, // Tarichuk - { 08789, new PositionInfo { X = 21.1f, Y = 13.2f } }, // Cracked Ronkan Vessel - - // The Tempest - { 08618, new PositionInfo { X = 28.6f, Y = 06.2f } }, // Clinoid - { 08619, new PositionInfo { X = 28.2f, Y = 18.3f } }, // Dagon - { 08621, new PositionInfo { X = 22.6f, Y = 31.7f } }, // Cubus - { 08622, new PositionInfo { X = 25.1f, Y = 18.6f } }, // Sea Anemone - { 08623, new PositionInfo { X = 32.1f, Y = 11.7f } }, // Amphisbaena - { 08625, new PositionInfo { X = 32.5f, Y = 21.5f } }, // Morgawr - { 08626, new PositionInfo { X = 36.6f, Y = 16.6f } }, // Trilobite - { 08629, new PositionInfo { X = 27.7f, Y = 08.7f } }, // Sea Gelatin - { 08630, new PositionInfo { X = 29.0f, Y = 21.0f } }, // Tempest Swallow - { 08631, new PositionInfo { X = 35.8f, Y = 07.2f } }, // Blue Swimmer - - // Endwalker - // Labyrinthos - { 10668, new PositionInfo { X = 28.8f, Y = 08.8f } }, // Troll - { 10669, new PositionInfo { X = 31.0f, Y = 25.5f } }, // Genomos - { 10670, new PositionInfo { X = 15.0f, Y = 06.5f } }, // Caribou - { 10672, new PositionInfo { X = 32.0f, Y = 08.8f } }, // Limascabra - { 10673, new PositionInfo { X = 21.5f, Y = 13.5f } }, // Luncheon Toad - { 10674, new PositionInfo { X = 17.0f, Y = 12.0f } }, // Yakow - { 10677, new PositionInfo { X = 34.0f, Y = 15.0f } }, // Labyrinth Screamer - { 10678, new PositionInfo { X = 24.0f, Y = 10.7f } }, // Northern Snapweed - { 10679, new PositionInfo { X = 26.0f, Y = 14.5f } }, // Pephredo - { 10683, new PositionInfo { X = 37.5f, Y = 19.5f } }, // Mythrilcap - - // Thavnair - { 10697, new PositionInfo { X = 19.0f, Y = 23.9f } }, // Pisaca - { 10698, new PositionInfo { X = 13.8f, Y = 18.5f } }, // Vajralangula - { 10699, new PositionInfo { X = 19.2f, Y = 32.6f } }, // Kacchapa - { 10700, new PositionInfo { X = 18.4f, Y = 26.7f } }, // Hamsa - { 10701, new PositionInfo { X = 29.1f, Y = 12.2f } }, // Asvattha - { 10702, new PositionInfo { X = 27.1f, Y = 27.8f } }, // Guhasaya - { 10703, new PositionInfo { X = 27.0f, Y = 17.4f } }, // Bhujamga - { 10704, new PositionInfo { X = 17.6f, Y = 17.8f } }, // Sotormurg - { 10705, new PositionInfo { X = 22.7f, Y = 30.4f } }, // Gaja - { 10706, new PositionInfo { X = 19.1f, Y = 11.7f } }, // Thavnairian Jhammel - { 10707, new PositionInfo { X = 25.9f, Y = 19.0f } }, // Ufiti - { 10709, new PositionInfo { X = 09.2f, Y = 12.8f } }, // Chamrosh - { 10711, new PositionInfo { X = 16.1f, Y = 09.2f } }, // Starmite - { 10712, new PositionInfo { X = 14.3f, Y = 12.7f } }, // Manjusaka - { 10713, new PositionInfo { X = 23.3f, Y = 19.9f } }, // Odqan - { 10715, new PositionInfo { X = 13.4f, Y = 28.5f } }, // Akyaali Crab - { 10716, new PositionInfo { X = 08.2f, Y = 16.2f } }, // Valras - - // Garlemald - { 10648, new PositionInfo { X = 18.8f, Y = 09.8f } }, // Automated Satellite - { 10649, new PositionInfo { X = 25.5f, Y = 17.5f } }, // Automated Death Machine - { 10650, new PositionInfo { X = 15.5f, Y = 19.5f } }, // Automated Cavalry - { 10651, new PositionInfo { X = 21.8f, Y = 17.4f } }, // Automated Bit - { 10652, new PositionInfo { X = 15.7f, Y = 09.8f } }, // Automated Roader - { 10653, new PositionInfo { X = 29.5f, Y = 13.7f } }, // Automated Slasher - { 10654, new PositionInfo { X = 24.3f, Y = 14.9f } }, // Automated Colossus - { 10655, new PositionInfo { X = 12.9f, Y = 11.7f } }, // Automated Avenger - { 10656, new PositionInfo { X = 29.6f, Y = 30.3f } }, // Almasty - { 10657, new PositionInfo { X = 14.6f, Y = 26.1f } }, // Eblan Bear - { 10658, new PositionInfo { X = 31.3f, Y = 17.4f } }, // Eblan Icetrap - { 10659, new PositionInfo { X = 19.8f, Y = 29.1f } }, // Ovibos - { 10660, new PositionInfo { X = 22.3f, Y = 24.9f } }, // Jotunn - { 10661, new PositionInfo { X = 28.4f, Y = 33.0f } }, // Ceruleum Zoblyn - { 10662, new PositionInfo { X = 25.4f, Y = 31.5f } }, // Ilsabardian Tursus - { 10663, new PositionInfo { X = 18.7f, Y = 24.8f } }, // Canis Lupinus - { 10664, new PositionInfo { X = 26.1f, Y = 26.5f } }, // Overgrown Rose - - // Mare Lamentorum - { 10458, new PositionInfo { X = 23.9f, Y = 20.0f } }, // Daphnia - { 10459, new PositionInfo { X = 23.7f, Y = 20.3f } }, // Osculator - { 10460, new PositionInfo { X = 08.6f, Y = 35.5f } }, // Sweeper - { 10461, new PositionInfo { X = 27.3f, Y = 26.0f } }, // Wanderer - { 10462, new PositionInfo { X = 31.1f, Y = 32.2f } }, // Weeper - { 10463, new PositionInfo { X = 19.8f, Y = 22.5f } }, // Thinker - { 10464, new PositionInfo { X = 26.0f, Y = 34.0f } }, // Regolith - { 10465, new PositionInfo { X = 21.4f, Y = 32.2f } }, // Trimmer - { 10467, new PositionInfo { X = 12.0f, Y = 36.7f } }, // Panopt - { 10468, new PositionInfo { X = 11.5f, Y = 22.3f } }, // Dynamite - { 10469, new PositionInfo { X = 16.7f, Y = 31.8f } }, // Armalcolite - { 10470, new PositionInfo { X = 12.9f, Y = 09.6f } }, // Caretaker - { 10471, new PositionInfo { X = 16.1f, Y = 24.9f } }, // Mousse - { 10473, new PositionInfo { X = 31.2f, Y = 27.0f } }, // Downfall Alarum - { 10474, new PositionInfo { X = 33.6f, Y = 26.2f } }, // Downfall Droid - { 10475, new PositionInfo { X = 34.5f, Y = 28.0f } }, // Downfall Hunter - { 10476, new PositionInfo { X = 13.0f, Y = 10.0f } }, // Supporter - { 10477, new PositionInfo { X = 30.1f, Y = 11.0f } }, // Scraper - - // Elpis - { 10590, new PositionInfo { X = 25.7f, Y = 33.9f } }, // Ophion - { 10591, new PositionInfo { X = 16.5f, Y = 29.9f } }, // Yggdreant - { 10592, new PositionInfo { X = 22.6f, Y = 20.0f } }, // Okyupete - { 10594, new PositionInfo { X = 12.4f, Y = 31.8f } }, // Gryps - { 10595, new PositionInfo { X = 26.6f, Y = 29.7f } }, // Monoceros - { 10596, new PositionInfo { X = 10.1f, Y = 14.1f } }, // Pegasos - { 10597, new PositionInfo { X = 28.7f, Y = 25.6f } }, // Bird of Elpis - { 10599, new PositionInfo { X = 33.4f, Y = 14.3f } }, // Hippe - { 10600, new PositionInfo { X = 14.1f, Y = 09.9f } }, // Harpuia - { 10601, new PositionInfo { X = 25.0f, Y = 10.0f } }, // Morbol Marquis - { 10602, new PositionInfo { X = 29.2f, Y = 09.3f } }, // Akantha - { 10603, new PositionInfo { X = 24.4f, Y = 14.3f } }, // Lykopersikon - { 10606, new PositionInfo { X = 21.5f, Y = 06.3f } }, // Lotis - { 10607, new PositionInfo { X = 10.2f, Y = 34.6f } }, // Phanopsyche - { 10608, new PositionInfo { X = 12.9f, Y = 23.4f } }, // Melanion - { 10609, new PositionInfo { X = 12.9f, Y = 08.7f } }, // Ophiotauros - { 10610, new PositionInfo { X = 13.3f, Y = 15.7f } }, // Elpis Minotaur - { 10611, new PositionInfo { X = 30.7f, Y = 17.1f } }, // Remora - - // Ultima Thule - { 10419, new PositionInfo { X = 30.1f, Y = 25.9f } }, // Broken Omicron - { 10420, new PositionInfo { X = 19.3f, Y = 11.8f } }, // Drifting Ea - { 10421, new PositionInfo { X = 34.8f, Y = 28.8f } }, // Beta - { 10422, new PositionInfo { X = 32.9f, Y = 28.8f } }, // Delta - { 10423, new PositionInfo { X = 36.5f, Y = 25.9f } }, // Lambda - { 10424, new PositionInfo { X = 32.1f, Y = 26.6f } }, // Level Tricker - { 10427, new PositionInfo { X = 10.0f, Y = 30.0f } }, // Stellar Amphiptere - { 10430, new PositionInfo { X = 14.4f, Y = 28.2f } }, // Stellar Brobinyak - { 10435, new PositionInfo { X = 16.3f, Y = 14.1f } }, // Other One - }; - - public enum OpenType - { - None, // Just place marker - ShowOpen, // Show special map with radius - MarkerOpen // Show normal map + public float Y { + get; + init; } - public static unsafe void CreateMapMarker(uint territoryType, uint mapId, uint mobHuntId, string? mobHuntName, OpenType openType = OpenType.MarkerOpen) - { - var map = FFXIVClientStructs.FFXIV.Client.UI.Agent.AgentMap.Instance(); - - if (map == null) - { - return; - } - - var pos = MapToWorldCoordinates(Database[mobHuntId].Coordinate, mapId); - - map->IsFlagMarkerSet = 0; - map->SetFlagMapMarker(territoryType, mapId, pos.X, pos.Y, 60004); - - switch (openType) - { - case OpenType.None: - break; - case OpenType.ShowOpen: - map->AgentInterface.Hide(); - map->AddGatheringTempMarker(pos.X, pos.Y, 150, 60004, 4, mobHuntName); - map->OpenMap(mapId, territoryType, mobHuntName, MapType.GatheringLog); - break; - case OpenType.MarkerOpen: - map->AgentInterface.Hide(); - map->OpenMap(mapId, territoryType); - break; - default: - throw new ArgumentOutOfRangeException(nameof(openType), openType, null); - } - } - - private static (int X, int Y) MapToWorldCoordinates(Vector2 pos, uint mapId) - { - var scale = Service.DataManager.GetExcelSheet()?.GetRow(mapId)?.SizeFactor ?? 100; - var num = scale / 100f; - var x = (float)(((pos.X - 1.0) * num / 41.0 * 2048.0) - 1024.0) / num * 1000f; - var y = (float)(((pos.Y - 1.0) * num / 41.0 * 2048.0) - 1024.0) / num * 1000f; - x = (int)(MathF.Round(x, 3, MidpointRounding.AwayFromZero) * 1000) * 0.001f / 1000f; - y = (int)(MathF.Round(y, 3, MidpointRounding.AwayFromZero) * 1000) * 0.001f / 1000f; - return ((int)x, (int)y); + public Vector2 Coordinate => new(this.X, this.Y); + } + + // MobHuntId as key + public static readonly Dictionary Database = new() { + // Heavensward + // Coerthas Western Highlands + { 03481, new PositionInfo { X = 15.0f, Y = 12.0f } }, // Archaeornis + { 03472, new PositionInfo { X = 32.0f, Y = 24.0f } }, // Bergthurs + { 03471, new PositionInfo { X = 30.0f, Y = 31.0f } }, // Deepeye + { 03476, new PositionInfo { X = 28.0f, Y = 12.0f } }, // Frost Grenade + { 03480, new PositionInfo { X = 11.0f, Y = 17.0f } }, // Gelato + { 03484, new PositionInfo { X = 10.0f, Y = 14.0f } }, // Ice Commander + { 03475, new PositionInfo { X = 23.0f, Y = 16.0f } }, // Icetrap + { 03487, new PositionInfo { X = 28.0f, Y = 09.0f } }, // Inland Tursus + { 03483, new PositionInfo { X = 19.0f, Y = 29.0f } }, // Lone Yeti + { 03485, new PositionInfo { X = 22.0f, Y = 21.0f } }, // Polar Bear + { 03482, new PositionInfo { X = 16.0f, Y = 20.0f } }, // Rheum + { 03473, new PositionInfo { X = 26.0f, Y = 24.0f } }, // Silver Wolf + { 03490, new PositionInfo { X = 25.0f, Y = 32.0f } }, // Slate Yeti + { 03478, new PositionInfo { X = 25.0f, Y = 12.0f } }, // Slush Zoblyn + { 03470, new PositionInfo { X = 30.0f, Y = 32.0f } }, // Steinbock + { 03474, new PositionInfo { X = 31.0f, Y = 20.0f } }, // Upland Mylodon + { 03493, new PositionInfo { X = 09.0f, Y = 09.0f } }, // Vindthurs + { 03479, new PositionInfo { X = 15.0f, Y = 17.0f } }, // Wooly Yak + + // The Sea of Clouds + { 03524, new PositionInfo { X = 21.0f, Y = 06.0f } }, // Anzu + { 03498, new PositionInfo { X = 28.0f, Y = 29.0f } }, // Cloudworm + { 03496, new PositionInfo { X = 27.0f, Y = 30.0f } }, // Conodont + { 03505, new PositionInfo { X = 19.0f, Y = 30.0f } }, // Dhalmel + { 03511, new PositionInfo { X = 17.0f, Y = 10.0f } }, // Endymion + { 03494, new PositionInfo { X = 11.0f, Y = 33.0f } }, // Gaelicat + { 03495, new PositionInfo { X = 16.0f, Y = 36.0f } }, // Gastornis + { 03512, new PositionInfo { X = 23.0f, Y = 09.0f } }, // Groundskeeper + { 03506, new PositionInfo { X = 20.0f, Y = 30.0f } }, // Korrigan + { 03501, new PositionInfo { X = 36.0f, Y = 24.0f } }, // Lan'laii Gundu + { 03502, new PositionInfo { X = 36.0f, Y = 20.0f } }, // Nat'laii Gundu + { 03516, new PositionInfo { X = 28.0f, Y = 10.0f } }, // Nat'laii Vundu + { 03497, new PositionInfo { X = 29.0f, Y = 30.0f } }, // Obdella + { 03499, new PositionInfo { X = 20.0f, Y = 34.0f } }, // Paissa + { 03500, new PositionInfo { X = 36.0f, Y = 24.0f } }, // Sanuwa + { 03514, new PositionInfo { X = 30.0f, Y = 14.0f } }, // Sanuwa Vundu + { 03525, new PositionInfo { X = 21.0f, Y = 07.0f } }, // Toco Toco + { 03523, new PositionInfo { X = 14.0f, Y = 07.0f } }, // Tsanahale + { 03503, new PositionInfo { X = 35.0f, Y = 25.0f } }, // Vuk'laii Gundu + { 03513, new PositionInfo { X = 18.0f, Y = 17.0f } }, // Vundu Totem + { 03509, new PositionInfo { X = 09.0f, Y = 16.0f } }, // Window Wamoura + { 03510, new PositionInfo { X = 10.0f, Y = 17.0f } }, // Window Wamouracampa + { 03504, new PositionInfo { X = 20.0f, Y = 38.0f } }, // Wisent + + // The Dravanian Forelands + { 03565, new PositionInfo { X = 30.0f, Y = 16.0f } }, // Bandersnatch + { 03566, new PositionInfo { X = 26.0f, Y = 11.0f } }, // Brown Bear + { 03570, new PositionInfo { X = 28.0f, Y = 22.0f } }, // Clearwater Nanka + { 03569, new PositionInfo { X = 27.0f, Y = 25.0f } }, // Clearwater Ninki Nanka + { 03572, new PositionInfo { X = 28.0f, Y = 32.0f } }, // Dragonfly Watcher + { 03567, new PositionInfo { X = 27.0f, Y = 22.0f } }, // Dravanian Aevis + { 03576, new PositionInfo { X = 16.0f, Y = 33.0f } }, // Dravanian Wyvern + { 03563, new PositionInfo { X = 36.0f, Y = 24.0f } }, // Feather Flea + { 03578, new PositionInfo { X = 17.0f, Y = 26.0f } }, // Forelands Hippocerf + { 03579, new PositionInfo { X = 18.0f, Y = 12.0f } }, // Gallimimus + { 03571, new PositionInfo { X = 25.0f, Y = 29.0f } }, // Loaghtan + { 03592, new PositionInfo { X = 27.0f, Y = 35.0f } }, // Loth Cultivator + { 03590, new PositionInfo { X = 27.0f, Y = 35.0f } }, // Loth Firedrone + { 03591, new PositionInfo { X = 29.0f, Y = 36.0f } }, // Loth Steeldrone + { 03568, new PositionInfo { X = 28.0f, Y = 25.0f } }, // Melia + { 03577, new PositionInfo { X = 18.0f, Y = 31.0f } }, // Miacid + { 03555, new PositionInfo { X = 18.0f, Y = 12.0f } }, // Syricta + { 03586, new PositionInfo { X = 31.0f, Y = 08.0f } }, // Thunder Dragon + { 03582, new PositionInfo { X = 13.0f, Y = 15.0f } }, // Tyrannosaur + { 03581, new PositionInfo { X = 13.0f, Y = 14.0f } }, // Vinegaroon + { 03564, new PositionInfo { X = 35.0f, Y = 21.0f } }, // Wild Chocobo + + // The Churning Mists + { 03619, new PositionInfo { X = 17.0f, Y = 27.0f } }, // Amphiptere + { 03620, new PositionInfo { X = 24.0f, Y = 26.0f } }, // Archaeosaur + { 03625, new PositionInfo { X = 18.0f, Y = 24.0f } }, // Bladed Vinegaroon + { 03630, new PositionInfo { X = 25.0f, Y = 10.0f } }, // Blood Dragon + { 03631, new PositionInfo { X = 09.0f, Y = 36.0f } }, // Cloud Aevis + { 03629, new PositionInfo { X = 08.0f, Y = 19.0f } }, // Diresaur + { 03623, new PositionInfo { X = 20.0f, Y = 12.0f } }, // Dragonet + { 03626, new PositionInfo { X = 34.0f, Y = 21.0f } }, // Elder Syricta + { 03628, new PositionInfo { X = 25.0f, Y = 30.0f } }, // Elder Wyvern + { 03668, new PositionInfo { X = 10.0f, Y = 20.0f } }, // Gnarled Melia + { 03614, new PositionInfo { X = 34.0f, Y = 28.0f } }, // Hropken + { 03621, new PositionInfo { X = 09.0f, Y = 12.0f } }, // Limestone Golem + { 03618, new PositionInfo { X = 20.0f, Y = 28.0f } }, // Lower Skylord + { 03627, new PositionInfo { X = 33.0f, Y = 31.0f } }, // Mists Biast + { 03622, new PositionInfo { X = 10.0f, Y = 18.0f } }, // Mists Drake + { 03617, new PositionInfo { X = 23.0f, Y = 25.0f } }, // Moss Dragon + { 03613, new PositionInfo { X = 28.0f, Y = 32.0f } }, // Sankchinni + { 03615, new PositionInfo { X = 32.0f, Y = 15.0f } }, // Tulihand + { 03624, new PositionInfo { X = 38.0f, Y = 17.7f } }, // Vouivre + { 03616, new PositionInfo { X = 26.0f, Y = 20.0f } }, // Wadjet + + // The Dravanian Hinterlands + { 03612, new PositionInfo { X = 25.0f, Y = 37.0f } }, // Bifericeras + { 03609, new PositionInfo { X = 18.0f, Y = 36.0f } }, // Cockatrice + { 03603, new PositionInfo { X = 12.0f, Y = 16.0f } }, // Crawler + { 03594, new PositionInfo { X = 24.0f, Y = 21.0f } }, // Damselfly + { 03598, new PositionInfo { X = 31.0f, Y = 22.0f } }, // Goblin Brandisher + { 03601, new PositionInfo { X = 31.0f, Y = 22.0f } }, // Goblin Glider + { 03600, new PositionInfo { X = 31.0f, Y = 22.0f } }, // Goblin Sharpshooter + { 03599, new PositionInfo { X = 31.0f, Y = 22.0f } }, // Goblin Tinkerer + { 03605, new PositionInfo { X = 10.0f, Y = 21.0f } }, // Great Morbol + { 03597, new PositionInfo { X = 37.0f, Y = 24.0f } }, // Narbrooi + { 03610, new PositionInfo { X = 17.0f, Y = 33.0f } }, // Okeanis + { 03608, new PositionInfo { X = 12.0f, Y = 33.0f } }, // Opken + { 03604, new PositionInfo { X = 11.0f, Y = 27.0f } }, // Orn Kite + { 03607, new PositionInfo { X = 09.0f, Y = 34.0f } }, // Poroggo + { 03595, new PositionInfo { X = 28.0f, Y = 27.0f } }, // Ratel + { 03611, new PositionInfo { X = 12.0f, Y = 32.0f } }, // Sun Leech + { 03593, new PositionInfo { X = 21.0f, Y = 16.0f } }, // Tarantula Hawk + { 03596, new PositionInfo { X = 34.0f, Y = 19.0f } }, // Wildebeest + + // Azys Lla + { 03545, new PositionInfo { X = 35.0f, Y = 24.0f } }, // 6th Legion Vanguard + { 03552, new PositionInfo { X = 27.0f, Y = 33.0f } }, // Adamantite Claw + { 03540, new PositionInfo { X = 31.0f, Y = 06.0f } }, // Allagan Chimera + { 03534, new PositionInfo { X = 15.0f, Y = 13.0f } }, // Clockwork Engineer + { 03536, new PositionInfo { X = 13.0f, Y = 08.0f } }, // Clockwork Harvestman + { 03535, new PositionInfo { X = 18.0f, Y = 13.0f } }, // Clockwork Paladin + { 03542, new PositionInfo { X = 35.0f, Y = 09.0f } }, // Corpse Flower + { 03541, new PositionInfo { X = 29.5f, Y = 12.0f } }, // Empuse + { 03537, new PositionInfo { X = 13.0f, Y = 17.0f } }, // Enforcement Droid + { 03539, new PositionInfo { X = 27.0f, Y = 11.0f } }, // Lamia Cybrid + { 03538, new PositionInfo { X = 28.0f, Y = 13.0f } }, // Lamia Thelytoke + { 03580, new PositionInfo { X = 13.0f, Y = 33.0f } }, // Lesser Hydra + { 03559, new PositionInfo { X = 18.0f, Y = 31.0f } }, // Meracydian Amphiptere + { 03557, new PositionInfo { X = 08.0f, Y = 32.0f } }, // Meracydian Brobinyak + { 03560, new PositionInfo { X = 08.0f, Y = 27.0f } }, // Meracydian Dragon + { 03558, new PositionInfo { X = 06.0f, Y = 35.0f } }, // Meracydian Dragonet + { 03554, new PositionInfo { X = 15.0f, Y = 29.0f } }, // Meracydian Falak + { 03556, new PositionInfo { X = 14.0f, Y = 35.0f } }, // Meracydian Vouivre + { 03533, new PositionInfo { X = 12.0f, Y = 15.0f } }, // Owlbear + { 03543, new PositionInfo { X = 35.0f, Y = 08.0f } }, // Proto-naga + { 03544, new PositionInfo { X = 33.0f, Y = 13.0f } }, // Reptoid + { 03532, new PositionInfo { X = 09.0f, Y = 12.0f } }, // Snapper-rook + + // Stormblood + // The Fringes + { 05685, new PositionInfo { X = 10.0f, Y = 27.0f } }, // Diakka + { 05674, new PositionInfo { X = 22.0f, Y = 16.0f } }, // Foper + { 05697, new PositionInfo { X = 25.0f, Y = 27.0f } }, // Gazelle + { 05676, new PositionInfo { X = 11.6f, Y = 12.0f } }, // Gazelle Hawk + { 05679, new PositionInfo { X = 25.0f, Y = 15.0f } }, // Gelid Bhoot + { 05686, new PositionInfo { X = 10.0f, Y = 27.0f } }, // Goosefish + { 05671, new PositionInfo { X = 11.0f, Y = 11.0f } }, // Leshy + { 05687, new PositionInfo { X = 11.0f, Y = 17.0f } }, // Mossling + { 05683, new PositionInfo { X = 12.0f, Y = 17.0f } }, // Mountain Grizzly + { 05691, new PositionInfo { X = 35.0f, Y = 25.0f } }, // Qalyana Brahmin + { 05689, new PositionInfo { X = 35.0f, Y = 25.0f } }, // Qalyana Kshatriya + { 05690, new PositionInfo { X = 35.0f, Y = 25.0f } }, // Qalyana Shudra + { 05688, new PositionInfo { X = 35.0f, Y = 25.0f } }, // Sacred Marid + { 05675, new PositionInfo { X = 10.0f, Y = 12.0f } }, // Sapria + { 05677, new PositionInfo { X = 22.0f, Y = 11.0f } }, // Spinner + { 05693, new PositionInfo { X = 29.0f, Y = 24.0f } }, // Teleoceras + { 05678, new PositionInfo { X = 28.0f, Y = 15.0f } }, // Velodyna Pugil + { 05680, new PositionInfo { X = 17.0f, Y = 10.0f } }, // Velodyna Sarcosuchus + + // The Peaks + { 05705, new PositionInfo { X = 25.0f, Y = 11.0f } }, // Crag Claw + { 05701, new PositionInfo { X = 18.7f, Y = 12.9f } }, // Bloodglider + { 05702, new PositionInfo { X = 14.0f, Y = 08.0f } }, // Fluturini + { 05703, new PositionInfo { X = 12.0f, Y = 08.0f } }, // Gyr Abanian Hornbill + { 05713, new PositionInfo { X = 25.0f, Y = 33.0f } }, // Highland Eruca + { 05712, new PositionInfo { X = 24.0f, Y = 29.0f } }, // Jhammel + { 05714, new PositionInfo { X = 15.0f, Y = 27.0f } }, // Kongamato + { 05707, new PositionInfo { X = 34.0f, Y = 09.0f } }, // Marble Urolith + { 05715, new PositionInfo { X = 09.0f, Y = 26.0f } }, // Pantera + { 05708, new PositionInfo { X = 24.0f, Y = 14.0f } }, // Scarab Beetle + { 05711, new PositionInfo { X = 24.0f, Y = 24.0f } }, // True Griffin + + // The Ruby Sea + { 05737, new PositionInfo { X = 31.0f, Y = 35.0f } }, // Bombfish + { 05736, new PositionInfo { X = 34.0f, Y = 05.0f } }, // Coralshell + { 05740, new PositionInfo { X = 26.0f, Y = 30.0f } }, // Flying Shark + { 05742, new PositionInfo { X = 23.0f, Y = 33.0f } }, // Gasame + { 05734, new PositionInfo { X = 14.0f, Y = 10.0f } }, // Gyuki + { 05751, new PositionInfo { X = 25.0f, Y = 25.0f } }, // Naked Yumemi + { 05743, new PositionInfo { X = 07.0f, Y = 30.0f } }, // Red Bukan + { 05745, new PositionInfo { X = 08.0f, Y = 28.0f } }, // Red Honkan + { 05744, new PositionInfo { X = 09.5f, Y = 25.2f } }, // Red Hyoe + { 05738, new PositionInfo { X = 33.0f, Y = 11.0f } }, // Sea Serpent + { 05739, new PositionInfo { X = 26.0f, Y = 06.0f } }, // Shiranui + { 05746, new PositionInfo { X = 07.0f, Y = 27.0f } }, // Striped Ray + { 05733, new PositionInfo { X = 29.0f, Y = 37.0f } }, // Tatsunoko + { 05735, new PositionInfo { X = 35.0f, Y = 21.0f } }, // Unkiu + { 05750, new PositionInfo { X = 25.0f, Y = 25.0f } }, // Yumemi + + // Yanxia + { 05761, new PositionInfo { X = 18.0f, Y = 31.0f } }, // Bi Fang + { 05769, new PositionInfo { X = 28.0f, Y = 08.0f } }, // Ebisu Catfish + { 05752, new PositionInfo { X = 27.0f, Y = 34.0f } }, // Lupin Bladehand + { 05754, new PositionInfo { X = 24.0f, Y = 32.0f } }, // Lupin Bowhand + { 05753, new PositionInfo { X = 23.0f, Y = 28.0f } }, // Lupin Spearhand + { 05768, new PositionInfo { X = 19.0f, Y = 11.0f } }, // Magatsu Kiyofusa + { 05763, new PositionInfo { X = 33.0f, Y = 17.0f } }, // Minobi + { 05757, new PositionInfo { X = 30.0f, Y = 23.0f } }, // Rhino Beetle + { 05765, new PositionInfo { X = 30.0f, Y = 34.0f } }, // Taoquan + { 05755, new PositionInfo { X = 24.0f, Y = 32.0f } }, // Tenaga + { 05764, new PositionInfo { X = 23.0f, Y = 30.0f } }, // Vanara + { 05762, new PositionInfo { X = 25.0f, Y = 26.0f } }, // Water Serpent + + // The Azim Steppe + { 05785, new PositionInfo { X = 15.0f, Y = 19.0f } }, // Baras + { 05788, new PositionInfo { X = 17.0f, Y = 26.0f } }, // Chaochu + { 05777, new PositionInfo { X = 23.0f, Y = 15.0f } }, // Halgai + { 05778, new PositionInfo { X = 16.0f, Y = 11.0f } }, // Khun Chuluu + { 05781, new PositionInfo { X = 31.0f, Y = 17.0f } }, // Mammoth + { 05783, new PositionInfo { X = 12.0f, Y = 29.0f } }, // Manzasiri + { 05775, new PositionInfo { X = 28.0f, Y = 13.0f } }, // Matamata + { 05782, new PositionInfo { X = 09.0f, Y = 21.0f } }, // Matanga + { 05779, new PositionInfo { X = 23.0f, Y = 10.0f } }, // Muu Shuwuu + { 05780, new PositionInfo { X = 34.0f, Y = 18.0f } }, // Purbol + { 05776, new PositionInfo { X = 26.0f, Y = 29.0f } }, // Steppe Dhole + { 05773, new PositionInfo { X = 31.0f, Y = 32.0f } }, // Steppe Dzo + + // The Lochs + { 05723, new PositionInfo { X = 18.0f, Y = 32.0f } }, // Abaddon + { 05725, new PositionInfo { X = 26.0f, Y = 11.0f } }, // Abalathian Minotaur + { 05720, new PositionInfo { X = 25.0f, Y = 18.0f } }, // Chelone + { 05727, new PositionInfo { X = 29.0f, Y = 15.0f } }, // Creeping Edila + { 05729, new PositionInfo { X = 05.7f, Y = 26.7f } }, // Dark Clay Beast + { 05732, new PositionInfo { X = 23.0f, Y = 10.0f } }, // Guard Bhoot + { 05716, new PositionInfo { X = 08.0f, Y = 17.0f } }, // Kaluk + { 05724, new PositionInfo { X = 16.0f, Y = 12.0f } }, // Loch Leech + { 05730, new PositionInfo { X = 17.0f, Y = 16.0f } }, // Loch Nanka + { 05717, new PositionInfo { X = 20.0f, Y = 18.0f } }, // Phoebad + { 05721, new PositionInfo { X = 16.0f, Y = 21.0f } }, // Soblyn + { 05722, new PositionInfo { X = 22.0f, Y = 23.0f } }, // Salt Dhruva + { 05728, new PositionInfo { X = 17.0f, Y = 08.0f } }, // Specter + { 05726, new PositionInfo { X = 25.0f, Y = 29.0f } }, // Vepar + { 05719, new PositionInfo { X = 20.0f, Y = 25.0f } }, // Yabby + // Shadowbringers + // Lakeland + { 08498, new PositionInfo { X = 19.0f, Y = 09.0f } }, // Chiliad Cama + { 08502, new PositionInfo { X = 28.0f, Y = 23.2f } }, // Violet Triffid + { 08503, new PositionInfo { X = 14.0f, Y = 16.5f } }, // Gnole + { 08504, new PositionInfo { X = 24.4f, Y = 23.9f } }, // Wetland Warg + { 08505, new PositionInfo { X = 33.2f, Y = 10.0f } }, // White Gremlin + { 08507, new PositionInfo { X = 25.8f, Y = 23.3f } }, // Hoptrap + { 08508, new PositionInfo { X = 28.5f, Y = 36.7f } }, // Wolverine + { 08511, new PositionInfo { X = 11.3f, Y = 11.0f } }, // Smilodon + { 08514, new PositionInfo { X = 34.2f, Y = 17.0f } }, // Ya-te-veo + { 08515, new PositionInfo { X = 29.0f, Y = 17.6f } }, // Proterosuchus + { 08786, new PositionInfo { X = 20.5f, Y = 25.3f } }, // Lake Viper + + // Kholusia + { 08517, new PositionInfo { X = 31.9f, Y = 18.9f } }, // Ironbeard + { 08518, new PositionInfo { X = 36.4f, Y = 28.7f } }, // Hobgoblin + { 08520, new PositionInfo { X = 17.0f, Y = 18.0f } }, // Defective Talos + { 08522, new PositionInfo { X = 34.8f, Y = 10.5f } }, // Sulfur Byrgen + { 08523, new PositionInfo { X = 35.4f, Y = 29.2f } }, // Maultasche + { 08524, new PositionInfo { X = 14.3f, Y = 11.4f } }, // Huldu + { 08525, new PositionInfo { X = 14.3f, Y = 27.1f } }, // Island Rail + { 08527, new PositionInfo { X = 17.0f, Y = 11.0f } }, // Cliffkite + { 08528, new PositionInfo { X = 27.1f, Y = 13.8f } }, // Cliffmole + { 08529, new PositionInfo { X = 08.0f, Y = 18.0f } }, // Scree Gnome + { 08532, new PositionInfo { X = 17.8f, Y = 26.5f } }, // Wood Eyes + { 08533, new PositionInfo { X = 25.0f, Y = 23.5f } }, // Island Wolf + { 08534, new PositionInfo { X = 10.1f, Y = 29.6f } }, // Kholusian Bison + { 08536, new PositionInfo { X = 32.5f, Y = 26.2f } }, // Whiptail + { 08538, new PositionInfo { X = 22.5f, Y = 09.6f } }, // Highland Hyssop + { 08539, new PositionInfo { X = 19.9f, Y = 33.0f } }, // Tragopan + { 08540, new PositionInfo { X = 13.0f, Y = 15.0f } }, // Saichania + { 08541, new PositionInfo { X = 21.0f, Y = 08.7f } }, // Gulgnu + { 08542, new PositionInfo { X = 21.6f, Y = 32.0f } }, // Germinant + + // Amh Araeng + { 08544, new PositionInfo { X = 11.4f, Y = 30.4f } }, // Masterless Talos + { 08545, new PositionInfo { X = 19.1f, Y = 20.9f } }, // Evil Weapon + { 08547, new PositionInfo { X = 30.4f, Y = 12.3f } }, // Gigantender + { 08550, new PositionInfo { X = 29.4f, Y = 25.4f } }, // Ancient Lizard + { 08556, new PositionInfo { X = 29.4f, Y = 21.7f } }, // Sand Mole + { 08557, new PositionInfo { X = 12.7f, Y = 19.0f } }, // Thistle Mole + { 08558, new PositionInfo { X = 30.9f, Y = 27.3f } }, // Scissorjaws + { 08559, new PositionInfo { X = 21.5f, Y = 09.7f } }, // Gnome + { 08561, new PositionInfo { X = 13.9f, Y = 18.2f } }, // Debitage + { 08562, new PositionInfo { X = 27.1f, Y = 29.6f } }, // Ghilman + { 08563, new PositionInfo { X = 25.0f, Y = 34.3f } }, // Flame Zonure + { 08565, new PositionInfo { X = 15.2f, Y = 16.7f } }, // Phorusrhacos + { 08566, new PositionInfo { X = 21.7f, Y = 09.8f } }, // Desert Coyote + { 08567, new PositionInfo { X = 23.9f, Y = 31.8f } }, // Molamander + + // Il Mheg + { 08155, new PositionInfo { X = 08.4f, Y = 30.0f } }, // Flower Basket + { 08569, new PositionInfo { X = 18.0f, Y = 31.0f } }, // Echevore + { 08574, new PositionInfo { X = 31.0f, Y = 14.3f } }, // Garden Porxie + { 08575, new PositionInfo { X = 19.9f, Y = 16.3f } }, // Phooka + { 08576, new PositionInfo { X = 11.1f, Y = 26.0f } }, // Etainmoth + { 08577, new PositionInfo { X = 29.4f, Y = 12.7f } }, // Green Glider + { 08578, new PositionInfo { X = 21.0f, Y = 08.8f } }, // Moss Fungus + { 08581, new PositionInfo { X = 07.8f, Y = 18.7f } }, // Hawker + { 08582, new PositionInfo { X = 25.0f, Y = 11.0f } }, // Rainbow Lorikeet + { 08583, new PositionInfo { X = 29.5f, Y = 11.4f } }, // Tot Aevis + { 08584, new PositionInfo { X = 30.4f, Y = 10.6f } }, // Rabbit's Tail + { 08585, new PositionInfo { X = 19.0f, Y = 32.0f } }, // Rosebear + { 08586, new PositionInfo { X = 31.6f, Y = 06.4f } }, // Garden Crocota + { 08587, new PositionInfo { X = 32.0f, Y = 05.8f } }, // Werewood + { 08590, new PositionInfo { X = 09.4f, Y = 15.0f } }, // Killer Bee + + // The Rak'tika Greatwood + { 08596, new PositionInfo { X = 08.8f, Y = 35.6f } }, // Tomatl + { 08597, new PositionInfo { X = 27.3f, Y = 25.6f } }, // Forest Echo + { 08598, new PositionInfo { X = 25.1f, Y = 14.2f } }, // Cracked Ronkan Doll + { 08599, new PositionInfo { X = 23.0f, Y = 14.0f } }, // Cracked Ronkan Thorn + { 08600, new PositionInfo { X = 16.0f, Y = 32.0f } }, // Vampire Vine + { 08601, new PositionInfo { X = 23.4f, Y = 07.6f } }, // Greatwood Rail + { 08603, new PositionInfo { X = 29.4f, Y = 21.7f } }, // Snapweed + { 08604, new PositionInfo { X = 12.0f, Y = 34.0f } }, // Atrociraptor + { 08606, new PositionInfo { X = 27.7f, Y = 23.2f } }, // Gizamaluk + { 08609, new PositionInfo { X = 16.9f, Y = 33.3f } }, // Helm Beetle + { 08610, new PositionInfo { X = 34.1f, Y = 16.5f } }, // Floor Mandrill + { 08611, new PositionInfo { X = 15.0f, Y = 19.4f } }, // Wild Swine + { 08612, new PositionInfo { X = 24.9f, Y = 30.2f } }, // Caracal + { 08614, new PositionInfo { X = 25.0f, Y = 07.2f } }, // Woodbat + { 08616, new PositionInfo { X = 27.9f, Y = 21.2f } }, // Tarichuk + { 08789, new PositionInfo { X = 21.1f, Y = 13.2f } }, // Cracked Ronkan Vessel + + // The Tempest + { 08618, new PositionInfo { X = 28.6f, Y = 06.2f } }, // Clinoid + { 08619, new PositionInfo { X = 28.2f, Y = 18.3f } }, // Dagon + { 08621, new PositionInfo { X = 22.6f, Y = 31.7f } }, // Cubus + { 08622, new PositionInfo { X = 25.1f, Y = 18.6f } }, // Sea Anemone + { 08623, new PositionInfo { X = 32.1f, Y = 11.7f } }, // Amphisbaena + { 08625, new PositionInfo { X = 32.5f, Y = 21.5f } }, // Morgawr + { 08626, new PositionInfo { X = 36.6f, Y = 16.6f } }, // Trilobite + { 08629, new PositionInfo { X = 27.7f, Y = 08.7f } }, // Sea Gelatin + { 08630, new PositionInfo { X = 29.0f, Y = 21.0f } }, // Tempest Swallow + { 08631, new PositionInfo { X = 35.8f, Y = 07.2f } }, // Blue Swimmer + + // Endwalker + // Labyrinthos + { 10668, new PositionInfo { X = 28.8f, Y = 08.8f } }, // Troll + { 10669, new PositionInfo { X = 31.0f, Y = 25.5f } }, // Genomos + { 10670, new PositionInfo { X = 15.0f, Y = 06.5f } }, // Caribou + { 10672, new PositionInfo { X = 32.0f, Y = 08.8f } }, // Limascabra + { 10673, new PositionInfo { X = 21.5f, Y = 13.5f } }, // Luncheon Toad + { 10674, new PositionInfo { X = 17.0f, Y = 12.0f } }, // Yakow + { 10677, new PositionInfo { X = 34.0f, Y = 15.0f } }, // Labyrinth Screamer + { 10678, new PositionInfo { X = 24.0f, Y = 10.7f } }, // Northern Snapweed + { 10679, new PositionInfo { X = 26.0f, Y = 14.5f } }, // Pephredo + { 10683, new PositionInfo { X = 37.5f, Y = 19.5f } }, // Mythrilcap + + // Thavnair + { 10697, new PositionInfo { X = 19.0f, Y = 23.9f } }, // Pisaca + { 10698, new PositionInfo { X = 13.8f, Y = 18.5f } }, // Vajralangula + { 10699, new PositionInfo { X = 19.2f, Y = 32.6f } }, // Kacchapa + { 10700, new PositionInfo { X = 18.4f, Y = 26.7f } }, // Hamsa + { 10701, new PositionInfo { X = 29.1f, Y = 12.2f } }, // Asvattha + { 10702, new PositionInfo { X = 27.1f, Y = 27.8f } }, // Guhasaya + { 10703, new PositionInfo { X = 27.0f, Y = 17.4f } }, // Bhujamga + { 10704, new PositionInfo { X = 17.6f, Y = 17.8f } }, // Sotormurg + { 10705, new PositionInfo { X = 22.7f, Y = 30.4f } }, // Gaja + { 10706, new PositionInfo { X = 19.1f, Y = 11.7f } }, // Thavnairian Jhammel + { 10707, new PositionInfo { X = 25.9f, Y = 19.0f } }, // Ufiti + { 10709, new PositionInfo { X = 09.2f, Y = 12.8f } }, // Chamrosh + { 10711, new PositionInfo { X = 16.1f, Y = 09.2f } }, // Starmite + { 10712, new PositionInfo { X = 14.3f, Y = 12.7f } }, // Manjusaka + { 10713, new PositionInfo { X = 23.3f, Y = 19.9f } }, // Odqan + { 10715, new PositionInfo { X = 13.4f, Y = 28.5f } }, // Akyaali Crab + { 10716, new PositionInfo { X = 08.2f, Y = 16.2f } }, // Valras + + // Garlemald + { 10648, new PositionInfo { X = 18.8f, Y = 09.8f } }, // Automated Satellite + { 10649, new PositionInfo { X = 25.5f, Y = 17.5f } }, // Automated Death Machine + { 10650, new PositionInfo { X = 15.5f, Y = 19.5f } }, // Automated Cavalry + { 10651, new PositionInfo { X = 21.8f, Y = 17.4f } }, // Automated Bit + { 10652, new PositionInfo { X = 15.7f, Y = 09.8f } }, // Automated Roader + { 10653, new PositionInfo { X = 29.5f, Y = 13.7f } }, // Automated Slasher + { 10654, new PositionInfo { X = 24.3f, Y = 14.9f } }, // Automated Colossus + { 10655, new PositionInfo { X = 12.9f, Y = 11.7f } }, // Automated Avenger + { 10656, new PositionInfo { X = 29.6f, Y = 30.3f } }, // Almasty + { 10657, new PositionInfo { X = 14.6f, Y = 26.1f } }, // Eblan Bear + { 10658, new PositionInfo { X = 31.3f, Y = 17.4f } }, // Eblan Icetrap + { 10659, new PositionInfo { X = 19.8f, Y = 29.1f } }, // Ovibos + { 10660, new PositionInfo { X = 22.3f, Y = 24.9f } }, // Jotunn + { 10661, new PositionInfo { X = 28.4f, Y = 33.0f } }, // Ceruleum Zoblyn + { 10662, new PositionInfo { X = 25.4f, Y = 31.5f } }, // Ilsabardian Tursus + { 10663, new PositionInfo { X = 18.7f, Y = 24.8f } }, // Canis Lupinus + { 10664, new PositionInfo { X = 26.1f, Y = 26.5f } }, // Overgrown Rose + + // Mare Lamentorum + { 10458, new PositionInfo { X = 23.9f, Y = 20.0f } }, // Daphnia + { 10459, new PositionInfo { X = 23.7f, Y = 20.3f } }, // Osculator + { 10460, new PositionInfo { X = 08.6f, Y = 35.5f } }, // Sweeper + { 10461, new PositionInfo { X = 27.3f, Y = 26.0f } }, // Wanderer + { 10462, new PositionInfo { X = 31.1f, Y = 32.2f } }, // Weeper + { 10463, new PositionInfo { X = 19.8f, Y = 22.5f } }, // Thinker + { 10464, new PositionInfo { X = 26.0f, Y = 34.0f } }, // Regolith + { 10465, new PositionInfo { X = 21.4f, Y = 32.2f } }, // Trimmer + { 10467, new PositionInfo { X = 12.0f, Y = 36.7f } }, // Panopt + { 10468, new PositionInfo { X = 11.5f, Y = 22.3f } }, // Dynamite + { 10469, new PositionInfo { X = 16.7f, Y = 31.8f } }, // Armalcolite + { 10470, new PositionInfo { X = 12.9f, Y = 09.6f } }, // Caretaker + { 10471, new PositionInfo { X = 16.1f, Y = 24.9f } }, // Mousse + { 10473, new PositionInfo { X = 31.2f, Y = 27.0f } }, // Downfall Alarum + { 10474, new PositionInfo { X = 33.6f, Y = 26.2f } }, // Downfall Droid + { 10475, new PositionInfo { X = 34.5f, Y = 28.0f } }, // Downfall Hunter + { 10476, new PositionInfo { X = 13.0f, Y = 10.0f } }, // Supporter + { 10477, new PositionInfo { X = 30.1f, Y = 11.0f } }, // Scraper + + // Elpis + { 10590, new PositionInfo { X = 25.7f, Y = 33.9f } }, // Ophion + { 10591, new PositionInfo { X = 16.5f, Y = 29.9f } }, // Yggdreant + { 10592, new PositionInfo { X = 22.6f, Y = 20.0f } }, // Okyupete + { 10594, new PositionInfo { X = 12.4f, Y = 31.8f } }, // Gryps + { 10595, new PositionInfo { X = 26.6f, Y = 29.7f } }, // Monoceros + { 10596, new PositionInfo { X = 10.1f, Y = 14.1f } }, // Pegasos + { 10597, new PositionInfo { X = 28.7f, Y = 25.6f } }, // Bird of Elpis + { 10599, new PositionInfo { X = 33.4f, Y = 14.3f } }, // Hippe + { 10600, new PositionInfo { X = 14.1f, Y = 09.9f } }, // Harpuia + { 10601, new PositionInfo { X = 25.0f, Y = 10.0f } }, // Morbol Marquis + { 10602, new PositionInfo { X = 29.2f, Y = 09.3f } }, // Akantha + { 10603, new PositionInfo { X = 24.4f, Y = 14.3f } }, // Lykopersikon + { 10606, new PositionInfo { X = 21.5f, Y = 06.3f } }, // Lotis + { 10607, new PositionInfo { X = 10.2f, Y = 34.6f } }, // Phanopsyche + { 10608, new PositionInfo { X = 12.9f, Y = 23.4f } }, // Melanion + { 10609, new PositionInfo { X = 12.9f, Y = 08.7f } }, // Ophiotauros + { 10610, new PositionInfo { X = 13.3f, Y = 15.7f } }, // Elpis Minotaur + { 10611, new PositionInfo { X = 30.7f, Y = 17.1f } }, // Remora + + // Ultima Thule + { 10419, new PositionInfo { X = 30.1f, Y = 25.9f } }, // Broken Omicron + { 10420, new PositionInfo { X = 19.3f, Y = 11.8f } }, // Drifting Ea + { 10421, new PositionInfo { X = 34.8f, Y = 28.8f } }, // Beta + { 10422, new PositionInfo { X = 32.9f, Y = 28.8f } }, // Delta + { 10423, new PositionInfo { X = 36.5f, Y = 25.9f } }, // Lambda + { 10424, new PositionInfo { X = 32.1f, Y = 26.6f } }, // Level Tricker + { 10427, new PositionInfo { X = 10.0f, Y = 30.0f } }, // Stellar Amphiptere + { 10430, new PositionInfo { X = 14.4f, Y = 28.2f } }, // Stellar Brobinyak + { 10435, new PositionInfo { X = 16.3f, Y = 14.1f } }, // Other One + }; + + public enum OpenType { + None, // Just place marker + ShowOpen, // Show special map with radius + MarkerOpen // Show normal map + } + + public static unsafe void CreateMapMarker(uint territoryType, uint mapId, uint mobHuntId, string? mobHuntName, + OpenType openType = OpenType.MarkerOpen) { + AgentMap* map = AgentMap.Instance(); + + if (map == null) { + return; } - private static Vector2 ConvertPixelPositionToMapCoordinate(int x, int y, float scale) - { - var num = scale / 100f; - return new Vector2( - ConvertRawPositionToMapCoordinate((int)((x - 1024) * num * 1000f), scale), - ConvertRawPositionToMapCoordinate((int)((y - 1024) * num * 1000f), scale)); + (int X, int Y) pos = MapToWorldCoordinates(Database[mobHuntId].Coordinate, mapId); + + map->IsFlagMarkerSet = 0; + map->SetFlagMapMarker(territoryType, mapId, pos.X, pos.Y, 60004); + + switch (openType) { + case OpenType.None: + break; + case OpenType.ShowOpen: + map->AgentInterface.Hide(); + map->AddGatheringTempMarker(pos.X, pos.Y, 150, 60004, 4, mobHuntName); + map->OpenMap(mapId, territoryType, mobHuntName, MapType.GatheringLog); + break; + case OpenType.MarkerOpen: + map->AgentInterface.Hide(); + map->OpenMap(mapId, territoryType); + break; + default: + throw new ArgumentOutOfRangeException(nameof(openType), openType, null); } + } + + private static (int X, int Y) MapToWorldCoordinates(Vector2 pos, uint mapId) { + ushort scale = Service.DataManager.GetExcelSheet()?.GetRow(mapId)?.SizeFactor ?? 100; + float num = scale / 100f; + float x = (float)(((pos.X - 1.0) * num / 41.0 * 2048.0) - 1024.0) / num * 1000f; + float y = (float)(((pos.Y - 1.0) * num / 41.0 * 2048.0) - 1024.0) / num * 1000f; + x = (int)(MathF.Round(x, 3, MidpointRounding.AwayFromZero) * 1000) * 0.001f / 1000f; + y = (int)(MathF.Round(y, 3, MidpointRounding.AwayFromZero) * 1000) * 0.001f / 1000f; + return ((int)x, (int)y); + } + + private static Vector2 ConvertPixelPositionToMapCoordinate(int x, int y, float scale) { + float num = scale / 100f; + return new Vector2( + ConvertRawPositionToMapCoordinate((int)((x - 1024) * num * 1000f), scale), + ConvertRawPositionToMapCoordinate((int)((y - 1024) * num * 1000f), scale)); + } - private static float ConvertRawPositionToMapCoordinate(int pos, float scale) - { - var num1 = scale / 100f; - var num2 = (float)(pos * (double)num1 / 1000.0f); - return (40.96f / num1 * ((num2 + 1024.0f) / 2048.0f)) + 1.0f; + private static float ConvertRawPositionToMapCoordinate(int pos, float scale) { + float num1 = scale / 100f; + float num2 = (float)(pos * (double)num1 / 1000.0f); + return (40.96f / num1 * ((num2 + 1024.0f) / 2048.0f)) + 1.0f; + } + + public static void TeleportToNearestAetheryte(uint territoryType, uint mapId, uint mobHuntId) { + Map? mapRow = Service.DataManager.Excel.GetSheet()?.GetRow(mapId); + + if (mapRow == null) { + return; } - public static void TeleportToNearestAetheryte(uint territoryType, uint mapId, uint mobHuntId) - { - var mapRow = Service.DataManager.Excel.GetSheet()?.GetRow(mapId); - - if (mapRow == null) - { - return; - } - - var nearestAetheryteId = Service.DataManager.Excel.GetSheet() - ?.Where(x => x.DataType == 3 && x.RowId == mapRow.MapMarkerRange) - .Select( - x => new - { - distance = Vector2.DistanceSquared( - Database[mobHuntId].Coordinate, - ConvertPixelPositionToMapCoordinate(x.X, x.Y, mapRow.SizeFactor)), - rowId = x.DataKey - }) - .OrderBy(x => x.distance) - .FirstOrDefault()?.rowId; - - var nearestAetheryte = - territoryType == 399 // Support the unique case of aetheryte not being in the same map - ? mapRow.TerritoryType?.Value?.Aetheryte.Value - : Service.DataManager.Excel.GetSheet()?.FirstOrDefault( - x => - x.IsAetheryte && x.Territory.Row == territoryType && x.RowId == nearestAetheryteId); - - if (nearestAetheryte == null) - { - return; - } - - Plugin.TeleportConsumer?.Teleport(nearestAetheryte.RowId); + ushort? nearestAetheryteId = Service.DataManager.Excel.GetSheet() + ?.Where(x => x.DataType == 3 && x.RowId == mapRow.MapMarkerRange) + .Select( + x => new { + distance = Vector2.DistanceSquared( + Database[mobHuntId].Coordinate, + ConvertPixelPositionToMapCoordinate(x.X, x.Y, mapRow.SizeFactor)), + rowId = x.DataKey + }) + .OrderBy(x => x.distance) + .FirstOrDefault()?.rowId; + + Aetheryte? nearestAetheryte = + territoryType == 399 // Support the unique case of aetheryte not being in the same map + ? mapRow.TerritoryType?.Value?.Aetheryte.Value + : Service.DataManager.Excel.GetSheet()?.FirstOrDefault( + x => + x.IsAetheryte && x.Territory.Row == territoryType && x.RowId == nearestAetheryteId); + + if (nearestAetheryte == null) { + return; } + + Plugin.TeleportConsumer?.Teleport(nearestAetheryte.RowId); } -} \ No newline at end of file +} diff --git a/HuntBuddy/MobHuntEntry.cs b/HuntBuddy/MobHuntEntry.cs index 9b325ae..ccaf78a 100644 --- a/HuntBuddy/MobHuntEntry.cs +++ b/HuntBuddy/MobHuntEntry.cs @@ -1,35 +1,31 @@ using System; + using Dalamud.Interface.Internal; -namespace HuntBuddy -{ - public class MobHuntEntry : IDisposable - { - public string? Name { get; init; } +namespace HuntBuddy; + +public class MobHuntEntry: IDisposable { + public string? Name { get; init; } - public string? TerritoryName { get; init; } + public string? TerritoryName { get; init; } - public string? ExpansionName { get; init; } + public string? ExpansionName { get; init; } - public uint ExpansionId { get; init; } + public uint ExpansionId { get; init; } - public uint MapId { get; init; } + public uint MapId { get; init; } - public uint TerritoryType { get; init; } + public uint TerritoryType { get; init; } - public uint MobHuntId { get; init; } + public uint MobHuntId { get; init; } - public bool IsEliteMark { get; init; } + public bool IsEliteMark { get; init; } - public uint CurrentKillsOffset { get; init; } + public uint CurrentKillsOffset { get; init; } - public uint NeededKills { get; set; } + public uint NeededKills { get; set; } - public IDalamudTextureWrap Icon { get; init; } = null!; + public IDalamudTextureWrap Icon { get; init; } = null!; - public void Dispose() - { - this.Icon.Dispose(); - } - } -} \ No newline at end of file + public void Dispose() => this.Icon.Dispose(); +} diff --git a/HuntBuddy/Plugin.cs b/HuntBuddy/Plugin.cs index 24dd885..3c9bc13 100644 --- a/HuntBuddy/Plugin.cs +++ b/HuntBuddy/Plugin.cs @@ -5,334 +5,337 @@ using System.Globalization; using System.Linq; using System.Threading.Tasks; + using Dalamud.Interface.Internal; using Dalamud.Interface.Windowing; using Dalamud.Plugin; using Dalamud.Plugin.Services; using Dalamud.Utility; + using Lumina.Excel.GeneratedSheets; + using HuntBuddy.Attributes; using HuntBuddy.Ipc; using HuntBuddy.Structs; using HuntBuddy.Windows; + using ImGuiNET; + +using Lumina.Data.Files; +using Lumina.Excel; using Lumina.Extensions; +using Lumina.Text; -namespace HuntBuddy -{ - public class Plugin : IDalamudPlugin - { - public string Name => "Hunt Buddy"; - - private readonly PluginCommandManager commandManager; - private ObtainedBillEnum lastState; - // Dictionary, List>> - public readonly Dictionary, List>> MobHuntEntries; - public readonly ConcurrentBag CurrentAreaMobHuntEntries; - public bool MobHuntEntriesReady = true; - public readonly unsafe MobHuntStruct* MobHuntStruct; - public readonly Configuration Configuration; - public static TeleportConsumer? TeleportConsumer; - - private WindowSystem WindowSystem { get; } - - private MainWindow MainWindow { get; } - private ConfigurationWindow ConfigurationWindow { get; } - - public static Plugin Instance { get; internal set; } = null!; - - public Plugin(DalamudPluginInterface pluginInterface) - { - Instance = this; - - pluginInterface.Create(); - - this.commandManager = new PluginCommandManager(this, Service.Commands); - this.MobHuntEntries = new Dictionary, List>>(); - this.CurrentAreaMobHuntEntries = new ConcurrentBag(); - this.Configuration = (Configuration)(Service.PluginInterface.GetPluginConfig() ?? new Configuration()); - this.Configuration.IconBackgroundColourU32 = - ImGui.ColorConvertFloat4ToU32(this.Configuration.IconBackgroundColour); - - unsafe - { - this.MobHuntStruct = - (MobHuntStruct*)Service.SigScanner.GetStaticAddressFromSig( - "48 8D 0D ?? ?? ?? ?? 8B D8 0F B6 52"); - } - - this.MainWindow = new MainWindow(); - this.ConfigurationWindow = new ConfigurationWindow(); - - this.WindowSystem = new WindowSystem("HuntBuddy"); - this.WindowSystem.AddWindow(this.MainWindow); - this.WindowSystem.AddWindow(new LocalHuntsWindow()); - this.WindowSystem.AddWindow(this.ConfigurationWindow); - - Plugin.TeleportConsumer = new TeleportConsumer(); - Service.ClientState.TerritoryChanged += this.ClientStateOnTerritoryChanged; - Service.PluginInterface.UiBuilder.Draw += this.WindowSystem.Draw; - Service.PluginInterface.UiBuilder.OpenConfigUi += this.OpenConfigUi; - Service.Framework.Update += this.FrameworkOnUpdate; - } +namespace HuntBuddy; - private unsafe void FrameworkOnUpdate(IFramework framework) - { - if (this.lastState == this.MobHuntStruct->ObtainedBillEnumFlags) - { - return; - } +public class Plugin: IDalamudPlugin { + public string Name => "Hunt Buddy"; - this.lastState = this.MobHuntStruct->ObtainedBillEnumFlags; - this.PluginCommand(string.Empty, "reload"); - } + private readonly PluginCommandManager commandManager; - private void ClientStateOnTerritoryChanged(ushort e) - { - this.CurrentAreaMobHuntEntries.Clear(); + private ObtainedBillEnum lastState; - foreach (var mobHuntEntry in this.MobHuntEntries.SelectMany( - expansionEntry => expansionEntry.Value - .Where(entry => entry.Key.Key == Service.ClientState.TerritoryType) - .SelectMany(entry => entry.Value))) - { - this.CurrentAreaMobHuntEntries.Add(mobHuntEntry); - } + // Dictionary, List>> + public readonly Dictionary, List>> MobHuntEntries; + public readonly ConcurrentBag CurrentAreaMobHuntEntries; + public bool MobHuntEntriesReady = true; + public readonly unsafe MobHuntStruct* MobHuntStruct; + public readonly Configuration Configuration; + public static TeleportConsumer? TeleportConsumer; + + private WindowSystem WindowSystem { + get; + } + + private MainWindow MainWindow { + get; + } + + private ConfigurationWindow ConfigurationWindow { + get; + } + + public static Plugin Instance { + get; + internal set; + } = null!; + + public Plugin(DalamudPluginInterface pluginInterface) { + Instance = this; + + pluginInterface.Create(); + + this.commandManager = new PluginCommandManager(this, Service.Commands); + this.MobHuntEntries = new Dictionary, List>>(); + this.CurrentAreaMobHuntEntries = new ConcurrentBag(); + this.Configuration = (Configuration)(Service.PluginInterface.GetPluginConfig() ?? new Configuration()); + this.Configuration.IconBackgroundColourU32 = + ImGui.ColorConvertFloat4ToU32(this.Configuration.IconBackgroundColour); + + unsafe { + this.MobHuntStruct = + (MobHuntStruct*)Service.SigScanner.GetStaticAddressFromSig( + "48 8D 0D ?? ?? ?? ?? 8B D8 0F B6 52"); } - private void DrawInterface() - { - this.MainWindow.Toggle(); + this.MainWindow = new MainWindow(); + this.ConfigurationWindow = new ConfigurationWindow(); + + this.WindowSystem = new WindowSystem("HuntBuddy"); + this.WindowSystem.AddWindow(this.MainWindow); + this.WindowSystem.AddWindow(new LocalHuntsWindow()); + this.WindowSystem.AddWindow(this.ConfigurationWindow); + + Plugin.TeleportConsumer = new TeleportConsumer(); + Service.ClientState.TerritoryChanged += this.ClientStateOnTerritoryChanged; + Service.PluginInterface.UiBuilder.Draw += this.WindowSystem.Draw; + Service.PluginInterface.UiBuilder.OpenConfigUi += this.OpenConfigUi; + Service.Framework.Update += this.FrameworkOnUpdate; + } + + private unsafe void FrameworkOnUpdate(IFramework framework) { + if (this.lastState == this.MobHuntStruct->ObtainedBillEnumFlags) { + return; } - public void OpenConfigUi() - { - this.ConfigurationWindow.Toggle(); + this.lastState = this.MobHuntStruct->ObtainedBillEnumFlags; + this.PluginCommand(string.Empty, "reload"); + } + + private void ClientStateOnTerritoryChanged(ushort e) { + 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))) { + this.CurrentAreaMobHuntEntries.Add(mobHuntEntry); } + } - private void Dispose(bool disposing) - { - if (!disposing) - { - return; - } + private void DrawInterface() => this.MainWindow.Toggle(); - this.MobHuntEntriesReady = false; - Service.ClientState.TerritoryChanged -= this.ClientStateOnTerritoryChanged; - Service.Framework.Update -= this.FrameworkOnUpdate; - Service.PluginInterface.UiBuilder.Draw -= this.WindowSystem.Draw; - Service.PluginInterface.UiBuilder.OpenConfigUi -= this.OpenConfigUi; - - this.WindowSystem.RemoveAllWindows(); + public void OpenConfigUi() => this.ConfigurationWindow.Toggle(); - this.commandManager.Dispose(); + private void Dispose(bool disposing) { + if (!disposing) { + return; } - [Command("/phb")] - [HelpMessage("Toggles UI\nArguments:\nreload - Reloads data\nlocal - Toggles the local hunt marks window\nnext - Flags the next hunt target to find\nlist - list all hunt targets by expansion")] - public unsafe void PluginCommand(string command, string args) - { - try - { - switch (args.Trim().ToLower()) { - case "reload": - this.MobHuntEntriesReady = false; - Task.Run(this.ReloadData); - break; - case "local": - this.Configuration.ShowLocalHunts = !this.Configuration.ShowLocalHunts; - this.Configuration.Save(); - break; - case "next": - if (this.MobHuntEntries.Count > 0) - { - var filterPredicate = (MobHuntEntry entry) => entry.IsEliteMark || this.MobHuntStruct->CurrentKills[entry.CurrentKillsOffset] < entry.NeededKills; - var openType = Location.OpenType.None; - var playerLocation = Service.ClientState.LocalPlayer!.Position; - var map = Service.DataManager.GetExcelSheet()!.GetRow(Service.ClientState.TerritoryType)!.Map!.Value!; - var playerVec2 = MapUtil.WorldToMap(new Vector2(playerLocation.X, playerLocation.Z), map); - var chosen = this.CurrentAreaMobHuntEntries - .Where(filterPredicate) - .OrderBy(entry => entry.IsEliteMark ? float.MaxValue : Vector2.Distance(Location.Database[entry.MobHuntId].Coordinate, playerVec2)) - .FirstOrDefault(); - if (chosen == null) - { - Service.PluginLog.Information("No marks in current zone, looking in current expansion"); - openType = this.Configuration.IncludeAreaOnMap ? Location.OpenType.ShowOpen : Location.OpenType.MarkerOpen; - var expansion = Service.DataManager.Excel.GetSheet()!.GetRow(Service.ClientState.TerritoryType)!.ExVersion.Value!.Name; - Service.PluginLog.Information($"Player is in a zone from {expansion}; known expansions are {string.Join(", ", this.MobHuntEntries.Keys)}"); - var candidates = this.MobHuntEntries.ContainsKey(expansion) - ? this.MobHuntEntries[expansion] - .Values + this.MobHuntEntriesReady = false; + Service.ClientState.TerritoryChanged -= this.ClientStateOnTerritoryChanged; + Service.Framework.Update -= this.FrameworkOnUpdate; + Service.PluginInterface.UiBuilder.Draw -= this.WindowSystem.Draw; + Service.PluginInterface.UiBuilder.OpenConfigUi -= this.OpenConfigUi; + + this.WindowSystem.RemoveAllWindows(); + + this.commandManager.Dispose(); + } + + [Command("/phb")] + [HelpMessage( + "Toggles UI\nArguments:\nreload - Reloads data\nlocal - Toggles the local hunt marks window\nnext - Flags the next hunt target to find\nlist - list all hunt targets by expansion")] + public unsafe void PluginCommand(string command, string args) { + try { + switch (args.Trim().ToLower()) { + case "reload": + this.MobHuntEntriesReady = false; + Task.Run(this.ReloadData); + break; + case "local": + this.Configuration.ShowLocalHunts = !this.Configuration.ShowLocalHunts; + this.Configuration.Save(); + break; + case "next": + if (this.MobHuntEntries.Count > 0) { + Func filterPredicate = (MobHuntEntry entry) => entry.IsEliteMark || + this.MobHuntStruct->CurrentKills[ + entry.CurrentKillsOffset] < + entry.NeededKills; + Location.OpenType openType = Location.OpenType.None; + Vector3 playerLocation = Service.ClientState.LocalPlayer!.Position; + Map map = Service.DataManager.GetExcelSheet()!.GetRow(Service.ClientState + .TerritoryType)!.Map!.Value!; + Vector2 playerVec2 = MapUtil.WorldToMap(new Vector2(playerLocation.X, playerLocation.Z), map); + MobHuntEntry? chosen = this.CurrentAreaMobHuntEntries + .Where(filterPredicate) + .OrderBy(entry => + entry.IsEliteMark + ? float.MaxValue + : Vector2.Distance(Location.Database[entry.MobHuntId].Coordinate, playerVec2)) + .FirstOrDefault(); + if (chosen == null) { + Service.PluginLog.Information("No marks in current zone, looking in current expansion"); + openType = this.Configuration.IncludeAreaOnMap + ? Location.OpenType.ShowOpen + : Location.OpenType.MarkerOpen; + SeString? expansion = + Service.DataManager.Excel.GetSheet()!.GetRow(Service.ClientState + .TerritoryType)!.ExVersion.Value!.Name; + Service.PluginLog.Information( + $"Player is in a zone from {expansion}; known expansions are {string.Join(", ", this.MobHuntEntries.Keys)}"); + List candidates = this.MobHuntEntries.ContainsKey(expansion) + ? this.MobHuntEntries[expansion] + .Values + .SelectMany(l => l) + .Where(filterPredicate) + .ToList() + : new List(); + // if we didn't find any candidates, we try a different method to fill it + if (candidates.Count == 0) { + Service.PluginLog.Information( + "Nothing available in current expansion, looking globally"); + candidates = + this.MobHuntEntries.Values + .SelectMany(dict => dict.Values) .SelectMany(l => l) .Where(filterPredicate) - .ToList() - : new List(); - // if we didn't find any candidates, we try a different method to fill it - if (candidates.Count == 0) - { - Service.PluginLog.Information("Nothing available in current expansion, looking globally"); - candidates = - this.MobHuntEntries.Values - .SelectMany(dict => dict.Values) - .SelectMany(l => l) - .Where(filterPredicate) - .ToList(); - } - // regardless of HOW we got our candidates, assuming we did in fact get them, we pick one - // note that this can't be merged into the above block because the above MAY run, and if so MUST run first, - // but this block must ALWAYS run, regardless - if (candidates.Count >= 1) - { - Service.PluginLog.Information($"Found {candidates.Count}"); - chosen = candidates[new Random().Next(candidates.Count)]; - } - } - if (chosen != null) - { - if (chosen.IsEliteMark) - { - Service.Chat.Print($"Hunting elite mark {chosen.Name} in {chosen.TerritoryName}"); - } - else - { - var remaining = chosen.NeededKills - this.MobHuntStruct->CurrentKills[chosen.CurrentKillsOffset]; - Service.Chat.Print($"Hunting {remaining}x {chosen.Name} in {chosen.TerritoryName}"); - Location.CreateMapMarker( - chosen.TerritoryType, - chosen.MapId, - chosen.MobHuntId, - chosen.Name, - openType); - } + .ToList(); } - else - { - Service.PluginLog.Information("Unable to find a hunt mark to target"); - Service.Chat.Print("Couldn't find any hunt marks. Either you have no bills, or this is a bug."); + + // regardless of HOW we got our candidates, assuming we did in fact get them, we pick one + // note that this can't be merged into the above block because the above MAY run, and if so MUST run first, + // but this block must ALWAYS run, regardless + if (candidates.Count >= 1) { + Service.PluginLog.Information($"Found {candidates.Count}"); + chosen = candidates[new Random().Next(candidates.Count)]; } } - break; - case "ls": - case "list": - if (this.MobHuntEntries.Count < 1) { - Service.Chat.Print("No hunt marks found. If this doesn't sound right, please use `/phb reload` and try again."); - break; + + if (chosen != null) { + if (chosen.IsEliteMark) { + Service.Chat.Print($"Hunting elite mark {chosen.Name} in {chosen.TerritoryName}"); + } + else { + long remaining = chosen.NeededKills - + this.MobHuntStruct->CurrentKills[chosen.CurrentKillsOffset]; + Service.Chat.Print($"Hunting {remaining}x {chosen.Name} in {chosen.TerritoryName}"); + Location.CreateMapMarker( + chosen.TerritoryType, + chosen.MapId, + chosen.MobHuntId, + chosen.Name, + openType); + } } - foreach (string expac in this.MobHuntEntries.Keys) { - Service.Chat.Print($"{expac}: {string.Join(", ", this.MobHuntEntries[expac].Values.SelectMany(e => e).OrderBy(s => s.Name).Select(m => m.Name))}"); + else { + Service.PluginLog.Information("Unable to find a hunt mark to target"); + Service.Chat.Print( + "Couldn't find any hunt marks. Either you have no bills, or this is a bug."); } + } + + break; + case "ls": + case "list": + if (this.MobHuntEntries.Count < 1) { + Service.Chat.Print( + "No hunt marks found. If this doesn't sound right, please use `/phb reload` and try again."); break; - default: - this.DrawInterface(); - break; - } - } - catch (Exception e) - { - Service.PluginLog.Error("Error in command handler: " + e.ToString()); + } + + foreach (string expac in this.MobHuntEntries.Keys) { + Service.Chat.Print( + $"{expac}: {string.Join(", ", this.MobHuntEntries[expac].Values.SelectMany(e => e).OrderBy(s => s.Name).Select(m => m.Name))}"); + } + + break; + default: + this.DrawInterface(); + break; } } + catch (Exception e) { + Service.PluginLog.Error($"Error in command handler: {e}"); + } + } - public unsafe void ReloadData() - { - this.MobHuntEntries.Clear(); - var mobHuntList = new List(); - var mobHuntOrderSheet = Service.DataManager.Excel.GetSheet()!; - - foreach (var billNumber in Enum.GetValues()) - { - if (!this.MobHuntStruct->ObtainedBillEnumFlags.HasFlag((ObtainedBillEnum)(1 << (int)billNumber))) - { - continue; - } + public unsafe void ReloadData() { + this.MobHuntEntries.Clear(); + List mobHuntList = new List(); + ExcelSheet? mobHuntOrderSheet = Service.DataManager.Excel.GetSheet()!; + + foreach (BillEnum billNumber in Enum.GetValues()) { + if (!this.MobHuntStruct->ObtainedBillEnumFlags.HasFlag((ObtainedBillEnum)(1 << (int)billNumber))) { + continue; + } - var mobHuntOrderTypeRow = - Service.DataManager.Excel.GetSheet()!.GetRow((uint)billNumber)!; + MobHuntOrderType mobHuntOrderTypeRow = + Service.DataManager.Excel.GetSheet()!.GetRow((uint)billNumber)!; - var rowId = mobHuntOrderTypeRow.OrderStart.Value!.RowId + - (uint)(this.MobHuntStruct->BillOffset[mobHuntOrderTypeRow.RowId] - 1); + uint rowId = mobHuntOrderTypeRow.OrderStart.Value!.RowId + + (uint)(this.MobHuntStruct->BillOffset[mobHuntOrderTypeRow.RowId] - 1); - if (rowId > mobHuntOrderSheet.RowCount) - { - continue; - } + if (rowId > mobHuntOrderSheet.RowCount) { + continue; + } - var mobHuntOrderRows = mobHuntOrderSheet.Where(x => x.RowId == rowId); - - foreach (var mobHuntOrderRow in mobHuntOrderRows) - { - var mobHuntEntry = - mobHuntList.FirstOrDefault(x => x.MobHuntId == mobHuntOrderRow.Target.Value!.Name.Row); - - if (mobHuntEntry == null) - { - mobHuntList.Add( - new MobHuntEntry - { - Name = CultureInfo.InvariantCulture.TextInfo.ToTitleCase( - mobHuntOrderRow.Target.Value!.Name.Value!.Singular), - TerritoryName = - mobHuntOrderRow.Target.Value!.TerritoryType.Value!.PlaceName.Value!.Name, - ExpansionName = mobHuntOrderRow.Target.Value!.TerritoryType.Value.TerritoryType.Value! - .ExVersion.Value!.Name, - ExpansionId = mobHuntOrderRow.Target.Value!.TerritoryType.Value.TerritoryType.Value! - .ExVersion.Row, - MapId = mobHuntOrderRow.Target.Value!.TerritoryType.Row, - TerritoryType = mobHuntOrderRow.Target.Value!.TerritoryType.Value.TerritoryType.Row, - MobHuntId = mobHuntOrderRow.Target.Value!.Name.Row, - IsEliteMark = billNumber is BillEnum.ArrElite or BillEnum.HwElite or BillEnum.SbElite - or BillEnum.ShbElite or BillEnum.EwElite, - CurrentKillsOffset = (5 * (uint)billNumber) + mobHuntOrderRow.SubRowId, - NeededKills = mobHuntOrderRow.NeededKills, - Icon = Plugin.LoadIcon(mobHuntOrderRow.Target.Value.Icon) - }); - } - else - { - if (mobHuntEntry.NeededKills < mobHuntOrderRow.NeededKills) - { - mobHuntEntry.NeededKills = mobHuntOrderRow.NeededKills; - } + IEnumerable mobHuntOrderRows = mobHuntOrderSheet.Where(x => x.RowId == rowId); + + foreach (MobHuntOrder mobHuntOrderRow in mobHuntOrderRows) { + MobHuntEntry? mobHuntEntry = + mobHuntList.FirstOrDefault(x => x.MobHuntId == mobHuntOrderRow.Target.Value!.Name.Row); + + if (mobHuntEntry == null) { + mobHuntList.Add( + new MobHuntEntry { + Name = CultureInfo.InvariantCulture.TextInfo.ToTitleCase( + mobHuntOrderRow.Target.Value!.Name.Value!.Singular), + TerritoryName = + mobHuntOrderRow.Target.Value!.TerritoryType.Value!.PlaceName.Value!.Name, + ExpansionName = mobHuntOrderRow.Target.Value!.TerritoryType.Value.TerritoryType.Value! + .ExVersion.Value!.Name, + ExpansionId = mobHuntOrderRow.Target.Value!.TerritoryType.Value.TerritoryType.Value! + .ExVersion.Row, + MapId = mobHuntOrderRow.Target.Value!.TerritoryType.Row, + TerritoryType = mobHuntOrderRow.Target.Value!.TerritoryType.Value.TerritoryType.Row, + MobHuntId = mobHuntOrderRow.Target.Value!.Name.Row, + IsEliteMark = billNumber is BillEnum.ArrElite or BillEnum.HwElite or BillEnum.SbElite + or BillEnum.ShbElite or BillEnum.EwElite, + CurrentKillsOffset = (5 * (uint)billNumber) + mobHuntOrderRow.SubRowId, + NeededKills = mobHuntOrderRow.NeededKills, + Icon = Plugin.LoadIcon(mobHuntOrderRow.Target.Value.Icon) + }); + } + else { + if (mobHuntEntry.NeededKills < mobHuntOrderRow.NeededKills) { + mobHuntEntry.NeededKills = mobHuntOrderRow.NeededKills; } } } + } - foreach (var entry in mobHuntList) - { - var key = entry.ExpansionName ?? "Unknown"; - var subKey = new KeyValuePair(entry.TerritoryType, entry.TerritoryName ?? "Unknown"); - - if (!this.MobHuntEntries.ContainsKey(key)) - { - this.MobHuntEntries[key] = new Dictionary, List>(); - } - - if (!this.MobHuntEntries[key].ContainsKey(subKey)) - { - this.MobHuntEntries[key][subKey] = new List(); - } + foreach (MobHuntEntry entry in mobHuntList) { + string key = entry.ExpansionName ?? "Unknown"; + KeyValuePair subKey = + new KeyValuePair(entry.TerritoryType, entry.TerritoryName ?? "Unknown"); - this.MobHuntEntries[key][subKey].Add(entry); + if (!this.MobHuntEntries.ContainsKey(key)) { + this.MobHuntEntries[key] = new Dictionary, List>(); } - this.ClientStateOnTerritoryChanged(0); + if (!this.MobHuntEntries[key].ContainsKey(subKey)) { + this.MobHuntEntries[key][subKey] = new List(); + } - this.MobHuntEntriesReady = true; + this.MobHuntEntries[key][subKey].Add(entry); } - private static IDalamudTextureWrap LoadIcon(uint id) - { - var icon = Service.DataManager.GameData.GetHqIcon(id) ?? Service.DataManager.GameData.GetIcon(id)!; - var iconData = icon.GetRgbaImageData(); + this.ClientStateOnTerritoryChanged(0); - return Service.PluginInterface.UiBuilder.LoadImageRaw(iconData, icon.Header.Width, icon.Header.Height, 4); - } + this.MobHuntEntriesReady = true; + } - public void Dispose() - { - this.Dispose(true); - GC.SuppressFinalize(this); - } + private static IDalamudTextureWrap LoadIcon(uint id) { + TexFile icon = Service.DataManager.GameData.GetHqIcon(id) ?? Service.DataManager.GameData.GetIcon(id)!; + byte[] iconData = icon.GetRgbaImageData(); + + return Service.PluginInterface.UiBuilder.LoadImageRaw(iconData, icon.Header.Width, icon.Header.Height, 4); + } + + public void Dispose() { + this.Dispose(true); + GC.SuppressFinalize(this); } } diff --git a/HuntBuddy/PluginCommandManager.cs b/HuntBuddy/PluginCommandManager.cs index 4f3b712..55aeb33 100644 --- a/HuntBuddy/PluginCommandManager.cs +++ b/HuntBuddy/PluginCommandManager.cs @@ -2,93 +2,84 @@ using System.Collections.Generic; using System.Linq; using System.Reflection; + using Dalamud.Game.Command; using Dalamud.Plugin.Services; + using HuntBuddy.Attributes; -namespace HuntBuddy -{ - public class PluginCommandManager : IDisposable - { - private readonly ICommandManager commandManager; - private readonly (string, CommandInfo)[] pluginCommands; - private readonly THost host; - - public PluginCommandManager(THost host, ICommandManager commandManager) - { - this.commandManager = commandManager; - this.host = host; - - this.pluginCommands = host!.GetType().GetMethods( - BindingFlags.NonPublic | BindingFlags.Public | - BindingFlags.Static | BindingFlags.Instance) - .Where(method => method.GetCustomAttribute() != null) - .SelectMany(this.GetCommandInfoTuple) - .ToArray(); - - this.AddCommandHandlers(); - } +namespace HuntBuddy; + +public class PluginCommandManager: IDisposable { + private readonly ICommandManager commandManager; + private readonly (string, CommandInfo)[] pluginCommands; + private readonly THost host; + + public PluginCommandManager(THost host, ICommandManager commandManager) { + this.commandManager = commandManager; + this.host = host; + + this.pluginCommands = host!.GetType().GetMethods( + BindingFlags.NonPublic | BindingFlags.Public | + BindingFlags.Static | BindingFlags.Instance) + .Where(method => method.GetCustomAttribute() != null) + .SelectMany(this.GetCommandInfoTuple) + .ToArray(); + + this.AddCommandHandlers(); + } - // http://codebetter.com/patricksmacchia/2008/11/19/an-easy-and-efficient-way-to-improve-net-code-performances/ - // Benchmarking this myself gave similar results, so I'm doing this to somewhat counteract using reflection to access command attributes. - // I like the convenience of attributes, but in principle it's a bit slower to use them as opposed to just initializing CommandInfos directly. - // It's usually sub-1 millisecond anyways, though. It probably doesn't matter at all. - private void AddCommandHandlers() - { - foreach (var t in this.pluginCommands) - { - var (command, commandInfo) = t; - this.commandManager.AddHandler(command, commandInfo); - } + // http://codebetter.com/patricksmacchia/2008/11/19/an-easy-and-efficient-way-to-improve-net-code-performances/ + // Benchmarking this myself gave similar results, so I'm doing this to somewhat counteract using reflection to access command attributes. + // I like the convenience of attributes, but in principle it's a bit slower to use them as opposed to just initializing CommandInfos directly. + // It's usually sub-1 millisecond anyways, though. It probably doesn't matter at all. + private void AddCommandHandlers() { + foreach ((string, CommandInfo) t in this.pluginCommands) { + (string command, CommandInfo commandInfo) = t; + this.commandManager.AddHandler(command, commandInfo); } + } - private void RemoveCommandHandlers() - { - foreach (var t in this.pluginCommands) - { - var (command, _) = t; - this.commandManager.RemoveHandler(command); - } + private void RemoveCommandHandlers() { + foreach ((string, CommandInfo) t in this.pluginCommands) { + (string command, _) = t; + this.commandManager.RemoveHandler(command); } + } + + private IEnumerable<(string, CommandInfo)> GetCommandInfoTuple(MethodInfo method) { + CommandInfo.HandlerDelegate handlerDelegate = (CommandInfo.HandlerDelegate)Delegate.CreateDelegate( + typeof(CommandInfo.HandlerDelegate), + this.host, + method); + + CommandAttribute? command = handlerDelegate.Method.GetCustomAttribute(); + AliasesAttribute? aliases = handlerDelegate.Method.GetCustomAttribute(); + HelpMessageAttribute? helpMessage = handlerDelegate.Method.GetCustomAttribute(); + DoNotShowInHelpAttribute? doNotShowInHelp = + handlerDelegate.Method.GetCustomAttribute(); - private IEnumerable<(string, CommandInfo)> GetCommandInfoTuple(MethodInfo method) - { - var handlerDelegate = (CommandInfo.HandlerDelegate)Delegate.CreateDelegate( - typeof(CommandInfo.HandlerDelegate), - this.host, - method); - - var command = handlerDelegate.Method.GetCustomAttribute(); - var aliases = handlerDelegate.Method.GetCustomAttribute(); - var helpMessage = handlerDelegate.Method.GetCustomAttribute(); - var doNotShowInHelp = handlerDelegate.Method.GetCustomAttribute(); - - var commandInfo = new CommandInfo(handlerDelegate) - { - HelpMessage = helpMessage?.HelpMessage ?? string.Empty, - ShowInHelp = doNotShowInHelp == null, - }; - - // Create list of tuples that will be filled with one tuple per alias, in addition to the base command tuple. - var commandInfoTuples = new List<(string, CommandInfo)> { (command!.Command, commandInfo) }; - if (aliases == null) - { - return commandInfoTuples; - } - - // ReSharper disable once LoopCanBeConvertedToQuery - foreach (var t in aliases.Aliases) - { - commandInfoTuples.Add((t, commandInfo)); - } + CommandInfo commandInfo = new CommandInfo(handlerDelegate) { + HelpMessage = helpMessage?.HelpMessage ?? string.Empty, ShowInHelp = doNotShowInHelp == null, + }; + // Create list of tuples that will be filled with one tuple per alias, in addition to the base command tuple. + List<(string, CommandInfo)> commandInfoTuples = + new List<(string, CommandInfo)> { (command!.Command, commandInfo) }; + if (aliases == null) { return commandInfoTuples; } - public void Dispose() - { - this.RemoveCommandHandlers(); - GC.SuppressFinalize(this); + // ReSharper disable once LoopCanBeConvertedToQuery + foreach (string t in aliases.Aliases) { + commandInfoTuples.Add((t, commandInfo)); } + + return commandInfoTuples; + } + + public void Dispose() { + this.RemoveCommandHandlers(); + GC.SuppressFinalize(this); } -} \ No newline at end of file +} diff --git a/HuntBuddy/Service.cs b/HuntBuddy/Service.cs index 7bedf8a..d7fffe1 100644 --- a/HuntBuddy/Service.cs +++ b/HuntBuddy/Service.cs @@ -5,23 +5,58 @@ namespace HuntBuddy; -public class Service -{ - [PluginService] public static DalamudPluginInterface PluginInterface { get; set; } = null!; - - [PluginService] public static ICommandManager Commands { get; set; } = null!; - - [PluginService] public static IChatGui Chat { get; set; } = null!; - - [PluginService] public static IDataManager DataManager { get; set; } = null!; - - [PluginService] public static ISigScanner SigScanner { get; set; } = null!; - - [PluginService] public static IGameGui GameGui { get; set; } = null!; - - [PluginService] public static IClientState ClientState { get; set; } = null!; - - [PluginService] public static IFramework Framework { get; set; } = null!; - - [PluginService] public static IPluginLog PluginLog { get; set; } = null!; -} \ No newline at end of file +public class Service { + [PluginService] + public static DalamudPluginInterface PluginInterface { + get; + set; + } = null!; + + [PluginService] + public static ICommandManager Commands { + get; + set; + } = null!; + + [PluginService] + public static IChatGui Chat { + get; + set; + } = null!; + + [PluginService] + public static IDataManager DataManager { + get; + set; + } = null!; + + [PluginService] + public static ISigScanner SigScanner { + get; + set; + } = null!; + + [PluginService] + public static IGameGui GameGui { + get; + set; + } = null!; + + [PluginService] + public static IClientState ClientState { + get; + set; + } = null!; + + [PluginService] + public static IFramework Framework { + get; + set; + } = null!; + + [PluginService] + public static IPluginLog PluginLog { + get; + set; + } = null!; +} diff --git a/HuntBuddy/Structs/MobHuntStruct.cs b/HuntBuddy/Structs/MobHuntStruct.cs index f643d7c..dabb0f9 100644 --- a/HuntBuddy/Structs/MobHuntStruct.cs +++ b/HuntBuddy/Structs/MobHuntStruct.cs @@ -1,60 +1,56 @@ using System; using System.Runtime.InteropServices; -namespace HuntBuddy.Structs -{ - public enum BillEnum : uint - { - ArrRank1, - HwRank1, - HwRank2, - HwRank3, - ArrElite, - HwElite, - SbRank1, - SbRank2, - SbRank3, - SbElite, - ShbRank1, - ShbRank2, - ShbRank3, - ShbElite, - EwRank1, - EwRank2, - EwRank3, - EwElite, - } - - [Flags] - public enum ObtainedBillEnum : uint - { - ArrRank1 = 1, - HwRank1 = 1 << 1, - HwRank2 = 1 << 2, - HwRank3 = 1 << 3, - ArrElite = 1 << 4, - HwElite = 1 << 5, - SbRank1 = 1 << 6, - SbRank2 = 1 << 7, - SbRank3 = 1 << 8, - SbElite = 1 << 9, - ShbRank1 = 1 << 10, - ShbRank2 = 1 << 11, - ShbRank3 = 1 << 12, - ShbElite = 1 << 13, - EwRank1 = 1 << 14, - EwRank2 = 1 << 15, - EwRank3 = 1 << 16, - EwElite = 1 << 17, - } +namespace HuntBuddy.Structs; - // Signature to get struct address - // D1 48 8D 0D ? ? ? ? 48 83 C4 20 5F E9 ? ? ? ? - [StructLayout(LayoutKind.Explicit, Size = 0x198)] - public unsafe struct MobHuntStruct - { - [FieldOffset(0x1A)] public fixed byte BillOffset[18]; - [FieldOffset(0x2C)] public fixed int CurrentKills[5 * 18]; - [FieldOffset(0x194)] public readonly ObtainedBillEnum ObtainedBillEnumFlags; - } -} \ No newline at end of file +public enum BillEnum: uint { + ArrRank1, + HwRank1, + HwRank2, + HwRank3, + ArrElite, + HwElite, + SbRank1, + SbRank2, + SbRank3, + SbElite, + ShbRank1, + ShbRank2, + ShbRank3, + ShbElite, + EwRank1, + EwRank2, + EwRank3, + EwElite, +} + +[Flags] +public enum ObtainedBillEnum: uint { + ArrRank1 = 1, + HwRank1 = 1 << 1, + HwRank2 = 1 << 2, + HwRank3 = 1 << 3, + ArrElite = 1 << 4, + HwElite = 1 << 5, + SbRank1 = 1 << 6, + SbRank2 = 1 << 7, + SbRank3 = 1 << 8, + SbElite = 1 << 9, + ShbRank1 = 1 << 10, + ShbRank2 = 1 << 11, + ShbRank3 = 1 << 12, + ShbElite = 1 << 13, + EwRank1 = 1 << 14, + EwRank2 = 1 << 15, + EwRank3 = 1 << 16, + EwElite = 1 << 17, +} + +// Signature to get struct address +// D1 48 8D 0D ? ? ? ? 48 83 C4 20 5F E9 ? ? ? ? +[StructLayout(LayoutKind.Explicit, Size = 0x198)] +public unsafe struct MobHuntStruct { + [FieldOffset(0x1A)] public fixed byte BillOffset[18]; + [FieldOffset(0x2C)] public fixed int CurrentKills[5 * 18]; + [FieldOffset(0x194)] public readonly ObtainedBillEnum ObtainedBillEnumFlags; +} diff --git a/HuntBuddy/Utils/InterfaceUtil.cs b/HuntBuddy/Utils/InterfaceUtil.cs index be86371..ab42907 100644 --- a/HuntBuddy/Utils/InterfaceUtil.cs +++ b/HuntBuddy/Utils/InterfaceUtil.cs @@ -1,5 +1,7 @@ using System.Numerics; + using Dalamud.Interface; + using ImGuiNET; namespace HuntBuddy.Utils; @@ -7,67 +9,62 @@ namespace HuntBuddy.Utils; /// /// Interface utilities class. /// -public static class InterfaceUtil -{ - /// - /// Draws hunt icons from game images. - /// - /// containing relevant information. - public static void DrawHuntIcon(MobHuntEntry mobHuntEntry) - { - var cursorPos = ImGui.GetCursorScreenPos(); - var imageSize = mobHuntEntry.ExpansionId < 3 ? new Vector2(192f, 128f) : new Vector2(210f); - imageSize *= ImGui.GetIO().FontGlobalScale * Plugin.Instance.Configuration.IconScale; +public static class InterfaceUtil { + /// + /// Draws hunt icons from game images. + /// + /// containing relevant information. + public static void DrawHuntIcon(MobHuntEntry mobHuntEntry) { + Vector2 cursorPos = ImGui.GetCursorScreenPos(); + Vector2 imageSize = mobHuntEntry.ExpansionId < 3 ? new Vector2(192f, 128f) : new Vector2(210f); + imageSize *= ImGui.GetIO().FontGlobalScale * Plugin.Instance.Configuration.IconScale; - ImGui.InvisibleButton("canvas", imageSize); + ImGui.InvisibleButton("canvas", imageSize); - var drawList = ImGui.GetWindowDrawList(); - if (mobHuntEntry is { ExpansionId: 4, IsEliteMark: false }) // Endwalker uses circle for non elite mobs - { - drawList.AddCircleFilled( - cursorPos + (imageSize / 2f), - imageSize.X / 2f, - Plugin.Instance.Configuration.IconBackgroundColourU32); - } - else - { - drawList.AddRectFilled( - cursorPos, - cursorPos + imageSize, - Plugin.Instance.Configuration.IconBackgroundColourU32); - } + ImDrawListPtr drawList = ImGui.GetWindowDrawList(); + if (mobHuntEntry is { ExpansionId: 4, IsEliteMark: false }) // Endwalker uses circle for non elite mobs + { + drawList.AddCircleFilled( + cursorPos + (imageSize / 2f), + imageSize.X / 2f, + Plugin.Instance.Configuration.IconBackgroundColourU32); + } + else { + drawList.AddRectFilled( + cursorPos, + cursorPos + imageSize, + Plugin.Instance.Configuration.IconBackgroundColourU32); + } - drawList.AddImage(mobHuntEntry.Icon.ImGuiHandle, cursorPos, cursorPos + imageSize); - } + drawList.AddImage(mobHuntEntry.Icon.ImGuiHandle, cursorPos, cursorPos + imageSize); + } - /// - /// Renders a button with an icon. - /// - /// Desired to be rendered. - /// Button ID. - /// True if pressed. - public static bool IconButton(FontAwesomeIcon icon, string? id) - { - ImGui.PushFont(UiBuilder.IconFont); + /// + /// Renders a button with an icon. + /// + /// Desired to be rendered. + /// Button ID. + /// True if pressed. + public static bool IconButton(FontAwesomeIcon icon, string? id) { + ImGui.PushFont(UiBuilder.IconFont); - var text = icon.ToIconString(); + string? text = icon.ToIconString(); - if (id != null) - { - text += $"##{id}"; - } + if (id != null) { + text += $"##{id}"; + } - var result = ImGui.Button(text); + bool result = ImGui.Button(text); - ImGui.PopFont(); + ImGui.PopFont(); - return result; - } + return result; + } - /// - /// Renders a button with an icon. - /// - /// Desired to be rendered. - /// True if pressed. - public static bool IconButton(FontAwesomeIcon icon) => IconButton(icon, null); -} \ No newline at end of file + /// + /// Renders a button with an icon. + /// + /// Desired to be rendered. + /// True if pressed. + public static bool IconButton(FontAwesomeIcon icon) => IconButton(icon, null); +} diff --git a/HuntBuddy/Windows/ConfigurationWindow.cs b/HuntBuddy/Windows/ConfigurationWindow.cs index 58aadb4..639dc03 100644 --- a/HuntBuddy/Windows/ConfigurationWindow.cs +++ b/HuntBuddy/Windows/ConfigurationWindow.cs @@ -1,5 +1,7 @@ using System.Numerics; + using Dalamud.Interface.Windowing; + using ImGuiNET; namespace HuntBuddy.Windows; @@ -7,59 +9,51 @@ namespace HuntBuddy.Windows; /// /// Configuration window. /// -public class ConfigurationWindow : Window -{ - public ConfigurationWindow() : base( - $"{Plugin.Instance.Name} configuration", - ImGuiWindowFlags.NoDocking, - true) - { - this.Size = Vector2.Zero; - this.SizeCondition = ImGuiCond.Always; - } +public class ConfigurationWindow: Window { + public ConfigurationWindow(): base( + $"{Plugin.Instance.Name} configuration", + ImGuiWindowFlags.NoDocking, + true) { + this.Size = Vector2.Zero; + this.SizeCondition = ImGuiCond.Always; + } - public override void PreOpenCheck() - { - if (Plugin.Instance.Configuration.LockWindowPositions) - { - if (!this.Flags.HasFlag(ImGuiWindowFlags.NoResize | ImGuiWindowFlags.NoMove)) - { - this.Flags |= ImGuiWindowFlags.NoResize | ImGuiWindowFlags.NoMove; - } - } - else - { - this.Flags &= ~(ImGuiWindowFlags.NoResize | ImGuiWindowFlags.NoMove); - } - } + public override void PreOpenCheck() { + if (Plugin.Instance.Configuration.LockWindowPositions) { + if (!this.Flags.HasFlag(ImGuiWindowFlags.NoResize | ImGuiWindowFlags.NoMove)) { + this.Flags |= ImGuiWindowFlags.NoResize | ImGuiWindowFlags.NoMove; + } + } + else { + this.Flags &= ~(ImGuiWindowFlags.NoResize | ImGuiWindowFlags.NoMove); + } + } - public override void Draw() - { - var save = false; + public override void Draw() { + bool save = false; - 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); - save |= ImGui.Checkbox("Show hunts in local area", ref Plugin.Instance.Configuration.ShowLocalHunts); - save |= ImGui.Checkbox( - "Show icons of hunts in local area", - ref Plugin.Instance.Configuration.ShowLocalHuntIcons); - save |= ImGui.Checkbox( - "Hide background of local hunts window", - ref Plugin.Instance.Configuration.HideLocalHuntBackground); - save |= ImGui.Checkbox( - "Hide completed targets in local hunts window", - ref Plugin.Instance.Configuration.HideCompletedHunts); - save |= ImGui.SliderFloat("Hunt icon scale", ref Plugin.Instance.Configuration.IconScale, 0.2f, 2f, "%.2f"); - if (ImGui.ColorEdit4("Hunt icon background colour", ref Plugin.Instance.Configuration.IconBackgroundColour)) - { - Plugin.Instance.Configuration.IconBackgroundColourU32 = - ImGui.ColorConvertFloat4ToU32(Plugin.Instance.Configuration.IconBackgroundColour); - save = true; - } + 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); + save |= ImGui.Checkbox("Show hunts in local area", ref Plugin.Instance.Configuration.ShowLocalHunts); + save |= ImGui.Checkbox( + "Show icons of hunts in local area", + ref Plugin.Instance.Configuration.ShowLocalHuntIcons); + save |= ImGui.Checkbox( + "Hide background of local hunts window", + ref Plugin.Instance.Configuration.HideLocalHuntBackground); + save |= ImGui.Checkbox( + "Hide completed targets in local hunts window", + ref Plugin.Instance.Configuration.HideCompletedHunts); + save |= ImGui.SliderFloat("Hunt icon scale", ref Plugin.Instance.Configuration.IconScale, 0.2f, 2f, "%.2f"); + if (ImGui.ColorEdit4("Hunt icon background colour", ref Plugin.Instance.Configuration.IconBackgroundColour)) { + Plugin.Instance.Configuration.IconBackgroundColourU32 = + ImGui.ColorConvertFloat4ToU32(Plugin.Instance.Configuration.IconBackgroundColour); + save = true; + } - if (save) - { - Plugin.Instance.Configuration.Save(); - } - } -} \ No newline at end of file + if (save) { + Plugin.Instance.Configuration.Save(); + } + } +} diff --git a/HuntBuddy/Windows/LocalHuntsWindow.cs b/HuntBuddy/Windows/LocalHuntsWindow.cs index 6ae12ed..a475737 100644 --- a/HuntBuddy/Windows/LocalHuntsWindow.cs +++ b/HuntBuddy/Windows/LocalHuntsWindow.cs @@ -1,8 +1,11 @@ using System.Linq; using System.Numerics; + using Dalamud.Interface; using Dalamud.Interface.Windowing; + using HuntBuddy.Utils; + using ImGuiNET; namespace HuntBuddy.Windows; @@ -10,138 +13,117 @@ namespace HuntBuddy.Windows; /// /// Local hunts window. /// -public class LocalHuntsWindow : Window -{ - public LocalHuntsWindow() : base( - "Hunts in current area", - ImGuiWindowFlags.NoNavInputs | ImGuiWindowFlags.NoDocking, - true) - { - this.Size = Vector2.Zero; - this.SizeCondition = ImGuiCond.Always; - - this.IsOpen = true; - this.ShowCloseButton = false; - this.RespectCloseHotkey = false; - } - - public override void PreOpenCheck() - { - if (Plugin.Instance.Configuration.HideLocalHuntBackground) - { - if (!this.Flags.HasFlag(ImGuiWindowFlags.NoBackground)) - { - this.Flags |= ImGuiWindowFlags.NoBackground; - } - } - else - { - this.Flags &= ~ImGuiWindowFlags.NoBackground; - } - - if (Plugin.Instance.Configuration.LockWindowPositions) - { - if (!this.Flags.HasFlag(ImGuiWindowFlags.NoResize | ImGuiWindowFlags.NoMove)) - { - this.Flags |= ImGuiWindowFlags.NoResize | ImGuiWindowFlags.NoMove; - } - } - else - { - this.Flags &= ~(ImGuiWindowFlags.NoResize | ImGuiWindowFlags.NoMove); - } - } - - 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 (var mobHuntEntry in Plugin.Instance.CurrentAreaMobHuntEntries) - { - var currentKills = Plugin.Instance.MobHuntStruct->CurrentKills[mobHuntEntry.CurrentKillsOffset]; - - if (Plugin.Instance.Configuration.HideCompletedHunts && currentKills == mobHuntEntry.NeededKills) - { - continue; - } - - if (Location.Database.ContainsKey(mobHuntEntry.MobHuntId)) - { - if (InterfaceUtil.IconButton(FontAwesomeIcon.MapMarkerAlt, $"pin##{mobHuntEntry.MobHuntId}")) - { - Location.CreateMapMarker( - mobHuntEntry.TerritoryType, - mobHuntEntry.MapId, - mobHuntEntry.MobHuntId, - mobHuntEntry.Name, - Location.OpenType.None); - } - - if (ImGui.IsItemHovered()) - { - ImGui.BeginTooltip(); - ImGui.Text("Place marker on the map"); - ImGui.EndTooltip(); - } - - ImGui.SameLine(); - - if (InterfaceUtil.IconButton(FontAwesomeIcon.MapMarkedAlt, $"open##{mobHuntEntry.MobHuntId}")) - { - var includeArea = Plugin.Instance.Configuration.IncludeAreaOnMap; - if (ImGui.IsKeyDown(ImGuiKey.ModShift)) - { - includeArea = !includeArea; - } - - Location.CreateMapMarker( - mobHuntEntry.TerritoryType, - mobHuntEntry.MapId, - mobHuntEntry.MobHuntId, - mobHuntEntry.Name, - includeArea ? Location.OpenType.ShowOpen : Location.OpenType.MarkerOpen); - } - - if (ImGui.IsItemHovered()) - { - var color = ImGui.IsKeyDown(ImGuiKey.ModShift) - ? new Vector4(0f, 0.7f, 0f, 1f) - : new Vector4(0.7f, 0.7f, 0.7f, 1f); - ImGui.BeginTooltip(); - if (Plugin.Instance.Configuration.IncludeAreaOnMap) - { - ImGui.Text("Show hunt area on the map"); - ImGui.TextColored( - color, - "Hold [SHIFT] to show the location only"); - } - else - { - ImGui.Text("Show hunt location on the map"); - ImGui.TextColored( - color, - "Hold [SHIFT] to include the area"); - } - - ImGui.EndTooltip(); - } - - ImGui.SameLine(); - } - - ImGui.Text($"{mobHuntEntry.Name} ({currentKills}/{mobHuntEntry.NeededKills})"); - - if (!Plugin.Instance.Configuration.ShowLocalHuntIcons) - { - continue; - } - - InterfaceUtil.DrawHuntIcon(mobHuntEntry); - } - } -} \ No newline at end of file +public class LocalHuntsWindow: Window { + public LocalHuntsWindow(): base( + "Hunts in current area", + ImGuiWindowFlags.NoNavInputs | ImGuiWindowFlags.NoDocking, + true) { + this.Size = Vector2.Zero; + this.SizeCondition = ImGuiCond.Always; + + this.IsOpen = true; + this.ShowCloseButton = false; + this.RespectCloseHotkey = false; + } + + public override void PreOpenCheck() { + if (Plugin.Instance.Configuration.HideLocalHuntBackground) { + if (!this.Flags.HasFlag(ImGuiWindowFlags.NoBackground)) { + this.Flags |= ImGuiWindowFlags.NoBackground; + } + } + else { + this.Flags &= ~ImGuiWindowFlags.NoBackground; + } + + if (Plugin.Instance.Configuration.LockWindowPositions) { + if (!this.Flags.HasFlag(ImGuiWindowFlags.NoResize | ImGuiWindowFlags.NoMove)) { + this.Flags |= ImGuiWindowFlags.NoResize | ImGuiWindowFlags.NoMove; + } + } + else { + this.Flags &= ~(ImGuiWindowFlags.NoResize | ImGuiWindowFlags.NoMove); + } + } + + 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) { + int currentKills = Plugin.Instance.MobHuntStruct->CurrentKills[mobHuntEntry.CurrentKillsOffset]; + + if (Plugin.Instance.Configuration.HideCompletedHunts && currentKills == mobHuntEntry.NeededKills) { + continue; + } + + if (Location.Database.ContainsKey(mobHuntEntry.MobHuntId)) { + if (InterfaceUtil.IconButton(FontAwesomeIcon.MapMarkerAlt, $"pin##{mobHuntEntry.MobHuntId}")) { + Location.CreateMapMarker( + mobHuntEntry.TerritoryType, + mobHuntEntry.MapId, + mobHuntEntry.MobHuntId, + mobHuntEntry.Name, + Location.OpenType.None); + } + + if (ImGui.IsItemHovered()) { + ImGui.BeginTooltip(); + ImGui.Text("Place marker on the map"); + ImGui.EndTooltip(); + } + + ImGui.SameLine(); + + if (InterfaceUtil.IconButton(FontAwesomeIcon.MapMarkedAlt, $"open##{mobHuntEntry.MobHuntId}")) { + bool includeArea = Plugin.Instance.Configuration.IncludeAreaOnMap; + if (ImGui.IsKeyDown(ImGuiKey.ModShift)) { + includeArea = !includeArea; + } + + Location.CreateMapMarker( + mobHuntEntry.TerritoryType, + mobHuntEntry.MapId, + mobHuntEntry.MobHuntId, + mobHuntEntry.Name, + includeArea ? Location.OpenType.ShowOpen : Location.OpenType.MarkerOpen); + } + + if (ImGui.IsItemHovered()) { + Vector4 color = ImGui.IsKeyDown(ImGuiKey.ModShift) + ? new Vector4(0f, 0.7f, 0f, 1f) + : new Vector4(0.7f, 0.7f, 0.7f, 1f); + ImGui.BeginTooltip(); + if (Plugin.Instance.Configuration.IncludeAreaOnMap) { + ImGui.Text("Show hunt area on the map"); + ImGui.TextColored( + color, + "Hold [SHIFT] to show the location only"); + } + else { + ImGui.Text("Show hunt location on the map"); + ImGui.TextColored( + color, + "Hold [SHIFT] to include the area"); + } + + ImGui.EndTooltip(); + } + + ImGui.SameLine(); + } + + ImGui.Text($"{mobHuntEntry.Name} ({currentKills}/{mobHuntEntry.NeededKills})"); + + if (!Plugin.Instance.Configuration.ShowLocalHuntIcons) { + continue; + } + + InterfaceUtil.DrawHuntIcon(mobHuntEntry); + } + } +} diff --git a/HuntBuddy/Windows/MainWindow.cs b/HuntBuddy/Windows/MainWindow.cs index db4493a..21c10aa 100644 --- a/HuntBuddy/Windows/MainWindow.cs +++ b/HuntBuddy/Windows/MainWindow.cs @@ -1,9 +1,13 @@ -using System.Linq; +using System.Collections.Generic; +using System.Linq; using System.Numerics; using System.Threading.Tasks; + using Dalamud.Interface; using Dalamud.Interface.Windowing; + using ImGuiNET; + using HuntBuddy.Utils; namespace HuntBuddy.Windows; @@ -11,203 +15,173 @@ namespace HuntBuddy.Windows; /// /// Main plugin window. /// -public class MainWindow : Window -{ - public MainWindow() : base( - $"{Plugin.Instance.Name}", - ImGuiWindowFlags.NoDocking, - true) - { - this.Size = new Vector2(400 * ImGui.GetIO().FontGlobalScale, 500); - this.SizeCondition = ImGuiCond.Once; - } - - public override void PreOpenCheck() - { - if (Plugin.Instance.Configuration.LockWindowPositions) - { - if (!this.Flags.HasFlag(ImGuiWindowFlags.NoResize | ImGuiWindowFlags.NoMove)) - { - this.Flags |= ImGuiWindowFlags.NoResize | ImGuiWindowFlags.NoMove; - } - } - else - { - this.Flags &= ~(ImGuiWindowFlags.NoResize | ImGuiWindowFlags.NoMove); - } - } - - public override unsafe void Draw() - { - if (!Plugin.Instance.MobHuntEntriesReady) - { - ImGui.Text("Reloading data ..."); - return; - } - - if (InterfaceUtil.IconButton(FontAwesomeIcon.Redo, "Reload")) - { - Plugin.Instance.MobHuntEntriesReady = false; - Task.Run(Plugin.Instance.ReloadData); - return; - } - - if (ImGui.IsItemHovered()) - { - ImGui.BeginTooltip(); - ImGui.Text("Click this button to reload daily hunt data"); - ImGui.EndTooltip(); - } - - ImGui.SameLine(); - - if (InterfaceUtil.IconButton(FontAwesomeIcon.Cog, "Config")) - { - Plugin.Instance.OpenConfigUi(); - } - - foreach (var expansionEntry in Plugin.Instance.MobHuntEntries.Where( - expansionEntry => - ImGui.TreeNode(expansionEntry.Key))) - { - foreach (var entry in expansionEntry.Value.Where( - entry => - { - var treeOpen = ImGui.TreeNodeEx(entry.Key.Value, ImGuiTreeNodeFlags.AllowItemOverlap); - ImGui.SameLine(); - var 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 (var mobHuntEntry in entry.Value) - { - if (Location.Database.ContainsKey(mobHuntEntry.MobHuntId)) - { - if (InterfaceUtil.IconButton(FontAwesomeIcon.MapMarkerAlt, $"pin##{mobHuntEntry.MobHuntId}")) - { - Location.CreateMapMarker( - mobHuntEntry.TerritoryType, - mobHuntEntry.MapId, - mobHuntEntry.MobHuntId, - mobHuntEntry.Name, - Location.OpenType.None); - } - - if (ImGui.IsItemHovered()) - { - ImGui.BeginTooltip(); - ImGui.Text("Place marker on the map"); - ImGui.EndTooltip(); - } - - ImGui.SameLine(); - - if (InterfaceUtil.IconButton(FontAwesomeIcon.MapMarkedAlt, $"open##{mobHuntEntry.MobHuntId}")) - { - var includeArea = Plugin.Instance.Configuration.IncludeAreaOnMap; - if (ImGui.IsKeyDown(ImGuiKey.ModShift)) - { - includeArea = !includeArea; - } - - Location.CreateMapMarker( - mobHuntEntry.TerritoryType, - mobHuntEntry.MapId, - mobHuntEntry.MobHuntId, - mobHuntEntry.Name, - includeArea ? Location.OpenType.ShowOpen : Location.OpenType.MarkerOpen); - } - - if (ImGui.IsItemHovered()) - { - var color = ImGui.IsKeyDown(ImGuiKey.ModShift) - ? new Vector4(0f, 0.7f, 0f, 1f) - : new Vector4(0.7f, 0.7f, 0.7f, 1f); - ImGui.BeginTooltip(); - if (Plugin.Instance.Configuration.IncludeAreaOnMap) - { - ImGui.Text("Show hunt area on the map"); - ImGui.TextColored( - color, - "Hold [SHIFT] to show the location only"); - } - else - { - ImGui.Text("Show hunt location on the map"); - ImGui.TextColored( - color, - "Hold [SHIFT] to include the area"); - } - - ImGui.EndTooltip(); - } - - ImGui.SameLine(); - - if (Plugin.TeleportConsumer?.IsAvailable == true) - { - if (InterfaceUtil.IconButton(FontAwesomeIcon.StreetView, $"t##{mobHuntEntry.MobHuntId}")) - { - Location.TeleportToNearestAetheryte( - mobHuntEntry.TerritoryType, - mobHuntEntry.MapId, - mobHuntEntry.MobHuntId); - } - - if (ImGui.IsItemHovered()) - { - ImGui.BeginTooltip(); - ImGui.Text("Teleport to nearest aetheryte"); - ImGui.EndTooltip(); - } - - ImGui.SameLine(); - } - } - - var currentKills = Plugin.Instance.MobHuntStruct->CurrentKills[mobHuntEntry.CurrentKillsOffset]; - ImGui.Text(mobHuntEntry.Name); - if (ImGui.IsItemHovered()) - { - ImGui.PushStyleColor(ImGuiCol.PopupBg, Vector4.Zero); - ImGui.BeginTooltip(); - InterfaceUtil.DrawHuntIcon(mobHuntEntry); - ImGui.PopStyleColor(); - ImGui.EndTooltip(); - } - - ImGui.SameLine(); - if (currentKills != mobHuntEntry.NeededKills) - { - ImGui.Text($"({currentKills}/{mobHuntEntry.NeededKills})"); - } - else - { - ImGui.TextColored( - new Vector4(0f, 1f, 0f, 1f), - $"({currentKills}/{mobHuntEntry.NeededKills})"); - } - } - - ImGui.TreePop(); - } - - ImGui.TreePop(); - } - } -} \ No newline at end of file +public class MainWindow: Window { + public MainWindow(): base( + $"{Plugin.Instance.Name}", + ImGuiWindowFlags.NoDocking, + true) { + this.Size = new Vector2(400 * ImGui.GetIO().FontGlobalScale, 500); + this.SizeCondition = ImGuiCond.Once; + } + + public override void PreOpenCheck() { + if (Plugin.Instance.Configuration.LockWindowPositions) { + if (!this.Flags.HasFlag(ImGuiWindowFlags.NoResize | ImGuiWindowFlags.NoMove)) { + this.Flags |= ImGuiWindowFlags.NoResize | ImGuiWindowFlags.NoMove; + } + } + else { + this.Flags &= ~(ImGuiWindowFlags.NoResize | ImGuiWindowFlags.NoMove); + } + } + + public override unsafe void Draw() { + if (!Plugin.Instance.MobHuntEntriesReady) { + ImGui.Text("Reloading data ..."); + return; + } + + if (InterfaceUtil.IconButton(FontAwesomeIcon.Redo, "Reload")) { + Plugin.Instance.MobHuntEntriesReady = false; + Task.Run(Plugin.Instance.ReloadData); + return; + } + + if (ImGui.IsItemHovered()) { + ImGui.BeginTooltip(); + ImGui.Text("Click this button to reload daily hunt data"); + ImGui.EndTooltip(); + } + + ImGui.SameLine(); + + if (InterfaceUtil.IconButton(FontAwesomeIcon.Cog, "Config")) { + Plugin.Instance.OpenConfigUi(); + } + + foreach (KeyValuePair, List>> expansionEntry in + Plugin.Instance.MobHuntEntries.Where( + expansionEntry => + ImGui.TreeNode(expansionEntry.Key))) { + foreach (KeyValuePair, List> 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; + })) { + foreach (MobHuntEntry? mobHuntEntry in entry.Value) { + if (Location.Database.ContainsKey(mobHuntEntry.MobHuntId)) { + if (InterfaceUtil.IconButton(FontAwesomeIcon.MapMarkerAlt, $"pin##{mobHuntEntry.MobHuntId}")) { + Location.CreateMapMarker( + mobHuntEntry.TerritoryType, + mobHuntEntry.MapId, + mobHuntEntry.MobHuntId, + mobHuntEntry.Name, + Location.OpenType.None); + } + + if (ImGui.IsItemHovered()) { + ImGui.BeginTooltip(); + ImGui.Text("Place marker on the map"); + ImGui.EndTooltip(); + } + + ImGui.SameLine(); + + if (InterfaceUtil.IconButton(FontAwesomeIcon.MapMarkedAlt, $"open##{mobHuntEntry.MobHuntId}")) { + bool includeArea = Plugin.Instance.Configuration.IncludeAreaOnMap; + if (ImGui.IsKeyDown(ImGuiKey.ModShift)) { + includeArea = !includeArea; + } + + Location.CreateMapMarker( + mobHuntEntry.TerritoryType, + mobHuntEntry.MapId, + mobHuntEntry.MobHuntId, + mobHuntEntry.Name, + includeArea ? Location.OpenType.ShowOpen : Location.OpenType.MarkerOpen); + } + + if (ImGui.IsItemHovered()) { + Vector4 color = ImGui.IsKeyDown(ImGuiKey.ModShift) + ? new Vector4(0f, 0.7f, 0f, 1f) + : new Vector4(0.7f, 0.7f, 0.7f, 1f); + ImGui.BeginTooltip(); + if (Plugin.Instance.Configuration.IncludeAreaOnMap) { + ImGui.Text("Show hunt area on the map"); + ImGui.TextColored( + color, + "Hold [SHIFT] to show the location only"); + } + else { + ImGui.Text("Show hunt location on the map"); + ImGui.TextColored( + color, + "Hold [SHIFT] to include the area"); + } + + ImGui.EndTooltip(); + } + + ImGui.SameLine(); + + if (Plugin.TeleportConsumer?.IsAvailable == true) { + if (InterfaceUtil.IconButton(FontAwesomeIcon.StreetView, $"t##{mobHuntEntry.MobHuntId}")) { + Location.TeleportToNearestAetheryte( + mobHuntEntry.TerritoryType, + mobHuntEntry.MapId, + mobHuntEntry.MobHuntId); + } + + if (ImGui.IsItemHovered()) { + ImGui.BeginTooltip(); + ImGui.Text("Teleport to nearest aetheryte"); + ImGui.EndTooltip(); + } + + ImGui.SameLine(); + } + } + + int currentKills = Plugin.Instance.MobHuntStruct->CurrentKills[mobHuntEntry.CurrentKillsOffset]; + ImGui.Text(mobHuntEntry.Name); + if (ImGui.IsItemHovered()) { + ImGui.PushStyleColor(ImGuiCol.PopupBg, Vector4.Zero); + ImGui.BeginTooltip(); + InterfaceUtil.DrawHuntIcon(mobHuntEntry); + ImGui.PopStyleColor(); + ImGui.EndTooltip(); + } + + ImGui.SameLine(); + if (currentKills != mobHuntEntry.NeededKills) { + ImGui.Text($"({currentKills}/{mobHuntEntry.NeededKills})"); + } + else { + ImGui.TextColored( + new Vector4(0f, 1f, 0f, 1f), + $"({currentKills}/{mobHuntEntry.NeededKills})"); + } + } + + ImGui.TreePop(); + } + + ImGui.TreePop(); + } + } +}