Subversion Repositories Games.Descent

Rev

Details | Last modification | View Log | RSS feed

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