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 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 + * , 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 at map coordinates + * (, ). + * + * If 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 @@ # 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