diff --git a/.editorconfig b/.editorconfig index f89800fe8..d98d61737 100644 --- a/.editorconfig +++ b/.editorconfig @@ -42,5 +42,129 @@ dotnet_diagnostic.CS1591.severity = none dotnet_diagnostic.CA1822.severity = error dotnet_analyzer_diagnostic.category-performance.severity = warning dotnet_analyzer_diagnostic.severity = suggestion +csharp_using_directive_placement = outside_namespace:silent +csharp_prefer_simple_using_statement = true:suggestion +csharp_prefer_braces = true:silent +csharp_style_namespace_declarations = file_scoped:silent +csharp_style_prefer_method_group_conversion = true:silent +csharp_style_prefer_top_level_statements = true:silent +csharp_style_expression_bodied_methods = false:silent +csharp_style_expression_bodied_constructors = false:silent +csharp_style_expression_bodied_operators = false:silent +csharp_style_expression_bodied_properties = true:silent +csharp_style_expression_bodied_indexers = true:silent +csharp_style_expression_bodied_accessors = true:silent +csharp_style_expression_bodied_lambdas = true:silent +csharp_style_expression_bodied_local_functions = false:silent +csharp_indent_labels = one_less_than_current +csharp_style_throw_expression = true:suggestion +csharp_style_prefer_null_check_over_type_check = true:suggestion +csharp_prefer_simple_default_expression = 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_implicit_object_creation_when_type_is_apparent = true:suggestion +csharp_style_prefer_tuple_swap = true:suggestion +csharp_style_prefer_utf8_string_literals = true:suggestion +csharp_style_inlined_variable_declaration = true:suggestion +csharp_style_deconstructed_variable_declaration = true:suggestion +csharp_style_unused_value_assignment_preference = discard_variable:suggestion +csharp_style_unused_value_expression_statement_preference = discard_variable:silent +csharp_prefer_static_local_function = true:suggestion +csharp_style_prefer_readonly_struct = true:suggestion +csharp_style_allow_embedded_statements_on_same_line_experimental = true:silent +csharp_style_allow_blank_lines_between_consecutive_braces_experimental = true:silent +csharp_style_allow_blank_line_after_colon_in_constructor_initializer_experimental = true:silent +csharp_style_conditional_delegate_call = true:suggestion +csharp_style_prefer_switch_expression = true:suggestion +csharp_style_prefer_pattern_matching = true:silent +csharp_style_pattern_matching_over_is_with_cast_check = true:suggestion +csharp_style_pattern_matching_over_as_with_null_check = true:suggestion +csharp_style_prefer_not_pattern = true:suggestion +csharp_style_prefer_extended_property_pattern = true:suggestion +csharp_style_var_for_built_in_types = false:silent +csharp_style_var_when_type_is_apparent = false:silent +csharp_style_var_elsewhere = false:silent [*.{cs,vb}] -dotnet_diagnostic.CA1848.severity=suggestion \ No newline at end of file +dotnet_diagnostic.CA1848.severity=suggestion +[*.{cs,vb}] +#### Naming styles #### + +# Naming rules + +dotnet_naming_rule.interface_should_be_begins_with_i.severity = suggestion +dotnet_naming_rule.interface_should_be_begins_with_i.symbols = interface +dotnet_naming_rule.interface_should_be_begins_with_i.style = begins_with_i + +dotnet_naming_rule.types_should_be_pascal_case.severity = suggestion +dotnet_naming_rule.types_should_be_pascal_case.symbols = types +dotnet_naming_rule.types_should_be_pascal_case.style = pascal_case + +dotnet_naming_rule.non_field_members_should_be_pascal_case.severity = suggestion +dotnet_naming_rule.non_field_members_should_be_pascal_case.symbols = non_field_members +dotnet_naming_rule.non_field_members_should_be_pascal_case.style = pascal_case + +# Symbol specifications + +dotnet_naming_symbols.interface.applicable_kinds = interface +dotnet_naming_symbols.interface.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected +dotnet_naming_symbols.interface.required_modifiers = + +dotnet_naming_symbols.types.applicable_kinds = class, struct, interface, enum +dotnet_naming_symbols.types.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected +dotnet_naming_symbols.types.required_modifiers = + +dotnet_naming_symbols.non_field_members.applicable_kinds = property, event, method +dotnet_naming_symbols.non_field_members.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected +dotnet_naming_symbols.non_field_members.required_modifiers = + +# Naming styles + +dotnet_naming_style.begins_with_i.required_prefix = I +dotnet_naming_style.begins_with_i.required_suffix = +dotnet_naming_style.begins_with_i.word_separator = +dotnet_naming_style.begins_with_i.capitalization = pascal_case + +dotnet_naming_style.pascal_case.required_prefix = +dotnet_naming_style.pascal_case.required_suffix = +dotnet_naming_style.pascal_case.word_separator = +dotnet_naming_style.pascal_case.capitalization = pascal_case + +dotnet_naming_style.pascal_case.required_prefix = +dotnet_naming_style.pascal_case.required_suffix = +dotnet_naming_style.pascal_case.word_separator = +dotnet_naming_style.pascal_case.capitalization = pascal_case +dotnet_style_coalesce_expression = true:suggestion +dotnet_style_null_propagation = true:suggestion +dotnet_style_prefer_is_null_check_over_reference_equality_method = true:suggestion +dotnet_style_prefer_auto_properties = true:silent +dotnet_style_object_initializer = true:suggestion +dotnet_style_collection_initializer = true:suggestion +dotnet_style_operator_placement_when_wrapping = beginning_of_line +tab_width = 4 +indent_size = 4 +end_of_line = crlf +dotnet_style_prefer_simplified_boolean_expressions = true:suggestion +dotnet_style_prefer_conditional_expression_over_assignment = true:silent +dotnet_style_prefer_conditional_expression_over_return = true:silent +dotnet_style_explicit_tuple_names = true:suggestion +dotnet_style_prefer_inferred_tuple_names = true:suggestion +dotnet_style_prefer_inferred_anonymous_type_member_names = true:suggestion +dotnet_style_prefer_compound_assignment = true:suggestion +dotnet_style_prefer_simplified_interpolation = true:suggestion +dotnet_style_namespace_match_folder = true:suggestion +dotnet_style_readonly_field = true:suggestion +dotnet_style_predefined_type_for_locals_parameters_members = true:silent +dotnet_style_predefined_type_for_member_access = true:silent +dotnet_style_require_accessibility_modifiers = for_non_interface_members:silent +dotnet_style_allow_multiple_blank_lines_experimental = true:silent +dotnet_style_allow_statement_immediately_after_block_experimental = true:silent +dotnet_code_quality_unused_parameters = all:suggestion +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_relational_binary_operators = always_for_clarity:silent +dotnet_style_parentheses_in_other_operators = never_if_unnecessary:silent +dotnet_style_qualification_for_field = false:silent +dotnet_style_qualification_for_property = false:silent +dotnet_style_qualification_for_method = false:silent +dotnet_style_qualification_for_event = false:silent diff --git a/BlazorServer/Program.cs b/BlazorServer/Program.cs index 5dab892d4..5c85d663d 100644 --- a/BlazorServer/Program.cs +++ b/BlazorServer/Program.cs @@ -1,47 +1,48 @@ +using System; + using Microsoft.AspNetCore.Hosting; using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Logging; + using Serilog; -using System; -namespace BlazorServer +namespace BlazorServer; + +public static class Program { - public static class Program - { - private static string hostUrl = "http://0.0.0.0:5000"; + private const string hostUrl = "http://0.0.0.0:5000"; - public static void Main(string[] args) + public static void Main(string[] args) + { + while (true) { - while (true) + Log.Information("Program.Main(): Starting blazor server"); + try + { + CreateHostBuilder(args) + .Build() + .Run(); + } + catch (Exception ex) { - Log.Information("Program.Main(): Starting blazor server"); - try - { - CreateHostBuilder(args) - .Build() - .Run(); - } - catch (Exception ex) - { - Log.Information($"Program.Main(): {ex.Message}"); - Log.Information(""); - System.Threading.Thread.Sleep(3000); - } + Log.Information($"Program.Main(): {ex.Message}"); + Log.Information(""); + System.Threading.Thread.Sleep(3000); } } + } - public static IHostBuilder CreateHostBuilder(string[] args) => + public static IHostBuilder CreateHostBuilder(string[] args) => - Host.CreateDefaultBuilder(args) - .ConfigureWebHostDefaults(webBuilder => - { - webBuilder.UseUrls(hostUrl); - webBuilder.UseStartup(); - }) - .ConfigureLogging((hostingContext, logging) => + Host.CreateDefaultBuilder(args) + .ConfigureWebHostDefaults(webBuilder => { - logging.AddConsole(); - logging.AddEventSourceLogger(); - }); - } + webBuilder.UseUrls(hostUrl); + webBuilder.UseStartup(); + }) + .ConfigureLogging((hostingContext, logging) => + { + logging.AddConsole(); + logging.AddEventSourceLogger(); + }); } \ No newline at end of file diff --git a/BlazorServer/Startup.cs b/BlazorServer/Startup.cs index bcaa90704..46e84ba7d 100644 --- a/BlazorServer/Startup.cs +++ b/BlazorServer/Startup.cs @@ -1,253 +1,261 @@ +using System; +using System.Drawing; +using System.IO; +using System.Threading; +using System.Threading.Tasks; + using BlazorTable; + using Core; using Core.Addon; using Core.Database; using Core.Environment; using Core.Session; + using Game; + using MatBlazor; + using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Hosting; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.FileProviders; using Microsoft.Extensions.Hosting; +using Microsoft.Extensions.Logging; + using PPather; + using Serilog; using Serilog.Events; using Serilog.Extensions.Logging; -using System; -using System.Drawing; -using System.Threading; -using System.Threading.Tasks; -using WinAPI; + using SharedLib; -using Microsoft.Extensions.Logging; -using Microsoft.Extensions.FileProviders; -using System.IO; -namespace BlazorServer +using WinAPI; + +namespace BlazorServer; + +public sealed class Startup { - public sealed class Startup + public Startup(IConfiguration configuration) { - public Startup(IConfiguration configuration) - { - Configuration = configuration; - } + Configuration = configuration; + } - public IConfiguration Configuration { get; } + public IConfiguration Configuration { get; } - // This method gets called by the runtime. Use this method to add services to the container. - // For more information on how to configure your application, visit https://go.microsoft.com/fwlink/?LinkID=398940 - public void ConfigureServices(IServiceCollection services) + // This method gets called by the runtime. Use this method to add services to the container. + // For more information on how to configure your application, visit https://go.microsoft.com/fwlink/?LinkID=398940 + public void ConfigureServices(IServiceCollection services) + { + services.AddLogging(builder => { - services.AddLogging(builder => + LoggerSink sink = new(); + builder.Services.AddSingleton(sink); + + const string outputTemplate = "[{Timestamp:HH:mm:ss:fff} {Level:u3}] {Message:lj}{NewLine}{Exception}"; + + Log.Logger = new LoggerConfiguration() + .MinimumLevel.Debug() + .MinimumLevel.Override("Microsoft", LogEventLevel.Information) + .WriteTo.Sink(sink) + .WriteTo.File("out.log", + rollingInterval: RollingInterval.Day, + outputTemplate: outputTemplate) + .WriteTo.Debug(outputTemplate: outputTemplate) + .WriteTo.Console(outputTemplate: outputTemplate) + .CreateLogger(); + + ILoggerFactory logFactory = LoggerFactory.Create(builder => { - LoggerSink sink = new(); - builder.Services.AddSingleton(sink); - - const string outputTemplate = "[{Timestamp:HH:mm:ss:fff} {Level:u3}] {Message:lj}{NewLine}{Exception}"; - - Log.Logger = new LoggerConfiguration() - .MinimumLevel.Debug() - .MinimumLevel.Override("Microsoft", LogEventLevel.Information) - .WriteTo.Sink(sink) - .WriteTo.File("out.log", - rollingInterval: RollingInterval.Day, - outputTemplate: outputTemplate) - .WriteTo.Debug(outputTemplate: outputTemplate) - .WriteTo.Console(outputTemplate: outputTemplate) - .CreateLogger(); - - ILoggerFactory logFactory = LoggerFactory.Create(builder => - { - builder.ClearProviders().AddSerilog(); - }); - - builder.Services.AddSingleton(logFactory.CreateLogger(nameof(Program))); + builder.ClearProviders().AddSerilog(); }); - Log.Information($"[{nameof(Startup)}] {Thread.CurrentThread.CurrentCulture.TwoLetterISOLanguageName} {DateTimeOffset.Now}"); + builder.Services.AddSingleton(logFactory.CreateLogger(nameof(Program))); + }); - StartupConfigPid StartupConfigPid = new(); - Configuration.GetSection(StartupConfigPid.Position).Bind(StartupConfigPid); - while (WowProcess.Get(StartupConfigPid.Id) == null) - { - Log.Warning($"[{nameof(Startup)}] Unable to find any Wow process, is it running ?"); - Thread.Sleep(1000); - } + Log.Information($"[{nameof(Startup)}] {Thread.CurrentThread.CurrentCulture.TwoLetterISOLanguageName} {DateTimeOffset.Now}"); - WowProcess wowProcess = new(StartupConfigPid.Id); - Log.Information($"[{nameof(Startup)}] Pid: {wowProcess.ProcessId}"); - Log.Information($"[{nameof(Startup)}] Version: {wowProcess.FileVersion}"); + StartupConfigPid StartupConfigPid = new(); + Configuration.GetSection(StartupConfigPid.Position).Bind(StartupConfigPid); + while (WowProcess.Get(StartupConfigPid.Id) == null) + { + Log.Warning($"[{nameof(Startup)}] Unable to find any Wow process, is it running ?"); + Thread.Sleep(1000); + } - NativeMethods.GetWindowRect(wowProcess.Process.MainWindowHandle, out Rectangle rect); + WowProcess wowProcess = new(StartupConfigPid.Id); + Log.Information($"[{nameof(Startup)}] Pid: {wowProcess.ProcessId}"); + Log.Information($"[{nameof(Startup)}] Version: {wowProcess.FileVersion}"); - var logger = new SerilogLoggerProvider(Log.Logger, true).CreateLogger(nameof(AddonConfigurator)); - AddonConfigurator addonConfigurator = new(logger, wowProcess); - Version? installVersion = addonConfigurator.GetInstallVersion(); - Log.Information($"[{nameof(Program)}] Addon version: {installVersion}"); + NativeMethods.GetWindowRect(wowProcess.Process.MainWindowHandle, out Rectangle rect); - if (addonConfigurator.IsDefault() || installVersion == null) - { - // At this point the webpage never loads so fallback to configuration page - addonConfigurator.Delete(); - FrameConfig.Delete(); + var logger = new SerilogLoggerProvider(Log.Logger, true).CreateLogger(nameof(AddonConfigurator)); + AddonConfigurator addonConfigurator = new(logger, wowProcess); + Version? installVersion = addonConfigurator.GetInstallVersion(); + Log.Information($"[{nameof(Program)}] Addon version: {installVersion}"); - Log.Error($"[{nameof(Startup)}] {nameof(AddonConfig)} doesn't exists or addon not installed yet!"); - } + if (addonConfigurator.IsDefault() || installVersion == null) + { + // At this point the webpage never loads so fallback to configuration page + addonConfigurator.Delete(); + FrameConfig.Delete(); - if (FrameConfig.Exists() && !FrameConfig.IsValid(rect, installVersion!)) - { - // At this point the webpage never loads so fallback to configuration page - FrameConfig.Delete(); - Log.Error($"[{nameof(Startup)}] {nameof(FrameConfig)} doesn't exists or window rect is different then config!"); - } + Log.Error($"[{nameof(Startup)}] {nameof(AddonConfig)} doesn't exists or addon not installed yet!"); + } - wowProcess.Dispose(); + if (FrameConfig.Exists() && !FrameConfig.IsValid(rect, installVersion!)) + { + // At this point the webpage never loads so fallback to configuration page + FrameConfig.Delete(); + Log.Error($"[{nameof(Startup)}] {nameof(FrameConfig)} doesn't exists or window rect is different then config!"); + } - services.AddSingleton(); - services.AddSingleton(x => new(false)); - services.AddSingleton(); + wowProcess.Dispose(); - services.AddSingleton(x => new(StartupConfigPid.Id)); - services.AddSingleton(); - services.AddSingleton(x => DataConfig.Load(x.GetRequiredService().Path)); - services.AddSingleton(); - services.AddSingleton(); - services.AddSingleton(); - services.AddSingleton(); + services.AddSingleton(); + services.AddSingleton(x => new(false)); + services.AddSingleton(); - services.AddSingleton(x => FrameConfig.LoadFrames()); - services.AddSingleton(); + services.AddSingleton(x => new(StartupConfigPid.Id)); + services.AddSingleton(); + services.AddSingleton(x => DataConfig.Load(x.GetRequiredService().Path)); + services.AddSingleton(); + services.AddSingleton(); + services.AddSingleton(); + services.AddSingleton(); - services.AddSingleton(); + services.AddSingleton(x => FrameConfig.LoadFrames()); + services.AddSingleton(); - if (AddonConfig.Exists() && FrameConfig.Exists()) - { - services.AddSingleton(); + services.AddSingleton(); - services.AddSingleton(); - services.AddSingleton(); - services.AddSingleton(x => - GetPather(x.GetRequiredService(), - x.GetRequiredService(), x.GetRequiredService())); - - StartupConfigReader scr = new(); - Configuration.GetSection(StartupConfigReader.Position).Bind(scr); + if (AddonConfig.Exists() && FrameConfig.Exists()) + { + services.AddSingleton(); - if (scr.ReaderType == AddonDataProviderType.DXGI) - { - services.AddSingleton(); - Log.Information($"[{nameof(Startup)}] {nameof(AddonDataProviderDXGI)}"); - } - else - { - services.AddSingleton(); - Log.Information($"[{nameof(Startup)}] {nameof(AddonDataProviderGDI)}"); - } + services.AddSingleton(); + services.AddSingleton(); + services.AddSingleton(x => + GetPather(x.GetRequiredService(), + x.GetRequiredService(), x.GetRequiredService())); - services.AddSingleton(); - services.AddSingleton(); - services.AddSingleton(); - services.AddSingleton(); - services.AddSingleton(); - services.AddSingleton(); + StartupConfigReader scr = new(); + Configuration.GetSection(StartupConfigReader.Position).Bind(scr); - services.AddSingleton(); - services.AddSingleton(); + if (scr.ReaderType == AddonDataProviderType.DXGI) + { + services.AddSingleton(); + Log.Information($"[{nameof(Startup)}] {nameof(AddonDataProviderDXGI)}"); } else { - services.AddSingleton(); - services.AddSingleton(); - services.AddSingleton(); + services.AddSingleton(); + Log.Information($"[{nameof(Startup)}] {nameof(AddonDataProviderGDI)}"); } - services.AddMatBlazor(); - services.AddRazorPages(); - services.AddServerSideBlazor(); - services.AddBlazorTable(); + services.AddSingleton(); + services.AddSingleton(); + services.AddSingleton(); + services.AddSingleton(); + services.AddSingleton(); + services.AddSingleton(); - services.BuildServiceProvider(new ServiceProviderOptions { ValidateOnBuild = true }); + services.AddSingleton(); + services.AddSingleton(); } - - private IPPather GetPather(Microsoft.Extensions.Logging.ILogger logger, DataConfig dataConfig, WorldMapAreaDB worldMapAreaDB) + else { - StartupConfigPathing scp = new(); - Configuration.GetSection(StartupConfigPathing.Position).Bind(scp); + services.AddSingleton(); + services.AddSingleton(); + services.AddSingleton(); + } + + services.AddMatBlazor(); + services.AddRazorPages(); + services.AddServerSideBlazor(); + services.AddBlazorTable(); + + services.BuildServiceProvider(new ServiceProviderOptions { ValidateOnBuild = true }); + } + + private IPPather GetPather(Microsoft.Extensions.Logging.ILogger logger, DataConfig dataConfig, WorldMapAreaDB worldMapAreaDB) + { + StartupConfigPathing scp = new(); + Configuration.GetSection(StartupConfigPathing.Position).Bind(scp); - bool failed = false; + bool failed = false; - if (scp.Type == StartupConfigPathing.Types.RemoteV3) + if (scp.Type == StartupConfigPathing.Types.RemoteV3) + { + RemotePathingAPIV3 api = new(logger, scp.hostv3, scp.portv3, worldMapAreaDB); + if (api.PingServer()) { - RemotePathingAPIV3 api = new(logger, scp.hostv3, scp.portv3, worldMapAreaDB); - if (api.PingServer()) - { - Log.Information($"[{nameof(Startup)}] Using {StartupConfigPathing.Types.RemoteV3}({api.GetType().Name}) {scp.hostv3}:{scp.portv3}"); - return api; - } - api.Dispose(); - failed = true; + Log.Information($"[{nameof(Startup)}] Using {StartupConfigPathing.Types.RemoteV3}({api.GetType().Name}) {scp.hostv3}:{scp.portv3}"); + return api; } + api.Dispose(); + failed = true; + } - if (scp.Type == StartupConfigPathing.Types.RemoteV1 || failed) + if (scp.Type == StartupConfigPathing.Types.RemoteV1 || failed) + { + RemotePathingAPI api = new(logger, scp.hostv1, scp.portv1); + Task pingTask = Task.Run(api.PingServer); + pingTask.Wait(); + if (pingTask.Result) { - RemotePathingAPI api = new(logger, scp.hostv1, scp.portv1); - Task pingTask = Task.Run(api.PingServer); - pingTask.Wait(); - if (pingTask.Result) + if (scp.Type == StartupConfigPathing.Types.RemoteV3) { - if (scp.Type == StartupConfigPathing.Types.RemoteV3) - { - Log.Warning($"[{nameof(Startup)}] Unavailable {StartupConfigPathing.Types.RemoteV3} {scp.hostv3}:{scp.portv3} - Fallback to {StartupConfigPathing.Types.RemoteV1}"); - } - - Log.Information($"[{nameof(Startup)}] Using {StartupConfigPathing.Types.RemoteV1}({api.GetType().Name}) {scp.hostv1}:{scp.portv1}"); - return api; + Log.Warning($"[{nameof(Startup)}] Unavailable {StartupConfigPathing.Types.RemoteV3} {scp.hostv3}:{scp.portv3} - Fallback to {StartupConfigPathing.Types.RemoteV1}"); } - failed = true; - } - - if (scp.Type != StartupConfigPathing.Types.Local) - { - Log.Warning($"[{nameof(Startup)}] {scp.Type} not available!"); + Log.Information($"[{nameof(Startup)}] Using {StartupConfigPathing.Types.RemoteV1}({api.GetType().Name}) {scp.hostv1}:{scp.portv1}"); + return api; } - LocalPathingApi localApi = new(logger, new PPatherService(logger, dataConfig, worldMapAreaDB), dataConfig); - Log.Information($"[{nameof(Startup)}] Using {StartupConfigPathing.Types.Local}({localApi.GetType().Name})"); + failed = true; + } - return localApi; + if (scp.Type != StartupConfigPathing.Types.Local) + { + Log.Warning($"[{nameof(Startup)}] {scp.Type} not available!"); } - // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. - public static void Configure(IApplicationBuilder app, IWebHostEnvironment env) + LocalPathingApi localApi = new(logger, new PPatherService(logger, dataConfig, worldMapAreaDB), dataConfig); + Log.Information($"[{nameof(Startup)}] Using {StartupConfigPathing.Types.Local}({localApi.GetType().Name})"); + + return localApi; + } + + // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. + public static void Configure(IApplicationBuilder app, IWebHostEnvironment env) + { + if (env.IsDevelopment()) { - if (env.IsDevelopment()) - { - app.UseDeveloperExceptionPage(); - } - else - { - app.UseExceptionHandler("/Error"); - } + app.UseDeveloperExceptionPage(); + } + else + { + app.UseExceptionHandler("/Error"); + } - app.UseStaticFiles(); + app.UseStaticFiles(); - var dataConfig = DataConfig.Load(); - app.UseStaticFiles(new StaticFileOptions - { - FileProvider = new PhysicalFileProvider(Path.Combine(env.ContentRootPath, dataConfig.Path)), - RequestPath = "/path" - }); + var dataConfig = DataConfig.Load(); + app.UseStaticFiles(new StaticFileOptions + { + FileProvider = new PhysicalFileProvider(Path.Combine(env.ContentRootPath, dataConfig.Path)), + RequestPath = "/path" + }); - app.UseRouting(); + app.UseRouting(); - app.UseEndpoints(endpoints => - { - endpoints.MapBlazorHub(); - endpoints.MapFallbackToPage("/_Host"); - }); - } + app.UseEndpoints(endpoints => + { + endpoints.MapBlazorHub(); + endpoints.MapFallbackToPage("/_Host"); + }); } } \ No newline at end of file diff --git a/Core/Actionbar/ActionBar.cs b/Core/Actionbar/ActionBar.cs index 2b24d1f4c..e536a170a 100644 --- a/Core/Actionbar/ActionBar.cs +++ b/Core/Actionbar/ActionBar.cs @@ -1,14 +1,13 @@ -namespace Core +namespace Core; + +public static class ActionBar { - public static class ActionBar - { - public const int CELL_COUNT = 5; - public const int BIT_PER_CELL = 24; + public const int CELL_COUNT = 5; + public const int BIT_PER_CELL = 24; - public const int NUM_OF_COST = 4; + public const int NUM_OF_COST = 4; - public const int MAIN_ACTIONBAR_SLOT = 12; + public const int MAIN_ACTIONBAR_SLOT = 12; - public const int ACTION_SLOT_MUL = 100000; - } + public const int ACTION_SLOT_MUL = 100000; } diff --git a/Core/Actionbar/ActionBarBits.cs b/Core/Actionbar/ActionBarBits.cs index a2072dfdc..27a33e245 100644 --- a/Core/Actionbar/ActionBarBits.cs +++ b/Core/Actionbar/ActionBarBits.cs @@ -1,34 +1,33 @@ using System.Collections.Specialized; -namespace Core +namespace Core; + +public sealed class ActionBarBits { - public sealed class ActionBarBits - { - private readonly int[] cells; + private readonly int[] cells; - private readonly BitVector32[] bits; + private readonly BitVector32[] bits; - public ActionBarBits(params int[] cells) - { - this.cells = cells; - bits = new BitVector32[cells.Length]; - } + public ActionBarBits(params int[] cells) + { + this.cells = cells; + bits = new BitVector32[cells.Length]; + } - public void Update(IAddonDataProvider reader) + public void Update(IAddonDataProvider reader) + { + for (int i = 0; i < bits.Length; i++) { - for (int i = 0; i < bits.Length; i++) - { - bits[i] = new(reader.GetInt(cells[i])); - } + bits[i] = new(reader.GetInt(cells[i])); } + } - // https://wowwiki-archive.fandom.com/wiki/ActionSlot - public bool Is(KeyAction keyAction) - { - if (keyAction.Slot == 0) return false; + // https://wowwiki-archive.fandom.com/wiki/ActionSlot + public bool Is(KeyAction keyAction) + { + if (keyAction.Slot == 0) return false; - int index = keyAction.SlotIndex; - return bits[index / ActionBar.BIT_PER_CELL][Mask.M[index % ActionBar.BIT_PER_CELL]]; - } + int index = keyAction.SlotIndex; + return bits[index / ActionBar.BIT_PER_CELL][Mask.M[index % ActionBar.BIT_PER_CELL]]; } } diff --git a/Core/Actionbar/ActionBarCooldownReader.cs b/Core/Actionbar/ActionBarCooldownReader.cs index d5b64cc26..aa93d5a58 100644 --- a/Core/Actionbar/ActionBarCooldownReader.cs +++ b/Core/Actionbar/ActionBarCooldownReader.cs @@ -1,59 +1,58 @@ using System; -namespace Core +namespace Core; + +public sealed class ActionBarCooldownReader { - public sealed class ActionBarCooldownReader + private readonly struct Data { - private readonly struct Data + public float DurationSec { get; } + public DateTime StartTime { get; } + + public Data(float duration, DateTime startTime) { - public float DurationSec { get; } - public DateTime StartTime { get; } - - public Data(float duration, DateTime startTime) - { - DurationSec = duration; - StartTime = startTime; - } + DurationSec = duration; + StartTime = startTime; } + } - private const float FRACTION_PART = 10f; + private const float FRACTION_PART = 10f; - private readonly int cActionbarNum; + private readonly int cActionbarNum; - private readonly Data[] data; + private readonly Data[] data; - public ActionBarCooldownReader(int cActionbarNum) - { - this.cActionbarNum = cActionbarNum; + public ActionBarCooldownReader(int cActionbarNum) + { + this.cActionbarNum = cActionbarNum; - data = new Data[ActionBar.CELL_COUNT * ActionBar.BIT_PER_CELL]; - Reset(); - } + data = new Data[ActionBar.CELL_COUNT * ActionBar.BIT_PER_CELL]; + Reset(); + } - public void Read(IAddonDataProvider reader) - { - int value = reader.GetInt(cActionbarNum); - if (value == 0 || value < ActionBar.ACTION_SLOT_MUL) - return; + public void Read(IAddonDataProvider reader) + { + int value = reader.GetInt(cActionbarNum); + if (value == 0 || value < ActionBar.ACTION_SLOT_MUL) + return; - int slotIdx = (value / ActionBar.ACTION_SLOT_MUL) - 1; - float durationSec = value % ActionBar.ACTION_SLOT_MUL / FRACTION_PART; + int slotIdx = (value / ActionBar.ACTION_SLOT_MUL) - 1; + float durationSec = value % ActionBar.ACTION_SLOT_MUL / FRACTION_PART; - data[slotIdx] = new(durationSec, DateTime.UtcNow); - } + data[slotIdx] = new(durationSec, DateTime.UtcNow); + } - public void Reset() + public void Reset() + { + for (int i = 0; i < data.Length; i++) { - for (int i = 0; i < data.Length; i++) - { - data[i] = new(0, DateTime.UtcNow); - } + data[i] = new(0, DateTime.UtcNow); } + } - public int Get(KeyAction keyAction) - { - int index = keyAction.SlotIndex; - return Math.Clamp((int)(data[index].StartTime.AddSeconds(data[index].DurationSec) - DateTime.UtcNow).TotalMilliseconds, 0, int.MaxValue); - } + public int Get(KeyAction keyAction) + { + int index = keyAction.SlotIndex; + return Math.Clamp((int)(data[index].StartTime.AddSeconds(data[index].DurationSec) - DateTime.UtcNow).TotalMilliseconds, 0, int.MaxValue); } } diff --git a/Core/Actionbar/ActionBarCostReader.cs b/Core/Actionbar/ActionBarCostReader.cs index 53d5022ef..e135b1e4b 100644 --- a/Core/Actionbar/ActionBarCostReader.cs +++ b/Core/Actionbar/ActionBarCostReader.cs @@ -1,99 +1,98 @@ using System; -namespace Core -{ - public sealed class ActionBarCostEventArgs : EventArgs - { - public int Slot { get; } - public ActionBarCost ActionBarCost { get; } +namespace Core; - public ActionBarCostEventArgs(int slot, ActionBarCost abc) - { - Slot = slot; - ActionBarCost = abc; - } - } +public sealed class ActionBarCostEventArgs : EventArgs +{ + public int Slot { get; } + public ActionBarCost ActionBarCost { get; } - public readonly struct ActionBarCost + public ActionBarCostEventArgs(int slot, ActionBarCost abc) { - public readonly PowerType PowerType; - public readonly int Cost; - - public ActionBarCost(PowerType powerType, int cost) - { - PowerType = powerType; - Cost = cost; - } + Slot = slot; + ActionBarCost = abc; } +} + +public readonly struct ActionBarCost +{ + public readonly PowerType PowerType; + public readonly int Cost; - public class ActionBarCostReader + public ActionBarCost(PowerType powerType, int cost) { - private const int COST_ORDER = 10000; - private const int POWER_TYPE_MOD = 100; + PowerType = powerType; + Cost = cost; + } +} - private readonly int cActionbarMeta; - private readonly int cActionbarNum; +public class ActionBarCostReader +{ + private const int COST_ORDER = 10000; + private const int POWER_TYPE_MOD = 100; - private static readonly ActionBarCost defaultCost = new(PowerType.Mana, 0); - private readonly ActionBarCost[][] data; + private readonly int cActionbarMeta; + private readonly int cActionbarNum; - public int Count { get; private set; } + private static readonly ActionBarCost defaultCost = new(PowerType.Mana, 0); + private readonly ActionBarCost[][] data; - public event EventHandler? OnActionCostChanged; - public event Action? OnActionCostReset; + public int Count { get; private set; } - public ActionBarCostReader(int cActionbarMeta, int cActionbarNum) - { - this.cActionbarMeta = cActionbarMeta; - this.cActionbarNum = cActionbarNum; + public event EventHandler? OnActionCostChanged; + public event Action? OnActionCostReset; - data = new ActionBarCost[ActionBar.CELL_COUNT * ActionBar.BIT_PER_CELL][]; - for (int s = 0; s < data.Length; s++) - { - data[s] = new ActionBarCost[ActionBar.NUM_OF_COST]; - } + public ActionBarCostReader(int cActionbarMeta, int cActionbarNum) + { + this.cActionbarMeta = cActionbarMeta; + this.cActionbarNum = cActionbarNum; - Reset(); + data = new ActionBarCost[ActionBar.CELL_COUNT * ActionBar.BIT_PER_CELL][]; + for (int s = 0; s < data.Length; s++) + { + data[s] = new ActionBarCost[ActionBar.NUM_OF_COST]; } - public void Read(IAddonDataProvider reader) - { - int meta = reader.GetInt(cActionbarMeta); - int cost = reader.GetInt(cActionbarNum); - if ((cost == 0 && meta == 0) || meta < ActionBar.ACTION_SLOT_MUL) - return; + Reset(); + } - int slotIdx = (meta / ActionBar.ACTION_SLOT_MUL) - 1; - int costIdx = (meta / COST_ORDER % 10) - 1; - int type = meta % POWER_TYPE_MOD; + public void Read(IAddonDataProvider reader) + { + int meta = reader.GetInt(cActionbarMeta); + int cost = reader.GetInt(cActionbarNum); + if ((cost == 0 && meta == 0) || meta < ActionBar.ACTION_SLOT_MUL) + return; - ActionBarCost old = data[slotIdx][costIdx]; - data[slotIdx][costIdx] = new((PowerType)type, cost); + int slotIdx = (meta / ActionBar.ACTION_SLOT_MUL) - 1; + int costIdx = (meta / COST_ORDER % 10) - 1; + int type = meta % POWER_TYPE_MOD; - if (cost != old.Cost || (PowerType)type != old.PowerType) - OnActionCostChanged?.Invoke(this, new(slotIdx + 1, data[slotIdx][costIdx])); + ActionBarCost old = data[slotIdx][costIdx]; + data[slotIdx][costIdx] = new((PowerType)type, cost); - if (slotIdx > Count) - Count = slotIdx; - } + if (cost != old.Cost || (PowerType)type != old.PowerType) + OnActionCostChanged?.Invoke(this, new(slotIdx + 1, data[slotIdx][costIdx])); - public void Reset() + if (slotIdx > Count) + Count = slotIdx; + } + + public void Reset() + { + Count = 0; + for (int s = 0; s < data.Length; s++) { - Count = 0; - for (int s = 0; s < data.Length; s++) + for (int c = 0; c < ActionBar.NUM_OF_COST; c++) { - for (int c = 0; c < ActionBar.NUM_OF_COST; c++) - { - data[s][c] = defaultCost; - } + data[s][c] = defaultCost; } - OnActionCostReset?.Invoke(); } + OnActionCostReset?.Invoke(); + } - public ActionBarCost Get(KeyAction keyAction, int costIndex = 0) - { - int slotIdx = keyAction.SlotIndex; - return data[slotIdx][costIndex]; - } + public ActionBarCost Get(KeyAction keyAction, int costIndex = 0) + { + int slotIdx = keyAction.SlotIndex; + return data[slotIdx][costIndex]; } } diff --git a/Core/Actionbar/ActionBarPopulator.cs b/Core/Actionbar/ActionBarPopulator.cs index 5bbe18f32..bb599970c 100644 --- a/Core/Actionbar/ActionBarPopulator.cs +++ b/Core/Actionbar/ActionBarPopulator.cs @@ -1,127 +1,126 @@ using System.Collections.Generic; -namespace Core +namespace Core; + +public sealed class ActionBarPopulator { - public sealed class ActionBarPopulator + internal sealed class ActionBarSlotItem { - internal sealed class ActionBarSlotItem + public string Name { get; } + public KeyAction KeyAction { get; } + public bool IsItem { get; } + + public ActionBarSlotItem(string name, KeyAction keyAction, bool isItem) { - public string Name { get; } - public KeyAction KeyAction { get; } - public bool IsItem { get; } - - public ActionBarSlotItem(string name, KeyAction keyAction, bool isItem) - { - Name = name; - KeyAction = keyAction; - IsItem = isItem; - } + Name = name; + KeyAction = keyAction; + IsItem = isItem; } + } + + private readonly ClassConfiguration config; + private readonly BagReader bagReader; + private readonly ExecGameCommand execGameCommand; + + public ActionBarPopulator(ClassConfiguration config, BagReader bagReader, ExecGameCommand execGameCommand) + { + this.config = config; + this.bagReader = bagReader; + this.execGameCommand = execGameCommand; + } - private readonly ClassConfiguration config; - private readonly BagReader bagReader; - private readonly ExecGameCommand execGameCommand; + public void Execute() + { + List items = new(); - public ActionBarPopulator(ClassConfiguration config, BagReader bagReader, ExecGameCommand execGameCommand) + for (int i = 0; i < config.Form.Length; i++) { - this.config = config; - this.bagReader = bagReader; - this.execGameCommand = execGameCommand; + AddUnique(items, config.Form[i]); } - public void Execute() + for (int i = 0; i < config.Adhoc.Sequence.Length; i++) { - List items = new(); - - for (int i = 0; i < config.Form.Length; i++) - { - AddUnique(items, config.Form[i]); - } - - for (int i = 0; i < config.Adhoc.Sequence.Length; i++) - { - AddUnique(items, config.Adhoc.Sequence[i]); - } - - for (int i = 0; i < config.Parallel.Sequence.Length; i++) - { - AddUnique(items, config.Parallel.Sequence[i]); - } - - for (int i = 0; i < config.Pull.Sequence.Length; i++) - { - AddUnique(items, config.Pull.Sequence[i]); - } - - for (int i = 0; i < config.Combat.Sequence.Length; i++) - { - AddUnique(items, config.Combat.Sequence[i]); - } - - for (int i = 0; i < config.NPC.Sequence.Length; i++) - { - AddUnique(items, config.NPC.Sequence[i]); - } - - items.Sort((a, b) => a.KeyAction.Slot.CompareTo(b.KeyAction.Slot)); - - for (int i = 0; i < items.Count; i++) - { - string content = ScriptBuilder(items[i]); - execGameCommand.Run(content); - } + AddUnique(items, config.Adhoc.Sequence[i]); } - private void AddUnique(List items, KeyAction keyAction) + for (int i = 0; i < config.Parallel.Sequence.Length; i++) { - // not bound to actionbar slot - if (keyAction.Slot == 0) return; - - for (int i = 0; i < items.Count; i++) - { - if (items[i].KeyAction.SlotIndex == keyAction.SlotIndex) - return; - } - - string name = keyAction.Name; - bool isItem = false; - - if (name.Equals(RequirementFactory.Drink, System.StringComparison.OrdinalIgnoreCase)) - { - name = bagReader.HighestQuantityOfDrinkItemId().ToString(); - isItem = true; - } - else if (name.Equals(RequirementFactory.Food, System.StringComparison.OrdinalIgnoreCase)) - { - name = bagReader.HighestQuantityOfFoodItemId().ToString(); - isItem = true; - } - - items.Add(new(name, keyAction, isItem)); + AddUnique(items, config.Parallel.Sequence[i]); } - private static string ScriptBuilder(ActionBarSlotItem abs) + for (int i = 0; i < config.Pull.Sequence.Length; i++) { - string nameOrId = $"\"{abs.Name}\""; - if (int.TryParse(abs.Name, out int id)) - { - nameOrId = id.ToString(); - } - - string func = GetFunction(abs); - int slot = abs.KeyAction.SlotIndex + 1; - return $"/run {func}({nameOrId})PlaceAction({slot})ClearCursor()--"; + AddUnique(items, config.Pull.Sequence[i]); } - private static string GetFunction(ActionBarSlotItem a) + for (int i = 0; i < config.Combat.Sequence.Length; i++) { - if (a.IsItem) - return "PickupItem"; + AddUnique(items, config.Combat.Sequence[i]); + } - if (char.IsLower(a.Name[0])) - return "PickupMacro"; + for (int i = 0; i < config.NPC.Sequence.Length; i++) + { + AddUnique(items, config.NPC.Sequence[i]); + } + + items.Sort((a, b) => a.KeyAction.Slot.CompareTo(b.KeyAction.Slot)); - return "PickupSpellBookItem"; + for (int i = 0; i < items.Count; i++) + { + string content = ScriptBuilder(items[i]); + execGameCommand.Run(content); + } + } + + private void AddUnique(List items, KeyAction keyAction) + { + // not bound to actionbar slot + if (keyAction.Slot == 0) return; + + for (int i = 0; i < items.Count; i++) + { + if (items[i].KeyAction.SlotIndex == keyAction.SlotIndex) + return; } + + string name = keyAction.Name; + bool isItem = false; + + if (name.Equals(RequirementFactory.Drink, System.StringComparison.OrdinalIgnoreCase)) + { + name = bagReader.HighestQuantityOfDrinkItemId().ToString(); + isItem = true; + } + else if (name.Equals(RequirementFactory.Food, System.StringComparison.OrdinalIgnoreCase)) + { + name = bagReader.HighestQuantityOfFoodItemId().ToString(); + isItem = true; + } + + items.Add(new(name, keyAction, isItem)); + } + + private static string ScriptBuilder(ActionBarSlotItem abs) + { + string nameOrId = $"\"{abs.Name}\""; + if (int.TryParse(abs.Name, out int id)) + { + nameOrId = id.ToString(); + } + + string func = GetFunction(abs); + int slot = abs.KeyAction.SlotIndex + 1; + return $"/run {func}({nameOrId})PlaceAction({slot})ClearCursor()--"; + } + + private static string GetFunction(ActionBarSlotItem a) + { + if (a.IsItem) + return "PickupItem"; + + if (char.IsLower(a.Name[0])) + return "PickupMacro"; + + return "PickupSpellBookItem"; } } diff --git a/Core/Addon/AddonConfig.cs b/Core/Addon/AddonConfig.cs index b643055d6..372cd0ae4 100644 --- a/Core/Addon/AddonConfig.cs +++ b/Core/Addon/AddonConfig.cs @@ -2,62 +2,61 @@ using static Newtonsoft.Json.JsonConvert; using Newtonsoft.Json; -namespace Core +namespace Core; + +public static class AddonConfigMeta { - public static class AddonConfigMeta - { - public const int Version = 1; - public const string DefaultFileName = "addon_config.json"; - } + public const int Version = 1; + public const string DefaultFileName = "addon_config.json"; +} - public sealed class AddonConfig - { - public int Version { get; init; } = AddonConfigMeta.Version; +public sealed class AddonConfig +{ + public int Version { get; init; } = AddonConfigMeta.Version; - public string Author { get; set; } = string.Empty; - public string CellSize { get; set; } = "1"; - public string Title { get; set; } = string.Empty; - public string Command { get; set; } = string.Empty; + public string Author { get; set; } = string.Empty; + public string CellSize { get; set; } = "1"; + public string Title { get; set; } = string.Empty; + public string Command { get; set; } = string.Empty; - [JsonIgnore] - public string CommandFlush => Command + "flush"; + [JsonIgnore] + public string CommandFlush => Command + "flush"; - public bool IsDefault() - { - return - string.IsNullOrEmpty(Author) || - string.IsNullOrEmpty(Title) || - string.IsNullOrEmpty(Command); - } + public bool IsDefault() + { + return + string.IsNullOrEmpty(Author) || + string.IsNullOrEmpty(Title) || + string.IsNullOrEmpty(Command); + } - public static AddonConfig Load() + public static AddonConfig Load() + { + if (Exists()) { - if (Exists()) - { - var loaded = DeserializeObject(File.ReadAllText(AddonConfigMeta.DefaultFileName))!; - if (loaded.Version == AddonConfigMeta.Version) - return loaded; - } - - return new AddonConfig(); + var loaded = DeserializeObject(File.ReadAllText(AddonConfigMeta.DefaultFileName))!; + if (loaded.Version == AddonConfigMeta.Version) + return loaded; } - public static bool Exists() - { - return File.Exists(AddonConfigMeta.DefaultFileName); - } + return new AddonConfig(); + } - public static void Delete() - { - if (Exists()) - { - File.Delete(AddonConfigMeta.DefaultFileName); - } - } + public static bool Exists() + { + return File.Exists(AddonConfigMeta.DefaultFileName); + } - public void Save() + public static void Delete() + { + if (Exists()) { - File.WriteAllText(AddonConfigMeta.DefaultFileName, SerializeObject(this)); + File.Delete(AddonConfigMeta.DefaultFileName); } } + + public void Save() + { + File.WriteAllText(AddonConfigMeta.DefaultFileName, SerializeObject(this)); + } } \ No newline at end of file diff --git a/Core/Addon/AddonReader.cs b/Core/Addon/AddonReader.cs index a0161b78b..f4dac8d03 100644 --- a/Core/Addon/AddonReader.cs +++ b/Core/Addon/AddonReader.cs @@ -7,226 +7,225 @@ using System; using System.Threading; -namespace Core +namespace Core; + +public sealed class AddonReader : IAddonReader, IDisposable { - public sealed class AddonReader : IAddonReader, IDisposable - { - private readonly ILogger logger; - private readonly IAddonDataProvider reader; - private readonly AutoResetEvent autoResetEvent; + private readonly ILogger logger; + private readonly IAddonDataProvider reader; + private readonly AutoResetEvent autoResetEvent; - public PlayerReader PlayerReader { get; } + public PlayerReader PlayerReader { get; } - public CombatLog CombatLog { get; } + public CombatLog CombatLog { get; } - public BagReader BagReader { get; } - public EquipmentReader EquipmentReader { get; } + public BagReader BagReader { get; } + public EquipmentReader EquipmentReader { get; } - public ActionBarCostReader ActionBarCostReader { get; } + public ActionBarCostReader ActionBarCostReader { get; } - public ActionBarCooldownReader ActionBarCooldownReader { get; } + public ActionBarCooldownReader ActionBarCooldownReader { get; } - public AuraTimeReader PlayerBuffTimeReader { get; } + public AuraTimeReader PlayerBuffTimeReader { get; } - public AuraTimeReader TargetDebuffTimeReader { get; } + public AuraTimeReader TargetDebuffTimeReader { get; } - public AuraTimeReader TargetBuffTimeReader { get; } + public AuraTimeReader TargetBuffTimeReader { get; } - public ActionBarBits CurrentAction { get; } - public ActionBarBits UsableAction { get; } + public ActionBarBits CurrentAction { get; } + public ActionBarBits UsableAction { get; } - public GossipReader GossipReader { get; } + public GossipReader GossipReader { get; } - public SpellBookReader SpellBookReader { get; } - public TalentReader TalentReader { get; } + public SpellBookReader SpellBookReader { get; } + public TalentReader TalentReader { get; } - public LevelTracker LevelTracker { get; } + public LevelTracker LevelTracker { get; } - public event Action? AddonDataChanged; - public event Action? PlayerDeath; + public event Action? AddonDataChanged; + public event Action? PlayerDeath; - public WorldMapAreaDB WorldMapAreaDb { get; } + public WorldMapAreaDB WorldMapAreaDb { get; } - public ItemDB ItemDb { get; } - public CreatureDB CreatureDb { get; } - public AreaDB AreaDb { get; } + public ItemDB ItemDb { get; } + public CreatureDB CreatureDb { get; } + public AreaDB AreaDb { get; } - public RecordInt GlobalTime { get; } = new(98); + public RecordInt GlobalTime { get; } = new(98); - public int DamageTakenCount() => CombatLog.DamageTaken.Count; - public int DamageDoneCount() => CombatLog.DamageDone.Count; + public int DamageTakenCount() => CombatLog.DamageTaken.Count; + public int DamageDoneCount() => CombatLog.DamageDone.Count; - private int lastTargetGuid = -1; - public string TargetName { get; private set; } = string.Empty; + private int lastTargetGuid = -1; + public string TargetName { get; private set; } = string.Empty; - private int lastMouseOverId = -1; - public string MouseOverName { get; private set; } = string.Empty; + private int lastMouseOverId = -1; + public string MouseOverName { get; private set; } = string.Empty; - public double AvgUpdateLatency { private set; get; } - private double updateSum; - private int updateIndex; - private DateTime lastUpdate; + public double AvgUpdateLatency { private set; get; } + private double updateSum; + private int updateIndex; + private DateTime lastUpdate; - public AddonReader(ILogger logger, IAddonDataProvider reader, - AutoResetEvent autoResetEvent, AreaDB areaDB, WorldMapAreaDB worldMapAreaDB, - ItemDB itemDB, CreatureDB creatureDB, SpellDB spellDB, TalentDB talentDB) - { - this.logger = logger; - this.reader = reader; - this.autoResetEvent = autoResetEvent; + public AddonReader(ILogger logger, IAddonDataProvider reader, + AutoResetEvent autoResetEvent, AreaDB areaDB, WorldMapAreaDB worldMapAreaDB, + ItemDB itemDB, CreatureDB creatureDB, SpellDB spellDB, TalentDB talentDB) + { + this.logger = logger; + this.reader = reader; + this.autoResetEvent = autoResetEvent; - this.AreaDb = areaDB; - this.WorldMapAreaDb = worldMapAreaDB; - this.ItemDb = itemDB; - this.CreatureDb = creatureDB; + this.AreaDb = areaDB; + this.WorldMapAreaDb = worldMapAreaDB; + this.ItemDb = itemDB; + this.CreatureDb = creatureDB; - this.CombatLog = new(64, 65, 66, 67); + this.CombatLog = new(64, 65, 66, 67); - this.EquipmentReader = new(ItemDb, 23, 24); - this.BagReader = new(ItemDb, EquipmentReader, 20, 21, 22); + this.EquipmentReader = new(ItemDb, 23, 24); + this.BagReader = new(ItemDb, EquipmentReader, 20, 21, 22); - this.ActionBarCostReader = new(35, 36); - this.ActionBarCooldownReader = new(37); + this.ActionBarCostReader = new(35, 36); + this.ActionBarCooldownReader = new(37); - this.GossipReader = new(73); + this.GossipReader = new(73); - this.SpellBookReader = new(71, spellDB); + this.SpellBookReader = new(71, spellDB); - this.PlayerReader = new(reader, worldMapAreaDB); - this.LevelTracker = new(this); - this.TalentReader = new(72, PlayerReader, talentDB); + this.PlayerReader = new(reader, worldMapAreaDB); + this.LevelTracker = new(this); + this.TalentReader = new(72, PlayerReader, talentDB); - this.CurrentAction = new(25, 26, 27, 28, 29); - this.UsableAction = new(30, 31, 32, 33, 34); + this.CurrentAction = new(25, 26, 27, 28, 29); + this.UsableAction = new(30, 31, 32, 33, 34); - this.PlayerBuffTimeReader = new(79, 80); - this.TargetDebuffTimeReader = new(81, 82); - this.TargetBuffTimeReader = new(83, 84); + this.PlayerBuffTimeReader = new(79, 80); + this.TargetDebuffTimeReader = new(81, 82); + this.TargetBuffTimeReader = new(83, 84); - lastUpdate = DateTime.UtcNow; - } + lastUpdate = DateTime.UtcNow; + } - public void Dispose() - { - BagReader.Dispose(); - LevelTracker.Dispose(); - } + public void Dispose() + { + BagReader.Dispose(); + LevelTracker.Dispose(); + } - public void Update() - { - FetchData(); + public void Update() + { + FetchData(); - if (!GlobalTime.UpdatedNoEvent(this.reader)) - return; + if (!GlobalTime.UpdatedNoEvent(this.reader)) + return; - if (GlobalTime.Value <= 3) - { - updateSum = 0; - updateIndex = 0; + if (GlobalTime.Value <= 3) + { + updateSum = 0; + updateIndex = 0; - FullReset(); - return; - } - else if (updateIndex >= 512) - { - updateSum = 0; - updateIndex = 0; - } + FullReset(); + return; + } + else if (updateIndex >= 512) + { + updateSum = 0; + updateIndex = 0; + } - updateSum += (DateTime.UtcNow - lastUpdate).TotalMilliseconds; - updateIndex++; - AvgUpdateLatency = updateSum / updateIndex; - lastUpdate = DateTime.UtcNow; + updateSum += (DateTime.UtcNow - lastUpdate).TotalMilliseconds; + updateIndex++; + AvgUpdateLatency = updateSum / updateIndex; + lastUpdate = DateTime.UtcNow; - IAddonDataProvider reader = this.reader; + IAddonDataProvider reader = this.reader; - CurrentAction.Update(reader); - UsableAction.Update(reader); + CurrentAction.Update(reader); + UsableAction.Update(reader); - PlayerReader.Update(reader); + PlayerReader.Update(reader); - if (lastTargetGuid != PlayerReader.TargetGuid) - { - lastTargetGuid = PlayerReader.TargetGuid; + if (lastTargetGuid != PlayerReader.TargetGuid) + { + lastTargetGuid = PlayerReader.TargetGuid; - TargetName = - CreatureDb.Entries.TryGetValue(PlayerReader.TargetId, out Creature creature) - ? creature.Name - : reader.GetString(16) + reader.GetString(17); - } + TargetName = + CreatureDb.Entries.TryGetValue(PlayerReader.TargetId, out Creature creature) + ? creature.Name + : reader.GetString(16) + reader.GetString(17); + } - if (lastMouseOverId != PlayerReader.MouseOverId) - { - lastMouseOverId = PlayerReader.MouseOverId; + if (lastMouseOverId != PlayerReader.MouseOverId) + { + lastMouseOverId = PlayerReader.MouseOverId; - MouseOverName = - CreatureDb.Entries.TryGetValue(PlayerReader.MouseOverId, out Creature creature) - ? creature.Name - : string.Empty; - } + MouseOverName = + CreatureDb.Entries.TryGetValue(PlayerReader.MouseOverId, out Creature creature) + ? creature.Name + : string.Empty; + } - CombatLog.Update(reader, PlayerReader.Bits.PlayerInCombat()); + CombatLog.Update(reader, PlayerReader.Bits.PlayerInCombat()); - BagReader.Read(reader); - EquipmentReader.Read(reader); + BagReader.Read(reader); + EquipmentReader.Read(reader); - ActionBarCostReader.Read(reader); - ActionBarCooldownReader.Read(reader); + ActionBarCostReader.Read(reader); + ActionBarCooldownReader.Read(reader); - GossipReader.Read(reader); + GossipReader.Read(reader); - SpellBookReader.Read(reader); - TalentReader.Read(reader); + SpellBookReader.Read(reader); + TalentReader.Read(reader); - PlayerBuffTimeReader.Read(reader); - TargetDebuffTimeReader.Read(reader); - TargetBuffTimeReader.Read(reader); + PlayerBuffTimeReader.Read(reader); + TargetDebuffTimeReader.Read(reader); + TargetBuffTimeReader.Read(reader); - AreaDb.Update(WorldMapAreaDb.GetAreaId(PlayerReader.UIMapId.Value)); + AreaDb.Update(WorldMapAreaDb.GetAreaId(PlayerReader.UIMapId.Value)); - autoResetEvent.Set(); - } + autoResetEvent.Set(); + } - public void FetchData() - { - reader.Update(); - } + public void FetchData() + { + reader.Update(); + } - public void SessionReset() - { - LevelTracker.Reset(); - CombatLog.Reset(); - } + public void SessionReset() + { + LevelTracker.Reset(); + CombatLog.Reset(); + } - public void FullReset() - { - PlayerReader.Reset(); + public void FullReset() + { + PlayerReader.Reset(); - ActionBarCostReader.Reset(); - ActionBarCooldownReader.Reset(); - SpellBookReader.Reset(); - TalentReader.Reset(); + ActionBarCostReader.Reset(); + ActionBarCooldownReader.Reset(); + SpellBookReader.Reset(); + TalentReader.Reset(); - PlayerBuffTimeReader.Reset(); - TargetDebuffTimeReader.Reset(); - TargetBuffTimeReader.Reset(); + PlayerBuffTimeReader.Reset(); + TargetDebuffTimeReader.Reset(); + TargetBuffTimeReader.Reset(); - SessionReset(); - } + SessionReset(); + } - public int GetInt(int index) - { - return reader.GetInt(index); - } + public int GetInt(int index) + { + return reader.GetInt(index); + } - public void PlayerDied() - { - PlayerDeath?.Invoke(); - } + public void PlayerDied() + { + PlayerDeath?.Invoke(); + } - public void UpdateUI() - { - AddonDataChanged?.Invoke(); - } + public void UpdateUI() + { + AddonDataChanged?.Invoke(); } } \ No newline at end of file diff --git a/Core/Addon/AuraTimeReader.cs b/Core/Addon/AuraTimeReader.cs index e5bdd7d32..302a843d5 100644 --- a/Core/Addon/AuraTimeReader.cs +++ b/Core/Addon/AuraTimeReader.cs @@ -1,61 +1,60 @@ using System; using System.Collections.Generic; -namespace Core +namespace Core; + +public sealed class AuraTimeReader { - public sealed class AuraTimeReader + public readonly struct Data { - public readonly struct Data + public int DurationSec { get; } + public DateTime StartTime { get; } + + public Data(int duration, DateTime startTime) { - public int DurationSec { get; } - public DateTime StartTime { get; } - - public Data(int duration, DateTime startTime) - { - DurationSec = duration; - StartTime = startTime; - } + DurationSec = duration; + StartTime = startTime; } + } - private const int UNLIMITED = 14400; // 4 hours - anything above considered unlimited duration - - private readonly int cTextureId; - private readonly int cDurationSec; + private const int UNLIMITED = 14400; // 4 hours - anything above considered unlimited duration - private readonly Dictionary data = new(); + private readonly int cTextureId; + private readonly int cDurationSec; - public AuraTimeReader(int cTextureId, int cDurationSec) - { - this.cTextureId = cTextureId; - this.cDurationSec = cDurationSec; - Reset(); - } + private readonly Dictionary data = new(); - public void Read(IAddonDataProvider reader) - { - int textureId = reader.GetInt(cTextureId); - if (textureId == 0) return; + public AuraTimeReader(int cTextureId, int cDurationSec) + { + this.cTextureId = cTextureId; + this.cDurationSec = cDurationSec; + Reset(); + } - int durationSec = reader.GetInt(cDurationSec); - data[textureId] = new(durationSec, DateTime.UtcNow); - } + public void Read(IAddonDataProvider reader) + { + int textureId = reader.GetInt(cTextureId); + if (textureId == 0) return; - public void Reset() - { - data.Clear(); - } + int durationSec = reader.GetInt(cDurationSec); + data[textureId] = new(durationSec, DateTime.UtcNow); + } - public int GetRemainingTimeMs(int textureId) - { - return data.TryGetValue(textureId, out Data d) ? - Math.Max(0, d.DurationSec >= UNLIMITED ? 1 : (int)(d.StartTime.AddSeconds(d.DurationSec) - DateTime.UtcNow).TotalMilliseconds) - : 0; - } + public void Reset() + { + data.Clear(); + } - public int GetTotalTimeMs(KeyAction keyAction) - { - return data[keyAction.SlotIndex].DurationSec * 1000; - } + public int GetRemainingTimeMs(int textureId) + { + return data.TryGetValue(textureId, out Data d) ? + Math.Max(0, d.DurationSec >= UNLIMITED ? 1 : (int)(d.StartTime.AddSeconds(d.DurationSec) - DateTime.UtcNow).TotalMilliseconds) + : 0; + } + public int GetTotalTimeMs(KeyAction keyAction) + { + return data[keyAction.SlotIndex].DurationSec * 1000; } + } diff --git a/Core/Addon/ClientVersion.cs b/Core/Addon/ClientVersion.cs index 17bf94854..9a844b79a 100644 --- a/Core/Addon/ClientVersion.cs +++ b/Core/Addon/ClientVersion.cs @@ -1,24 +1,23 @@ -namespace Core +namespace Core; + +public enum ClientVersion { - public enum ClientVersion - { - None, - Retail, - SoM, - TBC, - Wrath - } + None, + Retail, + SoM, + TBC, + Wrath +} - public static class ClientVersion_Extension +public static class ClientVersion_Extension +{ + public static string ToStringF(this ClientVersion value) => value switch { - public static string ToStringF(this ClientVersion value) => value switch - { - ClientVersion.None => nameof(ClientVersion.None), - ClientVersion.Retail => nameof(ClientVersion.Retail), - ClientVersion.SoM => nameof(ClientVersion.SoM), - ClientVersion.TBC => nameof(ClientVersion.TBC), - ClientVersion.Wrath => nameof(ClientVersion.Wrath), - _ => nameof(ClientVersion.None) - }; - } + ClientVersion.None => nameof(ClientVersion.None), + ClientVersion.Retail => nameof(ClientVersion.Retail), + ClientVersion.SoM => nameof(ClientVersion.SoM), + ClientVersion.TBC => nameof(ClientVersion.TBC), + ClientVersion.Wrath => nameof(ClientVersion.Wrath), + _ => nameof(ClientVersion.None) + }; } diff --git a/Core/Addon/ConfigAddonReader.cs b/Core/Addon/ConfigAddonReader.cs index fd7fc3512..98863dcf9 100644 --- a/Core/Addon/ConfigAddonReader.cs +++ b/Core/Addon/ConfigAddonReader.cs @@ -1,73 +1,72 @@ using System; using System.Threading; -namespace Core.Addon +namespace Core.Addon; + +public sealed class ConfigAddonReader : IAddonReader { - public sealed class ConfigAddonReader : IAddonReader - { - public PlayerReader PlayerReader => throw new NotImplementedException(); - public BagReader BagReader => throw new NotImplementedException(); - public EquipmentReader EquipmentReader => throw new NotImplementedException(); - public LevelTracker LevelTracker => throw new NotImplementedException(); - public ActionBarCostReader ActionBarCostReader => throw new NotImplementedException(); - public ActionBarCooldownReader ActionBarCooldownReader => throw new NotImplementedException(); + public PlayerReader PlayerReader => throw new NotImplementedException(); + public BagReader BagReader => throw new NotImplementedException(); + public EquipmentReader EquipmentReader => throw new NotImplementedException(); + public LevelTracker LevelTracker => throw new NotImplementedException(); + public ActionBarCostReader ActionBarCostReader => throw new NotImplementedException(); + public ActionBarCooldownReader ActionBarCooldownReader => throw new NotImplementedException(); - public AuraTimeReader PlayerBuffTimeReader => throw new NotImplementedException(); - public AuraTimeReader TargetDebuffTimeReader => throw new NotImplementedException(); + public AuraTimeReader PlayerBuffTimeReader => throw new NotImplementedException(); + public AuraTimeReader TargetDebuffTimeReader => throw new NotImplementedException(); - public SpellBookReader SpellBookReader => throw new NotImplementedException(); - public TalentReader TalentReader => throw new NotImplementedException(); + public SpellBookReader SpellBookReader => throw new NotImplementedException(); + public TalentReader TalentReader => throw new NotImplementedException(); - public double AvgUpdateLatency => throw new NotImplementedException(); + public double AvgUpdateLatency => throw new NotImplementedException(); - public int DamageTakenCount() => throw new NotImplementedException(); + public int DamageTakenCount() => throw new NotImplementedException(); - public string TargetName => throw new NotImplementedException(); + public string TargetName => throw new NotImplementedException(); - public event Action? AddonDataChanged; + public event Action? AddonDataChanged; #pragma warning disable CS0067 // The event is never used - public event Action? PlayerDeath; + public event Action? PlayerDeath; #pragma warning restore CS0067 - private readonly IAddonDataProvider reader; - private readonly AutoResetEvent autoResetEvent; - - public ConfigAddonReader(IAddonDataProvider reader, AutoResetEvent autoResetEvent) - { - this.reader = reader; - this.autoResetEvent = autoResetEvent; - } - - public int GetInt(int index) - { - return reader.GetInt(index); - } - - public void FetchData() - { - reader.Update(); - } - - public void FullReset() - { - throw new NotImplementedException(); - } - - public void Update() - { - FetchData(); - autoResetEvent.Set(); - } - - public void UpdateUI() - { - AddonDataChanged?.Invoke(); - } - - public void SessionReset() - { - throw new NotImplementedException(); - } + private readonly IAddonDataProvider reader; + private readonly AutoResetEvent autoResetEvent; + + public ConfigAddonReader(IAddonDataProvider reader, AutoResetEvent autoResetEvent) + { + this.reader = reader; + this.autoResetEvent = autoResetEvent; + } + + public int GetInt(int index) + { + return reader.GetInt(index); + } + + public void FetchData() + { + reader.Update(); + } + + public void FullReset() + { + throw new NotImplementedException(); + } + + public void Update() + { + FetchData(); + autoResetEvent.Set(); + } + + public void UpdateUI() + { + AddonDataChanged?.Invoke(); + } + + public void SessionReset() + { + throw new NotImplementedException(); } } \ No newline at end of file diff --git a/Core/Addon/GatherSpells.cs b/Core/Addon/GatherSpells.cs index 7f80f267b..84b7fbcfe 100644 --- a/Core/Addon/GatherSpells.cs +++ b/Core/Addon/GatherSpells.cs @@ -1,28 +1,27 @@ -namespace Core +namespace Core; + +public static class GatherSpells { - public static class GatherSpells + public static readonly int[] Herbalism = new int[] { - public static readonly int[] Herbalism = new int[] - { - 2366, - 2368, - 2369, // gathering mob - 2371, // gathering mob - 3570, - 11993, - 28695, - 32605, // gathering mob - 50300, - }; + 2366, + 2368, + 2369, // gathering mob + 2371, // gathering mob + 3570, + 11993, + 28695, + 32605, // gathering mob + 50300, + }; - public static readonly int[] Mining = new int[] - { - 2575, - 2576, - 3564, - 10248, - 29354, - 50310 - }; - } + public static readonly int[] Mining = new int[] + { + 2575, + 2576, + 3564, + 10248, + 29354, + 50310 + }; } diff --git a/Core/Addon/IAddonReader.cs b/Core/Addon/IAddonReader.cs index faff0acab..ee839199f 100644 --- a/Core/Addon/IAddonReader.cs +++ b/Core/Addon/IAddonReader.cs @@ -1,45 +1,44 @@ using System; -namespace Core +namespace Core; + +public interface IAddonReader { - public interface IAddonReader - { - PlayerReader PlayerReader { get; } + PlayerReader PlayerReader { get; } - BagReader BagReader { get; } + BagReader BagReader { get; } - EquipmentReader EquipmentReader { get; } + EquipmentReader EquipmentReader { get; } - ActionBarCostReader ActionBarCostReader { get; } + ActionBarCostReader ActionBarCostReader { get; } - ActionBarCooldownReader ActionBarCooldownReader { get; } + ActionBarCooldownReader ActionBarCooldownReader { get; } - AuraTimeReader PlayerBuffTimeReader { get; } + AuraTimeReader PlayerBuffTimeReader { get; } - AuraTimeReader TargetDebuffTimeReader { get; } + AuraTimeReader TargetDebuffTimeReader { get; } - SpellBookReader SpellBookReader { get; } + SpellBookReader SpellBookReader { get; } - TalentReader TalentReader { get; } + TalentReader TalentReader { get; } - LevelTracker LevelTracker { get; } + LevelTracker LevelTracker { get; } - double AvgUpdateLatency { get; } + double AvgUpdateLatency { get; } - int DamageTakenCount(); + int DamageTakenCount(); - string TargetName { get; } + string TargetName { get; } - event Action? AddonDataChanged; - event Action? PlayerDeath; + event Action? AddonDataChanged; + event Action? PlayerDeath; - void FetchData(); - void FullReset(); + void FetchData(); + void FullReset(); - void Update(); - void UpdateUI(); - void SessionReset(); + void Update(); + void UpdateUI(); + void SessionReset(); - int GetInt(int index); - } + int GetInt(int index); } \ No newline at end of file diff --git a/Core/Addon/IMouseOverReader.cs b/Core/Addon/IMouseOverReader.cs index 858424469..62913a10a 100644 --- a/Core/Addon/IMouseOverReader.cs +++ b/Core/Addon/IMouseOverReader.cs @@ -1,10 +1,9 @@ -namespace Core +namespace Core; + +public interface IMouseOverReader { - public interface IMouseOverReader - { - int MouseOverLevel { get; } - UnitClassification MouseOverClassification { get; } - int MouseOverId { get; } - int MouseOverGuid { get; } - } + int MouseOverLevel { get; } + UnitClassification MouseOverClassification { get; } + int MouseOverId { get; } + int MouseOverGuid { get; } } diff --git a/Core/Addon/Loot.cs b/Core/Addon/Loot.cs index 462df5985..0e7bd6cd6 100644 --- a/Core/Addon/Loot.cs +++ b/Core/Addon/Loot.cs @@ -1,14 +1,13 @@ -namespace Core +namespace Core; + +public enum LootStatus { - public enum LootStatus - { - CORPSE = 0, - READY = 1, - CLOSED = 2 - } + CORPSE = 0, + READY = 1, + CLOSED = 2 +} - public static class Loot - { - public const int LOOTFRAME_AUTOLOOT_DELAY = 300; - } +public static class Loot +{ + public const int LOOTFRAME_AUTOLOOT_DELAY = 300; } \ No newline at end of file diff --git a/Core/Addon/PlayerReader.cs b/Core/Addon/PlayerReader.cs index 631550c4b..9b295b861 100644 --- a/Core/Addon/PlayerReader.cs +++ b/Core/Addon/PlayerReader.cs @@ -3,238 +3,237 @@ using SharedLib; -namespace Core +namespace Core; + +public sealed partial class PlayerReader : IMouseOverReader { - public sealed partial class PlayerReader : IMouseOverReader - { - private readonly IAddonDataProvider reader; - private readonly WorldMapAreaDB worldMapAreaDB; + private readonly IAddonDataProvider reader; + private readonly WorldMapAreaDB worldMapAreaDB; - public int SpellQueueTimeMs { get; set; } = 400; + public int SpellQueueTimeMs { get; set; } = 400; - public PlayerReader(IAddonDataProvider reader, WorldMapAreaDB mapAreaDB) - { - this.worldMapAreaDB = mapAreaDB; - - this.reader = reader; - Bits = new(8, 9); - SpellInRange = new(40); - Buffs = new(41); - TargetDebuffs = new(42); - Stance = new(48); - CustomTrigger1 = new(reader.GetInt(74)); - } + public PlayerReader(IAddonDataProvider reader, WorldMapAreaDB mapAreaDB) + { + this.worldMapAreaDB = mapAreaDB; + + this.reader = reader; + Bits = new(8, 9); + SpellInRange = new(40); + Buffs = new(41); + TargetDebuffs = new(42); + Stance = new(48); + CustomTrigger1 = new(reader.GetInt(74)); + } - public WorldMapArea WorldMapArea { get; private set; } + public WorldMapArea WorldMapArea { get; private set; } - public Vector3 MapPos => new(MapX, MapY, WorldPosZ); - public Vector3 WorldPos => worldMapAreaDB.ToWorld_FlipXY(UIMapId.Value, MapPos); + public Vector3 MapPos => new(MapX, MapY, WorldPosZ); + public Vector3 WorldPos => worldMapAreaDB.ToWorld_FlipXY(UIMapId.Value, MapPos); - public float WorldPosZ { get; set; } // MapZ not exists. Alias for WorldLoc.Z + public float WorldPosZ { get; set; } // MapZ not exists. Alias for WorldLoc.Z - public float MapX => reader.GetFixed(1) * 10; - public float MapY => reader.GetFixed(2) * 10; + public float MapX => reader.GetFixed(1) * 10; + public float MapY => reader.GetFixed(2) * 10; - public float Direction => reader.GetFixed(3); + public float Direction => reader.GetFixed(3); - public RecordInt UIMapId { get; } = new(4); + public RecordInt UIMapId { get; } = new(4); - public int MapId { get; private set; } + public int MapId { get; private set; } - public RecordInt Level { get; } = new(5); + public RecordInt Level { get; } = new(5); - public Vector3 CorpseMapPos => new(CorpseMapX, CorpseMapY, 0); - public float CorpseMapX => reader.GetFixed(6) * 10; - public float CorpseMapY => reader.GetFixed(7) * 10; + public Vector3 CorpseMapPos => new(CorpseMapX, CorpseMapY, 0); + public float CorpseMapX => reader.GetFixed(6) * 10; + public float CorpseMapY => reader.GetFixed(7) * 10; - public AddonBits Bits { get; } + public AddonBits Bits { get; } - public int HealthMax() => reader.GetInt(10); - public int HealthCurrent() => reader.GetInt(11); - public int HealthPercent() => (1 + HealthCurrent()) * 100 / (1 + HealthMax()); + public int HealthMax() => reader.GetInt(10); + public int HealthCurrent() => reader.GetInt(11); + public int HealthPercent() => (1 + HealthCurrent()) * 100 / (1 + HealthMax()); - public int PTMax() => reader.GetInt(12); // Maximum amount of Power Type (dynamic) - public int PTCurrent() => reader.GetInt(13); // Current amount of Power Type (dynamic) - public int PTPercentage() => PTCurrent() * 100 / PTMax(); // Power Type (dynamic) in terms of a percentage + public int PTMax() => reader.GetInt(12); // Maximum amount of Power Type (dynamic) + public int PTCurrent() => reader.GetInt(13); // Current amount of Power Type (dynamic) + public int PTPercentage() => PTCurrent() * 100 / PTMax(); // Power Type (dynamic) in terms of a percentage - public int ManaMax() => reader.GetInt(14); - public int ManaCurrent() => reader.GetInt(15); - public int ManaPercentage() => (1 + ManaCurrent()) * 100 / (1 + ManaMax()); + public int ManaMax() => reader.GetInt(14); + public int ManaCurrent() => reader.GetInt(15); + public int ManaPercentage() => (1 + ManaCurrent()) * 100 / (1 + ManaMax()); - public int MaxRune() => reader.GetInt(14); + public int MaxRune() => reader.GetInt(14); - public int BloodRune() => reader.GetInt(15) / 100 % 10; - public int FrostRune() => reader.GetInt(15) / 10 % 10; - public int UnholyRune() => reader.GetInt(15) % 10; + public int BloodRune() => reader.GetInt(15) / 100 % 10; + public int FrostRune() => reader.GetInt(15) / 10 % 10; + public int UnholyRune() => reader.GetInt(15) % 10; - public int TargetMaxHealth() => reader.GetInt(18); - public int TargetHealth() => reader.GetInt(19); - public int TargetHealthPercentage() => (1 + TargetHealth()) * 100 / (1 + TargetMaxHealth()); + public int TargetMaxHealth() => reader.GetInt(18); + public int TargetHealth() => reader.GetInt(19); + public int TargetHealthPercentage() => (1 + TargetHealth()) * 100 / (1 + TargetMaxHealth()); - public int PetMaxHealth() => reader.GetInt(38); - public int PetHealth() => reader.GetInt(39); - public int PetHealthPercentage() => (1 + PetHealth()) * 100 / (1 + PetMaxHealth()); + public int PetMaxHealth() => reader.GetInt(38); + public int PetHealth() => reader.GetInt(39); + public int PetHealthPercentage() => (1 + PetHealth()) * 100 / (1 + PetMaxHealth()); - public SpellInRange SpellInRange { get; } - public bool WithInPullRange() => SpellInRange.WithinPullRange(this, Class); - public bool WithInCombatRange() => SpellInRange.WithinCombatRange(this, Class); - public bool OutOfCombatRange() => !SpellInRange.WithinCombatRange(this, Class); + public SpellInRange SpellInRange { get; } + public bool WithInPullRange() => SpellInRange.WithinPullRange(this, Class); + public bool WithInCombatRange() => SpellInRange.WithinCombatRange(this, Class); + public bool OutOfCombatRange() => !SpellInRange.WithinCombatRange(this, Class); - public BuffStatus Buffs { get; } - public TargetDebuffStatus TargetDebuffs { get; } + public BuffStatus Buffs { get; } + public TargetDebuffStatus TargetDebuffs { get; } - // TargetLevel * 100 + TargetClass - public int TargetLevel => reader.GetInt(43) / 100; - public UnitClassification TargetClassification => (UnitClassification)(reader.GetInt(43) % 100); + // TargetLevel * 100 + TargetClass + public int TargetLevel => reader.GetInt(43) / 100; + public UnitClassification TargetClassification => (UnitClassification)(reader.GetInt(43) % 100); - public int Money => reader.GetInt(44) + (reader.GetInt(45) * 1000000); + public int Money => reader.GetInt(44) + (reader.GetInt(45) * 1000000); - // RACE_ID * 10000 + CLASS_ID * 100 + ClientVersion - public UnitRace Race => (UnitRace)(reader.GetInt(46) / 10000); - public UnitClass Class => (UnitClass)(reader.GetInt(46) / 100 % 100); - public ClientVersion Version => (ClientVersion)(reader.GetInt(46) % 10); + // RACE_ID * 10000 + CLASS_ID * 100 + ClientVersion + public UnitRace Race => (UnitRace)(reader.GetInt(46) / 10000); + public UnitClass Class => (UnitClass)(reader.GetInt(46) / 100 % 100); + public ClientVersion Version => (ClientVersion)(reader.GetInt(46) % 10); - // 47 empty + // 47 empty - public Stance Stance { get; } - public Form Form => Stance.Get(Class, Bits.IsStealthed(), Version); + public Stance Stance { get; } + public Form Form => Stance.Get(Class, Bits.IsStealthed(), Version); - public int MinRange() => reader.GetInt(49) % 1000; - public int MaxRange() => reader.GetInt(49) / 1000 % 1000; + public int MinRange() => reader.GetInt(49) % 1000; + public int MaxRange() => reader.GetInt(49) / 1000 % 1000; - public bool IsInMeleeRange() => MinRange() == 0 && MaxRange() != 0 && MaxRange() <= 5; - public bool InCloseMeleeRange() => MinRange() == 0 && MaxRange() <= 2; + public bool IsInMeleeRange() => MinRange() == 0 && MaxRange() != 0 && MaxRange() <= 5; + public bool InCloseMeleeRange() => MinRange() == 0 && MaxRange() <= 2; - public bool IsInDeadZone() => MinRange() >= 5 && SpellInRange.Target_Trade; // between 5-8 yard - hunter and warrior + public bool IsInDeadZone() => MinRange() >= 5 && SpellInRange.Target_Trade; // between 5-8 yard - hunter and warrior - public RecordInt PlayerXp { get; } = new(50); + public RecordInt PlayerXp { get; } = new(50); - public int PlayerMaxXp => reader.GetInt(51); - public int PlayerXpPercentage => (1 + PlayerXp.Value) * 100 / (1 + PlayerMaxXp); + public int PlayerMaxXp => reader.GetInt(51); + public int PlayerXpPercentage => (1 + PlayerXp.Value) * 100 / (1 + PlayerMaxXp); - private UI_ERROR UIError => (UI_ERROR)reader.GetInt(52); - public UI_ERROR LastUIError { get; set; } + private UI_ERROR UIError => (UI_ERROR)reader.GetInt(52); + public UI_ERROR LastUIError { get; set; } - public int SpellBeingCast => reader.GetInt(53); - public bool IsCasting() => SpellBeingCast != 0; + public int SpellBeingCast => reader.GetInt(53); + public bool IsCasting() => SpellBeingCast != 0; - // avgEquipDurability * 100 + target combo points - public int ComboPoints() => reader.GetInt(54) % 100; - public int AvgEquipDurability() => reader.GetInt(54) / 100; // 0-99 + // avgEquipDurability * 100 + target combo points + public int ComboPoints() => reader.GetInt(54) % 100; + public int AvgEquipDurability() => reader.GetInt(54) / 100; // 0-99 - public AuraCount AuraCount => new(reader, 55); + public AuraCount AuraCount => new(reader, 55); - public int TargetId => reader.GetInt(56); - public int TargetGuid => reader.GetInt(57); + public int TargetId => reader.GetInt(56); + public int TargetGuid => reader.GetInt(57); - public int SpellBeingCastByTarget => reader.GetInt(58); - public bool IsTargetCasting() => SpellBeingCastByTarget != 0; + public int SpellBeingCastByTarget => reader.GetInt(58); + public bool IsTargetCasting() => SpellBeingCastByTarget != 0; - // 10 * MouseOverTarget + TargetTarget - public UnitsTarget MouseOverTarget => (UnitsTarget)(reader.GetInt(59) / 10 % 10); - public UnitsTarget TargetTarget => (UnitsTarget)(reader.GetInt(59) % 10); - public bool TargetsMe() => TargetTarget == UnitsTarget.Me; - public bool TargetsPet() => TargetTarget == UnitsTarget.Pet; - public bool TargetsNone() => TargetTarget == UnitsTarget.None; + // 10 * MouseOverTarget + TargetTarget + public UnitsTarget MouseOverTarget => (UnitsTarget)(reader.GetInt(59) / 10 % 10); + public UnitsTarget TargetTarget => (UnitsTarget)(reader.GetInt(59) % 10); + public bool TargetsMe() => TargetTarget == UnitsTarget.Me; + public bool TargetsPet() => TargetTarget == UnitsTarget.Pet; + public bool TargetsNone() => TargetTarget == UnitsTarget.None; - public RecordInt AutoShot { get; } = new(60); - public RecordInt MainHandSwing { get; } = new(61); - public RecordInt CastEvent { get; } = new(62); - public UI_ERROR CastState => (UI_ERROR)CastEvent.Value; - public RecordInt CastSpellId { get; } = new(63); + public RecordInt AutoShot { get; } = new(60); + public RecordInt MainHandSwing { get; } = new(61); + public RecordInt CastEvent { get; } = new(62); + public UI_ERROR CastState => (UI_ERROR)CastEvent.Value; + public RecordInt CastSpellId { get; } = new(63); - public int PetGuid => reader.GetInt(68); - public int PetTargetGuid => reader.GetInt(69); - public bool PetHasTarget() => PetTargetGuid != 0; + public int PetGuid => reader.GetInt(68); + public int PetTargetGuid => reader.GetInt(69); + public bool PetHasTarget() => PetTargetGuid != 0; - public int CastCount => reader.GetInt(70); + public int CastCount => reader.GetInt(70); - public BitVector32 CustomTrigger1; + public BitVector32 CustomTrigger1; - // 10000 * off * 100 + main * 100 - public int MainHandSpeedMs() => reader.GetInt(75) % 10000 * 10; - public int OffHandSpeed => reader.GetInt(75) / 10000 * 10; + // 10000 * off * 100 + main * 100 + public int MainHandSpeedMs() => reader.GetInt(75) % 10000 * 10; + public int OffHandSpeed => reader.GetInt(75) / 10000 * 10; - public int RemainCastMs => reader.GetInt(76); + public int RemainCastMs => reader.GetInt(76); - // MouseOverLevel * 100 + MouseOverClassification - public int MouseOverLevel => reader.GetInt(85) / 100; - public UnitClassification MouseOverClassification => (UnitClassification)(reader.GetInt(85) % 100); - public int MouseOverId => reader.GetInt(86); - public int MouseOverGuid => reader.GetInt(87); + // MouseOverLevel * 100 + MouseOverClassification + public int MouseOverLevel => reader.GetInt(85) / 100; + public UnitClassification MouseOverClassification => (UnitClassification)(reader.GetInt(85) % 100); + public int MouseOverId => reader.GetInt(86); + public int MouseOverGuid => reader.GetInt(87); - public int LastCastGCD { get; set; } - public void ReadLastCastGCD() - { - LastCastGCD = reader.GetInt(94); - } + public int LastCastGCD { get; set; } + public void ReadLastCastGCD() + { + LastCastGCD = reader.GetInt(94); + } - public RecordInt GCD { get; } = new(95); + public RecordInt GCD { get; } = new(95); - public RecordInt NetworkLatency { get; } = new(96); + public RecordInt NetworkLatency { get; } = new(96); - public RecordInt LootEvent { get; } = new(97); + public RecordInt LootEvent { get; } = new(97); - public int FocusGuid => reader.GetInt(77); - public int FocusTargetGuid => reader.GetInt(78); + public int FocusGuid => reader.GetInt(77); + public int FocusTargetGuid => reader.GetInt(78); - public int RangedSpeedMs() => reader.GetInt(88) * 10; + public int RangedSpeedMs() => reader.GetInt(88) * 10; - public void Update(IAddonDataProvider reader) + public void Update(IAddonDataProvider reader) + { + if (UIMapId.Updated(reader) && UIMapId.Value != 0) { - if (UIMapId.Updated(reader) && UIMapId.Value != 0) + if (worldMapAreaDB.TryGet(UIMapId.Value, out var wma)) { - if (worldMapAreaDB.TryGet(UIMapId.Value, out var wma)) - { - WorldMapArea = wma; - MapId = wma.MapID; - } + WorldMapArea = wma; + MapId = wma.MapID; } + } - Bits.Update(reader); - SpellInRange.Update(reader); - Buffs.Update(reader); - TargetDebuffs.Update(reader); - Stance.Update(reader); - CustomTrigger1 = new(reader.GetInt(74)); + Bits.Update(reader); + SpellInRange.Update(reader); + Buffs.Update(reader); + TargetDebuffs.Update(reader); + Stance.Update(reader); + CustomTrigger1 = new(reader.GetInt(74)); - PlayerXp.Update(reader); - Level.Update(reader); + PlayerXp.Update(reader); + Level.Update(reader); - AutoShot.Update(reader); - MainHandSwing.Update(reader); - CastEvent.Update(reader); - CastSpellId.Update(reader); + AutoShot.Update(reader); + MainHandSwing.Update(reader); + CastEvent.Update(reader); + CastSpellId.Update(reader); - LootEvent.Update(reader); + LootEvent.Update(reader); - GCD.Update(reader); - NetworkLatency.Update(reader); + GCD.Update(reader); + NetworkLatency.Update(reader); - if (UIError != UI_ERROR.NONE) - LastUIError = UIError; - } + if (UIError != UI_ERROR.NONE) + LastUIError = UIError; + } - public void Reset() - { - UIMapId.Reset(); + public void Reset() + { + UIMapId.Reset(); - // Reset all RecordInt - AutoShot.Reset(); - MainHandSwing.Reset(); - CastEvent.Reset(); - CastSpellId.Reset(); + // Reset all RecordInt + AutoShot.Reset(); + MainHandSwing.Reset(); + CastEvent.Reset(); + CastSpellId.Reset(); - PlayerXp.Reset(); - Level.Reset(); + PlayerXp.Reset(); + Level.Reset(); - LootEvent.Reset(); + LootEvent.Reset(); - GCD.Reset(); - NetworkLatency.Reset(); - } + GCD.Reset(); + NetworkLatency.Reset(); } } \ No newline at end of file diff --git a/Core/Addon/SpellBookReader.cs b/Core/Addon/SpellBookReader.cs index 00662290a..1931e7f81 100644 --- a/Core/Addon/SpellBookReader.cs +++ b/Core/Addon/SpellBookReader.cs @@ -3,58 +3,57 @@ using Core.Database; using SharedLib; -namespace Core +namespace Core; + +public sealed class SpellBookReader { - public sealed class SpellBookReader - { - private readonly int cSpellId; + private readonly int cSpellId; - private readonly HashSet spells = new(); + private readonly HashSet spells = new(); - public SpellDB SpellDB { get; } - public int Count => spells.Count; + public SpellDB SpellDB { get; } + public int Count => spells.Count; - public SpellBookReader(int cSpellId, SpellDB spellDB) - { - this.cSpellId = cSpellId; - this.SpellDB = spellDB; - } + public SpellBookReader(int cSpellId, SpellDB spellDB) + { + this.cSpellId = cSpellId; + this.SpellDB = spellDB; + } - public void Read(IAddonDataProvider reader) - { - int spellId = reader.GetInt(cSpellId); - if (spellId == 0) return; + public void Read(IAddonDataProvider reader) + { + int spellId = reader.GetInt(cSpellId); + if (spellId == 0) return; - spells.Add(spellId); - } + spells.Add(spellId); + } - public void Reset() - { - spells.Clear(); - } + public void Reset() + { + spells.Clear(); + } - public bool Has(int id) - { - return spells.Contains(id); - } + public bool Has(int id) + { + return spells.Contains(id); + } - public bool TryGetValue(int id, out Spell spell) - { - return SpellDB.Spells.TryGetValue(id, out spell); - } + public bool TryGetValue(int id, out Spell spell) + { + return SpellDB.Spells.TryGetValue(id, out spell); + } - public int GetId(string name) + public int GetId(string name) + { + foreach (int id in spells) { - foreach (int id in spells) + if (TryGetValue(id, out Spell spell) && + name.Contains(spell.Name, StringComparison.OrdinalIgnoreCase)) { - if (TryGetValue(id, out Spell spell) && - name.Contains(spell.Name, StringComparison.OrdinalIgnoreCase)) - { - return spell.Id; - } + return spell.Id; } - - return 0; } + + return 0; } } diff --git a/Core/AddonComponent/AddonBits.cs b/Core/AddonComponent/AddonBits.cs index b1997a014..0aa1c1a15 100644 --- a/Core/AddonComponent/AddonBits.cs +++ b/Core/AddonComponent/AddonBits.cs @@ -1,76 +1,75 @@ using System.Collections.Specialized; -namespace Core +namespace Core; + +public sealed class AddonBits { - public sealed class AddonBits - { - private readonly int cell1; - private readonly int cell2; + private readonly int cell1; + private readonly int cell2; - private BitVector32 v1; - private BitVector32 v2; + private BitVector32 v1; + private BitVector32 v2; - public AddonBits(int cell1, int cell2) - { - this.cell1 = cell1; - this.cell2 = cell2; - } + public AddonBits(int cell1, int cell2) + { + this.cell1 = cell1; + this.cell2 = cell2; + } - public void Update(IAddonDataProvider reader) - { - v1 = new(reader.GetInt(cell1)); - v2 = new(reader.GetInt(cell2)); - } + public void Update(IAddonDataProvider reader) + { + v1 = new(reader.GetInt(cell1)); + v2 = new(reader.GetInt(cell2)); + } - // -- value1 based flags - public bool TargetInCombat() => v1[Mask._0]; - public bool TargetIsDead() => v1[Mask._1]; - public bool TargetIsNotDead() => !v1[Mask._1]; - public bool TargetAlive() => HasTarget() && TargetIsNotDead(); - public bool IsDead() => v1[Mask._2]; - public bool HasTalentPoint() => v1[Mask._3]; - public bool HasMouseOver() => v1[Mask._4]; - public bool TargetCanBeHostile() => v1[Mask._5]; - public bool HasPet() => v1[Mask._6]; - public bool HasMainHandTempEnchant() => v1[Mask._7]; - public bool HasOffHandTempEnchant() => v1[Mask._8]; - public bool ItemsAreBroken() => v1[Mask._9]; - public bool PlayerOnTaxi() => v1[Mask._10]; - public bool IsSwimming() => v1[Mask._11]; - public bool PetHappy() => v1[Mask._12]; - public bool HasAmmo() => v1[Mask._13]; - public bool PlayerInCombat() => v1[Mask._14]; - public bool TargetOfTargetIsPlayerOrPet() => v1[Mask._15]; - public bool SpellOn_AutoShot() => v1[Mask._16]; - public bool HasTarget() => v1[Mask._17]; - public bool IsMounted() => v1[Mask._18]; - public bool SpellOn_Shoot() => v1[Mask._19]; - public bool SpellOn_AutoAttack() => v1[Mask._20]; - public bool TargetIsPlayer() => v1[Mask._21]; - public bool TargetIsTagged() => v1[Mask._22]; - public bool IsFalling() => v1[Mask._23]; + // -- value1 based flags + public bool TargetInCombat() => v1[Mask._0]; + public bool TargetIsDead() => v1[Mask._1]; + public bool TargetIsNotDead() => !v1[Mask._1]; + public bool TargetAlive() => HasTarget() && TargetIsNotDead(); + public bool IsDead() => v1[Mask._2]; + public bool HasTalentPoint() => v1[Mask._3]; + public bool HasMouseOver() => v1[Mask._4]; + public bool TargetCanBeHostile() => v1[Mask._5]; + public bool HasPet() => v1[Mask._6]; + public bool HasMainHandTempEnchant() => v1[Mask._7]; + public bool HasOffHandTempEnchant() => v1[Mask._8]; + public bool ItemsAreBroken() => v1[Mask._9]; + public bool PlayerOnTaxi() => v1[Mask._10]; + public bool IsSwimming() => v1[Mask._11]; + public bool PetHappy() => v1[Mask._12]; + public bool HasAmmo() => v1[Mask._13]; + public bool PlayerInCombat() => v1[Mask._14]; + public bool TargetOfTargetIsPlayerOrPet() => v1[Mask._15]; + public bool SpellOn_AutoShot() => v1[Mask._16]; + public bool HasTarget() => v1[Mask._17]; + public bool IsMounted() => v1[Mask._18]; + public bool SpellOn_Shoot() => v1[Mask._19]; + public bool SpellOn_AutoAttack() => v1[Mask._20]; + public bool TargetIsPlayer() => v1[Mask._21]; + public bool TargetIsTagged() => v1[Mask._22]; + public bool IsFalling() => v1[Mask._23]; - // -- value2 based flags - public bool IsDrowning() => v2[Mask._0]; - public bool CorpseInRange() => v2[Mask._1]; - public bool IsIndoors() => v2[Mask._2]; - public bool HasFocus() => v2[Mask._3]; - public bool FocusInCombat() => v2[Mask._4]; - public bool FocusHasTarget() => v2[Mask._5]; - public bool FocusTargetInCombat() => v2[Mask._6]; - public bool FocusTargetCanBeHostile() => v2[Mask._7]; - public bool MouseOverIsDead() => v2[Mask._8]; - public bool PetTargetIsDead() => v2[Mask._9]; - public bool IsStealthed() => v2[Mask._10]; - public bool TargetIsTrivial() => v2[Mask._11]; - public bool TargetIsNotTrivial() => !v2[Mask._11]; - public bool MouseOverIsTrivial() => v2[Mask._12]; - public bool MouseOverIsNotTrivial() => !v2[Mask._12]; - public bool MouseOverIsTagged() => v2[Mask._13]; - public bool MouseOverCanBeHostile() => v2[Mask._14]; - public bool MouseOverIsPlayer() => v2[Mask._15]; - public bool MouseOverTargetIsPlayerOrPet() => v2[Mask._16]; - public bool MouseOverPlayerControlled() => v2[Mask._17]; - public bool TargetIsPlayerControlled() => v2[Mask._18]; - } + // -- value2 based flags + public bool IsDrowning() => v2[Mask._0]; + public bool CorpseInRange() => v2[Mask._1]; + public bool IsIndoors() => v2[Mask._2]; + public bool HasFocus() => v2[Mask._3]; + public bool FocusInCombat() => v2[Mask._4]; + public bool FocusHasTarget() => v2[Mask._5]; + public bool FocusTargetInCombat() => v2[Mask._6]; + public bool FocusTargetCanBeHostile() => v2[Mask._7]; + public bool MouseOverIsDead() => v2[Mask._8]; + public bool PetTargetIsDead() => v2[Mask._9]; + public bool IsStealthed() => v2[Mask._10]; + public bool TargetIsTrivial() => v2[Mask._11]; + public bool TargetIsNotTrivial() => !v2[Mask._11]; + public bool MouseOverIsTrivial() => v2[Mask._12]; + public bool MouseOverIsNotTrivial() => !v2[Mask._12]; + public bool MouseOverIsTagged() => v2[Mask._13]; + public bool MouseOverCanBeHostile() => v2[Mask._14]; + public bool MouseOverIsPlayer() => v2[Mask._15]; + public bool MouseOverTargetIsPlayerOrPet() => v2[Mask._16]; + public bool MouseOverPlayerControlled() => v2[Mask._17]; + public bool TargetIsPlayerControlled() => v2[Mask._18]; } \ No newline at end of file diff --git a/Core/AddonComponent/AuraCount.cs b/Core/AddonComponent/AuraCount.cs index c6c61dce3..aff89bcab 100644 --- a/Core/AddonComponent/AuraCount.cs +++ b/Core/AddonComponent/AuraCount.cs @@ -1,28 +1,27 @@ -namespace Core +namespace Core; + +public readonly struct AuraCount { - public readonly struct AuraCount - { - public int Hash { get; } - public int PlayerDebuff { get; } - public int PlayerBuff { get; } - public int TargetDebuff { get; } - public int TargetBuff { get; } + public int Hash { get; } + public int PlayerDebuff { get; } + public int PlayerBuff { get; } + public int TargetDebuff { get; } + public int TargetBuff { get; } - public AuraCount(IAddonDataProvider reader, int cell) - { - int hash = reader.GetInt(cell); + public AuraCount(IAddonDataProvider reader, int cell) + { + int hash = reader.GetInt(cell); - // playerDebuffCount * 1000000 + playerBuffCount * 10000 + targetDebuffCount * 100 + targetBuffCount - Hash = hash; - PlayerDebuff = hash / 1000000; - PlayerBuff = hash / 10000 % 100; - TargetDebuff = hash / 100 % 100; - TargetBuff = hash % 100; - } + // playerDebuffCount * 1000000 + playerBuffCount * 10000 + targetDebuffCount * 100 + targetBuffCount + Hash = hash; + PlayerDebuff = hash / 1000000; + PlayerBuff = hash / 10000 % 100; + TargetDebuff = hash / 100 % 100; + TargetBuff = hash % 100; + } - public override string ToString() - { - return $"pb: {PlayerBuff} | pd: {PlayerDebuff} | tb: {TargetBuff} | td: {TargetDebuff}"; - } + public override string ToString() + { + return $"pb: {PlayerBuff} | pd: {PlayerDebuff} | tb: {TargetBuff} | td: {TargetDebuff}"; } } diff --git a/Core/AddonComponent/BuffStatus.cs b/Core/AddonComponent/BuffStatus.cs index 7652cd3c0..33b24b567 100644 --- a/Core/AddonComponent/BuffStatus.cs +++ b/Core/AddonComponent/BuffStatus.cs @@ -1,130 +1,129 @@ using System.Collections.Specialized; -namespace Core +namespace Core; + +public sealed class BuffStatus { - public sealed class BuffStatus + private readonly int cell; + + private BitVector32 v; + + public BuffStatus(int cell) { - private readonly int cell; - - private BitVector32 v; - - public BuffStatus(int cell) - { - this.cell = cell; - } - - public void Update(IAddonDataProvider reader) - { - v = new(reader.GetInt(cell)); - } - - // All - public bool Food() => v[Mask._0]; - - public bool Drink() => v[Mask._1]; - - public bool Well_Fed() => v[Mask._2]; - - public bool Mana_Regeneration() => v[Mask._3]; - - public bool Clearcasting() => v[Mask._4]; - - // Priest - public bool Fortitude() => v[Mask._10]; - public bool InnerFire() => v[Mask._11]; - public bool Renew() => v[Mask._12]; - public bool Shield() => v[Mask._13]; - public bool DivineSpirit() => v[Mask._14]; - - // Druid - public bool MarkOfTheWild() => v[Mask._10]; - public bool Thorns() => v[Mask._11]; - public bool TigersFury() => v[Mask._12]; - public bool Prowl() => v[Mask._13]; - public bool Rejuvenation() => v[Mask._14]; - public bool Regrowth() => v[Mask._15]; - public bool OmenOfClarity() => v[Mask._16]; - - // Paladin - public bool SealofRighteousness() => v[Mask._5]; - public bool SealoftheCrusader() => v[Mask._6]; - public bool SealofCommand() => v[Mask._7]; - public bool SealofWisdom() => v[Mask._8]; - public bool SealofLight() => v[Mask._9]; - public bool SealofBlood() => v[Mask._10]; - public bool SealofVengeance() => v[Mask._11]; - - public bool BlessingofMight() => v[Mask._12]; - public bool BlessingofProtection() => v[Mask._13]; - public bool BlessingofWisdom() => v[Mask._14]; - public bool BlessingofKings() => v[Mask._15]; - public bool BlessingofSalvation() => v[Mask._16]; - public bool BlessingofSanctuary() => v[Mask._17]; - public bool BlessingofLight() => v[Mask._18]; - - public bool RighteousFury() => v[Mask._19]; - public bool DivineProtection() => v[Mask._20]; - public bool AvengingWrath() => v[Mask._21]; - public bool HolyShield() => v[Mask._22]; - public bool DivineShield() => v[Mask._23]; - - // Mage - public bool FrostArmor() => v[Mask._10]; - public bool ArcaneIntellect() => v[Mask._11]; - public bool IceBarrier() => v[Mask._12]; - public bool Ward() => v[Mask._13]; - public bool FirePower() => v[Mask._14]; - public bool ManaShield() => v[Mask._15]; - public bool PresenceOfMind() => v[Mask._16]; - public bool ArcanePower() => v[Mask._17]; - - // Rogue - public bool SliceAndDice() => v[Mask._10]; - public bool Stealth() => v[Mask._11]; - - // Warrior - public bool BattleShout() => v[Mask._10]; - public bool Bloodrage() => v[Mask._11]; - - // Warlock - public bool Demon() => v[Mask._10]; //Skin and Armor - public bool SoulLink() => v[Mask._11]; - public bool SoulstoneResurrection() => v[Mask._12]; - public bool ShadowTrance() => v[Mask._13]; - public bool FelArmor() => v[Mask._14]; - public bool FelDomination() => v[Mask._15]; - public bool DemonicSacrifice() => v[Mask._16]; - - // Shaman - public bool LightningShield() => v[Mask._10]; - public bool WaterShield() => v[Mask._11]; - public bool ShamanisticFocus() => v[Mask._12]; - public bool Stoneskin() => v[Mask._13]; - - // Hunter - public bool AspectoftheCheetah() => v[Mask._10]; - public bool AspectofthePack() => v[Mask._11]; - public bool AspectoftheHawk() => v[Mask._12]; - public bool AspectoftheMonkey() => v[Mask._13]; - public bool AspectoftheViper() => v[Mask._14]; - public bool RapidFire() => v[Mask._15]; - public bool QuickShots() => v[Mask._16]; - public bool TrueshotAura() => v[Mask._17]; - public bool AspectoftheDragonhawk() => v[Mask._18]; - public bool LockandLoad() => v[Mask._19]; - - // Death Knight - public bool BloodTap() => v[Mask._10]; - public bool HornofWinter() => v[Mask._11]; - public bool IceboundFortitude() => v[Mask._12]; - public bool PathofFrost() => v[Mask._13]; - public bool AntiMagicShell() => v[Mask._14]; - public bool ArmyoftheDead() => v[Mask._15]; - public bool VampiricBlood() => v[Mask._16]; - public bool DancingRuneWeapon() => v[Mask._17]; - public bool UnbreakableArmor() => v[Mask._18]; - public bool BoneShield() => v[Mask._19]; - public bool SummonGargoyle() => v[Mask._20]; - public bool FreezingFog() => v[Mask._21]; + this.cell = cell; } + + public void Update(IAddonDataProvider reader) + { + v = new(reader.GetInt(cell)); + } + + // All + public bool Food() => v[Mask._0]; + + public bool Drink() => v[Mask._1]; + + public bool Well_Fed() => v[Mask._2]; + + public bool Mana_Regeneration() => v[Mask._3]; + + public bool Clearcasting() => v[Mask._4]; + + // Priest + public bool Fortitude() => v[Mask._10]; + public bool InnerFire() => v[Mask._11]; + public bool Renew() => v[Mask._12]; + public bool Shield() => v[Mask._13]; + public bool DivineSpirit() => v[Mask._14]; + + // Druid + public bool MarkOfTheWild() => v[Mask._10]; + public bool Thorns() => v[Mask._11]; + public bool TigersFury() => v[Mask._12]; + public bool Prowl() => v[Mask._13]; + public bool Rejuvenation() => v[Mask._14]; + public bool Regrowth() => v[Mask._15]; + public bool OmenOfClarity() => v[Mask._16]; + + // Paladin + public bool SealofRighteousness() => v[Mask._5]; + public bool SealoftheCrusader() => v[Mask._6]; + public bool SealofCommand() => v[Mask._7]; + public bool SealofWisdom() => v[Mask._8]; + public bool SealofLight() => v[Mask._9]; + public bool SealofBlood() => v[Mask._10]; + public bool SealofVengeance() => v[Mask._11]; + + public bool BlessingofMight() => v[Mask._12]; + public bool BlessingofProtection() => v[Mask._13]; + public bool BlessingofWisdom() => v[Mask._14]; + public bool BlessingofKings() => v[Mask._15]; + public bool BlessingofSalvation() => v[Mask._16]; + public bool BlessingofSanctuary() => v[Mask._17]; + public bool BlessingofLight() => v[Mask._18]; + + public bool RighteousFury() => v[Mask._19]; + public bool DivineProtection() => v[Mask._20]; + public bool AvengingWrath() => v[Mask._21]; + public bool HolyShield() => v[Mask._22]; + public bool DivineShield() => v[Mask._23]; + + // Mage + public bool FrostArmor() => v[Mask._10]; + public bool ArcaneIntellect() => v[Mask._11]; + public bool IceBarrier() => v[Mask._12]; + public bool Ward() => v[Mask._13]; + public bool FirePower() => v[Mask._14]; + public bool ManaShield() => v[Mask._15]; + public bool PresenceOfMind() => v[Mask._16]; + public bool ArcanePower() => v[Mask._17]; + + // Rogue + public bool SliceAndDice() => v[Mask._10]; + public bool Stealth() => v[Mask._11]; + + // Warrior + public bool BattleShout() => v[Mask._10]; + public bool Bloodrage() => v[Mask._11]; + + // Warlock + public bool Demon() => v[Mask._10]; //Skin and Armor + public bool SoulLink() => v[Mask._11]; + public bool SoulstoneResurrection() => v[Mask._12]; + public bool ShadowTrance() => v[Mask._13]; + public bool FelArmor() => v[Mask._14]; + public bool FelDomination() => v[Mask._15]; + public bool DemonicSacrifice() => v[Mask._16]; + + // Shaman + public bool LightningShield() => v[Mask._10]; + public bool WaterShield() => v[Mask._11]; + public bool ShamanisticFocus() => v[Mask._12]; + public bool Stoneskin() => v[Mask._13]; + + // Hunter + public bool AspectoftheCheetah() => v[Mask._10]; + public bool AspectofthePack() => v[Mask._11]; + public bool AspectoftheHawk() => v[Mask._12]; + public bool AspectoftheMonkey() => v[Mask._13]; + public bool AspectoftheViper() => v[Mask._14]; + public bool RapidFire() => v[Mask._15]; + public bool QuickShots() => v[Mask._16]; + public bool TrueshotAura() => v[Mask._17]; + public bool AspectoftheDragonhawk() => v[Mask._18]; + public bool LockandLoad() => v[Mask._19]; + + // Death Knight + public bool BloodTap() => v[Mask._10]; + public bool HornofWinter() => v[Mask._11]; + public bool IceboundFortitude() => v[Mask._12]; + public bool PathofFrost() => v[Mask._13]; + public bool AntiMagicShell() => v[Mask._14]; + public bool ArmyoftheDead() => v[Mask._15]; + public bool VampiricBlood() => v[Mask._16]; + public bool DancingRuneWeapon() => v[Mask._17]; + public bool UnbreakableArmor() => v[Mask._18]; + public bool BoneShield() => v[Mask._19]; + public bool SummonGargoyle() => v[Mask._20]; + public bool FreezingFog() => v[Mask._21]; } \ No newline at end of file diff --git a/Core/AddonComponent/CombatLog.cs b/Core/AddonComponent/CombatLog.cs index c23e9181d..1684a2ca8 100644 --- a/Core/AddonComponent/CombatLog.cs +++ b/Core/AddonComponent/CombatLog.cs @@ -1,92 +1,91 @@ using System; using System.Collections.Generic; -namespace Core +namespace Core; + +public sealed class CombatLog { - public sealed class CombatLog - { - private bool wasInCombat; + private bool wasInCombat; - public event Action? KillCredit; - public event Action? TargetEvade; + public event Action? KillCredit; + public event Action? TargetEvade; - public HashSet DamageDone { get; } = new(); - public HashSet DamageTaken { get; } = new(); + public HashSet DamageDone { get; } = new(); + public HashSet DamageTaken { get; } = new(); - public RecordInt DamageDoneGuid { get; } - public RecordInt DamageTakenGuid { get; } - public RecordInt DeadGuid { get; } + public RecordInt DamageDoneGuid { get; } + public RecordInt DamageTakenGuid { get; } + public RecordInt DeadGuid { get; } - public RecordInt TargetMissType { get; } - public RecordInt TargetDodge { get; } + public RecordInt TargetMissType { get; } + public RecordInt TargetDodge { get; } - public CombatLog(int cDamageDone, int cDamageTaken, int cDead, int cTargetMiss) - { - DamageDoneGuid = new RecordInt(cDamageDone); - DamageTakenGuid = new RecordInt(cDamageTaken); - DeadGuid = new RecordInt(cDead); + public CombatLog(int cDamageDone, int cDamageTaken, int cDead, int cTargetMiss) + { + DamageDoneGuid = new RecordInt(cDamageDone); + DamageTakenGuid = new RecordInt(cDamageTaken); + DeadGuid = new RecordInt(cDead); - TargetMissType = new(cTargetMiss); - TargetDodge = new(cTargetMiss); - } + TargetMissType = new(cTargetMiss); + TargetDodge = new(cTargetMiss); + } - public void Reset() - { - wasInCombat = false; + public void Reset() + { + wasInCombat = false; - DamageDone.Clear(); - DamageTaken.Clear(); + DamageDone.Clear(); + DamageTaken.Clear(); - DamageDoneGuid.Reset(); - DamageTakenGuid.Reset(); - DeadGuid.Reset(); + DamageDoneGuid.Reset(); + DamageTakenGuid.Reset(); + DeadGuid.Reset(); - TargetMissType.Reset(); - TargetDodge.Reset(); - } + TargetMissType.Reset(); + TargetDodge.Reset(); + } - public void Update(IAddonDataProvider reader, bool playerInCombat) + public void Update(IAddonDataProvider reader, bool playerInCombat) + { + if (TargetMissType.Updated(reader)) { - if (TargetMissType.Updated(reader)) + switch ((MissType)TargetMissType.Value) { - switch ((MissType)TargetMissType.Value) - { - case MissType.DODGE: - TargetDodge.UpdateTime(); - break; - case MissType.EVADE: - TargetEvade?.Invoke(); - break; - } - } - - if (playerInCombat && DamageTakenGuid.Updated(reader) && DamageTakenGuid.Value > 0) - { - DamageTaken.Add(DamageTakenGuid.Value); + case MissType.DODGE: + TargetDodge.UpdateTime(); + break; + case MissType.EVADE: + TargetEvade?.Invoke(); + break; } + } - if (playerInCombat && DamageDoneGuid.Updated(reader) && DamageDoneGuid.Value > 0) - { - DamageDone.Add(DamageDoneGuid.Value); - } + if (playerInCombat && DamageTakenGuid.Updated(reader) && DamageTakenGuid.Value > 0) + { + DamageTaken.Add(DamageTakenGuid.Value); + } - if (DeadGuid.Updated(reader) && DeadGuid.Value > 0) - { - int deadGuid = DeadGuid.Value; - DamageDone.Remove(deadGuid); - DamageTaken.Remove(deadGuid); + if (playerInCombat && DamageDoneGuid.Updated(reader) && DamageDoneGuid.Value > 0) + { + DamageDone.Add(DamageDoneGuid.Value); + } - KillCredit?.Invoke(); - } + if (DeadGuid.Updated(reader) && DeadGuid.Value > 0) + { + int deadGuid = DeadGuid.Value; + DamageDone.Remove(deadGuid); + DamageTaken.Remove(deadGuid); - if (wasInCombat && !playerInCombat) - { - // left combat - DamageTaken.Clear(); - DamageDone.Clear(); - } + KillCredit?.Invoke(); + } - wasInCombat = playerInCombat; + if (wasInCombat && !playerInCombat) + { + // left combat + DamageTaken.Clear(); + DamageDone.Clear(); } + + wasInCombat = playerInCombat; } } \ No newline at end of file diff --git a/Core/AddonComponent/Form.cs b/Core/AddonComponent/Form.cs index 5f3e8b397..23479081f 100644 --- a/Core/AddonComponent/Form.cs +++ b/Core/AddonComponent/Form.cs @@ -1,74 +1,72 @@ -namespace Core +namespace Core; + +public enum Form { - public enum Form - { - None = 0, + None = 0, - Druid_Bear = 1, - Druid_Aquatic = 2, - Druid_Cat = 3, - Druid_Travel = 4, - Druid_Moonkin = 5, - Druid_Flight = 6, - Druid_Cat_Prowl = 7, + Druid_Bear = 1, + Druid_Aquatic = 2, + Druid_Cat = 3, + Druid_Travel = 4, + Druid_Moonkin = 5, + Druid_Flight = 6, + Druid_Cat_Prowl = 7, - Priest_Shadowform = 8, + Priest_Shadowform = 8, - Rogue_Stealth = 9, - Rogue_Vanish = 10, + Rogue_Stealth = 9, + Rogue_Vanish = 10, - Shaman_GhostWolf = 11, + Shaman_GhostWolf = 11, - Warrior_BattleStance = 12, - Warrior_DefensiveStance = 13, - Warrior_BerserkerStance = 14, + Warrior_BattleStance = 12, + Warrior_DefensiveStance = 13, + Warrior_BerserkerStance = 14, - Paladin_Devotion_Aura = 15, - Paladin_Retribution_Aura = 16, - Paladin_Concentration_Aura = 17, - Paladin_Shadow_Resistance_Aura = 18, - Paladin_Frost_Resistance_Aura = 19, - Paladin_Fire_Resistance_Aura = 20, - Paladin_Sanctity_Aura = 21, - Paladin_Crusader_Aura = 22, + Paladin_Devotion_Aura = 15, + Paladin_Retribution_Aura = 16, + Paladin_Concentration_Aura = 17, + Paladin_Shadow_Resistance_Aura = 18, + Paladin_Frost_Resistance_Aura = 19, + Paladin_Fire_Resistance_Aura = 20, + Paladin_Sanctity_Aura = 21, + Paladin_Crusader_Aura = 22, - DeathKnight_Blood_Presence = 23, - DeathKnight_Frost_Presence = 24, - DeathKnight_Unholy_Presence = 25, - } + DeathKnight_Blood_Presence = 23, + DeathKnight_Frost_Presence = 24, + DeathKnight_Unholy_Presence = 25, +} - public static class Form_Extension +public static class Form_Extension +{ + public static string ToStringF(this Form value) => value switch { - public static string ToStringF(this Form value) => value switch - { - Form.None => nameof(Form.None), - Form.Druid_Bear => nameof(Form.Druid_Bear), - Form.Druid_Aquatic => nameof(Form.Druid_Aquatic), - Form.Druid_Cat => nameof(Form.Druid_Cat), - Form.Druid_Travel => nameof(Form.Druid_Travel), - Form.Druid_Moonkin => nameof(Form.Druid_Moonkin), - Form.Druid_Flight => nameof(Form.Druid_Flight), - Form.Druid_Cat_Prowl => nameof(Form.Druid_Cat_Prowl), - Form.Priest_Shadowform => nameof(Form.Priest_Shadowform), - Form.Rogue_Stealth => nameof(Form.Rogue_Stealth), - Form.Rogue_Vanish => nameof(Form.Rogue_Vanish), - Form.Shaman_GhostWolf => nameof(Form.Shaman_GhostWolf), - Form.Warrior_BattleStance => nameof(Form.Warrior_BattleStance), - Form.Warrior_DefensiveStance => nameof(Form.Warrior_DefensiveStance), - Form.Warrior_BerserkerStance => nameof(Form.Warrior_BerserkerStance), - Form.Paladin_Devotion_Aura => nameof(Form.Paladin_Devotion_Aura), - Form.Paladin_Retribution_Aura => nameof(Form.Paladin_Retribution_Aura), - Form.Paladin_Concentration_Aura => nameof(Form.Paladin_Concentration_Aura), - Form.Paladin_Shadow_Resistance_Aura => nameof(Form.Paladin_Shadow_Resistance_Aura), - Form.Paladin_Frost_Resistance_Aura => nameof(Form.Paladin_Frost_Resistance_Aura), - Form.Paladin_Fire_Resistance_Aura => nameof(Form.Paladin_Fire_Resistance_Aura), - Form.Paladin_Sanctity_Aura => nameof(Form.Paladin_Sanctity_Aura), - Form.Paladin_Crusader_Aura => nameof(Form.Paladin_Crusader_Aura), - Form.DeathKnight_Blood_Presence => nameof(Form.DeathKnight_Blood_Presence), - Form.DeathKnight_Frost_Presence => nameof(Form.DeathKnight_Frost_Presence), - Form.DeathKnight_Unholy_Presence => nameof(Form.DeathKnight_Unholy_Presence), - _ => nameof(Form.None) - }; - } - + Form.None => nameof(Form.None), + Form.Druid_Bear => nameof(Form.Druid_Bear), + Form.Druid_Aquatic => nameof(Form.Druid_Aquatic), + Form.Druid_Cat => nameof(Form.Druid_Cat), + Form.Druid_Travel => nameof(Form.Druid_Travel), + Form.Druid_Moonkin => nameof(Form.Druid_Moonkin), + Form.Druid_Flight => nameof(Form.Druid_Flight), + Form.Druid_Cat_Prowl => nameof(Form.Druid_Cat_Prowl), + Form.Priest_Shadowform => nameof(Form.Priest_Shadowform), + Form.Rogue_Stealth => nameof(Form.Rogue_Stealth), + Form.Rogue_Vanish => nameof(Form.Rogue_Vanish), + Form.Shaman_GhostWolf => nameof(Form.Shaman_GhostWolf), + Form.Warrior_BattleStance => nameof(Form.Warrior_BattleStance), + Form.Warrior_DefensiveStance => nameof(Form.Warrior_DefensiveStance), + Form.Warrior_BerserkerStance => nameof(Form.Warrior_BerserkerStance), + Form.Paladin_Devotion_Aura => nameof(Form.Paladin_Devotion_Aura), + Form.Paladin_Retribution_Aura => nameof(Form.Paladin_Retribution_Aura), + Form.Paladin_Concentration_Aura => nameof(Form.Paladin_Concentration_Aura), + Form.Paladin_Shadow_Resistance_Aura => nameof(Form.Paladin_Shadow_Resistance_Aura), + Form.Paladin_Frost_Resistance_Aura => nameof(Form.Paladin_Frost_Resistance_Aura), + Form.Paladin_Fire_Resistance_Aura => nameof(Form.Paladin_Fire_Resistance_Aura), + Form.Paladin_Sanctity_Aura => nameof(Form.Paladin_Sanctity_Aura), + Form.Paladin_Crusader_Aura => nameof(Form.Paladin_Crusader_Aura), + Form.DeathKnight_Blood_Presence => nameof(Form.DeathKnight_Blood_Presence), + Form.DeathKnight_Frost_Presence => nameof(Form.DeathKnight_Frost_Presence), + Form.DeathKnight_Unholy_Presence => nameof(Form.DeathKnight_Unholy_Presence), + _ => nameof(Form.None) + }; } diff --git a/Core/AddonComponent/LevelTracker.cs b/Core/AddonComponent/LevelTracker.cs index c324dcb7a..92249cfdd 100644 --- a/Core/AddonComponent/LevelTracker.cs +++ b/Core/AddonComponent/LevelTracker.cs @@ -1,82 +1,81 @@ using System; -namespace Core +namespace Core; + +public sealed class LevelTracker : IDisposable { - public sealed class LevelTracker : IDisposable - { - private readonly AddonReader addonReader; - private readonly PlayerReader playerReader; + private readonly AddonReader addonReader; + private readonly PlayerReader playerReader; - private DateTime levelStartTime = DateTime.UtcNow; - private int levelStartXP; + private DateTime levelStartTime = DateTime.UtcNow; + private int levelStartXP; - public TimeSpan TimeToLevel { get; private set; } = TimeSpan.Zero; - public DateTime PredictedLevelUpTime { get; private set; } = DateTime.MaxValue; + public TimeSpan TimeToLevel { get; private set; } = TimeSpan.Zero; + public DateTime PredictedLevelUpTime { get; private set; } = DateTime.MaxValue; - public int MobsKilled { get; private set; } - public int Death { get; private set; } + public int MobsKilled { get; private set; } + public int Death { get; private set; } - public LevelTracker(AddonReader addonReader) - { - this.addonReader = addonReader; - this.playerReader = addonReader.PlayerReader; + public LevelTracker(AddonReader addonReader) + { + this.addonReader = addonReader; + this.playerReader = addonReader.PlayerReader; - playerReader.Level.Changed += PlayerLevel_Changed; - playerReader.PlayerXp.Changed += PlayerExp_Changed; + playerReader.Level.Changed += PlayerLevel_Changed; + playerReader.PlayerXp.Changed += PlayerExp_Changed; - addonReader.PlayerDeath += OnPlayerDeath; - addonReader.CombatLog.KillCredit += OnKillCredit; - } + addonReader.PlayerDeath += OnPlayerDeath; + addonReader.CombatLog.KillCredit += OnKillCredit; + } - public void Dispose() - { - playerReader.Level.Changed -= PlayerLevel_Changed; - playerReader.PlayerXp.Changed -= PlayerExp_Changed; - addonReader.PlayerDeath -= OnPlayerDeath; - addonReader.CombatLog.KillCredit -= OnKillCredit; - } + public void Dispose() + { + playerReader.Level.Changed -= PlayerLevel_Changed; + playerReader.PlayerXp.Changed -= PlayerExp_Changed; + addonReader.PlayerDeath -= OnPlayerDeath; + addonReader.CombatLog.KillCredit -= OnKillCredit; + } - public void Reset() - { - MobsKilled = 0; - Death = 0; + public void Reset() + { + MobsKilled = 0; + Death = 0; - UpdateExpPerHour(); - } + UpdateExpPerHour(); + } - private void PlayerExp_Changed() - { - UpdateExpPerHour(); - } + private void PlayerExp_Changed() + { + UpdateExpPerHour(); + } - private void PlayerLevel_Changed() - { - levelStartTime = DateTime.UtcNow; - levelStartXP = playerReader.PlayerXp.Value; - } + private void PlayerLevel_Changed() + { + levelStartTime = DateTime.UtcNow; + levelStartXP = playerReader.PlayerXp.Value; + } - private void OnPlayerDeath() - { - Death++; - } + private void OnPlayerDeath() + { + Death++; + } - private void OnKillCredit() - { - MobsKilled++; - } + private void OnKillCredit() + { + MobsKilled++; + } - public void UpdateExpPerHour() - { - double runningSeconds = (DateTime.UtcNow - levelStartTime).TotalSeconds; - double xpPerSecond = (playerReader.PlayerXp.Value - levelStartXP) / runningSeconds; - double secondsLeft = (playerReader.PlayerMaxXp - playerReader.PlayerXp.Value) / xpPerSecond; + public void UpdateExpPerHour() + { + double runningSeconds = (DateTime.UtcNow - levelStartTime).TotalSeconds; + double xpPerSecond = (playerReader.PlayerXp.Value - levelStartXP) / runningSeconds; + double secondsLeft = (playerReader.PlayerMaxXp - playerReader.PlayerXp.Value) / xpPerSecond; - TimeToLevel = xpPerSecond > 0 ? new TimeSpan(0, 0, (int)secondsLeft) : TimeSpan.Zero; + TimeToLevel = xpPerSecond > 0 ? new TimeSpan(0, 0, (int)secondsLeft) : TimeSpan.Zero; - if (secondsLeft > 0 && secondsLeft < 60 * 60 * 10) - { - PredictedLevelUpTime = DateTime.UtcNow.AddSeconds(secondsLeft).ToLocalTime(); - } + if (secondsLeft > 0 && secondsLeft < 60 * 60 * 10) + { + PredictedLevelUpTime = DateTime.UtcNow.AddSeconds(secondsLeft).ToLocalTime(); } } } \ No newline at end of file diff --git a/Core/AddonComponent/Mask.cs b/Core/AddonComponent/Mask.cs index 797ee29fa..843a9cf27 100644 --- a/Core/AddonComponent/Mask.cs +++ b/Core/AddonComponent/Mask.cs @@ -1,76 +1,75 @@ using static System.Collections.Specialized.BitVector32; -namespace Core +namespace Core; + +public static class Mask { - public static class Mask + public static readonly int[] M = new int[32] { - public static readonly int[] M = new int[32] - { - CreateMask(), - 1 << 1, - 1 << 2, - 1 << 3, - 1 << 4, - 1 << 5, - 1 << 6, - 1 << 7, - 1 << 8, - 1 << 9, - 1 << 10, - 1 << 11, - 1 << 12, - 1 << 13, - 1 << 14, - 1 << 15, - 1 << 16, - 1 << 17, - 1 << 18, - 1 << 19, - 1 << 20, - 1 << 21, - 1 << 22, - 1 << 23, - 1 << 24, - 1 << 25, - 1 << 26, - 1 << 27, - 1 << 28, - 1 << 29, - 1 << 30, - 1 << 31, - }; + CreateMask(), + 1 << 1, + 1 << 2, + 1 << 3, + 1 << 4, + 1 << 5, + 1 << 6, + 1 << 7, + 1 << 8, + 1 << 9, + 1 << 10, + 1 << 11, + 1 << 12, + 1 << 13, + 1 << 14, + 1 << 15, + 1 << 16, + 1 << 17, + 1 << 18, + 1 << 19, + 1 << 20, + 1 << 21, + 1 << 22, + 1 << 23, + 1 << 24, + 1 << 25, + 1 << 26, + 1 << 27, + 1 << 28, + 1 << 29, + 1 << 30, + 1 << 31, + }; - public const int _0 = 1; - public const int _1 = 1 << 1; - public const int _2 = 1 << 2; - public const int _3 = 1 << 3; - public const int _4 = 1 << 4; - public const int _5 = 1 << 5; - public const int _6 = 1 << 6; - public const int _7 = 1 << 7; - public const int _8 = 1 << 8; - public const int _9 = 1 << 9; - public const int _10 = 1 << 10; - public const int _11 = 1 << 11; - public const int _12 = 1 << 12; - public const int _13 = 1 << 13; - public const int _14 = 1 << 14; - public const int _15 = 1 << 15; - public const int _16 = 1 << 16; - public const int _17 = 1 << 17; - public const int _18 = 1 << 18; - public const int _19 = 1 << 19; - public const int _20 = 1 << 20; - public const int _21 = 1 << 21; - public const int _22 = 1 << 22; - public const int _23 = 1 << 23; - public const int _24 = 1 << 24; - public const int _25 = 1 << 25; - public const int _26 = 1 << 26; - public const int _27 = 1 << 27; - public const int _28 = 1 << 28; - public const int _29 = 1 << 29; - public const int _30 = 1 << 30; - public const int _31 = 1 << 31; - } + public const int _0 = 1; + public const int _1 = 1 << 1; + public const int _2 = 1 << 2; + public const int _3 = 1 << 3; + public const int _4 = 1 << 4; + public const int _5 = 1 << 5; + public const int _6 = 1 << 6; + public const int _7 = 1 << 7; + public const int _8 = 1 << 8; + public const int _9 = 1 << 9; + public const int _10 = 1 << 10; + public const int _11 = 1 << 11; + public const int _12 = 1 << 12; + public const int _13 = 1 << 13; + public const int _14 = 1 << 14; + public const int _15 = 1 << 15; + public const int _16 = 1 << 16; + public const int _17 = 1 << 17; + public const int _18 = 1 << 18; + public const int _19 = 1 << 19; + public const int _20 = 1 << 20; + public const int _21 = 1 << 21; + public const int _22 = 1 << 22; + public const int _23 = 1 << 23; + public const int _24 = 1 << 24; + public const int _25 = 1 << 25; + public const int _26 = 1 << 26; + public const int _27 = 1 << 27; + public const int _28 = 1 << 28; + public const int _29 = 1 << 29; + public const int _30 = 1 << 30; + public const int _31 = 1 << 31; } diff --git a/Core/AddonComponent/MissType.cs b/Core/AddonComponent/MissType.cs index b4f5ee265..bb06869aa 100644 --- a/Core/AddonComponent/MissType.cs +++ b/Core/AddonComponent/MissType.cs @@ -1,36 +1,34 @@ -namespace Core +namespace Core; + +public enum MissType { - public enum MissType - { - NONE, - ABSORB, - BLOCK, - DEFLECT, - DODGE, - EVADE, - IMMUNE, - MISS, - PARRY, - REFLECT, - RESIST - } + NONE, + ABSORB, + BLOCK, + DEFLECT, + DODGE, + EVADE, + IMMUNE, + MISS, + PARRY, + REFLECT, + RESIST +} - public static class MissType_Extensions +public static class MissType_Extensions +{ + public static string ToStringF(this MissType value) => value switch { - public static string ToStringF(this MissType value) => value switch - { - MissType.ABSORB => nameof(MissType.ABSORB), - MissType.BLOCK => nameof(MissType.BLOCK), - MissType.DEFLECT => nameof(MissType.DEFLECT), - MissType.DODGE => nameof(MissType.DODGE), - MissType.EVADE => nameof(MissType.EVADE), - MissType.IMMUNE => nameof(MissType.IMMUNE), - MissType.MISS => nameof(MissType.MISS), - MissType.PARRY => nameof(MissType.PARRY), - MissType.REFLECT => nameof(MissType.REFLECT), - MissType.RESIST => nameof(MissType.RESIST), - _ => nameof(MissType.NONE) - }; - } - + MissType.ABSORB => nameof(MissType.ABSORB), + MissType.BLOCK => nameof(MissType.BLOCK), + MissType.DEFLECT => nameof(MissType.DEFLECT), + MissType.DODGE => nameof(MissType.DODGE), + MissType.EVADE => nameof(MissType.EVADE), + MissType.IMMUNE => nameof(MissType.IMMUNE), + MissType.MISS => nameof(MissType.MISS), + MissType.PARRY => nameof(MissType.PARRY), + MissType.REFLECT => nameof(MissType.REFLECT), + MissType.RESIST => nameof(MissType.RESIST), + _ => nameof(MissType.NONE) + }; } diff --git a/Core/AddonComponent/PowerType.cs b/Core/AddonComponent/PowerType.cs index d9107e374..b54805474 100644 --- a/Core/AddonComponent/PowerType.cs +++ b/Core/AddonComponent/PowerType.cs @@ -1,67 +1,66 @@ -namespace Core +namespace Core; + +// offset by 2 to avoid negative numbers +public enum PowerType { - // offset by 2 to avoid negative numbers - public enum PowerType - { - HealthCost, - None, - Mana, - Rage, - Focus, - Energy, - Happiness, - Runes, - RunicPower, - SoulShards, - LunarPower, - HolyPower, - Alternate, - Maelstrom, - Chi, - Insanity, - ComboPoints, - Obsolete2, - ArcaneCharges, - Fury, - Pain, - Essence, - RuneBlood, - RuneFrost, - RuneUnholy, - NumPowerTypes - } + HealthCost, + None, + Mana, + Rage, + Focus, + Energy, + Happiness, + Runes, + RunicPower, + SoulShards, + LunarPower, + HolyPower, + Alternate, + Maelstrom, + Chi, + Insanity, + ComboPoints, + Obsolete2, + ArcaneCharges, + Fury, + Pain, + Essence, + RuneBlood, + RuneFrost, + RuneUnholy, + NumPowerTypes +} - public static class PowerType_Extension +public static class PowerType_Extension +{ + public static string ToStringF(this PowerType value) => value switch { - public static string ToStringF(this PowerType value) => value switch - { - PowerType.HealthCost => nameof(PowerType.HealthCost), - PowerType.None => nameof(PowerType.None), - PowerType.Mana => nameof(PowerType.Mana), - PowerType.Rage => nameof(PowerType.Rage), - PowerType.Focus => nameof(PowerType.Focus), - PowerType.Energy => nameof(PowerType.Energy), - PowerType.Happiness => nameof(PowerType.Happiness), - PowerType.Runes => nameof(PowerType.Runes), - PowerType.RunicPower => nameof(PowerType.RunicPower), - PowerType.SoulShards => nameof(PowerType.SoulShards), - PowerType.LunarPower => nameof(PowerType.LunarPower), - PowerType.HolyPower => nameof(PowerType.HolyPower), - PowerType.Alternate => nameof(PowerType.Alternate), - PowerType.Maelstrom => nameof(PowerType.Maelstrom), - PowerType.Chi => nameof(PowerType.Chi), - PowerType.Insanity => nameof(PowerType.Insanity), - PowerType.ComboPoints => nameof(PowerType.ComboPoints), - PowerType.Obsolete2 => nameof(PowerType.Obsolete2), - PowerType.ArcaneCharges => nameof(PowerType.ArcaneCharges), - PowerType.Fury => nameof(PowerType.Fury), - PowerType.Pain => nameof(PowerType.Pain), - PowerType.Essence => nameof(PowerType.Essence), - PowerType.RuneBlood => nameof(PowerType.RuneBlood), - PowerType.RuneFrost => nameof(PowerType.RuneFrost), - PowerType.RuneUnholy => nameof(PowerType.RuneUnholy), - PowerType.NumPowerTypes => nameof(PowerType.NumPowerTypes), - _ => nameof(PowerType.None), - }; - } + PowerType.HealthCost => nameof(PowerType.HealthCost), + PowerType.None => nameof(PowerType.None), + PowerType.Mana => nameof(PowerType.Mana), + PowerType.Rage => nameof(PowerType.Rage), + PowerType.Focus => nameof(PowerType.Focus), + PowerType.Energy => nameof(PowerType.Energy), + PowerType.Happiness => nameof(PowerType.Happiness), + PowerType.Runes => nameof(PowerType.Runes), + PowerType.RunicPower => nameof(PowerType.RunicPower), + PowerType.SoulShards => nameof(PowerType.SoulShards), + PowerType.LunarPower => nameof(PowerType.LunarPower), + PowerType.HolyPower => nameof(PowerType.HolyPower), + PowerType.Alternate => nameof(PowerType.Alternate), + PowerType.Maelstrom => nameof(PowerType.Maelstrom), + PowerType.Chi => nameof(PowerType.Chi), + PowerType.Insanity => nameof(PowerType.Insanity), + PowerType.ComboPoints => nameof(PowerType.ComboPoints), + PowerType.Obsolete2 => nameof(PowerType.Obsolete2), + PowerType.ArcaneCharges => nameof(PowerType.ArcaneCharges), + PowerType.Fury => nameof(PowerType.Fury), + PowerType.Pain => nameof(PowerType.Pain), + PowerType.Essence => nameof(PowerType.Essence), + PowerType.RuneBlood => nameof(PowerType.RuneBlood), + PowerType.RuneFrost => nameof(PowerType.RuneFrost), + PowerType.RuneUnholy => nameof(PowerType.RuneUnholy), + PowerType.NumPowerTypes => nameof(PowerType.NumPowerTypes), + _ => nameof(PowerType.None), + }; } diff --git a/Core/AddonComponent/RecordInt.cs b/Core/AddonComponent/RecordInt.cs index e261a24e2..71ca6fb59 100644 --- a/Core/AddonComponent/RecordInt.cs +++ b/Core/AddonComponent/RecordInt.cs @@ -1,74 +1,73 @@ using System; -namespace Core -{ - public sealed class RecordInt - { - private readonly int cell; +namespace Core; - public int Value { private set; get; } - - public int _Value() => Value; +public sealed class RecordInt +{ + private readonly int cell; - public DateTime LastChanged { private set; get; } + public int Value { private set; get; } - public int ElapsedMs() => (int)(DateTime.UtcNow - LastChanged).TotalMilliseconds; + public int _Value() => Value; - public event Action? Changed; + public DateTime LastChanged { private set; get; } - public RecordInt(int cell) - { - this.cell = cell; - } + public int ElapsedMs() => (int)(DateTime.UtcNow - LastChanged).TotalMilliseconds; - public bool Updated(IAddonDataProvider reader) - { - int temp = Value; - Value = reader.GetInt(cell); + public event Action? Changed; - if (temp != Value) - { - Changed?.Invoke(); - LastChanged = DateTime.UtcNow; - return true; - } + public RecordInt(int cell) + { + this.cell = cell; + } - return false; - } + public bool Updated(IAddonDataProvider reader) + { + int temp = Value; + Value = reader.GetInt(cell); - public bool UpdatedNoEvent(IAddonDataProvider reader) + if (temp != Value) { - int temp = Value; - Value = reader.GetInt(cell); - return temp != Value; + Changed?.Invoke(); + LastChanged = DateTime.UtcNow; + return true; } - public void Update(IAddonDataProvider reader) - { - int temp = Value; - Value = reader.GetInt(cell); - - if (temp != Value) - { - Changed?.Invoke(); - LastChanged = DateTime.UtcNow; - } - } + return false; + } - public void UpdateTime() + public bool UpdatedNoEvent(IAddonDataProvider reader) + { + int temp = Value; + Value = reader.GetInt(cell); + return temp != Value; + } + + public void Update(IAddonDataProvider reader) + { + int temp = Value; + Value = reader.GetInt(cell); + + if (temp != Value) { + Changed?.Invoke(); LastChanged = DateTime.UtcNow; } + } - public void Reset() - { - Value = 0; - LastChanged = default; - } + public void UpdateTime() + { + LastChanged = DateTime.UtcNow; + } - public void ForceUpdate(int value) - { - Value = value; - } + public void Reset() + { + Value = 0; + LastChanged = default; + } + + public void ForceUpdate(int value) + { + Value = value; } } \ No newline at end of file diff --git a/Core/AddonComponent/SchoolMask.cs b/Core/AddonComponent/SchoolMask.cs index 0ba969021..c72bd3fea 100644 --- a/Core/AddonComponent/SchoolMask.cs +++ b/Core/AddonComponent/SchoolMask.cs @@ -1,30 +1,29 @@ -namespace Core +namespace Core; + +public enum SchoolMask { - public enum SchoolMask - { - None = 0, - Physical = 1, - Holy = 2, - Fire = 4, - Nature = 8, - Frost = 16, - Shadow = 32, - Arcane = 64, - } + None = 0, + Physical = 1, + Holy = 2, + Fire = 4, + Nature = 8, + Frost = 16, + Shadow = 32, + Arcane = 64, +} - public static class SchoolMask_Extension +public static class SchoolMask_Extension +{ + public static string ToStringF(this SchoolMask value) => value switch { - public static string ToStringF(this SchoolMask value) => value switch - { - SchoolMask.None => nameof(SchoolMask.None), - SchoolMask.Physical => nameof(SchoolMask.Physical), - SchoolMask.Holy => nameof(SchoolMask.Holy), - SchoolMask.Fire => nameof(SchoolMask.Fire), - SchoolMask.Nature => nameof(SchoolMask.Nature), - SchoolMask.Frost => nameof(SchoolMask.Frost), - SchoolMask.Shadow => nameof(SchoolMask.Shadow), - SchoolMask.Arcane => nameof(SchoolMask.Arcane), - _ => nameof(SchoolMask.None) - }; - } + SchoolMask.None => nameof(SchoolMask.None), + SchoolMask.Physical => nameof(SchoolMask.Physical), + SchoolMask.Holy => nameof(SchoolMask.Holy), + SchoolMask.Fire => nameof(SchoolMask.Fire), + SchoolMask.Nature => nameof(SchoolMask.Nature), + SchoolMask.Frost => nameof(SchoolMask.Frost), + SchoolMask.Shadow => nameof(SchoolMask.Shadow), + SchoolMask.Arcane => nameof(SchoolMask.Arcane), + _ => nameof(SchoolMask.None) + }; } \ No newline at end of file diff --git a/Core/AddonComponent/SpellInRange.cs b/Core/AddonComponent/SpellInRange.cs index 93a21d2a0..656415a1d 100644 --- a/Core/AddonComponent/SpellInRange.cs +++ b/Core/AddonComponent/SpellInRange.cs @@ -1,128 +1,127 @@ using System.Collections.Specialized; -namespace Core +namespace Core; + +public sealed class SpellInRange { - public sealed class SpellInRange + private readonly int cell; + + public bool this[int index] => b[index]; + + private BitVector32 b; + + public SpellInRange(int cell) + { + this.cell = cell; + } + + public void Update(IAddonDataProvider reader) { - private readonly int cell; - - public bool this[int index] => b[index]; - - private BitVector32 b; - - public SpellInRange(int cell) - { - this.cell = cell; - } - - public void Update(IAddonDataProvider reader) - { - b = new(reader.GetInt(cell)); - } - - // Warrior - public bool Warrior_Charge => b[Mask._0]; - public bool Warrior_Rend => b[Mask._1]; - public bool Warrior_ShootGun => b[Mask._2]; - public bool Warrior_Throw => b[Mask._3]; - - // Rogue - public bool Rogue_SinisterStrike => b[Mask._0]; - public bool Rogue_Throw => b[Mask._1]; - public bool Rogue_ShootGun => b[Mask._2]; - - // Priest - public bool Priest_ShadowWordPain => b[Mask._0]; - public bool Priest_Shoot => b[Mask._1]; - public bool Priest_MindFlay => b[Mask._2]; - public bool Priest_MindBlast => b[Mask._3]; - public bool Priest_Smite => b[Mask._4]; - - // Druid - public bool Druid_Wrath => b[Mask._0]; - public bool Druid_Bash => b[Mask._1]; - public bool Druid_Rip => b[Mask._2]; - public bool Druid_Maul => b[Mask._3]; - - //Paladin - public bool Paladin_Judgement => b[Mask._0]; - - //Mage - public bool Mage_Fireball => b[Mask._0]; - public bool Mage_Shoot => b[Mask._1]; - public bool Mage_Pyroblast => b[Mask._2]; - public bool Mage_Frostbolt => b[Mask._3]; - public bool Mage_Fireblast => b[Mask._4]; - - //Hunter - public bool Hunter_RaptorStrike => b[Mask._0]; - public bool Hunter_AutoShoot => b[Mask._1]; - public bool Hunter_SerpentSting => b[Mask._2]; - public bool Hunter_FeedPet => b[Mask._3]; - - // Warlock - public bool Warlock_ShadowBolt => b[Mask._0]; - public bool Warlock_Shoot => b[Mask._1]; - public bool Warlock_HealthFunnel => b[Mask._2]; - - // Shaman - public bool Shaman_LightningBolt => b[Mask._0]; - public bool Shaman_EarthShock => b[Mask._1]; - - // Death Knight - public bool DeathKnight_IcyTouch => b[Mask._0]; - public bool DeathKnight_DeathCoil => b[Mask._1]; - public bool DeathKnight_DeathGrip => b[Mask._2]; - public bool DeathKnight_DarkCommand => b[Mask._3]; - public bool DeathKnight_RaiseDead => b[Mask._4]; - - - // Unit based Non spell Ranges - public bool FocusTarget_Inspect => b[Mask._12]; - public bool FocusTarget_Trade => b[Mask._13]; - public bool FocusTarget_Duel => b[Mask._14]; - - public bool Focus_Inspect => b[Mask._15]; - public bool Focus_Trade => b[Mask._16]; - public bool Focus_Duel => b[Mask._17]; - - public bool Pet_Inspect => b[Mask._18]; - public bool Pet_Trade => b[Mask._19]; - public bool Pet_Duel => b[Mask._20]; - - public bool Target_Inspect => b[Mask._21]; - public bool Target_Trade => b[Mask._22]; - public bool Target_Duel => b[Mask._23]; - - public bool WithinPullRange(PlayerReader playerReader, UnitClass @class) => @class switch - { - UnitClass.Warrior => (playerReader.Level.Value >= 4 && Warrior_Charge) || playerReader.IsInMeleeRange(), - UnitClass.Rogue => Rogue_Throw || Rogue_SinisterStrike, - UnitClass.Priest => Priest_Smite, - UnitClass.Druid => Druid_Wrath, - UnitClass.Paladin => (playerReader.Level.Value >= 4 && Paladin_Judgement) || playerReader.IsInMeleeRange() || - (playerReader.Level.Value >= 20 && playerReader.MinRange() <= 20 && playerReader.MaxRange() <= 25), - UnitClass.Mage => (playerReader.Level.Value >= 4 && Mage_Frostbolt) || Mage_Fireball, - UnitClass.Hunter => (playerReader.Level.Value >= 4 && Hunter_SerpentSting) || Hunter_AutoShoot || playerReader.IsInMeleeRange(), - UnitClass.Warlock => Warlock_ShadowBolt, - UnitClass.Shaman => (playerReader.Level.Value >= 4 && Shaman_EarthShock) || Shaman_LightningBolt, - UnitClass.DeathKnight => DeathKnight_DeathGrip, - _ => true - }; - - public bool WithinCombatRange(PlayerReader playerReader, UnitClass @class) => @class switch - { - UnitClass.Warrior => (playerReader.Level.Value >= 4 && Warrior_Rend) || playerReader.IsInMeleeRange(), - UnitClass.Rogue => Rogue_SinisterStrike, - UnitClass.Priest => Priest_Smite, - UnitClass.Druid => Druid_Wrath || playerReader.IsInMeleeRange(), - UnitClass.Paladin => (playerReader.Level.Value >= 4 && Paladin_Judgement) || playerReader.IsInMeleeRange(), - UnitClass.Mage => Mage_Frostbolt || Mage_Fireball, - UnitClass.Hunter => (playerReader.Level.Value >= 4 && Hunter_SerpentSting) || Hunter_AutoShoot || playerReader.IsInMeleeRange(), - UnitClass.Warlock => Warlock_ShadowBolt, - UnitClass.Shaman => Shaman_LightningBolt, - UnitClass.DeathKnight => DeathKnight_IcyTouch, - _ => true - }; + b = new(reader.GetInt(cell)); } + + // Warrior + public bool Warrior_Charge => b[Mask._0]; + public bool Warrior_Rend => b[Mask._1]; + public bool Warrior_ShootGun => b[Mask._2]; + public bool Warrior_Throw => b[Mask._3]; + + // Rogue + public bool Rogue_SinisterStrike => b[Mask._0]; + public bool Rogue_Throw => b[Mask._1]; + public bool Rogue_ShootGun => b[Mask._2]; + + // Priest + public bool Priest_ShadowWordPain => b[Mask._0]; + public bool Priest_Shoot => b[Mask._1]; + public bool Priest_MindFlay => b[Mask._2]; + public bool Priest_MindBlast => b[Mask._3]; + public bool Priest_Smite => b[Mask._4]; + + // Druid + public bool Druid_Wrath => b[Mask._0]; + public bool Druid_Bash => b[Mask._1]; + public bool Druid_Rip => b[Mask._2]; + public bool Druid_Maul => b[Mask._3]; + + //Paladin + public bool Paladin_Judgement => b[Mask._0]; + + //Mage + public bool Mage_Fireball => b[Mask._0]; + public bool Mage_Shoot => b[Mask._1]; + public bool Mage_Pyroblast => b[Mask._2]; + public bool Mage_Frostbolt => b[Mask._3]; + public bool Mage_Fireblast => b[Mask._4]; + + //Hunter + public bool Hunter_RaptorStrike => b[Mask._0]; + public bool Hunter_AutoShoot => b[Mask._1]; + public bool Hunter_SerpentSting => b[Mask._2]; + public bool Hunter_FeedPet => b[Mask._3]; + + // Warlock + public bool Warlock_ShadowBolt => b[Mask._0]; + public bool Warlock_Shoot => b[Mask._1]; + public bool Warlock_HealthFunnel => b[Mask._2]; + + // Shaman + public bool Shaman_LightningBolt => b[Mask._0]; + public bool Shaman_EarthShock => b[Mask._1]; + + // Death Knight + public bool DeathKnight_IcyTouch => b[Mask._0]; + public bool DeathKnight_DeathCoil => b[Mask._1]; + public bool DeathKnight_DeathGrip => b[Mask._2]; + public bool DeathKnight_DarkCommand => b[Mask._3]; + public bool DeathKnight_RaiseDead => b[Mask._4]; + + + // Unit based Non spell Ranges + public bool FocusTarget_Inspect => b[Mask._12]; + public bool FocusTarget_Trade => b[Mask._13]; + public bool FocusTarget_Duel => b[Mask._14]; + + public bool Focus_Inspect => b[Mask._15]; + public bool Focus_Trade => b[Mask._16]; + public bool Focus_Duel => b[Mask._17]; + + public bool Pet_Inspect => b[Mask._18]; + public bool Pet_Trade => b[Mask._19]; + public bool Pet_Duel => b[Mask._20]; + + public bool Target_Inspect => b[Mask._21]; + public bool Target_Trade => b[Mask._22]; + public bool Target_Duel => b[Mask._23]; + + public bool WithinPullRange(PlayerReader playerReader, UnitClass @class) => @class switch + { + UnitClass.Warrior => (playerReader.Level.Value >= 4 && Warrior_Charge) || playerReader.IsInMeleeRange(), + UnitClass.Rogue => Rogue_Throw || Rogue_SinisterStrike, + UnitClass.Priest => Priest_Smite, + UnitClass.Druid => Druid_Wrath, + UnitClass.Paladin => (playerReader.Level.Value >= 4 && Paladin_Judgement) || playerReader.IsInMeleeRange() || + (playerReader.Level.Value >= 20 && playerReader.MinRange() <= 20 && playerReader.MaxRange() <= 25), + UnitClass.Mage => (playerReader.Level.Value >= 4 && Mage_Frostbolt) || Mage_Fireball, + UnitClass.Hunter => (playerReader.Level.Value >= 4 && Hunter_SerpentSting) || Hunter_AutoShoot || playerReader.IsInMeleeRange(), + UnitClass.Warlock => Warlock_ShadowBolt, + UnitClass.Shaman => (playerReader.Level.Value >= 4 && Shaman_EarthShock) || Shaman_LightningBolt, + UnitClass.DeathKnight => DeathKnight_DeathGrip, + _ => true + }; + + public bool WithinCombatRange(PlayerReader playerReader, UnitClass @class) => @class switch + { + UnitClass.Warrior => (playerReader.Level.Value >= 4 && Warrior_Rend) || playerReader.IsInMeleeRange(), + UnitClass.Rogue => Rogue_SinisterStrike, + UnitClass.Priest => Priest_Smite, + UnitClass.Druid => Druid_Wrath || playerReader.IsInMeleeRange(), + UnitClass.Paladin => (playerReader.Level.Value >= 4 && Paladin_Judgement) || playerReader.IsInMeleeRange(), + UnitClass.Mage => Mage_Frostbolt || Mage_Fireball, + UnitClass.Hunter => (playerReader.Level.Value >= 4 && Hunter_SerpentSting) || Hunter_AutoShoot || playerReader.IsInMeleeRange(), + UnitClass.Warlock => Warlock_ShadowBolt, + UnitClass.Shaman => Shaman_LightningBolt, + UnitClass.DeathKnight => DeathKnight_IcyTouch, + _ => true + }; } \ No newline at end of file diff --git a/Core/AddonComponent/Stance.cs b/Core/AddonComponent/Stance.cs index c106cbb2f..83d4b3680 100644 --- a/Core/AddonComponent/Stance.cs +++ b/Core/AddonComponent/Stance.cs @@ -1,82 +1,81 @@ -namespace Core +namespace Core; + +public sealed class Stance { - public sealed class Stance - { - private readonly int cell; + private readonly int cell; - private int value; + private int value; - public Stance(int cell) - { - this.cell = cell; - } + public Stance(int cell) + { + this.cell = cell; + } - public void Update(IAddonDataProvider reader) - { - value = reader.GetInt(cell); - } + public void Update(IAddonDataProvider reader) + { + value = reader.GetInt(cell); + } - public Form Get(UnitClass @class, bool stealth, ClientVersion version) => value == 0 ? Form.None : @class switch - { - UnitClass.Warrior => Form.Warrior_BattleStance + value - 1, - UnitClass.Rogue => Form.Rogue_Stealth + value - 1, - UnitClass.Priest => Form.Priest_Shadowform + value - 1, - UnitClass.Druid => version == ClientVersion.Wrath ? Form.Druid_Bear + value - 1 : (stealth ? Form.Druid_Cat_Prowl : Form.Druid_Bear + value - 1), - UnitClass.Paladin => Form.Paladin_Devotion_Aura + value - 1, - UnitClass.Shaman => Form.Shaman_GhostWolf + value - 1, - UnitClass.DeathKnight => Form.DeathKnight_Blood_Presence + value - 1, - _ => Form.None - }; + public Form Get(UnitClass @class, bool stealth, ClientVersion version) => value == 0 ? Form.None : @class switch + { + UnitClass.Warrior => Form.Warrior_BattleStance + value - 1, + UnitClass.Rogue => Form.Rogue_Stealth + value - 1, + UnitClass.Priest => Form.Priest_Shadowform + value - 1, + UnitClass.Druid => version == ClientVersion.Wrath ? Form.Druid_Bear + value - 1 : (stealth ? Form.Druid_Cat_Prowl : Form.Druid_Bear + value - 1), + UnitClass.Paladin => Form.Paladin_Devotion_Aura + value - 1, + UnitClass.Shaman => Form.Shaman_GhostWolf + value - 1, + UnitClass.DeathKnight => Form.DeathKnight_Blood_Presence + value - 1, + _ => Form.None + }; - public static int ToSlot(KeyAction item, PlayerReader playerReader) - { - return item.Slot <= ActionBar.MAIN_ACTIONBAR_SLOT - ? item.Slot + (int)FormToActionBar(playerReader.Class, item.HasFormRequirement ? item.FormEnum : playerReader.Form) - : item.Slot; - } + public static int ToSlot(KeyAction item, PlayerReader playerReader) + { + return item.Slot <= ActionBar.MAIN_ACTIONBAR_SLOT + ? item.Slot + (int)FormToActionBar(playerReader.Class, item.HasFormRequirement ? item.FormEnum : playerReader.Form) + : item.Slot; + } - private static StanceActionBar FormToActionBar(UnitClass @class, Form form) + private static StanceActionBar FormToActionBar(UnitClass @class, Form form) + { + switch (@class) { - switch (@class) - { - case UnitClass.Druid: - switch (form) - { - case Form.Druid_Cat: - return StanceActionBar.DruidCat; - case Form.Druid_Cat_Prowl: - return StanceActionBar.DruidCatProwl; - case Form.Druid_Bear: - return StanceActionBar.DruidBear; - case Form.Druid_Moonkin: - return StanceActionBar.DruidMoonkin; - } - break; - case UnitClass.Warrior: - switch (form) - { - case Form.Warrior_BattleStance: - return StanceActionBar.WarriorBattleStance; - case Form.Warrior_DefensiveStance: - return StanceActionBar.WarriorDefensiveStance; - case Form.Warrior_BerserkerStance: - return StanceActionBar.WarriorBerserkerStance; - } - break; - case UnitClass.Rogue: - if (form == Form.Rogue_Stealth) - return StanceActionBar.RogueStealth; - break; - case UnitClass.Priest: - if (form == Form.Priest_Shadowform) - return StanceActionBar.PriestShadowform; - break; - } - - return StanceActionBar.None; + case UnitClass.Druid: + switch (form) + { + case Form.Druid_Cat: + return StanceActionBar.DruidCat; + case Form.Druid_Cat_Prowl: + return StanceActionBar.DruidCatProwl; + case Form.Druid_Bear: + return StanceActionBar.DruidBear; + case Form.Druid_Moonkin: + return StanceActionBar.DruidMoonkin; + } + break; + case UnitClass.Warrior: + switch (form) + { + case Form.Warrior_BattleStance: + return StanceActionBar.WarriorBattleStance; + case Form.Warrior_DefensiveStance: + return StanceActionBar.WarriorDefensiveStance; + case Form.Warrior_BerserkerStance: + return StanceActionBar.WarriorBerserkerStance; + } + break; + case UnitClass.Rogue: + if (form == Form.Rogue_Stealth) + return StanceActionBar.RogueStealth; + break; + case UnitClass.Priest: + if (form == Form.Priest_Shadowform) + return StanceActionBar.PriestShadowform; + break; } + return StanceActionBar.None; + } + - } } diff --git a/Core/AddonComponent/StanceActionBar.cs b/Core/AddonComponent/StanceActionBar.cs index 7874f9170..4b7e6f93c 100644 --- a/Core/AddonComponent/StanceActionBar.cs +++ b/Core/AddonComponent/StanceActionBar.cs @@ -1,19 +1,18 @@ -namespace Core +namespace Core; + +public enum StanceActionBar { - public enum StanceActionBar - { - None = 0, - WarriorBattleStance = 73 - 1, - WarriorDefensiveStance = 85 - 1, - WarriorBerserkerStance = 97 - 1, + None = 0, + WarriorBattleStance = 73 - 1, + WarriorDefensiveStance = 85 - 1, + WarriorBerserkerStance = 97 - 1, - DruidCat = 73 - 1, - DruidCatProwl = 85 - 1, - DruidBear = 97 - 1, - DruidMoonkin = 109 - 1, + DruidCat = 73 - 1, + DruidCatProwl = 85 - 1, + DruidBear = 97 - 1, + DruidMoonkin = 109 - 1, - RogueStealth = 73 - 1, + RogueStealth = 73 - 1, - PriestShadowform = 73 - 1 - } + PriestShadowform = 73 - 1 } diff --git a/Core/AddonComponent/TargetDebuffStatus.cs b/Core/AddonComponent/TargetDebuffStatus.cs index ac962aab8..adf0b1a60 100644 --- a/Core/AddonComponent/TargetDebuffStatus.cs +++ b/Core/AddonComponent/TargetDebuffStatus.cs @@ -1,76 +1,75 @@ using System.Collections.Specialized; -namespace Core +namespace Core; + +public sealed class TargetDebuffStatus { - public sealed class TargetDebuffStatus - { - private readonly int cell; + private readonly int cell; - private BitVector32 v; + private BitVector32 v; - public TargetDebuffStatus(int cell) - { - this.cell = cell; - } + public TargetDebuffStatus(int cell) + { + this.cell = cell; + } - public void Update(IAddonDataProvider reader) - { - v = new(reader.GetInt(cell)); - } + public void Update(IAddonDataProvider reader) + { + v = new(reader.GetInt(cell)); + } - public override string ToString() - { - return string.Empty; - } + public override string ToString() + { + return string.Empty; + } - // Priest - public bool ShadowWordPain() => v[Mask._0]; + // Priest + public bool ShadowWordPain() => v[Mask._0]; - // Druid - public bool Roar() => v[Mask._0]; - public bool FaerieFire() => v[Mask._1]; - public bool Rip() => v[Mask._2]; - public bool Moonfire() => v[Mask._3]; - public bool EntanglingRoots() => v[Mask._4]; - public bool Rake() => v[Mask._5]; + // Druid + public bool Roar() => v[Mask._0]; + public bool FaerieFire() => v[Mask._1]; + public bool Rip() => v[Mask._2]; + public bool Moonfire() => v[Mask._3]; + public bool EntanglingRoots() => v[Mask._4]; + public bool Rake() => v[Mask._5]; - // Paladin - public bool JudgementoftheCrusader() => v[Mask._0]; - public bool HammerOfJustice() => v[Mask._1]; - public bool JudgementAny() => JudgementofWisdom() || JudgementofLight() || JudgementofJustice(); - public bool JudgementofWisdom() => v[Mask._2]; - public bool JudgementofLight() => v[Mask._3]; - public bool JudgementofJustice() => v[Mask._4]; + // Paladin + public bool JudgementoftheCrusader() => v[Mask._0]; + public bool HammerOfJustice() => v[Mask._1]; + public bool JudgementAny() => JudgementofWisdom() || JudgementofLight() || JudgementofJustice(); + public bool JudgementofWisdom() => v[Mask._2]; + public bool JudgementofLight() => v[Mask._3]; + public bool JudgementofJustice() => v[Mask._4]; - // Mage - public bool Frostbite() => v[Mask._0]; - public bool Slow() => v[Mask._1]; + // Mage + public bool Frostbite() => v[Mask._0]; + public bool Slow() => v[Mask._1]; - // Rogue + // Rogue - // Warrior - public bool Rend() => v[Mask._0]; - public bool ThunderClap() => v[Mask._1]; - public bool Hamstring() => v[Mask._2]; - public bool ChargeStun() => v[Mask._3]; + // Warrior + public bool Rend() => v[Mask._0]; + public bool ThunderClap() => v[Mask._1]; + public bool Hamstring() => v[Mask._2]; + public bool ChargeStun() => v[Mask._3]; - // Warlock - public bool Curseof() => v[Mask._0]; - public bool Corruption() => v[Mask._1]; - public bool Immolate() => v[Mask._2]; - public bool SiphonLife() => v[Mask._3]; + // Warlock + public bool Curseof() => v[Mask._0]; + public bool Corruption() => v[Mask._1]; + public bool Immolate() => v[Mask._2]; + public bool SiphonLife() => v[Mask._3]; - // Hunter - public bool SerpentSting() => v[Mask._0]; - public bool HuntersMark() => v[Mask._1]; - public bool ViperSting() => v[Mask._2]; - public bool ExplosiveShot() => v[Mask._3]; - public bool BlackArrow() => v[Mask._4]; + // Hunter + public bool SerpentSting() => v[Mask._0]; + public bool HuntersMark() => v[Mask._1]; + public bool ViperSting() => v[Mask._2]; + public bool ExplosiveShot() => v[Mask._3]; + public bool BlackArrow() => v[Mask._4]; - // Death Knight - public bool BloodPlague() => v[Mask._0]; - public bool FrostFever() => v[Mask._1]; - public bool Strangulate() => v[Mask._2]; - public bool ChainsofIce() => v[Mask._3]; - } + // Death Knight + public bool BloodPlague() => v[Mask._0]; + public bool FrostFever() => v[Mask._1]; + public bool Strangulate() => v[Mask._2]; + public bool ChainsofIce() => v[Mask._3]; } \ No newline at end of file diff --git a/Core/AddonComponent/UI_ERROR.cs b/Core/AddonComponent/UI_ERROR.cs index 63d41a08d..3bc33ba66 100644 --- a/Core/AddonComponent/UI_ERROR.cs +++ b/Core/AddonComponent/UI_ERROR.cs @@ -1,60 +1,59 @@ -namespace Core +namespace Core; + +public enum UI_ERROR { - public enum UI_ERROR - { - NONE = 0, - ERR_BADATTACKFACING = 1, - ERR_SPELL_FAILED_S = 2, - ERR_SPELL_OUT_OF_RANGE = 3, - ERR_BADATTACKPOS = 4, - ERR_AUTOFOLLOW_TOO_FAR = 5, - SPELL_FAILED_MOVING = 6, - ERR_SPELL_COOLDOWN = 7, - ERR_SPELL_FAILED_ANOTHER_IN_PROGRESS = 8, - ERR_SPELL_FAILED_STUNNED = 9, - ERR_SPELL_FAILED_INTERRUPTED = 10, - SPELL_FAILED_ITEM_NOT_READY = 11, - SPELL_FAILED_TRY_AGAIN = 12, - SPELL_FAILED_NOT_READY = 13, - SPELL_FAILED_TARGETS_DEAD = 14, - ERR_LOOT_LOCKED = 15, - ERR_ATTACK_PACIFIED = 16, - ERR_REQUIRES_S = 17, - SPELL_FAILED_LINE_OF_SIGHT = 18, + NONE = 0, + ERR_BADATTACKFACING = 1, + ERR_SPELL_FAILED_S = 2, + ERR_SPELL_OUT_OF_RANGE = 3, + ERR_BADATTACKPOS = 4, + ERR_AUTOFOLLOW_TOO_FAR = 5, + SPELL_FAILED_MOVING = 6, + ERR_SPELL_COOLDOWN = 7, + ERR_SPELL_FAILED_ANOTHER_IN_PROGRESS = 8, + ERR_SPELL_FAILED_STUNNED = 9, + ERR_SPELL_FAILED_INTERRUPTED = 10, + SPELL_FAILED_ITEM_NOT_READY = 11, + SPELL_FAILED_TRY_AGAIN = 12, + SPELL_FAILED_NOT_READY = 13, + SPELL_FAILED_TARGETS_DEAD = 14, + ERR_LOOT_LOCKED = 15, + ERR_ATTACK_PACIFIED = 16, + ERR_REQUIRES_S = 17, + SPELL_FAILED_LINE_OF_SIGHT = 18, - MAX_ERROR_RANGE = 2000, + MAX_ERROR_RANGE = 2000, - CAST_START = 999998, - CAST_SUCCESS = 999999 - } + CAST_START = 999998, + CAST_SUCCESS = 999999 +} - public static class UI_ERROR_Extensions +public static class UI_ERROR_Extensions +{ + public static string ToStringF(this UI_ERROR value) => value switch { - public static string ToStringF(this UI_ERROR value) => value switch - { - UI_ERROR.ERR_BADATTACKFACING => nameof(UI_ERROR.ERR_BADATTACKFACING), - UI_ERROR.ERR_SPELL_FAILED_S => nameof(UI_ERROR.ERR_SPELL_FAILED_S), - UI_ERROR.ERR_SPELL_OUT_OF_RANGE => nameof(UI_ERROR.ERR_SPELL_OUT_OF_RANGE), - UI_ERROR.ERR_BADATTACKPOS => nameof(UI_ERROR.ERR_BADATTACKPOS), - UI_ERROR.ERR_AUTOFOLLOW_TOO_FAR => nameof(UI_ERROR.ERR_AUTOFOLLOW_TOO_FAR), - UI_ERROR.SPELL_FAILED_MOVING => nameof(UI_ERROR.SPELL_FAILED_MOVING), - UI_ERROR.ERR_SPELL_COOLDOWN => nameof(UI_ERROR.ERR_SPELL_COOLDOWN), - UI_ERROR.ERR_SPELL_FAILED_ANOTHER_IN_PROGRESS => nameof(UI_ERROR.ERR_SPELL_FAILED_ANOTHER_IN_PROGRESS), - UI_ERROR.ERR_SPELL_FAILED_STUNNED => nameof(UI_ERROR.ERR_SPELL_FAILED_STUNNED), - UI_ERROR.ERR_SPELL_FAILED_INTERRUPTED => nameof(UI_ERROR.ERR_SPELL_FAILED_INTERRUPTED), - UI_ERROR.SPELL_FAILED_ITEM_NOT_READY => nameof(UI_ERROR.SPELL_FAILED_ITEM_NOT_READY), - UI_ERROR.SPELL_FAILED_TRY_AGAIN => nameof(UI_ERROR.SPELL_FAILED_TRY_AGAIN), - UI_ERROR.SPELL_FAILED_NOT_READY => nameof(UI_ERROR.SPELL_FAILED_NOT_READY), - UI_ERROR.SPELL_FAILED_TARGETS_DEAD => nameof(UI_ERROR.SPELL_FAILED_TARGETS_DEAD), - UI_ERROR.ERR_LOOT_LOCKED => nameof(UI_ERROR.ERR_LOOT_LOCKED), - UI_ERROR.ERR_ATTACK_PACIFIED => nameof(UI_ERROR.ERR_ATTACK_PACIFIED), - UI_ERROR.ERR_REQUIRES_S => nameof(UI_ERROR.ERR_REQUIRES_S), - UI_ERROR.SPELL_FAILED_LINE_OF_SIGHT => nameof(UI_ERROR.SPELL_FAILED_LINE_OF_SIGHT), - UI_ERROR.MAX_ERROR_RANGE => nameof(UI_ERROR.MAX_ERROR_RANGE), - UI_ERROR.CAST_START => nameof(UI_ERROR.CAST_START), - UI_ERROR.CAST_SUCCESS => nameof(UI_ERROR.CAST_SUCCESS), - UI_ERROR.NONE => nameof(UI_ERROR.NONE), - _ => throw new System.ArgumentOutOfRangeException() - }; - } + UI_ERROR.ERR_BADATTACKFACING => nameof(UI_ERROR.ERR_BADATTACKFACING), + UI_ERROR.ERR_SPELL_FAILED_S => nameof(UI_ERROR.ERR_SPELL_FAILED_S), + UI_ERROR.ERR_SPELL_OUT_OF_RANGE => nameof(UI_ERROR.ERR_SPELL_OUT_OF_RANGE), + UI_ERROR.ERR_BADATTACKPOS => nameof(UI_ERROR.ERR_BADATTACKPOS), + UI_ERROR.ERR_AUTOFOLLOW_TOO_FAR => nameof(UI_ERROR.ERR_AUTOFOLLOW_TOO_FAR), + UI_ERROR.SPELL_FAILED_MOVING => nameof(UI_ERROR.SPELL_FAILED_MOVING), + UI_ERROR.ERR_SPELL_COOLDOWN => nameof(UI_ERROR.ERR_SPELL_COOLDOWN), + UI_ERROR.ERR_SPELL_FAILED_ANOTHER_IN_PROGRESS => nameof(UI_ERROR.ERR_SPELL_FAILED_ANOTHER_IN_PROGRESS), + UI_ERROR.ERR_SPELL_FAILED_STUNNED => nameof(UI_ERROR.ERR_SPELL_FAILED_STUNNED), + UI_ERROR.ERR_SPELL_FAILED_INTERRUPTED => nameof(UI_ERROR.ERR_SPELL_FAILED_INTERRUPTED), + UI_ERROR.SPELL_FAILED_ITEM_NOT_READY => nameof(UI_ERROR.SPELL_FAILED_ITEM_NOT_READY), + UI_ERROR.SPELL_FAILED_TRY_AGAIN => nameof(UI_ERROR.SPELL_FAILED_TRY_AGAIN), + UI_ERROR.SPELL_FAILED_NOT_READY => nameof(UI_ERROR.SPELL_FAILED_NOT_READY), + UI_ERROR.SPELL_FAILED_TARGETS_DEAD => nameof(UI_ERROR.SPELL_FAILED_TARGETS_DEAD), + UI_ERROR.ERR_LOOT_LOCKED => nameof(UI_ERROR.ERR_LOOT_LOCKED), + UI_ERROR.ERR_ATTACK_PACIFIED => nameof(UI_ERROR.ERR_ATTACK_PACIFIED), + UI_ERROR.ERR_REQUIRES_S => nameof(UI_ERROR.ERR_REQUIRES_S), + UI_ERROR.SPELL_FAILED_LINE_OF_SIGHT => nameof(UI_ERROR.SPELL_FAILED_LINE_OF_SIGHT), + UI_ERROR.MAX_ERROR_RANGE => nameof(UI_ERROR.MAX_ERROR_RANGE), + UI_ERROR.CAST_START => nameof(UI_ERROR.CAST_START), + UI_ERROR.CAST_SUCCESS => nameof(UI_ERROR.CAST_SUCCESS), + UI_ERROR.NONE => nameof(UI_ERROR.NONE), + _ => throw new System.ArgumentOutOfRangeException() + }; } \ No newline at end of file diff --git a/Core/AddonComponent/UnitClass.cs b/Core/AddonComponent/UnitClass.cs index 59c333a54..fae7d52ee 100644 --- a/Core/AddonComponent/UnitClass.cs +++ b/Core/AddonComponent/UnitClass.cs @@ -1,40 +1,39 @@ -namespace Core +namespace Core; + +public enum UnitClass { - public enum UnitClass - { - None, - Warrior, - Paladin, - Hunter, - Rogue, - Priest, - DeathKnight, - Shaman, - Mage, - Warlock, - Monk, - Druid, - DemonHunter - } + None, + Warrior, + Paladin, + Hunter, + Rogue, + Priest, + DeathKnight, + Shaman, + Mage, + Warlock, + Monk, + Druid, + DemonHunter +} - public static class UnitClass_Extension +public static class UnitClass_Extension +{ + public static string ToStringF(this UnitClass value) => value switch { - public static string ToStringF(this UnitClass value) => value switch - { - UnitClass.None => nameof(UnitClass.None), - UnitClass.Warrior => nameof(UnitClass.Warrior), - UnitClass.Paladin => nameof(UnitClass.Paladin), - UnitClass.Hunter => nameof(UnitClass.Hunter), - UnitClass.Rogue => nameof(UnitClass.Rogue), - UnitClass.Priest => nameof(UnitClass.Priest), - UnitClass.DeathKnight => nameof(UnitClass.DeathKnight), - UnitClass.Shaman => nameof(UnitClass.Shaman), - UnitClass.Mage => nameof(UnitClass.Mage), - UnitClass.Warlock => nameof(UnitClass.Warlock), - UnitClass.Monk => nameof(UnitClass.Monk), - UnitClass.Druid => nameof(UnitClass.Druid), - UnitClass.DemonHunter => nameof(UnitClass.DemonHunter), - _ => nameof(UnitClass.None) - }; - } + UnitClass.None => nameof(UnitClass.None), + UnitClass.Warrior => nameof(UnitClass.Warrior), + UnitClass.Paladin => nameof(UnitClass.Paladin), + UnitClass.Hunter => nameof(UnitClass.Hunter), + UnitClass.Rogue => nameof(UnitClass.Rogue), + UnitClass.Priest => nameof(UnitClass.Priest), + UnitClass.DeathKnight => nameof(UnitClass.DeathKnight), + UnitClass.Shaman => nameof(UnitClass.Shaman), + UnitClass.Mage => nameof(UnitClass.Mage), + UnitClass.Warlock => nameof(UnitClass.Warlock), + UnitClass.Monk => nameof(UnitClass.Monk), + UnitClass.Druid => nameof(UnitClass.Druid), + UnitClass.DemonHunter => nameof(UnitClass.DemonHunter), + _ => nameof(UnitClass.None) + }; } \ No newline at end of file diff --git a/Core/AddonComponent/UnitClassification.cs b/Core/AddonComponent/UnitClassification.cs index 8cc62f3da..454711d41 100644 --- a/Core/AddonComponent/UnitClassification.cs +++ b/Core/AddonComponent/UnitClassification.cs @@ -2,40 +2,39 @@ using Newtonsoft.Json; -namespace Core +namespace Core; + +[Flags] +[JsonConverter(typeof(Newtonsoft.Json.Converters.StringEnumConverter))] +public enum UnitClassification { - [Flags] - [JsonConverter(typeof(Newtonsoft.Json.Converters.StringEnumConverter))] - public enum UnitClassification - { - None = 0, - Normal = 1, - Trivial = 2, - Minus = 4, - Rare = 8, - Elite = 16, - RareElite = 32, - WorldBoss = 64 - } + None = 0, + Normal = 1, + Trivial = 2, + Minus = 4, + Rare = 8, + Elite = 16, + RareElite = 32, + WorldBoss = 64 +} - public static class UnitClassification_Extension +public static class UnitClassification_Extension +{ + public static string ToStringF(this UnitClassification value) => value switch { - public static string ToStringF(this UnitClassification value) => value switch - { - UnitClassification.None => nameof(UnitClassification.None), - UnitClassification.Normal => nameof(UnitClassification.Normal), - UnitClassification.Trivial => nameof(UnitClassification.Trivial), - UnitClassification.Minus => nameof(UnitClassification.Minus), - UnitClassification.Rare => nameof(UnitClassification.Rare), - UnitClassification.Elite => nameof(UnitClassification.Elite), - UnitClassification.RareElite => nameof(UnitClassification.RareElite), - UnitClassification.WorldBoss => nameof(UnitClassification.WorldBoss), - _ => throw new ArgumentOutOfRangeException() - }; + UnitClassification.None => nameof(UnitClassification.None), + UnitClassification.Normal => nameof(UnitClassification.Normal), + UnitClassification.Trivial => nameof(UnitClassification.Trivial), + UnitClassification.Minus => nameof(UnitClassification.Minus), + UnitClassification.Rare => nameof(UnitClassification.Rare), + UnitClassification.Elite => nameof(UnitClassification.Elite), + UnitClassification.RareElite => nameof(UnitClassification.RareElite), + UnitClassification.WorldBoss => nameof(UnitClassification.WorldBoss), + _ => throw new ArgumentOutOfRangeException() + }; - public static bool HasFlagF(this UnitClassification value, UnitClassification flag) - { - return (value & flag) != 0; - } + public static bool HasFlagF(this UnitClassification value, UnitClassification flag) + { + return (value & flag) != 0; } } diff --git a/Core/AddonComponent/UnitRace.cs b/Core/AddonComponent/UnitRace.cs index 2983764cf..2bb0707be 100644 --- a/Core/AddonComponent/UnitRace.cs +++ b/Core/AddonComponent/UnitRace.cs @@ -1,38 +1,37 @@ -namespace Core +namespace Core; + +public enum UnitRace { - public enum UnitRace - { - None, - Human, - Orc, - Dwarf, - NightElf, - Undead, - Tauren, - Gnome, - Troll, - Goblin, - BloodElf, - Draenei - } + None, + Human, + Orc, + Dwarf, + NightElf, + Undead, + Tauren, + Gnome, + Troll, + Goblin, + BloodElf, + Draenei +} - public static class UnitRace_Extension +public static class UnitRace_Extension +{ + public static string ToStringF(this UnitRace value) => value switch { - public static string ToStringF(this UnitRace value) => value switch - { - UnitRace.None => nameof(UnitRace.None), - UnitRace.Human => nameof(UnitRace.Human), - UnitRace.Orc => nameof(UnitRace.Orc), - UnitRace.Dwarf => nameof(UnitRace.Dwarf), - UnitRace.NightElf => nameof(UnitRace.NightElf), - UnitRace.Undead => nameof(UnitRace.Undead), - UnitRace.Tauren => nameof(UnitRace.Tauren), - UnitRace.Gnome => nameof(UnitRace.Gnome), - UnitRace.Troll => nameof(UnitRace.Troll), - UnitRace.Goblin => nameof(UnitRace.Goblin), - UnitRace.BloodElf => nameof(UnitRace.BloodElf), - UnitRace.Draenei => nameof(UnitRace.Draenei), - _ => nameof(UnitRace.None) - }; - } + UnitRace.None => nameof(UnitRace.None), + UnitRace.Human => nameof(UnitRace.Human), + UnitRace.Orc => nameof(UnitRace.Orc), + UnitRace.Dwarf => nameof(UnitRace.Dwarf), + UnitRace.NightElf => nameof(UnitRace.NightElf), + UnitRace.Undead => nameof(UnitRace.Undead), + UnitRace.Tauren => nameof(UnitRace.Tauren), + UnitRace.Gnome => nameof(UnitRace.Gnome), + UnitRace.Troll => nameof(UnitRace.Troll), + UnitRace.Goblin => nameof(UnitRace.Goblin), + UnitRace.BloodElf => nameof(UnitRace.BloodElf), + UnitRace.Draenei => nameof(UnitRace.Draenei), + _ => nameof(UnitRace.None) + }; } \ No newline at end of file diff --git a/Core/AddonComponent/UnitsTarget.cs b/Core/AddonComponent/UnitsTarget.cs index e1dba4a86..a654115c6 100644 --- a/Core/AddonComponent/UnitsTarget.cs +++ b/Core/AddonComponent/UnitsTarget.cs @@ -1,13 +1,12 @@ -namespace Core +namespace Core; + +public enum UnitsTarget { - public enum UnitsTarget - { - Self = 0, - Me = 1, - None = 2, - Unknown = 3, - Pet = 4, - PetHasATarget = 5, - PartyOrPet = 6 - }; -} \ No newline at end of file + Self = 0, + Me = 1, + None = 2, + Unknown = 3, + Pet = 4, + PetHasATarget = 5, + PartyOrPet = 6 +}; \ No newline at end of file diff --git a/Core/AddonDataProvider/AddonDataProviderConfig.cs b/Core/AddonDataProvider/AddonDataProviderConfig.cs index e8a839b2d..fa2414a2f 100644 --- a/Core/AddonDataProvider/AddonDataProviderConfig.cs +++ b/Core/AddonDataProvider/AddonDataProviderConfig.cs @@ -1,14 +1,13 @@ using System.Drawing.Imaging; -namespace Core +namespace Core; + +internal static class AddonDataProviderConfig { - internal static class AddonDataProviderConfig - { - // B G R - public static readonly byte[] fColor = { 0, 0, 0 }; - public static readonly byte[] lColor = { 129, 132, 30 }; + // B G R + public static readonly byte[] fColor = { 0, 0, 0 }; + public static readonly byte[] lColor = { 129, 132, 30 }; - public const PixelFormat PIXEL_FORMAT = PixelFormat.Format32bppPArgb; - public const int BYTES_PER_PIXEL = 4; - } + public const PixelFormat PIXEL_FORMAT = PixelFormat.Format32bppPArgb; + public const int BYTES_PER_PIXEL = 4; } diff --git a/Core/AddonDataProvider/AddonDataProviderDXGI.cs b/Core/AddonDataProvider/AddonDataProviderDXGI.cs index 861058320..2198dc9de 100644 --- a/Core/AddonDataProvider/AddonDataProviderDXGI.cs +++ b/Core/AddonDataProvider/AddonDataProviderDXGI.cs @@ -13,203 +13,202 @@ using static WinAPI.NativeMethods; -namespace Core +namespace Core; + +public sealed class AddonDataProviderDXGI : IAddonDataProvider, IDisposable { - public sealed class AddonDataProviderDXGI : IAddonDataProvider, IDisposable + private readonly WowScreen wowScreen; + private readonly DataFrame[] frames; + + private static readonly FeatureLevel[] s_featureLevels = { - private readonly WowScreen wowScreen; - private readonly DataFrame[] frames; + FeatureLevel.Level_12_1, + FeatureLevel.Level_12_0, + FeatureLevel.Level_11_0, + }; - private static readonly FeatureLevel[] s_featureLevels = - { - FeatureLevel.Level_12_1, - FeatureLevel.Level_12_0, - FeatureLevel.Level_11_0, - }; + private readonly Vortice.RawRect bounds; + + private readonly IDXGIAdapter adapter; + private readonly IDXGIOutput output; + private readonly IDXGIOutput1 output1; - private readonly Vortice.RawRect bounds; + private readonly IDXGIOutputDuplication duplication; + private readonly ID3D11Texture2D addonTexture; + private readonly ID3D11Device device; - private readonly IDXGIAdapter adapter; - private readonly IDXGIOutput output; - private readonly IDXGIOutput1 output1; + private readonly Bitmap bitmap; + private readonly Rectangle rect; + private readonly int[] data; - private readonly IDXGIOutputDuplication duplication; - private readonly ID3D11Texture2D addonTexture; - private readonly ID3D11Device device; + private readonly StringBuilder sb = new(3); - private readonly Bitmap bitmap; - private readonly Rectangle rect; - private readonly int[] data; + public AddonDataProviderDXGI(WowScreen wowScreen, DataFrame[] frames) + { + this.wowScreen = wowScreen; - private readonly StringBuilder sb = new(3); + this.frames = frames; - public AddonDataProviderDXGI(WowScreen wowScreen, DataFrame[] frames) + data = new int[this.frames.Length]; + + for (int i = 0; i < this.frames.Length; i++) { - this.wowScreen = wowScreen; + rect.Width = Math.Max(rect.Width, frames[i].X); + rect.Height = Math.Max(rect.Height, frames[i].Y); + } + rect.Width++; + rect.Height++; + + bitmap = new(rect.Right, rect.Bottom, PixelFormat.Format32bppRgb); - this.frames = frames; + IntPtr hMonitor = MonitorFromWindow(wowScreen.ProcessHwnd, MONITOR_DEFAULTTONULL); - data = new int[this.frames.Length]; + Result result; - for (int i = 0; i < this.frames.Length; i++) + IDXGIFactory1 factory = DXGI.CreateDXGIFactory1(); + result = factory.EnumAdapters(0, out adapter); + if (result == Result.Fail) + throw new Exception($"Unable to enumerate adapter! {result.Description}"); + + int srcIdx = 0; + do + { + result = adapter.EnumOutputs(srcIdx, out output); + if (result == Result.Ok && + output.Description.Monitor == hMonitor) { - rect.Width = Math.Max(rect.Width, frames[i].X); - rect.Height = Math.Max(rect.Height, frames[i].Y); + break; } - rect.Width++; - rect.Height++; + } while (result != Result.Fail); - bitmap = new(rect.Right, rect.Bottom, PixelFormat.Format32bppRgb); + output1 = output.QueryInterface(); + result = D3D11.D3D11CreateDevice(adapter, DriverType.Unknown, DeviceCreationFlags.None, s_featureLevels, out device!); - IntPtr hMonitor = MonitorFromWindow(wowScreen.ProcessHwnd, MONITOR_DEFAULTTONULL); + if (result == Result.Fail) + throw new Exception($"device is null {result.Description}"); - Result result; + bounds = output1.Description.DesktopCoordinates; - IDXGIFactory1 factory = DXGI.CreateDXGIFactory1(); - result = factory.EnumAdapters(0, out adapter); - if (result == Result.Fail) - throw new Exception($"Unable to enumerate adapter! {result.Description}"); + duplication = output1.DuplicateOutput(device); - int srcIdx = 0; - do - { - result = adapter.EnumOutputs(srcIdx, out output); - if (result == Result.Ok && - output.Description.Monitor == hMonitor) - { - break; - } - } while (result != Result.Fail); + Texture2DDescription textureDesc = new() + { + CPUAccessFlags = CpuAccessFlags.Read, + BindFlags = BindFlags.None, + Format = Format.B8G8R8A8_UNorm, + Width = rect.Right, + Height = rect.Bottom, + MiscFlags = ResourceOptionFlags.None, + MipLevels = 1, + ArraySize = 1, + SampleDescription = { Count = 1, Quality = 0 }, + Usage = ResourceUsage.Staging + }; - output1 = output.QueryInterface(); - result = D3D11.D3D11CreateDevice(adapter, DriverType.Unknown, DeviceCreationFlags.None, s_featureLevels, out device!); + addonTexture = device.CreateTexture2D(textureDesc); + } - if (result == Result.Fail) - throw new Exception($"device is null {result.Description}"); + public void Dispose() + { + duplication.ReleaseFrame(); + duplication.Dispose(); - bounds = output1.Description.DesktopCoordinates; + addonTexture.Dispose(); + device.Dispose(); + adapter.Dispose(); + output1.Dispose(); + output.Dispose(); - duplication = output1.DuplicateOutput(device); + bitmap.Dispose(); + } - Texture2DDescription textureDesc = new() - { - CPUAccessFlags = CpuAccessFlags.Read, - BindFlags = BindFlags.None, - Format = Format.B8G8R8A8_UNorm, - Width = rect.Right, - Height = rect.Bottom, - MiscFlags = ResourceOptionFlags.None, - MipLevels = 1, - ArraySize = 1, - SampleDescription = { Count = 1, Quality = 0 }, - Usage = ResourceUsage.Staging - }; - - addonTexture = device.CreateTexture2D(textureDesc); - } + public void Update() + { + Point p = new(); + wowScreen.GetPosition(ref p); - public void Dispose() - { - duplication.ReleaseFrame(); - duplication.Dispose(); + duplication.ReleaseFrame(); - addonTexture.Dispose(); - device.Dispose(); - adapter.Dispose(); - output1.Dispose(); - output.Dispose(); + Result result = duplication.AcquireNextFrame(50, out OutduplFrameInfo frameInfo, out IDXGIResource? desktopResource); + if (!result.Success) + return; - bitmap.Dispose(); - } + ID3D11Texture2D texture = desktopResource.QueryInterface(); + device.ImmediateContext.CopySubresourceRegion(addonTexture, 0, 0, 0, 0, texture, 0, + new Vortice.Mathematics.Box(p.X, p.Y, 0, p.X + rect.Right, p.Y + rect.Bottom, 1)); + MappedSubresource dataBox = device.ImmediateContext.Map(addonTexture, 0, MapMode.Read, Vortice.Direct3D11.MapFlags.None); - public void Update() - { - Point p = new(); - wowScreen.GetPosition(ref p); + int sizeInBytesToCopy = rect.Right * AddonDataProviderConfig.BYTES_PER_PIXEL; - duplication.ReleaseFrame(); + BitmapData bd = bitmap.LockBits(rect, ImageLockMode.ReadWrite, bitmap.PixelFormat); + for (int y = 0; y < rect.Bottom; y++) + { + MemoryHelpers.CopyMemory(bd.Scan0 + y * bd.Stride, dataBox.DataPointer + y * dataBox.RowPitch, sizeInBytesToCopy); + } + device.ImmediateContext.Unmap(addonTexture, 0); + texture.Dispose(); - Result result = duplication.AcquireNextFrame(50, out OutduplFrameInfo frameInfo, out IDXGIResource? desktopResource); - if (!result.Success) - return; + //bitmap.Save($"bitmap.bmp", ImageFormat.Bmp); + //Thread.Sleep(1000); - ID3D11Texture2D texture = desktopResource.QueryInterface(); - device.ImmediateContext.CopySubresourceRegion(addonTexture, 0, 0, 0, 0, texture, 0, - new Vortice.Mathematics.Box(p.X, p.Y, 0, p.X + rect.Right, p.Y + rect.Bottom, 1)); - MappedSubresource dataBox = device.ImmediateContext.Map(addonTexture, 0, MapMode.Read, Vortice.Direct3D11.MapFlags.None); + unsafe + { + byte* fLine = (byte*)bd.Scan0 + (frames[0].Y * bd.Stride); + int fx = frames[0].X * AddonDataProviderConfig.BYTES_PER_PIXEL; - int sizeInBytesToCopy = rect.Right * AddonDataProviderConfig.BYTES_PER_PIXEL; + byte* lLine = (byte*)bd.Scan0 + (frames[^1].Y * bd.Stride); + int lx = frames[^1].X * AddonDataProviderConfig.BYTES_PER_PIXEL; - BitmapData bd = bitmap.LockBits(rect, ImageLockMode.ReadWrite, bitmap.PixelFormat); - for (int y = 0; y < rect.Bottom; y++) + for (int i = 0; i < 3; i++) { - MemoryHelpers.CopyMemory(bd.Scan0 + y * bd.Stride, dataBox.DataPointer + y * dataBox.RowPitch, sizeInBytesToCopy); + if (fLine[fx + i] != AddonDataProviderConfig.fColor[i] || + lLine[lx + i] != AddonDataProviderConfig.lColor[i]) + goto Unlock; } - device.ImmediateContext.Unmap(addonTexture, 0); - texture.Dispose(); - - //bitmap.Save($"bitmap.bmp", ImageFormat.Bmp); - //Thread.Sleep(1000); - unsafe + for (int i = 0; i < frames.Length; i++) { - byte* fLine = (byte*)bd.Scan0 + (frames[0].Y * bd.Stride); - int fx = frames[0].X * AddonDataProviderConfig.BYTES_PER_PIXEL; - - byte* lLine = (byte*)bd.Scan0 + (frames[^1].Y * bd.Stride); - int lx = frames[^1].X * AddonDataProviderConfig.BYTES_PER_PIXEL; + fLine = (byte*)bd.Scan0 + (frames[i].Y * bd.Stride); + fx = frames[i].X * AddonDataProviderConfig.BYTES_PER_PIXEL; - for (int i = 0; i < 3; i++) - { - if (fLine[fx + i] != AddonDataProviderConfig.fColor[i] || - lLine[lx + i] != AddonDataProviderConfig.lColor[i]) - goto Unlock; - } - - for (int i = 0; i < frames.Length; i++) - { - fLine = (byte*)bd.Scan0 + (frames[i].Y * bd.Stride); - fx = frames[i].X * AddonDataProviderConfig.BYTES_PER_PIXEL; - - data[frames[i].Index] = (fLine[fx + 2] * 65536) + (fLine[fx + 1] * 256) + fLine[fx]; - } - - Unlock: - bitmap.UnlockBits(bd); + data[frames[i].Index] = (fLine[fx + 2] * 65536) + (fLine[fx + 1] * 256) + fLine[fx]; } + + Unlock: + bitmap.UnlockBits(bd); } + } - public void InitFrames(DataFrame[] frames) { } + public void InitFrames(DataFrame[] frames) { } - public int GetInt(int index) - { - return data[index]; - } + public int GetInt(int index) + { + return data[index]; + } - public float GetFixed(int index) - { - return data[index] / 100000f; - } + public float GetFixed(int index) + { + return data[index] / 100000f; + } - public string GetString(int index) - { - int color = GetInt(index); - if (color == 0 || color > 999999) - return string.Empty; + public string GetString(int index) + { + int color = GetInt(index); + if (color == 0 || color > 999999) + return string.Empty; - sb.Clear(); + sb.Clear(); - int n = color / 10000; - if (n > 0) sb.Append((char)n); + int n = color / 10000; + if (n > 0) sb.Append((char)n); - n = color / 100 % 100; - if (n > 0) sb.Append((char)n); + n = color / 100 % 100; + if (n > 0) sb.Append((char)n); - n = color % 100; - if (n > 0) sb.Append((char)n); + n = color % 100; + if (n > 0) sb.Append((char)n); - return sb.ToString().Trim(); - } + return sb.ToString().Trim(); } } diff --git a/Core/AddonDataProvider/AddonDataProviderGDI.cs b/Core/AddonDataProvider/AddonDataProviderGDI.cs index 7f510ca13..ccd6782d2 100644 --- a/Core/AddonDataProvider/AddonDataProviderGDI.cs +++ b/Core/AddonDataProvider/AddonDataProviderGDI.cs @@ -5,113 +5,112 @@ using System.Drawing.Imaging; using System.Text; -namespace Core -{ - public sealed class AddonDataProviderGDI : IAddonDataProvider, IDisposable - { - private readonly int[] data; - private readonly DataFrame[] frames; - - private readonly Rectangle rect; - private readonly Bitmap bitmap; - private readonly Graphics graphics; +namespace Core; - private readonly WowScreen wowScreen; +public sealed class AddonDataProviderGDI : IAddonDataProvider, IDisposable +{ + private readonly int[] data; + private readonly DataFrame[] frames; - private readonly StringBuilder sb = new(3); + private readonly Rectangle rect; + private readonly Bitmap bitmap; + private readonly Graphics graphics; - public AddonDataProviderGDI(WowScreen wowScreen, DataFrame[] frames) - { - this.wowScreen = wowScreen; - this.frames = frames; + private readonly WowScreen wowScreen; - data = new int[this.frames.Length]; + private readonly StringBuilder sb = new(3); - for (int i = 0; i < this.frames.Length; i++) - { - rect.Width = Math.Max(rect.Width, frames[i].X); - rect.Height = Math.Max(rect.Height, frames[i].Y); - } - rect.Width++; - rect.Height++; + public AddonDataProviderGDI(WowScreen wowScreen, DataFrame[] frames) + { + this.wowScreen = wowScreen; + this.frames = frames; - bitmap = new(rect.Width, rect.Height, AddonDataProviderConfig.PIXEL_FORMAT); - graphics = Graphics.FromImage(bitmap); - } + data = new int[this.frames.Length]; - public void Dispose() + for (int i = 0; i < this.frames.Length; i++) { - graphics.Dispose(); - bitmap.Dispose(); + rect.Width = Math.Max(rect.Width, frames[i].X); + rect.Height = Math.Max(rect.Height, frames[i].Y); } + rect.Width++; + rect.Height++; - public void Update() - { - Point p = new(); - wowScreen.GetPosition(ref p); - graphics.CopyFromScreen(p, Point.Empty, rect.Size); + bitmap = new(rect.Width, rect.Height, AddonDataProviderConfig.PIXEL_FORMAT); + graphics = Graphics.FromImage(bitmap); + } - unsafe - { - BitmapData bd = bitmap.LockBits(rect, ImageLockMode.ReadOnly, AddonDataProviderConfig.PIXEL_FORMAT); + public void Dispose() + { + graphics.Dispose(); + bitmap.Dispose(); + } - byte* fLine = (byte*)bd.Scan0 + (frames[0].Y * bd.Stride); - int fx = frames[0].X * AddonDataProviderConfig.BYTES_PER_PIXEL; + public void Update() + { + Point p = new(); + wowScreen.GetPosition(ref p); + graphics.CopyFromScreen(p, Point.Empty, rect.Size); - byte* lLine = (byte*)bd.Scan0 + (frames[^1].Y * bd.Stride); - int lx = frames[^1].X * AddonDataProviderConfig.BYTES_PER_PIXEL; + unsafe + { + BitmapData bd = bitmap.LockBits(rect, ImageLockMode.ReadOnly, AddonDataProviderConfig.PIXEL_FORMAT); - for (int i = 0; i < 3; i++) - { - if (fLine[fx + i] != AddonDataProviderConfig.fColor[i] || - lLine[lx + i] != AddonDataProviderConfig.lColor[i]) - goto Exit; - } + byte* fLine = (byte*)bd.Scan0 + (frames[0].Y * bd.Stride); + int fx = frames[0].X * AddonDataProviderConfig.BYTES_PER_PIXEL; - for (int i = 0; i < frames.Length; i++) - { - fLine = (byte*)bd.Scan0 + (frames[i].Y * bd.Stride); - fx = frames[i].X * AddonDataProviderConfig.BYTES_PER_PIXEL; + byte* lLine = (byte*)bd.Scan0 + (frames[^1].Y * bd.Stride); + int lx = frames[^1].X * AddonDataProviderConfig.BYTES_PER_PIXEL; - data[frames[i].Index] = fLine[fx] | (fLine[fx + 1] << 8) | (fLine[fx + 2] << 16); - } + for (int i = 0; i < 3; i++) + { + if (fLine[fx + i] != AddonDataProviderConfig.fColor[i] || + lLine[lx + i] != AddonDataProviderConfig.lColor[i]) + goto Exit; + } + + for (int i = 0; i < frames.Length; i++) + { + fLine = (byte*)bd.Scan0 + (frames[i].Y * bd.Stride); + fx = frames[i].X * AddonDataProviderConfig.BYTES_PER_PIXEL; - Exit: - bitmap.UnlockBits(bd); + data[frames[i].Index] = fLine[fx] | (fLine[fx + 1] << 8) | (fLine[fx + 2] << 16); } + + Exit: + bitmap.UnlockBits(bd); } + } - public void InitFrames(DataFrame[] frames) { } + public void InitFrames(DataFrame[] frames) { } - public int GetInt(int index) - { - return data[index]; - } + public int GetInt(int index) + { + return data[index]; + } - public float GetFixed(int index) - { - return data[index] / 100000f; - } + public float GetFixed(int index) + { + return data[index] / 100000f; + } - public string GetString(int index) - { - int color = GetInt(index); - if (color == 0 || color > 999999) - return string.Empty; + public string GetString(int index) + { + int color = GetInt(index); + if (color == 0 || color > 999999) + return string.Empty; - sb.Clear(); + sb.Clear(); - int n = color / 10000; - if (n > 0) sb.Append((char)n); + int n = color / 10000; + if (n > 0) sb.Append((char)n); - n = color / 100 % 100; - if (n > 0) sb.Append((char)n); + n = color / 100 % 100; + if (n > 0) sb.Append((char)n); - n = color % 100; - if (n > 0) sb.Append((char)n); + n = color % 100; + if (n > 0) sb.Append((char)n); - return sb.ToString().Trim(); - } + return sb.ToString().Trim(); } } diff --git a/Core/AddonDataProvider/AddonDataProviderGDIConfig.cs b/Core/AddonDataProvider/AddonDataProviderGDIConfig.cs index a1a5ad77a..15a0f3193 100644 --- a/Core/AddonDataProvider/AddonDataProviderGDIConfig.cs +++ b/Core/AddonDataProvider/AddonDataProviderGDIConfig.cs @@ -6,140 +6,139 @@ using System.Text; using System.Threading; -namespace Core +namespace Core; + +public sealed class AddonDataProviderGDIConfig : IAddonDataProvider, IDisposable { - public sealed class AddonDataProviderGDIConfig : IAddonDataProvider, IDisposable - { - private readonly CancellationToken ct; - private readonly ManualResetEvent manualReset = new(true); - private readonly WowScreen wowScreen; + private readonly CancellationToken ct; + private readonly ManualResetEvent manualReset = new(true); + private readonly WowScreen wowScreen; - private readonly StringBuilder sb = new(3); + private readonly StringBuilder sb = new(3); - private int[] data = Array.Empty(); - private DataFrame[] frames = Array.Empty(); + private int[] data = Array.Empty(); + private DataFrame[] frames = Array.Empty(); - private Rectangle rect; - private Bitmap? bitmap; - private Graphics? graphics; + private Rectangle rect; + private Bitmap? bitmap; + private Graphics? graphics; - private bool disposing; + private bool disposing; - public AddonDataProviderGDIConfig(CancellationTokenSource cts, WowScreen wowScreen, DataFrame[] frames) - { - ct = cts.Token; - this.wowScreen = wowScreen; - InitFrames(frames); - } + public AddonDataProviderGDIConfig(CancellationTokenSource cts, WowScreen wowScreen, DataFrame[] frames) + { + ct = cts.Token; + this.wowScreen = wowScreen; + InitFrames(frames); + } - public void Dispose() - { - if (disposing) - return; + public void Dispose() + { + if (disposing) + return; - disposing = true; + disposing = true; - graphics?.Dispose(); - bitmap?.Dispose(); - } + graphics?.Dispose(); + bitmap?.Dispose(); + } - public void Update() + public void Update() + { + manualReset.WaitOne(); + ct.WaitHandle.WaitOne(25); + + if (ct.IsCancellationRequested || + disposing || + data.Length == 0 || + frames.Length == 0 || + bitmap == null || + graphics == null) + return; + + Point p = new(); + wowScreen.GetPosition(ref p); + graphics.CopyFromScreen(p, Point.Empty, rect.Size); + + unsafe { - manualReset.WaitOne(); - ct.WaitHandle.WaitOne(25); - - if (ct.IsCancellationRequested || - disposing || - data.Length == 0 || - frames.Length == 0 || - bitmap == null || - graphics == null) - return; - - Point p = new(); - wowScreen.GetPosition(ref p); - graphics.CopyFromScreen(p, Point.Empty, rect.Size); - - unsafe - { - BitmapData bd = bitmap.LockBits(rect, ImageLockMode.ReadOnly, AddonDataProviderConfig.PIXEL_FORMAT); - - byte* fLine = (byte*)bd.Scan0 + (frames[0].Y * bd.Stride); - int fx = frames[0].X * AddonDataProviderConfig.BYTES_PER_PIXEL; + BitmapData bd = bitmap.LockBits(rect, ImageLockMode.ReadOnly, AddonDataProviderConfig.PIXEL_FORMAT); - byte* lLine = (byte*)bd.Scan0 + (frames[^1].Y * bd.Stride); - int lx = frames[^1].X * AddonDataProviderConfig.BYTES_PER_PIXEL; + byte* fLine = (byte*)bd.Scan0 + (frames[0].Y * bd.Stride); + int fx = frames[0].X * AddonDataProviderConfig.BYTES_PER_PIXEL; - for (int i = 0; i < 3; i++) - { - if (fLine[fx + i] != AddonDataProviderConfig.fColor[i] || - lLine[lx + i] != AddonDataProviderConfig.lColor[i]) - goto Exit; - } + byte* lLine = (byte*)bd.Scan0 + (frames[^1].Y * bd.Stride); + int lx = frames[^1].X * AddonDataProviderConfig.BYTES_PER_PIXEL; - for (int i = 0; i < frames.Length; i++) - { - fLine = (byte*)bd.Scan0 + (frames[i].Y * bd.Stride); - fx = frames[i].X * AddonDataProviderConfig.BYTES_PER_PIXEL; + for (int i = 0; i < 3; i++) + { + if (fLine[fx + i] != AddonDataProviderConfig.fColor[i] || + lLine[lx + i] != AddonDataProviderConfig.lColor[i]) + goto Exit; + } - data[frames[i].Index] = (fLine[fx + 2] * 65536) + (fLine[fx + 1] * 256) + fLine[fx]; - } + for (int i = 0; i < frames.Length; i++) + { + fLine = (byte*)bd.Scan0 + (frames[i].Y * bd.Stride); + fx = frames[i].X * AddonDataProviderConfig.BYTES_PER_PIXEL; - Exit: - bitmap.UnlockBits(bd); + data[frames[i].Index] = (fLine[fx + 2] * 65536) + (fLine[fx + 1] * 256) + fLine[fx]; } + + Exit: + bitmap.UnlockBits(bd); } + } - public void InitFrames(DataFrame[] frames) - { - manualReset.Reset(); + public void InitFrames(DataFrame[] frames) + { + manualReset.Reset(); - this.frames = frames; - data = new int[this.frames.Length]; + this.frames = frames; + data = new int[this.frames.Length]; - for (int i = 0; i < this.frames.Length; i++) - { - rect.Width = Math.Max(rect.Width, frames[i].X); - rect.Height = Math.Max(rect.Height, frames[i].Y); - } - rect.Width++; - rect.Height++; + for (int i = 0; i < this.frames.Length; i++) + { + rect.Width = Math.Max(rect.Width, frames[i].X); + rect.Height = Math.Max(rect.Height, frames[i].Y); + } + rect.Width++; + rect.Height++; - bitmap = new(rect.Width, rect.Height, AddonDataProviderConfig.PIXEL_FORMAT); - graphics = Graphics.FromImage(bitmap); + bitmap = new(rect.Width, rect.Height, AddonDataProviderConfig.PIXEL_FORMAT); + graphics = Graphics.FromImage(bitmap); - manualReset.Set(); - } + manualReset.Set(); + } - public int GetInt(int index) - { - return index > data.Length ? 0 : data[index]; - } + public int GetInt(int index) + { + return index > data.Length ? 0 : data[index]; + } - public float GetFixed(int index) - { - return GetInt(index) / 100000f; - } + public float GetFixed(int index) + { + return GetInt(index) / 100000f; + } - public string GetString(int index) - { - int color = GetInt(index); - if (color == 0 || color > 999999) - return string.Empty; + public string GetString(int index) + { + int color = GetInt(index); + if (color == 0 || color > 999999) + return string.Empty; - sb.Clear(); + sb.Clear(); - int n = color / 10000; - if (n > 0) sb.Append((char)n); + int n = color / 10000; + if (n > 0) sb.Append((char)n); - n = color / 100 % 100; - if (n > 0) sb.Append((char)n); + n = color / 100 % 100; + if (n > 0) sb.Append((char)n); - n = color % 100; - if (n > 0) sb.Append((char)n); + n = color % 100; + if (n > 0) sb.Append((char)n); - return sb.ToString().Trim(); - } + return sb.ToString().Trim(); } } diff --git a/Core/AddonDataProvider/AddonDataProviderType.cs b/Core/AddonDataProvider/AddonDataProviderType.cs index 625653a80..fee1ca7e3 100644 --- a/Core/AddonDataProvider/AddonDataProviderType.cs +++ b/Core/AddonDataProvider/AddonDataProviderType.cs @@ -1,9 +1,8 @@ -namespace Core +namespace Core; + +public enum AddonDataProviderType { - public enum AddonDataProviderType - { - GDI, - DXGI, - GDIConfig - } + GDI, + DXGI, + GDIConfig } diff --git a/Core/AddonDataProvider/IAddonDataProvider.cs b/Core/AddonDataProvider/IAddonDataProvider.cs index a890c3eeb..cd95e98df 100644 --- a/Core/AddonDataProvider/IAddonDataProvider.cs +++ b/Core/AddonDataProvider/IAddonDataProvider.cs @@ -1,14 +1,13 @@ -namespace Core +namespace Core; + +public interface IAddonDataProvider { - public interface IAddonDataProvider - { - void Update(); - void InitFrames(DataFrame[] frames); + void Update(); + void InitFrames(DataFrame[] frames); - int GetInt(int index); - float GetFixed(int index); - string GetString(int index); + int GetInt(int index); + float GetFixed(int index); + string GetString(int index); - void Dispose(); - } + void Dispose(); } diff --git a/Core/Bag/Bag.cs b/Core/Bag/Bag.cs index b89e7f3a8..8124ee1d9 100644 --- a/Core/Bag/Bag.cs +++ b/Core/Bag/Bag.cs @@ -1,13 +1,12 @@ using SharedLib; -namespace Core +namespace Core; + +public sealed class Bag { - public sealed class Bag - { - public int ItemId { get; set; } - public BagType BagType { get; set; } - public int SlotCount { get; set; } - public int FreeSlot { get; set; } - public string Name { get; set; } = string.Empty; - } + public int ItemId { get; set; } + public BagType BagType { get; set; } + public int SlotCount { get; set; } + public int FreeSlot { get; set; } + public string Name { get; set; } = string.Empty; } diff --git a/Core/Bag/BagItem.cs b/Core/Bag/BagItem.cs index 0002cf60e..fd6a5b56b 100644 --- a/Core/Bag/BagItem.cs +++ b/Core/Bag/BagItem.cs @@ -2,36 +2,35 @@ using SharedLib; -namespace Core +namespace Core; + +public sealed class BagItem { - public sealed class BagItem - { - public int Bag { get; private set; } - public int ItemId { get; private set; } - public int BagIndex { get; private set; } - public int Count { get; private set; } - public int LastCount { get; private set; } - public Item Item { get; private set; } - public DateTime LastUpdated { get; set; } = DateTime.UtcNow; + public int Bag { get; private set; } + public int ItemId { get; private set; } + public int BagIndex { get; private set; } + public int Count { get; private set; } + public int LastCount { get; private set; } + public Item Item { get; private set; } + public DateTime LastUpdated { get; set; } = DateTime.UtcNow; - public int LastChange => Count - LastCount; + public int LastChange => Count - LastCount; - public BagItem(int bag, int bagIndex, int itemId, int count, Item item) - { - this.Bag = bag; - this.BagIndex = bagIndex; - this.ItemId = itemId; - this.Count = count; - this.LastCount = count; - this.Item = item; - } + public BagItem(int bag, int bagIndex, int itemId, int count, Item item) + { + this.Bag = bag; + this.BagIndex = bagIndex; + this.ItemId = itemId; + this.Count = count; + this.LastCount = count; + this.Item = item; + } - public void UpdateCount(int count) - { - LastCount = Count; - Count = count; + public void UpdateCount(int count) + { + LastCount = Count; + Count = count; - LastUpdated = DateTime.UtcNow; - } + LastUpdated = DateTime.UtcNow; } } \ No newline at end of file diff --git a/Core/Bag/BagReader.cs b/Core/Bag/BagReader.cs index 191b8e2e4..924f4a086 100644 --- a/Core/Bag/BagReader.cs +++ b/Core/Bag/BagReader.cs @@ -7,258 +7,257 @@ using System.Linq; using System.Runtime.InteropServices; -namespace Core +namespace Core; + +public sealed class BagReader : IDisposable { - public sealed class BagReader : IDisposable - { - private readonly int cBagMeta; - private readonly int cItemNumCount; - private readonly int cItemId; + private readonly int cBagMeta; + private readonly int cItemNumCount; + private readonly int cItemId; - private readonly EquipmentReader equipmentReader; + private readonly EquipmentReader equipmentReader; - public ItemDB ItemDB { get; private set; } + public ItemDB ItemDB { get; private set; } - public List BagItems { get; } = new(); + public List BagItems { get; } = new(); - public Bag[] Bags { get; } = new Bag[5]; + public Bag[] Bags { get; } = new Bag[5]; - public event Action? DataChanged; + public event Action? DataChanged; - public int Hash { private set; get; } - public int HashNewOrStackGain { private set; get; } + public int Hash { private set; get; } + public int HashNewOrStackGain { private set; get; } - public BagReader(ItemDB itemDb, EquipmentReader equipmentReader, int cbagMeta, int citemNumCount, int cItemId) - { - this.ItemDB = itemDb; - this.equipmentReader = equipmentReader; + public BagReader(ItemDB itemDb, EquipmentReader equipmentReader, int cbagMeta, int citemNumCount, int cItemId) + { + this.ItemDB = itemDb; + this.equipmentReader = equipmentReader; - this.equipmentReader.OnEquipmentChanged -= OnEquipmentChanged; - this.equipmentReader.OnEquipmentChanged += OnEquipmentChanged; + this.equipmentReader.OnEquipmentChanged -= OnEquipmentChanged; + this.equipmentReader.OnEquipmentChanged += OnEquipmentChanged; - this.cBagMeta = cbagMeta; - this.cItemNumCount = citemNumCount; - this.cItemId = cItemId; + this.cBagMeta = cbagMeta; + this.cItemNumCount = citemNumCount; + this.cItemId = cItemId; - for (int i = 0; i < Bags.Length; i++) + for (int i = 0; i < Bags.Length; i++) + { + Bags[i] = new Bag(); + if (i == 0) { - Bags[i] = new Bag(); - if (i == 0) - { - Bags[i].Name = "Backpack"; - } + Bags[i].Name = "Backpack"; } } + } - public void Dispose() - { - this.equipmentReader.OnEquipmentChanged -= OnEquipmentChanged; - } + public void Dispose() + { + this.equipmentReader.OnEquipmentChanged -= OnEquipmentChanged; + } - public void Read(IAddonDataProvider reader) - { - ReadBagMeta(reader, out bool metaChanged); + public void Read(IAddonDataProvider reader) + { + ReadBagMeta(reader, out bool metaChanged); - ReadInventory(reader, out bool inventoryChanged); + ReadInventory(reader, out bool inventoryChanged); - if (metaChanged || inventoryChanged) - { - DataChanged?.Invoke(); + if (metaChanged || inventoryChanged) + { + DataChanged?.Invoke(); - Hash++; - } + Hash++; } + } - private void ReadBagMeta(IAddonDataProvider reader, out bool changed) + private void ReadBagMeta(IAddonDataProvider reader, out bool changed) + { + int data = reader.GetInt(cBagMeta); + if (data == 0) { - int data = reader.GetInt(cBagMeta); - if (data == 0) - { - changed = false; - return; - } + changed = false; + return; + } - //bagType * 1000000 + bagNum * 100000 + freeSlots * 1000 + slotCount - int bagType = data / 1000000; - int index = data / 100000 % 10; - int freeSlots = data / 1000 % 100; - int slotCount = data % 1000; + //bagType * 1000000 + bagNum * 100000 + freeSlots * 1000 + slotCount + int bagType = data / 1000000; + int index = data / 100000 % 10; + int freeSlots = data / 1000 % 100; + int slotCount = data % 1000; - if (index >= 0 && index < Bags.Length) - { - Bag bag = Bags[index]; + if (index >= 0 && index < Bags.Length) + { + Bag bag = Bags[index]; - // default bag, the first has no equipment slot - if (index != 0) - { - bag.ItemId = equipmentReader.GetId((int)InventorySlotId.Bag_0 + index - 1); - UpdateBagName(index); - } + // default bag, the first has no equipment slot + if (index != 0) + { + bag.ItemId = equipmentReader.GetId((int)InventorySlotId.Bag_0 + index - 1); + UpdateBagName(index); + } - bag.BagType = (BagType)bagType; - bag.SlotCount = slotCount; - bag.FreeSlot = freeSlots; + bag.BagType = (BagType)bagType; + bag.SlotCount = slotCount; + bag.FreeSlot = freeSlots; - BagItems.RemoveAll(b => b.Bag == index && b.BagIndex > bag.SlotCount); + BagItems.RemoveAll(b => b.Bag == index && b.BagIndex > bag.SlotCount); - changed = true; - } - else - { - changed = false; - } + changed = true; } - - private void ReadInventory(IAddonDataProvider reader, out bool hasChanged) + else { - hasChanged = false; - - int data = reader.GetInt(cItemNumCount); - if (data == 0) return; + changed = false; + } + } - // 21 -- 0-4 bagNum + 1-21 itemNum + 1-1000 quantity - int bag = data / 1000000; - int slot = data / 10000 % 100; - int itemCount = data % 10000; + private void ReadInventory(IAddonDataProvider reader, out bool hasChanged) + { + hasChanged = false; - int itemId = reader.GetInt(cItemId); + int data = reader.GetInt(cItemNumCount); + if (data == 0) return; - BagItem? existingItem = BagItems.Where(b => b.BagIndex == slot).FirstOrDefault(b => b.Bag == bag); + // 21 -- 0-4 bagNum + 1-21 itemNum + 1-1000 quantity + int bag = data / 1000000; + int slot = data / 10000 % 100; + int itemCount = data % 10000; - if (itemCount > 0) - { - bool addItem = true; + int itemId = reader.GetInt(cItemId); - if (existingItem != null) - { - if (existingItem.ItemId != itemId) - { - BagItems.Remove(existingItem); - addItem = true; - } - else - { - addItem = false; + BagItem? existingItem = BagItems.Where(b => b.BagIndex == slot).FirstOrDefault(b => b.Bag == bag); - if (existingItem.Count != itemCount) - { - if (existingItem.Count < itemCount) - HashNewOrStackGain++; + if (itemCount > 0) + { + bool addItem = true; - existingItem.UpdateCount(itemCount); - hasChanged = true; - } - } + if (existingItem != null) + { + if (existingItem.ItemId != itemId) + { + BagItems.Remove(existingItem); + addItem = true; } - - if (addItem) + else { - hasChanged = true; + addItem = false; - if (ItemDB.Items.TryGetValue(itemId, out var item)) - { - BagItems.Add(new BagItem(bag, slot, itemId, itemCount, item)); - } - else + if (existingItem.Count != itemCount) { - BagItems.Add(new BagItem(bag, slot, itemId, itemCount, new Item() { Entry = itemId, Name = "Unknown" })); - } + if (existingItem.Count < itemCount) + HashNewOrStackGain++; - HashNewOrStackGain++; + existingItem.UpdateCount(itemCount); + hasChanged = true; + } } } - else + + if (addItem) { - if (existingItem != null) + hasChanged = true; + + if (ItemDB.Items.TryGetValue(itemId, out var item)) { - BagItems.Remove(existingItem); - hasChanged = true; + BagItems.Add(new BagItem(bag, slot, itemId, itemCount, item)); + } + else + { + BagItems.Add(new BagItem(bag, slot, itemId, itemCount, new Item() { Entry = itemId, Name = "Unknown" })); } + + HashNewOrStackGain++; + } + } + else + { + if (existingItem != null) + { + BagItems.Remove(existingItem); + hasChanged = true; } } + } - public int BagItemCount() => BagItems.Count; + public int BagItemCount() => BagItems.Count; - public int SlotCount => Bags.Sum(BagSlotCount); + public int SlotCount => Bags.Sum(BagSlotCount); - public bool BagsFull() => Bags.Sum(BagFreeSlotCount) == 0; + public bool BagsFull() => Bags.Sum(BagFreeSlotCount) == 0; - public bool AnyGreyItem() => BagItems.Any(BagItemCommonQuality); + public bool AnyGreyItem() => BagItems.Any(BagItemCommonQuality); - public int ItemCount(int itemId) - { - int count = 0; + public int ItemCount(int itemId) + { + int count = 0; - var span = CollectionsMarshal.AsSpan(BagItems); - for (int i = 0; i < span.Length; i++) + var span = CollectionsMarshal.AsSpan(BagItems); + for (int i = 0; i < span.Length; i++) + { + if (span[i].ItemId == itemId) { - if (span[i].ItemId == itemId) - { - count += span[i].Count; - } + count += span[i].Count; } - - return count; } - public bool HasItem(int itemId) => BagItems.Any(x => x.ItemId == itemId); - - public int HighestQuantityOfDrinkItemId() - { - return ItemDB.DrinkIds. - OrderByDescending(ItemCount). - FirstOrDefault(); - } + return count; + } - public int DrinkItemCount() - { - return ItemCount(HighestQuantityOfDrinkItemId()); - } + public bool HasItem(int itemId) => BagItems.Any(x => x.ItemId == itemId); - public int HighestQuantityOfFoodItemId() - { - return ItemDB.FoodIds. - OrderByDescending(ItemCount). - FirstOrDefault(); - } - public int FoodItemCount() - { - return ItemCount(HighestQuantityOfFoodItemId()); - } + public int HighestQuantityOfDrinkItemId() + { + return ItemDB.DrinkIds. + OrderByDescending(ItemCount). + FirstOrDefault(); + } - private void OnEquipmentChanged(object? s, (int, int) tuple) - { - if (tuple.Item1 is - not >= ((int)InventorySlotId.Bag_0) or - not <= ((int)InventorySlotId.Bag_3)) - { - return; - } - int index = tuple.Item1 - (int)InventorySlotId.Tabard; - Bags[index].ItemId = tuple.Item2; + public int DrinkItemCount() + { + return ItemCount(HighestQuantityOfDrinkItemId()); + } - UpdateBagName(index); - } + public int HighestQuantityOfFoodItemId() + { + return ItemDB.FoodIds. + OrderByDescending(ItemCount). + FirstOrDefault(); + } + public int FoodItemCount() + { + return ItemCount(HighestQuantityOfFoodItemId()); + } - private void UpdateBagName(int index) + private void OnEquipmentChanged(object? s, (int, int) tuple) + { + if (tuple.Item1 is + not >= ((int)InventorySlotId.Bag_0) or + not <= ((int)InventorySlotId.Bag_3)) { - Bags[index].Name = - ItemDB.Items.TryGetValue(Bags[index].ItemId, out Item item) - ? item.Name - : string.Empty; + return; } + int index = tuple.Item1 - (int)InventorySlotId.Tabard; + Bags[index].ItemId = tuple.Item2; + UpdateBagName(index); + } + + private void UpdateBagName(int index) + { + Bags[index].Name = + ItemDB.Items.TryGetValue(Bags[index].ItemId, out Item item) + ? item.Name + : string.Empty; + } - #region Helpers - private static int BagSlotCount(Bag b) => b.SlotCount; + #region Helpers - private static int BagFreeSlotCount(Bag b) => b.BagType == BagType.Unspecified ? b.FreeSlot : 0; + private static int BagSlotCount(Bag b) => b.SlotCount; - private static bool BagItemCommonQuality(BagItem bi) => bi.Item.Quality == 0; + private static int BagFreeSlotCount(Bag b) => b.BagType == BagType.Unspecified ? b.FreeSlot : 0; - public int MaxBagSlot() => Bags.Max(BagSlotCount); + private static bool BagItemCommonQuality(BagItem bi) => bi.Item.Quality == 0; - #endregion - } + public int MaxBagSlot() => Bags.Max(BagSlotCount); + + #endregion } \ No newline at end of file diff --git a/Core/Bag/BagType.cs b/Core/Bag/BagType.cs index a9defbc55..aad765a9b 100644 --- a/Core/Bag/BagType.cs +++ b/Core/Bag/BagType.cs @@ -1,20 +1,19 @@ -namespace Core +namespace Core; + +public enum BagType { - public enum BagType - { - Unspecified = 0, - Quiver = 1, - AmmoPouch = 2, - SoulBag = 4, - LeatherworkingBag = 8, - InscriptionBag = 16, - HerbBag = 32, - EnchantingBag = 64, - EngineeringBag = 128, - Keyring = 256, - GemBag = 512, - MiningBag = 1024, - Unknown = 2048, - VanityPets = 4096 - } + Unspecified = 0, + Quiver = 1, + AmmoPouch = 2, + SoulBag = 4, + LeatherworkingBag = 8, + InscriptionBag = 16, + HerbBag = 32, + EnchantingBag = 64, + EngineeringBag = 128, + Keyring = 256, + GemBag = 512, + MiningBag = 1024, + Unknown = 2048, + VanityPets = 4096 } diff --git a/Core/BotController.cs b/Core/BotController.cs index 8a4320aa3..1425d6c2e 100644 --- a/Core/BotController.cs +++ b/Core/BotController.cs @@ -17,362 +17,361 @@ using Microsoft.Extensions.DependencyInjection; using System.Numerics; -namespace Core +namespace Core; + +public sealed partial class BotController : IBotController, IDisposable { - public sealed partial class BotController : IBotController, IDisposable + private readonly WowProcess wowProcess; + private readonly WowProcessInput wowProcessInput; + private readonly ILogger logger; + private readonly IPPather pather; + private readonly MinimapNodeFinder minimapNodeFinder; + private readonly Wait wait; + private readonly ExecGameCommand execGameCommand; + private readonly DataConfig dataConfig; + + private readonly CancellationTokenSource cts; + private readonly AutoResetEvent npcNameFinderEvent; + private readonly NpcNameFinder npcNameFinder; + private readonly NpcNameTargeting npcNameTargeting; + + private readonly Thread addonThread; + + private readonly Thread screenshotThread; + private const int screenshotTickMs = 200; + + private readonly Thread? remotePathing; + private const int remotePathingTickMs = 500; + + private readonly Thread? frontendThread; + private const int frontendTickMs = 250; + + public bool IsBotActive => GoapAgent != null && GoapAgent.Active; + public AddonReader AddonReader { get; } + public WowScreen WowScreen { get; } + public IGrindSessionDAO GrindSessionDAO { get; } + + public string SelectedClassFilename { get; private set; } = string.Empty; + public string? SelectedPathFilename { get; private set; } + public ClassConfiguration? ClassConfig { get; private set; } + public GoapAgent? GoapAgent { get; private set; } + public RouteInfo? RouteInfo { get; private set; } + + public event Action? ProfileLoaded; + public event Action? StatusChanged; + + private const int SIZE = 32; + private readonly double[] ScreenLatencys = new double[SIZE]; + private readonly double[] NPCLatencys = new double[SIZE]; + + public double AvgScreenLatency => ScreenLatencys.Average(); + public double AvgNPCLatency => NPCLatencys.Average(); + + public BotController(ILogger logger, IEnvironment env, StartupClientVersion scv, + CancellationTokenSource cts, + IPPather pather, IGrindSessionDAO grindSessionDAO, DataConfig dataConfig, + WowProcess wowProcess, WowScreen wowScreen, WowProcessInput wowProcessInput, + ExecGameCommand execGameCommand, Wait wait, IAddonReader addonReader, + MinimapNodeFinder minimapNodeFinder) { - private readonly WowProcess wowProcess; - private readonly WowProcessInput wowProcessInput; - private readonly ILogger logger; - private readonly IPPather pather; - private readonly MinimapNodeFinder minimapNodeFinder; - private readonly Wait wait; - private readonly ExecGameCommand execGameCommand; - private readonly DataConfig dataConfig; - - private readonly CancellationTokenSource cts; - private readonly AutoResetEvent npcNameFinderEvent; - private readonly NpcNameFinder npcNameFinder; - private readonly NpcNameTargeting npcNameTargeting; - - private readonly Thread addonThread; - - private readonly Thread screenshotThread; - private const int screenshotTickMs = 200; - - private readonly Thread? remotePathing; - private const int remotePathingTickMs = 500; - - private readonly Thread? frontendThread; - private const int frontendTickMs = 250; - - public bool IsBotActive => GoapAgent != null && GoapAgent.Active; - public AddonReader AddonReader { get; } - public WowScreen WowScreen { get; } - public IGrindSessionDAO GrindSessionDAO { get; } - - public string SelectedClassFilename { get; private set; } = string.Empty; - public string? SelectedPathFilename { get; private set; } - public ClassConfiguration? ClassConfig { get; private set; } - public GoapAgent? GoapAgent { get; private set; } - public RouteInfo? RouteInfo { get; private set; } - - public event Action? ProfileLoaded; - public event Action? StatusChanged; - - private const int SIZE = 32; - private readonly double[] ScreenLatencys = new double[SIZE]; - private readonly double[] NPCLatencys = new double[SIZE]; - - public double AvgScreenLatency => ScreenLatencys.Average(); - public double AvgNPCLatency => NPCLatencys.Average(); - - public BotController(ILogger logger, IEnvironment env, StartupClientVersion scv, - CancellationTokenSource cts, - IPPather pather, IGrindSessionDAO grindSessionDAO, DataConfig dataConfig, - WowProcess wowProcess, WowScreen wowScreen, WowProcessInput wowProcessInput, - ExecGameCommand execGameCommand, Wait wait, IAddonReader addonReader, - MinimapNodeFinder minimapNodeFinder) + this.logger = logger; + this.pather = pather; + this.dataConfig = dataConfig; + GrindSessionDAO = grindSessionDAO; + this.wowProcess = wowProcess; + this.WowScreen = wowScreen; + this.wowProcessInput = wowProcessInput; + this.execGameCommand = execGameCommand; + this.AddonReader = (addonReader as AddonReader)!; + this.wait = wait; + this.minimapNodeFinder = minimapNodeFinder; + + this.cts = cts; + npcNameFinderEvent = new(false); + + addonThread = new(AddonThread); + addonThread.Start(); + + if (env is BlazorFrontend) { - this.logger = logger; - this.pather = pather; - this.dataConfig = dataConfig; - GrindSessionDAO = grindSessionDAO; - this.wowProcess = wowProcess; - this.WowScreen = wowScreen; - this.wowProcessInput = wowProcessInput; - this.execGameCommand = execGameCommand; - this.AddonReader = (addonReader as AddonReader)!; - this.wait = wait; - this.minimapNodeFinder = minimapNodeFinder; - - this.cts = cts; - npcNameFinderEvent = new(false); - - addonThread = new(AddonThread); - addonThread.Start(); - - if (env is BlazorFrontend) - { - WApi.Version = scv.Version; + WApi.Version = scv.Version; - frontendThread = new(FrontendThread); - frontendThread.Start(); - } + frontendThread = new(FrontendThread); + frontendThread.Start(); + } - long timestamp = Stopwatch.GetTimestamp(); - do - { - wait.Update(); + long timestamp = Stopwatch.GetTimestamp(); + do + { + wait.Update(); - if (Stopwatch.GetElapsedTime(timestamp).TotalMilliseconds > 5000) - { - logger.LogWarning("There is a problem with the addon, I have been unable to read the player class. Is it running ?"); - timestamp = Stopwatch.GetTimestamp(); - } - } while (!Enum.GetValues(typeof(UnitClass)).Cast().Contains(AddonReader.PlayerReader.Class)); + if (Stopwatch.GetElapsedTime(timestamp).TotalMilliseconds > 5000) + { + logger.LogWarning("There is a problem with the addon, I have been unable to read the player class. Is it running ?"); + timestamp = Stopwatch.GetTimestamp(); + } + } while (!Enum.GetValues(typeof(UnitClass)).Cast().Contains(AddonReader.PlayerReader.Class)); - logger.LogDebug($"Woohoo, I have read the player class. You are a {AddonReader.PlayerReader.Race.ToStringF()} {AddonReader.PlayerReader.Class.ToStringF()}."); + logger.LogDebug($"Woohoo, I have read the player class. You are a {AddonReader.PlayerReader.Race.ToStringF()} {AddonReader.PlayerReader.Class.ToStringF()}."); - npcNameFinder = new(logger, WowScreen, npcNameFinderEvent); - npcNameTargeting = new(logger, cts, WowScreen, npcNameFinder, wowProcessInput, addonReader.PlayerReader, new NoBlacklist(), wait); - WowScreen.AddDrawAction(npcNameFinder.ShowNames); - WowScreen.AddDrawAction(npcNameTargeting.ShowClickPositions); + npcNameFinder = new(logger, WowScreen, npcNameFinderEvent); + npcNameTargeting = new(logger, cts, WowScreen, npcNameFinder, wowProcessInput, addonReader.PlayerReader, new NoBlacklist(), wait); + WowScreen.AddDrawAction(npcNameFinder.ShowNames); + WowScreen.AddDrawAction(npcNameTargeting.ShowClickPositions); - screenshotThread = new(ScreenshotThread); - screenshotThread.Start(); + screenshotThread = new(ScreenshotThread); + screenshotThread.Start(); - if (pather is RemotePathingAPI) - { - remotePathing = new(RemotePathingThread); - remotePathing.Start(); - } + if (pather is RemotePathingAPI) + { + remotePathing = new(RemotePathingThread); + remotePathing.Start(); } + } - private void AddonThread() + private void AddonThread() + { + while (!cts.IsCancellationRequested) { - while (!cts.IsCancellationRequested) - { - AddonReader.Update(); - } - logger.LogWarning("Addon thread stoppped!"); + AddonReader.Update(); } + logger.LogWarning("Addon thread stoppped!"); + } - private void ScreenshotThread() - { - long timestamp; - int tickCount = 0; + private void ScreenshotThread() + { + long timestamp; + int tickCount = 0; - while (!cts.IsCancellationRequested) + while (!cts.IsCancellationRequested) + { + if (WowScreen.Enabled) { - if (WowScreen.Enabled) - { - timestamp = Stopwatch.GetTimestamp(); - WowScreen.Update(); - ScreenLatencys[tickCount % SIZE] = Stopwatch.GetElapsedTime(timestamp).TotalMilliseconds; - - timestamp = Stopwatch.GetTimestamp(); - npcNameFinder.Update(); - NPCLatencys[tickCount % SIZE] = Stopwatch.GetElapsedTime(timestamp).TotalMilliseconds; - - if (WowScreen.EnablePostProcess) - WowScreen.PostProcess(); - } - - if (ClassConfig?.Mode == Mode.AttendedGather) - { - timestamp = Stopwatch.GetTimestamp(); - WowScreen.UpdateMinimapBitmap(); - minimapNodeFinder.Update(); - ScreenLatencys[tickCount % SIZE] = Stopwatch.GetElapsedTime(timestamp).TotalMilliseconds; - } - - tickCount++; - - cts.Token.WaitHandle.WaitOne(WowScreen.Enabled || - ClassConfig?.Mode == Mode.AttendedGather ? screenshotTickMs : 4); - } + timestamp = Stopwatch.GetTimestamp(); + WowScreen.Update(); + ScreenLatencys[tickCount % SIZE] = Stopwatch.GetElapsedTime(timestamp).TotalMilliseconds; - if (logger.IsEnabled(LogLevel.Debug)) - logger.LogDebug("Screenshot thread stopped!"); - } + timestamp = Stopwatch.GetTimestamp(); + npcNameFinder.Update(); + NPCLatencys[tickCount % SIZE] = Stopwatch.GetElapsedTime(timestamp).TotalMilliseconds; - private void RemotePathingThread() - { - while (!cts.IsCancellationRequested) - { - _ = pather.DrawSphere( - new SphereArgs("Player", - AddonReader.PlayerReader.MapPos, - AddonReader.PlayerReader.Bits.PlayerInCombat() ? 1 : AddonReader.PlayerReader.Bits.HasTarget() ? 6 : 2, - AddonReader.PlayerReader.UIMapId.Value - )); - - cts.Token.WaitHandle.WaitOne(remotePathingTickMs); + if (WowScreen.EnablePostProcess) + WowScreen.PostProcess(); } - if (logger.IsEnabled(LogLevel.Debug)) - logger.LogDebug("RemotePathing thread stopped!"); - } - - private void FrontendThread() - { - while (!cts.IsCancellationRequested) + if (ClassConfig?.Mode == Mode.AttendedGather) { - AddonReader.UpdateUI(); - cts.Token.WaitHandle.WaitOne(frontendTickMs); + timestamp = Stopwatch.GetTimestamp(); + WowScreen.UpdateMinimapBitmap(); + minimapNodeFinder.Update(); + ScreenLatencys[tickCount % SIZE] = Stopwatch.GetElapsedTime(timestamp).TotalMilliseconds; } - if (logger.IsEnabled(LogLevel.Debug)) - logger.LogDebug("Frontend thread stopped!"); + tickCount++; + + cts.Token.WaitHandle.WaitOne(WowScreen.Enabled || + ClassConfig?.Mode == Mode.AttendedGather ? screenshotTickMs : 4); } - public void ToggleBotStatus() + if (logger.IsEnabled(LogLevel.Debug)) + logger.LogDebug("Screenshot thread stopped!"); + } + + private void RemotePathingThread() + { + while (!cts.IsCancellationRequested) { - if (GoapAgent == null) - return; + _ = pather.DrawSphere( + new SphereArgs("Player", + AddonReader.PlayerReader.MapPos, + AddonReader.PlayerReader.Bits.PlayerInCombat() ? 1 : AddonReader.PlayerReader.Bits.HasTarget() ? 6 : 2, + AddonReader.PlayerReader.UIMapId.Value + )); + + cts.Token.WaitHandle.WaitOne(remotePathingTickMs); + } - GoapAgent.Active = !GoapAgent.Active; + if (logger.IsEnabled(LogLevel.Debug)) + logger.LogDebug("RemotePathing thread stopped!"); + } - StatusChanged?.Invoke(); + private void FrontendThread() + { + while (!cts.IsCancellationRequested) + { + AddonReader.UpdateUI(); + cts.Token.WaitHandle.WaitOne(frontendTickMs); } - private bool InitialiseFromFile(string classFile, string? pathFile) - { - long timestamp = Stopwatch.GetTimestamp(); - try - { - ClassConfig?.Dispose(); - ClassConfig = ReadClassConfiguration(classFile, pathFile); - } - catch (Exception e) - { - logger.LogError(e.Message); - return false; - } + if (logger.IsEnabled(LogLevel.Debug)) + logger.LogDebug("Frontend thread stopped!"); + } - Initialize(ClassConfig); + public void ToggleBotStatus() + { + if (GoapAgent == null) + return; - LogProfileLoadedTime(logger, nameof(BotController), Stopwatch.GetElapsedTime(timestamp).TotalMilliseconds); + GoapAgent.Active = !GoapAgent.Active; - return true; - } + StatusChanged?.Invoke(); + } - private void Initialize(ClassConfiguration config) + private bool InitialiseFromFile(string classFile, string? pathFile) + { + long timestamp = Stopwatch.GetTimestamp(); + try { - AddonReader.SessionReset(); + ClassConfig?.Dispose(); + ClassConfig = ReadClassConfiguration(classFile, pathFile); + } + catch (Exception e) + { + logger.LogError(e.Message); + return false; + } - ConfigurableInput configInput = new(wowProcessInput, config); - GoapAgentState goapAgentState = new(); + Initialize(ClassConfig); - IServiceScope profileLoadedScope = - GoalFactory.CreateGoals(logger, AddonReader, configInput, dataConfig, npcNameFinder, - npcNameTargeting, pather, execGameCommand, config, goapAgentState, cts, wait); + LogProfileLoadedTime(logger, nameof(BotController), Stopwatch.GetElapsedTime(timestamp).TotalMilliseconds); - npcNameTargeting.UpdateBlacklist( - profileLoadedScope.ServiceProvider.GetService() - ?? profileLoadedScope.ServiceProvider.GetService()!); + return true; + } - IEnumerable availableActions = profileLoadedScope.ServiceProvider.GetServices(); - IEnumerable pathProviders = availableActions.OfType(); + private void Initialize(ClassConfiguration config) + { + AddonReader.SessionReset(); - Vector3[] mapRoute = profileLoadedScope.ServiceProvider.GetRequiredService(); - RouteInfo routeInfo = new(mapRoute, pathProviders, AddonReader.PlayerReader, AddonReader.AreaDb, AddonReader.WorldMapAreaDb); + ConfigurableInput configInput = new(wowProcessInput, config); + GoapAgentState goapAgentState = new(); - if (pather is RemotePathingAPI) - { - pather.DrawLines(new() - { - new LineArgs("grindpath", mapRoute, 2, AddonReader.PlayerReader.UIMapId.Value) - }).AsTask().Wait(); - } + IServiceScope profileLoadedScope = + GoalFactory.CreateGoals(logger, AddonReader, configInput, dataConfig, npcNameFinder, + npcNameTargeting, pather, execGameCommand, config, goapAgentState, cts, wait); - RouteInfo?.Dispose(); - RouteInfo = routeInfo; + npcNameTargeting.UpdateBlacklist( + profileLoadedScope.ServiceProvider.GetService() + ?? profileLoadedScope.ServiceProvider.GetService()!); - GoapAgent?.Dispose(); - GoapAgent = new(profileLoadedScope, dataConfig, GrindSessionDAO, WowScreen, routeInfo); - } + IEnumerable availableActions = profileLoadedScope.ServiceProvider.GetServices(); + IEnumerable pathProviders = availableActions.OfType(); + + Vector3[] mapRoute = profileLoadedScope.ServiceProvider.GetRequiredService(); + RouteInfo routeInfo = new(mapRoute, pathProviders, AddonReader.PlayerReader, AddonReader.AreaDb, AddonReader.WorldMapAreaDb); - private ClassConfiguration ReadClassConfiguration(string classFile, string? pathFile) + if (pather is RemotePathingAPI) { - string filePath = Path.Join(dataConfig.Class, classFile); + pather.DrawLines(new() + { + new LineArgs("grindpath", mapRoute, 2, AddonReader.PlayerReader.UIMapId.Value) + }).AsTask().Wait(); + } - ClassConfiguration classConfig = DeserializeObject(File.ReadAllText(filePath))!; - RequirementFactory requirementFactory = new(logger, AddonReader, npcNameFinder, classConfig.ImmunityBlacklist); - classConfig.Initialise(dataConfig, AddonReader, requirementFactory, logger, pathFile); + RouteInfo?.Dispose(); + RouteInfo = routeInfo; - LogProfileLoaded(logger, nameof(BotController), classFile, classConfig.PathFilename); + GoapAgent?.Dispose(); + GoapAgent = new(profileLoadedScope, dataConfig, GrindSessionDAO, WowScreen, routeInfo); + } - return classConfig; - } + private ClassConfiguration ReadClassConfiguration(string classFile, string? pathFile) + { + string filePath = Path.Join(dataConfig.Class, classFile); - public void Dispose() - { - cts.Cancel(); - ClassConfig?.Dispose(); - RouteInfo?.Dispose(); - GoapAgent?.Dispose(); + ClassConfiguration classConfig = DeserializeObject(File.ReadAllText(filePath))!; + RequirementFactory requirementFactory = new(logger, AddonReader, npcNameFinder, classConfig.ImmunityBlacklist); + classConfig.Initialise(dataConfig, AddonReader, requirementFactory, logger, pathFile); - npcNameTargeting.Dispose(); + LogProfileLoaded(logger, nameof(BotController), classFile, classConfig.PathFilename); - npcNameFinderEvent.Dispose(); - } + return classConfig; + } - public void MinimapNodeFound() - { - GoapAgent?.NodeFound(); - } + public void Dispose() + { + cts.Cancel(); + ClassConfig?.Dispose(); + RouteInfo?.Dispose(); + GoapAgent?.Dispose(); - public void Shutdown() - { - cts.Cancel(); - } + npcNameTargeting.Dispose(); - public IEnumerable ClassFiles() - { - var root = Path.Join(dataConfig.Class, Path.DirectorySeparatorChar.ToString()); - var files = Directory.EnumerateFiles(root, "*.json*", SearchOption.AllDirectories) - .Select(path => path.Replace(root, string.Empty)) - .OrderBy(x => x, new NaturalStringComparer()) - .ToList(); - - files.Insert(0, "Press Init State first!"); - return files; - } + npcNameFinderEvent.Dispose(); + } - public IEnumerable PathFiles() - { - var root = Path.Join(dataConfig.Path, Path.DirectorySeparatorChar.ToString()); - var files = Directory.EnumerateFiles(root, "*.json*", SearchOption.AllDirectories) - .Select(path => path.Replace(root, string.Empty)) - .OrderBy(x => x, new NaturalStringComparer()) - .ToList(); - - files.Insert(0, "Use Class Profile Default"); - return files; - } + public void MinimapNodeFound() + { + GoapAgent?.NodeFound(); + } - public void LoadClassProfile(string classFilename) - { - if (InitialiseFromFile(classFilename, SelectedPathFilename)) - { - SelectedClassFilename = classFilename; - } + public void Shutdown() + { + cts.Cancel(); + } - ProfileLoaded?.Invoke(); - } + public IEnumerable ClassFiles() + { + var root = Path.Join(dataConfig.Class, Path.DirectorySeparatorChar.ToString()); + var files = Directory.EnumerateFiles(root, "*.json*", SearchOption.AllDirectories) + .Select(path => path.Replace(root, string.Empty)) + .OrderBy(x => x, new NaturalStringComparer()) + .ToList(); + + files.Insert(0, "Press Init State first!"); + return files; + } - public void LoadPathProfile(string pathFilename) - { - if (InitialiseFromFile(SelectedClassFilename, pathFilename)) - { - SelectedPathFilename = pathFilename; - } + public IEnumerable PathFiles() + { + var root = Path.Join(dataConfig.Path, Path.DirectorySeparatorChar.ToString()); + var files = Directory.EnumerateFiles(root, "*.json*", SearchOption.AllDirectories) + .Select(path => path.Replace(root, string.Empty)) + .OrderBy(x => x, new NaturalStringComparer()) + .ToList(); + + files.Insert(0, "Use Class Profile Default"); + return files; + } - ProfileLoaded?.Invoke(); + public void LoadClassProfile(string classFilename) + { + if (InitialiseFromFile(classFilename, SelectedPathFilename)) + { + SelectedClassFilename = classFilename; } - public void OverrideClassConfig(ClassConfiguration classConfig) + ProfileLoaded?.Invoke(); + } + + public void LoadPathProfile(string pathFilename) + { + if (InitialiseFromFile(SelectedClassFilename, pathFilename)) { - this.ClassConfig = classConfig; - Initialize(this.ClassConfig); + SelectedPathFilename = pathFilename; } - #region logging + ProfileLoaded?.Invoke(); + } + + public void OverrideClassConfig(ClassConfiguration classConfig) + { + this.ClassConfig = classConfig; + Initialize(this.ClassConfig); + } - [LoggerMessage( - EventId = 200, - Level = LogLevel.Information, - Message = "[{typeName}] Elapsed time: {time} ms")] - static partial void LogProfileLoadedTime(ILogger logger, string typeName, double time); + #region logging - [LoggerMessage( - EventId = 201, - Level = LogLevel.Information, - Message = "[{typeName}] ClassConfig: {profile} with Path: {path}")] - static partial void LogProfileLoaded(ILogger logger, string typeName, string profile, string path); + [LoggerMessage( + EventId = 200, + Level = LogLevel.Information, + Message = "[{typeName}] Elapsed time: {time} ms")] + static partial void LogProfileLoadedTime(ILogger logger, string typeName, double time); - #endregion - } + [LoggerMessage( + EventId = 201, + Level = LogLevel.Information, + Message = "[{typeName}] ClassConfig: {profile} with Path: {path}")] + static partial void LogProfileLoaded(ILogger logger, string typeName, string profile, string path); + + #endregion } \ No newline at end of file diff --git a/Core/ClassConfig/ClassConfiguration.cs b/Core/ClassConfig/ClassConfiguration.cs index 368a1deab..09157d956 100644 --- a/Core/ClassConfig/ClassConfiguration.cs +++ b/Core/ClassConfig/ClassConfiguration.cs @@ -4,346 +4,345 @@ using System.IO; using System.Numerics; -namespace Core +namespace Core; + +public class BadZone { - public class BadZone - { - public int ZoneId { get; init; } = -1; - public Vector3 ExitZoneLocation { get; init; } - } + public int ZoneId { get; init; } = -1; + public Vector3 ExitZoneLocation { get; init; } +} - public enum Mode - { - Grind = 0, - CorpseRun = 1, - AttendedGather = 2, - AttendedGrind = 3, - AssistFocus = 4 - } +public enum Mode +{ + Grind = 0, + CorpseRun = 1, + AttendedGather = 2, + AttendedGrind = 3, + AssistFocus = 4 +} - public sealed class ClassConfiguration : IDisposable - { - public bool Log { get; set; } = true; - public bool Loot { get; set; } = true; - public bool Skin { get; set; } - public bool Herb { get; set; } - public bool Mine { get; set; } - public bool Salvage { get; set; } - public bool GatherCorpse => Skin || Herb || Mine || Salvage; +public sealed class ClassConfiguration : IDisposable +{ + public bool Log { get; set; } = true; + public bool Loot { get; set; } = true; + public bool Skin { get; set; } + public bool Herb { get; set; } + public bool Mine { get; set; } + public bool Salvage { get; set; } + public bool GatherCorpse => Skin || Herb || Mine || Salvage; + + public bool UseMount { get; set; } = true; + public bool KeyboardOnly { get; set; } + public bool AllowPvP { get; set; } + public bool AutoPetAttack { get; set; } = true; - public bool UseMount { get; set; } = true; - public bool KeyboardOnly { get; set; } - public bool AllowPvP { get; set; } - public bool AutoPetAttack { get; set; } = true; + public bool SpellQueue { get; set; } = true; - public bool SpellQueue { get; set; } = true; + public string PathFilename { get; set; } = string.Empty; - public string PathFilename { get; set; } = string.Empty; + public string? OverridePathFilename { get; set; } = string.Empty; - public string? OverridePathFilename { get; set; } = string.Empty; + public bool PathThereAndBack { get; set; } = true; + public bool PathReduceSteps { get; set; } - public bool PathThereAndBack { get; set; } = true; - public bool PathReduceSteps { get; set; } + public Mode Mode { get; init; } = Mode.Grind; - public Mode Mode { get; init; } = Mode.Grind; + public BadZone WrongZone { get; } = new BadZone(); - public BadZone WrongZone { get; } = new BadZone(); + public int NPCMaxLevels_Above { get; set; } = 1; + public int NPCMaxLevels_Below { get; set; } = 7; + public UnitClassification TargetMask { get; set; } = UnitClassification.Normal | UnitClassification.Trivial | UnitClassification.Rare; + public bool CheckTargetGivesExp { get; set; } + public string[] Blacklist { get; init; } = Array.Empty(); - public int NPCMaxLevels_Above { get; set; } = 1; - public int NPCMaxLevels_Below { get; set; } = 7; - public UnitClassification TargetMask { get; set; } = UnitClassification.Normal | UnitClassification.Trivial | UnitClassification.Rare; - public bool CheckTargetGivesExp { get; set; } - public string[] Blacklist { get; init; } = Array.Empty(); + public Dictionary ImmunityBlacklist { get; } = new(); - public Dictionary ImmunityBlacklist { get; } = new(); + public Dictionary IntVariables { get; } = new(); - public Dictionary IntVariables { get; } = new(); + public KeyActions Pull { get; } = new(); + public KeyActions Combat { get; } = new(); + public KeyActions Adhoc { get; } = new(); + public KeyActions Parallel { get; } = new(); + public KeyActions NPC { get; } = new(); + public WaitKeyActions Wait { get; } = new(); - public KeyActions Pull { get; } = new(); - public KeyActions Combat { get; } = new(); - public KeyActions Adhoc { get; } = new(); - public KeyActions Parallel { get; } = new(); - public KeyActions NPC { get; } = new(); - public WaitKeyActions Wait { get; } = new(); + public KeyAction[] Form { get; init; } = Array.Empty(); + public KeyAction[] GatherFindKeyConfig { get; set; } = Array.Empty(); + public string[] GatherFindKeys { get; init; } = Array.Empty(); - public KeyAction[] Form { get; init; } = Array.Empty(); - public KeyAction[] GatherFindKeyConfig { get; set; } = Array.Empty(); - public string[] GatherFindKeys { get; init; } = Array.Empty(); + public KeyAction Jump { get; } = new(); + public string JumpKey { get; init; } = "Spacebar"; - public KeyAction Jump { get; } = new(); - public string JumpKey { get; init; } = "Spacebar"; + public KeyAction Interact { get; } = new(); + public string InteractKey { get; init; } = "I"; - public KeyAction Interact { get; } = new(); - public string InteractKey { get; init; } = "I"; + public KeyAction InteractMouseOver { get; } = new(); + public string InteractMouseOverKey { get; init; } = "J"; - public KeyAction InteractMouseOver { get; } = new(); - public string InteractMouseOverKey { get; init; } = "J"; + public KeyAction Approach { get; } = new(); + public KeyAction AutoAttack { get; } = new(); - public KeyAction Approach { get; } = new(); - public KeyAction AutoAttack { get; } = new(); + public KeyAction TargetLastTarget { get; } = new(); + public string TargetLastTargetKey { get; init; } = "G"; - public KeyAction TargetLastTarget { get; } = new(); - public string TargetLastTargetKey { get; init; } = "G"; + public KeyAction StandUp { get; } = new(); + public string StandUpKey { get; init; } = "X"; - public KeyAction StandUp { get; } = new(); - public string StandUpKey { get; init; } = "X"; + public KeyAction ClearTarget { get; } = new(); + public string ClearTargetKey { get; init; } = "Insert"; - public KeyAction ClearTarget { get; } = new(); - public string ClearTargetKey { get; init; } = "Insert"; + public KeyAction StopAttack { get; } = new(); + public string StopAttackKey { get; init; } = "Delete"; - public KeyAction StopAttack { get; } = new(); - public string StopAttackKey { get; init; } = "Delete"; + public KeyAction TargetNearestTarget { get; } = new(); + public string TargetNearestTargetKey { get; init; } = "Tab"; - public KeyAction TargetNearestTarget { get; } = new(); - public string TargetNearestTargetKey { get; init; } = "Tab"; + public KeyAction TargetTargetOfTarget { get; } = new(); + public string TargetTargetOfTargetKey { get; init; } = "F"; + public KeyAction TargetPet { get; } = new(); + public string TargetPetKey { get; init; } = "Multiply"; - public KeyAction TargetTargetOfTarget { get; } = new(); - public string TargetTargetOfTargetKey { get; init; } = "F"; - public KeyAction TargetPet { get; } = new(); - public string TargetPetKey { get; init; } = "Multiply"; + public KeyAction PetAttack { get; } = new(); + public string PetAttackKey { get; init; } = "Subtract"; - public KeyAction PetAttack { get; } = new(); - public string PetAttackKey { get; init; } = "Subtract"; + public KeyAction TargetFocus { get; } = new(); + public string TargetFocusKey { get; init; } = "PageUp"; - public KeyAction TargetFocus { get; } = new(); - public string TargetFocusKey { get; init; } = "PageUp"; + public KeyAction FollowTarget { get; } = new(); + public string FollowTargetKey { get; init; } = "PageDown"; - public KeyAction FollowTarget { get; } = new(); - public string FollowTargetKey { get; init; } = "PageDown"; + public KeyAction Mount { get; } = new(); + public string MountKey { get; init; } = "O"; - public KeyAction Mount { get; } = new(); - public string MountKey { get; init; } = "O"; + public KeyAction Hearthstone { get; } = new(); + public string HearthstoneKey { get; init; } = "I"; - public KeyAction Hearthstone { get; } = new(); - public string HearthstoneKey { get; init; } = "I"; + public ConsoleKey ForwardKey { get; init; } = ConsoleKey.UpArrow; // 38 + public ConsoleKey BackwardKey { get; init; } = ConsoleKey.DownArrow; // 40 + public ConsoleKey TurnLeftKey { get; init; } = ConsoleKey.LeftArrow; // 37 + public ConsoleKey TurnRightKey { get; init; } = ConsoleKey.RightArrow; // 39 - public ConsoleKey ForwardKey { get; init; } = ConsoleKey.UpArrow; // 38 - public ConsoleKey BackwardKey { get; init; } = ConsoleKey.DownArrow; // 40 - public ConsoleKey TurnLeftKey { get; init; } = ConsoleKey.LeftArrow; // 37 - public ConsoleKey TurnRightKey { get; init; } = ConsoleKey.RightArrow; // 39 + public void Initialise(DataConfig dataConfig, AddonReader addonReader, RequirementFactory requirementFactory, ILogger logger, string? overridePathProfileFile) + { + if (!SpellQueue) + logger.LogWarning($"[{nameof(ClassConfiguration)}] {nameof(SpellQueue)} is disabled!"); + + requirementFactory.InitUserDefinedIntVariables(IntVariables, addonReader.PlayerBuffTimeReader, addonReader.TargetDebuffTimeReader, addonReader.TargetBuffTimeReader); + + Jump.Key = JumpKey; + Jump.Name = nameof(Jump); + Jump.BaseAction = true; + Jump.Initialise(this, addonReader, requirementFactory, logger, Log); + + TargetLastTarget.Key = TargetLastTargetKey; + TargetLastTarget.Name = nameof(TargetLastTarget); + TargetLastTarget.Cooldown = 0; + TargetLastTarget.BaseAction = true; + TargetLastTarget.Initialise(this, addonReader, requirementFactory, logger, Log); + + StandUp.Key = StandUpKey; + StandUp.Name = nameof(StandUp); + StandUp.Cooldown = 0; + StandUp.BaseAction = true; + StandUp.Initialise(this, addonReader, requirementFactory, logger, Log); + + ClearTarget.Key = ClearTargetKey; + ClearTarget.Name = nameof(ClearTarget); + ClearTarget.Cooldown = 0; + ClearTarget.BaseAction = true; + ClearTarget.Initialise(this, addonReader, requirementFactory, logger, Log); + + StopAttack.Key = StopAttackKey; + StopAttack.Name = nameof(StopAttack); + StopAttack.PressDuration = 20; + StopAttack.BaseAction = true; + StopAttack.Initialise(this, addonReader, requirementFactory, logger, Log); + + TargetNearestTarget.Key = TargetNearestTargetKey; + TargetNearestTarget.Name = nameof(TargetNearestTarget); + TargetNearestTarget.BaseAction = true; + TargetNearestTarget.Initialise(this, addonReader, requirementFactory, logger, Log); + + TargetPet.Key = TargetPetKey; + TargetPet.Name = nameof(TargetPet); + TargetPet.Cooldown = 0; + TargetPet.BaseAction = true; + TargetPet.Initialise(this, addonReader, requirementFactory, logger, Log); + + TargetTargetOfTarget.Key = TargetTargetOfTargetKey; + TargetTargetOfTarget.Name = nameof(TargetTargetOfTarget); + TargetTargetOfTarget.Cooldown = 0; + TargetTargetOfTarget.BaseAction = true; + TargetTargetOfTarget.Initialise(this, addonReader, requirementFactory, logger, Log); + + TargetFocus.Key = TargetFocusKey; + TargetFocus.Name = nameof(TargetFocus); + TargetFocus.Cooldown = 0; + TargetFocus.BaseAction = true; + TargetFocus.Initialise(this, addonReader, requirementFactory, logger, Log); + + FollowTarget.Key = FollowTargetKey; + FollowTarget.Name = nameof(FollowTarget); + FollowTarget.Cooldown = 0; + FollowTarget.BaseAction = true; + FollowTarget.Initialise(this, addonReader, requirementFactory, logger, Log); + + PetAttack.Key = PetAttackKey; + PetAttack.Name = nameof(PetAttack); + PetAttack.PressDuration = 10; + PetAttack.BaseAction = true; + PetAttack.Initialise(this, addonReader, requirementFactory, logger, Log); + + Mount.Key = MountKey; + Mount.Name = nameof(Mount); + Mount.BaseAction = true; + Mount.Cooldown = 6000; + Mount.Initialise(this, addonReader, requirementFactory, logger, Log); + + Hearthstone.Key = HearthstoneKey; + Hearthstone.Name = nameof(Hearthstone); + Hearthstone.HasCastBar = true; + Hearthstone.AfterCastWaitCastbar = true; + Hearthstone.Initialise(this, addonReader, requirementFactory, logger, Log); + + Interact.Key = InteractKey; + Interact.Name = nameof(Interact); + Interact.Cooldown = 0; + Interact.PressDuration = 30; + Interact.BaseAction = true; + Interact.Initialise(this, addonReader, requirementFactory, logger, Log); + + InteractMouseOver.Key = InteractMouseOverKey; + InteractMouseOver.Name = nameof(InteractMouseOver); + InteractMouseOver.Cooldown = 0; + InteractMouseOver.PressDuration = 15; + InteractMouseOver.BaseAction = true; + InteractMouseOver.Initialise(this, addonReader, requirementFactory, logger, Log); + + Approach.Key = InteractKey; + Approach.Name = nameof(Approach); + Approach.PressDuration = 10; + Approach.BaseAction = true; + Approach.Initialise(this, addonReader, requirementFactory, logger, Log); + + AutoAttack.Key = InteractKey; + AutoAttack.Name = nameof(AutoAttack); + AutoAttack.BaseAction = true; + AutoAttack.Item = true; + AutoAttack.Initialise(this, addonReader, requirementFactory, logger, Log); + + InitializeKeyActions(Pull, Interact, Approach, AutoAttack, StopAttack, PetAttack); + InitializeKeyActions(Combat, Interact, Approach, AutoAttack, StopAttack, PetAttack); + + logger.LogInformation($"[{nameof(Form)}] Initialise KeyActions."); + for (int i = 0; i < Form.Length; i++) + { + Form[i].InitialiseForm(this, addonReader, requirementFactory, logger, Log); + } - public void Initialise(DataConfig dataConfig, AddonReader addonReader, RequirementFactory requirementFactory, ILogger logger, string? overridePathProfileFile) + Pull.PreInitialise(nameof(Pull), requirementFactory, logger); + Combat.PreInitialise(nameof(Combat), requirementFactory, logger); + Adhoc.PreInitialise(nameof(Adhoc), requirementFactory, logger); + NPC.PreInitialise(nameof(NPC), requirementFactory, logger); + Parallel.PreInitialise(nameof(Parallel), requirementFactory, logger); + Wait.PreInitialise(nameof(Wait), requirementFactory, logger); + + Pull.Initialise(nameof(Pull), this, addonReader, requirementFactory, logger, Log); + Combat.Initialise(nameof(Combat), this, addonReader, requirementFactory, logger, Log); + Adhoc.Initialise(nameof(Adhoc), this, addonReader, requirementFactory, logger, Log); + NPC.Initialise(nameof(NPC), this, addonReader, requirementFactory, logger, Log); + Parallel.Initialise(nameof(Parallel), this, addonReader, requirementFactory, logger, Log); + Wait.Initialise(nameof(Wait), this, addonReader, requirementFactory, logger, Log); + + int index = 0; + GatherFindKeyConfig = new KeyAction[GatherFindKeys.Length]; + for (int i = 0; i < GatherFindKeys.Length; i++) { - if (!SpellQueue) - logger.LogWarning($"[{nameof(ClassConfiguration)}] {nameof(SpellQueue)} is disabled!"); - - requirementFactory.InitUserDefinedIntVariables(IntVariables, addonReader.PlayerBuffTimeReader, addonReader.TargetDebuffTimeReader, addonReader.TargetBuffTimeReader); - - Jump.Key = JumpKey; - Jump.Name = nameof(Jump); - Jump.BaseAction = true; - Jump.Initialise(this, addonReader, requirementFactory, logger, Log); - - TargetLastTarget.Key = TargetLastTargetKey; - TargetLastTarget.Name = nameof(TargetLastTarget); - TargetLastTarget.Cooldown = 0; - TargetLastTarget.BaseAction = true; - TargetLastTarget.Initialise(this, addonReader, requirementFactory, logger, Log); - - StandUp.Key = StandUpKey; - StandUp.Name = nameof(StandUp); - StandUp.Cooldown = 0; - StandUp.BaseAction = true; - StandUp.Initialise(this, addonReader, requirementFactory, logger, Log); - - ClearTarget.Key = ClearTargetKey; - ClearTarget.Name = nameof(ClearTarget); - ClearTarget.Cooldown = 0; - ClearTarget.BaseAction = true; - ClearTarget.Initialise(this, addonReader, requirementFactory, logger, Log); - - StopAttack.Key = StopAttackKey; - StopAttack.Name = nameof(StopAttack); - StopAttack.PressDuration = 20; - StopAttack.BaseAction = true; - StopAttack.Initialise(this, addonReader, requirementFactory, logger, Log); - - TargetNearestTarget.Key = TargetNearestTargetKey; - TargetNearestTarget.Name = nameof(TargetNearestTarget); - TargetNearestTarget.BaseAction = true; - TargetNearestTarget.Initialise(this, addonReader, requirementFactory, logger, Log); - - TargetPet.Key = TargetPetKey; - TargetPet.Name = nameof(TargetPet); - TargetPet.Cooldown = 0; - TargetPet.BaseAction = true; - TargetPet.Initialise(this, addonReader, requirementFactory, logger, Log); - - TargetTargetOfTarget.Key = TargetTargetOfTargetKey; - TargetTargetOfTarget.Name = nameof(TargetTargetOfTarget); - TargetTargetOfTarget.Cooldown = 0; - TargetTargetOfTarget.BaseAction = true; - TargetTargetOfTarget.Initialise(this, addonReader, requirementFactory, logger, Log); - - TargetFocus.Key = TargetFocusKey; - TargetFocus.Name = nameof(TargetFocus); - TargetFocus.Cooldown = 0; - TargetFocus.BaseAction = true; - TargetFocus.Initialise(this, addonReader, requirementFactory, logger, Log); - - FollowTarget.Key = FollowTargetKey; - FollowTarget.Name = nameof(FollowTarget); - FollowTarget.Cooldown = 0; - FollowTarget.BaseAction = true; - FollowTarget.Initialise(this, addonReader, requirementFactory, logger, Log); - - PetAttack.Key = PetAttackKey; - PetAttack.Name = nameof(PetAttack); - PetAttack.PressDuration = 10; - PetAttack.BaseAction = true; - PetAttack.Initialise(this, addonReader, requirementFactory, logger, Log); - - Mount.Key = MountKey; - Mount.Name = nameof(Mount); - Mount.BaseAction = true; - Mount.Cooldown = 6000; - Mount.Initialise(this, addonReader, requirementFactory, logger, Log); - - Hearthstone.Key = HearthstoneKey; - Hearthstone.Name = nameof(Hearthstone); - Hearthstone.HasCastBar = true; - Hearthstone.AfterCastWaitCastbar = true; - Hearthstone.Initialise(this, addonReader, requirementFactory, logger, Log); - - Interact.Key = InteractKey; - Interact.Name = nameof(Interact); - Interact.Cooldown = 0; - Interact.PressDuration = 30; - Interact.BaseAction = true; - Interact.Initialise(this, addonReader, requirementFactory, logger, Log); - - InteractMouseOver.Key = InteractMouseOverKey; - InteractMouseOver.Name = nameof(InteractMouseOver); - InteractMouseOver.Cooldown = 0; - InteractMouseOver.PressDuration = 15; - InteractMouseOver.BaseAction = true; - InteractMouseOver.Initialise(this, addonReader, requirementFactory, logger, Log); - - Approach.Key = InteractKey; - Approach.Name = nameof(Approach); - Approach.PressDuration = 10; - Approach.BaseAction = true; - Approach.Initialise(this, addonReader, requirementFactory, logger, Log); - - AutoAttack.Key = InteractKey; - AutoAttack.Name = nameof(AutoAttack); - AutoAttack.BaseAction = true; - AutoAttack.Item = true; - AutoAttack.Initialise(this, addonReader, requirementFactory, logger, Log); - - InitializeKeyActions(Pull, Interact, Approach, AutoAttack, StopAttack, PetAttack); - InitializeKeyActions(Combat, Interact, Approach, AutoAttack, StopAttack, PetAttack); - - logger.LogInformation($"[{nameof(Form)}] Initialise KeyActions."); - for (int i = 0; i < Form.Length; i++) + GatherFindKeyConfig[index] = new KeyAction { - Form[i].InitialiseForm(this, addonReader, requirementFactory, logger, Log); - } + Key = GatherFindKeys[index], + Name = $"Profession {index}" + }; + GatherFindKeyConfig[index].Initialise(this, addonReader, requirementFactory, logger, Log); + index++; + } - Pull.PreInitialise(nameof(Pull), requirementFactory, logger); - Combat.PreInitialise(nameof(Combat), requirementFactory, logger); - Adhoc.PreInitialise(nameof(Adhoc), requirementFactory, logger); - NPC.PreInitialise(nameof(NPC), requirementFactory, logger); - Parallel.PreInitialise(nameof(Parallel), requirementFactory, logger); - Wait.PreInitialise(nameof(Wait), requirementFactory, logger); - - Pull.Initialise(nameof(Pull), this, addonReader, requirementFactory, logger, Log); - Combat.Initialise(nameof(Combat), this, addonReader, requirementFactory, logger, Log); - Adhoc.Initialise(nameof(Adhoc), this, addonReader, requirementFactory, logger, Log); - NPC.Initialise(nameof(NPC), this, addonReader, requirementFactory, logger, Log); - Parallel.Initialise(nameof(Parallel), this, addonReader, requirementFactory, logger, Log); - Wait.Initialise(nameof(Wait), this, addonReader, requirementFactory, logger, Log); - - int index = 0; - GatherFindKeyConfig = new KeyAction[GatherFindKeys.Length]; - for (int i = 0; i < GatherFindKeys.Length; i++) - { - GatherFindKeyConfig[index] = new KeyAction - { - Key = GatherFindKeys[index], - Name = $"Profession {index}" - }; - GatherFindKeyConfig[index].Initialise(this, addonReader, requirementFactory, logger, Log); - index++; - } + OverridePathFilename = overridePathProfileFile; + if (!string.IsNullOrEmpty(OverridePathFilename)) + { + PathFilename = OverridePathFilename; + } - OverridePathFilename = overridePathProfileFile; + if (!File.Exists(Path.Join(dataConfig.Path, PathFilename))) + { if (!string.IsNullOrEmpty(OverridePathFilename)) - { - PathFilename = OverridePathFilename; - } + throw new Exception($"[{nameof(ClassConfiguration)}] `{OverridePathFilename}` file does not exists!"); + else + throw new Exception($"[{nameof(ClassConfiguration)}] `{PathFilename}` file does not exists!"); + } - if (!File.Exists(Path.Join(dataConfig.Path, PathFilename))) - { - if (!string.IsNullOrEmpty(OverridePathFilename)) - throw new Exception($"[{nameof(ClassConfiguration)}] `{OverridePathFilename}` file does not exists!"); - else - throw new Exception($"[{nameof(ClassConfiguration)}] `{PathFilename}` file does not exists!"); - } + CheckConfigConsistency(logger); + } - CheckConfigConsistency(logger); - } + public void Dispose() + { + Pull.Dispose(); + Combat.Dispose(); + Parallel.Dispose(); + Adhoc.Dispose(); + NPC.Dispose(); + Wait.Dispose(); + } - public void Dispose() + private void CheckConfigConsistency(ILogger logger) + { + if (CheckTargetGivesExp) { - Pull.Dispose(); - Combat.Dispose(); - Parallel.Dispose(); - Adhoc.Dispose(); - NPC.Dispose(); - Wait.Dispose(); + logger.LogWarning($"{nameof(CheckTargetGivesExp)} is enabled. {nameof(NPCMaxLevels_Above)} and {nameof(NPCMaxLevels_Below)} ignored!"); } - - private void CheckConfigConsistency(ILogger logger) + if (KeyboardOnly) { - if (CheckTargetGivesExp) - { - logger.LogWarning($"{nameof(CheckTargetGivesExp)} is enabled. {nameof(NPCMaxLevels_Above)} and {nameof(NPCMaxLevels_Below)} ignored!"); - } - if (KeyboardOnly) - { - logger.LogWarning($"{nameof(KeyboardOnly)} mode is enabled. Mouse based actions ignored."); + logger.LogWarning($"{nameof(KeyboardOnly)} mode is enabled. Mouse based actions ignored."); - if (GatherCorpse) - logger.LogWarning($"{nameof(GatherCorpse)} limited to the last target. Rest going to be skipped!"); - } + if (GatherCorpse) + logger.LogWarning($"{nameof(GatherCorpse)} limited to the last target. Rest going to be skipped!"); } + } - private static void InitializeKeyActions(KeyActions userActions, params KeyAction[] defaultActions) + private static void InitializeKeyActions(KeyActions userActions, params KeyAction[] defaultActions) + { + KeyAction dummyDefault = new(); + for (int i = 0; i < userActions.Sequence.Length; i++) { - KeyAction dummyDefault = new(); - for (int i = 0; i < userActions.Sequence.Length; i++) + KeyAction user = userActions.Sequence[i]; + for (int d = 0; d < defaultActions.Length; d++) { - KeyAction user = userActions.Sequence[i]; - for (int d = 0; d < defaultActions.Length; d++) - { - KeyAction @default = defaultActions[d]; + KeyAction @default = defaultActions[d]; - if (user.Name != @default.Name) - continue; + if (user.Name != @default.Name) + continue; - user.Key = @default.Key; + user.Key = @default.Key; - //if (!string.IsNullOrEmpty(@default.Requirement)) - // user.Requirement += " " + @default.Requirement; - //user.Requirements.AddRange(@default.Requirements); + //if (!string.IsNullOrEmpty(@default.Requirement)) + // user.Requirement += " " + @default.Requirement; + //user.Requirements.AddRange(@default.Requirements); - if (user.AfterCastDelay == dummyDefault.AfterCastDelay) - user.AfterCastDelay = @default.AfterCastDelay; + if (user.AfterCastDelay == dummyDefault.AfterCastDelay) + user.AfterCastDelay = @default.AfterCastDelay; - if (user.PressDuration == dummyDefault.PressDuration) - user.PressDuration = @default.PressDuration; + if (user.PressDuration == dummyDefault.PressDuration) + user.PressDuration = @default.PressDuration; - if (user.Cooldown == dummyDefault.Cooldown) - user.Cooldown = @default.Cooldown; + if (user.Cooldown == dummyDefault.Cooldown) + user.Cooldown = @default.Cooldown; - if (user.BaseAction == dummyDefault.BaseAction) - user.BaseAction = @default.BaseAction; + if (user.BaseAction == dummyDefault.BaseAction) + user.BaseAction = @default.BaseAction; - if (user.Item == dummyDefault.Item) - user.Item = @default.Item; - } + if (user.Item == dummyDefault.Item) + user.Item = @default.Item; } } } diff --git a/Core/ClassConfig/KeyAction.cs b/Core/ClassConfig/KeyAction.cs index 83fcaf714..d71abf6b5 100644 --- a/Core/ClassConfig/KeyAction.cs +++ b/Core/ClassConfig/KeyAction.cs @@ -6,441 +6,440 @@ using System.Collections.Generic; using System.Numerics; -namespace Core +namespace Core; + +public sealed partial class KeyAction : IDisposable { - public sealed partial class KeyAction : IDisposable - { - public float Cost { get; set; } = 18; - public string Name { get; set; } = string.Empty; - public bool HasCastBar { get; set; } - public ConsoleKey ConsoleKey { get; set; } - public string Key { get; set; } = string.Empty; - public int Slot { get; set; } - public int SlotIndex { get; private set; } - public int PressDuration { get; set; } = 50; - public string Form { get; set; } = string.Empty; - public Form FormEnum { get; set; } = Core.Form.None; - public bool FormAction { get; private set; } - public int Cooldown { get; set; } = CastingHandler.SPELL_QUEUE; + public float Cost { get; set; } = 18; + public string Name { get; set; } = string.Empty; + public bool HasCastBar { get; set; } + public ConsoleKey ConsoleKey { get; set; } + public string Key { get; set; } = string.Empty; + public int Slot { get; set; } + public int SlotIndex { get; private set; } + public int PressDuration { get; set; } = 50; + public string Form { get; set; } = string.Empty; + public Form FormEnum { get; set; } = Core.Form.None; + public bool FormAction { get; private set; } + public int Cooldown { get; set; } = CastingHandler.SPELL_QUEUE; - private int _charge; - public int Charge { get; set; } = 1; - public SchoolMask School { get; set; } = SchoolMask.None; - public int MinMana { get; set; } - public int MinRage { get; set; } - public int MinEnergy { get; set; } - public int MinRunicPower { get; set; } - public int MinRuneBlood { get; set; } - public int MinRuneFrost { get; set; } - public int MinRuneUnholy { get; set; } - public int MinComboPoints { get; set; } + private int _charge; + public int Charge { get; set; } = 1; + public SchoolMask School { get; set; } = SchoolMask.None; + public int MinMana { get; set; } + public int MinRage { get; set; } + public int MinEnergy { get; set; } + public int MinRunicPower { get; set; } + public int MinRuneBlood { get; set; } + public int MinRuneFrost { get; set; } + public int MinRuneUnholy { get; set; } + public int MinComboPoints { get; set; } - public int MinCost { get; set; } + public int MinCost { get; set; } - public Func FormCost = null!; + public Func FormCost = null!; - public bool HasFormRequirement { get; private set; } + public bool HasFormRequirement { get; private set; } - public string Requirement { get; set; } = string.Empty; - public List Requirements { get; } = new(); - public Requirement[] RequirementsRuntime { get; set; } = Array.Empty(); + public string Requirement { get; set; } = string.Empty; + public List Requirements { get; } = new(); + public Requirement[] RequirementsRuntime { get; set; } = Array.Empty(); - public string Interrupt { get; set; } = string.Empty; - public List Interrupts { get; } = new(); - public Requirement[] InterruptsRuntime { get; set; } = Array.Empty(); + public string Interrupt { get; set; } = string.Empty; + public List Interrupts { get; } = new(); + public Requirement[] InterruptsRuntime { get; set; } = Array.Empty(); - public bool WhenUsable { get; set; } + public bool WhenUsable { get; set; } - public bool ResetOnNewTarget { get; set; } + public bool ResetOnNewTarget { get; set; } - public bool Log { get; set; } = true; + public bool Log { get; set; } = true; - public bool BaseAction { get; set; } + public bool BaseAction { get; set; } - public bool Item { get; set; } + public bool Item { get; set; } - public int BeforeCastDelay { get; set; } - public bool BeforeCastStop { get; set; } - public bool BeforeCastDismount { get; set; } = true; + public int BeforeCastDelay { get; set; } + public bool BeforeCastStop { get; set; } + public bool BeforeCastDismount { get; set; } = true; - public int AfterCastDelay { get; set; } - public bool AfterCastWaitMeleeRange { get; set; } - public bool AfterCastWaitBuff { get; set; } - public bool AfterCastWaitBag { get; set; } - public bool AfterCastWaitSwing { get; set; } - public bool AfterCastWaitCastbar { get; set; } - public bool AfterCastWaitCombat { get; set; } - public bool AfterCastWaitGCD { get; set; } - public bool AfterCastAuraExpected { get; set; } - public int AfterCastStepBack { get; set; } + public int AfterCastDelay { get; set; } + public bool AfterCastWaitMeleeRange { get; set; } + public bool AfterCastWaitBuff { get; set; } + public bool AfterCastWaitBag { get; set; } + public bool AfterCastWaitSwing { get; set; } + public bool AfterCastWaitCastbar { get; set; } + public bool AfterCastWaitCombat { get; set; } + public bool AfterCastWaitGCD { get; set; } + public bool AfterCastAuraExpected { get; set; } + public int AfterCastStepBack { get; set; } - public string InCombat { get; set; } = "false"; + public string InCombat { get; set; } = "false"; - public bool? UseWhenTargetIsCasting { get; set; } + public bool? UseWhenTargetIsCasting { get; set; } - public string PathFilename { get; set; } = string.Empty; - public Vector3[] Path { get; set; } = Array.Empty(); + public string PathFilename { get; set; } = string.Empty; + public Vector3[] Path { get; set; } = Array.Empty(); - public int ConsoleKeyFormHash { private set; get; } + public int ConsoleKeyFormHash { private set; get; } - private DateTime LastClicked = DateTime.UtcNow.AddDays(-1); + private DateTime LastClicked = DateTime.UtcNow.AddDays(-1); - private static int LastKey; - private static DateTime LastKeyTime; + private static int LastKey; + private static DateTime LastKeyTime; - public static int LastKeyClicked() - { - return (DateTime.UtcNow - LastKeyTime).TotalSeconds > 2 - ? (int)ConsoleKey.NoName - : LastKey; - } + public static int LastKeyClicked() + { + return (DateTime.UtcNow - LastKeyTime).TotalSeconds > 2 + ? (int)ConsoleKey.NoName + : LastKey; + } - private ActionBarCostReader costReader = null!; - private ILogger logger = null!; + private ActionBarCostReader costReader = null!; + private ILogger logger = null!; - private RecordInt globalTime = null!; - private int canRunMemoTime; - private bool canRun; + private RecordInt globalTime = null!; + private int canRunMemoTime; + private bool canRun; - public void InitialiseSlot(ILogger logger) + public void InitialiseSlot(ILogger logger) + { + if (!KeyReader.ReadKey(logger, this) && !BaseAction) { - if (!KeyReader.ReadKey(logger, this) && !BaseAction) - { - LogInputNoValidKey(logger, Name, Key, ConsoleKey); - } - else if (Slot == 0) - { - LogInputNonActionbar(logger, Name, Key, ConsoleKey); - } + LogInputNoValidKey(logger, Name, Key, ConsoleKey); } - - public void InitDynamicBinding(RequirementFactory requirementFactory) + else if (Slot == 0) { - requirementFactory.InitDynamicBindings(this); + LogInputNonActionbar(logger, Name, Key, ConsoleKey); } + } - public void Initialise(ClassConfiguration config, AddonReader addonReader, - RequirementFactory requirementFactory, ILogger logger, bool globalLog) - { - this.costReader = addonReader.ActionBarCostReader; - this.logger = logger; + public void InitDynamicBinding(RequirementFactory requirementFactory) + { + requirementFactory.InitDynamicBindings(this); + } - globalTime = addonReader.GlobalTime; - this.canRunMemoTime = globalTime.Value; + public void Initialise(ClassConfiguration config, AddonReader addonReader, + RequirementFactory requirementFactory, ILogger logger, bool globalLog) + { + this.costReader = addonReader.ActionBarCostReader; + this.logger = logger; - FormCost = GetMinCost; + globalTime = addonReader.GlobalTime; + this.canRunMemoTime = globalTime.Value; - if (!globalLog) - Log = false; + FormCost = GetMinCost; - ResetCharges(); + if (!globalLog) + Log = false; - InitialiseSlot(logger); + ResetCharges(); - if (!string.IsNullOrEmpty(Requirement)) - { - Requirements.Add(Requirement); - } + InitialiseSlot(logger); - if (!string.IsNullOrEmpty(Interrupt)) - { - Interrupts.Add(Interrupt); - } + if (!string.IsNullOrEmpty(Requirement)) + { + Requirements.Add(Requirement); + } - HasFormRequirement = !string.IsNullOrEmpty(Form); + if (!string.IsNullOrEmpty(Interrupt)) + { + Interrupts.Add(Interrupt); + } - if (HasFormRequirement) + HasFormRequirement = !string.IsNullOrEmpty(Form); + + if (HasFormRequirement) + { + if (Enum.TryParse(Form, out Form desiredForm)) { - if (Enum.TryParse(Form, out Form desiredForm)) - { - this.FormEnum = desiredForm; - LogFormRequired(logger, Name, FormEnum.ToStringF()); + this.FormEnum = desiredForm; + LogFormRequired(logger, Name, FormEnum.ToStringF()); - if (!FormAction) + if (!FormAction) + { + for (int i = 0; i < config.Form.Length; i++) { - for (int i = 0; i < config.Form.Length; i++) + if (config.Form[i].FormEnum == FormEnum) { - if (config.Form[i].FormEnum == FormEnum) - { - FormCost = config.Form[i].FormCost; - } + FormCost = config.Form[i].FormCost; } } } - else - { - throw new Exception($"[{Name}] Unknown form: {Form}"); - } - } - - if (Slot > 0) - { - this.SlotIndex = Stance.ToSlot(this, addonReader.PlayerReader) - 1; - LogInputActionbar(logger, Name, Key, Slot, SlotIndex); } - - ConsoleKeyFormHash = ((int)FormEnum * 1000) + (int)ConsoleKey; - ResetCooldown(); - - if (Slot > 0) - InitMinPowerType(addonReader.ActionBarCostReader); - - requirementFactory.InitialiseRequirements(this); - - if (!string.IsNullOrEmpty(PathFilename)) + else { - LogPath(logger, Name, PathFilename); + throw new Exception($"[{Name}] Unknown form: {Form}"); } } - public void Dispose() + if (Slot > 0) { - costReader.OnActionCostChanged -= ActionBarCostReader_OnActionCostChanged; - costReader.OnActionCostReset -= ResetCosts; + this.SlotIndex = Stance.ToSlot(this, addonReader.PlayerReader) - 1; + LogInputActionbar(logger, Name, Key, Slot, SlotIndex); } - public void InitialiseForm(ClassConfiguration config, AddonReader addonReader, RequirementFactory requirementFactory, ILogger logger, bool globalLog) - { - FormAction = true; - Initialise(config, addonReader, requirementFactory, logger, globalLog); - - logger.LogInformation($"[{Name}] Added {FormEnum} to FormCost with {MinCost}"); - } + ConsoleKeyFormHash = ((int)FormEnum * 1000) + (int)ConsoleKey; + ResetCooldown(); - public void ResetCosts() - { - MinCost = 0; + if (Slot > 0) + InitMinPowerType(addonReader.ActionBarCostReader); - MinMana = 0; - MinRage = 0; - MinEnergy = 0; - MinRunicPower = 0; + requirementFactory.InitialiseRequirements(this); - MinRuneBlood = 0; - MinRuneFrost = 0; - MinRuneUnholy = 0; - } - - public int GetRemainingCooldown() + if (!string.IsNullOrEmpty(PathFilename)) { - return Math.Max(Cooldown - SinceLastClickMs, 0); + LogPath(logger, Name, PathFilename); } + } - public void SetClicked(double offset = 0) - { - LastKey = ConsoleKeyFormHash; - LastKeyTime = LastClicked = DateTime.UtcNow.AddMilliseconds(offset); - } + public void Dispose() + { + costReader.OnActionCostChanged -= ActionBarCostReader_OnActionCostChanged; + costReader.OnActionCostReset -= ResetCosts; + } - public int SinceLastClickMs => - (int)(DateTime.UtcNow - LastClicked).TotalMilliseconds; + public void InitialiseForm(ClassConfiguration config, AddonReader addonReader, RequirementFactory requirementFactory, ILogger logger, bool globalLog) + { + FormAction = true; + Initialise(config, addonReader, requirementFactory, logger, globalLog); - public void ResetCooldown() - { - LastClicked = DateTime.UtcNow.AddDays(-1); - } + logger.LogInformation($"[{Name}] Added {FormEnum} to FormCost with {MinCost}"); + } - public int GetChargeRemaining() - { - return _charge; - } + public void ResetCosts() + { + MinCost = 0; - public void ConsumeCharge() - { - if (Charge > 1) - { - _charge--; - if (_charge > 0) - { - ResetCooldown(); - } - else - { - ResetCharges(); - SetClicked(); - } - } - } + MinMana = 0; + MinRage = 0; + MinEnergy = 0; + MinRunicPower = 0; - public void ResetCharges() - { - _charge = Charge; - } + MinRuneBlood = 0; + MinRuneFrost = 0; + MinRuneUnholy = 0; + } - public bool CanRun() - { - if (canRunMemoTime == globalTime.Value) - return canRun; + public int GetRemainingCooldown() + { + return Math.Max(Cooldown - SinceLastClickMs, 0); + } - canRunMemoTime = globalTime.Value; + public void SetClicked(double offset = 0) + { + LastKey = ConsoleKeyFormHash; + LastKeyTime = LastClicked = DateTime.UtcNow.AddMilliseconds(offset); + } - Span span = RequirementsRuntime; - for (int i = 0; i < span.Length; i++) - { - if (!span[i].HasRequirement()) - return canRun = false; - } + public int SinceLastClickMs => + (int)(DateTime.UtcNow - LastClicked).TotalMilliseconds; - return canRun = true; - } + public void ResetCooldown() + { + LastClicked = DateTime.UtcNow.AddDays(-1); + } + + public int GetChargeRemaining() + { + return _charge; + } - public bool CanBeInterrupted() + public void ConsumeCharge() + { + if (Charge > 1) { - Span span = InterruptsRuntime; - for (int i = 0; i < span.Length; i++) + _charge--; + if (_charge > 0) { - if (!span[i].HasRequirement()) - return false; + ResetCooldown(); + } + else + { + ResetCharges(); + SetClicked(); } - - return true; } + } - private void InitMinPowerType(ActionBarCostReader actionBarCostReader) - { - for (int i = 0; i < ActionBar.NUM_OF_COST; i++) - { - ActionBarCost abc = actionBarCostReader.Get(this, i); - if (abc.Cost == 0) - continue; + public void ResetCharges() + { + _charge = Charge; + } - int oldValue = 0; - switch (abc.PowerType) - { - case PowerType.Mana: - oldValue = MinMana; - MinMana = abc.Cost; - break; - case PowerType.Rage: - oldValue = MinRage; - MinRage = abc.Cost; - break; - case PowerType.Energy: - oldValue = MinEnergy; - MinEnergy = abc.Cost; - break; - case PowerType.RunicPower: - oldValue = MinRunicPower; - MinRunicPower = abc.Cost; - break; - case PowerType.RuneBlood: - oldValue = MinRuneBlood; - MinRuneBlood = abc.Cost; - break; - case PowerType.RuneFrost: - oldValue = MinRuneFrost; - MinRuneFrost = abc.Cost; - break; - case PowerType.RuneUnholy: - oldValue = MinRuneUnholy; - MinRuneUnholy = abc.Cost; - break; - } + public bool CanRun() + { + if (canRunMemoTime == globalTime.Value) + return canRun; - MinCost = abc.Cost; + canRunMemoTime = globalTime.Value; - LogPowerCostChange(logger, Name, abc.PowerType.ToStringF(), abc.Cost, oldValue); - if (HasFormRequirement && FormEnum != Core.Form.None) - { - int formCost = FormCost(); - if (formCost > 0) - { - logger.LogInformation($"[{Name}] +{formCost} Mana to change into {FormEnum.ToStringF()}"); - } - } - } - actionBarCostReader.OnActionCostChanged += ActionBarCostReader_OnActionCostChanged; - actionBarCostReader.OnActionCostReset += ResetCosts; + Span span = RequirementsRuntime; + for (int i = 0; i < span.Length; i++) + { + if (!span[i].HasRequirement()) + return canRun = false; } - private void ActionBarCostReader_OnActionCostChanged(object? sender, ActionBarCostEventArgs e) + return canRun = true; + } + + public bool CanBeInterrupted() + { + Span span = InterruptsRuntime; + for (int i = 0; i < span.Length; i++) { - if (Slot != e.Slot) return; + if (!span[i].HasRequirement()) + return false; + } - MinCost = e.ActionBarCost.Cost; + return true; + } + + private void InitMinPowerType(ActionBarCostReader actionBarCostReader) + { + for (int i = 0; i < ActionBar.NUM_OF_COST; i++) + { + ActionBarCost abc = actionBarCostReader.Get(this, i); + if (abc.Cost == 0) + continue; int oldValue = 0; - switch (e.ActionBarCost.PowerType) + switch (abc.PowerType) { case PowerType.Mana: oldValue = MinMana; - MinMana = e.ActionBarCost.Cost; + MinMana = abc.Cost; break; case PowerType.Rage: oldValue = MinRage; - MinRage = e.ActionBarCost.Cost; + MinRage = abc.Cost; break; case PowerType.Energy: oldValue = MinEnergy; - MinEnergy = e.ActionBarCost.Cost; + MinEnergy = abc.Cost; break; case PowerType.RunicPower: oldValue = MinRunicPower; - MinRunicPower = e.ActionBarCost.Cost; + MinRunicPower = abc.Cost; break; case PowerType.RuneBlood: oldValue = MinRuneBlood; - MinRuneBlood = e.ActionBarCost.Cost; + MinRuneBlood = abc.Cost; break; case PowerType.RuneFrost: oldValue = MinRuneFrost; - MinRuneFrost = e.ActionBarCost.Cost; + MinRuneFrost = abc.Cost; break; case PowerType.RuneUnholy: oldValue = MinRuneUnholy; - MinRuneUnholy = e.ActionBarCost.Cost; + MinRuneUnholy = abc.Cost; break; } - if (e.ActionBarCost.Cost != oldValue) + MinCost = abc.Cost; + + LogPowerCostChange(logger, Name, abc.PowerType.ToStringF(), abc.Cost, oldValue); + if (HasFormRequirement && FormEnum != Core.Form.None) { - LogPowerCostChange(logger, Name, e.ActionBarCost.PowerType.ToStringF(), e.ActionBarCost.Cost, oldValue); + int formCost = FormCost(); + if (formCost > 0) + { + logger.LogInformation($"[{Name}] +{formCost} Mana to change into {FormEnum.ToStringF()}"); + } } } + actionBarCostReader.OnActionCostChanged += ActionBarCostReader_OnActionCostChanged; + actionBarCostReader.OnActionCostReset += ResetCosts; + } + + private void ActionBarCostReader_OnActionCostChanged(object? sender, ActionBarCostEventArgs e) + { + if (Slot != e.Slot) return; + + MinCost = e.ActionBarCost.Cost; + + int oldValue = 0; + switch (e.ActionBarCost.PowerType) + { + case PowerType.Mana: + oldValue = MinMana; + MinMana = e.ActionBarCost.Cost; + break; + case PowerType.Rage: + oldValue = MinRage; + MinRage = e.ActionBarCost.Cost; + break; + case PowerType.Energy: + oldValue = MinEnergy; + MinEnergy = e.ActionBarCost.Cost; + break; + case PowerType.RunicPower: + oldValue = MinRunicPower; + MinRunicPower = e.ActionBarCost.Cost; + break; + case PowerType.RuneBlood: + oldValue = MinRuneBlood; + MinRuneBlood = e.ActionBarCost.Cost; + break; + case PowerType.RuneFrost: + oldValue = MinRuneFrost; + MinRuneFrost = e.ActionBarCost.Cost; + break; + case PowerType.RuneUnholy: + oldValue = MinRuneUnholy; + MinRuneUnholy = e.ActionBarCost.Cost; + break; + } - private int GetMinCost() + if (e.ActionBarCost.Cost != oldValue) { - return MinCost; + LogPowerCostChange(logger, Name, e.ActionBarCost.PowerType.ToStringF(), e.ActionBarCost.Cost, oldValue); } + } - #region Logging - - [LoggerMessage( - EventId = 4, - Level = LogLevel.Information, - Message = "[{name}] Path: {path}")] - static partial void LogPath(ILogger logger, string name, string path); - - [LoggerMessage( - EventId = 5, - Level = LogLevel.Information, - Message = "[{name}] Required Form: {form}")] - static partial void LogFormRequired(ILogger logger, string name, string form); - - [LoggerMessage( - EventId = 6, - Level = LogLevel.Information, - Message = "[{name}] Actionbar Key:{key} -> Actionbar:{slot} -> Index:{slotIndex}")] - static partial void LogInputActionbar(ILogger logger, string name, string key, int slot, int slotIndex); - - [LoggerMessage( - EventId = 7, - Level = LogLevel.Information, - Message = "[{name}] Non Actionbar {key} -> {consoleKey}")] - static partial void LogInputNonActionbar(ILogger logger, string name, string key, ConsoleKey consoleKey); - - [LoggerMessage( - EventId = 8, - Level = LogLevel.Warning, - Message = "[{name}] has no valid Key={key} or ConsoleKey={consoleKey}")] - static partial void LogInputNoValidKey(ILogger logger, string name, string key, ConsoleKey consoleKey); - - [LoggerMessage( - EventId = 9, - Level = LogLevel.Information, - Message = "[{name}] Update {type} cost to {newCost} from {oldCost}")] - static partial void LogPowerCostChange(ILogger logger, string name, string type, int newCost, int oldCost); - - #endregion + private int GetMinCost() + { + return MinCost; } + + #region Logging + + [LoggerMessage( + EventId = 4, + Level = LogLevel.Information, + Message = "[{name}] Path: {path}")] + static partial void LogPath(ILogger logger, string name, string path); + + [LoggerMessage( + EventId = 5, + Level = LogLevel.Information, + Message = "[{name}] Required Form: {form}")] + static partial void LogFormRequired(ILogger logger, string name, string form); + + [LoggerMessage( + EventId = 6, + Level = LogLevel.Information, + Message = "[{name}] Actionbar Key:{key} -> Actionbar:{slot} -> Index:{slotIndex}")] + static partial void LogInputActionbar(ILogger logger, string name, string key, int slot, int slotIndex); + + [LoggerMessage( + EventId = 7, + Level = LogLevel.Information, + Message = "[{name}] Non Actionbar {key} -> {consoleKey}")] + static partial void LogInputNonActionbar(ILogger logger, string name, string key, ConsoleKey consoleKey); + + [LoggerMessage( + EventId = 8, + Level = LogLevel.Warning, + Message = "[{name}] has no valid Key={key} or ConsoleKey={consoleKey}")] + static partial void LogInputNoValidKey(ILogger logger, string name, string key, ConsoleKey consoleKey); + + [LoggerMessage( + EventId = 9, + Level = LogLevel.Information, + Message = "[{name}] Update {type} cost to {newCost} from {oldCost}")] + static partial void LogPowerCostChange(ILogger logger, string name, string type, int newCost, int oldCost); + + #endregion } \ No newline at end of file diff --git a/Core/ClassConfig/KeyActions.cs b/Core/ClassConfig/KeyActions.cs index a9cfc48bc..6e8ce7535 100644 --- a/Core/ClassConfig/KeyActions.cs +++ b/Core/ClassConfig/KeyActions.cs @@ -1,60 +1,59 @@ using Microsoft.Extensions.Logging; using System; -namespace Core +namespace Core; + +public partial class KeyActions : IDisposable { - public partial class KeyActions : IDisposable + public KeyAction[] Sequence { get; set; } = Array.Empty(); + + public virtual void PreInitialise(string prefix, RequirementFactory requirementFactory, ILogger logger) { - public KeyAction[] Sequence { get; set; } = Array.Empty(); + if (Sequence.Length > 0) + { + LogDynamicBinding(logger, prefix); + requirementFactory.AddSequenceRange(this); + } - public virtual void PreInitialise(string prefix, RequirementFactory requirementFactory, ILogger logger) + for (int i = 0; i < Sequence.Length; i++) { - if (Sequence.Length > 0) - { - LogDynamicBinding(logger, prefix); - requirementFactory.AddSequenceRange(this); - } - - for (int i = 0; i < Sequence.Length; i++) - { - KeyAction keyAction = Sequence[i]; - keyAction.InitialiseSlot(logger); - keyAction.InitDynamicBinding(requirementFactory); - } + KeyAction keyAction = Sequence[i]; + keyAction.InitialiseSlot(logger); + keyAction.InitDynamicBinding(requirementFactory); } + } - public virtual void Initialise(string prefix, ClassConfiguration config, AddonReader addonReader, RequirementFactory requirementFactory, ILogger logger, bool globalLog) + public virtual void Initialise(string prefix, ClassConfiguration config, AddonReader addonReader, RequirementFactory requirementFactory, ILogger logger, bool globalLog) + { + if (Sequence.Length > 0) { - if (Sequence.Length > 0) - { - LogInitKeyActions(logger, prefix); - } - - for (int i = 0; i < Sequence.Length; i++) - { - Sequence[i].Initialise(config, addonReader, requirementFactory, logger, globalLog); - } + LogInitKeyActions(logger, prefix); } - public void Dispose() + for (int i = 0; i < Sequence.Length; i++) { - for (int i = 0; i < Sequence.Length; i++) - { - Sequence[i].Dispose(); - } + Sequence[i].Initialise(config, addonReader, requirementFactory, logger, globalLog); } + } - [LoggerMessage( - EventId = 10, - Level = LogLevel.Information, - Message = "[{prefix}] CreateDynamicBindings.")] - protected static partial void LogDynamicBinding(ILogger logger, string prefix); + public void Dispose() + { + for (int i = 0; i < Sequence.Length; i++) + { + Sequence[i].Dispose(); + } + } - [LoggerMessage( - EventId = 11, - Level = LogLevel.Information, - Message = "[{prefix}] Initialise KeyActions.")] - protected static partial void LogInitKeyActions(ILogger logger, string prefix); + [LoggerMessage( + EventId = 10, + Level = LogLevel.Information, + Message = "[{prefix}] CreateDynamicBindings.")] + protected static partial void LogDynamicBinding(ILogger logger, string prefix); + + [LoggerMessage( + EventId = 11, + Level = LogLevel.Information, + Message = "[{prefix}] Initialise KeyActions.")] + protected static partial void LogInitKeyActions(ILogger logger, string prefix); - } } \ No newline at end of file diff --git a/Core/ClassConfig/KeyReader.cs b/Core/ClassConfig/KeyReader.cs index f8184e7c8..916c2b517 100644 --- a/Core/ClassConfig/KeyReader.cs +++ b/Core/ClassConfig/KeyReader.cs @@ -2,141 +2,140 @@ using System; using System.Collections.Generic; -namespace Core +namespace Core; + +public static class KeyReader { - public static class KeyReader - { - // Bottom Right Action Bar - public const string BR = "N"; - public const int BRIdx = 48; //49 - 1 + // Bottom Right Action Bar + public const string BR = "N"; + public const int BRIdx = 48; //49 - 1 - // Bottom Left Action Bar - public const string BL = "F"; - public const int BLIdx = 60; //61 - 1 + // Bottom Left Action Bar + public const string BL = "F"; + public const int BLIdx = 60; //61 - 1 - public static Dictionary KeyMapping { get; } = new() - { - { "1", ConsoleKey.D1 }, - { "2", ConsoleKey.D2 }, - { "3", ConsoleKey.D3 }, - { "4", ConsoleKey.D4 }, - { "5", ConsoleKey.D5 }, - { "6", ConsoleKey.D6 }, - { "7", ConsoleKey.D7 }, - { "8", ConsoleKey.D8 }, - { "9", ConsoleKey.D9 }, - { "0", ConsoleKey.D0 }, + public static Dictionary KeyMapping { get; } = new() + { + { "1", ConsoleKey.D1 }, + { "2", ConsoleKey.D2 }, + { "3", ConsoleKey.D3 }, + { "4", ConsoleKey.D4 }, + { "5", ConsoleKey.D5 }, + { "6", ConsoleKey.D6 }, + { "7", ConsoleKey.D7 }, + { "8", ConsoleKey.D8 }, + { "9", ConsoleKey.D9 }, + { "0", ConsoleKey.D0 }, - { BR + "1", ConsoleKey.NumPad1 }, - { BR + "2", ConsoleKey.NumPad2 }, - { BR + "3", ConsoleKey.NumPad3 }, - { BR + "4", ConsoleKey.NumPad4 }, - { BR + "5", ConsoleKey.NumPad5 }, - { BR + "6", ConsoleKey.NumPad6 }, - { BR + "7", ConsoleKey.NumPad7 }, - { BR + "8", ConsoleKey.NumPad8 }, - { BR + "9", ConsoleKey.NumPad9 }, - { BR + "0", ConsoleKey.NumPad0 }, + { BR + "1", ConsoleKey.NumPad1 }, + { BR + "2", ConsoleKey.NumPad2 }, + { BR + "3", ConsoleKey.NumPad3 }, + { BR + "4", ConsoleKey.NumPad4 }, + { BR + "5", ConsoleKey.NumPad5 }, + { BR + "6", ConsoleKey.NumPad6 }, + { BR + "7", ConsoleKey.NumPad7 }, + { BR + "8", ConsoleKey.NumPad8 }, + { BR + "9", ConsoleKey.NumPad9 }, + { BR + "0", ConsoleKey.NumPad0 }, - { BL + "0", ConsoleKey.F10 }, - { BL + "1", ConsoleKey.F1 }, - { BL + "2", ConsoleKey.F2 }, - { BL + "3", ConsoleKey.F3 }, - { BL + "4", ConsoleKey.F4 }, - { BL + "5", ConsoleKey.F5 }, - { BL + "6", ConsoleKey.F6 }, - { BL + "7", ConsoleKey.F7 }, - { BL + "8", ConsoleKey.F8 }, - { BL + "9", ConsoleKey.F9 }, - { BL + "11", ConsoleKey.F11 }, - { BL + "12", ConsoleKey.F12 }, + { BL + "0", ConsoleKey.F10 }, + { BL + "1", ConsoleKey.F1 }, + { BL + "2", ConsoleKey.F2 }, + { BL + "3", ConsoleKey.F3 }, + { BL + "4", ConsoleKey.F4 }, + { BL + "5", ConsoleKey.F5 }, + { BL + "6", ConsoleKey.F6 }, + { BL + "7", ConsoleKey.F7 }, + { BL + "8", ConsoleKey.F8 }, + { BL + "9", ConsoleKey.F9 }, + { BL + "11", ConsoleKey.F11 }, + { BL + "12", ConsoleKey.F12 }, - { "Space", ConsoleKey.Spacebar }, - { "-", ConsoleKey.OemMinus }, - { "=", ConsoleKey.OemPlus }, - { " ", ConsoleKey.Spacebar }, - }; + { "Space", ConsoleKey.Spacebar }, + { "-", ConsoleKey.OemMinus }, + { "=", ConsoleKey.OemPlus }, + { " ", ConsoleKey.Spacebar }, + }; - public static Dictionary ActionBarSlotMap { get; } = new() - { - // ActionBar page 1: slots 1 to 12 - { "1", 1 }, - { "2", 2 }, - { "3", 3 }, - { "4", 4 }, - { "5", 5 }, - { "6", 6 }, - { "7", 7 }, - { "8", 8 }, - { "9", 9 }, - { "0", 10 }, - { "-", 11 }, // English keyboard layout required - { "=", 12 }, // English keyboard layout required + public static Dictionary ActionBarSlotMap { get; } = new() + { + // ActionBar page 1: slots 1 to 12 + { "1", 1 }, + { "2", 2 }, + { "3", 3 }, + { "4", 4 }, + { "5", 5 }, + { "6", 6 }, + { "7", 7 }, + { "8", 8 }, + { "9", 9 }, + { "0", 10 }, + { "-", 11 }, // English keyboard layout required + { "=", 12 }, // English keyboard layout required - // ActionBar page 2: slots 13 to 24 - // ActionBar page 3 (Right ActionBar): slots 25 to 36 - // ActionBar page 4 (Right ActionBar 2): slots 37 to 48 - - // ActionBar page 5 (Bottom Right ActionBar): slots 49 to 60 - { BR + "1", 49 }, - { BR + "2", 50 }, - { BR + "3", 51 }, - { BR + "4", 52 }, - { BR + "5", 53 }, - { BR + "6", 54 }, - { BR + "7", 55 }, - { BR + "8", 56 }, - { BR + "9", 57 }, - { BR + "0", 58 }, - //11 - unused - //12 - unused + // ActionBar page 2: slots 13 to 24 + // ActionBar page 3 (Right ActionBar): slots 25 to 36 + // ActionBar page 4 (Right ActionBar 2): slots 37 to 48 + + // ActionBar page 5 (Bottom Right ActionBar): slots 49 to 60 + { BR + "1", 49 }, + { BR + "2", 50 }, + { BR + "3", 51 }, + { BR + "4", 52 }, + { BR + "5", 53 }, + { BR + "6", 54 }, + { BR + "7", 55 }, + { BR + "8", 56 }, + { BR + "9", 57 }, + { BR + "0", 58 }, + //11 - unused + //12 - unused - // ActionBar page 6 (Bottom Left ActionBar): slots 61 to 72 - { BL + "1", 61 }, - { BL + "2", 62 }, - { BL + "3", 63 }, - { BL + "4", 64 }, - { BL + "5", 65 }, - { BL + "6", 66 }, - { BL + "7", 67 }, - { BL + "8", 68 }, - { BL + "9", 69 }, - { BL + "10", 70 }, - { BL + "11", 71 }, - { BL + "12", 72 } - }; + // ActionBar page 6 (Bottom Left ActionBar): slots 61 to 72 + { BL + "1", 61 }, + { BL + "2", 62 }, + { BL + "3", 63 }, + { BL + "4", 64 }, + { BL + "5", 65 }, + { BL + "6", 66 }, + { BL + "7", 67 }, + { BL + "8", 68 }, + { BL + "9", 69 }, + { BL + "10", 70 }, + { BL + "11", 71 }, + { BL + "12", 72 } + }; - public static bool ReadKey(ILogger logger, KeyAction key) + public static bool ReadKey(ILogger logger, KeyAction key) + { + if (string.IsNullOrEmpty(key.Key)) { - if (string.IsNullOrEmpty(key.Key)) - { - return false; - } + return false; + } - if (KeyMapping.TryGetValue(key.Key, out ConsoleKey consoleKey)) - { - key.ConsoleKey = consoleKey; - } - else + if (KeyMapping.TryGetValue(key.Key, out ConsoleKey consoleKey)) + { + key.ConsoleKey = consoleKey; + } + else + { + if (!Enum.TryParse(key.Key, true, out consoleKey)) { - if (!Enum.TryParse(key.Key, true, out consoleKey)) - { - return false; - } - - key.ConsoleKey = consoleKey; + return false; } - if (ActionBarSlotMap.TryGetValue(key.Key, out int slot)) - { - key.Slot = slot; - } - else if (!key.BaseAction) - { - logger.LogWarning($"[{key.Name}] Unable to assign Actionbar {nameof(KeyAction.Slot)}!"); - } + key.ConsoleKey = consoleKey; + } - return true; + if (ActionBarSlotMap.TryGetValue(key.Key, out int slot)) + { + key.Slot = slot; } + else if (!key.BaseAction) + { + logger.LogWarning($"[{key.Name}] Unable to assign Actionbar {nameof(KeyAction.Slot)}!"); + } + + return true; } } \ No newline at end of file diff --git a/Core/ClassConfig/WaitKeyActions.cs b/Core/ClassConfig/WaitKeyActions.cs index d8a9e6e17..af80b9a98 100644 --- a/Core/ClassConfig/WaitKeyActions.cs +++ b/Core/ClassConfig/WaitKeyActions.cs @@ -2,110 +2,109 @@ using System; -namespace Core +namespace Core; + +public sealed partial class WaitKeyActions : KeyActions { - public sealed partial class WaitKeyActions : KeyActions + public float FoodDrinkCost { get; set; } = 4.09f; + + public bool AutoGenerateWaitForFoodAndDrink { get; set; } = true; + + public override void PreInitialise(string prefix, RequirementFactory requirementFactory, ILogger logger) { - public float FoodDrinkCost { get; set; } = 4.09f; + base.PreInitialise(prefix, requirementFactory, logger); + } - public bool AutoGenerateWaitForFoodAndDrink { get; set; } = true; + public override void Initialise(string prefix, ClassConfiguration config, AddonReader addonReader, RequirementFactory requirementFactory, ILogger logger, bool globalLog) + { + if (AutoGenerateWaitForFoodAndDrink) + AddWaitKeyActionsForFoodOrDrink(logger, config); - public override void PreInitialise(string prefix, RequirementFactory requirementFactory, ILogger logger) - { - base.PreInitialise(prefix, requirementFactory, logger); - } + base.Initialise(prefix, config, addonReader, requirementFactory, logger, globalLog); + } - public override void Initialise(string prefix, ClassConfiguration config, AddonReader addonReader, RequirementFactory requirementFactory, ILogger logger, bool globalLog) - { - if (AutoGenerateWaitForFoodAndDrink) - AddWaitKeyActionsForFoodOrDrink(logger, config); + private void AddWaitKeyActionsForFoodOrDrink(ILogger logger, ClassConfiguration classConfig) + { + bool foodExists = false; + bool drinkExists = false; - base.Initialise(prefix, config, addonReader, requirementFactory, logger, globalLog); + for (int i = 0; i < classConfig.Adhoc.Sequence.Length; i++) + { + KeyAction action = classConfig.Adhoc.Sequence[i]; + if (action.Name.Contains(RequirementFactory.Food, StringComparison.InvariantCultureIgnoreCase)) + { + foodExists = true; + } + else if (action.Name.Contains(RequirementFactory.Drink, StringComparison.InvariantCultureIgnoreCase)) + { + drinkExists = true; + } } - private void AddWaitKeyActionsForFoodOrDrink(ILogger logger, ClassConfiguration classConfig) + for (int i = 0; i < classConfig.Parallel.Sequence.Length; i++) { - bool foodExists = false; - bool drinkExists = false; - - for (int i = 0; i < classConfig.Adhoc.Sequence.Length; i++) + KeyAction action = classConfig.Parallel.Sequence[i]; + if (action.Name.Contains(RequirementFactory.Food, StringComparison.InvariantCultureIgnoreCase)) { - KeyAction action = classConfig.Adhoc.Sequence[i]; - if (action.Name.Contains(RequirementFactory.Food, StringComparison.InvariantCultureIgnoreCase)) - { - foodExists = true; - } - else if (action.Name.Contains(RequirementFactory.Drink, StringComparison.InvariantCultureIgnoreCase)) - { - drinkExists = true; - } + foodExists = true; } - - for (int i = 0; i < classConfig.Parallel.Sequence.Length; i++) + else if (action.Name.Contains(RequirementFactory.Drink, StringComparison.InvariantCultureIgnoreCase)) { - KeyAction action = classConfig.Parallel.Sequence[i]; - if (action.Name.Contains(RequirementFactory.Food, StringComparison.InvariantCultureIgnoreCase)) - { - foodExists = true; - } - else if (action.Name.Contains(RequirementFactory.Drink, StringComparison.InvariantCultureIgnoreCase)) - { - drinkExists = true; - } + drinkExists = true; } + } - if (foodExists) + if (foodExists) + { + KeyAction foodWaitAction = new() { - KeyAction foodWaitAction = new() - { - Cost = FoodDrinkCost, - Name = "Eating", - Requirement = $"{RequirementFactory.Food} && {RequirementFactory.HealthP} < 99" - }; + Cost = FoodDrinkCost, + Name = "Eating", + Requirement = $"{RequirementFactory.Food} && {RequirementFactory.HealthP} < 99" + }; - KeyAction[] keyActions = Sequence; + KeyAction[] keyActions = Sequence; - int newSize = keyActions.Length + 1; - Array.Resize(ref keyActions, newSize); + int newSize = keyActions.Length + 1; + Array.Resize(ref keyActions, newSize); - keyActions[^1] = foodWaitAction; + keyActions[^1] = foodWaitAction; - Sequence = keyActions; + Sequence = keyActions; - LogAddedWait(logger, nameof(WaitKeyActions), RequirementFactory.Food); - } + LogAddedWait(logger, nameof(WaitKeyActions), RequirementFactory.Food); + } - if (drinkExists) + if (drinkExists) + { + KeyAction drinkWaitAction = new() { - KeyAction drinkWaitAction = new() - { - Cost = FoodDrinkCost, - Name = "Drinking", - Requirement = $"{RequirementFactory.Drink} && {RequirementFactory.ManaP} < 99" - }; + Cost = FoodDrinkCost, + Name = "Drinking", + Requirement = $"{RequirementFactory.Drink} && {RequirementFactory.ManaP} < 99" + }; - KeyAction[] keyActions = Sequence; + KeyAction[] keyActions = Sequence; - int newSize = keyActions.Length + 1; - Array.Resize(ref keyActions, newSize); + int newSize = keyActions.Length + 1; + Array.Resize(ref keyActions, newSize); - keyActions[^1] = drinkWaitAction; + keyActions[^1] = drinkWaitAction; - Sequence = keyActions; + Sequence = keyActions; - LogAddedWait(logger, nameof(WaitKeyActions), RequirementFactory.Drink); - } + LogAddedWait(logger, nameof(WaitKeyActions), RequirementFactory.Drink); } + } - #region logging + #region logging - [LoggerMessage( - EventId = 12, - Level = LogLevel.Information, - Message = "[{typeName}] Added awaiting action for {keyActionName}")] - static partial void LogAddedWait(ILogger logger, string typeName, string keyActionName); + [LoggerMessage( + EventId = 12, + Level = LogLevel.Information, + Message = "[{typeName}] Added awaiting action for {keyActionName}")] + static partial void LogAddedWait(ILogger logger, string typeName, string keyActionName); - #endregion + #endregion - } } \ No newline at end of file diff --git a/Core/ConfigBotController.cs b/Core/ConfigBotController.cs index 9928941ce..3ca26db53 100644 --- a/Core/ConfigBotController.cs +++ b/Core/ConfigBotController.cs @@ -6,119 +6,118 @@ using System.Threading; using Microsoft.Extensions.Logging; -namespace Core +namespace Core; + +public sealed class ConfigBotController : IBotController, IDisposable { - public sealed class ConfigBotController : IBotController, IDisposable - { - public IAddonReader AddonReader { get; } - public GoapAgent? GoapAgent => throw new NotImplementedException(); - public RouteInfo? RouteInfo => throw new NotImplementedException(); - public WowScreen WowScreen => throw new NotImplementedException(); - public IGrindSessionDAO GrindSessionDAO => throw new NotImplementedException(); - public string SelectedClassFilename => throw new NotImplementedException(); - public string? SelectedPathFilename => throw new NotImplementedException(); + public IAddonReader AddonReader { get; } + public GoapAgent? GoapAgent => throw new NotImplementedException(); + public RouteInfo? RouteInfo => throw new NotImplementedException(); + public WowScreen WowScreen => throw new NotImplementedException(); + public IGrindSessionDAO GrindSessionDAO => throw new NotImplementedException(); + public string SelectedClassFilename => throw new NotImplementedException(); + public string? SelectedPathFilename => throw new NotImplementedException(); - public ClassConfiguration? ClassConfig => null; + public ClassConfiguration? ClassConfig => null; - public bool IsBotActive => false; + public bool IsBotActive => false; - public double AvgScreenLatency => throw new NotImplementedException(); - public double AvgNPCLatency => throw new NotImplementedException(); + public double AvgScreenLatency => throw new NotImplementedException(); + public double AvgNPCLatency => throw new NotImplementedException(); - AddonReader IBotController.AddonReader => throw new NotImplementedException(); + AddonReader IBotController.AddonReader => throw new NotImplementedException(); - public event Action? ProfileLoaded; - public event Action? StatusChanged; + public event Action? ProfileLoaded; + public event Action? StatusChanged; - private readonly ILogger logger; - private readonly CancellationTokenSource cts; + private readonly ILogger logger; + private readonly CancellationTokenSource cts; - private readonly Thread addonThread; + private readonly Thread addonThread; - private readonly Thread? frontendThread; - private const int frontendTickMs = 250; + private readonly Thread? frontendThread; + private const int frontendTickMs = 250; - public ConfigBotController(ILogger logger, CancellationTokenSource cts, IAddonReader addonReader) - { - this.logger = logger; - this.cts = cts; - this.AddonReader = addonReader; + public ConfigBotController(ILogger logger, CancellationTokenSource cts, IAddonReader addonReader) + { + this.logger = logger; + this.cts = cts; + this.AddonReader = addonReader; - addonThread = new(AddonThread); - addonThread.Start(); + addonThread = new(AddonThread); + addonThread.Start(); - frontendThread = new(FrontendThread); - frontendThread.Start(); - } + frontendThread = new(FrontendThread); + frontendThread.Start(); + } - public void Dispose() - { - cts.Cancel(); - } + public void Dispose() + { + cts.Cancel(); + } - private void FrontendThread() + private void FrontendThread() + { + while (!cts.IsCancellationRequested) { - while (!cts.IsCancellationRequested) - { - AddonReader.UpdateUI(); - cts.Token.WaitHandle.WaitOne(frontendTickMs); - } - - if (logger.IsEnabled(LogLevel.Debug)) - logger.LogDebug("Frontend thread stopped!"); + AddonReader.UpdateUI(); + cts.Token.WaitHandle.WaitOne(frontendTickMs); } - private void AddonThread() + if (logger.IsEnabled(LogLevel.Debug)) + logger.LogDebug("Frontend thread stopped!"); + } + + private void AddonThread() + { + while (!cts.IsCancellationRequested) { - while (!cts.IsCancellationRequested) - { - AddonReader.Update(); - } - logger.LogWarning("Addon thread stoppped!"); + AddonReader.Update(); } + logger.LogWarning("Addon thread stoppped!"); + } - public void Shutdown() - { - cts.Cancel(); - } + public void Shutdown() + { + cts.Cancel(); + } - public void MinimapNodeFound() - { - throw new NotImplementedException(); - } + public void MinimapNodeFound() + { + throw new NotImplementedException(); + } - public void ToggleBotStatus() - { - StatusChanged?.Invoke(); - throw new NotImplementedException(); - } + public void ToggleBotStatus() + { + StatusChanged?.Invoke(); + throw new NotImplementedException(); + } - public IEnumerable ClassFiles() - { - throw new NotImplementedException(); - } + public IEnumerable ClassFiles() + { + throw new NotImplementedException(); + } - public IEnumerable PathFiles() - { - throw new NotImplementedException(); - } + public IEnumerable PathFiles() + { + throw new NotImplementedException(); + } - public void LoadClassProfile(string classFilename) - { - ProfileLoaded?.Invoke(); - throw new NotImplementedException(); - } + public void LoadClassProfile(string classFilename) + { + ProfileLoaded?.Invoke(); + throw new NotImplementedException(); + } - public void LoadPathProfile(string pathFilename) - { - ProfileLoaded?.Invoke(); - throw new NotImplementedException(); - } + public void LoadPathProfile(string pathFilename) + { + ProfileLoaded?.Invoke(); + throw new NotImplementedException(); + } - public void OverrideClassConfig(ClassConfiguration classConfiguration) - { - throw new NotImplementedException(); - } + public void OverrideClassConfig(ClassConfiguration classConfiguration) + { + throw new NotImplementedException(); } } \ No newline at end of file diff --git a/Core/Configurator/AddonConfigurator.cs b/Core/Configurator/AddonConfigurator.cs index d5ad5a0d5..fc68236c5 100644 --- a/Core/Configurator/AddonConfigurator.cs +++ b/Core/Configurator/AddonConfigurator.cs @@ -6,320 +6,319 @@ using Game; using Core.Extensions; -namespace Core +namespace Core; + +public sealed class AddonConfigurator { - public sealed class AddonConfigurator - { - private readonly ILogger logger; - private readonly WowProcess wowProcess; + private readonly ILogger logger; + private readonly WowProcess wowProcess; - public AddonConfig Config { get; init; } + public AddonConfig Config { get; init; } - private const string DefaultAddonName = "DataToColor"; - private const string AddonSourcePath = @".\Addons\"; + private const string DefaultAddonName = "DataToColor"; + private const string AddonSourcePath = @".\Addons\"; - private string AddonBasePath => Path.Join(wowProcess.Path, "Interface", "AddOns"); + private string AddonBasePath => Path.Join(wowProcess.Path, "Interface", "AddOns"); - private string DefaultAddonPath => Path.Join(AddonBasePath, DefaultAddonName); - public string FinalAddonPath => Path.Join(AddonBasePath, Config.Title); + private string DefaultAddonPath => Path.Join(AddonBasePath, DefaultAddonName); + public string FinalAddonPath => Path.Join(AddonBasePath, Config.Title); - public event Action? OnChange; + public event Action? OnChange; - public AddonConfigurator(ILogger logger, WowProcess wowProcess) - { - this.logger = logger; - this.wowProcess = wowProcess; + public AddonConfigurator(ILogger logger, WowProcess wowProcess) + { + this.logger = logger; + this.wowProcess = wowProcess; - Config = AddonConfig.Load(); - } + Config = AddonConfig.Load(); + } - public bool Installed() - { - return GetInstallVersion() != null; - } + public bool Installed() + { + return GetInstallVersion() != null; + } - public bool IsDefault() + public bool IsDefault() + { + return Config.IsDefault(); + } + + public bool Validate() + { + if (string.IsNullOrEmpty(Config.Author)) { - return Config.IsDefault(); + logger.LogError($"{nameof(Config)}.{nameof(Config.Author)} - error - cannot be empty: '{Config.Author}'"); + return false; } - public bool Validate() + if (!string.IsNullOrEmpty(Config.Title)) { - if (string.IsNullOrEmpty(Config.Author)) - { - logger.LogError($"{nameof(Config)}.{nameof(Config.Author)} - error - cannot be empty: '{Config.Author}'"); - return false; - } - - if (!string.IsNullOrEmpty(Config.Title)) - { - // this will appear in the lua code so - // special character not allowed - // also numbers not allowed - Config.Title = Regex.Replace(Config.Title, @"[^\u0000-\u007F]+", string.Empty); - Config.Title = new string(Config.Title.Where(char.IsLetter).ToArray()); - Config.Title = - Config.Title.Trim() - .Replace(" ", ""); - - if (Config.Title.Length == 0) - { - logger.LogError($"{nameof(Config)}.{nameof(Config.Title)} - error - use letters only: '{Config.Title}'"); - return false; - } - - Config.Command = Config.Title.Trim().ToLower(); - } - else + // this will appear in the lua code so + // special character not allowed + // also numbers not allowed + Config.Title = Regex.Replace(Config.Title, @"[^\u0000-\u007F]+", string.Empty); + Config.Title = new string(Config.Title.Where(char.IsLetter).ToArray()); + Config.Title = + Config.Title.Trim() + .Replace(" ", ""); + + if (Config.Title.Length == 0) { - logger.LogError($"{nameof(Config)}.{nameof(Config.Title)} - error - cannot be empty: '{Config.Title}'"); + logger.LogError($"{nameof(Config)}.{nameof(Config.Title)} - error - use letters only: '{Config.Title}'"); return false; } - if (!int.TryParse(Config.CellSize, out int size)) - { - logger.LogError($"{nameof(Config)}.{nameof(Config.CellSize)} - error - be a number: '{Config.CellSize}'"); - return false; - } - else if (size < 1 || size > 9) - { - logger.LogError($"{nameof(Config)}.{nameof(Config.CellSize)} - error - must be, including between 1 and 9: '{Config.CellSize}'"); - return false; - } - - return true; + Config.Command = Config.Title.Trim().ToLower(); } - - public void Install() + else { - try - { - DeleteAddon(); - CopyAddonFiles(); - RenameAddon(); - MakeUnique(); - - logger.LogInformation($"{nameof(AddonConfigurator)}.{nameof(Install)} - Success"); - } - catch (Exception e) - { - logger.LogInformation($"{nameof(AddonConfigurator)}.{nameof(Install)} - Failed\n{e.Message}"); - } + logger.LogError($"{nameof(Config)}.{nameof(Config.Title)} - error - cannot be empty: '{Config.Title}'"); + return false; } - private void DeleteAddon() + if (!int.TryParse(Config.CellSize, out int size)) { - if (Directory.Exists(DefaultAddonPath)) - { - logger.LogInformation($"{nameof(AddonConfigurator)}.{nameof(DeleteAddon)} -> Default Addon Exists"); - Directory.Delete(DefaultAddonPath, true); - } - - if (!string.IsNullOrEmpty(Config.Title) && Directory.Exists(FinalAddonPath)) - { - logger.LogInformation($"{nameof(AddonConfigurator)}.{nameof(DeleteAddon)} -> Unique Addon Exists"); - Directory.Delete(FinalAddonPath, true); - } + logger.LogError($"{nameof(Config)}.{nameof(Config.CellSize)} - error - be a number: '{Config.CellSize}'"); + return false; } + else if (size < 1 || size > 9) + { + logger.LogError($"{nameof(Config)}.{nameof(Config.CellSize)} - error - must be, including between 1 and 9: '{Config.CellSize}'"); + return false; + } + + return true; + } - private void CopyAddonFiles() + public void Install() + { + try { - try - { - CopyFolder(""); - logger.LogInformation($"{nameof(AddonConfigurator)}.{nameof(CopyAddonFiles)} - Success"); - } - catch (Exception e) - { - logger.LogError(e.Message); + DeleteAddon(); + CopyAddonFiles(); + RenameAddon(); + MakeUnique(); - // This only should be happen when running from IDE - CopyFolder("."); - logger.LogInformation($"{nameof(AddonConfigurator)}.{nameof(CopyAddonFiles)} - Success"); - } + logger.LogInformation($"{nameof(AddonConfigurator)}.{nameof(Install)} - Success"); } - - private void CopyFolder(string parentFolder) + catch (Exception e) { - DirectoryCopy(Path.Join(parentFolder + AddonSourcePath), AddonBasePath, true); + logger.LogInformation($"{nameof(AddonConfigurator)}.{nameof(Install)} - Failed\n{e.Message}"); } + } - private void RenameAddon() + private void DeleteAddon() + { + if (Directory.Exists(DefaultAddonPath)) { - string src = Path.Join(AddonBasePath, DefaultAddonName); - if (src != FinalAddonPath) - Directory.Move(src, FinalAddonPath); + logger.LogInformation($"{nameof(AddonConfigurator)}.{nameof(DeleteAddon)} -> Default Addon Exists"); + Directory.Delete(DefaultAddonPath, true); } - private void MakeUnique() + if (!string.IsNullOrEmpty(Config.Title) && Directory.Exists(FinalAddonPath)) { - BulkRename(FinalAddonPath, DefaultAddonName, Config.Title); - EditToc(); - EditMainLua(); - EditModulesLua(); + logger.LogInformation($"{nameof(AddonConfigurator)}.{nameof(DeleteAddon)} -> Unique Addon Exists"); + Directory.Delete(FinalAddonPath, true); } + } - private static void BulkRename(string fPath, string match, string fNewName) + private void CopyAddonFiles() + { + try { - foreach (FileInfo f in new DirectoryInfo(fPath).GetFiles()) - { - string fromName = Path.GetFileNameWithoutExtension(f.Name); + CopyFolder(""); + logger.LogInformation($"{nameof(AddonConfigurator)}.{nameof(CopyAddonFiles)} - Success"); + } + catch (Exception e) + { + logger.LogError(e.Message); - if (!fromName.Contains(match)) - continue; + // This only should be happen when running from IDE + CopyFolder("."); + logger.LogInformation($"{nameof(AddonConfigurator)}.{nameof(CopyAddonFiles)} - Success"); + } + } - string ext = Path.GetExtension(f.Name); + private void CopyFolder(string parentFolder) + { + DirectoryCopy(Path.Join(parentFolder + AddonSourcePath), AddonBasePath, true); + } - fromName = Path.Join(fPath, f.Name); - string toName = Path.Join(fPath, fNewName) + ext; + private void RenameAddon() + { + string src = Path.Join(AddonBasePath, DefaultAddonName); + if (src != FinalAddonPath) + Directory.Move(src, FinalAddonPath); + } - File.Move(fromName, toName); - } - } + private void MakeUnique() + { + BulkRename(FinalAddonPath, DefaultAddonName, Config.Title); + EditToc(); + EditMainLua(); + EditModulesLua(); + } - private void EditToc() + private static void BulkRename(string fPath, string match, string fNewName) + { + foreach (FileInfo f in new DirectoryInfo(fPath).GetFiles()) { - string tocPath = Path.Join(FinalAddonPath, Config.Title + ".toc"); - string text = - File.ReadAllText(tocPath) - .Replace(DefaultAddonName, Config.Title) - .Replace("## Author: FreeHongKongMMO", "## Author: " + Config.Author); + string fromName = Path.GetFileNameWithoutExtension(f.Name); - File.WriteAllText(tocPath, text); - } + if (!fromName.Contains(match)) + continue; - private void EditMainLua() - { - string mainLuaPath = Path.Join(FinalAddonPath, Config.Title + ".lua"); - string text = - File.ReadAllText(mainLuaPath) - .Replace(DefaultAddonName, Config.Title) - .Replace("dc", Config.Command) - .Replace("DC", Config.Command); + string ext = Path.GetExtension(f.Name); - Regex cellSizeRegex = new(@"^local CELL_SIZE = (?[0-9]+)", RegexOptions.Multiline); - text = text.Replace(cellSizeRegex, "SIZE", Config.CellSize); + fromName = Path.Join(fPath, f.Name); + string toName = Path.Join(fPath, fNewName) + ext; - File.WriteAllText(mainLuaPath, text); + File.Move(fromName, toName); } + } + + private void EditToc() + { + string tocPath = Path.Join(FinalAddonPath, Config.Title + ".toc"); + string text = + File.ReadAllText(tocPath) + .Replace(DefaultAddonName, Config.Title) + .Replace("## Author: FreeHongKongMMO", "## Author: " + Config.Author); + + File.WriteAllText(tocPath, text); + } + + private void EditMainLua() + { + string mainLuaPath = Path.Join(FinalAddonPath, Config.Title + ".lua"); + string text = + File.ReadAllText(mainLuaPath) + .Replace(DefaultAddonName, Config.Title) + .Replace("dc", Config.Command) + .Replace("DC", Config.Command); + + Regex cellSizeRegex = new(@"^local CELL_SIZE = (?[0-9]+)", RegexOptions.Multiline); + text = text.Replace(cellSizeRegex, "SIZE", Config.CellSize); + + File.WriteAllText(mainLuaPath, text); + } - private void EditModulesLua() + private void EditModulesLua() + { + FileInfo[] files = new DirectoryInfo(FinalAddonPath).GetFiles(); + foreach (var f in files) { - FileInfo[] files = new DirectoryInfo(FinalAddonPath).GetFiles(); - foreach (var f in files) + if (f.Extension.Contains("lua")) { - if (f.Extension.Contains("lua")) - { - string path = f.FullName; - string text = File.ReadAllText(path); - text = text.Replace(DefaultAddonName, Config.Title); - - File.WriteAllText(path, text); - } + string path = f.FullName; + string text = File.ReadAllText(path); + text = text.Replace(DefaultAddonName, Config.Title); + + File.WriteAllText(path, text); } } + } - public void Delete() - { - DeleteAddon(); - AddonConfig.Delete(); + public void Delete() + { + DeleteAddon(); + AddonConfig.Delete(); - OnChange?.Invoke(); - } + OnChange?.Invoke(); + } - public void Save() - { - Config.Save(); + public void Save() + { + Config.Save(); - OnChange?.Invoke(); - } + OnChange?.Invoke(); + } - private static void DirectoryCopy(string sourceDirName, string destDirName, bool copySubDirs) - { - // Get the subdirectories for the specified directory. - DirectoryInfo dir = new DirectoryInfo(sourceDirName); + private static void DirectoryCopy(string sourceDirName, string destDirName, bool copySubDirs) + { + // Get the subdirectories for the specified directory. + DirectoryInfo dir = new DirectoryInfo(sourceDirName); - if (!dir.Exists) - { - throw new DirectoryNotFoundException( - "Source directory does not exist or could not be found: " - + sourceDirName); - } + if (!dir.Exists) + { + throw new DirectoryNotFoundException( + "Source directory does not exist or could not be found: " + + sourceDirName); + } - DirectoryInfo[] dirs = dir.GetDirectories(); + DirectoryInfo[] dirs = dir.GetDirectories(); - // If the destination directory doesn't exist, create it. - Directory.CreateDirectory(destDirName); + // If the destination directory doesn't exist, create it. + Directory.CreateDirectory(destDirName); - // Get the files in the directory and copy them to the new location. - FileInfo[] files = dir.GetFiles(); - foreach (FileInfo file in files) - { - string tempPath = Path.Combine(destDirName, file.Name); - file.CopyTo(tempPath, true); - } + // Get the files in the directory and copy them to the new location. + FileInfo[] files = dir.GetFiles(); + foreach (FileInfo file in files) + { + string tempPath = Path.Combine(destDirName, file.Name); + file.CopyTo(tempPath, true); + } - // If copying subdirectories, copy them and their contents to new location. - if (copySubDirs) + // If copying subdirectories, copy them and their contents to new location. + if (copySubDirs) + { + foreach (DirectoryInfo subdir in dirs) { - foreach (DirectoryInfo subdir in dirs) - { - string tempPath = Path.Combine(destDirName, subdir.Name); - DirectoryCopy(subdir.FullName, tempPath, copySubDirs); - } + string tempPath = Path.Combine(destDirName, subdir.Name); + DirectoryCopy(subdir.FullName, tempPath, copySubDirs); } } + } - public bool UpdateAvailable() - { - if (Config.IsDefault()) - return false; + public bool UpdateAvailable() + { + if (Config.IsDefault()) + return false; - Version? repo = GetRepoVerion(); - Version? installed = GetInstallVersion(); + Version? repo = GetRepoVerion(); + Version? installed = GetInstallVersion(); - return installed != null && repo != null && repo > installed; - } + return installed != null && repo != null && repo > installed; + } - public Version? GetRepoVerion() + public Version? GetRepoVerion() + { + Version? repo = null; + try { - Version? repo = null; - try - { - repo = GetVersion(Path.Join(AddonSourcePath, DefaultAddonName), DefaultAddonName); + repo = GetVersion(Path.Join(AddonSourcePath, DefaultAddonName), DefaultAddonName); - if (repo == null) - { - repo = GetVersion(Path.Join("." + AddonSourcePath, DefaultAddonName), DefaultAddonName); - } - } - catch (Exception e) + if (repo == null) { - logger.LogError(e.Message); + repo = GetVersion(Path.Join("." + AddonSourcePath, DefaultAddonName), DefaultAddonName); } - return repo; } - - public Version? GetInstallVersion() + catch (Exception e) { - return GetVersion(FinalAddonPath, Config.Title); + logger.LogError(e.Message); } + return repo; + } - private static Version? GetVersion(string path, string fileName) - { - string tocPath = Path.Join(path, fileName + ".toc"); + public Version? GetInstallVersion() + { + return GetVersion(FinalAddonPath, Config.Title); + } - if (!File.Exists(tocPath)) - return null; + private static Version? GetVersion(string path, string fileName) + { + string tocPath = Path.Join(path, fileName + ".toc"); - string begin = "## Version: "; - var line = File - .ReadLines(tocPath) - .SkipWhile(line => !line.StartsWith(begin)) - .FirstOrDefault(); + if (!File.Exists(tocPath)) + return null; - string? versionStr = line?.Split(begin)[1]; - return Version.TryParse(versionStr, out Version? version) ? version : null; - } + string begin = "## Version: "; + var line = File + .ReadLines(tocPath) + .SkipWhile(line => !line.StartsWith(begin)) + .FirstOrDefault(); + + string? versionStr = line?.Split(begin)[1]; + return Version.TryParse(versionStr, out Version? version) ? version : null; } } \ No newline at end of file diff --git a/Core/Configurator/FrameConfigurator.cs b/Core/Configurator/FrameConfigurator.cs index 3c3435d67..096c72b12 100644 --- a/Core/Configurator/FrameConfigurator.cs +++ b/Core/Configurator/FrameConfigurator.cs @@ -8,354 +8,353 @@ using System.IO; using System.Threading; -namespace Core +namespace Core; + +public sealed class FrameConfigurator : IDisposable { - public sealed class FrameConfigurator : IDisposable + private enum Stage { - private enum Stage - { - Reset, - DetectRunningGame, - CheckGameWindowLocation, - EnterConfigMode, - ValidateMetaSize, - CreateDataFrames, - ReturnNormalMode, - UpdateReader, - ValidateData, - Done - } + Reset, + DetectRunningGame, + CheckGameWindowLocation, + EnterConfigMode, + ValidateMetaSize, + CreateDataFrames, + ReturnNormalMode, + UpdateReader, + ValidateData, + Done + } - private Stage stage = Stage.Reset; + private Stage stage = Stage.Reset; - private const int MAX_HEIGHT = 25; // this one just arbitrary number for sanity check - private const int INTERVAL = 500; + private const int MAX_HEIGHT = 25; // this one just arbitrary number for sanity check + private const int INTERVAL = 500; - private readonly ILogger logger; - private readonly WowProcess wowProcess; - private readonly WowScreen wowScreen; - private readonly WowProcessInput wowProcessInput; - private readonly ExecGameCommand execGameCommand; - private readonly AddonConfigurator addonConfigurator; - private readonly Wait wait; - private readonly IAddonDataProvider reader; + private readonly ILogger logger; + private readonly WowProcess wowProcess; + private readonly WowScreen wowScreen; + private readonly WowProcessInput wowProcessInput; + private readonly ExecGameCommand execGameCommand; + private readonly AddonConfigurator addonConfigurator; + private readonly Wait wait; + private readonly IAddonDataProvider reader; - private Thread? screenshotThread; - private CancellationTokenSource cts = new(); + private Thread? screenshotThread; + private CancellationTokenSource cts = new(); - public DataFrameMeta DataFrameMeta { get; private set; } = DataFrameMeta.Empty; + public DataFrameMeta DataFrameMeta { get; private set; } = DataFrameMeta.Empty; - public DataFrame[] DataFrames { get; private set; } = Array.Empty(); + public DataFrame[] DataFrames { get; private set; } = Array.Empty(); - public bool Saved { get; private set; } - public bool AddonNotVisible { get; private set; } + public bool Saved { get; private set; } + public bool AddonNotVisible { get; private set; } - public string ImageBase64 { private set; get; } = "iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAHElEQVQI12P4//8/w38GIAXDIBKE0DHxgljNBAAO9TXL0Y4OHwAAAABJRU5ErkJggg=="; + public string ImageBase64 { private set; get; } = "iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAHElEQVQI12P4//8/w38GIAXDIBKE0DHxgljNBAAO9TXL0Y4OHwAAAABJRU5ErkJggg=="; - private Rectangle screenRect = Rectangle.Empty; - private Size size = Size.Empty; + private Rectangle screenRect = Rectangle.Empty; + private Size size = Size.Empty; - public event Action? OnUpdate; + public event Action? OnUpdate; - public FrameConfigurator(ILogger logger, Wait wait, - WowProcess wowProcess, IAddonDataProvider reader, - WowScreen wowScreen, WowProcessInput wowProcessInput, - ExecGameCommand execGameCommand, AddonConfigurator addonConfigurator) - { - this.logger = logger; - this.wait = wait; - this.wowProcess = wowProcess; - this.reader = reader; - this.wowScreen = wowScreen; - this.wowProcessInput = wowProcessInput; - this.execGameCommand = execGameCommand; - this.addonConfigurator = addonConfigurator; - } + public FrameConfigurator(ILogger logger, Wait wait, + WowProcess wowProcess, IAddonDataProvider reader, + WowScreen wowScreen, WowProcessInput wowProcessInput, + ExecGameCommand execGameCommand, AddonConfigurator addonConfigurator) + { + this.logger = logger; + this.wait = wait; + this.wowProcess = wowProcess; + this.reader = reader; + this.wowScreen = wowScreen; + this.wowProcessInput = wowProcessInput; + this.execGameCommand = execGameCommand; + this.addonConfigurator = addonConfigurator; + } - public void Dispose() - { - cts.Cancel(); - } + public void Dispose() + { + cts.Cancel(); + } - private void ManualConfigThread() + private void ManualConfigThread() + { + while (!cts.Token.IsCancellationRequested) { - while (!cts.Token.IsCancellationRequested) - { - DoConfig(false); + DoConfig(false); - OnUpdate?.Invoke(); - cts.Token.WaitHandle.WaitOne(INTERVAL); - wait.Update(); - } - screenshotThread = null; + OnUpdate?.Invoke(); + cts.Token.WaitHandle.WaitOne(INTERVAL); + wait.Update(); } + screenshotThread = null; + } - private bool DoConfig(bool auto) + private bool DoConfig(bool auto) + { + switch (stage) { - switch (stage) - { - case Stage.Reset: - screenRect = Rectangle.Empty; - size = Size.Empty; - ResetConfigState(); + case Stage.Reset: + screenRect = Rectangle.Empty; + size = Size.Empty; + ResetConfigState(); - stage++; - break; - case Stage.DetectRunningGame: - if (wowProcess.IsRunning) + stage++; + break; + case Stage.DetectRunningGame: + if (wowProcess.IsRunning) + { + if (auto) { - if (auto) - { - logger.LogInformation($"Found {nameof(WowProcess)}"); - } - stage++; + logger.LogInformation($"Found {nameof(WowProcess)}"); } - else + stage++; + } + else + { + if (auto) { - if (auto) - { - logger.LogWarning($"{nameof(WowProcess)} no longer running!"); - return false; - } - stage--; + logger.LogWarning($"{nameof(WowProcess)} no longer running!"); + return false; } - break; - case Stage.CheckGameWindowLocation: - wowScreen.GetRectangle(out screenRect); - if (screenRect.Location.X < 0 || screenRect.Location.Y < 0) - { - logger.LogWarning($"Client window outside of the visible area of the screen {screenRect.Location}"); - stage = Stage.Reset; + stage--; + } + break; + case Stage.CheckGameWindowLocation: + wowScreen.GetRectangle(out screenRect); + if (screenRect.Location.X < 0 || screenRect.Location.Y < 0) + { + logger.LogWarning($"Client window outside of the visible area of the screen {screenRect.Location}"); + stage = Stage.Reset; - if (auto) - { - return false; - } - } - else - { - AddonNotVisible = false; - stage++; - - if (auto) - { - logger.LogInformation($"Client window: {screenRect}"); - } - } - break; - case Stage.EnterConfigMode: if (auto) { - Version? version = addonConfigurator.GetInstallVersion(); - if (version == null) - { - stage = Stage.Reset; - logger.LogError($"Addon is not installed!"); - return false; - } - logger.LogInformation($"Addon installed! Version: {version}"); - - logger.LogInformation("Enter configuration mode."); - wowProcessInput.SetForegroundWindow(); - wait.Fixed(INTERVAL); - ToggleInGameConfiguration(execGameCommand); - wait.Update(); + return false; } + } + else + { + AddonNotVisible = false; + stage++; - DataFrameMeta temp = GetDataFrameMeta(); - if (DataFrameMeta == DataFrameMeta.Empty && temp != DataFrameMeta.Empty) - { - DataFrameMeta = temp; - stage++; - - if (auto) - { - logger.LogInformation($"DataFrameMeta: {DataFrameMeta}"); - } - } - break; - case Stage.ValidateMetaSize: - size = DataFrameMeta.EstimatedSize(screenRect); - if (!size.IsEmpty && - size.Width <= screenRect.Size.Width && - size.Height <= screenRect.Size.Height && - size.Height <= MAX_HEIGHT) + if (auto) { - stage++; + logger.LogInformation($"Client window: {screenRect}"); } - else + } + break; + case Stage.EnterConfigMode: + if (auto) + { + Version? version = addonConfigurator.GetInstallVersion(); + if (version == null) { - logger.LogWarning($"Addon Rect({size}) size issue. Either too small or too big!"); stage = Stage.Reset; - - if (auto) - return false; - } - break; - case Stage.CreateDataFrames: - Bitmap bitmap = wowScreen.GetBitmap(size.Width, size.Height); - if (!auto) - { - using MemoryStream ms = new(); - bitmap.Save(ms, ImageFormat.Png); - this.ImageBase64 = Convert.ToBase64String(ms.ToArray()); + logger.LogError($"Addon is not installed!"); + return false; } + logger.LogInformation($"Addon installed! Version: {version}"); - DataFrames = FrameConfig.TryCreateFrames(DataFrameMeta, bitmap); - if (DataFrames.Length == DataFrameMeta.frames) - { - stage++; - } - else - { - logger.LogWarning($"DataFrameMeta and FrameConfig dosen't match Frames: ({DataFrames.Length}) != Meta: ({DataFrameMeta.frames})"); - stage = Stage.Reset; + logger.LogInformation("Enter configuration mode."); + wowProcessInput.SetForegroundWindow(); + wait.Fixed(INTERVAL); + ToggleInGameConfiguration(execGameCommand); + wait.Update(); + } - if (auto) - return false; - } + DataFrameMeta temp = GetDataFrameMeta(); + if (DataFrameMeta == DataFrameMeta.Empty && temp != DataFrameMeta.Empty) + { + DataFrameMeta = temp; + stage++; - bitmap.Dispose(); - break; - case Stage.ReturnNormalMode: if (auto) { - logger.LogInformation($"Exit configuration mode."); - wowProcessInput.SetForegroundWindow(); - ToggleInGameConfiguration(execGameCommand); - wait.Fixed(INTERVAL); + logger.LogInformation($"DataFrameMeta: {DataFrameMeta}"); } + } + break; + case Stage.ValidateMetaSize: + size = DataFrameMeta.EstimatedSize(screenRect); + if (!size.IsEmpty && + size.Width <= screenRect.Size.Width && + size.Height <= screenRect.Size.Height && + size.Height <= MAX_HEIGHT) + { + stage++; + } + else + { + logger.LogWarning($"Addon Rect({size}) size issue. Either too small or too big!"); + stage = Stage.Reset; - if (GetDataFrameMeta() == DataFrameMeta.Empty) - stage++; - break; - case Stage.UpdateReader: - reader.InitFrames(DataFrames); - wait.Update(); - wait.Update(); + if (auto) + return false; + } + break; + case Stage.CreateDataFrames: + Bitmap bitmap = wowScreen.GetBitmap(size.Width, size.Height); + if (!auto) + { + using MemoryStream ms = new(); + bitmap.Save(ms, ImageFormat.Png); + this.ImageBase64 = Convert.ToBase64String(ms.ToArray()); + } + + DataFrames = FrameConfig.TryCreateFrames(DataFrameMeta, bitmap); + if (DataFrames.Length == DataFrameMeta.frames) + { stage++; - break; - case Stage.ValidateData: - if (TryResolveRaceAndClass(out UnitRace race, out UnitClass @class, out ClientVersion clientVersion)) - { - if (auto) - { - logger.LogInformation($"Found {clientVersion.ToStringF()} {race.ToStringF()} {@class.ToStringF()}!"); - } + } + else + { + logger.LogWarning($"DataFrameMeta and FrameConfig dosen't match Frames: ({DataFrames.Length}) != Meta: ({DataFrameMeta.frames})"); + stage = Stage.Reset; - stage++; - } - else + if (auto) + return false; + } + + bitmap.Dispose(); + break; + case Stage.ReturnNormalMode: + if (auto) + { + logger.LogInformation($"Exit configuration mode."); + wowProcessInput.SetForegroundWindow(); + ToggleInGameConfiguration(execGameCommand); + wait.Fixed(INTERVAL); + } + + if (GetDataFrameMeta() == DataFrameMeta.Empty) + stage++; + break; + case Stage.UpdateReader: + reader.InitFrames(DataFrames); + wait.Update(); + wait.Update(); + stage++; + break; + case Stage.ValidateData: + if (TryResolveRaceAndClass(out UnitRace race, out UnitClass @class, out ClientVersion clientVersion)) + { + if (auto) { - logger.LogError($"Unable to identify {nameof(ClientVersion)} {nameof(UnitRace)} and {nameof(UnitClass)}!"); - stage = Stage.Reset; - - if (auto) - return false; + logger.LogInformation($"Found {clientVersion.ToStringF()} {race.ToStringF()} {@class.ToStringF()}!"); } - break; - case Stage.Done: - return false; - default: - break; - } - - return true; - } + stage++; + } + else + { + logger.LogError($"Unable to identify {nameof(ClientVersion)} {nameof(UnitRace)} and {nameof(UnitClass)}!"); + stage = Stage.Reset; - private void ResetConfigState() - { - screenRect = Rectangle.Empty; - size = Size.Empty; + if (auto) + return false; + } + break; + case Stage.Done: + return false; + default: + break; + } - AddonNotVisible = true; - stage = Stage.Reset; - Saved = false; + return true; + } - DataFrameMeta = DataFrameMeta.Empty; - DataFrames = Array.Empty(); - reader.InitFrames(DataFrames); - wait.Update(); - } + private void ResetConfigState() + { + screenRect = Rectangle.Empty; + size = Size.Empty; - private DataFrameMeta GetDataFrameMeta() - { - using Bitmap bitmap = wowScreen.GetBitmap(5, 5); - return FrameConfig.GetMeta(bitmap.GetPixel(0, 0)); - } + AddonNotVisible = true; + stage = Stage.Reset; + Saved = false; - public void ToggleManualConfig() - { - if (screenshotThread == null) - { - ResetConfigState(); + DataFrameMeta = DataFrameMeta.Empty; + DataFrames = Array.Empty(); - cts.Dispose(); - cts = new(); - screenshotThread = new Thread(ManualConfigThread); - screenshotThread.Start(); - } - else - { - cts.Cancel(); - } - } + reader.InitFrames(DataFrames); + wait.Update(); + } - public bool FinishConfig() - { - Version? version = addonConfigurator.GetInstallVersion(); - if (version == null || - DataFrames.Length == 0 || - DataFrameMeta.frames == 0 || - DataFrames.Length != DataFrameMeta.frames || - !TryResolveRaceAndClass(out _, out _, out _)) - { - logger.LogInformation($"Frame configuration was incomplete! Please try again, after resolving the previusly mentioned issues..."); - ResetConfigState(); - return false; - } + private DataFrameMeta GetDataFrameMeta() + { + using Bitmap bitmap = wowScreen.GetBitmap(5, 5); + return FrameConfig.GetMeta(bitmap.GetPixel(0, 0)); + } - wowScreen.GetRectangle(out Rectangle rect); - FrameConfig.Save(rect, version, DataFrameMeta, DataFrames); - logger.LogInformation($"Frame configuration was successful! Configuration saved!"); - Saved = true; + public void ToggleManualConfig() + { + if (screenshotThread == null) + { + ResetConfigState(); - return true; + cts.Dispose(); + cts = new(); + screenshotThread = new Thread(ManualConfigThread); + screenshotThread.Start(); } - - public bool StartAutoConfig() + else { - while (DoConfig(true)) - { - wait.Update(); - } - - return FinishConfig(); + cts.Cancel(); } + } - public static void DeleteConfig() + public bool FinishConfig() + { + Version? version = addonConfigurator.GetInstallVersion(); + if (version == null || + DataFrames.Length == 0 || + DataFrameMeta.frames == 0 || + DataFrames.Length != DataFrameMeta.frames || + !TryResolveRaceAndClass(out _, out _, out _)) { - FrameConfig.Delete(); + logger.LogInformation($"Frame configuration was incomplete! Please try again, after resolving the previusly mentioned issues..."); + ResetConfigState(); + return false; } - private void ToggleInGameConfiguration(ExecGameCommand exec) + wowScreen.GetRectangle(out Rectangle rect); + FrameConfig.Save(rect, version, DataFrameMeta, DataFrames); + logger.LogInformation($"Frame configuration was successful! Configuration saved!"); + Saved = true; + + return true; + } + + public bool StartAutoConfig() + { + while (DoConfig(true)) { - exec.Run($"/{addonConfigurator.Config.Command}"); + wait.Update(); } - public bool TryResolveRaceAndClass(out UnitRace race, out UnitClass @class, out ClientVersion version) - { - int value = reader.GetInt(46); + return FinishConfig(); + } - // RACE_ID * 10000 + CLASS_ID * 100 + ClientVersion - race = (UnitRace)(value / 10000); - @class = (UnitClass)(value / 100 % 100); - version = (ClientVersion)(value % 10); + public static void DeleteConfig() + { + FrameConfig.Delete(); + } - return Enum.IsDefined(race) && Enum.IsDefined(@class) && Enum.IsDefined(version) && - race != UnitRace.None && @class != UnitClass.None && version != ClientVersion.None; - } + private void ToggleInGameConfiguration(ExecGameCommand exec) + { + exec.Run($"/{addonConfigurator.Config.Command}"); + } + + public bool TryResolveRaceAndClass(out UnitRace race, out UnitClass @class, out ClientVersion version) + { + int value = reader.GetInt(46); + + // RACE_ID * 10000 + CLASS_ID * 100 + ClientVersion + race = (UnitRace)(value / 10000); + @class = (UnitClass)(value / 100 % 100); + version = (ClientVersion)(value % 10); + + return Enum.IsDefined(race) && Enum.IsDefined(@class) && Enum.IsDefined(version) && + race != UnitRace.None && @class != UnitClass.None && version != ClientVersion.None; } } diff --git a/Core/Cursor/CursorClassifier.cs b/Core/Cursor/CursorClassifier.cs index a0e63447a..04c9d2b84 100644 --- a/Core/Cursor/CursorClassifier.cs +++ b/Core/Cursor/CursorClassifier.cs @@ -10,95 +10,94 @@ #pragma warning disable CS0162 -namespace Core +namespace Core; + +public sealed class CursorClassifier : IDisposable { - public sealed class CursorClassifier : IDisposable + private const bool saveImage = false; + + // index matches CursorType order + private static readonly ulong[][] imageHashes = { - private const bool saveImage = false; + new ulong[] { 4645529528554094592, 4665762466636896256, 6376251547633783040, 6376251547633783552 }, + new ulong[] { 9286546093378506253, 16208728271425048093, 16208728271425052189 }, + new ulong[] { 16205332705670085656, 16495805933079509016 }, + new ulong[] { 13901748381153107456, 16207591392111181312 }, + new ulong[] { 4669700909741929478, 4669700909674820614 }, + new ulong[] { 4683320813727784960, 4669700909741929478, 4683461550142398464 }, + new ulong[] { 17940331276560775168, 17940331276594329600, 17940331276594460672 }, + new ulong[] { 16207573517913036808, 4669140166357294088, 14185844589096599552, 16491828335798648832 }, + new ulong[] { 4667452417086599168, 4676529985085517824 }, + new ulong[] { 4682718988357606424, 4682718988358655000 } + }; - // index matches CursorType order - private static readonly ulong[][] imageHashes = - { - new ulong[] { 4645529528554094592, 4665762466636896256, 6376251547633783040, 6376251547633783552 }, - new ulong[] { 9286546093378506253, 16208728271425048093, 16208728271425052189 }, - new ulong[] { 16205332705670085656, 16495805933079509016 }, - new ulong[] { 13901748381153107456, 16207591392111181312 }, - new ulong[] { 4669700909741929478, 4669700909674820614 }, - new ulong[] { 4683320813727784960, 4669700909741929478, 4683461550142398464 }, - new ulong[] { 17940331276560775168, 17940331276594329600, 17940331276594460672 }, - new ulong[] { 16207573517913036808, 4669140166357294088, 14185844589096599552, 16491828335798648832 }, - new ulong[] { 4667452417086599168, 4676529985085517824 }, - new ulong[] { 4682718988357606424, 4682718988358655000 } - }; - - private readonly Bitmap bitmap; - private readonly Graphics graphics; - - private readonly Bitmap workBitmap; - private readonly Graphics workGraphics; - - public CursorClassifier() - { - Size size = GetCursorSize(); - bitmap = new(size.Width, size.Height); - graphics = Graphics.FromImage(bitmap); - - workBitmap = new(8, 8, PixelFormat.Format32bppArgb); - workGraphics = Graphics.FromImage(workBitmap); - workGraphics.CompositingQuality = CompositingQuality.HighQuality; - workGraphics.InterpolationMode = InterpolationMode.HighQualityBilinear; - workGraphics.SmoothingMode = SmoothingMode.HighQuality; - } + private readonly Bitmap bitmap; + private readonly Graphics graphics; - public void Dispose() - { - graphics.Dispose(); - bitmap.Dispose(); + private readonly Bitmap workBitmap; + private readonly Graphics workGraphics; - workGraphics.Dispose(); - workBitmap.Dispose(); - } + public CursorClassifier() + { + Size size = GetCursorSize(); + bitmap = new(size.Width, size.Height); + graphics = Graphics.FromImage(bitmap); + + workBitmap = new(8, 8, PixelFormat.Format32bppArgb); + workGraphics = Graphics.FromImage(workBitmap); + workGraphics.CompositingQuality = CompositingQuality.HighQuality; + workGraphics.InterpolationMode = InterpolationMode.HighQualityBilinear; + workGraphics.SmoothingMode = SmoothingMode.HighQuality; + } + public void Dispose() + { + graphics.Dispose(); + bitmap.Dispose(); + + workGraphics.Dispose(); + workBitmap.Dispose(); + } - public void Classify(out CursorType classification) + + public void Classify(out CursorType classification) + { + CURSORINFO cursorInfo = new(); + cursorInfo.cbSize = Marshal.SizeOf(cursorInfo); + if (GetCursorInfo(ref cursorInfo) && + cursorInfo.flags == CURSOR_SHOWING) { - CURSORINFO cursorInfo = new(); - cursorInfo.cbSize = Marshal.SizeOf(cursorInfo); - if (GetCursorInfo(ref cursorInfo) && - cursorInfo.flags == CURSOR_SHOWING) - { - graphics.Clear(Color.Transparent); - DrawIcon(graphics.GetHdc(), 0, 0, cursorInfo.hCursor); - graphics.ReleaseHdc(); - } + graphics.Clear(Color.Transparent); + DrawIcon(graphics.GetHdc(), 0, 0, cursorInfo.hCursor); + graphics.ReleaseHdc(); + } - ulong cursorHash = ImageHashing.AverageHash(bitmap, workBitmap, workGraphics); - if (saveImage) + ulong cursorHash = ImageHashing.AverageHash(bitmap, workBitmap, workGraphics); + if (saveImage) + { + string path = Path.Join("..", "..", "..", "..", "Cursors", $"{cursorHash}.bmp"); + if (!File.Exists(path)) { - string path = Path.Join("..", "..", "..", "..", "Cursors", $"{cursorHash}.bmp"); - if (!File.Exists(path)) - { - bitmap.Save(path); - } + bitmap.Save(path); } + } - int index = 0; - double similarity = 0; - for (int i = 0; i < imageHashes.Length; i++) + int index = 0; + double similarity = 0; + for (int i = 0; i < imageHashes.Length; i++) + { + for (int j = 0; j < imageHashes[i].Length; j++) { - for (int j = 0; j < imageHashes[i].Length; j++) + double sim = ImageHashing.Similarity(cursorHash, imageHashes[i][j]); + if (sim > 80 && sim > similarity) { - double sim = ImageHashing.Similarity(cursorHash, imageHashes[i][j]); - if (sim > 80 && sim > similarity) - { - index = i; - similarity = sim; - } + index = i; + similarity = sim; } } - - classification = (CursorType)index; - Debug.WriteLine($"[CursorClassifier.Classify] {classification.ToStringF()} - {similarity}"); } + + classification = (CursorType)index; + Debug.WriteLine($"[CursorClassifier.Classify] {classification.ToStringF()} - {similarity}"); } } \ No newline at end of file diff --git a/Core/Cursor/CursorType.cs b/Core/Cursor/CursorType.cs index b094e23c7..6dbf723b6 100644 --- a/Core/Cursor/CursorType.cs +++ b/Core/Cursor/CursorType.cs @@ -1,35 +1,34 @@ -namespace Core +namespace Core; + +public enum CursorType { - public enum CursorType - { - None = 0, - Kill = 1, - Loot = 2, - Skin = 3, - Mine = 4, - Herb = 5, - Vendor = 6, - Repair = 7, - Innkeeper = 8, - Quest = 9 - // todo salvage icon - } + None = 0, + Kill = 1, + Loot = 2, + Skin = 3, + Mine = 4, + Herb = 5, + Vendor = 6, + Repair = 7, + Innkeeper = 8, + Quest = 9 + // todo salvage icon +} - public static class CursorType_Extension +public static class CursorType_Extension +{ + public static string ToStringF(this CursorType value) => value switch { - public static string ToStringF(this CursorType value) => value switch - { - CursorType.None => nameof(CursorType.None), - CursorType.Kill => nameof(CursorType.Kill), - CursorType.Loot => nameof(CursorType.Loot), - CursorType.Skin => nameof(CursorType.Skin), - CursorType.Mine => nameof(CursorType.Mine), - CursorType.Herb => nameof(CursorType.Herb), - CursorType.Vendor => nameof(CursorType.Vendor), - CursorType.Repair => nameof(CursorType.Repair), - CursorType.Innkeeper => nameof(CursorType.Innkeeper), - CursorType.Quest => nameof(CursorType.Quest), - _ => nameof(CursorType.None) - }; - } + CursorType.None => nameof(CursorType.None), + CursorType.Kill => nameof(CursorType.Kill), + CursorType.Loot => nameof(CursorType.Loot), + CursorType.Skin => nameof(CursorType.Skin), + CursorType.Mine => nameof(CursorType.Mine), + CursorType.Herb => nameof(CursorType.Herb), + CursorType.Vendor => nameof(CursorType.Vendor), + CursorType.Repair => nameof(CursorType.Repair), + CursorType.Innkeeper => nameof(CursorType.Innkeeper), + CursorType.Quest => nameof(CursorType.Quest), + _ => nameof(CursorType.None) + }; } \ No newline at end of file diff --git a/Core/Cursor/ImageHashing.cs b/Core/Cursor/ImageHashing.cs index 596807b14..dc45498a4 100644 --- a/Core/Cursor/ImageHashing.cs +++ b/Core/Cursor/ImageHashing.cs @@ -2,109 +2,108 @@ using System.Drawing; using System.Drawing.Imaging; -namespace Core +namespace Core; + +/// +/// Contains a variety of methods useful in generating image hashes for image comparison +/// and recognition. +/// https://github.com/jforshee/ImageHashing +/// Credit for the AverageHash implementation to David Oftedal of the University of Oslo. +/// +public static class ImageHashing { /// - /// Contains a variety of methods useful in generating image hashes for image comparison - /// and recognition. - /// https://github.com/jforshee/ImageHashing - /// Credit for the AverageHash implementation to David Oftedal of the University of Oslo. + /// Bitcounts array used for BitCount method (used in Similarity comparisons). + /// Don't try to read this or understand it, I certainly don't. Credit goes to + /// David Oftedal of the University of Oslo, Norway for this. + /// http://folk.uio.no/davidjo/computing.php /// - public static class ImageHashing - { - /// - /// Bitcounts array used for BitCount method (used in Similarity comparisons). - /// Don't try to read this or understand it, I certainly don't. Credit goes to - /// David Oftedal of the University of Oslo, Norway for this. - /// http://folk.uio.no/davidjo/computing.php - /// - private static readonly byte[] bitCounts = { - 0,1,1,2,1,2,2,3,1,2,2,3,2,3,3,4,1,2,2,3,2,3,3,4,2,3,3,4,3,4,4,5,1,2,2,3,2,3,3,4, - 2,3,3,4,3,4,4,5,2,3,3,4,3,4,4,5,3,4,4,5,4,5,5,6,1,2,2,3,2,3,3,4,2,3,3,4,3,4,4,5, - 2,3,3,4,3,4,4,5,3,4,4,5,4,5,5,6,2,3,3,4,3,4,4,5,3,4,4,5,4,5,5,6,3,4,4,5,4,5,5,6, - 4,5,5,6,5,6,6,7,1,2,2,3,2,3,3,4,2,3,3,4,3,4,4,5,2,3,3,4,3,4,4,5,3,4,4,5,4,5,5,6, - 2,3,3,4,3,4,4,5,3,4,4,5,4,5,5,6,3,4,4,5,4,5,5,6,4,5,5,6,5,6,6,7,2,3,3,4,3,4,4,5, - 3,4,4,5,4,5,5,6,3,4,4,5,4,5,5,6,4,5,5,6,5,6,6,7,3,4,4,5,4,5,5,6,4,5,5,6,5,6,6,7, - 4,5,5,6,5,6,6,7,5,6,6,7,6,7,7,8 - }; + private static readonly byte[] bitCounts = { + 0,1,1,2,1,2,2,3,1,2,2,3,2,3,3,4,1,2,2,3,2,3,3,4,2,3,3,4,3,4,4,5,1,2,2,3,2,3,3,4, + 2,3,3,4,3,4,4,5,2,3,3,4,3,4,4,5,3,4,4,5,4,5,5,6,1,2,2,3,2,3,3,4,2,3,3,4,3,4,4,5, + 2,3,3,4,3,4,4,5,3,4,4,5,4,5,5,6,2,3,3,4,3,4,4,5,3,4,4,5,4,5,5,6,3,4,4,5,4,5,5,6, + 4,5,5,6,5,6,6,7,1,2,2,3,2,3,3,4,2,3,3,4,3,4,4,5,2,3,3,4,3,4,4,5,3,4,4,5,4,5,5,6, + 2,3,3,4,3,4,4,5,3,4,4,5,4,5,5,6,3,4,4,5,4,5,5,6,4,5,5,6,5,6,6,7,2,3,3,4,3,4,4,5, + 3,4,4,5,4,5,5,6,3,4,4,5,4,5,5,6,4,5,5,6,5,6,6,7,3,4,4,5,4,5,5,6,4,5,5,6,5,6,6,7, + 4,5,5,6,5,6,6,7,5,6,6,7,6,7,7,8 + }; - /// - /// Counts bits (duh). Utility function for similarity. - /// I wouldn't try to understand this. I just copy-pasta'd it - /// from Oftedal's implementation. It works. - /// - /// The hash we are counting. - /// The total bit count. - private static uint BitCount(ulong num) - { - uint count = 0; - for (; num > 0; num >>= 8) - count += bitCounts[num & 0xff]; - return count; - } + /// + /// Counts bits (duh). Utility function for similarity. + /// I wouldn't try to understand this. I just copy-pasta'd it + /// from Oftedal's implementation. It works. + /// + /// The hash we are counting. + /// The total bit count. + private static uint BitCount(ulong num) + { + uint count = 0; + for (; num > 0; num >>= 8) + count += bitCounts[num & 0xff]; + return count; + } - /// - /// Computes the average hash of an image according to the algorithm given by Dr. Neal Krawetz - /// on his blog: http://www.hackerfactor.com/blog/index.php?/archives/432-Looks-Like-It.html. - /// - /// The image to hash. - /// The hash of the image. - public static unsafe ulong AverageHash(Bitmap image, Bitmap squeezed, Graphics canvas) - { - Rectangle rect = new(0, 0, 8, 8); - canvas.Clear(Color.Transparent); - canvas.DrawImage(image, rect); + /// + /// Computes the average hash of an image according to the algorithm given by Dr. Neal Krawetz + /// on his blog: http://www.hackerfactor.com/blog/index.php?/archives/432-Looks-Like-It.html. + /// + /// The image to hash. + /// The hash of the image. + public static unsafe ulong AverageHash(Bitmap image, Bitmap squeezed, Graphics canvas) + { + Rectangle rect = new(0, 0, 8, 8); + canvas.Clear(Color.Transparent); + canvas.DrawImage(image, rect); - // Reduce colors to 6-bit grayscale and calculate average color value - Span grayscale = stackalloc byte[64]; + // Reduce colors to 6-bit grayscale and calculate average color value + Span grayscale = stackalloc byte[64]; - const int bytesPerPixel = 4; //Image.GetPixelFormatSize(squeezed.PixelFormat) / 8; - BitmapData data = squeezed.LockBits(rect, ImageLockMode.ReadOnly, squeezed.PixelFormat); + const int bytesPerPixel = 4; //Image.GetPixelFormatSize(squeezed.PixelFormat) / 8; + BitmapData data = squeezed.LockBits(rect, ImageLockMode.ReadOnly, squeezed.PixelFormat); - uint averageValue = 0; - for (int y = 0; y < 8; y++) + uint averageValue = 0; + for (int y = 0; y < 8; y++) + { + for (int x = 0; x < 8; x++) { - for (int x = 0; x < 8; x++) - { - byte* pixel = (byte*)data.Scan0 + (data.Stride * y) + (bytesPerPixel * x); - uint argb = (uint)(pixel[0] | (pixel[1] << 8) | (pixel[2] << 16) | (pixel[3] << 24)); - uint gray = (argb & 0x00ff0000) >> 16; - gray += (argb & 0x0000ff00) >> 8; - gray += (argb & 0x000000ff); - gray /= 12; + byte* pixel = (byte*)data.Scan0 + (data.Stride * y) + (bytesPerPixel * x); + uint argb = (uint)(pixel[0] | (pixel[1] << 8) | (pixel[2] << 16) | (pixel[3] << 24)); + uint gray = (argb & 0x00ff0000) >> 16; + gray += (argb & 0x0000ff00) >> 8; + gray += (argb & 0x000000ff); + gray /= 12; - grayscale[x + (y * 8)] = (byte)gray; - averageValue += gray; - } + grayscale[x + (y * 8)] = (byte)gray; + averageValue += gray; } - squeezed.UnlockBits(data); + } + squeezed.UnlockBits(data); - averageValue /= 64; + averageValue /= 64; - // Compute the hash: each bit is a pixel - // 1 = higher than average, 0 = lower than average - ulong hash = 0; - for (int i = 0; i < 64; i++) + // Compute the hash: each bit is a pixel + // 1 = higher than average, 0 = lower than average + ulong hash = 0; + for (int i = 0; i < 64; i++) + { + if (grayscale[i] >= averageValue) { - if (grayscale[i] >= averageValue) - { - hash |= 1UL << (63 - i); - } + hash |= 1UL << (63 - i); } - - return hash; } - /// - /// Returns a percentage-based similarity value between the two given hashes. The higher - /// the percentage, the closer the hashes are to being identical. - /// - /// The first hash. - /// The second hash. - /// The similarity percentage. - public static double Similarity(ulong hash1, ulong hash2) - { - return ((64.0 - BitCount(hash1 ^ hash2)) * 100.0) / 64.0; - } + return hash; + } + + /// + /// Returns a percentage-based similarity value between the two given hashes. The higher + /// the percentage, the closer the hashes are to being identical. + /// + /// The first hash. + /// The second hash. + /// The similarity percentage. + public static double Similarity(ulong hash1, ulong hash2) + { + return ((64.0 - BitCount(hash1 ^ hash2)) * 100.0) / 64.0; } } \ No newline at end of file diff --git a/Core/DataFrame/DataFrame.cs b/Core/DataFrame/DataFrame.cs index fc40a79fd..4221f7b6a 100644 --- a/Core/DataFrame/DataFrame.cs +++ b/Core/DataFrame/DataFrame.cs @@ -1,16 +1,15 @@ -namespace Core +namespace Core; + +public readonly struct DataFrame { - public readonly struct DataFrame - { - public readonly int Index; - public readonly int X; - public readonly int Y; + public readonly int Index; + public readonly int X; + public readonly int Y; - public DataFrame(int index, int x, int y) - { - Index = index; - X = x; - Y = y; - } + public DataFrame(int index, int x, int y) + { + Index = index; + X = x; + Y = y; } } \ No newline at end of file diff --git a/Core/DataFrame/DataFrameConfig.cs b/Core/DataFrame/DataFrameConfig.cs index 6b4919e65..489a6b972 100644 --- a/Core/DataFrame/DataFrameConfig.cs +++ b/Core/DataFrame/DataFrameConfig.cs @@ -1,23 +1,22 @@ using System; using System.Drawing; -namespace Core +namespace Core; + +public readonly struct DataFrameConfig { - public readonly struct DataFrameConfig - { - public int Version { get; } - public Version addonVersion { get; } - public Rectangle rect { get; } - public DataFrameMeta meta { get; } - public DataFrame[] frames { get; } + public int Version { get; } + public Version addonVersion { get; } + public Rectangle rect { get; } + public DataFrameMeta meta { get; } + public DataFrame[] frames { get; } - public DataFrameConfig(int version, Version addonVersion, Rectangle rect, DataFrameMeta meta, DataFrame[] frames) - { - Version = version; - this.addonVersion = addonVersion; - this.rect = rect; - this.meta = meta; - this.frames = frames; - } + public DataFrameConfig(int version, Version addonVersion, Rectangle rect, DataFrameMeta meta, DataFrame[] frames) + { + Version = version; + this.addonVersion = addonVersion; + this.rect = rect; + this.meta = meta; + this.frames = frames; } } diff --git a/Core/DataFrame/DataFrameMeta.cs b/Core/DataFrame/DataFrameMeta.cs index d6be36ee7..7bb3da79a 100644 --- a/Core/DataFrame/DataFrameMeta.cs +++ b/Core/DataFrame/DataFrameMeta.cs @@ -2,84 +2,83 @@ using System.Drawing; using Newtonsoft.Json; -namespace Core -{ - public readonly struct DataFrameMeta : IEquatable - { - [JsonIgnore] - public static DataFrameMeta Empty { get; } = new(-1, 0, 0, 0, 0); - - [JsonConstructor] - public DataFrameMeta(int hash, int spacing, int size, int rows, int frames) - { - this.hash = hash; - this.spacing = spacing; - this.size = size; - this.rows = rows; - this.frames = frames; - } +namespace Core; - public int hash { get; } +public readonly struct DataFrameMeta : IEquatable +{ + [JsonIgnore] + public static DataFrameMeta Empty { get; } = new(-1, 0, 0, 0, 0); - public int spacing { get; } + [JsonConstructor] + public DataFrameMeta(int hash, int spacing, int size, int rows, int frames) + { + this.hash = hash; + this.spacing = spacing; + this.size = size; + this.rows = rows; + this.frames = frames; + } - public int size { get; } + public int hash { get; } - public int rows { get; } + public int spacing { get; } - public int frames { get; } + public int size { get; } - public Size EstimatedSize(Rectangle screenRect) - { - const int error = 2; + public int rows { get; } - int squareSize = size + error + (spacing != 0 ? spacing + error : 0); - if (squareSize <= 0) - return Size.Empty; + public int frames { get; } - SizeF estimatedSize = new((float)Math.Ceiling(frames / (float)rows) * squareSize, rows * squareSize); + public Size EstimatedSize(Rectangle screenRect) + { + const int error = 2; - if (estimatedSize.Width > screenRect.Width || - estimatedSize.Height > screenRect.Height) - { - return Size.Empty; - } + int squareSize = size + error + (spacing != 0 ? spacing + error : 0); + if (squareSize <= 0) + return Size.Empty; - return estimatedSize.ToSize(); - } + SizeF estimatedSize = new((float)Math.Ceiling(frames / (float)rows) * squareSize, rows * squareSize); - public override int GetHashCode() + if (estimatedSize.Width > screenRect.Width || + estimatedSize.Height > screenRect.Height) { - return hash; + return Size.Empty; } - public static bool operator ==(DataFrameMeta left, DataFrameMeta right) - { - return left.Equals(right); - } + return estimatedSize.ToSize(); + } - public static bool operator !=(DataFrameMeta left, DataFrameMeta right) - { - return !(left == right); - } + public override int GetHashCode() + { + return hash; + } - public bool Equals(DataFrameMeta other) - { - return other.hash == hash && - other.spacing == spacing && - other.size == size && - other.rows == rows && - other.frames == frames; - } + public static bool operator ==(DataFrameMeta left, DataFrameMeta right) + { + return left.Equals(right); + } - public override bool Equals(object? obj) - { - return obj is DataFrameMeta && Equals((DataFrameMeta)obj); - } + public static bool operator !=(DataFrameMeta left, DataFrameMeta right) + { + return !(left == right); + } - public override string ToString() - { - return $"hash: {hash} | spacing: {spacing} | size: {size} | rows: {rows} | frames: {frames}"; - } + public bool Equals(DataFrameMeta other) + { + return other.hash == hash && + other.spacing == spacing && + other.size == size && + other.rows == rows && + other.frames == frames; + } + + public override bool Equals(object? obj) + { + return obj is DataFrameMeta && Equals((DataFrameMeta)obj); + } + + public override string ToString() + { + return $"hash: {hash} | spacing: {spacing} | size: {size} | rows: {rows} | frames: {frames}"; } } diff --git a/Core/DataFrame/FrameConfig.cs b/Core/DataFrame/FrameConfig.cs index 42dd8dd02..6cce2ab4b 100644 --- a/Core/DataFrame/FrameConfig.cs +++ b/Core/DataFrame/FrameConfig.cs @@ -3,128 +3,127 @@ using System.Drawing; using System.IO; -namespace Core +namespace Core; + +public static class FrameConfigMeta +{ + public const int Version = 3; + public const string DefaultFilename = "frame_config.json"; +} + +public static class FrameConfig { - public static class FrameConfigMeta + public static bool Exists() { - public const int Version = 3; - public const string DefaultFilename = "frame_config.json"; + return File.Exists(FrameConfigMeta.DefaultFilename); } - public static class FrameConfig + public static bool IsValid(Rectangle rect, Version addonVersion) { - public static bool Exists() + try { - return File.Exists(FrameConfigMeta.DefaultFilename); - } - - public static bool IsValid(Rectangle rect, Version addonVersion) - { - try - { - var config = JsonConvert.DeserializeObject(File.ReadAllText(FrameConfigMeta.DefaultFilename)); + var config = JsonConvert.DeserializeObject(File.ReadAllText(FrameConfigMeta.DefaultFilename)); - bool sameVersion = config.Version == FrameConfigMeta.Version; - bool sameAddonVersion = config.addonVersion == addonVersion; - bool sameRect = config.rect.Width == rect.Width && config.rect.Height == rect.Height; - return sameAddonVersion && sameVersion && sameRect && config.frames.Length > 1; - } - catch - { - return false; - } + bool sameVersion = config.Version == FrameConfigMeta.Version; + bool sameAddonVersion = config.addonVersion == addonVersion; + bool sameRect = config.rect.Width == rect.Width && config.rect.Height == rect.Height; + return sameAddonVersion && sameVersion && sameRect && config.frames.Length > 1; } - - public static DataFrame[] LoadFrames() + catch { - if (Exists()) - { - var config = JsonConvert.DeserializeObject(File.ReadAllText(FrameConfigMeta.DefaultFilename)); - if (config.Version == FrameConfigMeta.Version) - return config.frames; - } - - return Array.Empty(); + return false; } + } - public static DataFrameMeta LoadMeta() + public static DataFrame[] LoadFrames() + { + if (Exists()) { var config = JsonConvert.DeserializeObject(File.ReadAllText(FrameConfigMeta.DefaultFilename)); if (config.Version == FrameConfigMeta.Version) - return config.meta; - - return DataFrameMeta.Empty; + return config.frames; } - public static void Save(Rectangle rect, Version addonVersion, DataFrameMeta meta, DataFrame[] dataFrames) - { - DataFrameConfig config = new(FrameConfigMeta.Version, addonVersion, rect, meta, dataFrames); + return Array.Empty(); + } - string json = JsonConvert.SerializeObject(config); - File.WriteAllText(FrameConfigMeta.DefaultFilename, json); - } + public static DataFrameMeta LoadMeta() + { + var config = JsonConvert.DeserializeObject(File.ReadAllText(FrameConfigMeta.DefaultFilename)); + if (config.Version == FrameConfigMeta.Version) + return config.meta; + + return DataFrameMeta.Empty; + } + + public static void Save(Rectangle rect, Version addonVersion, DataFrameMeta meta, DataFrame[] dataFrames) + { + DataFrameConfig config = new(FrameConfigMeta.Version, addonVersion, rect, meta, dataFrames); - public static void Delete() + string json = JsonConvert.SerializeObject(config); + File.WriteAllText(FrameConfigMeta.DefaultFilename, json); + } + + public static void Delete() + { + if (Exists()) { - if (Exists()) - { - File.Delete(FrameConfigMeta.DefaultFilename); - } + File.Delete(FrameConfigMeta.DefaultFilename); } + } - public static DataFrameMeta GetMeta(Color color) - { - int hash = color.R * 65536 + color.G * 256 + color.B; - if (hash == 0) - return DataFrameMeta.Empty; + public static DataFrameMeta GetMeta(Color color) + { + int hash = color.R * 65536 + color.G * 256 + color.B; + if (hash == 0) + return DataFrameMeta.Empty; - // CELL_SPACING * 10000000 + CELL_SIZE * 100000 + 1000 * FRAME_ROWS + NUMBER_OF_FRAMES - int spacing = hash / 10000000; - int size = hash / 100000 % 100; - int rows = hash / 1000 % 100; - int count = hash % 1000; + // CELL_SPACING * 10000000 + CELL_SIZE * 100000 + 1000 * FRAME_ROWS + NUMBER_OF_FRAMES + int spacing = hash / 10000000; + int size = hash / 100000 % 100; + int rows = hash / 1000 % 100; + int count = hash % 1000; - return new DataFrameMeta(hash, spacing, size, rows, count); - } + return new DataFrameMeta(hash, spacing, size, rows, count); + } - public static DataFrame[] TryCreateFrames(DataFrameMeta meta, Bitmap bmp) - { - DataFrame[] frames = new DataFrame[meta.frames]; - frames[0] = new(0, 0, 0); + public static DataFrame[] TryCreateFrames(DataFrameMeta meta, Bitmap bmp) + { + DataFrame[] frames = new DataFrame[meta.frames]; + frames[0] = new(0, 0, 0); - for (int i = 1; i < meta.frames; i++) + for (int i = 1; i < meta.frames; i++) + { + if (TryGetNextPoint(bmp, i, frames[i].X, out int x, out int y)) { - if (TryGetNextPoint(bmp, i, frames[i].X, out int x, out int y)) - { - frames[i] = new(i, x, y); - } - else - { - break; - } + frames[i] = new(i, x, y); + } + else + { + break; } - - return frames; } - private static bool TryGetNextPoint(Bitmap bmp, int i, int startX, out int x, out int y) + return frames; + } + + private static bool TryGetNextPoint(Bitmap bmp, int i, int startX, out int x, out int y) + { + for (int xi = startX; xi < bmp.Width; xi++) { - for (int xi = startX; xi < bmp.Width; xi++) + for (int yi = 0; yi < bmp.Height; yi++) { - for (int yi = 0; yi < bmp.Height; yi++) + Color pixel = bmp.GetPixel(xi, yi); + if (pixel.B == i && pixel.R == 0 && pixel.G == 0) { - Color pixel = bmp.GetPixel(xi, yi); - if (pixel.B == i && pixel.R == 0 && pixel.G == 0) - { - x = xi; - y = yi; - return true; - } + x = xi; + y = yi; + return true; } } - - x = y = -1; - return false; } + + x = y = -1; + return false; } } \ No newline at end of file diff --git a/Core/Database/AreaDB.cs b/Core/Database/AreaDB.cs index 44050eb6d..7360f969e 100644 --- a/Core/Database/AreaDB.cs +++ b/Core/Database/AreaDB.cs @@ -7,87 +7,86 @@ using SharedLib.Extensions; using WowheadDB; -namespace Core.Database +namespace Core.Database; + +public sealed class AreaDB : IDisposable { - public sealed class AreaDB : IDisposable - { - private readonly ILogger logger; - private readonly DataConfig dataConfig; + private readonly ILogger logger; + private readonly DataConfig dataConfig; - private readonly CancellationToken ct; - private readonly AutoResetEvent autoResetEvent; - private readonly Thread thread; + private readonly CancellationToken ct; + private readonly AutoResetEvent autoResetEvent; + private readonly Thread thread; - private int areaId = -1; - public Area? CurrentArea { private set; get; } + private int areaId = -1; + public Area? CurrentArea { private set; get; } - public event Action? Changed; + public event Action? Changed; - public AreaDB(ILogger logger, DataConfig dataConfig, CancellationTokenSource cts) - { - this.logger = logger; - this.dataConfig = dataConfig; - ct = cts.Token; - autoResetEvent = new AutoResetEvent(false); + public AreaDB(ILogger logger, DataConfig dataConfig, CancellationTokenSource cts) + { + this.logger = logger; + this.dataConfig = dataConfig; + ct = cts.Token; + autoResetEvent = new AutoResetEvent(false); - thread = new(ReadArea); - thread.Start(); - } + thread = new(ReadArea); + thread.Start(); + } - public void Dispose() - { - autoResetEvent.Set(); - } + public void Dispose() + { + autoResetEvent.Set(); + } - public void Update(int areaId) - { - if (this.areaId == areaId) - return; + public void Update(int areaId) + { + if (this.areaId == areaId) + return; - this.areaId = areaId; - autoResetEvent.Set(); - } + this.areaId = areaId; + autoResetEvent.Set(); + } - private void ReadArea() - { - autoResetEvent.WaitOne(); + private void ReadArea() + { + autoResetEvent.WaitOne(); - while (!ct.IsCancellationRequested) + while (!ct.IsCancellationRequested) + { + try { - try - { - CurrentArea = JsonConvert.DeserializeObject(File.ReadAllText(Path.Join(dataConfig.ExpArea, $"{areaId}.json"))); - Changed?.Invoke(); - } - catch (Exception e) - { - logger.LogError(e.Message, e.StackTrace); - } - - autoResetEvent.WaitOne(); + CurrentArea = JsonConvert.DeserializeObject(File.ReadAllText(Path.Join(dataConfig.ExpArea, $"{areaId}.json"))); + Changed?.Invoke(); } + catch (Exception e) + { + logger.LogError(e.Message, e.StackTrace); + } + + autoResetEvent.WaitOne(); } + } - public Vector3 GetNearestVendor(Vector3 map) - { - if (CurrentArea == null || CurrentArea.vendor.Count == 0) - return Vector3.Zero; + public Vector3 GetNearestVendor(Vector3 map) + { + if (CurrentArea == null || CurrentArea.vendor.Count == 0) + return Vector3.Zero; - NPC closestNpc = CurrentArea.vendor[0]; - float mapDistance = map.MapDistanceXYTo(closestNpc.MapCoords[0]); + NPC closestNpc = CurrentArea.vendor[0]; + float mapDistance = map.MapDistanceXYTo(closestNpc.MapCoords[0]); - for (int i = 0; i < CurrentArea.vendor.Count; i++) + for (int i = 0; i < CurrentArea.vendor.Count; i++) + { + NPC npc = CurrentArea.vendor[i]; + float d = map.MapDistanceXYTo(npc.MapCoords[0]); + if (d < mapDistance) { - NPC npc = CurrentArea.vendor[i]; - float d = map.MapDistanceXYTo(npc.MapCoords[0]); - if (d < mapDistance) - { - mapDistance = d; - closestNpc = npc; - } + mapDistance = d; + closestNpc = npc; } - - return closestNpc.MapCoords[0]; } + + return closestNpc.MapCoords[0]; } } diff --git a/Core/Database/CreatureDB.cs b/Core/Database/CreatureDB.cs index c0fcf12f4..e35f2513d 100644 --- a/Core/Database/CreatureDB.cs +++ b/Core/Database/CreatureDB.cs @@ -5,21 +5,20 @@ using SharedLib; -namespace Core.Database +namespace Core.Database; + +public sealed class CreatureDB { - public sealed class CreatureDB + public Dictionary Entries { get; } = new(); + + public CreatureDB(DataConfig dataConfig) { - public Dictionary Entries { get; } = new(); + var creatures = DeserializeObject(ReadAllText(Join(dataConfig.ExpDbc, "creatures.json")))!; - public CreatureDB(DataConfig dataConfig) + for (int i = 0; i < creatures.Length; i++) { - var creatures = DeserializeObject(ReadAllText(Join(dataConfig.ExpDbc, "creatures.json")))!; - - for (int i = 0; i < creatures.Length; i++) - { - Entries.Add(creatures[i].Entry, creatures[i]); - } + Entries.Add(creatures[i].Entry, creatures[i]); } - } + } diff --git a/Core/Database/ItemDB.cs b/Core/Database/ItemDB.cs index 1c2601200..7df1f8bbb 100644 --- a/Core/Database/ItemDB.cs +++ b/Core/Database/ItemDB.cs @@ -4,26 +4,25 @@ using static Newtonsoft.Json.JsonConvert; using SharedLib; -namespace Core.Database +namespace Core.Database; + +public sealed class ItemDB { - public sealed class ItemDB - { - public static readonly Item EmptyItem = new() { Entry = 0, Name = string.Empty, Quality = 0, SellPrice = 0 }; + public static readonly Item EmptyItem = new() { Entry = 0, Name = string.Empty, Quality = 0, SellPrice = 0 }; - public Dictionary Items { get; } = new(); - public int[] FoodIds { get; } - public int[] DrinkIds { get; } + public Dictionary Items { get; } = new(); + public int[] FoodIds { get; } + public int[] DrinkIds { get; } - public ItemDB(DataConfig dataConfig) + public ItemDB(DataConfig dataConfig) + { + Item[] items = DeserializeObject(ReadAllText(Join(dataConfig.ExpDbc, "items.json")))!; + for (int i = 0; i < items.Length; i++) { - Item[] items = DeserializeObject(ReadAllText(Join(dataConfig.ExpDbc, "items.json")))!; - for (int i = 0; i < items.Length; i++) - { - Items.Add(items[i].Entry, items[i]); - } - - FoodIds = DeserializeObject(ReadAllText(Join(dataConfig.ExpDbc, "foods.json")))!; - DrinkIds = DeserializeObject(ReadAllText(Join(dataConfig.ExpDbc, "waters.json")))!; + Items.Add(items[i].Entry, items[i]); } + + FoodIds = DeserializeObject(ReadAllText(Join(dataConfig.ExpDbc, "foods.json")))!; + DrinkIds = DeserializeObject(ReadAllText(Join(dataConfig.ExpDbc, "waters.json")))!; } } diff --git a/Core/Database/SpellDB.cs b/Core/Database/SpellDB.cs index f992a55b7..5c2f7cc12 100644 --- a/Core/Database/SpellDB.cs +++ b/Core/Database/SpellDB.cs @@ -4,19 +4,18 @@ using static Newtonsoft.Json.JsonConvert; using SharedLib; -namespace Core.Database +namespace Core.Database; + +public sealed class SpellDB { - public sealed class SpellDB - { - public Dictionary Spells { get; } = new(); + public Dictionary Spells { get; } = new(); - public SpellDB(DataConfig dataConfig) + public SpellDB(DataConfig dataConfig) + { + Spell[] temp = DeserializeObject(ReadAllText(Join(dataConfig.ExpDbc, "spells.json")))!; + for (int i = 0; i < temp.Length; i++) { - Spell[] temp = DeserializeObject(ReadAllText(Join(dataConfig.ExpDbc, "spells.json")))!; - for (int i = 0; i < temp.Length; i++) - { - Spells.Add(temp[i].Id, temp[i]); - } + Spells.Add(temp[i].Id, temp[i]); } } } diff --git a/Core/Database/TalentDB.cs b/Core/Database/TalentDB.cs index 241e3ea65..9c32f9e3e 100644 --- a/Core/Database/TalentDB.cs +++ b/Core/Database/TalentDB.cs @@ -5,65 +5,64 @@ using Core.Talents; using SharedLib; -namespace Core.Database +namespace Core.Database; + +public sealed class TalentDB { - public sealed class TalentDB - { - private readonly SpellDB spellDB; + private readonly SpellDB spellDB; - private readonly TalentTab[] talentTabs; - private readonly TalentTreeElement[] talentTreeElements; + private readonly TalentTab[] talentTabs; + private readonly TalentTreeElement[] talentTreeElements; - public TalentDB(DataConfig dataConfig, SpellDB spellDB) - { - this.spellDB = spellDB; + public TalentDB(DataConfig dataConfig, SpellDB spellDB) + { + this.spellDB = spellDB; - talentTabs = DeserializeObject(ReadAllText(Join(dataConfig.ExpDbc, "talenttab.json")))!; - talentTreeElements = DeserializeObject(ReadAllText(Join(dataConfig.ExpDbc, "talent.json")))!; - } + talentTabs = DeserializeObject(ReadAllText(Join(dataConfig.ExpDbc, "talenttab.json")))!; + talentTreeElements = DeserializeObject(ReadAllText(Join(dataConfig.ExpDbc, "talent.json")))!; + } - public bool Update(ref Talent talent, UnitClass @class, out int spellId) - { - int classMask = (int)Math.Pow(2, (int)@class - 1); + public bool Update(ref Talent talent, UnitClass @class, out int spellId) + { + int classMask = (int)Math.Pow(2, (int)@class - 1); - int tabId = -1; - int tabIndex = talent.TabNum - 1; - for (int i = 0; i < talentTabs.Length; i++) + int tabId = -1; + int tabIndex = talent.TabNum - 1; + for (int i = 0; i < talentTabs.Length; i++) + { + if (talentTabs[i].ClassMask == classMask && + talentTabs[i].OrderIndex == tabIndex) { - if (talentTabs[i].ClassMask == classMask && - talentTabs[i].OrderIndex == tabIndex) - { - tabId = talentTabs[i].Id; - break; - } + tabId = talentTabs[i].Id; + break; } - spellId = 1; - if (tabId == -1) return false; - - int tierIndex = talent.TierNum - 1; - int columnIndex = talent.ColumnNum - 1; - int rankIndex = talent.CurrentRank - 1; + } + spellId = 1; + if (tabId == -1) return false; - int index = -1; - for (int i = 0; i < talentTreeElements.Length; i++) - { - if (talentTreeElements[i].TabID == tabId && - talentTreeElements[i].TierID == tierIndex && - talentTreeElements[i].ColumnIndex == columnIndex) - { - index = i; - break; - } - } + int tierIndex = talent.TierNum - 1; + int columnIndex = talent.ColumnNum - 1; + int rankIndex = talent.CurrentRank - 1; - spellId = talentTreeElements[index].SpellIds[rankIndex]; - if (spellDB.Spells.TryGetValue(spellId, out Spell spell)) + int index = -1; + for (int i = 0; i < talentTreeElements.Length; i++) + { + if (talentTreeElements[i].TabID == tabId && + talentTreeElements[i].TierID == tierIndex && + talentTreeElements[i].ColumnIndex == columnIndex) { - talent.Name = spell.Name; - return true; + index = i; + break; } + } - return false; + spellId = talentTreeElements[index].SpellIds[rankIndex]; + if (spellDB.Spells.TryGetValue(spellId, out Spell spell)) + { + talent.Name = spell.Name; + return true; } + + return false; } } diff --git a/Core/Environment/BlazorFrontend.cs b/Core/Environment/BlazorFrontend.cs index 99b05fba4..9754158b5 100644 --- a/Core/Environment/BlazorFrontend.cs +++ b/Core/Environment/BlazorFrontend.cs @@ -1,6 +1,5 @@ -namespace Core.Environment +namespace Core.Environment; + +public sealed class BlazorFrontend : IEnvironment { - public sealed class BlazorFrontend : IEnvironment - { - } } diff --git a/Core/Environment/Headless.cs b/Core/Environment/Headless.cs index 972337eea..e74e7dc08 100644 --- a/Core/Environment/Headless.cs +++ b/Core/Environment/Headless.cs @@ -1,6 +1,5 @@ -namespace Core.Environment +namespace Core.Environment; + +public sealed class Headless : IEnvironment { - public sealed class Headless : IEnvironment - { - } } diff --git a/Core/Environment/IEnvironment.cs b/Core/Environment/IEnvironment.cs index cf2936f7c..ff947e110 100644 --- a/Core/Environment/IEnvironment.cs +++ b/Core/Environment/IEnvironment.cs @@ -1,6 +1,5 @@ -namespace Core.Environment +namespace Core.Environment; + +public interface IEnvironment { - public interface IEnvironment - { - } } diff --git a/Core/Equipments/EquipmentReader.cs b/Core/Equipments/EquipmentReader.cs index 235d16444..0bbd6b740 100644 --- a/Core/Equipments/EquipmentReader.cs +++ b/Core/Equipments/EquipmentReader.cs @@ -4,80 +4,79 @@ using SharedLib; -namespace Core +namespace Core; + +public sealed class EquipmentReader { - public sealed class EquipmentReader - { - private const int MAX_EQUIPMENT_COUNT = 24; + private const int MAX_EQUIPMENT_COUNT = 24; + + private readonly ItemDB itemDB; + private readonly int cItemId; + private readonly int cSlotNum; - private readonly ItemDB itemDB; - private readonly int cItemId; - private readonly int cSlotNum; + private readonly int[] equipmentIds = new int[MAX_EQUIPMENT_COUNT]; + public Item[] Items { get; private set; } = new Item[MAX_EQUIPMENT_COUNT]; - private readonly int[] equipmentIds = new int[MAX_EQUIPMENT_COUNT]; - public Item[] Items { get; private set; } = new Item[MAX_EQUIPMENT_COUNT]; + public event EventHandler<(int, int)>? OnEquipmentChanged; - public event EventHandler<(int, int)>? OnEquipmentChanged; + public EquipmentReader(ItemDB itemDB, int cSlotNum, int cItemId) + { + this.itemDB = itemDB; + this.cSlotNum = cSlotNum; + this.cItemId = cItemId; - public EquipmentReader(ItemDB itemDB, int cSlotNum, int cItemId) + for (int i = 0; i < MAX_EQUIPMENT_COUNT; i++) { - this.itemDB = itemDB; - this.cSlotNum = cSlotNum; - this.cItemId = cItemId; - - for (int i = 0; i < MAX_EQUIPMENT_COUNT; i++) - { - Items[i] = ItemDB.EmptyItem; - } + Items[i] = ItemDB.EmptyItem; } + } - public void Read(IAddonDataProvider reader) - { - int index = reader.GetInt(cSlotNum); - if (index >= MAX_EQUIPMENT_COUNT) - return; + public void Read(IAddonDataProvider reader) + { + int index = reader.GetInt(cSlotNum); + if (index >= MAX_EQUIPMENT_COUNT) + return; - int itemId = reader.GetInt(cItemId); - bool changed = equipmentIds[index] != itemId; + int itemId = reader.GetInt(cItemId); + bool changed = equipmentIds[index] != itemId; - if (!changed) - return; + if (!changed) + return; - equipmentIds[index] = itemId; + equipmentIds[index] = itemId; - Items[index] = itemId == 0 - ? ItemDB.EmptyItem - : itemDB.Items.TryGetValue(itemId, out Item item) - ? item - : new Item() { Entry = itemId, Name = "Unknown" }; + Items[index] = itemId == 0 + ? ItemDB.EmptyItem + : itemDB.Items.TryGetValue(itemId, out Item item) + ? item + : new Item() { Entry = itemId, Name = "Unknown" }; - OnEquipmentChanged?.Invoke(this, (index, itemId)); - } + OnEquipmentChanged?.Invoke(this, (index, itemId)); + } - public string ToStringList() - { - return string.Join(", ", equipmentIds); - } + public string ToStringList() + { + return string.Join(", ", equipmentIds); + } - public bool HasRanged() - { - return equipmentIds[(int)InventorySlotId.Ranged] != 0; - } + public bool HasRanged() + { + return equipmentIds[(int)InventorySlotId.Ranged] != 0; + } - public bool HasItem(int itemId) + public bool HasItem(int itemId) + { + for (int i = 0; i < equipmentIds.Length; i++) { - for (int i = 0; i < equipmentIds.Length; i++) - { - if (equipmentIds[i] == itemId) - return true; - } - - return false; + if (equipmentIds[i] == itemId) + return true; } - public int GetId(int slot) - { - return equipmentIds[slot]; - } + return false; + } + + public int GetId(int slot) + { + return equipmentIds[slot]; } } \ No newline at end of file diff --git a/Core/Equipments/InventorySlotId.cs b/Core/Equipments/InventorySlotId.cs index 2d92b93d2..31f50ec85 100644 --- a/Core/Equipments/InventorySlotId.cs +++ b/Core/Equipments/InventorySlotId.cs @@ -1,62 +1,61 @@ -namespace Core +namespace Core; + +public enum InventorySlotId { - public enum InventorySlotId - { - Ammo = 0, - Head = 1, - Neck = 2, - Shoulder = 3, - Shirt = 4, - Chest = 5, - Waist = 6, - Legs = 7, - Feet = 8, - Wrists = 9, - Hands = 10, - Finger_1 = 11, - Finger_2 = 12, - Trinket_1 = 13, - Trinket_2 = 14, - Back = 15, - Mainhand = 16, - Offhand = 17, - Ranged = 18, - Tabard = 19, - Bag_0 = 20, - Bag_1 = 21, - Bag_2 = 22, - Bag_3 = 23 - } + Ammo = 0, + Head = 1, + Neck = 2, + Shoulder = 3, + Shirt = 4, + Chest = 5, + Waist = 6, + Legs = 7, + Feet = 8, + Wrists = 9, + Hands = 10, + Finger_1 = 11, + Finger_2 = 12, + Trinket_1 = 13, + Trinket_2 = 14, + Back = 15, + Mainhand = 16, + Offhand = 17, + Ranged = 18, + Tabard = 19, + Bag_0 = 20, + Bag_1 = 21, + Bag_2 = 22, + Bag_3 = 23 +} - public static class InventorySlotId_Extension +public static class InventorySlotId_Extension +{ + public static string ToStringF(this InventorySlotId value) => value switch { - public static string ToStringF(this InventorySlotId value) => value switch - { - InventorySlotId.Ammo => nameof(InventorySlotId.Ammo), - InventorySlotId.Head => nameof(InventorySlotId.Head), - InventorySlotId.Neck => nameof(InventorySlotId.Neck), - InventorySlotId.Shoulder => nameof(InventorySlotId.Shoulder), - InventorySlotId.Shirt => nameof(InventorySlotId.Shirt), - InventorySlotId.Chest => nameof(InventorySlotId.Chest), - InventorySlotId.Waist => nameof(InventorySlotId.Waist), - InventorySlotId.Legs => nameof(InventorySlotId.Legs), - InventorySlotId.Feet => nameof(InventorySlotId.Feet), - InventorySlotId.Wrists => nameof(InventorySlotId.Wrists), - InventorySlotId.Hands => nameof(InventorySlotId.Hands), - InventorySlotId.Finger_1 => nameof(InventorySlotId.Finger_1), - InventorySlotId.Finger_2 => nameof(InventorySlotId.Finger_2), - InventorySlotId.Trinket_1 => nameof(InventorySlotId.Trinket_1), - InventorySlotId.Trinket_2 => nameof(InventorySlotId.Trinket_2), - InventorySlotId.Back => nameof(InventorySlotId.Back), - InventorySlotId.Mainhand => nameof(InventorySlotId.Mainhand), - InventorySlotId.Offhand => nameof(InventorySlotId.Offhand), - InventorySlotId.Ranged => nameof(InventorySlotId.Ranged), - InventorySlotId.Tabard => nameof(InventorySlotId.Tabard), - InventorySlotId.Bag_0 => nameof(InventorySlotId.Bag_0), - InventorySlotId.Bag_1 => nameof(InventorySlotId.Bag_1), - InventorySlotId.Bag_2 => nameof(InventorySlotId.Bag_2), - InventorySlotId.Bag_3 => nameof(InventorySlotId.Bag_3), - _ => throw new System.NotImplementedException(), - }; - } + InventorySlotId.Ammo => nameof(InventorySlotId.Ammo), + InventorySlotId.Head => nameof(InventorySlotId.Head), + InventorySlotId.Neck => nameof(InventorySlotId.Neck), + InventorySlotId.Shoulder => nameof(InventorySlotId.Shoulder), + InventorySlotId.Shirt => nameof(InventorySlotId.Shirt), + InventorySlotId.Chest => nameof(InventorySlotId.Chest), + InventorySlotId.Waist => nameof(InventorySlotId.Waist), + InventorySlotId.Legs => nameof(InventorySlotId.Legs), + InventorySlotId.Feet => nameof(InventorySlotId.Feet), + InventorySlotId.Wrists => nameof(InventorySlotId.Wrists), + InventorySlotId.Hands => nameof(InventorySlotId.Hands), + InventorySlotId.Finger_1 => nameof(InventorySlotId.Finger_1), + InventorySlotId.Finger_2 => nameof(InventorySlotId.Finger_2), + InventorySlotId.Trinket_1 => nameof(InventorySlotId.Trinket_1), + InventorySlotId.Trinket_2 => nameof(InventorySlotId.Trinket_2), + InventorySlotId.Back => nameof(InventorySlotId.Back), + InventorySlotId.Mainhand => nameof(InventorySlotId.Mainhand), + InventorySlotId.Offhand => nameof(InventorySlotId.Offhand), + InventorySlotId.Ranged => nameof(InventorySlotId.Ranged), + InventorySlotId.Tabard => nameof(InventorySlotId.Tabard), + InventorySlotId.Bag_0 => nameof(InventorySlotId.Bag_0), + InventorySlotId.Bag_1 => nameof(InventorySlotId.Bag_1), + InventorySlotId.Bag_2 => nameof(InventorySlotId.Bag_2), + InventorySlotId.Bag_3 => nameof(InventorySlotId.Bag_3), + _ => throw new System.NotImplementedException(), + }; } diff --git a/Core/ExecGameCommand/ExecGameCommand.cs b/Core/ExecGameCommand/ExecGameCommand.cs index 68f321826..5abb68af8 100644 --- a/Core/ExecGameCommand/ExecGameCommand.cs +++ b/Core/ExecGameCommand/ExecGameCommand.cs @@ -3,43 +3,42 @@ using System.Threading; using Microsoft.Extensions.Logging; -namespace Core +namespace Core; + +public sealed class ExecGameCommand { - public sealed class ExecGameCommand + private readonly ILogger logger; + private readonly WowProcessInput wowProcessInput; + private readonly CancellationToken ct; + + public ExecGameCommand(ILogger logger, CancellationTokenSource cts, WowProcessInput wowProcessInput) + { + this.logger = logger; + ct = cts.Token; + this.wowProcessInput = wowProcessInput; + } + + public void Run(string content) + { + wowProcessInput.SetForegroundWindow(); + logger.LogInformation(content); + + wowProcessInput.SetClipboard(content); + Wait(100, 250); + + // Open chat inputbox + wowProcessInput.KeyPress(ConsoleKey.Enter, Random.Shared.Next(50, 100)); + + wowProcessInput.PasteFromClipboard(); + Wait(100, 250); + + // Close chat inputbox + wowProcessInput.KeyPress(ConsoleKey.Enter, Random.Shared.Next(50, 100)); + Wait(100, 250); + } + + private void Wait(int min, int max) { - private readonly ILogger logger; - private readonly WowProcessInput wowProcessInput; - private readonly CancellationToken ct; - - public ExecGameCommand(ILogger logger, CancellationTokenSource cts, WowProcessInput wowProcessInput) - { - this.logger = logger; - ct = cts.Token; - this.wowProcessInput = wowProcessInput; - } - - public void Run(string content) - { - wowProcessInput.SetForegroundWindow(); - logger.LogInformation(content); - - wowProcessInput.SetClipboard(content); - Wait(100, 250); - - // Open chat inputbox - wowProcessInput.KeyPress(ConsoleKey.Enter, Random.Shared.Next(50, 100)); - - wowProcessInput.PasteFromClipboard(); - Wait(100, 250); - - // Close chat inputbox - wowProcessInput.KeyPress(ConsoleKey.Enter, Random.Shared.Next(50, 100)); - Wait(100, 250); - } - - private void Wait(int min, int max) - { - ct.WaitHandle.WaitOne(Random.Shared.Next(min, max)); - } + ct.WaitHandle.WaitOne(Random.Shared.Next(min, max)); } } diff --git a/Core/Extensions/RegexExtension.cs b/Core/Extensions/RegexExtension.cs index 05be9d922..c3f2d498b 100644 --- a/Core/Extensions/RegexExtension.cs +++ b/Core/Extensions/RegexExtension.cs @@ -1,23 +1,22 @@ using System.Text.RegularExpressions; -namespace Core.Extensions +namespace Core.Extensions; + +public static class RegexExtension { - public static class RegexExtension + public static string Replace(this string input, Regex regex, string groupName, string replacement) { - public static string Replace(this string input, Regex regex, string groupName, string replacement) + return regex.Replace(input, m => { - return regex.Replace(input, m => - { - return ReplaceNamedGroup(groupName, replacement, m); - }); - } + return ReplaceNamedGroup(groupName, replacement, m); + }); + } - private static string ReplaceNamedGroup(string groupName, string replacement, Match m) - { - string capture = m.Value; - capture = capture.Remove(m.Groups[groupName].Index - m.Index, m.Groups[groupName].Length); - capture = capture.Insert(m.Groups[groupName].Index - m.Index, replacement); - return capture; - } + private static string ReplaceNamedGroup(string groupName, string replacement, Match m) + { + string capture = m.Value; + capture = capture.Remove(m.Groups[groupName].Index - m.Index, m.Groups[groupName].Length); + capture = capture.Insert(m.Groups[groupName].Index - m.Index, replacement); + return capture; } } diff --git a/Core/GOAP/Events/AbortEvent.cs b/Core/GOAP/Events/AbortEvent.cs index c960e96f3..f7c974503 100644 --- a/Core/GOAP/Events/AbortEvent.cs +++ b/Core/GOAP/Events/AbortEvent.cs @@ -1,4 +1,3 @@ -namespace Core.GOAP -{ - public sealed class AbortEvent : GoapEventArgs { } -} +namespace Core.GOAP; + +public sealed class AbortEvent : GoapEventArgs { } diff --git a/Core/GOAP/Events/CorpseEvent.cs b/Core/GOAP/Events/CorpseEvent.cs index e8e869ec0..9ba700362 100644 --- a/Core/GOAP/Events/CorpseEvent.cs +++ b/Core/GOAP/Events/CorpseEvent.cs @@ -1,20 +1,19 @@ using System; using System.Numerics; -namespace Core.GOAP +namespace Core.GOAP; + +public sealed class CorpseEvent : GoapEventArgs { - public sealed class CorpseEvent : GoapEventArgs - { - public const string NAME = "Corpse"; - public const string COLOR = "black"; + public const string NAME = "Corpse"; + public const string COLOR = "black"; - public Vector3 MapLoc { get; } - public float Radius { get; } + public Vector3 MapLoc { get; } + public float Radius { get; } - public CorpseEvent(Vector3 location, float radius) - { - MapLoc = location; - Radius = MathF.Max(1, radius); - } + public CorpseEvent(Vector3 location, float radius) + { + MapLoc = location; + Radius = MathF.Max(1, radius); } } diff --git a/Core/GOAP/Events/GoapEventArgs.cs b/Core/GOAP/Events/GoapEventArgs.cs index ec06f452d..ce17c6623 100644 --- a/Core/GOAP/Events/GoapEventArgs.cs +++ b/Core/GOAP/Events/GoapEventArgs.cs @@ -1,6 +1,5 @@ using System; -namespace Core.GOAP -{ - public class GoapEventArgs : EventArgs { } -} +namespace Core.GOAP; + +public class GoapEventArgs : EventArgs { } diff --git a/Core/GOAP/Events/GoapStateEvent.cs b/Core/GOAP/Events/GoapStateEvent.cs index 9e2a13e3f..68f68a3d3 100644 --- a/Core/GOAP/Events/GoapStateEvent.cs +++ b/Core/GOAP/Events/GoapStateEvent.cs @@ -1,14 +1,13 @@ -namespace Core.GOAP +namespace Core.GOAP; + +public sealed class GoapStateEvent : GoapEventArgs { - public sealed class GoapStateEvent : GoapEventArgs - { - public GoapKey Key { get; } - public bool Value { get; } + public GoapKey Key { get; } + public bool Value { get; } - public GoapStateEvent(GoapKey key, bool value) - { - Key = key; - Value = value; - } + public GoapStateEvent(GoapKey key, bool value) + { + Key = key; + Value = value; } } diff --git a/Core/GOAP/Events/IGoapEventListener.cs b/Core/GOAP/Events/IGoapEventListener.cs index cb66bede4..4df780cb4 100644 --- a/Core/GOAP/Events/IGoapEventListener.cs +++ b/Core/GOAP/Events/IGoapEventListener.cs @@ -1,7 +1,6 @@ -namespace Core.GOAP +namespace Core.GOAP; + +public interface IGoapEventListener { - public interface IGoapEventListener - { - void OnGoapEvent(GoapEventArgs e); - } + void OnGoapEvent(GoapEventArgs e); } diff --git a/Core/GOAP/Events/RemoveClosestPoi.cs b/Core/GOAP/Events/RemoveClosestPoi.cs index b4e642e7c..b78a9bc67 100644 --- a/Core/GOAP/Events/RemoveClosestPoi.cs +++ b/Core/GOAP/Events/RemoveClosestPoi.cs @@ -1,12 +1,11 @@ -namespace Core.GOAP +namespace Core.GOAP; + +public sealed class RemoveClosestPoi : GoapEventArgs { - public sealed class RemoveClosestPoi : GoapEventArgs - { - public string Name { get; } + public string Name { get; } - public RemoveClosestPoi(string name) - { - Name = name; - } + public RemoveClosestPoi(string name) + { + Name = name; } } diff --git a/Core/GOAP/Events/ResumeEvent.cs b/Core/GOAP/Events/ResumeEvent.cs index 884074deb..5a268929f 100644 --- a/Core/GOAP/Events/ResumeEvent.cs +++ b/Core/GOAP/Events/ResumeEvent.cs @@ -1,4 +1,3 @@ -namespace Core.GOAP -{ - public sealed class ResumeEvent : GoapEventArgs { } -} +namespace Core.GOAP; + +public sealed class ResumeEvent : GoapEventArgs { } diff --git a/Core/GOAP/Events/SkinCorpseEvent.cs b/Core/GOAP/Events/SkinCorpseEvent.cs index dca1f0d44..23b3cc2b0 100644 --- a/Core/GOAP/Events/SkinCorpseEvent.cs +++ b/Core/GOAP/Events/SkinCorpseEvent.cs @@ -1,22 +1,21 @@ using System; using System.Numerics; -namespace Core.GOAP +namespace Core.GOAP; + +public sealed class SkinCorpseEvent : GoapEventArgs { - public sealed class SkinCorpseEvent : GoapEventArgs - { - public const string NAME = "Skin"; - public const string COLOR = "white"; + public const string NAME = "Skin"; + public const string COLOR = "white"; - public Vector3 MapLoc { get; } - public float Radius { get; } - public int NpcId { get; } + public Vector3 MapLoc { get; } + public float Radius { get; } + public int NpcId { get; } - public SkinCorpseEvent(Vector3 location, float radius, int npcId) - { - MapLoc = location; - Radius = MathF.Max(1, radius); - NpcId = npcId; - } + public SkinCorpseEvent(Vector3 location, float radius, int npcId) + { + MapLoc = location; + Radius = MathF.Max(1, radius); + NpcId = npcId; } } diff --git a/Core/GOAP/GoapAgent.cs b/Core/GOAP/GoapAgent.cs index ec69bb55b..64f43fd5a 100644 --- a/Core/GOAP/GoapAgent.cs +++ b/Core/GOAP/GoapAgent.cs @@ -11,346 +11,345 @@ using Microsoft.Extensions.DependencyInjection; using System.Buffers; -namespace Core.GOAP +namespace Core.GOAP; + +public sealed partial class GoapAgent : IDisposable { - public sealed partial class GoapAgent : IDisposable - { - private readonly IServiceScope scope; + private readonly IServiceScope scope; - private readonly ILogger logger; - private readonly ClassConfiguration classConfig; - private readonly AddonReader addonReader; - private readonly PlayerReader playerReader; - private readonly IWowScreen wowScreen; - private readonly RouteInfo routeInfo; - private readonly ConfigurableInput input; + private readonly ILogger logger; + private readonly ClassConfiguration classConfig; + private readonly AddonReader addonReader; + private readonly PlayerReader playerReader; + private readonly IWowScreen wowScreen; + private readonly RouteInfo routeInfo; + private readonly ConfigurableInput input; - private readonly IGrindSessionHandler sessionHandler; - private readonly StopMoving stopMoving; + private readonly IGrindSessionHandler sessionHandler; + private readonly StopMoving stopMoving; - private readonly Thread goapThread; - private readonly CancellationTokenSource cts; - private readonly ManualResetEvent manualReset; + private readonly Thread goapThread; + private readonly CancellationTokenSource cts; + private readonly ManualResetEvent manualReset; - private bool active; - public bool Active + private bool active; + public bool Active + { + get => active; + set { - get => active; - set + active = value; + if (!active) { - active = value; - if (!active) - { - manualReset.Reset(); + manualReset.Reset(); - foreach (IGoapEventListener goal in AvailableGoals.OfType()) - { - goal.OnGoapEvent(new AbortEvent()); - } + foreach (IGoapEventListener goal in AvailableGoals.OfType()) + { + goal.OnGoapEvent(new AbortEvent()); + } - input.Proc.Reset(); - stopMoving.Stop(); + input.Proc.Reset(); + stopMoving.Stop(); - if (classConfig.Mode is Mode.AttendedGrind or Mode.Grind) - { - sessionHandler.Stop("Stopped", false); - } + if (classConfig.Mode is Mode.AttendedGrind or Mode.Grind) + { + sessionHandler.Stop("Stopped", false); + } - addonReader.SessionReset(); + addonReader.SessionReset(); - wowScreen.Enabled = false; - } - else + wowScreen.Enabled = false; + } + else + { + if (CurrentGoal is IGoapEventListener listener) { - if (CurrentGoal is IGoapEventListener listener) - { - listener.OnGoapEvent(new ResumeEvent()); - } + listener.OnGoapEvent(new ResumeEvent()); + } - manualReset.Set(); + manualReset.Set(); - if (classConfig.Mode is Mode.AttendedGrind or Mode.Grind) - { - sessionHandler.Start(classConfig.OverridePathFilename ?? classConfig.PathFilename); - } + if (classConfig.Mode is Mode.AttendedGrind or Mode.Grind) + { + sessionHandler.Start(classConfig.OverridePathFilename ?? classConfig.PathFilename); } } } + } - public GoapAgentState State { get; } - public IEnumerable AvailableGoals { get; } + public GoapAgentState State { get; } + public IEnumerable AvailableGoals { get; } - public Stack Plan { get; private set; } - public GoapGoal? CurrentGoal { get; private set; } + public Stack Plan { get; private set; } + public GoapGoal? CurrentGoal { get; private set; } - public GoapAgent(IServiceScope scope, DataConfig dataConfig, - IGrindSessionDAO sessionDAO, IWowScreen wowScreen, RouteInfo routeInfo) - { - this.scope = scope; + public GoapAgent(IServiceScope scope, DataConfig dataConfig, + IGrindSessionDAO sessionDAO, IWowScreen wowScreen, RouteInfo routeInfo) + { + this.scope = scope; - this.logger = scope.ServiceProvider.GetRequiredService(); - this.classConfig = scope.ServiceProvider.GetRequiredService(); - this.cts = new(); - this.wowScreen = wowScreen; - this.State = scope.ServiceProvider.GetRequiredService(); - this.addonReader = scope.ServiceProvider.GetRequiredService(); - this.playerReader = addonReader.PlayerReader; - this.routeInfo = routeInfo; - this.input = scope.ServiceProvider.GetRequiredService(); + this.logger = scope.ServiceProvider.GetRequiredService(); + this.classConfig = scope.ServiceProvider.GetRequiredService(); + this.cts = new(); + this.wowScreen = wowScreen; + this.State = scope.ServiceProvider.GetRequiredService(); + this.addonReader = scope.ServiceProvider.GetRequiredService(); + this.playerReader = addonReader.PlayerReader; + this.routeInfo = routeInfo; + this.input = scope.ServiceProvider.GetRequiredService(); - this.addonReader.CombatLog.KillCredit += OnKillCredit; + this.addonReader.CombatLog.KillCredit += OnKillCredit; - sessionHandler = new GrindSessionHandler(logger, dataConfig, addonReader, sessionDAO, cts); - stopMoving = new StopMoving(input.Proc, playerReader, cts); + sessionHandler = new GrindSessionHandler(logger, dataConfig, addonReader, sessionDAO, cts); + stopMoving = new StopMoving(input.Proc, playerReader, cts); - this.AvailableGoals = scope.ServiceProvider.GetServices().OrderBy(a => a.Cost); - this.Plan = new(); + this.AvailableGoals = scope.ServiceProvider.GetServices().OrderBy(a => a.Cost); + this.Plan = new(); - foreach (GoapGoal a in AvailableGoals) - { - a.GoapEvent += HandleGoapEvent; + foreach (GoapGoal a in AvailableGoals) + { + a.GoapEvent += HandleGoapEvent; - foreach (IGoapEventListener b in AvailableGoals.OfType()) - { - if (b != a) - a.GoapEvent += b.OnGoapEvent; - } + foreach (IGoapEventListener b in AvailableGoals.OfType()) + { + if (b != a) + a.GoapEvent += b.OnGoapEvent; } - - manualReset = new(false); - goapThread = new(GoapThread); - goapThread.Start(); } - public void Dispose() + manualReset = new(false); + goapThread = new(GoapThread); + goapThread.Start(); + } + + public void Dispose() + { + cts.Cancel(); + manualReset.Set(); + + foreach (GoapGoal a in AvailableGoals) { - cts.Cancel(); - manualReset.Set(); + a.GoapEvent -= HandleGoapEvent; - foreach (GoapGoal a in AvailableGoals) + foreach (IGoapEventListener b in AvailableGoals.OfType()) { - a.GoapEvent -= HandleGoapEvent; - - foreach (IGoapEventListener b in AvailableGoals.OfType()) - { - if (b != a) - a.GoapEvent -= b.OnGoapEvent; - } + if (b != a) + a.GoapEvent -= b.OnGoapEvent; } + } - scope.Dispose(); + scope.Dispose(); - addonReader.CombatLog.KillCredit -= OnKillCredit; - } + addonReader.CombatLog.KillCredit -= OnKillCredit; + } + + private void GoapThread() + { + bool wasEmpty = false; - private void GoapThread() + while (!cts.IsCancellationRequested) { - bool wasEmpty = false; + manualReset.WaitOne(); - while (!cts.IsCancellationRequested) + GoapGoal? newGoal = NextGoal(); + if (!cts.IsCancellationRequested && newGoal != null) { - manualReset.WaitOne(); - - GoapGoal? newGoal = NextGoal(); - if (!cts.IsCancellationRequested && newGoal != null) + if (newGoal != CurrentGoal) { - if (newGoal != CurrentGoal) - { - wasEmpty = false; - CurrentGoal?.OnExit(); - CurrentGoal = newGoal; - - LogNewGoal(logger, newGoal.Name); - CurrentGoal.OnEnter(); - } + wasEmpty = false; + CurrentGoal?.OnExit(); + CurrentGoal = newGoal; - newGoal.Update(); - } - else if (!cts.IsCancellationRequested && !wasEmpty) - { - LogNewEmptyGoal(logger); - wasEmpty = true; + LogNewGoal(logger, newGoal.Name); + CurrentGoal.OnEnter(); } - cts.Token.WaitHandle.WaitOne(1); + newGoal.Update(); } - - if (logger.IsEnabled(LogLevel.Debug)) - logger.LogDebug("Goap thread stopped!"); - } - - private GoapGoal? NextGoal() - { - if (Plan.Count == 0) + else if (!cts.IsCancellationRequested && !wasEmpty) { - Plan = GoapPlanner.Plan(AvailableGoals, GetWorldState(), GoapPlanner.EmptyGoalState); + LogNewEmptyGoal(logger); + wasEmpty = true; } - return Plan.Count > 0 ? Plan.Pop() : null; + cts.Token.WaitHandle.WaitOne(1); } - private bool[] GetWorldState() + if (logger.IsEnabled(LogLevel.Debug)) + logger.LogDebug("Goap thread stopped!"); + } + + private GoapGoal? NextGoal() + { + if (Plan.Count == 0) { - var pooler = ArrayPool.Shared; - bool[] a = pooler.Rent((int)GoapKey.LENGTH); + Plan = GoapPlanner.Plan(AvailableGoals, GetWorldState(), GoapPlanner.EmptyGoalState); + } + + return Plan.Count > 0 ? Plan.Pop() : null; + } - a[(int)GoapKey.hastarget] = playerReader.Bits.HasTarget(); - a[(int)GoapKey.dangercombat] = playerReader.Bits.PlayerInCombat() && addonReader.DamageTakenCount() > 0; + private bool[] GetWorldState() + { + var pooler = ArrayPool.Shared; + bool[] a = pooler.Rent((int)GoapKey.LENGTH); - a[(int)GoapKey.damagetaken] = addonReader.DamageTakenCount() > 0; - a[(int)GoapKey.damagedone] = addonReader.DamageDoneCount() > 0; - a[(int)GoapKey.damagetakenordone] = addonReader.DamageTakenCount() > 0 || addonReader.DamageDoneCount() > 0; + a[(int)GoapKey.hastarget] = playerReader.Bits.HasTarget(); + a[(int)GoapKey.dangercombat] = playerReader.Bits.PlayerInCombat() && addonReader.DamageTakenCount() > 0; - a[(int)GoapKey.targetisalive] = playerReader.Bits.HasTarget() && !playerReader.Bits.TargetIsDead(); - a[(int)GoapKey.targettargetsus] = (playerReader.Bits.HasTarget() && playerReader.TargetHealthPercentage() < 30) || playerReader.TargetTarget is - UnitsTarget.Me or - UnitsTarget.Pet or - UnitsTarget.PartyOrPet; + a[(int)GoapKey.damagetaken] = addonReader.DamageTakenCount() > 0; + a[(int)GoapKey.damagedone] = addonReader.DamageDoneCount() > 0; + a[(int)GoapKey.damagetakenordone] = addonReader.DamageTakenCount() > 0 || addonReader.DamageDoneCount() > 0; - a[(int)GoapKey.incombat] = playerReader.Bits.PlayerInCombat(); - a[(int)GoapKey.pethastarget] = playerReader.PetHasTarget() && !playerReader.Bits.PetTargetIsDead(); - a[(int)GoapKey.ismounted] = (playerReader.Class == UnitClass.Druid && - playerReader.Form is Form.Druid_Travel or Form.Druid_Flight) - || playerReader.Bits.IsMounted(); + a[(int)GoapKey.targetisalive] = playerReader.Bits.HasTarget() && !playerReader.Bits.TargetIsDead(); + a[(int)GoapKey.targettargetsus] = (playerReader.Bits.HasTarget() && playerReader.TargetHealthPercentage() < 30) || playerReader.TargetTarget is + UnitsTarget.Me or + UnitsTarget.Pet or + UnitsTarget.PartyOrPet; - a[(int)GoapKey.withinpullrange] = playerReader.WithInPullRange(); - a[(int)GoapKey.incombatrange] = playerReader.WithInCombatRange(); + a[(int)GoapKey.incombat] = playerReader.Bits.PlayerInCombat(); + a[(int)GoapKey.pethastarget] = playerReader.PetHasTarget() && !playerReader.Bits.PetTargetIsDead(); + a[(int)GoapKey.ismounted] = (playerReader.Class == UnitClass.Druid && + playerReader.Form is Form.Druid_Travel or Form.Druid_Flight) + || playerReader.Bits.IsMounted(); - a[(int)GoapKey.pulled] = false; - a[(int)GoapKey.isdead] = playerReader.Bits.IsDead(); + a[(int)GoapKey.withinpullrange] = playerReader.WithInPullRange(); + a[(int)GoapKey.incombatrange] = playerReader.WithInCombatRange(); - a[(int)GoapKey.shouldloot] = State.LootableCorpseCount > 0; - a[(int)GoapKey.shouldgather] = State.GatherableCorpseCount > 0; - a[(int)GoapKey.producedcorpse] = State.LastCombatKillCount > 0; - a[(int)GoapKey.consumecorpse] = State.ShouldConsumeCorpse; + a[(int)GoapKey.pulled] = false; + a[(int)GoapKey.isdead] = playerReader.Bits.IsDead(); - a[(int)GoapKey.isswimming] = playerReader.Bits.IsSwimming(); - a[(int)GoapKey.itemsbroken] = playerReader.Bits.ItemsAreBroken(); + a[(int)GoapKey.shouldloot] = State.LootableCorpseCount > 0; + a[(int)GoapKey.shouldgather] = State.GatherableCorpseCount > 0; + a[(int)GoapKey.producedcorpse] = State.LastCombatKillCount > 0; + a[(int)GoapKey.consumecorpse] = State.ShouldConsumeCorpse; - a[(int)GoapKey.gathering] = State.Gathering; + a[(int)GoapKey.isswimming] = playerReader.Bits.IsSwimming(); + a[(int)GoapKey.itemsbroken] = playerReader.Bits.ItemsAreBroken(); - a[(int)GoapKey.targethostile] = playerReader.Bits.TargetCanBeHostile(); - a[(int)GoapKey.hasfocus] = playerReader.Bits.HasFocus(); - a[(int)GoapKey.focushastarget] = playerReader.Bits.FocusHasTarget(); + a[(int)GoapKey.gathering] = State.Gathering; - a[(int)GoapKey.consumablecorpsenearby] = State.ConsumableCorpseCount > 0; + a[(int)GoapKey.targethostile] = playerReader.Bits.TargetCanBeHostile(); + a[(int)GoapKey.hasfocus] = playerReader.Bits.HasFocus(); + a[(int)GoapKey.focushastarget] = playerReader.Bits.FocusHasTarget(); - pooler.Return(a); - return a; - } + a[(int)GoapKey.consumablecorpsenearby] = State.ConsumableCorpseCount > 0; + + pooler.Return(a); + return a; + } - private void HandleGoapEvent(GoapEventArgs e) + private void HandleGoapEvent(GoapEventArgs e) + { + if (e is GoapStateEvent g) { - if (e is GoapStateEvent g) - { - switch (g.Key) - { - case GoapKey.consumecorpse: - State.ShouldConsumeCorpse = g.Value; - break; - case GoapKey.gathering: - State.Gathering = g.Value; - break; - } - } - else if (e is CorpseEvent c) + switch (g.Key) { - routeInfo.PoiList.Add(new RouteInfoPoi(c.MapLoc, CorpseEvent.NAME, CorpseEvent.COLOR, c.Radius)); - } - else if (e is SkinCorpseEvent s) - { - routeInfo.PoiList.Add(new RouteInfoPoi(s.MapLoc, SkinCorpseEvent.NAME, SkinCorpseEvent.COLOR, s.Radius)); - } - else if (e is RemoveClosestPoi r) - { - RemoveClosestPoiByType(r.Name); + case GoapKey.consumecorpse: + State.ShouldConsumeCorpse = g.Value; + break; + case GoapKey.gathering: + State.Gathering = g.Value; + break; } } + else if (e is CorpseEvent c) + { + routeInfo.PoiList.Add(new RouteInfoPoi(c.MapLoc, CorpseEvent.NAME, CorpseEvent.COLOR, c.Radius)); + } + else if (e is SkinCorpseEvent s) + { + routeInfo.PoiList.Add(new RouteInfoPoi(s.MapLoc, SkinCorpseEvent.NAME, SkinCorpseEvent.COLOR, s.Radius)); + } + else if (e is RemoveClosestPoi r) + { + RemoveClosestPoiByType(r.Name); + } + } - private void OnKillCredit() + private void OnKillCredit() + { + if (Active) { - if (Active) - { - State.LastCombatKillCount++; - State.ConsumableCorpseCount++; - BroadcastGoapEvent(GoapKey.producedcorpse, true); + State.LastCombatKillCount++; + State.ConsumableCorpseCount++; + BroadcastGoapEvent(GoapKey.producedcorpse, true); - LogActiveKillDetected(logger, State.LastCombatKillCount, addonReader.DamageTakenCount()); - } - else - { - LogInactiveKillDetected(logger); - } + LogActiveKillDetected(logger, State.LastCombatKillCount, addonReader.DamageTakenCount()); } - - private void BroadcastGoapEvent(GoapKey goapKey, bool value) + else { - foreach (IGoapEventListener goal in AvailableGoals.OfType()) - { - goal.OnGoapEvent(new GoapStateEvent(goapKey, value)); - } + LogInactiveKillDetected(logger); } + } - private void RemoveClosestPoiByType(string type) + private void BroadcastGoapEvent(GoapKey goapKey, bool value) + { + foreach (IGoapEventListener goal in AvailableGoals.OfType()) { - if (routeInfo.PoiList.Count == 0) - return; + goal.OnGoapEvent(new GoapStateEvent(goapKey, value)); + } + } - int index = -1; - float minDistance = float.MaxValue; - Vector3 playerMap = addonReader.PlayerReader.MapPos; - for (int i = 0; i < routeInfo.PoiList.Count; i++) - { - RouteInfoPoi poi = routeInfo.PoiList[i]; - if (poi.Name != type) - continue; + private void RemoveClosestPoiByType(string type) + { + if (routeInfo.PoiList.Count == 0) + return; - float mapMin = playerMap.MapDistanceXYTo(poi.MapLoc); - if (mapMin < minDistance) - { - minDistance = mapMin; - index = i; - } - } + int index = -1; + float minDistance = float.MaxValue; + Vector3 playerMap = addonReader.PlayerReader.MapPos; + for (int i = 0; i < routeInfo.PoiList.Count; i++) + { + RouteInfoPoi poi = routeInfo.PoiList[i]; + if (poi.Name != type) + continue; - if (index > -1) + float mapMin = playerMap.MapDistanceXYTo(poi.MapLoc); + if (mapMin < minDistance) { - routeInfo.PoiList.RemoveAt(index); + minDistance = mapMin; + index = i; } } - public void NodeFound() + if (index > -1) { - State.Gathering = true; - BroadcastGoapEvent(GoapKey.gathering, true); + routeInfo.PoiList.RemoveAt(index); } + } - #region Logging - - [LoggerMessage( - EventId = 50, - Level = LogLevel.Information, - Message = "Kill credit detected! Known kills: {count} | Fighting with: {remain}")] - static partial void LogActiveKillDetected(ILogger logger, int count, int remain); - - [LoggerMessage( - EventId = 51, - Level = LogLevel.Information, - Message = "Inactive, kill credit detected!")] - static partial void LogInactiveKillDetected(ILogger logger); - - [LoggerMessage( - EventId = 52, - Level = LogLevel.Information, - Message = "New Plan= {name}")] - static partial void LogNewGoal(ILogger logger, string name); - - [LoggerMessage( - EventId = 53, - Level = LogLevel.Warning, - Message = "New Plan= NO PLAN")] - static partial void LogNewEmptyGoal(ILogger logger); - - #endregion + public void NodeFound() + { + State.Gathering = true; + BroadcastGoapEvent(GoapKey.gathering, true); } + + #region Logging + + [LoggerMessage( + EventId = 50, + Level = LogLevel.Information, + Message = "Kill credit detected! Known kills: {count} | Fighting with: {remain}")] + static partial void LogActiveKillDetected(ILogger logger, int count, int remain); + + [LoggerMessage( + EventId = 51, + Level = LogLevel.Information, + Message = "Inactive, kill credit detected!")] + static partial void LogInactiveKillDetected(ILogger logger); + + [LoggerMessage( + EventId = 52, + Level = LogLevel.Information, + Message = "New Plan= {name}")] + static partial void LogNewGoal(ILogger logger, string name); + + [LoggerMessage( + EventId = 53, + Level = LogLevel.Warning, + Message = "New Plan= NO PLAN")] + static partial void LogNewEmptyGoal(ILogger logger); + + #endregion } \ No newline at end of file diff --git a/Core/GOAP/GoapAgentState.cs b/Core/GOAP/GoapAgentState.cs index 1e1e27cce..386f5d59e 100644 --- a/Core/GOAP/GoapAgentState.cs +++ b/Core/GOAP/GoapAgentState.cs @@ -1,13 +1,12 @@  -namespace Core.GOAP +namespace Core.GOAP; + +public sealed class GoapAgentState { - public sealed class GoapAgentState - { - public bool ShouldConsumeCorpse { get; set; } - public int LootableCorpseCount { get; set; } - public int GatherableCorpseCount { get; set; } - public int ConsumableCorpseCount { get; set; } - public int LastCombatKillCount { get; set; } - public bool Gathering { get; set; } - } + public bool ShouldConsumeCorpse { get; set; } + public int LootableCorpseCount { get; set; } + public int GatherableCorpseCount { get; set; } + public int ConsumableCorpseCount { get; set; } + public int LastCombatKillCount { get; set; } + public bool Gathering { get; set; } } diff --git a/Core/GOAP/GoapKey.cs b/Core/GOAP/GoapKey.cs index dc988bee7..e03987dab 100644 --- a/Core/GOAP/GoapKey.cs +++ b/Core/GOAP/GoapKey.cs @@ -1,102 +1,101 @@ -namespace Core.GOAP +namespace Core.GOAP; + +public enum GoapKey { - public enum GoapKey - { - hastarget, - dangercombat, - damagetaken, - damagedone, - damagetakenordone, - targetisalive, - targettargetsus, - incombat, - pethastarget, - ismounted, - withinpullrange, - incombatrange, - pulled, - isdead, - shouldloot, - shouldgather, - producedcorpse, - consumecorpse, - isswimming, - itemsbroken, - gathering, - targethostile, - hasfocus, - focushastarget, - consumablecorpsenearby, - LENGTH - } + hastarget, + dangercombat, + damagetaken, + damagedone, + damagetakenordone, + targetisalive, + targettargetsus, + incombat, + pethastarget, + ismounted, + withinpullrange, + incombatrange, + pulled, + isdead, + shouldloot, + shouldgather, + producedcorpse, + consumecorpse, + isswimming, + itemsbroken, + gathering, + targethostile, + hasfocus, + focushastarget, + consumablecorpsenearby, + LENGTH +} - public static class GoapKey_Extension - { - private const string unknown = "Unknown"; +public static class GoapKey_Extension +{ + private const string unknown = "Unknown"; - private static string ToStringTrue(GoapKey value) => value switch - { - GoapKey.hastarget => "Target", - GoapKey.dangercombat => "Danger", - GoapKey.damagetaken => "Damage Taken", - GoapKey.damagedone => "Damage Done", - GoapKey.targetisalive => "Target alive", - GoapKey.targettargetsus => "Targets us", - GoapKey.incombat => "Combat", - GoapKey.pethastarget => "Pet target", - GoapKey.ismounted => "Mounted", - GoapKey.withinpullrange => "Pull range", - GoapKey.incombatrange => "Combat range", - GoapKey.pulled => "Pulled", - GoapKey.isdead => "Dead", - GoapKey.shouldloot => "Loot", - GoapKey.shouldgather => "Gather", - GoapKey.producedcorpse => "Killing blow", - GoapKey.consumecorpse => "Consume Corpse", - GoapKey.isswimming => "Swimming", - GoapKey.itemsbroken => "Broken", - GoapKey.gathering => "Gathering", - GoapKey.hasfocus => "Focus", - GoapKey.focushastarget => "Focus Target", - GoapKey.targethostile => "Target Hostile", - GoapKey.damagetakenordone => "Damage Taken or Done", - GoapKey.consumablecorpsenearby => "Consume Corpse nearby", - _ => unknown - }; + private static string ToStringTrue(GoapKey value) => value switch + { + GoapKey.hastarget => "Target", + GoapKey.dangercombat => "Danger", + GoapKey.damagetaken => "Damage Taken", + GoapKey.damagedone => "Damage Done", + GoapKey.targetisalive => "Target alive", + GoapKey.targettargetsus => "Targets us", + GoapKey.incombat => "Combat", + GoapKey.pethastarget => "Pet target", + GoapKey.ismounted => "Mounted", + GoapKey.withinpullrange => "Pull range", + GoapKey.incombatrange => "Combat range", + GoapKey.pulled => "Pulled", + GoapKey.isdead => "Dead", + GoapKey.shouldloot => "Loot", + GoapKey.shouldgather => "Gather", + GoapKey.producedcorpse => "Killing blow", + GoapKey.consumecorpse => "Consume Corpse", + GoapKey.isswimming => "Swimming", + GoapKey.itemsbroken => "Broken", + GoapKey.gathering => "Gathering", + GoapKey.hasfocus => "Focus", + GoapKey.focushastarget => "Focus Target", + GoapKey.targethostile => "Target Hostile", + GoapKey.damagetakenordone => "Damage Taken or Done", + GoapKey.consumablecorpsenearby => "Consume Corpse nearby", + _ => unknown + }; - private static string ToStringFalse(GoapKey value) => value switch - { - GoapKey.hastarget => "!Target", - GoapKey.dangercombat => "!Danger", - GoapKey.damagetaken => "!Damage Taken", - GoapKey.damagedone => "!Damage Done", - GoapKey.targetisalive => "!Target alive", - GoapKey.targettargetsus => "!Targets us", - GoapKey.incombat => "!Combat", - GoapKey.pethastarget => "!Pet target", - GoapKey.ismounted => "!Mounted", - GoapKey.withinpullrange => "!Pull range", - GoapKey.incombatrange => "!Combat range", - GoapKey.pulled => "!Pulled", - GoapKey.isdead => "!Dead", - GoapKey.shouldloot => "!Loot", - GoapKey.shouldgather => "!Gather", - GoapKey.producedcorpse => "!Killing blow", - GoapKey.consumecorpse => "!Consume Corpse", - GoapKey.isswimming => "!Swimming", - GoapKey.itemsbroken => "!Broken", - GoapKey.gathering => "!Gathering", - GoapKey.hasfocus => "!Focus", - GoapKey.focushastarget => "!Focus Target", - GoapKey.targethostile => "!Target Hostile", - GoapKey.damagetakenordone => "!Damage Taken or Done", - GoapKey.consumablecorpsenearby => "!Consume Corpse nearby", - _ => unknown - }; + private static string ToStringFalse(GoapKey value) => value switch + { + GoapKey.hastarget => "!Target", + GoapKey.dangercombat => "!Danger", + GoapKey.damagetaken => "!Damage Taken", + GoapKey.damagedone => "!Damage Done", + GoapKey.targetisalive => "!Target alive", + GoapKey.targettargetsus => "!Targets us", + GoapKey.incombat => "!Combat", + GoapKey.pethastarget => "!Pet target", + GoapKey.ismounted => "!Mounted", + GoapKey.withinpullrange => "!Pull range", + GoapKey.incombatrange => "!Combat range", + GoapKey.pulled => "!Pulled", + GoapKey.isdead => "!Dead", + GoapKey.shouldloot => "!Loot", + GoapKey.shouldgather => "!Gather", + GoapKey.producedcorpse => "!Killing blow", + GoapKey.consumecorpse => "!Consume Corpse", + GoapKey.isswimming => "!Swimming", + GoapKey.itemsbroken => "!Broken", + GoapKey.gathering => "!Gathering", + GoapKey.hasfocus => "!Focus", + GoapKey.focushastarget => "!Focus Target", + GoapKey.targethostile => "!Target Hostile", + GoapKey.damagetakenordone => "!Damage Taken or Done", + GoapKey.consumablecorpsenearby => "!Consume Corpse nearby", + _ => unknown + }; - public static string ToStringF(this GoapKey key, bool state) - { - return state ? ToStringTrue(key) : ToStringFalse(key); - } + public static string ToStringF(this GoapKey key, bool state) + { + return state ? ToStringTrue(key) : ToStringFalse(key); } } \ No newline at end of file diff --git a/Core/GOAP/GoapPlanner.cs b/Core/GOAP/GoapPlanner.cs index ff70aff3d..c8d8924f2 100644 --- a/Core/GOAP/GoapPlanner.cs +++ b/Core/GOAP/GoapPlanner.cs @@ -4,192 +4,191 @@ using System; using System.Buffers; -namespace Core.GOAP -{ - /** +namespace Core.GOAP; + +/** * Plans what actions can be completed in order to fulfill a goal state. */ - public static class GoapPlanner - { - public static readonly bool[] EmptyGoalState = Array.Empty(); - public static readonly Stack EmptyGoal = new(); +public static class GoapPlanner +{ + public static readonly bool[] EmptyGoalState = Array.Empty(); + public static readonly Stack EmptyGoal = new(); - /** + /** * Plan what sequence of actions can fulfill the goal. * Returns null if a plan could not be found, or a list of the actions * that must be performed, in order, to fulfill the goal. */ - public static Stack Plan( - IEnumerable availableActions, - bool[] worldState, - bool[] goal) - { - Node root = new(null, 0, worldState, null); + public static Stack Plan( + IEnumerable availableActions, + bool[] worldState, + bool[] goal) + { + Node root = new(null, 0, worldState, null); - // check what actions can run using their checkProceduralPrecondition - HashSet usableActions = new(); - foreach (GoapGoal a in availableActions) + // check what actions can run using their checkProceduralPrecondition + HashSet usableActions = new(); + foreach (GoapGoal a in availableActions) + { + if (a.CanRun()) { - if (a.CanRun()) - { - usableActions.Add(a); - } - else - { - a.SetState(InState(a.Preconditions, root.state)); - } + usableActions.Add(a); } - - // build up the tree and record the leaf nodes that provide a solution to the goal. - List leaves = new(); - if (!BuildGraph(root, leaves, usableActions, goal)) + else { - return EmptyGoal; + a.SetState(InState(a.Preconditions, root.state)); } + } - // get the cheapest leaf - Stack result = new(); - static float Min(Node n) => n.runningCost; - Node? node = leaves.MinBy(Min); - while (node != null) - { - if (node.action != null) - { - result.Push(node.action); - } - node = node.parent; - } - return result; + // build up the tree and record the leaf nodes that provide a solution to the goal. + List leaves = new(); + if (!BuildGraph(root, leaves, usableActions, goal)) + { + return EmptyGoal; } - private static bool ContainsValue(in PartialState[] state, bool value) + // get the cheapest leaf + Stack result = new(); + static float Min(Node n) => n.runningCost; + Node? node = leaves.MinBy(Min); + while (node != null) { - for (int i = 0; i < state.Length; i++) + if (node.action != null) { - if (state[i].Value == value) - return true; + result.Push(node.action); } + node = node.parent; + } + return result; + } - return false; + private static bool ContainsValue(in PartialState[] state, bool value) + { + for (int i = 0; i < state.Length; i++) + { + if (state[i].Value == value) + return true; } - /** + return false; + } + + /** * Returns true if at least one solution was found. * The possible paths are stored in the leaves list. Each leaf has a * 'runningCost' value where the lowest cost will be the best action * sequence. */ - private static bool BuildGraph(Node parent, List leaves, HashSet usableActions, bool[] goal) + private static bool BuildGraph(Node parent, List leaves, HashSet usableActions, bool[] goal) + { + bool foundOne = false; + + // go through each action available at this node and see if we can use it here + foreach (GoapGoal action in usableActions) { - bool foundOne = false; + // if the parent state has the conditions for this action's preconditions, we can use it here + PartialState[] result = InState(action.Preconditions, parent.state); + action.SetState(result); - // go through each action available at this node and see if we can use it here - foreach (GoapGoal action in usableActions) + if (!ContainsValue(result, false)) { - // if the parent state has the conditions for this action's preconditions, we can use it here - PartialState[] result = InState(action.Preconditions, parent.state); - action.SetState(result); + // apply the action's effects to the parent state + bool[] currentState = PopulateState(parent.state, action.Effects); + //Debug.Log(GoapAgent.prettyPrint(currentState)); + Node node = new(parent, parent.runningCost + action.Cost, currentState, action); + result = InState(goal, currentState); if (!ContainsValue(result, false)) { - // apply the action's effects to the parent state - bool[] currentState = PopulateState(parent.state, action.Effects); - //Debug.Log(GoapAgent.prettyPrint(currentState)); - Node node = new(parent, parent.runningCost + action.Cost, currentState, action); + // we found a solution! + leaves.Add(node); + foundOne = true; + } + else + { + // not at a solution yet, so test all the remaining actions and branch out the tree + HashSet subset = new(usableActions); + subset.Remove(action); - result = InState(goal, currentState); - if (!ContainsValue(result, false)) + bool found = BuildGraph(node, leaves, subset, goal); + if (found) { - // we found a solution! - leaves.Add(node); foundOne = true; } - else - { - // not at a solution yet, so test all the remaining actions and branch out the tree - HashSet subset = new(usableActions); - subset.Remove(action); - - bool found = BuildGraph(node, leaves, subset, goal); - if (found) - { - foundOne = true; - } - } } } - - return foundOne; } - /** + return foundOne; + } + + /** * Check that all items in 'test' are in 'state'. If just one does not match or is not there * then this returns false. */ - private static PartialState[] InState(Dictionary test, bool[] state) + private static PartialState[] InState(Dictionary test, bool[] state) + { + PartialState[] resultState = new PartialState[test.Count]; + int i = 0; + foreach ((GoapKey key, bool value) in test) { - PartialState[] resultState = new PartialState[test.Count]; - int i = 0; - foreach ((GoapKey key, bool value) in test) - { - bool exists = (int)key < state.Length; - resultState[i++] = new(key, exists && test[key].Equals(state[(int)key])); - } - return resultState; + bool exists = (int)key < state.Length; + resultState[i++] = new(key, exists && test[key].Equals(state[(int)key])); } + return resultState; + } - private static PartialState[] InState(bool[] test, bool[] state) + private static PartialState[] InState(bool[] test, bool[] state) + { + PartialState[] resultState = new PartialState[test.Length]; + for (int i = 0; i < test.Length; i++) { - PartialState[] resultState = new PartialState[test.Length]; - for (int i = 0; i < test.Length; i++) - { - resultState[i] = new((GoapKey)i, test[i].Equals(state[i])); - } - return resultState; + resultState[i] = new((GoapKey)i, test[i].Equals(state[i])); } + return resultState; + } - /** + /** * Apply the stateChange to the currentState */ - private static bool[] PopulateState(bool[] currentState, Dictionary futureState) - { - var pooler = ArrayPool.Shared; - bool[] state = pooler.Rent(currentState.Length); - - currentState.CopyTo(state, 0); + private static bool[] PopulateState(bool[] currentState, Dictionary futureState) + { + var pooler = ArrayPool.Shared; + bool[] state = pooler.Rent(currentState.Length); - foreach ((GoapKey key, bool value) in futureState) - { - state[(int)key] = value; - } + currentState.CopyTo(state, 0); - pooler.Return(state); - return state; + foreach ((GoapKey key, bool value) in futureState) + { + state[(int)key] = value; } - /** + pooler.Return(state); + return state; + } + + /** * Used for building up the graph and holding the running costs of actions. */ - private sealed class Node - { - public readonly Node? parent; - public readonly float runningCost; - public readonly bool[] state; - public readonly GoapGoal? action; + private sealed class Node + { + public readonly Node? parent; + public readonly float runningCost; + public readonly bool[] state; + public readonly GoapGoal? action; - public Node(Node? parent, float runningCost, bool[] state, GoapGoal? action) - { - this.parent = parent; - this.runningCost = runningCost; - this.state = state; - this.action = action; - } + public Node(Node? parent, float runningCost, bool[] state, GoapGoal? action) + { + this.parent = parent; + this.runningCost = runningCost; + this.state = state; + this.action = action; } } } \ No newline at end of file diff --git a/Core/GOAP/PartialState.cs b/Core/GOAP/PartialState.cs index 945d03cbd..7597f389f 100644 --- a/Core/GOAP/PartialState.cs +++ b/Core/GOAP/PartialState.cs @@ -1,14 +1,13 @@ -namespace Core.GOAP +namespace Core.GOAP; + +public readonly struct PartialState { - public readonly struct PartialState - { - public readonly GoapKey Key; - public readonly bool Value; + public readonly GoapKey Key; + public readonly bool Value; - public PartialState(GoapKey key, bool value) - { - Key = key; - Value = value; - } + public PartialState(GoapKey key, bool value) + { + Key = key; + Value = value; } } diff --git a/Core/Goals/AdhocGoal.cs b/Core/Goals/AdhocGoal.cs index 929f49bc4..9a19bc0cd 100644 --- a/Core/Goals/AdhocGoal.cs +++ b/Core/Goals/AdhocGoal.cs @@ -2,81 +2,80 @@ using Microsoft.Extensions.Logging; using System; -namespace Core.Goals +namespace Core.Goals; + +public sealed class AdhocGoal : GoapGoal { - public sealed class AdhocGoal : GoapGoal - { - public override float Cost => key.Cost; + public override float Cost => key.Cost; - private readonly ILogger logger; - private readonly ConfigurableInput input; + private readonly ILogger logger; + private readonly ConfigurableInput input; - private readonly Wait wait; - private readonly StopMoving stopMoving; - private readonly AddonReader addonReader; - private readonly PlayerReader playerReader; + private readonly Wait wait; + private readonly StopMoving stopMoving; + private readonly AddonReader addonReader; + private readonly PlayerReader playerReader; - private readonly KeyAction key; - private readonly CastingHandler castingHandler; - private readonly IMountHandler mountHandler; + private readonly KeyAction key; + private readonly CastingHandler castingHandler; + private readonly IMountHandler mountHandler; - private readonly bool? combatMatters; + private readonly bool? combatMatters; - public AdhocGoal(KeyAction key, ILogger logger, ConfigurableInput input, Wait wait, AddonReader addonReader, StopMoving stopMoving, CastingHandler castingHandler, IMountHandler mountHandler) - : base(nameof(AdhocGoal)) - { - this.logger = logger; - this.input = input; - this.wait = wait; - this.stopMoving = stopMoving; - this.addonReader = addonReader; - this.playerReader = addonReader.PlayerReader; - this.key = key; - this.castingHandler = castingHandler; - this.mountHandler = mountHandler; - - if (bool.TryParse(key.InCombat, out bool result)) - { - AddPrecondition(GoapKey.incombat, result); - combatMatters = result; - } + public AdhocGoal(KeyAction key, ILogger logger, ConfigurableInput input, Wait wait, AddonReader addonReader, StopMoving stopMoving, CastingHandler castingHandler, IMountHandler mountHandler) + : base(nameof(AdhocGoal)) + { + this.logger = logger; + this.input = input; + this.wait = wait; + this.stopMoving = stopMoving; + this.addonReader = addonReader; + this.playerReader = addonReader.PlayerReader; + this.key = key; + this.castingHandler = castingHandler; + this.mountHandler = mountHandler; - Keys = new KeyAction[1] { key }; + if (bool.TryParse(key.InCombat, out bool result)) + { + AddPrecondition(GoapKey.incombat, result); + combatMatters = result; } - public override bool CanRun() => key.CanRun(); + Keys = new KeyAction[1] { key }; + } - public override void OnEnter() - { - if (key.BeforeCastDismount && mountHandler.IsMounted()) - { - mountHandler.Dismount(); - wait.Update(); - } + public override bool CanRun() => key.CanRun(); - castingHandler.UpdateGCD(true); + public override void OnEnter() + { + if (key.BeforeCastDismount && mountHandler.IsMounted()) + { + mountHandler.Dismount(); + wait.Update(); } - public override void Update() - { - if (castingHandler.SpellInQueue()) - { - wait.Update(); - return; - } + castingHandler.UpdateGCD(true); + } - if ((key.Charge >= 1 && key.CanRun())) - { - castingHandler.CastIfReady(key, Interrupt); - wait.Update(); - } + public override void Update() + { + if (castingHandler.SpellInQueue()) + { + wait.Update(); + return; } - private bool Interrupt() + if ((key.Charge >= 1 && key.CanRun())) { - return combatMatters.HasValue - ? combatMatters.Value == addonReader.PlayerReader.Bits.PlayerInCombat() && addonReader.DamageTakenCount() > 0 - : addonReader.DamageTakenCount() > 0; + castingHandler.CastIfReady(key, Interrupt); + wait.Update(); } } + + private bool Interrupt() + { + return combatMatters.HasValue + ? combatMatters.Value == addonReader.PlayerReader.Bits.PlayerInCombat() && addonReader.DamageTakenCount() > 0 + : addonReader.DamageTakenCount() > 0; + } } diff --git a/Core/Goals/AdhocNPCGoal.cs b/Core/Goals/AdhocNPCGoal.cs index 0a31291c8..2f3e9e0df 100644 --- a/Core/Goals/AdhocNPCGoal.cs +++ b/Core/Goals/AdhocNPCGoal.cs @@ -10,319 +10,318 @@ #pragma warning disable 162 -namespace Core.Goals +namespace Core.Goals; + +public sealed class AdhocNPCGoal : GoapGoal, IGoapEventListener, IRouteProvider, IDisposable { - public sealed class AdhocNPCGoal : GoapGoal, IGoapEventListener, IRouteProvider, IDisposable + private enum PathState { - private enum PathState - { - ApproachPathStart, - FollowPath, - Finished, - } + ApproachPathStart, + FollowPath, + Finished, + } - private const bool debug = false; + private const bool debug = false; - private const int TIMEOUT = 5000; + private const int TIMEOUT = 5000; - public override float Cost => key.Cost; + public override float Cost => key.Cost; - private readonly ILogger logger; - private readonly ConfigurableInput input; - private readonly KeyAction key; - private readonly Wait wait; - private readonly AddonReader addonReader; - private readonly Navigation navigation; - private readonly PlayerReader playerReader; - private readonly StopMoving stopMoving; - private readonly ClassConfiguration classConfig; - private readonly NpcNameTargeting npcNameTargeting; - private readonly IMountHandler mountHandler; - private readonly CancellationToken ct; - private readonly ExecGameCommand execGameCommand; - private readonly GossipReader gossipReader; + private readonly ILogger logger; + private readonly ConfigurableInput input; + private readonly KeyAction key; + private readonly Wait wait; + private readonly AddonReader addonReader; + private readonly Navigation navigation; + private readonly PlayerReader playerReader; + private readonly StopMoving stopMoving; + private readonly ClassConfiguration classConfig; + private readonly NpcNameTargeting npcNameTargeting; + private readonly IMountHandler mountHandler; + private readonly CancellationToken ct; + private readonly ExecGameCommand execGameCommand; + private readonly GossipReader gossipReader; - private PathState pathState; + private PathState pathState; - #region IRouteProvider + #region IRouteProvider - public Vector3[] PathingRoute() - { - return navigation.TotalRoute; - } + public Vector3[] PathingRoute() + { + return navigation.TotalRoute; + } - public bool HasNext() - { - return navigation.HasNext(); - } + public bool HasNext() + { + return navigation.HasNext(); + } - public Vector3 NextMapPoint() - { - return navigation.NextMapPoint(); - } + public Vector3 NextMapPoint() + { + return navigation.NextMapPoint(); + } - public DateTime LastActive => navigation.LastActive; + public DateTime LastActive => navigation.LastActive; - #endregion + #endregion - public AdhocNPCGoal(KeyAction key, ILogger logger, ConfigurableInput input, - Wait wait, AddonReader addonReader, Navigation navigation, StopMoving stopMoving, - NpcNameTargeting npcNameTargeting, ClassConfiguration classConfig, - IMountHandler mountHandler, ExecGameCommand exec, CancellationTokenSource cts) - : base(nameof(AdhocNPCGoal)) + public AdhocNPCGoal(KeyAction key, ILogger logger, ConfigurableInput input, + Wait wait, AddonReader addonReader, Navigation navigation, StopMoving stopMoving, + NpcNameTargeting npcNameTargeting, ClassConfiguration classConfig, + IMountHandler mountHandler, ExecGameCommand exec, CancellationTokenSource cts) + : base(nameof(AdhocNPCGoal)) + { + this.logger = logger; + this.input = input; + this.key = key; + this.wait = wait; + this.addonReader = addonReader; + this.playerReader = addonReader.PlayerReader; + this.stopMoving = stopMoving; + this.npcNameTargeting = npcNameTargeting; + this.classConfig = classConfig; + this.mountHandler = mountHandler; + ct = cts.Token; + this.execGameCommand = exec; + this.gossipReader = addonReader.GossipReader; + + this.navigation = navigation; + navigation.OnDestinationReached += Navigation_OnDestinationReached; + navigation.OnWayPointReached += Navigation_OnWayPointReached; + + if (bool.TryParse(key.InCombat, out bool result)) { - this.logger = logger; - this.input = input; - this.key = key; - this.wait = wait; - this.addonReader = addonReader; - this.playerReader = addonReader.PlayerReader; - this.stopMoving = stopMoving; - this.npcNameTargeting = npcNameTargeting; - this.classConfig = classConfig; - this.mountHandler = mountHandler; - ct = cts.Token; - this.execGameCommand = exec; - this.gossipReader = addonReader.GossipReader; - - this.navigation = navigation; - navigation.OnDestinationReached += Navigation_OnDestinationReached; - navigation.OnWayPointReached += Navigation_OnWayPointReached; - - if (bool.TryParse(key.InCombat, out bool result)) - { - if (!result) - AddPrecondition(GoapKey.dangercombat, result); - else - AddPrecondition(GoapKey.incombat, result); - } - - Keys = new KeyAction[] { key }; + if (!result) + AddPrecondition(GoapKey.dangercombat, result); + else + AddPrecondition(GoapKey.incombat, result); } - public void Dispose() - { - navigation.Dispose(); - } + Keys = new KeyAction[] { key }; + } - public override bool CanRun() => key.CanRun(); + public void Dispose() + { + navigation.Dispose(); + } + + public override bool CanRun() => key.CanRun(); - public void OnGoapEvent(GoapEventArgs e) + public void OnGoapEvent(GoapEventArgs e) + { + if (e.GetType() == typeof(ResumeEvent)) { - if (e.GetType() == typeof(ResumeEvent)) - { - navigation.ResetStuckParameters(); - MountIfPossible(); - } + navigation.ResetStuckParameters(); + MountIfPossible(); } + } - public override void OnEnter() - { - input.ClearTarget(); - stopMoving.Stop(); + public override void OnEnter() + { + input.ClearTarget(); + stopMoving.Stop(); - navigation.SetWayPoints(key.Path); + navigation.SetWayPoints(key.Path); - pathState = PathState.ApproachPathStart; + pathState = PathState.ApproachPathStart; - MountIfPossible(); - } + MountIfPossible(); + } + + public override void OnExit() + { + navigation.StopMovement(); + navigation.Stop(); + npcNameTargeting.ChangeNpcType(NpcNames.None); + } + + public override void Update() + { + if (playerReader.Bits.IsDrowning()) + input.Jump(); - public override void OnExit() + if (pathState != PathState.Finished) + navigation.Update(); + + wait.Update(); + } + + + private void Navigation_OnWayPointReached() + { + if (pathState is PathState.ApproachPathStart) { - navigation.StopMovement(); - navigation.Stop(); - npcNameTargeting.ChangeNpcType(NpcNames.None); + LogDebug("1 Reached the start point of the path."); + navigation.SimplifyRouteToWaypoint = false; } + } - public override void Update() - { - if (playerReader.Bits.IsDrowning()) - input.Jump(); + private void Navigation_OnDestinationReached() + { + if (pathState != PathState.ApproachPathStart || ct.IsCancellationRequested) + return; - if (pathState != PathState.Finished) - navigation.Update(); + LogDebug("Reached defined path end"); + stopMoving.Stop(); - wait.Update(); - } + input.ClearTarget(); + wait.Update(); + bool found = false; - private void Navigation_OnWayPointReached() + if (!classConfig.KeyboardOnly) { - if (pathState is PathState.ApproachPathStart) + npcNameTargeting.ChangeNpcType(NpcNames.Friendly | NpcNames.Neutral); + npcNameTargeting.WaitForUpdate(); + npcNameTargeting.WaitForUpdate(); + found = npcNameTargeting.FindBy(CursorType.Vendor, CursorType.Repair, CursorType.Innkeeper); + wait.Update(); + + if (!found) { - LogDebug("1 Reached the start point of the path."); - navigation.SimplifyRouteToWaypoint = false; + LogWarn($"No target found by cursor({CursorType.Vendor.ToStringF()}, {CursorType.Repair.ToStringF()}, {CursorType.Innkeeper.ToStringF()})!"); } } - private void Navigation_OnDestinationReached() + if (!found) { - if (pathState != PathState.ApproachPathStart || ct.IsCancellationRequested) - return; - - LogDebug("Reached defined path end"); - stopMoving.Stop(); - - input.ClearTarget(); + Log($"Use KeyAction.Key macro to aquire target"); + input.Proc.KeyPress(key.ConsoleKey, input.defaultKeyPress); wait.Update(); + } - bool found = false; + wait.Until(400, playerReader.Bits.HasTarget); + if (!playerReader.Bits.HasTarget()) + { + LogWarn("No target found! Turn left to find NPC"); + input.Proc.KeyPressSleep(input.Proc.TurnLeftKey, 250, ct); + return; + } - if (!classConfig.KeyboardOnly) - { - npcNameTargeting.ChangeNpcType(NpcNames.Friendly | NpcNames.Neutral); - npcNameTargeting.WaitForUpdate(); - npcNameTargeting.WaitForUpdate(); - found = npcNameTargeting.FindBy(CursorType.Vendor, CursorType.Repair, CursorType.Innkeeper); - wait.Update(); + Log($"Found Target!"); + input.Interact(); - if (!found) - { - LogWarn($"No target found by cursor({CursorType.Vendor.ToStringF()}, {CursorType.Repair.ToStringF()}, {CursorType.Innkeeper.ToStringF()})!"); - } - } + if (!OpenMerchantWindow()) + return; - if (!found) - { - Log($"Use KeyAction.Key macro to aquire target"); - input.Proc.KeyPress(key.ConsoleKey, input.defaultKeyPress); - wait.Update(); - } + input.Proc.KeyPress(ConsoleKey.Escape, input.defaultKeyPress); + input.ClearTarget(); + wait.Update(); - wait.Until(400, playerReader.Bits.HasTarget); - if (!playerReader.Bits.HasTarget()) - { - LogWarn("No target found! Turn left to find NPC"); - input.Proc.KeyPressSleep(input.Proc.TurnLeftKey, 250, ct); - return; - } + Span reverseMapPath = stackalloc Vector3[key.Path.Length]; + key.Path.CopyTo(reverseMapPath); + reverseMapPath.Reverse(); + navigation.SetWayPoints(reverseMapPath); - Log($"Found Target!"); - input.Interact(); + pathState++; - if (!OpenMerchantWindow()) - return; + LogDebug("Go back reverse to the start point of the path."); + navigation.ResetStuckParameters(); - input.Proc.KeyPress(ConsoleKey.Escape, input.defaultKeyPress); - input.ClearTarget(); + // At this point the BagsFull is false + // which mean it it would exit the Goal + // instead keep it trapped to follow the route back + while (navigation.HasWaypoint() && !ct.IsCancellationRequested) + { + navigation.Update(); wait.Update(); + } - Span reverseMapPath = stackalloc Vector3[key.Path.Length]; - key.Path.CopyTo(reverseMapPath); - reverseMapPath.Reverse(); - navigation.SetWayPoints(reverseMapPath); - - pathState++; + pathState = PathState.Finished; - LogDebug("Go back reverse to the start point of the path."); - navigation.ResetStuckParameters(); + LogDebug("2 Reached the start point of the path."); + stopMoving.Stop(); - // At this point the BagsFull is false - // which mean it it would exit the Goal - // instead keep it trapped to follow the route back - while (navigation.HasWaypoint() && !ct.IsCancellationRequested) - { - navigation.Update(); - wait.Update(); - } - - pathState = PathState.Finished; + navigation.SimplifyRouteToWaypoint = true; + MountIfPossible(); + } - LogDebug("2 Reached the start point of the path."); - stopMoving.Stop(); + private void MountIfPossible() + { + float totalDistance = VectorExt.TotalDistance(navigation.TotalRoute, VectorExt.WorldDistanceXY); - navigation.SimplifyRouteToWaypoint = true; - MountIfPossible(); + if (classConfig.UseMount && mountHandler.CanMount() && + (MountHandler.ShouldMount(totalDistance) || + (navigation.TotalRoute.Length > 0 && + mountHandler.ShouldMount(navigation.TotalRoute[^1])) + )) + { + Log("Mount up"); + mountHandler.MountUp(); + navigation.ResetStuckParameters(); } + } - private void MountIfPossible() + private bool OpenMerchantWindow() + { + (bool t, double e) = wait.Until(TIMEOUT, gossipReader.GossipStartOrMerchantWindowOpened); + if (gossipReader.MerchantWindowOpened()) { - float totalDistance = VectorExt.TotalDistance(navigation.TotalRoute, VectorExt.WorldDistanceXY); - - if (classConfig.UseMount && mountHandler.CanMount() && - (MountHandler.ShouldMount(totalDistance) || - (navigation.TotalRoute.Length > 0 && - mountHandler.ShouldMount(navigation.TotalRoute[^1])) - )) - { - Log("Mount up"); - mountHandler.MountUp(); - navigation.ResetStuckParameters(); - } + LogWarn($"Gossip no options! {e}ms"); } - - private bool OpenMerchantWindow() + else { - (bool t, double e) = wait.Until(TIMEOUT, gossipReader.GossipStartOrMerchantWindowOpened); - if (gossipReader.MerchantWindowOpened()) + (t, e) = wait.Until(TIMEOUT, gossipReader.GossipEnd); + if (t) { - LogWarn($"Gossip no options! {e}ms"); + LogWarn($"Gossip - {nameof(gossipReader.GossipEnd)} not fired after {e}ms"); + return false; } else { - (t, e) = wait.Until(TIMEOUT, gossipReader.GossipEnd); - if (t) + if (gossipReader.Gossips.TryGetValue(Gossip.Vendor, out int orderNum)) { - LogWarn($"Gossip - {nameof(gossipReader.GossipEnd)} not fired after {e}ms"); - return false; + Log($"Picked {orderNum}th for {Gossip.Vendor.ToStringF()}"); + execGameCommand.Run($"/run SelectGossipOption({orderNum})--"); } else { - if (gossipReader.Gossips.TryGetValue(Gossip.Vendor, out int orderNum)) - { - Log($"Picked {orderNum}th for {Gossip.Vendor.ToStringF()}"); - execGameCommand.Run($"/run SelectGossipOption({orderNum})--"); - } - else - { - LogWarn($"Target({playerReader.TargetId}) has no {Gossip.Vendor.ToStringF()} option!"); - return false; - } + LogWarn($"Target({playerReader.TargetId}) has no {Gossip.Vendor.ToStringF()} option!"); + return false; } } + } + + Log($"Merchant window opened after {e}ms"); - Log($"Merchant window opened after {e}ms"); + (t, e) = wait.Until(TIMEOUT, gossipReader.MerchantWindowSelling); + if (!t) + { + Log($"Merchant sell grey items started after {e}ms"); - (t, e) = wait.Until(TIMEOUT, gossipReader.MerchantWindowSelling); + (t, e) = wait.Until(TIMEOUT, gossipReader.MerchantWindowSellingFinished); if (!t) { - Log($"Merchant sell grey items started after {e}ms"); - - (t, e) = wait.Until(TIMEOUT, gossipReader.MerchantWindowSellingFinished); - if (!t) - { - Log($"Merchant sell grey items finished, took {e}ms"); - return true; - } - else - { - Log($"Merchant sell grey items timeout! Too many items to sell?! Increase {nameof(TIMEOUT)} - {e}ms"); - return true; - } + Log($"Merchant sell grey items finished, took {e}ms"); + return true; } else { - Log($"Merchant sell nothing! {e}ms"); + Log($"Merchant sell grey items timeout! Too many items to sell?! Increase {nameof(TIMEOUT)} - {e}ms"); return true; } } - - - private void Log(string text) + else { - logger.LogInformation(text); + Log($"Merchant sell nothing! {e}ms"); + return true; } + } - private void LogDebug(string text) - { - if (debug) - logger.LogDebug(text); - } - private void LogWarn(string text) - { - logger.LogWarning(text); - } + private void Log(string text) + { + logger.LogInformation(text); + } + + private void LogDebug(string text) + { + if (debug) + logger.LogDebug(text); + } + + private void LogWarn(string text) + { + logger.LogWarning(text); } } \ No newline at end of file diff --git a/Core/Goals/ApproachTargetGoal.cs b/Core/Goals/ApproachTargetGoal.cs index ad329af00..39eeac988 100644 --- a/Core/Goals/ApproachTargetGoal.cs +++ b/Core/Goals/ApproachTargetGoal.cs @@ -7,144 +7,131 @@ #pragma warning disable 162 -namespace Core.Goals +namespace Core.Goals; + +public sealed class ApproachTargetGoal : GoapGoal, IGoapEventListener { - public sealed class ApproachTargetGoal : GoapGoal, IGoapEventListener - { - private const bool debug = true; - private const double STUCK_INTERVAL_MS = 400; // cant be lower than Approach.Cooldown + private const bool debug = true; + private const double STUCK_INTERVAL_MS = 400; // cant be lower than Approach.Cooldown - public override float Cost => 8f; + public override float Cost => 8f; - private readonly ILogger logger; - private readonly ConfigurableInput input; - private readonly Wait wait; - private readonly AddonReader addonReader; - private readonly PlayerReader playerReader; - private readonly StopMoving stopMoving; - private readonly CombatUtil combatUtil; - private readonly IBlacklist targetBlacklist; + private readonly ILogger logger; + private readonly ConfigurableInput input; + private readonly Wait wait; + private readonly AddonReader addonReader; + private readonly PlayerReader playerReader; + private readonly StopMoving stopMoving; + private readonly CombatUtil combatUtil; + private readonly IBlacklist targetBlacklist; - private DateTime approachStart; + private DateTime approachStart; - private double nextStuckCheckTime; - private Vector3 playerMap; + private double nextStuckCheckTime; + private Vector3 playerMap; - private int initialTargetGuid; - private float initialMinRange; + private int initialTargetGuid; + private float initialMinRange; - private double ApproachDurationMs => (DateTime.UtcNow - approachStart).TotalMilliseconds; + private double ApproachDurationMs => (DateTime.UtcNow - approachStart).TotalMilliseconds; - public ApproachTargetGoal(ILogger logger, ConfigurableInput input, Wait wait, AddonReader addonReader, StopMoving stopMoving, CombatUtil combatUtil, IBlacklist blacklist) - : base(nameof(ApproachTargetGoal)) - { - this.logger = logger; - this.input = input; - - this.wait = wait; - this.addonReader = addonReader; - this.playerReader = addonReader.PlayerReader; - this.stopMoving = stopMoving; - this.combatUtil = combatUtil; - this.targetBlacklist = blacklist; - - AddPrecondition(GoapKey.hastarget, true); - AddPrecondition(GoapKey.targetisalive, true); - AddPrecondition(GoapKey.targethostile, true); - AddPrecondition(GoapKey.incombatrange, false); - - AddEffect(GoapKey.incombatrange, true); - } + public ApproachTargetGoal(ILogger logger, ConfigurableInput input, Wait wait, AddonReader addonReader, StopMoving stopMoving, CombatUtil combatUtil, IBlacklist blacklist) + : base(nameof(ApproachTargetGoal)) + { + this.logger = logger; + this.input = input; + + this.wait = wait; + this.addonReader = addonReader; + this.playerReader = addonReader.PlayerReader; + this.stopMoving = stopMoving; + this.combatUtil = combatUtil; + this.targetBlacklist = blacklist; + + AddPrecondition(GoapKey.hastarget, true); + AddPrecondition(GoapKey.targetisalive, true); + AddPrecondition(GoapKey.targethostile, true); + AddPrecondition(GoapKey.incombatrange, false); + + AddEffect(GoapKey.incombatrange, true); + } - public void OnGoapEvent(GoapEventArgs e) + public void OnGoapEvent(GoapEventArgs e) + { + if (e.GetType() == typeof(ResumeEvent)) { - if (e.GetType() == typeof(ResumeEvent)) - { - approachStart = DateTime.UtcNow; - } + approachStart = DateTime.UtcNow; } + } - public override void OnEnter() - { - initialTargetGuid = playerReader.TargetGuid; - initialMinRange = playerReader.MinRange(); - playerMap = playerReader.MapPos; + public override void OnEnter() + { + initialTargetGuid = playerReader.TargetGuid; + initialMinRange = playerReader.MinRange(); + playerMap = playerReader.MapPos; - combatUtil.Update(); + combatUtil.Update(); - approachStart = DateTime.UtcNow; - SetNextStuckTimeCheck(); - } + approachStart = DateTime.UtcNow; + SetNextStuckTimeCheck(); + } - public override void OnExit() - { - if (input.Proc.IsKeyDown(input.Proc.ForwardKey)) - input.Proc.SetKeyState(input.Proc.ForwardKey, false); - } + public override void OnExit() + { + if (input.Proc.IsKeyDown(input.Proc.ForwardKey)) + input.Proc.SetKeyState(input.Proc.ForwardKey, false); + } - public override void Update() - { - wait.Update(); + public override void Update() + { + wait.Update(); - if (combatUtil.EnteredCombat() && !playerReader.Bits.TargetInCombat()) - { - stopMoving.Stop(); + if (combatUtil.EnteredCombat() && !playerReader.Bits.TargetInCombat()) + { + stopMoving.Stop(); - input.ClearTarget(); - wait.Update(); + input.ClearTarget(); + wait.Update(); - combatUtil.AquiredTarget(5000); - return; - } + combatUtil.AquiredTarget(5000); + return; + } - if (input.ClassConfig.Approach.GetRemainingCooldown() == 0) - { - input.Approach(); - } + if (input.ClassConfig.Approach.GetRemainingCooldown() == 0) + { + input.Approach(); + } - if (!playerReader.Bits.PlayerInCombat()) - { - NonCombatApproach(); - RandomJump(); - } + if (!playerReader.Bits.PlayerInCombat()) + { + NonCombatApproach(); + RandomJump(); } + } - private void NonCombatApproach() + private void NonCombatApproach() + { + if (ApproachDurationMs >= nextStuckCheckTime) { - if (ApproachDurationMs >= nextStuckCheckTime) - { - SetNextStuckTimeCheck(); + SetNextStuckTimeCheck(); - Vector3 last = playerMap; - playerMap = playerReader.MapPos; - if (!combatUtil.IsPlayerMoving(last)) + Vector3 last = playerMap; + playerMap = playerReader.MapPos; + if (!combatUtil.IsPlayerMoving(last)) + { + if (playerReader.LastUIError == UI_ERROR.ERR_AUTOFOLLOW_TOO_FAR) { - if (playerReader.LastUIError == UI_ERROR.ERR_AUTOFOLLOW_TOO_FAR) - { - playerReader.LastUIError = UI_ERROR.NONE; - - if (debug) - Log($"Too far ({playerReader.MinRange()} yard), start moving forward!"); - - input.Proc.SetKeyState(input.Proc.ForwardKey, true); - return; - } + playerReader.LastUIError = UI_ERROR.NONE; if (debug) - Log($"Seems stuck! Clear Target."); - - input.ClearTarget(); - wait.Update(); - input.Proc.KeyPress(Random.Shared.Next(2) == 0 ? input.Proc.TurnLeftKey : input.Proc.TurnRightKey, 250 + Random.Shared.Next(250)); + Log($"Too far ({playerReader.MinRange()} yard), start moving forward!"); + input.Proc.SetKeyState(input.Proc.ForwardKey, true); return; } - } - if (ApproachDurationMs > 15_000) - { if (debug) - Log("Too long time. Clear Target. Turn away."); + Log($"Seems stuck! Clear Target."); input.ClearTarget(); wait.Update(); @@ -152,72 +139,84 @@ private void NonCombatApproach() return; } + } + + if (ApproachDurationMs > 15_000) + { + if (debug) + Log("Too long time. Clear Target. Turn away."); + + input.ClearTarget(); + wait.Update(); + input.Proc.KeyPress(Random.Shared.Next(2) == 0 ? input.Proc.TurnLeftKey : input.Proc.TurnRightKey, 250 + Random.Shared.Next(250)); - if (playerReader.TargetGuid == initialTargetGuid) + return; + } + + if (playerReader.TargetGuid == initialTargetGuid) + { + int initialTargetMinRange = playerReader.MinRange(); + if (input.ClassConfig.TargetNearestTarget.GetRemainingCooldown() == 0) { - int initialTargetMinRange = playerReader.MinRange(); - if (input.ClassConfig.TargetNearestTarget.GetRemainingCooldown() == 0) - { - input.NearestTarget(); - wait.Update(); - } + input.NearestTarget(); + wait.Update(); + } - if (playerReader.TargetGuid != initialTargetGuid) + if (playerReader.TargetGuid != initialTargetGuid) + { + if (playerReader.Bits.HasTarget() && !targetBlacklist.Is()) // blacklist { - if (playerReader.Bits.HasTarget() && !targetBlacklist.Is()) // blacklist + if (playerReader.MinRange() < initialTargetMinRange) { - if (playerReader.MinRange() < initialTargetMinRange) - { - if (debug) - Log($"Found a closer target! {playerReader.MinRange()} < {initialTargetMinRange}"); - - initialMinRange = playerReader.MinRange(); - } - else - { - initialTargetGuid = -1; - if (debug) - Log("Stick to initial target!"); - - input.LastTarget(); - wait.Update(); - } + if (debug) + Log($"Found a closer target! {playerReader.MinRange()} < {initialTargetMinRange}"); + + initialMinRange = playerReader.MinRange(); } else { + initialTargetGuid = -1; if (debug) - Log($"Lost the target due blacklist!"); + Log("Stick to initial target!"); + + input.LastTarget(); + wait.Update(); } } - } - - if (ApproachDurationMs > 2000 && initialMinRange < playerReader.MinRange()) - { - if (debug) - Log($"Going away from the target! {initialMinRange} < {playerReader.MinRange()}"); - - input.ClearTarget(); - wait.Update(); + else + { + if (debug) + Log($"Lost the target due blacklist!"); + } } } - private void SetNextStuckTimeCheck() + if (ApproachDurationMs > 2000 && initialMinRange < playerReader.MinRange()) { - nextStuckCheckTime = ApproachDurationMs + STUCK_INTERVAL_MS; - } + if (debug) + Log($"Going away from the target! {initialMinRange} < {playerReader.MinRange()}"); - private void RandomJump() - { - if (ApproachDurationMs > 2000 && - input.ClassConfig.Jump.SinceLastClickMs > Random.Shared.Next(5000, 25_000)) - { - input.Jump(); - } + input.ClearTarget(); + wait.Update(); } + } - private void Log(string text) + private void SetNextStuckTimeCheck() + { + nextStuckCheckTime = ApproachDurationMs + STUCK_INTERVAL_MS; + } + + private void RandomJump() + { + if (ApproachDurationMs > 2000 && + input.ClassConfig.Jump.SinceLastClickMs > Random.Shared.Next(5000, 25_000)) { - logger.LogDebug(text); + input.Jump(); } } + + private void Log(string text) + { + logger.LogDebug(text); + } } \ No newline at end of file diff --git a/Core/Goals/BlacklistTargetGoal.cs b/Core/Goals/BlacklistTargetGoal.cs index 7961ed236..f08959656 100644 --- a/Core/Goals/BlacklistTargetGoal.cs +++ b/Core/Goals/BlacklistTargetGoal.cs @@ -1,35 +1,34 @@ -namespace Core.Goals +namespace Core.Goals; + +public sealed class BlacklistTargetGoal : GoapGoal { - public sealed class BlacklistTargetGoal : GoapGoal - { - public override float Cost => 2; + public override float Cost => 2; - private readonly PlayerReader playerReader; - private readonly ConfigurableInput input; - private readonly IBlacklist targetBlacklist; + private readonly PlayerReader playerReader; + private readonly ConfigurableInput input; + private readonly IBlacklist targetBlacklist; - public BlacklistTargetGoal(PlayerReader playerReader, ConfigurableInput input, IBlacklist blacklist) - : base(nameof(BlacklistTargetGoal)) - { - this.playerReader = playerReader; - this.input = input; - this.targetBlacklist = blacklist; - } + public BlacklistTargetGoal(PlayerReader playerReader, ConfigurableInput input, IBlacklist blacklist) + : base(nameof(BlacklistTargetGoal)) + { + this.playerReader = playerReader; + this.input = input; + this.targetBlacklist = blacklist; + } - public override bool CanRun() - { - return playerReader.Bits.HasTarget() && targetBlacklist.Is(); - } + public override bool CanRun() + { + return playerReader.Bits.HasTarget() && targetBlacklist.Is(); + } - public override void OnEnter() - { - if (playerReader.PetHasTarget() || - playerReader.IsCasting() || - playerReader.Bits.SpellOn_AutoAttack() || playerReader.Bits.SpellOn_AutoShot() || - playerReader.Bits.SpellOn_Shoot()) - input.StopAttack(); + public override void OnEnter() + { + if (playerReader.PetHasTarget() || + playerReader.IsCasting() || + playerReader.Bits.SpellOn_AutoAttack() || playerReader.Bits.SpellOn_AutoShot() || + playerReader.Bits.SpellOn_Shoot()) + input.StopAttack(); - input.ClearTarget(); - } + input.ClearTarget(); } } diff --git a/Core/Goals/CombatGoal.cs b/Core/Goals/CombatGoal.cs index b1c86eaf7..48526d053 100644 --- a/Core/Goals/CombatGoal.cs +++ b/Core/Goals/CombatGoal.cs @@ -5,211 +5,210 @@ using System; using System.Numerics; -namespace Core.Goals +namespace Core.Goals; + +public sealed class CombatGoal : GoapGoal, IGoapEventListener { - public sealed class CombatGoal : GoapGoal, IGoapEventListener + public override float Cost => 4f; + + private readonly ILogger logger; + private readonly ConfigurableInput input; + + private readonly Wait wait; + private readonly AddonReader addonReader; + private readonly PlayerReader playerReader; + private readonly StopMoving stopMoving; + private readonly CastingHandler castingHandler; + private readonly IMountHandler mountHandler; + + private float lastDirection; + private float lastMinDistance; + private float lastMaxDistance; + + public CombatGoal(ILogger logger, ConfigurableInput input, Wait wait, + AddonReader addonReader, StopMoving stopMoving, + ClassConfiguration classConfiguration, CastingHandler castingHandler, + IMountHandler mountHandler) + : base(nameof(CombatGoal)) { - public override float Cost => 4f; - - private readonly ILogger logger; - private readonly ConfigurableInput input; - - private readonly Wait wait; - private readonly AddonReader addonReader; - private readonly PlayerReader playerReader; - private readonly StopMoving stopMoving; - private readonly CastingHandler castingHandler; - private readonly IMountHandler mountHandler; - - private float lastDirection; - private float lastMinDistance; - private float lastMaxDistance; - - public CombatGoal(ILogger logger, ConfigurableInput input, Wait wait, - AddonReader addonReader, StopMoving stopMoving, - ClassConfiguration classConfiguration, CastingHandler castingHandler, - IMountHandler mountHandler) - : base(nameof(CombatGoal)) + this.logger = logger; + this.input = input; + + this.wait = wait; + this.addonReader = addonReader; + this.playerReader = addonReader.PlayerReader; + this.stopMoving = stopMoving; + this.castingHandler = castingHandler; + this.mountHandler = mountHandler; + + AddPrecondition(GoapKey.incombat, true); + AddPrecondition(GoapKey.hastarget, true); + AddPrecondition(GoapKey.targetisalive, true); + AddPrecondition(GoapKey.targethostile, true); + //AddPrecondition(GoapKey.targettargetsus, true); + AddPrecondition(GoapKey.incombatrange, true); + + AddEffect(GoapKey.producedcorpse, true); + AddEffect(GoapKey.targetisalive, false); + AddEffect(GoapKey.hastarget, false); + + Keys = classConfiguration.Combat.Sequence; + } + + public void OnGoapEvent(GoapEventArgs e) + { + if (e is GoapStateEvent s && s.Key == GoapKey.producedcorpse) { - this.logger = logger; - this.input = input; - - this.wait = wait; - this.addonReader = addonReader; - this.playerReader = addonReader.PlayerReader; - this.stopMoving = stopMoving; - this.castingHandler = castingHandler; - this.mountHandler = mountHandler; - - AddPrecondition(GoapKey.incombat, true); - AddPrecondition(GoapKey.hastarget, true); - AddPrecondition(GoapKey.targetisalive, true); - AddPrecondition(GoapKey.targethostile, true); - //AddPrecondition(GoapKey.targettargetsus, true); - AddPrecondition(GoapKey.incombatrange, true); - - AddEffect(GoapKey.producedcorpse, true); - AddEffect(GoapKey.targetisalive, false); - AddEffect(GoapKey.hastarget, false); - - Keys = classConfiguration.Combat.Sequence; + // have to check range + // ex. target died far away have to consider the range and approximate + float distance = (lastMaxDistance + lastMinDistance) / 2f; + SendGoapEvent(new CorpseEvent(GetCorpseLocation(distance), distance)); } + } - public void OnGoapEvent(GoapEventArgs e) + private void ResetCooldowns() + { + for (int i = 0; i < Keys.Length; i++) { - if (e is GoapStateEvent s && s.Key == GoapKey.producedcorpse) + KeyAction keyAction = Keys[i]; + if (keyAction.ResetOnNewTarget) { - // have to check range - // ex. target died far away have to consider the range and approximate - float distance = (lastMaxDistance + lastMinDistance) / 2f; - SendGoapEvent(new CorpseEvent(GetCorpseLocation(distance), distance)); + keyAction.ResetCooldown(); + keyAction.ResetCharges(); } } + } - private void ResetCooldowns() + public override void OnEnter() + { + if (mountHandler.IsMounted()) { - for (int i = 0; i < Keys.Length; i++) - { - KeyAction keyAction = Keys[i]; - if (keyAction.ResetOnNewTarget) - { - keyAction.ResetCooldown(); - keyAction.ResetCharges(); - } - } + mountHandler.Dismount(); } - public override void OnEnter() - { - if (mountHandler.IsMounted()) - { - mountHandler.Dismount(); - } + castingHandler.UpdateGCD(true); - castingHandler.UpdateGCD(true); + lastDirection = playerReader.Direction; + } - lastDirection = playerReader.Direction; + public override void OnExit() + { + if (addonReader.DamageTakenCount() > 0 && !playerReader.Bits.HasTarget()) + { + stopMoving.Stop(); } + } + + public override void Update() + { + wait.Update(); - public override void OnExit() + if (MathF.Abs(lastDirection - playerReader.Direction) > MathF.PI / 2) { - if (addonReader.DamageTakenCount() > 0 && !playerReader.Bits.HasTarget()) - { - stopMoving.Stop(); - } + logger.LogInformation("Turning too fast!"); + stopMoving.Stop(); } - public override void Update() + lastDirection = playerReader.Direction; + lastMinDistance = playerReader.MinRange(); + lastMaxDistance = playerReader.MaxRange(); + + if (playerReader.Bits.IsDrowning()) { - wait.Update(); + input.Jump(); + return; + } - if (MathF.Abs(lastDirection - playerReader.Direction) > MathF.PI / 2) + if (playerReader.Bits.HasTarget()) + { + if (input.ClassConfig.AutoPetAttack && + playerReader.Bits.HasPet() && + (!playerReader.PetHasTarget() || playerReader.PetTargetGuid != playerReader.TargetGuid) && + input.ClassConfig.PetAttack.GetRemainingCooldown() == 0) { - logger.LogInformation("Turning too fast!"); - stopMoving.Stop(); + input.PetAttack(); } - lastDirection = playerReader.Direction; - lastMinDistance = playerReader.MinRange(); - lastMaxDistance = playerReader.MaxRange(); - - if (playerReader.Bits.IsDrowning()) + for (int i = 0; i < Keys.Length; i++) { - input.Jump(); - return; - } + KeyAction keyAction = Keys[i]; - if (playerReader.Bits.HasTarget()) - { - if (input.ClassConfig.AutoPetAttack && - playerReader.Bits.HasPet() && - (!playerReader.PetHasTarget() || playerReader.PetTargetGuid != playerReader.TargetGuid) && - input.ClassConfig.PetAttack.GetRemainingCooldown() == 0) + if (castingHandler.SpellInQueue() && !keyAction.BaseAction) { - input.PetAttack(); + continue; } - for (int i = 0; i < Keys.Length; i++) + if (playerReader.Bits.TargetAlive() && castingHandler.CastIfReady(keyAction, + keyAction.Interrupts.Count > 0 + ? keyAction.CanBeInterrupted + : playerReader.Bits.TargetAlive)) { - KeyAction keyAction = Keys[i]; - - if (castingHandler.SpellInQueue() && !keyAction.BaseAction) - { - continue; - } - - if (playerReader.Bits.TargetAlive() && castingHandler.CastIfReady(keyAction, - keyAction.Interrupts.Count > 0 - ? keyAction.CanBeInterrupted - : playerReader.Bits.TargetAlive)) - { - break; - } + break; } } + } - if (!playerReader.Bits.HasTarget()) - { - stopMoving.Stop(); - logger.LogInformation("Lost target!"); + if (!playerReader.Bits.HasTarget()) + { + stopMoving.Stop(); + logger.LogInformation("Lost target!"); - if (addonReader.DamageTakenCount() > 0 && !input.ClassConfig.KeyboardOnly) - { - FindNewTarget(); - } + if (addonReader.DamageTakenCount() > 0 && !input.ClassConfig.KeyboardOnly) + { + FindNewTarget(); } } + } - private void FindNewTarget() + private void FindNewTarget() + { + if (playerReader.PetHasTarget() && addonReader.CombatLog.DeadGuid.Value != playerReader.PetTargetGuid) { - if (playerReader.PetHasTarget() && addonReader.CombatLog.DeadGuid.Value != playerReader.PetTargetGuid) + ResetCooldowns(); + + input.TargetPet(); + input.TargetOfTarget(); + wait.Update(); + + if (!playerReader.Bits.TargetIsDead()) { - ResetCooldowns(); + logger.LogWarning("---- New targe from Pet target!"); + return; + } - input.TargetPet(); - input.TargetOfTarget(); - wait.Update(); + input.ClearTarget(); + } + + if (addonReader.DamageTakenCount() > 1) + { + logger.LogInformation("Checking target in front..."); + input.NearestTarget(); + wait.Update(); - if (!playerReader.Bits.TargetIsDead()) + if (playerReader.Bits.HasTarget() && !playerReader.Bits.TargetIsDead()) + { + if (playerReader.Bits.TargetInCombat() && playerReader.Bits.TargetOfTargetIsPlayerOrPet()) { - logger.LogWarning("---- New targe from Pet target!"); + stopMoving.Stop(); + ResetCooldowns(); + + logger.LogWarning("Found new target!"); return; } input.ClearTarget(); + wait.Update(); } - - if (addonReader.DamageTakenCount() > 1) + else if (addonReader.DamageTakenCount() > 0) { - logger.LogInformation("Checking target in front..."); - input.NearestTarget(); - wait.Update(); - - if (playerReader.Bits.HasTarget() && !playerReader.Bits.TargetIsDead()) - { - if (playerReader.Bits.TargetInCombat() && playerReader.Bits.TargetOfTargetIsPlayerOrPet()) - { - stopMoving.Stop(); - ResetCooldowns(); - - logger.LogWarning("Found new target!"); - return; - } - - input.ClearTarget(); - wait.Update(); - } - else if (addonReader.DamageTakenCount() > 0) - { - logger.LogWarning($"---- Possible threats from behind {addonReader.DamageTakenCount()}. Waiting target by damage taken!"); - wait.Till(2500, playerReader.Bits.HasTarget); - } + logger.LogWarning($"---- Possible threats from behind {addonReader.DamageTakenCount()}. Waiting target by damage taken!"); + wait.Till(2500, playerReader.Bits.HasTarget); } } + } - private Vector3 GetCorpseLocation(float distance) - { - return PointEstimator.GetPoint(playerReader.MapPos, playerReader.Direction, distance); - } + private Vector3 GetCorpseLocation(float distance) + { + return PointEstimator.GetPoint(playerReader.MapPos, playerReader.Direction, distance); } } \ No newline at end of file diff --git a/Core/Goals/ConditionalWaitGoal.cs b/Core/Goals/ConditionalWaitGoal.cs index 7e20a19c0..71484037e 100644 --- a/Core/Goals/ConditionalWaitGoal.cs +++ b/Core/Goals/ConditionalWaitGoal.cs @@ -1,33 +1,32 @@ using Microsoft.Extensions.Logging; -namespace Core.Goals +namespace Core.Goals; + +public sealed class ConditionalWaitGoal : GoapGoal { - public sealed class ConditionalWaitGoal : GoapGoal - { - public override float Cost => Keys[0].Cost; + public override float Cost => Keys[0].Cost; - private readonly ILogger logger; - private readonly Wait wait; + private readonly ILogger logger; + private readonly Wait wait; - public ConditionalWaitGoal(KeyAction keyAction, ILogger logger, Wait wait) - : base(nameof(ConditionalWaitGoal)) - { - this.logger = logger; - this.wait = wait; + public ConditionalWaitGoal(KeyAction keyAction, ILogger logger, Wait wait) + : base(nameof(ConditionalWaitGoal)) + { + this.logger = logger; + this.wait = wait; - Keys = new KeyAction[1] { keyAction }; - } + Keys = new KeyAction[1] { keyAction }; + } - public override bool CanRun() => Keys[0].CanRun(); + public override bool CanRun() => Keys[0].CanRun(); - public override void OnEnter() - { - logger.LogInformation($"Waiting for {Keys[0].Name}"); - } + public override void OnEnter() + { + logger.LogInformation($"Waiting for {Keys[0].Name}"); + } - public override void Update() - { - wait.Update(); - } + public override void Update() + { + wait.Update(); } } \ No newline at end of file diff --git a/Core/Goals/ConsumeCorpseGoal.cs b/Core/Goals/ConsumeCorpseGoal.cs index 372f6d7f0..55546c1df 100644 --- a/Core/Goals/ConsumeCorpseGoal.cs +++ b/Core/Goals/ConsumeCorpseGoal.cs @@ -1,65 +1,64 @@ using Microsoft.Extensions.Logging; using Core.GOAP; -namespace Core.Goals +namespace Core.Goals; + +public sealed partial class ConsumeCorpseGoal : GoapGoal { - public sealed partial class ConsumeCorpseGoal : GoapGoal - { - public override float Cost => 4.1f; + public override float Cost => 4.1f; + + private readonly ILogger logger; + private readonly ClassConfiguration classConfig; + private readonly GoapAgentState state; - private readonly ILogger logger; - private readonly ClassConfiguration classConfig; - private readonly GoapAgentState state; + public ConsumeCorpseGoal(ILogger logger, ClassConfiguration classConfig, GoapAgentState state) + : base(nameof(ConsumeCorpseGoal)) + { + this.logger = logger; + this.classConfig = classConfig; + this.state = state; - public ConsumeCorpseGoal(ILogger logger, ClassConfiguration classConfig, GoapAgentState state) - : base(nameof(ConsumeCorpseGoal)) + if (classConfig.KeyboardOnly) + { + AddPrecondition(GoapKey.consumablecorpsenearby, true); + } + else { - this.logger = logger; - this.classConfig = classConfig; - this.state = state; + AddPrecondition(GoapKey.damagedone, false); + AddPrecondition(GoapKey.damagetaken, false); + } - if (classConfig.KeyboardOnly) - { - AddPrecondition(GoapKey.consumablecorpsenearby, true); - } - else - { - AddPrecondition(GoapKey.damagedone, false); - AddPrecondition(GoapKey.damagetaken, false); - } + AddPrecondition(GoapKey.producedcorpse, true); + AddPrecondition(GoapKey.consumecorpse, false); - AddPrecondition(GoapKey.producedcorpse, true); - AddPrecondition(GoapKey.consumecorpse, false); + AddEffect(GoapKey.producedcorpse, false); + AddEffect(GoapKey.consumecorpse, true); - AddEffect(GoapKey.producedcorpse, false); - AddEffect(GoapKey.consumecorpse, true); + if (classConfig.Loot) + { + AddEffect(GoapKey.shouldloot, true); - if (classConfig.Loot) + if (classConfig.GatherCorpse) { - AddEffect(GoapKey.shouldloot, true); - - if (classConfig.GatherCorpse) - { - AddEffect(GoapKey.shouldgather, true); - } + AddEffect(GoapKey.shouldgather, true); } } + } - public override void OnEnter() - { - LogConsume(logger); - SendGoapEvent(new GoapStateEvent(GoapKey.consumecorpse, true)); + public override void OnEnter() + { + LogConsume(logger); + SendGoapEvent(new GoapStateEvent(GoapKey.consumecorpse, true)); - if (classConfig.Loot) - { - state.LootableCorpseCount++; - } + if (classConfig.Loot) + { + state.LootableCorpseCount++; } - - [LoggerMessage( - EventId = 100, - Level = LogLevel.Information, - Message = "----- Safe to consume a corpse.")] - static partial void LogConsume(ILogger logger); } + + [LoggerMessage( + EventId = 100, + Level = LogLevel.Information, + Message = "----- Safe to consume a corpse.")] + static partial void LogConsume(ILogger logger); } diff --git a/Core/Goals/CorpseConsumedGoal.cs b/Core/Goals/CorpseConsumedGoal.cs index 5056e0e6a..006c0d33c 100644 --- a/Core/Goals/CorpseConsumedGoal.cs +++ b/Core/Goals/CorpseConsumedGoal.cs @@ -4,70 +4,69 @@ using Microsoft.Extensions.Logging; -namespace Core.Goals +namespace Core.Goals; + +public sealed partial class CorpseConsumedGoal : GoapGoal { - public sealed partial class CorpseConsumedGoal : GoapGoal - { - public override float Cost => 4.7f; + public override float Cost => 4.7f; - private readonly ILogger logger; - private readonly GoapAgentState goapAgentState; - private readonly Wait wait; + private readonly ILogger logger; + private readonly GoapAgentState goapAgentState; + private readonly Wait wait; - private readonly bool lootEnabled; + private readonly bool lootEnabled; - public CorpseConsumedGoal(ILogger logger, ClassConfiguration classConfig, GoapAgentState goapAgentState, Wait wait) - : base(nameof(CorpseConsumedGoal)) - { - this.logger = logger; - this.goapAgentState = goapAgentState; - this.wait = wait; + public CorpseConsumedGoal(ILogger logger, ClassConfiguration classConfig, GoapAgentState goapAgentState, Wait wait) + : base(nameof(CorpseConsumedGoal)) + { + this.logger = logger; + this.goapAgentState = goapAgentState; + this.wait = wait; - this.lootEnabled = classConfig.Loot; + this.lootEnabled = classConfig.Loot; - if (classConfig.KeyboardOnly) - { - AddPrecondition(GoapKey.consumablecorpsenearby, true); - } - else - { - AddPrecondition(GoapKey.damagedone, false); - AddPrecondition(GoapKey.damagetaken, false); - } + if (classConfig.KeyboardOnly) + { + AddPrecondition(GoapKey.consumablecorpsenearby, true); + } + else + { + AddPrecondition(GoapKey.damagedone, false); + AddPrecondition(GoapKey.damagetaken, false); + } - AddPrecondition(GoapKey.consumecorpse, true); + AddPrecondition(GoapKey.consumecorpse, true); - AddEffect(GoapKey.consumecorpse, false); + AddEffect(GoapKey.consumecorpse, false); + } + + public override void OnEnter() + { + goapAgentState.ConsumableCorpseCount = Math.Max(goapAgentState.ConsumableCorpseCount - 1, 0); + if (goapAgentState.ConsumableCorpseCount == 0) + { + goapAgentState.LastCombatKillCount = 0; } - public override void OnEnter() + LogConsumed(logger, goapAgentState.LastCombatKillCount, goapAgentState.ConsumableCorpseCount); + + SendGoapEvent(new GoapStateEvent(GoapKey.consumecorpse, false)); + + if (goapAgentState.LastCombatKillCount > 1) { - goapAgentState.ConsumableCorpseCount = Math.Max(goapAgentState.ConsumableCorpseCount - 1, 0); - if (goapAgentState.ConsumableCorpseCount == 0) - { - goapAgentState.LastCombatKillCount = 0; - } - - LogConsumed(logger, goapAgentState.LastCombatKillCount, goapAgentState.ConsumableCorpseCount); - - SendGoapEvent(new GoapStateEvent(GoapKey.consumecorpse, false)); - - if (goapAgentState.LastCombatKillCount > 1) - { - wait.Fixed(Loot.LOOTFRAME_AUTOLOOT_DELAY); - } - - if (!lootEnabled) - { - SendGoapEvent(new RemoveClosestPoi(CorpseEvent.NAME)); - wait.Fixed(Loot.LOOTFRAME_AUTOLOOT_DELAY / 2); - } + wait.Fixed(Loot.LOOTFRAME_AUTOLOOT_DELAY); } - [LoggerMessage( - EventId = 101, - Level = LogLevel.Information, - Message = "----- Corpse consumed. Total: {total} | Remaining: {remains}")] - static partial void LogConsumed(ILogger logger, int total, int remains); + if (!lootEnabled) + { + SendGoapEvent(new RemoveClosestPoi(CorpseEvent.NAME)); + wait.Fixed(Loot.LOOTFRAME_AUTOLOOT_DELAY / 2); + } } + + [LoggerMessage( + EventId = 101, + Level = LogLevel.Information, + Message = "----- Corpse consumed. Total: {total} | Remaining: {remains}")] + static partial void LogConsumed(ILogger logger, int total, int remains); } \ No newline at end of file diff --git a/Core/Goals/DismountSubGoal.cs b/Core/Goals/DismountSubGoal.cs index 328baf3e3..9beaf585a 100644 --- a/Core/Goals/DismountSubGoal.cs +++ b/Core/Goals/DismountSubGoal.cs @@ -1,25 +1,24 @@ using Core.GOAP; -namespace Core.Goals +namespace Core.Goals; + +public sealed class DismountSubGoal : GoapGoal { - public sealed class DismountSubGoal : GoapGoal - { - public override float Cost => 0.5f; + public override float Cost => 0.5f; - private readonly IMountHandler mountHandler; + private readonly IMountHandler mountHandler; - public DismountSubGoal(IMountHandler mountHandler) - : base(nameof(DismountSubGoal)) - { - this.mountHandler = mountHandler; + public DismountSubGoal(IMountHandler mountHandler) + : base(nameof(DismountSubGoal)) + { + this.mountHandler = mountHandler; - AddPrecondition(GoapKey.ismounted, true); - AddEffect(GoapKey.ismounted, false); - } + AddPrecondition(GoapKey.ismounted, true); + AddEffect(GoapKey.ismounted, false); + } - public override void Update() - { - mountHandler.Dismount(); - } + public override void Update() + { + mountHandler.Dismount(); } } diff --git a/Core/Goals/FollowFocusGoal.cs b/Core/Goals/FollowFocusGoal.cs index edd4892cd..caafaf95b 100644 --- a/Core/Goals/FollowFocusGoal.cs +++ b/Core/Goals/FollowFocusGoal.cs @@ -1,42 +1,41 @@ using Core.GOAP; -namespace Core.Goals +namespace Core.Goals; + +public sealed class FollowFocusGoal : GoapGoal { - public sealed class FollowFocusGoal : GoapGoal + public override float Cost => 19f; + + private readonly ConfigurableInput input; + private readonly PlayerReader playerReader; + private readonly Wait wait; + + public FollowFocusGoal(ConfigurableInput input, PlayerReader playerReader, Wait wait) + : base(nameof(FollowFocusGoal)) { - public override float Cost => 19f; + this.input = input; + this.playerReader = playerReader; + this.wait = wait; - private readonly ConfigurableInput input; - private readonly PlayerReader playerReader; - private readonly Wait wait; + AddPrecondition(GoapKey.hasfocus, true); + AddPrecondition(GoapKey.dangercombat, false); + AddPrecondition(GoapKey.damagedone, false); + AddPrecondition(GoapKey.damagetaken, false); + AddPrecondition(GoapKey.producedcorpse, false); + AddPrecondition(GoapKey.consumecorpse, false); + } - public FollowFocusGoal(ConfigurableInput input, PlayerReader playerReader, Wait wait) - : base(nameof(FollowFocusGoal)) + public override void Update() + { + if (playerReader.TargetGuid != playerReader.FocusGuid) { - this.input = input; - this.playerReader = playerReader; - this.wait = wait; - - AddPrecondition(GoapKey.hasfocus, true); - AddPrecondition(GoapKey.dangercombat, false); - AddPrecondition(GoapKey.damagedone, false); - AddPrecondition(GoapKey.damagetaken, false); - AddPrecondition(GoapKey.producedcorpse, false); - AddPrecondition(GoapKey.consumecorpse, false); + input.TargetFocus(); + wait.Update(); } - public override void Update() - { - if (playerReader.TargetGuid != playerReader.FocusGuid) - { - input.TargetFocus(); - wait.Update(); - } - - if (playerReader.SpellInRange.Focus_Inspect) - input.FollowTarget(); + if (playerReader.SpellInRange.Focus_Inspect) + input.FollowTarget(); - wait.Update(); - } + wait.Update(); } } diff --git a/Core/Goals/FollowRouteGoal.cs b/Core/Goals/FollowRouteGoal.cs index a73c90c8c..2c75f4f38 100644 --- a/Core/Goals/FollowRouteGoal.cs +++ b/Core/Goals/FollowRouteGoal.cs @@ -9,377 +9,376 @@ #pragma warning disable 162 -namespace Core.Goals +namespace Core.Goals; + +public sealed class FollowRouteGoal : GoapGoal, IGoapEventListener, IRouteProvider, IEditedRouteReceiver, IDisposable { - public sealed class FollowRouteGoal : GoapGoal, IGoapEventListener, IRouteProvider, IEditedRouteReceiver, IDisposable - { - public override float Cost => 20f; + public override float Cost => 20f; - private const bool debug = false; + private const bool debug = false; - private readonly ILogger logger; - private readonly ConfigurableInput input; - private readonly Wait wait; - private readonly AddonReader addonReader; - private readonly PlayerReader playerReader; - private readonly ClassConfiguration classConfig; - private readonly IMountHandler mountHandler; - private readonly Navigation navigation; + private readonly ILogger logger; + private readonly ConfigurableInput input; + private readonly Wait wait; + private readonly AddonReader addonReader; + private readonly PlayerReader playerReader; + private readonly ClassConfiguration classConfig; + private readonly IMountHandler mountHandler; + private readonly Navigation navigation; - private readonly IBlacklist targetBlacklist; - private readonly TargetFinder targetFinder; - private const NpcNames NpcNameToFind = NpcNames.Enemy | NpcNames.Neutral; + private readonly IBlacklist targetBlacklist; + private readonly TargetFinder targetFinder; + private const NpcNames NpcNameToFind = NpcNames.Enemy | NpcNames.Neutral; - private const int MIN_TIME_TO_START_CYCLE_PROFESSION = 5000; - private const int CYCLE_PROFESSION_PERIOD = 8000; + private const int MIN_TIME_TO_START_CYCLE_PROFESSION = 5000; + private const int CYCLE_PROFESSION_PERIOD = 8000; - private readonly ManualResetEvent sideActivityManualReset; - private readonly Thread? sideActivityThread; - private CancellationTokenSource sideActivityCts; + private readonly ManualResetEvent sideActivityManualReset; + private readonly Thread? sideActivityThread; + private CancellationTokenSource sideActivityCts; - private Vector3[] mapRoute; + private Vector3[] mapRoute; - private DateTime onEnterTime; + private DateTime onEnterTime; - #region IRouteProvider + #region IRouteProvider - public DateTime LastActive => navigation.LastActive; + public DateTime LastActive => navigation.LastActive; - public Vector3[] PathingRoute() - { - return navigation.TotalRoute; - } + public Vector3[] PathingRoute() + { + return navigation.TotalRoute; + } - public bool HasNext() - { - return navigation.HasNext(); - } + public bool HasNext() + { + return navigation.HasNext(); + } - public Vector3 NextMapPoint() - { - return navigation.NextMapPoint(); - } + public Vector3 NextMapPoint() + { + return navigation.NextMapPoint(); + } - #endregion + #endregion - public FollowRouteGoal(ILogger logger, ConfigurableInput input, Wait wait, - AddonReader addonReader, ClassConfiguration classConfig, Vector3[] route, - Navigation navigation, IMountHandler mountHandler, TargetFinder targetFinder, - IBlacklist blacklist) - : base(nameof(FollowRouteGoal)) + public FollowRouteGoal(ILogger logger, ConfigurableInput input, Wait wait, + AddonReader addonReader, ClassConfiguration classConfig, Vector3[] route, + Navigation navigation, IMountHandler mountHandler, TargetFinder targetFinder, + IBlacklist blacklist) + : base(nameof(FollowRouteGoal)) + { + this.logger = logger; + this.input = input; + + this.wait = wait; + this.addonReader = addonReader; + this.classConfig = classConfig; + this.playerReader = addonReader.PlayerReader; + this.mapRoute = route; + this.mountHandler = mountHandler; + this.targetFinder = targetFinder; + this.targetBlacklist = blacklist; + + this.navigation = navigation; + navigation.OnPathCalculated += Navigation_OnPathCalculated; + navigation.OnDestinationReached += Navigation_OnDestinationReached; + navigation.OnWayPointReached += Navigation_OnWayPointReached; + + if (classConfig.Mode == Mode.AttendedGather) + { + AddPrecondition(GoapKey.dangercombat, false); + navigation.OnAnyPointReached += Navigation_OnWayPointReached; + } + else { - this.logger = logger; - this.input = input; - - this.wait = wait; - this.addonReader = addonReader; - this.classConfig = classConfig; - this.playerReader = addonReader.PlayerReader; - this.mapRoute = route; - this.mountHandler = mountHandler; - this.targetFinder = targetFinder; - this.targetBlacklist = blacklist; - - this.navigation = navigation; - navigation.OnPathCalculated += Navigation_OnPathCalculated; - navigation.OnDestinationReached += Navigation_OnDestinationReached; - navigation.OnWayPointReached += Navigation_OnWayPointReached; - - if (classConfig.Mode == Mode.AttendedGather) + if (classConfig.Loot) { - AddPrecondition(GoapKey.dangercombat, false); - navigation.OnAnyPointReached += Navigation_OnWayPointReached; + AddPrecondition(GoapKey.incombat, false); } - else - { - if (classConfig.Loot) - { - AddPrecondition(GoapKey.incombat, false); - } - AddPrecondition(GoapKey.damagedone, false); - AddPrecondition(GoapKey.damagetaken, false); + AddPrecondition(GoapKey.damagedone, false); + AddPrecondition(GoapKey.damagetaken, false); - AddPrecondition(GoapKey.producedcorpse, false); - AddPrecondition(GoapKey.consumecorpse, false); - } + AddPrecondition(GoapKey.producedcorpse, false); + AddPrecondition(GoapKey.consumecorpse, false); + } - sideActivityCts = new(); - sideActivityManualReset = new(false); + sideActivityCts = new(); + sideActivityManualReset = new(false); - if (classConfig.Mode == Mode.AttendedGather) - { - if (classConfig.GatherFindKeyConfig.Length > 1) - { - sideActivityThread = new(Thread_AttendedGather); - sideActivityThread.Start(); - } - } - else + if (classConfig.Mode == Mode.AttendedGather) + { + if (classConfig.GatherFindKeyConfig.Length > 1) { - sideActivityThread = new(Thread_LookingForTarget); + sideActivityThread = new(Thread_AttendedGather); sideActivityThread.Start(); } } - - public void Dispose() + else { - navigation.Dispose(); - - sideActivityCts.Cancel(); - sideActivityManualReset.Set(); + sideActivityThread = new(Thread_LookingForTarget); + sideActivityThread.Start(); } + } - private void Abort() - { - if (!targetBlacklist.Is()) - navigation.StopMovement(); + public void Dispose() + { + navigation.Dispose(); - navigation.Stop(); + sideActivityCts.Cancel(); + sideActivityManualReset.Set(); + } - sideActivityManualReset.Reset(); - targetFinder.Reset(); - } + private void Abort() + { + if (!targetBlacklist.Is()) + navigation.StopMovement(); - private void Resume() - { - onEnterTime = DateTime.UtcNow; + navigation.Stop(); - if (sideActivityCts.IsCancellationRequested) - { - sideActivityCts = new(); - } - sideActivityManualReset.Set(); + sideActivityManualReset.Reset(); + targetFinder.Reset(); + } - if (!navigation.HasWaypoint()) - { - RefillWaypoints(true); - } - else - { - navigation.Resume(); - } + private void Resume() + { + onEnterTime = DateTime.UtcNow; - MountIfPossible(); + if (sideActivityCts.IsCancellationRequested) + { + sideActivityCts = new(); } + sideActivityManualReset.Set(); - public void OnGoapEvent(GoapEventArgs e) + if (!navigation.HasWaypoint()) { - if (e.GetType() == typeof(AbortEvent)) - { - Abort(); - } - else if (e.GetType() == typeof(ResumeEvent)) - { - Resume(); - } + RefillWaypoints(true); } - - public override void OnEnter() => Resume(); - - public override void OnExit() => Abort(); - - public override void Update() + else { - if (playerReader.Bits.HasTarget() && playerReader.Bits.TargetIsDead()) - { - Log("Has target but its dead."); - input.ClearTarget(); - wait.Update(); - } + navigation.Resume(); + } - if (playerReader.Bits.IsDrowning()) - { - input.Jump(); - } + MountIfPossible(); + } - if (playerReader.Bits.PlayerInCombat() && classConfig.Mode != Mode.AttendedGather) { return; } + public void OnGoapEvent(GoapEventArgs e) + { + if (e.GetType() == typeof(AbortEvent)) + { + Abort(); + } + else if (e.GetType() == typeof(ResumeEvent)) + { + Resume(); + } + } - if (!sideActivityCts.IsCancellationRequested) - navigation.Update(sideActivityCts.Token); + public override void OnEnter() => Resume(); - RandomJump(); + public override void OnExit() => Abort(); + public override void Update() + { + if (playerReader.Bits.HasTarget() && playerReader.Bits.TargetIsDead()) + { + Log("Has target but its dead."); + input.ClearTarget(); wait.Update(); } - private void Thread_LookingForTarget() + if (playerReader.Bits.IsDrowning()) { - sideActivityManualReset.WaitOne(); + input.Jump(); + } - while (!sideActivityCts.IsCancellationRequested) - { - if (targetFinder.Search(NpcNameToFind, playerReader.Bits.TargetIsNotDead, sideActivityCts.Token)) - { - sideActivityCts.Cancel(); - sideActivityManualReset.Reset(); - } - else - sideActivityCts.Token.WaitHandle.WaitOne(1); - - sideActivityManualReset.WaitOne(); - } + if (playerReader.Bits.PlayerInCombat() && classConfig.Mode != Mode.AttendedGather) { return; } - if (logger.IsEnabled(LogLevel.Debug)) - logger.LogDebug("LookingForTarget thread stopped!"); - } + if (!sideActivityCts.IsCancellationRequested) + navigation.Update(sideActivityCts.Token); - private void Thread_AttendedGather() - { - sideActivityManualReset.WaitOne(); + RandomJump(); - while (!sideActivityCts.IsCancellationRequested) - { - if ((DateTime.UtcNow - onEnterTime).TotalMilliseconds > MIN_TIME_TO_START_CYCLE_PROFESSION) - { - AlternateGatherTypes(); - } - sideActivityCts.Token.WaitHandle.WaitOne(CYCLE_PROFESSION_PERIOD); - sideActivityManualReset.WaitOne(); - } + wait.Update(); + } - if (logger.IsEnabled(LogLevel.Debug)) - logger.LogDebug("AttendedGather thread stopped!"); - } + private void Thread_LookingForTarget() + { + sideActivityManualReset.WaitOne(); - private void AlternateGatherTypes() + while (!sideActivityCts.IsCancellationRequested) { - var oldestKey = classConfig.GatherFindKeyConfig.MaxBy(x => x.SinceLastClickMs); - if (!playerReader.IsCasting() && - oldestKey?.SinceLastClickMs > CYCLE_PROFESSION_PERIOD) + if (targetFinder.Search(NpcNameToFind, playerReader.Bits.TargetIsNotDead, sideActivityCts.Token)) { - logger.LogInformation($"[{oldestKey.Key}] {oldestKey.Name} pressed for {input.defaultKeyPress}ms"); - input.Proc.KeyPress(oldestKey.ConsoleKey, input.defaultKeyPress); - oldestKey.SetClicked(); + sideActivityCts.Cancel(); + sideActivityManualReset.Reset(); } + else + sideActivityCts.Token.WaitHandle.WaitOne(1); + + sideActivityManualReset.WaitOne(); } - private void MountIfPossible() - { - float totalDistance = VectorExt.TotalDistance(navigation.TotalRoute, VectorExt.WorldDistanceXY); + if (logger.IsEnabled(LogLevel.Debug)) + logger.LogDebug("LookingForTarget thread stopped!"); + } - if (classConfig.UseMount && mountHandler.CanMount() && - (MountHandler.ShouldMount(totalDistance) || - (navigation.TotalRoute.Length > 0 && - mountHandler.ShouldMount(navigation.TotalRoute[^1])) - )) + private void Thread_AttendedGather() + { + sideActivityManualReset.WaitOne(); + + while (!sideActivityCts.IsCancellationRequested) + { + if ((DateTime.UtcNow - onEnterTime).TotalMilliseconds > MIN_TIME_TO_START_CYCLE_PROFESSION) { - Log("Mount up"); - mountHandler.MountUp(); - navigation.ResetStuckParameters(); + AlternateGatherTypes(); } + sideActivityCts.Token.WaitHandle.WaitOne(CYCLE_PROFESSION_PERIOD); + sideActivityManualReset.WaitOne(); } - #region Refill rules + if (logger.IsEnabled(LogLevel.Debug)) + logger.LogDebug("AttendedGather thread stopped!"); + } - private void Navigation_OnPathCalculated() + private void AlternateGatherTypes() + { + var oldestKey = classConfig.GatherFindKeyConfig.MaxBy(x => x.SinceLastClickMs); + if (!playerReader.IsCasting() && + oldestKey?.SinceLastClickMs > CYCLE_PROFESSION_PERIOD) { - MountIfPossible(); + logger.LogInformation($"[{oldestKey.Key}] {oldestKey.Name} pressed for {input.defaultKeyPress}ms"); + input.Proc.KeyPress(oldestKey.ConsoleKey, input.defaultKeyPress); + oldestKey.SetClicked(); } + } - private void Navigation_OnDestinationReached() - { - if (debug) - LogDebug("Navigation_OnDestinationReached"); - - RefillWaypoints(false); - MountIfPossible(); - } + private void MountIfPossible() + { + float totalDistance = VectorExt.TotalDistance(navigation.TotalRoute, VectorExt.WorldDistanceXY); - private void Navigation_OnWayPointReached() + if (classConfig.UseMount && mountHandler.CanMount() && + (MountHandler.ShouldMount(totalDistance) || + (navigation.TotalRoute.Length > 0 && + mountHandler.ShouldMount(navigation.TotalRoute[^1])) + )) { - MountIfPossible(); + Log("Mount up"); + mountHandler.MountUp(); + navigation.ResetStuckParameters(); } + } - public void RefillWaypoints(bool onlyClosest) - { - Log($"{nameof(RefillWaypoints)} - findClosest:{onlyClosest} - ThereAndBack:{input.ClassConfig.PathThereAndBack}"); + #region Refill rules - Vector3 playerMap = playerReader.MapPos; + private void Navigation_OnPathCalculated() + { + MountIfPossible(); + } - Span pathMap = stackalloc Vector3[mapRoute.Length]; - mapRoute.CopyTo(pathMap); + private void Navigation_OnDestinationReached() + { + if (debug) + LogDebug("Navigation_OnDestinationReached"); - float mapDistanceToFirst = playerMap.MapDistanceXYTo(pathMap[0]); - float mapDistanceToLast = playerMap.MapDistanceXYTo(pathMap[^1]); + RefillWaypoints(false); + MountIfPossible(); + } - if (mapDistanceToLast < mapDistanceToFirst) - { - pathMap.Reverse(); - } + private void Navigation_OnWayPointReached() + { + MountIfPossible(); + } - int closestIndex = 0; - Vector3 mapClosestPoint = Vector3.Zero; - float distance = float.MaxValue; + public void RefillWaypoints(bool onlyClosest) + { + Log($"{nameof(RefillWaypoints)} - findClosest:{onlyClosest} - ThereAndBack:{input.ClassConfig.PathThereAndBack}"); - for (int i = 0; i < pathMap.Length; i++) - { - Vector3 p = pathMap[i]; - float d = playerMap.MapDistanceXYTo(p); - if (d < distance) - { - distance = d; - closestIndex = i; - mapClosestPoint = p; - } - } + Vector3 playerMap = playerReader.MapPos; - if (onlyClosest) - { - if (debug) - LogDebug($"{nameof(RefillWaypoints)}: Closest wayPoint: {mapClosestPoint}"); + Span pathMap = stackalloc Vector3[mapRoute.Length]; + mapRoute.CopyTo(pathMap); - navigation.SetWayPoints(stackalloc Vector3[1] { mapClosestPoint }); + float mapDistanceToFirst = playerMap.MapDistanceXYTo(pathMap[0]); + float mapDistanceToLast = playerMap.MapDistanceXYTo(pathMap[^1]); - return; - } + if (mapDistanceToLast < mapDistanceToFirst) + { + pathMap.Reverse(); + } - if (mapClosestPoint == pathMap[0] || mapClosestPoint == pathMap[^1]) - { - if (input.ClassConfig.PathThereAndBack) - { - navigation.SetWayPoints(pathMap); - } - else - { - pathMap.Reverse(); - navigation.SetWayPoints(pathMap); - } - } - else + int closestIndex = 0; + Vector3 mapClosestPoint = Vector3.Zero; + float distance = float.MaxValue; + + for (int i = 0; i < pathMap.Length; i++) + { + Vector3 p = pathMap[i]; + float d = playerMap.MapDistanceXYTo(p); + if (d < distance) { - Span points = pathMap[closestIndex..]; - Log($"{nameof(RefillWaypoints)} - Set destination from closest to nearest endpoint - with {points.Length} waypoints"); - navigation.SetWayPoints(points); + distance = d; + closestIndex = i; + mapClosestPoint = p; } } - #endregion - - public void ReceivePath(Vector3[] mapRoute) + if (onlyClosest) { - this.mapRoute = mapRoute; + if (debug) + LogDebug($"{nameof(RefillWaypoints)}: Closest wayPoint: {mapClosestPoint}"); + + navigation.SetWayPoints(stackalloc Vector3[1] { mapClosestPoint }); + + return; } - private void RandomJump() + if (mapClosestPoint == pathMap[0] || mapClosestPoint == pathMap[^1]) { - if ((DateTime.UtcNow - onEnterTime).TotalSeconds > 5 && - classConfig.Jump.SinceLastClickMs > Random.Shared.Next(10_000, 25_000)) + if (input.ClassConfig.PathThereAndBack) { - Log("Random jump"); - input.Jump(); + navigation.SetWayPoints(pathMap); + } + else + { + pathMap.Reverse(); + navigation.SetWayPoints(pathMap); } } - - private void LogDebug(string text) + else { - logger.LogDebug($"{nameof(FollowRouteGoal)}: {text}"); + Span points = pathMap[closestIndex..]; + Log($"{nameof(RefillWaypoints)} - Set destination from closest to nearest endpoint - with {points.Length} waypoints"); + navigation.SetWayPoints(points); } + } + + #endregion - private void Log(string text) + public void ReceivePath(Vector3[] mapRoute) + { + this.mapRoute = mapRoute; + } + + private void RandomJump() + { + if ((DateTime.UtcNow - onEnterTime).TotalSeconds > 5 && + classConfig.Jump.SinceLastClickMs > Random.Shared.Next(10_000, 25_000)) { - logger.LogInformation($"{nameof(FollowRouteGoal)}: {text}"); + Log("Random jump"); + input.Jump(); } } + + private void LogDebug(string text) + { + logger.LogDebug($"{nameof(FollowRouteGoal)}: {text}"); + } + + private void Log(string text) + { + logger.LogInformation($"{nameof(FollowRouteGoal)}: {text}"); + } } \ No newline at end of file diff --git a/Core/Goals/GoapGoal.cs b/Core/Goals/GoapGoal.cs index 6e1d56314..bb194eb0f 100644 --- a/Core/Goals/GoapGoal.cs +++ b/Core/Goals/GoapGoal.cs @@ -4,82 +4,81 @@ using System.Collections.Generic; using System.Text.RegularExpressions; -namespace Core.Goals +namespace Core.Goals; + +public abstract class GoapGoal { - public abstract class GoapGoal - { - public Dictionary Preconditions { get; } = new(); - public Dictionary Effects { get; } = new(); - public bool[] State { get; private set; } = new bool[(int)GoapKey.LENGTH]; + public Dictionary Preconditions { get; } = new(); + public Dictionary Effects { get; } = new(); + public bool[] State { get; private set; } = new bool[(int)GoapKey.LENGTH]; - private KeyAction[] keys = Array.Empty(); - public KeyAction[] Keys + private KeyAction[] keys = Array.Empty(); + public KeyAction[] Keys + { + get => keys; + protected set { - get => keys; - protected set - { - keys = value; - if (keys.Length == 1) - DisplayName = $"{Keys[0].Name} [{Keys[0].Key}]"; - } + keys = value; + if (keys.Length == 1) + DisplayName = $"{Keys[0].Name} [{Keys[0].Key}]"; } + } - public abstract float Cost { get; } + public abstract float Cost { get; } - public string Name { get; } + public string Name { get; } - public string DisplayName { get; protected set; } + public string DisplayName { get; protected set; } - public event Action? GoapEvent; + public event Action? GoapEvent; - protected GoapGoal(string name) - { - string output = Regex.Replace(name.Replace("Goal", ""), @"\p{Lu}", m => " " + m.Value.ToUpperInvariant()); - DisplayName = Name = string.Concat(output[0].ToString().ToUpper(), output.AsSpan(1)); - } + protected GoapGoal(string name) + { + string output = Regex.Replace(name.Replace("Goal", ""), @"\p{Lu}", m => " " + m.Value.ToUpperInvariant()); + DisplayName = Name = string.Concat(output[0].ToString().ToUpper(), output.AsSpan(1)); + } - public void SendGoapEvent(GoapEventArgs e) - { - GoapEvent?.Invoke(e); - } + public void SendGoapEvent(GoapEventArgs e) + { + GoapEvent?.Invoke(e); + } - public void SetState(PartialState[] newState) + public void SetState(PartialState[] newState) + { + for (int i = 0; i < newState.Length; i++) { - for (int i = 0; i < newState.Length; i++) - { - State[(int)newState[i].Key] = newState[i].Value; - } + State[(int)newState[i].Key] = newState[i].Value; } + } - public virtual bool CanRun() - { - return true; - } + public virtual bool CanRun() + { + return true; + } - public virtual void OnEnter() { } + public virtual void OnEnter() { } - public virtual void OnExit() { } + public virtual void OnExit() { } - public virtual void Update() { } + public virtual void Update() { } - public void AddPrecondition(GoapKey key, bool value) - { - Preconditions[key] = value; - } + public void AddPrecondition(GoapKey key, bool value) + { + Preconditions[key] = value; + } - public void RemovePrecondition(GoapKey key) - { - Preconditions.Remove(key); - } + public void RemovePrecondition(GoapKey key) + { + Preconditions.Remove(key); + } - public void AddEffect(GoapKey key, bool value) - { - Effects[key] = value; - } + public void AddEffect(GoapKey key, bool value) + { + Effects[key] = value; + } - public void RemoveEffect(GoapKey key) - { - Effects.Remove(key); - } + public void RemoveEffect(GoapKey key) + { + Effects.Remove(key); } } \ No newline at end of file diff --git a/Core/Goals/ItemsBrokenGoal.cs b/Core/Goals/ItemsBrokenGoal.cs index ad82e3586..c8fa1586d 100644 --- a/Core/Goals/ItemsBrokenGoal.cs +++ b/Core/Goals/ItemsBrokenGoal.cs @@ -1,31 +1,30 @@ using Core.GOAP; using Microsoft.Extensions.Logging; -namespace Core.Goals +namespace Core.Goals; + +public sealed class ItemsBrokenGoal : GoapGoal { - public sealed class ItemsBrokenGoal : GoapGoal - { - public override float Cost => 0; + public override float Cost => 0; - private readonly ILogger logger; - private readonly PlayerReader playerReader; + private readonly ILogger logger; + private readonly PlayerReader playerReader; - public ItemsBrokenGoal(PlayerReader playerReader, ILogger logger) - : base(nameof(ItemsBrokenGoal)) - { - this.playerReader = playerReader; - this.logger = logger; - } + public ItemsBrokenGoal(PlayerReader playerReader, ILogger logger) + : base(nameof(ItemsBrokenGoal)) + { + this.playerReader = playerReader; + this.logger = logger; + } - public override bool CanRun() - { - return playerReader.Bits.ItemsAreBroken(); - } + public override bool CanRun() + { + return playerReader.Bits.ItemsAreBroken(); + } - public override void Update() - { - logger.LogInformation("Items are broken"); - SendGoapEvent(new AbortEvent()); - } + public override void Update() + { + logger.LogInformation("Items are broken"); + SendGoapEvent(new AbortEvent()); } } \ No newline at end of file diff --git a/Core/Goals/LootGoal.cs b/Core/Goals/LootGoal.cs index 5cbe2cead..1654bfeb2 100644 --- a/Core/Goals/LootGoal.cs +++ b/Core/Goals/LootGoal.cs @@ -8,307 +8,306 @@ using SharedLib.Extensions; using System; -namespace Core.Goals +namespace Core.Goals; + +public sealed class LootGoal : GoapGoal, IGoapEventListener { - public sealed class LootGoal : GoapGoal, IGoapEventListener + public override float Cost => 4.6f; + + private const bool debug = true; + + private const int MAX_TIME_TO_REACH_MELEE = 10000; + private const int MAX_TIME_TO_DETECT_LOOT = 2 * CastingHandler.GCD; + private const int MAX_TIME_TO_WAIT_NPC_NAME = 1000; + + private readonly ILogger logger; + private readonly ConfigurableInput input; + + private readonly PlayerReader playerReader; + private readonly Wait wait; + private readonly AreaDB areaDb; + private readonly StopMoving stopMoving; + private readonly BagReader bagReader; + private readonly ClassConfiguration classConfig; + private readonly NpcNameTargeting npcNameTargeting; + private readonly CombatUtil combatUtil; + private readonly PlayerDirection playerDirection; + private readonly GoapAgentState state; + + private readonly List corpseLocations = new(); + + private bool gatherCorpse; + private int targetId; + private int bagHashNewOrStackGain; + private int money; + + public LootGoal(ILogger logger, ConfigurableInput input, Wait wait, + AddonReader addonReader, StopMoving stopMoving, ClassConfiguration classConfig, + NpcNameTargeting npcNameTargeting, CombatUtil combatUtil, PlayerDirection playerDirection, + GoapAgentState state) + : base(nameof(LootGoal)) { - public override float Cost => 4.6f; - - private const bool debug = true; - - private const int MAX_TIME_TO_REACH_MELEE = 10000; - private const int MAX_TIME_TO_DETECT_LOOT = 2 * CastingHandler.GCD; - private const int MAX_TIME_TO_WAIT_NPC_NAME = 1000; - - private readonly ILogger logger; - private readonly ConfigurableInput input; - - private readonly PlayerReader playerReader; - private readonly Wait wait; - private readonly AreaDB areaDb; - private readonly StopMoving stopMoving; - private readonly BagReader bagReader; - private readonly ClassConfiguration classConfig; - private readonly NpcNameTargeting npcNameTargeting; - private readonly CombatUtil combatUtil; - private readonly PlayerDirection playerDirection; - private readonly GoapAgentState state; - - private readonly List corpseLocations = new(); - - private bool gatherCorpse; - private int targetId; - private int bagHashNewOrStackGain; - private int money; - - public LootGoal(ILogger logger, ConfigurableInput input, Wait wait, - AddonReader addonReader, StopMoving stopMoving, ClassConfiguration classConfig, - NpcNameTargeting npcNameTargeting, CombatUtil combatUtil, PlayerDirection playerDirection, - GoapAgentState state) - : base(nameof(LootGoal)) - { - this.logger = logger; - this.input = input; - - this.wait = wait; - this.playerReader = addonReader.PlayerReader; - this.areaDb = addonReader.AreaDb; - this.stopMoving = stopMoving; - this.bagReader = addonReader.BagReader; - - this.classConfig = classConfig; - this.npcNameTargeting = npcNameTargeting; - this.combatUtil = combatUtil; - this.playerDirection = playerDirection; - this.state = state; - - AddPrecondition(GoapKey.shouldloot, true); - AddEffect(GoapKey.shouldloot, false); - } + this.logger = logger; + this.input = input; + + this.wait = wait; + this.playerReader = addonReader.PlayerReader; + this.areaDb = addonReader.AreaDb; + this.stopMoving = stopMoving; + this.bagReader = addonReader.BagReader; + + this.classConfig = classConfig; + this.npcNameTargeting = npcNameTargeting; + this.combatUtil = combatUtil; + this.playerDirection = playerDirection; + this.state = state; + + AddPrecondition(GoapKey.shouldloot, true); + AddEffect(GoapKey.shouldloot, false); + } - public override void OnEnter() - { - combatUtil.Update(); + public override void OnEnter() + { + combatUtil.Update(); - wait.While(LootReset); + wait.While(LootReset); - bagHashNewOrStackGain = bagReader.HashNewOrStackGain; - money = playerReader.Money; + bagHashNewOrStackGain = bagReader.HashNewOrStackGain; + money = playerReader.Money; - if (bagReader.BagsFull()) - { - logger.LogWarning("Inventory is full"); - } + if (bagReader.BagsFull()) + { + logger.LogWarning("Inventory is full"); + } - bool success = false; - if (classConfig.KeyboardOnly) + bool success = false; + if (classConfig.KeyboardOnly) + { + success = LootKeyboard(); + } + else + { + if (state.LastCombatKillCount == 1) { success = LootKeyboard(); } - else - { - if (state.LastCombatKillCount == 1) - { - success = LootKeyboard(); - } - - if (!success) - { - success = LootMouse(); - } - } - if (success) + if (!success) { - (bool t, double e) = wait.Until(MAX_TIME_TO_DETECT_LOOT, LootWindowClosedOrBagOrMoneyChanged, input.ApproachOnCooldown); - success = !t; - Log($"Loot {((success && !bagReader.BagsFull()) ? "Successful" : "Failed")} after {e}ms"); + success = LootMouse(); + } + } - gatherCorpse &= success; + if (success) + { + (bool t, double e) = wait.Until(MAX_TIME_TO_DETECT_LOOT, LootWindowClosedOrBagOrMoneyChanged, input.ApproachOnCooldown); + success = !t; + Log($"Loot {((success && !bagReader.BagsFull()) ? "Successful" : "Failed")} after {e}ms"); - if (gatherCorpse) - { - state.GatherableCorpseCount++; + gatherCorpse &= success; - CorpseEvent? ce = GetClosestCorpse(); - if (ce != null) - SendGoapEvent(new SkinCorpseEvent(ce.MapLoc, ce.Radius, targetId)); - } - } - else + if (gatherCorpse) { - Log("Loot Failed, target not found!"); - } - - SendGoapEvent(new RemoveClosestPoi(CorpseEvent.NAME)); - state.LootableCorpseCount = Math.Max(0, state.LootableCorpseCount - 1); + state.GatherableCorpseCount++; - if (!gatherCorpse && playerReader.Bits.HasTarget()) - { - input.ClearTarget(); - wait.Update(); + CorpseEvent? ce = GetClosestCorpse(); + if (ce != null) + SendGoapEvent(new SkinCorpseEvent(ce.MapLoc, ce.Radius, targetId)); } - - if (corpseLocations.Count > 0) - corpseLocations.Remove(GetClosestCorpse()!); } - - public void OnGoapEvent(GoapEventArgs e) + else { - if (e is CorpseEvent corpseEvent) - { - corpseLocations.Add(corpseEvent); - } + Log("Loot Failed, target not found!"); } - private bool FoundByCursor() - { - npcNameTargeting.ChangeNpcType(NpcNames.Corpse); + SendGoapEvent(new RemoveClosestPoi(CorpseEvent.NAME)); + state.LootableCorpseCount = Math.Max(0, state.LootableCorpseCount - 1); - wait.Fixed(playerReader.NetworkLatency.Value); - npcNameTargeting.WaitForUpdate(); + if (!gatherCorpse && playerReader.Bits.HasTarget()) + { + input.ClearTarget(); + wait.Update(); + } - if (!npcNameTargeting.FindBy(CursorType.Loot)) - { - return false; - } - npcNameTargeting.ChangeNpcType(NpcNames.None); + if (corpseLocations.Count > 0) + corpseLocations.Remove(GetClosestCorpse()!); + } - Log("Corpse clicked..."); - (bool searchTimeOut, double elapsedMs) = wait.Until(playerReader.NetworkLatency.Value, playerReader.Bits.HasTarget); - Log($"Found Npc Name ? {!searchTimeOut} | Count: {npcNameTargeting.NpcCount} {elapsedMs}ms"); + public void OnGoapEvent(GoapEventArgs e) + { + if (e is CorpseEvent corpseEvent) + { + corpseLocations.Add(corpseEvent); + } + } - CheckForGather(); + private bool FoundByCursor() + { + npcNameTargeting.ChangeNpcType(NpcNames.Corpse); - if (!MinRangeZero()) - { - (bool timeout, elapsedMs) = wait.Until(MAX_TIME_TO_REACH_MELEE, MinRangeZero, input.ApproachOnCooldown); - Log($"Reached clicked corpse ? {!timeout} {elapsedMs}ms"); - } + wait.Fixed(playerReader.NetworkLatency.Value); + npcNameTargeting.WaitForUpdate(); - return true; + if (!npcNameTargeting.FindBy(CursorType.Loot)) + { + return false; } + npcNameTargeting.ChangeNpcType(NpcNames.None); - private CorpseEvent? GetClosestCorpse() - { - if (corpseLocations.Count == 0) - return null; + Log("Corpse clicked..."); + (bool searchTimeOut, double elapsedMs) = wait.Until(playerReader.NetworkLatency.Value, playerReader.Bits.HasTarget); + Log($"Found Npc Name ? {!searchTimeOut} | Count: {npcNameTargeting.NpcCount} {elapsedMs}ms"); - int index = -1; - float minDistance = float.MaxValue; - Vector3 playerMap = playerReader.MapPos; - for (int i = 0; i < corpseLocations.Count; i++) - { - float mapDist = playerMap.MapDistanceXYTo(corpseLocations[i].MapLoc); - if (mapDist < minDistance) - { - minDistance = mapDist; - index = i; - } - } + CheckForGather(); - return corpseLocations[index]; + if (!MinRangeZero()) + { + (bool timeout, elapsedMs) = wait.Until(MAX_TIME_TO_REACH_MELEE, MinRangeZero, input.ApproachOnCooldown); + Log($"Reached clicked corpse ? {!timeout} {elapsedMs}ms"); } - private void CheckForGather() + return true; + } + + private CorpseEvent? GetClosestCorpse() + { + if (corpseLocations.Count == 0) + return null; + + int index = -1; + float minDistance = float.MaxValue; + Vector3 playerMap = playerReader.MapPos; + for (int i = 0; i < corpseLocations.Count; i++) { - if (!classConfig.GatherCorpse || - areaDb.CurrentArea == null) - return; - - gatherCorpse = false; - targetId = playerReader.TargetId; - Area area = areaDb.CurrentArea; - - if ((classConfig.Skin && Array.BinarySearch(area.skinnable, targetId) >= 0) || - (classConfig.Herb && Array.BinarySearch(area.gatherable, targetId) >= 0) || - (classConfig.Mine && Array.BinarySearch(area.minable, targetId) >= 0) || - (classConfig.Salvage && Array.BinarySearch(area.salvegable, targetId) >= 0)) + float mapDist = playerMap.MapDistanceXYTo(corpseLocations[i].MapLoc); + if (mapDist < minDistance) { - gatherCorpse = true; + minDistance = mapDist; + index = i; } - - Log($"Should gather {targetId} ? {gatherCorpse}"); } - private bool LootWindowClosedOrBagOrMoneyChanged() + return corpseLocations[index]; + } + + private void CheckForGather() + { + if (!classConfig.GatherCorpse || + areaDb.CurrentArea == null) + return; + + gatherCorpse = false; + targetId = playerReader.TargetId; + Area area = areaDb.CurrentArea; + + if ((classConfig.Skin && Array.BinarySearch(area.skinnable, targetId) >= 0) || + (classConfig.Herb && Array.BinarySearch(area.gatherable, targetId) >= 0) || + (classConfig.Mine && Array.BinarySearch(area.minable, targetId) >= 0) || + (classConfig.Salvage && Array.BinarySearch(area.salvegable, targetId) >= 0)) { - return bagHashNewOrStackGain != bagReader.HashNewOrStackGain || - money != playerReader.Money || - (LootStatus)playerReader.LootEvent.Value is - LootStatus.CLOSED; + gatherCorpse = true; } - private bool LootMouse() + Log($"Should gather {targetId} ? {gatherCorpse}"); + } + + private bool LootWindowClosedOrBagOrMoneyChanged() + { + return bagHashNewOrStackGain != bagReader.HashNewOrStackGain || + money != playerReader.Money || + (LootStatus)playerReader.LootEvent.Value is + LootStatus.CLOSED; + } + + private bool LootMouse() + { + stopMoving.Stop(); + wait.Update(); + + if (FoundByCursor()) { - stopMoving.Stop(); - wait.Update(); + return true; + } + else if (corpseLocations.Count > 0) + { + Vector3 playerMap = playerReader.MapPos; + CorpseEvent e = GetClosestCorpse()!; + float heading = DirectionCalculator.CalculateMapHeading(playerMap, e.MapLoc); + playerDirection.SetDirection(heading, e.MapLoc); + + logger.LogInformation("Look at possible closest corpse and try once again..."); + + wait.Fixed(playerReader.NetworkLatency.Value); if (FoundByCursor()) { return true; } - else if (corpseLocations.Count > 0) - { - Vector3 playerMap = playerReader.MapPos; - CorpseEvent e = GetClosestCorpse()!; - float heading = DirectionCalculator.CalculateMapHeading(playerMap, e.MapLoc); - playerDirection.SetDirection(heading, e.MapLoc); - - logger.LogInformation("Look at possible closest corpse and try once again..."); - - wait.Fixed(playerReader.NetworkLatency.Value); + } - if (FoundByCursor()) - { - return true; - } - } + return LootKeyboard(); + } - return LootKeyboard(); + private bool LootKeyboard() + { + if (!playerReader.Bits.HasTarget()) + { + input.FastLastTarget(); + wait.Update(); } - private bool LootKeyboard() + if (playerReader.Bits.HasTarget()) { - if (!playerReader.Bits.HasTarget()) - { - input.FastLastTarget(); - wait.Update(); - } - - if (playerReader.Bits.HasTarget()) + if (playerReader.Bits.TargetIsDead()) { - if (playerReader.Bits.TargetIsDead()) - { - CheckForGather(); + CheckForGather(); - Log("Target Last Target Found!"); - input.FastInteract(); + Log("Target Last Target Found!"); + input.FastInteract(); - if (!MinRangeZero()) - { - (bool timeout, double elapsedMs) = wait.Until(MAX_TIME_TO_REACH_MELEE, MinRangeZero, input.ApproachOnCooldown); - Log($"Reached Last Target ? {!timeout} {elapsedMs}ms"); - } - } - else + if (!MinRangeZero()) { - LogWarning("Don't attack alive target!"); - input.ClearTarget(); - wait.Update(); - - return false; + (bool timeout, double elapsedMs) = wait.Until(MAX_TIME_TO_REACH_MELEE, MinRangeZero, input.ApproachOnCooldown); + Log($"Reached Last Target ? {!timeout} {elapsedMs}ms"); } } + else + { + LogWarning("Don't attack alive target!"); + input.ClearTarget(); + wait.Update(); - return playerReader.Bits.HasTarget(); + return false; + } } - private bool LootReset() - { - return (LootStatus)playerReader.LootEvent.Value != LootStatus.CORPSE; - } + return playerReader.Bits.HasTarget(); + } - private bool MinRangeZero() - { - return playerReader.MinRange() == 0; - } + private bool LootReset() + { + return (LootStatus)playerReader.LootEvent.Value != LootStatus.CORPSE; + } - #region Logging + private bool MinRangeZero() + { + return playerReader.MinRange() == 0; + } - private void Log(string text) - { - if (debug) - { - logger.LogInformation($"{nameof(LootGoal)}: {text}"); - } - } + #region Logging - private void LogWarning(string text) + private void Log(string text) + { + if (debug) { - logger.LogWarning($"{nameof(LootGoal)}: {text}"); + logger.LogInformation($"{nameof(LootGoal)}: {text}"); } + } - #endregion + private void LogWarning(string text) + { + logger.LogWarning($"{nameof(LootGoal)}: {text}"); } + + #endregion } \ No newline at end of file diff --git a/Core/Goals/NullGoal.cs b/Core/Goals/NullGoal.cs index 772d3f15a..06d444b49 100644 --- a/Core/Goals/NullGoal.cs +++ b/Core/Goals/NullGoal.cs @@ -1,9 +1,8 @@ -namespace Core.Goals +namespace Core.Goals; + +public sealed class NullGoal : GoapGoal { - public sealed class NullGoal : GoapGoal - { - public override float Cost => 0; + public override float Cost => 0; - public NullGoal() : base(nameof(NullGoal)) { } - } + public NullGoal() : base(nameof(NullGoal)) { } } \ No newline at end of file diff --git a/Core/Goals/ParallelGoal.cs b/Core/Goals/ParallelGoal.cs index b2f4c9c72..6a2ee0b5d 100644 --- a/Core/Goals/ParallelGoal.cs +++ b/Core/Goals/ParallelGoal.cs @@ -3,107 +3,106 @@ using System; using System.Threading.Tasks; -namespace Core.Goals +namespace Core.Goals; + +public sealed class ParallelGoal : GoapGoal { - public sealed class ParallelGoal : GoapGoal - { - public override float Cost => 3f; + public override float Cost => 3f; - private readonly ILogger logger; - private readonly ConfigurableInput input; - private readonly StopMoving stopMoving; - private readonly Wait wait; - private readonly PlayerReader playerReader; - private readonly CastingHandler castingHandler; - private readonly IMountHandler mountHandler; + private readonly ILogger logger; + private readonly ConfigurableInput input; + private readonly StopMoving stopMoving; + private readonly Wait wait; + private readonly PlayerReader playerReader; + private readonly CastingHandler castingHandler; + private readonly IMountHandler mountHandler; - private static bool None() => false; + private static bool None() => false; - private bool castSuccess; + private bool castSuccess; - public ParallelGoal(ILogger logger, ConfigurableInput input, Wait wait, - PlayerReader playerReader, StopMoving stopMoving, ClassConfiguration classConfig, - CastingHandler castingHandler, IMountHandler mountHandler) - : base(nameof(ParallelGoal)) - { - this.logger = logger; - this.input = input; - this.stopMoving = stopMoving; - this.wait = wait; - this.playerReader = playerReader; - this.castingHandler = castingHandler; - this.mountHandler = mountHandler; + public ParallelGoal(ILogger logger, ConfigurableInput input, Wait wait, + PlayerReader playerReader, StopMoving stopMoving, ClassConfiguration classConfig, + CastingHandler castingHandler, IMountHandler mountHandler) + : base(nameof(ParallelGoal)) + { + this.logger = logger; + this.input = input; + this.stopMoving = stopMoving; + this.wait = wait; + this.playerReader = playerReader; + this.castingHandler = castingHandler; + this.mountHandler = mountHandler; - AddPrecondition(GoapKey.incombat, false); + AddPrecondition(GoapKey.incombat, false); - Keys = classConfig.Parallel.Sequence; - } + Keys = classConfig.Parallel.Sequence; + } - public override bool CanRun() + public override bool CanRun() + { + for (int i = 0; i < Keys.Length; i++) { - for (int i = 0; i < Keys.Length; i++) - { - if (Keys[i].CanRun()) - return true; - } - return false; + if (Keys[i].CanRun()) + return true; } + return false; + } - public override void OnEnter() + public override void OnEnter() + { + if (mountHandler.IsMounted()) { - if (mountHandler.IsMounted()) - { - mountHandler.Dismount(); - wait.Update(); - } - - castingHandler.UpdateGCD(true); - - for (int i = 0; i < Keys.Length; i++) - { - if (Keys[i].BeforeCastStop) - { - stopMoving.Stop(); - wait.Update(); - break; - } - } + mountHandler.Dismount(); + wait.Update(); } - public override void Update() - { - if (castingHandler.SpellInQueue()) - { - wait.Update(); - return; - } + castingHandler.UpdateGCD(true); - if (!castSuccess) + for (int i = 0; i < Keys.Length; i++) + { + if (Keys[i].BeforeCastStop) { - Cast(); + stopMoving.Stop(); wait.Update(); + break; } } + } - public override void OnExit() + public override void Update() + { + if (castingHandler.SpellInQueue()) { - castSuccess = false; + wait.Update(); + return; } - private void Cast() + if (!castSuccess) { - Parallel.For(0, Keys.Length, Execute); + Cast(); + wait.Update(); } + } - private void Execute(int i) + public override void OnExit() + { + castSuccess = false; + } + + private void Cast() + { + Parallel.For(0, Keys.Length, Execute); + } + + private void Execute(int i) + { + if (castingHandler.CastIfReady(Keys[i], None)) { - if (castingHandler.CastIfReady(Keys[i], None)) - { - Keys[i].ResetCooldown(); - Keys[i].SetClicked(); + Keys[i].ResetCooldown(); + Keys[i].SetClicked(); - castSuccess = true; - } + castSuccess = true; } } } \ No newline at end of file diff --git a/Core/Goals/PullTargetGoal.cs b/Core/Goals/PullTargetGoal.cs index 3784b54c9..2b5bed028 100644 --- a/Core/Goals/PullTargetGoal.cs +++ b/Core/Goals/PullTargetGoal.cs @@ -3,291 +3,290 @@ using SharedLib.NpcFinder; using System; -namespace Core.Goals +namespace Core.Goals; + +public sealed class PullTargetGoal : GoapGoal, IGoapEventListener { - public sealed class PullTargetGoal : GoapGoal, IGoapEventListener + public override float Cost => 7f; + + private readonly ILogger logger; + private readonly ConfigurableInput input; + private readonly Wait wait; + private readonly AddonReader addonReader; + private readonly PlayerReader playerReader; + private readonly StopMoving stopMoving; + private readonly StuckDetector stuckDetector; + private readonly NpcNameTargeting npcNameTargeting; + private readonly CastingHandler castingHandler; + private readonly IMountHandler mountHandler; + private readonly CombatUtil combatUtil; + private readonly IBlacklist targetBlacklist; + + private readonly KeyAction? approachKey; + private readonly Action approachAction; + + private readonly bool requiresNpcNameFinder; + + private DateTime pullStart; + + private double PullDurationMs => (DateTime.UtcNow - pullStart).TotalMilliseconds; + + public PullTargetGoal(ILogger logger, ConfigurableInput input, Wait wait, + AddonReader addonReader, IBlacklist blacklist, StopMoving stopMoving, + CastingHandler castingHandler, IMountHandler mountHandler, + NpcNameTargeting npcNameTargeting, StuckDetector stuckDetector, + CombatUtil combatUtil) + : base(nameof(PullTargetGoal)) { - public override float Cost => 7f; - - private readonly ILogger logger; - private readonly ConfigurableInput input; - private readonly Wait wait; - private readonly AddonReader addonReader; - private readonly PlayerReader playerReader; - private readonly StopMoving stopMoving; - private readonly StuckDetector stuckDetector; - private readonly NpcNameTargeting npcNameTargeting; - private readonly CastingHandler castingHandler; - private readonly IMountHandler mountHandler; - private readonly CombatUtil combatUtil; - private readonly IBlacklist targetBlacklist; - - private readonly KeyAction? approachKey; - private readonly Action approachAction; - - private readonly bool requiresNpcNameFinder; - - private DateTime pullStart; - - private double PullDurationMs => (DateTime.UtcNow - pullStart).TotalMilliseconds; - - public PullTargetGoal(ILogger logger, ConfigurableInput input, Wait wait, - AddonReader addonReader, IBlacklist blacklist, StopMoving stopMoving, - CastingHandler castingHandler, IMountHandler mountHandler, - NpcNameTargeting npcNameTargeting, StuckDetector stuckDetector, - CombatUtil combatUtil) - : base(nameof(PullTargetGoal)) + this.logger = logger; + this.input = input; + this.wait = wait; + this.addonReader = addonReader; + this.playerReader = addonReader.PlayerReader; + this.stopMoving = stopMoving; + this.castingHandler = castingHandler; + this.mountHandler = mountHandler; + this.npcNameTargeting = npcNameTargeting; + this.stuckDetector = stuckDetector; + this.combatUtil = combatUtil; + this.targetBlacklist = blacklist; + + Keys = input.ClassConfig.Pull.Sequence; + + approachAction = DefaultApproach; + + for (int i = 0; i < Keys.Length; i++) { - this.logger = logger; - this.input = input; - this.wait = wait; - this.addonReader = addonReader; - this.playerReader = addonReader.PlayerReader; - this.stopMoving = stopMoving; - this.castingHandler = castingHandler; - this.mountHandler = mountHandler; - this.npcNameTargeting = npcNameTargeting; - this.stuckDetector = stuckDetector; - this.combatUtil = combatUtil; - this.targetBlacklist = blacklist; - - Keys = input.ClassConfig.Pull.Sequence; - - approachAction = DefaultApproach; - - for (int i = 0; i < Keys.Length; i++) - { - KeyAction keyAction = Keys[i]; + KeyAction keyAction = Keys[i]; - if (keyAction.Name.Equals(input.ClassConfig.Approach.Name, StringComparison.OrdinalIgnoreCase)) - { - approachAction = ConditionalApproach; - approachKey = keyAction; - } + if (keyAction.Name.Equals(input.ClassConfig.Approach.Name, StringComparison.OrdinalIgnoreCase)) + { + approachAction = ConditionalApproach; + approachKey = keyAction; + } - if (keyAction.Requirements.Contains(RequirementFactory.AddVisible)) - { - requiresNpcNameFinder = true; - } + if (keyAction.Requirements.Contains(RequirementFactory.AddVisible)) + { + requiresNpcNameFinder = true; } + } + + AddPrecondition(GoapKey.incombat, false); + AddPrecondition(GoapKey.hastarget, true); + AddPrecondition(GoapKey.targetisalive, true); + AddPrecondition(GoapKey.targethostile, true); + AddPrecondition(GoapKey.pulled, false); + AddPrecondition(GoapKey.withinpullrange, true); + + AddEffect(GoapKey.pulled, true); + } + + public override void OnEnter() + { + combatUtil.Update(); + wait.Update(); + stuckDetector.Reset(); - AddPrecondition(GoapKey.incombat, false); - AddPrecondition(GoapKey.hastarget, true); - AddPrecondition(GoapKey.targetisalive, true); - AddPrecondition(GoapKey.targethostile, true); - AddPrecondition(GoapKey.pulled, false); - AddPrecondition(GoapKey.withinpullrange, true); + castingHandler.UpdateGCD(true); - AddEffect(GoapKey.pulled, true); + if (mountHandler.IsMounted()) + { + mountHandler.Dismount(); } - public override void OnEnter() + if (Keys.Length != 0 && input.ClassConfig.StopAttack.GetRemainingCooldown() == 0) { - combatUtil.Update(); + Log("Stop auto interact!"); + input.StopAttack(); wait.Update(); - stuckDetector.Reset(); - - castingHandler.UpdateGCD(true); + } - if (mountHandler.IsMounted()) - { - mountHandler.Dismount(); - } + if (requiresNpcNameFinder) + { + npcNameTargeting.ChangeNpcType(NpcNames.Enemy); + } - if (Keys.Length != 0 && input.ClassConfig.StopAttack.GetRemainingCooldown() == 0) - { - Log("Stop auto interact!"); - input.StopAttack(); - wait.Update(); - } + pullStart = DateTime.UtcNow; + } - if (requiresNpcNameFinder) - { - npcNameTargeting.ChangeNpcType(NpcNames.Enemy); - } + public override void OnExit() + { + if (requiresNpcNameFinder) + { + npcNameTargeting.ChangeNpcType(NpcNames.None); + } + } + public void OnGoapEvent(GoapEventArgs e) + { + if (e.GetType() == typeof(ResumeEvent)) + { pullStart = DateTime.UtcNow; } + } - public override void OnExit() + public override void Update() + { + wait.Update(); + + if (addonReader.DamageDoneCount() > 0) { - if (requiresNpcNameFinder) - { - npcNameTargeting.ChangeNpcType(NpcNames.None); - } + SendGoapEvent(new GoapStateEvent(GoapKey.pulled, true)); + return; } - public void OnGoapEvent(GoapEventArgs e) + if (PullDurationMs > 15_000) { - if (e.GetType() == typeof(ResumeEvent)) - { - pullStart = DateTime.UtcNow; - } + input.ClearTarget(); + Log("Pull taking too long. Clear target and face away!"); + input.Proc.KeyPress(Random.Shared.Next(2) == 0 ? input.Proc.TurnLeftKey : input.Proc.TurnRightKey, 1000); + return; } - public override void Update() + if (input.ClassConfig.AutoPetAttack && + playerReader.Bits.HasPet() && !playerReader.PetHasTarget()) { - wait.Update(); + if (input.ClassConfig.PetAttack.GetRemainingCooldown() == 0) + input.PetAttack(); + } - if (addonReader.DamageDoneCount() > 0) - { - SendGoapEvent(new GoapStateEvent(GoapKey.pulled, true)); - return; - } + bool castAny = false; + bool spellInQueue = false; + for (int i = 0; i < Keys.Length; i++) + { + KeyAction keyAction = Keys[i]; + + if (keyAction.Name.Equals(input.ClassConfig.Approach.Name, StringComparison.OrdinalIgnoreCase)) + continue; - if (PullDurationMs > 15_000) + if (!keyAction.CanRun()) { - input.ClearTarget(); - Log("Pull taking too long. Clear target and face away!"); - input.Proc.KeyPress(Random.Shared.Next(2) == 0 ? input.Proc.TurnLeftKey : input.Proc.TurnRightKey, 1000); - return; + continue; } - if (input.ClassConfig.AutoPetAttack && - playerReader.Bits.HasPet() && !playerReader.PetHasTarget()) + spellInQueue = castingHandler.SpellInQueue(); + if (spellInQueue) { - if (input.ClassConfig.PetAttack.GetRemainingCooldown() == 0) - input.PetAttack(); + break; } - bool castAny = false; - bool spellInQueue = false; - for (int i = 0; i < Keys.Length; i++) + if (castAny = castingHandler.Cast(keyAction, PullPrevention)) { - KeyAction keyAction = Keys[i]; - - if (keyAction.Name.Equals(input.ClassConfig.Approach.Name, StringComparison.OrdinalIgnoreCase)) - continue; - if (!keyAction.CanRun()) - { - continue; - } - - spellInQueue = castingHandler.SpellInQueue(); - if (spellInQueue) - { - break; - } - - if (castAny = castingHandler.Cast(keyAction, PullPrevention)) - { - - } - else if (PullPrevention() && - (playerReader.IsCasting() || - playerReader.Bits.SpellOn_AutoAttack() || - playerReader.Bits.SpellOn_AutoShot() || - playerReader.Bits.SpellOn_Shoot())) - { - Log("Preventing pulling possible tagged target!"); - input.StopAttack(); - input.ClearTarget(); - wait.Update(); - return; - } } + else if (PullPrevention() && + (playerReader.IsCasting() || + playerReader.Bits.SpellOn_AutoAttack() || + playerReader.Bits.SpellOn_AutoShot() || + playerReader.Bits.SpellOn_Shoot())) + { + Log("Preventing pulling possible tagged target!"); + input.StopAttack(); + input.ClearTarget(); + wait.Update(); + return; + } + } - if (!castAny && !spellInQueue && !playerReader.IsCasting()) + if (!castAny && !spellInQueue && !playerReader.IsCasting()) + { + if (combatUtil.EnteredCombat()) { - if (combatUtil.EnteredCombat()) + (bool t, double e) = wait.Until(5000, CombatLogChanged); + if (!t) { - (bool t, double e) = wait.Until(5000, CombatLogChanged); - if (!t) + if (addonReader.DamageTakenCount() > 0 && !playerReader.Bits.TargetInCombat()) { - if (addonReader.DamageTakenCount() > 0 && !playerReader.Bits.TargetInCombat()) - { - stopMoving.Stop(); + stopMoving.Stop(); - input.ClearTarget(); - wait.Update(); + input.ClearTarget(); + wait.Update(); - combatUtil.AquiredTarget(5000); - return; - } - - SendGoapEvent(new GoapStateEvent(GoapKey.pulled, true)); + combatUtil.AquiredTarget(5000); return; } - } - else if (playerReader.Bits.PlayerInCombat()) - { + SendGoapEvent(new GoapStateEvent(GoapKey.pulled, true)); return; } - - approachAction(); } - } + else if (playerReader.Bits.PlayerInCombat()) + { + SendGoapEvent(new GoapStateEvent(GoapKey.pulled, true)); + return; + } - private bool CombatLogChanged() - { - return - playerReader.Bits.TargetInCombat() || - addonReader.DamageDoneCount() > 0 || - addonReader.DamageTakenCount() > 0 || - playerReader.TargetTarget is - UnitsTarget.Me or - UnitsTarget.Pet or - UnitsTarget.PartyOrPet; + approachAction(); } + } - private void DefaultApproach() + private bool CombatLogChanged() + { + return + playerReader.Bits.TargetInCombat() || + addonReader.DamageDoneCount() > 0 || + addonReader.DamageTakenCount() > 0 || + playerReader.TargetTarget is + UnitsTarget.Me or + UnitsTarget.Pet or + UnitsTarget.PartyOrPet; + } + + private void DefaultApproach() + { + if (input.ClassConfig.Approach.GetRemainingCooldown() == 0) { - if (input.ClassConfig.Approach.GetRemainingCooldown() == 0) + if (!stuckDetector.IsMoving()) { - if (!stuckDetector.IsMoving()) - { - stuckDetector.Update(); - } - - input.Approach(); + stuckDetector.Update(); } + + input.Approach(); } + } - private void ConditionalApproach() + private void ConditionalApproach() + { + if (approachKey != null && (approachKey.CanRun() || approachKey.GetRemainingCooldown() > 0)) { - if (approachKey != null && (approachKey.CanRun() || approachKey.GetRemainingCooldown() > 0)) + if (approachKey.GetRemainingCooldown() == 0) { - if (approachKey.GetRemainingCooldown() == 0) - { - input.Approach(); - } - - if (!stuckDetector.IsMoving()) - { - stuckDetector.Update(); - } + input.Approach(); } - else + + if (!stuckDetector.IsMoving()) { - stopMoving.Stop(); + stuckDetector.Update(); } } - - private bool SuccessfulPull() + else { - return playerReader.TargetTarget is - UnitsTarget.Me or - UnitsTarget.Pet or - UnitsTarget.PartyOrPet || - addonReader.CombatLog.DamageDoneGuid.ElapsedMs() < CastingHandler.GCD || - playerReader.IsInMeleeRange(); + stopMoving.Stop(); } + } - private bool PullPrevention() - { - return targetBlacklist.Is() && - playerReader.TargetTarget is not - UnitsTarget.None or - UnitsTarget.Me or - UnitsTarget.Pet or - UnitsTarget.PartyOrPet; - } + private bool SuccessfulPull() + { + return playerReader.TargetTarget is + UnitsTarget.Me or + UnitsTarget.Pet or + UnitsTarget.PartyOrPet || + addonReader.CombatLog.DamageDoneGuid.ElapsedMs() < CastingHandler.GCD || + playerReader.IsInMeleeRange(); + } - private void Log(string text) - { - logger.LogInformation(text); - } + private bool PullPrevention() + { + return targetBlacklist.Is() && + playerReader.TargetTarget is not + UnitsTarget.None or + UnitsTarget.Me or + UnitsTarget.Pet or + UnitsTarget.PartyOrPet; + } + + private void Log(string text) + { + logger.LogInformation(text); } } \ No newline at end of file diff --git a/Core/Goals/SkinningGoal.cs b/Core/Goals/SkinningGoal.cs index b1ff13b7f..7c8fa2257 100644 --- a/Core/Goals/SkinningGoal.cs +++ b/Core/Goals/SkinningGoal.cs @@ -4,358 +4,357 @@ using System; using System.Collections.Generic; -namespace Core.Goals +namespace Core.Goals; + +public sealed class SkinningGoal : GoapGoal, IGoapEventListener, IDisposable { - public sealed class SkinningGoal : GoapGoal, IGoapEventListener, IDisposable + public override float Cost => 4.4f; + + private const int MAX_ATTEMPTS = 5; + private const int MAX_TIME_TO_REACH_MELEE = 10000; + private const int MAX_TIME_TO_DETECT_LOOT = 2 * CastingHandler.GCD; + private const int MAX_TIME_TO_DETECT_CAST = 2 * CastingHandler.GCD; + private const int MAX_TIME_TO_WAIT_NPC_NAME = 1000; + + private readonly ILogger logger; + private readonly ConfigurableInput input; + private readonly PlayerReader playerReader; + private readonly Wait wait; + private readonly StopMoving stopMoving; + private readonly BagReader bagReader; + private readonly EquipmentReader equipmentReader; + private readonly NpcNameTargeting npcNameTargeting; + private readonly CombatUtil combatUtil; + private readonly GoapAgentState state; + + private bool canRun; + private int bagHashNewOrStackGain; + + private readonly List corpses = new(); + + public SkinningGoal(ILogger logger, ConfigurableInput input, + AddonReader addonReader, Wait wait, StopMoving stopMoving, + NpcNameTargeting npcNameTargeting, CombatUtil combatUtil, + GoapAgentState state) + : base(nameof(SkinningGoal)) { - public override float Cost => 4.4f; - - private const int MAX_ATTEMPTS = 5; - private const int MAX_TIME_TO_REACH_MELEE = 10000; - private const int MAX_TIME_TO_DETECT_LOOT = 2 * CastingHandler.GCD; - private const int MAX_TIME_TO_DETECT_CAST = 2 * CastingHandler.GCD; - private const int MAX_TIME_TO_WAIT_NPC_NAME = 1000; - - private readonly ILogger logger; - private readonly ConfigurableInput input; - private readonly PlayerReader playerReader; - private readonly Wait wait; - private readonly StopMoving stopMoving; - private readonly BagReader bagReader; - private readonly EquipmentReader equipmentReader; - private readonly NpcNameTargeting npcNameTargeting; - private readonly CombatUtil combatUtil; - private readonly GoapAgentState state; - - private bool canRun; - private int bagHashNewOrStackGain; - - private readonly List corpses = new(); - - public SkinningGoal(ILogger logger, ConfigurableInput input, - AddonReader addonReader, Wait wait, StopMoving stopMoving, - NpcNameTargeting npcNameTargeting, CombatUtil combatUtil, - GoapAgentState state) - : base(nameof(SkinningGoal)) - { - this.logger = logger; - this.input = input; + this.logger = logger; + this.input = input; - this.playerReader = addonReader.PlayerReader; - this.wait = wait; - this.stopMoving = stopMoving; - this.bagReader = addonReader.BagReader; - this.equipmentReader = addonReader.EquipmentReader; + this.playerReader = addonReader.PlayerReader; + this.wait = wait; + this.stopMoving = stopMoving; + this.bagReader = addonReader.BagReader; + this.equipmentReader = addonReader.EquipmentReader; - this.npcNameTargeting = npcNameTargeting; - this.combatUtil = combatUtil; - this.state = state; + this.npcNameTargeting = npcNameTargeting; + this.combatUtil = combatUtil; + this.state = state; - canRun = HaveItemRequirement(); - bagReader.DataChanged -= BagReader_DataChanged; - bagReader.DataChanged += BagReader_DataChanged; - equipmentReader.OnEquipmentChanged -= EquipmentReader_OnEquipmentChanged; - equipmentReader.OnEquipmentChanged += EquipmentReader_OnEquipmentChanged; + canRun = HaveItemRequirement(); + bagReader.DataChanged -= BagReader_DataChanged; + bagReader.DataChanged += BagReader_DataChanged; + equipmentReader.OnEquipmentChanged -= EquipmentReader_OnEquipmentChanged; + equipmentReader.OnEquipmentChanged += EquipmentReader_OnEquipmentChanged; - //AddPrecondition(GoapKey.dangercombat, false); + //AddPrecondition(GoapKey.dangercombat, false); - AddPrecondition(GoapKey.shouldgather, true); - AddEffect(GoapKey.shouldgather, false); - } + AddPrecondition(GoapKey.shouldgather, true); + AddEffect(GoapKey.shouldgather, false); + } - public void Dispose() + public void Dispose() + { + bagReader.DataChanged -= BagReader_DataChanged; + equipmentReader.OnEquipmentChanged -= EquipmentReader_OnEquipmentChanged; + } + + public override bool CanRun() => canRun; + + public void OnGoapEvent(GoapEventArgs e) + { + if (e is SkinCorpseEvent corpseEvent) { - bagReader.DataChanged -= BagReader_DataChanged; - equipmentReader.OnEquipmentChanged -= EquipmentReader_OnEquipmentChanged; + corpses.Add(corpseEvent); } + } - public override bool CanRun() => canRun; + public override void OnEnter() + { + combatUtil.Update(); - public void OnGoapEvent(GoapEventArgs e) + (bool t, double e) = wait.Until(CastingHandler.GCD, LootReset); + if (t) { - if (e is SkinCorpseEvent corpseEvent) - { - corpses.Add(corpseEvent); - } + LogWarning($"Loot window still open! {e}ms"); + ExitInterruptOrFailed(false); + return; } - public override void OnEnter() - { - combatUtil.Update(); + bagHashNewOrStackGain = bagReader.HashNewOrStackGain; - (bool t, double e) = wait.Until(CastingHandler.GCD, LootReset); - if (t) - { - LogWarning($"Loot window still open! {e}ms"); - ExitInterruptOrFailed(false); - return; - } - - bagHashNewOrStackGain = bagReader.HashNewOrStackGain; + wait.Fixed(playerReader.NetworkLatency.Value); - wait.Fixed(playerReader.NetworkLatency.Value); + if (bagReader.BagsFull()) + { + LogWarning("Inventory is full!"); + } - if (bagReader.BagsFull()) - { - LogWarning("Inventory is full!"); - } + int attempts = 0; + while (attempts < MAX_ATTEMPTS) + { + bool foundTarget = playerReader.Bits.HasTarget() && playerReader.Bits.TargetIsDead(); - int attempts = 0; - while (attempts < MAX_ATTEMPTS) + if (!foundTarget && state.LastCombatKillCount == 1) { - bool foundTarget = playerReader.Bits.HasTarget() && playerReader.Bits.TargetIsDead(); + input.FastLastTarget(); + wait.Update(); - if (!foundTarget && state.LastCombatKillCount == 1) + if (playerReader.Bits.HasTarget()) { - input.FastLastTarget(); - wait.Update(); - - if (playerReader.Bits.HasTarget()) + if (playerReader.Bits.TargetIsDead()) { - if (playerReader.Bits.TargetIsDead()) - { - foundTarget = true; - Log("Last Target found!"); - } - else - { - Log("Last Target is alive!"); - input.ClearTarget(); - wait.Update(); - } + foundTarget = true; + Log("Last Target found!"); + } + else + { + Log("Last Target is alive!"); + input.ClearTarget(); + wait.Update(); } } + } - bool interact = false; - if (!foundTarget && !input.ClassConfig.KeyboardOnly) - { - stopMoving.Stop(); - combatUtil.Update(); + bool interact = false; + if (!foundTarget && !input.ClassConfig.KeyboardOnly) + { + stopMoving.Stop(); + combatUtil.Update(); - npcNameTargeting.ChangeNpcType(NpcNames.Corpse); - (t, e) = wait.Until(MAX_TIME_TO_WAIT_NPC_NAME, npcNameTargeting.FoundNpcName); - Log($"Found Npc Name ? {!t} | Count: {npcNameTargeting.NpcCount} {e}ms"); + npcNameTargeting.ChangeNpcType(NpcNames.Corpse); + (t, e) = wait.Until(MAX_TIME_TO_WAIT_NPC_NAME, npcNameTargeting.FoundNpcName); + Log($"Found Npc Name ? {!t} | Count: {npcNameTargeting.NpcCount} {e}ms"); - foundTarget = npcNameTargeting.FindBy(CursorType.Skin, CursorType.Mine, CursorType.Herb); // todo salvage icon - interact = true; - } + foundTarget = npcNameTargeting.FindBy(CursorType.Skin, CursorType.Mine, CursorType.Herb); // todo salvage icon + interact = true; + } - if (!foundTarget) - { - LogWarning($"Unable to gather Target({playerReader.TargetId})!"); - ExitInterruptOrFailed(false); - return; - } + if (!foundTarget) + { + LogWarning($"Unable to gather Target({playerReader.TargetId})!"); + ExitInterruptOrFailed(false); + return; + } - if (!MinRangeZero()) - { - (t, e) = wait.Until(MAX_TIME_TO_REACH_MELEE, MinRangeZero, input.ApproachOnCooldown); - Log($"Reached Target ? {!t} {e}ms"); - interact = true; - } + if (!MinRangeZero()) + { + (t, e) = wait.Until(MAX_TIME_TO_REACH_MELEE, MinRangeZero, input.ApproachOnCooldown); + Log($"Reached Target ? {!t} {e}ms"); + interact = true; + } - playerReader.LastUIError = 0; - playerReader.CastEvent.ForceUpdate(0); + playerReader.LastUIError = 0; + playerReader.CastEvent.ForceUpdate(0); - (t, e) = wait.Until(MAX_TIME_TO_DETECT_CAST, CastStartedOrFailed, interact ? Empty : WhileNotCastingInteract); + (t, e) = wait.Until(MAX_TIME_TO_DETECT_CAST, CastStartedOrFailed, interact ? Empty : WhileNotCastingInteract); - Log($"Started casting or interrupted ? {!t} - casting: {playerReader.IsCasting()} {e}ms"); - if (playerReader.LastUIError == UI_ERROR.ERR_REQUIRES_S) - { - LogWarning("Missing Spell/Item/Skill Requirement!"); - ExitInterruptOrFailed(false); - return; - } - else if ((t || playerReader.LastUIError == UI_ERROR.ERR_LOOT_LOCKED) && !playerReader.IsCasting()) - { - int delay = playerReader.LastUIError == UI_ERROR.ERR_LOOT_LOCKED - ? Loot.LOOTFRAME_AUTOLOOT_DELAY - : playerReader.NetworkLatency.Value; + Log($"Started casting or interrupted ? {!t} - casting: {playerReader.IsCasting()} {e}ms"); + if (playerReader.LastUIError == UI_ERROR.ERR_REQUIRES_S) + { + LogWarning("Missing Spell/Item/Skill Requirement!"); + ExitInterruptOrFailed(false); + return; + } + else if ((t || playerReader.LastUIError == UI_ERROR.ERR_LOOT_LOCKED) && !playerReader.IsCasting()) + { + int delay = playerReader.LastUIError == UI_ERROR.ERR_LOOT_LOCKED + ? Loot.LOOTFRAME_AUTOLOOT_DELAY + : playerReader.NetworkLatency.Value; - Log($"Wait {delay}ms and try again..."); - wait.Fixed(delay); + Log($"Wait {delay}ms and try again..."); + wait.Fixed(delay); - Log($"Try again: {playerReader.CastState.ToStringF()} | {playerReader.LastUIError.ToStringF()} | {playerReader.IsCasting()}"); - attempts++; + Log($"Try again: {playerReader.CastState.ToStringF()} | {playerReader.LastUIError.ToStringF()} | {playerReader.IsCasting()}"); + attempts++; - ClearTargetIfExists(); - continue; - } + ClearTargetIfExists(); + continue; + } - bool herbalism = Array.BinarySearch(GatherSpells.Herbalism, playerReader.SpellBeingCast) > -1; + bool herbalism = Array.BinarySearch(GatherSpells.Herbalism, playerReader.SpellBeingCast) > -1; - int remainMs = playerReader.RemainCastMs; - playerReader.LastUIError = 0; + int remainMs = playerReader.RemainCastMs; + playerReader.LastUIError = 0; - int waitTime = remainMs + playerReader.SpellQueueTimeMs + playerReader.NetworkLatency.Value; - Log($"Waiting for {(herbalism ? "Herb Gathering" : "Skinning")} castbar to end! {waitTime}ms"); + int waitTime = remainMs + playerReader.SpellQueueTimeMs + playerReader.NetworkLatency.Value; + Log($"Waiting for {(herbalism ? "Herb Gathering" : "Skinning")} castbar to end! {waitTime}ms"); - (t, e) = wait.Until(waitTime, herbalism ? HerbalismCastEnded : SkinningCastEnded); + (t, e) = wait.Until(waitTime, herbalism ? HerbalismCastEnded : SkinningCastEnded); - if (herbalism - ? t || playerReader.LastUIError != UI_ERROR.SPELL_FAILED_TRY_AGAIN - : playerReader.CastState == UI_ERROR.CAST_SUCCESS) + if (herbalism + ? t || playerReader.LastUIError != UI_ERROR.SPELL_FAILED_TRY_AGAIN + : playerReader.CastState == UI_ERROR.CAST_SUCCESS) + { + Log($"Gathering Successful!"); + ExitSuccess(); + return; + } + else + { + if (combatUtil.EnteredCombat()) { - Log($"Gathering Successful!"); - ExitSuccess(); + Log("Interrupted due combat!"); + ExitInterruptOrFailed(true); return; } - else - { - if (combatUtil.EnteredCombat()) - { - Log("Interrupted due combat!"); - ExitInterruptOrFailed(true); - return; - } - wait.Fixed(Loot.LOOTFRAME_AUTOLOOT_DELAY); - LogWarning($"Gathering Failed! {playerReader.CastState.ToStringF()} attempts: {attempts}"); - attempts++; + wait.Fixed(Loot.LOOTFRAME_AUTOLOOT_DELAY); + LogWarning($"Gathering Failed! {playerReader.CastState.ToStringF()} attempts: {attempts}"); + attempts++; - ClearTargetIfExists(); - } + ClearTargetIfExists(); } - - LogWarning($"Ran out of {attempts} maximum attempts..."); - ExitInterruptOrFailed(false); - } - - public override void OnExit() - { - npcNameTargeting.ChangeNpcType(NpcNames.None); } - private void ExitSuccess() - { - (bool t, double e) = wait.Until(MAX_TIME_TO_DETECT_LOOT, LootWindowClosedOrBagChanged); - Log($"Loot {((!t && !bagReader.BagsFull()) ? "Successful" : "Failed")} after {e}ms"); - - SendGoapEvent(new RemoveClosestPoi(SkinCorpseEvent.NAME)); - state.GatherableCorpseCount = Math.Max(0, state.GatherableCorpseCount - 1); + LogWarning($"Ran out of {attempts} maximum attempts..."); + ExitInterruptOrFailed(false); + } - ClearTargetIfExists(); - } + public override void OnExit() + { + npcNameTargeting.ChangeNpcType(NpcNames.None); + } - private void ExitInterruptOrFailed(bool interrupted) - { - if (!interrupted) - state.GatherableCorpseCount = Math.Max(0, state.GatherableCorpseCount - 1); + private void ExitSuccess() + { + (bool t, double e) = wait.Until(MAX_TIME_TO_DETECT_LOOT, LootWindowClosedOrBagChanged); + Log($"Loot {((!t && !bagReader.BagsFull()) ? "Successful" : "Failed")} after {e}ms"); - ClearTargetIfExists(); - } + SendGoapEvent(new RemoveClosestPoi(SkinCorpseEvent.NAME)); + state.GatherableCorpseCount = Math.Max(0, state.GatherableCorpseCount - 1); - private void ClearTargetIfExists() - { - if (playerReader.Bits.HasTarget() && playerReader.Bits.TargetIsDead()) - { - input.ClearTarget(); - wait.Update(); - } - } + ClearTargetIfExists(); + } - private void WhileNotCastingInteract() - { - if (!playerReader.IsCasting()) - input.ApproachOnCooldown(); - } + private void ExitInterruptOrFailed(bool interrupted) + { + if (!interrupted) + state.GatherableCorpseCount = Math.Max(0, state.GatherableCorpseCount - 1); - private static void Empty() { } + ClearTargetIfExists(); + } - private bool LootReset() + private void ClearTargetIfExists() + { + if (playerReader.Bits.HasTarget() && playerReader.Bits.TargetIsDead()) { - return (LootStatus)playerReader.LootEvent.Value == LootStatus.CORPSE; + input.ClearTarget(); + wait.Update(); } + } - private void EquipmentReader_OnEquipmentChanged(object? sender, (int, int) e) - { - canRun = HaveItemRequirement(); - } + private void WhileNotCastingInteract() + { + if (!playerReader.IsCasting()) + input.ApproachOnCooldown(); + } - private void BagReader_DataChanged() - { - canRun = HaveItemRequirement(); - } + private static void Empty() { } - private bool HaveItemRequirement() - { - if (input.ClassConfig.Herb) return true; + private bool LootReset() + { + return (LootStatus)playerReader.LootEvent.Value == LootStatus.CORPSE; + } - if (input.ClassConfig.Skin) - { - return - bagReader.HasItem(7005) || - bagReader.HasItem(12709) || - bagReader.HasItem(19901) || - bagReader.HasItem(40772) || // army knife - bagReader.HasItem(40893) || - - equipmentReader.HasItem(7005) || - equipmentReader.HasItem(12709) || - equipmentReader.HasItem(19901); - } + private void EquipmentReader_OnEquipmentChanged(object? sender, (int, int) e) + { + canRun = HaveItemRequirement(); + } - if (input.ClassConfig.Mine || input.ClassConfig.Salvage) - return - bagReader.HasItem(40772) || // army knife - // mining / todo salvage - bagReader.HasItem(40893) || - bagReader.HasItem(20723) || - bagReader.HasItem(1959) || - bagReader.HasItem(9465) || - bagReader.HasItem(1819) || - bagReader.HasItem(40892) || - bagReader.HasItem(778) || - bagReader.HasItem(1893) || - bagReader.HasItem(2901) || - bagReader.HasItem(756); - - return false; - } + private void BagReader_DataChanged() + { + canRun = HaveItemRequirement(); + } - private bool LootWindowClosedOrBagChanged() - { - return bagHashNewOrStackGain != bagReader.HashNewOrStackGain || - (LootStatus)playerReader.LootEvent.Value is - LootStatus.CLOSED; - } + private bool HaveItemRequirement() + { + if (input.ClassConfig.Herb) return true; - private bool SkinningCastEnded() + if (input.ClassConfig.Skin) { return - playerReader.CastState is - UI_ERROR.CAST_SUCCESS or - UI_ERROR.SPELL_FAILED_TRY_AGAIN; + bagReader.HasItem(7005) || + bagReader.HasItem(12709) || + bagReader.HasItem(19901) || + bagReader.HasItem(40772) || // army knife + bagReader.HasItem(40893) || + + equipmentReader.HasItem(7005) || + equipmentReader.HasItem(12709) || + equipmentReader.HasItem(19901); } - private bool HerbalismCastEnded() - { + if (input.ClassConfig.Mine || input.ClassConfig.Salvage) return - playerReader.LastUIError is - UI_ERROR.SPELL_FAILED_TRY_AGAIN; - } + bagReader.HasItem(40772) || // army knife + // mining / todo salvage + bagReader.HasItem(40893) || + bagReader.HasItem(20723) || + bagReader.HasItem(1959) || + bagReader.HasItem(9465) || + bagReader.HasItem(1819) || + bagReader.HasItem(40892) || + bagReader.HasItem(778) || + bagReader.HasItem(1893) || + bagReader.HasItem(2901) || + bagReader.HasItem(756); + + return false; + } - private bool CastStartedOrFailed() - { - return playerReader.IsCasting() || - playerReader.LastUIError is - UI_ERROR.ERR_LOOT_LOCKED or - UI_ERROR.ERR_REQUIRES_S; - } + private bool LootWindowClosedOrBagChanged() + { + return bagHashNewOrStackGain != bagReader.HashNewOrStackGain || + (LootStatus)playerReader.LootEvent.Value is + LootStatus.CLOSED; + } - private bool MinRangeZero() - { - return playerReader.MinRange() == 0; - } + private bool SkinningCastEnded() + { + return + playerReader.CastState is + UI_ERROR.CAST_SUCCESS or + UI_ERROR.SPELL_FAILED_TRY_AGAIN; + } - private void Log(string text) - { - logger.LogInformation(text); - } + private bool HerbalismCastEnded() + { + return + playerReader.LastUIError is + UI_ERROR.SPELL_FAILED_TRY_AGAIN; + } - private void LogWarning(string text) - { - logger.LogWarning(text); - } + private bool CastStartedOrFailed() + { + return playerReader.IsCasting() || + playerReader.LastUIError is + UI_ERROR.ERR_LOOT_LOCKED or + UI_ERROR.ERR_REQUIRES_S; + } + + private bool MinRangeZero() + { + return playerReader.MinRange() == 0; + } + + private void Log(string text) + { + logger.LogInformation(text); + } + + private void LogWarning(string text) + { + logger.LogWarning(text); } } diff --git a/Core/Goals/TargetFocusTargetGoal.cs b/Core/Goals/TargetFocusTargetGoal.cs index 3cb6d6e57..606b16f99 100644 --- a/Core/Goals/TargetFocusTargetGoal.cs +++ b/Core/Goals/TargetFocusTargetGoal.cs @@ -1,66 +1,65 @@ using Core.GOAP; -namespace Core.Goals -{ - public sealed class TargetFocusTargetGoal : GoapGoal - { - public override float Cost => 10f; +namespace Core.Goals; - private readonly ConfigurableInput input; - private readonly PlayerReader playerReader; - private readonly Wait wait; - - public TargetFocusTargetGoal(ConfigurableInput input, PlayerReader playerReader, Wait wait) - : base(nameof(TargetFocusTargetGoal)) - { - this.input = input; - this.playerReader = playerReader; - this.wait = wait; +public sealed class TargetFocusTargetGoal : GoapGoal +{ + public override float Cost => 10f; - if (input.ClassConfig.Loot) - { - AddPrecondition(GoapKey.incombat, false); - } + private readonly ConfigurableInput input; + private readonly PlayerReader playerReader; + private readonly Wait wait; - AddPrecondition(GoapKey.hasfocus, true); - AddPrecondition(GoapKey.focushastarget, true); - } + public TargetFocusTargetGoal(ConfigurableInput input, PlayerReader playerReader, Wait wait) + : base(nameof(TargetFocusTargetGoal)) + { + this.input = input; + this.playerReader = playerReader; + this.wait = wait; - public override void OnEnter() + if (input.ClassConfig.Loot) { - input.TargetFocus(); - wait.Update(); + AddPrecondition(GoapKey.incombat, false); } - public override void Update() + AddPrecondition(GoapKey.hasfocus, true); + AddPrecondition(GoapKey.focushastarget, true); + } + + public override void OnEnter() + { + input.TargetFocus(); + wait.Update(); + } + + public override void Update() + { + if (playerReader.Bits.FocusTargetCanBeHostile()) { - if (playerReader.Bits.FocusTargetCanBeHostile()) - { - if (playerReader.Bits.FocusTargetInCombat()) - { - input.TargetFocus(); - input.TargetOfTarget(); - wait.Update(); - } - } - else if (playerReader.SpellInRange.FocusTarget_Trade) + if (playerReader.Bits.FocusTargetInCombat()) { input.TargetFocus(); input.TargetOfTarget(); - input.Interact(); wait.Update(); } - + } + else if (playerReader.SpellInRange.FocusTarget_Trade) + { + input.TargetFocus(); + input.TargetOfTarget(); + input.Interact(); wait.Update(); } - public override void OnExit() + wait.Update(); + } + + public override void OnExit() + { + if (!playerReader.Bits.FocusHasTarget()) { - if (!playerReader.Bits.FocusHasTarget()) - { - input.ClearTarget(); - wait.Update(); - } + input.ClearTarget(); + wait.Update(); } } } diff --git a/Core/Goals/TargetLastDeadGoal.cs b/Core/Goals/TargetLastDeadGoal.cs index 690c8b554..756106285 100644 --- a/Core/Goals/TargetLastDeadGoal.cs +++ b/Core/Goals/TargetLastDeadGoal.cs @@ -1,28 +1,27 @@ using Core.GOAP; using Microsoft.Extensions.Logging; -namespace Core.Goals +namespace Core.Goals; + +public sealed class TargetLastDeadGoal : GoapGoal { - public sealed class TargetLastDeadGoal : GoapGoal - { - public override float Cost => 4.2f; + public override float Cost => 4.2f; - private readonly ILogger logger; - private readonly ConfigurableInput input; + private readonly ILogger logger; + private readonly ConfigurableInput input; - public TargetLastDeadGoal(ILogger logger, ConfigurableInput input) - : base(nameof(TargetLastDeadGoal)) - { - this.logger = logger; - this.input = input; + public TargetLastDeadGoal(ILogger logger, ConfigurableInput input) + : base(nameof(TargetLastDeadGoal)) + { + this.logger = logger; + this.input = input; - AddPrecondition(GoapKey.hastarget, false); - AddPrecondition(GoapKey.producedcorpse, true); - } + AddPrecondition(GoapKey.hastarget, false); + AddPrecondition(GoapKey.producedcorpse, true); + } - public override void Update() - { - input.LastTarget(); - } + public override void Update() + { + input.LastTarget(); } } \ No newline at end of file diff --git a/Core/Goals/TargetPetTargetGoal.cs b/Core/Goals/TargetPetTargetGoal.cs index ffd7cd5a2..7b3dcd12b 100644 --- a/Core/Goals/TargetPetTargetGoal.cs +++ b/Core/Goals/TargetPetTargetGoal.cs @@ -1,49 +1,48 @@ using Core.GOAP; -namespace Core.Goals +namespace Core.Goals; + +public sealed class TargetPetTargetGoal : GoapGoal { - public sealed class TargetPetTargetGoal : GoapGoal + public override float Cost => 4.01f; + + private readonly ConfigurableInput input; + private readonly PlayerReader playerReader; + private readonly Wait wait; + + public TargetPetTargetGoal(ConfigurableInput input, PlayerReader playerReader, Wait wait) + : base(nameof(TargetPetTargetGoal)) { - public override float Cost => 4.01f; + this.input = input; + this.playerReader = playerReader; + this.wait = wait; - private readonly ConfigurableInput input; - private readonly PlayerReader playerReader; - private readonly Wait wait; + AddPrecondition(GoapKey.targetisalive, false); - public TargetPetTargetGoal(ConfigurableInput input, PlayerReader playerReader, Wait wait) - : base(nameof(TargetPetTargetGoal)) + if (input.ClassConfig.KeyboardOnly) { - this.input = input; - this.playerReader = playerReader; - this.wait = wait; - - AddPrecondition(GoapKey.targetisalive, false); + AddPrecondition(GoapKey.consumablecorpsenearby, false); + } + else + { + AddPrecondition(GoapKey.damagetakenordone, true); + } - if (input.ClassConfig.KeyboardOnly) - { - AddPrecondition(GoapKey.consumablecorpsenearby, false); - } - else - { - AddPrecondition(GoapKey.damagetakenordone, true); - } + AddPrecondition(GoapKey.pethastarget, true); - AddPrecondition(GoapKey.pethastarget, true); + AddEffect(GoapKey.hastarget, true); + } - AddEffect(GoapKey.hastarget, true); - } + public override void Update() + { + input.TargetPet(); + input.TargetOfTarget(); + wait.Update(); - public override void Update() + if (playerReader.Bits.HasTarget() && (playerReader.Bits.TargetIsDead() || playerReader.TargetGuid == playerReader.PetGuid)) { - input.TargetPet(); - input.TargetOfTarget(); + input.ClearTarget(); wait.Update(); - - if (playerReader.Bits.HasTarget() && (playerReader.Bits.TargetIsDead() || playerReader.TargetGuid == playerReader.PetGuid)) - { - input.ClearTarget(); - wait.Update(); - } } } } diff --git a/Core/Goals/WaitForGatheringGoal.cs b/Core/Goals/WaitForGatheringGoal.cs index 541b67c35..c438c1282 100644 --- a/Core/Goals/WaitForGatheringGoal.cs +++ b/Core/Goals/WaitForGatheringGoal.cs @@ -3,191 +3,190 @@ using System.Diagnostics; using System; -namespace Core.Goals +namespace Core.Goals; + +public static class CastState_Extension { - public static class CastState_Extension + public static string ToStringF(this WaitForGatheringGoal.CastState value) => value switch { - public static string ToStringF(this WaitForGatheringGoal.CastState value) => value switch - { - WaitForGatheringGoal.CastState.None => nameof(WaitForGatheringGoal.CastState.None), - WaitForGatheringGoal.CastState.Casting => nameof(WaitForGatheringGoal.CastState.Casting), - WaitForGatheringGoal.CastState.Failed => nameof(WaitForGatheringGoal.CastState.Failed), - WaitForGatheringGoal.CastState.Abort => nameof(WaitForGatheringGoal.CastState.Abort), - WaitForGatheringGoal.CastState.Success => nameof(WaitForGatheringGoal.CastState.Success), - WaitForGatheringGoal.CastState.WaitUserInput => nameof(WaitForGatheringGoal.CastState.WaitUserInput), - _ => throw new System.NotImplementedException(), - }; - } + WaitForGatheringGoal.CastState.None => nameof(WaitForGatheringGoal.CastState.None), + WaitForGatheringGoal.CastState.Casting => nameof(WaitForGatheringGoal.CastState.Casting), + WaitForGatheringGoal.CastState.Failed => nameof(WaitForGatheringGoal.CastState.Failed), + WaitForGatheringGoal.CastState.Abort => nameof(WaitForGatheringGoal.CastState.Abort), + WaitForGatheringGoal.CastState.Success => nameof(WaitForGatheringGoal.CastState.Success), + WaitForGatheringGoal.CastState.WaitUserInput => nameof(WaitForGatheringGoal.CastState.WaitUserInput), + _ => throw new System.NotImplementedException(), + }; +} - public partial class WaitForGatheringGoal : GoapGoal - { - public override float Cost => 17; +public partial class WaitForGatheringGoal : GoapGoal +{ + public override float Cost => 17; - private const int Timeout = 5000; + private const int Timeout = 5000; - private readonly ILogger logger; - private readonly Wait wait; - private readonly PlayerReader playerReader; - private readonly StopMoving stopMoving; - private readonly Stopwatch stopWatch; + private readonly ILogger logger; + private readonly Wait wait; + private readonly PlayerReader playerReader; + private readonly StopMoving stopMoving; + private readonly Stopwatch stopWatch; - public enum CastState - { - None, - Casting, - Failed, - Abort, - Success, - WaitUserInput, - } + public enum CastState + { + None, + Casting, + Failed, + Abort, + Success, + WaitUserInput, + } - private CastState state; - private int lastKnownCast; + private CastState state; + private int lastKnownCast; - public WaitForGatheringGoal(ILogger logger, Wait wait, PlayerReader playerReader, StopMoving stopMoving) - : base(nameof(WaitForGatheringGoal)) - { - this.logger = logger; - this.wait = wait; - this.playerReader = playerReader; - this.stopMoving = stopMoving; - this.stopWatch = new(); + public WaitForGatheringGoal(ILogger logger, Wait wait, PlayerReader playerReader, StopMoving stopMoving) + : base(nameof(WaitForGatheringGoal)) + { + this.logger = logger; + this.wait = wait; + this.playerReader = playerReader; + this.stopMoving = stopMoving; + this.stopWatch = new(); - AddPrecondition(GoapKey.gathering, true); - } + AddPrecondition(GoapKey.gathering, true); + } + + public override void OnEnter() + { + stopMoving.Stop(); + wait.Update(); - public override void OnEnter() + while (playerReader.Bits.IsFalling()) { - stopMoving.Stop(); wait.Update(); - - while (playerReader.Bits.IsFalling()) - { - wait.Update(); - } - - LogOnEnter(logger); } - public override void OnExit() - { - state = CastState.None; - lastKnownCast = 0; + LogOnEnter(logger); + } - LogState(logger, state.ToStringF()); + public override void OnExit() + { + state = CastState.None; + lastKnownCast = 0; - stopWatch.Reset(); - stopWatch.Stop(); - } + LogState(logger, state.ToStringF()); - public override void Update() + stopWatch.Reset(); + stopWatch.Stop(); + } + + public override void Update() + { + switch (state) { - switch (state) - { - case CastState.None: - CheckCastStarted(false); - break; - case CastState.Casting: - if (!playerReader.IsCasting()) + case CastState.None: + CheckCastStarted(false); + break; + case CastState.Casting: + if (!playerReader.IsCasting()) + { + wait.Update(); + if (playerReader.LastUIError == UI_ERROR.ERR_SPELL_FAILED_S) + { + state = CastState.Failed; + LogFailed(logger, state.ToStringF(), Timeout); + } + else { - wait.Update(); - if (playerReader.LastUIError == UI_ERROR.ERR_SPELL_FAILED_S) + if (Array.BinarySearch(GatherSpells.Mining, lastKnownCast) < 0) { - state = CastState.Failed; - LogFailed(logger, state.ToStringF(), Timeout); + state = CastState.WaitUserInput; + LogSuccessMining(logger, CastState.Success.ToStringF(), state.ToStringF(), Timeout); + stopWatch.Restart(); + wait.Update(); } else { - if (Array.BinarySearch(GatherSpells.Mining, lastKnownCast) < 0) - { - state = CastState.WaitUserInput; - LogSuccessMining(logger, CastState.Success.ToStringF(), state.ToStringF(), Timeout); - stopWatch.Restart(); - wait.Update(); - } - else - { - state = CastState.Success; - LogState(logger, state.ToStringF()); - } + state = CastState.Success; + LogState(logger, state.ToStringF()); } } - break; - case CastState.Failed: - stopWatch.Restart(); - state = CastState.WaitUserInput; - LogFailed(logger, state.ToStringF(), Timeout); - wait.Update(); - break; - case CastState.Success: - case CastState.Abort: + } + break; + case CastState.Failed: + stopWatch.Restart(); + state = CastState.WaitUserInput; + LogFailed(logger, state.ToStringF(), Timeout); + wait.Update(); + break; + case CastState.Success: + case CastState.Abort: + SendGoapEvent(new GoapStateEvent(GoapKey.gathering, false)); + break; + case CastState.WaitUserInput: + CheckCastStarted(true); + + if (stopWatch.ElapsedMilliseconds > Timeout) + { SendGoapEvent(new GoapStateEvent(GoapKey.gathering, false)); - break; - case CastState.WaitUserInput: - CheckCastStarted(true); - - if (stopWatch.ElapsedMilliseconds > Timeout) - { - SendGoapEvent(new GoapStateEvent(GoapKey.gathering, false)); - } - break; - } - - wait.Update(); + } + break; } - private void CheckCastStarted(bool restartTimer) - { - if (playerReader.IsCasting() && - (Array.BinarySearch(GatherSpells.Herbalism, playerReader.CastSpellId.Value) >= 0 || - Array.BinarySearch(GatherSpells.Mining, playerReader.CastSpellId.Value) >= 0)) - { - lastKnownCast = playerReader.CastSpellId.Value; - state = CastState.Casting; + wait.Update(); + } - LogState(logger, state.ToStringF()); + private void CheckCastStarted(bool restartTimer) + { + if (playerReader.IsCasting() && + (Array.BinarySearch(GatherSpells.Herbalism, playerReader.CastSpellId.Value) >= 0 || + Array.BinarySearch(GatherSpells.Mining, playerReader.CastSpellId.Value) >= 0)) + { + lastKnownCast = playerReader.CastSpellId.Value; + state = CastState.Casting; - if (restartTimer) - { - stopWatch.Reset(); - stopWatch.Stop(); - } - } + LogState(logger, state.ToStringF()); - if (playerReader.Bits.IsFalling()) + if (restartTimer) { - state = CastState.Abort; - LogState(logger, state.ToStringF()); + stopWatch.Reset(); + stopWatch.Stop(); } } + if (playerReader.Bits.IsFalling()) + { + state = CastState.Abort; + LogState(logger, state.ToStringF()); + } + } - #region Logging - [LoggerMessage( - EventId = 101, - Level = LogLevel.Information, - Message = "{state}")] - static partial void LogState(ILogger logger, string state); + #region Logging - [LoggerMessage( - EventId = 102, - Level = LogLevel.Warning, - Message = "Waiting indefinitely for [Gathering cast to start] or [Press Jump to Abort]")] - static partial void LogOnEnter(ILogger logger); + [LoggerMessage( + EventId = 101, + Level = LogLevel.Information, + Message = "{state}")] + static partial void LogState(ILogger logger, string state); - [LoggerMessage( - EventId = 103, - Level = LogLevel.Error, - Message = "{state} -- Waiting(max {Timeout} ms) for [Gathering cast to start] or [Press Jump to Abort]")] - static partial void LogFailed(ILogger logger, string state, int Timeout); + [LoggerMessage( + EventId = 102, + Level = LogLevel.Warning, + Message = "Waiting indefinitely for [Gathering cast to start] or [Press Jump to Abort]")] + static partial void LogOnEnter(ILogger logger); - [LoggerMessage( - EventId = 104, - Level = LogLevel.Information, - Message = "{success} -> {state} Waiting(max {Timeout} ms) for [More Mining cast] or [Press Jump to Abort]")] - static partial void LogSuccessMining(ILogger logger, string success, string state, int Timeout); + [LoggerMessage( + EventId = 103, + Level = LogLevel.Error, + Message = "{state} -- Waiting(max {Timeout} ms) for [Gathering cast to start] or [Press Jump to Abort]")] + static partial void LogFailed(ILogger logger, string state, int Timeout); - #endregion - } + [LoggerMessage( + EventId = 104, + Level = LogLevel.Information, + Message = "{success} -> {state} Waiting(max {Timeout} ms) for [More Mining cast] or [Press Jump to Abort]")] + static partial void LogSuccessMining(ILogger logger, string success, string state, int Timeout); + + #endregion } diff --git a/Core/Goals/WaitGoal.cs b/Core/Goals/WaitGoal.cs index dcd30567c..151c88bf3 100644 --- a/Core/Goals/WaitGoal.cs +++ b/Core/Goals/WaitGoal.cs @@ -1,29 +1,28 @@ using Microsoft.Extensions.Logging; -namespace Core.Goals +namespace Core.Goals; + +public sealed class WaitGoal : GoapGoal { - public sealed class WaitGoal : GoapGoal - { - public override float Cost => 21; + public override float Cost => 21; - private readonly ILogger logger; - private readonly Wait wait; + private readonly ILogger logger; + private readonly Wait wait; - public WaitGoal(ILogger logger, Wait wait) - : base(nameof(WaitGoal)) - { - this.logger = logger; - this.wait = wait; - } + public WaitGoal(ILogger logger, Wait wait) + : base(nameof(WaitGoal)) + { + this.logger = logger; + this.wait = wait; + } - public override void OnEnter() - { - logger.LogInformation("Waiting"); - } + public override void OnEnter() + { + logger.LogInformation("Waiting"); + } - public override void Update() - { - wait.Update(); - } + public override void Update() + { + wait.Update(); } } \ No newline at end of file diff --git a/Core/Goals/WalkToCorpseGoal.cs b/Core/Goals/WalkToCorpseGoal.cs index 17fea0451..644f3c7ee 100644 --- a/Core/Goals/WalkToCorpseGoal.cs +++ b/Core/Goals/WalkToCorpseGoal.cs @@ -4,133 +4,132 @@ using System.Collections.Generic; using System.Numerics; -namespace Core.Goals +namespace Core.Goals; + +public sealed partial class WalkToCorpseGoal : GoapGoal, IGoapEventListener, IRouteProvider, IDisposable { - public sealed partial class WalkToCorpseGoal : GoapGoal, IGoapEventListener, IRouteProvider, IDisposable - { - public override float Cost => 1f; + public override float Cost => 1f; - private readonly ILogger logger; - private readonly Wait wait; - private readonly ConfigurableInput input; + private readonly ILogger logger; + private readonly Wait wait; + private readonly ConfigurableInput input; - private readonly AddonReader addonReader; - private readonly PlayerReader playerReader; - private readonly Navigation navigation; - private readonly StopMoving stopMoving; + private readonly AddonReader addonReader; + private readonly PlayerReader playerReader; + private readonly Navigation navigation; + private readonly StopMoving stopMoving; - public List Deaths { get; } = new(); + public List Deaths { get; } = new(); - private DateTime onEnterTime; + private DateTime onEnterTime; - #region IRouteProvider + #region IRouteProvider - public DateTime LastActive => navigation.LastActive; + public DateTime LastActive => navigation.LastActive; - public Vector3[] PathingRoute() - { - return navigation.TotalRoute; - } + public Vector3[] PathingRoute() + { + return navigation.TotalRoute; + } - public bool HasNext() - { - return navigation.HasNext(); - } + public bool HasNext() + { + return navigation.HasNext(); + } - public Vector3 NextMapPoint() - { - return navigation.NextMapPoint(); - } + public Vector3 NextMapPoint() + { + return navigation.NextMapPoint(); + } - #endregion + #endregion - public WalkToCorpseGoal(ILogger logger, ConfigurableInput input, Wait wait, AddonReader addonReader, Navigation navigation, StopMoving stopMoving) - : base(nameof(WalkToCorpseGoal)) - { - this.logger = logger; - this.wait = wait; - this.input = input; + public WalkToCorpseGoal(ILogger logger, ConfigurableInput input, Wait wait, AddonReader addonReader, Navigation navigation, StopMoving stopMoving) + : base(nameof(WalkToCorpseGoal)) + { + this.logger = logger; + this.wait = wait; + this.input = input; - this.addonReader = addonReader; - this.playerReader = addonReader.PlayerReader; - this.stopMoving = stopMoving; + this.addonReader = addonReader; + this.playerReader = addonReader.PlayerReader; + this.stopMoving = stopMoving; - this.navigation = navigation; + this.navigation = navigation; - AddPrecondition(GoapKey.isdead, true); - } + AddPrecondition(GoapKey.isdead, true); + } - public void Dispose() - { - navigation.Dispose(); - } + public void Dispose() + { + navigation.Dispose(); + } - public void OnGoapEvent(GoapEventArgs e) + public void OnGoapEvent(GoapEventArgs e) + { + if (e.GetType() == typeof(ResumeEvent)) { - if (e.GetType() == typeof(ResumeEvent)) - { - navigation.ResetStuckParameters(); - } + navigation.ResetStuckParameters(); } + } - public override void OnEnter() - { - playerReader.WorldPosZ = 0; - addonReader.PlayerDied(); + public override void OnEnter() + { + playerReader.WorldPosZ = 0; + addonReader.PlayerDied(); - wait.While(AliveOrLoadingScreen); - Log($"Player teleported to the graveyard!"); + wait.While(AliveOrLoadingScreen); + Log($"Player teleported to the graveyard!"); - Vector3 corpseLocation = playerReader.CorpseMapPos; - Log($"Corpse location is {corpseLocation}"); + Vector3 corpseLocation = playerReader.CorpseMapPos; + Log($"Corpse location is {corpseLocation}"); - Deaths.Add(corpseLocation); + Deaths.Add(corpseLocation); - navigation.SetWayPoints(stackalloc Vector3[] { corpseLocation }); + navigation.SetWayPoints(stackalloc Vector3[] { corpseLocation }); - onEnterTime = DateTime.UtcNow; - } + onEnterTime = DateTime.UtcNow; + } - public override void OnExit() - { - navigation.StopMovement(); - navigation.Stop(); - } + public override void OnExit() + { + navigation.StopMovement(); + navigation.Stop(); + } - public override void Update() + public override void Update() + { + if (!playerReader.Bits.CorpseInRange()) { - if (!playerReader.Bits.CorpseInRange()) - { - navigation.Update(); - } - else - { - stopMoving.Stop(); - navigation.ResetStuckParameters(); - } - - RandomJump(); - - wait.Update(); + navigation.Update(); } - - private void RandomJump() + else { - if ((DateTime.UtcNow - onEnterTime).TotalSeconds > 5 && input.ClassConfig.Jump.SinceLastClickMs > Random.Shared.Next(10_000, 25_000)) - { - Log("Random jump"); - input.Jump(); - } + stopMoving.Stop(); + navigation.ResetStuckParameters(); } - private bool AliveOrLoadingScreen() - { - return playerReader.CorpseMapPos == Vector3.Zero; - } + RandomJump(); + + wait.Update(); + } - private void Log(string text) + private void RandomJump() + { + if ((DateTime.UtcNow - onEnterTime).TotalSeconds > 5 && input.ClassConfig.Jump.SinceLastClickMs > Random.Shared.Next(10_000, 25_000)) { - logger.LogInformation($"[{nameof(WalkToCorpseGoal)}]: {text}"); + Log("Random jump"); + input.Jump(); } } + + private bool AliveOrLoadingScreen() + { + return playerReader.CorpseMapPos == Vector3.Zero; + } + + private void Log(string text) + { + logger.LogInformation($"[{nameof(WalkToCorpseGoal)}]: {text}"); + } } \ No newline at end of file diff --git a/Core/Goals/WrongZoneGoal.cs b/Core/Goals/WrongZoneGoal.cs index 9b249e655..10681a528 100644 --- a/Core/Goals/WrongZoneGoal.cs +++ b/Core/Goals/WrongZoneGoal.cs @@ -9,97 +9,96 @@ using static System.MathF; -namespace Core.Goals +namespace Core.Goals; + +public sealed class WrongZoneGoal : GoapGoal { - public sealed class WrongZoneGoal : GoapGoal - { - public override float Cost => 19f; + public override float Cost => 19f; + + private const float RADIAN = MathF.PI * 2; + + private readonly ILogger logger; + private readonly ConfigurableInput input; + private readonly PlayerReader playerReader; + private readonly PlayerDirection playerDirection; + private readonly StuckDetector stuckDetector; + private readonly ClassConfiguration classConfiguration; - private const float RADIAN = MathF.PI * 2; + private float lastDistance = 999; - private readonly ILogger logger; - private readonly ConfigurableInput input; - private readonly PlayerReader playerReader; - private readonly PlayerDirection playerDirection; - private readonly StuckDetector stuckDetector; - private readonly ClassConfiguration classConfiguration; + public DateTime LastActive { get; private set; } - private float lastDistance = 999; + public WrongZoneGoal(PlayerReader playerReader, ConfigurableInput input, PlayerDirection playerDirection, ILogger logger, StuckDetector stuckDetector, ClassConfiguration classConfiguration) + : base(nameof(WrongZoneGoal)) + { + this.playerReader = playerReader; + this.input = input; + this.playerDirection = playerDirection; + this.logger = logger; + this.stuckDetector = stuckDetector; + this.classConfiguration = classConfiguration; + + AddPrecondition(GoapKey.incombat, false); + } - public DateTime LastActive { get; private set; } + public override bool CanRun() + { + return playerReader.UIMapId.Value == classConfiguration.WrongZone.ZoneId; + } - public WrongZoneGoal(PlayerReader playerReader, ConfigurableInput input, PlayerDirection playerDirection, ILogger logger, StuckDetector stuckDetector, ClassConfiguration classConfiguration) - : base(nameof(WrongZoneGoal)) + public override void Update() + { + Vector3 exitMap = classConfiguration.WrongZone.ExitZoneLocation; + + input.Proc.SetKeyState(input.Proc.ForwardKey, true); + + if ((DateTime.UtcNow - LastActive).TotalMilliseconds > 10000) { - this.playerReader = playerReader; - this.input = input; - this.playerDirection = playerDirection; - this.logger = logger; - this.stuckDetector = stuckDetector; - this.classConfiguration = classConfiguration; - - AddPrecondition(GoapKey.incombat, false); + stuckDetector.SetTargetLocation(exitMap); } - public override bool CanRun() + Vector3 playerMap = playerReader.MapPos; + float mapDistance = playerMap.MapDistanceXYTo(exitMap); + float heading = DirectionCalculator.CalculateMapHeading(playerMap, exitMap); + + if (lastDistance < mapDistance) { - return playerReader.UIMapId.Value == classConfiguration.WrongZone.ZoneId; + logger.LogInformation("Further away"); + playerDirection.SetDirection(heading, exitMap); } - - public override void Update() + else if (!stuckDetector.IsGettingCloser()) { - Vector3 exitMap = classConfiguration.WrongZone.ExitZoneLocation; - + // stuck so jump input.Proc.SetKeyState(input.Proc.ForwardKey, true); - if ((DateTime.UtcNow - LastActive).TotalMilliseconds > 10000) + if (HasBeenActiveRecently()) { - stuckDetector.SetTargetLocation(exitMap); + stuckDetector.Update(); } - - Vector3 playerMap = playerReader.MapPos; - float mapDistance = playerMap.MapDistanceXYTo(exitMap); - float heading = DirectionCalculator.CalculateMapHeading(playerMap, exitMap); - - if (lastDistance < mapDistance) - { - logger.LogInformation("Further away"); - playerDirection.SetDirection(heading, exitMap); - } - else if (!stuckDetector.IsGettingCloser()) + else { - // stuck so jump - input.Proc.SetKeyState(input.Proc.ForwardKey, true); - - if (HasBeenActiveRecently()) - { - stuckDetector.Update(); - } - else - { - logger.LogInformation("Resuming movement"); - } + logger.LogInformation("Resuming movement"); } - else // distance closer + } + else // distance closer + { + float diff1 = Abs(RADIAN + heading - playerReader.Direction) % RADIAN; + float diff2 = Abs(heading - playerReader.Direction - RADIAN) % RADIAN; + + if (Min(diff1, diff2) > 0.3) { - float diff1 = Abs(RADIAN + heading - playerReader.Direction) % RADIAN; - float diff2 = Abs(heading - playerReader.Direction - RADIAN) % RADIAN; - - if (Min(diff1, diff2) > 0.3) - { - logger.LogInformation("Correcting direction"); - playerDirection.SetDirection(heading, exitMap); - } + logger.LogInformation("Correcting direction"); + playerDirection.SetDirection(heading, exitMap); } + } - lastDistance = mapDistance; + lastDistance = mapDistance; - LastActive = DateTime.UtcNow; - } + LastActive = DateTime.UtcNow; + } - private bool HasBeenActiveRecently() - { - return (DateTime.UtcNow - LastActive).TotalMilliseconds < 2000; - } + private bool HasBeenActiveRecently() + { + return (DateTime.UtcNow - LastActive).TotalMilliseconds < 2000; } } \ No newline at end of file diff --git a/Core/GoalsComponent/Blacklist/IBlacklist.cs b/Core/GoalsComponent/Blacklist/IBlacklist.cs index 96b19a359..e31d6d716 100644 --- a/Core/GoalsComponent/Blacklist/IBlacklist.cs +++ b/Core/GoalsComponent/Blacklist/IBlacklist.cs @@ -1,7 +1,6 @@ -namespace Core +namespace Core; + +public interface IBlacklist { - public interface IBlacklist - { - bool Is(); - } + bool Is(); } \ No newline at end of file diff --git a/Core/GoalsComponent/Blacklist/MouseOverBlacklist.cs b/Core/GoalsComponent/Blacklist/MouseOverBlacklist.cs index 03cbc9f22..b2a0e5fef 100644 --- a/Core/GoalsComponent/Blacklist/MouseOverBlacklist.cs +++ b/Core/GoalsComponent/Blacklist/MouseOverBlacklist.cs @@ -4,203 +4,202 @@ using SharedLib.Extensions; -namespace Core +namespace Core; + +public sealed partial class MouseOverBlacklist : IBlacklist { - public sealed partial class MouseOverBlacklist : IBlacklist - { - private readonly string[] blacklist; + private readonly string[] blacklist; - private readonly AddonReader addonReader; - private readonly PlayerReader playerReader; - private readonly ILogger logger; - private readonly int above; - private readonly int below; - private readonly bool checkMouseOverGivesExp; - private readonly UnitClassification mask; + private readonly AddonReader addonReader; + private readonly PlayerReader playerReader; + private readonly ILogger logger; + private readonly int above; + private readonly int below; + private readonly bool checkMouseOverGivesExp; + private readonly UnitClassification mask; - private readonly bool allowPvP; + private readonly bool allowPvP; - private int lastGuid; + private int lastGuid; - public MouseOverBlacklist(ILogger logger, AddonReader addonReader, ClassConfiguration classConfig) - { - this.addonReader = addonReader; - playerReader = addonReader.PlayerReader; - this.logger = logger; - this.above = classConfig.NPCMaxLevels_Above; - this.below = classConfig.NPCMaxLevels_Below; + public MouseOverBlacklist(ILogger logger, AddonReader addonReader, ClassConfiguration classConfig) + { + this.addonReader = addonReader; + playerReader = addonReader.PlayerReader; + this.logger = logger; + this.above = classConfig.NPCMaxLevels_Above; + this.below = classConfig.NPCMaxLevels_Below; + + this.checkMouseOverGivesExp = classConfig.CheckTargetGivesExp; + this.mask = classConfig.TargetMask; - this.checkMouseOverGivesExp = classConfig.CheckTargetGivesExp; - this.mask = classConfig.TargetMask; + this.blacklist = classConfig.Blacklist; - this.blacklist = classConfig.Blacklist; + this.allowPvP = classConfig.AllowPvP; - this.allowPvP = classConfig.AllowPvP; + logger.LogInformation($"[{nameof(MouseOverBlacklist)}] {nameof(classConfig.TargetMask)}: {string.Join(", ", mask.GetIndividualFlags())}"); - logger.LogInformation($"[{nameof(MouseOverBlacklist)}] {nameof(classConfig.TargetMask)}: {string.Join(", ", mask.GetIndividualFlags())}"); + if (blacklist.Length > 0) + logger.LogInformation($"[{nameof(MouseOverBlacklist)}] Name: {string.Join(", ", blacklist)}"); + } - if (blacklist.Length > 0) - logger.LogInformation($"[{nameof(MouseOverBlacklist)}] Name: {string.Join(", ", blacklist)}"); + public bool Is() + { + if (!playerReader.Bits.HasMouseOver()) + { + lastGuid = 0; + return false; + } + else if (addonReader.CombatLog.DamageTaken.Contains(playerReader.MouseOverGuid)) + { + return false; } - public bool Is() + if (playerReader.PetHasTarget() && playerReader.MouseOverGuid == playerReader.PetGuid) { - if (!playerReader.Bits.HasMouseOver()) - { - lastGuid = 0; - return false; - } - else if (addonReader.CombatLog.DamageTaken.Contains(playerReader.MouseOverGuid)) - { - return false; - } + return true; + } - if (playerReader.PetHasTarget() && playerReader.MouseOverGuid == playerReader.PetGuid) - { - return true; - } + // it is trying to kill me + if (playerReader.Bits.MouseOverTargetIsPlayerOrPet()) + { + return false; + } - // it is trying to kill me - if (playerReader.Bits.MouseOverTargetIsPlayerOrPet()) + if (!mask.HasFlagF(playerReader.MouseOverClassification)) + { + if (lastGuid != playerReader.MouseOverGuid) { - return false; + LogClassification(logger, playerReader.MouseOverId, playerReader.MouseOverGuid, addonReader.MouseOverName, playerReader.MouseOverClassification.ToStringF()); + lastGuid = playerReader.MouseOverGuid; } - if (!mask.HasFlagF(playerReader.MouseOverClassification)) - { - if (lastGuid != playerReader.MouseOverGuid) - { - LogClassification(logger, playerReader.MouseOverId, playerReader.MouseOverGuid, addonReader.MouseOverName, playerReader.MouseOverClassification.ToStringF()); - lastGuid = playerReader.MouseOverGuid; - } - - return true; // ignore non white listed unit classification - } + return true; // ignore non white listed unit classification + } - if (!allowPvP && (playerReader.Bits.MouseOverIsPlayer() || playerReader.Bits.MouseOverPlayerControlled())) + if (!allowPvP && (playerReader.Bits.MouseOverIsPlayer() || playerReader.Bits.MouseOverPlayerControlled())) + { + if (lastGuid != playerReader.MouseOverGuid) { - if (lastGuid != playerReader.MouseOverGuid) - { - LogPlayerOrPet(logger, playerReader.MouseOverId, playerReader.MouseOverGuid, addonReader.MouseOverName); - lastGuid = playerReader.MouseOverGuid; - } - - return true; // ignore players and pets + LogPlayerOrPet(logger, playerReader.MouseOverId, playerReader.MouseOverGuid, addonReader.MouseOverName); + lastGuid = playerReader.MouseOverGuid; } - if (!playerReader.Bits.MouseOverIsDead() && playerReader.Bits.MouseOverIsTagged()) - { - if (lastGuid != playerReader.MouseOverGuid) - { - LogTagged(logger, playerReader.MouseOverId, playerReader.MouseOverGuid, addonReader.MouseOverName); - lastGuid = playerReader.MouseOverGuid; - } + return true; // ignore players and pets + } - return true; // ignore tagged mobs + if (!playerReader.Bits.MouseOverIsDead() && playerReader.Bits.MouseOverIsTagged()) + { + if (lastGuid != playerReader.MouseOverGuid) + { + LogTagged(logger, playerReader.MouseOverId, playerReader.MouseOverGuid, addonReader.MouseOverName); + lastGuid = playerReader.MouseOverGuid; } + return true; // ignore tagged mobs + } - if (playerReader.Bits.MouseOverCanBeHostile() && playerReader.MouseOverLevel > playerReader.Level.Value + above) - { - if (lastGuid != playerReader.MouseOverGuid) - { - LogLevelHigh(logger, playerReader.MouseOverId, playerReader.MouseOverGuid, addonReader.MouseOverName); - lastGuid = playerReader.MouseOverGuid; - } - - return true; // ignore if current level + 2 - } - if (checkMouseOverGivesExp) - { - if (playerReader.Bits.MouseOverIsTrivial()) - { - if (lastGuid != playerReader.MouseOverGuid) - { - LogNoExperienceGain(logger, playerReader.MouseOverId, playerReader.MouseOverGuid, addonReader.MouseOverName); - lastGuid = playerReader.MouseOverGuid; - } - return true; - } - } - else if (playerReader.Bits.MouseOverCanBeHostile() && playerReader.MouseOverLevel < playerReader.Level.Value - below) + if (playerReader.Bits.MouseOverCanBeHostile() && playerReader.MouseOverLevel > playerReader.Level.Value + above) + { + if (lastGuid != playerReader.MouseOverGuid) { - if (lastGuid != playerReader.MouseOverGuid) - { - LogLevelLow(logger, playerReader.MouseOverId, playerReader.MouseOverGuid, addonReader.MouseOverName); - lastGuid = playerReader.MouseOverGuid; - } - return true; // ignore if current level - 7 + LogLevelHigh(logger, playerReader.MouseOverId, playerReader.MouseOverGuid, addonReader.MouseOverName); + lastGuid = playerReader.MouseOverGuid; } - if (blacklist.Length > 0 && Contains()) + return true; // ignore if current level + 2 + } + + if (checkMouseOverGivesExp) + { + if (playerReader.Bits.MouseOverIsTrivial()) { if (lastGuid != playerReader.MouseOverGuid) { - LogNameMatch(logger, playerReader.MouseOverId, playerReader.MouseOverGuid, addonReader.MouseOverName); + LogNoExperienceGain(logger, playerReader.MouseOverId, playerReader.MouseOverGuid, addonReader.MouseOverName); lastGuid = playerReader.MouseOverGuid; } return true; } - - return false; + } + else if (playerReader.Bits.MouseOverCanBeHostile() && playerReader.MouseOverLevel < playerReader.Level.Value - below) + { + if (lastGuid != playerReader.MouseOverGuid) + { + LogLevelLow(logger, playerReader.MouseOverId, playerReader.MouseOverGuid, addonReader.MouseOverName); + lastGuid = playerReader.MouseOverGuid; + } + return true; // ignore if current level - 7 } - private bool Contains() + if (blacklist.Length > 0 && Contains()) { - for (int i = 0; i < blacklist.Length; i++) + if (lastGuid != playerReader.MouseOverGuid) { - if (addonReader.MouseOverName.Contains(blacklist[i], StringComparison.OrdinalIgnoreCase)) - return true; + LogNameMatch(logger, playerReader.MouseOverId, playerReader.MouseOverGuid, addonReader.MouseOverName); + lastGuid = playerReader.MouseOverGuid; } + return true; + } - return false; + return false; + } + + private bool Contains() + { + for (int i = 0; i < blacklist.Length; i++) + { + if (addonReader.MouseOverName.Contains(blacklist[i], StringComparison.OrdinalIgnoreCase)) + return true; } - #region logging - - [LoggerMessage( - EventId = 60, - Level = LogLevel.Warning, - Message = "MouseOverBlacklist ({id},{guid},{name}) is player!")] - static partial void LogPlayerOrPet(ILogger logger, int id, int guid, string name); - - [LoggerMessage( - EventId = 61, - Level = LogLevel.Warning, - Message = "MouseOverBlacklist ({id},{guid},{name}) is tagged!")] - static partial void LogTagged(ILogger logger, int id, int guid, string name); - - [LoggerMessage( - EventId = 62, - Level = LogLevel.Warning, - Message = "MouseOverBlacklist ({id},{guid},{name}) too high level!")] - static partial void LogLevelHigh(ILogger logger, int id, int guid, string name); - - [LoggerMessage( - EventId = 63, - Level = LogLevel.Warning, - Message = "MouseOverBlacklist ({id},{guid},{name}) too low level!")] - static partial void LogLevelLow(ILogger logger, int id, int guid, string name); - - [LoggerMessage( - EventId = 64, - Level = LogLevel.Warning, - Message = "MouseOverBlacklist ({id},{guid},{name}) not yield experience!")] - static partial void LogNoExperienceGain(ILogger logger, int id, int guid, string name); - - [LoggerMessage( - EventId = 65, - Level = LogLevel.Warning, - Message = "MouseOverBlacklist ({id},{guid},{name}) name match!")] - static partial void LogNameMatch(ILogger logger, int id, int guid, string name); - - [LoggerMessage( - EventId = 66, - Level = LogLevel.Warning, - Message = "MouseOverBlacklist ({id},{guid},{name},{classification}) not defined in the TargetMask!")] - static partial void LogClassification(ILogger logger, int id, int guid, string name, string classification); - - #endregion + return false; } + + #region logging + + [LoggerMessage( + EventId = 60, + Level = LogLevel.Warning, + Message = "MouseOverBlacklist ({id},{guid},{name}) is player!")] + static partial void LogPlayerOrPet(ILogger logger, int id, int guid, string name); + + [LoggerMessage( + EventId = 61, + Level = LogLevel.Warning, + Message = "MouseOverBlacklist ({id},{guid},{name}) is tagged!")] + static partial void LogTagged(ILogger logger, int id, int guid, string name); + + [LoggerMessage( + EventId = 62, + Level = LogLevel.Warning, + Message = "MouseOverBlacklist ({id},{guid},{name}) too high level!")] + static partial void LogLevelHigh(ILogger logger, int id, int guid, string name); + + [LoggerMessage( + EventId = 63, + Level = LogLevel.Warning, + Message = "MouseOverBlacklist ({id},{guid},{name}) too low level!")] + static partial void LogLevelLow(ILogger logger, int id, int guid, string name); + + [LoggerMessage( + EventId = 64, + Level = LogLevel.Warning, + Message = "MouseOverBlacklist ({id},{guid},{name}) not yield experience!")] + static partial void LogNoExperienceGain(ILogger logger, int id, int guid, string name); + + [LoggerMessage( + EventId = 65, + Level = LogLevel.Warning, + Message = "MouseOverBlacklist ({id},{guid},{name}) name match!")] + static partial void LogNameMatch(ILogger logger, int id, int guid, string name); + + [LoggerMessage( + EventId = 66, + Level = LogLevel.Warning, + Message = "MouseOverBlacklist ({id},{guid},{name},{classification}) not defined in the TargetMask!")] + static partial void LogClassification(ILogger logger, int id, int guid, string name, string classification); + + #endregion } \ No newline at end of file diff --git a/Core/GoalsComponent/Blacklist/NoBlacklist.cs b/Core/GoalsComponent/Blacklist/NoBlacklist.cs index 8329b133f..8189b99a6 100644 --- a/Core/GoalsComponent/Blacklist/NoBlacklist.cs +++ b/Core/GoalsComponent/Blacklist/NoBlacklist.cs @@ -1,7 +1,6 @@ -namespace Core +namespace Core; + +public sealed class NoBlacklist : IBlacklist { - public sealed class NoBlacklist : IBlacklist - { - public bool Is() => false; - } + public bool Is() => false; } \ No newline at end of file diff --git a/Core/GoalsComponent/Blacklist/TargetBlacklist.cs b/Core/GoalsComponent/Blacklist/TargetBlacklist.cs index fff03e08c..454486cb2 100644 --- a/Core/GoalsComponent/Blacklist/TargetBlacklist.cs +++ b/Core/GoalsComponent/Blacklist/TargetBlacklist.cs @@ -5,235 +5,234 @@ using SharedLib.Extensions; using System.Collections.Generic; -namespace Core +namespace Core; + +public sealed partial class TargetBlacklist : IBlacklist, IDisposable { - public sealed partial class TargetBlacklist : IBlacklist, IDisposable - { - private readonly string[] blacklist; + private readonly string[] blacklist; - private readonly AddonReader addonReader; - private readonly PlayerReader playerReader; - private readonly ILogger logger; - private readonly int above; - private readonly int below; - private readonly bool checkTargetGivesExp; - private readonly UnitClassification targetMask; + private readonly AddonReader addonReader; + private readonly PlayerReader playerReader; + private readonly ILogger logger; + private readonly int above; + private readonly int below; + private readonly bool checkTargetGivesExp; + private readonly UnitClassification targetMask; - private readonly bool allowPvP; + private readonly bool allowPvP; - private int lastGuid; - private readonly HashSet evadeMobs; + private int lastGuid; + private readonly HashSet evadeMobs; - public TargetBlacklist(ILogger logger, AddonReader addonReader, ClassConfiguration classConfig) - { - this.addonReader = addonReader; - playerReader = addonReader.PlayerReader; - this.logger = logger; - this.above = classConfig.NPCMaxLevels_Above; - this.below = classConfig.NPCMaxLevels_Below; + public TargetBlacklist(ILogger logger, AddonReader addonReader, ClassConfiguration classConfig) + { + this.addonReader = addonReader; + playerReader = addonReader.PlayerReader; + this.logger = logger; + this.above = classConfig.NPCMaxLevels_Above; + this.below = classConfig.NPCMaxLevels_Below; + + this.checkTargetGivesExp = classConfig.CheckTargetGivesExp; + this.targetMask = classConfig.TargetMask; + + this.blacklist = classConfig.Blacklist; - this.checkTargetGivesExp = classConfig.CheckTargetGivesExp; - this.targetMask = classConfig.TargetMask; + this.allowPvP = classConfig.AllowPvP; - this.blacklist = classConfig.Blacklist; + evadeMobs = new HashSet(); - this.allowPvP = classConfig.AllowPvP; + addonReader.CombatLog.TargetEvade += CombatLog_TargetEvade; - evadeMobs = new HashSet(); + logger.LogInformation($"[{nameof(TargetBlacklist)}] {nameof(classConfig.TargetMask)}: {string.Join(", ", targetMask.GetIndividualFlags())}"); - addonReader.CombatLog.TargetEvade += CombatLog_TargetEvade; + if (blacklist.Length > 0) + logger.LogInformation($"[{nameof(TargetBlacklist)}] Name: {string.Join(", ", blacklist)}"); + } - logger.LogInformation($"[{nameof(TargetBlacklist)}] {nameof(classConfig.TargetMask)}: {string.Join(", ", targetMask.GetIndividualFlags())}"); + public void Dispose() + { + addonReader.CombatLog.TargetEvade -= CombatLog_TargetEvade; + } - if (blacklist.Length > 0) - logger.LogInformation($"[{nameof(TargetBlacklist)}] Name: {string.Join(", ", blacklist)}"); + public bool Is() + { + if (!playerReader.Bits.HasTarget()) + { + lastGuid = 0; + return false; + } + else if (addonReader.CombatLog.DamageTaken.Contains(playerReader.TargetGuid)) + { + return false; } - public void Dispose() + if (playerReader.PetHasTarget() && playerReader.TargetGuid == playerReader.PetGuid) { - addonReader.CombatLog.TargetEvade -= CombatLog_TargetEvade; + return true; } - public bool Is() + if (evadeMobs.Contains(playerReader.TargetGuid)) { - if (!playerReader.Bits.HasTarget()) + if (lastGuid != playerReader.TargetGuid) { - lastGuid = 0; - return false; - } - else if (addonReader.CombatLog.DamageTaken.Contains(playerReader.TargetGuid)) - { - return false; + LogEvade(logger, playerReader.TargetId, playerReader.TargetGuid, addonReader.TargetName, playerReader.TargetClassification.ToStringF()); + lastGuid = playerReader.TargetGuid; } + return true; + } - if (playerReader.PetHasTarget() && playerReader.TargetGuid == playerReader.PetGuid) - { - return true; - } + // it is trying to kill me + if (playerReader.Bits.TargetOfTargetIsPlayerOrPet()) + { + return false; + } - if (evadeMobs.Contains(playerReader.TargetGuid)) + if (!targetMask.HasFlagF(playerReader.TargetClassification)) + { + if (lastGuid != playerReader.TargetGuid) { - if (lastGuid != playerReader.TargetGuid) - { - LogEvade(logger, playerReader.TargetId, playerReader.TargetGuid, addonReader.TargetName, playerReader.TargetClassification.ToStringF()); - lastGuid = playerReader.TargetGuid; - } - return true; + LogClassification(logger, playerReader.TargetId, playerReader.TargetGuid, addonReader.TargetName, playerReader.TargetClassification.ToStringF()); + lastGuid = playerReader.TargetGuid; } - // it is trying to kill me - if (playerReader.Bits.TargetOfTargetIsPlayerOrPet()) - { - return false; - } + return true; // ignore non white listed unit classification + } - if (!targetMask.HasFlagF(playerReader.TargetClassification)) + if (!allowPvP && (playerReader.Bits.TargetIsPlayer() || playerReader.Bits.TargetIsPlayerControlled())) + { + if (lastGuid != playerReader.TargetGuid) { - if (lastGuid != playerReader.TargetGuid) - { - LogClassification(logger, playerReader.TargetId, playerReader.TargetGuid, addonReader.TargetName, playerReader.TargetClassification.ToStringF()); - lastGuid = playerReader.TargetGuid; - } - - return true; // ignore non white listed unit classification + LogPlayerOrPet(logger, playerReader.TargetId, playerReader.TargetGuid, addonReader.TargetName); + lastGuid = playerReader.TargetGuid; } - if (!allowPvP && (playerReader.Bits.TargetIsPlayer() || playerReader.Bits.TargetIsPlayerControlled())) - { - if (lastGuid != playerReader.TargetGuid) - { - LogPlayerOrPet(logger, playerReader.TargetId, playerReader.TargetGuid, addonReader.TargetName); - lastGuid = playerReader.TargetGuid; - } - - return true; // ignore players and pets - } + return true; // ignore players and pets + } - if (!playerReader.Bits.TargetIsDead() && playerReader.Bits.TargetIsTagged()) + if (!playerReader.Bits.TargetIsDead() && playerReader.Bits.TargetIsTagged()) + { + if (lastGuid != playerReader.TargetGuid) { - if (lastGuid != playerReader.TargetGuid) - { - LogTagged(logger, playerReader.TargetId, playerReader.TargetGuid, addonReader.TargetName); - lastGuid = playerReader.TargetGuid; - } - - return true; // ignore tagged mobs + LogTagged(logger, playerReader.TargetId, playerReader.TargetGuid, addonReader.TargetName); + lastGuid = playerReader.TargetGuid; } + return true; // ignore tagged mobs + } - if (playerReader.Bits.TargetCanBeHostile() && playerReader.TargetLevel > playerReader.Level.Value + above) - { - if (lastGuid != playerReader.TargetGuid) - { - LogLevelHigh(logger, playerReader.TargetId, playerReader.TargetGuid, addonReader.TargetName); - lastGuid = playerReader.TargetGuid; - } - - return true; // ignore if current level + 2 - } - if (checkTargetGivesExp) - { - if (playerReader.Bits.TargetIsTrivial()) - { - if (lastGuid != playerReader.TargetGuid) - { - LogNoExperienceGain(logger, playerReader.TargetId, playerReader.TargetGuid, addonReader.TargetName); - lastGuid = playerReader.TargetGuid; - } - return true; - } - } - else if (playerReader.Bits.TargetCanBeHostile() && playerReader.TargetLevel < playerReader.Level.Value - below) + if (playerReader.Bits.TargetCanBeHostile() && playerReader.TargetLevel > playerReader.Level.Value + above) + { + if (lastGuid != playerReader.TargetGuid) { - if (lastGuid != playerReader.TargetGuid) - { - LogLevelLow(logger, playerReader.TargetId, playerReader.TargetGuid, addonReader.TargetName); - lastGuid = playerReader.TargetGuid; - } - return true; // ignore if current level - 7 + LogLevelHigh(logger, playerReader.TargetId, playerReader.TargetGuid, addonReader.TargetName); + lastGuid = playerReader.TargetGuid; } - if (blacklist.Length > 0 && Contains()) + return true; // ignore if current level + 2 + } + + if (checkTargetGivesExp) + { + if (playerReader.Bits.TargetIsTrivial()) { if (lastGuid != playerReader.TargetGuid) { - LogNameMatch(logger, playerReader.TargetId, playerReader.TargetGuid, addonReader.TargetName); + LogNoExperienceGain(logger, playerReader.TargetId, playerReader.TargetGuid, addonReader.TargetName); lastGuid = playerReader.TargetGuid; } return true; } - - return false; } - - private bool Contains() + else if (playerReader.Bits.TargetCanBeHostile() && playerReader.TargetLevel < playerReader.Level.Value - below) { - for (int i = 0; i < blacklist.Length; i++) + if (lastGuid != playerReader.TargetGuid) { - if (addonReader.TargetName.Contains(blacklist[i], StringComparison.OrdinalIgnoreCase)) - return true; + LogLevelLow(logger, playerReader.TargetId, playerReader.TargetGuid, addonReader.TargetName); + lastGuid = playerReader.TargetGuid; } + return true; // ignore if current level - 7 + } - return false; + if (blacklist.Length > 0 && Contains()) + { + if (lastGuid != playerReader.TargetGuid) + { + LogNameMatch(logger, playerReader.TargetId, playerReader.TargetGuid, addonReader.TargetName); + lastGuid = playerReader.TargetGuid; + } + return true; } - private void CombatLog_TargetEvade() + return false; + } + + private bool Contains() + { + for (int i = 0; i < blacklist.Length; i++) { - if (playerReader.TargetGuid != 0) - evadeMobs.Add(playerReader.TargetGuid); + if (addonReader.TargetName.Contains(blacklist[i], StringComparison.OrdinalIgnoreCase)) + return true; } - #region logging - - [LoggerMessage( - EventId = 60, - Level = LogLevel.Warning, - Message = "Blacklist ({id},{guid},{name}) is player or pet!")] - static partial void LogPlayerOrPet(ILogger logger, int id, int guid, string name); - - [LoggerMessage( - EventId = 61, - Level = LogLevel.Warning, - Message = "Blacklist ({id},{guid},{name}) is tagged!")] - static partial void LogTagged(ILogger logger, int id, int guid, string name); - - [LoggerMessage( - EventId = 62, - Level = LogLevel.Warning, - Message = "Blacklist ({id},{guid},{name}) too high level!")] - static partial void LogLevelHigh(ILogger logger, int id, int guid, string name); - - [LoggerMessage( - EventId = 63, - Level = LogLevel.Warning, - Message = "Blacklist ({id},{guid},{name}) too low level!")] - static partial void LogLevelLow(ILogger logger, int id, int guid, string name); - - [LoggerMessage( - EventId = 64, - Level = LogLevel.Warning, - Message = "Blacklist ({id},{guid},{name}) not yield experience!")] - static partial void LogNoExperienceGain(ILogger logger, int id, int guid, string name); - - [LoggerMessage( - EventId = 65, - Level = LogLevel.Warning, - Message = "Blacklist ({id},{guid},{name}) name match!")] - static partial void LogNameMatch(ILogger logger, int id, int guid, string name); - - [LoggerMessage( - EventId = 66, - Level = LogLevel.Warning, - Message = "Blacklist ({id},{guid},{name},{classification}) not defined in the TargetMask!")] - static partial void LogClassification(ILogger logger, int id, int guid, string name, string classification); - - [LoggerMessage( - EventId = 67, - Level = LogLevel.Warning, - Message = "Blacklist ({id},{guid},{name},{classification}) evade on attack!")] - static partial void LogEvade(ILogger logger, int id, int guid, string name, string classification); - - #endregion + return false; } + + private void CombatLog_TargetEvade() + { + if (playerReader.TargetGuid != 0) + evadeMobs.Add(playerReader.TargetGuid); + } + + #region logging + + [LoggerMessage( + EventId = 60, + Level = LogLevel.Warning, + Message = "Blacklist ({id},{guid},{name}) is player or pet!")] + static partial void LogPlayerOrPet(ILogger logger, int id, int guid, string name); + + [LoggerMessage( + EventId = 61, + Level = LogLevel.Warning, + Message = "Blacklist ({id},{guid},{name}) is tagged!")] + static partial void LogTagged(ILogger logger, int id, int guid, string name); + + [LoggerMessage( + EventId = 62, + Level = LogLevel.Warning, + Message = "Blacklist ({id},{guid},{name}) too high level!")] + static partial void LogLevelHigh(ILogger logger, int id, int guid, string name); + + [LoggerMessage( + EventId = 63, + Level = LogLevel.Warning, + Message = "Blacklist ({id},{guid},{name}) too low level!")] + static partial void LogLevelLow(ILogger logger, int id, int guid, string name); + + [LoggerMessage( + EventId = 64, + Level = LogLevel.Warning, + Message = "Blacklist ({id},{guid},{name}) not yield experience!")] + static partial void LogNoExperienceGain(ILogger logger, int id, int guid, string name); + + [LoggerMessage( + EventId = 65, + Level = LogLevel.Warning, + Message = "Blacklist ({id},{guid},{name}) name match!")] + static partial void LogNameMatch(ILogger logger, int id, int guid, string name); + + [LoggerMessage( + EventId = 66, + Level = LogLevel.Warning, + Message = "Blacklist ({id},{guid},{name},{classification}) not defined in the TargetMask!")] + static partial void LogClassification(ILogger logger, int id, int guid, string name, string classification); + + [LoggerMessage( + EventId = 67, + Level = LogLevel.Warning, + Message = "Blacklist ({id},{guid},{name},{classification}) evade on attack!")] + static partial void LogEvade(ILogger logger, int id, int guid, string name, string classification); + + #endregion } \ No newline at end of file diff --git a/Core/GoalsComponent/CastingHandler.cs b/Core/GoalsComponent/CastingHandler.cs index 21e8928e6..cbdfe78c7 100644 --- a/Core/GoalsComponent/CastingHandler.cs +++ b/Core/GoalsComponent/CastingHandler.cs @@ -3,719 +3,718 @@ using System; using System.Threading; -namespace Core.Goals +namespace Core.Goals; + +public sealed partial class CastingHandler { - public sealed partial class CastingHandler - { - private const bool Log = true; + private const bool Log = true; - public const int GCD = 1500; - public const int MIN_GCD = 1000; - public const int SPELL_QUEUE = 400; + public const int GCD = 1500; + public const int MIN_GCD = 1000; + public const int SPELL_QUEUE = 400; - private const int MAX_WAIT_MELEE_RANGE = 10_000; + private const int MAX_WAIT_MELEE_RANGE = 10_000; - private readonly ILogger logger; - private readonly ConfigurableInput input; + private readonly ILogger logger; + private readonly ConfigurableInput input; - private readonly Wait wait; - private readonly AddonReader addonReader; - private readonly PlayerReader playerReader; + private readonly Wait wait; + private readonly AddonReader addonReader; + private readonly PlayerReader playerReader; - private readonly ClassConfiguration classConfig; - private readonly PlayerDirection direction; - private readonly StopMoving stopMoving; + private readonly ClassConfiguration classConfig; + private readonly PlayerDirection direction; + private readonly StopMoving stopMoving; - private readonly ReactCastError react; + private readonly ReactCastError react; - private readonly CastingHandlerInterruptWatchdog interruptWatchdog; + private readonly CastingHandlerInterruptWatchdog interruptWatchdog; - public DateTime SpellQueueOpen { get; private set; } - public bool SpellInQueue() => DateTime.UtcNow < SpellQueueOpen; + public DateTime SpellQueueOpen { get; private set; } + public bool SpellInQueue() => DateTime.UtcNow < SpellQueueOpen; - public static int _GCD() => GCD; + public static int _GCD() => GCD; - public CastingHandler(ILogger logger, ConfigurableInput input, - ClassConfiguration classConfiguration, - Wait wait, AddonReader addonReader, PlayerDirection direction, - StopMoving stopMoving, ReactCastError react, - CastingHandlerInterruptWatchdog interruptWatchdog) - { - this.logger = logger; - this.input = input; + public CastingHandler(ILogger logger, ConfigurableInput input, + ClassConfiguration classConfiguration, + Wait wait, AddonReader addonReader, PlayerDirection direction, + StopMoving stopMoving, ReactCastError react, + CastingHandlerInterruptWatchdog interruptWatchdog) + { + this.logger = logger; + this.input = input; - this.wait = wait; - this.addonReader = addonReader; - this.playerReader = addonReader.PlayerReader; + this.wait = wait; + this.addonReader = addonReader; + this.playerReader = addonReader.PlayerReader; - this.classConfig = input.ClassConfig; - this.direction = direction; - this.stopMoving = stopMoving; + this.classConfig = input.ClassConfig; + this.direction = direction; + this.stopMoving = stopMoving; - this.react = react; + this.react = react; - if (!classConfiguration.SpellQueue) - playerReader.SpellQueueTimeMs = 0; + if (!classConfiguration.SpellQueue) + playerReader.SpellQueueTimeMs = 0; - this.interruptWatchdog = interruptWatchdog; - } + this.interruptWatchdog = interruptWatchdog; + } + + private int PressKeyAction(KeyAction item, CancellationToken token) + { + playerReader.LastUIError = UI_ERROR.NONE; - private int PressKeyAction(KeyAction item, CancellationToken token) + if (item.AfterCastWaitSwing) { - playerReader.LastUIError = UI_ERROR.NONE; + if (Log && item.Log) + LogAfterCastWaitSwing(logger, item.Name); - if (item.AfterCastWaitSwing) - { - if (Log && item.Log) - LogAfterCastWaitSwing(logger, item.Name); + input.StopAttack(); + } - input.StopAttack(); - } + DateTime start = DateTime.UtcNow; + input.Proc.KeyPressSleep(item.ConsoleKey, item.PressDuration, token); + return (int)(DateTime.UtcNow - start).TotalMilliseconds; + } - DateTime start = DateTime.UtcNow; - input.Proc.KeyPressSleep(item.ConsoleKey, item.PressDuration, token); - return (int)(DateTime.UtcNow - start).TotalMilliseconds; - } + private static bool CastSuccessful(int uiEvent) + { + return + (UI_ERROR)uiEvent is + UI_ERROR.CAST_START or + UI_ERROR.CAST_SUCCESS or + UI_ERROR.NONE or + UI_ERROR.SPELL_FAILED_TARGETS_DEAD; + } - private static bool CastSuccessful(int uiEvent) + private bool CastInstant(KeyAction item, CancellationToken token) + { + if (!playerReader.IsCasting() && item.BeforeCastStop) { - return - (UI_ERROR)uiEvent is - UI_ERROR.CAST_START or - UI_ERROR.CAST_SUCCESS or - UI_ERROR.NONE or - UI_ERROR.SPELL_FAILED_TARGETS_DEAD; + stopMoving.Stop(); + wait.Update(); } - private bool CastInstant(KeyAction item, CancellationToken token) + playerReader.CastEvent.ForceUpdate(0); + int beforeCastEventValue = playerReader.CastEvent.Value; + int beforeSpellId = playerReader.CastSpellId.Value; + bool beforeUsable = addonReader.UsableAction.Is(item); + int beforePT = playerReader.PTCurrent(); + + int pressMs = PressKeyAction(item, token); + if (item.BaseAction) { - if (!playerReader.IsCasting() && item.BeforeCastStop) - { - stopMoving.Stop(); - wait.Update(); - } + item.SetClicked(); - playerReader.CastEvent.ForceUpdate(0); - int beforeCastEventValue = playerReader.CastEvent.Value; - int beforeSpellId = playerReader.CastSpellId.Value; - bool beforeUsable = addonReader.UsableAction.Is(item); - int beforePT = playerReader.PTCurrent(); + if (Log && item.Log) + LogInstantInput(logger, item.Name, pressMs, true, pressMs); - int pressMs = PressKeyAction(item, token); - if (item.BaseAction) - { - item.SetClicked(); + return true; + } - if (Log && item.Log) - LogInstantInput(logger, item.Name, pressMs, true, pressMs); + bool t; + double e; - return true; - } + if (item.AfterCastWaitSwing) + { + (t, e) = wait.Until(playerReader.MainHandSpeedMs() + playerReader.NetworkLatency.Value, + interrupt: () => !addonReader.CurrentAction.Is(item) || + playerReader.MainHandSwing.ElapsedMs() < playerReader.SpellQueueTimeMs, // swing timer reset from any miss + repeat: input.ApproachOnCooldown); + } + else if (item.Item) + { + (t, e) = wait.Until(SPELL_QUEUE + playerReader.NetworkLatency.Value, + interrupt: () => + beforeUsable != addonReader.UsableAction.Is(item) || + addonReader.CurrentAction.Is(item)); + } + else + { + (t, e) = wait.Until(SPELL_QUEUE + playerReader.NetworkLatency.Value, + interrupt: () => + (beforeSpellId != playerReader.CastSpellId.Value && + beforeCastEventValue != playerReader.CastEvent.Value) || + beforePT != playerReader.PTCurrent()); + } - bool t; - double e; + if (Log && item.Log) + LogInstantInput(logger, item.Name, pressMs, !t, e); - if (item.AfterCastWaitSwing) - { - (t, e) = wait.Until(playerReader.MainHandSpeedMs() + playerReader.NetworkLatency.Value, - interrupt: () => !addonReader.CurrentAction.Is(item) || - playerReader.MainHandSwing.ElapsedMs() < playerReader.SpellQueueTimeMs, // swing timer reset from any miss - repeat: input.ApproachOnCooldown); - } - else if (item.Item) - { - (t, e) = wait.Until(SPELL_QUEUE + playerReader.NetworkLatency.Value, - interrupt: () => - beforeUsable != addonReader.UsableAction.Is(item) || - addonReader.CurrentAction.Is(item)); - } - else - { - (t, e) = wait.Until(SPELL_QUEUE + playerReader.NetworkLatency.Value, - interrupt: () => - (beforeSpellId != playerReader.CastSpellId.Value && - beforeCastEventValue != playerReader.CastEvent.Value) || - beforePT != playerReader.PTCurrent()); - } + if (t) + return false; - if (Log && item.Log) - LogInstantInput(logger, item.Name, pressMs, !t, e); + if (Log && item.Log) + LogInstantUsableChange(logger, item.Name, beforeUsable, addonReader.UsableAction.Is(item), ((UI_ERROR)beforeCastEventValue).ToStringF(), ((UI_ERROR)playerReader.CastEvent.Value).ToStringF()); - if (t) - return false; + if (!CastSuccessful(playerReader.CastEvent.Value)) + { + react.Do(item, $"{item.Name}-{nameof(CastingHandler)}: {nameof(CastInstant)}"); + return false; + } - if (Log && item.Log) - LogInstantUsableChange(logger, item.Name, beforeUsable, addonReader.UsableAction.Is(item), ((UI_ERROR)beforeCastEventValue).ToStringF(), ((UI_ERROR)playerReader.CastEvent.Value).ToStringF()); + item.SetClicked(); + if (item.Item) + { + playerReader.LastCastGCD = 0; + wait.Update(); + } + else + playerReader.ReadLastCastGCD(); - if (!CastSuccessful(playerReader.CastEvent.Value)) - { - react.Do(item, $"{item.Name}-{nameof(CastingHandler)}: {nameof(CastInstant)}"); - return false; - } + return true; + } - item.SetClicked(); - if (item.Item) - { - playerReader.LastCastGCD = 0; - wait.Update(); - } - else - playerReader.ReadLastCastGCD(); + private bool CastCastbar(KeyAction item, CancellationToken token) + { + wait.While(playerReader.Bits.IsFalling); - return true; + if (!playerReader.IsCasting()) + { + stopMoving.Stop(); + wait.Update(); } - private bool CastCastbar(KeyAction item, CancellationToken token) - { - wait.While(playerReader.Bits.IsFalling); + bool beforeUsable = addonReader.UsableAction.Is(item); + int beforeCastEventValue = playerReader.CastEvent.Value; + int beforeSpellId = playerReader.CastSpellId.Value; + int beforeCastCount = playerReader.CastCount; - if (!playerReader.IsCasting()) - { - stopMoving.Stop(); - wait.Update(); - } + int pressMs = PressKeyAction(item, token); - bool beforeUsable = addonReader.UsableAction.Is(item); - int beforeCastEventValue = playerReader.CastEvent.Value; - int beforeSpellId = playerReader.CastSpellId.Value; - int beforeCastCount = playerReader.CastCount; + if (item.BaseAction) + { + if (Log && item.Log) + LogCastbarSkipValidation(logger, item.Name); - int pressMs = PressKeyAction(item, token); + item.SetClicked(); + return true; + } - if (item.BaseAction) - { - if (Log && item.Log) - LogCastbarSkipValidation(logger, item.Name); + (bool t, double e) = wait.Until(SPELL_QUEUE + playerReader.NetworkLatency.Value, + interrupt: () => + beforeCastEventValue != playerReader.CastEvent.Value || + beforeSpellId != playerReader.CastSpellId.Value || + beforeCastCount != playerReader.CastCount || + token.IsCancellationRequested + ); - item.SetClicked(); - return true; - } + if (Log && item.Log) + LogCastbarInput(logger, item.Name, pressMs, !t, e); - (bool t, double e) = wait.Until(SPELL_QUEUE + playerReader.NetworkLatency.Value, - interrupt: () => - beforeCastEventValue != playerReader.CastEvent.Value || - beforeSpellId != playerReader.CastSpellId.Value || - beforeCastCount != playerReader.CastCount || - token.IsCancellationRequested - ); + if (t) + return false; - if (Log && item.Log) - LogCastbarInput(logger, item.Name, pressMs, !t, e); + if (Log && item.Log) + LogCastbarUsableChange(logger, item.Name, playerReader.IsCasting(), playerReader.CastCount, beforeUsable, addonReader.UsableAction.Is(item), ((UI_ERROR)beforeCastEventValue).ToStringF(), ((UI_ERROR)playerReader.CastEvent.Value).ToStringF()); - if (t) - return false; + if (!CastSuccessful(playerReader.CastEvent.Value)) + { + react.Do(item, $"{item.Name}-{nameof(CastingHandler)}: {nameof(CastCastbar)}"); + return false; + } - if (Log && item.Log) - LogCastbarUsableChange(logger, item.Name, playerReader.IsCasting(), playerReader.CastCount, beforeUsable, addonReader.UsableAction.Is(item), ((UI_ERROR)beforeCastEventValue).ToStringF(), ((UI_ERROR)playerReader.CastEvent.Value).ToStringF()); + playerReader.ReadLastCastGCD(); + item.SetClicked(); - if (!CastSuccessful(playerReader.CastEvent.Value)) + if (item.AfterCastWaitCastbar) + { + if (playerReader.IsCasting()) { - react.Do(item, $"{item.Name}-{nameof(CastingHandler)}: {nameof(CastCastbar)}"); - return false; - } - - playerReader.ReadLastCastGCD(); - item.SetClicked(); + int remainMs = playerReader.RemainCastMs - playerReader.SpellQueueTimeMs; + if (Log && item.Log) + LogVisibleAfterCastWaitCastbar(logger, item.Name, remainMs); - if (item.AfterCastWaitCastbar) - { - if (playerReader.IsCasting()) + wait.Until(remainMs, () => !playerReader.IsCasting() || token.IsCancellationRequested, RepeatPetAttack); + if (token.IsCancellationRequested) { - int remainMs = playerReader.RemainCastMs - playerReader.SpellQueueTimeMs; if (Log && item.Log) - LogVisibleAfterCastWaitCastbar(logger, item.Name, remainMs); + LogVisibleAfterCastWaitCastbarInterrupted(logger, item.Name); - wait.Until(remainMs, () => !playerReader.IsCasting() || token.IsCancellationRequested, RepeatPetAttack); - if (token.IsCancellationRequested) - { - if (Log && item.Log) - LogVisibleAfterCastWaitCastbarInterrupted(logger, item.Name); - - return false; - } + return false; } - else if ((UI_ERROR)playerReader.CastEvent.Value == UI_ERROR.CAST_START) - { - beforeCastEventValue = playerReader.CastEvent.Value; + } + else if ((UI_ERROR)playerReader.CastEvent.Value == UI_ERROR.CAST_START) + { + beforeCastEventValue = playerReader.CastEvent.Value; - int remainMs = playerReader.RemainCastMs - playerReader.SpellQueueTimeMs; - if (Log && item.Log) - LogHiddenAfterCastWaitCastbar(logger, item.Name, remainMs); + int remainMs = playerReader.RemainCastMs - playerReader.SpellQueueTimeMs; + if (Log && item.Log) + LogHiddenAfterCastWaitCastbar(logger, item.Name, remainMs); - wait.Until(remainMs, () => beforeCastEventValue != playerReader.CastEvent.Value || token.IsCancellationRequested, RepeatPetAttack); - if (token.IsCancellationRequested) - { - if (Log && item.Log) - LogHiddenAfterCastWaitCastbarInterrupted(logger, item.Name); + wait.Until(remainMs, () => beforeCastEventValue != playerReader.CastEvent.Value || token.IsCancellationRequested, RepeatPetAttack); + if (token.IsCancellationRequested) + { + if (Log && item.Log) + LogHiddenAfterCastWaitCastbarInterrupted(logger, item.Name); - return false; - } + return false; } } - - return true; - } - - public bool CastIfReady(KeyAction item, Func interrupt) - { - return item.CanRun() && Cast(item, interrupt); } - public bool Cast(KeyAction item, Func interrupt) - { - using CancellationTokenSource cts = new(); - interruptWatchdog.Set(interrupt, cts); + return true; + } - bool t = false; - double e = 0; + public bool CastIfReady(KeyAction item, Func interrupt) + { + return item.CanRun() && Cast(item, interrupt); + } - if (item.HasFormRequirement && playerReader.Form != item.FormEnum) - { - bool beforeUsable = addonReader.UsableAction.Is(item); - Form beforeForm = playerReader.Form; + public bool Cast(KeyAction item, Func interrupt) + { + using CancellationTokenSource cts = new(); + interruptWatchdog.Set(interrupt, cts); - if (!SwitchForm(item)) - { - interruptWatchdog.Reset(); - return false; - } + bool t = false; + double e = 0; - if (!WaitForGCD(item, false, cts.Token)) - { - interruptWatchdog.Reset(); - return false; - } + if (item.HasFormRequirement && playerReader.Form != item.FormEnum) + { + bool beforeUsable = addonReader.UsableAction.Is(item); + Form beforeForm = playerReader.Form; - //TODO: upon form change and GCD - have to check Usable state - if (!beforeUsable && !addonReader.UsableAction.Is(item)) - { - if (Log && item.Log) - LogAfterFormSwitchNotUsable(logger, item.Name, beforeForm.ToStringF(), playerReader.Form.ToStringF()); + if (!SwitchForm(item)) + { + interruptWatchdog.Reset(); + return false; + } - interruptWatchdog.Reset(); - return false; - } + if (!WaitForGCD(item, false, cts.Token)) + { + interruptWatchdog.Reset(); + return false; } - if (playerReader.Bits.SpellOn_Shoot()) + //TODO: upon form change and GCD - have to check Usable state + if (!beforeUsable && !addonReader.UsableAction.Is(item)) { - input.StopAttack(); - input.StopAttack(); + if (Log && item.Log) + LogAfterFormSwitchNotUsable(logger, item.Name, beforeForm.ToStringF(), playerReader.Form.ToStringF()); - int waitTime = Math.Max(playerReader.GCD.Value, playerReader.RemainCastMs) + (2 * playerReader.NetworkLatency.Value); - (t, e) = wait.Until(waitTime, cts.Token); - logger.LogInformation($"Stop {nameof(playerReader.Bits.SpellOn_Shoot)} and wait {waitTime}ms | {e}ms"); - if (!t) - { - interruptWatchdog.Reset(); - return false; - } + interruptWatchdog.Reset(); + return false; } + } + + if (playerReader.Bits.SpellOn_Shoot()) + { + input.StopAttack(); + input.StopAttack(); - if (!item.BaseAction && !WaitForGCD(item, true, cts.Token)) + int waitTime = Math.Max(playerReader.GCD.Value, playerReader.RemainCastMs) + (2 * playerReader.NetworkLatency.Value); + (t, e) = wait.Until(waitTime, cts.Token); + logger.LogInformation($"Stop {nameof(playerReader.Bits.SpellOn_Shoot)} and wait {waitTime}ms | {e}ms"); + if (!t) { interruptWatchdog.Reset(); return false; } + } + + if (!item.BaseAction && !WaitForGCD(item, true, cts.Token)) + { + interruptWatchdog.Reset(); + return false; + } - if (item.BeforeCastDelay > 0) + if (item.BeforeCastDelay > 0) + { + if (!playerReader.IsCasting() && (item.BeforeCastStop || item.HasCastBar)) { - if (!playerReader.IsCasting() && (item.BeforeCastStop || item.HasCastBar)) - { - stopMoving.Stop(); - wait.Update(); - } + stopMoving.Stop(); + wait.Update(); + } - if (Log && item.Log) - LogBeforeCastDelay(logger, item.Name, item.BeforeCastDelay); + if (Log && item.Log) + LogBeforeCastDelay(logger, item.Name, item.BeforeCastDelay); - wait.Until(item.BeforeCastDelay, cts.Token); - } + wait.Until(item.BeforeCastDelay, cts.Token); + } - int auraHash = playerReader.AuraCount.Hash; - int bagHash = addonReader.BagReader.Hash; + int auraHash = playerReader.AuraCount.Hash; + int bagHash = addonReader.BagReader.Hash; - if (!item.HasCastBar) + if (!item.HasCastBar) + { + if (!CastInstant(item, cts.Token)) { - if (!CastInstant(item, cts.Token)) + // try again after reacted to UI_ERROR + if (cts.Token.IsCancellationRequested || !CastInstant(item, cts.Token)) { - // try again after reacted to UI_ERROR - if (cts.Token.IsCancellationRequested || !CastInstant(item, cts.Token)) - { - interruptWatchdog.Reset(); - return false; - } + interruptWatchdog.Reset(); + return false; } } - else + } + else + { + if (!CastCastbar(item, cts.Token)) { - if (!CastCastbar(item, cts.Token)) + // try again after reacted to UI_ERROR + if (cts.Token.IsCancellationRequested || !CastCastbar(item, cts.Token)) { - // try again after reacted to UI_ERROR - if (cts.Token.IsCancellationRequested || !CastCastbar(item, cts.Token)) - { - interruptWatchdog.Reset(); - return false; - } + interruptWatchdog.Reset(); + return false; } } + } - if (item.AfterCastWaitBuff) - { - int totalTime = Math.Max(playerReader.GCD.Value, playerReader.RemainCastMs) + playerReader.SpellQueueTimeMs; - - (t, e) = wait.Until(totalTime, () => - auraHash != playerReader.AuraCount.Hash || - (MissType)addonReader.CombatLog.TargetMissType.Value != MissType.NONE || - cts.Token.IsCancellationRequested - ); - - if (Log && item.Log) - LogAfterCastWaitBuff(logger, item.Name, !t, playerReader.AuraCount.ToString(), ((MissType)addonReader.CombatLog.TargetMissType.Value).ToStringF(), e); - } - - if (item.AfterCastAuraExpected) - { - wait.Update(); - - int delay = Math.Max(playerReader.RemainCastMs, item.Item ? 0 : playerReader.LastCastGCD) + playerReader.SpellQueueTimeMs; - - if (Log && item.Log) - LogAfterCastAuraExpected(logger, item.Name, delay); + if (item.AfterCastWaitBuff) + { + int totalTime = Math.Max(playerReader.GCD.Value, playerReader.RemainCastMs) + playerReader.SpellQueueTimeMs; - item.SetClicked(delay); - } + (t, e) = wait.Until(totalTime, () => + auraHash != playerReader.AuraCount.Hash || + (MissType)addonReader.CombatLog.TargetMissType.Value != MissType.NONE || + cts.Token.IsCancellationRequested + ); - if (item.AfterCastWaitBag) - { - int totalTime = Math.Max(playerReader.GCD.Value, playerReader.RemainCastMs) + playerReader.SpellQueueTimeMs; + if (Log && item.Log) + LogAfterCastWaitBuff(logger, item.Name, !t, playerReader.AuraCount.ToString(), ((MissType)addonReader.CombatLog.TargetMissType.Value).ToStringF(), e); + } - (t, e) = wait.Until(totalTime, - () => bagHash != addonReader.BagReader.Hash || cts.Token.IsCancellationRequested); - if (Log && item.Log) - LogAfterCastWaitBag(logger, item.Name, !t, e); - } + if (item.AfterCastAuraExpected) + { + wait.Update(); - if (item.AfterCastWaitCombat) - { - (t, e) = wait.Until(2 * GCD, - () => playerReader.Bits.PlayerInCombat() || cts.Token.IsCancellationRequested); + int delay = Math.Max(playerReader.RemainCastMs, item.Item ? 0 : playerReader.LastCastGCD) + playerReader.SpellQueueTimeMs; - if (Log && item.Log) - LogAfterCastWaitCombat(logger, item.Name, !t, e); - } + if (Log && item.Log) + LogAfterCastAuraExpected(logger, item.Name, delay); - if (item.AfterCastWaitMeleeRange) - { - int lastKnownHealth = playerReader.HealthCurrent(); + item.SetClicked(delay); + } - if (Log && item.Log) - LogAfterCastWaitMeleeRange(logger, item.Name); - - wait.Until(MAX_WAIT_MELEE_RANGE, - () => - playerReader.IsInMeleeRange() || - playerReader.IsTargetCasting() || - playerReader.HealthCurrent() < lastKnownHealth || - !playerReader.WithInPullRange() || - cts.Token.IsCancellationRequested - ); - } + if (item.AfterCastWaitBag) + { + int totalTime = Math.Max(playerReader.GCD.Value, playerReader.RemainCastMs) + playerReader.SpellQueueTimeMs; - if (item.AfterCastStepBack != 0) - { - if (Log && item.Log) - LogAfterCastStepBack(logger, item.Name, item.AfterCastStepBack); + (t, e) = wait.Until(totalTime, + () => bagHash != addonReader.BagReader.Hash || cts.Token.IsCancellationRequested); + if (Log && item.Log) + LogAfterCastWaitBag(logger, item.Name, !t, e); + } - input.Proc.SetKeyState(input.Proc.BackwardKey, true); + if (item.AfterCastWaitCombat) + { + (t, e) = wait.Until(2 * GCD, + () => playerReader.Bits.PlayerInCombat() || cts.Token.IsCancellationRequested); - if (Random.Shared.Next(3) == 0) // 33 % - input.Jump(); + if (Log && item.Log) + LogAfterCastWaitCombat(logger, item.Name, !t, e); + } - if (item.AfterCastStepBack == -1) - { - int waitAmount = playerReader.GCD.Value; - if (waitAmount == 0) - { - waitAmount = MIN_GCD - playerReader.SpellQueueTimeMs; - } - (t, e) = wait.Until(waitAmount, cts.Token); - } - else - { - (t, e) = wait.Until(item.AfterCastStepBack, cts.Token); - } + if (item.AfterCastWaitMeleeRange) + { + int lastKnownHealth = playerReader.HealthCurrent(); - if (Log && item.Log) - LogAfterCastStepBackInterrupted(logger, item.Name, !t, e); + if (Log && item.Log) + LogAfterCastWaitMeleeRange(logger, item.Name); + + wait.Until(MAX_WAIT_MELEE_RANGE, + () => + playerReader.IsInMeleeRange() || + playerReader.IsTargetCasting() || + playerReader.HealthCurrent() < lastKnownHealth || + !playerReader.WithInPullRange() || + cts.Token.IsCancellationRequested + ); + } - input.Proc.SetKeyState(input.Proc.BackwardKey, false); - } + if (item.AfterCastStepBack != 0) + { + if (Log && item.Log) + LogAfterCastStepBack(logger, item.Name, item.AfterCastStepBack); - if (item.AfterCastWaitGCD) - { - if (Log && item.Log) - LogAfterCastWaitGCD(logger, item.Name, playerReader.GCD.Value); + input.Proc.SetKeyState(input.Proc.BackwardKey, true); - wait.Until(playerReader.GCD.Value, cts.Token); - } + if (Random.Shared.Next(3) == 0) // 33 % + input.Jump(); - if (item.AfterCastDelay > 0) + if (item.AfterCastStepBack == -1) { - if (Log && item.Log) - LogAfterCastDelay(logger, item.Name, item.AfterCastDelay); - - (t, e) = wait.Until(item.AfterCastDelay, cts.Token); - if (Log && item.Log && !t) + int waitAmount = playerReader.GCD.Value; + if (waitAmount == 0) { - LogAfterCastDelayInterrupted(logger, item.Name, e); + waitAmount = MIN_GCD - playerReader.SpellQueueTimeMs; } + (t, e) = wait.Until(waitAmount, cts.Token); } - - if (!item.BaseAction) + else { - int durationMs = UpdateGCD(false); - if (Log && item.Log) - LogWaitForGCD(logger, item.Name, playerReader.GCD.Value, playerReader.RemainCastMs, durationMs); + (t, e) = wait.Until(item.AfterCastStepBack, cts.Token); } - item.ConsumeCharge(); + if (Log && item.Log) + LogAfterCastStepBackInterrupted(logger, item.Name, !t, e); - interruptWatchdog.Reset(); - return true; + input.Proc.SetKeyState(input.Proc.BackwardKey, false); } - private bool WaitForGCD(KeyAction item, bool spellQueue, CancellationToken token) //Func interrupt + if (item.AfterCastWaitGCD) { - int duration = Math.Max(playerReader.GCD.Value, playerReader.RemainCastMs); - - if (spellQueue) - duration -= (playerReader.SpellQueueTimeMs - playerReader.NetworkLatency.Value + playerReader.NetworkLatency.Value); //+ playerReader.NetworkLatency.Value - - if (duration <= 0) - return true; - - (bool t, double e) = wait.Until(duration, token); - if (Log && item.Log) - LogGCD(logger, item.Name, !t, addonReader.UsableAction.Is(item), duration, e); + LogAfterCastWaitGCD(logger, item.Name, playerReader.GCD.Value); - return t; + wait.Until(playerReader.GCD.Value, cts.Token); } - public int UpdateGCD(bool forced) + if (item.AfterCastDelay > 0) { - int durationMs; + if (Log && item.Log) + LogAfterCastDelay(logger, item.Name, item.AfterCastDelay); - if (SpellInQueue() && !forced) - { - durationMs = Math.Max(playerReader.LastCastGCD, playerReader.RemainCastMs) - - playerReader.SpellQueueTimeMs - + playerReader.NetworkLatency.Value; - } - else + (t, e) = wait.Until(item.AfterCastDelay, cts.Token); + if (Log && item.Log && !t) { - durationMs = Math.Max(playerReader.GCD.Value, playerReader.RemainCastMs) - - playerReader.SpellQueueTimeMs - + playerReader.NetworkLatency.Value; + LogAfterCastDelayInterrupted(logger, item.Name, e); } - - SpellQueueOpen = DateTime.UtcNow.AddMilliseconds(durationMs); - //logger.LogInformation($"Spell Queue window upens after {durationMs}"); - return durationMs; } - public bool SwitchForm(KeyAction item) + if (!item.BaseAction) { - KeyAction? formAction = null; - for (int i = 0; i < classConfig.Form.Length; i++) - { - formAction = classConfig.Form[i]; - if (formAction.FormEnum == item.FormEnum) - { - break; - } - } + int durationMs = UpdateGCD(false); + if (Log && item.Log) + LogWaitForGCD(logger, item.Name, playerReader.GCD.Value, playerReader.RemainCastMs, durationMs); + } - if (formAction == null) - { - logger.LogError($"Unable to find {nameof(KeyAction.Key)} in {nameof(ClassConfiguration.Form)} to transform into {item.FormEnum}"); - return false; - } + item.ConsumeCharge(); + + interruptWatchdog.Reset(); + return true; + } + + private bool WaitForGCD(KeyAction item, bool spellQueue, CancellationToken token) //Func interrupt + { + int duration = Math.Max(playerReader.GCD.Value, playerReader.RemainCastMs); + + if (spellQueue) + duration -= (playerReader.SpellQueueTimeMs - playerReader.NetworkLatency.Value + playerReader.NetworkLatency.Value); //+ playerReader.NetworkLatency.Value + + if (duration <= 0) + return true; + + (bool t, double e) = wait.Until(duration, token); + + if (Log && item.Log) + LogGCD(logger, item.Name, !t, addonReader.UsableAction.Is(item), duration, e); + + return t; + } - return CastInstant(formAction, CancellationToken.None); + public int UpdateGCD(bool forced) + { + int durationMs; + + if (SpellInQueue() && !forced) + { + durationMs = Math.Max(playerReader.LastCastGCD, playerReader.RemainCastMs) + - playerReader.SpellQueueTimeMs + + playerReader.NetworkLatency.Value; + } + else + { + durationMs = Math.Max(playerReader.GCD.Value, playerReader.RemainCastMs) + - playerReader.SpellQueueTimeMs + + playerReader.NetworkLatency.Value; } - private void RepeatPetAttack() + SpellQueueOpen = DateTime.UtcNow.AddMilliseconds(durationMs); + //logger.LogInformation($"Spell Queue window upens after {durationMs}"); + return durationMs; + } + + public bool SwitchForm(KeyAction item) + { + KeyAction? formAction = null; + for (int i = 0; i < classConfig.Form.Length; i++) { - if (playerReader.Bits.PlayerInCombat() && - playerReader.Bits.HasPet() && - !playerReader.PetHasTarget() && - input.ClassConfig.PetAttack.GetRemainingCooldown() == 0) + formAction = classConfig.Form[i]; + if (formAction.FormEnum == item.FormEnum) { - input.PetAttack(); + break; } } - #region Logging - - [LoggerMessage( - EventId = 70, - Level = LogLevel.Information, - Message = "[{name}] ... AfterCastWaitSwing")] - static partial void LogAfterCastWaitSwing(ILogger logger, string name); - - [LoggerMessage( - EventId = 71, - Level = LogLevel.Information, - Message = "[{name}] instant skip validation")] - static partial void LogInstantSkipValidation(ILogger logger, string name); - - [LoggerMessage( - EventId = 72, - Level = LogLevel.Information, - Message = "[{name}] instant input {pressTime}ms {register} {inputElapsedMs}ms")] - static partial void LogInstantInput(ILogger logger, string name, int pressTime, bool register, double inputElapsedMs); - - [LoggerMessage( - EventId = 73, - Level = LogLevel.Information, - Message = "[{name}] instant usable: {beforeUsable}->{afterUsable} -- {beforeCastEvent}->{afterCastEvent}")] - static partial void LogInstantUsableChange(ILogger logger, string name, bool beforeUsable, bool afterUsable, string beforeCastEvent, string afterCastEvent); - - [LoggerMessage( - EventId = 74, - Level = LogLevel.Information, - Message = "[{name}] ... castbar skip validation")] - static partial void LogCastbarSkipValidation(ILogger logger, string name); - - [LoggerMessage( - EventId = 75, - Level = LogLevel.Information, - Message = "[{name}] castbar input {pressTime}ms {register} {inputElapsedMs}ms")] - static partial void LogCastbarInput(ILogger logger, string name, int pressTime, bool register, double inputElapsedMs); - - [LoggerMessage( - EventId = 76, - Level = LogLevel.Information, - Message = "[{name}] ... casting: {casting} -- count:{castCount} -- usable: {beforeUsable}->{afterUsable} -- {beforeCastEvent}->{afterCastEvent}")] - static partial void LogCastbarUsableChange(ILogger logger, string name, bool casting, int castCount, bool beforeUsable, bool afterUsable, string beforeCastEvent, string afterCastEvent); - - [LoggerMessage( - EventId = 77, - Level = LogLevel.Information, - Message = "[{name}] ... AfterCastWaitCastbar(V) {remain}ms or interrupt...")] - static partial void LogVisibleAfterCastWaitCastbar(ILogger logger, string name, int remain); - - [LoggerMessage( - EventId = 78, - Level = LogLevel.Warning, - Message = "[{name}] ... AfterCastWaitCastbar(v) interrupted!")] - static partial void LogVisibleAfterCastWaitCastbarInterrupted(ILogger logger, string name); - - [LoggerMessage( - EventId = 79, - Level = LogLevel.Information, - Message = "[{name}] ... AfterCastWaitCastbar(h) {remain}ms or interrupt...")] - static partial void LogHiddenAfterCastWaitCastbar(ILogger logger, string name, int remain); - - [LoggerMessage( - EventId = 80, - Level = LogLevel.Warning, - Message = "[{name}] ... AfterCastWaitCastbar(h) interrupted!")] - static partial void LogHiddenAfterCastWaitCastbarInterrupted(ILogger logger, string name); - - - [LoggerMessage( - EventId = 81, - Level = LogLevel.Warning, - Message = "[{name}] ... after {before}->{after} form switch still not usable!")] - static partial void LogAfterFormSwitchNotUsable(ILogger logger, string name, string before, string after); - - [LoggerMessage( - EventId = 82, - Level = LogLevel.Information, - Message = "[{name}] ... BeforeCastDelay {delayBeforeCast}ms")] - static partial void LogBeforeCastDelay(ILogger logger, string name, int delayBeforeCast); - - [LoggerMessage( - EventId = 83, - Level = LogLevel.Information, - Message = "[{name}] ... AfterCastWaitBuff | Buff: {changeTimeOut} | {auraCount} | miss: {missType} | {elapsedMs}ms")] - static partial void LogAfterCastWaitBuff(ILogger logger, string name, bool changeTimeOut, string auraCount, string missType, double elapsedMs); - - [LoggerMessage( - EventId = 84, - Level = LogLevel.Information, - Message = "[{name}] ... AfterCastWaitCombat ? {success} {delayAfterCast}ms")] - static partial void LogAfterCastWaitCombat(ILogger logger, string name, bool success, double delayAfterCast); - - [LoggerMessage( - EventId = 85, - Level = LogLevel.Information, - Message = "[{name}] ... AfterCastDelay {delayAfterCast}ms")] - static partial void LogAfterCastDelay(ILogger logger, string name, int delayAfterCast); - - [LoggerMessage( - EventId = 86, - Level = LogLevel.Information, - Message = "[{name}] ... AfterCastDelay interrupted {delayElaspedMs}ms")] - static partial void LogAfterCastDelayInterrupted(ILogger logger, string name, double delayElaspedMs); - - [LoggerMessage( - EventId = 88, - Level = LogLevel.Information, - Message = "[{name}] ... AfterCastStepBack {stepBackAfterCast}ms")] - static partial void LogAfterCastStepBack(ILogger logger, string name, int stepBackAfterCast); - - [LoggerMessage( - EventId = 89, - Level = LogLevel.Information, - Message = "[{name}] .... AfterCastStepBack interrupt: {interrupt} | {stepbackElapsedMs}ms")] - static partial void LogAfterCastStepBackInterrupted(ILogger logger, string name, bool interrupt, double stepbackElapsedMs); - - [LoggerMessage( - EventId = 90, - Level = LogLevel.Information, - Message = "[{name}] ... gcd interrupt: {interrupt} | usable: {usable} | remain: {remain}ms | {elapsedMs}ms")] - static partial void LogGCD(ILogger logger, string name, bool interrupt, bool usable, int remain, double elapsedMs); - - [LoggerMessage( - EventId = 91, - Level = LogLevel.Information, - Message = "[{name}] ... form changed: {changedTimeOut} | {before}->{after} | {elapsedMs}ms")] - static partial void LogFormChanged(ILogger logger, string name, bool changedTimeOut, string before, string after, double elapsedMs); - - [LoggerMessage( - EventId = 92, - Level = LogLevel.Information, - Message = "[{name}] ... AfterCastWaitBag ? {changeTimeOut} | {elapsedMs}ms")] - static partial void LogAfterCastWaitBag(ILogger logger, string name, bool changeTimeOut, double elapsedMs); - - [LoggerMessage( - EventId = 93, - Level = LogLevel.Information, - Message = "[{name}] GCD: {gcd}ms | Cast: {remainCastMs}ms | Next spell {duration}ms")] - static partial void LogWaitForGCD(ILogger logger, string name, int gcd, int remainCastMs, double duration); - - [LoggerMessage( - EventId = 94, - Level = LogLevel.Information, - Message = "[{name}] ... AfterCastAuraExpected {delay}ms")] - static partial void LogAfterCastAuraExpected(ILogger logger, string name, int delay); - - [LoggerMessage( - EventId = 95, - Level = LogLevel.Information, - Message = "[{name}] ... AfterCastWaitGCD {gcd}ms")] - static partial void LogAfterCastWaitGCD(ILogger logger, string name, int gcd); - - [LoggerMessage( - EventId = 96, - Level = LogLevel.Information, - Message = "[{name}] ... AfterCastWaitMeleeRange")] - static partial void LogAfterCastWaitMeleeRange(ILogger logger, string name); - - #endregion + if (formAction == null) + { + logger.LogError($"Unable to find {nameof(KeyAction.Key)} in {nameof(ClassConfiguration.Form)} to transform into {item.FormEnum}"); + return false; + } + + return CastInstant(formAction, CancellationToken.None); + } + + private void RepeatPetAttack() + { + if (playerReader.Bits.PlayerInCombat() && + playerReader.Bits.HasPet() && + !playerReader.PetHasTarget() && + input.ClassConfig.PetAttack.GetRemainingCooldown() == 0) + { + input.PetAttack(); + } } + + #region Logging + + [LoggerMessage( + EventId = 70, + Level = LogLevel.Information, + Message = "[{name}] ... AfterCastWaitSwing")] + static partial void LogAfterCastWaitSwing(ILogger logger, string name); + + [LoggerMessage( + EventId = 71, + Level = LogLevel.Information, + Message = "[{name}] instant skip validation")] + static partial void LogInstantSkipValidation(ILogger logger, string name); + + [LoggerMessage( + EventId = 72, + Level = LogLevel.Information, + Message = "[{name}] instant input {pressTime}ms {register} {inputElapsedMs}ms")] + static partial void LogInstantInput(ILogger logger, string name, int pressTime, bool register, double inputElapsedMs); + + [LoggerMessage( + EventId = 73, + Level = LogLevel.Information, + Message = "[{name}] instant usable: {beforeUsable}->{afterUsable} -- {beforeCastEvent}->{afterCastEvent}")] + static partial void LogInstantUsableChange(ILogger logger, string name, bool beforeUsable, bool afterUsable, string beforeCastEvent, string afterCastEvent); + + [LoggerMessage( + EventId = 74, + Level = LogLevel.Information, + Message = "[{name}] ... castbar skip validation")] + static partial void LogCastbarSkipValidation(ILogger logger, string name); + + [LoggerMessage( + EventId = 75, + Level = LogLevel.Information, + Message = "[{name}] castbar input {pressTime}ms {register} {inputElapsedMs}ms")] + static partial void LogCastbarInput(ILogger logger, string name, int pressTime, bool register, double inputElapsedMs); + + [LoggerMessage( + EventId = 76, + Level = LogLevel.Information, + Message = "[{name}] ... casting: {casting} -- count:{castCount} -- usable: {beforeUsable}->{afterUsable} -- {beforeCastEvent}->{afterCastEvent}")] + static partial void LogCastbarUsableChange(ILogger logger, string name, bool casting, int castCount, bool beforeUsable, bool afterUsable, string beforeCastEvent, string afterCastEvent); + + [LoggerMessage( + EventId = 77, + Level = LogLevel.Information, + Message = "[{name}] ... AfterCastWaitCastbar(V) {remain}ms or interrupt...")] + static partial void LogVisibleAfterCastWaitCastbar(ILogger logger, string name, int remain); + + [LoggerMessage( + EventId = 78, + Level = LogLevel.Warning, + Message = "[{name}] ... AfterCastWaitCastbar(v) interrupted!")] + static partial void LogVisibleAfterCastWaitCastbarInterrupted(ILogger logger, string name); + + [LoggerMessage( + EventId = 79, + Level = LogLevel.Information, + Message = "[{name}] ... AfterCastWaitCastbar(h) {remain}ms or interrupt...")] + static partial void LogHiddenAfterCastWaitCastbar(ILogger logger, string name, int remain); + + [LoggerMessage( + EventId = 80, + Level = LogLevel.Warning, + Message = "[{name}] ... AfterCastWaitCastbar(h) interrupted!")] + static partial void LogHiddenAfterCastWaitCastbarInterrupted(ILogger logger, string name); + + + [LoggerMessage( + EventId = 81, + Level = LogLevel.Warning, + Message = "[{name}] ... after {before}->{after} form switch still not usable!")] + static partial void LogAfterFormSwitchNotUsable(ILogger logger, string name, string before, string after); + + [LoggerMessage( + EventId = 82, + Level = LogLevel.Information, + Message = "[{name}] ... BeforeCastDelay {delayBeforeCast}ms")] + static partial void LogBeforeCastDelay(ILogger logger, string name, int delayBeforeCast); + + [LoggerMessage( + EventId = 83, + Level = LogLevel.Information, + Message = "[{name}] ... AfterCastWaitBuff | Buff: {changeTimeOut} | {auraCount} | miss: {missType} | {elapsedMs}ms")] + static partial void LogAfterCastWaitBuff(ILogger logger, string name, bool changeTimeOut, string auraCount, string missType, double elapsedMs); + + [LoggerMessage( + EventId = 84, + Level = LogLevel.Information, + Message = "[{name}] ... AfterCastWaitCombat ? {success} {delayAfterCast}ms")] + static partial void LogAfterCastWaitCombat(ILogger logger, string name, bool success, double delayAfterCast); + + [LoggerMessage( + EventId = 85, + Level = LogLevel.Information, + Message = "[{name}] ... AfterCastDelay {delayAfterCast}ms")] + static partial void LogAfterCastDelay(ILogger logger, string name, int delayAfterCast); + + [LoggerMessage( + EventId = 86, + Level = LogLevel.Information, + Message = "[{name}] ... AfterCastDelay interrupted {delayElaspedMs}ms")] + static partial void LogAfterCastDelayInterrupted(ILogger logger, string name, double delayElaspedMs); + + [LoggerMessage( + EventId = 88, + Level = LogLevel.Information, + Message = "[{name}] ... AfterCastStepBack {stepBackAfterCast}ms")] + static partial void LogAfterCastStepBack(ILogger logger, string name, int stepBackAfterCast); + + [LoggerMessage( + EventId = 89, + Level = LogLevel.Information, + Message = "[{name}] .... AfterCastStepBack interrupt: {interrupt} | {stepbackElapsedMs}ms")] + static partial void LogAfterCastStepBackInterrupted(ILogger logger, string name, bool interrupt, double stepbackElapsedMs); + + [LoggerMessage( + EventId = 90, + Level = LogLevel.Information, + Message = "[{name}] ... gcd interrupt: {interrupt} | usable: {usable} | remain: {remain}ms | {elapsedMs}ms")] + static partial void LogGCD(ILogger logger, string name, bool interrupt, bool usable, int remain, double elapsedMs); + + [LoggerMessage( + EventId = 91, + Level = LogLevel.Information, + Message = "[{name}] ... form changed: {changedTimeOut} | {before}->{after} | {elapsedMs}ms")] + static partial void LogFormChanged(ILogger logger, string name, bool changedTimeOut, string before, string after, double elapsedMs); + + [LoggerMessage( + EventId = 92, + Level = LogLevel.Information, + Message = "[{name}] ... AfterCastWaitBag ? {changeTimeOut} | {elapsedMs}ms")] + static partial void LogAfterCastWaitBag(ILogger logger, string name, bool changeTimeOut, double elapsedMs); + + [LoggerMessage( + EventId = 93, + Level = LogLevel.Information, + Message = "[{name}] GCD: {gcd}ms | Cast: {remainCastMs}ms | Next spell {duration}ms")] + static partial void LogWaitForGCD(ILogger logger, string name, int gcd, int remainCastMs, double duration); + + [LoggerMessage( + EventId = 94, + Level = LogLevel.Information, + Message = "[{name}] ... AfterCastAuraExpected {delay}ms")] + static partial void LogAfterCastAuraExpected(ILogger logger, string name, int delay); + + [LoggerMessage( + EventId = 95, + Level = LogLevel.Information, + Message = "[{name}] ... AfterCastWaitGCD {gcd}ms")] + static partial void LogAfterCastWaitGCD(ILogger logger, string name, int gcd); + + [LoggerMessage( + EventId = 96, + Level = LogLevel.Information, + Message = "[{name}] ... AfterCastWaitMeleeRange")] + static partial void LogAfterCastWaitMeleeRange(ILogger logger, string name); + + #endregion } \ No newline at end of file diff --git a/Core/GoalsComponent/CastingHandlerInterruptWatchdog.cs b/Core/GoalsComponent/CastingHandlerInterruptWatchdog.cs index df11838f9..6409f3dee 100644 --- a/Core/GoalsComponent/CastingHandlerInterruptWatchdog.cs +++ b/Core/GoalsComponent/CastingHandlerInterruptWatchdog.cs @@ -5,93 +5,92 @@ #pragma warning disable 162 -namespace Core.Goals +namespace Core.Goals; + +public sealed class CastingHandlerInterruptWatchdog : IDisposable { - public sealed class CastingHandlerInterruptWatchdog : IDisposable - { - private const bool Log = false; + private const bool Log = false; - private readonly ILogger logger; - private readonly Wait wait; + private readonly ILogger logger; + private readonly Wait wait; - private readonly Thread thread; - private readonly CancellationTokenSource threadCts; - private readonly ManualResetEvent resetEvent; + private readonly Thread thread; + private readonly CancellationTokenSource threadCts; + private readonly ManualResetEvent resetEvent; - private bool? initial; - private Func? interrupt; - private CancellationTokenSource? cts; + private bool? initial; + private Func? interrupt; + private CancellationTokenSource? cts; - public CastingHandlerInterruptWatchdog(ILogger logger, Wait wait) - { - this.logger = logger; - this.wait = wait; + public CastingHandlerInterruptWatchdog(ILogger logger, Wait wait) + { + this.logger = logger; + this.wait = wait; - threadCts = new(); - resetEvent = new(false); + threadCts = new(); + resetEvent = new(false); - thread = new(Watchdog); - thread.Start(); - } + thread = new(Watchdog); + thread.Start(); + } - public void Dispose() - { - interrupt = null; + public void Dispose() + { + interrupt = null; - threadCts.Cancel(); - resetEvent.Set(); - } + threadCts.Cancel(); + resetEvent.Set(); + } - private void Watchdog() + private void Watchdog() + { + while (!threadCts.IsCancellationRequested) { - while (!threadCts.IsCancellationRequested) + while (interrupt != null && + cts != null && !cts.IsCancellationRequested) { - while (interrupt != null && - cts != null && !cts.IsCancellationRequested) + if (initial != interrupt?.Invoke()) { - if (initial != interrupt?.Invoke()) - { - if (Log) - logger.LogWarning($"[{nameof(CastingHandlerInterruptWatchdog)}] Interrupted!"); - - interrupt = null; + if (Log) + logger.LogWarning($"[{nameof(CastingHandlerInterruptWatchdog)}] Interrupted!"); - cts?.Cancel(); - cts = null; + interrupt = null; - break; - } + cts?.Cancel(); + cts = null; - wait.Update(); + break; } - if (Log) - logger.LogWarning($"[{nameof(CastingHandlerInterruptWatchdog)}] waiting..."); - - resetEvent.Reset(); - resetEvent.WaitOne(); + wait.Update(); } - if (logger.IsEnabled(LogLevel.Debug)) - logger.LogDebug($"{nameof(CastingHandlerInterruptWatchdog)} thread stopped!"); + if (Log) + logger.LogWarning($"[{nameof(CastingHandlerInterruptWatchdog)}] waiting..."); + + resetEvent.Reset(); + resetEvent.WaitOne(); } - public void Set(Func interrupt, CancellationTokenSource cts) - { - if (Log) - logger.LogWarning($"Set interrupt!"); + if (logger.IsEnabled(LogLevel.Debug)) + logger.LogDebug($"{nameof(CastingHandlerInterruptWatchdog)} thread stopped!"); + } + + public void Set(Func interrupt, CancellationTokenSource cts) + { + if (Log) + logger.LogWarning($"Set interrupt!"); - this.initial = interrupt(); - this.interrupt = interrupt; - this.cts = cts; + this.initial = interrupt(); + this.interrupt = interrupt; + this.cts = cts; - resetEvent.Set(); - } + resetEvent.Set(); + } - public void Reset() - { - interrupt = null; - cts?.Cancel(); - } + public void Reset() + { + interrupt = null; + cts?.Cancel(); } } diff --git a/Core/GoalsComponent/CombatUtil.cs b/Core/GoalsComponent/CombatUtil.cs index d373305d7..1cb475a79 100644 --- a/Core/GoalsComponent/CombatUtil.cs +++ b/Core/GoalsComponent/CombatUtil.cs @@ -2,118 +2,117 @@ using SharedLib.Extensions; using System.Numerics; -namespace Core +namespace Core; + +public sealed class CombatUtil { - public sealed class CombatUtil - { - private const float MIN_DISTANCE = 0.01f; + private const float MIN_DISTANCE = 0.01f; - private readonly ILogger logger; - private readonly AddonReader addonReader; - private readonly PlayerReader playerReader; - private readonly ConfigurableInput input; - private readonly Wait wait; + private readonly ILogger logger; + private readonly AddonReader addonReader; + private readonly PlayerReader playerReader; + private readonly ConfigurableInput input; + private readonly Wait wait; - private const bool debug = true; + private const bool debug = true; - private bool outOfCombat; + private bool outOfCombat; - public CombatUtil(ILogger logger, ConfigurableInput input, Wait wait, AddonReader addonReader) - { - this.logger = logger; - this.input = input; - this.wait = wait; - this.addonReader = addonReader; - this.playerReader = addonReader.PlayerReader; + public CombatUtil(ILogger logger, ConfigurableInput input, Wait wait, AddonReader addonReader) + { + this.logger = logger; + this.input = input; + this.wait = wait; + this.addonReader = addonReader; + this.playerReader = addonReader.PlayerReader; - outOfCombat = !playerReader.Bits.PlayerInCombat(); - } + outOfCombat = !playerReader.Bits.PlayerInCombat(); + } - public void Update() - { - outOfCombat = !playerReader.Bits.PlayerInCombat(); - } + public void Update() + { + outOfCombat = !playerReader.Bits.PlayerInCombat(); + } - public bool EnteredCombat() + public bool EnteredCombat() + { + if (!outOfCombat && !playerReader.Bits.PlayerInCombat()) { - if (!outOfCombat && !playerReader.Bits.PlayerInCombat()) - { - Log("Combat Leave"); - outOfCombat = true; - return false; - } - - if (outOfCombat && playerReader.Bits.PlayerInCombat()) - { - Log("Combat Enter"); - outOfCombat = false; - return true; - } - + Log("Combat Leave"); + outOfCombat = true; return false; } - public bool AquiredTarget(int maxTimeMs = 400) + if (outOfCombat && playerReader.Bits.PlayerInCombat()) { - if (this.playerReader.Bits.PlayerInCombat()) - { - if (this.playerReader.PetHasTarget()) - { - input.TargetPet(); - Log($"Pets target {playerReader.TargetTarget}"); - if (playerReader.TargetTarget == UnitsTarget.PetHasATarget) - { - Log($"{nameof(AquiredTarget)}: Found target by pet"); - input.TargetOfTarget(); - return true; - } - } + Log("Combat Enter"); + outOfCombat = false; + return true; + } - input.NearestTarget(); - wait.Update(); + return false; + } - if (playerReader.Bits.HasTarget() && - playerReader.Bits.TargetInCombat() && - (playerReader.Bits.TargetOfTargetIsPlayerOrPet() || - addonReader.CombatLog.DamageTaken.Contains(playerReader.TargetGuid))) + public bool AquiredTarget(int maxTimeMs = 400) + { + if (this.playerReader.Bits.PlayerInCombat()) + { + if (this.playerReader.PetHasTarget()) + { + input.TargetPet(); + Log($"Pets target {playerReader.TargetTarget}"); + if (playerReader.TargetTarget == UnitsTarget.PetHasATarget) { - Log("Found target"); + Log($"{nameof(AquiredTarget)}: Found target by pet"); + input.TargetOfTarget(); return true; } + } - input.ClearTarget(); - wait.Update(); + input.NearestTarget(); + wait.Update(); - if (!wait.Till(maxTimeMs, PlayerOrPetHasTarget)) - { - Log($"{nameof(AquiredTarget)}: Someone started attacking me!"); - return true; - } + if (playerReader.Bits.HasTarget() && + playerReader.Bits.TargetInCombat() && + (playerReader.Bits.TargetOfTargetIsPlayerOrPet() || + addonReader.CombatLog.DamageTaken.Contains(playerReader.TargetGuid))) + { + Log("Found target"); + return true; + } + + input.ClearTarget(); + wait.Update(); - Log($"{nameof(AquiredTarget)}: No target found after {maxTimeMs}ms"); - input.ClearTarget(); - wait.Update(); + if (!wait.Till(maxTimeMs, PlayerOrPetHasTarget)) + { + Log($"{nameof(AquiredTarget)}: Someone started attacking me!"); + return true; } - return false; - } - public bool IsPlayerMoving(Vector3 map) - { - float mapDistance = playerReader.MapPos.MapDistanceXYTo(map); - return mapDistance > MIN_DISTANCE; + Log($"{nameof(AquiredTarget)}: No target found after {maxTimeMs}ms"); + input.ClearTarget(); + wait.Update(); } + return false; + } - private bool PlayerOrPetHasTarget() - { - return playerReader.Bits.HasTarget() || playerReader.PetHasTarget(); - } + public bool IsPlayerMoving(Vector3 map) + { + float mapDistance = playerReader.MapPos.MapDistanceXYTo(map); + return mapDistance > MIN_DISTANCE; + } - private void Log(string text) + private bool PlayerOrPetHasTarget() + { + return playerReader.Bits.HasTarget() || playerReader.PetHasTarget(); + } + + private void Log(string text) + { + if (debug) { - if (debug) - { - logger.LogDebug($"{nameof(CombatUtil)}: {text}"); - } + logger.LogDebug($"{nameof(CombatUtil)}: {text}"); } } } diff --git a/Core/GoalsComponent/DruidMountHandler.cs b/Core/GoalsComponent/DruidMountHandler.cs index f92b66297..904d310d1 100644 --- a/Core/GoalsComponent/DruidMountHandler.cs +++ b/Core/GoalsComponent/DruidMountHandler.cs @@ -2,72 +2,71 @@ using Core.Goals; -namespace Core +namespace Core; + +public sealed class DruidMountHandler : IMountHandler { - public sealed class DruidMountHandler : IMountHandler - { - private readonly MountHandler mountHandler; - private readonly ClassConfiguration classConfig; - private readonly CastingHandler castingHandler; - private readonly PlayerReader playerReader; - private readonly ConfigurableInput input; + private readonly MountHandler mountHandler; + private readonly ClassConfiguration classConfig; + private readonly CastingHandler castingHandler; + private readonly PlayerReader playerReader; + private readonly ConfigurableInput input; - public DruidMountHandler(MountHandler mountHandler, - CastingHandler castingHandler, ClassConfiguration classConfig, - PlayerReader playerReader, ConfigurableInput input) - { - this.mountHandler = mountHandler; - this.castingHandler = castingHandler; - this.classConfig = classConfig; - this.playerReader = playerReader; - this.input = input; - } + public DruidMountHandler(MountHandler mountHandler, + CastingHandler castingHandler, ClassConfiguration classConfig, + PlayerReader playerReader, ConfigurableInput input) + { + this.mountHandler = mountHandler; + this.castingHandler = castingHandler; + this.classConfig = classConfig; + this.playerReader = playerReader; + this.input = input; + } - public bool CanMount() => - mountHandler.CanMount(); + public bool CanMount() => + mountHandler.CanMount(); - public void Dismount() + public void Dismount() + { + if (playerReader.Form is Form.Druid_Flight or Form.Druid_Travel) { - if (playerReader.Form is Form.Druid_Flight or Form.Druid_Travel) + for (int i = 0; i < classConfig.Form.Length; i++) { - for (int i = 0; i < classConfig.Form.Length; i++) + KeyAction form = classConfig.Form[i]; + if (form.FormEnum == playerReader.Form) { - KeyAction form = classConfig.Form[i]; - if (form.FormEnum == playerReader.Form) - { - input.Proc.KeyPress(form.ConsoleKey, input.defaultKeyPress); - return; - } + input.Proc.KeyPress(form.ConsoleKey, input.defaultKeyPress); + return; } } - - mountHandler.Dismount(); } - public bool IsMounted() - { - return - playerReader.Form is Form.Druid_Flight or Form.Druid_Travel || - mountHandler.IsMounted(); - } + mountHandler.Dismount(); + } + + public bool IsMounted() + { + return + playerReader.Form is Form.Druid_Flight or Form.Druid_Travel || + mountHandler.IsMounted(); + } - public void MountUp() + public void MountUp() + { + for (int i = 0; i < classConfig.Form.Length; i++) { - for (int i = 0; i < classConfig.Form.Length; i++) + KeyAction keyAction = classConfig.Form[i]; + if (keyAction.FormEnum is Form.Druid_Flight or Form.Druid_Travel && + castingHandler.SwitchForm(keyAction)) { - KeyAction keyAction = classConfig.Form[i]; - if (keyAction.FormEnum is Form.Druid_Flight or Form.Druid_Travel && - castingHandler.SwitchForm(keyAction)) - { - return; - } + return; } - - mountHandler.MountUp(); } - public bool ShouldMount(Vector3 targetW) => - mountHandler.ShouldMount(targetW); + mountHandler.MountUp(); } + + public bool ShouldMount(Vector3 targetW) => + mountHandler.ShouldMount(targetW); } diff --git a/Core/GoalsComponent/IMountHandler.cs b/Core/GoalsComponent/IMountHandler.cs index 790837a51..51d88207c 100644 --- a/Core/GoalsComponent/IMountHandler.cs +++ b/Core/GoalsComponent/IMountHandler.cs @@ -1,14 +1,13 @@ using System.Numerics; -namespace Core +namespace Core; + +public interface IMountHandler { - public interface IMountHandler - { - void MountUp(); - void Dismount(); + void MountUp(); + void Dismount(); - bool CanMount(); - bool ShouldMount(Vector3 targetW); - bool IsMounted(); - } + bool CanMount(); + bool ShouldMount(Vector3 targetW); + bool IsMounted(); } diff --git a/Core/GoalsComponent/MountHandler.cs b/Core/GoalsComponent/MountHandler.cs index eaa34e709..3bac4cc36 100644 --- a/Core/GoalsComponent/MountHandler.cs +++ b/Core/GoalsComponent/MountHandler.cs @@ -6,128 +6,127 @@ using System.Numerics; -namespace Core +namespace Core; + +public sealed class MountHandler : IMountHandler { - public sealed class MountHandler : IMountHandler + private const int DISTANCE_TO_MOUNT = 40; + + private const int MIN_DISTANCE_TO_INTERRUPT_CAST = 60; + + private readonly ILogger logger; + private readonly ConfigurableInput input; + private readonly ClassConfiguration classConfig; + private readonly Wait wait; + private readonly ActionBarBits usableAction; + private readonly ActionBarCooldownReader cooldownReader; + private readonly PlayerReader playerReader; + private readonly AddonBits bits; + private readonly StopMoving stopMoving; + private readonly IBlacklist targetBlacklist; + + public MountHandler(ILogger logger, ConfigurableInput input, + ClassConfiguration classConfig, Wait wait, AddonReader addonReader, + StopMoving stopMoving, IBlacklist blacklist) { - private const int DISTANCE_TO_MOUNT = 40; - - private const int MIN_DISTANCE_TO_INTERRUPT_CAST = 60; - - private readonly ILogger logger; - private readonly ConfigurableInput input; - private readonly ClassConfiguration classConfig; - private readonly Wait wait; - private readonly ActionBarBits usableAction; - private readonly ActionBarCooldownReader cooldownReader; - private readonly PlayerReader playerReader; - private readonly AddonBits bits; - private readonly StopMoving stopMoving; - private readonly IBlacklist targetBlacklist; - - public MountHandler(ILogger logger, ConfigurableInput input, - ClassConfiguration classConfig, Wait wait, AddonReader addonReader, - StopMoving stopMoving, IBlacklist blacklist) - { - this.logger = logger; - this.classConfig = classConfig; - this.input = input; - this.wait = wait; - this.usableAction = addonReader.UsableAction; - this.cooldownReader = addonReader.ActionBarCooldownReader; - this.playerReader = addonReader.PlayerReader; - this.bits = playerReader.Bits; - this.stopMoving = stopMoving; - this.targetBlacklist = blacklist; - } + this.logger = logger; + this.classConfig = classConfig; + this.input = input; + this.wait = wait; + this.usableAction = addonReader.UsableAction; + this.cooldownReader = addonReader.ActionBarCooldownReader; + this.playerReader = addonReader.PlayerReader; + this.bits = playerReader.Bits; + this.stopMoving = stopMoving; + this.targetBlacklist = blacklist; + } - public bool CanMount() - { - return - !IsMounted() && - !bits.IsIndoors() && - !bits.PlayerInCombat() && - !bits.IsSwimming() && - !bits.IsFalling() && - usableAction.Is(classConfig.Mount) && - cooldownReader.Get(classConfig.Mount) == 0; - } + public bool CanMount() + { + return + !IsMounted() && + !bits.IsIndoors() && + !bits.PlayerInCombat() && + !bits.IsSwimming() && + !bits.IsFalling() && + usableAction.Is(classConfig.Mount) && + cooldownReader.Get(classConfig.Mount) == 0; + } - public void MountUp() - { - wait.While(bits.IsFalling); + public void MountUp() + { + wait.While(bits.IsFalling); - stopMoving.Stop(); + stopMoving.Stop(); + wait.Update(); + + input.Mount(); + + (bool t, double e) = + wait.Until(CastingHandler.SPELL_QUEUE + playerReader.NetworkLatency.Value, CastDetected); + Log($"Cast started ? {!t} {e}ms"); + + if (!bits.IsMounted()) + { wait.Update(); - input.Mount(); + (t, e) = + wait.Until(playerReader.RemainCastMs + playerReader.NetworkLatency.Value, MountedOrNotCastingOrValidTargetOrEnteredCombat); + Log($"Cast ended ? {!t} {e}ms"); + + if (!HasValidTarget()) + { + (t, e) = + wait.Until(CastingHandler.SPELL_QUEUE + playerReader.NetworkLatency.Value, bits.IsMounted); - (bool t, double e) = - wait.Until(CastingHandler.SPELL_QUEUE + playerReader.NetworkLatency.Value, CastDetected); - Log($"Cast started ? {!t} {e}ms"); + Log($"Mounted ? {bits.IsMounted()} {e}ms"); + } - if (!bits.IsMounted()) + if (bits.PlayerInCombat() && bits.HasTarget() && !bits.TargetOfTargetIsPlayerOrPet()) { + input.ClearTarget(); wait.Update(); - - (t, e) = - wait.Until(playerReader.RemainCastMs + playerReader.NetworkLatency.Value, MountedOrNotCastingOrValidTargetOrEnteredCombat); - Log($"Cast ended ? {!t} {e}ms"); - - if (!HasValidTarget()) - { - (t, e) = - wait.Until(CastingHandler.SPELL_QUEUE + playerReader.NetworkLatency.Value, bits.IsMounted); - - Log($"Mounted ? {bits.IsMounted()} {e}ms"); - } - - if (bits.PlayerInCombat() && bits.HasTarget() && !bits.TargetOfTargetIsPlayerOrPet()) - { - input.ClearTarget(); - wait.Update(); - } } } + } - public bool ShouldMount(Vector3 targetW) - { - Vector3 playerW = playerReader.WorldPos; - float distance = playerW.WorldDistanceXYTo(targetW); - return distance > DISTANCE_TO_MOUNT; - } + public bool ShouldMount(Vector3 targetW) + { + Vector3 playerW = playerReader.WorldPos; + float distance = playerW.WorldDistanceXYTo(targetW); + return distance > DISTANCE_TO_MOUNT; + } - public static bool ShouldMount(float totalDistance) - { - return totalDistance > DISTANCE_TO_MOUNT; - } + public static bool ShouldMount(float totalDistance) + { + return totalDistance > DISTANCE_TO_MOUNT; + } - public void Dismount() - { - input.Dismount(); - } + public void Dismount() + { + input.Dismount(); + } - public bool IsMounted() - { - return bits.IsMounted(); - } + public bool IsMounted() + { + return bits.IsMounted(); + } - private bool CastDetected() => - bits.IsMounted() || playerReader.IsCasting(); + private bool CastDetected() => + bits.IsMounted() || playerReader.IsCasting(); - private bool MountedOrNotCastingOrValidTargetOrEnteredCombat() => - bits.IsMounted() || - !playerReader.IsCasting() || - HasValidTarget() || - bits.PlayerInCombat(); + private bool MountedOrNotCastingOrValidTargetOrEnteredCombat() => + bits.IsMounted() || + !playerReader.IsCasting() || + HasValidTarget() || + bits.PlayerInCombat(); - private bool HasValidTarget() => - bits.HasTarget() && !targetBlacklist.Is() && - playerReader.MinRange() < MIN_DISTANCE_TO_INTERRUPT_CAST; + private bool HasValidTarget() => + bits.HasTarget() && !targetBlacklist.Is() && + playerReader.MinRange() < MIN_DISTANCE_TO_INTERRUPT_CAST; - private void Log(string text) - { - logger.LogInformation($"{nameof(MountHandler)}: {text}"); - } + private void Log(string text) + { + logger.LogInformation($"{nameof(MountHandler)}: {text}"); } } diff --git a/Core/GoalsComponent/Navigation.cs b/Core/GoalsComponent/Navigation.cs index 2209dcae3..f46cacff4 100644 --- a/Core/GoalsComponent/Navigation.cs +++ b/Core/GoalsComponent/Navigation.cs @@ -11,564 +11,563 @@ #pragma warning disable 162 -namespace Core.Goals +namespace Core.Goals; + +public sealed partial class Navigation : IDisposable { - public sealed partial class Navigation : IDisposable - { - private const bool debug = false; + private const bool debug = false; - private const float DIFF_THRESHOLD = 1.5f; // within 50% difference - private const float UNIFORM_DIST_DIV = 2; // within 50% difference + private const float DIFF_THRESHOLD = 1.5f; // within 50% difference + private const float UNIFORM_DIST_DIV = 2; // within 50% difference - private readonly string patherName; + private readonly string patherName; - private readonly ILogger logger; - private readonly PlayerDirection playerDirection; - private readonly ConfigurableInput input; - private readonly AddonReader addonReader; - private readonly PlayerReader playerReader; - private readonly StopMoving stopMoving; - private readonly StuckDetector stuckDetector; - private readonly IPPather pather; - private readonly IMountHandler mountHandler; + private readonly ILogger logger; + private readonly PlayerDirection playerDirection; + private readonly ConfigurableInput input; + private readonly AddonReader addonReader; + private readonly PlayerReader playerReader; + private readonly StopMoving stopMoving; + private readonly StuckDetector stuckDetector; + private readonly IPPather pather; + private readonly IMountHandler mountHandler; - private const float MinDistanceMount = 10; - private readonly float MaxDistance = 200; - private readonly float MinDistance = 5; + private const float MinDistanceMount = 10; + private readonly float MaxDistance = 200; + private readonly float MinDistance = 5; - private float AvgDistance; - private float lastWorldDistance = float.MaxValue; + private float AvgDistance; + private float lastWorldDistance = float.MaxValue; - private const float minAngleToTurn = PI / 35f; // 5.14 degree - private const float minAngleToStopBeforeTurn = PI / 2f; // 90 degree + private const float minAngleToTurn = PI / 35f; // 5.14 degree + private const float minAngleToStopBeforeTurn = PI / 2f; // 90 degree - private readonly Stack wayPoints = new(); - private readonly Stack routeToNextWaypoint = new(); + private readonly Stack wayPoints = new(); + private readonly Stack routeToNextWaypoint = new(); - public Vector3[] TotalRoute { private set; get; } = Array.Empty(); + public Vector3[] TotalRoute { private set; get; } = Array.Empty(); - public DateTime LastActive { get; private set; } + public DateTime LastActive { get; private set; } - public event Action? OnPathCalculated; - public event Action? OnWayPointReached; - public event Action? OnDestinationReached; - public event Action? OnAnyPointReached; + public event Action? OnPathCalculated; + public event Action? OnWayPointReached; + public event Action? OnDestinationReached; + public event Action? OnAnyPointReached; - public bool SimplifyRouteToWaypoint { get; set; } = true; + public bool SimplifyRouteToWaypoint { get; set; } = true; - private bool active; - private Vector3 playerWorldPos; + private bool active; + private Vector3 playerWorldPos; - private readonly Queue pathRequests = new(1); - private readonly Queue pathResults = new(1); + private readonly Queue pathRequests = new(1); + private readonly Queue pathResults = new(1); - private readonly CancellationTokenSource _cts; - private readonly Thread pathfinderThread; - private readonly ManualResetEvent manualReset; + private readonly CancellationTokenSource _cts; + private readonly Thread pathfinderThread; + private readonly ManualResetEvent manualReset; - private int failedAttempt; - private Vector3 lastFailedDestination; + private int failedAttempt; + private Vector3 lastFailedDestination; - public Navigation(ILogger logger, PlayerDirection playerDirection, - ConfigurableInput input, AddonReader addonReader, StopMoving stopMoving, - StuckDetector stuckDetector, IPPather pather, IMountHandler mountHandler, - ClassConfiguration classConfiguration) + public Navigation(ILogger logger, PlayerDirection playerDirection, + ConfigurableInput input, AddonReader addonReader, StopMoving stopMoving, + StuckDetector stuckDetector, IPPather pather, IMountHandler mountHandler, + ClassConfiguration classConfiguration) + { + this.logger = logger; + this.playerDirection = playerDirection; + this.input = input; + this.addonReader = addonReader; + playerReader = addonReader.PlayerReader; + this.stopMoving = stopMoving; + this.stuckDetector = stuckDetector; + this.pather = pather; + this.mountHandler = mountHandler; + + patherName = pather.GetType().Name; + + AvgDistance = MinDistance; + + manualReset = new(false); + _cts = new(); + pathfinderThread = new(PathFinderThread); + pathfinderThread.Start(); + + switch (classConfiguration.Mode) { - this.logger = logger; - this.playerDirection = playerDirection; - this.input = input; - this.addonReader = addonReader; - playerReader = addonReader.PlayerReader; - this.stopMoving = stopMoving; - this.stuckDetector = stuckDetector; - this.pather = pather; - this.mountHandler = mountHandler; - - patherName = pather.GetType().Name; - - AvgDistance = MinDistance; - - manualReset = new(false); - _cts = new(); - pathfinderThread = new(PathFinderThread); - pathfinderThread.Start(); - - switch (classConfiguration.Mode) - { - case Mode.AttendedGather: - MaxDistance = MinDistance; - SimplifyRouteToWaypoint = false; - break; - } + case Mode.AttendedGather: + MaxDistance = MinDistance; + SimplifyRouteToWaypoint = false; + break; } + } - public void Dispose() + public void Dispose() + { + _cts.Cancel(); + manualReset.Set(); + } + + public void Update() + { + Update(_cts.Token); + } + + public void Update(CancellationToken ct) + { + active = true; + + if (wayPoints.Count == 0 && routeToNextWaypoint.Count == 0) { - _cts.Cancel(); - manualReset.Set(); + OnDestinationReached?.Invoke(); + return; } - public void Update() + while (pathResults.TryDequeue(out PathResult result)) { - Update(_cts.Token); + result.Callback(result); } - public void Update(CancellationToken ct) + if (ct.IsCancellationRequested || pathRequests.Count > 0) { - active = true; + return; + } - if (wayPoints.Count == 0 && routeToNextWaypoint.Count == 0) - { - OnDestinationReached?.Invoke(); - return; - } + if (routeToNextWaypoint.Count == 0) + { + RefillRouteToNextWaypoint(ct); + return; + } - while (pathResults.TryDequeue(out PathResult result)) - { - result.Callback(result); - } + LastActive = DateTime.UtcNow; + input.Proc.SetKeyState(input.Proc.ForwardKey, true, true); - if (ct.IsCancellationRequested || pathRequests.Count > 0) - { - return; - } + // main loop + Vector3 playerW = playerReader.WorldPos; + playerWorldPos = playerW; + Vector3 targetW = routeToNextWaypoint.Peek(); + float worldDistance = playerW.WorldDistanceXYTo(targetW); - if (routeToNextWaypoint.Count == 0) + Vector3 playerM = WorldMapAreaDB.ToMap_FlipXY(playerW, playerReader.WorldMapArea); + Vector3 targetM = WorldMapAreaDB.ToMap_FlipXY(targetW, playerReader.WorldMapArea); + float heading = DirectionCalculator.CalculateMapHeading(playerM, targetM); + + if (worldDistance < ReachedDistance(MinDistance)) + { + if (targetW.Z != 0 && targetW.Z != playerW.Z) { - RefillRouteToNextWaypoint(ct); - return; + playerReader.WorldPosZ = targetW.Z; } - LastActive = DateTime.UtcNow; - input.Proc.SetKeyState(input.Proc.ForwardKey, true, true); + if (SimplifyRouteToWaypoint) + ReduceByDistance(playerW, MinDistance); + else + routeToNextWaypoint.Pop(); - // main loop - Vector3 playerW = playerReader.WorldPos; - playerWorldPos = playerW; - Vector3 targetW = routeToNextWaypoint.Peek(); - float worldDistance = playerW.WorldDistanceXYTo(targetW); + OnAnyPointReached?.Invoke(); - Vector3 playerM = WorldMapAreaDB.ToMap_FlipXY(playerW, playerReader.WorldMapArea); - Vector3 targetM = WorldMapAreaDB.ToMap_FlipXY(targetW, playerReader.WorldMapArea); - float heading = DirectionCalculator.CalculateMapHeading(playerM, targetM); + lastWorldDistance = float.MaxValue; + UpdateTotalRoute(); - if (worldDistance < ReachedDistance(MinDistance)) + if (routeToNextWaypoint.Count == 0) { - if (targetW.Z != 0 && targetW.Z != playerW.Z) - { - playerReader.WorldPosZ = targetW.Z; - } - - if (SimplifyRouteToWaypoint) - ReduceByDistance(playerW, MinDistance); - else - routeToNextWaypoint.Pop(); - - OnAnyPointReached?.Invoke(); - - lastWorldDistance = float.MaxValue; - UpdateTotalRoute(); - - if (routeToNextWaypoint.Count == 0) + if (wayPoints.Count > 0) { - if (wayPoints.Count > 0) - { - wayPoints.Pop(); - UpdateTotalRoute(); + wayPoints.Pop(); + UpdateTotalRoute(); - if (debug) - LogDebug($"Reached wayPoint! Distance: {worldDistance} -- Remains: {wayPoints.Count}"); + if (debug) + LogDebug($"Reached wayPoint! Distance: {worldDistance} -- Remains: {wayPoints.Count}"); - OnWayPointReached?.Invoke(); - } + OnWayPointReached?.Invoke(); } - else - { - targetW = routeToNextWaypoint.Peek(); - stuckDetector.SetTargetLocation(targetW); - - playerM = WorldMapAreaDB.ToMap_FlipXY(playerW, playerReader.WorldMapArea); - targetM = WorldMapAreaDB.ToMap_FlipXY(targetW, playerReader.WorldMapArea); - heading = DirectionCalculator.CalculateMapHeading(playerM, targetM); + } + else + { + targetW = routeToNextWaypoint.Peek(); + stuckDetector.SetTargetLocation(targetW); - AdjustHeading(heading, ct); + playerM = WorldMapAreaDB.ToMap_FlipXY(playerW, playerReader.WorldMapArea); + targetM = WorldMapAreaDB.ToMap_FlipXY(targetW, playerReader.WorldMapArea); + heading = DirectionCalculator.CalculateMapHeading(playerM, targetM); - return; - } - } + AdjustHeading(heading, ct); - if (routeToNextWaypoint.Count > 0) - { - if (stuckDetector.IsGettingCloser()) - { - AdjustHeading(heading, ct); - } - else - { - if (stuckDetector.ActionDurationMs > 10_000) - { - LogClearRouteToWaypointStuck(logger, stuckDetector.ActionDurationMs); - stuckDetector.Reset(); - routeToNextWaypoint.Clear(); - return; - } - - if (HasBeenActiveRecently()) - { - stuckDetector.Update(); - worldDistance = playerW.WorldDistanceXYTo(routeToNextWaypoint.Peek()); - } - } + return; } - - lastWorldDistance = worldDistance; } - public void Resume() + if (routeToNextWaypoint.Count > 0) { - ResetStuckParameters(); - - if (pather.GetType() != typeof(RemotePathingAPIV3) && routeToNextWaypoint.Count > 0) + if (stuckDetector.IsGettingCloser()) { - V1_AttemptToKeepRouteToWaypoint(); + AdjustHeading(heading, ct); } - - int removed = 0; - while (AdjustNextWaypointPointToClosest() && removed < 5) { removed++; }; - if (removed > 0) + else { - UpdateTotalRoute(); + if (stuckDetector.ActionDurationMs > 10_000) + { + LogClearRouteToWaypointStuck(logger, stuckDetector.ActionDurationMs); + stuckDetector.Reset(); + routeToNextWaypoint.Clear(); + return; + } - if (debug) - LogDebug($"Resume: removed {removed} waypoint!"); + if (HasBeenActiveRecently()) + { + stuckDetector.Update(); + worldDistance = playerW.WorldDistanceXYTo(routeToNextWaypoint.Peek()); + } } } - public void Stop() - { - active = false; - - if (pather.GetType() == typeof(RemotePathingAPIV3)) - routeToNextWaypoint.Clear(); + lastWorldDistance = worldDistance; + } - ResetStuckParameters(); - } + public void Resume() + { + ResetStuckParameters(); - public void StopMovement() + if (pather.GetType() != typeof(RemotePathingAPIV3) && routeToNextWaypoint.Count > 0) { - if (input.Proc.IsKeyDown(input.Proc.ForwardKey)) - input.Proc.SetKeyState(input.Proc.ForwardKey, false, true); + V1_AttemptToKeepRouteToWaypoint(); } - public bool HasWaypoint() + int removed = 0; + while (AdjustNextWaypointPointToClosest() && removed < 5) { removed++; }; + if (removed > 0) { - return wayPoints.Count != 0; - } + UpdateTotalRoute(); - public bool HasNext() - { - return routeToNextWaypoint.Count != 0; + if (debug) + LogDebug($"Resume: removed {removed} waypoint!"); } + } - public Vector3 NextMapPoint() - { - return WorldMapAreaDB.ToMap_FlipXY(routeToNextWaypoint.Peek(), playerReader.WorldMapArea); - } + public void Stop() + { + active = false; - public void SetWayPoints(Span mapPoints) - { - wayPoints.Clear(); + if (pather.GetType() == typeof(RemotePathingAPIV3)) routeToNextWaypoint.Clear(); - float mapDistanceXY = 0; - WorldMapArea wma = playerReader.WorldMapArea; - for (int i = mapPoints.Length - 1; i >= 0; i--) - { - Vector3 worldPos = WorldMapAreaDB.ToWorld_FlipXY(mapPoints[i], wma); - if (i != mapPoints.Length - 1) - { - Vector3 prev = wayPoints.Peek(); - mapDistanceXY += worldPos.WorldDistanceXYTo(prev); - } - wayPoints.Push(worldPos); - } + ResetStuckParameters(); + } - AvgDistance = wayPoints.Count > 1 ? Max(mapDistanceXY / wayPoints.Count, MinDistance) : MinDistance; + public void StopMovement() + { + if (input.Proc.IsKeyDown(input.Proc.ForwardKey)) + input.Proc.SetKeyState(input.Proc.ForwardKey, false, true); + } - UpdateTotalRoute(); - } + public bool HasWaypoint() + { + return wayPoints.Count != 0; + } - public void ResetStuckParameters() - { - stuckDetector.Reset(); - } + public bool HasNext() + { + return routeToNextWaypoint.Count != 0; + } - private void RefillRouteToNextWaypoint(CancellationToken ct) - { - routeToNextWaypoint.Clear(); + public Vector3 NextMapPoint() + { + return WorldMapAreaDB.ToMap_FlipXY(routeToNextWaypoint.Peek(), playerReader.WorldMapArea); + } - Vector3 playerW = playerReader.WorldPos; - Vector3 targetW = wayPoints.Peek(); - float distance = playerW.WorldDistanceXYTo(targetW); + public void SetWayPoints(Span mapPoints) + { + wayPoints.Clear(); + routeToNextWaypoint.Clear(); - if (distance > MaxDistance || distance > AvgDistance * 2) + float mapDistanceXY = 0; + WorldMapArea wma = playerReader.WorldMapArea; + for (int i = mapPoints.Length - 1; i >= 0; i--) + { + Vector3 worldPos = WorldMapAreaDB.ToWorld_FlipXY(mapPoints[i], wma); + if (i != mapPoints.Length - 1) { - if (debug) - LogDebug($"Distance: {distance} vs Avg:({AvgDistance * 2},{AvgDistance}) - TAVG: {DIFF_THRESHOLD * AvgDistance} "); - - stopMoving.Stop(); - PathRequest(new PathRequest(playerReader.UIMapId.Value, playerW, targetW, distance, PathCalculatedCallback)); + Vector3 prev = wayPoints.Peek(); + mapDistanceXY += worldPos.WorldDistanceXYTo(prev); } - else - { - if (debug) - LogDebug($"non pathfinder - {distance} - {playerW} -> {targetW}"); + wayPoints.Push(worldPos); + } - routeToNextWaypoint.Push(targetW); + AvgDistance = wayPoints.Count > 1 ? Max(mapDistanceXY / wayPoints.Count, MinDistance) : MinDistance; - Vector3 playerM = WorldMapAreaDB.ToMap_FlipXY(playerW, playerReader.WorldMapArea); - Vector3 targetM = WorldMapAreaDB.ToMap_FlipXY(targetW, playerReader.WorldMapArea); - float heading = DirectionCalculator.CalculateMapHeading(playerM, targetM); - AdjustHeading(heading, ct); + UpdateTotalRoute(); + } - stuckDetector.SetTargetLocation(targetW); - UpdateTotalRoute(); - } - } + public void ResetStuckParameters() + { + stuckDetector.Reset(); + } - private void PathRequest(PathRequest pathRequest) + private void RefillRouteToNextWaypoint(CancellationToken ct) + { + routeToNextWaypoint.Clear(); + + Vector3 playerW = playerReader.WorldPos; + Vector3 targetW = wayPoints.Peek(); + float distance = playerW.WorldDistanceXYTo(targetW); + + if (distance > MaxDistance || distance > AvgDistance * 2) { - pathRequests.Enqueue(pathRequest); - manualReset.Set(); - } + if (debug) + LogDebug($"Distance: {distance} vs Avg:({AvgDistance * 2},{AvgDistance}) - TAVG: {DIFF_THRESHOLD * AvgDistance} "); - private void PathCalculatedCallback(PathResult result) + stopMoving.Stop(); + PathRequest(new PathRequest(playerReader.UIMapId.Value, playerW, targetW, distance, PathCalculatedCallback)); + } + else { - if (!active) - return; + if (debug) + LogDebug($"non pathfinder - {distance} - {playerW} -> {targetW}"); - if (result.Path.Length == 0) - { - if (lastFailedDestination != result.EndW) - { - lastFailedDestination = result.EndW; - LogPathfinderFailed(logger, result.StartW, result.EndW, result.ElapsedMs); - } + routeToNextWaypoint.Push(targetW); - failedAttempt++; - if (failedAttempt > 2) - { - failedAttempt = 0; - stuckDetector.SetTargetLocation(result.EndW); - stuckDetector.Update(); - } - return; - } + Vector3 playerM = WorldMapAreaDB.ToMap_FlipXY(playerW, playerReader.WorldMapArea); + Vector3 targetM = WorldMapAreaDB.ToMap_FlipXY(targetW, playerReader.WorldMapArea); + float heading = DirectionCalculator.CalculateMapHeading(playerM, targetM); + AdjustHeading(heading, ct); - failedAttempt = 0; - LogPathfinderSuccess(logger, result.Distance, result.StartW, result.EndW, result.ElapsedMs); + stuckDetector.SetTargetLocation(targetW); + UpdateTotalRoute(); + } + } - for (int i = result.Path.Length - 1; i >= 0; i--) - { - routeToNextWaypoint.Push(result.Path[i]); - } + private void PathRequest(PathRequest pathRequest) + { + pathRequests.Enqueue(pathRequest); + manualReset.Set(); + } - if (SimplifyRouteToWaypoint) - SimplyfyRouteToWaypoint(); + private void PathCalculatedCallback(PathResult result) + { + if (!active) + return; - if (routeToNextWaypoint.Count == 0) + if (result.Path.Length == 0) + { + if (lastFailedDestination != result.EndW) { - routeToNextWaypoint.Push(wayPoints.Peek()); + lastFailedDestination = result.EndW; + LogPathfinderFailed(logger, result.StartW, result.EndW, result.ElapsedMs); + } - if (debug) - LogDebug($"RefillRouteToNextWaypoint -- WayPoint reached! {wayPoints.Count}"); + failedAttempt++; + if (failedAttempt > 2) + { + failedAttempt = 0; + stuckDetector.SetTargetLocation(result.EndW); + stuckDetector.Update(); } + return; + } - stuckDetector.SetTargetLocation(routeToNextWaypoint.Peek()); - UpdateTotalRoute(); + failedAttempt = 0; + LogPathfinderSuccess(logger, result.Distance, result.StartW, result.EndW, result.ElapsedMs); - OnPathCalculated?.Invoke(); + for (int i = result.Path.Length - 1; i >= 0; i--) + { + routeToNextWaypoint.Push(result.Path[i]); } - private void PathFinderThread() + if (SimplifyRouteToWaypoint) + SimplyfyRouteToWaypoint(); + + if (routeToNextWaypoint.Count == 0) { - while (!_cts.IsCancellationRequested) - { - manualReset.Reset(); - if (pathRequests.TryPeek(out PathRequest pathRequest)) - { - Vector3[] path = pather.FindWorldRoute(pathRequest.MapId, pathRequest.StartW, pathRequest.EndW); - if (active) - { - pathResults.Enqueue(new PathResult(pathRequest, path, pathRequest.Callback)); - } - pathRequests.Dequeue(); - } - manualReset.WaitOne(); - } + routeToNextWaypoint.Push(wayPoints.Peek()); - if (logger.IsEnabled(LogLevel.Debug)) - logger.LogDebug("PathFinder thread stopped!"); + if (debug) + LogDebug($"RefillRouteToNextWaypoint -- WayPoint reached! {wayPoints.Count}"); } - private float ReachedDistance(float minDistance) - { - return mountHandler.IsMounted() ? MinDistanceMount : minDistance; - } + stuckDetector.SetTargetLocation(routeToNextWaypoint.Peek()); + UpdateTotalRoute(); - private void ReduceByDistance(Vector3 playerW, float minDistance) + OnPathCalculated?.Invoke(); + } + + private void PathFinderThread() + { + while (!_cts.IsCancellationRequested) { - float worldDistance = playerW.WorldDistanceXYTo(routeToNextWaypoint.Peek()); - while (worldDistance < ReachedDistance(minDistance) && routeToNextWaypoint.Count > 0) + manualReset.Reset(); + if (pathRequests.TryPeek(out PathRequest pathRequest)) { - routeToNextWaypoint.Pop(); - if (routeToNextWaypoint.Count > 0) + Vector3[] path = pather.FindWorldRoute(pathRequest.MapId, pathRequest.StartW, pathRequest.EndW); + if (active) { - worldDistance = playerW.WorldDistanceXYTo(routeToNextWaypoint.Peek()); + pathResults.Enqueue(new PathResult(pathRequest, path, pathRequest.Callback)); } + pathRequests.Dequeue(); } + manualReset.WaitOne(); } - private void AdjustHeading(float heading, CancellationToken ct) - { - const float RADIAN = PI * 2; + if (logger.IsEnabled(LogLevel.Debug)) + logger.LogDebug("PathFinder thread stopped!"); + } - float diff1 = Abs(RADIAN + heading - playerReader.Direction) % RADIAN; - float diff2 = Abs(heading - playerReader.Direction - RADIAN) % RADIAN; + private float ReachedDistance(float minDistance) + { + return mountHandler.IsMounted() ? MinDistanceMount : minDistance; + } - float diff = Min(diff1, diff2); - if (diff > minAngleToTurn) + private void ReduceByDistance(Vector3 playerW, float minDistance) + { + float worldDistance = playerW.WorldDistanceXYTo(routeToNextWaypoint.Peek()); + while (worldDistance < ReachedDistance(minDistance) && routeToNextWaypoint.Count > 0) + { + routeToNextWaypoint.Pop(); + if (routeToNextWaypoint.Count > 0) { - if (diff > minAngleToStopBeforeTurn) - { - stopMoving.Stop(); - } - - playerDirection.SetDirection(heading, routeToNextWaypoint.Peek(), MinDistance, ct); + worldDistance = playerW.WorldDistanceXYTo(routeToNextWaypoint.Peek()); } } + } - private bool AdjustNextWaypointPointToClosest() - { - if (wayPoints.Count < 2) { return false; } + private void AdjustHeading(float heading, CancellationToken ct) + { + const float RADIAN = PI * 2; - Vector3 A = wayPoints.Pop(); - Vector3 B = wayPoints.Peek(); - Vector2 result = VectorExt.GetClosestPointOnLineSegment(A.AsVector2(), B.AsVector2(), playerReader.WorldPos.AsVector2()); - Vector3 newPoint = new(result.X, result.Y, playerReader.WorldPosZ); + float diff1 = Abs(RADIAN + heading - playerReader.Direction) % RADIAN; + float diff2 = Abs(heading - playerReader.Direction - RADIAN) % RADIAN; - if (newPoint.WorldDistanceXYTo(wayPoints.Peek()) > MinDistance) + float diff = Min(diff1, diff2); + if (diff > minAngleToTurn) + { + if (diff > minAngleToStopBeforeTurn) { - wayPoints.Push(newPoint); - if (debug) - LogDebug("Adjusted resume point"); - - return false; + stopMoving.Stop(); } + playerDirection.SetDirection(heading, routeToNextWaypoint.Peek(), MinDistance, ct); + } + } + + private bool AdjustNextWaypointPointToClosest() + { + if (wayPoints.Count < 2) { return false; } + + Vector3 A = wayPoints.Pop(); + Vector3 B = wayPoints.Peek(); + Vector2 result = VectorExt.GetClosestPointOnLineSegment(A.AsVector2(), B.AsVector2(), playerReader.WorldPos.AsVector2()); + Vector3 newPoint = new(result.X, result.Y, playerReader.WorldPosZ); + + if (newPoint.WorldDistanceXYTo(wayPoints.Peek()) > MinDistance) + { + wayPoints.Push(newPoint); if (debug) - LogDebug("Skipped next point in path"); + LogDebug("Adjusted resume point"); - return true; + return false; } - private void V1_AttemptToKeepRouteToWaypoint() + if (debug) + LogDebug("Skipped next point in path"); + + return true; + } + + private void V1_AttemptToKeepRouteToWaypoint() + { + float totalDistance = VectorExt.TotalDistance(TotalRoute, VectorExt.WorldDistanceXY); + if (totalDistance > MaxDistance / 2) { - float totalDistance = VectorExt.TotalDistance(TotalRoute, VectorExt.WorldDistanceXY); - if (totalDistance > MaxDistance / 2) + Vector3 playerW = playerReader.WorldPos; + float distanceToRoute = playerW.WorldDistanceXYTo(routeToNextWaypoint.Peek()); + float distanceToPrevLoc = playerW.WorldDistanceXYTo(playerWorldPos); + if (distanceToRoute > 2 * MinDistanceMount && + distanceToPrevLoc > 2 * MinDistanceMount) { - Vector3 playerW = playerReader.WorldPos; - float distanceToRoute = playerW.WorldDistanceXYTo(routeToNextWaypoint.Peek()); - float distanceToPrevLoc = playerW.WorldDistanceXYTo(playerWorldPos); - if (distanceToRoute > 2 * MinDistanceMount && - distanceToPrevLoc > 2 * MinDistanceMount) - { - LogV1ClearRouteToWaypoint(logger, patherName, distanceToRoute); - routeToNextWaypoint.Clear(); - } - else - { - LogV1KeepRouteToWaypoint(logger, patherName, distanceToRoute); - ResetStuckParameters(); - } + LogV1ClearRouteToWaypoint(logger, patherName, distanceToRoute); + routeToNextWaypoint.Clear(); } else { - LogV1ClearRouteToWaypointTooFar(logger, patherName, totalDistance, MaxDistance / 2); - routeToNextWaypoint.Clear(); + LogV1KeepRouteToWaypoint(logger, patherName, distanceToRoute); + ResetStuckParameters(); } } - - private void SimplyfyRouteToWaypoint() + else { - const bool HighQuality = false; - Span reduced = PathSimplify.Simplify(routeToNextWaypoint.ToArray(), MinDistance / 2, HighQuality); - if (debug) - LogDebug($"{nameof(SimplyfyRouteToWaypoint)} {routeToNextWaypoint.Count} -> {reduced.Length} | HQ: {HighQuality}"); - + LogV1ClearRouteToWaypointTooFar(logger, patherName, totalDistance, MaxDistance / 2); routeToNextWaypoint.Clear(); - for (int i = reduced.Length - 1; i >= 0; i--) - { - routeToNextWaypoint.Push(reduced[i]); - } } + } - private void UpdateTotalRoute() - { - TotalRoute = new Vector3[routeToNextWaypoint.Count + wayPoints.Count]; - routeToNextWaypoint.CopyTo(TotalRoute, 0); - wayPoints.CopyTo(TotalRoute, routeToNextWaypoint.Count); - } + private void SimplyfyRouteToWaypoint() + { + const bool HighQuality = false; + Span reduced = PathSimplify.Simplify(routeToNextWaypoint.ToArray(), MinDistance / 2, HighQuality); + if (debug) + LogDebug($"{nameof(SimplyfyRouteToWaypoint)} {routeToNextWaypoint.Count} -> {reduced.Length} | HQ: {HighQuality}"); - private bool HasBeenActiveRecently() + routeToNextWaypoint.Clear(); + for (int i = reduced.Length - 1; i >= 0; i--) { - return (DateTime.UtcNow - LastActive).TotalSeconds < 2; + routeToNextWaypoint.Push(reduced[i]); } + } + + private void UpdateTotalRoute() + { + TotalRoute = new Vector3[routeToNextWaypoint.Count + wayPoints.Count]; + routeToNextWaypoint.CopyTo(TotalRoute, 0); + wayPoints.CopyTo(TotalRoute, routeToNextWaypoint.Count); + } + private bool HasBeenActiveRecently() + { + return (DateTime.UtcNow - LastActive).TotalSeconds < 2; + } - private void LogDebug(string text) - { - logger.LogDebug($"{nameof(Navigation)}: {text}"); - } - #region Logging - - [LoggerMessage( - EventId = 40, - Level = LogLevel.Warning, - Message = "Unable to find path {start} -> {end}. Character may stuck! {elapsedMs}ms")] - static partial void LogPathfinderFailed(ILogger logger, Vector3 start, Vector3 end, double elapsedMs); - - [LoggerMessage( - EventId = 41, - Level = LogLevel.Information, - Message = "Pathfinder - {distance} - {start} -> {end} {elapsedMs}ms")] - static partial void LogPathfinderSuccess(ILogger logger, float distance, Vector3 start, Vector3 end, double elapsedMs); - - [LoggerMessage( - EventId = 42, - Level = LogLevel.Information, - Message = "Clear route to waypoint! Stucked for {elapsedMs}ms")] - static partial void LogClearRouteToWaypointStuck(ILogger logger, double elapsedMs); - - [LoggerMessage( - EventId = 43, - Level = LogLevel.Information, - Message = "[{name}] distance from nearlest point is {distance}. Have to clear RouteToWaypoint.")] - static partial void LogV1ClearRouteToWaypoint(ILogger logger, string name, float distance); - - [LoggerMessage( - EventId = 44, - Level = LogLevel.Information, - Message = "[{name}] distance is close {distance}. Keep RouteToWaypoint.")] - static partial void LogV1KeepRouteToWaypoint(ILogger logger, string name, float distance); - - [LoggerMessage( - EventId = 45, - Level = LogLevel.Information, - Message = "[{name}] total distance {totalDistance} > {maxDistancehalf}. Have to clear RouteToWaypoint.")] - static partial void LogV1ClearRouteToWaypointTooFar(ILogger logger, string name, float totalDistance, float maxDistancehalf); - - #endregion + private void LogDebug(string text) + { + logger.LogDebug($"{nameof(Navigation)}: {text}"); } + + #region Logging + + [LoggerMessage( + EventId = 40, + Level = LogLevel.Warning, + Message = "Unable to find path {start} -> {end}. Character may stuck! {elapsedMs}ms")] + static partial void LogPathfinderFailed(ILogger logger, Vector3 start, Vector3 end, double elapsedMs); + + [LoggerMessage( + EventId = 41, + Level = LogLevel.Information, + Message = "Pathfinder - {distance} - {start} -> {end} {elapsedMs}ms")] + static partial void LogPathfinderSuccess(ILogger logger, float distance, Vector3 start, Vector3 end, double elapsedMs); + + [LoggerMessage( + EventId = 42, + Level = LogLevel.Information, + Message = "Clear route to waypoint! Stucked for {elapsedMs}ms")] + static partial void LogClearRouteToWaypointStuck(ILogger logger, double elapsedMs); + + [LoggerMessage( + EventId = 43, + Level = LogLevel.Information, + Message = "[{name}] distance from nearlest point is {distance}. Have to clear RouteToWaypoint.")] + static partial void LogV1ClearRouteToWaypoint(ILogger logger, string name, float distance); + + [LoggerMessage( + EventId = 44, + Level = LogLevel.Information, + Message = "[{name}] distance is close {distance}. Keep RouteToWaypoint.")] + static partial void LogV1KeepRouteToWaypoint(ILogger logger, string name, float distance); + + [LoggerMessage( + EventId = 45, + Level = LogLevel.Information, + Message = "[{name}] total distance {totalDistance} > {maxDistancehalf}. Have to clear RouteToWaypoint.")] + static partial void LogV1ClearRouteToWaypointTooFar(ILogger logger, string name, float totalDistance, float maxDistancehalf); + + #endregion } \ No newline at end of file diff --git a/Core/GoalsComponent/Navigation/PathRequest.cs b/Core/GoalsComponent/Navigation/PathRequest.cs index 20a9c0625..d4aa0778c 100644 --- a/Core/GoalsComponent/Navigation/PathRequest.cs +++ b/Core/GoalsComponent/Navigation/PathRequest.cs @@ -1,25 +1,24 @@ using System; using System.Numerics; -namespace Core.Goals +namespace Core.Goals; + +internal readonly struct PathRequest { - internal readonly struct PathRequest - { - public readonly int MapId; - public readonly Vector3 StartW; - public readonly Vector3 EndW; - public readonly float Distance; - public readonly Action Callback; - public readonly DateTime Time; + public readonly int MapId; + public readonly Vector3 StartW; + public readonly Vector3 EndW; + public readonly float Distance; + public readonly Action Callback; + public readonly DateTime Time; - public PathRequest(int mapId, Vector3 startW, Vector3 endW, float distance, Action callback) - { - MapId = mapId; - StartW = startW; - EndW = endW; - Distance = distance; - Callback = callback; - Time = DateTime.UtcNow; - } + public PathRequest(int mapId, Vector3 startW, Vector3 endW, float distance, Action callback) + { + MapId = mapId; + StartW = startW; + EndW = endW; + Distance = distance; + Callback = callback; + Time = DateTime.UtcNow; } } diff --git a/Core/GoalsComponent/Navigation/PathResult.cs b/Core/GoalsComponent/Navigation/PathResult.cs index 01100987f..5bef8ba15 100644 --- a/Core/GoalsComponent/Navigation/PathResult.cs +++ b/Core/GoalsComponent/Navigation/PathResult.cs @@ -1,25 +1,24 @@ using System; using System.Numerics; -namespace Core.Goals +namespace Core.Goals; + +internal readonly struct PathResult { - internal readonly struct PathResult - { - public readonly Vector3 StartW; - public readonly Vector3 EndW; - public readonly float Distance; - public readonly Vector3[] Path; - public readonly double ElapsedMs; - public readonly Action Callback; + public readonly Vector3 StartW; + public readonly Vector3 EndW; + public readonly float Distance; + public readonly Vector3[] Path; + public readonly double ElapsedMs; + public readonly Action Callback; - public PathResult(in PathRequest request, Vector3[] path, Action callback) - { - StartW = request.StartW; - EndW = request.EndW; - Distance = request.Distance; - Path = path; - Callback = callback; - ElapsedMs = (DateTime.UtcNow - request.Time).TotalMilliseconds; - } + public PathResult(in PathRequest request, Vector3[] path, Action callback) + { + StartW = request.StartW; + EndW = request.EndW; + Distance = request.Distance; + Path = path; + Callback = callback; + ElapsedMs = (DateTime.UtcNow - request.Time).TotalMilliseconds; } } diff --git a/Core/GoalsComponent/NpcNameTargeting.cs b/Core/GoalsComponent/NpcNameTargeting.cs index 475099df2..4e06a7f0d 100644 --- a/Core/GoalsComponent/NpcNameTargeting.cs +++ b/Core/GoalsComponent/NpcNameTargeting.cs @@ -7,212 +7,211 @@ using Game; using Microsoft.Extensions.Logging; -namespace Core.Goals +namespace Core.Goals; + +public sealed class NpcNameTargeting : IDisposable { - public sealed class NpcNameTargeting : IDisposable - { - private const int INTERACT_DELAY = 5; + private const int INTERACT_DELAY = 5; + + private readonly ILogger logger; + private readonly CancellationToken ct; + private readonly IWowScreen wowScreen; + private readonly NpcNameFinder npcNameFinder; + private readonly IMouseInput input; + private readonly IMouseOverReader mouseOverReader; + private readonly Wait wait; + + private readonly CursorClassifier classifier; + + private IBlacklist mouseOverBlacklist; - private readonly ILogger logger; - private readonly CancellationToken ct; - private readonly IWowScreen wowScreen; - private readonly NpcNameFinder npcNameFinder; - private readonly IMouseInput input; - private readonly IMouseOverReader mouseOverReader; - private readonly Wait wait; + private readonly Pen whitePen; - private readonly CursorClassifier classifier; + private int index; + private int npcCount = -1; - private IBlacklist mouseOverBlacklist; + public int NpcCount => npcNameFinder.NpcCount; - private readonly Pen whitePen; + public Point[] locTargeting { get; } + public Point[] locFindBy { get; } - private int index; - private int npcCount = -1; + public NpcNameTargeting(ILogger logger, CancellationTokenSource cts, + IWowScreen wowScreen, NpcNameFinder npcNameFinder, IMouseInput input, + IMouseOverReader mouseOverReader, IBlacklist blacklist, Wait wait) + { + this.logger = logger; + ct = cts.Token; + this.wowScreen = wowScreen; + this.npcNameFinder = npcNameFinder; + this.input = input; + this.mouseOverReader = mouseOverReader; + this.mouseOverBlacklist = blacklist; + this.wait = wait; - public int NpcCount => npcNameFinder.NpcCount; + classifier = new(); - public Point[] locTargeting { get; } - public Point[] locFindBy { get; } + whitePen = new Pen(Color.White, 3); - public NpcNameTargeting(ILogger logger, CancellationTokenSource cts, - IWowScreen wowScreen, NpcNameFinder npcNameFinder, IMouseInput input, - IMouseOverReader mouseOverReader, IBlacklist blacklist, Wait wait) + locTargeting = new Point[] { - this.logger = logger; - ct = cts.Token; - this.wowScreen = wowScreen; - this.npcNameFinder = npcNameFinder; - this.input = input; - this.mouseOverReader = mouseOverReader; - this.mouseOverBlacklist = blacklist; - this.wait = wait; + new Point(0, 0), + new Point(-10, 5).Scale(npcNameFinder.ScaleToRefWidth, npcNameFinder.ScaleToRefHeight), + new Point(10, 5).Scale(npcNameFinder.ScaleToRefWidth, npcNameFinder.ScaleToRefHeight), + }; - classifier = new(); + locFindBy = new Point[] + { + new Point(0, 0), + new Point(0, 15).Scale(npcNameFinder.ScaleToRefWidth, npcNameFinder.ScaleToRefHeight), - whitePen = new Pen(Color.White, 3); + new Point(0, 50).Scale(npcNameFinder.ScaleToRefWidth, npcNameFinder.ScaleToRefHeight), + new Point(-15, 50).Scale(npcNameFinder.ScaleToRefWidth, npcNameFinder.ScaleToRefHeight), + new Point(15, 50).Scale(npcNameFinder.ScaleToRefWidth, npcNameFinder.ScaleToRefHeight), - locTargeting = new Point[] - { - new Point(0, 0), - new Point(-10, 5).Scale(npcNameFinder.ScaleToRefWidth, npcNameFinder.ScaleToRefHeight), - new Point(10, 5).Scale(npcNameFinder.ScaleToRefWidth, npcNameFinder.ScaleToRefHeight), - }; + new Point(0, 100).Scale(npcNameFinder.ScaleToRefWidth, npcNameFinder.ScaleToRefHeight), + new Point(-15, 100).Scale(npcNameFinder.ScaleToRefWidth, npcNameFinder.ScaleToRefHeight), + new Point(15, 100).Scale(npcNameFinder.ScaleToRefWidth, npcNameFinder.ScaleToRefHeight), - locFindBy = new Point[] - { - new Point(0, 0), - new Point(0, 15).Scale(npcNameFinder.ScaleToRefWidth, npcNameFinder.ScaleToRefHeight), + new Point(0, 150).Scale(npcNameFinder.ScaleToRefWidth, npcNameFinder.ScaleToRefHeight), + new Point(-15, 150).Scale(npcNameFinder.ScaleToRefWidth, npcNameFinder.ScaleToRefHeight), + new Point(15, 150).Scale(npcNameFinder.ScaleToRefWidth, npcNameFinder.ScaleToRefHeight), - new Point(0, 50).Scale(npcNameFinder.ScaleToRefWidth, npcNameFinder.ScaleToRefHeight), - new Point(-15, 50).Scale(npcNameFinder.ScaleToRefWidth, npcNameFinder.ScaleToRefHeight), - new Point(15, 50).Scale(npcNameFinder.ScaleToRefWidth, npcNameFinder.ScaleToRefHeight), + new Point(0, 200).Scale(npcNameFinder.ScaleToRefWidth, npcNameFinder.ScaleToRefHeight), + new Point(-15, 200).Scale(npcNameFinder.ScaleToRefWidth, npcNameFinder.ScaleToRefHeight), + new Point(-15, 200).Scale(npcNameFinder.ScaleToRefWidth, npcNameFinder.ScaleToRefHeight), + }; + } - new Point(0, 100).Scale(npcNameFinder.ScaleToRefWidth, npcNameFinder.ScaleToRefHeight), - new Point(-15, 100).Scale(npcNameFinder.ScaleToRefWidth, npcNameFinder.ScaleToRefHeight), - new Point(15, 100).Scale(npcNameFinder.ScaleToRefWidth, npcNameFinder.ScaleToRefHeight), + public void Dispose() + { + classifier.Dispose(); + } - new Point(0, 150).Scale(npcNameFinder.ScaleToRefWidth, npcNameFinder.ScaleToRefHeight), - new Point(-15, 150).Scale(npcNameFinder.ScaleToRefWidth, npcNameFinder.ScaleToRefHeight), - new Point(15, 150).Scale(npcNameFinder.ScaleToRefWidth, npcNameFinder.ScaleToRefHeight), + public void UpdateBlacklist(IBlacklist blacklist) + { + this.mouseOverBlacklist = blacklist; - new Point(0, 200).Scale(npcNameFinder.ScaleToRefWidth, npcNameFinder.ScaleToRefHeight), - new Point(-15, 200).Scale(npcNameFinder.ScaleToRefWidth, npcNameFinder.ScaleToRefHeight), - new Point(-15, 200).Scale(npcNameFinder.ScaleToRefWidth, npcNameFinder.ScaleToRefHeight), - }; - } + logger.LogInformation($"{nameof(NpcNameTargeting)}: set blacklist to {blacklist.GetType().Name}"); + } - public void Dispose() - { - classifier.Dispose(); - } + public void ChangeNpcType(NpcNames npcNames) + { + npcNameFinder.ChangeNpcType(npcNames); + wowScreen.Enabled = npcNames != NpcNames.None; + } - public void UpdateBlacklist(IBlacklist blacklist) - { - this.mouseOverBlacklist = blacklist; + public void Reset() + { + npcCount = -1; + index = 0; + } - logger.LogInformation($"{nameof(NpcNameTargeting)}: set blacklist to {blacklist.GetType().Name}"); - } + public void WaitForUpdate() + { + npcNameFinder.WaitForUpdate(); + } - public void ChangeNpcType(NpcNames npcNames) - { - npcNameFinder.ChangeNpcType(npcNames); - wowScreen.Enabled = npcNames != NpcNames.None; - } + public bool FoundNpcName() + { + return npcNameFinder.NpcCount > 0; + } - public void Reset() + public bool AquireNonBlacklisted(CancellationToken ct) + { + if (npcCount != NpcCount) { - npcCount = -1; + npcCount = NpcCount; index = 0; } - public void WaitForUpdate() - { - npcNameFinder.WaitForUpdate(); - } + if (index > NpcCount - 1) + return false; + NpcPosition npc = npcNameFinder.Npcs[index]; - public bool FoundNpcName() + for (int i = 0; i < locTargeting.Length; i++) { - return npcNameFinder.NpcCount > 0; - } + if (ct.IsCancellationRequested) + return false; - public bool AquireNonBlacklisted(CancellationToken ct) - { - if (npcCount != NpcCount) - { - npcCount = NpcCount; - index = 0; - } + Point p = locTargeting[i]; + p.Offset(npc.ClickPoint); + p.Offset(npcNameFinder.ToScreenCoordinates()); - if (index > NpcCount - 1) - return false; - NpcPosition npc = npcNameFinder.Npcs[index]; + input.SetCursorPosition(p); + classifier.Classify(out CursorType cls); - for (int i = 0; i < locTargeting.Length; i++) + if (cls is CursorType.Kill) { - if (ct.IsCancellationRequested) - return false; + if (mouseOverReader.MouseOverId == 0) + wait.Update(); - Point p = locTargeting[i]; - p.Offset(npc.ClickPoint); - p.Offset(npcNameFinder.ToScreenCoordinates()); + if (mouseOverReader.MouseOverId == 0) + continue; - input.SetCursorPosition(p); - classifier.Classify(out CursorType cls); - - if (cls is CursorType.Kill) + if (mouseOverBlacklist.Is()) { - if (mouseOverReader.MouseOverId == 0) - wait.Update(); - - if (mouseOverReader.MouseOverId == 0) - continue; - - if (mouseOverBlacklist.Is()) - { - logger.LogInformation($"> NPCs {index} added to blacklist {mouseOverReader.MouseOverId} - {npc.Rect}"); - index++; - return false; - } - - logger.LogInformation($"> mouseover NPC found: {mouseOverReader.MouseOverId} - {npc.Rect}"); - input.InteractMouseOver(ct); - return true; + logger.LogInformation($"> NPCs {index} added to blacklist {mouseOverReader.MouseOverId} - {npc.Rect}"); + index++; + return false; } - ct.WaitHandle.WaitOne(1); + + logger.LogInformation($"> mouseover NPC found: {mouseOverReader.MouseOverId} - {npc.Rect}"); + input.InteractMouseOver(ct); + return true; } - return false; + ct.WaitHandle.WaitOne(1); } + return false; + } + + public bool FindBy(params CursorType[] cursor) + { + int c = locFindBy.Length; + const int e = 3; + Span attemptPoints = stackalloc Point[c + (c * e)]; - public bool FindBy(params CursorType[] cursor) + for (int ni = 0; ni < npcNameFinder.Npcs.Length; ni++) { - int c = locFindBy.Length; - const int e = 3; - Span attemptPoints = stackalloc Point[c + (c * e)]; + NpcPosition npc = npcNameFinder.Npcs[ni]; + for (int i = 0; i < c; i += e) + { + Point p = locFindBy[i]; + attemptPoints[i] = p; + attemptPoints[i + c] = new Point(npc.Rect.Width / 2, p.Y).Scale(npcNameFinder.ScaleToRefWidth, npcNameFinder.ScaleToRefHeight); + attemptPoints[i + c + 1] = new Point(-npc.Rect.Width / 2, p.Y).Scale(npcNameFinder.ScaleToRefWidth, npcNameFinder.ScaleToRefHeight); + } - for (int ni = 0; ni < npcNameFinder.Npcs.Length; ni++) + for (int pi = 0; pi < attemptPoints.Length; pi++) { - NpcPosition npc = npcNameFinder.Npcs[ni]; - for (int i = 0; i < c; i += e) - { - Point p = locFindBy[i]; - attemptPoints[i] = p; - attemptPoints[i + c] = new Point(npc.Rect.Width / 2, p.Y).Scale(npcNameFinder.ScaleToRefWidth, npcNameFinder.ScaleToRefHeight); - attemptPoints[i + c + 1] = new Point(-npc.Rect.Width / 2, p.Y).Scale(npcNameFinder.ScaleToRefWidth, npcNameFinder.ScaleToRefHeight); - } + Point p = attemptPoints[pi]; + p.Offset(npc.ClickPoint); + p.Offset(npcNameFinder.ToScreenCoordinates()); + input.SetCursorPosition(p); + + ct.WaitHandle.WaitOne(INTERACT_DELAY); - for (int pi = 0; pi < attemptPoints.Length; pi++) + classifier.Classify(out CursorType cls); + if (cursor.Contains(cls)) { - Point p = attemptPoints[pi]; - p.Offset(npc.ClickPoint); - p.Offset(npcNameFinder.ToScreenCoordinates()); - input.SetCursorPosition(p); - - ct.WaitHandle.WaitOne(INTERACT_DELAY); - - classifier.Classify(out CursorType cls); - if (cursor.Contains(cls)) - { - input.InteractMouseOver(ct); - logger.LogInformation($"> NPCs found: {npc.Rect}"); - return true; - } + input.InteractMouseOver(ct); + logger.LogInformation($"> NPCs found: {npc.Rect}"); + return true; } } - return false; } + return false; + } - public void ShowClickPositions(Graphics gr) + public void ShowClickPositions(Graphics gr) + { + for (int i = 0; i < npcNameFinder.Npcs.Length; i++) { - for (int i = 0; i < npcNameFinder.Npcs.Length; i++) + NpcPosition npc = npcNameFinder.Npcs[i]; + for (int j = 0; j < locFindBy.Length; j++) { - NpcPosition npc = npcNameFinder.Npcs[i]; - for (int j = 0; j < locFindBy.Length; j++) - { - Point p = locFindBy[j]; - p.Offset(npc.ClickPoint); - gr.DrawEllipse(whitePen, p.X, p.Y, 5, 5); - } + Point p = locFindBy[j]; + p.Offset(npc.ClickPoint); + gr.DrawEllipse(whitePen, p.X, p.Y, 5, 5); } } } diff --git a/Core/GoalsComponent/PlayerDirection.cs b/Core/GoalsComponent/PlayerDirection.cs index ad2a7fb29..6e1a92bdc 100644 --- a/Core/GoalsComponent/PlayerDirection.cs +++ b/Core/GoalsComponent/PlayerDirection.cs @@ -7,92 +7,91 @@ #pragma warning disable 162 -namespace Core +namespace Core; + +public sealed partial class PlayerDirection : IDisposable { - public sealed partial class PlayerDirection : IDisposable - { - private const bool debug = false; + private const bool debug = false; - private const float RADIAN = PI * 2; - private const int DefaultIgnoreDistance = 10; + private const float RADIAN = PI * 2; + private const int DefaultIgnoreDistance = 10; - private readonly ILogger logger; - private readonly ConfigurableInput input; - private readonly PlayerReader playerReader; - private readonly CancellationTokenSource _cts; + private readonly ILogger logger; + private readonly ConfigurableInput input; + private readonly PlayerReader playerReader; + private readonly CancellationTokenSource _cts; - public PlayerDirection(ILogger logger, ConfigurableInput input, PlayerReader playerReader) - { - this.logger = logger; - this.input = input; - this.playerReader = playerReader; - _cts = new(); - } + public PlayerDirection(ILogger logger, ConfigurableInput input, PlayerReader playerReader) + { + this.logger = logger; + this.input = input; + this.playerReader = playerReader; + _cts = new(); + } - public void Dispose() - { - _cts.Cancel(); - } + public void Dispose() + { + _cts.Cancel(); + } - public void SetDirection(float targetDir, Vector3 map) - { - SetDirection(targetDir, map, DefaultIgnoreDistance, _cts.Token); - } + public void SetDirection(float targetDir, Vector3 map) + { + SetDirection(targetDir, map, DefaultIgnoreDistance, _cts.Token); + } - public void SetDirection(float targetDir, Vector3 world, float ignoreDistance, CancellationToken ct) + public void SetDirection(float targetDir, Vector3 world, float ignoreDistance, CancellationToken ct) + { + float distance = playerReader.WorldPos.WorldDistanceXYTo(world); + if (distance < ignoreDistance) { - float distance = playerReader.WorldPos.WorldDistanceXYTo(world); - if (distance < ignoreDistance) - { - if (debug) - LogDebugClose(logger, distance, ignoreDistance); - - return; - } - if (debug) - LogDebugSetDirection(logger, playerReader.Direction, targetDir, distance); + LogDebugClose(logger, distance, ignoreDistance); - input.Proc.KeyPressSleep(GetDirectionKeyToPress(targetDir), - TurnDuration(targetDir), ct); + return; } - private float TurnAmount(float targetDir) - { - float result = (RADIAN + targetDir - playerReader.Direction) % RADIAN; + if (debug) + LogDebugSetDirection(logger, playerReader.Direction, targetDir, distance); - if (result > PI) - result = RADIAN - result; + input.Proc.KeyPressSleep(GetDirectionKeyToPress(targetDir), + TurnDuration(targetDir), ct); + } - return result; - } + private float TurnAmount(float targetDir) + { + float result = (RADIAN + targetDir - playerReader.Direction) % RADIAN; - private int TurnDuration(float targetDir) - { - return (int)(TurnAmount(targetDir) * 1000 / PI); - } + if (result > PI) + result = RADIAN - result; - private ConsoleKey GetDirectionKeyToPress(float desiredDirection) - { - return (RADIAN + desiredDirection - playerReader.Direction) % RADIAN < PI - ? input.Proc.TurnLeftKey - : input.Proc.TurnRightKey; - } + return result; + } + + private int TurnDuration(float targetDir) + { + return (int)(TurnAmount(targetDir) * 1000 / PI); + } - #region Logging + private ConsoleKey GetDirectionKeyToPress(float desiredDirection) + { + return (RADIAN + desiredDirection - playerReader.Direction) % RADIAN < PI + ? input.Proc.TurnLeftKey + : input.Proc.TurnRightKey; + } - [LoggerMessage( - EventId = 30, - Level = LogLevel.Debug, - Message = "SetDirection: Too close, ignored direction change. {distance} < {ignoreDistance}")] - static partial void LogDebugClose(ILogger logger, float distance, float ignoreDistance); + #region Logging - [LoggerMessage( - EventId = 31, - Level = LogLevel.Debug, - Message = "SetDirection: {direction:0.000} -> {desiredDirection:0.000} - {distance:0.000}")] - static partial void LogDebugSetDirection(ILogger logger, float direction, float desiredDirection, float distance); + [LoggerMessage( + EventId = 30, + Level = LogLevel.Debug, + Message = "SetDirection: Too close, ignored direction change. {distance} < {ignoreDistance}")] + static partial void LogDebugClose(ILogger logger, float distance, float ignoreDistance); - #endregion - } + [LoggerMessage( + EventId = 31, + Level = LogLevel.Debug, + Message = "SetDirection: {direction:0.000} -> {desiredDirection:0.000} - {distance:0.000}")] + static partial void LogDebugSetDirection(ILogger logger, float direction, float desiredDirection, float distance); + + #endregion } \ No newline at end of file diff --git a/Core/GoalsComponent/ReactCastError.cs b/Core/GoalsComponent/ReactCastError.cs index d739c0960..8ea5bf781 100644 --- a/Core/GoalsComponent/ReactCastError.cs +++ b/Core/GoalsComponent/ReactCastError.cs @@ -5,214 +5,213 @@ using System; using System.Numerics; -namespace Core +namespace Core; + +public sealed class ReactCastError { - public sealed class ReactCastError + private readonly ILogger logger; + private readonly AddonReader addonReader; + private readonly PlayerReader playerReader; + private readonly Wait wait; + private readonly ConfigurableInput input; + private readonly StopMoving stopMoving; + private readonly PlayerDirection direction; + + public ReactCastError(ILogger logger, AddonReader addonReader, Wait wait, ConfigurableInput input, StopMoving stopMoving, PlayerDirection direction) { - private readonly ILogger logger; - private readonly AddonReader addonReader; - private readonly PlayerReader playerReader; - private readonly Wait wait; - private readonly ConfigurableInput input; - private readonly StopMoving stopMoving; - private readonly PlayerDirection direction; - - public ReactCastError(ILogger logger, AddonReader addonReader, Wait wait, ConfigurableInput input, StopMoving stopMoving, PlayerDirection direction) - { - this.logger = logger; - this.addonReader = addonReader; - this.playerReader = addonReader.PlayerReader; - this.wait = wait; - this.input = input; - this.stopMoving = stopMoving; - this.direction = direction; - } + this.logger = logger; + this.addonReader = addonReader; + this.playerReader = addonReader.PlayerReader; + this.wait = wait; + this.input = input; + this.stopMoving = stopMoving; + this.direction = direction; + } - public void Do(KeyAction item, string source) + public void Do(KeyAction item, string source) + { + UI_ERROR value = (UI_ERROR)playerReader.CastEvent.Value; + switch (value) { - UI_ERROR value = (UI_ERROR)playerReader.CastEvent.Value; - switch (value) - { - case UI_ERROR.NONE: - case UI_ERROR.CAST_START: - case UI_ERROR.CAST_SUCCESS: - case UI_ERROR.SPELL_FAILED_TARGETS_DEAD: - break; - case UI_ERROR.ERR_SPELL_FAILED_INTERRUPTED: - item.SetClicked(); - break; - case UI_ERROR.SPELL_FAILED_NOT_READY: - /* + case UI_ERROR.NONE: + case UI_ERROR.CAST_START: + case UI_ERROR.CAST_SUCCESS: + case UI_ERROR.SPELL_FAILED_TARGETS_DEAD: + break; + case UI_ERROR.ERR_SPELL_FAILED_INTERRUPTED: + item.SetClicked(); + break; + case UI_ERROR.SPELL_FAILED_NOT_READY: + /* + int waitTime = Math.Max(playerReader.GCD.Value, playerReader.RemainCastMs); + logger.LogInformation($"{source} React to {value.ToStringF()} -- wait for GCD {waitTime}ms"); + if (waitTime > 0) + wait.Fixed(waitTime); + break; + */ + case UI_ERROR.ERR_SPELL_COOLDOWN: + logger.LogInformation($"{source} React to {value.ToStringF()} -- wait until its ready"); int waitTime = Math.Max(playerReader.GCD.Value, playerReader.RemainCastMs); - logger.LogInformation($"{source} React to {value.ToStringF()} -- wait for GCD {waitTime}ms"); - if (waitTime > 0) - wait.Fixed(waitTime); + bool before = addonReader.UsableAction.Is(item); + wait.Until(waitTime, () => before != addonReader.UsableAction.Is(item) || addonReader.UsableAction.Is(item)); break; - */ - case UI_ERROR.ERR_SPELL_COOLDOWN: - logger.LogInformation($"{source} React to {value.ToStringF()} -- wait until its ready"); - int waitTime = Math.Max(playerReader.GCD.Value, playerReader.RemainCastMs); - bool before = addonReader.UsableAction.Is(item); - wait.Until(waitTime, () => before != addonReader.UsableAction.Is(item) || addonReader.UsableAction.Is(item)); - break; - case UI_ERROR.ERR_ATTACK_PACIFIED: - case UI_ERROR.ERR_SPELL_FAILED_STUNNED: - int debuffCount = playerReader.AuraCount.PlayerDebuff; - if (debuffCount != 0) - { - logger.LogInformation($"{source} -- React to {value.ToStringF()} -- Wait till losing debuff!"); - wait.While(() => debuffCount == playerReader.AuraCount.PlayerDebuff); - } - else - { - logger.LogInformation($"{source} -- Didn't know how to react {value.ToStringF()} when PlayerDebuffCount: {debuffCount}"); - } + case UI_ERROR.ERR_ATTACK_PACIFIED: + case UI_ERROR.ERR_SPELL_FAILED_STUNNED: + int debuffCount = playerReader.AuraCount.PlayerDebuff; + if (debuffCount != 0) + { + logger.LogInformation($"{source} -- React to {value.ToStringF()} -- Wait till losing debuff!"); + wait.While(() => debuffCount == playerReader.AuraCount.PlayerDebuff); + } + else + { + logger.LogInformation($"{source} -- Didn't know how to react {value.ToStringF()} when PlayerDebuffCount: {debuffCount}"); + } - break; - case UI_ERROR.ERR_SPELL_OUT_OF_RANGE: + break; + case UI_ERROR.ERR_SPELL_OUT_OF_RANGE: - if (!playerReader.Bits.HasTarget()) - return; + if (!playerReader.Bits.HasTarget()) + return; - if (playerReader.Class == UnitClass.Hunter && playerReader.IsInMeleeRange()) - { - logger.LogInformation($"{source} -- As a Hunter didn't know how to react {value.ToStringF()}"); - return; - } + if (playerReader.Class == UnitClass.Hunter && playerReader.IsInMeleeRange()) + { + logger.LogInformation($"{source} -- As a Hunter didn't know how to react {value.ToStringF()}"); + return; + } - int minRange = playerReader.MinRange(); - if (playerReader.Bits.PlayerInCombat() && playerReader.Bits.HasTarget() && !playerReader.IsTargetCasting()) + int minRange = playerReader.MinRange(); + if (playerReader.Bits.PlayerInCombat() && playerReader.Bits.HasTarget() && !playerReader.IsTargetCasting()) + { + if (playerReader.TargetTarget == UnitsTarget.Me) { - if (playerReader.TargetTarget == UnitsTarget.Me) + if (playerReader.InCloseMeleeRange()) { - if (playerReader.InCloseMeleeRange()) - { - logger.LogInformation($"{source} -- React to {value.ToStringF()} -- ({minRange}) wait for close melee range."); - wait.Fixed(30); - wait.Update(); - return; - } - - logger.LogInformation($"{source} -- React to {value.ToStringF()} -- ({minRange}) Just wait for the target to get in range."); - - int duration = CastingHandler.GCD; - if (playerReader.MinRange() <= 5) - duration = CastingHandler.SPELL_QUEUE; - - (bool t, double e) = wait.Until(duration, - () => minRange != playerReader.MinRange() || playerReader.IsTargetCasting() - ); + logger.LogInformation($"{source} -- React to {value.ToStringF()} -- ({minRange}) wait for close melee range."); + wait.Fixed(30); wait.Update(); + return; } - } - else - { - double beforeDirection = playerReader.Direction; - input.Interact(); - input.StopAttack(); - stopMoving.Stop(); - wait.Update(); - if (beforeDirection != playerReader.Direction) - { - input.Interact(); + logger.LogInformation($"{source} -- React to {value.ToStringF()} -- ({minRange}) Just wait for the target to get in range."); - (bool t, double e) = wait.Until(CastingHandler.GCD, () => minRange != playerReader.MinRange()); - - logger.LogInformation($"{source} -- React to {value.ToStringF()} -- Approached target {minRange}->{playerReader.MinRange()}"); - } - else if (!playerReader.WithInPullRange()) - { - logger.LogInformation($"{source} -- React to {value.ToStringF()} -- Start moving forward as outside of pull range."); - input.Proc.SetKeyState(input.Proc.ForwardKey, true); - } - else - { - input.Interact(); - } - } - break; - case UI_ERROR.ERR_BADATTACKFACING: - if (playerReader.IsInMeleeRange()) - { - logger.LogInformation($"{source} -- React to {value.ToStringF()} -- Interact!"); - input.Interact(); - stopMoving.Stop(); - } - else - { - switch (playerReader.Class) - { - case UnitClass.None: - break; - case UnitClass.Monk: - case UnitClass.DemonHunter: - case UnitClass.Druid: - case UnitClass.DeathKnight: - case UnitClass.Warrior: - case UnitClass.Paladin: - case UnitClass.Rogue: - logger.LogInformation($"{source} -- React to {value.ToStringF()} -- Interact!"); - input.Interact(); - stopMoving.Stop(); - break; - case UnitClass.Hunter: - case UnitClass.Priest: - case UnitClass.Shaman: - case UnitClass.Mage: - case UnitClass.Warlock: - stopMoving.Stop(); - logger.LogInformation($"{source} -- React to {value.ToStringF()} -- Turning 180!"); - float desiredDirection = playerReader.Direction + MathF.PI; - desiredDirection = desiredDirection > MathF.PI * 2 ? desiredDirection - (MathF.PI * 2) : desiredDirection; - direction.SetDirection(desiredDirection, Vector3.Zero); - break; - } + int duration = CastingHandler.GCD; + if (playerReader.MinRange() <= 5) + duration = CastingHandler.SPELL_QUEUE; + (bool t, double e) = wait.Until(duration, + () => minRange != playerReader.MinRange() || playerReader.IsTargetCasting() + ); wait.Update(); } - break; - case UI_ERROR.SPELL_FAILED_MOVING: - logger.LogInformation($"{source} -- React to {value.ToStringF()} -- Stop moving!"); - wait.While(playerReader.Bits.IsFalling); + } + else + { + double beforeDirection = playerReader.Direction; + input.Interact(); + input.StopAttack(); stopMoving.Stop(); wait.Update(); - break; - case UI_ERROR.ERR_SPELL_FAILED_ANOTHER_IN_PROGRESS: - logger.LogInformation($"{source} -- React to {value.ToStringF()} -- Wait till casting!"); - wait.While(playerReader.IsCasting); - break; - case UI_ERROR.ERR_BADATTACKPOS: - if (playerReader.Bits.SpellOn_AutoAttack()) + + if (beforeDirection != playerReader.Direction) { - logger.LogInformation($"{source} -- React to {value.ToStringF()} -- Interact!"); input.Interact(); - stopMoving.Stop(); - wait.Update(); + + (bool t, double e) = wait.Until(CastingHandler.GCD, () => minRange != playerReader.MinRange()); + + logger.LogInformation($"{source} -- React to {value.ToStringF()} -- Approached target {minRange}->{playerReader.MinRange()}"); } - else + else if (!playerReader.WithInPullRange()) { - logger.LogInformation($"{source} -- Didn't know how to React to {value.ToStringF()}"); + logger.LogInformation($"{source} -- React to {value.ToStringF()} -- Start moving forward as outside of pull range."); + input.Proc.SetKeyState(input.Proc.ForwardKey, true); } - break; - case UI_ERROR.SPELL_FAILED_LINE_OF_SIGHT: - if (!playerReader.Bits.PlayerInCombat()) + else { - logger.LogInformation($"{source} -- React to {value.ToStringF()} -- Stop attack and clear target!"); - input.StopAttack(); - input.ClearTarget(); - wait.Update(); + input.Interact(); } - else + } + break; + case UI_ERROR.ERR_BADATTACKFACING: + if (playerReader.IsInMeleeRange()) + { + logger.LogInformation($"{source} -- React to {value.ToStringF()} -- Interact!"); + input.Interact(); + stopMoving.Stop(); + } + else + { + switch (playerReader.Class) { - logger.LogInformation($"{source} -- Didn't know how to React to {value.ToStringF()}"); + case UnitClass.None: + break; + case UnitClass.Monk: + case UnitClass.DemonHunter: + case UnitClass.Druid: + case UnitClass.DeathKnight: + case UnitClass.Warrior: + case UnitClass.Paladin: + case UnitClass.Rogue: + logger.LogInformation($"{source} -- React to {value.ToStringF()} -- Interact!"); + input.Interact(); + stopMoving.Stop(); + break; + case UnitClass.Hunter: + case UnitClass.Priest: + case UnitClass.Shaman: + case UnitClass.Mage: + case UnitClass.Warlock: + stopMoving.Stop(); + logger.LogInformation($"{source} -- React to {value.ToStringF()} -- Turning 180!"); + float desiredDirection = playerReader.Direction + MathF.PI; + desiredDirection = desiredDirection > MathF.PI * 2 ? desiredDirection - (MathF.PI * 2) : desiredDirection; + direction.SetDirection(desiredDirection, Vector3.Zero); + break; } - break; - default: + + wait.Update(); + } + break; + case UI_ERROR.SPELL_FAILED_MOVING: + logger.LogInformation($"{source} -- React to {value.ToStringF()} -- Stop moving!"); + wait.While(playerReader.Bits.IsFalling); + stopMoving.Stop(); + wait.Update(); + break; + case UI_ERROR.ERR_SPELL_FAILED_ANOTHER_IN_PROGRESS: + logger.LogInformation($"{source} -- React to {value.ToStringF()} -- Wait till casting!"); + wait.While(playerReader.IsCasting); + break; + case UI_ERROR.ERR_BADATTACKPOS: + if (playerReader.Bits.SpellOn_AutoAttack()) + { + logger.LogInformation($"{source} -- React to {value.ToStringF()} -- Interact!"); + input.Interact(); + stopMoving.Stop(); + wait.Update(); + } + else + { logger.LogInformation($"{source} -- Didn't know how to React to {value.ToStringF()}"); - break; - } + } + break; + case UI_ERROR.SPELL_FAILED_LINE_OF_SIGHT: + if (!playerReader.Bits.PlayerInCombat()) + { + logger.LogInformation($"{source} -- React to {value.ToStringF()} -- Stop attack and clear target!"); + input.StopAttack(); + input.ClearTarget(); + wait.Update(); + } + else + { + logger.LogInformation($"{source} -- Didn't know how to React to {value.ToStringF()}"); + } + break; + default: + logger.LogInformation($"{source} -- Didn't know how to React to {value.ToStringF()}"); + break; } - } + } diff --git a/Core/GoalsComponent/StopMoving.cs b/Core/GoalsComponent/StopMoving.cs index eeede6e16..baa463a53 100644 --- a/Core/GoalsComponent/StopMoving.cs +++ b/Core/GoalsComponent/StopMoving.cs @@ -4,88 +4,87 @@ using SharedLib.Extensions; using Game; -namespace Core.Goals +namespace Core.Goals; + +public sealed class StopMoving { - public sealed class StopMoving - { - private readonly WowProcessInput input; - private readonly PlayerReader playerReader; - private readonly CancellationToken ct; + private readonly WowProcessInput input; + private readonly PlayerReader playerReader; + private readonly CancellationToken ct; - private const float MinDist = 0.001f; + private const float MinDist = 0.001f; - private Vector3 mapPos; - private float direction; + private Vector3 mapPos; + private float direction; - public StopMoving(WowProcessInput input, PlayerReader playerReader, CancellationTokenSource cts) - { - this.input = input; - this.playerReader = playerReader; - ct = cts.Token; - } + public StopMoving(WowProcessInput input, PlayerReader playerReader, CancellationTokenSource cts) + { + this.input = input; + this.playerReader = playerReader; + ct = cts.Token; + } - public void Stop() - { - StopForward(); - StopTurn(); - } + public void Stop() + { + StopForward(); + StopTurn(); + } - public void StopForward() + public void StopForward() + { + if (mapPos != playerReader.MapPos) { - if (mapPos != playerReader.MapPos) + bool pressedAny = false; + + if (!input.IsKeyDown(input.BackwardKey) && + !input.IsKeyDown(input.ForwardKey) && + mapPos.MapDistanceXYTo(playerReader.MapPos) >= MinDist) + { + input.KeyPressSleep(input.ForwardKey, Random.Shared.Next(2, 5), ct); + pressedAny = true; + } + + if (input.IsKeyDown(input.ForwardKey)) { - bool pressedAny = false; - - if (!input.IsKeyDown(input.BackwardKey) && - !input.IsKeyDown(input.ForwardKey) && - mapPos.MapDistanceXYTo(playerReader.MapPos) >= MinDist) - { - input.KeyPressSleep(input.ForwardKey, Random.Shared.Next(2, 5), ct); - pressedAny = true; - } - - if (input.IsKeyDown(input.ForwardKey)) - { - input.SetKeyState(input.ForwardKey, false, true); - pressedAny = true; - } - - if (input.IsKeyDown(input.BackwardKey)) - { - input.SetKeyState(input.BackwardKey, false, true); - pressedAny = true; - } - - if (pressedAny) - ct.WaitHandle.WaitOne(Random.Shared.Next(25, 30)); + input.SetKeyState(input.ForwardKey, false, true); + pressedAny = true; } - mapPos = playerReader.MapPos; + if (input.IsKeyDown(input.BackwardKey)) + { + input.SetKeyState(input.BackwardKey, false, true); + pressedAny = true; + } + + if (pressedAny) + ct.WaitHandle.WaitOne(Random.Shared.Next(25, 30)); } - public void StopTurn() + mapPos = playerReader.MapPos; + } + + public void StopTurn() + { + if (direction != playerReader.Direction) { - if (direction != playerReader.Direction) + bool pressedAny = false; + + if (input.IsKeyDown(input.TurnLeftKey)) + { + input.SetKeyState(input.TurnLeftKey, false, true); + pressedAny = true; + } + + if (input.IsKeyDown(input.TurnRightKey)) { - bool pressedAny = false; - - if (input.IsKeyDown(input.TurnLeftKey)) - { - input.SetKeyState(input.TurnLeftKey, false, true); - pressedAny = true; - } - - if (input.IsKeyDown(input.TurnRightKey)) - { - input.SetKeyState(input.TurnRightKey, false, true); - pressedAny = true; - } - - if (pressedAny) - ct.WaitHandle.WaitOne(1); + input.SetKeyState(input.TurnRightKey, false, true); + pressedAny = true; } - direction = playerReader.Direction; + if (pressedAny) + ct.WaitHandle.WaitOne(1); } + + direction = playerReader.Direction; } } \ No newline at end of file diff --git a/Core/GoalsComponent/StuckDetector.cs b/Core/GoalsComponent/StuckDetector.cs index 216ace576..17dafc174 100644 --- a/Core/GoalsComponent/StuckDetector.cs +++ b/Core/GoalsComponent/StuckDetector.cs @@ -10,125 +10,124 @@ #pragma warning disable 162 -namespace Core +namespace Core; + +public sealed class StuckDetector { - public sealed class StuckDetector - { - private const bool debug = false; + private const bool debug = false; - private const float MIN_RANGE_DIFF = 2f; - private const float MIN_DISTANCE = 0.2f; - private const float MAX_RANGE = 999999; - private const double UNSTUCK_AFTER_MS = 2000; - private const double ACTION_STUCK_TIME = 3000; + private const float MIN_RANGE_DIFF = 2f; + private const float MIN_DISTANCE = 0.2f; + private const float MAX_RANGE = 999999; + private const double UNSTUCK_AFTER_MS = 2000; + private const double ACTION_STUCK_TIME = 3000; - private readonly ILogger logger; - private readonly ConfigurableInput input; + private readonly ILogger logger; + private readonly ConfigurableInput input; - private readonly PlayerReader playerReader; - private readonly PlayerDirection playerDirection; - private readonly StopMoving stopMoving; + private readonly PlayerReader playerReader; + private readonly PlayerDirection playerDirection; + private readonly StopMoving stopMoving; - private Vector3 worldTarget; + private Vector3 worldTarget; - private float prevDistance = MAX_RANGE; - private DateTime startTime; - private DateTime attemptTime; + private float prevDistance = MAX_RANGE; + private DateTime startTime; + private DateTime attemptTime; - public double ActionDurationMs => (DateTime.UtcNow - startTime).TotalMilliseconds; - private double UnstuckMs => (DateTime.UtcNow - attemptTime).TotalMilliseconds; + public double ActionDurationMs => (DateTime.UtcNow - startTime).TotalMilliseconds; + private double UnstuckMs => (DateTime.UtcNow - attemptTime).TotalMilliseconds; - public StuckDetector(ILogger logger, ConfigurableInput input, PlayerReader playerReader, PlayerDirection playerDirection, StopMoving stopMoving) - { - this.logger = logger; - this.input = input; + public StuckDetector(ILogger logger, ConfigurableInput input, PlayerReader playerReader, PlayerDirection playerDirection, StopMoving stopMoving) + { + this.logger = logger; + this.input = input; - this.playerReader = playerReader; - this.playerDirection = playerDirection; - this.stopMoving = stopMoving; + this.playerReader = playerReader; + this.playerDirection = playerDirection; + this.stopMoving = stopMoving; - Reset(); - } + Reset(); + } - public void SetTargetLocation(Vector3 worldTarget) + public void SetTargetLocation(Vector3 worldTarget) + { + if (this.worldTarget != worldTarget) { - if (this.worldTarget != worldTarget) - { - this.worldTarget = worldTarget; - Reset(); - } + this.worldTarget = worldTarget; + Reset(); } + } + + public void Reset() + { + attemptTime = DateTime.UtcNow; + startTime = DateTime.UtcNow; + + prevDistance = MAX_RANGE; + } + + public void Update() + { + if (playerReader.Bits.IsFalling()) + return; - public void Reset() + if (debug) + logger.LogDebug($"Stuck for {ActionDurationMs}ms, last tried to unstick {UnstuckMs}ms ago."); + + if (UnstuckMs > UNSTUCK_AFTER_MS) { - attemptTime = DateTime.UtcNow; - startTime = DateTime.UtcNow; + stopMoving.Stop(); - prevDistance = MAX_RANGE; - } + // Turn + ConsoleKey turnKey = Random.Shared.Next(2) == 0 ? input.Proc.TurnLeftKey : input.Proc.TurnRightKey; + int turnDuration = Random.Shared.Next(350); + logger.LogInformation($"Unstuck by turning for {turnDuration}ms"); + input.Proc.KeyPress(turnKey, turnDuration); + + // Move + ConsoleKey moveKey = Random.Shared.Next(100) >= 25 ? input.Proc.ForwardKey : input.Proc.BackwardKey; + int moveDuration = Random.Shared.Next(750) + 1000; + logger.LogInformation($"Unstuck by moving for {moveDuration}ms"); + input.Proc.KeyPress(moveKey, moveDuration); - public void Update() + input.Jump(); + + Vector3 targetM = WorldMapAreaDB.ToMap_FlipXY(worldTarget, playerReader.WorldMapArea); + float heading = DirectionCalculator.CalculateMapHeading(playerReader.MapPos, targetM); + playerDirection.SetDirection(heading, targetM); + + attemptTime = DateTime.UtcNow; + } + else { - if (playerReader.Bits.IsFalling()) - return; - - if (debug) - logger.LogDebug($"Stuck for {ActionDurationMs}ms, last tried to unstick {UnstuckMs}ms ago."); - - if (UnstuckMs > UNSTUCK_AFTER_MS) - { - stopMoving.Stop(); - - // Turn - ConsoleKey turnKey = Random.Shared.Next(2) == 0 ? input.Proc.TurnLeftKey : input.Proc.TurnRightKey; - int turnDuration = Random.Shared.Next(350); - logger.LogInformation($"Unstuck by turning for {turnDuration}ms"); - input.Proc.KeyPress(turnKey, turnDuration); - - // Move - ConsoleKey moveKey = Random.Shared.Next(100) >= 25 ? input.Proc.ForwardKey : input.Proc.BackwardKey; - int moveDuration = Random.Shared.Next(750) + 1000; - logger.LogInformation($"Unstuck by moving for {moveDuration}ms"); - input.Proc.KeyPress(moveKey, moveDuration); - - input.Jump(); - - Vector3 targetM = WorldMapAreaDB.ToMap_FlipXY(worldTarget, playerReader.WorldMapArea); - float heading = DirectionCalculator.CalculateMapHeading(playerReader.MapPos, targetM); - playerDirection.SetDirection(heading, targetM); - - attemptTime = DateTime.UtcNow; - } - else - { - input.Jump(); - } + input.Jump(); } + } - public bool IsGettingCloser() + public bool IsGettingCloser() + { + float distance = playerReader.WorldPos.WorldDistanceXYTo(worldTarget); + if (distance <= prevDistance - MIN_RANGE_DIFF) { - float distance = playerReader.WorldPos.WorldDistanceXYTo(worldTarget); - if (distance <= prevDistance - MIN_RANGE_DIFF) - { - Reset(); - prevDistance = distance; - return true; - } - - return ActionDurationMs < ACTION_STUCK_TIME; + Reset(); + prevDistance = distance; + return true; } - public bool IsMoving() + return ActionDurationMs < ACTION_STUCK_TIME; + } + + public bool IsMoving() + { + float distance = playerReader.WorldPos.WorldDistanceXYTo(worldTarget); + if (MathF.Abs(distance - prevDistance) > MIN_DISTANCE) { - float distance = playerReader.WorldPos.WorldDistanceXYTo(worldTarget); - if (MathF.Abs(distance - prevDistance) > MIN_DISTANCE) - { - Reset(); - prevDistance = distance; - return true; - } - - return ActionDurationMs < ACTION_STUCK_TIME; + Reset(); + prevDistance = distance; + return true; } + + return ActionDurationMs < ACTION_STUCK_TIME; } } \ No newline at end of file diff --git a/Core/GoalsComponent/TargetFinder.cs b/Core/GoalsComponent/TargetFinder.cs index 76773b89c..820419130 100644 --- a/Core/GoalsComponent/TargetFinder.cs +++ b/Core/GoalsComponent/TargetFinder.cs @@ -2,75 +2,74 @@ using System.Threading; using System; -namespace Core.Goals +namespace Core.Goals; + +public sealed class TargetFinder { - public sealed class TargetFinder - { - private const int minMs = 400, maxMs = 1000; + private const int minMs = 400, maxMs = 1000; - private readonly ConfigurableInput input; - private readonly ClassConfiguration classConfig; - private readonly PlayerReader playerReader; - private readonly NpcNameTargeting npcNameTargeting; - private readonly Wait wait; + private readonly ConfigurableInput input; + private readonly ClassConfiguration classConfig; + private readonly PlayerReader playerReader; + private readonly NpcNameTargeting npcNameTargeting; + private readonly Wait wait; - private DateTime lastActive; + private DateTime lastActive; - public int ElapsedMs => (int)(DateTime.UtcNow - lastActive).TotalMilliseconds; + public int ElapsedMs => (int)(DateTime.UtcNow - lastActive).TotalMilliseconds; - public TargetFinder(ConfigurableInput input, ClassConfiguration classConfig, - PlayerReader playerReader, NpcNameTargeting npcNameTargeting, Wait wait) - { - this.classConfig = classConfig; - this.input = input; - this.playerReader = playerReader; - this.npcNameTargeting = npcNameTargeting; - this.wait = wait; + public TargetFinder(ConfigurableInput input, ClassConfiguration classConfig, + PlayerReader playerReader, NpcNameTargeting npcNameTargeting, Wait wait) + { + this.classConfig = classConfig; + this.input = input; + this.playerReader = playerReader; + this.npcNameTargeting = npcNameTargeting; + this.wait = wait; - lastActive = DateTime.UtcNow; - } + lastActive = DateTime.UtcNow; + } - public void Reset() - { - npcNameTargeting.ChangeNpcType(NpcNames.None); - npcNameTargeting.Reset(); - } + public void Reset() + { + npcNameTargeting.ChangeNpcType(NpcNames.None); + npcNameTargeting.Reset(); + } + + public bool Search(NpcNames target, Func validTarget, CancellationToken ct) + { + return LookForTarget(target, ct) && validTarget(); + } + + private bool LookForTarget(NpcNames target, CancellationToken ct) + { + if (ElapsedMs < Random.Shared.Next(minMs, maxMs)) + return playerReader.Bits.HasTarget(); - public bool Search(NpcNames target, Func validTarget, CancellationToken ct) + if (!ct.IsCancellationRequested && + classConfig.TargetNearestTarget.GetRemainingCooldown() == 0) { - return LookForTarget(target, ct) && validTarget(); + input.NearestTarget(); + wait.Update(); } - private bool LookForTarget(NpcNames target, CancellationToken ct) + if (!ct.IsCancellationRequested && !classConfig.KeyboardOnly && !playerReader.Bits.HasTarget()) { - if (ElapsedMs < Random.Shared.Next(minMs, maxMs)) - return playerReader.Bits.HasTarget(); + npcNameTargeting.ChangeNpcType(target); + npcNameTargeting.WaitForUpdate(); if (!ct.IsCancellationRequested && - classConfig.TargetNearestTarget.GetRemainingCooldown() == 0) - { - input.NearestTarget(); - wait.Update(); - } - - if (!ct.IsCancellationRequested && !classConfig.KeyboardOnly && !playerReader.Bits.HasTarget()) + npcNameTargeting.NpcCount > 0 && + !input.Proc.IsKeyDown(input.Proc.TurnLeftKey) && + !input.Proc.IsKeyDown(input.Proc.TurnRightKey)) { - npcNameTargeting.ChangeNpcType(target); - npcNameTargeting.WaitForUpdate(); - - if (!ct.IsCancellationRequested && - npcNameTargeting.NpcCount > 0 && - !input.Proc.IsKeyDown(input.Proc.TurnLeftKey) && - !input.Proc.IsKeyDown(input.Proc.TurnRightKey)) - { - npcNameTargeting.AquireNonBlacklisted(ct); - } + npcNameTargeting.AquireNonBlacklisted(ct); } - - lastActive = DateTime.UtcNow; - - return playerReader.Bits.HasTarget(); } + lastActive = DateTime.UtcNow; + + return playerReader.Bits.HasTarget(); } + } diff --git a/Core/GoalsComponent/Wait.cs b/Core/GoalsComponent/Wait.cs index 298f46780..d6e7a06e2 100644 --- a/Core/GoalsComponent/Wait.cs +++ b/Core/GoalsComponent/Wait.cs @@ -2,114 +2,113 @@ using System.Runtime.CompilerServices; using System.Threading; -namespace Core +namespace Core; + +public sealed class Wait { - public sealed class Wait + private readonly AutoResetEvent globalTime; + private readonly CancellationToken ct; + + public Wait(AutoResetEvent globalTime, CancellationTokenSource cts) { - private readonly AutoResetEvent globalTime; - private readonly CancellationToken ct; + this.globalTime = globalTime; + this.ct = cts.Token; + } - public Wait(AutoResetEvent globalTime, CancellationTokenSource cts) - { - this.globalTime = globalTime; - this.ct = cts.Token; - } + public void Update() + { + globalTime.WaitOne(); + } - public void Update() - { - globalTime.WaitOne(); - } + public void Fixed(int durationMs) + { + ct.WaitHandle.WaitOne(durationMs); + } - public void Fixed(int durationMs) + [SkipLocalsInit] + public bool Till(int timeoutMs, Func interrupt) + { + DateTime start = DateTime.UtcNow; + while ((DateTime.UtcNow - start).TotalMilliseconds < timeoutMs) { - ct.WaitHandle.WaitOne(durationMs); + if (interrupt()) + return false; + + Update(); } - [SkipLocalsInit] - public bool Till(int timeoutMs, Func interrupt) - { - DateTime start = DateTime.UtcNow; - while ((DateTime.UtcNow - start).TotalMilliseconds < timeoutMs) - { - if (interrupt()) - return false; + return true; + } - Update(); - } + [SkipLocalsInit] + public WaitResult Until(int timeoutMs, Func interrupt) + { + DateTime start = DateTime.UtcNow; + double elapsedMs; + while ((elapsedMs = (DateTime.UtcNow - start).TotalMilliseconds) < timeoutMs) + { + if (interrupt()) + return new(false, elapsedMs); - return true; + Update(); } - [SkipLocalsInit] - public WaitResult Until(int timeoutMs, Func interrupt) - { - DateTime start = DateTime.UtcNow; - double elapsedMs; - while ((elapsedMs = (DateTime.UtcNow - start).TotalMilliseconds) < timeoutMs) - { - if (interrupt()) - return new(false, elapsedMs); + return new(true, elapsedMs); + } - Update(); - } + [SkipLocalsInit] + public WaitResult Until(int timeoutMs, CancellationToken token) + { + DateTime start = DateTime.UtcNow; + double elapsedMs; + while ((elapsedMs = (DateTime.UtcNow - start).TotalMilliseconds) < timeoutMs) + { + if (token.IsCancellationRequested) + return new(false, elapsedMs); - return new(true, elapsedMs); + Update(); } - [SkipLocalsInit] - public WaitResult Until(int timeoutMs, CancellationToken token) - { - DateTime start = DateTime.UtcNow; - double elapsedMs; - while ((elapsedMs = (DateTime.UtcNow - start).TotalMilliseconds) < timeoutMs) - { - if (token.IsCancellationRequested) - return new(false, elapsedMs); + return new(true, elapsedMs); + } - Update(); - } + [SkipLocalsInit] + public WaitResult Until(int timeoutMs, Func interrupt, Action repeat) + { + DateTime start = DateTime.UtcNow; + double elapsedMs; + while ((elapsedMs = (DateTime.UtcNow - start).TotalMilliseconds) < timeoutMs) + { + repeat.Invoke(); + if (interrupt()) + return new(false, elapsedMs); - return new(true, elapsedMs); + Update(); } - [SkipLocalsInit] - public WaitResult Until(int timeoutMs, Func interrupt, Action repeat) - { - DateTime start = DateTime.UtcNow; - double elapsedMs; - while ((elapsedMs = (DateTime.UtcNow - start).TotalMilliseconds) < timeoutMs) - { - repeat.Invoke(); - if (interrupt()) - return new(false, elapsedMs); - - Update(); - } - - return new(true, elapsedMs); - } + return new(true, elapsedMs); + } - public WaitResult UntilNot(int timeoutMs, Func interrupt) + public WaitResult UntilNot(int timeoutMs, Func interrupt) + { + DateTime start = DateTime.UtcNow; + double elapsedMs; + while ((elapsedMs = (DateTime.UtcNow - start).TotalMilliseconds) < timeoutMs) { - DateTime start = DateTime.UtcNow; - double elapsedMs; - while ((elapsedMs = (DateTime.UtcNow - start).TotalMilliseconds) < timeoutMs) - { - if (!interrupt()) - return new(false, elapsedMs); + if (!interrupt()) + return new(false, elapsedMs); - Update(); - } - - return new(true, elapsedMs); + Update(); } - public void While(Func condition) + return new(true, elapsedMs); + } + + public void While(Func condition) + { + while (condition()) { - while (condition()) - { - Update(); - } + Update(); } } } diff --git a/Core/GoalsComponent/WaitResult.cs b/Core/GoalsComponent/WaitResult.cs index e08c5654f..716830ed2 100644 --- a/Core/GoalsComponent/WaitResult.cs +++ b/Core/GoalsComponent/WaitResult.cs @@ -1,20 +1,19 @@ -namespace Core +namespace Core; + +public readonly struct WaitResult { - public readonly struct WaitResult - { - private readonly bool timeout; - private readonly double elapsedMs; + private readonly bool timeout; + private readonly double elapsedMs; - public WaitResult(bool timeout, double elapsedMs) - { - this.timeout = timeout; - this.elapsedMs = elapsedMs; - } + public WaitResult(bool timeout, double elapsedMs) + { + this.timeout = timeout; + this.elapsedMs = elapsedMs; + } - public void Deconstruct(out bool timeout, out double elapsedMs) - { - timeout = this.timeout; - elapsedMs = this.elapsedMs; - } + public void Deconstruct(out bool timeout, out double elapsedMs) + { + timeout = this.timeout; + elapsedMs = this.elapsedMs; } } diff --git a/Core/GoalsFactory/GoalFactory.cs b/Core/GoalsFactory/GoalFactory.cs index ccfb1fc39..8cebd8948 100644 --- a/Core/GoalsFactory/GoalFactory.cs +++ b/Core/GoalsFactory/GoalFactory.cs @@ -12,262 +12,261 @@ using System.Threading; using static Newtonsoft.Json.JsonConvert; -namespace Core +namespace Core; + +public static class GoalFactory { - public static class GoalFactory + public static IServiceScope CreateGoals(ILogger logger, AddonReader addonReader, + ConfigurableInput input, DataConfig dataConfig, NpcNameFinder npcNameFinder, + NpcNameTargeting npcNameTargeting, IPPather pather, ExecGameCommand execGameCommand, + ClassConfiguration classConfig, GoapAgentState goapAgentState, CancellationTokenSource cts, Wait wait) { - public static IServiceScope CreateGoals(ILogger logger, AddonReader addonReader, - ConfigurableInput input, DataConfig dataConfig, NpcNameFinder npcNameFinder, - NpcNameTargeting npcNameTargeting, IPPather pather, ExecGameCommand execGameCommand, - ClassConfiguration classConfig, GoapAgentState goapAgentState, CancellationTokenSource cts, Wait wait) + ServiceCollection services = new(); + + services.AddSingleton(logger); + services.AddSingleton(addonReader); + services.AddSingleton(addonReader.PlayerReader); + services.AddSingleton(input); + services.AddSingleton(input.Proc); + services.AddSingleton(npcNameFinder); + services.AddSingleton(npcNameTargeting); + services.AddSingleton(pather); + services.AddSingleton(execGameCommand); + services.AddSingleton(addonReader.WorldMapAreaDb); + services.AddSingleton(classConfig); + services.AddSingleton(goapAgentState); + services.AddSingleton(cts); + services.AddSingleton(wait); + + // TODO: Should be scoped as it comes from ClassConfig + // 432 issue + Vector3[] mapRoute = GetPath(classConfig, dataConfig); + services.AddSingleton(mapRoute); + + if (classConfig.Mode != Mode.Grind) { - ServiceCollection services = new(); - - services.AddSingleton(logger); - services.AddSingleton(addonReader); - services.AddSingleton(addonReader.PlayerReader); - services.AddSingleton(input); - services.AddSingleton(input.Proc); - services.AddSingleton(npcNameFinder); - services.AddSingleton(npcNameTargeting); - services.AddSingleton(pather); - services.AddSingleton(execGameCommand); - services.AddSingleton(addonReader.WorldMapAreaDb); - services.AddSingleton(classConfig); - services.AddSingleton(goapAgentState); - services.AddSingleton(cts); - services.AddSingleton(wait); - - // TODO: Should be scoped as it comes from ClassConfig - // 432 issue - Vector3[] mapRoute = GetPath(classConfig, dataConfig); - services.AddSingleton(mapRoute); - - if (classConfig.Mode != Mode.Grind) - { - services.AddScoped(); - } - else - { - services.AddScoped(); - services.AddScoped(); - services.AddScoped(); - } + services.AddScoped(); + } + else + { + services.AddScoped(); + services.AddScoped(); + services.AddScoped(); + } - // Goals components - services.AddScoped(); - services.AddScoped(); - services.AddScoped(); - services.AddScoped(); - services.AddScoped(); - services.AddScoped(); - services.AddScoped(); + // Goals components + services.AddScoped(); + services.AddScoped(); + services.AddScoped(); + services.AddScoped(); + services.AddScoped(); + services.AddScoped(); + services.AddScoped(); - if (addonReader.PlayerReader.Class is UnitClass.Druid) - { - logger.LogInformation($"[{nameof(GoalFactory)}] {nameof(IMountHandler)} is {nameof(DruidMountHandler)}"); + if (addonReader.PlayerReader.Class is UnitClass.Druid) + { + logger.LogInformation($"[{nameof(GoalFactory)}] {nameof(IMountHandler)} is {nameof(DruidMountHandler)}"); - services.AddScoped(); - services.AddScoped(); - } - else - { - logger.LogInformation($"[{nameof(GoalFactory)}] {nameof(IMountHandler)} is {nameof(MountHandler)}"); - services.AddScoped(); - } + services.AddScoped(); + services.AddScoped(); + } + else + { + logger.LogInformation($"[{nameof(GoalFactory)}] {nameof(IMountHandler)} is {nameof(MountHandler)}"); + services.AddScoped(); + } - services.AddScoped(); + services.AddScoped(); - // each GoapGoal gets an individual instance - services.AddTransient(); + // each GoapGoal gets an individual instance + services.AddTransient(); - if (classConfig.Mode == Mode.CorpseRun) + if (classConfig.Mode == Mode.CorpseRun) + { + services.AddScoped(); + } + else if (classConfig.Mode == Mode.AttendedGather) + { + services.AddScoped(); + services.AddScoped(); + services.AddScoped(); + services.AddScoped(); + services.AddScoped(); + + ResolveLootAndSkin(services, classConfig); + + ResolvePetClass(services, addonReader.PlayerReader.Class); + + if (classConfig.Parallel.Sequence.Length > 0) { - services.AddScoped(); + services.AddScoped(); } - else if (classConfig.Mode == Mode.AttendedGather) - { - services.AddScoped(); - services.AddScoped(); - services.AddScoped(); - services.AddScoped(); - services.AddScoped(); - ResolveLootAndSkin(services, classConfig); + ResolveAdhocGoals(services, classConfig); + ResolveAdhocNPCGoal(services, classConfig, dataConfig); + ResolveWaitGoal(services, classConfig); + } + else if (classConfig.Mode == Mode.AssistFocus) + { + services.AddScoped(); + services.AddScoped(); + services.AddScoped(); - ResolvePetClass(services, addonReader.PlayerReader.Class); + ResolveLootAndSkin(services, classConfig); - if (classConfig.Parallel.Sequence.Length > 0) - { - services.AddScoped(); - } + services.AddScoped(); + services.AddScoped(); - ResolveAdhocGoals(services, classConfig); - ResolveAdhocNPCGoal(services, classConfig, dataConfig); - ResolveWaitGoal(services, classConfig); + if (classConfig.Parallel.Sequence.Length > 0) + { + services.AddScoped(); } - else if (classConfig.Mode == Mode.AssistFocus) + + ResolveAdhocGoals(services, classConfig); + } + else if (classConfig.Mode is Mode.Grind or Mode.AttendedGrind) + { + if (classConfig.Mode == Mode.AttendedGrind) + { + services.AddScoped(); + } + else { - services.AddScoped(); - services.AddScoped(); - services.AddScoped(); + services.AddScoped(); + } - ResolveLootAndSkin(services, classConfig); + services.AddScoped(); + services.AddScoped(); + services.AddScoped(); + services.AddScoped(); + + if (classConfig.WrongZone.ZoneId > 0) + { + services.AddScoped(); + } - services.AddScoped(); - services.AddScoped(); + ResolveLootAndSkin(services, classConfig); - if (classConfig.Parallel.Sequence.Length > 0) - { - services.AddScoped(); - } + ResolvePetClass(services, addonReader.PlayerReader.Class); - ResolveAdhocGoals(services, classConfig); - } - else if (classConfig.Mode is Mode.Grind or Mode.AttendedGrind) + if (classConfig.Parallel.Sequence.Length > 0) { - if (classConfig.Mode == Mode.AttendedGrind) - { - services.AddScoped(); - } - else - { - services.AddScoped(); - } - - services.AddScoped(); - services.AddScoped(); - services.AddScoped(); - services.AddScoped(); - - if (classConfig.WrongZone.ZoneId > 0) - { - services.AddScoped(); - } - - ResolveLootAndSkin(services, classConfig); - - ResolvePetClass(services, addonReader.PlayerReader.Class); - - if (classConfig.Parallel.Sequence.Length > 0) - { - services.AddScoped(); - } - - ResolveAdhocGoals(services, classConfig); - ResolveAdhocNPCGoal(services, classConfig, dataConfig); - ResolveWaitGoal(services, classConfig); + services.AddScoped(); } - ServiceProvider provider = services.BuildServiceProvider( - new ServiceProviderOptions { ValidateOnBuild = true, ValidateScopes = true }); - - IServiceScope scope = provider.CreateScope(); - return scope; + ResolveAdhocGoals(services, classConfig); + ResolveAdhocNPCGoal(services, classConfig, dataConfig); + ResolveWaitGoal(services, classConfig); } - private static void ResolveLootAndSkin(ServiceCollection services, ClassConfiguration classConfig) + ServiceProvider provider = services.BuildServiceProvider( + new ServiceProviderOptions { ValidateOnBuild = true, ValidateScopes = true }); + + IServiceScope scope = provider.CreateScope(); + return scope; + } + + private static void ResolveLootAndSkin(ServiceCollection services, ClassConfiguration classConfig) + { + services.AddScoped(); + services.AddScoped(); + + if (classConfig.Loot) { - services.AddScoped(); - services.AddScoped(); + services.AddScoped(); - if (classConfig.Loot) + if (classConfig.GatherCorpse) { - services.AddScoped(); - - if (classConfig.GatherCorpse) - { - services.AddScoped(); - } + services.AddScoped(); } } + } - private static void ResolveAdhocGoals(ServiceCollection services, ClassConfiguration classConfig) + private static void ResolveAdhocGoals(ServiceCollection services, ClassConfiguration classConfig) + { + for (int i = 0; i < classConfig.Adhoc.Sequence.Length; i++) { - for (int i = 0; i < classConfig.Adhoc.Sequence.Length; i++) - { - KeyAction keyAction = classConfig.Adhoc.Sequence[i]; - services.AddScoped(x => new(keyAction, - x.GetRequiredService(), - x.GetRequiredService(), x.GetRequiredService(), - x.GetRequiredService(), x.GetRequiredService(), - x.GetRequiredService(), x.GetRequiredService())); - } + KeyAction keyAction = classConfig.Adhoc.Sequence[i]; + services.AddScoped(x => new(keyAction, + x.GetRequiredService(), + x.GetRequiredService(), x.GetRequiredService(), + x.GetRequiredService(), x.GetRequiredService(), + x.GetRequiredService(), x.GetRequiredService())); } + } - private static void ResolveAdhocNPCGoal(ServiceCollection services, ClassConfiguration classConfig, DataConfig dataConfig) + private static void ResolveAdhocNPCGoal(ServiceCollection services, ClassConfiguration classConfig, DataConfig dataConfig) + { + for (int i = 0; i < classConfig.NPC.Sequence.Length; i++) { - for (int i = 0; i < classConfig.NPC.Sequence.Length; i++) - { - KeyAction keyAction = classConfig.NPC.Sequence[i]; - keyAction.Path = GetPath(keyAction, dataConfig); - - services.AddScoped(x => new(keyAction, - x.GetRequiredService(), x.GetRequiredService(), - x.GetRequiredService(), x.GetRequiredService(), - x.GetRequiredService(), x.GetRequiredService(), - x.GetRequiredService(), x.GetRequiredService(), - x.GetRequiredService(), x.GetRequiredService(), - x.GetRequiredService())); - } + KeyAction keyAction = classConfig.NPC.Sequence[i]; + keyAction.Path = GetPath(keyAction, dataConfig); + + services.AddScoped(x => new(keyAction, + x.GetRequiredService(), x.GetRequiredService(), + x.GetRequiredService(), x.GetRequiredService(), + x.GetRequiredService(), x.GetRequiredService(), + x.GetRequiredService(), x.GetRequiredService(), + x.GetRequiredService(), x.GetRequiredService(), + x.GetRequiredService())); } + } - private static void ResolveWaitGoal(ServiceCollection services, ClassConfiguration classConfig) + private static void ResolveWaitGoal(ServiceCollection services, ClassConfiguration classConfig) + { + for (int i = 0; i < classConfig.Wait.Sequence.Length; i++) { - for (int i = 0; i < classConfig.Wait.Sequence.Length; i++) - { - KeyAction keyAction = classConfig.Wait.Sequence[i]; + KeyAction keyAction = classConfig.Wait.Sequence[i]; - services.AddScoped(x => new(keyAction, - x.GetRequiredService(), x.GetRequiredService())); - } + services.AddScoped(x => new(keyAction, + x.GetRequiredService(), x.GetRequiredService())); } + } - private static void ResolvePetClass(ServiceCollection services, UnitClass @class) + private static void ResolvePetClass(ServiceCollection services, UnitClass @class) + { + if (@class is + UnitClass.Hunter or + UnitClass.Warlock or + UnitClass.Mage or + UnitClass.DeathKnight) { - if (@class is - UnitClass.Hunter or - UnitClass.Warlock or - UnitClass.Mage or - UnitClass.DeathKnight) - { - services.AddScoped(); - } + services.AddScoped(); } + } - private static string RelativeFilePath(DataConfig dataConfig, string path) - { - return !path.Contains(dataConfig.Path) ? Join(dataConfig.Path, path) : path; - } + private static string RelativeFilePath(DataConfig dataConfig, string path) + { + return !path.Contains(dataConfig.Path) ? Join(dataConfig.Path, path) : path; + } - private static Vector3[] GetPath(ClassConfiguration classConfig, DataConfig dataConfig) - { - classConfig.PathFilename = RelativeFilePath(dataConfig, classConfig.PathFilename); + private static Vector3[] GetPath(ClassConfiguration classConfig, DataConfig dataConfig) + { + classConfig.PathFilename = RelativeFilePath(dataConfig, classConfig.PathFilename); - Vector3[] rawPath = DeserializeObject(ReadAllText(classConfig.PathFilename))!; - if (!classConfig.PathReduceSteps) - return rawPath; + Vector3[] rawPath = DeserializeObject(ReadAllText(classConfig.PathFilename))!; + if (!classConfig.PathReduceSteps) + return rawPath; - int step = 2; - int reducedLength = rawPath.Length % step == 0 ? - rawPath.Length / step : - (rawPath.Length / step) + 1; + int step = 2; + int reducedLength = rawPath.Length % step == 0 ? + rawPath.Length / step : + (rawPath.Length / step) + 1; - Vector3[] path = new Vector3[reducedLength]; - for (int i = 0; i < path.Length; i++) - { - path[i] = rawPath[i * step]; - } - return path; + Vector3[] path = new Vector3[reducedLength]; + for (int i = 0; i < path.Length; i++) + { + path[i] = rawPath[i * step]; } + return path; + } - public static Vector3[] GetPath(KeyAction keyAction, DataConfig dataConfig) - { - if (string.IsNullOrEmpty(keyAction.PathFilename)) - return Array.Empty(); + public static Vector3[] GetPath(KeyAction keyAction, DataConfig dataConfig) + { + if (string.IsNullOrEmpty(keyAction.PathFilename)) + return Array.Empty(); - return DeserializeObject(ReadAllText(RelativeFilePath(dataConfig, keyAction.PathFilename)))!; - } + return DeserializeObject(ReadAllText(RelativeFilePath(dataConfig, keyAction.PathFilename)))!; } } \ No newline at end of file diff --git a/Core/Gossip/Gossip.cs b/Core/Gossip/Gossip.cs index a8ef62f8c..a503f689c 100644 --- a/Core/Gossip/Gossip.cs +++ b/Core/Gossip/Gossip.cs @@ -1,37 +1,35 @@ -namespace Core +namespace Core; + +public enum Gossip { - public enum Gossip - { - Banker, - Battlemaster, - Binder, - Gossip, - Healer, - Petition, - Tabard, - Taxi, - Trainer, - Unlearn, - Vendor - } + Banker, + Battlemaster, + Binder, + Gossip, + Healer, + Petition, + Tabard, + Taxi, + Trainer, + Unlearn, + Vendor +} - public static class Gossip_Extension +public static class Gossip_Extension +{ + public static string ToStringF(this Gossip value) => value switch { - public static string ToStringF(this Gossip value) => value switch - { - Gossip.Banker => nameof(Gossip.Banker), - Gossip.Battlemaster => nameof(Gossip.Battlemaster), - Gossip.Binder => nameof(Gossip.Binder), - Gossip.Gossip => nameof(Gossip.Gossip), - Gossip.Healer => nameof(Gossip.Healer), - Gossip.Petition => nameof(Gossip.Petition), - Gossip.Tabard => nameof(Gossip.Tabard), - Gossip.Taxi => nameof(Gossip.Taxi), - Gossip.Trainer => nameof(Gossip.Trainer), - Gossip.Unlearn => nameof(Gossip.Unlearn), - Gossip.Vendor => nameof(Gossip.Vendor), - _ => throw new System.NotImplementedException() - }; - } - + Gossip.Banker => nameof(Gossip.Banker), + Gossip.Battlemaster => nameof(Gossip.Battlemaster), + Gossip.Binder => nameof(Gossip.Binder), + Gossip.Gossip => nameof(Gossip.Gossip), + Gossip.Healer => nameof(Gossip.Healer), + Gossip.Petition => nameof(Gossip.Petition), + Gossip.Tabard => nameof(Gossip.Tabard), + Gossip.Taxi => nameof(Gossip.Taxi), + Gossip.Trainer => nameof(Gossip.Trainer), + Gossip.Unlearn => nameof(Gossip.Unlearn), + Gossip.Vendor => nameof(Gossip.Vendor), + _ => throw new System.NotImplementedException() + }; } diff --git a/Core/Gossip/GossipReader.cs b/Core/Gossip/GossipReader.cs index 70243a88a..811d4264c 100644 --- a/Core/Gossip/GossipReader.cs +++ b/Core/Gossip/GossipReader.cs @@ -1,62 +1,61 @@ using System.Collections.Generic; -namespace Core +namespace Core; + +public sealed class GossipReader { - public sealed class GossipReader - { - private readonly int cGossip; + private readonly int cGossip; - public int Count { private set; get; } - public Dictionary Gossips { get; } = new(); + public int Count { private set; get; } + public Dictionary Gossips { get; } = new(); - private int data; + private int data; - public bool Ready => Gossips.Count == Count; + public bool Ready => Gossips.Count == Count; - public bool GossipStart() => data == 69; - public bool GossipEnd() => data == 9999994; + public bool GossipStart() => data == 69; + public bool GossipEnd() => data == 9999994; - public bool MerchantWindowOpened() => data == 9999999; + public bool MerchantWindowOpened() => data == 9999999; - public bool MerchantWindowClosed() => data == 9999998; + public bool MerchantWindowClosed() => data == 9999998; - public bool MerchantWindowSelling() => data == 9999997; + public bool MerchantWindowSelling() => data == 9999997; - public bool MerchantWindowSellingFinished() => data == 9999996; + public bool MerchantWindowSellingFinished() => data == 9999996; - public bool GossipStartOrMerchantWindowOpened() => GossipStart() || MerchantWindowOpened(); + public bool GossipStartOrMerchantWindowOpened() => GossipStart() || MerchantWindowOpened(); - public GossipReader(int cGossip) - { - this.cGossip = cGossip; - } + public GossipReader(int cGossip) + { + this.cGossip = cGossip; + } - public void Read(IAddonDataProvider reader) + public void Read(IAddonDataProvider reader) + { + data = reader.GetInt(cGossip); + + // used for merchant window open state + if (MerchantWindowClosed() || + MerchantWindowOpened() || + MerchantWindowSelling() || + MerchantWindowSellingFinished() || + GossipEnd()) + return; + + if (data == 0 || GossipStart()) { - data = reader.GetInt(cGossip); - - // used for merchant window open state - if (MerchantWindowClosed() || - MerchantWindowOpened() || - MerchantWindowSelling() || - MerchantWindowSellingFinished() || - GossipEnd()) - return; - - if (data == 0 || GossipStart()) - { - Count = 0; - Gossips.Clear(); - return; - } - - // formula - // 10000 * count + 100 * index + value - Count = data / 10000; - int order = data / 100 % 100; - Gossip gossip = (Gossip)(data % 100); - - Gossips[gossip] = order; + Count = 0; + Gossips.Clear(); + return; } + + // formula + // 10000 * count + 100 * index + value + Count = data / 10000; + int order = data / 100 % 100; + Gossip gossip = (Gossip)(data % 100); + + Gossips[gossip] = order; } } diff --git a/Core/IBotController.cs b/Core/IBotController.cs index 974a82527..ad7cea8c8 100644 --- a/Core/IBotController.cs +++ b/Core/IBotController.cs @@ -4,39 +4,38 @@ using Core.Session; using Game; -namespace Core +namespace Core; + +public interface IBotController { - public interface IBotController - { - bool IsBotActive { get; } - AddonReader AddonReader { get; } - WowScreen WowScreen { get; } - string SelectedClassFilename { get; } - string? SelectedPathFilename { get; } - ClassConfiguration? ClassConfig { get; } - GoapAgent? GoapAgent { get; } - RouteInfo? RouteInfo { get; } - IGrindSessionDAO GrindSessionDAO { get; } - double AvgScreenLatency { get; } - double AvgNPCLatency { get; } + bool IsBotActive { get; } + AddonReader AddonReader { get; } + WowScreen WowScreen { get; } + string SelectedClassFilename { get; } + string? SelectedPathFilename { get; } + ClassConfiguration? ClassConfig { get; } + GoapAgent? GoapAgent { get; } + RouteInfo? RouteInfo { get; } + IGrindSessionDAO GrindSessionDAO { get; } + double AvgScreenLatency { get; } + double AvgNPCLatency { get; } - event Action? ProfileLoaded; - event Action? StatusChanged; + event Action? ProfileLoaded; + event Action? StatusChanged; - void ToggleBotStatus(); + void ToggleBotStatus(); - void MinimapNodeFound(); + void MinimapNodeFound(); - void Shutdown(); + void Shutdown(); - IEnumerable ClassFiles(); + IEnumerable ClassFiles(); - IEnumerable PathFiles(); + IEnumerable PathFiles(); - void LoadClassProfile(string classFilename); + void LoadClassProfile(string classFilename); - void LoadPathProfile(string pathFilename); + void LoadPathProfile(string pathFilename); - void OverrideClassConfig(ClassConfiguration classConfig); - } + void OverrideClassConfig(ClassConfiguration classConfig); } \ No newline at end of file diff --git a/Core/Input/ConfigurableInput.cs b/Core/Input/ConfigurableInput.cs index 94f5bf319..b06ff673e 100644 --- a/Core/Input/ConfigurableInput.cs +++ b/Core/Input/ConfigurableInput.cs @@ -1,148 +1,147 @@ using Game; -namespace Core -{ - public sealed class ConfigurableInput - { - public readonly WowProcessInput Proc; - public readonly ClassConfiguration ClassConfig; +namespace Core; - public readonly int defaultKeyPress = 50; - public readonly int fastKeyPress = 30; +public sealed class ConfigurableInput +{ + public readonly WowProcessInput Proc; + public readonly ClassConfiguration ClassConfig; - public ConfigurableInput(WowProcessInput wowProcessInput, ClassConfiguration classConfig) - { - this.Proc = wowProcessInput; - ClassConfig = classConfig; + public readonly int defaultKeyPress = 50; + public readonly int fastKeyPress = 30; - wowProcessInput.ForwardKey = classConfig.ForwardKey; - wowProcessInput.BackwardKey = classConfig.BackwardKey; - wowProcessInput.TurnLeftKey = classConfig.TurnLeftKey; - wowProcessInput.TurnRightKey = classConfig.TurnRightKey; + public ConfigurableInput(WowProcessInput wowProcessInput, ClassConfiguration classConfig) + { + this.Proc = wowProcessInput; + ClassConfig = classConfig; - wowProcessInput.InteractMouseover = classConfig.InteractMouseOver.ConsoleKey; - wowProcessInput.InteractMouseoverPress = classConfig.InteractMouseOver.PressDuration; - } + wowProcessInput.ForwardKey = classConfig.ForwardKey; + wowProcessInput.BackwardKey = classConfig.BackwardKey; + wowProcessInput.TurnLeftKey = classConfig.TurnLeftKey; + wowProcessInput.TurnRightKey = classConfig.TurnRightKey; - public void Stop() - { - Proc.KeyPress(Proc.ForwardKey, defaultKeyPress); - } + wowProcessInput.InteractMouseover = classConfig.InteractMouseOver.ConsoleKey; + wowProcessInput.InteractMouseoverPress = classConfig.InteractMouseOver.PressDuration; + } - public void Interact() - { - Proc.KeyPress(ClassConfig.Interact.ConsoleKey, defaultKeyPress); - ClassConfig.Interact.SetClicked(); - } + public void Stop() + { + Proc.KeyPress(Proc.ForwardKey, defaultKeyPress); + } - public void FastInteract() - { - Proc.KeyPress(ClassConfig.Interact.ConsoleKey, fastKeyPress); - ClassConfig.Interact.SetClicked(); - } + public void Interact() + { + Proc.KeyPress(ClassConfig.Interact.ConsoleKey, defaultKeyPress); + ClassConfig.Interact.SetClicked(); + } - public void ApproachOnCooldown() - { - if (ClassConfig.Approach.GetRemainingCooldown() == 0) - { - Proc.KeyPress(ClassConfig.Approach.ConsoleKey, fastKeyPress); - ClassConfig.Approach.SetClicked(); - } - } + public void FastInteract() + { + Proc.KeyPress(ClassConfig.Interact.ConsoleKey, fastKeyPress); + ClassConfig.Interact.SetClicked(); + } - public void Approach() + public void ApproachOnCooldown() + { + if (ClassConfig.Approach.GetRemainingCooldown() == 0) { - Proc.KeyPress(ClassConfig.Approach.ConsoleKey, ClassConfig.Approach.PressDuration); + Proc.KeyPress(ClassConfig.Approach.ConsoleKey, fastKeyPress); ClassConfig.Approach.SetClicked(); } + } - public void LastTarget() - { - Proc.KeyPress(ClassConfig.TargetLastTarget.ConsoleKey, defaultKeyPress); - ClassConfig.TargetLastTarget.SetClicked(); - } + public void Approach() + { + Proc.KeyPress(ClassConfig.Approach.ConsoleKey, ClassConfig.Approach.PressDuration); + ClassConfig.Approach.SetClicked(); + } - public void FastLastTarget() - { - Proc.KeyPress(ClassConfig.TargetLastTarget.ConsoleKey, fastKeyPress); - ClassConfig.TargetLastTarget.SetClicked(); - } + public void LastTarget() + { + Proc.KeyPress(ClassConfig.TargetLastTarget.ConsoleKey, defaultKeyPress); + ClassConfig.TargetLastTarget.SetClicked(); + } - public void StandUp() - { - Proc.KeyPress(ClassConfig.StandUp.ConsoleKey, defaultKeyPress); - ClassConfig.StandUp.SetClicked(); - } + public void FastLastTarget() + { + Proc.KeyPress(ClassConfig.TargetLastTarget.ConsoleKey, fastKeyPress); + ClassConfig.TargetLastTarget.SetClicked(); + } - public void ClearTarget() - { - Proc.KeyPress(ClassConfig.ClearTarget.ConsoleKey, defaultKeyPress); - ClassConfig.ClearTarget.SetClicked(); - } + public void StandUp() + { + Proc.KeyPress(ClassConfig.StandUp.ConsoleKey, defaultKeyPress); + ClassConfig.StandUp.SetClicked(); + } - public void StopAttack() - { - Proc.KeyPress(ClassConfig.StopAttack.ConsoleKey, ClassConfig.StopAttack.PressDuration); - ClassConfig.StopAttack.SetClicked(); - } + public void ClearTarget() + { + Proc.KeyPress(ClassConfig.ClearTarget.ConsoleKey, defaultKeyPress); + ClassConfig.ClearTarget.SetClicked(); + } - public void NearestTarget() - { - Proc.KeyPress(ClassConfig.TargetNearestTarget.ConsoleKey, defaultKeyPress); - ClassConfig.TargetNearestTarget.SetClicked(); - } + public void StopAttack() + { + Proc.KeyPress(ClassConfig.StopAttack.ConsoleKey, ClassConfig.StopAttack.PressDuration); + ClassConfig.StopAttack.SetClicked(); + } - public void TargetPet() - { - Proc.KeyPress(ClassConfig.TargetPet.ConsoleKey, defaultKeyPress); - ClassConfig.TargetPet.SetClicked(); - } + public void NearestTarget() + { + Proc.KeyPress(ClassConfig.TargetNearestTarget.ConsoleKey, defaultKeyPress); + ClassConfig.TargetNearestTarget.SetClicked(); + } - public void TargetOfTarget() - { - Proc.KeyPress(ClassConfig.TargetTargetOfTarget.ConsoleKey, defaultKeyPress); - ClassConfig.TargetTargetOfTarget.SetClicked(); - } + public void TargetPet() + { + Proc.KeyPress(ClassConfig.TargetPet.ConsoleKey, defaultKeyPress); + ClassConfig.TargetPet.SetClicked(); + } - public void Jump() - { - Proc.KeyPress(ClassConfig.Jump.ConsoleKey, defaultKeyPress); - ClassConfig.Jump.SetClicked(); - } + public void TargetOfTarget() + { + Proc.KeyPress(ClassConfig.TargetTargetOfTarget.ConsoleKey, defaultKeyPress); + ClassConfig.TargetTargetOfTarget.SetClicked(); + } - public void PetAttack() - { - Proc.KeyPress(ClassConfig.PetAttack.ConsoleKey, ClassConfig.PetAttack.PressDuration); - ClassConfig.PetAttack.SetClicked(); - } + public void Jump() + { + Proc.KeyPress(ClassConfig.Jump.ConsoleKey, defaultKeyPress); + ClassConfig.Jump.SetClicked(); + } - public void Hearthstone() - { - Proc.KeyPress(ClassConfig.Hearthstone.ConsoleKey, defaultKeyPress); - ClassConfig.Hearthstone.SetClicked(); - } + public void PetAttack() + { + Proc.KeyPress(ClassConfig.PetAttack.ConsoleKey, ClassConfig.PetAttack.PressDuration); + ClassConfig.PetAttack.SetClicked(); + } - public void Mount() - { - Proc.KeyPress(ClassConfig.Mount.ConsoleKey, defaultKeyPress); - ClassConfig.Mount.SetClicked(); - } + public void Hearthstone() + { + Proc.KeyPress(ClassConfig.Hearthstone.ConsoleKey, defaultKeyPress); + ClassConfig.Hearthstone.SetClicked(); + } - public void Dismount() - { - Proc.KeyPress(ClassConfig.Mount.ConsoleKey, defaultKeyPress); - } + public void Mount() + { + Proc.KeyPress(ClassConfig.Mount.ConsoleKey, defaultKeyPress); + ClassConfig.Mount.SetClicked(); + } - public void TargetFocus() - { - Proc.KeyPress(ClassConfig.TargetFocus.ConsoleKey, defaultKeyPress); - ClassConfig.TargetFocus.SetClicked(); - } + public void Dismount() + { + Proc.KeyPress(ClassConfig.Mount.ConsoleKey, defaultKeyPress); + } - public void FollowTarget() - { - Proc.KeyPress(ClassConfig.FollowTarget.ConsoleKey, defaultKeyPress); - ClassConfig.FollowTarget.SetClicked(); - } + public void TargetFocus() + { + Proc.KeyPress(ClassConfig.TargetFocus.ConsoleKey, defaultKeyPress); + ClassConfig.TargetFocus.SetClicked(); + } + + public void FollowTarget() + { + Proc.KeyPress(ClassConfig.FollowTarget.ConsoleKey, defaultKeyPress); + ClassConfig.FollowTarget.SetClicked(); } } diff --git a/Core/Logging/LoggerSink.cs b/Core/Logging/LoggerSink.cs index 4892748a1..b9c825810 100644 --- a/Core/Logging/LoggerSink.cs +++ b/Core/Logging/LoggerSink.cs @@ -5,21 +5,20 @@ using System; -namespace Core +namespace Core; + +public sealed class LoggerSink : ILogEventSink { - public sealed class LoggerSink : ILogEventSink - { - public event Action? OnLogChanged; + public event Action? OnLogChanged; - public const int SIZE = 256; - private int callCount; - public LogEvent[] Log { get; private set; } = new LogEvent[SIZE]; - public int Head => callCount % SIZE; + public const int SIZE = 256; + private int callCount; + public LogEvent[] Log { get; private set; } = new LogEvent[SIZE]; + public int Head => callCount % SIZE; - public void Emit(LogEvent logEvent) - { - Log[callCount++ % SIZE] = logEvent; - OnLogChanged?.Invoke(); - } + public void Emit(LogEvent logEvent) + { + Log[callCount++ % SIZE] = logEvent; + OnLogChanged?.Invoke(); } } diff --git a/Core/Minimap/MinimapNodeEventArgs.cs b/Core/Minimap/MinimapNodeEventArgs.cs index b99f2347b..d4dae2c6b 100644 --- a/Core/Minimap/MinimapNodeEventArgs.cs +++ b/Core/Minimap/MinimapNodeEventArgs.cs @@ -1,18 +1,17 @@ using System; -namespace Core +namespace Core; + +public sealed class MinimapNodeEventArgs : EventArgs { - public sealed class MinimapNodeEventArgs : EventArgs - { - public int X { get; } - public int Y { get; } - public int Amount { get; } + public int X { get; } + public int Y { get; } + public int Amount { get; } - public MinimapNodeEventArgs(int x, int y, int amount) - { - X = x; - Y = y; - Amount = amount; - } + public MinimapNodeEventArgs(int x, int y, int amount) + { + X = x; + Y = y; + Amount = amount; } } \ No newline at end of file diff --git a/Core/Minimap/MinimapNodeFinder.cs b/Core/Minimap/MinimapNodeFinder.cs index 0005629ea..ce9c1e476 100644 --- a/Core/Minimap/MinimapNodeFinder.cs +++ b/Core/Minimap/MinimapNodeFinder.cs @@ -12,169 +12,168 @@ #pragma warning disable 162 -namespace Core +namespace Core; + +public sealed class MinimapNodeFinder { - public sealed class MinimapNodeFinder + private readonly struct PixelPoint { - private readonly struct PixelPoint - { - public readonly int X; - public readonly int Y; + public readonly int X; + public readonly int Y; - public PixelPoint(int x, int y) - { - X = x; - Y = y; - } + public PixelPoint(int x, int y) + { + X = x; + Y = y; } + } - private const bool DEBUG_MASK = false; + private const bool DEBUG_MASK = false; - private readonly ILogger logger; - private readonly WowScreen wowScreen; - public event EventHandler? NodeEvent; + private readonly ILogger logger; + private readonly WowScreen wowScreen; + public event EventHandler? NodeEvent; - private const int MinScore = 2; - private const int MaxBlue = 34; - private const int MinRedGreen = 176; + private const int MinScore = 2; + private const int MaxBlue = 34; + private const int MinRedGreen = 176; - public MinimapNodeFinder(ILogger logger, WowScreen wowScreen) - { - this.logger = logger; - this.wowScreen = wowScreen; - } + public MinimapNodeFinder(ILogger logger, WowScreen wowScreen) + { + this.logger = logger; + this.wowScreen = wowScreen; + } - public void Update() - { - var span = FindYellowPoints(); - ScorePoints(span, out PixelPoint best, out int amountAboveMin); - NodeEvent?.Invoke(this, new MinimapNodeEventArgs(best.X, best.Y, amountAboveMin)); - } + public void Update() + { + var span = FindYellowPoints(); + ScorePoints(span, out PixelPoint best, out int amountAboveMin); + NodeEvent?.Invoke(this, new MinimapNodeEventArgs(best.X, best.Y, amountAboveMin)); + } - private Span FindYellowPoints() - { - const int SIZE = 100; - var pooler = ArrayPool.Shared; - PixelPoint[] points = pooler.Rent(SIZE); + private Span FindYellowPoints() + { + const int SIZE = 100; + var pooler = ArrayPool.Shared; + PixelPoint[] points = pooler.Rent(SIZE); - Bitmap bitmap = wowScreen.MiniMapBitmap; + Bitmap bitmap = wowScreen.MiniMapBitmap; - // TODO: adjust these values based on resolution - // The reference resolution is 1920x1080 - const int minX = 6; - const int maxX = 170; - const int minY = 36; - int maxY = bitmap.Height - 6; + // TODO: adjust these values based on resolution + // The reference resolution is 1920x1080 + const int minX = 6; + const int maxX = 170; + const int minY = 36; + int maxY = bitmap.Height - 6; - Rectangle rect = new(minX, minY, maxX - minX, maxY - minY); - Point center = rect.Centre(); - float radius = (maxX - minX) / 2f; + Rectangle rect = new(minX, minY, maxX - minX, maxY - minY); + Point center = rect.Centre(); + float radius = (maxX - minX) / 2f; - int count = 0; + int count = 0; - unsafe - { - BitmapData data = bitmap.LockBits(new Rectangle(Point.Empty, bitmap.Size), - DEBUG_MASK ? ImageLockMode.ReadWrite : ImageLockMode.ReadOnly, bitmap.PixelFormat); - const int bytesPerPixel = 4; //Bitmap.GetPixelFormatSize(bitmap.PixelFormat) / 8; + unsafe + { + BitmapData data = bitmap.LockBits(new Rectangle(Point.Empty, bitmap.Size), + DEBUG_MASK ? ImageLockMode.ReadWrite : ImageLockMode.ReadOnly, bitmap.PixelFormat); + const int bytesPerPixel = 4; //Bitmap.GetPixelFormatSize(bitmap.PixelFormat) / 8; - Parallel.For(minY, maxY, y => + Parallel.For(minY, maxY, y => + { + byte* currentLine = (byte*)data.Scan0 + (y * data.Stride); + for (int x = minX; x < maxX; x++) { - byte* currentLine = (byte*)data.Scan0 + (y * data.Stride); - for (int x = minX; x < maxX; x++) + if (!IsValidSquareLocation(x, y, center, radius)) { - if (!IsValidSquareLocation(x, y, center, radius)) + if (DEBUG_MASK) { - if (DEBUG_MASK) - { - int xii = x * bytesPerPixel; - currentLine[xii + 2] = 0; - currentLine[xii + 1] = 0; - currentLine[xii + 0] = 0; - } - continue; + int xii = x * bytesPerPixel; + currentLine[xii + 2] = 0; + currentLine[xii + 1] = 0; + currentLine[xii + 0] = 0; } + continue; + } - int xi = x * bytesPerPixel; - if (IsMatch(currentLine[xi + 2], currentLine[xi + 1], currentLine[xi])) - { - if (count >= SIZE) - return; + int xi = x * bytesPerPixel; + if (IsMatch(currentLine[xi + 2], currentLine[xi + 1], currentLine[xi])) + { + if (count >= SIZE) + return; - points[count++] = new PixelPoint(x, y); + points[count++] = new PixelPoint(x, y); - if (DEBUG_MASK) - { - currentLine[xi + 2] = 255; - currentLine[xi + 1] = 0; - currentLine[xi + 0] = 0; - } + if (DEBUG_MASK) + { + currentLine[xi + 2] = 255; + currentLine[xi + 1] = 0; + currentLine[xi + 0] = 0; } } - }); - - bitmap.UnlockBits(data); - } + } + }); - if (count >= SIZE) - { - //logger.LogWarning("Too much yellow in this image!"); - } + bitmap.UnlockBits(data); + } - return points.AsSpan(0, count); + if (count >= SIZE) + { + //logger.LogWarning("Too much yellow in this image!"); + } - static bool IsValidSquareLocation(int x, int y, Point center, float width) - { - return MathF.Sqrt(((x - center.X) * (x - center.X)) + ((y - center.Y) * (y - center.Y))) < width; - } + return points.AsSpan(0, count); - static bool IsMatch(byte red, byte green, byte blue) - { - return blue < MaxBlue && red > MinRedGreen && green > MinRedGreen; - } + static bool IsValidSquareLocation(int x, int y, Point center, float width) + { + return MathF.Sqrt(((x - center.X) * (x - center.X)) + ((y - center.Y) * (y - center.Y))) < width; } - private static void ScorePoints(Span points, out PixelPoint best, out int amountAboveMin) + static bool IsMatch(byte red, byte green, byte blue) { - const int size = 5; - - best = new PixelPoint(); - amountAboveMin = 0; + return blue < MaxBlue && red > MinRedGreen && green > MinRedGreen; + } + } - int maxIndex = -1; - int maxScore = 0; + private static void ScorePoints(Span points, out PixelPoint best, out int amountAboveMin) + { + const int size = 5; - for (int i = 0; i < points.Length; i++) - { - PixelPoint pi = points[i]; + best = new PixelPoint(); + amountAboveMin = 0; - int score = 0; - for (int j = 0; j < points.Length; j++) - { - PixelPoint pj = points[j]; + int maxIndex = -1; + int maxScore = 0; - if (i != j && - (Math.Abs(pi.X - pj.X) < size || - Math.Abs(pi.Y - pj.Y) < size)) - { - score++; - } - } + for (int i = 0; i < points.Length; i++) + { + PixelPoint pi = points[i]; - if (score > MinScore) - amountAboveMin++; + int score = 0; + for (int j = 0; j < points.Length; j++) + { + PixelPoint pj = points[j]; - if (maxScore < score) + if (i != j && + (Math.Abs(pi.X - pj.X) < size || + Math.Abs(pi.Y - pj.Y) < size)) { - maxIndex = i; - maxScore = score; + score++; } } - if (maxIndex >= 0 && maxScore > MinScore) + if (score > MinScore) + amountAboveMin++; + + if (maxScore < score) { - best = points[maxIndex]; + maxIndex = i; + maxScore = score; } } + + if (maxIndex >= 0 && maxScore > MinScore) + { + best = points[maxIndex]; + } } } \ No newline at end of file diff --git a/Core/PPather/Client/AnTcpClient.cs b/Core/PPather/Client/AnTcpClient.cs index 241df2edf..198650224 100644 --- a/Core/PPather/Client/AnTcpClient.cs +++ b/Core/PPather/Client/AnTcpClient.cs @@ -4,82 +4,81 @@ using System.Net.Sockets; using System.Runtime.CompilerServices; -namespace AnTCP.Client +namespace AnTCP.Client; + +public sealed unsafe class AnTcpClient { - public sealed unsafe class AnTcpClient - { #pragma warning disable CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider declaring as nullable. - public AnTcpClient(string ip, int port) + public AnTcpClient(string ip, int port) #pragma warning restore CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider declaring as nullable. - { - Ip = ip; - Port = port; - } + { + Ip = ip; + Port = port; + } - public string Ip { get; } + public string Ip { get; } - public bool IsConnected => Client != null && Client.Connected; + public bool IsConnected => Client != null && Client.Connected; - public int Port { get; } + public int Port { get; } - private TcpClient Client { get; set; } + private TcpClient Client { get; set; } - private BinaryReader Reader { get; set; } + private BinaryReader Reader { get; set; } - private NetworkStream Stream { get; set; } + private NetworkStream Stream { get; set; } - /// - /// Connect to the server. - /// - public void Connect() - { - Client = new TcpClient(Ip, Port); - Stream = Client.GetStream(); - Reader = new BinaryReader(Stream); - } + /// + /// Connect to the server. + /// + public void Connect() + { + Client = new TcpClient(Ip, Port); + Stream = Client.GetStream(); + Reader = new BinaryReader(Stream); + } - /// - /// Disconnect from the server. - /// - public void Disconnect() - { - Stream.Close(); - Client.Close(); - } + /// + /// Disconnect from the server. + /// + public void Disconnect() + { + Stream.Close(); + Client.Close(); + } - /// - /// Send data to the server, data can be any unmanaged type. - /// - /// Unamnaged type of the - /// Mesagte type - /// Data to send - /// Server response - public AnTcpResponse Send(byte type, T data) where T : unmanaged - { - int size = sizeof(T); - return SendData(BitConverter.GetBytes(size + 1).AsSpan(), new Span(&type, 1), new Span(&data, size)); - } + /// + /// Send data to the server, data can be any unmanaged type. + /// + /// Unamnaged type of the + /// Mesagte type + /// Data to send + /// Server response + public AnTcpResponse Send(byte type, T data) where T : unmanaged + { + int size = sizeof(T); + return SendData(BitConverter.GetBytes(size + 1).AsSpan(), new Span(&type, 1), new Span(&data, size)); + } - /// - /// Send a byte array to the server. - /// - /// Mesagte type - /// Data to send - /// Server response - public AnTcpResponse SendBytes(byte type, byte[] data) - { - return SendData(BitConverter.GetBytes(data.Length + 1).AsSpan(), new Span(&type, 1), data.AsSpan()); - } + /// + /// Send a byte array to the server. + /// + /// Mesagte type + /// Data to send + /// Server response + public AnTcpResponse SendBytes(byte type, byte[] data) + { + return SendData(BitConverter.GetBytes(data.Length + 1).AsSpan(), new Span(&type, 1), data.AsSpan()); + } - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private AnTcpResponse SendData(Span size, Span type, Span data) - { - Stream.Write(size); - Stream.Write(type); - Stream.Write(data); + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private AnTcpResponse SendData(Span size, Span type, Span data) + { + Stream.Write(size); + Stream.Write(type); + Stream.Write(data); - byte[] response = Reader.ReadBytes(Reader.ReadInt32()); - return new AnTcpResponse(response[0], response[1..response.Length]); - } + byte[] response = Reader.ReadBytes(Reader.ReadInt32()); + return new AnTcpResponse(response[0], response[1..response.Length]); } } \ No newline at end of file diff --git a/Core/PPather/IPPather.cs b/Core/PPather/IPPather.cs index b5b8facd7..5e2c26f32 100644 --- a/Core/PPather/IPPather.cs +++ b/Core/PPather/IPPather.cs @@ -3,13 +3,12 @@ using System.Threading.Tasks; using System.Numerics; -namespace Core +namespace Core; + +public interface IPPather { - public interface IPPather - { - Vector3[] FindMapRoute(int uiMap, Vector3 mapFrom, Vector3 mapTo); - Vector3[] FindWorldRoute(int uiMap, Vector3 worldFrom, Vector3 worldTo); - ValueTask DrawLines(List lineArgs); - ValueTask DrawSphere(SphereArgs args); - } + Vector3[] FindMapRoute(int uiMap, Vector3 mapFrom, Vector3 mapTo); + Vector3[] FindWorldRoute(int uiMap, Vector3 worldFrom, Vector3 worldTo); + ValueTask DrawLines(List lineArgs); + ValueTask DrawSphere(SphereArgs args); } \ No newline at end of file diff --git a/Core/PPather/LocalPathingApi.cs b/Core/PPather/LocalPathingApi.cs index b7e19c0ae..d43be211e 100644 --- a/Core/PPather/LocalPathingApi.cs +++ b/Core/PPather/LocalPathingApi.cs @@ -12,110 +12,109 @@ #pragma warning disable 162 -namespace Core -{ - public sealed class LocalPathingApi : IPPather - { - private const bool debug = false; - - private readonly ILogger logger; +namespace Core; - private readonly PPatherService service; +public sealed class LocalPathingApi : IPPather +{ + private const bool debug = false; - private DateTime lastSave; + private readonly ILogger logger; - public LocalPathingApi(ILogger logger, PPatherService service, DataConfig dataConfig) - { - this.logger = logger; - this.service = service; - } + private readonly PPatherService service; - public ValueTask DrawLines(List lineArgs) - { - return ValueTask.CompletedTask; - } + private DateTime lastSave; - public ValueTask DrawSphere(SphereArgs args) - { - return ValueTask.CompletedTask; - } + public LocalPathingApi(ILogger logger, PPatherService service, DataConfig dataConfig) + { + this.logger = logger; + this.service = service; + } - public Vector3[] FindMapRoute(int uiMap, Vector3 mapFrom, Vector3 mapTo) - { - long timestamp = Stopwatch.GetTimestamp(); + public ValueTask DrawLines(List lineArgs) + { + return ValueTask.CompletedTask; + } - service.SetLocations( - service.ToWorld(uiMap, mapFrom.X, mapFrom.Y, mapFrom.Z), - service.ToWorld(uiMap, mapTo.X, mapTo.Y)); + public ValueTask DrawSphere(SphereArgs args) + { + return ValueTask.CompletedTask; + } - Path path = service.DoSearch(PathGraph.eSearchScoreSpot.A_Star_With_Model_Avoidance); - if (path == null) - { - if (debug) - LogWarning($"Failed to find a path from {mapFrom} to {mapTo} took {Stopwatch.GetElapsedTime(timestamp).TotalMilliseconds} ms."); + public Vector3[] FindMapRoute(int uiMap, Vector3 mapFrom, Vector3 mapTo) + { + long timestamp = Stopwatch.GetTimestamp(); - return Array.Empty(); - } + service.SetLocations( + service.ToWorld(uiMap, mapFrom.X, mapFrom.Y, mapFrom.Z), + service.ToWorld(uiMap, mapTo.X, mapTo.Y)); + Path path = service.DoSearch(PathGraph.eSearchScoreSpot.A_Star_With_Model_Avoidance); + if (path == null) + { if (debug) - LogDebug($"Finding route from {mapFrom} map {uiMap} to {mapTo} took {Stopwatch.GetElapsedTime(timestamp).TotalMilliseconds} ms."); - - if ((DateTime.UtcNow - lastSave).TotalMinutes >= 1) - { - service.Save(); - lastSave = DateTime.UtcNow; - } - - Vector3[] array = new Vector3[path.locations.Count]; - for (int i = 0; i < array.Length; i++) - { - array[i] = service.ToLocal(path.locations[i], (int)service.SearchFrom.W, uiMap); - } - return array; + LogWarning($"Failed to find a path from {mapFrom} to {mapTo} took {Stopwatch.GetElapsedTime(timestamp).TotalMilliseconds} ms."); + + return Array.Empty(); } - public Vector3[] FindWorldRoute(int uiMap, Vector3 worldFrom, Vector3 worldTo) + if (debug) + LogDebug($"Finding route from {mapFrom} map {uiMap} to {mapTo} took {Stopwatch.GetElapsedTime(timestamp).TotalMilliseconds} ms."); + + if ((DateTime.UtcNow - lastSave).TotalMinutes >= 1) { - long timestamp = Stopwatch.GetTimestamp(); + service.Save(); + lastSave = DateTime.UtcNow; + } - service.SetLocations( - service.ToWorldZ(uiMap, worldFrom.X, worldFrom.Y, worldFrom.Z), - service.ToWorldZ(uiMap, worldTo.X, worldTo.Y, worldTo.Z)); + Vector3[] array = new Vector3[path.locations.Count]; + for (int i = 0; i < array.Length; i++) + { + array[i] = service.ToLocal(path.locations[i], (int)service.SearchFrom.W, uiMap); + } + return array; + } - Path path = service.DoSearch(PathGraph.eSearchScoreSpot.A_Star_With_Model_Avoidance); - if (path == null) - { - if (debug) - LogWarning($"Failed to find a path from {worldFrom} to {worldTo} took {Stopwatch.GetElapsedTime(timestamp).TotalMilliseconds} ms."); + public Vector3[] FindWorldRoute(int uiMap, Vector3 worldFrom, Vector3 worldTo) + { + long timestamp = Stopwatch.GetTimestamp(); - return Array.Empty(); - } + service.SetLocations( + service.ToWorldZ(uiMap, worldFrom.X, worldFrom.Y, worldFrom.Z), + service.ToWorldZ(uiMap, worldTo.X, worldTo.Y, worldTo.Z)); + Path path = service.DoSearch(PathGraph.eSearchScoreSpot.A_Star_With_Model_Avoidance); + if (path == null) + { if (debug) - LogDebug($"Finding route from {worldFrom} map {uiMap} to {worldTo} took {Stopwatch.GetElapsedTime(timestamp).TotalMilliseconds} ms."); - - if ((DateTime.UtcNow - lastSave).TotalMinutes >= 1) - { - service.Save(); - lastSave = DateTime.UtcNow; - } + LogWarning($"Failed to find a path from {worldFrom} to {worldTo} took {Stopwatch.GetElapsedTime(timestamp).TotalMilliseconds} ms."); - return path.locations.ToArray(); + return Array.Empty(); } + if (debug) + LogDebug($"Finding route from {worldFrom} map {uiMap} to {worldTo} took {Stopwatch.GetElapsedTime(timestamp).TotalMilliseconds} ms."); - #region Logging - - private void LogDebug(string text) + if ((DateTime.UtcNow - lastSave).TotalMinutes >= 1) { - logger.LogDebug($"{nameof(LocalPathingApi)}: {text}"); + service.Save(); + lastSave = DateTime.UtcNow; } - private void LogWarning(string text) - { - logger.LogWarning($"{nameof(LocalPathingApi)}: {text}"); - } + return path.locations.ToArray(); + } + - #endregion + #region Logging + + private void LogDebug(string text) + { + logger.LogDebug($"{nameof(LocalPathingApi)}: {text}"); } + + private void LogWarning(string text) + { + logger.LogWarning($"{nameof(LocalPathingApi)}: {text}"); + } + + #endregion } \ No newline at end of file diff --git a/Core/PPather/Objects/AnTcpResponse.cs b/Core/PPather/Objects/AnTcpResponse.cs index 35662a78c..f32b64a45 100644 --- a/Core/PPather/Objects/AnTcpResponse.cs +++ b/Core/PPather/Objects/AnTcpResponse.cs @@ -1,64 +1,63 @@ using System; using System.Buffers; -namespace AnTCP.Client.Objects -{ +namespace AnTCP.Client.Objects; + #pragma warning disable CA1815 // Override equals and operator equals on value types - public readonly unsafe struct AnTcpResponse +public readonly unsafe struct AnTcpResponse #pragma warning restore CA1815 // Override equals and operator equals on value types +{ + public AnTcpResponse(byte type, byte[] data) { - public AnTcpResponse(byte type, byte[] data) - { - Type = type; - Data = data; - } + Type = type; + Data = data; + } - /// - /// Raw data received by the TCP client. - /// + /// + /// Raw data received by the TCP client. + /// #pragma warning disable CA1819 // Properties should not return arrays - public byte[] Data { get; } + public byte[] Data { get; } #pragma warning restore CA1819 // Properties should not return arrays - /// - /// Lenght of the data array. - /// - public int Length => Data.Length; + /// + /// Lenght of the data array. + /// + public int Length => Data.Length; - /// - /// Type of the response. - /// - public byte Type { get; } + /// + /// Type of the response. + /// + public byte Type { get; } - /// - /// Retrieve the data as any unmanaged type or struct. - /// - /// Unmanaged type - /// Data - public T As() where T : unmanaged => *Pointer(); + /// + /// Retrieve the data as any unmanaged type or struct. + /// + /// Unmanaged type + /// Data + public T As() where T : unmanaged => *Pointer(); - /// - /// Retrieve the data as any unmanaged type array. - /// - /// Unmanaged type - /// Data array - public T[] AsArray() where T : unmanaged - { - T[] array = new T[Length / sizeof(T)]; - using MemoryHandle h = array.AsMemory().Pin(); - Buffer.MemoryCopy(Pointer(), h.Pointer, Length, Length); - return array; - } + /// + /// Retrieve the data as any unmanaged type array. + /// + /// Unmanaged type + /// Data array + public T[] AsArray() where T : unmanaged + { + T[] array = new T[Length / sizeof(T)]; + using MemoryHandle h = array.AsMemory().Pin(); + Buffer.MemoryCopy(Pointer(), h.Pointer, Length, Length); + return array; + } - /// - /// Retrieve the data as any unamanaged pointer. - /// - /// Unmanaged type - /// Pointer to the data - public T* Pointer() where T : unmanaged - { - using MemoryHandle h = Data.AsMemory().Pin(); - return (T*)h.Pointer; - } + /// + /// Retrieve the data as any unamanaged pointer. + /// + /// Unmanaged type + /// Pointer to the data + public T* Pointer() where T : unmanaged + { + using MemoryHandle h = Data.AsMemory().Pin(); + return (T*)h.Pointer; } } \ No newline at end of file diff --git a/Core/PPather/RemotePathingAPI.cs b/Core/PPather/RemotePathingAPI.cs index 0461acec1..68f537874 100644 --- a/Core/PPather/RemotePathingAPI.cs +++ b/Core/PPather/RemotePathingAPI.cs @@ -11,124 +11,123 @@ using System.Numerics; using SharedLib.Converters; -namespace Core +namespace Core; + +public sealed class RemotePathingAPI : IPPather { - public sealed class RemotePathingAPI : IPPather - { - private readonly ILogger logger; + private readonly ILogger logger; - private readonly string host = "localhost"; + private readonly string host = "localhost"; - private readonly int port = 5001; + private readonly int port = 5001; - private readonly string api; + private readonly string api; - private readonly JsonSerializerOptions options; + private readonly JsonSerializerOptions options; - public RemotePathingAPI(ILogger logger, string host = "", int port = 0) - { - this.logger = logger; - this.host = host; - this.port = port; - - options = new() - { - PropertyNameCaseInsensitive = true - }; - options.Converters.Add(new Vector3Converter()); - options.Converters.Add(new Vector4Converter()); - - api = $"http://{host}:{port}/api/PPather/"; - } + public RemotePathingAPI(ILogger logger, string host = "", int port = 0) + { + this.logger = logger; + this.host = host; + this.port = port; - public async ValueTask DrawLines(List lineArgs) + options = new() { - using HttpClient client = new(); - using StringContent content = new(JsonSerializer.Serialize(lineArgs, options), Encoding.UTF8, "application/json"); - LogInformation($"Drawing lines '{string.Join(", ", lineArgs.Select(l => l.MapId))}'..."); - await client.PostAsync($"{api}Drawlines", content); - } + PropertyNameCaseInsensitive = true + }; + options.Converters.Add(new Vector3Converter()); + options.Converters.Add(new Vector4Converter()); + + api = $"http://{host}:{port}/api/PPather/"; + } - public async ValueTask DrawSphere(SphereArgs args) + public async ValueTask DrawLines(List lineArgs) + { + using HttpClient client = new(); + using StringContent content = new(JsonSerializer.Serialize(lineArgs, options), Encoding.UTF8, "application/json"); + LogInformation($"Drawing lines '{string.Join(", ", lineArgs.Select(l => l.MapId))}'..."); + await client.PostAsync($"{api}Drawlines", content); + } + + public async ValueTask DrawSphere(SphereArgs args) + { + using HttpClient client = new(); + using StringContent content = new(JsonSerializer.Serialize(args, options), Encoding.UTF8, "application/json"); + await client.PostAsync($"{api}DrawSphere", content); + } + + public Vector3[] FindMapRoute(int uiMap, Vector3 mapFrom, Vector3 mapTo) + { + try { + LogInformation($"Finding route from {mapFrom} map {uiMap} to {mapTo} map {uiMap}..."); + var url = $"{api}MapRoute?uimap1={uiMap}&x1={mapFrom.X}&y1={mapFrom.Y}&uimap2={uiMap}&x2={mapTo.X}&y2={mapTo.Y}"; + + long timestamp = Stopwatch.GetTimestamp(); using HttpClient client = new(); - using StringContent content = new(JsonSerializer.Serialize(args, options), Encoding.UTF8, "application/json"); - await client.PostAsync($"{api}DrawSphere", content); + var task = client.GetStringAsync(url); + string response = task.GetAwaiter().GetResult(); + LogInformation($"Finding route from {mapFrom} map {uiMap} to {mapTo} took {Stopwatch.GetElapsedTime(timestamp).TotalMilliseconds} ms."); + return JsonSerializer.Deserialize(response, options) ?? Array.Empty(); } - - public Vector3[] FindMapRoute(int uiMap, Vector3 mapFrom, Vector3 mapTo) + catch (Exception ex) { - try - { - LogInformation($"Finding route from {mapFrom} map {uiMap} to {mapTo} map {uiMap}..."); - var url = $"{api}MapRoute?uimap1={uiMap}&x1={mapFrom.X}&y1={mapFrom.Y}&uimap2={uiMap}&x2={mapTo.X}&y2={mapTo.Y}"; - - long timestamp = Stopwatch.GetTimestamp(); - using HttpClient client = new(); - var task = client.GetStringAsync(url); - string response = task.GetAwaiter().GetResult(); - LogInformation($"Finding route from {mapFrom} map {uiMap} to {mapTo} took {Stopwatch.GetElapsedTime(timestamp).TotalMilliseconds} ms."); - return JsonSerializer.Deserialize(response, options) ?? Array.Empty(); - } - catch (Exception ex) - { - LogError($"Finding route from {mapFrom} to {mapTo}", ex); - Console.WriteLine(ex); - return Array.Empty(); - } + LogError($"Finding route from {mapFrom} to {mapTo}", ex); + Console.WriteLine(ex); + return Array.Empty(); } + } - public Vector3[] FindWorldRoute(int uiMap, Vector3 worldFrom, Vector3 worldTo) + public Vector3[] FindWorldRoute(int uiMap, Vector3 worldFrom, Vector3 worldTo) + { + try { - try - { - LogInformation($"Finding route from {worldFrom} map {uiMap} to {worldTo} map {uiMap}..."); - var url = - $"{api}WorldRoute2?x1={worldFrom.X}&y1={worldFrom.Y}&z1={worldFrom.Z}&x2={worldTo.X}&y2={worldTo.Y}&z2={worldTo.Z}&uimap={uiMap}"; - - long timestamp = Stopwatch.GetTimestamp(); - using HttpClient client = new(); - string response = client.GetStringAsync(url).GetAwaiter().GetResult(); - LogInformation($"Finding route from {worldFrom} map {uiMap} to {worldTo} took {Stopwatch.GetElapsedTime(timestamp).TotalMilliseconds} ms."); - return JsonSerializer.Deserialize(response, options) ?? Array.Empty(); - } - catch (Exception ex) - { - LogError($"Finding route from {worldFrom} to {worldTo}", ex); - Console.WriteLine(ex); - return Array.Empty(); - } - } + LogInformation($"Finding route from {worldFrom} map {uiMap} to {worldTo} map {uiMap}..."); + var url = + $"{api}WorldRoute2?x1={worldFrom.X}&y1={worldFrom.Y}&z1={worldFrom.Z}&x2={worldTo.X}&y2={worldTo.Y}&z2={worldTo.Z}&uimap={uiMap}"; - public async Task PingServer() + long timestamp = Stopwatch.GetTimestamp(); + using HttpClient client = new(); + string response = client.GetStringAsync(url).GetAwaiter().GetResult(); + LogInformation($"Finding route from {worldFrom} map {uiMap} to {worldTo} took {Stopwatch.GetElapsedTime(timestamp).TotalMilliseconds} ms."); + return JsonSerializer.Deserialize(response, options) ?? Array.Empty(); + } + catch (Exception ex) { - try - { - string url = $"{api}SelfTest"; - - using HttpClient client = new(); - string response = await client.GetStringAsync(url); - return JsonSerializer.Deserialize(response); - } - catch (Exception ex) - { - LogError($"{nameof(PingServer)} - Connected: false - {api} Gave({ex.Message})"); - return false; - } + LogError($"Finding route from {worldFrom} to {worldTo}", ex); + Console.WriteLine(ex); + return Array.Empty(); } + } - #region Logging - - private void LogError(string text, Exception? ex = null) + public async Task PingServer() + { + try { - logger.LogError(ex, $"{nameof(RemotePathingAPI)}: {text}"); - } + string url = $"{api}SelfTest"; - private void LogInformation(string text) + using HttpClient client = new(); + string response = await client.GetStringAsync(url); + return JsonSerializer.Deserialize(response); + } + catch (Exception ex) { - logger.LogInformation($"{nameof(RemotePathingAPI)}: {text}"); + LogError($"{nameof(PingServer)} - Connected: false - {api} Gave({ex.Message})"); + return false; } + } - #endregion + #region Logging + + private void LogError(string text, Exception? ex = null) + { + logger.LogError(ex, $"{nameof(RemotePathingAPI)}: {text}"); } + + private void LogInformation(string text) + { + logger.LogInformation($"{nameof(RemotePathingAPI)}: {text}"); + } + + #endregion } \ No newline at end of file diff --git a/Core/PPather/RemotePathingAPIV3.cs b/Core/PPather/RemotePathingAPIV3.cs index 7d12c6809..fa37aacf2 100644 --- a/Core/PPather/RemotePathingAPIV3.cs +++ b/Core/PPather/RemotePathingAPIV3.cs @@ -10,216 +10,215 @@ #pragma warning disable 162 -namespace Core +namespace Core; + +public sealed class RemotePathingAPIV3 : IPPather, IDisposable { - public sealed class RemotePathingAPIV3 : IPPather, IDisposable + private const bool debug = false; + private const int watchdogPollMs = 1000; + + public enum EMessageType { - private const bool debug = false; - private const int watchdogPollMs = 1000; + PATH, + MOVE_ALONG_SURFACE, + RANDOM_POINT, + RANDOM_POINT_AROUND, + CAST_RAY, + RANDOM_PATH + } - public enum EMessageType - { - PATH, - MOVE_ALONG_SURFACE, - RANDOM_POINT, - RANDOM_POINT_AROUND, - CAST_RAY, - RANDOM_PATH - } + public enum PathRequestFlags + { + NONE = 0, + CHAIKIN = 1, + CATMULLROM = 2, + FIND_LOCATION = 4 + }; - public enum PathRequestFlags - { - NONE = 0, - CHAIKIN = 1, - CATMULLROM = 2, - FIND_LOCATION = 4 - }; + private readonly ILogger logger; + private readonly WorldMapAreaDB worldMapAreaDB; - private readonly ILogger logger; - private readonly WorldMapAreaDB worldMapAreaDB; + private readonly AnTcpClient Client; + private readonly Thread ConnectionWatchdog; + private readonly CancellationTokenSource cts; - private readonly AnTcpClient Client; - private readonly Thread ConnectionWatchdog; - private readonly CancellationTokenSource cts; + public RemotePathingAPIV3(ILogger logger, string ip, int port, WorldMapAreaDB worldMapAreaDB) + { + this.logger = logger; + this.worldMapAreaDB = worldMapAreaDB; - public RemotePathingAPIV3(ILogger logger, string ip, int port, WorldMapAreaDB worldMapAreaDB) - { - this.logger = logger; - this.worldMapAreaDB = worldMapAreaDB; + cts = new(); - cts = new(); + Client = new AnTcpClient(ip, port); + ConnectionWatchdog = new Thread(ObserveConnection); + ConnectionWatchdog.Start(); + } - Client = new AnTcpClient(ip, port); - ConnectionWatchdog = new Thread(ObserveConnection); - ConnectionWatchdog.Start(); - } + public void Dispose() + { + RequestDisconnect(); + } - public void Dispose() - { - RequestDisconnect(); - } + #region old - #region old + public ValueTask DrawLines(List lineArgs) + { + return ValueTask.CompletedTask; + } - public ValueTask DrawLines(List lineArgs) - { - return ValueTask.CompletedTask; - } + public ValueTask DrawSphere(SphereArgs args) + { + return ValueTask.CompletedTask; + } - public ValueTask DrawSphere(SphereArgs args) - { - return ValueTask.CompletedTask; - } + public Vector3[] FindMapRoute(int uiMap, Vector3 mapFrom, Vector3 mapTo) + { + if (!Client.IsConnected) + return Array.Empty(); - public Vector3[] FindMapRoute(int uiMap, Vector3 mapFrom, Vector3 mapTo) - { - if (!Client.IsConnected) - return Array.Empty(); + if (!worldMapAreaDB.TryGet(uiMap, out WorldMapArea area)) + return Array.Empty(); - if (!worldMapAreaDB.TryGet(uiMap, out WorldMapArea area)) - return Array.Empty(); + try + { + Vector3 worldFrom = worldMapAreaDB.ToWorld_FlipXY(uiMap, mapFrom); + Vector3 worldTo = worldMapAreaDB.ToWorld_FlipXY(uiMap, mapTo); - try + // incase haven't asked a pathfinder for a route this value will be 0 + // that case use the highest location + if (worldFrom.Z == 0) { - Vector3 worldFrom = worldMapAreaDB.ToWorld_FlipXY(uiMap, mapFrom); - Vector3 worldTo = worldMapAreaDB.ToWorld_FlipXY(uiMap, mapTo); - - // incase haven't asked a pathfinder for a route this value will be 0 - // that case use the highest location - if (worldFrom.Z == 0) - { - worldFrom.Z = area.LocTop / 2; - worldTo.Z = area.LocTop / 2; - } - - if (debug) - LogInformation($"Finding map route from {mapFrom}({worldFrom}) map {uiMap} to {mapTo}({worldTo}) map {uiMap}..."); - - Vector3[] path = Client.Send((byte)EMessageType.PATH, - (area.MapID, PathRequestFlags.FIND_LOCATION | PathRequestFlags.CATMULLROM, - worldFrom.X, worldFrom.Y, worldFrom.Z, worldTo.X, worldTo.Y, worldTo.Z)).AsArray(); + worldFrom.Z = area.LocTop / 2; + worldTo.Z = area.LocTop / 2; + } - if (path.Length == 1 && path[0] == Vector3.Zero) - return Array.Empty(); + if (debug) + LogInformation($"Finding map route from {mapFrom}({worldFrom}) map {uiMap} to {mapTo}({worldTo}) map {uiMap}..."); - for (int i = 0; i < path.Length; i++) - { - if (debug) - LogInformation($"new float[] {{ {path[i].X}f, {path[i].Y}f, {path[i].Z}f }},"); + Vector3[] path = Client.Send((byte)EMessageType.PATH, + (area.MapID, PathRequestFlags.FIND_LOCATION | PathRequestFlags.CATMULLROM, + worldFrom.X, worldFrom.Y, worldFrom.Z, worldTo.X, worldTo.Y, worldTo.Z)).AsArray(); - path[i] = worldMapAreaDB.ToMap_FlipXY(path[i], area.MapID, uiMap); - } + if (path.Length == 1 && path[0] == Vector3.Zero) + return Array.Empty(); - return path; - } - catch (Exception ex) + for (int i = 0; i < path.Length; i++) { - LogError($"Finding map route from {mapFrom} to {mapTo}", ex); - Console.WriteLine(ex); - return Array.Empty(); + if (debug) + LogInformation($"new float[] {{ {path[i].X}f, {path[i].Y}f, {path[i].Z}f }},"); + + path[i] = worldMapAreaDB.ToMap_FlipXY(path[i], area.MapID, uiMap); } - } - public Vector3[] FindWorldRoute(int uiMap, Vector3 worldFrom, Vector3 worldTo) + return path; + } + catch (Exception ex) { - if (!Client.IsConnected) - return Array.Empty(); + LogError($"Finding map route from {mapFrom} to {mapTo}", ex); + Console.WriteLine(ex); + return Array.Empty(); + } + } - if (!worldMapAreaDB.TryGet(uiMap, out WorldMapArea area)) - return Array.Empty(); + public Vector3[] FindWorldRoute(int uiMap, Vector3 worldFrom, Vector3 worldTo) + { + if (!Client.IsConnected) + return Array.Empty(); - try - { - // incase haven't asked a pathfinder for a route this value will be 0 - // that case use the highest location - if (worldFrom.Z == 0) - { - worldFrom.Z = area.LocTop / 2; - worldTo.Z = area.LocTop / 2; - } + if (!worldMapAreaDB.TryGet(uiMap, out WorldMapArea area)) + return Array.Empty(); - if (debug) - LogInformation($"Finding world route from {worldFrom}({worldFrom}) map {uiMap} to {worldTo}({worldTo}) map {uiMap}..."); + try + { + // incase haven't asked a pathfinder for a route this value will be 0 + // that case use the highest location + if (worldFrom.Z == 0) + { + worldFrom.Z = area.LocTop / 2; + worldTo.Z = area.LocTop / 2; + } - Vector3[] path = Client.Send((byte)EMessageType.PATH, - (area.MapID, PathRequestFlags.FIND_LOCATION | PathRequestFlags.CATMULLROM, - worldFrom.X, worldFrom.Y, worldFrom.Z, worldTo.X, worldTo.Y, worldTo.Z)).AsArray(); + if (debug) + LogInformation($"Finding world route from {worldFrom}({worldFrom}) map {uiMap} to {worldTo}({worldTo}) map {uiMap}..."); - if (path.Length == 1 && path[0] == Vector3.Zero) - return Array.Empty(); + Vector3[] path = Client.Send((byte)EMessageType.PATH, + (area.MapID, PathRequestFlags.FIND_LOCATION | PathRequestFlags.CATMULLROM, + worldFrom.X, worldFrom.Y, worldFrom.Z, worldTo.X, worldTo.Y, worldTo.Z)).AsArray(); - return path; - } - catch (Exception ex) - { - LogError($"Finding world route from {worldFrom} to {worldTo}", ex); - Console.WriteLine(ex); + if (path.Length == 1 && path[0] == Vector3.Zero) return Array.Empty(); - } - } - - public bool PingServer() + return path; + } + catch (Exception ex) { - using CancellationTokenSource cts = new(); - cts.CancelAfter(2 * watchdogPollMs); + LogError($"Finding world route from {worldFrom} to {worldTo}", ex); + Console.WriteLine(ex); + return Array.Empty(); + } + } - while (!cts.IsCancellationRequested) - { - if (Client.IsConnected) - { - break; - } - cts.Token.WaitHandle.WaitOne(1); - } - return Client.IsConnected; - } + public bool PingServer() + { + using CancellationTokenSource cts = new(); + cts.CancelAfter(2 * watchdogPollMs); - private void RequestDisconnect() + while (!cts.IsCancellationRequested) { - cts.Cancel(); if (Client.IsConnected) { - Client.Disconnect(); + break; } + cts.Token.WaitHandle.WaitOne(1); } - #endregion old + return Client.IsConnected; + } + + private void RequestDisconnect() + { + cts.Cancel(); + if (Client.IsConnected) + { + Client.Disconnect(); + } + } - private void ObserveConnection() + #endregion old + + private void ObserveConnection() + { + while (!cts.IsCancellationRequested) { - while (!cts.IsCancellationRequested) + if (!Client.IsConnected) { - if (!Client.IsConnected) + try { - try - { - Client.Connect(); - } - catch (Exception ex) - { - LogError(ex.Message, ex); - // ignored, will happen when we cant connect - } + Client.Connect(); + } + catch (Exception ex) + { + LogError(ex.Message, ex); + // ignored, will happen when we cant connect } - - cts.Token.WaitHandle.WaitOne(watchdogPollMs); } - } - - #region Logging - private void LogError(string text, Exception? ex = null) - { - logger.LogError($"{nameof(RemotePathingAPIV3)}: {text}", ex); + cts.Token.WaitHandle.WaitOne(watchdogPollMs); } + } - private void LogInformation(string text) - { - logger.LogInformation($"{nameof(RemotePathingAPIV3)}: {text}"); - } + #region Logging - #endregion + private void LogError(string text, Exception? ex = null) + { + logger.LogError($"{nameof(RemotePathingAPIV3)}: {text}", ex); } + + private void LogInformation(string text) + { + logger.LogInformation($"{nameof(RemotePathingAPIV3)}: {text}"); + } + + #endregion } \ No newline at end of file diff --git a/Core/Path/DirectionCalculator.cs b/Core/Path/DirectionCalculator.cs index 146679c15..127eda151 100644 --- a/Core/Path/DirectionCalculator.cs +++ b/Core/Path/DirectionCalculator.cs @@ -3,24 +3,23 @@ using static System.MathF; -namespace Core +namespace Core; + +public static class DirectionCalculator { - public static class DirectionCalculator + public static float CalculateMapHeading(Vector3 mapFrom, Vector3 mapTo) { - public static float CalculateMapHeading(Vector3 mapFrom, Vector3 mapTo) - { - //logger.LogInformation($"from: ({from.X},{from.Y}) to: ({to.X},{to.Y})"); + //logger.LogInformation($"from: ({from.X},{from.Y}) to: ({to.X},{to.Y})"); - float target = Atan2(mapTo.X - mapFrom.X, mapTo.Y - mapFrom.Y); - return PI + target; - } + float target = Atan2(mapTo.X - mapFrom.X, mapTo.Y - mapFrom.Y); + return PI + target; + } - public static Vector2 ToNormalRadian(float wowRadian) - { - // wow origo is north side - shifted 90 degree - return new( - Cos(wowRadian + (PI / 2)), - Sin(wowRadian - (PI / 2))); - } + public static Vector2 ToNormalRadian(float wowRadian) + { + // wow origo is north side - shifted 90 degree + return new( + Cos(wowRadian + (PI / 2)), + Sin(wowRadian - (PI / 2))); } } \ No newline at end of file diff --git a/Core/Path/IRouteProvider.cs b/Core/Path/IRouteProvider.cs index 8f1ba26ea..4b5bd5681 100644 --- a/Core/Path/IRouteProvider.cs +++ b/Core/Path/IRouteProvider.cs @@ -1,16 +1,15 @@ using System; using System.Numerics; -namespace Core +namespace Core; + +public interface IRouteProvider { - public interface IRouteProvider - { - Vector3[] PathingRoute(); + Vector3[] PathingRoute(); - DateTime LastActive { get; } + DateTime LastActive { get; } - bool HasNext(); + bool HasNext(); - Vector3 NextMapPoint(); - } + Vector3 NextMapPoint(); } diff --git a/Core/Path/IRouteReceiver.cs b/Core/Path/IRouteReceiver.cs index 47ff67757..b6e11a125 100644 --- a/Core/Path/IRouteReceiver.cs +++ b/Core/Path/IRouteReceiver.cs @@ -1,9 +1,8 @@ using System.Numerics; -namespace Core +namespace Core; + +public interface IEditedRouteReceiver { - public interface IEditedRouteReceiver - { - void ReceivePath(Vector3[] mapRoute); - } + void ReceivePath(Vector3[] mapRoute); } diff --git a/Core/Path/PointEstimator.cs b/Core/Path/PointEstimator.cs index 87924804f..9519326ed 100644 --- a/Core/Path/PointEstimator.cs +++ b/Core/Path/PointEstimator.cs @@ -1,32 +1,31 @@ using System.Numerics; -namespace Core +namespace Core; + +public static class PointEstimator { - public static class PointEstimator - { - public const float YARD_TO_COORD = 0.035921f; + public const float YARD_TO_COORD = 0.035921f; - public static Vector3 GetPoint(Vector3 map, float wowRad, float rangeYard) - { - //player direction - //0.00061 + public static Vector3 GetPoint(Vector3 map, float wowRad, float rangeYard) + { + //player direction + //0.00061 - //player location - //37.4017,44.4587 + //player location + //37.4017,44.4587 - //NPC - //37.4016,44.2791 + //NPC + //37.4016,44.2791 - //~5yard Distance - //44.4587 - 44.2791 = 0.1796 + //~5yard Distance + //44.4587 - 44.2791 = 0.1796 - //~1yard Distance - //0.1796 / 5 = 0.03592 + //~1yard Distance + //0.1796 / 5 = 0.03592 - float range = rangeYard * YARD_TO_COORD; - Vector2 dir = DirectionCalculator.ToNormalRadian(wowRad); + float range = rangeYard * YARD_TO_COORD; + Vector2 dir = DirectionCalculator.ToNormalRadian(wowRad); - return new Vector3(map.X + (range * dir.X), map.Y + (range * dir.Y), map.Z); - } + return new Vector3(map.X + (range * dir.X), map.Y + (range * dir.Y), map.Z); } } diff --git a/Core/Path/RouteInfo.cs b/Core/Path/RouteInfo.cs index e62efc881..3c4fc004c 100644 --- a/Core/Path/RouteInfo.cs +++ b/Core/Path/RouteInfo.cs @@ -10,263 +10,262 @@ using WowheadDB; -namespace Core -{ - public readonly struct RouteInfoPoi - { - public readonly Vector3 MapLoc; - public readonly string Name; - public readonly string Color; - public readonly float Radius; +namespace Core; - public RouteInfoPoi(NPC npc, string color) - { - MapLoc = npc.MapCoords[0]; - Name = npc.name; - Color = color; - Radius = 1; - } +public readonly struct RouteInfoPoi +{ + public readonly Vector3 MapLoc; + public readonly string Name; + public readonly string Color; + public readonly float Radius; - public RouteInfoPoi(Vector3 mapLoc, string name, string color, float radius) - { - MapLoc = mapLoc; - Name = name; - Color = color; - Radius = radius; - } + public RouteInfoPoi(NPC npc, string color) + { + MapLoc = npc.MapCoords[0]; + Name = npc.name; + Color = color; + Radius = 1; } - public sealed class RouteInfo : IDisposable + public RouteInfoPoi(Vector3 mapLoc, string name, string color, float radius) { - public Vector3[] Route { get; private set; } + MapLoc = mapLoc; + Name = name; + Color = color; + Radius = radius; + } +} - public Vector3[] RouteToWaypoint => pathedRoutes.Any() - ? pathedRoutes.OrderByDescending(x => x.LastActive).First().PathingRoute() - : Array.Empty(); +public sealed class RouteInfo : IDisposable +{ + public Vector3[] Route { get; private set; } - private readonly IEnumerable pathedRoutes; - private readonly AreaDB areaDB; - private readonly PlayerReader playerReader; - private readonly WorldMapAreaDB worldmapAreaDB; + public Vector3[] RouteToWaypoint => pathedRoutes.Any() + ? pathedRoutes.OrderByDescending(x => x.LastActive).First().PathingRoute() + : Array.Empty(); - public List PoiList { get; } = new(); + private readonly IEnumerable pathedRoutes; + private readonly AreaDB areaDB; + private readonly PlayerReader playerReader; + private readonly WorldMapAreaDB worldmapAreaDB; - private float min; - private float diff; + public List PoiList { get; } = new(); - private float addY; - private float addX; + private float min; + private float diff; - private int margin; - private int canvasSize; + private float addY; + private float addX; - private float pointToGrid; + private int margin; + private int canvasSize; - private const int dSize = 2; + private float pointToGrid; - public RouteInfo(Vector3[] route, IEnumerable pathedRoutes, PlayerReader playerReader, AreaDB areaDB, WorldMapAreaDB worldmapAreaDB) - { - this.Route = route; - this.pathedRoutes = pathedRoutes; - this.playerReader = playerReader; - this.areaDB = areaDB; - this.worldmapAreaDB = worldmapAreaDB; + private const int dSize = 2; - this.areaDB.Changed += OnZoneChanged; - OnZoneChanged(); + public RouteInfo(Vector3[] route, IEnumerable pathedRoutes, PlayerReader playerReader, AreaDB areaDB, WorldMapAreaDB worldmapAreaDB) + { + this.Route = route; + this.pathedRoutes = pathedRoutes; + this.playerReader = playerReader; + this.areaDB = areaDB; + this.worldmapAreaDB = worldmapAreaDB; - CalculateDiffs(); - } + this.areaDB.Changed += OnZoneChanged; + OnZoneChanged(); - public void Dispose() - { - areaDB.Changed -= OnZoneChanged; - } + CalculateDiffs(); + } - public void UpdateRoute(Vector3[] mapRoute) - { - Route = mapRoute; + public void Dispose() + { + areaDB.Changed -= OnZoneChanged; + } - foreach (IEditedRouteReceiver receiver in pathedRoutes.OfType()) - { - receiver.ReceivePath(Route); - } - } + public void UpdateRoute(Vector3[] mapRoute) + { + Route = mapRoute; - public void SetMargin(int margin) + foreach (IEditedRouteReceiver receiver in pathedRoutes.OfType()) { - this.margin = margin; - CalculatePointToGrid(); + receiver.ReceivePath(Route); } + } - public void SetCanvasSize(int size) - { - this.canvasSize = size; - CalculatePointToGrid(); - } + public void SetMargin(int margin) + { + this.margin = margin; + CalculatePointToGrid(); + } + + public void SetCanvasSize(int size) + { + this.canvasSize = size; + CalculatePointToGrid(); + } + + public void CalculatePointToGrid() + { + pointToGrid = ((float)canvasSize - (margin * 2)) / diff; + CalculateDiffs(); + } + + public int ToCanvasPointX(float value) + { + return (int)(margin + ((value + addX - min) * pointToGrid)); + } + + public int ToCanvasPointY(float value) + { + return (int)(margin + ((value + addY - min) * pointToGrid)); + } - public void CalculatePointToGrid() + public float DistanceToGrid(int value) + { + return value / 100f * pointToGrid; + } + + private void OnZoneChanged() + { + if (areaDB.CurrentArea == null) + return; + + PoiList.Clear(); + /* + // Visualize the zone pois + for (int i = 0; i < areaDB.CurrentArea.vendor?.Count; i++) { - pointToGrid = ((float)canvasSize - (margin * 2)) / diff; - CalculateDiffs(); + NPC npc = areaDB.CurrentArea.vendor[i]; + PoiList.Add(new RouteInfoPoi(npc, "green")); } - public int ToCanvasPointX(float value) + for (int i = 0; i < areaDB.CurrentArea.repair?.Count; i++) { - return (int)(margin + ((value + addX - min) * pointToGrid)); + NPC npc = areaDB.CurrentArea.repair[i]; + PoiList.Add(new RouteInfoPoi(npc, "purple")); } - public int ToCanvasPointY(float value) + for (int i = 0; i < areaDB.CurrentArea.innkeeper?.Count; i++) { - return (int)(margin + ((value + addY - min) * pointToGrid)); + NPC npc = areaDB.CurrentArea.innkeeper[i]; + PoiList.Add(new RouteInfoPoi(npc, "blue")); } - public float DistanceToGrid(int value) + for (int i = 0; i < areaDB.CurrentArea.flightmaster?.Count; i++) { - return value / 100f * pointToGrid; + NPC npc = areaDB.CurrentArea.flightmaster[i]; + PoiList.Add(new RouteInfoPoi(npc, "orange")); } + */ + } + + private void CalculateDiffs() + { + /* + TODO> route disappears after time? + Vector3[] pois = PoiList.Select(p => p.MapLoc).ToArray(); + Vector3[] route = Route.ToArray(); + Vector3[] navigation = RouteToWaypoint.ToArray(); + worldmapAreaDB.ToMap_FlipXY(playerReader.UIMapId.Value, ref navigation); + + Vector3[] allPoints = new Vector3[route.Length + navigation.Length + pois.Length]; + Array.Copy(Route, allPoints, route.Length); + Array.ConstrainedCopy(navigation, 0, allPoints, route.Length, navigation.Length); + Array.ConstrainedCopy(pois, 0, allPoints, route.Length + navigation.Length, pois.Length); + allPoints[^1] = playerReader.MapPos; + */ + + var allPoints = Route.ToList(); - private void OnZoneChanged() + Vector3[] navigation = RouteToWaypoint.ToArray(); + worldmapAreaDB.ToMap_FlipXY(playerReader.UIMapId.Value, ref navigation); + allPoints.AddRange(navigation); + + var pois = PoiList.Select(p => p.MapLoc); + allPoints.AddRange(pois); + + allPoints.Add(playerReader.MapPos); + + float maxX = allPoints.Max(s => s.X); + float minX = allPoints.Min(s => s.X); + float diffX = maxX - minX; + + float maxY = allPoints.Max(s => s.Y); + float minY = allPoints.Min(s => s.Y); + float diffY = maxY - minY; + + this.addY = 0; + this.addX = 0; + + if (diffX > diffY) { - if (areaDB.CurrentArea == null) - return; - - PoiList.Clear(); - /* - // Visualize the zone pois - for (int i = 0; i < areaDB.CurrentArea.vendor?.Count; i++) - { - NPC npc = areaDB.CurrentArea.vendor[i]; - PoiList.Add(new RouteInfoPoi(npc, "green")); - } - - for (int i = 0; i < areaDB.CurrentArea.repair?.Count; i++) - { - NPC npc = areaDB.CurrentArea.repair[i]; - PoiList.Add(new RouteInfoPoi(npc, "purple")); - } - - for (int i = 0; i < areaDB.CurrentArea.innkeeper?.Count; i++) - { - NPC npc = areaDB.CurrentArea.innkeeper[i]; - PoiList.Add(new RouteInfoPoi(npc, "blue")); - } - - for (int i = 0; i < areaDB.CurrentArea.flightmaster?.Count; i++) - { - NPC npc = areaDB.CurrentArea.flightmaster[i]; - PoiList.Add(new RouteInfoPoi(npc, "orange")); - } - */ + this.addY = minX - minY; + this.min = minX; + this.diff = diffX; } - - private void CalculateDiffs() + else { - /* - TODO> route disappears after time? - Vector3[] pois = PoiList.Select(p => p.MapLoc).ToArray(); - Vector3[] route = Route.ToArray(); - Vector3[] navigation = RouteToWaypoint.ToArray(); - worldmapAreaDB.ToMap_FlipXY(playerReader.UIMapId.Value, ref navigation); - - Vector3[] allPoints = new Vector3[route.Length + navigation.Length + pois.Length]; - Array.Copy(Route, allPoints, route.Length); - Array.ConstrainedCopy(navigation, 0, allPoints, route.Length, navigation.Length); - Array.ConstrainedCopy(pois, 0, allPoints, route.Length + navigation.Length, pois.Length); - allPoints[^1] = playerReader.MapPos; - */ - - var allPoints = Route.ToList(); - - Vector3[] navigation = RouteToWaypoint.ToArray(); - worldmapAreaDB.ToMap_FlipXY(playerReader.UIMapId.Value, ref navigation); - allPoints.AddRange(navigation); - - var pois = PoiList.Select(p => p.MapLoc); - allPoints.AddRange(pois); - - allPoints.Add(playerReader.MapPos); - - float maxX = allPoints.Max(s => s.X); - float minX = allPoints.Min(s => s.X); - float diffX = maxX - minX; - - float maxY = allPoints.Max(s => s.Y); - float minY = allPoints.Min(s => s.Y); - float diffY = maxY - minY; - - this.addY = 0; - this.addX = 0; - - if (diffX > diffY) - { - this.addY = minX - minY; - this.min = minX; - this.diff = diffX; - } - else - { - this.addX = minY - minX; - this.min = minY; - this.diff = diffY; - } + this.addX = minY - minX; + this.min = minY; + this.diff = diffY; } + } - public string RenderPathLines(Vector3[] path) + public string RenderPathLines(Vector3[] path) + { + StringBuilder sb = new(); + for (int i = 0; i < path.Length - 1; i++) { - StringBuilder sb = new(); - for (int i = 0; i < path.Length - 1; i++) - { - Vector3 p1 = path[i]; - Vector3 p2 = path[i + 1]; - sb.AppendLine($""); - } - return sb.ToString(); + Vector3 p1 = path[i]; + Vector3 p2 = path[i + 1]; + sb.AppendLine($""); } + return sb.ToString(); + } - private const string first = "
First"; - private const string last = "
Last"; + private const string first = "
First"; + private const string last = "
Last"; - public string RenderPathPoints(Vector3[] path) + public string RenderPathPoints(Vector3[] path) + { + StringBuilder sb = new(); + for (int i = 0; i < path.Length; i++) { - StringBuilder sb = new(); - for (int i = 0; i < path.Length; i++) - { - Vector3 p = path[i]; - float x = p.X; - float y = p.Y; - sb.AppendLine($""); - } - return sb.ToString(); + Vector3 p = path[i]; + float x = p.X; + float y = p.Y; + sb.AppendLine($""); } + return sb.ToString(); + } - public Vector3 NextPoint() - { - var route = pathedRoutes.OrderByDescending(s => s.LastActive).FirstOrDefault(); - if (route == null || !route.HasNext()) - return Vector3.Zero; + public Vector3 NextPoint() + { + var route = pathedRoutes.OrderByDescending(s => s.LastActive).FirstOrDefault(); + if (route == null || !route.HasNext()) + return Vector3.Zero; - return route.NextMapPoint(); - } + return route.NextMapPoint(); + } - public string RenderNextPoint() - { - Vector3 pt = NextPoint(); - if (pt == Vector3.Zero) - return string.Empty; + public string RenderNextPoint() + { + Vector3 pt = NextPoint(); + if (pt == Vector3.Zero) + return string.Empty; - return $""; - } + return $""; + } - public string DeathImage(Vector3 pt) - { - var size = this.canvasSize / 25; - return pt == Vector3.Zero ? string.Empty : $""; - } + public string DeathImage(Vector3 pt) + { + var size = this.canvasSize / 25; + return pt == Vector3.Zero ? string.Empty : $""; + } - public string DrawPoi(RouteInfoPoi poi) - { - return $"{poi.MapLoc.X},{poi.MapLoc.Y}');\" onmouseout=\"hideTooltip();\" cx='{ToCanvasPointX(poi.MapLoc.X)}' cy='{ToCanvasPointY(poi.MapLoc.Y)}' r='{(poi.Radius == 1 ? dSize : DistanceToGrid((int)poi.Radius))}' " + (poi.Radius == 1 ? $"fill='{poi.Color}'" : $"stroke='{poi.Color}' stroke-width='1' fill='none'") + " />"; - } + public string DrawPoi(RouteInfoPoi poi) + { + return $"{poi.MapLoc.X},{poi.MapLoc.Y}');\" onmouseout=\"hideTooltip();\" cx='{ToCanvasPointX(poi.MapLoc.X)}' cy='{ToCanvasPointY(poi.MapLoc.Y)}' r='{(poi.Radius == 1 ? dSize : DistanceToGrid((int)poi.Radius))}' " + (poi.Radius == 1 ? $"fill='{poi.Color}'" : $"stroke='{poi.Color}' stroke-width='1' fill='none'") + " />"; } } \ No newline at end of file diff --git a/Core/Path/Simplify/PathSimplify.cs b/Core/Path/Simplify/PathSimplify.cs index 81192229c..83f175bbf 100644 --- a/Core/Path/Simplify/PathSimplify.cs +++ b/Core/Path/Simplify/PathSimplify.cs @@ -3,144 +3,143 @@ using System.Collections.Generic; using System.Numerics; -namespace Core +namespace Core; + +public static class PathSimplify { - public static class PathSimplify + // square distance from a Vector3 to a segment + private static float GetSquareSegmentDistance(in Vector3 p, in Vector3 p1, in Vector3 p2) { - // square distance from a Vector3 to a segment - private static float GetSquareSegmentDistance(in Vector3 p, in Vector3 p1, in Vector3 p2) + float x = p1.X; + float y = p1.Y; + float dx = p2.X - x; + float dy = p2.Y - y; + + if (!dx.Equals(0f) || !dy.Equals(0f)) { - float x = p1.X; - float y = p1.Y; - float dx = p2.X - x; - float dy = p2.Y - y; + float t = ((p.X - x) * dx + (p.Y - y) * dy) / (dx * dx + dy * dy); - if (!dx.Equals(0f) || !dy.Equals(0f)) + if (t > 1) { - float t = ((p.X - x) * dx + (p.Y - y) * dy) / (dx * dx + dy * dy); - - if (t > 1) - { - x = p2.X; - y = p2.Y; - } - else if (t > 0) - { - x += dx * t; - y += dy * t; - } + x = p2.X; + y = p2.Y; } + else if (t > 0) + { + x += dx * t; + y += dy * t; + } + } - dx = p.X - x; - dy = p.Y - y; + dx = p.X - x; + dy = p.Y - y; - return (dx * dx) + (dy * dy); - } + return (dx * dx) + (dy * dy); + } - // basic distance-based simplification - private static Span RadialDistance(Span points, float sqTolerance) - { - var pooler = ArrayPool.Shared; - Vector3[] reduced = pooler.Rent(points.Length); - int c = 1; + // basic distance-based simplification + private static Span RadialDistance(Span points, float sqTolerance) + { + var pooler = ArrayPool.Shared; + Vector3[] reduced = pooler.Rent(points.Length); + int c = 1; - Vector3 prev = points[0]; - Vector3 curr = Vector3.Zero; + Vector3 prev = points[0]; + Vector3 curr = Vector3.Zero; - reduced[0] = prev; + reduced[0] = prev; - for (int i = 1; i < points.Length; i++) + for (int i = 1; i < points.Length; i++) + { + curr = points[i]; + if (Vector3.Distance(curr, prev) > sqTolerance) { - curr = points[i]; - if (Vector3.Distance(curr, prev) > sqTolerance) - { - reduced[c++] = curr; - prev = curr; - } - } - - if (curr != Vector3.Zero && !prev.Equals(curr)) reduced[c++] = curr; - - pooler.Return(reduced); - return reduced.AsSpan(0, c); + prev = curr; + } } - // simplification using optimized Douglas-Peucker algorithm with recursion elimination - private static Span DouglasPeucker(Span points, float sqTolerance) - { - int len = points.Length; - Span markers = stackalloc bool[len]; + if (curr != Vector3.Zero && !prev.Equals(curr)) + reduced[c++] = curr; - int? first = 0; - int? last = len - 1; - int? index = 0; + pooler.Return(reduced); + return reduced.AsSpan(0, c); + } - Stack stack = new(len); + // simplification using optimized Douglas-Peucker algorithm with recursion elimination + private static Span DouglasPeucker(Span points, float sqTolerance) + { + int len = points.Length; + Span markers = stackalloc bool[len]; - var pooler = ArrayPool.Shared; - Vector3[] reduced = pooler.Rent(len); - int count = 0; + int? first = 0; + int? last = len - 1; + int? index = 0; - markers[first.Value] = true; - markers[last.Value] = true; + Stack stack = new(len); - while (last != null) - { - float maxSqDist = 0f; + var pooler = ArrayPool.Shared; + Vector3[] reduced = pooler.Rent(len); + int count = 0; - for (int? i = first + 1; first.HasValue && i < last; i++) - { - float sqDist = GetSquareSegmentDistance(points[i.Value], points[first.Value], points[last.Value]); - if (sqDist > maxSqDist) - { - index = i; - maxSqDist = sqDist; - } - } + markers[first.Value] = true; + markers[last.Value] = true; + + while (last != null) + { + float maxSqDist = 0f; - if (maxSqDist > sqTolerance) + for (int? i = first + 1; first.HasValue && i < last; i++) + { + float sqDist = GetSquareSegmentDistance(points[i.Value], points[first.Value], points[last.Value]); + if (sqDist > maxSqDist) { - markers[index.Value] = true; - stack.Push(first); - stack.Push(index); - stack.Push(index); - stack.Push(last); + index = i; + maxSqDist = sqDist; } - - last = stack.Count > 0 ? stack.Pop() : null; - first = stack.Count > 0 ? stack.Pop() : null; } - for (int i = 0; i < len; i++) + if (maxSqDist > sqTolerance) { - if (markers[i]) - reduced[count++] = points[i]; + markers[index.Value] = true; + stack.Push(first); + stack.Push(index); + stack.Push(index); + stack.Push(last); } - pooler.Return(reduced); - return reduced.AsSpan(0, count); + last = stack.Count > 0 ? stack.Pop() : null; + first = stack.Count > 0 ? stack.Pop() : null; } - /// - /// Simplifies a list of Vector3 to a shorter list of Vector3. - /// - /// Vector3 original list of Vector3 - /// Tolerance tolerance in the same measurement as the Vector3 coordinates - /// Enable highest quality for using Douglas-Peucker, set false for Radial-Distance algorithm - /// Simplified list of Vector3 - public static Span Simplify(Span points, float tolerance = 0.3f, bool highestQuality = false) + for (int i = 0; i < len; i++) { - if (points.Length == 0) - return Array.Empty(); + if (markers[i]) + reduced[count++] = points[i]; + } - float sqTolerance = tolerance * tolerance; + pooler.Return(reduced); + return reduced.AsSpan(0, count); + } - if (highestQuality) - return DouglasPeucker(points, sqTolerance); + /// + /// Simplifies a list of Vector3 to a shorter list of Vector3. + /// + /// Vector3 original list of Vector3 + /// Tolerance tolerance in the same measurement as the Vector3 coordinates + /// Enable highest quality for using Douglas-Peucker, set false for Radial-Distance algorithm + /// Simplified list of Vector3 + public static Span Simplify(Span points, float tolerance = 0.3f, bool highestQuality = false) + { + if (points.Length == 0) + return Array.Empty(); - Span reduced = RadialDistance(points, sqTolerance); - return DouglasPeucker(reduced, sqTolerance); - } + float sqTolerance = tolerance * tolerance; + + if (highestQuality) + return DouglasPeucker(points, sqTolerance); + + Span reduced = RadialDistance(points, sqTolerance); + return DouglasPeucker(reduced, sqTolerance); } } diff --git a/Core/RPN/InfixToPostfix.cs b/Core/RPN/InfixToPostfix.cs index 5451055b9..5cca8a850 100644 --- a/Core/RPN/InfixToPostfix.cs +++ b/Core/RPN/InfixToPostfix.cs @@ -1,96 +1,95 @@ using System; using System.Collections.Generic; -namespace Core +namespace Core; + +public static class InfixToPostfix { - public static class InfixToPostfix + public static List Convert(ReadOnlySpan span) { - public static List Convert(ReadOnlySpan span) + List output = new(); + Stack stack = new(); + + int i = 0; + while (i < span.Length) { - List output = new(); - Stack stack = new(); + ReadOnlySpan c = span.Slice(i, 1); - int i = 0; - while (i < span.Length) + if (IsSpecial(c)) { - ReadOnlySpan c = span.Slice(i, 1); - - if (IsSpecial(c)) + if (c.SequenceEqual("(")) { - if (c.SequenceEqual("(")) - { - stack.Push(c.ToString()); - i++; - } - else if (c.SequenceEqual(")")) - { - while (stack.Count != 0 && stack.Peek() != "(") - { - output.Add(stack.Pop()); - } - stack.Pop(); - i++; - } - else if (IsOperator(span, i, out ReadOnlySpan op)) + stack.Push(c.ToString()); + i++; + } + else if (c.SequenceEqual(")")) + { + while (stack.Count != 0 && stack.Peek() != "(") { - i += op.Length; - - while (stack.Count != 0 && OperatorPriority(stack.Peek()) >= OperatorPriority(op)) - { - output.Add(stack.Pop()); - } - - stack.Push(op.ToString()); + output.Add(stack.Pop()); } + stack.Pop(); + i++; } - else + else if (IsOperator(span, i, out ReadOnlySpan op)) { - int start = i; - while (i < span.Length && !IsSpecial(span.Slice(i, 1))) + i += op.Length; + + while (stack.Count != 0 && OperatorPriority(stack.Peek()) >= OperatorPriority(op)) { - i++; + output.Add(stack.Pop()); } - output.Add(span[start..i].ToString()); // operand + stack.Push(op.ToString()); } } - - while (stack.Count != 0) + else { - output.Add(stack.Pop()); + int start = i; + while (i < span.Length && !IsSpecial(span.Slice(i, 1))) + { + i++; + } + + output.Add(span[start..i].ToString()); // operand } + } - return output; + while (stack.Count != 0) + { + output.Add(stack.Pop()); + } - static bool IsSpecial(ReadOnlySpan c) - { - // where - // '|' means "||" - // '&' means "&&" - return - c.SequenceEqual("(") || - c.SequenceEqual(")") || - c.SequenceEqual("&") || - c.SequenceEqual("|"); - } + return output; - static bool IsOperator(ReadOnlySpan span, int index, out ReadOnlySpan @operator) - { - @operator = span.Slice(index, 2); - return - @operator.SequenceEqual(Requirement.SymbolAnd) || - @operator.SequenceEqual(Requirement.SymbolOr); - } + static bool IsSpecial(ReadOnlySpan c) + { + // where + // '|' means "||" + // '&' means "&&" + return + c.SequenceEqual("(") || + c.SequenceEqual(")") || + c.SequenceEqual("&") || + c.SequenceEqual("|"); + } - static int OperatorPriority(ReadOnlySpan o) - { - if (o.SequenceEqual(Requirement.SymbolAnd)) - return 2; - else if (o.SequenceEqual(Requirement.SymbolOr)) - return 1; + static bool IsOperator(ReadOnlySpan span, int index, out ReadOnlySpan @operator) + { + @operator = span.Slice(index, 2); + return + @operator.SequenceEqual(Requirement.SymbolAnd) || + @operator.SequenceEqual(Requirement.SymbolOr); + } - return 0; // "(" or ")" - } + static int OperatorPriority(ReadOnlySpan o) + { + if (o.SequenceEqual(Requirement.SymbolAnd)) + return 2; + else if (o.SequenceEqual(Requirement.SymbolOr)) + return 1; + + return 0; // "(" or ")" } } } diff --git a/Core/Requirement/Requirement.cs b/Core/Requirement/Requirement.cs index a82fbd4cc..01e275e19 100644 --- a/Core/Requirement/Requirement.cs +++ b/Core/Requirement/Requirement.cs @@ -1,50 +1,49 @@ using System; -namespace Core +namespace Core; + +public static class RequirementExt { - public static class RequirementExt + public static void Or(this Requirement f1, Requirement f2) { - public static void Or(this Requirement f1, Requirement f2) - { - Func HasRequirement = f1.HasRequirement; - Func LogMessage = f1.LogMessage; - - f1.HasRequirement = () => HasRequirement() || f2.HasRequirement(); - f1.LogMessage = () => string.Join(Requirement.Or, LogMessage(), f2.LogMessage()); - } - - public static void And(this Requirement f1, Requirement f2) - { - Func HasRequirement = f1.HasRequirement; - Func LogMessage = f1.LogMessage; - - f1.HasRequirement = () => HasRequirement() && f2.HasRequirement(); - f1.LogMessage = () => string.Join(Requirement.And, LogMessage(), f2.LogMessage()); - } - - public static void Negate(this Requirement f, string keyword) - { - Func HasRequirement = f.HasRequirement; - Func LogMessage = f.LogMessage; - - f.HasRequirement = () => !HasRequirement(); - f.LogMessage = () => $"{keyword}{LogMessage()}"; - } + Func HasRequirement = f1.HasRequirement; + Func LogMessage = f1.LogMessage; + + f1.HasRequirement = () => HasRequirement() || f2.HasRequirement(); + f1.LogMessage = () => string.Join(Requirement.Or, LogMessage(), f2.LogMessage()); } - public sealed class Requirement + public static void And(this Requirement f1, Requirement f2) { - public const string And = " and "; - public const string Or = " or "; + Func HasRequirement = f1.HasRequirement; + Func LogMessage = f1.LogMessage; - public const string SymbolAnd = "&&"; - public const string SymbolOr = "||"; + f1.HasRequirement = () => HasRequirement() && f2.HasRequirement(); + f1.LogMessage = () => string.Join(Requirement.And, LogMessage(), f2.LogMessage()); + } - public static bool False() => false; - public static string Default() => "Unknown requirement"; + public static void Negate(this Requirement f, string keyword) + { + Func HasRequirement = f.HasRequirement; + Func LogMessage = f.LogMessage; - public Func HasRequirement { get; set; } = False; - public Func LogMessage { get; set; } = Default; - public bool VisibleIfHasRequirement { get; init; } = true; + f.HasRequirement = () => !HasRequirement(); + f.LogMessage = () => $"{keyword}{LogMessage()}"; } +} + +public sealed class Requirement +{ + public const string And = " and "; + public const string Or = " or "; + + public const string SymbolAnd = "&&"; + public const string SymbolOr = "||"; + + public static bool False() => false; + public static string Default() => "Unknown requirement"; + + public Func HasRequirement { get; set; } = False; + public Func LogMessage { get; set; } = Default; + public bool VisibleIfHasRequirement { get; init; } = true; } \ No newline at end of file diff --git a/Core/Requirement/RequirementFactory.cs b/Core/Requirement/RequirementFactory.cs index 3a6a0cdfa..eb69076f7 100644 --- a/Core/Requirement/RequirementFactory.cs +++ b/Core/Requirement/RequirementFactory.cs @@ -8,776 +8,846 @@ using Core.Goals; using System.Runtime.InteropServices; -namespace Core +namespace Core; + +public sealed partial class RequirementFactory { - public sealed partial class RequirementFactory + private readonly ILogger logger; + private readonly AddonReader addonReader; + private readonly PlayerReader playerReader; + private readonly BagReader bagReader; + private readonly EquipmentReader equipmentReader; + private readonly SpellBookReader spellBookReader; + private readonly TalentReader talentReader; + private readonly CreatureDB creatureDb; + private readonly ItemDB itemDb; + private readonly AuraTimeReader playerBuffTimeReader; + private readonly AuraTimeReader targetDebuffTimeReader; + + private KeyAction[] keyActions = Array.Empty(); + + private readonly Dictionary immunityBlacklist; + + private readonly string[] negateKeywords = new string[2] { - private readonly ILogger logger; - private readonly AddonReader addonReader; - private readonly PlayerReader playerReader; - private readonly BagReader bagReader; - private readonly EquipmentReader equipmentReader; - private readonly SpellBookReader spellBookReader; - private readonly TalentReader talentReader; - private readonly CreatureDB creatureDb; - private readonly ItemDB itemDb; - private readonly AuraTimeReader playerBuffTimeReader; - private readonly AuraTimeReader targetDebuffTimeReader; - - private KeyAction[] keyActions = Array.Empty(); - - private readonly Dictionary immunityBlacklist; - - private readonly string[] negateKeywords = new string[2] - { - "not ", - "!" - }; + "not ", + "!" + }; - private readonly Dictionary> intVariables; + private readonly Dictionary> intVariables; - private readonly Dictionary> boolVariables; + private readonly Dictionary> boolVariables; - private readonly Dictionary> requirementMap; + private readonly Dictionary> requirementMap; - private const char SEP1 = ':'; - private const char SEP2 = ','; + private const char SEP1 = ':'; + private const char SEP2 = ','; - private const string Swimming = "Swimming"; - private const string Falling = "Falling"; + private const string Swimming = "Swimming"; + private const string Falling = "Falling"; - public const string AddVisible = "AddVisible"; - public const string Drink = "Drink"; - public const string Food = "Food"; + public const string AddVisible = "AddVisible"; + public const string Drink = "Drink"; + public const string Food = "Food"; - public const string HealthP = "Health%"; - public const string ManaP = "Mana%"; + public const string HealthP = "Health%"; + public const string ManaP = "Mana%"; - public RequirementFactory(ILogger logger, AddonReader addonReader, - NpcNameFinder npcNameFinder, Dictionary immunityBlacklist) + public RequirementFactory(ILogger logger, AddonReader addonReader, + NpcNameFinder npcNameFinder, Dictionary immunityBlacklist) + { + this.logger = logger; + this.addonReader = addonReader; + this.playerReader = addonReader.PlayerReader; + this.bagReader = addonReader.BagReader; + this.equipmentReader = addonReader.EquipmentReader; + this.spellBookReader = addonReader.SpellBookReader; + this.talentReader = addonReader.TalentReader; + this.creatureDb = addonReader.CreatureDb; + this.itemDb = addonReader.ItemDb; + this.immunityBlacklist = immunityBlacklist; + this.playerBuffTimeReader = addonReader.PlayerBuffTimeReader; + this.targetDebuffTimeReader = addonReader.TargetDebuffTimeReader; + + requirementMap = new() { - this.logger = logger; - this.addonReader = addonReader; - this.playerReader = addonReader.PlayerReader; - this.bagReader = addonReader.BagReader; - this.equipmentReader = addonReader.EquipmentReader; - this.spellBookReader = addonReader.SpellBookReader; - this.talentReader = addonReader.TalentReader; - this.creatureDb = addonReader.CreatureDb; - this.itemDb = addonReader.ItemDb; - this.immunityBlacklist = immunityBlacklist; - this.playerBuffTimeReader = addonReader.PlayerBuffTimeReader; - this.targetDebuffTimeReader = addonReader.TargetDebuffTimeReader; - - requirementMap = new() - { - { ">=", CreateGreaterOrEquals }, - { "<=", CreateLesserOrEquals }, - { ">", CreateGreaterThen }, - { "<", CreateLesserThen }, - { "==", CreateEquals }, - { "npcID:", CreateNpcId }, - { "BagItem:", CreateBagItem }, - { "SpellInRange:", CreateSpellInRange }, - { "TargetCastingSpell", CreateTargetCastingSpell }, - { "Form", CreateForm }, - { "Race", CreateRace }, - { "Spell", CreateSpell }, - { "Talent", CreateTalent }, - { "Trigger:", CreateTrigger }, - { "Usable:", CreateUsable } - }; - - boolVariables = new() - { - // Target Based - { "TargetYieldXP", playerReader.Bits.TargetIsNotTrivial }, - { "TargetsMe", playerReader.TargetsMe }, - { "TargetsPet", playerReader.TargetsPet }, - { "TargetsNone", playerReader.TargetsNone }, - - { AddVisible, npcNameFinder._PotentialAddsExist }, - { "InCombat", playerReader.Bits.PlayerInCombat }, - - // Range - { "InMeleeRange", playerReader.IsInMeleeRange }, - { "InCloseMeleeRange", playerReader.InCloseMeleeRange }, - { "InDeadZoneRange", playerReader.IsInDeadZone }, - { "OutOfCombatRange", playerReader.OutOfCombatRange }, - { "InCombatRange", playerReader.WithInCombatRange }, - - // Pet - { "Has Pet", playerReader.Bits.HasPet }, - { "Pet Happy", playerReader.Bits.PetHappy }, - { "Pet HasTarget", playerReader.PetHasTarget }, - { "Mounted", playerReader.Bits.IsMounted }, - - // Auto Spell - { "AutoAttacking", playerReader.Bits.SpellOn_AutoAttack }, - { "Shooting", playerReader.Bits.SpellOn_Shoot }, - { "AutoShot", playerReader.Bits.SpellOn_AutoShot }, - - // Temporary Enchants - { "HasMainHandEnchant", playerReader.Bits.HasMainHandTempEnchant }, - { "HasOffHandEnchant", playerReader.Bits.HasOffHandTempEnchant }, - - // Equipment - Bag - { "Items Broken", playerReader.Bits.ItemsAreBroken }, - { "BagFull", bagReader.BagsFull }, - { "BagGreyItem", bagReader.AnyGreyItem }, - { "HasRangedWeapon", equipmentReader.HasRanged }, - { "HasAmmo", playerReader.Bits.HasAmmo }, - - { "Casting", playerReader.IsCasting }, - { "HasTarget", playerReader.Bits.HasTarget }, - { "TargetAlive", playerReader.Bits.TargetAlive }, - - // General Buff Condition - { Food, playerReader.Buffs.Food }, - { Drink, playerReader.Buffs.Drink }, - { "Mana Regeneration", playerReader.Buffs.Mana_Regeneration }, - { "Well Fed", playerReader.Buffs.Well_Fed }, - { "Clearcasting", playerReader.Buffs.Clearcasting }, - - // Player Affected - { Swimming, playerReader.Bits.IsSwimming }, - { Falling, playerReader.Bits.IsFalling }, - - //Priest - { "Fortitude", playerReader.Buffs.Fortitude }, - { "InnerFire", playerReader.Buffs.InnerFire }, - { "Divine Spirit", playerReader.Buffs.DivineSpirit }, - { "Renew", playerReader.Buffs.Renew }, - { "Shield", playerReader.Buffs.Shield }, - - // Druid - { "Mark of the Wild", playerReader.Buffs.MarkOfTheWild }, - { "Thorns", playerReader.Buffs.Thorns }, - { "TigersFury", playerReader.Buffs.TigersFury }, - { "Prowl", playerReader.Buffs.Prowl }, - { "Rejuvenation", playerReader.Buffs.Rejuvenation }, - { "Regrowth", playerReader.Buffs.Regrowth }, - { "Omen of Clarity", playerReader.Buffs.OmenOfClarity }, - - // Paladin - { "Seal of Righteousness", playerReader.Buffs.SealofRighteousness }, - { "Seal of the Crusader", playerReader.Buffs.SealoftheCrusader }, - { "Seal of Command", playerReader.Buffs.SealofCommand }, - { "Seal of Wisdom", playerReader.Buffs.SealofWisdom }, - { "Seal of Light", playerReader.Buffs.SealofLight }, - { "Seal of Blood", playerReader.Buffs.SealofBlood }, - { "Seal of Vengeance", playerReader.Buffs.SealofVengeance }, - { "Blessing of Might", playerReader.Buffs.BlessingofMight }, - { "Blessing of Protection", playerReader.Buffs.BlessingofProtection }, - { "Blessing of Wisdom", playerReader.Buffs.BlessingofWisdom }, - { "Blessing of Kings", playerReader.Buffs.BlessingofKings }, - { "Blessing of Salvation", playerReader.Buffs.BlessingofSalvation }, - { "Blessing of Sanctuary", playerReader.Buffs.BlessingofSanctuary }, - { "Blessing of Light", playerReader.Buffs.BlessingofLight }, - { "Righteous Fury", playerReader.Buffs.RighteousFury }, - { "Divine Protection", playerReader.Buffs.DivineProtection }, - { "Avenging Wrath", playerReader.Buffs.AvengingWrath }, - { "Holy Shield", playerReader.Buffs.HolyShield }, - { "Divine Shield", playerReader.Buffs.DivineShield }, - - // Mage - { "Frost Armor", playerReader.Buffs.FrostArmor }, - { "Ice Armor", playerReader.Buffs.FrostArmor }, - { "Molten Armor", playerReader.Buffs.FrostArmor }, - { "Mage Armor", playerReader.Buffs.FrostArmor }, - { "Arcane Intellect", playerReader.Buffs.ArcaneIntellect }, - { "Ice Barrier", playerReader.Buffs.IceBarrier }, - { "Ward", playerReader.Buffs.Ward }, - { "Fire Power", playerReader.Buffs.FirePower }, - { "Mana Shield", playerReader.Buffs.ManaShield }, - { "Presence of Mind", playerReader.Buffs.PresenceOfMind }, - { "Arcane Power", playerReader.Buffs.ArcanePower }, - - // Rogue - { "Slice and Dice", playerReader.Buffs.SliceAndDice }, - { "Stealth", playerReader.Buffs.Stealth }, - - // Warrior - { "Battle Shout", playerReader.Buffs.BattleShout }, - { "Bloodrage", playerReader.Buffs.Bloodrage }, - - // Warlock - { "Demon Skin", playerReader.Buffs.Demon }, - { "Demon Armor", playerReader.Buffs.Demon }, - { "Soul Link", playerReader.Buffs.SoulLink }, - { "Soulstone Resurrection", playerReader.Buffs.SoulstoneResurrection }, - { "Shadow Trance", playerReader.Buffs.ShadowTrance }, - { "Fel Armor", playerReader.Buffs.FelArmor }, - { "Fel Domination", playerReader.Buffs.FelDomination }, - { "Demonic Sacrifice", playerReader.Buffs.DemonicSacrifice }, - - // Shaman - { "Lightning Shield", playerReader.Buffs.LightningShield }, - { "Water Shield", playerReader.Buffs.WaterShield }, - { "Shamanistic Focus", playerReader.Buffs.ShamanisticFocus }, - { "Focused", playerReader.Buffs.ShamanisticFocus }, - { "Stoneskin", playerReader.Buffs.Stoneskin }, - - //Hunter - { "Aspect of the Cheetah", playerReader.Buffs.AspectoftheCheetah }, - { "Aspect of the Pack", playerReader.Buffs.AspectofthePack }, - { "Aspect of the Hawk", playerReader.Buffs.AspectoftheHawk }, - { "Aspect of the Monkey", playerReader.Buffs.AspectoftheMonkey }, - { "Aspect of the Viper", playerReader.Buffs.AspectoftheViper }, - { "Rapid Fire", playerReader.Buffs.RapidFire }, - { "Quick Shots", playerReader.Buffs.QuickShots }, - { "Trueshot Aura", playerReader.Buffs.TrueshotAura }, - { "Aspect of the Dragonhawk", playerReader.Buffs.AspectoftheDragonhawk }, - { "Lock and Load", playerReader.Buffs.LockandLoad }, - - //Death Knight - { "Blood Tap", playerReader.Buffs.BloodTap }, - { "Horn of Winter", playerReader.Buffs.HornofWinter }, - { "Icebound Fortitude", playerReader.Buffs.IceboundFortitude }, - { "Path of Frost", playerReader.Buffs.PathofFrost }, - { "Anti-Magic Shell", playerReader.Buffs.AntiMagicShell }, - { "Army of the Dead", playerReader.Buffs.ArmyoftheDead }, - { "Vampiric Blood", playerReader.Buffs.VampiricBlood }, - { "Dancing Rune Weapon", playerReader.Buffs.DancingRuneWeapon }, - { "Unbreakable Armor", playerReader.Buffs.UnbreakableArmor }, - { "Bone Shield", playerReader.Buffs.BoneShield }, - { "Summon Gargoyle", playerReader.Buffs.SummonGargoyle }, - { "Freezing Fog", playerReader.Buffs.FreezingFog }, - - // Debuff Section - // Druid Debuff - { "Demoralizing Roar", playerReader.TargetDebuffs.Roar }, - { "Faerie Fire", playerReader.TargetDebuffs.FaerieFire }, - { "Rip", playerReader.TargetDebuffs.Rip }, - { "Moonfire", playerReader.TargetDebuffs.Moonfire }, - { "Entangling Roots", playerReader.TargetDebuffs.EntanglingRoots }, - { "Rake", playerReader.TargetDebuffs.Rake }, - - // Paladin Debuff - { "Judgement of the Crusader", playerReader.TargetDebuffs.JudgementoftheCrusader }, - { "Hammer of Justice", playerReader.TargetDebuffs.HammerOfJustice }, - { "Judgement of Wisdom", playerReader.TargetDebuffs.JudgementofWisdom }, - { "Judgement of Light", playerReader.TargetDebuffs.JudgementofLight }, - { "Judgement of Justice", playerReader.TargetDebuffs.JudgementofJustice }, - { "Judgement of Any", playerReader.TargetDebuffs.JudgementAny }, - - // Warrior Debuff - { "Rend", playerReader.TargetDebuffs.Rend }, - { "Thunder Clap", playerReader.TargetDebuffs.ThunderClap }, - { "Hamstring", playerReader.TargetDebuffs.Hamstring }, - { "Charge Stun", playerReader.TargetDebuffs.ChargeStun }, - - // Priest Debuff - { "Shadow Word: Pain", playerReader.TargetDebuffs.ShadowWordPain }, - - // Mage Debuff - { "Frostbite", playerReader.TargetDebuffs.Frostbite }, - { "Slow", playerReader.TargetDebuffs.Slow }, - - // Warlock Debuff - { "Curse of Weakness", playerReader.TargetDebuffs.Curseof }, - { "Curse of Elements", playerReader.TargetDebuffs.Curseof }, - { "Curse of Recklessness", playerReader.TargetDebuffs.Curseof }, - { "Curse of Shadow", playerReader.TargetDebuffs.Curseof }, - { "Curse of Agony", playerReader.TargetDebuffs.Curseof }, - { "Curse of", playerReader.TargetDebuffs.Curseof }, - { "Corruption", playerReader.TargetDebuffs.Corruption }, - { "Immolate", playerReader.TargetDebuffs.Immolate }, - { "Siphon Life", playerReader.TargetDebuffs.SiphonLife }, - - // Hunter Debuff - { "Serpent Sting", playerReader.TargetDebuffs.SerpentSting }, - { "Hunter's Mark", playerReader.TargetDebuffs.HuntersMark }, - { "Viper Sting", playerReader.TargetDebuffs.ViperSting }, - { "Explosive Shot", playerReader.TargetDebuffs.ExplosiveShot }, - { "Black Arrow", playerReader.TargetDebuffs.BlackArrow }, - - // Death Knight Debuff - { "Blood Plague", playerReader.TargetDebuffs.BloodPlague }, - { "Frost Fever", playerReader.TargetDebuffs.FrostFever }, - { "Strangulate", playerReader.TargetDebuffs.Strangulate }, - { "Chains of Ice", playerReader.TargetDebuffs.ChainsofIce }, - }; - - intVariables = new Dictionary> - { - { HealthP, playerReader.HealthPercent }, - { "TargetHealth%", playerReader.TargetHealthPercentage }, - { "PetHealth%", playerReader.PetHealthPercentage }, - { ManaP, playerReader.ManaPercentage }, - { "Mana", playerReader.ManaCurrent }, - { "Energy", playerReader.PTCurrent }, - { "Rage", playerReader.PTCurrent }, - { "RunicPower", playerReader.PTCurrent }, - { "BloodRune", playerReader.BloodRune }, - { "FrostRune", playerReader.FrostRune }, - { "UnholyRune", playerReader.UnholyRune }, - { "TotalRune", playerReader.MaxRune }, - { "Combo Point", playerReader.ComboPoints }, - { "Durability%", playerReader.AvgEquipDurability }, - { "BagCount", bagReader.BagItemCount }, - { "FoodCount", addonReader.BagReader.FoodItemCount }, - { "DrinkCount", addonReader.BagReader.DrinkItemCount }, - { "MobCount", addonReader.DamageTakenCount }, - { "MinRange", playerReader.MinRange }, - { "MaxRange", playerReader.MaxRange }, - { "LastAutoShotMs", playerReader.AutoShot.ElapsedMs }, - { "LastMainHandMs", playerReader.MainHandSwing.ElapsedMs }, - { "LastTargetDodgeMs", () => Math.Max(0, addonReader.CombatLog.TargetDodge.ElapsedMs()) }, - //"CD_{KeyAction.Name} - //"Cost_{KeyAction.Name}" - //"Buff_{textureId}" - //"Debuff_{textureId}" - //"TBuff_{textureId}" - { "MainHandSpeed", playerReader.MainHandSpeedMs }, - { "MainHandSwing", () => Math.Clamp(playerReader.MainHandSwing.ElapsedMs() - playerReader.MainHandSpeedMs(), -playerReader.MainHandSpeedMs(), 0) }, - { "RangedSpeed", playerReader.RangedSpeedMs }, - { "RangedSwing", () => Math.Clamp(playerReader.AutoShot.ElapsedMs() - playerReader.RangedSpeedMs(), -playerReader.RangedSpeedMs(), 0) }, - { "CurGCD", playerReader.GCD._Value }, - { "GCD", CastingHandler._GCD } - }; - } + { ">=", CreateGreaterOrEquals }, + { "<=", CreateLesserOrEquals }, + { ">", CreateGreaterThen }, + { "<", CreateLesserThen }, + { "==", CreateEquals }, + { "npcID:", CreateNpcId }, + { "BagItem:", CreateBagItem }, + { "SpellInRange:", CreateSpellInRange }, + { "TargetCastingSpell", CreateTargetCastingSpell }, + { "Form", CreateForm }, + { "Race", CreateRace }, + { "Spell", CreateSpell }, + { "Talent", CreateTalent }, + { "Trigger:", CreateTrigger }, + { "Usable:", CreateUsable } + }; - public void AddSequenceRange(KeyActions keyActions) + boolVariables = new() { - int sizeBefore = this.keyActions.Length; - Array.Resize(ref this.keyActions, this.keyActions.Length + keyActions.Sequence.Length); - Array.ConstrainedCopy(keyActions.Sequence, 0, this.keyActions, sizeBefore, keyActions.Sequence.Length); - } + // Target Based + { "TargetYieldXP", playerReader.Bits.TargetIsNotTrivial }, + { "TargetsMe", playerReader.TargetsMe }, + { "TargetsPet", playerReader.TargetsPet }, + { "TargetsNone", playerReader.TargetsNone }, + + { AddVisible, npcNameFinder._PotentialAddsExist }, + { "InCombat", playerReader.Bits.PlayerInCombat }, + + // Range + { "InMeleeRange", playerReader.IsInMeleeRange }, + { "InCloseMeleeRange", playerReader.InCloseMeleeRange }, + { "InDeadZoneRange", playerReader.IsInDeadZone }, + { "OutOfCombatRange", playerReader.OutOfCombatRange }, + { "InCombatRange", playerReader.WithInCombatRange }, + + // Pet + { "Has Pet", playerReader.Bits.HasPet }, + { "Pet Happy", playerReader.Bits.PetHappy }, + { "Pet HasTarget", playerReader.PetHasTarget }, + { "Mounted", playerReader.Bits.IsMounted }, + + // Auto Spell + { "AutoAttacking", playerReader.Bits.SpellOn_AutoAttack }, + { "Shooting", playerReader.Bits.SpellOn_Shoot }, + { "AutoShot", playerReader.Bits.SpellOn_AutoShot }, + + // Temporary Enchants + { "HasMainHandEnchant", playerReader.Bits.HasMainHandTempEnchant }, + { "HasOffHandEnchant", playerReader.Bits.HasOffHandTempEnchant }, + + // Equipment - Bag + { "Items Broken", playerReader.Bits.ItemsAreBroken }, + { "BagFull", bagReader.BagsFull }, + { "BagGreyItem", bagReader.AnyGreyItem }, + { "HasRangedWeapon", equipmentReader.HasRanged }, + { "HasAmmo", playerReader.Bits.HasAmmo }, + + { "Casting", playerReader.IsCasting }, + { "HasTarget", playerReader.Bits.HasTarget }, + { "TargetAlive", playerReader.Bits.TargetAlive }, + + // General Buff Condition + { Food, playerReader.Buffs.Food }, + { Drink, playerReader.Buffs.Drink }, + { "Mana Regeneration", playerReader.Buffs.Mana_Regeneration }, + { "Well Fed", playerReader.Buffs.Well_Fed }, + { "Clearcasting", playerReader.Buffs.Clearcasting }, + + // Player Affected + { Swimming, playerReader.Bits.IsSwimming }, + { Falling, playerReader.Bits.IsFalling }, + + //Priest + { "Fortitude", playerReader.Buffs.Fortitude }, + { "InnerFire", playerReader.Buffs.InnerFire }, + { "Divine Spirit", playerReader.Buffs.DivineSpirit }, + { "Renew", playerReader.Buffs.Renew }, + { "Shield", playerReader.Buffs.Shield }, + + // Druid + { "Mark of the Wild", playerReader.Buffs.MarkOfTheWild }, + { "Thorns", playerReader.Buffs.Thorns }, + { "TigersFury", playerReader.Buffs.TigersFury }, + { "Prowl", playerReader.Buffs.Prowl }, + { "Rejuvenation", playerReader.Buffs.Rejuvenation }, + { "Regrowth", playerReader.Buffs.Regrowth }, + { "Omen of Clarity", playerReader.Buffs.OmenOfClarity }, + + // Paladin + { "Seal of Righteousness", playerReader.Buffs.SealofRighteousness }, + { "Seal of the Crusader", playerReader.Buffs.SealoftheCrusader }, + { "Seal of Command", playerReader.Buffs.SealofCommand }, + { "Seal of Wisdom", playerReader.Buffs.SealofWisdom }, + { "Seal of Light", playerReader.Buffs.SealofLight }, + { "Seal of Blood", playerReader.Buffs.SealofBlood }, + { "Seal of Vengeance", playerReader.Buffs.SealofVengeance }, + { "Blessing of Might", playerReader.Buffs.BlessingofMight }, + { "Blessing of Protection", playerReader.Buffs.BlessingofProtection }, + { "Blessing of Wisdom", playerReader.Buffs.BlessingofWisdom }, + { "Blessing of Kings", playerReader.Buffs.BlessingofKings }, + { "Blessing of Salvation", playerReader.Buffs.BlessingofSalvation }, + { "Blessing of Sanctuary", playerReader.Buffs.BlessingofSanctuary }, + { "Blessing of Light", playerReader.Buffs.BlessingofLight }, + { "Righteous Fury", playerReader.Buffs.RighteousFury }, + { "Divine Protection", playerReader.Buffs.DivineProtection }, + { "Avenging Wrath", playerReader.Buffs.AvengingWrath }, + { "Holy Shield", playerReader.Buffs.HolyShield }, + { "Divine Shield", playerReader.Buffs.DivineShield }, + + // Mage + { "Frost Armor", playerReader.Buffs.FrostArmor }, + { "Ice Armor", playerReader.Buffs.FrostArmor }, + { "Molten Armor", playerReader.Buffs.FrostArmor }, + { "Mage Armor", playerReader.Buffs.FrostArmor }, + { "Arcane Intellect", playerReader.Buffs.ArcaneIntellect }, + { "Ice Barrier", playerReader.Buffs.IceBarrier }, + { "Ward", playerReader.Buffs.Ward }, + { "Fire Power", playerReader.Buffs.FirePower }, + { "Mana Shield", playerReader.Buffs.ManaShield }, + { "Presence of Mind", playerReader.Buffs.PresenceOfMind }, + { "Arcane Power", playerReader.Buffs.ArcanePower }, + + // Rogue + { "Slice and Dice", playerReader.Buffs.SliceAndDice }, + { "Stealth", playerReader.Buffs.Stealth }, + + // Warrior + { "Battle Shout", playerReader.Buffs.BattleShout }, + { "Bloodrage", playerReader.Buffs.Bloodrage }, + + // Warlock + { "Demon Skin", playerReader.Buffs.Demon }, + { "Demon Armor", playerReader.Buffs.Demon }, + { "Soul Link", playerReader.Buffs.SoulLink }, + { "Soulstone Resurrection", playerReader.Buffs.SoulstoneResurrection }, + { "Shadow Trance", playerReader.Buffs.ShadowTrance }, + { "Fel Armor", playerReader.Buffs.FelArmor }, + { "Fel Domination", playerReader.Buffs.FelDomination }, + { "Demonic Sacrifice", playerReader.Buffs.DemonicSacrifice }, + + // Shaman + { "Lightning Shield", playerReader.Buffs.LightningShield }, + { "Water Shield", playerReader.Buffs.WaterShield }, + { "Shamanistic Focus", playerReader.Buffs.ShamanisticFocus }, + { "Focused", playerReader.Buffs.ShamanisticFocus }, + { "Stoneskin", playerReader.Buffs.Stoneskin }, + + //Hunter + { "Aspect of the Cheetah", playerReader.Buffs.AspectoftheCheetah }, + { "Aspect of the Pack", playerReader.Buffs.AspectofthePack }, + { "Aspect of the Hawk", playerReader.Buffs.AspectoftheHawk }, + { "Aspect of the Monkey", playerReader.Buffs.AspectoftheMonkey }, + { "Aspect of the Viper", playerReader.Buffs.AspectoftheViper }, + { "Rapid Fire", playerReader.Buffs.RapidFire }, + { "Quick Shots", playerReader.Buffs.QuickShots }, + { "Trueshot Aura", playerReader.Buffs.TrueshotAura }, + { "Aspect of the Dragonhawk", playerReader.Buffs.AspectoftheDragonhawk }, + { "Lock and Load", playerReader.Buffs.LockandLoad }, + + //Death Knight + { "Blood Tap", playerReader.Buffs.BloodTap }, + { "Horn of Winter", playerReader.Buffs.HornofWinter }, + { "Icebound Fortitude", playerReader.Buffs.IceboundFortitude }, + { "Path of Frost", playerReader.Buffs.PathofFrost }, + { "Anti-Magic Shell", playerReader.Buffs.AntiMagicShell }, + { "Army of the Dead", playerReader.Buffs.ArmyoftheDead }, + { "Vampiric Blood", playerReader.Buffs.VampiricBlood }, + { "Dancing Rune Weapon", playerReader.Buffs.DancingRuneWeapon }, + { "Unbreakable Armor", playerReader.Buffs.UnbreakableArmor }, + { "Bone Shield", playerReader.Buffs.BoneShield }, + { "Summon Gargoyle", playerReader.Buffs.SummonGargoyle }, + { "Freezing Fog", playerReader.Buffs.FreezingFog }, + + // Debuff Section + // Druid Debuff + { "Demoralizing Roar", playerReader.TargetDebuffs.Roar }, + { "Faerie Fire", playerReader.TargetDebuffs.FaerieFire }, + { "Rip", playerReader.TargetDebuffs.Rip }, + { "Moonfire", playerReader.TargetDebuffs.Moonfire }, + { "Entangling Roots", playerReader.TargetDebuffs.EntanglingRoots }, + { "Rake", playerReader.TargetDebuffs.Rake }, + + // Paladin Debuff + { "Judgement of the Crusader", playerReader.TargetDebuffs.JudgementoftheCrusader }, + { "Hammer of Justice", playerReader.TargetDebuffs.HammerOfJustice }, + { "Judgement of Wisdom", playerReader.TargetDebuffs.JudgementofWisdom }, + { "Judgement of Light", playerReader.TargetDebuffs.JudgementofLight }, + { "Judgement of Justice", playerReader.TargetDebuffs.JudgementofJustice }, + { "Judgement of Any", playerReader.TargetDebuffs.JudgementAny }, + + // Warrior Debuff + { "Rend", playerReader.TargetDebuffs.Rend }, + { "Thunder Clap", playerReader.TargetDebuffs.ThunderClap }, + { "Hamstring", playerReader.TargetDebuffs.Hamstring }, + { "Charge Stun", playerReader.TargetDebuffs.ChargeStun }, + + // Priest Debuff + { "Shadow Word: Pain", playerReader.TargetDebuffs.ShadowWordPain }, + + // Mage Debuff + { "Frostbite", playerReader.TargetDebuffs.Frostbite }, + { "Slow", playerReader.TargetDebuffs.Slow }, + + // Warlock Debuff + { "Curse of Weakness", playerReader.TargetDebuffs.Curseof }, + { "Curse of Elements", playerReader.TargetDebuffs.Curseof }, + { "Curse of Recklessness", playerReader.TargetDebuffs.Curseof }, + { "Curse of Shadow", playerReader.TargetDebuffs.Curseof }, + { "Curse of Agony", playerReader.TargetDebuffs.Curseof }, + { "Curse of", playerReader.TargetDebuffs.Curseof }, + { "Corruption", playerReader.TargetDebuffs.Corruption }, + { "Immolate", playerReader.TargetDebuffs.Immolate }, + { "Siphon Life", playerReader.TargetDebuffs.SiphonLife }, + + // Hunter Debuff + { "Serpent Sting", playerReader.TargetDebuffs.SerpentSting }, + { "Hunter's Mark", playerReader.TargetDebuffs.HuntersMark }, + { "Viper Sting", playerReader.TargetDebuffs.ViperSting }, + { "Explosive Shot", playerReader.TargetDebuffs.ExplosiveShot }, + { "Black Arrow", playerReader.TargetDebuffs.BlackArrow }, + + // Death Knight Debuff + { "Blood Plague", playerReader.TargetDebuffs.BloodPlague }, + { "Frost Fever", playerReader.TargetDebuffs.FrostFever }, + { "Strangulate", playerReader.TargetDebuffs.Strangulate }, + { "Chains of Ice", playerReader.TargetDebuffs.ChainsofIce }, + }; - public void InitialiseRequirements(KeyAction item) + intVariables = new Dictionary> { - if (item.Name is Drink or Food) - AddConsumableRequirement(item); + { HealthP, playerReader.HealthPercent }, + { "TargetHealth%", playerReader.TargetHealthPercentage }, + { "PetHealth%", playerReader.PetHealthPercentage }, + { ManaP, playerReader.ManaPercentage }, + { "Mana", playerReader.ManaCurrent }, + { "Energy", playerReader.PTCurrent }, + { "Rage", playerReader.PTCurrent }, + { "RunicPower", playerReader.PTCurrent }, + { "BloodRune", playerReader.BloodRune }, + { "FrostRune", playerReader.FrostRune }, + { "UnholyRune", playerReader.UnholyRune }, + { "TotalRune", playerReader.MaxRune }, + { "Combo Point", playerReader.ComboPoints }, + { "Durability%", playerReader.AvgEquipDurability }, + { "BagCount", bagReader.BagItemCount }, + { "FoodCount", addonReader.BagReader.FoodItemCount }, + { "DrinkCount", addonReader.BagReader.DrinkItemCount }, + { "MobCount", addonReader.DamageTakenCount }, + { "MinRange", playerReader.MinRange }, + { "MaxRange", playerReader.MaxRange }, + { "LastAutoShotMs", playerReader.AutoShot.ElapsedMs }, + { "LastMainHandMs", playerReader.MainHandSwing.ElapsedMs }, + { "LastTargetDodgeMs", () => Math.Max(0, addonReader.CombatLog.TargetDodge.ElapsedMs()) }, + //"CD_{KeyAction.Name} + //"Cost_{KeyAction.Name}" + //"Buff_{textureId}" + //"Debuff_{textureId}" + //"TBuff_{textureId}" + { "MainHandSpeed", playerReader.MainHandSpeedMs }, + { "MainHandSwing", () => Math.Clamp(playerReader.MainHandSwing.ElapsedMs() - playerReader.MainHandSpeedMs(), -playerReader.MainHandSpeedMs(), 0) }, + { "RangedSpeed", playerReader.RangedSpeedMs }, + { "RangedSwing", () => Math.Clamp(playerReader.AutoShot.ElapsedMs() - playerReader.RangedSpeedMs(), -playerReader.RangedSpeedMs(), 0) }, + { "CurGCD", playerReader.GCD._Value }, + { "GCD", CastingHandler._GCD } + }; + } - InitPerKeyActionRequirements(item); + public void AddSequenceRange(KeyActions keyActions) + { + int sizeBefore = this.keyActions.Length; + Array.Resize(ref this.keyActions, this.keyActions.Length + keyActions.Sequence.Length); + Array.ConstrainedCopy(keyActions.Sequence, 0, this.keyActions, sizeBefore, keyActions.Sequence.Length); + } - List requirements = new(); + public void InitialiseRequirements(KeyAction item) + { + if (item.Name is Drink or Food) + AddConsumableRequirement(item); + + InitPerKeyActionRequirements(item); + + List requirements = new(); - foreach (string requirement in CollectionsMarshal.AsSpan(item.Requirements)) + foreach (string requirement in CollectionsMarshal.AsSpan(item.Requirements)) + { + List expressions = InfixToPostfix.Convert(requirement); + Stack stack = new(); + foreach (string expr in CollectionsMarshal.AsSpan(expressions)) { - List expressions = InfixToPostfix.Convert(requirement); - Stack stack = new(); - foreach (string expr in CollectionsMarshal.AsSpan(expressions)) + if (expr.Contains(Requirement.SymbolAnd)) { - if (expr.Contains(Requirement.SymbolAnd)) - { - Requirement a = stack.Pop(); - Requirement b = stack.Pop(); - b.And(a); + Requirement a = stack.Pop(); + Requirement b = stack.Pop(); + b.And(a); - stack.Push(b); - } - else if (expr.Contains(Requirement.SymbolOr)) - { - Requirement a = stack.Pop(); - Requirement b = stack.Pop(); - b.Or(a); + stack.Push(b); + } + else if (expr.Contains(Requirement.SymbolOr)) + { + Requirement a = stack.Pop(); + Requirement b = stack.Pop(); + b.Or(a); - stack.Push(b); - } - else + stack.Push(b); + } + else + { + string trim = expr.Trim(); + if (string.IsNullOrEmpty(trim)) { - string trim = expr.Trim(); - if (string.IsNullOrEmpty(trim)) - { - continue; - } - - stack.Push(CreateRequirement(item.Name, trim)); + continue; } - } - requirements.Add(stack.Pop()); + stack.Push(CreateRequirement(item.Name, trim)); + } } - AddMinRequirement(requirements, item); - AddTargetIsCastingRequirement(requirements, item, playerReader); + requirements.Add(stack.Pop()); + } - if (item.WhenUsable && !string.IsNullOrEmpty(item.Key)) - { - requirements.Add(CreateActionUsableRequirement(item, playerReader, addonReader.UsableAction)); + AddMinRequirement(requirements, item); + AddTargetIsCastingRequirement(requirements, item, playerReader); - if (item.Slot > 0) - requirements.Add(CreateActionNotInGameCooldown(item, playerReader, intVariables)); - } + if (item.WhenUsable && !string.IsNullOrEmpty(item.Key)) + { + requirements.Add(CreateActionUsableRequirement(item, playerReader, addonReader.UsableAction)); - AddCooldownRequirement(requirements, item); - AddChargeRequirement(requirements, item); + if (item.Slot > 0) + requirements.Add(CreateActionNotInGameCooldown(item, playerReader, intVariables)); + } - AddSpellSchoolRequirement(requirements, item, playerReader, immunityBlacklist); + AddCooldownRequirement(requirements, item); + AddChargeRequirement(requirements, item); - item.RequirementsRuntime = requirements.ToArray(); + AddSpellSchoolRequirement(requirements, item, playerReader, immunityBlacklist); - if (item.Interrupts.Count > 0) - InitialiseInterrupts(item); - } + item.RequirementsRuntime = requirements.ToArray(); - private void InitialiseInterrupts(KeyAction item) - { - List requirements = new(); + if (item.Interrupts.Count > 0) + InitialiseInterrupts(item); + } + + private void InitialiseInterrupts(KeyAction item) + { + List requirements = new(); - foreach (string requirement in CollectionsMarshal.AsSpan(item.Interrupts)) + foreach (string requirement in CollectionsMarshal.AsSpan(item.Interrupts)) + { + List expressions = InfixToPostfix.Convert(requirement); + Stack stack = new(); + foreach (string expr in CollectionsMarshal.AsSpan(expressions)) { - List expressions = InfixToPostfix.Convert(requirement); - Stack stack = new(); - foreach (string expr in CollectionsMarshal.AsSpan(expressions)) + if (expr.Contains(Requirement.SymbolAnd)) { - if (expr.Contains(Requirement.SymbolAnd)) - { - Requirement a = stack.Pop(); - Requirement b = stack.Pop(); - b.And(a); + Requirement a = stack.Pop(); + Requirement b = stack.Pop(); + b.And(a); - stack.Push(b); - } - else if (expr.Contains(Requirement.SymbolOr)) - { - Requirement a = stack.Pop(); - Requirement b = stack.Pop(); - b.Or(a); + stack.Push(b); + } + else if (expr.Contains(Requirement.SymbolOr)) + { + Requirement a = stack.Pop(); + Requirement b = stack.Pop(); + b.Or(a); - stack.Push(b); - } - else + stack.Push(b); + } + else + { + string trim = expr.Trim(); + if (string.IsNullOrEmpty(trim)) { - string trim = expr.Trim(); - if (string.IsNullOrEmpty(trim)) - { - continue; - } - - stack.Push(CreateRequirement(item.Name, trim)); + continue; } - } - requirements.Add(stack.Pop()); + stack.Push(CreateRequirement(item.Name, trim)); + } } - item.InterruptsRuntime = requirements.ToArray(); + requirements.Add(stack.Pop()); } - public void InitUserDefinedIntVariables(Dictionary intKeyValues, - AuraTimeReader playerBuffTimeReader, AuraTimeReader targetDebuffTimeReader, - AuraTimeReader targetBuffTimeReader) + item.InterruptsRuntime = requirements.ToArray(); + } + + public void InitUserDefinedIntVariables(Dictionary intKeyValues, + AuraTimeReader playerBuffTimeReader, AuraTimeReader targetDebuffTimeReader, + AuraTimeReader targetBuffTimeReader) + { + foreach ((string key, int value) in intKeyValues) { - foreach ((string key, int value) in intKeyValues) - { - int f() => value; + int f() => value; - if (!intVariables.TryAdd(key, f)) + if (!intVariables.TryAdd(key, f)) + { + throw new Exception($"Unable to add user defined variable to values. [{key} -> {value}]"); + } + else + { + if (key.StartsWith("Buff_")) { - throw new Exception($"Unable to add user defined variable to values. [{key} -> {value}]"); + int l() => playerBuffTimeReader.GetRemainingTimeMs(value); + intVariables.TryAdd($"{value}", l); } - else + else if (key.StartsWith("Debuff_")) { - if (key.StartsWith("Buff_")) - { - int l() => playerBuffTimeReader.GetRemainingTimeMs(value); - intVariables.TryAdd($"{value}", l); - } - else if (key.StartsWith("Debuff_")) - { - int l() => targetDebuffTimeReader.GetRemainingTimeMs(value); - intVariables.TryAdd($"{value}", l); - } - else if (key.StartsWith("TBuff_")) - { - int l() => targetBuffTimeReader.GetRemainingTimeMs(value); - intVariables.TryAdd($"{value}", l); - } - - LogUserDefinedValue(logger, nameof(RequirementFactory), key, value); + int l() => targetDebuffTimeReader.GetRemainingTimeMs(value); + intVariables.TryAdd($"{value}", l); } + else if (key.StartsWith("TBuff_")) + { + int l() => targetBuffTimeReader.GetRemainingTimeMs(value); + intVariables.TryAdd($"{value}", l); + } + + LogUserDefinedValue(logger, nameof(RequirementFactory), key, value); } } + } - public void InitDynamicBindings(KeyAction item) - { - if (string.IsNullOrEmpty(item.Name) || item.Slot == 0) return; + public void InitDynamicBindings(KeyAction item) + { + if (string.IsNullOrEmpty(item.Name) || item.Slot == 0) return; - BindCooldown(item, addonReader.ActionBarCooldownReader); - BindMinCost(item, addonReader.ActionBarCostReader); + BindCooldown(item, addonReader.ActionBarCooldownReader); + BindMinCost(item, addonReader.ActionBarCostReader); + } + + private void BindCooldown(KeyAction item, ActionBarCooldownReader reader) + { + string key = $"CD_{item.Name}"; + if (!intVariables.ContainsKey(key)) + { + intVariables.Add(key, () => reader.Get(item)); } + } - private void BindCooldown(KeyAction item, ActionBarCooldownReader reader) + private void BindMinCost(KeyAction item, ActionBarCostReader reader) + { + string key = $"Cost_{item.Name}"; + if (!intVariables.ContainsKey(key)) { - string key = $"CD_{item.Name}"; - if (!intVariables.ContainsKey(key)) - { - intVariables.Add(key, () => reader.Get(item)); - } + intVariables.Add(key, + () => reader.Get(item).Cost); } + } + + private void InitPerKeyActionRequirements(KeyAction item) + { + InitPerKeyActionRequirementByKey(item, "CD"); + InitPerKeyActionRequirementByKey(item, "Cost"); + } + + private void InitPerKeyActionRequirementByKey(KeyAction item, string prefixKey) + { + string key = $"{prefixKey}_{item.Name}"; + intVariables.Remove(prefixKey); - private void BindMinCost(KeyAction item, ActionBarCostReader reader) + if (intVariables.TryGetValue(key, out Func? func)) + intVariables.Add(prefixKey, func); + } + + private void AddTargetIsCastingRequirement(List list, KeyAction item, PlayerReader playerReader) + { + if (item.UseWhenTargetIsCasting == null) + return; + + bool f() => playerReader.IsTargetCasting() == item.UseWhenTargetIsCasting.Value; + string l() => "Target casting"; + list.Add(new Requirement { - string key = $"Cost_{item.Name}"; - if (!intVariables.ContainsKey(key)) - { - intVariables.Add(key, - () => reader.Get(item).Cost); - } + HasRequirement = f, + LogMessage = l + }); + } + + private void AddMinRequirement(List list, KeyAction item) + { + AddMinPowerTypeRequirement(list, PowerType.Mana, item, playerReader); + AddMinPowerTypeRequirement(list, PowerType.Rage, item, playerReader); + AddMinPowerTypeRequirement(list, PowerType.Energy, item, playerReader); + if (playerReader.Class == UnitClass.DeathKnight) + { + AddMinPowerTypeRequirement(list, PowerType.RunicPower, item, playerReader); + AddMinPowerTypeRequirement(list, PowerType.RuneBlood, item, playerReader); + AddMinPowerTypeRequirement(list, PowerType.RuneFrost, item, playerReader); + AddMinPowerTypeRequirement(list, PowerType.RuneUnholy, item, playerReader); } + AddMinComboPointsRequirement(list, item, playerReader); + } - private void InitPerKeyActionRequirements(KeyAction item) + private void AddMinPowerTypeRequirement(List list, PowerType type, KeyAction keyAction, PlayerReader playerReader) + { + switch (type) { - InitPerKeyActionRequirementByKey(item, "CD"); - InitPerKeyActionRequirementByKey(item, "Cost"); + case PowerType.Mana: + bool fmana() => playerReader.ManaCurrent() >= keyAction.MinMana || playerReader.Buffs.Clearcasting(); + string smana() => $"{type.ToStringF()} {playerReader.ManaCurrent()} >= {keyAction.MinMana}"; + list.Add(new Requirement + { + HasRequirement = fmana, + LogMessage = smana, + VisibleIfHasRequirement = keyAction.MinMana > 0 + }); + break; + case PowerType.Rage: + bool frage() => playerReader.PTCurrent() >= keyAction.MinRage || playerReader.Buffs.Clearcasting(); + string srage() => $"{type.ToStringF()} {playerReader.PTCurrent()} >= {keyAction.MinRage}"; + list.Add(new Requirement + { + HasRequirement = frage, + LogMessage = srage, + VisibleIfHasRequirement = keyAction.MinRage > 0 + }); + break; + case PowerType.Energy: + bool fenergy() => playerReader.PTCurrent() >= keyAction.MinEnergy || playerReader.Buffs.Clearcasting(); + string senergy() => $"{type.ToStringF()} {playerReader.PTCurrent()} >= {keyAction.MinEnergy}"; + list.Add(new Requirement + { + HasRequirement = fenergy, + LogMessage = senergy, + VisibleIfHasRequirement = keyAction.MinEnergy > 0 + }); + break; + case PowerType.RunicPower: + bool frunicpower() => playerReader.PTCurrent() >= keyAction.MinRunicPower; + string srunicpower() => $"{type.ToStringF()} {playerReader.PTCurrent()} >= {keyAction.MinRunicPower}"; + list.Add(new Requirement + { + HasRequirement = frunicpower, + LogMessage = srunicpower, + VisibleIfHasRequirement = keyAction.MinRunicPower > 0 + }); + break; + case PowerType.RuneBlood: + bool fbloodrune() => playerReader.BloodRune() >= keyAction.MinRuneBlood; + string sbloodrune() => $"{type.ToStringF()} {playerReader.BloodRune()} >= {keyAction.MinRuneBlood}"; + list.Add(new Requirement + { + HasRequirement = fbloodrune, + LogMessage = sbloodrune, + VisibleIfHasRequirement = keyAction.MinRuneBlood > 0 + }); + break; + case PowerType.RuneFrost: + bool ffrostrune() => playerReader.FrostRune() >= keyAction.MinRuneFrost; + string sfrostrune() => $"{type.ToStringF()} {playerReader.FrostRune()} >= {keyAction.MinRuneFrost}"; + list.Add(new Requirement + { + HasRequirement = ffrostrune, + LogMessage = sfrostrune, + VisibleIfHasRequirement = keyAction.MinRuneFrost > 0 + }); + break; + case PowerType.RuneUnholy: + bool funholyrune() => playerReader.UnholyRune() >= keyAction.MinRuneUnholy; + string sunholyrune() => $"{type.ToStringF()} {playerReader.UnholyRune()} >= {keyAction.MinRuneUnholy}"; + list.Add(new Requirement + { + HasRequirement = funholyrune, + LogMessage = sunholyrune, + VisibleIfHasRequirement = keyAction.MinRuneUnholy > 0 + }); + break; } + } + + private void AddMinComboPointsRequirement(List list, KeyAction item, PlayerReader playerReader) + { + if (item.MinComboPoints <= 0) + return; - private void InitPerKeyActionRequirementByKey(KeyAction item, string prefixKey) + bool f() => playerReader.ComboPoints() >= item.MinComboPoints; + string s() => $"Combo point {playerReader.ComboPoints()} >= {item.MinComboPoints}"; + list.Add(new Requirement { - string key = $"{prefixKey}_{item.Name}"; - intVariables.Remove(prefixKey); + HasRequirement = f, + LogMessage = s + }); + } - if (intVariables.TryGetValue(key, out Func? func)) - intVariables.Add(prefixKey, func); - } + private static void AddCooldownRequirement(List list, KeyAction item) + { + if (item.Cooldown <= 0) + return; - private void AddTargetIsCastingRequirement(List list, KeyAction item, PlayerReader playerReader) + bool f() => item.GetRemainingCooldown() == 0; + string s() => $"Cooldown {item.GetRemainingCooldown() / 1000:F1}"; + list.Add(new Requirement { - if (item.UseWhenTargetIsCasting == null) - return; + HasRequirement = f, + LogMessage = s, + VisibleIfHasRequirement = false + }); + } - bool f() => playerReader.IsTargetCasting() == item.UseWhenTargetIsCasting.Value; - string l() => "Target casting"; - list.Add(new Requirement - { - HasRequirement = f, - LogMessage = l - }); - } + private static void AddChargeRequirement(List list, KeyAction item) + { + if (item.BaseAction || item.Charge < 1) + return; - private void AddMinRequirement(List list, KeyAction item) + bool f() => item.GetChargeRemaining() != 0; + string s() => $"Charge {item.GetChargeRemaining()}"; + list.Add(new Requirement { - AddMinPowerTypeRequirement(list, PowerType.Mana, item, playerReader); - AddMinPowerTypeRequirement(list, PowerType.Rage, item, playerReader); - AddMinPowerTypeRequirement(list, PowerType.Energy, item, playerReader); - if (playerReader.Class == UnitClass.DeathKnight) - { - AddMinPowerTypeRequirement(list, PowerType.RunicPower, item, playerReader); - AddMinPowerTypeRequirement(list, PowerType.RuneBlood, item, playerReader); - AddMinPowerTypeRequirement(list, PowerType.RuneFrost, item, playerReader); - AddMinPowerTypeRequirement(list, PowerType.RuneUnholy, item, playerReader); - } - AddMinComboPointsRequirement(list, item, playerReader); + HasRequirement = f, + LogMessage = s, + VisibleIfHasRequirement = false + }); + } + + private static void AddConsumableRequirement(KeyAction item) + { + item.BeforeCastStop = true; + item.WhenUsable = true; + item.AfterCastWaitBuff = true; + item.Item = true; + + item.Requirements.Add($"!{item.Name}"); + item.Requirements.Add($"!{Swimming}"); + item.Requirements.Add($"!{Falling}"); + } + + private void AddSpellSchoolRequirement(List list, KeyAction item, + PlayerReader playerReader, Dictionary immunityBlacklist) + { + if (item.School == SchoolMask.None) + return; + + bool f() => + !immunityBlacklist.TryGetValue(playerReader.TargetId, out SchoolMask[]? immuneAgaints) || + !immuneAgaints.Contains(item.School); + + string s() => item.School.ToStringF(); + list.Add(new Requirement + { + HasRequirement = f, + LogMessage = s, + VisibleIfHasRequirement = false + }); + } + + + public Requirement CreateRequirement(string name, string requirement) + { + LogProcessingRequirement(logger, name, requirement); + + string? negated = negateKeywords.FirstOrDefault(requirement.StartsWith); + if (!string.IsNullOrEmpty(negated)) + { + requirement = requirement[negated.Length..]; } - private void AddMinPowerTypeRequirement(List list, PowerType type, KeyAction keyAction, PlayerReader playerReader) + string? key = requirementMap.Keys.FirstOrDefault(requirement.Contains); + if (!string.IsNullOrEmpty(key)) { - switch (type) + Requirement req = requirementMap[key](requirement); + if (negated != null) { - case PowerType.Mana: - bool fmana() => playerReader.ManaCurrent() >= keyAction.MinMana || playerReader.Buffs.Clearcasting(); - string smana() => $"{type.ToStringF()} {playerReader.ManaCurrent()} >= {keyAction.MinMana}"; - list.Add(new Requirement - { - HasRequirement = fmana, - LogMessage = smana, - VisibleIfHasRequirement = keyAction.MinMana > 0 - }); - break; - case PowerType.Rage: - bool frage() => playerReader.PTCurrent() >= keyAction.MinRage || playerReader.Buffs.Clearcasting(); - string srage() => $"{type.ToStringF()} {playerReader.PTCurrent()} >= {keyAction.MinRage}"; - list.Add(new Requirement - { - HasRequirement = frage, - LogMessage = srage, - VisibleIfHasRequirement = keyAction.MinRage > 0 - }); - break; - case PowerType.Energy: - bool fenergy() => playerReader.PTCurrent() >= keyAction.MinEnergy || playerReader.Buffs.Clearcasting(); - string senergy() => $"{type.ToStringF()} {playerReader.PTCurrent()} >= {keyAction.MinEnergy}"; - list.Add(new Requirement - { - HasRequirement = fenergy, - LogMessage = senergy, - VisibleIfHasRequirement = keyAction.MinEnergy > 0 - }); - break; - case PowerType.RunicPower: - bool frunicpower() => playerReader.PTCurrent() >= keyAction.MinRunicPower; - string srunicpower() => $"{type.ToStringF()} {playerReader.PTCurrent()} >= {keyAction.MinRunicPower}"; - list.Add(new Requirement - { - HasRequirement = frunicpower, - LogMessage = srunicpower, - VisibleIfHasRequirement = keyAction.MinRunicPower > 0 - }); - break; - case PowerType.RuneBlood: - bool fbloodrune() => playerReader.BloodRune() >= keyAction.MinRuneBlood; - string sbloodrune() => $"{type.ToStringF()} {playerReader.BloodRune()} >= {keyAction.MinRuneBlood}"; - list.Add(new Requirement - { - HasRequirement = fbloodrune, - LogMessage = sbloodrune, - VisibleIfHasRequirement = keyAction.MinRuneBlood > 0 - }); - break; - case PowerType.RuneFrost: - bool ffrostrune() => playerReader.FrostRune() >= keyAction.MinRuneFrost; - string sfrostrune() => $"{type.ToStringF()} {playerReader.FrostRune()} >= {keyAction.MinRuneFrost}"; - list.Add(new Requirement - { - HasRequirement = ffrostrune, - LogMessage = sfrostrune, - VisibleIfHasRequirement = keyAction.MinRuneFrost > 0 - }); - break; - case PowerType.RuneUnholy: - bool funholyrune() => playerReader.UnholyRune() >= keyAction.MinRuneUnholy; - string sunholyrune() => $"{type.ToStringF()} {playerReader.UnholyRune()} >= {keyAction.MinRuneUnholy}"; - list.Add(new Requirement - { - HasRequirement = funholyrune, - LogMessage = sunholyrune, - VisibleIfHasRequirement = keyAction.MinRuneUnholy > 0 - }); - break; + req.Negate(negated); } + return req; } - private void AddMinComboPointsRequirement(List list, KeyAction item, PlayerReader playerReader) + if (boolVariables.ContainsKey(requirement)) { - if (item.MinComboPoints <= 0) - return; - - bool f() => playerReader.ComboPoints() >= item.MinComboPoints; - string s() => $"Combo point {playerReader.ComboPoints()} >= {item.MinComboPoints}"; - list.Add(new Requirement + string s() => requirement; + Requirement req = new() { - HasRequirement = f, + HasRequirement = boolVariables[requirement], LogMessage = s - }); - } - - private static void AddCooldownRequirement(List list, KeyAction item) - { - if (item.Cooldown <= 0) - return; + }; - bool f() => item.GetRemainingCooldown() == 0; - string s() => $"Cooldown {item.GetRemainingCooldown() / 1000:F1}"; - list.Add(new Requirement + if (negated != null) { - HasRequirement = f, - LogMessage = s, - VisibleIfHasRequirement = false - }); + req.Negate(negated); + } + return req; } - private static void AddChargeRequirement(List list, KeyAction item) + LogUnknownRequirement(logger, requirement, string.Join(", ", boolVariables.Keys)); + return new Requirement { - if (item.BaseAction || item.Charge < 1) - return; - - bool f() => item.GetChargeRemaining() != 0; - string s() => $"Charge {item.GetChargeRemaining()}"; - list.Add(new Requirement - { - HasRequirement = f, - LogMessage = s, - VisibleIfHasRequirement = false - }); - } + LogMessage = () => $"UNKNOWN REQUIREMENT! {requirement}" + }; + } - private static void AddConsumableRequirement(KeyAction item) + private Requirement CreateActionUsableRequirement(KeyAction item, PlayerReader playerReader, ActionBarBits usableAction) + { + bool CanDoFormChangeMinMana() { - item.BeforeCastStop = true; - item.WhenUsable = true; - item.AfterCastWaitBuff = true; - item.Item = true; - - item.Requirements.Add($"!{item.Name}"); - item.Requirements.Add($"!{Swimming}"); - item.Requirements.Add($"!{Falling}"); + return playerReader.ManaCurrent() >= item.FormCost() + item.MinMana; } - private void AddSpellSchoolRequirement(List list, KeyAction item, - PlayerReader playerReader, Dictionary immunityBlacklist) - { - if (item.School == SchoolMask.None) - return; + bool f() => + !item.HasFormRequirement + ? usableAction.Is(item) + : (playerReader.Form == item.FormEnum && usableAction.Is(item)) || + (playerReader.Form != item.FormEnum && CanDoFormChangeMinMana()); - bool f() => - !immunityBlacklist.TryGetValue(playerReader.TargetId, out SchoolMask[]? immuneAgaints) || - !immuneAgaints.Contains(item.School); + string s() => + !item.HasFormRequirement + ? "Usable" + : (playerReader.Form != item.FormEnum && CanDoFormChangeMinMana()) ? "Usable after Form change" : + (playerReader.Form == item.FormEnum && usableAction.Is(item)) ? "Usable current Form" : "not Usable current Form"; - string s() => item.School.ToStringF(); - list.Add(new Requirement - { - HasRequirement = f, - LogMessage = s, - VisibleIfHasRequirement = false - }); - } + return new Requirement + { + HasRequirement = f, + LogMessage = s + }; + } + private Requirement CreateActionNotInGameCooldown(KeyAction item, PlayerReader playerReader, Dictionary> intVariables) + { + string key = $"CD_{item.Name}"; + bool f() => UsableGCD(key, playerReader, intVariables); + string s() => $"CD {intVariables[key]() / 1000f:F1}"; - public Requirement CreateRequirement(string name, string requirement) + return new Requirement { - LogProcessingRequirement(logger, name, requirement); - - string? negated = negateKeywords.FirstOrDefault(requirement.StartsWith); - if (!string.IsNullOrEmpty(negated)) - { - requirement = requirement[negated.Length..]; - } + HasRequirement = f, + VisibleIfHasRequirement = false, + LogMessage = s + }; + } - string? key = requirementMap.Keys.FirstOrDefault(requirement.Contains); - if (!string.IsNullOrEmpty(key)) - { - Requirement req = requirementMap[key](requirement); - if (negated != null) - { - req.Negate(negated); - } - return req; - } + private static bool UsableGCD(string key, PlayerReader playerReader, Dictionary> intVariables) + { + return intVariables[key]() <= CastingHandler.SPELL_QUEUE - playerReader.NetworkLatency.Value; + } - if (boolVariables.ContainsKey(requirement)) + private Requirement CreateTargetCastingSpell(string requirement) + { + return create(requirement, playerReader); + static Requirement create(string requirement, PlayerReader playerReader) + { + ReadOnlySpan span = requirement; + int sep1 = span.IndexOf(SEP1); + // 'TargetCastingSpell' + if (sep1 == -1) { - string s() => requirement; - Requirement req = new() + return new Requirement { - HasRequirement = boolVariables[requirement], - LogMessage = s + HasRequirement = playerReader.IsTargetCasting, + LogMessage = () => "Target casting" }; - - if (negated != null) - { - req.Negate(negated); - } - return req; } - LogUnknownRequirement(logger, requirement, string.Join(", ", boolVariables.Keys)); + // 'TargetCastingSpell:_1_?,_n_' + string[] spellsPart = span[(sep1 + 1)..].ToString().Split(SEP2); + int[] spellIds = spellsPart.Select(int.Parse).ToArray(); + + bool f() => spellIds.Contains(playerReader.SpellBeingCastByTarget); + string s() => $"Target casts {playerReader.SpellBeingCastByTarget} ∈ [{string.Join(SEP2, spellIds)}]"; return new Requirement { - LogMessage = () => $"UNKNOWN REQUIREMENT! {requirement}" + HasRequirement = f, + LogMessage = s }; } + } - private Requirement CreateActionUsableRequirement(KeyAction item, PlayerReader playerReader, ActionBarBits usableAction) + private Requirement CreateForm(string requirement) + { + return create(requirement, playerReader); + static Requirement create(string requirement, PlayerReader playerReader) { - bool CanDoFormChangeMinMana() - { - return playerReader.ManaCurrent() >= item.FormCost() + item.MinMana; - } - - bool f() => - !item.HasFormRequirement - ? usableAction.Is(item) - : (playerReader.Form == item.FormEnum && usableAction.Is(item)) || - (playerReader.Form != item.FormEnum && CanDoFormChangeMinMana()); + // 'Form:_FORM_' + ReadOnlySpan span = requirement; + int sep = span.IndexOf(SEP1); + Form form = Enum.Parse
(span[(sep + 1)..]); - string s() => - !item.HasFormRequirement - ? "Usable" - : (playerReader.Form != item.FormEnum && CanDoFormChangeMinMana()) ? "Usable after Form change" : - (playerReader.Form == item.FormEnum && usableAction.Is(item)) ? "Usable current Form" : "not Usable current Form"; + bool f() => playerReader.Form == form; + string s() => playerReader.Form.ToStringF(); return new Requirement { @@ -785,431 +855,360 @@ string s() => LogMessage = s }; } + } - private Requirement CreateActionNotInGameCooldown(KeyAction item, PlayerReader playerReader, Dictionary> intVariables) + private Requirement CreateRace(string requirement) + { + return create(requirement, playerReader); + static Requirement create(string requirement, PlayerReader playerReader) { - string key = $"CD_{item.Name}"; - bool f() => UsableGCD(key, playerReader, intVariables); - string s() => $"CD {intVariables[key]() / 1000f:F1}"; + // 'Race:_RACE_' + ReadOnlySpan span = requirement; + int sep = span.IndexOf(SEP1); + UnitRace race = Enum.Parse(span[(sep + 1)..]); + + bool f() => playerReader.Race == race; + string s() => playerReader.Race.ToStringF(); return new Requirement { HasRequirement = f, - VisibleIfHasRequirement = false, LogMessage = s }; } + } - private static bool UsableGCD(string key, PlayerReader playerReader, Dictionary> intVariables) + private Requirement CreateSpell(string requirement) + { + return create(requirement, spellBookReader); + static Requirement create(string requirement, SpellBookReader spellBookReader) { - return intVariables[key]() <= CastingHandler.SPELL_QUEUE - playerReader.NetworkLatency.Value; - } + // 'Spell:_NAME_OR_ID_' + ReadOnlySpan span = requirement; + int sep = span.IndexOf(SEP1); + string name = span[(sep + 1)..].Trim().ToString(); - private Requirement CreateTargetCastingSpell(string requirement) - { - return create(requirement, playerReader); - static Requirement create(string requirement, PlayerReader playerReader) + int id; + if (int.TryParse(name, out id) && + spellBookReader.TryGetValue(id, out Spell spell)) { - ReadOnlySpan span = requirement; - int sep1 = span.IndexOf(SEP1); - // 'TargetCastingSpell' - if (sep1 == -1) - { - return new Requirement - { - HasRequirement = playerReader.IsTargetCasting, - LogMessage = () => "Target casting" - }; - } - - // 'TargetCastingSpell:_1_?,_n_' - string[] spellsPart = span[(sep1 + 1)..].ToString().Split(SEP2); - int[] spellIds = spellsPart.Select(int.Parse).ToArray(); - - bool f() => spellIds.Contains(playerReader.SpellBeingCastByTarget); - string s() => $"Target casts {playerReader.SpellBeingCastByTarget} ∈ [{string.Join(SEP2, spellIds)}]"; - return new Requirement - { - HasRequirement = f, - LogMessage = s - }; + name = $"{spell.Name}({id})"; } - } - - private Requirement CreateForm(string requirement) - { - return create(requirement, playerReader); - static Requirement create(string requirement, PlayerReader playerReader) + else { - // 'Form:_FORM_' - ReadOnlySpan span = requirement; - int sep = span.IndexOf(SEP1); - Form form = Enum.Parse(span[(sep + 1)..]); + id = spellBookReader.GetId(name); + } - bool f() => playerReader.Form == form; - string s() => playerReader.Form.ToStringF(); + bool f() => spellBookReader.Has(id); + string s() => $"Spell {name}"; - return new Requirement - { - HasRequirement = f, - LogMessage = s - }; - } + return new Requirement + { + HasRequirement = f, + LogMessage = s + }; } + } - private Requirement CreateRace(string requirement) + private Requirement CreateTalent(string requirement) + { + return create(requirement, talentReader); + static Requirement create(string requirement, TalentReader talentReader) { - return create(requirement, playerReader); - static Requirement create(string requirement, PlayerReader playerReader) - { - // 'Race:_RACE_' - ReadOnlySpan span = requirement; - int sep = span.IndexOf(SEP1); - UnitRace race = Enum.Parse(span[(sep + 1)..]); + // 'Talent:_NAME_?:_RANK_' + ReadOnlySpan span = requirement; - bool f() => playerReader.Race == race; - string s() => playerReader.Race.ToStringF(); + int firstSep = span.IndexOf(SEP1); + int lastSep = span.LastIndexOf(SEP1); - return new Requirement - { - HasRequirement = f, - LogMessage = s - }; + int rank = 1; + if (firstSep != lastSep) + { + rank = int.Parse(span[(lastSep + 1)..]); } - } - - private Requirement CreateSpell(string requirement) - { - return create(requirement, spellBookReader); - static Requirement create(string requirement, SpellBookReader spellBookReader) + else { - // 'Spell:_NAME_OR_ID_' - ReadOnlySpan span = requirement; - int sep = span.IndexOf(SEP1); - string name = span[(sep + 1)..].Trim().ToString(); - - int id; - if (int.TryParse(name, out id) && - spellBookReader.TryGetValue(id, out Spell spell)) - { - name = $"{spell.Name}({id})"; - } - else - { - id = spellBookReader.GetId(name); - } + lastSep = span.Length; + } - bool f() => spellBookReader.Has(id); - string s() => $"Spell {name}"; + string name = span[(firstSep + 1)..lastSep].ToString(); - return new Requirement - { - HasRequirement = f, - LogMessage = s - }; - } - } + bool f() => talentReader.HasTalent(name, rank); + string s() => rank == 1 ? $"Talent {name}" : $"Talent {name} (Rank {rank})"; - private Requirement CreateTalent(string requirement) - { - return create(requirement, talentReader); - static Requirement create(string requirement, TalentReader talentReader) + return new Requirement { - // 'Talent:_NAME_?:_RANK_' - ReadOnlySpan span = requirement; + HasRequirement = f, + LogMessage = s + }; + } + } - int firstSep = span.IndexOf(SEP1); - int lastSep = span.LastIndexOf(SEP1); + private Requirement CreateTrigger(string requirement) + { + return create(requirement, playerReader); + static Requirement create(string requirement, PlayerReader playerReader) + { + // 'Trigger:_BIT_NUM_?:_TEXT_' + ReadOnlySpan span = requirement; + int firstSep = span.IndexOf(SEP1); + int lastSep = span.LastIndexOf(SEP1); - int rank = 1; - if (firstSep != lastSep) - { - rank = int.Parse(span[(lastSep + 1)..]); - } - else - { - lastSep = span.Length; - } + string text = string.Empty; + if (firstSep != lastSep) + { + text = span[(lastSep + 1)..].ToString(); + } + else + { + lastSep = span.Length; + } - string name = span[(firstSep + 1)..lastSep].ToString(); + int bitNum = int.Parse(span[(firstSep + 1)..lastSep]); + int bitMask = Mask.M[bitNum]; - bool f() => talentReader.HasTalent(name, rank); - string s() => rank == 1 ? $"Talent {name}" : $"Talent {name} (Rank {rank})"; + bool f() => playerReader.CustomTrigger1[bitMask]; + string s() => $"Trigger({bitNum}) {text}"; - return new Requirement - { - HasRequirement = f, - LogMessage = s - }; - } + return new Requirement + { + HasRequirement = f, + LogMessage = s + }; } + } - private Requirement CreateTrigger(string requirement) + private Requirement CreateNpcId(string requirement) + { + return create(requirement, playerReader, intVariables, creatureDb); + static Requirement create(string requirement, PlayerReader playerReader, + Dictionary> intVariables, CreatureDB creatureDb) { - return create(requirement, playerReader); - static Requirement create(string requirement, PlayerReader playerReader) - { - // 'Trigger:_BIT_NUM_?:_TEXT_' - ReadOnlySpan span = requirement; - int firstSep = span.IndexOf(SEP1); - int lastSep = span.LastIndexOf(SEP1); + // 'npcID:_INTVARIABLE_OR_ID_' + ReadOnlySpan span = requirement; + int sep = span.IndexOf(SEP1); + ReadOnlySpan name_or_id = span[(sep + 1)..]; - string text = string.Empty; - if (firstSep != lastSep) - { - text = span[(lastSep + 1)..].ToString(); - } - else - { - lastSep = span.Length; - } + int npcId; + if (intVariables.TryGetValue(name_or_id.ToString(), out Func? value)) + npcId = value(); + else + npcId = int.Parse(name_or_id); - int bitNum = int.Parse(span[(firstSep + 1)..lastSep]); - int bitMask = Mask.M[bitNum]; + string npcName = string.Empty; + if (creatureDb.Entries.TryGetValue(npcId, out Creature creature)) + { + npcName = creature.Name; + } - bool f() => playerReader.CustomTrigger1[bitMask]; - string s() => $"Trigger({bitNum}) {text}"; + bool f() => playerReader.TargetId == npcId; + string s() => $"TargetID {npcName}({npcId})"; - return new Requirement - { - HasRequirement = f, - LogMessage = s - }; - } + return new Requirement + { + HasRequirement = f, + LogMessage = s + }; } + } - private Requirement CreateNpcId(string requirement) + private Requirement CreateBagItem(string requirement) + { + return create(requirement, bagReader, intVariables, itemDb); + static Requirement create(string requirement, BagReader bagReader, + Dictionary> intVariables, ItemDB itemDb) { - return create(requirement, playerReader, intVariables, creatureDb); - static Requirement create(string requirement, PlayerReader playerReader, - Dictionary> intVariables, CreatureDB creatureDb) - { - // 'npcID:_INTVARIABLE_OR_ID_' - ReadOnlySpan span = requirement; - int sep = span.IndexOf(SEP1); - ReadOnlySpan name_or_id = span[(sep + 1)..]; - - int npcId; - if (intVariables.TryGetValue(name_or_id.ToString(), out Func? value)) - npcId = value(); - else - npcId = int.Parse(name_or_id); - - string npcName = string.Empty; - if (creatureDb.Entries.TryGetValue(npcId, out Creature creature)) - { - npcName = creature.Name; - } + // 'BagItem:_INTVARIABLE_OR_ID_?:_COUNT_' + ReadOnlySpan span = requirement; - bool f() => playerReader.TargetId == npcId; - string s() => $"TargetID {npcName}({npcId})"; + int firstSep = span.IndexOf(SEP1); + int lastSep = span.LastIndexOf(SEP1); - return new Requirement - { - HasRequirement = f, - LogMessage = s - }; + int count = 1; + if (firstSep != lastSep) + { + count = int.Parse(span[(lastSep + 1)..]); } - } - - private Requirement CreateBagItem(string requirement) - { - return create(requirement, bagReader, intVariables, itemDb); - static Requirement create(string requirement, BagReader bagReader, - Dictionary> intVariables, ItemDB itemDb) + else { - // 'BagItem:_INTVARIABLE_OR_ID_?:_COUNT_' - ReadOnlySpan span = requirement; - - int firstSep = span.IndexOf(SEP1); - int lastSep = span.LastIndexOf(SEP1); - - int count = 1; - if (firstSep != lastSep) - { - count = int.Parse(span[(lastSep + 1)..]); - } - else - { - lastSep = span.Length; - } + lastSep = span.Length; + } - ReadOnlySpan name_or_id = span[(firstSep + 1)..lastSep]; + ReadOnlySpan name_or_id = span[(firstSep + 1)..lastSep]; - int itemId; - if (intVariables.TryGetValue(name_or_id.ToString(), out Func? value)) - itemId = value(); - else - itemId = int.Parse(name_or_id); + int itemId; + if (intVariables.TryGetValue(name_or_id.ToString(), out Func? value)) + itemId = value(); + else + itemId = int.Parse(name_or_id); - string itemName = string.Empty; - if (itemDb.Items.TryGetValue(itemId, out Item item)) - { - itemName = item.Name; - } + string itemName = string.Empty; + if (itemDb.Items.TryGetValue(itemId, out Item item)) + { + itemName = item.Name; + } - bool f() => bagReader.ItemCount(itemId) >= count; - string s() => count == 1 ? $"in bag {itemName}({itemId})" : $"{itemName}({itemId}) count >= {count}"; + bool f() => bagReader.ItemCount(itemId) >= count; + string s() => count == 1 ? $"in bag {itemName}({itemId})" : $"{itemName}({itemId}) count >= {count}"; - return new Requirement - { - HasRequirement = f, - LogMessage = s - }; - } + return new Requirement + { + HasRequirement = f, + LogMessage = s + }; } + } - private Requirement CreateSpellInRange(string requirement) + private Requirement CreateSpellInRange(string requirement) + { + return create(requirement, playerReader.SpellInRange); + static Requirement create(string requirement, SpellInRange range) { - return create(requirement, playerReader.SpellInRange); - static Requirement create(string requirement, SpellInRange range) - { - // 'SpellInRange:_BIT_NUM_' - ReadOnlySpan span = requirement; - int sep = span.IndexOf(SEP1); - int bitNum = int.Parse(span[(sep + 1)..]); - int bitMask = Mask.M[bitNum]; + // 'SpellInRange:_BIT_NUM_' + ReadOnlySpan span = requirement; + int sep = span.IndexOf(SEP1); + int bitNum = int.Parse(span[(sep + 1)..]); + int bitMask = Mask.M[bitNum]; - bool f() => range[bitMask]; - string s() => $"SpellInRange {bitNum}"; + bool f() => range[bitMask]; + string s() => $"SpellInRange {bitNum}"; - return new Requirement - { - HasRequirement = f, - LogMessage = s - }; - } + return new Requirement + { + HasRequirement = f, + LogMessage = s + }; } + } - private Requirement CreateUsable(string requirement) - { - // 'Usable:_KeyAction_Name_' - ReadOnlySpan span = requirement; - int sep = span.IndexOf(SEP1); - ReadOnlySpan name = span[(sep + 1)..].Trim(); + private Requirement CreateUsable(string requirement) + { + // 'Usable:_KeyAction_Name_' + ReadOnlySpan span = requirement; + int sep = span.IndexOf(SEP1); + ReadOnlySpan name = span[(sep + 1)..].Trim(); - KeyAction? keyAction = null; - for (int i = 0; i < keyActions.Length; i++) + KeyAction? keyAction = null; + for (int i = 0; i < keyActions.Length; i++) + { + KeyAction test = keyActions[i]; + if (name.SequenceEqual(test.Name)) { - KeyAction test = keyActions[i]; - if (name.SequenceEqual(test.Name)) - { - keyAction = test; - break; - } + keyAction = test; + break; } + } - if (keyAction == null) - throw new InvalidOperationException($"'{requirement}' related named '{name}' {nameof(KeyAction)} not found!"); + if (keyAction == null) + throw new InvalidOperationException($"'{requirement}' related named '{name}' {nameof(KeyAction)} not found!"); - return CreateActionUsableRequirement(keyAction, playerReader, addonReader.UsableAction); - } + return CreateActionUsableRequirement(keyAction, playerReader, addonReader.UsableAction); + } - private Requirement CreateGreaterThen(string requirement) - { - return CreateArithmeticRequirement(">", requirement, intVariables); - } + private Requirement CreateGreaterThen(string requirement) + { + return CreateArithmeticRequirement(">", requirement, intVariables); + } + + private Requirement CreateLesserThen(string requirement) + { + return CreateArithmeticRequirement("<", requirement, intVariables); + } + + private Requirement CreateGreaterOrEquals(string requirement) + { + return CreateArithmeticRequirement(">=", requirement, intVariables); + } + + private Requirement CreateLesserOrEquals(string requirement) + { + return CreateArithmeticRequirement("<=", requirement, intVariables); + } + + private Requirement CreateEquals(string requirement) + { + return CreateArithmeticRequirement("==", requirement, intVariables); + } + + private Requirement CreateArithmeticRequirement(string symbol, string requirement, Dictionary> intVariables) + { + ReadOnlySpan span = requirement; + int sep = span.IndexOf(symbol); - private Requirement CreateLesserThen(string requirement) + string key = span[..sep].Trim().ToString(); + ReadOnlySpan variable_or_constValue = span[(sep + symbol.Length)..]; + + if (!intVariables.ContainsKey(key)) { - return CreateArithmeticRequirement("<", requirement, intVariables); + LogUnknownRequirement(logger, requirement, string.Join(", ", intVariables.Keys)); + throw new ArgumentOutOfRangeException(requirement); } - private Requirement CreateGreaterOrEquals(string requirement) + string display = key; + + Func alias = intVariables[key]; + string aliasKey = alias().ToString(); + if (intVariables.ContainsKey(aliasKey)) { - return CreateArithmeticRequirement(">=", requirement, intVariables); + key = aliasKey; } - private Requirement CreateLesserOrEquals(string requirement) + Func value; + if (int.TryParse(variable_or_constValue, out int constValue)) { - return CreateArithmeticRequirement("<=", requirement, intVariables); + int _constValue() => constValue; + value = _constValue; } - - private Requirement CreateEquals(string requirement) + else { - return CreateArithmeticRequirement("==", requirement, intVariables); + value = intVariables.TryGetValue(variable_or_constValue.Trim().ToString(), out Func? variable) + ? variable + : throw new ArgumentOutOfRangeException(requirement); } - private Requirement CreateArithmeticRequirement(string symbol, string requirement, Dictionary> intVariables) + string msg() => $"{display} {intVariables[key]()} {symbol} {value()}"; + switch (symbol) { - ReadOnlySpan span = requirement; - int sep = span.IndexOf(symbol); - - string key = span[..sep].Trim().ToString(); - ReadOnlySpan variable_or_constValue = span[(sep + symbol.Length)..]; - - if (!intVariables.ContainsKey(key)) - { - LogUnknownRequirement(logger, requirement, string.Join(", ", intVariables.Keys)); + case "==": + bool e() => intVariables[key]() == value(); + return new Requirement { HasRequirement = e, LogMessage = msg }; + case ">": + bool g() => intVariables[key]() > value(); + return new Requirement { HasRequirement = g, LogMessage = msg }; + case "<": + bool l() => intVariables[key]() < value(); + return new Requirement { HasRequirement = l, LogMessage = msg }; + case ">=": + bool ge() => intVariables[key]() >= value(); + return new Requirement { HasRequirement = ge, LogMessage = msg }; + case "<=": + bool le() => intVariables[key]() <= value(); + return new Requirement { HasRequirement = le, LogMessage = msg }; + default: throw new ArgumentOutOfRangeException(requirement); - } - - string display = key; - - Func alias = intVariables[key]; - string aliasKey = alias().ToString(); - if (intVariables.ContainsKey(aliasKey)) - { - key = aliasKey; - } - - Func value; - if (int.TryParse(variable_or_constValue, out int constValue)) - { - int _constValue() => constValue; - value = _constValue; - } - else - { - value = intVariables.TryGetValue(variable_or_constValue.Trim().ToString(), out Func? variable) - ? variable - : throw new ArgumentOutOfRangeException(requirement); - } - - string msg() => $"{display} {intVariables[key]()} {symbol} {value()}"; - switch (symbol) - { - case "==": - bool e() => intVariables[key]() == value(); - return new Requirement { HasRequirement = e, LogMessage = msg }; - case ">": - bool g() => intVariables[key]() > value(); - return new Requirement { HasRequirement = g, LogMessage = msg }; - case "<": - bool l() => intVariables[key]() < value(); - return new Requirement { HasRequirement = l, LogMessage = msg }; - case ">=": - bool ge() => intVariables[key]() >= value(); - return new Requirement { HasRequirement = ge, LogMessage = msg }; - case "<=": - bool le() => intVariables[key]() <= value(); - return new Requirement { HasRequirement = le, LogMessage = msg }; - default: - throw new ArgumentOutOfRangeException(requirement); - }; - } + }; + } - #region Logging + #region Logging - [LoggerMessage( - EventId = 11, - Level = LogLevel.Information, - Message = "[{typeName}] Defined int variable [{key} -> {value}]")] - static partial void LogUserDefinedValue(ILogger logger, string typeName, string key, int value); + [LoggerMessage( + EventId = 11, + Level = LogLevel.Information, + Message = "[{typeName}] Defined int variable [{key} -> {value}]")] + static partial void LogUserDefinedValue(ILogger logger, string typeName, string key, int value); - [LoggerMessage( - EventId = 12, - Level = LogLevel.Information, - Message = "[{name}] Requirement: \"{requirement}\"")] - static partial void LogProcessingRequirement(ILogger logger, string name, string requirement); + [LoggerMessage( + EventId = 12, + Level = LogLevel.Information, + Message = "[{name}] Requirement: \"{requirement}\"")] + static partial void LogProcessingRequirement(ILogger logger, string name, string requirement); - [LoggerMessage( - EventId = 13, - Level = LogLevel.Error, - Message = "UNKNOWN REQUIREMENT! {requirement}: try one of: {available}")] - static partial void LogUnknownRequirement(ILogger logger, string requirement, string available); + [LoggerMessage( + EventId = 13, + Level = LogLevel.Error, + Message = "UNKNOWN REQUIREMENT! {requirement}: try one of: {available}")] + static partial void LogUnknownRequirement(ILogger logger, string requirement, string available); - #endregion - } + #endregion } \ No newline at end of file diff --git a/Core/Session/ExperienceProvider.cs b/Core/Session/ExperienceProvider.cs index cff3f9140..011bf27dd 100644 --- a/Core/Session/ExperienceProvider.cs +++ b/Core/Session/ExperienceProvider.cs @@ -2,14 +2,13 @@ using static System.IO.File; using static Newtonsoft.Json.JsonConvert; -namespace Core.Session +namespace Core.Session; + +public static class ExperienceProvider { - public static class ExperienceProvider + public static int[] GetExperienceList(DataConfig dataConfig) { - public static int[] GetExperienceList(DataConfig dataConfig) - { - string json = ReadAllText(Join(dataConfig.ExpExperience, "exp.json")); - return DeserializeObject(json)!; - } + string json = ReadAllText(Join(dataConfig.ExpExperience, "exp.json")); + return DeserializeObject(json)!; } } diff --git a/Core/Session/GrindSession.cs b/Core/Session/GrindSession.cs index 410c18c8c..3e5e01b4a 100644 --- a/Core/Session/GrindSession.cs +++ b/Core/Session/GrindSession.cs @@ -1,67 +1,66 @@ using System; using Newtonsoft.Json; -namespace Core.Session +namespace Core.Session; + +public sealed class GrindSession { - public sealed class GrindSession - { - [JsonIgnore] - public int[] ExpList { get; set; } = Array.Empty(); + [JsonIgnore] + public int[] ExpList { get; set; } = Array.Empty(); - public Guid SessionId { get; set; } - public string PathName { get; set; } = string.Empty; - public UnitClass PlayerClass { get; set; } - public DateTime SessionStart { get; set; } - public DateTime SessionEnd { get; set; } - [JsonIgnore] - public int TotalTimeInMinutes => (int)(SessionEnd - SessionStart).TotalMinutes; - public int LevelFrom { get; set; } - public float XpFrom { get; set; } - public int LevelTo { get; set; } - public float XpTo { get; set; } - public int MobsKilled { get; set; } - public float MobsPerMinute => MathF.Round(MobsKilled / (float)TotalTimeInMinutes, 2); - public int Death { get; set; } - public string? Reason { get; set; } - [JsonIgnore] - public float ExperiencePerHour => TotalTimeInMinutes == 0 ? 0 : MathF.Round(ExpGetInBotSession / TotalTimeInMinutes * 60f, 0); - [JsonIgnore] - public float ExpGetInBotSession + public Guid SessionId { get; set; } + public string PathName { get; set; } = string.Empty; + public UnitClass PlayerClass { get; set; } + public DateTime SessionStart { get; set; } + public DateTime SessionEnd { get; set; } + [JsonIgnore] + public int TotalTimeInMinutes => (int)(SessionEnd - SessionStart).TotalMinutes; + public int LevelFrom { get; set; } + public float XpFrom { get; set; } + public int LevelTo { get; set; } + public float XpTo { get; set; } + public int MobsKilled { get; set; } + public float MobsPerMinute => MathF.Round(MobsKilled / (float)TotalTimeInMinutes, 2); + public int Death { get; set; } + public string? Reason { get; set; } + [JsonIgnore] + public float ExperiencePerHour => TotalTimeInMinutes == 0 ? 0 : MathF.Round(ExpGetInBotSession / TotalTimeInMinutes * 60f, 0); + [JsonIgnore] + public float ExpGetInBotSession + { + get { - get - { - if (ExpList.Length == 0) - return 0; - - int maxLevel = ExpList.Length + 1; - if (LevelFrom == maxLevel) - return 0; + if (ExpList.Length == 0) + return 0; - if (LevelFrom == maxLevel - 1 && LevelTo == maxLevel) - return ExpList[LevelFrom - 1] - XpFrom; + int maxLevel = ExpList.Length + 1; + if (LevelFrom == maxLevel) + return 0; - if (LevelTo == LevelFrom) - { - return XpTo - XpFrom; - } + if (LevelFrom == maxLevel - 1 && LevelTo == maxLevel) + return ExpList[LevelFrom - 1] - XpFrom; - if (LevelTo > LevelFrom) - { - float expSoFar = XpTo; + if (LevelTo == LevelFrom) + { + return XpTo - XpFrom; + } - for (int i = 0; i < LevelTo - LevelFrom; i++) - { - expSoFar += ExpList[LevelFrom - 1 + i] - XpFrom; - XpFrom = 0; - if (LevelTo > maxLevel) - break; - } + if (LevelTo > LevelFrom) + { + float expSoFar = XpTo; - return expSoFar; + for (int i = 0; i < LevelTo - LevelFrom; i++) + { + expSoFar += ExpList[LevelFrom - 1 + i] - XpFrom; + XpFrom = 0; + if (LevelTo > maxLevel) + break; } - return 0; + return expSoFar; } + + return 0; } } } diff --git a/Core/Session/GrindSessionHandler.cs b/Core/Session/GrindSessionHandler.cs index 2ba9de2e5..2487a0e5b 100644 --- a/Core/Session/GrindSessionHandler.cs +++ b/Core/Session/GrindSessionHandler.cs @@ -2,81 +2,80 @@ using System; using System.Threading; -namespace Core.Session +namespace Core.Session; + +public sealed class GrindSessionHandler : IGrindSessionHandler { - public sealed class GrindSessionHandler : IGrindSessionHandler - { - private readonly ILogger logger; - private readonly AddonReader addonReader; - private readonly IGrindSessionDAO grindSessionDAO; - private readonly CancellationToken ct; + private readonly ILogger logger; + private readonly AddonReader addonReader; + private readonly IGrindSessionDAO grindSessionDAO; + private readonly CancellationToken ct; - private readonly GrindSession session; - private readonly Thread thread; + private readonly GrindSession session; + private readonly Thread thread; - private bool active; + private bool active; - public GrindSessionHandler(ILogger logger, DataConfig dataConfig, AddonReader addonReader, IGrindSessionDAO grindSessionDAO, CancellationTokenSource cts) - { - this.logger = logger; - this.addonReader = addonReader; - this.grindSessionDAO = grindSessionDAO; - ct = cts.Token; - - session = new() - { - ExpList = ExperienceProvider.GetExperienceList(dataConfig) - }; - - thread = new Thread(PeriodicSave); - thread.Start(); - } + public GrindSessionHandler(ILogger logger, DataConfig dataConfig, AddonReader addonReader, IGrindSessionDAO grindSessionDAO, CancellationTokenSource cts) + { + this.logger = logger; + this.addonReader = addonReader; + this.grindSessionDAO = grindSessionDAO; + ct = cts.Token; - public void Start(string path) + session = new() { - active = true; - - session.SessionId = Guid.NewGuid(); - session.PathName = path; - session.PlayerClass = addonReader.PlayerReader.Class; - session.SessionStart = DateTime.Now; - session.LevelFrom = addonReader.PlayerReader.Level.Value; - session.XpFrom = addonReader.PlayerReader.PlayerXp.Value; - session.MobsKilled = addonReader.LevelTracker.MobsKilled; - } + ExpList = ExperienceProvider.GetExperienceList(dataConfig) + }; - public void Stop(string reason, bool active) - { - this.active = active; + thread = new Thread(PeriodicSave); + thread.Start(); + } - session.SessionEnd = DateTime.Now; - session.LevelTo = addonReader.PlayerReader.Level.Value; - session.XpTo = addonReader.PlayerReader.PlayerXp.Value; - session.Reason = reason; - session.Death = addonReader.LevelTracker.Death; - session.MobsKilled = addonReader.LevelTracker.MobsKilled; + public void Start(string path) + { + active = true; - if (session.MobsKilled > 0 && session.TotalTimeInMinutes > 0) - Save(); - } + session.SessionId = Guid.NewGuid(); + session.PathName = path; + session.PlayerClass = addonReader.PlayerReader.Class; + session.SessionStart = DateTime.Now; + session.LevelFrom = addonReader.PlayerReader.Level.Value; + session.XpFrom = addonReader.PlayerReader.PlayerXp.Value; + session.MobsKilled = addonReader.LevelTracker.MobsKilled; + } - private void Save() - { - grindSessionDAO.Save(session); - } + public void Stop(string reason, bool active) + { + this.active = active; - private void PeriodicSave() - { - while (!ct.IsCancellationRequested) - { - if (active) - Stop("auto save", true); + session.SessionEnd = DateTime.Now; + session.LevelTo = addonReader.PlayerReader.Level.Value; + session.XpTo = addonReader.PlayerReader.PlayerXp.Value; + session.Reason = reason; + session.Death = addonReader.LevelTracker.Death; + session.MobsKilled = addonReader.LevelTracker.MobsKilled; - ct.WaitHandle.WaitOne(TimeSpan.FromMinutes(1)); - } + if (session.MobsKilled > 0 && session.TotalTimeInMinutes > 0) + Save(); + } - if (logger.IsEnabled(LogLevel.Debug)) - logger.LogDebug("SessionHandler thread stopped!"); + private void Save() + { + grindSessionDAO.Save(session); + } + + private void PeriodicSave() + { + while (!ct.IsCancellationRequested) + { + if (active) + Stop("auto save", true); + + ct.WaitHandle.WaitOne(TimeSpan.FromMinutes(1)); } + + if (logger.IsEnabled(LogLevel.Debug)) + logger.LogDebug("SessionHandler thread stopped!"); } } diff --git a/Core/Session/IGrindSessionDAO.cs b/Core/Session/IGrindSessionDAO.cs index a463ab8a1..be5325849 100644 --- a/Core/Session/IGrindSessionDAO.cs +++ b/Core/Session/IGrindSessionDAO.cs @@ -1,10 +1,9 @@ using System.Collections.Generic; -namespace Core.Session +namespace Core.Session; + +public interface IGrindSessionDAO { - public interface IGrindSessionDAO - { - IEnumerable Load(); - void Save(GrindSession session); - } + IEnumerable Load(); + void Save(GrindSession session); } diff --git a/Core/Session/IGrindSessionHandler.cs b/Core/Session/IGrindSessionHandler.cs index c2f1b5300..eeaffbca7 100644 --- a/Core/Session/IGrindSessionHandler.cs +++ b/Core/Session/IGrindSessionHandler.cs @@ -1,8 +1,7 @@ -namespace Core.Session +namespace Core.Session; + +public interface IGrindSessionHandler { - public interface IGrindSessionHandler - { - void Start(string path); - void Stop(string reason, bool active); - } + void Start(string path); + void Stop(string reason, bool active); } \ No newline at end of file diff --git a/Core/Session/LocalGrindSessionDAO.cs b/Core/Session/LocalGrindSessionDAO.cs index 863fef690..fab8fc4a8 100644 --- a/Core/Session/LocalGrindSessionDAO.cs +++ b/Core/Session/LocalGrindSessionDAO.cs @@ -4,46 +4,45 @@ using static Newtonsoft.Json.JsonConvert; -namespace Core.Session +namespace Core.Session; + +// this is gonna save the bot session data locally atm +// there will be an AWS session handler later to upload the session data to AWS S3 +// the idea is we will have two session data handlers working at the same time +public sealed class LocalGrindSessionDAO : IGrindSessionDAO { - // this is gonna save the bot session data locally atm - // there will be an AWS session handler later to upload the session data to AWS S3 - // the idea is we will have two session data handlers working at the same time - public sealed class LocalGrindSessionDAO : IGrindSessionDAO + private readonly DataConfig dataConfig; + + public LocalGrindSessionDAO(DataConfig dataConfig) { - private readonly DataConfig dataConfig; + this.dataConfig = dataConfig; - public LocalGrindSessionDAO(DataConfig dataConfig) - { - this.dataConfig = dataConfig; + if (!Directory.Exists(dataConfig.ExpHistory)) + Directory.CreateDirectory(dataConfig.ExpHistory); + } - if (!Directory.Exists(dataConfig.ExpHistory)) - Directory.CreateDirectory(dataConfig.ExpHistory); - } + public IEnumerable Load() + { + var sessions = Directory.EnumerateFiles(dataConfig.ExpHistory, "*.json") + .Select(file => DeserializeObject(File.ReadAllText(file))!) + .OrderByDescending(grindingSession => grindingSession.SessionStart) + .ToList(); - public IEnumerable Load() + if (sessions.Any()) { - var sessions = Directory.EnumerateFiles(dataConfig.ExpHistory, "*.json") - .Select(file => DeserializeObject(File.ReadAllText(file))!) - .OrderByDescending(grindingSession => grindingSession.SessionStart) - .ToList(); - - if (sessions.Any()) + int[] expList = ExperienceProvider.GetExperienceList(dataConfig); + foreach (var s in sessions) { - int[] expList = ExperienceProvider.GetExperienceList(dataConfig); - foreach (var s in sessions) - { - s.ExpList = expList; - } + s.ExpList = expList; } - - return sessions; } - public void Save(GrindSession session) - { - string json = SerializeObject(session); - File.WriteAllText(Path.Join(dataConfig.ExpHistory, $"{session.SessionId}.json"), json); - } + return sessions; + } + + public void Save(GrindSession session) + { + string json = SerializeObject(session); + File.WriteAllText(Path.Join(dataConfig.ExpHistory, $"{session.SessionId}.json"), json); } } \ No newline at end of file diff --git a/Core/StartupConfig/StartupClientVersion.cs b/Core/StartupConfig/StartupClientVersion.cs index ddf1cb122..6ffd9128c 100644 --- a/Core/StartupConfig/StartupClientVersion.cs +++ b/Core/StartupConfig/StartupClientVersion.cs @@ -1,35 +1,34 @@ using Game; -namespace Core +namespace Core; + +public sealed class StartupClientVersion { - public sealed class StartupClientVersion - { - public ClientVersion Version { get; } + public ClientVersion Version { get; } - public string Path { get; } + public string Path { get; } - public StartupClientVersion(WowProcess process) + public StartupClientVersion(WowProcess process) + { + switch (process.FileVersion.Major) { - switch (process.FileVersion.Major) - { - case 1: - Version = ClientVersion.SoM; - Path = "som"; - break; - case 2: - Version = ClientVersion.TBC; - Path = "tbc"; - break; - case 3: - Version = ClientVersion.Wrath; - Path = "wrath"; - break; - default: - Version = ClientVersion.Retail; - Path = "retail"; - break; - } + case 1: + Version = ClientVersion.SoM; + Path = "som"; + break; + case 2: + Version = ClientVersion.TBC; + Path = "tbc"; + break; + case 3: + Version = ClientVersion.Wrath; + Path = "wrath"; + break; + default: + Version = ClientVersion.Retail; + Path = "retail"; + break; } - } + } diff --git a/Core/StartupConfig/StartupConfigPathing.cs b/Core/StartupConfig/StartupConfigPathing.cs index e855e8f06..78ec4a550 100644 --- a/Core/StartupConfig/StartupConfigPathing.cs +++ b/Core/StartupConfig/StartupConfigPathing.cs @@ -1,37 +1,36 @@ -namespace Core +namespace Core; + +public sealed class StartupConfigPathing { - public sealed class StartupConfigPathing - { - public const string Position = "Pathing"; + public const string Position = "Pathing"; - public enum Types - { - Local, - RemoteV1, - RemoteV3 - } + public enum Types + { + Local, + RemoteV1, + RemoteV3 + } - public StartupConfigPathing() { } + public StartupConfigPathing() { } - public StartupConfigPathing(string Mode, string hostv1, int portv1, string hostv3, int portv3) - { - this.Mode = Mode; + public StartupConfigPathing(string Mode, string hostv1, int portv1, string hostv3, int portv3) + { + this.Mode = Mode; - this.hostv1 = hostv1; - this.portv1 = portv1; + this.hostv1 = hostv1; + this.portv1 = portv1; - this.hostv3 = hostv3; - this.portv3 = portv3; - } + this.hostv3 = hostv3; + this.portv3 = portv3; + } - public Types Type => System.Enum.TryParse(Mode, out Types m) ? m : Types.Local; + public Types Type => System.Enum.TryParse(Mode, out Types m) ? m : Types.Local; - public string Mode { get; set; } = string.Empty; + public string Mode { get; set; } = string.Empty; - public string hostv1 { get; set; } = string.Empty; - public int portv1 { get; set; } + public string hostv1 { get; set; } = string.Empty; + public int portv1 { get; set; } - public string hostv3 { get; set; } = string.Empty; - public int portv3 { get; set; } - } + public string hostv3 { get; set; } = string.Empty; + public int portv3 { get; set; } } diff --git a/Core/StartupConfig/StartupConfigPid.cs b/Core/StartupConfig/StartupConfigPid.cs index ac027b6ed..d24997d5e 100644 --- a/Core/StartupConfig/StartupConfigPid.cs +++ b/Core/StartupConfig/StartupConfigPid.cs @@ -1,11 +1,10 @@ -namespace Core +namespace Core; + +public sealed class StartupConfigPid { - public sealed class StartupConfigPid - { - public const string Position = "Process"; + public const string Position = "Process"; - public StartupConfigPid() { } + public StartupConfigPid() { } - public int Id { get; set; } - } + public int Id { get; set; } } diff --git a/Core/StartupConfig/StartupConfigReader.cs b/Core/StartupConfig/StartupConfigReader.cs index 4d5ea9b85..1fd0a794b 100644 --- a/Core/StartupConfig/StartupConfigReader.cs +++ b/Core/StartupConfig/StartupConfigReader.cs @@ -1,13 +1,12 @@ -namespace Core +namespace Core; + +public sealed class StartupConfigReader { - public sealed class StartupConfigReader - { - public const string Position = "Reader"; + public const string Position = "Reader"; - public StartupConfigReader() { } + public StartupConfigReader() { } - public string Type { get; set; } = string.Empty; + public string Type { get; set; } = string.Empty; - public AddonDataProviderType ReaderType => System.Enum.TryParse(Type, out AddonDataProviderType m) ? m : AddonDataProviderType.GDI; - } + public AddonDataProviderType ReaderType => System.Enum.TryParse(Type, out AddonDataProviderType m) ? m : AddonDataProviderType.GDI; } diff --git a/Core/Talents/Talent.cs b/Core/Talents/Talent.cs index 3fb23c994..821c9e32d 100644 --- a/Core/Talents/Talent.cs +++ b/Core/Talents/Talent.cs @@ -1,17 +1,16 @@ -namespace Core.Talents +namespace Core.Talents; + +public struct Talent { - public struct Talent - { - public int Hash { get; init; } - public int TabNum { get; init; } - public int TierNum { get; init; } - public int ColumnNum { get; init; } - public int CurrentRank { get; init; } - public string Name { get; set; } + public int Hash { get; init; } + public int TabNum { get; init; } + public int TierNum { get; init; } + public int ColumnNum { get; init; } + public int CurrentRank { get; init; } + public string Name { get; set; } - public override string ToString() - { - return $"{TabNum} - {TierNum} - {ColumnNum} - {CurrentRank} - {Name}"; - } + public override string ToString() + { + return $"{TabNum} - {TierNum} - {ColumnNum} - {CurrentRank} - {Name}"; } } diff --git a/Core/Talents/TalentReader.cs b/Core/Talents/TalentReader.cs index a93b4cd06..89b81f4a1 100644 --- a/Core/Talents/TalentReader.cs +++ b/Core/Talents/TalentReader.cs @@ -2,75 +2,74 @@ using Core.Talents; using Core.Database; -namespace Core -{ - public sealed class TalentReader - { - private readonly int cTalent; +namespace Core; - private readonly PlayerReader playerReader; - private readonly TalentDB talentDB; - public int Count { get; private set; } +public sealed class TalentReader +{ + private readonly int cTalent; - public Dictionary Talents { get; } = new(); - public Dictionary Spells { get; } = new(); + private readonly PlayerReader playerReader; + private readonly TalentDB talentDB; + public int Count { get; private set; } - public TalentReader(int cTalent, PlayerReader playerReader, TalentDB talentDB) - { - this.cTalent = cTalent; + public Dictionary Talents { get; } = new(); + public Dictionary Spells { get; } = new(); - this.playerReader = playerReader; - this.talentDB = talentDB; - } + public TalentReader(int cTalent, PlayerReader playerReader, TalentDB talentDB) + { + this.cTalent = cTalent; - public void Read(IAddonDataProvider reader) - { - int hash = reader.GetInt(cTalent); - if (hash == 0 || Talents.ContainsKey(hash)) return; + this.playerReader = playerReader; + this.talentDB = talentDB; + } - // 1-3 + 1-11 + 1-4 + 1-5 - // tab * 1000000 + tier * 10000 + column * 10 + currentRank - int tab = hash / 1000000; - int tier = hash / 10000 % 100; - int column = hash / 10 % 10; - int rank = hash % 10; + public void Read(IAddonDataProvider reader) + { + int hash = reader.GetInt(cTalent); + if (hash == 0 || Talents.ContainsKey(hash)) return; - Talent talent = new() - { - Hash = hash, - TabNum = tab, - TierNum = tier, - ColumnNum = column, - CurrentRank = rank - }; + // 1-3 + 1-11 + 1-4 + 1-5 + // tab * 1000000 + tier * 10000 + column * 10 + currentRank + int tab = hash / 1000000; + int tier = hash / 10000 % 100; + int column = hash / 10 % 10; + int rank = hash % 10; - if (talentDB.Update(ref talent, playerReader.Class, out int id)) - { - Talents.Add(hash, talent); - Spells.Add(hash, id); - Count += talent.CurrentRank; - } - } + Talent talent = new() + { + Hash = hash, + TabNum = tab, + TierNum = tier, + ColumnNum = column, + CurrentRank = rank + }; - public void Reset() + if (talentDB.Update(ref talent, playerReader.Class, out int id)) { - Count = 0; - Talents.Clear(); - Spells.Clear(); + Talents.Add(hash, talent); + Spells.Add(hash, id); + Count += talent.CurrentRank; } + } + + public void Reset() + { + Count = 0; + Talents.Clear(); + Spells.Clear(); + } - public bool HasTalent(string name, int rank) + public bool HasTalent(string name, int rank) + { + foreach ((int _, Talent t) in Talents) { - foreach ((int _, Talent t) in Talents) + if (t.CurrentRank >= rank && + t.Name.Contains(name, System.StringComparison.OrdinalIgnoreCase)) { - if (t.CurrentRank >= rank && - t.Name.Contains(name, System.StringComparison.OrdinalIgnoreCase)) - { - return true; - } + return true; } - - return false; } + + return false; } } diff --git a/Core/WowheadAPI/WApi.cs b/Core/WowheadAPI/WApi.cs index 46c25238d..45b71045c 100644 --- a/Core/WowheadAPI/WApi.cs +++ b/Core/WowheadAPI/WApi.cs @@ -1,59 +1,58 @@ using System.Threading.Tasks; using System.Xml; -namespace Core +namespace Core; + +public static class WApi { - public static class WApi + public static ClientVersion Version { - public static ClientVersion Version + set { - set + switch (value) { - switch (value) - { - case ClientVersion.SoM: - BaseUrl = "https://classic.wowhead.com"; - break; - case ClientVersion.TBC: - BaseUrl = "https://tbc.wowhead.com"; - break; - case ClientVersion.Wrath: - BaseUrl = "https://www.wowhead.com/wotlk"; - break; - default: - case ClientVersion.Retail: - BaseUrl = "https://www.wowhead.com"; - break; - } + case ClientVersion.SoM: + BaseUrl = "https://classic.wowhead.com"; + break; + case ClientVersion.TBC: + BaseUrl = "https://tbc.wowhead.com"; + break; + case ClientVersion.Wrath: + BaseUrl = "https://www.wowhead.com/wotlk"; + break; + default: + case ClientVersion.Retail: + BaseUrl = "https://www.wowhead.com"; + break; } } + } - public static string BaseUrl { get; set; } = "https://www.wowhead.com"; + public static string BaseUrl { get; set; } = "https://www.wowhead.com"; - public static string NpcId => $"{BaseUrl}/npc="; - public static string ItemId => $"{BaseUrl}/item="; - public static string SpellId => $"{BaseUrl}/spell="; + public static string NpcId => $"{BaseUrl}/npc="; + public static string ItemId => $"{BaseUrl}/item="; + public static string SpellId => $"{BaseUrl}/spell="; - public const string TinyIconUrl = "https://wow.zamimg.com/images/wow/icons/tiny/{0}.gif"; - public const string MedIconUrl = "https://wow.zamimg.com/images/wow/icons/medium/{0}.jpg"; + public const string TinyIconUrl = "https://wow.zamimg.com/images/wow/icons/tiny/{0}.gif"; + public const string MedIconUrl = "https://wow.zamimg.com/images/wow/icons/medium/{0}.jpg"; - public static async Task FetchItemIconName(int itemId) + public static async Task FetchItemIconName(int itemId) + { + try { - try + using XmlReader reader = XmlReader.Create($"{ItemId}{itemId}&xml", new XmlReaderSettings { Async = true, LineNumberOffset = 14 }); + while (await reader.ReadAsync()) { - using XmlReader reader = XmlReader.Create($"{ItemId}{itemId}&xml", new XmlReaderSettings { Async = true, LineNumberOffset = 14 }); - while (await reader.ReadAsync()) + if (reader.NodeType == XmlNodeType.Element && reader.Name.Contains("icon")) { - if (reader.NodeType == XmlNodeType.Element && reader.Name.Contains("icon")) - { - await reader.ReadAsync(); - return reader.Value; - } + await reader.ReadAsync(); + return reader.Value; } } - catch { } - - return string.Empty; } + catch { } + + return string.Empty; } } \ No newline at end of file diff --git a/CoreTests/Input/Test_Input.cs b/CoreTests/Input/Test_Input.cs index b1418a128..36c278ada 100644 --- a/CoreTests/Input/Test_Input.cs +++ b/CoreTests/Input/Test_Input.cs @@ -4,92 +4,91 @@ using Microsoft.Extensions.Logging; using System.Threading; -namespace CoreTests +namespace CoreTests; + +public class Test_Input : IDisposable { - public class Test_Input : IDisposable - { - private const int delay = 500; + private const int delay = 500; - private readonly CancellationTokenSource cts; + private readonly CancellationTokenSource cts; - private readonly ILogger logger; - private readonly WowProcess wowProcess; - private readonly WowScreen wowScreen; - private readonly WowProcessInput wowProcessInput; + private readonly ILogger logger; + private readonly WowProcess wowProcess; + private readonly WowScreen wowScreen; + private readonly WowProcessInput wowProcessInput; - public Test_Input(ILogger logger) - { - this.logger = logger; - this.cts = new(); + public Test_Input(ILogger logger) + { + this.logger = logger; + this.cts = new(); - wowProcess = new WowProcess(); - wowScreen = new WowScreen(logger, wowProcess); - wowProcessInput = new WowProcessInput(logger, cts, wowProcess); - } + wowProcess = new WowProcess(); + wowScreen = new WowScreen(logger, wowProcess); + wowProcessInput = new WowProcessInput(logger, cts, wowProcess); + } - public void Dispose() - { - wowScreen.Dispose(); - wowProcess.Dispose(); - cts.Dispose(); - } + public void Dispose() + { + wowScreen.Dispose(); + wowProcess.Dispose(); + cts.Dispose(); + } - public void Mouse_Movement() - { - wowProcessInput.SetForegroundWindow(); + public void Mouse_Movement() + { + wowProcessInput.SetForegroundWindow(); - wowProcessInput.SetCursorPosition(new Point(25, 25)); - cts.Token.WaitHandle.WaitOne(delay); + wowProcessInput.SetCursorPosition(new Point(25, 25)); + cts.Token.WaitHandle.WaitOne(delay); - wowProcessInput.SetCursorPosition(new Point(50, 50)); - cts.Token.WaitHandle.WaitOne(delay); + wowProcessInput.SetCursorPosition(new Point(50, 50)); + cts.Token.WaitHandle.WaitOne(delay); - logger.LogInformation($"{nameof(Mouse_Movement)} Finished"); - } + logger.LogInformation($"{nameof(Mouse_Movement)} Finished"); + } - public void Mouse_Clicks() - { - wowProcessInput.SetForegroundWindow(); + public void Mouse_Clicks() + { + wowProcessInput.SetForegroundWindow(); - Point p = new(120, 120); - wowProcessInput.LeftClickMouse(p); + Point p = new(120, 120); + wowProcessInput.LeftClickMouse(p); - cts.Token.WaitHandle.WaitOne(delay); + cts.Token.WaitHandle.WaitOne(delay); - wowProcessInput.RightClickMouse(p); + wowProcessInput.RightClickMouse(p); - cts.Token.WaitHandle.WaitOne(delay); + cts.Token.WaitHandle.WaitOne(delay); - wowProcessInput.RightClickMouse(p); + wowProcessInput.RightClickMouse(p); - wowScreen.GetRectangle(out Rectangle rect); - p = new Point(rect.Width / 2, rect.Height / 2); + wowScreen.GetRectangle(out Rectangle rect); + p = new Point(rect.Width / 2, rect.Height / 2); - cts.Token.WaitHandle.WaitOne(delay); + cts.Token.WaitHandle.WaitOne(delay); - wowProcessInput.RightClickMouse(p); + wowProcessInput.RightClickMouse(p); - cts.Token.WaitHandle.WaitOne(delay); + cts.Token.WaitHandle.WaitOne(delay); - wowProcessInput.RightClickMouse(p); + wowProcessInput.RightClickMouse(p); - logger.LogInformation($"{nameof(Mouse_Clicks)} Finished"); - } + logger.LogInformation($"{nameof(Mouse_Clicks)} Finished"); + } - public void Clipboard() - { - wowProcessInput.SetClipboard("/help"); + public void Clipboard() + { + wowProcessInput.SetClipboard("/help"); - // Open chat inputbox - wowProcessInput.KeyPress(ConsoleKey.Enter, delay); + // Open chat inputbox + wowProcessInput.KeyPress(ConsoleKey.Enter, delay); - wowProcessInput.PasteFromClipboard(); - cts.Token.WaitHandle.WaitOne(delay); + wowProcessInput.PasteFromClipboard(); + cts.Token.WaitHandle.WaitOne(delay); - // Close chat inputbox - wowProcessInput.KeyPress(ConsoleKey.Enter, delay); + // Close chat inputbox + wowProcessInput.KeyPress(ConsoleKey.Enter, delay); - logger.LogInformation($"{nameof(Clipboard)} Finished"); - } + logger.LogInformation($"{nameof(Clipboard)} Finished"); } } diff --git a/CoreTests/MinimapNodeFinder/Test_MinimapNodeFinder.cs b/CoreTests/MinimapNodeFinder/Test_MinimapNodeFinder.cs index bdd37dbff..19a89708e 100644 --- a/CoreTests/MinimapNodeFinder/Test_MinimapNodeFinder.cs +++ b/CoreTests/MinimapNodeFinder/Test_MinimapNodeFinder.cs @@ -13,65 +13,64 @@ #nullable enable -namespace CoreTests +namespace CoreTests; + +internal sealed class Test_MinimapNodeFinder : IDisposable { - internal sealed class Test_MinimapNodeFinder : IDisposable - { - private const bool saveImage = false; - private const bool LogEachUpdate = false; + private const bool saveImage = false; + private const bool LogEachUpdate = false; - private readonly ILogger logger; + private readonly ILogger logger; - private readonly WowProcess wowProcess; - private readonly WowScreen wowScreen; + private readonly WowProcess wowProcess; + private readonly WowScreen wowScreen; - private readonly MinimapNodeFinder minimapNodeFinder; + private readonly MinimapNodeFinder minimapNodeFinder; - private readonly Stopwatch stopwatch = new(); + private readonly Stopwatch stopwatch = new(); - public Test_MinimapNodeFinder(ILogger logger) - { - this.logger = logger; + public Test_MinimapNodeFinder(ILogger logger) + { + this.logger = logger; - wowProcess = new(); - wowScreen = new(logger, wowProcess); + wowProcess = new(); + wowScreen = new(logger, wowProcess); - minimapNodeFinder = new(logger, wowScreen); - } - - public void Dispose() - { - wowScreen.Dispose(); - wowProcess.Dispose(); - } + minimapNodeFinder = new(logger, wowScreen); + } - public void Execute() - { - if (LogEachUpdate) - stopwatch.Restart(); + public void Dispose() + { + wowScreen.Dispose(); + wowProcess.Dispose(); + } - wowScreen.UpdateMinimapBitmap(); + public void Execute() + { + if (LogEachUpdate) + stopwatch.Restart(); - if (LogEachUpdate) - logger.LogInformation($"Capture: {stopwatch.ElapsedMilliseconds}ms"); + wowScreen.UpdateMinimapBitmap(); - if (LogEachUpdate) - stopwatch.Restart(); + if (LogEachUpdate) + logger.LogInformation($"Capture: {stopwatch.ElapsedMilliseconds}ms"); - minimapNodeFinder.Update(); + if (LogEachUpdate) + stopwatch.Restart(); - if (LogEachUpdate) - logger.LogInformation($"Update: {stopwatch.ElapsedMilliseconds}ms"); + minimapNodeFinder.Update(); - if (saveImage) - { - SaveImage(); - } - } + if (LogEachUpdate) + logger.LogInformation($"Update: {stopwatch.ElapsedMilliseconds}ms"); - private void SaveImage() + if (saveImage) { - wowScreen.MiniMapBitmap.Save("minimap.png"); + SaveImage(); } } + + private void SaveImage() + { + wowScreen.MiniMapBitmap.Save("minimap.png"); + } } diff --git a/CoreTests/NpcNameFinder/MockMouseOverReader.cs b/CoreTests/NpcNameFinder/MockMouseOverReader.cs index 12fc2c9c6..7b975cc26 100644 --- a/CoreTests/NpcNameFinder/MockMouseOverReader.cs +++ b/CoreTests/NpcNameFinder/MockMouseOverReader.cs @@ -1,15 +1,14 @@ using Core; -namespace CoreTests +namespace CoreTests; + +public class MockMouseOverReader : IMouseOverReader { - public class MockMouseOverReader : IMouseOverReader - { - public int MouseOverLevel => throw new System.NotImplementedException(); + public int MouseOverLevel => throw new System.NotImplementedException(); - public UnitClassification MouseOverClassification => throw new System.NotImplementedException(); + public UnitClassification MouseOverClassification => throw new System.NotImplementedException(); - public int MouseOverId => throw new System.NotImplementedException(); + public int MouseOverId => throw new System.NotImplementedException(); - public int MouseOverGuid => throw new System.NotImplementedException(); - } + public int MouseOverGuid => throw new System.NotImplementedException(); } diff --git a/CoreTests/NpcNameFinder/MockWoWProcess.cs b/CoreTests/NpcNameFinder/MockWoWProcess.cs index 50f80fd28..8dabcf4cd 100644 --- a/CoreTests/NpcNameFinder/MockWoWProcess.cs +++ b/CoreTests/NpcNameFinder/MockWoWProcess.cs @@ -3,28 +3,27 @@ using Game; -namespace CoreTests +namespace CoreTests; + +public class MockWoWProcess : IMouseInput { - public class MockWoWProcess : IMouseInput + public void RightClickMouse(Point p) { - public void RightClickMouse(Point p) - { - throw new System.NotImplementedException(); - } + throw new System.NotImplementedException(); + } - public void LeftClickMouse(Point p) - { - throw new System.NotImplementedException(); - } + public void LeftClickMouse(Point p) + { + throw new System.NotImplementedException(); + } - public void SetCursorPosition(Point p) - { - throw new System.NotImplementedException(); - } + public void SetCursorPosition(Point p) + { + throw new System.NotImplementedException(); + } - public void InteractMouseOver(CancellationToken ct) - { - throw new System.NotImplementedException(); - } + public void InteractMouseOver(CancellationToken ct) + { + throw new System.NotImplementedException(); } } diff --git a/CoreTests/NpcNameFinder/MockWoWScreen.cs b/CoreTests/NpcNameFinder/MockWoWScreen.cs index 29b9ac455..10c581dde 100644 --- a/CoreTests/NpcNameFinder/MockWoWScreen.cs +++ b/CoreTests/NpcNameFinder/MockWoWScreen.cs @@ -2,30 +2,29 @@ using System; using System.Drawing; -namespace CoreTests +namespace CoreTests; + +public class MockWoWScreen : IWowScreen { - public class MockWoWScreen : IWowScreen - { - public bool Enabled { get => throw new NotImplementedException(); set => throw new NotImplementedException(); } + public bool Enabled { get => throw new NotImplementedException(); set => throw new NotImplementedException(); } - public Bitmap GetBitmap(int width, int height) - { - throw new NotImplementedException(); - } + public Bitmap GetBitmap(int width, int height) + { + throw new NotImplementedException(); + } - public Color GetColorAt(Point point) - { - throw new NotImplementedException(); - } + public Color GetColorAt(Point point) + { + throw new NotImplementedException(); + } - public void GetPosition(ref Point point) - { - throw new NotImplementedException(); - } + public void GetPosition(ref Point point) + { + throw new NotImplementedException(); + } - public void GetRectangle(out Rectangle rect) - { - throw new NotImplementedException(); - } + public void GetRectangle(out Rectangle rect) + { + throw new NotImplementedException(); } } diff --git a/CoreTests/NpcNameFinder/NpcNameOverlay.cs b/CoreTests/NpcNameFinder/NpcNameOverlay.cs index 755e4f0d1..c5b95e0c9 100644 --- a/CoreTests/NpcNameFinder/NpcNameOverlay.cs +++ b/CoreTests/NpcNameFinder/NpcNameOverlay.cs @@ -9,150 +9,149 @@ using SharedLib.Extensions; using SharedLib.NpcFinder; -namespace CoreTests +namespace CoreTests; + +public class NpcNameOverlay : IDisposable { - public class NpcNameOverlay : IDisposable - { - private readonly GraphicsWindow window; - private readonly Graphics graphics; + private readonly GraphicsWindow window; + private readonly Graphics graphics; - private Font font; - private SolidBrush brush; - private SolidBrush brushBackground; + private Font font; + private SolidBrush brush; + private SolidBrush brushBackground; - private readonly NpcNameFinder npcNameFinder; - private readonly NpcNameTargeting npcNameTargeting; + private readonly NpcNameFinder npcNameFinder; + private readonly NpcNameTargeting npcNameTargeting; - private readonly bool debugTargeting; - private readonly bool debugSkinning; + private readonly bool debugTargeting; + private readonly bool debugSkinning; - public NpcNameOverlay(IntPtr handle, NpcNameFinder npcNameFinder, - NpcNameTargeting npcNameTargeting, bool debugTargeting, bool debugSkinning) + public NpcNameOverlay(IntPtr handle, NpcNameFinder npcNameFinder, + NpcNameTargeting npcNameTargeting, bool debugTargeting, bool debugSkinning) + { + this.npcNameFinder = npcNameFinder; + this.npcNameTargeting = npcNameTargeting; + this.debugTargeting = debugTargeting; + this.debugSkinning = debugSkinning; + + graphics = new Graphics() + { + MeasureFPS = true, + PerPrimitiveAntiAliasing = true, + TextAntiAliasing = true, + UseMultiThreadedFactories = false, + VSync = false, + WindowHandle = IntPtr.Zero + }; + + window = new StickyWindow(handle, graphics) { - this.npcNameFinder = npcNameFinder; - this.npcNameTargeting = npcNameTargeting; - this.debugTargeting = debugTargeting; - this.debugSkinning = debugSkinning; + FPS = 60, + AttachToClientArea = true, + BypassTopmost = true, + }; + window.SetupGraphics += SetupGraphics; + window.DestroyGraphics += DestroyGraphics; + window.DrawGraphics += DrawGraphics; + + window.Create(); + } - graphics = new Graphics() - { - MeasureFPS = true, - PerPrimitiveAntiAliasing = true, - TextAntiAliasing = true, - UseMultiThreadedFactories = false, - VSync = false, - WindowHandle = IntPtr.Zero - }; - - window = new StickyWindow(handle, graphics) - { - FPS = 60, - AttachToClientArea = true, - BypassTopmost = true, - }; - window.SetupGraphics += SetupGraphics; - window.DestroyGraphics += DestroyGraphics; - window.DrawGraphics += DrawGraphics; - - window.Create(); - } + ~NpcNameOverlay() + { + Dispose(false); + } - ~NpcNameOverlay() - { - Dispose(false); - } + #region IDisposable Support - #region IDisposable Support + private bool disposed; - private bool disposed; + protected virtual void Dispose(bool disposing) + { + if (disposed) return; - protected virtual void Dispose(bool disposing) - { - if (disposed) return; + window.Dispose(); + disposed = true; + } - window.Dispose(); - disposed = true; - } + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } - public void Dispose() - { - Dispose(true); - GC.SuppressFinalize(this); - } + #endregion - #endregion + private void SetupGraphics(object sender, SetupGraphicsEventArgs e) + { + Graphics graphics = e.Graphics; + + font = graphics.CreateFont("Arial", 16); + brushBackground = graphics.CreateSolidBrush(0, 0x27, 0x31, 0); + + if (npcNameFinder.nameType.HasFlagF(NpcNames.Enemy)) + brush = graphics.CreateSolidBrush( + NpcNameFinder.sE_R,NpcNameFinder.sE_G, NpcNameFinder.sE_B); + else if (npcNameFinder.nameType.HasFlagF(NpcNames.Friendly)) + brush = graphics.CreateSolidBrush( + NpcNameFinder.sF_R, NpcNameFinder.sF_G, NpcNameFinder.sF_B); + else if (npcNameFinder.nameType.HasFlagF(NpcNames.Neutral)) + brush = graphics.CreateSolidBrush( + NpcNameFinder.sN_R, NpcNameFinder.sN_G, NpcNameFinder.sN_B); + else if (npcNameFinder.nameType.HasFlagF(NpcNames.Corpse)) + brush = graphics.CreateSolidBrush( + NpcNameFinder.fC_RGB, NpcNameFinder.fC_RGB, NpcNameFinder.fC_RGB); + } - private void SetupGraphics(object sender, SetupGraphicsEventArgs e) - { - Graphics graphics = e.Graphics; - - font = graphics.CreateFont("Arial", 16); - brushBackground = graphics.CreateSolidBrush(0, 0x27, 0x31, 0); - - if (npcNameFinder.nameType.HasFlagF(NpcNames.Enemy)) - brush = graphics.CreateSolidBrush( - NpcNameFinder.sE_R,NpcNameFinder.sE_G, NpcNameFinder.sE_B); - else if (npcNameFinder.nameType.HasFlagF(NpcNames.Friendly)) - brush = graphics.CreateSolidBrush( - NpcNameFinder.sF_R, NpcNameFinder.sF_G, NpcNameFinder.sF_B); - else if (npcNameFinder.nameType.HasFlagF(NpcNames.Neutral)) - brush = graphics.CreateSolidBrush( - NpcNameFinder.sN_R, NpcNameFinder.sN_G, NpcNameFinder.sN_B); - else if (npcNameFinder.nameType.HasFlagF(NpcNames.Corpse)) - brush = graphics.CreateSolidBrush( - NpcNameFinder.fC_RGB, NpcNameFinder.fC_RGB, NpcNameFinder.fC_RGB); - } + private void DestroyGraphics(object sender, DestroyGraphicsEventArgs e) + { + brush.Dispose(); + brushBackground.Dispose(); + font.Dispose(); + } - private void DestroyGraphics(object sender, DestroyGraphicsEventArgs e) - { - brush.Dispose(); - brushBackground.Dispose(); - font.Dispose(); - } + private void DrawGraphics(object sender, DrawGraphicsEventArgs e) + { + Graphics g = e.Graphics; - private void DrawGraphics(object sender, DrawGraphicsEventArgs e) - { - Graphics g = e.Graphics; + g.ClearScene(brushBackground); - g.ClearScene(brushBackground); + if (npcNameFinder.NpcCount <= 0) + return; - if (npcNameFinder.NpcCount <= 0) - return; + g.DrawRectangle(brush, npcNameFinder.Area.Left, npcNameFinder.Area.Top, npcNameFinder.Area.Right, npcNameFinder.Area.Bottom, 1); - g.DrawRectangle(brush, npcNameFinder.Area.Left, npcNameFinder.Area.Top, npcNameFinder.Area.Right, npcNameFinder.Area.Bottom, 1); + int j = 0; + foreach (NpcPosition npc in npcNameFinder.Npcs) + { + g.DrawRectangle(brush, npc.Rect.Left, npc.Rect.Top, npc.Rect.Right, npc.Rect.Bottom, 1); + g.DrawText(font, 10, brush, npc.Rect.Left - 20f, npc.Rect.Top, j.ToString()); + j++; - int j = 0; - foreach (NpcPosition npc in npcNameFinder.Npcs) + if (debugTargeting) { - g.DrawRectangle(brush, npc.Rect.Left, npc.Rect.Top, npc.Rect.Right, npc.Rect.Bottom, 1); - g.DrawText(font, 10, brush, npc.Rect.Left - 20f, npc.Rect.Top, j.ToString()); - j++; + foreach (var l in npcNameTargeting.locTargeting) + { + g.DrawCircle(brush, l.X + npc.ClickPoint.X, l.Y + npc.ClickPoint.Y, 5, 1); + } + } - if (debugTargeting) + if (debugSkinning) + { + int c = npcNameTargeting.locFindBy.Length; + int ex = 3; + System.Drawing.Point[] attemptPoints = new System.Drawing.Point[c + (c * ex)]; + for (int i = 0; i < c; i += ex) { - foreach (var l in npcNameTargeting.locTargeting) - { - g.DrawCircle(brush, l.X + npc.ClickPoint.X, l.Y + npc.ClickPoint.Y, 5, 1); - } + System.Drawing.Point p = npcNameTargeting.locFindBy[i]; + attemptPoints[i] = p; + attemptPoints[i + c] = new System.Drawing.Point(npc.Rect.Width / 2, p.Y).Scale(npcNameFinder.ScaleToRefWidth, npcNameFinder.ScaleToRefHeight); + attemptPoints[i + c + 1] = new System.Drawing.Point(-npc.Rect.Width / 2, p.Y).Scale(npcNameFinder.ScaleToRefWidth, npcNameFinder.ScaleToRefHeight); } - if (debugSkinning) + foreach (var l in attemptPoints) { - int c = npcNameTargeting.locFindBy.Length; - int ex = 3; - System.Drawing.Point[] attemptPoints = new System.Drawing.Point[c + (c * ex)]; - for (int i = 0; i < c; i += ex) - { - System.Drawing.Point p = npcNameTargeting.locFindBy[i]; - attemptPoints[i] = p; - attemptPoints[i + c] = new System.Drawing.Point(npc.Rect.Width / 2, p.Y).Scale(npcNameFinder.ScaleToRefWidth, npcNameFinder.ScaleToRefHeight); - attemptPoints[i + c + 1] = new System.Drawing.Point(-npc.Rect.Width / 2, p.Y).Scale(npcNameFinder.ScaleToRefWidth, npcNameFinder.ScaleToRefHeight); - } - - foreach (var l in attemptPoints) - { - g.DrawCircle(brush, l.X + npc.ClickPoint.X, l.Y + npc.ClickPoint.Y, 5, 1); - } + g.DrawCircle(brush, l.X + npc.ClickPoint.X, l.Y + npc.ClickPoint.Y, 5, 1); } } } diff --git a/CoreTests/NpcNameFinder/RectProvider.cs b/CoreTests/NpcNameFinder/RectProvider.cs index c9dc807c8..d5bbcfae0 100644 --- a/CoreTests/NpcNameFinder/RectProvider.cs +++ b/CoreTests/NpcNameFinder/RectProvider.cs @@ -1,25 +1,24 @@ using SharedLib; using System.Drawing; -namespace CoreTests +namespace CoreTests; + +public class RectProvider : IRectProvider { - public class RectProvider : IRectProvider + public RectProvider() { - public RectProvider() - { - } + } - public void GetRectangle(out Rectangle rect) - { - rect = new Rectangle(0, 0, 1920, 1080); - //rect = new Rectangle(0, 0, 3840, 2160); - //rect = new Rectangle(0, 0, 2560, 1440); - //WowScreen.GetRectangle(out rect); - } + public void GetRectangle(out Rectangle rect) + { + rect = new Rectangle(0, 0, 1920, 1080); + //rect = new Rectangle(0, 0, 3840, 2160); + //rect = new Rectangle(0, 0, 2560, 1440); + //WowScreen.GetRectangle(out rect); + } - public void GetPosition(ref Point point) - { - point = new(); - } + public void GetPosition(ref Point point) + { + point = new(); } } diff --git a/CoreTests/NpcNameFinder/Test_NpcNameFinder.cs b/CoreTests/NpcNameFinder/Test_NpcNameFinder.cs index 13390177c..af665d4e5 100644 --- a/CoreTests/NpcNameFinder/Test_NpcNameFinder.cs +++ b/CoreTests/NpcNameFinder/Test_NpcNameFinder.cs @@ -14,166 +14,165 @@ #pragma warning disable 0162 #nullable enable -namespace CoreTests -{ - public sealed class Test_NpcNameFinder : IDisposable - { - private const bool saveImage = true; - private const bool LogEachUpdate = true; - private const bool LogShowResult = false; - - private const bool debugTargeting = false; - private const bool debugSkinning = false; +namespace CoreTests; - private readonly ILogger logger; - private readonly NpcNameFinder npcNameFinder; - private readonly NpcNameTargeting npcNameTargeting; +public sealed class Test_NpcNameFinder : IDisposable +{ + private const bool saveImage = true; + private const bool LogEachUpdate = true; + private const bool LogShowResult = false; - private readonly WowProcess wowProcess; - private readonly WowScreen wowScreen; + private const bool debugTargeting = false; + private const bool debugSkinning = false; - private readonly Stopwatch stopwatch = new(); - private readonly StringBuilder stringBuilder = new(); + private readonly ILogger logger; + private readonly NpcNameFinder npcNameFinder; + private readonly NpcNameTargeting npcNameTargeting; - private readonly Graphics paint; - private readonly Bitmap paintBitmap; - private readonly Font font = new("Arial", 10); - private readonly SolidBrush brush = new(Color.White); - private readonly Pen whitePen = new(Color.White, 1); + private readonly WowProcess wowProcess; + private readonly WowScreen wowScreen; - private readonly NpcNameOverlay? npcNameOverlay; + private readonly Stopwatch stopwatch = new(); + private readonly StringBuilder stringBuilder = new(); - public Test_NpcNameFinder(ILogger logger, NpcNames types, bool useOverlay) - { - this.logger = logger; + private readonly Graphics paint; + private readonly Bitmap paintBitmap; + private readonly Font font = new("Arial", 10); + private readonly SolidBrush brush = new(Color.White); + private readonly Pen whitePen = new(Color.White, 1); - wowProcess = new(); - wowScreen = new(logger, wowProcess); - WowProcessInput wowProcessInput = new(logger, new(), wowProcess); + private readonly NpcNameOverlay? npcNameOverlay; - npcNameFinder = new(logger, wowScreen, new AutoResetEvent(false)); + public Test_NpcNameFinder(ILogger logger, NpcNames types, bool useOverlay) + { + this.logger = logger; - MockMouseOverReader mouseOverReader = new(); - npcNameTargeting = new(logger, new(), wowScreen, npcNameFinder, wowProcessInput, mouseOverReader, new NoBlacklist(), null!); + wowProcess = new(); + wowScreen = new(logger, wowProcess); + WowProcessInput wowProcessInput = new(logger, new(), wowProcess); - npcNameFinder.ChangeNpcType(types); + npcNameFinder = new(logger, wowScreen, new AutoResetEvent(false)); - if (saveImage) - { - paintBitmap = wowScreen.Bitmap; - paint = Graphics.FromImage(paintBitmap); - } + MockMouseOverReader mouseOverReader = new(); + npcNameTargeting = new(logger, new(), wowScreen, npcNameFinder, wowProcessInput, mouseOverReader, new NoBlacklist(), null!); - if (useOverlay) - npcNameOverlay = new(wowProcess.Process.MainWindowHandle, npcNameFinder, npcNameTargeting, debugTargeting, debugSkinning); - } + npcNameFinder.ChangeNpcType(types); - ~Test_NpcNameFinder() + if (saveImage) { - Dispose(); + paintBitmap = wowScreen.Bitmap; + paint = Graphics.FromImage(paintBitmap); } - public void Dispose() - { - npcNameOverlay?.Dispose(); + if (useOverlay) + npcNameOverlay = new(wowProcess.Process.MainWindowHandle, npcNameFinder, npcNameTargeting, debugTargeting, debugSkinning); + } - wowScreen.Dispose(); - wowProcess.Dispose(); - } + ~Test_NpcNameFinder() + { + Dispose(); + } - public void Execute() - { - if (LogEachUpdate) - stopwatch.Restart(); + public void Dispose() + { + npcNameOverlay?.Dispose(); - wowScreen.Update(); + wowScreen.Dispose(); + wowProcess.Dispose(); + } - if (LogEachUpdate) - logger.LogInformation($"Capture: {stopwatch.ElapsedMilliseconds}ms"); + public void Execute() + { + if (LogEachUpdate) + stopwatch.Restart(); - if (LogEachUpdate) - stopwatch.Restart(); + wowScreen.Update(); - npcNameFinder.Update(); + if (LogEachUpdate) + logger.LogInformation($"Capture: {stopwatch.ElapsedMilliseconds}ms"); - if (LogEachUpdate) - logger.LogInformation($"Update: {stopwatch.ElapsedMilliseconds}ms"); + if (LogEachUpdate) + stopwatch.Restart(); - if (saveImage) - { - SaveImage(); - } + npcNameFinder.Update(); - if (LogEachUpdate && LogShowResult) - { - stringBuilder.Length = 0; + if (LogEachUpdate) + logger.LogInformation($"Update: {stopwatch.ElapsedMilliseconds}ms"); + + if (saveImage) + { + SaveImage(); + } - if (npcNameFinder.Npcs.Any()) - stringBuilder.AppendLine(); + if (LogEachUpdate && LogShowResult) + { + stringBuilder.Length = 0; - int i = 0; - foreach (NpcPosition n in npcNameFinder.Npcs) - { - stringBuilder.Append($"{i,2}"); - stringBuilder.Append(" -> rect="); - stringBuilder.Append(n.Rect); - stringBuilder.Append(" ClickPoint="); - stringBuilder.AppendLine($"{{{n.ClickPoint.X,4},{n.ClickPoint.Y,4}}}"); - i++; - } + if (npcNameFinder.Npcs.Any()) + stringBuilder.AppendLine(); - logger.LogInformation(stringBuilder.ToString()); + int i = 0; + foreach (NpcPosition n in npcNameFinder.Npcs) + { + stringBuilder.Append($"{i,2}"); + stringBuilder.Append(" -> rect="); + stringBuilder.Append(n.Rect); + stringBuilder.Append(" ClickPoint="); + stringBuilder.AppendLine($"{{{n.ClickPoint.X,4},{n.ClickPoint.Y,4}}}"); + i++; } - } - public bool Execute_FindTargetBy(CursorType cursorType) - { - return npcNameTargeting.FindBy(cursorType); + logger.LogInformation(stringBuilder.ToString()); } + } + + public bool Execute_FindTargetBy(CursorType cursorType) + { + return npcNameTargeting.FindBy(cursorType); + } - private void SaveImage() + private void SaveImage() + { + if (npcNameFinder.Npcs.Any()) { - if (npcNameFinder.Npcs.Any()) - { - paint.DrawRectangle(whitePen, npcNameFinder.Area); + paint.DrawRectangle(whitePen, npcNameFinder.Area); - int j = 0; - foreach (var npc in npcNameFinder.Npcs) + int j = 0; + foreach (var npc in npcNameFinder.Npcs) + { + if (debugTargeting) { - if (debugTargeting) + foreach (var l in npcNameTargeting.locTargeting) { - foreach (var l in npcNameTargeting.locTargeting) - { - paint.DrawEllipse(whitePen, l.X + npc.ClickPoint.X, l.Y + npc.ClickPoint.Y, 5, 5); - } + paint.DrawEllipse(whitePen, l.X + npc.ClickPoint.X, l.Y + npc.ClickPoint.Y, 5, 5); } + } - if (debugSkinning) + if (debugSkinning) + { + int c = npcNameTargeting.locFindBy.Length; + int e = 3; + Point[] attemptPoints = new Point[c + (c * e)]; + for (int i = 0; i < c; i += e) { - int c = npcNameTargeting.locFindBy.Length; - int e = 3; - Point[] attemptPoints = new Point[c + (c * e)]; - for (int i = 0; i < c; i += e) - { - Point p = npcNameTargeting.locFindBy[i]; - attemptPoints[i] = p; - attemptPoints[i + c] = new Point(npc.Rect.Width / 2, p.Y).Scale(npcNameFinder.ScaleToRefWidth, npcNameFinder.ScaleToRefHeight); - attemptPoints[i + c + 1] = new Point(-npc.Rect.Width / 2, p.Y).Scale(npcNameFinder.ScaleToRefWidth, npcNameFinder.ScaleToRefHeight); - } - - foreach (var l in attemptPoints) - { - paint.DrawEllipse(whitePen, l.X + npc.ClickPoint.X, l.Y + npc.ClickPoint.Y, 5, 5); - } + Point p = npcNameTargeting.locFindBy[i]; + attemptPoints[i] = p; + attemptPoints[i + c] = new Point(npc.Rect.Width / 2, p.Y).Scale(npcNameFinder.ScaleToRefWidth, npcNameFinder.ScaleToRefHeight); + attemptPoints[i + c + 1] = new Point(-npc.Rect.Width / 2, p.Y).Scale(npcNameFinder.ScaleToRefWidth, npcNameFinder.ScaleToRefHeight); } - paint.DrawRectangle(whitePen, npc.Rect); - paint.DrawString(j.ToString(), font, brush, new PointF(npc.Rect.Left - 20f, npc.Rect.Top)); - j++; + foreach (var l in attemptPoints) + { + paint.DrawEllipse(whitePen, l.X + npc.ClickPoint.X, l.Y + npc.ClickPoint.Y, 5, 5); + } } - } - paintBitmap.Save("target_names.png"); + paint.DrawRectangle(whitePen, npc.Rect); + paint.DrawString(j.ToString(), font, brush, new PointF(npc.Rect.Left - 20f, npc.Rect.Top)); + j++; + } } + + paintBitmap.Save("target_names.png"); } } diff --git a/CoreTests/Program.cs b/CoreTests/Program.cs index ced7cbd89..2c62527cf 100644 --- a/CoreTests/Program.cs +++ b/CoreTests/Program.cs @@ -8,146 +8,145 @@ #pragma warning disable 0162 -namespace CoreTests -{ - sealed class Program - { - private static Microsoft.Extensions.Logging.ILogger logger; - - private const bool LogOverall = false; - private const bool ShowOverlay = false; - private const int delay = 150; +namespace CoreTests; - public static void Main() - { - var logConfig = new LoggerConfiguration() - .WriteTo.File("names.log") - .WriteTo.Debug() - .CreateLogger(); - - Log.Logger = logConfig; - logger = new SerilogLoggerProvider(Log.Logger).CreateLogger(nameof(Program)); - - Test_NPCNameFinder(); - //Test_Input(); - //Test_CursorGrabber(); - //Test_MinimapNodeFinder(); - //Test_FindTargetByCursor(); - } +sealed class Program +{ + private static Microsoft.Extensions.Logging.ILogger logger; - private static void Test_NPCNameFinder() - { - //NpcNames types = NpcNames.Enemy; - //NpcNames types = NpcNames.Corpse; - NpcNames types = NpcNames.Enemy | NpcNames.Neutral; - //NpcNames types = NpcNames.Friendly | NpcNames.Neutral; + private const bool LogOverall = false; + private const bool ShowOverlay = false; + private const int delay = 150; - using Test_NpcNameFinder test = new(logger, types, ShowOverlay); - int count = 100; - int i = 0; + public static void Main() + { + var logConfig = new LoggerConfiguration() + .WriteTo.File("names.log") + .WriteTo.Debug() + .CreateLogger(); + + Log.Logger = logConfig; + logger = new SerilogLoggerProvider(Log.Logger).CreateLogger(nameof(Program)); + + Test_NPCNameFinder(); + //Test_Input(); + //Test_CursorGrabber(); + //Test_MinimapNodeFinder(); + //Test_FindTargetByCursor(); + } - long timestamp = Stopwatch.GetTimestamp(); - double[] sample = new double[count]; + private static void Test_NPCNameFinder() + { + //NpcNames types = NpcNames.Enemy; + //NpcNames types = NpcNames.Corpse; + NpcNames types = NpcNames.Enemy | NpcNames.Neutral; + //NpcNames types = NpcNames.Friendly | NpcNames.Neutral; - Log.Logger.Information($"running {count} samples..."); + using Test_NpcNameFinder test = new(logger, types, ShowOverlay); + int count = 100; + int i = 0; - while (i < count) - { - if (LogOverall) - timestamp = Stopwatch.GetTimestamp(); + long timestamp = Stopwatch.GetTimestamp(); + double[] sample = new double[count]; - test.Execute(); + Log.Logger.Information($"running {count} samples..."); - if (LogOverall) - sample[i] = Stopwatch.GetElapsedTime(timestamp).TotalMilliseconds; + while (i < count) + { + if (LogOverall) + timestamp = Stopwatch.GetTimestamp(); - i++; - Thread.Sleep(delay); - } + test.Execute(); if (LogOverall) - Log.Logger.Information($"sample: {count} | avg: {sample.Average(),0:0.00} | min: {sample.Min(),0:0.00} | max: {sample.Max(),0:0.00} | total: {sample.Sum()}"); - } + sample[i] = Stopwatch.GetElapsedTime(timestamp).TotalMilliseconds; - private static void Test_Input() - { - Test_Input test = new(logger); - test.Mouse_Movement(); - test.Mouse_Clicks(); - test.Clipboard(); + i++; + Thread.Sleep(delay); } - private static void Test_CursorGrabber() - { - using CursorClassifier classifier = new(); - int i = 5; - while (i > 0) - { - Thread.Sleep(1000); - - classifier.Classify(out CursorType cursorType); - Log.Logger.Information($"{cursorType.ToStringF()}"); + if (LogOverall) + Log.Logger.Information($"sample: {count} | avg: {sample.Average(),0:0.00} | min: {sample.Min(),0:0.00} | max: {sample.Max(),0:0.00} | total: {sample.Sum()}"); + } - i--; - } - } + private static void Test_Input() + { + Test_Input test = new(logger); + test.Mouse_Movement(); + test.Mouse_Clicks(); + test.Clipboard(); + } - private static void Test_MinimapNodeFinder() + private static void Test_CursorGrabber() + { + using CursorClassifier classifier = new(); + int i = 5; + while (i > 0) { - using Test_MinimapNodeFinder test = new(logger); + Thread.Sleep(1000); - int count = 100; - int i = 0; + classifier.Classify(out CursorType cursorType); + Log.Logger.Information($"{cursorType.ToStringF()}"); - long timestamp = Stopwatch.GetTimestamp(); - double[] sample = new double[count]; + i--; + } + } - Log.Logger.Information($"running {count} samples..."); + private static void Test_MinimapNodeFinder() + { + using Test_MinimapNodeFinder test = new(logger); - while (i < count) - { - if (LogOverall) - timestamp = Stopwatch.GetTimestamp(); + int count = 100; + int i = 0; - test.Execute(); + long timestamp = Stopwatch.GetTimestamp(); + double[] sample = new double[count]; - if (LogOverall) - sample[i] = Stopwatch.GetElapsedTime(timestamp).TotalMilliseconds; + Log.Logger.Information($"running {count} samples..."); - i++; - Thread.Sleep(delay); - } + while (i < count) + { + if (LogOverall) + timestamp = Stopwatch.GetTimestamp(); + + test.Execute(); if (LogOverall) - Log.Logger.Information($"sample: {count} | avg: {sample.Average(),0:0.00} | min: {sample.Min(),0:0.00} | max: {sample.Max(),0:0.00} | total: {sample.Sum()}"); + sample[i] = Stopwatch.GetElapsedTime(timestamp).TotalMilliseconds; + + i++; + Thread.Sleep(delay); } - private static void Test_FindTargetByCursor() - { - //CursorType cursorType = CursorType.Kill; - CursorType cursorType = CursorType.Vendor; + if (LogOverall) + Log.Logger.Information($"sample: {count} | avg: {sample.Average(),0:0.00} | min: {sample.Min(),0:0.00} | max: {sample.Max(),0:0.00} | total: {sample.Sum()}"); + } + + private static void Test_FindTargetByCursor() + { + //CursorType cursorType = CursorType.Kill; + CursorType cursorType = CursorType.Vendor; - //NpcNames types = NpcNames.Enemy; - //NpcNames types = NpcNames.Corpse; - //NpcNames types = NpcNames.Enemy | NpcNames.Neutral; - NpcNames types = NpcNames.Friendly | NpcNames.Neutral; + //NpcNames types = NpcNames.Enemy; + //NpcNames types = NpcNames.Corpse; + //NpcNames types = NpcNames.Enemy | NpcNames.Neutral; + NpcNames types = NpcNames.Friendly | NpcNames.Neutral; - using Test_NpcNameFinder test = new(logger, types, ShowOverlay); + using Test_NpcNameFinder test = new(logger, types, ShowOverlay); - int count = 2; - int i = 0; + int count = 2; + int i = 0; - while (i < count) + while (i < count) + { + test.Execute(); + if (test.Execute_FindTargetBy(cursorType)) { - test.Execute(); - if (test.Execute_FindTargetBy(cursorType)) - { - break; - } - - i++; - Thread.Sleep(delay); + break; } + + i++; + Thread.Sleep(delay); } } } diff --git a/Frontend/Helper/Currency.cs b/Frontend/Helper/Currency.cs index 01aaa9541..c24f8bd54 100644 --- a/Frontend/Helper/Currency.cs +++ b/Frontend/Helper/Currency.cs @@ -1,34 +1,33 @@ -namespace Frontend +namespace Frontend; + +public readonly struct Currency { - public readonly struct Currency - { - public static readonly Currency Empty = new(0); + public static readonly Currency Empty = new(0); - public int Gold { get; } - public int Silver { get; } - public int Copper { get; } + public int Gold { get; } + public int Silver { get; } + public int Copper { get; } - public Currency(int amount) - { - int sign = amount < 0 ? -1 : 1; - - Gold = 0; - Silver = 0; - Copper = Math.Abs(amount); + public Currency(int amount) + { + int sign = amount < 0 ? -1 : 1; - if (Copper >= 10000) - { - Gold = Copper / 10000 * sign; - Copper %= 10000; - } + Gold = 0; + Silver = 0; + Copper = Math.Abs(amount); - if (Copper >= 100) - { - Silver = Copper / 100 * sign; - Copper %= 100; - } + if (Copper >= 10000) + { + Gold = Copper / 10000 * sign; + Copper %= 10000; + } - Copper *= sign; + if (Copper >= 100) + { + Silver = Copper / 100 * sign; + Copper %= 100; } + + Copper *= sign; } } diff --git a/Frontend/Helper/ItemPrice.cs b/Frontend/Helper/ItemPrice.cs index 5d88928b0..0bf5a1dc2 100644 --- a/Frontend/Helper/ItemPrice.cs +++ b/Frontend/Helper/ItemPrice.cs @@ -1,13 +1,12 @@ -namespace Frontend +namespace Frontend; + +public static class ItemPrice { - public static class ItemPrice + public static Currency ToSellPrice(int sellPrice) { - public static Currency ToSellPrice(int sellPrice) - { - if (sellPrice == 0) - return Currency.Empty; + if (sellPrice == 0) + return Currency.Empty; - return new Currency(sellPrice); - } + return new Currency(sellPrice); } } diff --git a/Frontend/Pages/GoapGoalView.razor b/Frontend/Pages/GoapGoalView.razor index 09a069316..d308353c5 100644 --- a/Frontend/Pages/GoapGoalView.razor +++ b/Frontend/Pages/GoapGoalView.razor @@ -9,7 +9,7 @@ @goal.Cost: @goal.DisplayName - @foreach((GoapKey key, bool value) in this.goal.Preconditions) + @foreach ((GoapKey key, bool value) in this.goal.Preconditions) { @(key.ToStringF(value)) } diff --git a/Frontend/Pages/RawPlayerReaderComponent.razor b/Frontend/Pages/RawPlayerReaderComponent.razor index f88bcd2d0..1d6aa6569 100644 --- a/Frontend/Pages/RawPlayerReaderComponent.razor +++ b/Frontend/Pages/RawPlayerReaderComponent.razor @@ -170,7 +170,7 @@ foreach (var minfo in obj.GetType().GetMethods(BindingFlags.DeclaredOnly | BindingFlags.Public | BindingFlags.Instance)) { - if(minfo.ReturnType != t) + if (minfo.ReturnType != t) continue; ParameterInfo[] parameters = minfo.GetParameters(); diff --git a/Frontend/Pages/Swag.razor b/Frontend/Pages/Swag.razor index 6f1a1ccc8..77d5b4575 100644 --- a/Frontend/Pages/Swag.razor +++ b/Frontend/Pages/Swag.razor @@ -64,7 +64,7 @@ - + diff --git a/Game/Input/IInput.cs b/Game/Input/IInput.cs index 9c9f67a36..fd2431458 100644 --- a/Game/Input/IInput.cs +++ b/Game/Input/IInput.cs @@ -1,28 +1,27 @@ using System.Drawing; using System.Threading; -namespace Game +namespace Game; + +public interface IInput { - public interface IInput - { - void KeyDown(int key); + void KeyDown(int key); - void KeyUp(int key); + void KeyUp(int key); - int KeyPress(int key, int milliseconds); + int KeyPress(int key, int milliseconds); - void KeyPressSleep(int key, int milliseconds, CancellationToken ct); + void KeyPressSleep(int key, int milliseconds, CancellationToken ct); - void SetCursorPosition(Point p); + void SetCursorPosition(Point p); - void RightClickMouse(Point p); + void RightClickMouse(Point p); - void LeftClickMouse(Point p); + void LeftClickMouse(Point p); - void SendText(string text); + void SendText(string text); - void SetClipboard(string text); + void SetClipboard(string text); - void PasteFromClipboard(); - } + void PasteFromClipboard(); } diff --git a/Game/Input/IMouseInput.cs b/Game/Input/IMouseInput.cs index 1750ac6fa..ca87a4d60 100644 --- a/Game/Input/IMouseInput.cs +++ b/Game/Input/IMouseInput.cs @@ -1,16 +1,15 @@ using System.Drawing; using System.Threading; -namespace Game +namespace Game; + +public interface IMouseInput { - public interface IMouseInput - { - void SetCursorPosition(Point p); + void SetCursorPosition(Point p); - void RightClickMouse(Point p); + void RightClickMouse(Point p); - void LeftClickMouse(Point p); + void LeftClickMouse(Point p); - void InteractMouseOver(CancellationToken ct); - } + void InteractMouseOver(CancellationToken ct); } diff --git a/Game/Input/InputSimulator.cs b/Game/Input/InputSimulator.cs index 385c4e1ea..731a76512 100644 --- a/Game/Input/InputSimulator.cs +++ b/Game/Input/InputSimulator.cs @@ -5,111 +5,110 @@ using static WinAPI.NativeMethods; using TextCopy; -namespace Game +namespace Game; + +public sealed class InputSimulator : IInput { - public sealed class InputSimulator : IInput + private readonly int MIN_DELAY; + private readonly int MAX_DELAY; + + private readonly GregsStack.InputSimulatorStandard.InputSimulator simulator; + private readonly WowProcess wowProcess; + + private readonly CancellationToken _ct; + + public InputSimulator(WowProcess wowProcess, CancellationTokenSource cts, int minDelay, int maxDelay) + { + this.wowProcess = wowProcess; + _ct = cts.Token; + + MIN_DELAY = minDelay; + MAX_DELAY = maxDelay; + + simulator = new GregsStack.InputSimulatorStandard.InputSimulator(); + } + + private int Delay(int milliseconds) + { + int delay = milliseconds + Random.Shared.Next(1, MAX_DELAY); + _ct.WaitHandle.WaitOne(delay); + return delay; + } + + public void KeyDown(int key) + { + if (GetForegroundWindow() != wowProcess.Process.MainWindowHandle) + SetForegroundWindow(wowProcess.Process.MainWindowHandle); + + simulator.Keyboard.KeyDown((VirtualKeyCode)key); + } + + public void KeyUp(int key) { - private readonly int MIN_DELAY; - private readonly int MAX_DELAY; - - private readonly GregsStack.InputSimulatorStandard.InputSimulator simulator; - private readonly WowProcess wowProcess; - - private readonly CancellationToken _ct; - - public InputSimulator(WowProcess wowProcess, CancellationTokenSource cts, int minDelay, int maxDelay) - { - this.wowProcess = wowProcess; - _ct = cts.Token; - - MIN_DELAY = minDelay; - MAX_DELAY = maxDelay; - - simulator = new GregsStack.InputSimulatorStandard.InputSimulator(); - } - - private int Delay(int milliseconds) - { - int delay = milliseconds + Random.Shared.Next(1, MAX_DELAY); - _ct.WaitHandle.WaitOne(delay); - return delay; - } - - public void KeyDown(int key) - { - if (GetForegroundWindow() != wowProcess.Process.MainWindowHandle) - SetForegroundWindow(wowProcess.Process.MainWindowHandle); - - simulator.Keyboard.KeyDown((VirtualKeyCode)key); - } - - public void KeyUp(int key) - { - if (GetForegroundWindow() != wowProcess.Process.MainWindowHandle) - SetForegroundWindow(wowProcess.Process.MainWindowHandle); - - simulator.Keyboard.KeyUp((VirtualKeyCode)key); - } - - public int KeyPress(int key, int milliseconds) - { - simulator.Keyboard.KeyDown((VirtualKeyCode)key); - int delay = Delay(milliseconds); - simulator.Keyboard.KeyUp((VirtualKeyCode)key); - return delay; - } - - public void KeyPressSleep(int key, int milliseconds, CancellationToken ct) - { - simulator.Keyboard.KeyDown((VirtualKeyCode)key); - ct.WaitHandle.WaitOne(milliseconds); - simulator.Keyboard.KeyUp((VirtualKeyCode)key); - } - - public void LeftClickMouse(Point p) - { - SetCursorPosition(p); - simulator.Mouse.LeftButtonDown(); - Delay(MIN_DELAY); - simulator.Mouse.LeftButtonUp(); - } - - public void RightClickMouse(Point p) - { - SetCursorPosition(p); - simulator.Mouse.RightButtonDown(); - Delay(MIN_DELAY); - simulator.Mouse.RightButtonUp(); - } - - public void SetCursorPosition(Point p) - { - GetWindowRect(wowProcess.Process.MainWindowHandle, out Rectangle rect); - p.X = p.X * 65535 / rect.Width; - p.Y = p.Y * 65535 / rect.Height; - simulator.Mouse.MoveMouseTo(Convert.ToDouble(p.X), Convert.ToDouble(p.Y)); - } - - public void SendText(string text) - { - if (GetForegroundWindow() != wowProcess.Process.MainWindowHandle) - SetForegroundWindow(wowProcess.Process.MainWindowHandle); - - simulator.Keyboard.TextEntry(text); - Delay(25); - } - - public void SetClipboard(string text) - { - ClipboardService.SetText(text); - } - - public void PasteFromClipboard() - { - if (GetForegroundWindow() != wowProcess.Process.MainWindowHandle) - SetForegroundWindow(wowProcess.Process.MainWindowHandle); - - simulator.Keyboard.ModifiedKeyStroke(VirtualKeyCode.LCONTROL, VirtualKeyCode.VK_V); - } + if (GetForegroundWindow() != wowProcess.Process.MainWindowHandle) + SetForegroundWindow(wowProcess.Process.MainWindowHandle); + + simulator.Keyboard.KeyUp((VirtualKeyCode)key); + } + + public int KeyPress(int key, int milliseconds) + { + simulator.Keyboard.KeyDown((VirtualKeyCode)key); + int delay = Delay(milliseconds); + simulator.Keyboard.KeyUp((VirtualKeyCode)key); + return delay; + } + + public void KeyPressSleep(int key, int milliseconds, CancellationToken ct) + { + simulator.Keyboard.KeyDown((VirtualKeyCode)key); + ct.WaitHandle.WaitOne(milliseconds); + simulator.Keyboard.KeyUp((VirtualKeyCode)key); + } + + public void LeftClickMouse(Point p) + { + SetCursorPosition(p); + simulator.Mouse.LeftButtonDown(); + Delay(MIN_DELAY); + simulator.Mouse.LeftButtonUp(); + } + + public void RightClickMouse(Point p) + { + SetCursorPosition(p); + simulator.Mouse.RightButtonDown(); + Delay(MIN_DELAY); + simulator.Mouse.RightButtonUp(); + } + + public void SetCursorPosition(Point p) + { + GetWindowRect(wowProcess.Process.MainWindowHandle, out Rectangle rect); + p.X = p.X * 65535 / rect.Width; + p.Y = p.Y * 65535 / rect.Height; + simulator.Mouse.MoveMouseTo(Convert.ToDouble(p.X), Convert.ToDouble(p.Y)); + } + + public void SendText(string text) + { + if (GetForegroundWindow() != wowProcess.Process.MainWindowHandle) + SetForegroundWindow(wowProcess.Process.MainWindowHandle); + + simulator.Keyboard.TextEntry(text); + Delay(25); + } + + public void SetClipboard(string text) + { + ClipboardService.SetText(text); + } + + public void PasteFromClipboard() + { + if (GetForegroundWindow() != wowProcess.Process.MainWindowHandle) + SetForegroundWindow(wowProcess.Process.MainWindowHandle); + + simulator.Keyboard.ModifiedKeyStroke(VirtualKeyCode.LCONTROL, VirtualKeyCode.VK_V); } } diff --git a/Game/Input/InputWindowsNative.cs b/Game/Input/InputWindowsNative.cs index ec9ff26ca..1e3caf21b 100644 --- a/Game/Input/InputWindowsNative.cs +++ b/Game/Input/InputWindowsNative.cs @@ -4,115 +4,114 @@ using TextCopy; using static WinAPI.NativeMethods; -namespace Game +namespace Game; + +public sealed class InputWindowsNative : IInput { - public sealed class InputWindowsNative : IInput - { - private readonly int MIN_DELAY; - private readonly int MAX_DELAY; + private readonly int MIN_DELAY; + private readonly int MAX_DELAY; - private readonly WowProcess wowProcess; + private readonly WowProcess wowProcess; - private readonly CancellationToken _ct; + private readonly CancellationToken _ct; - public InputWindowsNative(WowProcess wowProcess, CancellationTokenSource cts, int minDelay, int maxDelay) - { - this.wowProcess = wowProcess; - _ct = cts.Token; + public InputWindowsNative(WowProcess wowProcess, CancellationTokenSource cts, int minDelay, int maxDelay) + { + this.wowProcess = wowProcess; + _ct = cts.Token; - MIN_DELAY = minDelay; - MAX_DELAY = maxDelay; - } + MIN_DELAY = minDelay; + MAX_DELAY = maxDelay; + } - private int Delay(int milliseconds) - { - int delay = milliseconds + Random.Shared.Next(1, MAX_DELAY); - _ct.WaitHandle.WaitOne(delay); - return delay; - } + private int Delay(int milliseconds) + { + int delay = milliseconds + Random.Shared.Next(1, MAX_DELAY); + _ct.WaitHandle.WaitOne(delay); + return delay; + } - public void KeyDown(int key) - { - PostMessage(wowProcess.Process.MainWindowHandle, WM_KEYDOWN, key, 0); - } + public void KeyDown(int key) + { + PostMessage(wowProcess.Process.MainWindowHandle, WM_KEYDOWN, key, 0); + } - public void KeyUp(int key) - { - PostMessage(wowProcess.Process.MainWindowHandle, WM_KEYUP, key, 0); - } + public void KeyUp(int key) + { + PostMessage(wowProcess.Process.MainWindowHandle, WM_KEYUP, key, 0); + } - public int KeyPress(int key, int milliseconds) - { - PostMessage(wowProcess.Process.MainWindowHandle, WM_KEYDOWN, key, 0); - int delay = Delay(milliseconds); - PostMessage(wowProcess.Process.MainWindowHandle, WM_KEYUP, key, 0); + public int KeyPress(int key, int milliseconds) + { + PostMessage(wowProcess.Process.MainWindowHandle, WM_KEYDOWN, key, 0); + int delay = Delay(milliseconds); + PostMessage(wowProcess.Process.MainWindowHandle, WM_KEYUP, key, 0); - return delay; - } + return delay; + } - public void KeyPressSleep(int key, int milliseconds, CancellationToken ct) - { - PostMessage(wowProcess.Process.MainWindowHandle, WM_KEYDOWN, key, 0); - ct.WaitHandle.WaitOne(milliseconds); - PostMessage(wowProcess.Process.MainWindowHandle, WM_KEYUP, key, 0); - } + public void KeyPressSleep(int key, int milliseconds, CancellationToken ct) + { + PostMessage(wowProcess.Process.MainWindowHandle, WM_KEYDOWN, key, 0); + ct.WaitHandle.WaitOne(milliseconds); + PostMessage(wowProcess.Process.MainWindowHandle, WM_KEYUP, key, 0); + } - public void LeftClickMouse(Point p) - { - SetCursorPosition(p); + public void LeftClickMouse(Point p) + { + SetCursorPosition(p); - ScreenToClient(wowProcess.Process.MainWindowHandle, ref p); - int lparam = MakeLParam(p.X, p.Y); + ScreenToClient(wowProcess.Process.MainWindowHandle, ref p); + int lparam = MakeLParam(p.X, p.Y); - PostMessage(wowProcess.Process.MainWindowHandle, WM_LBUTTONDOWN, 0, lparam); + PostMessage(wowProcess.Process.MainWindowHandle, WM_LBUTTONDOWN, 0, lparam); - Delay(MIN_DELAY); + Delay(MIN_DELAY); - GetCursorPos(out p); - ScreenToClient(wowProcess.Process.MainWindowHandle, ref p); - lparam = MakeLParam(p.X, p.Y); + GetCursorPos(out p); + ScreenToClient(wowProcess.Process.MainWindowHandle, ref p); + lparam = MakeLParam(p.X, p.Y); - PostMessage(wowProcess.Process.MainWindowHandle, WM_LBUTTONUP, 0, lparam); - } + PostMessage(wowProcess.Process.MainWindowHandle, WM_LBUTTONUP, 0, lparam); + } - public void RightClickMouse(Point p) - { - SetCursorPosition(p); + public void RightClickMouse(Point p) + { + SetCursorPosition(p); - ScreenToClient(wowProcess.Process.MainWindowHandle, ref p); - int lparam = MakeLParam(p.X, p.Y); + ScreenToClient(wowProcess.Process.MainWindowHandle, ref p); + int lparam = MakeLParam(p.X, p.Y); - PostMessage(wowProcess.Process.MainWindowHandle, WM_RBUTTONDOWN, 0, lparam); + PostMessage(wowProcess.Process.MainWindowHandle, WM_RBUTTONDOWN, 0, lparam); - Delay(MIN_DELAY); + Delay(MIN_DELAY); - GetCursorPos(out p); - ScreenToClient(wowProcess.Process.MainWindowHandle, ref p); - lparam = MakeLParam(p.X, p.Y); + GetCursorPos(out p); + ScreenToClient(wowProcess.Process.MainWindowHandle, ref p); + lparam = MakeLParam(p.X, p.Y); - PostMessage(wowProcess.Process.MainWindowHandle, WM_RBUTTONUP, 0, lparam); - } + PostMessage(wowProcess.Process.MainWindowHandle, WM_RBUTTONUP, 0, lparam); + } - public void SetCursorPosition(Point p) - { - SetCursorPos(p.X, p.Y); - } + public void SetCursorPosition(Point p) + { + SetCursorPos(p.X, p.Y); + } - public void SendText(string text) - { - // currently not supported - throw new NotImplementedException(); - } + public void SendText(string text) + { + // currently not supported + throw new NotImplementedException(); + } - public void SetClipboard(string text) - { - ClipboardService.SetText(text); - } + public void SetClipboard(string text) + { + ClipboardService.SetText(text); + } - public void PasteFromClipboard() - { - // currently not supported - throw new NotImplementedException(); - } + public void PasteFromClipboard() + { + // currently not supported + throw new NotImplementedException(); } } diff --git a/Game/Input/WowProcessInput.cs b/Game/Input/WowProcessInput.cs index 437d27a4f..e91878f9e 100644 --- a/Game/Input/WowProcessInput.cs +++ b/Game/Input/WowProcessInput.cs @@ -8,210 +8,209 @@ #pragma warning disable 162 -namespace Game +namespace Game; + +public sealed partial class WowProcessInput : IMouseInput { - public sealed partial class WowProcessInput : IMouseInput - { - private const bool LogInput = false; - private const bool LogMove = false; + private const bool LogInput = false; + private const bool LogMove = false; - private const int MIN_DELAY = 25; - private const int MAX_DELAY = 55; + private const int MIN_DELAY = 25; + private const int MAX_DELAY = 55; - private readonly ILogger logger; + private readonly ILogger logger; - private readonly WowProcess wowProcess; - private readonly InputWindowsNative nativeInput; - private readonly IInput simulatorInput; + private readonly WowProcess wowProcess; + private readonly InputWindowsNative nativeInput; + private readonly IInput simulatorInput; - private readonly bool[] keysDown = new bool[(int)ConsoleKey.OemClear]; + private readonly bool[] keysDown = new bool[(int)ConsoleKey.OemClear]; - public ConsoleKey ForwardKey { get; set; } - public ConsoleKey BackwardKey { get; set; } - public ConsoleKey TurnLeftKey { get; set; } - public ConsoleKey TurnRightKey { get; set; } - public ConsoleKey InteractMouseover { get; set; } - public int InteractMouseoverPress { get; set; } = 10; + public ConsoleKey ForwardKey { get; set; } + public ConsoleKey BackwardKey { get; set; } + public ConsoleKey TurnLeftKey { get; set; } + public ConsoleKey TurnRightKey { get; set; } + public ConsoleKey InteractMouseover { get; set; } + public int InteractMouseoverPress { get; set; } = 10; - public WowProcessInput(ILogger logger, CancellationTokenSource cts, WowProcess wowProcess) - { - this.logger = logger; - this.wowProcess = wowProcess; + public WowProcessInput(ILogger logger, CancellationTokenSource cts, WowProcess wowProcess) + { + this.logger = logger; + this.wowProcess = wowProcess; - nativeInput = new InputWindowsNative(wowProcess, cts, MIN_DELAY, MAX_DELAY); - simulatorInput = new InputSimulator(wowProcess, cts, MIN_DELAY, MAX_DELAY); - } + nativeInput = new InputWindowsNative(wowProcess, cts, MIN_DELAY, MAX_DELAY); + simulatorInput = new InputSimulator(wowProcess, cts, MIN_DELAY, MAX_DELAY); + } - public void Reset() + public void Reset() + { + lock (keysDown) { - lock (keysDown) + for (int i = 0; i < keysDown.Length; i++) { - for (int i = 0; i < keysDown.Length; i++) - { - keysDown[i] = false; - } + keysDown[i] = false; } } + } - public void KeyDown(ConsoleKey key, bool forced) + public void KeyDown(ConsoleKey key, bool forced) + { + if (IsKeyDown(key)) { - if (IsKeyDown(key)) - { - if (!forced) - return; - } + if (!forced) + return; + } - if (LogInput) + if (LogInput) + { + if (key == ForwardKey || key == BackwardKey || key == TurnLeftKey || key == TurnRightKey) { - if (key == ForwardKey || key == BackwardKey || key == TurnLeftKey || key == TurnRightKey) - { - if (LogMove) - LogKeyDown(logger, key); - } - else - { + if (LogMove) LogKeyDown(logger, key); - } } + else + { + LogKeyDown(logger, key); + } + } + + keysDown[(int)key] = true; + nativeInput.KeyDown((int)key); + } - keysDown[(int)key] = true; - nativeInput.KeyDown((int)key); + public void KeyUp(ConsoleKey key, bool forced) + { + if (!IsKeyDown(key)) + { + if (!forced) + return; } - public void KeyUp(ConsoleKey key, bool forced) + if (LogInput) { - if (!IsKeyDown(key)) + if (key == ForwardKey || key == BackwardKey || key == TurnLeftKey || key == TurnRightKey) { - if (!forced) - return; + if (LogMove) + LogKeyUp(logger, key); } - - if (LogInput) + else { - if (key == ForwardKey || key == BackwardKey || key == TurnLeftKey || key == TurnRightKey) - { - if (LogMove) - LogKeyUp(logger, key); - } - else - { - LogKeyUp(logger, key); - } + LogKeyUp(logger, key); } - - nativeInput.KeyUp((int)key); - keysDown[(int)key] = false; } - public bool IsKeyDown(ConsoleKey key) - { - return keysDown[(int)key]; - } + nativeInput.KeyUp((int)key); + keysDown[(int)key] = false; + } - public void SendText(string payload) - { - simulatorInput.SendText(payload); - } + public bool IsKeyDown(ConsoleKey key) + { + return keysDown[(int)key]; + } - public void SetClipboard(string text) - { - simulatorInput.SetClipboard(text); - } + public void SendText(string payload) + { + simulatorInput.SendText(payload); + } - public void PasteFromClipboard() - { - simulatorInput.PasteFromClipboard(); - } + public void SetClipboard(string text) + { + simulatorInput.SetClipboard(text); + } - public void SetForegroundWindow() - { - NativeMethods.SetForegroundWindow(wowProcess.Process.MainWindowHandle); - } + public void PasteFromClipboard() + { + simulatorInput.PasteFromClipboard(); + } - public int KeyPress(ConsoleKey key, int milliseconds) - { - keysDown[(int)key] = true; - int totalElapsedMs = nativeInput.KeyPress((int)key, milliseconds); - keysDown[(int)key] = false; + public void SetForegroundWindow() + { + NativeMethods.SetForegroundWindow(wowProcess.Process.MainWindowHandle); + } - if (LogInput) - { - LogKeyPress(logger, key, totalElapsedMs); - } + public int KeyPress(ConsoleKey key, int milliseconds) + { + keysDown[(int)key] = true; + int totalElapsedMs = nativeInput.KeyPress((int)key, milliseconds); + keysDown[(int)key] = false; - return totalElapsedMs; + if (LogInput) + { + LogKeyPress(logger, key, totalElapsedMs); } - public void KeyPressSleep(ConsoleKey key, int milliseconds, CancellationToken ct) - { - if (milliseconds < 1) - return; + return totalElapsedMs; + } + + public void KeyPressSleep(ConsoleKey key, int milliseconds, CancellationToken ct) + { + if (milliseconds < 1) + return; - if (LogInput) + if (LogInput) + { + if (key == ForwardKey || key == BackwardKey || key == TurnLeftKey || key == TurnRightKey) { - if (key == ForwardKey || key == BackwardKey || key == TurnLeftKey || key == TurnRightKey) - { - if (LogMove) - LogKeyPress(logger, key, milliseconds); - } - else - { + if (LogMove) LogKeyPress(logger, key, milliseconds); - } } - - keysDown[(int)key] = true; - nativeInput.KeyPressSleep((int)key, milliseconds, ct); - keysDown[(int)key] = false; + else + { + LogKeyPress(logger, key, milliseconds); + } } - public void SetKeyState(ConsoleKey key, bool pressDown, bool forced = false) - { - if (pressDown) { KeyDown(key, forced); } else { KeyUp(key, forced); } - } + keysDown[(int)key] = true; + nativeInput.KeyPressSleep((int)key, milliseconds, ct); + keysDown[(int)key] = false; + } - public void SetCursorPosition(Point p) - { - nativeInput.SetCursorPosition(p); - } + public void SetKeyState(ConsoleKey key, bool pressDown, bool forced = false) + { + if (pressDown) { KeyDown(key, forced); } else { KeyUp(key, forced); } + } - public void RightClickMouse(Point p) - { - nativeInput.RightClickMouse(p); - } + public void SetCursorPosition(Point p) + { + nativeInput.SetCursorPosition(p); + } - public void LeftClickMouse(Point p) - { - nativeInput.LeftClickMouse(p); - } + public void RightClickMouse(Point p) + { + nativeInput.RightClickMouse(p); + } - public void InteractMouseOver(CancellationToken ct) - { - KeyPressSleep(InteractMouseover, InteractMouseoverPress, ct); - } + public void LeftClickMouse(Point p) + { + nativeInput.LeftClickMouse(p); + } - [LoggerMessage( - EventId = 25, - Level = LogLevel.Debug, - Message = @"Input: KeyDown {key}")] - static partial void LogKeyDown(ILogger logger, ConsoleKey key); - - [LoggerMessage( - EventId = 26, - Level = LogLevel.Debug, - Message = @"Input: KeyUp {key}")] - static partial void LogKeyUp(ILogger logger, ConsoleKey key); - - [LoggerMessage( - EventId = 27, - Level = LogLevel.Debug, - Message = @"Input: [{key}] pressed for {milliseconds}ms")] - static partial void LogKeyPress(ILogger logger, ConsoleKey key, int milliseconds); - - [LoggerMessage( - EventId = 28, - Level = LogLevel.Debug, - Message = @"Input: [{key}] pressing for {milliseconds}ms")] - static partial void LogKeyPressNoDelay(ILogger logger, ConsoleKey key, int milliseconds); + public void InteractMouseOver(CancellationToken ct) + { + KeyPressSleep(InteractMouseover, InteractMouseoverPress, ct); } + + [LoggerMessage( + EventId = 25, + Level = LogLevel.Debug, + Message = @"Input: KeyDown {key}")] + static partial void LogKeyDown(ILogger logger, ConsoleKey key); + + [LoggerMessage( + EventId = 26, + Level = LogLevel.Debug, + Message = @"Input: KeyUp {key}")] + static partial void LogKeyUp(ILogger logger, ConsoleKey key); + + [LoggerMessage( + EventId = 27, + Level = LogLevel.Debug, + Message = @"Input: [{key}] pressed for {milliseconds}ms")] + static partial void LogKeyPress(ILogger logger, ConsoleKey key, int milliseconds); + + [LoggerMessage( + EventId = 28, + Level = LogLevel.Debug, + Message = @"Input: [{key}] pressing for {milliseconds}ms")] + static partial void LogKeyPressNoDelay(ILogger logger, ConsoleKey key, int milliseconds); } diff --git a/Game/WoWProcess/WowProcess.cs b/Game/WoWProcess/WowProcess.cs index 930b4c8c0..05f4e3033 100644 --- a/Game/WoWProcess/WowProcess.cs +++ b/Game/WoWProcess/WowProcess.cs @@ -4,122 +4,121 @@ #nullable enable -namespace Game +namespace Game; + +public sealed class WowProcess : IDisposable { - public sealed class WowProcess : IDisposable - { - private static readonly string[] defaultProcessNames = new string[] { - "Wow", - "WowClassic", - "WowClassicT", - "Wow-64", - "WowClassicB" - }; + private static readonly string[] defaultProcessNames = new string[] { + "Wow", + "WowClassic", + "WowClassicT", + "Wow-64", + "WowClassicB" + }; - private readonly Thread thread; - private readonly CancellationTokenSource cts; + private readonly Thread thread; + private readonly CancellationTokenSource cts; - public Process Process { get; private set; } + public Process Process { get; private set; } - public Version FileVersion { get; private set; } + public Version FileVersion { get; private set; } - public string Path { get; private set; } + public string Path { get; private set; } - private int processId = -1; - public int ProcessId + private int processId = -1; + public int ProcessId + { + get => processId; + set { - get => processId; - set - { - processId = value; - Process = Process.GetProcessById(processId); - } + processId = value; + Process = Process.GetProcessById(processId); } + } - public bool IsRunning { get; private set; } + public bool IsRunning { get; private set; } - public WowProcess(int pid = -1) - { - Process? p = Get(pid); - if (p == null) - throw new NullReferenceException("Unable to find running World of Warcraft process!"); - - Process = p; - processId = Process.Id; - IsRunning = true; - (Path, FileVersion) = GetProcessInfo(); - - cts = new(); - thread = new(PollProcessExited); - thread.Start(); - } + public WowProcess(int pid = -1) + { + Process? p = Get(pid); + if (p == null) + throw new NullReferenceException("Unable to find running World of Warcraft process!"); + + Process = p; + processId = Process.Id; + IsRunning = true; + (Path, FileVersion) = GetProcessInfo(); + + cts = new(); + thread = new(PollProcessExited); + thread.Start(); + } - public void Dispose() - { - cts.Cancel(); - } + public void Dispose() + { + cts.Cancel(); + } - private void PollProcessExited() + private void PollProcessExited() + { + while (!cts.IsCancellationRequested) { - while (!cts.IsCancellationRequested) + Process.Refresh(); + if (Process.HasExited) { - Process.Refresh(); - if (Process.HasExited) + IsRunning = false; + + Process? p = Get(); + if (p != null) { - IsRunning = false; - - Process? p = Get(); - if (p != null) - { - Process = p; - processId = Process.Id; - IsRunning = true; - (Path, FileVersion) = GetProcessInfo(); - } + Process = p; + processId = Process.Id; + IsRunning = true; + (Path, FileVersion) = GetProcessInfo(); } - - cts.Token.WaitHandle.WaitOne(5000); } + + cts.Token.WaitHandle.WaitOne(5000); } + } - public static Process? Get(int processId = -1) + public static Process? Get(int processId = -1) + { + if (processId != -1) { - if (processId != -1) - { - return Process.GetProcessById(processId); - } + return Process.GetProcessById(processId); + } - Process[] processList = Process.GetProcesses(); - for (int i = 0; i < processList.Length; i++) + Process[] processList = Process.GetProcesses(); + for (int i = 0; i < processList.Length; i++) + { + Process p = processList[i]; + for (int j = 0; j < defaultProcessNames.Length; j++) { - Process p = processList[i]; - for (int j = 0; j < defaultProcessNames.Length; j++) + if (defaultProcessNames[j].Contains(p.ProcessName, StringComparison.OrdinalIgnoreCase)) { - if (defaultProcessNames[j].Contains(p.ProcessName, StringComparison.OrdinalIgnoreCase)) - { - return p; - } + return p; } } - - return null; } - private (string path, Version version) GetProcessInfo() - { - string path = WinAPI.ExecutablePath.Get(Process); - if (string.IsNullOrEmpty(path)) - { - throw new NullReferenceException("Unable identify World of Warcraft process path!"); - } + return null; + } - FileVersionInfo fileVersion = FileVersionInfo.GetVersionInfo(System.IO.Path.Join(path, Process.ProcessName + ".exe")); - if (Version.TryParse(fileVersion.FileVersion, out Version? v)) - { - return (path, v); - } + private (string path, Version version) GetProcessInfo() + { + string path = WinAPI.ExecutablePath.Get(Process); + if (string.IsNullOrEmpty(path)) + { + throw new NullReferenceException("Unable identify World of Warcraft process path!"); + } - return (path, new()); + FileVersionInfo fileVersion = FileVersionInfo.GetVersionInfo(System.IO.Path.Join(path, Process.ProcessName + ".exe")); + if (Version.TryParse(fileVersion.FileVersion, out Version? v)) + { + return (path, v); } + + return (path, new()); } } \ No newline at end of file diff --git a/Game/WoWScreen/IWowScreen.cs b/Game/WoWScreen/IWowScreen.cs index ed91ce6a0..6a2499a7a 100644 --- a/Game/WoWScreen/IWowScreen.cs +++ b/Game/WoWScreen/IWowScreen.cs @@ -1,9 +1,8 @@ using SharedLib; -namespace Game +namespace Game; + +public interface IWowScreen : IColorReader, IRectProvider { - public interface IWowScreen : IColorReader, IRectProvider - { - bool Enabled { get; set; } - } + bool Enabled { get; set; } } diff --git a/Game/WoWScreen/WowScreen.cs b/Game/WoWScreen/WowScreen.cs index ca4c9b057..3a328ae0a 100644 --- a/Game/WoWScreen/WowScreen.cs +++ b/Game/WoWScreen/WowScreen.cs @@ -8,179 +8,178 @@ using System.IO; using WinAPI; -namespace Game +namespace Game; + +public sealed class WowScreen : IWowScreen, IBitmapProvider, IDisposable { - public sealed class WowScreen : IWowScreen, IBitmapProvider, IDisposable - { - private readonly ILogger logger; - private readonly WowProcess wowProcess; + private readonly ILogger logger; + private readonly WowProcess wowProcess; - public event Action OnScreenChanged; + public event Action OnScreenChanged; - private readonly List> drawActions = new(); + private readonly List> drawActions = new(); - // TODO: make it work for higher resolution ex. 4k - public const int MinimapSize = 200; + // TODO: make it work for higher resolution ex. 4k + public const int MinimapSize = 200; - public bool Enabled { get; set; } + public bool Enabled { get; set; } - public bool EnablePostProcess { get; set; } - public Bitmap Bitmap { get; private set; } + public bool EnablePostProcess { get; set; } + public Bitmap Bitmap { get; private set; } - public Bitmap MiniMapBitmap { get; private set; } + public Bitmap MiniMapBitmap { get; private set; } - public IntPtr ProcessHwnd => wowProcess.Process.MainWindowHandle; + public IntPtr ProcessHwnd => wowProcess.Process.MainWindowHandle; - private Rectangle rect; - public Rectangle Rect => rect; + private Rectangle rect; + public Rectangle Rect => rect; - private readonly Graphics graphics; - private readonly Graphics graphicsMinimap; + private readonly Graphics graphics; + private readonly Graphics graphicsMinimap; - private readonly SolidBrush blackPen; + private readonly SolidBrush blackPen; - public WowScreen(ILogger logger, WowProcess wowProcess) - { - this.logger = logger; - this.wowProcess = wowProcess; + public WowScreen(ILogger logger, WowProcess wowProcess) + { + this.logger = logger; + this.wowProcess = wowProcess; - Point p = new(); - GetPosition(ref p); - GetRectangle(out rect); - rect.Location = p; + Point p = new(); + GetPosition(ref p); + GetRectangle(out rect); + rect.Location = p; - Bitmap = new Bitmap(rect.Width, rect.Height, PixelFormat.Format32bppPArgb); - graphics = Graphics.FromImage(Bitmap); + Bitmap = new Bitmap(rect.Width, rect.Height, PixelFormat.Format32bppPArgb); + graphics = Graphics.FromImage(Bitmap); - MiniMapBitmap = new Bitmap(MinimapSize, MinimapSize, PixelFormat.Format32bppPArgb); - graphicsMinimap = Graphics.FromImage(MiniMapBitmap); + MiniMapBitmap = new Bitmap(MinimapSize, MinimapSize, PixelFormat.Format32bppPArgb); + graphicsMinimap = Graphics.FromImage(MiniMapBitmap); - blackPen = new SolidBrush(Color.Black); + blackPen = new SolidBrush(Color.Black); - logger.LogInformation($"[{nameof(WowScreen)}] {rect} - Windowed Mode: {NativeMethods.IsWindowedMode(p)}"); - } + logger.LogInformation($"[{nameof(WowScreen)}] {rect} - Windowed Mode: {NativeMethods.IsWindowedMode(p)}"); + } - public void Update() - { - Point p = new(); - GetPosition(ref p); - rect.Location = p; + public void Update() + { + Point p = new(); + GetPosition(ref p); + rect.Location = p; - graphics.CopyFromScreen(rect.Location, Point.Empty, Bitmap.Size); - } + graphics.CopyFromScreen(rect.Location, Point.Empty, Bitmap.Size); + } - public void AddDrawAction(Action a) - { - drawActions.Add(a); - } + public void AddDrawAction(Action a) + { + drawActions.Add(a); + } - public void PostProcess() + public void PostProcess() + { + using (Graphics gr = Graphics.FromImage(Bitmap)) { - using (Graphics gr = Graphics.FromImage(Bitmap)) - { - gr.FillRectangle(blackPen, - new Rectangle(new Point(Bitmap.Width / 15, Bitmap.Height / 40), - new Size(Bitmap.Width / 15, Bitmap.Height / 40))); + gr.FillRectangle(blackPen, + new Rectangle(new Point(Bitmap.Width / 15, Bitmap.Height / 40), + new Size(Bitmap.Width / 15, Bitmap.Height / 40))); - for (int i = 0; i < drawActions.Count; i++) - { - drawActions[i](gr); - } + for (int i = 0; i < drawActions.Count; i++) + { + drawActions[i](gr); } - - OnScreenChanged?.Invoke(); } - public void GetPosition(ref Point point) - { - NativeMethods.GetPosition(wowProcess.Process.MainWindowHandle, ref point); - } + OnScreenChanged?.Invoke(); + } - public void GetRectangle(out Rectangle rect) - { - NativeMethods.GetWindowRect(wowProcess.Process.MainWindowHandle, out rect); - } + public void GetPosition(ref Point point) + { + NativeMethods.GetPosition(wowProcess.Process.MainWindowHandle, ref point); + } + public void GetRectangle(out Rectangle rect) + { + NativeMethods.GetWindowRect(wowProcess.Process.MainWindowHandle, out rect); + } - public Bitmap GetBitmap(int width, int height) - { - Update(); - Bitmap bitmap = new(width, height); - Rectangle sourceRect = new(0, 0, width, height); - using (var graphics = Graphics.FromImage(bitmap)) - { - graphics.DrawImage(Bitmap, 0, 0, sourceRect, GraphicsUnit.Pixel); - } - return bitmap; - } + public Bitmap GetBitmap(int width, int height) + { + Update(); - public Color GetColorAt(Point point) + Bitmap bitmap = new(width, height); + Rectangle sourceRect = new(0, 0, width, height); + using (var graphics = Graphics.FromImage(bitmap)) { - return Bitmap.GetPixel(point.X, point.Y); + graphics.DrawImage(Bitmap, 0, 0, sourceRect, GraphicsUnit.Pixel); } + return bitmap; + } - public void UpdateMinimapBitmap() - { - GetRectangle(out var rect); - graphicsMinimap.CopyFromScreen(rect.Right - MinimapSize, rect.Top, 0, 0, MiniMapBitmap.Size); - } + public Color GetColorAt(Point point) + { + return Bitmap.GetPixel(point.X, point.Y); + } - public void Dispose() - { - Bitmap.Dispose(); - graphics.Dispose(); - graphicsMinimap.Dispose(); + public void UpdateMinimapBitmap() + { + GetRectangle(out var rect); + graphicsMinimap.CopyFromScreen(rect.Right - MinimapSize, rect.Top, 0, 0, MiniMapBitmap.Size); + } - blackPen.Dispose(); - } + public void Dispose() + { + Bitmap.Dispose(); + graphics.Dispose(); + graphicsMinimap.Dispose(); - private static Bitmap CropImage(Bitmap img, bool highlight) - { - int x = img.Width / 2; - int y = img.Height / 2; - int r = Math.Min(x, y); + blackPen.Dispose(); + } + + private static Bitmap CropImage(Bitmap img, bool highlight) + { + int x = img.Width / 2; + int y = img.Height / 2; + int r = Math.Min(x, y); - var tmp = new Bitmap(2 * r, 2 * r); - using (Graphics g = Graphics.FromImage(tmp)) + var tmp = new Bitmap(2 * r, 2 * r); + using (Graphics g = Graphics.FromImage(tmp)) + { + if (highlight) { - if (highlight) + using (SolidBrush brush = new SolidBrush(Color.FromArgb(255, 0, 0))) { - using (SolidBrush brush = new SolidBrush(Color.FromArgb(255, 0, 0))) - { - g.FillRectangle(brush, 0, 0, img.Width, img.Height); - } + g.FillRectangle(brush, 0, 0, img.Width, img.Height); } + } - g.SmoothingMode = SmoothingMode.None; - g.TranslateTransform(tmp.Width / 2, tmp.Height / 2); - using (var gp = new GraphicsPath()) + g.SmoothingMode = SmoothingMode.None; + g.TranslateTransform(tmp.Width / 2, tmp.Height / 2); + using (var gp = new GraphicsPath()) + { + gp.AddEllipse(0 - r, 0 - r, 2 * r, 2 * r); + using (var region = new Region(gp)) { - gp.AddEllipse(0 - r, 0 - r, 2 * r, 2 * r); - using (var region = new Region(gp)) + g.SetClip(region, CombineMode.Replace); + using (var bmp = new Bitmap(img)) { - g.SetClip(region, CombineMode.Replace); - using (var bmp = new Bitmap(img)) - { - g.DrawImage(bmp, new Rectangle(-r, -r, 2 * r, 2 * r), new Rectangle(x - r, y - r, 2 * r, 2 * r), GraphicsUnit.Pixel); - } + g.DrawImage(bmp, new Rectangle(-r, -r, 2 * r, 2 * r), new Rectangle(x - r, y - r, 2 * r, 2 * r), GraphicsUnit.Pixel); } } } - return tmp; } + return tmp; + } - public static string ToBase64(Bitmap bitmap, Bitmap resized, Graphics graphics) - { - graphics.DrawImage(bitmap, 0, 0, resized.Width, resized.Height); - - using MemoryStream ms = new(); - resized.Save(ms, ImageFormat.Png); + public static string ToBase64(Bitmap bitmap, Bitmap resized, Graphics graphics) + { + graphics.DrawImage(bitmap, 0, 0, resized.Width, resized.Height); - byte[] byteImage = ms.ToArray(); - return Convert.ToBase64String(byteImage); - } + using MemoryStream ms = new(); + resized.Save(ms, ImageFormat.Png); + byte[] byteImage = ms.ToArray(); + return Convert.ToBase64String(byteImage); } + } \ No newline at end of file diff --git a/HeadlessServer/HeadlessServer.cs b/HeadlessServer/HeadlessServer.cs index 2e1bcde99..a31f28ed0 100644 --- a/HeadlessServer/HeadlessServer.cs +++ b/HeadlessServer/HeadlessServer.cs @@ -2,67 +2,66 @@ using Core; using Microsoft.Extensions.Logging; -namespace HeadlessServer +namespace HeadlessServer; + +public sealed class HeadlessServer { - public sealed class HeadlessServer + private readonly ILogger logger; + private readonly IBotController botController; + private readonly IAddonReader addonReader; + private readonly ExecGameCommand exec; + private readonly AddonConfigurator addonConfigurator; + private readonly Wait wait; + + public HeadlessServer(ILogger logger, IBotController botController, + IAddonReader addonReader, ExecGameCommand exec, AddonConfigurator addonConfigurator, Wait wait) { - private readonly ILogger logger; - private readonly IBotController botController; - private readonly IAddonReader addonReader; - private readonly ExecGameCommand exec; - private readonly AddonConfigurator addonConfigurator; - private readonly Wait wait; + this.logger = logger; + this.botController = botController; + this.addonReader = addonReader; + this.exec = exec; + this.addonConfigurator = addonConfigurator; + this.wait = wait; + } - public HeadlessServer(ILogger logger, IBotController botController, - IAddonReader addonReader, ExecGameCommand exec, AddonConfigurator addonConfigurator, Wait wait) + public void Run(ParserResult options) + { + logger.LogInformation($"Running {nameof(HeadlessServer)}!"); + + if (options.Value.Pid != -1) { - this.logger = logger; - this.botController = botController; - this.addonReader = addonReader; - this.exec = exec; - this.addonConfigurator = addonConfigurator; - this.wait = wait; + logger.LogInformation($"Attached pid={options.Value.Pid}"); } - public void Run(ParserResult options) - { - logger.LogInformation($"Running {nameof(HeadlessServer)}!"); + InitState(); - if (options.Value.Pid != -1) - { - logger.LogInformation($"Attached pid={options.Value.Pid}"); - } + botController.LoadClassProfile(options.Value.ClassConfig!); - InitState(); + botController.ToggleBotStatus(); + } - botController.LoadClassProfile(options.Value.ClassConfig!); + private void InitState() + { + addonReader.FullReset(); + exec.Run($"/{addonConfigurator.Config.CommandFlush}"); - botController.ToggleBotStatus(); - } + int actionbarCost; + int spellBook; + int bag; - private void InitState() + do { - addonReader.FullReset(); - exec.Run($"/{addonConfigurator.Config.CommandFlush}"); - - int actionbarCost; - int spellBook; - int bag; - - do - { - actionbarCost = addonReader.ActionBarCostReader.Count; - spellBook = addonReader.SpellBookReader.Count; - bag = addonReader.BagReader.BagItems.Count; + actionbarCost = addonReader.ActionBarCostReader.Count; + spellBook = addonReader.SpellBookReader.Count; + bag = addonReader.BagReader.BagItems.Count; - wait.Fixed(3000); - - logger.LogInformation($"{nameof(addonReader.ActionBarCostReader)}: {actionbarCost} | {nameof(addonReader.SpellBookReader)}: {spellBook} | {nameof(addonReader.BagReader)}: {addonReader.BagReader.BagItems.Count}"); - } while ( - actionbarCost != addonReader.ActionBarCostReader.Count || - spellBook != addonReader.SpellBookReader.Count || - bag != addonReader.BagReader.BagItems.Count); - } + wait.Fixed(3000); + logger.LogInformation($"{nameof(addonReader.ActionBarCostReader)}: {actionbarCost} | {nameof(addonReader.SpellBookReader)}: {spellBook} | {nameof(addonReader.BagReader)}: {addonReader.BagReader.BagItems.Count}"); + } while ( + actionbarCost != addonReader.ActionBarCostReader.Count || + spellBook != addonReader.SpellBookReader.Count || + bag != addonReader.BagReader.BagItems.Count); } + } diff --git a/HeadlessServer/Program.cs b/HeadlessServer/Program.cs index c554510dc..baf89a174 100644 --- a/HeadlessServer/Program.cs +++ b/HeadlessServer/Program.cs @@ -14,216 +14,215 @@ using SharedLib; using Microsoft.Extensions.Logging; -namespace HeadlessServer +namespace HeadlessServer; + +internal sealed class Program { - internal sealed class Program + private static void Main(string[] args) { - private static void Main(string[] args) - { - IServiceCollection services = new ServiceCollection(); - - services.AddLogging(builder => - { - const string outputTemplate = "[{Timestamp:HH:mm:ss:fff} {Level:u3}] {Message:lj}{NewLine}{Exception}"; - - Log.Logger = new LoggerConfiguration() - .MinimumLevel.Debug() - .MinimumLevel.Override("Microsoft", LogEventLevel.Information) - .WriteTo.File("headless_out.log", - rollingInterval: RollingInterval.Day, - outputTemplate: outputTemplate) - .WriteTo.Debug(outputTemplate: outputTemplate) - .WriteTo.Console(outputTemplate: outputTemplate) - .CreateLogger(); - - ILoggerFactory logFactory = LoggerFactory.Create(builder => - { - builder.ClearProviders().AddSerilog(); - }); - - builder.Services.AddSingleton(logFactory.CreateLogger(nameof(Program))); - }); - - Log.Information($"[{nameof(Program)}] {Thread.CurrentThread.CurrentCulture.TwoLetterISOLanguageName} {DateTimeOffset.Now}"); + IServiceCollection services = new ServiceCollection(); - ParserResult options = Parser.Default.ParseArguments(args).WithNotParsed(a => + services.AddLogging(builder => + { + const string outputTemplate = "[{Timestamp:HH:mm:ss:fff} {Level:u3}] {Message:lj}{NewLine}{Exception}"; + + Log.Logger = new LoggerConfiguration() + .MinimumLevel.Debug() + .MinimumLevel.Override("Microsoft", LogEventLevel.Information) + .WriteTo.File("headless_out.log", + rollingInterval: RollingInterval.Day, + outputTemplate: outputTemplate) + .WriteTo.Debug(outputTemplate: outputTemplate) + .WriteTo.Console(outputTemplate: outputTemplate) + .CreateLogger(); + + ILoggerFactory logFactory = LoggerFactory.Create(builder => { - Log.Error("Missing Required command line argument!"); + builder.ClearProviders().AddSerilog(); }); - if (options.Tag == ParserResultType.NotParsed) - { - Console.ReadLine(); - return; - } + builder.Services.AddSingleton(logFactory.CreateLogger(nameof(Program))); + }); - if (!FrameConfig.Exists() || !AddonConfig.Exists()) - { - Log.Error("Unable to run headless server as crucial configuration files missing!"); - Log.Warning($"Please be sure, the following validated configuration files present next to the executable:"); - Log.Warning($"{System.IO.Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location)}"); - Log.Warning($"* {DataConfigMeta.DefaultFileName}"); - Log.Warning($"* {FrameConfigMeta.DefaultFilename}"); - Log.Warning($"* {AddonConfigMeta.DefaultFileName}"); - Console.ReadLine(); - return; - } + Log.Information($"[{nameof(Program)}] {Thread.CurrentThread.CurrentCulture.TwoLetterISOLanguageName} {DateTimeOffset.Now}"); - while (WowProcess.Get(options.Value.Pid) == null) - { - Log.Warning($"[{nameof(Program)}] Unable to find any Wow process, is it running ?"); - Thread.Sleep(1000); - } + ParserResult options = Parser.Default.ParseArguments(args).WithNotParsed(a => + { + Log.Error("Missing Required command line argument!"); + }); - if (ConfigureServices(services, options)) - { - services - .AddSingleton() - .BuildServiceProvider(new ServiceProviderOptions() { ValidateOnBuild = true }) - .GetRequiredService() - .Run(options); - } + if (options.Tag == ParserResultType.NotParsed) + { + Console.ReadLine(); + return; + } + if (!FrameConfig.Exists() || !AddonConfig.Exists()) + { + Log.Error("Unable to run headless server as crucial configuration files missing!"); + Log.Warning($"Please be sure, the following validated configuration files present next to the executable:"); + Log.Warning($"{System.IO.Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location)}"); + Log.Warning($"* {DataConfigMeta.DefaultFileName}"); + Log.Warning($"* {FrameConfigMeta.DefaultFilename}"); + Log.Warning($"* {AddonConfigMeta.DefaultFileName}"); Console.ReadLine(); + return; } - private static bool ConfigureServices(IServiceCollection services, ParserResult options) + while (WowProcess.Get(options.Value.Pid) == null) { - if (!Validate(options.Value.Pid)) - return false; + Log.Warning($"[{nameof(Program)}] Unable to find any Wow process, is it running ?"); + Thread.Sleep(1000); + } - services.AddSingleton(); + if (ConfigureServices(services, options)) + { + services + .AddSingleton() + .BuildServiceProvider(new ServiceProviderOptions() { ValidateOnBuild = true }) + .GetRequiredService() + .Run(options); + } - services.AddSingleton(); + Console.ReadLine(); + } - services.AddSingleton(x => new(options.Value.Pid)); - services.AddSingleton(); - services.AddSingleton(x => DataConfig.Load(x.GetRequiredService().Path)); - services.AddSingleton(); - services.AddSingleton(); - services.AddSingleton(); - services.AddSingleton(); + private static bool ConfigureServices(IServiceCollection services, ParserResult options) + { + if (!Validate(options.Value.Pid)) + return false; - services.AddSingleton(); + services.AddSingleton(); - services.AddSingleton(); - services.AddSingleton(); - services.AddSingleton(x => - GetPather(x.GetRequiredService(), - x.GetRequiredService(), x.GetRequiredService(), options)); + services.AddSingleton(); - services.AddSingleton(x => new(false)); - services.AddSingleton(x => FrameConfig.LoadFrames()); - services.AddSingleton(); + services.AddSingleton(x => new(options.Value.Pid)); + services.AddSingleton(); + services.AddSingleton(x => DataConfig.Load(x.GetRequiredService().Path)); + services.AddSingleton(); + services.AddSingleton(); + services.AddSingleton(); + services.AddSingleton(); - if (options.Value.Reader == AddonDataProviderType.DXGI) - { - services.AddSingleton(); - Log.Information($"[{nameof(Program)}] {nameof(AddonDataProviderDXGI)}"); - } - else - { - services.AddSingleton(); - Log.Information($"[{nameof(Program)}] {nameof(AddonDataProviderGDI)}"); - } + services.AddSingleton(); - services.AddSingleton(); - services.AddSingleton(); - services.AddSingleton(); - services.AddSingleton(); - services.AddSingleton(); + services.AddSingleton(); + services.AddSingleton(); + services.AddSingleton(x => + GetPather(x.GetRequiredService(), + x.GetRequiredService(), x.GetRequiredService(), options)); - services.AddSingleton(); - services.AddSingleton(); + services.AddSingleton(x => new(false)); + services.AddSingleton(x => FrameConfig.LoadFrames()); + services.AddSingleton(); - return true; + if (options.Value.Reader == AddonDataProviderType.DXGI) + { + services.AddSingleton(); + Log.Information($"[{nameof(Program)}] {nameof(AddonDataProviderDXGI)}"); } - - private static IPPather GetPather(Microsoft.Extensions.Logging.ILogger logger, DataConfig dataConfig, WorldMapAreaDB worldMapAreaDB, ParserResult options) + else { - StartupConfigPathing scp = new(options.Value.Mode.ToString()!, - options.Value.Hostv1!, options.Value.Portv1, - options.Value.Hostv3!, options.Value.Portv3); + services.AddSingleton(); + Log.Information($"[{nameof(Program)}] {nameof(AddonDataProviderGDI)}"); + } + + services.AddSingleton(); + services.AddSingleton(); + services.AddSingleton(); + services.AddSingleton(); + services.AddSingleton(); - bool failed = false; + services.AddSingleton(); + services.AddSingleton(); + + return true; + } + + private static IPPather GetPather(Microsoft.Extensions.Logging.ILogger logger, DataConfig dataConfig, WorldMapAreaDB worldMapAreaDB, ParserResult options) + { + StartupConfigPathing scp = new(options.Value.Mode.ToString()!, + options.Value.Hostv1!, options.Value.Portv1, + options.Value.Hostv3!, options.Value.Portv3); - if (scp.Type == StartupConfigPathing.Types.RemoteV3) + bool failed = false; + + if (scp.Type == StartupConfigPathing.Types.RemoteV3) + { + RemotePathingAPIV3 api = new(logger, scp.hostv3, scp.portv3, worldMapAreaDB); + if (api.PingServer()) { - RemotePathingAPIV3 api = new(logger, scp.hostv3, scp.portv3, worldMapAreaDB); - if (api.PingServer()) - { - Log.Information($"[{nameof(Program)}] Using {StartupConfigPathing.Types.RemoteV3}({api.GetType().Name}) {scp.hostv3}:{scp.portv3}"); - return api; - } - api.Dispose(); - failed = true; + Log.Information($"[{nameof(Program)}] Using {StartupConfigPathing.Types.RemoteV3}({api.GetType().Name}) {scp.hostv3}:{scp.portv3}"); + return api; } + api.Dispose(); + failed = true; + } - if (scp.Type == StartupConfigPathing.Types.RemoteV1 || failed) + if (scp.Type == StartupConfigPathing.Types.RemoteV1 || failed) + { + RemotePathingAPI api = new(logger, scp.hostv1, scp.portv1); + Task pingTask = Task.Run(api.PingServer); + pingTask.Wait(); + if (pingTask.Result) { - RemotePathingAPI api = new(logger, scp.hostv1, scp.portv1); - Task pingTask = Task.Run(api.PingServer); - pingTask.Wait(); - if (pingTask.Result) + if (scp.Type == StartupConfigPathing.Types.RemoteV3) { - if (scp.Type == StartupConfigPathing.Types.RemoteV3) - { - Log.Warning($"[{nameof(Program)}] Unavailable {StartupConfigPathing.Types.RemoteV3} {scp.hostv3}:{scp.portv3} - Fallback to {StartupConfigPathing.Types.RemoteV1}"); - } - - Log.Information($"[{nameof(Program)}] Using {StartupConfigPathing.Types.RemoteV1}({api.GetType().Name}) {scp.hostv1}:{scp.portv1}"); - return api; + Log.Warning($"[{nameof(Program)}] Unavailable {StartupConfigPathing.Types.RemoteV3} {scp.hostv3}:{scp.portv3} - Fallback to {StartupConfigPathing.Types.RemoteV1}"); } - failed = true; + Log.Information($"[{nameof(Program)}] Using {StartupConfigPathing.Types.RemoteV1}({api.GetType().Name}) {scp.hostv1}:{scp.portv1}"); + return api; } - if (scp.Type != StartupConfigPathing.Types.Local) - { - Log.Warning($"[{nameof(Program)}] {scp.Type} not available!"); - } - - LocalPathingApi localApi = new(logger, new PPatherService(logger, dataConfig, worldMapAreaDB), dataConfig); - Log.Information($"[{nameof(Program)}] Using {StartupConfigPathing.Types.Local}({localApi.GetType().Name})"); - return localApi; + failed = true; } - private static bool Validate(int pid) + if (scp.Type != StartupConfigPathing.Types.Local) { - WowProcess wowProcess = new(pid); - Log.Information($"[{nameof(Program)}] Pid: {wowProcess.ProcessId}"); - Log.Information($"[{nameof(Program)}] Version: {wowProcess.FileVersion}"); - NativeMethods.GetWindowRect(wowProcess.Process.MainWindowHandle, out Rectangle rect); + Log.Warning($"[{nameof(Program)}] {scp.Type} not available!"); + } - var logger = new SerilogLoggerProvider(Log.Logger, true).CreateLogger(nameof(AddonConfigurator)); - AddonConfigurator addonConfigurator = new(logger, wowProcess); - Version? installVersion = addonConfigurator.GetInstallVersion(); - Log.Information($"[{nameof(Program)}] Addon version: {installVersion}"); + LocalPathingApi localApi = new(logger, new PPatherService(logger, dataConfig, worldMapAreaDB), dataConfig); + Log.Information($"[{nameof(Program)}] Using {StartupConfigPathing.Types.Local}({localApi.GetType().Name})"); + return localApi; + } - bool valid = true; + private static bool Validate(int pid) + { + WowProcess wowProcess = new(pid); + Log.Information($"[{nameof(Program)}] Pid: {wowProcess.ProcessId}"); + Log.Information($"[{nameof(Program)}] Version: {wowProcess.FileVersion}"); + NativeMethods.GetWindowRect(wowProcess.Process.MainWindowHandle, out Rectangle rect); - if (addonConfigurator.IsDefault() || installVersion == null) - { - // At this point the webpage never loads so fallback to configuration page - addonConfigurator.Delete(); - FrameConfig.Delete(); - valid = false; + var logger = new SerilogLoggerProvider(Log.Logger, true).CreateLogger(nameof(AddonConfigurator)); + AddonConfigurator addonConfigurator = new(logger, wowProcess); + Version? installVersion = addonConfigurator.GetInstallVersion(); + Log.Information($"[{nameof(Program)}] Addon version: {installVersion}"); - Log.Error($"[{nameof(Program)}] {nameof(AddonConfig)} doesn't exists or addon not installed yet!"); - } + bool valid = true; - if (FrameConfig.Exists() && !FrameConfig.IsValid(rect, installVersion!)) - { - // At this point the webpage never loads so fallback to configuration page - FrameConfig.Delete(); - valid = false; + if (addonConfigurator.IsDefault() || installVersion == null) + { + // At this point the webpage never loads so fallback to configuration page + addonConfigurator.Delete(); + FrameConfig.Delete(); + valid = false; - Log.Error($"[{nameof(Program)}] {nameof(FrameConfig)} doesn't exists or window rect is different then config!"); - } + Log.Error($"[{nameof(Program)}] {nameof(AddonConfig)} doesn't exists or addon not installed yet!"); + } - wowProcess.Dispose(); + if (FrameConfig.Exists() && !FrameConfig.IsValid(rect, installVersion!)) + { + // At this point the webpage never loads so fallback to configuration page + FrameConfig.Delete(); + valid = false; - return valid; + Log.Error($"[{nameof(Program)}] {nameof(FrameConfig)} doesn't exists or window rect is different then config!"); } + + wowProcess.Dispose(); + + return valid; } } \ No newline at end of file diff --git a/HeadlessServer/RunOptions.cs b/HeadlessServer/RunOptions.cs index aebb89caa..7da69895b 100644 --- a/HeadlessServer/RunOptions.cs +++ b/HeadlessServer/RunOptions.cs @@ -1,59 +1,58 @@ using CommandLine; using Core; -namespace HeadlessServer +namespace HeadlessServer; + +public sealed class RunOptions { - public sealed class RunOptions - { - [Value(0, - MetaName = "ClassConfig file", - Required = true, - HelpText = "ClassConfiguration file found in 'Json\\class\\'\nexample: Warrior_1.json")] - public string? ClassConfig { get; set; } - - [Option('m', - "mode", - Required = false, - Default = StartupConfigPathing.Types.Local, - HelpText = $"Navigation services: \n{nameof(StartupConfigPathing.Types.Local)}\n{nameof(StartupConfigPathing.Types.RemoteV1)}\n{nameof(StartupConfigPathing.Types.RemoteV3)}")] - public StartupConfigPathing.Types Mode { get; set; } - - [Option('p', - "pid", - Required = false, - Default = -1, - HelpText = $"World of Warcraft Process Id")] - public int Pid { get; set; } - - [Option('r', - "reader", - Required = false, - Default = AddonDataProviderType.GDI, - HelpText = $"Screen reader backend.\n'{nameof(AddonDataProviderType.GDI)}': is the default, compatible from Win7.\n'{nameof(AddonDataProviderType.DXGI)}': DirectX based works from Win8.")] - public AddonDataProviderType Reader { get; set; } - - [Option("hostv1", - Required = false, - Default = "localhost", - HelpText = $"Navigation Remote V1 host")] - public string? Hostv1 { get; set; } - - [Option("portv1", - Required = false, - Default = 5001, - HelpText = $"Navigation Remote V1 port")] - public int Portv1 { get; set; } - - [Option("hostv3", - Required = false, - Default = "127.0.0.1", - HelpText = $"Navigation Remote V3 host")] - public string? Hostv3 { get; set; } - - [Option("portv3", - Required = false, - Default = 47111, - HelpText = $"Navigation Remote V3 port")] - public int Portv3 { get; set; } - } + [Value(0, + MetaName = "ClassConfig file", + Required = true, + HelpText = "ClassConfiguration file found in 'Json\\class\\'\nexample: Warrior_1.json")] + public string? ClassConfig { get; set; } + + [Option('m', + "mode", + Required = false, + Default = StartupConfigPathing.Types.Local, + HelpText = $"Navigation services: \n{nameof(StartupConfigPathing.Types.Local)}\n{nameof(StartupConfigPathing.Types.RemoteV1)}\n{nameof(StartupConfigPathing.Types.RemoteV3)}")] + public StartupConfigPathing.Types Mode { get; set; } + + [Option('p', + "pid", + Required = false, + Default = -1, + HelpText = $"World of Warcraft Process Id")] + public int Pid { get; set; } + + [Option('r', + "reader", + Required = false, + Default = AddonDataProviderType.GDI, + HelpText = $"Screen reader backend.\n'{nameof(AddonDataProviderType.GDI)}': is the default, compatible from Win7.\n'{nameof(AddonDataProviderType.DXGI)}': DirectX based works from Win8.")] + public AddonDataProviderType Reader { get; set; } + + [Option("hostv1", + Required = false, + Default = "localhost", + HelpText = $"Navigation Remote V1 host")] + public string? Hostv1 { get; set; } + + [Option("portv1", + Required = false, + Default = 5001, + HelpText = $"Navigation Remote V1 port")] + public int Portv1 { get; set; } + + [Option("hostv3", + Required = false, + Default = "127.0.0.1", + HelpText = $"Navigation Remote V3 host")] + public string? Hostv3 { get; set; } + + [Option("portv3", + Required = false, + Default = 47111, + HelpText = $"Navigation Remote V3 port")] + public int Portv3 { get; set; } } diff --git a/PPather/Data/LineArgs.cs b/PPather/Data/LineArgs.cs index c6a132e9d..ade5cd774 100644 --- a/PPather/Data/LineArgs.cs +++ b/PPather/Data/LineArgs.cs @@ -1,20 +1,19 @@ using System.Numerics; -namespace PPather.Data +namespace PPather.Data; + +public sealed class LineArgs { - public sealed class LineArgs - { - public string Name { get; set; } - public Vector3[] Spots { get; set; } - public int Colour { get; set; } - public int MapId { get; set; } + public string Name { get; set; } + public Vector3[] Spots { get; set; } + public int Colour { get; set; } + public int MapId { get; set; } - public LineArgs(string name, Vector3[] spots, int colour, int mapId) - { - Name = name; - Spots = spots; - Colour = colour; - MapId = mapId; - } + public LineArgs(string name, Vector3[] spots, int colour, int mapId) + { + Name = name; + Spots = spots; + Colour = colour; + MapId = mapId; } } \ No newline at end of file diff --git a/PPather/Data/LinesEventArgs.cs b/PPather/Data/LinesEventArgs.cs index ddb18c3eb..d54a67786 100644 --- a/PPather/Data/LinesEventArgs.cs +++ b/PPather/Data/LinesEventArgs.cs @@ -1,18 +1,17 @@ using System.Numerics; -namespace PPather.Data +namespace PPather.Data; + +public sealed class LinesEventArgs { - public sealed class LinesEventArgs - { - public Vector4[] Locations { get; set; } - public int Colour { get; set; } - public string Name { get; set; } + public Vector4[] Locations { get; set; } + public int Colour { get; set; } + public string Name { get; set; } - public LinesEventArgs(string name, Vector4[] locations, int colour) - { - this.Name = name; - this.Locations = locations; - this.Colour = colour; - } + public LinesEventArgs(string name, Vector4[] locations, int colour) + { + this.Name = name; + this.Locations = locations; + this.Colour = colour; } } \ No newline at end of file diff --git a/PPather/Data/SphereArgs.cs b/PPather/Data/SphereArgs.cs index e1ebf023c..71f9192e7 100644 --- a/PPather/Data/SphereArgs.cs +++ b/PPather/Data/SphereArgs.cs @@ -1,20 +1,19 @@ using System.Numerics; -namespace PPather.Data +namespace PPather.Data; + +public sealed class SphereArgs { - public sealed class SphereArgs - { - public string Name { get; set; } - public Vector3 Spot { get; set; } - public int Colour { get; set; } - public int MapId { get; set; } + public string Name { get; set; } + public Vector3 Spot { get; set; } + public int Colour { get; set; } + public int MapId { get; set; } - public SphereArgs(string name, Vector3 spot, int colour, int mapId) - { - Name = name; - Spot = spot; - Colour = colour; - MapId = mapId; - } + public SphereArgs(string name, Vector3 spot, int colour, int mapId) + { + Name = name; + Spot = spot; + Colour = colour; + MapId = mapId; } } \ No newline at end of file diff --git a/PPather/Data/SphereEventArgs.cs b/PPather/Data/SphereEventArgs.cs index db36d46ca..b54b8ffb5 100644 --- a/PPather/Data/SphereEventArgs.cs +++ b/PPather/Data/SphereEventArgs.cs @@ -1,18 +1,17 @@ using System.Numerics; -namespace PPather.Data +namespace PPather.Data; + +public sealed class SphereEventArgs { - public sealed class SphereEventArgs - { - public Vector4 Location { get; set; } - public int Colour { get; set; } - public string Name { get; set; } + public Vector4 Location { get; set; } + public int Colour { get; set; } + public string Name { get; set; } - public SphereEventArgs(string name, Vector4 location, int colour) - { - this.Name = name; - this.Location = location; - this.Colour = colour; - } + public SphereEventArgs(string name, Vector4 location, int colour) + { + this.Name = name; + this.Location = location; + this.Colour = colour; } } \ No newline at end of file diff --git a/PPather/Graph/GraphChunk.cs b/PPather/Graph/GraphChunk.cs index 32a29f7d9..4963ce261 100644 --- a/PPather/Graph/GraphChunk.cs +++ b/PPather/Graph/GraphChunk.cs @@ -23,249 +23,248 @@ You should have received a copy of the GNU Lesser General Public License using System.Diagnostics; using System.Runtime.InteropServices; -namespace PPather.Graph +namespace PPather.Graph; + +public sealed class GraphChunk { - public sealed class GraphChunk + public const int CHUNK_SIZE = 256; + public const int SIZE = CHUNK_SIZE * CHUNK_SIZE; + private const bool saveEnabled = true; + + private const uint FILE_MAGIC = 0x23452345; + private const uint FILE_ENDMAGIC = 0x54325432; + private const uint SPOT_MAGIC = 0x53504f54; + + private readonly ILogger logger; + private readonly float base_x, base_y; + private readonly string filePath; + private readonly Spot[] spots; + + public readonly int ix, iy; + public bool modified; + public long LRU; + + // Per spot: + // uint32 magic + // uint32 reserved; + // uint32 flags; + // float x; + // float y; + // float z; + // uint32 no_paths + // for each path + // float x; + // float y; + // float z; + + public GraphChunk(float base_x, float base_y, int ix, int iy, ILogger logger, string baseDir, long lru) { - public const int CHUNK_SIZE = 256; - public const int SIZE = CHUNK_SIZE * CHUNK_SIZE; - private const bool saveEnabled = true; - - private const uint FILE_MAGIC = 0x23452345; - private const uint FILE_ENDMAGIC = 0x54325432; - private const uint SPOT_MAGIC = 0x53504f54; - - private readonly ILogger logger; - private readonly float base_x, base_y; - private readonly string filePath; - private readonly Spot[] spots; - - public readonly int ix, iy; - public bool modified; - public long LRU; - - // Per spot: - // uint32 magic - // uint32 reserved; - // uint32 flags; - // float x; - // float y; - // float z; - // uint32 no_paths - // for each path - // float x; - // float y; - // float z; - - public GraphChunk(float base_x, float base_y, int ix, int iy, ILogger logger, string baseDir, long lru) - { - this.logger = logger; - this.base_x = base_x; - this.base_y = base_y; + this.logger = logger; + this.base_x = base_x; + this.base_y = base_y; - this.ix = ix; - this.iy = iy; + this.ix = ix; + this.iy = iy; - LRU = lru; + LRU = lru; - spots = new Spot[SIZE]; + spots = new Spot[SIZE]; - filePath = System.IO.Path.Join(baseDir, string.Format("c_{0,3:000}_{1,3:000}.bin", ix, iy)); - } - - public void Clear() - { - for (int i = 0; i < SIZE; i++) - { - spots[i]?.Clear(); - } - } + filePath = System.IO.Path.Join(baseDir, string.Format("c_{0,3:000}_{1,3:000}.bin", ix, iy)); + } - private void LocalCoords(float x, float y, out int ix, out int iy) + public void Clear() + { + for (int i = 0; i < SIZE; i++) { - ix = (int)(x - base_x); - iy = (int)(y - base_y); + spots[i]?.Clear(); } + } - public Spot GetSpot2D(float x, float y) - { - LocalCoords(x, y, out int ix, out int iy); - return spots[Index(ix, iy)]; - } + private void LocalCoords(float x, float y, out int ix, out int iy) + { + ix = (int)(x - base_x); + iy = (int)(y - base_y); + } - public Spot GetSpot(float x, float y, float z) - { - Spot s = GetSpot2D(x, y); + public Spot GetSpot2D(float x, float y) + { + LocalCoords(x, y, out int ix, out int iy); + return spots[Index(ix, iy)]; + } - while (s != null && !s.IsCloseZ(z)) - { - s = s.next; - } + public Spot GetSpot(float x, float y, float z) + { + Spot s = GetSpot2D(x, y); - return s; + while (s != null && !s.IsCloseZ(z)) + { + s = s.next; } - // return old spot at conflicting poision - // or the same as passed the function if all was ok - public Spot AddSpot(Spot s) - { - Spot old = GetSpot(s.Loc.X, s.Loc.Y, s.Loc.Z); - if (old != null) - return old; + return s; + } - s.chunk = this; + // return old spot at conflicting poision + // or the same as passed the function if all was ok + public Spot AddSpot(Spot s) + { + Spot old = GetSpot(s.Loc.X, s.Loc.Y, s.Loc.Z); + if (old != null) + return old; - LocalCoords(s.Loc.X, s.Loc.Y, out int x, out int y); + s.chunk = this; - int i = Index(x, y); - s.next = spots[i]; - spots[i] = s; - modified = true; - return s; - } + LocalCoords(s.Loc.X, s.Loc.Y, out int x, out int y); + + int i = Index(x, y); + s.next = spots[i]; + spots[i] = s; + modified = true; + return s; + } - public List GetAllSpots() + public List GetAllSpots() + { + List l = new(); + for (int i = 0; i < SIZE; i++) { - List l = new(); - for (int i = 0; i < SIZE; i++) + Spot s = spots[i]; + while (s != null) { - Spot s = spots[i]; - while (s != null) - { - l.Add(s); - s = s.next; - } + l.Add(s); + s = s.next; } - - return l; } - private static int Index(int x, int y) + return l; + } + + private static int Index(int x, int y) + { + return (y * CHUNK_SIZE) + x; + } + + public bool Load() + { + if (!File.Exists(filePath)) { - return (y * CHUNK_SIZE) + x; + return false; } - public bool Load() + try { - if (!File.Exists(filePath)) + long timestamp = Stopwatch.GetTimestamp(); + + using Stream stream = File.OpenRead(filePath); + using BinaryReader br = new(stream); + + if (br.ReadUInt32() != FILE_MAGIC) { + br.Close(); + stream.Close(); + + File.Delete(filePath); + logger.LogWarning($"[{nameof(GraphChunk)}] {nameof(FILE_MAGIC)} mismatch! Delete '{filePath}'!"); + return false; } - try + int n_spots = 0; + int n_steps = 0; + while (br.ReadUInt32() != FILE_ENDMAGIC) { - long timestamp = Stopwatch.GetTimestamp(); - - using Stream stream = File.OpenRead(filePath); - using BinaryReader br = new(stream); - - if (br.ReadUInt32() != FILE_MAGIC) + n_spots++; + uint reserved = br.ReadUInt32(); + uint flags = br.ReadUInt32(); + float x = br.ReadSingle(); + float y = br.ReadSingle(); + float z = br.ReadSingle(); + uint n_paths = br.ReadUInt32(); + + if (x == 0 || y == 0) { - br.Close(); - stream.Close(); - - File.Delete(filePath); - logger.LogWarning($"[{nameof(GraphChunk)}] {nameof(FILE_MAGIC)} mismatch! Delete '{filePath}'!"); - - return false; + continue; } - int n_spots = 0; - int n_steps = 0; - while (br.ReadUInt32() != FILE_ENDMAGIC) + Spot s = new(x, y, z) { - n_spots++; - uint reserved = br.ReadUInt32(); - uint flags = br.ReadUInt32(); - float x = br.ReadSingle(); - float y = br.ReadSingle(); - float z = br.ReadSingle(); - uint n_paths = br.ReadUInt32(); - - if (x == 0 || y == 0) - { - continue; - } - - Spot s = new(x, y, z) - { - flags = flags - }; - - for (uint i = 0; i < n_paths; i++) - { - n_steps++; - s.AddPathTo(br.ReadSingle(), br.ReadSingle(), br.ReadSingle()); - } - _ = AddSpot(s); - - // After loading a Chunk mark it unmodified - modified = false; - } + flags = flags + }; - if (logger.IsEnabled(LogLevel.Trace)) - logger.LogTrace($"[{nameof(GraphChunk)}] Loaded {filePath} {n_spots} spots {n_steps} steps {Stopwatch.GetElapsedTime(timestamp).TotalMilliseconds} ms"); + for (uint i = 0; i < n_paths; i++) + { + n_steps++; + s.AddPathTo(br.ReadSingle(), br.ReadSingle(), br.ReadSingle()); + } + _ = AddSpot(s); - return true; - } - catch (Exception e) - { - logger.LogError(e.Message); + // After loading a Chunk mark it unmodified + modified = false; } - modified = false; - return false; - } + if (logger.IsEnabled(LogLevel.Trace)) + logger.LogTrace($"[{nameof(GraphChunk)}] Loaded {filePath} {n_spots} spots {n_steps} steps {Stopwatch.GetElapsedTime(timestamp).TotalMilliseconds} ms"); - public void Save() + return true; + } + catch (Exception e) { - if (!saveEnabled || !modified) - return; + logger.LogError(e.Message); + } - try + modified = false; + return false; + } + + public void Save() + { + if (!saveEnabled || !modified) + return; + + try + { + using Stream stream = File.Create(filePath); + using BinaryWriter bw = new(stream); + bw.Write(FILE_MAGIC); + + int n_spots = 0; + int n_steps = 0; + var span = CollectionsMarshal.AsSpan(GetAllSpots()); + for (int j = 0; j < span.Length; j++) { - using Stream stream = File.Create(filePath); - using BinaryWriter bw = new(stream); - bw.Write(FILE_MAGIC); - - int n_spots = 0; - int n_steps = 0; - var span = CollectionsMarshal.AsSpan(GetAllSpots()); - for (int j = 0; j < span.Length; j++) + Spot s = span[j]; + + bw.Write(SPOT_MAGIC); + bw.Write((uint)0); // reserved + bw.Write(s.flags); + bw.Write(s.Loc.X); + bw.Write(s.Loc.Y); + bw.Write(s.Loc.Z); + uint n_paths = (uint)s.n_paths; + bw.Write(n_paths); + for (uint i = 0; i < n_paths; i++) { - Spot s = span[j]; - - bw.Write(SPOT_MAGIC); - bw.Write((uint)0); // reserved - bw.Write(s.flags); - bw.Write(s.Loc.X); - bw.Write(s.Loc.Y); - bw.Write(s.Loc.Z); - uint n_paths = (uint)s.n_paths; - bw.Write(n_paths); - for (uint i = 0; i < n_paths; i++) - { - uint off = i * 3; - bw.Write(s.paths[off]); - bw.Write(s.paths[off + 1]); - bw.Write(s.paths[off + 2]); - n_steps++; - } - n_spots++; + uint off = i * 3; + bw.Write(s.paths[off]); + bw.Write(s.paths[off + 1]); + bw.Write(s.paths[off + 2]); + n_steps++; } - bw.Write(FILE_ENDMAGIC); + n_spots++; + } + bw.Write(FILE_ENDMAGIC); - bw.Close(); - stream.Close(); - modified = false; + bw.Close(); + stream.Close(); + modified = false; - if (logger.IsEnabled(LogLevel.Trace)) - logger.LogTrace($"[{nameof(GraphChunk)}] Saved {filePath} {n_spots} spots {n_steps} steps"); - } - catch (Exception e) - { - logger.LogError($"[{nameof(GraphChunk)}] Save failed " + e); - } + if (logger.IsEnabled(LogLevel.Trace)) + logger.LogTrace($"[{nameof(GraphChunk)}] Saved {filePath} {n_spots} spots {n_steps} steps"); + } + catch (Exception e) + { + logger.LogError($"[{nameof(GraphChunk)}] Save failed " + e); } } } \ No newline at end of file diff --git a/PPather/Graph/ILocationHeuristics.cs b/PPather/Graph/ILocationHeuristics.cs index f23d7ffd3..579aef3db 100644 --- a/PPather/Graph/ILocationHeuristics.cs +++ b/PPather/Graph/ILocationHeuristics.cs @@ -16,10 +16,9 @@ You should have received a copy of the GNU Lesser General Public License */ -namespace PPather.Graph +namespace PPather.Graph; + +public interface ILocationHeuristics { - public interface ILocationHeuristics - { - float Score(float x, float y, float z); - } + float Score(float x, float y, float z); } \ No newline at end of file diff --git a/PPather/Graph/Path.cs b/PPather/Graph/Path.cs index da4e6ae72..5b750de2a 100644 --- a/PPather/Graph/Path.cs +++ b/PPather/Graph/Path.cs @@ -19,25 +19,24 @@ You should have received a copy of the GNU Lesser General Public License using System.Collections.Generic; using System.Numerics; -namespace PPather.Graph +namespace PPather.Graph; + +public sealed class Path { - public sealed class Path - { - public List locations { get; } = new(); + public List locations { get; } = new(); - public Vector3 GetLast => locations[^1]; + public Vector3 GetLast => locations[^1]; - public Path(List steps) + public Path(List steps) + { + foreach (Spot s in steps) { - foreach (Spot s in steps) - { - Add(s.Loc); - } + Add(s.Loc); } + } - public void Add(Vector3 l) - { - locations.Add(l); - } + public void Add(Vector3 l) + { + locations.Add(l); } } \ No newline at end of file diff --git a/PPather/Graph/PathGraph.cs b/PPather/Graph/PathGraph.cs index 5cd0e7e27..03ef460b8 100644 --- a/PPather/Graph/PathGraph.cs +++ b/PPather/Graph/PathGraph.cs @@ -32,925 +32,924 @@ You should have received a copy of the GNU Lesser General Public License #pragma warning disable 162 -namespace PPather.Graph +namespace PPather.Graph; + +public sealed class PathGraph { - public sealed class PathGraph - { - public static bool SearchEnabled; + public static bool SearchEnabled; - public enum eSearchScoreSpot - { - OriginalPather, - A_Star, - A_Star_With_Model_Avoidance, - } + public enum eSearchScoreSpot + { + OriginalPather, + A_Star, + A_Star_With_Model_Avoidance, + } - public const int gradiantMax = 5; + public const int gradiantMax = 5; - public eSearchScoreSpot searchScoreSpot = eSearchScoreSpot.A_Star_With_Model_Avoidance; - public const int sleepMSBetweenSpots = 0; + public eSearchScoreSpot searchScoreSpot = eSearchScoreSpot.A_Star_With_Model_Avoidance; + public const int sleepMSBetweenSpots = 0; - public const float toonHeight = 2.0f; - public const float toonSize = 0.5f; + public const float toonHeight = 2.0f; + public const float toonSize = 0.5f; - public const float MinStepLength = 2f; - public const float WantedStepLength = 3f; - public const float MaxStepLength = 5f; + public const float MinStepLength = 2f; + public const float WantedStepLength = 3f; + public const float MaxStepLength = 5f; - public const float STEP_D = 0.1f; + public const float STEP_D = 0.1f; - public const float IsCloseToModelRange = 2; + public const float IsCloseToModelRange = 2; - /* + /* public const float IndoorsWantedStepLength = 1.5f; public const float IndoorsMaxStepLength = 2.5f; */ - public const float CHUNK_BASE = 100000.0f; // Always keep positive - public const float MaximumAllowedRangeFromTarget = 40; + public const float CHUNK_BASE = 100000.0f; // Always keep positive + public const float MaximumAllowedRangeFromTarget = 40; - private readonly string chunkDir; + private readonly string chunkDir; - private readonly float MapId; - private readonly SparseMatrix2D chunks; - public readonly ChunkedTriangleCollection triangleWorld; + private readonly float MapId; + private readonly SparseMatrix2D chunks; + public readonly ChunkedTriangleCollection triangleWorld; - private const int maxCache = 512; - private long LRU; + private const int maxCache = 512; + private long LRU; - public int GetTriangleClosenessScore(Vector3 loc) + public int GetTriangleClosenessScore(Vector3 loc) + { + if (!triangleWorld.IsCloseToModel(loc.X, loc.Y, loc.Z, 3)) { - if (!triangleWorld.IsCloseToModel(loc.X, loc.Y, loc.Z, 3)) - { - return 0; - } + return 0; + } - if (!triangleWorld.IsCloseToModel(loc.X, loc.Y, loc.Z, 2)) - { - return 8; - } + if (!triangleWorld.IsCloseToModel(loc.X, loc.Y, loc.Z, 2)) + { + return 8; + } - if (!triangleWorld.IsCloseToModel(loc.X, loc.Y, loc.Z, 1)) - { - return 64; - } + if (!triangleWorld.IsCloseToModel(loc.X, loc.Y, loc.Z, 1)) + { + return 64; + } + + return 128; + } + public int GetTriangleGradiantScore(Vector3 loc, int gradiantMax) + { + if (triangleWorld.GradiantScore(loc.X, loc.Y, loc.Z, 1) > gradiantMax) + { return 128; } - public int GetTriangleGradiantScore(Vector3 loc, int gradiantMax) + if (triangleWorld.GradiantScore(loc.X, loc.Y, loc.Z, 2) > gradiantMax) { - if (triangleWorld.GradiantScore(loc.X, loc.Y, loc.Z, 1) > gradiantMax) - { - return 128; - } - - if (triangleWorld.GradiantScore(loc.X, loc.Y, loc.Z, 2) > gradiantMax) - { - return 64; - } - - if (triangleWorld.GradiantScore(loc.X, loc.Y, loc.Z, 3) > gradiantMax) - { - return 8; - } + return 64; + } - return 0; + if (triangleWorld.GradiantScore(loc.X, loc.Y, loc.Z, 3) > gradiantMax) + { + return 8; } - public static int TimeoutSeconds = 20; - public static int ProgressTimeoutSeconds = 10; + return 0; + } - private readonly ILogger logger; + public static int TimeoutSeconds = 20; + public static int ProgressTimeoutSeconds = 10; - public PathGraph(float mapId, - ChunkedTriangleCollection triangles, - ILogger logger, DataConfig dataConfig) - { - this.logger = logger; - this.MapId = mapId; - this.triangleWorld = triangles; + private readonly ILogger logger; - chunkDir = System.IO.Path.Join(dataConfig.PathInfo, ContinentDB.IdToName[MapId]); - if (!Directory.Exists(chunkDir)) - Directory.CreateDirectory(chunkDir); + public PathGraph(float mapId, + ChunkedTriangleCollection triangles, + ILogger logger, DataConfig dataConfig) + { + this.logger = logger; + this.MapId = mapId; + this.triangleWorld = triangles; - chunks = new SparseMatrix2D(8); - } + chunkDir = System.IO.Path.Join(dataConfig.PathInfo, ContinentDB.IdToName[MapId]); + if (!Directory.Exists(chunkDir)) + Directory.CreateDirectory(chunkDir); - public void Clear() - { - triangleWorld.Close(); + chunks = new SparseMatrix2D(8); + } - foreach (GraphChunk chunk in chunks.GetAllElements()) - { - chunk.Clear(); - } - chunks.Clear(); - } + public void Clear() + { + triangleWorld.Close(); - private static void GetChunkCoord(float x, float y, out int ix, out int iy) + foreach (GraphChunk chunk in chunks.GetAllElements()) { - ix = (int)((CHUNK_BASE + x) / GraphChunk.CHUNK_SIZE); - iy = (int)((CHUNK_BASE + y) / GraphChunk.CHUNK_SIZE); + chunk.Clear(); } + chunks.Clear(); + } - private static void GetChunkBase(int ix, int iy, out float bx, out float by) - { - bx = (float)ix * GraphChunk.CHUNK_SIZE - CHUNK_BASE; - by = (float)iy * GraphChunk.CHUNK_SIZE - CHUNK_BASE; - } + private static void GetChunkCoord(float x, float y, out int ix, out int iy) + { + ix = (int)((CHUNK_BASE + x) / GraphChunk.CHUNK_SIZE); + iy = (int)((CHUNK_BASE + y) / GraphChunk.CHUNK_SIZE); + } - private GraphChunk GetChunkAt(float x, float y) - { - GetChunkCoord(x, y, out int ix, out int iy); - if (chunks.TryGetValue(ix, iy, out GraphChunk c)) - c.LRU = LRU++; + private static void GetChunkBase(int ix, int iy, out float bx, out float by) + { + bx = (float)ix * GraphChunk.CHUNK_SIZE - CHUNK_BASE; + by = (float)iy * GraphChunk.CHUNK_SIZE - CHUNK_BASE; + } - return c ?? default; - } + private GraphChunk GetChunkAt(float x, float y) + { + GetChunkCoord(x, y, out int ix, out int iy); + if (chunks.TryGetValue(ix, iy, out GraphChunk c)) + c.LRU = LRU++; - private void CheckForChunkEvict() - { - if (chunks.Count < maxCache) - return; + return c ?? default; + } - //lock (chunks) + private void CheckForChunkEvict() + { + if (chunks.Count < maxCache) + return; + + //lock (chunks) + { + GraphChunk evict = null; + foreach (GraphChunk gc in chunks.GetAllElements()) { - GraphChunk evict = null; - foreach (GraphChunk gc in chunks.GetAllElements()) + if (evict == null || gc.LRU < evict.LRU) { - if (evict == null || gc.LRU < evict.LRU) - { - evict = gc; - } + evict = gc; } - - evict.Save(); - chunks.Remove(evict.ix, evict.iy); - evict.Clear(); } + + evict.Save(); + chunks.Remove(evict.ix, evict.iy); + evict.Clear(); } + } - public void Save() + public void Save() + { + lock (chunks) { - lock (chunks) + long timestamp = Stopwatch.GetTimestamp(); + foreach (GraphChunk gc in chunks.GetAllElements()) { - long timestamp = Stopwatch.GetTimestamp(); - foreach (GraphChunk gc in chunks.GetAllElements()) + if (gc.modified) { - if (gc.modified) - { - gc.Save(); - } + gc.Save(); } - - if (logger.IsEnabled(LogLevel.Trace)) - logger.LogTrace($"Saved GraphChunks {Stopwatch.GetElapsedTime(timestamp).TotalMilliseconds} ms"); } + + if (logger.IsEnabled(LogLevel.Trace)) + logger.LogTrace($"Saved GraphChunks {Stopwatch.GetElapsedTime(timestamp).TotalMilliseconds} ms"); } + } - // Create and load from file if exisiting - private void LoadChunk(float x, float y) - { - GraphChunk gc = GetChunkAt(x, y); - if (gc != null) - return; + // Create and load from file if exisiting + private void LoadChunk(float x, float y) + { + GraphChunk gc = GetChunkAt(x, y); + if (gc != null) + return; - GetChunkCoord(x, y, out int ix, out int iy); - GetChunkBase(ix, iy, out float base_x, out float base_y); + GetChunkCoord(x, y, out int ix, out int iy); + GetChunkBase(ix, iy, out float base_x, out float base_y); - gc = new GraphChunk(base_x, base_y, ix, iy, logger, chunkDir, LRU++); + gc = new GraphChunk(base_x, base_y, ix, iy, logger, chunkDir, LRU++); - //CheckForChunkEvict(); + //CheckForChunkEvict(); - gc.Load(); - chunks.Add(ix, iy, gc); - } + gc.Load(); + chunks.Add(ix, iy, gc); + } - public Spot AddSpot(Spot s) + public Spot AddSpot(Spot s) + { + LoadChunk(s.Loc.X, s.Loc.Y); + GraphChunk gc = GetChunkAt(s.Loc.X, s.Loc.Y); + return gc.AddSpot(s); + } + + // Connect according to MPQ data + public Spot AddAndConnectSpot(Spot s) + { + s = AddSpot(s); + if (s.IsFlagSet(Spot.FLAG_MPQ_MAPPED)) { - LoadChunk(s.Loc.X, s.Loc.Y); - GraphChunk gc = GetChunkAt(s.Loc.X, s.Loc.Y); - return gc.AddSpot(s); + return s; } - // Connect according to MPQ data - public Spot AddAndConnectSpot(Spot s) + Span close = FindAllSpots(s.Loc, MaxStepLength); + for (int i = 0; i < close.Length; i++) { - s = AddSpot(s); - if (s.IsFlagSet(Spot.FLAG_MPQ_MAPPED)) + Spot cs = close[i]; + if (cs.HasPathTo(this, s) && s.HasPathTo(this, cs) || cs.IsBlocked()) { - return s; } - - Span close = FindAllSpots(s.Loc, MaxStepLength); - for (int i = 0; i < close.Length; i++) + else if (!triangleWorld.IsStepBlocked(s.Loc.X, s.Loc.Y, s.Loc.Z, cs.Loc.X, cs.Loc.Y, cs.Loc.Z, toonHeight, toonSize)) { - Spot cs = close[i]; - if (cs.HasPathTo(this, s) && s.HasPathTo(this, cs) || cs.IsBlocked()) - { - } - else if (!triangleWorld.IsStepBlocked(s.Loc.X, s.Loc.Y, s.Loc.Z, cs.Loc.X, cs.Loc.Y, cs.Loc.Z, toonHeight, toonSize)) - { - float mid_x = (s.Loc.X + cs.Loc.X) / 2; - float mid_y = (s.Loc.Y + cs.Loc.Y) / 2; - float mid_z = (s.Loc.Z + cs.Loc.Z) / 2; + float mid_x = (s.Loc.X + cs.Loc.X) / 2; + float mid_y = (s.Loc.Y + cs.Loc.Y) / 2; + float mid_z = (s.Loc.Z + cs.Loc.Z) / 2; - if (triangleWorld.FindStandableAt(mid_x, mid_y, - mid_z - WantedStepLength * .75f, mid_z + WantedStepLength * .75f, - out _, out _, toonHeight, toonSize)) - { - s.AddPathTo(cs); - cs.AddPathTo(s); - } + if (triangleWorld.FindStandableAt(mid_x, mid_y, + mid_z - WantedStepLength * .75f, mid_z + WantedStepLength * .75f, + out _, out _, toonHeight, toonSize)) + { + s.AddPathTo(cs); + cs.AddPathTo(s); } } - return s; } + return s; + } - public Spot GetSpot(float x, float y, float z) - { - LoadChunk(x, y); - GraphChunk gc = GetChunkAt(x, y); - return gc.GetSpot(x, y, z); - } + public Spot GetSpot(float x, float y, float z) + { + LoadChunk(x, y); + GraphChunk gc = GetChunkAt(x, y); + return gc.GetSpot(x, y, z); + } - public Spot GetSpot2D(float x, float y) - { - LoadChunk(x, y); - GraphChunk gc = GetChunkAt(x, y); - return gc.GetSpot2D(x, y); - } + public Spot GetSpot2D(float x, float y) + { + LoadChunk(x, y); + GraphChunk gc = GetChunkAt(x, y); + return gc.GetSpot2D(x, y); + } - public Spot GetSpot(Vector3 l) - { - // Null? - //if (l == null) - // return null; - return GetSpot(l.X, l.Y, l.Z); - } + public Spot GetSpot(Vector3 l) + { + // Null? + //if (l == null) + // return null; + return GetSpot(l.X, l.Y, l.Z); + } - // this can be slow... + // this can be slow... - public Spot FindClosestSpot(Vector3 l_d) - { - return FindClosestSpot(l_d, 30.0f); - } + public Spot FindClosestSpot(Vector3 l_d) + { + return FindClosestSpot(l_d, 30.0f); + } - /* - public Spot FindClosestSpot(Vector3 l, float max_d) - { - return FindClosestSpot(l, max_d, null); - } - */ + /* + public Spot FindClosestSpot(Vector3 l, float max_d) + { + return FindClosestSpot(l, max_d, null); + } + */ - // this can be slow... - public Spot FindClosestSpot(Vector3 l, float max_d) - { - Spot closest = null; - float closest_d = 1E30f; - int d = 0; + // this can be slow... + public Spot FindClosestSpot(Vector3 l, float max_d) + { + Spot closest = null; + float closest_d = 1E30f; + int d = 0; - const int SV_LENGTH = 4; - var pooler = ArrayPool.Shared; - var sv = pooler.Rent(SV_LENGTH); + const int SV_LENGTH = 4; + var pooler = ArrayPool.Shared; + var sv = pooler.Rent(SV_LENGTH); - while (d <= max_d + STEP_D) + while (d <= max_d + STEP_D) + { + for (int i = -d; i <= d; i++) { - for (int i = -d; i <= d; i++) - { - float x_up = l.X + d; - float x_dn = l.X - d; - float y_up = l.Y + d; - float y_dn = l.Y - d; + float x_up = l.X + d; + float x_dn = l.X - d; + float y_up = l.Y + d; + float y_dn = l.Y - d; - sv[0] = GetSpot2D(x_up, l.Y + i); - sv[1] = GetSpot2D(x_dn, l.Y + i); - sv[2] = GetSpot2D(l.X + i, y_dn); - sv[3] = GetSpot2D(l.X + i, y_up); + sv[0] = GetSpot2D(x_up, l.Y + i); + sv[1] = GetSpot2D(x_dn, l.Y + i); + sv[2] = GetSpot2D(l.X + i, y_dn); + sv[3] = GetSpot2D(l.X + i, y_up); - for (int i1 = 0; i1 < SV_LENGTH; i1++) + for (int i1 = 0; i1 < SV_LENGTH; i1++) + { + Spot s = sv[i1]; + Spot ss = s; + while (ss != null) { - Spot s = sv[i1]; - Spot ss = s; - while (ss != null) + float di = ss.GetDistanceTo(l); + if (di < max_d && !ss.IsBlocked() && + (di < closest_d)) { - float di = ss.GetDistanceTo(l); - if (di < max_d && !ss.IsBlocked() && - (di < closest_d)) - { - closest = ss; - closest_d = di; - } - ss = ss.next; + closest = ss; + closest_d = di; } + ss = ss.next; } } + } - if (closest_d < d) // can't get better - { - pooler.Return(sv); - //Log("Closest2 spot to " + l + " is " + closest); - return closest; - } - d++; + if (closest_d < d) // can't get better + { + pooler.Return(sv); + //Log("Closest2 spot to " + l + " is " + closest); + return closest; } - pooler.Return(sv); - //Log("Closest1 spot to " + l + " is " + closest); - return closest; + d++; } + pooler.Return(sv); + //Log("Closest1 spot to " + l + " is " + closest); + return closest; + } - public Span FindAllSpots(Vector3 l, float max_d) - { - const int SV_LENGTH = 4; - var pooler = ArrayPool.Shared; - var sv = pooler.Rent(SV_LENGTH); + public Span FindAllSpots(Vector3 l, float max_d) + { + const int SV_LENGTH = 4; + var pooler = ArrayPool.Shared; + var sv = pooler.Rent(SV_LENGTH); - int size = (int)Ceiling(2 * (max_d / STEP_D)); - var sl = pooler.Rent(size); - int c = 0; + int size = (int)Ceiling(2 * (max_d / STEP_D)); + var sl = pooler.Rent(size); + int c = 0; - int d = 0; - while (d <= max_d + STEP_D) + int d = 0; + while (d <= max_d + STEP_D) + { + for (int i = -d; i <= d; i++) { - for (int i = -d; i <= d; i++) - { - float x_up = l.X + d; - float x_dn = l.X - d; - float y_up = l.Y + d; - float y_dn = l.Y - d; + float x_up = l.X + d; + float x_dn = l.X - d; + float y_up = l.Y + d; + float y_dn = l.Y - d; - sv[0] = GetSpot2D(x_up, l.Y + i); - sv[1] = GetSpot2D(x_dn, l.Y + i); - sv[2] = GetSpot2D(l.X + i, y_dn); - sv[3] = GetSpot2D(l.X + i, y_up); + sv[0] = GetSpot2D(x_up, l.Y + i); + sv[1] = GetSpot2D(x_dn, l.Y + i); + sv[2] = GetSpot2D(l.X + i, y_dn); + sv[3] = GetSpot2D(l.X + i, y_up); - for (int j = 0; j < SV_LENGTH; j++) + for (int j = 0; j < SV_LENGTH; j++) + { + Spot s = sv[j]; + Spot ss = s; + while (ss != null) { - Spot s = sv[j]; - Spot ss = s; - while (ss != null) + float di = ss.GetDistanceTo(l); + if (di < max_d) { - float di = ss.GetDistanceTo(l); - if (di < max_d) - { - sl[c++] = ss; - } - ss = ss.next; + sl[c++] = ss; } + ss = ss.next; } } - d++; } + d++; + } - pooler.Return(sv); - pooler.Return(sl); + pooler.Return(sv); + pooler.Return(sl); - return sl.AsSpan(0, c); - } + return sl.AsSpan(0, c); + } - public Spot TryAddSpot(Spot wasAt, Vector3 isAt) + public Spot TryAddSpot(Spot wasAt, Vector3 isAt) + { + //if (IsUnderwaterOrInAir(isAt)) { return wasAt; } + Spot isAtSpot = FindClosestSpot(isAt, WantedStepLength); + if (isAtSpot == null) { - //if (IsUnderwaterOrInAir(isAt)) { return wasAt; } - Spot isAtSpot = FindClosestSpot(isAt, WantedStepLength); + isAtSpot = GetSpot(isAt); if (isAtSpot == null) { - isAtSpot = GetSpot(isAt); - if (isAtSpot == null) - { - Spot s = new Spot(isAt); - s = AddSpot(s); - isAtSpot = s; - } - if (isAtSpot.IsFlagSet(Spot.FLAG_BLOCKED)) - { - isAtSpot.SetFlag(Spot.FLAG_BLOCKED, false); - if (logger.IsEnabled(LogLevel.Debug)) - logger.LogDebug("Cleared blocked flag"); - } - if (wasAt != null) - { - wasAt.AddPathTo(isAtSpot); - isAtSpot.AddPathTo(wasAt); - } - - Span sl = FindAllSpots(isAtSpot.Loc, MaxStepLength); - int connected = 0; - for (int i = 0; i < sl.Length; i++) - { - Spot other = sl[i]; - if (other != isAtSpot) - { - other.AddPathTo(isAtSpot); - isAtSpot.AddPathTo(other); - connected++; - // Log(" connect to " + other.location); - } - } + Spot s = new Spot(isAt); + s = AddSpot(s); + isAtSpot = s; + } + if (isAtSpot.IsFlagSet(Spot.FLAG_BLOCKED)) + { + isAtSpot.SetFlag(Spot.FLAG_BLOCKED, false); if (logger.IsEnabled(LogLevel.Debug)) - logger.LogDebug("Learned a new spot at " + isAtSpot.Loc + " connected to " + connected + " other spots"); - wasAt = isAtSpot; + logger.LogDebug("Cleared blocked flag"); } - else + if (wasAt != null) + { + wasAt.AddPathTo(isAtSpot); + isAtSpot.AddPathTo(wasAt); + } + + Span sl = FindAllSpots(isAtSpot.Loc, MaxStepLength); + int connected = 0; + for (int i = 0; i < sl.Length; i++) { - if (wasAt != null && wasAt != isAtSpot) + Spot other = sl[i]; + if (other != isAtSpot) { - // moved to an old spot, make sure they are connected - wasAt.AddPathTo(isAtSpot); - isAtSpot.AddPathTo(wasAt); + other.AddPathTo(isAtSpot); + isAtSpot.AddPathTo(other); + connected++; + // Log(" connect to " + other.location); } - wasAt = isAtSpot; } - - return wasAt; + if (logger.IsEnabled(LogLevel.Debug)) + logger.LogDebug("Learned a new spot at " + isAtSpot.Loc + " connected to " + connected + " other spots"); + wasAt = isAtSpot; } - - private static bool LineCrosses(Vector3 line0, Vector3 line1, Vector3 point) + else { - //float LineMag = line0.GetDistanceTo(line1); // Magnitude( LineEnd, LineStart ); - float LineMag = Vector3.DistanceSquared(line0, line1); + if (wasAt != null && wasAt != isAtSpot) + { + // moved to an old spot, make sure they are connected + wasAt.AddPathTo(isAtSpot); + isAtSpot.AddPathTo(wasAt); + } + wasAt = isAtSpot; + } - float U = - (((point.X - line0.X) * (line1.X - line0.X)) + - ((point.Y - line0.Y) * (line1.Y - line0.Y)) + - ((point.Z - line0.Z) * (line1.Z - line0.Z))) / - (LineMag * LineMag); + return wasAt; + } - if (U < 0.0f || U > 1.0f) - return false; + private static bool LineCrosses(Vector3 line0, Vector3 line1, Vector3 point) + { + //float LineMag = line0.GetDistanceTo(line1); // Magnitude( LineEnd, LineStart ); + float LineMag = Vector3.DistanceSquared(line0, line1); - float InterX = line0.X + U * (line1.X - line0.X); - float InterY = line0.Y + U * (line1.Y - line0.Y); - float InterZ = line0.Z + U * (line1.Z - line0.Z); + float U = + (((point.X - line0.X) * (line1.X - line0.X)) + + ((point.Y - line0.Y) * (line1.Y - line0.Y)) + + ((point.Z - line0.Z) * (line1.Z - line0.Z))) / + (LineMag * LineMag); - float Distance = Vector3.DistanceSquared(point, new(InterX, InterY, InterZ)); - if (Distance < 0.5f) - return true; + if (U < 0.0f || U > 1.0f) return false; - } - ////////////////////////////////////////////////////// - // Searching - ////////////////////////////////////////////////////// + float InterX = line0.X + U * (line1.X - line0.X); + float InterY = line0.Y + U * (line1.Y - line0.Y); + float InterZ = line0.Z + U * (line1.Z - line0.Z); - public Spot currentSearchStartSpot; - public Spot currentSearchSpot; + float Distance = Vector3.DistanceSquared(point, new(InterX, InterY, InterZ)); + if (Distance < 0.5f) + return true; + return false; + } - private static float TurnCost(Spot from, Spot to) - { - if (from.traceBack == null) - return 0.0f; + ////////////////////////////////////////////////////// + // Searching + ////////////////////////////////////////////////////// - Spot prev = from.traceBack; - return TurnCost(prev.Loc.X, prev.Loc.Y, prev.Loc.Z, from.Loc.X, from.Loc.Y, from.Loc.Z, to.Loc.X, to.Loc.Y, to.Loc.Z); - } + public Spot currentSearchStartSpot; + public Spot currentSearchSpot; - private static float TurnCost(float x0, float y0, float z0, float x1, float y1, float z1, float x2, float y2, float z2) - { - float v1x = x1 - x0; - float v1y = y1 - y0; - float v1z = z1 - z0; + private static float TurnCost(Spot from, Spot to) + { + if (from.traceBack == null) + return 0.0f; - float v1l = Sqrt(v1x * v1x + v1y * v1y + v1z * v1z); - v1x /= v1l; - v1y /= v1l; - v1z /= v1l; + Spot prev = from.traceBack; + return TurnCost(prev.Loc.X, prev.Loc.Y, prev.Loc.Z, from.Loc.X, from.Loc.Y, from.Loc.Z, to.Loc.X, to.Loc.Y, to.Loc.Z); + } - float v2x = x2 - x1; - float v2y = y2 - y1; - float v2z = z2 - z1; + private static float TurnCost(float x0, float y0, float z0, float x1, float y1, float z1, float x2, float y2, float z2) + { + float v1x = x1 - x0; + float v1y = y1 - y0; + float v1z = z1 - z0; + + float v1l = Sqrt(v1x * v1x + v1y * v1y + v1z * v1z); + v1x /= v1l; + v1y /= v1l; + v1z /= v1l; + + float v2x = x2 - x1; + float v2y = y2 - y1; + float v2z = z2 - z1; + + float v2l = Sqrt(v2x * v2x + v2y * v2y + v2z * v2z); + v2x /= v2l; + v2y /= v2l; + v2z /= v2l; + + float ddx = v1x - v2x; + float ddy = v1y - v2y; + float ddz = v1z - v2z; + return Sqrt(ddx * ddx + ddy * ddy + ddz * ddz); + } - float v2l = Sqrt(v2x * v2x + v2y * v2y + v2z * v2z); - v2x /= v2l; - v2y /= v2l; - v2z /= v2l; + // return null if failed or the last spot in the path found - float ddx = v1x - v2x; - float ddy = v1y - v2y; - float ddz = v1z - v2z; - return Sqrt(ddx * ddx + ddy * ddy + ddz * ddz); - } + //SearchProgress searchProgress; + //public SearchProgress SearchProgress + //{ + // get + // { + // return searchProgress; + // } + //} + private int searchID; - // return null if failed or the last spot in the path found + private float heuristicsFactor = 5f; - //SearchProgress searchProgress; - //public SearchProgress SearchProgress - //{ - // get - // { - // return searchProgress; - // } - //} - private int searchID; + public Spot ClosestSpot; + public Spot PeekSpot; - private float heuristicsFactor = 5f; + private Spot Search(Spot fromSpot, Spot destinationSpot, float minHowClose) + { + long searchDuration = Stopwatch.GetTimestamp(); + long timeSinceProgress = Stopwatch.GetTimestamp(); - public Spot ClosestSpot; - public Spot PeekSpot; + float closest = 99999f; + ClosestSpot = null; - private Spot Search(Spot fromSpot, Spot destinationSpot, float minHowClose) - { - long searchDuration = Stopwatch.GetTimestamp(); - long timeSinceProgress = Stopwatch.GetTimestamp(); + currentSearchStartSpot = fromSpot; + searchID++; + int currentSearchID = searchID; + //searchProgress = new SearchProgress(fromSpot, destinationSpot, searchID); - float closest = 99999f; - ClosestSpot = null; + // lowest first queue + PriorityQueue prioritySpotQueue = new(); + prioritySpotQueue.Enqueue(fromSpot, fromSpot.GetDistanceTo(destinationSpot) * heuristicsFactor); - currentSearchStartSpot = fromSpot; - searchID++; - int currentSearchID = searchID; - //searchProgress = new SearchProgress(fromSpot, destinationSpot, searchID); + fromSpot.SearchScoreSet(currentSearchID, 0.0f); + fromSpot.traceBack = null; + fromSpot.traceBackDistance = 0; - // lowest first queue - PriorityQueue prioritySpotQueue = new(); - prioritySpotQueue.Enqueue(fromSpot, fromSpot.GetDistanceTo(destinationSpot) * heuristicsFactor); + // A* -ish algorithm + while (prioritySpotQueue.TryDequeue(out currentSearchSpot, out _)) + { + if (sleepMSBetweenSpots > 0) { Thread.Sleep(sleepMSBetweenSpots); } // slow down the pathing - fromSpot.SearchScoreSet(currentSearchID, 0.0f); - fromSpot.traceBack = null; - fromSpot.traceBackDistance = 0; + // force the world to be loaded + _ = triangleWorld.GetChunkAt(currentSearchSpot.Loc.X, currentSearchSpot.Loc.Y); - // A* -ish algorithm - while (prioritySpotQueue.TryDequeue(out currentSearchSpot, out _)) + if (currentSearchSpot.SearchIsClosed(currentSearchID)) { - if (sleepMSBetweenSpots > 0) { Thread.Sleep(sleepMSBetweenSpots); } // slow down the pathing - - // force the world to be loaded - _ = triangleWorld.GetChunkAt(currentSearchSpot.Loc.X, currentSearchSpot.Loc.Y); - - if (currentSearchSpot.SearchIsClosed(currentSearchID)) - { - continue; - } - currentSearchSpot.SearchClose(currentSearchID); - - //update status - //if (!searchProgress.CheckProgress(currentSearchSpot)) { break; } - - // are we there? + continue; + } + currentSearchSpot.SearchClose(currentSearchID); - //float distance = currentSearchSpot.location.GetDistanceTo(destinationSpot.location); - float distance = Vector3.Distance(currentSearchSpot.Loc, destinationSpot.Loc); + //update status + //if (!searchProgress.CheckProgress(currentSearchSpot)) { break; } - if (distance <= minHowClose) - { - return currentSearchSpot; // got there - } + // are we there? - if (distance < closest) - { - // spamming as hell - //logger.WriteLine($"Closet spot is {distance} from the target"); - closest = distance; - ClosestSpot = currentSearchSpot; - PeekSpot = ClosestSpot; - timeSinceProgress = Stopwatch.GetTimestamp(); - } + //float distance = currentSearchSpot.location.GetDistanceTo(destinationSpot.location); + float distance = Vector3.Distance(currentSearchSpot.Loc, destinationSpot.Loc); - if (Stopwatch.GetElapsedTime(timeSinceProgress).TotalSeconds > ProgressTimeoutSeconds || - Stopwatch.GetElapsedTime(searchDuration).TotalSeconds > TimeoutSeconds) - { - logger.LogWarning("search failed, 10 seconds since last progress, returning the closest spot."); - return ClosestSpot; - } - - //Find spots to link to - CreateSpotsAroundSpot(currentSearchSpot); - - //score each spot around the current search spot and add them to the queue - Span list = currentSearchSpot.GetPathsToSpots(this); - for (int i = 0; i < list.Length; i++) - { - Spot spotLinkedToCurrent = list[i]; - if (spotLinkedToCurrent != null && !spotLinkedToCurrent.IsBlocked() && !spotLinkedToCurrent.SearchIsClosed(currentSearchID)) - { - ScoreSpot(spotLinkedToCurrent, destinationSpot, currentSearchID, prioritySpotQueue); - } - } + if (distance <= minHowClose) + { + return currentSearchSpot; // got there } - //we ran out of spots to search - //searchProgress.LogStatus(" search failed. "); + if (distance < closest) + { + // spamming as hell + //logger.WriteLine($"Closet spot is {distance} from the target"); + closest = distance; + ClosestSpot = currentSearchSpot; + PeekSpot = ClosestSpot; + timeSinceProgress = Stopwatch.GetTimestamp(); + } - if (ClosestSpot != null && closest < MaximumAllowedRangeFromTarget) + if (Stopwatch.GetElapsedTime(timeSinceProgress).TotalSeconds > ProgressTimeoutSeconds || + Stopwatch.GetElapsedTime(searchDuration).TotalSeconds > TimeoutSeconds) { - logger.LogWarning("search failed, returning the closest spot."); + logger.LogWarning("search failed, 10 seconds since last progress, returning the closest spot."); return ClosestSpot; } - return null; - } + //Find spots to link to + CreateSpotsAroundSpot(currentSearchSpot); - private void ScoreSpot(Spot spotLinkedToCurrent, Spot destinationSpot, int currentSearchID, PriorityQueue prioritySpotQueue) - { - switch (searchScoreSpot) + //score each spot around the current search spot and add them to the queue + Span list = currentSearchSpot.GetPathsToSpots(this); + for (int i = 0; i < list.Length; i++) { - case eSearchScoreSpot.A_Star: - ScoreSpot_A_Star(spotLinkedToCurrent, destinationSpot, currentSearchID, prioritySpotQueue); - break; - - case eSearchScoreSpot.A_Star_With_Model_Avoidance: - ScoreSpot_A_Star_With_Model_And_Gradient_Avoidance(spotLinkedToCurrent, destinationSpot, currentSearchID, prioritySpotQueue); - break; - - case eSearchScoreSpot.OriginalPather: - default: - ScoreSpot_Pather(spotLinkedToCurrent, destinationSpot, currentSearchID, prioritySpotQueue); - break; + Spot spotLinkedToCurrent = list[i]; + if (spotLinkedToCurrent != null && !spotLinkedToCurrent.IsBlocked() && !spotLinkedToCurrent.SearchIsClosed(currentSearchID)) + { + ScoreSpot(spotLinkedToCurrent, destinationSpot, currentSearchID, prioritySpotQueue); + } } } - public void ScoreSpot_A_Star(Spot spotLinkedToCurrent, Spot destinationSpot, int currentSearchID, PriorityQueue prioritySpotQueue) - { - //score spot - float G_Score = currentSearchSpot.traceBackDistance + currentSearchSpot.GetDistanceTo(spotLinkedToCurrent);// the movement cost to move from the starting point A to a given square on the grid, following the path generated to get there. - float H_Score = spotLinkedToCurrent.GetDistanceTo2D(destinationSpot) * heuristicsFactor;// the estimated movement cost to move from that given square on the grid to the final destination, point B. This is often referred to as the heuristic, which can be a bit confusing. The reason why it is called that is because it is a guess. We really don�t know the actual distance until we find the path, because all sorts of things can be in the way (walls, water, etc.). You are given one way to calculate H in this tutorial, but there are many others that you can find in other articles on the web. - float F_Score = G_Score + H_Score; - - if (spotLinkedToCurrent.IsFlagSet(Spot.FLAG_WATER)) { F_Score += 50; } + //we ran out of spots to search + //searchProgress.LogStatus(" search failed. "); - if (!spotLinkedToCurrent.SearchScoreIsSet(currentSearchID) || F_Score < spotLinkedToCurrent.SearchScoreGet(currentSearchID)) - { - // shorter path to here found - spotLinkedToCurrent.traceBack = currentSearchSpot; - spotLinkedToCurrent.traceBackDistance = G_Score; - spotLinkedToCurrent.SearchScoreSet(currentSearchID, F_Score); - prioritySpotQueue.Enqueue(spotLinkedToCurrent, F_Score); - } + if (ClosestSpot != null && closest < MaximumAllowedRangeFromTarget) + { + logger.LogWarning("search failed, returning the closest spot."); + return ClosestSpot; } - public void ScoreSpot_A_Star_With_Model_And_Gradient_Avoidance(Spot spotLinkedToCurrent, Spot destinationSpot, int currentSearchID, PriorityQueue prioritySpotQueue) - { - //score spot - float G_Score = currentSearchSpot.traceBackDistance + currentSearchSpot.GetDistanceTo(spotLinkedToCurrent);// the movement cost to move from the starting point A to a given square on the grid, following the path generated to get there. - float H_Score = spotLinkedToCurrent.GetDistanceTo2D(destinationSpot) * heuristicsFactor;// the estimated movement cost to move from that given square on the grid to the final destination, point B. This is often referred to as the heuristic, which can be a bit confusing. The reason why it is called that is because it is a guess. We really don�t know the actual distance until we find the path, because all sorts of things can be in the way (walls, water, etc.). You are given one way to calculate H in this tutorial, but there are many others that you can find in other articles on the web. - float F_Score = G_Score + H_Score; + return null; + } - if (spotLinkedToCurrent.IsFlagSet(Spot.FLAG_WATER)) { F_Score += 50; } + private void ScoreSpot(Spot spotLinkedToCurrent, Spot destinationSpot, int currentSearchID, PriorityQueue prioritySpotQueue) + { + switch (searchScoreSpot) + { + case eSearchScoreSpot.A_Star: + ScoreSpot_A_Star(spotLinkedToCurrent, destinationSpot, currentSearchID, prioritySpotQueue); + break; - int score = GetTriangleClosenessScore(spotLinkedToCurrent.Loc); - score += GetTriangleGradiantScore(spotLinkedToCurrent.Loc, gradiantMax); - F_Score += score * 2; + case eSearchScoreSpot.A_Star_With_Model_Avoidance: + ScoreSpot_A_Star_With_Model_And_Gradient_Avoidance(spotLinkedToCurrent, destinationSpot, currentSearchID, prioritySpotQueue); + break; - if (!spotLinkedToCurrent.SearchScoreIsSet(currentSearchID) || F_Score < spotLinkedToCurrent.SearchScoreGet(currentSearchID)) - { - // shorter path to here found - spotLinkedToCurrent.traceBack = currentSearchSpot; - spotLinkedToCurrent.traceBackDistance = G_Score; - spotLinkedToCurrent.SearchScoreSet(currentSearchID, F_Score); - prioritySpotQueue.Enqueue(spotLinkedToCurrent, F_Score); - } + case eSearchScoreSpot.OriginalPather: + default: + ScoreSpot_Pather(spotLinkedToCurrent, destinationSpot, currentSearchID, prioritySpotQueue); + break; } + } + + public void ScoreSpot_A_Star(Spot spotLinkedToCurrent, Spot destinationSpot, int currentSearchID, PriorityQueue prioritySpotQueue) + { + //score spot + float G_Score = currentSearchSpot.traceBackDistance + currentSearchSpot.GetDistanceTo(spotLinkedToCurrent);// the movement cost to move from the starting point A to a given square on the grid, following the path generated to get there. + float H_Score = spotLinkedToCurrent.GetDistanceTo2D(destinationSpot) * heuristicsFactor;// the estimated movement cost to move from that given square on the grid to the final destination, point B. This is often referred to as the heuristic, which can be a bit confusing. The reason why it is called that is because it is a guess. We really don�t know the actual distance until we find the path, because all sorts of things can be in the way (walls, water, etc.). You are given one way to calculate H in this tutorial, but there are many others that you can find in other articles on the web. + float F_Score = G_Score + H_Score; + + if (spotLinkedToCurrent.IsFlagSet(Spot.FLAG_WATER)) { F_Score += 50; } - public void ScoreSpot_Pather(Spot spotLinkedToCurrent, Spot destinationSpot, int currentSearchID, PriorityQueue prioritySpotQueue) + if (!spotLinkedToCurrent.SearchScoreIsSet(currentSearchID) || F_Score < spotLinkedToCurrent.SearchScoreGet(currentSearchID)) { - //score spots - float currentSearchSpotScore = currentSearchSpot.SearchScoreGet(currentSearchID); - float linkedSpotScore = 1E30f; - float new_score = currentSearchSpotScore + currentSearchSpot.GetDistanceTo(spotLinkedToCurrent) + TurnCost(currentSearchSpot, spotLinkedToCurrent); + // shorter path to here found + spotLinkedToCurrent.traceBack = currentSearchSpot; + spotLinkedToCurrent.traceBackDistance = G_Score; + spotLinkedToCurrent.SearchScoreSet(currentSearchID, F_Score); + prioritySpotQueue.Enqueue(spotLinkedToCurrent, F_Score); + } + } - if (spotLinkedToCurrent.IsFlagSet(Spot.FLAG_WATER)) { new_score += 50; } + public void ScoreSpot_A_Star_With_Model_And_Gradient_Avoidance(Spot spotLinkedToCurrent, Spot destinationSpot, int currentSearchID, PriorityQueue prioritySpotQueue) + { + //score spot + float G_Score = currentSearchSpot.traceBackDistance + currentSearchSpot.GetDistanceTo(spotLinkedToCurrent);// the movement cost to move from the starting point A to a given square on the grid, following the path generated to get there. + float H_Score = spotLinkedToCurrent.GetDistanceTo2D(destinationSpot) * heuristicsFactor;// the estimated movement cost to move from that given square on the grid to the final destination, point B. This is often referred to as the heuristic, which can be a bit confusing. The reason why it is called that is because it is a guess. We really don�t know the actual distance until we find the path, because all sorts of things can be in the way (walls, water, etc.). You are given one way to calculate H in this tutorial, but there are many others that you can find in other articles on the web. + float F_Score = G_Score + H_Score; - if (spotLinkedToCurrent.SearchScoreIsSet(currentSearchID)) - { - linkedSpotScore = spotLinkedToCurrent.SearchScoreGet(currentSearchID); - } + if (spotLinkedToCurrent.IsFlagSet(Spot.FLAG_WATER)) { F_Score += 50; } - if (new_score < linkedSpotScore) - { - // shorter path to here found - spotLinkedToCurrent.traceBack = currentSearchSpot; - spotLinkedToCurrent.SearchScoreSet(currentSearchID, new_score); - prioritySpotQueue.Enqueue(spotLinkedToCurrent, (new_score + spotLinkedToCurrent.GetDistanceTo(destinationSpot) * heuristicsFactor)); - } - } + int score = GetTriangleClosenessScore(spotLinkedToCurrent.Loc); + score += GetTriangleGradiantScore(spotLinkedToCurrent.Loc, gradiantMax); + F_Score += score * 2; - public void CreateSpotsAroundSpot(Spot currentSearchSpot) + if (!spotLinkedToCurrent.SearchScoreIsSet(currentSearchID) || F_Score < spotLinkedToCurrent.SearchScoreGet(currentSearchID)) { - CreateSpotsAroundSpot(currentSearchSpot, currentSearchSpot.IsFlagSet(Spot.FLAG_MPQ_MAPPED)); + // shorter path to here found + spotLinkedToCurrent.traceBack = currentSearchSpot; + spotLinkedToCurrent.traceBackDistance = G_Score; + spotLinkedToCurrent.SearchScoreSet(currentSearchID, F_Score); + prioritySpotQueue.Enqueue(spotLinkedToCurrent, F_Score); } + } - public void CreateSpotsAroundSpot(Spot currentSearchSpot, bool mapped) - { - if (mapped) - { - return; - } + public void ScoreSpot_Pather(Spot spotLinkedToCurrent, Spot destinationSpot, int currentSearchID, PriorityQueue prioritySpotQueue) + { + //score spots + float currentSearchSpotScore = currentSearchSpot.SearchScoreGet(currentSearchID); + float linkedSpotScore = 1E30f; + float new_score = currentSearchSpotScore + currentSearchSpot.GetDistanceTo(spotLinkedToCurrent) + TurnCost(currentSearchSpot, spotLinkedToCurrent); - //mark as mapped - currentSearchSpot.SetFlag(Spot.FLAG_MPQ_MAPPED, true); + if (spotLinkedToCurrent.IsFlagSet(Spot.FLAG_WATER)) { new_score += 50; } - //loop through the spots in a circle around the current search spot - for (float radianAngle = 0; radianAngle < PI * 2; radianAngle += PI / 8) - { - //calculate the location of the spot at the angle - float nx = currentSearchSpot.Loc.X + (Sin(radianAngle) * WantedStepLength);// *0.8f; - float ny = currentSearchSpot.Loc.Y + (Cos(radianAngle) * WantedStepLength);// *0.8f; + if (spotLinkedToCurrent.SearchScoreIsSet(currentSearchID)) + { + linkedSpotScore = spotLinkedToCurrent.SearchScoreGet(currentSearchID); + } - PeekSpot = new Spot(nx, ny, currentSearchSpot.Loc.Z); + if (new_score < linkedSpotScore) + { + // shorter path to here found + spotLinkedToCurrent.traceBack = currentSearchSpot; + spotLinkedToCurrent.SearchScoreSet(currentSearchID, new_score); + prioritySpotQueue.Enqueue(spotLinkedToCurrent, (new_score + spotLinkedToCurrent.GetDistanceTo(destinationSpot) * heuristicsFactor)); + } + } - //find the spot at this location, stop if there is one already - if (GetSpot(nx, ny, currentSearchSpot.Loc.Z) != null) { continue; } //found a spot so don't create a new one + public void CreateSpotsAroundSpot(Spot currentSearchSpot) + { + CreateSpotsAroundSpot(currentSearchSpot, currentSearchSpot.IsFlagSet(Spot.FLAG_MPQ_MAPPED)); + } - // TODO: - //see if there is a close spot, stop if there is - if (FindClosestSpot(new(nx, ny, currentSearchSpot.Loc.Z), MinStepLength) != null) - { - continue; - } // TODO: this is slow + public void CreateSpotsAroundSpot(Spot currentSearchSpot, bool mapped) + { + if (mapped) + { + return; + } - // check we can stand at this new location - if (!triangleWorld.FindStandableAt(nx, ny, currentSearchSpot.Loc.Z - WantedStepLength * .75f, - currentSearchSpot.Loc.Z + WantedStepLength * .75f, out float new_z, out TriangleType flags, toonHeight, toonSize)) - { - continue; - } + //mark as mapped + currentSearchSpot.SetFlag(Spot.FLAG_MPQ_MAPPED, true); - // TODO: - //see if a spot already exists at this location - if (FindClosestSpot(new(nx, ny, new_z), MinStepLength) != null) - { - continue; - } + //loop through the spots in a circle around the current search spot + for (float radianAngle = 0; radianAngle < PI * 2; radianAngle += PI / 8) + { + //calculate the location of the spot at the angle + float nx = currentSearchSpot.Loc.X + (Sin(radianAngle) * WantedStepLength);// *0.8f; + float ny = currentSearchSpot.Loc.Y + (Cos(radianAngle) * WantedStepLength);// *0.8f; - //if the step is blocked then stop - if (triangleWorld.IsStepBlocked(currentSearchSpot.Loc.X, currentSearchSpot.Loc.Y, currentSearchSpot.Loc.Z, nx, ny, new_z, toonHeight, toonSize)) - { - continue; - } + PeekSpot = new Spot(nx, ny, currentSearchSpot.Loc.Z); - //create a new spot and connect it - Spot newSpot = AddAndConnectSpot(new Spot(nx, ny, new_z)); - //PeekSpot = newSpot; + //find the spot at this location, stop if there is one already + if (GetSpot(nx, ny, currentSearchSpot.Loc.Z) != null) { continue; } //found a spot so don't create a new one - //check flags return by triangleWorld.FindStandableA - if ((flags & TriangleType.Water) != 0) - { - newSpot.SetFlag(Spot.FLAG_WATER, true); - } - if (((flags & TriangleType.Model) != 0) || ((flags & TriangleType.Object) != 0)) - { - newSpot.SetFlag(Spot.FLAG_INDOORS, true); - } - if (triangleWorld.IsCloseToModel(newSpot.Loc.X, newSpot.Loc.Y, newSpot.Loc.Z, IsCloseToModelRange)) - { - newSpot.SetFlag(Spot.FLAG_CLOSETOMODEL, true); - } + // TODO: + //see if there is a close spot, stop if there is + if (FindClosestSpot(new(nx, ny, currentSearchSpot.Loc.Z), MinStepLength) != null) + { + continue; + } // TODO: this is slow + + // check we can stand at this new location + if (!triangleWorld.FindStandableAt(nx, ny, currentSearchSpot.Loc.Z - WantedStepLength * .75f, + currentSearchSpot.Loc.Z + WantedStepLength * .75f, out float new_z, out TriangleType flags, toonHeight, toonSize)) + { + continue; } - } - private Spot lastCurrentSearchSpot; + // TODO: + //see if a spot already exists at this location + if (FindClosestSpot(new(nx, ny, new_z), MinStepLength) != null) + { + continue; + } - public List CurrentSearchPath() - { - if (lastCurrentSearchSpot == currentSearchSpot) + //if the step is blocked then stop + if (triangleWorld.IsStepBlocked(currentSearchSpot.Loc.X, currentSearchSpot.Loc.Y, currentSearchSpot.Loc.Z, nx, ny, new_z, toonHeight, toonSize)) { - return null; + continue; } - lastCurrentSearchSpot = currentSearchSpot; - return FollowTraceBack(currentSearchStartSpot, currentSearchSpot); - } + //create a new spot and connect it + Spot newSpot = AddAndConnectSpot(new Spot(nx, ny, new_z)); + //PeekSpot = newSpot; - private static List FollowTraceBack(Spot from, Spot to) - { - List path = new(); - Spot backtrack = to; - while (backtrack != from && backtrack != null) + //check flags return by triangleWorld.FindStandableA + if ((flags & TriangleType.Water) != 0) + { + newSpot.SetFlag(Spot.FLAG_WATER, true); + } + if (((flags & TriangleType.Model) != 0) || ((flags & TriangleType.Object) != 0)) + { + newSpot.SetFlag(Spot.FLAG_INDOORS, true); + } + if (triangleWorld.IsCloseToModel(newSpot.Loc.X, newSpot.Loc.Y, newSpot.Loc.Z, IsCloseToModelRange)) { - path.Insert(0, backtrack); - backtrack = backtrack.traceBack; + newSpot.SetFlag(Spot.FLAG_CLOSETOMODEL, true); } - path.Insert(0, from); - return path; } + } + + private Spot lastCurrentSearchSpot; - public bool IsUnderwaterOrInAir(Vector3 l) + public List CurrentSearchPath() + { + if (lastCurrentSearchSpot == currentSearchSpot) { - if (triangleWorld.FindStandableAt(l.X, l.Y, l.Z - 50.0f, l.Z + 5.0f, out _, out TriangleType flags, toonHeight, toonSize)) - { - if ((flags & TriangleType.Water) != 0) - return true; - else - return false; - } - return false; + return null; } - /* - public bool IsUnderwaterOrInAir(Spot s) + lastCurrentSearchSpot = currentSearchSpot; + return FollowTraceBack(currentSearchStartSpot, currentSearchSpot); + } + + private static List FollowTraceBack(Spot from, Spot to) + { + List path = new(); + Spot backtrack = to; + while (backtrack != from && backtrack != null) { - return IsUnderwaterOrInAir(s.GetLocation()); + path.Insert(0, backtrack); + backtrack = backtrack.traceBack; } - */ + path.Insert(0, from); + return path; + } - public static bool IsInABuilding(Vector3 l) + public bool IsUnderwaterOrInAir(Vector3 l) + { + if (triangleWorld.FindStandableAt(l.X, l.Y, l.Z - 50.0f, l.Z + 5.0f, out _, out TriangleType flags, toonHeight, toonSize)) { - //int flags; - //float z; - //if (triangleWorld.FindStandableAt(l.X, l.Y, l.Z +12.0f, l.Z + 50.0f, out z, out flags, toonHeight, toonSize)) - //{ - // return true; - // //return false; - //} - //return triangleWorld.IsCloseToModel(l.X,l.Y,l.Z,1); - //return true; - return false; + if ((flags & TriangleType.Water) != 0) + return true; + else + return false; } + return false; + } - public Path LastPath; + /* + public bool IsUnderwaterOrInAir(Spot s) + { + return IsUnderwaterOrInAir(s.GetLocation()); + } + */ - private Path CreatePath(Spot from, Spot to, float minHowClose) - { - Spot newTo = Search(from, to, minHowClose); - if (newTo == null) - return null; + public static bool IsInABuilding(Vector3 l) + { + //int flags; + //float z; + //if (triangleWorld.FindStandableAt(l.X, l.Y, l.Z +12.0f, l.Z + 50.0f, out z, out flags, toonHeight, toonSize)) + //{ + // return true; + // //return false; + //} + //return triangleWorld.IsCloseToModel(l.X,l.Y,l.Z,1); + //return true; + return false; + } - float distance = newTo.GetDistanceTo(to); - if (distance <= MaximumAllowedRangeFromTarget) - { - List path = FollowTraceBack(from, newTo); - LastPath = new Path(path); - return LastPath; - } + public Path LastPath; - logger.LogWarning($"Closest spot is too far from target. {distance}>{MaximumAllowedRangeFromTarget}"); + private Path CreatePath(Spot from, Spot to, float minHowClose) + { + Spot newTo = Search(from, to, minHowClose); + if (newTo == null) return null; - } - private Vector3 GetBestLocations(Vector3 location) + float distance = newTo.GetDistanceTo(to); + if (distance <= MaximumAllowedRangeFromTarget) { - const float zExtend = 1; + List path = FollowTraceBack(from, newTo); + LastPath = new Path(path); + return LastPath; + } - float newZ = 0; - Span a = stackalloc float[] { 0, 1f, 0.5f, -0.5f, -1f }; + logger.LogWarning($"Closest spot is too far from target. {distance}>{MaximumAllowedRangeFromTarget}"); + return null; + } + + private Vector3 GetBestLocations(Vector3 location) + { + const float zExtend = 1; - for (int z = 0; z < a.Length; z++) + float newZ = 0; + Span a = stackalloc float[] { 0, 1f, 0.5f, -0.5f, -1f }; + + for (int z = 0; z < a.Length; z++) + { + for (int x = 0; x < a.Length; x++) { - for (int x = 0; x < a.Length; x++) + for (int y = 0; y < a.Length; y++) { - for (int y = 0; y < a.Length; y++) + if (triangleWorld.FindStandableAt( + location.X + a[x], + location.Y + a[y], + location.Z + a[z] - zExtend - WantedStepLength * .75f, + location.Z + a[z] + zExtend + WantedStepLength * .75f, + out newZ, out _, + toonHeight, toonSize)) { - if (triangleWorld.FindStandableAt( - location.X + a[x], - location.Y + a[y], - location.Z + a[z] - zExtend - WantedStepLength * .75f, - location.Z + a[z] + zExtend + WantedStepLength * .75f, - out newZ, out _, - toonHeight, toonSize)) - { - goto end; - } + goto end; } } } - end: - return new(location.X, location.Y, newZ); } + end: + return new(location.X, location.Y, newZ); + } - public Path CreatePath(Vector3 fromLoc, Vector3 toLoc, float howClose) - { - if (logger.IsEnabled(LogLevel.Trace)) - logger.LogTrace($"Creating Path from {fromLoc} to {toLoc}"); + public Path CreatePath(Vector3 fromLoc, Vector3 toLoc, float howClose) + { + if (logger.IsEnabled(LogLevel.Trace)) + logger.LogTrace($"Creating Path from {fromLoc} to {toLoc}"); - long timestamp = Stopwatch.GetTimestamp(); + long timestamp = Stopwatch.GetTimestamp(); - fromLoc = GetBestLocations(fromLoc); - toLoc = GetBestLocations(toLoc); + fromLoc = GetBestLocations(fromLoc); + toLoc = GetBestLocations(toLoc); - Spot from = FindClosestSpot(fromLoc, MinStepLength); - Spot to = FindClosestSpot(toLoc, MinStepLength); + Spot from = FindClosestSpot(fromLoc, MinStepLength); + Spot to = FindClosestSpot(toLoc, MinStepLength); - if (from == null) - { - from = AddAndConnectSpot(new Spot(fromLoc)); - } - if (to == null) - { - to = AddAndConnectSpot(new Spot(toLoc)); - } + if (from == null) + { + from = AddAndConnectSpot(new Spot(fromLoc)); + } + if (to == null) + { + to = AddAndConnectSpot(new Spot(toLoc)); + } - Path rawPath = CreatePath(from, to, howClose); + Path rawPath = CreatePath(from, to, howClose); - if (logger.IsEnabled(LogLevel.Trace)) - logger.LogTrace($"CreatePath took {Stopwatch.GetElapsedTime(timestamp).TotalMilliseconds}ms"); + if (logger.IsEnabled(LogLevel.Trace)) + logger.LogTrace($"CreatePath took {Stopwatch.GetElapsedTime(timestamp).TotalMilliseconds}ms"); - if (rawPath == null) - { - return null; - } - else + if (rawPath == null) + { + return null; + } + else + { + Vector3 last = rawPath.GetLast; + //if (last.GetDistanceTo(toLoc) > 1.0) + if (Vector3.DistanceSquared(last, toLoc) > 1.0) { - Vector3 last = rawPath.GetLast; - //if (last.GetDistanceTo(toLoc) > 1.0) - if (Vector3.DistanceSquared(last, toLoc) > 1.0) - { - rawPath.Add(toLoc); - } + rawPath.Add(toLoc); } - LastPath = rawPath; - return rawPath; } + LastPath = rawPath; + return rawPath; } } \ No newline at end of file diff --git a/PPather/Graph/SearchLocation.cs b/PPather/Graph/SearchLocation.cs index 099266541..ccd5cd183 100644 --- a/PPather/Graph/SearchLocation.cs +++ b/PPather/Graph/SearchLocation.cs @@ -1,17 +1,16 @@ using System.Numerics; -namespace PPather.Graph +namespace PPather.Graph; + +public sealed class SearchLocation { - public sealed class SearchLocation - { - public Vector4 Location { get; } + public Vector4 Location { get; } - public string Description { get; } + public string Description { get; } - public SearchLocation(float x, float y, float z, float mapId, string description) - { - Location = new Vector4(x, y, z, mapId); - Description = description; - } + public SearchLocation(float x, float y, float z, float mapId, string description) + { + Location = new Vector4(x, y, z, mapId); + Description = description; } } diff --git a/PPather/Graph/Spot.cs b/PPather/Graph/Spot.cs index 176a9265b..4b0c7899b 100644 --- a/PPather/Graph/Spot.cs +++ b/PPather/Graph/Spot.cs @@ -21,278 +21,277 @@ You should have received a copy of the GNU Lesser General Public License using System.Numerics; using SharedLib.Extensions; -namespace PPather.Graph -{ - public sealed class Spot - { - public const float Z_RESOLUTION = 2.0f; // Z spots max this close +namespace PPather.Graph; - public const uint FLAG_VISITED = 0x0001; - public const uint FLAG_BLOCKED = 0x0002; - public const uint FLAG_MPQ_MAPPED = 0x0004; - public const uint FLAG_WATER = 0x0008; - public const uint FLAG_INDOORS = 0x0010; - public const uint FLAG_CLOSETOMODEL = 0x0020; +public sealed class Spot +{ + public const float Z_RESOLUTION = 2.0f; // Z spots max this close - public Vector3 Loc; + public const uint FLAG_VISITED = 0x0001; + public const uint FLAG_BLOCKED = 0x0002; + public const uint FLAG_MPQ_MAPPED = 0x0004; + public const uint FLAG_WATER = 0x0008; + public const uint FLAG_INDOORS = 0x0010; + public const uint FLAG_CLOSETOMODEL = 0x0020; - public uint flags; + public Vector3 Loc; - public int n_paths; - public float[] paths; // 3 floats per outgoing path + public uint flags; - public GraphChunk chunk; - public Spot next; // list on same x,y, used by chunk + public int n_paths; + public float[] paths; // 3 floats per outgoing path - public int searchID; - public Spot traceBack; // Used by search - public float traceBackDistance; // Used by search - public float score; // Used by search - public bool closed, scoreSet; + public GraphChunk chunk; + public Spot next; // list on same x,y, used by chunk - public Spot(float x, float y, float z) - { - Loc = new(x, y, z); - } + public int searchID; + public Spot traceBack; // Used by search + public float traceBackDistance; // Used by search + public float score; // Used by search + public bool closed, scoreSet; - public Spot(Vector3 l) - { - Loc = l; - } - - public void Clear() - { - next = null; - chunk = null; - traceBack = null; - } + public Spot(float x, float y, float z) + { + Loc = new(x, y, z); + } - public bool IsCloseToModel() - { - return IsFlagSet(FLAG_CLOSETOMODEL); - } + public Spot(Vector3 l) + { + Loc = l; + } - public bool IsBlocked() - { - return IsFlagSet(FLAG_BLOCKED); - } + public void Clear() + { + next = null; + chunk = null; + traceBack = null; + } - public bool IsInWater() - { - return IsFlagSet(FLAG_WATER); - } + public bool IsCloseToModel() + { + return IsFlagSet(FLAG_CLOSETOMODEL); + } - public float GetDistanceTo(Vector3 l) - { - return Vector3.Distance(Loc, l); - } + public bool IsBlocked() + { + return IsFlagSet(FLAG_BLOCKED); + } - public float GetDistanceTo(Spot s) - { - return Vector3.Distance(Loc, s.Loc); - } + public bool IsInWater() + { + return IsFlagSet(FLAG_WATER); + } - public float GetDistanceTo2D(Spot s) - { - return Vector2.Distance(Loc.AsVector2(), s.Loc.AsVector2()); - } + public float GetDistanceTo(Vector3 l) + { + return Vector3.Distance(Loc, l); + } - public bool IsCloseZ(float z) - { - float dz = z - Loc.Z; - return dz >= -Z_RESOLUTION && dz <= Z_RESOLUTION; - } + public float GetDistanceTo(Spot s) + { + return Vector3.Distance(Loc, s.Loc); + } - public void SetFlag(uint flag, bool val) - { - uint old = flags; - if (val) - flags |= flag; - else - flags &= ~flag; - if (chunk != null && old != flags) - chunk.modified = true; - } + public float GetDistanceTo2D(Spot s) + { + return Vector2.Distance(Loc.AsVector2(), s.Loc.AsVector2()); + } - public bool IsFlagSet(uint flag) - { - return (flags & flag) != 0; - } + public bool IsCloseZ(float z) + { + float dz = z - Loc.Z; + return dz >= -Z_RESOLUTION && dz <= Z_RESOLUTION; + } - public bool GetPath(int i, out float x, out float y, out float z) - { - if (i > n_paths) - { - x = y = z = 0; - return false; - } + public void SetFlag(uint flag, bool val) + { + uint old = flags; + if (val) + flags |= flag; + else + flags &= ~flag; + if (chunk != null && old != flags) + chunk.modified = true; + } - int off = i * 3; - x = paths[off]; - y = paths[off + 1]; - z = paths[off + 2]; - return true; - } + public bool IsFlagSet(uint flag) + { + return (flags & flag) != 0; + } - public Spot GetToSpot(PathGraph pg, int i) + public bool GetPath(int i, out float x, out float y, out float z) + { + if (i > n_paths) { - GetPath(i, out float x, out float y, out float z); - return pg.GetSpot(x, y, z); + x = y = z = 0; + return false; } - public Span GetPathsToSpots(PathGraph pg) - { - var pooler = ArrayPool.Shared; - Spot[] array = pooler.Rent(n_paths); + int off = i * 3; + x = paths[off]; + y = paths[off + 1]; + z = paths[off + 2]; + return true; + } - int j = 0; - for (int i = 0; i < n_paths; i++) - { - Spot spot = GetToSpot(pg, i); - if (spot != null) - array[j++] = spot; - } + public Spot GetToSpot(PathGraph pg, int i) + { + GetPath(i, out float x, out float y, out float z); + return pg.GetSpot(x, y, z); + } - pooler.Return(array); - return array.AsSpan(0, j); - } + public Span GetPathsToSpots(PathGraph pg) + { + var pooler = ArrayPool.Shared; + Spot[] array = pooler.Rent(n_paths); - public bool HasPathTo(PathGraph pg, Spot s) + int j = 0; + for (int i = 0; i < n_paths; i++) { - for (int i = 0; i < n_paths; i++) - { - Spot to = GetToSpot(pg, i); - if (to == s) - return true; - } - return false; + Spot spot = GetToSpot(pg, i); + if (spot != null) + array[j++] = spot; } - public bool HasPathTo(float x, float y, float z) - { - if (paths == null) - return false; - for (int i = 0; i < n_paths; i++) - { - int off = i * 3; - if (x == paths[off] && - y == paths[off + 1] && - z == paths[off + 2]) - return true; - } - return false; - } + pooler.Return(array); + return array.AsSpan(0, j); + } - public void AddPathTo(Spot s) + public bool HasPathTo(PathGraph pg, Spot s) + { + for (int i = 0; i < n_paths; i++) { - AddPathTo(s.Loc.X, s.Loc.Y, s.Loc.Z); + Spot to = GetToSpot(pg, i); + if (to == s) + return true; } + return false; + } - public void AddPathTo(Vector3 l) + public bool HasPathTo(float x, float y, float z) + { + if (paths == null) + return false; + for (int i = 0; i < n_paths; i++) { - AddPathTo(l.X, l.Y, l.Z); + int off = i * 3; + if (x == paths[off] && + y == paths[off + 1] && + z == paths[off + 2]) + return true; } + return false; + } - public void AddPathTo(float x, float y, float z) - { - if (HasPathTo(x, y, z)) - return; - int old_size; - if (paths == null) - old_size = 0; - else - old_size = paths.Length / 3; - if (n_paths + 1 > old_size) - { - int new_size = old_size * 2; - if (new_size < 4) - new_size = 4; - Array.Resize(ref paths, new_size * 3); - } + public void AddPathTo(Spot s) + { + AddPathTo(s.Loc.X, s.Loc.Y, s.Loc.Z); + } - int off = n_paths * 3; - paths[off] = x; - paths[off + 1] = y; - paths[off + 2] = z; - n_paths++; - if (chunk != null) - chunk.modified = true; - } + public void AddPathTo(Vector3 l) + { + AddPathTo(l.X, l.Y, l.Z); + } - public void RemovePathTo(Vector3 l) + public void AddPathTo(float x, float y, float z) + { + if (HasPathTo(x, y, z)) + return; + int old_size; + if (paths == null) + old_size = 0; + else + old_size = paths.Length / 3; + if (n_paths + 1 > old_size) { - RemovePathTo(l.X, l.Y, l.Z); + int new_size = old_size * 2; + if (new_size < 4) + new_size = 4; + Array.Resize(ref paths, new_size * 3); } - public void RemovePathTo(float x, float y, float z) + int off = n_paths * 3; + paths[off] = x; + paths[off + 1] = y; + paths[off + 2] = z; + n_paths++; + if (chunk != null) + chunk.modified = true; + } + + public void RemovePathTo(Vector3 l) + { + RemovePathTo(l.X, l.Y, l.Z); + } + + public void RemovePathTo(float x, float y, float z) + { + // look for it + int found_index = -1; + for (int i = 0; i < n_paths && found_index == -1; i++) { - // look for it - int found_index = -1; - for (int i = 0; i < n_paths && found_index == -1; i++) - { - int off = i * 3; - if (paths[off] == x && - paths[off + 1] == y && - paths[off + 2] == z) - { - found_index = i; - } - } - if (found_index != -1) + int off = i * 3; + if (paths[off] == x && + paths[off + 1] == y && + paths[off + 2] == z) { - for (int i = found_index; i < n_paths - 1; i++) - { - int off = i * 3; - paths[off] = paths[off + 3]; - paths[off + 1] = paths[off + 4]; - paths[off + 2] = paths[off + 5]; - } - n_paths--; - if (chunk != null) - chunk.modified = true; + found_index = i; } } - - // search stuff - - public bool SetSearchID(int id) + if (found_index != -1) { - if (searchID != id) + for (int i = found_index; i < n_paths - 1; i++) { - closed = false; - scoreSet = false; - searchID = id; - return true; + int off = i * 3; + paths[off] = paths[off + 3]; + paths[off + 1] = paths[off + 4]; + paths[off + 2] = paths[off + 5]; } - return false; + n_paths--; + if (chunk != null) + chunk.modified = true; } + } - public bool SearchIsClosed(int id) - { - return id == searchID && closed; - } + // search stuff - public void SearchClose(int id) + public bool SetSearchID(int id) + { + if (searchID != id) { - SetSearchID(id); - closed = true; + closed = false; + scoreSet = false; + searchID = id; + return true; } + return false; + } - public bool SearchScoreIsSet(int id) - { - return id == searchID && scoreSet; - } + public bool SearchIsClosed(int id) + { + return id == searchID && closed; + } - public float SearchScoreGet(int id) - { - return id == searchID ? score : float.MaxValue; - } + public void SearchClose(int id) + { + SetSearchID(id); + closed = true; + } - public void SearchScoreSet(int id, float score) - { - SetSearchID(id); - this.score = score; - scoreSet = true; - } + public bool SearchScoreIsSet(int id) + { + return id == searchID && scoreSet; + } + + public float SearchScoreGet(int id) + { + return id == searchID ? score : float.MaxValue; + } + + public void SearchScoreSet(int id, float score) + { + SetSearchID(id); + this.score = score; + scoreSet = true; } } \ No newline at end of file diff --git a/PPather/Search/MeshFactory.cs b/PPather/Search/MeshFactory.cs index 7118f2e1f..8200a142e 100644 --- a/PPather/Search/MeshFactory.cs +++ b/PPather/Search/MeshFactory.cs @@ -3,36 +3,35 @@ using System.Collections.Generic; using System.Runtime.InteropServices; -namespace PPather +namespace PPather; + +public static class MeshFactory { - public static class MeshFactory + public static Vector3[] CreatePoints(TriangleCollection collection) { - public static Vector3[] CreatePoints(TriangleCollection collection) + Vector3[] points = new Vector3[collection.VertexCount]; + var span = CollectionsMarshal.AsSpan(collection.Vertecies); + for (int i = 0; i < span.Length; i++) { - Vector3[] points = new Vector3[collection.VertexCount]; - var span = CollectionsMarshal.AsSpan(collection.Vertecies); - for (int i = 0; i < span.Length; i++) - { - points[i] = span[i]; - } - - return points; + points[i] = span[i]; } - public static IEnumerable CreateTriangles(TriangleType modelType, TriangleCollection tc) + return points; + } + + public static IEnumerable CreateTriangles(TriangleType modelType, TriangleCollection tc) + { + List triangles = new(); + for (int i = 0; i < tc.TriangleCount; i++) { - List triangles = new(); - for (int i = 0; i < tc.TriangleCount; i++) - { - tc.GetTriangle(i, out int v0, out int v1, out int v2, out TriangleType flags); - if (flags != modelType) - continue; + tc.GetTriangle(i, out int v0, out int v1, out int v2, out TriangleType flags); + if (flags != modelType) + continue; - triangles.Add(v0); - triangles.Add(v1); - triangles.Add(v2); - } - return triangles; + triangles.Add(v0); + triangles.Add(v1); + triangles.Add(v2); } + return triangles; } } diff --git a/PPather/Search/PPatherService.cs b/PPather/Search/PPatherService.cs index d8675ea28..a8341a02d 100644 --- a/PPather/Search/PPatherService.cs +++ b/PPather/Search/PPatherService.cs @@ -8,179 +8,178 @@ using Microsoft.Extensions.Logging; using System.Numerics; -namespace PPather +namespace PPather; + +public sealed class PPatherService { - public sealed class PPatherService - { - private readonly ILogger logger; - private readonly DataConfig dataConfig; - private readonly WorldMapAreaDB worldMapAreaDB; + private readonly ILogger logger; + private readonly DataConfig dataConfig; + private readonly WorldMapAreaDB worldMapAreaDB; - public event Action SearchBegin; - public event Action OnPathCreated; - public event Action OnChunkAdded; + public event Action SearchBegin; + public event Action OnPathCreated; + public event Action OnChunkAdded; - public Action OnLinesAdded; - public Action OnSphereAdded; + public Action OnLinesAdded; + public Action OnSphereAdded; - private Search search { get; set; } + private Search search { get; set; } - public Vector4 SearchFrom => search.locationFrom; - public Vector4 SearchTo => search.locationTo; - public Vector3 ClosestLocation => search?.PathGraph?.ClosestSpot?.Loc ?? Vector3.Zero; - public Vector3 PeekLocation => search?.PathGraph?.PeekSpot?.Loc ?? Vector3.Zero; + public Vector4 SearchFrom => search.locationFrom; + public Vector4 SearchTo => search.locationTo; + public Vector3 ClosestLocation => search?.PathGraph?.ClosestSpot?.Loc ?? Vector3.Zero; + public Vector3 PeekLocation => search?.PathGraph?.PeekSpot?.Loc ?? Vector3.Zero; - public PPatherService(ILogger logger, DataConfig dataConfig, WorldMapAreaDB worldMapAreaDB) - { - this.dataConfig = dataConfig; - this.logger = logger; - this.worldMapAreaDB = worldMapAreaDB; - ContinentDB.Init(worldMapAreaDB.Values); + public PPatherService(ILogger logger, DataConfig dataConfig, WorldMapAreaDB worldMapAreaDB) + { + this.dataConfig = dataConfig; + this.logger = logger; + this.worldMapAreaDB = worldMapAreaDB; + ContinentDB.Init(worldMapAreaDB.Values); - MPQSelfTest(); + MPQSelfTest(); - ClearTemporaryFiles(); - } + ClearTemporaryFiles(); + } - private void ClearTemporaryFiles() + private void ClearTemporaryFiles() + { + System.IO.DirectoryInfo di = new(dataConfig.PPather); + System.IO.FileInfo[] files = di.GetFiles("*.tmp"); + for (int i = 0; i < files.Length; i++) { - System.IO.DirectoryInfo di = new(dataConfig.PPather); - System.IO.FileInfo[] files = di.GetFiles("*.tmp"); - for (int i = 0; i < files.Length; i++) - { - files[i].Delete(); - } + files[i].Delete(); } + } - public void Reset() + public void Reset() + { + if (search != null) { - if (search != null) - { - search.PathGraph.triangleWorld.NotifyChunkAdded = null; - search.Clear(); - GC.Collect(); - } - search = null; + search.PathGraph.triangleWorld.NotifyChunkAdded = null; + search.Clear(); + GC.Collect(); } + search = null; + } - private void Initialise(float mapId) - { - if (search != null && mapId == search.MapId) - return; + private void Initialise(float mapId) + { + if (search != null && mapId == search.MapId) + return; - PathGraph.SearchEnabled = false; - search = new Search(mapId, logger, dataConfig); - search.PathGraph.triangleWorld.NotifyChunkAdded = ChunkAdded; - } + PathGraph.SearchEnabled = false; + search = new Search(mapId, logger, dataConfig); + search.PathGraph.triangleWorld.NotifyChunkAdded = ChunkAdded; + } - public bool MPQSelfTest() + public bool MPQSelfTest() + { + string[] mpqFiles = MPQTriangleSupplier.GetArchiveNames(dataConfig); + if (mpqFiles.Length == 0) { - string[] mpqFiles = MPQTriangleSupplier.GetArchiveNames(dataConfig); - if (mpqFiles.Length == 0) - { - logger.LogInformation("No MPQ files found, refer to the Readme to download them!"); - return false; - } - - logger.LogInformation("MPQ files exist."); - return true; + logger.LogInformation("No MPQ files found, refer to the Readme to download them!"); + return false; } - public TriangleCollection GetChunkAt(int grid_x, int grid_y) - { - return search.PathGraph.triangleWorld.GetChunkAt(grid_x, grid_y); - } + logger.LogInformation("MPQ files exist."); + return true; + } - public void ChunkAdded(ChunkEventArgs e) - { - OnChunkAdded?.Invoke(e); - } + public TriangleCollection GetChunkAt(int grid_x, int grid_y) + { + return search.PathGraph.triangleWorld.GetChunkAt(grid_x, grid_y); + } - public Vector4 ToWorld(int uiMap, float mapX, float mapY, float z = 0) - { - if (!worldMapAreaDB.TryGet(uiMap, out WorldMapArea wma)) - return Vector4.Zero; + public void ChunkAdded(ChunkEventArgs e) + { + OnChunkAdded?.Invoke(e); + } - float worldX = wma.ToWorldX(mapY); - float worldY = wma.ToWorldY(mapX); + public Vector4 ToWorld(int uiMap, float mapX, float mapY, float z = 0) + { + if (!worldMapAreaDB.TryGet(uiMap, out WorldMapArea wma)) + return Vector4.Zero; - Initialise(wma.MapID); + float worldX = wma.ToWorldX(mapY); + float worldY = wma.ToWorldY(mapX); - return search.CreateWorldLocation(worldX, worldY, z, wma.MapID); - } + Initialise(wma.MapID); - public Vector4 ToWorldZ(int uiMap, float x, float y, float z) - { - if (!worldMapAreaDB.TryGet(uiMap, out WorldMapArea wma)) - return Vector4.Zero; + return search.CreateWorldLocation(worldX, worldY, z, wma.MapID); + } - Initialise(wma.MapID); + public Vector4 ToWorldZ(int uiMap, float x, float y, float z) + { + if (!worldMapAreaDB.TryGet(uiMap, out WorldMapArea wma)) + return Vector4.Zero; - return search.CreateWorldLocation(x, y, z, wma.MapID); - } + Initialise(wma.MapID); - public Vector3 ToLocal(Vector3 world, float mapId, int uiMapId) - { - WorldMapArea wma = worldMapAreaDB.GetWorldMapArea(world.X, world.Y, (int)mapId, uiMapId); - return new Vector3(wma.ToMapY(world.Y), wma.ToMapX(world.X), world.Z); - } + return search.CreateWorldLocation(x, y, z, wma.MapID); + } - public Path DoSearch(PathGraph.eSearchScoreSpot searchType) - { - SearchBegin?.Invoke(); - var path = search.DoSearch(searchType); - OnPathCreated?.Invoke(path); - return path; - } + public Vector3 ToLocal(Vector3 world, float mapId, int uiMapId) + { + WorldMapArea wma = worldMapAreaDB.GetWorldMapArea(world.X, world.Y, (int)mapId, uiMapId); + return new Vector3(wma.ToMapY(world.Y), wma.ToMapX(world.X), world.Z); + } - public void Save() - { - search.PathGraph.Save(); - } + public Path DoSearch(PathGraph.eSearchScoreSpot searchType) + { + SearchBegin?.Invoke(); + var path = search.DoSearch(searchType); + OnPathCreated?.Invoke(path); + return path; + } - public void SetLocations(Vector4 from, Vector4 to) - { - Initialise(from.W); + public void Save() + { + search.PathGraph.Save(); + } - search.locationFrom = from; - search.locationTo = to; - } + public void SetLocations(Vector4 from, Vector4 to) + { + Initialise(from.W); - public List GetCurrentSearchPath() - { - if (search == null || search.PathGraph == null) - { - return null; - } + search.locationFrom = from; + search.locationTo = to; + } - return search.PathGraph.CurrentSearchPath(); + public List GetCurrentSearchPath() + { + if (search == null || search.PathGraph == null) + { + return null; } - public void DrawPath(float mapId, List coords) - { - var first = coords[0]; - var last = coords[^1]; + return search.PathGraph.CurrentSearchPath(); + } - var fromLoc = new Vector4(first[0], first[1], first[2], mapId); - var toLoc = new Vector4(last[0], last[1], last[2], mapId); + public void DrawPath(float mapId, List coords) + { + var first = coords[0]; + var last = coords[^1]; - SetLocations(fromLoc, toLoc); + var fromLoc = new Vector4(first[0], first[1], first[2], mapId); + var toLoc = new Vector4(last[0], last[1], last[2], mapId); - if (search.PathGraph == null) - { - search.CreatePathGraph(mapId); - } + SetLocations(fromLoc, toLoc); - List spots = new(); - for (int i = 0; i < coords.Count; i++) - { - Spot spot = new(new(coords[i][0], coords[i][1], coords[i][2])); - spots.Add(spot); - search.PathGraph.CreateSpotsAroundSpot(spot, false); - } + if (search.PathGraph == null) + { + search.CreatePathGraph(mapId); + } - var path = new Path(spots); - OnPathCreated?.Invoke(path); + List spots = new(); + for (int i = 0; i < coords.Count; i++) + { + Spot spot = new(new(coords[i][0], coords[i][1], coords[i][2])); + spots.Add(spot); + search.PathGraph.CreateSpotsAroundSpot(spot, false); } + + var path = new Path(spots); + OnPathCreated?.Invoke(path); } } \ No newline at end of file diff --git a/PPather/Search/Search.cs b/PPather/Search/Search.cs index cdced8a57..b39723cc8 100644 --- a/PPather/Search/Search.cs +++ b/PPather/Search/Search.cs @@ -5,106 +5,105 @@ using System.Numerics; using SharedLib.Extensions; -namespace PPather +namespace PPather; + +public sealed class Search { - public sealed class Search - { - public PathGraph PathGraph { get; set; } - public float MapId { get; set; } + public PathGraph PathGraph { get; set; } + public float MapId { get; set; } - private readonly DataConfig dataConfig; - private readonly ILogger logger; + private readonly DataConfig dataConfig; + private readonly ILogger logger; - public Vector4 locationFrom { get; set; } - public Vector4 locationTo { get; set; } + public Vector4 locationFrom { get; set; } + public Vector4 locationTo { get; set; } - private const float toonHeight = 2.0f; - private const float toonSize = 0.5f; - private const float howClose = 5f; + private const float toonHeight = 2.0f; + private const float toonSize = 0.5f; + private const float howClose = 5f; - public Search(float mapId, ILogger logger, DataConfig dataConfig) - { - this.logger = logger; - this.MapId = mapId; - this.dataConfig = dataConfig; + public Search(float mapId, ILogger logger, DataConfig dataConfig) + { + this.logger = logger; + this.MapId = mapId; + this.dataConfig = dataConfig; - CreatePathGraph(mapId); - } + CreatePathGraph(mapId); + } + + public void Clear() + { + MapId = 0; + PathGraph.Clear(); + PathGraph = null; + } + + public Vector4 CreateWorldLocation(float x, float y, float z, int mapId) + { + float zTerrain = GetZValueAt(x, y, z, TriangleType.Terrain); + float zWater = GetZValueAt(x, y, z, TriangleType.Water); - public void Clear() + if (zWater > zTerrain) { - MapId = 0; - PathGraph.Clear(); - PathGraph = null; + return new Vector4(x, y, zWater, mapId); } - public Vector4 CreateWorldLocation(float x, float y, float z, int mapId) - { - float zTerrain = GetZValueAt(x, y, z, TriangleType.Terrain); - float zWater = GetZValueAt(x, y, z, TriangleType.Water); + float zModel = GetZValueAt(x, y, z, TriangleType.Model | TriangleType.Object); - if (zWater > zTerrain) - { - return new Vector4(x, y, zWater, mapId); - } + return zModel != float.MinValue + ? MathF.Abs(zModel - zTerrain) > toonHeight / 2 + ? new Vector4(x, y, zTerrain, mapId) + : new Vector4(x, y, zModel, mapId) + : new Vector4(x, y, zTerrain, mapId); + } - float zModel = GetZValueAt(x, y, z, TriangleType.Model | TriangleType.Object); + private float GetZValueAt(float x, float y, float z, TriangleType allowedFlags) + { + float min = -1000; + float max = 2000; - return zModel != float.MinValue - ? MathF.Abs(zModel - zTerrain) > toonHeight / 2 - ? new Vector4(x, y, zTerrain, mapId) - : new Vector4(x, y, zModel, mapId) - : new Vector4(x, y, zTerrain, mapId); + if (z != 0) + { + min = z - 1000; + max = z + 2000; } - private float GetZValueAt(float x, float y, float z, TriangleType allowedFlags) + if (PathGraph.triangleWorld.FindStandableAt1(x, y, min, max, out float z1, out _, toonHeight, toonSize, true, allowedFlags)) { - float min = -1000; - float max = 2000; + return z1; + } - if (z != 0) - { - min = z - 1000; - max = z + 2000; - } + return float.MinValue; + } - if (PathGraph.triangleWorld.FindStandableAt1(x, y, min, max, out float z1, out _, toonHeight, toonSize, true, allowedFlags)) - { - return z1; - } + public void CreatePathGraph(float mapId) + { + this.MapId = mapId; - return float.MinValue; - } + MPQTriangleSupplier mpq = new(logger, dataConfig, mapId); + ChunkedTriangleCollection triangleWorld = new(logger, 64, mpq); + PathGraph = new(mapId, triangleWorld, logger, dataConfig); + } - public void CreatePathGraph(float mapId) - { - this.MapId = mapId; + public Path DoSearch(PathGraph.eSearchScoreSpot searchType) + { + PathGraph.SearchEnabled = true; - MPQTriangleSupplier mpq = new(logger, dataConfig, mapId); - ChunkedTriangleCollection triangleWorld = new(logger, 64, mpq); - PathGraph = new(mapId, triangleWorld, logger, dataConfig); - } + // tell the pathgraph which type of search to do + PathGraph.searchScoreSpot = searchType; - public Path DoSearch(PathGraph.eSearchScoreSpot searchType) + try + { + return PathGraph.CreatePath(locationFrom.AsVector3(), locationTo.AsVector3(), howClose); + } + catch (Exception ex) + { + logger.LogError(ex.Message); + return null; + } + finally { - PathGraph.SearchEnabled = true; - - // tell the pathgraph which type of search to do - PathGraph.searchScoreSpot = searchType; - - try - { - return PathGraph.CreatePath(locationFrom.AsVector3(), locationTo.AsVector3(), howClose); - } - catch (Exception ex) - { - logger.LogError(ex.Message); - return null; - } - finally - { - PathGraph.SearchEnabled = false; - } + PathGraph.SearchEnabled = false; } } } \ No newline at end of file diff --git a/PPather/Search/SearchParam.cs b/PPather/Search/SearchParam.cs index 728eab84b..36877e0dd 100644 --- a/PPather/Search/SearchParam.cs +++ b/PPather/Search/SearchParam.cs @@ -1,12 +1,11 @@ using PPather.Graph; -namespace PPather +namespace PPather; + +public sealed class SearchParam { - public sealed class SearchParam - { - public string Continent { get; set; } - public PathGraph.eSearchScoreSpot SearchType { get; set; } - public SearchLocation From { get; set; } - public SearchLocation To { get; set; } - } + public string Continent { get; set; } + public PathGraph.eSearchScoreSpot SearchType { get; set; } + public SearchLocation From { get; set; } + public SearchLocation To { get; set; } } diff --git a/PPather/Triangles/ChunkEventArgs.cs b/PPather/Triangles/ChunkEventArgs.cs index 149c0c552..08bf670c0 100644 --- a/PPather/Triangles/ChunkEventArgs.cs +++ b/PPather/Triangles/ChunkEventArgs.cs @@ -4,17 +4,16 @@ * */ -namespace WowTriangles +namespace WowTriangles; + +public sealed class ChunkEventArgs { - public sealed class ChunkEventArgs - { - public int GridX { get; } - public int GridY { get; } + public int GridX { get; } + public int GridY { get; } - public ChunkEventArgs(int gridX, int gridY) - { - GridX = gridX; - GridY = gridY; - } + public ChunkEventArgs(int gridX, int gridY) + { + GridX = gridX; + GridY = gridY; } } \ No newline at end of file diff --git a/PPather/Triangles/ChunkedTriangleCollection.cs b/PPather/Triangles/ChunkedTriangleCollection.cs index 23dbb48b5..805b2cbb4 100644 --- a/PPather/Triangles/ChunkedTriangleCollection.cs +++ b/PPather/Triangles/ChunkedTriangleCollection.cs @@ -15,518 +15,517 @@ using PPather.Triangles.Data; using PPather; -namespace WowTriangles +namespace WowTriangles; + +/// +/// A chunked collection of triangles +/// +public sealed class ChunkedTriangleCollection { - /// - /// A chunked collection of triangles - /// - public sealed class ChunkedTriangleCollection - { - private readonly ILogger logger; - private readonly MPQTriangleSupplier supplier; - private readonly SparseMatrix2D chunks; + private readonly ILogger logger; + private readonly MPQTriangleSupplier supplier; + private readonly SparseMatrix2D chunks; - private const int maxCache = 128; - public Action NotifyChunkAdded; + private const int maxCache = 128; + public Action NotifyChunkAdded; - public ChunkedTriangleCollection(ILogger logger, int initCapacity, MPQTriangleSupplier supplier) - { - this.logger = logger; - this.supplier = supplier; - chunks = new SparseMatrix2D(initCapacity); - } + public ChunkedTriangleCollection(ILogger logger, int initCapacity, MPQTriangleSupplier supplier) + { + this.logger = logger; + this.supplier = supplier; + chunks = new SparseMatrix2D(initCapacity); + } - public void Close() - { - supplier.Clear(); - EvictAll(); - } + public void Close() + { + supplier.Clear(); + EvictAll(); + } - public void EvictAll() + public void EvictAll() + { + foreach (TriangleCollection chunk in chunks.GetAllElements()) { - foreach (TriangleCollection chunk in chunks.GetAllElements()) - { - chunk.Clear(); - } - - chunks.Clear(); + chunk.Clear(); } - public static void GetGridStartAt(float x, float y, out int grid_x, out int grid_y) - { - x = ChunkReader.ZEROPOINT - x; - grid_x = (int)(x / ChunkReader.TILESIZE); - y = ChunkReader.ZEROPOINT - y; - grid_y = (int)(y / ChunkReader.TILESIZE); - } + chunks.Clear(); + } - private static void GetGridLimits(int grid_x, int grid_y, - out float min_x, out float min_y, - out float max_x, out float max_y) - { - max_x = ChunkReader.ZEROPOINT - (grid_x * ChunkReader.TILESIZE); - min_x = max_x - ChunkReader.TILESIZE; - max_y = ChunkReader.ZEROPOINT - (grid_y * ChunkReader.TILESIZE); - min_y = max_y - ChunkReader.TILESIZE; - } + public static void GetGridStartAt(float x, float y, out int grid_x, out int grid_y) + { + x = ChunkReader.ZEROPOINT - x; + grid_x = (int)(x / ChunkReader.TILESIZE); + y = ChunkReader.ZEROPOINT - y; + grid_y = (int)(y / ChunkReader.TILESIZE); + } - private void LoadChunkAt(float x, float y) - { - GetGridStartAt(x, y, out int grid_x, out int grid_y); + private static void GetGridLimits(int grid_x, int grid_y, + out float min_x, out float min_y, + out float max_x, out float max_y) + { + max_x = ChunkReader.ZEROPOINT - (grid_x * ChunkReader.TILESIZE); + min_x = max_x - ChunkReader.TILESIZE; + max_y = ChunkReader.ZEROPOINT - (grid_y * ChunkReader.TILESIZE); + min_y = max_y - ChunkReader.TILESIZE; + } - if (chunks.ContainsKey(grid_x, grid_y)) - return; + private void LoadChunkAt(float x, float y) + { + GetGridStartAt(x, y, out int grid_x, out int grid_y); - GetGridLimits(grid_x, grid_y, out float min_x, out float min_y, out float max_x, out float max_y); + if (chunks.ContainsKey(grid_x, grid_y)) + return; - TriangleCollection tc = new(logger); - tc.SetLimits(min_x - 1, min_y - 1, -1E30f, max_x + 1, max_y + 1, 1E30f); + GetGridLimits(grid_x, grid_y, out float min_x, out float min_y, out float max_x, out float max_y); - supplier.GetTriangles(tc, min_x, min_y, max_x, max_y); + TriangleCollection tc = new(logger); + tc.SetLimits(min_x - 1, min_y - 1, -1E30f, max_x + 1, max_y + 1, 1E30f); - chunks.Add(grid_x, grid_y, tc); + supplier.GetTriangles(tc, min_x, min_y, max_x, max_y); - if (logger.IsEnabled(LogLevel.Trace)) - { - logger.LogTrace($"Grid [{grid_x},{grid_y}] Bounds: [{min_x:F4}, {min_y:F4}] [{max_x:F4}, {max_y:F4}] [{x}, {y}] - Count: {chunks.Count}"); - } + chunks.Add(grid_x, grid_y, tc); - NotifyChunkAdded?.Invoke(new ChunkEventArgs(grid_x, grid_y)); + if (logger.IsEnabled(LogLevel.Trace)) + { + logger.LogTrace($"Grid [{grid_x},{grid_y}] Bounds: [{min_x:F4}, {min_y:F4}] [{max_x:F4}, {max_y:F4}] [{x}, {y}] - Count: {chunks.Count}"); } - public TriangleCollection GetChunkAt(float x, float y) - { - LoadChunkAt(x, y); - GetGridStartAt(x, y, out int grid_x, out int grid_y); + NotifyChunkAdded?.Invoke(new ChunkEventArgs(grid_x, grid_y)); + } - return GetChunkAt(grid_x, grid_y); - } + public TriangleCollection GetChunkAt(float x, float y) + { + LoadChunkAt(x, y); + GetGridStartAt(x, y, out int grid_x, out int grid_y); - public TriangleCollection GetChunkAt(int grid_x, int grid_y) - { - return chunks.TryGetValue(grid_x, grid_y, out TriangleCollection tc) - ? tc - : default; - } + return GetChunkAt(grid_x, grid_y); + } - public bool IsSpotBlocked(float x, float y, float z, - float toonHeight, float toonSize) - { - TriangleCollection tc = GetChunkAt(x, y); + public TriangleCollection GetChunkAt(int grid_x, int grid_y) + { + return chunks.TryGetValue(grid_x, grid_y, out TriangleCollection tc) + ? tc + : default; + } - TriangleMatrix tm = tc.GetTriangleMatrix(); - ArraySegment ts = tm.GetAllCloseTo(x, y, toonSize); + public bool IsSpotBlocked(float x, float y, float z, + float toonHeight, float toonSize) + { + TriangleCollection tc = GetChunkAt(x, y); - Vector3 toon = new(x, y, z + toonHeight - toonSize); + TriangleMatrix tm = tc.GetTriangleMatrix(); + ArraySegment ts = tm.GetAllCloseTo(x, y, toonSize); - for (int i = 0; i < ts.Count; i++) - { - int t = ts[i]; + Vector3 toon = new(x, y, z + toonHeight - toonSize); - Vector3 vertex0; - Vector3 vertex1; - Vector3 vertex2; + for (int i = 0; i < ts.Count; i++) + { + int t = ts[i]; - tc.GetTriangleVertices(t, - out vertex0.X, out vertex0.Y, out vertex0.Z, - out vertex1.X, out vertex1.Y, out vertex1.Z, - out vertex2.X, out vertex2.Y, out vertex2.Z, out _); + Vector3 vertex0; + Vector3 vertex1; + Vector3 vertex2; - float d = PointDistanceToTriangle(toon, vertex0, vertex1, vertex2); - if (d < toonSize) - return true; - } + tc.GetTriangleVertices(t, + out vertex0.X, out vertex0.Y, out vertex0.Z, + out vertex1.X, out vertex1.Y, out vertex1.Z, + out vertex2.X, out vertex2.Y, out vertex2.Z, out _); - return false; + float d = PointDistanceToTriangle(toon, vertex0, vertex1, vertex2); + if (d < toonSize) + return true; } - public bool IsStepBlocked(float x0, float y0, float z0, - float x1, float y1, float z1, - float toonHeight, float toonSize) - { - TriangleCollection tc = GetChunkAt(x0, y0); + return false; + } - float dx = x0 - x1; - float dy = y0 - y1; - float dz = z0 - z1; - float stepLength = Sqrt((dx * dx) + (dy * dy) + (dz * dz)); - // 1: check steepness + public bool IsStepBlocked(float x0, float y0, float z0, + float x1, float y1, float z1, + float toonHeight, float toonSize) + { + TriangleCollection tc = GetChunkAt(x0, y0); - // TODO + float dx = x0 - x1; + float dy = y0 - y1; + float dz = z0 - z1; + float stepLength = Sqrt((dx * dx) + (dy * dy) + (dz * dz)); + // 1: check steepness - // 2: check is there is a big step + // TODO - float mid_x = (x0 + x1) / 2.0f; - float mid_y = (y0 + y1) / 2.0f; - float mid_z = (z0 + z1) / 2.0f; - float mid_z_hit; - float mid_dz = Abs(stepLength); - //if (mid_dz < 1.0f) mid_dz = 1.0f; - if (FindStandableAt(mid_x, mid_y, mid_z - mid_dz, mid_z + mid_dz, out mid_z_hit, out _, toonHeight, toonSize)) - { - float dz0 = Abs(z0 - mid_z_hit); - float dz1 = Abs(z1 - mid_z_hit); + // 2: check is there is a big step - // Console.WriteLine("z0 " + z0 + " z1 " + z1 + " dz0 " + dz0+ " dz1 " + dz1 ); - if (dz0 > stepLength / 2.0 && dz0 > 1.0) - return true; // too steep + float mid_x = (x0 + x1) / 2.0f; + float mid_y = (y0 + y1) / 2.0f; + float mid_z = (z0 + z1) / 2.0f; + float mid_z_hit; + float mid_dz = Abs(stepLength); + //if (mid_dz < 1.0f) mid_dz = 1.0f; + if (FindStandableAt(mid_x, mid_y, mid_z - mid_dz, mid_z + mid_dz, out mid_z_hit, out _, toonHeight, toonSize)) + { + float dz0 = Abs(z0 - mid_z_hit); + float dz1 = Abs(z1 - mid_z_hit); - if (dz1 > stepLength / 2.0 && dz1 > 1.0) - return true; // too steep - } - else - { - // bad! - return true; - } + // Console.WriteLine("z0 " + z0 + " z1 " + z1 + " dz0 " + dz0+ " dz1 " + dz1 ); + if (dz0 > stepLength / 2.0 && dz0 > 1.0) + return true; // too steep - TriangleMatrix tm = tc.GetTriangleMatrix(); - ArraySegment ts = tm.GetAllInSquare(Min(x0, x1), Min(y0, y1), Max(x0, x1), Max(y0, y1)); + if (dz1 > stepLength / 2.0 && dz1 > 1.0) + return true; // too steep + } + else + { + // bad! + return true; + } - // 3: check collision with objects + TriangleMatrix tm = tc.GetTriangleMatrix(); + ArraySegment ts = tm.GetAllInSquare(Min(x0, x1), Min(y0, y1), Max(x0, x1), Max(y0, y1)); - Vector3 from, from_up, from_low; - Vector3 to, to_up, to_low; + // 3: check collision with objects - from.X = x0; - from.Y = y0; - from.Z = z0 + toonSize; //+0.5 + Vector3 from, from_up, from_low; + Vector3 to, to_up, to_low; - to.X = x1; - to.Y = y1; - to.Z = z1 + toonSize; + from.X = x0; + from.Y = y0; + from.Z = z0 + toonSize; //+0.5 - from_up = new Vector3(from.X, from.Y, from.Z); - from_up.Z = z0 + toonHeight - toonSize; + to.X = x1; + to.Y = y1; + to.Z = z1 + toonSize; - to_up = new Vector3(to.X, from.Y, from.Z); - to_up.Z = z1 + toonHeight - toonSize; + from_up = new Vector3(from.X, from.Y, from.Z); + from_up.Z = z0 + toonHeight - toonSize; - //diagonal - if (CheckForCollision(tc, ts, from, to_up)) { return true; } + to_up = new Vector3(to.X, from.Y, from.Z); + to_up.Z = z1 + toonHeight - toonSize; - //diagonal - if (CheckForCollision(tc, ts, from_up, to)) { return true; } + //diagonal + if (CheckForCollision(tc, ts, from, to_up)) { return true; } - //head height - // if (CheckForCollision(tc, ts, ref from_up, ref to_up)) { return true; } + //diagonal + if (CheckForCollision(tc, ts, from_up, to)) { return true; } - //close to the ground - from_low = new Vector3(from.X, from.Y, from.Z); - from_low.Z = z0 + 0.2f; - to_low = new Vector3(to.X, to.Y, to.Z); - to_low.Z = z1 + 0.2f; - if (CheckForCollision(tc, ts, from_low, to_low)) { return true; } + //head height + // if (CheckForCollision(tc, ts, ref from_up, ref to_up)) { return true; } - GetNormal(x0, y0, x1, y1, out float ddx, out float ddy, 0.2f); + //close to the ground + from_low = new Vector3(from.X, from.Y, from.Z); + from_low.Z = z0 + 0.2f; + to_low = new Vector3(to.X, to.Y, to.Z); + to_low.Z = z1 + 0.2f; + if (CheckForCollision(tc, ts, from_low, to_low)) { return true; } - from_low.X += ddy; - from_low.Y += ddx; - to_low.X += ddy; - to_low.Y += ddx; - if (CheckForCollision(tc, ts, from_low, to_low)) { return true; } + GetNormal(x0, y0, x1, y1, out float ddx, out float ddy, 0.2f); - from_low.X -= 2 * ddy; - from_low.Y -= 2 * ddx; - to_low.X -= 2 * ddy; - to_low.Y -= 2 * ddx; + from_low.X += ddy; + from_low.Y += ddx; + to_low.X += ddy; + to_low.Y += ddx; + if (CheckForCollision(tc, ts, from_low, to_low)) { return true; } - return CheckForCollision(tc, ts, from_low, to_low); - } + from_low.X -= 2 * ddy; + from_low.Y -= 2 * ddx; + to_low.X -= 2 * ddy; + to_low.Y -= 2 * ddx; - public static void GetNormal(float x1, float y1, float x2, float y2, out float dx, out float dy, float factor) - { - dx = x2 - x1; - dy = y2 - y1; + return CheckForCollision(tc, ts, from_low, to_low); + } - if (Abs(dx) > Abs(dy)) - { - dy /= dx; - dx = 1; - } - else - { - dx /= dy; - dy = 1; - } + public static void GetNormal(float x1, float y1, float x2, float y2, out float dx, out float dy, float factor) + { + dx = x2 - x1; + dy = y2 - y1; - dx *= factor; - dy *= factor; + if (Abs(dx) > Abs(dy)) + { + dy /= dx; + dx = 1; } + else + { + dx /= dy; + dy = 1; + } + + dx *= factor; + dy *= factor; + } - private static bool CheckForCollision(TriangleCollection tc, ArraySegment ts, in Vector3 from, in Vector3 to) + private static bool CheckForCollision(TriangleCollection tc, ArraySegment ts, in Vector3 from, in Vector3 to) + { + for (int i = 0; i < ts.Count; i++) { - for (int i = 0; i < ts.Count; i++) - { - int t = ts[i]; + int t = ts[i]; - Vector3 vertex0; - Vector3 vertex1; - Vector3 vertex2; + Vector3 vertex0; + Vector3 vertex1; + Vector3 vertex2; - tc.GetTriangleVertices(t, - out vertex0.X, out vertex0.Y, out vertex0.Z, - out vertex1.X, out vertex1.Y, out vertex1.Z, - out vertex2.X, out vertex2.Y, out vertex2.Z); + tc.GetTriangleVertices(t, + out vertex0.X, out vertex0.Y, out vertex0.Z, + out vertex1.X, out vertex1.Y, out vertex1.Z, + out vertex2.X, out vertex2.Y, out vertex2.Z); - if (SegmentTriangleIntersect(from, to, vertex0, vertex1, vertex2, out _)) - { - return true; - } + if (SegmentTriangleIntersect(from, to, vertex0, vertex1, vertex2, out _)) + { + return true; } - return false; } + return false; + } - public bool FindStandableAt(float x, float y, float min_z, float max_z, - out float z0, out TriangleType flags, float toonHeight, float toonSize) - { - return FindStandableAt1(x, y, min_z, max_z, out z0, out flags, toonHeight, toonSize, false, TriangleType.Terrain); - } + public bool FindStandableAt(float x, float y, float min_z, float max_z, + out float z0, out TriangleType flags, float toonHeight, float toonSize) + { + return FindStandableAt1(x, y, min_z, max_z, out z0, out flags, toonHeight, toonSize, false, TriangleType.Terrain); + } - public bool IsInWater(float x, float y, float min_z, float max_z) - { - TriangleCollection tc = GetChunkAt(x, y); - TriangleMatrix tm = tc.GetTriangleMatrix(); - ArraySegment ts = tm.GetAllCloseTo(x, y, 1.0f); + public bool IsInWater(float x, float y, float min_z, float max_z) + { + TriangleCollection tc = GetChunkAt(x, y); + TriangleMatrix tm = tc.GetTriangleMatrix(); + ArraySegment ts = tm.GetAllCloseTo(x, y, 1.0f); - Vector3 s0 = new(x, y, min_z); - Vector3 s1 = new(x, y, max_z); + Vector3 s0 = new(x, y, min_z); + Vector3 s1 = new(x, y, max_z); - for (int i = 0; i < ts.Count; i++) - { - int t = ts[i]; + for (int i = 0; i < ts.Count; i++) + { + int t = ts[i]; - Vector3 vertex0; - Vector3 vertex1; - Vector3 vertex2; + Vector3 vertex0; + Vector3 vertex1; + Vector3 vertex2; - tc.GetTriangleVertices(t, - out vertex0.X, out vertex0.Y, out vertex0.Z, - out vertex1.X, out vertex1.Y, out vertex1.Z, - out vertex2.X, out vertex2.Y, out vertex2.Z, out TriangleType t_flags); + tc.GetTriangleVertices(t, + out vertex0.X, out vertex0.Y, out vertex0.Z, + out vertex1.X, out vertex1.Y, out vertex1.Z, + out vertex2.X, out vertex2.Y, out vertex2.Z, out TriangleType t_flags); - GetTriangleNormal(vertex0, vertex1, vertex2, out _); + GetTriangleNormal(vertex0, vertex1, vertex2, out _); - if (SegmentTriangleIntersect(s0, s1, vertex0, vertex1, vertex2, out _)) + if (SegmentTriangleIntersect(s0, s1, vertex0, vertex1, vertex2, out _)) + { + if ((t_flags & TriangleType.Water) != 0) { - if ((t_flags & TriangleType.Water) != 0) - { - return true; - } + return true; } } - return false; } + return false; + } - public int GradiantScore(float x, float y, float z, float range) - { - TriangleCollection tc = GetChunkAt(x, y); - TriangleMatrix tm = tc.GetTriangleMatrix(); - - float maxZ = float.MinValue; - float minZ = float.MaxValue; + public int GradiantScore(float x, float y, float z, float range) + { + TriangleCollection tc = GetChunkAt(x, y); + TriangleMatrix tm = tc.GetTriangleMatrix(); - ArraySegment array = tm.GetAllCloseTo(x, y, range); - for (int i = 0; i < array.Count; i++) - { - int t = array[i]; + float maxZ = float.MinValue; + float minZ = float.MaxValue; - Vector3 vertex0; - Vector3 vertex1; - Vector3 vertex2; + ArraySegment array = tm.GetAllCloseTo(x, y, range); + for (int i = 0; i < array.Count; i++) + { + int t = array[i]; - tc.GetTriangleVertices(t, - out vertex0.X, out vertex0.Y, out vertex0.Z, - out vertex1.X, out vertex1.Y, out vertex1.Z, - out vertex2.X, out vertex2.Y, out vertex2.Z, out TriangleType flags); + Vector3 vertex0; + Vector3 vertex1; + Vector3 vertex2; - if (flags == TriangleType.Terrain) - { - if (vertex0.Z > maxZ) { maxZ = vertex0.Z; } - if (vertex2.Z > maxZ) { maxZ = vertex1.Z; } - if (vertex1.Z > maxZ) { maxZ = vertex2.Z; } + tc.GetTriangleVertices(t, + out vertex0.X, out vertex0.Y, out vertex0.Z, + out vertex1.X, out vertex1.Y, out vertex1.Z, + out vertex2.X, out vertex2.Y, out vertex2.Z, out TriangleType flags); - if (vertex0.Z < minZ) { minZ = vertex0.Z; } - if (vertex2.Z < minZ) { minZ = vertex1.Z; } - if (vertex1.Z < minZ) { minZ = vertex2.Z; } - } - } - int g = (int)(maxZ - minZ); - if (g > 10) + if (flags == TriangleType.Terrain) { - g = 10; + if (vertex0.Z > maxZ) { maxZ = vertex0.Z; } + if (vertex2.Z > maxZ) { maxZ = vertex1.Z; } + if (vertex1.Z > maxZ) { maxZ = vertex2.Z; } + + if (vertex0.Z < minZ) { minZ = vertex0.Z; } + if (vertex2.Z < minZ) { minZ = vertex1.Z; } + if (vertex1.Z < minZ) { minZ = vertex2.Z; } } - return g; } + int g = (int)(maxZ - minZ); + if (g > 10) + { + g = 10; + } + return g; + } + + public bool IsCloseToModel(float x, float y, float z, float range) + { + TriangleCollection tc = GetChunkAt(x, y); + TriangleMatrix tm = tc.GetTriangleMatrix(); - public bool IsCloseToModel(float x, float y, float z, float range) + ArraySegment array = tm.GetAllCloseTo(x, y, range); + for (int i = 0; i < array.Count; i++) { - TriangleCollection tc = GetChunkAt(x, y); - TriangleMatrix tm = tc.GetTriangleMatrix(); + int t = array[i]; - ArraySegment array = tm.GetAllCloseTo(x, y, range); - for (int i = 0; i < array.Count; i++) - { - int t = array[i]; + Vector3 vertex0; + Vector3 vertex1; + Vector3 vertex2; - Vector3 vertex0; - Vector3 vertex1; - Vector3 vertex2; + tc.GetTriangleVertices(t, + out vertex0.X, out vertex0.Y, out vertex0.Z, + out vertex1.X, out vertex1.Y, out vertex1.Z, + out vertex2.X, out vertex2.Y, out vertex2.Z, out TriangleType flags); - tc.GetTriangleVertices(t, - out vertex0.X, out vertex0.Y, out vertex0.Z, - out vertex1.X, out vertex1.Y, out vertex1.Z, - out vertex2.X, out vertex2.Y, out vertex2.Z, out TriangleType flags); + //check triangle is part of a model + if ((flags & TriangleType.Object) != 0 || (flags & TriangleType.Model) != 0) + { + float minHeight = 0.75f; + float height = 2; - //check triangle is part of a model - if ((flags & TriangleType.Object) != 0 || (flags & TriangleType.Model) != 0) + //and the vertex is close to the char + if ((vertex0.Z > z + minHeight && vertex0.Z < z + height) || + (vertex1.Z > z + minHeight && vertex1.Z < z + height) || + (vertex2.Z > z + minHeight && vertex2.Z < z + height)) { - float minHeight = 0.75f; - float height = 2; - - //and the vertex is close to the char - if ((vertex0.Z > z + minHeight && vertex0.Z < z + height) || - (vertex1.Z > z + minHeight && vertex1.Z < z + height) || - (vertex2.Z > z + minHeight && vertex2.Z < z + height)) - { - return true; - } + return true; } } - return false; } + return false; + } - public bool LineOfSightExists(Spot a, Spot b) - { - TriangleCollection tc = GetChunkAt(a.Loc.X, a.Loc.Y); - TriangleMatrix tm = tc.GetTriangleMatrix(); - ICollection ts = tm.GetAllCloseTo(a.Loc.X, a.Loc.Y, a.GetDistanceTo(b) + 1); + public bool LineOfSightExists(Spot a, Spot b) + { + TriangleCollection tc = GetChunkAt(a.Loc.X, a.Loc.Y); + TriangleMatrix tm = tc.GetTriangleMatrix(); + ICollection ts = tm.GetAllCloseTo(a.Loc.X, a.Loc.Y, a.GetDistanceTo(b) + 1); - Vector3 s0 = a.Loc; - Vector3 s1 = b.Loc; + Vector3 s0 = a.Loc; + Vector3 s1 = b.Loc; - foreach (int t in ts) - { - Vector3 vertex0; - Vector3 vertex1; - Vector3 vertex2; + foreach (int t in ts) + { + Vector3 vertex0; + Vector3 vertex1; + Vector3 vertex2; - tc.GetTriangleVertices(t, - out vertex0.X, out vertex0.Y, out vertex0.Z, - out vertex1.X, out vertex1.Y, out vertex1.Z, - out vertex2.X, out vertex2.Y, out vertex2.Z, out _); + tc.GetTriangleVertices(t, + out vertex0.X, out vertex0.Y, out vertex0.Z, + out vertex1.X, out vertex1.Y, out vertex1.Z, + out vertex2.X, out vertex2.Y, out vertex2.Z, out _); - if (SegmentTriangleIntersect(s0, s1, vertex0, vertex1, vertex2, out _)) - { - return false; - } + if (SegmentTriangleIntersect(s0, s1, vertex0, vertex1, vertex2, out _)) + { + return false; } - return true; } + return true; + } - public bool FindStandableAt1(float x, float y, float min_z, float max_z, - out float z0, out TriangleType flags, - float toonHeight, float toonSize, - bool IgnoreGradient, TriangleType allowedFlags) - { - const float minCliffD = 0.5f; + public bool FindStandableAt1(float x, float y, float min_z, float max_z, + out float z0, out TriangleType flags, + float toonHeight, float toonSize, + bool IgnoreGradient, TriangleType allowedFlags) + { + const float minCliffD = 0.5f; - TriangleCollection tc = GetChunkAt(x, y); - TriangleMatrix tm = tc.GetTriangleMatrix(); - ArraySegment ts = tm.GetAllCloseTo(x, y, 1.0f); + TriangleCollection tc = GetChunkAt(x, y); + TriangleMatrix tm = tc.GetTriangleMatrix(); + ArraySegment ts = tm.GetAllCloseTo(x, y, 1.0f); - Vector3 s0 = new(x, y, min_z); - Vector3 s1 = new(x, y, max_z); + Vector3 s0 = new(x, y, min_z); + Vector3 s1 = new(x, y, max_z); - float best_z = -1E30f; - TriangleType best_flags = TriangleType.Terrain; - bool found = false; + float best_z = -1E30f; + TriangleType best_flags = TriangleType.Terrain; + bool found = false; - for (int i = 0; i < ts.Count; i++) - { - int t = ts[i]; + for (int i = 0; i < ts.Count; i++) + { + int t = ts[i]; - Vector3 vertex0; - Vector3 vertex1; - Vector3 vertex2; + Vector3 vertex0; + Vector3 vertex1; + Vector3 vertex2; - tc.GetTriangleVertices(t, - out vertex0.X, out vertex0.Y, out vertex0.Z, - out vertex1.X, out vertex1.Y, out vertex1.Z, - out vertex2.X, out vertex2.Y, out vertex2.Z, out TriangleType t_flags); + tc.GetTriangleVertices(t, + out vertex0.X, out vertex0.Y, out vertex0.Z, + out vertex1.X, out vertex1.Y, out vertex1.Z, + out vertex2.X, out vertex2.Y, out vertex2.Z, out TriangleType t_flags); - if (allowedFlags == TriangleType.Terrain || allowedFlags.Has(t_flags)) + if (allowedFlags == TriangleType.Terrain || allowedFlags.Has(t_flags)) + { + GetTriangleNormal(vertex0, vertex1, vertex2, out Vector3 normal); + float angle_z = Sin(30f / 360.0f * PI * 2f); // 45f -> 40 degree || 60f -> 50 degree || 30f -> 28.6 degree + if (Abs(normal.Z) > angle_z) { - GetTriangleNormal(vertex0, vertex1, vertex2, out Vector3 normal); - float angle_z = Sin(30f / 360.0f * PI * 2f); // 45f -> 40 degree || 60f -> 50 degree || 30f -> 28.6 degree - if (Abs(normal.Z) > angle_z) + if (SegmentTriangleIntersect(s0, s1, vertex0, vertex1, vertex2, out Vector3 intersect)) { - if (SegmentTriangleIntersect(s0, s1, vertex0, vertex1, vertex2, out Vector3 intersect)) + if (intersect.Z > best_z && + !IsSpotBlocked(intersect.X, intersect.Y, intersect.Z, toonHeight, toonSize)) { - if (intersect.Z > best_z && - !IsSpotBlocked(intersect.X, intersect.Y, intersect.Z, toonHeight, toonSize)) - { - best_z = intersect.Z; - best_flags = t_flags; - found = true; - } + best_z = intersect.Z; + best_flags = t_flags; + found = true; } } } } - if (found) + } + if (found) + { + Vector3 up, dn; + up.Z = best_z + 2; + dn.Z = best_z - 5; + + const int size = 4; + Span nearCliff = stackalloc bool[size] { true, true, true, true }; + Span dx = stackalloc float[size] { minCliffD, -minCliffD, 0, 0 }; + Span dy = stackalloc float[size] { 0, 0, minCliffD, -minCliffD }; + + bool allGood; + for (int j = 0; j < ts.Count; j++) { - Vector3 up, dn; - up.Z = best_z + 2; - dn.Z = best_z - 5; + int t = ts[j]; + Vector3 vertex0; + Vector3 vertex1; + Vector3 vertex2; - const int size = 4; - Span nearCliff = stackalloc bool[size] { true, true, true, true }; - Span dx = stackalloc float[size] { minCliffD, -minCliffD, 0, 0 }; - Span dy = stackalloc float[size] { 0, 0, minCliffD, -minCliffD }; + tc.GetTriangleVertices(t, + out vertex0.X, out vertex0.Y, out vertex0.Z, + out vertex1.X, out vertex1.Y, out vertex1.Z, + out vertex2.X, out vertex2.Y, out vertex2.Z); - bool allGood; - for (int j = 0; j < ts.Count; j++) + allGood = true; + for (int i = 0; i < size; i++) { - int t = ts[j]; - Vector3 vertex0; - Vector3 vertex1; - Vector3 vertex2; - - tc.GetTriangleVertices(t, - out vertex0.X, out vertex0.Y, out vertex0.Z, - out vertex1.X, out vertex1.Y, out vertex1.Z, - out vertex2.X, out vertex2.Y, out vertex2.Z); - - allGood = true; - for (int i = 0; i < size; i++) + if (nearCliff[i]) { - if (nearCliff[i]) - { - up.X = dn.X = x + dx[i]; - up.Y = dn.Y = y + dy[i]; - if (SegmentTriangleIntersect(up, dn, vertex0, vertex1, vertex2, out _)) - nearCliff[i] = false; - } - allGood &= !nearCliff[i]; + up.X = dn.X = x + dx[i]; + up.Y = dn.Y = y + dy[i]; + if (SegmentTriangleIntersect(up, dn, vertex0, vertex1, vertex2, out _)) + nearCliff[i] = false; } - if (allGood) - break; - } - - allGood = true; - for (int i = 0; i < size; i++) allGood &= !nearCliff[i]; - if (!allGood) - { - z0 = best_z; - flags = best_flags; - return false; // too close to cliff } + if (allGood) + break; + } + + allGood = true; + for (int i = 0; i < size; i++) + allGood &= !nearCliff[i]; + if (!allGood) + { + z0 = best_z; + flags = best_flags; + return false; // too close to cliff } - z0 = best_z; - flags = best_flags; - return found; } + z0 = best_z; + flags = best_flags; + return found; } } \ No newline at end of file diff --git a/PPather/Triangles/Data/Matrix4.cs b/PPather/Triangles/Data/Matrix4.cs index 371b7ee50..c0da7c50a 100644 --- a/PPather/Triangles/Data/Matrix4.cs +++ b/PPather/Triangles/Data/Matrix4.cs @@ -1,35 +1,34 @@ using System.Numerics; -namespace PPather.Triangles.Data +namespace PPather.Triangles.Data; + +public struct Matrix4 { - public struct Matrix4 - { - private readonly float[,] m = new float[4, 4]; + private readonly float[,] m = new float[4, 4]; - public Matrix4() { } + public Matrix4() { } - public void makeQuaternionRotate(Quaternion q) - { - m[0, 0] = 1.0f - 2.0f * q.Y * q.Y - 2.0f * q.Z * q.Z; - m[0, 1] = 2.0f * q.X * q.Y + 2.0f * q.W * q.Z; - m[0, 2] = 2.0f * q.X * q.Z - 2.0f * q.W * q.Y; - m[1, 0] = 2.0f * q.X * q.Y - 2.0f * q.W * q.Z; - m[1, 1] = 1.0f - 2.0f * q.X * q.X - 2.0f * q.Z * q.Z; - m[1, 2] = 2.0f * q.Y * q.Z + 2.0f * q.W * q.X; - m[2, 0] = 2.0f * q.X * q.Z + 2.0f * q.W * q.Y; - m[2, 1] = 2.0f * q.Y * q.Z - 2.0f * q.W * q.X; - m[2, 2] = 1.0f - 2.0f * q.X * q.X - 2.0f * q.Y * q.Y; - m[0, 3] = m[1, 3] = m[2, 3] = m[3, 0] = m[3, 1] = m[3, 2] = 0; - m[3, 3] = 1.0f; - } + public void makeQuaternionRotate(Quaternion q) + { + m[0, 0] = 1.0f - 2.0f * q.Y * q.Y - 2.0f * q.Z * q.Z; + m[0, 1] = 2.0f * q.X * q.Y + 2.0f * q.W * q.Z; + m[0, 2] = 2.0f * q.X * q.Z - 2.0f * q.W * q.Y; + m[1, 0] = 2.0f * q.X * q.Y - 2.0f * q.W * q.Z; + m[1, 1] = 1.0f - 2.0f * q.X * q.X - 2.0f * q.Z * q.Z; + m[1, 2] = 2.0f * q.Y * q.Z + 2.0f * q.W * q.X; + m[2, 0] = 2.0f * q.X * q.Z + 2.0f * q.W * q.Y; + m[2, 1] = 2.0f * q.Y * q.Z - 2.0f * q.W * q.X; + m[2, 2] = 1.0f - 2.0f * q.X * q.X - 2.0f * q.Y * q.Y; + m[0, 3] = m[1, 3] = m[2, 3] = m[3, 0] = m[3, 1] = m[3, 2] = 0; + m[3, 3] = 1.0f; + } - public Vector3 mutiply(Vector3 v) - { - Vector3 o; - o.X = m[0, 0] * v.X + m[0, 1] * v.Y + m[0, 2] * v.Z + m[0, 3]; - o.Y = m[1, 0] * v.X + m[1, 1] * v.Y + m[1, 2] * v.Z + m[1, 3]; - o.Z = m[2, 0] * v.X + m[2, 1] * v.Y + m[2, 2] * v.Z + m[2, 3]; - return o; - } + public Vector3 mutiply(Vector3 v) + { + Vector3 o; + o.X = m[0, 0] * v.X + m[0, 1] * v.Y + m[0, 2] * v.Z + m[0, 3]; + o.Y = m[1, 0] * v.X + m[1, 1] * v.Y + m[1, 2] * v.Z + m[1, 3]; + o.Z = m[2, 0] * v.X + m[2, 1] * v.Y + m[2, 2] * v.Z + m[2, 3]; + return o; } } diff --git a/PPather/Triangles/Data/SparseFloatMatrix2D.cs b/PPather/Triangles/Data/SparseFloatMatrix2D.cs index f99669abc..04f4aacc0 100644 --- a/PPather/Triangles/Data/SparseFloatMatrix2D.cs +++ b/PPather/Triangles/Data/SparseFloatMatrix2D.cs @@ -2,71 +2,69 @@ using static System.MathF; -namespace PPather.Triangles.Data +namespace PPather.Triangles.Data; + +public sealed class SparseFloatMatrix2D : SparseMatrix2D { - public sealed class SparseFloatMatrix2D : SparseMatrix2D - { - private const float offset = 100000f; - private readonly float gridSize; + private const float offset = 100000f; + private readonly float gridSize; - public SparseFloatMatrix2D(float gridSize) : base(0) - { - this.gridSize = gridSize; - } + public SparseFloatMatrix2D(float gridSize) : base(0) + { + this.gridSize = gridSize; + } - public SparseFloatMatrix2D(float gridSize, int initialCapazity) - : base(initialCapazity) - { - this.gridSize = gridSize; - } + public SparseFloatMatrix2D(float gridSize, int initialCapazity) + : base(initialCapazity) + { + this.gridSize = gridSize; + } - public float GridToLocal(int grid) - { - return (grid * gridSize) - offset; - } + public float GridToLocal(int grid) + { + return (grid * gridSize) - offset; + } - public int LocalToGrid(float f) - { - return (int)((f + offset) / gridSize); - } + public int LocalToGrid(float f) + { + return (int)((f + offset) / gridSize); + } - public (T[], int) GetAllInSquare(float min_x, float min_y, - float max_x, float max_y) - { - int sx = LocalToGrid(min_x); - int ex = LocalToGrid(max_x); + public (T[], int) GetAllInSquare(float min_x, float min_y, + float max_x, float max_y) + { + int sx = LocalToGrid(min_x); + int ex = LocalToGrid(max_x); - int sy = LocalToGrid(min_y); - int ey = LocalToGrid(max_y); + int sy = LocalToGrid(min_y); + int ey = LocalToGrid(max_y); - var pooler = ArrayPool.Shared; - var l = pooler.Rent((int)Ceiling(((ex - sx + 1) * gridSize) + ((ey - sy + 1) * gridSize))); + var pooler = ArrayPool.Shared; + var l = pooler.Rent((int)Ceiling(((ex - sx + 1) * gridSize) + ((ey - sy + 1) * gridSize))); - int i = 0; - for (int x = sx; x <= ex; x++) + int i = 0; + for (int x = sx; x <= ex; x++) + { + for (int y = sy; y <= ey; y++) { - for (int y = sy; y <= ey; y++) + if (base.TryGetValue(x, y, out T t)) { - if (base.TryGetValue(x, y, out T t)) - { - l[i++] = t ?? default; - } + l[i++] = t ?? default; } } - - pooler.Return(l); - return (l, i); } - public bool TryGetValue(float x, float y, out T t) - { - return base.TryGetValue(LocalToGrid(x), LocalToGrid(y), out t); - } + pooler.Return(l); + return (l, i); + } - public void Add(float x, float y, T val) - { - base.Add(LocalToGrid(x), LocalToGrid(y), val); - } + public bool TryGetValue(float x, float y, out T t) + { + return base.TryGetValue(LocalToGrid(x), LocalToGrid(y), out t); } + public void Add(float x, float y, T val) + { + base.Add(LocalToGrid(x), LocalToGrid(y), val); + } } diff --git a/PPather/Triangles/Data/SparseMatrix2D.cs b/PPather/Triangles/Data/SparseMatrix2D.cs index 7fdb0370e..e26c5cd7b 100644 --- a/PPather/Triangles/Data/SparseMatrix2D.cs +++ b/PPather/Triangles/Data/SparseMatrix2D.cs @@ -1,47 +1,45 @@ using System.Collections.Generic; -namespace PPather.Triangles.Data +namespace PPather.Triangles.Data; + +public class SparseMatrix2D { - public class SparseMatrix2D + private readonly Dictionary dict; + + public int Count => dict.Count; + + public SparseMatrix2D(int initialCapacity) + { + dict = new(initialCapacity); + } + + public bool ContainsKey(int x, int y) + { + return dict.ContainsKey((y << 16) ^ x); + } + + public bool TryGetValue(int x, int y, out T r) + { + return dict.TryGetValue((y << 16) ^ x, out r); + } + + public void Add(int x, int y, T val) + { + dict[(y << 16) ^ x] = val; + } + + public void Remove(int x, int y) { - private readonly Dictionary dict; - - public int Count => dict.Count; - - public SparseMatrix2D(int initialCapacity) - { - dict = new(initialCapacity); - } - - public bool ContainsKey(int x, int y) - { - return dict.ContainsKey((y << 16) ^ x); - } - - public bool TryGetValue(int x, int y, out T r) - { - return dict.TryGetValue((y << 16) ^ x, out r); - } - - public void Add(int x, int y, T val) - { - dict[(y << 16) ^ x] = val; - } - - public void Remove(int x, int y) - { - dict.Remove((y << 16) ^ x); - } - - public void Clear() - { - dict.Clear(); - } - - public ICollection GetAllElements() - { - return dict.Values; - } + dict.Remove((y << 16) ^ x); } + public void Clear() + { + dict.Clear(); + } + + public ICollection GetAllElements() + { + return dict.Values; + } } diff --git a/PPather/Triangles/Data/SparseMatrix3D.cs b/PPather/Triangles/Data/SparseMatrix3D.cs index 6b633f1e8..ec8a64db2 100644 --- a/PPather/Triangles/Data/SparseMatrix3D.cs +++ b/PPather/Triangles/Data/SparseMatrix3D.cs @@ -1,30 +1,29 @@ using System.Collections.Generic; -namespace PPather.Triangles.Data +namespace PPather.Triangles.Data; + +public class SparseMatrix3D { - public class SparseMatrix3D - { - private readonly Dictionary<(int, int, int), T> dict = new(); + private readonly Dictionary<(int, int, int), T> dict = new(); - public T Get(int x, int y, int z) - { - dict.TryGetValue((x, y, z), out T r); - return r; - } + public T Get(int x, int y, int z) + { + dict.TryGetValue((x, y, z), out T r); + return r; + } - public bool ContainsKey(int x, int y, int z) - { - return dict.ContainsKey((x, y, z)); - } + public bool ContainsKey(int x, int y, int z) + { + return dict.ContainsKey((x, y, z)); + } - public void Add(int x, int y, int z, T val) - { - dict[(x, y, z)] = val; - } + public void Add(int x, int y, int z, T val) + { + dict[(x, y, z)] = val; + } - public void Remove(int x, int y, int z) - { - dict.Remove((x, y, z)); - } + public void Remove(int x, int y, int z) + { + dict.Remove((x, y, z)); } } diff --git a/PPather/Triangles/Data/Triangle.cs b/PPather/Triangles/Data/Triangle.cs index fb71269bb..1874db188 100644 --- a/PPather/Triangles/Data/Triangle.cs +++ b/PPather/Triangles/Data/Triangle.cs @@ -1,26 +1,25 @@ -namespace PPather.Triangles +namespace PPather.Triangles; + +public readonly struct Triangle { - public readonly struct Triangle - { - private readonly T v0; - private readonly T v1; - private readonly T v2; - private readonly TriangleType Flags; + private readonly T v0; + private readonly T v1; + private readonly T v2; + private readonly TriangleType Flags; - public Triangle(T v0, T v1, T v2, TriangleType flags) - { - this.v0 = v0; - this.v1 = v1; - this.v2 = v2; - Flags = flags; - } + public Triangle(T v0, T v1, T v2, TriangleType flags) + { + this.v0 = v0; + this.v1 = v1; + this.v2 = v2; + Flags = flags; + } - public void Deconstruct(out T v0, out T v1, out T v2, out TriangleType flags) - { - v0 = this.v0; - v1 = this.v1; - v2 = this.v2; - flags = Flags; - } + public void Deconstruct(out T v0, out T v1, out T v2, out TriangleType flags) + { + v0 = this.v0; + v1 = this.v1; + v2 = this.v2; + flags = Flags; } } diff --git a/PPather/Triangles/Data/TriangleType.cs b/PPather/Triangles/Data/TriangleType.cs index 4711b89ae..a8e97ab93 100644 --- a/PPather/Triangles/Data/TriangleType.cs +++ b/PPather/Triangles/Data/TriangleType.cs @@ -1,20 +1,18 @@ -namespace PPather +namespace PPather; + +[System.Flags] +public enum TriangleType : byte { - [System.Flags] - public enum TriangleType : byte - { - Terrain = 0, - Water = 1, - Object = 2, - Model = 4, - } + Terrain = 0, + Water = 1, + Object = 2, + Model = 4, +} - public static class TriangleType_Ext +public static class TriangleType_Ext +{ + public static bool Has(this TriangleType flags, TriangleType flag) { - public static bool Has(this TriangleType flags, TriangleType flag) - { - return (flags & flag) != 0; - } + return (flags & flag) != 0; } - } diff --git a/PPather/Triangles/MPQTriangleSupplier.cs b/PPather/Triangles/MPQTriangleSupplier.cs index 5c7332538..091b956b2 100644 --- a/PPather/Triangles/MPQTriangleSupplier.cs +++ b/PPather/Triangles/MPQTriangleSupplier.cs @@ -29,405 +29,405 @@ Copyright Pontus Borg 2008 using System.Buffers; using PPather; -namespace WowTriangles +namespace WowTriangles; + +public sealed class MPQTriangleSupplier { - public sealed class MPQTriangleSupplier + private readonly ILogger logger; + private readonly StormDll.ArchiveSet archive; + private readonly ModelManager modelmanager; + private readonly WMOManager wmomanager; + private readonly WDT wdt; + private readonly WDTFile wdtf; + + private readonly float mapId; + + public MPQTriangleSupplier(ILogger logger, DataConfig dataConfig, float mapId) { - private readonly ILogger logger; - private readonly StormDll.ArchiveSet archive; - private readonly ModelManager modelmanager; - private readonly WMOManager wmomanager; - private readonly WDT wdt; - private readonly WDTFile wdtf; + this.logger = logger; + this.mapId = mapId; + + archive = new StormDll.ArchiveSet(this.logger, GetArchiveNames(dataConfig)); + modelmanager = new ModelManager(archive, 160, dataConfig); + wmomanager = new WMOManager(archive, modelmanager, 120, dataConfig); - private readonly float mapId; + wdt = new WDT(); + wdtf = new WDTFile(archive, this.mapId, wdt, wmomanager, modelmanager, this.logger, dataConfig); - public MPQTriangleSupplier(ILogger logger, DataConfig dataConfig, float mapId) + // TODO: move this to WDTFile + if (!wdtf.loaded) { - this.logger = logger; - this.mapId = mapId; + wdt = null; // bad + throw new Exception("Failed to set continent to: " + mapId); + } + } + + public void Clear() + { + archive.Close(); + modelmanager.Clear(); + wmomanager.Clear(); + } - archive = new StormDll.ArchiveSet(this.logger, GetArchiveNames(dataConfig)); - modelmanager = new ModelManager(archive, 160, dataConfig); - wmomanager = new WMOManager(archive, modelmanager, 120, dataConfig); + public static string[] GetArchiveNames(DataConfig dataConfig) + { + return Directory.GetFiles(dataConfig.MPQ); + } - wdt = new WDT(); - wdtf = new WDTFile(archive, this.mapId, wdt, wmomanager, modelmanager, this.logger, dataConfig); + private void GetChunkData(TriangleCollection triangles, int chunk_x, int chunk_y) + { + if (triangles == null || wdtf == null || wdt == null) + return; + if (chunk_x < 0 || chunk_y < 0) + return; + if (chunk_x > 63 || chunk_y > 63) + return; + + int index = chunk_y * WDT.SIZE + chunk_x; + wdtf.LoadMapTile(chunk_x, chunk_y, index); + + MapTile mapTile = wdt.maptiles[index]; + if (!wdt.loaded[index]) + return; + + // Map tiles + for (int i = 0; i < MapTile.SIZE * MapTile.SIZE; i++) + { + if (mapTile.hasChunk[i]) + AddTriangles(triangles, mapTile.chunks[i]); + } - // TODO: move this to WDTFile - if (!wdtf.loaded) + // Map Tile - World objects + SparseMatrix3D instances = new(); + for (int i = 0; i < mapTile.wmois.Length; i++) + { + WMOInstance wi = mapTile.wmois[i]; + // TODO: check if this ever get hit + if (instances.ContainsKey((int)wi.pos.X, (int)wi.pos.Y, (int)wi.pos.Z)) { - wdt = null; // bad - throw new Exception("Failed to set continent to: " + mapId); + continue; } + + instances.Add((int)wi.pos.X, (int)wi.pos.Y, (int)wi.pos.Z, wi.wmo); + AddTriangles(triangles, wi); } - public void Clear() + for (int i = 0; i < mapTile.modelis.Length; i++) { - archive.Close(); - modelmanager.Clear(); - wmomanager.Clear(); + AddTriangles(triangles, mapTile.modelis[i]); } - public static string[] GetArchiveNames(DataConfig dataConfig) + wdt.loaded[index] = false; + } + + private static void GetChunkCoord(float x, float y, out int chunk_x, out int chunk_y) + { + // yeah, this is ugly. But safe + for (chunk_x = 0; chunk_x < 64; chunk_x++) { - return Directory.GetFiles(dataConfig.MPQ); + float max_y = ChunkReader.ZEROPOINT - (chunk_x * ChunkReader.TILESIZE); + float min_y = max_y - ChunkReader.TILESIZE; + if (y >= min_y - 0.1f && y < max_y + 0.1f) + break; } - - private void GetChunkData(TriangleCollection triangles, int chunk_x, int chunk_y) + for (chunk_y = 0; chunk_y < 64; chunk_y++) { - if (triangles == null || wdtf == null || wdt == null) - return; - if (chunk_x < 0 || chunk_y < 0) - return; - if (chunk_x > 63 || chunk_y > 63) - return; - - int index = chunk_y * WDT.SIZE + chunk_x; - wdtf.LoadMapTile(chunk_x, chunk_y, index); - - MapTile mapTile = wdt.maptiles[index]; - if (!wdt.loaded[index]) - return; - - // Map tiles - for (int i = 0; i < MapTile.SIZE * MapTile.SIZE; i++) - { - if (mapTile.hasChunk[i]) - AddTriangles(triangles, mapTile.chunks[i]); - } - - // Map Tile - World objects - SparseMatrix3D instances = new(); - for (int i = 0; i < mapTile.wmois.Length; i++) - { - WMOInstance wi = mapTile.wmois[i]; - // TODO: check if this ever get hit - if (instances.ContainsKey((int)wi.pos.X, (int)wi.pos.Y, (int)wi.pos.Z)) - { - continue; - } + float max_x = ChunkReader.ZEROPOINT - (chunk_y * ChunkReader.TILESIZE); + float min_x = max_x - ChunkReader.TILESIZE; + if (x >= min_x - 0.1f && x < max_x + 0.1f) + break; + } + } - instances.Add((int)wi.pos.X, (int)wi.pos.Y, (int)wi.pos.Z, wi.wmo); - AddTriangles(triangles, wi); - } + public void GetTriangles(TriangleCollection tc, float min_x, float min_y, float max_x, float max_y) + { + for (int i = 0; i < wdt.gwmois.Length; i++) + { + AddTriangles(tc, wdt.gwmois[i]); + } - for (int i = 0; i < mapTile.modelis.Length; i++) + for (float x = min_x; x < max_x; x += ChunkReader.TILESIZE) + { + for (float y = min_y; y < max_y; y += ChunkReader.TILESIZE) { - AddTriangles(triangles, mapTile.modelis[i]); + GetChunkCoord(x, y, out int chunk_x, out int chunk_y); + GetChunkData(tc, chunk_x, chunk_y); } - - wdt.loaded[index] = false; } + } + + private static void AddTriangles(TriangleCollection tc, MapChunk c) + { + int[,] vertices = new int[9, 9]; + int[,] verticesMid = new int[8, 8]; - private static void GetChunkCoord(float x, float y, out int chunk_x, out int chunk_y) + for (int row = 0; row < 9; row++) { - // yeah, this is ugly. But safe - for (chunk_x = 0; chunk_x < 64; chunk_x++) - { - float max_y = ChunkReader.ZEROPOINT - (chunk_x * ChunkReader.TILESIZE); - float min_y = max_y - ChunkReader.TILESIZE; - if (y >= min_y - 0.1f && y < max_y + 0.1f) - break; - } - for (chunk_y = 0; chunk_y < 64; chunk_y++) + for (int col = 0; col < 9; col++) { - float max_x = ChunkReader.ZEROPOINT - (chunk_y * ChunkReader.TILESIZE); - float min_x = max_x - ChunkReader.TILESIZE; - if (x >= min_x - 0.1f && x < max_x + 0.1f) - break; + ChunkGetCoordForPoint(c, row, col, out float x, out float y, out float z); + int index = tc.AddVertex(x, y, z); + vertices[row, col] = index; } } - public void GetTriangles(TriangleCollection tc, float min_x, float min_y, float max_x, float max_y) + for (int row = 0; row < 8; row++) { - for (int i = 0; i < wdt.gwmois.Length; i++) + for (int col = 0; col < 8; col++) { - AddTriangles(tc, wdt.gwmois[i]); + ChunkGetCoordForMiddlePoint(c, row, col, out float x, out float y, out float z); + int index = tc.AddVertex(x, y, z); + verticesMid[row, col] = index; } + } - for (float x = min_x; x < max_x; x += ChunkReader.TILESIZE) + for (int row = 0; row < 8; row++) + { + for (int col = 0; col < 8; col++) { - for (float y = min_y; y < max_y; y += ChunkReader.TILESIZE) + if (!c.isHole(col, row)) { - GetChunkCoord(x, y, out int chunk_x, out int chunk_y); - GetChunkData(tc, chunk_x, chunk_y); + int v0 = vertices[row, col]; + int v1 = vertices[row + 1, col]; + int v2 = vertices[row + 1, col + 1]; + int v3 = vertices[row, col + 1]; + int vMid = verticesMid[row, col]; + + tc.AddTriangle(v0, v1, vMid, TriangleType.Terrain); + tc.AddTriangle(v1, v2, vMid, TriangleType.Terrain); + tc.AddTriangle(v2, v3, vMid, TriangleType.Terrain); + tc.AddTriangle(v3, v0, vMid, TriangleType.Terrain); } } } - private static void AddTriangles(TriangleCollection tc, MapChunk c) + if (c.haswater) { - int[,] vertices = new int[9, 9]; - int[,] verticesMid = new int[8, 8]; - - for (int row = 0; row < 9; row++) + // paint the water + for (int row = 0; row < LiquidData.HEIGHT_SIZE; row++) { - for (int col = 0; col < 9; col++) + for (int col = 0; col < LiquidData.HEIGHT_SIZE; col++) { + int ii = row * LiquidData.HEIGHT_SIZE + col; + ChunkGetCoordForPoint(c, row, col, out float x, out float y, out float z); - int index = tc.AddVertex(x, y, z); + float height = c.water_height[ii]; // - 1.5f //why this here + int index = tc.AddVertex(x, y, height); vertices[row, col] = index; } } - for (int row = 0; row < 8; row++) + for (int row = 0; row < LiquidData.FLAG_SIZE; row++) { - for (int col = 0; col < 8; col++) + for (int col = 0; col < LiquidData.FLAG_SIZE; col++) { - ChunkGetCoordForMiddlePoint(c, row, col, out float x, out float y, out float z); - int index = tc.AddVertex(x, y, z); - verticesMid[row, col] = index; - } - } + int ii = row * LiquidData.FLAG_SIZE + col; - for (int row = 0; row < 8; row++) - { - for (int col = 0; col < 8; col++) - { - if (!c.isHole(col, row)) - { - int v0 = vertices[row, col]; - int v1 = vertices[row + 1, col]; - int v2 = vertices[row + 1, col + 1]; - int v3 = vertices[row, col + 1]; - int vMid = verticesMid[row, col]; - - tc.AddTriangle(v0, v1, vMid, TriangleType.Terrain); - tc.AddTriangle(v1, v2, vMid, TriangleType.Terrain); - tc.AddTriangle(v2, v3, vMid, TriangleType.Terrain); - tc.AddTriangle(v3, v0, vMid, TriangleType.Terrain); - } - } - } - - if (c.haswater) - { - // paint the water - for (int row = 0; row < LiquidData.HEIGHT_SIZE; row++) - { - for (int col = 0; col < LiquidData.HEIGHT_SIZE; col++) - { - int ii = row * LiquidData.HEIGHT_SIZE + col; - - ChunkGetCoordForPoint(c, row, col, out float x, out float y, out float z); - float height = c.water_height[ii]; // - 1.5f //why this here - int index = tc.AddVertex(x, y, height); - vertices[row, col] = index; - } - } - - for (int row = 0; row < LiquidData.FLAG_SIZE; row++) - { - for (int col = 0; col < LiquidData.FLAG_SIZE; col++) - { - int ii = row * LiquidData.FLAG_SIZE + col; - - if (c.water_flags[ii] == 0xf) - continue; + if (c.water_flags[ii] == 0xf) + continue; - int v0 = vertices[row, col]; - int v1 = vertices[row + 1, col]; - int v2 = vertices[row + 1, col + 1]; - int v3 = vertices[row, col + 1]; + int v0 = vertices[row, col]; + int v1 = vertices[row + 1, col]; + int v2 = vertices[row + 1, col + 1]; + int v3 = vertices[row, col + 1]; - tc.AddTriangle(v0, v1, v3, TriangleType.Water); - tc.AddTriangle(v1, v2, v3, TriangleType.Water); - } + tc.AddTriangle(v0, v1, v3, TriangleType.Water); + tc.AddTriangle(v1, v2, v3, TriangleType.Water); } } } + } - private static void AddTriangles(TriangleCollection tc, WMOInstance wi) - { - float dx = wi.pos.X; - float dy = wi.pos.Y; - float dz = wi.pos.Z; + private static void AddTriangles(TriangleCollection tc, WMOInstance wi) + { + float dx = wi.pos.X; + float dy = wi.pos.Y; + float dz = wi.pos.Z; - float dir_x = wi.dir.Z; - float dir_y = wi.dir.Y - 90; - float dir_z = -wi.dir.X; + float dir_x = wi.dir.Z; + float dir_y = wi.dir.Y - 90; + float dir_z = -wi.dir.X; - var pooler = ArrayPool.Shared; - WMO wmo = wi.wmo; - for (int gi = 0; gi < wmo.groups.Length; gi++) - { - WMOGroup g = wmo.groups[gi]; - int[] vertices = pooler.Rent((int)g.nVertices); + var pooler = ArrayPool.Shared; + WMO wmo = wi.wmo; + for (int gi = 0; gi < wmo.groups.Length; gi++) + { + WMOGroup g = wmo.groups[gi]; + int[] vertices = pooler.Rent((int)g.nVertices); - float minx = float.MaxValue; - float miny = float.MaxValue; - float minz = float.MaxValue; + float minx = float.MaxValue; + float miny = float.MaxValue; + float minz = float.MaxValue; - float maxx = float.MinValue; - float maxy = float.MinValue; - float maxz = float.MinValue; + float maxx = float.MinValue; + float maxy = float.MinValue; + float maxz = float.MinValue; - for (int i = 0; i < g.nVertices; i++) - { - int off = i * 3; + for (int i = 0; i < g.nVertices; i++) + { + int off = i * 3; - float x = g.vertices[off]; - float y = g.vertices[off + 2]; - float z = g.vertices[off + 1]; + float x = g.vertices[off]; + float y = g.vertices[off + 2]; + float z = g.vertices[off + 1]; - Rotate(z, y, dir_x, out z, out y); - Rotate(x, y, dir_z, out x, out y); - Rotate(x, z, dir_y, out x, out z); + Rotate(z, y, dir_x, out z, out y); + Rotate(x, y, dir_z, out x, out y); + Rotate(x, z, dir_y, out x, out z); - float xx = x + dx; - float yy = y + dy; - float zz = -z + dz; + float xx = x + dx; + float yy = y + dy; + float zz = -z + dz; - float finalx = ChunkReader.ZEROPOINT - zz; - float finaly = ChunkReader.ZEROPOINT - xx; - float finalz = yy; + float finalx = ChunkReader.ZEROPOINT - zz; + float finaly = ChunkReader.ZEROPOINT - xx; + float finalz = yy; - vertices[i] = tc.AddVertex(finalx, finaly, finalz); + vertices[i] = tc.AddVertex(finalx, finaly, finalz); - if (finalx < minx) { minx = finalx; } - if (finaly < miny) { miny = finalx; } - if (finalz < minz) { minz = finalx; } + if (finalx < minx) { minx = finalx; } + if (finaly < miny) { miny = finalx; } + if (finalz < minz) { minz = finalx; } - if (finalx > maxx) { maxx = finalx; } - if (finaly > maxy) { maxy = finalx; } - if (finalz > maxz) { maxz = finalx; } - } + if (finalx > maxx) { maxx = finalx; } + if (finaly > maxy) { maxy = finalx; } + if (finalz > maxz) { maxz = finalx; } + } - for (int i = 0; i < g.nTriangles; i++) + for (int i = 0; i < g.nTriangles; i++) + { + //if ((g.materials[i] & 0x1000) != 0) { - //if ((g.materials[i] & 0x1000) != 0) - { - int off = i * 3; - int i0 = vertices[g.triangles[off]]; - int i1 = vertices[g.triangles[off + 1]]; - int i2 = vertices[g.triangles[off + 2]]; - - tc.AddTriangle(i0, i1, i2, TriangleType.Object); - //if(t != -1) s.SetTriangleExtra(t, g.materials[0], 0, 0); - } + int off = i * 3; + int i0 = vertices[g.triangles[off]]; + int i1 = vertices[g.triangles[off + 1]]; + int i2 = vertices[g.triangles[off + 2]]; + + tc.AddTriangle(i0, i1, i2, TriangleType.Object); + //if(t != -1) s.SetTriangleExtra(t, g.materials[0], 0, 0); } - pooler.Return(vertices); } + pooler.Return(vertices); + } - /* - int doodadset = wi.doodadset; - if (doodadset < wmo.nDoodadSets) - { - uint firstDoodad = wmo.doodads[doodadset].firstInstance; - uint nDoodads = wmo.doodads[doodadset].nInstances; + /* + int doodadset = wi.doodadset; + if (doodadset < wmo.nDoodadSets) + { + uint firstDoodad = wmo.doodads[doodadset].firstInstance; + uint nDoodads = wmo.doodads[doodadset].nInstances; - for (uint i = 0; i < nDoodads; i++) + for (uint i = 0; i < nDoodads; i++) + { + uint d = firstDoodad + i; + ModelInstance mi = wmo.doodadInstances[d]; + if (mi != null) { - uint d = firstDoodad + i; - ModelInstance mi = wmo.doodadInstances[d]; - if (mi != null) - { - //logger.WriteLine("I got model " + mi.model.fileName + " at " + mi.pos); - //AddTrianglesGroupDoodads(s, mi, wi.dir, wi.pos, 0.0f); // DOes not work :( - } + //logger.WriteLine("I got model " + mi.model.fileName + " at " + mi.pos); + //AddTrianglesGroupDoodads(s, mi, wi.dir, wi.pos, 0.0f); // DOes not work :( } } - */ } + */ + } - private static void AddTrianglesGroupDoodads(TriangleCollection s, ModelInstance mi, Vector3 world_dir, Vector3 world_off, float rot) - { - float dx = mi.pos.X; - float dy = mi.pos.Y; - float dz = mi.pos.Z; - - Rotate(dx, dz, rot + 90f, out dx, out dz); + private static void AddTrianglesGroupDoodads(TriangleCollection s, ModelInstance mi, Vector3 world_dir, Vector3 world_off, float rot) + { + float dx = mi.pos.X; + float dy = mi.pos.Y; + float dz = mi.pos.Z; - dx += world_off.X; - dy += world_off.Y; - dz += world_off.Z; + Rotate(dx, dz, rot + 90f, out dx, out dz); - Quaternion q; - q.X = mi.dir.Z; - q.Y = mi.dir.X; - q.Z = mi.dir.Y; - q.W = mi.w; - Matrix4 rotMatrix = new(); - rotMatrix.makeQuaternionRotate(q); + dx += world_off.X; + dy += world_off.Y; + dz += world_off.Z; - Model m = mi.model; + Quaternion q; + q.X = mi.dir.Z; + q.Y = mi.dir.X; + q.Z = mi.dir.Y; + q.W = mi.w; + Matrix4 rotMatrix = new(); + rotMatrix.makeQuaternionRotate(q); - if (m.boundingTriangles == null) - { - } - else - { - // We got boiuding stuff, that is better - int nBoundingVertices = m.boundingVertices.Length / 3; + Model m = mi.model; - var pooler = ArrayPool.Shared; - int[] vertices = pooler.Rent(nBoundingVertices); + if (m.boundingTriangles == null) + { + } + else + { + // We got boiuding stuff, that is better + int nBoundingVertices = m.boundingVertices.Length / 3; - for (uint i = 0; i < nBoundingVertices; i++) - { - uint off = i * 3; - float x = m.boundingVertices[off]; - float y = m.boundingVertices[off + 2]; - float z = m.boundingVertices[off + 1]; - x *= mi.scale; - y *= mi.scale; - z *= -mi.scale; - - Vector3 pos = new(x, y, z); - Vector3 new_pos = rotMatrix.mutiply(pos); - x = pos.X; - y = pos.Y; - z = pos.Z; - - float dir_x = world_dir.Z; - float dir_y = world_dir.Y - 90; - float dir_z = -world_dir.X; - - Rotate(z, y, dir_x, out z, out y); - Rotate(x, y, dir_z, out x, out y); - Rotate(x, z, dir_y, out x, out z); - - float xx = x + dx; - float yy = y + dy; - float zz = -z + dz; - - float finalx = ChunkReader.ZEROPOINT - zz; - float finaly = ChunkReader.ZEROPOINT - xx; - float finalz = yy; - vertices[i] = s.AddVertex(finalx, finaly, finalz); - } + var pooler = ArrayPool.Shared; + int[] vertices = pooler.Rent(nBoundingVertices); - int nBoundingTriangles = m.boundingTriangles.Length / 3; - for (uint i = 0; i < nBoundingTriangles; i++) - { - uint off = i * 3; - int v0 = vertices[m.boundingTriangles[off]]; - int v1 = vertices[m.boundingTriangles[off + 1]]; - int v2 = vertices[m.boundingTriangles[off + 2]]; - s.AddTriangle(v0, v2, v1, TriangleType.Model); - } + for (uint i = 0; i < nBoundingVertices; i++) + { + uint off = i * 3; + float x = m.boundingVertices[off]; + float y = m.boundingVertices[off + 2]; + float z = m.boundingVertices[off + 1]; + x *= mi.scale; + y *= mi.scale; + z *= -mi.scale; + + Vector3 pos = new(x, y, z); + Vector3 new_pos = rotMatrix.mutiply(pos); + x = pos.X; + y = pos.Y; + z = pos.Z; + + float dir_x = world_dir.Z; + float dir_y = world_dir.Y - 90; + float dir_z = -world_dir.X; + + Rotate(z, y, dir_x, out z, out y); + Rotate(x, y, dir_z, out x, out y); + Rotate(x, z, dir_y, out x, out z); + + float xx = x + dx; + float yy = y + dy; + float zz = -z + dz; + + float finalx = ChunkReader.ZEROPOINT - zz; + float finaly = ChunkReader.ZEROPOINT - xx; + float finalz = yy; + vertices[i] = s.AddVertex(finalx, finaly, finalz); + } - pooler.Return(vertices); + int nBoundingTriangles = m.boundingTriangles.Length / 3; + for (uint i = 0; i < nBoundingTriangles; i++) + { + uint off = i * 3; + int v0 = vertices[m.boundingTriangles[off]]; + int v1 = vertices[m.boundingTriangles[off + 1]]; + int v2 = vertices[m.boundingTriangles[off + 2]]; + s.AddTriangle(v0, v2, v1, TriangleType.Model); } + + pooler.Return(vertices); } + } - private static void AddTriangles(TriangleCollection s, ModelInstance mi) - { - float dx = mi.pos.X; - float dy = mi.pos.Y; - float dz = mi.pos.Z; + private static void AddTriangles(TriangleCollection s, ModelInstance mi) + { + float dx = mi.pos.X; + float dy = mi.pos.Y; + float dz = mi.pos.Z; - float dir_x = mi.dir.Z; - float dir_y = mi.dir.Y - 90; // -90 is correct! - float dir_z = -mi.dir.X; + float dir_x = mi.dir.Z; + float dir_y = mi.dir.Y - 90; // -90 is correct! + float dir_z = -mi.dir.X; - Model m = mi.model; + Model m = mi.model; - if (m.boundingTriangles == null) - { - // /cry no bouding info, revert to normal vertives - /* + if (m.boundingTriangles == null) + { + // /cry no bouding info, revert to normal vertives + /* ModelView mv = m.view[0]; // View number 1 ?!?! if (mv == null) return; int[] vertices = new int[m.vertices.Length / 3]; @@ -472,81 +472,80 @@ private static void AddTriangles(TriangleCollection s, ModelInstance mi) s.AddTriangle(v0, v1, v2, ChunkedTriangleCollection.TriangleFlagModel); } */ - } - else - { - // We got boiuding stuff, that is better - int nBoundingVertices = m.boundingVertices.Length / 3; - - var pooler = ArrayPool.Shared; - int[] vertices = pooler.Rent(nBoundingVertices); + } + else + { + // We got boiuding stuff, that is better + int nBoundingVertices = m.boundingVertices.Length / 3; - for (uint i = 0; i < nBoundingVertices; i++) - { - uint off = i * 3; - float x = m.boundingVertices[off]; - float y = m.boundingVertices[off + 2]; - float z = m.boundingVertices[off + 1]; + var pooler = ArrayPool.Shared; + int[] vertices = pooler.Rent(nBoundingVertices); - Rotate(z, y, dir_x, out z, out y); - Rotate(x, y, dir_z, out x, out y); - Rotate(x, z, dir_y, out x, out z); + for (uint i = 0; i < nBoundingVertices; i++) + { + uint off = i * 3; + float x = m.boundingVertices[off]; + float y = m.boundingVertices[off + 2]; + float z = m.boundingVertices[off + 1]; - x *= mi.scale; - y *= mi.scale; - z *= mi.scale; + Rotate(z, y, dir_x, out z, out y); + Rotate(x, y, dir_z, out x, out y); + Rotate(x, z, dir_y, out x, out z); - float xx = x + dx; - float yy = y + dy; - float zz = -z + dz; + x *= mi.scale; + y *= mi.scale; + z *= mi.scale; - float finalx = ChunkReader.ZEROPOINT - zz; - float finaly = ChunkReader.ZEROPOINT - xx; - float finalz = yy; + float xx = x + dx; + float yy = y + dy; + float zz = -z + dz; - vertices[i] = s.AddVertex(finalx, finaly, finalz); - } + float finalx = ChunkReader.ZEROPOINT - zz; + float finaly = ChunkReader.ZEROPOINT - xx; + float finalz = yy; - int nBoundingTriangles = m.boundingTriangles.Length / 3; - for (uint i = 0; i < nBoundingTriangles; i++) - { - uint off = i * 3; - int v0 = vertices[m.boundingTriangles[off]]; - int v1 = vertices[m.boundingTriangles[off + 1]]; - int v2 = vertices[m.boundingTriangles[off + 2]]; - s.AddTriangle(v0, v1, v2, TriangleType.Model); - } + vertices[i] = s.AddVertex(finalx, finaly, finalz); + } - pooler.Return(vertices); + int nBoundingTriangles = m.boundingTriangles.Length / 3; + for (uint i = 0; i < nBoundingTriangles; i++) + { + uint off = i * 3; + int v0 = vertices[m.boundingTriangles[off]]; + int v1 = vertices[m.boundingTriangles[off + 1]]; + int v2 = vertices[m.boundingTriangles[off + 2]]; + s.AddTriangle(v0, v1, v2, TriangleType.Model); } - } - private static void ChunkGetCoordForPoint(MapChunk c, int row, int col, - out float x, out float y, out float z) - { - int off = ((row * 17) + col) * 3; - x = ChunkReader.ZEROPOINT - c.vertices[off + 2]; - y = ChunkReader.ZEROPOINT - c.vertices[off]; - z = c.vertices[off + 1]; + pooler.Return(vertices); } + } - private static void ChunkGetCoordForMiddlePoint(MapChunk c, int row, int col, - out float x, out float y, out float z) - { - int off = (9 + (row * 17) + col) * 3; - x = ChunkReader.ZEROPOINT - c.vertices[off + 2]; - y = ChunkReader.ZEROPOINT - c.vertices[off]; - z = c.vertices[off + 1]; - } + private static void ChunkGetCoordForPoint(MapChunk c, int row, int col, + out float x, out float y, out float z) + { + int off = ((row * 17) + col) * 3; + x = ChunkReader.ZEROPOINT - c.vertices[off + 2]; + y = ChunkReader.ZEROPOINT - c.vertices[off]; + z = c.vertices[off + 1]; + } - public static void Rotate(float x, float y, float angle, out float nx, out float ny) - { - float rot = angle / 360.0f * PI * 2; - float c_y = Cos(rot); - float s_y = Sin(rot); + private static void ChunkGetCoordForMiddlePoint(MapChunk c, int row, int col, + out float x, out float y, out float z) + { + int off = (9 + (row * 17) + col) * 3; + x = ChunkReader.ZEROPOINT - c.vertices[off + 2]; + y = ChunkReader.ZEROPOINT - c.vertices[off]; + z = c.vertices[off + 1]; + } - nx = (c_y * x) - (s_y * y); - ny = (s_y * x) + (c_y * y); - } + public static void Rotate(float x, float y, float angle, out float nx, out float ny) + { + float rot = angle / 360.0f * PI * 2; + float c_y = Cos(rot); + float s_y = Sin(rot); + + nx = (c_y * x) - (s_y * y); + ny = (s_y * x) + (c_y * y); } } \ No newline at end of file diff --git a/PPather/Triangles/StormDll.cs b/PPather/Triangles/StormDll.cs index 3716cff43..6e28a36c1 100644 --- a/PPather/Triangles/StormDll.cs +++ b/PPather/Triangles/StormDll.cs @@ -25,188 +25,187 @@ Copyright Pontus Borg 2008 using System.Runtime.InteropServices; using System.Collections.Generic; -namespace StormDll -{ - internal sealed class StormDllx64 - { - [DllImport("MPQ\\StormLib_x64.dll")] - public static extern bool SFileOpenArchive( - [MarshalAs(UnmanagedType.LPWStr)] string szMpqName, - uint dwPriority, - [MarshalAs(UnmanagedType.U4)] OpenArchive dwFlags, - out IntPtr phMpq); - - [DllImport("MPQ\\StormLib_x64.dll")] - public static extern bool SFileCloseArchive(IntPtr hMpq); - - [DllImport("MPQ\\StormLib_x64.dll")] - public static extern bool SFileExtractFile( - IntPtr hMpq, - [MarshalAs(UnmanagedType.LPStr)] string szToExtract, - [MarshalAs(UnmanagedType.LPWStr)] string szExtracted, - [MarshalAs(UnmanagedType.U4)] OpenFile dwSearchScope); - } +namespace StormDll; - internal sealed class StormDllx86 - { - [DllImport("MPQ\\StormLib_x86.dll")] - public static extern bool SFileOpenArchive( - [MarshalAs(UnmanagedType.LPWStr)] string szMpqName, - uint dwPriority, - [MarshalAs(UnmanagedType.U4)] OpenArchive dwFlags, - out IntPtr phMpq); - - [DllImport("MPQ\\StormLib_x86.dll")] - public static extern bool SFileCloseArchive(IntPtr hMpq); - - [DllImport("MPQ\\StormLib_x86.dll")] - public static extern bool SFileExtractFile( - IntPtr hMpq, - [MarshalAs(UnmanagedType.LPStr)] string szToExtract, - [MarshalAs(UnmanagedType.LPWStr)] string szExtracted, - [MarshalAs(UnmanagedType.U4)] OpenFile dwSearchScope); - } +internal sealed class StormDllx64 +{ + [DllImport("MPQ\\StormLib_x64.dll")] + public static extern bool SFileOpenArchive( + [MarshalAs(UnmanagedType.LPWStr)] string szMpqName, + uint dwPriority, + [MarshalAs(UnmanagedType.U4)] OpenArchive dwFlags, + out IntPtr phMpq); + + [DllImport("MPQ\\StormLib_x64.dll")] + public static extern bool SFileCloseArchive(IntPtr hMpq); + + [DllImport("MPQ\\StormLib_x64.dll")] + public static extern bool SFileExtractFile( + IntPtr hMpq, + [MarshalAs(UnmanagedType.LPStr)] string szToExtract, + [MarshalAs(UnmanagedType.LPWStr)] string szExtracted, + [MarshalAs(UnmanagedType.U4)] OpenFile dwSearchScope); +} + +internal sealed class StormDllx86 +{ + [DllImport("MPQ\\StormLib_x86.dll")] + public static extern bool SFileOpenArchive( + [MarshalAs(UnmanagedType.LPWStr)] string szMpqName, + uint dwPriority, + [MarshalAs(UnmanagedType.U4)] OpenArchive dwFlags, + out IntPtr phMpq); + + [DllImport("MPQ\\StormLib_x86.dll")] + public static extern bool SFileCloseArchive(IntPtr hMpq); + + [DllImport("MPQ\\StormLib_x86.dll")] + public static extern bool SFileExtractFile( + IntPtr hMpq, + [MarshalAs(UnmanagedType.LPStr)] string szToExtract, + [MarshalAs(UnmanagedType.LPWStr)] string szExtracted, + [MarshalAs(UnmanagedType.U4)] OpenFile dwSearchScope); +} + +// Flags for SFileOpenArchive +[Flags] +public enum OpenArchive : uint +{ + BASE_PROVIDER_FILE = 0x00000000, // Base data source is a file + BASE_PROVIDER_MAP = 0x00000001, // Base data source is memory-mapped file + BASE_PROVIDER_HTTP = 0x00000002, // Base data source is a file on web server + BASE_PROVIDER_MASK = 0x0000000F, // Mask for base provider value + STREAM_PROVIDER_FLAT = 0x00000000, // Stream is linear with no offset mapping + STREAM_PROVIDER_PARTIAL = 0x00000010, // Stream is partial file (.part) + STREAM_PROVIDER_MPQE = 0x00000020, // Stream is an encrypted MPQ + STREAM_PROVIDER_BLOCK4 = 0x00000030, // = 0x4000 per block, text MD5 after each block, max = 0x2000 blocks per file + STREAM_PROVIDER_MASK = 0x000000F0, // Mask for stream provider value + STREAM_FLAG_READ_ONLY = 0x00000100, // Stream is read only + STREAM_FLAG_WRITE_SHARE = 0x00000200, // Allow write sharing when open for write + STREAM_FLAG_USE_BITMAP = 0x00000400, // If the file has a file bitmap, load it and use it + STREAM_OPTIONS_MASK = 0x0000FF00, // Mask for stream options + STREAM_PROVIDERS_MASK = 0x000000FF, // Mask to get stream providers + STREAM_FLAGS_MASK = 0x0000FFFF, // Mask for all stream flags (providers+options) + MPQ_OPEN_NO_LISTFILE = 0x00010000, // Don't load the internal listfile + MPQ_OPEN_NO_ATTRIBUTES = 0x00020000, // Don't open the attributes + MPQ_OPEN_NO_HEADER_SEARCH = 0x00040000, // Don't search for the MPQ header past the begin of the file + MPQ_OPEN_FORCE_MPQ_V1 = 0x00080000, // Always open the archive as MPQ v 1.00, ignore the "wFormatVersion" variable in the header + MPQ_OPEN_CHECK_SECTOR_CRC = 0x00100000, // On files with MPQ_FILE_SECTOR_CRC, the CRC will be checked when reading file + MPQ_OPEN_FORCE_LISTFILE = 0x00400000, // Force add listfile even if there is none at the moment of opening + MPQ_OPEN_READ_ONLY = STREAM_FLAG_READ_ONLY +}; + +// Values for SFileExtractFile +public enum OpenFile : uint +{ + SFILE_OPEN_FROM_MPQ = 0x00000000, // Open the file from the MPQ archive + SFILE_OPEN_CHECK_EXISTS = 0xFFFFFFFC, // Only check whether the file exists + SFILE_OPEN_LOCAL_FILE = 0xFFFFFFFF // Open a local file +}; - // Flags for SFileOpenArchive - [Flags] - public enum OpenArchive : uint - { - BASE_PROVIDER_FILE = 0x00000000, // Base data source is a file - BASE_PROVIDER_MAP = 0x00000001, // Base data source is memory-mapped file - BASE_PROVIDER_HTTP = 0x00000002, // Base data source is a file on web server - BASE_PROVIDER_MASK = 0x0000000F, // Mask for base provider value - STREAM_PROVIDER_FLAT = 0x00000000, // Stream is linear with no offset mapping - STREAM_PROVIDER_PARTIAL = 0x00000010, // Stream is partial file (.part) - STREAM_PROVIDER_MPQE = 0x00000020, // Stream is an encrypted MPQ - STREAM_PROVIDER_BLOCK4 = 0x00000030, // = 0x4000 per block, text MD5 after each block, max = 0x2000 blocks per file - STREAM_PROVIDER_MASK = 0x000000F0, // Mask for stream provider value - STREAM_FLAG_READ_ONLY = 0x00000100, // Stream is read only - STREAM_FLAG_WRITE_SHARE = 0x00000200, // Allow write sharing when open for write - STREAM_FLAG_USE_BITMAP = 0x00000400, // If the file has a file bitmap, load it and use it - STREAM_OPTIONS_MASK = 0x0000FF00, // Mask for stream options - STREAM_PROVIDERS_MASK = 0x000000FF, // Mask to get stream providers - STREAM_FLAGS_MASK = 0x0000FFFF, // Mask for all stream flags (providers+options) - MPQ_OPEN_NO_LISTFILE = 0x00010000, // Don't load the internal listfile - MPQ_OPEN_NO_ATTRIBUTES = 0x00020000, // Don't open the attributes - MPQ_OPEN_NO_HEADER_SEARCH = 0x00040000, // Don't search for the MPQ header past the begin of the file - MPQ_OPEN_FORCE_MPQ_V1 = 0x00080000, // Always open the archive as MPQ v 1.00, ignore the "wFormatVersion" variable in the header - MPQ_OPEN_CHECK_SECTOR_CRC = 0x00100000, // On files with MPQ_FILE_SECTOR_CRC, the CRC will be checked when reading file - MPQ_OPEN_FORCE_LISTFILE = 0x00400000, // Force add listfile even if there is none at the moment of opening - MPQ_OPEN_READ_ONLY = STREAM_FLAG_READ_ONLY - }; - - // Values for SFileExtractFile - public enum OpenFile : uint - { - SFILE_OPEN_FROM_MPQ = 0x00000000, // Open the file from the MPQ archive - SFILE_OPEN_CHECK_EXISTS = 0xFFFFFFFC, // Only check whether the file exists - SFILE_OPEN_LOCAL_FILE = 0xFFFFFFFF // Open a local file - }; +public sealed class ArchiveSet +{ + private readonly Archive[] archives; - public sealed class ArchiveSet + public ArchiveSet(ILogger logger, string[] files) { - private readonly Archive[] archives; + archives = new Archive[files.Length]; - public ArchiveSet(ILogger logger, string[] files) + for (int i = 0; i < files.Length; i++) { - archives = new Archive[files.Length]; + Archive a = new(files[i], out bool open, 0, + OpenArchive.MPQ_OPEN_NO_LISTFILE | + OpenArchive.MPQ_OPEN_NO_ATTRIBUTES | + OpenArchive.MPQ_OPEN_NO_HEADER_SEARCH | + OpenArchive.MPQ_OPEN_READ_ONLY); - for (int i = 0; i < files.Length; i++) + if (open && a.IsOpen()) { - Archive a = new(files[i], out bool open, 0, - OpenArchive.MPQ_OPEN_NO_LISTFILE | - OpenArchive.MPQ_OPEN_NO_ATTRIBUTES | - OpenArchive.MPQ_OPEN_NO_HEADER_SEARCH | - OpenArchive.MPQ_OPEN_READ_ONLY); - - if (open && a.IsOpen()) - { - archives[i] = a; - - if (logger.IsEnabled(LogLevel.Trace)) - logger.LogTrace($"Archive[{i}] open {files[i]}"); - } - else if (logger.IsEnabled(LogLevel.Trace)) - logger.LogTrace($"Archive[{i}] openfail {files[i]}"); + archives[i] = a; + + if (logger.IsEnabled(LogLevel.Trace)) + logger.LogTrace($"Archive[{i}] open {files[i]}"); } + else if (logger.IsEnabled(LogLevel.Trace)) + logger.LogTrace($"Archive[{i}] openfail {files[i]}"); } + } - public bool SFileExtractFile(string from, string to, OpenFile dwSearchScope = OpenFile.SFILE_OPEN_FROM_MPQ) + public bool SFileExtractFile(string from, string to, OpenFile dwSearchScope = OpenFile.SFILE_OPEN_FROM_MPQ) + { + for (int i = 0; i < archives.Length; i++) { - for (int i = 0; i < archives.Length; i++) + Archive a = archives[i]; + if (a.HasFile(from)) { - Archive a = archives[i]; - if (a.HasFile(from)) - { - return a.SFileExtractFile(from, to, dwSearchScope); - } + return a.SFileExtractFile(from, to, dwSearchScope); } - - return false; } - public void Close() - { - for (int i = 0; i < archives.Length; i++) - archives[i].SFileCloseArchive(); - } + return false; } - internal sealed class Archive + public void Close() { - private readonly IntPtr handle; + for (int i = 0; i < archives.Length; i++) + archives[i].SFileCloseArchive(); + } +} + +internal sealed class Archive +{ + private readonly IntPtr handle; - private readonly HashSet fileList = new(StringComparer.InvariantCultureIgnoreCase); + private readonly HashSet fileList = new(StringComparer.InvariantCultureIgnoreCase); - private static readonly bool Is64Bit = Environment.Is64BitProcess; + private static readonly bool Is64Bit = Environment.Is64BitProcess; - public Archive(string file, out bool open, uint Prio, OpenArchive Flags) - { - open = Is64Bit - ? StormDllx64.SFileOpenArchive(file, Prio, Flags, out handle) - : StormDllx86.SFileOpenArchive(file, Prio, Flags, out handle); + public Archive(string file, out bool open, uint Prio, OpenArchive Flags) + { + open = Is64Bit + ? StormDllx64.SFileOpenArchive(file, Prio, Flags, out handle) + : StormDllx86.SFileOpenArchive(file, Prio, Flags, out handle); - if (!open) - return; + if (!open) + return; - string temp = Path.GetTempFileName(); + string temp = Path.GetTempFileName(); - bool extracted = Is64Bit - ? StormDllx64.SFileExtractFile(handle, "(listfile)", temp, OpenFile.SFILE_OPEN_FROM_MPQ) - : StormDllx86.SFileExtractFile(handle, "(listfile)", temp, OpenFile.SFILE_OPEN_FROM_MPQ); + bool extracted = Is64Bit + ? StormDllx64.SFileExtractFile(handle, "(listfile)", temp, OpenFile.SFILE_OPEN_FROM_MPQ) + : StormDllx86.SFileExtractFile(handle, "(listfile)", temp, OpenFile.SFILE_OPEN_FROM_MPQ); - if (extracted && File.Exists(temp)) + if (extracted && File.Exists(temp)) + { + foreach (string line in File.ReadLines(temp)) { - foreach (string line in File.ReadLines(temp)) - { - fileList.Add(line); - } + fileList.Add(line); } } + } - public bool IsOpen() - { - return handle != IntPtr.Zero; - } + public bool IsOpen() + { + return handle != IntPtr.Zero; + } - public bool HasFile(string name) - { - return fileList.Contains(name); - } + public bool HasFile(string name) + { + return fileList.Contains(name); + } - public bool SFileCloseArchive() - { - fileList.Clear(); - return Is64Bit - ? StormDllx64.SFileCloseArchive(handle) - : StormDllx86.SFileCloseArchive(handle); - } + public bool SFileCloseArchive() + { + fileList.Clear(); + return Is64Bit + ? StormDllx64.SFileCloseArchive(handle) + : StormDllx86.SFileCloseArchive(handle); + } - public bool SFileExtractFile(string from, string to, OpenFile dwSearchScope) - { - return Is64Bit - ? StormDllx64.SFileExtractFile(handle, from, to, dwSearchScope) - : StormDllx86.SFileExtractFile(handle, from, to, dwSearchScope); - } + public bool SFileExtractFile(string from, string to, OpenFile dwSearchScope) + { + return Is64Bit + ? StormDllx64.SFileExtractFile(handle, from, to, dwSearchScope) + : StormDllx86.SFileExtractFile(handle, from, to, dwSearchScope); } } \ No newline at end of file diff --git a/PPather/Triangles/TriangleCollection.cs b/PPather/Triangles/TriangleCollection.cs index c963ed829..df69feefc 100644 --- a/PPather/Triangles/TriangleCollection.cs +++ b/PPather/Triangles/TriangleCollection.cs @@ -12,184 +12,183 @@ using PPather; using PPather.Triangles; -namespace WowTriangles +namespace WowTriangles; + +/// +/// +/// +public sealed class TriangleCollection { - /// - /// - /// - public sealed class TriangleCollection - { - private readonly ILogger logger; - public List Vertecies { get; } - public List> Triangles { get; } - - private TriangleMatrix matrix; - - public int LRU; - - private Vector3 max = new(-1E30f, -1E30f, -1E30f); - public Vector3 Max => max; - - private Vector3 min = new(1E30f, 1E30f, 1E30f); - public Vector3 Min => min; - - private Vector3 limit_max = new(1E30f, 1E30f, 1E30f); - private Vector3 limit_min = new(-1E30f, -1E30f, -1E30f); - - private int triangleCount; - public int TriangleCount => Triangles.Count; - - public int VertexCount { get; private set; } - - public TriangleCollection(ILogger logger) - { - this.logger = logger; - Vertecies = new(2 ^ 16); // terrain mesh - Triangles = new(128); - } - - public void Clear() - { - triangleCount = 0; - VertexCount = 0; - - Triangles.Clear(); - Vertecies.Clear(); - matrix.Clear(); - } - - public bool HasTriangleMatrix => matrix != null; - - public TriangleMatrix GetTriangleMatrix() - { - if (matrix == null) - matrix = new TriangleMatrix(this, logger); - - return matrix; - } - - public void SetLimits(float min_x, float min_y, float min_z, - float max_x, float max_y, float max_z) - { - limit_max = new(max_x, max_y, max_z); - limit_min = new(min_x, min_y, min_z); - } - - public int AddVertex(float x, float y, float z) - { - VerticesSet(VertexCount, x, y, z); - return VertexCount++; - } - - // big list if triangles (3 vertice IDs per triangle) - public void AddTriangle(int v0, int v1, int v2, TriangleType flags) - { - // check limits - if (!CheckVertexLimits(v0) && - !CheckVertexLimits(v1) && - !CheckVertexLimits(v2)) - return; - - // Create new - SetMinMax(v0); - SetMinMax(v1); - SetMinMax(v2); - - TrianglesSet(triangleCount, v0, v1, v2, flags); - triangleCount++; - } - - private void SetMinMax(int v) - { - GetVertex(v, out float x, out float y, out float z); - if (x < min.X) - min.X = x; - if (y < min.Y) - min.Y = y; - if (z < min.Z) - min.Z = z; - - if (x > max.X) - max.X = x; - if (y > max.Y) - max.Y = y; - if (z > max.Z) - max.Z = z; - } - - private bool CheckVertexLimits(int v) - { - GetVertex(v, out float x, out float y, out float z); - - return x >= limit_min.X && x <= limit_max.X && - y >= limit_min.Y && y <= limit_max.Y && - z >= limit_min.Z && z <= limit_max.Z; - } - - - public void GetVertex(int i, out float x, out float y, out float z) - { - VerticesGet(i, out x, out y, out z); - } - - public void GetTriangle(int i, - out int v0, out int v1, out int v2, out TriangleType flags) - { - TrianglesGet(i, out v0, out v1, out v2, out flags); - } - - public void GetTriangleVertices(int i, - out float x0, out float y0, out float z0, - out float x1, out float y1, out float z1, - out float x2, out float y2, out float z2, out TriangleType flags) - { - TrianglesGet(i, out int v0, out int v1, out int v2, out flags); - - VerticesGet(v0, out x0, out y0, out z0); - VerticesGet(v1, out x1, out y1, out z1); - VerticesGet(v2, out x2, out y2, out z2); - } - - [System.Runtime.CompilerServices.SkipLocalsInit] - public void GetTriangleVertices(int i, - out float x0, out float y0, out float z0, - out float x1, out float y1, out float z1, - out float x2, out float y2, out float z2) - { - TrianglesGet(i, out int v0, out int v1, out int v2, out _); - - VerticesGet(v0, out x0, out y0, out z0); - VerticesGet(v1, out x1, out y1, out z1); - VerticesGet(v2, out x2, out y2, out z2); - } - - [System.Runtime.CompilerServices.SkipLocalsInit] - private void VerticesGet(int index, out float x, out float y, out float z) - { - var local = Vertecies; - Vector3 v = local[index]; - x = v.X; - y = v.Y; - z = v.Z; - } - - private void VerticesSet(int index, float x, float y, float z) - { - Vertecies.Insert(index, new(x, y, z)); - } - - - [System.Runtime.CompilerServices.SkipLocalsInit] - private void TrianglesGet(int index, out int v0, out int v1, out int v2, out TriangleType flags) - { - var local = Triangles; - Triangle t = local[index]; - (v0, v1, v2, flags) = t; - } - - private void TrianglesSet(int index, int v0, int v1, int v2, TriangleType flags) - { - Triangles.Insert(index, new(v0, v1, v2, flags)); - } + private readonly ILogger logger; + public List Vertecies { get; } + public List> Triangles { get; } + + private TriangleMatrix matrix; + + public int LRU; + + private Vector3 max = new(-1E30f, -1E30f, -1E30f); + public Vector3 Max => max; + + private Vector3 min = new(1E30f, 1E30f, 1E30f); + public Vector3 Min => min; + + private Vector3 limit_max = new(1E30f, 1E30f, 1E30f); + private Vector3 limit_min = new(-1E30f, -1E30f, -1E30f); + + private int triangleCount; + public int TriangleCount => Triangles.Count; + + public int VertexCount { get; private set; } + + public TriangleCollection(ILogger logger) + { + this.logger = logger; + Vertecies = new(2 ^ 16); // terrain mesh + Triangles = new(128); + } + + public void Clear() + { + triangleCount = 0; + VertexCount = 0; + + Triangles.Clear(); + Vertecies.Clear(); + matrix.Clear(); + } + + public bool HasTriangleMatrix => matrix != null; + + public TriangleMatrix GetTriangleMatrix() + { + if (matrix == null) + matrix = new TriangleMatrix(this, logger); + + return matrix; + } + + public void SetLimits(float min_x, float min_y, float min_z, + float max_x, float max_y, float max_z) + { + limit_max = new(max_x, max_y, max_z); + limit_min = new(min_x, min_y, min_z); + } + + public int AddVertex(float x, float y, float z) + { + VerticesSet(VertexCount, x, y, z); + return VertexCount++; + } + + // big list if triangles (3 vertice IDs per triangle) + public void AddTriangle(int v0, int v1, int v2, TriangleType flags) + { + // check limits + if (!CheckVertexLimits(v0) && + !CheckVertexLimits(v1) && + !CheckVertexLimits(v2)) + return; + + // Create new + SetMinMax(v0); + SetMinMax(v1); + SetMinMax(v2); + + TrianglesSet(triangleCount, v0, v1, v2, flags); + triangleCount++; + } + + private void SetMinMax(int v) + { + GetVertex(v, out float x, out float y, out float z); + if (x < min.X) + min.X = x; + if (y < min.Y) + min.Y = y; + if (z < min.Z) + min.Z = z; + + if (x > max.X) + max.X = x; + if (y > max.Y) + max.Y = y; + if (z > max.Z) + max.Z = z; + } + + private bool CheckVertexLimits(int v) + { + GetVertex(v, out float x, out float y, out float z); + + return x >= limit_min.X && x <= limit_max.X && + y >= limit_min.Y && y <= limit_max.Y && + z >= limit_min.Z && z <= limit_max.Z; + } + + + public void GetVertex(int i, out float x, out float y, out float z) + { + VerticesGet(i, out x, out y, out z); + } + + public void GetTriangle(int i, + out int v0, out int v1, out int v2, out TriangleType flags) + { + TrianglesGet(i, out v0, out v1, out v2, out flags); + } + + public void GetTriangleVertices(int i, + out float x0, out float y0, out float z0, + out float x1, out float y1, out float z1, + out float x2, out float y2, out float z2, out TriangleType flags) + { + TrianglesGet(i, out int v0, out int v1, out int v2, out flags); + + VerticesGet(v0, out x0, out y0, out z0); + VerticesGet(v1, out x1, out y1, out z1); + VerticesGet(v2, out x2, out y2, out z2); + } + + [System.Runtime.CompilerServices.SkipLocalsInit] + public void GetTriangleVertices(int i, + out float x0, out float y0, out float z0, + out float x1, out float y1, out float z1, + out float x2, out float y2, out float z2) + { + TrianglesGet(i, out int v0, out int v1, out int v2, out _); + + VerticesGet(v0, out x0, out y0, out z0); + VerticesGet(v1, out x1, out y1, out z1); + VerticesGet(v2, out x2, out y2, out z2); + } + + [System.Runtime.CompilerServices.SkipLocalsInit] + private void VerticesGet(int index, out float x, out float y, out float z) + { + var local = Vertecies; + Vector3 v = local[index]; + x = v.X; + y = v.Y; + z = v.Z; + } + + private void VerticesSet(int index, float x, float y, float z) + { + Vertecies.Insert(index, new(x, y, z)); + } + + + [System.Runtime.CompilerServices.SkipLocalsInit] + private void TrianglesGet(int index, out int v0, out int v1, out int v2, out TriangleType flags) + { + var local = Triangles; + Triangle t = local[index]; + (v0, v1, v2, flags) = t; + } + + private void TrianglesSet(int index, int v0, int v1, int v2, TriangleType flags) + { + Triangles.Insert(index, new(v0, v1, v2, flags)); } } \ No newline at end of file diff --git a/PPather/Triangles/TriangleMatrix.cs b/PPather/Triangles/TriangleMatrix.cs index 3b523d623..700c451a9 100644 --- a/PPather/Triangles/TriangleMatrix.cs +++ b/PPather/Triangles/TriangleMatrix.cs @@ -16,136 +16,135 @@ using static WowTriangles.Utils; -namespace WowTriangles +namespace WowTriangles; + +public sealed class TriangleMatrix { - public sealed class TriangleMatrix - { - private const float resolution = 6.0f; - private readonly SparseFloatMatrix2D> matrix; + private const float resolution = 6.0f; + private readonly SparseFloatMatrix2D> matrix; - public TriangleMatrix(TriangleCollection tc, ILogger logger) - { - DateTime pre = DateTime.UtcNow; + public TriangleMatrix(TriangleCollection tc, ILogger logger) + { + DateTime pre = DateTime.UtcNow; - matrix = new SparseFloatMatrix2D>(resolution); + matrix = new SparseFloatMatrix2D>(resolution); - Vector3 vertex0; - Vector3 vertex1; - Vector3 vertex2; + Vector3 vertex0; + Vector3 vertex1; + Vector3 vertex2; - for (int i = 0; i < tc.TriangleCount; i++) + for (int i = 0; i < tc.TriangleCount; i++) + { + tc.GetTriangleVertices(i, + out vertex0.X, out vertex0.Y, out vertex0.Z, + out vertex1.X, out vertex1.Y, out vertex1.Z, + out vertex2.X, out vertex2.Y, out vertex2.Z); + + float minx = Min3(vertex0.X, vertex1.X, vertex2.X); + float maxx = Max3(vertex0.X, vertex1.X, vertex2.X); + float miny = Min3(vertex0.Y, vertex1.Y, vertex2.Y); + float maxy = Max3(vertex0.Y, vertex1.Y, vertex2.Y); + + Vector3 box_center; + Vector3 box_halfsize; + box_halfsize.X = resolution / 2; + box_halfsize.Y = resolution / 2; + box_halfsize.Z = 1E6f; + + int startx = matrix.LocalToGrid(minx); + int endx = matrix.LocalToGrid(maxx); + int starty = matrix.LocalToGrid(miny); + int endy = matrix.LocalToGrid(maxy); + + for (int x = startx; x <= endx; x++) { - tc.GetTriangleVertices(i, - out vertex0.X, out vertex0.Y, out vertex0.Z, - out vertex1.X, out vertex1.Y, out vertex1.Z, - out vertex2.X, out vertex2.Y, out vertex2.Z); - - float minx = Min3(vertex0.X, vertex1.X, vertex2.X); - float maxx = Max3(vertex0.X, vertex1.X, vertex2.X); - float miny = Min3(vertex0.Y, vertex1.Y, vertex2.Y); - float maxy = Max3(vertex0.Y, vertex1.Y, vertex2.Y); - - Vector3 box_center; - Vector3 box_halfsize; - box_halfsize.X = resolution / 2; - box_halfsize.Y = resolution / 2; - box_halfsize.Z = 1E6f; - - int startx = matrix.LocalToGrid(minx); - int endx = matrix.LocalToGrid(maxx); - int starty = matrix.LocalToGrid(miny); - int endy = matrix.LocalToGrid(maxy); - - for (int x = startx; x <= endx; x++) + for (int y = starty; y <= endy; y++) { - for (int y = starty; y <= endy; y++) + float grid_x = matrix.GridToLocal(x); + float grid_y = matrix.GridToLocal(y); + box_center.X = grid_x + (resolution / 2); + box_center.Y = grid_y + (resolution / 2); + box_center.Z = 0; + + if (!TestTriangleBoxIntersect(vertex0, vertex1, vertex2, box_center, box_halfsize)) { - float grid_x = matrix.GridToLocal(x); - float grid_y = matrix.GridToLocal(y); - box_center.X = grid_x + (resolution / 2); - box_center.Y = grid_y + (resolution / 2); - box_center.Z = 0; - - if (!TestTriangleBoxIntersect(vertex0, vertex1, vertex2, box_center, box_halfsize)) - { - continue; - } - - if (!matrix.TryGetValue(grid_x, grid_y, out List list)) - { - list = new(); - matrix.Add(grid_x, grid_y, list); - } - list.Add(i); + continue; } + + if (!matrix.TryGetValue(grid_x, grid_y, out List list)) + { + list = new(); + matrix.Add(grid_x, grid_y, list); + } + list.Add(i); } } - - if (logger.IsEnabled(LogLevel.Trace)) - logger.LogTrace($"Mesh [||,||] Bounds: [{tc.Min.X:F4}, {tc.Min.Y:F4}] [{tc.Max.X:F4}, {tc.Max.Y:F4}] - {tc.TriangleCount} tri - {tc.VertexCount} ver - c {matrix.Count} - {(DateTime.UtcNow - pre).TotalMilliseconds}ms"); } - public void Clear() - { - foreach (List list in matrix.GetAllElements()) - { - list.Clear(); - } + if (logger.IsEnabled(LogLevel.Trace)) + logger.LogTrace($"Mesh [||,||] Bounds: [{tc.Min.X:F4}, {tc.Min.Y:F4}] [{tc.Max.X:F4}, {tc.Max.Y:F4}] - {tc.TriangleCount} tri - {tc.VertexCount} ver - c {matrix.Count} - {(DateTime.UtcNow - pre).TotalMilliseconds}ms"); + } - matrix.Clear(); + public void Clear() + { + foreach (List list in matrix.GetAllElements()) + { + list.Clear(); } - public ArraySegment GetAllCloseTo(float x, float y, float distance) - { - (List[] close, int count) = matrix.GetAllInSquare(x - distance, y - distance, x + distance, y + distance); + matrix.Clear(); + } - int totalSize = 0; - for (int i = 0; i < count; i++) - { - totalSize += close[i].Count; - } + public ArraySegment GetAllCloseTo(float x, float y, float distance) + { + (List[] close, int count) = matrix.GetAllInSquare(x - distance, y - distance, x + distance, y + distance); - var pooler = ArrayPool.Shared; - var all = pooler.Rent(totalSize); + int totalSize = 0; + for (int i = 0; i < count; i++) + { + totalSize += close[i].Count; + } - int index = 0; - for (int i = 0; i < count; i++) + var pooler = ArrayPool.Shared; + var all = pooler.Rent(totalSize); + + int index = 0; + for (int i = 0; i < count; i++) + { + Span span = CollectionsMarshal.AsSpan(close[i]); + for (int j = 0; j < span.Length; j++) { - Span span = CollectionsMarshal.AsSpan(close[i]); - for (int j = 0; j < span.Length; j++) - { - all[index++] = span[j]; - } + all[index++] = span[j]; } - - pooler.Return(all); - return new ArraySegment(all, 0, index); } - public ArraySegment GetAllInSquare(float x0, float y0, float x1, float y1) - { - (List[] close, int count) = matrix.GetAllInSquare(x0, y0, x1, y1); + pooler.Return(all); + return new ArraySegment(all, 0, index); + } - int totalSize = 0; - for (int i = 0; i < count; i++) - { - totalSize += close[i].Count; - } + public ArraySegment GetAllInSquare(float x0, float y0, float x1, float y1) + { + (List[] close, int count) = matrix.GetAllInSquare(x0, y0, x1, y1); - var pooler = ArrayPool.Shared; - var all = pooler.Rent(totalSize); + int totalSize = 0; + for (int i = 0; i < count; i++) + { + totalSize += close[i].Count; + } - int index = 0; - for (int i = 0; i < count; i++) + var pooler = ArrayPool.Shared; + var all = pooler.Rent(totalSize); + + int index = 0; + for (int i = 0; i < count; i++) + { + Span span = CollectionsMarshal.AsSpan(close[i]); + for (int j = 0; j < span.Length; j++) { - Span span = CollectionsMarshal.AsSpan(close[i]); - for (int j = 0; j < span.Length; j++) - { - all[index++] = span[j]; - } + all[index++] = span[j]; } - pooler.Return(all); - return new ArraySegment(all, 0, index); } + pooler.Return(all); + return new ArraySegment(all, 0, index); } } \ No newline at end of file diff --git a/PPather/Triangles/Utils.cs b/PPather/Triangles/Utils.cs index b6e0ef060..d1799db8d 100644 --- a/PPather/Triangles/Utils.cs +++ b/PPather/Triangles/Utils.cs @@ -24,309 +24,308 @@ Copyright Pontus Borg 2008 using static System.MathF; using static System.Numerics.Vector3; -namespace WowTriangles +namespace WowTriangles; + +public static class Utils { - public static class Utils + [SkipLocalsInit] + public static bool SegmentTriangleIntersect(in Vector3 p0, in Vector3 p1, + in Vector3 t0, in Vector3 t1, in Vector3 t2, + out Vector3 I) { - [SkipLocalsInit] - public static bool SegmentTriangleIntersect(in Vector3 p0, in Vector3 p1, - in Vector3 t0, in Vector3 t1, in Vector3 t2, - out Vector3 I) + Vector3 u = Subtract(t1, t0); // triangle vector 1 + Vector3 v = Subtract(t2, t0); // triangle vector 2 + Vector3 n = Cross(u, v); // triangle normal + + Vector3 dir = Subtract(p1, p0); // ray direction vector + Vector3 w0 = Subtract(p0, t0); + float a = -Dot(n, w0); + float b = Dot(n, dir); + if (Abs(b) < float.Epsilon) { - Vector3 u = Subtract(t1, t0); // triangle vector 1 - Vector3 v = Subtract(t2, t0); // triangle vector 2 - Vector3 n = Cross(u, v); // triangle normal - - Vector3 dir = Subtract(p1, p0); // ray direction vector - Vector3 w0 = Subtract(p0, t0); - float a = -Dot(n, w0); - float b = Dot(n, dir); - if (Abs(b) < float.Epsilon) - { - I = default; - return false; // parallel - } - - // get intersect point of ray with triangle plane - float r = a / b; - if (r < 0.0f) - { - I = default; - return false; // "before" p0 - } - if (r > 1.0f) - { - I = default; - return false; // "after" p1 - } - - Vector3 M = Multiply(dir, r); - I = Add(p0, M);// intersect point of line and plane - - // is I inside T? - float uu = Dot(u, u); - float uv = Dot(u, v); - float vv = Dot(v, v); - Vector3 w = Subtract(I, t0); - float wu = Dot(w, u); - float wv = Dot(w, v); - float D = uv * uv - uu * vv; - - // get and test parametric coords - float s = (uv * wv - vv * wu) / D; - if (s < 0.0f || s > 1.0f) // I is outside T - return false; - - float t = (uv * wu - uu * wv) / D; - if (t < 0.0f || (s + t) > 1.0f) // I is outside T - return false; - - return true; + I = default; + return false; // parallel } - [SkipLocalsInit] - public static float PointDistanceToSegment(in Vector3 p0, - in Vector3 x1, in Vector3 x2) + // get intersect point of ray with triangle plane + float r = a / b; + if (r < 0.0f) + { + I = default; + return false; // "before" p0 + } + if (r > 1.0f) { - Vector3 L = Subtract(x2, x1); // the segment vector - float l2 = Dot(L, L); // square length of the segment + I = default; + return false; // "after" p1 + } + + Vector3 M = Multiply(dir, r); + I = Add(p0, M);// intersect point of line and plane + + // is I inside T? + float uu = Dot(u, u); + float uv = Dot(u, v); + float vv = Dot(v, v); + Vector3 w = Subtract(I, t0); + float wu = Dot(w, u); + float wv = Dot(w, v); + float D = uv * uv - uu * vv; + + // get and test parametric coords + float s = (uv * wv - vv * wu) / D; + if (s < 0.0f || s > 1.0f) // I is outside T + return false; + + float t = (uv * wu - uu * wv) / D; + if (t < 0.0f || (s + t) > 1.0f) // I is outside T + return false; - Vector3 D = Subtract(p0, x1); // vector from point to segment start - float d = Dot(D, L); // projection factor [x2-x1].[p0-x1] + return true; + } + + [SkipLocalsInit] + public static float PointDistanceToSegment(in Vector3 p0, + in Vector3 x1, in Vector3 x2) + { + Vector3 L = Subtract(x2, x1); // the segment vector + float l2 = Dot(L, L); // square length of the segment - if (d < 0.0f) // closest to x1 - return D.Length(); + Vector3 D = Subtract(p0, x1); // vector from point to segment start + float d = Dot(D, L); // projection factor [x2-x1].[p0-x1] - Vector3 E = Multiply(L, d / l2); // intersect + if (d < 0.0f) // closest to x1 + return D.Length(); - if (Dot(E, L) > l2) // closest to x2 - { - Vector3 L2 = Subtract(D, L); - return L2.Length(); - } + Vector3 E = Multiply(L, d / l2); // intersect - Vector3 L3 = Subtract(D, E); - return L3.Length(); + if (Dot(E, L) > l2) // closest to x2 + { + Vector3 L2 = Subtract(D, L); + return L2.Length(); } - [SkipLocalsInit] - public static void GetTriangleNormal(in Vector3 t0, in Vector3 t1, in Vector3 t2, out Vector3 normal) + Vector3 L3 = Subtract(D, E); + return L3.Length(); + } + + [SkipLocalsInit] + public static void GetTriangleNormal(in Vector3 t0, in Vector3 t1, in Vector3 t2, out Vector3 normal) + { + Vector3 u = Subtract(t1, t0); // triangle vector 1 + Vector3 v = Subtract(t2, t0); // triangle vector 2 + normal = Cross(u, v); // triangle normal + float l = normal.Length(); + normal = Divide(normal, l); + } + + [SkipLocalsInit] + public static float PointDistanceToTriangle(in Vector3 p0, + in Vector3 t0, in Vector3 t1, in Vector3 t2) + { + Vector3 u = Subtract(t1, t0); // triangle vector 1 + Vector3 v = Subtract(t2, t0); // triangle vector 2 + Vector3 n = Cross(u, v); // triangle normal + n.X *= -1E6f; + n.Y *= -1E6f; + n.Z *= -1E6f; + + if (SegmentTriangleIntersect(p0, n, t0, t1, t2, out Vector3 intersect)) { - Vector3 u = Subtract(t1, t0); // triangle vector 1 - Vector3 v = Subtract(t2, t0); // triangle vector 2 - normal = Cross(u, v); // triangle normal - float l = normal.Length(); - normal = Divide(normal, l); + Vector3 L = Subtract(intersect, p0); + return L.Length(); } - [SkipLocalsInit] - public static float PointDistanceToTriangle(in Vector3 p0, - in Vector3 t0, in Vector3 t1, in Vector3 t2) + float d0 = PointDistanceToSegment(p0, t0, t1); + float d1 = PointDistanceToSegment(p0, t0, t1); + float d2 = PointDistanceToSegment(p0, t0, t1); + + return Min(Min(d0, d1), d2); + } + + public static bool TestBoxBoxIntersect(in Vector3 box0_min, in Vector3 box0_max, + in Vector3 box1_min, in Vector3 box1_max) + { + if (box0_min.X > box1_max.X) return false; + if (box0_min.Y > box1_max.Y) return false; + if (box0_min.Z > box1_max.Z) return false; + + if (box1_min.X > box0_max.X) return false; + if (box1_min.Y > box0_max.Y) return false; + if (box1_min.Z > box0_max.Z) return false; + + return true; + } + + // From the book "Real-Time Collision Detection" by Christer Ericson, page 169 + // See also the published Errata at http://realtimecollisiondetection.net/books/rtcd/errata/ + [SkipLocalsInit] + public static bool TestTriangleBoxIntersect(in Vector3 a, in Vector3 b, in Vector3 c, in Vector3 boxCenter, in Vector3 boxExtents) + { + // Translate triangle as conceptually moving AABB to origin + Vector3 v0 = a - boxCenter; + Vector3 v1 = b - boxCenter; + Vector3 v2 = c - boxCenter; + + // Compute edge vectors for triangle + Vector3 f0 = v1 - v0; + Vector3 f1 = v2 - v1; + Vector3 f2 = v0 - v2; + + #region Test axes a00..a22 (category 3) + + // Test axis a00 + Vector3 a00 = new(0, -f0.Z, f0.Y); + float p0 = Dot(v0, a00); + float p1 = Dot(v1, a00); + float p2 = Dot(v2, a00); + float r = boxExtents.Y * Abs(f0.Z) + boxExtents.Z * Abs(f0.Y); + if (Max(-Max3(p0, p1, p2), Min3(p0, p1, p2)) > r) { - Vector3 u = Subtract(t1, t0); // triangle vector 1 - Vector3 v = Subtract(t2, t0); // triangle vector 2 - Vector3 n = Cross(u, v); // triangle normal - n.X *= -1E6f; - n.Y *= -1E6f; - n.Z *= -1E6f; - - if (SegmentTriangleIntersect(p0, n, t0, t1, t2, out Vector3 intersect)) - { - Vector3 L = Subtract(intersect, p0); - return L.Length(); - } - - float d0 = PointDistanceToSegment(p0, t0, t1); - float d1 = PointDistanceToSegment(p0, t0, t1); - float d2 = PointDistanceToSegment(p0, t0, t1); - - return Min(Min(d0, d1), d2); + return false; } - public static bool TestBoxBoxIntersect(in Vector3 box0_min, in Vector3 box0_max, - in Vector3 box1_min, in Vector3 box1_max) + // Test axis a01 + Vector3 a01 = new(0, -f1.Z, f1.Y); + p0 = Dot(v0, a01); + p1 = Dot(v1, a01); + p2 = Dot(v2, a01); + r = boxExtents.Y * Abs(f1.Z) + boxExtents.Z * Abs(f1.Y); + if (Max(-Max3(p0, p1, p2), Min3(p0, p1, p2)) > r) { - if (box0_min.X > box1_max.X) return false; - if (box0_min.Y > box1_max.Y) return false; - if (box0_min.Z > box1_max.Z) return false; + return false; + } - if (box1_min.X > box0_max.X) return false; - if (box1_min.Y > box0_max.Y) return false; - if (box1_min.Z > box0_max.Z) return false; + // Test axis a02 + Vector3 a02 = new(0, -f2.Z, f2.Y); + p0 = Dot(v0, a02); + p1 = Dot(v1, a02); + p2 = Dot(v2, a02); + r = boxExtents.Y * Abs(f2.Z) + boxExtents.Z * Abs(f2.Y); + if (Max(-Max3(p0, p1, p2), Min3(p0, p1, p2)) > r) + { + return false; + } - return true; + // Test axis a10 + Vector3 a10 = new(f0.Z, 0, -f0.X); + p0 = Dot(v0, a10); + p1 = Dot(v1, a10); + p2 = Dot(v2, a10); + r = boxExtents.X * Abs(f0.Z) + boxExtents.Z * Abs(f0.X); + if (Max(-Max3(p0, p1, p2), Min3(p0, p1, p2)) > r) + { + return false; } - // From the book "Real-Time Collision Detection" by Christer Ericson, page 169 - // See also the published Errata at http://realtimecollisiondetection.net/books/rtcd/errata/ - [SkipLocalsInit] - public static bool TestTriangleBoxIntersect(in Vector3 a, in Vector3 b, in Vector3 c, in Vector3 boxCenter, in Vector3 boxExtents) + // Test axis a11 + Vector3 a11 = new(f1.Z, 0, -f1.X); + p0 = Dot(v0, a11); + p1 = Dot(v1, a11); + p2 = Dot(v2, a11); + r = boxExtents.X * Abs(f1.Z) + boxExtents.Z * Abs(f1.X); + if (Max(-Max3(p0, p1, p2), Min3(p0, p1, p2)) > r) { - // Translate triangle as conceptually moving AABB to origin - Vector3 v0 = a - boxCenter; - Vector3 v1 = b - boxCenter; - Vector3 v2 = c - boxCenter; - - // Compute edge vectors for triangle - Vector3 f0 = v1 - v0; - Vector3 f1 = v2 - v1; - Vector3 f2 = v0 - v2; - - #region Test axes a00..a22 (category 3) - - // Test axis a00 - Vector3 a00 = new(0, -f0.Z, f0.Y); - float p0 = Dot(v0, a00); - float p1 = Dot(v1, a00); - float p2 = Dot(v2, a00); - float r = boxExtents.Y * Abs(f0.Z) + boxExtents.Z * Abs(f0.Y); - if (Max(-Max3(p0, p1, p2), Min3(p0, p1, p2)) > r) - { - return false; - } - - // Test axis a01 - Vector3 a01 = new(0, -f1.Z, f1.Y); - p0 = Dot(v0, a01); - p1 = Dot(v1, a01); - p2 = Dot(v2, a01); - r = boxExtents.Y * Abs(f1.Z) + boxExtents.Z * Abs(f1.Y); - if (Max(-Max3(p0, p1, p2), Min3(p0, p1, p2)) > r) - { - return false; - } - - // Test axis a02 - Vector3 a02 = new(0, -f2.Z, f2.Y); - p0 = Dot(v0, a02); - p1 = Dot(v1, a02); - p2 = Dot(v2, a02); - r = boxExtents.Y * Abs(f2.Z) + boxExtents.Z * Abs(f2.Y); - if (Max(-Max3(p0, p1, p2), Min3(p0, p1, p2)) > r) - { - return false; - } - - // Test axis a10 - Vector3 a10 = new(f0.Z, 0, -f0.X); - p0 = Dot(v0, a10); - p1 = Dot(v1, a10); - p2 = Dot(v2, a10); - r = boxExtents.X * Abs(f0.Z) + boxExtents.Z * Abs(f0.X); - if (Max(-Max3(p0, p1, p2), Min3(p0, p1, p2)) > r) - { - return false; - } - - // Test axis a11 - Vector3 a11 = new(f1.Z, 0, -f1.X); - p0 = Dot(v0, a11); - p1 = Dot(v1, a11); - p2 = Dot(v2, a11); - r = boxExtents.X * Abs(f1.Z) + boxExtents.Z * Abs(f1.X); - if (Max(-Max3(p0, p1, p2), Min3(p0, p1, p2)) > r) - { - return false; - } - - // Test axis a12 - Vector3 a12 = new(f2.Z, 0, -f2.X); - p0 = Dot(v0, a12); - p1 = Dot(v1, a12); - p2 = Dot(v2, a12); - r = boxExtents.X * Abs(f2.Z) + boxExtents.Z * Abs(f2.X); - if (Max(-Max3(p0, p1, p2), Min3(p0, p1, p2)) > r) - { - return false; - } - - // Test axis a20 - Vector3 a20 = new(-f0.Y, f0.X, 0); - p0 = Dot(v0, a20); - p1 = Dot(v1, a20); - p2 = Dot(v2, a20); - r = boxExtents.X * Abs(f0.Y) + boxExtents.Y * Abs(f0.X); - if (Max(-Max3(p0, p1, p2), Min3(p0, p1, p2)) > r) - { - return false; - } - - // Test axis a21 - Vector3 a21 = new(-f1.Y, f1.X, 0); - p0 = Dot(v0, a21); - p1 = Dot(v1, a21); - p2 = Dot(v2, a21); - r = boxExtents.X * Abs(f1.Y) + boxExtents.Y * Abs(f1.X); - if (Max(-Max3(p0, p1, p2), Min3(p0, p1, p2)) > r) - { - return false; - } - - // Test axis a22 - Vector3 a22 = new(-f2.Y, f2.X, 0); - p0 = Dot(v0, a22); - p1 = Dot(v1, a22); - p2 = Dot(v2, a22); - r = boxExtents.X * Abs(f2.Y) + boxExtents.Y * Abs(f2.X); - if (Max(-Max3(p0, p1, p2), Min3(p0, p1, p2)) > r) - { - return false; - } - - #endregion - - #region Test the three axes corresponding to the face normals of AABB b (category 1) - - // Exit if... - // ... [-extents.X, extents.X] and [min(v0.X,v1.X,v2.X), max(v0.X,v1.X,v2.X)] do not overlap - if (Max3(v0.X, v1.X, v2.X) < -boxExtents.X || Min3(v0.X, v1.X, v2.X) > boxExtents.X) - { - return false; - } - - // ... [-extents.Y, extents.Y] and [min(v0.Y,v1.Y,v2.Y), max(v0.Y,v1.Y,v2.Y)] do not overlap - if (Max3(v0.Y, v1.Y, v2.Y) < -boxExtents.Y || Min3(v0.Y, v1.Y, v2.Y) > boxExtents.Y) - { - return false; - } - - // ... [-extents.Z, extents.Z] and [min(v0.Z,v1.Z,v2.Z), max(v0.Z,v1.Z,v2.Z)] do not overlap - if (Max3(v0.Z, v1.Z, v2.Z) < -boxExtents.Z || Min3(v0.Z, v1.Z, v2.Z) > boxExtents.Z) - { - return false; - } - - #endregion - - #region Test separating axis corresponding to triangle face normal (category 2) - - Vector3 planeNormal = Cross(f0, f1); - float planeDistance = Dot(planeNormal, v0); - - // Compute the projection interval radius of b onto L(t) = b.c + t * p.n - r = boxExtents.X * Abs(planeNormal.X) - + boxExtents.Y * Abs(planeNormal.Y) - + boxExtents.Z * Abs(planeNormal.Z); - - // Intersection occurs when plane distance falls within [-r,+r] interval - if (planeDistance > r) - { - return false; - } - - #endregion - - return true; + return false; } - public static float Min3(float a, float b, float c) + // Test axis a12 + Vector3 a12 = new(f2.Z, 0, -f2.X); + p0 = Dot(v0, a12); + p1 = Dot(v1, a12); + p2 = Dot(v2, a12); + r = boxExtents.X * Abs(f2.Z) + boxExtents.Z * Abs(f2.X); + if (Max(-Max3(p0, p1, p2), Min3(p0, p1, p2)) > r) { - return Min(a, Min(b, c)); + return false; } - public static float Max3(float a, float b, float c) + // Test axis a20 + Vector3 a20 = new(-f0.Y, f0.X, 0); + p0 = Dot(v0, a20); + p1 = Dot(v1, a20); + p2 = Dot(v2, a20); + r = boxExtents.X * Abs(f0.Y) + boxExtents.Y * Abs(f0.X); + if (Max(-Max3(p0, p1, p2), Min3(p0, p1, p2)) > r) { - return Max(a, Max(b, c)); + return false; } + + // Test axis a21 + Vector3 a21 = new(-f1.Y, f1.X, 0); + p0 = Dot(v0, a21); + p1 = Dot(v1, a21); + p2 = Dot(v2, a21); + r = boxExtents.X * Abs(f1.Y) + boxExtents.Y * Abs(f1.X); + if (Max(-Max3(p0, p1, p2), Min3(p0, p1, p2)) > r) + { + return false; + } + + // Test axis a22 + Vector3 a22 = new(-f2.Y, f2.X, 0); + p0 = Dot(v0, a22); + p1 = Dot(v1, a22); + p2 = Dot(v2, a22); + r = boxExtents.X * Abs(f2.Y) + boxExtents.Y * Abs(f2.X); + if (Max(-Max3(p0, p1, p2), Min3(p0, p1, p2)) > r) + { + return false; + } + + #endregion + + #region Test the three axes corresponding to the face normals of AABB b (category 1) + + // Exit if... + // ... [-extents.X, extents.X] and [min(v0.X,v1.X,v2.X), max(v0.X,v1.X,v2.X)] do not overlap + if (Max3(v0.X, v1.X, v2.X) < -boxExtents.X || Min3(v0.X, v1.X, v2.X) > boxExtents.X) + { + return false; + } + + // ... [-extents.Y, extents.Y] and [min(v0.Y,v1.Y,v2.Y), max(v0.Y,v1.Y,v2.Y)] do not overlap + if (Max3(v0.Y, v1.Y, v2.Y) < -boxExtents.Y || Min3(v0.Y, v1.Y, v2.Y) > boxExtents.Y) + { + return false; + } + + // ... [-extents.Z, extents.Z] and [min(v0.Z,v1.Z,v2.Z), max(v0.Z,v1.Z,v2.Z)] do not overlap + if (Max3(v0.Z, v1.Z, v2.Z) < -boxExtents.Z || Min3(v0.Z, v1.Z, v2.Z) > boxExtents.Z) + { + return false; + } + + #endregion + + #region Test separating axis corresponding to triangle face normal (category 2) + + Vector3 planeNormal = Cross(f0, f1); + float planeDistance = Dot(planeNormal, v0); + + // Compute the projection interval radius of b onto L(t) = b.c + t * p.n + r = boxExtents.X * Abs(planeNormal.X) + + boxExtents.Y * Abs(planeNormal.Y) + + boxExtents.Z * Abs(planeNormal.Z); + + // Intersection occurs when plane distance falls within [-r,+r] interval + if (planeDistance > r) + { + return false; + } + + #endregion + + return true; + } + + public static float Min3(float a, float b, float c) + { + return Min(a, Min(b, c)); + } + + public static float Max3(float a, float b, float c) + { + return Max(a, Max(b, c)); } } \ No newline at end of file diff --git a/PPather/Triangles/WmoFile.cs b/PPather/Triangles/WmoFile.cs index ee7e37308..f2fece468 100644 --- a/PPather/Triangles/WmoFile.cs +++ b/PPather/Triangles/WmoFile.cs @@ -25,1133 +25,1133 @@ Copyright Pontus Borg 2008 using Microsoft.Extensions.Logging; using SharedLib.Data; -namespace Wmo +namespace Wmo; + +internal sealed unsafe class ChunkReader { - internal sealed unsafe class ChunkReader + public const float TILESIZE = 533.33333f; + public const float ZEROPOINT = 32.0f * TILESIZE; + public const float CHUNKSIZE = TILESIZE / 16.0f; + public const float UNITSIZE = CHUNKSIZE / 8.0f; + + public const uint MWMO = 0b_01001101_01010111_01001101_01001111; + public const uint MODF = 0b_01001101_01001111_01000100_01000110; + public const uint MAIN = 0b_01001101_01000001_01001001_01001110; + public const uint MPHD = 0b_01001101_01010000_01001000_01000100; + public const uint MVER = 0b_01001101_01010110_01000101_01010010; + public const uint MOGI = 0b_01001101_01001111_01000111_01001001; + public const uint MOHD = 0b_01001101_01001111_01001000_01000100; + public const uint MODN = 0b_01001101_01001111_01000100_01001110; + public const uint MODS = 0b_01001101_01001111_01000100_01010011; + public const uint MODD = 0b_01001101_01001111_01000100_01000100; + public const uint MOPY = 0b_01001101_01001111_01010000_01011001; + public const uint MOVI = 0b_01001101_01001111_01010110_01001001; + public const uint MOVT = 0b_01001101_01001111_01010110_01010100; + public const uint MCIN = 0b_01001101_01000011_01001001_01001110; + public const uint MMDX = 0b_01001101_01001101_01000100_01011000; + public const uint MDDF = 0b_01001101_01000100_01000100_01000110; + public const uint MCNR = 0b_01001101_01000011_01001110_01010010; + public const uint MCVT = 0b_01001101_01000011_01010110_01010100; + public const uint MCLQ = 0b_01001101_01000011_01001100_01010001; + public const uint MH2O = 0b_01001101_01001000_00110010_01001111; + + public static unsafe string ExtractString(byte[] buff, int off) { - public const float TILESIZE = 533.33333f; - public const float ZEROPOINT = 32.0f * TILESIZE; - public const float CHUNKSIZE = TILESIZE / 16.0f; - public const float UNITSIZE = CHUNKSIZE / 8.0f; - - public const uint MWMO = 0b_01001101_01010111_01001101_01001111; - public const uint MODF = 0b_01001101_01001111_01000100_01000110; - public const uint MAIN = 0b_01001101_01000001_01001001_01001110; - public const uint MPHD = 0b_01001101_01010000_01001000_01000100; - public const uint MVER = 0b_01001101_01010110_01000101_01010010; - public const uint MOGI = 0b_01001101_01001111_01000111_01001001; - public const uint MOHD = 0b_01001101_01001111_01001000_01000100; - public const uint MODN = 0b_01001101_01001111_01000100_01001110; - public const uint MODS = 0b_01001101_01001111_01000100_01010011; - public const uint MODD = 0b_01001101_01001111_01000100_01000100; - public const uint MOPY = 0b_01001101_01001111_01010000_01011001; - public const uint MOVI = 0b_01001101_01001111_01010110_01001001; - public const uint MOVT = 0b_01001101_01001111_01010110_01010100; - public const uint MCIN = 0b_01001101_01000011_01001001_01001110; - public const uint MMDX = 0b_01001101_01001101_01000100_01011000; - public const uint MDDF = 0b_01001101_01000100_01000100_01000110; - public const uint MCNR = 0b_01001101_01000011_01001110_01010010; - public const uint MCVT = 0b_01001101_01000011_01010110_01010100; - public const uint MCLQ = 0b_01001101_01000011_01001100_01010001; - public const uint MH2O = 0b_01001101_01001000_00110010_01001111; - - public static unsafe string ExtractString(byte[] buff, int off) + fixed (byte* bp = buff) { - fixed (byte* bp = buff) - { - sbyte* sp = (sbyte*)bp; - sp += off; - return new string(sp); - } + sbyte* sp = (sbyte*)bp; + sp += off; + return new string(sp); } } +} - public sealed class WMOManager : Manager +public sealed class WMOManager : Manager +{ + private readonly StormDll.ArchiveSet archive; + private readonly ModelManager modelmanager; + private readonly DataConfig dataConfig; + + public WMOManager(StormDll.ArchiveSet archive, ModelManager modelmanager, int maxItems, DataConfig dataConfig) + : base(maxItems) { - private readonly StormDll.ArchiveSet archive; - private readonly ModelManager modelmanager; - private readonly DataConfig dataConfig; + this.archive = archive; + this.modelmanager = modelmanager; + this.dataConfig = dataConfig; + } - public WMOManager(StormDll.ArchiveSet archive, ModelManager modelmanager, int maxItems, DataConfig dataConfig) - : base(maxItems) + public override bool Load(string path, out WMO t) + { + string tempFile = Path.Join(dataConfig.PPather, "wmo.tmp"); //wmo + if (!archive.SFileExtractFile(path, tempFile)) { - this.archive = archive; - this.modelmanager = modelmanager; - this.dataConfig = dataConfig; + t = default; + return false; } - public override bool Load(string path, out WMO t) + t = new() { - string tempFile = Path.Join(dataConfig.PPather, "wmo.tmp"); //wmo - if (!archive.SFileExtractFile(path, tempFile)) - { - t = default; - return false; - } - - t = new() - { - fileName = path - }; - - _ = new WmoRootFile(tempFile, t, modelmanager); + fileName = path + }; - for (int i = 0; i < t.groups.Length; i++) - { - ReadOnlySpan part = path[..^4].AsSpan(); - string gf = string.Format("{0}_{1,3:000}.wmo", part.ToString(), i); + _ = new WmoRootFile(tempFile, t, modelmanager); - if (!archive.SFileExtractFile(gf, tempFile)) - continue; + for (int i = 0; i < t.groups.Length; i++) + { + ReadOnlySpan part = path[..^4].AsSpan(); + string gf = string.Format("{0}_{1,3:000}.wmo", part.ToString(), i); - _ = new WmoGroupFile(t.groups[i], tempFile); - } - return true; - } - } + if (!archive.SFileExtractFile(gf, tempFile)) + continue; - public readonly struct WMOInstance - { - public readonly WMO wmo; - public readonly int id; - public readonly Vector3 pos, pos2, pos3; - public readonly Vector3 dir; - public readonly int d2; //d3 - public readonly int doodadset; - - public WMOInstance(BinaryReader file, WMO wmo) - { - // read X bytes from file - this.wmo = wmo; - - id = file.ReadInt32(); - pos = new Vector3(file.ReadSingle(), file.ReadSingle(), file.ReadSingle()); - dir = new Vector3(file.ReadSingle(), file.ReadSingle(), file.ReadSingle()); - pos2 = new Vector3(file.ReadSingle(), file.ReadSingle(), file.ReadSingle()); - pos3 = new Vector3(file.ReadSingle(), file.ReadSingle(), file.ReadSingle()); - - d2 = file.ReadInt32(); - doodadset = file.ReadInt16(); - _ = file.ReadInt16(); + _ = new WmoGroupFile(t.groups[i], tempFile); } + return true; } +} - public struct DoodadSet +public readonly struct WMOInstance +{ + public readonly WMO wmo; + public readonly int id; + public readonly Vector3 pos, pos2, pos3; + public readonly Vector3 dir; + public readonly int d2; //d3 + public readonly int doodadset; + + public WMOInstance(BinaryReader file, WMO wmo) { - public uint firstInstance; - public uint nInstances; + // read X bytes from file + this.wmo = wmo; + + id = file.ReadInt32(); + pos = new Vector3(file.ReadSingle(), file.ReadSingle(), file.ReadSingle()); + dir = new Vector3(file.ReadSingle(), file.ReadSingle(), file.ReadSingle()); + pos2 = new Vector3(file.ReadSingle(), file.ReadSingle(), file.ReadSingle()); + pos3 = new Vector3(file.ReadSingle(), file.ReadSingle(), file.ReadSingle()); + + d2 = file.ReadInt32(); + doodadset = file.ReadInt16(); + _ = file.ReadInt16(); } +} - public sealed class WMO - { - public string fileName; - public WMOGroup[] groups; +public struct DoodadSet +{ + public uint firstInstance; + public uint nInstances; +} - //int nTextures, nGroups, nP, nLight nX; - public Vector3 v1, v2; // bounding box +public sealed class WMO +{ + public string fileName; + public WMOGroup[] groups; - public byte[] MODNraw; - public uint nModels; - public uint nDoodads; - public uint nDoodadSets; + //int nTextures, nGroups, nP, nLight nX; + public Vector3 v1, v2; // bounding box - public DoodadSet[] doodads; - public ModelInstance[] doodadInstances; - } + public byte[] MODNraw; + public uint nModels; + public uint nDoodads; + public uint nDoodadSets; - public abstract class Manager - { - private readonly Dictionary items; + public DoodadSet[] doodads; + public ModelInstance[] doodadInstances; +} - private readonly int maxItems; +public abstract class Manager +{ + private readonly Dictionary items; - public Manager(int maxItems) - { - this.maxItems = maxItems; + private readonly int maxItems; - items = new Dictionary(maxItems, StringComparer.OrdinalIgnoreCase); - } + public Manager(int maxItems) + { + this.maxItems = maxItems; - public abstract bool Load(string path, out T t); + items = new Dictionary(maxItems, StringComparer.OrdinalIgnoreCase); + } - public void Clear() - { - items.Clear(); - } + public abstract bool Load(string path, out T t); - public T AddAndLoadIfNeeded(string path) - { - if (!items.TryGetValue(path, out T t)) - { - if (Load(path, out t)) - { - items[path] = t; - } - } - return t; - } + public void Clear() + { + items.Clear(); } - public sealed class ModelManager : Manager + public T AddAndLoadIfNeeded(string path) { - private readonly StormDll.ArchiveSet archive; - private readonly DataConfig dataConfig; - - public ModelManager(StormDll.ArchiveSet archive, int maxModels, DataConfig dataConfig) - : base(maxModels) - { - this.archive = archive; - this.dataConfig = dataConfig; - } - - public override bool Load(string path, out Model t) + if (!items.TryGetValue(path, out T t)) { - // change .mdx to .m2 - //string file=path.Substring(0, path.Length-4)+".m2"; - if (Path.GetExtension(path).Equals(".mdx") || Path.GetExtension(path).Equals(".mdl")) - { - path = Path.ChangeExtension(path, ".m2"); - } - - string tempFile = Path.Join(dataConfig.PPather, "model.tmp"); //model - if (!archive.SFileExtractFile(path, tempFile)) + if (Load(path, out t)) { - t = default; - return false; + items[path] = t; } - - t = ModelFile.Read(tempFile, path); - return true; } + return t; } +} + +public sealed class ModelManager : Manager +{ + private readonly StormDll.ArchiveSet archive; + private readonly DataConfig dataConfig; - public readonly struct ModelInstance + public ModelManager(StormDll.ArchiveSet archive, int maxModels, DataConfig dataConfig) + : base(maxModels) { - public readonly Model model; - public readonly Vector3 pos; - public readonly Vector3 dir; - public readonly float w; - public readonly float scale; + this.archive = archive; + this.dataConfig = dataConfig; + } - public ModelInstance(BinaryReader file, Model model) + public override bool Load(string path, out Model t) + { + // change .mdx to .m2 + //string file=path.Substring(0, path.Length-4)+".m2"; + if (Path.GetExtension(path).Equals(".mdx") || Path.GetExtension(path).Equals(".mdl")) { - this.model = model; - _ = file.ReadUInt32(); // uint d1 - pos = new(file.ReadSingle(), file.ReadSingle(), file.ReadSingle()); - dir = new(file.ReadSingle(), file.ReadSingle(), file.ReadSingle()); - w = 0; - scale = file.ReadUInt32() / 1024.0f; + path = Path.ChangeExtension(path, ".m2"); } - public ModelInstance(Model m, Vector3 pos, Vector3 dir, float sc, float w) + string tempFile = Path.Join(dataConfig.PPather, "model.tmp"); //model + if (!archive.SFileExtractFile(path, tempFile)) { - this.model = m; - this.pos = pos; - this.dir = dir; - this.scale = sc; - this.w = w; + t = default; + return false; } + + t = ModelFile.Read(tempFile, path); + return true; } +} - public readonly struct Model - { - public readonly string fileName; - public readonly float[] vertices; // 3 per vertex - public readonly float[] boundingVertices; // 3 per vertex - public readonly ushort[] boundingTriangles; +public readonly struct ModelInstance +{ + public readonly Model model; + public readonly Vector3 pos; + public readonly Vector3 dir; + public readonly float w; + public readonly float scale; - public Model(string fileName, float[] vertices, ushort[] boundingTriangles, float[] boundingVertices) - { - this.fileName = fileName; - this.vertices = vertices; - this.boundingTriangles = boundingTriangles; - this.boundingVertices = boundingVertices; - } + public ModelInstance(BinaryReader file, Model model) + { + this.model = model; + _ = file.ReadUInt32(); // uint d1 + pos = new(file.ReadSingle(), file.ReadSingle(), file.ReadSingle()); + dir = new(file.ReadSingle(), file.ReadSingle(), file.ReadSingle()); + w = 0; + scale = file.ReadUInt32() / 1024.0f; } - public static class ModelFile + public ModelInstance(Model m, Vector3 pos, Vector3 dir, float sc, float w) { - public static Model Read(string path, string fileName) - { - using Stream stream = File.OpenRead(path); - using BinaryReader file = new(stream); - - // UPDATED FOR WOTLK 17.10.2008 by toblakai - // SOURCE: http://www.madx.dk/wowdev/wiki/index.php?title=M2/WotLK - - _ = file.ReadChars(4); - //PPather.Debug("M2 MAGIC: {0}",new string(Magic)); - _ = file.ReadUInt32(); // (including \0); - // check that we have the new known WOTLK Magic 0x80100000 - //PPather.Debug("M2 HEADER VERSION: 0x{0:x8}", - // (uint) (version >> 24) | ((version << 8) & 0x00FF0000) | ((version >> 8) & 0x0000FF00) | (version << 24)); - _ = file.ReadUInt32(); // (including \0); - _ = file.ReadUInt32(); - _ = file.ReadUInt32(); // ? always 0, 1 or 3 (mostly 0); - _ = file.ReadUInt32(); // - number of global sequences; - _ = file.ReadUInt32(); // - offset to global sequences; - _ = file.ReadUInt32(); // - number of animation sequences; - _ = file.ReadUInt32(); // - offset to animation sequences; - _ = file.ReadUInt32(); - _ = file.ReadUInt32(); // Mapping of global IDs to the entries in the Animation sequences block. - // NOT IN WOTLK uint nD=file.ReadUInt32(); // - always 201 or 203 depending on WoW client version; - // NOT IN WOTLK uint ofsD=file.ReadUInt32(); - _ = file.ReadUInt32(); // - number of bones; - _ = file.ReadUInt32(); // - offset to bones; - _ = file.ReadUInt32(); // - bone lookup table; - _ = file.ReadUInt32(); + this.model = m; + this.pos = pos; + this.dir = dir; + this.scale = sc; + this.w = w; + } +} - uint nVertices = file.ReadUInt32(); // - number of vertices; - uint ofsVertices = file.ReadUInt32(); // - offset to vertices; - - _ = file.ReadUInt32(); // - number of views (LOD versions?) 4 for every model; - // NOT IN WOTLK (now in .skins) uint ofsViews=file.ReadUInt32(); // - offset to views; - _ = file.ReadUInt32(); // - number of color definitions; - _ = file.ReadUInt32(); // - offset to color definitions; - _ = file.ReadUInt32(); // - number of textures; - _ = file.ReadUInt32(); // - offset to texture definitions; - _ = file.ReadUInt32(); // - number of transparency definitions; - _ = file.ReadUInt32(); // - offset to transparency definitions; - // NOT IN WOTLK uint nTexAnims = file.ReadUInt32(); // - number of texture animations; - // NOT IN WOTLK uint ofsTexAnims = file.ReadUInt32(); // - offset to texture animations; - _ = file.ReadUInt32(); // - always 0; - _ = file.ReadUInt32(); - _ = file.ReadUInt32(); - _ = file.ReadUInt32(); - _ = file.ReadUInt32(); // - number of blending mode definitions; - _ = file.ReadUInt32(); // - offset to blending mode definitions; - _ = file.ReadUInt32(); // - bone lookup table; - _ = file.ReadUInt32(); - _ = file.ReadUInt32(); // - number of texture lookup table entries; - _ = file.ReadUInt32(); // - offset to texture lookup table; - _ = file.ReadUInt32(); // - texture unit definitions?; - _ = file.ReadUInt32(); - _ = file.ReadUInt32(); // - number of transparency lookup table entries; - _ = file.ReadUInt32(); // - offset to transparency lookup table; - _ = file.ReadUInt32(); // - number of texture animation lookup table entries; - _ = file.ReadUInt32(); // - offset to texture animation lookup table; +public readonly struct Model +{ + public readonly string fileName; + public readonly float[] vertices; // 3 per vertex + public readonly float[] boundingVertices; // 3 per vertex + public readonly ushort[] boundingTriangles; - //float[] theFloats = new float[14]; // Noone knows. Meeh, they are here. - for (int i = 0; i < 14; i++) - file.ReadSingle(); + public Model(string fileName, float[] vertices, ushort[] boundingTriangles, float[] boundingVertices) + { + this.fileName = fileName; + this.vertices = vertices; + this.boundingTriangles = boundingTriangles; + this.boundingVertices = boundingVertices; + } +} - uint nBoundingTriangles = file.ReadUInt32(); - uint ofsBoundingTriangles = file.ReadUInt32(); - uint nBoundingVertices = file.ReadUInt32(); - uint ofsBoundingVertices = file.ReadUInt32(); +public static class ModelFile +{ + public static Model Read(string path, string fileName) + { + using Stream stream = File.OpenRead(path); + using BinaryReader file = new(stream); + + // UPDATED FOR WOTLK 17.10.2008 by toblakai + // SOURCE: http://www.madx.dk/wowdev/wiki/index.php?title=M2/WotLK + + _ = file.ReadChars(4); + //PPather.Debug("M2 MAGIC: {0}",new string(Magic)); + _ = file.ReadUInt32(); // (including \0); + // check that we have the new known WOTLK Magic 0x80100000 + //PPather.Debug("M2 HEADER VERSION: 0x{0:x8}", + // (uint) (version >> 24) | ((version << 8) & 0x00FF0000) | ((version >> 8) & 0x0000FF00) | (version << 24)); + _ = file.ReadUInt32(); // (including \0); + _ = file.ReadUInt32(); + _ = file.ReadUInt32(); // ? always 0, 1 or 3 (mostly 0); + _ = file.ReadUInt32(); // - number of global sequences; + _ = file.ReadUInt32(); // - offset to global sequences; + _ = file.ReadUInt32(); // - number of animation sequences; + _ = file.ReadUInt32(); // - offset to animation sequences; + _ = file.ReadUInt32(); + _ = file.ReadUInt32(); // Mapping of global IDs to the entries in the Animation sequences block. + // NOT IN WOTLK uint nD=file.ReadUInt32(); // - always 201 or 203 depending on WoW client version; + // NOT IN WOTLK uint ofsD=file.ReadUInt32(); + _ = file.ReadUInt32(); // - number of bones; + _ = file.ReadUInt32(); // - offset to bones; + _ = file.ReadUInt32(); // - bone lookup table; + _ = file.ReadUInt32(); + + uint nVertices = file.ReadUInt32(); // - number of vertices; + uint ofsVertices = file.ReadUInt32(); // - offset to vertices; + + _ = file.ReadUInt32(); // - number of views (LOD versions?) 4 for every model; + // NOT IN WOTLK (now in .skins) uint ofsViews=file.ReadUInt32(); // - offset to views; + _ = file.ReadUInt32(); // - number of color definitions; + _ = file.ReadUInt32(); // - offset to color definitions; + _ = file.ReadUInt32(); // - number of textures; + _ = file.ReadUInt32(); // - offset to texture definitions; + _ = file.ReadUInt32(); // - number of transparency definitions; + _ = file.ReadUInt32(); // - offset to transparency definitions; + // NOT IN WOTLK uint nTexAnims = file.ReadUInt32(); // - number of texture animations; + // NOT IN WOTLK uint ofsTexAnims = file.ReadUInt32(); // - offset to texture animations; + _ = file.ReadUInt32(); // - always 0; + _ = file.ReadUInt32(); + _ = file.ReadUInt32(); + _ = file.ReadUInt32(); + _ = file.ReadUInt32(); // - number of blending mode definitions; + _ = file.ReadUInt32(); // - offset to blending mode definitions; + _ = file.ReadUInt32(); // - bone lookup table; + _ = file.ReadUInt32(); + _ = file.ReadUInt32(); // - number of texture lookup table entries; + _ = file.ReadUInt32(); // - offset to texture lookup table; + _ = file.ReadUInt32(); // - texture unit definitions?; + _ = file.ReadUInt32(); + _ = file.ReadUInt32(); // - number of transparency lookup table entries; + _ = file.ReadUInt32(); // - offset to transparency lookup table; + _ = file.ReadUInt32(); // - number of texture animation lookup table entries; + _ = file.ReadUInt32(); // - offset to texture animation lookup table; + + //float[] theFloats = new float[14]; // Noone knows. Meeh, they are here. + for (int i = 0; i < 14; i++) + file.ReadSingle(); + + uint nBoundingTriangles = file.ReadUInt32(); + uint ofsBoundingTriangles = file.ReadUInt32(); + uint nBoundingVertices = file.ReadUInt32(); + uint ofsBoundingVertices = file.ReadUInt32(); + + _ = file.ReadUInt32(); + _ = file.ReadUInt32(); + _ = file.ReadUInt32(); + _ = file.ReadUInt32(); + _ = file.ReadUInt32(); + _ = file.ReadUInt32(); + _ = file.ReadUInt32(); + _ = file.ReadUInt32(); + _ = file.ReadUInt32(); // - number of lights; + _ = file.ReadUInt32(); // - offset to lights; + _ = file.ReadUInt32(); // - number of cameras; + _ = file.ReadUInt32(); // - offset to cameras; + _ = file.ReadUInt32(); + _ = file.ReadUInt32(); + _ = file.ReadUInt32(); // - number of ribbon emitters; + _ = file.ReadUInt32(); // - offset to ribbon emitters; + _ = file.ReadUInt32(); // - number of particle emitters; + _ = file.ReadUInt32(); // - offset to particle emitters; + + return new( + fileName, + ReadVertices(file, nVertices, ofsVertices), + ReadBoundingTriangles(file, nBoundingTriangles, ofsBoundingTriangles), + ReadBoundingVertices(file, nBoundingVertices, ofsBoundingVertices)); + } - _ = file.ReadUInt32(); - _ = file.ReadUInt32(); - _ = file.ReadUInt32(); - _ = file.ReadUInt32(); - _ = file.ReadUInt32(); - _ = file.ReadUInt32(); - _ = file.ReadUInt32(); - _ = file.ReadUInt32(); - _ = file.ReadUInt32(); // - number of lights; - _ = file.ReadUInt32(); // - offset to lights; - _ = file.ReadUInt32(); // - number of cameras; - _ = file.ReadUInt32(); // - offset to cameras; - _ = file.ReadUInt32(); - _ = file.ReadUInt32(); - _ = file.ReadUInt32(); // - number of ribbon emitters; - _ = file.ReadUInt32(); // - offset to ribbon emitters; - _ = file.ReadUInt32(); // - number of particle emitters; - _ = file.ReadUInt32(); // - offset to particle emitters; - - return new( - fileName, - ReadVertices(file, nVertices, ofsVertices), - ReadBoundingTriangles(file, nBoundingTriangles, ofsBoundingTriangles), - ReadBoundingVertices(file, nBoundingVertices, ofsBoundingVertices)); - } + private static float[] ReadBoundingVertices(BinaryReader file, uint nVertices, uint ofsVertices) + { + if (nVertices == 0) + return Array.Empty(); - private static float[] ReadBoundingVertices(BinaryReader file, uint nVertices, uint ofsVertices) + file.BaseStream.Seek(ofsVertices, SeekOrigin.Begin); + float[] vertices = new float[nVertices * 3]; + for (int i = 0; i < vertices.Length; i++) { - if (nVertices == 0) - return Array.Empty(); - - file.BaseStream.Seek(ofsVertices, SeekOrigin.Begin); - float[] vertices = new float[nVertices * 3]; - for (int i = 0; i < vertices.Length; i++) - { - vertices[i] = file.ReadSingle(); - } - - return vertices; + vertices[i] = file.ReadSingle(); } - private static ushort[] ReadBoundingTriangles(BinaryReader file, uint nTriangles, uint ofsTriangles) - { - if (nTriangles == 0) - return Array.Empty(); + return vertices; + } - file.BaseStream.Seek(ofsTriangles, SeekOrigin.Begin); - ushort[] triangles = new ushort[nTriangles]; + private static ushort[] ReadBoundingTriangles(BinaryReader file, uint nTriangles, uint ofsTriangles) + { + if (nTriangles == 0) + return Array.Empty(); - for (int i = 0; i < triangles.Length; i++) - { - triangles[i] = file.ReadUInt16(); - } - return triangles; - } + file.BaseStream.Seek(ofsTriangles, SeekOrigin.Begin); + ushort[] triangles = new ushort[nTriangles]; - private static float[] ReadVertices(BinaryReader file, uint nVertices, uint ofcVertices) + for (int i = 0; i < triangles.Length; i++) { - float[] vertices = new float[nVertices * 3]; - - file.BaseStream.Seek(ofcVertices, SeekOrigin.Begin); - for (int i = 0; i < nVertices; i++) - { - vertices[i * 3 + 0] = file.ReadSingle(); - vertices[i * 3 + 1] = file.ReadSingle(); - vertices[i * 3 + 2] = file.ReadSingle(); - - _ = file.ReadUInt32(); // bone weights - _ = file.ReadUInt32(); // bone indices - - _ = file.ReadSingle(); // normal *3 - _ = file.ReadSingle(); - _ = file.ReadSingle(); - - _ = file.ReadSingle(); // texture coordinates - _ = file.ReadSingle(); - - _ = file.ReadSingle(); // some crap - _ = file.ReadSingle(); - } - return vertices; + triangles[i] = file.ReadUInt16(); } + return triangles; } - public sealed class WMOGroup + private static float[] ReadVertices(BinaryReader file, uint nVertices, uint ofcVertices) { - public uint nameStart, nameStart2; - public uint flags; - public Vector3 v1; - public Vector3 v2; - public UInt16 batchesA; - public UInt16 batchesB; - public UInt16 batchesC; - public UInt16 batchesD; - public UInt16 portalStart; - public UInt16 portalCount; - public uint id; - - public uint nVertices; - public float[] vertices; // 3 per vertex - - public uint nTriangles; - public UInt16[] triangles; // 3 per triangle - public UInt16[] materials; // 1 per triangle - - public const UInt16 MAT_FLAG_NOCAMCOLLIDE = 0x001; - public const UInt16 MAT_FLAG_DETAIL = 0x002; - public const UInt16 MAT_FLAG_COLLISION = 0x004; - public const UInt16 MAT_FLAG_HINT = 0x008; - public const UInt16 MAT_FLAG_RENDER = 0x010; - public const UInt16 MAT_FLAG_COLLIDE_HIT = 0x020; - } + float[] vertices = new float[nVertices * 3]; - internal sealed class WDT - { - public const int SIZE = 64; + file.BaseStream.Seek(ofcVertices, SeekOrigin.Begin); + for (int i = 0; i < nVertices; i++) + { + vertices[i * 3 + 0] = file.ReadSingle(); + vertices[i * 3 + 1] = file.ReadSingle(); + vertices[i * 3 + 2] = file.ReadSingle(); - public readonly bool[] maps = new bool[SIZE * SIZE]; - public readonly MapTile[] maptiles = new MapTile[SIZE * SIZE]; - public readonly bool[] loaded = new bool[SIZE * SIZE]; - public WMOInstance[] gwmois = Array.Empty(); - } + _ = file.ReadUInt32(); // bone weights + _ = file.ReadUInt32(); // bone indices - internal sealed class WDTFile - { - private readonly ILogger logger; - private readonly DataConfig dataConfig; - private readonly WMOManager wmomanager; - private readonly ModelManager modelmanager; - private readonly WDT wdt; - private readonly StormDll.ArchiveSet archive; + _ = file.ReadSingle(); // normal *3 + _ = file.ReadSingle(); + _ = file.ReadSingle(); - private readonly string pathName; + _ = file.ReadSingle(); // texture coordinates + _ = file.ReadSingle(); - public bool loaded; + _ = file.ReadSingle(); // some crap + _ = file.ReadSingle(); + } + return vertices; + } +} - public WDTFile(StormDll.ArchiveSet archive, float mapId, WDT wdt, WMOManager wmomanager, ModelManager modelmanager, ILogger logger, DataConfig dataConfig) - { - this.logger = logger; - this.dataConfig = dataConfig; - this.pathName = ContinentDB.IdToName[mapId]; +public sealed class WMOGroup +{ + public uint nameStart, nameStart2; + public uint flags; + public Vector3 v1; + public Vector3 v2; + public UInt16 batchesA; + public UInt16 batchesB; + public UInt16 batchesC; + public UInt16 batchesD; + public UInt16 portalStart; + public UInt16 portalCount; + public uint id; + + public uint nVertices; + public float[] vertices; // 3 per vertex + + public uint nTriangles; + public UInt16[] triangles; // 3 per triangle + public UInt16[] materials; // 1 per triangle + + public const UInt16 MAT_FLAG_NOCAMCOLLIDE = 0x001; + public const UInt16 MAT_FLAG_DETAIL = 0x002; + public const UInt16 MAT_FLAG_COLLISION = 0x004; + public const UInt16 MAT_FLAG_HINT = 0x008; + public const UInt16 MAT_FLAG_RENDER = 0x010; + public const UInt16 MAT_FLAG_COLLIDE_HIT = 0x020; +} + +internal sealed class WDT +{ + public const int SIZE = 64; - this.wdt = wdt; - this.wmomanager = wmomanager; - this.modelmanager = modelmanager; - this.archive = archive; + public readonly bool[] maps = new bool[SIZE * SIZE]; + public readonly MapTile[] maptiles = new MapTile[SIZE * SIZE]; + public readonly bool[] loaded = new bool[SIZE * SIZE]; + public WMOInstance[] gwmois = Array.Empty(); +} - string wdtfile = Path.Join("World", "Maps", pathName, pathName + ".wdt"); - string tempFile = Path.Join(dataConfig.PPather, "wdt.tmp"); //wdt - if (!archive.SFileExtractFile(wdtfile, tempFile)) - return; +internal sealed class WDTFile +{ + private readonly ILogger logger; + private readonly DataConfig dataConfig; + private readonly WMOManager wmomanager; + private readonly ModelManager modelmanager; + private readonly WDT wdt; + private readonly StormDll.ArchiveSet archive; - using Stream stream = File.OpenRead(tempFile); - using BinaryReader file = new(stream); + private readonly string pathName; - List gwmos = new(); + public bool loaded; - do - { - uint type = file.ReadUInt32(); - uint size = file.ReadUInt32(); - long curpos = file.BaseStream.Position; + public WDTFile(StormDll.ArchiveSet archive, float mapId, WDT wdt, WMOManager wmomanager, ModelManager modelmanager, ILogger logger, DataConfig dataConfig) + { + this.logger = logger; + this.dataConfig = dataConfig; + this.pathName = ContinentDB.IdToName[mapId]; - if (type == ChunkReader.MVER) - { - } - else if (type == ChunkReader.MPHD) - { - } - else if (type == ChunkReader.MODF) - { - HandleMODF(file, wdt, gwmos, wmomanager, size); - } - else if (type == ChunkReader.MWMO) - { - HandleMWMO(file, gwmos, size); - } - else if (type == ChunkReader.MAIN) - { - HandleMAIN(file, size); - } - else - { - logger.LogWarning("WDT Unknown " + type); - } - file.BaseStream.Seek(curpos + size, SeekOrigin.Begin); + this.wdt = wdt; + this.wmomanager = wmomanager; + this.modelmanager = modelmanager; + this.archive = archive; - } while (file.BaseStream.Position < file.BaseStream.Length); + string wdtfile = Path.Join("World", "Maps", pathName, pathName + ".wdt"); + string tempFile = Path.Join(dataConfig.PPather, "wdt.tmp"); //wdt + if (!archive.SFileExtractFile(wdtfile, tempFile)) + return; - loaded = true; - } + using Stream stream = File.OpenRead(tempFile); + using BinaryReader file = new(stream); + + List gwmos = new(); - public void LoadMapTile(int x, int y, int index) + do { - if (!wdt.maps[index]) - return; + uint type = file.ReadUInt32(); + uint size = file.ReadUInt32(); + long curpos = file.BaseStream.Position; - string filename = Path.Join("World", "Maps", pathName, $"{pathName}_{x}_{y}.adt"); - string tempFile = Path.Join(dataConfig.PPather, "adt.tmp"); //adt - if (!archive.SFileExtractFile(filename, tempFile)) - return; + if (type == ChunkReader.MVER) + { + } + else if (type == ChunkReader.MPHD) + { + } + else if (type == ChunkReader.MODF) + { + HandleMODF(file, wdt, gwmos, wmomanager, size); + } + else if (type == ChunkReader.MWMO) + { + HandleMWMO(file, gwmos, size); + } + else if (type == ChunkReader.MAIN) + { + HandleMAIN(file, size); + } + else + { + logger.LogWarning("WDT Unknown " + type); + } + file.BaseStream.Seek(curpos + size, SeekOrigin.Begin); - if (logger.IsEnabled(LogLevel.Trace)) - logger.LogTrace($"Reading adt: {filename}"); + } while (file.BaseStream.Position < file.BaseStream.Length); - wdt.maptiles[index] = MapTileFile.Read(tempFile, wmomanager, modelmanager); - wdt.loaded[index] = true; - } + loaded = true; + } - private static void HandleMWMO(BinaryReader file, List gwmos, uint size) - { - if (size != 0) - { - int l = 0; - byte[] raw = file.ReadBytes((int)size); - while (l < size) - { - string s = ChunkReader.ExtractString(raw, l); - l += s.Length + 1; - gwmos.Add(s); - } - } - } + public void LoadMapTile(int x, int y, int index) + { + if (!wdt.maps[index]) + return; - private static void HandleMODF(BinaryReader file, WDT wdt, List gwmos, WMOManager wmomanager, uint size) - { - // global wmo instance data - int gnWMO = (int)size / 64; - wdt.gwmois = new WMOInstance[gnWMO]; + string filename = Path.Join("World", "Maps", pathName, $"{pathName}_{x}_{y}.adt"); + string tempFile = Path.Join(dataConfig.PPather, "adt.tmp"); //adt + if (!archive.SFileExtractFile(filename, tempFile)) + return; - for (uint i = 0; i < gnWMO; i++) - { - int id = file.ReadInt32(); - string path = gwmos[id]; + if (logger.IsEnabled(LogLevel.Trace)) + logger.LogTrace($"Reading adt: {filename}"); - WMO wmo = wmomanager.AddAndLoadIfNeeded(path); - wdt.gwmois[i] = new(file, wmo); - } - } + wdt.maptiles[index] = MapTileFile.Read(tempFile, wmomanager, modelmanager); + wdt.loaded[index] = true; + } - private void HandleMAIN(BinaryReader file, uint size) + private static void HandleMWMO(BinaryReader file, List gwmos, uint size) + { + if (size != 0) { - // global map objects - for (int index = 0; index < WDT.SIZE * WDT.SIZE; index++) + int l = 0; + byte[] raw = file.ReadBytes((int)size); + while (l < size) { - wdt.maps[index] = file.ReadInt32() != 0; - file.ReadInt32(); // kasta + string s = ChunkReader.ExtractString(raw, l); + l += s.Length + 1; + gwmos.Add(s); } } } - internal readonly struct MapChunk + private static void HandleMODF(BinaryReader file, WDT wdt, List gwmos, WMOManager wmomanager, uint size) { - public readonly float xbase, ybase, zbase; - public readonly uint areaID; - public readonly bool haswater; - public readonly bool hasholes; - public readonly uint holes; - //public float waterlevel; - - // 0 1 2 3 4 5 6 7 8 - // 9 10 11 12 13 14 15 16 - // 17 18 19 20 21 22 23 24 25 - // ... - public readonly float[] vertices; - - public readonly float water_height1; - public readonly float water_height2; - public readonly float[] water_height; - public readonly byte[] water_flags; - - - private static readonly int[] holetab_h = new int[] { 0x1111, 0x2222, 0x4444, 0x8888 }; - private static readonly int[] holetab_v = new int[] { 0x000F, 0x00F0, 0x0F00, 0xF000 }; + // global wmo instance data + int gnWMO = (int)size / 64; + wdt.gwmois = new WMOInstance[gnWMO]; - // 0 ..3, 0 ..3 - public bool isHole(int i, int j) + for (uint i = 0; i < gnWMO; i++) { - if (!hasholes) - return false; - i /= 2; - j /= 2; + int id = file.ReadInt32(); + string path = gwmos[id]; - return i <= 3 && j <= 3 && (holes & holetab_h[i] & holetab_v[j]) != 0; + WMO wmo = wmomanager.AddAndLoadIfNeeded(path); + wdt.gwmois[i] = new(file, wmo); } + } - - public MapChunk(float xbase, float ybase, float zbase, - uint areaID, bool haswater, uint holes, float[] vertices, - float water_height1, float water_height2, float[] water_height, byte[] water_flags) + private void HandleMAIN(BinaryReader file, uint size) + { + // global map objects + for (int index = 0; index < WDT.SIZE * WDT.SIZE; index++) { - this.xbase = xbase; - this.ybase = ybase; - this.zbase = zbase; - this.areaID = areaID; - this.haswater = haswater; - this.holes = holes; - this.hasholes = holes != 0; - this.vertices = vertices; - - this.water_height1 = water_height1; - this.water_height2 = water_height2; - this.water_height = water_height; - this.water_flags = water_flags; + wdt.maps[index] = file.ReadInt32() != 0; + file.ReadInt32(); // kasta } } +} - internal readonly struct MapTile +internal readonly struct MapChunk +{ + public readonly float xbase, ybase, zbase; + public readonly uint areaID; + public readonly bool haswater; + public readonly bool hasholes; + public readonly uint holes; + //public float waterlevel; + + // 0 1 2 3 4 5 6 7 8 + // 9 10 11 12 13 14 15 16 + // 17 18 19 20 21 22 23 24 25 + // ... + public readonly float[] vertices; + + public readonly float water_height1; + public readonly float water_height2; + public readonly float[] water_height; + public readonly byte[] water_flags; + + + private static readonly int[] holetab_h = new int[] { 0x1111, 0x2222, 0x4444, 0x8888 }; + private static readonly int[] holetab_v = new int[] { 0x000F, 0x00F0, 0x0F00, 0xF000 }; + + // 0 ..3, 0 ..3 + public bool isHole(int i, int j) { - public const int SIZE = 16; + if (!hasholes) + return false; + i /= 2; + j /= 2; - public readonly ModelInstance[] modelis; - public readonly WMOInstance[] wmois; + return i <= 3 && j <= 3 && (holes & holetab_h[i] & holetab_v[j]) != 0; + } - public readonly MapChunk[] chunks; - public readonly bool[] hasChunk; - public MapTile(ModelInstance[] modelis, WMOInstance[] wmois, MapChunk[] chunks, bool[] hasChunk) - { - this.modelis = modelis; - this.wmois = wmois; - this.chunks = chunks; - this.hasChunk = hasChunk; - } + public MapChunk(float xbase, float ybase, float zbase, + uint areaID, bool haswater, uint holes, float[] vertices, + float water_height1, float water_height2, float[] water_height, byte[] water_flags) + { + this.xbase = xbase; + this.ybase = ybase; + this.zbase = zbase; + this.areaID = areaID; + this.haswater = haswater; + this.holes = holes; + this.hasholes = holes != 0; + this.vertices = vertices; + + this.water_height1 = water_height1; + this.water_height2 = water_height2; + this.water_height = water_height; + this.water_flags = water_flags; } +} + +internal readonly struct MapTile +{ + public const int SIZE = 16; - internal static class MapTileFile // adt file + public readonly ModelInstance[] modelis; + public readonly WMOInstance[] wmois; + + public readonly MapChunk[] chunks; + public readonly bool[] hasChunk; + + public MapTile(ModelInstance[] modelis, WMOInstance[] wmois, MapChunk[] chunks, bool[] hasChunk) { - private static readonly MH2OData1 eMH2OData1 = new(0, 0, 0, 0, 0, 0, 0, 0, 0, 0); - public static ref readonly MH2OData1 EmptyMH2OData1 => ref eMH2OData1; + this.modelis = modelis; + this.wmois = wmois; + this.chunks = chunks; + this.hasChunk = hasChunk; + } +} - private static readonly LiquidData eLiquidData = new(0, 0, 0, EmptyMH2OData1, Array.Empty(), Array.Empty()); - public static ref readonly LiquidData EmptyLiquidData => ref eLiquidData; +internal static class MapTileFile // adt file +{ + private static readonly MH2OData1 eMH2OData1 = new(0, 0, 0, 0, 0, 0, 0, 0, 0, 0); + public static ref readonly MH2OData1 EmptyMH2OData1 => ref eMH2OData1; - public static MapTile Read(string name, WMOManager wmomanager, ModelManager modelmanager) - { - LiquidData[] LiquidDataChunk = Array.Empty(); - int[] mcnk_offsets = new int[MapTile.SIZE * MapTile.SIZE]; - int[] mcnk_sizes = new int[MapTile.SIZE * MapTile.SIZE]; + private static readonly LiquidData eLiquidData = new(0, 0, 0, EmptyMH2OData1, Array.Empty(), Array.Empty()); + public static ref readonly LiquidData EmptyLiquidData => ref eLiquidData; - List models = new(); - List wmos = new(); + public static MapTile Read(string name, WMOManager wmomanager, ModelManager modelmanager) + { + LiquidData[] LiquidDataChunk = Array.Empty(); + int[] mcnk_offsets = new int[MapTile.SIZE * MapTile.SIZE]; + int[] mcnk_sizes = new int[MapTile.SIZE * MapTile.SIZE]; - WMOInstance[] wmois = Array.Empty(); - ModelInstance[] modelis = Array.Empty(); + List models = new(); + List wmos = new(); - MapChunk[] chunks = new MapChunk[MapTile.SIZE * MapTile.SIZE]; - bool[] hasChunk = new bool[chunks.Length]; + WMOInstance[] wmois = Array.Empty(); + ModelInstance[] modelis = Array.Empty(); - using Stream stream = File.OpenRead(name); - using BinaryReader file = new(stream); + MapChunk[] chunks = new MapChunk[MapTile.SIZE * MapTile.SIZE]; + bool[] hasChunk = new bool[chunks.Length]; - do - { - uint type = file.ReadUInt32(); - uint size = file.ReadUInt32(); - long curpos = file.BaseStream.Position; - - if (type == ChunkReader.MCIN) - HandleMCIN(file, mcnk_offsets, mcnk_sizes); - else if (type == ChunkReader.MMDX && size != 0) - HandleMMDX(file, models, size); - else if (type == ChunkReader.MWMO && size != 0) - HandleMWMO(file, wmos, size); - else if (type == ChunkReader.MDDF) - HandleMDDF(file, modelmanager, models, size, out modelis); - else if (type == ChunkReader.MODF) - HandleMODF(file, wmos, wmomanager, size, out wmois); - else if (type == ChunkReader.MH2O) - HandleMH2O(file, out LiquidDataChunk); - - file.BaseStream.Seek(curpos + size, SeekOrigin.Begin); - } while (file.BaseStream.Position < file.BaseStream.Length); - - for (int index = 0; index < MapTile.SIZE * MapTile.SIZE; index++) - { - int off = mcnk_offsets[index]; - file.BaseStream.Seek(off, SeekOrigin.Begin); + using Stream stream = File.OpenRead(name); + using BinaryReader file = new(stream); - chunks[index] = ReadMapChunk(file, LiquidDataChunk.Length > 0 ? LiquidDataChunk[index] : EmptyLiquidData); - hasChunk[index] = true; - } + do + { + uint type = file.ReadUInt32(); + uint size = file.ReadUInt32(); + long curpos = file.BaseStream.Position; + + if (type == ChunkReader.MCIN) + HandleMCIN(file, mcnk_offsets, mcnk_sizes); + else if (type == ChunkReader.MMDX && size != 0) + HandleMMDX(file, models, size); + else if (type == ChunkReader.MWMO && size != 0) + HandleMWMO(file, wmos, size); + else if (type == ChunkReader.MDDF) + HandleMDDF(file, modelmanager, models, size, out modelis); + else if (type == ChunkReader.MODF) + HandleMODF(file, wmos, wmomanager, size, out wmois); + else if (type == ChunkReader.MH2O) + HandleMH2O(file, out LiquidDataChunk); + + file.BaseStream.Seek(curpos + size, SeekOrigin.Begin); + } while (file.BaseStream.Position < file.BaseStream.Length); + + for (int index = 0; index < MapTile.SIZE * MapTile.SIZE; index++) + { + int off = mcnk_offsets[index]; + file.BaseStream.Seek(off, SeekOrigin.Begin); - return new(modelis, wmois, chunks, hasChunk); + chunks[index] = ReadMapChunk(file, LiquidDataChunk.Length > 0 ? LiquidDataChunk[index] : EmptyLiquidData); + hasChunk[index] = true; } - public readonly struct LiquidData - { - public const int SIZE = 256; - public const int HEIGHT_SIZE = 9; - public const int FLAG_SIZE = 8; + return new(modelis, wmois, chunks, hasChunk); + } - public readonly uint offsetData1; - public readonly int used; - public readonly uint offsetData2; + public readonly struct LiquidData + { + public const int SIZE = 256; + public const int HEIGHT_SIZE = 9; + public const int FLAG_SIZE = 8; - public readonly MH2OData1 data1; + public readonly uint offsetData1; + public readonly int used; + public readonly uint offsetData2; - public readonly float[] water_height; - public readonly byte[] water_flags; + public readonly MH2OData1 data1; - public LiquidData(uint offsetData1, int used, uint offsetData2, - MH2OData1 data1, float[] water_height, byte[] water_flags) - { - this.offsetData1 = offsetData1; - this.used = used; - this.offsetData2 = offsetData2; - this.data1 = data1; + public readonly float[] water_height; + public readonly byte[] water_flags; - this.water_height = water_height; - this.water_flags = water_flags; - } + public LiquidData(uint offsetData1, int used, uint offsetData2, + MH2OData1 data1, float[] water_height, byte[] water_flags) + { + this.offsetData1 = offsetData1; + this.used = used; + this.offsetData2 = offsetData2; + this.data1 = data1; + + this.water_height = water_height; + this.water_flags = water_flags; } + } - public readonly struct MH2OData1 + public readonly struct MH2OData1 + { + public readonly UInt16 flags; //0x1 might mean there is a height map @ data2b ?? + public readonly UInt16 type; //0 = normal/lake, 1 = lava, 2 = ocean + public readonly float heightLevel1; + public readonly float heightLevel2; + public readonly byte xOffset; + public readonly byte yOffset; + public readonly byte Width; + public readonly byte Height; + public readonly uint offsetData2a; + public readonly uint offsetData2b; + + public MH2OData1(UInt16 flags, UInt16 type, float heightLevel1, + float heightLevel2, byte xOffet, byte yOffset, byte width, byte height, + uint offsetData2a, uint offsetData2b) { - public readonly UInt16 flags; //0x1 might mean there is a height map @ data2b ?? - public readonly UInt16 type; //0 = normal/lake, 1 = lava, 2 = ocean - public readonly float heightLevel1; - public readonly float heightLevel2; - public readonly byte xOffset; - public readonly byte yOffset; - public readonly byte Width; - public readonly byte Height; - public readonly uint offsetData2a; - public readonly uint offsetData2b; - - public MH2OData1(UInt16 flags, UInt16 type, float heightLevel1, - float heightLevel2, byte xOffet, byte yOffset, byte width, byte height, - uint offsetData2a, uint offsetData2b) - { - this.flags = flags; - this.type = type; - this.heightLevel1 = heightLevel1; - this.heightLevel2 = heightLevel2; - this.xOffset = xOffet; - this.yOffset = yOffset; - this.Width = width; - this.Height = height; - this.offsetData2a = offsetData2a; - this.offsetData2b = offsetData2b; - } + this.flags = flags; + this.type = type; + this.heightLevel1 = heightLevel1; + this.heightLevel2 = heightLevel2; + this.xOffset = xOffet; + this.yOffset = yOffset; + this.Width = width; + this.Height = height; + this.offsetData2a = offsetData2a; + this.offsetData2b = offsetData2b; } + } + + private static void HandleMH2O(BinaryReader file, out LiquidData[] liquidData) + { + liquidData = new LiquidData[LiquidData.SIZE]; - private static void HandleMH2O(BinaryReader file, out LiquidData[] liquidData) + long chunkStart = file.BaseStream.Position; + for (int i = 0; i < LiquidData.SIZE; i++) { - liquidData = new LiquidData[LiquidData.SIZE]; + uint offsetData1 = file.ReadUInt32(); + int used = file.ReadInt32(); + uint offsetData2 = file.ReadUInt32(); + MH2OData1 data1 = EmptyMH2OData1; - long chunkStart = file.BaseStream.Position; - for (int i = 0; i < LiquidData.SIZE; i++) + if (offsetData1 != 0) { - uint offsetData1 = file.ReadUInt32(); - int used = file.ReadInt32(); - uint offsetData2 = file.ReadUInt32(); - MH2OData1 data1 = EmptyMH2OData1; + long lastPos = file.BaseStream.Position; - if (offsetData1 != 0) - { - long lastPos = file.BaseStream.Position; - - file.BaseStream.Seek(chunkStart + offsetData1, SeekOrigin.Begin); - data1 = new MH2OData1 - ( - file.ReadUInt16(), - file.ReadUInt16(), - file.ReadSingle(), - file.ReadSingle(), - file.ReadByte(), - file.ReadByte(), - file.ReadByte(), - file.ReadByte(), - file.ReadUInt32(), - file.ReadUInt32() - ); - - file.BaseStream.Seek(lastPos, SeekOrigin.Begin); - } + file.BaseStream.Seek(chunkStart + offsetData1, SeekOrigin.Begin); + data1 = new MH2OData1 + ( + file.ReadUInt16(), + file.ReadUInt16(), + file.ReadSingle(), + file.ReadSingle(), + file.ReadByte(), + file.ReadByte(), + file.ReadByte(), + file.ReadByte(), + file.ReadUInt32(), + file.ReadUInt32() + ); - float[] water_height = new float[LiquidData.HEIGHT_SIZE * LiquidData.HEIGHT_SIZE]; - byte[] water_flags = new byte[LiquidData.FLAG_SIZE * LiquidData.FLAG_SIZE]; + file.BaseStream.Seek(lastPos, SeekOrigin.Begin); + } - if ((used & 1) == 1 && offsetData1 != 0 && data1.offsetData2b != 0 && (data1.flags & 1) == 1) - { - long lastPos = file.BaseStream.Position; - file.BaseStream.Seek(chunkStart + data1.offsetData2b, SeekOrigin.Begin); + float[] water_height = new float[LiquidData.HEIGHT_SIZE * LiquidData.HEIGHT_SIZE]; + byte[] water_flags = new byte[LiquidData.FLAG_SIZE * LiquidData.FLAG_SIZE]; + + if ((used & 1) == 1 && offsetData1 != 0 && data1.offsetData2b != 0 && (data1.flags & 1) == 1) + { + long lastPos = file.BaseStream.Position; + file.BaseStream.Seek(chunkStart + data1.offsetData2b, SeekOrigin.Begin); - for (int x = data1.xOffset; x <= data1.xOffset + data1.Width; x++) + for (int x = data1.xOffset; x <= data1.xOffset + data1.Width; x++) + { + for (int y = data1.yOffset; y <= data1.yOffset + data1.Height; y++) { - for (int y = data1.yOffset; y <= data1.yOffset + data1.Height; y++) - { - int index = y * LiquidData.HEIGHT_SIZE + x; - water_height[index] = file.ReadSingle(); - } + int index = y * LiquidData.HEIGHT_SIZE + x; + water_height[index] = file.ReadSingle(); } + } - for (int x = data1.xOffset; x < data1.xOffset + data1.Width; x++) + for (int x = data1.xOffset; x < data1.xOffset + data1.Width; x++) + { + for (int y = data1.yOffset; y < data1.yOffset + data1.Height; y++) { - for (int y = data1.yOffset; y < data1.yOffset + data1.Height; y++) - { - int index = y * LiquidData.FLAG_SIZE + x; - water_flags[index] = file.ReadByte(); - } + int index = y * LiquidData.FLAG_SIZE + x; + water_flags[index] = file.ReadByte(); } - - file.BaseStream.Seek(lastPos, SeekOrigin.Begin); } - liquidData[i] = new - ( - offsetData1, - used, - offsetData2, - data1, - water_height, - water_flags - ); + file.BaseStream.Seek(lastPos, SeekOrigin.Begin); } + + liquidData[i] = new + ( + offsetData1, + used, + offsetData2, + data1, + water_height, + water_flags + ); } + } - private static void HandleMCIN(BinaryReader file, int[] mcnk_offsets, int[] mcnk_sizes) + private static void HandleMCIN(BinaryReader file, int[] mcnk_offsets, int[] mcnk_sizes) + { + for (int i = 0; i < mcnk_offsets.Length; i++) { - for (int i = 0; i < mcnk_offsets.Length; i++) - { - mcnk_offsets[i] = file.ReadInt32(); - mcnk_sizes[i] = file.ReadInt32(); - file.ReadInt32(); // crap - file.ReadInt32();// crap - } + mcnk_offsets[i] = file.ReadInt32(); + mcnk_sizes[i] = file.ReadInt32(); + file.ReadInt32(); // crap + file.ReadInt32();// crap } + } - private static void HandleMMDX(BinaryReader file, List models, uint size) + private static void HandleMMDX(BinaryReader file, List models, uint size) + { + int l = 0; + byte[] raw = file.ReadBytes((int)size); + while (l < size) { - int l = 0; - byte[] raw = file.ReadBytes((int)size); - while (l < size) - { - string s = ChunkReader.ExtractString(raw, l); - l += s.Length + 1; + string s = ChunkReader.ExtractString(raw, l); + l += s.Length + 1; - models.Add(s); - } + models.Add(s); } + } - private static void HandleMWMO(BinaryReader file, List wmos, uint size) + private static void HandleMWMO(BinaryReader file, List wmos, uint size) + { + int l = 0; + byte[] raw = file.ReadBytes((int)size); + while (l < size) { - int l = 0; - byte[] raw = file.ReadBytes((int)size); - while (l < size) - { - string s = ChunkReader.ExtractString(raw, l); - l += s.Length + 1; + string s = ChunkReader.ExtractString(raw, l); + l += s.Length + 1; - wmos.Add(s); - } + wmos.Add(s); } + } - private static void HandleMDDF(BinaryReader file, ModelManager modelmanager, List models, uint size, out ModelInstance[] modelis) - { - int nMDX = (int)size / 36; + private static void HandleMDDF(BinaryReader file, ModelManager modelmanager, List models, uint size, out ModelInstance[] modelis) + { + int nMDX = (int)size / 36; - modelis = new ModelInstance[nMDX]; - for (int i = 0; i < nMDX; i++) - { - int id = file.ReadInt32(); + modelis = new ModelInstance[nMDX]; + for (int i = 0; i < nMDX; i++) + { + int id = file.ReadInt32(); - string path = models[id]; - Model model = modelmanager.AddAndLoadIfNeeded(path); - modelis[i] = new(file, model); - } + string path = models[id]; + Model model = modelmanager.AddAndLoadIfNeeded(path); + modelis[i] = new(file, model); } + } - private static void HandleMODF(BinaryReader file, List wmos, WMOManager wmomanager, uint size, out WMOInstance[] wmois) - { - int nWMO = (int)size / 64; - wmois = new WMOInstance[nWMO]; + private static void HandleMODF(BinaryReader file, List wmos, WMOManager wmomanager, uint size, out WMOInstance[] wmois) + { + int nWMO = (int)size / 64; + wmois = new WMOInstance[nWMO]; - for (int i = 0; i < nWMO; i++) - { - int id = file.ReadInt32(); - WMO wmo = wmomanager.AddAndLoadIfNeeded(wmos[id]); + for (int i = 0; i < nWMO; i++) + { + int id = file.ReadInt32(); + WMO wmo = wmomanager.AddAndLoadIfNeeded(wmos[id]); - wmois[i] = new(file, wmo); - } + wmois[i] = new(file, wmo); } + } - /* MapChunk */ + /* MapChunk */ - private static MapChunk ReadMapChunk(BinaryReader file, in LiquidData liquidData) + private static MapChunk ReadMapChunk(BinaryReader file, in LiquidData liquidData) + { + // Read away Magic and size + _ = file.ReadUInt32(); // uint crap_head + _ = file.ReadUInt32(); // uint crap_size + + // Each map chunk has 9x9 vertices, + // and in between them 8x8 additional vertices, several texture layers, normal vectors, a shadow map, etc. + + _ = file.ReadUInt32(); // uint flags + _ = file.ReadUInt32(); // uint ix + _ = file.ReadUInt32(); // uint iy + _ = file.ReadUInt32(); // uint nLayers + _ = file.ReadUInt32(); // uint nDoodadRefs + _ = file.ReadUInt32(); // uint ofsHeight + _ = file.ReadUInt32(); // uint ofsNormal + _ = file.ReadUInt32(); // uint ofsLayer + _ = file.ReadUInt32(); // uint ofsRefs + _ = file.ReadUInt32(); // uint ofsAlpha + _ = file.ReadUInt32(); // uint sizeAlpha + _ = file.ReadUInt32(); // uint ofsShadow + _ = file.ReadUInt32(); // uint sizeShadow + + uint areaID = file.ReadUInt32(); + _ = file.ReadUInt32(); // uint nMapObjRefs + uint holes = file.ReadUInt32(); + + _ = file.ReadUInt16(); // ushort s1 + _ = file.ReadUInt16(); // ushort s2 + _ = file.ReadUInt32(); // uint d1 + _ = file.ReadUInt32(); // uint d2 + _ = file.ReadUInt32(); // uint d3 + _ = file.ReadUInt32(); // uint predTex + _ = file.ReadUInt32(); // uint nEffectDoodad + _ = file.ReadUInt32(); // uint ofsSndEmitters + _ = file.ReadUInt32(); // uint nSndEmitters + _ = file.ReadUInt32(); // uint ofsLiquid + + uint sizeLiquid = file.ReadUInt32(); + float zpos = file.ReadSingle(); + float xpos = file.ReadSingle(); + float ypos = file.ReadSingle(); + + _ = file.ReadUInt32(); // uint textureId + _ = file.ReadUInt32(); // uint props + _ = file.ReadUInt32(); // uint effectId + + float xbase = -xpos + ChunkReader.ZEROPOINT; + float ybase = ypos; + float zbase = -zpos + ChunkReader.ZEROPOINT; + + float[] vertices = new float[3 * ((9 * 9) + (8 * 8))]; + + bool haswater = false; + float water_height1 = 0; + float water_height2 = 0; + float[] water_height = new float[LiquidData.HEIGHT_SIZE * LiquidData.HEIGHT_SIZE]; + byte[] water_flags = new byte[LiquidData.FLAG_SIZE * LiquidData.FLAG_SIZE]; + + //logger.WriteLine(" " + zpos + " " + xpos + " " + ypos); + do { - // Read away Magic and size - _ = file.ReadUInt32(); // uint crap_head - _ = file.ReadUInt32(); // uint crap_size - - // Each map chunk has 9x9 vertices, - // and in between them 8x8 additional vertices, several texture layers, normal vectors, a shadow map, etc. - - _ = file.ReadUInt32(); // uint flags - _ = file.ReadUInt32(); // uint ix - _ = file.ReadUInt32(); // uint iy - _ = file.ReadUInt32(); // uint nLayers - _ = file.ReadUInt32(); // uint nDoodadRefs - _ = file.ReadUInt32(); // uint ofsHeight - _ = file.ReadUInt32(); // uint ofsNormal - _ = file.ReadUInt32(); // uint ofsLayer - _ = file.ReadUInt32(); // uint ofsRefs - _ = file.ReadUInt32(); // uint ofsAlpha - _ = file.ReadUInt32(); // uint sizeAlpha - _ = file.ReadUInt32(); // uint ofsShadow - _ = file.ReadUInt32(); // uint sizeShadow - - uint areaID = file.ReadUInt32(); - _ = file.ReadUInt32(); // uint nMapObjRefs - uint holes = file.ReadUInt32(); - - _ = file.ReadUInt16(); // ushort s1 - _ = file.ReadUInt16(); // ushort s2 - _ = file.ReadUInt32(); // uint d1 - _ = file.ReadUInt32(); // uint d2 - _ = file.ReadUInt32(); // uint d3 - _ = file.ReadUInt32(); // uint predTex - _ = file.ReadUInt32(); // uint nEffectDoodad - _ = file.ReadUInt32(); // uint ofsSndEmitters - _ = file.ReadUInt32(); // uint nSndEmitters - _ = file.ReadUInt32(); // uint ofsLiquid - - uint sizeLiquid = file.ReadUInt32(); - float zpos = file.ReadSingle(); - float xpos = file.ReadSingle(); - float ypos = file.ReadSingle(); - - _ = file.ReadUInt32(); // uint textureId - _ = file.ReadUInt32(); // uint props - _ = file.ReadUInt32(); // uint effectId - - float xbase = -xpos + ChunkReader.ZEROPOINT; - float ybase = ypos; - float zbase = -zpos + ChunkReader.ZEROPOINT; - - float[] vertices = new float[3 * ((9 * 9) + (8 * 8))]; - - bool haswater = false; - float water_height1 = 0; - float water_height2 = 0; - float[] water_height = new float[LiquidData.HEIGHT_SIZE * LiquidData.HEIGHT_SIZE]; - byte[] water_flags = new byte[LiquidData.FLAG_SIZE * LiquidData.FLAG_SIZE]; + uint type = file.ReadUInt32(); + uint size = file.ReadUInt32(); + long curpos = file.BaseStream.Position; - //logger.WriteLine(" " + zpos + " " + xpos + " " + ypos); - do + if (type == ChunkReader.MCNR) { - uint type = file.ReadUInt32(); - uint size = file.ReadUInt32(); - long curpos = file.BaseStream.Position; - - if (type == ChunkReader.MCNR) - { - size = 0x1C0; // WTF - } + size = 0x1C0; // WTF + } - if (type == ChunkReader.MCVT) - { - HandleChunkMCVT(file, xbase, ybase, zbase, vertices); - } - else if (type == ChunkReader.MCLQ) + if (type == ChunkReader.MCVT) + { + HandleChunkMCVT(file, xbase, ybase, zbase, vertices); + } + else if (type == ChunkReader.MCLQ) + { + /* Some .adt-files are still using the old MCLQ chunks. Far from all though. + * And those which use the MH2O chunk does not use these MCLQ chunks */ + size = sizeLiquid; + if (sizeLiquid != 8) { - /* Some .adt-files are still using the old MCLQ chunks. Far from all though. - * And those which use the MH2O chunk does not use these MCLQ chunks */ - size = sizeLiquid; - if (sizeLiquid != 8) - { - haswater = true; - HandleChunkMCLQ(file, out water_height1, out water_height2, water_height, water_flags); - } + haswater = true; + HandleChunkMCLQ(file, out water_height1, out water_height2, water_height, water_flags); } + } - file.BaseStream.Seek(curpos + size, SeekOrigin.Begin); - } while (file.BaseStream.Position < file.BaseStream.Length); - - //set liquid info from the MH2O chunk since the old MCLQ is no more - if (liquidData.offsetData1 != 0) - { - haswater = (liquidData.used & 1) == 1; + file.BaseStream.Seek(curpos + size, SeekOrigin.Begin); + } while (file.BaseStream.Position < file.BaseStream.Length); - water_height1 = liquidData.data1.heightLevel1; - water_height2 = liquidData.data1.heightLevel2; + //set liquid info from the MH2O chunk since the old MCLQ is no more + if (liquidData.offsetData1 != 0) + { + haswater = (liquidData.used & 1) == 1; - //TODO: set height map and flags, very important - water_height = liquidData.water_height; - water_flags = liquidData.water_flags; - } + water_height1 = liquidData.data1.heightLevel1; + water_height2 = liquidData.data1.heightLevel2; - return new(xbase, ybase, zbase, - areaID, haswater, holes, - vertices, water_height1, water_height2, - water_height, water_flags); + //TODO: set height map and flags, very important + water_height = liquidData.water_height; + water_flags = liquidData.water_flags; } - private static void HandleChunkMCVT(BinaryReader file, float xbase, float ybase, float zbase, float[] vertices) + return new(xbase, ybase, zbase, + areaID, haswater, holes, + vertices, water_height1, water_height2, + water_height, water_flags); + } + + private static void HandleChunkMCVT(BinaryReader file, float xbase, float ybase, float zbase, float[] vertices) + { + int index = 0; + for (int j = 0; j < 17; j++) { - int index = 0; - for (int j = 0; j < 17; j++) + for (int i = 0; i < ((j % 2 != 0) ? 8 : 9); i++) { - for (int i = 0; i < ((j % 2 != 0) ? 8 : 9); i++) - { - float y = file.ReadSingle(); - float x = i * ChunkReader.UNITSIZE; - float z = j * 0.5f * ChunkReader.UNITSIZE; + float y = file.ReadSingle(); + float x = i * ChunkReader.UNITSIZE; + float z = j * 0.5f * ChunkReader.UNITSIZE; - if (j % 2 != 0) - { - x += ChunkReader.UNITSIZE * 0.5f; - } - - vertices[index++] = xbase + x; - vertices[index++] = ybase + y; - vertices[index++] = zbase + z; + if (j % 2 != 0) + { + x += ChunkReader.UNITSIZE * 0.5f; } + + vertices[index++] = xbase + x; + vertices[index++] = ybase + y; + vertices[index++] = zbase + z; } } + } - private static void HandleChunkMCLQ(BinaryReader file, out float water_height1, out float water_height2, float[] water_height, byte[] water_flags) - { - water_height1 = file.ReadSingle(); - water_height2 = file.ReadSingle(); + private static void HandleChunkMCLQ(BinaryReader file, out float water_height1, out float water_height2, float[] water_height, byte[] water_flags) + { + water_height1 = file.ReadSingle(); + water_height2 = file.ReadSingle(); - for (int i = 0; i < LiquidData.HEIGHT_SIZE * LiquidData.HEIGHT_SIZE; i++) - { - _ = file.ReadUInt32(); - water_height[i] = file.ReadSingle(); - } + for (int i = 0; i < LiquidData.HEIGHT_SIZE * LiquidData.HEIGHT_SIZE; i++) + { + _ = file.ReadUInt32(); + water_height[i] = file.ReadSingle(); + } - for (int i = 0; i < LiquidData.FLAG_SIZE * LiquidData.FLAG_SIZE; i++) - { - water_flags[i] = file.ReadByte(); - } + for (int i = 0; i < LiquidData.FLAG_SIZE * LiquidData.FLAG_SIZE; i++) + { + water_flags[i] = file.ReadByte(); } } +} - internal sealed class WmoRootFile +internal sealed class WmoRootFile +{ + public WmoRootFile(string name, WMO wmo, ModelManager modelmanager) { - public WmoRootFile(string name, WMO wmo, ModelManager modelmanager) + using Stream stream = File.OpenRead(name); + using BinaryReader file = new(stream); + + do { - using Stream stream = File.OpenRead(name); - using BinaryReader file = new(stream); + uint type = file.ReadUInt32(); + uint size = file.ReadUInt32(); + long curpos = file.BaseStream.Position; - do + if (type == ChunkReader.MOHD) { - uint type = file.ReadUInt32(); - uint size = file.ReadUInt32(); - long curpos = file.BaseStream.Position; - - if (type == ChunkReader.MOHD) - { - HandleMOHD(file, wmo, size); - } - else if (type == ChunkReader.MOGI) - { - HandleMOGI(file, wmo, size); - } - else if (type == ChunkReader.MODS) - { - HandleMODS(file, wmo); - } - else if (type == ChunkReader.MODD) - { - HandleMODD(file, wmo, modelmanager, size); - } - else if (type == ChunkReader.MODN) - { - HandleMODN(file, wmo, size); - } + HandleMOHD(file, wmo, size); + } + else if (type == ChunkReader.MOGI) + { + HandleMOGI(file, wmo, size); + } + else if (type == ChunkReader.MODS) + { + HandleMODS(file, wmo); + } + else if (type == ChunkReader.MODD) + { + HandleMODD(file, wmo, modelmanager, size); + } + else if (type == ChunkReader.MODN) + { + HandleMODN(file, wmo, size); + } - file.BaseStream.Seek(curpos + size, SeekOrigin.Begin); - } while (file.BaseStream.Position != file.BaseStream.Length); - } + file.BaseStream.Seek(curpos + size, SeekOrigin.Begin); + } while (file.BaseStream.Position != file.BaseStream.Length); + } - private static void HandleMOHD(BinaryReader file, WMO wmo, uint size) - { - file.ReadUInt32(); // uint nTextures - uint nGroups = file.ReadUInt32(); - file.ReadUInt32(); // uint nP - file.ReadUInt32(); // uint nLights - wmo.nModels = file.ReadUInt32(); - wmo.nDoodads = file.ReadUInt32(); - wmo.nDoodadSets = file.ReadUInt32(); + private static void HandleMOHD(BinaryReader file, WMO wmo, uint size) + { + file.ReadUInt32(); // uint nTextures + uint nGroups = file.ReadUInt32(); + file.ReadUInt32(); // uint nP + file.ReadUInt32(); // uint nLights + wmo.nModels = file.ReadUInt32(); + wmo.nDoodads = file.ReadUInt32(); + wmo.nDoodadSets = file.ReadUInt32(); - file.ReadUInt32(); //uint col - file.ReadUInt32(); //uint nX + file.ReadUInt32(); //uint col + file.ReadUInt32(); //uint nX - wmo.v1 = new Vector3(file.ReadSingle(), file.ReadSingle(), file.ReadSingle()); - wmo.v2 = new Vector3(file.ReadSingle(), file.ReadSingle(), file.ReadSingle()); + wmo.v1 = new Vector3(file.ReadSingle(), file.ReadSingle(), file.ReadSingle()); + wmo.v2 = new Vector3(file.ReadSingle(), file.ReadSingle(), file.ReadSingle()); - wmo.groups = new WMOGroup[nGroups]; - } + wmo.groups = new WMOGroup[nGroups]; + } - private static void HandleMODS(BinaryReader file, WMO wmo) + private static void HandleMODS(BinaryReader file, WMO wmo) + { + wmo.doodads = new DoodadSet[wmo.nDoodadSets]; + for (int i = 0; i < wmo.nDoodadSets; i++) { - wmo.doodads = new DoodadSet[wmo.nDoodadSets]; - for (int i = 0; i < wmo.nDoodadSets; i++) - { - file.ReadBytes(20); // byte[] name - wmo.doodads[i].firstInstance = file.ReadUInt32(); - wmo.doodads[i].nInstances = file.ReadUInt32(); - file.ReadUInt32(); - } + file.ReadBytes(20); // byte[] name + wmo.doodads[i].firstInstance = file.ReadUInt32(); + wmo.doodads[i].nInstances = file.ReadUInt32(); + file.ReadUInt32(); } + } - private static void HandleMODD(BinaryReader file, WMO wmo, ModelManager modelmanager, uint size) - { - // 40 bytes per doodad instance, nDoodads entries. - // While WMOs and models (M2s) in a map tile are rotated along the axes, - // doodads within a WMO are oriented using quaternions! Hooray for consistency! - /* + private static void HandleMODD(BinaryReader file, WMO wmo, ModelManager modelmanager, uint size) + { + // 40 bytes per doodad instance, nDoodads entries. + // While WMOs and models (M2s) in a map tile are rotated along the axes, + // doodads within a WMO are oriented using quaternions! Hooray for consistency! + /* 0x00 uint32 Offset to the start of the model's filename in the MODN chunk. 0x04 3 * float Position (X,Z,-Y) 0x10 float W component of the orientation quaternion @@ -1160,107 +1160,107 @@ 0x20 float Scale factor 0x24 4 * uint8 (B,G,R,A) color. Unknown. It is often (0,0,0,255). (something to do with lighting maybe?) */ - uint sets = size / 0x28; - wmo.doodadInstances = new ModelInstance[wmo.nDoodads]; - for (int i = 0; i < sets /*wmo.nDoodads*/; i++) - { - byte[] s = file.ReadBytes(4); - s[3] = 0; - uint nameOffsetInMODN = BitConverter.ToUInt32(s, 0); // 0x00 - float posx = file.ReadSingle(); // 0x04 - float posz = file.ReadSingle(); // 0x08 - float posy = -file.ReadSingle(); // 0x0c + uint sets = size / 0x28; + wmo.doodadInstances = new ModelInstance[wmo.nDoodads]; + for (int i = 0; i < sets /*wmo.nDoodads*/; i++) + { + byte[] s = file.ReadBytes(4); + s[3] = 0; + uint nameOffsetInMODN = BitConverter.ToUInt32(s, 0); // 0x00 + float posx = file.ReadSingle(); // 0x04 + float posz = file.ReadSingle(); // 0x08 + float posy = -file.ReadSingle(); // 0x0c - float quatw = file.ReadSingle(); // 0x10 + float quatw = file.ReadSingle(); // 0x10 - float quatx = file.ReadSingle(); // 0x14 - float quaty = file.ReadSingle(); // 0x18 - float quatz = file.ReadSingle();// 0x1c + float quatx = file.ReadSingle(); // 0x14 + float quaty = file.ReadSingle(); // 0x18 + float quatz = file.ReadSingle();// 0x1c - float scale = file.ReadSingle(); // 0x20 + float scale = file.ReadSingle(); // 0x20 - file.ReadUInt32(); // lighning crap 0x24 - // float last = file.ReadSingle(); // 0x28 + file.ReadUInt32(); // lighning crap 0x24 + // float last = file.ReadSingle(); // 0x28 - string name = ChunkReader.ExtractString(wmo.MODNraw, (int)nameOffsetInMODN); - Model m = modelmanager.AddAndLoadIfNeeded(name); + string name = ChunkReader.ExtractString(wmo.MODNraw, (int)nameOffsetInMODN); + Model m = modelmanager.AddAndLoadIfNeeded(name); - Vector3 pos = new(posx, posy, posz); - Vector3 dir = new(quatx, quaty, quatz); + Vector3 pos = new(posx, posy, posz); + Vector3 dir = new(quatx, quaty, quatz); - ModelInstance mi = new(m, pos, dir, scale, quatw); - wmo.doodadInstances[i] = mi; - } + ModelInstance mi = new(m, pos, dir, scale, quatw); + wmo.doodadInstances[i] = mi; } + } - private static void HandleMODN(BinaryReader file, WMO wmo, uint size) - { - wmo.MODNraw = file.ReadBytes((int)size); - // List of filenames for M2 (mdx) models that appear in this WMO. - } + private static void HandleMODN(BinaryReader file, WMO wmo, uint size) + { + wmo.MODNraw = file.ReadBytes((int)size); + // List of filenames for M2 (mdx) models that appear in this WMO. + } - private static void HandleMOGI(BinaryReader file, WMO wmo, uint size) + private static void HandleMOGI(BinaryReader file, WMO wmo, uint size) + { + for (int i = 0; i < wmo.groups.Length; i++) { - for (int i = 0; i < wmo.groups.Length; i++) - { - WMOGroup g = new(); - wmo.groups[i] = g; + WMOGroup g = new(); + wmo.groups[i] = g; - g.flags = file.ReadUInt32(); - float f0 = file.ReadSingle(); - float f1 = file.ReadSingle(); - float f2 = file.ReadSingle(); - g.v1 = new Vector3(f0, f1, f2); + g.flags = file.ReadUInt32(); + float f0 = file.ReadSingle(); + float f1 = file.ReadSingle(); + float f2 = file.ReadSingle(); + g.v1 = new Vector3(f0, f1, f2); - float f3 = file.ReadSingle(); - float f4 = file.ReadSingle(); - float f5 = file.ReadSingle(); - g.v2 = new Vector3(f3, f4, f5); + float f3 = file.ReadSingle(); + float f4 = file.ReadSingle(); + float f5 = file.ReadSingle(); + g.v2 = new Vector3(f3, f4, f5); - uint nameOfs = file.ReadUInt32(); - } + uint nameOfs = file.ReadUInt32(); } } +} - internal sealed class WmoGroupFile +internal sealed class WmoGroupFile +{ + public WmoGroupFile(WMOGroup g, string name) { - public WmoGroupFile(WMOGroup g, string name) - { - using Stream stream = File.OpenRead(name); - using BinaryReader file = new(stream); + using Stream stream = File.OpenRead(name); + using BinaryReader file = new(stream); - file.BaseStream.Seek(0x14, SeekOrigin.Begin); - HandleMOGP(file, g, 11); + file.BaseStream.Seek(0x14, SeekOrigin.Begin); + HandleMOGP(file, g, 11); - file.BaseStream.Seek(0x58, SeekOrigin.Begin);// first chunk + file.BaseStream.Seek(0x58, SeekOrigin.Begin);// first chunk - do + do + { + uint type = file.ReadUInt32(); + uint size = file.ReadUInt32(); + long curpos = file.BaseStream.Position; + if (type == ChunkReader.MOPY) { - uint type = file.ReadUInt32(); - uint size = file.ReadUInt32(); - long curpos = file.BaseStream.Position; - if (type == ChunkReader.MOPY) - { - HandleMOPY(file, g, size); - } - else if (type == ChunkReader.MOVI) - { - HandleMOVI(file, g, size); - } - else if (type == ChunkReader.MOVT) - { - HandleMOVT(file, g, size); - } + HandleMOPY(file, g, size); + } + else if (type == ChunkReader.MOVI) + { + HandleMOVI(file, g, size); + } + else if (type == ChunkReader.MOVT) + { + HandleMOVT(file, g, size); + } - file.BaseStream.Seek(curpos + size, SeekOrigin.Begin); - } while (file.BaseStream.Position != file.BaseStream.Length); - } + file.BaseStream.Seek(curpos + size, SeekOrigin.Begin); + } while (file.BaseStream.Position != file.BaseStream.Length); + } - private static void HandleMOPY(BinaryReader file, WMOGroup g, uint size) - { - g.nTriangles = size / 2; - // materials - /* 0x01 - inside small houses and paths leading indoors + private static void HandleMOPY(BinaryReader file, WMOGroup g, uint size) + { + g.nTriangles = size / 2; + // materials + /* 0x01 - inside small houses and paths leading indoors * 0x02 - ??? * 0x04 - set on indoor things and ruins * 0x08 - ??? @@ -1271,63 +1271,62 @@ private static void HandleMOPY(BinaryReader file, WMOGroup g, uint size) * */ - g.materials = new ushort[g.nTriangles]; - - for (int i = 0; i < g.nTriangles; i++) - { - g.materials[i] = file.ReadUInt16(); - } - } + g.materials = new ushort[g.nTriangles]; - private static void HandleMOVI(BinaryReader file, WMOGroup g, uint size) + for (int i = 0; i < g.nTriangles; i++) { - //indicesFileMarker = file.BaseStream.Position; - g.triangles = new UInt16[g.nTriangles * 3]; - for (uint i = 0; i < g.nTriangles; i++) - { - uint off = i * 3; - g.triangles[off + 0] = file.ReadUInt16(); - g.triangles[off + 1] = file.ReadUInt16(); - g.triangles[off + 2] = file.ReadUInt16(); - } + g.materials[i] = file.ReadUInt16(); } + } - private static void HandleMOVT(BinaryReader file, WMOGroup g, uint size) + private static void HandleMOVI(BinaryReader file, WMOGroup g, uint size) + { + //indicesFileMarker = file.BaseStream.Position; + g.triangles = new UInt16[g.nTriangles * 3]; + for (uint i = 0; i < g.nTriangles; i++) { - g.nVertices = size / 12; - // let's hope it's padded to 12 bytes, not 16... - g.vertices = new float[g.nVertices * 3]; - for (uint i = 0; i < g.nVertices; i++) - { - uint off = i * 3; - g.vertices[off + 0] = file.ReadSingle(); - g.vertices[off + 1] = file.ReadSingle(); - g.vertices[off + 2] = file.ReadSingle(); - } + uint off = i * 3; + g.triangles[off + 0] = file.ReadUInt16(); + g.triangles[off + 1] = file.ReadUInt16(); + g.triangles[off + 2] = file.ReadUInt16(); } + } - private static void HandleMOGP(BinaryReader file, WMOGroup g, uint size) + private static void HandleMOVT(BinaryReader file, WMOGroup g, uint size) + { + g.nVertices = size / 12; + // let's hope it's padded to 12 bytes, not 16... + g.vertices = new float[g.nVertices * 3]; + for (uint i = 0; i < g.nVertices; i++) { - g.nameStart = file.ReadUInt32(); - g.nameStart2 = file.ReadUInt32(); - g.flags = file.ReadUInt32(); - - g.v1 = new Vector3(file.ReadSingle(), file.ReadSingle(), file.ReadSingle()); - g.v2 = new Vector3(file.ReadSingle(), file.ReadSingle(), file.ReadSingle()); - - g.portalStart = file.ReadUInt16(); - g.portalCount = file.ReadUInt16(); - g.batchesA = file.ReadUInt16(); - g.batchesB = file.ReadUInt16(); - g.batchesC = file.ReadUInt16(); - g.batchesD = file.ReadUInt16(); - - file.ReadUInt32(); // uint fogCrap - - file.ReadUInt32(); // uint unknown1 - g.id = file.ReadUInt32(); - file.ReadUInt32(); // uint unknown2 - file.ReadUInt32(); // uint unknown3 + uint off = i * 3; + g.vertices[off + 0] = file.ReadSingle(); + g.vertices[off + 1] = file.ReadSingle(); + g.vertices[off + 2] = file.ReadSingle(); } } + + private static void HandleMOGP(BinaryReader file, WMOGroup g, uint size) + { + g.nameStart = file.ReadUInt32(); + g.nameStart2 = file.ReadUInt32(); + g.flags = file.ReadUInt32(); + + g.v1 = new Vector3(file.ReadSingle(), file.ReadSingle(), file.ReadSingle()); + g.v2 = new Vector3(file.ReadSingle(), file.ReadSingle(), file.ReadSingle()); + + g.portalStart = file.ReadUInt16(); + g.portalCount = file.ReadUInt16(); + g.batchesA = file.ReadUInt16(); + g.batchesB = file.ReadUInt16(); + g.batchesC = file.ReadUInt16(); + g.batchesD = file.ReadUInt16(); + + file.ReadUInt32(); // uint fogCrap + + file.ReadUInt32(); // uint unknown1 + g.id = file.ReadUInt32(); + file.ReadUInt32(); // uint unknown2 + file.ReadUInt32(); // uint unknown3 + } } \ No newline at end of file diff --git a/PathingAPI/Controllers/PPatherController.cs b/PathingAPI/Controllers/PPatherController.cs index 43a965fc4..fbac008c6 100644 --- a/PathingAPI/Controllers/PPatherController.cs +++ b/PathingAPI/Controllers/PPatherController.cs @@ -1,234 +1,233 @@ -using Microsoft.AspNetCore.Mvc; -using Microsoft.Extensions.Logging; +using System.Collections.Generic; +using System.Numerics; + +using Microsoft.AspNetCore.Mvc; + using PPather; using PPather.Data; using PPather.Graph; -using System.Collections.Generic; -using System.Linq; -using WowTriangles; -using System.Numerics; + using SharedLib.Data; -namespace PathingAPI.Controllers +namespace PathingAPI.Controllers; + +[Route("api/[controller]")] +[ApiController] +public sealed class PPatherController : ControllerBase { - [Route("api/[controller]")] - [ApiController] - public sealed class PPatherController : ControllerBase - { - private readonly PPatherService service; + private readonly PPatherService service; - private static bool isBusy; - private static bool initialised; + private static bool isBusy; + private static bool initialised; - public PPatherController(PPatherService service) - { - this.service = service; - } + public PPatherController(PPatherService service) + { + this.service = service; + } - /// - /// Allows a route to be calculated from one point to another using minimap coords. - /// - /// - /// uimap1 and uimap2 are the map ids. See https://wow.gamepedia.com/API_C_Map.GetBestMapForUnit - /// - /// /dump C_Map.GetBestMapForUnit("player") - /// - /// Dump: value=_Map.GetBestMapForUnit("player") - /// [1]=1451 - /// - /// x and y are the map coordinates for the zone (same as the mini map). See https://wowwiki.fandom.com/wiki/API_GetPlayerMapPosition - /// - /// local posX, posY = GetPlayerMapPosition("player"); - /// - /// from map e.g. 1451 - /// from X e.g. 46.8 - /// from Y e.g. 54.2 - /// to map e.g. 1451 - /// to X e.g. 51.2 - /// to Y e.g. 38.9 - /// A list of x,y,z and mapid - [HttpGet("MapRoute")] - [Produces("application/json")] - public JsonResult MapRoute(int uimap1, float x1, float y1, int uimap2, float x2, float y2) + /// + /// Allows a route to be calculated from one point to another using minimap coords. + /// + /// + /// uimap1 and uimap2 are the map ids. See https://wow.gamepedia.com/API_C_Map.GetBestMapForUnit + /// + /// /dump C_Map.GetBestMapForUnit("player") + /// + /// Dump: value=_Map.GetBestMapForUnit("player") + /// [1]=1451 + /// + /// x and y are the map coordinates for the zone (same as the mini map). See https://wowwiki.fandom.com/wiki/API_GetPlayerMapPosition + /// + /// local posX, posY = GetPlayerMapPosition("player"); + /// + /// from map e.g. 1451 + /// from X e.g. 46.8 + /// from Y e.g. 54.2 + /// to map e.g. 1451 + /// to X e.g. 51.2 + /// to Y e.g. 38.9 + /// A list of x,y,z and mapid + [HttpGet("MapRoute")] + [Produces("application/json")] + public JsonResult MapRoute(int uimap1, float x1, float y1, int uimap2, float x2, float y2) + { + isBusy = true; + service.SetLocations(service.ToWorld(uimap1, x1, y1), service.ToWorld(uimap2, x2, y2)); + var path = service.DoSearch(PathGraph.eSearchScoreSpot.A_Star_With_Model_Avoidance); + if (path == null) { - isBusy = true; - service.SetLocations(service.ToWorld(uimap1, x1, y1), service.ToWorld(uimap2, x2, y2)); - var path = service.DoSearch(PathGraph.eSearchScoreSpot.A_Star_With_Model_Avoidance); - if (path == null) - { - isBusy = false; - return new JsonResult(System.Array.Empty()); - } - - service.Save(); - - Vector3[] array = path.locations.ToArray(); - for (int i = 0; i < array.Length; i++) - { - array[i] = service.ToLocal(array[i], (int)service.SearchFrom.W, uimap1); - } - isBusy = false; - return new JsonResult(array); + return new JsonResult(System.Array.Empty()); } - /// - /// Allows a route to be calculated from one point to another using world coords. - /// e.g. -896, -3770, 11, (Barrens,Rachet) to -441, -2596, 96, (Barrens,Crossroads,Barrens) - /// - /// from X e.g. -896 - /// from Y e.g. -3770 - /// from Y e.g. 11 - /// to X e.g. -441 - /// to Y e.g. -2596 - /// from Y e.g. 96 - /// from ["Azeroth=0", "Kalimdor=1", "Northrend=??", "Expansion01=530"] e.g. Kalimdor - /// A list of x,y,z - [HttpGet("WorldRoute")] - [Produces("application/json")] - public JsonResult WorldRoute(float x1, float y1, float z1, float x2, float y2, float z2, float mapid) - { - isBusy = true; - service.SetLocations(new(x1, y1, z1, mapid), new(x2, y2, z2, mapid)); - var path = service.DoSearch(PathGraph.eSearchScoreSpot.A_Star_With_Model_Avoidance); - if (path == null) - { - isBusy = false; - return new JsonResult(System.Array.Empty()); - } - - service.Save(); - isBusy = false; + service.Save(); - return new JsonResult(path.locations); + Vector3[] array = path.locations.ToArray(); + for (int i = 0; i < array.Length; i++) + { + array[i] = service.ToLocal(array[i], (int)service.SearchFrom.W, uimap1); } - /// - /// Allows a route to be calculated from one point to another using world coords. - /// e.g. -896, -3770, 11, (Barrens,Rachet) to -441, -2596, 96, (Barrens,Crossroads,Barrens) - /// - /// from X e.g. -896 - /// from Y e.g. -3770 - /// from Y e.g. 11 - /// to X e.g. -441 - /// to Y e.g. -2596 - /// from Y e.g. 96 - /// todo - /// A list of x,y,z - [HttpGet("WorldRoute2")] - [Produces("application/json")] - public JsonResult WorldRoute2(float x1, float y1, float z1, float x2, float y2, float z2, int uimap) + isBusy = false; + return new JsonResult(array); + } + + /// + /// Allows a route to be calculated from one point to another using world coords. + /// e.g. -896, -3770, 11, (Barrens,Rachet) to -441, -2596, 96, (Barrens,Crossroads,Barrens) + /// + /// from X e.g. -896 + /// from Y e.g. -3770 + /// from Y e.g. 11 + /// to X e.g. -441 + /// to Y e.g. -2596 + /// from Y e.g. 96 + /// from ["Azeroth=0", "Kalimdor=1", "Northrend=??", "Expansion01=530"] e.g. Kalimdor + /// A list of x,y,z + [HttpGet("WorldRoute")] + [Produces("application/json")] + public JsonResult WorldRoute(float x1, float y1, float z1, float x2, float y2, float z2, float mapid) + { + isBusy = true; + service.SetLocations(new(x1, y1, z1, mapid), new(x2, y2, z2, mapid)); + var path = service.DoSearch(PathGraph.eSearchScoreSpot.A_Star_With_Model_Avoidance); + if (path == null) { - isBusy = true; - service.SetLocations(service.ToWorldZ(uimap, x1, y1, z1), service.ToWorldZ(uimap, x2, y2, z2)); - var path = service.DoSearch(PathGraph.eSearchScoreSpot.A_Star_With_Model_Avoidance); - if (path == null) - { - isBusy = false; - return new JsonResult(System.Array.Empty()); - } - service.Save(); isBusy = false; - - return new JsonResult(path.locations); + return new JsonResult(System.Array.Empty()); } - /// - /// Draws lines on the landscape - /// Used by the client to show the grind path and the spirit healer path. - /// - /// - /// - [HttpPost("Drawlines")] - [Produces("application/json")] - public bool Drawlines(List lines) - { - if (isBusy) { return false; } - isBusy = true; + service.Save(); + isBusy = false; - for (int i = 0; i < lines.Count; i++) - { - LineArgs l = lines[i]; - Vector4[] locations = CreateLocations(l); - service.OnLinesAdded?.Invoke(new LinesEventArgs(l.Name, locations, l.Colour)); - } + return new JsonResult(path.locations); + } + /// + /// Allows a route to be calculated from one point to another using world coords. + /// e.g. -896, -3770, 11, (Barrens,Rachet) to -441, -2596, 96, (Barrens,Crossroads,Barrens) + /// + /// from X e.g. -896 + /// from Y e.g. -3770 + /// from Y e.g. 11 + /// to X e.g. -441 + /// to Y e.g. -2596 + /// from Y e.g. 96 + /// todo + /// A list of x,y,z + [HttpGet("WorldRoute2")] + [Produces("application/json")] + public JsonResult WorldRoute2(float x1, float y1, float z1, float x2, float y2, float z2, int uimap) + { + isBusy = true; + service.SetLocations(service.ToWorldZ(uimap, x1, y1, z1), service.ToWorldZ(uimap, x2, y2, z2)); + var path = service.DoSearch(PathGraph.eSearchScoreSpot.A_Star_With_Model_Avoidance); + if (path == null) + { isBusy = false; - initialised = true; - return true; + return new JsonResult(System.Array.Empty()); } + service.Save(); + isBusy = false; - /// - /// Draws spheres on the landscape. - /// Used by the client to show the player's location. - /// - /// - /// - [HttpPost("DrawSphere")] - [Produces("application/json")] - public bool DrawSphere(SphereArgs sphere) - { - if (isBusy || !initialised) { return false; } - isBusy = true; - - Vector4 location = service.ToWorld(sphere.MapId, sphere.Spot.X, sphere.Spot.Y); - service.OnSphereAdded?.Invoke(new SphereEventArgs(sphere.Name, location, sphere.Colour)); + return new JsonResult(path.locations); + } - isBusy = false; - return true; - } + /// + /// Draws lines on the landscape + /// Used by the client to show the grind path and the spirit healer path. + /// + /// + /// + [HttpPost("Drawlines")] + [Produces("application/json")] + public bool Drawlines(List lines) + { + if (isBusy) { return false; } + isBusy = true; - private Vector4[] CreateLocations(LineArgs lines) + for (int i = 0; i < lines.Count; i++) { - Vector4[] result = new Vector4[lines.Spots.Length]; - for (int i = 0; i < result.Length; i++) - { - Vector3 s = lines.Spots[i]; - result[i] = service.ToWorld(lines.MapId, s.X, s.Y, s.Z); - } - return result; + LineArgs l = lines[i]; + Vector4[] locations = CreateLocations(l); + service.OnLinesAdded?.Invoke(new LinesEventArgs(l.Name, locations, l.Colour)); } - /// - /// Returns true to indicate that the server is listening. - /// - /// - [HttpGet("SelfTest")] - [Produces("application/json")] - public JsonResult SelfTest() + isBusy = false; + initialised = true; + return true; + } + + /// + /// Draws spheres on the landscape. + /// Used by the client to show the player's location. + /// + /// + /// + [HttpPost("DrawSphere")] + [Produces("application/json")] + public bool DrawSphere(SphereArgs sphere) + { + if (isBusy || !initialised) { return false; } + isBusy = true; + + Vector4 location = service.ToWorld(sphere.MapId, sphere.Spot.X, sphere.Spot.Y); + service.OnSphereAdded?.Invoke(new SphereEventArgs(sphere.Name, location, sphere.Colour)); + + isBusy = false; + return true; + } + + private Vector4[] CreateLocations(LineArgs lines) + { + Vector4[] result = new Vector4[lines.Spots.Length]; + for (int i = 0; i < result.Length; i++) { - return new JsonResult(service.MPQSelfTest()); + Vector3 s = lines.Spots[i]; + result[i] = service.ToWorld(lines.MapId, s.X, s.Y, s.Z); } + return result; + } - [HttpPost("DrawPathTest")] - [Produces("application/json")] - public bool DrawPathTest() - { - float mapId = ContinentDB.NameToId["Azeroth"]; // Azeroth - List coords = new() - { - new float[] { -5609.00f,-479.00f,397.49f }, - new float[] { -5609.33f,-444.00f,405.22f }, - new float[] { -5609.33f,-438.40f,406.02f }, - new float[] { -5608.80f,-427.73f,404.69f }, - new float[] { -5608.80f,-426.67f,404.69f }, - new float[] { -5610.67f,-405.33f,402.02f }, - new float[] { -5635.20f,-368.00f,392.15f }, - new float[] { -5645.07f,-362.67f,385.49f }, - new float[] { -5646.40f,-362.13f,384.69f }, - new float[] { -5664.27f,-355.73f,378.29f }, - new float[] { -5696.00f,-362.67f,366.02f }, - new float[] { -5758.93f,-385.87f,366.82f }, - new float[] { -5782.00f,-394.00f,366.09f } - }; - - if (isBusy) { return false; } - isBusy = true; - - service.DrawPath(mapId, coords); + /// + /// Returns true to indicate that the server is listening. + /// + /// + [HttpGet("SelfTest")] + [Produces("application/json")] + public JsonResult SelfTest() + { + return new JsonResult(service.MPQSelfTest()); + } - isBusy = false; - return true; - } + [HttpPost("DrawPathTest")] + [Produces("application/json")] + public bool DrawPathTest() + { + float mapId = ContinentDB.NameToId["Azeroth"]; // Azeroth + List coords = new() + { + new float[] { -5609.00f,-479.00f,397.49f }, + new float[] { -5609.33f,-444.00f,405.22f }, + new float[] { -5609.33f,-438.40f,406.02f }, + new float[] { -5608.80f,-427.73f,404.69f }, + new float[] { -5608.80f,-426.67f,404.69f }, + new float[] { -5610.67f,-405.33f,402.02f }, + new float[] { -5635.20f,-368.00f,392.15f }, + new float[] { -5645.07f,-362.67f,385.49f }, + new float[] { -5646.40f,-362.13f,384.69f }, + new float[] { -5664.27f,-355.73f,378.29f }, + new float[] { -5696.00f,-362.67f,366.02f }, + new float[] { -5758.93f,-385.87f,366.82f }, + new float[] { -5782.00f,-394.00f,366.09f } + }; + + if (isBusy) { return false; } + isBusy = true; + + service.DrawPath(mapId, coords); + + isBusy = false; + return true; } } \ No newline at end of file diff --git a/PathingAPI/Pages/Index.razor b/PathingAPI/Pages/Index.razor index b9761cb7d..7f2348061 100644 --- a/PathingAPI/Pages/Index.razor +++ b/PathingAPI/Pages/Index.razor @@ -19,7 +19,7 @@ Kalimdor Search Barrens (API using world Coords) Azeroth Dun morogh 1 (API using world Coords) Azeroth Dun morogh 2 (API using world Coords) - + Azshara issue no result (API using world Coords) Outland honor hold issue no result (API using world Coords) diff --git a/PathingAPI/Pages/SwaggerFrame.razor b/PathingAPI/Pages/SwaggerFrame.razor index 67bc70a38..06ec74134 100644 --- a/PathingAPI/Pages/SwaggerFrame.razor +++ b/PathingAPI/Pages/SwaggerFrame.razor @@ -9,6 +9,5 @@ width: 100%; } - \ No newline at end of file diff --git a/PathingAPI/PathingAPILoggerSink.cs b/PathingAPI/PathingAPILoggerSink.cs index a15458aed..6d6d41517 100644 --- a/PathingAPI/PathingAPILoggerSink.cs +++ b/PathingAPI/PathingAPILoggerSink.cs @@ -1,17 +1,16 @@ -using Serilog.Core; +using System; + +using Serilog.Core; using Serilog.Events; -using System; +namespace PathingAPI; -namespace PathingAPI +public sealed class PathingAPILoggerSink : ILogEventSink { - public sealed class PathingAPILoggerSink : ILogEventSink - { - public event Action OnLog; + public event Action OnLog; - public void Emit(LogEvent logEvent) - { - OnLog?.Invoke(logEvent); - } + public void Emit(LogEvent logEvent) + { + OnLog?.Invoke(logEvent); } } diff --git a/PathingAPI/Program.cs b/PathingAPI/Program.cs index 0506a9ff7..265bb0d58 100644 --- a/PathingAPI/Program.cs +++ b/PathingAPI/Program.cs @@ -1,23 +1,22 @@ using Microsoft.AspNetCore.Hosting; using Microsoft.Extensions.Hosting; -namespace PathingAPI -{ - public sealed class Program - { - public static string hostUrl = "http://127.0.0.1:5001"; +namespace PathingAPI; - public static void Main(string[] args) - { - CreateHostBuilder(args).Build().Run(); - } +public sealed class Program +{ + public static string hostUrl = "http://127.0.0.1:5001"; - public static IHostBuilder CreateHostBuilder(string[] args) => - Host.CreateDefaultBuilder(args) - .ConfigureWebHostDefaults(webBuilder => - { - webBuilder.UseUrls(hostUrl); - webBuilder.UseStartup(); - }); + public static void Main(string[] args) + { + CreateHostBuilder(args).Build().Run(); } + + public static IHostBuilder CreateHostBuilder(string[] args) => + Host.CreateDefaultBuilder(args) + .ConfigureWebHostDefaults(webBuilder => + { + webBuilder.UseUrls(hostUrl); + webBuilder.UseStartup(); + }); } diff --git a/PathingAPI/Shared/NavMenu.razor b/PathingAPI/Shared/NavMenu.razor index c6f04b4f6..612adfcc9 100644 --- a/PathingAPI/Shared/NavMenu.razor +++ b/PathingAPI/Shared/NavMenu.razor @@ -35,8 +35,8 @@ -