diff --git a/src/Sidekick.Apis.Poe/ITradeFilterService.cs b/src/Sidekick.Apis.Poe/ITradeFilterService.cs index 4fb38e76..b2e65f8e 100644 --- a/src/Sidekick.Apis.Poe/ITradeFilterService.cs +++ b/src/Sidekick.Apis.Poe/ITradeFilterService.cs @@ -24,6 +24,6 @@ public interface ITradeFilterService /// /// The item for which to get property filters. /// The property filters. - PropertyFilters GetPropertyFilters(Item item); + Task GetPropertyFilters(Item item); } } diff --git a/src/Sidekick.Apis.Poe/Metadata/IInvariantMetadataProvider.cs b/src/Sidekick.Apis.Poe/Metadata/IInvariantMetadataProvider.cs index ae726166..6357963b 100644 --- a/src/Sidekick.Apis.Poe/Metadata/IInvariantMetadataProvider.cs +++ b/src/Sidekick.Apis.Poe/Metadata/IInvariantMetadataProvider.cs @@ -1,10 +1,11 @@ using Sidekick.Common.Game.Items; using Sidekick.Common.Initialization; -namespace Sidekick.Apis.Poe.Metadata +namespace Sidekick.Apis.Poe.Metadata; + +public interface IInvariantMetadataProvider : IInitializableService { - public interface IInvariantMetadataProvider : IInitializableService - { - Dictionary IdDictionary { get; } - } + Dictionary IdDictionary { get; } + + List UncutGemIds { get; } } diff --git a/src/Sidekick.Apis.Poe/Metadata/InvariantMetadataProvider.cs b/src/Sidekick.Apis.Poe/Metadata/InvariantMetadataProvider.cs index f36f8e88..1327ee19 100644 --- a/src/Sidekick.Apis.Poe/Metadata/InvariantMetadataProvider.cs +++ b/src/Sidekick.Apis.Poe/Metadata/InvariantMetadataProvider.cs @@ -23,6 +23,8 @@ ISettingsService settingsService { public Dictionary IdDictionary { get; } = new(); + public List UncutGemIds { get; } = []; + /// public int Priority => 100; @@ -46,6 +48,8 @@ public async Task Initialize() { FillPattern(game, result.Result, category.Key, category.Value.Category); } + + InitializeUncutGemIds(); } private void FillPattern(GameType game, List categories, string id, Category category) @@ -93,5 +97,18 @@ private static Rarity GetRarityForCategory(Category category, ApiItem item) _ => Rarity.Unknown }; } + + private void InitializeUncutGemIds() + { + UncutGemIds.Clear(); + + foreach (var item in IdDictionary.Values) + { + if(item.Type == "Uncut Skill Gem" || item.Type == "Uncut Spirit Gem" || item.Type == "Uncut Support Gem") + { + UncutGemIds.Add(item.Id); + } + } + } } } diff --git a/src/Sidekick.Apis.Poe/Parser/Headers/HeaderParser.cs b/src/Sidekick.Apis.Poe/Parser/Headers/HeaderParser.cs index efa1b271..9d763ab2 100644 --- a/src/Sidekick.Apis.Poe/Parser/Headers/HeaderParser.cs +++ b/src/Sidekick.Apis.Poe/Parser/Headers/HeaderParser.cs @@ -1,11 +1,11 @@ +using System.Text.RegularExpressions; using FuzzySharp; using FuzzySharp.SimilarityRatio; using FuzzySharp.SimilarityRatio.Scorer.StrategySensitive; -using Microsoft.Extensions.Logging; using Sidekick.Apis.Poe.Filters; using Sidekick.Apis.Poe.Fuzzy; using Sidekick.Apis.Poe.Parser.Headers.Models; -using Sidekick.Common.Exceptions; +using Sidekick.Apis.Poe.Parser.Patterns; using Sidekick.Common.Game.Items; using Sidekick.Common.Game.Languages; @@ -15,15 +15,23 @@ public class HeaderParser ( IGameLanguageProvider gameLanguageProvider, IFuzzyService fuzzyService, - IFilterProvider filterProvider, - ILogger logger + IFilterProvider filterProvider ) : IHeaderParser { public int Priority => 100; private List ItemCategories { get; set; } = []; + private Dictionary RarityPatterns { get; set; } = []; + public Task Initialize() + { + InitializeItemCategories(); + InitializeRarityPatterns(); + return Task.CompletedTask; + } + + private void InitializeItemCategories() { ItemCategories = filterProvider.TypeCategoryOptions.ConvertAll(x => new ItemCategory() { @@ -31,26 +39,20 @@ public Task Initialize() Text = x.Text, FuzzyText = fuzzyService.CleanFuzzyText(x.Text), }); - - return Task.CompletedTask; } - public Header Parse(string itemText) + private void InitializeRarityPatterns() { - if (string.IsNullOrEmpty(itemText)) + RarityPatterns = new Dictionary { - throw new UnparsableException(); - } - - try - { - return Parse(new ParsingItem(itemText)); - } - catch (Exception e) - { - logger.LogWarning(e, "Could not parse item."); - throw new UnparsableException(); - } + { Rarity.Normal, gameLanguageProvider.Language.RarityNormal.ToRegexEndOfLine() }, + { Rarity.Magic, gameLanguageProvider.Language.RarityMagic.ToRegexEndOfLine() }, + { Rarity.Rare, gameLanguageProvider.Language.RarityRare.ToRegexEndOfLine() }, + { Rarity.Unique, gameLanguageProvider.Language.RarityUnique.ToRegexEndOfLine() }, + { Rarity.Currency, gameLanguageProvider.Language.RarityCurrency.ToRegexEndOfLine() }, + { Rarity.Gem, gameLanguageProvider.Language.RarityGem.ToRegexEndOfLine() }, + { Rarity.DivinationCard, gameLanguageProvider.Language.RarityDivinationCard.ToRegexEndOfLine() } + }; } public Header Parse(ParsingItem parsingItem) @@ -76,13 +78,38 @@ public Header Parse(ParsingItem parsingItem) apiItemCategoryId = null; } + string? type = null; + if (parsingItem.Blocks[0].Lines.Count >= 2) + { + type = parsingItem.Blocks[0].Lines[^1].Text; + } + + string? name = null; + if (parsingItem.Blocks[0].Lines.Count >= 3) + { + name = parsingItem.Blocks[0].Lines[^2].Text; + } + return new Header() { - Name = parsingItem.Blocks[0].Lines.ElementAtOrDefault(2)?.Text, - Type = parsingItem.Blocks[0].Lines.ElementAtOrDefault(3)?.Text, + Name = name, + Type = type, ItemCategory = apiItemCategoryId }; } + + public Rarity ParseRarity(ParsingItem parsingItem) + { + foreach (var pattern in RarityPatterns) + { + if (pattern.Value.IsMatch(parsingItem.Blocks[0].Lines[1].Text)) + { + return pattern.Key; + } + } + + return Rarity.Unknown; + } } diff --git a/src/Sidekick.Apis.Poe/Parser/Headers/IHeaderParser.cs b/src/Sidekick.Apis.Poe/Parser/Headers/IHeaderParser.cs index f3f57197..f3478d59 100644 --- a/src/Sidekick.Apis.Poe/Parser/Headers/IHeaderParser.cs +++ b/src/Sidekick.Apis.Poe/Parser/Headers/IHeaderParser.cs @@ -6,4 +6,6 @@ namespace Sidekick.Apis.Poe.Parser.Headers; public interface IHeaderParser : IInitializableService { Header Parse(ParsingItem parsingItem); + + Rarity ParseRarity(ParsingItem parsingItem); } diff --git a/src/Sidekick.Apis.Poe/Parser/Metadata/MetadataParser.cs b/src/Sidekick.Apis.Poe/Parser/Metadata/MetadataParser.cs index 9e72d9b5..bb0f6902 100644 --- a/src/Sidekick.Apis.Poe/Parser/Metadata/MetadataParser.cs +++ b/src/Sidekick.Apis.Poe/Parser/Metadata/MetadataParser.cs @@ -1,6 +1,6 @@ using System.Text.RegularExpressions; using Sidekick.Apis.Poe.Metadata; -using Sidekick.Apis.Poe.Parser.Patterns; +using Sidekick.Apis.Poe.Parser.Headers; using Sidekick.Common.Game.Items; using Sidekick.Common.Game.Languages; @@ -9,7 +9,7 @@ namespace Sidekick.Apis.Poe.Parser.Metadata public class MetadataParser ( IGameLanguageProvider gameLanguageProvider, - IParserPatterns parserPatterns, + IHeaderParser headerParser, IMetadataProvider data ) : IMetadataParser { @@ -50,7 +50,7 @@ public Task Initialize() var parsingBlock = parsingItem.Blocks.First(); parsingBlock.Parsed = true; - var itemRarity = GetRarity(parsingBlock); + var itemRarity = headerParser.ParseRarity(parsingItem); var canBeVaalGem = itemRarity == Rarity.Gem && parsingItem.Blocks.Count > 7; if (canBeVaalGem && data.NameAndTypeDictionary.TryGetValue(parsingItem.Blocks[5].Lines[0].Text, out var vaalGem)) @@ -169,18 +169,5 @@ public Task Initialize() return orderedResults.FirstOrDefault(); } - - private Rarity GetRarity(ParsingBlock parsingBlock) - { - foreach (var pattern in parserPatterns.Rarity) - { - if (pattern.Value.IsMatch(parsingBlock.Lines[1].Text)) - { - return pattern.Key; - } - } - - return Rarity.Unknown; - } } } diff --git a/src/Sidekick.Apis.Poe/Parser/Patterns/IParserPatterns.cs b/src/Sidekick.Apis.Poe/Parser/Patterns/IParserPatterns.cs index 2a4efc52..6050f996 100644 --- a/src/Sidekick.Apis.Poe/Parser/Patterns/IParserPatterns.cs +++ b/src/Sidekick.Apis.Poe/Parser/Patterns/IParserPatterns.cs @@ -1,5 +1,4 @@ using System.Text.RegularExpressions; -using Sidekick.Common.Game.Items; using Sidekick.Common.Initialization; namespace Sidekick.Apis.Poe.Parser.Patterns @@ -10,7 +9,6 @@ public interface IParserPatterns : IInitializableService Regex Elder { get; } Regex Hunter { get; } Regex Requirements { get; } - Dictionary Rarity { get; } Regex Redeemer { get; } Regex Shaper { get; } Regex Warlord { get; } diff --git a/src/Sidekick.Apis.Poe/Parser/Patterns/ParserPatterns.cs b/src/Sidekick.Apis.Poe/Parser/Patterns/ParserPatterns.cs index 13b4fbda..6b7f3bdd 100644 --- a/src/Sidekick.Apis.Poe/Parser/Patterns/ParserPatterns.cs +++ b/src/Sidekick.Apis.Poe/Parser/Patterns/ParserPatterns.cs @@ -12,7 +12,6 @@ public class ParserPatterns(IGameLanguageProvider gameLanguageProvider) : IParse /// public Task Initialize() { - InitHeader(); InitInfluences(); Requirements = gameLanguageProvider.Language.DescriptionRequirements.ToRegexLine(); @@ -22,27 +21,6 @@ public Task Initialize() #region Header (Rarity, Name, Type) - private void InitHeader() - { - Rarity = new Dictionary - { - { Common.Game.Items.Rarity.Normal, gameLanguageProvider.Language.RarityNormal.ToRegexEndOfLine() }, - { Common.Game.Items.Rarity.Magic, gameLanguageProvider.Language.RarityMagic.ToRegexEndOfLine() }, - { Common.Game.Items.Rarity.Rare, gameLanguageProvider.Language.RarityRare.ToRegexEndOfLine() }, - { Common.Game.Items.Rarity.Unique, gameLanguageProvider.Language.RarityUnique.ToRegexEndOfLine() }, - { Common.Game.Items.Rarity.Currency, gameLanguageProvider.Language.RarityCurrency.ToRegexEndOfLine() }, - { Common.Game.Items.Rarity.Gem, gameLanguageProvider.Language.RarityGem.ToRegexEndOfLine() }, - { Common.Game.Items.Rarity.DivinationCard, gameLanguageProvider.Language.RarityDivinationCard.ToRegexEndOfLine() } - }; - - } - - public Dictionary Rarity { get; private set; } = null!; - - public Regex Scourged { get; private set; } = null!; - - public Regex IsRelic { get; private set; } = null!; - public Regex Requirements { get; private set; } = null!; #endregion Header (Rarity, Name, Type) diff --git a/src/Sidekick.Apis.Poe/Trade/Models/PropertyFilter.cs b/src/Sidekick.Apis.Poe/Trade/Models/PropertyFilter.cs index a43d3cbf..4f38a0d6 100644 --- a/src/Sidekick.Apis.Poe/Trade/Models/PropertyFilter.cs +++ b/src/Sidekick.Apis.Poe/Trade/Models/PropertyFilter.cs @@ -9,23 +9,18 @@ public PropertyFilter( PropertyFilterType type, string text, object value, - decimal? min = null, - decimal? max = null) + double? normalizeValue) { Checked = @checked; Type = type; Text = text; Value = value; - Max = max; + NormalizeEnabled = normalizeValue != null; + NormalizeValue = normalizeValue ?? 0; - if (min.HasValue) + if (@checked == true) { - Min = min; - } - - if (max.HasValue) - { - Max = max; + NormalizeMinValue(); } } @@ -40,6 +35,9 @@ public PropertyFilter( public string Text { get; } public object Value { get; } + + private bool NormalizeEnabled { get; } + public double NormalizeValue { get; set; } public FilterValueType ValueType diff --git a/src/Sidekick.Apis.Poe/Trade/Requests/Filters/MiscFilters.cs b/src/Sidekick.Apis.Poe/Trade/Requests/Filters/MiscFilters.cs index 772482fe..fdfa92cb 100644 --- a/src/Sidekick.Apis.Poe/Trade/Requests/Filters/MiscFilters.cs +++ b/src/Sidekick.Apis.Poe/Trade/Requests/Filters/MiscFilters.cs @@ -12,6 +12,9 @@ internal class MiscFilters [JsonPropertyName("gem_alternate_quality")] public SearchFilterOption? GemQualityType { get; set; } + /// + /// 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; } diff --git a/src/Sidekick.Apis.Poe/Trade/Requests/Filters/TypeFilters.cs b/src/Sidekick.Apis.Poe/Trade/Requests/Filters/TypeFilters.cs index 2937fb4b..bba327e2 100644 --- a/src/Sidekick.Apis.Poe/Trade/Requests/Filters/TypeFilters.cs +++ b/src/Sidekick.Apis.Poe/Trade/Requests/Filters/TypeFilters.cs @@ -1,8 +1,17 @@ +using System.Text.Json.Serialization; + namespace Sidekick.Apis.Poe.Trade.Requests.Filters { internal class TypeFilters { public SearchFilterOption? Category { get; set; } + public SearchFilterOption? Rarity { get; set; } + + /// + /// 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; } } } diff --git a/src/Sidekick.Apis.Poe/Trade/TradeFilterService.cs b/src/Sidekick.Apis.Poe/Trade/TradeFilterService.cs index 0e4ebff6..6a21efb0 100644 --- a/src/Sidekick.Apis.Poe/Trade/TradeFilterService.cs +++ b/src/Sidekick.Apis.Poe/Trade/TradeFilterService.cs @@ -2,12 +2,14 @@ using Sidekick.Apis.Poe.Trade.Models; using Sidekick.Common.Game.Items; using Sidekick.Common.Game.Languages; +using Sidekick.Common.Settings; namespace Sidekick.Apis.Poe.Trade { public class TradeFilterService ( IGameLanguageProvider gameLanguageProvider, + ISettingsService settingsService, FilterResources resources ) : ITradeFilterService { @@ -39,7 +41,7 @@ public IEnumerable GetPseudoModifierFilters(Item item) } } - public PropertyFilters GetPropertyFilters(Item item) + public async Task GetPropertyFilters(Item item) { // No filters for currencies and divination cards, etc. if (item.Metadata.Category == Category.DivinationCard || item.Metadata.Category == Category.Currency || item.Metadata.Category == Category.ItemisedMonster || item.Metadata.Category == Category.Leaguestone || item.Metadata.Category == Category.Unknown) @@ -47,36 +49,37 @@ public PropertyFilters GetPropertyFilters(Item item) return new(); } + var normalizeValue = await settingsService.GetObject(SettingKeys.PriceCheckNormalizeValue); var result = new PropertyFilters(); // Armour properties - InitializeNumericFilter(result.Armour, PropertyFilterType.Armour_Armour, gameLanguageProvider.Language.DescriptionArmour, item.Properties.Armor); - InitializeNumericFilter(result.Armour, PropertyFilterType.Armour_Evasion, gameLanguageProvider.Language.DescriptionEvasion, item.Properties.Evasion); - InitializeNumericFilter(result.Armour, PropertyFilterType.Armour_EnergyShield, gameLanguageProvider.Language.DescriptionEnergyShield, item.Properties.EnergyShield); - InitializeNumericFilter(result.Armour, PropertyFilterType.Armour_Block, gameLanguageProvider.Language.DescriptionChanceToBlock, item.Properties.ChanceToBlock); + InitializeNumericFilter(result.Armour, PropertyFilterType.Armour_Armour, gameLanguageProvider.Language.DescriptionArmour, item.Properties.Armor, false, normalizeValue); + InitializeNumericFilter(result.Armour, PropertyFilterType.Armour_Evasion, gameLanguageProvider.Language.DescriptionEvasion, item.Properties.Evasion, false, normalizeValue); + InitializeNumericFilter(result.Armour, PropertyFilterType.Armour_EnergyShield, gameLanguageProvider.Language.DescriptionEnergyShield, item.Properties.EnergyShield, false, normalizeValue); + InitializeNumericFilter(result.Armour, PropertyFilterType.Armour_Block, gameLanguageProvider.Language.DescriptionChanceToBlock, item.Properties.ChanceToBlock, false, normalizeValue); // Weapon properties - InitializeNumericFilter(result.Weapon, PropertyFilterType.Weapon_Damage, resources.Filters_Damage, item.Properties.TotalDamage, @checked: false); - InitializeNumericFilter(result.Weapon, PropertyFilterType.Weapon_PhysicalDps, resources.Filters_PDps, item.Properties.PhysicalDps, @checked: false); - InitializeNumericFilter(result.Weapon, PropertyFilterType.Weapon_ElementalDps, resources.Filters_EDps, item.Properties.ElementalDps, @checked: false); - InitializeNumericFilter(result.Weapon, PropertyFilterType.Weapon_ChaosDps, resources.Filters_ChaosDps, item.Properties.ChaosDps, @checked: false); - InitializeNumericFilter(result.Weapon, PropertyFilterType.Weapon_Dps, resources.Filters_Dps, item.Properties.TotalDps, @checked: false); - InitializeNumericFilter(result.Weapon, PropertyFilterType.Weapon_AttacksPerSecond, gameLanguageProvider.Language.DescriptionAttacksPerSecond, item.Properties.AttacksPerSecond, @checked: false); - InitializeNumericFilter(result.Weapon, PropertyFilterType.Weapon_CriticalStrikeChance, gameLanguageProvider.Language.DescriptionCriticalStrikeChance, item.Properties.CriticalStrikeChance, @checked: false); + InitializeNumericFilter(result.Weapon, PropertyFilterType.Weapon_Damage, resources.Filters_Damage, item.Properties.TotalDamage, @checked: false, normalizeValue); + InitializeNumericFilter(result.Weapon, PropertyFilterType.Weapon_PhysicalDps, resources.Filters_PDps, item.Properties.PhysicalDps, @checked: false, normalizeValue); + InitializeNumericFilter(result.Weapon, PropertyFilterType.Weapon_ElementalDps, resources.Filters_EDps, item.Properties.ElementalDps, @checked: false, normalizeValue); + InitializeNumericFilter(result.Weapon, PropertyFilterType.Weapon_ChaosDps, resources.Filters_ChaosDps, item.Properties.ChaosDps, @checked: false, normalizeValue); + InitializeNumericFilter(result.Weapon, PropertyFilterType.Weapon_Dps, resources.Filters_Dps, item.Properties.TotalDps, @checked: false, normalizeValue); + InitializeNumericFilter(result.Weapon, PropertyFilterType.Weapon_AttacksPerSecond, gameLanguageProvider.Language.DescriptionAttacksPerSecond, item.Properties.AttacksPerSecond, false, normalizeValue); + InitializeNumericFilter(result.Weapon, PropertyFilterType.Weapon_CriticalStrikeChance, gameLanguageProvider.Language.DescriptionCriticalStrikeChance, item.Properties.CriticalStrikeChance, false, normalizeValue); // Map properties - InitializeNumericFilter(result.Map, PropertyFilterType.Map_ItemQuantity, gameLanguageProvider.Language.DescriptionItemQuantity, item.Properties.ItemQuantity); - InitializeNumericFilter(result.Map, PropertyFilterType.Map_ItemRarity, gameLanguageProvider.Language.DescriptionItemRarity, item.Properties.ItemRarity); - InitializeNumericFilter(result.Map, PropertyFilterType.Map_MonsterPackSize, gameLanguageProvider.Language.DescriptionMonsterPackSize, item.Properties.MonsterPackSize); + InitializeNumericFilter(result.Map, PropertyFilterType.Map_ItemQuantity, gameLanguageProvider.Language.DescriptionItemQuantity, item.Properties.ItemQuantity, false, normalizeValue); + InitializeNumericFilter(result.Map, PropertyFilterType.Map_ItemRarity, gameLanguageProvider.Language.DescriptionItemRarity, item.Properties.ItemRarity, false, normalizeValue); + InitializeNumericFilter(result.Map, PropertyFilterType.Map_MonsterPackSize, gameLanguageProvider.Language.DescriptionMonsterPackSize, item.Properties.MonsterPackSize, false, normalizeValue); InitializeBooleanFilter(result.Map, PropertyFilterType.Map_Blighted, gameLanguageProvider.Language.AffixBlighted, item.Properties.Blighted); InitializeBooleanFilter(result.Map, PropertyFilterType.Map_BlightRavaged, gameLanguageProvider.Language.AffixBlightRavaged, item.Properties.BlightRavaged); - InitializeNumericFilter(result.Map, PropertyFilterType.Map_Tier, gameLanguageProvider.Language.DescriptionMapTier, item.Properties.MapTier, @checked: true); - InitializeNumericFilter(result.Map, PropertyFilterType.Map_AreaLevel, gameLanguageProvider.Language.DescriptionAreaLevel, item.Properties.AreaLevel, @checked: true); + InitializeNumericFilter(result.Map, PropertyFilterType.Map_Tier, gameLanguageProvider.Language.DescriptionMapTier, item.Properties.MapTier, @checked: true, null); + InitializeNumericFilter(result.Map, PropertyFilterType.Map_AreaLevel, gameLanguageProvider.Language.DescriptionAreaLevel, item.Properties.AreaLevel, @checked: true, null); // Misc properties - InitializeNumericFilter(result.Misc, PropertyFilterType.Misc_Quality, gameLanguageProvider.Language.DescriptionQuality, item.Properties.Quality, @checked: item.Metadata.Rarity == Rarity.Gem); - InitializeNumericFilter(result.Misc, PropertyFilterType.Misc_GemLevel, gameLanguageProvider.Language.DescriptionLevel, item.Properties.GemLevel, @checked: true); - InitializeNumericFilter(result.Misc, PropertyFilterType.Misc_ItemLevel, gameLanguageProvider.Language.DescriptionItemLevel, item.Properties.ItemLevel, @checked: item.Properties.ItemLevel >= 80 && item.Properties.MapTier == 0 && item.Metadata.Rarity != Rarity.Unique); + InitializeNumericFilter(result.Misc, PropertyFilterType.Misc_Quality, gameLanguageProvider.Language.DescriptionQuality, item.Properties.Quality, @checked: item.Metadata.Rarity == Rarity.Gem, null); + InitializeNumericFilter(result.Misc, PropertyFilterType.Misc_GemLevel, gameLanguageProvider.Language.DescriptionLevel, item.Properties.GemLevel, @checked: true, null); + InitializeNumericFilter(result.Misc, PropertyFilterType.Misc_ItemLevel, gameLanguageProvider.Language.DescriptionItemLevel, item.Properties.ItemLevel, @checked: item.Properties.ItemLevel >= 80 && item.Properties.MapTier == 0 && item.Metadata.Rarity != Rarity.Unique, null); InitializeBooleanFilter(result.Misc, PropertyFilterType.Misc_Corrupted, gameLanguageProvider.Language.DescriptionCorrupted, item.Properties.Corrupted, true); // Influence properties @@ -95,23 +98,22 @@ private void InitializePropertyFilter( PropertyFilterType type, string? label, object value, - bool? @checked = null, - decimal? min = null, - decimal? max = null) + bool? @checked, + double? normalizeValue) { if (string.IsNullOrEmpty(label)) { return; } - filters.Add(new PropertyFilter(@checked, type, label, value, min, max)); + filters.Add(new PropertyFilter(@checked, type, label, value, normalizeValue)); } - private void InitializeNumericFilter(List filters, PropertyFilterType type, string? label, double? value, bool @checked = false) + private void InitializeNumericFilter(List filters, PropertyFilterType type, string? label, double? value, bool @checked, double? normalizeValue) { if (value > 0) { - InitializePropertyFilter(filters, type, label, value.Value, @checked: @checked); + InitializePropertyFilter(filters, type, label, value.Value, @checked: @checked, normalizeValue); } } @@ -119,7 +121,7 @@ private void InitializeBooleanFilter(List filters, PropertyFilte { if (force || @checked) { - InitializePropertyFilter(filters, type, label, @checked || force, @checked: @checked); + InitializePropertyFilter(filters, type, label, @checked || force, @checked: @checked, 0); } } } diff --git a/src/Sidekick.Apis.Poe/Trade/TradeSearchService.cs b/src/Sidekick.Apis.Poe/Trade/TradeSearchService.cs index be7eb326..3a86b23d 100644 --- a/src/Sidekick.Apis.Poe/Trade/TradeSearchService.cs +++ b/src/Sidekick.Apis.Poe/Trade/TradeSearchService.cs @@ -5,6 +5,7 @@ using Sidekick.Apis.Poe.Clients; using Sidekick.Apis.Poe.Clients.Models; using Sidekick.Apis.Poe.Filters; +using Sidekick.Apis.Poe.Metadata; using Sidekick.Apis.Poe.Modifiers; using Sidekick.Apis.Poe.Trade.Models; using Sidekick.Apis.Poe.Trade.Requests; @@ -27,7 +28,8 @@ public class TradeSearchService ISettingsService settingsService, IPoeTradeClient poeTradeClient, IModifierProvider modifierProvider, - IFilterProvider filterProvider + IFilterProvider filterProvider, + IInvariantMetadataProvider invariantMetadataProvider ) : ITradeSearchService { private readonly ILogger logger = logger; @@ -51,7 +53,7 @@ public async Task> Search(Item item, PropertyFilters? Discriminator = metadata.ApiTypeDiscriminator, }; } - else if (!string.IsNullOrEmpty(item.Header.ItemCategory)) + else { query.Type = metadata.ApiType; } @@ -118,6 +120,16 @@ public async Task> Search(Item item, PropertyFilters? query.Filters.MiscFilters = GetMiscFilters(item, propertyFilters.Misc); } + // The item level filter for Path of Exile 2 is inside the type filters instead of the misc filters. + if (item.Metadata.Game == GameType.PathOfExile2) + { + query.Filters.TypeFilters.Filters.ItemLevel = query.Filters.MiscFilters?.Filters.ItemLevel; + if (query.Filters.MiscFilters != null) + { + query.Filters.MiscFilters.Filters.ItemLevel = null; + } + } + var leagueId = await settingsService.GetString(SettingKeys.LeagueId); var uri = new Uri($"{await GetBaseApiUrl(metadata.Game)}search/{leagueId.GetUrlSlugForLeague()}"); @@ -462,7 +474,15 @@ public async Task> Search(Item item, PropertyFilters? break; case PropertyFilterType.Misc_GemLevel: - filters.Filters.GemLevel = new SearchFilterValue(propertyFilter); + if (invariantMetadataProvider.UncutGemIds.Contains(item.Metadata.Id)) + { + filters.Filters.ItemLevel = new SearchFilterValue(propertyFilter); + } + else + { + filters.Filters.GemLevel = new SearchFilterValue(propertyFilter); + } + hasValue = true; break; diff --git a/src/Sidekick.Common.Ui/wwwroot/css/app.css b/src/Sidekick.Common.Ui/wwwroot/css/app.css index 166a6436..37a29a7e 100644 --- a/src/Sidekick.Common.Ui/wwwroot/css/app.css +++ b/src/Sidekick.Common.Ui/wwwroot/css/app.css @@ -1508,11 +1508,6 @@ video { margin-bottom: 0px; } -.my-1 { - margin-top: 0.25rem; - margin-bottom: 0.25rem; -} - .my-2 { margin-top: 0.5rem; margin-bottom: 0.5rem; @@ -1540,10 +1535,6 @@ video { margin-bottom: 0.75rem; } -.mb-4 { - margin-bottom: 1rem; -} - .ml-0 { margin-left: 0px; } @@ -1568,22 +1559,10 @@ video { margin-left: auto; } -.mr-1 { - margin-right: 0.25rem; -} - .mr-2 { margin-right: 0.5rem; } -.mr-3 { - margin-right: 0.75rem; -} - -.mr-4 { - margin-right: 1rem; -} - .mt-1 { margin-top: 0.25rem; } @@ -1600,10 +1579,6 @@ video { margin-top: 1rem; } -.mt-6 { - margin-top: 1.5rem; -} - .mt-9 { margin-top: 2.25rem; } @@ -1928,10 +1903,6 @@ video { flex-grow: 1; } -.flex-grow-0 { - flex-grow: 0; -} - .grow { flex-grow: 1; } @@ -3321,20 +3292,11 @@ video { padding-bottom: 0.5rem; } -.py-3 { - padding-top: 0.75rem; - padding-bottom: 0.75rem; -} - .py-4 { padding-top: 1rem; padding-bottom: 1rem; } -.pb-0 { - padding-bottom: 0px; -} - .pb-2 { padding-bottom: 0.5rem; } @@ -3355,10 +3317,6 @@ video { padding-bottom: 4px; } -.pl-12 { - padding-left: 3rem; -} - .pl-2 { padding-left: 0.5rem; } @@ -3375,22 +3333,10 @@ video { padding-right: 0.5rem; } -.pr-4 { - padding-right: 1rem; -} - -.pt-0 { - padding-top: 0px; -} - .pt-2 { padding-top: 0.5rem; } -.pt-4 { - padding-top: 1rem; -} - .pt-\[4px\] { padding-top: 4px; } diff --git a/src/Sidekick.Common/Game/Items/Item.cs b/src/Sidekick.Common/Game/Items/Item.cs index 2a6736f2..4fd57a96 100644 --- a/src/Sidekick.Common/Game/Items/Item.cs +++ b/src/Sidekick.Common/Game/Items/Item.cs @@ -44,7 +44,9 @@ public class Item( Category.Contract => true, Category.Logbook => true, Category.Affliction => true, - _ => ModifierLines.Count != 0, + + // In PoE2, the uncut gems are currency. But we still want to display the gem levels. + _ => ModifierLines.Count != 0 || Properties.GemLevel > 0, }; /// diff --git a/src/Sidekick.Modules.Trade/Components/Items/ItemComponent.razor b/src/Sidekick.Modules.Trade/Components/Items/ItemComponent.razor index d357803c..f0997869 100644 --- a/src/Sidekick.Modules.Trade/Components/Items/ItemComponent.razor +++ b/src/Sidekick.Modules.Trade/Components/Items/ItemComponent.razor @@ -3,7 +3,7 @@ @using Sidekick.Modules.Trade.Localization @using Sidekick.Modules.Trade.Components.Prices -@if (Item.CanHaveModifiers) +@if (Item.CanHaveModifiers || PriceCheckService.Item?.Metadata.Category == Category.Gem) {
} -
+
diff --git a/src/Sidekick.Modules.Trade/Components/Items/ItemRequirements.razor b/src/Sidekick.Modules.Trade/Components/Items/ItemRequirements.razor index 1dba6cc1..72b71305 100644 --- a/src/Sidekick.Modules.Trade/Components/Items/ItemRequirements.razor +++ b/src/Sidekick.Modules.Trade/Components/Items/ItemRequirements.razor @@ -1,7 +1,11 @@ @using Sidekick.Apis.Poe.Trade.Models @using Sidekick.Modules.Trade.Localization -@Resources["Requires"] @((MarkupString)Text) +@if (!string.IsNullOrEmpty(Text)) +{ + @Resources["Requires"] @((MarkupString)Text) +} @inject IStringLocalizer Resources @@ -36,6 +40,8 @@ Text = Text.Replace(value.Value, $"{value.Value}"); } } + + Text = Text.Trim(); } } diff --git a/src/Sidekick.Modules.Trade/PriceCheckService.cs b/src/Sidekick.Modules.Trade/PriceCheckService.cs index e74d6367..a69e7c0c 100644 --- a/src/Sidekick.Modules.Trade/PriceCheckService.cs +++ b/src/Sidekick.Modules.Trade/PriceCheckService.cs @@ -53,7 +53,7 @@ public async Task Initialize(string itemText) Item = await itemParser.ParseItemAsync(itemText.DecodeBase64Url() ?? string.Empty); - PropertyFilters = tradeFilterService.GetPropertyFilters(Item); + PropertyFilters = await tradeFilterService.GetPropertyFilters(Item); ModifierFilters = tradeFilterService .GetModifierFilters(Item) .ToList(); @@ -74,7 +74,7 @@ public async Task Initialize(string itemText) IsFilterLoading = false; FilterLoadingChanged?.Invoke(); - if (Item.Metadata.Rarity != Rarity.Rare && Item.Metadata.Rarity != Rarity.Magic) + if (Item.Metadata.Rarity != Rarity.Rare && Item.Metadata.Rarity != Rarity.Magic && Item.Metadata.Category != Category.Gem) { if (CurrentMode == TradeMode.Bulk) { diff --git a/tests/Sidekick.Apis.Poe.Tests/Poe2/Parser/FlaskParsing.cs b/tests/Sidekick.Apis.Poe.Tests/Poe2/Parser/FlaskParsing.cs index d8fb2107..b30dddbf 100644 --- a/tests/Sidekick.Apis.Poe.Tests/Poe2/Parser/FlaskParsing.cs +++ b/tests/Sidekick.Apis.Poe.Tests/Poe2/Parser/FlaskParsing.cs @@ -39,6 +39,6 @@ Currently has 0 Charges Assert.Equal(66, actual.Properties.ItemLevel); actual.AssertHasModifier(ModifierCategory.Explicit, "#% of Recovery applied Instantly", 23); - actual.AssertHasModifier(ModifierCategory.Explicit, "#% increased Charges per use", 26); + // actual.AssertHasModifier(ModifierCategory.Explicit, "#% increased Charges per use", 26); } }