home *** CD-ROM | disk | FTP | other *** search
/ Amiga ACS 1998 #6 / amigaacscoverdisc1998-061998.iso / games / descent / source / main / editor / group.c < prev    next >
C/C++ Source or Header  |  1998-06-08  |  66KB  |  2,100 lines

  1. /*
  2. THE COMPUTER CODE CONTAINED HEREIN IS THE SOLE PROPERTY OF PARALLAX
  3. SOFTWARE CORPORATION ("PARALLAX").  PARALLAX, IN DISTRIBUTING THE CODE TO
  4. END-USERS, AND SUBJECT TO ALL OF THE TERMS AND CONDITIONS HEREIN, GRANTS A
  5. ROYALTY-FREE, PERPETUAL LICENSE TO SUCH END-USERS FOR USE BY SUCH END-USERS
  6. IN USING, DISPLAYING,  AND CREATING DERIVATIVE WORKS THEREOF, SO LONG AS
  7. SUCH USE, DISPLAY OR CREATION IS FOR NON-COMMERCIAL, ROYALTY OR REVENUE
  8. FREE PURPOSES.  IN NO EVENT SHALL THE END-USER USE THE COMPUTER CODE
  9. CONTAINED HEREIN FOR REVENUE-BEARING PURPOSES.  THE END-USER UNDERSTANDS
  10. AND AGREES TO THE TERMS HEREIN AND ACCEPTS THE SAME BY USE OF THIS FILE.  
  11. COPYRIGHT 1993-1998 PARALLAX SOFTWARE CORPORATION.  ALL RIGHTS RESERVED.
  12. */
  13. /*        
  14.  * $Source: f:/miner/source/main/editor/rcs/group.c $
  15.  * $Revision: 2.0 $
  16.  * $Author: john $
  17.  * $Date: 1995/02/27 11:35:05 $
  18.  * 
  19.  * group functions
  20.  * 
  21.  * $Log: group.c $
  22.  * Revision 2.0  1995/02/27  11:35:05  john
  23.  * Version 2.0! No anonymous unions, Watcom 10.0, with no need
  24.  * for bitmaps.tbl.
  25.  * 
  26.  * Revision 1.65  1994/11/27  23:17:21  matt
  27.  * Made changes for new mprintf calling convention
  28.  * 
  29.  * Revision 1.64  1994/11/17  14:48:08  mike
  30.  * validation functions moved from editor to game.
  31.  * 
  32.  * Revision 1.63  1994/11/17  11:38:56  matt
  33.  * Ripped out code to load old mines
  34.  * 
  35.  * Revision 1.62  1994/10/27  10:06:20  mike
  36.  * adapt to no inverse table.
  37.  * 
  38.  * Revision 1.61  1994/10/03  23:40:08  mike
  39.  * New fuelcen_activate parameters.
  40.  * 
  41.  * Revision 1.60  1994/09/28  17:32:01  mike
  42.  * Make group copying work for copying a group's walls.
  43.  * 
  44.  * Revision 1.59  1994/09/20  14:35:28  mike
  45.  * Fix bugs in group subtraction code.  Don't allow to attach a group if the attach side is unfree.
  46.  * 
  47.  * Revision 1.58  1994/08/25  21:58:07  mike
  48.  * IS_CHILD stuff.
  49.  * 
  50.  * Revision 1.57  1994/08/04  19:12:58  matt
  51.  * Changed a bunch of vecmat calls to use multiple-function routines, and to
  52.  * allow the use of C macros for some functions
  53.  * 
  54.  * Revision 1.56  1994/08/03  15:40:01  mike
  55.  * Enable calls to compress_mine to get rid of bugs in group
  56.  * copying -- was creating invalid segments.
  57.  * 
  58.  * Revision 1.55  1994/06/30  10:59:13  yuan
  59.  * Fixed texture translations.
  60.  * 
  61.  * Revision 1.54  1994/06/22  17:36:00  mike
  62.  * Fix bug in group creation, was stuffing first two group segs over number
  63.  * of segments in group (then number would overwrite them), so there would
  64.  * be two bogus segments in group, one of which was always 0, the other
  65.  * would be a small number.
  66.  * 
  67.  * Revision 1.53  1994/06/14  17:07:15  john
  68.  * *** empty log message ***
  69.  * 
  70.  * Revision 1.52  1994/06/14  16:59:09  mike
  71.  * Fix references to tmap_num2, must strip off orientation bits.
  72.  * 
  73.  * Revision 1.51  1994/05/23  14:56:37  mike
  74.  * make current segment be add segment.
  75.  * 
  76.  * Revision 1.50  1994/05/19  12:10:01  matt
  77.  * Use new vecmat macros and globals
  78.  * 
  79.  * Revision 1.49  1994/05/17  10:33:59  matt
  80.  * Deleted unused get_free_object_num() func.
  81.  * 
  82.  * Revision 1.48  1994/05/09  23:34:17  mike
  83.  * Punch all sloppy sides in a group, speed up segment rotation.
  84.  * 
  85.  * Revision 1.47  1994/05/06  14:39:56  mike
  86.  * Make objects move and copy with groups.
  87.  * 
  88.  * Revision 1.46  1994/05/05  16:05:54  yuan
  89.  * Added fuelcen/repaircens to groups...
  90.  * 
  91.  * Eventually, walls will be added too...
  92.  * 
  93.  * Revision 1.45  1994/05/05  12:56:25  yuan
  94.  * Fixed a bunch of group bugs.
  95.  * 
  96.  * Revision 1.44  1994/05/04  14:10:04  mike
  97.  * Assert added to prevent bombing out when current_group = -1
  98.  * 
  99.  * Revision 1.43  1994/05/02  17:59:18  yuan
  100.  * Changed undo_status into an array rather than malloced pointers.
  101.  * 
  102.  * Revision 1.42  1994/05/02  15:23:19  mike
  103.  * Call med_combine_duplicate_vertices in med_copy_group and med_move_group.
  104.  * 
  105.  * Revision 1.41  1994/04/27  12:11:23  mike
  106.  * Fix bug in group rotation.
  107.  * 
  108.  * Revision 1.40  1994/04/22  10:07:37  yuan
  109.  * Make sure we don't get obj->next equal itself error.
  110.  * 
  111.  * Revision 1.39  1994/04/18  17:15:13  yuan
  112.  * Added error checking for select prev, and next group.
  113.  * 
  114.  */
  115.  
  116.  
  117. #pragma off (unreferenced)
  118. static char rcsid[] = "$Id: group.c 2.0 1995/02/27 11:35:05 john Exp $";
  119. #pragma on (unreferenced)
  120.  
  121.  
  122. #include <stdio.h>
  123. #include <string.h>
  124.  
  125. #include "mono.h"
  126. #include "gr.h"
  127. #include "cflib.h"
  128. #include "nocfile.h"
  129. #include "ui.h"
  130.  
  131. #include "inferno.h"
  132. #include "segment.h"
  133. #include    "editor.h"
  134. #include "error.h"
  135. #include "gamemine.h"
  136. #include "gameseg.h"
  137.  
  138. #include "bm.h"                // For MAX_TEXTURES.
  139. #include "textures.h"
  140. #include "hash.h"
  141. #include "fuelcen.h"
  142.  
  143. #include "medwall.h"
  144.  
  145. struct {
  146.     int     fileinfo_version;
  147.     int     fileinfo_sizeof;
  148. } group_top_fileinfo;    // Should be same as first two fields below...
  149.  
  150. struct {
  151.     int     fileinfo_version;
  152.     int     fileinfo_sizeof;
  153.     int     header_offset;              // Stuff common to game & editor
  154.     int     header_size;
  155.     int     editor_offset;               // Editor specific stuff
  156.     int     editor_size;
  157.     int     vertex_offset;
  158.     int     vertex_howmany;
  159.     int     vertex_sizeof;
  160.     int     segment_offset;
  161.     int     segment_howmany;
  162.     int     segment_sizeof;
  163.     int     texture_offset;
  164.     int     texture_howmany;
  165.     int     texture_sizeof;
  166. } group_fileinfo;
  167.  
  168. struct {
  169.     int     num_vertices;
  170.     int     num_segments;
  171. } group_header;
  172.  
  173. struct {
  174.     int     current_seg;
  175.     int     newsegment_offset;
  176.     int     newsegment_size;
  177.     int     Groupsegp;
  178.     int     Groupside;
  179. } group_editor;
  180.  
  181. group        GroupList[MAX_GROUPS+1];
  182. segment  *Groupsegp[MAX_GROUPS+1];
  183. int        Groupside[MAX_GROUPS+1];
  184. int        Group_orientation[MAX_GROUPS+1];
  185. int         Current_group=-1;
  186. int         Num_groups=0; 
  187.  
  188. extern void validate_segment_side(segment *sp, int sidenum);
  189.  
  190. // -- void swap_negate_columns(vms_matrix *rotmat, int col1, int col2)
  191. // -- {
  192. // --     fix    col1_1,col1_2,col1_3;
  193. // --     fix    col2_1,col2_2,col2_3;
  194. // -- 
  195. // --     switch (col1) {
  196. // --         case 0:
  197. // --             col1_1 = rotmat->m1;
  198. // --             col1_2 = rotmat->m2;
  199. // --             col1_3 = rotmat->m3;
  200. // --             break;
  201. // -- 
  202. // --         case 1:
  203. // --             col1_1 = rotmat->m4;
  204. // --             col1_2 = rotmat->m5;
  205. // --             col1_3 = rotmat->m6;
  206. // --             break;
  207. // -- 
  208. // --         case 2:
  209. // --             col1_1 = rotmat->m7;
  210. // --             col1_2 = rotmat->m8;
  211. // --             col1_3 = rotmat->m9;
  212. // --             break;
  213. // --     }
  214. // -- 
  215. // --     switch (col2) {
  216. // --         case 0:
  217. // --             col2_1 = rotmat->m1;
  218. // --             col2_2 = rotmat->m2;
  219. // --             col2_3 = rotmat->m3;
  220. // --             break;
  221. // -- 
  222. // --         case 1:
  223. // --             col2_1 = rotmat->m4;
  224. // --             col2_2 = rotmat->m5;
  225. // --             col2_3 = rotmat->m6;
  226. // --             break;
  227. // -- 
  228. // --         case 2:
  229. // --             col2_1 = rotmat->m7;
  230. // --             col2_2 = rotmat->m8;
  231. // --             col2_3 = rotmat->m9;
  232. // --             break;
  233. // --     }
  234. // -- 
  235. // --     switch (col2) {
  236. // --         case 0:
  237. // --             rotmat->m1 = -col1_1;
  238. // --             rotmat->m2 = -col1_2;
  239. // --             rotmat->m3 = -col1_3;
  240. // --             break;
  241. // -- 
  242. // --         case 1:
  243. // --             rotmat->m4 = -col1_1;
  244. // --             rotmat->m5 = -col1_2;
  245. // --             rotmat->m6 = -col1_3;
  246. // --             break;
  247. // -- 
  248. // --         case 2:
  249. // --             rotmat->m7 = -col1_1;
  250. // --             rotmat->m8 = -col1_2;
  251. // --             rotmat->m9 = -col1_3;
  252. // --             break;
  253. // --     }
  254. // -- 
  255. // --     switch (col1) {
  256. // --         case 0:
  257. // --             rotmat->m1 = -col2_1;
  258. // --             rotmat->m2 = -col2_2;
  259. // --             rotmat->m3 = -col2_3;
  260. // --             break;
  261. // -- 
  262. // --         case 1:
  263. // --             rotmat->m4 = -col2_1;
  264. // --             rotmat->m5 = -col2_2;
  265. // --             rotmat->m6 = -col2_3;
  266. // --             break;
  267. // -- 
  268. // --         case 2:
  269. // --             rotmat->m7 = -col2_1;
  270. // --             rotmat->m8 = -col2_2;
  271. // --             rotmat->m9 = -col2_3;
  272. // --             break;
  273. // --     }
  274. // -- 
  275. // -- }
  276. // -- 
  277. // -- void swap_negate_rows(vms_matrix *rotmat, int row1, int row2)
  278. // -- {
  279. // --     fix    row1_1,row1_2,row1_3;
  280. // --     fix    row2_1,row2_2,row2_3;
  281. // -- 
  282. // --     switch (row1) {
  283. // --         case 0:
  284. // --             row1_1 = rotmat->m1;
  285. // --             row1_2 = rotmat->m4;
  286. // --             row1_3 = rotmat->m7;
  287. // --             break;
  288. // -- 
  289. // --         case 1:
  290. // --             row1_1 = rotmat->m2;
  291. // --             row1_2 = rotmat->m5;
  292. // --             row1_3 = rotmat->m8;
  293. // --             break;
  294. // -- 
  295. // --         case 2:
  296. // --             row1_1 = rotmat->m3;
  297. // --             row1_2 = rotmat->m6;
  298. // --             row1_3 = rotmat->m9;
  299. // --             break;
  300. // --     }
  301. // -- 
  302. // --     switch (row2) {
  303. // --         case 0:
  304. // --             row2_1 = rotmat->m1;
  305. // --             row2_2 = rotmat->m4;
  306. // --             row2_3 = rotmat->m7;
  307. // --             break;
  308. // -- 
  309. // --         case 1:
  310. // --             row2_1 = rotmat->m2;
  311. // --             row2_2 = rotmat->m5;
  312. // --             row2_3 = rotmat->m8;
  313. // --             break;
  314. // -- 
  315. // --         case 2:
  316. // --             row2_1 = rotmat->m3;
  317. // --             row2_2 = rotmat->m6;
  318. // --             row2_3 = rotmat->m9;
  319. // --             break;
  320. // --     }
  321. // -- 
  322. // --     switch (row2) {
  323. // --         case 0:
  324. // --             rotmat->m1 = -row1_1;
  325. // --             rotmat->m4 = -row1_2;
  326. // --             rotmat->m7 = -row1_3;
  327. // --             break;
  328. // -- 
  329. // --         case 1:
  330. // --             rotmat->m2 = -row1_1;
  331. // --             rotmat->m5 = -row1_2;
  332. // --             rotmat->m8 = -row1_3;
  333. // --             break;
  334. // -- 
  335. // --         case 2:
  336. // --             rotmat->m3 = -row1_1;
  337. // --             rotmat->m6 = -row1_2;
  338. // --             rotmat->m9 = -row1_3;
  339. // --             break;
  340. // --     }
  341. // -- 
  342. // --     switch (row1) {
  343. // --         case 0:
  344. // --             rotmat->m1 = -row2_1;
  345. // --             rotmat->m4 = -row2_2;
  346. // --             rotmat->m7 = -row2_3;
  347. // --             break;
  348. // -- 
  349. // --         case 1:
  350. // --             rotmat->m2 = -row2_1;
  351. // --             rotmat->m5 = -row2_2;
  352. // --             rotmat->m8 = -row2_3;
  353. // --             break;
  354. // -- 
  355. // --         case 2:
  356. // --             rotmat->m3 = -row2_1;
  357. // --             rotmat->m6 = -row2_2;
  358. // --             rotmat->m9 = -row2_3;
  359. // --             break;
  360. // --     }
  361. // -- 
  362. // -- }
  363. // -- 
  364. // -- // ------------------------------------------------------------------------------------------------
  365. // -- void    side_based_matrix(vms_matrix *rotmat,int destside)
  366. // -- {
  367. // --     vms_angvec    rotvec;
  368. // --     vms_matrix    r1,rtemp;
  369. // -- 
  370. // --     switch (destside) {
  371. // --         case WLEFT:
  372. // -- //            swap_negate_columns(rotmat,1,2);
  373. // -- //            swap_negate_rows(rotmat,1,2);
  374. // --             break;
  375. // -- 
  376. // --         case WTOP:
  377. // --             break;
  378. // -- 
  379. // --         case WRIGHT:
  380. // -- //            swap_negate_columns(rotmat,1,2);
  381. // -- //            swap_negate_rows(rotmat,1,2);
  382. // --             break;
  383. // -- 
  384. // --         case WBOTTOM:
  385. // --             break;
  386. // -- 
  387. // --         case WFRONT:
  388. // --             break;
  389. // -- 
  390. // --         case WBACK:
  391. // --             break;
  392. // --     }
  393. // -- 
  394. // -- }
  395.  
  396.  
  397. // ------------------------------------------------------------------------------------------------
  398. //    Rotate a group about a point.
  399. //    The segments in the group are indicated (by segment number) in group_seglist.  There are group_size segments.
  400. //    The point about which the groups is rotated is the center of first_seg:first_side.
  401. //    delta_flag:
  402. //        0    absolute rotation, destination specified in terms of base_seg:base_side, used in moving or copying a group
  403. //        1    relative rotation, destination specified relative to current orientation of first_seg:first_side
  404. //    Note: The group must exist in the mine, consisting of actual points in the world.  If any points in the
  405. //            segments in the group are shared by segments not in the group, those points will get rotated and the
  406. //            segments not in the group will have their shapes modified.
  407. //    Return value:
  408. //        0    group rotated
  409. //        1    unable to rotate group
  410. 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)
  411. {
  412.     vms_matrix    rotmat2,rotmat,rotmat3,rotmat4;
  413.     vms_angvec    pbh = {0,0,0};
  414.  
  415.     //    Determine whether this rotation is a delta rotation, meaning to just rotate in place, or an absolute rotation,
  416.     //    which means that the destination rotation is specified, not as a delta, but as an absolute
  417.     if (delta_flag) {
  418.          //    Create rotation matrix describing rotation.
  419.          med_extract_matrix_from_segment(first_seg, &rotmat4);        // get rotation matrix describing current orientation of first seg
  420.         set_matrix_based_on_side(&rotmat4, first_side);
  421.         rotmat3 = *orient_matrix;
  422.         vm_transpose_matrix(&rotmat3);
  423.          vm_matrix_x_matrix(&rotmat,&rotmat4,&rotmat3);            // this is the desired orientation of the new segment
  424.         vm_transpose_matrix(&rotmat4);
  425.          vm_matrix_x_matrix(&rotmat2,&rotmat,&rotmat4);            // this is the desired orientation of the new segment
  426.     } else {
  427.          //    Create rotation matrix describing rotation.
  428.  
  429.          med_extract_matrix_from_segment(base_seg, &rotmat);        // get rotation matrix describing desired orientation
  430.          set_matrix_based_on_side(&rotmat, base_side);                // modify rotation matrix for desired side
  431.  
  432.          //    If the new segment is to be attached without rotation, then its orientation is the same as the base_segment
  433.          vm_matrix_x_matrix(&rotmat4,&rotmat,orient_matrix);            // this is the desired orientation of the new segment
  434.  
  435.         pbh.b = orientation*16384;
  436.         vm_angles_2_matrix(&rotmat3,&pbh);
  437.         vm_matrix_x_matrix(&rotmat, &rotmat4, &rotmat3);
  438.         rotmat4 = rotmat;
  439.  
  440.          rotmat = rotmat4;
  441.  
  442.          med_extract_matrix_from_segment(first_seg, &rotmat3);        // get rotation matrix describing current orientation of first seg
  443.  
  444.          // It is curious that the following statement has no analogue in the med_attach_segment_rotated code.
  445.          //    Perhaps it is because segments are always attached at their front side.  If the back side is the side
  446.          //    passed to the function, then the matrix is not modified, which might suggest that what you need to do below
  447.          //    is use Side_opposite[first_side].
  448.          set_matrix_based_on_side(&rotmat3, Side_opposite[first_side]);                // modify rotation matrix for desired side
  449.  
  450.          vm_transpose_matrix(&rotmat3);                                // get the inverse of the current orientation matrix
  451.          vm_matrix_x_matrix(&rotmat2,&rotmat,&rotmat3);            // now rotmat2 takes the current segment to the desired orientation
  452.          vm_transpose_matrix(&rotmat2);
  453.     }
  454.  
  455.     *result_mat = rotmat2;
  456.  
  457. }
  458.  
  459. // -----------------------------------------------------------------------------------------
  460. // Rotate all vertices and objects in group.
  461. void med_rotate_group(vms_matrix *rotmat, short *group_seglist, int group_size, segment *first_seg, int first_side)
  462. {
  463.     int            v,s, objnum;
  464.     byte            vertex_list[MAX_VERTICES];
  465.     vms_vector    rotate_center;
  466.  
  467.     compute_center_point_on_side(&rotate_center, first_seg, first_side);
  468.  
  469.     //    Create list of points to rotate.
  470.     for (v=0; v<=Highest_vertex_index; v++)
  471.         vertex_list[v] = 0;
  472.  
  473.     for (s=0; s<group_size; s++) {
  474.         segment *sp = &Segments[group_seglist[s]];
  475.  
  476.         for (v=0; v<MAX_VERTICES_PER_SEGMENT; v++)
  477.             vertex_list[sp->verts[v]] = 1;
  478.  
  479.         //    Rotate center of all objects in group.
  480.         objnum = sp->objects;
  481.         while (objnum != -1) {
  482.             vms_vector    tv, tv1;
  483.  
  484.             mprintf((0, "%2i ", objnum));
  485.             vm_vec_sub(&tv1,&Objects[objnum].pos,&rotate_center);
  486.             vm_vec_rotate(&tv,&tv1,rotmat);
  487.             vm_vec_add(&Objects[objnum].pos, &tv, &rotate_center);
  488.  
  489.             objnum = Objects[objnum].next;
  490.         }            
  491.     }
  492.  
  493.     // Do the pre-rotation xlate, do the rotation, do the post-rotation xlate
  494.     for (v=0; v<=Highest_vertex_index; v++)
  495.         if (vertex_list[v]) {
  496.             vms_vector    tv,tv1;
  497.  
  498.             vm_vec_sub(&tv1,&Vertices[v],&rotate_center);
  499.             vm_vec_rotate(&tv,&tv1,rotmat);
  500.             vm_vec_add(&Vertices[v],&tv,&rotate_center);
  501.  
  502.         }
  503.  
  504. }
  505.  
  506.  
  507. // ------------------------------------------------------------------------------------------------
  508. void cgl_aux(segment *segp, short *seglistp, int *num_segs, short *ignore_list, int num_ignore_segs)
  509. {
  510.     int    i, side;
  511.     int    curseg = segp-Segments;
  512.  
  513.     for (i=0; i<num_ignore_segs; i++)
  514.         if (curseg == ignore_list[i])
  515.             return;
  516.  
  517.     if ((segp-Segments < 0) || (segp-Segments >= MAX_SEGMENTS)) {
  518.         mprintf((0,"Warning -- invalid segment index = %i, max = %i\n",segp-Segments,MAX_SEGMENTS));
  519.         Int3();
  520.     }
  521.  
  522.     if (!Been_visited[segp-Segments]) {
  523.         seglistp[(*num_segs)++] = segp-Segments;
  524.         Been_visited[segp-Segments] = 1;
  525.  
  526.         for (side=0; side<MAX_SIDES_PER_SEGMENT; side++)
  527.             if (IS_CHILD(segp->children[side]))
  528.                 cgl_aux(&Segments[segp->children[side]], seglistp, num_segs, ignore_list, num_ignore_segs);
  529.     }
  530. }
  531.  
  532. // ------------------------------------------------------------------------------------------------
  533. //    Sets Been_visited[n] if n is reachable from segp
  534. void create_group_list(segment *segp, short *seglistp, int *num_segs, short *ignore_list, int num_ignore_segs)
  535. {
  536.     int    i;
  537.  
  538.     for (i=0; i<MAX_SEGMENTS; i++)
  539.         Been_visited[i] = 0;
  540.  
  541.     cgl_aux(segp, seglistp, num_segs, ignore_list, num_ignore_segs);
  542. }
  543.  
  544.  
  545. #define MXS MAX_SEGMENTS
  546. #define MXV MAX_VERTICES
  547.  
  548. // ------------------------------------------------------------------------------------------------
  549. void duplicate_group(byte *vertex_ids, short *segment_ids, int num_segments)
  550. {
  551.     int    v,s,ss,new_vertex_id,new_segment_id,sidenum;
  552.     short    new_segment_ids[MAX_SEGMENTS];
  553.     short    new_vertex_ids[MAX_VERTICES];        // If new_vertex_ids[v] != -1, then vertex v has been remapped to new_vertex_ids[v]
  554.     short    new_object_ids[MAX_OBJECTS];
  555.  
  556.     //    duplicate vertices
  557.     for (v=0; v<MXV; v++)
  558.         new_vertex_ids[v] = -1;
  559.  
  560.     for (v=0; v<MAX_OBJECTS; v++)
  561.         new_object_ids[v] = -1;
  562.  
  563.     //    duplicate vertices
  564.     for (v=0; v<=Highest_vertex_index; v++) {
  565.         if (vertex_ids[v]) {
  566.             new_vertex_id = med_create_duplicate_vertex(&Vertices[v]);
  567.             new_vertex_ids[v] = new_vertex_id;
  568.         }
  569.     }
  570.  
  571.     //    duplicate segments
  572.     for (s=0; s<num_segments; s++) {
  573.         int    objnum;
  574.  
  575.         new_segment_id = med_create_duplicate_segment(&Segments[segment_ids[s]]);
  576.         new_segment_ids[s] = new_segment_id;
  577.         objnum = Segments[new_segment_id].objects;
  578.         Segments[new_segment_id].objects = -1;
  579.         while (objnum != -1) {
  580.             if (Objects[objnum].type != OBJ_PLAYER) {
  581.                 int new_obj_id;
  582.                 new_obj_id = obj_create_copy(objnum, &Objects[objnum].pos, new_segment_id);
  583.                 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));
  584.             }
  585.             objnum = Objects[objnum].next;
  586.         }
  587.     }
  588.  
  589.     //    Now, for each segment in segment_ids, correct its children numbers by translating through new_segment_ids
  590.     //    and correct its vertex numbers by translating through new_vertex_ids
  591.     for (s=0; s<num_segments; s++) {
  592.         segment *sp = &Segments[new_segment_ids[s]];
  593.         for (sidenum=0; sidenum<MAX_SIDES_PER_SEGMENT; sidenum++) {
  594.             int seg = sp->children[sidenum];
  595.             if (IS_CHILD(seg)) {
  596.                 for (ss=0; ss<num_segments; ss++) {
  597.                     if (seg == segment_ids[ss])
  598.                         Segments[new_segment_ids[s]].children[sidenum] = new_segment_ids[ss];
  599.                 }
  600.             }
  601.         }    // end for (sidenum=0...
  602.  
  603.         //    Now fixup vertex ids
  604.         for (v=0; v<MAX_VERTICES_PER_SEGMENT; v++) {
  605.             if (vertex_ids[sp->verts[v]]) {
  606.                 sp->verts[v] = new_vertex_ids[sp->verts[v]];
  607.             }
  608.         }
  609.     }    // end for (s=0...
  610.  
  611.     //    Now, copy new_segment_ids into segment_ids
  612.     for (s=0; s<num_segments; s++) {
  613.         segment_ids[s] = new_segment_ids[s];
  614.     }
  615.  
  616.     //    Now, copy new_vertex_ids into vertex_ids
  617.     for (v=0; v<MXV; v++)
  618.         vertex_ids[v] = 0;
  619.  
  620.     for (v=0; v<MXV; v++) {
  621.         if (new_vertex_ids[v] != -1)
  622.             vertex_ids[new_vertex_ids[v]] = 1;
  623.  
  624.     }
  625. }
  626.  
  627.  
  628. // ------------------------------------------------------------------------------------------------
  629. int in_group(int segnum, int group_num)
  630. {
  631.     int    i;
  632.  
  633.     for (i=0; i<GroupList[group_num].num_segments; i++)
  634.         if (segnum == GroupList[group_num].segments[i])
  635.             return 1;
  636.  
  637.     return 0;
  638. }
  639.  
  640. // ------------------------------------------------------------------------------------------------
  641. //    Copy a group of segments.
  642. //    The group is defined as all segments accessible from group_seg.
  643. //    The group is copied so group_seg:group_side is incident upon base_seg:base_side.
  644. //    group_seg and its vertices are bashed to coincide with base_seg.
  645. //    If any vertex of base_seg is contained in a segment that is reachable from group_seg, then errror.
  646. int med_copy_group(int delta_flag, segment *base_seg, int base_side, segment *group_seg, int group_side, vms_matrix *orient_matrix)
  647. {
  648.     int            v,s;
  649.     vms_vector    srcv,destv;
  650.     int             x;
  651.     int            new_current_group;
  652.     segment        *segp;
  653.     int             c;
  654.     int            gs_index;
  655.     byte            in_vertex_list[MAX_VERTICES];
  656.     vms_matrix    rotmat;
  657.     int            objnum;
  658.  
  659.     if (IS_CHILD(base_seg->children[base_side])) {
  660.         editor_status("Error -- unable to copy group, base_seg:base_side must be free.");
  661.         return 1;
  662.     }
  663.  
  664.     if (Num_groups == MAX_GROUPS) {
  665.         x = MessageBox( -2, -2, 2, "Warning: You have reached the MAXIMUM group number limit. Continue?", "No", "Yes" );
  666.         if (x==1)
  667.             return 0;
  668.     }
  669.  
  670.     if (Num_groups < MAX_GROUPS) { 
  671.         Num_groups++;
  672.         new_current_group = Num_groups-1;
  673.     } else
  674.         new_current_group = 0;
  675.  
  676.     Assert(Current_group >= 0);
  677.  
  678.     // Find groupsegp index
  679.     for (s=0;s<GroupList[Current_group].num_segments;s++)
  680.         if (GroupList[Current_group].segments[s] == (Groupsegp[Current_group]-Segments))
  681.             gs_index=s; 
  682.  
  683.     GroupList[new_current_group] = GroupList[Current_group];
  684.  
  685.     //    Make a list of all vertices in group.
  686.     if (group_seg == &New_segment)
  687.         for (v=0; v<MAX_VERTICES_PER_SEGMENT; v++)
  688.             in_vertex_list[group_seg->verts[v]] = 1;
  689.     else {
  690.         for (v=0; v<=Highest_vertex_index; v++)
  691.             in_vertex_list[v] = 0;
  692.  
  693.         for (s=0; s<GroupList[new_current_group].num_segments; s++)
  694.             for (v=0; v<MAX_VERTICES_PER_SEGMENT; v++)
  695.                 in_vertex_list[Segments[GroupList[new_current_group].segments[s]].verts[v]] = 1;
  696.     }
  697.  
  698.     //    Show which objects are in which segments before group copy.
  699.     // for (s=0; s<=Highest_segment_index; s++) {
  700.         // int    objnum = Segments[s].objects;
  701.  
  702.         // mprintf((0, "Before: Segment #%2i contains objects ", s));
  703.  
  704.         // while (objnum != -1) {
  705.         //     mprintf((0, "%2i ",objnum));
  706.         //     objnum = Objects[objnum].next;
  707.         // }
  708.         // mprintf((0, "\n"));
  709.     // }
  710.  
  711.     // Given a list of vertex indices (indicated by !0 in in_vertex_list) and segment indices (in list GroupList[Current_group].segments, there
  712.     //    are GroupList[Current_group].num_segments segments), copy all segments and vertices
  713.     //    Return updated lists of vertices and segments in in_vertex_list and GroupList[Current_group].segments
  714.     duplicate_group(in_vertex_list, GroupList[new_current_group].segments, GroupList[new_current_group].num_segments);
  715.  
  716.     //group_seg = &Segments[GroupList[new_current_group].segments[0]];                    // connecting segment in group has been changed, so update group_seg
  717.  
  718.    Groupsegp[new_current_group] = group_seg = &Segments[GroupList[new_current_group].segments[gs_index]];
  719.     Groupside[new_current_group] = Groupside[Current_group];
  720.  
  721.     for (s=0; s<GroupList[new_current_group].num_segments; s++) {
  722.         Segments[GroupList[new_current_group].segments[s]].group = new_current_group;
  723.         Segments[GroupList[new_current_group].segments[s]].special = SEGMENT_IS_NOTHING;
  724.         Segments[GroupList[new_current_group].segments[s]].matcen_num = -1;
  725.     }
  726.  
  727.     // Breaking connections between segments in the current group and segments not in the group.
  728.     for (s=0; s<GroupList[new_current_group].num_segments; s++) {
  729.         mprintf((0, "[%3i %3i] ", GroupList[new_current_group].segments[s], GroupList[current_group].segments[s]));
  730.         segp = &Segments[GroupList[new_current_group].segments[s]];
  731.         for (c=0; c<MAX_SIDES_PER_SEGMENT; c++) 
  732.             if (IS_CHILD(segp->children[c])) {
  733.                 if (!in_group(segp->children[c], new_current_group)) {
  734.                     mprintf((0, "2: Breaking connection at seg:side = %i:%i\n", segp-Segments, c));
  735.                     segp->children[c] = -1;
  736.                     validate_segment_side(segp,c);                    // we have converted a connection to a side so validate the segment
  737.                 }
  738.             }
  739.     }
  740.  
  741.     copy_uvs_seg_to_seg(&New_segment, Groupsegp[new_current_group]);
  742.     
  743.     //    Now do the copy
  744.     //    First, xlate all vertices so center of group_seg:group_side is at origin
  745.     compute_center_point_on_side(&srcv,group_seg,group_side);
  746.     for (v=0; v<=Highest_vertex_index; v++)
  747.         if (in_vertex_list[v])
  748.             vm_vec_sub2(&Vertices[v],&srcv);
  749.  
  750.     //    Now, translate all object positions.
  751.     for (s=0; s<GroupList[new_current_group].num_segments; s++) {
  752.         int    segnum = GroupList[new_current_group].segments[s];
  753.  
  754.         objnum = Segments[segnum].objects;
  755.  
  756.         while (objnum != -1) {
  757.             vm_vec_sub2(&Objects[objnum].pos, &srcv);
  758.             objnum = Objects[objnum].next;
  759.         }
  760.     }
  761.  
  762.     //    Now, rotate segments in group so orientation of group_seg is same as base_seg.
  763.     med_create_group_rotation_matrix(&rotmat, delta_flag, group_seg, group_side, base_seg, base_side, orient_matrix, 0);
  764.     med_rotate_group(&rotmat, GroupList[new_current_group].segments, GroupList[new_current_group].num_segments, group_seg, group_side);
  765.  
  766.     //    Now xlate all vertices so group_seg:group_side shares center point with base_seg:base_side
  767.     compute_center_point_on_side(&destv,base_seg,base_side);
  768.     for (v=0; v<=Highest_vertex_index; v++)
  769.         if (in_vertex_list[v])
  770.             vm_vec_add2(&Vertices[v],&destv);
  771.  
  772.     //    Now, xlate all object positions.
  773.     for (s=0; s<GroupList[new_current_group].num_segments; s++) {
  774.         int    segnum = GroupList[new_current_group].segments[s];
  775.         int    objnum = Segments[segnum].objects;
  776.  
  777.         while (objnum != -1) {
  778.             vm_vec_add2(&Objects[objnum].pos, &destv);
  779.             objnum = Objects[objnum].next;
  780.         }
  781.     }
  782.  
  783.     //    Now, copy all walls (ie, doors, illusionary, etc.) into the new group.
  784.     copy_group_walls(Current_group, new_current_group);
  785.  
  786.     Current_group = new_current_group;
  787.  
  788.     //    Now, form joint on connecting sides.
  789.     med_form_joint(base_seg,base_side,Groupsegp[Current_group],Groupside[new_current_group]);
  790.  
  791.     validate_selected_segments();
  792.     med_combine_duplicate_vertices(in_vertex_list);
  793.  
  794.     return 0;
  795. }
  796.  
  797.  
  798. // ------------------------------------------------------------------------------------------------
  799. //    Move a group of segments.
  800. //    The group is defined as all segments accessible from group_seg.
  801. //    The group is moved so group_seg:group_side is incident upon base_seg:base_side.
  802. //    group_seg and its vertices are bashed to coincide with base_seg.
  803. //    If any vertex of base_seg is contained in a segment that is reachable from group_seg, then errror.
  804. 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)
  805. {
  806.     int            v,vv,s,ss,c,d;
  807.     vms_vector    srcv,destv;
  808.     segment        *segp, *csegp, *dsegp;
  809.     byte            in_vertex_list[MAX_VERTICES], out_vertex_list[MAX_VERTICES];
  810.     int            local_hvi;
  811.     vms_matrix    rotmat;
  812.  
  813.     if (IS_CHILD(base_seg->children[base_side]))
  814.         if (base_seg->children[base_side] != group_seg-Segments) {
  815.             editor_status("Error -- unable to move group, base_seg:base_side must be free or point to group_seg.");
  816.             return 1;
  817.     }
  818.  
  819. //    // See if any vertices in base_seg are contained in any vertex in group_list
  820. //    for (v=0; v<MAX_VERTICES_PER_SEGMENT; v++)
  821. //        for (s=0; s<GroupList[Current_group].num_segments; s++)
  822. //            for (vv=0; vv<MAX_VERTICES_PER_SEGMENT; vv++)
  823. //                if (Segments[GroupList[Current_group].segments[s]].verts[vv] == base_seg->verts[v]) {
  824. //                    editor_status("Error -- unable to move group, it shares a vertex with destination segment.");
  825. //                    return 1;
  826. //                }
  827.  
  828.     for (v=0; v<=Highest_vertex_index; v++) {
  829.         in_vertex_list[v] = 0;
  830.         out_vertex_list[v] = 0;
  831.     }
  832.  
  833.     //    Make a list of all vertices in group.
  834.     for (s=0; s<GroupList[Current_group].num_segments; s++)
  835.         for (v=0; v<MAX_VERTICES_PER_SEGMENT; v++)
  836.             in_vertex_list[Segments[GroupList[Current_group].segments[s]].verts[v]] = 1;
  837.  
  838.     //    For all segments which are not in GroupList[Current_group].segments, mark all their vertices in the out list.
  839.     for (s=0; s<=Highest_segment_index; s++) {
  840.         for (ss=0; ss<GroupList[Current_group].num_segments; ss++)
  841.             if (GroupList[Current_group].segments[ss] == s)
  842.                 break;
  843.         if (ss == GroupList[Current_group].num_segments) 
  844.             for (v=0; v<MAX_VERTICES_PER_SEGMENT; v++)
  845.                 out_vertex_list[Segments[s].verts[v]] = 1;
  846.     }
  847.  
  848.     //    Now, for all vertices present in both the in (part of group segment) and out (part of non-group segment)
  849.     // create an extra copy of the vertex so we can just move the ones in the in list.
  850.     local_hvi = Highest_vertex_index;        //    Can't use Highest_vertex_index as loop termination because it gets increased by med_create_duplicate_vertex.
  851.  
  852.     for (v=0; v<=local_hvi; v++)
  853.         if (in_vertex_list[v])
  854.             if (out_vertex_list[v]) {
  855.                 int new_vertex_id;
  856.  
  857.                 new_vertex_id = med_create_duplicate_vertex(&Vertices[v]);
  858.                 in_vertex_list[v] = 0;
  859.                 in_vertex_list[new_vertex_id] = 1;
  860.  
  861.                 // Create a new vertex and assign all occurrences of vertex v in IN list to new vertex number.
  862.                 for (s=0; s<GroupList[Current_group].num_segments; s++) {
  863.                     segment *sp = &Segments[GroupList[Current_group].segments[s]];
  864.                     for (vv=0; vv<MAX_VERTICES_PER_SEGMENT; vv++)
  865.                         if (sp->verts[vv] == v)
  866.                             sp->verts[vv] = new_vertex_id;
  867.                 }
  868.             }
  869.  
  870.     for (s=0;s<GroupList[Current_group].num_segments;s++) 
  871.         Segments[GroupList[Current_group].segments[s]].group = Current_group;
  872.  
  873.     // Breaking connections between segments in the group and segments not in the group.
  874.     for (s=0; s<GroupList[Current_group].num_segments; s++) 
  875.         {
  876.         segp = &Segments[GroupList[Current_group].segments[s]];
  877.         for (c=0; c<MAX_SIDES_PER_SEGMENT; c++) 
  878.             if (IS_CHILD(segp->children[c]))
  879.                 {
  880.                 csegp = &Segments[segp->children[c]];
  881.                 if (csegp->group != Current_group)
  882.                     {
  883.                     for (d=0; d<MAX_SIDES_PER_SEGMENT; d++)
  884.                         if (IS_CHILD(csegp->children[d]))
  885.                             {
  886.                             dsegp = &Segments[csegp->children[d]];
  887.                             if (dsegp->group == Current_group) 
  888.                                 {
  889.                                 csegp->children[d] = -1;
  890.                                 validate_segment_side(csegp,d);                    // we have converted a connection to a side so validate the segment
  891.                                 }
  892.                             }
  893.                     segp->children[c] = -1;
  894.                     validate_segment_side(segp,c);                    // we have converted a connection to a side so validate the segment
  895.                     }
  896.                 }
  897.         }
  898.  
  899.     copy_uvs_seg_to_seg(&New_segment, Groupsegp[Current_group]);
  900.  
  901.     //    Now do the move
  902.     //    First, xlate all vertices so center of group_seg:group_side is at origin
  903.     compute_center_point_on_side(&srcv,group_seg,group_side);
  904.     for (v=0; v<=Highest_vertex_index; v++)
  905.         if (in_vertex_list[v])
  906.             vm_vec_sub2(&Vertices[v],&srcv);
  907.  
  908.     //    Now, move all object positions.
  909.     for (s=0; s<GroupList[Current_group].num_segments; s++) {
  910.         int    segnum = GroupList[Current_group].segments[s];
  911.         int    objnum = Segments[segnum].objects;
  912.  
  913.         // mprintf((0, "Translating objects in segment #%2i by [%7.3f %7.3f %7.3f]: ", segnum, f2fl(srcv.x), f2fl(srcv.y), f2fl(srcv.z)));
  914.  
  915.         while (objnum != -1) {
  916.             mprintf((0, "%2i ", objnum));
  917.             vm_vec_sub2(&Objects[objnum].pos, &srcv);
  918.             objnum = Objects[objnum].next;
  919.         }
  920.     }
  921.     // mprintf((0, "\n"));
  922.  
  923.     //    Now, rotate segments in group so orientation of group_seg is same as base_seg.
  924.     med_create_group_rotation_matrix(&rotmat, delta_flag, group_seg, group_side, base_seg, base_side, orient_matrix, orientation);
  925.     med_rotate_group(&rotmat, GroupList[Current_group].segments, GroupList[Current_group].num_segments, group_seg, group_side);
  926.  
  927.     //    Now xlate all vertices so group_seg:group_side shares center point with base_seg:base_side
  928.     compute_center_point_on_side(&destv,base_seg,base_side);
  929.     for (v=0; v<=Highest_vertex_index; v++)
  930.         if (in_vertex_list[v])
  931.             vm_vec_add2(&Vertices[v],&destv);
  932.  
  933.     //    Now, rotate all object positions.
  934.     for (s=0; s<GroupList[Current_group].num_segments; s++) {
  935.         int    segnum = GroupList[Current_group].segments[s];
  936.         int    objnum = Segments[segnum].objects;
  937.  
  938.         while (objnum != -1) {
  939.             vm_vec_add2(&Objects[objnum].pos, &destv);
  940.             objnum = Objects[objnum].next;
  941.         }
  942.     }
  943.  
  944.     //    Now, form joint on connecting sides.
  945.     med_form_joint(base_seg,base_side,group_seg,group_side);
  946.  
  947.     validate_selected_segments();
  948.     med_combine_duplicate_vertices(in_vertex_list);
  949.  
  950.     return 0;
  951. }
  952.  
  953.  
  954. //    -----------------------------------------------------------------------------
  955. int place_new_segment_in_world(void)
  956. {
  957.     int    v,segnum;
  958.  
  959.     segnum = get_free_segment_number();
  960.  
  961.     Segments[segnum] = New_segment;
  962.  
  963.     for (v=0; v<MAX_VERTICES_PER_SEGMENT; v++)
  964.         Segments[segnum].verts[v] = med_create_duplicate_vertex(&Vertices[New_segment.verts[v]]);
  965.  
  966.     return segnum;
  967.  
  968. }
  969.  
  970. //    -----------------------------------------------------------------------------
  971. //    Attach segment in the new-fangled way, which is by using the CopyGroup code.
  972. int AttachSegmentNewAng(vms_angvec *pbh)
  973. {
  974.     int            newseg;
  975.     vms_matrix    orient_matrix;
  976.  
  977.     GroupList[Current_group].num_segments = 1;
  978.     newseg = place_new_segment_in_world();
  979.     GroupList[Current_group].segments[0] = newseg;
  980.  
  981.     if (!med_move_group(1, Cursegp, Curside, &Segments[newseg], AttachSide, vm_angles_2_matrix(&orient_matrix,pbh),0)) {
  982.         autosave_mine(mine_filename);
  983.  
  984.         med_propagate_tmaps_to_segments(Cursegp,&Segments[newseg],0);
  985.         med_propagate_tmaps_to_back_side(&Segments[newseg], Side_opposite[AttachSide],0);
  986.         copy_uvs_seg_to_seg(&New_segment,&Segments[newseg]);
  987.  
  988.         Cursegp = &Segments[newseg];
  989.         Curside = Side_opposite[AttachSide];
  990.         med_create_new_segment_from_cursegp();
  991.  
  992.         if (Lock_view_to_cursegp)
  993.             set_view_target_from_segment(Cursegp);
  994.  
  995.         Update_flags |= UF_WORLD_CHANGED;
  996.         mine_changed = 1;
  997.         warn_if_concave_segment(Cursegp);
  998.     }
  999.  
  1000.     return 1;
  1001. }
  1002.  
  1003. int AttachSegmentNew(void)
  1004. {
  1005.     vms_angvec    pbh;
  1006.  
  1007.     pbh.p = 0;
  1008.     pbh.b = 0;
  1009.     pbh.h = 0;
  1010.  
  1011.     AttachSegmentNewAng(&pbh);
  1012.     return 1;
  1013.  
  1014. }
  1015.  
  1016. //    -----------------------------------------------------------------------------
  1017. void save_selected_segs(int *num, short *segs)
  1018. {
  1019.     int    i;
  1020.  
  1021.     for (i=0; i<GroupList[Current_group].num_segments; i++)
  1022.         segs[i] = GroupList[Current_group].segments[i];
  1023.  
  1024.     *num = GroupList[Current_group].num_segments;
  1025. }
  1026.  
  1027. //    -----------------------------------------------------------------------------
  1028. void restore_selected_segs(int num, short *segs)
  1029. {
  1030.     int    i;
  1031.  
  1032.     for (i=0; i<GroupList[Current_group].num_segments; i++)
  1033.         GroupList[Current_group].segments[i] = segs[i];
  1034.  
  1035.     GroupList[Current_group].num_segments = num;
  1036. }
  1037.  
  1038. //    -----------------------------------------------------------------------------
  1039. void validate_selected_segments(void)
  1040. {
  1041.     int    i;
  1042.  
  1043.     for (i=0; i<GroupList[Current_group].num_segments; i++)
  1044.         validate_segment(&Segments[GroupList[Current_group].segments[i]]);
  1045. }
  1046.  
  1047. // =====================================================================================
  1048.  
  1049.  
  1050. //    -----------------------------------------------------------------------------
  1051. void delete_segment_from_group(int segment_num, int group_num)
  1052. {
  1053.     int g, del_seg_index;
  1054.     
  1055.     del_seg_index = -1;
  1056.     for (g=0; g<GroupList[group_num].num_segments; g++)
  1057.         if (segment_num == GroupList[group_num].segments[g]) {  
  1058.             del_seg_index = g;
  1059.             break;
  1060.         }
  1061.  
  1062.     //mprintf((0, "segment_num=%d delseg_index=%d\n", segment_num, del_seg_index)); 
  1063.     
  1064.     if (IS_CHILD(del_seg_index)) {
  1065.         for (g=del_seg_index;g<GroupList[group_num].num_segments-1;g++) { 
  1066.             GroupList[group_num].segments[g] = GroupList[group_num].segments[g+1];
  1067.             }
  1068.         GroupList[group_num].num_segments--;
  1069.         //mprintf((0, "num_segments=%d\n\n", GroupList[group_num].num_segments));
  1070.         Segments[segment_num].group = -1;        
  1071.         }
  1072.  
  1073. }
  1074. // =====================================================================================
  1075.  
  1076.  
  1077. //    -----------------------------------------------------------------------------
  1078. void add_segment_to_group(int segment_num, int group_num)
  1079. {  
  1080.     GroupList[group_num].num_segments++;
  1081.     GroupList[group_num].segments[GroupList[group_num].num_segments-1] = segment_num;
  1082. }
  1083. // =====================================================================================
  1084.  
  1085.  
  1086. //    -----------------------------------------------------------------------------
  1087. int rotate_segment_new(vms_angvec *pbh)
  1088. {
  1089.     int            newseg,baseseg,newseg_side,baseseg_side;
  1090.     vms_matrix    orient_matrix,tm1,tm2;
  1091.     int            n_selected_segs_save;
  1092.     short            selected_segs_save[MAX_SEGMENTS];
  1093.     int            child_save;
  1094.     int            current_group_save;
  1095.  
  1096.     if (!IS_CHILD(Cursegp->children[Side_opposite[Curside]])) {
  1097.         // -- I don't understand this, MK, 01/25/94: if (Cursegp->children[Curside] != group_seg-Segments) {
  1098.             editor_status("Error -- unable to rotate group, Cursegp:Side_opposite[Curside] cannot be free.");
  1099.             return 1;
  1100.     }
  1101.  
  1102.     current_group_save = Current_group;
  1103.     Current_group = ROT_GROUP;
  1104.     Groupsegp[ROT_GROUP] = Cursegp;
  1105.     
  1106.     save_selected_segs(&n_selected_segs_save, selected_segs_save);
  1107.     GroupList[ROT_GROUP].num_segments = 0;
  1108.     newseg = Cursegp - Segments;
  1109.     newseg_side = Side_opposite[Curside];
  1110.  
  1111.     // Create list of segments to rotate.
  1112.     //    Sever connection between first seg to rotate and its connection on Side_opposite[Curside].
  1113.     child_save = Cursegp->children[newseg_side];    // save connection we are about to sever
  1114.     Cursegp->children[newseg_side] = -1;            // sever connection
  1115.     create_group_list(Cursegp, &GroupList[ROT_GROUP].segments, &GroupList[ROT_GROUP].num_segments, Selected_segs, 0);    // create list of segments in group
  1116.     //mprintf((0, "NumSegs = %d\n", GroupList[ROT_GROUP].num_segments));
  1117.     Cursegp->children[newseg_side] = child_save;    // restore severed connection
  1118.     GroupList[ROT_GROUP].segments[0] = newseg;
  1119.  
  1120.     baseseg = Segments[newseg].children[newseg_side];
  1121.     if (!IS_CHILD(baseseg)) {
  1122.         editor_status("Error -- unable to rotate segment, side opposite curside is not attached.");
  1123.         restore_selected_segs(n_selected_segs_save,selected_segs_save);
  1124.         Current_group = current_group_save;
  1125.         return 1;
  1126.     }
  1127.  
  1128.     baseseg_side = find_connect_side(&Segments[newseg], &Segments[baseseg]);
  1129.  
  1130.     med_extract_matrix_from_segment(&Segments[newseg],&tm1);
  1131.     tm1 = vmd_identity_matrix;
  1132.     vm_angles_2_matrix(&tm2,pbh);
  1133.     vm_matrix_x_matrix(&orient_matrix,&tm1,&tm2);
  1134.  
  1135.     Segments[baseseg].children[baseseg_side] = -1;
  1136.     Segments[newseg].children[newseg_side] = -1;
  1137.  
  1138.     if (!med_move_group(1, &Segments[baseseg], baseseg_side, &Segments[newseg], newseg_side, &orient_matrix, 0)) {
  1139.         Cursegp = &Segments[newseg];
  1140.         med_create_new_segment_from_cursegp();
  1141. //        validate_selected_segments();
  1142.         med_propagate_tmaps_to_segments(&Segments[baseseg], &Segments[newseg], 1);
  1143.         med_propagate_tmaps_to_back_side(&Segments[newseg], Curside, 1);
  1144.     }
  1145.  
  1146.     restore_selected_segs(n_selected_segs_save,selected_segs_save);
  1147.     Current_group = current_group_save;
  1148.  
  1149.     return 1;
  1150. }
  1151.  
  1152. //    -----------------------------------------------------------------------------
  1153. //    Attach segment in the new-fangled way, which is by using the CopyGroup code.
  1154. int RotateSegmentNew(vms_angvec *pbh)
  1155. {
  1156.     int    rval;
  1157.  
  1158.     autosave_mine(mine_filename);
  1159.  
  1160.     rval = rotate_segment_new(pbh);
  1161.  
  1162.     if (Lock_view_to_cursegp)
  1163.         set_view_target_from_segment(Cursegp);
  1164.  
  1165.     Update_flags |= UF_WORLD_CHANGED;
  1166.     mine_changed = 1;
  1167.     warn_if_concave_segment(Cursegp);
  1168.  
  1169.     return rval;
  1170. }
  1171.  
  1172. static char     current_tmap_list[MAX_TEXTURES][13];
  1173.  
  1174. // -----------------------------------------------------------------------------
  1175. // Save mine will:
  1176. // 1. Write file info, header info, editor info, vertex data, segment data,
  1177. //    and new_segment in that order, marking their file offset.
  1178. // 2. Go through all the fields and fill in the offset, size, and sizeof
  1179. //    values in the headers.
  1180. int med_save_group( char *filename, short *vertex_ids, short *segment_ids, int num_vertices, int num_segments)
  1181. {
  1182.     FILE * SaveFile;
  1183.     int header_offset, editor_offset, vertex_offset, segment_offset, texture_offset;
  1184.     char ErrorMessage[100];
  1185.     int i, j, k;
  1186.     int segnum;
  1187.     segment tseg;
  1188.    vms_vector tvert;
  1189.     int found;
  1190.  
  1191.     SaveFile = fopen( filename, "wb" );
  1192.     if (!SaveFile)
  1193.     {
  1194.         sprintf( ErrorMessage, "ERROR: Unable to open %s\n", filename );
  1195.         MessageBox( -2, -2, 1, ErrorMessage, "Ok" );
  1196.         return 1;
  1197.     }
  1198.  
  1199.     //===================== SAVE FILE INFO ========================
  1200.  
  1201.     group_fileinfo.fileinfo_version  =   MINE_VERSION;
  1202.     group_fileinfo.fileinfo_sizeof   =   sizeof(group_fileinfo);
  1203.     group_fileinfo.header_offset     =   -1;
  1204.     group_fileinfo.header_size       =   sizeof(group_header);
  1205.     group_fileinfo.editor_offset     =   -1;
  1206.     group_fileinfo.editor_size       =   sizeof(group_editor);
  1207.     group_fileinfo.vertex_offset     =   -1;
  1208.     group_fileinfo.vertex_howmany    =   num_vertices;
  1209.     group_fileinfo.vertex_sizeof     =   sizeof(vms_vector);
  1210.     group_fileinfo.segment_offset    =   -1;
  1211.     group_fileinfo.segment_howmany   =   num_segments;
  1212.     group_fileinfo.segment_sizeof    =   sizeof(segment);
  1213.     group_fileinfo.texture_offset    =   -1;
  1214.     group_fileinfo.texture_howmany   =   0;
  1215.     group_fileinfo.texture_sizeof    =   13;  // num characters in a name
  1216.  
  1217.     // Write the fileinfo
  1218.     fwrite( &group_fileinfo, sizeof(group_fileinfo), 1, SaveFile );
  1219.  
  1220.     //===================== SAVE HEADER INFO ========================
  1221.  
  1222.     group_header.num_vertices        =   num_vertices;
  1223.     group_header.num_segments        =   num_segments;
  1224.  
  1225.     // Write the editor info
  1226.     header_offset = ftell(SaveFile);
  1227.     fwrite( &group_header, sizeof(group_header), 1, SaveFile );
  1228.  
  1229.     //===================== SAVE EDITOR INFO ==========================
  1230.     group_editor.newsegment_offset   =   -1; // To be written
  1231.     group_editor.newsegment_size     =   sizeof(segment);
  1232.     // Next 3 vars added 10/07 by JAS
  1233.     if (Groupsegp[Current_group]) { 
  1234.         segnum = Groupsegp[Current_group]-Segments;         
  1235.         for (i=0;i<num_segments;i++)
  1236.             if (segnum == segment_ids[i])    
  1237.                 group_editor.Groupsegp = i;
  1238.     } 
  1239.     else
  1240.         group_editor.Groupsegp          =   0;
  1241.     group_editor.Groupside           =   Groupside[Current_group];
  1242.  
  1243.     editor_offset = ftell(SaveFile);
  1244.     fwrite( &group_editor, sizeof(group_editor), 1, SaveFile );
  1245.  
  1246.  
  1247.     //===================== SAVE VERTEX INFO ==========================
  1248.  
  1249.     vertex_offset = ftell(SaveFile);
  1250.     for (i=0;i<num_vertices;i++) {
  1251.         tvert = Vertices[vertex_ids[i]];    
  1252.         fwrite( &tvert, sizeof(tvert), 1, SaveFile ); 
  1253.     }
  1254.  
  1255.     //===================== SAVE SEGMENT INFO =========================
  1256.  
  1257.  
  1258.     segment_offset = ftell(SaveFile);
  1259.     for (i=0;i<num_segments;i++) {
  1260.         tseg = Segments[segment_ids[i]];
  1261.         
  1262.         for (j=0;j<6;j++)    {
  1263.             found = 0;
  1264.             for (k=0;k<num_segments;k++) 
  1265.                 if (tseg.children[j] == segment_ids[k]) { 
  1266.                     tseg.children[j] = k;
  1267.                     found = 1;
  1268.                     break;
  1269.                     }    
  1270.             if (found==0) tseg.children[j] = -1;
  1271.         }
  1272.  
  1273.         for (j=0;j<8;j++)
  1274.             for (k=0;k<num_vertices;k++)
  1275.                 if (tseg.verts[j] == vertex_ids[k])    {
  1276.                     tseg.verts[j] = k;
  1277.                     break;
  1278.                     }
  1279.  
  1280.         fwrite( &tseg, sizeof(tseg), 1, SaveFile );
  1281.  
  1282.      }
  1283.  
  1284.     //===================== SAVE TEXTURE INFO ==========================
  1285.  
  1286.     texture_offset = ftell(SaveFile);
  1287.  
  1288.     for (i=0;i<NumTextures;i++)
  1289.         strncpy(current_tmap_list[i], TmapInfo[i].filename, 13);
  1290.  
  1291.     fwrite( current_tmap_list, 13, NumTextures, SaveFile );
  1292.  
  1293.     //============= REWRITE FILE INFO, TO SAVE OFFSETS ===============
  1294.  
  1295.     // Update the offset fields
  1296.     group_fileinfo.header_offset     =   header_offset;
  1297.     group_fileinfo.editor_offset     =   editor_offset;
  1298.     group_fileinfo.vertex_offset     =   vertex_offset;
  1299.     group_fileinfo.segment_offset    =   segment_offset;
  1300.     group_fileinfo.texture_offset    =   texture_offset;
  1301.     
  1302.     // Write the fileinfo
  1303.     fseek(  SaveFile, 0, SEEK_SET );  // Move to TOF
  1304.     fwrite( &group_fileinfo, sizeof(group_fileinfo), 1, SaveFile );
  1305.  
  1306.     //==================== CLOSE THE FILE =============================
  1307.     fclose(SaveFile);
  1308.  
  1309.     return 0;
  1310.  
  1311. }
  1312.  
  1313. static char old_tmap_list[MAX_TEXTURES][13];
  1314. static short tmap_xlate_table[MAX_TEXTURES];
  1315.  
  1316. // -----------------------------------------------------------------------------
  1317. // Load group will:
  1318. //int med_load_group(char * filename)
  1319. int med_load_group( char *filename, short *vertex_ids, short *segment_ids, int *num_vertices, int *num_segments)
  1320. {
  1321.     int segnum, vertnum;
  1322.     char ErrorMessage[200];
  1323.     short tmap_xlate;
  1324.     int     translate;
  1325.     char     *temptr;
  1326.     int i, j; 
  1327.     segment tseg;
  1328.    vms_vector tvert;
  1329.     CFILE * LoadFile;
  1330.  
  1331.     LoadFile = cfopen( filename, CF_READ_MODE );
  1332.     if (!LoadFile)
  1333.     {
  1334.         sprintf( ErrorMessage, "ERROR: Unable to open %s\n", filename );
  1335.         MessageBox( -2, -2, 1, ErrorMessage, "Ok" );
  1336.         return 1;
  1337.     }
  1338.  
  1339.     //===================== READ FILE INFO ========================
  1340.  
  1341.     // These are the default values... version and fileinfo_sizeof
  1342.     // don't have defaults.
  1343.     group_fileinfo.header_offset     =   -1;
  1344.     group_fileinfo.header_size       =   sizeof(group_header);
  1345.     group_fileinfo.editor_offset     =   -1;
  1346.     group_fileinfo.editor_size       =   sizeof(group_editor);
  1347.     group_fileinfo.vertex_offset     =   -1;
  1348.     group_fileinfo.vertex_howmany    =   0;
  1349.     group_fileinfo.vertex_sizeof     =   sizeof(vms_vector);
  1350.     group_fileinfo.segment_offset    =   -1;
  1351.     group_fileinfo.segment_howmany   =   0;
  1352.     group_fileinfo.segment_sizeof    =   sizeof(segment);
  1353.     group_fileinfo.texture_offset    =   -1;
  1354.     group_fileinfo.texture_howmany   =   0;
  1355.     group_fileinfo.texture_sizeof    =   13;  // num characters in a name
  1356.  
  1357.     // Read in group_top_fileinfo to get size of saved fileinfo.
  1358.  
  1359.     if (cfseek( LoadFile, 0, SEEK_SET ))
  1360.         Error( "Error seeking to 0 in group.c" );
  1361.  
  1362.     if (cfread( &group_top_fileinfo, sizeof(group_top_fileinfo),1,LoadFile )!=1)
  1363.         Error( "Error reading top_fileinfo in group.c" );
  1364.  
  1365.     // Check version number
  1366.     if (group_top_fileinfo.fileinfo_version < COMPATIBLE_VERSION )
  1367.     {
  1368.         sprintf( ErrorMessage, "ErrorMessage: You are trying to load %s\n" \
  1369.                           "a version %d group, which is known to be incompatible\n" \
  1370.                           "with the current expected version %d groups.", \
  1371.                           filename, group_top_fileinfo.fileinfo_version, MINE_VERSION );
  1372.  
  1373.         if (MessageBox( -2, -2, 2, ErrorMessage, "Forget it", "Try anyway" )==1)
  1374.         {
  1375.             cfclose( LoadFile );
  1376.             return 1;
  1377.         }
  1378.  
  1379.         MessageBox( -2, -2, 1, "Good luck!", "I need it" );
  1380.     }
  1381.  
  1382.     // Now, Read in the fileinfo
  1383.  
  1384.     if (cfseek( LoadFile, 0, SEEK_SET ))
  1385.         Error( "Error seeking to 0b in group.c" );
  1386.  
  1387.     if (cfread( &group_fileinfo, group_top_fileinfo.fileinfo_sizeof,1,LoadFile )!=1)
  1388.         Error( "Error reading group_fileinfo in group.c" );
  1389.  
  1390.     //===================== READ HEADER INFO ========================
  1391.  
  1392.     // Set default values.
  1393.     group_header.num_vertices        =   0;
  1394.     group_header.num_segments        =   0;
  1395.  
  1396.     if (group_fileinfo.header_offset > -1 )
  1397.     {
  1398.         if (cfseek( LoadFile,group_fileinfo.header_offset, SEEK_SET ))
  1399.             Error( "Error seeking to header_offset in group.c" );
  1400.  
  1401.         if (cfread( &group_header, group_fileinfo.header_size,1,LoadFile )!=1)
  1402.             Error( "Error reading group_header in group.c" );
  1403.     }
  1404.  
  1405.     //===================== READ EDITOR INFO ==========================
  1406.  
  1407.     // Set default values
  1408.     group_editor.current_seg         =   0;
  1409.     group_editor.newsegment_offset   =   -1; // To be written
  1410.     group_editor.newsegment_size     =   sizeof(segment);
  1411.     group_editor.Groupsegp                =   -1;
  1412.     group_editor.Groupside                =   0;
  1413.  
  1414.     if (group_fileinfo.editor_offset > -1 )
  1415.     {
  1416.         if (cfseek( LoadFile,group_fileinfo.editor_offset, SEEK_SET ))
  1417.             Error( "Error seeking to editor_offset in group.c" );
  1418.  
  1419.         if (cfread( &group_editor, group_fileinfo.editor_size,1,LoadFile )!=1)
  1420.             Error( "Error reading group_editor in group.c" );
  1421.  
  1422.     }
  1423.  
  1424.     //===================== READ VERTEX INFO ==========================
  1425.  
  1426.     if ( (group_fileinfo.vertex_offset > -1) && (group_fileinfo.vertex_howmany > 0))
  1427.     {
  1428.         if (cfseek( LoadFile,group_fileinfo.vertex_offset, SEEK_SET ))
  1429.             Error( "Error seeking to vertex_offset in group.c" );
  1430.  
  1431.             for (i=0;i<group_header.num_vertices;i++) {
  1432.  
  1433.                 if (cfread( &tvert, sizeof(tvert),1,LoadFile )!=1)
  1434.                     Error( "Error reading tvert in group.c" );
  1435.                 vertex_ids[i] = med_create_duplicate_vertex( &tvert ); 
  1436.                 //mprintf((0, "vertex %d created from original %d\n", vertex_ids[i], i));
  1437.             }
  1438.  
  1439.         }
  1440.  
  1441.     //==================== READ SEGMENT INFO ===========================
  1442.  
  1443.     if ( (group_fileinfo.segment_offset > -1) && (group_fileinfo.segment_howmany > 0))
  1444.     {
  1445.         if (cfseek( LoadFile,group_fileinfo.segment_offset, SEEK_SET ))
  1446.             Error( "Error seeking to segment_offset in group.c" );
  1447.  
  1448.         for (i=0;i<group_header.num_segments;i++) {
  1449.             if (cfread( &tseg, sizeof(segment),1,LoadFile )!=1)
  1450.                 Error( "Error reading tseg in group.c" );
  1451.                 
  1452.             segment_ids[i] = get_free_segment_number();
  1453.             Segments[segment_ids[i]] = tseg; 
  1454.             Segments[segment_ids[i]].objects = -1;
  1455.  
  1456.             fuelcen_activate( &Segments[segment_ids[i]], Segments[segment_ids[i]].special );
  1457.             }
  1458.  
  1459.         for (i=0;i<group_header.num_segments;i++) {
  1460.             // Fix vertices
  1461.             for (j=0;j<MAX_VERTICES_PER_SEGMENT;j++) {
  1462.                 vertnum = vertex_ids[Segments[segment_ids[i]].verts[j]];
  1463.                 Segments[segment_ids[i]].verts[j] = vertnum;
  1464.                 }
  1465.  
  1466.             // Fix children and walls.
  1467.             for (j=0;j<MAX_SIDES_PER_SEGMENT;j++) {
  1468.                 Segments[segment_ids[i]].sides[j].wall_num = -1;
  1469.                 if (IS_CHILD(Segments[segment_ids[i]].children[j])) {
  1470.                     segnum = segment_ids[Segments[segment_ids[i]].children[j]];
  1471.                     Segments[segment_ids[i]].children[j] = segnum;
  1472.                     } 
  1473.                 //Translate textures.
  1474.                 if (translate == 1) {
  1475.                     int    temp;
  1476.                     tmap_xlate = Segments[segment_ids[i]].sides[j].tmap_num;
  1477.                     Segments[segment_ids[i]].sides[j].tmap_num = tmap_xlate_table[tmap_xlate];
  1478.                     temp = Segments[segment_ids[i]].sides[j].tmap_num2;
  1479.                     tmap_xlate = temp & 0x3fff;            // strip off orientation bits
  1480.                     if (tmap_xlate != 0)
  1481.                         Segments[segment_ids[i]].sides[j].tmap_num2 = temp & (!0x3fff) | tmap_xlate_table[tmap_xlate];    // mask on original orientation bits
  1482.                     }
  1483.                 }
  1484.             }
  1485.     }
  1486.     
  1487.     //===================== READ TEXTURE INFO ==========================
  1488.  
  1489.     if ( (group_fileinfo.texture_offset > -1) && (group_fileinfo.texture_howmany > 0))
  1490.     {
  1491.         if (cfseek( LoadFile, group_fileinfo.texture_offset, SEEK_SET ))
  1492.             Error( "Error seeking to texture_offset in gamemine.c" );
  1493.  
  1494.         for (i=0; i< group_fileinfo.texture_howmany; i++ )
  1495.         {
  1496.             if (cfread( &old_tmap_list[i], group_fileinfo.texture_sizeof, 1, LoadFile )!=1)
  1497.                 Error( "Error reading old_tmap_list[i] in gamemine.c" );
  1498.         }
  1499.     }
  1500.  
  1501.     //=============== GENERATE TEXTURE TRANSLATION TABLE ===============
  1502.  
  1503.     translate = 0;
  1504.     
  1505.     Assert (NumTextures < MAX_TEXTURES);
  1506. {
  1507.     hashtable ht;
  1508.  
  1509.     hashtable_init( &ht, NumTextures );
  1510.  
  1511.     // Remove all the file extensions in the textures list
  1512.  
  1513.     for (i=0;i<NumTextures;i++)    {
  1514.         temptr = strchr(TmapInfo[i].filename, '.');
  1515.         if (temptr) *temptr = '\0';
  1516. //        mprintf( (0, "Texture %d is '%s'\n", i, TmapInfo[i].filename ));
  1517. //        key_getch();
  1518.         hashtable_insert( &ht, TmapInfo[i].filename, i );
  1519.     }
  1520.  
  1521.     // For every texture, search through the texture list
  1522.     // to find a matching name.
  1523.     for (j=0;j<group_fileinfo.texture_howmany;j++)     {
  1524.         // Remove this texture name's extension
  1525.         temptr = strchr(old_tmap_list[j], '.');
  1526.         if (temptr) *temptr = '\0';
  1527.  
  1528.         tmap_xlate_table[j] = hashtable_search( &ht,old_tmap_list[j]);
  1529.         if (tmap_xlate_table[j]    < 0 )
  1530.             tmap_xlate_table[j] = 0;
  1531.         if (tmap_xlate_table[j] != j ) translate = 1;
  1532.     }
  1533.  
  1534.     hashtable_free( &ht );
  1535. }
  1536.  
  1537.  
  1538.     //======================== CLOSE FILE ==============================
  1539.     cfclose( LoadFile );
  1540.  
  1541.     //========================= UPDATE VARIABLES ======================
  1542.  
  1543.     if (group_editor.Groupsegp != -1 ) 
  1544.         Groupsegp[Current_group] = &Segments[segment_ids[group_editor.Groupsegp]];
  1545.     else
  1546.         Groupsegp[Current_group] = NULL;    
  1547.  
  1548.     Groupside[Current_group] = group_editor.Groupside;
  1549.  
  1550.     *num_vertices = group_fileinfo.vertex_howmany;
  1551.     *num_segments = group_fileinfo.segment_howmany;
  1552.     warn_if_concave_segments();
  1553.     
  1554.     return 0;
  1555. }
  1556.  
  1557. char group_filename[128] = "*.GRP";
  1558.  
  1559. void checkforgrpext( char * f )
  1560. {
  1561.     int i;
  1562.  
  1563.     for (i=1; i<strlen(f); i++ )
  1564.     {
  1565.         if (f[i]=='.') return;
  1566.  
  1567.         if ((f[i]==' '||f[i]==0) )
  1568.         {
  1569.             f[i]='.';
  1570.             f[i+1]='G';
  1571.             f[i+2]= 'R';
  1572.             f[i+3]= 'P';
  1573.             f[i+4]=0;
  1574.             return;
  1575.         }
  1576.     }
  1577.  
  1578.     if (i < 123)
  1579.     {
  1580.         f[i]='.';
  1581.         f[i+1]='G';
  1582.         f[i+2]= 'R';
  1583.         f[i+3]= 'P';
  1584.         f[i+4]=0;
  1585.         return;
  1586.     }
  1587. }
  1588.  
  1589. //short vertex_list[MAX_VERTICES];
  1590.  
  1591.  
  1592. int SaveGroup()
  1593. {
  1594.     // Save group
  1595.     int i, s, v;
  1596.     char  ErrorMessage[200];
  1597.     byte    vertex_list[MAX_VERTICES];
  1598.  
  1599.     if (Current_group == -1)
  1600.         {
  1601.         sprintf( ErrorMessage, "ERROR: No current group." );
  1602.         MessageBox( -2, -2, 1, ErrorMessage, "Ok" );
  1603.          return 0;
  1604.         }
  1605.  
  1606.     for (v=0; v<=Highest_vertex_index; v++) {
  1607.         vertex_list[v] = 0;
  1608.     }
  1609.  
  1610.     //    Make a list of all vertices in group.
  1611.     for (s=0; s<GroupList[Current_group].num_segments; s++)
  1612.         for (v=0; v<MAX_VERTICES_PER_SEGMENT; v++) {
  1613.             vertex_list[Segments[GroupList[Current_group].segments[s]].verts[v]] = 1;
  1614.         }    
  1615.  
  1616.     v=0;
  1617.     for (i=0; i<=Highest_vertex_index; i++) 
  1618.         if (vertex_list[i] == 1) { 
  1619.             GroupList[Current_group].vertices[v++] = i;
  1620.         }
  1621.     GroupList[Current_group].num_vertices = v;
  1622.     //mprintf((0, "Saving %d vertices, %d segments\n", GroupList[Current_group].num_vertices, GroupList[Current_group].num_segments));
  1623.     med_save_group("TEMP.GRP", &GroupList[Current_group].vertices, &GroupList[Current_group].segments, 
  1624.         GroupList[Current_group].num_vertices, GroupList[Current_group].num_segments);
  1625.    if (ui_get_filename( group_filename, "*.GRP", "SAVE GROUP" ))
  1626.     {
  1627.       checkforgrpext(group_filename);
  1628.         if (med_save_group(group_filename, &GroupList[Current_group].vertices, &GroupList[Current_group].segments, 
  1629.                     GroupList[Current_group].num_vertices, GroupList[Current_group].num_segments))
  1630.             return 0;
  1631.         mine_changed = 0;
  1632.     }
  1633.     
  1634.     return 1;
  1635. }
  1636.  
  1637.  
  1638. int LoadGroup()
  1639. {
  1640.     int x;
  1641.  
  1642.     if (Num_groups == MAX_GROUPS)
  1643.         {
  1644.         x = MessageBox( -2, -2, 2, "Warning: You are about to wipe out a group.", "ARGH! NO!", "No problemo." );
  1645.         if (x==1) return 0;
  1646.         }
  1647.  
  1648.     if (Num_groups < MAX_GROUPS)
  1649.         {
  1650.         Num_groups++;
  1651.         Current_group = Num_groups-1;
  1652.         }
  1653.          else Current_group = 0;
  1654.  
  1655.    if (ui_get_filename( group_filename, "*.GRP", "LOAD GROUP" ))
  1656.     {
  1657.       checkforgrpext(group_filename);
  1658.       med_load_group(group_filename, &GroupList[Current_group].vertices, &GroupList[Current_group].segments,
  1659.                      &GroupList[Current_group].num_vertices, &GroupList[Current_group].num_segments) ;
  1660.         //mprintf((0, "Loaded %d vertices, %d segments\n", GroupList[Current_group].num_vertices, GroupList[Current_group].num_segments));
  1661.         
  1662.     if (!med_move_group(0, Cursegp, Curside, Groupsegp[Current_group], Groupside[Current_group], &vmd_identity_matrix, 0)) {
  1663.         autosave_mine(mine_filename);
  1664.         set_view_target_from_segment(Cursegp);
  1665.         Update_flags |= UF_WORLD_CHANGED;
  1666.         mine_changed = 1;
  1667.         diagnostic_message("Group moved.");
  1668.         return 0;
  1669.         } else
  1670.     return 1;
  1671.     }    else
  1672.  
  1673.     return 1;
  1674. }
  1675.  
  1676.  
  1677. int UngroupSegment( void )
  1678. {
  1679.     if (Cursegp->group == Current_group) {
  1680.     
  1681.         Cursegp->group = -1;
  1682.         delete_segment_from_group( Cursegp-Segments, Current_group );
  1683.     
  1684.        Update_flags |= UF_WORLD_CHANGED;
  1685.        mine_changed = 1;
  1686.        diagnostic_message("Segment Ungrouped from Group %d.", Current_group);
  1687.     
  1688.         return 1;
  1689.     } else
  1690.     return 0;
  1691. }
  1692.  
  1693. int GroupSegment( void )
  1694. {
  1695.     if (Cursegp->group == -1) {
  1696.  
  1697.         Cursegp->group = Current_group;
  1698.         add_segment_to_group( Cursegp-Segments, Current_group );
  1699.     
  1700.        Update_flags |= UF_WORLD_CHANGED;
  1701.        mine_changed = 1;
  1702.        diagnostic_message("Segment Added to Group %d.", Current_group);
  1703.  
  1704.         return 1;
  1705.     } else
  1706.     return 0;
  1707. }
  1708.  
  1709. int Degroup( void )
  1710. {
  1711.     int i;
  1712.  
  1713. //    GroupList[Current_group].num_segments = 0;
  1714. //    Groupsegp[Current_group] = 0;
  1715.  
  1716.     if (Num_groups==0) return 0;
  1717.  
  1718.     for (i=0; i<GroupList[Current_group].num_segments; i++)  
  1719.         delete_segment_from_group( GroupList[Current_group].segments[i], Current_group );
  1720.  
  1721.       //    delete_segment_from_group( &Segments[GroupList[Current_group].segments[i]]-Segments, Current_group );
  1722.  
  1723.     for (i=Current_group;i<Num_groups-1;i++) 
  1724.         {
  1725.         GroupList[i] = GroupList[i+1];
  1726.         Groupsegp[i] = Groupsegp[i+1];
  1727.         }
  1728.  
  1729.     Num_groups--;
  1730.  
  1731.     GroupList[Num_groups].num_segments = 0;
  1732.     Groupsegp[Num_groups] = 0;
  1733.     
  1734.     if (Current_group > Num_groups-1) Current_group--;
  1735.  
  1736.     if (Num_groups == 0)
  1737.         Current_group = -1;
  1738.  
  1739.    if (Lock_view_to_cursegp)
  1740.        set_view_target_from_segment(Cursegp);
  1741.    Update_flags |= UF_WORLD_CHANGED;
  1742.    mine_changed = 1;
  1743.    diagnostic_message("Group UNgrouped.");
  1744.  
  1745.     return 1;
  1746. }
  1747.  
  1748. void NextGroup( void ) 
  1749. {
  1750.  
  1751.     if (Num_groups > 0)
  1752.         {
  1753.         Current_group++;
  1754.         if (Current_group >= Num_groups ) Current_group = 0;
  1755.         
  1756.         Update_flags |= UF_ED_STATE_CHANGED;
  1757.         mine_changed = 1;
  1758.         }
  1759.     else editor_status("No Next Group\n");
  1760. }
  1761.  
  1762. void PrevGroup( void ) 
  1763. {
  1764.     if (Num_groups > 0)
  1765.         {
  1766.         Current_group--;
  1767.         if (Current_group < 0 ) Current_group = Num_groups-1;
  1768.         
  1769.         Update_flags |= UF_ED_STATE_CHANGED;
  1770.         mine_changed = 1;
  1771.         }
  1772.     else editor_status("No Previous Group\n");
  1773. }
  1774.  
  1775. // Returns:
  1776. //     0 = successfully selected
  1777. //  1 = bad group number
  1778. int select_group( int num )
  1779. {
  1780.     if ((num>=0) && (num<Num_groups))
  1781.         {
  1782.         Current_group = num;
  1783.         return 0;
  1784.         }
  1785.     else return 1;
  1786. }
  1787.  
  1788.  
  1789. //    -----------------------------------------------------------------------------
  1790. int MoveGroup(void)
  1791. {
  1792.     if (!Groupsegp[Current_group]) {
  1793.         editor_status("Error -- Cannot move group, no group segment.");
  1794.         return 1;
  1795.     }
  1796.  
  1797.     med_compress_mine();
  1798.  
  1799.     if (!med_move_group(0, Cursegp, Curside, Groupsegp[Current_group], Groupside[Current_group], &vmd_identity_matrix, 0)) {
  1800.         autosave_mine(mine_filename);
  1801.         Update_flags |= UF_WORLD_CHANGED;
  1802.         mine_changed = 1;
  1803.         diagnostic_message("Group moved.");
  1804.         return 0;
  1805.     } else
  1806.         return 1;
  1807. }                  
  1808.  
  1809.  
  1810. //    -----------------------------------------------------------------------------
  1811. int CopyGroup(void)
  1812. {
  1813.     int    attach_seg;
  1814.  
  1815.     if (!Groupsegp[Current_group]) {
  1816.         editor_status("Error -- Cannot copy group, no group segment.");
  1817.         return 1;
  1818.     }
  1819.  
  1820.     //    See if the attach side in the group is attached to another segment.
  1821.     //    If so, it must not be in the group for group copy to be legal.
  1822.     attach_seg = Groupsegp[Current_group]->children[Groupside[Current_group]];
  1823.     if (attach_seg != -1) {
  1824.         int    i;
  1825.  
  1826.         for (i=0; i<GroupList[Current_group].num_segments; i++)
  1827.             if (GroupList[Current_group].segments[i] == attach_seg)
  1828.                 break;
  1829.  
  1830.         if (i != GroupList[Current_group].num_segments) {
  1831.             editor_status("Error -- Cannot copy group, attach side has a child (segment %i) attached.", Groupsegp[Current_group]->children[Groupside[Current_group]]);
  1832.             return 1;
  1833.         }
  1834.     }
  1835.  
  1836.     med_compress_mine();
  1837.  
  1838.     if (!med_copy_group(0, Cursegp, Curside, Groupsegp[Current_group], Groupside[Current_group], &vmd_identity_matrix)) {
  1839.         autosave_mine(mine_filename);
  1840.         Update_flags |= UF_WORLD_CHANGED;
  1841.         mine_changed = 1;
  1842.         diagnostic_message("Group copied.");
  1843.         return 0;
  1844.     } else      
  1845.         return 1;
  1846. }
  1847.  
  1848.  
  1849. //    -----------------------------------------------------------------------------
  1850. int RotateGroup(void)
  1851. {
  1852.  
  1853.     if (!Groupsegp[Current_group]) {
  1854.         editor_status("Error -- Cannot rotate group, no group segment.");
  1855.         return 1;
  1856.     }
  1857.  
  1858.     Group_orientation[Current_group]++;
  1859.     if ((Group_orientation[Current_group] <0) || (Group_orientation[Current_group] >4)) 
  1860.         Group_orientation[Current_group]=0;
  1861.  
  1862.     med_compress_mine();
  1863.     
  1864.     if (!med_move_group(0, Cursegp, Curside, Groupsegp[Current_group], Groupside[Current_group], 
  1865.                                 &vmd_identity_matrix, Group_orientation[Current_group])) 
  1866.             {
  1867.             Update_flags |= UF_WORLD_CHANGED;
  1868.             mine_changed = 1;
  1869.             diagnostic_message("Group rotated.");
  1870.             return 0;
  1871.             } 
  1872.         else      
  1873.             return 1;
  1874. }
  1875.  
  1876.  
  1877. //    -----------------------------------------------------------------------------
  1878. //    Creates a group from all segments connected to marked segment.
  1879. int SubtractFromGroup(void)
  1880. {
  1881.     int    x, s, original_group;
  1882.     short    *gp;
  1883.     int    cur_num_segs;
  1884.  
  1885.     if (!Markedsegp) {
  1886.         editor_status("Error -- Cannot create group, no marked segment.");
  1887.         return 1;
  1888.     }
  1889.  
  1890.     med_compress_mine();
  1891.     autosave_mine(mine_filename);
  1892.  
  1893.     if (Num_groups == MAX_GROUPS) {
  1894.         x = MessageBox( -2, -2, 2, "Warning: You are about to wipe out a group.", "ARGH! NO!", "No problemo." );
  1895.         if (x==1) return 0;
  1896.     }                       
  1897.  
  1898.     if (Current_group == -1) {
  1899.         editor_status("Error -- No current group.  Cannot subtract.");
  1900.         return 1;
  1901.     }
  1902.  
  1903.     original_group = Current_group;
  1904.  
  1905.     Current_group = (Current_group + 1) % MAX_GROUPS;
  1906.  
  1907. //    if (Num_groups < MAX_GROUPS) {
  1908. //        Current_group = Num_groups;
  1909. //        Num_groups++;
  1910. //    } else
  1911. //        Current_group = 0;
  1912.  
  1913.     // mprintf((0, "Old group: "));
  1914.     // for (s=0; s<GroupList[original_group].num_segments; s++)
  1915.     //     mprintf((0, "%3i ", GroupList[original_group].segments[s]));
  1916.     // mprintf((0, "\n"));
  1917.     
  1918.     //    Create a list of segments to copy.
  1919.     GroupList[Current_group].num_segments = 0;
  1920.     create_group_list(Markedsegp, &GroupList[Current_group].segments, &GroupList[Current_group].num_segments, Selected_segs, N_selected_segs);
  1921.  
  1922.     // mprintf((0, "New group: "));
  1923.     // for (s=0; s<GroupList[Current_group].num_segments; s++)
  1924.     //     mprintf((0, "%3i ", GroupList[Current_group].segments[s]));
  1925.     // mprintf((0, "\n"));
  1926.     
  1927.     //    Now, scan the two groups, forming a group which consists of only those segments common to the two groups.
  1928.     gp = GroupList[Current_group].segments;
  1929.     cur_num_segs = GroupList[Current_group].num_segments;
  1930.     for (s=0; s<cur_num_segs; s++) {
  1931.         short    *gp1 = GroupList[original_group].segments;
  1932.         short    s0 = gp[s];
  1933.         int    s1;
  1934.  
  1935.         for (s1=0; s1<GroupList[original_group].num_segments; s1++)
  1936.             if (gp1[s1] == s0)
  1937.                 break;                // If break executed, then segment found in both lists.
  1938.  
  1939.         //    If current segment was not found in both lists, remove it by copying the last segment over
  1940.         //    it and decreasing the number of segments.
  1941.         if (s1 == GroupList[original_group].num_segments) {
  1942.             gp[s] = gp[cur_num_segs];
  1943.             cur_num_segs--;
  1944.         }
  1945.     }
  1946.  
  1947.     //    Go through mine and seg group number of all segments which are in group
  1948.     //    All segments which were subtracted from group get group set to -1.
  1949.     mprintf((0, "In segments: "));
  1950.     for (s=0; s<cur_num_segs; s++) {
  1951.         Segments[GroupList[Current_group].segments[s]].group = Current_group;
  1952.         mprintf((0, "%2i ", GroupList[Current_group].segments[s]));
  1953.     }
  1954.  
  1955.     mprintf((0, "\nRemoved segments: "));
  1956.     for (s=0; s<=Highest_segment_index; s++) {
  1957.         int    t;
  1958.         if (Segments[s].group == Current_group) {
  1959.             for (t=0; t<cur_num_segs; t++)
  1960.                 if (GroupList[Current_group].segments[t] == s)
  1961.                     break;
  1962.             if (s == cur_num_segs) {
  1963.                 Segments[s].group = -1;
  1964.                 mprintf((0, "%2i ", s));
  1965.             }
  1966.         }
  1967.     }
  1968.  
  1969.     // mprintf((0, "Combined group: "));
  1970.     // for (s=0; s<GroupList[Current_group].num_segments; s++)
  1971.     //     mprintf((0, "%3i ", GroupList[Current_group].segments[s]));
  1972.     // mprintf((0, "\n\n"));
  1973.  
  1974.     GroupList[Current_group].num_segments = cur_num_segs;
  1975.  
  1976.     // Replace Marked segment with Group Segment.
  1977.     Groupsegp[Current_group] = Markedsegp;
  1978.     Groupside[Current_group] = Markedside;
  1979.  
  1980.     for (x=0;x<GroupList[Current_group].num_segments;x++)
  1981.         Segments[GroupList[Current_group].segments[x]].group = Current_group;
  1982.     
  1983.     Update_flags |= UF_WORLD_CHANGED;
  1984.     mine_changed = 1;
  1985.     diagnostic_message("Group created.");
  1986.  
  1987.     return 1; 
  1988.                   
  1989. }
  1990.  
  1991. //    -----------------------------------------------------------------------------
  1992. //    Creates a group from all segments already in CurrentGroup which can be reached from marked segment
  1993. //    without passing through current segment.
  1994. int CreateGroup(void)
  1995. {
  1996.     int x;
  1997.  
  1998.     if (!Markedsegp) {
  1999.         editor_status("Error -- Cannot create group, no marked segment.");
  2000.         return 1;
  2001.     }
  2002.  
  2003.     med_compress_mine();
  2004.     autosave_mine(mine_filename);
  2005.  
  2006.     if (Num_groups == MAX_GROUPS) {
  2007.         x = MessageBox( -2, -2, 2, "Warning: You are about to wipe out a group.", "ARGH! NO!", "No problemo." );
  2008.         if (x==1)
  2009.             return 0;                // Aborting at user's request.
  2010.     }                       
  2011.  
  2012.     if (Num_groups < MAX_GROUPS) {
  2013.         Num_groups++;
  2014.         Current_group = Num_groups-1;
  2015.     } else
  2016.         Current_group = 0;
  2017.  
  2018.     //    Create a list of segments to copy.
  2019.     GroupList[Current_group].num_segments = 0;
  2020.     create_group_list(Markedsegp, &GroupList[Current_group].segments, &GroupList[Current_group].num_segments, Selected_segs, 0);
  2021.     
  2022.     // Replace Marked segment with Group Segment.
  2023.     Groupsegp[Current_group] = Markedsegp;
  2024.     Groupside[Current_group] = Markedside;
  2025. //    Markedsegp = 0;
  2026. //    Markedside = WBACK;
  2027.  
  2028.     for (x=0;x<GroupList[Current_group].num_segments;x++)
  2029.         Segments[GroupList[Current_group].segments[x]].group = Current_group;
  2030.     
  2031.     Update_flags |= UF_WORLD_CHANGED;
  2032.     mine_changed = 1;
  2033.     diagnostic_message("Group created.");
  2034.  
  2035.     return 1; 
  2036.                   
  2037. }
  2038.  
  2039. //    -----------------------------------------------------------------------------
  2040. // Deletes current group.
  2041. int DeleteGroup( void )
  2042. {
  2043.     int i, numsegs;
  2044.  
  2045.     autosave_mine(mine_filename);
  2046.         
  2047.     if (Num_groups==0) return 0;
  2048.  
  2049.     //mprintf((0, "num_segments = %d\n", GroupList[Current_group].num_segments));
  2050.  
  2051.     numsegs = GroupList[Current_group].num_segments;
  2052.     
  2053.     for (i=0; i<numsegs; i++) {
  2054.         med_delete_segment(&Segments[GroupList[Current_group].segments[0]]);
  2055.     }
  2056.  
  2057.     for (i=Current_group;i<Num_groups-1;i++) {
  2058.         GroupList[i] = GroupList[i+1];
  2059.         Groupsegp[i] = Groupsegp[i+1];
  2060.     }
  2061.  
  2062.     Num_groups--;
  2063.     GroupList[Num_groups].num_segments = 0;
  2064.     Groupsegp[Num_groups] = 0;
  2065.  
  2066.     if (Current_group > Num_groups-1) Current_group--;
  2067.  
  2068.     if (Num_groups==0)
  2069.         Current_group = -1;
  2070.  
  2071.     strcpy(undo_status[Autosave_count], "Delete Group UNDONE.");
  2072.    if (Lock_view_to_cursegp)
  2073.        set_view_target_from_segment(Cursegp);
  2074.  
  2075.    Update_flags |= UF_WORLD_CHANGED;
  2076.    mine_changed = 1;
  2077.    diagnostic_message("Group deleted.");
  2078.    // warn_if_concave_segments();     // This could be faster -- just check if deleted segment was concave, warn accordingly
  2079.  
  2080.     return 1;
  2081.  
  2082. }
  2083.  
  2084.  
  2085. int MarkGroupSegment( void )
  2086. {
  2087.     if ((Cursegp->group != -1) && (Cursegp->group == Current_group))
  2088.         {
  2089.        autosave_mine(mine_filename);
  2090.         Groupsegp[Current_group] = Cursegp;
  2091.         Groupside[Current_group] = Curside;
  2092.         editor_status("Group Segment Marked.");
  2093.         Update_flags |= UF_ED_STATE_CHANGED;
  2094.        strcpy(undo_status[Autosave_count], "Mark Group Segment UNDONE.");
  2095.         mine_changed = 1;
  2096.         return 1;
  2097.         }
  2098.     else return 0;
  2099. }
  2100.