diff -Naur yadex-1.7.0.orig/src/editloop.cc yadex-1.7.0.allpatches/src/editloop.cc
--- yadex-1.7.0.orig/src/editloop.cc 2013-01-10 17:24:11.554023988 +0100
+++ yadex-1.7.0.allpatches/src/editloop.cc 2013-01-10 17:25:53.187909413 +0100
@@ -43,6 +43,7 @@
#include "entry.h"
#include "entry2.h"
#include "events.h"
+#include "game.h"
#include "gfx.h"
#include "gfx2.h" // show_character_set() show_pcolours()
#include "gfx3.h"
@@ -94,6 +95,7 @@
/* prototypes of private functions */
static int SortLevels (const void *item1, const void *item2);
+static char *GetBehaviorFileName (const char *levelname);
/*
* SelectLevel
@@ -303,15 +305,28 @@
"~Quit", 'q', 0,
NULL);
-e.mb_menu[MBM_EDIT] = new Menu (NULL,
- "~Copy object(s)", 'o', 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,
- "~Lock grid step", 'z', MIF_VTICK, &e.grid_step_locked, 0,
- NULL);
+if (yg_level_format == YGLF_HEXEN)
+ e.mb_menu[MBM_EDIT] = new Menu (NULL,
+ "~Copy object(s)", 'o', 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,
+ "~Lock grid step", 'z', MIF_VTICK, &e.grid_step_locked, 0,
+ "Load ~BEHAVIOR lump", 'b', 0,
+ NULL);
+else
+ e.mb_menu[MBM_EDIT] = new Menu (NULL,
+ "~Copy object(s)", 'o', 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,
+ "~Lock grid step", 'z', MIF_VTICK, &e.grid_step_locked, 0,
+ NULL);
+
// If you change the order of modes here, don't forget
// to modify the <modes> array.
@@ -2415,6 +2430,30 @@
RedrawMap = 1;
}
+ // Load BEHAVIOR lump (JL)
+ else if (is.key == 'b')
+ {
+ char *acsfile;
+ const char *acsname;
+ if (levelname)
+ acsname = levelname;
+ else
+ acsname = "behavior";
+ acsfile = GetBehaviorFileName (acsname);
+ FILE* f = fopen(acsfile, "rb");
+ if (f)
+ {
+ FreeFarMemory(Behavior);
+ fseek(f, 0, SEEK_END);
+ BehaviorSize = ftell(f);
+ Behavior = (u8*)GetFarMemory(BehaviorSize);
+ fseek(f, 0, SEEK_SET);
+ fread(Behavior, BehaviorSize, 1, f);
+ fclose(f);
+ }
+ RedrawMap = 1;
+ }
+
/* user likes music */
else if (is.key)
{
@@ -2564,4 +2603,32 @@
return 0;
}
+/*
+ get the name of the BEHAVIOR lump file (returns NULL on Esc)
+*/
+
+static char *GetBehaviorFileName (const char *levelname)
+{
+#define BUFSZ 79
+ char *outfile = (char *) GetMemory (BUFSZ + 1);
+
+ /* get the file name */
+ // If no name, find a default one
+ if (! levelname)
+ {
+ levelname = "behavior";
+ }
+
+ al_scpslower (outfile, levelname, BUFSZ);
+ al_saps (outfile, ".o", BUFSZ);
+ InputFileName (-1, -1, "Name of the BEHAVIOR script file:", BUFSZ, outfile);
+ /* escape */
+ if (outfile[0] == '\0')
+ {
+ FreeMemory (outfile);
+ return 0;
+ }
+ return outfile;
+}
+
diff -Naur yadex-1.7.0.orig/src/levels.cc yadex-1.7.0.allpatches/src/levels.cc
--- yadex-1.7.0.orig/src/levels.cc 2013-01-10 17:24:11.563024067 +0100
+++ yadex-1.7.0.allpatches/src/levels.cc 2013-01-10 17:25:53.193909464 +0100
@@ -58,6 +58,8 @@
VPtr Vertices; /* vertex data */
int NumSectors; /* number of sectors */
SPtr Sectors; /* sectors data */
+u8* Behavior;
+int BehaviorSize;
// FIXME should be somewhere else
int NumWTexture; /* number of wall textures */
@@ -95,6 +97,10 @@
the Level has never been saved yet,
an empty string. */
+static u8 DefaultBehavior[16] = {
+ 'A', 'C', 'S', 0, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+};
+
void EmptyLevelData (const char *levelname)
{
Things = 0;
@@ -109,6 +115,12 @@
NumSectors = 0;
Vertices = 0;
NumVertices = 0;
+if (yg_level_format == YGLF_HEXEN)
+ {
+ BehaviorSize = sizeof(DefaultBehavior);
+ Behavior = (u8*) GetFarMemory ((unsigned long) BehaviorSize );
+ memcpy(Behavior, DefaultBehavior, BehaviorSize);
+ }
}
@@ -199,7 +211,7 @@
{
offset = dir->dir.start;
length = dir->dir.size;
- if (MainWad == Iwad4) // Hexen mode
+ if (yg_level_format == YGLF_HEXEN) // Hexen mode
{
NumThings = (int) (length / WAD_HEXEN_THING_BYTES);
if ((i32) (NumThings * WAD_HEXEN_THING_BYTES) != length)
@@ -234,18 +246,23 @@
rc = 1;
goto byebye;
}
- if (MainWad == Iwad4) // Hexen mode
+ if (yg_level_format == YGLF_HEXEN) // Hexen mode
for (long n = 0; n < NumThings; n++)
{
u8 dummy2[6];
- wf->read_i16 (); // Tid
+ wf->read_i16 (&Things[n].tid );
wf->read_i16 (&Things[n].xpos );
wf->read_i16 (&Things[n].ypos );
- wf->read_i16 (); // Height
+ wf->read_i16 (&Things[n].height);
wf->read_i16 (&Things[n].angle);
wf->read_i16 (&Things[n].type );
wf->read_i16 (&Things[n].when );
- wf->read_bytes (dummy2, sizeof dummy2);
+ wf->read_u8 (Things[n].special);
+ wf->read_u8 (Things[n].arg1 );
+ wf->read_u8 (Things[n].arg2 );
+ wf->read_u8 (Things[n].arg3 );
+ wf->read_u8 (Things[n].arg4 );
+ wf->read_u8 (Things[n].arg5 );
if (wf->error ())
{
err ("%s: error reading thing #%ld", lump_name, n);
@@ -283,7 +300,7 @@
NumLineDefs = 0;
else
{
- if (MainWad == Iwad4) // Hexen mode
+ if (yg_level_format == YGLF_HEXEN) // Hexen mode
{
NumLineDefs = (int) (dir->dir.size / WAD_HEXEN_LINEDEF_BYTES);
if ((i32) (NumLineDefs * WAD_HEXEN_LINEDEF_BYTES) != dir->dir.size)
@@ -310,7 +327,7 @@
rc = 1;
goto byebye;
}
- if (MainWad == Iwad4) // Hexen mode
+ if (yg_level_format == YGLF_HEXEN) // Hexen mode
for (long n = 0; n < NumLineDefs; n++)
{
u8 dummy[6];
@@ -322,6 +339,10 @@
wf->read_i16 (&LineDefs[n].sidedef2);
LineDefs[n].type = dummy[0];
LineDefs[n].tag = dummy[1]; // arg1 often contains a tag
+ LineDefs[n].arg2 = dummy[2];
+ LineDefs[n].arg3 = dummy[3];
+ LineDefs[n].arg4 = dummy[4];
+ LineDefs[n].arg5 = dummy[5];
if (wf->error ())
{
err ("%s: error reading linedef #%ld", lump_name, n);
@@ -907,6 +928,37 @@
}
}
+// Read BEHAVIOR
+if (yg_level_format == YGLF_HEXEN)
+{
+const char *lump_name = "BEHAVIOR";
+verbmsg (" behavior\n");
+dir = FindMasterDir (Level, lump_name);
+if (dir)
+ {
+ BehaviorSize = (int)dir->dir.size;
+ if (BehaviorSize > 0)
+ {
+ Behavior = (u8*) GetFarMemory ((unsigned long) BehaviorSize );
+ const Wad_file *wf = dir->wadfile;
+ wf->seek (dir->dir.start);
+ if (wf->error ())
+ {
+ err ("%s: seek error", lump_name);
+ rc = 1;
+ goto byebye;
+ }
+ wf->read_bytes (Behavior, BehaviorSize);
+ if (wf->error ())
+ {
+ err ("%s: error behavior lump", lump_name);
+ rc = 1;
+ goto byebye;
+ }
+ }
+ }
+}
+
/* Sanity checking on sidedefs: the sector must exist. I don't
make this a fatal error, though, because it's not exceptional
to find wads with unused sidedefs with a sector# of -1. Well
@@ -1011,16 +1063,10 @@
FILE *file;
MDirPtr dir;
int n;
-long lump_offset[WAD_LL__];
-size_t lump_size[WAD_LL__];
+long lump_offset[WAD_LL__MAX];
+size_t lump_size[WAD_LL__MAX];
wad_level_lump_no_t l;
-if (yg_level_format == YGLF_HEXEN || ! strcmp (Game, "hexen"))
- {
- Notify (-1, -1, "I refuse to save. Hexen mode is still",
- "too badly broken. You would lose data.");
- return 1;
- }
if (! level_name || ! levelname2levelno (level_name))
{
nf_bug ("SaveLevelData: bad level_name \"%s\", using \"E1M1\" instead.",
@@ -1047,9 +1093,15 @@
&& ! MadeMapChanges
&& yg_level_format != YGLF_ALPHA;
+int NumLumps;
+if (yg_level_format == YGLF_HEXEN)
+ NumLumps = WAD_LL__HEXEN;
+else
+ NumLumps = WAD_LL__DOOM;
+
// Write the pwad header
WriteBytes (file, "PWAD", 4); // Pwad file
-file_write_i32 (file, WAD_LL__); // Number of entries = 11
+file_write_i32 (file, NumLumps); // Number of entries = 11
file_write_i32 (file, 0); // Fix this up later
if (Level)
dir = Level->next;
@@ -1067,11 +1119,30 @@
ObjectsNeeded (OBJ_THINGS, 0);
for (n = 0; n < NumThings; n++)
{
- file_write_i16 (file, Things[n].xpos );
- file_write_i16 (file, Things[n].ypos );
- file_write_i16 (file, Things[n].angle);
- file_write_i16 (file, Things[n].type );
- file_write_i16 (file, Things[n].when );
+ if (yg_level_format == YGLF_HEXEN)
+ {
+ file_write_i16 (file, Things[n].tid );
+ file_write_i16 (file, Things[n].xpos );
+ file_write_i16 (file, Things[n].ypos );
+ file_write_i16 (file, Things[n].height);
+ file_write_i16 (file, Things[n].angle);
+ file_write_i16 (file, Things[n].type );
+ file_write_i16 (file, Things[n].when );
+ WriteBytes (file, &Things[n].special, 1);
+ WriteBytes (file, &Things[n].arg1, 1 );
+ WriteBytes (file, &Things[n].arg2, 1 );
+ WriteBytes (file, &Things[n].arg3, 1 );
+ WriteBytes (file, &Things[n].arg4, 1 );
+ WriteBytes (file, &Things[n].arg5, 1 );
+ }
+ else
+ {
+ file_write_i16 (file, Things[n].xpos );
+ file_write_i16 (file, Things[n].ypos );
+ file_write_i16 (file, Things[n].angle);
+ file_write_i16 (file, Things[n].type );
+ file_write_i16 (file, Things[n].when );
+ }
}
lump_size[l] = ftell (file) - lump_offset[l];
if (Level)
@@ -1083,13 +1154,32 @@
ObjectsNeeded (OBJ_LINEDEFS, 0);
for (n = 0; n < NumLineDefs; n++)
{
- file_write_i16 (file, LineDefs[n].start );
- file_write_i16 (file, LineDefs[n].end );
- file_write_i16 (file, LineDefs[n].flags );
- file_write_i16 (file, LineDefs[n].type );
- file_write_i16 (file, LineDefs[n].tag );
- file_write_i16 (file, LineDefs[n].sidedef1);
- file_write_i16 (file, LineDefs[n].sidedef2);
+ if (yg_level_format == YGLF_HEXEN)
+ {
+ u8 dummy[6];
+ dummy[0] = LineDefs[n].type;
+ dummy[1] = LineDefs[n].tag;
+ dummy[2] = LineDefs[n].arg2;
+ dummy[3] = LineDefs[n].arg3;
+ dummy[4] = LineDefs[n].arg4;
+ dummy[5] = LineDefs[n].arg5;
+ file_write_i16 (file, LineDefs[n].start );
+ file_write_i16 (file, LineDefs[n].end );
+ file_write_i16 (file, LineDefs[n].flags );
+ WriteBytes (file, dummy, 6);
+ file_write_i16 (file, LineDefs[n].sidedef1);
+ file_write_i16 (file, LineDefs[n].sidedef2);
+ }
+ else
+ {
+ file_write_i16 (file, LineDefs[n].start );
+ file_write_i16 (file, LineDefs[n].end );
+ file_write_i16 (file, LineDefs[n].flags );
+ file_write_i16 (file, LineDefs[n].type );
+ file_write_i16 (file, LineDefs[n].tag );
+ file_write_i16 (file, LineDefs[n].sidedef1);
+ file_write_i16 (file, LineDefs[n].sidedef2);
+ }
}
lump_size[l] = ftell (file) - lump_offset[l];
if (Level)
@@ -1221,9 +1311,21 @@
if (Level)
dir = dir->next;
+
+// Write the BEHAVIOR lump
+if (yg_level_format == YGLF_HEXEN)
+{
+ l = WAD_LL_BEHAVIOR;
+ lump_offset[l] = ftell (file);
+ WriteBytes(file, Behavior, BehaviorSize);
+ lump_size[l] = BehaviorSize;
+ if (Level)
+ dir = dir->next;
+}
+
// Write the actual directory
long dir_offset = ftell (file);
-for (int L = 0; L < (int) WAD_LL__; L++)
+for (int L = 0; L < (int) NumLumps; L++)
{
file_write_i32 (file, lump_offset[L]);
file_write_i32 (file, lump_size[L]);
diff -Naur yadex-1.7.0.orig/src/levels.cc.orig yadex-1.7.0.allpatches/src/levels.cc.orig
--- yadex-1.7.0.orig/src/levels.cc.orig 1970-01-01 01:00:00.000000000 +0100
+++ yadex-1.7.0.allpatches/src/levels.cc.orig 2013-01-10 17:25:53.195909482 +0100
@@ -0,0 +1,1811 @@
+/*
+ * levels.cc
+ * Level loading and saving routines,
+ * global variables used to hold the level data.
+ * BW & RQ sometime in 1993 or 1994.
+ */
+
+
+/*
+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-2003 Andrť Majorel and others.
+
+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 "bitvec.h"
+#include "dialog.h"
+#include "game.h"
+#include "levels.h"
+#include "objid.h"
+#include "wstructs.h"
+#include "things.h"
+#include "wadfile.h"
+#include "wads.h"
+#include "wads2.h"
+
+
+/*
+ FIXME
+ All these variables should be turned
+ into members of a "Level" class.
+*/
+MDirPtr Level; /* master dictionary entry for the level */
+int NumThings; /* number of things */
+TPtr Things; /* things data */
+int NumLineDefs; /* number of line defs */
+LDPtr LineDefs; /* line defs data */
+int NumSideDefs; /* number of side defs */
+SDPtr SideDefs; /* side defs data */
+int NumVertices; /* number of vertexes */
+VPtr Vertices; /* vertex data */
+int NumSectors; /* number of sectors */
+SPtr Sectors; /* sectors data */
+u8* Behavior;
+int BehaviorSize;
+
+// FIXME should be somewhere else
+int NumWTexture; /* number of wall textures */
+char **WTexture; /* array of wall texture names */
+
+// FIXME all the flat list stuff should be put in a separate class
+size_t NumFTexture; /* number of floor/ceiling textures */
+flat_list_entry_t *flat_list; // List of all flats in the directory
+
+int MapMaxX = -32767; /* maximum X value of map */
+int MapMaxY = -32767; /* maximum Y value of map */
+int MapMinX = 32767; /* minimum X value of map */
+int MapMinY = 32767; /* minimum Y value of map */
+bool MadeChanges; /* made changes? */
+bool MadeMapChanges; /* made changes that need rebuilding? */
+unsigned long things_angles; // See levels.h for description.
+unsigned long things_types; // See levels.h for description.
+char Level_name[WAD_NAME + 1]; /* The name of the level (E.G.
+ "MAP01" or "E1M1"), followed by a
+ NUL. If the Level has been created as
+ the result of a "c" command with no
+ argument, an empty string. The name
+ is not necesarily in upper case but
+ it always a valid lump name, not a
+ command line shortcut like "17". */
+
+y_file_name_t Level_file_name; /* The name of the file in which
+ the level would be saved. If the
+ level has been created as the result
+ of a "c" command, with or without
+ argument, an empty string. */
+
+y_file_name_t Level_file_name_saved; /* The name of the file in
+ which the level was last saved. If
+ the Level has never been saved yet,
+ an empty string. */
+
+static u8 DefaultBehavior[16] = {
+ 'A', 'C', 'S', 0, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+};
+
+void EmptyLevelData (const char *levelname)
+{
+Things = 0;
+NumThings = 0;
+things_angles++;
+things_types++;
+LineDefs = 0;
+NumLineDefs = 0;
+SideDefs = 0;
+NumSideDefs = 0;
+Sectors = 0;
+NumSectors = 0;
+Vertices = 0;
+NumVertices = 0;
+if (yg_level_format == YGLF_HEXEN)
+ {
+ BehaviorSize = sizeof(DefaultBehavior);
+ Behavior = (u8*) GetFarMemory ((unsigned long) BehaviorSize );
+ memcpy(Behavior, DefaultBehavior, BehaviorSize);
+ }
+}
+
+
+/*
+ * texno_texname
+ * A convenience function when loading Doom alpha levels
+ */
+static char *tex_list = 0;
+static size_t ntex = 0;
+static char tex_name[WAD_TEX_NAME + 1];
+inline const char *texno_texname (i16 texno)
+{
+if (texno < 0)
+ return "-";
+else
+ if (yg_texture_format == YGTF_NAMELESS)
+ {
+ sprintf (tex_name, "TEX%04u", (unsigned) texno);
+ return tex_name;
+ }
+ else
+ {
+ if (texno < (i16) ntex)
+ return tex_list + WAD_TEX_NAME * texno;
+ else
+ return "unknown";
+ }
+}
+
+
+/*
+ read in the level data
+*/
+
+int ReadLevelData (const char *levelname) /* SWAP! */
+{
+int rc = 0;
+MDirPtr dir;
+int OldNumVertices;
+
+/* No objects are needed: they may be swapped after they have been read */
+ObjectsNeeded (0);
+
+/* Find the various level information from the master directory */
+DisplayMessage (-1, -1, "Reading data for level %s...", levelname);
+Level = FindMasterDir (MasterDir, levelname);
+if (!Level)
+ fatal_error ("level data not found");
+
+/* Get the number of vertices */
+i32 v_offset = 42;
+i32 v_length = 42;
+{
+const char *lump_name = "BUG";
+if (yg_level_format == YGLF_ALPHA) // Doom alpha
+ lump_name = "POINTS";
+else
+ lump_name = "VERTEXES";
+dir = FindMasterDir (Level, lump_name);
+if (dir == 0)
+ OldNumVertices = 0;
+else
+ {
+ v_offset = dir->dir.start;
+ v_length = dir->dir.size;
+ if (yg_level_format == YGLF_ALPHA) // Doom alpha: skip leading count
+ {
+ v_offset += 4;
+ v_length -= 4;
+ }
+ OldNumVertices = (int) (v_length / WAD_VERTEX_BYTES);
+ if ((i32) (OldNumVertices * WAD_VERTEX_BYTES) != v_length)
+ warn ("the %s lump has a weird size."
+ " The wad might be corrupt.\n", lump_name);
+ }
+}
+
+// Read THINGS
+{
+const char *lump_name = "THINGS";
+verbmsg ("Reading %s things", levelname);
+i32 offset = 42;
+i32 length;
+dir = FindMasterDir (Level, lump_name);
+if (dir == 0)
+ NumThings = 0;
+else
+ {
+ offset = dir->dir.start;
+ length = dir->dir.size;
+ if (yg_level_format == YGLF_HEXEN) // Hexen mode
+ {
+ NumThings = (int) (length / WAD_HEXEN_THING_BYTES);
+ if ((i32) (NumThings * WAD_HEXEN_THING_BYTES) != length)
+ warn ("the %s lump has a weird size."
+ " The wad might be corrupt.\n", lump_name);
+ }
+ else // Doom/Heretic/Strife mode
+ {
+ if (yg_level_format == YGLF_ALPHA) // Doom alpha: skip leading count
+ {
+ offset += 4;
+ length -= 4;
+ }
+ size_t thing_size = yg_level_format == YGLF_ALPHA ? 12 : WAD_THING_BYTES;
+ NumThings = (int) (length / thing_size);
+ if ((i32) (NumThings * thing_size) != length)
+ warn ("the %s lump has a weird size."
+ " The wad might be corrupt.\n", lump_name);
+ }
+ }
+things_angles++;
+things_types++;
+if (NumThings > 0)
+ {
+ Things = (TPtr) GetFarMemory ((unsigned long) NumThings
+ * sizeof (struct Thing));
+ const Wad_file *wf = dir->wadfile;
+ wf->seek (offset);
+ if (wf->error ())
+ {
+ err ("%s: seek error", lump_name);
+ rc = 1;
+ goto byebye;
+ }
+ if (yg_level_format == YGLF_HEXEN) // Hexen mode
+ for (long n = 0; n < NumThings; n++)
+ {
+ u8 dummy2[6];
+ wf->read_i16 (&Things[n].tid );
+ wf->read_i16 (&Things[n].xpos );
+ wf->read_i16 (&Things[n].ypos );
+ wf->read_i16 (&Things[n].height);
+ wf->read_i16 (&Things[n].angle);
+ wf->read_i16 (&Things[n].type );
+ wf->read_i16 (&Things[n].when );
+ wf->read_u8 (Things[n].special);
+ wf->read_u8 (Things[n].arg1 );
+ wf->read_u8 (Things[n].arg2 );
+ wf->read_u8 (Things[n].arg3 );
+ wf->read_u8 (Things[n].arg4 );
+ wf->read_u8 (Things[n].arg5 );
+ if (wf->error ())
+ {
+ err ("%s: error reading thing #%ld", lump_name, n);
+ rc = 1;
+ goto byebye;
+ }
+ }
+ else // Doom/Heretic/Strife mode
+ for (long n = 0; n < NumThings; n++)
+ {
+ wf->read_i16 (&Things[n].xpos );
+ wf->read_i16 (&Things[n].ypos );
+ wf->read_i16 (&Things[n].angle);
+ wf->read_i16 (&Things[n].type );
+ if (yg_level_format == YGLF_ALPHA)
+ wf->read_i16 (); // Alpha. Don't know what it's for.
+ wf->read_i16 (&Things[n].when );
+ if (wf->error ())
+ {
+ err ("%s: error reading thing #%ld", lump_name, n);
+ rc = 1;
+ goto byebye;
+ }
+ }
+ }
+}
+
+// Read LINEDEFS
+if (yg_level_format != YGLF_ALPHA)
+ {
+ const char *lump_name = "LINEDEFS";
+ verbmsg (" linedefs");
+ dir = FindMasterDir (Level, lump_name);
+ if (dir == 0)
+ NumLineDefs = 0;
+ else
+ {
+ if (yg_level_format == YGLF_HEXEN) // Hexen mode
+ {
+ NumLineDefs = (int) (dir->dir.size / WAD_HEXEN_LINEDEF_BYTES);
+ if ((i32) (NumLineDefs * WAD_HEXEN_LINEDEF_BYTES) != dir->dir.size)
+ warn ("the %s lump has a weird size."
+ " The wad might be corrupt.\n", lump_name);
+ }
+ else // Doom/Heretic/Strife mode
+ {
+ NumLineDefs = (int) (dir->dir.size / WAD_LINEDEF_BYTES);
+ if ((i32) (NumLineDefs * WAD_LINEDEF_BYTES) != dir->dir.size)
+ warn ("the %s lump has a weird size."
+ " The wad might be corrupt.\n", lump_name);
+ }
+ }
+ if (NumLineDefs > 0)
+ {
+ LineDefs = (LDPtr) GetFarMemory ((unsigned long) NumLineDefs
+ * sizeof (struct LineDef));
+ const Wad_file *wf = dir->wadfile;
+ wf->seek (dir->dir.start);
+ if (wf->error ())
+ {
+ err ("%s: seek error", lump_name);
+ rc = 1;
+ goto byebye;
+ }
+ if (yg_level_format == YGLF_HEXEN) // Hexen mode
+ for (long n = 0; n < NumLineDefs; n++)
+ {
+ u8 dummy[6];
+ wf->read_i16 (&LineDefs[n].start);
+ wf->read_i16 (&LineDefs[n].end);
+ wf->read_i16 (&LineDefs[n].flags);
+ wf->read_bytes (dummy, sizeof dummy);
+ wf->read_i16 (&LineDefs[n].sidedef1);
+ wf->read_i16 (&LineDefs[n].sidedef2);
+ LineDefs[n].type = dummy[0];
+ LineDefs[n].tag = dummy[1]; // arg1 often contains a tag
+ LineDefs[n].arg2 = dummy[2];
+ LineDefs[n].arg3 = dummy[3];
+ LineDefs[n].arg4 = dummy[4];
+ LineDefs[n].arg5 = dummy[5];
+ if (wf->error ())
+ {
+ err ("%s: error reading linedef #%ld", lump_name, n);
+ rc = 1;
+ goto byebye;
+ }
+ }
+ else // Doom/Heretic/Strife mode
+ for (long n = 0; n < NumLineDefs; n++)
+ {
+ wf->read_i16 (&LineDefs[n].start);
+ wf->read_i16 (&LineDefs[n].end);
+ wf->read_i16 (&LineDefs[n].flags);
+ wf->read_i16 (&LineDefs[n].type);
+ wf->read_i16 (&LineDefs[n].tag);
+ wf->read_i16 (&LineDefs[n].sidedef1);
+ wf->read_i16 (&LineDefs[n].sidedef2);
+ if (wf->error ())
+ {
+ err ("%s: error reading linedef #%ld", lump_name, n);
+ rc = 1;
+ goto byebye;
+ }
+ }
+ }
+ }
+
+// Read SIDEDEFS
+{
+const char *lump_name = "SIDEDEFS";
+verbmsg (" sidedefs");
+dir = FindMasterDir (Level, lump_name);
+if (dir)
+ {
+ NumSideDefs = (int) (dir->dir.size / WAD_SIDEDEF_BYTES);
+ if ((i32) (NumSideDefs * WAD_SIDEDEF_BYTES) != dir->dir.size)
+ warn ("the SIDEDEFS lump has a weird size."
+ " The wad might be corrupt.\n");
+ }
+else
+ NumSideDefs = 0;
+if (NumSideDefs > 0)
+ {
+ SideDefs = (SDPtr) GetFarMemory ((unsigned long) NumSideDefs
+ * sizeof (struct SideDef));
+ const Wad_file *wf = dir->wadfile;
+ wf->seek (dir->dir.start);
+ if (wf->error ())
+ {
+ err ("%s: seek error", lump_name);
+ rc = 1;
+ goto byebye;
+ }
+ for (long n = 0; n < NumSideDefs; n++)
+ {
+ wf->read_i16 (&SideDefs[n].xoff);
+ wf->read_i16 (&SideDefs[n].yoff);
+ wf->read_bytes (&SideDefs[n].tex1, WAD_TEX_NAME);
+ wf->read_bytes (&SideDefs[n].tex2, WAD_TEX_NAME);
+ wf->read_bytes (&SideDefs[n].tex3, WAD_TEX_NAME);
+ wf->read_i16 (&SideDefs[n].sector);
+ if (wf->error ())
+ {
+ err ("%s: error reading sidedef #%ld", lump_name, n);
+ rc = 1;
+ goto byebye;
+ }
+ }
+ }
+}
+
+/* Sanity checkings on linedefs: the 1st and 2nd vertices
+ must exist. The 1st and 2nd sidedefs must exist or be
+ set to -1. */
+for (long n = 0; n < NumLineDefs; n++)
+ {
+ if (LineDefs[n].sidedef1 != -1
+ && outside (LineDefs[n].sidedef1, 0, NumSideDefs - 1))
+ {
+ err ("linedef %ld has bad 1st sidedef number %d, giving up",
+ n, LineDefs[n].sidedef1);
+ rc = 1;
+ goto byebye;
+ }
+ if (LineDefs[n].sidedef2 != -1
+ && outside (LineDefs[n].sidedef2, 0, NumSideDefs - 1))
+ {
+ err ("linedef %ld has bad 2nd sidedef number %d, giving up",
+ n, LineDefs[n].sidedef2);
+ rc = 1;
+ goto byebye;
+ }
+ if (outside (LineDefs[n].start, 0, OldNumVertices -1))
+ {
+ err ("linedef %ld has bad 1st vertex number %d, giving up",
+ n, LineDefs[n].start);
+ rc = 1;
+ goto byebye;
+ }
+ if (outside (LineDefs[n].end, 0, OldNumVertices - 1))
+ {
+ err ("linedef %ld has bad 2nd vertex number %d, giving up",
+ n, LineDefs[n].end);
+ rc = 1;
+ goto byebye;
+ }
+ }
+
+// Read LINES (Doom alpha only)
+if (yg_level_format == YGLF_ALPHA)
+ {
+ const char *lump_name = "LINES";
+ verbmsg (" lines");
+ dir = FindMasterDir (Level, lump_name);
+ if (dir)
+ {
+ if ((dir->dir.size - 4) % 36)
+ warn ("the %s lump has a weird size. The wad might be corrupt.\n",
+ lump_name);
+ const size_t nlines = dir->dir.size / 36;
+ NumLineDefs = nlines;
+ NumSideDefs = 2 * nlines; // Worst case. We'll adjust later.
+ LineDefs = (LDPtr) GetFarMemory ((unsigned long) NumLineDefs
+ * sizeof (struct LineDef));
+ SideDefs = (SDPtr) GetFarMemory ((unsigned long) NumSideDefs
+ * sizeof (struct SideDef));
+ // Read TEXTURES
+ if (yg_texture_format != YGTF_NAMELESS)
+ {
+ const char *lump_name = "TEXTURES";
+ bool success = false;
+ ntex = 0;
+ i32 *offset_table = 0;
+ MDirPtr d = FindMasterDir (MasterDir, lump_name);
+ if (! d)
+ {
+ warn ("%s: lump not found in directory\n", lump_name);
+ goto textures_done;
+ }
+ {
+ const Wad_file *wf = d->wadfile;
+ wf->seek (d->dir.start);
+ if (wf->error ())
+ {
+ warn ("%s: seek error\n", lump_name);
+ goto textures_done;
+ }
+ i32 num;
+ wf->read_i32 (&num);
+ if (wf->error ())
+ {
+ warn ("%s: error reading texture count\n", lump_name);
+ }
+ if (num < 0 || num > 32767)
+ {
+ warn ("%s: bad texture count, giving up\n", lump_name);
+ goto textures_done;
+ }
+ ntex = num;
+ offset_table = new i32[ntex];
+ for (size_t n = 0; n < ntex; n++)
+ {
+ wf->read_i32 (offset_table + n);
+ if (wf->error ())
+ {
+ warn ("%s: error reading offsets table\n");
+ goto textures_done;
+ }
+ }
+ tex_list = (char *) GetMemory (ntex * WAD_TEX_NAME);
+ for (size_t n = 0; n < ntex; n++)
+ {
+ const long offset = d->dir.start + offset_table[n];
+ wf->seek (offset);
+ if (wf->error ())
+ {
+ warn ("%s: seek error\n", lump_name);
+ goto textures_done;
+ }
+ wf->read_bytes (tex_list + WAD_TEX_NAME * n, WAD_TEX_NAME);
+ if (wf->error ())
+ {
+ warn ("%s: error reading texture names\n", lump_name);
+ goto textures_done;
+ }
+ }
+ success = true;
+ }
+
+ textures_done:
+ if (offset_table != 0)
+ delete[] offset_table;
+ if (! success)
+ warn ("%s: errors found, won't be able to import texture names\n",
+ lump_name);
+ }
+
+ const Wad_file *wf = dir->wadfile;
+ wf->seek (dir->dir.start + 4);
+ if (wf->error ())
+ {
+ err ("%s: seek error", lump_name);
+ rc = 1;
+ goto byebye;
+ }
+ size_t s = 0;
+ for (size_t n = 0; n < nlines; n++)
+ {
+ LDPtr ld = LineDefs + n;
+ ld->start = wf->read_i16 ();
+ ld->end = wf->read_i16 ();
+ ld->flags = wf->read_i16 ();
+ wf->read_i16 (); // Unused ?
+ ld->type = wf->read_i16 ();
+ ld->tag = wf->read_i16 ();
+ wf->read_i16 (); // Unused ?
+ i16 sector1 = wf->read_i16 ();
+ i16 xofs1 = wf->read_i16 ();
+ i16 tex1m = wf->read_i16 ();
+ i16 tex1u = wf->read_i16 ();
+ i16 tex1l = wf->read_i16 ();
+ wf->read_i16 (); // Unused ?
+ i16 sector2 = wf->read_i16 ();
+ i16 xofs2 = wf->read_i16 ();
+ i16 tex2m = wf->read_i16 ();
+ i16 tex2u = wf->read_i16 ();
+ i16 tex2l = wf->read_i16 ();
+ if (sector1 >= 0) // Create first sidedef
+ {
+ ld->sidedef1 = s;
+ SDPtr sd = SideDefs + s;
+ sd->xoff = xofs1;
+ sd->yoff = 0;
+ memcpy (sd->tex1, texno_texname (tex1u), sizeof sd->tex1);
+ memcpy (sd->tex2, texno_texname (tex1l), sizeof sd->tex2);
+ memcpy (sd->tex3, texno_texname (tex1m), sizeof sd->tex3);
+ sd->sector = sector1;
+ s++;
+ }
+ else // No first sidedef !
+ ld->sidedef1 = -1;
+ if (ld->flags & 0x04) // Create second sidedef
+ {
+ ld->sidedef2 = s;
+ SDPtr sd = SideDefs + s;
+ sd->xoff = xofs2;
+ sd->yoff = 0;
+ memcpy (sd->tex1, texno_texname (tex2u), sizeof sd->tex1);
+ memcpy (sd->tex2, texno_texname (tex2l), sizeof sd->tex2);
+ memcpy (sd->tex3, texno_texname (tex2m), sizeof sd->tex3);
+ sd->sector = sector2;
+ s++;
+ }
+ else
+ ld->sidedef2 = -1;
+ if (wf->error ())
+ {
+ err ("%s: error reading line #%d", lump_name, int (n));
+ rc = 1;
+ goto byebye;
+ }
+ }
+ // (size_t) to silence GCC warning
+ if ((size_t) NumSideDefs > s) // Almost always true.
+ {
+ NumSideDefs = s;
+ SideDefs = (SDPtr) ResizeFarMemory (SideDefs,
+ (unsigned long) NumSideDefs * sizeof (struct SideDef));
+ }
+ if (tex_list)
+ FreeMemory (tex_list);
+ tex_list = 0;
+ ntex = 0;
+ }
+ }
+
+/* Read the vertices. If the wad has been run through a nodes
+ builder, there is a bunch of vertices at the end that are not
+ used by any linedefs. Those vertices have been created by the
+ nodes builder for the segs. We ignore them, because they're
+ useless to the level designer. However, we do NOT ignore
+ unused vertices in the middle because that's where the
+ "string art" bug came from.
+
+ Note that there is absolutely no guarantee that the nodes
+ builders add their own vertices at the end, instead of at the
+ beginning or right in the middle, AFAIK. It's just that they
+ all seem to do that (1). What if some don't ? Well, we would
+ end up with many unwanted vertices in the level data. Nothing
+ that a good CheckCrossReferences() couldn't take care of. */
+{
+verbmsg (" vertices");
+int last_used_vertex = -1;
+for (long n = 0; n < NumLineDefs; n++)
+ {
+ last_used_vertex = y_max (last_used_vertex, LineDefs[n].start);
+ last_used_vertex = y_max (last_used_vertex, LineDefs[n].end);
+ }
+NumVertices = last_used_vertex + 1;
+// This block is only here to warn me if (1) is false.
+{
+ bitvec_c vertex_used (OldNumVertices);
+ for (long n = 0; n < NumLineDefs; n++)
+ {
+ vertex_used.set (LineDefs[n].start);
+ vertex_used.set (LineDefs[n].end);
+ }
+ int unused = 0;
+ for (long n = 0; n <= last_used_vertex; n++)
+ {
+ if (! vertex_used.get (n))
+ unused++;
+ }
+ if (unused > 0)
+ {
+ warn ("this level has unused vertices in the middle.\n");
+ warn ("total %d, tail %d (%d%%), unused %d (",
+ OldNumVertices,
+ OldNumVertices - NumVertices,
+ NumVertices - unused
+ ? 100 * (OldNumVertices - NumVertices) / (NumVertices - unused)
+ : 0,
+ unused);
+ int first = 1;
+ for (int n = 0; n <= last_used_vertex; n++)
+ {
+ if (! vertex_used.get (n))
+ {
+ if (n == 0 || vertex_used.get (n - 1))
+ {
+ if (first)
+ first = 0;
+ else
+ warn (", ");
+ warn ("%d", n);
+ }
+ else if (n == last_used_vertex || vertex_used.get (n + 1))
+ warn ("-%d", n);
+ }
+ }
+ warn (")\n");
+ }
+}
+// Now load all the vertices except the unused ones at the end.
+if (NumVertices > 0)
+ {
+ const char *lump_name = "BUG";
+ Vertices = (VPtr) GetFarMemory ((unsigned long) NumVertices
+ * sizeof (struct Vertex));
+ if (yg_level_format == YGLF_ALPHA) // Doom alpha
+ lump_name = "POINTS";
+ else
+ lump_name = "VERTEXES";
+ dir = FindMasterDir (Level, lump_name);
+ if (dir == 0)
+ goto vertexes_done; // FIXME isn't that fatal ?
+ {
+ const Wad_file *wf = dir->wadfile;
+ wf->seek (v_offset);
+ if (wf->error ())
+ {
+ err ("%s: seek error", lump_name);
+ rc = 1;
+ goto byebye;
+ }
+ MapMaxX = -32767;
+ MapMaxY = -32767;
+ MapMinX = 32767;
+ MapMinY = 32767;
+ for (long n = 0; n < NumVertices; n++)
+ {
+ i16 val;
+ wf->read_i16 (&val);
+ if (val < MapMinX)
+ MapMinX = val;
+ if (val > MapMaxX)
+ MapMaxX = val;
+ Vertices[n].x = val;
+ wf->read_i16 (&val);
+ if (val < MapMinY)
+ MapMinY = val;
+ if (val > MapMaxY)
+ MapMaxY = val;
+ Vertices[n].y = val;
+ if (wf->error ())
+ {
+ err ("%s: error reading vertex #%ld", lump_name, n);
+ rc = 1;
+ goto byebye;
+ }
+ }
+ }
+ vertexes_done:
+ ;
+ }
+}
+
+// Ignore SEGS, SSECTORS and NODES
+
+// Read SECTORS
+{
+const char *lump_name = "SECTORS";
+verbmsg (" sectors\n");
+dir = FindMasterDir (Level, lump_name);
+if (yg_level_format != YGLF_ALPHA)
+ {
+ if (dir)
+ {
+ NumSectors = (int) (dir->dir.size / WAD_SECTOR_BYTES);
+ if ((i32) (NumSectors * WAD_SECTOR_BYTES) != dir->dir.size)
+ warn ("the %s lump has a weird size."
+ " The wad might be corrupt.\n", lump_name);
+ }
+ else
+ NumSectors = 0;
+ if (NumSectors > 0)
+ {
+ Sectors = (SPtr) GetFarMemory ((unsigned long) NumSectors
+ * sizeof (struct Sector));
+ const Wad_file *wf = dir->wadfile;
+ wf->seek (dir->dir.start);
+ if (wf->error ())
+ {
+ err ("%s: seek error", lump_name);
+ rc = 1;
+ goto byebye;
+ }
+ for (long n = 0; n < NumSectors; n++)
+ {
+ wf->read_i16 (&Sectors[n].floorh);
+ wf->read_i16 (&Sectors[n].ceilh);
+ wf->read_bytes (&Sectors[n].floort, WAD_FLAT_NAME);
+ wf->read_bytes (&Sectors[n].ceilt, WAD_FLAT_NAME);
+ wf->read_i16 (&Sectors[n].light);
+ wf->read_i16 (&Sectors[n].special);
+ wf->read_i16 (&Sectors[n].tag);
+ if (wf->error ())
+ {
+ err ("%s: error reading sector #%ld", lump_name, n);
+ rc = 1;
+ goto byebye;
+ }
+ }
+ }
+ }
+else // Doom alpha--a wholly different SECTORS format
+ {
+ i32 *offset_table = 0;
+ i32 nsectors = 0;
+ i32 nflatnames = 0;
+ char *flatnames = 0;
+ if (dir == 0)
+ {
+ warn ("%s: lump not found in directory\n", lump_name); // FIXME fatal ?
+ goto sectors_alpha_done;
+ }
+ {
+ const Wad_file *wf = dir->wadfile;
+ wf->seek (dir->dir.start);
+ if (wf->error ())
+ {
+ err ("%s: seek error", lump_name);
+ rc = 1;
+ goto byebye;
+ }
+ wf->read_i32 (&nsectors);
+ if (wf->error ())
+ {
+ err ("%s: error reading sector count", lump_name);
+ rc = 1;
+ goto byebye;
+ }
+ if (nsectors < 0)
+ {
+ warn ("Negative sector count. Clamping to 0.\n");
+ nsectors = 0;
+ }
+ NumSectors = nsectors;
+ Sectors = (SPtr) GetFarMemory ((unsigned long) NumSectors
+ * sizeof (struct Sector));
+ offset_table = new i32[nsectors];
+ for (size_t n = 0; n < (size_t) nsectors; n++)
+ wf->read_i32 (offset_table + n);
+ if (wf->error ())
+ {
+ err ("%s: error reading offsets table", lump_name);
+ rc = 1;
+ goto sectors_alpha_done;
+ }
+ // Load FLATNAME
+ {
+ const char *lump_name = "FLATNAME";
+ bool success = false;
+ MDirPtr dir2 = FindMasterDir (Level, lump_name);
+ if (dir2 == 0)
+ {
+ warn ("%s: lump not found in directory\n", lump_name);
+ goto flatname_done; // FIXME warn ?
+ }
+ {
+ const Wad_file *wf = dir2->wadfile;
+ wf->seek (dir2->dir.start);
+ if (wf->error ())
+ {
+ warn ("%s: seek error\n", lump_name);
+ goto flatname_done;
+ }
+ wf->read_i32 (&nflatnames);
+ if (wf->error ())
+ {
+ warn ("%s: error reading flat name count\n", lump_name);
+ nflatnames = 0;
+ goto flatname_done;
+ }
+ if (nflatnames < 0 || nflatnames > 32767)
+ {
+ warn ("%s: bad flat name count, giving up\n", lump_name);
+ nflatnames = 0;
+ goto flatname_done;
+ }
+ else
+ {
+ flatnames = new char[WAD_FLAT_NAME * nflatnames];
+ wf->read_bytes (flatnames, WAD_FLAT_NAME * nflatnames);
+ if (wf->error ())
+ {
+ warn ("%s: error reading flat names\n", lump_name);
+ nflatnames = 0;
+ goto flatname_done;
+ }
+ success = true;
+ }
+ }
+ flatname_done:
+ if (! success)
+ warn ("%s: errors found, you'll have to do without flat names\n",
+ lump_name);
+ }
+ for (size_t n = 0; n < (size_t) nsectors; n++)
+ {
+ wf->seek (dir->dir.start + offset_table[n]);
+ if (wf->error ())
+ {
+ err ("%s: seek error", lump_name);
+ rc = 1;
+ goto sectors_alpha_done;
+ }
+ i16 index;
+ wf->read_i16 (&Sectors[n].floorh);
+ wf->read_i16 (&Sectors[n].ceilh );
+ wf->read_i16 (&index);
+ if (nflatnames && flatnames && index >= 0 && index < nflatnames)
+ memcpy (Sectors[n].floort, flatnames + WAD_FLAT_NAME * index,
+ WAD_FLAT_NAME);
+ else
+ strcpy (Sectors[n].floort, "unknown");
+ wf->read_i16 (&index);
+ if (nflatnames && flatnames && index >= 0 && index < nflatnames)
+ memcpy (Sectors[n].ceilt, flatnames + WAD_FLAT_NAME * index,
+ WAD_FLAT_NAME);
+ else
+ strcpy (Sectors[n].ceilt, "unknown");
+ wf->read_i16 (&Sectors[n].light);
+ wf->read_i16 (&Sectors[n].special);
+ wf->read_i16 (&Sectors[n].tag);
+ // Don't know what the tail is for. Ignore it.
+ if (wf->error ())
+ {
+ err ("%s: error reading sector #%ld", lump_name, long (n));
+ rc = 1;
+ goto sectors_alpha_done;
+ }
+ }
+ }
+
+ sectors_alpha_done:
+ if (offset_table != 0)
+ delete[] offset_table;
+ if (flatnames != 0)
+ delete[] flatnames;
+ if (rc != 0)
+ goto byebye;
+ }
+}
+
+// Read BEHAVIOR
+if (yg_level_format == YGLF_HEXEN)
+{
+const char *lump_name = "BEHAVIOR";
+verbmsg (" behavior\n");
+dir = FindMasterDir (Level, lump_name);
+if (dir)
+ {
+ BehaviorSize = (int)dir->dir.size;
+ if (BehaviorSize > 0)
+ {
+ Behavior = (u8*) GetFarMemory ((unsigned long) BehaviorSize );
+ const Wad_file *wf = dir->wadfile;
+ wf->seek (dir->dir.start);
+ if (wf->error ())
+ {
+ err ("%s: seek error", lump_name);
+ rc = 1;
+ goto byebye;
+ }
+ wf->read_bytes (Behavior, BehaviorSize);
+ if (wf->error ())
+ {
+ err ("%s: error behavior lump", lump_name);
+ rc = 1;
+ goto byebye;
+ }
+ }
+ }
+}
+
+/* Sanity checking on sidedefs: the sector must exist. I don't
+ make this a fatal error, though, because it's not exceptional
+ to find wads with unused sidedefs with a sector# of -1. Well
+ known ones include dyst3 (MAP06, MAP07, MAP08), mm (MAP16),
+ mm2 (MAP13, MAP28) and requiem (MAP03, MAP08, ...). */
+for (long n = 0; n < NumSideDefs; n++)
+ {
+ if (outside (SideDefs[n].sector, 0, NumSectors - 1))
+ warn ("sidedef %ld has bad sector number %d\n",
+ n, SideDefs[n].sector);
+ }
+
+// Ignore REJECT and BLOCKMAP
+
+// Silly statistics
+verbmsg (" %d things, %d vertices, %d linedefs, %d sidedefs, %d sectors\n",
+ (int) NumThings, (int) NumVertices, (int) NumLineDefs,
+ (int) NumSideDefs, (int) NumSectors);
+verbmsg (" Map: (%d,%d)-(%d,%d)\n", MapMinX, MapMinY, MapMaxX, MapMaxY);
+
+byebye:
+if (rc != 0)
+ err ("%s: errors found, giving up", levelname);
+return rc;
+}
+
+
+
+/*
+ forget the level data
+*/
+
+void ForgetLevelData () /* SWAP! */
+{
+/* forget the things */
+ObjectsNeeded (OBJ_THINGS, 0);
+NumThings = 0;
+if (Things != 0)
+ FreeFarMemory (Things);
+Things = 0;
+things_angles++;
+things_types++;
+
+/* forget the vertices */
+ObjectsNeeded (OBJ_VERTICES, 0);
+NumVertices = 0;
+if (Vertices != 0)
+ FreeFarMemory (Vertices);
+Vertices = 0;
+
+/* forget the linedefs */
+ObjectsNeeded (OBJ_LINEDEFS, 0);
+NumLineDefs = 0;
+if (LineDefs != 0)
+ FreeFarMemory (LineDefs);
+LineDefs = 0;
+
+/* forget the sidedefs */
+ObjectsNeeded (OBJ_SIDEDEFS, 0);
+NumSideDefs = 0;
+if (SideDefs != 0)
+ FreeFarMemory (SideDefs);
+SideDefs = 0;
+
+/* forget the sectors */
+ObjectsNeeded (OBJ_SECTORS, 0);
+NumSectors = 0;
+if (Sectors != 0)
+ FreeFarMemory (Sectors);
+Sectors = 0;
+ObjectsNeeded (0);
+}
+
+
+/*
+ * Save the level data to a pwad file
+ * The name of the level is always obtained from
+ * <level_name>, whether or not the level was created from
+ * scratch.
+ *
+ * The previous contents of the pwad file are lost. Yes, it
+ * sucks but it's not easy to fix.
+ *
+ * The lumps are always written in the same order, the same
+ * as the one in the Doom iwad. The length field of the
+ * marker lump is always set to 0. Its offset field is
+ * always set to the offset of the first lump of the level
+ * (THINGS).
+ *
+ * If the level has been created by editing an existing
+ * level and has not been changed in a way that calls for a
+ * rebuild of the nodes, the VERTEXES, SEGS, SSECTORS,
+ * NODES, REJECT and BLOCKMAP lumps are copied from the
+ * original level. Otherwise, they are created with a
+ * length of 0 bytes and an offset equal to the offset of
+ * the previous lump plus its length.
+ *
+ * Returns 0 on success and non-zero on failure (see errno).
+ */
+int SaveLevelData (const char *outfile, const char *level_name) /* SWAP! */
+{
+FILE *file;
+MDirPtr dir;
+int n;
+long lump_offset[WAD_LL__MAX];
+size_t lump_size[WAD_LL__MAX];
+wad_level_lump_no_t l;
+
+if (! level_name || ! levelname2levelno (level_name))
+ {
+ nf_bug ("SaveLevelData: bad level_name \"%s\", using \"E1M1\" instead.",
+ level_name);
+ level_name = "E1M1";
+ }
+DisplayMessage (-1, -1, "Saving data to \"%s\"...", outfile);
+LogMessage (": Saving data to \"%s\"...\n", outfile);
+if ((file = fopen (outfile, "wb")) == NULL)
+ {
+ char buf1[81];
+ char buf2[81];
+ y_snprintf (buf1, sizeof buf1, "Can't open \"%.64s\"", outfile);
+ y_snprintf (buf2, sizeof buf1, "for writing (%.64s)", strerror (errno));
+ Notify (-1, -1, buf1, buf2);
+ return 1;
+ }
+
+/* Can we reuse the old nodes ? Not if this is a new level from
+ scratch or if the structure of the level has changed. If the
+ level comes from an alpha version of Doom, we can't either
+ because that version of Doom didn't have SEGS, NODES, etc. */
+bool reuse_nodes = Level
+ && ! MadeMapChanges
+ && yg_level_format != YGLF_ALPHA;
+
+int NumLumps;
+if (yg_level_format == YGLF_HEXEN)
+ NumLumps = WAD_LL__HEXEN;
+else
+ NumLumps = WAD_LL__DOOM;
+
+// Write the pwad header
+WriteBytes (file, "PWAD", 4); // Pwad file
+file_write_i32 (file, NumLumps); // Number of entries = 11
+file_write_i32 (file, 0); // Fix this up later
+if (Level)
+ dir = Level->next;
+else
+ dir = 0; // Useless except to trap accidental dereferences
+
+// The label (EnMm or MAPnm)
+l = WAD_LL_LABEL;
+lump_offset[l] = ftell (file); // By definition
+lump_size[l] = 0; // By definition
+
+// Write the THINGS lump
+l = WAD_LL_THINGS;
+lump_offset[l] = ftell (file);
+ObjectsNeeded (OBJ_THINGS, 0);
+for (n = 0; n < NumThings; n++)
+ {
+ if (yg_level_format == YGLF_HEXEN)
+ {
+ file_write_i16 (file, Things[n].tid );
+ file_write_i16 (file, Things[n].xpos );
+ file_write_i16 (file, Things[n].ypos );
+ file_write_i16 (file, Things[n].height);
+ file_write_i16 (file, Things[n].angle);
+ file_write_i16 (file, Things[n].type );
+ file_write_i16 (file, Things[n].when );
+ WriteBytes (file, &Things[n].special, 1);
+ WriteBytes (file, &Things[n].arg1, 1 );
+ WriteBytes (file, &Things[n].arg2, 1 );
+ WriteBytes (file, &Things[n].arg3, 1 );
+ WriteBytes (file, &Things[n].arg4, 1 );
+ WriteBytes (file, &Things[n].arg5, 1 );
+ }
+ else
+ {
+ file_write_i16 (file, Things[n].xpos );
+ file_write_i16 (file, Things[n].ypos );
+ file_write_i16 (file, Things[n].angle);
+ file_write_i16 (file, Things[n].type );
+ file_write_i16 (file, Things[n].when );
+ }
+ }
+lump_size[l] = ftell (file) - lump_offset[l];
+if (Level)
+ dir = dir->next;
+
+// Write the LINEDEFS lump
+l = WAD_LL_LINEDEFS;
+lump_offset[WAD_LL_LINEDEFS] = ftell (file);
+ObjectsNeeded (OBJ_LINEDEFS, 0);
+for (n = 0; n < NumLineDefs; n++)
+ {
+ if (yg_level_format == YGLF_HEXEN)
+ {
+ u8 dummy[6];
+ dummy[0] = LineDefs[n].type;
+ dummy[1] = LineDefs[n].tag;
+ dummy[2] = LineDefs[n].arg2;
+ dummy[3] = LineDefs[n].arg3;
+ dummy[4] = LineDefs[n].arg4;
+ dummy[5] = LineDefs[n].arg5;
+ file_write_i16 (file, LineDefs[n].start );
+ file_write_i16 (file, LineDefs[n].end );
+ file_write_i16 (file, LineDefs[n].flags );
+ WriteBytes (file, dummy, 6);
+ file_write_i16 (file, LineDefs[n].sidedef1);
+ file_write_i16 (file, LineDefs[n].sidedef2);
+ }
+ else
+ {
+ file_write_i16 (file, LineDefs[n].start );
+ file_write_i16 (file, LineDefs[n].end );
+ file_write_i16 (file, LineDefs[n].flags );
+ file_write_i16 (file, LineDefs[n].type );
+ file_write_i16 (file, LineDefs[n].tag );
+ file_write_i16 (file, LineDefs[n].sidedef1);
+ file_write_i16 (file, LineDefs[n].sidedef2);
+ }
+ }
+lump_size[l] = ftell (file) - lump_offset[l];
+if (Level)
+ dir = dir->next;
+
+// Write the SIDEDEFS lump
+l = WAD_LL_SIDEDEFS;
+lump_offset[l] = ftell (file);
+ObjectsNeeded (OBJ_SIDEDEFS, 0);
+for (n = 0; n < NumSideDefs; n++)
+ {
+ file_write_i16 (file, SideDefs[n].xoff);
+ file_write_i16 (file, SideDefs[n].yoff);
+ WriteBytes (file, &(SideDefs[n].tex1), WAD_TEX_NAME);
+ WriteBytes (file, &(SideDefs[n].tex2), WAD_TEX_NAME);
+ WriteBytes (file, &(SideDefs[n].tex3), WAD_TEX_NAME);
+ file_write_i16 (file, SideDefs[n].sector);
+ }
+lump_size[l] = ftell (file) - lump_offset[l];
+if (Level)
+ dir = dir->next;
+
+// Write the VERTEXES lump
+l = WAD_LL_VERTEXES;
+lump_offset[WAD_LL_VERTEXES] = ftell (file);
+if (reuse_nodes)
+ {
+ /* Copy the vertices */
+ ObjectsNeeded (0);
+ const Wad_file *wf = dir->wadfile;
+ wf->seek (dir->dir.start);
+ if (wf->error ())
+ {
+ warn ("%s: seek error\n", wad_level_lump[l]);
+ }
+ copy_bytes (file, wf->fp, dir->dir.size);
+ }
+else
+ {
+ /* Write the vertices */
+ ObjectsNeeded (OBJ_VERTICES, 0);
+ for (n = 0; n < NumVertices; n++)
+ {
+ file_write_i16 (file, Vertices[n].x);
+ file_write_i16 (file, Vertices[n].y);
+ }
+ }
+lump_size[l] = ftell (file) - lump_offset[l];
+if (Level)
+ dir = dir->next;
+
+// Write the SEGS, SSECTORS and NODES lumps
+for (n = 0; n < 3; n++)
+ {
+ if (n == 0)
+ l = WAD_LL_SEGS;
+ else if (n == 1)
+ l = WAD_LL_SSECTORS;
+ else if (n == 2)
+ l = WAD_LL_NODES;
+ lump_offset[l] = ftell (file);
+ if (reuse_nodes)
+ {
+ const Wad_file *wf = dir->wadfile;
+ wf->seek (dir->dir.start);
+ if (wf->error ())
+ {
+ warn ("%s: seek error\n", wad_level_lump[l]);
+ }
+ copy_bytes (file, wf->fp, dir->dir.size);
+ }
+ lump_size[l] = ftell (file) - lump_offset[l];
+ if (Level)
+ dir = dir->next;
+ }
+
+// Write the SECTORS lump
+l = WAD_LL_SECTORS;
+lump_offset[l] = ftell (file);
+ObjectsNeeded (OBJ_SECTORS, 0);
+for (n = 0; n < NumSectors; n++)
+ {
+ file_write_i16 (file, Sectors[n].floorh);
+ file_write_i16 (file, Sectors[n].ceilh );
+ WriteBytes (file, Sectors[n].floort, WAD_FLAT_NAME);
+ WriteBytes (file, Sectors[n].ceilt, WAD_FLAT_NAME);
+ file_write_i16 (file, Sectors[n].light );
+ file_write_i16 (file, Sectors[n].special);
+ file_write_i16 (file, Sectors[n].tag );
+ }
+lump_size[l] = ftell (file) - lump_offset[l];
+if (Level)
+ dir = dir->next;
+
+// Write the REJECT lump
+l = WAD_LL_REJECT;
+lump_offset[l] = ftell (file);
+if (reuse_nodes)
+ {
+ /* Copy the REJECT data */
+ ObjectsNeeded (0);
+ const Wad_file *wf = dir->wadfile;
+ wf->seek (dir->dir.start);
+ if (wf->error ())
+ {
+ warn ("%s: seek error\n", wad_level_lump[l]);
+ }
+ copy_bytes (file, wf->fp, dir->dir.size);
+ }
+lump_size[l] = ftell (file) - lump_offset[l];
+if (Level)
+ dir = dir->next;
+
+// Write the BLOCKMAP lump
+l = WAD_LL_BLOCKMAP;
+lump_offset[l] = ftell (file);
+if (reuse_nodes)
+ {
+ ObjectsNeeded (0);
+ const Wad_file *wf = dir->wadfile;
+ wf->seek (dir->dir.start);
+ if (wf->error ())
+ {
+ warn ("%s: seek error\n", wad_level_lump[l]);
+ }
+ copy_bytes (file, wf->fp, dir->dir.size);
+ }
+lump_size[l] = ftell (file) - lump_offset[l];
+if (Level)
+ dir = dir->next;
+
+
+// Write the BEHAVIOR lump
+if (yg_level_format == YGLF_HEXEN)
+{
+ l = WAD_LL_BEHAVIOR;
+ lump_offset[l] = ftell (file);
+ WriteBytes(file, Behavior, BehaviorSize);
+ lump_size[l] = BehaviorSize;
+ if (Level)
+ dir = dir->next;
+}
+
+// Write the actual directory
+long dir_offset = ftell (file);
+for (int L = 0; L < (int) NumLumps; L++)
+ {
+ file_write_i32 (file, lump_offset[L]);
+ file_write_i32 (file, lump_size[L]);
+ if (L == (int) WAD_LL_LABEL)
+ file_write_name (file, level_name);
+ else
+ file_write_name (file, wad_level_lump[L].name);
+ }
+
+/* Fix up the directory start information */
+if (fseek (file, 8, SEEK_SET))
+ {
+ char buf1[81];
+ char buf2[81];
+ y_snprintf (buf1, sizeof buf1, "%.64s: seek error", outfile);
+ y_snprintf (buf2, sizeof buf2, "(%.64s)", strerror (errno));
+ Notify (-1, -1, buf1, buf2);
+ fclose (file);
+ return 1;
+ }
+file_write_i32 (file, dir_offset);
+
+/* Close the file */
+if (fclose (file))
+ {
+ char buf1[81];
+ char buf2[81];
+ y_snprintf (buf1, sizeof buf1, "%.64s: write error", outfile);
+ y_snprintf (buf2, sizeof buf2, "(%.64s)", strerror (errno));
+ Notify (-1, -1, buf1, buf2);
+ return 1;
+ }
+
+/* The file is now up to date */
+if (! Level || MadeMapChanges)
+ remind_to_build_nodes = 1;
+MadeChanges = 0;
+MadeMapChanges = 0;
+ObjectsNeeded (0);
+
+/* Update pointers in Master Directory */
+OpenPatchWad (outfile);
+
+/* This should free the old "*.bak" file */
+CloseUnusedWadFiles ();
+
+/* Update MapMinX, MapMinY, MapMaxX, MapMaxY */
+// Probably not necessary anymore -- AYM 1999-04-05
+ObjectsNeeded (OBJ_VERTICES, 0);
+update_level_bounds ();
+return 0;
+}
+
+
+/*
+ * flat_list_entry_cmp
+ * Function used by qsort() to sort the flat_list_entry array
+ * by ascending flat name.
+ */
+static int flat_list_entry_cmp (const void *a, const void *b)
+{
+return y_strnicmp (
+ ((const flat_list_entry_t *) a)->name,
+ ((const flat_list_entry_t *) b)->name,
+ WAD_FLAT_NAME);
+}
+
+
+/*
+ function used by qsort to sort the texture names
+*/
+static int SortTextures (const void *a, const void *b)
+{
+return y_strnicmp (*((const char *const *)a), *((const char *const *)b),
+ WAD_TEX_NAME);
+}
+
+
+/*
+ read the texture names
+*/
+void ReadWTextureNames ()
+{
+MDirPtr dir;
+int n;
+i32 val;
+
+verbmsg ("Reading texture names\n");
+
+// Doom alpha 0.4 : "TEXTURES", no names
+if (yg_texture_lumps == YGTL_TEXTURES
+ && yg_texture_format == YGTF_NAMELESS)
+ {
+ const char *lump_name = "TEXTURES";
+ dir = FindMasterDir (MasterDir, lump_name);
+ if (dir == NULL)
+ {
+ warn ("%s: lump not found in directory\n", lump_name);
+ goto textures04_done;
+ }
+ {
+ const Wad_file *wf = dir->wadfile;
+ wf->seek (dir->dir.start);
+ if (wf->error ())
+ {
+ warn ("%s: seek error\n", lump_name);
+ goto textures04_done;
+ }
+ wf->read_i32 (&val);
+ if (wf->error ())
+ {
+ warn ("%s: error reading texture count\n", lump_name);
+ goto textures04_done;
+ }
+ NumWTexture = (int) val + 1;
+ WTexture = (char **) GetMemory ((long) NumWTexture * sizeof *WTexture);
+ WTexture[0] = (char *) GetMemory (WAD_TEX_NAME + 1);
+ strcpy (WTexture[0], "-");
+ if (WAD_TEX_NAME < 7) nf_bug ("WAD_TEX_NAME too small"); // Sanity
+ for (long n = 0; n < val; n++)
+ {
+ WTexture[n + 1] = (char *) GetMemory (WAD_TEX_NAME + 1);
+ if (n > 9999)
+ {
+ warn ("more than 10,000 textures. Ignoring excess.\n");
+ break;
+ }
+ sprintf (WTexture[n + 1], "TEX%04ld", n);
+ }
+ }
+ textures04_done:
+ ;
+ }
+
+// Doom alpha 0.5 : only "TEXTURES"
+else if (yg_texture_lumps == YGTL_TEXTURES
+ && (yg_texture_format == YGTF_NORMAL
+ || yg_texture_format == YGTF_STRIFE11))
+ {
+ const char *lump_name = "TEXTURES";
+ i32 *offsets = 0;
+ dir = FindMasterDir (MasterDir, lump_name);
+ if (dir == NULL) // In theory it always exists, though
+ {
+ warn ("%s: lump not found in directory\n", lump_name);
+ goto textures05_done;
+ }
+ {
+ const Wad_file *wf = dir->wadfile;
+ wf->seek (dir->dir.start);
+ if (wf->error ())
+ {
+ warn ("%s: seek error\n", lump_name);
+ goto textures05_done;
+ }
+ wf->read_i32 (&val);
+ if (wf->error ())
+ {
+ warn ("%s: error reading texture count\n", lump_name);
+ goto textures05_done;
+ }
+ NumWTexture = (int) val + 1;
+ /* read in the offsets for texture1 names */
+ offsets = (i32 *) GetMemory ((long) NumWTexture * 4);
+ wf->read_i32 (offsets + 1, NumWTexture - 1);
+ if (wf->error ())
+ {
+ warn ("%s: error reading offsets table\n", lump_name);
+ goto textures05_done;
+ }
+ /* read in the actual names */
+ WTexture = (char **) GetMemory ((long) NumWTexture * sizeof (char *));
+ WTexture[0] = (char *) GetMemory (WAD_TEX_NAME + 1);
+ strcpy (WTexture[0], "-");
+ for (n = 1; n < NumWTexture; n++)
+ {
+ WTexture[n] = (char *) GetMemory (WAD_TEX_NAME + 1);
+ long offset = dir->dir.start + offsets[n];
+ wf->seek (offset);
+ if (wf->error ())
+ {
+ warn ("%s: error seeking to error\n", lump_name);
+ goto textures05_done; // FIXME cleanup
+ }
+ wf->read_bytes (WTexture[n], WAD_TEX_NAME);
+ if (wf->error ())
+ {
+ warn ("%s: error reading texture names\n", lump_name);
+ goto textures05_done; // FIXME cleanup
+ }
+ WTexture[n][WAD_TEX_NAME] = '\0';
+ }
+ }
+ textures05_done:
+ if (offsets != 0)
+ FreeMemory (offsets);
+ }
+// Other iwads : "TEXTURE1" and possibly "TEXTURE2"
+else if (yg_texture_lumps == YGTL_NORMAL
+ && (yg_texture_format == YGTF_NORMAL
+ || yg_texture_format == YGTF_STRIFE11))
+ {
+ const char *lump_name = "TEXTURE1";
+ i32 *offsets = 0;
+ dir = FindMasterDir (MasterDir, lump_name);
+ if (dir != NULL) // In theory it always exists, though
+ {
+ const Wad_file *wf = dir->wadfile;
+ wf->seek (dir->dir.start);
+ if (wf->error ())
+ {
+ warn ("%s: seek error\n", lump_name);
+ // FIXME
+ }
+ wf->read_i32 (&val);
+ if (wf->error ())
+ {
+ // FIXME
+ }
+ NumWTexture = (int) val + 1;
+ /* read in the offsets for texture1 names */
+ offsets = (i32 *) GetMemory ((long) NumWTexture * 4);
+ wf->read_i32 (offsets + 1, NumWTexture - 1);
+ {
+ // FIXME
+ }
+ /* read in the actual names */
+ WTexture = (char **) GetMemory ((long) NumWTexture * sizeof (char *));
+ WTexture[0] = (char *) GetMemory (WAD_TEX_NAME + 1);
+ strcpy (WTexture[0], "-");
+ for (n = 1; n < NumWTexture; n++)
+ {
+ WTexture[n] = (char *) GetMemory (WAD_TEX_NAME + 1);
+ wf->seek (dir->dir.start + offsets[n]);
+ if (wf->error ())
+ {
+ warn ("%s: seek error\n", lump_name);
+ // FIXME
+ }
+ wf->read_bytes (WTexture[n], WAD_TEX_NAME);
+ if (wf->error ())
+ {
+ // FIXME
+ }
+ WTexture[n][WAD_TEX_NAME] = '\0';
+ }
+ FreeMemory (offsets);
+ }
+ {
+ dir = FindMasterDir (MasterDir, "TEXTURE2");
+ if (dir) /* Doom II has no TEXTURE2 */
+ {
+ const Wad_file *wf = dir->wadfile;
+ wf->seek (dir->dir.start);
+ if (wf->error ())
+ {
+ warn ("%s: seek error\n", lump_name);
+ // FIXME
+ }
+ wf->read_i32 (&val);
+ if (wf->error ())
+ {
+ // FIXME
+ }
+ /* read in the offsets for texture2 names */
+ offsets = (i32 *) GetMemory ((long) val * 4);
+ wf->read_i32 (offsets, val);
+ if (wf->error ())
+ {
+ // FIXME
+ }
+ /* read in the actual names */
+ WTexture = (char **) ResizeMemory (WTexture,
+ (NumWTexture + val) * sizeof (char *));
+ for (n = 0; n < val; n++)
+ {
+ WTexture[NumWTexture + n] = (char *) GetMemory (WAD_TEX_NAME + 1);
+ wf->seek (dir->dir.start + offsets[n]);
+ if (wf->error ())
+ {
+ warn ("%s: seek error\n", lump_name);
+ // FIXME
+ }
+ wf->read_bytes (WTexture[NumWTexture + n], WAD_TEX_NAME);
+ if (wf->error ())
+ ; // FIXME
+ WTexture[NumWTexture + n][WAD_TEX_NAME] = '\0';
+ }
+ NumWTexture += val;
+ FreeMemory (offsets);
+ }
+ }
+ }
+else
+ nf_bug ("Invalid texture_format/texture_lumps combination.");
+
+/* sort the names */
+qsort (WTexture, NumWTexture, sizeof (char *), SortTextures);
+}
+
+
+
+/*
+ forget the texture names
+*/
+
+void ForgetWTextureNames ()
+{
+int n;
+
+/* forget all names */
+for (n = 0; n < NumWTexture; n++)
+ FreeMemory (WTexture[n]);
+
+/* forget the array */
+NumWTexture = 0;
+FreeMemory (WTexture);
+}
+
+
+
+/*
+ read the flat names
+*/
+
+void ReadFTextureNames ()
+{
+MDirPtr dir;
+int n;
+
+verbmsg ("Reading flat names");
+NumFTexture = 0;
+
+for (dir = MasterDir; (dir = FindMasterDir (dir, "F_START", "FF_START"));)
+ {
+ bool ff_start = ! y_strnicmp (dir->dir.name, "FF_START", WAD_NAME);
+ MDirPtr dir0;
+ /* count the names */
+ dir = dir->next;
+ dir0 = dir;
+ for (n = 0; dir && y_strnicmp (dir->dir.name, "F_END", WAD_NAME)
+ && (! ff_start || y_strnicmp (dir->dir.name, "FF_END", WAD_NAME));
+ dir = dir->next)
+ {
+ if (dir->dir.start == 0 || dir->dir.size == 0)
+ {
+ if (! (toupper (dir->dir.name[0]) == 'F'
+ && (dir->dir.name[1] == '1'
+ || dir->dir.name[1] == '2'
+ || dir->dir.name[1] == '3'
+ || toupper (dir->dir.name[1]) == 'F')
+ && dir->dir.name[2] == '_'
+ && (! y_strnicmp (dir->dir.name + 3, "START", WAD_NAME - 3)
+ || ! y_strnicmp (dir->dir.name + 3, "END", WAD_NAME - 3))))
+ warn ("unexpected label \"%.*s\" among flats.\n",
+ WAD_NAME, dir->dir.name);
+ continue;
+ }
+ if (dir->dir.size != 4096)
+ warn ("flat \"%.*s\" has weird size %lu."
+ " Using 4096 instead.\n",
+ WAD_NAME, dir->dir.name, (unsigned long) dir->dir.size);
+ n++;
+ }
+ /* If FF_START/FF_END followed by F_END (mm2.wad), advance
+ past F_END. In fact, this does not work because the F_END
+ that follows has been snatched by OpenPatchWad(), that
+ thinks it replaces the F_END from the iwad. OpenPatchWad()
+ needs to be kludged to take this special case into
+ account. Fortunately, the only consequence is a useless
+ "this wad uses FF_END" warning. -- AYM 1999-07-10 */
+ if (ff_start && dir && ! y_strnicmp (dir->dir.name, "FF_END", WAD_NAME))
+ if (dir->next && ! y_strnicmp (dir->next->dir.name, "F_END", WAD_NAME))
+ dir = dir->next;
+
+ verbmsg (" FF_START/%s %d", dir->dir.name, n);
+ if (dir && ! y_strnicmp (dir->dir.name, "FF_END", WAD_NAME))
+ warn ("this wad uses FF_END. That won't work with Doom."
+ " Use F_END instead.\n");
+ /* get the actual names from master dir. */
+ flat_list = (flat_list_entry_t *) ResizeMemory (flat_list,
+ (NumFTexture + n) * sizeof *flat_list);
+ for (size_t m = NumFTexture; m < NumFTexture + n; dir0 = dir0->next)
+ {
+ // Skip all labels.
+ if (dir0->dir.start == 0
+ || dir0->dir.size == 0
+ || (toupper (dir0->dir.name[0]) == 'F'
+ && (dir0->dir.name[1] == '1'
+ || dir0->dir.name[1] == '2'
+ || dir0->dir.name[1] == '3'
+ || toupper (dir0->dir.name[1]) == 'F')
+ && dir0->dir.name[2] == '_'
+ && (! y_strnicmp (dir0->dir.name + 3, "START", WAD_NAME - 3)
+ || ! y_strnicmp (dir0->dir.name + 3, "END", WAD_NAME - 3))
+ ))
+ continue;
+ *flat_list[m].name = '\0';
+ strncat (flat_list[m].name, dir0->dir.name, sizeof flat_list[m].name - 1);
+ flat_list[m].wadfile = dir0->wadfile;
+ flat_list[m].offset = dir0->dir.start;
+ m++;
+ }
+ NumFTexture += n;
+ }
+
+verbmsg ("\n");
+
+/* sort the flats by names */
+qsort (flat_list, NumFTexture, sizeof *flat_list, flat_list_entry_cmp);
+
+/* Eliminate all but the last duplicates of a flat. Suboptimal.
+ Would be smarter to start by the end. */
+for (size_t n = 0; n < NumFTexture; n++)
+ {
+ size_t m = n;
+ while (m + 1 < NumFTexture
+ && ! flat_list_entry_cmp (flat_list + n, flat_list + m + 1))
+ m++;
+ // m now contains the index of the last duplicate
+ int nduplicates = m - n;
+ if (nduplicates > 0)
+ {
+ memmove (flat_list + n, flat_list + m,
+ (NumFTexture - m) * sizeof *flat_list);
+ NumFTexture -= nduplicates;
+ // Note that I'm too lazy to resize flat_list...
+ }
+ }
+}
+
+
+/*
+ * is_flat_name_in_list
+ * FIXME should use bsearch()
+ */
+int is_flat_name_in_list (const char *name)
+{
+ if (! flat_list)
+ return 0;
+ for (size_t n = 0; n < NumFTexture; n++)
+ if (! y_strnicmp (name, flat_list[n].name, WAD_FLAT_NAME))
+ return 1;
+ return 0;
+}
+
+
+/*
+ forget the flat names
+*/
+
+void ForgetFTextureNames ()
+{
+NumFTexture = 0;
+FreeMemory (flat_list);
+flat_list = 0;
+}
+
+
+/*
+ * update_level_bounds - update Map{Min,Max}{X,Y}
+ */
+void update_level_bounds ()
+{
+MapMaxX = -32767;
+MapMaxY = -32767;
+MapMinX = 32767;
+MapMinY = 32767;
+for (obj_no_t n = 0; n < NumVertices; n++)
+ {
+ int x = Vertices[n].x;
+ if (x < MapMinX)
+ MapMinX = x;
+ if (x > MapMaxX)
+ MapMaxX = x;
+ int y = Vertices[n].y;
+ if (y < MapMinY)
+ MapMinY = y;
+ if (y > MapMaxY)
+ MapMaxY = y;
+ }
+}
+
diff -Naur yadex-1.7.0.orig/src/levels.h yadex-1.7.0.allpatches/src/levels.h
--- yadex-1.7.0.orig/src/levels.h 2001-10-13 16:57:33.000000000 +0200
+++ yadex-1.7.0.allpatches/src/levels.h 2013-01-10 17:25:53.196909491 +0100
@@ -28,6 +28,8 @@
extern int NumSegs; /* number of segments */
extern int NumSectors; /* number of sectors */
extern SPtr Sectors; /* sectors data */
+extern u8* Behavior;
+extern int BehaviorSize;
// FIXME should be somewhere else
extern int NumWTexture; /* number of wall textures */
diff -Naur yadex-1.7.0.orig/src/l_prop.cc yadex-1.7.0.allpatches/src/l_prop.cc
--- yadex-1.7.0.orig/src/l_prop.cc 2013-01-10 17:24:11.556024006 +0100
+++ yadex-1.7.0.allpatches/src/l_prop.cc 2013-01-10 17:25:53.196909491 +0100
@@ -111,7 +111,7 @@
void LinedefProperties (int x0, int y0, SelPtr obj)
{
- char *menustr[8];
+ char *menustr[12];
char texname[WAD_TEX_NAME + 1];
int n, val;
SelPtr cur, sdlist;
@@ -132,9 +132,9 @@
switch (val)
{
case 1:
- for (n = 0; n < 8; n++)
+ for (n = 0; n < 12; n++)
menustr[n] = (char *) GetMemory (60);
- sprintf (menustr[7], "Edit linedef #%d", obj->objnum);
+ sprintf (menustr[11], "Edit linedef #%d", obj->objnum);
sprintf (menustr[0], "Change flags (Current: %d)",
LineDefs[obj->objnum].flags);
sprintf (menustr[1], "Change type (Current: %d)",
@@ -149,7 +149,16 @@
LineDefs[obj->objnum].sidedef1);
sprintf (menustr[6], "Change 2nd sidedef ref. (Current: #%d)",
LineDefs[obj->objnum].sidedef2);
- val = vDisplayMenu (x0 + 42, subwin_y0, menustr[7],
+ sprintf (menustr[7], "Change special arg2 (Current: %d)",
+ LineDefs[obj->objnum].arg2);
+ sprintf (menustr[8], "Change special arg3 (Current: %d)",
+ LineDefs[obj->objnum].arg3);
+ sprintf (menustr[9], "Change special arg4 (Current: %d)",
+ LineDefs[obj->objnum].arg4);
+ sprintf (menustr[10], "Change special arg5 (Current: %d)",
+ LineDefs[obj->objnum].arg5);
+ if (yg_level_format == YGLF_HEXEN)
+ val = vDisplayMenu (x0 + 42, subwin_y0, menustr[11],
menustr[0], YK_, 0,
menustr[1], YK_, 0,
menustr[2], YK_, 0,
@@ -157,8 +166,22 @@
menustr[4], YK_, 0,
menustr[5], YK_, 0,
menustr[6], YK_, 0,
+ menustr[7], YK_, 0,
+ menustr[8], YK_, 0,
+ menustr[9], YK_, 0,
+ menustr[10], YK_, 0,
NULL);
- for (n = 0; n < 8; n++)
+ else
+ val = vDisplayMenu (x0 + 42, subwin_y0, menustr[11],
+ menustr[0], YK_, 0,
+ menustr[1], YK_, 0,
+ menustr[2], YK_, 0,
+ menustr[3], YK_, 0,
+ menustr[4], YK_, 0,
+ menustr[5], YK_, 0,
+ menustr[6], YK_, 0,
+ NULL);
+ for (n = 0; n < 12; n++)
FreeMemory (menustr[n]);
subsubwin_y0 = subwin_y0 + BOX_BORDER + (2 + val) * FONTH;
switch (val)
@@ -269,6 +292,50 @@
MadeMapChanges = 1;
}
break;
+
+ case 8:
+ val = InputIntegerValue (x0 + 84, subsubwin_y0,
+ 0, 255, LineDefs[obj->objnum].arg2);
+ if (val != IIV_CANCEL) // Not [esc]
+ {
+ for (cur = obj; cur; cur = cur->next)
+ LineDefs[cur->objnum].arg2 = val;
+ MadeChanges = 1;
+ }
+ break;
+
+ case 9:
+ val = InputIntegerValue (x0 + 84, subsubwin_y0,
+ 0, 255, LineDefs[obj->objnum].arg3);
+ if (val != IIV_CANCEL) // Not [esc]
+ {
+ for (cur = obj; cur; cur = cur->next)
+ LineDefs[cur->objnum].arg3 = val;
+ MadeChanges = 1;
+ }
+ break;
+
+ case 10:
+ val = InputIntegerValue (x0 + 84, subsubwin_y0,
+ 0, 255, LineDefs[obj->objnum].arg4);
+ if (val != IIV_CANCEL) // Not [esc]
+ {
+ for (cur = obj; cur; cur = cur->next)
+ LineDefs[cur->objnum].arg4 = val;
+ MadeChanges = 1;
+ }
+ break;
+
+ case 11:
+ val = InputIntegerValue (x0 + 84, subsubwin_y0,
+ 0, 255, LineDefs[obj->objnum].arg5);
+ if (val != IIV_CANCEL) // Not [esc]
+ {
+ for (cur = obj; cur; cur = cur->next)
+ LineDefs[cur->objnum].arg5 = val;
+ MadeChanges = 1;
+ }
+ break;
}
break;
diff -Naur yadex-1.7.0.orig/src/objects.cc yadex-1.7.0.allpatches/src/objects.cc
--- yadex-1.7.0.orig/src/objects.cc 2013-01-10 17:24:11.579024205 +0100
+++ yadex-1.7.0.allpatches/src/objects.cc 2013-01-10 17:25:53.197909500 +0100
@@ -465,12 +465,28 @@
Things[last].type = Things[copyfrom].type;
Things[last].angle = Things[copyfrom].angle;
Things[last].when = Things[copyfrom].when;
+ Things[last].tid = Things[copyfrom].tid;
+ Things[last].height = Things[copyfrom].height;
+ Things[last].special = Things[copyfrom].special;
+ Things[last].arg1 = Things[copyfrom].arg1;
+ Things[last].arg2 = Things[copyfrom].arg2;
+ Things[last].arg3 = Things[copyfrom].arg3;
+ Things[last].arg4 = Things[copyfrom].arg4;
+ Things[last].arg5 = Things[copyfrom].arg5;
}
else
{
Things[last].type = default_thing;
Things[last].angle = 0;
Things[last].when = 0x07;
+ Things[last].tid = 0;
+ Things[last].height = 0;
+ Things[last].special = 0;
+ Things[last].arg1 = 0;
+ Things[last].arg2 = 0;
+ Things[last].arg3 = 0;
+ Things[last].arg4 = 0;
+ Things[last].arg5 = 0;
}
break;
@@ -508,6 +524,10 @@
LineDefs[last].flags = LineDefs[copyfrom].flags;
LineDefs[last].type = LineDefs[copyfrom].type;
LineDefs[last].tag = LineDefs[copyfrom].tag;
+ LineDefs[last].arg2 = LineDefs[copyfrom].arg2;
+ LineDefs[last].arg3 = LineDefs[copyfrom].arg3;
+ LineDefs[last].arg4 = LineDefs[copyfrom].arg4;
+ LineDefs[last].arg5 = LineDefs[copyfrom].arg5;
}
else
{
@@ -516,6 +536,10 @@
LineDefs[last].flags = 1;
LineDefs[last].type = 0;
LineDefs[last].tag = 0;
+ LineDefs[last].arg2 = 0;
+ LineDefs[last].arg3 = 0;
+ LineDefs[last].arg4 = 0;
+ LineDefs[last].arg5 = 0;
}
LineDefs[last].sidedef1 = OBJ_NO_NONE;
LineDefs[last].sidedef2 = OBJ_NO_NONE;
diff -Naur yadex-1.7.0.orig/src/objects.cc.orig yadex-1.7.0.allpatches/src/objects.cc.orig
--- yadex-1.7.0.orig/src/objects.cc.orig 1970-01-01 01:00:00.000000000 +0100
+++ yadex-1.7.0.allpatches/src/objects.cc.orig 2013-01-10 17:25:53.198909508 +0100
@@ -0,0 +1,1252 @@
+/*
+ * objects.cc
+ * Object handling routines.
+ * BW & RQ sometime in 1993 or 1994.
+ */
+
+
+/*
+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-2003 Andrť Majorel and others.
+
+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 "drawmap.h"
+#include "gfx.h"
+#include "l_vertices.h"
+#include "levels.h"
+#include "objects.h"
+#include "objid.h"
+#include "s_vertices.h"
+#include "selectn.h"
+#include "things.h"
+
+
+/*
+ highlight the selected objects
+*/
+void HighlightSelection (int objtype, SelPtr list) /* SWAP! */
+{
+SelPtr cur;
+
+if (! list)
+ return;
+for (cur = list; cur; cur = cur->next)
+ HighlightObject (objtype, cur->objnum, GREEN);
+}
+
+
+
+/*
+ get the number of objects of a given type minus one
+*/
+obj_no_t GetMaxObjectNum (int objtype)
+{
+switch (objtype)
+ {
+ case OBJ_THINGS:
+ return NumThings - 1;
+ case OBJ_LINEDEFS:
+ return NumLineDefs - 1;
+ case OBJ_SIDEDEFS:
+ return NumSideDefs - 1;
+ case OBJ_VERTICES:
+ return NumVertices - 1;
+ case OBJ_SECTORS:
+ return NumSectors - 1;
+ }
+return -1;
+}
+
+
+/*
+ highlight the selected object
+*/
+void HighlightObject (int objtype, int objnum, int colour) /* SWAP! */
+{
+int n, m;
+
+/* use XOR mode : drawing any line twice erases it */
+SetDrawingMode (1);
+set_colour (colour);
+switch (objtype)
+ {
+ case OBJ_THINGS:
+ ObjectsNeeded (OBJ_THINGS, 0);
+ m = (get_thing_radius (Things[objnum].type) * 3) / 2;
+ DrawMapLine (Things[objnum].xpos - m, Things[objnum].ypos - m,
+ Things[objnum].xpos - m, Things[objnum].ypos + m);
+ DrawMapLine (Things[objnum].xpos - m, Things[objnum].ypos + m,
+ Things[objnum].xpos + m, Things[objnum].ypos + m);
+ DrawMapLine (Things[objnum].xpos + m, Things[objnum].ypos + m,
+ Things[objnum].xpos + m, Things[objnum].ypos - m);
+ DrawMapLine (Things[objnum].xpos + m, Things[objnum].ypos - m,
+ Things[objnum].xpos - m, Things[objnum].ypos - m);
+ DrawMapArrow (Things[objnum].xpos, Things[objnum].ypos,
+ Things[objnum].angle * 182);
+ break;
+
+ case OBJ_LINEDEFS:
+ ObjectsNeeded (OBJ_LINEDEFS, OBJ_VERTICES, 0);
+ n = (Vertices[LineDefs[objnum].start].x
+ + Vertices[LineDefs[objnum].end].x) / 2;
+ m = (Vertices[LineDefs[objnum].start].y
+ + Vertices[LineDefs[objnum].end].y) / 2;
+ DrawMapLine (n, m, n + (Vertices[LineDefs[objnum].end].y
+ - Vertices[LineDefs[objnum].start].y) / 3,
+ m + (Vertices[LineDefs[objnum].start].x
+ - Vertices[LineDefs[objnum].end].x) / 3);
+ SetLineThickness (1);
+ DrawMapVector (Vertices[LineDefs[objnum].start].x,
+ Vertices[LineDefs[objnum].start].y,
+ Vertices[LineDefs[objnum].end].x,
+ Vertices[LineDefs[objnum].end].y);
+ if (colour != LIGHTRED && LineDefs[objnum].tag > 0)
+ {
+ for (m = 0; m < NumSectors; m++)
+ if (Sectors[m].tag == LineDefs[objnum].tag)
+ HighlightObject (OBJ_SECTORS, m, LIGHTRED);
+ }
+ SetLineThickness (0);
+ break;
+
+ case OBJ_VERTICES:
+ ObjectsNeeded (OBJ_VERTICES, 0);
+ {
+ int r = vertex_radius (Scale) * 3 / 2;
+ int scrx0 = SCREENX (Vertices[objnum].x) - r;
+ int scrx9 = SCREENX (Vertices[objnum].x) + r;
+ int scry0 = SCREENY (Vertices[objnum].y) - r;
+ int scry9 = SCREENY (Vertices[objnum].y) + r;
+ DrawScreenLine (scrx0, scry0, scrx9, scry0);
+ DrawScreenLine (scrx9, scry0, scrx9, scry9);
+ DrawScreenLine (scrx9, scry9, scrx0, scry9);
+ DrawScreenLine (scrx0, scry9, scrx0, scry0);
+ }
+ break;
+
+ case OBJ_SECTORS:
+ {
+ ObjectsNeeded (OBJ_LINEDEFS, OBJ_SIDEDEFS, OBJ_VERTICES, 0);
+ SetLineThickness (1);
+ const int mapx0 = MAPX (0);
+ const int mapy0 = MAPY (ScrMaxY);
+ const int mapx1 = MAPX (ScrMaxX);
+ const int mapy1 = MAPY (0);
+ for (n = 0; n < NumLineDefs; n++)
+ if (LineDefs[n].sidedef1 != -1
+ && SideDefs[LineDefs[n].sidedef1].sector == objnum
+ || LineDefs[n].sidedef2 != -1
+ && SideDefs[LineDefs[n].sidedef2].sector == objnum)
+ {
+ const struct Vertex *v1 = Vertices + LineDefs[n].start;
+ const struct Vertex *v2 = Vertices + LineDefs[n].end;
+ if (v1->x < mapx0 && v2->x < mapx0
+ || v1->x > mapx1 && v2->x > mapx1
+ || v1->y < mapy0 && v2->y < mapy0
+ || v1->y > mapy1 && v2->y > mapy1)
+ continue; // Off-screen
+ DrawMapLine (v1->x, v1->y, v2->x, v2->y);
+ }
+ if (colour != LIGHTRED && Sectors[objnum].tag > 0)
+ {
+ for (m = 0; m < NumLineDefs; m++)
+ if (LineDefs[m].tag == Sectors[objnum].tag)
+ HighlightObject (OBJ_LINEDEFS, m, LIGHTRED);
+ }
+ SetLineThickness (0);
+ }
+ break;
+ }
+/* restore normal write mode */
+SetDrawingMode (0);
+}
+
+
+
+/*
+ delete an object
+*/
+void DeleteObject (const Objid& obj) /* SWAP! */
+{
+SelPtr list;
+
+list = 0;
+SelectObject (&list, obj.num);
+DeleteObjects (obj.type, &list);
+}
+
+
+
+/*
+ delete a group of objects (*recursive*)
+*/
+void DeleteObjects (int objtype, SelPtr *list) /* SWAP! */
+{
+int n, objnum;
+SelPtr cur;
+
+MadeChanges = 1;
+switch (objtype)
+ {
+ case OBJ_THINGS:
+ ObjectsNeeded (OBJ_THINGS, 0);
+ if (*list)
+ {
+ things_angles++;
+ things_types++;
+ }
+ while (*list)
+ {
+ objnum = (*list)->objnum;
+ if (objnum < 0 || objnum >= NumThings) // Paranoia
+ {
+ nf_bug ("attempt to delete non-existent thing #%d", objnum);
+ goto next_thing;
+ }
+ // Delete the thing
+ NumThings--;
+ if (NumThings > 0)
+ {
+ for (n = objnum; n < NumThings; n++)
+ Things[n] = Things[n + 1];
+ Things = (TPtr) ResizeFarMemory (Things,
+ NumThings * sizeof (struct Thing));
+ }
+ else
+ {
+ FreeFarMemory (Things);
+ Things = 0;
+ }
+ for (cur = (*list)->next; cur; cur = cur->next)
+ if (cur->objnum > objnum)
+ cur->objnum--;
+ next_thing:
+ UnSelectObject (list, objnum);
+ }
+ break;
+
+ case OBJ_VERTICES:
+ if (*list)
+ MadeMapChanges = 1;
+ while (*list)
+ {
+ objnum = (*list)->objnum;
+ if (objnum < 0 || objnum >= NumVertices) // Paranoia
+ {
+ nf_bug ("attempt to delete non-existent vertex #%d", objnum);
+ goto next_vertex;
+ }
+ // Delete the linedefs bound to this vertex and change the references
+ ObjectsNeeded (OBJ_LINEDEFS, 0);
+ for (n = 0; n < NumLineDefs; n++)
+ {
+ if (LineDefs[n].start == objnum || LineDefs[n].end == objnum)
+ DeleteObject (Objid (OBJ_LINEDEFS, n--));
+ else
+ {
+ if (LineDefs[n].start >= objnum)
+ LineDefs[n].start--;
+ if (LineDefs[n].end >= objnum)
+ LineDefs[n].end--;
+ }
+ }
+ // Delete the vertex
+ ObjectsNeeded (OBJ_VERTICES, 0);
+ NumVertices--;
+ if (NumVertices > 0)
+ {
+ for (n = objnum; n < NumVertices; n++)
+ Vertices[n] = Vertices[n + 1];
+ Vertices = (VPtr) ResizeFarMemory (Vertices,
+ NumVertices * sizeof (struct Vertex));
+ }
+ else
+ {
+ FreeFarMemory (Vertices);
+ Vertices = 0;
+ }
+ for (cur = (*list)->next; cur; cur = cur->next)
+ if (cur->objnum > objnum)
+ cur->objnum--;
+ next_vertex:
+ UnSelectObject (list, objnum);
+ }
+ break;
+
+ case OBJ_LINEDEFS:
+ /* In DEU, deleting a linedef was not considered to be a
+ map change. Deleting a _sidedef_ was. In Yadex,
+ sidedefs are not automatically deleted when the linedef
+ is because some sidedefs are shared by more than one
+ linedef. So we need to set MadeMapChanges here. */
+ if (*list)
+ MadeMapChanges = 1;
+ /* AYM 19980203 I've removed the deletion of sidedefs
+ because if several linedefs use the same sidedef, this
+ would lead to trouble. Instead, I let the xref checking
+ take care of that. */
+ while (*list)
+ {
+ ObjectsNeeded (OBJ_LINEDEFS, 0);
+ objnum = (*list)->objnum;
+ if (objnum < 0 || objnum >= NumLineDefs) // Paranoia
+ {
+ nf_bug ("attempt to delete non-existent linedef #%d", objnum);
+ goto next_linedef;
+ }
+ // delete the linedef
+ NumLineDefs--;
+ if (NumLineDefs > 0)
+ {
+ for (n = objnum; n < NumLineDefs; n++)
+ LineDefs[n] = LineDefs[n + 1];
+ LineDefs = (LDPtr) ResizeFarMemory (LineDefs,
+ NumLineDefs * sizeof (struct LineDef));
+ }
+ else
+ {
+ FreeFarMemory (LineDefs);
+ LineDefs = 0;
+ }
+ for (cur = (*list)->next; cur; cur = cur->next)
+ if (cur->objnum > objnum)
+ cur->objnum--;
+ next_linedef:
+ UnSelectObject (list, objnum);
+ }
+ break;
+
+ case OBJ_SIDEDEFS:
+ if (*list)
+ MadeMapChanges = 1;
+ while (*list)
+ {
+ objnum = (*list)->objnum;
+ if (objnum < 0 || objnum >= NumSideDefs) // Paranoia
+ {
+ nf_bug ("attempt to delete non-existent sidedef #%d", objnum);
+ goto next_sidedef;
+ }
+ /* change the linedefs references */
+ ObjectsNeeded (OBJ_LINEDEFS, 0);
+ for (n = 0; n < NumLineDefs; n++)
+ {
+ if (LineDefs[n].sidedef1 == objnum)
+ LineDefs[n].sidedef1 = -1;
+ else if (LineDefs[n].sidedef1 >= objnum)
+ LineDefs[n].sidedef1--;
+ if (LineDefs[n].sidedef2 == objnum)
+ LineDefs[n].sidedef2 = -1;
+ else if (LineDefs[n].sidedef2 >= objnum)
+ LineDefs[n].sidedef2--;
+ }
+ /* delete the sidedef */
+ ObjectsNeeded (OBJ_SIDEDEFS, 0);
+ NumSideDefs--;
+ if (NumSideDefs > 0)
+ {
+ for (n = objnum; n < NumSideDefs; n++)
+ SideDefs[n] = SideDefs[n + 1];
+ SideDefs = (SDPtr) ResizeFarMemory (SideDefs,
+ NumSideDefs * sizeof (struct SideDef));
+ }
+ else
+ {
+ FreeFarMemory (SideDefs);
+ SideDefs = 0;
+ }
+ for (cur = (*list)->next; cur; cur = cur->next)
+ if (cur->objnum > objnum)
+ cur->objnum--;
+ next_sidedef:
+ UnSelectObject (list, objnum);
+ }
+ break;
+ case OBJ_SECTORS:
+ while (*list)
+ {
+ objnum = (*list)->objnum;
+ if (objnum < 0 || objnum >= NumSectors) // Paranoia
+ {
+ nf_bug ("attempt to delete non-existent sector #%d", objnum);
+ goto next_sector;
+ }
+ // Delete the sidedefs bound to this sector and change the references
+ // AYM 19980203: Hmm, hope this is OK with multiply used sidedefs...
+ ObjectsNeeded (OBJ_SIDEDEFS, 0);
+ for (n = 0; n < NumSideDefs; n++)
+ if (SideDefs[n].sector == objnum)
+ DeleteObject (Objid (OBJ_SIDEDEFS, n--));
+ else if (SideDefs[n].sector >= objnum)
+ SideDefs[n].sector--;
+ /* delete the sector */
+ ObjectsNeeded (OBJ_SECTORS, 0);
+ NumSectors--;
+ if (NumSectors > 0)
+ {
+ for (n = objnum; n < NumSectors; n++)
+ Sectors[n] = Sectors[n + 1];
+ Sectors = (SPtr) ResizeFarMemory (Sectors,
+ NumSectors * sizeof (struct Sector));
+ }
+ else
+ {
+ FreeFarMemory (Sectors);
+ Sectors = 0;
+ }
+ for (cur = (*list)->next; cur; cur = cur->next)
+ if (cur->objnum > objnum)
+ cur->objnum--;
+ next_sector:
+ UnSelectObject (list, objnum);
+ }
+ break;
+ default:
+ nf_bug ("DeleteObjects: bad objtype %d", (int) objtype);
+ }
+}
+
+
+
+/*
+ * InsertObject
+ * Insert a new object of type <objtype> at map coordinates
+ * (<xpos>, <ypos>).
+ *
+ * If <copyfrom> is a valid object number, the other properties
+ * of the new object are set from the properties of that object,
+ * with the exception of sidedef numbers, which are forced
+ * to OBJ_NO_NONE.
+ *
+ * The object is inserted at the exact coordinates given.
+ * No snapping to grid is done.
+ */
+void InsertObject (obj_type_t objtype, obj_no_t copyfrom, int xpos, int ypos)
+ /* SWAP! */
+{
+int last;
+
+ObjectsNeeded (objtype, 0);
+MadeChanges = 1;
+switch (objtype)
+ {
+ case OBJ_THINGS:
+ last = NumThings++;
+ if (last > 0)
+ Things = (TPtr) ResizeFarMemory (Things,
+ (unsigned long) NumThings * sizeof (struct Thing));
+ else
+ Things = (TPtr) GetFarMemory (sizeof (struct Thing));
+ Things[last].xpos = xpos;
+ Things[last].ypos = ypos;
+ things_angles++;
+ things_types++;
+ if (is_obj (copyfrom))
+ {
+ Things[last].type = Things[copyfrom].type;
+ Things[last].angle = Things[copyfrom].angle;
+ Things[last].when = Things[copyfrom].when;
+ Things[last].tid = Things[copyfrom].tid;
+ Things[last].height = Things[copyfrom].height;
+ Things[last].special = Things[copyfrom].special;
+ Things[last].arg1 = Things[copyfrom].arg1;
+ Things[last].arg2 = Things[copyfrom].arg2;
+ Things[last].arg3 = Things[copyfrom].arg3;
+ Things[last].arg4 = Things[copyfrom].arg4;
+ Things[last].arg5 = Things[copyfrom].arg5;
+ }
+ else
+ {
+ Things[last].type = default_thing;
+ Things[last].angle = 0;
+ Things[last].when = 0x07;
+ Things[last].tid = 0;
+ Things[last].height = 0;
+ Things[last].special = 0;
+ Things[last].arg1 = 0;
+ Things[last].arg2 = 0;
+ Things[last].arg3 = 0;
+ Things[last].arg4 = 0;
+ Things[last].arg5 = 0;
+ }
+ break;
+
+ case OBJ_VERTICES:
+ last = NumVertices++;
+ if (last > 0)
+ Vertices = (VPtr) ResizeFarMemory (Vertices,
+ (unsigned long) NumVertices * sizeof (struct Vertex));
+ else
+ Vertices = (VPtr) GetFarMemory (sizeof (struct Vertex));
+ Vertices[last].x = xpos;
+ Vertices[last].y = ypos;
+ if (Vertices[last].x < MapMinX)
+ MapMinX = Vertices[last].x;
+ if (Vertices[last].x > MapMaxX)
+ MapMaxX = Vertices[last].x;
+ if (Vertices[last].y < MapMinY)
+ MapMinY = Vertices[last].y;
+ if (Vertices[last].y > MapMaxY)
+ MapMaxY = Vertices[last].y;
+ MadeMapChanges = 1;
+ break;
+
+ case OBJ_LINEDEFS:
+ last = NumLineDefs++;
+ if (last > 0)
+ LineDefs = (LDPtr) ResizeFarMemory (LineDefs,
+ (unsigned long) NumLineDefs * sizeof (struct LineDef));
+ else
+ LineDefs = (LDPtr) GetFarMemory (sizeof (struct LineDef));
+ if (is_obj (copyfrom))
+ {
+ LineDefs[last].start = LineDefs[copyfrom].start;
+ LineDefs[last].end = LineDefs[copyfrom].end;
+ LineDefs[last].flags = LineDefs[copyfrom].flags;
+ LineDefs[last].type = LineDefs[copyfrom].type;
+ LineDefs[last].tag = LineDefs[copyfrom].tag;
+ LineDefs[last].arg2 = LineDefs[copyfrom].arg2;
+ LineDefs[last].arg3 = LineDefs[copyfrom].arg3;
+ LineDefs[last].arg4 = LineDefs[copyfrom].arg4;
+ LineDefs[last].arg5 = LineDefs[copyfrom].arg5;
+ }
+ else
+ {
+ LineDefs[last].start = 0;
+ LineDefs[last].end = NumVertices - 1;
+ LineDefs[last].flags = 1;
+ LineDefs[last].type = 0;
+ LineDefs[last].tag = 0;
+ LineDefs[last].arg2 = 0;
+ LineDefs[last].arg3 = 0;
+ LineDefs[last].arg4 = 0;
+ LineDefs[last].arg5 = 0;
+ }
+ LineDefs[last].sidedef1 = OBJ_NO_NONE;
+ LineDefs[last].sidedef2 = OBJ_NO_NONE;
+ break;
+
+ case OBJ_SIDEDEFS:
+ last = NumSideDefs++;
+ if (last > 0)
+ SideDefs = (SDPtr) ResizeFarMemory (SideDefs,
+ (unsigned long) NumSideDefs * sizeof (struct SideDef));
+ else
+ SideDefs = (SDPtr) GetFarMemory (sizeof (struct SideDef));
+ if (is_obj (copyfrom))
+ {
+ SideDefs[last].xoff = SideDefs[copyfrom].xoff;
+ SideDefs[last].yoff = SideDefs[copyfrom].yoff;
+ strncpy (SideDefs[last].tex1, SideDefs[copyfrom].tex1, WAD_TEX_NAME);
+ strncpy (SideDefs[last].tex2, SideDefs[copyfrom].tex2, WAD_TEX_NAME);
+ strncpy (SideDefs[last].tex3, SideDefs[copyfrom].tex3, WAD_TEX_NAME);
+ SideDefs[last].sector = SideDefs[copyfrom].sector;
+ }
+ else
+ {
+ SideDefs[last].xoff = 0;
+ SideDefs[last].yoff = 0;
+ strcpy (SideDefs[last].tex1, "-");
+ strcpy (SideDefs[last].tex2, "-");
+ strcpy (SideDefs[last].tex3, default_middle_texture);
+ SideDefs[last].sector = NumSectors - 1;
+ }
+ MadeMapChanges = 1;
+ break;
+
+ case OBJ_SECTORS:
+ last = NumSectors++;
+ if (last > 0)
+ Sectors = (SPtr) ResizeFarMemory (Sectors,
+ (unsigned long) NumSectors * sizeof (struct Sector));
+ else
+ Sectors = (SPtr) GetFarMemory (sizeof (struct Sector));
+ if (is_obj (copyfrom))
+ {
+ Sectors[last].floorh = Sectors[copyfrom].floorh;
+ Sectors[last].ceilh = Sectors[copyfrom].ceilh;
+ strncpy (Sectors[last].floort, Sectors[copyfrom].floort, WAD_FLAT_NAME);
+ strncpy (Sectors[last].ceilt, Sectors[copyfrom].ceilt, WAD_FLAT_NAME);
+ Sectors[last].light = Sectors[copyfrom].light;
+ Sectors[last].special = Sectors[copyfrom].special;
+ Sectors[last].tag = Sectors[copyfrom].tag;
+ }
+ else
+ {
+ Sectors[last].floorh = default_floor_height;
+ Sectors[last].ceilh = default_ceiling_height;
+ strncpy (Sectors[last].floort, default_floor_texture, WAD_FLAT_NAME);
+ strncpy (Sectors[last].ceilt, default_ceiling_texture, WAD_FLAT_NAME);
+ Sectors[last].light = default_light_level;
+ Sectors[last].special = 0;
+ Sectors[last].tag = 0;
+ }
+ break;
+
+ default:
+ nf_bug ("InsertObject: bad objtype %d", (int) objtype);
+ }
+}
+
+
+
+/*
+ check if a (part of a) LineDef is inside a given block
+*/
+bool IsLineDefInside (int ldnum, int x0, int y0, int x1, int y1) /* SWAP - needs Vertices & LineDefs */
+{
+int lx0 = Vertices[LineDefs[ldnum].start].x;
+int ly0 = Vertices[LineDefs[ldnum].start].y;
+int lx1 = Vertices[LineDefs[ldnum].end].x;
+int ly1 = Vertices[LineDefs[ldnum].end].y;
+int i;
+
+/* do you like mathematics? */
+if (lx0 >= x0 && lx0 <= x1 && ly0 >= y0 && ly0 <= y1)
+ return 1; /* the linedef start is entirely inside the square */
+if (lx1 >= x0 && lx1 <= x1 && ly1 >= y0 && ly1 <= y1)
+ return 1; /* the linedef end is entirely inside the square */
+if ((ly0 > y0) != (ly1 > y0))
+ {
+ i = lx0 + (int) ((long) (y0 - ly0) * (long) (lx1 - lx0) / (long) (ly1 - ly0));
+ if (i >= x0 && i <= x1)
+ return true; /* the linedef crosses the y0 side (left) */
+ }
+if ((ly0 > y1) != (ly1 > y1))
+ {
+ i = lx0 + (int) ((long) (y1 - ly0) * (long) (lx1 - lx0) / (long) (ly1 - ly0));
+ if (i >= x0 && i <= x1)
+ return true; /* the linedef crosses the y1 side (right) */
+ }
+if ((lx0 > x0) != (lx1 > x0))
+ {
+ i = ly0 + (int) ((long) (x0 - lx0) * (long) (ly1 - ly0) / (long) (lx1 - lx0));
+ if (i >= y0 && i <= y1)
+ return true; /* the linedef crosses the x0 side (down) */
+ }
+if ((lx0 > x1) != (lx1 > x1))
+ {
+ i = ly0 + (int) ((long) (x1 - lx0) * (long) (ly1 - ly0) / (long) (lx1 - lx0));
+ if (i >= y0 && i <= y1)
+ return true; /* the linedef crosses the x1 side (up) */
+ }
+return false;
+}
+
+
+
+/*
+ get the sector number of the sidedef opposite to this sidedef
+ (returns -1 if it cannot be found)
+*/
+int GetOppositeSector (int ld1, bool firstside) /* SWAP! */
+{
+int x0, y0, dx0, dy0;
+int x1, y1, dx1, dy1;
+int x2, y2, dx2, dy2;
+int ld2, dist;
+int bestld, bestdist, bestmdist;
+
+/* get the coords for this LineDef */
+ObjectsNeeded (OBJ_LINEDEFS, OBJ_VERTICES, 0);
+x0 = Vertices[LineDefs[ld1].start].x;
+y0 = Vertices[LineDefs[ld1].start].y;
+dx0 = Vertices[LineDefs[ld1].end].x - x0;
+dy0 = Vertices[LineDefs[ld1].end].y - y0;
+
+/* find the normal vector for this LineDef */
+x1 = (dx0 + x0 + x0) / 2;
+y1 = (dy0 + y0 + y0) / 2;
+if (firstside)
+ {
+ dx1 = dy0;
+ dy1 = -dx0;
+ }
+else
+ {
+ dx1 = -dy0;
+ dy1 = dx0;
+ }
+
+bestld = -1;
+/* use a parallel to an axis instead of the normal vector (faster method) */
+if (abs (dy1) > abs (dx1))
+ {
+ if (dy1 > 0)
+ {
+ /* get the nearest LineDef in that direction (increasing Y's: North) */
+ bestdist = 32767;
+ bestmdist = 32767;
+ for (ld2 = 0; ld2 < NumLineDefs; ld2++)
+ if (ld2 != ld1 && ((Vertices[LineDefs[ld2].start].x > x1)
+ != (Vertices[LineDefs[ld2].end].x > x1)))
+ {
+ x2 = Vertices[LineDefs[ld2].start].x;
+ y2 = Vertices[LineDefs[ld2].start].y;
+ dx2 = Vertices[LineDefs[ld2].end].x - x2;
+ dy2 = Vertices[LineDefs[ld2].end].y - y2;
+ dist = y2 + (int) ((long) (x1 - x2) * (long) dy2 / (long) dx2);
+ if (dist > y1 && (dist < bestdist
+ || (dist == bestdist && (y2 + dy2 / 2) < bestmdist)))
+ {
+ bestld = ld2;
+ bestdist = dist;
+ bestmdist = y2 + dy2 / 2;
+ }
+ }
+ }
+ else
+ {
+ /* get the nearest LineDef in that direction (decreasing Y's: South) */
+ bestdist = -32767;
+ bestmdist = -32767;
+ for (ld2 = 0; ld2 < NumLineDefs; ld2++)
+ if (ld2 != ld1 && ((Vertices[LineDefs[ld2].start].x > x1)
+ != (Vertices[LineDefs[ld2].end].x > x1)))
+ {
+ x2 = Vertices[LineDefs[ld2].start].x;
+ y2 = Vertices[LineDefs[ld2].start].y;
+ dx2 = Vertices[LineDefs[ld2].end].x - x2;
+ dy2 = Vertices[LineDefs[ld2].end].y - y2;
+ dist = y2 + (int) ((long) (x1 - x2) * (long) dy2 / (long) dx2);
+ if (dist < y1 && (dist > bestdist
+ || (dist == bestdist && (y2 + dy2 / 2) > bestmdist)))
+ {
+ bestld = ld2;
+ bestdist = dist;
+ bestmdist = y2 + dy2 / 2;
+ }
+ }
+ }
+ }
+else
+ {
+ if (dx1 > 0)
+ {
+ /* get the nearest LineDef in that direction (increasing X's: East) */
+ bestdist = 32767;
+ bestmdist = 32767;
+ for (ld2 = 0; ld2 < NumLineDefs; ld2++)
+ if (ld2 != ld1 && ((Vertices[LineDefs[ld2].start].y > y1)
+ != (Vertices[LineDefs[ld2].end].y > y1)))
+ {
+ x2 = Vertices[LineDefs[ld2].start].x;
+ y2 = Vertices[LineDefs[ld2].start].y;
+ dx2 = Vertices[LineDefs[ld2].end].x - x2;
+ dy2 = Vertices[LineDefs[ld2].end].y - y2;
+ dist = x2 + (int) ((long) (y1 - y2) * (long) dx2 / (long) dy2);
+ if (dist > x1 && (dist < bestdist
+ || (dist == bestdist && (x2 + dx2 / 2) < bestmdist)))
+ {
+ bestld = ld2;
+ bestdist = dist;
+ bestmdist = x2 + dx2 / 2;
+ }
+ }
+ }
+ else
+ {
+ /* get the nearest LineDef in that direction (decreasing X's: West) */
+ bestdist = -32767;
+ bestmdist = -32767;
+ for (ld2 = 0; ld2 < NumLineDefs; ld2++)
+ if (ld2 != ld1 && ((Vertices[LineDefs[ld2].start].y > y1)
+ != (Vertices[LineDefs[ld2].end].y > y1)))
+ {
+ x2 = Vertices[LineDefs[ld2].start].x;
+ y2 = Vertices[LineDefs[ld2].start].y;
+ dx2 = Vertices[LineDefs[ld2].end].x - x2;
+ dy2 = Vertices[LineDefs[ld2].end].y - y2;
+ dist = x2 + (int) ((long) (y1 - y2) * (long) dx2 / (long) dy2);
+ if (dist < x1 && (dist > bestdist
+ || (dist == bestdist && (x2 + dx2 / 2) > bestmdist)))
+ {
+ bestld = ld2;
+ bestdist = dist;
+ bestmdist = x2 + dx2 / 2;
+ }
+ }
+ }
+ }
+
+/* no intersection: the LineDef was pointing outwards! */
+if (bestld < 0)
+ return -1;
+
+/* now look if this LineDef has a SideDef bound to one sector */
+if (abs (dy1) > abs (dx1))
+ {
+ if ((Vertices[LineDefs[bestld].start].x
+ < Vertices[LineDefs[bestld].end].x) == (dy1 > 0))
+ x0 = LineDefs[bestld].sidedef1;
+ else
+ x0 = LineDefs[bestld].sidedef2;
+ }
+else
+ {
+ if ((Vertices[LineDefs[bestld].start].y
+ < Vertices[LineDefs[bestld].end].y) != (dx1 > 0))
+ x0 = LineDefs[bestld].sidedef1;
+ else
+ x0 = LineDefs[bestld].sidedef2;
+ }
+
+/* there is no SideDef on this side of the LineDef! */
+if (x0 < 0)
+ return -1;
+
+/* OK, we got it -- return the Sector number */
+ObjectsNeeded (OBJ_SIDEDEFS, 0);
+return SideDefs[x0].sector;
+}
+
+
+
+/*
+ copy a group of objects to a new position
+*/
+void CopyObjects (int objtype, SelPtr obj) /* SWAP! */
+{
+int n, m;
+SelPtr cur;
+SelPtr list1, list2;
+SelPtr ref1, ref2;
+
+if (! obj)
+ return;
+ObjectsNeeded (objtype, 0);
+/* copy the object(s) */
+switch (objtype)
+ {
+ case OBJ_THINGS:
+ for (cur = obj; cur; cur = cur->next)
+ {
+ InsertObject (OBJ_THINGS, cur->objnum, Things[cur->objnum].xpos,
+ Things[cur->objnum].ypos);
+ cur->objnum = NumThings - 1;
+ }
+ MadeChanges = 1;
+ break;
+
+ case OBJ_VERTICES:
+ for (cur = obj; cur; cur = cur->next)
+ {
+ InsertObject (OBJ_VERTICES, cur->objnum, Vertices[cur->objnum].x,
+ Vertices[cur->objnum].y);
+ cur->objnum = NumVertices - 1;
+ }
+ MadeChanges = 1;
+ MadeMapChanges = 1;
+ break;
+
+ case OBJ_LINEDEFS:
+ list1 = 0;
+ list2 = 0;
+
+ // Create the linedefs and maybe the sidedefs
+ for (cur = obj; cur; cur = cur->next)
+ {
+ int old = cur->objnum; // No. of original linedef
+ int New; // No. of duplicate linedef
+
+ InsertObject (OBJ_LINEDEFS, old, 0, 0);
+ New = NumLineDefs - 1;
+
+ if (copy_linedef_reuse_sidedefs)
+ {
+ /* AYM 1997-07-25: not very orthodox (the New linedef and
+ the old one use the same sidedefs). but, in the case where
+ you're copying into the same sector, it's much better than
+ having to create the New sidedefs manually. plus it saves
+ space in the .wad and also it makes editing easier (editing
+ one sidedef impacts all linedefs that use it). */
+ LineDefs[New].sidedef1 = LineDefs[old].sidedef1;
+ LineDefs[New].sidedef2 = LineDefs[old].sidedef2;
+ }
+ else
+ {
+ /* AYM 1998-11-08: duplicate sidedefs too.
+ DEU 5.21 just left the sidedef references to -1. */
+ if (is_sidedef (LineDefs[old].sidedef1))
+ {
+ InsertObject (OBJ_SIDEDEFS, LineDefs[old].sidedef1, 0, 0);
+ LineDefs[New].sidedef1 = NumSideDefs - 1;
+ }
+ if (is_sidedef (LineDefs[old].sidedef2))
+ {
+ InsertObject (OBJ_SIDEDEFS, LineDefs[old].sidedef2, 0, 0);
+ LineDefs[New].sidedef2 = NumSideDefs - 1;
+ }
+ }
+ cur->objnum = New;
+ if (!IsSelected (list1, LineDefs[New].start))
+ {
+ SelectObject (&list1, LineDefs[New].start);
+ SelectObject (&list2, LineDefs[New].start);
+ }
+ if (!IsSelected (list1, LineDefs[New].end))
+ {
+ SelectObject (&list1, LineDefs[New].end);
+ SelectObject (&list2, LineDefs[New].end);
+ }
+ }
+
+ // Create the vertices
+ CopyObjects (OBJ_VERTICES, list2);
+ ObjectsNeeded (OBJ_LINEDEFS, 0);
+
+ // Update the references to the vertices
+ for (ref1 = list1, ref2 = list2;
+ ref1 && ref2;
+ ref1 = ref1->next, ref2 = ref2->next)
+ {
+ for (cur = obj; cur; cur = cur->next)
+ {
+ if (ref1->objnum == LineDefs[cur->objnum].start)
+ LineDefs[cur->objnum].start = ref2->objnum;
+ if (ref1->objnum == LineDefs[cur->objnum].end)
+ LineDefs[cur->objnum].end = ref2->objnum;
+ }
+ }
+ ForgetSelection (&list1);
+ ForgetSelection (&list2);
+ break;
+
+ case OBJ_SECTORS:
+ ObjectsNeeded (OBJ_LINEDEFS, OBJ_SIDEDEFS, 0);
+ list1 = 0;
+ list2 = 0;
+ // Create the linedefs (and vertices)
+ for (cur = obj; cur; cur = cur->next)
+ {
+ for (n = 0; n < NumLineDefs; n++)
+ if ((((m = LineDefs[n].sidedef1) >= 0
+ && SideDefs[m].sector == cur->objnum)
+ || ((m = LineDefs[n].sidedef2) >= 0
+ && SideDefs[m].sector == cur->objnum))
+ && ! IsSelected (list1, n))
+ {
+ SelectObject (&list1, n);
+ SelectObject (&list2, n);
+ }
+ }
+ CopyObjects (OBJ_LINEDEFS, list2);
+ /* create the sidedefs */
+ ObjectsNeeded (OBJ_LINEDEFS, 0);
+ for (ref1 = list1, ref2 = list2;
+ ref1 && ref2;
+ ref1 = ref1->next, ref2 = ref2->next)
+ {
+ if ((n = LineDefs[ref1->objnum].sidedef1) >= 0)
+ {
+ InsertObject (OBJ_SIDEDEFS, n, 0, 0);
+ n = NumSideDefs - 1;
+ ObjectsNeeded (OBJ_LINEDEFS, 0);
+ LineDefs[ref2->objnum].sidedef1 = n;
+ }
+ if ((m = LineDefs[ref1->objnum].sidedef2) >= 0)
+ {
+ InsertObject (OBJ_SIDEDEFS, m, 0, 0);
+ m = NumSideDefs - 1;
+ ObjectsNeeded (OBJ_LINEDEFS, 0);
+ LineDefs[ref2->objnum].sidedef2 = m;
+ }
+ ref1->objnum = n;
+ ref2->objnum = m;
+ }
+ /* create the Sectors */
+ for (cur = obj; cur; cur = cur->next)
+ {
+ InsertObject (OBJ_SECTORS, cur->objnum, 0, 0);
+ ObjectsNeeded (OBJ_SIDEDEFS, 0);
+ for (ref1 = list1, ref2 = list2;
+ ref1 && ref2;
+ ref1 = ref1->next, ref2 = ref2->next)
+ {
+ if (ref1->objnum >= 0
+ && SideDefs[ref1->objnum].sector == cur->objnum)
+ SideDefs[ref1->objnum].sector = NumSectors - 1;
+ if (ref2->objnum >= 0
+ && SideDefs[ref2->objnum].sector == cur->objnum)
+ SideDefs[ref2->objnum].sector = NumSectors - 1;
+ }
+ cur->objnum = NumSectors - 1;
+ }
+ ForgetSelection (&list1);
+ ForgetSelection (&list2);
+ break;
+ }
+}
+
+
+
+/*
+ * MoveObjectsToCoords
+ * Move a group of objects to a new position
+ *
+ * You must first call it with obj == NULL and newx and newy
+ * set to the coordinates of the reference point (E.G. the
+ * object being dragged).
+ * Then, every time the object being dragged has changed its
+ * coordinates, call the it again with newx and newy set to
+ * the new position and obj set to the selection.
+ *
+ * Returns <>0 iff an object was moved.
+ */
+bool MoveObjectsToCoords (
+ int objtype,
+ SelPtr obj,
+ int newx,
+ int newy,
+ int grid) /* SWAP! */
+{
+int dx, dy;
+SelPtr cur, vertices;
+static int refx, refy; /* previous position */
+
+ObjectsNeeded (objtype, 0);
+if (grid > 0)
+ {
+ newx = (newx + grid / 2) & ~(grid - 1);
+ newy = (newy + grid / 2) & ~(grid - 1);
+ }
+
+// Only update the reference point ?
+if (! obj)
+ {
+ refx = newx;
+ refy = newy;
+ return true;
+ }
+
+/* compute the displacement */
+dx = newx - refx;
+dy = newy - refy;
+/* nothing to do? */
+if (dx == 0 && dy == 0)
+ return false;
+
+/* move the object(s) */
+switch (objtype)
+ {
+ case OBJ_THINGS:
+ for (cur = obj; cur; cur = cur->next)
+ {
+ Things[cur->objnum].xpos += dx;
+ Things[cur->objnum].ypos += dy;
+ }
+ refx = newx;
+ refy = newy;
+ MadeChanges = 1;
+ break;
+
+ case OBJ_VERTICES:
+ for (cur = obj; cur; cur = cur->next)
+ {
+ Vertices[cur->objnum].x += dx;
+ Vertices[cur->objnum].y += dy;
+ }
+ refx = newx;
+ refy = newy;
+ MadeChanges = 1;
+ MadeMapChanges = 1;
+ break;
+
+ case OBJ_LINEDEFS:
+ vertices = list_vertices_of_linedefs (obj);
+ MoveObjectsToCoords (OBJ_VERTICES, vertices, newx, newy, grid);
+ ForgetSelection (&vertices);
+ break;
+
+ case OBJ_SECTORS:
+ ObjectsNeeded (OBJ_LINEDEFS, OBJ_SIDEDEFS, 0);
+ vertices = list_vertices_of_sectors (obj);
+ MoveObjectsToCoords (OBJ_VERTICES, vertices, newx, newy, grid);
+ ForgetSelection (&vertices);
+ break;
+ }
+return true;
+}
+
+
+
+/*
+ get the coordinates (approx.) of an object
+*/
+void GetObjectCoords (int objtype, int objnum, int *xpos, int *ypos) /* SWAP! */
+{
+int n, v1, v2, sd1, sd2;
+long accx, accy, num;
+
+switch (objtype)
+ {
+ case OBJ_THINGS:
+ if (! is_thing (objnum)) // Can't happen
+ {
+ nf_bug ("GetObjectCoords: bad thing# %d", objnum);
+ *xpos = 0;
+ *ypos = 0;
+ return;
+ }
+ ObjectsNeeded (OBJ_THINGS, 0);
+ *xpos = Things[objnum].xpos;
+ *ypos = Things[objnum].ypos;
+ break;
+
+ case OBJ_VERTICES:
+ if (! is_vertex (objnum)) // Can't happen
+ {
+ nf_bug ("GetObjectCoords: bad vertex# %d", objnum);
+ *xpos = 0;
+ *ypos = 0;
+ return;
+ }
+ ObjectsNeeded (OBJ_VERTICES, 0);
+ *xpos = Vertices[objnum].x;
+ *ypos = Vertices[objnum].y;
+ break;
+
+ case OBJ_LINEDEFS:
+ if (! is_linedef (objnum)) // Can't happen
+ {
+ nf_bug ("GetObjectCoords: bad linedef# %d", objnum);
+ *xpos = 0;
+ *ypos = 0;
+ return;
+ }
+ ObjectsNeeded (OBJ_LINEDEFS, 0);
+ v1 = LineDefs[objnum].start;
+ v2 = LineDefs[objnum].end;
+ ObjectsNeeded (OBJ_VERTICES, 0);
+ *xpos = (Vertices[v1].x + Vertices[v2].x) / 2;
+ *ypos = (Vertices[v1].y + Vertices[v2].y) / 2;
+ break;
+
+ case OBJ_SIDEDEFS:
+ if (! is_sidedef (objnum)) // Can't happen
+ {
+ nf_bug ("GetObjectCoords: bad sidedef# %d", objnum);
+ *xpos = 0;
+ *ypos = 0;
+ return;
+ }
+ ObjectsNeeded (OBJ_LINEDEFS, 0);
+ for (n = 0; n < NumLineDefs; n++)
+ if (LineDefs[n].sidedef1 == objnum || LineDefs[n].sidedef2 == objnum)
+ {
+ v1 = LineDefs[n].start;
+ v2 = LineDefs[n].end;
+ ObjectsNeeded (OBJ_VERTICES, 0);
+ *xpos = (Vertices[v1].x + Vertices[v2].x) / 2;
+ *ypos = (Vertices[v1].y + Vertices[v2].y) / 2;
+ return;
+ }
+ *xpos = (MapMinX + MapMaxX) / 2;
+ *ypos = (MapMinY + MapMaxY) / 2;
+ // FIXME is the fall through intentional ? -- AYM 2000-11-08
+
+ case OBJ_SECTORS:
+ if (! is_sector (objnum)) // Can't happen
+ {
+ nf_bug ("GetObjectCoords: bad sector# %d", objnum);
+ *xpos = 0;
+ *ypos = 0;
+ return;
+ }
+ accx = 0L;
+ accy = 0L;
+ num = 0L;
+ for (n = 0; n < NumLineDefs; n++)
+ {
+ ObjectsNeeded (OBJ_LINEDEFS, 0);
+ sd1 = LineDefs[n].sidedef1;
+ sd2 = LineDefs[n].sidedef2;
+ v1 = LineDefs[n].start;
+ v2 = LineDefs[n].end;
+ ObjectsNeeded (OBJ_SIDEDEFS, 0);
+ if ((sd1 >= 0 && SideDefs[sd1].sector == objnum)
+ || (sd2 >= 0 && SideDefs[sd2].sector == objnum))
+ {
+ ObjectsNeeded (OBJ_VERTICES, 0);
+ /* if the Sector is closed, all Vertices will be counted twice */
+ accx += (long) Vertices[v1].x;
+ accy += (long) Vertices[v1].y;
+ num++;
+ accx += (long) Vertices[v2].x;
+ accy += (long) Vertices[v2].y;
+ num++;
+ }
+ }
+ if (num > 0)
+ {
+ *xpos = (int) ((accx + num / 2L) / num);
+ *ypos = (int) ((accy + num / 2L) / num);
+ }
+ else
+ {
+ *xpos = (MapMinX + MapMaxX) / 2;
+ *ypos = (MapMinY + MapMaxY) / 2;
+ }
+ break;
+
+ default:
+ nf_bug ("GetObjectCoords: bad objtype %d", objtype); // Can't happen
+ *xpos = 0;
+ *ypos = 0;
+ }
+}
+
+
+
+/*
+ find a free tag number
+*/
+int FindFreeTag () /* SWAP! */
+{
+int tag, n;
+bool ok;
+
+ObjectsNeeded (OBJ_LINEDEFS, OBJ_SECTORS, 0);
+tag = 1;
+ok = false;
+while (! ok)
+ {
+ ok = true;
+ for (n = 0; n < NumLineDefs; n++)
+ if (LineDefs[n].tag == tag)
+ {
+ ok = false;
+ break;
+ }
+ if (ok)
+ for (n = 0; n < NumSectors; n++)
+ if (Sectors[n].tag == tag)
+ {
+ ok = false;
+ break;
+ }
+ tag++;
+ }
+return tag - 1;
+}
+
+
diff -Naur yadex-1.7.0.orig/src/sanity.cc yadex-1.7.0.allpatches/src/sanity.cc
--- yadex-1.7.0.orig/src/sanity.cc 2003-03-28 13:37:32.000000000 +0100
+++ yadex-1.7.0.allpatches/src/sanity.cc 2013-01-10 17:25:53.231909795 +0100
@@ -67,10 +67,10 @@
assert_size (i16, 2);
assert_size (u32, 4);
assert_size (i32, 4);
- assert_size (struct LineDef, 14);
+ assert_size (struct LineDef, 18);
assert_size (struct Sector, 26);
assert_size (struct SideDef, 30);
- assert_size (struct Thing, 10);
+ assert_size (struct Thing, 20);
assert_size (struct Vertex, 4);
assert_wrap (u8, 255, 0);
assert_wrap (i8, 127, -128);
diff -Naur yadex-1.7.0.orig/src/t_prop.cc yadex-1.7.0.allpatches/src/t_prop.cc
--- yadex-1.7.0.orig/src/t_prop.cc 2013-01-10 17:24:11.560024040 +0100
+++ yadex-1.7.0.allpatches/src/t_prop.cc 2013-01-10 17:25:53.234909821 +0100
@@ -47,6 +47,7 @@
static const char *PrintThinggroup (void *ptr);
static const char *PrintThingdef (void *ptr);
int InputThingType (int x0, int y0, int *number);
+int InputLinedefType (int x0, int y0, int *number);
/*
@@ -61,9 +62,9 @@
SelPtr cur;
int subwin_y0;
-for (n = 0; n < 6; n++)
+for (n = 0; n < 14; n++)
menustr[n] = (char *) GetMemory (60);
-sprintf (menustr[5], "Edit thing #%d", obj->objnum);
+sprintf (menustr[13], "Edit thing #%d", obj->objnum);
sprintf (menustr[0], "Change type (Current: %s)",
get_thing_name (Things[obj->objnum].type));
sprintf (menustr[1], "Change angle (Current: %s)",
@@ -74,14 +75,47 @@
Things[obj->objnum].xpos);
sprintf (menustr[4], "Change Y position (Current: %d)",
Things[obj->objnum].ypos);
-val = vDisplayMenu (x0, y0, menustr[5],
+sprintf (menustr[5], "Change Z position (Current: %d)",
+ Things[obj->objnum].height);
+sprintf (menustr[6], "Change TID (Current: %d)",
+ Things[obj->objnum].tid);
+sprintf (menustr[7], "Change special (Current: %d)",
+ Things[obj->objnum].special);
+sprintf (menustr[8], "Change arg1 (Current: %d)",
+ Things[obj->objnum].arg1);
+sprintf (menustr[9], "Change arg2 (Current: %d)",
+ Things[obj->objnum].arg2);
+sprintf (menustr[10], "Change arg3 (Current: %d)",
+ Things[obj->objnum].arg3);
+sprintf (menustr[11], "Change arg4 (Current: %d)",
+ Things[obj->objnum].arg4);
+sprintf (menustr[12], "Change arg5 (Current: %d)",
+ Things[obj->objnum].arg5);
+if (yg_level_format == YGLF_HEXEN) // Hexen mode
+val = vDisplayMenu (x0, y0, menustr[13],
menustr[0], YK_, 0,
menustr[1], YK_, 0,
menustr[2], YK_, 0,
menustr[3], YK_, 0,
menustr[4], YK_, 0,
+ menustr[5], YK_, 0,
+ menustr[6], YK_, 0,
+ menustr[7], YK_, 0,
+ menustr[8], YK_, 0,
+ menustr[9], YK_, 0,
+ menustr[10], YK_, 0,
+ menustr[11], YK_, 0,
+ menustr[12], YK_, 0,
NULL);
-for (n = 0; n < 6; n++)
+else
+val = vDisplayMenu (x0, y0, menustr[13],
+ menustr[0], YK_, 0,
+ menustr[1], YK_, 0,
+ menustr[2], YK_, 0,
+ menustr[3], YK_, 0,
+ menustr[4], YK_, 0,
+ NULL);
+for (n = 0; n < 14; n++)
FreeMemory (menustr[n]);
subwin_y0 = y0 + BOX_BORDER + (2 + val) * FONTH;
switch (val)
@@ -242,6 +276,93 @@
MadeChanges = 1;
}
break;
+
+ case 6:
+ val = InputIntegerValue (x0 + 42, subwin_y0, -32768, 32767,
+ Things[obj->objnum].height);
+ if (val != IIV_CANCEL)
+ {
+ n = val - Things[obj->objnum].height;
+ for (cur = obj; cur; cur = cur->next)
+ Things[cur->objnum].height += n;
+ MadeChanges = 1;
+ }
+ break;
+
+ case 7:
+ val = InputIntegerValue (x0 + 42, subwin_y0, -32768, 32767,
+ Things[obj->objnum].tid);
+ if (val != IIV_CANCEL)
+ {
+ for (cur = obj; cur; cur = cur->next)
+ Things[cur->objnum].tid = val;
+ MadeChanges = 1;
+ }
+ break;
+
+ case 8:
+ if (! InputLinedefType (x0 + 42, subwin_y0, &val))
+ {
+ for (cur = obj; cur; cur = cur->next)
+ Things[cur->objnum].special = val;
+ MadeChanges = 1;
+ }
+ break;
+
+ case 9:
+ val = InputIntegerValue (x0 + 42, subwin_y0, 0, 255,
+ Things[obj->objnum].arg1);
+ if (val != IIV_CANCEL)
+ {
+ for (cur = obj; cur; cur = cur->next)
+ Things[cur->objnum].arg1 = val;
+ MadeChanges = 1;
+ }
+ break;
+
+ case 10:
+ val = InputIntegerValue (x0 + 42, subwin_y0, 0, 255,
+ Things[obj->objnum].arg2);
+ if (val != IIV_CANCEL)
+ {
+ for (cur = obj; cur; cur = cur->next)
+ Things[cur->objnum].arg2 = val;
+ MadeChanges = 1;
+ }
+ break;
+
+ case 11:
+ val = InputIntegerValue (x0 + 42, subwin_y0, 0, 255,
+ Things[obj->objnum].arg3);
+ if (val != IIV_CANCEL)
+ {
+ for (cur = obj; cur; cur = cur->next)
+ Things[cur->objnum].arg3 = val;
+ MadeChanges = 1;
+ }
+ break;
+
+ case 12:
+ val = InputIntegerValue (x0 + 42, subwin_y0, 0, 255,
+ Things[obj->objnum].arg4);
+ if (val != IIV_CANCEL)
+ {
+ for (cur = obj; cur; cur = cur->next)
+ Things[cur->objnum].arg4 = val;
+ MadeChanges = 1;
+ }
+ break;
+
+ case 13:
+ val = InputIntegerValue (x0 + 42, subwin_y0, 0, 255,
+ Things[obj->objnum].arg5);
+ if (val != IIV_CANCEL)
+ {
+ for (cur = obj; cur; cur = cur->next)
+ Things[cur->objnum].arg5 = val;
+ MadeChanges = 1;
+ }
+ break;
}
}
diff -Naur yadex-1.7.0.orig/src/wstructs.h yadex-1.7.0.allpatches/src/wstructs.h
--- yadex-1.7.0.orig/src/wstructs.h 2003-03-28 13:37:32.000000000 +0100
+++ yadex-1.7.0.allpatches/src/wstructs.h 2013-01-10 17:25:53.235909830 +0100
@@ -84,14 +84,30 @@
typedef i16 wad_tflags_t;
struct Thing
{
+ i16 tid;
wad_coord_t xpos; // FIXME rename to "x"
wad_coord_t ypos; // FIXME rename to "y"
+ wad_z_t height;
wad_tangle_t angle;
wad_ttype_t type;
wad_tflags_t when; // FIXME rename to "flags"
+ u8 special;
+ u8 arg1;
+ u8 arg2;
+ u8 arg3;
+ u8 arg4;
+ u8 arg5;
};
typedef struct
{
+ wad_coord_t xpos; // FIXME rename to "x"
+ wad_coord_t ypos; // FIXME rename to "y"
+ wad_tangle_t angle;
+ wad_ttype_t type;
+ wad_tflags_t when; // FIXME rename to "flags"
+} wad_doom_thing_t;
+typedef struct
+{
i16 tid;
wad_coord_t x;
wad_coord_t y;
@@ -121,11 +137,25 @@
wad_ldflags_t flags;
wad_ldtype_t type;
wad_tag_t tag;
+ u8 arg2;
+ u8 arg3;
+ u8 arg4;
+ u8 arg5;
wad_sdn_t sidedef1; // # of first (right) sidedef
wad_sdn_t sidedef2; // # of second (left) sidedef or 0xffff
};
typedef struct
{
+ wad_vn_t start; // # of start vertex
+ wad_vn_t end; // # of end vertex
+ wad_ldflags_t flags;
+ wad_ldtype_t type;
+ wad_tag_t tag;
+ wad_sdn_t sidedef1; // # of first (right) sidedef
+ wad_sdn_t sidedef2; // # of second (left) sidedef or 0xffff
+} wad_doom_linedef_t;
+typedef struct
+{
wad_vn_t start;
wad_vn_t end;
wad_ldflags_t flags;
@@ -197,8 +227,11 @@
WAD_LL_SECTORS,
WAD_LL_REJECT,
WAD_LL_BLOCKMAP,
- // Hexen has a BEHAVIOR lump here
- WAD_LL__
+ WAD_LL_BEHAVIOR,
+
+ WAD_LL__MAX,
+ WAD_LL__HEXEN = WAD_LL__MAX,
+ WAD_LL__DOOM = WAD_LL_BEHAVIOR
} wad_level_lump_no_t;
typedef struct
@@ -207,7 +240,7 @@
size_t item_size;
} wad_level_lump_def_t;
-const wad_level_lump_def_t wad_level_lump[WAD_LL__] =
+const wad_level_lump_def_t wad_level_lump[WAD_LL__MAX] =
{
{ 0, 0 }, // Label -- no fixed name
{ "THINGS", WAD_THING_BYTES },
@@ -219,8 +252,8 @@
{ "NODES", 0 },
{ "SECTORS", WAD_SECTOR_BYTES },
{ "REJECT", 0 },
- { "BLOCKMAP", 0 }
- // Hexen has a BEHAVIOR lump here
+ { "BLOCKMAP", 0 },
+ { "BEHAVIOR", 0 }
};
diff -Naur yadex-1.7.0.orig/ygd/hexen.ygd yadex-1.7.0.allpatches/ygd/hexen.ygd
--- yadex-1.7.0.orig/ygd/hexen.ygd 2002-12-31 03:53:33.000000000 +0100
+++ yadex-1.7.0.allpatches/ygd/hexen.ygd 2013-01-10 17:25:53.237909848 +0100
@@ -108,6 +108,7 @@
ldt 101 x "?? Scroll right" "?? Scroll_Texture_Right"
ldt 102 x "?? Scroll up" "?? Scroll_Texture_Up"
ldt 103 x "?? Scroll down" "?? Scroll_Texture_Down"
+ldt 109 L "?? Force lightng" "?? Light_ForceLightning"
ldt 110 L "?? Raise light" "?? Light_RaiseByValue"
ldt 111 L "?? Lower light" "?? Light_LowerByValue"
ldt 112 L "?? Set light" "?? Light_ChangeToValue"
@@ -136,25 +137,12 @@
#
st 0 " Normal" "Normal"
-st 1 " Phased light" "light phased" # FIXME
-st 2 " Light start" "lightsequencestart" # FIXME
-st 3 " Light specl1" "lightsequencespecial1" # FIXME
-st 4 " Light specl2" "lightsequencespecial2" # FIXME
-st 9 " Secret" "Sector counts toward secret count"
+st 1 " Phased light" "light phased"
+st 2 " Light start" "lightsequencestart"
+st 3 " Light specl1" "lightsequencespecial1"
+st 4 " Light specl2" "lightsequencespecial2"
st 26 " Stairs1" "Stairs special 1"
st 27 " Stairs2" "Stairs special 2"
-st 40 " Wind east 1" "Wind east force 1"
-st 41 " Wind east 2" "Wind east force 2"
-st 42 " Wind east 3" "Wind east force 3"
-st 43 " Wind north 1" "Wind north force 1"
-st 44 " Wind north 2" "Wind north force 2"
-st 45 " Wind north 3" "Wind north force 3"
-st 46 " Wind south 1" "Wind south force 1"
-st 47 " Wind south 2" "Wind south force 2"
-st 48 " Wind south 3" "Wind south force 3"
-st 49 " Wind west 1" "Wind west force 1"
-st 50 " Wind west 2" "Wind west force 2"
-st 51 " Wind west 3" "Wind west force 3"
st 198 " Lightning 64" "Indoor lightning, +64 units"
st 199 " Lightning 32" "Indoor lightning, +32 units"
st 200 " Sky2" "Use MAPINFO sky2"
@@ -191,17 +179,21 @@
thinggroup p rgb:4/f/4 "Player"
thinggroup m rgb:f/0/0 "Monster"
thinggroup w rgb:f/a/0 "Weapon"
-thinggroup a rgb:8/5/0 "Ammunition"
+thinggroup a rgb:8/5/0 "Mana"
thinggroup h rgb:2/8/0 "Health & armour"
+thinggroup A rgb:2/8/0 "Artifacts"
+thinggroup q rgb:2/8/0 "Quest items"
thinggroup b rgb:2/8/0 "Misc. bonus"
thinggroup k rgb:f/0/f "Key"
thinggroup P rgb:6/6/c "Plants"
+thinggroup t rgb:6/6/c "Stalagm. & stalact."
+thinggroup g rgb:6/6/c "Gargoyle statues"
+thinggroup T rgb:6/6/c "Table stuff"
+thinggroup D rgb:6/6/c "Dungeon stuff"
thinggroup d rgb:6/6/c "Misc. decoration"
thinggroup l rgb:6/6/c "Light source"
-#thinggroup g rgb:6/6/c "Gory decoration"
-#thinggroup c rgb:6/6/c "Corpse"
-thinggroup e rgb:0/b/d "Environment sound"
-thinggroup s rgb:0/b/d "Ambient sound"
+thinggroup s rgb:0/b/d "Sound"
+thinggroup S rgb:0/b/d "Special"
#
# Definition of things
@@ -210,56 +202,268 @@
# <desc> must not exceed 19 characters.
#
-thing 1 p - 16 "Player 1 start" PLAY
-thing 2 p - 16 "Player 2 start" PLAY
-thing 3 p - 16 "Player 3 start" PLAY
-thing 4 p - 16 "Player 4 start" PLAY
+thing 1 p - 16 "Player 1 start *" PLAY
+thing 2 p - 16 "Player 2 start *" PLAY
+thing 3 p - 16 "Player 3 start *" PLAY
+thing 4 p - 16 "Player 4 start *" PLAY
thing 11 p - 16 "Deathmatch start" PLAYF1
thing 14 p - 16 "Teleport exit" TELE
+thing 9100 p - 16 "Player 5 start *" PLAY
+thing 9101 p - 16 "Player 6 start *" PLAY
+thing 9102 p - 16 "Player 7 start *" PLAY
+thing 9103 p - 16 "Player 8 start *" PLAY
-thing 31 m - 32 "Demon" DEMN
+thing 31 m - 32 "Chaos serpent" DEMN
+thing 34 m - 40 "Reiver" WRTH
thing 107 m - 20 "Centaur" CENT
+thing 114 m - 44 "Dark bishop" BISH
thing 115 m - 20 "Centaur leader" CENTF
-thing 120 m - 17 "Serpent leader" SSPTK # Not too sure...
-thing 121 m - 17 "Serpent" SSDV # Not too sure...
-thing 8020 m - 22 "Ice guy" ICEY
+thing 120 m - 17 "Stalker leader" SSPTK
+thing 121 m - 17 "Stalker" SSDV
+thing 254 m - 40 "Death wyvern *" DRAG
+thing 8020 m - 22 "Wendigo" ICEY
+thing 8080 m - 64 "Chaos serpent (gas)" DEM2
+thing 10011 m - 40 "Reiver leader" WRTH
thing 10030 m - 25 "Ettin" ETTN
-thing 10060 m - 20 "Fire demon" FDMN
-
-thing 10 w - 17 "Serpent staff" WCSS # Cleric
-thing 53 w - 17 "Frost chards" WMCS # Mage
-thing 8010 w - 17 "Timon's axe" WFAX # Fighter
+thing 10060 m - 20 "Fire gargoyle" FDMN
+thing 10080 m - 80 "Heresiarch" SORC
+thing 10100 m - 32 "Zedek (fighter)" PLAYA8
+thing 10101 m - 32 "Traductus (cleric)" CLERA8
+thing 10102 m - 32 "Menelkir (mage)" MAGEA8
+thing 10200 m - 20 "Korax" KORX
+
+thing 10 w - 17 "Serpent staff" WCSS
+thing 12 w - 20 "Quietus blade" WFR1
+thing 13 w - 20 "Quietus guard" WFR2
+thing 16 w - 20 "Quietus hilt" WFR3
+thing 18 w - 20 "Wraithverge head" WCH1
+thing 19 w - 20 "Wraithverge center" WCH2
+thing 20 w - 20 "Wraithverge grip" WCH3
+thing 21 w - 20 "Bloodscourge head" WMS1
+thing 22 w - 20 "Bloodscourge center" WMS2
+thing 23 w - 20 "Bloodscourge grip" WMS3
+thing 53 w - 17 "Frost chards" WMCS
+thing 123 w - 10 "Hammer of retributn" WFHM
+thing 8009 w - 10 "Firestorm" WCFM
+thing 8010 w - 17 "Timon's axe" WFAX
+thing 8040 w - 20 "Arc of death" WMLG
thing 122 a - 17 "Blue manna" MAN1
thing 124 a - 17 "Green manna" MAN2
thing 8004 a - 17 "Combined manna" MAN3
thing 81 h - 17 "Crystal vial" PTN1
-thing 82 h - 17 "Quartz flask" PTN2
-thing 8000 h - 17 "Flechette" PSBG
+thing 8005 h - 10 "Mesh armor" ARM1
+thing 8006 h - 20 "Falcon shield" ARM2
thing 8007 h - 17 "Platinum helmet" ARM3
+thing 8008 h - 20 "Amulet of warding" ARM4
-thing 314 k - 17 "Stone (?) key" KEY7
+thing 30 A - 20 "Porkalator" ARTIPORK
+thing 32 A - 20 "Mystic urn" ARTISPHL
+thing 33 A - 20 "Torch" ARTITRCH
+thing 36 A - 20 "Chaos device" ARTIATLP
+thing 82 A - 17 "Quartz flask" PTN2
+thing 83 A - 20 "Wings of wrath" SOAR
+thing 84 A - 20 "Icon of defender" INVU
+thing 86 A - 15 "Dark servant" ARTISUMN
+thing 8000 A - 17 "Flechette" PSBG
+thing 8002 A - 20 "Boots of speed" ARTISPED
+thing 8003 A - 20 "Krater of might" ARTIBMAN
+thing 8041 A - 20 "Dragonskin bracers" ARTIBRAC
+thing 10040 A - 20 "Banishment device" ARTITELO
+thing 10110 A - 20 "Disc of repulsion" ARTIBLST
+thing 10120 A - 20 "Mystic ambient inc" ARTIHRAD
+
+thing 9002 q - 20 "Yorick's skull" ARTISKLL
+thing 9003 q - 20 "Heart of D'Sparil" ARTIBGEM
+thing 9004 q - 20 "Ruby planet" ARTIGEMR
+thing 9005 q - 20 "Emerald planet 1" ARTIGEMG
+thing 9006 q - 20 "Sapphire planet 1" ARTIGEMB
+thing 9007 q - 20 "Daemon codex" ABK1
+thing 9008 q - 20 "Liber oscura" ABK2
+thing 9009 q - 20 "Emerald planet 2" ARTIGMG2
+thing 9010 q - 20 "Sapphire planet 2" ARTIGMB2
+thing 9014 q - 20 "Flame mask" ARTISKL2
+thing 9015 q - 20 "Glaive seal" ARTIFWEP
+thing 9016 q - 20 "Holy relic" ARTICWEP
+thing 9017 q - 20 "Sigil of the Magus" ARTIMWEP
+thing 9018 q - 20 "Clock gear 1" ARTIGEAR
+thing 9019 q - 20 "Clock gear 2" ARTIGER2
+thing 9020 q - 20 "Clock gear 3" ARTIGER3
+thing 9021 q - 20 "Clock gear 4" ARTIGER4
+
+thing 8030 k - 10 "Steel key" KEY1
+thing 8031 k - 10 "Cave key" KEY2
+thing 8032 k - 10 "Axe key" KEY3
+thing 8033 k - 10 "Fire key" KEY4
+thing 8034 k - 10 "Emerald key" KEY5
+thing 8035 k - 10 "Dungeon key" KEY6
+thing 8036 k - 10 "Silver key" KEY7
+thing 8037 k - 10 "Rusted key" KEY8
+thing 8038 k - 10 "Horn key" KEY9
+thing 8039 k - 10 "Swamp key" KEYA
+thing 8200 k - 10 "Castle key" KEYB
+thing 24 P - 20 "Tree trunk (brown)" TRE1
+thing 25 P - 20 "Tree trunk (brownD)" TRE1
thing 26 P - 17 "Swamp tree tall" TRE2
thing 27 P - 17 "Swamp tree short" TRE3
+thing 28 P - 20 "Tree stump (splint)" STM1
+thing 29 P - 20 "Tree stump" STM2
+thing 39 P - 20 "Mushroom (large, L)" MSH1
+thing 40 P - 20 "Mushroom (large, R)" MSH2
+thing 41 P - 20 "Mushroom (med, L)" MSH3
+thing 42 P - 20 "Mushroom (small, R)" MSH4
+thing 44 P - 20 "Mushroom (small)" MSH5
+thing 45 P - 20 "Mushroom (small, F)" MSH6
+thing 46 P - 20 "Mushroom (small, B)" MSH7
+thing 47 P - 20 "Mushroom (small, M)" MSH8
+thing 60 P - 20 "Vine" TRE3
thing 78 P - 17 "Tree leaning right" TRE4
thing 79 P - 17 "Tree leaning left" TRE5
thing 80 P - 17 "Gnarled tree right" TRE6
thing 87 P - 17 "Gnarled tree left" TRE7
+thing 113 P - 10 "Blowing leaves" LEF1
thing 8062 P - 17 "Dead tree" TRDT
thing 8068 P - 17 "Conic tree" XMAS
-
-thing 48 d - 17 "Stalactite+gmite" SGMP
-thing 72 d - 17 "Statue" STT2
-thing 74 d - 17 "Short statue" STT4
+thing 8101 P - 20 "Shrub (small)" SHB1
+thing 8102 P - 20 "Shrub (large)" SHB2
+thing 8103 P - 20 "Bucket (hanging)" BCKT
+thing 8104 P - 20 "Mushroom (explodng)" SHRM
+
+thing 48 t - 17 "Stalactite+gmite" SGMP
+thing 49 t - 20 "Stalagmite (large)" SGM1
+thing 50 t - 20 "Stalagmite (medium)" SGM2
+thing 51 t - 20 "Stalagmite (small)" SGM3
+thing 52 t - 20 "Stalactite (large)" SLC1
+thing 56 t - 20 "Stalactite (medium)" SLC2
+thing 57 t - 20 "Stalactite (small)" SLC3
+thing 89 t - 20 "Ice stalactite(lrg)" ICT1
+thing 90 t - 20 "Ice stalactite(med)" ICT2
+thing 91 t - 20 "Ice stalactite(sml)" ICT3
+thing 92 t - 20 "Ice stalactite(tny)" ICT4
+thing 93 t - 20 "Ice stalagmite(lrg)" ICM1
+thing 94 t - 20 "Ice stalagmite(med)" ICM2
+thing 95 t - 20 "Ice stalagmite(sml)" ICM3
+thing 96 t - 20 "Ice stalagmite(tny)" ICM4
+
+thing 5 g - 20 "Gargoyle statue w/s" STTW
+thing 72 g - 17 "Stone gargoyle tall" STT2
+thing 73 g - 20 "Ice gargoyle tall" STT3
+thing 74 g - 17 "Stone gargoyle shrt" STT4
+thing 76 g - 20 "Ice gargoyle short" STT5
+thing 8044 g - 20 "Wooden gargoyle tal" GAR1
+thing 8045 g - 20 "Fire gargoyle tall" GAR2
+thing 8046 g - 20 "Red gargoyle tall" GAR3
+thing 8047 g - 20 "Wooden gargoyle tal" GAR4
+thing 8048 g - 20 "Metal gargoyle tall" GAR5
+thing 8049 g - 20 "Fire gargoyle short" GAR6
+thing 8050 g - 20 "Red gargoyle short" GAR7
+thing 8051 g - 20 "Wooden gargoyle srt" GAR8
+thing 8052 g - 20 "Metal gargoyle shrt" GAR9
+thing 9011 g - 20 "Gargoyle statue" STWN
+
+thing 8500 T - 20 "Stein (tall)" TST1
+thing 8501 T - 20 "Stein (short)" TST2
+thing 8502 T - 20 "Candle w/spider web" TST3
+thing 8503 T - 20 "Candle (short)" TST4
+thing 8504 T - 20 "Candle (tall)" TST5
+thing 8505 T - 20 "Goblet (spilled)" TST6
+thing 8506 T - 20 "Goblet (tall)" TST7
+thing 8507 T - 20 "Goblet (short)" TST8
+thing 8508 T - 20 "Goblet w/silver bnd" TST9
+thing 8509 T - 20 "Meat cleaver" TST0
+
+thing 61 D - 20 "Corpse impaled" CPS1
+thing 62 D - 20 "Corpse sleeping" CPS2
+thing 71 D - 20 "Corpse hung legs" CPS3
+thing 108 D - 20 "Corpse hung" CPS4
+thing 109 D - 20 "Corpse bleeding" CPS5
+thing 110 D - 20 "Corpse chained" CPS6
+thing 111 D - 10 "Pool of blood" BDPL
+thing 8067 D - 20 "Iron maiden" IRON
+thing 8071 D - 20 "Chain (short)" CHNS
+thing 8072 D - 20 "Chain (long)" CHNSB0
+thing 8073 D - 20 "Chain heart on hook" CHNSC0
+thing 8074 D - 20 "Chain w/large hook" CHNSD0
+thing 8075 D - 20 "Chain w/small hook" CHNSE0
+thing 8076 D - 20 "Chain w/spiked ball" CHNSF0
+thing 8077 D - 20 "Chain skull on hook" CHNSG0
+
+thing 6 d - 20 "Rock w/moss (tiny)" RCK1
+thing 7 d - 20 "Rock w/moss (small)" RCK2
+thing 9 d - 20 "Rock w/moss (medium)" RCK3
+thing 15 d - 20 "Rock w/moss (large)" RCK4
+thing 37 d - 20 "Stump w/moss (short)" STM3
+thing 38 d - 20 "Stump w/moss (tall)" STM4
+thing 58 d - 20 "Moss (three strands)" MSS1
+thing 59 d - 20 "Moss (one strand)" MSS2
+thing 63 d - 20 "Tombstone (R.I.P.)" TMS1
+thing 64 d - 20 "Tombstone (Shane)" TMS2
+thing 65 d - 20 "Tombstone (large cross)" TMS3
+thing 66 d - 20 "Tombstone (Brian R.)" TMS4
+thing 67 d - 20 "Tombstone (circular cross)" TMS5
+thing 68 d - 20 "Tombstone (small cross on pedestal)" TMS6
+thing 69 d - 20 "Tombstone (Brian P.)" TMS7
thing 77 d - 17 "Banner" BNR1
-
-thing 54 l - 17 "Torch" WLTR
-thing 8061 l - 17 "FIXME" BRTR
-
-thing 314 e - 17 "Bogus sound"
-
-thing 314 s - 17 "Bogus sound"
-
-
+thing 88 d - 20 "Log" LOGG
+thing 97 d - 20 "Rock formation (large, brown)" RKBL
+thing 98 d - 20 "Rock formation (small, brown)" RKBS
+thing 99 d - 20 "Rock formation (small, gray)" RKBK
+thing 100 d - 20 "Rubble (large)" RBL1
+thing 101 d - 20 "Rubble (small)" RBL2
+thing 102 d - 20 "Rubble (medium)" RBL3
+thing 103 d - 20 "Vase on pedestal" VASE
+thing 104 d - 20 "Pot (tall, skinny) *" POT1
+thing 105 d - 20 "Pot (medium, skinny) *" POT2
+thing 106 d - 20 "Pot (short, chipped) *" POT3
+thing 140 d - 20 "Sparkling red smoke" TSMK
+thing 8064 d - 20 "Suit of armor *" SUIT
+thing 8065 d - 40 "Bell" BBLL
+thing 8100 d - 20 "Barrel" BARL
+thing 9012 d - 20 "Pedestal" GMPD
+thing 10001 d - 20 "Fog (small) *" FOGS
+thing 10002 d - 20 "Fog (medium) *" FOGM
+thing 10003 d - 20 "Fog (large) *" FOGL
+thing 10090 d - 20 "Spike (down)" TSPKC0
+thing 10091 d - 20 "Spike (up)" TSPK
+
+thing 17 l - 20 "Chandelier w/flame" CDLR
+thing 54 l - 17 "Wall torch w/flame" WLTR
+thing 55 l - 20 "Wall torch" WLTRI0
+thing 116 l - 10 "Brazier w/flame" TWTR
+thing 117 l - 10 "Brazier" TWTRI0
+thing 119 l - 20 "Candles" CNDL
+thing 8042 l - 20 "Minotaur statue f" FBUL
+thing 8043 l - 20 "Minotaur statue" FBULH0
+thing 8060 l - 20 "Fire skull" FSKL
+thing 8061 l - 17 "Brazier w/flame sm" BRTR
+thing 8063 l - 20 "Chandelier" CDLRD0
+thing 8066 l - 20 "Candle (blue)" CAND
+thing 8069 l - 20 "Cauldron (w/flame)" CDRNB0
+thing 8070 l - 20 "Cauldron" CDRN
+thing 10500 l - 20 "Flame (small, t) *" FFSM
+thing 10501 l - 20 "Flame (small, cnt)" FFSMC0
+thing 10502 l - 20 "Flame (large, t) *" FFLG
+thing 10503 l - 20 "Flame (large, cnt)" FFLGF0
+
+thing 1400 s - 20 "Stone" TELE
+thing 1401 s - 20 "Heavy" TELE
+thing 1402 s - 20 "Metal" TELE
+thing 1403 s - 20 "Creak" TELE
+thing 1404 s - 20 "Silent" TELE
+thing 1405 s - 20 "Lava" TELE
+thing 1406 s - 20 "Water" TELE
+thing 1407 s - 20 "Ice" TELE
+thing 1408 s - 20 "Earth crack" TELE
+thing 1409 s - 20 "Metal2" TELE
+thing 1410 s - 20 "Wind blowing" TELE
+
+thing 118 S - 10 "Magic step" TLGL
+thing 3000 S - 20 "Polyobject anchor" ICPRD0
+thing 3001 S - 20 "Start spot" SBFXH0
+thing 3002 S - 20 "Start spot w/crush" SBFXC0
+thing 9001 S - 10 "Map spot *" TELEC0
+thing 9013 S - 10 "Map spot w/gravity" TELED0
+thing 10000 S - 20 "Spawn fog *" SPIRK0
+thing 10225 S - 20 "Spawn bat *" ABATC3C7