changeset 4:db8b4f975bad

"Tools" patch by Andrew Apted. Adds a few extra tools to Yadex's repertoire: [w] key splits a sector in vertex mode, [T] key transfers properties (especially sectors), and [Z] key is a very useful feature to change the sector of an area (all surrounding linedefs) either to an existing sector or to a new one.
author Matti Hamalainen <ccr@tnsp.org>
date Wed, 21 Sep 2011 04:40:12 +0300
parents 7817e13d25ea
children 45385d3407c4
files src/editloop.cc src/l_prop.cc src/s_misc.cc src/s_prop.cc src/s_split.cc src/t_prop.cc src/yadex.h
diffstat 7 files changed, 496 insertions(+), 1 deletions(-) [+]
line wrap: on
line diff
--- a/src/editloop.cc	Wed Sep 21 04:38:23 2011 +0300
+++ b/src/editloop.cc	Wed Sep 21 04:40:12 2011 +0300
@@ -1707,7 +1707,15 @@
 	 select_linedefs_path (&e.Selected, e.highlighted.num, YS_TOGGLE);
 	 RedrawMap = 1;
 	 }
-
+      // [E]: add linedef and split sector -- [AJA]
+      else if (is.key == 'E' && e.obj_type == OBJ_VERTICES)
+        {
+        if (e.Selected)
+          {
+          MiscOperations (e.obj_type, &e.Selected, 5);
+          RedrawMap = 1;
+          }
+        }
       // [E]: Select/unselect all 1s linedefs in path
       else if (is.key == 'E' && e.highlighted._is_linedef ())
 	 {
@@ -1972,6 +1980,17 @@
          StretchSelBox = false;
          }
 
+      // [w]: split sector between vertices
+      else if (is.key == 'w' && e.obj_type == OBJ_VERTICES
+         && e.Selected && e.Selected->next && ! e.Selected->next->next)
+         {
+         SplitSector (e.Selected->next->objnum, e.Selected->objnum);
+         ForgetSelection (&e.Selected);
+         RedrawMap = 1;
+         DragObject = false;
+         StretchSelBox = false;
+         }
+
       // [x]: spin things 1/8 turn clockwise
       else if (is.key == 'x' && e.obj_type == OBJ_THINGS
          && (e.Selected || e.highlighted ()))
@@ -2252,6 +2271,21 @@
 	 RedrawMap = 1;
 	 }
 
+      // [Z] Set sector on surrounding linedefs (AJA)
+      else if (is.key == 'Z' && e.pointer_in_window) 
+         {
+         if (e.obj_type == OBJ_SECTORS && e.Selected)
+            {
+            SuperSectorSelector (e.pointer_x, e.pointer_y,
+               e.Selected->objnum);
+            }
+         else
+            {
+            SuperSectorSelector (e.pointer_x, e.pointer_y, OBJ_NO_NONE);
+            }
+         RedrawMap = 1;
+         }
+
       // [!] Debug info (not documented)
       else if (is.key == '!')
          {
@@ -2265,6 +2299,30 @@
 	 RedrawMap = 1;
          }
 
+      // [T] Transfer properties to selected objects (AJA)
+      else if (is.key == 'T' && e.Selected 
+            && e.highlighted.num >= 0)
+         {
+         switch (e.obj_type)
+            {
+            case OBJ_SECTORS:
+               TransferSectorProperties (e.highlighted.num, e.Selected);
+               RedrawMap = 1;
+               break;
+            case OBJ_THINGS:
+               TransferThingProperties (e.highlighted.num, e.Selected);
+               RedrawMap = 1;
+               break;
+            case OBJ_LINEDEFS:
+               TransferLinedefProperties (e.highlighted.num, e.Selected);
+               RedrawMap = 1;
+               break;
+            default:
+               Beep ();
+               break;
+            }
+         }
+
       // [|] Show colours (not documented)
       else if (is.key == '|')
          {
--- a/src/l_prop.cc	Wed Sep 21 04:38:23 2011 +0300
+++ b/src/l_prop.cc	Wed Sep 21 04:40:12 2011 +0300
@@ -547,3 +547,37 @@
   return ((ldtgroup_t *)ptr)->desc;
 }
 
+/*
+ *   TransferLinedefProperties
+ *
+ *   Note: right now nothing is done about sidedefs.  Being able to
+ *   (intelligently) transfer sidedef properties from source line to
+ *   destination linedefs could be a useful feature -- though it is
+ *   unclear the best way to do it.  OTOH not touching sidedefs might
+ *   be useful too.
+ *
+ *   -AJA- 2001-05-27
+ */
+#define LINEDEF_FLAG_KEEP  (1 + 4)
+
+void TransferLinedefProperties (int src_linedef, SelPtr linedefs)
+{
+   SelPtr cur;
+   wad_ldflags_t src_flags = LineDefs[src_linedef].flags & ~LINEDEF_FLAG_KEEP;
+
+   for (cur=linedefs; cur; cur=cur->next)
+   {
+      if (! is_obj(cur->objnum))
+         continue;
+
+      // don't transfer certain flags
+      LineDefs[cur->objnum].flags &= LINEDEF_FLAG_KEEP;
+      LineDefs[cur->objnum].flags |= src_flags;
+
+      LineDefs[cur->objnum].type = LineDefs[src_linedef].type;
+      LineDefs[cur->objnum].tag  = LineDefs[src_linedef].tag;
+
+      MadeChanges = 1;
+   }
+}
+
--- a/src/s_misc.cc	Wed Sep 21 04:38:23 2011 +0300
+++ b/src/s_misc.cc	Wed Sep 21 04:40:12 2011 +0300
@@ -29,11 +29,15 @@
 
 
 #include "yadex.h"
+#include <math.h>
+
 #include "entry.h"
 #include "gfx.h"
 #include "levels.h"
 #include "objid.h"
 #include "selectn.h"
+#include "objects.h"
+#include "dialog.h"
 
 
 /*
@@ -179,4 +183,340 @@
 MadeChanges = 1;
 }
 
+
+static int find_linedef_for_area (int x, int y, int& side)
+{
+   int n, m, curx;
+   int best_match = -1;
+
+   curx = 32767;  // Oh yes, one more hard-coded constant!
+
+   for (n = 0; n < NumLineDefs; n++)
+      if ((Vertices[LineDefs[n].start].y > y)
+       != (Vertices[LineDefs[n].end].y > y))
+      {
+         int lx0 = Vertices[LineDefs[n].start].x;
+         int ly0 = Vertices[LineDefs[n].start].y;
+         int lx1 = Vertices[LineDefs[n].end].x;
+         int ly1 = Vertices[LineDefs[n].end].y;
+         m = lx0 + (int) ((long) (y - ly0) * (long) (lx1 - lx0)
+                                           / (long) (ly1 - ly0));
+         if (m >= x && m < curx)
+         {
+            curx = m;
+            best_match = n;
+         }
+      }
+
+   /* now look if this linedef has a sidedef bound to one sector */
+   if (best_match < 0)
+      return OBJ_NO_NONE;
+
+   if (Vertices[LineDefs[best_match].start].y
+     > Vertices[LineDefs[best_match].end].y)
+      side = 1;
+   else
+      side = 2;
+
+   return best_match;
+}
+
+/*
+   compute the angle between lines AB and BC, going anticlockwise.
+   result is in degrees 0 - 359.  A, B and C are vertex indices.
+   -AJA- 2001-05-09
+ */
+#define DEBUG_ANGLE  0
+
+static double angle_between_linedefs (int A, int B, int C)
+{
+   int a_dx = Vertices[B].x - Vertices[A].x;
+   int a_dy = Vertices[B].y - Vertices[A].y;
+   
+   int c_dx = Vertices[B].x - Vertices[C].x;
+   int c_dy = Vertices[B].y - Vertices[C].y;
+   
+   double AB_angle = (a_dx == 0) ? (a_dy >= 0 ? 90 : -90) :
+      atan2 (a_dy, a_dx) * 180 / M_PI;
+
+   double CB_angle = (c_dx == 0) ? (c_dy >= 0 ? 90 : -90) :
+      atan2 (c_dy, c_dx) * 180 / M_PI;
+
+   double result = CB_angle - AB_angle;
+
+   if (result >= 360)
+      result -= 360;
+   
+   while (result < 0)
+      result += 360;
+
+#if (DEBUG_ANGLE)
+   fprintf(stderr, "ANGLE %1.6f  (%d,%d) -> (%d,%d) -> (%d,%d)\n",
+      result, Vertices[A].x, Vertices[A].y,
+      Vertices[B].x, Vertices[B].y, Vertices[C].x, Vertices[C].y);
+#endif
+
+   return result;
+}
+
+/*
+   follows the path clockwise from the given start line, adding each
+   line into the appropriate set.  If the path is not closed, zero is
+   returned.  
+   
+   -AJA- 2001-05-09
+ */
+#define DEBUG_PATH  0
+
+static int select_sides_in_closed_path (bitvec_c& ld_side1,
+    bitvec_c& ld_side2, int line, int side)
+{
+   int cur_vert, prev_vert, final_vert;
+   
+   if (side == 1)
+   {
+      ld_side1.set (line);
+      cur_vert = LineDefs[line].end;
+      prev_vert = final_vert = LineDefs[line].start;
+   }
+   else
+   {
+      ld_side2.set (line);
+      cur_vert = LineDefs[line].start;
+      prev_vert = final_vert = LineDefs[line].end;
+   }
+
+#if (DEBUG_PATH)
+      fprintf(stderr, "PATH: line %d  side %d  cur %d  final %d\n",
+         line, side, cur_vert, final_vert);
+#endif
+
+   while (cur_vert != final_vert)
+   {
+      int next_line = OBJ_NO_NONE;
+      int next_vert = OBJ_NO_NONE;
+      int next_side;
+      double best_angle = 999;
+
+      // Look for the next linedef in the path.  It's the linedef that
+      // uses the current vertex and is not the current one.
+
+      for (int n = 0; n < NumLineDefs; n++)
+      {
+         if (n == line)
+            continue;
+
+         int other_vert;
+         int which_side;
+
+         if (LineDefs[n].start == cur_vert)
+         {
+            other_vert = LineDefs[n].end;
+            which_side = 1;
+         }
+         else if (LineDefs[n].end == cur_vert)
+         {
+            other_vert = LineDefs[n].start;
+            which_side = 2;
+         }
+         else
+            continue;
+
+         // found adjoining linedef
+         
+         double angle = angle_between_linedefs (prev_vert, cur_vert,
+            other_vert);
+         
+         if (! is_obj (next_line) || angle < best_angle)
+         {
+            next_line = n;
+            next_vert = other_vert;
+            next_side = which_side;
+            
+            best_angle = angle;
+         }
+
+         // Continue the search
+      }
+ 
+      line = next_line;
+      side = next_side;
+
+#if (DEBUG_PATH)
+      fprintf(stderr, "PATH NEXT: line %d  side %d  vert %d  angle %1.6f\n",
+         line, side, next_vert, best_angle);
+#endif
+
+      // None ?  Path cannot be closed
+      if (! is_obj (line))
+         return 0;
+
+      // Line already seen ?  Under normal circumstances this won't
+      // happen, but it _can_ happen and indicates a non-closed
+      // structure
+      if (ld_side1.get (line) || ld_side2.get (line))
+         return 0;
+
+      if (side == 1)
+         ld_side1.set (line);
+      else
+         ld_side2.set (line);
+       
+      prev_vert = cur_vert;
+      cur_vert = next_vert;
+   }
+
+#if (DEBUG_PATH)
+      fprintf(stderr, "PATH CLOSED !\n");
+#endif
+
+   return 1;
+}
+
+/*
+   update the side on a single linedef, using the given sector
+   reference.  Will create a new sidedef if necessary.
+ */
+static void super_set_sector_on_side (int line, wad_sdn_t& side,
+   wad_sdn_t& other, int side_no, int sector)
+{
+   if (is_obj (side) && SideDefs[side].sector == sector)
+   {
+      // there was no change.
+      return;
+   }
+   
+   int must_flip = 0;
+
+   if (! is_obj (side))
+   {
+      // if we're adding a sidedef to a line that has no sides, and
+      // the sidedef would be the 2nd one, then flip the linedef.
+      // Thus we don't end up with invalid lines -- i.e. ones with a
+      // left side but no right side.
+
+      if (! is_obj (other) && side_no == 2)
+         must_flip = 1;
+
+      InsertObject (OBJ_SIDEDEFS, OBJ_NO_NONE, 0, 0);
+      side = NumSideDefs - 1;
+
+      // if we're adding a second side to the linedef, clear out some
+      // of the properties that aren't needed anymore: middle texture,
+      // two-sided flag, and impassible flag.
+      
+      if (is_obj (other))
+      {
+         strncpy (SideDefs[side].tex3,  "-", WAD_TEX_NAME);
+         strncpy (SideDefs[other].tex3, "-", WAD_TEX_NAME);
+
+         LineDefs[line].flags |=  4;  // Set the 2S bit
+         LineDefs[line].flags &= ~1;  // Clear the Im bit
+      }
+   }
+
+   SideDefs[side].sector = sector;
+   
+   if (must_flip)
+   {
+      int temp = LineDefs[line].start;
+      LineDefs[line].start = LineDefs[line].end;
+      LineDefs[line].end = temp;
+
+      temp = side; 
+      side = other; 
+      other = temp;
+   }
+
+   MadeChanges = 1;
+   MadeMapChanges = 1;
+}
+
+static int super_find_sector_model (bitvec_c& ld_side1,
+    bitvec_c& ld_side2)
+{
+   for (int line=0; line < NumLineDefs; line++)
+   {
+      int side1 = LineDefs[line].sidedef1;
+      int side2 = LineDefs[line].sidedef2;
+
+      if (ld_side1.get (line))
+         if (is_obj (side2))
+            return SideDefs[side2].sector;
+
+      if (ld_side2.get (line))
+         if (is_obj (side1))
+            return SideDefs[side1].sector;
+   }
+
+   return OBJ_NO_NONE;
+}
+
+
+/*
+   Change the closed sector at the pointer
+
+   "sector" here really means a bunch of sidedefs that all face
+   inward to the current area under the mouse cursor.  Two basic
+   operations: (a) set the sidedef sector references to a completely
+   new sector, or (b) set them to an existing sector.  This is
+   controlled by the `new_sec' parameter.
+
+   -AJA- 2001-05-08
+ */
+
+void SuperSectorSelector (int map_x, int map_y, int new_sec)
+{
+   int line, side;
+   char msg_buf[200];
+
+   line = find_linedef_for_area (map_x, map_y, side);
+
+   if (! is_obj (line))
+   {
+      Beep ();
+      sprintf (msg_buf, "Chosen area is not closed");
+      Notify (-1, -1, msg_buf, NULL);
+      return;
+   }
+
+   bitvec_c ld_side1 (NumLineDefs);
+   bitvec_c ld_side2 (NumLineDefs);
+   
+   int closed = select_sides_in_closed_path (ld_side1, ld_side2,
+      line, side);
+
+   if (! closed)
+   {
+      Beep ();
+      sprintf (msg_buf, "Area chosen is not closed");
+      Notify (-1, -1, msg_buf, NULL);
+      return;
+   }
+
+   // -AJA- FIXME: look for "islands", closed linedef paths that lie
+   // completely inside the area, i.e. not connected to the main path.
+   // Example: the two pillars at the start of MAP01 of DOOM 2.  See
+   // GetOppositeSector() and the end of SplitSector() for a possible
+   // algorithm.
+   
+   if (! is_obj (new_sec))
+   {
+      int model = super_find_sector_model (ld_side1, ld_side2);
+      InsertObject (OBJ_SECTORS, model, 0, 0);
+      new_sec = NumSectors - 1;
+   }
+   
+   for (line=0; line < NumLineDefs; line++)
+   {
+      if (ld_side1.get (line))
+         super_set_sector_on_side (line, LineDefs[line].sidedef1,
+            LineDefs[line].sidedef2, 1, new_sec);
+
+      else if (ld_side2.get (line))
+         super_set_sector_on_side (line, LineDefs[line].sidedef2,
+            LineDefs[line].sidedef1, 2, new_sec);
+   }
+}
+
 /* end of file */
--- a/src/s_prop.cc	Wed Sep 21 04:38:23 2011 +0300
+++ b/src/s_prop.cc	Wed Sep 21 04:40:12 2011 +0300
@@ -260,6 +260,35 @@
 }
 
 /*
+ *   TransferSectorProperties
+ *
+ *   -AJA- 2001-05-27
+ */
+void TransferSectorProperties (int src_sector, SelPtr sectors)
+{
+   SelPtr cur;
+
+   for (cur=sectors; cur; cur=cur->next)
+   {
+     if (! is_obj(cur->objnum))
+         continue;
+
+      strncpy (Sectors[cur->objnum].floort, Sectors[src_sector].floort,
+            WAD_FLAT_NAME);
+      strncpy (Sectors[cur->objnum].ceilt, Sectors[src_sector].ceilt,
+            WAD_FLAT_NAME);
+
+      Sectors[cur->objnum].floorh  = Sectors[src_sector].floorh;
+      Sectors[cur->objnum].ceilh   = Sectors[src_sector].ceilh;
+      Sectors[cur->objnum].light   = Sectors[src_sector].light;
+      Sectors[cur->objnum].special = Sectors[src_sector].special;
+      Sectors[cur->objnum].tag     = Sectors[src_sector].tag;
+
+      MadeChanges = 1;
+   }
+}
+
+/*
  * 	InputSectorType
  * 	Let the user select a sector type number and return it
  * 	Returns 0 if OK, <>0 if cancelled
--- a/src/s_split.cc	Wed Sep 21 04:38:23 2011 +0300
+++ b/src/s_split.cc	Wed Sep 21 04:40:12 2011 +0300
@@ -36,6 +36,7 @@
 #include "s_linedefs.h"
 #include "selectn.h"
 #include "x_hover.h"
+#include "entry.h"
 
 
 /*
--- a/src/t_prop.cc	Wed Sep 21 04:38:23 2011 +0300
+++ b/src/t_prop.cc	Wed Sep 21 04:40:12 2011 +0300
@@ -36,6 +36,8 @@
 #include "gfx.h"
 #include "levels.h"
 #include "oldmenus.h"
+#include "objid.h"
+#include "objects.h"
 #include "selectn.h"
 #include "things.h"
 
@@ -331,4 +333,30 @@
 }
 
 
+/*
+ *   TransferThingProperties
+ *
+ *   -AJA- 2001-05-27
+ */
+void TransferThingProperties (int src_thing, SelPtr things)
+{
+   SelPtr cur;
+
+   for (cur=things; cur; cur=cur->next)
+   {
+      if (! is_obj(cur->objnum))
+         continue;
+
+      Things[cur->objnum].angle = Things[src_thing].angle;
+      Things[cur->objnum].type  = Things[src_thing].type;
+      Things[cur->objnum].when  = Things[src_thing].when;
+
+      MadeChanges = 1;
+
+      things_types++;
+      things_angles++;
+   }
+}
+
+
 /* end of file */
--- a/src/yadex.h	Wed Sep 21 04:38:23 2011 +0300
+++ b/src/yadex.h	Wed Sep 21 04:40:12 2011 +0300
@@ -479,6 +479,7 @@
 
 // l_prop.cc (previously in editobj.cc)
 void LinedefProperties (int x0, int y0, SelPtr obj);
+void TransferLinedefProperties (int src_linedef, SelPtr linedefs);
 
 // l_unlink.cc
 void unlink_sidedef (SelPtr linedefs, int side1, int side2);
@@ -541,13 +542,16 @@
 void DistributeSectorCeilings (SelPtr); /* SWAP! */
 void RaiseOrLowerSectors (SelPtr obj);
 void BrightenOrDarkenSectors (SelPtr obj);
+void SuperSectorSelector (int map_x, int map_y, int new_sec);
 
 // s_prop.cc (previously in editobj.cc)
 void SectorProperties (int x0, int y0, SelPtr obj);
+void TransferSectorProperties (int src_sector, SelPtr sectors);
 
 // s_split.cc (previously in objects.cc)
 void SplitSector (int, int); /* SWAP! */
 void SplitLineDefsAndSector (int, int); /* SWAP! */
+void MultiSplitLineDefsAndSector (int, int); /* SWAP! */
 
 // swapmem.cc
 void InitSwap (void);
@@ -560,6 +564,7 @@
 // selrect.cc
 // t_prop.c (previously in editobj.c)
 void ThingProperties (int x0, int y0, SelPtr obj);
+void TransferThingProperties (int src_thing, SelPtr things);
 
 // v_merge.cc
 void DeleteVerticesJoinLineDefs (SelPtr ); /* SWAP! */