#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.Generic; using System.Windows.Forms; using CodeImp.DoomBuilder.IO; using CodeImp.DoomBuilder.Map; #endregion namespace CodeImp.DoomBuilder.Config { public class ThingFlagsCompare { public enum CompareMethod { Equal, And }; #region ================== Constants #endregion #region ================== Variables private readonly string flag; private string requiredgroup; //mxd. This flag only works if at least one flag is set in the "requiredgroup" private string ignoredgroup; //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 string group; #endregion #region ================== Properties public string Flag { get { return flag; } } public string Group { get { return group; } } public string RequiredGroup { get { return requiredgroup; } internal set { requiredgroup = value; } } //mxd public string IgnoredGroup { get { return ignoredgroup; } internal set { ignoredgroup = value; } } //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; this.group = group; 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); requiredgroup = cfg.ReadSetting(cfgpath + ".requiredgroup", string.Empty); //mxd ignoredgroup = cfg.ReadSetting(cfgpath + ".ignoredgroup", string.Empty); //mxd 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. // Returns: // -1 if the flag does not overlap // 0 if the flag should be ignored // 1 if the flag overlaps public int Compare(Thing t1, Thing t2) { bool t1flag; bool t2flag; // Check if the flags exist if (!t1.Flags.ContainsKey(flag) || !t2.Flags.ContainsKey(flag)) return 0; //mxd. We should ignore the flag if requiredgroup doesn't have any flags set if(!string.IsNullOrEmpty(requiredgroup)) { bool t1hasrequiredflags = false; bool t2hasrequiredflags = false; foreach(string key in General.Map.Config.ThingFlagsCompare[requiredgroup].Keys) { if(t1.Flags.ContainsKey(key) && (General.Map.Config.ThingFlagsCompare[requiredgroup][key].invert ? !t1.Flags[key] : t1.Flags[key])) t1hasrequiredflags = true; if(t2.Flags.ContainsKey(key) && (General.Map.Config.ThingFlagsCompare[requiredgroup][key].invert ? !t2.Flags[key] : t2.Flags[key])) t2hasrequiredflags = true; } // Can't compare... if (!t1hasrequiredflags || !t2hasrequiredflags) return 0; } //mxd. We should ignore the flag if requiredflag is not set if(!string.IsNullOrEmpty(requiredflag)) { bool inverted = General.Map.Config.ThingFlagsCompare[group].ContainsKey(requiredflag) && General.Map.Config.ThingFlagsCompare[group][requiredflag].invert; bool t1hasrequiredflag = inverted ? !t1.Flags[requiredflag] : t1.Flags[requiredflag]; bool t2hasrequiredflag = inverted ? !t2.Flags[requiredflag] : t2.Flags[requiredflag]; // Can't compare... if(!t1hasrequiredflag || !t2hasrequiredflag) return 0; } //mxd. We should also ignore the flag if it's in ingoredgroup foreach(KeyValuePair> pair in General.Map.Config.ThingFlagsCompare) { foreach(KeyValuePair flaggrp in pair.Value) { if (!string.IsNullOrEmpty(flaggrp.Value.ignoredgroup) && group == flaggrp.Value.ignoredgroup) { bool t1ignoreflagset = flaggrp.Value.invert ? !t1.Flags[flaggrp.Key] : t1.Flags[flaggrp.Key]; bool t2ignoreflagset = flaggrp.Value.invert ? !t2.Flags[flaggrp.Key] : t2.Flags[flaggrp.Key]; // Can be ignored? if(!t1ignoreflagset && !t2ignoreflagset && flaggrp.Value.IgnoreGroupWhenUnset) continue; // Can't compare... if(!t1ignoreflagset || !t2ignoreflagset) return 0; } } } // Take flag inversion into account t1flag = invert ? !t1.Flags[flag] : t1.Flags[flag]; t2flag = invert ? !t2.Flags[flag] : t2.Flags[flag]; if (comparemethod == CompareMethod.And && (t1flag && t2flag)) return 1; if (comparemethod == CompareMethod.Equal && (t1flag == t2flag)) return 1; return 0; } //mxd public static string CheckThingEditFormFlags(List checkboxes) { Dictionary flags = new Dictionary(checkboxes.Count); Dictionary> flagspergroup = new Dictionary>(General.Map.Config.ThingFlagsCompare.Count); Dictionary requiredgroups = new Dictionary(); Dictionary ignoredgroups = new Dictionary(); // Gather flags foreach (CheckBox cb in checkboxes) { flags.Add(cb.Tag.ToString(), cb.CheckState != CheckState.Unchecked); } // Gather flags per group foreach (KeyValuePair> group in General.Map.Config.ThingFlagsCompare) { flagspergroup.Add(group.Key, new Dictionary()); foreach (KeyValuePair flaggrp in group.Value) { bool flagset = IsFlagSet(flags, flaggrp.Key, flaggrp.Value.invert) && (string.IsNullOrEmpty(flaggrp.Value.requiredflag) || IsFlagSet(flags, flaggrp.Value.requiredflag, group.Value[flaggrp.Value.requiredflag].invert)); if(flagset) { flagspergroup[group.Key].Add(flaggrp.Key, true); if(!string.IsNullOrEmpty(flaggrp.Value.requiredgroup) && !requiredgroups.ContainsKey(flaggrp.Value.requiredgroup)) requiredgroups.Add(flaggrp.Value.requiredgroup, false); } else if(flaggrp.Value.ingnorethisgroupwhenunset) { ignoredgroups.Add(group.Key, false); } } } // Check dependancies foreach (KeyValuePair> group in flagspergroup) { foreach(KeyValuePair flaggrp in group.Value) { if(!flaggrp.Value) continue; string ignoredgrp = General.Map.Config.ThingFlagsCompare[group.Key][flaggrp.Key].ignoredgroup; if (!string.IsNullOrEmpty(ignoredgrp) && !requiredgroups.ContainsKey(ignoredgrp)) { ignoredgroups.Add(ignoredgrp, false); } } } // Get rid of ignoredgroups foreach (KeyValuePair group in ignoredgroups) { flagspergroup.Remove(group.Key); } // Return message string result = string.Empty; foreach (KeyValuePair> group in flagspergroup) { if (group.Value.Count == 0) { switch(group.Key) { case "skills": result += "Thing is not used in any skill level."; break; case "gamemodes": result += "Thing is not used in any game mode."; break; case "classes": result += "Thing is not used by any class."; break; default: result += "At least one '" + group.Key + "' flag should be set."; break; } } } return result; } //mxd private static bool IsFlagSet(Dictionary flags, string flag, bool invert) { bool result = flags.ContainsKey(flag) && flags[flag]; return (invert ? !result : result); } #endregion } }