From cef5e522c5f95327aa14e0d98bd0cac628c5968f Mon Sep 17 00:00:00 2001 From: leMicin Date: Sat, 28 Dec 2024 21:36:20 -0500 Subject: [PATCH 1/7] Started work on pseudo modifiers --- .../Clients/PoeTradeHandler.cs | 30 +- .../Modifiers/InvariantModifierProvider.cs | 12 +- src/Sidekick.Apis.Poe/Parser/ItemParser.cs | 13 +- .../ElementalResistanceDefinition.cs | 16 + .../Pseudo/Models/PseudoDefinition.cs | 27 - .../Pseudo/Models/PseudoDefinitionModifier.cs | 25 - .../Pseudo/Models/PseudoPattern.cs | 19 - .../Pseudo/Models/PseudoPatternGroup.cs | 43 - .../Pseudo/Models/PseudoPatternMatch.cs | 21 - .../Pseudo/PseudoDefinition.cs | 36 + .../Pseudo/PseudoModifierDefinition.cs | 24 + .../Pseudo/PseudoModifierProvider.cs | 939 +++++++++--------- src/Sidekick.Apis.Poe/Pseudo/PseudoPattern.cs | 14 + .../Trade/Models/PseudoModifierFilter.cs | 28 +- .../Trade/Requests/Filters/IStatFilter.cs | 6 + .../Requests/Filters/SearchFilterValue.cs | 8 +- .../Trade/Requests/Filters/StatFilters.cs | 2 +- .../Trade/Requests/Filters/StatType.cs | 3 + .../Requests/Filters/WeightedStatFilter.cs | 6 + .../Trade/TradeFilterService.cs | 8 +- .../Trade/TradeSearchService.cs | 80 +- src/Sidekick.Common.Ui/wwwroot/css/app.css | 4 - .../Game/Items/PseudoModifier.cs | 14 +- .../Filters/PseudoFilterComponent.razor | 4 +- 24 files changed, 635 insertions(+), 747 deletions(-) create mode 100644 src/Sidekick.Apis.Poe/Pseudo/Definitions/ElementalResistanceDefinition.cs delete mode 100644 src/Sidekick.Apis.Poe/Pseudo/Models/PseudoDefinition.cs delete mode 100644 src/Sidekick.Apis.Poe/Pseudo/Models/PseudoDefinitionModifier.cs delete mode 100644 src/Sidekick.Apis.Poe/Pseudo/Models/PseudoPattern.cs delete mode 100644 src/Sidekick.Apis.Poe/Pseudo/Models/PseudoPatternGroup.cs delete mode 100644 src/Sidekick.Apis.Poe/Pseudo/Models/PseudoPatternMatch.cs create mode 100644 src/Sidekick.Apis.Poe/Pseudo/PseudoDefinition.cs create mode 100644 src/Sidekick.Apis.Poe/Pseudo/PseudoModifierDefinition.cs create mode 100644 src/Sidekick.Apis.Poe/Pseudo/PseudoPattern.cs create mode 100644 src/Sidekick.Apis.Poe/Trade/Requests/Filters/IStatFilter.cs create mode 100644 src/Sidekick.Apis.Poe/Trade/Requests/Filters/WeightedStatFilter.cs diff --git a/src/Sidekick.Apis.Poe/Clients/PoeTradeHandler.cs b/src/Sidekick.Apis.Poe/Clients/PoeTradeHandler.cs index 045dd95a0..4bfd1e332 100644 --- a/src/Sidekick.Apis.Poe/Clients/PoeTradeHandler.cs +++ b/src/Sidekick.Apis.Poe/Clients/PoeTradeHandler.cs @@ -34,17 +34,6 @@ protected override async Task SendAsync(HttpRequestMessage return response; } - if (response.StatusCode == HttpStatusCode.TooManyRequests) - { - var errorResponse = await ParseErrorResponse(response); - throw new SidekickException("Rate limit exceeded.", "The official trade website has a rate limit to avoid spam. Sidekick cannot change this.", errorResponse?.Error?.Message ?? string.Empty); - } - - if (response.StatusCode == HttpStatusCode.Moved || response.StatusCode == HttpStatusCode.Redirect || response.StatusCode == HttpStatusCode.RedirectKeepVerb) - { - response = await HandleRedirect(request, response, cancellationToken); - } - if (response.StatusCode == HttpStatusCode.Moved || response.StatusCode == HttpStatusCode.Redirect || response.StatusCode == HttpStatusCode.RedirectKeepVerb) { logger.LogWarning("[PoeTradeHandler] Received redirect response."); @@ -64,6 +53,25 @@ protected override async Task SendAsync(HttpRequestMessage } } + var errorResponse = await ParseErrorResponse(response); + if (response.StatusCode == HttpStatusCode.BadRequest) + { + if (errorResponse?.Error?.Message?.StartsWith("Query is too complex.") ?? false) + { + throw new SidekickException("Query is too complex.", "The official trade website has limit on complex queries. Sidekick cannot change this.", "Use the official website to search for your current item.", errorResponse.Error?.Message ?? string.Empty); + } + } + + if (response.StatusCode == HttpStatusCode.TooManyRequests) + { + throw new SidekickException("Rate limit exceeded.", "The official trade website has a rate limit to avoid spam. Sidekick cannot change this.", errorResponse?.Error?.Message ?? string.Empty); + } + + if (response.StatusCode == HttpStatusCode.Moved || response.StatusCode == HttpStatusCode.Redirect || response.StatusCode == HttpStatusCode.RedirectKeepVerb) + { + response = await HandleRedirect(request, response, cancellationToken); + } + // Sidekick does not support authentication yet. if (response.StatusCode == HttpStatusCode.Unauthorized) { diff --git a/src/Sidekick.Apis.Poe/Modifiers/InvariantModifierProvider.cs b/src/Sidekick.Apis.Poe/Modifiers/InvariantModifierProvider.cs index 429a2b335..6f1202dd6 100644 --- a/src/Sidekick.Apis.Poe/Modifiers/InvariantModifierProvider.cs +++ b/src/Sidekick.Apis.Poe/Modifiers/InvariantModifierProvider.cs @@ -128,11 +128,21 @@ public async Task> GetList() var game = leagueId.GetGameFromLeagueId(); var cacheKey = $"{game.GetValueAttribute()}_InvariantModifiers"; - return await cacheProvider.GetOrSet(cacheKey, + var apiCategories = await cacheProvider.GetOrSet(cacheKey, async () => { var result = await poeTradeClient.Fetch(game, gameLanguageProvider.InvariantLanguage, "data/stats"); return result.Result; }, (cache) => cache.Any()); + + apiCategories.ForEach(category => + { + category.Entries.ForEach(entry => + { + entry.Text = ModifierProvider.RemoveSquareBrackets(entry.Text); + }); + }); + + return apiCategories; } } diff --git a/src/Sidekick.Apis.Poe/Parser/ItemParser.cs b/src/Sidekick.Apis.Poe/Parser/ItemParser.cs index 69c985569..969c11a87 100644 --- a/src/Sidekick.Apis.Poe/Parser/ItemParser.cs +++ b/src/Sidekick.Apis.Poe/Parser/ItemParser.cs @@ -10,7 +10,6 @@ using Sidekick.Apis.Poe.Parser.Sockets; using Sidekick.Apis.Poe.Pseudo; using Sidekick.Common.Exceptions; -using Sidekick.Common.Game; using Sidekick.Common.Game.Items; namespace Sidekick.Apis.Poe.Parser @@ -72,7 +71,7 @@ public Item ParseItem(string itemText) var sockets = socketParser.Parse(parsingItem); var modifierLines = ParseModifiers(parsingItem); var properties = propertyParser.Parse(parsingItem, modifierLines); - var pseudoModifiers = parsingItem.Metadata.Game == GameType.PathOfExile ? ParsePseudoModifiers(modifierLines) : []; + var pseudoModifiers = pseudoModifierProvider.Parse(modifierLines); var item = new Item(metadata: metadata, invariant: invariant, header: header, @@ -137,16 +136,6 @@ private List ParseModifiers(ParsingItem parsingItem) }; } - private List ParsePseudoModifiers(List modifierLines) - { - if (modifierLines.Count == 0) - { - return new(); - } - - return pseudoModifierProvider.Parse(modifierLines); - } - #region Helpers private static bool GetBool(Regex pattern, ParsingItem parsingItem) diff --git a/src/Sidekick.Apis.Poe/Pseudo/Definitions/ElementalResistanceDefinition.cs b/src/Sidekick.Apis.Poe/Pseudo/Definitions/ElementalResistanceDefinition.cs new file mode 100644 index 000000000..a39df3438 --- /dev/null +++ b/src/Sidekick.Apis.Poe/Pseudo/Definitions/ElementalResistanceDefinition.cs @@ -0,0 +1,16 @@ +using System.Text.RegularExpressions; + +namespace Sidekick.Apis.Poe.Pseudo.Definitions; + +public class ElementalResistanceDefinition : PseudoDefinition +{ + protected override List Patterns => + [ + new(new Regex("to (?:Fire|Cold|Lightning) Resistance$")), + new(new Regex("to (?:Fire|Cold|Lightning) and (?:Fire|Cold|Lightning) Resistances$"), 2), + new(new Regex("(?=.*Chaos)to (?:Fire|Cold|Lightning|Chaos) and (?:Fire|Cold|Lightning|Chaos) Resistances$")), + new(new Regex("to all Elemental Resistances$"), 3), + ]; + + protected override Regex Exception => new("Minions|Enemies|Totems"); +} diff --git a/src/Sidekick.Apis.Poe/Pseudo/Models/PseudoDefinition.cs b/src/Sidekick.Apis.Poe/Pseudo/Models/PseudoDefinition.cs deleted file mode 100644 index c6888ea94..000000000 --- a/src/Sidekick.Apis.Poe/Pseudo/Models/PseudoDefinition.cs +++ /dev/null @@ -1,27 +0,0 @@ -namespace Sidekick.Apis.Poe.Pseudo.Models -{ - public class PseudoDefinition - { - public PseudoDefinition(string id, string text) - { - Id = id; - Text = text; - } - - public string Id { get; } - - public string Text { get; } - - public List Modifiers { get; set; } = new(); - - public override string ToString() - { - if (Text == null) - { - return Id; - } - - return $"{Text} - {Id}"; - } - } -} diff --git a/src/Sidekick.Apis.Poe/Pseudo/Models/PseudoDefinitionModifier.cs b/src/Sidekick.Apis.Poe/Pseudo/Models/PseudoDefinitionModifier.cs deleted file mode 100644 index d74ce6cba..000000000 --- a/src/Sidekick.Apis.Poe/Pseudo/Models/PseudoDefinitionModifier.cs +++ /dev/null @@ -1,25 +0,0 @@ -namespace Sidekick.Apis.Poe.Pseudo.Models -{ - public class PseudoDefinitionModifier - { - public PseudoDefinitionModifier(string type, string text, double multiplier) - { - Type = type; - Text = text; - Multiplier = multiplier; - } - - public List Ids { get; set; } = new List(); - - public string Type { get; set; } - - public string Text { get; set; } - - public double Multiplier { get; set; } - - public override string ToString() - { - return $"{Text} - {Multiplier}x - {string.Join(", ", Ids)} ({Ids.Count})"; - } - } -} diff --git a/src/Sidekick.Apis.Poe/Pseudo/Models/PseudoPattern.cs b/src/Sidekick.Apis.Poe/Pseudo/Models/PseudoPattern.cs deleted file mode 100644 index 550a1bbdd..000000000 --- a/src/Sidekick.Apis.Poe/Pseudo/Models/PseudoPattern.cs +++ /dev/null @@ -1,19 +0,0 @@ -using System.Text.RegularExpressions; - -namespace Sidekick.Apis.Poe.Pseudo.Models -{ - public class PseudoPattern - { - public PseudoPattern(Regex regex, double multiplier = 1) - { - Pattern = regex; - Multiplier = multiplier; - } - - public Regex Pattern { get; set; } - - public double Multiplier { get; set; } - - public List Matches { get; set; } = new List(); - } -} diff --git a/src/Sidekick.Apis.Poe/Pseudo/Models/PseudoPatternGroup.cs b/src/Sidekick.Apis.Poe/Pseudo/Models/PseudoPatternGroup.cs deleted file mode 100644 index 5b0baca2a..000000000 --- a/src/Sidekick.Apis.Poe/Pseudo/Models/PseudoPatternGroup.cs +++ /dev/null @@ -1,43 +0,0 @@ -using System.Text.RegularExpressions; - -namespace Sidekick.Apis.Poe.Pseudo.Models -{ - public class PseudoPatternGroup - { - public PseudoPatternGroup( - Modifiers.Models.ApiCategory apiCategory, - string id, - params PseudoPattern[] patterns) - : this(apiCategory, id, null, patterns) - { - } - - public PseudoPatternGroup( - Modifiers.Models.ApiCategory apiCategory, - string id, - Regex? exception, - params PseudoPattern[] patterns) - { - Id = id; - Exception = exception; - Patterns = patterns.ToList(); - - Text = apiCategory.Entries.First(x => x.Id == id).Text; - } - - public string Id { get; } - public Regex? Exception { get; } - public List Patterns { get; } - public string? Text { get; } - - public override string ToString() - { - if (Text == null) - { - return Id; - } - - return $"{Text} - {Id}"; - } - } -} diff --git a/src/Sidekick.Apis.Poe/Pseudo/Models/PseudoPatternMatch.cs b/src/Sidekick.Apis.Poe/Pseudo/Models/PseudoPatternMatch.cs deleted file mode 100644 index e4c1d5b2e..000000000 --- a/src/Sidekick.Apis.Poe/Pseudo/Models/PseudoPatternMatch.cs +++ /dev/null @@ -1,21 +0,0 @@ -namespace Sidekick.Apis.Poe.Pseudo.Models -{ - public class PseudoPatternMatch - { - public PseudoPatternMatch(string id, string type, string text) - { - Id = id; - Type = type; - Text = text; - } - - public string Id { get; set; } - public string Type { get; set; } - public string Text { get; set; } - - public override string ToString() - { - return $"{Text} - {Id} ({Type})"; - } - } -} diff --git a/src/Sidekick.Apis.Poe/Pseudo/PseudoDefinition.cs b/src/Sidekick.Apis.Poe/Pseudo/PseudoDefinition.cs new file mode 100644 index 000000000..e3349180a --- /dev/null +++ b/src/Sidekick.Apis.Poe/Pseudo/PseudoDefinition.cs @@ -0,0 +1,36 @@ +using System.Text.RegularExpressions; +using Sidekick.Apis.Poe.Modifiers.Models; + +namespace Sidekick.Apis.Poe.Pseudo; + +public abstract class PseudoDefinition +{ + public void AddModifierIfMatch(ApiModifier entry) + { + if (Exception != null && Exception.IsMatch(entry.Text)) + { + return; + } + + foreach (var pattern in Patterns) + { + if (entry.Id == null || entry.Type == null || entry.Text == null) + { + continue; + } + + if (pattern.Pattern.IsMatch(entry.Text)) + { + Modifiers.Add(new PseudoModifierDefinition(entry.Id, entry.Type, entry.Text, pattern.Multiplier)); + } + } + } + + protected abstract List Patterns { get; } + + protected abstract Regex? Exception { get; } + + public string Text => Modifiers.FirstOrDefault()?.Text ?? string.Empty; + + public List Modifiers { get; set; } = new(); +} diff --git a/src/Sidekick.Apis.Poe/Pseudo/PseudoModifierDefinition.cs b/src/Sidekick.Apis.Poe/Pseudo/PseudoModifierDefinition.cs new file mode 100644 index 000000000..3c0dcf70b --- /dev/null +++ b/src/Sidekick.Apis.Poe/Pseudo/PseudoModifierDefinition.cs @@ -0,0 +1,24 @@ +namespace Sidekick.Apis.Poe.Pseudo +{ + public class PseudoModifierDefinition + ( + string id, + string type, + string text, + double multiplier + ) + { + public string Id { get; } = id; + + public string Type { get; } = type; + + public string Text { get; } = text; + + public double Multiplier { get; } = multiplier; + + public override string ToString() + { + return $"{Text} - {Multiplier}x ({Type})"; + } + } +} diff --git a/src/Sidekick.Apis.Poe/Pseudo/PseudoModifierProvider.cs b/src/Sidekick.Apis.Poe/Pseudo/PseudoModifierProvider.cs index 79bd7bc5e..068f678ae 100644 --- a/src/Sidekick.Apis.Poe/Pseudo/PseudoModifierProvider.cs +++ b/src/Sidekick.Apis.Poe/Pseudo/PseudoModifierProvider.cs @@ -1,14 +1,13 @@ using System.Text.RegularExpressions; using Sidekick.Apis.Poe.Modifiers; -using Sidekick.Apis.Poe.Modifiers.Models; -using Sidekick.Apis.Poe.Pseudo.Models; +using Sidekick.Apis.Poe.Pseudo.Definitions; using Sidekick.Common.Game.Items; namespace Sidekick.Apis.Poe.Pseudo { public class PseudoModifierProvider(IInvariantModifierProvider invariantModifierProvider) : IPseudoModifierProvider { - private readonly Regex ParseHashPattern = new("\\#"); + private readonly Regex parseHashPattern = new("\\#"); private List Definitions { get; } = new(); @@ -18,642 +17,594 @@ public class PseudoModifierProvider(IInvariantModifierProvider invariantModifier /// public async Task Initialize() { - if (Definitions.Count > 0) - { - return; - } - - var result = await invariantModifierProvider.GetList(); - var groups = InitializeGroups(result); + Definitions.Clear(); + Definitions.AddRange([ + new ElementalResistanceDefinition(), + ]); - foreach (var category in result) + var categories = await invariantModifierProvider.GetList(); + foreach (var category in categories) { var first = category.Entries.FirstOrDefault(); - if (first == null || first.Id?.Split('.').First() == "pseudo") + if (first == null || first.Id.Split('.').First() == "pseudo") { continue; } foreach (var entry in category.Entries) { - foreach (var group in groups) + foreach (var definition in Definitions) { - if (group.Exception != null && group.Exception.IsMatch(entry.Text ?? string.Empty)) - { - continue; - } - - foreach (var pattern in group.Patterns) - { - if (entry.Id == null || entry.Type == null || entry.Text == null) - { - continue; - } - - if (pattern.Pattern.IsMatch(entry.Text)) - { - pattern.Matches.Add(new PseudoPatternMatch(entry.Id, entry.Type, entry.Text)); - } - } + definition.AddModifierIfMatch(entry); } } } - foreach (var group in groups) + foreach (var definition in Definitions) { - if (group.Text == null) - { - continue; - } - - var definition = new PseudoDefinition(group.Id, group.Text); - - foreach (var pattern in group.Patterns) - { - PseudoDefinitionModifier? modifier = null; - - foreach (var match in pattern.Matches.OrderBy(x => x.Type).ThenBy(x => x.Text.Length)) + definition.Modifiers = definition.Modifiers.OrderBy(x => x.Type switch { - if (modifier != null) - { - if (modifier.Type != match.Type) - { - modifier = null; - } - else if (!match.Text.StartsWith(modifier.Text)) - { - modifier = null; - } - } - - if (modifier == null) - { - modifier = new PseudoDefinitionModifier(match.Type, match.Text, pattern.Multiplier); - } - - modifier.Ids.Add(match.Id); - - if (!definition.Modifiers.Contains(modifier)) - { - definition.Modifiers.Add(modifier); - } - } - } - - Definitions.Add(definition); + "explicit" => 0, + "implicit" => 1, + "crafted" => 2, + "enchant" => 3, + "fractured" => 4, + "veiled" => 5, + "pseudo" => 6, + _ => 7, + }) + .ThenBy(x => x.Text) + .ToList(); } } - private static List InitializeGroups(List categories) - { - var pseudoCategory = categories - .FirstOrDefault(x => x.Entries.FirstOrDefault()?.Id?.Split('.').FirstOrDefault() == "pseudo"); - if (pseudoCategory == null) - { - return new(); - } + /* + private static List InitializeGroups(List categories) + { + var pseudoCategory = categories + .FirstOrDefault(x => x.Entries.FirstOrDefault()?.Id?.Split('.').FirstOrDefault() == "pseudo"); + if (pseudoCategory == null) + { + return new(); + } - var groups = new List() { - // +#% total to Cold Resistance - new PseudoPatternGroup( - apiCategory: pseudoCategory, - id: "pseudo.pseudo_total_cold_resistance", - exception: new Regex("Minions|Enemies|Totems"), - new PseudoPattern(new Regex("to Cold Resistance$")), - new PseudoPattern(new Regex("(?=.*Cold)to (?:Fire|Cold|Lightning|Chaos) and (?:Fire|Cold|Lightning|Chaos) Resistances$"))), - - // +#% total to Fire Resistance - new PseudoPatternGroup( - apiCategory: pseudoCategory, - id: "pseudo.pseudo_total_fire_resistance", - exception: new Regex("Minions|Enemies|Totems"), - new PseudoPattern(new Regex("to Fire Resistance$")), - new PseudoPattern(new Regex("(?=.*Fire)to (?:Fire|Cold|Lightning|Chaos) and (?:Fire|Cold|Lightning|Chaos) Resistances$"))), - - // +#% total to Lightning Resistance - new PseudoPatternGroup( - apiCategory: pseudoCategory, - id: "pseudo.pseudo_total_lightning_resistance", - exception: new Regex("Minions|Enemies|Totems"), - new PseudoPattern(new Regex("to Lightning Resistance$")), - new PseudoPattern(new Regex("(?=.*Lightning)to (?:Fire|Cold|Lightning|Chaos) and (?:Fire|Cold|Lightning|Chaos) Resistances$"))), - - // +#% total to Chaos Resistance - new PseudoPatternGroup( - apiCategory: pseudoCategory, - id: "pseudo.pseudo_total_chaos_resistance", - exception: new Regex("Minions|Enemies|Totems"), - new PseudoPattern(new Regex("to Chaos Resistance$")), - new PseudoPattern(new Regex("(?=.*Chaos)to (?:Fire|Cold|Lightning|Chaos) and (?:Fire|Cold|Lightning|Chaos) Resistances$"))), - - // +#% total to all Elemental Resistances - new PseudoPatternGroup( - apiCategory: pseudoCategory, - id: "pseudo.pseudo_total_all_elemental_resistances", - exception: new Regex("Minions|Enemies|Totems"), - new PseudoPattern(new Regex("to all Elemental Resistances$"))), - - // +#% total Elemental Resistance - new PseudoPatternGroup( - apiCategory: pseudoCategory, - id: "pseudo.pseudo_total_elemental_resistance", - exception: new Regex("Minions|Enemies|Totems"), - new PseudoPattern(new Regex("to (?:Fire|Cold|Lightning) Resistance$")), - new PseudoPattern(new Regex("to (?:Fire|Cold|Lightning) and (?:Fire|Cold|Lightning) Resistances$"), 2), - new PseudoPattern(new Regex("(?=.*Chaos)to (?:Fire|Cold|Lightning|Chaos) and (?:Fire|Cold|Lightning|Chaos) Resistances$")), - new PseudoPattern(new Regex("to all Elemental Resistances$"), 3)), - - // +#% total Resistance - new PseudoPatternGroup( - apiCategory: pseudoCategory, - id: "pseudo.pseudo_total_resistance", - exception: new Regex("Minions|Enemies|Totems"), - new PseudoPattern(new Regex("to (Fire|Cold|Lightning|Chaos) Resistance$")), - new PseudoPattern(new Regex("to (?:Fire|Cold|Lightning|Chaos) and (?:Fire|Cold|Lightning|Chaos) Resistances$"), 2), - new PseudoPattern(new Regex("to all Elemental Resistances$"), 3)), - - // # total Resistances - // pseudo.pseudo_count_resistances - - // +# total to Strength - new PseudoPatternGroup( - apiCategory: pseudoCategory, - id: "pseudo.pseudo_total_strength", - exception: new Regex("Passive"), - new PseudoPattern(new Regex("to Strength$")), - new PseudoPattern(new Regex("(?=.*Strength)to (?:Strength|Dexterity|Intelligence) and (?:Strength|Dexterity|Intelligence)$")), - new PseudoPattern(new Regex("to all Attributes$"))), - - // +# total to Dexterity - new PseudoPatternGroup( - apiCategory: pseudoCategory, - id: "pseudo.pseudo_total_dexterity", - exception: new Regex("Passive"), - new PseudoPattern(new Regex("to Dexterity$")), - new PseudoPattern(new Regex("(?=.*Dexterity)to (?:Strength|Dexterity|Intelligence) and (?:Strength|Dexterity|Intelligence)$")), - new PseudoPattern(new Regex("to all Attributes$"))), - - // +# total to Intelligence - new PseudoPatternGroup( - apiCategory: pseudoCategory, - id: "pseudo.pseudo_total_intelligence", - exception: new Regex("Passive"), - new PseudoPattern(new Regex("to Intelligence$")), - new PseudoPattern(new Regex("(?=.*Intelligence)to (?:Strength|Dexterity|Intelligence) and (?:Strength|Dexterity|Intelligence)$")), - new PseudoPattern(new Regex("to all Attributes$"))), - - // +# total to all Attributes - new PseudoPatternGroup( - apiCategory: pseudoCategory, - id: "pseudo.pseudo_total_all_attributes", - exception: new Regex("Passive"), - new PseudoPattern(new Regex("to all Attributes$"))), - - // +# total maximum Life - new PseudoPatternGroup( - apiCategory: pseudoCategory, - id: "pseudo.pseudo_total_life", - exception: new Regex("Zombies|Transformed"), - new PseudoPattern(new Regex("to maximum Life$")), - new PseudoPattern(new Regex("to Strength$"), 0.5), - new PseudoPattern(new Regex("(?=.*Strength)to (?:Strength|Dexterity|Intelligence) and (?:Strength|Dexterity|Intelligence)$"), 0.5), - new PseudoPattern(new Regex("to all Attributes$"), 0.5)), - - // +# total maximum Mana - new PseudoPatternGroup( - apiCategory: pseudoCategory, - id: "pseudo.pseudo_total_mana", - exception: new Regex("Transformed"), - new PseudoPattern(new Regex("to maximum Mana$")), - new PseudoPattern(new Regex("to Intelligence$"), 0.5), - new PseudoPattern(new Regex("(?=.*Intelligence)to (?:Strength|Dexterity|Intelligence) and (?:Strength|Dexterity|Intelligence)$"), 0.5), - new PseudoPattern(new Regex("to all Attributes$"), 0.5)), - - // +# total maximum Energy Shield - new PseudoPatternGroup( - apiCategory: pseudoCategory, - id: "pseudo.pseudo_total_energy_shield", - new PseudoPattern(new Regex("to maximum Energy Shield$"))), - - // #% total increased maximum Energy Shield - new PseudoPatternGroup( - apiCategory: pseudoCategory, - id: "pseudo.pseudo_increased_energy_shield", - new PseudoPattern(new Regex("% increased maximum Energy Shield$"))), - - // +#% total Attack Speed - new PseudoPatternGroup( - apiCategory: pseudoCategory, - id: "pseudo.pseudo_total_attack_speed", - new PseudoPattern(new Regex("^\\#% increased Attack Speed$"))), - - // +#% total Cast Speed - new PseudoPatternGroup( - apiCategory: pseudoCategory, - id: "pseudo.pseudo_total_cast_speed", - new PseudoPattern(new Regex("^\\#% increased Cast Speed$"))), - - // #% increased Movement Speed - new PseudoPatternGroup( - apiCategory: pseudoCategory, - id: "pseudo.pseudo_increased_movement_speed", - new PseudoPattern(new Regex("^\\#% increased Movement Speed$"))), - - // #% total increased Physical Damage - // pseudo.pseudo_increased_physical_damage - - // +#% Global Critical Strike Chance - // pseudo.pseudo_global_critical_strike_chance - - // +#% total Critical Strike Chance for Spells - // pseudo.pseudo_critical_strike_chance_for_spells - - // +#% Global Critical Strike Multiplier - // pseudo.pseudo_global_critical_strike_multiplier + var groups = new List() { + // +#% total to Cold Resistance + new PseudoPatternGroup( + apiCategory: pseudoCategory, + id: "pseudo.pseudo_total_cold_resistance", + exception: new Regex("Minions|Enemies|Totems"), + new PseudoPattern(new Regex("to Cold Resistance$")), + new PseudoPattern(new Regex("(?=.*Cold)to (?:Fire|Cold|Lightning|Chaos) and (?:Fire|Cold|Lightning|Chaos) Resistances$"))), + + // +#% total to Fire Resistance + new PseudoPatternGroup( + apiCategory: pseudoCategory, + id: "pseudo.pseudo_total_fire_resistance", + exception: new Regex("Minions|Enemies|Totems"), + new PseudoPattern(new Regex("to Fire Resistance$")), + new PseudoPattern(new Regex("(?=.*Fire)to (?:Fire|Cold|Lightning|Chaos) and (?:Fire|Cold|Lightning|Chaos) Resistances$"))), + + // +#% total to Lightning Resistance + new PseudoPatternGroup( + apiCategory: pseudoCategory, + id: "pseudo.pseudo_total_lightning_resistance", + exception: new Regex("Minions|Enemies|Totems"), + new PseudoPattern(new Regex("to Lightning Resistance$")), + new PseudoPattern(new Regex("(?=.*Lightning)to (?:Fire|Cold|Lightning|Chaos) and (?:Fire|Cold|Lightning|Chaos) Resistances$"))), + + // +#% total to Chaos Resistance + new PseudoPatternGroup( + apiCategory: pseudoCategory, + id: "pseudo.pseudo_total_chaos_resistance", + exception: new Regex("Minions|Enemies|Totems"), + new PseudoPattern(new Regex("to Chaos Resistance$")), + new PseudoPattern(new Regex("(?=.*Chaos)to (?:Fire|Cold|Lightning|Chaos) and (?:Fire|Cold|Lightning|Chaos) Resistances$"))), + + // +#% total to all Elemental Resistances + new PseudoPatternGroup( + apiCategory: pseudoCategory, + id: "pseudo.pseudo_total_all_elemental_resistances", + exception: new Regex("Minions|Enemies|Totems"), + new PseudoPattern(new Regex("to all Elemental Resistances$"))), + + // +#% total Elemental Resistance + new PseudoPatternGroup( + apiCategory: pseudoCategory, + id: "pseudo.pseudo_total_elemental_resistance", + exception: new Regex("Minions|Enemies|Totems"), + new PseudoPattern(new Regex("to (?:Fire|Cold|Lightning) Resistance$")), + new PseudoPattern(new Regex("to (?:Fire|Cold|Lightning) and (?:Fire|Cold|Lightning) Resistances$"), 2), + new PseudoPattern(new Regex("(?=.*Chaos)to (?:Fire|Cold|Lightning|Chaos) and (?:Fire|Cold|Lightning|Chaos) Resistances$")), + new PseudoPattern(new Regex("to all Elemental Resistances$"), 3)), + + // +#% total Resistance + new PseudoPatternGroup( + apiCategory: pseudoCategory, + id: "pseudo.pseudo_total_resistance", + exception: new Regex("Minions|Enemies|Totems"), + new PseudoPattern(new Regex("to (Fire|Cold|Lightning|Chaos) Resistance$")), + new PseudoPattern(new Regex("to (?:Fire|Cold|Lightning|Chaos) and (?:Fire|Cold|Lightning|Chaos) Resistances$"), 2), + new PseudoPattern(new Regex("to all Elemental Resistances$"), 3)), + + // # total Resistances + // pseudo.pseudo_count_resistances + + // +# total to Strength + new PseudoPatternGroup( + apiCategory: pseudoCategory, + id: "pseudo.pseudo_total_strength", + exception: new Regex("Passive"), + new PseudoPattern(new Regex("to Strength$")), + new PseudoPattern(new Regex("(?=.*Strength)to (?:Strength|Dexterity|Intelligence) and (?:Strength|Dexterity|Intelligence)$")), + new PseudoPattern(new Regex("to all Attributes$"))), + + // +# total to Dexterity + new PseudoPatternGroup( + apiCategory: pseudoCategory, + id: "pseudo.pseudo_total_dexterity", + exception: new Regex("Passive"), + new PseudoPattern(new Regex("to Dexterity$")), + new PseudoPattern(new Regex("(?=.*Dexterity)to (?:Strength|Dexterity|Intelligence) and (?:Strength|Dexterity|Intelligence)$")), + new PseudoPattern(new Regex("to all Attributes$"))), + + // +# total to Intelligence + new PseudoPatternGroup( + apiCategory: pseudoCategory, + id: "pseudo.pseudo_total_intelligence", + exception: new Regex("Passive"), + new PseudoPattern(new Regex("to Intelligence$")), + new PseudoPattern(new Regex("(?=.*Intelligence)to (?:Strength|Dexterity|Intelligence) and (?:Strength|Dexterity|Intelligence)$")), + new PseudoPattern(new Regex("to all Attributes$"))), + + // +# total to all Attributes + new PseudoPatternGroup( + apiCategory: pseudoCategory, + id: "pseudo.pseudo_total_all_attributes", + exception: new Regex("Passive"), + new PseudoPattern(new Regex("to all Attributes$"))), + + // +# total maximum Life + new PseudoPatternGroup( + apiCategory: pseudoCategory, + id: "pseudo.pseudo_total_life", + exception: new Regex("Zombies|Transformed"), + new PseudoPattern(new Regex("to maximum Life$")), + new PseudoPattern(new Regex("to Strength$"), 0.5), + new PseudoPattern(new Regex("(?=.*Strength)to (?:Strength|Dexterity|Intelligence) and (?:Strength|Dexterity|Intelligence)$"), 0.5), + new PseudoPattern(new Regex("to all Attributes$"), 0.5)), + + // +# total maximum Mana + new PseudoPatternGroup( + apiCategory: pseudoCategory, + id: "pseudo.pseudo_total_mana", + exception: new Regex("Transformed"), + new PseudoPattern(new Regex("to maximum Mana$")), + new PseudoPattern(new Regex("to Intelligence$"), 0.5), + new PseudoPattern(new Regex("(?=.*Intelligence)to (?:Strength|Dexterity|Intelligence) and (?:Strength|Dexterity|Intelligence)$"), 0.5), + new PseudoPattern(new Regex("to all Attributes$"), 0.5)), + + // +# total maximum Energy Shield + new PseudoPatternGroup( + apiCategory: pseudoCategory, + id: "pseudo.pseudo_total_energy_shield", + new PseudoPattern(new Regex("to maximum Energy Shield$"))), + + // #% total increased maximum Energy Shield + new PseudoPatternGroup( + apiCategory: pseudoCategory, + id: "pseudo.pseudo_increased_energy_shield", + new PseudoPattern(new Regex("% increased maximum Energy Shield$"))), + + // +#% total Attack Speed + new PseudoPatternGroup( + apiCategory: pseudoCategory, + id: "pseudo.pseudo_total_attack_speed", + new PseudoPattern(new Regex("^\\#% increased Attack Speed$"))), + + // +#% total Cast Speed + new PseudoPatternGroup( + apiCategory: pseudoCategory, + id: "pseudo.pseudo_total_cast_speed", + new PseudoPattern(new Regex("^\\#% increased Cast Speed$"))), + + // #% increased Movement Speed + new PseudoPatternGroup( + apiCategory: pseudoCategory, + id: "pseudo.pseudo_increased_movement_speed", + new PseudoPattern(new Regex("^\\#% increased Movement Speed$"))), + + // #% total increased Physical Damage + // pseudo.pseudo_increased_physical_damage + + // +#% Global Critical Strike Chance + // pseudo.pseudo_global_critical_strike_chance + + // +#% total Critical Strike Chance for Spells + // pseudo.pseudo_critical_strike_chance_for_spells + + // +#% Global Critical Strike Multiplier + // pseudo.pseudo_global_critical_strike_multiplier - // Adds # to # Physical Damage - // pseudo.pseudo_adds_physical_damage + // Adds # to # Physical Damage + // pseudo.pseudo_adds_physical_damage - // Adds # to # Lightning Damage - // pseudo.pseudo_adds_lightning_damage + // Adds # to # Lightning Damage + // pseudo.pseudo_adds_lightning_damage - // Adds # to # Cold Damage - // pseudo.pseudo_adds_cold_damage + // Adds # to # Cold Damage + // pseudo.pseudo_adds_cold_damage - // Adds # to # Fire Damage - // pseudo.pseudo_adds_fire_damage + // Adds # to # Fire Damage + // pseudo.pseudo_adds_fire_damage - // Adds # to # Elemental Damage - // pseudo.pseudo_adds_elemental_damage + // Adds # to # Elemental Damage + // pseudo.pseudo_adds_elemental_damage - // Adds # to # Chaos Damage - // pseudo.pseudo_adds_chaos_damage + // Adds # to # Chaos Damage + // pseudo.pseudo_adds_chaos_damage - // Adds # to # Damage - // pseudo.pseudo_adds_damage + // Adds # to # Damage + // pseudo.pseudo_adds_damage - // Adds # to # Physical Damage to Attacks - // pseudo.pseudo_adds_physical_damage_to_attacks + // Adds # to # Physical Damage to Attacks + // pseudo.pseudo_adds_physical_damage_to_attacks - // Adds # to # Lightning Damage to Attacks - // pseudo.pseudo_adds_lightning_damage_to_attacks + // Adds # to # Lightning Damage to Attacks + // pseudo.pseudo_adds_lightning_damage_to_attacks - // Adds # to # Cold Damage to Attacks - // pseudo.pseudo_adds_cold_damage_to_attacks + // Adds # to # Cold Damage to Attacks + // pseudo.pseudo_adds_cold_damage_to_attacks - // Adds # to # Fire Damage to Attacks - // pseudo.pseudo_adds_fire_damage_to_attacks + // Adds # to # Fire Damage to Attacks + // pseudo.pseudo_adds_fire_damage_to_attacks - // Adds # to # Elemental Damage to Attacks - // pseudo.pseudo_adds_elemental_damage_to_attacks + // Adds # to # Elemental Damage to Attacks + // pseudo.pseudo_adds_elemental_damage_to_attacks - // Adds # to # Chaos Damage to Attacks - // pseudo.pseudo_adds_chaos_damage_to_attacks + // Adds # to # Chaos Damage to Attacks + // pseudo.pseudo_adds_chaos_damage_to_attacks - // Adds # to # Damage to Attacks - // pseudo.pseudo_adds_damage_to_attacks + // Adds # to # Damage to Attacks + // pseudo.pseudo_adds_damage_to_attacks - // Adds # to # Physical Damage to Spells - // pseudo.pseudo_adds_physical_damage_to_spells + // Adds # to # Physical Damage to Spells + // pseudo.pseudo_adds_physical_damage_to_spells - // Adds # to # Lightning Damage to Spells - // pseudo.pseudo_adds_lightning_damage_to_spells + // Adds # to # Lightning Damage to Spells + // pseudo.pseudo_adds_lightning_damage_to_spells - // Adds # to # Cold Damage to Spells - // pseudo.pseudo_adds_cold_damage_to_spells + // Adds # to # Cold Damage to Spells + // pseudo.pseudo_adds_cold_damage_to_spells - // Adds # to # Fire Damage to Spells - // pseudo.pseudo_adds_fire_damage_to_spells + // Adds # to # Fire Damage to Spells + // pseudo.pseudo_adds_fire_damage_to_spells - // Adds # to # Elemental Damage to Spells - // pseudo.pseudo_adds_elemental_damage_to_spells + // Adds # to # Elemental Damage to Spells + // pseudo.pseudo_adds_elemental_damage_to_spells - // Adds # to # Chaos Damage to Spells - // pseudo.pseudo_adds_chaos_damage_to_spells + // Adds # to # Chaos Damage to Spells + // pseudo.pseudo_adds_chaos_damage_to_spells - // Adds # to # Damage to Spells - // pseudo.pseudo_adds_damage_to_spells + // Adds # to # Damage to Spells + // pseudo.pseudo_adds_damage_to_spells - // #% increased Elemental Damage - // pseudo.pseudo_increased_elemental_damage + // #% increased Elemental Damage + // pseudo.pseudo_increased_elemental_damage - // #% increased Lightning Damage - // pseudo.pseudo_increased_lightning_damage + // #% increased Lightning Damage + // pseudo.pseudo_increased_lightning_damage - // #% increased Cold Damage - // pseudo.pseudo_increased_cold_damage + // #% increased Cold Damage + // pseudo.pseudo_increased_cold_damage - // #% increased Fire Damage - // pseudo.pseudo_increased_fire_damage + // #% increased Fire Damage + // pseudo.pseudo_increased_fire_damage - // #% increased Spell Damage - // pseudo.pseudo_increased_spell_damage + // #% increased Spell Damage + // pseudo.pseudo_increased_spell_damage - // #% increased Lightning Spell Damage - // pseudo.pseudo_increased_lightning_spell_damage + // #% increased Lightning Spell Damage + // pseudo.pseudo_increased_lightning_spell_damage - // #% increased Cold Spell Damage - // pseudo.pseudo_increased_cold_spell_damage + // #% increased Cold Spell Damage + // pseudo.pseudo_increased_cold_spell_damage - // #% increased Fire Spell Damage - // pseudo.pseudo_increased_fire_spell_damage + // #% increased Fire Spell Damage + // pseudo.pseudo_increased_fire_spell_damage - // #% increased Lightning Damage with Attack Skills - // pseudo.pseudo_increased_lightning_damage_with_attack_skills + // #% increased Lightning Damage with Attack Skills + // pseudo.pseudo_increased_lightning_damage_with_attack_skills - // #% increased Cold Damage with Attack Skills - // pseudo.pseudo_increased_cold_damage_with_attack_skills + // #% increased Cold Damage with Attack Skills + // pseudo.pseudo_increased_cold_damage_with_attack_skills - // #% increased Fire Damage with Attack Skills - // pseudo.pseudo_increased_fire_damage_with_attack_skills + // #% increased Fire Damage with Attack Skills + // pseudo.pseudo_increased_fire_damage_with_attack_skills - // #% increased Elemental Damage with Attack Skills - // pseudo.pseudo_increased_elemental_damage_with_attack_skills + // #% increased Elemental Damage with Attack Skills + // pseudo.pseudo_increased_elemental_damage_with_attack_skills - // #% increased Rarity of Items found - // pseudo.pseudo_increased_rarity + // #% increased Rarity of Items found + // pseudo.pseudo_increased_rarity - // #% increased Burning Damage - // pseudo.pseudo_increased_burning_damage + // #% increased Burning Damage + // pseudo.pseudo_increased_burning_damage - // # Life Regenerated per Second - // pseudo.pseudo_total_life_regen + // # Life Regenerated per Second + // pseudo.pseudo_total_life_regen - // #% of Life Regenerated per Second - // pseudo.pseudo_percent_life_regen + // #% of Life Regenerated per Second + // pseudo.pseudo_percent_life_regen - // #% of Physical Attack Damage Leeched as Life - // pseudo.pseudo_physical_attack_damage_leeched_as_life + // #% of Physical Attack Damage Leeched as Life + // pseudo.pseudo_physical_attack_damage_leeched_as_life - // #% of Physical Attack Damage Leeched as Mana - // pseudo.pseudo_physical_attack_damage_leeched_as_mana + // #% of Physical Attack Damage Leeched as Mana + // pseudo.pseudo_physical_attack_damage_leeched_as_mana - // #% increased Mana Regeneration Rate - // pseudo.pseudo_increased_mana_regen + // #% increased Mana Regeneration Rate + // pseudo.pseudo_increased_mana_regen - // +# total to Level of Socketed Gems - // pseudo.pseudo_total_additional_gem_levels + // +# total to Level of Socketed Gems + // pseudo.pseudo_total_additional_gem_levels - // +# total to Level of Socketed Elemental Gems - // pseudo.pseudo_total_additional_elemental_gem_levels + // +# total to Level of Socketed Elemental Gems + // pseudo.pseudo_total_additional_elemental_gem_levels - // +# total to Level of Socketed Fire Gems - // pseudo.pseudo_total_additional_fire_gem_levels + // +# total to Level of Socketed Fire Gems + // pseudo.pseudo_total_additional_fire_gem_levels - // +# total to Level of Socketed Cold Gems - // pseudo.pseudo_total_additional_cold_gem_levels + // +# total to Level of Socketed Cold Gems + // pseudo.pseudo_total_additional_cold_gem_levels - // +# total to Level of Socketed Lightning Gems - // pseudo.pseudo_total_additional_lightning_gem_levels + // +# total to Level of Socketed Lightning Gems + // pseudo.pseudo_total_additional_lightning_gem_levels - // +# total to Level of Socketed Chaos Gems - // pseudo.pseudo_total_additional_chaos_gem_levels + // +# total to Level of Socketed Chaos Gems + // pseudo.pseudo_total_additional_chaos_gem_levels - // +# total to Level of Socketed Spell Gems - // pseudo.pseudo_total_additional_spell_gem_levels + // +# total to Level of Socketed Spell Gems + // pseudo.pseudo_total_additional_spell_gem_levels - // +# total to Level of Socketed Projectile Gems - // pseudo.pseudo_total_additional_projectile_gem_levels + // +# total to Level of Socketed Projectile Gems + // pseudo.pseudo_total_additional_projectile_gem_levels - // +# total to Level of Socketed Bow Gems - // pseudo.pseudo_total_additional_bow_gem_levels + // +# total to Level of Socketed Bow Gems + // pseudo.pseudo_total_additional_bow_gem_levels - // +# total to Level of Socketed Melee Gems - // pseudo.pseudo_total_additional_melee_gem_levels + // +# total to Level of Socketed Melee Gems + // pseudo.pseudo_total_additional_melee_gem_levels - // +# total to Level of Socketed Minion Gems - // pseudo.pseudo_total_additional_minion_gem_levels + // +# total to Level of Socketed Minion Gems + // pseudo.pseudo_total_additional_minion_gem_levels - // +# total to Level of Socketed Strength Gems - // pseudo.pseudo_total_additional_strength_gem_levels + // +# total to Level of Socketed Strength Gems + // pseudo.pseudo_total_additional_strength_gem_levels - // +# total to Level of Socketed Dexterity Gems - // pseudo.pseudo_total_additional_dexterity_gem_levels + // +# total to Level of Socketed Dexterity Gems + // pseudo.pseudo_total_additional_dexterity_gem_levels - // +# total to Level of Socketed Intelligence Gems - // pseudo.pseudo_total_additional_intelligence_gem_levels + // +# total to Level of Socketed Intelligence Gems + // pseudo.pseudo_total_additional_intelligence_gem_levels - // +# total to Level of Socketed Aura Gems - // pseudo.pseudo_total_additional_aura_gem_levels + // +# total to Level of Socketed Aura Gems + // pseudo.pseudo_total_additional_aura_gem_levels - // +# total to Level of Socketed Movement Gems - // pseudo.pseudo_total_additional_movement_gem_levels + // +# total to Level of Socketed Movement Gems + // pseudo.pseudo_total_additional_movement_gem_levels - // +# total to Level of Socketed Curse Gems - // pseudo.pseudo_total_additional_curse_gem_levels + // +# total to Level of Socketed Curse Gems + // pseudo.pseudo_total_additional_curse_gem_levels - // +# total to Level of Socketed Vaal Gems - // pseudo.pseudo_total_additional_vaal_gem_levels + // +# total to Level of Socketed Vaal Gems + // pseudo.pseudo_total_additional_vaal_gem_levels - // +# total to Level of Socketed Support Gems - // pseudo.pseudo_total_additional_support_gem_levels + // +# total to Level of Socketed Support Gems + // pseudo.pseudo_total_additional_support_gem_levels - // +# total to Level of Socketed Skill Gems - // pseudo.pseudo_total_additional_skill_gem_levels + // +# total to Level of Socketed Skill Gems + // pseudo.pseudo_total_additional_skill_gem_levels - // +# total to Level of Socketed Warcry Gems - // pseudo.pseudo_total_additional_warcry_gem_levels + // +# total to Level of Socketed Warcry Gems + // pseudo.pseudo_total_additional_warcry_gem_levels - // +# total to Level of Socketed Golem Gems - // pseudo.pseudo_total_additional_golem_gem_levels + // +# total to Level of Socketed Golem Gems + // pseudo.pseudo_total_additional_golem_gem_levels - // # Implicit Modifiers - // pseudo.pseudo_number_of_implicit_mods + // # Implicit Modifiers + // pseudo.pseudo_number_of_implicit_mods - // # Prefix Modifiers - // pseudo.pseudo_number_of_prefix_mods + // # Prefix Modifiers + // pseudo.pseudo_number_of_prefix_mods - // # Suffix Modifiers - // pseudo.pseudo_number_of_suffix_mods + // # Suffix Modifiers + // pseudo.pseudo_number_of_suffix_mods - // # Modifiers - // pseudo.pseudo_number_of_affix_mods + // # Modifiers + // pseudo.pseudo_number_of_affix_mods - // # Crafted Prefix Modifiers - // pseudo.pseudo_number_of_crafted_prefix_mods + // # Crafted Prefix Modifiers + // pseudo.pseudo_number_of_crafted_prefix_mods - // # Crafted Suffix Modifiers - // pseudo.pseudo_number_of_crafted_suffix_mods + // # Crafted Suffix Modifiers + // pseudo.pseudo_number_of_crafted_suffix_mods - // # Crafted Modifiers - // pseudo.pseudo_number_of_crafted_mods + // # Crafted Modifiers + // pseudo.pseudo_number_of_crafted_mods - // # Empty Prefix Modifiers - // pseudo.pseudo_number_of_empty_prefix_mods + // # Empty Prefix Modifiers + // pseudo.pseudo_number_of_empty_prefix_mods - // # Empty Suffix Modifiers - // pseudo.pseudo_number_of_empty_suffix_mods + // # Empty Suffix Modifiers + // pseudo.pseudo_number_of_empty_suffix_mods - // # Empty Modifiers - // pseudo.pseudo_number_of_empty_affix_mods + // # Empty Modifiers + // pseudo.pseudo_number_of_empty_affix_mods - // # Incubator Kills (Whispering) - // pseudo.pseudo_whispering_incubator_kills + // # Incubator Kills (Whispering) + // pseudo.pseudo_whispering_incubator_kills - // # Incubator Kills (Fine) - // pseudo.pseudo_fine_incubator_kills + // # Incubator Kills (Fine) + // pseudo.pseudo_fine_incubator_kills - // # Incubator Kills (Singular) - // pseudo.pseudo_singular_incubator_kills + // # Incubator Kills (Singular) + // pseudo.pseudo_singular_incubator_kills - // # Incubator Kills (Cartographer's) - // pseudo.pseudo_cartographers_incubator_kills + // # Incubator Kills (Cartographer's) + // pseudo.pseudo_cartographers_incubator_kills - // # Incubator Kills (Otherwordly) - // pseudo.pseudo_otherworldly_incubator_kills + // # Incubator Kills (Otherwordly) + // pseudo.pseudo_otherworldly_incubator_kills - // # Incubator Kills (Abyssal) - // pseudo.pseudo_abyssal_incubator_kills + // # Incubator Kills (Abyssal) + // pseudo.pseudo_abyssal_incubator_kills - // # Incubator Kills (Fragmented) - // pseudo.pseudo_fragmented_incubator_kills + // # Incubator Kills (Fragmented) + // pseudo.pseudo_fragmented_incubator_kills - // # Incubator Kills (Skittering) - // pseudo.pseudo_skittering_incubator_kills + // # Incubator Kills (Skittering) + // pseudo.pseudo_skittering_incubator_kills - // # Incubator Kills (Infused) - // pseudo.pseudo_infused_incubator_kills + // # Incubator Kills (Infused) + // pseudo.pseudo_infused_incubator_kills - // # Incubator Kills (Fossilised) - // pseudo.pseudo_fossilised_incubator_kills + // # Incubator Kills (Fossilised) + // pseudo.pseudo_fossilised_incubator_kills - // # Incubator Kills (Decadent) - // pseudo.pseudo_decadent_incubator_kills + // # Incubator Kills (Decadent) + // pseudo.pseudo_decadent_incubator_kills - // # Incubator Kills (Diviner's) - // pseudo.pseudo_diviners_incubator_kills + // # Incubator Kills (Diviner's) + // pseudo.pseudo_diviners_incubator_kills - // # Incubator Kills (Primal) - // pseudo.pseudo_primal_incubator_kills + // # Incubator Kills (Primal) + // pseudo.pseudo_primal_incubator_kills - // # Incubator Kills (Enchanted) - // pseudo.pseudo_enchanted_incubator_kills + // # Incubator Kills (Enchanted) + // pseudo.pseudo_enchanted_incubator_kills - // # Incubator Kills (Geomancer's) - // pseudo.pseudo_geomancers_incubator_kills + // # Incubator Kills (Geomancer's) + // pseudo.pseudo_geomancers_incubator_kills - // # Incubator Kills (Ornate) - // pseudo.pseudo_ornate_incubator_kills + // # Incubator Kills (Ornate) + // pseudo.pseudo_ornate_incubator_kills - // # Incubator Kills (Time-Lost) - // pseudo.pseudo_timelost_incubator_kills + // # Incubator Kills (Time-Lost) + // pseudo.pseudo_timelost_incubator_kills - // # Incubator Kills (Celestial Armoursmith's) - // pseudo.pseudo_celestial_armoursmiths_incubator_kills + // # Incubator Kills (Celestial Armoursmith's) + // pseudo.pseudo_celestial_armoursmiths_incubator_kills - // # Incubator Kills (Celestial Blacksmith's) - // pseudo.pseudo_celestial_blacksmiths_incubator_kills + // # Incubator Kills (Celestial Blacksmith's) + // pseudo.pseudo_celestial_blacksmiths_incubator_kills - // # Incubator Kills (Celestial Jeweller's) - // pseudo.pseudo_celestial_jewellers_incubator_kills + // # Incubator Kills (Celestial Jeweller's) + // pseudo.pseudo_celestial_jewellers_incubator_kills - // # Incubator Kills (Eldritch) - // pseudo.pseudo_eldritch_incubator_kills + // # Incubator Kills (Eldritch) + // pseudo.pseudo_eldritch_incubator_kills - // # Incubator Kills (Obscured) - // pseudo.pseudo_obscured_incubator_kills + // # Incubator Kills (Obscured) + // pseudo.pseudo_obscured_incubator_kills - // # Incubator Kills (Foreboding) - // pseudo.pseudo_foreboding_incubator_kills + // # Incubator Kills (Foreboding) + // pseudo.pseudo_foreboding_incubator_kills - // # Incubator Kills (Thaumaturge's) - // pseudo.pseudo_thaumaturges_incubator_kills + // # Incubator Kills (Thaumaturge's) + // pseudo.pseudo_thaumaturges_incubator_kills - // # Incubator Kills (Mysterious) - // pseudo.pseudo_mysterious_incubator_kills + // # Incubator Kills (Mysterious) + // pseudo.pseudo_mysterious_incubator_kills - // # Incubator Kills (Gemcutter's) - // pseudo.pseudo_gemcutters_incubator_kills + // # Incubator Kills (Gemcutter's) + // pseudo.pseudo_gemcutters_incubator_kills - // # Incubator Kills (Feral) - // pseudo.pseudo_feral_incubator_kills + // # Incubator Kills (Feral) + // pseudo.pseudo_feral_incubator_kills - // # Fractured Modifiers - // pseudo.pseudo_number_of_fractured_mods + // # Fractured Modifiers + // pseudo.pseudo_number_of_fractured_mods - // +#% Quality to Elemental Damage Modifiers - // pseudo.pseudo_jewellery_elemental_quality + // +#% Quality to Elemental Damage Modifiers + // pseudo.pseudo_jewellery_elemental_quality - // +#% Quality to Caster Modifiers - // pseudo.pseudo_jewellery_caster_quality + // +#% Quality to Caster Modifiers + // pseudo.pseudo_jewellery_caster_quality - // +#% Quality to Attack Modifiers - // pseudo.pseudo_jewellery_attack_quality + // +#% Quality to Attack Modifiers + // pseudo.pseudo_jewellery_attack_quality - // +#% Quality to Defence Modifiers - // pseudo.pseudo_jewellery_defense_quality + // +#% Quality to Defence Modifiers + // pseudo.pseudo_jewellery_defense_quality - // +#% Quality to Life and Mana Modifiers - // pseudo.pseudo_jewellery_resource_quality + // +#% Quality to Life and Mana Modifiers + // pseudo.pseudo_jewellery_resource_quality - // +#% Quality to Resistance Modifiers - // pseudo.pseudo_jewellery_resistance_quality + // +#% Quality to Resistance Modifiers + // pseudo.pseudo_jewellery_resistance_quality - // +#% Quality to Attribute Modifiers - // pseudo.pseudo_jewellery_attribute_quality - }; + // +#% Quality to Attribute Modifiers + // pseudo.pseudo_jewellery_attribute_quality + }; - return groups; - } + return groups; + } + */ public List Parse(List lines) { - var modifiers = new List(); - - foreach (var line in lines) + if (lines.Count == 0) { - var modifier = line.Modifiers.FirstOrDefault(); - if (modifier == null) - { - continue; - } - - FillPseudo(modifiers, line); + return []; } - modifiers.ForEach(x => + var results = new List(); + + foreach (var definition in Definitions) { - if (x.Text == null) + var result = new PseudoModifier() { - return; - } + Text = definition.Text, + }; - x.Text = ParseHashPattern.Replace(x.Text, ((int)x.Value).ToString(), 1); - }); - - return modifiers; - } - - private void FillPseudo(List modifiers, ModifierLine line) - { - foreach (var pseudoDefinition in Definitions) - { - foreach (var pseudoModifier in pseudoDefinition.Modifiers) + foreach (var definitionModifier in definition.Modifiers) { - var lineModifier = line.Modifiers.FirstOrDefault(); - if (lineModifier == null || !pseudoModifier.Ids.Any(id => id == lineModifier.Id)) + foreach (var line in lines) { - continue; - } + var hasModifier = false; - var modifier = modifiers.FirstOrDefault(x => x.Id == pseudoDefinition.Id); - if (modifier == null) - { - modifiers.Add(new PseudoModifier( - text: pseudoDefinition.Text) + foreach (var modifier in line.Modifiers) { - Id = pseudoDefinition.Id, - Value = (int)(line.Values.FirstOrDefault() * pseudoModifier.Multiplier), - }); - } - else - { - modifier.Value += (int)(line.Values.FirstOrDefault() * pseudoModifier.Multiplier); + if (modifier.Id != definitionModifier.Id) + { + continue; + } + + hasModifier = true; + result.Modifiers.Add(modifier.Id, definitionModifier.Multiplier); + } + + if (hasModifier) + { + result.Value += line.Values.Average() * definitionModifier.Multiplier; + } } + } - break; + if (result.Modifiers.Count > 0) + { + results.Add(result); } } + + results.ForEach(x => + { + x.Value = (int)x.Value; + x.Text = parseHashPattern.Replace(x.Text, ((int)x.Value).ToString(), 1); + }); + + return results; } } } diff --git a/src/Sidekick.Apis.Poe/Pseudo/PseudoPattern.cs b/src/Sidekick.Apis.Poe/Pseudo/PseudoPattern.cs new file mode 100644 index 000000000..190be2a5f --- /dev/null +++ b/src/Sidekick.Apis.Poe/Pseudo/PseudoPattern.cs @@ -0,0 +1,14 @@ +using System.Text.RegularExpressions; + +namespace Sidekick.Apis.Poe.Pseudo; + +public class PseudoPattern +( + Regex regex, + int multiplier = 1 +) +{ + public Regex Pattern { get; set; } = regex; + + public int Multiplier { get; set; } = multiplier; +} diff --git a/src/Sidekick.Apis.Poe/Trade/Models/PseudoModifierFilter.cs b/src/Sidekick.Apis.Poe/Trade/Models/PseudoModifierFilter.cs index 918653eda..6160cfc19 100644 --- a/src/Sidekick.Apis.Poe/Trade/Models/PseudoModifierFilter.cs +++ b/src/Sidekick.Apis.Poe/Trade/Models/PseudoModifierFilter.cs @@ -2,17 +2,9 @@ namespace Sidekick.Apis.Poe.Trade.Models { - public class PseudoModifierFilter : ITradeFilter + public class PseudoModifierFilter : PseudoModifier, ITradeFilter { - public PseudoModifierFilter(PseudoModifier modifier) - { - Modifier = modifier; - Checked = false; - } - - public PseudoModifier Modifier { get; } - - public bool? @Checked { get; set; } + public bool? @Checked { get; set; } = false; public decimal? Min { get; set; } @@ -25,13 +17,13 @@ public PseudoModifierFilter(PseudoModifier modifier) /// public void NormalizeMinValue() { - if (Modifier.Value > 0) + if (Value > 0) { - Min = (int)Math.Max((1 - NormalizeValue) * Modifier.Value, 0); + Min = (int)Math.Max((1 - NormalizeValue) * Value, 0); } else { - Min = (int)Math.Min((1 + NormalizeValue) * Modifier.Value, 0); + Min = (int)Math.Min((1 + NormalizeValue) * Value, 0); } } @@ -40,13 +32,13 @@ public void NormalizeMinValue() /// public void NormalizeMaxValue() { - if (Modifier.Value > 0) + if (Value > 0) { - Max = (int)Math.Max(Math.Max(Modifier.Value + 1, (1 + NormalizeValue) * Modifier.Value), 0); + Max = (int)Math.Max(Math.Max(Value + 1, (1 + NormalizeValue) * Value), 0); } else { - Max = (int)Math.Min(Math.Max(Modifier.Value + 1, (1 - NormalizeValue) * Modifier.Value), 0); + Max = (int)Math.Min(Math.Max(Value + 1, (1 - NormalizeValue) * Value), 0); } } @@ -55,8 +47,8 @@ public void NormalizeMaxValue() /// public void SetExactValue() { - Min = (int)Modifier.Value; - Max = (int)Modifier.Value; + Min = (int)Value; + Max = (int)Value; } } } diff --git a/src/Sidekick.Apis.Poe/Trade/Requests/Filters/IStatFilter.cs b/src/Sidekick.Apis.Poe/Trade/Requests/Filters/IStatFilter.cs new file mode 100644 index 000000000..43a6a7856 --- /dev/null +++ b/src/Sidekick.Apis.Poe/Trade/Requests/Filters/IStatFilter.cs @@ -0,0 +1,6 @@ +namespace Sidekick.Apis.Poe.Trade.Requests.Filters; + +public interface IStatFilter +{ + +} diff --git a/src/Sidekick.Apis.Poe/Trade/Requests/Filters/SearchFilterValue.cs b/src/Sidekick.Apis.Poe/Trade/Requests/Filters/SearchFilterValue.cs index cd4ec3a6b..431183f6c 100644 --- a/src/Sidekick.Apis.Poe/Trade/Requests/Filters/SearchFilterValue.cs +++ b/src/Sidekick.Apis.Poe/Trade/Requests/Filters/SearchFilterValue.cs @@ -2,7 +2,7 @@ namespace Sidekick.Apis.Poe.Trade.Requests.Filters; -internal class SearchFilterValue +internal class SearchFilterValue : IStatFilter { internal SearchFilterValue() { @@ -27,12 +27,6 @@ public SearchFilterValue(ModifierFilter filter) Max = filter.Max; } - public SearchFilterValue(PseudoModifierFilter filter) - { - Min = filter.Min; - Max = filter.Max; - } - public object? Option { get; set; } public decimal? Min { get; set; } diff --git a/src/Sidekick.Apis.Poe/Trade/Requests/Filters/StatFilters.cs b/src/Sidekick.Apis.Poe/Trade/Requests/Filters/StatFilters.cs index b4ed7b255..3c16971be 100644 --- a/src/Sidekick.Apis.Poe/Trade/Requests/Filters/StatFilters.cs +++ b/src/Sidekick.Apis.Poe/Trade/Requests/Filters/StatFilters.cs @@ -3,7 +3,7 @@ namespace Sidekick.Apis.Poe.Trade.Requests.Filters internal class StatFilters { public string? Id { get; set; } - public SearchFilterValue? Value { get; set; } + public IStatFilter? Value { get; set; } public bool Disabled => false; } } diff --git a/src/Sidekick.Apis.Poe/Trade/Requests/Filters/StatType.cs b/src/Sidekick.Apis.Poe/Trade/Requests/Filters/StatType.cs index daca89b22..9bff78062 100644 --- a/src/Sidekick.Apis.Poe/Trade/Requests/Filters/StatType.cs +++ b/src/Sidekick.Apis.Poe/Trade/Requests/Filters/StatType.cs @@ -9,4 +9,7 @@ internal enum StatType [EnumValue("count")] Count, + + [EnumValue("weight")] + WeightedSum, } diff --git a/src/Sidekick.Apis.Poe/Trade/Requests/Filters/WeightedStatFilter.cs b/src/Sidekick.Apis.Poe/Trade/Requests/Filters/WeightedStatFilter.cs new file mode 100644 index 000000000..eb18eff07 --- /dev/null +++ b/src/Sidekick.Apis.Poe/Trade/Requests/Filters/WeightedStatFilter.cs @@ -0,0 +1,6 @@ +namespace Sidekick.Apis.Poe.Trade.Requests.Filters; + +internal class WeightedStatFilter : IStatFilter +{ + public double? Weight { get; set; } +} diff --git a/src/Sidekick.Apis.Poe/Trade/TradeFilterService.cs b/src/Sidekick.Apis.Poe/Trade/TradeFilterService.cs index 6a21efb02..9f3ade2ed 100644 --- a/src/Sidekick.Apis.Poe/Trade/TradeFilterService.cs +++ b/src/Sidekick.Apis.Poe/Trade/TradeFilterService.cs @@ -37,7 +37,13 @@ public IEnumerable GetPseudoModifierFilters(Item item) foreach (var modifier in item.PseudoModifiers) { - yield return new PseudoModifierFilter(modifier); + yield return new PseudoModifierFilter() + { + Text = modifier.Text, + Checked = false, + Value = modifier.Value, + Modifiers = modifier.Modifiers, + }; } } diff --git a/src/Sidekick.Apis.Poe/Trade/TradeSearchService.cs b/src/Sidekick.Apis.Poe/Trade/TradeSearchService.cs index 3a86b23da..4ba1f63ce 100644 --- a/src/Sidekick.Apis.Poe/Trade/TradeSearchService.cs +++ b/src/Sidekick.Apis.Poe/Trade/TradeSearchService.cs @@ -108,8 +108,8 @@ public async Task> Search(Item item, PropertyFilters? } SetModifierFilters(query.Stats, modifierFilters); - SetPseudoModifierFilters(query.Stats, pseudoFilters); SetSocketFilters(item, query.Filters); + query.Stats.AddRange(GetPseudoStatFilterGroups(pseudoFilters)); if (propertyFilters != null) { @@ -581,22 +581,14 @@ private static void SetModifierFilters(List stats, List stats, List? pseudoFilters) + private List GetPseudoStatFilterGroups(List? pseudoFilters) { if (pseudoFilters == null) { - return; + return []; } - var andGroup = stats.FirstOrDefault(x => x.Type == StatType.And); - if (andGroup == null) - { - andGroup = new StatFilterGroup() - { - Type = StatType.And, - }; - stats.Add(andGroup); - } + var stats = new List(); foreach (var filter in pseudoFilters) { @@ -605,12 +597,30 @@ private static void SetPseudoModifierFilters(List stats, List

0) + { + stats.Add(group); + } } + + return stats; } private static void SetSocketFilters(Item item, SearchFilters filters) @@ -640,13 +650,7 @@ public async Task> GetResults(GameType game, string queryId, Lis { logger.LogInformation($"[Trade API] Fetching Trade API Listings from Query {queryId}."); - var pseudo = string.Empty; - if (pseudoFilters?.Count > 0) - { - pseudo = string.Join("", pseudoFilters.Select(x => $"&pseudos[]={x.Modifier.Id}")); - } - - var response = await poeTradeClient.HttpClient.GetAsync(await GetBaseApiUrl(game) + "fetch/" + string.Join(",", ids) + "?query=" + queryId + pseudo); + var response = await poeTradeClient.HttpClient.GetAsync(await GetBaseApiUrl(game) + "fetch/" + string.Join(",", ids) + "?query=" + queryId); if (!response.IsSuccessStatusCode) { return new(); @@ -750,8 +754,6 @@ private TradeItem GetItem(GameType game, Result result) item.ModifierLines.AddRange(ParseModifierLines(result.Item?.ScourgeMods, result.Item?.Extended?.Mods?.Scourge, ParseHash(result.Item?.Extended?.Hashes?.Scourge))); - item.PseudoModifiers.AddRange(ParsePseudoModifiers(result.Item?.PseudoMods, result.Item?.Extended?.Mods?.Pseudo, ParseHash(result.Item?.Extended?.Hashes?.Pseudo))); - item.ModifierLines = item.ModifierLines.OrderBy(x => item.Text.IndexOf(x.Text, StringComparison.InvariantCultureIgnoreCase)).ToList(); return item; @@ -898,34 +900,6 @@ private IEnumerable ParseModifierLines(List? texts, List ParsePseudoModifiers(List? texts, List? mods, List? hashes) - { - if (texts == null || mods == null || hashes == null) - { - yield break; - } - - foreach (var hash in hashes) - { - var id = hash.Value; - if (id == null) - { - continue; - } - - var text = texts.FirstOrDefault(x => modifierProvider.IsMatch(id, x)); - if (text == null) - { - continue; - } - - yield return new PseudoModifier(text: text) - { - Id = id, - }; - } - } - private static IEnumerable ParseSockets(List? sockets) { if (sockets == null) diff --git a/src/Sidekick.Common.Ui/wwwroot/css/app.css b/src/Sidekick.Common.Ui/wwwroot/css/app.css index 30874af5a..431ac92c4 100644 --- a/src/Sidekick.Common.Ui/wwwroot/css/app.css +++ b/src/Sidekick.Common.Ui/wwwroot/css/app.css @@ -3331,10 +3331,6 @@ video { padding-right: 0.5rem; } -.pt-2 { - padding-top: 0.5rem; -} - .pt-\[4px\] { padding-top: 4px; } diff --git a/src/Sidekick.Common/Game/Items/PseudoModifier.cs b/src/Sidekick.Common/Game/Items/PseudoModifier.cs index 2f9697903..7592b3e62 100644 --- a/src/Sidekick.Common/Game/Items/PseudoModifier.cs +++ b/src/Sidekick.Common/Game/Items/PseudoModifier.cs @@ -1,15 +1,13 @@ namespace Sidekick.Common.Game.Items; -public class PseudoModifier(string text) +public class PseudoModifier { - public string? Id { get; init; } + ///

+ /// Gets or sets a dictionary of modifiers. The key represents the modifier id from the API. The value represents its weighted sum value. + /// + public Dictionary Modifiers { get; init; } = []; - public string Text { get; set; } = text; + public required string Text { get; set; } public double Value { get; set; } - - /// - /// Gets a value indicating whether this modifier has value. - /// - public bool HasValue => Value != 0; } diff --git a/src/Sidekick.Modules.Trade/Components/Filters/PseudoFilterComponent.razor b/src/Sidekick.Modules.Trade/Components/Filters/PseudoFilterComponent.razor index 2cb41e6e5..e715387fb 100644 --- a/src/Sidekick.Modules.Trade/Components/Filters/PseudoFilterComponent.razor +++ b/src/Sidekick.Modules.Trade/Components/Filters/PseudoFilterComponent.razor @@ -10,7 +10,7 @@ NoMargin="true" ValueChanged="CheckedChanged"> @Filter.Modifier.Text + Category="ModifierCategory.Pseudo">@Filter.Text @@ -19,7 +19,7 @@
- @if ((Filter.Checked ?? false) && Filter.Modifier.HasValue) + @if ((Filter.Checked ?? false) && Filter.Value != 0) { } From 67b5aa27e4140a08cb8e24aa3847e17c5223b1f8 Mon Sep 17 00:00:00 2001 From: leMicin Date: Sat, 28 Dec 2024 23:29:33 -0500 Subject: [PATCH 2/7] Pseudo parser now sends all the modifiers as expected. Though we get a query too complex error --- .../Clients/PoeTradeHandler.cs | 41 +- src/Sidekick.Apis.Poe/Parser/ItemParser.cs | 6 +- .../ElementalResistanceDefinition.cs | 2 +- .../Pseudo/IPseudoParser.cs} | 4 +- .../Parser/Pseudo/PseudoDefinition.cs | 88 +++ .../Pseudo/PseudoModifierDefinition.cs | 2 +- .../Parser/Pseudo/PseudoParser.cs | 573 ++++++++++++++++ .../{ => Parser}/Pseudo/PseudoPattern.cs | 2 +- .../Pseudo/PseudoDefinition.cs | 36 -- .../Pseudo/PseudoModifierProvider.cs | 610 ------------------ src/Sidekick.Apis.Poe/StartupExtensions.cs | 4 +- 11 files changed, 692 insertions(+), 676 deletions(-) rename src/Sidekick.Apis.Poe/{ => Parser}/Pseudo/Definitions/ElementalResistanceDefinition.cs (91%) rename src/Sidekick.Apis.Poe/{Pseudo/IPseudoModifierProvider.cs => Parser/Pseudo/IPseudoParser.cs} (59%) create mode 100644 src/Sidekick.Apis.Poe/Parser/Pseudo/PseudoDefinition.cs rename src/Sidekick.Apis.Poe/{ => Parser}/Pseudo/PseudoModifierDefinition.cs (91%) create mode 100644 src/Sidekick.Apis.Poe/Parser/Pseudo/PseudoParser.cs rename src/Sidekick.Apis.Poe/{ => Parser}/Pseudo/PseudoPattern.cs (83%) delete mode 100644 src/Sidekick.Apis.Poe/Pseudo/PseudoDefinition.cs delete mode 100644 src/Sidekick.Apis.Poe/Pseudo/PseudoModifierProvider.cs diff --git a/src/Sidekick.Apis.Poe/Clients/PoeTradeHandler.cs b/src/Sidekick.Apis.Poe/Clients/PoeTradeHandler.cs index 4bfd1e332..7f81c15d9 100644 --- a/src/Sidekick.Apis.Poe/Clients/PoeTradeHandler.cs +++ b/src/Sidekick.Apis.Poe/Clients/PoeTradeHandler.cs @@ -53,31 +53,11 @@ protected override async Task SendAsync(HttpRequestMessage } } - var errorResponse = await ParseErrorResponse(response); - if (response.StatusCode == HttpStatusCode.BadRequest) - { - if (errorResponse?.Error?.Message?.StartsWith("Query is too complex.") ?? false) - { - throw new SidekickException("Query is too complex.", "The official trade website has limit on complex queries. Sidekick cannot change this.", "Use the official website to search for your current item.", errorResponse.Error?.Message ?? string.Empty); - } - } - - if (response.StatusCode == HttpStatusCode.TooManyRequests) - { - throw new SidekickException("Rate limit exceeded.", "The official trade website has a rate limit to avoid spam. Sidekick cannot change this.", errorResponse?.Error?.Message ?? string.Empty); - } - if (response.StatusCode == HttpStatusCode.Moved || response.StatusCode == HttpStatusCode.Redirect || response.StatusCode == HttpStatusCode.RedirectKeepVerb) { response = await HandleRedirect(request, response, cancellationToken); } - // Sidekick does not support authentication yet. - if (response.StatusCode == HttpStatusCode.Unauthorized) - { - throw new SidekickException("Sidekick failed to communicate with the trade API.", "The trade website requires authentication, which Sidekick does not support currently.", "Try using a different game language and/or force to search using English only in the settings."); - } - // 403 probably means a cloudflare issue. if (response.StatusCode == HttpStatusCode.Forbidden) { @@ -116,6 +96,27 @@ protected override async Task SendAsync(HttpRequestMessage logger.LogWarning("[PoeTradeHandler] Query Failed: {responseCode} {responseMessage}", response.StatusCode, content); logger.LogWarning("[PoeTradeHandler] Uri: {uri}", request.RequestUri); logger.LogWarning("[PoeTradeHandler] Body: {uri}", body); + + var errorResponse = await ParseErrorResponse(response); + if (response.StatusCode == HttpStatusCode.BadRequest) + { + if (errorResponse?.Error?.Message?.StartsWith("Query is too complex.") ?? false) + { + throw new SidekickException("Query is too complex.", "The official trade website has limit on complex queries. Sidekick cannot change this.", "Use the official website to search for your current item."); + } + } + + if (response.StatusCode == HttpStatusCode.TooManyRequests) + { + throw new SidekickException("Rate limit exceeded.", "The official trade website has a rate limit to avoid spam. Sidekick cannot change this.", errorResponse?.Error?.Message ?? string.Empty); + } + + // Sidekick does not support authentication yet. + if (response.StatusCode == HttpStatusCode.Unauthorized) + { + throw new SidekickException("Sidekick failed to communicate with the trade API.", "The trade website requires authentication, which Sidekick does not support currently.", "Try using a different game language and/or force to search using English only in the settings."); + } + throw new ApiErrorException(); } diff --git a/src/Sidekick.Apis.Poe/Parser/ItemParser.cs b/src/Sidekick.Apis.Poe/Parser/ItemParser.cs index 969c11a87..7de6b29ff 100644 --- a/src/Sidekick.Apis.Poe/Parser/ItemParser.cs +++ b/src/Sidekick.Apis.Poe/Parser/ItemParser.cs @@ -7,8 +7,8 @@ using Sidekick.Apis.Poe.Parser.Modifiers; using Sidekick.Apis.Poe.Parser.Patterns; using Sidekick.Apis.Poe.Parser.Properties; +using Sidekick.Apis.Poe.Parser.Pseudo; using Sidekick.Apis.Poe.Parser.Sockets; -using Sidekick.Apis.Poe.Pseudo; using Sidekick.Common.Exceptions; using Sidekick.Common.Game.Items; @@ -19,7 +19,7 @@ public class ItemParser ILogger logger, IMetadataParser metadataProvider, IModifierParser modifierParser, - IPseudoModifierProvider pseudoModifierProvider, + IPseudoParser pseudoParser, IParserPatterns patterns, ClusterJewelParser clusterJewelParser, IInvariantMetadataProvider invariantMetadataProvider, @@ -71,7 +71,7 @@ public Item ParseItem(string itemText) var sockets = socketParser.Parse(parsingItem); var modifierLines = ParseModifiers(parsingItem); var properties = propertyParser.Parse(parsingItem, modifierLines); - var pseudoModifiers = pseudoModifierProvider.Parse(modifierLines); + var pseudoModifiers = pseudoParser.Parse(modifierLines); var item = new Item(metadata: metadata, invariant: invariant, header: header, diff --git a/src/Sidekick.Apis.Poe/Pseudo/Definitions/ElementalResistanceDefinition.cs b/src/Sidekick.Apis.Poe/Parser/Pseudo/Definitions/ElementalResistanceDefinition.cs similarity index 91% rename from src/Sidekick.Apis.Poe/Pseudo/Definitions/ElementalResistanceDefinition.cs rename to src/Sidekick.Apis.Poe/Parser/Pseudo/Definitions/ElementalResistanceDefinition.cs index a39df3438..2e224f953 100644 --- a/src/Sidekick.Apis.Poe/Pseudo/Definitions/ElementalResistanceDefinition.cs +++ b/src/Sidekick.Apis.Poe/Parser/Pseudo/Definitions/ElementalResistanceDefinition.cs @@ -1,6 +1,6 @@ using System.Text.RegularExpressions; -namespace Sidekick.Apis.Poe.Pseudo.Definitions; +namespace Sidekick.Apis.Poe.Parser.Pseudo.Definitions; public class ElementalResistanceDefinition : PseudoDefinition { diff --git a/src/Sidekick.Apis.Poe/Pseudo/IPseudoModifierProvider.cs b/src/Sidekick.Apis.Poe/Parser/Pseudo/IPseudoParser.cs similarity index 59% rename from src/Sidekick.Apis.Poe/Pseudo/IPseudoModifierProvider.cs rename to src/Sidekick.Apis.Poe/Parser/Pseudo/IPseudoParser.cs index 6f7bfff1c..ba64dcc71 100644 --- a/src/Sidekick.Apis.Poe/Pseudo/IPseudoModifierProvider.cs +++ b/src/Sidekick.Apis.Poe/Parser/Pseudo/IPseudoParser.cs @@ -1,9 +1,9 @@ using Sidekick.Common.Game.Items; using Sidekick.Common.Initialization; -namespace Sidekick.Apis.Poe.Pseudo +namespace Sidekick.Apis.Poe.Parser.Pseudo { - public interface IPseudoModifierProvider : IInitializableService + public interface IPseudoParser : IInitializableService { List Parse(List modifiers); } diff --git a/src/Sidekick.Apis.Poe/Parser/Pseudo/PseudoDefinition.cs b/src/Sidekick.Apis.Poe/Parser/Pseudo/PseudoDefinition.cs new file mode 100644 index 000000000..29d5536cf --- /dev/null +++ b/src/Sidekick.Apis.Poe/Parser/Pseudo/PseudoDefinition.cs @@ -0,0 +1,88 @@ +using System.Text.RegularExpressions; +using Sidekick.Apis.Poe.Modifiers.Models; +using Sidekick.Common.Game.Items; + +namespace Sidekick.Apis.Poe.Parser.Pseudo; + +public abstract class PseudoDefinition +{ + internal void AddModifierIfMatch(ApiModifier entry) + { + if (Exception != null && Exception.IsMatch(entry.Text)) + { + return; + } + + foreach (var pattern in Patterns) + { + if (entry.Id == null || entry.Type == null || entry.Text == null) + { + continue; + } + + if (pattern.Pattern.IsMatch(entry.Text)) + { + Modifiers.Add(new PseudoModifierDefinition(entry.Id, entry.Type, entry.Text, pattern.Multiplier)); + } + } + } + + internal PseudoModifier? Parse(List itemModifierLines) + { + var hasPseudo = false; + foreach (var definitionModifier in Modifiers) + { + foreach (var itemModifierLine in itemModifierLines) + { + foreach (var modifier in itemModifierLine.Modifiers) + { + hasPseudo = hasPseudo || modifier.Id == definitionModifier.Id; + if (hasPseudo) break; + } + + if (hasPseudo) break; + } + + if (hasPseudo) break; + } + + if (!hasPseudo) + { + return null; + } + + var result = new PseudoModifier() + { + Text = Text, + }; + + foreach (var definitionModifier in Modifiers) + { + result.Modifiers.Add(definitionModifier.Id, definitionModifier.Multiplier); + } + + foreach (var itemModifierLine in itemModifierLines) + { + foreach (var definitionModifier in Modifiers) + { + if (itemModifierLine.Modifiers.All(itemModifier => definitionModifier.Id != itemModifier.Id)) + { + continue; + } + + result.Value += itemModifierLine.Values.Average() * definitionModifier.Multiplier; + break; + } + } + + return result; + } + + protected abstract List Patterns { get; } + + protected abstract Regex? Exception { get; } + + public string Text => Modifiers.FirstOrDefault()?.Text ?? string.Empty; + + public List Modifiers { get; set; } = new(); +} diff --git a/src/Sidekick.Apis.Poe/Pseudo/PseudoModifierDefinition.cs b/src/Sidekick.Apis.Poe/Parser/Pseudo/PseudoModifierDefinition.cs similarity index 91% rename from src/Sidekick.Apis.Poe/Pseudo/PseudoModifierDefinition.cs rename to src/Sidekick.Apis.Poe/Parser/Pseudo/PseudoModifierDefinition.cs index 3c0dcf70b..3ff398e53 100644 --- a/src/Sidekick.Apis.Poe/Pseudo/PseudoModifierDefinition.cs +++ b/src/Sidekick.Apis.Poe/Parser/Pseudo/PseudoModifierDefinition.cs @@ -1,4 +1,4 @@ -namespace Sidekick.Apis.Poe.Pseudo +namespace Sidekick.Apis.Poe.Parser.Pseudo { public class PseudoModifierDefinition ( diff --git a/src/Sidekick.Apis.Poe/Parser/Pseudo/PseudoParser.cs b/src/Sidekick.Apis.Poe/Parser/Pseudo/PseudoParser.cs new file mode 100644 index 000000000..b25f7b83f --- /dev/null +++ b/src/Sidekick.Apis.Poe/Parser/Pseudo/PseudoParser.cs @@ -0,0 +1,573 @@ +using System.Text.RegularExpressions; +using Sidekick.Apis.Poe.Modifiers; +using Sidekick.Apis.Poe.Parser.Pseudo.Definitions; +using Sidekick.Common.Game.Items; + +namespace Sidekick.Apis.Poe.Parser.Pseudo; + +public class PseudoParser(IInvariantModifierProvider invariantModifierProvider) : IPseudoParser +{ + private readonly Regex parseHashPattern = new("\\#"); + + private List Definitions { get; } = new(); + + /// + public int Priority => 200; + + /// + public async Task Initialize() + { + Definitions.Clear(); + Definitions.AddRange([ + new ElementalResistanceDefinition(), + ]); + + var categories = await invariantModifierProvider.GetList(); + foreach (var category in categories) + { + var first = category.Entries.FirstOrDefault(); + if (first == null || first.Id.Split('.').First() == "pseudo") + { + continue; + } + + foreach (var entry in category.Entries) + { + foreach (var definition in Definitions) + { + definition.AddModifierIfMatch(entry); + } + } + } + + foreach (var definition in Definitions) + { + definition.Modifiers = definition.Modifiers.OrderBy(x => x.Type switch + { + "explicit" => 0, + "implicit" => 1, + "crafted" => 2, + "enchant" => 3, + "fractured" => 4, + "veiled" => 5, + "pseudo" => 6, + _ => 7, + }) + .ThenBy(x => x.Text) + .ToList(); + } + } + + /* + private static List InitializeGroups(List categories) + { + var pseudoCategory = categories + .FirstOrDefault(x => x.Entries.FirstOrDefault()?.Id?.Split('.').FirstOrDefault() == "pseudo"); + if (pseudoCategory == null) + { + return new(); + } + + var groups = new List() { + // +#% total to Cold Resistance + new PseudoPatternGroup( + apiCategory: pseudoCategory, + id: "pseudo.pseudo_total_cold_resistance", + exception: new Regex("Minions|Enemies|Totems"), + new PseudoPattern(new Regex("to Cold Resistance$")), + new PseudoPattern(new Regex("(?=.*Cold)to (?:Fire|Cold|Lightning|Chaos) and (?:Fire|Cold|Lightning|Chaos) Resistances$"))), + + // +#% total to Fire Resistance + new PseudoPatternGroup( + apiCategory: pseudoCategory, + id: "pseudo.pseudo_total_fire_resistance", + exception: new Regex("Minions|Enemies|Totems"), + new PseudoPattern(new Regex("to Fire Resistance$")), + new PseudoPattern(new Regex("(?=.*Fire)to (?:Fire|Cold|Lightning|Chaos) and (?:Fire|Cold|Lightning|Chaos) Resistances$"))), + + // +#% total to Lightning Resistance + new PseudoPatternGroup( + apiCategory: pseudoCategory, + id: "pseudo.pseudo_total_lightning_resistance", + exception: new Regex("Minions|Enemies|Totems"), + new PseudoPattern(new Regex("to Lightning Resistance$")), + new PseudoPattern(new Regex("(?=.*Lightning)to (?:Fire|Cold|Lightning|Chaos) and (?:Fire|Cold|Lightning|Chaos) Resistances$"))), + + // +#% total to Chaos Resistance + new PseudoPatternGroup( + apiCategory: pseudoCategory, + id: "pseudo.pseudo_total_chaos_resistance", + exception: new Regex("Minions|Enemies|Totems"), + new PseudoPattern(new Regex("to Chaos Resistance$")), + new PseudoPattern(new Regex("(?=.*Chaos)to (?:Fire|Cold|Lightning|Chaos) and (?:Fire|Cold|Lightning|Chaos) Resistances$"))), + + // +#% total to all Elemental Resistances + new PseudoPatternGroup( + apiCategory: pseudoCategory, + id: "pseudo.pseudo_total_all_elemental_resistances", + exception: new Regex("Minions|Enemies|Totems"), + new PseudoPattern(new Regex("to all Elemental Resistances$"))), + + // +#% total Elemental Resistance + new PseudoPatternGroup( + apiCategory: pseudoCategory, + id: "pseudo.pseudo_total_elemental_resistance", + exception: new Regex("Minions|Enemies|Totems"), + new PseudoPattern(new Regex("to (?:Fire|Cold|Lightning) Resistance$")), + new PseudoPattern(new Regex("to (?:Fire|Cold|Lightning) and (?:Fire|Cold|Lightning) Resistances$"), 2), + new PseudoPattern(new Regex("(?=.*Chaos)to (?:Fire|Cold|Lightning|Chaos) and (?:Fire|Cold|Lightning|Chaos) Resistances$")), + new PseudoPattern(new Regex("to all Elemental Resistances$"), 3)), + + // +#% total Resistance + new PseudoPatternGroup( + apiCategory: pseudoCategory, + id: "pseudo.pseudo_total_resistance", + exception: new Regex("Minions|Enemies|Totems"), + new PseudoPattern(new Regex("to (Fire|Cold|Lightning|Chaos) Resistance$")), + new PseudoPattern(new Regex("to (?:Fire|Cold|Lightning|Chaos) and (?:Fire|Cold|Lightning|Chaos) Resistances$"), 2), + new PseudoPattern(new Regex("to all Elemental Resistances$"), 3)), + + // # total Resistances + // pseudo.pseudo_count_resistances + + // +# total to Strength + new PseudoPatternGroup( + apiCategory: pseudoCategory, + id: "pseudo.pseudo_total_strength", + exception: new Regex("Passive"), + new PseudoPattern(new Regex("to Strength$")), + new PseudoPattern(new Regex("(?=.*Strength)to (?:Strength|Dexterity|Intelligence) and (?:Strength|Dexterity|Intelligence)$")), + new PseudoPattern(new Regex("to all Attributes$"))), + + // +# total to Dexterity + new PseudoPatternGroup( + apiCategory: pseudoCategory, + id: "pseudo.pseudo_total_dexterity", + exception: new Regex("Passive"), + new PseudoPattern(new Regex("to Dexterity$")), + new PseudoPattern(new Regex("(?=.*Dexterity)to (?:Strength|Dexterity|Intelligence) and (?:Strength|Dexterity|Intelligence)$")), + new PseudoPattern(new Regex("to all Attributes$"))), + + // +# total to Intelligence + new PseudoPatternGroup( + apiCategory: pseudoCategory, + id: "pseudo.pseudo_total_intelligence", + exception: new Regex("Passive"), + new PseudoPattern(new Regex("to Intelligence$")), + new PseudoPattern(new Regex("(?=.*Intelligence)to (?:Strength|Dexterity|Intelligence) and (?:Strength|Dexterity|Intelligence)$")), + new PseudoPattern(new Regex("to all Attributes$"))), + + // +# total to all Attributes + new PseudoPatternGroup( + apiCategory: pseudoCategory, + id: "pseudo.pseudo_total_all_attributes", + exception: new Regex("Passive"), + new PseudoPattern(new Regex("to all Attributes$"))), + + // +# total maximum Life + new PseudoPatternGroup( + apiCategory: pseudoCategory, + id: "pseudo.pseudo_total_life", + exception: new Regex("Zombies|Transformed"), + new PseudoPattern(new Regex("to maximum Life$")), + new PseudoPattern(new Regex("to Strength$"), 0.5), + new PseudoPattern(new Regex("(?=.*Strength)to (?:Strength|Dexterity|Intelligence) and (?:Strength|Dexterity|Intelligence)$"), 0.5), + new PseudoPattern(new Regex("to all Attributes$"), 0.5)), + + // +# total maximum Mana + new PseudoPatternGroup( + apiCategory: pseudoCategory, + id: "pseudo.pseudo_total_mana", + exception: new Regex("Transformed"), + new PseudoPattern(new Regex("to maximum Mana$")), + new PseudoPattern(new Regex("to Intelligence$"), 0.5), + new PseudoPattern(new Regex("(?=.*Intelligence)to (?:Strength|Dexterity|Intelligence) and (?:Strength|Dexterity|Intelligence)$"), 0.5), + new PseudoPattern(new Regex("to all Attributes$"), 0.5)), + + // +# total maximum Energy Shield + new PseudoPatternGroup( + apiCategory: pseudoCategory, + id: "pseudo.pseudo_total_energy_shield", + new PseudoPattern(new Regex("to maximum Energy Shield$"))), + + // #% total increased maximum Energy Shield + new PseudoPatternGroup( + apiCategory: pseudoCategory, + id: "pseudo.pseudo_increased_energy_shield", + new PseudoPattern(new Regex("% increased maximum Energy Shield$"))), + + // +#% total Attack Speed + new PseudoPatternGroup( + apiCategory: pseudoCategory, + id: "pseudo.pseudo_total_attack_speed", + new PseudoPattern(new Regex("^\\#% increased Attack Speed$"))), + + // +#% total Cast Speed + new PseudoPatternGroup( + apiCategory: pseudoCategory, + id: "pseudo.pseudo_total_cast_speed", + new PseudoPattern(new Regex("^\\#% increased Cast Speed$"))), + + // #% increased Movement Speed + new PseudoPatternGroup( + apiCategory: pseudoCategory, + id: "pseudo.pseudo_increased_movement_speed", + new PseudoPattern(new Regex("^\\#% increased Movement Speed$"))), + + // #% total increased Physical Damage + // pseudo.pseudo_increased_physical_damage + + // +#% Global Critical Strike Chance + // pseudo.pseudo_global_critical_strike_chance + + // +#% total Critical Strike Chance for Spells + // pseudo.pseudo_critical_strike_chance_for_spells + + // +#% Global Critical Strike Multiplier + // pseudo.pseudo_global_critical_strike_multiplier + + // Adds # to # Physical Damage + // pseudo.pseudo_adds_physical_damage + + // Adds # to # Lightning Damage + // pseudo.pseudo_adds_lightning_damage + + // Adds # to # Cold Damage + // pseudo.pseudo_adds_cold_damage + + // Adds # to # Fire Damage + // pseudo.pseudo_adds_fire_damage + + // Adds # to # Elemental Damage + // pseudo.pseudo_adds_elemental_damage + + // Adds # to # Chaos Damage + // pseudo.pseudo_adds_chaos_damage + + // Adds # to # Damage + // pseudo.pseudo_adds_damage + + // Adds # to # Physical Damage to Attacks + // pseudo.pseudo_adds_physical_damage_to_attacks + + // Adds # to # Lightning Damage to Attacks + // pseudo.pseudo_adds_lightning_damage_to_attacks + + // Adds # to # Cold Damage to Attacks + // pseudo.pseudo_adds_cold_damage_to_attacks + + // Adds # to # Fire Damage to Attacks + // pseudo.pseudo_adds_fire_damage_to_attacks + + // Adds # to # Elemental Damage to Attacks + // pseudo.pseudo_adds_elemental_damage_to_attacks + + // Adds # to # Chaos Damage to Attacks + // pseudo.pseudo_adds_chaos_damage_to_attacks + + // Adds # to # Damage to Attacks + // pseudo.pseudo_adds_damage_to_attacks + + // Adds # to # Physical Damage to Spells + // pseudo.pseudo_adds_physical_damage_to_spells + + // Adds # to # Lightning Damage to Spells + // pseudo.pseudo_adds_lightning_damage_to_spells + + // Adds # to # Cold Damage to Spells + // pseudo.pseudo_adds_cold_damage_to_spells + + // Adds # to # Fire Damage to Spells + // pseudo.pseudo_adds_fire_damage_to_spells + + // Adds # to # Elemental Damage to Spells + // pseudo.pseudo_adds_elemental_damage_to_spells + + // Adds # to # Chaos Damage to Spells + // pseudo.pseudo_adds_chaos_damage_to_spells + + // Adds # to # Damage to Spells + // pseudo.pseudo_adds_damage_to_spells + + // #% increased Elemental Damage + // pseudo.pseudo_increased_elemental_damage + + // #% increased Lightning Damage + // pseudo.pseudo_increased_lightning_damage + + // #% increased Cold Damage + // pseudo.pseudo_increased_cold_damage + + // #% increased Fire Damage + // pseudo.pseudo_increased_fire_damage + + // #% increased Spell Damage + // pseudo.pseudo_increased_spell_damage + + // #% increased Lightning Spell Damage + // pseudo.pseudo_increased_lightning_spell_damage + + // #% increased Cold Spell Damage + // pseudo.pseudo_increased_cold_spell_damage + + // #% increased Fire Spell Damage + // pseudo.pseudo_increased_fire_spell_damage + + // #% increased Lightning Damage with Attack Skills + // pseudo.pseudo_increased_lightning_damage_with_attack_skills + + // #% increased Cold Damage with Attack Skills + // pseudo.pseudo_increased_cold_damage_with_attack_skills + + // #% increased Fire Damage with Attack Skills + // pseudo.pseudo_increased_fire_damage_with_attack_skills + + // #% increased Elemental Damage with Attack Skills + // pseudo.pseudo_increased_elemental_damage_with_attack_skills + + // #% increased Rarity of Items found + // pseudo.pseudo_increased_rarity + + // #% increased Burning Damage + // pseudo.pseudo_increased_burning_damage + + // # Life Regenerated per Second + // pseudo.pseudo_total_life_regen + + // #% of Life Regenerated per Second + // pseudo.pseudo_percent_life_regen + + // #% of Physical Attack Damage Leeched as Life + // pseudo.pseudo_physical_attack_damage_leeched_as_life + + // #% of Physical Attack Damage Leeched as Mana + // pseudo.pseudo_physical_attack_damage_leeched_as_mana + + // #% increased Mana Regeneration Rate + // pseudo.pseudo_increased_mana_regen + + // +# total to Level of Socketed Gems + // pseudo.pseudo_total_additional_gem_levels + + // +# total to Level of Socketed Elemental Gems + // pseudo.pseudo_total_additional_elemental_gem_levels + + // +# total to Level of Socketed Fire Gems + // pseudo.pseudo_total_additional_fire_gem_levels + + // +# total to Level of Socketed Cold Gems + // pseudo.pseudo_total_additional_cold_gem_levels + + // +# total to Level of Socketed Lightning Gems + // pseudo.pseudo_total_additional_lightning_gem_levels + + // +# total to Level of Socketed Chaos Gems + // pseudo.pseudo_total_additional_chaos_gem_levels + + // +# total to Level of Socketed Spell Gems + // pseudo.pseudo_total_additional_spell_gem_levels + + // +# total to Level of Socketed Projectile Gems + // pseudo.pseudo_total_additional_projectile_gem_levels + + // +# total to Level of Socketed Bow Gems + // pseudo.pseudo_total_additional_bow_gem_levels + + // +# total to Level of Socketed Melee Gems + // pseudo.pseudo_total_additional_melee_gem_levels + + // +# total to Level of Socketed Minion Gems + // pseudo.pseudo_total_additional_minion_gem_levels + + // +# total to Level of Socketed Strength Gems + // pseudo.pseudo_total_additional_strength_gem_levels + + // +# total to Level of Socketed Dexterity Gems + // pseudo.pseudo_total_additional_dexterity_gem_levels + + // +# total to Level of Socketed Intelligence Gems + // pseudo.pseudo_total_additional_intelligence_gem_levels + + // +# total to Level of Socketed Aura Gems + // pseudo.pseudo_total_additional_aura_gem_levels + + // +# total to Level of Socketed Movement Gems + // pseudo.pseudo_total_additional_movement_gem_levels + + // +# total to Level of Socketed Curse Gems + // pseudo.pseudo_total_additional_curse_gem_levels + + // +# total to Level of Socketed Vaal Gems + // pseudo.pseudo_total_additional_vaal_gem_levels + + // +# total to Level of Socketed Support Gems + // pseudo.pseudo_total_additional_support_gem_levels + + // +# total to Level of Socketed Skill Gems + // pseudo.pseudo_total_additional_skill_gem_levels + + // +# total to Level of Socketed Warcry Gems + // pseudo.pseudo_total_additional_warcry_gem_levels + + // +# total to Level of Socketed Golem Gems + // pseudo.pseudo_total_additional_golem_gem_levels + + // # Implicit Modifiers + // pseudo.pseudo_number_of_implicit_mods + + // # Prefix Modifiers + // pseudo.pseudo_number_of_prefix_mods + + // # Suffix Modifiers + // pseudo.pseudo_number_of_suffix_mods + + // # Modifiers + // pseudo.pseudo_number_of_affix_mods + + // # Crafted Prefix Modifiers + // pseudo.pseudo_number_of_crafted_prefix_mods + + // # Crafted Suffix Modifiers + // pseudo.pseudo_number_of_crafted_suffix_mods + + // # Crafted Modifiers + // pseudo.pseudo_number_of_crafted_mods + + // # Empty Prefix Modifiers + // pseudo.pseudo_number_of_empty_prefix_mods + + // # Empty Suffix Modifiers + // pseudo.pseudo_number_of_empty_suffix_mods + + // # Empty Modifiers + // pseudo.pseudo_number_of_empty_affix_mods + + // # Incubator Kills (Whispering) + // pseudo.pseudo_whispering_incubator_kills + + // # Incubator Kills (Fine) + // pseudo.pseudo_fine_incubator_kills + + // # Incubator Kills (Singular) + // pseudo.pseudo_singular_incubator_kills + + // # Incubator Kills (Cartographer's) + // pseudo.pseudo_cartographers_incubator_kills + + // # Incubator Kills (Otherwordly) + // pseudo.pseudo_otherworldly_incubator_kills + + // # Incubator Kills (Abyssal) + // pseudo.pseudo_abyssal_incubator_kills + + // # Incubator Kills (Fragmented) + // pseudo.pseudo_fragmented_incubator_kills + + // # Incubator Kills (Skittering) + // pseudo.pseudo_skittering_incubator_kills + + // # Incubator Kills (Infused) + // pseudo.pseudo_infused_incubator_kills + + // # Incubator Kills (Fossilised) + // pseudo.pseudo_fossilised_incubator_kills + + // # Incubator Kills (Decadent) + // pseudo.pseudo_decadent_incubator_kills + + // # Incubator Kills (Diviner's) + // pseudo.pseudo_diviners_incubator_kills + + // # Incubator Kills (Primal) + // pseudo.pseudo_primal_incubator_kills + + // # Incubator Kills (Enchanted) + // pseudo.pseudo_enchanted_incubator_kills + + // # Incubator Kills (Geomancer's) + // pseudo.pseudo_geomancers_incubator_kills + + // # Incubator Kills (Ornate) + // pseudo.pseudo_ornate_incubator_kills + + // # Incubator Kills (Time-Lost) + // pseudo.pseudo_timelost_incubator_kills + + // # Incubator Kills (Celestial Armoursmith's) + // pseudo.pseudo_celestial_armoursmiths_incubator_kills + + // # Incubator Kills (Celestial Blacksmith's) + // pseudo.pseudo_celestial_blacksmiths_incubator_kills + + // # Incubator Kills (Celestial Jeweller's) + // pseudo.pseudo_celestial_jewellers_incubator_kills + + // # Incubator Kills (Eldritch) + // pseudo.pseudo_eldritch_incubator_kills + + // # Incubator Kills (Obscured) + // pseudo.pseudo_obscured_incubator_kills + + // # Incubator Kills (Foreboding) + // pseudo.pseudo_foreboding_incubator_kills + + // # Incubator Kills (Thaumaturge's) + // pseudo.pseudo_thaumaturges_incubator_kills + + // # Incubator Kills (Mysterious) + // pseudo.pseudo_mysterious_incubator_kills + + // # Incubator Kills (Gemcutter's) + // pseudo.pseudo_gemcutters_incubator_kills + + // # Incubator Kills (Feral) + // pseudo.pseudo_feral_incubator_kills + + // # Fractured Modifiers + // pseudo.pseudo_number_of_fractured_mods + + // +#% Quality to Elemental Damage Modifiers + // pseudo.pseudo_jewellery_elemental_quality + + // +#% Quality to Caster Modifiers + // pseudo.pseudo_jewellery_caster_quality + + // +#% Quality to Attack Modifiers + // pseudo.pseudo_jewellery_attack_quality + + // +#% Quality to Defence Modifiers + // pseudo.pseudo_jewellery_defense_quality + + // +#% Quality to Life and Mana Modifiers + // pseudo.pseudo_jewellery_resource_quality + + // +#% Quality to Resistance Modifiers + // pseudo.pseudo_jewellery_resistance_quality + + // +#% Quality to Attribute Modifiers + // pseudo.pseudo_jewellery_attribute_quality + }; + + return groups; + } + */ + + public List Parse(List lines) + { + var results = new List(); + + foreach (var definition in Definitions) + { + var result = definition.Parse(lines); + if (result != null) results.Add(result); + } + + results.ForEach(x => + { + x.Value = (int)x.Value; + x.Text = parseHashPattern.Replace(x.Text, ((int)x.Value).ToString(), 1); + }); + + return results; + } +} diff --git a/src/Sidekick.Apis.Poe/Pseudo/PseudoPattern.cs b/src/Sidekick.Apis.Poe/Parser/Pseudo/PseudoPattern.cs similarity index 83% rename from src/Sidekick.Apis.Poe/Pseudo/PseudoPattern.cs rename to src/Sidekick.Apis.Poe/Parser/Pseudo/PseudoPattern.cs index 190be2a5f..26d3b4cd6 100644 --- a/src/Sidekick.Apis.Poe/Pseudo/PseudoPattern.cs +++ b/src/Sidekick.Apis.Poe/Parser/Pseudo/PseudoPattern.cs @@ -1,6 +1,6 @@ using System.Text.RegularExpressions; -namespace Sidekick.Apis.Poe.Pseudo; +namespace Sidekick.Apis.Poe.Parser.Pseudo; public class PseudoPattern ( diff --git a/src/Sidekick.Apis.Poe/Pseudo/PseudoDefinition.cs b/src/Sidekick.Apis.Poe/Pseudo/PseudoDefinition.cs deleted file mode 100644 index e3349180a..000000000 --- a/src/Sidekick.Apis.Poe/Pseudo/PseudoDefinition.cs +++ /dev/null @@ -1,36 +0,0 @@ -using System.Text.RegularExpressions; -using Sidekick.Apis.Poe.Modifiers.Models; - -namespace Sidekick.Apis.Poe.Pseudo; - -public abstract class PseudoDefinition -{ - public void AddModifierIfMatch(ApiModifier entry) - { - if (Exception != null && Exception.IsMatch(entry.Text)) - { - return; - } - - foreach (var pattern in Patterns) - { - if (entry.Id == null || entry.Type == null || entry.Text == null) - { - continue; - } - - if (pattern.Pattern.IsMatch(entry.Text)) - { - Modifiers.Add(new PseudoModifierDefinition(entry.Id, entry.Type, entry.Text, pattern.Multiplier)); - } - } - } - - protected abstract List Patterns { get; } - - protected abstract Regex? Exception { get; } - - public string Text => Modifiers.FirstOrDefault()?.Text ?? string.Empty; - - public List Modifiers { get; set; } = new(); -} diff --git a/src/Sidekick.Apis.Poe/Pseudo/PseudoModifierProvider.cs b/src/Sidekick.Apis.Poe/Pseudo/PseudoModifierProvider.cs deleted file mode 100644 index 068f678ae..000000000 --- a/src/Sidekick.Apis.Poe/Pseudo/PseudoModifierProvider.cs +++ /dev/null @@ -1,610 +0,0 @@ -using System.Text.RegularExpressions; -using Sidekick.Apis.Poe.Modifiers; -using Sidekick.Apis.Poe.Pseudo.Definitions; -using Sidekick.Common.Game.Items; - -namespace Sidekick.Apis.Poe.Pseudo -{ - public class PseudoModifierProvider(IInvariantModifierProvider invariantModifierProvider) : IPseudoModifierProvider - { - private readonly Regex parseHashPattern = new("\\#"); - - private List Definitions { get; } = new(); - - /// - public int Priority => 200; - - /// - public async Task Initialize() - { - Definitions.Clear(); - Definitions.AddRange([ - new ElementalResistanceDefinition(), - ]); - - var categories = await invariantModifierProvider.GetList(); - foreach (var category in categories) - { - var first = category.Entries.FirstOrDefault(); - if (first == null || first.Id.Split('.').First() == "pseudo") - { - continue; - } - - foreach (var entry in category.Entries) - { - foreach (var definition in Definitions) - { - definition.AddModifierIfMatch(entry); - } - } - } - - foreach (var definition in Definitions) - { - definition.Modifiers = definition.Modifiers.OrderBy(x => x.Type switch - { - "explicit" => 0, - "implicit" => 1, - "crafted" => 2, - "enchant" => 3, - "fractured" => 4, - "veiled" => 5, - "pseudo" => 6, - _ => 7, - }) - .ThenBy(x => x.Text) - .ToList(); - } - } - - /* - private static List InitializeGroups(List categories) - { - var pseudoCategory = categories - .FirstOrDefault(x => x.Entries.FirstOrDefault()?.Id?.Split('.').FirstOrDefault() == "pseudo"); - if (pseudoCategory == null) - { - return new(); - } - - var groups = new List() { - // +#% total to Cold Resistance - new PseudoPatternGroup( - apiCategory: pseudoCategory, - id: "pseudo.pseudo_total_cold_resistance", - exception: new Regex("Minions|Enemies|Totems"), - new PseudoPattern(new Regex("to Cold Resistance$")), - new PseudoPattern(new Regex("(?=.*Cold)to (?:Fire|Cold|Lightning|Chaos) and (?:Fire|Cold|Lightning|Chaos) Resistances$"))), - - // +#% total to Fire Resistance - new PseudoPatternGroup( - apiCategory: pseudoCategory, - id: "pseudo.pseudo_total_fire_resistance", - exception: new Regex("Minions|Enemies|Totems"), - new PseudoPattern(new Regex("to Fire Resistance$")), - new PseudoPattern(new Regex("(?=.*Fire)to (?:Fire|Cold|Lightning|Chaos) and (?:Fire|Cold|Lightning|Chaos) Resistances$"))), - - // +#% total to Lightning Resistance - new PseudoPatternGroup( - apiCategory: pseudoCategory, - id: "pseudo.pseudo_total_lightning_resistance", - exception: new Regex("Minions|Enemies|Totems"), - new PseudoPattern(new Regex("to Lightning Resistance$")), - new PseudoPattern(new Regex("(?=.*Lightning)to (?:Fire|Cold|Lightning|Chaos) and (?:Fire|Cold|Lightning|Chaos) Resistances$"))), - - // +#% total to Chaos Resistance - new PseudoPatternGroup( - apiCategory: pseudoCategory, - id: "pseudo.pseudo_total_chaos_resistance", - exception: new Regex("Minions|Enemies|Totems"), - new PseudoPattern(new Regex("to Chaos Resistance$")), - new PseudoPattern(new Regex("(?=.*Chaos)to (?:Fire|Cold|Lightning|Chaos) and (?:Fire|Cold|Lightning|Chaos) Resistances$"))), - - // +#% total to all Elemental Resistances - new PseudoPatternGroup( - apiCategory: pseudoCategory, - id: "pseudo.pseudo_total_all_elemental_resistances", - exception: new Regex("Minions|Enemies|Totems"), - new PseudoPattern(new Regex("to all Elemental Resistances$"))), - - // +#% total Elemental Resistance - new PseudoPatternGroup( - apiCategory: pseudoCategory, - id: "pseudo.pseudo_total_elemental_resistance", - exception: new Regex("Minions|Enemies|Totems"), - new PseudoPattern(new Regex("to (?:Fire|Cold|Lightning) Resistance$")), - new PseudoPattern(new Regex("to (?:Fire|Cold|Lightning) and (?:Fire|Cold|Lightning) Resistances$"), 2), - new PseudoPattern(new Regex("(?=.*Chaos)to (?:Fire|Cold|Lightning|Chaos) and (?:Fire|Cold|Lightning|Chaos) Resistances$")), - new PseudoPattern(new Regex("to all Elemental Resistances$"), 3)), - - // +#% total Resistance - new PseudoPatternGroup( - apiCategory: pseudoCategory, - id: "pseudo.pseudo_total_resistance", - exception: new Regex("Minions|Enemies|Totems"), - new PseudoPattern(new Regex("to (Fire|Cold|Lightning|Chaos) Resistance$")), - new PseudoPattern(new Regex("to (?:Fire|Cold|Lightning|Chaos) and (?:Fire|Cold|Lightning|Chaos) Resistances$"), 2), - new PseudoPattern(new Regex("to all Elemental Resistances$"), 3)), - - // # total Resistances - // pseudo.pseudo_count_resistances - - // +# total to Strength - new PseudoPatternGroup( - apiCategory: pseudoCategory, - id: "pseudo.pseudo_total_strength", - exception: new Regex("Passive"), - new PseudoPattern(new Regex("to Strength$")), - new PseudoPattern(new Regex("(?=.*Strength)to (?:Strength|Dexterity|Intelligence) and (?:Strength|Dexterity|Intelligence)$")), - new PseudoPattern(new Regex("to all Attributes$"))), - - // +# total to Dexterity - new PseudoPatternGroup( - apiCategory: pseudoCategory, - id: "pseudo.pseudo_total_dexterity", - exception: new Regex("Passive"), - new PseudoPattern(new Regex("to Dexterity$")), - new PseudoPattern(new Regex("(?=.*Dexterity)to (?:Strength|Dexterity|Intelligence) and (?:Strength|Dexterity|Intelligence)$")), - new PseudoPattern(new Regex("to all Attributes$"))), - - // +# total to Intelligence - new PseudoPatternGroup( - apiCategory: pseudoCategory, - id: "pseudo.pseudo_total_intelligence", - exception: new Regex("Passive"), - new PseudoPattern(new Regex("to Intelligence$")), - new PseudoPattern(new Regex("(?=.*Intelligence)to (?:Strength|Dexterity|Intelligence) and (?:Strength|Dexterity|Intelligence)$")), - new PseudoPattern(new Regex("to all Attributes$"))), - - // +# total to all Attributes - new PseudoPatternGroup( - apiCategory: pseudoCategory, - id: "pseudo.pseudo_total_all_attributes", - exception: new Regex("Passive"), - new PseudoPattern(new Regex("to all Attributes$"))), - - // +# total maximum Life - new PseudoPatternGroup( - apiCategory: pseudoCategory, - id: "pseudo.pseudo_total_life", - exception: new Regex("Zombies|Transformed"), - new PseudoPattern(new Regex("to maximum Life$")), - new PseudoPattern(new Regex("to Strength$"), 0.5), - new PseudoPattern(new Regex("(?=.*Strength)to (?:Strength|Dexterity|Intelligence) and (?:Strength|Dexterity|Intelligence)$"), 0.5), - new PseudoPattern(new Regex("to all Attributes$"), 0.5)), - - // +# total maximum Mana - new PseudoPatternGroup( - apiCategory: pseudoCategory, - id: "pseudo.pseudo_total_mana", - exception: new Regex("Transformed"), - new PseudoPattern(new Regex("to maximum Mana$")), - new PseudoPattern(new Regex("to Intelligence$"), 0.5), - new PseudoPattern(new Regex("(?=.*Intelligence)to (?:Strength|Dexterity|Intelligence) and (?:Strength|Dexterity|Intelligence)$"), 0.5), - new PseudoPattern(new Regex("to all Attributes$"), 0.5)), - - // +# total maximum Energy Shield - new PseudoPatternGroup( - apiCategory: pseudoCategory, - id: "pseudo.pseudo_total_energy_shield", - new PseudoPattern(new Regex("to maximum Energy Shield$"))), - - // #% total increased maximum Energy Shield - new PseudoPatternGroup( - apiCategory: pseudoCategory, - id: "pseudo.pseudo_increased_energy_shield", - new PseudoPattern(new Regex("% increased maximum Energy Shield$"))), - - // +#% total Attack Speed - new PseudoPatternGroup( - apiCategory: pseudoCategory, - id: "pseudo.pseudo_total_attack_speed", - new PseudoPattern(new Regex("^\\#% increased Attack Speed$"))), - - // +#% total Cast Speed - new PseudoPatternGroup( - apiCategory: pseudoCategory, - id: "pseudo.pseudo_total_cast_speed", - new PseudoPattern(new Regex("^\\#% increased Cast Speed$"))), - - // #% increased Movement Speed - new PseudoPatternGroup( - apiCategory: pseudoCategory, - id: "pseudo.pseudo_increased_movement_speed", - new PseudoPattern(new Regex("^\\#% increased Movement Speed$"))), - - // #% total increased Physical Damage - // pseudo.pseudo_increased_physical_damage - - // +#% Global Critical Strike Chance - // pseudo.pseudo_global_critical_strike_chance - - // +#% total Critical Strike Chance for Spells - // pseudo.pseudo_critical_strike_chance_for_spells - - // +#% Global Critical Strike Multiplier - // pseudo.pseudo_global_critical_strike_multiplier - - // Adds # to # Physical Damage - // pseudo.pseudo_adds_physical_damage - - // Adds # to # Lightning Damage - // pseudo.pseudo_adds_lightning_damage - - // Adds # to # Cold Damage - // pseudo.pseudo_adds_cold_damage - - // Adds # to # Fire Damage - // pseudo.pseudo_adds_fire_damage - - // Adds # to # Elemental Damage - // pseudo.pseudo_adds_elemental_damage - - // Adds # to # Chaos Damage - // pseudo.pseudo_adds_chaos_damage - - // Adds # to # Damage - // pseudo.pseudo_adds_damage - - // Adds # to # Physical Damage to Attacks - // pseudo.pseudo_adds_physical_damage_to_attacks - - // Adds # to # Lightning Damage to Attacks - // pseudo.pseudo_adds_lightning_damage_to_attacks - - // Adds # to # Cold Damage to Attacks - // pseudo.pseudo_adds_cold_damage_to_attacks - - // Adds # to # Fire Damage to Attacks - // pseudo.pseudo_adds_fire_damage_to_attacks - - // Adds # to # Elemental Damage to Attacks - // pseudo.pseudo_adds_elemental_damage_to_attacks - - // Adds # to # Chaos Damage to Attacks - // pseudo.pseudo_adds_chaos_damage_to_attacks - - // Adds # to # Damage to Attacks - // pseudo.pseudo_adds_damage_to_attacks - - // Adds # to # Physical Damage to Spells - // pseudo.pseudo_adds_physical_damage_to_spells - - // Adds # to # Lightning Damage to Spells - // pseudo.pseudo_adds_lightning_damage_to_spells - - // Adds # to # Cold Damage to Spells - // pseudo.pseudo_adds_cold_damage_to_spells - - // Adds # to # Fire Damage to Spells - // pseudo.pseudo_adds_fire_damage_to_spells - - // Adds # to # Elemental Damage to Spells - // pseudo.pseudo_adds_elemental_damage_to_spells - - // Adds # to # Chaos Damage to Spells - // pseudo.pseudo_adds_chaos_damage_to_spells - - // Adds # to # Damage to Spells - // pseudo.pseudo_adds_damage_to_spells - - // #% increased Elemental Damage - // pseudo.pseudo_increased_elemental_damage - - // #% increased Lightning Damage - // pseudo.pseudo_increased_lightning_damage - - // #% increased Cold Damage - // pseudo.pseudo_increased_cold_damage - - // #% increased Fire Damage - // pseudo.pseudo_increased_fire_damage - - // #% increased Spell Damage - // pseudo.pseudo_increased_spell_damage - - // #% increased Lightning Spell Damage - // pseudo.pseudo_increased_lightning_spell_damage - - // #% increased Cold Spell Damage - // pseudo.pseudo_increased_cold_spell_damage - - // #% increased Fire Spell Damage - // pseudo.pseudo_increased_fire_spell_damage - - // #% increased Lightning Damage with Attack Skills - // pseudo.pseudo_increased_lightning_damage_with_attack_skills - - // #% increased Cold Damage with Attack Skills - // pseudo.pseudo_increased_cold_damage_with_attack_skills - - // #% increased Fire Damage with Attack Skills - // pseudo.pseudo_increased_fire_damage_with_attack_skills - - // #% increased Elemental Damage with Attack Skills - // pseudo.pseudo_increased_elemental_damage_with_attack_skills - - // #% increased Rarity of Items found - // pseudo.pseudo_increased_rarity - - // #% increased Burning Damage - // pseudo.pseudo_increased_burning_damage - - // # Life Regenerated per Second - // pseudo.pseudo_total_life_regen - - // #% of Life Regenerated per Second - // pseudo.pseudo_percent_life_regen - - // #% of Physical Attack Damage Leeched as Life - // pseudo.pseudo_physical_attack_damage_leeched_as_life - - // #% of Physical Attack Damage Leeched as Mana - // pseudo.pseudo_physical_attack_damage_leeched_as_mana - - // #% increased Mana Regeneration Rate - // pseudo.pseudo_increased_mana_regen - - // +# total to Level of Socketed Gems - // pseudo.pseudo_total_additional_gem_levels - - // +# total to Level of Socketed Elemental Gems - // pseudo.pseudo_total_additional_elemental_gem_levels - - // +# total to Level of Socketed Fire Gems - // pseudo.pseudo_total_additional_fire_gem_levels - - // +# total to Level of Socketed Cold Gems - // pseudo.pseudo_total_additional_cold_gem_levels - - // +# total to Level of Socketed Lightning Gems - // pseudo.pseudo_total_additional_lightning_gem_levels - - // +# total to Level of Socketed Chaos Gems - // pseudo.pseudo_total_additional_chaos_gem_levels - - // +# total to Level of Socketed Spell Gems - // pseudo.pseudo_total_additional_spell_gem_levels - - // +# total to Level of Socketed Projectile Gems - // pseudo.pseudo_total_additional_projectile_gem_levels - - // +# total to Level of Socketed Bow Gems - // pseudo.pseudo_total_additional_bow_gem_levels - - // +# total to Level of Socketed Melee Gems - // pseudo.pseudo_total_additional_melee_gem_levels - - // +# total to Level of Socketed Minion Gems - // pseudo.pseudo_total_additional_minion_gem_levels - - // +# total to Level of Socketed Strength Gems - // pseudo.pseudo_total_additional_strength_gem_levels - - // +# total to Level of Socketed Dexterity Gems - // pseudo.pseudo_total_additional_dexterity_gem_levels - - // +# total to Level of Socketed Intelligence Gems - // pseudo.pseudo_total_additional_intelligence_gem_levels - - // +# total to Level of Socketed Aura Gems - // pseudo.pseudo_total_additional_aura_gem_levels - - // +# total to Level of Socketed Movement Gems - // pseudo.pseudo_total_additional_movement_gem_levels - - // +# total to Level of Socketed Curse Gems - // pseudo.pseudo_total_additional_curse_gem_levels - - // +# total to Level of Socketed Vaal Gems - // pseudo.pseudo_total_additional_vaal_gem_levels - - // +# total to Level of Socketed Support Gems - // pseudo.pseudo_total_additional_support_gem_levels - - // +# total to Level of Socketed Skill Gems - // pseudo.pseudo_total_additional_skill_gem_levels - - // +# total to Level of Socketed Warcry Gems - // pseudo.pseudo_total_additional_warcry_gem_levels - - // +# total to Level of Socketed Golem Gems - // pseudo.pseudo_total_additional_golem_gem_levels - - // # Implicit Modifiers - // pseudo.pseudo_number_of_implicit_mods - - // # Prefix Modifiers - // pseudo.pseudo_number_of_prefix_mods - - // # Suffix Modifiers - // pseudo.pseudo_number_of_suffix_mods - - // # Modifiers - // pseudo.pseudo_number_of_affix_mods - - // # Crafted Prefix Modifiers - // pseudo.pseudo_number_of_crafted_prefix_mods - - // # Crafted Suffix Modifiers - // pseudo.pseudo_number_of_crafted_suffix_mods - - // # Crafted Modifiers - // pseudo.pseudo_number_of_crafted_mods - - // # Empty Prefix Modifiers - // pseudo.pseudo_number_of_empty_prefix_mods - - // # Empty Suffix Modifiers - // pseudo.pseudo_number_of_empty_suffix_mods - - // # Empty Modifiers - // pseudo.pseudo_number_of_empty_affix_mods - - // # Incubator Kills (Whispering) - // pseudo.pseudo_whispering_incubator_kills - - // # Incubator Kills (Fine) - // pseudo.pseudo_fine_incubator_kills - - // # Incubator Kills (Singular) - // pseudo.pseudo_singular_incubator_kills - - // # Incubator Kills (Cartographer's) - // pseudo.pseudo_cartographers_incubator_kills - - // # Incubator Kills (Otherwordly) - // pseudo.pseudo_otherworldly_incubator_kills - - // # Incubator Kills (Abyssal) - // pseudo.pseudo_abyssal_incubator_kills - - // # Incubator Kills (Fragmented) - // pseudo.pseudo_fragmented_incubator_kills - - // # Incubator Kills (Skittering) - // pseudo.pseudo_skittering_incubator_kills - - // # Incubator Kills (Infused) - // pseudo.pseudo_infused_incubator_kills - - // # Incubator Kills (Fossilised) - // pseudo.pseudo_fossilised_incubator_kills - - // # Incubator Kills (Decadent) - // pseudo.pseudo_decadent_incubator_kills - - // # Incubator Kills (Diviner's) - // pseudo.pseudo_diviners_incubator_kills - - // # Incubator Kills (Primal) - // pseudo.pseudo_primal_incubator_kills - - // # Incubator Kills (Enchanted) - // pseudo.pseudo_enchanted_incubator_kills - - // # Incubator Kills (Geomancer's) - // pseudo.pseudo_geomancers_incubator_kills - - // # Incubator Kills (Ornate) - // pseudo.pseudo_ornate_incubator_kills - - // # Incubator Kills (Time-Lost) - // pseudo.pseudo_timelost_incubator_kills - - // # Incubator Kills (Celestial Armoursmith's) - // pseudo.pseudo_celestial_armoursmiths_incubator_kills - - // # Incubator Kills (Celestial Blacksmith's) - // pseudo.pseudo_celestial_blacksmiths_incubator_kills - - // # Incubator Kills (Celestial Jeweller's) - // pseudo.pseudo_celestial_jewellers_incubator_kills - - // # Incubator Kills (Eldritch) - // pseudo.pseudo_eldritch_incubator_kills - - // # Incubator Kills (Obscured) - // pseudo.pseudo_obscured_incubator_kills - - // # Incubator Kills (Foreboding) - // pseudo.pseudo_foreboding_incubator_kills - - // # Incubator Kills (Thaumaturge's) - // pseudo.pseudo_thaumaturges_incubator_kills - - // # Incubator Kills (Mysterious) - // pseudo.pseudo_mysterious_incubator_kills - - // # Incubator Kills (Gemcutter's) - // pseudo.pseudo_gemcutters_incubator_kills - - // # Incubator Kills (Feral) - // pseudo.pseudo_feral_incubator_kills - - // # Fractured Modifiers - // pseudo.pseudo_number_of_fractured_mods - - // +#% Quality to Elemental Damage Modifiers - // pseudo.pseudo_jewellery_elemental_quality - - // +#% Quality to Caster Modifiers - // pseudo.pseudo_jewellery_caster_quality - - // +#% Quality to Attack Modifiers - // pseudo.pseudo_jewellery_attack_quality - - // +#% Quality to Defence Modifiers - // pseudo.pseudo_jewellery_defense_quality - - // +#% Quality to Life and Mana Modifiers - // pseudo.pseudo_jewellery_resource_quality - - // +#% Quality to Resistance Modifiers - // pseudo.pseudo_jewellery_resistance_quality - - // +#% Quality to Attribute Modifiers - // pseudo.pseudo_jewellery_attribute_quality - }; - - return groups; - } - */ - - public List Parse(List lines) - { - if (lines.Count == 0) - { - return []; - } - - var results = new List(); - - foreach (var definition in Definitions) - { - var result = new PseudoModifier() - { - Text = definition.Text, - }; - - foreach (var definitionModifier in definition.Modifiers) - { - foreach (var line in lines) - { - var hasModifier = false; - - foreach (var modifier in line.Modifiers) - { - if (modifier.Id != definitionModifier.Id) - { - continue; - } - - hasModifier = true; - result.Modifiers.Add(modifier.Id, definitionModifier.Multiplier); - } - - if (hasModifier) - { - result.Value += line.Values.Average() * definitionModifier.Multiplier; - } - } - } - - if (result.Modifiers.Count > 0) - { - results.Add(result); - } - } - - results.ForEach(x => - { - x.Value = (int)x.Value; - x.Text = parseHashPattern.Replace(x.Text, ((int)x.Value).ToString(), 1); - }); - - return results; - } - } -} diff --git a/src/Sidekick.Apis.Poe/StartupExtensions.cs b/src/Sidekick.Apis.Poe/StartupExtensions.cs index 1d17f0003..e6e6c66ca 100644 --- a/src/Sidekick.Apis.Poe/StartupExtensions.cs +++ b/src/Sidekick.Apis.Poe/StartupExtensions.cs @@ -18,8 +18,8 @@ using Sidekick.Apis.Poe.Parser.Modifiers; using Sidekick.Apis.Poe.Parser.Patterns; using Sidekick.Apis.Poe.Parser.Properties; +using Sidekick.Apis.Poe.Parser.Pseudo; using Sidekick.Apis.Poe.Parser.Sockets; -using Sidekick.Apis.Poe.Pseudo; using Sidekick.Apis.Poe.Stash; using Sidekick.Apis.Poe.Static; using Sidekick.Apis.Poe.Trade; @@ -73,7 +73,7 @@ public static IServiceCollection AddSidekickPoeApi(this IServiceCollection servi services.AddSidekickInitializableService(); services.AddSidekickInitializableService(); services.AddSidekickInitializableService(); - services.AddSidekickInitializableService(); + services.AddSidekickInitializableService(); services.AddSidekickInitializableService(); services.AddSidekickInitializableService(); From 7652c6915796db2c175c2ea7ea926b0fbb813bd9 Mon Sep 17 00:00:00 2001 From: leMicin Date: Sun, 29 Dec 2024 00:18:39 -0500 Subject: [PATCH 3/7] Small cleanups --- .../Parser/Pseudo/PseudoDefinition.cs | 35 +++++++++-------- .../Trade/Requests/Filters/StatType.cs | 2 +- .../Trade/TradeSearchService.cs | 38 +++++++++---------- 3 files changed, 37 insertions(+), 38 deletions(-) diff --git a/src/Sidekick.Apis.Poe/Parser/Pseudo/PseudoDefinition.cs b/src/Sidekick.Apis.Poe/Parser/Pseudo/PseudoDefinition.cs index 29d5536cf..cb834c187 100644 --- a/src/Sidekick.Apis.Poe/Parser/Pseudo/PseudoDefinition.cs +++ b/src/Sidekick.Apis.Poe/Parser/Pseudo/PseudoDefinition.cs @@ -29,24 +29,7 @@ internal void AddModifierIfMatch(ApiModifier entry) internal PseudoModifier? Parse(List itemModifierLines) { - var hasPseudo = false; - foreach (var definitionModifier in Modifiers) - { - foreach (var itemModifierLine in itemModifierLines) - { - foreach (var modifier in itemModifierLine.Modifiers) - { - hasPseudo = hasPseudo || modifier.Id == definitionModifier.Id; - if (hasPseudo) break; - } - - if (hasPseudo) break; - } - - if (hasPseudo) break; - } - - if (!hasPseudo) + if (!HasPseudoMods(itemModifierLines)) { return null; } @@ -78,6 +61,22 @@ internal void AddModifierIfMatch(ApiModifier entry) return result; } + private bool HasPseudoMods(List itemModifierLines) + { + foreach (var definitionModifier in Modifiers) + { + foreach (var itemModifierLine in itemModifierLines) + { + foreach (var modifier in itemModifierLine.Modifiers) + { + if (modifier.Id == definitionModifier.Id) return true; + } + } + } + + return false; + } + protected abstract List Patterns { get; } protected abstract Regex? Exception { get; } diff --git a/src/Sidekick.Apis.Poe/Trade/Requests/Filters/StatType.cs b/src/Sidekick.Apis.Poe/Trade/Requests/Filters/StatType.cs index 9bff78062..e1dce5452 100644 --- a/src/Sidekick.Apis.Poe/Trade/Requests/Filters/StatType.cs +++ b/src/Sidekick.Apis.Poe/Trade/Requests/Filters/StatType.cs @@ -10,6 +10,6 @@ internal enum StatType [EnumValue("count")] Count, - [EnumValue("weight")] + [EnumValue("weight2")] WeightedSum, } diff --git a/src/Sidekick.Apis.Poe/Trade/TradeSearchService.cs b/src/Sidekick.Apis.Poe/Trade/TradeSearchService.cs index 4ba1f63ce..2dcf78ba1 100644 --- a/src/Sidekick.Apis.Poe/Trade/TradeSearchService.cs +++ b/src/Sidekick.Apis.Poe/Trade/TradeSearchService.cs @@ -508,29 +508,19 @@ private static void SetModifierFilters(List stats, List x.Type == StatType.And); - if (andGroup == null) + var andGroup = new StatFilterGroup() { - andGroup = new StatFilterGroup() - { - Type = StatType.And, - }; - stats.Add(andGroup); - } + Type = StatType.And, + }; - var countGroup = stats.FirstOrDefault(x => x.Type == StatType.Count); - if (countGroup == null) + var countGroup = new StatFilterGroup() { - countGroup = new StatFilterGroup() + Type = StatType.Count, + Value = new SearchFilterValue() { - Type = StatType.Count, - Value = new SearchFilterValue() - { - Min = 0, - }, - }; - stats.Add(countGroup); - } + Min = 0, + }, + }; foreach (var filter in modifierFilters) { @@ -579,6 +569,16 @@ private static void SetModifierFilters(List stats, List 0) + { + stats.Add(andGroup); + } + + if (countGroup.Filters.Count > 0) + { + stats.Add(countGroup); + } } private List GetPseudoStatFilterGroups(List? pseudoFilters) From bf847c1cdc36a6e85ab4a42a812ccb888d09fab5 Mon Sep 17 00:00:00 2001 From: leMicin Date: Sun, 29 Dec 2024 01:27:40 -0500 Subject: [PATCH 4/7] Refactored pseudo modifiers for Path of Exile 1 and 2. --- .../Definitions/ChaosResistancesDefinition.cs | 19 + .../Pseudo/Definitions/DexterityDefinition.cs | 20 + ...n.cs => ElementalResistancesDefinition.cs} | 7 +- .../Definitions/IntelligenceDefinition.cs | 20 + .../Pseudo/Definitions/LifeDefinition.cs | 23 + .../Pseudo/Definitions/ManaDefinition.cs | 23 + .../Pseudo/Definitions/StrengthDefinition.cs | 20 + .../Parser/Pseudo/PseudoDefinition.cs | 35 +- .../Parser/Pseudo/PseudoParser.cs | 533 +----------------- .../Parser/Pseudo/PseudoPattern.cs | 4 +- .../Trade/TradeFilterService.cs | 2 +- .../Trade/TradeSearchService.cs | 140 +++-- .../Game/Items/PseudoModifier.cs | 4 +- 13 files changed, 274 insertions(+), 576 deletions(-) create mode 100644 src/Sidekick.Apis.Poe/Parser/Pseudo/Definitions/ChaosResistancesDefinition.cs create mode 100644 src/Sidekick.Apis.Poe/Parser/Pseudo/Definitions/DexterityDefinition.cs rename src/Sidekick.Apis.Poe/Parser/Pseudo/Definitions/{ElementalResistanceDefinition.cs => ElementalResistancesDefinition.cs} (65%) create mode 100644 src/Sidekick.Apis.Poe/Parser/Pseudo/Definitions/IntelligenceDefinition.cs create mode 100644 src/Sidekick.Apis.Poe/Parser/Pseudo/Definitions/LifeDefinition.cs create mode 100644 src/Sidekick.Apis.Poe/Parser/Pseudo/Definitions/ManaDefinition.cs create mode 100644 src/Sidekick.Apis.Poe/Parser/Pseudo/Definitions/StrengthDefinition.cs diff --git a/src/Sidekick.Apis.Poe/Parser/Pseudo/Definitions/ChaosResistancesDefinition.cs b/src/Sidekick.Apis.Poe/Parser/Pseudo/Definitions/ChaosResistancesDefinition.cs new file mode 100644 index 000000000..12988a55c --- /dev/null +++ b/src/Sidekick.Apis.Poe/Parser/Pseudo/Definitions/ChaosResistancesDefinition.cs @@ -0,0 +1,19 @@ +using System.Text.RegularExpressions; +using Sidekick.Common.Game; + +namespace Sidekick.Apis.Poe.Parser.Pseudo.Definitions; + +public class ChaosResistancesDefinition(GameType game) : PseudoDefinition +{ + protected override bool Enabled => game == GameType.PathOfExile; + + protected override string? ModifierId => game == GameType.PathOfExile ? "pseudo.pseudo_total_chaos_resistance" : null; + + protected override List Patterns => + [ + new(new Regex("to Chaos Resistance$")), + new(new Regex("(?=.*Chaos)to (?:Fire|Cold|Lightning|Chaos) and (?:Fire|Cold|Lightning|Chaos) Resistances$")), + ]; + + protected override Regex Exception => new("Minions|Enemies|Totems"); +} diff --git a/src/Sidekick.Apis.Poe/Parser/Pseudo/Definitions/DexterityDefinition.cs b/src/Sidekick.Apis.Poe/Parser/Pseudo/Definitions/DexterityDefinition.cs new file mode 100644 index 000000000..e7b9de213 --- /dev/null +++ b/src/Sidekick.Apis.Poe/Parser/Pseudo/Definitions/DexterityDefinition.cs @@ -0,0 +1,20 @@ +using System.Text.RegularExpressions; +using Sidekick.Common.Game; + +namespace Sidekick.Apis.Poe.Parser.Pseudo.Definitions; + +public class DexterityDefinition(GameType game) : PseudoDefinition +{ + protected override bool Enabled => game == GameType.PathOfExile; + + protected override string? ModifierId => game == GameType.PathOfExile ? "pseudo.pseudo_total_dexterity" : null; + + protected override List Patterns => + [ + new(new Regex("to Dexterity$")), + new(new Regex("(?=.*Dexterity)to (?:Strength|Dexterity|Intelligence) and (?:Strength|Dexterity|Intelligence)$")), + new(new Regex("to all Attributes$")), + ]; + + protected override Regex Exception => new("Passive"); +} diff --git a/src/Sidekick.Apis.Poe/Parser/Pseudo/Definitions/ElementalResistanceDefinition.cs b/src/Sidekick.Apis.Poe/Parser/Pseudo/Definitions/ElementalResistancesDefinition.cs similarity index 65% rename from src/Sidekick.Apis.Poe/Parser/Pseudo/Definitions/ElementalResistanceDefinition.cs rename to src/Sidekick.Apis.Poe/Parser/Pseudo/Definitions/ElementalResistancesDefinition.cs index 2e224f953..2f5dd3999 100644 --- a/src/Sidekick.Apis.Poe/Parser/Pseudo/Definitions/ElementalResistanceDefinition.cs +++ b/src/Sidekick.Apis.Poe/Parser/Pseudo/Definitions/ElementalResistancesDefinition.cs @@ -1,9 +1,14 @@ using System.Text.RegularExpressions; +using Sidekick.Common.Game; namespace Sidekick.Apis.Poe.Parser.Pseudo.Definitions; -public class ElementalResistanceDefinition : PseudoDefinition +public class ElementalResistancesDefinition(GameType game) : PseudoDefinition { + protected override bool Enabled => game == GameType.PathOfExile; + + protected override string? ModifierId => game == GameType.PathOfExile ? "pseudo.pseudo_total_elemental_resistance" : null; + protected override List Patterns => [ new(new Regex("to (?:Fire|Cold|Lightning) Resistance$")), diff --git a/src/Sidekick.Apis.Poe/Parser/Pseudo/Definitions/IntelligenceDefinition.cs b/src/Sidekick.Apis.Poe/Parser/Pseudo/Definitions/IntelligenceDefinition.cs new file mode 100644 index 000000000..6a8dae4da --- /dev/null +++ b/src/Sidekick.Apis.Poe/Parser/Pseudo/Definitions/IntelligenceDefinition.cs @@ -0,0 +1,20 @@ +using System.Text.RegularExpressions; +using Sidekick.Common.Game; + +namespace Sidekick.Apis.Poe.Parser.Pseudo.Definitions; + +public class IntelligenceDefinition(GameType game) : PseudoDefinition +{ + protected override bool Enabled => game == GameType.PathOfExile; + + protected override string? ModifierId => game == GameType.PathOfExile ? "pseudo.pseudo_total_intelligence" : null; + + protected override List Patterns => + [ + new(new Regex("to Intelligence$")), + new(new Regex("(?=.*Intelligence)to (?:Strength|Dexterity|Intelligence) and (?:Strength|Dexterity|Intelligence)$")), + new(new Regex("to all Attributes$")), + ]; + + protected override Regex Exception => new("Passive"); +} diff --git a/src/Sidekick.Apis.Poe/Parser/Pseudo/Definitions/LifeDefinition.cs b/src/Sidekick.Apis.Poe/Parser/Pseudo/Definitions/LifeDefinition.cs new file mode 100644 index 000000000..ed0c80adb --- /dev/null +++ b/src/Sidekick.Apis.Poe/Parser/Pseudo/Definitions/LifeDefinition.cs @@ -0,0 +1,23 @@ +using System.Text.RegularExpressions; +using Sidekick.Common.Game; + +namespace Sidekick.Apis.Poe.Parser.Pseudo.Definitions; + +public class LifeDefinition(GameType game) : PseudoDefinition +{ + protected override bool Enabled => game == GameType.PathOfExile; + + protected override string? ModifierId => game == GameType.PathOfExile ? "pseudo.pseudo_total_life" : null; + + protected override List Patterns => + [ + new(new Regex("to maximum Life$")), + new(new Regex("to Strength$"), AttributeMultiplier), + new(new Regex("(?=.*Strength)to (?:Strength|Dexterity|Intelligence) and (?:Strength|Dexterity|Intelligence)$"), AttributeMultiplier), + new(new Regex("to all Attributes$"), AttributeMultiplier), + ]; + + protected override Regex Exception => new("Zombies|Transformed"); + + private double AttributeMultiplier => game == GameType.PathOfExile ? 0.5 : 2; +} diff --git a/src/Sidekick.Apis.Poe/Parser/Pseudo/Definitions/ManaDefinition.cs b/src/Sidekick.Apis.Poe/Parser/Pseudo/Definitions/ManaDefinition.cs new file mode 100644 index 000000000..e3d65a9b8 --- /dev/null +++ b/src/Sidekick.Apis.Poe/Parser/Pseudo/Definitions/ManaDefinition.cs @@ -0,0 +1,23 @@ +using System.Text.RegularExpressions; +using Sidekick.Common.Game; + +namespace Sidekick.Apis.Poe.Parser.Pseudo.Definitions; + +public class ManaDefinition(GameType game) : PseudoDefinition +{ + protected override bool Enabled => game == GameType.PathOfExile; + + protected override string? ModifierId => game == GameType.PathOfExile ? "pseudo.pseudo_total_mana" : null; + + protected override List Patterns => + [ + new PseudoPattern(new Regex("to maximum Mana$")), + new PseudoPattern(new Regex("to Intelligence$"), AttributeMultiplier), + new PseudoPattern(new Regex("(?=.*Intelligence)to (?:Strength|Dexterity|Intelligence) and (?:Strength|Dexterity|Intelligence)$"), AttributeMultiplier), + new PseudoPattern(new Regex("to all Attributes$"), AttributeMultiplier), + ]; + + protected override Regex Exception => new("Zombies|Transformed"); + + private double AttributeMultiplier => game == GameType.PathOfExile ? 0.5 : 2; +} diff --git a/src/Sidekick.Apis.Poe/Parser/Pseudo/Definitions/StrengthDefinition.cs b/src/Sidekick.Apis.Poe/Parser/Pseudo/Definitions/StrengthDefinition.cs new file mode 100644 index 000000000..faa9048f7 --- /dev/null +++ b/src/Sidekick.Apis.Poe/Parser/Pseudo/Definitions/StrengthDefinition.cs @@ -0,0 +1,20 @@ +using System.Text.RegularExpressions; +using Sidekick.Common.Game; + +namespace Sidekick.Apis.Poe.Parser.Pseudo.Definitions; + +public class StrengthDefinition(GameType game) : PseudoDefinition +{ + protected override bool Enabled => game == GameType.PathOfExile; + + protected override string? ModifierId => game == GameType.PathOfExile ? "pseudo.pseudo_total_strength" : null; + + protected override List Patterns => + [ + new(new Regex("to Strength$")), + new(new Regex("(?=.*Strength)to (?:Strength|Dexterity|Intelligence) and (?:Strength|Dexterity|Intelligence)$")), + new(new Regex("to all Attributes$")), + ]; + + protected override Regex Exception => new("Passive"); +} diff --git a/src/Sidekick.Apis.Poe/Parser/Pseudo/PseudoDefinition.cs b/src/Sidekick.Apis.Poe/Parser/Pseudo/PseudoDefinition.cs index cb834c187..19ce78e91 100644 --- a/src/Sidekick.Apis.Poe/Parser/Pseudo/PseudoDefinition.cs +++ b/src/Sidekick.Apis.Poe/Parser/Pseudo/PseudoDefinition.cs @@ -6,8 +6,25 @@ namespace Sidekick.Apis.Poe.Parser.Pseudo; public abstract class PseudoDefinition { + protected abstract bool Enabled { get; } + + protected abstract string? ModifierId { get; } + + protected abstract List Patterns { get; } + + protected abstract Regex? Exception { get; } + + public string Text => Modifiers.FirstOrDefault()?.Text ?? string.Empty; + + public List Modifiers { get; set; } = new(); + internal void AddModifierIfMatch(ApiModifier entry) { + if (!Enabled) + { + return; + } + if (Exception != null && Exception.IsMatch(entry.Text)) { return; @@ -29,19 +46,23 @@ internal void AddModifierIfMatch(ApiModifier entry) internal PseudoModifier? Parse(List itemModifierLines) { - if (!HasPseudoMods(itemModifierLines)) + if (!Enabled || !HasPseudoMods(itemModifierLines)) { return null; } var result = new PseudoModifier() { + PseudoModifierId = ModifierId, Text = Text, }; - foreach (var definitionModifier in Modifiers) + if (string.IsNullOrEmpty(ModifierId)) { - result.Modifiers.Add(definitionModifier.Id, definitionModifier.Multiplier); + foreach (var definitionModifier in Modifiers) + { + result.WeightedSumModifiers.Add(definitionModifier.Id, definitionModifier.Multiplier); + } } foreach (var itemModifierLine in itemModifierLines) @@ -76,12 +97,4 @@ private bool HasPseudoMods(List itemModifierLines) return false; } - - protected abstract List Patterns { get; } - - protected abstract Regex? Exception { get; } - - public string Text => Modifiers.FirstOrDefault()?.Text ?? string.Empty; - - public List Modifiers { get; set; } = new(); } diff --git a/src/Sidekick.Apis.Poe/Parser/Pseudo/PseudoParser.cs b/src/Sidekick.Apis.Poe/Parser/Pseudo/PseudoParser.cs index b25f7b83f..a5fa2cdf8 100644 --- a/src/Sidekick.Apis.Poe/Parser/Pseudo/PseudoParser.cs +++ b/src/Sidekick.Apis.Poe/Parser/Pseudo/PseudoParser.cs @@ -1,11 +1,17 @@ using System.Text.RegularExpressions; using Sidekick.Apis.Poe.Modifiers; using Sidekick.Apis.Poe.Parser.Pseudo.Definitions; +using Sidekick.Common.Extensions; using Sidekick.Common.Game.Items; +using Sidekick.Common.Settings; namespace Sidekick.Apis.Poe.Parser.Pseudo; -public class PseudoParser(IInvariantModifierProvider invariantModifierProvider) : IPseudoParser +public class PseudoParser +( + IInvariantModifierProvider invariantModifierProvider, + ISettingsService settingsService +) : IPseudoParser { private readonly Regex parseHashPattern = new("\\#"); @@ -17,20 +23,23 @@ public class PseudoParser(IInvariantModifierProvider invariantModifierProvider) /// public async Task Initialize() { + var leagueId = await settingsService.GetString(SettingKeys.LeagueId); + var game = leagueId.GetGameFromLeagueId(); + Definitions.Clear(); Definitions.AddRange([ - new ElementalResistanceDefinition(), + new ElementalResistancesDefinition(game), + new ChaosResistancesDefinition(game), + new StrengthDefinition(game), + new IntelligenceDefinition(game), + new DexterityDefinition(game), + new LifeDefinition(game), + new ManaDefinition(game), ]); var categories = await invariantModifierProvider.GetList(); foreach (var category in categories) { - var first = category.Entries.FirstOrDefault(); - if (first == null || first.Id.Split('.').First() == "pseudo") - { - continue; - } - foreach (var entry in category.Entries) { foreach (var definition in Definitions) @@ -44,13 +53,13 @@ public async Task Initialize() { definition.Modifiers = definition.Modifiers.OrderBy(x => x.Type switch { - "explicit" => 0, - "implicit" => 1, - "crafted" => 2, - "enchant" => 3, - "fractured" => 4, - "veiled" => 5, - "pseudo" => 6, + "pseudo" => 0, + "explicit" => 1, + "implicit" => 2, + "crafted" => 3, + "enchant" => 4, + "fractured" => 5, + "veiled" => 6, _ => 7, }) .ThenBy(x => x.Text) @@ -58,500 +67,6 @@ public async Task Initialize() } } - /* - private static List InitializeGroups(List categories) - { - var pseudoCategory = categories - .FirstOrDefault(x => x.Entries.FirstOrDefault()?.Id?.Split('.').FirstOrDefault() == "pseudo"); - if (pseudoCategory == null) - { - return new(); - } - - var groups = new List() { - // +#% total to Cold Resistance - new PseudoPatternGroup( - apiCategory: pseudoCategory, - id: "pseudo.pseudo_total_cold_resistance", - exception: new Regex("Minions|Enemies|Totems"), - new PseudoPattern(new Regex("to Cold Resistance$")), - new PseudoPattern(new Regex("(?=.*Cold)to (?:Fire|Cold|Lightning|Chaos) and (?:Fire|Cold|Lightning|Chaos) Resistances$"))), - - // +#% total to Fire Resistance - new PseudoPatternGroup( - apiCategory: pseudoCategory, - id: "pseudo.pseudo_total_fire_resistance", - exception: new Regex("Minions|Enemies|Totems"), - new PseudoPattern(new Regex("to Fire Resistance$")), - new PseudoPattern(new Regex("(?=.*Fire)to (?:Fire|Cold|Lightning|Chaos) and (?:Fire|Cold|Lightning|Chaos) Resistances$"))), - - // +#% total to Lightning Resistance - new PseudoPatternGroup( - apiCategory: pseudoCategory, - id: "pseudo.pseudo_total_lightning_resistance", - exception: new Regex("Minions|Enemies|Totems"), - new PseudoPattern(new Regex("to Lightning Resistance$")), - new PseudoPattern(new Regex("(?=.*Lightning)to (?:Fire|Cold|Lightning|Chaos) and (?:Fire|Cold|Lightning|Chaos) Resistances$"))), - - // +#% total to Chaos Resistance - new PseudoPatternGroup( - apiCategory: pseudoCategory, - id: "pseudo.pseudo_total_chaos_resistance", - exception: new Regex("Minions|Enemies|Totems"), - new PseudoPattern(new Regex("to Chaos Resistance$")), - new PseudoPattern(new Regex("(?=.*Chaos)to (?:Fire|Cold|Lightning|Chaos) and (?:Fire|Cold|Lightning|Chaos) Resistances$"))), - - // +#% total to all Elemental Resistances - new PseudoPatternGroup( - apiCategory: pseudoCategory, - id: "pseudo.pseudo_total_all_elemental_resistances", - exception: new Regex("Minions|Enemies|Totems"), - new PseudoPattern(new Regex("to all Elemental Resistances$"))), - - // +#% total Elemental Resistance - new PseudoPatternGroup( - apiCategory: pseudoCategory, - id: "pseudo.pseudo_total_elemental_resistance", - exception: new Regex("Minions|Enemies|Totems"), - new PseudoPattern(new Regex("to (?:Fire|Cold|Lightning) Resistance$")), - new PseudoPattern(new Regex("to (?:Fire|Cold|Lightning) and (?:Fire|Cold|Lightning) Resistances$"), 2), - new PseudoPattern(new Regex("(?=.*Chaos)to (?:Fire|Cold|Lightning|Chaos) and (?:Fire|Cold|Lightning|Chaos) Resistances$")), - new PseudoPattern(new Regex("to all Elemental Resistances$"), 3)), - - // +#% total Resistance - new PseudoPatternGroup( - apiCategory: pseudoCategory, - id: "pseudo.pseudo_total_resistance", - exception: new Regex("Minions|Enemies|Totems"), - new PseudoPattern(new Regex("to (Fire|Cold|Lightning|Chaos) Resistance$")), - new PseudoPattern(new Regex("to (?:Fire|Cold|Lightning|Chaos) and (?:Fire|Cold|Lightning|Chaos) Resistances$"), 2), - new PseudoPattern(new Regex("to all Elemental Resistances$"), 3)), - - // # total Resistances - // pseudo.pseudo_count_resistances - - // +# total to Strength - new PseudoPatternGroup( - apiCategory: pseudoCategory, - id: "pseudo.pseudo_total_strength", - exception: new Regex("Passive"), - new PseudoPattern(new Regex("to Strength$")), - new PseudoPattern(new Regex("(?=.*Strength)to (?:Strength|Dexterity|Intelligence) and (?:Strength|Dexterity|Intelligence)$")), - new PseudoPattern(new Regex("to all Attributes$"))), - - // +# total to Dexterity - new PseudoPatternGroup( - apiCategory: pseudoCategory, - id: "pseudo.pseudo_total_dexterity", - exception: new Regex("Passive"), - new PseudoPattern(new Regex("to Dexterity$")), - new PseudoPattern(new Regex("(?=.*Dexterity)to (?:Strength|Dexterity|Intelligence) and (?:Strength|Dexterity|Intelligence)$")), - new PseudoPattern(new Regex("to all Attributes$"))), - - // +# total to Intelligence - new PseudoPatternGroup( - apiCategory: pseudoCategory, - id: "pseudo.pseudo_total_intelligence", - exception: new Regex("Passive"), - new PseudoPattern(new Regex("to Intelligence$")), - new PseudoPattern(new Regex("(?=.*Intelligence)to (?:Strength|Dexterity|Intelligence) and (?:Strength|Dexterity|Intelligence)$")), - new PseudoPattern(new Regex("to all Attributes$"))), - - // +# total to all Attributes - new PseudoPatternGroup( - apiCategory: pseudoCategory, - id: "pseudo.pseudo_total_all_attributes", - exception: new Regex("Passive"), - new PseudoPattern(new Regex("to all Attributes$"))), - - // +# total maximum Life - new PseudoPatternGroup( - apiCategory: pseudoCategory, - id: "pseudo.pseudo_total_life", - exception: new Regex("Zombies|Transformed"), - new PseudoPattern(new Regex("to maximum Life$")), - new PseudoPattern(new Regex("to Strength$"), 0.5), - new PseudoPattern(new Regex("(?=.*Strength)to (?:Strength|Dexterity|Intelligence) and (?:Strength|Dexterity|Intelligence)$"), 0.5), - new PseudoPattern(new Regex("to all Attributes$"), 0.5)), - - // +# total maximum Mana - new PseudoPatternGroup( - apiCategory: pseudoCategory, - id: "pseudo.pseudo_total_mana", - exception: new Regex("Transformed"), - new PseudoPattern(new Regex("to maximum Mana$")), - new PseudoPattern(new Regex("to Intelligence$"), 0.5), - new PseudoPattern(new Regex("(?=.*Intelligence)to (?:Strength|Dexterity|Intelligence) and (?:Strength|Dexterity|Intelligence)$"), 0.5), - new PseudoPattern(new Regex("to all Attributes$"), 0.5)), - - // +# total maximum Energy Shield - new PseudoPatternGroup( - apiCategory: pseudoCategory, - id: "pseudo.pseudo_total_energy_shield", - new PseudoPattern(new Regex("to maximum Energy Shield$"))), - - // #% total increased maximum Energy Shield - new PseudoPatternGroup( - apiCategory: pseudoCategory, - id: "pseudo.pseudo_increased_energy_shield", - new PseudoPattern(new Regex("% increased maximum Energy Shield$"))), - - // +#% total Attack Speed - new PseudoPatternGroup( - apiCategory: pseudoCategory, - id: "pseudo.pseudo_total_attack_speed", - new PseudoPattern(new Regex("^\\#% increased Attack Speed$"))), - - // +#% total Cast Speed - new PseudoPatternGroup( - apiCategory: pseudoCategory, - id: "pseudo.pseudo_total_cast_speed", - new PseudoPattern(new Regex("^\\#% increased Cast Speed$"))), - - // #% increased Movement Speed - new PseudoPatternGroup( - apiCategory: pseudoCategory, - id: "pseudo.pseudo_increased_movement_speed", - new PseudoPattern(new Regex("^\\#% increased Movement Speed$"))), - - // #% total increased Physical Damage - // pseudo.pseudo_increased_physical_damage - - // +#% Global Critical Strike Chance - // pseudo.pseudo_global_critical_strike_chance - - // +#% total Critical Strike Chance for Spells - // pseudo.pseudo_critical_strike_chance_for_spells - - // +#% Global Critical Strike Multiplier - // pseudo.pseudo_global_critical_strike_multiplier - - // Adds # to # Physical Damage - // pseudo.pseudo_adds_physical_damage - - // Adds # to # Lightning Damage - // pseudo.pseudo_adds_lightning_damage - - // Adds # to # Cold Damage - // pseudo.pseudo_adds_cold_damage - - // Adds # to # Fire Damage - // pseudo.pseudo_adds_fire_damage - - // Adds # to # Elemental Damage - // pseudo.pseudo_adds_elemental_damage - - // Adds # to # Chaos Damage - // pseudo.pseudo_adds_chaos_damage - - // Adds # to # Damage - // pseudo.pseudo_adds_damage - - // Adds # to # Physical Damage to Attacks - // pseudo.pseudo_adds_physical_damage_to_attacks - - // Adds # to # Lightning Damage to Attacks - // pseudo.pseudo_adds_lightning_damage_to_attacks - - // Adds # to # Cold Damage to Attacks - // pseudo.pseudo_adds_cold_damage_to_attacks - - // Adds # to # Fire Damage to Attacks - // pseudo.pseudo_adds_fire_damage_to_attacks - - // Adds # to # Elemental Damage to Attacks - // pseudo.pseudo_adds_elemental_damage_to_attacks - - // Adds # to # Chaos Damage to Attacks - // pseudo.pseudo_adds_chaos_damage_to_attacks - - // Adds # to # Damage to Attacks - // pseudo.pseudo_adds_damage_to_attacks - - // Adds # to # Physical Damage to Spells - // pseudo.pseudo_adds_physical_damage_to_spells - - // Adds # to # Lightning Damage to Spells - // pseudo.pseudo_adds_lightning_damage_to_spells - - // Adds # to # Cold Damage to Spells - // pseudo.pseudo_adds_cold_damage_to_spells - - // Adds # to # Fire Damage to Spells - // pseudo.pseudo_adds_fire_damage_to_spells - - // Adds # to # Elemental Damage to Spells - // pseudo.pseudo_adds_elemental_damage_to_spells - - // Adds # to # Chaos Damage to Spells - // pseudo.pseudo_adds_chaos_damage_to_spells - - // Adds # to # Damage to Spells - // pseudo.pseudo_adds_damage_to_spells - - // #% increased Elemental Damage - // pseudo.pseudo_increased_elemental_damage - - // #% increased Lightning Damage - // pseudo.pseudo_increased_lightning_damage - - // #% increased Cold Damage - // pseudo.pseudo_increased_cold_damage - - // #% increased Fire Damage - // pseudo.pseudo_increased_fire_damage - - // #% increased Spell Damage - // pseudo.pseudo_increased_spell_damage - - // #% increased Lightning Spell Damage - // pseudo.pseudo_increased_lightning_spell_damage - - // #% increased Cold Spell Damage - // pseudo.pseudo_increased_cold_spell_damage - - // #% increased Fire Spell Damage - // pseudo.pseudo_increased_fire_spell_damage - - // #% increased Lightning Damage with Attack Skills - // pseudo.pseudo_increased_lightning_damage_with_attack_skills - - // #% increased Cold Damage with Attack Skills - // pseudo.pseudo_increased_cold_damage_with_attack_skills - - // #% increased Fire Damage with Attack Skills - // pseudo.pseudo_increased_fire_damage_with_attack_skills - - // #% increased Elemental Damage with Attack Skills - // pseudo.pseudo_increased_elemental_damage_with_attack_skills - - // #% increased Rarity of Items found - // pseudo.pseudo_increased_rarity - - // #% increased Burning Damage - // pseudo.pseudo_increased_burning_damage - - // # Life Regenerated per Second - // pseudo.pseudo_total_life_regen - - // #% of Life Regenerated per Second - // pseudo.pseudo_percent_life_regen - - // #% of Physical Attack Damage Leeched as Life - // pseudo.pseudo_physical_attack_damage_leeched_as_life - - // #% of Physical Attack Damage Leeched as Mana - // pseudo.pseudo_physical_attack_damage_leeched_as_mana - - // #% increased Mana Regeneration Rate - // pseudo.pseudo_increased_mana_regen - - // +# total to Level of Socketed Gems - // pseudo.pseudo_total_additional_gem_levels - - // +# total to Level of Socketed Elemental Gems - // pseudo.pseudo_total_additional_elemental_gem_levels - - // +# total to Level of Socketed Fire Gems - // pseudo.pseudo_total_additional_fire_gem_levels - - // +# total to Level of Socketed Cold Gems - // pseudo.pseudo_total_additional_cold_gem_levels - - // +# total to Level of Socketed Lightning Gems - // pseudo.pseudo_total_additional_lightning_gem_levels - - // +# total to Level of Socketed Chaos Gems - // pseudo.pseudo_total_additional_chaos_gem_levels - - // +# total to Level of Socketed Spell Gems - // pseudo.pseudo_total_additional_spell_gem_levels - - // +# total to Level of Socketed Projectile Gems - // pseudo.pseudo_total_additional_projectile_gem_levels - - // +# total to Level of Socketed Bow Gems - // pseudo.pseudo_total_additional_bow_gem_levels - - // +# total to Level of Socketed Melee Gems - // pseudo.pseudo_total_additional_melee_gem_levels - - // +# total to Level of Socketed Minion Gems - // pseudo.pseudo_total_additional_minion_gem_levels - - // +# total to Level of Socketed Strength Gems - // pseudo.pseudo_total_additional_strength_gem_levels - - // +# total to Level of Socketed Dexterity Gems - // pseudo.pseudo_total_additional_dexterity_gem_levels - - // +# total to Level of Socketed Intelligence Gems - // pseudo.pseudo_total_additional_intelligence_gem_levels - - // +# total to Level of Socketed Aura Gems - // pseudo.pseudo_total_additional_aura_gem_levels - - // +# total to Level of Socketed Movement Gems - // pseudo.pseudo_total_additional_movement_gem_levels - - // +# total to Level of Socketed Curse Gems - // pseudo.pseudo_total_additional_curse_gem_levels - - // +# total to Level of Socketed Vaal Gems - // pseudo.pseudo_total_additional_vaal_gem_levels - - // +# total to Level of Socketed Support Gems - // pseudo.pseudo_total_additional_support_gem_levels - - // +# total to Level of Socketed Skill Gems - // pseudo.pseudo_total_additional_skill_gem_levels - - // +# total to Level of Socketed Warcry Gems - // pseudo.pseudo_total_additional_warcry_gem_levels - - // +# total to Level of Socketed Golem Gems - // pseudo.pseudo_total_additional_golem_gem_levels - - // # Implicit Modifiers - // pseudo.pseudo_number_of_implicit_mods - - // # Prefix Modifiers - // pseudo.pseudo_number_of_prefix_mods - - // # Suffix Modifiers - // pseudo.pseudo_number_of_suffix_mods - - // # Modifiers - // pseudo.pseudo_number_of_affix_mods - - // # Crafted Prefix Modifiers - // pseudo.pseudo_number_of_crafted_prefix_mods - - // # Crafted Suffix Modifiers - // pseudo.pseudo_number_of_crafted_suffix_mods - - // # Crafted Modifiers - // pseudo.pseudo_number_of_crafted_mods - - // # Empty Prefix Modifiers - // pseudo.pseudo_number_of_empty_prefix_mods - - // # Empty Suffix Modifiers - // pseudo.pseudo_number_of_empty_suffix_mods - - // # Empty Modifiers - // pseudo.pseudo_number_of_empty_affix_mods - - // # Incubator Kills (Whispering) - // pseudo.pseudo_whispering_incubator_kills - - // # Incubator Kills (Fine) - // pseudo.pseudo_fine_incubator_kills - - // # Incubator Kills (Singular) - // pseudo.pseudo_singular_incubator_kills - - // # Incubator Kills (Cartographer's) - // pseudo.pseudo_cartographers_incubator_kills - - // # Incubator Kills (Otherwordly) - // pseudo.pseudo_otherworldly_incubator_kills - - // # Incubator Kills (Abyssal) - // pseudo.pseudo_abyssal_incubator_kills - - // # Incubator Kills (Fragmented) - // pseudo.pseudo_fragmented_incubator_kills - - // # Incubator Kills (Skittering) - // pseudo.pseudo_skittering_incubator_kills - - // # Incubator Kills (Infused) - // pseudo.pseudo_infused_incubator_kills - - // # Incubator Kills (Fossilised) - // pseudo.pseudo_fossilised_incubator_kills - - // # Incubator Kills (Decadent) - // pseudo.pseudo_decadent_incubator_kills - - // # Incubator Kills (Diviner's) - // pseudo.pseudo_diviners_incubator_kills - - // # Incubator Kills (Primal) - // pseudo.pseudo_primal_incubator_kills - - // # Incubator Kills (Enchanted) - // pseudo.pseudo_enchanted_incubator_kills - - // # Incubator Kills (Geomancer's) - // pseudo.pseudo_geomancers_incubator_kills - - // # Incubator Kills (Ornate) - // pseudo.pseudo_ornate_incubator_kills - - // # Incubator Kills (Time-Lost) - // pseudo.pseudo_timelost_incubator_kills - - // # Incubator Kills (Celestial Armoursmith's) - // pseudo.pseudo_celestial_armoursmiths_incubator_kills - - // # Incubator Kills (Celestial Blacksmith's) - // pseudo.pseudo_celestial_blacksmiths_incubator_kills - - // # Incubator Kills (Celestial Jeweller's) - // pseudo.pseudo_celestial_jewellers_incubator_kills - - // # Incubator Kills (Eldritch) - // pseudo.pseudo_eldritch_incubator_kills - - // # Incubator Kills (Obscured) - // pseudo.pseudo_obscured_incubator_kills - - // # Incubator Kills (Foreboding) - // pseudo.pseudo_foreboding_incubator_kills - - // # Incubator Kills (Thaumaturge's) - // pseudo.pseudo_thaumaturges_incubator_kills - - // # Incubator Kills (Mysterious) - // pseudo.pseudo_mysterious_incubator_kills - - // # Incubator Kills (Gemcutter's) - // pseudo.pseudo_gemcutters_incubator_kills - - // # Incubator Kills (Feral) - // pseudo.pseudo_feral_incubator_kills - - // # Fractured Modifiers - // pseudo.pseudo_number_of_fractured_mods - - // +#% Quality to Elemental Damage Modifiers - // pseudo.pseudo_jewellery_elemental_quality - - // +#% Quality to Caster Modifiers - // pseudo.pseudo_jewellery_caster_quality - - // +#% Quality to Attack Modifiers - // pseudo.pseudo_jewellery_attack_quality - - // +#% Quality to Defence Modifiers - // pseudo.pseudo_jewellery_defense_quality - - // +#% Quality to Life and Mana Modifiers - // pseudo.pseudo_jewellery_resource_quality - - // +#% Quality to Resistance Modifiers - // pseudo.pseudo_jewellery_resistance_quality - - // +#% Quality to Attribute Modifiers - // pseudo.pseudo_jewellery_attribute_quality - }; - - return groups; - } - */ - public List Parse(List lines) { var results = new List(); diff --git a/src/Sidekick.Apis.Poe/Parser/Pseudo/PseudoPattern.cs b/src/Sidekick.Apis.Poe/Parser/Pseudo/PseudoPattern.cs index 26d3b4cd6..2beb360ac 100644 --- a/src/Sidekick.Apis.Poe/Parser/Pseudo/PseudoPattern.cs +++ b/src/Sidekick.Apis.Poe/Parser/Pseudo/PseudoPattern.cs @@ -5,10 +5,10 @@ namespace Sidekick.Apis.Poe.Parser.Pseudo; public class PseudoPattern ( Regex regex, - int multiplier = 1 + double multiplier = 1 ) { public Regex Pattern { get; set; } = regex; - public int Multiplier { get; set; } = multiplier; + public double Multiplier { get; set; } = multiplier; } diff --git a/src/Sidekick.Apis.Poe/Trade/TradeFilterService.cs b/src/Sidekick.Apis.Poe/Trade/TradeFilterService.cs index 9f3ade2ed..73b67615f 100644 --- a/src/Sidekick.Apis.Poe/Trade/TradeFilterService.cs +++ b/src/Sidekick.Apis.Poe/Trade/TradeFilterService.cs @@ -42,7 +42,7 @@ public IEnumerable GetPseudoModifierFilters(Item item) Text = modifier.Text, Checked = false, Value = modifier.Value, - Modifiers = modifier.Modifiers, + WeightedSumModifiers = modifier.WeightedSumModifiers, }; } } diff --git a/src/Sidekick.Apis.Poe/Trade/TradeSearchService.cs b/src/Sidekick.Apis.Poe/Trade/TradeSearchService.cs index 2dcf78ba1..541e311ad 100644 --- a/src/Sidekick.Apis.Poe/Trade/TradeSearchService.cs +++ b/src/Sidekick.Apis.Poe/Trade/TradeSearchService.cs @@ -107,9 +107,15 @@ public async Task> Search(Item item, PropertyFilters? }; } - SetModifierFilters(query.Stats, modifierFilters); SetSocketFilters(item, query.Filters); - query.Stats.AddRange(GetPseudoStatFilterGroups(pseudoFilters)); + + var andGroup = GetAndStats(modifierFilters, pseudoFilters); + if (andGroup != null) query.Stats.Add(andGroup); + + var countGroup = GetCountStats(modifierFilters); + if (countGroup != null) query.Stats.Add(countGroup); + + query.Stats.AddRange(GetWeightedSumStats(pseudoFilters)); if (propertyFilters != null) { @@ -501,87 +507,114 @@ public async Task> Search(Item item, PropertyFilters? return hasValue ? filters : null; } - private static void SetModifierFilters(List stats, List? modifierFilters) + private StatFilterGroup? GetAndStats(List? modifierFilters, List? pseudoFilters) { - if (modifierFilters == null) - { - return; - } - var andGroup = new StatFilterGroup() { Type = StatType.And, }; - var countGroup = new StatFilterGroup() + if (modifierFilters != null) { - Type = StatType.Count, - Value = new SearchFilterValue() + foreach (var filter in modifierFilters) { - Min = 0, - }, - }; + if (filter.Checked != true || filter.Line.Modifiers.Count != 1) + { + continue; + } - foreach (var filter in modifierFilters) - { - if (filter.Checked != true) - { - continue; + andGroup.Filters.Add(new StatFilters() + { + Id = filter.Line.Modifiers.First().Id, + Value = new SearchFilterValue(filter), + }); } + } - if (filter.Line.Modifiers.Count == 1) + if (pseudoFilters != null) + { + foreach (var pseudoFilter in pseudoFilters) { - var modifier = filter.Line.Modifiers.FirstOrDefault(); - if (modifier == null) + if (pseudoFilter.Checked != true || string.IsNullOrEmpty(pseudoFilter.PseudoModifierId)) { continue; } andGroup.Filters.Add(new StatFilters() { - Id = modifier.Id, - Value = new SearchFilterValue(filter), + Id = pseudoFilter.PseudoModifierId, + Value = new SearchFilterValue() + { + Min = pseudoFilter.Min, + Max = pseudoFilter.Max, + }, }); - continue; } + } - var modifiers = filter.Line.Modifiers; - if (filter.ForceFirstCategory) - { - modifiers = modifiers.Where(x => x.Category == filter.FirstCategory).ToList(); - } - else if (modifiers.Any(x => x.Category == ModifierCategory.Explicit)) + if (andGroup.Filters.Count == 0) + { + return null; + } + + return andGroup; + } + + private StatFilterGroup? GetCountStats(List? modifierFilters) + { + var countGroup = new StatFilterGroup() + { + Type = StatType.Count, + Value = new SearchFilterValue() { - modifiers = modifiers.Where(x => x.Category == ModifierCategory.Explicit).ToList(); - } + Min = 0, + }, + }; - foreach (var modifier in modifiers) + if (modifierFilters != null) + { + foreach (var filter in modifierFilters) { - countGroup.Filters.Add(new StatFilters() + if (filter.Checked != true || filter.Line.Modifiers.Count <= 1) { - Id = modifier.Id, - Value = new SearchFilterValue(filter), - }); - } + continue; + } - if (countGroup.Value != null && modifiers.Any()) - { - countGroup.Value.Min += 1; + var modifiers = filter.Line.Modifiers; + if (filter.ForceFirstCategory) + { + modifiers = modifiers.Where(x => x.Category == filter.FirstCategory).ToList(); + } + else if (modifiers.Any(x => x.Category == ModifierCategory.Explicit)) + { + modifiers = modifiers.Where(x => x.Category == ModifierCategory.Explicit).ToList(); + } + + foreach (var modifier in modifiers) + { + countGroup.Filters.Add(new StatFilters() + { + Id = modifier.Id, + Value = new SearchFilterValue(filter), + }); + } + + if (countGroup.Value != null && modifiers.Any()) + { + countGroup.Value.Min += 1; + } } } - if (andGroup.Filters.Count > 0) + if (countGroup.Filters.Count == 0) { - stats.Add(andGroup); + return null; } - if (countGroup.Filters.Count > 0) - { - stats.Add(countGroup); - } + return countGroup; } - private List GetPseudoStatFilterGroups(List? pseudoFilters) + private List GetWeightedSumStats(List? pseudoFilters) { if (pseudoFilters == null) { @@ -592,7 +625,7 @@ private List GetPseudoStatFilterGroups(List GetPseudoStatFilterGroups(List /// Gets or sets a dictionary of modifiers. The key represents the modifier id from the API. The value represents its weighted sum value. /// - public Dictionary Modifiers { get; init; } = []; + public Dictionary WeightedSumModifiers { get; init; } = []; public required string Text { get; set; } From 8a0a37a97004af6e52b77383ad46d7c99bfd7d45 Mon Sep 17 00:00:00 2001 From: leMicin Date: Sun, 29 Dec 2024 02:06:22 -0500 Subject: [PATCH 5/7] Pseudo modifier cleanup --- .../Parser/Pseudo/PseudoDefinition.cs | 72 +++++++++++----- .../Parser/Pseudo/PseudoParser.cs | 37 ++------- .../Trade/Models/PseudoModifierFilter.cs | 20 +++-- .../Trade/Requests/Filters/ArmourFilters.cs | 8 +- .../Requests/Filters/EquipmentFilters.cs | 20 ++--- .../Trade/Requests/Filters/IStatFilter.cs | 6 -- .../Trade/Requests/Filters/MapFilters.cs | 10 +-- .../Trade/Requests/Filters/MiscFilters.cs | 8 +- .../Requests/Filters/RequirementFilters.cs | 8 +- .../Requests/Filters/SocketFilterOption.cs | 2 +- .../Trade/Requests/Filters/StatFilterGroup.cs | 2 +- ...earchFilterValue.cs => StatFilterValue.cs} | 12 +-- .../Trade/Requests/Filters/StatFilters.cs | 2 +- .../Trade/Requests/Filters/TradeFilters.cs | 2 +- .../Trade/Requests/Filters/TypeFilters.cs | 2 +- .../Trade/Requests/Filters/WeaponFilters.cs | 12 +-- .../Requests/Filters/WeightedStatFilter.cs | 6 -- .../Trade/TradeFilterService.cs | 4 +- .../Trade/TradeSearchService.cs | 82 +++++++++---------- .../Game/Items/PseudoModifier.cs | 2 +- .../Filters/PseudoFilterComponent.razor | 4 +- 21 files changed, 159 insertions(+), 162 deletions(-) delete mode 100644 src/Sidekick.Apis.Poe/Trade/Requests/Filters/IStatFilter.cs rename src/Sidekick.Apis.Poe/Trade/Requests/Filters/{SearchFilterValue.cs => StatFilterValue.cs} (66%) delete mode 100644 src/Sidekick.Apis.Poe/Trade/Requests/Filters/WeightedStatFilter.cs diff --git a/src/Sidekick.Apis.Poe/Parser/Pseudo/PseudoDefinition.cs b/src/Sidekick.Apis.Poe/Parser/Pseudo/PseudoDefinition.cs index 19ce78e91..691ab03e3 100644 --- a/src/Sidekick.Apis.Poe/Parser/Pseudo/PseudoDefinition.cs +++ b/src/Sidekick.Apis.Poe/Parser/Pseudo/PseudoDefinition.cs @@ -6,6 +6,8 @@ namespace Sidekick.Apis.Poe.Parser.Pseudo; public abstract class PseudoDefinition { + private static readonly Regex parseHashPattern = new("\\#"); + protected abstract bool Enabled { get; } protected abstract string? ModifierId { get; } @@ -14,33 +16,63 @@ public abstract class PseudoDefinition protected abstract Regex? Exception { get; } - public string Text => Modifiers.FirstOrDefault()?.Text ?? string.Empty; + private string? Text { get; set; } - public List Modifiers { get; set; } = new(); + public List Modifiers { get; private set; } = new(); - internal void AddModifierIfMatch(ApiModifier entry) + internal void InitializeDefinition(List apiCategories, List? localizedPseudoModifiers) { - if (!Enabled) + foreach (var apiCategory in apiCategories) { - return; + foreach (var apiModifier in apiCategory.Entries) + { + if (!Enabled) + { + return; + } + + if (Exception != null && Exception.IsMatch(apiModifier.Text)) + { + continue; + } + + foreach (var pattern in Patterns) + { + if (apiModifier.Id == null || apiModifier.Type == null || apiModifier.Text == null) + { + continue; + } + + if (pattern.Pattern.IsMatch(apiModifier.Text)) + { + Modifiers.Add(new PseudoModifierDefinition(apiModifier.Id, apiModifier.Type, apiModifier.Text, pattern.Multiplier)); + } + } + } } - if (Exception != null && Exception.IsMatch(entry.Text)) + Modifiers = Modifiers.OrderBy(x => x.Type switch + { + "pseudo" => 0, + "explicit" => 1, + "implicit" => 2, + "crafted" => 3, + "enchant" => 4, + "fractured" => 5, + "veiled" => 6, + _ => 7, + }) + .ThenBy(x => x.Text) + .ToList(); + + if (localizedPseudoModifiers != null && !string.IsNullOrEmpty(ModifierId)) { - return; + Text = localizedPseudoModifiers.FirstOrDefault(x => x.Id == ModifierId)?.Text ?? ""; } - foreach (var pattern in Patterns) + if (string.IsNullOrEmpty(Text)) { - if (entry.Id == null || entry.Type == null || entry.Text == null) - { - continue; - } - - if (pattern.Pattern.IsMatch(entry.Text)) - { - Modifiers.Add(new PseudoModifierDefinition(entry.Id, entry.Type, entry.Text, pattern.Multiplier)); - } + Text = string.Join(", ", Modifiers.Select(x => x.Text).Distinct().ToList()); } } @@ -53,8 +85,8 @@ internal void AddModifierIfMatch(ApiModifier entry) var result = new PseudoModifier() { - PseudoModifierId = ModifierId, - Text = Text, + ModifierId = ModifierId, + Text = Text ?? string.Empty, }; if (string.IsNullOrEmpty(ModifierId)) @@ -79,6 +111,8 @@ internal void AddModifierIfMatch(ApiModifier entry) } } + result.Value = (int)result.Value; + result.Text = parseHashPattern.Replace(result.Text, ((int)result.Value).ToString(), 1); return result; } diff --git a/src/Sidekick.Apis.Poe/Parser/Pseudo/PseudoParser.cs b/src/Sidekick.Apis.Poe/Parser/Pseudo/PseudoParser.cs index a5fa2cdf8..a21acd22d 100644 --- a/src/Sidekick.Apis.Poe/Parser/Pseudo/PseudoParser.cs +++ b/src/Sidekick.Apis.Poe/Parser/Pseudo/PseudoParser.cs @@ -1,4 +1,3 @@ -using System.Text.RegularExpressions; using Sidekick.Apis.Poe.Modifiers; using Sidekick.Apis.Poe.Parser.Pseudo.Definitions; using Sidekick.Common.Extensions; @@ -10,11 +9,10 @@ namespace Sidekick.Apis.Poe.Parser.Pseudo; public class PseudoParser ( IInvariantModifierProvider invariantModifierProvider, + IModifierProvider modifierProvider, ISettingsService settingsService ) : IPseudoParser { - private readonly Regex parseHashPattern = new("\\#"); - private List Definitions { get; } = new(); /// @@ -38,32 +36,13 @@ public async Task Initialize() ]); var categories = await invariantModifierProvider.GetList(); - foreach (var category in categories) - { - foreach (var entry in category.Entries) - { - foreach (var definition in Definitions) - { - definition.AddModifierIfMatch(entry); - } - } - } + categories.RemoveAll(x => x.Entries.FirstOrDefault()?.Id.StartsWith("pseudo") == true); + + var localizedPseudoModifiers = modifierProvider.Patterns.GetValueOrDefault(ModifierCategory.Pseudo); foreach (var definition in Definitions) { - definition.Modifiers = definition.Modifiers.OrderBy(x => x.Type switch - { - "pseudo" => 0, - "explicit" => 1, - "implicit" => 2, - "crafted" => 3, - "enchant" => 4, - "fractured" => 5, - "veiled" => 6, - _ => 7, - }) - .ThenBy(x => x.Text) - .ToList(); + definition.InitializeDefinition(categories, localizedPseudoModifiers); } } @@ -77,12 +56,6 @@ public List Parse(List lines) if (result != null) results.Add(result); } - results.ForEach(x => - { - x.Value = (int)x.Value; - x.Text = parseHashPattern.Replace(x.Text, ((int)x.Value).ToString(), 1); - }); - return results; } } diff --git a/src/Sidekick.Apis.Poe/Trade/Models/PseudoModifierFilter.cs b/src/Sidekick.Apis.Poe/Trade/Models/PseudoModifierFilter.cs index 6160cfc19..df384a4e2 100644 --- a/src/Sidekick.Apis.Poe/Trade/Models/PseudoModifierFilter.cs +++ b/src/Sidekick.Apis.Poe/Trade/Models/PseudoModifierFilter.cs @@ -2,8 +2,10 @@ namespace Sidekick.Apis.Poe.Trade.Models { - public class PseudoModifierFilter : PseudoModifier, ITradeFilter + public class PseudoModifierFilter : ITradeFilter { + public required PseudoModifier PseudoModifier { get; set; } + public bool? @Checked { get; set; } = false; public decimal? Min { get; set; } @@ -17,13 +19,13 @@ public class PseudoModifierFilter : PseudoModifier, ITradeFilter /// public void NormalizeMinValue() { - if (Value > 0) + if (PseudoModifier.Value > 0) { - Min = (int)Math.Max((1 - NormalizeValue) * Value, 0); + Min = (int)Math.Max((1 - NormalizeValue) * PseudoModifier.Value, 0); } else { - Min = (int)Math.Min((1 + NormalizeValue) * Value, 0); + Min = (int)Math.Min((1 + NormalizeValue) * PseudoModifier.Value, 0); } } @@ -32,13 +34,13 @@ public void NormalizeMinValue() /// public void NormalizeMaxValue() { - if (Value > 0) + if (PseudoModifier.Value > 0) { - Max = (int)Math.Max(Math.Max(Value + 1, (1 + NormalizeValue) * Value), 0); + Max = (int)Math.Max(Math.Max(PseudoModifier.Value + 1, (1 + NormalizeValue) * PseudoModifier.Value), 0); } else { - Max = (int)Math.Min(Math.Max(Value + 1, (1 - NormalizeValue) * Value), 0); + Max = (int)Math.Min(Math.Max(PseudoModifier.Value + 1, (1 - NormalizeValue) * PseudoModifier.Value), 0); } } @@ -47,8 +49,8 @@ public void NormalizeMaxValue() /// public void SetExactValue() { - Min = (int)Value; - Max = (int)Value; + Min = (int)PseudoModifier.Value; + Max = (int)PseudoModifier.Value; } } } diff --git a/src/Sidekick.Apis.Poe/Trade/Requests/Filters/ArmourFilters.cs b/src/Sidekick.Apis.Poe/Trade/Requests/Filters/ArmourFilters.cs index cf01063ad..cb18a8dea 100644 --- a/src/Sidekick.Apis.Poe/Trade/Requests/Filters/ArmourFilters.cs +++ b/src/Sidekick.Apis.Poe/Trade/Requests/Filters/ArmourFilters.cs @@ -5,15 +5,15 @@ namespace Sidekick.Apis.Poe.Trade.Requests.Filters internal class ArmourFilters { [JsonPropertyName("ar")] - public SearchFilterValue? Armor { get; set; } + public StatFilterValue? Armor { get; set; } [JsonPropertyName("es")] - public SearchFilterValue? EnergyShield { get; set; } + public StatFilterValue? EnergyShield { get; set; } [JsonPropertyName("ev")] - public SearchFilterValue? Evasion { get; set; } + public StatFilterValue? Evasion { get; set; } [JsonPropertyName("block")] - public SearchFilterValue? Block { get; set; } + public StatFilterValue? Block { get; set; } } } diff --git a/src/Sidekick.Apis.Poe/Trade/Requests/Filters/EquipmentFilters.cs b/src/Sidekick.Apis.Poe/Trade/Requests/Filters/EquipmentFilters.cs index 96e907951..73e4d53fe 100644 --- a/src/Sidekick.Apis.Poe/Trade/Requests/Filters/EquipmentFilters.cs +++ b/src/Sidekick.Apis.Poe/Trade/Requests/Filters/EquipmentFilters.cs @@ -5,33 +5,33 @@ namespace Sidekick.Apis.Poe.Trade.Requests.Filters internal class EquipmentFilters { [JsonPropertyName("ar")] - public SearchFilterValue? Armor { get; set; } + public StatFilterValue? Armor { get; set; } [JsonPropertyName("es")] - public SearchFilterValue? EnergyShield { get; set; } + public StatFilterValue? EnergyShield { get; set; } [JsonPropertyName("ev")] - public SearchFilterValue? Evasion { get; set; } + public StatFilterValue? Evasion { get; set; } [JsonPropertyName("block")] - public SearchFilterValue? Block { get; set; } + public StatFilterValue? Block { get; set; } [JsonPropertyName("crit")] - public SearchFilterValue? CriticalStrikeChance { get; set; } + public StatFilterValue? CriticalStrikeChance { get; set; } [JsonPropertyName("aps")] - public SearchFilterValue? AttacksPerSecond { get; set; } + public StatFilterValue? AttacksPerSecond { get; set; } [JsonPropertyName("dps")] - public SearchFilterValue? DamagePerSecond { get; set; } + public StatFilterValue? DamagePerSecond { get; set; } [JsonPropertyName("edps")] - public SearchFilterValue? ElementalDps { get; set; } + public StatFilterValue? ElementalDps { get; set; } [JsonPropertyName("pdps")] - public SearchFilterValue? PhysicalDps { get; set; } + public StatFilterValue? PhysicalDps { get; set; } [JsonPropertyName("damage")] - public SearchFilterValue? Damage { get; set; } + public StatFilterValue? Damage { get; set; } } } diff --git a/src/Sidekick.Apis.Poe/Trade/Requests/Filters/IStatFilter.cs b/src/Sidekick.Apis.Poe/Trade/Requests/Filters/IStatFilter.cs deleted file mode 100644 index 43a6a7856..000000000 --- a/src/Sidekick.Apis.Poe/Trade/Requests/Filters/IStatFilter.cs +++ /dev/null @@ -1,6 +0,0 @@ -namespace Sidekick.Apis.Poe.Trade.Requests.Filters; - -public interface IStatFilter -{ - -} diff --git a/src/Sidekick.Apis.Poe/Trade/Requests/Filters/MapFilters.cs b/src/Sidekick.Apis.Poe/Trade/Requests/Filters/MapFilters.cs index 57871446e..17b821438 100644 --- a/src/Sidekick.Apis.Poe/Trade/Requests/Filters/MapFilters.cs +++ b/src/Sidekick.Apis.Poe/Trade/Requests/Filters/MapFilters.cs @@ -5,19 +5,19 @@ namespace Sidekick.Apis.Poe.Trade.Requests.Filters internal class MapFilters { [JsonPropertyName("map_iiq")] - public SearchFilterValue? ItemQuantity { get; set; } + public StatFilterValue? ItemQuantity { get; set; } [JsonPropertyName("map_iir")] - public SearchFilterValue? ItemRarity { get; set; } + public StatFilterValue? ItemRarity { get; set; } [JsonPropertyName("area_level")] - public SearchFilterValue? AreaLevel { get; set; } + public StatFilterValue? AreaLevel { get; set; } [JsonPropertyName("map_tier")] - public SearchFilterValue? MapTier { get; set; } + public StatFilterValue? MapTier { get; set; } [JsonPropertyName("map_packsize")] - public SearchFilterValue? MonsterPackSize { get; set; } + public StatFilterValue? MonsterPackSize { get; set; } [JsonPropertyName("map_blighted")] public SearchFilterOption? Blighted { get; set; } diff --git a/src/Sidekick.Apis.Poe/Trade/Requests/Filters/MiscFilters.cs b/src/Sidekick.Apis.Poe/Trade/Requests/Filters/MiscFilters.cs index fdfa92cb5..bc243bd30 100644 --- a/src/Sidekick.Apis.Poe/Trade/Requests/Filters/MiscFilters.cs +++ b/src/Sidekick.Apis.Poe/Trade/Requests/Filters/MiscFilters.cs @@ -4,10 +4,10 @@ namespace Sidekick.Apis.Poe.Trade.Requests.Filters { internal class MiscFilters { - public SearchFilterValue? Quality { get; set; } + public StatFilterValue? Quality { get; set; } [JsonPropertyName("gem_level")] - public SearchFilterValue? GemLevel { get; set; } + public StatFilterValue? GemLevel { get; set; } [JsonPropertyName("gem_alternate_quality")] public SearchFilterOption? GemQualityType { get; set; } @@ -16,12 +16,12 @@ internal class MiscFilters /// The item level filter for Path of Exile 1 is inside the misc filters instead of the type filters. /// [JsonPropertyName("ilvl")] - public SearchFilterValue? ItemLevel { get; set; } + public StatFilterValue? ItemLevel { get; set; } public SearchFilterOption? Corrupted { get; set; } [JsonPropertyName("scourge_tier")] - public SearchFilterValue? Scourged { get; set; } + public StatFilterValue? Scourged { get; set; } [JsonPropertyName("elder_item")] public SearchFilterOption? ElderItem { get; set; } diff --git a/src/Sidekick.Apis.Poe/Trade/Requests/Filters/RequirementFilters.cs b/src/Sidekick.Apis.Poe/Trade/Requests/Filters/RequirementFilters.cs index ef04ac26a..aa2c6561f 100644 --- a/src/Sidekick.Apis.Poe/Trade/Requests/Filters/RequirementFilters.cs +++ b/src/Sidekick.Apis.Poe/Trade/Requests/Filters/RequirementFilters.cs @@ -5,15 +5,15 @@ namespace Sidekick.Apis.Poe.Trade.Requests.Filters internal class RequirementFilters { [JsonPropertyName("lvl")] - public SearchFilterValue? Level { get; set; } + public StatFilterValue? Level { get; set; } [JsonPropertyName("dex")] - public SearchFilterValue? Dexterity { get; set; } + public StatFilterValue? Dexterity { get; set; } [JsonPropertyName("str")] - public SearchFilterValue? Strength { get; set; } + public StatFilterValue? Strength { get; set; } [JsonPropertyName("int")] - public SearchFilterValue? Intelligence { get; set; } + public StatFilterValue? Intelligence { get; set; } } } diff --git a/src/Sidekick.Apis.Poe/Trade/Requests/Filters/SocketFilterOption.cs b/src/Sidekick.Apis.Poe/Trade/Requests/Filters/SocketFilterOption.cs index fa952b1d3..5a93ef5a8 100644 --- a/src/Sidekick.Apis.Poe/Trade/Requests/Filters/SocketFilterOption.cs +++ b/src/Sidekick.Apis.Poe/Trade/Requests/Filters/SocketFilterOption.cs @@ -2,7 +2,7 @@ namespace Sidekick.Apis.Poe.Trade.Requests.Filters { - internal class SocketFilterOption : SearchFilterValue + internal class SocketFilterOption : StatFilterValue { [JsonPropertyName("r")] public int? Red { get; set; } diff --git a/src/Sidekick.Apis.Poe/Trade/Requests/Filters/StatFilterGroup.cs b/src/Sidekick.Apis.Poe/Trade/Requests/Filters/StatFilterGroup.cs index e2ef7aa04..5c1478058 100644 --- a/src/Sidekick.Apis.Poe/Trade/Requests/Filters/StatFilterGroup.cs +++ b/src/Sidekick.Apis.Poe/Trade/Requests/Filters/StatFilterGroup.cs @@ -13,5 +13,5 @@ internal class StatFilterGroup public List Filters { get; set; } = new(); - public SearchFilterValue? Value { get; set; } + public StatFilterValue? Value { get; set; } } diff --git a/src/Sidekick.Apis.Poe/Trade/Requests/Filters/SearchFilterValue.cs b/src/Sidekick.Apis.Poe/Trade/Requests/Filters/StatFilterValue.cs similarity index 66% rename from src/Sidekick.Apis.Poe/Trade/Requests/Filters/SearchFilterValue.cs rename to src/Sidekick.Apis.Poe/Trade/Requests/Filters/StatFilterValue.cs index 431183f6c..8b7241540 100644 --- a/src/Sidekick.Apis.Poe/Trade/Requests/Filters/SearchFilterValue.cs +++ b/src/Sidekick.Apis.Poe/Trade/Requests/Filters/StatFilterValue.cs @@ -2,25 +2,25 @@ namespace Sidekick.Apis.Poe.Trade.Requests.Filters; -internal class SearchFilterValue : IStatFilter +internal class StatFilterValue { - internal SearchFilterValue() + internal StatFilterValue() { } - public SearchFilterValue(string? option) + public StatFilterValue(string? option) { Option = option; } - public SearchFilterValue(PropertyFilter filter) + public StatFilterValue(PropertyFilter filter) { Option = filter.Checked == true ? "true" : "false"; Min = filter.Min; Max = filter.Max; } - public SearchFilterValue(ModifierFilter filter) + public StatFilterValue(ModifierFilter filter) { Option = filter.Line.OptionValue; Min = filter.Min; @@ -32,4 +32,6 @@ public SearchFilterValue(ModifierFilter filter) public decimal? Min { get; set; } public decimal? Max { get; set; } + + public double? Weight { get; set; } } diff --git a/src/Sidekick.Apis.Poe/Trade/Requests/Filters/StatFilters.cs b/src/Sidekick.Apis.Poe/Trade/Requests/Filters/StatFilters.cs index 3c16971be..ca05d76cf 100644 --- a/src/Sidekick.Apis.Poe/Trade/Requests/Filters/StatFilters.cs +++ b/src/Sidekick.Apis.Poe/Trade/Requests/Filters/StatFilters.cs @@ -3,7 +3,7 @@ namespace Sidekick.Apis.Poe.Trade.Requests.Filters internal class StatFilters { public string? Id { get; set; } - public IStatFilter? Value { get; set; } + public StatFilterValue? Value { get; set; } public bool Disabled => false; } } diff --git a/src/Sidekick.Apis.Poe/Trade/Requests/Filters/TradeFilters.cs b/src/Sidekick.Apis.Poe/Trade/Requests/Filters/TradeFilters.cs index c6b5c5ebb..cf4f1ce9e 100644 --- a/src/Sidekick.Apis.Poe/Trade/Requests/Filters/TradeFilters.cs +++ b/src/Sidekick.Apis.Poe/Trade/Requests/Filters/TradeFilters.cs @@ -2,6 +2,6 @@ namespace Sidekick.Apis.Poe.Trade.Requests.Filters { internal class TradeFilters { - public SearchFilterValue? Price { get; set; } + public StatFilterValue? Price { get; set; } } } diff --git a/src/Sidekick.Apis.Poe/Trade/Requests/Filters/TypeFilters.cs b/src/Sidekick.Apis.Poe/Trade/Requests/Filters/TypeFilters.cs index bba327e25..5711cc0ff 100644 --- a/src/Sidekick.Apis.Poe/Trade/Requests/Filters/TypeFilters.cs +++ b/src/Sidekick.Apis.Poe/Trade/Requests/Filters/TypeFilters.cs @@ -12,6 +12,6 @@ internal class TypeFilters /// The item level filter for Path of Exile 2 is inside the type filters instead of the misc filters. /// [JsonPropertyName("ilvl")] - public SearchFilterValue? ItemLevel { get; set; } + public StatFilterValue? ItemLevel { get; set; } } } diff --git a/src/Sidekick.Apis.Poe/Trade/Requests/Filters/WeaponFilters.cs b/src/Sidekick.Apis.Poe/Trade/Requests/Filters/WeaponFilters.cs index f4286c045..19c6db45c 100644 --- a/src/Sidekick.Apis.Poe/Trade/Requests/Filters/WeaponFilters.cs +++ b/src/Sidekick.Apis.Poe/Trade/Requests/Filters/WeaponFilters.cs @@ -5,21 +5,21 @@ namespace Sidekick.Apis.Poe.Trade.Requests.Filters internal class WeaponFilters { [JsonPropertyName("crit")] - public SearchFilterValue? CriticalStrikeChance { get; set; } + public StatFilterValue? CriticalStrikeChance { get; set; } [JsonPropertyName("aps")] - public SearchFilterValue? AttacksPerSecond { get; set; } + public StatFilterValue? AttacksPerSecond { get; set; } [JsonPropertyName("dps")] - public SearchFilterValue? DamagePerSecond { get; set; } + public StatFilterValue? DamagePerSecond { get; set; } [JsonPropertyName("edps")] - public SearchFilterValue? ElementalDps { get; set; } + public StatFilterValue? ElementalDps { get; set; } [JsonPropertyName("pdps")] - public SearchFilterValue? PhysicalDps { get; set; } + public StatFilterValue? PhysicalDps { get; set; } [JsonPropertyName("damage")] - public SearchFilterValue? Damage { get; set; } + public StatFilterValue? Damage { get; set; } } } diff --git a/src/Sidekick.Apis.Poe/Trade/Requests/Filters/WeightedStatFilter.cs b/src/Sidekick.Apis.Poe/Trade/Requests/Filters/WeightedStatFilter.cs deleted file mode 100644 index eb18eff07..000000000 --- a/src/Sidekick.Apis.Poe/Trade/Requests/Filters/WeightedStatFilter.cs +++ /dev/null @@ -1,6 +0,0 @@ -namespace Sidekick.Apis.Poe.Trade.Requests.Filters; - -internal class WeightedStatFilter : IStatFilter -{ - public double? Weight { get; set; } -} diff --git a/src/Sidekick.Apis.Poe/Trade/TradeFilterService.cs b/src/Sidekick.Apis.Poe/Trade/TradeFilterService.cs index 73b67615f..ac57d5846 100644 --- a/src/Sidekick.Apis.Poe/Trade/TradeFilterService.cs +++ b/src/Sidekick.Apis.Poe/Trade/TradeFilterService.cs @@ -39,10 +39,8 @@ public IEnumerable GetPseudoModifierFilters(Item item) { yield return new PseudoModifierFilter() { - Text = modifier.Text, + PseudoModifier = modifier, Checked = false, - Value = modifier.Value, - WeightedSumModifiers = modifier.WeightedSumModifiers, }; } } diff --git a/src/Sidekick.Apis.Poe/Trade/TradeSearchService.cs b/src/Sidekick.Apis.Poe/Trade/TradeSearchService.cs index 541e311ad..664140f9b 100644 --- a/src/Sidekick.Apis.Poe/Trade/TradeSearchService.cs +++ b/src/Sidekick.Apis.Poe/Trade/TradeSearchService.cs @@ -102,7 +102,7 @@ public async Task> Search(Item item, PropertyFilters? { Filters = { - Price = new SearchFilterValue(currency), + Price = new StatFilterValue(currency), }, }; } @@ -197,32 +197,32 @@ public async Task> Search(Item item, PropertyFilters? switch (propertyFilter.Type) { case PropertyFilterType.Weapon_Damage: - filters.Filters.Damage = new SearchFilterValue(propertyFilter); + filters.Filters.Damage = new StatFilterValue(propertyFilter); hasValue = true; break; case PropertyFilterType.Weapon_PhysicalDps: - filters.Filters.PhysicalDps = new SearchFilterValue(propertyFilter); + filters.Filters.PhysicalDps = new StatFilterValue(propertyFilter); hasValue = true; break; case PropertyFilterType.Weapon_ElementalDps: - filters.Filters.ElementalDps = new SearchFilterValue(propertyFilter); + filters.Filters.ElementalDps = new StatFilterValue(propertyFilter); hasValue = true; break; case PropertyFilterType.Weapon_Dps: - filters.Filters.DamagePerSecond = new SearchFilterValue(propertyFilter); + filters.Filters.DamagePerSecond = new StatFilterValue(propertyFilter); hasValue = true; break; case PropertyFilterType.Weapon_AttacksPerSecond: - filters.Filters.AttacksPerSecond = new SearchFilterValue(propertyFilter); + filters.Filters.AttacksPerSecond = new StatFilterValue(propertyFilter); hasValue = true; break; case PropertyFilterType.Weapon_CriticalStrikeChance: - filters.Filters.CriticalStrikeChance = new SearchFilterValue(propertyFilter); + filters.Filters.CriticalStrikeChance = new StatFilterValue(propertyFilter); hasValue = true; break; } @@ -251,22 +251,22 @@ public async Task> Search(Item item, PropertyFilters? switch (propertyFilter.Type) { case PropertyFilterType.Armour_Armour: - filters.Filters.Armor = new SearchFilterValue(propertyFilter); + filters.Filters.Armor = new StatFilterValue(propertyFilter); hasValue = true; break; case PropertyFilterType.Armour_Block: - filters.Filters.Block = new SearchFilterValue(propertyFilter); + filters.Filters.Block = new StatFilterValue(propertyFilter); hasValue = true; break; case PropertyFilterType.Armour_EnergyShield: - filters.Filters.EnergyShield = new SearchFilterValue(propertyFilter); + filters.Filters.EnergyShield = new StatFilterValue(propertyFilter); hasValue = true; break; case PropertyFilterType.Armour_Evasion: - filters.Filters.Evasion = new SearchFilterValue(propertyFilter); + filters.Filters.Evasion = new StatFilterValue(propertyFilter); hasValue = true; break; } @@ -295,52 +295,52 @@ public async Task> Search(Item item, PropertyFilters? switch (propertyFilter.Type) { case PropertyFilterType.Weapon_Damage: - filters.Filters.Damage = new SearchFilterValue(propertyFilter); + filters.Filters.Damage = new StatFilterValue(propertyFilter); hasValue = true; break; case PropertyFilterType.Weapon_PhysicalDps: - filters.Filters.PhysicalDps = new SearchFilterValue(propertyFilter); + filters.Filters.PhysicalDps = new StatFilterValue(propertyFilter); hasValue = true; break; case PropertyFilterType.Weapon_ElementalDps: - filters.Filters.ElementalDps = new SearchFilterValue(propertyFilter); + filters.Filters.ElementalDps = new StatFilterValue(propertyFilter); hasValue = true; break; case PropertyFilterType.Weapon_Dps: - filters.Filters.DamagePerSecond = new SearchFilterValue(propertyFilter); + filters.Filters.DamagePerSecond = new StatFilterValue(propertyFilter); hasValue = true; break; case PropertyFilterType.Weapon_AttacksPerSecond: - filters.Filters.AttacksPerSecond = new SearchFilterValue(propertyFilter); + filters.Filters.AttacksPerSecond = new StatFilterValue(propertyFilter); hasValue = true; break; case PropertyFilterType.Weapon_CriticalStrikeChance: - filters.Filters.CriticalStrikeChance = new SearchFilterValue(propertyFilter); + filters.Filters.CriticalStrikeChance = new StatFilterValue(propertyFilter); hasValue = true; break; case PropertyFilterType.Armour_Armour: - filters.Filters.Armor = new SearchFilterValue(propertyFilter); + filters.Filters.Armor = new StatFilterValue(propertyFilter); hasValue = true; break; case PropertyFilterType.Armour_Block: - filters.Filters.Block = new SearchFilterValue(propertyFilter); + filters.Filters.Block = new StatFilterValue(propertyFilter); hasValue = true; break; case PropertyFilterType.Armour_EnergyShield: - filters.Filters.EnergyShield = new SearchFilterValue(propertyFilter); + filters.Filters.EnergyShield = new StatFilterValue(propertyFilter); hasValue = true; break; case PropertyFilterType.Armour_Evasion: - filters.Filters.Evasion = new SearchFilterValue(propertyFilter); + filters.Filters.Evasion = new StatFilterValue(propertyFilter); hasValue = true; break; } @@ -364,22 +364,22 @@ public async Task> Search(Item item, PropertyFilters? switch (propertyFilter.Type) { case PropertyFilterType.Map_ItemQuantity: - filters.Filters.ItemQuantity = new SearchFilterValue(propertyFilter); + filters.Filters.ItemQuantity = new StatFilterValue(propertyFilter); hasValue = true; break; case PropertyFilterType.Map_ItemRarity: - filters.Filters.ItemRarity = new SearchFilterValue(propertyFilter); + filters.Filters.ItemRarity = new StatFilterValue(propertyFilter); hasValue = true; break; case PropertyFilterType.Map_AreaLevel: - filters.Filters.AreaLevel = new SearchFilterValue(propertyFilter); + filters.Filters.AreaLevel = new StatFilterValue(propertyFilter); hasValue = true; break; case PropertyFilterType.Map_MonsterPackSize: - filters.Filters.MonsterPackSize = new SearchFilterValue(propertyFilter); + filters.Filters.MonsterPackSize = new StatFilterValue(propertyFilter); hasValue = true; break; @@ -394,7 +394,7 @@ public async Task> Search(Item item, PropertyFilters? break; case PropertyFilterType.Map_Tier: - filters.Filters.MapTier = new SearchFilterValue(propertyFilter); + filters.Filters.MapTier = new StatFilterValue(propertyFilter); hasValue = true; break; } @@ -475,30 +475,30 @@ public async Task> Search(Item item, PropertyFilters? // Misc case PropertyFilterType.Misc_Quality: - filters.Filters.Quality = new SearchFilterValue(propertyFilter); + filters.Filters.Quality = new StatFilterValue(propertyFilter); hasValue = true; break; case PropertyFilterType.Misc_GemLevel: if (invariantMetadataProvider.UncutGemIds.Contains(item.Metadata.Id)) { - filters.Filters.ItemLevel = new SearchFilterValue(propertyFilter); + filters.Filters.ItemLevel = new StatFilterValue(propertyFilter); } else { - filters.Filters.GemLevel = new SearchFilterValue(propertyFilter); + filters.Filters.GemLevel = new StatFilterValue(propertyFilter); } hasValue = true; break; case PropertyFilterType.Misc_ItemLevel: - filters.Filters.ItemLevel = new SearchFilterValue(propertyFilter); + filters.Filters.ItemLevel = new StatFilterValue(propertyFilter); hasValue = true; break; case PropertyFilterType.Misc_Scourged: - filters.Filters.Scourged = new SearchFilterValue(propertyFilter); + filters.Filters.Scourged = new StatFilterValue(propertyFilter); hasValue = true; break; } @@ -526,7 +526,7 @@ public async Task> Search(Item item, PropertyFilters? andGroup.Filters.Add(new StatFilters() { Id = filter.Line.Modifiers.First().Id, - Value = new SearchFilterValue(filter), + Value = new StatFilterValue(filter), }); } } @@ -535,15 +535,15 @@ public async Task> Search(Item item, PropertyFilters? { foreach (var pseudoFilter in pseudoFilters) { - if (pseudoFilter.Checked != true || string.IsNullOrEmpty(pseudoFilter.PseudoModifierId)) + if (pseudoFilter.Checked != true || string.IsNullOrEmpty(pseudoFilter.PseudoModifier.ModifierId)) { continue; } andGroup.Filters.Add(new StatFilters() { - Id = pseudoFilter.PseudoModifierId, - Value = new SearchFilterValue() + Id = pseudoFilter.PseudoModifier.ModifierId, + Value = new StatFilterValue() { Min = pseudoFilter.Min, Max = pseudoFilter.Max, @@ -565,7 +565,7 @@ public async Task> Search(Item item, PropertyFilters? var countGroup = new StatFilterGroup() { Type = StatType.Count, - Value = new SearchFilterValue() + Value = new StatFilterValue() { Min = 0, }, @@ -595,7 +595,7 @@ public async Task> Search(Item item, PropertyFilters? countGroup.Filters.Add(new StatFilters() { Id = modifier.Id, - Value = new SearchFilterValue(filter), + Value = new StatFilterValue(filter), }); } @@ -625,7 +625,7 @@ private List GetWeightedSumStats(List? ps foreach (var filter in pseudoFilters) { - if (filter.Checked != true || filter.WeightedSumModifiers.Count == 0) + if (filter.Checked != true || filter.PseudoModifier.WeightedSumModifiers.Count == 0) { continue; } @@ -633,19 +633,19 @@ private List GetWeightedSumStats(List? ps var group = new StatFilterGroup() { Type = StatType.WeightedSum, - Value = new SearchFilterValue() + Value = new StatFilterValue() { Min = filter.Min, Max = filter.Max, }, }; - foreach (var modifier in filter.WeightedSumModifiers) + foreach (var modifier in filter.PseudoModifier.WeightedSumModifiers) { group.Filters.Add(new StatFilters() { Id = modifier.Key, - Value = new WeightedStatFilter() + Value = new StatFilterValue() { Weight = modifier.Value, }, diff --git a/src/Sidekick.Common/Game/Items/PseudoModifier.cs b/src/Sidekick.Common/Game/Items/PseudoModifier.cs index c477978dc..fb4bf1d4a 100644 --- a/src/Sidekick.Common/Game/Items/PseudoModifier.cs +++ b/src/Sidekick.Common/Game/Items/PseudoModifier.cs @@ -2,7 +2,7 @@ namespace Sidekick.Common.Game.Items; public class PseudoModifier { - public string? PseudoModifierId { get; init; } + public string? ModifierId { get; init; } /// /// Gets or sets a dictionary of modifiers. The key represents the modifier id from the API. The value represents its weighted sum value. diff --git a/src/Sidekick.Modules.Trade/Components/Filters/PseudoFilterComponent.razor b/src/Sidekick.Modules.Trade/Components/Filters/PseudoFilterComponent.razor index e715387fb..460e9ed1a 100644 --- a/src/Sidekick.Modules.Trade/Components/Filters/PseudoFilterComponent.razor +++ b/src/Sidekick.Modules.Trade/Components/Filters/PseudoFilterComponent.razor @@ -10,7 +10,7 @@ NoMargin="true" ValueChanged="CheckedChanged"> @Filter.Text + Category="ModifierCategory.Pseudo">@Filter.PseudoModifier.Text @@ -19,7 +19,7 @@
- @if ((Filter.Checked ?? false) && Filter.Value != 0) + @if ((Filter.Checked ?? false) && Filter.PseudoModifier.Value != 0) { } From a5fded5a4b76bbbdd19cdc276db0a3ab1a7f9419 Mon Sep 17 00:00:00 2001 From: leMicin Date: Sun, 29 Dec 2024 09:41:10 -0500 Subject: [PATCH 6/7] Fixed tests --- src/Sidekick.Apis.Poe/Parser/Pseudo/PseudoDefinition.cs | 2 +- tests/Sidekick.Apis.Poe.Tests/Poe1/Parser/BodyArmourParsing.cs | 2 -- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/src/Sidekick.Apis.Poe/Parser/Pseudo/PseudoDefinition.cs b/src/Sidekick.Apis.Poe/Parser/Pseudo/PseudoDefinition.cs index 691ab03e3..c9a0d8cc9 100644 --- a/src/Sidekick.Apis.Poe/Parser/Pseudo/PseudoDefinition.cs +++ b/src/Sidekick.Apis.Poe/Parser/Pseudo/PseudoDefinition.cs @@ -101,7 +101,7 @@ internal void InitializeDefinition(List apiCategories, List definitionModifier.Id != itemModifier.Id)) + if (itemModifierLine.Modifiers.All(itemModifier => definitionModifier.Id != itemModifier.Id) || itemModifierLine.Values.Count == 0) { continue; } diff --git a/tests/Sidekick.Apis.Poe.Tests/Poe1/Parser/BodyArmourParsing.cs b/tests/Sidekick.Apis.Poe.Tests/Poe1/Parser/BodyArmourParsing.cs index a25d134f4..37e7a954e 100644 --- a/tests/Sidekick.Apis.Poe.Tests/Poe1/Parser/BodyArmourParsing.cs +++ b/tests/Sidekick.Apis.Poe.Tests/Poe1/Parser/BodyArmourParsing.cs @@ -58,9 +58,7 @@ can deny that my work has made quite the splash..."" actual.AssertHasModifier(ModifierCategory.Explicit, "#% increased Area Damage", 47); actual.AssertHasModifier(ModifierCategory.Explicit, "Extra gore"); - actual.AssertHasPseudoModifier("+12% total to all Elemental Resistances", 12); actual.AssertHasPseudoModifier("+36% total Elemental Resistance", 36); - actual.AssertHasPseudoModifier("+36% total Resistance", 36); actual.AssertHasPseudoModifier("+55 total maximum Life", 55); } From 168c8ce402327bf4d88ae373bab1b6a9e4870a16 Mon Sep 17 00:00:00 2001 From: leMicin Date: Sun, 29 Dec 2024 09:53:59 -0500 Subject: [PATCH 7/7] Added comment on Exception --- .../Parser/Pseudo/PseudoDefinition.cs | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/Sidekick.Apis.Poe/Parser/Pseudo/PseudoDefinition.cs b/src/Sidekick.Apis.Poe/Parser/Pseudo/PseudoDefinition.cs index c9a0d8cc9..b14fd6959 100644 --- a/src/Sidekick.Apis.Poe/Parser/Pseudo/PseudoDefinition.cs +++ b/src/Sidekick.Apis.Poe/Parser/Pseudo/PseudoDefinition.cs @@ -14,6 +14,16 @@ public abstract class PseudoDefinition protected abstract List Patterns { get; } + /// + /// Represents a regular expression pattern used to exclude certain modifier texts + /// during the processing of pseudo-modifier definitions in the Path of Exile API. + /// + /// + /// This property defines a regular expression that matches modifier texts + /// which should be excluded from further processing. Each derived class provides + /// a specific implementation of this property. It is utilized within the + /// initialization and parsing processes to filter out unwanted modifier entries. + /// protected abstract Regex? Exception { get; } private string? Text { get; set; }