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
/*
21
 *
22
 * New Triggers and Switches.
23
 *
24
 */
25
 
26
#include <stdexcept>
27
#include <stdio.h>
28
#include <stdlib.h>
29
#include <math.h>
30
#include <string.h>
31
 
32
#include "gauges.h"
33
#include "newmenu.h"
34
#include "game.h"
35
#include "switch.h"
36
#include "inferno.h"
37
#include "segment.h"
38
#include "dxxerror.h"
39
#include "gameseg.h"
40
#include "wall.h"
41
#include "texmap.h"
42
#include "object.h"
43
#include "fuelcen.h"
44
#include "cntrlcen.h"
45
#include "newdemo.h"
46
#include "player.h"
47
#include "endlevel.h"
48
#include "gameseq.h"
49
#include "multi.h"
50
#include "palette.h"
51
#include "hudmsg.h"
52
#include "robot.h"
53
#include "bm.h"
54
 
55
#if DXX_USE_EDITOR
56
#include "editor/editor.h"
57
#endif
58
 
59
#include "physfs-serial.h"
60
#include "compiler-range_for.h"
61
#include "partial_range.h"
62
 
63
#if DXX_USE_EDITOR
64
//-----------------------------------------------------------------
65
// Initializes all the switches.
66
void trigger_init()
67
{
68
        auto &Triggers = LevelUniqueWallSubsystemState.Triggers;
69
        Triggers.set_count(0);
70
}
71
#endif
72
 
73
template <typename SF, typename O, typename... Oa>
74
static inline void trigger_wall_op(const trigger &t, SF &segment_factory, const O &op, Oa &&... oargs)
75
{
76
        for (unsigned i = 0, num_links = t.num_links; i != num_links; ++i)
77
                op(std::forward<Oa>(oargs)..., segment_factory(t.seg[i]), t.side[i]);
78
}
79
 
80
//-----------------------------------------------------------------
81
// Executes a link, attached to a trigger.
82
// Toggles all walls linked to the switch.
83
// Opens doors, Blasts blast walls, turns off illusions.
84
static void do_link(const trigger &t)
85
{
86
        auto &Walls = LevelUniqueWallSubsystemState.Walls;
87
        auto &vmwallptr = Walls.vmptr;
88
        trigger_wall_op(t, vmsegptridx, wall_toggle, vmwallptr);
89
}
90
 
91
#if defined(DXX_BUILD_DESCENT_II)
92
namespace dsx {
93
//close a door
94
static void do_close_door(const trigger &t)
95
{
96
        auto &Walls = LevelUniqueWallSubsystemState.Walls;
97
        trigger_wall_op(t, vmsegptridx, wall_close_door, Walls);
98
}
99
 
100
//turns lighting on.  returns true if lights were actually turned on. (they
101
//would not be if they had previously been shot out).
102
static int do_light_on(const d_level_shared_destructible_light_state &LevelSharedDestructibleLightState, const d_level_unique_tmap_info_state::TmapInfo_array &TmapInfo, d_flickering_light_state &Flickering_light_state, const trigger &t)
103
{
104
        int ret=0;
105
        const auto op = [&LevelSharedDestructibleLightState, &Flickering_light_state, &TmapInfo, &ret](const vmsegptridx_t segnum, const unsigned sidenum) {
106
                        //check if tmap2 casts light before turning the light on.  This
107
                        //is to keep us from turning on blown-out lights
108
                        if (TmapInfo[segnum->unique_segment::sides[sidenum].tmap_num2 & 0x3fff].lighting) {
109
                                ret |= add_light(LevelSharedDestructibleLightState, segnum, sidenum);           //any light sets flag
110
                                enable_flicker(Flickering_light_state, segnum, sidenum);
111
                        }
112
        };
113
        trigger_wall_op(t, vmsegptridx, op);
114
        return ret;
115
}
116
 
117
//turns lighting off.  returns true if lights were actually turned off. (they
118
//would not be if they had previously been shot out).
119
static int do_light_off(const d_level_shared_destructible_light_state &LevelSharedDestructibleLightState, const d_level_unique_tmap_info_state::TmapInfo_array &TmapInfo, d_flickering_light_state &Flickering_light_state, const trigger &t)
120
{
121
        int ret=0;
122
        const auto op = [&LevelSharedDestructibleLightState, &Flickering_light_state, &TmapInfo, &ret](const vmsegptridx_t segnum, const unsigned sidenum) {
123
                        //check if tmap2 casts light before turning the light off.  This
124
                        //is to keep us from turning off blown-out lights
125
                        if (TmapInfo[segnum->unique_segment::sides[sidenum].tmap_num2 & 0x3fff].lighting) {
126
                                ret |= subtract_light(LevelSharedDestructibleLightState, segnum, sidenum);      //any light sets flag
127
                                disable_flicker(Flickering_light_state, segnum, sidenum);
128
                        }
129
        };
130
        trigger_wall_op(t, vmsegptridx, op);
131
        return ret;
132
}
133
 
134
// Unlocks all doors linked to the switch.
135
static void do_unlock_doors(fvcsegptr &vcsegptr, fvmwallptr &vmwallptr, const trigger &t)
136
{
137
        const auto op = [&vmwallptr](const shared_segment &segp, const unsigned sidenum) {
138
                const auto wall_num = segp.sides[sidenum].wall_num;
139
                auto &w = *vmwallptr(wall_num);
140
                w.flags &= ~WALL_DOOR_LOCKED;
141
                w.keys = KEY_NONE;
142
        };
143
        trigger_wall_op(t, vcsegptr, op);
144
}
145
 
146
// Locks all doors linked to the switch.
147
static void do_lock_doors(fvcsegptr &vcsegptr, fvmwallptr &vmwallptr, const trigger &t)
148
{
149
        const auto op = [&vmwallptr](const shared_segment &segp, const unsigned sidenum) {
150
                const auto wall_num = segp.sides[sidenum].wall_num;
151
                auto &w = *vmwallptr(wall_num);
152
                w.flags |= WALL_DOOR_LOCKED;
153
        };
154
        trigger_wall_op(t, vcsegptr, op);
155
}
156
 
157
// Changes walls pointed to by a trigger. returns true if any walls changed
158
static int do_change_walls(const trigger &t, const uint8_t new_wall_type)
159
{
160
        auto &LevelSharedVertexState = LevelSharedSegmentState.get_vertex_state();
161
        auto &Objects = LevelUniqueObjectState.Objects;
162
        auto &Vertices = LevelSharedVertexState.get_vertices();
163
        auto &vmobjptr = Objects.vmptr;
164
        int ret=0;
165
 
166
        auto &TmapInfo = LevelUniqueTmapInfoState.TmapInfo;
167
        auto &Walls = LevelUniqueWallSubsystemState.Walls;
168
        auto &vmwallptr = Walls.vmptr;
169
        for (unsigned i = 0; i < t.num_links; ++i)
170
        {
171
                uint8_t cside;
172
                const auto &&segp = vmsegptridx(t.seg[i]);
173
                const auto side = t.side[i];
174
                        imsegptridx_t csegp = segment_none;
175
 
176
                        if (!IS_CHILD(segp->children[side]))
177
                        {
178
                                cside = side_none;
179
                        }
180
                        else
181
                        {
182
                                csegp = imsegptridx(segp->children[side]);
183
                                cside = find_connect_side(segp, csegp);
184
                                Assert(cside != side_none);
185
                        }
186
 
187
                        wall *w0p;
188
                        const auto w0num = segp->shared_segment::sides[side].wall_num;
189
                        if (const auto &&uw0p = vmwallptr.check_untrusted(w0num))
190
                                w0p = *uw0p;
191
                        else
192
                        {
193
                                LevelError("trigger %p link %u tried to open segment %hu, side %u which is an invalid wall; ignoring.", std::addressof(t), i, static_cast<segnum_t>(segp), side);
194
                                continue;
195
                        }
196
                        auto &wall0 = *w0p;
197
                        imwallptr_t wall1 = nullptr;
198
                        if ((cside == side_none || csegp->shared_segment::sides[cside].wall_num == wall_none ||
199
                                (wall1 = vmwallptr(csegp->shared_segment::sides[cside].wall_num))->type == new_wall_type) &&
200
                                wall0.type == new_wall_type)
201
                                continue;               //already in correct state, so skip
202
 
203
                        ret |= 1;
204
 
205
                        auto &vcvertptr = Vertices.vcptr;
206
                        switch (t.type)
207
                        {
208
                                case trigger_action::open_wall:
209
                                        if ((TmapInfo[segp->unique_segment::sides[side].tmap_num].flags & TMI_FORCE_FIELD)) {
210
                                                ret |= 2;
211
                                                const auto &&pos = compute_center_point_on_side(vcvertptr, segp, side);
212
                                                digi_link_sound_to_pos( SOUND_FORCEFIELD_OFF, segp, side, pos, 0, F1_0 );
213
                                                digi_kill_sound_linked_to_segment(segp,side,SOUND_FORCEFIELD_HUM);
214
                                                wall0.type = new_wall_type;
215
                                                if (wall1)
216
                                                {
217
                                                        wall1->type = new_wall_type;
218
                                                        digi_kill_sound_linked_to_segment(csegp, cside, SOUND_FORCEFIELD_HUM);
219
                                                }
220
                                        }
221
                                        else
222
                                                start_wall_cloak(segp,side);
223
                                        break;
224
 
225
                                case trigger_action::close_wall:
226
                                        if ((TmapInfo[segp->unique_segment::sides[side].tmap_num].flags & TMI_FORCE_FIELD)) {
227
                                                ret |= 2;
228
                                                {
229
                                                const auto &&pos = compute_center_point_on_side(vcvertptr, segp, side);
230
                                                digi_link_sound_to_pos(SOUND_FORCEFIELD_HUM,segp,side,pos,1, F1_0/2);
231
                                                }
232
                                        case trigger_action::illusory_wall:
233
                                                wall0.type = new_wall_type;
234
                                                if (wall1)
235
                                                        wall1->type = new_wall_type;
236
                                        }
237
                                        else
238
                                                start_wall_decloak(segp,side);
239
                                        break;
240
                                default:
241
                                        return 0;
242
                        }
243
 
244
                        LevelUniqueStuckObjectState.kill_stuck_objects(vmobjptr, segp->shared_segment::sides[side].wall_num);
245
                        if (wall1)
246
                                LevelUniqueStuckObjectState.kill_stuck_objects(vmobjptr, csegp->shared_segment::sides[cside].wall_num);
247
        }
248
        flush_fcd_cache();
249
 
250
        return ret;
251
}
252
 
253
#define print_trigger_message(pnum,trig,shot,message)   \
254
        ((void)((print_trigger_message(pnum,trig,shot)) &&              \
255
                HUD_init_message(HM_DEFAULT, message, &"s"[trig.num_links <= 1])))
256
 
257
static int (print_trigger_message)(int pnum, const trigger &t, int shot)
258
 {
259
        if (shot && pnum == Player_num && !(t.flags & trigger_behavior_flags::no_message))
260
                return 1;
261
        return 0;
262
 }
263
}
264
#endif
265
 
266
static void do_matcen(const trigger &t)
267
{
268
        range_for (auto &i, partial_const_range(t.seg, t.num_links))
269
                trigger_matcen(vmsegptridx(i));
270
}
271
 
272
static void do_il_on(fvcsegptridx &vcsegptridx, fvmwallptr &vmwallptr, const trigger &t)
273
{
274
        trigger_wall_op(t, vcsegptridx, wall_illusion_on, vmwallptr);
275
}
276
 
277
namespace dsx {
278
 
279
#if defined(DXX_BUILD_DESCENT_I)
280
static void do_il_off(fvcsegptridx &vcsegptridx, fvmwallptr &vmwallptr, const trigger &t)
281
{
282
        trigger_wall_op(t, vcsegptridx, wall_illusion_off, vmwallptr);
283
}
284
#elif defined(DXX_BUILD_DESCENT_II)
285
static void do_il_off(fvcsegptridx &vcsegptridx, fvcvertptr &vcvertptr, fvmwallptr &vmwallptr, const trigger &t)
286
{
287
        const auto &&op = [&vcvertptr, &vmwallptr](const vcsegptridx_t seg, const unsigned side) {
288
                wall_illusion_off(vmwallptr, seg, side);
289
                const auto &&cp = compute_center_point_on_side(vcvertptr, seg, side);
290
                digi_link_sound_to_pos(SOUND_WALL_REMOVED, seg, side, cp, 0, F1_0);
291
        };
292
        trigger_wall_op(t, vcsegptridx, op);
293
}
294
#endif
295
 
296
// Slight variation on window_event_result meaning
297
// 'ignored' means we still want check_trigger to call multi_send_trigger
298
// 'handled' or 'close' means we don't
299
// 'close' will still close the game window
300
window_event_result check_trigger_sub(object &plrobj, const trgnum_t trigger_num, const playernum_t pnum, const unsigned shot)
301
{
302
#if defined(DXX_BUILD_DESCENT_II)
303
        auto &LevelSharedVertexState = LevelSharedSegmentState.get_vertex_state();
304
        auto &Vertices = LevelSharedVertexState.get_vertices();
305
#endif
306
        auto &LevelUniqueControlCenterState = LevelUniqueObjectState.ControlCenterState;
307
        auto result = window_event_result::ignored;
308
 
309
        if ((Game_mode & GM_MULTI) && vcplayerptr(pnum)->connected != CONNECT_PLAYING) // as a host we may want to handle triggers for our clients. to do that properly we must check wether we (host) or client is actually playing.
310
                return window_event_result::handled;
311
        auto &Triggers = LevelUniqueWallSubsystemState.Triggers;
312
        auto &vmtrgptr = Triggers.vmptr;
313
        auto &trigger = *vmtrgptr(trigger_num);
314
        auto &Walls = LevelUniqueWallSubsystemState.Walls;
315
        auto &vmwallptr = Walls.vmptr;
316
 
317
#if defined(DXX_BUILD_DESCENT_I)
318
        (void)shot;
319
        if (pnum == Player_num) {
320
                auto &player_info = plrobj.ctype.player_info;
321
                if (trigger.flags & TRIGGER_SHIELD_DAMAGE) {
322
                        plrobj.shields -= trigger.value;
323
                }
324
 
325
                if (trigger.flags & TRIGGER_EXIT)
326
                {
327
                        result = start_endlevel_sequence();
328
                        if (result == window_event_result::handled)
329
                                result = window_event_result::ignored;  // call multi_send_trigger, or end game anyway
330
                }
331
 
332
                if (trigger.flags & TRIGGER_SECRET_EXIT) {
333
                        if (trigger.flags & TRIGGER_EXIT)
334
                                LevelError("Trigger %u is both a regular and secret exit! This is not a recommended combination.", trigger_num);
335
                        if (Newdemo_state == ND_STATE_RECORDING)                // stop demo recording
336
                                Newdemo_state = ND_STATE_PAUSED;
337
 
338
                        if (Game_mode & GM_MULTI)
339
                                multi_send_endlevel_start(multi_endlevel_type::secret);
340
                        if (Game_mode & GM_NETWORK)
341
                                multi_do_protocol_frame(1, 1);
342
                        result = std::max(PlayerFinishedLevel(1), result);              //1 means go to secret level
343
                        LevelUniqueControlCenterState.Control_center_destroyed = 0;
344
                        return std::max(result, window_event_result::handled);
345
                }
346
 
347
                if (trigger.flags & TRIGGER_ENERGY_DRAIN) {
348
                        player_info.energy -= trigger.value;
349
                }
350
        }
351
 
352
        if (trigger.flags & TRIGGER_CONTROL_DOORS) {
353
                do_link(trigger);
354
        }
355
 
356
        if (trigger.flags & TRIGGER_MATCEN) {
357
                if (!(Game_mode & GM_MULTI) || (Game_mode & GM_MULTI_ROBOTS))
358
                        do_matcen(trigger);
359
        }
360
 
361
        if (trigger.flags & TRIGGER_ILLUSION_ON) {
362
                do_il_on(vcsegptridx, vmwallptr, trigger);
363
        }
364
 
365
        if (trigger.flags & TRIGGER_ILLUSION_OFF) {
366
                do_il_off(vcsegptridx, vmwallptr, trigger);
367
        }
368
#elif defined(DXX_BUILD_DESCENT_II)
369
        if (trigger.flags & trigger_behavior_flags::disabled)
370
                return window_event_result::handled;            // don't send trigger hit to other players
371
 
372
        if (trigger.flags & trigger_behavior_flags::one_shot)           //if this is a one-shot...
373
                trigger.flags |= trigger_behavior_flags::disabled;              //..then don't let it happen again
374
 
375
        auto &LevelSharedDestructibleLightState = LevelSharedSegmentState.DestructibleLights;
376
        auto &TmapInfo = LevelUniqueTmapInfoState.TmapInfo;
377
        auto &vcvertptr = Vertices.vcptr;
378
        switch (trigger.type)
379
        {
380
                case trigger_action::normal_exit:
381
                        if (pnum!=Player_num)
382
                                break;
383
 
384
                        if (!EMULATING_D1)
385
                                digi_stop_digi_sounds();  //Sound shouldn't cut out when exiting a D1 lvl
386
 
387
                        if (Current_level_num > 0) {
388
                                result = start_endlevel_sequence();
389
                        } else if (Current_level_num < 0) {
390
                                if (plrobj.shields < 0 ||
391
                                        Player_dead_state != player_dead_state::no)
392
                                        break;
393
                                // NMN 04/09/07 Do endlevel movie if we are
394
                                //             playing a D1 secret level
395
                                if (EMULATING_D1)
396
                                {
397
                                        result = start_endlevel_sequence();
398
                                } else {
399
                                        result = ExitSecretLevel();
400
                                }
401
                                return std::max(result, window_event_result::handled);
402
                        } else {
403
#if DXX_USE_EDITOR
404
                                        nm_messagebox_str( "Yo!", "You have hit the exit trigger!", "" );
405
                                #else
406
                                        Int3();         //level num == 0, but no editor!
407
                                #endif
408
                        }
409
                        return std::max(result, window_event_result::handled);
410
                        break;
411
 
412
                case trigger_action::secret_exit: {
413
                        int     truth;
414
 
415
                        if (pnum!=Player_num)
416
                                break;
417
 
418
                        if (plrobj.shields < 0 ||
419
                                Player_dead_state != player_dead_state::no)
420
                                break;
421
 
422
                        if (is_SHAREWARE || is_MAC_SHARE) {
423
                                HUD_init_message_literal(HM_DEFAULT, "Secret Level Teleporter disabled in Descent 2 Demo");
424
                                digi_play_sample( SOUND_BAD_SELECTION, F1_0 );
425
                                break;
426
                        }
427
 
428
                        if (Game_mode & GM_MULTI) {
429
                                HUD_init_message_literal(HM_DEFAULT, "Secret Level Teleporter disabled in multiplayer!");
430
                                digi_play_sample( SOUND_BAD_SELECTION, F1_0 );
431
                                break;
432
                        }
433
 
434
                        truth = p_secret_level_destroyed();
435
 
436
                        if (Newdemo_state == ND_STATE_RECORDING)                        // record whether we're really going to the secret level
437
                                newdemo_record_secret_exit_blown(truth);
438
 
439
                        if ((Newdemo_state != ND_STATE_PLAYBACK) && truth) {
440
                                HUD_init_message_literal(HM_DEFAULT, "Secret Level destroyed.  Exit disabled.");
441
                                digi_play_sample( SOUND_BAD_SELECTION, F1_0 );
442
                                break;
443
                        }
444
 
445
                        if (Newdemo_state == ND_STATE_RECORDING)                // stop demo recording
446
                                Newdemo_state = ND_STATE_PAUSED;
447
 
448
                        digi_stop_digi_sounds();
449
 
450
                        EnterSecretLevel();
451
                        LevelUniqueControlCenterState.Control_center_destroyed = 0;
452
                        return window_event_result::handled;
453
                }
454
 
455
                case trigger_action::open_door:
456
                        do_link(trigger);
457
                        print_trigger_message(pnum, trigger, shot, "Door%s opened!");
458
 
459
                        break;
460
 
461
                case trigger_action::close_door:
462
                        do_close_door(trigger);
463
                        print_trigger_message(pnum, trigger, shot, "Door%s closed!");
464
                        break;
465
 
466
                case trigger_action::unlock_door:
467
                        do_unlock_doors(vcsegptr, vmwallptr, trigger);
468
                        print_trigger_message(pnum, trigger, shot, "Door%s unlocked!");
469
 
470
                        break;
471
 
472
                case trigger_action::lock_door:
473
                        do_lock_doors(vcsegptr, vmwallptr, trigger);
474
                        print_trigger_message(pnum, trigger, shot, "Door%s locked!");
475
                        break;
476
 
477
                case trigger_action::open_wall:
478
                        if (const auto w = do_change_walls(trigger, WALL_OPEN))
479
                                print_trigger_message(pnum, trigger, shot, (w & 2) ? "Force field%s deactivated!" : "Wall%s opened!");
480
                        break;
481
 
482
                case trigger_action::close_wall:
483
                        if (const auto w = do_change_walls(trigger, WALL_CLOSED))
484
                                print_trigger_message(pnum, trigger, shot, (w & 2) ? "Force field%s activated!" : "Wall%s closed!");
485
                        break;
486
 
487
                case trigger_action::illusory_wall:
488
                        //don't know what to say, so say nothing
489
                        do_change_walls(trigger, WALL_ILLUSION);
490
                        break;
491
 
492
                case trigger_action::matcen:
493
                        if (!(Game_mode & GM_MULTI) || (Game_mode & GM_MULTI_ROBOTS))
494
                                do_matcen(trigger);
495
                        break;
496
 
497
                case trigger_action::illusion_on:
498
                        do_il_on(vcsegptridx, vmwallptr, trigger);
499
                        print_trigger_message(pnum, trigger, shot, "Illusion%s on!");
500
                        break;
501
 
502
                case trigger_action::illusion_off:
503
                        do_il_off(vcsegptridx, vcvertptr, vmwallptr, trigger);
504
                        print_trigger_message(pnum, trigger, shot, "Illusion%s off!");
505
                        break;
506
 
507
                case trigger_action::light_off:
508
                        if (do_light_off(LevelSharedDestructibleLightState, TmapInfo, Flickering_light_state, trigger))
509
                                print_trigger_message(pnum, trigger, shot, "Light%s off!");
510
                        break;
511
 
512
                case trigger_action::light_on:
513
                        if (do_light_on(LevelSharedDestructibleLightState, TmapInfo, Flickering_light_state, trigger))
514
                                print_trigger_message(pnum, trigger, shot, "Light%s on!");
515
 
516
                        break;
517
 
518
                default:
519
                        Int3();
520
                        break;
521
        }
522
#endif
523
 
524
        return result;
525
}
526
 
527
//-----------------------------------------------------------------
528
// Checks for a trigger whenever an object hits a trigger side.
529
window_event_result check_trigger(const vcsegptridx_t seg, const unsigned side, object &plrobj, const vcobjptridx_t objnum, int shot)
530
{
531
        if ((Game_mode & GM_MULTI) && (get_local_player().connected != CONNECT_PLAYING)) // as a host we may want to handle triggers for our clients. so this function may be called when we are not playing.
532
                return window_event_result::ignored;
533
 
534
#if defined(DXX_BUILD_DESCENT_I)
535
        if (objnum == &plrobj)
536
#elif defined(DXX_BUILD_DESCENT_II)
537
        auto &Robot_info = LevelSharedRobotInfoState.Robot_info;
538
        if (objnum == &plrobj || (objnum->type == OBJ_ROBOT && Robot_info[get_robot_id(objnum)].companion))
539
#endif
540
        {
541
 
542
#if defined(DXX_BUILD_DESCENT_I)
543
                if ( Newdemo_state == ND_STATE_PLAYBACK )
544
                        return window_event_result::ignored;
545
#elif defined(DXX_BUILD_DESCENT_II)
546
                if ( Newdemo_state == ND_STATE_RECORDING )
547
                        newdemo_record_trigger( seg, side, objnum,shot);
548
#endif
549
 
550
                const auto wall_num = seg->shared_segment::sides[side].wall_num;
551
                if ( wall_num == wall_none ) return window_event_result::ignored;
552
 
553
                auto &Walls = LevelUniqueWallSubsystemState.Walls;
554
                auto &vcwallptr = Walls.vcptr;
555
                const auto trigger_num = vcwallptr(wall_num)->trigger;
556
                if (trigger_num == trigger_none)
557
                        return window_event_result::ignored;
558
 
559
                {
560
                        auto result = check_trigger_sub(plrobj, trigger_num, Player_num,shot);
561
                        if (result != window_event_result::ignored)
562
                                return result;
563
                }
564
 
565
                if (Game_mode & GM_MULTI)
566
                        multi_send_trigger(trigger_num);
567
        }
568
 
569
        return window_event_result::handled;
570
}
571
 
572
/*
573
 * reads a v29_trigger structure from a PHYSFS_File
574
 */
575
#if defined(DXX_BUILD_DESCENT_I)
576
void v26_trigger_read(PHYSFS_File *fp, trigger &t)
577
{
578
        switch (const auto type = static_cast<trigger_action>(PHYSFSX_readByte(fp)))
579
        {
580
                case trigger_action::open_door: // door
581
                        t.flags = TRIGGER_CONTROL_DOORS;
582
                        break;
583
                case trigger_action::matcen: // matcen
584
                        t.flags = TRIGGER_MATCEN;
585
                        break;
586
                case trigger_action::normal_exit: // exit
587
                        t.flags = TRIGGER_EXIT;
588
                        break;
589
                case trigger_action::secret_exit: // secret exit
590
                        t.flags = TRIGGER_SECRET_EXIT;
591
                        break;
592
                case trigger_action::illusion_off: // illusion off
593
                        t.flags = TRIGGER_ILLUSION_OFF;
594
                        break;
595
                case trigger_action::illusion_on: // illusion on
596
                        t.flags = TRIGGER_ILLUSION_ON;
597
                        break;
598
                default:
599
                        con_printf(CON_URGENT, "error: unsupported trigger type %d", static_cast<int>(type));
600
                        throw std::runtime_error("unsupported trigger type");
601
        }
602
        if (PHYSFSX_readByte(fp) & 2)   // one shot
603
                t.flags |= TRIGGER_ONE_SHOT;
604
        t.num_links = PHYSFSX_readShort(fp);
605
        t.value = PHYSFSX_readInt(fp);
606
        PHYSFSX_readInt(fp);
607
        for (unsigned i=0; i < MAX_WALLS_PER_LINK; i++ )
608
                t.seg[i] = PHYSFSX_readShort(fp);
609
        for (unsigned i=0; i < MAX_WALLS_PER_LINK; i++ )
610
                t.side[i] = PHYSFSX_readShort(fp);
611
}
612
 
613
void v25_trigger_read(PHYSFS_File *fp, trigger *t)
614
#elif defined(DXX_BUILD_DESCENT_II)
615
extern void v29_trigger_read(v29_trigger *t, PHYSFS_File *fp)
616
#endif
617
{
618
#if defined(DXX_BUILD_DESCENT_I)
619
        PHYSFSX_readByte(fp);
620
#elif defined(DXX_BUILD_DESCENT_II)
621
        t->type = PHYSFSX_readByte(fp);
622
#endif
623
        t->flags = PHYSFSX_readShort(fp);
624
        t->value = PHYSFSX_readFix(fp);
625
        PHYSFSX_readFix(fp);
626
        PHYSFSX_readByte(fp);
627
        t->num_links = PHYSFSX_readShort(fp);
628
        for (unsigned i=0; i<MAX_WALLS_PER_LINK; i++ )
629
                t->seg[i] = PHYSFSX_readShort(fp);
630
        for (unsigned i=0; i<MAX_WALLS_PER_LINK; i++ )
631
                t->side[i] = PHYSFSX_readShort(fp);
632
}
633
 
634
#if defined(DXX_BUILD_DESCENT_II)
635
/*
636
 * reads a v30_trigger structure from a PHYSFS_File
637
 */
638
extern void v30_trigger_read(v30_trigger *t, PHYSFS_File *fp)
639
{
640
        t->flags = PHYSFSX_readShort(fp);
641
        t->num_links = PHYSFSX_readByte(fp);
642
        t->pad = PHYSFSX_readByte(fp);
643
        t->value = PHYSFSX_readFix(fp);
644
        t->time = PHYSFSX_readFix(fp);
645
        for (unsigned i=0; i<MAX_WALLS_PER_LINK; i++ )
646
                t->seg[i] = PHYSFSX_readShort(fp);
647
        for (unsigned i=0; i<MAX_WALLS_PER_LINK; i++ )
648
                t->side[i] = PHYSFSX_readShort(fp);
649
}
650
 
651
/*
652
 * reads a trigger structure from a PHYSFS_File
653
 */
654
extern void trigger_read(trigger *t, PHYSFS_File *fp)
655
{
656
        t->type = trigger_action{static_cast<uint8_t>(PHYSFSX_readByte(fp))};
657
        t->flags = trigger_behavior_flags{static_cast<uint8_t>(PHYSFSX_readByte(fp))};
658
        t->num_links = PHYSFSX_readByte(fp);
659
        PHYSFSX_readByte(fp);
660
        t->value = PHYSFSX_readFix(fp);
661
        PHYSFSX_readFix(fp);
662
        for (unsigned i=0; i<MAX_WALLS_PER_LINK; i++ )
663
                t->seg[i] = PHYSFSX_readShort(fp);
664
        for (unsigned i=0; i<MAX_WALLS_PER_LINK; i++ )
665
                t->side[i] = PHYSFSX_readShort(fp);
666
}
667
 
668
static trigger_action trigger_type_from_flags(short flags)
669
{
670
        if (flags & TRIGGER_CONTROL_DOORS)
671
                return trigger_action::open_door;
672
        else if (flags & (TRIGGER_SHIELD_DAMAGE | TRIGGER_ENERGY_DRAIN))
673
        {
674
        }
675
        else if (flags & TRIGGER_EXIT)
676
                return trigger_action::normal_exit;
677
        else if (flags & TRIGGER_MATCEN)
678
                return trigger_action::matcen;
679
        else if (flags & TRIGGER_ILLUSION_OFF)
680
                return trigger_action::illusion_off;
681
        else if (flags & TRIGGER_SECRET_EXIT)
682
                return trigger_action::secret_exit;
683
        else if (flags & TRIGGER_ILLUSION_ON)
684
                return trigger_action::illusion_on;
685
        else if (flags & TRIGGER_UNLOCK_DOORS)
686
                return trigger_action::unlock_door;
687
        else if (flags & TRIGGER_OPEN_WALL)
688
                return trigger_action::open_wall;
689
        else if (flags & TRIGGER_CLOSE_WALL)
690
                return trigger_action::close_wall;
691
        else if (flags & TRIGGER_ILLUSORY_WALL)
692
                return trigger_action::illusory_wall;
693
        throw std::runtime_error("unsupported trigger type");
694
}
695
 
696
static void v30_trigger_to_v31_trigger(trigger &t, const v30_trigger &trig)
697
{
698
        t.type        = trigger_type_from_flags(trig.flags);
699
        t.flags       = (trig.flags & TRIGGER_ONE_SHOT) ? trigger_behavior_flags::one_shot : trigger_behavior_flags{0};
700
        t.num_links   = trig.num_links;
701
        t.num_links   = trig.num_links;
702
        t.value       = trig.value;
703
        t.seg = trig.seg;
704
        t.side = trig.side;
705
}
706
 
707
static void v29_trigger_read_as_v30(PHYSFS_File *fp, v30_trigger &trig)
708
{
709
        v29_trigger trig29;
710
        v29_trigger_read(&trig29, fp);
711
        trig.flags      = trig29.flags;
712
        // skip trig29.link_num. v30_trigger does not need it
713
        trig.num_links  = trig29.num_links;
714
        trig.value      = trig29.value;
715
        trig.time       = trig29.time;
716
        trig.seg = trig29.seg;
717
        trig.side = trig29.side;
718
}
719
 
720
void v29_trigger_read_as_v31(PHYSFS_File *fp, trigger &t)
721
{
722
        v30_trigger trig;
723
        v29_trigger_read_as_v30(fp, trig);
724
        v30_trigger_to_v31_trigger(t, trig);
725
}
726
 
727
void v30_trigger_read_as_v31(PHYSFS_File *fp, trigger &t)
728
{
729
        v30_trigger trig;
730
        v30_trigger_read(&trig, fp);
731
        v30_trigger_to_v31_trigger(t, trig);
732
}
733
#endif
734
 
735
#if defined(DXX_BUILD_DESCENT_I)
736
DEFINE_SERIAL_UDT_TO_MESSAGE(trigger, t, (serial::pad<1>(), t.flags, t.value, serial::pad<5>(), t.num_links, t.seg, t.side));
737
ASSERT_SERIAL_UDT_MESSAGE_SIZE(trigger, 54);
738
#elif defined(DXX_BUILD_DESCENT_II)
739
DEFINE_SERIAL_UDT_TO_MESSAGE(trigger, t, (t.type, t.flags, t.num_links, serial::pad<1>(), t.value, serial::pad<4>(), t.seg, t.side));
740
ASSERT_SERIAL_UDT_MESSAGE_SIZE(trigger, 52);
741
#endif
742
}
743
 
744
/*
745
 * reads n trigger structs from a PHYSFS_File and swaps if specified
746
 */
747
void trigger_read(PHYSFS_File *fp, trigger &t)
748
{
749
        PHYSFSX_serialize_read(fp, t);
750
}
751
 
752
void trigger_write(PHYSFS_File *fp, const trigger &t)
753
{
754
        PHYSFSX_serialize_write(fp, t);
755
}
756
 
757
namespace dsx {
758
void v29_trigger_write(PHYSFS_File *fp, const trigger &rt)
759
{
760
        const trigger *t = &rt;
761
        PHYSFSX_writeU8(fp, 0);         // unused 'type'
762
#if defined(DXX_BUILD_DESCENT_I)
763
        PHYSFS_writeSLE16(fp, t->flags);
764
#elif defined(DXX_BUILD_DESCENT_II)
765
        const auto one_shot_flag = (t->flags & trigger_behavior_flags::one_shot) ? TRIGGER_ONE_SHOT : TRIGGER_FLAG{0};
766
        switch (t->type)
767
        {
768
                case trigger_action::open_door:
769
                        PHYSFS_writeSLE16(fp, TRIGGER_CONTROL_DOORS | one_shot_flag);
770
                        break;
771
 
772
                case trigger_action::normal_exit:
773
                        PHYSFS_writeSLE16(fp, TRIGGER_EXIT | one_shot_flag);
774
                        break;
775
 
776
                case trigger_action::matcen:
777
                        PHYSFS_writeSLE16(fp, TRIGGER_MATCEN | one_shot_flag);
778
                        break;
779
 
780
                case trigger_action::illusion_off:
781
                        PHYSFS_writeSLE16(fp, TRIGGER_ILLUSION_OFF | one_shot_flag);
782
                        break;
783
 
784
                case trigger_action::secret_exit:
785
                        PHYSFS_writeSLE16(fp, TRIGGER_SECRET_EXIT | one_shot_flag);
786
                        break;
787
 
788
                case trigger_action::illusion_on:
789
                        PHYSFS_writeSLE16(fp, TRIGGER_ILLUSION_ON | one_shot_flag);
790
                        break;
791
 
792
                case trigger_action::unlock_door:
793
                        PHYSFS_writeSLE16(fp, TRIGGER_UNLOCK_DOORS | one_shot_flag);
794
                        break;
795
 
796
                case trigger_action::open_wall:
797
                        PHYSFS_writeSLE16(fp, TRIGGER_OPEN_WALL | one_shot_flag);
798
                        break;
799
 
800
                case trigger_action::close_wall:
801
                        PHYSFS_writeSLE16(fp, TRIGGER_CLOSE_WALL | one_shot_flag);
802
                        break;
803
 
804
                case trigger_action::illusory_wall:
805
                        PHYSFS_writeSLE16(fp, TRIGGER_ILLUSORY_WALL | one_shot_flag);
806
                        break;
807
 
808
                default:
809
                        Int3();
810
                        PHYSFS_writeSLE16(fp, 0);
811
                        break;
812
        }
813
#endif
814
 
815
        PHYSFSX_writeFix(fp, t->value);
816
        PHYSFSX_writeFix(fp, 0);
817
 
818
        PHYSFSX_writeU8(fp, -1);        //t->link_num
819
        PHYSFS_writeSLE16(fp, t->num_links);
820
 
821
        for (unsigned i = 0; i < MAX_WALLS_PER_LINK; i++)
822
                PHYSFS_writeSLE16(fp, t->seg[i]);
823
        for (unsigned i = 0; i < MAX_WALLS_PER_LINK; i++)
824
                PHYSFS_writeSLE16(fp, t->side[i]);
825
}
826
}
827
 
828
namespace dsx {
829
void v30_trigger_write(PHYSFS_File *fp, const trigger &rt)
830
{
831
        const trigger *t = &rt;
832
#if defined(DXX_BUILD_DESCENT_I)
833
        uint8_t action;
834
        if (t->flags & TRIGGER_CONTROL_DOORS)
835
                action = static_cast<uint8_t>(trigger_action::open_door); // door
836
        else if (t->flags & TRIGGER_MATCEN)
837
                action = static_cast<uint8_t>(trigger_action::matcen); // matcen
838
        else if (t->flags & TRIGGER_EXIT)
839
                action = static_cast<uint8_t>(trigger_action::normal_exit); // exit
840
        else if (t->flags & TRIGGER_SECRET_EXIT)
841
                action = static_cast<uint8_t>(trigger_action::secret_exit); // secret exit
842
        else if (t->flags & TRIGGER_ILLUSION_OFF)
843
                action = static_cast<uint8_t>(trigger_action::illusion_off); // illusion off
844
        else if (t->flags & TRIGGER_ILLUSION_ON)
845
                action = static_cast<uint8_t>(trigger_action::illusion_on); // illusion on
846
        else
847
                action = 0;
848
        PHYSFSX_writeU8(fp, action);
849
#elif defined(DXX_BUILD_DESCENT_II)
850
        PHYSFSX_writeU8(fp, static_cast<uint8_t>(t->type));
851
#endif
852
 
853
#if defined(DXX_BUILD_DESCENT_I)
854
        PHYSFS_writeSLE16(fp, t->flags);
855
#elif defined(DXX_BUILD_DESCENT_II)
856
        const auto one_shot_flag = (t->flags & trigger_behavior_flags::one_shot) ? TRIGGER_ONE_SHOT : TRIGGER_FLAG{0};
857
        switch (t->type)
858
        {
859
                case trigger_action::open_door:
860
                        PHYSFS_writeSLE16(fp, TRIGGER_CONTROL_DOORS | one_shot_flag);
861
                        break;
862
 
863
                case trigger_action::normal_exit:
864
                        PHYSFS_writeSLE16(fp, TRIGGER_EXIT | one_shot_flag);
865
                        break;
866
 
867
                case trigger_action::matcen:
868
                        PHYSFS_writeSLE16(fp, TRIGGER_MATCEN | one_shot_flag);
869
                        break;
870
 
871
                case trigger_action::illusion_off:
872
                        PHYSFS_writeSLE16(fp, TRIGGER_ILLUSION_OFF | one_shot_flag);
873
                        break;
874
 
875
                case trigger_action::secret_exit:
876
                        PHYSFS_writeSLE16(fp, TRIGGER_SECRET_EXIT | one_shot_flag);
877
                        break;
878
 
879
                case trigger_action::illusion_on:
880
                        PHYSFS_writeSLE16(fp, TRIGGER_ILLUSION_ON | one_shot_flag);
881
                        break;
882
 
883
                case trigger_action::unlock_door:
884
                        PHYSFS_writeSLE16(fp, TRIGGER_UNLOCK_DOORS | one_shot_flag);
885
                        break;
886
 
887
                case trigger_action::open_wall:
888
                        PHYSFS_writeSLE16(fp, TRIGGER_OPEN_WALL | one_shot_flag);
889
                        break;
890
 
891
                case trigger_action::close_wall:
892
                        PHYSFS_writeSLE16(fp, TRIGGER_CLOSE_WALL | one_shot_flag);
893
                        break;
894
 
895
                case trigger_action::illusory_wall:
896
                        PHYSFS_writeSLE16(fp, TRIGGER_ILLUSORY_WALL | one_shot_flag);
897
                        break;
898
 
899
                default:
900
                        Int3();
901
                        PHYSFS_writeSLE16(fp, 0);
902
                        break;
903
        }
904
#endif
905
 
906
        PHYSFSX_writeU8(fp, t->num_links);
907
        PHYSFSX_writeU8(fp, 0); // t->pad
908
 
909
        PHYSFSX_writeFix(fp, t->value);
910
        PHYSFSX_writeFix(fp, 0);
911
 
912
        for (unsigned i = 0; i < MAX_WALLS_PER_LINK; i++)
913
                PHYSFS_writeSLE16(fp, t->seg[i]);
914
        for (unsigned i = 0; i < MAX_WALLS_PER_LINK; i++)
915
                PHYSFS_writeSLE16(fp, t->side[i]);
916
}
917
}
918
 
919
namespace dsx {
920
void v31_trigger_write(PHYSFS_File *fp, const trigger &rt)
921
{
922
        const trigger *t = &rt;
923
#if defined(DXX_BUILD_DESCENT_I)
924
        uint8_t action;
925
        if (t->flags & TRIGGER_CONTROL_DOORS)
926
                action = static_cast<uint8_t>(trigger_action::open_door); // door
927
        else if (t->flags & TRIGGER_MATCEN)
928
                action = static_cast<uint8_t>(trigger_action::matcen); // matcen
929
        else if (t->flags & TRIGGER_EXIT)
930
                action = static_cast<uint8_t>(trigger_action::normal_exit); // exit
931
        else if (t->flags & TRIGGER_SECRET_EXIT)
932
                action = static_cast<uint8_t>(trigger_action::secret_exit); // secret exit
933
        else if (t->flags & TRIGGER_ILLUSION_OFF)
934
                action = static_cast<uint8_t>(trigger_action::illusion_off); // illusion off
935
        else if (t->flags & TRIGGER_ILLUSION_ON)
936
                action = static_cast<uint8_t>(trigger_action::illusion_on); // illusion on
937
        else
938
                action = 0;
939
        PHYSFSX_writeU8(fp, action);
940
#elif defined(DXX_BUILD_DESCENT_II)
941
        PHYSFSX_writeU8(fp, static_cast<uint8_t>(t->type));
942
#endif
943
 
944
#if defined(DXX_BUILD_DESCENT_I)
945
        PHYSFSX_writeU8(fp, (t->flags & TRIGGER_ONE_SHOT) ? 2 : 0);             // flags
946
#elif defined(DXX_BUILD_DESCENT_II)
947
        PHYSFSX_writeU8(fp, static_cast<uint8_t>(t->flags));
948
#endif
949
 
950
        PHYSFSX_writeU8(fp, t->num_links);
951
        PHYSFSX_writeU8(fp, 0); // t->pad
952
 
953
        PHYSFSX_writeFix(fp, t->value);
954
        PHYSFSX_writeFix(fp, 0);
955
 
956
        for (unsigned i = 0; i < MAX_WALLS_PER_LINK; i++)
957
                PHYSFS_writeSLE16(fp, t->seg[i]);
958
        for (unsigned i = 0; i < MAX_WALLS_PER_LINK; i++)
959
                PHYSFS_writeSLE16(fp, t->side[i]);
960
}
961
}