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