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-1999 PARALLAX SOFTWARE CORPORATION.  ALL RIGHTS RESERVED.
18
*/
19
 
20
#include "wall.h"
21
#include "player.h"
22
#include "text.h"
23
#include "fireball.h"
24
#include "textures.h"
25
#include "newdemo.h"
26
#include "multi.h"
27
#include "gameseq.h"
28
#include "physfs-serial.h"
29
#include "gameseg.h"
30
#include "hudmsg.h"
31
#include "laser.h"              //      For seeing if a flare is stuck in a wall.
32
#include "effects.h"
33
 
34
#include "d_enumerate.h"
35
#include "d_range.h"
36
#include "compiler-range_for.h"
37
#include "segiter.h"
38
#include "d_zip.h"
39
 
40
//      Special door on boss level which is locked if not in multiplayer...sorry for this awful solution --MK.
41
#define BOSS_LOCKED_DOOR_LEVEL  7
42
#define BOSS_LOCKED_DOOR_SEG            595
43
#define BOSS_LOCKED_DOOR_SIDE   5
44
 
45
namespace dcx {
46
unsigned Num_wall_anims;
47
}
48
 
49
namespace dsx {
50
 
51
namespace {
52
 
53
struct ad_removal_predicate
54
{
55
        bool operator()(active_door &) const;
56
};
57
 
58
struct find_active_door_predicate
59
{
60
        const wallnum_t wall_num;
61
        explicit find_active_door_predicate(const wallnum_t i) :
62
                wall_num(i)
63
        {
64
        }
65
        bool operator()(active_door &d) const {
66
                if (d.front_wallnum[0] == wall_num)
67
                        return true;
68
                if (d.back_wallnum[0] == wall_num)
69
                        return true;
70
                if (d.n_parts != 2)
71
                        return false;
72
                if (d.front_wallnum[1] == wall_num)
73
                        return true;
74
                if (d.back_wallnum[1] == wall_num)
75
                        return true;
76
                return false;
77
        }
78
};
79
 
80
}
81
 
82
}
83
#if defined(DXX_BUILD_DESCENT_II)
84
#include "collide.h"
85
namespace dsx {
86
constexpr std::integral_constant<unsigned, F1_0> CLOAKING_WALL_TIME{};
87
 
88
namespace {
89
 
90
struct cwframe
91
{
92
        wall &w;
93
        std::array<uvl, 4> &uvls;
94
        cwframe(fvmsegptr &vmsegptr, wall &wr) :
95
                w(wr),
96
                uvls(vmsegptr(w.segnum)->unique_segment::sides[w.sidenum].uvls)
97
        {
98
        }
99
};
100
 
101
struct cwresult
102
{
103
        bool remove;
104
        bool record;
105
        cwresult() = default;
106
        explicit cwresult(bool r) :
107
                remove(false), record(r)
108
        {
109
        }
110
};
111
 
112
struct cw_removal_predicate
113
{
114
        fvmsegptr &vmsegptr;
115
        wall_array &Walls;
116
        unsigned num_cloaking_walls = 0;
117
        bool operator()(cloaking_wall &d);
118
        cw_removal_predicate(fvmsegptr &v, wall_array &w) :
119
                vmsegptr(v), Walls(w)
120
        {
121
        }
122
};
123
 
124
struct find_cloaked_wall_predicate
125
{
126
        const vmwallidx_t w;
127
        find_cloaked_wall_predicate(const vmwallidx_t i) :
128
                w(i)
129
        {
130
        }
131
        bool operator()(const cloaking_wall &cw) const
132
        {
133
                return cw.front_wallnum == w || cw.back_wallnum == w;
134
        }
135
};
136
 
137
}
138
 
139
}
140
#endif
141
 
142
static std::pair<uint_fast32_t, uint_fast32_t> get_transparency_check_values(const unique_side &side)
143
{
144
        if (const uint_fast32_t masked_tmap_num2 = side.tmap_num2 & 0x3FFF)
145
                return {masked_tmap_num2, BM_FLAG_SUPER_TRANSPARENT};
146
        return {side.tmap_num, BM_FLAG_TRANSPARENT};
147
}
148
 
149
// This function determines whether the current segment/side is transparent
150
//              1 = YES
151
//              0 = NO
152
static uint_fast32_t check_transparency(const GameBitmaps_array &GameBitmaps, const Textures_array &Textures, const unique_side &side)
153
{
154
        const auto &&v = get_transparency_check_values(side);
155
        return GameBitmaps[Textures[v.first].index].get_flag_mask(v.second);
156
}
157
 
158
//-----------------------------------------------------------------
159
// This function checks whether we can fly through the given side.
160
//      In other words, whether or not we have a 'doorway'
161
//       Flags:
162
//              WID_FLY_FLAG                            1
163
//              WID_RENDER_FLAG                 2
164
//              WID_RENDPAST_FLAG                       4
165
//       Return values:
166
//              WID_WALL                                                2       // 0/1/0                wall    
167
//              WID_TRANSPARENT_WALL            6       //      0/1/1           transparent wall
168
//              WID_ILLUSORY_WALL                       3       //      1/1/0           illusory wall
169
//              WID_TRANSILLUSORY_WALL  7       //      1/1/1           transparent illusory wall
170
//              WID_NO_WALL                                     5       //      1/0/1           no wall, can fly through
171
namespace dsx {
172
 
173
static WALL_IS_DOORWAY_result_t wall_is_doorway(const GameBitmaps_array &GameBitmaps, const Textures_array &Textures, fvcwallptr &vcwallptr, const shared_side &sside, const unique_side &uside)
174
{
175
        auto &w = *vcwallptr(sside.wall_num);
176
        const auto type = w.type;
177
        if (type == WALL_OPEN)
178
                return WID_NO_WALL;
179
 
180
#if defined(DXX_BUILD_DESCENT_II)
181
        if (unlikely(type == WALL_CLOAKED))
182
                return WID_CLOAKED_WALL;
183
#endif
184
 
185
        const auto flags = w.flags;
186
        if (type == WALL_ILLUSION) {
187
                if (flags & WALL_ILLUSION_OFF)
188
                        return WID_NO_WALL;
189
                else {
190
                        if (check_transparency(GameBitmaps, Textures, uside))
191
                                return WID_TRANSILLUSORY_WALL;
192
                        else
193
                                return WID_ILLUSORY_WALL;
194
                }
195
        }
196
 
197
        if (type == WALL_BLASTABLE) {
198
                if (flags & WALL_BLASTED)
199
                        return WID_TRANSILLUSORY_WALL;
200
        }      
201
        else
202
        {
203
        if (unlikely(flags & WALL_DOOR_OPENED))
204
                return WID_TRANSILLUSORY_WALL;
205
        if (likely(type == WALL_DOOR) && unlikely(w.state == WALL_DOOR_OPENING))
206
                return WID_TRANSPARENT_WALL;
207
        }
208
// If none of the above flags are set, there is no doorway.
209
        if (check_transparency(GameBitmaps, Textures, uside))
210
                return WID_TRANSPARENT_WALL;
211
        else
212
                return WID_WALL; // There are children behind the door.
213
}
214
 
215
WALL_IS_DOORWAY_result_t WALL_IS_DOORWAY(const GameBitmaps_array &GameBitmaps, const Textures_array &Textures, fvcwallptr &vcwallptr, const cscusegment seg, const uint_fast32_t side)
216
{
217
        const auto child = seg.s.children[side];
218
        if (unlikely(child == segment_none))
219
                return WID_WALL;
220
        if (unlikely(child == segment_exit))
221
                return WID_EXTERNAL;
222
        auto &sside = seg.s.sides[side];
223
        if (likely(sside.wall_num == wall_none))
224
                return WID_NO_WALL;
225
        auto &uside = seg.u.sides[side];
226
        return wall_is_doorway(GameBitmaps, Textures, vcwallptr, sside, uside);
227
}
228
 
229
}
230
 
231
#if DXX_USE_EDITOR
232
//-----------------------------------------------------------------
233
// Initializes all the walls (in other words, no special walls)
234
namespace dsx {
235
void wall_init()
236
{
237
        init_exploding_walls();
238
        auto &Walls = LevelUniqueWallSubsystemState.Walls;
239
        Walls.set_count(0);
240
        range_for (auto &w, Walls)
241
        {
242
                w.segnum = segment_none;
243
                w.sidenum = -1;
244
                w.type = WALL_NORMAL;
245
                w.flags = 0;
246
                w.hps = 0;
247
                w.trigger = -1;
248
                w.clip_num = -1;
249
                w.linked_wall = -1;
250
        }
251
        auto &ActiveDoors = LevelUniqueWallSubsystemState.ActiveDoors;
252
        ActiveDoors.set_count(0);
253
#if defined(DXX_BUILD_DESCENT_II)
254
        auto &CloakingWalls = LevelUniqueWallSubsystemState.CloakingWalls;
255
        CloakingWalls.set_count(0);
256
#endif
257
 
258
}
259
}
260
#endif
261
 
262
//set the tmap_num or tmap_num2 field for a wall/door
263
void wall_set_tmap_num(const wclip &anim, const vmsegptridx_t seg, const unsigned side, const vmsegptridx_t csegp, const unsigned cside, const unsigned frame_num)
264
{
265
        const auto newdemo_state = Newdemo_state;
266
        if (newdemo_state == ND_STATE_PLAYBACK)
267
                return;
268
 
269
        const auto tmap = anim.frames[frame_num];
270
        auto &uside = seg->unique_segment::sides[side];
271
        auto &cuside = csegp->unique_segment::sides[cside];
272
        if (anim.flags & WCF_TMAP1)     {
273
                if (tmap != uside.tmap_num || tmap != cuside.tmap_num)
274
                {
275
                        uside.tmap_num = cuside.tmap_num = tmap;
276
                        if (newdemo_state == ND_STATE_RECORDING)
277
                                newdemo_record_wall_set_tmap_num1(seg,side,csegp,cside,tmap);
278
                }
279
        } else  {
280
                assert(tmap != 0 && uside.tmap_num2 != 0);
281
                if (tmap != uside.tmap_num2 || tmap != cuside.tmap_num2)
282
                {
283
                        uside.tmap_num2 = cuside.tmap_num2 = tmap;
284
                        if (newdemo_state == ND_STATE_RECORDING)
285
                                newdemo_record_wall_set_tmap_num2(seg,side,csegp,cside,tmap);
286
                }
287
        }
288
}
289
 
290
 
291
// -------------------------------------------------------------------------------
292
//when the wall has used all its hitpoints, this will destroy it
293
static void blast_blastable_wall(const vmsegptridx_t seg, const unsigned side, wall &w0)
294
{
295
        auto &LevelSharedVertexState = LevelSharedSegmentState.get_vertex_state();
296
        auto &Objects = LevelUniqueObjectState.Objects;
297
        auto &Vertices = LevelSharedVertexState.get_vertices();
298
        auto &vmobjptr = Objects.vmptr;
299
        auto &sside = seg->shared_segment::sides[side];
300
        const auto wall_num = sside.wall_num;
301
        w0.hps = -1;    //say it's blasted
302
 
303
        const auto &&csegp = seg.absolute_sibling(seg->children[side]);
304
        auto Connectside = find_connect_side(seg, csegp);
305
        Assert(Connectside != side_none);
306
        const auto cwall_num = csegp->shared_segment::sides[Connectside].wall_num;
307
        auto &Walls = LevelUniqueWallSubsystemState.Walls;
308
        auto &WallAnims = GameSharedState.WallAnims;
309
        auto &imwallptr = Walls.imptr;
310
        const auto &&w1 = imwallptr(cwall_num);
311
        if (w1)
312
                LevelUniqueStuckObjectState.kill_stuck_objects(vmobjptr, cwall_num);
313
        LevelUniqueStuckObjectState.kill_stuck_objects(vmobjptr, wall_num);
314
        flush_fcd_cache();
315
 
316
        const auto a = w0.clip_num;
317
        auto &wa = WallAnims[a];
318
        //if this is an exploding wall, explode it
319
        if (wa.flags & WCF_EXPLODES)
320
        {
321
                auto &vcvertptr = Vertices.vcptr;
322
                explode_wall(vcvertptr, seg, side, w0);
323
        }
324
        else {
325
                //if not exploding, set final frame, and make door passable
326
                const auto n = wa.num_frames;
327
                w0.flags |= WALL_BLASTED;
328
                if (w1)
329
                        w1->flags |= WALL_BLASTED;
330
                wall_set_tmap_num(wa, seg, side, csegp, Connectside, n - 1);
331
        }
332
 
333
}
334
 
335
 
336
//-----------------------------------------------------------------
337
// Destroys a blastable wall.
338
void wall_destroy(const vmsegptridx_t seg, const unsigned side)
339
{
340
        auto &Walls = LevelUniqueWallSubsystemState.Walls;
341
        auto &vmwallptr = Walls.vmptr;
342
        auto &w = *vmwallptr(seg->shared_segment::sides[side].wall_num);
343
        if (w.type == WALL_BLASTABLE)
344
                blast_blastable_wall(seg, side, w);
345
        else
346
                Error("Hey bub, you are trying to destroy an indestructable wall.");
347
}
348
 
349
//-----------------------------------------------------------------
350
// Deteriorate appearance of wall. (Changes bitmap (paste-ons))
351
void wall_damage(const vmsegptridx_t seg, const unsigned side, fix damage)
352
{
353
        auto &WallAnims = GameSharedState.WallAnims;
354
        int i;
355
 
356
        auto &sside = seg->shared_segment::sides[side];
357
        const auto wall_num = sside.wall_num;
358
        if (wall_num == wall_none) {
359
                return;
360
        }
361
 
362
        auto &Walls = LevelUniqueWallSubsystemState.Walls;
363
        auto &vmwallptr = Walls.vmptr;
364
        auto &w0 = *vmwallptr(wall_num);
365
        if (w0.type != WALL_BLASTABLE)
366
                return;
367
 
368
        if (!(w0.flags & WALL_BLASTED) && w0.hps >= 0)
369
                {
370
                const auto &&csegp = seg.absolute_sibling(seg->children[side]);
371
                auto Connectside = find_connect_side(seg, csegp);
372
                Assert(Connectside != side_none);
373
                const auto cwall_num = csegp->shared_segment::sides[Connectside].wall_num;
374
                auto &imwallptr = Walls.imptr;
375
                if (const auto &&w1p = imwallptr(cwall_num))
376
                {
377
                        auto &w1 = *w1p;
378
                        w1.hps -= damage;
379
                }
380
                w0.hps -= damage;
381
 
382
                const auto a = w0.clip_num;
383
                const auto n = WallAnims[a].num_frames;
384
 
385
                if (w0.hps < WALL_HPS*1/n) {
386
                        blast_blastable_wall(seg, side, w0);
387
                        if (Game_mode & GM_MULTI)
388
                                multi_send_door_open(seg, side, w0.flags);
389
                }
390
                else
391
                        for (i=0;i<n;i++)
392
                                if (w0.hps < WALL_HPS*(n-i)/n)
393
                                {
394
                                        wall_set_tmap_num(WallAnims[a], seg, side, csegp, Connectside, i);
395
                                }
396
                }
397
}
398
 
399
 
400
//-----------------------------------------------------------------
401
// Opens a door
402
namespace dsx {
403
void wall_open_door(const vmsegptridx_t seg, const unsigned side)
404
{
405
        auto &LevelSharedVertexState = LevelSharedSegmentState.get_vertex_state();
406
        auto &Objects = LevelUniqueObjectState.Objects;
407
        auto &Vertices = LevelSharedVertexState.get_vertices();
408
        auto &WallAnims = GameSharedState.WallAnims;
409
        auto &vmobjptr = Objects.vmptr;
410
        active_door *d;
411
 
412
        auto &sside = seg->shared_segment::sides[side];
413
        const auto wall_num = sside.wall_num;
414
        auto &Walls = LevelUniqueWallSubsystemState.Walls;
415
        auto &vmwallptr = Walls.vmptr;
416
        wall *const w = vmwallptr(wall_num);
417
        LevelUniqueStuckObjectState.kill_stuck_objects(vmobjptr, wall_num);
418
 
419
        if ((w->state == WALL_DOOR_OPENING) ||          //already opening
420
                 (w->state == WALL_DOOR_WAITING))               //open, waiting to close
421
                return;
422
#if defined(DXX_BUILD_DESCENT_II)
423
        if (w->state == WALL_DOOR_OPEN)                 //open, & staying open
424
                return;
425
#endif
426
 
427
        auto &ActiveDoors = LevelUniqueWallSubsystemState.ActiveDoors;
428
        auto &vmactdoorptr = ActiveDoors.vmptr;
429
        if (w->state == WALL_DOOR_CLOSING) {            //closing, so reuse door
430
                const auto &&r = make_range(vmactdoorptr);
431
                const auto &&i = std::find_if(r.begin(), r.end(), find_active_door_predicate(wall_num));
432
                if (i == r.end())       // likely in demo playback or multiplayer
433
                {
434
                        const auto c = ActiveDoors.get_count();
435
                        ActiveDoors.set_count(c + 1);
436
                        d = vmactdoorptr(static_cast<actdoornum_t>(c));
437
                        d->time = 0;
438
                }
439
                else
440
                {
441
                        d = *i;
442
                        d->time = WallAnims[w->clip_num].play_time - d->time;
443
                        if (d->time < 0)
444
                                d->time = 0;
445
                }
446
        }
447
        else {                                                                                  //create new door
448
                Assert(w->state == WALL_DOOR_CLOSED);
449
                const auto i = ActiveDoors.get_count();
450
                ActiveDoors.set_count(i + 1);
451
                d = vmactdoorptr(static_cast<actdoornum_t>(i));
452
                d->time = 0;
453
        }
454
 
455
 
456
        w->state = WALL_DOOR_OPENING;
457
 
458
        // So that door can't be shot while opening
459
        const auto &&csegp = vcsegptr(seg->children[side]);
460
        auto Connectside = find_connect_side(seg, csegp);
461
        if (Connectside != side_none)
462
        {
463
                const auto cwall_num = csegp->shared_segment::sides[Connectside].wall_num;
464
                auto &imwallptr = Walls.imptr;
465
                if (const auto &&w1 = imwallptr(cwall_num))
466
                {
467
                        w1->state = WALL_DOOR_OPENING;
468
                        d->back_wallnum[0] = cwall_num;
469
                }
470
                d->front_wallnum[0] = seg->shared_segment::sides[side].wall_num;
471
        }
472
        else
473
                con_printf(CON_URGENT, "Illegal Connectside %i in wall_open_door. Trying to hop over. Please check your level!", side);
474
 
475
        if (Newdemo_state == ND_STATE_RECORDING) {
476
                newdemo_record_door_opening(seg, side);
477
        }
478
 
479
        if (w->linked_wall != wall_none)
480
        {
481
                wall *const w2 = vmwallptr(w->linked_wall);
482
 
483
                Assert(w2->linked_wall == seg->shared_segment::sides[side].wall_num);
484
                //Assert(!(w2->flags & WALL_DOOR_OPENING  ||  w2->flags & WALL_DOOR_OPENED));
485
 
486
                w2->state = WALL_DOOR_OPENING;
487
 
488
                const auto &&seg2 = vcsegptridx(w2->segnum);
489
                const auto &&csegp2 = vcsegptr(seg2->children[w2->sidenum]);
490
                Connectside = find_connect_side(seg2, csegp2);
491
                Assert(Connectside != side_none);
492
                const auto cwall_num = csegp2->shared_segment::sides[Connectside].wall_num;
493
                auto &imwallptr = Walls.imptr;
494
                if (const auto &&w3 = imwallptr(cwall_num))
495
                        w3->state = WALL_DOOR_OPENING;
496
 
497
                d->n_parts = 2;
498
                d->front_wallnum[1] = w->linked_wall;
499
                d->back_wallnum[1] = cwall_num;
500
        }
501
        else
502
                d->n_parts = 1;
503
 
504
 
505
        if ( Newdemo_state != ND_STATE_PLAYBACK )
506
        {
507
                // NOTE THE LINK TO ABOVE!!!!
508
                auto &vcvertptr = Vertices.vcptr;
509
                const auto &&cp = compute_center_point_on_side(vcvertptr, seg, side);
510
                const auto open_sound = WallAnims[w->clip_num].open_sound;
511
                if (open_sound > -1)
512
                        digi_link_sound_to_pos(open_sound, seg, side, cp, 0, F1_0);
513
 
514
        }
515
}
516
}
517
 
518
#if defined(DXX_BUILD_DESCENT_II)
519
namespace dsx {
520
 
521
//-----------------------------------------------------------------
522
// start the transition from closed -> open wall
523
void start_wall_cloak(const vmsegptridx_t seg, const unsigned side)
524
{
525
        auto &LevelSharedVertexState = LevelSharedSegmentState.get_vertex_state();
526
        auto &Vertices = LevelSharedVertexState.get_vertices();
527
        cloaking_wall *d;
528
 
529
        if ( Newdemo_state==ND_STATE_PLAYBACK ) return;
530
 
531
        auto &Walls = LevelUniqueWallSubsystemState.Walls;
532
        const auto &&w = Walls.vmptridx(seg->shared_segment::sides[side].wall_num);
533
 
534
        if (w->type == WALL_OPEN || w->state == WALL_DOOR_CLOAKING)             //already open or cloaking
535
                return;
536
 
537
        const auto &&csegp = vcsegptr(seg->children[side]);
538
        auto Connectside = find_connect_side(seg, csegp);
539
        Assert(Connectside != side_none);
540
        const auto cwall_num = csegp->shared_segment::sides[Connectside].wall_num;
541
 
542
        auto &CloakingWalls = LevelUniqueWallSubsystemState.CloakingWalls;
543
        if (w->state == WALL_DOOR_DECLOAKING) { //decloaking, so reuse door
544
                const auto &&r = make_range(CloakingWalls.vmptr);
545
                const auto i = std::find_if(r.begin(), r.end(), find_cloaked_wall_predicate(w));
546
                if (i == r.end())
547
                {
548
                        d_debugbreak();
549
                        return;
550
                }
551
                d = *i;
552
                d->time = CLOAKING_WALL_TIME - d->time;
553
        }
554
        else if (w->state == WALL_DOOR_CLOSED) {        //create new door
555
                const clwallnum_t c = CloakingWalls.get_count();
556
                if (c >= CloakingWalls.size())
557
                {
558
                        Int3();         //ran out of cloaking wall slots
559
                        w->type = WALL_OPEN;
560
                        if (const auto &&w1 = Walls.imptr(cwall_num))
561
                                w1->type = WALL_OPEN;
562
                        return;
563
                }
564
                CloakingWalls.set_count(c + 1);
565
                d = CloakingWalls.vmptr(c);
566
                d->time = 0;
567
        }
568
        else {
569
                Int3();         //unexpected wall state
570
                return;
571
        }
572
 
573
        w->state = WALL_DOOR_CLOAKING;
574
        if (const auto &&w1 = Walls.imptr(cwall_num))
575
                w1->state = WALL_DOOR_CLOAKING;
576
 
577
        d->front_wallnum = seg->shared_segment::sides[side].wall_num;
578
        d->back_wallnum = cwall_num;
579
        Assert(w->linked_wall == wall_none);
580
 
581
        if ( Newdemo_state != ND_STATE_PLAYBACK ) {
582
                auto &vcvertptr = Vertices.vcptr;
583
                const auto &&cp = compute_center_point_on_side(vcvertptr, seg, side);
584
                digi_link_sound_to_pos( SOUND_WALL_CLOAK_ON, seg, side, cp, 0, F1_0 );
585
        }
586
 
587
        for (auto &&[front_ls, back_ls, s0_uvls, s1_uvls] : zip(
588
                        d->front_ls,
589
                        d->back_ls,
590
                        seg->unique_segment::sides[side].uvls,
591
                        csegp->unique_segment::sides[Connectside].uvls
592
        ))
593
        {
594
                front_ls = s0_uvls.l;
595
                back_ls = s1_uvls.l;
596
        }
597
}
598
 
599
//-----------------------------------------------------------------
600
// start the transition from open -> closed wall
601
void start_wall_decloak(const vmsegptridx_t seg, const unsigned side)
602
{
603
        auto &LevelSharedVertexState = LevelSharedSegmentState.get_vertex_state();
604
        auto &Vertices = LevelSharedVertexState.get_vertices();
605
        cloaking_wall *d;
606
 
607
        if ( Newdemo_state==ND_STATE_PLAYBACK ) return;
608
 
609
        auto &sside = seg->shared_segment::sides[side];
610
        assert(sside.wall_num != wall_none);    //Opening door on illegal wall
611
 
612
        auto &Walls = LevelUniqueWallSubsystemState.Walls;
613
        const auto &&w = Walls.vmptridx(sside.wall_num);
614
 
615
        if (w->type == WALL_CLOSED || w->state == WALL_DOOR_DECLOAKING)         //already closed or decloaking
616
                return;
617
 
618
        auto &CloakingWalls = LevelUniqueWallSubsystemState.CloakingWalls;
619
        if (w->state == WALL_DOOR_CLOAKING) {   //cloaking, so reuse door
620
                const auto &&r = make_range(CloakingWalls.vmptr);
621
                const auto i = std::find_if(r.begin(), r.end(), find_cloaked_wall_predicate(w));
622
                if (i == r.end())
623
                {
624
                        d_debugbreak();
625
                        return;
626
                }
627
                d = *i;
628
                d->time = CLOAKING_WALL_TIME - d->time;
629
        }
630
        else if (w->state == WALL_DOOR_CLOSED) {        //create new door
631
                const clwallnum_t c = CloakingWalls.get_count();
632
                if (c >= CloakingWalls.size())
633
                {
634
                        Int3();         //ran out of cloaking wall slots
635
                        /* what is this _doing_ here?
636
                        w->type = WALL_CLOSED;
637
                        Walls[csegp->sides[Connectside].wall_num].type = WALL_CLOSED;
638
                        */
639
                        return;
640
                }
641
                CloakingWalls.set_count(c + 1);
642
                d = CloakingWalls.vmptr(c);
643
                d->time = 0;
644
        }
645
        else {
646
                Int3();         //unexpected wall state
647
                return;
648
        }
649
 
650
        w->state = WALL_DOOR_DECLOAKING;
651
 
652
        // So that door can't be shot while opening
653
        const auto &&csegp = vcsegptr(seg->children[side]);
654
        auto Connectside = find_connect_side(seg, csegp);
655
        Assert(Connectside != side_none);
656
        auto &csside = csegp->shared_segment::sides[Connectside];
657
        const auto cwall_num = csside.wall_num;
658
        if (const auto &&w1 = Walls.imptr(cwall_num))
659
                w1->state = WALL_DOOR_DECLOAKING;
660
 
661
        d->front_wallnum = seg->shared_segment::sides[side].wall_num;
662
        d->back_wallnum = csside.wall_num;
663
        Assert(w->linked_wall == wall_none);
664
 
665
        if ( Newdemo_state != ND_STATE_PLAYBACK ) {
666
                auto &vcvertptr = Vertices.vcptr;
667
                const auto &&cp = compute_center_point_on_side(vcvertptr, seg, side);
668
                digi_link_sound_to_pos( SOUND_WALL_CLOAK_OFF, seg, side, cp, 0, F1_0 );
669
        }
670
 
671
        for (auto &&[front_ls, back_ls, s0_uvls, s1_uvls] : zip(
672
                        d->front_ls,
673
                        d->back_ls,
674
                        seg->unique_segment::sides[side].uvls,
675
                        csegp->unique_segment::sides[Connectside].uvls
676
        ))
677
        {
678
                front_ls = s0_uvls.l;
679
                back_ls = s1_uvls.l;
680
        }
681
}
682
 
683
}
684
#endif
685
 
686
//-----------------------------------------------------------------
687
// This function closes the specified door and restores the closed
688
//  door texture.  This is called when the animation is done
689
void wall_close_door_ref(fvmsegptridx &vmsegptridx, wall_array &Walls, const wall_animations_array &WallAnims, active_door &d)
690
{
691
        range_for (const auto p, partial_const_range(d.front_wallnum, d.n_parts))
692
        {
693
                wall &w = *Walls.vmptr(p);
694
 
695
                const auto &&seg = vmsegptridx(w.segnum);
696
                const auto side = w.sidenum;
697
                w.state = WALL_DOOR_CLOSED;
698
 
699
                assert(seg->shared_segment::sides[side].wall_num != wall_none);         //Closing door on illegal wall
700
 
701
                const auto &&csegp = seg.absolute_sibling(seg->children[side]);
702
                auto Connectside = find_connect_side(seg, csegp);
703
                Assert(Connectside != side_none);
704
                const auto cwall_num = csegp->shared_segment::sides[Connectside].wall_num;
705
                if (const auto &&w1 = Walls.imptr(cwall_num))
706
                        w1->state = WALL_DOOR_CLOSED;
707
 
708
                wall_set_tmap_num(WallAnims[w.clip_num], seg, side, csegp, Connectside, 0);
709
        }
710
}
711
 
712
static unsigned check_poke(fvcvertptr &vcvertptr, const object_base &obj, const shared_segment &seg, const unsigned side)
713
{
714
        //note: don't let objects with zero size block door
715
        if (!obj.size)
716
                return 0;
717
        return get_seg_masks(vcvertptr, obj.pos, seg, obj.size).sidemask & (1 << side);         //pokes through side!
718
}
719
 
720
namespace dsx {
721
static unsigned is_door_side_obstructed(fvcobjptridx &vcobjptridx, fvcsegptr &vcsegptr, const cscusegment seg, const unsigned side)
722
{
723
        auto &LevelSharedVertexState = LevelSharedSegmentState.get_vertex_state();
724
        auto &Vertices = LevelSharedVertexState.get_vertices();
725
        auto &vcvertptr = Vertices.vcptr;
726
        range_for (const object_base &obj, objects_in(seg, vcobjptridx, vcsegptr))
727
        {
728
#if defined(DXX_BUILD_DESCENT_II)
729
                if (obj.type == OBJ_WEAPON)
730
                        continue;
731
                if (obj.type == OBJ_FIREBALL)
732
                        continue;
733
#endif
734
                if (const auto obstructed = check_poke(vcvertptr, obj, seg, side))
735
                        return obstructed;
736
        }
737
        return 0;
738
}
739
 
740
//returns true if door is obstructed (& thus cannot close)
741
static unsigned is_door_obstructed(fvcobjptridx &vcobjptridx, fvcsegptr &vcsegptr, const vcsegptridx_t seg, const unsigned side)
742
{
743
        if (const auto obstructed = is_door_side_obstructed(vcobjptridx, vcsegptr, seg, side))
744
                return obstructed;
745
        const auto &&csegp = vcsegptr(seg->children[side]);
746
        const auto &&Connectside = find_connect_side(seg, csegp);
747
        Assert(Connectside != side_none);
748
        //go through each object in each of two segments, and see if
749
        //it pokes into the connecting seg
750
        return is_door_side_obstructed(vcobjptridx, vcsegptr, csegp, Connectside);
751
}
752
 
753
#if defined(DXX_BUILD_DESCENT_II)
754
//-----------------------------------------------------------------
755
// Closes a door
756
void wall_close_door(wall_array &Walls, const vmsegptridx_t seg, const unsigned side)
757
{
758
        auto &LevelSharedVertexState = LevelSharedSegmentState.get_vertex_state();
759
        auto &Objects = LevelUniqueObjectState.Objects;
760
        auto &Vertices = LevelSharedVertexState.get_vertices();
761
        auto &WallAnims = GameSharedState.WallAnims;
762
        auto &vcobjptridx = Objects.vcptridx;
763
        active_door *d;
764
 
765
        const auto wall_num = seg->shared_segment::sides[side].wall_num;
766
        wall *const w = Walls.vmptr(wall_num);
767
        if ((w->state == WALL_DOOR_CLOSING) ||          //already closing
768
                 (w->state == WALL_DOOR_WAITING)        ||              //open, waiting to close
769
                 (w->state == WALL_DOOR_CLOSED))                        //closed
770
                return;
771
 
772
        if (is_door_obstructed(vcobjptridx, vcsegptr, seg, side))
773
                return;
774
 
775
        auto &ActiveDoors = LevelUniqueWallSubsystemState.ActiveDoors;
776
        auto &vmactdoorptr = ActiveDoors.vmptr;
777
        if (w->state == WALL_DOOR_OPENING) {    //reuse door
778
                const auto &&r = make_range(vmactdoorptr);
779
                const auto &&i = std::find_if(r.begin(), r.end(), find_active_door_predicate(wall_num));
780
                if (i == r.end())
781
                {
782
                        d_debugbreak();
783
                        return;
784
                }
785
                d = *i;
786
                d->time = WallAnims[w->clip_num].play_time - d->time;
787
 
788
                if (d->time < 0)
789
                        d->time = 0;
790
 
791
        }
792
        else {                                                                                  //create new door
793
                Assert(w->state == WALL_DOOR_OPEN);
794
                const auto i = ActiveDoors.get_count();
795
                ActiveDoors.set_count(i + 1);
796
                d = vmactdoorptr(static_cast<actdoornum_t>(i));
797
                d->time = 0;
798
        }
799
 
800
        w->state = WALL_DOOR_CLOSING;
801
 
802
        // So that door can't be shot while opening
803
        const auto &&csegp = vcsegptr(seg->children[side]);
804
        const auto &&Connectside = find_connect_side(seg, csegp);
805
        Assert(Connectside != side_none);
806
        const auto cwall_num = csegp->shared_segment::sides[Connectside].wall_num;
807
        if (const auto &&w1 = Walls.imptr(cwall_num))
808
                w1->state = WALL_DOOR_CLOSING;
809
 
810
        d->front_wallnum[0] = seg->shared_segment::sides[side].wall_num;
811
        d->back_wallnum[0] = cwall_num;
812
        if (Newdemo_state == ND_STATE_RECORDING) {
813
                newdemo_record_door_opening(seg, side);
814
        }
815
 
816
        if (w->linked_wall != wall_none)
817
        {
818
                Int3();         //don't think we ever used linked walls
819
        }
820
        else
821
                d->n_parts = 1;
822
 
823
 
824
        if ( Newdemo_state != ND_STATE_PLAYBACK )
825
        {
826
                // NOTE THE LINK TO ABOVE!!!!
827
                auto &vcvertptr = Vertices.vcptr;
828
                const auto &&cp = compute_center_point_on_side(vcvertptr, seg, side);
829
                const auto open_sound = WallAnims[w->clip_num].open_sound;
830
                if (open_sound > -1)
831
                        digi_link_sound_to_pos(open_sound, seg, side, cp, 0, F1_0);
832
 
833
        }
834
}
835
#endif
836
 
837
//-----------------------------------------------------------------
838
// Animates opening of a door.
839
// Called in the game loop.
840
static bool do_door_open(active_door &d)
841
{
842
        auto &Objects = LevelUniqueObjectState.Objects;
843
        auto &WallAnims = GameSharedState.WallAnims;
844
        auto &vmobjptr = Objects.vmptr;
845
        bool remove = false;
846
        auto &Walls = LevelUniqueWallSubsystemState.Walls;
847
        auto &vmwallptr = Walls.vmptr;
848
        for (unsigned p = 0; p < d.n_parts; ++p)
849
        {
850
                int side;
851
                fix time_elapsed, time_total, one_frame;
852
                int i;
853
 
854
                wall &w = *vmwallptr(d.front_wallnum[p]);
855
                LevelUniqueStuckObjectState.kill_stuck_objects(vmobjptr, d.front_wallnum[p]);
856
                LevelUniqueStuckObjectState.kill_stuck_objects(vmobjptr, d.back_wallnum[p]);
857
 
858
                const auto &&seg = vmsegptridx(w.segnum);
859
                side = w.sidenum;
860
 
861
                if (seg->shared_segment::sides[side].wall_num == wall_none)
862
                {
863
                        con_printf(CON_URGENT, "Trying to do_door_open on illegal wall %i. Please check your level!",side);
864
                        continue;
865
                }
866
 
867
                const auto &&csegp = seg.absolute_sibling(seg->children[side]);
868
                const auto &&Connectside = find_connect_side(seg, csegp);
869
                Assert(Connectside != side_none);
870
 
871
                d.time += FrameTime;
872
 
873
                time_elapsed = d.time;
874
                auto &wa = WallAnims[w.clip_num];
875
                const auto n = wa.num_frames;
876
                time_total = wa.play_time;
877
 
878
                one_frame = time_total/n;      
879
 
880
                i = time_elapsed/one_frame;
881
 
882
                if (i < n)
883
                        wall_set_tmap_num(wa, seg, side, csegp, Connectside, i);
884
 
885
                const auto cwall_num = csegp->shared_segment::sides[Connectside].wall_num;
886
                auto &w1 = *vmwallptr(cwall_num);
887
                if (i> n/2) {
888
                        w.flags |= WALL_DOOR_OPENED;
889
                        w1.flags |= WALL_DOOR_OPENED;
890
                }
891
 
892
                if (i >= n-1) {
893
                        wall_set_tmap_num(wa, seg, side, csegp, Connectside, n - 1);
894
 
895
                        // If our door is not automatic just remove it from the list.
896
                        if (!(w.flags & WALL_DOOR_AUTO)) {
897
                                remove = true;
898
#if defined(DXX_BUILD_DESCENT_II)
899
                                w.state = WALL_DOOR_OPEN;
900
                                w1.state = WALL_DOOR_OPEN;
901
#endif
902
                        }
903
                        else {
904
                                w.state = WALL_DOOR_WAITING;
905
                                w1.state = WALL_DOOR_WAITING;
906
                        }
907
                }
908
 
909
        }
910
        flush_fcd_cache();
911
        return remove;
912
}
913
 
914
//-----------------------------------------------------------------
915
// Animates and processes the closing of a door.
916
// Called from the game loop.
917
static bool do_door_close(active_door &d)
918
{
919
        auto &LevelSharedVertexState = LevelSharedSegmentState.get_vertex_state();
920
        auto &Objects = LevelUniqueObjectState.Objects;
921
        auto &Vertices = LevelSharedVertexState.get_vertices();
922
        auto &vcobjptridx = Objects.vcptridx;
923
        auto &Walls = LevelUniqueWallSubsystemState.Walls;
924
        auto &WallAnims = GameSharedState.WallAnims;
925
        auto &vmwallptr = Walls.vmptr;
926
        auto &w0 = *vmwallptr(d.front_wallnum[0]);
927
        const auto &&seg0 = vmsegptridx(w0.segnum);
928
 
929
        //check for objects in doorway before closing
930
        if (w0.flags & WALL_DOOR_AUTO)
931
                if (is_door_obstructed(vcobjptridx, vcsegptr, seg0, w0.sidenum))
932
                {
933
#if defined(DXX_BUILD_DESCENT_II)
934
                        digi_kill_sound_linked_to_segment(w0.segnum, w0.sidenum, -1);
935
                        wall_open_door(seg0, w0.sidenum);               //re-open door
936
#endif
937
                        return false;
938
                }
939
 
940
        bool played_sound = false;
941
        bool remove = false;
942
        range_for (const auto p, partial_const_range(d.front_wallnum, d.n_parts))
943
        {
944
                int side;
945
                fix time_elapsed, time_total, one_frame;
946
                int i;
947
 
948
                auto &wp = *vmwallptr(p);
949
 
950
                const auto &&seg = vmsegptridx(wp.segnum);
951
                side = wp.sidenum;
952
 
953
                if (seg->shared_segment::sides[side].wall_num == wall_none) {
954
                        return false;
955
                }
956
 
957
#if defined(DXX_BUILD_DESCENT_I)
958
                //if here, must be auto door
959
//don't assert here, because now we have triggers to close non-auto doors
960
                Assert(wp.flags & WALL_DOOR_AUTO);
961
#endif
962
 
963
                // Otherwise, close it.
964
                const auto &&csegp = seg.absolute_sibling(seg->children[side]);
965
                const auto &&Connectside = find_connect_side(seg, csegp);
966
                Assert(Connectside != side_none);
967
 
968
 
969
                auto &wa = WallAnims[wp.clip_num];
970
                if ( Newdemo_state != ND_STATE_PLAYBACK )
971
                {
972
                        // NOTE THE LINK TO ABOVE!!
973
                        if (!played_sound)      //only play one sound for linked doors
974
                        {
975
                                played_sound = true;
976
                                if (d.time == 0)
977
                                {               //first time
978
                                        const auto close_sound = wa.close_sound;
979
                                        if (close_sound > -1)
980
                                        {
981
                                                auto &vcvertptr = Vertices.vcptr;
982
                                                digi_link_sound_to_pos(close_sound, seg, side, compute_center_point_on_side(vcvertptr, seg, side), 0, F1_0);
983
                                        }
984
                                }
985
                        }
986
                }
987
 
988
                d.time += FrameTime;
989
 
990
                time_elapsed = d.time;
991
                const auto n = wa.num_frames;
992
                time_total = wa.play_time;
993
 
994
                one_frame = time_total/n;      
995
 
996
                i = n-time_elapsed/one_frame-1;
997
 
998
                const auto cwall_num = csegp->shared_segment::sides[Connectside].wall_num;
999
                auto &w1 = *vmwallptr(cwall_num);
1000
                if (i < n/2) {
1001
                        wp.flags &= ~WALL_DOOR_OPENED;
1002
                        w1.flags &= ~WALL_DOOR_OPENED;
1003
                }
1004
 
1005
                // Animate door.
1006
                if (i > 0) {
1007
                        wall_set_tmap_num(wa, seg, side, csegp, Connectside, i);
1008
 
1009
                        wp.state = WALL_DOOR_CLOSING;
1010
                        w1.state = WALL_DOOR_CLOSING;
1011
                } else
1012
                        remove = true;
1013
        }
1014
 
1015
        if (remove)
1016
                wall_close_door_ref(Segments.vmptridx, Walls, WallAnims, d);
1017
        return remove;
1018
}
1019
 
1020
static std::pair<wall *, wall *> wall_illusion_op(fvmwallptr &vmwallptr, const vcsegptridx_t seg, const unsigned side)
1021
{
1022
        const auto wall0 = seg->shared_segment::sides[side].wall_num;
1023
        if (wall0 == wall_none)
1024
                return {nullptr, nullptr};
1025
        const shared_segment &csegp = *seg.absolute_sibling(seg->children[side]);
1026
        const auto &&cside = find_connect_side(seg, csegp);
1027
        if (cside == side_none)
1028
        {
1029
                assert(cside != side_none);
1030
                return {nullptr, nullptr};
1031
        }
1032
        const auto wall1 = csegp.sides[cside].wall_num;
1033
        if (wall1 == wall_none)
1034
                return {nullptr, nullptr};
1035
        return {vmwallptr(wall0), vmwallptr(wall1)};
1036
}
1037
 
1038
template <typename F>
1039
static void wall_illusion_op(fvmwallptr &vmwallptr, const vcsegptridx_t seg, const unsigned side, const F op)
1040
{
1041
        const auto &&r = wall_illusion_op(vmwallptr, seg, side);
1042
        if (r.first)
1043
        {
1044
                op(*r.first);
1045
                op(*r.second);
1046
        }
1047
}
1048
 
1049
//-----------------------------------------------------------------
1050
// Turns off an illusionary wall (This will be used primarily for
1051
//  wall switches or triggers that can turn on/off illusionary walls.)
1052
void wall_illusion_off(fvmwallptr &vmwallptr, const vcsegptridx_t seg, const unsigned side)
1053
{
1054
        const auto &&op = [](wall &w) {
1055
                w.flags |= WALL_ILLUSION_OFF;
1056
        };
1057
        wall_illusion_op(vmwallptr, seg, side, op);
1058
}
1059
 
1060
//-----------------------------------------------------------------
1061
// Turns on an illusionary wall (This will be used primarily for
1062
//  wall switches or triggers that can turn on/off illusionary walls.)
1063
void wall_illusion_on(fvmwallptr &vmwallptr, const vcsegptridx_t seg, const unsigned side)
1064
{
1065
        const auto &&op = [](wall &w) {
1066
                w.flags &= ~WALL_ILLUSION_OFF;
1067
        };
1068
        wall_illusion_op(vmwallptr, seg, side, op);
1069
}
1070
 
1071
}
1072
 
1073
//      -----------------------------------------------------------------------------
1074
//      Allowed to open the normally locked special boss door if in multiplayer mode.
1075
static int special_boss_opening_allowed(segnum_t segnum, int sidenum)
1076
{
1077
        if (Game_mode & GM_MULTI)
1078
                return (Current_level_num == BOSS_LOCKED_DOOR_LEVEL) && (segnum == BOSS_LOCKED_DOOR_SEG) && (sidenum == BOSS_LOCKED_DOOR_SIDE);
1079
        else
1080
                return 0;
1081
}
1082
 
1083
//-----------------------------------------------------------------
1084
// Determines what happens when a wall is shot
1085
//returns info about wall.  see wall.h for codes
1086
//obj is the object that hit...either a weapon or the player himself
1087
//playernum is the number the player who hit the wall or fired the weapon,
1088
//or -1 if a robot fired the weapon
1089
namespace dsx {
1090
wall_hit_process_t wall_hit_process(const player_flags powerup_flags, const vmsegptridx_t seg, const unsigned side, const fix damage, const unsigned playernum, const object &obj)
1091
{
1092
        fix     show_message;
1093
 
1094
        // If it is not a "wall" then just return.
1095
        const auto wall_num = seg->shared_segment::sides[side].wall_num;
1096
        if (wall_num == wall_none)
1097
                return wall_hit_process_t::WHP_NOT_SPECIAL;
1098
 
1099
        auto &Walls = LevelUniqueWallSubsystemState.Walls;
1100
        auto &vmwallptr = Walls.vmptr;
1101
        wall *const w = vmwallptr(wall_num);
1102
 
1103
        if ( Newdemo_state == ND_STATE_RECORDING )
1104
                newdemo_record_wall_hit_process( seg, side, damage, playernum );
1105
 
1106
        if (w->type == WALL_BLASTABLE) {
1107
#if defined(DXX_BUILD_DESCENT_II)
1108
                if (obj.ctype.laser_info.parent_type == OBJ_PLAYER)
1109
#endif
1110
                        wall_damage(seg, side, damage);
1111
                return wall_hit_process_t::WHP_BLASTABLE;
1112
        }
1113
 
1114
        if (playernum != Player_num)    //return if was robot fire
1115
                return wall_hit_process_t::WHP_NOT_SPECIAL;
1116
 
1117
        //      Determine whether player is moving forward.  If not, don't say negative
1118
        //      messages because he probably didn't intentionally hit the door.
1119
        if (obj.type == OBJ_PLAYER)
1120
                show_message = (vm_vec_dot(obj.orient.fvec, obj.mtype.phys_info.velocity) > 0);
1121
#if defined(DXX_BUILD_DESCENT_II)
1122
        else if (obj.type == OBJ_ROBOT)
1123
                show_message = 0;
1124
        else if (obj.type == OBJ_WEAPON && obj.ctype.laser_info.parent_type == OBJ_ROBOT)
1125
                show_message = 0;
1126
#endif
1127
        else
1128
                show_message = 1;
1129
 
1130
        /* Set key_color only after the type matches, since TXT_* are macros
1131
         * that trigger a load from memory.  Use operator,() to suppress the
1132
         * truth test on the second branch since the compiler cannot prove
1133
         * that the loaded value will always be non-null.
1134
         */
1135
        const char *key_color;
1136
        if (
1137
                (w->keys == KEY_BLUE && (key_color = TXT_BLUE, true)) ||
1138
                (w->keys == KEY_GOLD && (key_color = TXT_YELLOW, true)) ||
1139
                (w->keys == KEY_RED && (key_color = TXT_RED, true))
1140
        )
1141
        {
1142
                if (!(powerup_flags & static_cast<PLAYER_FLAG>(w->keys)))
1143
                {
1144
                        static_assert(KEY_BLUE == static_cast<unsigned>(PLAYER_FLAGS_BLUE_KEY), "BLUE key flag mismatch");
1145
                        static_assert(KEY_GOLD == static_cast<unsigned>(PLAYER_FLAGS_GOLD_KEY), "GOLD key flag mismatch");
1146
                        static_assert(KEY_RED == static_cast<unsigned>(PLAYER_FLAGS_RED_KEY), "RED key flag mismatch");
1147
                                if (show_message)
1148
                                        HUD_init_message(HM_DEFAULT, "%s %s",key_color,TXT_ACCESS_DENIED);
1149
                        return wall_hit_process_t::WHP_NO_KEY;
1150
                }
1151
        }
1152
 
1153
        if (w->type == WALL_DOOR)
1154
        {
1155
                if ((w->flags & WALL_DOOR_LOCKED ) && !(special_boss_opening_allowed(seg, side)) ) {
1156
                                if (show_message)
1157
                                        HUD_init_message_literal(HM_DEFAULT, TXT_CANT_OPEN_DOOR);
1158
                        return wall_hit_process_t::WHP_NO_KEY;
1159
                }
1160
                else {
1161
                        if (w->state != WALL_DOOR_OPENING)
1162
                        {
1163
                                wall_open_door(seg, side);
1164
                                if (Game_mode & GM_MULTI)
1165
                                {
1166
                                        int flags;
1167
#if defined(DXX_BUILD_DESCENT_I)
1168
                                        flags = 0;
1169
#elif defined(DXX_BUILD_DESCENT_II)
1170
                                        flags = w->flags;
1171
#endif
1172
                                        multi_send_door_open(seg, side,flags);
1173
                                }
1174
                        }
1175
                        return wall_hit_process_t::WHP_DOOR;
1176
 
1177
                }
1178
        }
1179
        return wall_hit_process_t::WHP_NOT_SPECIAL;             //default is treat like normal wall
1180
}
1181
}
1182
 
1183
//-----------------------------------------------------------------
1184
// Opens doors/destroys wall/shuts off triggers.
1185
void wall_toggle(fvmwallptr &vmwallptr, const vmsegptridx_t segp, const unsigned side)
1186
{
1187
        if (side >= MAX_SIDES_PER_SEGMENT)
1188
        {
1189
#ifndef NDEBUG
1190
                Warning("Can't toggle side %u of segment %d (%u)!\n", side, static_cast<segnum_t>(segp), Highest_segment_index);
1191
#endif
1192
                return;
1193
        }
1194
        const auto wall_num = segp->shared_segment::sides[side].wall_num;
1195
        if (wall_num == wall_none)
1196
        {
1197
                LevelError("Ignoring attempt to toggle wall in segment %hu, side %u: no wall exists there.", segp.get_unchecked_index(), side);
1198
                return;
1199
        }
1200
 
1201
        if ( Newdemo_state == ND_STATE_RECORDING )
1202
                newdemo_record_wall_toggle(segp, side);
1203
 
1204
        wall *const w = vmwallptr(wall_num);
1205
        if (w->type == WALL_BLASTABLE)
1206
                wall_destroy(segp, side);
1207
 
1208
        if (w->type == WALL_DOOR && w->state == WALL_DOOR_CLOSED)
1209
                wall_open_door(segp, side);
1210
}
1211
 
1212
bool ad_removal_predicate::operator()(active_door &d) const
1213
{
1214
#if defined(DXX_BUILD_DESCENT_II)
1215
        auto &Objects = LevelUniqueObjectState.Objects;
1216
        auto &vcobjptridx = Objects.vcptridx;
1217
#endif
1218
        auto &Walls = LevelUniqueWallSubsystemState.Walls;
1219
        wall &w = *Walls.vmptr(d.front_wallnum[0]);
1220
        if (w.state == WALL_DOOR_OPENING)
1221
                return do_door_open(d);
1222
        else if (w.state == WALL_DOOR_CLOSING)
1223
                return do_door_close(d);
1224
        else if (w.state == WALL_DOOR_WAITING) {
1225
                d.time += FrameTime;
1226
                // set flags to fix occasional netgame problem where door is waiting to close but open flag isn't set
1227
                w.flags |= WALL_DOOR_OPENED;
1228
                if (wall *const w1 = Walls.imptr(d.back_wallnum[0]))
1229
                        w1->flags |= WALL_DOOR_OPENED;
1230
                if (d.time > DOOR_WAIT_TIME)
1231
#if defined(DXX_BUILD_DESCENT_II)
1232
                        if (!is_door_obstructed(vcobjptridx, vcsegptr, vcsegptridx(w.segnum), w.sidenum))
1233
#endif
1234
                        {
1235
                                w.state = WALL_DOOR_CLOSING;
1236
                                d.time = 0;
1237
                        }
1238
        }
1239
        return false;
1240
}
1241
 
1242
#if defined(DXX_BUILD_DESCENT_II)
1243
static void copy_cloaking_wall_light_to_wall(std::array<uvl, 4> &back_uvls, std::array<uvl, 4> &front_uvls, const cloaking_wall &d)
1244
{
1245
        range_for (const uint_fast32_t i, xrange(4u))
1246
        {
1247
                back_uvls[i].l = d.back_ls[i];
1248
                front_uvls[i].l = d.front_ls[i];
1249
        }
1250
}
1251
 
1252
static void scale_cloaking_wall_light_to_wall(std::array<uvl, 4> &back_uvls, std::array<uvl, 4> &front_uvls, const cloaking_wall &d, const fix light_scale)
1253
{
1254
        range_for (const uint_fast32_t i, xrange(4u))
1255
        {
1256
                back_uvls[i].l = fixmul(d.back_ls[i], light_scale);
1257
                front_uvls[i].l = fixmul(d.front_ls[i], light_scale);
1258
        }
1259
}
1260
 
1261
static cwresult do_cloaking_wall_frame(const bool initial, cloaking_wall &d, const cwframe front, const cwframe back)
1262
{
1263
        cwresult r(initial);
1264
        if (d.time > CLOAKING_WALL_TIME) {
1265
                front.w.type = back.w.type = WALL_OPEN;
1266
                front.w.state = back.w.state = WALL_DOOR_CLOSED;                //why closed? why not?
1267
                r.remove = true;
1268
        }
1269
        else if (d.time > CLOAKING_WALL_TIME/2) {
1270
                const int8_t cloak_value = ((d.time - CLOAKING_WALL_TIME / 2) * (GR_FADE_LEVELS - 2)) / (CLOAKING_WALL_TIME / 2);
1271
                if (front.w.cloak_value != cloak_value)
1272
                {
1273
                        r.record = true;
1274
                        front.w.cloak_value = back.w.cloak_value = cloak_value;
1275
                }
1276
 
1277
                if (front.w.type != WALL_CLOAKED)
1278
                {               //just switched
1279
                        front.w.type = back.w.type = WALL_CLOAKED;
1280
                        copy_cloaking_wall_light_to_wall(back.uvls, front.uvls, d);
1281
                }
1282
        }
1283
        else {          //fading out
1284
                fix light_scale;
1285
                light_scale = fixdiv(CLOAKING_WALL_TIME / 2 - d.time, CLOAKING_WALL_TIME / 2);
1286
                scale_cloaking_wall_light_to_wall(back.uvls, front.uvls, d, light_scale);
1287
        }
1288
        return r;
1289
}
1290
 
1291
static cwresult do_decloaking_wall_frame(const bool initial, cloaking_wall &d, const cwframe front, const cwframe back)
1292
{
1293
        cwresult r(initial);
1294
        if (d.time > CLOAKING_WALL_TIME) {
1295
 
1296
                back.w.state = WALL_DOOR_CLOSED;
1297
                front.w.state = WALL_DOOR_CLOSED;
1298
                copy_cloaking_wall_light_to_wall(back.uvls, front.uvls, d);
1299
                r.remove = true;
1300
        }
1301
        else if (d.time > CLOAKING_WALL_TIME/2) {               //fading in
1302
                fix light_scale;
1303
                front.w.type = back.w.type = WALL_CLOSED;
1304
 
1305
                light_scale = fixdiv(d.time - CLOAKING_WALL_TIME / 2, CLOAKING_WALL_TIME / 2);
1306
                scale_cloaking_wall_light_to_wall(back.uvls, front.uvls, d, light_scale);
1307
        }
1308
        else {          //cloaking in
1309
                const int8_t cloak_value = ((CLOAKING_WALL_TIME / 2 - d.time) * (GR_FADE_LEVELS - 2)) / (CLOAKING_WALL_TIME / 2);
1310
                if (front.w.cloak_value != cloak_value)
1311
                {
1312
                        front.w.cloak_value = back.w.cloak_value = cloak_value;
1313
                        r.record = true;
1314
                }
1315
                front.w.type = WALL_CLOAKED;
1316
                back.w.type = WALL_CLOAKED;
1317
        }
1318
        return r;
1319
}
1320
 
1321
bool cw_removal_predicate::operator()(cloaking_wall &d)
1322
{
1323
        const cwframe front(vmsegptr, *Walls.vmptr(d.front_wallnum));
1324
        const auto &&wpback = Walls.imptr(d.back_wallnum);
1325
        const cwframe back = (wpback ? cwframe(vmsegptr, *wpback) : front);
1326
        const bool initial = (d.time == 0);
1327
        d.time += FrameTime;
1328
 
1329
        cwresult r;
1330
        if (front.w.state == WALL_DOOR_CLOAKING)
1331
                r = do_cloaking_wall_frame(initial, d, front, back);
1332
        else if (front.w.state == WALL_DOOR_DECLOAKING)
1333
                r = do_decloaking_wall_frame(initial, d, front, back);
1334
        else
1335
        {
1336
                d_debugbreak(); //unexpected wall state
1337
                return false;
1338
        }
1339
        if (r.record)
1340
        {
1341
                // check if the actual cloak_value changed in this frame to prevent redundant recordings and wasted bytes
1342
                if (Newdemo_state == ND_STATE_RECORDING && r.record)
1343
                        newdemo_record_cloaking_wall(d.front_wallnum, d.back_wallnum, front.w.type, front.w.state, front.w.cloak_value, front.uvls[0].l, front.uvls[1].l, front.uvls[2].l, front.uvls[3].l);
1344
        }
1345
        if (!r.remove)
1346
                ++ num_cloaking_walls;
1347
        return r.remove;
1348
}
1349
#endif
1350
 
1351
namespace dsx {
1352
static void process_exploding_walls()
1353
{
1354
        if (Newdemo_state == ND_STATE_PLAYBACK)
1355
                return;
1356
        if (unsigned num_exploding_walls = Num_exploding_walls)
1357
        {
1358
                auto &Walls = LevelUniqueWallSubsystemState.Walls;
1359
                range_for (auto &&wp, Walls.vmptr)
1360
                {
1361
                        auto &w1 = *wp;
1362
                        if (w1.flags & WALL_EXPLODING)
1363
                        {
1364
                                assert(num_exploding_walls);
1365
                                const auto n = do_exploding_wall_frame(w1);
1366
                                num_exploding_walls -= n;
1367
                                if (!num_exploding_walls)
1368
                                        break;
1369
                        }
1370
                }
1371
        }
1372
}
1373
 
1374
void wall_frame_process()
1375
{
1376
        process_exploding_walls();
1377
        {
1378
                auto &ActiveDoors = LevelUniqueWallSubsystemState.ActiveDoors;
1379
                const auto &&r = partial_range(ActiveDoors, ActiveDoors.get_count());
1380
                auto &&i = std::remove_if(r.begin(), r.end(), ad_removal_predicate());
1381
                ActiveDoors.set_count(std::distance(r.begin(), i));
1382
        }
1383
#if defined(DXX_BUILD_DESCENT_II)
1384
        if (Newdemo_state != ND_STATE_PLAYBACK)
1385
        {
1386
                auto &CloakingWalls = LevelUniqueWallSubsystemState.CloakingWalls;
1387
                const auto &&r = partial_range(CloakingWalls, CloakingWalls.get_count());
1388
                auto &Walls = LevelUniqueWallSubsystemState.Walls;
1389
                cw_removal_predicate rp{Segments.vmptr, Walls};
1390
                std::remove_if(r.begin(), r.end(), std::ref(rp));
1391
                CloakingWalls.set_count(rp.num_cloaking_walls);
1392
        }
1393
#endif
1394
}
1395
 
1396
d_level_unique_stuck_object_state LevelUniqueStuckObjectState;
1397
 
1398
//      An object got stuck in a door (like a flare).
1399
//      Add global entry.
1400
void d_level_unique_stuck_object_state::add_stuck_object(fvcwallptr &vcwallptr, const vmobjptridx_t objp, const shared_segment &segp, const unsigned sidenum)
1401
{
1402
        const auto wallnum = segp.sides[sidenum].wall_num;
1403
        if (wallnum != wall_none)
1404
        {
1405
                if (vcwallptr(wallnum)->flags & WALL_BLASTED)
1406
                {
1407
                        objp->flags |= OF_SHOULD_BE_DEAD;
1408
                        return;
1409
                }
1410
                if (Num_stuck_objects >= Stuck_objects.size())
1411
                {
1412
                        assert(Num_stuck_objects <= Stuck_objects.size());
1413
                        con_printf(CON_NORMAL, "%s:%u: all stuck objects are busy; terminating %hu early", __FILE__, __LINE__, objp.get_unchecked_index());
1414
                        objp->flags |= OF_SHOULD_BE_DEAD;
1415
                        return;
1416
                }
1417
                auto &so = Stuck_objects[Num_stuck_objects++];
1418
                so.wallnum = wallnum;
1419
                so.objnum = objp;
1420
        }
1421
}
1422
 
1423
void d_level_unique_stuck_object_state::remove_stuck_object(const vcobjidx_t obj)
1424
{
1425
        auto &&pr = partial_range(Stuck_objects, Num_stuck_objects);
1426
        const auto predicate = [obj](const stuckobj &so) { return so.objnum == obj; };
1427
        const auto i = std::find_if(pr.begin(), pr.end(), predicate);
1428
        if (i == pr.end())
1429
                /* Objects enter this function if they are able to become stuck,
1430
                 * without regard to whether they actually are stuck.  If the
1431
                 * object terminated without being stuck in a wall, then it will
1432
                 * not be found in Stuck_objects.
1433
                 */
1434
                return;
1435
        /* If pr.begin() == pr.end(), then i == pr.end(), and this line
1436
         * cannot be reached.
1437
         *
1438
         * If pr.begin() != pr.end(), then prev(pr.end()) must point to a
1439
         * valid element.
1440
         *
1441
         * Move that valid element to the location vacated by the removed
1442
         * object.  This may be a self-move if the removed object is the
1443
         * last object.
1444
         */
1445
        auto &last_element = *std::prev(pr.end());
1446
        static_assert(std::is_trivially_move_assignable<stuckobj>::value, "stuckobj move may require a check to prevent self-move");
1447
        *i = std::move(last_element);
1448
        DXX_POISON_VAR(last_element.wallnum, 0xcc);
1449
        DXX_POISON_VAR(last_element.objnum, 0xcc);
1450
        -- Num_stuck_objects;
1451
}
1452
 
1453
//      ----------------------------------------------------------------------------------------------------
1454
//      Door with wall index wallnum is opening, kill all objects stuck in it.
1455
void d_level_unique_stuck_object_state::kill_stuck_objects(fvmobjptr &vmobjptr, const vcwallidx_t wallnum)
1456
{
1457
        if (!Num_stuck_objects)
1458
                return;
1459
        auto &&pr = partial_range(Stuck_objects, Num_stuck_objects);
1460
        const auto predicate = [&vmobjptr, wallnum](const stuckobj &so)
1461
        {
1462
                if (so.wallnum != wallnum)
1463
                        return false;
1464
                auto &obj = *vmobjptr(so.objnum);
1465
#if defined(DXX_BUILD_DESCENT_I)
1466
#define DXX_WEAPON_LIFELEFT     F1_0/4
1467
#elif defined(DXX_BUILD_DESCENT_II)
1468
#define DXX_WEAPON_LIFELEFT     F1_0/8
1469
#endif
1470
                assert(obj.type == OBJ_WEAPON);
1471
                assert(obj.movement_type == MT_PHYSICS);
1472
                assert(obj.mtype.phys_info.flags & PF_STICK);
1473
                obj.lifeleft = DXX_WEAPON_LIFELEFT;
1474
                return true;
1475
        };
1476
        const auto i = std::remove_if(pr.begin(), pr.end(), predicate);
1477
        static_assert(std::is_trivially_destructible<stuckobj>::value, "stuckobj destructor not called");
1478
        Num_stuck_objects = std::distance(pr.begin(), i);
1479
        std::array<stuckobj, 1> empty;
1480
        DXX_POISON_VAR(empty, 0xcc);
1481
        std::fill(i, pr.end(), empty[0]);
1482
}
1483
 
1484
}
1485
 
1486
namespace dcx {
1487
// -----------------------------------------------------------------------------------
1488
// Initialize stuck objects array.  Called at start of level
1489
void d_level_unique_stuck_object_state::init_stuck_objects()
1490
{
1491
        DXX_POISON_VAR(Stuck_objects, 0xcc);
1492
        Num_stuck_objects = 0;
1493
}
1494
}
1495
 
1496
#if defined(DXX_BUILD_DESCENT_II)
1497
// -----------------------------------------------------------------------------------
1498
#define MAX_BLAST_GLASS_DEPTH   5
1499
 
1500
namespace dsx {
1501
namespace {
1502
 
1503
class blast_nearby_glass_context
1504
{
1505
        using TmapInfo_array = d_level_unique_tmap_info_state::TmapInfo_array;
1506
        const object &obj;
1507
        const fix damage;
1508
        const d_eclip_array &Effects;
1509
        const GameBitmaps_array &GameBitmaps;
1510
        const Textures_array &Textures;
1511
        const TmapInfo_array &TmapInfo;
1512
        const d_vclip_array &Vclip;
1513
        fvcvertptr &vcvertptr;
1514
        fvcwallptr &vcwallptr;
1515
        visited_segment_bitarray_t visited;
1516
        unsigned can_blast(const int16_t &tmap_num2) const;
1517
public:
1518
        blast_nearby_glass_context(const object &obj, const fix damage, const d_eclip_array &Effects, const GameBitmaps_array &GameBitmaps, const Textures_array &Textures, const TmapInfo_array &TmapInfo, const d_vclip_array &Vclip, fvcvertptr &vcvertptr, fvcwallptr &vcwallptr) :
1519
                obj(obj), damage(damage), Effects(Effects), GameBitmaps(GameBitmaps),
1520
                Textures(Textures), TmapInfo(TmapInfo), Vclip(Vclip),
1521
                vcvertptr(vcvertptr), vcwallptr(vcwallptr), visited{}
1522
        {
1523
        }
1524
        blast_nearby_glass_context(const blast_nearby_glass_context &) = delete;
1525
        blast_nearby_glass_context &operator=(const blast_nearby_glass_context &) = delete;
1526
        void process_segment(vmsegptridx_t segp, unsigned steps_remaining);
1527
};
1528
 
1529
unsigned blast_nearby_glass_context::can_blast(const int16_t &tmap_num2) const
1530
{
1531
        const auto tm = tmap_num2 & 0x3fff;                     //tm without flags
1532
        auto &ti = TmapInfo[tm];
1533
        const auto ec = ti.eclip_num;
1534
        if (ec == eclip_none)
1535
        {
1536
                return ti.destroyed != -1;
1537
        }
1538
        else
1539
        {
1540
                auto &e = Effects[ec];
1541
                return e.dest_bm_num != ~0u && !(e.flags & EF_ONE_SHOT);
1542
        }
1543
}
1544
 
1545
void blast_nearby_glass_context::process_segment(const vmsegptridx_t segp, const unsigned steps_remaining)
1546
{
1547
        visited[segp] = true;
1548
 
1549
        const auto &objp = obj;
1550
        range_for (const auto &&e, enumerate(segp->unique_segment::sides))
1551
        {
1552
                fix                     dist;
1553
 
1554
                //      Process only walls which have glass.
1555
                auto &&uside = e.value;
1556
                if (const auto &tmap_num2 = uside.tmap_num2)
1557
                {
1558
                        if (can_blast(tmap_num2))
1559
                        {
1560
                                auto &&sidenum = e.idx;
1561
                                const auto &&pnt = compute_center_point_on_side(vcvertptr, segp, sidenum);
1562
                                dist = vm_vec_dist_quick(pnt, objp.pos);
1563
                                if (dist < damage/2) {
1564
                                        dist = find_connected_distance(pnt, segp, objp.pos, segp.absolute_sibling(objp.segnum), MAX_BLAST_GLASS_DEPTH, WID_RENDPAST_FLAG);
1565
                                        if ((dist > 0) && (dist < damage/2))
1566
                                        {
1567
                                                assert(objp.type == OBJ_WEAPON);
1568
                                                check_effect_blowup(LevelSharedSegmentState.DestructibleLights, Vclip, segp, sidenum, pnt, objp.ctype.laser_info, 1, 0);
1569
                                        }
1570
                                }
1571
                        }
1572
                }
1573
        }
1574
 
1575
        const unsigned next_steps_remaining = steps_remaining - 1;
1576
        if (!next_steps_remaining)
1577
                return;
1578
        range_for (const auto &&e, enumerate(segp->children))
1579
        {
1580
                auto &&segnum = e.value;
1581
                if (segnum != segment_none) {
1582
                        if (!visited[segnum]) {
1583
                                auto &&i = e.idx;
1584
                                if (WALL_IS_DOORWAY(GameBitmaps, Textures, vcwallptr, segp, i) & WID_FLY_FLAG)
1585
                                {
1586
                                        process_segment(segp.absolute_sibling(segnum), next_steps_remaining);
1587
                                }
1588
                        }
1589
                }
1590
        }
1591
}
1592
 
1593
struct d1wclip
1594
{
1595
        wclip *const wc;
1596
        d1wclip(wclip &w) : wc(&w) {}
1597
};
1598
 
1599
DEFINE_SERIAL_UDT_TO_MESSAGE(d1wclip, dwc, (dwc.wc->play_time, dwc.wc->num_frames, dwc.wc->d1_frames, dwc.wc->open_sound, dwc.wc->close_sound, dwc.wc->flags, dwc.wc->filename, serial::pad<1>()));
1600
ASSERT_SERIAL_UDT_MESSAGE_SIZE(d1wclip, 26 + (sizeof(int16_t) * MAX_CLIP_FRAMES_D1));
1601
 
1602
}
1603
 
1604
// -----------------------------------------------------------------------------------
1605
//      objp is going to detonate
1606
//      blast nearby monitors, lights, maybe other things
1607
void blast_nearby_glass(const object &objp, const fix damage)
1608
{
1609
        auto &LevelSharedVertexState = LevelSharedSegmentState.get_vertex_state();
1610
        auto &Effects = LevelUniqueEffectsClipState.Effects;
1611
        auto &TmapInfo = LevelUniqueTmapInfoState.TmapInfo;
1612
        auto &Vertices = LevelSharedVertexState.get_vertices();
1613
        auto &vcvertptr = Vertices.vcptr;
1614
        auto &Walls = LevelUniqueWallSubsystemState.Walls;
1615
        auto &vcwallptr = Walls.vcptr;
1616
        blast_nearby_glass_context{objp, damage, Effects, GameBitmaps, Textures, TmapInfo, Vclip, vcvertptr, vcwallptr}.process_segment(vmsegptridx(objp.segnum), MAX_BLAST_GLASS_DEPTH);
1617
}
1618
 
1619
}
1620
 
1621
#endif
1622
 
1623
DEFINE_SERIAL_UDT_TO_MESSAGE(wclip, wc, (wc.play_time, wc.num_frames, wc.frames, wc.open_sound, wc.close_sound, wc.flags, wc.filename, serial::pad<1>()));
1624
ASSERT_SERIAL_UDT_MESSAGE_SIZE(wclip, 26 + (sizeof(int16_t) * MAX_CLIP_FRAMES));
1625
 
1626
/*
1627
 * reads a wclip structure from a PHYSFS_File
1628
 */
1629
void wclip_read(PHYSFS_File *fp, wclip &wc)
1630
{
1631
        PHYSFSX_serialize_read(fp, wc);
1632
}
1633
 
1634
#if 0
1635
void wclip_write(PHYSFS_File *fp, const wclip &wc)
1636
{
1637
        PHYSFSX_serialize_write(fp, wc);
1638
}
1639
#endif
1640
 
1641
struct wrap_v16_wall
1642
{
1643
        const wall *w;
1644
        wrap_v16_wall(const wall &t) : w(&t) {}
1645
};
1646
 
1647
#define _SERIAL_UDT_WALL_V16_MEMBERS(P) (P type, P flags, P hps, P trigger, P clip_num, P keys)
1648
 
1649
DEFINE_SERIAL_UDT_TO_MESSAGE(v16_wall, w, _SERIAL_UDT_WALL_V16_MEMBERS(w.));
1650
DEFINE_SERIAL_UDT_TO_MESSAGE(wrap_v16_wall, w, _SERIAL_UDT_WALL_V16_MEMBERS(w.w->));
1651
ASSERT_SERIAL_UDT_MESSAGE_SIZE(wrap_v16_wall, 9);
1652
 
1653
/*
1654
 * reads a v16_wall structure from a PHYSFS_File
1655
 */
1656
void v16_wall_read(PHYSFS_File *fp, v16_wall &w)
1657
{
1658
        PHYSFSX_serialize_read(fp, w);
1659
}
1660
 
1661
struct wrap_v19_wall
1662
{
1663
        const wall *w;
1664
        wrap_v19_wall(const wall &t) : w(&t) {}
1665
};
1666
 
1667
DEFINE_SERIAL_UDT_TO_MESSAGE(v19_wall, w, (w.segnum, serial::pad<2>(), w.sidenum, w.type, w.flags, w.hps, w.trigger, w.clip_num, w.keys, w.linked_wall));
1668
DEFINE_SERIAL_UDT_TO_MESSAGE(wrap_v19_wall, w, (w.w->segnum, serial::pad<2>(), w.w->sidenum, serial::pad<3>(), w.w->type, w.w->flags, w.w->hps, w.w->trigger, w.w->clip_num, w.w->keys, w.w->linked_wall, serial::pad<2>()));
1669
ASSERT_SERIAL_UDT_MESSAGE_SIZE(v19_wall, 21);
1670
ASSERT_SERIAL_UDT_MESSAGE_SIZE(wrap_v19_wall, 21);
1671
 
1672
/*
1673
 * reads a v19_wall structure from a PHYSFS_File
1674
 */
1675
void v19_wall_read(PHYSFS_File *fp, v19_wall &w)
1676
{
1677
        PHYSFSX_serialize_read(fp, w);
1678
}
1679
 
1680
#if defined(DXX_BUILD_DESCENT_I)
1681
#define _SERIAL_UDT_WALL_D2X_MEMBERS    serial::pad<2>()
1682
#elif defined(DXX_BUILD_DESCENT_II)
1683
#define _SERIAL_UDT_WALL_D2X_MEMBERS    w.controlling_trigger, w.cloak_value
1684
#endif
1685
DEFINE_SERIAL_UDT_TO_MESSAGE(wall, w, (serial::sign_extend<int>(w.segnum), w.sidenum, serial::pad<3, 0>(), w.hps, serial::sign_extend<int>(w.linked_wall), w.type, w.flags, w.state, w.trigger, w.clip_num, w.keys, _SERIAL_UDT_WALL_D2X_MEMBERS));
1686
ASSERT_SERIAL_UDT_MESSAGE_SIZE(wall, 24);
1687
 
1688
/*
1689
 * reads a wall structure from a PHYSFS_File
1690
 */
1691
void wall_read(PHYSFS_File *fp, wall &w)
1692
{
1693
        PHYSFSX_serialize_read(fp, w);
1694
        w.flags &= ~WALL_EXPLODING;
1695
}
1696
 
1697
DEFINE_SERIAL_UDT_TO_MESSAGE(active_door, d, (d.n_parts, d.front_wallnum, d.back_wallnum, d.time));
1698
ASSERT_SERIAL_UDT_MESSAGE_SIZE(active_door, 16);
1699
 
1700
/*
1701
 * reads an active_door structure from a PHYSFS_File
1702
 */
1703
void active_door_read(PHYSFS_File *fp, active_door &ad)
1704
{
1705
        PHYSFSX_serialize_read(fp, ad);
1706
}
1707
 
1708
void active_door_write(PHYSFS_File *fp, const active_door &ad)
1709
{
1710
        PHYSFSX_serialize_write(fp, ad);
1711
}
1712
 
1713
void wall_write(PHYSFS_File *fp, const wall &w, short version)
1714
{
1715
        if (version <= 16)
1716
                PHYSFSX_serialize_write<wrap_v16_wall>(fp, w);
1717
        else if (version <= 19)
1718
                PHYSFSX_serialize_write<wrap_v19_wall>(fp, w);
1719
        else
1720
                PHYSFSX_serialize_write(fp, w);
1721
}
1722
 
1723
#if defined(DXX_BUILD_DESCENT_II)
1724
DEFINE_SERIAL_UDT_TO_MESSAGE(cloaking_wall, cw, (cw.front_wallnum, cw.back_wallnum, cw.front_ls, cw.back_ls, cw.time));
1725
ASSERT_SERIAL_UDT_MESSAGE_SIZE(cloaking_wall, 40);
1726
 
1727
namespace dsx {
1728
void cloaking_wall_read(cloaking_wall &cw, PHYSFS_File *fp)
1729
{
1730
        PHYSFSX_serialize_read(fp, cw);
1731
}
1732
 
1733
void cloaking_wall_write(const cloaking_wall &cw, PHYSFS_File *fp)
1734
{
1735
        PHYSFSX_serialize_write(fp, cw);
1736
}
1737
}
1738
#endif