#region ================== Copyright (c) 2007 Pascal vd Heiden /* * Copyright (c) 2007 Pascal vd Heiden, www.codeimp.com * This program is released under GNU General Public License * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * */ #endregion #region ================== Namespaces using System; using System.Collections; using System.Collections.Generic; using CodeImp.DoomBuilder.IO; using CodeImp.DoomBuilder.Map; #endregion namespace CodeImp.DoomBuilder.Config { //mxd public class ThingFlagsCompareGroup { public readonly string Name; public readonly bool IsOptional; // When set to true, group flags won't be considered as required for a thing to show up ingame by CheckUnusedThings error check and ThingFlagsCompare.CheckThingEditFormFlags() method. public readonly Dictionary Flags; public ThingFlagsCompareGroup(Configuration cfg, string name) { Name = name; Flags = new Dictionary(); IsOptional = cfg.ReadSetting("thingflagscompare." + name + ".optional", false); IDictionary dic = cfg.ReadSetting("thingflagscompare." + name, new Hashtable()); foreach(DictionaryEntry de in dic) { if(de.Value != null && !(de.Value is IDictionary)) continue; // flag either has no value, or is defined as block string flag = de.Key.ToString(); // Duplicate flags check if(Flags.ContainsKey(flag)) General.ErrorLogger.Add(ErrorType.Warning, "ThingFlagsCompare flag '" + flag + "' is double-defined in '" + name + "' group"); Flags[flag] = new ThingFlagsCompare(cfg, name, flag); } } // Compares flags group of the two things. public ThingFlagsCompareResult Compare(Thing t1, Thing t2) { ThingFlagsCompareResult result = new ThingFlagsCompareResult(); foreach(ThingFlagsCompare tfc in Flags.Values) { // Current flag doesn't overlap when required flag does not overlap if(!string.IsNullOrEmpty(tfc.RequiredFlag) && !GetFlag(tfc.RequiredFlag).Compare(t1, t2)) { result.Result = -1; continue; } // Compare current flag bool flagoverlaps = tfc.Compare(t1, t2); // Ignore this group when whole group doens't match or required flag is not set if(!flagoverlaps && tfc.IgnoreGroupWhenUnset) return new ThingFlagsCompareResult { Result = 0 }; // If current flag overlaps, check IgnoredGroup and RequiredGroup settings if(flagoverlaps) { result.Result = 1; foreach(string s in tfc.IgnoredGroups) { if(!result.IgnoredGroups.Contains(s)) result.IgnoredGroups.Add(s); } if(tfc.RequiredGroups.Count > 0) { foreach(string s in tfc.RequiredGroups) { if(result.IgnoredGroups.Contains(s)) result.IgnoredGroups.Remove(s); if(!result.RequiredGroups.Contains(s)) result.RequiredGroups.Add(s); } } } } return result; } public ThingFlagsCompare GetFlag(string flag) { // Check our flags if(Flags.ContainsKey(flag)) return Flags[flag]; // Check other groups foreach(ThingFlagsCompareGroup group in General.Map.Config.ThingFlagsCompare.Values) { if(group != this && group.Flags.ContainsKey(flag)) return group.Flags[flag]; } // Fial... return null; } } //mxd public class ThingFlagsCompareResult { public readonly HashSet IgnoredGroups; public readonly HashSet RequiredGroups; // -1 if group does not overlap // 0 if group should be ignored // 1 if group overlaps public int Result; public ThingFlagsCompareResult() { Result = -1; IgnoredGroups = new HashSet(); RequiredGroups = new HashSet(); } } public class ThingFlagsCompare { private enum CompareMethod { Equal, And }; #region ================== Constants #endregion #region ================== Variables private readonly string flag; private readonly HashSet requiredgroups; //mxd. This flag only works if at least one flag is set in the "requiredgroup" private readonly HashSet ignoredgroups; //mxd. If this flag is set, flags from ignoredgroup can be... well... ignored! private string requiredflag; //mxd. This flag only works if requiredflag is set. private readonly bool ingnorethisgroupwhenunset; //mxd private readonly CompareMethod comparemethod; private readonly bool invert; private readonly char[] comma = new[] {','}; #endregion #region ================== Properties public string Flag { get { return flag; } } public HashSet RequiredGroups { get { return requiredgroups; } } //mxd public HashSet IgnoredGroups { get { return ignoredgroups; } } //mxd public string RequiredFlag { get { return requiredflag; } internal set { requiredflag = value; } } //mxd public bool IgnoreGroupWhenUnset { get { return ingnorethisgroupwhenunset; } } //mxd #endregion #region ================== Constructor / Disposer // Constructor public ThingFlagsCompare(Configuration cfg, string group, string flag) { string cfgpath = "thingflagscompare." + group + "." + flag; this.flag = flag; string cm = cfg.ReadSetting(cfgpath + ".comparemethod", "and"); switch(cm) { default: General.ErrorLogger.Add(ErrorType.Warning, "Unrecognized value \"" + cm + "\" for comparemethod in " + cfgpath + " in game configuration " + cfg.ReadSetting("game", "") + ". Defaulting to \"and\"."); goto case "and"; case "and": comparemethod = CompareMethod.And; break; case "equal": comparemethod = CompareMethod.Equal; break; } invert = cfg.ReadSetting(cfgpath + ".invert", false); //mxd requiredgroups = new HashSet(); string[] requiredgroupsarr = cfg.ReadSetting(cfgpath + ".requiredgroups", string.Empty).Split(comma, StringSplitOptions.RemoveEmptyEntries); foreach(string s in requiredgroupsarr) if(!requiredgroups.Contains(s)) requiredgroups.Add(s); //mxd ignoredgroups = new HashSet(); string[] ignoredgroupsarr = cfg.ReadSetting(cfgpath + ".ignoredgroups", string.Empty).Split(comma, StringSplitOptions.RemoveEmptyEntries); foreach(string s in ignoredgroupsarr) if(!ignoredgroups.Contains(s)) ignoredgroups.Add(s); requiredflag = cfg.ReadSetting(cfgpath + ".requiredflag", string.Empty); //mxd ingnorethisgroupwhenunset = cfg.ReadSetting(cfgpath + ".ingnorethisgroupwhenunset", false); //mxd // We have no destructor GC.SuppressFinalize(this); } #endregion #region ================== Methods // Compares the flag of the two things. public bool Compare(Thing t1, Thing t2) { //mxd. Get flags bool t1flag = (invert ? !t1.IsFlagSet(flag) : t1.IsFlagSet(flag)); bool t2flag = (invert ? !t2.IsFlagSet(flag) : t2.IsFlagSet(flag)); //mxd. Ignore the flag when ingnorethisgroupwhenunset is set and both flags are unset if(!t1flag && !t2flag && ingnorethisgroupwhenunset) return false; //mxd. Compare them switch(comparemethod) { case CompareMethod.And: return t1flag && t2flag; case CompareMethod.Equal: return t1flag == t2flag; default: throw new NotImplementedException("Unknown compare method!"); } } //mxd public static List CheckFlags(HashSet flags) { Dictionary> flagspergroup = new Dictionary>(General.Map.Config.ThingFlagsCompare.Count); HashSet ignoredgroups = new HashSet(); // Gather flags per group foreach(KeyValuePair group in General.Map.Config.ThingFlagsCompare) { flagspergroup.Add(group.Key, new HashSet()); foreach(ThingFlagsCompare flag in group.Value.Flags.Values) { if(IsFlagSet(flags, flag.flag, flag.invert) && (string.IsNullOrEmpty(flag.requiredflag) || IsFlagSet(flags, flag.requiredflag, group.Value.GetFlag(flag.requiredflag).invert))) { flagspergroup[group.Key].Add(flag.Flag); foreach(string s in flag.ignoredgroups) if(!ignoredgroups.Contains(s)) ignoredgroups.Add(s); } else if(flag.ingnorethisgroupwhenunset) { flagspergroup.Remove(group.Key); break; } } } // Check required dependancies foreach(KeyValuePair> group in flagspergroup) { foreach(string flag in group.Value) { foreach(string s in General.Map.Config.ThingFlagsCompare[group.Key].Flags[flag].requiredgroups) { if(ignoredgroups.Contains(s)) ignoredgroups.Remove(s); } } } // Get rid of ignoredgroups foreach(string s in ignoredgroups) flagspergroup.Remove(s); // Return message List result = new List(); foreach(KeyValuePair> group in flagspergroup) { if(group.Value.Count == 0 && !General.Map.Config.ThingFlagsCompare[group.Key].IsOptional) { switch(group.Key) { case "skills": result.Add("Thing is not used in any skill level."); break; case "gamemodes": result.Add("Thing is not used in any game mode."); break; case "classes": result.Add("Thing is not used by any class."); break; default: result.Add("At least one '" + group.Key + "' flag should be set."); break; } } } return result; } //mxd private static bool IsFlagSet(HashSet flags, string flag, bool invert) { bool result = flags.Contains(flag); return (invert ? !result : result); } #endregion } }