/*
 
 * Portions of this file are copyright Rebirth contributors and licensed as
 
 * described in COPYING.txt.
 
 * Portions of this file are copyright Parallax Software and licensed
 
 * according to the Parallax license below.
 
 * See COPYING.txt for license details.
 
 
 
THE COMPUTER CODE CONTAINED HEREIN IS THE SOLE PROPERTY OF PARALLAX
 
SOFTWARE CORPORATION ("PARALLAX").  PARALLAX, IN DISTRIBUTING THE CODE TO
 
END-USERS, AND SUBJECT TO ALL OF THE TERMS AND CONDITIONS HEREIN, GRANTS A
 
ROYALTY-FREE, PERPETUAL LICENSE TO SUCH END-USERS FOR USE BY SUCH END-USERS
 
IN USING, DISPLAYING,  AND CREATING DERIVATIVE WORKS THEREOF, SO LONG AS
 
SUCH USE, DISPLAY OR CREATION IS FOR NON-COMMERCIAL, ROYALTY OR REVENUE
 
FREE PURPOSES.  IN NO EVENT SHALL THE END-USER USE THE COMPUTER CODE
 
CONTAINED HEREIN FOR REVENUE-BEARING PURPOSES.  THE END-USER UNDERSTANDS
 
AND AGREES TO THE TERMS HEREIN AND ACCEPTS THE SAME BY USE OF THIS FILE.
 
COPYRIGHT 1993-1998 PARALLAX SOFTWARE CORPORATION.  ALL RIGHTS RESERVED.
 
*/
 
 
 
/*
 
 *
 
 * group functions
 
 *
 
 */
 
 
 
#include <stdio.h>
 
#include <string.h>
 
 
 
#include "gr.h"
 
#include "ui.h"
 
#include "inferno.h"
 
#include "segment.h"
 
#include "editor/editor.h"
 
#include "editor/esegment.h"
 
#include "editor/medmisc.h"
 
#include "dxxerror.h"
 
#include "gamemine.h"
 
#include "physfsx.h"
 
#include "gameseg.h"
 
#include "bm.h"                         // For MAX_TEXTURES.
 
#include "textures.h"
 
#include "hash.h"
 
#include "fuelcen.h"
 
#include "kdefs.h"
 
#include "fwd-wall.h"
 
#include "medwall.h"
 
#include "dxxsconf.h"
 
#include "compiler-range_for.h"
 
#include "d_enumerate.h"
 
#include "d_range.h"
 
#include "partial_range.h"
 
#include "segiter.h"
 
 
 
static void validate_selected_segments(void);
 
 
 
struct group_top_fileinfo {
 
        int     fileinfo_version;
 
        int     fileinfo_sizeof;
 
} group_top_fileinfo;    // Should be same as first two fields below...
 
 
 
struct group_fileinfo {
 
        int     fileinfo_version;
 
        int     fileinfo_sizeof;
 
        int     header_offset;                  // Stuff common to game & editor
 
        int     header_size;
 
        int     editor_offset;                          // Editor specific stuff
 
        int     editor_size;
 
        int     vertex_offset;
 
        int     vertex_howmany;
 
        int     vertex_sizeof;
 
        int     segment_offset;
 
        int     segment_howmany;
 
        int     segment_sizeof;
 
        int     texture_offset;
 
        uint32_t texture_howmany;
 
        int     texture_sizeof;
 
} group_fileinfo;
 
 
 
struct group_header {
 
        int     num_vertices;
 
        int     num_segments;
 
} group_header;
 
 
 
struct group_editor {
 
        int     current_seg;
 
        int     newsegment_offset;
 
        int     newsegment_size;
 
        int     Groupsegp;
 
        int     Groupside;
 
} group_editor;
 
 
 
std::array<group, MAX_GROUPS+1> GroupList;
 
std::array<segment *, MAX_GROUPS+1> Groupsegp;
 
std::array<int, MAX_GROUPS+1> Groupside;
 
std::array<int, MAX_GROUPS+1> Group_orientation;
 
int             current_group=-1;
 
unsigned num_groups;
 
 
 
// -- void swap_negate_columns(vms_matrix *rotmat, int col1, int col2)
 
// -- {
 
// --   fix     col1_1,col1_2,col1_3;
 
// --   fix     col2_1,col2_2,col2_3;
 
// -- 
 
// --   switch (col1) {
 
// --           case 0:
 
// --                   col1_1 = rotmat->m1;
 
// --                   col1_2 = rotmat->m2;
 
// --                   col1_3 = rotmat->m3;
 
// --                   break;
 
// -- 
 
// --           case 1:
 
// --                   col1_1 = rotmat->m4;
 
// --                   col1_2 = rotmat->m5;
 
// --                   col1_3 = rotmat->m6;
 
// --                   break;
 
// -- 
 
// --           case 2:
 
// --                   col1_1 = rotmat->m7;
 
// --                   col1_2 = rotmat->m8;
 
// --                   col1_3 = rotmat->m9;
 
// --                   break;
 
// --   }
 
// -- 
 
// --   switch (col2) {
 
// --           case 0:
 
// --                   col2_1 = rotmat->m1;
 
// --                   col2_2 = rotmat->m2;
 
// --                   col2_3 = rotmat->m3;
 
// --                   break;
 
// -- 
 
// --           case 1:
 
// --                   col2_1 = rotmat->m4;
 
// --                   col2_2 = rotmat->m5;
 
// --                   col2_3 = rotmat->m6;
 
// --                   break;
 
// -- 
 
// --           case 2:
 
// --                   col2_1 = rotmat->m7;
 
// --                   col2_2 = rotmat->m8;
 
// --                   col2_3 = rotmat->m9;
 
// --                   break;
 
// --   }
 
// -- 
 
// --   switch (col2) {
 
// --           case 0:
 
// --                   rotmat->m1 = -col1_1;
 
// --                   rotmat->m2 = -col1_2;
 
// --                   rotmat->m3 = -col1_3;
 
// --                   break;
 
// -- 
 
// --           case 1:
 
// --                   rotmat->m4 = -col1_1;
 
// --                   rotmat->m5 = -col1_2;
 
// --                   rotmat->m6 = -col1_3;
 
// --                   break;
 
// -- 
 
// --           case 2:
 
// --                   rotmat->m7 = -col1_1;
 
// --                   rotmat->m8 = -col1_2;
 
// --                   rotmat->m9 = -col1_3;
 
// --                   break;
 
// --   }
 
// -- 
 
// --   switch (col1) {
 
// --           case 0:
 
// --                   rotmat->m1 = -col2_1;
 
// --                   rotmat->m2 = -col2_2;
 
// --                   rotmat->m3 = -col2_3;
 
// --                   break;
 
// -- 
 
// --           case 1:
 
// --                   rotmat->m4 = -col2_1;
 
// --                   rotmat->m5 = -col2_2;
 
// --                   rotmat->m6 = -col2_3;
 
// --                   break;
 
// -- 
 
// --           case 2:
 
// --                   rotmat->m7 = -col2_1;
 
// --                   rotmat->m8 = -col2_2;
 
// --                   rotmat->m9 = -col2_3;
 
// --                   break;
 
// --   }
 
// -- 
 
// -- }
 
// -- 
 
// -- void swap_negate_rows(vms_matrix *rotmat, int row1, int row2)
 
// -- {
 
// --   fix     row1_1,row1_2,row1_3;
 
// --   fix     row2_1,row2_2,row2_3;
 
// -- 
 
// --   switch (row1) {
 
// --           case 0:
 
// --                   row1_1 = rotmat->m1;
 
// --                   row1_2 = rotmat->m4;
 
// --                   row1_3 = rotmat->m7;
 
// --                   break;
 
// -- 
 
// --           case 1:
 
// --                   row1_1 = rotmat->m2;
 
// --                   row1_2 = rotmat->m5;
 
// --                   row1_3 = rotmat->m8;
 
// --                   break;
 
// -- 
 
// --           case 2:
 
// --                   row1_1 = rotmat->m3;
 
// --                   row1_2 = rotmat->m6;
 
// --                   row1_3 = rotmat->m9;
 
// --                   break;
 
// --   }
 
// -- 
 
// --   switch (row2) {
 
// --           case 0:
 
// --                   row2_1 = rotmat->m1;
 
// --                   row2_2 = rotmat->m4;
 
// --                   row2_3 = rotmat->m7;
 
// --                   break;
 
// -- 
 
// --           case 1:
 
// --                   row2_1 = rotmat->m2;
 
// --                   row2_2 = rotmat->m5;
 
// --                   row2_3 = rotmat->m8;
 
// --                   break;
 
// -- 
 
// --           case 2:
 
// --                   row2_1 = rotmat->m3;
 
// --                   row2_2 = rotmat->m6;
 
// --                   row2_3 = rotmat->m9;
 
// --                   break;
 
// --   }
 
// -- 
 
// --   switch (row2) {
 
// --           case 0:
 
// --                   rotmat->m1 = -row1_1;
 
// --                   rotmat->m4 = -row1_2;
 
// --                   rotmat->m7 = -row1_3;
 
// --                   break;
 
// -- 
 
// --           case 1:
 
// --                   rotmat->m2 = -row1_1;
 
// --                   rotmat->m5 = -row1_2;
 
// --                   rotmat->m8 = -row1_3;
 
// --                   break;
 
// -- 
 
// --           case 2:
 
// --                   rotmat->m3 = -row1_1;
 
// --                   rotmat->m6 = -row1_2;
 
// --                   rotmat->m9 = -row1_3;
 
// --                   break;
 
// --   }
 
// -- 
 
// --   switch (row1) {
 
// --           case 0:
 
// --                   rotmat->m1 = -row2_1;
 
// --                   rotmat->m4 = -row2_2;
 
// --                   rotmat->m7 = -row2_3;
 
// --                   break;
 
// -- 
 
// --           case 1:
 
// --                   rotmat->m2 = -row2_1;
 
// --                   rotmat->m5 = -row2_2;
 
// --                   rotmat->m8 = -row2_3;
 
// --                   break;
 
// -- 
 
// --           case 2:
 
// --                   rotmat->m3 = -row2_1;
 
// --                   rotmat->m6 = -row2_2;
 
// --                   rotmat->m9 = -row2_3;
 
// --                   break;
 
// --   }
 
// -- 
 
// -- }
 
// -- 
 
// -- // ------------------------------------------------------------------------------------------------
 
// -- void      side_based_matrix(vms_matrix *rotmat,int destside)
 
// -- {
 
// --   vms_angvec      rotvec;
 
// --   vms_matrix      r1,rtemp;
 
// -- 
 
// --   switch (destside) {
 
// --           case WLEFT:
 
// -- //                        swap_negate_columns(rotmat,1,2);
 
// -- //                        swap_negate_rows(rotmat,1,2);
 
// --                   break;
 
// -- 
 
// --           case WTOP:
 
// --                   break;
 
// -- 
 
// --           case WRIGHT:
 
// -- //                        swap_negate_columns(rotmat,1,2);
 
// -- //                        swap_negate_rows(rotmat,1,2);
 
// --                   break;
 
// -- 
 
// --           case WBOTTOM:
 
// --                   break;
 
// -- 
 
// --           case WFRONT:
 
// --                   break;
 
// -- 
 
// --           case WBACK:
 
// --                   break;
 
// --   }
 
// -- 
 
// -- }
 
 
 
 
 
// ------------------------------------------------------------------------------------------------
 
//      Rotate a group about a point.
 
//      The segments in the group are indicated (by segment number) in group_seglist.  There are group_size segments.
 
//      The point about which the groups is rotated is the center of first_seg:first_side.
 
//      delta_flag:
 
//              0       absolute rotation, destination specified in terms of base_seg:base_side, used in moving or copying a group
 
//              1       relative rotation, destination specified relative to current orientation of first_seg:first_side
 
//      Note: The group must exist in the mine, consisting of actual points in the world.  If any points in the
 
//                      segments in the group are shared by segments not in the group, those points will get rotated and the
 
//                      segments not in the group will have their shapes modified.
 
//      Return value:
 
//              0       group rotated
 
//              1       unable to rotate group
 
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)
 
{
 
        vms_matrix      rotmat2,rotmat,rotmat3,rotmat4;
 
        vms_angvec      pbh = {0,0,0};
 
 
 
        //      Determine whether this rotation is a delta rotation, meaning to just rotate in place, or an absolute rotation,
 
        //      which means that the destination rotation is specified, not as a delta, but as an absolute
 
        if (delta_flag) {
 
                //      Create rotation matrix describing rotation.
 
                med_extract_matrix_from_segment(first_seg, rotmat4);            // get rotation matrix describing current orientation of first seg
 
                update_matrix_based_on_side(rotmat4, first_side);
 
                rotmat3 = vm_transposed_matrix(orient_matrix);
 
                const auto vm_desired_orientation = vm_matrix_x_matrix(rotmat4,rotmat3);                        // this is the desired orientation of the new segment
 
                vm_transpose_matrix(rotmat4);
 
                vm_matrix_x_matrix(rotmat2,vm_desired_orientation,rotmat4);                     // this is the desired orientation of the new segment
 
        } else {
 
                //      Create rotation matrix describing rotation.
 
 
 
                med_extract_matrix_from_segment(base_seg, rotmat);              // get rotation matrix describing desired orientation
 
                update_matrix_based_on_side(rotmat, base_side);                         // modify rotation matrix for desired side
 
 
 
                //      If the new segment is to be attached without rotation, then its orientation is the same as the base_segment
 
                vm_matrix_x_matrix(rotmat4,rotmat,orient_matrix);                       // this is the desired orientation of the new segment
 
 
 
                pbh.b = orientation*16384;
 
                vm_angles_2_matrix(rotmat3,pbh);
 
                rotmat4 = rotmat = vm_matrix_x_matrix(rotmat4, rotmat3);
 
                med_extract_matrix_from_segment(first_seg, rotmat3);            // get rotation matrix describing current orientation of first seg
 
 
 
                // It is curious that the following statement has no analogue in the med_attach_segment_rotated code.
 
                //      Perhaps it is because segments are always attached at their front side.  If the back side is the side
 
                //      passed to the function, then the matrix is not modified, which might suggest that what you need to do below
 
                //      is use Side_opposite[first_side].
 
                update_matrix_based_on_side(rotmat3, Side_opposite[first_side]);                                // modify rotation matrix for desired side
 
 
 
                vm_transpose_matrix(rotmat3);                                                           // get the inverse of the current orientation matrix
 
                rotmat2 = vm_transposed_matrix(vm_matrix_x_matrix(rotmat,rotmat3));                     // now rotmat2 takes the current segment to the desired orientation
 
        }
 
        result_mat = rotmat2;
 
}
 
 
 
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)
 
{
 
        vms_matrix result_mat;
 
        return med_create_group_rotation_matrix(result_mat, delta_flag, first_seg, first_side, base_seg, base_side, orient_matrix, orientation), result_mat;
 
}
 
 
 
// -----------------------------------------------------------------------------------------
 
// Rotate all vertices and objects in group.
 
static void med_rotate_group(const vms_matrix &rotmat, group::segment_array_type_t &group_seglist, const vcsegptr_t first_seg, int first_side)
 
{
 
        auto &LevelSharedVertexState = LevelSharedSegmentState.get_vertex_state();
 
        auto &Objects = LevelUniqueObjectState.Objects;
 
        auto &Vertices = LevelSharedVertexState.get_vertices();
 
        auto &vmobjptridx = Objects.vmptridx;
 
        std::array<int8_t, MAX_VERTICES> vertex_list;
 
        auto &vcvertptr = Vertices.vcptr;
 
        auto &vmvertptridx = Vertices.vmptridx;
 
        const auto &&rotate_center = compute_center_point_on_side(vcvertptr, first_seg, first_side);
 
 
 
        //      Create list of points to rotate.
 
        vertex_list = {};
 
 
 
        range_for (const auto &gs, group_seglist)
 
        {
 
                auto &sp = *vmsegptr(gs);
 
 
 
                range_for (const auto v, sp.verts)
 
                        vertex_list[v] = 1;
 
 
 
                //      Rotate center of all objects in group.
 
                range_for (const auto objp, objects_in(sp, vmobjptridx, vcsegptr))
 
                {
 
                        const auto tv1 = vm_vec_sub(objp->pos,rotate_center);
 
                        const auto tv = vm_vec_rotate(tv1,rotmat);
 
                        vm_vec_add(objp->pos, tv, rotate_center);
 
                }                       
 
        }
 
 
 
        // Do the pre-rotation xlate, do the rotation, do the post-rotation xlate
 
        range_for (auto &&v, vmvertptridx)
 
                if (vertex_list[v]) {
 
                        const auto &&tv1 = vm_vec_sub(*v, rotate_center);
 
                        const auto tv = vm_vec_rotate(tv1,rotmat);
 
                        vm_vec_add(*v, tv, rotate_center);
 
                }
 
}
 
 
 
// ------------------------------------------------------------------------------------------------
 
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)
 
{
 
        if (ignore_list)
 
                if (ignore_list->contains(segp))
 
                        return;
 
 
 
        if (!visited[segp]) {
 
                visited[segp] = true;
 
                seglistp.emplace_back(segp);
 
 
 
                range_for (const auto c, segp->children)
 
                        if (IS_CHILD(c))
 
                                cgl_aux(segp.absolute_sibling(c), seglistp, ignore_list, visited);
 
        }
 
}
 
 
 
// ------------------------------------------------------------------------------------------------
 
//      Sets Been_visited[n] if n is reachable from segp
 
static void create_group_list(const vmsegptridx_t segp, group::segment_array_type_t &seglistp, selected_segment_array_t *ignore_list)
 
{
 
        visited_segment_bitarray_t visited;
 
        cgl_aux(segp, seglistp, ignore_list, visited);
 
}
 
 
 
 
 
#define MXS MAX_SEGMENTS
 
#define MXV MAX_VERTICES
 
 
 
// ------------------------------------------------------------------------------------------------
 
static void duplicate_group(std::array<uint8_t, MAX_VERTICES> &vertex_ids, group::segment_array_type_t &segments)
 
{
 
        auto &LevelSharedVertexState = LevelSharedSegmentState.get_vertex_state();
 
        auto &Objects = LevelUniqueObjectState.Objects;
 
        auto &Vertices = LevelSharedVertexState.get_vertices();
 
        auto &vmobjptridx = Objects.vmptridx;
 
        group::segment_array_type_t new_segments;
 
        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]
 
 
 
        //      duplicate vertices
 
        new_vertex_ids.fill(-1);
 
 
 
        //      duplicate vertices
 
        auto &vcvertptridx = Vertices.vcptridx;
 
        range_for (auto &&v, vcvertptridx)
 
        {
 
                if (vertex_ids[v])
 
                {
 
                        new_vertex_ids[v] = med_create_duplicate_vertex(*v);
 
                }
 
        }
 
 
 
        //      duplicate segments
 
        range_for(const auto &gs, segments)
 
        {
 
                const auto &&segp = vmsegptr(gs);
 
                const auto &&new_segment_id = med_create_duplicate_segment(Segments, segp);
 
                new_segments.emplace_back(new_segment_id);
 
                range_for (const auto objp, objects_in(segp, vmobjptridx, vmsegptr))
 
                {
 
                        if (objp->type != OBJ_PLAYER) {
 
                                const auto &&new_obj_id = obj_create_copy(objp, vmsegptridx(new_segment_id));
 
                                (void)new_obj_id; // FIXME!
 
                        }
 
                }
 
        }
 
 
 
        //      Now, for each segment in segment_ids, correct its children numbers by translating through new_segment_ids
 
        //      and correct its vertex numbers by translating through new_vertex_ids
 
        range_for(const auto &gs, new_segments)
 
        {
 
                auto &sp = *vmsegptr(gs);
 
                range_for (auto &seg, sp.children)
 
                {
 
                        if (IS_CHILD(seg)) {
 
                                group::segment_array_type_t::iterator inew = new_segments.begin();
 
                                range_for (const auto i, segments)
 
                                {
 
                                        if (seg == i)
 
                                        {
 
                                                seg = *inew;
 
                                                break;
 
                                        }
 
                                        ++inew;
 
                                }
 
                        }
 
                }       // end for (sidenum=0...
 
 
 
                //      Now fixup vertex ids
 
                range_for (auto &v, sp.verts)
 
                {
 
                        if (vertex_ids[v]) {
 
                                v = new_vertex_ids[v];
 
                        }
 
                }
 
        }       // end for (s=0...
 
 
 
        //      Now, copy new_segment_ids into segment_ids
 
        segments = new_segments;
 
 
 
        //      Now, copy new_vertex_ids into vertex_ids
 
        vertex_ids = {};
 
 
 
        range_for (auto &v, new_vertex_ids)
 
                if (v != -1)
 
                        vertex_ids[v] = 1;
 
}
 
 
 
 
 
// ------------------------------------------------------------------------------------------------
 
static int in_group(segnum_t segnum, int group_num)
 
{
 
        range_for(const auto& s, GroupList[group_num].segments)
 
                if (segnum == s)
 
                        return 1;
 
 
 
        return 0;
 
}
 
 
 
// ------------------------------------------------------------------------------------------------
 
//      Copy a group of segments.
 
//      The group is defined as all segments accessible from group_seg.
 
//      The group is copied so group_seg:group_side is incident upon base_seg:base_side.
 
//      group_seg and its vertices are bashed to coincide with base_seg.
 
//      If any vertex of base_seg is contained in a segment that is reachable from group_seg, then errror.
 
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)
 
{
 
        auto &LevelSharedVertexState = LevelSharedSegmentState.get_vertex_state();
 
        auto &Objects = LevelUniqueObjectState.Objects;
 
        auto &Vertices = LevelSharedVertexState.get_vertices();
 
        auto &vmobjptridx = Objects.vmptridx;
 
        int                     x;
 
        int                     new_current_group;
 
 
 
        if (IS_CHILD(base_seg->children[base_side])) {
 
                editor_status("Error -- unable to copy group, base_seg:base_side must be free.");
 
                return 1;
 
        }
 
 
 
        if (num_groups == MAX_GROUPS) {
 
                x = ui_messagebox( -2, -2, 2, "Warning: You have reached the MAXIMUM group number limit. Continue?", "No", "Yes" );
 
                if (x==1)
 
                        return 0;
 
        }
 
 
 
        if (num_groups < MAX_GROUPS) {
 
                num_groups++;
 
                new_current_group = num_groups-1;
 
        } else
 
                new_current_group = 0;
 
 
 
        Assert(current_group >= 0);
 
 
 
        // Find groupsegp index
 
        auto gb = GroupList[current_group].segments.begin();
 
        auto ge = GroupList[current_group].segments.end();
 
        auto gp = Groupsegp[current_group];
 
        auto gi = std::find_if(gb, ge, [gp](const segnum_t segnum){ return vcsegptr(segnum) == gp; });
 
        int gs_index = (gi == ge) ? 0 : std::distance(gb, gi);
 
 
 
        GroupList[new_current_group] = GroupList[current_group];
 
 
 
        //      Make a list of all vertices in group.
 
        std::array<uint8_t, MAX_VERTICES> in_vertex_list{};
 
        if (group_seg == &New_segment)
 
                range_for (auto &v, group_seg->verts)
 
                        in_vertex_list[v] = 1;
 
        else {
 
                range_for(const auto &gs, GroupList[new_current_group].segments)
 
                        range_for (auto &v, vmsegptr(gs)->verts)
 
                                in_vertex_list[v] = 1;
 
        }
 
 
 
        // Given a list of vertex indices (indicated by !0 in in_vertex_list) and segment indices (in list GroupList[current_group].segments, there
 
        //      are GroupList[current_group].num_segments segments), copy all segments and vertices
 
        //      Return updated lists of vertices and segments in in_vertex_list and GroupList[current_group].segments
 
        duplicate_group(in_vertex_list, GroupList[new_current_group].segments);
 
 
 
        //group_seg = &Segments[GroupList[new_current_group].segments[0]];                                      // connecting segment in group has been changed, so update group_seg
 
 
 
        {
 
                const auto &&gs = vmsegptr(GroupList[new_current_group].segments[gs_index]);
 
                group_seg = gs;
 
                Groupsegp[new_current_group] = gs;
 
        }
 
        Groupside[new_current_group] = Groupside[current_group];
 
 
 
        range_for(const auto &gs, GroupList[new_current_group].segments)
 
        {
 
                auto &s = *vmsegptr(gs);
 
                s.group = new_current_group;
 
                s.special = SEGMENT_IS_NOTHING;
 
                s.matcen_num = -1;
 
        }
 
 
 
        auto &vcvertptr = Vertices.vcptr;
 
        // Breaking connections between segments in the current group and segments not in the group.
 
        range_for(const auto &gs, GroupList[new_current_group].segments)
 
        {
 
                const auto &&segp = base_seg.absolute_sibling(gs);
 
                range_for (const auto &&es, enumerate(segp->children))
 
                        if (IS_CHILD(es.value))
 
                        {
 
                                if (!in_group(es.value, new_current_group))
 
                                {
 
                                        es.value = segment_none;
 
                                        validate_segment_side(vcvertptr, segp, es.idx);                                 // we have converted a connection to a side so validate the segment
 
                                }
 
                        }
 
        }
 
 
 
        copy_uvs_seg_to_seg(vmsegptr(&New_segment), group_seg);
 
        
 
        //      Now do the copy
 
        //      First, xlate all vertices so center of group_seg:group_side is at origin
 
        const auto &&srcv = compute_center_point_on_side(vcvertptr, group_seg, group_side);
 
        auto &vmvertptridx = Vertices.vmptridx;
 
        range_for (auto &&v, vmvertptridx)
 
                if (in_vertex_list[v])
 
                        vm_vec_sub2(*v, srcv);
 
 
 
        //      Now, translate all object positions.
 
        range_for(const auto &segnum, GroupList[new_current_group].segments)
 
        {
 
                range_for (const auto objp, objects_in(vmsegptr(segnum), vmobjptridx, vmsegptr))
 
                        vm_vec_sub2(objp->pos, srcv);
 
        }
 
 
 
        //      Now, rotate segments in group so orientation of group_seg is same as base_seg.
 
        const auto rotmat = med_create_group_rotation_matrix(delta_flag, group_seg, group_side, base_seg, base_side, orient_matrix, 0);
 
        med_rotate_group(rotmat, GroupList[new_current_group].segments, group_seg, group_side);
 
 
 
        //      Now xlate all vertices so group_seg:group_side shares center point with base_seg:base_side
 
        const auto &&destv = compute_center_point_on_side(vcvertptr, base_seg, base_side);
 
        range_for (auto &&v, vmvertptridx)
 
                if (in_vertex_list[v])
 
                        vm_vec_add2(*v, destv);
 
 
 
        //      Now, xlate all object positions.
 
        range_for(const auto &segnum, GroupList[new_current_group].segments)
 
        {
 
                range_for (const auto objp, objects_in(vmsegptr(segnum), vmobjptridx, vmsegptr))
 
                        vm_vec_add2(objp->pos, destv);
 
        }
 
 
 
        //      Now, copy all walls (ie, doors, illusionary, etc.) into the new group.
 
        copy_group_walls(current_group, new_current_group);
 
 
 
        current_group = new_current_group;
 
 
 
        //      Now, form joint on connecting sides.
 
        med_form_joint(base_seg,base_side,vmsegptridx(Groupsegp[current_group]),Groupside[new_current_group]);
 
 
 
        validate_selected_segments();
 
        med_combine_duplicate_vertices(in_vertex_list);
 
 
 
        return 0;
 
}
 
 
 
 
 
// ------------------------------------------------------------------------------------------------
 
//      Move a group of segments.
 
//      The group is defined as all segments accessible from group_seg.
 
//      The group is moved so group_seg:group_side is incident upon base_seg:base_side.
 
//      group_seg and its vertices are bashed to coincide with base_seg.
 
//      If any vertex of base_seg is contained in a segment that is reachable from group_seg, then errror.
 
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)
 
{
 
        auto &LevelSharedVertexState = LevelSharedSegmentState.get_vertex_state();
 
        auto &Objects = LevelUniqueObjectState.Objects;
 
        auto &Vertices = LevelSharedVertexState.get_vertices();
 
        auto &vmobjptridx = Objects.vmptridx;
 
        if (IS_CHILD(base_seg->children[base_side]))
 
                if (base_seg->children[base_side] != group_seg) {
 
                        editor_status("Error -- unable to move group, base_seg:base_side must be free or point to group_seg.");
 
                        return 1;
 
        }
 
 
 
//      // See if any vertices in base_seg are contained in any vertex in group_list
 
//      for (v=0; v<MAX_VERTICES_PER_SEGMENT; v++)
 
//              for (s=0; s<GroupList[current_group].num_segments; s++)
 
//                      for (vv=0; vv<MAX_VERTICES_PER_SEGMENT; vv++)
 
//                              if (Segments[GroupList[current_group].segments[s]].verts[vv] == base_seg->verts[v]) {
 
//                                      editor_status("Error -- unable to move group, it shares a vertex with destination segment.");
 
//                                      return 1;
 
//                              }
 
 
 
        std::array<uint8_t, MAX_VERTICES> in_vertex_list{};
 
        std::array<int8_t, MAX_VERTICES> out_vertex_list{};
 
 
 
        //      Make a list of all vertices in group.
 
        range_for(const auto &gs, GroupList[current_group].segments)
 
                range_for (auto &v, vmsegptr(gs)->verts)
 
                        in_vertex_list[v] = 1;
 
 
 
        //      For all segments which are not in GroupList[current_group].segments, mark all their vertices in the out list.
 
        range_for (const auto &&segp, vmsegptridx)
 
        {
 
                if (!GroupList[current_group].segments.contains(segp))
 
                        {
 
                                range_for (auto &v, segp->verts)
 
                                        out_vertex_list[v] = 1;
 
                        }
 
        }
 
 
 
        //      Now, for all vertices present in both the in (part of group segment) and out (part of non-group segment)
 
        // create an extra copy of the vertex so we can just move the ones in the in list.
 
        //      Can't use Highest_vertex_index as loop termination because it gets increased by med_create_duplicate_vertex.
 
 
 
        auto &vcvertptr = Vertices.vcptr;
 
        auto &vmvertptridx = Vertices.vmptridx;
 
        range_for (auto &&v, vmvertptridx)
 
                if (in_vertex_list[v])
 
                        if (out_vertex_list[v]) {
 
                                const auto new_vertex_id = med_create_duplicate_vertex(*v);
 
                                in_vertex_list[v] = 0;
 
                                in_vertex_list[new_vertex_id] = 1;
 
 
 
                                // Create a new vertex and assign all occurrences of vertex v in IN list to new vertex number.
 
                                range_for(const auto &gs, GroupList[current_group].segments)
 
                                {
 
                                        auto &sp = *vmsegptr(gs);
 
                                        range_for (auto &vv, sp.verts)
 
                                                if (vv == v)
 
                                                        vv = new_vertex_id;
 
                                }
 
                        }
 
 
 
        range_for(const auto &gs, GroupList[current_group].segments)
 
                vmsegptr(gs)->group = current_group;
 
 
 
        // Breaking connections between segments in the group and segments not in the group.
 
        range_for(const auto &gs, GroupList[current_group].segments)
 
                {
 
                const auto &&segp = base_seg.absolute_sibling(gs);
 
                range_for (const auto &&es0, enumerate(segp->children))
 
                        if (IS_CHILD(es0.value))
 
                                {
 
                                const auto &&csegp = base_seg.absolute_sibling(es0.value);
 
                                if (csegp->group != current_group)
 
                                        {
 
                                        range_for (const auto &&es1, enumerate(csegp->children))
 
                                                if (IS_CHILD(es1.value))
 
                                                        {
 
                                                        auto &dsegp = *vmsegptr(es1.value);
 
                                                        if (dsegp.group == current_group)
 
                                                                {
 
                                                                es1.value = segment_none;
 
                                                                validate_segment_side(vcvertptr, csegp, es1.idx);                                       // we have converted a connection to a side so validate the segment
 
                                                                }
 
                                                        }
 
                                        es0.value = segment_none;
 
                                        validate_segment_side(vcvertptr, segp, es0.idx);                                        // we have converted a connection to a side so validate the segment
 
                                        }
 
                                }
 
                }
 
 
 
        copy_uvs_seg_to_seg(vmsegptr(&New_segment), vcsegptr(Groupsegp[current_group]));
 
 
 
        //      Now do the move
 
        //      First, xlate all vertices so center of group_seg:group_side is at origin
 
        const auto &&srcv = compute_center_point_on_side(vcvertptr, group_seg, group_side);
 
        range_for (auto &&v, vmvertptridx)
 
                if (in_vertex_list[v])
 
                        vm_vec_sub2(*v, srcv);
 
 
 
        //      Now, move all object positions.
 
        range_for(const auto &segnum, GroupList[current_group].segments)
 
        {
 
                range_for (const auto objp, objects_in(vmsegptr(segnum), vmobjptridx, vmsegptr))
 
                        vm_vec_sub2(objp->pos, srcv);
 
        }
 
 
 
        //      Now, rotate segments in group so orientation of group_seg is same as base_seg.
 
        const auto rotmat = med_create_group_rotation_matrix(delta_flag, group_seg, group_side, base_seg, base_side, orient_matrix, orientation);
 
        med_rotate_group(rotmat, GroupList[current_group].segments, group_seg, group_side);
 
 
 
        //      Now xlate all vertices so group_seg:group_side shares center point with base_seg:base_side
 
        const auto &&destv = compute_center_point_on_side(vcvertptr, base_seg, base_side);
 
        range_for (auto &&v, vmvertptridx)
 
                if (in_vertex_list[v])
 
                        vm_vec_add2(*v, destv);
 
 
 
        //      Now, rotate all object positions.
 
        range_for(const auto &segnum, GroupList[current_group].segments)
 
        {
 
                range_for (const auto objp, objects_in(vmsegptr(segnum), vmobjptridx, vmsegptr))
 
                        vm_vec_add2(objp->pos, destv);
 
        }
 
 
 
        //      Now, form joint on connecting sides.
 
        med_form_joint(base_seg,base_side,group_seg,group_side);
 
 
 
        validate_selected_segments();
 
        med_combine_duplicate_vertices(in_vertex_list);
 
 
 
        return 0;
 
}
 
 
 
 
 
//      -----------------------------------------------------------------------------
 
static segnum_t place_new_segment_in_world(void)
 
{
 
        auto &LevelSharedVertexState = LevelSharedSegmentState.get_vertex_state();
 
        auto &Vertices = LevelSharedVertexState.get_vertices();
 
        const auto &&segnum = Segments.vmptridx(get_free_segment_number(Segments));
 
        auto &seg = *segnum;
 
        seg = New_segment;
 
 
 
        auto &vcvertptr = Vertices.vcptr;
 
        range_for (const unsigned v, xrange(MAX_VERTICES_PER_SEGMENT))
 
                seg.verts[v] = med_create_duplicate_vertex(vcvertptr(New_segment.verts[v]));
 
 
 
        return segnum;
 
 
 
}
 
 
 
//      -----------------------------------------------------------------------------
 
//      Attach segment in the new-fangled way, which is by using the CopyGroup code.
 
static int AttachSegmentNewAng(const vms_angvec &pbh)
 
{
 
        auto &LevelSharedVertexState = LevelSharedSegmentState.get_vertex_state();
 
        auto &Vertices = LevelSharedVertexState.get_vertices();
 
        GroupList[current_group].segments.clear();
 
        const auto newseg = place_new_segment_in_world();
 
        GroupList[current_group].segments.emplace_back(newseg);
 
 
 
        const auto &&nsegp = vmsegptridx(newseg);
 
        if (!med_move_group(1, Cursegp, Curside, nsegp, AttachSide, vm_angles_2_matrix(pbh),0))
 
        {
 
                autosave_mine(mine_filename);
 
 
 
                med_propagate_tmaps_to_segments(Cursegp,nsegp,0);
 
                med_propagate_tmaps_to_back_side(nsegp, Side_opposite[AttachSide],0);
 
                copy_uvs_seg_to_seg(vmsegptr(&New_segment),nsegp);
 
 
 
                Cursegp = nsegp;
 
                Curside = Side_opposite[AttachSide];
 
                med_create_new_segment_from_cursegp();
 
 
 
                if (Lock_view_to_cursegp)
 
                {
 
                        auto &vcvertptr = Vertices.vcptr;
 
                        set_view_target_from_segment(vcvertptr, Cursegp);
 
                }
 
 
 
                Update_flags |= UF_WORLD_CHANGED;
 
                mine_changed = 1;
 
                warn_if_concave_segment(Cursegp);
 
        }
 
 
 
        return 1;
 
}
 
 
 
int AttachSegmentNew(void)
 
{
 
        vms_angvec      pbh;
 
 
 
        pbh.p = 0;
 
        pbh.b = 0;
 
        pbh.h = 0;
 
 
 
        AttachSegmentNewAng(pbh);
 
        return 1;
 
 
 
}
 
 
 
//      -----------------------------------------------------------------------------
 
void validate_selected_segments(void)
 
{
 
        auto &LevelSharedVertexState = LevelSharedSegmentState.get_vertex_state();
 
        auto &Vertices = LevelSharedVertexState.get_vertices();
 
        auto &vcvertptr = Vertices.vcptr;
 
        range_for (const auto &gs, GroupList[current_group].segments)
 
                validate_segment(vcvertptr, vmsegptridx(gs));
 
}
 
 
 
// =====================================================================================
 
 
 
 
 
//      -----------------------------------------------------------------------------
 
namespace dsx {
 
void delete_segment_from_group(const vmsegptridx_t segment_num, unsigned group_num)
 
{
 
        segment_num->group = -1;
 
        GroupList[group_num].segments.erase(segment_num);
 
}
 
}
 
// =====================================================================================
 
 
 
 
 
//      -----------------------------------------------------------------------------
 
void add_segment_to_group(segnum_t segment_num, int group_num)
 
{  
 
        GroupList[group_num].segments.emplace_back(segment_num);
 
}
 
// =====================================================================================
 
 
 
 
 
//      -----------------------------------------------------------------------------
 
int rotate_segment_new(const vms_angvec &pbh)
 
{
 
        int                     newseg_side;
 
        vms_matrix      tm1;
 
        group::segment_array_type_t selected_segs_save;
 
        int                     child_save;
 
        int                     current_group_save;
 
 
 
        if (!IS_CHILD(Cursegp->children[static_cast<int>(Side_opposite[Curside])]))
 
                // -- I don't understand this, MK, 01/25/94: if (Cursegp->children[Curside] != group_seg-Segments)
 
                {
 
                        editor_status("Error -- unable to rotate group, Cursegp:Side_opposite[Curside] cannot be free.");
 
                        return 1;
 
        }
 
 
 
        current_group_save = current_group;
 
        current_group = ROT_GROUP;
 
        Groupsegp[ROT_GROUP] = Cursegp;
 
        
 
        selected_segs_save = GroupList[current_group].segments;
 
        GroupList[ROT_GROUP].segments.clear();
 
        const auto newseg = Cursegp;
 
        newseg_side = Side_opposite[Curside];
 
 
 
        // Create list of segments to rotate.
 
        //      Sever connection between first seg to rotate and its connection on Side_opposite[Curside].
 
        child_save = Cursegp->children[newseg_side];    // save connection we are about to sever
 
        Cursegp->children[newseg_side] = segment_none;                  // sever connection
 
        create_group_list(Cursegp, GroupList[ROT_GROUP].segments, NULL);       // create list of segments in group
 
        Cursegp->children[newseg_side] = child_save;    // restore severed connection
 
        GroupList[ROT_GROUP].segments.emplace_back(newseg);
 
 
 
        const auto baseseg = newseg->children[newseg_side];
 
        if (!IS_CHILD(baseseg)) {
 
                editor_status("Error -- unable to rotate segment, side opposite curside is not attached.");
 
                GroupList[current_group].segments = selected_segs_save;
 
                current_group = current_group_save;
 
                return 1;
 
        }
 
        const auto &&basesegp = vmsegptridx(baseseg);
 
        const auto &&baseseg_side = find_connect_side(newseg, basesegp);
 
 
 
        med_extract_matrix_from_segment(newseg, tm1);
 
        tm1 = vmd_identity_matrix;
 
        const auto tm2 = vm_angles_2_matrix(pbh);
 
        const auto orient_matrix = vm_matrix_x_matrix(tm1,tm2);
 
 
 
        basesegp->children[baseseg_side] = segment_none;
 
        newseg->children[newseg_side] = segment_none;
 
 
 
        if (!med_move_group(1, basesegp, baseseg_side, newseg, newseg_side, orient_matrix, 0))
 
        {
 
                Cursegp = newseg;
 
                med_create_new_segment_from_cursegp();
 
//              validate_selected_segments();
 
                med_propagate_tmaps_to_segments(basesegp, newseg, 1);
 
                med_propagate_tmaps_to_back_side(newseg, Curside, 1);
 
        }
 
 
 
        GroupList[current_group].segments = selected_segs_save;
 
        current_group = current_group_save;
 
 
 
        return 1;
 
}
 
 
 
//      -----------------------------------------------------------------------------
 
//      Attach segment in the new-fangled way, which is by using the CopyGroup code.
 
int RotateSegmentNew(vms_angvec *pbh)
 
{
 
        auto &LevelSharedVertexState = LevelSharedSegmentState.get_vertex_state();
 
        auto &Vertices = LevelSharedVertexState.get_vertices();
 
        int     rval;
 
 
 
        autosave_mine(mine_filename);
 
 
 
        rval = rotate_segment_new(*pbh);
 
 
 
        if (Lock_view_to_cursegp)
 
        {
 
                auto &vcvertptr = Vertices.vcptr;
 
                set_view_target_from_segment(vcvertptr, Cursegp);
 
        }
 
 
 
        Update_flags |= UF_WORLD_CHANGED;
 
        mine_changed = 1;
 
        warn_if_concave_segment(Cursegp);
 
 
 
        return rval;
 
}
 
 
 
#if 0
 
static std::array<d_fname, MAX_TEXTURES> current_tmap_list;
 
 
 
// -----------------------------------------------------------------------------
 
// Save mine will:
 
// 1. Write file info, header info, editor info, vertex data, segment data,
 
//    and new_segment in that order, marking their file offset.
 
// 2. Go through all the fields and fill in the offset, size, and sizeof
 
//    values in the headers.
 
static int med_save_group( const char *filename, const group::vertex_array_type_t &vertex_ids, const group::segment_array_type_t &segment_ids)
 
{
 
        int header_offset, editor_offset, vertex_offset, segment_offset, texture_offset;
 
        char ErrorMessage[100];
 
        int j;
 
 
 
        auto SaveFile = PHYSFSX_openWriteBuffered(filename);
 
        if (!SaveFile)
 
        {
 
                snprintf(ErrorMessage, sizeof(ErrorMessage), "ERROR: Unable to open %s\n", filename);
 
                ui_messagebox( -2, -2, 1, ErrorMessage, "Ok" );
 
                return 1;
 
        }
 
 
 
        //===================== SAVE FILE INFO ========================
 
 
 
        group_fileinfo.fileinfo_version  =   MINE_VERSION;
 
        group_fileinfo.fileinfo_sizeof   =   sizeof(group_fileinfo);
 
        group_fileinfo.header_offset     =   -1;
 
        group_fileinfo.header_size       =   sizeof(group_header);
 
        group_fileinfo.editor_offset     =   -1;
 
        group_fileinfo.editor_size       =   sizeof(group_editor);
 
        group_fileinfo.vertex_offset     =   -1;
 
        group_fileinfo.vertex_howmany    =   vertex_ids.size();
 
        group_fileinfo.vertex_sizeof     =   sizeof(vms_vector);
 
        group_fileinfo.segment_offset    =   -1;
 
        group_fileinfo.segment_howmany   =   segment_ids.size();
 
        group_fileinfo.segment_sizeof    =   sizeof(segment);
 
        group_fileinfo.texture_offset    =   -1;
 
        group_fileinfo.texture_howmany   =   0;
 
        group_fileinfo.texture_sizeof    =   13;  // num characters in a name
 
 
 
        // Write the fileinfo
 
        PHYSFS_write( SaveFile, &group_fileinfo, sizeof(group_fileinfo), 1);
 
 
 
        //===================== SAVE HEADER INFO ========================
 
 
 
        group_header.num_vertices        =   vertex_ids.size();
 
        group_header.num_segments        =   segment_ids.size();
 
 
 
        // Write the editor info
 
        header_offset = PHYSFS_tell(SaveFile);
 
        PHYSFS_write( SaveFile, &group_header, sizeof(group_header), 1);
 
 
 
        //===================== SAVE EDITOR INFO ==========================
 
        group_editor.newsegment_offset   =   -1; // To be written
 
        group_editor.newsegment_size     =   sizeof(segment);
 
        // Next 3 vars added 10/07 by JAS
 
        group_editor.Groupsegp =   0;
 
        if (Groupsegp[current_group]) {
 
                const auto i = segment_ids.find(vmsegptridx(Groupsegp[current_group]));
 
                if (i != segment_ids.end())
 
                        group_editor.Groupsegp = std::distance(segment_ids.begin(), i);
 
        } 
 
        group_editor.Groupside           =   Groupside[current_group];
 
 
 
        editor_offset = PHYSFS_tell(SaveFile);
 
        PHYSFS_write( SaveFile, &group_editor, sizeof(group_editor), 1);
 
 
 
 
 
        //===================== SAVE VERTEX INFO ==========================
 
 
 
        vertex_offset = PHYSFS_tell(SaveFile);
 
        range_for (const auto &gv, vertex_ids)
 
        {
 
                const vertex tvert = *vcvertptr(gv);
 
                PHYSFS_write(SaveFile, &tvert, sizeof(tvert), 1);
 
        }
 
 
 
        //===================== SAVE SEGMENT INFO =========================
 
 
 
 
 
        segment_offset = PHYSFS_tell(SaveFile);
 
        range_for (const auto &gs, segment_ids)
 
        {
 
                auto &&tseg = *vmsegptr(gs);
 
                
 
                for (j=0;j<6;j++)       {
 
                        group::segment_array_type_t::const_iterator i = segment_ids.find(tseg.children[j]);
 
                        tseg.children[j] = (i == segment_ids.end()) ? segment_none : std::distance(segment_ids.begin(), i);
 
                }
 
 
 
                for (j=0;j<8;j++)
 
                {
 
                        group::vertex_array_type_t::const_iterator i = vertex_ids.find(tseg.verts[j]);
 
                        if (i != vertex_ids.end())
 
                                tseg.verts[j] = std::distance(vertex_ids.begin(), i);
 
                }
 
                PHYSFS_write( SaveFile, &tseg, sizeof(tseg), 1);
 
 
 
         }
 
 
 
        //===================== SAVE TEXTURE INFO ==========================
 
 
 
        texture_offset = PHYSFS_tell(SaveFile);
 
 
 
        for (unsigned i = 0, n = NumTextures; i < n; ++i)
 
        {
 
                current_tmap_list[i] = TmapInfo[i].filename;
 
                PHYSFS_write(SaveFile, current_tmap_list[i].data(), current_tmap_list[i].size(), 1);
 
        }
 
 
 
        //============= REWRITE FILE INFO, TO SAVE OFFSETS ===============
 
 
 
        // Update the offset fields
 
        group_fileinfo.header_offset     =   header_offset;
 
        group_fileinfo.editor_offset     =   editor_offset;
 
        group_fileinfo.vertex_offset     =   vertex_offset;
 
        group_fileinfo.segment_offset    =   segment_offset;
 
        group_fileinfo.texture_offset    =   texture_offset;
 
        
 
        // Write the fileinfo
 
        PHYSFSX_fseek(  SaveFile, 0, SEEK_SET );  // Move to TOF
 
        PHYSFS_write( SaveFile, &group_fileinfo, sizeof(group_fileinfo), 1);
 
 
 
        //==================== CLOSE THE FILE =============================
 
        return 0;
 
}
 
 
 
static std::array<d_fname, MAX_TEXTURES> old_tmap_list;
 
// static short tmap_xlate_table[MAX_TEXTURES]; // ZICO - FIXME
 
 
 
// -----------------------------------------------------------------------------
 
// Load group will:
 
//int med_load_group(char * filename)
 
static int med_load_group( const char *filename, group::vertex_array_type_t &vertex_ids, group::segment_array_type_t &segment_ids)
 
{
 
        int vertnum;
 
        char ErrorMessage[200];
 
        short tmap_xlate;
 
        int     translate=0;
 
        char    *temptr;
 
        segment tseg;
 
        auto LoadFile = PHYSFSX_openReadBuffered(filename);
 
        if (!LoadFile)
 
        {
 
                snprintf(ErrorMessage, sizeof(ErrorMessage), "ERROR: Unable to open %s\n", filename);
 
                ui_messagebox( -2, -2, 1, ErrorMessage, "Ok" );
 
                return 1;
 
        }
 
 
 
        //===================== READ FILE INFO ========================
 
 
 
        // These are the default values... version and fileinfo_sizeof
 
        // don't have defaults.
 
        group_fileinfo.header_offset     =   -1;
 
        group_fileinfo.header_size       =   sizeof(group_header);
 
        group_fileinfo.editor_offset     =   -1;
 
        group_fileinfo.editor_size       =   sizeof(group_editor);
 
        group_fileinfo.vertex_offset     =   -1;
 
        group_fileinfo.vertex_howmany    =   0;
 
        group_fileinfo.vertex_sizeof     =   sizeof(vms_vector);
 
        group_fileinfo.segment_offset    =   -1;
 
        group_fileinfo.segment_howmany   =   0;
 
        group_fileinfo.segment_sizeof    =   sizeof(segment);
 
        group_fileinfo.texture_offset    =   -1;
 
        group_fileinfo.texture_howmany   =   0;
 
        group_fileinfo.texture_sizeof    =   13;  // num characters in a name
 
 
 
        // Read in group_top_fileinfo to get size of saved fileinfo.
 
 
 
        if (PHYSFSX_fseek( LoadFile, 0, SEEK_SET ))
 
                Error( "Error seeking to 0 in group.c" );
 
 
 
        if (PHYSFS_read( LoadFile, &group_top_fileinfo, sizeof(group_top_fileinfo),1 )!=1)
 
                Error( "Error reading top_fileinfo in group.c" );
 
 
 
        // Check version number
 
        if (group_top_fileinfo.fileinfo_version < COMPATIBLE_VERSION )
 
        {
 
                snprintf(ErrorMessage, sizeof(ErrorMessage), "You are trying to load %s\n" \
 
                                                  "a version %d group, which is known to be incompatible\n" \
 
                                                  "with the current expected version %d groups.", \
 
                                                  filename, group_top_fileinfo.fileinfo_version, MINE_VERSION );
 
 
 
                if (ui_messagebox( -2, -2, 2, ErrorMessage, "Forget it", "Try anyway" )==1)
 
                {
 
                        return 1;
 
                }
 
 
 
                ui_messagebox( -2, -2, 1, "Good luck!", "I need it" );
 
        }
 
 
 
        // Now, Read in the fileinfo
 
 
 
        if (PHYSFSX_fseek( LoadFile, 0, SEEK_SET ))
 
                Error( "Error seeking to 0b in group.c" );
 
 
 
        if (PHYSFS_read( LoadFile, &group_fileinfo, group_top_fileinfo.fileinfo_sizeof,1 )!=1)
 
                Error( "Error reading group_fileinfo in group.c" );
 
 
 
        //===================== READ HEADER INFO ========================
 
 
 
        // Set default values.
 
        group_header.num_vertices        =   0;
 
        group_header.num_segments        =   0;
 
 
 
        if (group_fileinfo.header_offset > -1 )
 
        {
 
                if (PHYSFSX_fseek( LoadFile,group_fileinfo.header_offset, SEEK_SET ))
 
                        Error( "Error seeking to header_offset in group.c" );
 
 
 
                if (PHYSFS_read( LoadFile, &group_header, group_fileinfo.header_size,1 )!=1)
 
                        Error( "Error reading group_header in group.c" );
 
        }
 
 
 
        //===================== READ EDITOR INFO ==========================
 
 
 
        // Set default values
 
        group_editor.current_seg         =   0;
 
        group_editor.newsegment_offset   =   -1; // To be written
 
        group_editor.newsegment_size     =   sizeof(segment);
 
        group_editor.Groupsegp                          =   -1;
 
        group_editor.Groupside                          =   0;
 
 
 
        if (group_fileinfo.editor_offset > -1 )
 
        {
 
                if (PHYSFSX_fseek( LoadFile,group_fileinfo.editor_offset, SEEK_SET ))
 
                        Error( "Error seeking to editor_offset in group.c" );
 
 
 
                if (PHYSFS_read( LoadFile, &group_editor, group_fileinfo.editor_size,1 )!=1)
 
                        Error( "Error reading group_editor in group.c" );
 
 
 
        }
 
 
 
        //===================== READ VERTEX INFO ==========================
 
 
 
        if ( (group_fileinfo.vertex_offset > -1) && (group_fileinfo.vertex_howmany > 0))
 
        {
 
                if (PHYSFSX_fseek( LoadFile,group_fileinfo.vertex_offset, SEEK_SET ))
 
                        Error( "Error seeking to vertex_offset in group.c" );
 
 
 
                vertex_ids.clear();
 
                        for (unsigned i = 0; i< group_header.num_vertices; ++i)
 
                        {
 
                                vertex tvert;
 
                                if (PHYSFS_read( LoadFile, &tvert, sizeof(tvert),1 )!=1)
 
                                        Error( "Error reading tvert in group.c" );
 
                                vertex_ids.emplace_back(med_create_duplicate_vertex(tvert));
 
                        }
 
 
 
                }
 
 
 
        //==================== READ SEGMENT INFO ===========================
 
 
 
        if ( (group_fileinfo.segment_offset > -1) && (group_fileinfo.segment_howmany > 0))
 
        {
 
                if (PHYSFSX_fseek( LoadFile,group_fileinfo.segment_offset, SEEK_SET ))
 
                        Error( "Error seeking to segment_offset in group.c" );
 
 
 
                segment_ids.clear();
 
                for (unsigned i = 0; i < group_header.num_segments; ++i)
 
                {
 
                        if (PHYSFS_read( LoadFile, &tseg, sizeof(segment),1 )!=1)
 
                                Error( "Error reading tseg in group.c" );
 
                                
 
                        group::segment_array_type_t::value_type s = get_free_segment_number(Segments);
 
                        segment_ids.emplace_back(s);
 
                        const auto &&segp = vmsegptridx(s);
 
                        *segp = tseg; 
 
                        segp->objects = object_none;
 
 
 
                        fuelcen_activate(segp);
 
                        }
 
 
 
                range_for (const auto &gs, segment_ids)
 
                {
 
                        auto &segp = *vmsegptr(gs);
 
                        // Fix vertices
 
                        range_for (auto &j, segp.verts)
 
                        {
 
                                vertnum = vertex_ids[j];
 
                                j = vertnum;
 
                                }
 
 
 
                        // Fix children and walls.
 
                        for (unsigned j = 0; j < MAX_SIDES_PER_SEGMENT; ++j)
 
                        {
 
                                auto &seg = Segments[gs];
 
                                shared_segment &useg = seg;
 
                                unique_segment &useg = seg;
 
                                sseg.sides[j].wall_num = wall_none;
 
                                if (IS_CHILD(Segments[gs].children[j])) {
 
                                        segnum_t segnum;
 
                                        segnum = segment_ids[Segments[gs].children[j]];
 
                                        Segments[gs].children[j] = segnum;
 
                                        } 
 
                                //Translate textures.
 
                                if (translate == 1) {
 
                                        int     temp;
 
                                        tmap_xlate = useg.sides[j].tmap_num;
 
                                        useg.sides[j].tmap_num = tmap_xlate_table[tmap_xlate];
 
                                        temp = useg.sides[j].tmap_num2;
 
                                        tmap_xlate = temp & 0x3fff;                     // strip off orientation bits
 
                                        if (tmap_xlate != 0)
 
                                                useg.sides[j].tmap_num2 = (temp & (~0x3fff)) | tmap_xlate_table[tmap_xlate];  // mask on original orientation bits
 
                                        }
 
                                }
 
                        }
 
        }
 
        
 
        //===================== READ TEXTURE INFO ==========================
 
 
 
        if ( (group_fileinfo.texture_offset > -1) && (group_fileinfo.texture_howmany > 0))
 
        {
 
                if (PHYSFSX_fseek( LoadFile, group_fileinfo.texture_offset, SEEK_SET ))
 
                        Error( "Error seeking to texture_offset in gamemine.c" );
 
 
 
                range_for (auto &i, partial_range(old_tmap_list, group_fileinfo.texture_howmany))
 
                {
 
                        std::array<char, FILENAME_LEN> a;
 
                        if (PHYSFS_read(LoadFile, a.data(), std::min(static_cast<size_t>(group_fileinfo.texture_sizeof), a.size()), 1) != 1)
 
                                Error( "Error reading old_tmap_list[i] in gamemine.c" );
 
                        i.copy_if(a);
 
                }
 
        }
 
 
 
        //=============== GENERATE TEXTURE TRANSLATION TABLE ===============
 
 
 
        translate = 0;
 
        
 
        Assert (NumTextures < MAX_TEXTURES);
 
{
 
        hashtable ht;
 
        // Remove all the file extensions in the textures list
 
 
 
        for (unsigned i = 0; i < NumTextures; ++i)
 
        {
 
                temptr = strchr(&TmapInfo[i].filename[0u], '.');
 
                if (temptr) *temptr = '\0';
 
                hashtable_insert( &ht, &TmapInfo[i].filename[0u], i );
 
        }
 
 
 
        // For every texture, search through the texture list
 
        // to find a matching name.
 
        for (unsigned j = 0; j < group_fileinfo.texture_howmany; ++j)
 
        {
 
                // Remove this texture name's extension
 
                temptr = strchr(&old_tmap_list[j][0u], '.');
 
                if (temptr) *temptr = '\0';
 
 
 
                tmap_xlate_table[j] = hashtable_search( &ht, static_cast<const char *>(old_tmap_list[j]));
 
                if (tmap_xlate_table[j] < 0 )
 
                        tmap_xlate_table[j] = 0;
 
                if (tmap_xlate_table[j] != j ) translate = 1;
 
        }
 
}
 
 
 
 
 
        //======================== CLOSE FILE ==============================
 
        LoadFile.reset();
 
 
 
        //========================= UPDATE VARIABLES ======================
 
 
 
        if (group_editor.Groupsegp != -1 ) 
 
                Groupsegp[current_group] = &Segments[segment_ids[group_editor.Groupsegp]];
 
        else
 
                Groupsegp[current_group] = NULL;
 
 
 
        Groupside[current_group] = group_editor.Groupside;
 
 
 
        warn_if_concave_segments();
 
        
 
        return 0;
 
}
 
 
 
static char group_filename[PATH_MAX] = "*.GRP";
 
 
 
static void checkforgrpext( char * f )
 
{
 
        int i;
 
 
 
        for (i=1; f[i]; i++ )
 
        {
 
                if (f[i]=='.') return;
 
 
 
                if ((f[i]==' '||f[i]==0) )
 
                {
 
                        f[i]='.';
 
                        f[i+1]='G';
 
                        f[i+2]= 'R';
 
                        f[i+3]= 'P';
 
                        f[i+4]=0;
 
                        return;
 
                }
 
        }
 
 
 
        if (i < 123)
 
        {
 
                f[i]='.';
 
                f[i+1]='G';
 
                f[i+2]= 'R';
 
                f[i+3]= 'P';
 
                f[i+4]=0;
 
                return;
 
        }
 
}
 
#endif
 
 
 
//short vertex_list[MAX_VERTICES];
 
 
 
 
 
int SaveGroup()
 
{
 
        ui_messagebox(-2, -2, 1, "ERROR: Groups are broken.", "Ok");
 
        return 0;
 
#if 0
 
        // Save group
 
        int i;
 
 
 
        if (current_group == -1)
 
                {
 
                ui_messagebox(-2, -2, 1, "ERROR: No current group.", "Ok");
 
                return 0;
 
                }
 
 
 
        std::array<int8_t, MAX_VERTICES> vertex_list{};
 
 
 
        //      Make a list of all vertices in group.
 
        range_for (const auto &gs, GroupList[current_group].segments)
 
                range_for (auto &v, Segments[gs].verts)
 
                {
 
                        vertex_list[v] = 1;
 
                }       
 
 
 
        GroupList[current_group].vertices.clear();
 
        for (i=0; i<=Highest_vertex_index; i++) 
 
                if (vertex_list[i] == 1) { 
 
                        GroupList[current_group].vertices.emplace_back(i);
 
                }
 
        med_save_group("TEMP.GRP", GroupList[current_group].vertices, GroupList[current_group].segments);
 
   if (ui_get_filename( group_filename, "*.GRP", "SAVE GROUP" ))
 
        {
 
      checkforgrpext(group_filename);
 
                if (med_save_group(group_filename, GroupList[current_group].vertices, GroupList[current_group].segments))
 
                        return 0;
 
                mine_changed = 0;
 
        }
 
        
 
        return 1;
 
#endif
 
}
 
 
 
 
 
int LoadGroup()
 
{
 
        ui_messagebox(-2, -2, 1, "ERROR: Groups are broken.", "Ok");
 
        return 0;
 
#if 0
 
        int x;
 
 
 
        if (num_groups == MAX_GROUPS)
 
                {
 
                x = ui_messagebox( -2, -2, 2, "Warning: You are about to wipe out a group.", "ARGH! NO!", "No problemo." );
 
                if (x==1) return 0;
 
                }
 
 
 
        if (num_groups < MAX_GROUPS)
 
                {
 
                num_groups++;
 
                current_group = num_groups-1;
 
                }
 
                else current_group = 0;
 
 
 
   if (ui_get_filename( group_filename, "*.GRP", "LOAD GROUP" ))
 
        {
 
      checkforgrpext(group_filename);
 
          med_load_group(group_filename, GroupList[current_group].vertices, GroupList[current_group].segments);
 
                
 
        if (!med_move_group(0, Cursegp, Curside, vmsegptridx(Groupsegp[current_group]), Groupside[current_group], vmd_identity_matrix, 0))
 
        {
 
                autosave_mine(mine_filename);
 
                set_view_target_from_segment(Cursegp);
 
                Update_flags |= UF_WORLD_CHANGED;
 
                mine_changed = 1;
 
                diagnostic_message("Group moved.");
 
                return 0;
 
                } else
 
        return 1;
 
        }       else
 
 
 
        return 1;
 
#endif
 
}
 
 
 
 
 
int UngroupSegment( void )
 
{
 
        if (Cursegp->group == current_group) {
 
        
 
                Cursegp->group = -1;
 
                delete_segment_from_group(Cursegp, current_group);
 
        
 
           Update_flags |= UF_WORLD_CHANGED;
 
           mine_changed = 1;
 
           diagnostic_message_fmt("Segment Ungrouped from Group %d.", current_group);
 
        
 
                return 1;
 
        } else
 
        return 0;
 
}
 
 
 
int GroupSegment( void )
 
{
 
        if (Cursegp->group == -1) {
 
 
 
                Cursegp->group = current_group;
 
                add_segment_to_group(Cursegp, current_group);
 
        
 
           Update_flags |= UF_WORLD_CHANGED;
 
           mine_changed = 1;
 
           diagnostic_message_fmt("Segment Added to Group %d.", current_group);
 
 
 
                return 1;
 
        } else
 
        return 0;
 
}
 
 
 
int Degroup( void )
 
{
 
        auto &LevelSharedVertexState = LevelSharedSegmentState.get_vertex_state();
 
        auto &Vertices = LevelSharedVertexState.get_vertices();
 
        int i;
 
 
 
//      GroupList[current_group].num_segments = 0;
 
//      Groupsegp[current_group] = 0;
 
 
 
        if (num_groups==0) return 0;
 
 
 
        range_for (const auto &gs, GroupList[current_group].segments)
 
                delete_segment_from_group(vmsegptridx(gs), current_group);
 
 
 
          //    delete_segment_from_group( &Segments[GroupList[current_group].segments[i]]-Segments, current_group );
 
 
 
        for (i=current_group;i<num_groups-1;i++)
 
                {
 
                GroupList[i] = GroupList[i+1];
 
                Groupsegp[i] = Groupsegp[i+1];
 
                }
 
 
 
        num_groups--;
 
 
 
        GroupList[num_groups].segments.clear();
 
        Groupsegp[num_groups] = 0;
 
        
 
        if (current_group > num_groups-1) current_group--;
 
 
 
        if (num_groups == 0)
 
                current_group = -1;
 
 
 
   if (Lock_view_to_cursegp)
 
        {
 
                auto &vcvertptr = Vertices.vcptr;
 
       set_view_target_from_segment(vcvertptr, Cursegp);
 
        }
 
   Update_flags |= UF_WORLD_CHANGED;
 
   mine_changed = 1;
 
   diagnostic_message("Group UNgrouped.");
 
 
 
        return 1;
 
}
 
 
 
int NextGroup( void ) 
 
{
 
 
 
        if (num_groups > 0)
 
                {
 
                current_group++;
 
                if (current_group >= num_groups ) current_group = 0;
 
                
 
                Update_flags |= UF_ED_STATE_CHANGED;
 
                mine_changed = 1;
 
                }
 
        else editor_status("No Next Group\n");
 
        return 0;
 
}
 
 
 
int PrevGroup( void ) 
 
{
 
        if (num_groups > 0)
 
                {
 
                current_group--;
 
                if (current_group < 0 ) current_group = num_groups-1;
 
                
 
                Update_flags |= UF_ED_STATE_CHANGED;
 
                mine_changed = 1;
 
                }
 
        else editor_status("No Previous Group\n");
 
        return 0;
 
}
 
 
 
 
 
//      -----------------------------------------------------------------------------
 
int MoveGroup(void)
 
{
 
        if (!Groupsegp[current_group]) {
 
                editor_status("Error -- Cannot move group, no group segment.");
 
                return 1;
 
        }
 
 
 
        med_compress_mine();
 
 
 
        if (!med_move_group(0, Cursegp, Curside, vmsegptridx(Groupsegp[current_group]), Groupside[current_group], vmd_identity_matrix, 0))
 
        {
 
                autosave_mine(mine_filename);
 
                Update_flags |= UF_WORLD_CHANGED;
 
                mine_changed = 1;
 
                diagnostic_message("Group moved.");
 
                return 0;
 
        } else
 
                return 1;
 
}                                 
 
 
 
 
 
//      -----------------------------------------------------------------------------
 
int CopyGroup(void)
 
{
 
        segnum_t        attach_seg;
 
 
 
        if (!Groupsegp[current_group]) {
 
                editor_status("Error -- Cannot copy group, no group segment.");
 
                return 1;
 
        }
 
 
 
        //      See if the attach side in the group is attached to another segment.
 
        //      If so, it must not be in the group for group copy to be legal.
 
        attach_seg = Groupsegp[current_group]->children[Groupside[current_group]];
 
        if (attach_seg != segment_none) {
 
                if (GroupList[current_group].segments.contains(attach_seg)) {
 
                        editor_status_fmt("Error -- Cannot copy group, attach side has a child (segment %i) attached.", attach_seg);
 
                        return 1;
 
                }
 
        }
 
 
 
        med_compress_mine();
 
 
 
        if (!med_copy_group(0, Cursegp, Curside, vcsegptr(Groupsegp[current_group]), Groupside[current_group], vmd_identity_matrix))
 
        {
 
                autosave_mine(mine_filename);
 
                Update_flags |= UF_WORLD_CHANGED;
 
                mine_changed = 1;
 
                diagnostic_message("Group copied.");
 
                return 0;
 
        } else    
 
                return 1;
 
}
 
 
 
 
 
//      -----------------------------------------------------------------------------
 
int RotateGroup(void)
 
{
 
 
 
        if (!Groupsegp[current_group]) {
 
                editor_status("Error -- Cannot rotate group, no group segment.");
 
                return 1;
 
        }
 
 
 
        Group_orientation[current_group]++;
 
        if ((Group_orientation[current_group] <0) || (Group_orientation[current_group] >4))
 
                Group_orientation[current_group]=0;
 
 
 
        med_compress_mine();
 
        
 
        if (!med_move_group(0, Cursegp, Curside, vmsegptridx(Groupsegp[current_group]), Groupside[current_group],
 
                                                                vmd_identity_matrix, Group_orientation[current_group]))
 
                        {
 
                        Update_flags |= UF_WORLD_CHANGED;
 
                        mine_changed = 1;
 
                        diagnostic_message("Group rotated.");
 
                        return 0;
 
                        } 
 
                else      
 
                        return 1;
 
}
 
 
 
 
 
//      -----------------------------------------------------------------------------
 
//      Creates a group from all segments connected to marked segment.
 
int SubtractFromGroup(void)
 
{
 
        int     x, original_group;
 
        if (!Markedsegp) {
 
                editor_status("Error -- Cannot create group, no marked segment.");
 
                return 1;
 
        }
 
 
 
        med_compress_mine();
 
        autosave_mine(mine_filename);
 
 
 
        if (num_groups == MAX_GROUPS) {
 
                x = ui_messagebox( -2, -2, 2, "Warning: You are about to wipe out a group.", "ARGH! NO!", "No problemo." );
 
                if (x==1) return 0;
 
        }                                          
 
 
 
        if (current_group == -1) {
 
                editor_status("Error -- No current group.  Cannot subtract.");
 
                return 1;
 
        }
 
 
 
        original_group = current_group;
 
 
 
        current_group = (current_group + 1) % MAX_GROUPS;
 
 
 
        //      Create a list of segments to copy.
 
        GroupList[current_group].segments.clear();
 
        create_group_list(Markedsegp, GroupList[current_group].segments, &Selected_segs);
 
 
 
        //      Now, scan the two groups, forming a group which consists of only those segments common to the two groups.
 
        auto intersects = [original_group](group::segment_array_type_t::const_reference r) -> bool {
 
                bool contains = GroupList[original_group].segments.contains(r);
 
                if (!contains)
 
                        Segments[r].group = -1;
 
                return !contains;
 
        };
 
        GroupList[current_group].segments.erase_if(intersects);
 
 
 
        // Replace Marked segment with Group Segment.
 
        Groupsegp[current_group] = Markedsegp;
 
        Groupside[current_group] = Markedside;
 
 
 
        range_for (const auto &gs, GroupList[current_group].segments)
 
                Segments[gs].group = current_group;
 
        
 
        Update_flags |= UF_WORLD_CHANGED;
 
        mine_changed = 1;
 
        diagnostic_message("Group created.");
 
 
 
        return 1; 
 
                                  
 
}
 
 
 
//      -----------------------------------------------------------------------------
 
//      Creates a group from all segments already in CurrentGroup which can be reached from marked segment
 
//      without passing through current segment.
 
int CreateGroup(void)
 
{
 
        int x;
 
 
 
        if (!Markedsegp) {
 
                editor_status("Error -- Cannot create group, no marked segment.");
 
                return 1;
 
        }
 
 
 
        med_compress_mine();
 
        autosave_mine(mine_filename);
 
 
 
        if (num_groups == MAX_GROUPS) {
 
                x = ui_messagebox( -2, -2, 2, "Warning: You are about to wipe out a group.", "ARGH! NO!", "No problemo." );
 
                if (x==1)
 
                        return 0;                               // Aborting at user's request.
 
        }                                          
 
 
 
        if (num_groups < MAX_GROUPS) {
 
                num_groups++;
 
                current_group = num_groups-1;
 
        } else
 
                current_group = 0;
 
 
 
        //      Create a list of segments to copy.
 
        GroupList[current_group].clear();
 
        create_group_list(Markedsegp, GroupList[current_group].segments, NULL);
 
        
 
        // Replace Marked segment with Group Segment.
 
        Groupsegp[current_group] = Markedsegp;
 
        Groupside[current_group] = Markedside;
 
//      Markedsegp = 0;
 
//      Markedside = WBACK;
 
 
 
        range_for (const auto &gs, GroupList[current_group].segments)
 
                Segments[gs].group = current_group;
 
        
 
        Update_flags |= UF_WORLD_CHANGED;
 
        mine_changed = 1;
 
        diagnostic_message("Group created.");
 
 
 
        return 1; 
 
                                  
 
}
 
 
 
//      -----------------------------------------------------------------------------
 
// Deletes current group.
 
int DeleteGroup( void )
 
{
 
        auto &LevelSharedVertexState = LevelSharedSegmentState.get_vertex_state();
 
        auto &Vertices = LevelSharedVertexState.get_vertices();
 
        int i;
 
 
 
        autosave_mine(mine_filename);
 
                
 
        if (num_groups==0) return 0;
 
 
 
        range_for (const auto &gs, GroupList[current_group].segments)
 
        {
 
                const auto &&segp = vmsegptridx(gs);
 
                segp->group = -1;
 
                med_delete_segment(segp);
 
        }
 
 
 
        for (i=current_group;i<num_groups-1;i++) {
 
                GroupList[i] = GroupList[i+1];
 
                Groupsegp[i] = Groupsegp[i+1];
 
        }
 
 
 
        num_groups--;
 
        GroupList[num_groups].clear();
 
        Groupsegp[num_groups] = 0;
 
 
 
        if (current_group > num_groups-1) current_group--;
 
 
 
        if (num_groups==0)
 
                current_group = -1;
 
 
 
        undo_status[Autosave_count] = "Delete Group UNDONE.";
 
   if (Lock_view_to_cursegp)
 
        {
 
                auto &vcvertptr = Vertices.vcptr;
 
       set_view_target_from_segment(vcvertptr, Cursegp);
 
        }
 
 
 
   Update_flags |= UF_WORLD_CHANGED;
 
   mine_changed = 1;
 
   diagnostic_message("Group deleted.");
 
   // warn_if_concave_segments();     // This could be faster -- just check if deleted segment was concave, warn accordingly
 
 
 
        return 1;
 
 
 
}
 
 
 
 
 
int MarkGroupSegment( void )
 
{
 
        if ((Cursegp->group != -1) && (Cursegp->group == current_group))
 
                {
 
           autosave_mine(mine_filename);
 
                Groupsegp[current_group] = Cursegp;
 
                Groupside[current_group] = Curside;
 
                editor_status("Group Segment Marked.");
 
                Update_flags |= UF_ED_STATE_CHANGED;
 
                undo_status[Autosave_count] = "Mark Group Segment UNDONE.";
 
                mine_changed = 1;
 
                return 1;
 
                }
 
        else return 0;
 
}