remove duplicate tmap_xlate_table
[btb/d2x.git] / main / editor / group.c
1 /* $Id: group.c,v 1.6 2005-07-22 11:01:35 chris 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.6 2005-07-22 11:01:35 chris 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
1223 // -----------------------------------------------------------------------------
1224 // Load group will:
1225 //int med_load_group(char * filename)
1226 int med_load_group( char *filename, short *vertex_ids, short *segment_ids, int *num_vertices, int *num_segments)
1227 {
1228         int segnum, vertnum;
1229         char ErrorMessage[200];
1230         short tmap_xlate;
1231         int     translate=0;
1232         char    *temptr;
1233         int i, j; 
1234         segment tseg;
1235    vms_vector tvert;
1236         CFILE * LoadFile;
1237
1238         LoadFile = cfopen( filename, CF_READ_MODE );
1239         if (!LoadFile)
1240         {
1241                 sprintf( ErrorMessage, "ERROR: Unable to open %s\n", filename );
1242                 MessageBox( -2, -2, 1, ErrorMessage, "Ok" );
1243                 return 1;
1244         }
1245
1246         //===================== READ FILE INFO ========================
1247
1248         // These are the default values... version and fileinfo_sizeof
1249         // don't have defaults.
1250         group_fileinfo.header_offset     =   -1;
1251         group_fileinfo.header_size       =   sizeof(group_header);
1252         group_fileinfo.editor_offset     =   -1;
1253         group_fileinfo.editor_size       =   sizeof(group_editor);
1254         group_fileinfo.vertex_offset     =   -1;
1255         group_fileinfo.vertex_howmany    =   0;
1256         group_fileinfo.vertex_sizeof     =   sizeof(vms_vector);
1257         group_fileinfo.segment_offset    =   -1;
1258         group_fileinfo.segment_howmany   =   0;
1259         group_fileinfo.segment_sizeof    =   sizeof(segment);
1260         group_fileinfo.texture_offset    =   -1;
1261         group_fileinfo.texture_howmany   =   0;
1262         group_fileinfo.texture_sizeof    =   13;  // num characters in a name
1263
1264         // Read in group_top_fileinfo to get size of saved fileinfo.
1265
1266         if (cfseek( LoadFile, 0, SEEK_SET ))
1267                 Error( "Error seeking to 0 in group.c" );
1268
1269         if (cfread( &group_top_fileinfo, sizeof(group_top_fileinfo),1,LoadFile )!=1)
1270                 Error( "Error reading top_fileinfo in group.c" );
1271
1272         // Check version number
1273         if (group_top_fileinfo.fileinfo_version < COMPATIBLE_VERSION )
1274         {
1275                 sprintf( ErrorMessage, "ErrorMessage: You are trying to load %s\n" \
1276                                                   "a version %d group, which is known to be incompatible\n" \
1277                                                   "with the current expected version %d groups.", \
1278                                                   filename, group_top_fileinfo.fileinfo_version, MINE_VERSION );
1279
1280                 if (MessageBox( -2, -2, 2, ErrorMessage, "Forget it", "Try anyway" )==1)
1281                 {
1282                         cfclose( LoadFile );
1283                         return 1;
1284                 }
1285
1286                 MessageBox( -2, -2, 1, "Good luck!", "I need it" );
1287         }
1288
1289         // Now, Read in the fileinfo
1290
1291         if (cfseek( LoadFile, 0, SEEK_SET ))
1292                 Error( "Error seeking to 0b in group.c" );
1293
1294         if (cfread( &group_fileinfo, group_top_fileinfo.fileinfo_sizeof,1,LoadFile )!=1)
1295                 Error( "Error reading group_fileinfo in group.c" );
1296
1297         //===================== READ HEADER INFO ========================
1298
1299         // Set default values.
1300         group_header.num_vertices        =   0;
1301         group_header.num_segments        =   0;
1302
1303         if (group_fileinfo.header_offset > -1 )
1304         {
1305                 if (cfseek( LoadFile,group_fileinfo.header_offset, SEEK_SET ))
1306                         Error( "Error seeking to header_offset in group.c" );
1307
1308                 if (cfread( &group_header, group_fileinfo.header_size,1,LoadFile )!=1)
1309                         Error( "Error reading group_header in group.c" );
1310         }
1311
1312         //===================== READ EDITOR INFO ==========================
1313
1314         // Set default values
1315         group_editor.current_seg         =   0;
1316         group_editor.newsegment_offset   =   -1; // To be written
1317         group_editor.newsegment_size     =   sizeof(segment);
1318         group_editor.Groupsegp                          =   -1;
1319         group_editor.Groupside                          =   0;
1320
1321         if (group_fileinfo.editor_offset > -1 )
1322         {
1323                 if (cfseek( LoadFile,group_fileinfo.editor_offset, SEEK_SET ))
1324                         Error( "Error seeking to editor_offset in group.c" );
1325
1326                 if (cfread( &group_editor, group_fileinfo.editor_size,1,LoadFile )!=1)
1327                         Error( "Error reading group_editor in group.c" );
1328
1329         }
1330
1331         //===================== READ VERTEX INFO ==========================
1332
1333         if ( (group_fileinfo.vertex_offset > -1) && (group_fileinfo.vertex_howmany > 0))
1334         {
1335                 if (cfseek( LoadFile,group_fileinfo.vertex_offset, SEEK_SET ))
1336                         Error( "Error seeking to vertex_offset in group.c" );
1337
1338                         for (i=0;i<group_header.num_vertices;i++) {
1339
1340                                 if (cfread( &tvert, sizeof(tvert),1,LoadFile )!=1)
1341                                         Error( "Error reading tvert in group.c" );
1342                                 vertex_ids[i] = med_create_duplicate_vertex( &tvert ); 
1343                                 //mprintf((0, "vertex %d created from original %d\n", vertex_ids[i], i));
1344                         }
1345
1346                 }
1347
1348         //==================== READ SEGMENT INFO ===========================
1349
1350         if ( (group_fileinfo.segment_offset > -1) && (group_fileinfo.segment_howmany > 0))
1351         {
1352                 if (cfseek( LoadFile,group_fileinfo.segment_offset, SEEK_SET ))
1353                         Error( "Error seeking to segment_offset in group.c" );
1354
1355                 for (i=0;i<group_header.num_segments;i++) {
1356                         if (cfread( &tseg, sizeof(segment),1,LoadFile )!=1)
1357                                 Error( "Error reading tseg in group.c" );
1358                                 
1359                         segment_ids[i] = get_free_segment_number();
1360                         Segments[segment_ids[i]] = tseg; 
1361                         Segments[segment_ids[i]].objects = -1;
1362
1363                         fuelcen_activate(&Segments[segment_ids[i]], Segment2s[segment_ids[i]].special);
1364                         }
1365
1366                 for (i=0;i<group_header.num_segments;i++) {
1367                         // Fix vertices
1368                         for (j=0;j<MAX_VERTICES_PER_SEGMENT;j++) {
1369                                 vertnum = vertex_ids[Segments[segment_ids[i]].verts[j]];
1370                                 Segments[segment_ids[i]].verts[j] = vertnum;
1371                                 }
1372
1373                         // Fix children and walls.
1374                         for (j=0;j<MAX_SIDES_PER_SEGMENT;j++) {
1375                                 Segments[segment_ids[i]].sides[j].wall_num = -1;
1376                                 if (IS_CHILD(Segments[segment_ids[i]].children[j])) {
1377                                         segnum = segment_ids[Segments[segment_ids[i]].children[j]];
1378                                         Segments[segment_ids[i]].children[j] = segnum;
1379                                         } 
1380                                 //Translate textures.
1381                                 if (translate == 1) {
1382                                         int     temp;
1383                                         tmap_xlate = Segments[segment_ids[i]].sides[j].tmap_num;
1384                                         Segments[segment_ids[i]].sides[j].tmap_num = tmap_xlate_table[tmap_xlate];
1385                                         temp = Segments[segment_ids[i]].sides[j].tmap_num2;
1386                                         tmap_xlate = temp & 0x3fff;                     // strip off orientation bits
1387                                         if (tmap_xlate != 0)
1388                                                 Segments[segment_ids[i]].sides[j].tmap_num2 = (temp & (!0x3fff)) | tmap_xlate_table[tmap_xlate];  // mask on original orientation bits
1389                                         }
1390                                 }
1391                         }
1392         }
1393         
1394         //===================== READ TEXTURE INFO ==========================
1395
1396         if ( (group_fileinfo.texture_offset > -1) && (group_fileinfo.texture_howmany > 0))
1397         {
1398                 if (cfseek( LoadFile, group_fileinfo.texture_offset, SEEK_SET ))
1399                         Error( "Error seeking to texture_offset in gamemine.c" );
1400
1401                 for (i=0; i< group_fileinfo.texture_howmany; i++ )
1402                 {
1403                         if (cfread( &old_tmap_list[i], group_fileinfo.texture_sizeof, 1, LoadFile )!=1)
1404                                 Error( "Error reading old_tmap_list[i] in gamemine.c" );
1405                 }
1406         }
1407
1408         //=============== GENERATE TEXTURE TRANSLATION TABLE ===============
1409
1410         translate = 0;
1411         
1412         Assert (NumTextures < MAX_TEXTURES);
1413 {
1414         hashtable ht;
1415
1416         hashtable_init( &ht, NumTextures );
1417
1418         // Remove all the file extensions in the textures list
1419
1420         for (i=0;i<NumTextures;i++)     {
1421                 temptr = strchr(TmapInfo[i].filename, '.');
1422                 if (temptr) *temptr = '\0';
1423 //              mprintf( (0, "Texture %d is '%s'\n", i, TmapInfo[i].filename ));
1424 //              key_getch();
1425                 hashtable_insert( &ht, TmapInfo[i].filename, i );
1426         }
1427
1428         // For every texture, search through the texture list
1429         // to find a matching name.
1430         for (j=0;j<group_fileinfo.texture_howmany;j++)  {
1431                 // Remove this texture name's extension
1432                 temptr = strchr(old_tmap_list[j], '.');
1433                 if (temptr) *temptr = '\0';
1434
1435                 tmap_xlate_table[j] = hashtable_search( &ht,old_tmap_list[j]);
1436                 if (tmap_xlate_table[j] < 0 )
1437                         tmap_xlate_table[j] = 0;
1438                 if (tmap_xlate_table[j] != j ) translate = 1;
1439         }
1440
1441         hashtable_free( &ht );
1442 }
1443
1444
1445         //======================== CLOSE FILE ==============================
1446         cfclose( LoadFile );
1447
1448         //========================= UPDATE VARIABLES ======================
1449
1450         if (group_editor.Groupsegp != -1 ) 
1451                 Groupsegp[current_group] = &Segments[segment_ids[group_editor.Groupsegp]];
1452         else
1453                 Groupsegp[current_group] = NULL;
1454
1455         Groupside[current_group] = group_editor.Groupside;
1456
1457         *num_vertices = group_fileinfo.vertex_howmany;
1458         *num_segments = group_fileinfo.segment_howmany;
1459         warn_if_concave_segments();
1460         
1461         return 0;
1462 }
1463
1464 char group_filename[PATH_MAX] = "*.GRP";
1465
1466 void checkforgrpext( char * f )
1467 {
1468         int i;
1469
1470         for (i=1; i<strlen(f); i++ )
1471         {
1472                 if (f[i]=='.') return;
1473
1474                 if ((f[i]==' '||f[i]==0) )
1475                 {
1476                         f[i]='.';
1477                         f[i+1]='G';
1478                         f[i+2]= 'R';
1479                         f[i+3]= 'P';
1480                         f[i+4]=0;
1481                         return;
1482                 }
1483         }
1484
1485         if (i < 123)
1486         {
1487                 f[i]='.';
1488                 f[i+1]='G';
1489                 f[i+2]= 'R';
1490                 f[i+3]= 'P';
1491                 f[i+4]=0;
1492                 return;
1493         }
1494 }
1495
1496 //short vertex_list[MAX_VERTICES];
1497
1498
1499 int SaveGroup()
1500 {
1501         // Save group
1502         int i, s, v;
1503         char  ErrorMessage[200];
1504         sbyte   vertex_list[MAX_VERTICES];
1505
1506         if (current_group == -1)
1507                 {
1508                 sprintf( ErrorMessage, "ERROR: No current group." );
1509                 MessageBox( -2, -2, 1, ErrorMessage, "Ok" );
1510                 return 0;
1511                 }
1512
1513         for (v=0; v<=Highest_vertex_index; v++) {
1514                 vertex_list[v] = 0;
1515         }
1516
1517         //      Make a list of all vertices in group.
1518         for (s=0; s<GroupList[current_group].num_segments; s++)
1519                 for (v=0; v<MAX_VERTICES_PER_SEGMENT; v++) {
1520                         vertex_list[Segments[GroupList[current_group].segments[s]].verts[v]] = 1;
1521                 }       
1522
1523         v=0;
1524         for (i=0; i<=Highest_vertex_index; i++) 
1525                 if (vertex_list[i] == 1) { 
1526                         GroupList[current_group].vertices[v++] = i;
1527                 }
1528         GroupList[current_group].num_vertices = v;
1529         //mprintf((0, "Saving %d vertices, %d segments\n", GroupList[current_group].num_vertices, GroupList[current_group].num_segments));
1530         med_save_group("TEMP.GRP", GroupList[current_group].vertices, GroupList[current_group].segments,
1531                 GroupList[current_group].num_vertices, GroupList[current_group].num_segments);
1532    if (ui_get_filename( group_filename, "*.GRP", "SAVE GROUP" ))
1533         {
1534       checkforgrpext(group_filename);
1535                 if (med_save_group(group_filename, GroupList[current_group].vertices, GroupList[current_group].segments,
1536                                         GroupList[current_group].num_vertices, GroupList[current_group].num_segments))
1537                         return 0;
1538                 mine_changed = 0;
1539         }
1540         
1541         return 1;
1542 }
1543
1544
1545 int LoadGroup()
1546 {
1547         int x;
1548
1549         if (num_groups == MAX_GROUPS)
1550                 {
1551                 x = MessageBox( -2, -2, 2, "Warning: You are about to wipe out a group.", "ARGH! NO!", "No problemo." );
1552                 if (x==1) return 0;
1553                 }
1554
1555         if (num_groups < MAX_GROUPS)
1556                 {
1557                 num_groups++;
1558                 current_group = num_groups-1;
1559                 }
1560                 else current_group = 0;
1561
1562    if (ui_get_filename( group_filename, "*.GRP", "LOAD GROUP" ))
1563         {
1564       checkforgrpext(group_filename);
1565       med_load_group(group_filename, GroupList[current_group].vertices, GroupList[current_group].segments,
1566                                          &GroupList[current_group].num_vertices, &GroupList[current_group].num_segments) ;
1567                 //mprintf((0, "Loaded %d vertices, %d segments\n", GroupList[current_group].num_vertices, GroupList[current_group].num_segments));
1568                 
1569         if (!med_move_group(0, Cursegp, Curside, Groupsegp[current_group], Groupside[current_group], &vmd_identity_matrix, 0)) {
1570                 autosave_mine(mine_filename);
1571                 set_view_target_from_segment(Cursegp);
1572                 Update_flags |= UF_WORLD_CHANGED;
1573                 mine_changed = 1;
1574                 diagnostic_message("Group moved.");
1575                 return 0;
1576                 } else
1577         return 1;
1578         }       else
1579
1580         return 1;
1581 }
1582
1583
1584 int UngroupSegment( void )
1585 {
1586         if (Cursegp->group == current_group) {
1587         
1588                 Cursegp->group = -1;
1589                 delete_segment_from_group( Cursegp-Segments, current_group );
1590         
1591            Update_flags |= UF_WORLD_CHANGED;
1592            mine_changed = 1;
1593            diagnostic_message("Segment Ungrouped from Group %d.", current_group);
1594         
1595                 return 1;
1596         } else
1597         return 0;
1598 }
1599
1600 int GroupSegment( void )
1601 {
1602         if (Cursegp->group == -1) {
1603
1604                 Cursegp->group = current_group;
1605                 add_segment_to_group( Cursegp-Segments, current_group );
1606         
1607            Update_flags |= UF_WORLD_CHANGED;
1608            mine_changed = 1;
1609            diagnostic_message("Segment Added to Group %d.", current_group);
1610
1611                 return 1;
1612         } else
1613         return 0;
1614 }
1615
1616 int Degroup( void )
1617 {
1618         int i;
1619
1620 //      GroupList[current_group].num_segments = 0;
1621 //      Groupsegp[current_group] = 0;
1622
1623         if (num_groups==0) return 0;
1624
1625         for (i=0; i<GroupList[current_group].num_segments; i++)
1626                 delete_segment_from_group( GroupList[current_group].segments[i], current_group );
1627
1628           //    delete_segment_from_group( &Segments[GroupList[current_group].segments[i]]-Segments, current_group );
1629
1630         for (i=current_group;i<num_groups-1;i++)
1631                 {
1632                 GroupList[i] = GroupList[i+1];
1633                 Groupsegp[i] = Groupsegp[i+1];
1634                 }
1635
1636         num_groups--;
1637
1638         GroupList[num_groups].num_segments = 0;
1639         Groupsegp[num_groups] = 0;
1640         
1641         if (current_group > num_groups-1) current_group--;
1642
1643         if (num_groups == 0)
1644                 current_group = -1;
1645
1646    if (Lock_view_to_cursegp)
1647        set_view_target_from_segment(Cursegp);
1648    Update_flags |= UF_WORLD_CHANGED;
1649    mine_changed = 1;
1650    diagnostic_message("Group UNgrouped.");
1651
1652         return 1;
1653 }
1654
1655 void NextGroup( void ) 
1656 {
1657
1658         if (num_groups > 0)
1659                 {
1660                 current_group++;
1661                 if (current_group >= num_groups ) current_group = 0;
1662                 
1663                 Update_flags |= UF_ED_STATE_CHANGED;
1664                 mine_changed = 1;
1665                 }
1666         else editor_status("No Next Group\n");
1667 }
1668
1669 void PrevGroup( void ) 
1670 {
1671         if (num_groups > 0)
1672                 {
1673                 current_group--;
1674                 if (current_group < 0 ) current_group = num_groups-1;
1675                 
1676                 Update_flags |= UF_ED_STATE_CHANGED;
1677                 mine_changed = 1;
1678                 }
1679         else editor_status("No Previous Group\n");
1680 }
1681
1682 // Returns:
1683 //       0 = successfully selected
1684 //  1 = bad group number
1685 int select_group( int num )
1686 {
1687         if ((num>=0) && (num<num_groups))
1688                 {
1689                 current_group = num;
1690                 return 0;
1691                 }
1692         else return 1;
1693 }
1694
1695
1696 //      -----------------------------------------------------------------------------
1697 int MoveGroup(void)
1698 {
1699         if (!Groupsegp[current_group]) {
1700                 editor_status("Error -- Cannot move group, no group segment.");
1701                 return 1;
1702         }
1703
1704         med_compress_mine();
1705
1706         if (!med_move_group(0, Cursegp, Curside, Groupsegp[current_group], Groupside[current_group], &vmd_identity_matrix, 0)) {
1707                 autosave_mine(mine_filename);
1708                 Update_flags |= UF_WORLD_CHANGED;
1709                 mine_changed = 1;
1710                 diagnostic_message("Group moved.");
1711                 return 0;
1712         } else
1713                 return 1;
1714 }                                 
1715
1716
1717 //      -----------------------------------------------------------------------------
1718 int CopyGroup(void)
1719 {
1720         int     attach_seg;
1721
1722         if (!Groupsegp[current_group]) {
1723                 editor_status("Error -- Cannot copy group, no group segment.");
1724                 return 1;
1725         }
1726
1727         //      See if the attach side in the group is attached to another segment.
1728         //      If so, it must not be in the group for group copy to be legal.
1729         attach_seg = Groupsegp[current_group]->children[Groupside[current_group]];
1730         if (attach_seg != -1) {
1731                 int     i;
1732
1733                 for (i=0; i<GroupList[current_group].num_segments; i++)
1734                         if (GroupList[current_group].segments[i] == attach_seg)
1735                                 break;
1736
1737                 if (i != GroupList[current_group].num_segments) {
1738                         editor_status("Error -- Cannot copy group, attach side has a child (segment %i) attached.", Groupsegp[current_group]->children[Groupside[current_group]]);
1739                         return 1;
1740                 }
1741         }
1742
1743         med_compress_mine();
1744
1745         if (!med_copy_group(0, Cursegp, Curside, Groupsegp[current_group], Groupside[current_group], &vmd_identity_matrix)) {
1746                 autosave_mine(mine_filename);
1747                 Update_flags |= UF_WORLD_CHANGED;
1748                 mine_changed = 1;
1749                 diagnostic_message("Group copied.");
1750                 return 0;
1751         } else    
1752                 return 1;
1753 }
1754
1755
1756 //      -----------------------------------------------------------------------------
1757 int RotateGroup(void)
1758 {
1759
1760         if (!Groupsegp[current_group]) {
1761                 editor_status("Error -- Cannot rotate group, no group segment.");
1762                 return 1;
1763         }
1764
1765         Group_orientation[current_group]++;
1766         if ((Group_orientation[current_group] <0) || (Group_orientation[current_group] >4))
1767                 Group_orientation[current_group]=0;
1768
1769         med_compress_mine();
1770         
1771         if (!med_move_group(0, Cursegp, Curside, Groupsegp[current_group], Groupside[current_group],
1772                                                                 &vmd_identity_matrix, Group_orientation[current_group]))
1773                         {
1774                         Update_flags |= UF_WORLD_CHANGED;
1775                         mine_changed = 1;
1776                         diagnostic_message("Group rotated.");
1777                         return 0;
1778                         } 
1779                 else      
1780                         return 1;
1781 }
1782
1783
1784 //      -----------------------------------------------------------------------------
1785 //      Creates a group from all segments connected to marked segment.
1786 int SubtractFromGroup(void)
1787 {
1788         int     x, s, original_group;
1789         short   *gp;
1790         int     cur_num_segs;
1791
1792         if (!Markedsegp) {
1793                 editor_status("Error -- Cannot create group, no marked segment.");
1794                 return 1;
1795         }
1796
1797         med_compress_mine();
1798         autosave_mine(mine_filename);
1799
1800         if (num_groups == MAX_GROUPS) {
1801                 x = MessageBox( -2, -2, 2, "Warning: You are about to wipe out a group.", "ARGH! NO!", "No problemo." );
1802                 if (x==1) return 0;
1803         }                                          
1804
1805         if (current_group == -1) {
1806                 editor_status("Error -- No current group.  Cannot subtract.");
1807                 return 1;
1808         }
1809
1810         original_group = current_group;
1811
1812         current_group = (current_group + 1) % MAX_GROUPS;
1813
1814 //      if (num_groups < MAX_GROUPS) {
1815 //              current_group = num_groups;
1816 //              num_groups++;
1817 //      } else
1818 //              current_group = 0;
1819
1820         // mprintf((0, "Old group: "));
1821         // for (s=0; s<GroupList[original_group].num_segments; s++)
1822         //      mprintf((0, "%3i ", GroupList[original_group].segments[s]));
1823         // mprintf((0, "\n"));
1824         
1825         //      Create a list of segments to copy.
1826         GroupList[current_group].num_segments = 0;
1827         create_group_list(Markedsegp, GroupList[current_group].segments, &GroupList[current_group].num_segments, Selected_segs, N_selected_segs);
1828
1829         // mprintf((0, "New group: "));
1830         // for (s=0; s<GroupList[current_group].num_segments; s++)
1831         //      mprintf((0, "%3i ", GroupList[current_group].segments[s]));
1832         // mprintf((0, "\n"));
1833         
1834         //      Now, scan the two groups, forming a group which consists of only those segments common to the two groups.
1835         gp = GroupList[current_group].segments;
1836         cur_num_segs = GroupList[current_group].num_segments;
1837         for (s=0; s<cur_num_segs; s++) {
1838                 short   *gp1 = GroupList[original_group].segments;
1839                 short   s0 = gp[s];
1840                 int     s1;
1841
1842                 for (s1=0; s1<GroupList[original_group].num_segments; s1++)
1843                         if (gp1[s1] == s0)
1844                                 break;                          // If break executed, then segment found in both lists.
1845
1846                 //      If current segment was not found in both lists, remove it by copying the last segment over
1847                 //      it and decreasing the number of segments.
1848                 if (s1 == GroupList[original_group].num_segments) {
1849                         gp[s] = gp[cur_num_segs];
1850                         cur_num_segs--;
1851                 }
1852         }
1853
1854         //      Go through mine and seg group number of all segments which are in group
1855         //      All segments which were subtracted from group get group set to -1.
1856         mprintf((0, "In segments: "));
1857         for (s=0; s<cur_num_segs; s++) {
1858                 Segments[GroupList[current_group].segments[s]].group = current_group;
1859                 mprintf((0, "%2i ", GroupList[current_group].segments[s]));
1860         }
1861
1862         mprintf((0, "\nRemoved segments: "));
1863         for (s=0; s<=Highest_segment_index; s++) {
1864                 int     t;
1865                 if (Segments[s].group == current_group) {
1866                         for (t=0; t<cur_num_segs; t++)
1867                                 if (GroupList[current_group].segments[t] == s)
1868                                         break;
1869                         if (s == cur_num_segs) {
1870                                 Segments[s].group = -1;
1871                                 mprintf((0, "%2i ", s));
1872                         }
1873                 }
1874         }
1875
1876         // mprintf((0, "Combined group: "));
1877         // for (s=0; s<GroupList[current_group].num_segments; s++)
1878         //      mprintf((0, "%3i ", GroupList[current_group].segments[s]));
1879         // mprintf((0, "\n\n"));
1880
1881         GroupList[current_group].num_segments = cur_num_segs;
1882
1883         // Replace Marked segment with Group Segment.
1884         Groupsegp[current_group] = Markedsegp;
1885         Groupside[current_group] = Markedside;
1886
1887         for (x=0;x<GroupList[current_group].num_segments;x++)
1888                 Segments[GroupList[current_group].segments[x]].group = current_group;
1889         
1890         Update_flags |= UF_WORLD_CHANGED;
1891         mine_changed = 1;
1892         diagnostic_message("Group created.");
1893
1894         return 1; 
1895                                   
1896 }
1897
1898 //      -----------------------------------------------------------------------------
1899 //      Creates a group from all segments already in CurrentGroup which can be reached from marked segment
1900 //      without passing through current segment.
1901 int CreateGroup(void)
1902 {
1903         int x;
1904
1905         if (!Markedsegp) {
1906                 editor_status("Error -- Cannot create group, no marked segment.");
1907                 return 1;
1908         }
1909
1910         med_compress_mine();
1911         autosave_mine(mine_filename);
1912
1913         if (num_groups == MAX_GROUPS) {
1914                 x = MessageBox( -2, -2, 2, "Warning: You are about to wipe out a group.", "ARGH! NO!", "No problemo." );
1915                 if (x==1)
1916                         return 0;                               // Aborting at user's request.
1917         }                                          
1918
1919         if (num_groups < MAX_GROUPS) {
1920                 num_groups++;
1921                 current_group = num_groups-1;
1922         } else
1923                 current_group = 0;
1924
1925         //      Create a list of segments to copy.
1926         GroupList[current_group].num_segments = 0;
1927         create_group_list(Markedsegp, GroupList[current_group].segments, &GroupList[current_group].num_segments, Selected_segs, 0);
1928         
1929         // Replace Marked segment with Group Segment.
1930         Groupsegp[current_group] = Markedsegp;
1931         Groupside[current_group] = Markedside;
1932 //      Markedsegp = 0;
1933 //      Markedside = WBACK;
1934
1935         for (x=0;x<GroupList[current_group].num_segments;x++)
1936                 Segments[GroupList[current_group].segments[x]].group = current_group;
1937         
1938         Update_flags |= UF_WORLD_CHANGED;
1939         mine_changed = 1;
1940         diagnostic_message("Group created.");
1941
1942         return 1; 
1943                                   
1944 }
1945
1946 //      -----------------------------------------------------------------------------
1947 // Deletes current group.
1948 int DeleteGroup( void )
1949 {
1950         int i, numsegs;
1951
1952         autosave_mine(mine_filename);
1953                 
1954         if (num_groups==0) return 0;
1955
1956         //mprintf((0, "num_segments = %d\n", GroupList[current_group].num_segments));
1957
1958         numsegs = GroupList[current_group].num_segments;
1959         
1960         for (i=0; i<numsegs; i++) {
1961                 med_delete_segment(&Segments[GroupList[current_group].segments[0]]);
1962         }
1963
1964         for (i=current_group;i<num_groups-1;i++) {
1965                 GroupList[i] = GroupList[i+1];
1966                 Groupsegp[i] = Groupsegp[i+1];
1967         }
1968
1969         num_groups--;
1970         GroupList[num_groups].num_segments = 0;
1971         Groupsegp[num_groups] = 0;
1972
1973         if (current_group > num_groups-1) current_group--;
1974
1975         if (num_groups==0)
1976                 current_group = -1;
1977
1978         strcpy(undo_status[Autosave_count], "Delete Group UNDONE.");
1979    if (Lock_view_to_cursegp)
1980        set_view_target_from_segment(Cursegp);
1981
1982    Update_flags |= UF_WORLD_CHANGED;
1983    mine_changed = 1;
1984    diagnostic_message("Group deleted.");
1985    // warn_if_concave_segments();     // This could be faster -- just check if deleted segment was concave, warn accordingly
1986
1987         return 1;
1988
1989 }
1990
1991
1992 int MarkGroupSegment( void )
1993 {
1994         if ((Cursegp->group != -1) && (Cursegp->group == current_group))
1995                 {
1996            autosave_mine(mine_filename);
1997                 Groupsegp[current_group] = Cursegp;
1998                 Groupside[current_group] = Curside;
1999                 editor_status("Group Segment Marked.");
2000                 Update_flags |= UF_ED_STATE_CHANGED;
2001            strcpy(undo_status[Autosave_count], "Mark Group Segment UNDONE.");
2002                 mine_changed = 1;
2003                 return 1;
2004                 }
2005         else return 0;
2006 }