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