diff --git a/IgnoreRules/CustomIgnoreSettings.cs b/IgnoreRules/CustomIgnoreSettings.cs new file mode 100644 index 0000000..3baf5b7 --- /dev/null +++ b/IgnoreRules/CustomIgnoreSettings.cs @@ -0,0 +1,39 @@ +using ExileCore.Shared.Attributes; +using ExileCore.Shared.Interfaces; +using ExileCore.Shared.Nodes; + +namespace MinimapIcons.IgnoreRules; + +public class CustomIgnoreSettings : ISettings +{ + [Menu("Enable Custom Ignore Rules")] + public ToggleNode EnableCustomIgnoreRules { get; set; } = new ToggleNode(true); + + [Menu("New Rule Type")] + public ListNode NewRuleType { get; set; } = new ListNode + { + Values = new System.Collections.Generic.List + { + "Metadata Exact", + "Metadata Starts With", + "Metadata Contains", + "Name Exact", + "Name Contains" + }, + Value = "Metadata Starts With" + }; + + [Menu("New Rule Pattern")] + public TextNode NewRulePattern { get; set; } = new TextNode(""); + + [Menu("Add Rule")] + public ButtonNode AddRule { get; set; } = new ButtonNode(); + + [Menu("Reload Rules from File")] + public ButtonNode ReloadRules { get; set; } = new ButtonNode(); + + [Menu("Open Config Folder")] + public ButtonNode OpenConfigFolder { get; set; } = new ButtonNode(); + + public ToggleNode Enable { get; set; } = new ToggleNode(true); +} diff --git a/IgnoreRules/IgnoreRule.cs b/IgnoreRules/IgnoreRule.cs new file mode 100644 index 0000000..ea6ebfa --- /dev/null +++ b/IgnoreRules/IgnoreRule.cs @@ -0,0 +1,60 @@ +using System; + +namespace MinimapIcons.IgnoreRules; + +public class IgnoreRule +{ + public IgnoreRuleType Type { get; set; } + public string Pattern { get; set; } + public bool IsEnabled { get; set; } = true; + + public IgnoreRule() + { + } + + public IgnoreRule(IgnoreRuleType type, string pattern, bool isEnabled = true) + { + Type = type; + Pattern = pattern; + IsEnabled = isEnabled; + } + + public bool Matches(string metadata, string renderName) + { + if (!IsEnabled || string.IsNullOrWhiteSpace(Pattern)) + return false; + + return Type switch + { + IgnoreRuleType.MetadataExact => metadata.Equals(Pattern, StringComparison.OrdinalIgnoreCase), + IgnoreRuleType.MetadataStartsWith => metadata.StartsWith(Pattern, StringComparison.OrdinalIgnoreCase), + IgnoreRuleType.MetadataContains => metadata.Contains(Pattern, StringComparison.OrdinalIgnoreCase), + IgnoreRuleType.NameExact => renderName.Equals(Pattern, StringComparison.OrdinalIgnoreCase), + IgnoreRuleType.NameContains => renderName.Contains(Pattern, StringComparison.OrdinalIgnoreCase), + _ => false + }; + } + + public override string ToString() + { + var prefix = Type switch + { + IgnoreRuleType.MetadataExact => "[META=]", + IgnoreRuleType.MetadataStartsWith => "[META^]", + IgnoreRuleType.MetadataContains => "[META*]", + IgnoreRuleType.NameExact => "[NAME=]", + IgnoreRuleType.NameContains => "[NAME*]", + _ => "[???]" + }; + return $"{prefix} {Pattern}"; + } +} + +public enum IgnoreRuleType +{ + MetadataExact, // Exact metadata match + MetadataStartsWith, // Metadata starts with + MetadataContains, // Metadata contains + NameExact, // Exact name match + NameContains // Name contains +} diff --git a/IgnoreRules/IgnoreRulesManager.cs b/IgnoreRules/IgnoreRulesManager.cs new file mode 100644 index 0000000..5ade124 --- /dev/null +++ b/IgnoreRules/IgnoreRulesManager.cs @@ -0,0 +1,181 @@ +using System.Collections.Generic; +using System.IO; + +namespace MinimapIcons.IgnoreRules; + +public class IgnoreRulesManager +{ + private readonly List _customRules = new(); + private readonly Dictionary _cache = new(); + private readonly string _customRulesFilePath; + + public IReadOnlyList CustomRules => _customRules.AsReadOnly(); + + public IgnoreRulesManager(string directoryFullName) + { + var configDir = Path.Combine(directoryFullName, "config"); + if (!Directory.Exists(configDir)) + Directory.CreateDirectory(configDir); + + _customRulesFilePath = Path.Combine(configDir, "custom_ignore_rules.txt"); + LoadCustomRules(); + } + + + public void LoadCustomRules() + { + _customRules.Clear(); + ClearCache(); + + if (!File.Exists(_customRulesFilePath)) + { + SaveCustomRules(); // Create default file + return; + } + + try + { + var lines = File.ReadAllLines(_customRulesFilePath); + foreach (var line in lines) + { + var trimmedLine = line.Trim(); + if (string.IsNullOrWhiteSpace(trimmedLine) || trimmedLine.StartsWith("#")) + continue; + + var rule = ParseRule(trimmedLine); + if (rule != null) + _customRules.Add(rule); + } + } + catch + { + // Silent fail - error will be handled by caller + } + } + + public void SaveCustomRules() + { + try + { + var lines = new List + { + "# Custom Ignore Rules for MinimapIcons", + "# Format examples:", + "# META=Metadata/Monsters/Exact/Path - Exact metadata match", + "# META^Metadata/Monsters/Prefix - Metadata starts with", + "# META*PartialMetadata - Metadata contains", + "# NAME=Exact Entity Name - Exact name match", + "# NAME*Partial Name - Name contains", + "# Lines starting with # are comments", + "# Prefix with ! to disable a rule without deleting it", + "" + }; + + foreach (var rule in _customRules) + { + var prefix = rule.Type switch + { + IgnoreRuleType.MetadataExact => "META=", + IgnoreRuleType.MetadataStartsWith => "META^", + IgnoreRuleType.MetadataContains => "META*", + IgnoreRuleType.NameExact => "NAME=", + IgnoreRuleType.NameContains => "NAME*", + _ => "META^" + }; + + var line = $"{(rule.IsEnabled ? "" : "!")}{prefix}{rule.Pattern}"; + lines.Add(line); + } + + File.WriteAllLines(_customRulesFilePath, lines); + } + catch + { + // Silent fail - error will be handled by caller + } + } + + private IgnoreRule ParseRule(string line) + { + var isEnabled = !line.StartsWith("!"); + if (!isEnabled) + line = line.Substring(1); + + IgnoreRuleType type; + string pattern; + + if (line.StartsWith("META=")) + { + type = IgnoreRuleType.MetadataExact; + pattern = line.Substring(5); + } + else if (line.StartsWith("META^")) + { + type = IgnoreRuleType.MetadataStartsWith; + pattern = line.Substring(5); + } + else if (line.StartsWith("META*")) + { + type = IgnoreRuleType.MetadataContains; + pattern = line.Substring(5); + } + else if (line.StartsWith("NAME=")) + { + type = IgnoreRuleType.NameExact; + pattern = line.Substring(5); + } + else if (line.StartsWith("NAME*")) + { + type = IgnoreRuleType.NameContains; + pattern = line.Substring(5); + } + else + { + // Default to MetadataStartsWith for backward compatibility + type = IgnoreRuleType.MetadataStartsWith; + pattern = line; + } + + return new IgnoreRule(type, pattern, isEnabled); + } + + public void AddRule(IgnoreRule rule) + { + _customRules.Add(rule); + ClearCache(); + SaveCustomRules(); + } + + public void RemoveRule(IgnoreRule rule) + { + _customRules.Remove(rule); + ClearCache(); + SaveCustomRules(); + } + + public void ClearCache() + { + _cache.Clear(); + } + + public bool ShouldIgnore(string metadata, string renderName) + { + var cacheKey = $"{metadata}|{renderName}"; + + if (_cache.TryGetValue(cacheKey, out var cached)) + return cached; + + // Check only custom rules + foreach (var rule in _customRules) + { + if (rule.Matches(metadata, renderName)) + { + _cache[cacheKey] = true; + return true; + } + } + + _cache[cacheKey] = false; + return false; + } +} diff --git a/MapIconsSettings.cs b/MapIconsSettings.cs index 4195147..2b1c1b0 100644 --- a/MapIconsSettings.cs +++ b/MapIconsSettings.cs @@ -2,6 +2,7 @@ using ExileCore.Shared.Interfaces; using ExileCore.Shared.Nodes; using MinimapIcons.IconsBuilder; +using MinimapIcons.IgnoreRules; namespace MinimapIcons; @@ -41,10 +42,13 @@ public class MapIconsSettings : ISettings Content = [ ], - EnableControls = true, + EnableControls = true, ItemFactory = () => new TextNode(""), UseFlatItems = true, }; + [Menu("Custom Ignore Rules", CollapsedByDefault = true)] + public CustomIgnoreSettings CustomIgnoreSettings { get; set; } = new(); + public IconsBuilderSettings IconsBuilderSettings { get; set; } = new(); } \ No newline at end of file diff --git a/MinimapIcons.cs b/MinimapIcons.cs index 47ce1fd..5e8852f 100644 --- a/MinimapIcons.cs +++ b/MinimapIcons.cs @@ -1,7 +1,3 @@ -using System; -using System.Collections.Generic; -using System.Drawing; -using System.Linq; using ExileCore; using ExileCore.PoEMemory.Components; using ExileCore.PoEMemory.Elements; @@ -10,6 +6,11 @@ using ExileCore.Shared.Enums; using ExileCore.Shared.Helpers; using MinimapIcons.IconsBuilder.Icons; +using MinimapIcons.IgnoreRules; +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq; using Color = SharpDX.Color; using RectangleF = SharpDX.RectangleF; using Vector2 = System.Numerics.Vector2; @@ -157,6 +158,7 @@ public class MinimapIcons : BaseSettingsPlugin private readonly Dictionary IgnoreCache = new Dictionary(); + private IgnoreRulesManager _ignoreRulesManager; private IngameUIElements _ingameUi; private bool? _largeMap; private float _mapScale; @@ -168,6 +170,7 @@ public class MinimapIcons : BaseSettingsPlugin public override bool Initialise() { + _ignoreRulesManager = new IgnoreRulesManager(DirectoryFullName); IconsBuilder.Initialise(); Settings.AlwaysShownIngameIcons.Content = Settings.AlwaysShownIngameIcons.Content.DistinctBy(x => x.Value).ToList(); Graphics.InitImage("sprites.png"); @@ -175,6 +178,48 @@ public override bool Initialise() CanUseMultiThreading = true; _iconListCache = CreateIconListCache(); Settings.IconListRefreshPeriod.OnValueChanged += (_, _) => _iconListCache = CreateIconListCache(); + + // Setup custom ignore rules UI handlers + Settings.CustomIgnoreSettings.ReloadRules.OnPressed += () => + { + _ignoreRulesManager.LoadCustomRules(); + DebugWindow.LogMsg("Custom ignore rules reloaded"); + }; + + Settings.CustomIgnoreSettings.OpenConfigFolder.OnPressed += () => + { + var configPath = System.IO.Path.Combine(DirectoryFullName, "config"); + Process.Start("explorer.exe", configPath); + }; + + Settings.CustomIgnoreSettings.AddRule.OnPressed += () => + { + var pattern = Settings.CustomIgnoreSettings.NewRulePattern.Value; + if (string.IsNullOrWhiteSpace(pattern)) + { + DebugWindow.LogMsg("Please enter a pattern for the rule"); + return; + } + + var ruleType = Settings.CustomIgnoreSettings.NewRuleType.Value switch + { + "Metadata Exact" => IgnoreRuleType.MetadataExact, + "Metadata Starts With" => IgnoreRuleType.MetadataStartsWith, + "Metadata Contains" => IgnoreRuleType.MetadataContains, + "Name Exact" => IgnoreRuleType.NameExact, + "Name Contains" => IgnoreRuleType.NameContains, + _ => IgnoreRuleType.MetadataStartsWith + }; + + var newRule = new IgnoreRule(ruleType, pattern); + _ignoreRulesManager.AddRule(newRule); + + DebugWindow.LogMsg($"Added ignore rule: {newRule}"); + + // Clear the pattern field after adding + Settings.CustomIgnoreSettings.NewRulePattern.Value = ""; + }; + return true; } @@ -229,9 +274,9 @@ public override Job Tick() public override void Render() { - if (_largeMap == null || + if (_largeMap == null || !GameController.InGame || - Settings.DrawOnlyOnLargeMap && _largeMap != true) + Settings.DrawOnlyOnLargeMap && _largeMap != true) return; if (!Settings.IgnoreFullscreenPanels && @@ -257,9 +302,19 @@ public override void Render() if (!Settings.DrawMonsters && icon.Entity.Type == EntityType.Monster) continue; + + // Use custom ignore rules if enabled + if (Settings.CustomIgnoreSettings.EnableCustomIgnoreRules) + { + if (_ignoreRulesManager.ShouldIgnore(icon.Entity.Path, icon.Entity.RenderName)) + continue; + } + + // hardcoded ignore list if (IgnoreCache.GetOrAdd(icon.Entity.Path, () => Ignored.Any(x => icon.Entity.Path.StartsWith(x)))) continue; + if (icon.Entity.Path.StartsWith( "Metadata/Monsters/AtlasExiles/BasiliskInfluenceMonsters/BasiliskBurrowingViper", StringComparison.Ordinal) && icon.Entity.Rarity != MonsterRarity.Unique) @@ -284,7 +339,7 @@ icon is not CustomIcon && var halfSize = size / 2f; icon.DrawRect = new RectangleF(position.X - halfSize, position.Y - halfSize, size, size); var drawRect = icon.DrawRect; - if (_largeMap == false && !_ingameUi.Map.SmallMiniMap.GetClientRectCache.Contains(drawRect)) + if (_largeMap == false && !_ingameUi.Map.SmallMiniMap.GetClientRectCache.Contains(drawRect)) continue; Graphics.DrawImage(iconValueMainTexture.FileName, drawRect, iconValueMainTexture.UV, iconValueMainTexture.Color.ToSharpDx()); diff --git a/config/custom_ignore_rules.txt b/config/custom_ignore_rules.txt new file mode 100644 index 0000000..d5eedc1 --- /dev/null +++ b/config/custom_ignore_rules.txt @@ -0,0 +1,25 @@ +# Custom Ignore Rules for MinimapIcons +# +# Rule formats: +# META=Metadata/Monsters/Exact/Path - Exact metadata match +# META^Metadata/Monsters/Prefix - Metadata starts with +# META*PartialMetadata - Metadata contains +# NAME=Exact Entity Name - Exact name match +# NAME*Partial Name - Name contains +# +# Lines starting with # are comments +# Prefix with ! to disable a rule without deleting it +# +# Examples: +# META*DoodadDaemon - Ignore all containing "DoodadDaemon" in metadata +# META^Metadata/Monsters/AtlasExiles/ - Ignore all starting with this path +# NAME*Volatile Core - Ignore by name containing "Volatile Core" +# !META^Metadata/Monsters/Disabled - Disabled rule (not applied) +# +# How to add entity to ignore: +# 1. Open plugin settings -> Custom Ignore Rules +# 2. Select rule type in "New Rule Type" +# 3. Enter pattern in "New Rule Pattern" +# 4. Click "Add Rule" +# +# Your rules below: