Blob Blame History Raw
diff -u -r -N yadex-1.7.0/atclib/al_adigits.c yadex-1.7.0-all/atclib/al_adigits.c
--- yadex-1.7.0/atclib/al_adigits.c	1999-08-02 00:52:00.000000000 +1000
+++ yadex-1.7.0-all/atclib/al_adigits.c	2005-01-10 14:30:53.000000000 +1100
@@ -29,5 +29,11 @@
 #include "atclib.h"
 
 
-const char al_adigits[36] = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
+const char al_adigits[36] =
+{
+'0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
+'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J',
+'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T',
+'U', 'V', 'W', 'X', 'Y', 'Z'
+};
 
diff -u -r -N yadex-1.7.0/docsrc/index.html yadex-1.7.0-all/docsrc/index.html
--- yadex-1.7.0/docsrc/index.html	2002-05-09 23:36:06.000000000 +1000
+++ yadex-1.7.0-all/docsrc/index.html	2005-01-10 14:30:53.000000000 +1100
@@ -34,6 +34,7 @@
 
 <ul>
 <li><a href="palette.html">Palette viewer</a>
+<li><a href="preview.html">3D Level Preview</a>
 <li><a href="advanced.html">Advanced user's guide</a>
 <li><a href="../TODO"><code>TODO</code></a>
 <li><a href="yadex.6">The man page for Yadex</a>
diff -u -r -N yadex-1.7.0/docsrc/preview.html yadex-1.7.0-all/docsrc/preview.html
--- yadex-1.7.0/docsrc/preview.html	1970-01-01 10:00:00.000000000 +1000
+++ yadex-1.7.0-all/docsrc/preview.html	2005-01-10 14:34:20.000000000 +1100
@@ -0,0 +1,108 @@
+<html>
+<head>
+<title>Yadex 3D level preview</title>
+</head>
+<body>
+
+<div align="center">
+<img src="logo_small.png" alt="Fancy logo">
+<br>Yadex $VERSION ($SOURCE_DATE)
+<h1>3D Level Preview</h1>
+</div>
+<br>
+<br>
+<br>
+
+	<h2>What's it for</h2>
+
+<p>The 3D level preview function lets you get a rough idea of what
+your level looks like, quickly, without all the hassle of saving,
+building nodes and starting DOOM. It is activated by pressing the
+`R' key while editing a level, and draws the player's view that
+you would see within DOOM (with some limitations). While the
+rendering window is up, you can move around the level using the
+cursor keys and toggle things like texturing and sprites on/off.
+
+	<h2>Key bindings</h2>
+
+<dl>
+<dt>[<kbd>Left</kbd>]
+<br>[<kbd>Right</kbd>]
+<dd>Turn the view left or right. Use the SHIFT key to
+turn a greater amount.
+
+<dt>[<kbd>Up</kbd>]
+<br>[<kbd>Down</kbd>]
+<dd>Move the view forward or back. Use the SHIFT key to
+move a greater distance.
+
+<dt>[<kbd>n</kbd>]
+<br>[<kbd>m</kbd>]
+<dd>Move the view sideways left or right (strafing). Uppercase `N'
+and `M' (i.e. with the SHIFT key) will move a greater distance.
+
+<dt>[<kbd>c</kbd>]
+<br>[<kbd>d</kbd>]
+<dd>Move the view upwards or downwards (flying). Uppercase `C' and
+`D' (i.e. with the SHIFT key) will move a greater distance. Note
+that you cannot move up or down when "walking" mode is enabled.
+
+<dt>[<kbd>t</kbd>]
+<dd>Toggle texture mapping. When disabled (the default), all walls,
+ceilings and floors are drawn with solid (somewhat random) colours.
+
+<dt>[<kbd>s</kbd>]
+<dd>Toggle sprites.
+
+<dt>[<kbd>w</kbd>]
+<dd>Toggle walking mode. When enabled, the view height is always
+above the current floor. For example, if you move forward over a
+cliff, the view will drop down.  When disabled (the default), you can
+fly about the level at any height.
+
+<dt>[<kbd>Esc</kbd>]
+<br>[<kbd>q</kbd>]
+<dd>Exit the 3D level preview. The current viewing state (position,
+direction, etc) are remembered, and will be used next time the 3D
+preview is activated (unless the player object has been moved, or a
+different level was loaded).
+
+</dl>
+
+	<h2>Features</h2>
+
+<ul>
+<li>No BSP (nodes) required !
+<li>Textures and flats are drawn exactly like DOOM, including X/Y
+offsets and upper/lower unpegging flags.
+<li>Sky is handled just like in DOOM (but drawn in solid blue).
+</ul>
+
+	<h2>Limitations</h2>
+
+<ul>
+<li>No lighting, the level appears full-bright all the time.
+<li>No mid-masked textures (rails, gratings) are drawn.
+<li>Thing sprites (especially monsters) are always drawn facing
+you, even when their direction is away from you.
+<li>Sprite positioning may be inaccurate, because their X and
+Y offsets are not honoured.
+<li>Things that are supposed to hang from the ceiling
+(hanging body parts, chandeliers, etc) will appear on the floor.
+<li>There are some glitches in the current renderer, which look like
+"slimetrails" (vertical lines).
+</ul>
+
+	<h2>Caveats</h2>
+
+One last thing. The 3D preview function can use a huge amount of
+memory when texturing and sprites are both enabled (and a large amount
+even when texturing and sprites are both disabled). If your computer
+has a very low amount of memory (e.g. less than 16 MB), then Yadex may
+crash with an out of memory error. I think you are unlikely to hit
+this problem, but if in doubt, save your work first.
+
+
+<p><hr>AJA $SELF_DATE
+</body>
+</html>
diff -u -r -N yadex-1.7.0/GNUmakefile yadex-1.7.0-all/GNUmakefile
--- yadex-1.7.0/GNUmakefile	2003-12-29 04:23:56.000000000 +1100
+++ yadex-1.7.0-all/GNUmakefile	2005-01-10 14:30:53.000000000 +1100
@@ -160,6 +160,7 @@
 	s_swapf		s_vertices	sanity		scrnshot	\
 	selbox		selectn		selpath		selrect		\
 	serialnum	spritdir	sticker		swapmem		\
+ 	r_render	r_images					\
 	t_centre	t_flags		t_prop		t_spin		\
 	textures	things		trace		v_centre	\
 	v_merge		v_polyg		vectext		verbmsg		\
@@ -237,6 +238,7 @@
 	docsrc/legal.html		\
 	docsrc/packagers_guide.html	\
 	docsrc/palette.html		\
+	docsrc/preview.html		\
 	docsrc/reporting.html		\
 	docsrc/tips.html		\
 	docsrc/trivia.html		\
@@ -609,8 +611,9 @@
 #
 ########################################################################
 
-# If Makefile.config doesn't exist, give a hint...
+# If Makefile.config or config.h don't exist, give a hint...
 $(OBJDIR)/Makefile.config:
+$(OBJDIR)/config.h:
 	@echo "Sorry guv'nor, but... did you run ./configure ?" >&2
 	@false
 
@@ -637,7 +640,7 @@
 # Note: the modules of Atclib are not scanned as they all
 # depend on $(HEADERS_ATCLIB) and nothing else.
 
-yadex.dep: $(SRC_NON_GEN)
+yadex.dep: $(SRC_NON_GEN) src/config.h
 	@echo "Generating $@"
 	@makedepend -f- -Y -Iatclib $(SRC_NON_GEN) 2>/dev/null	\
 		| awk 'sub (/^src/, "") == 1 {				\
diff -u -r -N yadex-1.7.0/src/editloop.cc yadex-1.7.0-all/src/editloop.cc
--- yadex-1.7.0/src/editloop.cc	2003-07-05 21:12:32.000000000 +1000
+++ yadex-1.7.0-all/src/editloop.cc	2005-01-10 14:30:53.000000000 +1100
@@ -70,6 +70,7 @@
 #include "x_exchng.h"
 #include "x_hover.h"
 #include "xref.h"
+#include "r_render.h"
 
 #ifdef Y_X11
 #include <X11/Xlib.h>
@@ -304,8 +305,8 @@
 
 e.mb_menu[MBM_EDIT] = new Menu (NULL,
    "~Copy object(s)",          'o',    0,
-   "~Add object",              YK_INS, 0,
-   "~Delete object(s)",        YK_DEL, 0,
+   "~Add object",              'I', 0,
+   "~Delete object(s)",        '\b', 0,
    "~Exchange object numbers", 24,     0,
    "~Preferences...",          YK_F5,  0,
    "~Snap to grid",            'y',    MIF_VTICK, &e.grid_snap,		     0,
@@ -342,6 +343,7 @@
    "~Next object",       'n',   0,
    "~Prev object",       'p',   0,
    "~Jump to object...", 'j',   0,
+   "~Find by type",	 'f',	0,
    NULL);
 
 e.mb_menu[MBM_MISC_L] = new Menu ("Misc. operations",
@@ -1344,8 +1346,8 @@
             }
          }
 
-      // [F8]: pop up the "Misc. operations" menu
-      else if (is.key == YK_F8
+      // [M]: pop up the "Misc. operations" menu
+      else if (is.key == 'M'
          && e.menubar->highlighted () < 0)
          {
          e.modpopup->set (e.menubar->get_menu (MBI_MISC), 1);
@@ -1706,7 +1708,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 ())
 	 {
@@ -1834,6 +1844,56 @@
 	 RedrawMap = 1;
 	 }
 
+      // [f]: find object by type
+      else if (is.key == 'f' && (! e.global || e.highlighted ())) 
+	 {
+	 Objid find_obj;
+	 int otype;
+	 obj_no_t omax,onum;
+	 find_obj.type = e.highlighted () ? e.highlighted.type : e.obj_type;
+	 onum = find_obj.num  = e.highlighted () ? e.highlighted.num  : 0;
+	 omax = GetMaxObjectNum(find_obj.type);
+         switch (find_obj.type)
+            {
+	    case OBJ_SECTORS:
+               if ( ! InputSectorType( 84, 21, &otype))
+		  {
+	          for (onum = e.highlighted () ? onum + 1 : onum; onum <= omax; onum++)
+	             if (Sectors[onum].special == (wad_stype_t) otype)
+			{
+		        find_obj.num = onum;
+	                GoToObject(find_obj);
+		        break;
+		        }
+		  }
+	    break;
+	    case OBJ_THINGS:
+	       if ( ! InputThingType( 42, 21, &otype))
+	 	  {
+                  for (onum = e.highlighted () ? onum + 1 : onum; onum <= omax; onum++)
+	             if (Things[onum].type == (wad_ttype_t) otype)
+                        {
+		        find_obj.num = onum;
+	                GoToObject(find_obj);
+		        break;
+		        }
+		  }
+	    break;
+	    case OBJ_LINEDEFS:
+	       if ( ! InputLinedefType( 0, 21, &otype))
+		  {
+	          for (onum = e.highlighted () ? onum + 1 : onum; onum <= omax; onum++)
+		     if (LineDefs[onum].type == (wad_ldtype_t) otype)
+			{
+		        find_obj.num = onum;
+	                GoToObject(find_obj);
+			break;
+		        }
+		  }
+	    break;
+	    }
+         RedrawMap = 1;
+         }
 #if 0
       // [c]: clear selection and redraw the map
       else if (is.key == 'c')
@@ -1921,6 +1981,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 ()))
@@ -1987,7 +2058,7 @@
       }
       
       // [Del]: delete the current object
-      else if (is.key == YK_DEL
+      else if (is.key == '\b'
          && (e.Selected || e.highlighted ())) /* 'Del' */
 	 {
 	 if (e.obj_type == OBJ_THINGS
@@ -2015,7 +2086,7 @@
 	 }
 
       // [Ins]: insert a new object
-      else if (is.key == YK_INS || is.key == YK_INS + YK_SHIFT) /* 'Ins' */
+      else if (is.key == 'I' || is.key == YK_INS + YK_SHIFT) /* 'Ins' */
 	 {
 	 SelPtr cur;
          int prev_obj_type = e.obj_type;
@@ -2201,12 +2272,34 @@
 	 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 == '!')
          {
          DumpSelection (e.Selected);
          }
 
+      // [R] Render 3D view (AJA)
+      else if (is.key == 'R')
+        {
+        Render3D ();
+        RedrawMap = 1;
+        }
+
       // [@] Show font (not documented)
       else if (is.key == '@')
          {
@@ -2214,6 +2307,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 == '|')
          {
diff -u -r -N yadex-1.7.0/src/editloop.h yadex-1.7.0-all/src/editloop.h
--- yadex-1.7.0/src/editloop.h	2000-01-11 00:40:12.000000000 +1100
+++ yadex-1.7.0-all/src/editloop.h	2005-01-10 14:30:53.000000000 +1100
@@ -6,6 +6,9 @@
 
 void EditorLoop (const char *); /* SWAP! */
 const char *SelectLevel (int levelno);
+extern int InputSectorType(int x0, int y0, int *number);
+extern int InputLinedefType(int x0, int y0, int *number);
+extern int InputThingType(int x0, int y0, int *number);
 
 
 
diff -u -r -N yadex-1.7.0/src/gcolour1.cc yadex-1.7.0-all/src/gcolour1.cc
--- yadex-1.7.0/src/gcolour1.cc	2003-03-28 23:37:32.000000000 +1100
+++ yadex-1.7.0-all/src/gcolour1.cc	2005-01-10 14:30:53.000000000 +1100
@@ -127,6 +127,21 @@
   }
   verbmsg ("colours: colour %d remapped to %d (delta %d)\n",
     IMG_TRANSP, colour0, smallest_delta);
+   
+   rgb_c med_blue (0, 0, 128);
+   sky_colour = 0;
+   smallest_delta = INT_MAX;
+ 
+   for (size_t n = 0; n < DOOM_COLOURS; n++)
+   {
+     int delta = med_blue - rgb_values[n];
+     if (delta < smallest_delta)
+     {
+       sky_colour = n;
+       smallest_delta = delta;
+     }
+   }
+   verbmsg ("Sky Colour remapped to %d (delta %d)\n", sky_colour, smallest_delta);
 }
 
 #endif
diff -u -r -N yadex-1.7.0/src/gcolour2.cc yadex-1.7.0-all/src/gcolour2.cc
--- yadex-1.7.0/src/gcolour2.cc	2003-03-28 23:37:32.000000000 +1100
+++ yadex-1.7.0-all/src/gcolour2.cc	2005-01-10 14:30:53.000000000 +1100
@@ -35,4 +35,5 @@
 
 pcolour_t *game_colour = 0;	// Pixel values for the DOOM_COLOURS game clrs.
 int colour0;			// Game colour to which g. colour 0 is remapped
+int sky_colour;			// Game colour for a medium sky blue
 
diff -u -r -N yadex-1.7.0/src/gcolour2.h yadex-1.7.0-all/src/gcolour2.h
--- yadex-1.7.0/src/gcolour2.h	2000-08-11 07:18:16.000000000 +1000
+++ yadex-1.7.0-all/src/gcolour2.h	2005-01-10 14:30:53.000000000 +1100
@@ -10,4 +10,5 @@
 
 extern pcolour_t *game_colour;  // Pixel values for the DOOM_COLOURS game clrs.
 extern int colour0;		// Game colour to which g. colour 0 is remapped
+extern int sky_colour;		// Game colour for a medium blue sky
 
diff -u -r -N yadex-1.7.0/src/l_prop.cc yadex-1.7.0-all/src/l_prop.cc
--- yadex-1.7.0/src/l_prop.cc	2003-03-28 23:37:32.000000000 +1100
+++ yadex-1.7.0-all/src/l_prop.cc	2005-01-10 14:30:53.000000000 +1100
@@ -105,7 +105,7 @@
  *	Prototypes of private functions
  */
 static char *GetTaggedLineDefFlag (int linedefnum, int flagndx);
-static int InputLinedefType (int x0, int y0, int *number);
+int InputLinedefType (int x0, int y0, int *number);
 static const char *PrintLdtgroup (void *ptr);
 
 
@@ -475,7 +475,7 @@
  *	Let the user select a linedef type number and return it.
  *	Returns 0 if OK, <>0 if cancelled
  */
-static int InputLinedefType (int x0, int y0, int *number)
+int InputLinedefType (int x0, int y0, int *number)
 {
   int         r;
   int         ldtgno = 0;
@@ -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;
+   }
+}
+
diff -u -r -N yadex-1.7.0/src/r_images.cc yadex-1.7.0-all/src/r_images.cc
--- yadex-1.7.0/src/r_images.cc	1970-01-01 10:00:00.000000000 +1000
+++ yadex-1.7.0-all/src/r_images.cc	2005-01-10 14:30:53.000000000 +1100
@@ -0,0 +1,400 @@
+/*
+ *	r_images.cc
+ *	AJA 2002-04-23 (based on textures.cc and flats.cc)
+ */
+
+
+/*
+This file is part of Yadex.
+
+Yadex incorporates code from DEU 5.21 that was put in the public domain in
+1994 by RaphaŽl Quinet and Brendon Wyber.
+
+The rest of Yadex is Copyright © 1997-2000 Andrť Majorel.
+
+This program is free software; you can redistribute it and/or modify it under
+the terms of the GNU General Public License as published by the Free Software
+Foundation; either version 2 of the License, or (at your option) any later
+version.
+
+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.
+
+You should have received a copy of the GNU General Public License along with
+this program; if not, write to the Free Software Foundation, Inc., 59 Temple
+Place, Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+
+#include "yadex.h"
+#ifdef Y_X11
+#include <X11/Xlib.h>
+#endif
+#include "dialog.h"
+#include "game.h"      /* yg_picture_format */
+#include "gfx.h"
+#include "levels.h"
+#include "lists.h"
+#include "patchdir.h"
+#include "pic2img.h"
+#include "sticker.h"
+#include "flats.h"
+#include "textures.h"
+#include "wadfile.h"
+#include "wads.h"
+#include "wadres.h"
+#include "wstructs.h"
+
+#include "r_images.h"
+
+
+/*
+ *	flat_list_entry_match
+ *	Function used by bsearch() to locate a particular 
+ *	flat in the FTexture.
+ */
+static int flat_list_entry_match (const void *key, const void *flat_list_entry)
+{
+return y_strnicmp ((const char *) key,
+      ((const flat_list_entry_t *) flat_list_entry)->name,
+      WAD_FLAT_NAME);
+}
+
+
+/*
+ *  load a flat into a new image.  NULL if not found.
+ */
+
+Img * Flat2Img (const wad_flat_name_t& fname)
+{
+char name[WAD_FLAT_NAME + 1];
+strncpy (name, fname, WAD_FLAT_NAME);
+name[WAD_FLAT_NAME] = 0;
+
+flat_list_entry_t *flat = (flat_list_entry_t *)
+   bsearch (name, flat_list, NumFTexture, sizeof *flat_list,
+         flat_list_entry_match);
+
+if (! flat)  // Not found in list
+   return 0;
+
+int width  = DOOM_FLAT_WIDTH;  // Big deal !
+int height = DOOM_FLAT_HEIGHT;
+
+const Wad_file *wadfile = flat->wadfile;
+wadfile->seek (flat->offset);
+
+Img *img = new Img (width, height, false);
+
+wadfile->read_bytes (img->wbuf (), (long) width * height);
+
+return img;
+}
+
+
+/*
+ * load a wall texture ("TEXTURE1" or "TEXTURE2" object) into an image.
+ * Returns NULL if not found or error.
+ */
+
+Img * Tex2Img (const wad_tex_name_t& texname)
+{
+MDirPtr  dir = 0;	/* main directory pointer to the TEXTURE* entries */
+i32     *offsets;	/* array of offsets to texture names */
+int      n;		/* general counter */
+i16      width, height;	/* size of the texture */
+i16      npatches;	/* number of wall patches used to build this texture */
+i32      numtex;	/* number of texture names in TEXTURE* list */
+i32      texofs;	/* offset in the wad file to the texture data */
+char     tname[WAD_TEX_NAME + 1];	/* texture name */
+char     picname[WAD_PIC_NAME + 1];	/* wall patch name */
+bool     have_dummy_bytes;
+int      header_size;
+int      item_size;
+
+char name[WAD_TEX_NAME + 1];
+strncpy (name, texname, WAD_TEX_NAME);
+name[WAD_TEX_NAME] = 0;
+
+// Iwad-dependant details
+if (yg_texture_format == YGTF_NAMELESS)
+   {
+   have_dummy_bytes = true;
+   header_size      = 14;
+   item_size        = 10;
+   }
+else if (yg_texture_format == YGTF_NORMAL)
+   {
+   have_dummy_bytes = true;
+   header_size      = 14;
+   item_size        = 10;
+   }
+else if (yg_texture_format == YGTF_STRIFE11)
+   {
+   have_dummy_bytes = false;
+   header_size      = 10;
+   item_size        = 6;
+   }
+else
+   {
+   nf_bug ("Bad texture format %d.", (int) yg_texture_format);
+   return 0;
+   }
+
+/* offset for texture we want. */
+texofs = 0;
+// Doom alpha 0.4 : "TEXTURES", no names
+if (yg_texture_lumps == YGTL_TEXTURES && yg_texture_format == YGTF_NAMELESS)
+   {
+   dir = FindMasterDir (MasterDir, "TEXTURES");
+   if (dir != NULL)
+      {
+      dir->wadfile->seek (dir->dir.start);
+      dir->wadfile->read_i32 (&numtex);
+      if (WAD_TEX_NAME < 7) nf_bug ("WAD_TEX_NAME too small");  // Sanity
+      if (! y_strnicmp (name, "TEX", 3)
+            && isdigit (name[3])
+            && isdigit (name[4])
+            && isdigit (name[5])
+            && isdigit (name[6])
+            && name[7] == '\0')
+         {
+         long num;
+         if (sscanf (name + 3, "%4ld", &num) == 1
+               && num >= 0 && num < numtex)
+            {
+            dir->wadfile->seek (dir->dir.start + 4 + 4 * num);
+            dir->wadfile->read_i32 (&texofs);
+            texofs += dir->dir.start;
+            }
+         }
+      }
+   }
+// Doom alpha 0.5 : only "TEXTURES"
+else if (yg_texture_lumps == YGTL_TEXTURES
+      && (yg_texture_format == YGTF_NORMAL || yg_texture_format == YGTF_STRIFE11))
+   {
+   // Is it in TEXTURES ?
+   dir = FindMasterDir (MasterDir, "TEXTURES");
+   if (dir != NULL)  // (Theoretically, it should always exist)
+      {
+      dir->wadfile->seek (dir->dir.start);
+      dir->wadfile->read_i32 (&numtex);
+      /* read in the offsets for texture1 names and info. */
+      offsets = (i32 *) GetMemory ((long) numtex * 4);
+      dir->wadfile->read_i32 (offsets, numtex);
+      for (n = 0; n < numtex && !texofs; n++)
+         {
+         dir->wadfile->seek (dir->dir.start + offsets[n]);
+         dir->wadfile->read_bytes (&tname, WAD_TEX_NAME);
+         if (!y_strnicmp (tname, name, WAD_TEX_NAME))
+            texofs = dir->dir.start + offsets[n];
+         }
+      FreeMemory (offsets);
+      }
+   }
+// Other iwads : "TEXTURE1" and "TEXTURE2"
+else if (yg_texture_lumps == YGTL_NORMAL
+      && (yg_texture_format == YGTF_NORMAL || yg_texture_format == YGTF_STRIFE11))
+   {
+   // Is it in TEXTURE1 ?
+   dir = FindMasterDir (MasterDir, "TEXTURE1");
+   if (dir != NULL)  // (Theoretically, it should always exist)
+      {
+      dir->wadfile->seek (dir->dir.start);
+      dir->wadfile->read_i32 (&numtex);
+      /* read in the offsets for texture1 names and info. */
+      offsets = (i32 *) GetMemory ((long) numtex * 4);
+      dir->wadfile->read_i32 (offsets, numtex);
+      for (n = 0; n < numtex && !texofs; n++)
+         {
+         dir->wadfile->seek (dir->dir.start + offsets[n]);
+         dir->wadfile->read_bytes (&tname, WAD_TEX_NAME);
+         if (!y_strnicmp (tname, name, WAD_TEX_NAME))
+            texofs = dir->dir.start + offsets[n];
+         }
+      FreeMemory (offsets);
+      }
+   // Well, then is it in TEXTURE2 ?
+   if (texofs == 0)
+      {
+      dir = FindMasterDir (MasterDir, "TEXTURE2");
+      if (dir != NULL)  // Doom II has no TEXTURE2
+         {
+         dir->wadfile->seek (dir->dir.start);
+         dir->wadfile->read_i32 (&numtex);
+         /* read in the offsets for texture2 names */
+         offsets = (i32 *) GetMemory ((long) numtex * 4);
+         dir->wadfile->read_i32 (offsets, numtex);
+         for (n = 0; n < numtex && !texofs; n++)
+            {
+            dir->wadfile->seek (dir->dir.start + offsets[n]);
+            dir->wadfile->read_bytes (&tname, WAD_TEX_NAME);
+            if (!y_strnicmp (tname, name, WAD_TEX_NAME))
+               texofs = dir->dir.start + offsets[n];
+            }
+         FreeMemory (offsets);
+         }
+      }
+   }
+else
+   nf_bug ("Invalid texture_format/texture_lumps combination.");
+
+/* texture name not found */
+if (texofs == 0)
+   return 0;
+
+/* read the info for this texture */
+i32 header_ofs;
+if (yg_texture_format == YGTF_NAMELESS)
+   header_ofs = texofs;
+else
+   header_ofs = texofs + WAD_TEX_NAME;
+dir->wadfile->seek (header_ofs + 4);
+dir->wadfile->read_i16 (&width);
+dir->wadfile->read_i16 (&height);
+if (have_dummy_bytes)
+   {
+   i16 dummy;
+   dir->wadfile->read_i16 (&dummy);
+   dir->wadfile->read_i16 (&dummy);
+   }
+dir->wadfile->read_i16 (&npatches);
+
+/* Compose the texture */
+Img *texbuf = new Img (width, height, false);
+
+/* Paste onto the buffer all the patches that the texture is
+   made of. */
+for (n = 0; n < npatches; n++)
+   {
+   i16 xofs, yofs;	// offset in texture space for the patch
+   i16 pnameind;	// index of patch in PNAMES
+
+   dir->wadfile->seek (header_ofs + header_size + (long) n * item_size);
+   dir->wadfile->read_i16 (&xofs);
+   dir->wadfile->read_i16 (&yofs);
+   dir->wadfile->read_i16 (&pnameind);
+
+   if (have_dummy_bytes)
+      {
+      i16 stepdir;
+      i16 colormap;
+      dir->wadfile->read_i16 (&stepdir);   // Always 1, unused.
+      dir->wadfile->read_i16 (&colormap);  // Always 0, unused.
+      }
+
+   /* AYM 1998-08-08: Yes, that's weird but that's what Doom
+      does. Without these two lines, the few textures that have
+      patches with negative y-offsets (BIGDOOR7, SKY1, TEKWALL1,
+      TEKWALL5 and a few others) would not look in the texture
+      viewer quite like in Doom. This should be mentioned in
+      the UDS, by the way. */
+   if (yofs < 0)
+      yofs = 0;
+
+   Lump_loc loc;
+      {
+      wad_pic_name_t *wname = patch_dir.name_for_num (pnameind);
+      if (wname == 0)
+         {
+         warn ("texture \"%.*s\": patch %2d has bad index %d.\n",
+               WAD_TEX_NAME, tname, (int) n, (int) pnameind);
+         continue;
+         }
+      patch_dir.loc_by_name ((const char *) *wname, loc);
+      *picname = '\0';
+      strncat (picname, (const char *) *wname, sizeof picname - 1);
+      }
+
+   if (LoadPicture (*texbuf, picname, loc, xofs, yofs, 0, 0))
+      warn ("texture \"%.*s\": patch \"%.*s\" not found.\n",
+            WAD_TEX_NAME, tname, WAD_PIC_NAME, picname);
+   }
+
+return texbuf;
+}
+
+
+/* --- ImageCache methods --- */
+
+
+Img *ImageCache::GetFlat (const wad_flat_name_t& fname)
+{
+std::string f_str = WadToString(fname);
+
+flat_map_t::iterator P = flats.find (f_str);
+
+if (P != flats.end ())
+   return P->second;
+
+// flat not in the list yet.  Add it.
+
+Img *result = Flat2Img (fname);
+flats[f_str] = result;
+
+// note that a NULL return from Flat2Img is OK, it means that no
+// such flat exists.  Our renderer will revert to using a solid
+// colour.
+
+return result;
+}
+
+
+Img *ImageCache::GetTex (const wad_tex_name_t& tname)
+{
+if (tname[0] == 0 || tname[0] == '-')
+   return 0;
+
+std::string t_str = WadToString(tname);
+
+tex_map_t::iterator P = textures.find (t_str);
+
+if (P != textures.end ())
+   return P->second;
+
+// texture not in the list yet.  Add it.
+
+Img *result = Tex2Img (tname);
+textures[t_str] = result;
+
+// note that a NULL return from Tex2Img is OK, it means that no
+// such texture exists.  Our renderer will revert to using a solid
+// colour.
+
+return result;
+}
+
+
+Img *ImageCache::GetSprite (const wad_ttype_t& type)
+{
+sprite_map_t::iterator P = sprites.find (type);
+
+if (P != sprites.end ())
+   return P->second;
+
+// sprite not in the list yet.  Add it.
+
+Img *result = 0;
+
+const char *sprite_root = get_thing_sprite (type);
+if (sprite_root)
+   {
+   Lump_loc loc;
+   wad_res.sprites.loc_by_root (sprite_root, loc);
+   result = new Img ();
+
+   if (LoadPicture (*result, sprite_root, loc, 0, 0) != 0)
+      {
+      delete result;
+      result = 0;
+      }
+   }
+
+// note that a NULL image is OK.  Our renderer will just ignore the
+// missing sprite.
+
+sprites[type] = result;
+return result;
+}
diff -u -r -N yadex-1.7.0/src/r_images.h yadex-1.7.0-all/src/r_images.h
--- yadex-1.7.0/src/r_images.h	1970-01-01 10:00:00.000000000 +1000
+++ yadex-1.7.0-all/src/r_images.h	2005-01-10 14:30:53.000000000 +1100
@@ -0,0 +1,69 @@
+/*
+ *	r_images.h
+ *	AJA 2002-04-27
+ */
+
+
+#ifndef YH_R_IMAGES  /* DO NOT INSERT ANYTHING BEFORE THIS LINE */
+#define YH_R_IMAGES
+
+
+#include <map>
+#include <algorithm>
+#include <string>
+
+
+struct ImageCache
+{
+public:
+   typedef std::map<std::string, Img *> flat_map_t;
+   typedef std::map<std::string, Img *> tex_map_t;
+   typedef std::map<wad_ttype_t, Img *> sprite_map_t;
+
+   flat_map_t   flats;
+   tex_map_t    textures;
+   sprite_map_t sprites;
+
+   static std::string WadToString(const wad_flat_name_t& fname)
+   {
+      int len;
+
+      for (len = 0; len < WAD_NAME && fname[len]; len++)
+      { }
+        
+      return std::string(fname, len);
+   }
+
+   static void DeleteFlat(const flat_map_t::value_type& P)
+      {
+      delete P.second;
+      }
+
+   static void DeleteTex(const tex_map_t::value_type& P)
+      {
+      delete P.second;
+      }
+
+   static void DeleteSprite(const sprite_map_t::value_type& P)
+      {
+      delete P.second;
+      }
+
+   ~ImageCache ()
+      {
+      std::for_each (flats.begin (), flats.end (), DeleteFlat);
+      std::for_each (textures.begin (), textures.end (), DeleteTex);
+      std::for_each (sprites.begin (), sprites.end (), DeleteSprite);
+
+      flats.clear ();
+      textures.clear ();
+      sprites.clear ();
+      }
+
+   Img *GetFlat   (const wad_flat_name_t& fname);
+   Img *GetTex    (const wad_tex_name_t& tname);
+   Img *GetSprite (const wad_ttype_t& type);
+};
+
+
+#endif  /* DO NOT ADD ANYTHING AFTER THIS LINE */
diff -u -r -N yadex-1.7.0/src/r_render.cc yadex-1.7.0-all/src/r_render.cc
--- yadex-1.7.0/src/r_render.cc	1970-01-01 10:00:00.000000000 +1000
+++ yadex-1.7.0-all/src/r_render.cc	2005-01-10 14:30:53.000000000 +1100
@@ -0,0 +1,1275 @@
+/*
+ *	r_render.cc
+ *	3D Rendering
+ *	AJA 2002-04-21
+ */
+
+
+/*
+This file is part of Yadex.
+
+Yadex incorporates code from DEU 5.21 that was put in the public domain in
+1994 by RaphaŽl Quinet and Brendon Wyber.
+
+The rest of Yadex is Copyright © 1997-2000 Andrť Majorel.
+
+This program is free software; you can redistribute it and/or modify it under
+the terms of the GNU General Public License as published by the Free Software
+Foundation; either version 2 of the License, or (at your option) any later
+version.
+
+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.
+
+You should have received a copy of the GNU General Public License along with
+this program; if not, write to the Free Software Foundation, Inc., 59 Temple
+Place, Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+
+#include "yadex.h"
+
+#include <math.h>
+#include <vector>
+#include <map>
+#include <algorithm>
+
+#ifdef Y_X11
+#include <X11/Xlib.h>
+#include <X11/Xutil.h>
+#endif
+#include "levels.h"
+#include "wstructs.h"
+#include "gfx.h"
+#include "img.h"
+#include "sticker.h"
+#include "gamesky.h"
+#include "things.h"
+#include "wadres.h"
+#include "objid.h"
+#include "objects.h"
+#include "pic2img.h"
+#include "rgb.h"
+#include "gcolour2.h"
+
+#include "r_render.h"
+#include "r_images.h"
+
+
+#define ML_UPPER_UNPEGGED  0x08
+#define ML_LOWER_UNPEGGED  0x10
+
+
+struct Y_View
+{
+public:
+   int p_type, px, py;
+   // player type and position.
+
+   float x, y; 
+   int z;
+   // view position.
+
+   static const int EYE_HEIGHT = 41;
+   // standard height above the floor.
+
+   float angle;
+   float Sin, Cos;
+   // view direction.
+
+   int sw, sh;
+   Img *screen;
+   // screen image.
+
+   bool texturing;
+   bool sprites;
+   bool walking;
+
+   ImageCache *im_ch;
+
+   int *thing_floors;
+
+   Y_View () { memset (this, 0, sizeof *this); }
+
+   void SetAngle (float new_ang)
+      {
+      angle = new_ang;
+
+      if (angle >= TWOPI)
+         angle -= TWOPI;
+      else if (angle < 0)
+         angle += TWOPI;
+
+      Sin = sin (angle);
+      Cos = cos (angle);
+      }
+
+   void CalcViewZ ()
+      {
+        Objid o;
+        GetCurObject (o, OBJ_SECTORS, int (x), int (y));
+      int secnum = o.num;
+      if (secnum >= 0)
+         z = Sectors[secnum].floorh + EYE_HEIGHT;
+      }
+
+   void ClearScreen ()
+      {
+      memset (screen->wbuf (), colour0, sw * sh);
+      }
+
+   void PutScreen (int x, int y)
+      {
+      DrawScreenBox3D (x, y, x + BOX_BORDER*2 + sw, y + BOX_BORDER*2 + sh);
+
+      Sticker sticker (*screen, true);
+
+      sticker.draw (drw, 't', x + BOX_BORDER, y + BOX_BORDER);
+      }
+
+   void FindThingFloors ()
+   {
+   thing_floors = new int[NumThings];
+
+   for (int i = 0; i < NumThings; i++)
+      {
+        Objid o;
+        GetCurObject (o, OBJ_SECTORS, Things[i].xpos, 
+            Things[i].ypos);
+      int secnum = o.num;
+      
+      if (secnum < 0)
+         thing_floors[i] = 0;
+      else
+         thing_floors[i] = Sectors[secnum].floorh;
+      }
+   }
+};
+
+
+static Y_View view;
+
+
+struct DrawSurf
+{
+public:
+   enum
+      {
+      K_INVIS = 0,
+      K_FLAT,
+      K_TEXTURE
+      };
+   int kind;  
+
+   int h1, h2, tex_h;
+   // heights for the surface (h1 is above h2).
+
+   Img *img;
+   img_pixel_t col;  /* used if img is zero */
+
+   enum
+      {
+      SOLID_ABOVE = 1,
+      SOLID_BELOW = 2
+      };
+   int y_clip;
+
+   /* CTor */
+
+   DrawSurf () { kind = K_INVIS; img = 0; }
+
+   void FindFlat (const wad_flat_name_t& fname, Sector *sec)
+      {
+      if (view.texturing)
+         {
+         img = view.im_ch->GetFlat (fname);
+
+         if (img != 0)
+            return;
+         }
+      col = 0x70 + ((sec - Sectors) % 48);
+      }
+
+   void FindTex (const wad_tex_name_t& tname, LineDef *ld)
+      {
+      if (view.texturing)
+         {
+         img = view.im_ch->GetTex (tname);
+
+         if (img != 0)
+            return;
+         }
+      col = 0x30 + ((ld - LineDefs) % 64);
+
+      if (col >= 0x60)
+         col += 0x70;
+      }
+};
+
+
+struct DrawWall
+{
+public:
+   typedef std::vector<struct DrawWall *> vec_t;
+
+   Thing *th;
+   // when `th' is non-zero, this is actually a sprite, and `ld' and
+   // `sd' will be zero.  Sprites use the info in the `ceil' surface.
+
+   LineDef *ld;
+   SideDef *sd;
+   Sector *sec;
+
+   int side;
+   // which side this wall faces (0 right, 1 left)
+
+   float ang1, dang, cur_ang;
+   float base_ang;
+   // clipped angles
+
+   float dist, t_dist;
+   float normal;
+   // line constants
+
+   double iz1, diz, cur_iz; 
+   double mid_iz;
+   // distance values (inverted, so they can be lerped)
+
+   float spr_tx1;
+   // translate coord, for sprite
+
+   int sx1, sx2;
+   // screen X coordinates
+ 
+   int oy1, oy2;
+   // for sprites, the remembered open space to clip to
+
+   /* surfaces */
+   
+   DrawSurf ceil;
+   DrawSurf upper;
+   DrawSurf lower;
+   DrawSurf floor;
+
+   static const double IZ_EPSILON = 0.000001;
+
+   /* PREDICATES */
+
+   struct MidDistCmp
+      {
+      inline bool operator() (const DrawWall * A, const DrawWall * B) const
+         {
+         return A->mid_iz > B->mid_iz;
+         }
+      };
+
+   struct DistCmp
+      {
+      inline bool operator() (const DrawWall * A, const DrawWall * B) const
+         {
+         if (fabs (A->cur_iz - B->cur_iz) < IZ_EPSILON)
+            return A->diz > B->diz;
+
+         return A->cur_iz > B->cur_iz;
+         }
+      };
+
+   struct SX1Cmp
+      {
+      inline bool operator() (const DrawWall * A, const DrawWall * B) const
+         {
+         return A->sx1 < B->sx1;
+         }
+
+      inline bool operator() (const DrawWall * A, int x) const
+         {
+         return A->sx1 < x;
+         }
+
+      inline bool operator() (int x, const DrawWall * A) const
+         {
+         return x < A->sx1;
+         }
+      };
+
+   struct SX2Less
+      {
+      int x;
+
+      SX2Less (int _x) : x (_x) { }
+
+      inline bool operator() (const DrawWall * A) const
+         {
+         return A->sx2 < x;
+         }
+      };
+
+   /* methods */
+
+   void ComputeWallSurface ()
+      {
+      Sector *front = sec;
+      Sector *back  = 0;
+
+      if (is_obj (side ? ld->sidedef1 : ld->sidedef2))
+         {
+         SideDef *bsd = SideDefs + (side ? ld->sidedef1 : ld->sidedef2);
+
+         if (is_obj (bsd->sector))
+            back = Sectors + bsd->sector;
+         }
+
+      bool sky_upper = back && is_sky (front->ceilt) && is_sky (back->ceilt);
+
+      if ((front->ceilh > view.z || is_sky (front->ceilt)) && ! sky_upper) 
+         {
+         ceil.kind = DrawSurf::K_FLAT;
+         ceil.h1 = +99999;
+         ceil.h2 = front->ceilh;
+         ceil.tex_h = ceil.h2;
+         ceil.y_clip = DrawSurf::SOLID_ABOVE;
+
+         if (is_sky (front->ceilt))
+            ceil.col = sky_colour;
+         else
+            ceil.FindFlat (front->ceilt, front);
+         }
+
+      if (front->floorh < view.z)
+         {
+         floor.kind = DrawSurf::K_FLAT;
+         floor.h1 = front->floorh;
+         floor.h2 = -99999;
+         floor.tex_h = floor.h1;
+         floor.y_clip = DrawSurf::SOLID_BELOW;
+
+         if (is_sky (front->floort))
+            floor.col = sky_colour;
+         else
+            floor.FindFlat (front->floort, front);
+         }
+
+      if (! back)
+         {
+         /* ONE-sided line */
+
+         lower.kind = DrawSurf::K_TEXTURE;
+         lower.h1 = front->ceilh;
+         lower.h2 = front->floorh;
+         lower.y_clip = DrawSurf::SOLID_ABOVE | DrawSurf::SOLID_BELOW;
+
+         lower.FindTex (sd->tex3, ld);
+
+         if (lower.img && (ld->flags & ML_LOWER_UNPEGGED))
+            lower.tex_h = lower.h2 + lower.img->height ();
+         else
+            lower.tex_h = lower.h1;
+         }
+      else
+         {
+         /* TWO-sided line */
+
+         if (back->ceilh < front->ceilh && ! sky_upper)
+            {
+            upper.kind = DrawSurf::K_TEXTURE;
+            upper.h1 = front->ceilh;
+            upper.h2 = back->ceilh;
+            upper.tex_h = upper.h1;
+            upper.y_clip = DrawSurf::SOLID_ABOVE;
+
+            upper.FindTex (sd->tex1, ld);
+
+            if (upper.img && ! (ld->flags & ML_UPPER_UNPEGGED))
+               upper.tex_h = upper.h2 + upper.img->height ();
+            else
+               upper.tex_h = upper.h1;
+            }
+
+         if (back->floorh > front->floorh)
+            {
+            lower.kind = DrawSurf::K_TEXTURE;
+            lower.h1 = back->floorh;
+            lower.h2 = front->floorh;
+            lower.y_clip = DrawSurf::SOLID_BELOW;
+
+            lower.FindTex (sd->tex2, ld);
+
+            if (ld->flags & ML_LOWER_UNPEGGED)
+               lower.tex_h = front->ceilh;
+            else
+               lower.tex_h = lower.h1;
+            }
+         }
+      }
+};
+
+
+struct RendInfo
+{
+public:
+   DrawWall::vec_t walls;
+   // complete set of walls/sprites to draw.
+
+   DrawWall::vec_t active;
+   // the active list.  Pointers here are always duplicates of ones in
+   // the walls list (no need to `delete' any of them).
+
+   std::vector<double> depth_x;  
+   // inverse distances over X range, 0 when empty.
+
+   int open_y1;
+   int open_y2;
+
+   static const double Y_SLOPE = 1.70;
+
+   static void DeleteWall (DrawWall *P)
+      {
+      delete P;
+      }
+
+   ~RendInfo ()
+      {
+      std::for_each (walls.begin (), walls.end (), DeleteWall);
+      
+      walls.clear ();
+      active.clear ();
+      }
+
+   void InitDepthBuf (int width)
+      {
+      depth_x.resize (width);
+
+      std::fill_n (depth_x.begin (), width, 0);
+      }
+
+   static inline float PointToAngle (float x, float y)
+      {
+      if (-0.01 < x && x < 0.01)
+         return (y > 0) ? HALFPI : (3 * HALFPI);
+
+      float angle = atan2(y, x);
+
+      if (angle < 0)
+         angle += TWOPI;
+
+      return angle;
+      }
+
+   static inline int AngleToX (float ang)
+      {
+      float t = tan (HALFPI - ang);
+
+      int x = int (view.sw * t);
+
+      x = (view.sw + x) / 2;
+
+      if (x < 0)
+         x = 0;
+      else if (x > view.sw)
+         x = view.sw;
+
+      return x;
+      }
+
+   static inline float XToAngle (int x)
+      {
+      x = x * 2 - view.sw;
+
+      float ang = HALFPI + atan (x / float (view.sw));
+
+      if (ang < 0)
+         ang = 0;
+      else if (ang > ONEPI)
+         ang = ONEPI;
+
+      return ang;
+      }
+
+   static inline int DeltaToX (double iz, float tx)
+      {
+      int x = int (view.sw * tx * iz);
+
+      x = (x + view.sw) / 2;
+
+      return x;
+      }
+
+   static inline float XToDelta (int x, double iz)
+      {
+      x = x * 2 - view.sw;
+
+      float tx = x / iz / view.sw;
+
+      return tx;
+      }
+
+   static inline int DistToY (double iz, int sec_h)
+      {
+      if (sec_h > 32770)
+         return -9999;
+
+      if (sec_h < -32770)
+         return +9999;
+
+      sec_h -= view.z;
+
+      int y = int (view.sh * sec_h * iz * Y_SLOPE);
+
+      y = (view.sh - y) / 2;
+
+      return y;
+      }
+
+   static inline float YToDist (int y, int sec_h)
+      {
+      sec_h -= view.z;
+
+      y = y * 2 - view.sh;
+
+      if (y == 0)
+         return 999999;
+
+      return view.sh * sec_h * Y_SLOPE / y;
+      }
+
+   static inline float YToSecH (int y, double iz)
+      {
+      y = y * 2 - view.sh;
+
+      return view.z - (float (y) / view.sh / iz / Y_SLOPE);
+      }
+
+   void AddLine (int linenum)
+      {
+      LineDef *ld = LineDefs + linenum;
+
+      if (! is_obj (ld->start) || ! is_obj (ld->end))
+         return;
+
+      float x1 = Vertices[ld->start].x - view.x;
+      float y1 = Vertices[ld->start].y - view.y;
+      float x2 = Vertices[ld->end].x - view.x;
+      float y2 = Vertices[ld->end].y - view.y;
+
+      float tx1 = x1 * view.Sin - y1 * view.Cos;
+      float ty1 = x1 * view.Cos + y1 * view.Sin;
+      float tx2 = x2 * view.Sin - y2 * view.Cos;
+      float ty2 = x2 * view.Cos + y2 * view.Sin;
+
+      // reject line if complete behind viewplane
+      if (ty1 <= 0 && ty2 <= 0)
+         return;
+
+      float angle1 = PointToAngle (tx1, ty1);
+      float angle2 = PointToAngle (tx2, ty2);
+      float span = angle1 - angle2;
+
+      if (span < 0)
+         span += TWOPI;
+
+      int side = 0;
+      SideDef *sd;
+
+      if (span >= ONEPI)
+         side = 1;
+
+      // ignore the line when there is no facing sidedef
+      if (! is_obj (side ? ld->sidedef2 : ld->sidedef1))
+         return;
+
+      sd = SideDefs + (side ? ld->sidedef2 : ld->sidedef1);
+
+      if (! is_obj (sd->sector))
+         return;
+
+      if (side == 1)
+         {
+         float tmp = angle1;
+         angle1 = angle2;
+         angle2 = tmp;
+         }
+
+      // clip angles to view volume
+
+      float base_ang = angle1;
+
+      float leftclip  = (3 * ONEPI / 4);
+      float rightclip = ONEPI / 4;
+
+      float tspan1 = angle1 - rightclip;
+      float tspan2 = leftclip - angle2;
+
+      if (tspan1 < 0) tspan1 += TWOPI;
+      if (tspan2 < 0) tspan2 += TWOPI;
+
+      if (tspan1 > HALFPI)
+         {
+         // Totally off the left edge?
+         if (tspan2 >= ONEPI)
+            return;
+
+         angle1 = leftclip;
+         }
+
+      if (tspan2 > HALFPI)
+         {
+         // Totally off the left edge?
+         if (tspan1 >= ONEPI)
+            return;
+
+         angle2 = rightclip;
+         }
+
+      // convert angles to on-screen X positions
+      int sx1 = AngleToX (angle1);
+      int sx2 = AngleToX (angle2) - 1;
+
+      if (sx1 > sx2)
+         return;
+
+      // compute distance from eye to wall
+      float wdx = x2 - x1;
+      float wdy = y2 - y1;
+
+      float wlen = sqrt (wdx * wdx + wdy * wdy);
+      float dist = fabs ((y1 * wdx / wlen) - (x1 * wdy / wlen));
+
+      if (dist < 0.01)
+         return;
+
+      // compute normal of wall (translated coords)
+      float normal;
+
+      if (side == 1)
+         normal = PointToAngle (ty2 - ty1, tx1 - tx2);
+      else
+         normal = PointToAngle (ty1 - ty2, tx2 - tx1);
+
+      // compute inverse distances
+      double iz1 = cos (normal - angle1) / dist / cos (HALFPI - angle1);
+      double iz2 = cos (normal - angle2) / dist / cos (HALFPI - angle2);
+
+      double diz = (iz2 - iz1) / y_max (1, sx2 - sx1);
+
+      // create drawwall structure
+
+      DrawWall *dw = new DrawWall;
+
+      dw->th = 0;
+      dw->ld = ld;
+      dw->sd = sd;
+      dw->sec = Sectors + sd->sector;
+
+      dw->side = side;
+
+      dw->base_ang = base_ang;
+      dw->ang1 = angle1;
+      dw->dang = (angle2 - angle1) / y_max (1, sx2 - sx1);
+
+      dw->dist = dist;
+      dw->normal = normal;
+      dw->t_dist = tan (base_ang - normal) * dist;
+
+      dw->iz1 = iz1;
+      dw->diz = diz;
+      dw->mid_iz = iz1 + (sx2 - sx1 + 1) * diz / 2;
+
+      dw->sx1 = sx1;  dw->sx2 = sx2;
+
+      walls.push_back (dw);
+      }
+
+   void AddThing (int thingnum)
+      {
+      Thing *th = Things + thingnum;
+
+      float x = th->xpos - view.x;
+      float y = th->ypos - view.y;
+
+      float tx = x * view.Sin - y * view.Cos;
+      float ty = x * view.Cos + y * view.Sin;
+
+      // reject sprite if complete behind viewplane
+      if (ty < 4)
+         return;
+
+      Img *sprite = view.im_ch->GetSprite (th->type);
+      if (! sprite)
+         return;
+
+      float tx1 = tx - sprite->width () / 2.0;
+      float tx2 = tx + sprite->width () / 2.0;
+
+      double iz = 1 / ty;
+
+      int sx1 = DeltaToX (iz, tx1);
+      int sx2 = DeltaToX (iz, tx2) - 1;
+
+      if (sx1 < 0)
+         sx1 = 0;
+
+      if (sx2 >= view.sw)
+         sx2 = view.sw - 1;
+
+      if (sx1 > sx2)
+         return;
+
+      int h2 = view.thing_floors[thingnum];
+      int h1 = h2 + sprite->height ();
+
+      // create drawwall structure
+
+      DrawWall *dw = new DrawWall;
+
+      dw->th = th;
+      dw->ld = 0;
+      dw->sd = 0;
+      dw->sec = 0;
+
+      dw->spr_tx1 = tx1;
+
+      dw->ang1 = dw->dang = 0;
+
+      dw->iz1 = dw->mid_iz = iz;
+      dw->diz = 0;
+
+      dw->sx1 = sx1;  dw->sx2 = sx2;
+
+      dw->ceil.img = sprite;
+      dw->ceil.h1  = h1;
+      dw->ceil.h2  = h2;
+
+      walls.push_back (dw);
+      }
+
+   void ComputeSurfaces ()
+      {
+      DrawWall::vec_t::iterator S;
+
+      for (S = walls.begin (); S != walls.end (); S++)
+         if ((*S)->ld)
+            (*S)->ComputeWallSurface ();
+      }
+
+   void ClipSolids ()
+      {
+      // perform a rough depth sort of the walls and sprites.
+
+      std::sort (walls.begin (), walls.end (), DrawWall::MidDistCmp ());
+
+      // go forwards, from closest to furthest away
+
+      DrawWall::vec_t::iterator S;
+
+      for (S = walls.begin (); S != walls.end (); S++)
+         {
+         DrawWall *dw = (*S);
+
+         if (! dw)
+            continue;
+
+         int one_sided = dw->ld && ! is_obj (dw->ld->sidedef2);
+         int vis_count = dw->sx2 - dw->sx1 + 1;
+
+         for (int x = dw->sx1; x <= dw->sx2; x++)
+            {
+            double iz = dw->iz1 + (dw->diz * (x - dw->sx1));
+
+            if (iz < depth_x[x])
+               vis_count--;
+            else if (one_sided)
+               depth_x[x] = iz;
+            }
+
+         if (vis_count == 0)
+            {
+            delete dw;
+            (*S) = 0;
+            }
+         }
+
+      // remove null pointers
+
+      S = std::remove (walls.begin (), walls.end (), (DrawWall *) 0);
+
+      walls.erase (S, walls.end ());
+      }
+
+   void RenderFlatColumn (DrawWall *dw, DrawSurf& surf,
+         int x, int y1, int y2)
+      {
+      img_pixel_t *buf = view.screen->wbuf ();
+      img_pixel_t *wbuf = surf.img->wbuf ();
+
+      int tw = surf.img->width ();
+      int th = surf.img->height ();
+
+      float ang = XToAngle (x);
+      float modv = cos (ang - HALFPI);
+
+      float t_cos = cos (ONEPI + -view.angle + ang) / modv;
+      float t_sin = sin (ONEPI + -view.angle + ang) / modv;
+
+      buf += x + y1 * view.sw;
+
+      for (; y1 <= y2; y1++, buf += view.sw)
+         {
+         float dist = YToDist (y1, surf.tex_h);
+
+         int tx = int ( view.x + t_sin * dist) & (tw - 1);
+         int ty = int (-view.y - t_cos * dist) & (th - 1);
+
+         *buf = wbuf[ty * tw + tx];
+         }
+      }
+
+   void RenderTexColumn (DrawWall *dw, DrawSurf& surf,
+         int x, int y1, int y2)
+      {
+      img_pixel_t *buf = view.screen->wbuf ();
+      img_pixel_t *wbuf = surf.img->wbuf ();
+
+      int tw = surf.img->width ();
+      int th = surf.img->height ();
+
+      /* compute texture X coord */
+
+      int tx = int (dw->t_dist - tan (dw->cur_ang - dw->normal) * dw->dist);
+
+      tx = (dw->sd->xoff + tx) & (tw - 1);
+
+      /* compute texture Y coords */
+
+      float base_h = surf.tex_h + dw->sd->yoff;
+
+      float h1 = base_h - YToSecH (y1, dw->cur_iz);
+      float dh = base_h - YToSecH (y2, dw->cur_iz);
+
+      dh = (dh - h1) / y_max (1, y2 - y1);
+       
+      buf  += x + y1 * view.sw;
+      wbuf += tx;
+
+      for (; y1 <= y2; y1++, h1 += dh, buf += view.sw)
+         {
+         int ty = int (h1) % th;
+
+         // handle negative values (use % twice)
+         ty = (ty + th) % th;
+
+         *buf = wbuf[ty * tw];
+         }
+      }
+
+   void RenderSolidColumn (DrawWall *w, DrawSurf& surf,
+         int x, int y1, int y2)
+      {
+      img_pixel_t *buf = view.screen->wbuf ();
+
+      buf += x + y1 * view.sw;
+       
+      for (; y1 <= y2; y1++, buf += view.sw)
+         {
+         *buf = surf.col;
+         }
+      }
+
+   inline void RenderWallSurface (DrawWall *dw, DrawSurf& surf, 
+         int x)
+      {
+      if (surf.kind == DrawSurf::K_INVIS)
+         return;
+
+      int y1 = DistToY (dw->cur_iz, surf.h1);
+      int y2 = DistToY (dw->cur_iz, surf.h2) - 1;
+
+      if (y1 < open_y1)
+         y1 = open_y1;
+
+      if (y2 > open_y2)
+         y2 = open_y2;
+
+      if (y1 > y2)
+         return;
+
+      /* clip the open region */
+
+      if (surf.y_clip & DrawSurf::SOLID_ABOVE)
+         if (y2 > open_y1)
+            open_y1 = y2;
+
+      if (surf.y_clip & DrawSurf::SOLID_BELOW)
+         if (y1 < open_y2)
+            open_y2 = y1;
+
+      /* fill pixels */
+
+      if (! surf.img)
+         {
+         RenderSolidColumn (dw, surf, x, y1, y2);
+         }
+      else switch (surf.kind)
+         {
+         case DrawSurf::K_FLAT:
+            RenderFlatColumn (dw, surf, x, y1, y2);
+            break;
+
+         case DrawSurf::K_TEXTURE:
+            RenderTexColumn  (dw, surf, x, y1, y2);
+            break;
+         }
+      }
+
+   inline void RenderSprite (DrawWall *dw, int x)
+      {
+      int y1 = DistToY (dw->cur_iz, dw->ceil.h1);
+      int y2 = DistToY (dw->cur_iz, dw->ceil.h2) - 1;
+
+      if (y1 < dw->oy1)
+         y1 = dw->oy1;
+
+      if (y2 > dw->oy2)
+         y2 = dw->oy2;
+
+      if (y1 > y2)
+         return;
+
+      /* fill pixels */
+
+      img_pixel_t *buf = view.screen->wbuf ();
+      img_pixel_t *wbuf = dw->ceil.img->wbuf ();
+
+      int tw = dw->ceil.img->width ();
+      int th = dw->ceil.img->height ();
+
+      int tx = int (XToDelta (x, dw->cur_iz) - dw->spr_tx1);
+
+      if (tx < 0 || tx >= tw)
+         return;
+
+      float h1 = dw->ceil.h1 - YToSecH (y1, dw->cur_iz);
+      float dh = dw->ceil.h1 - YToSecH (y2, dw->cur_iz);
+
+      dh = (dh - h1) / y_max (1, y2 - y1);
+       
+      buf  += x + y1 * view.sw;
+      wbuf += tx;
+
+      for (; y1 <= y2; y1++, h1 += dh, buf += view.sw)
+         {
+         int ty = int (h1);
+
+         if (ty < 0 || ty >= th)
+            continue;
+
+         img_pixel_t pix = wbuf[ty * tw];
+
+         if (pix != IMG_TRANSP)
+            *buf = pix;
+         }
+      }
+
+   void UpdateActiveList (int x)
+      {
+      DrawWall::vec_t::iterator S, E, P;
+
+      bool changes = false;
+
+      // remove walls that have finished.
+
+      S = active.begin ();
+      E = active.end ();
+
+      S = std::remove_if (S, E, DrawWall::SX2Less (x));
+
+      if (S != E)
+         {
+         active.erase (S, E);
+         changes = true;
+         }
+
+      // add new walls that start in this column.
+
+      S = walls.begin ();
+      E = walls.end ();
+
+      S = std::lower_bound (S, E, x, DrawWall::SX1Cmp ());
+      E = std::upper_bound (S, E, x, DrawWall::SX1Cmp ());
+
+      if (S != E)
+         changes = true;
+
+      for (; S != E; S++)
+         {
+         active.push_back (*S);
+         }
+
+      // calculate new depth values
+
+      S = active.begin ();
+      E = active.end ();
+
+      for (P=S; (P != E); P++)
+         {
+         DrawWall *dw = (*P);
+
+         dw->cur_iz = dw->iz1 + dw->diz * (x - dw->sx1);
+
+         if (P != S && (*(P-1))->cur_iz < dw->cur_iz)
+            changes = true;
+
+         dw->cur_ang = dw->ang1 + dw->dang * (x - dw->sx1);
+         }
+
+      // if there are changes, re-sort the active list...
+
+      if (changes)
+         {
+         std::sort (active.begin (), active.end (), DrawWall::DistCmp ());
+         }
+      }
+
+   void RenderWalls ()
+      {
+      // sort walls by their starting column, to allow binary search.
+
+      std::sort (walls.begin (), walls.end (), DrawWall::SX1Cmp ());
+
+      active.clear ();
+
+      for (int x=0; x < view.sw; x++)
+         {
+         // clear vertical depth buffer
+
+         open_y1 = 0;
+         open_y2 = view.sh - 1;
+
+         UpdateActiveList (x);
+
+         // render, front to back
+
+         DrawWall::vec_t::iterator S, E, P;
+
+         S = active.begin ();
+         E = active.end ();
+
+         for (P=S; P != E; P++)
+            {
+            DrawWall *dw = (*P);
+
+            // for things, just remember the open space
+            if (dw->th)
+               {
+               dw->oy1 = open_y1;
+               dw->oy2 = open_y2;
+               continue;
+               }
+
+            RenderWallSurface (dw, dw->ceil,  x);
+            RenderWallSurface (dw, dw->floor, x);
+            RenderWallSurface (dw, dw->upper, x);
+            RenderWallSurface (dw, dw->lower, x);
+
+            if (open_y1 >= open_y2)
+               break;
+            }
+
+         // now render things, back to front
+
+         if (P == E)
+            P--;
+
+         for (; P != (S-1); P--)
+            {
+            DrawWall *dw = (*P);
+
+            if (dw->th)
+               RenderSprite (dw, x);
+            }
+         }
+      }
+
+   void DoRender3D ()
+      {
+      view.ClearScreen ();
+
+      InitDepthBuf (view.sw);
+
+      for (int i=0; i < NumLineDefs; i++)
+         AddLine (i);
+
+      if (view.sprites)
+         for (int j=0; j < NumThings; j++)
+            AddThing (j);
+
+      ClipSolids ();
+      ComputeSurfaces ();
+      RenderWalls ();
+      }
+};
+
+
+static Thing *FindPlayer (int typenum)
+{
+for (int i=0; i < NumThings; i++)
+   if (Things[i].type == typenum)
+      return Things + i;
+
+return 0;
+}
+
+
+/*
+ *  Render a 3D view from the player's position. 
+ */
+
+void Render3D ()
+{
+if (! view.p_type)
+   {
+   view.p_type = THING_PLAYER1;
+   view.px = 99999;
+   }
+
+Thing *player = FindPlayer (view.p_type);
+
+if (! player)
+   {
+   if (view.p_type != THING_DEATHMATCH)
+      view.p_type = THING_DEATHMATCH;
+
+   player = FindPlayer (view.p_type);
+
+   if (! player)
+      return;
+   }
+
+if (view.px != player->xpos || view.py != player->ypos)
+   {
+   // if player moved, re-create view parameters
+
+   view.x = view.px = player->xpos;
+   view.y = view.py = player->ypos;
+
+   view.CalcViewZ ();
+   view.SetAngle (player->angle * ONEPI / 180.0);
+   }
+
+/* create image */
+
+view.sw = 320;
+view.sh = 200;
+
+view.screen = new Img ((unsigned short int) view.sw, (unsigned short int) view.sh, false);
+view.im_ch = new ImageCache;
+
+view.FindThingFloors ();
+
+bool Redraw = true;
+
+/* input loop */
+
+for (;;)
+   {
+   /* render image */
+
+   if (Redraw)
+      {
+      if (view.walking)
+         view.CalcViewZ ();
+
+      RendInfo rend;
+
+      rend.DoRender3D ();
+
+      view.PutScreen (40, 40);
+
+      Redraw = false;
+      }
+
+   /* handle keypress */
+
+   int key = get_key ();
+
+   if (key == YK_ESC || key == 'q')
+      break;
+
+   if ((key & ~YK_SHIFT) == YK_LEFT)
+      {
+      view.SetAngle (view.angle + ONEPI / ((key & YK_SHIFT) ? 4 : 8));
+      Redraw = true;
+      }
+   else if ((key & ~YK_SHIFT) == YK_RIGHT)
+      {
+      view.SetAngle (view.angle -ONEPI / ((key & YK_SHIFT) ? 4 : 8));
+      Redraw = true;
+      }
+   else if ((key & ~YK_SHIFT) == YK_UP)
+      {
+      view.x += view.Cos * ((key & YK_SHIFT) ? 192 : 32);
+      view.y += view.Sin * ((key & YK_SHIFT) ? 192 : 32);
+      Redraw = true;
+      }
+   else if ((key & ~YK_SHIFT) == YK_DOWN)
+      {
+      view.x -= view.Cos * ((key & YK_SHIFT) ? 192 : 32);
+      view.y -= view.Sin * ((key & YK_SHIFT) ? 192 : 32);
+      Redraw = true;
+      }
+   else if (key == 'n' || key == 'N')
+      {
+      view.x -= view.Sin * ((key == 'N') ? 192 : 32);
+      view.y += view.Cos * ((key == 'N') ? 192 : 32);
+      Redraw = true;
+      }
+   else if (key == 'm' || key == 'M')
+      {
+      view.x += view.Sin * ((key == 'M') ? 192 : 32);
+      view.y -= view.Cos * ((key == 'M') ? 192 : 32);
+      Redraw = true;
+      }
+   else if (key == 'd' || key == 'D')
+      {
+      view.z += (key == 'D') ? 128 : 32;
+      Redraw = true;
+      }
+   else if (key == 'c' || key == 'C')
+      {
+      view.z -= (key == 'C') ? 128 : 32;
+      Redraw = true;
+      }
+   else if (key == 't')
+      {
+      view.texturing = ! view.texturing;
+      Redraw = true;
+      }
+   else if (key == 's')
+      {
+      view.sprites = ! view.sprites;
+      Redraw = true;
+      }
+   else if (key == 'w')
+      {
+      view.walking = ! view.walking;
+      Redraw = true;
+      }
+   else if (key)
+      {
+      // key no good, get another one
+      Beep ();
+      }
+   }
+
+/* all done */
+
+delete view.screen;
+view.screen = 0;
+
+delete view.im_ch;
+view.im_ch = 0;
+
+delete[] view.thing_floors;
+view.thing_floors = 0;
+}
+
diff -u -r -N yadex-1.7.0/src/r_render.h yadex-1.7.0-all/src/r_render.h
--- yadex-1.7.0/src/r_render.h	1970-01-01 10:00:00.000000000 +1000
+++ yadex-1.7.0-all/src/r_render.h	2005-01-10 14:30:53.000000000 +1100
@@ -0,0 +1,14 @@
+/*
+ *	r_render.h
+ *	AJA 2002-04-27
+ */
+
+
+#ifndef YH_R_RENDER  /* DO NOT INSERT ANYTHING BEFORE THIS LINE */
+#define YH_R_RENDER
+
+
+void Render3D ();
+
+
+#endif  /* DO NOT ADD ANYTHING AFTER THIS LINE */
diff -u -r -N yadex-1.7.0/src/s_misc.cc yadex-1.7.0-all/src/s_misc.cc
--- yadex-1.7.0/src/s_misc.cc	2003-03-28 23:37:32.000000000 +1100
+++ yadex-1.7.0-all/src/s_misc.cc	2005-01-10 14:30:53.000000000 +1100
@@ -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 */
diff -u -r -N yadex-1.7.0/src/s_prop.cc yadex-1.7.0-all/src/s_prop.cc
--- yadex-1.7.0/src/s_prop.cc	2003-03-28 23:37:32.000000000 +1100
+++ yadex-1.7.0-all/src/s_prop.cc	2005-01-10 14:30:53.000000000 +1100
@@ -259,4 +259,67 @@
   }
 }
 
+/*
+ * 	InputSectorType
+ * 	Let the user select a sector type number and return it
+ * 	Returns 0 if OK, <>0 if cancelled
+ */
+int InputSectorType (int x0, int y0, int *number)
+{
+  int	val;
+  val = 0;
+  *number = 0;
+  Menu_data_st menudata (stdef);
+	if (DisplayMenuList (x0 , y0, "Select type", menudata, &val)
+	  < 0)
+	  return 1;
+  // KLUDGE last element of stdef means "enter value"
+  if (val == al_lcount (stdef) - 1)
+  	{
+	  val = InputIntegerValue (x0 + 84,
+	    y0 + BOX_BORDER + (3 + val) * FONTH,
+	    -32768, 32767, 0);
+	  if (val == IIV_CANCEL)  // [Esc]
+	    return 1;
+	}
+	else
+	{
+	  if (al_lseek (stdef, val, SEEK_SET))
+	    fatal_error ("%s SP1 (%s)\n",
+	      msg_unexpected, al_astrerror (al_aerrno));
+	  val = CUR_STDEF->number;
+	}
+  if (val < 0) return 1;  //unsuccessful
+  *number = val;
+  return 0;               //successful
+}
+
+/*
+ *   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;
+   }
+}
 
diff -u -r -N yadex-1.7.0/src/s_split.cc yadex-1.7.0-all/src/s_split.cc
--- yadex-1.7.0/src/s_split.cc	2003-03-28 23:37:32.000000000 +1100
+++ yadex-1.7.0-all/src/s_split.cc	2005-01-10 14:30:53.000000000 +1100
@@ -36,6 +36,7 @@
 #include "s_linedefs.h"
 #include "selectn.h"
 #include "x_hover.h"
+#include "entry.h"
 
 
 /*
diff -u -r -N yadex-1.7.0/src/t_prop.cc yadex-1.7.0-all/src/t_prop.cc
--- yadex-1.7.0/src/t_prop.cc	2003-03-28 23:37:32.000000000 +1100
+++ yadex-1.7.0-all/src/t_prop.cc	2005-01-10 14:30:53.000000000 +1100
@@ -36,16 +36,17 @@
 #include "gfx.h"
 #include "levels.h"
 #include "oldmenus.h"
+#include "objid.h"
+#include "objects.h"
 #include "selectn.h"
 #include "things.h"
 
-
 /*
  *	Private functions prototypes
  */
-static int InputThingType (int x0, int y0, int *number);
 static const char *PrintThinggroup (void *ptr);
 static const char *PrintThingdef (void *ptr);
+int InputThingType (int x0, int y0, int *number);
 
 
 /*
@@ -250,7 +251,7 @@
  *	Let the user select a thing number and return it.
  *	Returns 0 if OK, <>0 if cancelled
  */
-static int InputThingType (int x0, int y0, int *number)
+int InputThingType (int x0, int y0, int *number)
 {
 int         r;
 int         tgno = 0;
@@ -331,4 +332,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 */
diff -u -r -N yadex-1.7.0/src/yadex.h yadex-1.7.0-all/src/yadex.h
--- yadex-1.7.0/src/yadex.h	2003-12-15 11:37:45.000000000 +1100
+++ yadex-1.7.0-all/src/yadex.h	2005-01-10 14:30:53.000000000 +1100
@@ -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! */