diff --git a/FilterExtension/ConfigNodes/Check.cs b/FilterExtension/ConfigNodes/Check.cs index 1b74f7d8..6e24216c 100644 --- a/FilterExtension/ConfigNodes/Check.cs +++ b/FilterExtension/ConfigNodes/Check.cs @@ -31,7 +31,8 @@ public enum CheckType maxTemp, profile, check, - subcategory + subcategory, + tag } public enum Equality @@ -57,7 +58,7 @@ public CheckParameters(CheckType Type, string TypeStr, bool Contains = false, bo } } - public static readonly Dictionary checkParams = new Dictionary(PartType.comparer) + public static readonly Dictionary checkParams = new Dictionary(StringComparer.OrdinalIgnoreCase) { { "name", new CheckParameters(CheckType.partName, "name") }, { "title", new CheckParameters(CheckType.partTitle, "title") }, @@ -79,11 +80,12 @@ public CheckParameters(CheckType Type, string TypeStr, bool Contains = false, bo { "maxTemp", new CheckParameters(CheckType.maxTemp, "maxTemp", false, true) }, { "profile", new CheckParameters(CheckType.profile, "profile", true) }, { "check", new CheckParameters(CheckType.check, "check") }, - { "subcategory", new CheckParameters(CheckType.subcategory, "subcategory") } + { "subcategory", new CheckParameters(CheckType.subcategory, "subcategory") }, + { "tag", new CheckParameters(CheckType.tag, "tag", true) } }; public CheckParameters type { get; set; } - public string[] value { get; set; } + public string[] values { get; set; } public bool invert { get; set; } public bool contains { get; set; } public Equality equality { get; set; } @@ -97,9 +99,9 @@ public Check(ConfigNode node) if (node.TryGetValue("value", ref tmpStr) && !string.IsNullOrEmpty(tmpStr)) { - value = tmpStr.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries); - for (int i = 0; i < value.Length; ++i) - value[i] = value[i].Trim(); + values = tmpStr.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries); + for (int i = 0; i < values.Length; ++i) + values[i] = values[i].Trim(); } if (node.TryGetValue("invert", ref tmpBool)) @@ -137,7 +139,7 @@ public Check(ConfigNode node) public Check(Check c) { type = c.type; - value = (string[])c.value.Clone(); + values = (string[])c.values.Clone(); invert = c.invert; contains = c.contains; @@ -149,9 +151,9 @@ public Check(Check c) public Check(string Type, string Value, bool Invert = false, bool Contains = true, Equality Compare = Equality.Equals) { type = getCheckType(Type); - value = Value.Split(','); - for (int i = 0; i < value.Length; ++i) - value[i] = value[i].Trim(); + values = Value.Split(','); + for (int i = 0; i < values.Length; ++i) + values[i] = values[i].Trim(); invert = Invert; contains = Contains; @@ -164,8 +166,8 @@ public ConfigNode toConfigNode() ConfigNode node = new ConfigNode("CHECK"); node.AddValue("type", type.typeString); - if (value != null) - node.AddValue("value", string.Join(",", value)); + if (values != null) + node.AddValue("value", string.Join(",", values)); node.AddValue("invert", invert.ToString()); if (type.usesContains) @@ -185,61 +187,61 @@ public bool checkPart(AvailablePart part, int depth = 0) switch (type.typeEnum) { case CheckType.moduleTitle: // check by module title - result = PartType.checkModuleTitle(part, value, contains); + result = PartType.checkModuleTitle(part, values, contains); break; case CheckType.moduleName: - result = PartType.checkModuleName(part, value, contains); + result = PartType.checkModuleName(part, values, contains); break; case CheckType.partName: // check by part name (cfg name) - result = PartType.checkName(part, value); + result = PartType.checkName(part, values); break; case CheckType.partTitle: // check by part title (in game name) - result = PartType.checkTitle(part, value); + result = PartType.checkTitle(part, values); break; case CheckType.resource: // check for a resource - result = PartType.checkResource(part, value, contains); + result = PartType.checkResource(part, values, contains); break; case CheckType.propellant: // check for engine propellant - result = PartType.checkPropellant(part, value, contains); + result = PartType.checkPropellant(part, values, contains); break; case CheckType.tech: // check by tech - result = PartType.checkTech(part, value); + result = PartType.checkTech(part, values); break; case CheckType.manufacturer: // check by manufacturer - result = PartType.checkManufacturer(part, value); + result = PartType.checkManufacturer(part, values); break; case CheckType.folder: // check by mod root folder - result = PartType.checkFolder(part, value); + result = PartType.checkFolder(part, values); break; case CheckType.path: // check by part folder location - result = PartType.checkPath(part, value); + result = PartType.checkPath(part, values); break; case CheckType.category: - result = PartType.checkCategory(part, value); + result = PartType.checkCategory(part, values); break; case CheckType.size: // check by largest stack node size - result = PartType.checkPartSize(part, value, contains, equality); + result = PartType.checkPartSize(part, values, contains, equality); break; case CheckType.crew: - result = PartType.checkCrewCapacity(part, value, equality); + result = PartType.checkCrewCapacity(part, values, equality); break; case CheckType.custom: // for when things get tricky - result = PartType.checkCustom(part, value); + result = PartType.checkCustom(part, values); break; case CheckType.mass: - result = PartType.checkMass(part, value, equality); + result = PartType.checkMass(part, values, equality); break; case CheckType.cost: - result = PartType.checkCost(part, value, equality); + result = PartType.checkCost(part, values, equality); break; case CheckType.crashTolerance: - result = PartType.checkCrashTolerance(part, value, equality); + result = PartType.checkCrashTolerance(part, values, equality); break; case CheckType.maxTemp: - result = PartType.checkTemperature(part, value, equality); + result = PartType.checkTemperature(part, values, equality); break; case CheckType.profile: - result = PartType.checkBulkHeadProfiles(part, value, contains); + result = PartType.checkBulkHeadProfiles(part, values, contains); break; case CheckType.check: for (int i = 0; i < checks.Count; i++ ) @@ -252,7 +254,10 @@ public bool checkPart(AvailablePart part, int depth = 0) } break; case CheckType.subcategory: - result = PartType.checkSubcategory(part, value, depth); + result = PartType.checkSubcategory(part, values, depth); + break; + case CheckType.tag: + result = PartType.checkTags(part, values, contains); break; default: Core.Log("invalid Check type specified"); @@ -279,11 +284,16 @@ public static CheckParameters getCheckType(string type) return tmpParams; } + public bool isEmpty() + { + return !checks.Any() || values == null || values.Length > 0; + } + public bool Equals(Check c2) { if (c2 == null) return false; - if (this.type == c2.type && this.value == c2.value && this.invert == c2.invert && this.contains == c2.contains && this.checks == c2.checks && this.equality == c2.equality) + if (this.type == c2.type && this.values == c2.values && this.invert == c2.invert && this.contains == c2.contains && this.checks == c2.checks && this.equality == c2.equality) return true; else return false; @@ -292,7 +302,7 @@ public bool Equals(Check c2) public override int GetHashCode() { int checks = this.checks.Any() ? this.checks.GetHashCode() : 1; - return this.type.GetHashCode() * this.value.GetHashCode() * this.invert.GetHashCode() * this.contains.GetHashCode() * this.equality.GetHashCode() * checks; + return this.type.GetHashCode() * this.values.GetHashCode() * this.invert.GetHashCode() * this.contains.GetHashCode() * this.equality.GetHashCode() * checks; } } } diff --git a/FilterExtension/ConfigNodes/Filter.cs b/FilterExtension/ConfigNodes/Filter.cs index 4cc7bfb4..8c9c0dfa 100644 --- a/FilterExtension/ConfigNodes/Filter.cs +++ b/FilterExtension/ConfigNodes/Filter.cs @@ -17,7 +17,7 @@ public Filter(ConfigNode node) { checks.Add(new Check(subNode)); } - checks.RemoveAll(c => c.value == null && !c.checks.Any()); + checks.RemoveAll(c => c.isEmpty()); bool tmp; bool.TryParse(node.GetValue("invert"), out tmp); @@ -29,7 +29,7 @@ public Filter(Filter f) checks = new List(); for (int i = 0; i < f.checks.Count; i++) { - if (f.checks[i].value != null) + if (!f.checks[i].isEmpty()) checks.Add(new Check(f.checks[i])); } diff --git a/FilterExtension/ConfigNodes/customSubCategory.cs b/FilterExtension/ConfigNodes/customSubCategory.cs index b3e3bb04..94ca2853 100644 --- a/FilterExtension/ConfigNodes/customSubCategory.cs +++ b/FilterExtension/ConfigNodes/customSubCategory.cs @@ -123,7 +123,7 @@ public static bool checkForCheckMatch(customSubCategory subcategory, Check.Check for (int k = 0; k < f.checks.Count; k++) { Check c = f.checks[k]; - if (c.type.typeEnum == type && c.value.Contains(value) && c.value.Length == 1 && c.invert == invert && c.contains == contains && c.equality == equality) + if (c.type.typeEnum == type && c.values.Contains(value) && c.values.Length == 1 && c.invert == invert && c.contains == contains && c.equality == equality) return true; } } diff --git a/FilterExtension/Utility/PartType.cs b/FilterExtension/Utility/PartType.cs index 5c6f242a..1fc06a05 100644 --- a/FilterExtension/Utility/PartType.cs +++ b/FilterExtension/Utility/PartType.cs @@ -9,7 +9,6 @@ namespace FilterExtensions.Utility public static class PartType { - public static CaseInsensitiveComparer comparer = new CaseInsensitiveComparer(); /// /// check the part against another subcategory. Hard limited to a depth of 10 /// @@ -60,45 +59,45 @@ public static bool checkCategory(AvailablePart part, string[] value) switch (part.category) { case PartCategories.Pods: - if (value.Contains("Pods", comparer)) + if (value.Contains("Pods", StringComparer.OrdinalIgnoreCase)) return true; break; case PartCategories.Propulsion: - if (value.Contains("Engines", comparer) && isEngine(part)) + if (value.Contains("Engines", StringComparer.OrdinalIgnoreCase) && isEngine(part)) return true; - if (value.Contains("Fuel Tanks", comparer) && !isEngine(part)) + if (value.Contains("Fuel Tanks", StringComparer.OrdinalIgnoreCase) && !isEngine(part)) return true; break; case PartCategories.Engine: - if (value.Contains("Engines", comparer)) + if (value.Contains("Engines", StringComparer.OrdinalIgnoreCase)) return true; break; case PartCategories.FuelTank: - if (value.Contains("Fuel Tanks", comparer)) + if (value.Contains("Fuel Tanks", StringComparer.OrdinalIgnoreCase)) return true; break; case PartCategories.Control: - if (value.Contains("Control", comparer)) + if (value.Contains("Control", StringComparer.OrdinalIgnoreCase)) return true; break; case PartCategories.Structural: - if (value.Contains("Structural", comparer)) + if (value.Contains("Structural", StringComparer.OrdinalIgnoreCase)) return true; break; case PartCategories.Aero: - if (value.Contains("Aerodynamics", comparer)) + if (value.Contains("Aerodynamics", StringComparer.OrdinalIgnoreCase)) return true; break; case PartCategories.Utility: - if (value.Contains("Utility", comparer)) + if (value.Contains("Utility", StringComparer.OrdinalIgnoreCase)) return true; break; case PartCategories.Science: - if (value.Contains("Science", comparer)) + if (value.Contains("Science", StringComparer.OrdinalIgnoreCase)) return true; break; case PartCategories.none: - if (value.Contains("None", comparer)) + if (value.Contains("None", StringComparer.OrdinalIgnoreCase)) return true; break; } @@ -114,10 +113,11 @@ public static bool checkModuleTitle(AvailablePart part, string[] values, bool co { if (part.moduleInfos == null) return false; + if (contains) - return part.moduleInfos.Any(m => values.Contains(m.moduleName, comparer)); + return part.moduleInfos.Any(m => values.Contains(m.moduleName, StringComparer.OrdinalIgnoreCase)); else - return part.moduleInfos.Any(m => !values.Contains(m.moduleName, comparer)); + return part.moduleInfos.Any(m => !values.Contains(m.moduleName, StringComparer.OrdinalIgnoreCase)); } /// @@ -322,7 +322,7 @@ public static bool checkModuleNameType(AvailablePart part, string value) /// public static bool checkName(AvailablePart part, string[] value) { - return value.Contains(part.name.Replace('.', '_'), comparer); + return value.Contains(part.name.Replace('.', '_'), StringComparer.OrdinalIgnoreCase); } /// @@ -414,12 +414,12 @@ public static bool checkPartSize(AvailablePart part, string[] value, bool contai { if (contains) { - if (value.Contains(node.size.ToString(), comparer)) + if (value.Contains(node.size.ToString(), StringComparer.OrdinalIgnoreCase)) return true; } else { - if (!value.Contains(node.size.ToString(), comparer)) + if (!value.Contains(node.size.ToString(), StringComparer.OrdinalIgnoreCase)) return true; } } @@ -456,7 +456,7 @@ public static bool checkCrewCapacity(AvailablePart part, string[] value, ConfigN return false; if (equality == ConfigNodes.Check.Equality.Equals) - return value.Contains(part.partPrefab.CrewCapacity.ToString(), comparer); + return value.Contains(part.partPrefab.CrewCapacity.ToString(), StringComparer.OrdinalIgnoreCase); else // only compare against the first value here { if (value.Length > 1) @@ -489,7 +489,7 @@ public static bool checkMass(AvailablePart part, string[] value, ConfigNodes.Che return false; if (equality == ConfigNodes.Check.Equality.Equals) - return value.Contains(part.partPrefab.mass.ToString(), comparer); + return value.Contains(part.partPrefab.mass.ToString(), StringComparer.OrdinalIgnoreCase); else { double d; @@ -510,7 +510,7 @@ public static bool checkMass(AvailablePart part, string[] value, ConfigNodes.Che public static bool checkCost(AvailablePart part, string[] value, ConfigNodes.Check.Equality equality) { if (equality == ConfigNodes.Check.Equality.Equals) - return value.Contains(part.cost.ToString(), comparer); + return value.Contains(part.cost.ToString(), StringComparer.OrdinalIgnoreCase); else { double d; @@ -558,7 +558,7 @@ public static bool checkTemperature(AvailablePart part, string[] value, ConfigNo return false; if (equality == ConfigNodes.Check.Equality.Equals) - return value.Contains(part.partPrefab.maxTemp.ToString(), comparer); + return value.Contains(part.partPrefab.maxTemp.ToString(), StringComparer.OrdinalIgnoreCase); else { double d; @@ -591,6 +591,21 @@ public static bool checkBulkHeadProfiles(AvailablePart part, string[] value, boo return false; } + public static bool checkTags(AvailablePart part, string[] value, bool contains) + { + if (string.IsNullOrEmpty(part.tags)) + return false; + + foreach (string s in part.tags.Split(new char[4] { ' ', ',', '|', ';' }, StringSplitOptions.RemoveEmptyEntries).Select(s => s.Trim()).Where(s => s!= string.Empty).ToArray()) + { + if (contains && value.Contains(s.Trim())) + return true; + if (!contains && !value.Contains(s.Trim())) + return true; + } + return false; + } + /// /// checks if the part can be used to control a vessel /// @@ -649,21 +664,5 @@ public static bool isAdapter(AvailablePart part) return false; return part.partPrefab.attachNodes[0].size != part.partPrefab.attachNodes[1].size; } - - /// - /// used for string contains where we don't care about the string case - /// - public class CaseInsensitiveComparer : IEqualityComparer - { - public bool Equals(string s1, string s2) - { - return string.Equals(s1, s2, StringComparison.OrdinalIgnoreCase); - } - - public int GetHashCode(string s) - { - return StringComparer.OrdinalIgnoreCase.GetHashCode(s); - } - } } } diff --git a/GameData/000_FilterExtensions/FilterExtensions.dll b/GameData/000_FilterExtensions/FilterExtensions.dll index 04d56696..3666868f 100644 Binary files a/GameData/000_FilterExtensions/FilterExtensions.dll and b/GameData/000_FilterExtensions/FilterExtensions.dll differ