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