From f986f5c98dbd6131482ac197d01dbd3d3e9db710 Mon Sep 17 00:00:00 2001
From: MaxED <j.maxed@gmail.com>
Date: Mon, 3 Nov 2014 13:02:59 +0000
Subject: [PATCH] Fixed, game configurations: Boom game configurations used
 incorrect "thingflagscompare" block. Fixed, Thing edit form: implemented
 overcomplicated required flags check (which should work as expected this
 time. probably.).

---
 Build/Configurations/Includes/Boom_common.cfg |   2 +-
 Build/Configurations/Includes/Boom_misc.cfg   |  43 +++--
 Build/Configurations/Includes/Doom_misc.cfg   |   2 +
 Build/Configurations/Includes/Hexen_misc.cfg  |  12 +-
 Build/Configurations/Includes/UDMF_misc.cfg   |  12 +-
 Source/Core/Config/GameConfiguration.cs       |  28 +++
 Source/Core/Config/ThingsFlagsCompare.cs      | 167 ++++++++++++++++--
 Source/Core/Windows/ThingEditForm.cs          |  46 +----
 Source/Core/Windows/ThingEditFormUDMF.cs      |  48 ++---
 9 files changed, 246 insertions(+), 114 deletions(-)

diff --git a/Build/Configurations/Includes/Boom_common.cfg b/Build/Configurations/Includes/Boom_common.cfg
index 2f3151886..9a6dc4137 100644
--- a/Build/Configurations/Includes/Boom_common.cfg
+++ b/Build/Configurations/Includes/Boom_common.cfg
@@ -97,7 +97,7 @@ mapformat_doom
 		// How to compare thing flags (for the stuck things error checker)
 	thingflagscompare
 	{
-		include("Doom_misc.cfg", "thingflagscompare");
+		include("Boom_misc.cfg", "thingflagscompare");
 	}
 
 	// Things flags masks
diff --git a/Build/Configurations/Includes/Boom_misc.cfg b/Build/Configurations/Includes/Boom_misc.cfg
index 2b4c30530..666f0863d 100644
--- a/Build/Configurations/Includes/Boom_misc.cfg
+++ b/Build/Configurations/Includes/Boom_misc.cfg
@@ -35,25 +35,30 @@ thingflagstranslation
 // How thing flags should be compared (for the stuck thing error check)
 thingflagscompare
 {
-    skills {
-        1;
-        2;
-        4;
-    }
-
-    gamemodes {
-        16 {
-            invert = true;
-        }
-
-        32 {
-            invert = true;
-        }
-
-        64 {
-            invert = true;
-        }
-    }
+	skills {
+		1;
+		2;
+		4;
+	}
+
+	gamemodes {
+		16 {
+			//invert = true;
+			ignoredgroup = "skills";
+			ingnorethisgroupwhenunset = true;
+		}
+
+		32 {
+			invert = true;
+			requiredflag = "16";
+		}
+
+		64 {
+			invert = true;
+			requiredflag = "16";
+			requiredgroup = "skills";
+		}
+	}
 }
 
 
diff --git a/Build/Configurations/Includes/Doom_misc.cfg b/Build/Configurations/Includes/Doom_misc.cfg
index e7444d4f1..9c94327f1 100644
--- a/Build/Configurations/Includes/Doom_misc.cfg
+++ b/Build/Configurations/Includes/Doom_misc.cfg
@@ -57,6 +57,8 @@ thingflagscompare
 	gamemodes {
 		16 {
 			comparemethod = "equal";
+			ignoredgroup = "skills";
+			ingnorethisgroupwhenunset = true;
 		}
 	}
 }
diff --git a/Build/Configurations/Includes/Hexen_misc.cfg b/Build/Configurations/Includes/Hexen_misc.cfg
index e6124b42b..e3292fc91 100644
--- a/Build/Configurations/Includes/Hexen_misc.cfg
+++ b/Build/Configurations/Includes/Hexen_misc.cfg
@@ -59,9 +59,15 @@ thingflagscompare
 	}
 
 	gamemodes {
-		256;
-		512;
-		1024;
+		256 {
+			requiredgroup = "skills";
+		}
+		512 {
+			requiredgroup = "skills";
+		}
+		1024 {
+			ignoredgroup = "skills";
+		}
 	}
 }
 
diff --git a/Build/Configurations/Includes/UDMF_misc.cfg b/Build/Configurations/Includes/UDMF_misc.cfg
index 8b20e8968..9bf0768db 100644
--- a/Build/Configurations/Includes/UDMF_misc.cfg
+++ b/Build/Configurations/Includes/UDMF_misc.cfg
@@ -48,9 +48,15 @@ thingflagscompare
 	}
 
 	gamemodes {
-		single;
-		dm;
-		coop;
+		single {
+			requiredgroup = "skills";
+		}
+		coop {
+			requiredgroup = "skills";
+		}
+		dm {
+			ignoredgroup = "skills";
+		}
 	}
 
 	classes {
diff --git a/Source/Core/Config/GameConfiguration.cs b/Source/Core/Config/GameConfiguration.cs
index ce324935d..86bb77add 100644
--- a/Source/Core/Config/GameConfiguration.cs
+++ b/Source/Core/Config/GameConfiguration.cs
@@ -733,6 +733,34 @@ namespace CodeImp.DoomBuilder.Config
 				}
 			}
 
+			//mxd. Integrity check
+			foreach (KeyValuePair<string, Dictionary<string, ThingFlagsCompare>> group in thingflagscompare)
+			{
+				foreach (KeyValuePair<string, ThingFlagsCompare> flaggrp in group.Value)
+				{
+					// Required group is missing?
+					if (!string.IsNullOrEmpty(flaggrp.Value.RequiredGroup) && !thingflagscompare.ContainsKey(flaggrp.Value.RequiredGroup))
+					{
+						General.ErrorLogger.Add(ErrorType.Warning, "thingflagscompare group '" + flaggrp.Value.RequiredGroup + "', required by flag '" + flaggrp.Key + "' does not exist!");
+						flaggrp.Value.RequiredGroup = string.Empty;
+					}
+
+					// Ignored group is missing?
+					if(!string.IsNullOrEmpty(flaggrp.Value.IgnoredGroup) && !thingflagscompare.ContainsKey(flaggrp.Value.IgnoredGroup)) 
+					{
+						General.ErrorLogger.Add(ErrorType.Warning, "thingflagscompare group '" + flaggrp.Value.IgnoredGroup + "', ignored by flag '" + flaggrp.Key + "' does not exist!");
+						flaggrp.Value.IgnoredGroup = string.Empty;
+					}
+
+					// Required flag is missing?
+					if(!string.IsNullOrEmpty(flaggrp.Value.RequiredFlag) && !group.Value.ContainsKey(flaggrp.Value.RequiredFlag)) 
+					{
+						General.ErrorLogger.Add(ErrorType.Warning, "thingflagscompare flag '" + flaggrp.Value.RequiredFlag + "', required by flag '" + flaggrp.Key + "' does not exist!");
+						flaggrp.Value.RequiredFlag = string.Empty;
+					}
+				}
+			}
+
 			// Sort the translation flags, because they must be compared highest first!
 			thingflagstranslation.Sort();
 		}
diff --git a/Source/Core/Config/ThingsFlagsCompare.cs b/Source/Core/Config/ThingsFlagsCompare.cs
index 417c50fba..fe71cb788 100644
--- a/Source/Core/Config/ThingsFlagsCompare.cs
+++ b/Source/Core/Config/ThingsFlagsCompare.cs
@@ -17,6 +17,8 @@
 #region ================== Namespaces
 
 using System;
+using System.Collections.Generic;
+using System.Windows.Forms;
 using CodeImp.DoomBuilder.IO;
 using CodeImp.DoomBuilder.Map;
 
@@ -39,6 +41,10 @@ namespace CodeImp.DoomBuilder.Config
 		#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;
@@ -49,6 +55,9 @@ namespace CodeImp.DoomBuilder.Config
 
 		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
 
 		#endregion
 
@@ -77,6 +86,10 @@ namespace CodeImp.DoomBuilder.Config
 			}
 
 			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);
@@ -97,25 +110,155 @@ namespace CodeImp.DoomBuilder.Config
 			bool t2flag;
 
 			// Check if the flags exist
-			if(!t1.Flags.ContainsKey(flag) || !t2.Flags.ContainsKey(flag)) {
-				//mxd. If a map is in UDMF format - check Fields
-				if(!General.Map.UDMF || !t1.Fields.ContainsKey(flag) || !t2.Fields.ContainsKey(flag)) 
-					return 0;
-
-				// tag flag inversion into account
-				t1flag = invert ? !(bool)t1.Fields[flag].Value : (bool)t1.Fields[flag].Value;
-				t2flag = invert ? !(bool)t2.Fields[flag].Value : (bool)t2.Fields[flag].Value;
-			} else {
-				// tag flag inversion into account
-				t1flag = invert ? !t1.Flags[flag] : t1.Flags[flag];
-				t2flag = invert ? !t2.Flags[flag] : t2.Flags[flag];
+			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<string, Dictionary<string, ThingFlagsCompare>> pair in General.Map.Config.ThingFlagsCompare)
+			{
+				foreach(KeyValuePair<string, ThingFlagsCompare> 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'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<CheckBox> checkboxes)
+		{
+			Dictionary<string, bool> flags = new Dictionary<string, bool>(checkboxes.Count);
+			Dictionary<string, Dictionary<string, bool>> flagspergroup = new Dictionary<string, Dictionary<string, bool>>(General.Map.Config.ThingFlagsCompare.Count);
+			Dictionary<string, bool> requiredgroups = new Dictionary<string, bool>();
+			Dictionary<string, bool> ignoredgroups = new Dictionary<string, bool>();
+
+			// Gather flags
+			foreach (CheckBox cb in checkboxes)
+			{
+				flags.Add(cb.Tag.ToString(), cb.CheckState == CheckState.Checked);
+			}
+
+			// Gather flags per group
+			foreach (KeyValuePair<string, Dictionary<string, ThingFlagsCompare>> group in General.Map.Config.ThingFlagsCompare)
+			{
+				flagspergroup.Add(group.Key, new Dictionary<string, bool>());
+
+				foreach (KeyValuePair<string, ThingFlagsCompare> 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<string, Dictionary<string, bool>> group in flagspergroup)
+			{
+				foreach(KeyValuePair<string, bool> 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<string, bool> group in ignoredgroups)
+			{
+				flagspergroup.Remove(group.Key);
+			}
+
+			// Return message
+			string result = string.Empty;
+
+			foreach (KeyValuePair<string, Dictionary<string, bool>> 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<string, bool> flags, string flag, bool invert)
+		{
+			bool result = flags.ContainsKey(flag) && flags[flag];
+			return (invert ? !result : result);
+		}
+
 		#endregion
 	}
 }
diff --git a/Source/Core/Windows/ThingEditForm.cs b/Source/Core/Windows/ThingEditForm.cs
index f9765300c..e463eb239 100644
--- a/Source/Core/Windows/ThingEditForm.cs
+++ b/Source/Core/Windows/ThingEditForm.cs
@@ -633,47 +633,17 @@ namespace CodeImp.DoomBuilder.Windows
 		{
 			if(preventchanges) return;
 
-			foreach(KeyValuePair<string, Dictionary<string, ThingFlagsCompare>> group in General.Map.Config.ThingFlagsCompare)
+			string warn = ThingFlagsCompare.CheckThingEditFormFlags(flags.Checkboxes);
+			if(!string.IsNullOrEmpty(warn)) 
 			{
-				if(group.Value.Count < 2) continue;
-				bool haveflags = false;
-				
-				foreach(CheckBox cb in flags.Checkboxes)
-				{
-					if (group.Value.ContainsKey(cb.Tag.ToString()) && cb.CheckState != CheckState.Unchecked)
-					{
-						haveflags = true;
-						break;
-					}
-				}
-
-				if (!haveflags)
-				{
-					switch(group.Key)
-					{
-						case "skills":
-							tooltip.SetToolTip(missingflags, "Thing is not used in any skill level.");
-							break;
-
-						case "gamemodes":
-							tooltip.SetToolTip(missingflags, "Thing is not used in any game mode.");
-							break;
-
-						case "classes":
-							tooltip.SetToolTip(missingflags, "Thing is not used by any class.");
-							break;
-
-						default:
-							tooltip.SetToolTip(missingflags, "At least one '" + group.Key + "' flag should be set.");
-							break;
-					}
-					
-					missingflags.Visible = true;
-					settingsgroup.ForeColor = Color.DarkRed;
-					return;
-				}
+				//got missing flags
+				tooltip.SetToolTip(missingflags, warn);
+				missingflags.Visible = true;
+				settingsgroup.ForeColor = Color.DarkRed;
+				return;
 			}
 
+			//everything is OK
 			missingflags.Visible = false;
 			settingsgroup.ForeColor = SystemColors.ControlText;
 		}
diff --git a/Source/Core/Windows/ThingEditFormUDMF.cs b/Source/Core/Windows/ThingEditFormUDMF.cs
index 07bce8a8f..444038d1d 100644
--- a/Source/Core/Windows/ThingEditFormUDMF.cs
+++ b/Source/Core/Windows/ThingEditFormUDMF.cs
@@ -335,10 +335,11 @@ namespace CodeImp.DoomBuilder.Windows
 
 			preventchanges = false;
 
-			//mxd. Trigger angle/pitch/roll update manually...
+			//mxd. Trigger updates manually...
 			angle_WhenTextChanged(angle, EventArgs.Empty);
 			pitch_WhenTextChanged(pitch, EventArgs.Empty);
 			roll_WhenTextChanged(roll, EventArgs.Empty);
+			flags_OnValueChanged(flags, EventArgs.Empty);
 
 			updateScriptControls(); //mxd
 
@@ -862,46 +863,17 @@ namespace CodeImp.DoomBuilder.Windows
 		{
 			if(preventchanges) return;
 
-			foreach(KeyValuePair<string, Dictionary<string, ThingFlagsCompare>> group in General.Map.Config.ThingFlagsCompare) 
+			string warn = ThingFlagsCompare.CheckThingEditFormFlags(flags.Checkboxes);
+			if(!string.IsNullOrEmpty(warn)) 
 			{
-				if(group.Value.Count < 2) continue;
-				bool haveflags = false;
-
-				foreach(CheckBox cb in flags.Checkboxes) 
-				{
-					if(group.Value.ContainsKey(cb.Tag.ToString()) && cb.CheckState != CheckState.Unchecked) {
-						haveflags = true;
-						break;
-					}
-				}
-
-				if(!haveflags) 
-				{
-					switch(group.Key) 
-					{
-						case "skills":
-							tooltip.SetToolTip(missingflags, "Thing is not used in any skill level.");
-							break;
-
-						case "gamemodes":
-							tooltip.SetToolTip(missingflags, "Thing is not used in any game mode.");
-							break;
-
-						case "classes":
-							tooltip.SetToolTip(missingflags, "Thing is not used by any class.");
-							break;
-
-						default:
-							tooltip.SetToolTip(missingflags, "At least one '" + group.Key + "' flag should be set.");
-							break;
-					}
-
-					missingflags.Visible = true;
-					settingsgroup.ForeColor = Color.DarkRed;
-					return;
-				}
+				//got missing flags
+				tooltip.SetToolTip(missingflags, warn);
+				missingflags.Visible = true;
+				settingsgroup.ForeColor = Color.DarkRed;
+				return;
 			}
 
+			//everything is OK
 			missingflags.Visible = false;
 			settingsgroup.ForeColor = SystemColors.ControlText;
 		}
-- 
GitLab