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