From 9908e41197b0c2ef8a5df9d6c88798b4a9fd5ef0 Mon Sep 17 00:00:00 2001
From: MaxED <j.maxed@gmail.com>
Date: Mon, 24 Oct 2016 19:19:11 +0000
Subject: [PATCH] Added, Game configurations: added "ignoreddirectories"
 parameter. It lists directory names to be ignored when loading
 PK3/PK7/Directory resources. Added, Game configurations: added
 "ignoredextensions" parameter. It lists file extensions to be ignored when
 loading PK3/PK7/Directory resources. Updated: sector triangulation logic now
 works ~20% faster. Changed: a case when a pk3/pk7 archive contains several
 entries with identical filename is now treated as a warning, not as an error.
 Fixed, Visual mode: absolute floor/ceiling brightness should not be affected
 by brightness transfer effects (like 3d floors). Fixed, Draw Lines mode: in
 some cases unclosed sectors were created when several points were
 successively drawn at the same location. Updated documentation.

---
 Build/Configurations/Includes/Common.cfg      |  6 ++
 Help/gc_basicsettings.html                    | 18 +++-
 Source/Core/Config/GameConfiguration.cs       | 12 +++
 Source/Core/Data/PK3Reader.cs                 |  2 +-
 Source/Core/Geometry/Triangulation.cs         | 90 +++++++++++--------
 Source/Core/IO/DirectoryFilesList.cs          | 44 ++++++---
 .../ClassicModes/DrawGeometryMode.cs          |  8 ++
 .../BuilderModes/VisualModes/SectorData.cs    | 45 ++++++----
 8 files changed, 158 insertions(+), 67 deletions(-)

diff --git a/Build/Configurations/Includes/Common.cfg b/Build/Configurations/Includes/Common.cfg
index 57b125880..93880b232 100644
--- a/Build/Configurations/Includes/Common.cfg
+++ b/Build/Configurations/Includes/Common.cfg
@@ -27,6 +27,12 @@ bottomboundary = -32768;
 longtexturenames = false;
 
 
+//mxd. These directory names are ignored when loading PK3/PK7/Directory resources
+ignoreddirectories = ".svn .git";
+
+
+//mxd. Files with these extensions are ignored when loading PK3/PK7/Directory resources
+ignoredextensions = "wad pk3 pk7 bak backup1 backup2 backup3 zip rar 7z";
 
 // Things used by the editor
 thingtypes
diff --git a/Help/gc_basicsettings.html b/Help/gc_basicsettings.html
index 218d6bd21..9e1f001c4 100644
--- a/Help/gc_basicsettings.html
+++ b/Help/gc_basicsettings.html
@@ -81,7 +81,6 @@ skills
 damagetypes = "None BFGSplash Drowning Slime";
 </pre>
 	<br />
-	
 	<b class="fat">internalsoundnames</b> (string) - <span class="red">GZDB only</span>.<br />
 	Space-separated list of built-in logical sound names. These names won't trigger an editor warning when they are not bound to actual sounds in SNDINFO.<br />
 	<br />
@@ -90,7 +89,22 @@ damagetypes = "None BFGSplash Drowning Slime";
 internalsoundnames = "*death *xdeath *wimpydeath *crazydeath *burndeath";
 </pre>
 	<br />
-	
+    <b class="fat">ignoredextensions</b> (string) - <span class="red">GZDB only</span>.<br />
+	Space-separated list of file extensions. Files with these extensions will be ignored when loading PK3/PK7/Directory resources.<br />
+	<br />
+	<strong>Example:</strong>
+	<pre>
+ignoredextensions = "wad pk3 pk7 bak backup1 backup2 backup3 zip rar 7z";
+</pre>
+<br />
+<b class="fat">ignoreddirectories</b> (string) - <span class="red">GZDB only</span>.<br />
+	Space-separated list of directory names. These directory names are ignored when loading PK3/PK7/Directory resources. This applies to top level directories only.<br />
+	<br />
+	<strong>Example:</strong>
+	<pre>
+ignoreddirectories = ".svn .git";
+</pre>
+<br />
 	<b class="fat">linetagindicatesectors</b> (boolean)<br />
 	When <b>true</b>, Doom Builder will highlight sectors associated with the same tag number when a line is highlighted. This is only really useful for Doom format maps, because Hexen format and UDMF format has no single tag on linedefs (in those formats, the arguments of the linedef's action can be tags, which also works to highlight sectors).<br />
 	Default value is <b>false</b>.<br />
diff --git a/Source/Core/Config/GameConfiguration.cs b/Source/Core/Config/GameConfiguration.cs
index 93f7d7bfa..c1a195628 100644
--- a/Source/Core/Config/GameConfiguration.cs
+++ b/Source/Core/Config/GameConfiguration.cs
@@ -160,6 +160,10 @@ namespace CodeImp.DoomBuilder.Config
 
 		//mxd. Internal sounds. These logical sound names won't trigger a warning when they are not bound to actual sounds in SOUNDINFO.
 		private HashSet<string> internalsoundnames;
+
+		//mxd. Stuff to ignore
+		private HashSet<string> ignoreddirectories;
+		private HashSet<string> ignoredextensions;
 		
 		// Defaults
 		private readonly List<DefinedTextureSet> texturesets;
@@ -291,6 +295,10 @@ namespace CodeImp.DoomBuilder.Config
 		//mxd. Internal sounds
 		internal HashSet<string> InternalSoundNames { get { return internalsoundnames; } }
 
+		//mxd. Stuff to ignore
+		internal HashSet<string> IgnoredFileExtensions { get { return ignoredextensions; } }
+		internal HashSet<string> IgnoredDirectoryNames { get { return ignoreddirectories; } }
+
 		// Defaults
 		internal List<DefinedTextureSet> TextureSets { get { return texturesets; } }
 		public List<ThingsFilter> ThingsFilters { get { return thingfilters; } }
@@ -438,6 +446,10 @@ namespace CodeImp.DoomBuilder.Config
 			damagetypes = new HashSet<string>(cfg.ReadSetting("damagetypes", "None").Split(splitter, StringSplitOptions.RemoveEmptyEntries), StringComparer.OrdinalIgnoreCase);
 			internalsoundnames = new HashSet<string>(cfg.ReadSetting("internalsoundnames", string.Empty).Split(splitter, StringSplitOptions.RemoveEmptyEntries), StringComparer.OrdinalIgnoreCase);
 			
+			//mxd. Load stuff to ignore
+			ignoreddirectories = new HashSet<string>(cfg.ReadSetting("ignoreddirectories", string.Empty).Split(splitter, StringSplitOptions.RemoveEmptyEntries), StringComparer.OrdinalIgnoreCase);
+			ignoredextensions = new HashSet<string>(cfg.ReadSetting("ignoredextensions", string.Empty).Split(splitter, StringSplitOptions.RemoveEmptyEntries), StringComparer.OrdinalIgnoreCase);
+
 			// Things
 			LoadThingFlags();
 			LoadDefaultThingFlags();
diff --git a/Source/Core/Data/PK3Reader.cs b/Source/Core/Data/PK3Reader.cs
index 0483a52ef..53ab3b2f1 100644
--- a/Source/Core/Data/PK3Reader.cs
+++ b/Source/Core/Data/PK3Reader.cs
@@ -97,7 +97,7 @@ namespace CodeImp.DoomBuilder.Data
 			archive = null;
 
 			// Make files list
-			files = new DirectoryFilesList(fileentries);
+			files = new DirectoryFilesList(dl.GetDisplayName(), fileentries);
 			
 			// Initialize without path (because we use paths relative to the PK3 file)
 			Initialize();
diff --git a/Source/Core/Geometry/Triangulation.cs b/Source/Core/Geometry/Triangulation.cs
index 9f6e554c5..42de01ff6 100644
--- a/Source/Core/Geometry/Triangulation.cs
+++ b/Source/Core/Geometry/Triangulation.cs
@@ -374,18 +374,18 @@ namespace CodeImp.DoomBuilder.Geometry
 			Vertex found = null;
 			
 			// Go for all sides to find the right-most side
-			foreach(KeyValuePair<Sidedef, bool> sd in sides)
+			foreach(Sidedef sd in sides.Keys)
 			{
 				// First found?
-				if((found == null) && !ignores.ContainsKey(sd.Key.Line.Start)) found = sd.Key.Line.Start;
-				if((found == null) && !ignores.ContainsKey(sd.Key.Line.End)) found = sd.Key.Line.End;
+				if((found == null) && !ignores.ContainsKey(sd.Line.Start)) found = sd.Line.Start;
+				if((found == null) && !ignores.ContainsKey(sd.Line.End)) found = sd.Line.End;
 				
 				// Compare?
 				if(found != null)
 				{
 					// Check if more to the right than the previous found
-					if((sd.Key.Line.Start.Position.x > found.Position.x) && !ignores.ContainsKey(sd.Key.Line.Start)) found = sd.Key.Line.Start;
-					if((sd.Key.Line.End.Position.x > found.Position.x) && !ignores.ContainsKey(sd.Key.Line.End)) found = sd.Key.Line.End;
+					if((sd.Line.Start.Position.x > found.Position.x) && !ignores.ContainsKey(sd.Line.Start)) found = sd.Line.Start;
+					if((sd.Line.End.Position.x > found.Position.x) && !ignores.ContainsKey(sd.Line.End)) found = sd.Line.End;
 				}
 			}
 			
@@ -779,11 +779,16 @@ namespace CodeImp.DoomBuilder.Geometry
 		// This checks if a given ear is a valid (no intersections from reflex vertices)
 		private static bool CheckValidEar(EarClipVertex[] t, LinkedList<EarClipVertex> reflexes)
 		{
+			//mxd
+			Vector2D pos0 = t[0].Position;
+			Vector2D pos1 = t[1].Position;
+			Vector2D pos2 = t[2].Position;
+			
 			// Go for all reflex vertices
 			foreach(EarClipVertex rv in reflexes)
 			{
 				// Not one of the triangle corners?
-				if((rv.Position != t[0].Position) && (rv.Position != t[1].Position) && (rv.Position != t[2].Position))
+				if((rv.Position != pos0) && (rv.Position != pos1) && (rv.Position != pos2))
 				{
 					// Return false on intersection
 					if(PointInsideTriangle(t, rv.MainListNode)) return false;
@@ -797,11 +802,12 @@ namespace CodeImp.DoomBuilder.Geometry
 		// This returns the 3-vertex array triangle for an ear
 		private static EarClipVertex[] GetTriangle(EarClipVertex v)
 		{
-			EarClipVertex[] t = new EarClipVertex[3];
-			t[0] = (v.MainListNode.Previous == null) ? v.MainListNode.List.Last.Value : v.MainListNode.Previous.Value;
-			t[1] = v;
-			t[2] = (v.MainListNode.Next == null) ? v.MainListNode.List.First.Value : v.MainListNode.Next.Value;
-			return t;
+			return new []
+			{
+				(v.MainListNode.Previous == null) ? v.MainListNode.List.Last.Value : v.MainListNode.Previous.Value,
+				v,
+				(v.MainListNode.Next == null) ? v.MainListNode.List.First.Value : v.MainListNode.Next.Value
+			};
 		}
 		
 		// This checks if a vertex is reflex (corner > 180 deg) or convex (corner < 180 deg)
@@ -820,18 +826,23 @@ namespace CodeImp.DoomBuilder.Geometry
 			// If the triangle has no area, there can never be a point inside
 			if(TriangleHasArea(t))
 			{
-				float lineside01 = Line2D.GetSideOfLine(t[0].Position, t[1].Position, p.Value.Position);
-				float lineside12 = Line2D.GetSideOfLine(t[1].Position, t[2].Position, p.Value.Position);
-				float lineside20 = Line2D.GetSideOfLine(t[2].Position, t[0].Position, p.Value.Position);
+				//mxd
+				Vector2D pos0 = t[0].Position;
+				Vector2D pos1 = t[1].Position;
+				Vector2D pos2 = t[2].Position;
+
+				float lineside01 = Line2D.GetSideOfLine(pos0, pos1, p.Value.Position);
+				float lineside12 = Line2D.GetSideOfLine(pos1, pos2, p.Value.Position);
+				float lineside20 = Line2D.GetSideOfLine(pos2, pos0, p.Value.Position);
 				float u_on_line = 0.5f;
 
 				// If point p is on the line of an edge, find out where on the edge segment p is.
 				if(lineside01 == 0.0f)
-					u_on_line = Line2D.GetNearestOnLine(t[0].Position, t[1].Position, p.Value.Position);
+					u_on_line = Line2D.GetNearestOnLine(pos0, pos1, p.Value.Position);
 				else if(lineside12 == 0.0f)
-					u_on_line = Line2D.GetNearestOnLine(t[1].Position, t[2].Position, p.Value.Position);
+					u_on_line = Line2D.GetNearestOnLine(pos1, pos2, p.Value.Position);
 				else if(lineside20 == 0.0f)
-					u_on_line = Line2D.GetNearestOnLine(t[2].Position, t[0].Position, p.Value.Position);
+					u_on_line = Line2D.GetNearestOnLine(pos2, pos0, p.Value.Position);
 				
 				// If any of the lineside results are 0 then that means the point p lies on that edge and we
 				// need to test if the lines adjacent to the point p are in the triangle or not.
@@ -877,9 +888,10 @@ namespace CodeImp.DoomBuilder.Geometry
 				// Line is inside triangle, because p2 is
 				return true;
 			}
+
 			// Test if p2 is on an edge of the triangle and if it is we would
 			// like to know where on the edge segment p2 is
-			else if(s01 == 0.0f)
+			if(s01 == 0.0f)
 			{
 				p2_on_edge = Line2D.GetNearestOnLine(t[0].Position, t[1].Position, p2);
 				p1_on_same_edge = Line2D.GetSideOfLine(t[0].Position, t[1].Position, p1);
@@ -912,13 +924,10 @@ namespace CodeImp.DoomBuilder.Geometry
 			Line2D t20 = new Line2D(t[2].Position, t[0].Position);
 			float pu, pt;
 			
-			// Test intersections
-			t01.GetIntersection(p, out pu, out pt);
-			if(!float.IsNaN(pu) && (pu >= 0.0f) && (pu <= 1.0f) && (pt >= 0.0f) && (pt <= 1.0f)) return true;
-			t12.GetIntersection(p, out pu, out pt);
-			if(!float.IsNaN(pu) && (pu >= 0.0f) && (pu <= 1.0f) && (pt >= 0.0f) && (pt <= 1.0f)) return true;
-			t20.GetIntersection(p, out pu, out pt);
-			if(!float.IsNaN(pu) && (pu >= 0.0f) && (pu <= 1.0f) && (pt >= 0.0f) && (pt <= 1.0f)) return true;
+			//mxd. Test intersections
+			if(t01.GetIntersection(p, out pu, out pt)) return true;
+			if(t12.GetIntersection(p, out pu, out pt)) return true;
+			if(t20.GetIntersection(p, out pu, out pt)) return true;
 			
 			return false;
 		}
@@ -926,24 +935,33 @@ namespace CodeImp.DoomBuilder.Geometry
 		// This checks if the triangle has an area greater than 0
 		private static bool TriangleHasArea(EarClipVertex[] t)
 		{
-			return ((t[0].Position.x * (t[1].Position.y - t[2].Position.y) +
-					 t[1].Position.x * (t[2].Position.y - t[0].Position.y) +
-					 t[2].Position.x * (t[0].Position.y - t[1].Position.y)) != 0.0f);
+			Vector2D tp0 = t[0].Position;
+			Vector2D tp1 = t[1].Position;
+			Vector2D tp2 = t[2].Position;
+
+			return ((tp0.x * (tp1.y - tp2.y) +
+					 tp1.x * (tp2.y - tp0.y) +
+					 tp2.x * (tp0.y - tp1.y)) != 0.0f);
 		}
 		
 		// This adds an array of vertices
 		private static void AddTriangleToList(EarClipVertex[] triangle, List<Vector2D> verticeslist, List<Sidedef> sidedefslist, bool last)
 		{
-			// Create triangle
-			verticeslist.Add(triangle[0].Position);
-			sidedefslist.Add(triangle[0].Sidedef);
-			verticeslist.Add(triangle[1].Position);
-			sidedefslist.Add(triangle[1].Sidedef);
-			verticeslist.Add(triangle[2].Position);
-			if(!last) sidedefslist.Add(null); else sidedefslist.Add(triangle[2].Sidedef);
+			//mxd
+			EarClipVertex v0 = triangle[0];
+			EarClipVertex v1 = triangle[1];
+			EarClipVertex v2 = triangle[2];
 			
+			// Create triangle
+			verticeslist.Add(v0.Position);
+			sidedefslist.Add(v0.Sidedef);
+			verticeslist.Add(v1.Position);
+			sidedefslist.Add(v1.Sidedef);
+			verticeslist.Add(v2.Position);
+			sidedefslist.Add(!last ? null : v2.Sidedef);
+
 			// Modify the first earclipvertex of this triangle, it no longer lies along a sidedef
-			triangle[0].Sidedef = null;
+			v0.Sidedef = null;
 		}
 		
 		#endregion
diff --git a/Source/Core/IO/DirectoryFilesList.cs b/Source/Core/IO/DirectoryFilesList.cs
index f373d99ac..8e390c05c 100644
--- a/Source/Core/IO/DirectoryFilesList.cs
+++ b/Source/Core/IO/DirectoryFilesList.cs
@@ -29,11 +29,6 @@ namespace CodeImp.DoomBuilder.IO
 	{
 		#region ================== Constants (mxd)
 
-		private static HashSet<string> EXLUDE_EXTENSIONS = new HashSet<string>(StringComparer.OrdinalIgnoreCase)
-		{
-			"wad", "pk3", "pk7", "bak", "backup1", "backup2", "backup3", "zip", "rar", "7z"
-		};
-
 		#endregion
 
 		#region ================== Variables
@@ -68,17 +63,26 @@ namespace CodeImp.DoomBuilder.IO
 					wadentries.Add(file);
 					continue;
 				}
-				if(EXLUDE_EXTENSIONS.Contains(e.extension)) continue;
-			
-				if(entries.ContainsKey(e.filepathname))
-					throw new IOException("Multiple files with the same filename in the same directory are not allowed. See: \"" + e.filepathname + "\"");
+
+				if(General.Map.Config.IgnoredFileExtensions.Contains(e.extension)) continue;
+
+				bool skipfolder = false;
+				foreach(string ef in General.Map.Config.IgnoredDirectoryNames)
+				{
+					if(e.path.StartsWith(ef + Path.DirectorySeparatorChar))
+					{
+						skipfolder = true;
+						break;
+					}
+				}
+				if(skipfolder) continue;
 
 				entries.Add(e.filepathname, e);
 			}
 		}
 
 		// Constructor for custom list
-		public DirectoryFilesList(ICollection<DirectoryFileEntry> sourceentries)
+		public DirectoryFilesList(string resourcename, ICollection<DirectoryFileEntry> sourceentries)
 		{
 			entries = new Dictionary<string, DirectoryFileEntry>(sourceentries.Count, StringComparer.OrdinalIgnoreCase);
 			wadentries = new List<string>();
@@ -89,10 +93,26 @@ namespace CodeImp.DoomBuilder.IO
 					wadentries.Add(e.filepathname);
 					continue;
 				}
-				if(EXLUDE_EXTENSIONS.Contains(e.extension)) continue;
+
+				if(General.Map.Config.IgnoredFileExtensions.Contains(e.extension)) continue;
+
+				bool skipfolder = false;
+				foreach(string ef in General.Map.Config.IgnoredDirectoryNames)
+				{
+					if(e.path.StartsWith(ef + Path.DirectorySeparatorChar))
+					{
+						skipfolder = true;
+						break;
+					}
+				}
+				if(skipfolder) continue;
 
 				if(entries.ContainsKey(e.filepathname))
-					throw new IOException("Multiple files with the same filename in the same directory are not allowed. See: \"" + e.filepathname + "\"");
+				{
+					General.ErrorLogger.Add(ErrorType.Warning, "Resource \"" + resourcename + "\" contains multiple files with the same filename. See: \"" + e.filepathname + "\"");
+					continue;
+				}
+
 				entries.Add(e.filepathname, e);
 			}
 		}
diff --git a/Source/Plugins/BuilderModes/ClassicModes/DrawGeometryMode.cs b/Source/Plugins/BuilderModes/ClassicModes/DrawGeometryMode.cs
index a792ac32e..14f0d03b9 100644
--- a/Source/Plugins/BuilderModes/ClassicModes/DrawGeometryMode.cs
+++ b/Source/Plugins/BuilderModes/ClassicModes/DrawGeometryMode.cs
@@ -545,6 +545,14 @@ namespace CodeImp.DoomBuilder.BuilderModes
 				pos.y > General.Map.Config.TopBoundary || pos.y < General.Map.Config.BottomBoundary)
 				return false;
 
+			//mxd. Avoid zero-length lines...
+			if(points.Count > 0)
+			{
+				Vector2D delta = points[points.Count - 1].pos - pos;
+				if((Math.Abs(delta.x) <= 0.001f) && (Math.Abs(delta.y) <= 0.001f))
+					return true;
+			}
+
 			DrawnVertex newpoint = new DrawnVertex();
 			newpoint.pos = pos;
 			newpoint.stitch = stitch;
diff --git a/Source/Plugins/BuilderModes/VisualModes/SectorData.cs b/Source/Plugins/BuilderModes/VisualModes/SectorData.cs
index b156b2d45..50926a0af 100644
--- a/Source/Plugins/BuilderModes/VisualModes/SectorData.cs
+++ b/Source/Plugins/BuilderModes/VisualModes/SectorData.cs
@@ -56,6 +56,12 @@ namespace CodeImp.DoomBuilder.BuilderModes
 		private bool floorchanged;
 		private bool ceilingchanged;
 
+		//mxd. Absolute lights are not affected by brightness transfers...
+		private bool lightfloorabsolute;
+		private bool lightceilingabsolute;
+		private int lightfloor;
+		private int lightceiling;
+
 		#endregion
 		
 		#region ================== Properties
@@ -288,17 +294,17 @@ namespace CodeImp.DoomBuilder.BuilderModes
 			
 			// Fetch ZDoom fields
 			int color = sector.Fields.GetValue("lightcolor", -1);
-			int flight = sector.Fields.GetValue("lightfloor", 0);
-			bool fabs = sector.Fields.GetValue("lightfloorabsolute", false);
-			int clight = sector.Fields.GetValue("lightceiling", 0);
-			bool cabs = sector.Fields.GetValue("lightceilingabsolute", false);
+			lightfloor = sector.Fields.GetValue("lightfloor", 0);
+			lightfloorabsolute = sector.Fields.GetValue("lightfloorabsolute", false);
+			lightceiling = sector.Fields.GetValue("lightceiling", 0);
+			lightceilingabsolute = sector.Fields.GetValue("lightceilingabsolute", false);
 
 			// Determine colors & light levels
 			PixelColor lightcolor = PixelColor.FromInt(color);
-			if(!fabs) flight = sector.Brightness + flight;
-			if(!cabs) clight = sector.Brightness + clight;
-			PixelColor floorbrightness = PixelColor.FromInt(mode.CalculateBrightness(flight));
-			PixelColor ceilingbrightness = PixelColor.FromInt(mode.CalculateBrightness(clight));
+			if(!lightfloorabsolute) lightfloor = sector.Brightness + lightfloor;
+			if(!lightceilingabsolute) lightceiling = sector.Brightness + lightceiling;
+			PixelColor floorbrightness = PixelColor.FromInt(mode.CalculateBrightness(lightfloor));
+			PixelColor ceilingbrightness = PixelColor.FromInt(mode.CalculateBrightness(lightceiling));
 			PixelColor floorcolor = PixelColor.Modulate(lightcolor, floorbrightness);
 			PixelColor ceilingcolor = PixelColor.Modulate(lightcolor, ceilingbrightness);
 			floor.color = floorcolor.WithAlpha(255).ToInt();
@@ -357,7 +363,7 @@ namespace CodeImp.DoomBuilder.BuilderModes
 				{
 					lightlevels[lightlevels.Count - 1].colorbelow = stored.colorbelow;
 					lightlevels[lightlevels.Count - 1].brightnessbelow = stored.brightnessbelow;
-					lightlevels[lightlevels.Count - 1].color = GetLevelColor(stored);
+					lightlevels[lightlevels.Count - 1].color = GetLevelColor(stored, lightlevels[lightlevels.Count - 1]);
 				}
 
 				//mxd. Cast light properties from top to bottom
@@ -378,12 +384,12 @@ namespace CodeImp.DoomBuilder.BuilderModes
 					{
 						l.colorbelow = stored.colorbelow;
 						l.brightnessbelow = stored.brightnessbelow;
-						l.color = GetLevelColor(stored);
+						l.color = GetLevelColor(stored, l);
 					}
 					else if(l.restrictlighting)
 					{
 						if(!pl.restrictlighting && pl != ceiling) stored = pl;
-						l.color = GetLevelColor(stored);
+						l.color = GetLevelColor(stored, l);
 
 						// This is the bottom side of extrafloor with "restrict lighting" flag. Make it cast stored light props. 
 						if(l.type == SectorLevelType.Ceiling)
@@ -394,10 +400,10 @@ namespace CodeImp.DoomBuilder.BuilderModes
 								// Use light and color settings from previous layer
 								l.colorbelow = pl.colorbelow;
 								l.brightnessbelow = pl.brightnessbelow;
-								l.color = GetLevelColor(pl);
+								l.color = GetLevelColor(pl, l);
 
 								// Also colorize previous layer using next higher level color 
-								if(i + 2 < lightlevels.Count) pl.color = GetLevelColor(lightlevels[i + 2]);
+								if(i + 2 < lightlevels.Count) pl.color = GetLevelColor(lightlevels[i + 2], pl);
 							}
 							else
 							{
@@ -439,7 +445,7 @@ namespace CodeImp.DoomBuilder.BuilderModes
 						if(src.colorbelow.a > 0 && src.brightnessbelow != -1)
 						{
 							// Only surface brightness is retained when a glowing flat is used as extrafloor texture
-							if(!l.affectedbyglow) l.color = GetLevelColor(src);
+							if(!l.affectedbyglow) l.color = GetLevelColor(src, l);
 
 							// Transfer brightnessbelow and colorbelow if current level is not extrafloor top
 							if(!(l.extrafloor && l.type == SectorLevelType.Floor))
@@ -594,9 +600,16 @@ namespace CodeImp.DoomBuilder.BuilderModes
 		}
 
 		//mxd
-		private int GetLevelColor(SectorLevel src)
+		private int GetLevelColor(SectorLevel src, SectorLevel target)
 		{
-			PixelColor brightness = PixelColor.FromInt(mode.CalculateBrightness(src.brightnessbelow));
+			PixelColor brightness;
+			if(lightfloorabsolute && target == floor)
+				brightness = PixelColor.FromInt(mode.CalculateBrightness(lightfloor));
+			else if(lightceilingabsolute && target == ceiling)
+				brightness = PixelColor.FromInt(mode.CalculateBrightness(lightceiling));
+			else
+				brightness = PixelColor.FromInt(mode.CalculateBrightness(src.brightnessbelow));
+			
 			PixelColor color = PixelColor.Modulate(src.colorbelow, brightness);
 			return color.WithAlpha(255).ToInt();
 		}
-- 
GitLab