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

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