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