From 6c8a3cb50b8974d85cf730312eca900b431ccdcd Mon Sep 17 00:00:00 2001
From: biwa <6475593+biwa@users.noreply.github.com>
Date: Mon, 5 Apr 2021 19:15:14 +0200
Subject: [PATCH] Visual Mode: things inside self-referencing sectors are now
 displayed at the correct height

---
 Source/Core/Map/Sector.cs                 | 15 ++++++---
 Source/Core/Map/Thing.cs                  | 37 ++++++++++++++++++---
 Source/Core/VisualModes/VisualBlockMap.cs | 40 ++++++++++++++++++-----
 3 files changed, 75 insertions(+), 17 deletions(-)

diff --git a/Source/Core/Map/Sector.cs b/Source/Core/Map/Sector.cs
index 8047f5d64..7ad3d227e 100755
--- a/Source/Core/Map/Sector.cs
+++ b/Source/Core/Map/Sector.cs
@@ -546,6 +546,9 @@ namespace CodeImp.DoomBuilder.Map
 		
 		// This checks if the given point is inside the sector polygon
 		// See: http://paulbourke.net/geometry/polygonmesh/index.html#insidepoly
+		// This checks for the linedefs instead of the sidedefs, since self-referencing sectors
+		// will never result in an odd number of intersections when using sidedefs, since both
+		// sidedefs of the outer linedef belong to the same sector
 		public bool Intersect(Vector2D p) { return Intersect(p, true); }
 		public bool Intersect(Vector2D p, bool countontopastrue)
 		{
@@ -554,13 +557,17 @@ namespace CodeImp.DoomBuilder.Map
 			
 			uint c = 0;
 			Vector2D v1, v2;
+			HashSet<Linedef> linedefs = new HashSet<Linedef>(sidedefs.Count);
+
+			foreach (Sidedef sd in sidedefs)
+				linedefs.Add(sd.Line);
 			
-			// Go for all sidedefs
-			foreach(Sidedef sd in sidedefs)
+			// Go for all linedefs
+			foreach(Linedef ld in linedefs)
 			{
 				// Get vertices
-				v1 = sd.Line.Start.Position;
-				v2 = sd.Line.End.Position;
+				v1 = ld.Start.Position;
+				v2 = ld.End.Position;
 
 				//mxd. On top of a vertex?
 				if(p == v1 || p == v2) return countontopastrue;
diff --git a/Source/Core/Map/Thing.cs b/Source/Core/Map/Thing.cs
index 23e44224c..7105fb4cd 100755
--- a/Source/Core/Map/Thing.cs
+++ b/Source/Core/Map/Thing.cs
@@ -273,19 +273,46 @@ namespace CodeImp.DoomBuilder.Map
 		public void DetermineSector(BlockMap<BlockEntry> blockmap)
 		{
 			BlockEntry be = blockmap.GetBlockAt(pos);
+			List<Sector> sectors = new List<Sector>(1);
 
 			foreach (Sector s in be.Sectors)
 				if (s.Intersect(pos))
-				{
-					sector = s;
-					return;
-				}
+					sectors.Add(s);
+
+			if(sectors.Count == 0)
+			{
+				sector = null;
+			}
+			if (sectors.Count == 1)
+			{
+				sector = sectors[0];
+			}
+			else
+			{
+				// Having multiple intersections indicates that there are self-referencing sectors in this spot.
+				// In this case we have to check which side of the nearest linedef pos is on, and then use that sector
+				HashSet<Linedef> linedefs = new HashSet<Linedef>(sectors[0].Sidedefs.Count * sectors.Count);
+
+				foreach (Sector s in sectors)
+					foreach (Sidedef sd in s.Sidedefs)
+						linedefs.Add(sd.Line);
+
+				Linedef nearest = MapSet.NearestLinedef(linedefs, pos);
+				double d = nearest.SideOfLine(pos);
+
+				if (d <= 0.0 && nearest.Front != null)
+					sector = nearest.Front.Sector;
+				else if (nearest.Back != null)
+					sector = nearest.Back.Sector;
+				else
+					sector = null;
+			}
 		}
 
 		// This determines which sector the thing is in and links it
 		public void DetermineSector(VisualBlockMap blockmap)
 		{
-            sector = blockmap.GetSectorAt(pos);
+             sector = blockmap.GetSectorAt(pos);
 		}
 
 		// This translates the flags into UDMF fields
diff --git a/Source/Core/VisualModes/VisualBlockMap.cs b/Source/Core/VisualModes/VisualBlockMap.cs
index 9a985921a..5a4c9641b 100755
--- a/Source/Core/VisualModes/VisualBlockMap.cs
+++ b/Source/Core/VisualModes/VisualBlockMap.cs
@@ -76,16 +76,40 @@ namespace CodeImp.DoomBuilder.VisualModes
 
         public Sector GetSectorAt(Vector2D pos)
         {
-            foreach (VisualBlockEntry e in GetBlocks(pos))
-            {
+			List<Sector> sectors = new List<Sector>(1);
+
+			foreach (VisualBlockEntry e in GetBlocks(pos))
                 foreach (Sector s in e.Sectors)
-                {
                     if (s.Intersect(pos))
-                    {
-                        return s;
-                    }
-                }
-            }
+						sectors.Add(s);
+
+			if (sectors.Count == 0)
+			{
+				return null;
+			}
+			else if (sectors.Count == 1)
+			{
+				return sectors[0];
+			}
+			else
+			{
+				// Having multiple intersections indicates that there are self-referencing sectors in this spot.
+				// In this case we have to check which side of the nearest linedef pos is on, and then use that sector
+				HashSet<Linedef> linedefs = new HashSet<Linedef>(sectors[0].Sidedefs.Count * sectors.Count);
+
+				foreach (Sector s in sectors)
+					foreach (Sidedef sd in s.Sidedefs)
+						linedefs.Add(sd.Line);
+
+				Linedef nearest = MapSet.NearestLinedef(linedefs, pos);
+				double d = nearest.SideOfLine(pos);
+
+				if (d <= 0.0 && nearest.Front != null)
+					return nearest.Front.Sector;
+				else if (nearest.Back != null)
+					return nearest.Back.Sector;
+			}
+
             return null;
         }
 
-- 
GitLab