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
 * Created from version 1.11 of main\wall.c
23
 *
24
 */
25
 
26
#include <stdio.h>
27
#include <stdlib.h>
28
#include <math.h>
29
#include <string.h>
30
#include "wall.h"
31
#include "editor/medwall.h"
32
#include "inferno.h"
33
#include "editor/editor.h"
34
#include "editor/esegment.h"
35
#include "segment.h"
36
#include "dxxerror.h"
37
#include "event.h"
38
#include "game.h"
39
#include "gameseg.h"
40
#include "textures.h"
41
#include "screens.h"
42
#include "switch.h"
43
#include "editor/eswitch.h"
44
#include "texmerge.h"
45
#include "medrobot.h"
46
#include "timer.h"
47
#include "cntrlcen.h"
48
#include "key.h"
49
#include "ehostage.h"
50
#include "centers.h"
51
#include "piggy.h"
52
#include "kdefs.h"
53
#include "u_mem.h"
54
#include "d_enumerate.h"
55
 
56
#include "compiler-range_for.h"
57
#include "d_range.h"
58
#include "partial_range.h"
59
#include "d_zip.h"
60
#include <memory>
61
#include <utility>
62
 
63
static int wall_add_to_side(fvcvertptr &vcvertptr, wall_array &Walls, const vmsegptridx_t segp, unsigned side, unsigned type);
64
 
65
//-------------------------------------------------------------------------
66
// Variables for this module...
67
//-------------------------------------------------------------------------
68
static UI_DIALOG                                *MainWindow = NULL;
69
 
70
namespace {
71
 
72
struct wall_dialog
73
{
74
        std::unique_ptr<UI_GADGET_USERBOX> wallViewBox;
75
        std::unique_ptr<UI_GADGET_BUTTON> quitButton, prev_wall, next_wall, blastable, door, illusory, closed_wall, goto_prev_wall, goto_next_wall, remove, bind_trigger, bind_control;
76
        std::array<std::unique_ptr<UI_GADGET_CHECKBOX>, 3> doorFlag;
77
        std::array<std::unique_ptr<UI_GADGET_RADIO>, 4> keyFlag;
78
        int old_wall_num;
79
        fix64 time;
80
        int framenum;
81
};
82
 
83
static int Current_door_type=1;
84
 
85
struct count_wall
86
{
87
        wallnum_t wallnum;
88
        segnum_t        segnum;
89
        short sidenum;
90
};
91
 
92
static unsigned predicate_find_nonblastable_wall(const wclip &w)
93
{
94
        if (w.num_frames == wclip_frames_none)
95
                return 0;
96
        return !(w.flags & WCF_BLASTABLE);
97
}
98
 
99
static unsigned predicate_find_blastable_wall(const wclip &w)
100
{
101
        if (w.num_frames == wclip_frames_none)
102
                return 0;
103
        return w.flags & WCF_BLASTABLE;
104
}
105
 
106
}
107
 
108
static window_event_result wall_dialog_handler(UI_DIALOG *dlg,const d_event &event, wall_dialog *wd);
109
 
110
//---------------------------------------------------------------------
111
// Add a wall (removable 2 sided)
112
static int add_wall(fvcvertptr &vcvertptr, wall_array &Walls, const vmsegptridx_t seg, const unsigned side)
113
{
114
        if (Walls.get_count() < MAX_WALLS-2)
115
        if (IS_CHILD(seg->children[side])) {
116
                shared_segment &sseg = seg;
117
                auto &side0 = sseg.sides[side];
118
                if (side0.wall_num == wall_none) {
119
                        side0.wall_num = Walls.get_count();
120
                        Walls.set_count(Walls.get_count() + 1);
121
                        }
122
 
123
                const auto &&csegp = seg.absolute_sibling(seg->children[side]);
124
                auto Connectside = find_connect_side(seg, csegp);
125
 
126
                shared_segment &scseg = csegp;
127
                auto &side1 = scseg.sides[Connectside];
128
                if (side1.wall_num == wall_none) {
129
                        side1.wall_num = Walls.get_count();
130
                        Walls.set_count(Walls.get_count() + 1);
131
                        }
132
 
133
                create_removable_wall(vcvertptr, seg, side, CurrentTexture);
134
                create_removable_wall(vcvertptr, csegp, Connectside, CurrentTexture);
135
 
136
                return 1;
137
                }
138
 
139
        return 0;
140
}
141
 
142
static int wall_assign_door(int door_type)
143
{
144
        shared_segment &sseg = Cursegp;
145
        unique_segment &useg = Cursegp;
146
        if (sseg.sides[Curside].wall_num == wall_none) {
147
                editor_status("Cannot assign door. No wall at Curside.");
148
                return 0;
149
        }
150
 
151
        auto &Walls = LevelUniqueWallSubsystemState.Walls;
152
        auto &vmwallptr = Walls.vmptr;
153
        auto &wall0 = *vmwallptr(sseg.sides[Curside].wall_num);
154
        if (wall0.type != WALL_DOOR && wall0.type != WALL_BLASTABLE)
155
        {
156
                editor_status("Cannot assign door. No door at Curside.");
157
                return 0;
158
        }
159
 
160
        Current_door_type = door_type;
161
 
162
        auto &csegp = *vmsegptr(Cursegp->children[Curside]);
163
        auto Connectside = find_connect_side(Cursegp, csegp);
164
 
165
        wall0.clip_num = door_type;
166
        shared_segment &scseg = csegp;
167
        unique_segment &ucseg = csegp;
168
        vmwallptr(scseg.sides[Connectside].wall_num)->clip_num = door_type;
169
 
170
        auto &wa = GameSharedState.WallAnims[door_type];
171
        if (wa.flags & WCF_TMAP1) {
172
                useg.sides[Curside].tmap_num = wa.frames[0];
173
                ucseg.sides[Connectside].tmap_num = wa.frames[0];
174
                useg.sides[Curside].tmap_num2 = 0;
175
                ucseg.sides[Connectside].tmap_num2 = 0;
176
        }
177
        else {
178
                useg.sides[Curside].tmap_num2 = wa.frames[0];
179
                ucseg.sides[Connectside].tmap_num2 = wa.frames[0];
180
        }
181
 
182
        Update_flags |= UF_WORLD_CHANGED;
183
        return 1;
184
}
185
 
186
int wall_add_blastable()
187
{
188
        auto &LevelSharedVertexState = LevelSharedSegmentState.get_vertex_state();
189
        auto &Vertices = LevelSharedVertexState.get_vertices();
190
        auto &vcvertptr = Vertices.vcptr;
191
        auto &Walls = LevelUniqueWallSubsystemState.Walls;
192
        return wall_add_to_side(vcvertptr, Walls, Cursegp, Curside, WALL_BLASTABLE);
193
}
194
 
195
int wall_add_door()
196
{
197
        auto &LevelSharedVertexState = LevelSharedSegmentState.get_vertex_state();
198
        auto &Vertices = LevelSharedVertexState.get_vertices();
199
        auto &vcvertptr = Vertices.vcptr;
200
        auto &Walls = LevelUniqueWallSubsystemState.Walls;
201
        return wall_add_to_side(vcvertptr, Walls, Cursegp, Curside, WALL_DOOR);
202
}
203
 
204
int wall_add_closed_wall()
205
{
206
        auto &LevelSharedVertexState = LevelSharedSegmentState.get_vertex_state();
207
        auto &Vertices = LevelSharedVertexState.get_vertices();
208
        auto &vcvertptr = Vertices.vcptr;
209
        auto &Walls = LevelUniqueWallSubsystemState.Walls;
210
        return wall_add_to_side(vcvertptr, Walls, Cursegp, Curside, WALL_CLOSED);
211
}
212
 
213
int wall_add_external_wall()
214
{
215
        if (Cursegp->children[Curside] == segment_exit)
216
        {
217
                editor_status( "Wall is already external!" );
218
                return 1;
219
        }
220
 
221
        if (IS_CHILD(Cursegp->children[Curside])) {
222
                editor_status( "Cannot add external wall here - seg has children" );
223
                return 0;
224
        }
225
 
226
        Cursegp->children[Curside] = -2;
227
 
228
        return 1;
229
}
230
 
231
int wall_add_illusion()
232
{
233
        auto &LevelSharedVertexState = LevelSharedSegmentState.get_vertex_state();
234
        auto &Vertices = LevelSharedVertexState.get_vertices();
235
        auto &vcvertptr = Vertices.vcptr;
236
        auto &Walls = LevelUniqueWallSubsystemState.Walls;
237
        return wall_add_to_side(vcvertptr, Walls, Cursegp, Curside, WALL_ILLUSION);
238
}
239
 
240
static int GotoPrevWall() {
241
        wallnum_t current_wall;
242
 
243
        shared_segment &sseg = Cursegp;
244
        auto &side = sseg.sides[Curside];
245
        auto &Walls = LevelUniqueWallSubsystemState.Walls;
246
        auto &vcwallptr = Walls.vcptr;
247
        if (side.wall_num == wall_none)
248
                current_wall = Walls.get_count();
249
        else
250
                current_wall = side.wall_num;
251
 
252
        current_wall--;
253
        if (current_wall >= Walls.get_count()) current_wall = Walls.get_count()-1;
254
 
255
        auto &w = *vcwallptr(current_wall);
256
        if (w.segnum == segment_none)
257
        {
258
                return 0;
259
        }
260
 
261
        if (w.sidenum == side_none)
262
        {
263
                return 0;
264
        }
265
 
266
        Cursegp = imsegptridx(w.segnum);
267
        Curside = w.sidenum;
268
 
269
        return 1;
270
}
271
 
272
 
273
static int GotoNextWall() {
274
        shared_segment &sseg = Cursegp;
275
        auto &side = sseg.sides[Curside];
276
        auto current_wall = side.wall_num; // It's ok to be -1 because it will immediately become 0
277
 
278
        current_wall++;
279
 
280
        auto &Walls = LevelUniqueWallSubsystemState.Walls;
281
        auto &vcwallptr = Walls.vcptr;
282
        if (current_wall >= Walls.get_count()) current_wall = 0;
283
 
284
        auto &w = *vcwallptr(current_wall);
285
        if (w.segnum == segment_none)
286
        {
287
                return 0;
288
        }
289
 
290
        if (w.sidenum == side_none)
291
        {
292
                return 0;
293
        }
294
 
295
        Cursegp = imsegptridx(w.segnum);
296
        Curside = w.sidenum;
297
 
298
        return 1;
299
}
300
 
301
template <typename I, typename P>
302
I wraparound_find_if(const I begin, const I start, const I end, P &&predicate)
303
{
304
        for (I iter = start;;)
305
        {
306
                ++ iter;
307
                if (iter == end)
308
                        iter = begin;
309
                if (iter == start)
310
                        return iter;
311
                if (predicate(*iter))
312
                        return iter;
313
        }
314
}
315
 
316
/*
317
 * Given a range defined by [`begin`, `end`), a starting point `start`
318
 * that is within that range, and a predicate `predicate`, examine each
319
 * element in the range (`start`, `begin`].  If `predicate(*iter)`
320
 * returns true, return `iter`.  Otherwise, perform the same search on
321
 * the range (`end`, `start`).  If traversal reaches `start` without
322
 * finding such an element, return `start` without calling
323
 * `predicate(*start)`.
324
 */
325
template <typename I, typename P>
326
I wraparound_backward_find_if(const I begin, const I start, const I end, P &&predicate)
327
{
328
        for (I iter = start;;)
329
        {
330
                if (iter == begin)
331
                        iter = end;
332
                -- iter;
333
                if (iter == start)
334
                        return iter;
335
                if (predicate(*iter))
336
                        return iter;
337
        }
338
}
339
 
340
static int PrevWall() {
341
        int wall_type;
342
        const auto cur_wall_num = Cursegp->shared_segment::sides[Curside].wall_num;
343
        if (cur_wall_num == wall_none)
344
        {
345
                editor_status("Cannot assign new wall. No wall on curside.");
346
                return 0;
347
        }
348
 
349
        auto &Walls = LevelUniqueWallSubsystemState.Walls;
350
        auto &vcwallptr = Walls.vcptr;
351
        auto &w = *vcwallptr(cur_wall_num);
352
        wall_type = w.clip_num;
353
        auto &WallAnims = GameSharedState.WallAnims;
354
 
355
        const auto b = WallAnims.begin();
356
        const auto s = std::next(b, wall_type);
357
        const auto e = std::next(b, Num_wall_anims);
358
        if (w.type == WALL_DOOR)
359
        {
360
                auto iter = wraparound_backward_find_if(b, s, e, predicate_find_nonblastable_wall);
361
                if (iter == s)
362
                        throw std::runtime_error("Cannot find clip for door.");
363
                wall_type = std::distance(b, iter);
364
        }
365
        else if (w.type == WALL_BLASTABLE)
366
        {
367
                auto iter = wraparound_backward_find_if(b, s, e, predicate_find_blastable_wall);
368
                if (iter == s)
369
                        throw std::runtime_error("Cannot find clip for blastable wall.");
370
                wall_type = std::distance(b, iter);
371
        }
372
 
373
        wall_assign_door(wall_type);
374
 
375
        Update_flags |= UF_WORLD_CHANGED;
376
        return 1;
377
}
378
 
379
static int NextWall() {
380
        int wall_type;
381
        const auto cur_wall_num = Cursegp->shared_segment::sides[Curside].wall_num;
382
        if (cur_wall_num == wall_none)
383
        {
384
                editor_status("Cannot assign new wall. No wall on curside.");
385
                return 0;
386
        }
387
 
388
        auto &Walls = LevelUniqueWallSubsystemState.Walls;
389
        auto &WallAnims = GameSharedState.WallAnims;
390
        auto &vcwallptr = Walls.vcptr;
391
        auto &w = *vcwallptr(cur_wall_num);
392
        wall_type = w.clip_num;
393
 
394
        const auto b = WallAnims.begin();
395
        const auto s = std::next(b, wall_type);
396
        const auto e = std::next(b, Num_wall_anims);
397
        if (w.type == WALL_DOOR)
398
        {
399
                auto iter = wraparound_find_if(b, s, e, predicate_find_nonblastable_wall);
400
                if (iter == s)
401
                        throw std::runtime_error("Cannot find clip for door.");
402
                wall_type = std::distance(b, iter);
403
        }
404
        else if (w.type == WALL_BLASTABLE)
405
        {
406
                auto iter = wraparound_find_if(b, s, e, predicate_find_blastable_wall);
407
                if (iter == s)
408
                        throw std::runtime_error("Cannot find clip for blastable wall.");
409
                wall_type = std::distance(b, iter);
410
        }
411
 
412
        wall_assign_door(wall_type);   
413
 
414
        Update_flags |= UF_WORLD_CHANGED;
415
        return 1;
416
 
417
}
418
 
419
//-------------------------------------------------------------------------
420
// Called from the editor... does one instance of the wall dialog box
421
//-------------------------------------------------------------------------
422
int do_wall_dialog()
423
{
424
        // Only open 1 instance of this window...
425
        if ( MainWindow != NULL ) return 0;
426
 
427
        auto wd = std::make_unique<wall_dialog>();
428
        wd->framenum = 0;
429
 
430
        // Close other windows. 
431
        close_all_windows();
432
 
433
        // Open a window with a quit button
434
        MainWindow = ui_create_dialog(TMAPBOX_X+20, TMAPBOX_Y+20, 765-TMAPBOX_X, 545-TMAPBOX_Y, DF_DIALOG, wall_dialog_handler, std::move(wd));
435
        return 1;
436
}
437
 
438
static window_event_result wall_dialog_created(UI_DIALOG *const w, wall_dialog *const wd)
439
{
440
        wd->quitButton = ui_add_gadget_button(w, 20, 252, 48, 40, "Done", NULL);
441
        // These are the checkboxes for each door flag.
442
        int i = 80;
443
        wd->doorFlag[0] = ui_add_gadget_checkbox(w, 22, i, 16, 16, 0, "Locked"); i += 24;
444
        wd->doorFlag[1] = ui_add_gadget_checkbox(w, 22, i, 16, 16, 0, "Auto"); i += 24;
445
        wd->doorFlag[2] = ui_add_gadget_checkbox(w, 22, i, 16, 16, 0, "Illusion OFF"); i += 24;
446
        wd->keyFlag[0] = ui_add_gadget_radio(w, 22, i, 16, 16, 0, "NONE"); i += 24;
447
        wd->keyFlag[1] = ui_add_gadget_radio(w, 22, i, 16, 16, 0, "Blue"); i += 24;
448
        wd->keyFlag[2] = ui_add_gadget_radio(w, 22, i, 16, 16, 0, "Red");  i += 24;
449
        wd->keyFlag[3] = ui_add_gadget_radio(w, 22, i, 16, 16, 0, "Yellow"); i += 24;
450
        // The little box the wall will appear in.
451
        wd->wallViewBox = ui_add_gadget_userbox(w, 155, 5, 64, 64);
452
        // A bunch of buttons...
453
        i = 80;
454
        wd->prev_wall = ui_add_gadget_button(w, 155, i, 70, 22, "<< Clip", PrevWall);
455
        wd->next_wall = ui_add_gadget_button(w, 155+70, i, 70, 22, "Clip >>", NextWall);i += 25;
456
        wd->blastable = ui_add_gadget_button(w, 155, i, 140, 22, "Add Blastable", wall_add_blastable); i += 25;
457
        wd->door = ui_add_gadget_button(w, 155, i, 140, 22, "Add Door", wall_add_door );        i += 25;
458
        wd->illusory = ui_add_gadget_button(w, 155, i, 140, 22, "Add Illusory", wall_add_illusion);     i += 25;
459
        wd->closed_wall = ui_add_gadget_button(w, 155, i, 140, 22, "Add Closed Wall", wall_add_closed_wall); i+=25;
460
        wd->goto_prev_wall = ui_add_gadget_button(w, 155, i, 70, 22, "<< Prev", GotoPrevWall);
461
        wd->goto_next_wall = ui_add_gadget_button(w, 155+70, i, 70, 22, "Next >>", GotoNextWall);i += 25;
462
        wd->remove = ui_add_gadget_button(w, 155, i, 140, 22, "Remove Wall", wall_remove); i += 25;
463
        wd->bind_trigger = ui_add_gadget_button(w, 155, i, 140, 22, "Bind to Trigger", bind_wall_to_trigger); i += 25;
464
        wd->bind_control = ui_add_gadget_button(w, 155, i, 140, 22, "Bind to Control", bind_wall_to_control_center); i+=25;
465
        wd->old_wall_num = -2;          // Set to some dummy value so everything works ok on the first frame.
466
 
467
        return window_event_result::handled;
468
}
469
 
470
void close_wall_window()
471
{
472
        if (MainWindow)
473
                ui_close_dialog(std::exchange(MainWindow, nullptr));
474
}
475
 
476
window_event_result wall_dialog_handler(UI_DIALOG *dlg,const d_event &event, wall_dialog *wd)
477
{
478
        switch(event.type)
479
        {
480
                case EVENT_WINDOW_CREATED:
481
                        return wall_dialog_created(dlg, wd);
482
                case EVENT_WINDOW_CLOSE:
483
                        std::default_delete<wall_dialog>()(wd);
484
                        MainWindow = nullptr;
485
                        return window_event_result::ignored;
486
                default:
487
                        break;
488
        }
489
        sbyte type;
490
        fix DeltaTime;
491
        fix64 Temp;
492
        int keypress = 0;
493
        window_event_result rval = window_event_result::ignored;
494
 
495
        if (event.type == EVENT_KEY_COMMAND)
496
                keypress = event_key_get(event);
497
 
498
        Assert(MainWindow != NULL);
499
 
500
        //------------------------------------------------------------
501
        // Call the ui code..
502
        //------------------------------------------------------------
503
        ui_button_any_drawn = 0;
504
 
505
        auto &Walls = LevelUniqueWallSubsystemState.Walls;
506
        auto &WallAnims = GameSharedState.WallAnims;
507
        auto &imwallptridx = Walls.imptridx;
508
        const auto &&w = imwallptridx(Cursegp->shared_segment::sides[Curside].wall_num);
509
        //------------------------------------------------------------
510
        // If we change walls, we need to reset the ui code for all
511
        // of the checkboxes that control the wall flags.  
512
        //------------------------------------------------------------
513
        if (wd->old_wall_num != w)
514
        {
515
                if (w)
516
                {
517
                        ui_checkbox_check(wd->doorFlag[0].get(), w->flags & WALL_DOOR_LOCKED);
518
                        ui_checkbox_check(wd->doorFlag[1].get(), w->flags & WALL_DOOR_AUTO);
519
                        ui_checkbox_check(wd->doorFlag[2].get(), w->flags & WALL_ILLUSION_OFF);
520
 
521
                        ui_radio_set_value(wd->keyFlag[0].get(), w->keys & KEY_NONE);
522
                        ui_radio_set_value(wd->keyFlag[1].get(), w->keys & KEY_BLUE);
523
                        ui_radio_set_value(wd->keyFlag[2].get(), w->keys & KEY_RED);
524
                        ui_radio_set_value(wd->keyFlag[3].get(), w->keys & KEY_GOLD);
525
                }
526
        }
527
 
528
        //------------------------------------------------------------
529
        // If any of the checkboxes that control the wallflags are set, then
530
        // update the corresponding wall flag.
531
        //------------------------------------------------------------
532
 
533
        if (w && w->type == WALL_DOOR)
534
        {
535
                if (GADGET_PRESSED(wd->doorFlag[0].get()))
536
                {
537
                        if ( wd->doorFlag[0]->flag == 1 )      
538
                                w->flags |= WALL_DOOR_LOCKED;
539
                        else
540
                                w->flags &= ~WALL_DOOR_LOCKED;
541
                        rval = window_event_result::handled;
542
                }
543
                else if (GADGET_PRESSED(wd->doorFlag[1].get()))
544
                {
545
                        if ( wd->doorFlag[1]->flag == 1 )      
546
                                w->flags |= WALL_DOOR_AUTO;
547
                        else
548
                                w->flags &= ~WALL_DOOR_AUTO;
549
                        rval = window_event_result::handled;
550
                }
551
 
552
                //------------------------------------------------------------
553
                // If any of the radio buttons that control the mode are set, then
554
                // update the corresponding key.
555
                //------------------------------------------------------------
556
                range_for (const int i, xrange(4u)) {
557
                        if (GADGET_PRESSED(wd->keyFlag[i].get()))
558
                        {
559
                                w->keys = 1<<i;         // Set the ai_state to the cooresponding radio button
560
                                rval = window_event_result::handled;
561
                        }
562
                }
563
        } else {
564
                range_for (auto &i, partial_const_range(wd->doorFlag, 2u))
565
                        ui_checkbox_check(i.get(), 0);
566
                range_for (auto &i, wd->keyFlag)
567
                        ui_radio_set_value(i.get(), 0);
568
        }
569
 
570
        if (w && w->type == WALL_ILLUSION) {
571
                if (GADGET_PRESSED(wd->doorFlag[2].get()))
572
                {
573
                        if ( wd->doorFlag[2]->flag == 1 )      
574
                                w->flags |= WALL_ILLUSION_OFF;
575
                        else
576
                                w->flags &= ~WALL_ILLUSION_OFF;
577
                        rval = window_event_result::handled;
578
                }
579
        } else
580
                for (   int i=2; i < 3; i++ )
581
                        if (wd->doorFlag[i]->flag == 1) {
582
                                wd->doorFlag[i]->flag = 0;              // Tells ui that this button isn't checked
583
                                wd->doorFlag[i]->status = 1;    // Tells ui to redraw button
584
                        }
585
 
586
        //------------------------------------------------------------
587
        // Draw the wall in the little 64x64 box
588
        //------------------------------------------------------------
589
        if (event.type == EVENT_UI_DIALOG_DRAW)
590
        {
591
                // A simple frame time counter for animating the walls...
592
                Temp = timer_query();
593
                DeltaTime = Temp - wd->time;
594
 
595
                gr_set_current_canvas( wd->wallViewBox->canvas );
596
                if (w) {
597
                        type = w->type;
598
                        if ((type == WALL_DOOR) || (type == WALL_BLASTABLE)) {
599
                                if (DeltaTime > ((F1_0*200)/1000)) {
600
                                        wd->framenum++;
601
                                        wd->time = Temp;
602
                                }
603
                                auto &wa = WallAnims[w->clip_num];
604
                                if (wd->framenum >= wa.num_frames)
605
                                        wd->framenum=0;
606
                                const auto frame = wa.frames[wd->framenum];
607
                                auto &texture = Textures[frame];
608
                                PIGGY_PAGE_IN(texture);
609
                                gr_ubitmap(*grd_curcanv, GameBitmaps[texture.index]);
610
                        } else {
611
                                if (type == WALL_OPEN)
612
                                        gr_clear_canvas(*grd_curcanv, CBLACK);
613
                                else {
614
                                        auto &curside = Cursegp->unique_segment::sides[Curside];
615
                                        const auto tmap_num = curside.tmap_num;
616
                                        if (curside.tmap_num2 > 0)
617
                                                gr_ubitmap(*grd_curcanv, texmerge_get_cached_bitmap(tmap_num, curside.tmap_num2));
618
                                        else    {
619
                                                PIGGY_PAGE_IN(Textures[tmap_num]);
620
                                                gr_ubitmap(*grd_curcanv, GameBitmaps[Textures[tmap_num].index]);
621
                                        }
622
                                }
623
                        }
624
                } else
625
                        gr_clear_canvas(*grd_curcanv, CGREY);
626
        }
627
 
628
        //------------------------------------------------------------
629
        // If anything changes in the ui system, redraw all the text that
630
        // identifies this wall.
631
        //------------------------------------------------------------
632
        if (event.type == EVENT_UI_DIALOG_DRAW)
633
        {
634
                if (w)  {
635
                        ui_dprintf_at( MainWindow, 12, 6, "Wall: %hi    ", static_cast<int16_t>(w));
636
                        switch (w->type) {
637
                                case WALL_NORMAL:
638
                                        ui_dprintf_at( MainWindow, 12, 23, " Type: Normal   " );
639
                                        break;
640
                                case WALL_BLASTABLE:
641
                                        ui_dprintf_at( MainWindow, 12, 23, " Type: Blastable" );
642
                                        break;
643
                                case WALL_DOOR:
644
                                        ui_dprintf_at( MainWindow, 12, 23, " Type: Door     " );
645
                                        ui_dputs_at( MainWindow, 223, 6, &WallAnims[w->clip_num].filename[0]);
646
                                        break;
647
                                case WALL_ILLUSION:
648
                                        ui_dprintf_at( MainWindow, 12, 23, " Type: Illusion " );
649
                                        break;
650
                                case WALL_OPEN:
651
                                        ui_dprintf_at( MainWindow, 12, 23, " Type: Open     " );
652
                                        break;
653
                                case WALL_CLOSED:
654
                                        ui_dprintf_at( MainWindow, 12, 23, " Type: Closed   " );
655
                                        break;
656
                                default:
657
                                        ui_dprintf_at( MainWindow, 12, 23, " Type: Unknown  " );
658
                                        break;
659
                        }                      
660
                        if (w->type != WALL_DOOR)
661
                                        ui_dprintf_at( MainWindow, 223, 6, "            " );
662
 
663
                        ui_dprintf_at( MainWindow, 12, 40, " Clip: %d   ", w->clip_num );
664
                        ui_dprintf_at( MainWindow, 12, 57, " Trigger: %d  ", w->trigger );
665
                }       else {
666
                        ui_dprintf_at( MainWindow, 12, 6, "Wall: none ");
667
                        ui_dprintf_at( MainWindow, 12, 23, " Type: none ");
668
                        ui_dprintf_at( MainWindow, 12, 40, " Clip: none   ");
669
                        ui_dprintf_at( MainWindow, 12, 57, " Trigger: none  ");
670
                }
671
        }
672
 
673
        if (ui_button_any_drawn || (wd->old_wall_num != w) )
674
                Update_flags |= UF_WORLD_CHANGED;
675
        if (GADGET_PRESSED(wd->quitButton.get()) || keypress == KEY_ESC)
676
        {
677
                return window_event_result::close;
678
        }              
679
 
680
        wd->old_wall_num = w;
681
 
682
        return rval;
683
}
684
 
685
 
686
//---------------------------------------------------------------------
687
 
688
// Restore all walls to original status (closed doors, repaired walls)
689
int wall_restore_all()
690
{
691
        auto &Walls = LevelUniqueWallSubsystemState.Walls;
692
        auto &WallAnims = GameSharedState.WallAnims;
693
        auto &vcwallptr = Walls.vcptr;
694
        auto &vmwallptr = Walls.vmptr;
695
        range_for (const auto &&wp, vmwallptr)
696
        {
697
                auto &w = *wp;
698
                if (w.flags & WALL_BLASTED) {
699
                        w.hps = WALL_HPS;
700
                }
701
                w.flags &= ~(WALL_BLASTED | WALL_DOOR_OPENED | WALL_DOOR_OPENING | WALL_EXPLODING);
702
        }
703
 
704
        auto &ActiveDoors = LevelUniqueWallSubsystemState.ActiveDoors;
705
        range_for (auto &&i, ActiveDoors.vmptr)
706
                wall_close_door_ref(Segments.vmptridx, Walls, WallAnims, i);
707
 
708
        range_for (auto &&i, vmsegptr)
709
                for (auto &&[us, ss] : zip(i->unique_segment::sides, i->shared_segment::sides))
710
                {
711
                        const auto wall_num = ss.wall_num;
712
                        if (wall_num != wall_none)
713
                        {
714
                                auto &w = *vcwallptr(wall_num);
715
                                if (w.type == WALL_BLASTABLE || w.type == WALL_DOOR)
716
                                        us.tmap_num2 = WallAnims[w.clip_num].frames[0];
717
                        }
718
                }
719
 
720
#if defined(DXX_BUILD_DESCENT_II)
721
        auto &Triggers = LevelUniqueWallSubsystemState.Triggers;
722
        auto &vmtrgptr = Triggers.vmptr;
723
        range_for (const auto i, vmtrgptr)
724
                i->flags &= ~trigger_behavior_flags::disabled;
725
#endif
726
        Update_flags |= UF_GAME_VIEW_CHANGED;
727
 
728
        return 1;
729
}
730
 
731
//---------------------------------------------------------------------
732
//      Remove a specific side.
733
int wall_remove_side(const vmsegptridx_t seg, short side)
734
{
735
        if (IS_CHILD(seg->children[side]) && seg->shared_segment::sides[side].wall_num != wall_none)
736
        {
737
                shared_segment &csegp = *vmsegptr(seg->children[side]);
738
                const auto Connectside = find_connect_side(seg, csegp);
739
 
740
                remove_trigger(seg, side);
741
                remove_trigger(csegp, Connectside);
742
 
743
                // Remove walls 'wall_num' and connecting side 'wall_num'
744
                //  from Walls array.  
745
                const auto wall0 = seg->shared_segment::sides[side].wall_num;
746
                const auto wall1 = csegp.sides[Connectside].wall_num;
747
                const auto lower_wallnum = (wall0 < wall1) ? wall0 : wall1;
748
 
749
                auto &Walls = LevelUniqueWallSubsystemState.Walls;
750
                auto &vcwallptr = Walls.vcptr;
751
                auto &vmwallptr = Walls.vmptr;
752
                {
753
                        const auto linked_wall = vcwallptr(lower_wallnum)->linked_wall;
754
                        if (linked_wall != wall_none)
755
                                vmwallptr(linked_wall)->linked_wall = wall_none;
756
                }
757
                {
758
                        const wallnum_t upper_wallnum = lower_wallnum + 1;
759
                        const auto linked_wall = vcwallptr(upper_wallnum)->linked_wall;
760
                        if (linked_wall != wall_none)
761
                                vmwallptr(linked_wall)->linked_wall = wall_none;
762
                }
763
 
764
                {
765
                        const auto num_walls = Walls.get_count();
766
                        auto &&sr = partial_const_range(Walls, static_cast<wallnum_t>(lower_wallnum + 2), num_walls);
767
                        std::move(sr.begin(), sr.end(), partial_range(Walls, lower_wallnum, num_walls - 2).begin());
768
                        Walls.set_count(num_walls - 2);
769
                }
770
 
771
                range_for (const auto &&segp, vmsegptr)
772
                {
773
                        if (segp->segnum != segment_none)
774
                                range_for (auto &w, segp->shared_segment::sides)
775
                                        if (w.wall_num != wall_none && w.wall_num > lower_wallnum+1)
776
                                                w.wall_num -= 2;
777
                }
778
 
779
                // Destroy any links to the deleted wall.
780
                auto &Triggers = LevelUniqueWallSubsystemState.Triggers;
781
                auto &vmtrgptr = Triggers.vmptr;
782
                range_for (const auto vt, vmtrgptr)
783
                {
784
                        auto &t = *vt;
785
                        for (int l=0;l < t.num_links;l++)
786
                                if (t.seg[l] == seg && t.side[l] == side) {
787
                                        for (int t1=0;t1 < t.num_links-1;t1++) {
788
                                                t.seg[t1] = t.seg[t1+1];
789
                                                t.side[t1] = t.side[t1+1];
790
                                        }
791
                                        t.num_links--; 
792
                                }
793
                }
794
 
795
                // Destroy control center links as well.
796
                for (int l=0;l<ControlCenterTriggers.num_links;l++)
797
                        if (ControlCenterTriggers.seg[l] == seg && ControlCenterTriggers.side[l] == side) {
798
                                for (int t1=0;t1<ControlCenterTriggers.num_links-1;t1++) {
799
                                        ControlCenterTriggers.seg[t1] = ControlCenterTriggers.seg[t1+1];
800
                                        ControlCenterTriggers.side[t1] = ControlCenterTriggers.side[t1+1];
801
                                }
802
                                ControlCenterTriggers.num_links--;     
803
                        }
804
 
805
                seg->shared_segment::sides[side].wall_num = wall_none;
806
                csegp.sides[Connectside].wall_num = wall_none;
807
 
808
                Update_flags |= UF_WORLD_CHANGED;
809
                return 1;
810
        }
811
 
812
        editor_status( "Can't remove wall.  No wall present.");
813
        return 0;
814
}
815
 
816
//---------------------------------------------------------------------
817
//      Remove a special wall.
818
int wall_remove()
819
{
820
        return wall_remove_side(Cursegp, Curside);
821
}
822
 
823
//---------------------------------------------------------------------
824
// Add a wall to curside
825
static int wall_add_to_side(fvcvertptr &vcvertptr, wall_array &Walls, const vmsegptridx_t segp, const unsigned side, const unsigned type)
826
{
827
        if (add_wall(vcvertptr, Walls, segp, side)) {
828
                const auto &&csegp = segp.absolute_sibling(segp->children[side]);
829
                auto connectside = find_connect_side(segp, csegp);
830
 
831
                auto &vmwallptr = Walls.vmptr;
832
                auto &w0 = *vmwallptr(segp->shared_segment::sides[side].wall_num);
833
                auto &w1 = *vmwallptr(csegp->shared_segment::sides[connectside].wall_num);
834
                w0.segnum = segp;
835
                w1.segnum = csegp;
836
 
837
                w0.sidenum = side;
838
                w1.sidenum = connectside;
839
 
840
                w0.flags = 0;
841
                w1.flags = 0;
842
 
843
                w0.type = type;
844
                w1.type = type;
845
 
846
                w0.clip_num = -1;
847
                w1.clip_num = -1;
848
 
849
                w0.keys = KEY_NONE;
850
                w1.keys = KEY_NONE;
851
 
852
                if (type == WALL_BLASTABLE) {
853
                        w0.hps = WALL_HPS;
854
                        w1.hps = WALL_HPS;
855
                        }      
856
 
857
                if (type != WALL_DOOR) {
858
                        segp->unique_segment::sides[side].tmap_num2 = 0;
859
                        csegp->unique_segment::sides[connectside].tmap_num2 = 0;
860
                        }
861
 
862
                if (type == WALL_DOOR) {
863
                        w0.flags |= WALL_DOOR_AUTO;
864
                        w1.flags |= WALL_DOOR_AUTO;
865
 
866
                        w0.clip_num = Current_door_type;
867
                        w1.clip_num = Current_door_type;
868
                }
869
 
870
                //Update_flags |= UF_WORLD_CHANGED;
871
                //return 1;
872
 
873
//              return NextWall();              //assign a clip num
874
                return wall_assign_door(Current_door_type);
875
 
876
        } else {
877
                editor_status( "Cannot add wall here, no children" );
878
                return 0;
879
        }
880
}
881
 
882
 
883
//---------------------------------------------------------------------
884
// Add a wall to markedside
885
int wall_add_to_markedside(fvcvertptr &vcvertptr, wall_array &Walls, const int8_t type)
886
{
887
        if (add_wall(vcvertptr, Walls, Markedsegp, Markedside)) {
888
                const auto &&csegp = vmsegptridx(Markedsegp->children[Markedside]);
889
                auto Connectside = find_connect_side(Markedsegp, csegp);
890
 
891
                const auto wall_num = Markedsegp->shared_segment::sides[Markedside].wall_num;
892
                const auto cwall_num = csegp->shared_segment::sides[Connectside].wall_num;
893
                auto &vmwallptr = Walls.vmptr;
894
                auto &w0 = *vmwallptr(wall_num);
895
                auto &w1 = *vmwallptr(cwall_num);
896
 
897
                w0.segnum = Markedsegp;
898
                w1.segnum = csegp;
899
 
900
                w0.sidenum = Markedside;
901
                w1.sidenum = Connectside;
902
 
903
                w0.flags = 0;
904
                w1.flags = 0;
905
 
906
                w0.type = type;
907
                w1.type = type;
908
 
909
                w0.trigger = trigger_none;
910
                w1.trigger = trigger_none;
911
 
912
                w0.clip_num = -1;
913
                w1.clip_num = -1;
914
 
915
                w0.keys = KEY_NONE;
916
                w1.keys = KEY_NONE;
917
 
918
                if (type == WALL_BLASTABLE) {
919
                        w0.hps = WALL_HPS;
920
                        w1.hps = WALL_HPS;
921
 
922
                        w0.clip_num = 0;
923
                        w1.clip_num = 0;
924
                        }      
925
 
926
                if (type != WALL_DOOR) {
927
                        Markedsegp->unique_segment::sides[Markedside].tmap_num2 = 0;
928
                        csegp->unique_segment::sides[Connectside].tmap_num2 = 0;
929
                        }
930
 
931
                Update_flags |= UF_WORLD_CHANGED;
932
                return 1;
933
        } else {
934
                editor_status( "Cannot add wall here, no children" );
935
                return 0;
936
        }
937
}
938
 
939
int bind_wall_to_control_center() {
940
 
941
        int link_num;
942
        if (Cursegp->shared_segment::sides[Curside].wall_num == wall_none) {
943
                editor_status("No wall at Curside.");
944
                return 0;
945
        }
946
 
947
        link_num = ControlCenterTriggers.num_links;
948
        for (int i=0;i<link_num;i++)
949
                if (Cursegp == ControlCenterTriggers.seg[i] && Curside == ControlCenterTriggers.side[i])
950
                {
951
                        editor_status("Curside already bound to Control Center.");
952
                        return 0;
953
                }
954
 
955
        // Error checking completed, actual binding begins
956
        ControlCenterTriggers.seg[link_num] = Cursegp;
957
        ControlCenterTriggers.side[link_num] = Curside;
958
        ControlCenterTriggers.num_links++;
959
 
960
        editor_status("Wall linked to control center");
961
 
962
        return 1;
963
}
964
 
965
//link two doors, curseg/curside and markedseg/markedside
966
int wall_link_doors()
967
{
968
        const auto cwall_num = Cursegp->shared_segment::sides[Curside].wall_num;
969
        auto &Walls = LevelUniqueWallSubsystemState.Walls;
970
        auto &imwallptr = Walls.imptr;
971
        const auto &&w1 = imwallptr(cwall_num);
972
 
973
        if (!w1 || w1->type != WALL_DOOR) {
974
                editor_status("Curseg/curside is not a door");
975
                return 0;
976
        }
977
 
978
        if (!Markedsegp) {
979
                editor_status("No marked side.");
980
                return 0;
981
        }
982
 
983
        const auto mwall_num = Markedsegp->shared_segment::sides[Markedside].wall_num;
984
        const auto &&w2 = imwallptr(mwall_num);
985
 
986
        if (!w2 || w2->type != WALL_DOOR) {
987
                editor_status("Markedseg/markedside is not a door");
988
                return 0;
989
        }
990
 
991
        if (w1->linked_wall != wall_none)
992
                editor_status("Curseg/curside is already linked");
993
 
994
        if (w2->linked_wall != wall_none)
995
                editor_status("Markedseg/markedside is already linked");
996
 
997
        w1->linked_wall = Markedsegp->shared_segment::sides[Markedside].wall_num;
998
        w2->linked_wall = Cursegp->shared_segment::sides[Curside].wall_num;
999
 
1000
        return 1;
1001
}
1002
 
1003
int wall_unlink_door()
1004
{
1005
        const auto cwall_num = Cursegp->shared_segment::sides[Curside].wall_num;
1006
        auto &Walls = LevelUniqueWallSubsystemState.Walls;
1007
        auto &imwallptr = Walls.imptr;
1008
        const auto &&w1 = imwallptr(cwall_num);
1009
 
1010
        if (!w1 || w1->type != WALL_DOOR) {
1011
                editor_status("Curseg/curside is not a door");
1012
                return 0;
1013
        }
1014
 
1015
        if (w1->linked_wall == wall_none)
1016
        {
1017
                editor_status("Curseg/curside is not linked");
1018
                return 0;
1019
        }
1020
 
1021
        auto &vmwallptr = Walls.vmptr;
1022
        auto &w2 = *vmwallptr(w1->linked_wall);
1023
        Assert(w2.linked_wall == cwall_num);
1024
 
1025
        w2.linked_wall = wall_none;
1026
        w1->linked_wall = wall_none;
1027
 
1028
        return 1;
1029
 
1030
}
1031
 
1032
int check_walls()
1033
{
1034
        auto &RobotCenters = LevelSharedRobotcenterState.RobotCenters;
1035
        std::array<count_wall, MAX_WALLS> CountedWalls;
1036
        int matcen_num;
1037
 
1038
        unsigned wall_count = 0;
1039
        range_for (const auto &&segp, vmsegptridx)
1040
        {
1041
                if (segp->segnum != segment_none) {
1042
                        // Check fuelcenters
1043
                        matcen_num = segp->matcen_num;
1044
                        if (matcen_num == 0)
1045
                                if (RobotCenters[0].segnum != segp) {
1046
                                        segp->matcen_num = -1;
1047
                                }
1048
 
1049
                        if (matcen_num > -1)
1050
                                        RobotCenters[matcen_num].segnum = segp;
1051
 
1052
                        range_for (auto &&e, enumerate(segp->shared_segment::sides))
1053
                        {
1054
                                auto &s = e.value;
1055
                                if (s.wall_num != wall_none) {
1056
                                        CountedWalls[wall_count].wallnum = s.wall_num;
1057
                                        CountedWalls[wall_count].segnum = segp;
1058
                                        CountedWalls[wall_count].sidenum = e.idx;
1059
                                        wall_count++;
1060
                                }
1061
                        }
1062
                }
1063
        }
1064
 
1065
        auto &Walls = LevelUniqueWallSubsystemState.Walls;
1066
        if (wall_count != Walls.get_count()) {
1067
                if (ui_messagebox(-2, -2, 2, "Num_walls is bogus\nDo you wish to correct it?\n", "Yes", "No") == 1)
1068
                {
1069
                        Walls.set_count(wall_count);
1070
                        editor_status_fmt("Num_walls set to %d\n", Walls.get_count());
1071
                }
1072
        }
1073
 
1074
        // Check validity of Walls array.
1075
        auto &vmwallptr = Walls.vmptr;
1076
        range_for (auto &cw, partial_const_range(CountedWalls, Walls.get_count()))
1077
        {
1078
                auto &w = *vmwallptr(cw.wallnum);
1079
                if (w.segnum != cw.segnum || w.sidenum != cw.sidenum)
1080
                {
1081
                        if (ui_messagebox( -2, -2, 2, "Unmatched wall detected\nDo you wish to correct it?\n", "Yes", "No") == 1)
1082
                        {
1083
                                w.segnum = cw.segnum;
1084
                                w.sidenum = cw.sidenum;
1085
                        }
1086
                }
1087
        }
1088
 
1089
        const auto &&used_walls = partial_const_range(Walls, wall_count);
1090
        const auto predicate = [](const wall &w) {
1091
                return w.trigger != trigger_none;
1092
        };
1093
        unsigned trigger_count = std::count_if(used_walls.begin(), used_walls.end(), predicate);
1094
 
1095
        auto &Triggers = LevelUniqueWallSubsystemState.Triggers;
1096
        if (trigger_count != Triggers.get_count()) {
1097
                if (ui_messagebox(-2, -2, 2, "Num_triggers is bogus\nDo you wish to correct it?\n", "Yes", "No") == 1)
1098
                {
1099
                        Triggers.set_count(trigger_count);
1100
                        editor_status_fmt("Num_triggers set to %d\n", Triggers.get_count());
1101
                }
1102
        }
1103
 
1104
        return 1;
1105
 
1106
}
1107
 
1108
 
1109
int delete_all_walls()
1110
{
1111
        if (ui_messagebox(-2, -2, 2, "Are you sure that walls are hosed so\n badly that you want them ALL GONE!?\n", "YES!", "No") == 1)
1112
        {
1113
                range_for (shared_segment &segp, vmsegptr)
1114
                {
1115
                        range_for (auto &side, segp.sides)
1116
                                side.wall_num = wall_none;
1117
                }
1118
                auto &Triggers = LevelUniqueWallSubsystemState.Triggers;
1119
                auto &Walls = LevelUniqueWallSubsystemState.Walls;
1120
                Walls.set_count(0);
1121
                Triggers.set_count(0);
1122
 
1123
                return 1;
1124
        }
1125
 
1126
        return 0;
1127
}
1128
 
1129
// ------------------------------------------------------------------------------------------------
1130
static void copy_old_wall_data_to_new(wallnum_t owall, wallnum_t nwall)
1131
{
1132
        auto &Walls = LevelUniqueWallSubsystemState.Walls;
1133
        auto &o = *Walls.vcptr(owall);
1134
        auto &n = *Walls.vmptr(nwall);
1135
        n.flags = o.flags;
1136
        n.type = o.type;
1137
        n.clip_num = o.clip_num;
1138
        n.keys = o.keys;
1139
        n.hps = o.hps;
1140
        n.state = o.state;
1141
        n.linked_wall = wall_none;
1142
 
1143
        n.trigger = trigger_none;
1144
        if (o.trigger != trigger_none)
1145
        {
1146
                editor_status("Warning: Trigger not copied in group copy.");
1147
        }
1148
}
1149
 
1150
// ------------------------------------------------------------------------------------------------
1151
void copy_group_walls(int old_group, int new_group)
1152
{
1153
        group::segment_array_type_t::const_iterator bn = GroupList[new_group].segments.begin();
1154
 
1155
        auto &Walls = LevelUniqueWallSubsystemState.Walls;
1156
        auto &vmwallptr = Walls.vmptr;
1157
        range_for (const auto old_seg, GroupList[old_group].segments)
1158
        {
1159
                const auto new_seg = *bn++;
1160
                auto &os = vcsegptr(old_seg)->shared_segment::sides;
1161
                auto &ns = vmsegptr(new_seg)->shared_segment::sides;
1162
                for (int j=0; j<MAX_SIDES_PER_SEGMENT; j++) {
1163
                        if (os[j].wall_num != wall_none) {
1164
                                ns[j].wall_num = Walls.get_count();
1165
                                copy_old_wall_data_to_new(os[j].wall_num, Walls.get_count());
1166
                                auto &w = *vmwallptr(static_cast<wallnum_t>(Walls.get_count()));
1167
                                w.segnum = new_seg;
1168
                                w.sidenum = j;
1169
                                Walls.set_count(Walls.get_count() + 1);
1170
                                Assert(Walls.get_count() < MAX_WALLS);
1171
                        }
1172
                }
1173
        }
1174
}
1175
 
1176
static int Validate_walls=1;
1177
 
1178
//      --------------------------------------------------------------------------------------------------------
1179
//      This function should be in medwall.c.
1180
//      Make sure all wall/segment connections are valid.
1181
void check_wall_validity(void)
1182
{
1183
        int sidenum;
1184
 
1185
        if (!Validate_walls)
1186
                return;
1187
 
1188
        auto &Walls = LevelUniqueWallSubsystemState.Walls;
1189
        auto &vcwallptr = Walls.vcptr;
1190
        range_for (const auto &&w, vcwallptr)
1191
        {
1192
                segnum_t        segnum;
1193
                segnum = w->segnum;
1194
                sidenum = w->sidenum;
1195
 
1196
                if (vcwallptr(vcsegptr(segnum)->shared_segment::sides[sidenum].wall_num) != w) {
1197
                        if (!Validate_walls)
1198
                                return;
1199
                        Int3();         //      Error! Your mine has been invalidated!
1200
                                                        // Do not continue!  Do not save!
1201
                                                        //      Remember your last action and Contact Mike!
1202
                                                        //      To continue, set the variable Validate_walls to 1 by doing:
1203
                                                        //              /Validate_walls = 1
1204
                                                        //      Then do the usual /eip++;g
1205
 
1206
                }
1207
        }
1208
 
1209
        std::array<bool, MAX_WALLS> wall_flags{};
1210
 
1211
        range_for (const auto &&segp, vmsegptridx)
1212
        {
1213
                if (segp->segnum != segment_none)
1214
                        for (int j=0; j<MAX_SIDES_PER_SEGMENT; j++) {
1215
                                // Check walls
1216
                                auto wall_num = segp->shared_segment::sides[j].wall_num;
1217
                                if (wall_num != wall_none) {
1218
                                        if (wall_flags[wall_num] != 0) {
1219
                                                if (!Validate_walls)
1220
                                                        return;
1221
                                                Int3();         //      Error! Your mine has been invalidated!
1222
                                                                                // Do not continue!  Do not save!
1223
                                                                                //      Remember your last action and Contact Mike!
1224
                                                                                //      To continue, set the variable Validate_walls to 1 by doing:
1225
                                                                                //              /Validate_walls = 1
1226
                                                                                //      Then do the usual /eip++;g
1227
                                        }
1228
 
1229
                                        auto &w = *vcwallptr(wall_num);
1230
                                        if (w.segnum != segp || w.sidenum != j)
1231
                                        {
1232
                                                if (!Validate_walls)
1233
                                                        return;
1234
                                                Int3();         //      Error! Your mine has been invalidated!
1235
                                                                                // Do not continue!  Do not save!
1236
                                                                                //      Remember your last action and Contact Mike!
1237
                                                                                //      To continue, set the variable Validate_walls to 1 by doing:
1238
                                                                                //              /Validate_walls = 1
1239
                                                                                //      Then do the usual /eip++;g
1240
                                        }
1241
 
1242
                                        wall_flags[wall_num] = 1;
1243
                                }
1244
                        }
1245
 
1246
        }
1247
}
1248