imported missing editor files from d1x
authorBradley Bell <btb@icculus.org>
Sun, 19 Dec 2004 13:54:27 +0000 (13:54 +0000)
committerBradley Bell <btb@icculus.org>
Sun, 19 Dec 2004 13:54:27 +0000 (13:54 +0000)
50 files changed:
ChangeLog
main/editor/Makefile.am
main/editor/autosave.c [new file with mode: 0644]
main/editor/centers.c [new file with mode: 0644]
main/editor/centers.h [new file with mode: 0644]
main/editor/curves.c [new file with mode: 0644]
main/editor/eglobal.c [new file with mode: 0644]
main/editor/ehostage.c [new file with mode: 0644]
main/editor/ehostage.h [new file with mode: 0644]
main/editor/elight.c [new file with mode: 0644]
main/editor/eobject.c [new file with mode: 0644]
main/editor/eobject.h [new file with mode: 0644]
main/editor/eswitch.c [new file with mode: 0644]
main/editor/eswitch.h [new file with mode: 0644]
main/editor/fixseg.c [new file with mode: 0644]
main/editor/func.c [new file with mode: 0644]
main/editor/group.c [new file with mode: 0644]
main/editor/info.c [new file with mode: 0644]
main/editor/info.h [new file with mode: 0644]
main/editor/kbuild.c [new file with mode: 0644]
main/editor/kcurve.c [new file with mode: 0644]
main/editor/kfuncs.c [new file with mode: 0644]
main/editor/kfuncs.h [new file with mode: 0644]
main/editor/kgame.c [new file with mode: 0644]
main/editor/kgroup.c [new file with mode: 0644]
main/editor/khelp.c [new file with mode: 0644]
main/editor/kmine.c [new file with mode: 0644]
main/editor/ksegmove.c [new file with mode: 0644]
main/editor/ksegsel.c [new file with mode: 0644]
main/editor/ksegsize.c [new file with mode: 0644]
main/editor/ktmap.c [new file with mode: 0644]
main/editor/kview.c [new file with mode: 0644]
main/editor/macro.c [new file with mode: 0644]
main/editor/macro.h [new file with mode: 0644]
main/editor/meddraw.c [new file with mode: 0644]
main/editor/meddraw.h [new file with mode: 0644]
main/editor/medlisp.h [new file with mode: 0644]
main/editor/medmisc.c [new file with mode: 0644]
main/editor/medmisc.h [new file with mode: 0644]
main/editor/medrobot.c [new file with mode: 0644]
main/editor/medrobot.h [new file with mode: 0644]
main/editor/medsel.c [new file with mode: 0644]
main/editor/medsel.h [new file with mode: 0644]
main/editor/medwall.c [new file with mode: 0644]
main/editor/mine.c [new file with mode: 0644]
main/editor/objpage.c [new file with mode: 0644]
main/editor/objpage.h [new file with mode: 0644]
main/editor/seguvs.c [new file with mode: 0644]
main/editor/texpage.c [new file with mode: 0644]
main/editor/texture.c [new file with mode: 0644]

index d646653..75d008d 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
 
        * ui/Makefile.am: added Makefile.am for ui
 
+       * main/editor/Makefile.am, main/editor/autosave.c,
+       main/editor/centers.c, main/editor/centers.h,
+       main/editor/curves.c, main/editor/eglobal.c,
+       main/editor/ehostage.c, main/editor/ehostage.h,
+       main/editor/elight.c, main/editor/eobject.c,
+       main/editor/eobject.h, main/editor/eswitch.c,
+       main/editor/eswitch.h, main/editor/fixseg.c, main/editor/func.c,
+       main/editor/group.c, main/editor/info.c, main/editor/info.h,
+       main/editor/kbuild.c, main/editor/kcurve.c, main/editor/kfuncs.c,
+       main/editor/kfuncs.h, main/editor/kgame.c, main/editor/kgroup.c,
+       main/editor/khelp.c, main/editor/kmine.c, main/editor/ksegmove.c,
+       main/editor/ksegsel.c, main/editor/ksegsize.c,
+       main/editor/ktmap.c, main/editor/kview.c, main/editor/macro.c,
+       main/editor/macro.h, main/editor/meddraw.c, main/editor/meddraw.h,
+       main/editor/medlisp.h, main/editor/medmisc.c,
+       main/editor/medmisc.h, main/editor/medrobot.c,
+       main/editor/medrobot.h, main/editor/medsel.c,
+       main/editor/medsel.h, main/editor/medwall.c, main/editor/mine.c,
+       main/editor/objpage.c, main/editor/objpage.h,
+       main/editor/seguvs.c, main/editor/texpage.c,
+       main/editor/texture.c: imported missing editor files from d1x
+
 2004-12-17  Chris Taylor  <c.taylor@student.curtin.edu.au>
 
        * main/state.c: open autosave file for writing, not reading
index 3309c31..1622c7f 100644 (file)
@@ -1,6 +1,18 @@
 noinst_LIBRARIES = libeditor.a
 INCLUDES = -I$(top_srcdir)/include -I$(top_srcdir)/arch/include -I$(top_srcdir)/main
 
-libeditor_a_SOURCES = med.c segment.c
+libeditor_a_SOURCES = \
+autosave.c  centers.c   curves.c    eglobal.c   ehostage.c  \
+elight.c    eobject.c   eswitch.c   fixseg.c    func.c      \
+group.c     info.c      kbuild.c    kcurve.c    kfuncs.c    \
+kgame.c     kgroup.c    khelp.c     kmine.c     ksegmove.c  \
+ksegsel.c   ksegsize.c  ktmap.c     kview.c     macro.c     \
+med.c       meddraw.c   medmisc.c   medrobot.c  medsel.c    \
+medwall.c   mine.c      objpage.c   segment.c   seguvs.c    \
+texpage.c   texture.c
 
-EXTRA_DIST = editor.h kdefs.h medwall.h seguvs.h texpage.h
+EXTRA_DIST = \
+centers.h   editor.h    ehostage.h  eobject.h   eswitch.h   \
+info.h      kdefs.h     kfuncs.h    macro.h     meddraw.h   \
+medlisp.h   medmisc.h   medrobot.h  medsel.h    medwall.h   \
+objpage.h   seguvs.h    texpage.h
diff --git a/main/editor/autosave.c b/main/editor/autosave.c
new file mode 100644 (file)
index 0000000..9fcee9d
--- /dev/null
@@ -0,0 +1,329 @@
+/*
+THE COMPUTER CODE CONTAINED HEREIN IS THE SOLE PROPERTY OF PARALLAX
+SOFTWARE CORPORATION ("PARALLAX").  PARALLAX, IN DISTRIBUTING THE CODE TO
+END-USERS, AND SUBJECT TO ALL OF THE TERMS AND CONDITIONS HEREIN, GRANTS A
+ROYALTY-FREE, PERPETUAL LICENSE TO SUCH END-USERS FOR USE BY SUCH END-USERS
+IN USING, DISPLAYING,  AND CREATING DERIVATIVE WORKS THEREOF, SO LONG AS
+SUCH USE, DISPLAY OR CREATION IS FOR NON-COMMERCIAL, ROYALTY OR REVENUE
+FREE PURPOSES.  IN NO EVENT SHALL THE END-USER USE THE COMPUTER CODE
+CONTAINED HEREIN FOR REVENUE-BEARING PURPOSES.  THE END-USER UNDERSTANDS
+AND AGREES TO THE TERMS HEREIN AND ACCEPTS THE SAME BY USE OF THIS FILE.  
+COPYRIGHT 1993-1998 PARALLAX SOFTWARE CORPORATION.  ALL RIGHTS RESERVED.
+*/
+/*
+ * $Source: /cvs/cvsroot/d2x/main/editor/autosave.c,v $
+ * $Revision: 1.1 $
+ * $Author: btb $
+ * $Date: 2004-12-19 13:54:27 $
+ * 
+ * Autosave system: 
+ * Saves current mine to disk to prevent loss of work, and support undo.
+ * 
+ * $Log: not supported by cvs2svn $
+ * Revision 1.1.1.1  1999/06/14 22:02:47  donut
+ * Import of d1x 1.37 source.
+ *
+ * Revision 2.0  1995/02/27  11:34:53  john
+ * Version 2.0! No anonymous unions, Watcom 10.0, with no need
+ * for bitmaps.tbl.
+ * 
+ * Revision 1.25  1994/11/19  00:04:40  john
+ * Changed some shorts to ints.
+ * 
+ * Revision 1.24  1994/11/17  11:38:59  matt
+ * Ripped out code to load old mines
+ * 
+ * Revision 1.23  1994/07/28  17:00:01  mike
+ * fix diagnostic_message erasing.
+ * 
+ * Revision 1.22  1994/07/21  12:48:28  mike
+ * Make time of day a global, fix clock so it doesn't show 10:2 instead of 10:02
+ * 
+ * Revision 1.21  1994/05/14  17:17:58  matt
+ * Got rid of externs in source (non-header) files
+ * 
+ * Revision 1.20  1994/05/02  18:04:14  yuan
+ * Fixed warning.
+ * 
+ * Revision 1.19  1994/05/02  17:59:04  yuan
+ * Changed undo_status into an array rather than malloced pointers.
+ * 
+ * Revision 1.18  1994/03/16  09:55:48  mike
+ * Flashing : in time.
+ * 
+ * Revision 1.17  1994/02/11  10:27:36  matt
+ * Changed 'if !DEMO' to 'ifndef DEMO'
+ * 
+ * Revision 1.16  1994/02/08  12:43:18  yuan
+ * Crippled save game function from demo version
+ * 
+ * Revision 1.15  1994/02/01  13:27:26  yuan
+ * autosave default off.
+ * 
+ * Revision 1.14  1994/01/05  09:57:37  yuan
+ * Fixed calendar/clock problem.
+ * 
+ * Revision 1.13  1993/12/17  16:09:59  yuan
+ * Changed clock font from Red to Black.
+ * 
+ * Revision 1.12  1993/12/15  13:08:38  yuan
+ * Fixed :0x times, so that the 0 shows up.
+ * 
+ * Revision 1.11  1993/12/15  11:19:52  yuan
+ * Added code to display clock in upper right.
+ * 
+ * Revision 1.10  1993/12/14  21:18:51  yuan
+ * Added diagnostic message to display
+ * 
+ * Revision 1.9  1993/12/14  18:32:59  yuan
+ * Added timed autosave code
+ * 
+ * Revision 1.8  1993/12/13  17:23:25  yuan
+ * Fixed bugs with undo.
+ * They were caused by badly changed extensions.
+ * 
+ * Revision 1.7  1993/12/09  16:42:32  yuan
+ * Changed extension of temp mines from .mi? -> .mn? 
+ * and now to .m? (So it doesn't interfere with .mnu)
+ * 
+ * Revision 1.6  1993/12/09  16:27:06  yuan
+ * Added toggle for autosave
+ * 
+ * Revision 1.5  1993/11/29  19:46:32  matt
+ * Changed includes
+ * 
+ * Revision 1.4  1993/11/11  15:54:11  yuan
+ * Added display message for Undo...
+ * Eg. Attach Segment UNDONE.
+ * 
+ * Revision 1.3  1993/11/09  18:53:11  yuan
+ * Autosave/Undo works up to 10 moves.
+ * 
+ * Revision 1.2  1993/11/08  19:14:03  yuan
+ * Added Undo command (not working yet)
+ * 
+ * Revision 1.1  1993/11/08  16:57:59  yuan
+ * Initial revision
+ * 
+ * 
+ */
+
+
+#ifdef RCS
+static char rcsid[] = "$Id: autosave.c,v 1.1 2004-12-19 13:54:27 btb Exp $";
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <math.h>
+#include <string.h>
+#ifndef __LINUX__
+#include <process.h>
+#endif
+#include <time.h>
+
+#include "error.h"
+
+#include "inferno.h"
+#include "editor.h"
+#include "mono.h"
+#include "u_mem.h"
+#include "ui.h"
+
+#define AUTOSAVE_PERIOD 5                      // Number of minutes for timed autosave
+
+int            Autosave_count;
+int            Autosave_numfiles;
+int            Autosave_total;
+int            undo_count;
+int            original;
+
+int            Timer_save_flag=0;
+int            Autosave_flag;
+int            save_second=-1;
+
+char           undo_status[10][100];
+
+void init_autosave(void) {
+//    int i;
+
+    Autosave_count = 0;
+    Autosave_numfiles = 0;
+        Autosave_flag = 0;
+    undo_count = 0;
+    //MALLOC( undo_status, char *, 10 );
+    //for (i=0; i<10; i++)
+    //    MALLOC( undo_status[i], char, 100 );
+    autosave_mine(mine_filename);
+}
+
+void close_autosave(void) {
+    int i;
+    char *delname, *ext;
+
+    for (i=0;i<Autosave_total;i++) {
+
+       MALLOC(delname, char, 128);
+
+        strcpy ( delname, mine_filename );
+        strupr( delname );
+       if ( !strcmp(delname, "*.MIN") ) strcpy(delname, "TEMP.MIN");
+
+        ext = strstr(delname, ".MIN");
+        sprintf( ext, ".M%d", i );
+
+        remove( delname );
+        free( delname );
+    }
+    //for (i=0;i<10;i++) free( undo_status[i] );
+    //free( undo_status );
+
+}
+
+void autosave_mine(char *name) {
+    char *savename, *ext;
+
+       if (Autosave_flag) {
+       
+           MALLOC(savename, char, 128);
+
+       
+           strcpy ( savename, name );
+           strupr( savename );
+           if ( !strcmp(savename, "*.MIN") ) strcpy(savename, "TEMP.MIN");
+       
+           ext = strstr(savename, ".MIN");
+           sprintf( ext, ".M%d", Autosave_count );
+       
+           //mprintf( 0, "Autosave: %s\n", savename );
+           med_save_mine( savename );
+           Autosave_count++;
+           if (undo_count > 0) undo_count--;
+           if (Autosave_count > 9) Autosave_count -= 10;
+           if (Autosave_numfiles < 10)
+               Autosave_numfiles++;
+           if (Autosave_total < 10)
+               Autosave_total++;
+       
+           free(savename);
+
+       }
+
+}
+
+
+void print_clock( int seconds, char message[10] ) {
+       int w,h,aw;
+       char    *p;
+
+       //      Make colon flash
+       if (seconds & 1)
+               if ((p = strchr(message, ':')) != NULL)
+                       *p = ' ';
+
+       gr_set_current_canvas( NULL );
+       gr_set_fontcolor( CBLACK, CGREY );
+       gr_get_string_size( message, &w, &h, &aw );
+       gr_setcolor( CGREY );
+       gr_rect( 700, 0, 799, h+1 );
+       gr_string( 700, 0, message );
+       gr_set_fontcolor( CBLACK, CWHITE );
+}
+
+static char the_time[14];      // changed from 10, I don't think that was long enough
+
+void clock_message( int seconds, char *format, ... ) {
+       va_list ap;
+
+       va_start(ap, format);
+       vsprintf(the_time, format, ap);
+       va_end(ap);
+
+       print_clock(seconds, the_time);
+}
+
+
+struct tm Editor_time_of_day;
+
+void set_editor_time_of_day()
+{
+       time_t   ltime;
+
+       time( &ltime );
+       Editor_time_of_day = *localtime( &ltime );
+}
+
+void TimedAutosave(char *name) 
+{
+       int              month,day,hour,minute,second;
+
+       month = (Editor_time_of_day.tm_mon) + 1;
+       day =   Editor_time_of_day.tm_mday;
+       minute = Editor_time_of_day.tm_min;
+       hour = Editor_time_of_day.tm_hour;
+       second = Editor_time_of_day.tm_sec;
+
+       if (hour > 12) hour-=12;
+
+       if (second!=save_second) {
+               save_second = second;
+               clock_message(second, "%d/%d %d:%02d", month, day, hour, minute);
+       }
+       
+
+#ifndef DEMO
+       if (minute%AUTOSAVE_PERIOD != 0)
+               Timer_save_flag = 1;
+
+       if ((minute%AUTOSAVE_PERIOD == 0) && (Timer_save_flag)) {
+               time_t   ltime;
+
+               autosave_mine(name);
+               Timer_save_flag = 0;
+               time( &ltime );
+       diagnostic_message("Mine Autosaved at %s\n", ctime(&ltime));
+       }
+#endif
+
+}
+
+
+int undo( void ) {
+       Int3();
+       return 2;
+
+//@@    char *loadname, *ext;
+//@@    if (undo_count == 0) original = Autosave_count;
+//@@
+//@@    if (!Autosave_flag) 
+//@@            return 2;
+//@@
+//@@    if (Autosave_numfiles > 1) {
+//@@
+//@@        MALLOC(loadname, char, 128);
+//@@
+//@@        strcpy ( loadname, mine_filename );
+//@@        strupr( loadname );
+//@@        if ( !strcmp(loadname, "*.MIN") ) strcpy(loadname, "TEMP.MIN");
+//@@
+//@@        undo_count++;
+//@@        Autosave_count = original - undo_count;
+//@@        if (Autosave_count < 0) Autosave_count = Autosave_count+10;
+//@@        Autosave_numfiles--;
+//@@        //mprintf(0, "u=%d  a=%d  o=%d  num=%d\n", undo_count, Autosave_count, original, Autosave_numfiles);
+//@@
+//@@        ext = strstr(loadname, ".MIN");
+//@@        if (Autosave_count == 0) sprintf( ext, ".M9" );
+//@@            else sprintf( ext, ".M%d", Autosave_count-1 );
+//@@        //mprintf( 0, "Loading: %s\n", loadname );
+//@@        med_load_mine( loadname );
+//@@
+//@@        free(loadname);
+//@@        return 0;
+//@@     }
+//@@     else return 1;
+//@@     //diagnostic_message("Can't undo\n");
+
+}
+
diff --git a/main/editor/centers.c b/main/editor/centers.c
new file mode 100644 (file)
index 0000000..9976c00
--- /dev/null
@@ -0,0 +1,259 @@
+/*
+THE COMPUTER CODE CONTAINED HEREIN IS THE SOLE PROPERTY OF PARALLAX
+SOFTWARE CORPORATION ("PARALLAX").  PARALLAX, IN DISTRIBUTING THE CODE TO
+END-USERS, AND SUBJECT TO ALL OF THE TERMS AND CONDITIONS HEREIN, GRANTS A
+ROYALTY-FREE, PERPETUAL LICENSE TO SUCH END-USERS FOR USE BY SUCH END-USERS
+IN USING, DISPLAYING,  AND CREATING DERIVATIVE WORKS THEREOF, SO LONG AS
+SUCH USE, DISPLAY OR CREATION IS FOR NON-COMMERCIAL, ROYALTY OR REVENUE
+FREE PURPOSES.  IN NO EVENT SHALL THE END-USER USE THE COMPUTER CODE
+CONTAINED HEREIN FOR REVENUE-BEARING PURPOSES.  THE END-USER UNDERSTANDS
+AND AGREES TO THE TERMS HEREIN AND ACCEPTS THE SAME BY USE OF THIS FILE.  
+COPYRIGHT 1993-1998 PARALLAX SOFTWARE CORPORATION.  ALL RIGHTS RESERVED.
+*/
+/*
+ * $Source: /cvs/cvsroot/d2x/main/editor/centers.c,v $
+ * $Revision: 1.1 $
+ * $Author: btb $
+ * $Date: 2004-12-19 13:54:27 $
+ * 
+ * Dialog box stuff for control centers, material centers, etc.
+ * 
+ * $Log: not supported by cvs2svn $
+ * Revision 1.1.1.1  1999/06/14 22:02:47  donut
+ * Import of d1x 1.37 source.
+ *
+ * Revision 2.0  1995/02/27  11:35:30  john
+ * Version 2.0! No anonymous unions, Watcom 10.0, with no need
+ * for bitmaps.tbl.
+ * 
+ * Revision 1.9  1994/11/27  23:17:28  matt
+ * Made changes for new mprintf calling convention
+ * 
+ * Revision 1.8  1994/10/05  22:13:46  mike
+ * Clean up Centers dialog.
+ * 
+ * Revision 1.7  1994/10/03  23:39:55  mike
+ * Call fuelcen_activate instead of fuelcen_create.
+ * 
+ * Revision 1.6  1994/08/02  12:16:35  mike
+ * Change materialization center functionality.
+ * 
+ * Revision 1.5  1994/08/01  11:04:42  yuan
+ * New materialization centers.
+ * 
+ * Revision 1.4  1994/07/22  17:19:10  yuan
+ * Working on dialog box for refuel/repair/material/control centers.
+ * 
+ * Revision 1.3  1994/07/21  19:35:09  yuan
+ * Fixed #include problem
+ * 
+ * Revision 1.2  1994/07/21  19:02:41  yuan
+ * *** empty log message ***
+ * 
+ * Revision 1.1  1994/07/18  16:00:54  yuan
+ * Initial revision
+ * 
+ * 
+ */
+
+
+#ifdef RCS
+static char rcsid[] = "$Id: centers.c,v 1.1 2004-12-19 13:54:27 btb Exp $";
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <math.h>
+#include <string.h>
+
+#include "fuelcen.h"
+#include "screens.h"
+#include "inferno.h"
+#include "segment.h"
+#include "editor.h"
+
+#include "timer.h"
+#include "objpage.h"
+#include "fix.h"
+#include "mono.h"
+#include "error.h"
+#include "kdefs.h"
+#include       "object.h"
+#include "polyobj.h"
+#include "game.h"
+#include "powerup.h"
+#include "ai.h"
+#include "hostage.h"
+#include "eobject.h"
+#include "medwall.h"
+#include "eswitch.h"
+#include "ehostage.h"
+#include "key.h"
+#include "medrobot.h"
+#include "bm.h"
+#include "centers.h"
+
+//-------------------------------------------------------------------------
+// Variables for this module...
+//-------------------------------------------------------------------------
+static UI_WINDOW                               *MainWindow = NULL;
+static UI_GADGET_BUTTON        *QuitButton;
+static UI_GADGET_RADIO         *CenterFlag[MAX_CENTER_TYPES];
+static UI_GADGET_CHECKBOX      *RobotMatFlag[MAX_ROBOT_TYPES];
+
+static int old_seg_num;
+
+char   center_names[MAX_CENTER_TYPES][CENTER_STRING_LENGTH] = {
+       "Nothing",
+       "FuelCen",
+       "RepairCen",
+       "ControlCen",
+       "RobotMaker"
+};
+
+//-------------------------------------------------------------------------
+// Called from the editor... does one instance of the centers dialog box
+//-------------------------------------------------------------------------
+int do_centers_dialog()
+{
+       int i;
+
+       // Only open 1 instance of this window...
+       if ( MainWindow != NULL ) return 0;
+
+       // Close other windows. 
+       close_trigger_window();
+       hostage_close_window();
+       close_wall_window();
+       robot_close_window();
+
+       // Open a window with a quit button
+       MainWindow = ui_open_window( TMAPBOX_X+20, TMAPBOX_Y+20, 765-TMAPBOX_X, 545-TMAPBOX_Y, WIN_DIALOG );
+       QuitButton = ui_add_gadget_button( MainWindow, 20, 252, 48, 40, "Done", NULL );
+
+       // These are the checkboxes for each door flag.
+       i = 80;
+       CenterFlag[0] = ui_add_gadget_radio( MainWindow, 18, i, 16, 16, 0, "NONE" );                    i += 24;
+       CenterFlag[1] = ui_add_gadget_radio( MainWindow, 18, i, 16, 16, 0, "FuelCen" );         i += 24;
+       CenterFlag[2] = ui_add_gadget_radio( MainWindow, 18, i, 16, 16, 0, "RepairCen" );       i += 24;
+       CenterFlag[3] = ui_add_gadget_radio( MainWindow, 18, i, 16, 16, 0, "ControlCen" );      i += 24;
+       CenterFlag[4] = ui_add_gadget_radio( MainWindow, 18, i, 16, 16, 0, "RobotCen" );                i += 24;
+
+       // These are the checkboxes for each door flag.
+       for (i=0; i<N_robot_types; i++)
+               RobotMatFlag[i] = ui_add_gadget_checkbox( MainWindow, 128 + (i%2)*92, 20+(i/2)*24, 16, 16, 0, Robot_names[i]);
+                                                                                                                                                                                                         
+       old_seg_num = -2;               // Set to some dummy value so everything works ok on the first frame.
+
+       return 1;
+}
+
+void close_centers_window()
+{
+       if ( MainWindow!=NULL ) {
+               ui_close_window( MainWindow );
+               MainWindow = NULL;
+       }
+}
+
+void do_centers_window()
+{
+       int i;
+//     int robot_flags;
+       int redraw_window;
+
+       if ( MainWindow == NULL ) return;
+
+       //------------------------------------------------------------
+       // Call the ui code..
+       //------------------------------------------------------------
+       ui_button_any_drawn = 0;
+       ui_window_do_gadgets(MainWindow);
+
+       //------------------------------------------------------------
+       // If we change walls, we need to reset the ui code for all
+       // of the checkboxes that control the wall flags.  
+       //------------------------------------------------------------
+       if (old_seg_num != Cursegp-Segments) {
+               for (   i=0; i < MAX_CENTER_TYPES; i++ ) {
+                       CenterFlag[i]->flag = 0;                // Tells ui that this button isn't checked
+                       CenterFlag[i]->status = 1;              // Tells ui to redraw button
+               }
+
+               Assert(Cursegp->special < MAX_CENTER_TYPES);
+               CenterFlag[Cursegp->special]->flag = 1;
+
+               mprintf((0, "Cursegp->matcen_num = %i\n", Cursegp->matcen_num));
+
+               //      Read materialization center robot bit flags
+               for (   i=0; i < N_robot_types; i++ ) {
+                       RobotMatFlag[i]->status = 1;            // Tells ui to redraw button
+                       if (RobotCenters[Cursegp->matcen_num].robot_flags & (1 << i))
+                               RobotMatFlag[i]->flag = 1;              // Tells ui that this button is checked
+                       else
+                               RobotMatFlag[i]->flag = 0;              // Tells ui that this button is not checked
+               }
+
+       }
+
+       //------------------------------------------------------------
+       // If any of the radio buttons that control the mode are set, then
+       // update the corresponding center.
+       //------------------------------------------------------------
+
+       redraw_window=0;
+       for (   i=0; i < MAX_CENTER_TYPES; i++ )        {
+               if ( CenterFlag[i]->flag == 1 )
+                 {
+                       if ( i == 0)
+                               fuelcen_delete(Cursegp);
+                       else if ( Cursegp->special != i ) {
+                               fuelcen_delete(Cursegp);
+                               redraw_window = 1;
+                               fuelcen_activate( Cursegp, i );
+                       }
+                 }
+       }
+
+       for (   i=0; i < N_robot_types; i++ )   {
+               if ( RobotMatFlag[i]->flag == 1 ) {
+                       if (!(RobotCenters[Cursegp->matcen_num].robot_flags & (1<<i) )) {
+                               RobotCenters[Cursegp->matcen_num].robot_flags |= (1<<i);
+                               mprintf((0,"Segment %i, matcen = %i, Robot_flags %d\n", Cursegp-Segments, Cursegp->matcen_num, RobotCenters[Cursegp->matcen_num].robot_flags));
+                       } 
+               } else if (RobotCenters[Cursegp->matcen_num].robot_flags & 1<<i) {
+                       RobotCenters[Cursegp->matcen_num].robot_flags &= ~(1<<i);
+                       mprintf((0,"Segment %i, matcen = %i, Robot_flags %d\n", Cursegp-Segments, Cursegp->matcen_num, RobotCenters[Cursegp->matcen_num].robot_flags));
+               }
+       }
+       
+       //------------------------------------------------------------
+       // If anything changes in the ui system, redraw all the text that
+       // identifies this wall.
+       //------------------------------------------------------------
+       if (redraw_window || (old_seg_num != Cursegp-Segments ) ) {
+//             int     i;
+//             char    temp_text[CENTER_STRING_LENGTH];
+       
+               ui_wprintf_at( MainWindow, 12, 6, "Seg: %3d", Cursegp-Segments );
+
+//             for (i=0; i<CENTER_STRING_LENGTH; i++)
+//                     temp_text[i] = ' ';
+//             temp_text[i] = 0;
+
+//             Assert(Cursegp->special < MAX_CENTER_TYPES);
+//             strncpy(temp_text, Center_names[Cursegp->special], strlen(Center_names[Cursegp->special]));
+//             ui_wprintf_at( MainWindow, 12, 23, " Type: %s", temp_text );
+               Update_flags |= UF_WORLD_CHANGED;
+       }
+
+       if ( QuitButton->pressed || (last_keypress==KEY_ESC) )  {
+               close_centers_window();
+               return;
+       }               
+
+       old_seg_num = Cursegp-Segments;
+}
+
+
+
diff --git a/main/editor/centers.h b/main/editor/centers.h
new file mode 100644 (file)
index 0000000..90aad12
--- /dev/null
@@ -0,0 +1,55 @@
+/*
+THE COMPUTER CODE CONTAINED HEREIN IS THE SOLE PROPERTY OF PARALLAX
+SOFTWARE CORPORATION ("PARALLAX").  PARALLAX, IN DISTRIBUTING THE CODE TO
+END-USERS, AND SUBJECT TO ALL OF THE TERMS AND CONDITIONS HEREIN, GRANTS A
+ROYALTY-FREE, PERPETUAL LICENSE TO SUCH END-USERS FOR USE BY SUCH END-USERS
+IN USING, DISPLAYING,  AND CREATING DERIVATIVE WORKS THEREOF, SO LONG AS
+SUCH USE, DISPLAY OR CREATION IS FOR NON-COMMERCIAL, ROYALTY OR REVENUE
+FREE PURPOSES.  IN NO EVENT SHALL THE END-USER USE THE COMPUTER CODE
+CONTAINED HEREIN FOR REVENUE-BEARING PURPOSES.  THE END-USER UNDERSTANDS
+AND AGREES TO THE TERMS HEREIN AND ACCEPTS THE SAME BY USE OF THIS FILE.  
+COPYRIGHT 1993-1998 PARALLAX SOFTWARE CORPORATION.  ALL RIGHTS RESERVED.
+*/
+/*
+ * $Source: /cvs/cvsroot/d2x/main/editor/centers.h,v $
+ * $Revision: 1.1 $
+ * $Author: btb $
+ * $Date: 2004-12-19 13:54:27 $
+ * 
+ * Center header
+ * 
+ * $Log: not supported by cvs2svn $
+ * Revision 1.1.1.1  1999/06/14 22:02:29  donut
+ * Import of d1x 1.37 source.
+ *
+ * Revision 2.0  1995/02/27  11:35:02  john
+ * Version 2.0! No anonymous unions, Watcom 10.0, with no need
+ * for bitmaps.tbl.
+ * 
+ * Revision 1.3  1994/08/02  12:16:27  mike
+ * Prototype Center_names.
+ * 
+ * Revision 1.2  1994/07/21  19:35:20  yuan
+ * Fixed #include problem
+ * 
+ * Revision 1.1  1994/07/21  19:11:36  yuan
+ * Initial revision
+ * 
+ * 
+ */
+
+
+
+#ifndef _CENTERS_H
+#define _CENTERS_H
+
+#define       CENTER_STRING_LENGTH    12
+
+#include      "fuelcen.h"
+
+extern        char    Center_names[MAX_CENTER_TYPES][CENTER_STRING_LENGTH];
+
+void close_centers_window();
+void do_centers_window();
+
+#endif
diff --git a/main/editor/curves.c b/main/editor/curves.c
new file mode 100644 (file)
index 0000000..ded13c9
--- /dev/null
@@ -0,0 +1,513 @@
+/*
+THE COMPUTER CODE CONTAINED HEREIN IS THE SOLE PROPERTY OF PARALLAX
+SOFTWARE CORPORATION ("PARALLAX").  PARALLAX, IN DISTRIBUTING THE CODE TO
+END-USERS, AND SUBJECT TO ALL OF THE TERMS AND CONDITIONS HEREIN, GRANTS A
+ROYALTY-FREE, PERPETUAL LICENSE TO SUCH END-USERS FOR USE BY SUCH END-USERS
+IN USING, DISPLAYING,  AND CREATING DERIVATIVE WORKS THEREOF, SO LONG AS
+SUCH USE, DISPLAY OR CREATION IS FOR NON-COMMERCIAL, ROYALTY OR REVENUE
+FREE PURPOSES.  IN NO EVENT SHALL THE END-USER USE THE COMPUTER CODE
+CONTAINED HEREIN FOR REVENUE-BEARING PURPOSES.  THE END-USER UNDERSTANDS
+AND AGREES TO THE TERMS HEREIN AND ACCEPTS THE SAME BY USE OF THIS FILE.  
+COPYRIGHT 1993-1998 PARALLAX SOFTWARE CORPORATION.  ALL RIGHTS RESERVED.
+*/
+/*
+ * $Source: /cvs/cvsroot/d2x/main/editor/curves.c,v $
+ * $Revision: 1.1 $
+ * $Author: btb $
+ * $Date: 2004-12-19 13:54:27 $
+ *
+ * curve generation stuff
+ *
+ * 
+ *
+ */
+
+#ifdef RCS
+static char rcsid[] = "$Id: curves.c,v 1.1 2004-12-19 13:54:27 btb Exp $";
+#endif
+
+#include <time.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <stdarg.h>
+#ifndef __LINUX__
+#include <conio.h>
+#include <dos.h>
+#endif
+
+#include "inferno.h"
+#include "mono.h"
+#include "vecmat.h"
+#include "gr.h"
+#include "key.h"
+#include "editor.h"
+#include "gameseg.h"
+
+#define ONE_OVER_SQRT2 F1_0 * 0.707106781
+#define CURVE_RIGHT 1
+#define CURVE_UP 2
+
+segment *OriginalSeg;
+segment *OriginalMarkedSeg;
+int OriginalSide;
+int OriginalMarkedSide;
+segment *CurveSegs[MAX_SEGMENTS];
+int CurveNumSegs;
+const fix Mh[4][4] = { { 2*F1_0, -2*F1_0,  1*F1_0,  1*F1_0 },
+                       {-3*F1_0,  3*F1_0, -2*F1_0, -1*F1_0 },
+                       { 0*F1_0,  0*F1_0,  1*F1_0,  0*F1_0 },
+                       { 1*F1_0,  0*F1_0,  0*F1_0,  0*F1_0 } };
+
+void generate_banked_curve(fix maxscale, vms_equation coeffs);
+
+void create_curve(vms_vector *p1, vms_vector *p4, vms_vector *r1, vms_vector *r4, vms_equation *coeffs) {
+// Q(t) = (2t^3 - 3t^2 + 1) p1 + (-2t^3 + 3t^2) p4 + (t~3 - 2t^2 + t) r1 + (t^3 - t^2 ) r4
+
+    coeffs->n.x3 = fixmul(2*F1_0,p1->x) - fixmul(2*F1_0,p4->x) + r1->x + r4->x;
+    coeffs->n.x2 = fixmul(-3*F1_0,p1->x) + fixmul(3*F1_0,p4->x) - fixmul(2*F1_0,r1->x) - fixmul(1*F1_0,r4->x);
+    coeffs->n.x1 = r1->x;
+    coeffs->n.x0 = p1->x;
+    coeffs->n.y3 = fixmul(2*F1_0,p1->y) - fixmul(2*F1_0,p4->y) + r1->y + r4->y;
+    coeffs->n.y2 = fixmul(-3*F1_0,p1->y) + fixmul(3*F1_0,p4->y) - fixmul(2*F1_0,r1->y) - fixmul(1*F1_0,r4->y);
+    coeffs->n.y1 = r1->y;
+    coeffs->n.y0 = p1->y;
+    coeffs->n.z3 = fixmul(2*F1_0,p1->z) - fixmul(2*F1_0,p4->z) + r1->z + r4->z;
+    coeffs->n.z2 = fixmul(-3*F1_0,p1->z) + fixmul(3*F1_0,p4->z) - fixmul(2*F1_0,r1->z) - fixmul(1*F1_0,r4->z);
+    coeffs->n.z1 = r1->z;
+    coeffs->n.z0 = p1->z;
+
+}
+
+vms_vector evaluate_curve(vms_equation *coeffs, int degree, fix t) {
+    fix t2, t3;
+    vms_vector coord;
+
+    if (degree!=3) printf("ERROR: for Hermite Curves degree must be 3\n");
+
+    t2 = fixmul(t,t); t3 = fixmul(t2,t);
+
+    coord.x = fixmul(coeffs->n.x3,t3) + fixmul(coeffs->n.x2,t2) + fixmul(coeffs->n.x1,t) + coeffs->n.x0;
+    coord.y = fixmul(coeffs->n.y3,t3) + fixmul(coeffs->n.y2,t2) + fixmul(coeffs->n.y1,t) + coeffs->n.y0;
+    coord.z = fixmul(coeffs->n.z3,t3) + fixmul(coeffs->n.z2,t2) + fixmul(coeffs->n.z1,t) + coeffs->n.z0;
+
+    return coord;
+}
+
+
+fix curve_dist(vms_equation *coeffs, int degree, fix t0, vms_vector *p0, fix dist) {
+        vms_vector coord;
+    fix t, diff;
+
+    if (degree!=3) printf("ERROR: for Hermite Curves degree must be 3\n");
+
+    for (t=t0;t<1*F1_0;t+=0.001*F1_0) {
+        coord = evaluate_curve(coeffs, 3, t);
+        diff = dist - vm_vec_dist(&coord, p0);
+        if (diff<ACCURACY)   //&&(diff>-ACCURACY))
+            return t;
+    }
+    return -1*F1_0;
+
+}
+
+void curve_dir(vms_equation *coeffs, int degree, fix t0, vms_vector *dir) {
+    fix t2;
+
+    if (degree!=3) printf("ERROR: for Hermite Curves degree must be 3\n");
+
+    t2 = fixmul(t0,t0);
+
+    dir->x = fixmul(3*F1_0,fixmul(coeffs->n.x3,t2)) + fixmul(2*F1_0,fixmul(coeffs->n.x2,t0)) + coeffs->n.x1;
+    dir->y = fixmul(3*F1_0,fixmul(coeffs->n.y3,t2)) + fixmul(2*F1_0,fixmul(coeffs->n.y2,t0)) + coeffs->n.y1;
+    dir->z = fixmul(3*F1_0,fixmul(coeffs->n.z3,t2)) + fixmul(2*F1_0,fixmul(coeffs->n.z2,t0)) + coeffs->n.z1;
+    vm_vec_normalize( dir );
+
+}
+
+void plot_parametric(vms_equation *coeffs, fix min_t, fix max_t, fix del_t) {
+    vms_vector coord, dcoord;
+    fix t, dt;
+
+    gr_setcolor(15);
+    gr_box(  75,  40, 325, 290 );
+    gr_box(  75, 310, 325, 560 );
+    gr_box( 475, 310, 725, 560 );
+    //gr_pal_fade_in( grd_curscreen->pal );
+
+    for (t=min_t;t<max_t-del_t;t+=del_t) {
+        dt = t+del_t;
+
+        coord = evaluate_curve(coeffs, 3, t);
+        dcoord = evaluate_curve(coeffs, 3, dt);
+
+        gr_setcolor(9);
+        gr_line (  75*F1_0 + coord.x, 290*F1_0 - coord.z,  75*F1_0 + dcoord.x, 290*F1_0 - dcoord.z );
+        gr_setcolor(10);
+        gr_line (  75*F1_0 + coord.x, 560*F1_0 - coord.y,  75*F1_0 + dcoord.x, 560*F1_0 - dcoord.y );
+        gr_setcolor(12);
+        gr_line ( 475*F1_0 + coord.z, 560*F1_0 - coord.y, 475*F1_0 + dcoord.z, 560*F1_0 - dcoord.y );
+
+    }
+
+}
+
+
+vms_vector *vm_vec_interp(vms_vector *result, vms_vector *v0, vms_vector *v1, fix scale) {
+    vms_vector tvec;
+
+       vm_vec_sub(&tvec, v1, v0);
+    vm_vec_scale_add(result, v0, &tvec, scale);
+    vm_vec_normalize(result);
+    return result;
+}
+
+vms_vector p1, p4, r1, r4;
+vms_vector r4t, r1save;
+
+int generate_curve( fix r1scale, fix r4scale ) {
+    vms_vector vec_dir, tvec;
+    vms_vector coord,prev_point;
+    vms_equation coeffs;
+    fix enddist, nextdist;
+    int firstsegflag;
+    fix t, maxscale;
+    fixang rangle, uangle;
+
+    compute_center_point_on_side( &p1, Cursegp, Curside );
+
+    switch( Curside ) {
+        case WLEFT:
+            extract_right_vector_from_segment(Cursegp, &r1);
+            vm_vec_scale( &r1, -F1_0 );
+            break;
+        case WTOP:
+            extract_up_vector_from_segment(Cursegp, &r1);
+            break;
+        case WRIGHT:
+            extract_right_vector_from_segment(Cursegp, &r1);
+            break;
+        case WBOTTOM:
+            extract_up_vector_from_segment(Cursegp, &r1);
+            vm_vec_scale( &r1, -F1_0 );
+            break;
+        case WBACK:
+            extract_forward_vector_from_segment(Cursegp, &r1);
+            break;
+        case WFRONT:
+            extract_forward_vector_from_segment(Cursegp, &r1);
+            vm_vec_scale( &r1, -F1_0 );
+            break;
+        }            
+
+    compute_center_point_on_side( &p4, Markedsegp, Markedside );
+
+    switch( Markedside ) {
+        case WLEFT:
+            extract_right_vector_from_segment(Markedsegp, &r4);
+            extract_up_vector_from_segment(Markedsegp, &r4t);
+            break;
+        case WTOP:
+            extract_up_vector_from_segment(Markedsegp, &r4);
+            vm_vec_scale( &r4, -F1_0 );
+            extract_forward_vector_from_segment(Markedsegp, &r4t);
+            vm_vec_scale( &r4t, -F1_0 );
+            break;
+        case WRIGHT:
+            extract_right_vector_from_segment(Markedsegp, &r4);
+            vm_vec_scale( &r4, -F1_0 );
+            extract_up_vector_from_segment(Markedsegp, &r4t);
+            break;
+        case WBOTTOM:
+            extract_up_vector_from_segment(Markedsegp, &r4);
+            extract_forward_vector_from_segment(Markedsegp, &r4t);
+            break;
+        case WBACK:
+            extract_forward_vector_from_segment(Markedsegp, &r4);
+            vm_vec_scale( &r4, -F1_0 );
+            extract_up_vector_from_segment(Markedsegp, &r4t);
+            break;
+        case WFRONT:
+            extract_forward_vector_from_segment(Markedsegp, &r4);
+            extract_up_vector_from_segment(Markedsegp, &r4t);
+            break;
+        }
+
+    r1save = r1;
+    tvec = r1;
+    vm_vec_scale(&r1,r1scale);
+    vm_vec_scale(&r4,r4scale);
+
+    create_curve( &p1, &p4, &r1, &r4, &coeffs );
+    OriginalSeg = Cursegp;
+    OriginalMarkedSeg = Markedsegp;
+    OriginalSide = Curside;
+    OriginalMarkedSide = Markedside;
+    CurveNumSegs = 0;
+    coord = prev_point = p1;
+
+    t=0;
+    firstsegflag = 1;
+    enddist = F1_0; nextdist = 0;
+    while ( enddist > fixmul( nextdist, 1.5*F1_0 )) {
+            vms_matrix  rotmat,rotmat2;
+                       vms_vector      tdest;
+
+            if (firstsegflag==1)
+                firstsegflag=0;
+            else
+                extract_forward_vector_from_segment(Cursegp, &tvec);
+            nextdist = vm_vec_mag(&tvec);                                   // nextdist := distance to next point
+            t = curve_dist(&coeffs, 3, t, &prev_point, nextdist);               // t = argument at which function is forward vector magnitude units away from prev_point (in 3-space, not along curve)
+            coord = evaluate_curve(&coeffs, 3, t);                                          // coord := point about forward vector magnitude units away from prev_point
+            enddist = vm_vec_dist(&coord, &p4);                  // enddist := distance from current to end point, vec_dir used as a temporary variable
+            //vm_vec_normalize(vm_vec_sub(&vec_dir, &coord, &prev_point));
+            vm_vec_normalized_dir(&vec_dir, &coord, &prev_point);
+        if (!med_attach_segment( Cursegp, &New_segment, Curside, AttachSide )) {
+            med_extract_matrix_from_segment( Cursegp,&rotmat );                   // rotmat := matrix describing orientation of Cursegp
+                       vm_vec_rotate(&tdest,&vec_dir,&rotmat); // tdest := vec_dir in reference frame of Cursegp
+                       vec_dir = tdest;
+
+            vm_vector_2_matrix(&rotmat2,&vec_dir,NULL,NULL);
+//            mprintf(0, "[ [%6.2f %6.2f %6.2f]", f2fl(rotmat2.m1), f2fl(rotmat2.m2), f2fl(rotmat2.m3));
+//           mprintf(0, "  [%6.2f %6.2f %6.2f]", f2fl(rotmat2.m4), f2fl(rotmat2.m5), f2fl(rotmat2.m6));
+//            mprintf(0, "  [%6.2f %6.2f %6.2f] ]\n", f2fl(rotmat2.m7), f2fl(rotmat2.m8), f2fl(rotmat2.m9));
+
+            med_rotate_segment( Cursegp, &rotmat2 );
+                       prev_point = coord;
+            Curside = Side_opposite[AttachSide];
+
+            CurveSegs[CurveNumSegs]=Cursegp;
+            CurveNumSegs++;
+        } else return 0;
+       }
+
+    extract_up_vector_from_segment( Cursegp,&tvec );
+    uangle = vm_vec_delta_ang( &tvec, &r4t, &r4 );
+    if (uangle >= F1_0 * 1/8) uangle -= F1_0 * 1/4;
+    if (uangle >= F1_0 * 1/8) uangle -= F1_0 * 1/4;
+    if (uangle <= -F1_0 * 1/8) uangle += F1_0 * 1/4;
+    if (uangle <= -F1_0 * 1/8) uangle += F1_0 * 1/4;
+    extract_right_vector_from_segment( Cursegp,&tvec );
+    rangle = vm_vec_delta_ang( &tvec, &r4t, &r4 );
+    if (rangle >= F1_0/8) rangle -= F1_0/4;
+    if (rangle >= F1_0/8) rangle -= F1_0/4;
+    if (rangle <= -F1_0/8) rangle += F1_0/4;
+    if (rangle <= -F1_0/8) rangle += F1_0/4;
+
+    if ((uangle != 0) && (rangle != 0)) {
+        maxscale = CurveNumSegs*F1_0;
+//        mprintf(0, "Banked Curve Generation.. %f.\n", f2fl(maxscale));
+        generate_banked_curve(maxscale, coeffs);
+    }
+
+    if (CurveNumSegs) {
+        med_form_bridge_segment( Cursegp, Side_opposite[AttachSide], Markedsegp, Markedside );
+        CurveSegs[CurveNumSegs] = &Segments[ Markedsegp->children[Markedside] ];
+        CurveNumSegs++;
+       }
+
+    Cursegp = OriginalSeg;
+    Curside = OriginalSide;
+
+       med_create_new_segment_from_cursegp();
+
+       //warn_if_concave_segments();
+
+    if (CurveNumSegs) return 1;
+        else return 0;
+}
+
+void generate_banked_curve(fix maxscale, vms_equation coeffs) {
+    vms_vector vec_dir, tvec, b4r4t;
+    vms_vector coord,prev_point;
+    fix enddist, nextdist;
+    int firstsegflag;
+    fixang rangle, uangle, angle, scaled_ang=0;
+    fix t;
+
+    if (CurveNumSegs) {
+
+    extract_up_vector_from_segment( Cursegp,&b4r4t );
+    uangle = vm_vec_delta_ang( &b4r4t, &r4t, &r4 );
+    if (uangle >= F1_0 * 1/8) uangle -= F1_0 * 1/4;
+    if (uangle >= F1_0 * 1/8) uangle -= F1_0 * 1/4;
+    if (uangle <= -F1_0 * 1/8) uangle += F1_0 * 1/4;
+    if (uangle <= -F1_0 * 1/8) uangle += F1_0 * 1/4;
+//    mprintf(0, "up angle %f\n", f2fl(uangle)*360);
+
+    extract_right_vector_from_segment( Cursegp,&b4r4t );
+    rangle = vm_vec_delta_ang( &b4r4t, &r4t, &r4 );
+    if (rangle >= F1_0/8) rangle -= F1_0/4;
+    if (rangle >= F1_0/8) rangle -= F1_0/4;
+    if (rangle <= -F1_0/8) rangle += F1_0/4;
+    if (rangle <= -F1_0/8) rangle += F1_0/4;
+//    mprintf(0, "right angle %f\n", f2fl(rangle)*360);
+
+    angle = uangle;
+    if (abs(rangle) < abs(uangle)) angle = rangle;
+
+       delete_curve();
+
+    coord = prev_point = p1;
+
+#define MAGIC_NUM 0.707*F1_0
+
+    if (maxscale)
+        scaled_ang = fixdiv(angle,fixmul(maxscale,MAGIC_NUM));
+    mprintf((0, "scaled angle = %f\n", f2fl(scaled_ang)));
+
+    t=0; 
+    tvec = r1save;
+    firstsegflag = 1;
+    enddist = F1_0; nextdist = 0;
+    while ( enddist > fixmul( nextdist, 1.5*F1_0 )) {
+            vms_matrix  rotmat,rotmat2;
+            vms_vector  tdest;
+
+            if (firstsegflag==1)
+                firstsegflag=0;
+            else
+                extract_forward_vector_from_segment(Cursegp, &tvec);
+            nextdist = vm_vec_mag(&tvec);                                   // nextdist := distance to next point
+            t = curve_dist(&coeffs, 3, t, &prev_point, nextdist);               // t = argument at which function is forward vector magnitude units away from prev_point (in 3-space, not along curve)
+            coord = evaluate_curve(&coeffs, 3, t);                                          // coord := point about forward vector magnitude units away from prev_point
+            enddist = vm_vec_dist(&coord, &p4);                  // enddist := distance from current to end point, vec_dir used as a temporary variable
+            //vm_vec_normalize(vm_vec_sub(&vec_dir, &coord, &prev_point));
+            vm_vec_normalized_dir(&vec_dir, &coord, &prev_point);
+        if (!med_attach_segment( Cursegp, &New_segment, Curside, AttachSide )) {
+            med_extract_matrix_from_segment( Cursegp,&rotmat );                   // rotmat := matrix describing orientation of Cursegp
+                       vm_vec_rotate(&tdest,&vec_dir,&rotmat); // tdest := vec_dir in reference frame of Cursegp
+                       vec_dir = tdest;
+            vm_vec_ang_2_matrix(&rotmat2,&vec_dir,scaled_ang);
+//            mprintf((0, "[ [%6.2f %6.2f %6.2f]", f2fl(rotmat2.m1), f2fl(rotmat2.m2), f2fl(rotmat2.m3)));
+//            mprintf((0, "  [%6.2f %6.2f %6.2f]", f2fl(rotmat2.m4), f2fl(rotmat2.m5), f2fl(rotmat2.m6)));
+//            mprintf((0, "  [%6.2f %6.2f %6.2f] ]\n", f2fl(rotmat2.m7), f2fl(rotmat2.m8), f2fl(rotmat2.m9)));
+
+                       med_rotate_segment( Cursegp, &rotmat2 );
+                       prev_point = coord;
+            Curside = Side_opposite[AttachSide];
+
+            CurveSegs[CurveNumSegs]=Cursegp;
+            CurveNumSegs++;
+        }
+      }
+    }
+}
+
+
+void delete_curve() {
+    int i;
+
+       for (i=0; i<CurveNumSegs; i++) {
+//        mprintf((0, "[%d] %d\n", i, CurveSegs[i]->segnum ));
+        if (CurveSegs[i]->segnum != -1)
+            med_delete_segment(CurveSegs[i]);
+    }
+    Markedsegp = OriginalMarkedSeg;
+    Markedside = OriginalMarkedSide;
+    Cursegp = OriginalSeg;
+    Curside = OriginalSide;
+       med_create_new_segment_from_cursegp();
+    CurveNumSegs = 0;
+//    mprintf((0, "Num_segments %d\n", Num_segments));
+
+       //editor_status("");
+       //warn_if_concave_segments();
+}
+
+/*
+void main() {
+    vms_vector p1;
+    vms_vector p4;
+    vms_vector r1;
+    vms_vector r4;
+    vms_equation coeffs;
+    float x, y, z;
+    vms_vector test, test2, tvec;
+    fix t, t0;
+    fix distance, dist;
+    int key;
+
+
+    key_init();
+    printf("Enter p1 (x,y,z): ");
+    scanf("%f %f %f", &x, &y, &z);
+    p1.x = x*F1_0; p1.y = y*F1_0; p1.z = z*F1_0;
+    printf("Enter p4 (x,y,z): ");
+    scanf("%f %f %f", &x, &y, &z);
+    p4.x = x*F1_0; p4.y = y*F1_0; p4.z = z*F1_0;
+    printf("Enter r1 <x,y,z>: ");
+    scanf("%f %f %f", &x, &y, &z);
+    r1.x = x*F1_0; r1.y = y*F1_0; r1.z = z*F1_0;
+    printf("Enter r4 <x,y,z>: ");
+    scanf("%f %f %f", &x, &y, &z);
+    r4.x = x*F1_0; r4.y = y*F1_0; r4.z = z*F1_0;
+
+    create_curve( &p1, &p4, &r1, &r4, &coeffs );
+
+    printf("\nQ(t) = ");
+    printf("x [%6.3f %6.3f %6.3f %6.3f]\n", f2fl(coeffs.n.x3), f2fl(coeffs.n.x2), f2fl(coeffs.n.x1), f2fl(coeffs.n.x0));
+    printf("       y [%6.3f %6.3f %6.3f %6.3f]\n", f2fl(coeffs.n.y3), f2fl(coeffs.n.y2), f2fl(coeffs.n.y1), f2fl(coeffs.n.y0));
+    printf("       z [%6.3f %6.3f %6.3f %6.3f]\n", f2fl(coeffs.n.z3), f2fl(coeffs.n.z2), f2fl(coeffs.n.z1), f2fl(coeffs.n.z0));
+
+    printf("\nChecking direction vectors.\n");
+
+    for (t=0*F1_0;t<1*F1_0;t+=0.1*F1_0) {
+        curve_dir(&coeffs, 3, t, &test);
+        printf(" t = %.3f  dir = <%6.3f, %6.3f, %6.3f >\n", f2fl(t), f2fl(test.x), f2fl(test.y), f2fl(test.z) );
+    }
+
+    printf("\nChecking distance function.\n");
+    printf("Enter a distance: ");
+    scanf("%f", &x);
+    distance = x*F1_0;
+    printf("Enter a (0<t<1) value: ");
+    scanf("%f", &y);
+    t0 = y*F1_0;
+
+    gr_init(15);  // 800x600 mode
+    plot_parametric(&coeffs, 0*F1_0, 1*F1_0, 0.05*F1_0);
+
+    test = evaluate_curve(&coeffs, 3, t0);
+    t = curve_dist(&coeffs, 3, t0, &test, distance);
+    test2 = evaluate_curve(&coeffs, 3, t);
+
+    dist = vm_vec_mag(vm_vec_sub(&tvec, &test, &test2));
+
+    if (t != -1*F1_0) {
+        gr_setcolor(14);
+        gr_rect(  74+f2fl(test.x), 289-f2fl(test.z),  76+f2fl(test.x), 291-f2fl(test.z) );
+        gr_rect(  74+f2fl(test.x), 559-f2fl(test.y),  76+f2fl(test.x), 561-f2fl(test.y) );
+        gr_rect( 474+f2fl(test.z), 559-f2fl(test.y), 476+f2fl(test.z), 561-f2fl(test.y) );
+        gr_setcolor(13);
+        gr_rect(  74+f2fl(test2.x), 289-f2fl(test2.z),  76+f2fl(test2.x), 291-f2fl(test2.z) );
+        gr_rect(  74+f2fl(test2.x), 559-f2fl(test2.y),  76+f2fl(test2.x), 561-f2fl(test2.y) );
+        gr_rect( 474+f2fl(test2.z), 559-f2fl(test2.y), 476+f2fl(test2.z), 561-f2fl(test2.y) );
+    }
+
+    key = -1;
+    while (1)
+        if (key == KEY_ESC) break;
+        else key = key_getch();
+
+    gr_close();
+    key_close();
+
+    if (t == -1*F1_0) {
+        printf("From t=%.3f to t=1.000, ", f2fl(t0));
+        printf("two points separated by the distance %.3f\n do not exist on this curve.\n", x);
+    }
+    else {
+        printf("\nThe distance between points at:\n");
+        printf(" t0 = %.3f  ( %6.3f,%6.3f,%6.3f ) and\n", f2fl(t0), f2fl(test.x), f2fl(test.y), f2fl(test.z));
+        printf(" t  = %.3f  ( %6.3f,%6.3f,%6.3f ) is:\n", f2fl(t), f2fl(test2.x), f2fl(test2.y), f2fl(test2.z));
+        printf(" expected: %.3f\n", x);
+        printf("  actual : %.3f\n", f2fl(dist) );
+    }
+
+}
+*/
+
+
diff --git a/main/editor/eglobal.c b/main/editor/eglobal.c
new file mode 100644 (file)
index 0000000..f27f593
--- /dev/null
@@ -0,0 +1,221 @@
+/*
+THE COMPUTER CODE CONTAINED HEREIN IS THE SOLE PROPERTY OF PARALLAX
+SOFTWARE CORPORATION ("PARALLAX").  PARALLAX, IN DISTRIBUTING THE CODE TO
+END-USERS, AND SUBJECT TO ALL OF THE TERMS AND CONDITIONS HEREIN, GRANTS A
+ROYALTY-FREE, PERPETUAL LICENSE TO SUCH END-USERS FOR USE BY SUCH END-USERS
+IN USING, DISPLAYING,  AND CREATING DERIVATIVE WORKS THEREOF, SO LONG AS
+SUCH USE, DISPLAY OR CREATION IS FOR NON-COMMERCIAL, ROYALTY OR REVENUE
+FREE PURPOSES.  IN NO EVENT SHALL THE END-USER USE THE COMPUTER CODE
+CONTAINED HEREIN FOR REVENUE-BEARING PURPOSES.  THE END-USER UNDERSTANDS
+AND AGREES TO THE TERMS HEREIN AND ACCEPTS THE SAME BY USE OF THIS FILE.  
+COPYRIGHT 1993-1998 PARALLAX SOFTWARE CORPORATION.  ALL RIGHTS RESERVED.
+*/
+/*
+ * $Source: /cvs/cvsroot/d2x/main/editor/eglobal.c,v $
+ * $Revision: 1.1 $
+ * $Author: btb $
+ * $Date: 2004-12-19 13:54:27 $
+ *
+ * Globals for editor.
+ *
+ * $Log: not supported by cvs2svn $
+ * Revision 1.2  2003/03/09 06:34:09  donut
+ * change byte typedef to sbyte to avoid conflict with win32 byte which is unsigned
+ *
+ * Revision 1.1.1.1  1999/06/14 22:02:51  donut
+ * Import of d1x 1.37 source.
+ *
+ * Revision 2.0  1995/02/27  11:35:52  john
+ * Version 2.0! No anonymous unions, Watcom 10.0, with no need
+ * for bitmaps.tbl.
+ * 
+ * Revision 1.35  1994/05/23  14:48:15  mike
+ * make current segment be add segment.
+ * 
+ * Revision 1.34  1994/05/19  12:10:30  matt
+ * Use new vecmat macros and globals
+ * 
+ * Revision 1.33  1994/05/04  19:16:27  mike
+ * Add Degenerate_segment_found.
+ * 
+ * Revision 1.32  1994/05/03  11:02:57  mike
+ * Add SegSizeMode.
+ * 
+ * Revision 1.31  1994/02/16  13:49:12  mike
+ * enable editor to compile out.
+ * 
+ * Revision 1.30  1994/02/10  15:36:35  matt
+ * Various changes to make editor compile out.
+ * 
+ * Revision 1.29  1993/12/16  14:00:33  mike
+ * Add Curvert and Curedge.
+ * 
+ * Revision 1.28  1993/12/10  14:48:28  mike
+ * Kill orthogonal views.
+ * 
+ * Revision 1.27  1993/12/08  10:58:49  mike
+ * Add Cur_object_index.
+ * 
+ * Revision 1.26  1993/12/06  18:45:45  matt
+ * Changed object loading & handling
+ * 
+ * Revision 1.25  1993/12/02  17:51:49  john
+ * Changed my variable to match Mike's.
+ * 
+ * Revision 1.24  1993/12/02  17:36:13  john
+ * Added cur_obj_type
+ * 
+ * Revision 1.23  1993/11/24  14:41:16  mike
+ * Add variable Num_tilings.
+ * 
+ * Revision 1.22  1993/11/12  16:40:55  mike
+ * Add Identity_matrix, which is an identity matrix.
+ * 
+ * Revision 1.21  1993/11/02  13:08:17  mike
+ * Add N_warning_segs and Warning_segs
+ * 
+ * Revision 1.20  1993/11/02  10:31:53  mike
+ * Document some variables,
+ * Add Been_visited, removing it from editor.c
+ * Add Selected_segs[] and N_selected_segs.
+ * 
+ * Revision 1.19  1993/10/31  18:07:48  mike
+ * Add variable Lock_view_to_cursegp.
+ * 
+ * Revision 1.18  1993/10/19  20:54:51  matt
+ * Changed/cleaned up window updates
+ * 
+ * Revision 1.17  1993/10/18  18:35:43  mike
+ * Move Highest_vertex_index and Highest_segment_index here because they need
+ * to be globals.
+ * 
+ * Revision 1.16  1993/10/15  13:10:00  mike
+ * Move globals from editor.c to eglobal.c
+ * 
+ * Revision 1.15  1993/10/14  18:08:55  mike
+ * Change use of CONNECTIVITY to MAX_SIDES_PER_SEGMENT
+ * 
+ * Revision 1.14  1993/10/13  11:11:38  matt
+ * Made coordinate axes off by default
+ * 
+ * Revision 1.13  1993/10/12  09:59:27  mike
+ * Remove definition of Side_to_verts, which belongs in the game, not in the editor.
+ * 
+ * Revision 1.12  1993/10/09  15:48:07  mike
+ * Change type of Vertex_active and Side_to_verts from char to byte
+ * Move N_found_segs and Found_segs here from render.c
+ * Add Show_axes_flag.
+ * 
+ * Revision 1.11  1993/10/06  11:29:58  mike
+ * Add prototype for Side_opposite
+ * 
+ * Revision 1.10  1993/10/05  17:00:17  mike
+ * Add Vertex_active.
+ * 
+ * Revision 1.9  1993/10/04  17:18:16  mike
+ * Add variables Markedsegp, Markedside
+ * 
+ * Revision 1.8  1993/10/02  18:18:02  mike
+ * Added Draw_all_segments.  If !0, then all segments are drawn in draw_world.  If not set, then only those segments which
+ * are connected to the first segment are drawn.
+ * 
+ * Revision 1.7  1993/10/01  10:03:15  mike
+ * Fix ordering of vertices on front face: Used to be 0,1,2,3 made it 3,2,1,0
+ * 
+ * Revision 1.6  1993/09/27  16:04:28  mike
+ * Add Side_to_verts to replace _verts, which was local to segment.c
+ * 
+ * Revision 1.5  1993/09/27  15:20:52  mike
+ * Add Curside, which is current side, so we can make a certain side active.
+ * 
+ * Revision 1.4  1993/09/23  15:01:13  mike
+ * Remove game specific variables, put in mglobal.c
+ * 
+ * Revision 1.3  1993/09/22  10:52:17  mike
+ * Add global New_segment
+ * 
+ * Revision 1.2  1993/09/22  09:41:21  mike
+ * Change constand and variable names to conform to coding standards.
+ * 
+ * Revision 1.1  1993/09/20  17:06:09  mike
+ * Initial revision
+ * 
+ *
+ */
+
+#ifdef RCS
+static char rcsid[] = "$Id: eglobal.c,v 1.1 2004-12-19 13:54:27 btb Exp $";
+#endif
+
+#include <stdlib.h>
+#include "inferno.h"
+#include "segment.h"
+#include "editor.h"
+
+// Global pointer to current vertices, right now always Vertices.  Set in create_new_mine.
+segment        New_segment;                            // The segment which can be added to the mine.
+segment        *Cursegp;                                       // Pointer to current segment in mine.
+int            Curside;                                                // Side index in 0..MAX_SIDES_PER_SEGMENT of active side.
+int            Curedge;                                                //      Current edge on current side, in 0..3
+int            Curvert;                                                //      Current vertex on current side, in 0..3
+int            AttachSide = WFRONT;            //      Side on segment to attach.
+segment        *Markedsegp;                            // Marked segment, used in conjunction with *Cursegp to form joints.
+int            Markedside;                                     // Marked side on Markedsegp.
+
+int            Draw_all_segments;              // Set to 1 means draw_world draws all segments in Segments, else draw only connected segments
+
+sbyte          Vertex_active[MAX_VERTICES];    // !0 means vertex is in use, 0 means not in use.
+
+int            N_selected_segs=0;                                                      // Number of segments found at Selected_segs
+short          Selected_segs[MAX_SELECTED_SEGS];               // List of segment numbers currently selected
+
+int            N_warning_segs=0;                                                               // Number of segments warning-worthy, such as a concave segment
+short          Warning_segs[MAX_WARNING_SEGS];                 // List of segment numbers currently selected
+
+int            N_found_segs=0;                                                         // Number of segments found with last shift-mouse-click
+short          Found_segs[MAX_FOUND_SEGS];                             // List of warning-worthy segments
+
+int            Show_axes_flag=0;                                                               // 0 = don't show, !0 = do show coordinate axes in *Cursegp orientation
+
+sbyte          Been_visited[MAX_SEGMENTS];                             //      List of segments visited in a recursive search, if element n set, segment n done been visited
+
+// Variables global to this editor.c and the k?????.c files.
+uint        Update_flags = UF_ALL;  //force total redraw
+int                    Funky_chase_mode = 0;
+vms_angvec     Seg_orientation = {0,0,0};
+vms_vector     Seg_scale = {F1_0*20,F1_0*20,F1_0*20};
+int         mine_changed = 0;
+int         ModeFlag;
+editor_view *current_view;
+
+int    SegSizeMode = 1;                                                                        // Mode = 0/1 = not/is legal to move bound vertices, 
+
+//the view for the different windows.
+editor_view LargeView = {0,1, NULL, i2f(100),{{f1_0,0,0},{0,f1_0,0},{0,0,f1_0}},f1_0};
+#if ORTHO_VIEWS
+editor_view TopView   = {1,1, NULL, i2f(100),{{f1_0,0,0},{0,0,-f1_0},{0,f1_0,0}},f1_0};
+editor_view FrontView = {2,1, NULL, i2f(100),{{f1_0,0,0},{0,f1_0,0},{0,0,f1_0}},f1_0};
+editor_view RightView = {3,1, NULL, i2f(100),{{0,0,f1_0},{0,f1_0,0},{f1_0,0,0}},f1_0};
+#endif
+
+
+editor_view *Views[] = {&LargeView,
+#if ORTHO_VIEWS
+       &TopView,&FrontView,&RightView
+#endif
+       };
+
+int N_views = (sizeof(Views) / sizeof(*Views));
+
+int    Lock_view_to_cursegp = 1;               // !0 means whenever cursegp changes, view it
+
+int    Num_tilings = 1;                                        // Number of tilings per wall
+
+short Cur_object_index = -1;
+
+// The current robot type
+int Cur_robot_type = 0;
+
+//     !0 if a degenerate segment has been found.
+int    Degenerate_segment_found=0;
+
diff --git a/main/editor/ehostage.c b/main/editor/ehostage.c
new file mode 100644 (file)
index 0000000..76ba91a
--- /dev/null
@@ -0,0 +1,565 @@
+/*
+THE COMPUTER CODE CONTAINED HEREIN IS THE SOLE PROPERTY OF PARALLAX
+SOFTWARE CORPORATION ("PARALLAX").  PARALLAX, IN DISTRIBUTING THE CODE TO
+END-USERS, AND SUBJECT TO ALL OF THE TERMS AND CONDITIONS HEREIN, GRANTS A
+ROYALTY-FREE, PERPETUAL LICENSE TO SUCH END-USERS FOR USE BY SUCH END-USERS
+IN USING, DISPLAYING,  AND CREATING DERIVATIVE WORKS THEREOF, SO LONG AS
+SUCH USE, DISPLAY OR CREATION IS FOR NON-COMMERCIAL, ROYALTY OR REVENUE
+FREE PURPOSES.  IN NO EVENT SHALL THE END-USER USE THE COMPUTER CODE
+CONTAINED HEREIN FOR REVENUE-BEARING PURPOSES.  THE END-USER UNDERSTANDS
+AND AGREES TO THE TERMS HEREIN AND ACCEPTS THE SAME BY USE OF THIS FILE.  
+COPYRIGHT 1993-1998 PARALLAX SOFTWARE CORPORATION.  ALL RIGHTS RESERVED.
+*/
+/*
+ * $Source: /cvs/cvsroot/d2x/main/editor/ehostage.c,v $
+ * $Revision: 1.1 $
+ * $Author: btb $
+ * $Date: 2004-12-19 13:54:27 $
+ * 
+ * Routines for placing hostages, etc...
+ * 
+ * $Log: not supported by cvs2svn $
+ * Revision 1.1.1.1  1999/06/14 22:02:53  donut
+ * Import of d1x 1.37 source.
+ *
+ * Revision 2.0  1995/02/27  11:35:45  john
+ * Version 2.0! No anonymous unions, Watcom 10.0, with no need
+ * for bitmaps.tbl.
+ * 
+ * Revision 1.17  1995/01/14  19:18:05  john
+ * First version of object paging.
+ * 
+ * Revision 1.16  1994/11/20  14:11:41  matt
+ * Show object number in hostage window
+ * 
+ * Revision 1.15  1994/11/19  19:55:46  matt
+ * Added code to full support different hostage head clip & message for
+ * each hostage.
+ * 
+ * 
+ * Revision 1.14  1994/10/28  15:03:27  john
+ * Made digi_play_sample use volume.
+ * 
+ * 
+ * Revision 1.13  1994/10/23  02:11:39  matt
+ * Got rid of obsolete hostage_info stuff
+ * 
+ * Revision 1.12  1994/10/04  13:15:44  john
+ * Changed PLAY_SOUND to digi_play_sample.
+ * 
+ * Revision 1.11  1994/08/02  14:17:28  mike
+ * Clean up dialog boxes.
+ * 
+ * Revision 1.10  1994/07/22  17:19:17  yuan
+ * Working on dialog box for refuel/repair/material/control centers.
+ * 
+ * Revision 1.9  1994/07/06  15:22:34  john
+ * Added new sound.
+ * 
+ * 
+ * Revision 1.8  1994/07/06  14:26:07  john
+ * Added vclip.
+ * 
+ * Revision 1.7  1994/07/06  13:25:52  john
+ * Added compress hostages functions.
+ * 
+ * Revision 1.6  1994/07/06  12:52:27  john
+ * Fixed warnings.
+ * 
+ * Revision 1.5  1994/07/06  12:43:04  john
+ * Made generic messages for hostages.
+ * 
+ * Revision 1.4  1994/07/06  11:49:01  john
+ * Made adding hostage update current object.
+ * 
+ * Revision 1.3  1994/07/06  10:56:00  john
+ * New structures for hostages.
+ * 
+ * Revision 1.2  1994/07/01  17:57:13  john
+ * First version of not-working hostage system
+ * 
+ * 
+ * Revision 1.1  1994/07/01  14:21:44  john
+ * Initial revision
+ * 
+ * 
+ */
+
+
+#ifdef RCS
+static char rcsid[] = "$Id: ehostage.c,v 1.1 2004-12-19 13:54:27 btb Exp $";
+#endif
+
+#include <stdlib.h>
+#include <stdio.h>
+#ifndef __LINUX__
+#include <conio.h>
+#include <dos.h>
+#include <direct.h>
+#endif 
+#include <string.h>
+#include <math.h>
+
+#include "screens.h"
+#include "inferno.h"
+#include "segment.h"
+#include "editor.h"
+
+#include "timer.h"
+#include "objpage.h"
+#include "fix.h"
+#include "mono.h"
+#include "error.h"
+#include "kdefs.h"
+#include       "object.h"
+#include "polyobj.h"
+#include "game.h"
+#include "powerup.h"
+#include "ai.h"
+#include "hostage.h"
+#include "eobject.h"
+#include "medwall.h"
+#include "eswitch.h"
+#include "medrobot.h"
+#include "key.h"
+#include "bm.h"
+#include "sounds.h"
+#include "centers.h"
+#include "piggy.h"
+
+//-------------------------------------------------------------------------
+// Variables for this module...
+//-------------------------------------------------------------------------
+static UI_WINDOW                               *MainWindow = NULL;
+static UI_GADGET_USERBOX       *HostageViewBox;
+static UI_GADGET_INPUTBOX      *HostageText;
+static UI_GADGET_BUTTON        *QuitButton;
+static int                                             CurrentHostageIndex=-1;
+static int                                             LastHostageIndex=-1;
+
+static fix                     Vclip_animation_time=0;                 // How long the rescue sequence has been playing
+static fix                     Vclip_playback_speed=0;                         // Calculated internally.  Frames/second of vclip.
+static vclip           *Vclip_ptr = NULL;                              // Used for the vclip on monitor
+
+void vclip_play( vclip * vc, fix frame_time )  
+{
+       int bitmapnum;
+
+       if ( vc == NULL )
+               return;
+
+       if ( vc != Vclip_ptr )  {
+               // Start new vclip
+               Vclip_ptr = vc;
+               Vclip_animation_time = 1;
+
+               // Calculate the frame/second of the playback
+               Vclip_playback_speed = fixdiv(i2f(Vclip_ptr->num_frames),Vclip_ptr->play_time);
+       }
+
+       if ( Vclip_animation_time <= 0 )
+               return;
+
+       // Find next bitmap in the vclip
+       bitmapnum = f2i(Vclip_animation_time);
+
+       // Check if vclip is done playing.
+       if (bitmapnum >= Vclip_ptr->num_frames)         {
+               Vclip_animation_time    = 1;                                                                                    // Restart this vclip
+               bitmapnum = 0;
+       }
+
+       PIGGY_PAGE_IN( Vclip_ptr->frames[bitmapnum] );
+       gr_bitmap(0,0,&GameBitmaps[Vclip_ptr->frames[bitmapnum].index] );
+       
+       Vclip_animation_time += fixmul(frame_time, Vclip_playback_speed );
+}
+
+
+
+static char HostageMessage[]  = "  ";
+
+static fix Time;
+
+int SelectPrevHostage()        {
+       int start=0;
+
+       do      {
+               CurrentHostageIndex--;
+               if ( CurrentHostageIndex < 0 ) CurrentHostageIndex = MAX_HOSTAGES-1;
+               start++;
+               if ( start > MAX_HOSTAGES ) break;
+       } while ( !hostage_is_valid( CurrentHostageIndex ) );
+
+       if (hostage_is_valid( CurrentHostageIndex ) )   {
+               Cur_object_index = Hostages[CurrentHostageIndex].objnum;
+       } else {
+               CurrentHostageIndex =-1;
+       }
+       
+       return CurrentHostageIndex;
+}
+
+
+int SelectNextHostage()        {
+       int start=0;
+
+       do      {
+               CurrentHostageIndex++;
+               if ( CurrentHostageIndex >= MAX_HOSTAGES ) CurrentHostageIndex = 0;
+               start++;
+               if ( start > MAX_HOSTAGES ) break;
+       } while ( !hostage_is_valid( CurrentHostageIndex ) );
+
+       if (hostage_is_valid( CurrentHostageIndex ) )   {
+               Cur_object_index = Hostages[CurrentHostageIndex].objnum;
+       } else {
+               CurrentHostageIndex =-1;
+       }
+       
+       return CurrentHostageIndex;
+}
+
+
+int SelectClosestHostage()     {
+       int start=0;
+
+       while ( !hostage_is_valid( CurrentHostageIndex ) )      {
+               CurrentHostageIndex++;
+               if ( CurrentHostageIndex >= MAX_HOSTAGES ) CurrentHostageIndex = 0;
+               start++;
+               if ( start > MAX_HOSTAGES ) break;
+       }
+
+       if (hostage_is_valid( CurrentHostageIndex ) )   {
+               Cur_object_index = Hostages[CurrentHostageIndex].objnum;
+       } else {
+               CurrentHostageIndex =-1;
+       }
+       
+       return CurrentHostageIndex;
+}
+
+
+int PlaceHostage()     {
+       int ctype,i;
+       vms_vector      cur_object_loc;
+
+       //update_due_to_new_segment();
+       compute_segment_center(&cur_object_loc, Cursegp);
+
+       ctype = -1;
+       for (i=0; i<Num_total_object_types; i++ )       {
+               if (ObjType[i] == OL_HOSTAGE )  {
+                       ctype = i;      
+                       break;
+               }
+       }
+
+       Assert( ctype != -1 );
+
+       if (place_object(Cursegp, &cur_object_loc, ctype )==0)  {
+               Int3();         // Debug below
+               i=place_object(Cursegp, &cur_object_loc, ctype );
+               return 1;
+       }
+
+       if (hostage_object_is_valid( Cur_object_index ) )       {
+               CurrentHostageIndex     = Objects[Cur_object_index].id;
+       } else {
+               Int3();         // Get John! (Object should be valid)
+               i=hostage_object_is_valid( Cur_object_index );  // For debugging only
+       }
+
+       return 0;
+}
+
+int CompressHostages()
+{
+       hostage_compress_all();
+
+       return 0;
+}
+
+//@@int SelectPrevVclip()      {
+//@@   if (!hostage_is_valid( CurrentHostageIndex ) )  
+//@@           return 0;
+//@@
+//@@   if ( Hostages[CurrentHostageIndex].type == 0 )
+//@@           Hostages[CurrentHostageIndex].type = N_hostage_types-1;
+//@@   else
+//@@           Hostages[CurrentHostageIndex].type--;
+//@@   
+//@@   if ( Hostages[CurrentHostageIndex].type >= N_hostage_types )
+//@@           Hostages[CurrentHostageIndex].type = 0;
+//@@   
+//@@   return 1;
+//@@}
+//@@
+//@@int SelectNextVclip()      {
+//@@   if (!hostage_is_valid( CurrentHostageIndex ) )  
+//@@           return 0;
+//@@
+//@@   Hostages[CurrentHostageIndex].type++;
+//@@   if ( Hostages[CurrentHostageIndex].type >= N_hostage_types )
+//@@           Hostages[CurrentHostageIndex].type = 0;
+//@@
+//@@   return 1;
+//@@}
+
+int SelectNextFace()
+{
+       int start = Hostages[CurrentHostageIndex].vclip_num;
+       
+       if (!hostage_is_valid( CurrentHostageIndex ) )  
+               return 0;
+
+       do {
+               Hostages[CurrentHostageIndex].vclip_num++;
+               if ( Hostages[CurrentHostageIndex].vclip_num >= MAX_HOSTAGES)
+                       Hostages[CurrentHostageIndex].vclip_num = 0;
+
+               if (Hostages[CurrentHostageIndex].vclip_num == start)
+                       return 0;
+
+       } while (Hostage_face_clip[Hostages[CurrentHostageIndex].vclip_num].num_frames == 0);
+
+       return 1;
+}
+
+int SelectPrevFace()
+{
+       int start = Hostages[CurrentHostageIndex].vclip_num;
+       
+       if (!hostage_is_valid( CurrentHostageIndex ) )  
+               return 0;
+
+       do {
+               Hostages[CurrentHostageIndex].vclip_num--;
+               if ( Hostages[CurrentHostageIndex].vclip_num < 0)
+                       Hostages[CurrentHostageIndex].vclip_num = MAX_HOSTAGES-1;
+
+               if (Hostages[CurrentHostageIndex].vclip_num == start)
+                       return 0;
+
+       } while (Hostage_face_clip[Hostages[CurrentHostageIndex].vclip_num].num_frames == 0);
+
+       return 1;
+}
+
+int PlayHostageSound() {
+       int sound_num;
+
+       if (!hostage_is_valid( CurrentHostageIndex ) )  
+               return 0;
+
+       sound_num = Hostage_face_clip[Hostages[CurrentHostageIndex].vclip_num].sound_num;
+
+       if ( sound_num > -1 )   {
+               digi_play_sample( sound_num, F1_0 );
+       }
+
+       return 1;       
+}
+
+//@@int find_next_hostage_sound()      {
+//@@   int start=0,n;
+//@@
+//@@   n = Hostages[CurrentHostageIndex].sound_num;
+//@@   do      {
+//@@           n++;
+//@@           if ( n < SOUND_HOSTAGE_VOICES ) n = SOUND_HOSTAGE_VOICES+MAX_HOSTAGE_SOUNDS-1;
+//@@           if ( n >= SOUND_HOSTAGE_VOICES+MAX_HOSTAGE_SOUNDS ) n = SOUND_HOSTAGE_VOICES;
+//@@           start++;
+//@@           if ( start > MAX_HOSTAGE_SOUNDS ) break;
+//@@   } while ( Sounds[n] == NULL );
+//@@
+//@@   if ( Sounds[n] == NULL )
+//@@           Hostages[CurrentHostageIndex].sound_num = -1;
+//@@   else    {
+//@@           Hostages[CurrentHostageIndex].sound_num = n;
+//@@           PlayHostageSound();
+//@@   }
+//@@   return 1;
+//@@}
+//@@
+//@@int find_prev_hostage_sound()      {
+//@@   int start=0,n;
+//@@
+//@@   n = Hostages[CurrentHostageIndex].sound_num;
+//@@   do      {
+//@@           n--;
+//@@           if ( n < SOUND_HOSTAGE_VOICES ) n = SOUND_HOSTAGE_VOICES+MAX_HOSTAGE_SOUNDS-1;
+//@@           if ( n >= SOUND_HOSTAGE_VOICES+MAX_HOSTAGE_SOUNDS ) n = SOUND_HOSTAGE_VOICES;
+//@@           start++;
+//@@           if ( start > MAX_HOSTAGE_SOUNDS ) break;
+//@@   } while ( Sounds[n] == NULL );
+//@@
+//@@   if ( Sounds[n] == NULL )
+//@@           Hostages[CurrentHostageIndex].sound_num = -1;
+//@@   else    {
+//@@           Hostages[CurrentHostageIndex].sound_num = n;
+//@@           PlayHostageSound();
+//@@   }
+//@@   return 1;
+//@@}
+
+
+//-------------------------------------------------------------------------
+// Called from the editor... does one instance of the hostage dialog box
+//-------------------------------------------------------------------------
+int do_hostage_dialog()
+{
+       int i;
+
+       // Only open 1 instance of this window...
+       if ( MainWindow != NULL ) return 0;
+       
+       // Close other windows
+       close_all_windows();
+
+       CurrentHostageIndex = 0;
+       SelectClosestHostage();
+
+       // Open a window with a quit button
+       MainWindow = ui_open_window( TMAPBOX_X+10, TMAPBOX_Y+20, 765-TMAPBOX_X, 545-TMAPBOX_Y, WIN_DIALOG );
+       QuitButton = ui_add_gadget_button( MainWindow, 20, 222, 48, 40, "Done", NULL );
+
+       ui_wprintf_at( MainWindow, 10, 32,"&Message:" );
+       HostageText = ui_add_gadget_inputbox( MainWindow, 10, 50, HOSTAGE_MESSAGE_LEN, HOSTAGE_MESSAGE_LEN, HostageMessage );
+
+       // The little box the hostage vclip will play in.
+       HostageViewBox = ui_add_gadget_userbox( MainWindow,10, 90+10, 64, 64 );
+
+       // A bunch of buttons...
+       i = 90;
+//@@   ui_add_gadget_button( MainWindow,155,i,70, 26, "<< Type", SelectPrevVclip );
+//@@   ui_add_gadget_button( MainWindow,155+70,i,70, 26, "Type >>", SelectNextVclip );i += 29;         
+//@@   ui_add_gadget_button( MainWindow,155,i,70, 26, "<< Sound",  find_prev_hostage_sound );
+//@@   ui_add_gadget_button( MainWindow,155+70,i,70, 26, "Sound >>", find_next_hostage_sound );i += 29;                
+
+       ui_add_gadget_button( MainWindow,155,i,70, 26, "<< Face", SelectPrevFace );
+       ui_add_gadget_button( MainWindow,155+70,i,70, 26, "Face >>", SelectNextFace );i += 29;          
+       ui_add_gadget_button( MainWindow,155,i,140, 26, "Play sound", PlayHostageSound );i += 29;               
+       ui_add_gadget_button( MainWindow,155,i,140, 26, "Next Hostage", SelectNextHostage );    i += 29;                
+       ui_add_gadget_button( MainWindow,155,i,140, 26, "Prev Hostage", SelectPrevHostage ); i += 29;           
+       ui_add_gadget_button( MainWindow,155,i,140, 26, "Compress All", CompressHostages ); i += 29;            
+       ui_add_gadget_button( MainWindow,155,i,140, 26, "Delete", ObjectDelete );       i += 29;                
+       ui_add_gadget_button( MainWindow,155,i,140, 26, "Create New", PlaceHostage );   i += 29;                
+       
+       Time = timer_get_fixed_seconds();
+
+       LastHostageIndex = -2;          // Set to some dummy value so everything works ok on the first frame.
+       
+//     if ( CurrentHostageIndex == -1 )
+//             SelectNextHostage();
+
+       return 1;
+
+}
+
+void hostage_close_window()
+{
+       if ( MainWindow!=NULL ) {
+               ui_close_window( MainWindow );
+               MainWindow = NULL;
+       }
+}
+
+void do_hostage_window()
+{
+       fix DeltaTime, Temp;
+
+       if ( MainWindow == NULL ) return;
+
+       SelectClosestHostage();
+
+       //------------------------------------------------------------
+       // Call the ui code..
+       //------------------------------------------------------------
+       ui_button_any_drawn = 0;
+       ui_window_do_gadgets(MainWindow);
+
+       //------------------------------------------------------------
+       // If we change objects, we need to reset the ui code for all
+       // of the radio buttons that control the ai mode.  Also makes
+       // the current AI mode button be flagged as pressed down.
+       //------------------------------------------------------------
+       if (LastHostageIndex != CurrentHostageIndex )   {
+
+               if ( CurrentHostageIndex > -1 ) 
+                       strcpy( HostageText->text, Hostages[CurrentHostageIndex].text );
+               else
+                       strcpy(HostageText->text, " " );
+
+               HostageText->position = strlen(HostageText->text);
+               HostageText->oldposition = HostageText->position;
+               HostageText->status=1;
+               HostageText->first_time = 1;
+
+       }
+
+       //------------------------------------------------------------
+       // If any of the radio buttons that control the mode are set, then
+       // update the cooresponding AI state.
+       //------------------------------------------------------------
+       if ( CurrentHostageIndex > -1 ) 
+               strcpy( Hostages[CurrentHostageIndex].text, HostageText->text );
+
+       //------------------------------------------------------------
+       // A simple frame time counter for spinning the objects...
+       //------------------------------------------------------------
+       Temp = timer_get_fixed_seconds();
+       DeltaTime = Temp - Time;
+       Time = Temp;
+
+       //------------------------------------------------------------
+       // Redraw the object in the little 64x64 box
+       //------------------------------------------------------------
+       if (CurrentHostageIndex > -1 )  {
+               int vclip_num;
+               
+               vclip_num = Hostages[CurrentHostageIndex].vclip_num;
+
+               Assert(vclip_num != -1);
+
+               gr_set_current_canvas( HostageViewBox->canvas );
+
+               if ( vclip_num > -1 )   {
+                       vclip_play( &Hostage_face_clip[vclip_num], DeltaTime ); 
+               } else {
+                       gr_clear_canvas( CGREY );
+               }
+       } else {
+               // no hostage, so just blank out
+               gr_set_current_canvas( HostageViewBox->canvas );
+               gr_clear_canvas( CGREY );
+       }
+
+       //------------------------------------------------------------
+       // If anything changes in the ui system, redraw all the text that
+       // identifies this robot.
+       //------------------------------------------------------------
+       if (ui_button_any_drawn || (LastHostageIndex != CurrentHostageIndex) )  {
+               if ( CurrentHostageIndex > -1 ) {
+                       ui_wprintf_at( MainWindow, 10, 15, "Hostage: %d   Object: %d", CurrentHostageIndex, Hostages[CurrentHostageIndex].objnum );
+                       //@@ui_wprintf_at( MainWindow, 10, 73, "Type: %d   Sound: %d   ", Hostages[CurrentHostageIndex].type, Hostages[CurrentHostageIndex].sound_num );
+                       ui_wprintf_at( MainWindow, 10, 73, "Face: %d   ", Hostages[CurrentHostageIndex].vclip_num);
+               }       else {
+                       ui_wprintf_at( MainWindow, 10, 15, "Hostage: none " );
+                       //@@ui_wprintf_at( MainWindow, 10, 73, "Type:    Sound:       " );
+                       ui_wprintf_at( MainWindow, 10, 73, "Face:         " );
+               }
+               Update_flags |= UF_WORLD_CHANGED;
+       }
+
+       if ( QuitButton->pressed || (last_keypress==KEY_ESC))   {
+               hostage_close_window();
+               return;
+       }               
+
+       LastHostageIndex = CurrentHostageIndex;
+}
+
+
+
diff --git a/main/editor/ehostage.h b/main/editor/ehostage.h
new file mode 100644 (file)
index 0000000..ac6cace
--- /dev/null
@@ -0,0 +1,48 @@
+/*
+THE COMPUTER CODE CONTAINED HEREIN IS THE SOLE PROPERTY OF PARALLAX
+SOFTWARE CORPORATION ("PARALLAX").  PARALLAX, IN DISTRIBUTING THE CODE TO
+END-USERS, AND SUBJECT TO ALL OF THE TERMS AND CONDITIONS HEREIN, GRANTS A
+ROYALTY-FREE, PERPETUAL LICENSE TO SUCH END-USERS FOR USE BY SUCH END-USERS
+IN USING, DISPLAYING,  AND CREATING DERIVATIVE WORKS THEREOF, SO LONG AS
+SUCH USE, DISPLAY OR CREATION IS FOR NON-COMMERCIAL, ROYALTY OR REVENUE
+FREE PURPOSES.  IN NO EVENT SHALL THE END-USER USE THE COMPUTER CODE
+CONTAINED HEREIN FOR REVENUE-BEARING PURPOSES.  THE END-USER UNDERSTANDS
+AND AGREES TO THE TERMS HEREIN AND ACCEPTS THE SAME BY USE OF THIS FILE.  
+COPYRIGHT 1993-1998 PARALLAX SOFTWARE CORPORATION.  ALL RIGHTS RESERVED.
+*/
+/*
+ * $Source: /cvs/cvsroot/d2x/main/editor/ehostage.h,v $
+ * $Revision: 1.1 $
+ * $Author: btb $
+ * $Date: 2004-12-19 13:54:27 $
+ * 
+ * .
+ * 
+ * $Log: not supported by cvs2svn $
+ * Revision 1.1.1.1  1999/06/14 22:02:35  donut
+ * Import of d1x 1.37 source.
+ *
+ * Revision 2.0  1995/02/27  11:35:13  john
+ * Version 2.0! No anonymous unions, Watcom 10.0, with no need
+ * for bitmaps.tbl.
+ * 
+ * Revision 1.2  1994/07/01  17:57:14  john
+ * First version of not-working hostage system
+ * 
+ * 
+ * Revision 1.1  1994/07/01  14:24:41  john
+ * Initial revision
+ * 
+ * 
+ */
+
+
+
+#ifndef _EHOSTAGE_H
+#define _EHOSTAGE_H
+
+extern int do_hostage_dialog();
+extern void hostage_close_window();
+extern void do_hostage_window();
+
+#endif
diff --git a/main/editor/elight.c b/main/editor/elight.c
new file mode 100644 (file)
index 0000000..9559cda
--- /dev/null
@@ -0,0 +1,404 @@
+/*
+THE COMPUTER CODE CONTAINED HEREIN IS THE SOLE PROPERTY OF PARALLAX
+SOFTWARE CORPORATION ("PARALLAX").  PARALLAX, IN DISTRIBUTING THE CODE TO
+END-USERS, AND SUBJECT TO ALL OF THE TERMS AND CONDITIONS HEREIN, GRANTS A
+ROYALTY-FREE, PERPETUAL LICENSE TO SUCH END-USERS FOR USE BY SUCH END-USERS
+IN USING, DISPLAYING,  AND CREATING DERIVATIVE WORKS THEREOF, SO LONG AS
+SUCH USE, DISPLAY OR CREATION IS FOR NON-COMMERCIAL, ROYALTY OR REVENUE
+FREE PURPOSES.  IN NO EVENT SHALL THE END-USER USE THE COMPUTER CODE
+CONTAINED HEREIN FOR REVENUE-BEARING PURPOSES.  THE END-USER UNDERSTANDS
+AND AGREES TO THE TERMS HEREIN AND ACCEPTS THE SAME BY USE OF THIS FILE.  
+COPYRIGHT 1993-1998 PARALLAX SOFTWARE CORPORATION.  ALL RIGHTS RESERVED.
+*/
+/*
+ * $Source: /cvs/cvsroot/d2x/main/editor/elight.c,v $
+ * $Revision: 1.1 $
+ * $Author: btb $
+ * $Date: 2004-12-19 13:54:27 $
+ * 
+ * Editor lighting functions.
+ * 
+ * $Log: not supported by cvs2svn $
+ * Revision 1.1.1.1  1999/06/14 22:02:55  donut
+ * Import of d1x 1.37 source.
+ *
+ * Revision 2.0  1995/02/27  11:35:16  john
+ * Version 2.0! No anonymous unions, Watcom 10.0, with no need
+ * for bitmaps.tbl.
+ * 
+ * Revision 1.21  1994/06/14  16:59:23  mike
+ * Fix references to tmap_num2, must strip off orientation bits.
+ * 
+ * Revision 1.20  1994/05/31  12:31:57  mike
+ * fix bug in lighting -- WALL_IS_DOORWAY return value getting ignored,
+ * almost never recursively propagated light.
+ * 
+ * Revision 1.19  1994/05/19  23:35:12  mike
+ * Support uv coordinates in range 0..1.0.
+ * 
+ * Revision 1.18  1994/05/16  12:05:29  john
+ * Made texturemap light be a fix from 0 to 1.
+ * 
+ * Revision 1.17  1994/05/14  18:00:38  matt
+ * Got rid of externs in source (non-header) files
+ * 
+ * Revision 1.16  1994/05/03  11:04:27  mike
+ * Add function to select edge.
+ * 
+ * Revision 1.15  1994/04/20  17:29:11  yuan
+ * Fixed bug where tmaps above 256 don't light properly.
+ * (duh!)
+ * 
+ * Revision 1.14  1994/03/22  14:20:46  yuan
+ * Made texture map 1 also cast light.  (Cumulative with tmap_num2)
+ * 
+ * Revision 1.13  1994/03/15  16:34:14  yuan
+ * Fixed bm loader (might have some changes in walls and switches)
+ * 
+ * Revision 1.12  1994/02/22  18:55:10  yuan
+ * Ambient lighting "shines" on doors too!
+ * 
+ * Revision 1.11  1994/02/17  12:05:55  matt
+ * Got rid of warnings
+ * 
+ * Revision 1.10  1994/02/16  22:28:03  mike
+ * fix ambient lighting and smoothing.
+ * 
+ * Revision 1.9  1994/02/14  12:05:42  mike
+ * change segment data structure.
+ * 
+ * Revision 1.8  1994/01/26  17:27:45  yuan
+ * Still not perfected ambient lighting
+ * 
+ * Revision 1.7  1994/01/25  17:58:08  yuan
+ * Added ambient lighting, and also added fixing bogus segments
+ * functions to the editor... (they don't work fully... need to
+ * check out seguvs.c
+ * 
+ * Revision 1.6  1994/01/24  11:46:10  yuan
+ * *** empty log message ***
+ * 
+ * Revision 1.5  1994/01/24  11:03:05  yuan
+ * Set lgiht maximum added... Changes are still in progress
+ * 
+ * Revision 1.4  1994/01/18  19:16:07  yuan
+ * Added assign default to lighting pad.
+ * 
+ * Revision 1.3  1993/12/17  12:26:00  mike
+ * Add functions for setting light values on whole segment at once.
+ * 
+ * Revision 1.2  1993/12/16  16:56:12  mike
+ * Add new texture map lighting control functions.
+ * 
+ * Revision 1.1  1993/12/16  13:21:50  mike
+ * Initial revision
+ * 
+ * 
+ */
+
+
+#ifdef RCS
+static char rcsid[] = "$Id: elight.c,v 1.1 2004-12-19 13:54:27 btb Exp $";
+#endif
+
+#include <stdio.h>
+//#include <stdlib.h>
+//#include <stdarg.h>
+//#include <math.h>
+//#include <string.h>
+
+#include "inferno.h"
+#include "segment.h"
+#include "editor.h"
+#include "seguvs.h"
+
+#include "wall.h"
+
+#include "textures.h"
+
+#include "fix.h"
+#include "mono.h"
+#include "error.h"
+#include "kdefs.h"
+#include "gameseg.h"
+
+#include "texmap.h"
+
+// -----------------------------------------------------------------------------
+//     Return light intensity at an instance of a vertex on a side in a segment.
+fix get_light_intensity(segment *segp, int sidenum, int vert)
+{
+       Assert(sidenum <= MAX_SIDES_PER_SEGMENT);
+       Assert(vert <= 3);
+
+       return segp->sides[sidenum].uvls[vert].l;
+}
+
+// -----------------------------------------------------------------------------
+//     Set light intensity at a vertex, saturating in .5 to 15.5
+void set_light_intensity(segment *segp, int sidenum, int vert, fix intensity)
+{
+       Assert(sidenum <= MAX_SIDES_PER_SEGMENT);
+       Assert(vert <= 3);
+
+       if (intensity < MIN_LIGHTING_VALUE)
+               intensity = MIN_LIGHTING_VALUE;
+
+       if (intensity > MAX_LIGHTING_VALUE)
+               intensity = MAX_LIGHTING_VALUE;
+
+       segp->sides[sidenum].uvls[vert].l = intensity;
+
+       Update_flags |= UF_WORLD_CHANGED;
+}
+
+
+// -----------------------------------------------------------------------------
+//     Add light intensity to a vertex, saturating in .5 to 15.5
+void add_light_intensity(segment *segp, int sidenum, int vert, fix intensity)
+{
+//     fix     new_intensity;
+
+       set_light_intensity(segp, sidenum, vert, segp->sides[sidenum].uvls[vert].l + intensity);
+}
+
+
+// -----------------------------------------------------------------------------
+//     Recursively apply light to segments.
+//     If current side is a wall, apply light there.
+//     If not a wall, apply light to child through that wall.
+//     Notes:
+//             It is possible to enter a segment twice by taking different paths.  It is easy
+//             to prevent this by maintaining a list of visited segments, but it is important
+//             to reach segments with the greatest light intensity.  This can be done by doing
+//             a breadth-first-search, or by storing the applied intensity with a visited segment,
+//             and if the current intensity is brighter, then apply the difference between it and
+//             the previous intensity.
+//             Note that it is also possible to visit the original light-casting segment, for example
+//             going from segment 0 to 2, then from 2 to 0.  This is peculiar and probably not
+//             desired, but not entirely invalid.  2 reflects some light back to 0.
+void apply_light_intensity(segment *segp, int sidenum, fix intensity, int depth)
+{
+       int     wid_result;
+
+       if (intensity == 0)
+               return;
+
+       wid_result = WALL_IS_DOORWAY(segp, sidenum);
+       if ((wid_result != WID_FLY_FLAG) && (wid_result != WID_NO_WALL)) {
+               int     v;
+               for (v=0; v<4; v++)                                                     // add light to this wall
+                       add_light_intensity(segp, sidenum, v, intensity);
+               return;                                                                         // we return because there is a wall here, and light does not shine through walls
+       }
+
+       //      No wall here, so apply light recursively
+       if (depth < 3) {
+               int     s;
+               for (s=0; s<MAX_SIDES_PER_SEGMENT; s++)
+                       apply_light_intensity(&Segments[segp->children[sidenum]], s, intensity/3, depth+1);
+       }
+
+}
+
+// -----------------------------------------------------------------------------
+//     Top level recursive function for applying light.
+//     Calls apply_light_intensity.
+//     Uses light value on segp:sidenum (tmap_num2 defines light value) and applies
+//     the associated intensity to segp.  It calls apply_light_intensity to apply intensity/3
+//     to all neighbors.  apply_light_intensity recursively calls itself to apply light to
+//     subsequent neighbors (and forming loops, see above).
+void propagate_light_intensity(segment *segp, int sidenum) 
+{
+       int             v,s;
+       fix             intensity;
+       short           texmap;
+
+       intensity = 0;
+       texmap = segp->sides[sidenum].tmap_num;
+       intensity += TmapInfo[texmap].lighting;
+       texmap = (segp->sides[sidenum].tmap_num2) & 0x3fff;
+       intensity += TmapInfo[texmap].lighting;
+
+       if (intensity > 0) {
+               for (v=0; v<4; v++)
+                       add_light_intensity(segp, sidenum, v, intensity);
+       
+               //      Now, for all sides which are not the same as sidenum (the side casting the light),
+               //      add a light value to them (if they have no children, ie, they have a wall there).
+               for (s=0; s<MAX_SIDES_PER_SEGMENT; s++)
+                       if (s != sidenum)
+                               apply_light_intensity(segp, s, intensity/2, 1);
+       }
+
+}
+
+
+// -----------------------------------------------------------------------------
+//     Highest level function, bound to a key.  Apply ambient light to all segments based
+//     on user-defined light sources.
+int LightAmbientLighting()
+{
+       int seg, side;
+
+       for (seg=0; seg<=Highest_segment_index; seg++)
+               for (side=0;side<MAX_SIDES_PER_SEGMENT;side++)
+                       propagate_light_intensity(&Segments[seg], side);
+       return 0;
+}
+
+
+// -----------------------------------------------------------------------------
+int LightSelectNextVertex(void)
+{
+       Curvert++;
+       if (Curvert >= 4)
+               Curvert = 0;
+
+       Update_flags |= UF_WORLD_CHANGED;
+
+       return  1;
+}
+
+// -----------------------------------------------------------------------------
+int LightSelectNextEdge(void)
+{
+       Curedge++;
+       if (Curedge >= 4)
+               Curedge = 0;
+
+       Update_flags |= UF_WORLD_CHANGED;
+
+       return  1;
+}
+
+// -----------------------------------------------------------------------------
+//     Copy intensity from current vertex to all other vertices on side.
+int LightCopyIntensity(void)
+{
+       int     v,intensity;
+
+       intensity = get_light_intensity(Cursegp, Curside, Curvert);
+
+       for (v=0; v<4; v++)
+               if (v != Curvert)
+                       set_light_intensity(Cursegp, Curside, v, intensity);
+
+       return  1;
+}
+
+// -----------------------------------------------------------------------------
+//     Copy intensity from current vertex to all other vertices on side.
+int LightCopyIntensitySegment(void)
+{
+       int     s,v,intensity;
+
+       intensity = get_light_intensity(Cursegp, Curside, Curvert);
+
+       for (s=0; s<MAX_SIDES_PER_SEGMENT; s++)
+               for (v=0; v<4; v++)
+                       if ((s != Curside) || (v != Curvert))
+                               set_light_intensity(Cursegp, s, v, intensity);
+
+       return  1;
+}
+
+// -----------------------------------------------------------------------------
+int LightDecreaseLightVertex(void)
+{
+       set_light_intensity(Cursegp, Curside, Curvert, get_light_intensity(Cursegp, Curside, Curvert)-F1_0/NUM_LIGHTING_LEVELS);
+
+       return  1;
+}
+
+// -----------------------------------------------------------------------------
+int LightIncreaseLightVertex(void)
+{
+       set_light_intensity(Cursegp, Curside, Curvert, get_light_intensity(Cursegp, Curside, Curvert)+F1_0/NUM_LIGHTING_LEVELS);
+
+       return  1;
+}
+
+// -----------------------------------------------------------------------------
+int LightDecreaseLightSide(void)
+{
+       int     v;
+
+       for (v=0; v<4; v++)
+               set_light_intensity(Cursegp, Curside, v, get_light_intensity(Cursegp, Curside, v)-F1_0/NUM_LIGHTING_LEVELS);
+
+       return  1;
+}
+
+// -----------------------------------------------------------------------------
+int LightIncreaseLightSide(void)
+{
+       int     v;
+
+       for (v=0; v<4; v++)
+               set_light_intensity(Cursegp, Curside, v, get_light_intensity(Cursegp, Curside, v)+F1_0/NUM_LIGHTING_LEVELS);
+
+       return  1;
+}
+
+// -----------------------------------------------------------------------------
+int LightDecreaseLightSegment(void)
+{
+       int     s,v;
+
+       for (s=0; s<MAX_SIDES_PER_SEGMENT; s++)
+               for (v=0; v<4; v++)
+                       set_light_intensity(Cursegp, s, v, get_light_intensity(Cursegp, s, v)-F1_0/NUM_LIGHTING_LEVELS);
+
+       return  1;
+}
+
+// -----------------------------------------------------------------------------
+int LightIncreaseLightSegment(void)
+{
+       int     s,v;
+
+       for (s=0; s<MAX_SIDES_PER_SEGMENT; s++)
+               for (v=0; v<4; v++)
+                       set_light_intensity(Cursegp, s, v, get_light_intensity(Cursegp, s, v)+F1_0/NUM_LIGHTING_LEVELS);
+
+       return  1;
+}
+
+// -----------------------------------------------------------------------------
+int LightSetDefault(void)
+{
+       int     v;
+
+       for (v=0; v<4; v++)
+               set_light_intensity(Cursegp, Curside, v, DEFAULT_LIGHTING);
+
+       return  1;
+}
+
+
+// -----------------------------------------------------------------------------
+int LightSetMaximum(void)
+{
+       int     v;
+
+       for (v=0; v<4; v++)
+               set_light_intensity(Cursegp, Curside, v, (NUM_LIGHTING_LEVELS-1)*F1_0/NUM_LIGHTING_LEVELS);
+
+       return  1;
+}
+
+
+// -----------------------------------------------------------------------------
+int LightSetDefaultAll(void)
+{
+
+       assign_default_lighting_all();
+
+       Update_flags |= UF_WORLD_CHANGED;
+
+       return  1;
+}
+
+
+
diff --git a/main/editor/eobject.c b/main/editor/eobject.c
new file mode 100644 (file)
index 0000000..431b2b9
--- /dev/null
@@ -0,0 +1,1138 @@
+/*
+THE COMPUTER CODE CONTAINED HEREIN IS THE SOLE PROPERTY OF PARALLAX
+SOFTWARE CORPORATION ("PARALLAX").  PARALLAX, IN DISTRIBUTING THE CODE TO
+END-USERS, AND SUBJECT TO ALL OF THE TERMS AND CONDITIONS HEREIN, GRANTS A
+ROYALTY-FREE, PERPETUAL LICENSE TO SUCH END-USERS FOR USE BY SUCH END-USERS
+IN USING, DISPLAYING,  AND CREATING DERIVATIVE WORKS THEREOF, SO LONG AS
+SUCH USE, DISPLAY OR CREATION IS FOR NON-COMMERCIAL, ROYALTY OR REVENUE
+FREE PURPOSES.  IN NO EVENT SHALL THE END-USER USE THE COMPUTER CODE
+CONTAINED HEREIN FOR REVENUE-BEARING PURPOSES.  THE END-USER UNDERSTANDS
+AND AGREES TO THE TERMS HEREIN AND ACCEPTS THE SAME BY USE OF THIS FILE.  
+COPYRIGHT 1993-1998 PARALLAX SOFTWARE CORPORATION.  ALL RIGHTS RESERVED.
+*/
+/*
+ * $Source: /cvs/cvsroot/d2x/main/editor/eobject.c,v $
+ * $Revision: 1.1 $
+ * $Author: btb $
+ * $Date: 2004-12-19 13:54:27 $
+ * 
+ * Editor object functions.
+ * 
+ * $Log: not supported by cvs2svn $
+ * Revision 1.1.1.1  1999/06/14 22:03:00  donut
+ * Import of d1x 1.37 source.
+ *
+ * Revision 2.0  1995/02/27  11:35:14  john
+ * Version 2.0! No anonymous unions, Watcom 10.0, with no need
+ * for bitmaps.tbl.
+ * 
+ * Revision 1.93  1995/02/22  15:09:04  allender
+ * remove anonymous unions from object structure
+ * 
+ * Revision 1.92  1995/01/12  12:10:32  yuan
+ * Added coop object capability.
+ * 
+ * Revision 1.91  1994/12/20  17:57:02  yuan
+ * Multiplayer object stuff.
+ * 
+ * Revision 1.90  1994/11/27  23:17:49  matt
+ * Made changes for new mprintf calling convention
+ * 
+ * Revision 1.89  1994/11/17  14:48:06  mike
+ * validation functions moved from editor to game.
+ * 
+ * Revision 1.88  1994/11/14  11:40:03  mike
+ * fix default robot behavior.
+ * 
+ * Revision 1.87  1994/10/25  10:51:31  matt
+ * Vulcan cannon powerups now contain ammo count
+ * 
+ * Revision 1.86  1994/10/23  02:11:40  matt
+ * Got rid of obsolete hostage_info stuff
+ * 
+ * Revision 1.85  1994/10/17  21:35:32  matt
+ * Added support for new Control Center/Main Reactor
+ * 
+ * Revision 1.84  1994/10/10  17:23:13  mike
+ * Verify that not placing too many player objects.
+ * 
+ * Revision 1.83  1994/09/24  14:15:35  mike
+ * Custom colored object support.
+ * 
+ * Revision 1.82  1994/09/15  22:58:12  matt
+ * Made new objects be oriented to their segment
+ * Added keypad function to flip an object upside-down
+ * 
+ * Revision 1.81  1994/09/01  10:58:41  matt
+ * Sizes for powerups now specified in bitmaps.tbl; blob bitmaps now plot
+ * correctly if width & height of bitmap are different.
+ * 
+ * Revision 1.80  1994/08/25  21:58:14  mike
+ * Write ObjectSelectPrevInMine and something else, I think...
+ * 
+ * Revision 1.79  1994/08/16  20:19:54  mike
+ * Make STILL default (from CHASE_OBJECT).
+ * 
+ * Revision 1.78  1994/08/14  23:15:45  matt
+ * Added animating bitmap hostages, and cleaned up vclips a bit
+ * 
+ * Revision 1.77  1994/08/13  14:58:43  matt
+ * Finished adding support for miscellaneous objects
+ * 
+ * Revision 1.76  1994/08/12  22:24:58  matt
+ * Generalized polygon objects (such as control center)
+ * 
+ * Revision 1.75  1994/08/09  16:06:11  john
+ * Added the ability to place players.  Made old
+ * Player variable be ConsoleObject.
+ * 
+ * Revision 1.74  1994/08/05  18:18:55  matt
+ * Made object rotation have 4x resolution, and SHIFT+rotate do old resolution.
+ * 
+ * Revision 1.73  1994/08/01  13:30:56  matt
+ * Made fvi() check holes in transparent walls, and changed fvi() calling
+ * parms to take all input data in query structure.
+ * 
+ */
+
+
+#ifdef RCS
+static char rcsid[] = "$Id: eobject.c,v 1.1 2004-12-19 13:54:27 btb Exp $";
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <math.h>
+#include <string.h>
+
+#include "inferno.h"
+#include "segment.h"
+#include "editor.h"
+
+#include "objpage.h"
+#include "fix.h"
+#include "mono.h"
+#include "error.h"
+#include "kdefs.h"
+#include       "object.h"
+#include "polyobj.h"
+#include "game.h"
+#include "ai.h"
+#include "bm.h"
+#include "3d.h"                //      For g3_point_to_vec
+#include       "fvi.h"
+
+#include "powerup.h"
+#include "fuelcen.h"
+#include "hostage.h"
+#include "medrobot.h"
+#include "player.h"
+#include "gameseg.h"
+
+#define        OBJ_SCALE               (F1_0/2)
+#define        OBJ_DEL_SIZE    (F1_0/2)
+
+#define ROTATION_UNIT (4096/4)
+
+//segment              *Cur_object_seg = -1;
+
+void show_objects_in_segment(segment *sp)
+{
+       short           objid;
+
+       mprintf((0,"Objects in segment #%i: ",sp-Segments));
+
+       objid = sp->objects;
+       while (objid != -1) {
+               mprintf((0,"%2i ",objid));
+               objid = Objects[objid].next;
+       }
+       mprintf((0,"\n"));
+}
+
+//returns the number of the first object in a segment, skipping the player
+int get_first_object(segment *seg)
+{
+       int id;
+
+       id = seg->objects;
+
+       if (id == (ConsoleObject-Objects))
+               id = Objects[id].next;
+
+       return id;
+}
+
+//returns the number of the next object in a segment, skipping the player
+int get_next_object(segment *seg,int id)
+{
+       if (id==-1 || (id=Objects[id].next)==-1)
+               return get_first_object(seg);
+
+       if (id == (ConsoleObject-Objects))
+               return get_next_object(seg,id);
+
+       return id;
+}
+
+
+//@@// ------------------------------------------------------------------------------------------------------
+//@@// this should be called whenever the current segment may have changed
+//@@// If Cur_object_seg != Cursegp, then update various variables.
+//@@// this used to be called update_due_to_new_segment()
+//@@void ObjectUpdateCurrent(void)
+//@@{
+//@@   if (Cur_object_seg != Cursegp) {
+//@@           Cur_object_seg = Cursegp;
+//@@           Cur_object_index = get_first_object(Cur_object_seg);
+//@@           Update_flags |= UF_WORLD_CHANGED;
+//@@   }
+//@@
+//@@}
+
+//     ------------------------------------------------------------------------------------
+int place_object(segment *segp, vms_vector *object_pos, int object_type)
+{
+        short objnum=0;
+       object *obj;
+       vms_matrix seg_matrix;
+
+       med_extract_matrix_from_segment(segp,&seg_matrix);
+
+       switch(ObjType[object_type]) {
+
+               case OL_HOSTAGE:
+
+                       objnum = obj_create(OBJ_HOSTAGE, -1, 
+                                       segp-Segments,object_pos,&seg_matrix,HOSTAGE_SIZE,
+                                       CT_NONE,MT_NONE,RT_HOSTAGE);
+
+                       if ( objnum < 0 )
+                               return 0;
+
+                       obj = &Objects[objnum];
+
+                       // Fill in obj->id and other hostage info
+                       hostage_init_info( objnum );
+               
+                       obj->control_type = CT_POWERUP;
+                       
+                       obj->rtype.vclip_info.vclip_num = Hostage_vclip_num[ObjId[object_type]];
+                       obj->rtype.vclip_info.frametime = Vclip[obj->rtype.vclip_info.vclip_num].frame_time;
+                       obj->rtype.vclip_info.framenum = 0;
+
+                       break;
+
+               case OL_ROBOT:
+
+                       objnum = obj_create(OBJ_ROBOT,ObjId[object_type],segp-Segments,object_pos,
+                               &seg_matrix,Polygon_models[Robot_info[ObjId[object_type]].model_num].rad,
+                               CT_AI,MT_PHYSICS,RT_POLYOBJ);
+
+                       if ( objnum < 0 )
+                               return 0;
+
+                       obj = &Objects[objnum];
+
+                       //Set polygon-object-specific data 
+
+                       obj->rtype.pobj_info.model_num = Robot_info[obj->id].model_num;
+                       obj->rtype.pobj_info.subobj_flags = 0;
+
+                       //set Physics info
+               
+                       obj->mtype.phys_info.mass = Robot_info[obj->id].mass;
+                       obj->mtype.phys_info.drag = Robot_info[obj->id].drag;
+
+                       obj->mtype.phys_info.flags |= (PF_LEVELLING);
+
+                       obj->shields = Robot_info[obj->id].strength;
+
+                       {       int     hide_segment;
+                       if (Markedsegp)
+                               hide_segment = Markedsegp-Segments;
+                       else
+                               hide_segment = -1;
+                       //      robots which lunge forward to attack cannot have behavior type still.
+                       if (Robot_info[obj->id].attack_type)
+                               init_ai_object(obj-Objects, AIB_NORMAL, hide_segment);
+                       else
+                               init_ai_object(obj-Objects, AIB_STILL, hide_segment);
+                       }
+                       break;
+
+               case OL_POWERUP:
+
+                       objnum = obj_create(OBJ_POWERUP,ObjId[object_type],
+                                       segp-Segments,object_pos,&seg_matrix,Powerup_info[ObjId[object_type]].size,
+                                       CT_POWERUP,MT_NONE,RT_POWERUP);
+
+                       if ( objnum < 0 )
+                               return 0;
+
+                       obj = &Objects[objnum];
+
+                       //set powerup-specific data
+
+                       obj->rtype.vclip_info.vclip_num = Powerup_info[obj->id].vclip_num;
+                       obj->rtype.vclip_info.frametime = Vclip[obj->rtype.vclip_info.vclip_num].play_time/Vclip[obj->rtype.vclip_info.vclip_num].num_frames;
+                       obj->rtype.vclip_info.framenum = 0;
+
+                       if (obj->id == POW_VULCAN_WEAPON)
+                               obj->ctype.powerup_info.count = VULCAN_WEAPON_AMMO_AMOUNT;
+                       else
+                               obj->ctype.powerup_info.count = 1;
+
+                       break;
+
+               case OL_CLUTTER:
+               case OL_CONTROL_CENTER: 
+               {
+                       int obj_type,control_type;
+
+                       if (ObjType[object_type]==OL_CONTROL_CENTER) {
+                               obj_type = OBJ_CNTRLCEN;
+                               control_type = CT_CNTRLCEN;
+                       }
+                       else {
+                               obj_type = OBJ_CLUTTER;
+                               control_type = CT_NONE;
+                       }
+
+                       objnum = obj_create(obj_type,object_type,segp-Segments,object_pos,
+                                       &seg_matrix,Polygon_models[ObjId[object_type]].rad,
+                                       control_type,MT_NONE,RT_POLYOBJ);
+
+                       if ( objnum < 0 )
+                               return 0;
+
+                       obj = &Objects[objnum];
+
+                       obj->shields = ObjStrength[object_type];
+
+                       //Set polygon-object-specific data 
+                       obj->shields = ObjStrength[object_type];
+                       obj->rtype.pobj_info.model_num = ObjId[object_type];
+                       obj->rtype.pobj_info.subobj_flags = 0;
+
+                       break;
+               }
+
+               case OL_PLAYER: {
+                       objnum = obj_create(OBJ_PLAYER,ObjId[object_type],segp-Segments,object_pos,
+                               &seg_matrix,Polygon_models[Player_ship->model_num].rad,
+                               CT_NONE,MT_PHYSICS,RT_POLYOBJ);
+
+                       if ( objnum < 0 )
+                               return 0;
+
+                       obj = &Objects[objnum];
+
+                       //Set polygon-object-specific data 
+
+                       obj->rtype.pobj_info.model_num = Player_ship->model_num;
+                       obj->rtype.pobj_info.subobj_flags = 0;
+                       //for (i=0;i<MAX_SUBMODELS;i++)
+                       //      vm_angvec_zero(&obj->rtype.pobj_info.anim_angles[i]);
+
+                       //set Physics info
+
+                       vm_vec_zero(&obj->mtype.phys_info.velocity);
+                       obj->mtype.phys_info.mass = Player_ship->mass;
+                       obj->mtype.phys_info.drag = Player_ship->drag;
+                       obj->mtype.phys_info.flags |= PF_TURNROLL | PF_LEVELLING | PF_WIGGLE;
+                       obj->shields = i2f(100);
+
+                       break;
+               }
+               default:
+                       break;  
+               }
+
+       Cur_object_index = objnum;
+       //Cur_object_seg = Cursegp;
+
+       show_objects_in_segment(Cursegp);               //mprintf the objects
+
+       Update_flags |= UF_WORLD_CHANGED;
+
+       return 1;
+}
+
+//     ------------------------------------------------------------------------------------------------------
+//     Count number of player objects, return value.
+int compute_num_players(void)
+{
+       int     i, count = 0;
+
+       for (i=0; i<=Highest_object_index; i++)
+               if (Objects[i].type == OBJ_PLAYER)
+                       count++;
+
+       return count;
+
+}
+
+int ObjectMakeCoop(void)
+{
+       Assert(Cur_object_index != -1);
+       Assert(Cur_object_index < MAX_OBJECTS);
+//     Assert(Objects[Cur_object_index.type == OBJ_PLAYER);
+
+       if (Objects[Cur_object_index].type == OBJ_PLAYER ) {
+               Objects[Cur_object_index].type = OBJ_COOP;
+               editor_status("You just made a player object COOPERATIVE");
+       } else
+               editor_status("This is not a player object");
+
+       return 1;
+}
+
+//     ------------------------------------------------------------------------------------------------------
+//     Place current object at center of current segment.
+int ObjectPlaceObject(void)
+{
+       int     old_cur_object_index;
+       int     rval;
+
+       vms_vector      cur_object_loc;
+
+#ifdef SHAREWARE
+       if (ObjType[Cur_robot_type] == OL_PLAYER) {
+               int num_players = compute_num_players();
+               Assert(num_players <= MAX_PLAYERS);
+               if (num_players == MAX_PLAYERS) {
+                       editor_status("Can't place player object.  Already %i players.", MAX_PLAYERS);
+                       return -1;
+               }
+       }
+#endif
+
+#ifndef SHAREWARE
+       if (ObjType[Cur_robot_type] == OL_PLAYER) {
+               int num_players = compute_num_players();
+               Assert(num_players <= MAX_MULTI_PLAYERS);
+               if (num_players > MAX_PLAYERS)
+                       editor_status("You just placed a cooperative player object");
+               if (num_players == MAX_MULTI_PLAYERS) {
+                       editor_status("Can't place player object.  Already %i players.", MAX_MULTI_PLAYERS);
+                       return -1;
+               }
+       }
+#endif
+
+       //update_due_to_new_segment();
+       compute_segment_center(&cur_object_loc, Cursegp);
+
+       old_cur_object_index = Cur_object_index;
+       rval = place_object(Cursegp, &cur_object_loc, Cur_robot_type);
+
+       if (old_cur_object_index != Cur_object_index)
+               Objects[Cur_object_index].rtype.pobj_info.tmap_override = -1;
+
+       return rval;
+
+}
+
+//     ------------------------------------------------------------------------------------------------------
+//     Place current object at center of current segment.
+int ObjectPlaceObjectTmap(void)
+{
+       int     rval, old_cur_object_index;
+       vms_vector      cur_object_loc;
+
+       //update_due_to_new_segment();
+       compute_segment_center(&cur_object_loc, Cursegp);
+
+       old_cur_object_index = Cur_object_index;
+       rval = place_object(Cursegp, &cur_object_loc, Cur_robot_type);
+
+       if ((Cur_object_index != old_cur_object_index) && (Objects[Cur_object_index].render_type == RT_POLYOBJ))
+               Objects[Cur_object_index].rtype.pobj_info.tmap_override = CurrentTexture;
+       else
+               editor_status("Unable to apply current texture map to this object.");
+       
+       return rval;
+}
+
+//     ------------------------------------------------------------------------------------------------------
+int ObjectSelectNextinSegment(void)
+{
+       int     id;
+       segment *objsegp;
+
+
+       //update_due_to_new_segment();
+
+       //Assert(Cur_object_seg == Cursegp);
+
+       if (Cur_object_index == -1) {
+               objsegp = Cursegp;
+               Cur_object_index = objsegp->objects;
+       } else {
+               objsegp = Cursegp;
+               if (Objects[Cur_object_index].segnum != Cursegp-Segments)
+                       Cur_object_index = objsegp->objects;
+       }
+
+
+       //Debug: make sure current object is in current segment
+       for (id=objsegp->objects;(id != Cur_object_index)  && (id != -1);id=Objects[id].next);
+       Assert(id == Cur_object_index);         //should have found object
+
+       //      Select the next object, wrapping back to start if we are at the end of the linked list for this segment.
+       if (id != -1)
+               Cur_object_index = get_next_object(objsegp,Cur_object_index);
+
+       //mprintf((0,"Cur_object_index == %i\n", Cur_object_index));
+
+       Update_flags |= UF_WORLD_CHANGED;
+
+       return 1;
+
+}
+
+//Moves to next object in the mine, skipping the player
+int ObjectSelectNextInMine()
+{      int i;
+       for (i=0;i<MAX_OBJECTS;i++) {
+               Cur_object_index++;
+               if (Cur_object_index>= MAX_OBJECTS ) Cur_object_index= 0;
+
+               if ((Objects[Cur_object_index ].type != OBJ_NONE) && (Cur_object_index != (ConsoleObject-Objects)) )    {
+                       Cursegp = &Segments[Objects[Cur_object_index ].segnum];
+                       med_create_new_segment_from_cursegp();
+                       //Cur_object_seg = Cursegp;
+                       return 1;
+               }
+       }
+       Cur_object_index = -1;
+
+       Update_flags |= UF_WORLD_CHANGED;
+
+       return 0;
+}
+
+//Moves to next object in the mine, skipping the player
+int ObjectSelectPrevInMine()
+{      int i;
+       for (i=0;i<MAX_OBJECTS;i++) {
+               Cur_object_index--;
+               if (Cur_object_index < 0 )
+                       Cur_object_index = MAX_OBJECTS-1;
+
+               if ((Objects[Cur_object_index ].type != OBJ_NONE) && (Cur_object_index != (ConsoleObject-Objects)) )    {
+                       Cursegp = &Segments[Objects[Cur_object_index ].segnum];
+                       med_create_new_segment_from_cursegp();
+                       //Cur_object_seg = Cursegp;
+                       return 1;
+               }
+       }
+       Cur_object_index = -1;
+
+       Update_flags |= UF_WORLD_CHANGED;
+
+       return 0;
+}
+
+//     ------------------------------------------------------------------------------------------------------
+//     Delete current object, if it exists.
+//     If it doesn't exist, reformat Matt's hard disk, even if he is in Boston.
+int ObjectDelete(void)
+{
+
+       if (Cur_object_index != -1) {
+               int delete_objnum;
+
+               delete_objnum = Cur_object_index;
+
+               ObjectSelectNextinSegment();
+
+               obj_delete(delete_objnum);
+
+               if (delete_objnum == Cur_object_index)
+                       Cur_object_index = -1;
+
+               Update_flags |= UF_WORLD_CHANGED;
+       }
+
+       return 1;
+}
+
+//     -----------------------------------------------------------------------------------------------------------------
+//     Object has moved to another segment, (or at least poked through).
+//     If still in mine, that is legal, so relink into new segment.
+//     Return value:   0 = in mine, 1 = not in mine
+int move_object_within_mine(object * obj, vms_vector *newpos )
+{
+       int segnum;
+
+       for (segnum=0;segnum <= Highest_segment_index; segnum++) {
+               segmasks        result = get_seg_masks(&obj->pos,segnum,0);
+               if (result.centermask == 0) {
+                       int     fate;
+                       fvi_info        hit_info;
+                       fvi_query fq;
+
+                       //      See if the radius pokes through any wall.
+                       fq.p0                                           = &obj->pos;
+                       fq.startseg                             = obj->segnum;
+                       fq.p1                                           = newpos;
+                       fq.rad                                  = obj->size;
+                       fq.thisobjnum                   = -1;
+                       fq.ignore_obj_list      = NULL;
+                       fq.flags                                        = 0;
+
+                       fate = find_vector_intersection(&fq,&hit_info);
+
+                       if (fate != HIT_WALL) {
+                               if ( segnum != obj->segnum )
+                                       obj_relink( obj-Objects, segnum);
+                               obj->pos = *newpos;
+                               return 0;
+                       } //else
+                               //mprintf((0, "Hit wall seg:side = %i:%i\n", hit_info.hit_seg, hit_info.hit_side));
+               }
+       }
+
+       return 1;
+
+}
+
+
+//     Return 0 if object is in expected segment, else return 1
+int verify_object_seg(object *objp, vms_vector *newpos)
+{
+       segmasks        result = get_seg_masks(newpos, objp->segnum, objp->size);
+
+       if (result.facemask == 0)
+               return 0;
+       else
+               return move_object_within_mine(objp, newpos);
+
+}
+
+//     ------------------------------------------------------------------------------------------------------
+int    ObjectMoveForward(void)
+{
+       object *obj;
+       vms_vector      fvec;
+       vms_vector      newpos;
+
+       if (Cur_object_index == -1) {
+               editor_status("No current object, cannot move.");
+               return 1;
+       }
+
+       obj = &Objects[Cur_object_index];
+
+       extract_forward_vector_from_segment(&Segments[obj->segnum], &fvec);
+       vm_vec_normalize(&fvec);
+
+       vm_vec_add(&newpos, &obj->pos, vm_vec_scale(&fvec, OBJ_SCALE));
+
+       if (!verify_object_seg(obj, &newpos))
+               obj->pos = newpos;
+
+       Update_flags |= UF_WORLD_CHANGED;
+
+       return 1;
+}
+
+//     ------------------------------------------------------------------------------------------------------
+int    ObjectMoveBack(void)
+{
+       object *obj;
+       vms_vector      fvec;
+       vms_vector      newpos;
+
+       if (Cur_object_index == -1) {
+               editor_status("No current object, cannot move.");
+               return 1;
+       }
+
+       obj = &Objects[Cur_object_index];
+
+       extract_forward_vector_from_segment(&Segments[obj->segnum], &fvec);
+       vm_vec_normalize(&fvec);
+
+       vm_vec_sub(&newpos, &obj->pos, vm_vec_scale(&fvec, OBJ_SCALE));
+
+       if (!verify_object_seg(obj, &newpos))
+               obj->pos = newpos;
+
+       Update_flags |= UF_WORLD_CHANGED;
+
+       return 1;
+}
+
+//     ------------------------------------------------------------------------------------------------------
+int    ObjectMoveLeft(void)
+{
+       object *obj;
+       vms_vector      rvec;
+       vms_vector      newpos;
+
+       if (Cur_object_index == -1) {
+               editor_status("No current object, cannot move.");
+               return 1;
+       }
+
+       obj = &Objects[Cur_object_index];
+
+       extract_right_vector_from_segment(&Segments[obj->segnum], &rvec);
+       vm_vec_normalize(&rvec);
+
+       vm_vec_sub(&newpos, &obj->pos, vm_vec_scale(&rvec, OBJ_SCALE));
+
+       if (!verify_object_seg(obj, &newpos))
+               obj->pos = newpos;
+
+       Update_flags |= UF_WORLD_CHANGED;
+
+       return 1;
+}
+
+//     ------------------------------------------------------------------------------------------------------
+int    ObjectMoveRight(void)
+{
+       object *obj;
+       vms_vector      rvec;
+       vms_vector      newpos;
+
+       if (Cur_object_index == -1) {
+               editor_status("No current object, cannot move.");
+               return 1;
+       }
+
+       obj = &Objects[Cur_object_index];
+
+       extract_right_vector_from_segment(&Segments[obj->segnum], &rvec);
+       vm_vec_normalize(&rvec);
+
+       vm_vec_add(&newpos, &obj->pos, vm_vec_scale(&rvec, OBJ_SCALE));
+
+       if (!verify_object_seg(obj, &newpos))
+               obj->pos = newpos;
+
+       Update_flags |= UF_WORLD_CHANGED;
+
+       return 1;
+}
+
+//     ------------------------------------------------------------------------------------------------------
+int    ObjectSetDefault(void)
+{
+       //update_due_to_new_segment();
+
+       if (Cur_object_index == -1) {
+               editor_status("No current object, cannot move.");
+               return 1;
+       }
+
+       compute_segment_center(&Objects[Cur_object_index].pos, &Segments[Objects[Cur_object_index].segnum]);
+
+       Update_flags |= UF_WORLD_CHANGED;
+
+       return 1;
+}
+
+
+//     ------------------------------------------------------------------------------------------------------
+int    ObjectMoveUp(void)
+{
+       object *obj;
+       vms_vector      uvec;
+       vms_vector      newpos;
+
+       if (Cur_object_index == -1) {
+               editor_status("No current object, cannot move.");
+               return 1;
+       }
+
+       obj = &Objects[Cur_object_index];
+
+       extract_up_vector_from_segment(&Segments[obj->segnum], &uvec);
+       vm_vec_normalize(&uvec);
+
+       vm_vec_add(&newpos, &obj->pos, vm_vec_scale(&uvec, OBJ_SCALE));
+
+       if (!verify_object_seg(obj, &newpos))
+               obj->pos = newpos;
+
+       Update_flags |= UF_WORLD_CHANGED;
+
+       return 1;
+}
+
+//     ------------------------------------------------------------------------------------------------------
+int    ObjectMoveDown(void)
+{
+       object *obj;
+       vms_vector      uvec;
+       vms_vector      newpos;
+
+       if (Cur_object_index == -1) {
+               editor_status("No current object, cannot move.");
+               return 1;
+       }
+
+       obj = &Objects[Cur_object_index];
+
+       extract_up_vector_from_segment(&Segments[obj->segnum], &uvec);
+       vm_vec_normalize(&uvec);
+
+       vm_vec_sub(&newpos, &obj->pos, vm_vec_scale(&uvec, OBJ_SCALE));
+
+       if (!verify_object_seg(obj, &newpos))
+               obj->pos = newpos;
+
+       Update_flags |= UF_WORLD_CHANGED;
+
+       return 1;
+}
+
+//     ------------------------------------------------------------------------------------------------------
+int    ObjectMakeSmaller(void)
+{
+       fix     cur_size;
+
+       //update_due_to_new_segment();
+
+       cur_size = Objects[Cur_object_index].size;
+
+       cur_size -= OBJ_DEL_SIZE;
+       if (cur_size < OBJ_DEL_SIZE)
+               cur_size = OBJ_DEL_SIZE;
+
+       Objects[Cur_object_index].size = cur_size;
+       
+       Update_flags |= UF_WORLD_CHANGED;
+
+       return 1;
+}
+
+//     ------------------------------------------------------------------------------------------------------
+int    ObjectMakeLarger(void)
+{
+       fix     cur_size;
+
+       //update_due_to_new_segment();
+
+       cur_size = Objects[Cur_object_index].size;
+
+       cur_size += OBJ_DEL_SIZE;
+
+       Objects[Cur_object_index].size = cur_size;
+       
+       Update_flags |= UF_WORLD_CHANGED;
+
+       return 1;
+}
+
+//     ------------------------------------------------------------------------------------------------------
+
+int rotate_object(short objnum, int p, int b, int h)
+{
+       object *obj = &Objects[objnum];
+       vms_angvec ang;
+       vms_matrix rotmat,tempm;
+       
+//     vm_extract_angles_matrix( &ang,&obj->orient);
+
+//     ang.p += p;
+//     ang.b += b;
+//     ang.h += h;
+
+       ang.p = p;
+       ang.b = b;
+       ang.h = h;
+
+       vm_angles_2_matrix(&rotmat, &ang);
+       vm_matrix_x_matrix(&tempm, &obj->orient, &rotmat);
+       obj->orient = tempm;
+
+//   vm_angles_2_matrix(&obj->orient, &ang);
+
+       Update_flags |= UF_WORLD_CHANGED;
+
+       return 1;
+}
+
+
+void reset_object(short objnum)
+{
+       object *obj = &Objects[objnum];
+
+       med_extract_matrix_from_segment(&Segments[obj->segnum],&obj->orient);
+
+}
+
+
+
+int ObjectResetObject()
+{
+       reset_object(Cur_object_index);
+
+       Update_flags |= UF_WORLD_CHANGED;
+
+       return 1;
+}
+
+
+int ObjectFlipObject()
+{
+       vms_matrix *m=&Objects[Cur_object_index].orient;
+
+       vm_vec_negate(&m->uvec);
+       vm_vec_negate(&m->rvec);
+
+       Update_flags |= UF_WORLD_CHANGED;
+
+       return 1;
+}
+
+int ObjectDecreaseBank()               {return rotate_object(Cur_object_index, 0, -ROTATION_UNIT, 0);}
+int ObjectIncreaseBank()               {return rotate_object(Cur_object_index, 0, ROTATION_UNIT, 0);}
+int ObjectDecreasePitch()              {return rotate_object(Cur_object_index, -ROTATION_UNIT, 0, 0);}
+int ObjectIncreasePitch()              {return rotate_object(Cur_object_index, ROTATION_UNIT, 0, 0);}
+int ObjectDecreaseHeading()    {return rotate_object(Cur_object_index, 0, 0, -ROTATION_UNIT);}
+int ObjectIncreaseHeading()    {return rotate_object(Cur_object_index, 0, 0, ROTATION_UNIT);}
+
+int ObjectDecreaseBankBig()            {return rotate_object(Cur_object_index, 0, -(ROTATION_UNIT*4), 0);}
+int ObjectIncreaseBankBig()            {return rotate_object(Cur_object_index, 0, (ROTATION_UNIT*4), 0);}
+int ObjectDecreasePitchBig()           {return rotate_object(Cur_object_index, -(ROTATION_UNIT*4), 0, 0);}
+int ObjectIncreasePitchBig()           {return rotate_object(Cur_object_index, (ROTATION_UNIT*4), 0, 0);}
+int ObjectDecreaseHeadingBig() {return rotate_object(Cur_object_index, 0, 0, -(ROTATION_UNIT*4));}
+int ObjectIncreaseHeadingBig() {return rotate_object(Cur_object_index, 0, 0, (ROTATION_UNIT*4));}
+
+//     -----------------------------------------------------------------------------------------------------
+//     Move object around based on clicks in 2d screen.
+//     Slide an object parallel to the 2d screen, to a point on a vector.
+//     The vector is defined by a point on the 2d screen and the eye.
+
+//     V       =       vector from eye to 2d screen point.
+//     E       =       eye
+//     F       =       forward vector from eye
+//     O       =       3-space location of object
+
+//     D       =       depth of object given forward vector F
+//             =       (OE dot norm(F))
+
+//     Must solve intersection of:
+//             E + tV          ( equation of vector from eye through point on 2d screen)
+//             Fs + D          ( equation of plane parallel to 2d screen, at depth D)
+//             =       Fx(Ex + tVx) + Fy(Ey + tVy) + Fz(Ez + tVz) + D = 0
+//
+//                           FxEx + FyEy + FzEz - D
+//                     t = - ----------------------
+//                                       VxFx + VyFy + VzFz
+
+
+//void print_vec(vms_vector *vec, char *text)
+//{
+//     mprintf((0, "%10s = %9.5f %9.5f %9.5f\n", text, f2fl(vec->x), f2fl(vec->y), f2fl(vec->z)));
+//}
+//
+// void solve(vms_vector *result, vms_vector *E, vms_vector *V, vms_vector *O, vms_vector *F)
+// {
+//     fix     t, D;
+//     vms_vector      Fnorm, Vnorm;
+//     fix                     num, denom;
+//     // float                        test_plane;
+// 
+//     print_vec(E, "E");
+//     print_vec(V, "V");
+//     print_vec(O, "O");
+//     print_vec(F, "F");
+// 
+//     Fnorm = *F;     vm_vec_normalize(&Fnorm);
+//     Vnorm = *V;     vm_vec_normalize(&Vnorm);
+// 
+//     D = (fixmul(O->x, Fnorm.x) + fixmul(O->y, Fnorm.y) + fixmul(O->z, Fnorm.z));
+//     mprintf((0, "D = %9.5f\n", f2fl(D)));
+// 
+//     num = fixmul(Fnorm.x, E->x) + fixmul(Fnorm.y, E->y) + fixmul(Fnorm.z, E->z) - D;
+//     denom = vm_vec_dot(&Vnorm, &Fnorm);
+//     t = - num/denom;
+// 
+//     mprintf((0, "num = %9.5f, denom = %9.5f, t = %9.5f\n", f2fl(num), f2fl(denom), f2fl(t)));
+// 
+//     result->x = E->x + fixmul(t, Vnorm.x);
+//     result->y = E->y + fixmul(t, Vnorm.y);
+//     result->z = E->z + fixmul(t, Vnorm.z);
+// 
+//     print_vec(result, "result");
+// 
+//     // test_plane = fixmul(result->x, Fnorm.x) + fixmul(result->y, Fnorm.y) + fixmul(result->z, Fnorm.z) - D;
+//     // if (abs(test_plane) > .001)
+//     //      printf("OOPS: test_plane = %9.5f\n", test_plane);
+// }
+
+void move_object_to_position(int objnum, vms_vector *newpos)
+{
+       object  *objp = &Objects[objnum];
+
+       segmasks        result = get_seg_masks(newpos, objp->segnum, objp->size);
+
+       if (result.facemask == 0) {
+               //mprintf((0, "Object #%i moved from (%7.3f %7.3f %7.3f) to (%7.3f %7.3f %7.3f)\n", objnum, f2fl(objp->pos.x), f2fl(objp->pos.y), f2fl(objp->pos.z), f2fl(newpos->x), f2fl(newpos->y), f2fl(newpos->z)));
+               objp->pos = *newpos;
+       } else {
+               if (verify_object_seg(&Objects[objnum], newpos)) {
+                       int             fate, count;
+                       int             viewer_segnum;
+                       object  temp_viewer_obj;
+                       fvi_query fq;
+                       fvi_info        hit_info;
+                       vms_vector      last_outside_pos;
+                       vms_vector      last_inside_pos;
+
+                       temp_viewer_obj = *Viewer;
+                       viewer_segnum = find_object_seg(&temp_viewer_obj);
+                       temp_viewer_obj.segnum = viewer_segnum;
+
+                       //      If the viewer is outside the mine, get him in the mine!
+                       if (viewer_segnum == -1) {
+                               //      While outside mine, move towards object
+                               count = 0;
+                               while (viewer_segnum == -1) {
+                                       vms_vector      temp_vec;
+
+                                       //mprintf((0, "[towards %7.3f %7.3f %7.3f]\n", f2fl(temp_viewer_obj.pos.x), f2fl(temp_viewer_obj.pos.y), f2fl(temp_viewer_obj.pos.z)));
+                                       last_outside_pos = temp_viewer_obj.pos;
+
+                                       vm_vec_avg(&temp_vec, &temp_viewer_obj.pos, newpos);
+                                       temp_viewer_obj.pos = temp_vec;
+                                       viewer_segnum = find_object_seg(&temp_viewer_obj);
+                                       temp_viewer_obj.segnum = viewer_segnum;
+
+                                       if (count > 5) {
+                                               editor_status("Unable to move object, can't get viewer in mine.  Aborting");
+                                               return;
+                                       }
+                               }
+
+                               count = 0;
+                               //      While inside mine, move away from object.
+                               while (viewer_segnum != -1) {
+
+                                       vms_vector      temp_vec;
+
+                                       //mprintf((0, "[away %7.3f %7.3f %7.3f]\n", f2fl(temp_viewer_obj.pos.x), f2fl(temp_viewer_obj.pos.y), f2fl(temp_viewer_obj.pos.z)));
+                                       last_inside_pos = temp_viewer_obj.pos;
+
+                                       vm_vec_avg(&temp_vec, &temp_viewer_obj.pos, &last_outside_pos);
+                                       temp_viewer_obj.pos = temp_vec;
+                                       update_object_seg(&temp_viewer_obj);
+                                       viewer_segnum = find_object_seg(&temp_viewer_obj);
+                                       temp_viewer_obj.segnum = viewer_segnum;
+
+                                       if (count > 5) {
+                                               editor_status("Unable to move object, can't get viewer back out of mine.  Aborting");
+                                               return;
+                                       }
+                               }
+                       }
+
+                       fq.p0                                           = &temp_viewer_obj.pos;
+                       fq.startseg                             = temp_viewer_obj.segnum;
+                       fq.p1                                           = newpos;
+                       fq.rad                                  = temp_viewer_obj.size;
+                       fq.thisobjnum                   = -1;
+                       fq.ignore_obj_list      = NULL;
+                       fq.flags                                        = 0;
+
+                       fate = find_vector_intersection(&fq,&hit_info);
+                       if (fate == HIT_WALL) {
+                               int     new_segnum;
+
+                               //mprintf((0, "Hit wall seg:side = %i:%i, point = (%7.3f %7.3f %7.3f)\n", hit_info.hit_seg, hit_info.hit_side, f2fl(hit_info.hit_pnt.x), f2fl(hit_info.hit_pnt.y), f2fl(hit_info.hit_pnt.z)));
+                               objp->pos = hit_info.hit_pnt;
+                               new_segnum = find_object_seg(objp);
+                               Assert(new_segnum != -1);
+                               obj_relink(objp-Objects, new_segnum);
+                               //mprintf((0, "Object moved from segment %i to %i\n", old_segnum, objp->segnum));
+                       } else {
+                               editor_status("Attempted to move object out of mine.  Object not moved.");
+                               //mprintf((0,"Attempted to move object out of mine.  Object not moved."));
+                       }
+               }
+       }
+
+       Update_flags |= UF_WORLD_CHANGED;
+}
+
+void move_object_to_vector(vms_vector *vec_through_screen, fix delta_distance)
+{
+       vms_vector      result;
+
+       vm_vec_scale_add(&result, &Viewer->pos, vec_through_screen, vm_vec_dist(&Viewer->pos,&Objects[Cur_object_index].pos)+delta_distance);
+
+       move_object_to_position(Cur_object_index, &result);
+
+}
+
+void move_object_to_mouse_click_delta(fix delta_distance)
+{
+       short                   xcrd,ycrd;
+       vms_vector      vec_through_screen;
+
+       if (Cur_object_index == -1) {
+               editor_status("Cur_object_index == -1, cannot move that peculiar object...aborting!");
+               return;
+       }
+
+       xcrd = GameViewBox->b1_drag_x1;
+       ycrd = GameViewBox->b1_drag_y1;
+
+       med_point_2_vec(&_canv_editor_game, &vec_through_screen, xcrd, ycrd);
+
+       //mprintf((0, "Mouse click at %i %i, vector = %7.3f %7.3f %7.3f\n", xcrd, ycrd, f2fl(vec_through_screen.x), f2fl(vec_through_screen.y), f2fl(vec_through_screen.z)));
+
+       move_object_to_vector(&vec_through_screen, delta_distance);
+
+}
+
+void move_object_to_mouse_click(void)
+{
+       move_object_to_mouse_click_delta(0);
+}
+
+int    ObjectMoveNearer(void)
+{
+       vms_vector      result;
+
+       if (Cur_object_index == -1) {
+               editor_status("Cur_object_index == -1, cannot move that peculiar object...aborting!");
+               return 1;
+       }
+
+//     move_object_to_mouse_click_delta(-4*F1_0);              //      Move four units closer to eye
+
+       vm_vec_sub(&result, &Objects[Cur_object_index].pos, &Viewer->pos);
+       vm_vec_normalize(&result);
+       move_object_to_vector(&result, -4*F1_0);
+
+       return 1;       
+}
+
+int    ObjectMoveFurther(void)
+{
+       vms_vector      result;
+
+       if (Cur_object_index == -1) {
+               editor_status("Cur_object_index == -1, cannot move that peculiar object...aborting!");
+               return 1;
+       }
+
+//     move_object_to_mouse_click_delta(+4*F1_0);              //      Move four units further from eye
+
+       vm_vec_sub(&result, &Objects[Cur_object_index].pos, &Viewer->pos);
+       vm_vec_normalize(&result);
+       move_object_to_vector(&result, 4*F1_0);
+
+       return 1;       
+}
+
diff --git a/main/editor/eobject.h b/main/editor/eobject.h
new file mode 100644 (file)
index 0000000..a7fb4c7
--- /dev/null
@@ -0,0 +1,64 @@
+/*
+THE COMPUTER CODE CONTAINED HEREIN IS THE SOLE PROPERTY OF PARALLAX
+SOFTWARE CORPORATION ("PARALLAX").  PARALLAX, IN DISTRIBUTING THE CODE TO
+END-USERS, AND SUBJECT TO ALL OF THE TERMS AND CONDITIONS HEREIN, GRANTS A
+ROYALTY-FREE, PERPETUAL LICENSE TO SUCH END-USERS FOR USE BY SUCH END-USERS
+IN USING, DISPLAYING,  AND CREATING DERIVATIVE WORKS THEREOF, SO LONG AS
+SUCH USE, DISPLAY OR CREATION IS FOR NON-COMMERCIAL, ROYALTY OR REVENUE
+FREE PURPOSES.  IN NO EVENT SHALL THE END-USER USE THE COMPUTER CODE
+CONTAINED HEREIN FOR REVENUE-BEARING PURPOSES.  THE END-USER UNDERSTANDS
+AND AGREES TO THE TERMS HEREIN AND ACCEPTS THE SAME BY USE OF THIS FILE.  
+COPYRIGHT 1993-1998 PARALLAX SOFTWARE CORPORATION.  ALL RIGHTS RESERVED.
+*/
+/*
+ * $Source: /cvs/cvsroot/d2x/main/editor/eobject.h,v $
+ * $Revision: 1.1 $
+ * $Author: btb $
+ * $Date: 2004-12-19 13:54:27 $
+ * 
+ * Header for eobject.c
+ * 
+ * $Log: not supported by cvs2svn $
+ * Revision 1.1.1.1  1999/06/14 22:02:35  donut
+ * Import of d1x 1.37 source.
+ *
+ * Revision 2.0  1995/02/27  11:35:30  john
+ * Version 2.0! No anonymous unions, Watcom 10.0, with no need
+ * for bitmaps.tbl.
+ * 
+ * Revision 1.5  1994/09/15  22:57:46  matt
+ * Made new objects be oriented to their segment
+ * Added keypad function to flip an object upside-down
+ * 
+ * Revision 1.4  1994/08/25  21:57:23  mike
+ * Prototype ObjectSelectPrevInMine, and probably wrote it too, though not in this file.
+ * 
+ * Revision 1.3  1994/08/05  18:17:48  matt
+ * Made object rotation have 4x resolution, and SHIFT+rotate do old resolution.
+ * 
+ * Revision 1.2  1994/05/14  18:00:59  matt
+ * Got rid of externs in source (non-header) files
+ * 
+ * Revision 1.1  1994/05/14  17:36:30  matt
+ * Initial revision
+ * 
+ * 
+ */
+
+
+
+#ifndef _EOBJECT_H
+#define _EOBJECT_H
+
+int ObjectSelectNextInMine(void);
+int ObjectSelectPrevInMine(void);
+
+int   ObjectDecreaseBankBig(); 
+int   ObjectIncreaseBankBig(); 
+int   ObjectDecreasePitchBig();
+int   ObjectIncreasePitchBig();
+int   ObjectDecreaseHeadingBig();
+int   ObjectIncreaseHeadingBig();
+int   ObjectFlipObject();
+
+#endif
diff --git a/main/editor/eswitch.c b/main/editor/eswitch.c
new file mode 100644 (file)
index 0000000..c6b96e9
--- /dev/null
@@ -0,0 +1,635 @@
+/*
+THE COMPUTER CODE CONTAINED HEREIN IS THE SOLE PROPERTY OF PARALLAX
+SOFTWARE CORPORATION ("PARALLAX").  PARALLAX, IN DISTRIBUTING THE CODE TO
+END-USERS, AND SUBJECT TO ALL OF THE TERMS AND CONDITIONS HEREIN, GRANTS A
+ROYALTY-FREE, PERPETUAL LICENSE TO SUCH END-USERS FOR USE BY SUCH END-USERS
+IN USING, DISPLAYING,  AND CREATING DERIVATIVE WORKS THEREOF, SO LONG AS
+SUCH USE, DISPLAY OR CREATION IS FOR NON-COMMERCIAL, ROYALTY OR REVENUE
+FREE PURPOSES.  IN NO EVENT SHALL THE END-USER USE THE COMPUTER CODE
+CONTAINED HEREIN FOR REVENUE-BEARING PURPOSES.  THE END-USER UNDERSTANDS
+AND AGREES TO THE TERMS HEREIN AND ACCEPTS THE SAME BY USE OF THIS FILE.  
+COPYRIGHT 1993-1998 PARALLAX SOFTWARE CORPORATION.  ALL RIGHTS RESERVED.
+*/
+/*
+ * $Source: /cvs/cvsroot/d2x/main/editor/eswitch.c,v $
+ * $Revision: 1.1 $
+ * $Author: btb $
+ * $Date: 2004-12-19 13:54:27 $
+ * 
+ * Editor switch functions. 
+ * 
+ * $Log: not supported by cvs2svn $
+ * Revision 1.1.1.1  1999/06/14 22:03:03  donut
+ * Import of d1x 1.37 source.
+ *
+ * Revision 2.0  1995/02/27  11:35:18  john
+ * Version 2.0! No anonymous unions, Watcom 10.0, with no need
+ * for bitmaps.tbl.
+ * 
+ * Revision 1.33  1995/01/14  19:18:04  john
+ * First version of object paging.
+ * 
+ * Revision 1.32  1994/11/27  23:18:01  matt
+ * Made changes for new mprintf calling convention
+ * 
+ * Revision 1.31  1994/11/07  10:55:42  yuan
+ * *** empty log message ***
+ * 
+ * Revision 1.30  1994/10/13  13:15:06  yuan
+ * Fixed trigger removal bug.
+ * 
+ * Revision 1.29  1994/10/06  21:24:16  matt
+ * Added switch for exit to secret level
+ * 
+ * Revision 1.28  1994/09/29  17:06:10  matt
+ * Took out references to obsolete external triggers
+ * 
+ * Revision 1.27  1994/09/28  13:40:34  yuan
+ * Fixed control center trigger bug.
+ * 
+ * Revision 1.26  1994/09/26  16:25:04  yuan
+ * Only allow one binding of each matcen .
+ * 
+ * Revision 1.25  1994/09/24  17:10:19  yuan
+ * Added Matcen triggers.
+ * 
+ * Revision 1.24  1994/09/20  18:23:58  yuan
+ * Killed the BOGIFYING WALL DRAGON...
+ * 
+ * There was a problem with triggers being created that had bogus
+ * pointers back to their segments.
+ * 
+ * Revision 1.23  1994/08/25  21:56:33  mike
+ * IS_CHILD stuff.
+ * 
+ * Revision 1.22  1994/08/15  18:06:54  yuan
+ * Added external trigger.
+ * 
+ * Revision 1.21  1994/07/22  17:18:47  yuan
+ * Working on dialog box for refuel/repair/material/control centers.
+ * 
+ * Revision 1.20  1994/07/06  10:55:55  john
+ * New structures for hostages.
+ * 
+ * Revision 1.19  1994/07/01  16:36:20  yuan
+ * Working on triggers that aren't always on.
+ * 
+ * Revision 1.18  1994/06/21  18:50:14  john
+ * Made ESC key exit dialog.
+ * 
+ * Revision 1.17  1994/06/20  22:30:36  yuan
+ * Fixed crazy runaway trigger bug that Adam found
+ * 
+ * Revision 1.16  1994/05/31  10:03:48  yuan
+ * Fixed compiler warnings...
+ * 
+ * 
+ * Revision 1.15  1994/05/30  20:22:30  yuan
+ * New triggers.
+ * 
+ * Revision 1.14  1994/05/27  12:33:50  yuan
+ * Fixed some bugs when adding trigger.
+ * 
+ * Revision 1.13  1994/05/27  10:34:29  yuan
+ * Added new Dialog boxes for Walls and Triggers.
+ * 
+ * Revision 1.12  1994/05/25  18:15:02  yuan
+ * Fixed make warnings to save 10 cents!
+ * 
+ * Revision 1.11  1994/05/25  18:08:03  yuan
+ * Revamping walls and triggers interface.
+ * Wall interface complete, but triggers are still in progress.
+ * 
+ * Revision 1.10  1994/04/29  15:05:50  yuan
+ * Trigger/Link removing stuff still needs to be fixed.
+ * 
+ * Revision 1.9  1994/04/28  23:25:34  yuan
+ * Obliterated warnings.
+ * 
+ * Revision 1.8  1994/04/28  18:08:06  yuan
+ * Fixed trigger bug.
+ * 
+ */
+
+
+#ifdef RCS
+static char rcsid[] = "$Id: eswitch.c,v 1.1 2004-12-19 13:54:27 btb Exp $";
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <math.h>
+#include <string.h>
+
+#include "inferno.h"
+#include "editor.h"
+#include "eswitch.h"
+#include "segment.h"
+#include "error.h"
+#include "gameseg.h"
+#include "mono.h"
+#include "wall.h"
+#include "medwall.h"
+
+#include "screens.h"
+
+#include "textures.h"
+#include "texmerge.h"
+#include "medrobot.h"
+#include "timer.h"
+#include "key.h"
+#include "ehostage.h"
+#include "centers.h"
+#include "piggy.h"
+
+//-------------------------------------------------------------------------
+// Variables for this module...
+//-------------------------------------------------------------------------
+#define NUM_TRIGGER_FLAGS 10
+
+static UI_WINDOW                               *MainWindow = NULL;
+static UI_GADGET_USERBOX       *WallViewBox;
+static UI_GADGET_BUTTON        *QuitButton;
+static UI_GADGET_CHECKBOX      *TriggerFlag[NUM_TRIGGER_FLAGS];
+
+static int old_trigger_num;
+
+//-----------------------------------------------------------------
+// Adds a trigger to wall, and returns the trigger number. 
+// If there is a trigger already present, it returns the trigger number. (To be replaced)
+int add_trigger(segment *seg, short side)
+{
+       int trigger_num = Num_triggers;
+       int wall_num = seg->sides[side].wall_num;
+
+       Assert(trigger_num < MAX_TRIGGERS);
+       if (trigger_num>=MAX_TRIGGERS) return -1;
+
+       if (wall_num == -1) {
+               wall_add_to_markedside(WALL_OPEN);
+               wall_num = seg->sides[side].wall_num;
+               Walls[wall_num].trigger = trigger_num;
+               
+               // Set default values first time trigger is added
+               Triggers[trigger_num].flags = 0;
+               Triggers[trigger_num].value = F1_0*5;
+               Triggers[trigger_num].num_links = 0;
+               Triggers[trigger_num].flags &= TRIGGER_ON;              
+
+               Num_triggers++;
+               return trigger_num;
+       } else {
+               if (Walls[wall_num].trigger != -1)
+                       return Walls[wall_num].trigger;
+
+               // Create new trigger.
+               Walls[wall_num].trigger = trigger_num;
+
+               // Set default values first time trigger is added
+               Triggers[trigger_num].flags = 0;
+               Triggers[trigger_num].value = F1_0*5;
+               Triggers[trigger_num].num_links = 0;
+               Triggers[trigger_num].flags &= TRIGGER_ON;
+
+               Num_triggers++;
+               return trigger_num;
+       }
+}              
+
+//-----------------------------------------------------------------
+// Adds a specific trigger flag to Markedsegp/Markedside if it is possible.
+// Automatically adds flag to Connectside if possible unless it is a control trigger.
+// Returns 1 if trigger flag added.
+// Returns 0 if trigger flag cannot be added.
+int trigger_add_to_Markedside(short flag) {
+       int trigger_num; //, ctrigger_num;
+
+       if (!Markedsegp) {
+               editor_status("No Markedside.");
+               return 0;
+       }
+
+       // If no child on Markedside return
+       if (!IS_CHILD(Markedsegp->children[Markedside])) return 0;
+
+       trigger_num = add_trigger(Markedsegp, Markedside);
+
+       if (trigger_num == -1) {
+               editor_status("Cannot add trigger at Markedside.");
+               return 0;
+       }
+
+       Triggers[trigger_num].flags |= flag;
+
+       return 1;
+}
+
+int trigger_remove_flag_from_Markedside(short flag) {
+       int trigger_num; //, ctrigger_num;
+       int wall_num;
+       
+       if (!Markedsegp) {
+               editor_status("No Markedside.");
+               return 0;
+       }
+
+       // If no child on Markedside return
+       if (!IS_CHILD(Markedsegp->children[Markedside])) return 0;
+
+       // If no wall just return
+       wall_num = Markedsegp->sides[Markedside].wall_num;
+       if (wall_num == -1) return 0;
+
+       trigger_num = Walls[wall_num].trigger;
+
+       // If flag is already cleared, then don't change anything.
+       if ( trigger_num == -1 ) {
+               editor_status("No trigger at Markedside.");
+               return 0;
+       }
+
+       if (!Triggers[trigger_num].flags & flag)
+               return 1;
+
+       Triggers[trigger_num].flags &= ~flag;
+
+       return 1;
+}
+
+
+int bind_matcen_to_trigger() {
+
+       int wall_num, trigger_num, link_num;
+       int i;
+
+       if (!Markedsegp) {
+               editor_status("No marked segment.");
+               return 0;
+       }
+
+       wall_num = Markedsegp->sides[Markedside].wall_num;
+       if (wall_num == -1) {
+               editor_status("No wall at Markedside.");
+               return 0;
+       }
+
+       trigger_num = Walls[wall_num].trigger;  
+
+       if (trigger_num == -1) {
+               editor_status("No trigger at Markedside.");
+               return 0;
+       }
+
+       if (!(Cursegp->special & SEGMENT_IS_ROBOTMAKER)) {
+               editor_status("No Matcen at Cursegp.");
+               return 0;
+       }
+
+       link_num = Triggers[trigger_num].num_links;
+       for (i=0;i<link_num;i++)
+               if (Cursegp-Segments == Triggers[trigger_num].seg[i]) {
+                       editor_status("Matcen already bound to Markedside.");
+                       return 0;
+               }
+
+       // Error checking completed, actual binding begins
+       Triggers[trigger_num].seg[link_num] = Cursegp - Segments;
+       Triggers[trigger_num].num_links++;
+
+       mprintf((0, "seg %d linked to link_num %d\n",
+                               Triggers[trigger_num].seg[link_num], link_num)); 
+
+       editor_status("Matcen linked to trigger");
+
+       return 1;
+}
+
+
+int bind_wall_to_trigger() {
+
+       int wall_num, trigger_num, link_num;
+       int i;
+
+       if (!Markedsegp) {
+               editor_status("No marked segment.");
+               return 0;
+       }
+
+       wall_num = Markedsegp->sides[Markedside].wall_num;
+       if (wall_num == -1) {
+               editor_status("No wall at Markedside.");
+               return 0;
+       }
+
+       trigger_num = Walls[wall_num].trigger;  
+
+       if (trigger_num == -1) {
+               editor_status("No trigger at Markedside.");
+               return 0;
+       }
+
+       if (Cursegp->sides[Curside].wall_num == -1) {
+               editor_status("No wall at Curside.");
+               return 0;
+       }
+
+       if ((Cursegp==Markedsegp) && (Curside==Markedside)) {
+               editor_status("Cannot bind wall to itself.");
+               return 0;
+       }
+
+       link_num = Triggers[trigger_num].num_links;
+       for (i=0;i<link_num;i++)
+               if ((Cursegp-Segments == Triggers[trigger_num].seg[i]) && (Curside == Triggers[trigger_num].side[i])) {
+                       editor_status("Curside already bound to Markedside.");
+                       return 0;
+               }
+
+       // Error checking completed, actual binding begins
+       Triggers[trigger_num].seg[link_num] = Cursegp - Segments;
+       Triggers[trigger_num].side[link_num] = Curside;
+       Triggers[trigger_num].num_links++;
+
+       mprintf((0, "seg %d:side %d linked to link_num %d\n",
+                               Triggers[trigger_num].seg[link_num], Triggers[trigger_num].side[link_num], link_num)); 
+
+       editor_status("Wall linked to trigger");
+
+       return 1;
+}
+
+int remove_trigger(segment *seg, short side)
+{      
+       int trigger_num, t, w;
+
+       if (seg->sides[side].wall_num == -1) {
+               mprintf((0, "Can't remove trigger from wall_num -1\n"));        
+               return 0;
+       }
+
+       trigger_num = Walls[seg->sides[side].wall_num].trigger;
+
+       if (trigger_num != -1) {
+               Walls[seg->sides[side].wall_num].trigger = -1;
+               for (t=trigger_num;t<Num_triggers-1;t++)
+                       Triggers[t] = Triggers[t+1];
+       
+               for (w=0; w<Num_walls; w++) {
+                       if (Walls[w].trigger > trigger_num) 
+                               Walls[w].trigger--;
+               }
+
+               Num_triggers--;
+               for (t=0;t<Num_walls;t++)
+                       if (Walls[seg->sides[side].wall_num].trigger > trigger_num)
+                               Walls[seg->sides[side].wall_num].trigger--;
+               
+               return 1;
+       }
+
+       editor_status("No trigger to remove");
+       return 0;
+}
+
+
+int add_trigger_control()
+{
+       trigger_add_to_Markedside(TRIGGER_CONTROL_DOORS);
+       Update_flags = UF_WORLD_CHANGED;
+       return 1;
+}
+
+int trigger_remove()
+{
+       remove_trigger(Markedsegp, Markedside);
+       Update_flags = UF_WORLD_CHANGED;
+       return 1;
+}
+
+int trigger_turn_all_ON()
+{
+       int t;
+
+       for (t=0;t<Num_triggers;t++)
+               Triggers[t].flags &= TRIGGER_ON;
+       return 1;
+}
+
+//-------------------------------------------------------------------------
+// Called from the editor... does one instance of the trigger dialog box
+//-------------------------------------------------------------------------
+int do_trigger_dialog()
+{
+       int i;
+
+       if (!Markedsegp) {
+               editor_status("Trigger requires Marked Segment & Side.");
+               return 0;
+       }
+
+       // Only open 1 instance of this window...
+       if ( MainWindow != NULL ) return 0;
+
+       // Close other windows. 
+       robot_close_window();
+       close_wall_window();
+       close_centers_window();
+       hostage_close_window();
+
+       // Open a window with a quit button
+       MainWindow = ui_open_window( TMAPBOX_X+20, TMAPBOX_Y+20, 765-TMAPBOX_X, 545-TMAPBOX_Y, WIN_DIALOG );
+
+       // These are the checkboxes for each door flag.
+       i = 44;
+       TriggerFlag[0] = ui_add_gadget_checkbox( MainWindow, 22, i, 16, 16, 0, "Door Control" );        i+=22;
+       TriggerFlag[1] = ui_add_gadget_checkbox( MainWindow, 22, i, 16, 16, 0, "Shield damage" );       i+=22;
+       TriggerFlag[2] = ui_add_gadget_checkbox( MainWindow, 22, i, 16, 16, 0, "Energy drain" );                i+=22;
+       TriggerFlag[3] = ui_add_gadget_checkbox( MainWindow, 22, i, 16, 16, 0, "Exit" );                                        i+=22;
+       TriggerFlag[4] = ui_add_gadget_checkbox( MainWindow, 22, i, 16, 16, 0, "One-shot" );                    i+=22;
+       TriggerFlag[5] = ui_add_gadget_checkbox( MainWindow, 22, i, 16, 16, 0, "Illusion ON" );         i+=22;
+       TriggerFlag[6] = ui_add_gadget_checkbox( MainWindow, 22, i, 16, 16, 0, "Illusion OFF" );                i+=22;
+       TriggerFlag[7] = ui_add_gadget_checkbox( MainWindow, 22, i, 16, 16, 0, "Trigger ON" );                  i+=22;
+       TriggerFlag[8] = ui_add_gadget_checkbox( MainWindow, 22, i, 16, 16, 0, "Matcen Trigger" );      i+=22;
+       TriggerFlag[9] = ui_add_gadget_checkbox( MainWindow, 22, i, 16, 16, 0, "Secret Exit" );                 i+=22;
+
+       QuitButton = ui_add_gadget_button( MainWindow, 20, i, 48, 40, "Done", NULL );
+                                                                                                                                                                
+       // The little box the wall will appear in.
+       WallViewBox = ui_add_gadget_userbox( MainWindow, 155, 5, 64, 64 );
+
+       // A bunch of buttons...
+       i = 80;
+//     ui_add_gadget_button( MainWindow,155,i,140, 26, "Add Door Control", add_trigger_control ); i += 29;
+       ui_add_gadget_button( MainWindow,155,i,140, 26, "Remove Trigger", trigger_remove ); i += 29;
+       ui_add_gadget_button( MainWindow,155,i,140, 26, "Bind Wall", bind_wall_to_trigger ); i += 29;
+       ui_add_gadget_button( MainWindow,155,i,140, 26, "Bind Matcen", bind_matcen_to_trigger ); i += 29;
+       ui_add_gadget_button( MainWindow,155,i,140, 26, "All Triggers ON", trigger_turn_all_ON ); i += 29;
+
+       old_trigger_num = -2;           // Set to some dummy value so everything works ok on the first frame.
+
+       return 1;
+}
+
+void close_trigger_window()
+{
+       if ( MainWindow!=NULL ) {
+               ui_close_window( MainWindow );
+               MainWindow = NULL;
+       }
+}
+
+void do_trigger_window()
+{
+       int i;
+       short Markedwall, trigger_num;
+
+       if ( MainWindow == NULL ) return;
+       if (!Markedsegp) {
+               close_trigger_window();
+               return;
+       }
+
+       //------------------------------------------------------------
+       // Call the ui code..
+       //------------------------------------------------------------
+       ui_button_any_drawn = 0;
+       ui_window_do_gadgets(MainWindow);
+       
+       //------------------------------------------------------------
+       // If we change walls, we need to reset the ui code for all
+       // of the checkboxes that control the wall flags.  
+       //------------------------------------------------------------
+       Markedwall = Markedsegp->sides[Markedside].wall_num;
+       if (Markedwall != -1)
+               trigger_num = Walls[Markedwall].trigger;
+       else trigger_num = -1;
+
+       if (old_trigger_num != trigger_num ) {
+               for (   i=0; i < NUM_TRIGGER_FLAGS; i++ )       {
+                       TriggerFlag[i]->flag = 0;                               // Tells ui that this button isn't checked
+                       TriggerFlag[i]->status = 1;                             // Tells ui to redraw button
+               }
+
+               if (trigger_num != -1) {
+                       if (Triggers[trigger_num].flags & TRIGGER_CONTROL_DOORS)
+                               TriggerFlag[0]->flag = 1;
+                       if (Triggers[trigger_num].flags & TRIGGER_SHIELD_DAMAGE)
+                               TriggerFlag[1]->flag = 1;
+                       if (Triggers[trigger_num].flags & TRIGGER_ENERGY_DRAIN)
+                               TriggerFlag[2]->flag = 1;
+                       if (Triggers[trigger_num].flags & TRIGGER_EXIT)
+                               TriggerFlag[3]->flag = 1;
+                       if (Triggers[trigger_num].flags & TRIGGER_ONE_SHOT)
+                               TriggerFlag[4]->flag = 1;
+                       if (Triggers[trigger_num].flags & TRIGGER_ILLUSION_ON)
+                               TriggerFlag[5]->flag = 1;
+                       if (Triggers[trigger_num].flags & TRIGGER_ILLUSION_OFF)
+                               TriggerFlag[6]->flag = 1;
+                       if (Triggers[trigger_num].flags & TRIGGER_ON)
+                               TriggerFlag[7]->flag = 1;
+                       if (Triggers[trigger_num].flags & TRIGGER_MATCEN)
+                               TriggerFlag[8]->flag = 1;
+                       if (Triggers[trigger_num].flags & TRIGGER_SECRET_EXIT)
+                               TriggerFlag[9]->flag = 1;
+               }
+       }
+       
+       //------------------------------------------------------------
+       // If any of the checkboxes that control the wallflags are set, then
+       // update the cooresponding wall flag.
+       //------------------------------------------------------------
+       if (IS_CHILD(Markedsegp->children[Markedside])) {
+               if (TriggerFlag[0]->flag == 1) 
+                       trigger_add_to_Markedside(TRIGGER_CONTROL_DOORS); 
+               else
+                       trigger_remove_flag_from_Markedside(TRIGGER_CONTROL_DOORS);
+               if (TriggerFlag[1]->flag == 1)
+                       trigger_add_to_Markedside(TRIGGER_SHIELD_DAMAGE); 
+               else
+                       trigger_remove_flag_from_Markedside(TRIGGER_SHIELD_DAMAGE);
+               if (TriggerFlag[2]->flag == 1)
+                       trigger_add_to_Markedside(TRIGGER_ENERGY_DRAIN); 
+               else
+                       trigger_remove_flag_from_Markedside(TRIGGER_ENERGY_DRAIN);
+               if (TriggerFlag[3]->flag == 1)
+                       trigger_add_to_Markedside(TRIGGER_EXIT); 
+               else
+                       trigger_remove_flag_from_Markedside(TRIGGER_EXIT);
+               if (TriggerFlag[4]->flag == 1)
+                       trigger_add_to_Markedside(TRIGGER_ONE_SHOT); 
+               else
+                       trigger_remove_flag_from_Markedside(TRIGGER_ONE_SHOT);
+               if (TriggerFlag[5]->flag == 1)
+                       trigger_add_to_Markedside(TRIGGER_ILLUSION_ON); 
+               else
+                       trigger_remove_flag_from_Markedside(TRIGGER_ILLUSION_ON);
+               if (TriggerFlag[6]->flag == 1)
+                       trigger_add_to_Markedside(TRIGGER_ILLUSION_OFF);
+               else
+                       trigger_remove_flag_from_Markedside(TRIGGER_ILLUSION_OFF);
+               if (TriggerFlag[7]->flag == 1)
+                       trigger_add_to_Markedside(TRIGGER_ON);
+               else
+                       trigger_remove_flag_from_Markedside(TRIGGER_ON);
+
+               if (TriggerFlag[8]->flag == 1) 
+                       trigger_add_to_Markedside(TRIGGER_MATCEN);
+               else
+                       trigger_remove_flag_from_Markedside(TRIGGER_MATCEN);
+
+               if (TriggerFlag[9]->flag == 1) 
+                       trigger_add_to_Markedside(TRIGGER_SECRET_EXIT);
+               else
+                       trigger_remove_flag_from_Markedside(TRIGGER_SECRET_EXIT);
+
+       } else
+               for (   i=0; i < NUM_TRIGGER_FLAGS; i++ )
+                       if (TriggerFlag[i]->flag == 1) { 
+                               TriggerFlag[i]->flag = 0;                                       // Tells ui that this button isn't checked
+                               TriggerFlag[i]->status = 1;                             // Tells ui to redraw button
+                       }
+       
+       //------------------------------------------------------------
+       // Draw the wall in the little 64x64 box
+       //------------------------------------------------------------
+       gr_set_current_canvas( WallViewBox->canvas );
+
+       if ((Markedsegp->sides[Markedside].wall_num == -1) || (Walls[Markedsegp->sides[Markedside].wall_num].trigger) == -1)
+               gr_clear_canvas( CBLACK );
+       else {
+               if (Markedsegp->sides[Markedside].tmap_num2 > 0)  {
+                       gr_ubitmap(0,0, texmerge_get_cached_bitmap( Markedsegp->sides[Markedside].tmap_num, Markedsegp->sides[Markedside].tmap_num2));
+               } else {
+                       if (Markedsegp->sides[Markedside].tmap_num > 0) {
+                               PIGGY_PAGE_IN(Textures[Markedsegp->sides[Markedside].tmap_num]);
+                               gr_ubitmap(0,0, &GameBitmaps[Textures[Markedsegp->sides[Markedside].tmap_num].index]);
+                       } else
+                               gr_clear_canvas( CGREY );
+               }
+       }
+
+       //------------------------------------------------------------
+       // If anything changes in the ui system, redraw all the text that
+       // identifies this robot.
+       //------------------------------------------------------------
+       if (ui_button_any_drawn || (old_trigger_num != trigger_num) ) {
+               if ( Markedsegp->sides[Markedside].wall_num > -1 )      {
+                       ui_wprintf_at( MainWindow, 12, 6, "Trigger: %d    ", trigger_num);
+               }       else {
+                       ui_wprintf_at( MainWindow, 12, 6, "Trigger: none ");
+               }
+               Update_flags |= UF_WORLD_CHANGED;
+       }
+
+       if ( QuitButton->pressed || (last_keypress==KEY_ESC))   {
+               close_trigger_window();
+               return;
+       }               
+
+       old_trigger_num = trigger_num;
+}
+
+
+
+
+
diff --git a/main/editor/eswitch.h b/main/editor/eswitch.h
new file mode 100644 (file)
index 0000000..4e99ecf
--- /dev/null
@@ -0,0 +1,69 @@
+/*
+THE COMPUTER CODE CONTAINED HEREIN IS THE SOLE PROPERTY OF PARALLAX
+SOFTWARE CORPORATION ("PARALLAX").  PARALLAX, IN DISTRIBUTING THE CODE TO
+END-USERS, AND SUBJECT TO ALL OF THE TERMS AND CONDITIONS HEREIN, GRANTS A
+ROYALTY-FREE, PERPETUAL LICENSE TO SUCH END-USERS FOR USE BY SUCH END-USERS
+IN USING, DISPLAYING,  AND CREATING DERIVATIVE WORKS THEREOF, SO LONG AS
+SUCH USE, DISPLAY OR CREATION IS FOR NON-COMMERCIAL, ROYALTY OR REVENUE
+FREE PURPOSES.  IN NO EVENT SHALL THE END-USER USE THE COMPUTER CODE
+CONTAINED HEREIN FOR REVENUE-BEARING PURPOSES.  THE END-USER UNDERSTANDS
+AND AGREES TO THE TERMS HEREIN AND ACCEPTS THE SAME BY USE OF THIS FILE.  
+COPYRIGHT 1993-1998 PARALLAX SOFTWARE CORPORATION.  ALL RIGHTS RESERVED.
+*/
+/*
+ * $Source: /cvs/cvsroot/d2x/main/editor/eswitch.h,v $
+ * $Revision: 1.1 $
+ * $Author: btb $
+ * $Date: 2004-12-19 13:54:27 $
+ * 
+ * Headers for switch adding functions
+ * 
+ * $Log: not supported by cvs2svn $
+ * Revision 1.1.1.1  1999/06/14 22:02:35  donut
+ * Import of d1x 1.37 source.
+ *
+ * Revision 2.0  1995/02/27  11:35:40  john
+ * Version 2.0! No anonymous unions, Watcom 10.0, with no need
+ * for bitmaps.tbl.
+ * 
+ * Revision 1.6  1994/05/30  20:22:35  yuan
+ * New triggers.
+ * 
+ * Revision 1.5  1994/05/27  10:34:35  yuan
+ * Added new Dialog boxes for Walls and Triggers.
+ * 
+ * Revision 1.4  1994/05/25  18:08:39  yuan
+ * Revamping walls and triggers interface.
+ * Wall interface complete, but triggers are still in progress.
+ * 
+ * Revision 1.3  1994/04/28  23:46:56  yuan
+ * Added prototype for remove_trigger.
+ * 
+ * Revision 1.2  1994/03/15  16:34:20  yuan
+ * Fixed bm loader (might have some changes in walls and switches)
+ * 
+ * Revision 1.1  1994/03/10  14:49:03  yuan
+ * Initial revision
+ * 
+ * 
+ */
+
+#ifndef _ESWITCH_H
+#define _ESWITCH_H
+
+#include "inferno.h"
+#include "segment.h"
+#include "switch.h"
+
+extern int bind_wall_to_trigger();
+
+extern int trigger_remove();
+
+extern int remove_trigger(segment *seg, short side);
+
+extern void close_trigger_window();
+
+extern void do_trigger_window();
+
+#endif
+
diff --git a/main/editor/fixseg.c b/main/editor/fixseg.c
new file mode 100644 (file)
index 0000000..e441411
--- /dev/null
@@ -0,0 +1,244 @@
+/*
+THE COMPUTER CODE CONTAINED HEREIN IS THE SOLE PROPERTY OF PARALLAX
+SOFTWARE CORPORATION ("PARALLAX").  PARALLAX, IN DISTRIBUTING THE CODE TO
+END-USERS, AND SUBJECT TO ALL OF THE TERMS AND CONDITIONS HEREIN, GRANTS A
+ROYALTY-FREE, PERPETUAL LICENSE TO SUCH END-USERS FOR USE BY SUCH END-USERS
+IN USING, DISPLAYING,  AND CREATING DERIVATIVE WORKS THEREOF, SO LONG AS
+SUCH USE, DISPLAY OR CREATION IS FOR NON-COMMERCIAL, ROYALTY OR REVENUE
+FREE PURPOSES.  IN NO EVENT SHALL THE END-USER USE THE COMPUTER CODE
+CONTAINED HEREIN FOR REVENUE-BEARING PURPOSES.  THE END-USER UNDERSTANDS
+AND AGREES TO THE TERMS HEREIN AND ACCEPTS THE SAME BY USE OF THIS FILE.  
+COPYRIGHT 1993-1998 PARALLAX SOFTWARE CORPORATION.  ALL RIGHTS RESERVED.
+*/
+/*
+ * $Source: /cvs/cvsroot/d2x/main/editor/fixseg.c,v $
+ * $Revision: 1.1 $
+ * $Author: btb $
+ * $Date: 2004-12-19 13:54:27 $
+ * 
+ * Functions to make faces planar and probably other things.
+ * 
+ * $Log: not supported by cvs2svn $
+ * Revision 1.2  2003/03/09 06:34:09  donut
+ * change byte typedef to sbyte to avoid conflict with win32 byte which is unsigned
+ *
+ * Revision 1.1.1.1  1999/06/14 22:03:05  donut
+ * Import of d1x 1.37 source.
+ *
+ * Revision 2.0  1995/02/27  11:36:25  john
+ * Version 2.0. Ansi-fied.
+ * 
+ * Revision 1.7  1994/11/27  23:18:01  matt
+ * Made changes for new mprintf calling convention
+ * 
+ * Revision 1.6  1994/11/17  14:48:00  mike
+ * validation functions moved from editor to game.
+ * 
+ * Revision 1.5  1994/08/04  19:13:26  matt
+ * Changed a bunch of vecmat calls to use multiple-function routines, and to
+ * allow the use of C macros for some functions
+ * 
+ * Revision 1.4  1994/02/10  15:36:31  matt
+ * Various changes to make editor compile out.
+ * 
+ * Revision 1.3  1993/12/03  18:45:09  mike
+ * initial stuff.
+ * 
+ * Revision 1.2  1993/11/30  17:05:09  mike
+ * Added part of code to make a side planar.
+ * 
+ * Revision 1.1  1993/11/30  10:05:36  mike
+ * Initial revision
+ * 
+ * 
+ */
+
+
+#ifdef RCS
+static char rcsid[] = "$Id: fixseg.c,v 1.1 2004-12-19 13:54:27 btb Exp $";
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <math.h>
+#include <string.h>
+
+#include "mono.h"
+#include "key.h"
+#include "gr.h"
+
+#include "inferno.h"
+#include "segment.h"
+//#include "segment2.h"
+#include       "editor.h"
+#include "error.h"
+#include "gameseg.h"
+
+#define SWAP(a,b) {temp = (a); (a) = (b); (b) = temp;}
+
+//     -----------------------------------------------------------------------------------------------------------------
+//     Gauss-Jordan elimination solution of a system of linear equations.
+//     a[1..n][1..n] is the input matrix.  b[1..n][1..m] is input containing the m right-hand side vectors.
+//     On output, a is replaced by its matrix inverse and b is replaced by the corresponding set of solution vectors.
+void gaussj(fix **a, int n, fix **b, int m)
+{
+       int     indxc[4], indxr[4], ipiv[4];
+        int     i, icol=0, irow=0, j, k, l, ll;
+       fix     big, dum, pivinv, temp;
+
+       if (n > 4) {
+               mprintf((0,"Error -- array too large in gaussj.\n"));
+               Int3();
+       }
+
+       for (j=1; j<=n; j++)
+               ipiv[j] = 0;
+
+       for (i=1; i<=n; i++) {
+               big = 0;
+               for (j=1; j<=n; j++)
+                       if (ipiv[j] != 1)
+                               for (k=1; k<=n; k++) {
+                                       if (ipiv[k] == 0) {
+                                               if (abs(a[j][k]) >= big) {
+                                                       big = abs(a[j][k]);
+                                                       irow = j;
+                                                       icol = k;
+                                               }
+                                       } else if (ipiv[k] > 1) {
+                                               mprintf((0,"Error: Singular matrix-1\n"));
+                                               Int3();
+                                       }
+                               }
+
+               ++(ipiv[icol]);
+
+               // We now have the pivot element, so we interchange rows, if needed, to put the pivot
+               //      element on the diagonal.  The columns are not physically interchanged, only relabeled:
+               //      indxc[i], the column of the ith pivot element, is the ith column that is reduced, while
+               //      indxr[i] is the row in which that pivot element was originally located.  If indxr[i] !=
+               //      indxc[i] there is an implied column interchange.  With this form of bookkeeping, the
+               //      solution b's will end up in the correct order, and the inverse matrix will be scrambled
+               //      by columns.
+
+               if (irow != icol) {
+                       for (l=1; l<=n; l++)
+                               SWAP(a[irow][l], a[icol][l]);
+                       for (l=1; l<=m; l++)
+                               SWAP(b[irow][l], b[icol][l]);
+               }
+
+               indxr[i] = irow;
+               indxc[i] = icol;
+               if (a[icol][icol] == 0) {
+                       mprintf((0,"Error: Singular matrix-2\n"));
+                       Int3();
+               }
+               pivinv = fixdiv(F1_0, a[icol][icol]);
+               a[icol][icol] = F1_0;
+
+               for (l=1; l<=n; l++)
+                       a[icol][l] = fixmul(a[icol][l], pivinv);
+               for (l=1; l<=m; l++)
+                       b[icol][l] = fixmul(b[icol][l], pivinv);
+
+               for (ll=1; ll<=n; ll++)
+                       if (ll != icol) {
+                               dum = a[ll][icol];
+                               a[ll][icol] = 0;
+                               for (l=1; l<=n; l++)
+                                       a[ll][l] -= a[icol][l]*dum;
+                               for (l=1; l<=m; l++)
+                                       b[ll][l] -= b[icol][l]*dum;
+                       }
+       }
+
+       //      This is the end of the main loop over columns of the reduction.  It only remains to unscramble
+       //      the solution in view of the column interchanges.  We do this by interchanging pairs of
+       //      columns in the reverse order that the permutation was built up.
+       for (l=n; l>=1; l--) {
+               if (indxr[l] != indxc[l])
+                       for (k=1; k<=n; k++)
+                               SWAP(a[k][indxr[l]], a[k][indxc[l]]);
+       }
+
+}
+
+
+//     -----------------------------------------------------------------------------------------------------------------
+//     Return true if side is planar, else return false.
+int side_is_planar_p(segment *sp, int side)
+{
+       sbyte                   *vp;
+       vms_vector      *v0,*v1,*v2,*v3;
+       vms_vector      va,vb;
+
+       vp = Side_to_verts[side];
+       v0 = &Vertices[sp->verts[vp[0]]];
+       v1 = &Vertices[sp->verts[vp[1]]];
+       v2 = &Vertices[sp->verts[vp[2]]];
+       v3 = &Vertices[sp->verts[vp[3]]];
+
+       vm_vec_normalize(vm_vec_normal(&va,v0,v1,v2));
+       vm_vec_normalize(vm_vec_normal(&vb,v0,v2,v3));
+       
+       // If the two vectors are very close to being the same, then generate one quad, else generate two triangles.
+       return (vm_vec_dist(&va,&vb) < F1_0/1000);
+}
+
+//     -------------------------------------------------------------------------------------------------
+//     Return coordinates of a vertex which is vertex v moved so that all sides of which it is a part become planar.
+void compute_planar_vert(segment *sp, int side, int v, vms_vector *vp)
+{
+       if ((sp) && (side > -3))
+               *vp = Vertices[v];
+}
+
+//     -------------------------------------------------------------------------------------------------
+//     Making Cursegp:Curside planar.
+//     If already planar, return.
+//     for each vertex v on side, not part of another segment
+//             choose the vertex v which can be moved to make all sides of which it is a part planar, minimizing distance moved
+//     if there is no vertex v on side, not part of another segment, give up in disgust
+//     Return value:
+//             0       curside made planar (or already was)
+//             1       did not make curside planar
+int make_curside_planar(void)
+{
+       int                     v;
+       sbyte                   *vp;
+       vms_vector      planar_verts[4];                        // store coordinates of up to 4 vertices which will make Curside planar, corresponding to each of 4 vertices on side
+       int                     present_verts[4];                       //      set to 1 if vertex is present
+
+       if (side_is_planar_p(Cursegp, Curside))
+               return 0;
+
+       //      Look at all vertices in side to find a free one.
+       for (v=0; v<4; v++)
+               present_verts[v] = 0;
+
+       vp = Side_to_verts[Curside];
+
+       for (v=0; v<4; v++) {
+               int v1 = vp[v];         // absolute vertex id
+               if (is_free_vertex(Cursegp->verts[v1])) {
+                       compute_planar_vert(Cursegp, Curside, Cursegp->verts[v1], &planar_verts[v]);
+                       present_verts[v] = 1;
+               }
+       }
+
+       //      Now, for each v for which present_verts[v] == 1, there is a vector (point) in planar_verts[v].
+       //      See which one is closest to the plane defined by the other three points.
+       //      Nah...just use the first one we find.
+       for (v=0; v<4; v++)
+               if (present_verts[v]) {
+                       med_set_vertex(vp[v],&planar_verts[v]);
+                       validate_segment(Cursegp);
+                       // -- should propagate tmaps to segments or something here...
+                       
+                       return 0;
+               }
+       //      We tried, but we failed, to make Curside planer.
+       return 1;
+}
+
diff --git a/main/editor/func.c b/main/editor/func.c
new file mode 100644 (file)
index 0000000..a73e8c7
--- /dev/null
@@ -0,0 +1,128 @@
+/*
+THE COMPUTER CODE CONTAINED HEREIN IS THE SOLE PROPERTY OF PARALLAX
+SOFTWARE CORPORATION ("PARALLAX").  PARALLAX, IN DISTRIBUTING THE CODE TO
+END-USERS, AND SUBJECT TO ALL OF THE TERMS AND CONDITIONS HEREIN, GRANTS A
+ROYALTY-FREE, PERPETUAL LICENSE TO SUCH END-USERS FOR USE BY SUCH END-USERS
+IN USING, DISPLAYING,  AND CREATING DERIVATIVE WORKS THEREOF, SO LONG AS
+SUCH USE, DISPLAY OR CREATION IS FOR NON-COMMERCIAL, ROYALTY OR REVENUE
+FREE PURPOSES.  IN NO EVENT SHALL THE END-USER USE THE COMPUTER CODE
+CONTAINED HEREIN FOR REVENUE-BEARING PURPOSES.  THE END-USER UNDERSTANDS
+AND AGREES TO THE TERMS HEREIN AND ACCEPTS THE SAME BY USE OF THIS FILE.  
+COPYRIGHT 1993-1998 PARALLAX SOFTWARE CORPORATION.  ALL RIGHTS RESERVED.
+*/
+/*
+ * $Source: /cvs/cvsroot/d2x/main/editor/func.c,v $
+ * $Revision: 1.1 $
+ * $Author: btb $
+ * $Date: 2004-12-19 13:54:27 $
+ * 
+ * .
+ * 
+ * $Log: not supported by cvs2svn $
+ * Revision 1.1.1.1  1999/06/14 22:03:05  donut
+ * Import of d1x 1.37 source.
+ *
+ * Revision 1.1  1994/11/21  14:13:17  matt
+ * Initial revision
+ * 
+ * Revision 1.1  1993/11/15  12:28:17  john
+ * Initial revision
+ * 
+ * 
+ */
+
+
+#ifdef RCS
+static char rcsid[] = "$Id: func.c,v 1.1 2004-12-19 13:54:27 btb Exp $";
+#endif
+
+#include <stdlib.h>
+#include <string.h>
+
+#include "func.h"
+
+#define MAX_PARAMS 10
+
+static FUNCTION * func_table = NULL;
+static int func_size = 0;
+static int initialized = 0;
+static int func_params[MAX_PARAMS];
+
+int func_howmany()
+{
+       return func_size;
+}
+
+void func_init( FUNCTION * funtable, int size )
+{
+       if (!initialized)
+       {
+               initialized = 1;
+               func_table = funtable;
+               func_size = size;
+               atexit( func_close );
+       }
+}
+
+
+void func_close()
+{
+       if (initialized)
+       {
+               initialized = 0;
+               func_table = NULL;
+               func_size = 0;
+       }
+}
+
+int (*func_get( char * name, int * numparams ))(void)
+{
+       int i;
+
+       for (i=0; i<func_size; i++ )
+               if (!stricmp( name, func_table[i].name ))
+               {
+                       *numparams = func_table[i].nparams;
+                       return func_table[i].cfunction;
+               }
+
+       return NULL;
+}
+
+int func_get_index( char * name )
+{
+       int i;
+
+       for (i=0; i<func_size; i++ )
+               if (!stricmp( name, func_table[i].name ))
+               {
+                       return i;
+               }
+
+       return -1;
+}
+
+
+int (*func_nget( int func_number, int * numparams, char **name ))(void)
+{
+       if (func_number < func_size )
+       {
+               *name = func_table[func_number].name;
+               *numparams = func_table[func_number].nparams;
+               return func_table[func_number].cfunction;
+       }
+
+       return NULL;
+}
+
+void func_set_param( int n, int value )
+{
+       func_params[n] = value;
+}
+
+int func_get_param( int n )
+{
+       return func_params[n];
+}
+
+
diff --git a/main/editor/group.c b/main/editor/group.c
new file mode 100644 (file)
index 0000000..b80fa97
--- /dev/null
@@ -0,0 +1,2106 @@
+/*
+THE COMPUTER CODE CONTAINED HEREIN IS THE SOLE PROPERTY OF PARALLAX
+SOFTWARE CORPORATION ("PARALLAX").  PARALLAX, IN DISTRIBUTING THE CODE TO
+END-USERS, AND SUBJECT TO ALL OF THE TERMS AND CONDITIONS HEREIN, GRANTS A
+ROYALTY-FREE, PERPETUAL LICENSE TO SUCH END-USERS FOR USE BY SUCH END-USERS
+IN USING, DISPLAYING,  AND CREATING DERIVATIVE WORKS THEREOF, SO LONG AS
+SUCH USE, DISPLAY OR CREATION IS FOR NON-COMMERCIAL, ROYALTY OR REVENUE
+FREE PURPOSES.  IN NO EVENT SHALL THE END-USER USE THE COMPUTER CODE
+CONTAINED HEREIN FOR REVENUE-BEARING PURPOSES.  THE END-USER UNDERSTANDS
+AND AGREES TO THE TERMS HEREIN AND ACCEPTS THE SAME BY USE OF THIS FILE.  
+COPYRIGHT 1993-1998 PARALLAX SOFTWARE CORPORATION.  ALL RIGHTS RESERVED.
+*/
+/*             
+ * $Source: /cvs/cvsroot/d2x/main/editor/group.c,v $
+ * $Revision: 1.1 $
+ * $Author: btb $
+ * $Date: 2004-12-19 13:54:27 $
+ * 
+ * group functions
+ * 
+ * $Log: not supported by cvs2svn $
+ * Revision 1.2  2003/03/09 06:34:09  donut
+ * change byte typedef to sbyte to avoid conflict with win32 byte which is unsigned
+ *
+ * Revision 1.1.1.1  1999/06/14 22:03:16  donut
+ * Import of d1x 1.37 source.
+ *
+ * Revision 2.0  1995/02/27  11:35:05  john
+ * Version 2.0! No anonymous unions, Watcom 10.0, with no need
+ * for bitmaps.tbl.
+ * 
+ * Revision 1.65  1994/11/27  23:17:21  matt
+ * Made changes for new mprintf calling convention
+ * 
+ * Revision 1.64  1994/11/17  14:48:08  mike
+ * validation functions moved from editor to game.
+ * 
+ * Revision 1.63  1994/11/17  11:38:56  matt
+ * Ripped out code to load old mines
+ * 
+ * Revision 1.62  1994/10/27  10:06:20  mike
+ * adapt to no inverse table.
+ * 
+ * Revision 1.61  1994/10/03  23:40:08  mike
+ * New fuelcen_activate parameters.
+ * 
+ * Revision 1.60  1994/09/28  17:32:01  mike
+ * Make group copying work for copying a group's walls.
+ * 
+ * Revision 1.59  1994/09/20  14:35:28  mike
+ * Fix bugs in group subtraction code.  Don't allow to attach a group if the attach side is unfree.
+ * 
+ * Revision 1.58  1994/08/25  21:58:07  mike
+ * IS_CHILD stuff.
+ * 
+ * Revision 1.57  1994/08/04  19:12:58  matt
+ * Changed a bunch of vecmat calls to use multiple-function routines, and to
+ * allow the use of C macros for some functions
+ * 
+ * Revision 1.56  1994/08/03  15:40:01  mike
+ * Enable calls to compress_mine to get rid of bugs in group
+ * copying -- was creating invalid segments.
+ * 
+ * Revision 1.55  1994/06/30  10:59:13  yuan
+ * Fixed texture translations.
+ * 
+ * Revision 1.54  1994/06/22  17:36:00  mike
+ * Fix bug in group creation, was stuffing first two group segs over number
+ * of segments in group (then number would overwrite them), so there would
+ * be two bogus segments in group, one of which was always 0, the other
+ * would be a small number.
+ * 
+ * Revision 1.53  1994/06/14  17:07:15  john
+ * *** empty log message ***
+ * 
+ * Revision 1.52  1994/06/14  16:59:09  mike
+ * Fix references to tmap_num2, must strip off orientation bits.
+ * 
+ * Revision 1.51  1994/05/23  14:56:37  mike
+ * make current segment be add segment.
+ * 
+ * Revision 1.50  1994/05/19  12:10:01  matt
+ * Use new vecmat macros and globals
+ * 
+ * Revision 1.49  1994/05/17  10:33:59  matt
+ * Deleted unused get_free_object_num() func.
+ * 
+ * Revision 1.48  1994/05/09  23:34:17  mike
+ * Punch all sloppy sides in a group, speed up segment rotation.
+ * 
+ * Revision 1.47  1994/05/06  14:39:56  mike
+ * Make objects move and copy with groups.
+ * 
+ * Revision 1.46  1994/05/05  16:05:54  yuan
+ * Added fuelcen/repaircens to groups...
+ * 
+ * Eventually, walls will be added too...
+ * 
+ * Revision 1.45  1994/05/05  12:56:25  yuan
+ * Fixed a bunch of group bugs.
+ * 
+ * Revision 1.44  1994/05/04  14:10:04  mike
+ * Assert added to prevent bombing out when current_group = -1
+ * 
+ * Revision 1.43  1994/05/02  17:59:18  yuan
+ * Changed undo_status into an array rather than malloced pointers.
+ * 
+ * Revision 1.42  1994/05/02  15:23:19  mike
+ * Call med_combine_duplicate_vertices in med_copy_group and med_move_group.
+ * 
+ * Revision 1.41  1994/04/27  12:11:23  mike
+ * Fix bug in group rotation.
+ * 
+ * Revision 1.40  1994/04/22  10:07:37  yuan
+ * Make sure we don't get obj->next equal itself error.
+ * 
+ * Revision 1.39  1994/04/18  17:15:13  yuan
+ * Added error checking for select prev, and next group.
+ * 
+ */
+
+
+#ifdef RCS
+static char rcsid[] = "$Id: group.c,v 1.1 2004-12-19 13:54:27 btb Exp $";
+#endif
+
+
+#include <stdio.h>
+#include <string.h>
+
+#include "mono.h"
+#include "gr.h"
+#include "nocfile.h"
+#include "ui.h"
+
+#include "inferno.h"
+#include "segment.h"
+#include "editor/editor.h"
+#include "error.h"
+#include "gamemine.h"
+#include "gameseg.h"
+
+#include "bm.h"                                // For MAX_TEXTURES.
+#include "textures.h"
+#include "hash.h"
+#include "fuelcen.h"
+
+#include "medwall.h"
+
+void validate_selected_segments(void);
+
+struct {
+       int     fileinfo_version;
+       int     fileinfo_sizeof;
+} group_top_fileinfo;    // Should be same as first two fields below...
+
+struct {
+       int     fileinfo_version;
+       int     fileinfo_sizeof;
+       int     header_offset;                  // Stuff common to game & editor
+       int     header_size;
+       int     editor_offset;                          // Editor specific stuff
+       int     editor_size;
+       int     vertex_offset;
+       int     vertex_howmany;
+       int     vertex_sizeof;
+       int     segment_offset;
+       int     segment_howmany;
+       int     segment_sizeof;
+       int     texture_offset;
+       int     texture_howmany;
+       int     texture_sizeof;
+} group_fileinfo;
+
+struct {
+       int     num_vertices;
+       int     num_segments;
+} group_header;
+
+struct {
+       int     current_seg;
+       int     newsegment_offset;
+       int     newsegment_size;
+       int     Groupsegp;
+       int     Groupside;
+} group_editor;
+
+group          GroupList[MAX_GROUPS+1];
+segment  *Groupsegp[MAX_GROUPS+1];
+int            Groupside[MAX_GROUPS+1];
+int            Group_orientation[MAX_GROUPS+1];
+int            current_group=-1;
+int            num_groups=0;
+
+extern void validate_segment_side(segment *sp, int sidenum);
+
+// -- void swap_negate_columns(vms_matrix *rotmat, int col1, int col2)
+// -- {
+// --  fix     col1_1,col1_2,col1_3;
+// --  fix     col2_1,col2_2,col2_3;
+// -- 
+// --  switch (col1) {
+// --          case 0:
+// --                  col1_1 = rotmat->m1;
+// --                  col1_2 = rotmat->m2;
+// --                  col1_3 = rotmat->m3;
+// --                  break;
+// -- 
+// --          case 1:
+// --                  col1_1 = rotmat->m4;
+// --                  col1_2 = rotmat->m5;
+// --                  col1_3 = rotmat->m6;
+// --                  break;
+// -- 
+// --          case 2:
+// --                  col1_1 = rotmat->m7;
+// --                  col1_2 = rotmat->m8;
+// --                  col1_3 = rotmat->m9;
+// --                  break;
+// --  }
+// -- 
+// --  switch (col2) {
+// --          case 0:
+// --                  col2_1 = rotmat->m1;
+// --                  col2_2 = rotmat->m2;
+// --                  col2_3 = rotmat->m3;
+// --                  break;
+// -- 
+// --          case 1:
+// --                  col2_1 = rotmat->m4;
+// --                  col2_2 = rotmat->m5;
+// --                  col2_3 = rotmat->m6;
+// --                  break;
+// -- 
+// --          case 2:
+// --                  col2_1 = rotmat->m7;
+// --                  col2_2 = rotmat->m8;
+// --                  col2_3 = rotmat->m9;
+// --                  break;
+// --  }
+// -- 
+// --  switch (col2) {
+// --          case 0:
+// --                  rotmat->m1 = -col1_1;
+// --                  rotmat->m2 = -col1_2;
+// --                  rotmat->m3 = -col1_3;
+// --                  break;
+// -- 
+// --          case 1:
+// --                  rotmat->m4 = -col1_1;
+// --                  rotmat->m5 = -col1_2;
+// --                  rotmat->m6 = -col1_3;
+// --                  break;
+// -- 
+// --          case 2:
+// --                  rotmat->m7 = -col1_1;
+// --                  rotmat->m8 = -col1_2;
+// --                  rotmat->m9 = -col1_3;
+// --                  break;
+// --  }
+// -- 
+// --  switch (col1) {
+// --          case 0:
+// --                  rotmat->m1 = -col2_1;
+// --                  rotmat->m2 = -col2_2;
+// --                  rotmat->m3 = -col2_3;
+// --                  break;
+// -- 
+// --          case 1:
+// --                  rotmat->m4 = -col2_1;
+// --                  rotmat->m5 = -col2_2;
+// --                  rotmat->m6 = -col2_3;
+// --                  break;
+// -- 
+// --          case 2:
+// --                  rotmat->m7 = -col2_1;
+// --                  rotmat->m8 = -col2_2;
+// --                  rotmat->m9 = -col2_3;
+// --                  break;
+// --  }
+// -- 
+// -- }
+// -- 
+// -- void swap_negate_rows(vms_matrix *rotmat, int row1, int row2)
+// -- {
+// --  fix     row1_1,row1_2,row1_3;
+// --  fix     row2_1,row2_2,row2_3;
+// -- 
+// --  switch (row1) {
+// --          case 0:
+// --                  row1_1 = rotmat->m1;
+// --                  row1_2 = rotmat->m4;
+// --                  row1_3 = rotmat->m7;
+// --                  break;
+// -- 
+// --          case 1:
+// --                  row1_1 = rotmat->m2;
+// --                  row1_2 = rotmat->m5;
+// --                  row1_3 = rotmat->m8;
+// --                  break;
+// -- 
+// --          case 2:
+// --                  row1_1 = rotmat->m3;
+// --                  row1_2 = rotmat->m6;
+// --                  row1_3 = rotmat->m9;
+// --                  break;
+// --  }
+// -- 
+// --  switch (row2) {
+// --          case 0:
+// --                  row2_1 = rotmat->m1;
+// --                  row2_2 = rotmat->m4;
+// --                  row2_3 = rotmat->m7;
+// --                  break;
+// -- 
+// --          case 1:
+// --                  row2_1 = rotmat->m2;
+// --                  row2_2 = rotmat->m5;
+// --                  row2_3 = rotmat->m8;
+// --                  break;
+// -- 
+// --          case 2:
+// --                  row2_1 = rotmat->m3;
+// --                  row2_2 = rotmat->m6;
+// --                  row2_3 = rotmat->m9;
+// --                  break;
+// --  }
+// -- 
+// --  switch (row2) {
+// --          case 0:
+// --                  rotmat->m1 = -row1_1;
+// --                  rotmat->m4 = -row1_2;
+// --                  rotmat->m7 = -row1_3;
+// --                  break;
+// -- 
+// --          case 1:
+// --                  rotmat->m2 = -row1_1;
+// --                  rotmat->m5 = -row1_2;
+// --                  rotmat->m8 = -row1_3;
+// --                  break;
+// -- 
+// --          case 2:
+// --                  rotmat->m3 = -row1_1;
+// --                  rotmat->m6 = -row1_2;
+// --                  rotmat->m9 = -row1_3;
+// --                  break;
+// --  }
+// -- 
+// --  switch (row1) {
+// --          case 0:
+// --                  rotmat->m1 = -row2_1;
+// --                  rotmat->m4 = -row2_2;
+// --                  rotmat->m7 = -row2_3;
+// --                  break;
+// -- 
+// --          case 1:
+// --                  rotmat->m2 = -row2_1;
+// --                  rotmat->m5 = -row2_2;
+// --                  rotmat->m8 = -row2_3;
+// --                  break;
+// -- 
+// --          case 2:
+// --                  rotmat->m3 = -row2_1;
+// --                  rotmat->m6 = -row2_2;
+// --                  rotmat->m9 = -row2_3;
+// --                  break;
+// --  }
+// -- 
+// -- }
+// -- 
+// -- // ------------------------------------------------------------------------------------------------
+// -- void     side_based_matrix(vms_matrix *rotmat,int destside)
+// -- {
+// --  vms_angvec      rotvec;
+// --  vms_matrix      r1,rtemp;
+// -- 
+// --  switch (destside) {
+// --          case WLEFT:
+// -- //                       swap_negate_columns(rotmat,1,2);
+// -- //                       swap_negate_rows(rotmat,1,2);
+// --                  break;
+// -- 
+// --          case WTOP:
+// --                  break;
+// -- 
+// --          case WRIGHT:
+// -- //                       swap_negate_columns(rotmat,1,2);
+// -- //                       swap_negate_rows(rotmat,1,2);
+// --                  break;
+// -- 
+// --          case WBOTTOM:
+// --                  break;
+// -- 
+// --          case WFRONT:
+// --                  break;
+// -- 
+// --          case WBACK:
+// --                  break;
+// --  }
+// -- 
+// -- }
+
+
+// ------------------------------------------------------------------------------------------------
+//     Rotate a group about a point.
+//     The segments in the group are indicated (by segment number) in group_seglist.  There are group_size segments.
+//     The point about which the groups is rotated is the center of first_seg:first_side.
+//     delta_flag:
+//             0       absolute rotation, destination specified in terms of base_seg:base_side, used in moving or copying a group
+//             1       relative rotation, destination specified relative to current orientation of first_seg:first_side
+//     Note: The group must exist in the mine, consisting of actual points in the world.  If any points in the
+//                     segments in the group are shared by segments not in the group, those points will get rotated and the
+//                     segments not in the group will have their shapes modified.
+//     Return value:
+//             0       group rotated
+//             1       unable to rotate group
+void med_create_group_rotation_matrix(vms_matrix *result_mat, int delta_flag, segment *first_seg, int first_side, segment *base_seg, int base_side, vms_matrix *orient_matrix, int orientation)
+{
+       vms_matrix      rotmat2,rotmat,rotmat3,rotmat4;
+       vms_angvec      pbh = {0,0,0};
+
+       //      Determine whether this rotation is a delta rotation, meaning to just rotate in place, or an absolute rotation,
+       //      which means that the destination rotation is specified, not as a delta, but as an absolute
+       if (delta_flag) {
+               //      Create rotation matrix describing rotation.
+               med_extract_matrix_from_segment(first_seg, &rotmat4);           // get rotation matrix describing current orientation of first seg
+               set_matrix_based_on_side(&rotmat4, first_side);
+               rotmat3 = *orient_matrix;
+               vm_transpose_matrix(&rotmat3);
+               vm_matrix_x_matrix(&rotmat,&rotmat4,&rotmat3);                  // this is the desired orientation of the new segment
+               vm_transpose_matrix(&rotmat4);
+               vm_matrix_x_matrix(&rotmat2,&rotmat,&rotmat4);                  // this is the desired orientation of the new segment
+       } else {
+               //      Create rotation matrix describing rotation.
+               med_extract_matrix_from_segment(base_seg, &rotmat);             // get rotation matrix describing desired orientation
+               set_matrix_based_on_side(&rotmat, base_side);                           // modify rotation matrix for desired side
+               //      If the new segment is to be attached without rotation, then its orientation is the same as the base_segment
+               vm_matrix_x_matrix(&rotmat4,&rotmat,orient_matrix);                     // this is the desired orientation of the new segment
+
+               pbh.b = orientation*16384;
+               vm_angles_2_matrix(&rotmat3,&pbh);
+               vm_matrix_x_matrix(&rotmat, &rotmat4, &rotmat3);
+               rotmat4 = rotmat;
+
+               rotmat = rotmat4;
+
+               med_extract_matrix_from_segment(first_seg, &rotmat3);           // get rotation matrix describing current orientation of first seg
+               // It is curious that the following statement has no analogue in the med_attach_segment_rotated code.
+               //      Perhaps it is because segments are always attached at their front side.  If the back side is the side
+               //      passed to the function, then the matrix is not modified, which might suggest that what you need to do below
+               //      is use Side_opposite[first_side].
+               set_matrix_based_on_side(&rotmat3, Side_opposite[first_side]);                          // modify rotation matrix for desired side
+               vm_transpose_matrix(&rotmat3);                                                          // get the inverse of the current orientation matrix
+               vm_matrix_x_matrix(&rotmat2,&rotmat,&rotmat3);                  // now rotmat2 takes the current segment to the desired orientation
+               vm_transpose_matrix(&rotmat2);
+       }
+
+       *result_mat = rotmat2;
+
+}
+
+// -----------------------------------------------------------------------------------------
+// Rotate all vertices and objects in group.
+void med_rotate_group(vms_matrix *rotmat, short *group_seglist, int group_size, segment *first_seg, int first_side)
+{
+       int                     v,s, objnum;
+       sbyte                   vertex_list[MAX_VERTICES];
+       vms_vector      rotate_center;
+
+       compute_center_point_on_side(&rotate_center, first_seg, first_side);
+
+       //      Create list of points to rotate.
+       for (v=0; v<=Highest_vertex_index; v++)
+               vertex_list[v] = 0;
+
+       for (s=0; s<group_size; s++) {
+               segment *sp = &Segments[group_seglist[s]];
+
+               for (v=0; v<MAX_VERTICES_PER_SEGMENT; v++)
+                       vertex_list[sp->verts[v]] = 1;
+
+               //      Rotate center of all objects in group.
+               objnum = sp->objects;
+               while (objnum != -1) {
+                       vms_vector      tv, tv1;
+
+                       mprintf((0, "%2i ", objnum));
+                       vm_vec_sub(&tv1,&Objects[objnum].pos,&rotate_center);
+                       vm_vec_rotate(&tv,&tv1,rotmat);
+                       vm_vec_add(&Objects[objnum].pos, &tv, &rotate_center);
+
+                       objnum = Objects[objnum].next;
+               }                       
+       }
+
+       // Do the pre-rotation xlate, do the rotation, do the post-rotation xlate
+       for (v=0; v<=Highest_vertex_index; v++)
+               if (vertex_list[v]) {
+                       vms_vector      tv,tv1;
+
+                       vm_vec_sub(&tv1,&Vertices[v],&rotate_center);
+                       vm_vec_rotate(&tv,&tv1,rotmat);
+                       vm_vec_add(&Vertices[v],&tv,&rotate_center);
+
+               }
+
+}
+
+
+// ------------------------------------------------------------------------------------------------
+void cgl_aux(segment *segp, short *seglistp, int *num_segs, short *ignore_list, int num_ignore_segs)
+{
+       int     i, side;
+       int     curseg = segp-Segments;
+
+       for (i=0; i<num_ignore_segs; i++)
+               if (curseg == ignore_list[i])
+                       return;
+
+       if ((segp-Segments < 0) || (segp-Segments >= MAX_SEGMENTS)) {
+               mprintf((0,"Warning -- invalid segment index = %i, max = %i\n",segp-Segments,MAX_SEGMENTS));
+               Int3();
+       }
+
+       if (!Been_visited[segp-Segments]) {
+               seglistp[(*num_segs)++] = segp-Segments;
+               Been_visited[segp-Segments] = 1;
+
+               for (side=0; side<MAX_SIDES_PER_SEGMENT; side++)
+                       if (IS_CHILD(segp->children[side]))
+                               cgl_aux(&Segments[segp->children[side]], seglistp, num_segs, ignore_list, num_ignore_segs);
+       }
+}
+
+// ------------------------------------------------------------------------------------------------
+//     Sets Been_visited[n] if n is reachable from segp
+void create_group_list(segment *segp, short *seglistp, int *num_segs, short *ignore_list, int num_ignore_segs)
+{
+       int     i;
+
+       for (i=0; i<MAX_SEGMENTS; i++)
+               Been_visited[i] = 0;
+
+       cgl_aux(segp, seglistp, num_segs, ignore_list, num_ignore_segs);
+}
+
+
+#define MXS MAX_SEGMENTS
+#define MXV MAX_VERTICES
+
+// ------------------------------------------------------------------------------------------------
+void duplicate_group(sbyte *vertex_ids, short *segment_ids, int num_segments)
+{
+       int     v,s,ss,new_vertex_id,new_segment_id,sidenum;
+       short   new_segment_ids[MAX_SEGMENTS];
+       short   new_vertex_ids[MAX_VERTICES];           // If new_vertex_ids[v] != -1, then vertex v has been remapped to new_vertex_ids[v]
+       short   new_object_ids[MAX_OBJECTS];
+
+       //      duplicate vertices
+       for (v=0; v<MXV; v++)
+               new_vertex_ids[v] = -1;
+
+       for (v=0; v<MAX_OBJECTS; v++)
+               new_object_ids[v] = -1;
+
+       //      duplicate vertices
+       for (v=0; v<=Highest_vertex_index; v++) {
+               if (vertex_ids[v]) {
+                       new_vertex_id = med_create_duplicate_vertex(&Vertices[v]);
+                       new_vertex_ids[v] = new_vertex_id;
+               }
+       }
+
+       //      duplicate segments
+       for (s=0; s<num_segments; s++) {
+               int     objnum;
+
+               new_segment_id = med_create_duplicate_segment(&Segments[segment_ids[s]]);
+               new_segment_ids[s] = new_segment_id;
+               objnum = Segments[new_segment_id].objects;
+               Segments[new_segment_id].objects = -1;
+               while (objnum != -1) {
+                       if (Objects[objnum].type != OBJ_PLAYER) {
+                               int new_obj_id;
+                               new_obj_id = obj_create_copy(objnum, &Objects[objnum].pos, new_segment_id);
+                               mprintf((0, "Object #%i in segment #%i copied to object #%i, segment #%i: new_obj->segnum = %i\n", objnum, Objects[objnum].segnum, new_obj_id, new_segment_id, Objects[new_obj_id].segnum));
+                       }
+                       objnum = Objects[objnum].next;
+               }
+       }
+
+       //      Now, for each segment in segment_ids, correct its children numbers by translating through new_segment_ids
+       //      and correct its vertex numbers by translating through new_vertex_ids
+       for (s=0; s<num_segments; s++) {
+               segment *sp = &Segments[new_segment_ids[s]];
+               for (sidenum=0; sidenum<MAX_SIDES_PER_SEGMENT; sidenum++) {
+                       int seg = sp->children[sidenum];
+                       if (IS_CHILD(seg)) {
+                               for (ss=0; ss<num_segments; ss++) {
+                                       if (seg == segment_ids[ss])
+                                               Segments[new_segment_ids[s]].children[sidenum] = new_segment_ids[ss];
+                               }
+                       }
+               }       // end for (sidenum=0...
+
+               //      Now fixup vertex ids
+               for (v=0; v<MAX_VERTICES_PER_SEGMENT; v++) {
+                       if (vertex_ids[sp->verts[v]]) {
+                               sp->verts[v] = new_vertex_ids[sp->verts[v]];
+                       }
+               }
+       }       // end for (s=0...
+
+       //      Now, copy new_segment_ids into segment_ids
+       for (s=0; s<num_segments; s++) {
+               segment_ids[s] = new_segment_ids[s];
+       }
+
+       //      Now, copy new_vertex_ids into vertex_ids
+       for (v=0; v<MXV; v++)
+               vertex_ids[v] = 0;
+
+       for (v=0; v<MXV; v++) {
+               if (new_vertex_ids[v] != -1)
+                       vertex_ids[new_vertex_ids[v]] = 1;
+
+       }
+}
+
+
+// ------------------------------------------------------------------------------------------------
+int in_group(int segnum, int group_num)
+{
+       int     i;
+
+       for (i=0; i<GroupList[group_num].num_segments; i++)
+               if (segnum == GroupList[group_num].segments[i])
+                       return 1;
+
+       return 0;
+}
+
+// ------------------------------------------------------------------------------------------------
+//     Copy a group of segments.
+//     The group is defined as all segments accessible from group_seg.
+//     The group is copied so group_seg:group_side is incident upon base_seg:base_side.
+//     group_seg and its vertices are bashed to coincide with base_seg.
+//     If any vertex of base_seg is contained in a segment that is reachable from group_seg, then errror.
+int med_copy_group(int delta_flag, segment *base_seg, int base_side, segment *group_seg, int group_side, vms_matrix *orient_matrix)
+{
+       int                     v,s;
+       vms_vector      srcv,destv;
+       int                     x;
+       int                     new_current_group;
+       segment         *segp;
+       int                     c;
+        int                     gs_index=0;
+       sbyte                   in_vertex_list[MAX_VERTICES];
+       vms_matrix      rotmat;
+       int                     objnum;
+
+       if (IS_CHILD(base_seg->children[base_side])) {
+               editor_status("Error -- unable to copy group, base_seg:base_side must be free.");
+               return 1;
+       }
+
+       if (num_groups == MAX_GROUPS) {
+               x = MessageBox( -2, -2, 2, "Warning: You have reached the MAXIMUM group number limit. Continue?", "No", "Yes" );
+               if (x==1)
+                       return 0;
+       }
+
+       if (num_groups < MAX_GROUPS) {
+               num_groups++;
+               new_current_group = num_groups-1;
+       } else
+               new_current_group = 0;
+
+       Assert(current_group >= 0);
+
+       // Find groupsegp index
+       for (s=0;s<GroupList[current_group].num_segments;s++)
+               if (GroupList[current_group].segments[s] == (Groupsegp[current_group]-Segments))
+                       gs_index=s; 
+
+       GroupList[new_current_group] = GroupList[current_group];
+
+       //      Make a list of all vertices in group.
+       if (group_seg == &New_segment)
+               for (v=0; v<MAX_VERTICES_PER_SEGMENT; v++)
+                       in_vertex_list[group_seg->verts[v]] = 1;
+       else {
+               for (v=0; v<=Highest_vertex_index; v++)
+                       in_vertex_list[v] = 0;
+
+               for (s=0; s<GroupList[new_current_group].num_segments; s++)
+                       for (v=0; v<MAX_VERTICES_PER_SEGMENT; v++)
+                               in_vertex_list[Segments[GroupList[new_current_group].segments[s]].verts[v]] = 1;
+       }
+
+       //      Show which objects are in which segments before group copy.
+       // for (s=0; s<=Highest_segment_index; s++) {
+               // int  objnum = Segments[s].objects;
+
+               // mprintf((0, "Before: Segment #%2i contains objects ", s));
+
+               // while (objnum != -1) {
+               //      mprintf((0, "%2i ",objnum));
+               //      objnum = Objects[objnum].next;
+               // }
+               // mprintf((0, "\n"));
+       // }
+
+       // Given a list of vertex indices (indicated by !0 in in_vertex_list) and segment indices (in list GroupList[current_group].segments, there
+       //      are GroupList[current_group].num_segments segments), copy all segments and vertices
+       //      Return updated lists of vertices and segments in in_vertex_list and GroupList[current_group].segments
+       duplicate_group(in_vertex_list, GroupList[new_current_group].segments, GroupList[new_current_group].num_segments);
+
+       //group_seg = &Segments[GroupList[new_current_group].segments[0]];                                      // connecting segment in group has been changed, so update group_seg
+
+   Groupsegp[new_current_group] = group_seg = &Segments[GroupList[new_current_group].segments[gs_index]];
+       Groupside[new_current_group] = Groupside[current_group];
+
+       for (s=0; s<GroupList[new_current_group].num_segments; s++) {
+               Segments[GroupList[new_current_group].segments[s]].group = new_current_group;
+               Segments[GroupList[new_current_group].segments[s]].special = SEGMENT_IS_NOTHING;
+               Segments[GroupList[new_current_group].segments[s]].matcen_num = -1;
+       }
+
+       // Breaking connections between segments in the current group and segments not in the group.
+       for (s=0; s<GroupList[new_current_group].num_segments; s++) {
+               mprintf((0, "[%3i %3i] ", GroupList[new_current_group].segments[s], GroupList[current_group].segments[s]));
+               segp = &Segments[GroupList[new_current_group].segments[s]];
+               for (c=0; c<MAX_SIDES_PER_SEGMENT; c++) 
+                       if (IS_CHILD(segp->children[c])) {
+                               if (!in_group(segp->children[c], new_current_group)) {
+                                       mprintf((0, "2: Breaking connection at seg:side = %i:%i\n", segp-Segments, c));
+                                       segp->children[c] = -1;
+                                       validate_segment_side(segp,c);                                  // we have converted a connection to a side so validate the segment
+                               }
+                       }
+       }
+
+       copy_uvs_seg_to_seg(&New_segment, Groupsegp[new_current_group]);
+       
+       //      Now do the copy
+       //      First, xlate all vertices so center of group_seg:group_side is at origin
+       compute_center_point_on_side(&srcv,group_seg,group_side);
+       for (v=0; v<=Highest_vertex_index; v++)
+               if (in_vertex_list[v])
+                       vm_vec_sub2(&Vertices[v],&srcv);
+
+       //      Now, translate all object positions.
+       for (s=0; s<GroupList[new_current_group].num_segments; s++) {
+               int     segnum = GroupList[new_current_group].segments[s];
+
+               objnum = Segments[segnum].objects;
+
+               while (objnum != -1) {
+                       vm_vec_sub2(&Objects[objnum].pos, &srcv);
+                       objnum = Objects[objnum].next;
+               }
+       }
+
+       //      Now, rotate segments in group so orientation of group_seg is same as base_seg.
+       med_create_group_rotation_matrix(&rotmat, delta_flag, group_seg, group_side, base_seg, base_side, orient_matrix, 0);
+       med_rotate_group(&rotmat, GroupList[new_current_group].segments, GroupList[new_current_group].num_segments, group_seg, group_side);
+
+       //      Now xlate all vertices so group_seg:group_side shares center point with base_seg:base_side
+       compute_center_point_on_side(&destv,base_seg,base_side);
+       for (v=0; v<=Highest_vertex_index; v++)
+               if (in_vertex_list[v])
+                       vm_vec_add2(&Vertices[v],&destv);
+
+       //      Now, xlate all object positions.
+       for (s=0; s<GroupList[new_current_group].num_segments; s++) {
+               int     segnum = GroupList[new_current_group].segments[s];
+               int     objnum = Segments[segnum].objects;
+
+               while (objnum != -1) {
+                       vm_vec_add2(&Objects[objnum].pos, &destv);
+                       objnum = Objects[objnum].next;
+               }
+       }
+
+       //      Now, copy all walls (ie, doors, illusionary, etc.) into the new group.
+       copy_group_walls(current_group, new_current_group);
+
+       current_group = new_current_group;
+
+       //      Now, form joint on connecting sides.
+       med_form_joint(base_seg,base_side,Groupsegp[current_group],Groupside[new_current_group]);
+
+       validate_selected_segments();
+       med_combine_duplicate_vertices(in_vertex_list);
+
+       return 0;
+}
+
+
+// ------------------------------------------------------------------------------------------------
+//     Move a group of segments.
+//     The group is defined as all segments accessible from group_seg.
+//     The group is moved so group_seg:group_side is incident upon base_seg:base_side.
+//     group_seg and its vertices are bashed to coincide with base_seg.
+//     If any vertex of base_seg is contained in a segment that is reachable from group_seg, then errror.
+int med_move_group(int delta_flag, segment *base_seg, int base_side, segment *group_seg, int group_side, vms_matrix *orient_matrix, int orientation)
+{
+       int                     v,vv,s,ss,c,d;
+       vms_vector      srcv,destv;
+       segment         *segp, *csegp, *dsegp;
+       sbyte                   in_vertex_list[MAX_VERTICES], out_vertex_list[MAX_VERTICES];
+       int                     local_hvi;
+       vms_matrix      rotmat;
+
+       if (IS_CHILD(base_seg->children[base_side]))
+               if (base_seg->children[base_side] != group_seg-Segments) {
+                       editor_status("Error -- unable to move group, base_seg:base_side must be free or point to group_seg.");
+                       return 1;
+       }
+
+//     // See if any vertices in base_seg are contained in any vertex in group_list
+//     for (v=0; v<MAX_VERTICES_PER_SEGMENT; v++)
+//             for (s=0; s<GroupList[current_group].num_segments; s++)
+//                     for (vv=0; vv<MAX_VERTICES_PER_SEGMENT; vv++)
+//                             if (Segments[GroupList[current_group].segments[s]].verts[vv] == base_seg->verts[v]) {
+//                                     editor_status("Error -- unable to move group, it shares a vertex with destination segment.");
+//                                     return 1;
+//                             }
+
+       for (v=0; v<=Highest_vertex_index; v++) {
+               in_vertex_list[v] = 0;
+               out_vertex_list[v] = 0;
+       }
+
+       //      Make a list of all vertices in group.
+       for (s=0; s<GroupList[current_group].num_segments; s++)
+               for (v=0; v<MAX_VERTICES_PER_SEGMENT; v++)
+                       in_vertex_list[Segments[GroupList[current_group].segments[s]].verts[v]] = 1;
+
+       //      For all segments which are not in GroupList[current_group].segments, mark all their vertices in the out list.
+       for (s=0; s<=Highest_segment_index; s++) {
+               for (ss=0; ss<GroupList[current_group].num_segments; ss++)
+                       if (GroupList[current_group].segments[ss] == s)
+                               break;
+               if (ss == GroupList[current_group].num_segments)
+                       for (v=0; v<MAX_VERTICES_PER_SEGMENT; v++)
+                               out_vertex_list[Segments[s].verts[v]] = 1;
+       }
+
+       //      Now, for all vertices present in both the in (part of group segment) and out (part of non-group segment)
+       // create an extra copy of the vertex so we can just move the ones in the in list.
+       local_hvi = Highest_vertex_index;               //      Can't use Highest_vertex_index as loop termination because it gets increased by med_create_duplicate_vertex.
+
+       for (v=0; v<=local_hvi; v++)
+               if (in_vertex_list[v])
+                       if (out_vertex_list[v]) {
+                               int new_vertex_id;
+
+                               new_vertex_id = med_create_duplicate_vertex(&Vertices[v]);
+                               in_vertex_list[v] = 0;
+                               in_vertex_list[new_vertex_id] = 1;
+
+                               // Create a new vertex and assign all occurrences of vertex v in IN list to new vertex number.
+                               for (s=0; s<GroupList[current_group].num_segments; s++) {
+                                       segment *sp = &Segments[GroupList[current_group].segments[s]];
+                                       for (vv=0; vv<MAX_VERTICES_PER_SEGMENT; vv++)
+                                               if (sp->verts[vv] == v)
+                                                       sp->verts[vv] = new_vertex_id;
+                               }
+                       }
+
+       for (s=0;s<GroupList[current_group].num_segments;s++)
+               Segments[GroupList[current_group].segments[s]].group = current_group;
+
+       // Breaking connections between segments in the group and segments not in the group.
+       for (s=0; s<GroupList[current_group].num_segments; s++)
+               {
+               segp = &Segments[GroupList[current_group].segments[s]];
+               for (c=0; c<MAX_SIDES_PER_SEGMENT; c++) 
+                       if (IS_CHILD(segp->children[c]))
+                               {
+                               csegp = &Segments[segp->children[c]];
+                               if (csegp->group != current_group)
+                                       {
+                                       for (d=0; d<MAX_SIDES_PER_SEGMENT; d++)
+                                               if (IS_CHILD(csegp->children[d]))
+                                                       {
+                                                       dsegp = &Segments[csegp->children[d]];
+                                                       if (dsegp->group == current_group)
+                                                               {
+                                                               csegp->children[d] = -1;
+                                                               validate_segment_side(csegp,d);                                 // we have converted a connection to a side so validate the segment
+                                                               }
+                                                       }
+                                       segp->children[c] = -1;
+                                       validate_segment_side(segp,c);                                  // we have converted a connection to a side so validate the segment
+                                       }
+                               }
+               }
+
+       copy_uvs_seg_to_seg(&New_segment, Groupsegp[current_group]);
+
+       //      Now do the move
+       //      First, xlate all vertices so center of group_seg:group_side is at origin
+       compute_center_point_on_side(&srcv,group_seg,group_side);
+       for (v=0; v<=Highest_vertex_index; v++)
+               if (in_vertex_list[v])
+                       vm_vec_sub2(&Vertices[v],&srcv);
+
+       //      Now, move all object positions.
+       for (s=0; s<GroupList[current_group].num_segments; s++) {
+               int     segnum = GroupList[current_group].segments[s];
+               int     objnum = Segments[segnum].objects;
+
+               // mprintf((0, "Translating objects in segment #%2i by [%7.3f %7.3f %7.3f]: ", segnum, f2fl(srcv.x), f2fl(srcv.y), f2fl(srcv.z)));
+
+               while (objnum != -1) {
+                       mprintf((0, "%2i ", objnum));
+                       vm_vec_sub2(&Objects[objnum].pos, &srcv);
+                       objnum = Objects[objnum].next;
+               }
+       }
+       // mprintf((0, "\n"));
+
+       //      Now, rotate segments in group so orientation of group_seg is same as base_seg.
+       med_create_group_rotation_matrix(&rotmat, delta_flag, group_seg, group_side, base_seg, base_side, orient_matrix, orientation);
+       med_rotate_group(&rotmat, GroupList[current_group].segments, GroupList[current_group].num_segments, group_seg, group_side);
+
+       //      Now xlate all vertices so group_seg:group_side shares center point with base_seg:base_side
+       compute_center_point_on_side(&destv,base_seg,base_side);
+       for (v=0; v<=Highest_vertex_index; v++)
+               if (in_vertex_list[v])
+                       vm_vec_add2(&Vertices[v],&destv);
+
+       //      Now, rotate all object positions.
+       for (s=0; s<GroupList[current_group].num_segments; s++) {
+               int     segnum = GroupList[current_group].segments[s];
+               int     objnum = Segments[segnum].objects;
+
+               while (objnum != -1) {
+                       vm_vec_add2(&Objects[objnum].pos, &destv);
+                       objnum = Objects[objnum].next;
+               }
+       }
+
+       //      Now, form joint on connecting sides.
+       med_form_joint(base_seg,base_side,group_seg,group_side);
+
+       validate_selected_segments();
+       med_combine_duplicate_vertices(in_vertex_list);
+
+       return 0;
+}
+
+
+//     -----------------------------------------------------------------------------
+int place_new_segment_in_world(void)
+{
+       int     v,segnum;
+
+       segnum = get_free_segment_number();
+
+       Segments[segnum] = New_segment;
+
+       for (v=0; v<MAX_VERTICES_PER_SEGMENT; v++)
+               Segments[segnum].verts[v] = med_create_duplicate_vertex(&Vertices[New_segment.verts[v]]);
+
+       return segnum;
+
+}
+
+//     -----------------------------------------------------------------------------
+//     Attach segment in the new-fangled way, which is by using the CopyGroup code.
+int AttachSegmentNewAng(vms_angvec *pbh)
+{
+       int                     newseg;
+       vms_matrix      orient_matrix;
+
+       GroupList[current_group].num_segments = 1;
+       newseg = place_new_segment_in_world();
+       GroupList[current_group].segments[0] = newseg;
+
+       if (!med_move_group(1, Cursegp, Curside, &Segments[newseg], AttachSide, vm_angles_2_matrix(&orient_matrix,pbh),0)) {
+               autosave_mine(mine_filename);
+
+               med_propagate_tmaps_to_segments(Cursegp,&Segments[newseg],0);
+               med_propagate_tmaps_to_back_side(&Segments[newseg], Side_opposite[AttachSide],0);
+               copy_uvs_seg_to_seg(&New_segment,&Segments[newseg]);
+
+               Cursegp = &Segments[newseg];
+               Curside = Side_opposite[AttachSide];
+               med_create_new_segment_from_cursegp();
+
+               if (Lock_view_to_cursegp)
+                       set_view_target_from_segment(Cursegp);
+
+               Update_flags |= UF_WORLD_CHANGED;
+               mine_changed = 1;
+               warn_if_concave_segment(Cursegp);
+       }
+
+       return 1;
+}
+
+int AttachSegmentNew(void)
+{
+       vms_angvec      pbh;
+
+       pbh.p = 0;
+       pbh.b = 0;
+       pbh.h = 0;
+
+       AttachSegmentNewAng(&pbh);
+       return 1;
+
+}
+
+//     -----------------------------------------------------------------------------
+void save_selected_segs(int *num, short *segs)
+{
+       int     i;
+
+       for (i=0; i<GroupList[current_group].num_segments; i++)
+               segs[i] = GroupList[current_group].segments[i];
+
+       *num = GroupList[current_group].num_segments;
+}
+
+//     -----------------------------------------------------------------------------
+void restore_selected_segs(int num, short *segs)
+{
+       int     i;
+
+       for (i=0; i<GroupList[current_group].num_segments; i++)
+               GroupList[current_group].segments[i] = segs[i];
+
+       GroupList[current_group].num_segments = num;
+}
+
+//     -----------------------------------------------------------------------------
+void validate_selected_segments(void)
+{
+       int     i;
+
+       for (i=0; i<GroupList[current_group].num_segments; i++)
+               validate_segment(&Segments[GroupList[current_group].segments[i]]);
+}
+
+// =====================================================================================
+
+
+//     -----------------------------------------------------------------------------
+void delete_segment_from_group(int segment_num, int group_num)
+{
+       int g, del_seg_index;
+       
+       del_seg_index = -1;
+       for (g=0; g<GroupList[group_num].num_segments; g++)
+               if (segment_num == GroupList[group_num].segments[g]) {  
+                       del_seg_index = g;
+                       break;
+               }
+
+       //mprintf((0, "segment_num=%d delseg_index=%d\n", segment_num, del_seg_index)); 
+       
+       if (IS_CHILD(del_seg_index)) {
+               for (g=del_seg_index;g<GroupList[group_num].num_segments-1;g++) { 
+                       GroupList[group_num].segments[g] = GroupList[group_num].segments[g+1];
+                       }
+               GroupList[group_num].num_segments--;
+               //mprintf((0, "num_segments=%d\n\n", GroupList[group_num].num_segments));
+               Segments[segment_num].group = -1;               
+               }
+
+}
+// =====================================================================================
+
+
+//     -----------------------------------------------------------------------------
+void add_segment_to_group(int segment_num, int group_num)
+{  
+       GroupList[group_num].num_segments++;
+       GroupList[group_num].segments[GroupList[group_num].num_segments-1] = segment_num;
+}
+// =====================================================================================
+
+
+//     -----------------------------------------------------------------------------
+int rotate_segment_new(vms_angvec *pbh)
+{
+       int                     newseg,baseseg,newseg_side,baseseg_side;
+       vms_matrix      orient_matrix,tm1,tm2;
+       int                     n_selected_segs_save;
+       short                   selected_segs_save[MAX_SEGMENTS];
+       int                     child_save;
+       int                     current_group_save;
+
+        if (!IS_CHILD(Cursegp->children[(int) Side_opposite[Curside]])) {
+               // -- I don't understand this, MK, 01/25/94: if (Cursegp->children[Curside] != group_seg-Segments) {
+                       editor_status("Error -- unable to rotate group, Cursegp:Side_opposite[Curside] cannot be free.");
+                       return 1;
+       }
+
+       current_group_save = current_group;
+       current_group = ROT_GROUP;
+       Groupsegp[ROT_GROUP] = Cursegp;
+       
+       save_selected_segs(&n_selected_segs_save, selected_segs_save);
+       GroupList[ROT_GROUP].num_segments = 0;
+       newseg = Cursegp - Segments;
+       newseg_side = Side_opposite[Curside];
+
+       // Create list of segments to rotate.
+       //      Sever connection between first seg to rotate and its connection on Side_opposite[Curside].
+       child_save = Cursegp->children[newseg_side];    // save connection we are about to sever
+       Cursegp->children[newseg_side] = -1;                    // sever connection
+       create_group_list(Cursegp, GroupList[ROT_GROUP].segments, &GroupList[ROT_GROUP].num_segments, Selected_segs, 0);       // create list of segments in group
+       //mprintf((0, "NumSegs = %d\n", GroupList[ROT_GROUP].num_segments));
+       Cursegp->children[newseg_side] = child_save;    // restore severed connection
+       GroupList[ROT_GROUP].segments[0] = newseg;
+
+       baseseg = Segments[newseg].children[newseg_side];
+       if (!IS_CHILD(baseseg)) {
+               editor_status("Error -- unable to rotate segment, side opposite curside is not attached.");
+               restore_selected_segs(n_selected_segs_save,selected_segs_save);
+               current_group = current_group_save;
+               return 1;
+       }
+
+       baseseg_side = find_connect_side(&Segments[newseg], &Segments[baseseg]);
+
+       med_extract_matrix_from_segment(&Segments[newseg],&tm1);
+       tm1 = vmd_identity_matrix;
+       vm_angles_2_matrix(&tm2,pbh);
+       vm_matrix_x_matrix(&orient_matrix,&tm1,&tm2);
+
+       Segments[baseseg].children[baseseg_side] = -1;
+       Segments[newseg].children[newseg_side] = -1;
+
+       if (!med_move_group(1, &Segments[baseseg], baseseg_side, &Segments[newseg], newseg_side, &orient_matrix, 0)) {
+               Cursegp = &Segments[newseg];
+               med_create_new_segment_from_cursegp();
+//             validate_selected_segments();
+               med_propagate_tmaps_to_segments(&Segments[baseseg], &Segments[newseg], 1);
+               med_propagate_tmaps_to_back_side(&Segments[newseg], Curside, 1);
+       }
+
+       restore_selected_segs(n_selected_segs_save,selected_segs_save);
+       current_group = current_group_save;
+
+       return 1;
+}
+
+//     -----------------------------------------------------------------------------
+//     Attach segment in the new-fangled way, which is by using the CopyGroup code.
+int RotateSegmentNew(vms_angvec *pbh)
+{
+       int     rval;
+
+       autosave_mine(mine_filename);
+
+       rval = rotate_segment_new(pbh);
+
+       if (Lock_view_to_cursegp)
+               set_view_target_from_segment(Cursegp);
+
+       Update_flags |= UF_WORLD_CHANGED;
+       mine_changed = 1;
+       warn_if_concave_segment(Cursegp);
+
+       return rval;
+}
+
+static char     current_tmap_list[MAX_TEXTURES][13];
+
+// -----------------------------------------------------------------------------
+// Save mine will:
+// 1. Write file info, header info, editor info, vertex data, segment data,
+//    and new_segment in that order, marking their file offset.
+// 2. Go through all the fields and fill in the offset, size, and sizeof
+//    values in the headers.
+int med_save_group( char *filename, short *vertex_ids, short *segment_ids, int num_vertices, int num_segments)
+{
+       FILE * SaveFile;
+       int header_offset, editor_offset, vertex_offset, segment_offset, texture_offset;
+       char ErrorMessage[100];
+       int i, j, k;
+       int segnum;
+       segment tseg;
+   vms_vector tvert;
+       int found;
+
+       SaveFile = fopen( filename, "wb" );
+       if (!SaveFile)
+       {
+               sprintf( ErrorMessage, "ERROR: Unable to open %s\n", filename );
+               MessageBox( -2, -2, 1, ErrorMessage, "Ok" );
+               return 1;
+       }
+
+       //===================== SAVE FILE INFO ========================
+
+       group_fileinfo.fileinfo_version  =   MINE_VERSION;
+       group_fileinfo.fileinfo_sizeof   =   sizeof(group_fileinfo);
+       group_fileinfo.header_offset     =   -1;
+       group_fileinfo.header_size       =   sizeof(group_header);
+       group_fileinfo.editor_offset     =   -1;
+       group_fileinfo.editor_size       =   sizeof(group_editor);
+       group_fileinfo.vertex_offset     =   -1;
+       group_fileinfo.vertex_howmany    =   num_vertices;
+       group_fileinfo.vertex_sizeof     =   sizeof(vms_vector);
+       group_fileinfo.segment_offset    =   -1;
+       group_fileinfo.segment_howmany   =   num_segments;
+       group_fileinfo.segment_sizeof    =   sizeof(segment);
+       group_fileinfo.texture_offset    =   -1;
+       group_fileinfo.texture_howmany   =   0;
+       group_fileinfo.texture_sizeof    =   13;  // num characters in a name
+
+       // Write the fileinfo
+       fwrite( &group_fileinfo, sizeof(group_fileinfo), 1, SaveFile );
+
+       //===================== SAVE HEADER INFO ========================
+
+       group_header.num_vertices        =   num_vertices;
+       group_header.num_segments        =   num_segments;
+
+       // Write the editor info
+       header_offset = ftell(SaveFile);
+       fwrite( &group_header, sizeof(group_header), 1, SaveFile );
+
+       //===================== SAVE EDITOR INFO ==========================
+       group_editor.newsegment_offset   =   -1; // To be written
+       group_editor.newsegment_size     =   sizeof(segment);
+       // Next 3 vars added 10/07 by JAS
+       if (Groupsegp[current_group]) {
+               segnum = Groupsegp[current_group]-Segments;
+               for (i=0;i<num_segments;i++)
+                       if (segnum == segment_ids[i])   
+                               group_editor.Groupsegp = i;
+       } 
+       else
+               group_editor.Groupsegp          =   0;
+       group_editor.Groupside           =   Groupside[current_group];
+
+       editor_offset = ftell(SaveFile);
+       fwrite( &group_editor, sizeof(group_editor), 1, SaveFile );
+
+
+       //===================== SAVE VERTEX INFO ==========================
+
+       vertex_offset = ftell(SaveFile);
+       for (i=0;i<num_vertices;i++) {
+               tvert = Vertices[vertex_ids[i]];        
+               fwrite( &tvert, sizeof(tvert), 1, SaveFile ); 
+       }
+
+       //===================== SAVE SEGMENT INFO =========================
+
+
+       segment_offset = ftell(SaveFile);
+       for (i=0;i<num_segments;i++) {
+               tseg = Segments[segment_ids[i]];
+               
+               for (j=0;j<6;j++)       {
+                       found = 0;
+                       for (k=0;k<num_segments;k++) 
+                               if (tseg.children[j] == segment_ids[k]) { 
+                                       tseg.children[j] = k;
+                                       found = 1;
+                                       break;
+                                       }       
+                       if (found==0) tseg.children[j] = -1;
+               }
+
+               for (j=0;j<8;j++)
+                       for (k=0;k<num_vertices;k++)
+                               if (tseg.verts[j] == vertex_ids[k])     {
+                                       tseg.verts[j] = k;
+                                       break;
+                                       }
+
+               fwrite( &tseg, sizeof(tseg), 1, SaveFile );
+
+        }
+
+       //===================== SAVE TEXTURE INFO ==========================
+
+       texture_offset = ftell(SaveFile);
+
+       for (i=0;i<NumTextures;i++)
+               strncpy(current_tmap_list[i], TmapInfo[i].filename, 13);
+
+       fwrite( current_tmap_list, 13, NumTextures, SaveFile );
+
+       //============= REWRITE FILE INFO, TO SAVE OFFSETS ===============
+
+       // Update the offset fields
+       group_fileinfo.header_offset     =   header_offset;
+       group_fileinfo.editor_offset     =   editor_offset;
+       group_fileinfo.vertex_offset     =   vertex_offset;
+       group_fileinfo.segment_offset    =   segment_offset;
+       group_fileinfo.texture_offset    =   texture_offset;
+       
+       // Write the fileinfo
+       fseek(  SaveFile, 0, SEEK_SET );  // Move to TOF
+       fwrite( &group_fileinfo, sizeof(group_fileinfo), 1, SaveFile );
+
+       //==================== CLOSE THE FILE =============================
+       fclose(SaveFile);
+
+       return 0;
+
+}
+
+static char old_tmap_list[MAX_TEXTURES][13];
+static short tmap_xlate_table[MAX_TEXTURES];
+
+// -----------------------------------------------------------------------------
+// Load group will:
+//int med_load_group(char * filename)
+int med_load_group( char *filename, short *vertex_ids, short *segment_ids, int *num_vertices, int *num_segments)
+{
+       int segnum, vertnum;
+       char ErrorMessage[200];
+       short tmap_xlate;
+        int     translate=0;
+       char    *temptr;
+       int i, j; 
+       segment tseg;
+   vms_vector tvert;
+       CFILE * LoadFile;
+
+       LoadFile = cfopen( filename, CF_READ_MODE );
+       if (!LoadFile)
+       {
+               sprintf( ErrorMessage, "ERROR: Unable to open %s\n", filename );
+               MessageBox( -2, -2, 1, ErrorMessage, "Ok" );
+               return 1;
+       }
+
+       //===================== READ FILE INFO ========================
+
+       // These are the default values... version and fileinfo_sizeof
+       // don't have defaults.
+       group_fileinfo.header_offset     =   -1;
+       group_fileinfo.header_size       =   sizeof(group_header);
+       group_fileinfo.editor_offset     =   -1;
+       group_fileinfo.editor_size       =   sizeof(group_editor);
+       group_fileinfo.vertex_offset     =   -1;
+       group_fileinfo.vertex_howmany    =   0;
+       group_fileinfo.vertex_sizeof     =   sizeof(vms_vector);
+       group_fileinfo.segment_offset    =   -1;
+       group_fileinfo.segment_howmany   =   0;
+       group_fileinfo.segment_sizeof    =   sizeof(segment);
+       group_fileinfo.texture_offset    =   -1;
+       group_fileinfo.texture_howmany   =   0;
+       group_fileinfo.texture_sizeof    =   13;  // num characters in a name
+
+       // Read in group_top_fileinfo to get size of saved fileinfo.
+
+       if (cfseek( LoadFile, 0, SEEK_SET ))
+               Error( "Error seeking to 0 in group.c" );
+
+       if (cfread( &group_top_fileinfo, sizeof(group_top_fileinfo),1,LoadFile )!=1)
+               Error( "Error reading top_fileinfo in group.c" );
+
+       // Check version number
+       if (group_top_fileinfo.fileinfo_version < COMPATIBLE_VERSION )
+       {
+               sprintf( ErrorMessage, "ErrorMessage: You are trying to load %s\n" \
+                                                 "a version %d group, which is known to be incompatible\n" \
+                                                 "with the current expected version %d groups.", \
+                                                 filename, group_top_fileinfo.fileinfo_version, MINE_VERSION );
+
+               if (MessageBox( -2, -2, 2, ErrorMessage, "Forget it", "Try anyway" )==1)
+               {
+                       cfclose( LoadFile );
+                       return 1;
+               }
+
+               MessageBox( -2, -2, 1, "Good luck!", "I need it" );
+       }
+
+       // Now, Read in the fileinfo
+
+       if (cfseek( LoadFile, 0, SEEK_SET ))
+               Error( "Error seeking to 0b in group.c" );
+
+       if (cfread( &group_fileinfo, group_top_fileinfo.fileinfo_sizeof,1,LoadFile )!=1)
+               Error( "Error reading group_fileinfo in group.c" );
+
+       //===================== READ HEADER INFO ========================
+
+       // Set default values.
+       group_header.num_vertices        =   0;
+       group_header.num_segments        =   0;
+
+       if (group_fileinfo.header_offset > -1 )
+       {
+               if (cfseek( LoadFile,group_fileinfo.header_offset, SEEK_SET ))
+                       Error( "Error seeking to header_offset in group.c" );
+
+               if (cfread( &group_header, group_fileinfo.header_size,1,LoadFile )!=1)
+                       Error( "Error reading group_header in group.c" );
+       }
+
+       //===================== READ EDITOR INFO ==========================
+
+       // Set default values
+       group_editor.current_seg         =   0;
+       group_editor.newsegment_offset   =   -1; // To be written
+       group_editor.newsegment_size     =   sizeof(segment);
+       group_editor.Groupsegp                          =   -1;
+       group_editor.Groupside                          =   0;
+
+       if (group_fileinfo.editor_offset > -1 )
+       {
+               if (cfseek( LoadFile,group_fileinfo.editor_offset, SEEK_SET ))
+                       Error( "Error seeking to editor_offset in group.c" );
+
+               if (cfread( &group_editor, group_fileinfo.editor_size,1,LoadFile )!=1)
+                       Error( "Error reading group_editor in group.c" );
+
+       }
+
+       //===================== READ VERTEX INFO ==========================
+
+       if ( (group_fileinfo.vertex_offset > -1) && (group_fileinfo.vertex_howmany > 0))
+       {
+               if (cfseek( LoadFile,group_fileinfo.vertex_offset, SEEK_SET ))
+                       Error( "Error seeking to vertex_offset in group.c" );
+
+                       for (i=0;i<group_header.num_vertices;i++) {
+
+                               if (cfread( &tvert, sizeof(tvert),1,LoadFile )!=1)
+                                       Error( "Error reading tvert in group.c" );
+                               vertex_ids[i] = med_create_duplicate_vertex( &tvert ); 
+                               //mprintf((0, "vertex %d created from original %d\n", vertex_ids[i], i));
+                       }
+
+               }
+
+       //==================== READ SEGMENT INFO ===========================
+
+       if ( (group_fileinfo.segment_offset > -1) && (group_fileinfo.segment_howmany > 0))
+       {
+               if (cfseek( LoadFile,group_fileinfo.segment_offset, SEEK_SET ))
+                       Error( "Error seeking to segment_offset in group.c" );
+
+               for (i=0;i<group_header.num_segments;i++) {
+                       if (cfread( &tseg, sizeof(segment),1,LoadFile )!=1)
+                               Error( "Error reading tseg in group.c" );
+                               
+                       segment_ids[i] = get_free_segment_number();
+                       Segments[segment_ids[i]] = tseg; 
+                       Segments[segment_ids[i]].objects = -1;
+
+                       fuelcen_activate( &Segments[segment_ids[i]], Segments[segment_ids[i]].special );
+                       }
+
+               for (i=0;i<group_header.num_segments;i++) {
+                       // Fix vertices
+                       for (j=0;j<MAX_VERTICES_PER_SEGMENT;j++) {
+                               vertnum = vertex_ids[Segments[segment_ids[i]].verts[j]];
+                               Segments[segment_ids[i]].verts[j] = vertnum;
+                               }
+
+                       // Fix children and walls.
+                       for (j=0;j<MAX_SIDES_PER_SEGMENT;j++) {
+                               Segments[segment_ids[i]].sides[j].wall_num = -1;
+                               if (IS_CHILD(Segments[segment_ids[i]].children[j])) {
+                                       segnum = segment_ids[Segments[segment_ids[i]].children[j]];
+                                       Segments[segment_ids[i]].children[j] = segnum;
+                                       } 
+                               //Translate textures.
+                               if (translate == 1) {
+                                       int     temp;
+                                       tmap_xlate = Segments[segment_ids[i]].sides[j].tmap_num;
+                                       Segments[segment_ids[i]].sides[j].tmap_num = tmap_xlate_table[tmap_xlate];
+                                       temp = Segments[segment_ids[i]].sides[j].tmap_num2;
+                                       tmap_xlate = temp & 0x3fff;                     // strip off orientation bits
+                                       if (tmap_xlate != 0)
+                                                Segments[segment_ids[i]].sides[j].tmap_num2 = (temp & (!0x3fff)) | tmap_xlate_table[tmap_xlate];  // mask on original orientation bits
+                                       }
+                               }
+                       }
+       }
+       
+       //===================== READ TEXTURE INFO ==========================
+
+       if ( (group_fileinfo.texture_offset > -1) && (group_fileinfo.texture_howmany > 0))
+       {
+               if (cfseek( LoadFile, group_fileinfo.texture_offset, SEEK_SET ))
+                       Error( "Error seeking to texture_offset in gamemine.c" );
+
+               for (i=0; i< group_fileinfo.texture_howmany; i++ )
+               {
+                       if (cfread( &old_tmap_list[i], group_fileinfo.texture_sizeof, 1, LoadFile )!=1)
+                               Error( "Error reading old_tmap_list[i] in gamemine.c" );
+               }
+       }
+
+       //=============== GENERATE TEXTURE TRANSLATION TABLE ===============
+
+       translate = 0;
+       
+       Assert (NumTextures < MAX_TEXTURES);
+{
+       hashtable ht;
+
+       hashtable_init( &ht, NumTextures );
+
+       // Remove all the file extensions in the textures list
+
+       for (i=0;i<NumTextures;i++)     {
+               temptr = strchr(TmapInfo[i].filename, '.');
+               if (temptr) *temptr = '\0';
+//             mprintf( (0, "Texture %d is '%s'\n", i, TmapInfo[i].filename ));
+//             key_getch();
+               hashtable_insert( &ht, TmapInfo[i].filename, i );
+       }
+
+       // For every texture, search through the texture list
+       // to find a matching name.
+       for (j=0;j<group_fileinfo.texture_howmany;j++)  {
+               // Remove this texture name's extension
+               temptr = strchr(old_tmap_list[j], '.');
+               if (temptr) *temptr = '\0';
+
+               tmap_xlate_table[j] = hashtable_search( &ht,old_tmap_list[j]);
+               if (tmap_xlate_table[j] < 0 )
+                       tmap_xlate_table[j] = 0;
+               if (tmap_xlate_table[j] != j ) translate = 1;
+       }
+
+       hashtable_free( &ht );
+}
+
+
+       //======================== CLOSE FILE ==============================
+       cfclose( LoadFile );
+
+       //========================= UPDATE VARIABLES ======================
+
+       if (group_editor.Groupsegp != -1 ) 
+               Groupsegp[current_group] = &Segments[segment_ids[group_editor.Groupsegp]];
+       else
+               Groupsegp[current_group] = NULL;
+
+       Groupside[current_group] = group_editor.Groupside;
+
+       *num_vertices = group_fileinfo.vertex_howmany;
+       *num_segments = group_fileinfo.segment_howmany;
+       warn_if_concave_segments();
+       
+       return 0;
+}
+
+char group_filename[128] = "*.GRP";
+
+void checkforgrpext( char * f )
+{
+       int i;
+
+       for (i=1; i<strlen(f); i++ )
+       {
+               if (f[i]=='.') return;
+
+               if ((f[i]==' '||f[i]==0) )
+               {
+                       f[i]='.';
+                       f[i+1]='G';
+                       f[i+2]= 'R';
+                       f[i+3]= 'P';
+                       f[i+4]=0;
+                       return;
+               }
+       }
+
+       if (i < 123)
+       {
+               f[i]='.';
+               f[i+1]='G';
+               f[i+2]= 'R';
+               f[i+3]= 'P';
+               f[i+4]=0;
+               return;
+       }
+}
+
+//short vertex_list[MAX_VERTICES];
+
+
+int SaveGroup()
+{
+       // Save group
+       int i, s, v;
+       char  ErrorMessage[200];
+       sbyte   vertex_list[MAX_VERTICES];
+
+       if (current_group == -1)
+               {
+               sprintf( ErrorMessage, "ERROR: No current group." );
+               MessageBox( -2, -2, 1, ErrorMessage, "Ok" );
+               return 0;
+               }
+
+       for (v=0; v<=Highest_vertex_index; v++) {
+               vertex_list[v] = 0;
+       }
+
+       //      Make a list of all vertices in group.
+       for (s=0; s<GroupList[current_group].num_segments; s++)
+               for (v=0; v<MAX_VERTICES_PER_SEGMENT; v++) {
+                       vertex_list[Segments[GroupList[current_group].segments[s]].verts[v]] = 1;
+               }       
+
+       v=0;
+       for (i=0; i<=Highest_vertex_index; i++) 
+               if (vertex_list[i] == 1) { 
+                       GroupList[current_group].vertices[v++] = i;
+               }
+       GroupList[current_group].num_vertices = v;
+       //mprintf((0, "Saving %d vertices, %d segments\n", GroupList[current_group].num_vertices, GroupList[current_group].num_segments));
+       med_save_group("TEMP.GRP", GroupList[current_group].vertices, GroupList[current_group].segments,
+               GroupList[current_group].num_vertices, GroupList[current_group].num_segments);
+   if (ui_get_filename( group_filename, "*.GRP", "SAVE GROUP" ))
+       {
+      checkforgrpext(group_filename);
+               if (med_save_group(group_filename, GroupList[current_group].vertices, GroupList[current_group].segments,
+                                       GroupList[current_group].num_vertices, GroupList[current_group].num_segments))
+                       return 0;
+               mine_changed = 0;
+       }
+       
+       return 1;
+}
+
+
+int LoadGroup()
+{
+       int x;
+
+       if (num_groups == MAX_GROUPS)
+               {
+               x = MessageBox( -2, -2, 2, "Warning: You are about to wipe out a group.", "ARGH! NO!", "No problemo." );
+               if (x==1) return 0;
+               }
+
+       if (num_groups < MAX_GROUPS)
+               {
+               num_groups++;
+               current_group = num_groups-1;
+               }
+               else current_group = 0;
+
+   if (ui_get_filename( group_filename, "*.GRP", "LOAD GROUP" ))
+       {
+      checkforgrpext(group_filename);
+      med_load_group(group_filename, GroupList[current_group].vertices, GroupList[current_group].segments,
+                                        &GroupList[current_group].num_vertices, &GroupList[current_group].num_segments) ;
+               //mprintf((0, "Loaded %d vertices, %d segments\n", GroupList[current_group].num_vertices, GroupList[current_group].num_segments));
+               
+       if (!med_move_group(0, Cursegp, Curside, Groupsegp[current_group], Groupside[current_group], &vmd_identity_matrix, 0)) {
+               autosave_mine(mine_filename);
+               set_view_target_from_segment(Cursegp);
+               Update_flags |= UF_WORLD_CHANGED;
+               mine_changed = 1;
+               diagnostic_message("Group moved.");
+               return 0;
+               } else
+       return 1;
+       }       else
+
+       return 1;
+}
+
+
+int UngroupSegment( void )
+{
+       if (Cursegp->group == current_group) {
+       
+               Cursegp->group = -1;
+               delete_segment_from_group( Cursegp-Segments, current_group );
+       
+          Update_flags |= UF_WORLD_CHANGED;
+          mine_changed = 1;
+          diagnostic_message("Segment Ungrouped from Group %d.", current_group);
+       
+               return 1;
+       } else
+       return 0;
+}
+
+int GroupSegment( void )
+{
+       if (Cursegp->group == -1) {
+
+               Cursegp->group = current_group;
+               add_segment_to_group( Cursegp-Segments, current_group );
+       
+          Update_flags |= UF_WORLD_CHANGED;
+          mine_changed = 1;
+          diagnostic_message("Segment Added to Group %d.", current_group);
+
+               return 1;
+       } else
+       return 0;
+}
+
+int Degroup( void )
+{
+       int i;
+
+//     GroupList[current_group].num_segments = 0;
+//     Groupsegp[current_group] = 0;
+
+       if (num_groups==0) return 0;
+
+       for (i=0; i<GroupList[current_group].num_segments; i++)
+               delete_segment_from_group( GroupList[current_group].segments[i], current_group );
+
+         //    delete_segment_from_group( &Segments[GroupList[current_group].segments[i]]-Segments, current_group );
+
+       for (i=current_group;i<num_groups-1;i++)
+               {
+               GroupList[i] = GroupList[i+1];
+               Groupsegp[i] = Groupsegp[i+1];
+               }
+
+       num_groups--;
+
+       GroupList[num_groups].num_segments = 0;
+       Groupsegp[num_groups] = 0;
+       
+       if (current_group > num_groups-1) current_group--;
+
+       if (num_groups == 0)
+               current_group = -1;
+
+   if (Lock_view_to_cursegp)
+       set_view_target_from_segment(Cursegp);
+   Update_flags |= UF_WORLD_CHANGED;
+   mine_changed = 1;
+   diagnostic_message("Group UNgrouped.");
+
+       return 1;
+}
+
+void NextGroup( void ) 
+{
+
+       if (num_groups > 0)
+               {
+               current_group++;
+               if (current_group >= num_groups ) current_group = 0;
+               
+               Update_flags |= UF_ED_STATE_CHANGED;
+               mine_changed = 1;
+               }
+       else editor_status("No Next Group\n");
+}
+
+void PrevGroup( void ) 
+{
+       if (num_groups > 0)
+               {
+               current_group--;
+               if (current_group < 0 ) current_group = num_groups-1;
+               
+               Update_flags |= UF_ED_STATE_CHANGED;
+               mine_changed = 1;
+               }
+       else editor_status("No Previous Group\n");
+}
+
+// Returns:
+//      0 = successfully selected
+//  1 = bad group number
+int select_group( int num )
+{
+       if ((num>=0) && (num<num_groups))
+               {
+               current_group = num;
+               return 0;
+               }
+       else return 1;
+}
+
+
+//     -----------------------------------------------------------------------------
+int MoveGroup(void)
+{
+       if (!Groupsegp[current_group]) {
+               editor_status("Error -- Cannot move group, no group segment.");
+               return 1;
+       }
+
+       med_compress_mine();
+
+       if (!med_move_group(0, Cursegp, Curside, Groupsegp[current_group], Groupside[current_group], &vmd_identity_matrix, 0)) {
+               autosave_mine(mine_filename);
+               Update_flags |= UF_WORLD_CHANGED;
+               mine_changed = 1;
+               diagnostic_message("Group moved.");
+               return 0;
+       } else
+               return 1;
+}                                
+
+
+//     -----------------------------------------------------------------------------
+int CopyGroup(void)
+{
+       int     attach_seg;
+
+       if (!Groupsegp[current_group]) {
+               editor_status("Error -- Cannot copy group, no group segment.");
+               return 1;
+       }
+
+       //      See if the attach side in the group is attached to another segment.
+       //      If so, it must not be in the group for group copy to be legal.
+       attach_seg = Groupsegp[current_group]->children[Groupside[current_group]];
+       if (attach_seg != -1) {
+               int     i;
+
+               for (i=0; i<GroupList[current_group].num_segments; i++)
+                       if (GroupList[current_group].segments[i] == attach_seg)
+                               break;
+
+               if (i != GroupList[current_group].num_segments) {
+                       editor_status("Error -- Cannot copy group, attach side has a child (segment %i) attached.", Groupsegp[current_group]->children[Groupside[current_group]]);
+                       return 1;
+               }
+       }
+
+       med_compress_mine();
+
+       if (!med_copy_group(0, Cursegp, Curside, Groupsegp[current_group], Groupside[current_group], &vmd_identity_matrix)) {
+               autosave_mine(mine_filename);
+               Update_flags |= UF_WORLD_CHANGED;
+               mine_changed = 1;
+               diagnostic_message("Group copied.");
+               return 0;
+       } else    
+               return 1;
+}
+
+
+//     -----------------------------------------------------------------------------
+int RotateGroup(void)
+{
+
+       if (!Groupsegp[current_group]) {
+               editor_status("Error -- Cannot rotate group, no group segment.");
+               return 1;
+       }
+
+       Group_orientation[current_group]++;
+       if ((Group_orientation[current_group] <0) || (Group_orientation[current_group] >4))
+               Group_orientation[current_group]=0;
+
+       med_compress_mine();
+       
+       if (!med_move_group(0, Cursegp, Curside, Groupsegp[current_group], Groupside[current_group],
+                                                               &vmd_identity_matrix, Group_orientation[current_group]))
+                       {
+                       Update_flags |= UF_WORLD_CHANGED;
+                       mine_changed = 1;
+                       diagnostic_message("Group rotated.");
+                       return 0;
+                       } 
+               else      
+                       return 1;
+}
+
+
+//     -----------------------------------------------------------------------------
+//     Creates a group from all segments connected to marked segment.
+int SubtractFromGroup(void)
+{
+       int     x, s, original_group;
+       short   *gp;
+       int     cur_num_segs;
+
+       if (!Markedsegp) {
+               editor_status("Error -- Cannot create group, no marked segment.");
+               return 1;
+       }
+
+       med_compress_mine();
+       autosave_mine(mine_filename);
+
+       if (num_groups == MAX_GROUPS) {
+               x = MessageBox( -2, -2, 2, "Warning: You are about to wipe out a group.", "ARGH! NO!", "No problemo." );
+               if (x==1) return 0;
+       }                                          
+
+       if (current_group == -1) {
+               editor_status("Error -- No current group.  Cannot subtract.");
+               return 1;
+       }
+
+       original_group = current_group;
+
+       current_group = (current_group + 1) % MAX_GROUPS;
+
+//     if (num_groups < MAX_GROUPS) {
+//             current_group = num_groups;
+//             num_groups++;
+//     } else
+//             current_group = 0;
+
+       // mprintf((0, "Old group: "));
+       // for (s=0; s<GroupList[original_group].num_segments; s++)
+       //      mprintf((0, "%3i ", GroupList[original_group].segments[s]));
+       // mprintf((0, "\n"));
+       
+       //      Create a list of segments to copy.
+       GroupList[current_group].num_segments = 0;
+       create_group_list(Markedsegp, GroupList[current_group].segments, &GroupList[current_group].num_segments, Selected_segs, N_selected_segs);
+
+       // mprintf((0, "New group: "));
+       // for (s=0; s<GroupList[current_group].num_segments; s++)
+       //      mprintf((0, "%3i ", GroupList[current_group].segments[s]));
+       // mprintf((0, "\n"));
+       
+       //      Now, scan the two groups, forming a group which consists of only those segments common to the two groups.
+       gp = GroupList[current_group].segments;
+       cur_num_segs = GroupList[current_group].num_segments;
+       for (s=0; s<cur_num_segs; s++) {
+               short   *gp1 = GroupList[original_group].segments;
+               short   s0 = gp[s];
+               int     s1;
+
+               for (s1=0; s1<GroupList[original_group].num_segments; s1++)
+                       if (gp1[s1] == s0)
+                               break;                          // If break executed, then segment found in both lists.
+
+               //      If current segment was not found in both lists, remove it by copying the last segment over
+               //      it and decreasing the number of segments.
+               if (s1 == GroupList[original_group].num_segments) {
+                       gp[s] = gp[cur_num_segs];
+                       cur_num_segs--;
+               }
+       }
+
+       //      Go through mine and seg group number of all segments which are in group
+       //      All segments which were subtracted from group get group set to -1.
+       mprintf((0, "In segments: "));
+       for (s=0; s<cur_num_segs; s++) {
+               Segments[GroupList[current_group].segments[s]].group = current_group;
+               mprintf((0, "%2i ", GroupList[current_group].segments[s]));
+       }
+
+       mprintf((0, "\nRemoved segments: "));
+       for (s=0; s<=Highest_segment_index; s++) {
+               int     t;
+               if (Segments[s].group == current_group) {
+                       for (t=0; t<cur_num_segs; t++)
+                               if (GroupList[current_group].segments[t] == s)
+                                       break;
+                       if (s == cur_num_segs) {
+                               Segments[s].group = -1;
+                               mprintf((0, "%2i ", s));
+                       }
+               }
+       }
+
+       // mprintf((0, "Combined group: "));
+       // for (s=0; s<GroupList[current_group].num_segments; s++)
+       //      mprintf((0, "%3i ", GroupList[current_group].segments[s]));
+       // mprintf((0, "\n\n"));
+
+       GroupList[current_group].num_segments = cur_num_segs;
+
+       // Replace Marked segment with Group Segment.
+       Groupsegp[current_group] = Markedsegp;
+       Groupside[current_group] = Markedside;
+
+       for (x=0;x<GroupList[current_group].num_segments;x++)
+               Segments[GroupList[current_group].segments[x]].group = current_group;
+       
+       Update_flags |= UF_WORLD_CHANGED;
+       mine_changed = 1;
+       diagnostic_message("Group created.");
+
+       return 1; 
+                                 
+}
+
+//     -----------------------------------------------------------------------------
+//     Creates a group from all segments already in CurrentGroup which can be reached from marked segment
+//     without passing through current segment.
+int CreateGroup(void)
+{
+       int x;
+
+       if (!Markedsegp) {
+               editor_status("Error -- Cannot create group, no marked segment.");
+               return 1;
+       }
+
+       med_compress_mine();
+       autosave_mine(mine_filename);
+
+       if (num_groups == MAX_GROUPS) {
+               x = MessageBox( -2, -2, 2, "Warning: You are about to wipe out a group.", "ARGH! NO!", "No problemo." );
+               if (x==1)
+                       return 0;                               // Aborting at user's request.
+       }                                          
+
+       if (num_groups < MAX_GROUPS) {
+               num_groups++;
+               current_group = num_groups-1;
+       } else
+               current_group = 0;
+
+       //      Create a list of segments to copy.
+       GroupList[current_group].num_segments = 0;
+       create_group_list(Markedsegp, GroupList[current_group].segments, &GroupList[current_group].num_segments, Selected_segs, 0);
+       
+       // Replace Marked segment with Group Segment.
+       Groupsegp[current_group] = Markedsegp;
+       Groupside[current_group] = Markedside;
+//     Markedsegp = 0;
+//     Markedside = WBACK;
+
+       for (x=0;x<GroupList[current_group].num_segments;x++)
+               Segments[GroupList[current_group].segments[x]].group = current_group;
+       
+       Update_flags |= UF_WORLD_CHANGED;
+       mine_changed = 1;
+       diagnostic_message("Group created.");
+
+       return 1; 
+                                 
+}
+
+//     -----------------------------------------------------------------------------
+// Deletes current group.
+int DeleteGroup( void )
+{
+       int i, numsegs;
+
+       autosave_mine(mine_filename);
+               
+       if (num_groups==0) return 0;
+
+       //mprintf((0, "num_segments = %d\n", GroupList[current_group].num_segments));
+
+       numsegs = GroupList[current_group].num_segments;
+       
+       for (i=0; i<numsegs; i++) {
+               med_delete_segment(&Segments[GroupList[current_group].segments[0]]);
+       }
+
+       for (i=current_group;i<num_groups-1;i++) {
+               GroupList[i] = GroupList[i+1];
+               Groupsegp[i] = Groupsegp[i+1];
+       }
+
+       num_groups--;
+       GroupList[num_groups].num_segments = 0;
+       Groupsegp[num_groups] = 0;
+
+       if (current_group > num_groups-1) current_group--;
+
+       if (num_groups==0)
+               current_group = -1;
+
+       strcpy(undo_status[Autosave_count], "Delete Group UNDONE.");
+   if (Lock_view_to_cursegp)
+       set_view_target_from_segment(Cursegp);
+
+   Update_flags |= UF_WORLD_CHANGED;
+   mine_changed = 1;
+   diagnostic_message("Group deleted.");
+   // warn_if_concave_segments();     // This could be faster -- just check if deleted segment was concave, warn accordingly
+
+       return 1;
+
+}
+
+
+int MarkGroupSegment( void )
+{
+       if ((Cursegp->group != -1) && (Cursegp->group == current_group))
+               {
+          autosave_mine(mine_filename);
+               Groupsegp[current_group] = Cursegp;
+               Groupside[current_group] = Curside;
+               editor_status("Group Segment Marked.");
+               Update_flags |= UF_ED_STATE_CHANGED;
+          strcpy(undo_status[Autosave_count], "Mark Group Segment UNDONE.");
+               mine_changed = 1;
+               return 1;
+               }
+       else return 0;
+}
diff --git a/main/editor/info.c b/main/editor/info.c
new file mode 100644 (file)
index 0000000..3d7c3c2
--- /dev/null
@@ -0,0 +1,492 @@
+/*
+THE COMPUTER CODE CONTAINED HEREIN IS THE SOLE PROPERTY OF PARALLAX
+SOFTWARE CORPORATION ("PARALLAX").  PARALLAX, IN DISTRIBUTING THE CODE TO
+END-USERS, AND SUBJECT TO ALL OF THE TERMS AND CONDITIONS HEREIN, GRANTS A
+ROYALTY-FREE, PERPETUAL LICENSE TO SUCH END-USERS FOR USE BY SUCH END-USERS
+IN USING, DISPLAYING,  AND CREATING DERIVATIVE WORKS THEREOF, SO LONG AS
+SUCH USE, DISPLAY OR CREATION IS FOR NON-COMMERCIAL, ROYALTY OR REVENUE
+FREE PURPOSES.  IN NO EVENT SHALL THE END-USER USE THE COMPUTER CODE
+CONTAINED HEREIN FOR REVENUE-BEARING PURPOSES.  THE END-USER UNDERSTANDS
+AND AGREES TO THE TERMS HEREIN AND ACCEPTS THE SAME BY USE OF THIS FILE.  
+COPYRIGHT 1993-1998 PARALLAX SOFTWARE CORPORATION.  ALL RIGHTS RESERVED.
+*/
+/*
+ * $Source: /cvs/cvsroot/d2x/main/editor/info.c,v $
+ * $Revision: 1.1 $
+ * $Author: btb $
+ * $Date: 2004-12-19 13:54:27 $
+ * 
+ * Print debugging info in ui.
+ * 
+ * $Log: not supported by cvs2svn $
+ * Revision 1.1.1.1  1999/06/14 22:03:18  donut
+ * Import of d1x 1.37 source.
+ *
+ * Revision 2.0  1995/02/27  11:35:34  john
+ * Version 2.0! No anonymous unions, Watcom 10.0, with no need
+ * for bitmaps.tbl.
+ * 
+ * Revision 1.42  1995/02/22  15:12:50  allender
+ * remove anonymous unions from object structure
+ * 
+ * Revision 1.41  1994/12/08  13:59:39  matt
+ * *** empty log message ***
+ * 
+ * Revision 1.40  1994/09/30  00:38:30  mike
+ * Fix some diagnostic messages
+ * 
+ * Revision 1.39  1994/09/29  20:13:12  mike
+ * Clean up some text, prevent it from writing outside canvas.
+ * 
+ * Revision 1.38  1994/09/29  09:32:17  mike
+ * Fix text clipping problem in UI keypad info text.
+ * 
+ * Revision 1.37  1994/09/25  23:42:20  matt
+ * Took out references to obsolete constants
+ * 
+ * Revision 1.36  1994/08/25  21:57:05  mike
+ * IS_CHILD stuff.
+ * 
+ * Revision 1.35  1994/08/23  16:39:50  mike
+ * mode replaced by behavior in ai_info.
+ * 
+ * Revision 1.34  1994/07/18  10:45:23  mike
+ * Fix erase window in texture pads after adding more click-boxes.
+ * 
+ * Revision 1.33  1994/07/15  12:34:10  mike
+ * Remove use of AIM_FOLLOW_PATH_CIRCULAR.
+ * 
+ * Revision 1.32  1994/06/17  17:13:46  yuan
+ * Fixed text so it doesn't overflow screen
+ * 
+ * Revision 1.31  1994/06/01  17:22:31  matt
+ * Set font color before drawing info; got rid of superfluous %d
+ * 
+ * Revision 1.30  1994/05/29  23:40:29  matt
+ * Killed reference to now-unused movement type
+ * 
+ * Revision 1.29  1994/05/29  22:52:32  matt
+ * Deleted unused stuff
+ * 
+ * Revision 1.28  1994/05/27  10:34:16  yuan
+ * Added new Dialog boxes for Walls and Triggers.
+ * 
+ * Revision 1.27  1994/05/17  10:34:35  matt
+ * Changed Num_objects to num_objects, since it's not really global anymore
+ * 
+ * Revision 1.26  1994/05/14  17:17:59  matt
+ * Got rid of externs in source (non-header) files
+ * 
+ * Revision 1.25  1994/05/12  14:47:07  mike
+ * Adjust for Ai_states killed, replaced by field in object structure.
+ * 
+ * Revision 1.24  1994/05/06  12:52:11  yuan
+ * Adding some gamesave checks...
+ * 
+ * Revision 1.23  1994/05/03  19:21:28  matt
+ * Removed reference to robot flythrough mode, which doesn't exist anymore
+ * 
+ * Revision 1.22  1994/05/03  11:03:06  mike
+ * Customize text for segment sizing keypad.
+ * 
+ * Revision 1.21  1994/04/29  15:05:40  yuan
+ * More info added...
+ * 
+ * Revision 1.20  1994/04/22  17:45:58  john
+ * MAde top 2 bits of paste-ons pick the 
+ * orientation of the bitmap.
+ * 
+ * Revision 1.19  1994/04/20  17:29:30  yuan
+ * Added tmap_num info.
+ * 
+ * Revision 1.18  1994/04/13  19:12:55  mike
+ * Fix font color problems in keypads.
+ * 
+ * Revision 1.17  1994/04/13  13:26:37  mike
+ * Kill a mprintf.
+ * 
+ * Revision 1.16  1994/04/13  13:24:44  mike
+ * Separate info display, customize for each keypad.
+ * 
+ * Revision 1.15  1994/03/19  17:21:31  yuan
+ * Wall system implemented until specific features need to be added...
+ * (Needs to be hammered on though.)
+ * 
+ * Revision 1.14  1994/02/22  18:13:13  yuan
+ * Added tmap number field.
+ * 
+ * Revision 1.13  1994/02/17  09:46:27  matt
+ * Removed include of slew.h
+ * 
+ * Revision 1.12  1994/02/16  19:58:56  yuan
+ * Added type to info
+ * 
+ * Revision 1.11  1994/02/16  16:48:08  yuan
+ * Added Curside.
+ * 
+ * Revision 1.10  1994/02/03  17:26:43  yuan
+ * Fixed formatting of vertex numbering.
+ * 
+ * Revision 1.9  1994/01/31  12:17:06  yuan
+ * Make sure Num_segments, etc. are drawn.
+ * 
+ * Revision 1.8  1994/01/22  13:43:12  yuan
+ * Cosmetic fixes.
+ * 
+ * Revision 1.7  1994/01/21  12:14:59  yuan
+ * Fixed cosmetic problem
+ * 
+ * Revision 1.6  1994/01/21  12:01:03  yuan
+ * Added segment and vertex info
+ * 
+ * Revision 1.5  1994/01/20  11:28:11  john
+ * *** empty log message ***
+ * 
+ * Revision 1.4  1994/01/19  10:44:42  john
+ * *** empty log message ***
+ * 
+ * Revision 1.3  1994/01/19  10:32:36  john
+ * *** empty log message ***
+ * 
+ * Revision 1.2  1994/01/19  09:34:31  john
+ * First version.
+ * 
+ * Revision 1.1  1994/01/19  09:30:43  john
+ * Initial revision
+ * 
+ * 
+ */
+
+
+#ifdef RCS
+static char rcsid[] = "$Id: info.c,v 1.1 2004-12-19 13:54:27 btb Exp $";
+#endif
+
+#include <i86.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>                                                                                              
+#include <malloc.h>
+
+#include "inferno.h"
+#include "segment.h"
+#include "gr.h"
+#include "ui.h"
+#include "editor.h"
+
+#include "mono.h"
+#include "error.h"
+#include "textures.h"
+#include "object.h"
+#include "ai.h"
+
+#include "texpage.h"           // Textue selection paging stuff
+#include "objpage.h"           // Object selection paging stuff
+
+#include "wall.h"
+#include "switch.h"
+
+int init_info;
+
+#ifdef DO_MEMINFO
+struct meminfo {
+    int LargestBlockAvail;
+    int MaxUnlockedPage;
+    int LargestLockablePage;
+    int LinAddrSpace;
+    int NumFreePagesAvail;
+    int NumPhysicalPagesFree;
+    int TotalPhysicalPages;
+    int FreeLinAddrSpace;
+    int SizeOfPageFile;
+    int Reserved[3];
+} MemInfo;
+
+#define DPMI_INT        0x31
+
+void read_mem_info()
+{
+    union REGS regs;
+    struct SREGS sregs;
+
+    regs.x.eax = 0x00000500;
+    memset( &sregs, 0, sizeof(sregs) );
+    sregs.es = FP_SEG( &MemInfo );
+    regs.x.edi = FP_OFF( &MemInfo );
+
+    int386x( DPMI_INT, &regs, &regs, &sregs );
+}
+#endif
+
+char * get_object_type(int num, char *name)
+{
+       switch (num) {
+               case OBJ_NONE:                  strcpy(name, "OBJ_NONE    ");   break;
+               case OBJ_WALL:          strcpy(name, "OBJ_WALL    ");   break;
+               case OBJ_FIREBALL:      strcpy(name, "OBJ_FIREBALL");   break;
+               case OBJ_ROBOT:                 strcpy(name, "OBJ_ROBOT   ");   break;
+               case OBJ_HOSTAGE:       strcpy(name, "OBJ_HOSTAGE ");   break;
+               case OBJ_PLAYER:                strcpy(name, "OBJ_PLAYER  ");   break;
+               case OBJ_WEAPON:                strcpy(name, "OBJ_WEAPON  ");   break;
+               case OBJ_CAMERA:                strcpy(name, "OBJ_CAMERA  ");   break;
+               case OBJ_POWERUP:       strcpy(name, "OBJ_POWERUP ");   break;
+               default:                                        strcpy(name, " (unknown)  ");   break;
+       }
+
+       return name;
+}
+
+char * get_control_type(int num, char *name)
+{
+       switch (num) {
+               case CT_NONE:                                   strcpy(name, "CT_NONE       "); break;
+               case CT_AI:                                             strcpy(name, "CT_AI         "); break;
+               case CT_EXPLOSION:                      strcpy(name, "CT_EXPLOSION  "); break;
+               //case CT_MULTIPLAYER:                  strcpy(name, "CT_MULTIPLAYER"); break;
+               case CT_FLYING:                         strcpy(name, "CT_FLYING     "); break;
+               case CT_SLEW:                                   strcpy(name, "CT_SLEW       "); break;
+               case CT_FLYTHROUGH:                     strcpy(name, "CT_FLYTHROUGH "); break;
+               //case CT_DEMO:                                 strcpy(name, "CT_DEMO       "); break;
+               //case CT_ROBOT_FLYTHROUGH:     strcpy(name, "CT_FLYTHROUGH "); break;
+               case CT_WEAPON:                         strcpy(name, "CT_WEAPON     "); break;
+               default:                                                        strcpy(name, " (unknown)    "); break;
+       }
+       return name;
+}
+
+char * get_movement_type(int num, char *name)
+{
+       switch (num) {
+               case MT_NONE:                   strcpy(name, "MT_NONE       "); break;
+               case MT_PHYSICS:                strcpy(name, "MT_PHYSICS    "); break;
+               //case MT_MULTIPLAYER:  strcpy(name, "MT_MULTIPLAYER"); break;
+               default:                                        strcpy(name, " (unknown)    "); break;
+       }
+       return name;
+}
+
+char * get_ai_behavior(int num, char *name)
+{
+#define        AIB_STILL                                               0x80
+#define        AIB_NORMAL                                              0x81
+#define        AIB_HIDE                                                        0x82
+#define        AIB_RUN_FROM                                    0x83
+#define        AIB_FOLLOW_PATH                         0x84
+
+       switch (num) {
+               case AIB_STILL:                         strcpy(name, "STILL       ");   break;
+               case AIB_NORMAL:                                strcpy(name, "NORMAL      ");   break;
+               case AIB_HIDE:                                  strcpy(name, "HIDE        ");   break;
+               case AIB_RUN_FROM:                      strcpy(name, "RUN_FROM    ");   break;
+               case AIB_FOLLOW_PATH:           strcpy(name, "FOLLOW_PATH ");   break;
+               default:                                                        strcpy(name, " (unknown)  ");   break;
+       }
+       return name;
+}
+
+//     ---------------------------------------------------------------------------------------------------
+void info_display_object_placement(int show_all)
+{
+       static  int     old_Cur_object_index;
+       static  int     old_type;
+       static  int     old_movement_type;
+       static  int     old_control_type;
+       static  int     old_mode;
+
+       char            name[30];
+
+       if (init_info | show_all) {
+               old_Cur_object_index = -2;
+               old_type = -2;
+               old_movement_type = -2;
+               old_control_type = -2;
+               old_mode = -2;
+       }
+
+       if ( ( Cur_object_index != old_Cur_object_index) || 
+                       ( Objects[Cur_object_index].type != old_type) || 
+                       ( Objects[Cur_object_index].movement_type != old_movement_type) || 
+                       ( Objects[Cur_object_index].control_type != old_control_type) || 
+                       ( Objects[Cur_object_index].ctype.ai_info.behavior != old_mode) ) {
+
+               gr_uprintf( 0, 0, "Object id: %4d\n", Cur_object_index);
+               gr_uprintf( 0, 16, "Type: %s\n", get_object_type(Objects[Cur_object_index].type , name));
+               gr_uprintf( 0, 32, "Movmnt: %s\n", get_movement_type(Objects[Cur_object_index].movement_type, name));
+               gr_uprintf( 0, 48, "Cntrl: %s\n", get_control_type(Objects[Cur_object_index].control_type, name));
+               gr_uprintf( 0, 64, "Mode: %s\n", get_ai_behavior(Objects[Cur_object_index].ctype.ai_info.behavior, name));
+
+               old_Cur_object_index = Cur_object_index;
+               old_type = Objects[Cur_object_index].type;
+               old_movement_type = Objects[Cur_object_index].movement_type;
+               old_mode = Objects[Cur_object_index].control_type;
+               old_mode = Objects[Cur_object_index].ctype.ai_info.behavior;
+       }
+
+}
+
+//     ---------------------------------------------------------------------------------------------------
+void info_display_segsize(int show_all)
+{
+       static  int     old_SegSizeMode;
+
+       char            name[30];
+
+       if (init_info | show_all) {
+               old_SegSizeMode = -2;
+       }
+
+       if (old_SegSizeMode != SegSizeMode  ) {
+               switch (SegSizeMode) {
+                       case SEGSIZEMODE_FREE:          strcpy(name, "free   ");        break;
+                       case SEGSIZEMODE_ALL:           strcpy(name, "all    ");        break;
+                       case SEGSIZEMODE_CURSIDE:       strcpy(name, "curside");        break;
+                       case SEGSIZEMODE_EDGE:          strcpy(name, "edge   ");        break;
+                       case SEGSIZEMODE_VERTEX:        strcpy(name, "vertex ");        break;
+                       default:
+                               Error("Illegal value for SegSizeMode in info.c/info_display_segsize\n");
+               }
+
+               gr_uprintf( 0, 0, "Mode: %s\n", name);
+
+               old_SegSizeMode = SegSizeMode;
+       }
+
+}
+
+extern int num_objects;
+
+//     ---------------------------------------------------------------------------------------------------
+void info_display_default(int show_all)
+{
+       static int old_Num_segments = -1;
+       static int old_Num_vertices = -1;
+       static int old_Num_objects = -1;
+       static int old_Cursegp_num = -1;
+       static int old_Curside = -1;
+       static int old_Cursegp_num_for_verts = -1;
+       static int old_CurrentTexture = -1;
+       static int old_Num_walls = -1;
+       static int old_Num_triggers = -1;
+
+       if (init_info | show_all) {
+               init_info = 0;
+               old_Num_segments = -1;
+               old_Num_vertices = -1;
+               old_Num_objects = -1;
+               old_Cursegp_num = -1;
+               old_Cursegp_num_for_verts = -1;
+               old_Curside = -1;
+               old_CurrentTexture = -1;
+               old_Num_walls = -1;
+               old_Num_triggers = -1;
+       }
+
+       gr_set_fontcolor(CBLACK,CWHITE);
+
+       //--------------- Number of segments ----------------
+
+       if ( old_Num_segments != Num_segments ) {
+               gr_uprintf( 0, 0, "Segments: %4d/%4d", Num_segments, MAX_SEGMENTS );
+               old_Num_segments = Num_segments;
+       }
+
+       //---------------- Number of vertics -----------------
+       
+       if ( old_Num_vertices != Num_vertices ) {
+               gr_uprintf( 0, 16, "Vertices: %4d/%4d", Num_vertices, MAX_VERTICES );
+               old_Num_vertices = Num_vertices;
+       }
+
+       //---------------- Number of objects -----------------
+       
+       if ( old_Num_objects != num_objects )   {
+               gr_uprintf( 0, 32, "Objs: %3d/%3d", num_objects, MAX_OBJECTS );
+               old_Num_objects = num_objects;
+       }
+
+       //--------------- Current_segment_number -------------
+       //--------------- Current_side_number -------------
+
+       if (( old_Cursegp_num != Cursegp-Segments ) || ( old_Curside != Curside ))      {
+               gr_uprintf( 0, 48, "Cursegp/side: %3d/%1d", Cursegp-Segments, Curside);
+               gr_uprintf( 0, 128, " tmap1,2,o: %3d/%3dx%1d", Cursegp->sides[Curside].tmap_num, Cursegp->sides[Curside].tmap_num2 & 0x3FFF, (Cursegp->sides[Curside].tmap_num2 >> 14) & 3);
+               old_Cursegp_num = Cursegp-Segments;
+               old_Curside = Curside;
+       }
+
+       //--------------- Current_vertex_numbers -------------
+
+       if ( old_Cursegp_num_for_verts != Cursegp-Segments )    {
+
+               gr_uprintf( 0, 64, "{%3d,%3d,%3d,%3d,", Cursegp->verts[0],Cursegp->verts[1],
+                                                                                                                                                                                        Cursegp->verts[2],Cursegp->verts[3] );
+               gr_uprintf( 0, 80," %3d,%3d,%3d,%3d}", Cursegp->verts[4],Cursegp->verts[5],
+                                                                                                                                                                                        Cursegp->verts[6],Cursegp->verts[7] );
+               old_Cursegp_num_for_verts = Cursegp-Segments;
+       }
+
+       //--------------- Num walls/links/triggers -------------------------
+
+       if ( old_Num_walls != Num_walls ) {
+//             gr_uprintf( 0, 96, "Walls/Links %d/%d", Num_walls, Num_links );
+               gr_uprintf( 0, 96, "Walls %3d", Num_walls );
+               old_Num_walls = Num_walls;
+       }
+
+       //--------------- Num triggers ----------------------
+
+       if ( old_Num_triggers != Num_triggers ) {
+               gr_uprintf( 0, 112, "Num_triggers %2d", Num_triggers );
+               old_Num_triggers = Num_triggers;
+       }
+
+       //--------------- Current texture number -------------
+
+       if ( old_CurrentTexture != CurrentTexture )     {
+               gr_uprintf( 0, 144, "Tex/Light: %3d %5.2f", CurrentTexture, f2fl(TmapInfo[CurrentTexture].lighting));
+               old_CurrentTexture = CurrentTexture;
+       }
+
+}
+
+//     ------------------------------------------------------------------------------------
+void clear_pad_display(void)
+{
+       gr_clear_canvas(CWHITE);
+   gr_set_fontcolor( CBLACK, CWHITE );
+}
+
+//     ------------------------------------------------------------------------------------
+void info_display_all( UI_WINDOW * wnd )
+{
+        static int old_padnum = -1;
+        int        padnum,show_all = 0;
+       grs_canvas *save_canvas = grd_curcanv;
+
+       wnd++;          //kill warning
+
+       grd_curcanv = Pad_text_canvas;
+
+       padnum = ui_pad_get_current();
+       Assert(padnum <= MAX_PAD_ID);
+
+       if (padnum != old_padnum) {
+               clear_pad_display();
+               old_padnum = padnum;
+               show_all = 1;
+       }
+
+       switch (padnum) {
+               case OBJECT_PAD_ID:                     // Object placement
+                       info_display_object_placement(show_all);
+                       break;
+               case SEGSIZE_PAD_ID:                    // Segment sizing
+                       info_display_segsize(show_all);
+                       break;
+               default:
+                       info_display_default(show_all);
+                       break;
+       }
+       grd_curcanv = save_canvas;
+}
+
diff --git a/main/editor/info.h b/main/editor/info.h
new file mode 100644 (file)
index 0000000..d283981
--- /dev/null
@@ -0,0 +1,47 @@
+/*
+THE COMPUTER CODE CONTAINED HEREIN IS THE SOLE PROPERTY OF PARALLAX
+SOFTWARE CORPORATION ("PARALLAX").  PARALLAX, IN DISTRIBUTING THE CODE TO
+END-USERS, AND SUBJECT TO ALL OF THE TERMS AND CONDITIONS HEREIN, GRANTS A
+ROYALTY-FREE, PERPETUAL LICENSE TO SUCH END-USERS FOR USE BY SUCH END-USERS
+IN USING, DISPLAYING,  AND CREATING DERIVATIVE WORKS THEREOF, SO LONG AS
+SUCH USE, DISPLAY OR CREATION IS FOR NON-COMMERCIAL, ROYALTY OR REVENUE
+FREE PURPOSES.  IN NO EVENT SHALL THE END-USER USE THE COMPUTER CODE
+CONTAINED HEREIN FOR REVENUE-BEARING PURPOSES.  THE END-USER UNDERSTANDS
+AND AGREES TO THE TERMS HEREIN AND ACCEPTS THE SAME BY USE OF THIS FILE.  
+COPYRIGHT 1993-1998 PARALLAX SOFTWARE CORPORATION.  ALL RIGHTS RESERVED.
+*/
+/*
+ * $Source: /cvs/cvsroot/d2x/main/editor/info.h,v $
+ * $Revision: 1.1 $
+ * $Author: btb $
+ * $Date: 2004-12-19 13:54:27 $
+ * 
+ * Header for info.c
+ * 
+ * $Log: not supported by cvs2svn $
+ * Revision 1.1.1.1  1999/06/14 22:02:36  donut
+ * Import of d1x 1.37 source.
+ *
+ * Revision 2.0  1995/02/27  11:34:32  john
+ * Version 2.0! No anonymous unions, Watcom 10.0, with no need
+ * for bitmaps.tbl.
+ * 
+ * Revision 1.2  1994/05/14  17:18:17  matt
+ * Got rid of externs in source (non-header) files
+ * 
+ * Revision 1.1  1994/05/14  16:30:39  matt
+ * Initial revision
+ * 
+ * 
+ */
+
+
+
+#ifndef _INFO_H
+#define _INFO_H
+
+void info_display_all( UI_WINDOW * wnd );
+
+extern int init_info;
+
+#endif
diff --git a/main/editor/kbuild.c b/main/editor/kbuild.c
new file mode 100644 (file)
index 0000000..738ecef
--- /dev/null
@@ -0,0 +1,284 @@
+/*
+THE COMPUTER CODE CONTAINED HEREIN IS THE SOLE PROPERTY OF PARALLAX
+SOFTWARE CORPORATION ("PARALLAX").  PARALLAX, IN DISTRIBUTING THE CODE TO
+END-USERS, AND SUBJECT TO ALL OF THE TERMS AND CONDITIONS HEREIN, GRANTS A
+ROYALTY-FREE, PERPETUAL LICENSE TO SUCH END-USERS FOR USE BY SUCH END-USERS
+IN USING, DISPLAYING,  AND CREATING DERIVATIVE WORKS THEREOF, SO LONG AS
+SUCH USE, DISPLAY OR CREATION IS FOR NON-COMMERCIAL, ROYALTY OR REVENUE
+FREE PURPOSES.  IN NO EVENT SHALL THE END-USER USE THE COMPUTER CODE
+CONTAINED HEREIN FOR REVENUE-BEARING PURPOSES.  THE END-USER UNDERSTANDS
+AND AGREES TO THE TERMS HEREIN AND ACCEPTS THE SAME BY USE OF THIS FILE.  
+COPYRIGHT 1993-1998 PARALLAX SOFTWARE CORPORATION.  ALL RIGHTS RESERVED.
+*/
+/*
+ * $Source: /cvs/cvsroot/d2x/main/editor/kbuild.c,v $
+ * $Revision: 1.1 $
+ * $Author: btb $
+ * $Date: 2004-12-19 13:54:27 $
+ *
+ * Functions for building parts of mines.
+ *
+ * $Log: not supported by cvs2svn $
+ * Revision 1.1.1.1  1999/06/14 22:03:19  donut
+ * Import of d1x 1.37 source.
+ *
+ * Revision 2.0  1995/02/27  11:35:43  john
+ * Version 2.0! No anonymous unions, Watcom 10.0, with no need
+ * for bitmaps.tbl.
+ * 
+ * Revision 1.20  1995/02/22  11:00:47  yuan
+ * prototype include.
+ * 
+ * Revision 1.19  1995/02/22  10:59:01  yuan
+ * Save sloppy mine before punching.
+ * 
+ * Revision 1.18  1994/08/25  21:57:56  mike
+ * IS_CHILD stuff.
+ * 
+ * Revision 1.17  1994/05/16  12:00:52  mike
+ * Call med_combine_duplicate_vertices before various build functions.
+ * 
+ * Revision 1.16  1994/05/09  23:34:31  mike
+ * Punch all sloppy sides in a group.
+ * 
+ * Revision 1.15  1994/02/16  15:23:06  yuan
+ * Checking in for editor make.
+ * 
+ * Revision 1.14  1994/01/21  12:01:31  yuan
+ * Added clearer editor_status messages (sloppy joint vs. joint)
+ * 
+ * Revision 1.13  1994/01/14  11:59:52  yuan
+ * New function in build menu. 
+ * "Punch" through walls to force a joint formation with
+ * closest segment:side, if the closest segment:side allows
+ * a connection.
+ * 
+ * Revision 1.12  1994/01/07  17:45:05  yuan
+ * Just changed some tabs and formatting I believe.
+ * 
+ * Revision 1.11  1993/12/06  19:33:36  yuan
+ * Fixed autosave stuff so that undo restores Cursegp and
+ * Markedsegp
+ * 
+ * Revision 1.10  1993/12/02  12:39:15  matt
+ * Removed extra includes
+ * 
+ * Revision 1.9  1993/11/12  14:31:31  yuan
+ * Added warn_if_concave_segments.
+ * 
+ * Revision 1.8  1993/11/11  17:12:45  yuan
+ * Fixed display of messages, so that concave segment
+ * warning doesn't wipe them out immediately.
+ * 
+ * Revision 1.7  1993/11/09  12:09:28  mike
+ * Remove extern for mine_filename, put it in editor.h
+ * 
+ * Revision 1.6  1993/11/08  19:14:06  yuan
+ * Added Undo command (not working yet)
+ * 
+ * Revision 1.5  1993/11/05  17:32:36  john
+ * added funcs
+ * .,
+ * 
+ * Revision 1.4  1993/11/01  16:53:51  mike
+ * Add CreateAdjacentJointsSegment and CreateAdjacentJointsAll
+ * 
+ * Revision 1.3  1993/11/01  11:24:59  mike
+ * Add CreateJointAdjacent
+ * 
+ * Revision 1.2  1993/10/29  19:13:11  yuan
+ * Added diagnostic messages
+ * 
+ * Revision 1.1  1993/10/13  18:53:27  john
+ * Initial revision
+ * 
+ *
+ */
+
+#ifdef RCS
+static char rcsid[] = "$Id: kbuild.c,v 1.1 2004-12-19 13:54:27 btb Exp $";
+#endif
+
+#include <string.h>
+
+#include "inferno.h"
+#include "editor/editor.h"
+#include "gameseg.h"
+#include "gamesave.h"
+#include "mono.h"
+
+//  ---------- Create a bridge segment between current segment/side and marked segment/side ----------
+int CreateBridge()
+{
+    if (!med_form_bridge_segment(Cursegp,Curside,Markedsegp,Markedside)) {
+               Update_flags |= UF_WORLD_CHANGED;
+               mine_changed = 1;
+       autosave_mine(mine_filename);
+       diagnostic_message("Bridge segment formed.");
+       strcpy(undo_status[Autosave_count], "Bridge segment UNDONE.");
+       warn_if_concave_segments();
+       }
+    return 1;
+}
+
+
+
+// ---------- Form a joint between current segment:side and marked segment:side, modifying marked segment ----------
+int FormJoint()
+{
+       if (!Markedsegp)
+               diagnostic_message("Marked segment not set -- unable to form joint.");
+       else {
+        if (!med_form_joint(Cursegp,Curside,Markedsegp,Markedside)) {
+            Update_flags |= UF_WORLD_CHANGED;
+            mine_changed = 1;
+            autosave_mine(mine_filename);
+            diagnostic_message("Joint formed.");
+            strcpy(undo_status[Autosave_count], "Joint undone.");
+                       warn_if_concave_segments();
+        }
+       }
+
+       return 1;
+
+}
+
+//  ---------- Create a bridge segment between current segment:side adjacent segment:side ----------
+int CreateAdjacentJoint()
+{
+       int             adj_side;
+       segment *adj_sp;
+
+       if (med_find_adjacent_segment_side(Cursegp, Curside, &adj_sp, &adj_side)) {
+               if (Cursegp->children[Curside] != adj_sp-Segments) {
+                       med_form_joint(Cursegp,Curside,adj_sp,adj_side);
+                       Update_flags |= UF_WORLD_CHANGED;
+                       mine_changed = 1;
+         autosave_mine(mine_filename);
+         diagnostic_message("Joint segment formed.");
+         strcpy(undo_status[Autosave_count], "Joint segment undone.");
+               warn_if_concave_segments();
+               } else
+                       editor_status("Attempted to form joint through connected side -- joint segment not formed (you bozo).");
+       } else
+               editor_status("Could not find adjacent segment -- joint segment not formed.");
+
+       return 1;
+}
+
+//  ---------- Create a bridge segment between current segment:side adjacent segment:side ----------
+int CreateSloppyAdjacentJoint()
+{
+       int             adj_side;
+       segment *adj_sp;
+
+       save_level("SLOPPY.LVL");
+
+       if (med_find_closest_threshold_segment_side(Cursegp, Curside, &adj_sp, &adj_side, 20*F1_0)) {
+               if (Cursegp->children[Curside] != adj_sp-Segments) {
+                       if (!med_form_joint(Cursegp,Curside,adj_sp,adj_side))
+                               {
+                               Update_flags |= UF_WORLD_CHANGED;
+                               mine_changed = 1;
+                autosave_mine(mine_filename);
+                diagnostic_message("Sloppy Joint segment formed.");
+                strcpy(undo_status[Autosave_count], "Sloppy Joint segment undone.");
+                       warn_if_concave_segments();
+                               }
+                       else editor_status("Couldn't form sloppy joint.\n");
+               } else
+                       editor_status("Attempted to form sloppy joint through connected side -- joint segment not formed (you bozo).");
+       } else
+               editor_status("Could not find close threshold segment -- joint segment not formed.");
+
+       return 1;
+}
+
+
+//  -------------- Create all sloppy joints within CurrentGroup ------------------
+int CreateSloppyAdjacentJointsGroup()
+{
+       int             adj_side;
+       segment *adj_sp;
+       int             num_segs = GroupList[current_group].num_segments;
+       short           *segs = GroupList[current_group].segments;
+       segment *segp;
+       int             done_been_a_change = 0;
+       int             segind, sidenum;
+
+       for (segind=0; segind<num_segs; segind++) {
+               segp = &Segments[segs[segind]];
+
+               for (sidenum=0; sidenum < MAX_SIDES_PER_SEGMENT; sidenum++)
+                       if (!IS_CHILD(segp->children[sidenum]))
+                               if (med_find_closest_threshold_segment_side(segp, sidenum, &adj_sp, &adj_side, 5*F1_0)) {
+                                       if (adj_sp->group == segp->group) {
+                                               if (segp->children[sidenum] != adj_sp-Segments)
+                                                       if (!med_form_joint(segp, sidenum, adj_sp,adj_side))
+                                                               done_been_a_change = 1;
+                                       }
+                               }
+       }
+
+       if (done_been_a_change) {
+               Update_flags |= UF_WORLD_CHANGED;
+               mine_changed = 1;
+               autosave_mine(mine_filename);
+               diagnostic_message("Sloppy Joint segment formed.");
+               strcpy(undo_status[Autosave_count], "Sloppy Joint segment undone.");
+               warn_if_concave_segments();
+       }
+
+       return 1;
+}
+
+
+//  ---------- Create a bridge segment between current segment and all adjacent segment:side ----------
+int CreateAdjacentJointsSegment()
+{
+       int             adj_side,s;
+       segment *adj_sp;
+
+       med_combine_duplicate_vertices(Vertex_active);
+
+       for (s=0; s<MAX_SIDES_PER_SEGMENT; s++) {
+               if (med_find_adjacent_segment_side(Cursegp, s, &adj_sp, &adj_side))
+                       if (Cursegp->children[s] != adj_sp-Segments)
+                                       {
+                                       med_form_joint(Cursegp,s,adj_sp,adj_side);
+                                       Update_flags |= UF_WORLD_CHANGED;
+                                       mine_changed = 1;
+                   autosave_mine(mine_filename);
+                   diagnostic_message("Adjacent Joint segment formed.");
+                   strcpy(undo_status[Autosave_count], "Adjacent Joint segment UNDONE.");
+                               warn_if_concave_segments();
+                                       }
+       }
+
+       return 1;
+}
+
+//  ---------- Create a bridge segment between all segment:side and all adjacent segment:side ----------
+int CreateAdjacentJointsAll()
+{
+       int             adj_side,seg,s;
+       segment *adj_sp;
+
+       med_combine_duplicate_vertices(Vertex_active);
+
+       for (seg=0; seg<=Highest_segment_index; seg++)
+               for (s=0; s<MAX_SIDES_PER_SEGMENT; s++)
+                       if (med_find_adjacent_segment_side(&Segments[seg], s, &adj_sp, &adj_side))
+                               if (Segments[seg].children[s] != adj_sp-Segments)
+                                               med_form_joint(&Segments[seg],s,adj_sp,adj_side);
+
+       Update_flags |= UF_WORLD_CHANGED;
+       mine_changed = 1;
+   autosave_mine(mine_filename);
+   diagnostic_message("All Adjacent Joint segments formed.");
+   strcpy(undo_status[Autosave_count], "All Adjacent Joint segments UNDONE.");
+       warn_if_concave_segments();
+   return 1;
+}
+
+
diff --git a/main/editor/kcurve.c b/main/editor/kcurve.c
new file mode 100644 (file)
index 0000000..d39deee
--- /dev/null
@@ -0,0 +1,204 @@
+/*
+THE COMPUTER CODE CONTAINED HEREIN IS THE SOLE PROPERTY OF PARALLAX
+SOFTWARE CORPORATION ("PARALLAX").  PARALLAX, IN DISTRIBUTING THE CODE TO
+END-USERS, AND SUBJECT TO ALL OF THE TERMS AND CONDITIONS HEREIN, GRANTS A
+ROYALTY-FREE, PERPETUAL LICENSE TO SUCH END-USERS FOR USE BY SUCH END-USERS
+IN USING, DISPLAYING,  AND CREATING DERIVATIVE WORKS THEREOF, SO LONG AS
+SUCH USE, DISPLAY OR CREATION IS FOR NON-COMMERCIAL, ROYALTY OR REVENUE
+FREE PURPOSES.  IN NO EVENT SHALL THE END-USER USE THE COMPUTER CODE
+CONTAINED HEREIN FOR REVENUE-BEARING PURPOSES.  THE END-USER UNDERSTANDS
+AND AGREES TO THE TERMS HEREIN AND ACCEPTS THE SAME BY USE OF THIS FILE.  
+COPYRIGHT 1993-1998 PARALLAX SOFTWARE CORPORATION.  ALL RIGHTS RESERVED.
+*/
+/*
+ * $Source: /cvs/cvsroot/d2x/main/editor/kcurve.c,v $
+ * $Revision: 1.1 $
+ * $Author: btb $
+ * $Date: 2004-12-19 13:54:27 $
+ *
+ * Functions for curve stuff.
+ *
+ * $Log: not supported by cvs2svn $
+ * Revision 1.1.1.1  1999/06/14 22:03:20  donut
+ * Import of d1x 1.37 source.
+ *
+ * Revision 2.0  1995/02/27  11:35:29  john
+ * Version 2.0! No anonymous unions, Watcom 10.0, with no need
+ * for bitmaps.tbl.
+ * 
+ * Revision 1.17  1994/08/25  21:56:43  mike
+ * IS_CHILD stuff.
+ * 
+ * Revision 1.16  1994/05/14  17:17:54  matt
+ * Got rid of externs in source (non-header) files
+ * 
+ * Revision 1.15  1994/01/28  10:52:24  mike
+ * Bind set_average_light_on_curside to DeleteCurve
+ * 
+ * Revision 1.14  1994/01/25  17:34:47  mike
+ * Stupidly bound fix_bogus_uvs_all to delete curve.
+ * 
+ * Revision 1.13  1993/12/06  19:34:15  yuan
+ * Fixed autosave so that undo restores Cursegp
+ * and Markedsegp
+ * 
+ * Revision 1.12  1993/12/02  12:39:28  matt
+ * Removed extra includes
+ * 
+ * Revision 1.11  1993/11/12  13:08:38  yuan
+ * Fixed warning for concave segment so it appears after any
+ * "less important" diagnostic messages.
+ * 
+ * Revision 1.10  1993/11/11  17:03:25  yuan
+ * Fixed undo-status display
+ * 
+ * Revision 1.9  1993/11/11  15:55:11  yuan
+ * Added undo messages.
+ * 
+ * Revision 1.8  1993/11/08  19:13:30  yuan
+ * Added Undo command (not working yet)
+ * 
+ * Revision 1.7  1993/11/05  17:32:51  john
+ * added funcs
+ * .,
+ * 
+ * Revision 1.6  1993/10/29  19:12:41  yuan
+ * Added diagnostic messages
+ * 
+ * Revision 1.5  1993/10/29  16:26:30  yuan
+ * Added diagnostic messages for curve generation
+ * 
+ * Revision 1.4  1993/10/22  19:47:30  yuan
+ * Can't build curve if Marked Seg has a segment attached.
+ * 
+ * Revision 1.3  1993/10/19  20:54:50  matt
+ * Changed/cleaned up window updates
+ * 
+ * Revision 1.2  1993/10/14  13:52:17  mike
+ * Add return value to AssignTexture
+ * 
+ * Revision 1.1  1993/10/13  18:53:11  john
+ * Initial revision
+ * 
+ *
+ */
+
+#ifdef RCS
+static char rcsid[] = "$Id: kcurve.c,v 1.1 2004-12-19 13:54:27 btb Exp $";
+#endif
+
+#include <string.h>
+
+#include "inferno.h"
+#include "editor.h"
+#include "kdefs.h"
+
+static fix         r1scale, r4scale;
+static int         curve;
+
+int InitCurve()
+{
+       curve = 0;
+    return 1;
+}
+
+int GenerateCurve()
+{
+    if ( (Markedsegp != 0) && !IS_CHILD(Markedsegp->children[Markedside])) {
+               r1scale = r4scale = F1_0*20;
+      autosave_mine( mine_filename );
+      diagnostic_message("Curve Generated.");
+               Update_flags |= UF_WORLD_CHANGED;
+      curve = generate_curve(r1scale, r4scale);
+               mine_changed = 1;
+        if (curve == 1) {
+            strcpy(undo_status[Autosave_count], "Curve Generation UNDONE.\n");
+        }
+        if (curve == 0) diagnostic_message("Cannot generate curve -- check Current segment.");
+    }
+    else diagnostic_message("Cannot generate curve -- check Marked segment.");
+       warn_if_concave_segments();
+
+       return 1;
+}
+
+int DecreaseR4()
+{
+       if (curve) {
+          Update_flags |= UF_WORLD_CHANGED;
+          delete_curve();
+          r4scale -= F1_0;
+          generate_curve(r1scale, r4scale);
+      diagnostic_message("R4 vector decreased.");
+          mine_changed = 1;
+               warn_if_concave_segments();
+       }
+       return 1;
+}
+
+int IncreaseR4()
+{
+       if (curve) {
+          Update_flags |= UF_WORLD_CHANGED;
+          delete_curve();
+          r4scale += F1_0;
+          generate_curve(r1scale, r4scale);
+      diagnostic_message("R4 vector increased.");
+          mine_changed = 1;
+               warn_if_concave_segments();
+       }
+       return 1;
+}
+
+int DecreaseR1()
+{
+       if (curve) {
+          Update_flags |= UF_WORLD_CHANGED;
+          delete_curve();
+          r1scale -= F1_0;
+          generate_curve(r1scale, r4scale);
+      diagnostic_message("R1 vector decreased.");
+          mine_changed = 1;
+               warn_if_concave_segments();
+       }
+       return 1;
+}
+
+int IncreaseR1()
+{
+       if (curve) {
+          Update_flags |= UF_WORLD_CHANGED;
+          delete_curve();
+          r1scale += F1_0;
+          generate_curve(r1scale, r4scale);
+      diagnostic_message("R1 vector increased.");
+          mine_changed = 1;
+               warn_if_concave_segments();
+       }
+       return 1;
+}
+
+int DeleteCurve()
+{
+// fix_bogus_uvs_all();
+set_average_light_on_curside();
+
+       if (curve) {
+          Update_flags |= UF_WORLD_CHANGED;
+          delete_curve();
+          curve = 0;
+          mine_changed = 1;
+      diagnostic_message("Curve Deleted.");
+               warn_if_concave_segments();
+       }
+       return 1;
+}
+
+int SetCurve()
+{
+       if (curve) curve = 0;
+   //autosave_mine( mine_filename );
+   //strcpy(undo_status[Autosave_count], "Curve Generation UNDONE.\n");
+   return 1;
+}
+
diff --git a/main/editor/kfuncs.c b/main/editor/kfuncs.c
new file mode 100644 (file)
index 0000000..a5a7595
--- /dev/null
@@ -0,0 +1,498 @@
+/*
+THE COMPUTER CODE CONTAINED HEREIN IS THE SOLE PROPERTY OF PARALLAX
+SOFTWARE CORPORATION ("PARALLAX").  PARALLAX, IN DISTRIBUTING THE CODE TO
+END-USERS, AND SUBJECT TO ALL OF THE TERMS AND CONDITIONS HEREIN, GRANTS A
+ROYALTY-FREE, PERPETUAL LICENSE TO SUCH END-USERS FOR USE BY SUCH END-USERS
+IN USING, DISPLAYING,  AND CREATING DERIVATIVE WORKS THEREOF, SO LONG AS
+SUCH USE, DISPLAY OR CREATION IS FOR NON-COMMERCIAL, ROYALTY OR REVENUE
+FREE PURPOSES.  IN NO EVENT SHALL THE END-USER USE THE COMPUTER CODE
+CONTAINED HEREIN FOR REVENUE-BEARING PURPOSES.  THE END-USER UNDERSTANDS
+AND AGREES TO THE TERMS HEREIN AND ACCEPTS THE SAME BY USE OF THIS FILE.  
+COPYRIGHT 1993-1998 PARALLAX SOFTWARE CORPORATION.  ALL RIGHTS RESERVED.
+*/
+/*
+ * $Source: /cvs/cvsroot/d2x/main/editor/kfuncs.c,v $
+ * $Revision: 1.1 $
+ * $Author: btb $
+ * $Date: 2004-12-19 13:54:27 $
+ * 
+ * .
+ * 
+ * $Log: not supported by cvs2svn $
+ * Revision 1.1.1.1  1999/06/14 22:03:23  donut
+ * Import of d1x 1.37 source.
+ *
+ * Revision 2.1  1995/03/08  16:07:23  yuan
+ * Added segment sizing default functions.
+ * 
+ * Revision 2.0  1995/02/27  11:34:45  john
+ * Version 2.0! No anonymous unions, Watcom 10.0, with no need
+ * for bitmaps.tbl.
+ * 
+ * Revision 1.112  1995/01/12  12:10:31  yuan
+ * Added coop object capability.
+ * 
+ * Revision 1.111  1994/11/27  23:17:16  matt
+ * Made changes for new mprintf calling convention
+ * 
+ * Revision 1.110  1994/11/23  12:17:22  mike
+ * bind retmap functions.
+ * 
+ * Revision 1.109  1994/11/19  15:20:54  mike
+ * rip out unused code and data.
+ * 
+ * Revision 1.108  1994/11/17  11:39:02  matt
+ * Ripped out code to load old mines
+ * 
+ * Revision 1.107  1994/10/27  10:06:12  mike
+ * kill macro stuff.
+ * 
+ * Revision 1.106  1994/10/18  15:31:43  mike
+ * Bind mine correction function to test keypad, key=1.
+ * 
+ * Revision 1.105  1994/09/26  23:22:06  matt
+ * Added functions to keep player's starting position from getting messed up
+ * 
+ * Revision 1.104  1994/09/24  14:15:41  mike
+ * Custom colored object support.
+ * 
+ * Revision 1.103  1994/09/23  18:03:54  yuan
+ * Finished wall checking code.
+ * 
+ * Revision 1.102  1994/09/22  19:03:52  mike
+ * Test function test_shortpos on ctrl-keypad3
+ * 
+ * Revision 1.101  1994/09/20  14:35:56  mike
+ * Bind functions to keypad 2, 3.
+ * 
+ * Revision 1.100  1994/09/15  22:58:10  matt
+ * Made new objects be oriented to their segment
+ * Added keypad function to flip an object upside-down
+ * 
+ * Revision 1.99  1994/09/14  16:50:50  yuan
+ * Added load mine only function
+ * 
+ * Revision 1.98  1994/08/25  21:57:52  mike
+ * IS_CHILD stuff.
+ * 
+ * Revision 1.97  1994/08/16  18:10:52  yuan
+ * Maded C place you in the center of a segment.
+ * 
+ * Revision 1.96  1994/08/15  17:48:01  yuan
+ * Added external walls.
+ * 
+ * Revision 1.95  1994/08/13  17:32:38  mike
+ * test function.
+ * 
+ * Revision 1.94  1994/08/05  21:17:57  matt
+ * Allow two doors to be linked together
+ * 
+ * Revision 1.93  1994/08/05  18:18:52  matt
+ * Made object rotation have 4x resolution, and SHIFT+rotate do old resolution.
+ * 
+ * Revision 1.92  1994/08/03  10:31:06  mike
+ * Add texture map propagation without uv assignment.
+ * 
+ * Revision 1.91  1994/08/02  14:17:00  mike
+ * Clean up dialog boxes, add one for Matt.
+ * 
+ * Revision 1.90  1994/07/30  17:04:47  mike
+ * Bind key for max_depth on path testing.
+ * 
+ */
+
+
+#ifdef RCS
+static char rcsid[] = "$Id: kfuncs.c,v 1.1 2004-12-19 13:54:27 btb Exp $";
+#endif
+
+#include <stdlib.h>
+
+#include "inferno.h"
+#include "func.h"
+#include "editor/kdefs.h"
+#include "segment.h"
+#include "editor/editor.h"
+#include "error.h"
+#include "slew.h"
+#include "mono.h"
+#include "gamesave.h"
+#include "editor/eobject.h"
+#include "editor/medwall.h"
+
+// Test function prototypes (replace Test1, 2 and 3 with whatever function 
+//                                                                             you wish to test.)
+extern void test_create_path();
+extern void test_create_all_paths();
+extern void test_create_path_many();
+extern void create_all_paths();
+extern void test_create_all_anchors();
+// extern void make_curside_bottom_side();
+extern void move_object_to_mouse_click();
+extern void test_create_n_segment_path();
+
+extern void set_all_modes_to_hover(void);
+
+extern void check_for_overlapping_segments(void);
+extern void init_replacements();
+
+extern void do_replacements(void);
+extern void do_replacements_all(void);
+
+int Test1() 
+{
+//     { int i;
+//             for (i=Highest_segment_index+1; i<MAX_SEGMENTS; i++)
+//                     Segments[i].segnum = -1;
+//     }
+
+       mprintf((0, "Initializing replacement list.\n"));
+       init_replacements();
+
+       return 0;
+}
+
+int Test2() 
+{
+//     mprintf((0, "\nCalling check_for_overlapping_segments.\n"));
+//     check_for_overlapping_segments();
+
+       mprintf((0, "Going to do texture replacements.\n"));
+       do_replacements();
+
+       return 0;
+}
+
+//extern fix fcd_test(void);
+//extern void test_shortpos(void);
+
+int Test3()
+{
+       Int3(); //      Are you sure you want to do this?
+       //      This will replace all textures in your replacement list
+       //      in all mines.
+       //      If you don't want to do this, set eip to the return statement
+       //      and continue.
+
+       mprintf((0, "Going to do texture replacements.\n"));
+       do_replacements_all();
+
+       return 0;
+}
+
+FUNCTION med_functions[] = {
+
+// Test functions
+{   "med-test-1",                         0,    Test1 },
+{   "med-test-2",                         0,    Test2 },
+{   "med-test-3",                         0,    Test3 },
+
+// In khelp.c
+{   "med-help",                         0,      DoHelp },
+
+// In kcurve.c
+{   "med-curve-init",                   0,      InitCurve },
+{   "med-curve-generate",               0,      GenerateCurve },
+{   "med-curve-decrease-r4",            0,      DecreaseR4 },
+{   "med-curve-increase-r4",            0,      IncreaseR4 },
+{   "med-curve-decrease-r1",            0,      DecreaseR1 },
+{   "med-curve-increase-r1",            0,      IncreaseR1 },
+{   "med-curve-delete",                 0,      DeleteCurve },
+{   "med-curve-set",                    0,      SetCurve },
+
+// In kmine.c
+{   "med-mine-save",                    0,      SaveMine },
+//{   "med-mine-load",                    0,      LoadMine },
+{   "med-mine-menu",                    0,      MineMenu },
+{   "med-mine-create-new",              0,      CreateNewMine },
+//{     "med-mine-load-old",                                    0,             LoadOldMine },                  
+{   "med-situation-save",               0,      SaveSituation },
+{   "med-situation-load",               0,      LoadSituation },
+{       "med-restore-game-state",                               0,             RestoreGameState },
+//{   "load-mine-only",                    0,      LoadMineOnly   },
+
+// In kgame.c
+{   "med-game-save",                    0,      SaveGameData },
+{   "med-game-load",                    0,      LoadGameData },
+
+// In kview.c
+{   "med-view-zoom-out",                0,      ZoomOut },
+{   "med-view-zoom-in",                 0,      ZoomIn },
+{   "med-view-move-away",               0,      MoveAway },
+{   "med-view-move-closer",             0,      MoveCloser },
+{   "med-view-toggle-chase",            0,      ToggleChaseMode },
+
+// In kbuild.c
+{   "med-build-bridge",                 0,      CreateBridge },
+{   "med-build-joint",                  0,      FormJoint },
+{   "med-build-adj-joint",              0,      CreateAdjacentJoint },
+{   "med-build-sloppy-adj-joint",       0,      CreateSloppyAdjacentJoint },
+{   "med-build-sloppy-adj-joint-group", 0,      CreateSloppyAdjacentJointsGroup },
+{   "med-build-adj-joints-segment",     0,      CreateAdjacentJointsSegment },
+{   "med-build-adj-joints-all",         0,      CreateAdjacentJointsAll },
+
+// In segment.c
+{   "med-segment-bottom",                       0,      ToggleBottom }, //make_curside_bottom_side },
+{   "med-segment-show-bottom",          0,      ToggleBottom },
+
+// In ksegmove.c
+{   "med-segmove-decrease-heading",     0,      DecreaseHeading },
+{   "med-segmove-increase-heading",     0,      IncreaseHeading },
+{   "med-segmove-decrease-pitch",       0,      DecreasePitch },
+{   "med-segmove-increase-pitch",       0,      IncreasePitch },
+{   "med-segmove-decrease-bank",        0,      DecreaseBank },
+{   "med-segmove-increase-bank",        0,      IncreaseBank },
+
+// In ksegsel.c
+{   "med-segsel-next-segment",          0,      SelectCurrentSegForward },
+{   "med-segsel-prev-segment",          0,      SelectCurrentSegBackward },
+{   "med-segsel-next-side",             0,      SelectNextSide },
+{   "med-segsel-prev-side",             0,      SelectPrevSide },
+{   "med-segsel-set-marked",            0,      CopySegToMarked },
+{   "med-segsel-bottom",                0,      SelectBottom },
+{   "med-segsel-front",                 0,      SelectFront },
+{   "med-segsel-top",                   0,      SelectTop },
+{   "med-segsel-back",                  0,      SelectBack },
+{   "med-segsel-left",                  0,      SelectLeft },
+{   "med-segsel-right",                 0,      SelectRight },
+
+// In ksegsize.c
+{   "med-segsize-increase-length",      0,      IncreaseSegLength },
+{   "med-segsize-decrease-length",      0,      DecreaseSegLength },
+{   "med-segsize-decrease-width",       0,      DecreaseSegWidth },
+{   "med-segsize-increase-width",       0,      IncreaseSegWidth },
+{   "med-segsize-increase-height",      0,      IncreaseSegHeight },
+{   "med-segsize-decrease-height",      0,      DecreaseSegHeight },
+
+{   "med-segsize-increase-length-big",  0,      IncreaseSegLengthBig },
+{   "med-segsize-decrease-length-big",  0,      DecreaseSegLengthBig },
+{   "med-segsize-decrease-width-big",   0,      DecreaseSegWidthBig },
+{   "med-segsize-increase-width-big",   0,      IncreaseSegWidthBig },
+{   "med-segsize-increase-height-big",  0,      IncreaseSegHeightBig },
+{   "med-segsize-decrease-height-big",  0,      DecreaseSegHeightBig },
+{   "med-segsize-toggle-mode",                          0,      ToggleSegSizeMode },
+{   "med-segsize-perturb-curside",              0,      PerturbCurside },
+{   "med-segsize-perturb-curside-big",  0,      PerturbCursideBig },
+{   "med-segsize-increase-length-default",  0,  IncreaseSegLengthDefault },
+{   "med-segsize-decrease-length-default",  0,  DecreaseSegLengthDefault },
+{   "med-segsize-increase-width-default",  0,   IncreaseSegWidthDefault },
+{   "med-segsize-decrease-width-default",  0,   DecreaseSegWidthDefault },
+{   "med-segsize-increase-height-default",  0,   IncreaseSegHeightDefault },
+{   "med-segsize-decrease-height-default",  0,   DecreaseSegHeightDefault },
+
+//  In ktmap.c
+{   "med-tmap-assign",                  0,      AssignTexture },
+{   "med-tmap-assign2",                 0,      AssignTexture2 },
+{   "med-tmap-clear2",                  0,      ClearTexture2 },
+{   "med-tmap-propogate",               0,      PropagateTextures },
+{   "med-tmap-propogate-move",          0,      PropagateTexturesMove },
+{   "med-tmap-propogate-move-uvs",      0,      PropagateTexturesMoveUVs },
+{   "med-tmap-propogate-move-uvs",      0,      PropagateTexturesMoveUVs },
+{   "med-tmap-propogate-uvs",           0,      PropagateTexturesUVs },
+{   "med-tmap-propogate-selected",      0,      PropagateTexturesSelected },
+
+//  In wall.c
+{   "med-wall-add-blastable",           0,      wall_add_blastable },
+{   "med-wall-add-door",                        0,      wall_add_door },
+{   "med-wall-add-closed",                      0,             wall_add_closed_wall },
+{   "med-wall-add-external",            0,             wall_add_external_wall },
+{   "med-wall-add-illusion",          0,      wall_add_illusion },
+{   "med-wall-restore-all",                             0,      wall_restore_all },
+{   "med-wall-remove",                          0,      wall_remove },
+{   "do-wall-dialog",                                           0,      do_wall_dialog },
+{   "med-link-doors",                                           0,             wall_link_doors },
+{   "med-unlink-door",                                          0,             wall_unlink_door },
+{   "check-walls",                                                      0,             check_walls },
+{   "delete-all-walls",                                                 0,             delete_all_walls },
+
+// In centers.c
+{   "do-centers-dialog",                                                0,      do_centers_dialog },
+
+//  In switch.c
+//{   "med-trigger-add-damage",           0,      trigger_add_damage },
+//{   "med-trigger-add-exit",            0,      trigger_add_exit },
+//{   "med-trigger-control",                            0,      trigger_control },
+//{   "med-trigger-remove",                      0,      trigger_remove },
+//{   "med-bind-wall-to-control",         0,      bind_wall_to_control_trigger },
+{   "do-trigger-dialog",                                        0,      do_trigger_dialog },
+
+//--//// In macro.c
+//--//{   "med-macro-menu",                   0,      MacroMenu },
+//--//{   "med-macro-play-fast",              0,      MacroPlayFast },
+//--//{   "med-macro-play-normal",            0,      MacroPlayNormal },
+//--//{   "med-macro-record-all",             0,      MacroRecordAll },
+//--//{   "med-macro-record-keys",            0,      MacroRecordKeys },
+//--//{   "med-macro-save",                   0,      MacroSave },
+//--//{   "med-macro-load",                   0,      MacroLoad },
+
+// In editor.c
+{   "med-update",                       0,      medlisp_update_screen },
+{   "med-segment-add",                  0,      AttachSegment },
+{   "med-segment-delete",               0,      medlisp_delete_segment },
+{   "med-segment-scale",                3,      medlisp_scale_segment },
+{   "med-segment-rotate",               3,      medlisp_rotate_segment },
+{   "med-dos-shell",                    0,      DosShell },
+//{   "med-lisp-call",                    0,      CallLisp },
+{   "med-editor-exit",                  0,      ExitEditor },
+{   "med-segment-exchange",             0,      ExchangeMarkandCurseg },
+{   "med-segment-mark",                 0,      CopySegToMarked },
+{       "med-about",                                                            0,      ShowAbout },
+{       "med-mark-start",                                               0,             MarkStart },
+{       "med-mark-end",                                                         0,             MarkEnd },
+
+//     In group.c
+{       "med-group-load",                                               0,             LoadGroup },
+{       "med-group-save",                                               0,             SaveGroup },
+{   "med-move-group",                   0,      MoveGroup },
+{   "med-copy-group",                   0,      CopyGroup },
+{       "med-rotate-group",                                             0,             RotateGroup },
+{   "med-segment-add-new",              0,      AttachSegmentNew },
+{       "med-mark-groupseg",                                    0,             MarkGroupSegment },
+{       "med-next-group",                                               0,             NextGroup },
+{       "med-prev-group",                                               0,             PrevGroup },
+{       "med-delete-group",                                             0,      DeleteGroup },
+{       "med-create-group",                                             0,             CreateGroup },
+{       "med-ungroup-segment",                                  0,             UngroupSegment },
+{       "med-group-segment",                                    0,             GroupSegment },
+{       "med-degroup-group",                                    0,             Degroup },
+{       "med-subtract-from-group",                      0,             SubtractFromGroup },
+
+//  In autosave.c
+{   "med-autosave-undo",                0,      UndoCommand },
+{       "med-autosave-toggle",                                  0,             ToggleAutosave },
+
+//     In texture.c
+{      "med-tass-flip-x",                                               0,     TexFlipX },
+{      "med-tass-flip-y",                                               0,     TexFlipY },
+{      "med-tass-slide-up",                                             0,     TexSlideUp },
+{      "med-tass-slide-left",                                   0,     TexSlideLeft },
+{      "med-tass-set-default",                                  0,     TexSetDefault },
+{      "med-tass-slide-right",                                  0,     TexSlideRight },
+{      "med-tass-rotate-left",                                  0,     TexRotateLeft },
+{      "med-tass-slide-down",                                   0,     TexSlideDown },
+{      "med-tass-stretch-down",                                 0,     TexStretchDown },
+{      "med-tass-stretch-up",                                   0,     TexStretchUp },
+{      "med-tass-rotate-right",                                 0,     TexRotateRight },
+{      "med-tass-select-active-edge",           0,     TexSelectActiveEdge },
+{      "med-tass-rotate-90-degrees",                    0,     TexRotate90Degrees },
+{  "med-tass-increase-tiling",          0,   TexIncreaseTiling },
+{  "med-tass-decrease-tiling",          0,   TexDecreaseTiling },
+{      "med-tass-slide-up-big",                                 0,     TexSlideUpBig },
+{      "med-tass-slide-left-big",                               0,     TexSlideLeftBig },
+{      "med-tass-slide-right-big",                      0,     TexSlideRightBig },
+{      "med-tass-rotate-left-big",                      0,     TexRotateLeftBig },
+{      "med-tass-slide-down-big",                               0,     TexSlideDownBig },
+{      "med-tass-rotate-right-big",                     0,     TexRotateRightBig },
+
+//     In eobject.c
+{      "med-obj-set-player",                                    0,     SetPlayerPosition },
+{      "med-obj-place-object",                                  0,     ObjectPlaceObject },
+{      "med-obj-place-object-tmap",                     0,     ObjectPlaceObjectTmap },
+{      "med-obj-move-nearer",                                   0,     ObjectMoveNearer },
+{      "med-obj-move-further",                                  0,     ObjectMoveFurther },
+{      "med-obj-delete-object",                                 0,     ObjectDelete },
+{  "med-obj-move-forward",                                      0,     ObjectMoveForward },
+{  "med-obj-move-left",                                                 0,     ObjectMoveLeft },
+{  "med-obj-set-default",                                       0,     ObjectSetDefault },
+{  "med-obj-move-right",                                        0,     ObjectMoveRight },
+{  "med-obj-move-back",                                                 0,     ObjectMoveBack },
+{  "med-obj-move-down",                                                 0,     ObjectMoveDown },
+{  "med-obj-move-up",                                           0,     ObjectMoveUp },
+{  "med-obj-select-next-in-segment",    0,     ObjectSelectNextinSegment },
+{  "med-obj-decrease-bank",                             0,     ObjectDecreaseBank },
+{  "med-obj-increase-bank",                             0,     ObjectIncreaseBank },
+{  "med-obj-decrease-pitch",                            0,     ObjectDecreasePitch },
+{  "med-obj-increase-pitch",                            0,     ObjectIncreasePitch },
+{  "med-obj-decrease-heading",                  0,     ObjectDecreaseHeading },
+{  "med-obj-increase-heading",                          0,     ObjectIncreaseHeading },
+{  "med-obj-decrease-bank-big",                         0,     ObjectDecreaseBankBig },
+{  "med-obj-increase-bank-big",                         0,     ObjectIncreaseBankBig },
+{  "med-obj-decrease-pitch-big",                        0,     ObjectDecreasePitchBig },
+{  "med-obj-increase-pitch-big",                        0,     ObjectIncreasePitchBig },
+{  "med-obj-decrease-heading-big",              0,     ObjectDecreaseHeadingBig },
+{  "med-obj-increase-heading-big",              0,     ObjectIncreaseHeadingBig },
+{  "med-obj-reset",                                                     0,     ObjectResetObject },
+{  "med-obj-flip",