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
 * Game Controls Stuff
23
 *
24
 */
25
 
26
#include <algorithm>
27
#include <stdio.h>
28
#include <cstdlib>
29
#include <string.h>
30
#include <stdarg.h>
31
 
32
#include "pstypes.h"
33
#include "window.h"
34
#include "console.h"
35
#include "collide.h"
36
#include "strutil.h"
37
#include "game.h"
38
#include "player.h"
39
#include "key.h"
40
#include "object.h"
41
#include "menu.h"
42
#include "physics.h"
43
#include "dxxerror.h"
44
#include "joy.h"
45
#include "iff.h"
46
#include "pcx.h"
47
#include "timer.h"
48
#include "render.h"
49
#include "laser.h"
50
#include "screens.h"
51
#include "textures.h"
52
#include "slew.h"
53
#include "gauges.h"
54
#include "texmap.h"
55
#include "3d.h"
56
#include "effects.h"
57
#include "gameseg.h"
58
#include "wall.h"
59
#include "ai.h"
60
#include "rbaudio.h"
61
#include "digi.h"
62
#include "u_mem.h"
63
#include "palette.h"
64
#include "morph.h"
65
#include "robot.h"
66
#include "lighting.h"
67
#include "newdemo.h"
68
#include "weapon.h"
69
#include "sounds.h"
70
#include "args.h"
71
#include "gameseq.h"
72
#include "automap.h"
73
#include "text.h"
74
#include "powerup.h"
75
#include "songs.h"
76
#include "newmenu.h"
77
#include "gamefont.h"
78
#include "endlevel.h"
79
#include "config.h"
80
#include "hudmsg.h"
81
#include "kconfig.h"
82
#include "mouse.h"
83
#include "titles.h"
84
#include "gr.h"
85
#include "playsave.h"
86
#include "scores.h"
87
 
88
#include "multi.h"
89
#include "cntrlcen.h"
90
#include "fuelcen.h"
91
#include "pcx.h"
92
#include "state.h"
93
#include "piggy.h"
94
#include "multibot.h"
95
#include "ai.h"
96
#include "rbaudio.h"
97
#include "switch.h"
98
#if defined(DXX_BUILD_DESCENT_II)
99
#include "escort.h"
100
#include "movie.h"
101
#endif
102
#include "window.h"
103
 
104
#if DXX_USE_EDITOR
105
#include "editor/editor.h"
106
#include "editor/esegment.h"
107
#endif
108
 
109
#include "compiler-range_for.h"
110
#include "d_range.h"
111
#include "partial_range.h"
112
#include <array>
113
#include <utility>
114
 
115
#include <SDL.h>
116
 
117
#if defined(__GNUC__) && defined(WIN32)
118
/* Mingw64 _mingw_print_pop.h changes PRIi64 to POSIX-style.  Change it
119
 * back here.
120
 *
121
 * Some outdated mingw32 users are also affected.
122
 */
123
#undef PRIi64
124
#define PRIi64 "I64i"
125
#endif
126
 
127
using std::min;
128
 
129
// Global Variables -----------------------------------------------------------
130
 
131
//      Function prototypes --------------------------------------------------------
132
#ifndef RELEASE
133
static void do_cheat_menu();
134
static void play_test_sound();
135
#endif
136
 
137
#define key_isfunc(k) (((k&0xff)>=KEY_F1 && (k&0xff)<=KEY_F10) || (k&0xff)==KEY_F11 || (k&0xff)==KEY_F12)
138
 
139
// Functions ------------------------------------------------------------------
140
 
141
#if defined(DXX_BUILD_DESCENT_II)
142
#define CONVERTER_RATE  20              //10 units per second xfer rate
143
#define CONVERTER_SCALE  2              //2 units energy -> 1 unit shields
144
 
145
#define CONVERTER_SOUND_DELAY (f1_0/2)          //play every half second
146
 
147
static void transfer_energy_to_shield(object &plrobj)
148
{
149
        static fix64 last_play_time=0;
150
 
151
        auto &player_info = plrobj.ctype.player_info;
152
        auto &shields = plrobj.shields;
153
        auto &energy = player_info.energy;
154
        //how much energy gets transfered
155
        const fix e = min(min(FrameTime*CONVERTER_RATE, energy - INITIAL_ENERGY), (MAX_SHIELDS - shields) * CONVERTER_SCALE);
156
 
157
        if (e <= 0) {
158
 
159
                if (energy <= INITIAL_ENERGY) {
160
                        HUD_init_message(HM_DEFAULT, "Need more than %i energy to enable transfer", f2i(INITIAL_ENERGY));
161
                }
162
                else if (shields >= MAX_SHIELDS)
163
                {
164
                        HUD_init_message_literal(HM_DEFAULT, "No transfer: Shields already at max");
165
                }
166
                return;
167
        }
168
 
169
        energy  -= e;
170
        shields += e / CONVERTER_SCALE;
171
 
172
        if (last_play_time > GameTime64)
173
                last_play_time = 0;
174
 
175
        if (GameTime64 > last_play_time+CONVERTER_SOUND_DELAY) {
176
                digi_play_sample_once(SOUND_CONVERT_ENERGY, F1_0);
177
                last_play_time = GameTime64;
178
        }
179
}
180
#endif
181
 
182
 
183
// Control Functions
184
 
185
static fix64 newdemo_single_frame_time;
186
 
187
static void update_vcr_state(void)
188
{
189
        if ((keyd_pressed[KEY_LSHIFT] || keyd_pressed[KEY_RSHIFT]) && keyd_pressed[KEY_RIGHT] && d_tick_step)
190
                Newdemo_vcr_state = ND_STATE_FASTFORWARD;
191
        else if ((keyd_pressed[KEY_LSHIFT] || keyd_pressed[KEY_RSHIFT]) && keyd_pressed[KEY_LEFT] && d_tick_step)
192
                Newdemo_vcr_state = ND_STATE_REWINDING;
193
        else if (!(keyd_pressed[KEY_LCTRL] || keyd_pressed[KEY_RCTRL]) && keyd_pressed[KEY_RIGHT] && ((GameTime64 - newdemo_single_frame_time) >= F1_0) && d_tick_step)
194
                Newdemo_vcr_state = ND_STATE_ONEFRAMEFORWARD;
195
        else if (!(keyd_pressed[KEY_LCTRL] || keyd_pressed[KEY_RCTRL]) && keyd_pressed[KEY_LEFT] && ((GameTime64 - newdemo_single_frame_time) >= F1_0) && d_tick_step)
196
                Newdemo_vcr_state = ND_STATE_ONEFRAMEBACKWARD;
197
        else if ((Newdemo_vcr_state == ND_STATE_FASTFORWARD) || (Newdemo_vcr_state == ND_STATE_REWINDING))
198
                Newdemo_vcr_state = ND_STATE_PLAYBACK;
199
}
200
 
201
namespace dsx {
202
#if defined(DXX_BUILD_DESCENT_II)
203
//returns which bomb will be dropped next time the bomb key is pressed
204
int which_bomb()
205
{
206
        auto &Objects = LevelUniqueObjectState.Objects;
207
        auto &vmobjptr = Objects.vmptr;
208
        //use the last one selected, unless there aren't any, in which case use
209
        //the other if there are any
210
        auto &player_info = get_local_plrobj().ctype.player_info;
211
        auto &Secondary_last_was_super = player_info.Secondary_last_was_super;
212
        const auto mask = 1 << PROXIMITY_INDEX;
213
        const auto bomb = (Secondary_last_was_super & mask) ? SMART_MINE_INDEX : PROXIMITY_INDEX;
214
 
215
        auto &secondary_ammo = player_info.secondary_ammo;
216
        if (secondary_ammo[bomb] == 0 &&
217
                secondary_ammo[SMART_MINE_INDEX + PROXIMITY_INDEX - bomb] != 0)
218
        {
219
                Secondary_last_was_super ^= mask;
220
        }
221
        return bomb;
222
}
223
#endif
224
 
225
static void do_weapon_n_item_stuff(object_array &Objects)
226
{
227
        auto &vmobjptridx = Objects.vmptridx;
228
        auto &vmobjptr = Objects.vmptr;
229
        auto &plrobj = get_local_plrobj();
230
        auto &player_info = plrobj.ctype.player_info;
231
        if (Controls.state.fire_flare > 0)
232
        {
233
                Controls.state.fire_flare = 0;
234
                if (allowed_to_fire_flare(player_info))
235
                        Flare_create(vmobjptridx(ConsoleObject));
236
        }
237
 
238
        if (allowed_to_fire_missile(player_info) && Controls.state.fire_secondary)
239
        {
240
                Global_missile_firing_count += Weapon_info[Secondary_weapon_to_weapon_info[player_info.Secondary_weapon]].fire_count;
241
        }
242
 
243
        if (Global_missile_firing_count) {
244
                do_missile_firing(0);
245
                Global_missile_firing_count--;
246
        }
247
 
248
        if (Controls.state.cycle_primary > 0)
249
        {
250
                for (uint_fast32_t i = std::exchange(Controls.state.cycle_primary, 0); i--;)
251
                        CyclePrimary(player_info);
252
        }
253
        if (Controls.state.cycle_secondary > 0)
254
        {
255
                for (uint_fast32_t i = std::exchange(Controls.state.cycle_secondary, 0); i--;)
256
                        CycleSecondary(player_info);
257
        }
258
        if (Controls.state.select_weapon > 0)
259
        {
260
                const auto select_weapon = std::exchange(Controls.state.select_weapon, 0) - 1;
261
                const auto weapon_num = select_weapon > 4 ? select_weapon - 5 : select_weapon;
262
                if (select_weapon > 4)
263
                        do_secondary_weapon_select(player_info, static_cast<secondary_weapon_index_t>(select_weapon - 5));
264
                else
265
                        do_primary_weapon_select(player_info, weapon_num);
266
        }
267
#if defined(DXX_BUILD_DESCENT_II)
268
        if (auto &headlight = Controls.state.headlight)
269
        {
270
                if (std::exchange(headlight, 0) & 1)
271
                        toggle_headlight_active(plrobj);
272
        }
273
#endif
274
 
275
        if (Global_missile_firing_count < 0)
276
                Global_missile_firing_count = 0;
277
 
278
        //      Drop proximity bombs.
279
        if (Controls.state.drop_bomb > 0)
280
        {
281
                for (uint_fast32_t i = std::exchange(Controls.state.drop_bomb, 0); i--;)
282
                do_missile_firing(1);
283
        }
284
#if defined(DXX_BUILD_DESCENT_II)
285
        if (Controls.state.toggle_bomb > 0)
286
        {
287
                auto &Secondary_last_was_super = player_info.Secondary_last_was_super;
288
                auto &secondary_ammo = player_info.secondary_ammo;
289
                int sound;
290
                if (!secondary_ammo[PROXIMITY_INDEX] && !secondary_ammo[SMART_MINE_INDEX])
291
                {
292
                        HUD_init_message_literal(HM_DEFAULT, "No bombs available!");
293
                        sound = SOUND_BAD_SELECTION;
294
                }
295
                else
296
                {      
297
                        const auto mask = (1 << PROXIMITY_INDEX);
298
                        const char *desc;
299
                        const auto bomb = (Secondary_last_was_super & mask) ? (desc = "Proximity bombs", PROXIMITY_INDEX) : (desc = "Smart mines", SMART_MINE_INDEX);
300
                        if (secondary_ammo[bomb] == 0)
301
                        {
302
                                HUD_init_message(HM_DEFAULT, "No %s available!", desc);
303
                                sound = SOUND_BAD_SELECTION;
304
                        }
305
                        else
306
                        {
307
                                Secondary_last_was_super ^= mask;
308
                                sound = SOUND_GOOD_SELECTION_SECONDARY;
309
                        }
310
                }
311
                digi_play_sample_once(sound, F1_0);
312
                Controls.state.toggle_bomb = 0;
313
        }
314
 
315
        if (Controls.state.energy_to_shield && (player_info.powerup_flags & PLAYER_FLAGS_CONVERTER))
316
                transfer_energy_to_shield(plrobj);
317
#endif
318
}
319
}
320
 
321
static void format_time(char (&str)[9], unsigned secs_int, unsigned hours_extra)
322
{
323
        auto d1 = std::div(secs_int, 60);
324
        const unsigned s = d1.rem;
325
        const unsigned m1 = d1.quot;
326
        auto d2 = std::div(m1, 60);
327
        const unsigned m = d2.rem;
328
        const unsigned h = d2.quot + hours_extra;
329
        snprintf(str, sizeof(str), "%1u:%02u:%02u", h, m, s);
330
}
331
 
332
struct pause_window : ignore_window_pointer_t
333
{
334
        std::array<char, 1024> msg;
335
};
336
 
337
//Process selected keys until game unpaused
338
static window_event_result pause_handler(window *, const d_event &event, pause_window *p)
339
{
340
        int key;
341
 
342
        switch (event.type)
343
        {
344
                case EVENT_WINDOW_ACTIVATED:
345
                        game_flush_inputs();
346
                        break;
347
 
348
                case EVENT_KEY_COMMAND:
349
                        key = event_key_get(event);
350
 
351
                        switch (key)
352
                        {
353
                                case 0:
354
                                        break;
355
                                case KEY_ESC:
356
                                        return window_event_result::close;
357
                                case KEY_F1:
358
                                        show_help();
359
                                        return window_event_result::handled;
360
                                case KEY_PAUSE:
361
                                        return window_event_result::close;
362
                                default:
363
                                        break;
364
                        }
365
                        break;
366
 
367
                case EVENT_IDLE:
368
                        timer_delay2(50);
369
                        break;
370
 
371
                case EVENT_WINDOW_DRAW:
372
                        show_boxed_message(&p->msg[0], 1);
373
                        break;
374
 
375
                case EVENT_WINDOW_CLOSE:
376
                        songs_resume();
377
                        delete p;
378
                        break;
379
 
380
                default:
381
                        break;
382
        }
383
        return window_event_result::ignored;
384
}
385
 
386
static int do_game_pause()
387
{
388
        auto &Objects = LevelUniqueObjectState.Objects;
389
        auto &vcobjptr = Objects.vcptr;
390
        char total_time[9],level_time[9];
391
 
392
        if (Game_mode & GM_MULTI)
393
        {
394
                netplayerinfo_on= !netplayerinfo_on;
395
                return(KEY_PAUSE);
396
        }
397
 
398
        pause_window *p = new pause_window;
399
        songs_pause();
400
 
401
        auto &plr = get_local_player();
402
        format_time(total_time, f2i(plr.time_total), plr.hours_total);
403
        format_time(level_time, f2i(plr.time_level), plr.hours_level);
404
        auto &player_info = vcobjptr(plr.objnum)->ctype.player_info;
405
        if (Newdemo_state!=ND_STATE_PLAYBACK)
406
                snprintf(&p->msg[0], p->msg.size(), "PAUSE\n\nSkill level:  %s\nHostages on board:  %d\nTime on level: %s\nTotal time in game: %s", MENU_DIFFICULTY_TEXT(GameUniqueState.Difficulty_level), player_info.mission.hostages_on_board, level_time, total_time);
407
        else
408
                snprintf(&p->msg[0], p->msg.size(), "PAUSE\n\n\n\n");
409
        set_screen_mode(SCREEN_MENU);
410
 
411
        if (!window_create(grd_curscreen->sc_canvas, 0, 0, SWIDTH, SHEIGHT, pause_handler, p))
412
                delete p;
413
 
414
        return 0 /*key*/;       // Keycode returning ripped out (kreatordxx)
415
}
416
 
417
static window_event_result HandleEndlevelKey(int key)
418
{
419
        switch (key)
420
        {
421
                case KEY_COMMAND+KEY_P:
422
                case KEY_PAUSE:
423
                        do_game_pause();
424
                        return window_event_result::handled;
425
 
426
                case KEY_ESC:
427
                        last_drawn_cockpit=-1;
428
                        return stop_endlevel_sequence();
429
        }
430
 
431
        return window_event_result::ignored;
432
}
433
 
434
static int HandleDeathInput(const d_event &event)
435
{
436
        const auto input_aborts_death_sequence = [&]() {
437
                const auto RespawnMode = PlayerCfg.RespawnMode;
438
                if (event.type == EVENT_KEY_COMMAND)
439
                {
440
                        const auto key = event_key_get(event);
441
                        if ((RespawnMode == RespawnPress::Any && !key_isfunc(key) && key != KEY_PAUSE && key) ||
442
                                (key == KEY_ESC && ConsoleObject->flags & OF_EXPLODING))
443
                                return 1;
444
                }
445
 
446
                if (RespawnMode == RespawnPress::Any
447
                        ? (event.type == EVENT_JOYSTICK_BUTTON_UP || event.type == EVENT_MOUSE_BUTTON_UP)
448
                        : (Controls.state.fire_primary || Controls.state.fire_secondary || Controls.state.fire_flare))
449
                        return 1;
450
                return 0;
451
        };
452
        if (Player_dead_state == player_dead_state::exploded && input_aborts_death_sequence())
453
        {
454
                GameViewUniqueState.Death_sequence_aborted = 1;
455
        }
456
 
457
        if (GameViewUniqueState.Death_sequence_aborted)
458
        {
459
                game_flush_respawn_inputs();
460
                return 1;
461
        }
462
 
463
        return 0;
464
}
465
 
466
#if DXX_USE_SCREENSHOT
467
static void save_pr_screenshot()
468
{
469
        gr_set_default_canvas();
470
        auto &canvas = *grd_curcanv;
471
        render_frame(canvas, 0);
472
        auto &medium2_font = *MEDIUM2_FONT;
473
        gr_string(canvas, medium2_font, SWIDTH - FSPACX(92), SHEIGHT - LINE_SPACING(medium2_font, *GAME_FONT), "DXX-Rebirth\n");
474
        gr_flip();
475
        save_screen_shot(0);
476
}
477
 
478
static void save_clean_screenshot()
479
{
480
        game_render_frame_mono(CGameArg.DbgNoDoubleBuffer);
481
        save_screen_shot(0);
482
}
483
#endif
484
 
485
static window_event_result HandleDemoKey(int key)
486
{
487
        switch (key) {
488
                KEY_MAC(case KEY_COMMAND+KEY_1:)
489
                case KEY_F1:    show_newdemo_help();    break;
490
                KEY_MAC(case KEY_COMMAND+KEY_2:)
491
                case KEY_F2:    do_options_menu();      break;
492
                KEY_MAC(case KEY_COMMAND+KEY_3:)
493
                case KEY_F3:
494
                         if (Viewer->type == OBJ_PLAYER)
495
                                toggle_cockpit();
496
                         break;
497
                KEY_MAC(case KEY_COMMAND+KEY_4:)
498
                case KEY_F4:    Newdemo_show_percentage = !Newdemo_show_percentage; break;
499
                KEY_MAC(case KEY_COMMAND+KEY_7:)
500
                case KEY_F7:
501
                        Show_kill_list = (Show_kill_list+1) % ((Newdemo_game_mode & GM_TEAM) ? 4 : 3);
502
                        break;
503
                case KEY_ESC:
504
                        if (CGameArg.SysAutoDemo)
505
                        {
506
                                int choice;
507
                                choice = nm_messagebox( NULL, 2, TXT_YES, TXT_NO, TXT_ABORT_AUTODEMO );
508
                                if (choice == 0)
509
                                        CGameArg.SysAutoDemo = false;
510
                                else
511
                                        break;
512
                        }
513
                        newdemo_stop_playback();
514
                        return window_event_result::close;
515
                        break;
516
                case KEY_UP:
517
                        Newdemo_vcr_state = ND_STATE_PLAYBACK;
518
                        break;
519
                case KEY_DOWN:
520
                        Newdemo_vcr_state = ND_STATE_PAUSED;
521
                        break;
522
                case KEY_LEFT:
523
                        newdemo_single_frame_time = GameTime64;
524
                        Newdemo_vcr_state = ND_STATE_ONEFRAMEBACKWARD;
525
                        break;
526
                case KEY_RIGHT:
527
                        newdemo_single_frame_time = GameTime64;
528
                        Newdemo_vcr_state = ND_STATE_ONEFRAMEFORWARD;
529
                        break;
530
                case KEY_CTRLED + KEY_RIGHT:
531
                        return newdemo_goto_end(0);
532
                        break;
533
                case KEY_CTRLED + KEY_LEFT:
534
                        return newdemo_goto_beginning();
535
                        break;
536
 
537
                KEY_MAC(case KEY_COMMAND+KEY_P:)
538
                case KEY_PAUSE:
539
                        do_game_pause();
540
                        break;
541
 
542
#if DXX_USE_SCREENSHOT
543
#ifdef macintosh
544
                case KEY_COMMAND + KEY_SHIFTED + KEY_3:
545
#endif
546
                case KEY_PRINT_SCREEN:
547
                {
548
                        if (PlayerCfg.PRShot)
549
                        {
550
                                save_pr_screenshot();
551
                        }
552
                        else
553
                        {
554
                                int old_state;
555
                                old_state = Newdemo_show_percentage;
556
                                Newdemo_show_percentage = 0;
557
                                save_clean_screenshot();
558
                                Newdemo_show_percentage = old_state;
559
                        }
560
                        break;
561
                }
562
#endif
563
#ifndef NDEBUG
564
                case KEY_DEBUGGED + KEY_I:
565
                        Newdemo_do_interpolate = !Newdemo_do_interpolate;
566
                        HUD_init_message(HM_DEFAULT, "Demo playback interpolation %s", Newdemo_do_interpolate?"ON":"OFF");
567
                        break;
568
                case KEY_DEBUGGED + KEY_K: {
569
                        int how_many, c;
570
                        char filename[FILENAME_LEN], num[16];
571
                        std::array<newmenu_item, 2> m{{
572
                                nm_item_text("output file name"),
573
                                nm_item_input(filename),
574
                        }};
575
                        filename[0] = '\0';
576
                        c = newmenu_do( NULL, NULL, m, unused_newmenu_subfunction, unused_newmenu_userdata);
577
                        if (c == -2)
578
                                break;
579
                        strcat(filename, DEMO_EXT);
580
                        num[0] = '\0';
581
                        m = {{
582
                                nm_item_text("strip how many bytes"),
583
                                nm_item_input(num),
584
                        }};
585
                        c = newmenu_do( NULL, NULL, m, unused_newmenu_subfunction, unused_newmenu_userdata);
586
                        if (c == -2)
587
                                break;
588
                        how_many = atoi(num);
589
                        if (how_many <= 0)
590
                                break;
591
                        newdemo_strip_frames(filename, how_many);
592
 
593
                        break;
594
                }
595
#endif
596
 
597
                default:
598
                        return window_event_result::ignored;
599
        }
600
 
601
        return window_event_result::handled;
602
}
603
 
604
#if defined(DXX_BUILD_DESCENT_II)
605
//switch a cockpit window to the next function
606
static int select_next_window_function(int w)
607
{
608
        auto &Objects = LevelUniqueObjectState.Objects;
609
        auto &vmobjptridx = Objects.vmptridx;
610
        Assert(w==0 || w==1);
611
 
612
        auto &Robot_info = LevelSharedRobotInfoState.Robot_info;
613
        switch (PlayerCfg.Cockpit3DView[w]) {
614
                case CV_NONE:
615
                        PlayerCfg.Cockpit3DView[w] = CV_REAR;
616
                        break;
617
                case CV_REAR:
618
                        if (find_escort(vmobjptridx, Robot_info) != object_none)
619
                        {
620
                                PlayerCfg.Cockpit3DView[w] = CV_ESCORT;
621
                                break;
622
                        }
623
                        //if no ecort, fall through
624
                        DXX_BOOST_FALLTHROUGH;
625
                case CV_ESCORT:
626
                        Coop_view_player[w] = UINT_MAX;         //force first player
627
                        DXX_BOOST_FALLTHROUGH;
628
                case CV_COOP:
629
                        Marker_viewer_num[w] = game_marker_index::None;
630
                        if ((Game_mode & GM_MULTI_COOP) || (Game_mode & GM_TEAM)) {
631
                                PlayerCfg.Cockpit3DView[w] = CV_COOP;
632
                                for (;;)
633
                                {
634
                                        const auto cvp = ++ Coop_view_player[w];
635
                                        if (cvp == MAX_PLAYERS - 1)
636
                                        {
637
                                                PlayerCfg.Cockpit3DView[w] = CV_MARKER;
638
                                                goto case_marker;
639
                                        }
640
                                        if (cvp == Player_num)
641
                                                continue;
642
                                        if (vcplayerptr(cvp)->connected != CONNECT_PLAYING)
643
                                                continue;
644
 
645
                                        if (Game_mode & GM_MULTI_COOP)
646
                                                break;
647
                                        else if (get_team(cvp) == get_team(Player_num))
648
                                                break;
649
                                }
650
                                break;
651
                        }
652
                        //if not multi,
653
                        DXX_BOOST_FALLTHROUGH;
654
                case CV_MARKER:
655
                case_marker:;
656
                        if ((Game_mode & GM_MULTI) && !(Game_mode & GM_MULTI_COOP) && Netgame.Allow_marker_view) {      //anarchy only
657
                                PlayerCfg.Cockpit3DView[w] = CV_MARKER;
658
                                auto &mvn = Marker_viewer_num[w];
659
                                const auto gmi0 = convert_player_marker_index_to_game_marker_index(Game_mode, Netgame.max_numplayers, Player_num, player_marker_index::_0);
660
                                if (!MarkerState.imobjidx.valid_index(mvn))
661
                                        mvn = gmi0;
662
                                else
663
                                {
664
                                        ++ mvn;
665
                                        if (!MarkerState.imobjidx.valid_index(mvn))
666
                                                PlayerCfg.Cockpit3DView[w] = CV_NONE;
667
                                }
668
                        }
669
                        else
670
                                PlayerCfg.Cockpit3DView[w] = CV_NONE;
671
                        break;
672
        }
673
        write_player_file();
674
 
675
        return 1;        //screen_changed
676
}
677
#endif
678
 
679
//this is for system-level keys, such as help, etc.
680
//returns 1 if screen changed
681
namespace dsx {
682
static window_event_result HandleSystemKey(int key)
683
{
684
        if (Player_dead_state == player_dead_state::no)
685
                switch (key)
686
                {
687
                        case KEY_ESC:
688
                        {
689
                                const bool allow_saveload = !(Game_mode & GM_MULTI) || ((Game_mode & GM_MULTI_COOP) && Player_num == 0);
690
                                const auto choice = nm_messagebox_str(nullptr, allow_saveload ? nm_messagebox_tie("Abort Game", TXT_OPTIONS_, "Save Game...", TXT_LOAD_GAME) : nm_messagebox_tie("Abort Game", TXT_OPTIONS_), "Game Menu");
691
                                switch(choice)
692
                                {
693
                                        case 0:
694
                                                return window_event_result::close;
695
                                        case 1:
696
                                                return HandleSystemKey(KEY_F2);
697
                                        case 2:
698
                                                return HandleSystemKey(KEY_ALTED | KEY_F2);
699
                                        case 3:
700
                                                return HandleSystemKey(KEY_ALTED | KEY_F3);
701
                                }
702
                                return window_event_result::handled;
703
                        }
704
#if defined(DXX_BUILD_DESCENT_II)
705
// fleshed these out because F1 and F2 aren't sequenctial keycodes on mac -- MWA
706
 
707
                        KEY_MAC(case KEY_COMMAND+KEY_SHIFTED+KEY_1:)
708
                        case KEY_SHIFTED+KEY_F1:
709
                                select_next_window_function(0);
710
                                return window_event_result::handled;
711
                        KEY_MAC(case KEY_COMMAND+KEY_SHIFTED+KEY_2:)
712
                        case KEY_SHIFTED+KEY_F2:
713
                                select_next_window_function(1);
714
                                return window_event_result::handled;
715
#endif
716
                }
717
 
718
        switch (key)
719
        {
720
                KEY_MAC( case KEY_COMMAND+KEY_PAUSE+KEY_SHIFTED: )
721
                case KEY_PAUSE+KEY_SHIFTED:
722
                        if (Game_mode & GM_MULTI)
723
                                show_netgame_info(Netgame);
724
                        break;
725
                KEY_MAC( case KEY_COMMAND+KEY_P: )
726
                case KEY_PAUSE:
727
                        do_game_pause();        break;
728
 
729
 
730
#if DXX_USE_SCREENSHOT
731
#ifdef macintosh
732
                case KEY_COMMAND + KEY_SHIFTED + KEY_3:
733
#endif
734
                case KEY_PRINT_SCREEN:
735
                {
736
                        if (PlayerCfg.PRShot)
737
                        {
738
                                save_pr_screenshot();
739
                        }
740
                        else
741
                        {
742
                                save_clean_screenshot();
743
                        }
744
                        break;
745
                }
746
#endif
747
 
748
                KEY_MAC(case KEY_COMMAND+KEY_1:)
749
                case KEY_F1:                            if (Game_mode & GM_MULTI) show_netgame_help(); else show_help();        break;
750
 
751
                KEY_MAC(case KEY_COMMAND+KEY_2:)
752
                case KEY_F2:
753
                        {
754
                                do_options_menu();
755
                                break;
756
                        }
757
 
758
 
759
                KEY_MAC(case KEY_COMMAND+KEY_3:)
760
 
761
                case KEY_F3:
762
                        if (Player_dead_state == player_dead_state::no && Viewer->type == OBJ_PLAYER)
763
                        {
764
                                toggle_cockpit();
765
                        }
766
                        break;
767
 
768
                KEY_MAC(case KEY_COMMAND+KEY_5:)
769
                case KEY_F5:
770
                        if ( Newdemo_state == ND_STATE_RECORDING )
771
                                newdemo_stop_recording();
772
                        else if ( Newdemo_state == ND_STATE_NORMAL )
773
                                newdemo_start_recording();
774
                        break;
775
                KEY_MAC(case KEY_COMMAND+KEY_ALTED+KEY_4:)
776
                case KEY_ALTED + KEY_F4:
777
                        Show_reticle_name = (Show_reticle_name+1)%2;
778
                        break;
779
 
780
                KEY_MAC(case KEY_COMMAND+KEY_7:)
781
                case KEY_F7:
782
                        Show_kill_list = (Show_kill_list+1) % ((Game_mode & GM_TEAM) ? 4 : 3);
783
                        if (Game_mode & GM_MULTI)
784
                                multi_sort_kill_list();
785
                        break;
786
 
787
                KEY_MAC(case KEY_COMMAND+KEY_8:)
788
                case KEY_F8:
789
                        multi_send_message_start();
790
                        break;
791
 
792
                case KEY_F9:
793
                case KEY_F10:
794
                case KEY_F11:
795
                case KEY_F12:
796
                        multi_send_macro(key);
797
                        break;          // send taunt macros
798
 
799
#if defined(__APPLE__) || defined(macintosh)
800
                case KEY_9 + KEY_COMMAND:
801
                        multi_send_macro(KEY_F9);
802
                        break;
803
                case KEY_0 + KEY_COMMAND:
804
                        multi_send_macro(KEY_F10);
805
                        break;
806
                case KEY_1 + KEY_COMMAND + KEY_CTRLED:
807
                        multi_send_macro(KEY_F11);
808
                        break;
809
                case KEY_2 + KEY_COMMAND + KEY_CTRLED:
810
                        multi_send_macro(KEY_F12);
811
                        break;
812
#endif
813
 
814
                case KEY_SHIFTED + KEY_F9:
815
                case KEY_SHIFTED + KEY_F10:
816
                case KEY_SHIFTED + KEY_F11:
817
                case KEY_SHIFTED + KEY_F12:
818
                        multi_define_macro(key);
819
                        break;          // redefine taunt macros
820
 
821
#if defined(__APPLE__) || defined(macintosh)
822
                case KEY_9 + KEY_SHIFTED + KEY_COMMAND:
823
                        multi_define_macro(KEY_F9);
824
                        break;
825
                case KEY_0 + KEY_SHIFTED + KEY_COMMAND:
826
                        multi_define_macro(KEY_F10);
827
                        break;
828
                case KEY_1 + KEY_SHIFTED + KEY_COMMAND + KEY_CTRLED:
829
                        multi_define_macro(KEY_F11);
830
                        break;
831
                case KEY_2 + KEY_SHIFTED + KEY_COMMAND + KEY_CTRLED:
832
                        multi_define_macro(KEY_F12);
833
                        break;
834
#endif
835
 
836
 
837
                KEY_MAC(case KEY_COMMAND+KEY_SHIFTED+KEY_S:)
838
                KEY_MAC(case KEY_COMMAND+KEY_ALTED+KEY_2:)
839
                case KEY_ALTED+KEY_F2:
840
                        if (Player_dead_state == player_dead_state::no)
841
                                state_save_all(secret_save::none, blind_save::no); // 0 means not between levels.
842
                        break;
843
 
844
                KEY_MAC(case KEY_COMMAND+KEY_S:)
845
                case KEY_ALTED+KEY_SHIFTED+KEY_F2:
846
                        if (Player_dead_state == player_dead_state::no)
847
                                state_save_all(secret_save::none, blind_save::yes);
848
                        break;
849
                KEY_MAC(case KEY_COMMAND+KEY_SHIFTED+KEY_O:)
850
                KEY_MAC(case KEY_COMMAND+KEY_ALTED+KEY_3:)
851
                case KEY_ALTED+KEY_F3:
852
                        if (!((Game_mode & GM_MULTI) && !(Game_mode & GM_MULTI_COOP)))
853
                                state_restore_all(1, secret_restore::none, nullptr, blind_save::no);
854
                        break;
855
                KEY_MAC(case KEY_COMMAND+KEY_O:)
856
                case KEY_ALTED+KEY_SHIFTED+KEY_F3:
857
                        if (!((Game_mode & GM_MULTI) && !(Game_mode & GM_MULTI_COOP)))
858
                                state_restore_all(1, secret_restore::none, nullptr, blind_save::yes);
859
                        break;
860
 
861
#if defined(DXX_BUILD_DESCENT_II)
862
                KEY_MAC(case KEY_COMMAND+KEY_SHIFTED+KEY_4:)
863
                case KEY_F4 + KEY_SHIFTED:
864
                        do_escort_menu();
865
                        break;
866
 
867
 
868
                KEY_MAC(case KEY_COMMAND+KEY_SHIFTED+KEY_ALTED+KEY_4:)
869
                case KEY_F4 + KEY_SHIFTED + KEY_ALTED:
870
                        change_guidebot_name();
871
                        break;
872
#endif
873
 
874
                        /*
875
                         * Jukebox hotkeys -- MD2211, 2007
876
                         * Now for all music
877
                         * ==============================================
878
                         */
879
                case KEY_ALTED + KEY_SHIFTED + KEY_F9:
880
                KEY_MAC(case KEY_COMMAND+KEY_E:)
881
#if DXX_USE_SDL_REDBOOK_AUDIO
882
                        if (GameCfg.MusicType == MUSIC_TYPE_REDBOOK)
883
                        {
884
                                songs_stop_all();
885
                                RBAEjectDisk();
886
                        }
887
#endif
888
                        break;
889
 
890
                case KEY_ALTED + KEY_SHIFTED + KEY_F10:
891
                KEY_MAC(case KEY_COMMAND+KEY_UP:)
892
                KEY_MAC(case KEY_COMMAND+KEY_DOWN:)
893
                        songs_pause_resume();
894
                        break;
895
 
896
                case KEY_MINUS + KEY_ALTED:
897
                case KEY_ALTED + KEY_SHIFTED + KEY_F11:
898
                KEY_MAC(case KEY_COMMAND+KEY_LEFT:)
899
                        songs_play_level_song( Current_level_num, -1 );
900
                        break;
901
                case KEY_EQUAL + KEY_ALTED:
902
                case KEY_ALTED + KEY_SHIFTED + KEY_F12:
903
                KEY_MAC(case KEY_COMMAND+KEY_RIGHT:)
904
                        songs_play_level_song( Current_level_num, 1 );
905
                        break;
906
 
907
                default:
908
                        return window_event_result::ignored;
909
        }
910
        return window_event_result::handled;
911
}
912
}
913
 
914
namespace dsx {
915
static window_event_result HandleGameKey(int key)
916
{
917
        auto &Objects = LevelUniqueObjectState.Objects;
918
        auto &vmobjptr = Objects.vmptr;
919
        switch (key) {
920
#if defined(DXX_BUILD_DESCENT_II)
921
                case KEY_1 + KEY_SHIFTED:
922
                case KEY_2 + KEY_SHIFTED:
923
                case KEY_3 + KEY_SHIFTED:
924
                case KEY_4 + KEY_SHIFTED:
925
                case KEY_5 + KEY_SHIFTED:
926
                case KEY_6 + KEY_SHIFTED:
927
                case KEY_7 + KEY_SHIFTED:
928
                case KEY_8 + KEY_SHIFTED:
929
                case KEY_9 + KEY_SHIFTED:
930
                case KEY_0 + KEY_SHIFTED:
931
                        if (PlayerCfg.EscortHotKeys)
932
                        {
933
                                auto &BuddyState = LevelUniqueObjectState.BuddyState;
934
                                if (Game_mode & GM_MULTI)
935
                                {
936
                                        if (!check_warn_local_player_can_control_guidebot(Objects.vcptr, BuddyState, Netgame))
937
                                                return window_event_result::handled;
938
                                }
939
                                set_escort_special_goal(BuddyState, key);
940
                                game_flush_inputs();
941
                                return window_event_result::handled;
942
                        }
943
                        else
944
                                return window_event_result::ignored;
945
#endif
946
 
947
                case KEY_ALTED+KEY_F7:
948
                KEY_MAC(case KEY_COMMAND+KEY_ALTED+KEY_7:)
949
                        PlayerCfg.HudMode = static_cast<HudType>((static_cast<unsigned>(PlayerCfg.HudMode) + 1) % GAUGE_HUD_NUMMODES);
950
                        write_player_file();
951
                        switch (PlayerCfg.HudMode)
952
                        {
953
                                case HudType::Standard:
954
                                        HUD_init_message_literal(HM_DEFAULT, "Standard HUD");
955
                                        break;
956
                                case HudType::Alternate1:
957
                                        HUD_init_message_literal(HM_DEFAULT, "Alternative HUD #1");
958
                                        break;
959
                                case HudType::Alternate2:
960
                                        HUD_init_message_literal(HM_DEFAULT, "Alternative HUD #2");
961
                                        break;
962
                                case HudType::Hidden:
963
                                        HUD_init_message_literal(HM_DEFAULT, "No HUD");
964
                                        break;
965
                        }
966
                        return window_event_result::handled;
967
 
968
                KEY_MAC(case KEY_COMMAND+KEY_6:)
969
                case KEY_F6:
970
                        if (Netgame.RefusePlayers && WaitForRefuseAnswer && !(Game_mode & GM_TEAM))
971
                        {
972
                                RefuseThisPlayer=1;
973
                                HUD_init_message_literal(HM_MULTI, "Player accepted!");
974
                        }
975
                        return window_event_result::handled;
976
                case KEY_ALTED + KEY_1:
977
                        if (Netgame.RefusePlayers && WaitForRefuseAnswer && (Game_mode & GM_TEAM))
978
                                {
979
                                        RefuseThisPlayer=1;
980
                                        HUD_init_message_literal(HM_MULTI, "Player accepted!");
981
                                        RefuseTeam=1;
982
                                        game_flush_inputs();
983
                                }
984
                        return window_event_result::handled;
985
                case KEY_ALTED + KEY_2:
986
                        if (Netgame.RefusePlayers && WaitForRefuseAnswer && (Game_mode & GM_TEAM))
987
                                {
988
                                        RefuseThisPlayer=1;
989
                                        HUD_init_message_literal(HM_MULTI, "Player accepted!");
990
                                        RefuseTeam=2;
991
                                        game_flush_inputs();
992
                                }
993
                        return window_event_result::handled;
994
 
995
                default:
996
                        break;
997
 
998
        }        //switch (key)
999
 
1000
        if (Player_dead_state == player_dead_state::no)
1001
                switch (key)
1002
                {
1003
                                KEY_MAC(case KEY_COMMAND+KEY_SHIFTED+KEY_5:)
1004
                        case KEY_F5 + KEY_SHIFTED:
1005
                                DropCurrentWeapon(get_local_plrobj().ctype.player_info);
1006
                                break;
1007
 
1008
                        KEY_MAC(case KEY_COMMAND+KEY_SHIFTED+KEY_6:)
1009
                        case KEY_F6 + KEY_SHIFTED:
1010
                                DropSecondaryWeapon(get_local_plrobj().ctype.player_info);
1011
                                break;
1012
 
1013
#if defined(DXX_BUILD_DESCENT_II)
1014
                        case KEY_0 + KEY_ALTED:
1015
                                DropFlag ();
1016
                                game_flush_inputs();
1017
                                break;
1018
 
1019
                        KEY_MAC(case KEY_COMMAND+KEY_4:)
1020
                        case KEY_F4:
1021
                                if (!MarkerState.DefiningMarkerMessage())
1022
                                        InitMarkerInput();
1023
                                break;
1024
#endif
1025
 
1026
                        default:
1027
                                return window_event_result::ignored;
1028
                }
1029
        else
1030
                return window_event_result::ignored;
1031
 
1032
        return window_event_result::handled;
1033
}
1034
}
1035
 
1036
#if defined(DXX_BUILD_DESCENT_II)
1037
static void kill_all_robots(void)
1038
{
1039
        auto &Objects = LevelUniqueObjectState.Objects;
1040
        auto &vmobjptr = Objects.vmptr;
1041
        int     dead_count=0;
1042
        //int   boss_index = -1;
1043
        auto &Robot_info = LevelSharedRobotInfoState.Robot_info;
1044
 
1045
        // Kill all bots except for Buddy bot and boss.  However, if only boss and buddy left, kill boss.
1046
        range_for (const auto &&objp, vmobjptr)
1047
        {
1048
                if (objp->type == OBJ_ROBOT)
1049
                {
1050
                        if (!Robot_info[get_robot_id(objp)].companion && !Robot_info[get_robot_id(objp)].boss_flag) {
1051
                                dead_count++;
1052
                                objp->flags |= OF_EXPLODING|OF_SHOULD_BE_DEAD;
1053
                        }
1054
                }
1055
        }
1056
 
1057
// --           // Now, if more than boss and buddy left, un-kill boss.
1058
// --           if ((dead_count > 2) && (boss_index != -1)) {
1059
// --                   Objects[boss_index].flags &= ~(OF_EXPLODING|OF_SHOULD_BE_DEAD);
1060
// --                   dead_count--;
1061
// --           } else if (boss_index != -1)
1062
// --                   HUD_init_message(HM_DEFAULT, "Toasted the BOSS!");
1063
 
1064
        // Toast the buddy if nothing else toasted!
1065
        if (dead_count == 0)
1066
                range_for (const auto &&objp, vmobjptr)
1067
                {
1068
                        if (objp->type == OBJ_ROBOT)
1069
                                if (Robot_info[get_robot_id(objp)].companion) {
1070
                                        objp->flags |= OF_EXPLODING|OF_SHOULD_BE_DEAD;
1071
                                        HUD_init_message_literal(HM_DEFAULT, "Toasted the Buddy! *sniff*");
1072
                                        dead_count++;
1073
                                }
1074
                }
1075
 
1076
        HUD_init_message(HM_DEFAULT, "%i robots toasted!", dead_count);
1077
}
1078
#endif
1079
 
1080
//      --------------------------------------------------------------------------
1081
//      Detonate reactor.
1082
//      Award player all powerups in mine.
1083
//      Place player just outside exit.
1084
//      Kill all bots in mine.
1085
//      Yippee!!
1086
static void kill_and_so_forth(fvmobjptridx &vmobjptridx, fvmsegptridx &vmsegptridx)
1087
{
1088
        auto &LevelSharedVertexState = LevelSharedSegmentState.get_vertex_state();
1089
        auto &Objects = LevelUniqueObjectState.Objects;
1090
        auto &Vertices = LevelSharedVertexState.get_vertices();
1091
        auto &vmobjptr = Objects.vmptr;
1092
        HUD_init_message_literal(HM_DEFAULT, "Killing, awarding, etc.!");
1093
 
1094
        range_for (const auto &&o, vmobjptridx)
1095
        {
1096
                switch (o->type) {
1097
                        case OBJ_ROBOT:
1098
                                apply_damage_to_robot(o, o->shields + 1, get_local_player().objnum);
1099
                                break;
1100
                        case OBJ_POWERUP:
1101
                                do_powerup(o);
1102
                                break;
1103
                        default:
1104
                                break;
1105
                }
1106
        }
1107
 
1108
        do_controlcen_destroyed_stuff(object_none);
1109
 
1110
        auto &Triggers = LevelUniqueWallSubsystemState.Triggers;
1111
        auto &vctrgptr = Triggers.vcptr;
1112
        auto &Walls = LevelUniqueWallSubsystemState.Walls;
1113
        auto &vcwallptr = Walls.vcptr;
1114
        for (trgnum_t i = 0; i < Triggers.get_count(); i++)
1115
        {
1116
                const auto &&t = vctrgptr(i);
1117
                if (trigger_is_exit(t))
1118
                {
1119
                        range_for (const auto &&wp, vcwallptr)
1120
                        {
1121
                                auto &w = *wp;
1122
                                if (w.trigger == i)
1123
                                {
1124
                                        const auto &&segp = vmsegptridx(w.segnum);
1125
                                        auto &vcvertptr = Vertices.vcptr;
1126
                                        compute_segment_center(vcvertptr, ConsoleObject->pos, segp);
1127
                                        obj_relink(vmobjptr, vmsegptr, vmobjptridx(ConsoleObject), segp);
1128
                                        return;
1129
                                }
1130
                        }
1131
                }
1132
        }
1133
}
1134
 
1135
#ifndef RELEASE
1136
#if defined(DXX_BUILD_DESCENT_II)
1137
static void kill_all_snipers(void) __attribute_used;
1138
static void kill_all_snipers(void)
1139
{
1140
        auto &Objects = LevelUniqueObjectState.Objects;
1141
        auto &vmobjptr = Objects.vmptr;
1142
        int     dead_count=0;
1143
 
1144
        //      Kill all snipers.
1145
        range_for (const auto &&objp, vmobjptr)
1146
        {
1147
                if (objp->type == OBJ_ROBOT)
1148
                        if (objp->ctype.ai_info.behavior == ai_behavior::AIB_SNIPE)
1149
                        {
1150
                                dead_count++;
1151
                                objp->flags |= OF_EXPLODING|OF_SHOULD_BE_DEAD;
1152
                        }
1153
        }
1154
 
1155
        HUD_init_message(HM_DEFAULT, "%i robots toasted!", dead_count);
1156
}
1157
 
1158
static void kill_thief(void) __attribute_used;
1159
static void kill_thief(void)
1160
{
1161
        auto &Objects = LevelUniqueObjectState.Objects;
1162
        auto &vmobjptr = Objects.vmptr;
1163
        auto &Robot_info = LevelSharedRobotInfoState.Robot_info;
1164
        //      Kill thief.
1165
        range_for (const auto &&objp, vmobjptr)
1166
        {
1167
                if (objp->type == OBJ_ROBOT)
1168
                        if (Robot_info[get_robot_id(objp)].thief)
1169
                        {
1170
                                objp->flags |= OF_EXPLODING|OF_SHOULD_BE_DEAD;
1171
                                HUD_init_message_literal(HM_DEFAULT, "Thief toasted!");
1172
                        }
1173
        }
1174
}
1175
 
1176
static void kill_buddy(void) __attribute_used;
1177
static void kill_buddy(void)
1178
{
1179
        auto &Objects = LevelUniqueObjectState.Objects;
1180
        auto &vmobjptr = Objects.vmptr;
1181
        auto &Robot_info = LevelSharedRobotInfoState.Robot_info;
1182
        //      Kill buddy.
1183
        range_for (const auto &&objp, vmobjptr)
1184
        {
1185
                if (objp->type == OBJ_ROBOT)
1186
                        if (Robot_info[get_robot_id(objp)].companion)
1187
                        {
1188
                                objp->flags |= OF_EXPLODING|OF_SHOULD_BE_DEAD;
1189
                                HUD_init_message_literal(HM_DEFAULT, "Buddy toasted!");
1190
                        }
1191
        }
1192
}
1193
#endif
1194
 
1195
namespace dsx {
1196
static window_event_result HandleTestKey(fvmsegptridx &vmsegptridx, int key)
1197
{
1198
        auto &LevelSharedVertexState = LevelSharedSegmentState.get_vertex_state();
1199
        auto &LevelUniqueMorphObjectState = LevelUniqueObjectState.MorphObjectState;
1200
        auto &Objects = LevelUniqueObjectState.Objects;
1201
        auto &Vertices = LevelSharedVertexState.get_vertices();
1202
        auto &vmobjptr = Objects.vmptr;
1203
        auto &vmobjptridx = Objects.vmptridx;
1204
        switch (key)
1205
        {
1206
 
1207
#ifdef SHOW_EXIT_PATH
1208
                case KEY_DEBUGGED+KEY_1:        create_special_path();  break;
1209
#endif
1210
 
1211
                case KEY_DEBUGGED+KEY_Y:
1212
                        do_controlcen_destroyed_stuff(object_none);
1213
                        break;
1214
 
1215
#if defined(DXX_BUILD_DESCENT_II)
1216
        case KEY_DEBUGGED+KEY_ALTED+KEY_D:
1217
                        PlayerCfg.NetlifeKills=4000; PlayerCfg.NetlifeKilled=5;
1218
                        multi_add_lifetime_kills(1);
1219
                        break;
1220
 
1221
                case KEY_DEBUGGED+KEY_R+KEY_SHIFTED:
1222
                        kill_all_robots();
1223
                        break;
1224
#endif
1225
 
1226
                case KEY_BACKSP:
1227
                case KEY_CTRLED+KEY_BACKSP:
1228
                case KEY_ALTED+KEY_BACKSP:
1229
                case KEY_SHIFTED+KEY_BACKSP:
1230
                case KEY_SHIFTED+KEY_ALTED+KEY_BACKSP:
1231
                case KEY_CTRLED+KEY_ALTED+KEY_BACKSP:
1232
                case KEY_SHIFTED+KEY_CTRLED+KEY_BACKSP:
1233
                case KEY_SHIFTED+KEY_CTRLED+KEY_ALTED+KEY_BACKSP:
1234
 
1235
                        Int3(); break;
1236
 
1237
                case KEY_DEBUGGED+KEY_S:                                digi_reset(); break;
1238
 
1239
                case KEY_DEBUGGED+KEY_P:
1240
                        if (Game_suspended & SUSP_ROBOTS)
1241
                                Game_suspended &= ~SUSP_ROBOTS;         //robots move
1242
                        else
1243
                                Game_suspended |= SUSP_ROBOTS;          //robots don't move
1244
                        break;
1245
                case KEY_DEBUGGED+KEY_M:
1246
                {
1247
                        static int i = 0;
1248
                        const auto &&segp = vmsegptridx(ConsoleObject->segnum);
1249
                        auto &vcvertptr = Vertices.vcptr;
1250
                        const auto &&new_obj = create_morph_robot(segp, compute_segment_center(vcvertptr, segp), i);
1251
                        if (new_obj != object_none)
1252
                                morph_start(LevelUniqueMorphObjectState, LevelSharedPolygonModelState, new_obj);
1253
                        i++;
1254
                        if (i >= LevelSharedRobotInfoState.N_robot_types)
1255
                                i = 0;
1256
                        break;
1257
                }
1258
                case KEY_DEBUGGED+KEY_K:
1259
                        get_local_plrobj().shields = 1;
1260
                        break;                                                  //      a virtual kill
1261
                case KEY_DEBUGGED+KEY_SHIFTED + KEY_K:
1262
                        get_local_plrobj().shields = -1;
1263
                        break;  //      an actual kill
1264
                case KEY_DEBUGGED+KEY_X: get_local_player().lives++; break; // Extra life cheat key.
1265
                case KEY_DEBUGGED+KEY_H:
1266
                {
1267
                        if (Player_dead_state != player_dead_state::no)
1268
                                return window_event_result::ignored;
1269
 
1270
                        auto &player_info = get_local_plrobj().ctype.player_info;
1271
                        auto &pl_flags = player_info.powerup_flags;
1272
                        pl_flags ^= PLAYER_FLAGS_CLOAKED;
1273
                        if (pl_flags & PLAYER_FLAGS_CLOAKED) {
1274
                                if (Game_mode & GM_MULTI)
1275
                                        multi_send_cloak();
1276
                                ai_do_cloak_stuff();
1277
                                player_info.cloak_time = GameTime64;
1278
                        }
1279
                        break;
1280
                }
1281
 
1282
                case KEY_DEBUGGED+KEY_R:
1283
                        cheats.robotfiringsuspended = !cheats.robotfiringsuspended;
1284
                        break;
1285
 
1286
#if DXX_USE_EDITOR              //editor-specific functions
1287
 
1288
                case KEY_E + KEY_DEBUGGED:
1289
                {
1290
                        window_set_visible(Game_wind, 0);       // don't let the game do anything while we set the editor up
1291
                        auto old_gamestate = gamestate;
1292
                        gamestate = editor_gamestate::unsaved;  // saved game editing mode
1293
 
1294
                        init_editor();
1295
                        // If editor failed to load, carry on playing
1296
                        if (!EditorWindow)
1297
                        {
1298
                                window_set_visible(Game_wind, 1);
1299
                                gamestate = old_gamestate;
1300
                                return window_event_result::handled;
1301
                        }
1302
                        return window_event_result::close;
1303
                }
1304
 
1305
#if defined(DXX_BUILD_DESCENT_II)
1306
        case KEY_Q + KEY_SHIFTED + KEY_DEBUGGED:
1307
                {
1308
                        palette_array_t save_pal;
1309
                        save_pal = gr_palette;
1310
                        PlayMovie ("end.tex", "end.mve",MOVIE_ABORT_ON);
1311
                        Screen_mode = -1;
1312
                        set_screen_mode(SCREEN_GAME);
1313
                        reset_cockpit();
1314
                        gr_palette = save_pal;
1315
                        gr_palette_load(gr_palette);
1316
                        break;
1317
                }
1318
#endif
1319
                case KEY_C + KEY_SHIFTED + KEY_DEBUGGED:
1320
                        if (Player_dead_state == player_dead_state::no &&
1321
                                !(Game_mode & GM_MULTI))
1322
                                move_player_2_segment(Cursegp,Curside);
1323
                        break;   //move eye to curseg
1324
 
1325
 
1326
                case KEY_DEBUGGED+KEY_W:        draw_world_from_game(); break;
1327
 
1328
                #endif  //#ifdef EDITOR
1329
 
1330
                case KEY_DEBUGGED+KEY_LAPOSTRO: Show_view_text_timer = 0x30000; object_goto_next_viewer(); break;
1331
                case KEY_DEBUGGED+KEY_SHIFTED+KEY_LAPOSTRO: Viewer=ConsoleObject; break;
1332
                case KEY_DEBUGGED+KEY_O: toggle_outline_mode(); break;
1333
                case KEY_DEBUGGED+KEY_T:
1334
#if defined(DXX_BUILD_DESCENT_II)
1335
                {
1336
                        static int Toggle_var;
1337
                        Toggle_var = !Toggle_var;
1338
                        if (Toggle_var)
1339
                                CGameArg.SysMaxFPS = 300;
1340
                        else
1341
                                CGameArg.SysMaxFPS = 30;
1342
                }
1343
#endif
1344
                        break;
1345
                case KEY_DEBUGGED + KEY_L:
1346
#if !DXX_USE_OGL
1347
                        if (++Lighting_on >= 2)
1348
                                Lighting_on = 0;
1349
#endif
1350
                        break;
1351
                case KEY_PAD5: slew_stop(); break;
1352
 
1353
#ifndef NDEBUG
1354
                case KEY_DEBUGGED + KEY_F11: play_test_sound(); break;
1355
#endif
1356
 
1357
                case KEY_DEBUGGED + KEY_C:
1358
                        do_cheat_menu();
1359
                        break;
1360
                case KEY_DEBUGGED + KEY_SHIFTED + KEY_A:
1361
                        do_megawow_powerup(10);
1362
                        break;
1363
                case KEY_DEBUGGED + KEY_A:      {
1364
                        do_megawow_powerup(200);
1365
                                break;
1366
                }
1367
 
1368
                case KEY_DEBUGGED+KEY_SPACEBAR:         //KEY_F7:                               // Toggle physics flying
1369
                        slew_stop();
1370
                        game_flush_inputs();
1371
                        if ( ConsoleObject->control_type != CT_FLYING ) {
1372
                                fly_init(*ConsoleObject);
1373
                                Game_suspended &= ~SUSP_ROBOTS; //robots move
1374
                        } else {
1375
                                slew_init(vmobjptr(ConsoleObject));                     //start player slewing
1376
                                Game_suspended |= SUSP_ROBOTS;  //robots don't move
1377
                        }
1378
                        break;
1379
 
1380
                case KEY_DEBUGGED+KEY_COMMA: Render_zoom = fixmul(Render_zoom,62259); break;
1381
                case KEY_DEBUGGED+KEY_PERIOD: Render_zoom = fixmul(Render_zoom,68985); break;
1382
 
1383
                #ifndef NDEBUG
1384
                case KEY_DEBUGGED+KEY_D:
1385
                        if ((CGameArg.DbgNoDoubleBuffer = !CGameArg.DbgNoDoubleBuffer)!=0)
1386
                                init_cockpit();
1387
                        break;
1388
                #endif
1389
 
1390
#if DXX_USE_EDITOR
1391
                case KEY_DEBUGGED+KEY_Q:
1392
                        {
1393
                                pause_game_world_time p;
1394
                        dump_used_textures_all();
1395
                        }
1396
                        break;
1397
#endif
1398
 
1399
                case KEY_DEBUGGED+KEY_B: {
1400
                        d_fname text{};
1401
                        int item;
1402
                        std::array<newmenu_item, 1> m{{
1403
                                nm_item_input(text),
1404
                        }};
1405
                        item = newmenu_do( NULL, "Briefing to play?", m, unused_newmenu_subfunction, unused_newmenu_userdata);
1406
                        if (item != -1) {
1407
                                do_briefing_screens(text,1);
1408
                        }
1409
                        break;
1410
                }
1411
 
1412
                case KEY_DEBUGGED+KEY_SHIFTED+KEY_B:
1413
                        if (Player_dead_state != player_dead_state::no)
1414
                                return window_event_result::ignored;
1415
 
1416
                        kill_and_so_forth(vmobjptridx, vmsegptridx);
1417
                        break;
1418
                case KEY_DEBUGGED+KEY_G:
1419
                        GameTime64 = (INT64_MAX) - (F1_0*10);
1420
                        HUD_init_message(HM_DEFAULT, "GameTime %" PRIi64 " - Reset in 10 seconds!", GameTime64);
1421
                        break;
1422
                default:
1423
                        return window_event_result::ignored;
1424
        }
1425
        return window_event_result::handled;
1426
}
1427
}
1428
#endif          //#ifndef RELEASE
1429
 
1430
#define CHEAT_MAX_LEN 15
1431
struct cheat_code
1432
{
1433
        const char string[CHEAT_MAX_LEN];
1434
        int game_cheats::*stateptr;
1435
};
1436
 
1437
constexpr cheat_code cheat_codes[] = {
1438
#if defined(DXX_BUILD_DESCENT_I)
1439
        { "gabbagabbahey", &game_cheats::enabled },
1440
        { "scourge", &game_cheats::wowie },
1441
        { "bigred", &game_cheats::wowie2 },
1442
        { "mitzi", &game_cheats::allkeys },
1443
        { "racerx", &game_cheats::invul },
1444
        { "guile", &game_cheats::cloak },
1445
        { "twilight", &game_cheats::shields },
1446
        { "poboys", &game_cheats::killreactor },
1447
        { "farmerjoe", &game_cheats::levelwarp },
1448
        { "bruin", &game_cheats::extralife },
1449
        { "porgys", &game_cheats::rapidfire },
1450
        { "ahimsa", &game_cheats::robotfiringsuspended },
1451
        { "baldguy", &game_cheats::baldguy },
1452
#elif defined(DXX_BUILD_DESCENT_II)
1453
        { "gabbagabbahey", &game_cheats::lamer },
1454
        { "motherlode", &game_cheats::lamer },
1455
        { "currygoat", &game_cheats::lamer },
1456
        { "zingermans", &game_cheats::lamer },
1457
        { "eatangelos", &game_cheats::lamer },
1458
        { "ericaanne", &game_cheats::lamer },
1459
        { "joshuaakira", &game_cheats::lamer },
1460
        { "whammazoom", &game_cheats::lamer },
1461
        { "honestbob", &game_cheats::wowie },
1462
        { "oralgroove", &game_cheats::allkeys },
1463
        { "alifalafel", &game_cheats::accessory },
1464
        { "almighty", &game_cheats::invul },
1465
        { "blueorb", &game_cheats::shields },
1466
        { "delshiftb", &game_cheats::killreactor },
1467
        { "freespace", &game_cheats::levelwarp },
1468
        { "rockrgrl", &game_cheats::fullautomap },
1469
        { "wildfire", &game_cheats::rapidfire },
1470
        { "duddaboo", &game_cheats::bouncyfire },
1471
        { "lpnlizard", &game_cheats::homingfire },
1472
        { "imagespace", &game_cheats::robotfiringsuspended },
1473
        { "spaniard", &game_cheats::killallrobots },
1474
        { "silkwing", &game_cheats::robotskillrobots },
1475
        { "godzilla", &game_cheats::monsterdamage },
1476
        { "helpvishnu", &game_cheats::buddyclone },
1477
        { "gowingnut", &game_cheats::buddyangry },
1478
#endif
1479
        { "flash", &game_cheats::exitpath },
1480
        { "astral", &game_cheats::ghostphysics },
1481
        { "buggin", &game_cheats::turbo },
1482
        { "bittersweet", &game_cheats::acid },
1483
};
1484
 
1485
namespace dsx {
1486
static window_event_result FinalCheats()
1487
{
1488
        auto &Objects = LevelUniqueObjectState.Objects;
1489
        auto &vmobjptr = Objects.vmptr;
1490
        auto &vmobjptridx = Objects.vmptridx;
1491
        int game_cheats::*gotcha;
1492
 
1493
        if (Game_mode & GM_MULTI)
1494
                return window_event_result::ignored;
1495
 
1496
        static std::array<char, CHEAT_MAX_LEN> cheat_buffer;
1497
        std::move(std::next(cheat_buffer.begin()), cheat_buffer.end(), cheat_buffer.begin());
1498
        cheat_buffer.back() = key_ascii();
1499
        for (unsigned i = 0;; i++)
1500
        {
1501
                if (i >= std::size(cheat_codes))
1502
                        return window_event_result::ignored;
1503
                int cheatlen = strlen(cheat_codes[i].string);
1504
                Assert(cheatlen <= CHEAT_MAX_LEN);
1505
                if (d_strnicmp(cheat_codes[i].string, &cheat_buffer[CHEAT_MAX_LEN-cheatlen], cheatlen)==0)
1506
                {
1507
                        gotcha = cheat_codes[i].stateptr;
1508
#if defined(DXX_BUILD_DESCENT_I)
1509
                        if (!cheats.enabled && gotcha != &game_cheats::enabled)
1510
                                return window_event_result::ignored;
1511
                        if (!cheats.enabled)
1512
                                HUD_init_message_literal(HM_DEFAULT, TXT_CHEATS_ENABLED);
1513
#endif
1514
                        cheats.*gotcha = !(cheats.*gotcha);
1515
                        cheats.enabled = 1;
1516
                        digi_play_sample( SOUND_CHEATER, F1_0);
1517
                        break;
1518
                }
1519
        }
1520
        auto &plrobj = get_local_plrobj();
1521
        auto &player_info = plrobj.ctype.player_info;
1522
        player_info.mission.score = 0;
1523
 
1524
#if defined(DXX_BUILD_DESCENT_I)
1525
        if (gotcha == &game_cheats::wowie)
1526
        {
1527
                HUD_init_message_literal(HM_DEFAULT, TXT_WOWIE_ZOWIE);
1528
 
1529
                player_info.primary_weapon_flags |= (HAS_LASER_FLAG | HAS_VULCAN_FLAG | HAS_SPREADFIRE_FLAG);
1530
 
1531
                player_info.vulcan_ammo = VULCAN_AMMO_MAX;
1532
                auto &secondary_ammo = player_info.secondary_ammo;
1533
                range_for (const unsigned i, xrange(3u))
1534
                        secondary_ammo[i] = Secondary_ammo_max[i];
1535
 
1536
                if (Newdemo_state == ND_STATE_RECORDING)
1537
                        newdemo_record_laser_level(player_info.laser_level, MAX_LASER_LEVEL);
1538
 
1539
                player_info.energy = MAX_ENERGY;
1540
                player_info.laser_level = MAX_LASER_LEVEL;
1541
                player_info.powerup_flags |= PLAYER_FLAGS_QUAD_LASERS;
1542
                update_laser_weapon_info();
1543
        }
1544
 
1545
        if (gotcha == &game_cheats::wowie2)
1546
        {
1547
                HUD_init_message(HM_DEFAULT, "SUPER %s",TXT_WOWIE_ZOWIE);
1548
 
1549
                player_info.primary_weapon_flags = (HAS_LASER_FLAG | HAS_VULCAN_FLAG | HAS_SPREADFIRE_FLAG | HAS_PLASMA_FLAG | HAS_FUSION_FLAG);
1550
 
1551
                player_info.vulcan_ammo = VULCAN_AMMO_MAX;
1552
                auto &secondary_ammo = player_info.secondary_ammo;
1553
                secondary_ammo = Secondary_ammo_max;
1554
 
1555
                if (Newdemo_state == ND_STATE_RECORDING)
1556
                        newdemo_record_laser_level(player_info.laser_level, MAX_LASER_LEVEL);
1557
 
1558
                player_info.energy = MAX_ENERGY;
1559
                player_info.laser_level = MAX_LASER_LEVEL;
1560
                player_info.powerup_flags |= PLAYER_FLAGS_QUAD_LASERS;
1561
                update_laser_weapon_info();
1562
        }
1563
#elif defined(DXX_BUILD_DESCENT_II)
1564
        if (gotcha == &game_cheats::lamer)
1565
        {
1566
                plrobj.shields = player_info.energy = i2f(1);
1567
                HUD_init_message_literal(HM_DEFAULT, "Take that...cheater!");
1568
        }
1569
 
1570
        if (gotcha == &game_cheats::wowie)
1571
        {
1572
                HUD_init_message_literal(HM_DEFAULT, TXT_WOWIE_ZOWIE);
1573
 
1574
                if (Piggy_hamfile_version < 3) // SHAREWARE
1575
                {
1576
                        player_info.primary_weapon_flags |= (HAS_LASER_FLAG | HAS_VULCAN_FLAG | HAS_SPREADFIRE_FLAG | HAS_PLASMA_FLAG) | (HAS_GAUSS_FLAG | HAS_HELIX_FLAG);
1577
                }
1578
                else
1579
                {
1580
                        player_info.primary_weapon_flags = (HAS_LASER_FLAG | HAS_VULCAN_FLAG | HAS_SPREADFIRE_FLAG | HAS_PLASMA_FLAG | HAS_FUSION_FLAG) | (HAS_GAUSS_FLAG | HAS_HELIX_FLAG | HAS_PHOENIX_FLAG | HAS_OMEGA_FLAG);
1581
                }
1582
 
1583
                player_info.vulcan_ammo = VULCAN_AMMO_MAX;
1584
                auto &secondary_ammo = player_info.secondary_ammo;
1585
                secondary_ammo = Secondary_ammo_max;
1586
 
1587
                if (Piggy_hamfile_version < 3) // SHAREWARE
1588
                {
1589
                        secondary_ammo[SMISSILE4_INDEX] = 0;
1590
                        secondary_ammo[SMISSILE5_INDEX] = 0;
1591
                        secondary_ammo[MEGA_INDEX] = 0;
1592
                }
1593
 
1594
                if (Newdemo_state == ND_STATE_RECORDING)
1595
                        newdemo_record_laser_level(player_info.laser_level, MAX_SUPER_LASER_LEVEL);
1596
 
1597
                player_info.energy = MAX_ENERGY;
1598
                player_info.laser_level = MAX_SUPER_LASER_LEVEL;
1599
                player_info.powerup_flags |= PLAYER_FLAGS_QUAD_LASERS;
1600
                update_laser_weapon_info();
1601
        }
1602
 
1603
        if (gotcha == &game_cheats::accessory)
1604
        {
1605
                player_info.powerup_flags |= PLAYER_FLAGS_HEADLIGHT | PLAYER_FLAGS_AFTERBURNER | PLAYER_FLAGS_AMMO_RACK | PLAYER_FLAGS_CONVERTER;
1606
                HUD_init_message_literal(HM_DEFAULT, "Accessories!!");
1607
        }
1608
#endif
1609
 
1610
        if (gotcha == &game_cheats::allkeys)
1611
        {
1612
                HUD_init_message_literal(HM_DEFAULT, TXT_ALL_KEYS);
1613
                player_info.powerup_flags |= PLAYER_FLAGS_BLUE_KEY | PLAYER_FLAGS_RED_KEY | PLAYER_FLAGS_GOLD_KEY;
1614
        }
1615
 
1616
        if (gotcha == &game_cheats::invul)
1617
        {
1618
                player_info.invulnerable_time = GameTime64+i2f(1000);
1619
                auto &pl_flags = player_info.powerup_flags;
1620
                pl_flags ^= PLAYER_FLAGS_INVULNERABLE;
1621
                HUD_init_message(HM_DEFAULT, "%s %s!", TXT_INVULNERABILITY, (pl_flags & PLAYER_FLAGS_INVULNERABLE) ? (player_info.FakingInvul = 0, TXT_ON) : TXT_OFF);
1622
        }
1623
 
1624
        if (gotcha == &game_cheats::shields)
1625
        {
1626
                HUD_init_message_literal(HM_DEFAULT, TXT_FULL_SHIELDS);
1627
                plrobj.shields = MAX_SHIELDS;
1628
        }
1629
 
1630
#if defined(DXX_BUILD_DESCENT_I)
1631
        if (gotcha == &game_cheats::cloak)
1632
        {
1633
                auto &pl_flags = player_info.powerup_flags;
1634
                pl_flags ^= PLAYER_FLAGS_CLOAKED;
1635
                const auto have_cloaked = pl_flags & PLAYER_FLAGS_CLOAKED;
1636
                HUD_init_message(HM_DEFAULT, "%s %s!", TXT_CLOAK, have_cloaked ? TXT_ON : TXT_OFF);
1637
                if (have_cloaked)
1638
                {
1639
                        ai_do_cloak_stuff();
1640
                        player_info.cloak_time = GameTime64;
1641
                }
1642
        }
1643
 
1644
        if (gotcha == &game_cheats::extralife)
1645
        {
1646
                auto &plr = get_local_player();
1647
                if (plr.lives < 50)
1648
                {
1649
                        plr.lives++;
1650
                        HUD_init_message_literal(HM_DEFAULT, "Extra life!");
1651
                }
1652
        }
1653
#endif
1654
 
1655
        if (gotcha == &game_cheats::killreactor)
1656
        {
1657
                kill_and_so_forth(vmobjptridx, vmsegptridx);
1658
        }
1659
 
1660
        if (gotcha == &game_cheats::exitpath)
1661
        {
1662
                if (create_special_path())
1663
                        HUD_init_message_literal(HM_DEFAULT, "Exit path illuminated!");
1664
        }
1665
 
1666
        if (gotcha == &game_cheats::levelwarp)
1667
        {
1668
                char text[10]="";
1669
                int new_level_num;
1670
                int item;
1671
                std::array<newmenu_item, 1> m{{
1672
                        nm_item_input(text),
1673
                }};
1674
                item = newmenu_do( NULL, TXT_WARP_TO_LEVEL, m, unused_newmenu_subfunction, unused_newmenu_userdata);
1675
                if (item != -1) {
1676
                        new_level_num = atoi(m[0].text);
1677
                        if (new_level_num!=0 && new_level_num>=0 && new_level_num<=Last_level) {
1678
                                window_set_visible(Game_wind, 0);
1679
                                StartNewLevel(new_level_num);
1680
                                window_set_visible(Game_wind, 1);
1681
                        }
1682
                }
1683
        }
1684
 
1685
#if defined(DXX_BUILD_DESCENT_II)
1686
        if (gotcha == &game_cheats::fullautomap)
1687
                HUD_init_message_literal(HM_DEFAULT, cheats.fullautomap ? "FULL MAP!" : "REGULAR MAP");
1688
#endif
1689
 
1690
        if (gotcha == &game_cheats::ghostphysics)
1691
        {
1692
                HUD_init_message(HM_DEFAULT, "%s %s!", "Ghosty mode", cheats.ghostphysics?TXT_ON:TXT_OFF);
1693
        }
1694
 
1695
        if (gotcha == &game_cheats::rapidfire)
1696
        {
1697
                HUD_init_message(HM_DEFAULT, "Rapid fire %s!", cheats.rapidfire?TXT_ON:TXT_OFF);
1698
#if defined(DXX_BUILD_DESCENT_I)
1699
                if (cheats.rapidfire)
1700
                        do_megawow_powerup(200);
1701
#endif
1702
        }
1703
 
1704
#if defined(DXX_BUILD_DESCENT_II)
1705
        if (gotcha == &game_cheats::bouncyfire)
1706
        {
1707
 
1708
                HUD_init_message(HM_DEFAULT, "Bouncing weapons %s!", cheats.bouncyfire?TXT_ON:TXT_OFF);
1709
        }
1710
 
1711
        if (gotcha == &game_cheats::homingfire)
1712
        {
1713
                HUD_init_message(HM_DEFAULT, "Homing weapons %s!", cheats.homingfire ? (weapons_homing_all(), TXT_ON) : (weapons_homing_all_reset(), TXT_OFF));
1714
        }
1715
#endif
1716
 
1717
        if (gotcha == &game_cheats::turbo)
1718
        {
1719
                HUD_init_message(HM_DEFAULT, "%s %s!", "Turbo mode", cheats.turbo?TXT_ON:TXT_OFF);
1720
        }
1721
 
1722
        if (gotcha == &game_cheats::robotfiringsuspended)
1723
        {
1724
                HUD_init_message(HM_DEFAULT, "Robot firing %s!", cheats.robotfiringsuspended?TXT_OFF:TXT_ON);
1725
        }
1726
 
1727
#if defined(DXX_BUILD_DESCENT_II)
1728
        if (gotcha == &game_cheats::killallrobots)
1729
        {
1730
                kill_all_robots();
1731
        }
1732
 
1733
        if (gotcha == &game_cheats::robotskillrobots)
1734
        {
1735
                HUD_init_message_literal(HM_DEFAULT, cheats.robotskillrobots?"Rabid robots!":"Kill the player!");
1736
        }
1737
 
1738
        if (gotcha == &game_cheats::monsterdamage)
1739
        {
1740
                HUD_init_message_literal(HM_DEFAULT, cheats.monsterdamage?"Oh no, there goes Tokyo!":"What have you done, I'm shrinking!!");
1741
        }
1742
 
1743
        if (gotcha == &game_cheats::buddyclone)
1744
        {
1745
                HUD_init_message_literal(HM_DEFAULT, "What's this? Another buddy bot!");
1746
                create_buddy_bot();
1747
        }
1748
 
1749
        if (gotcha == &game_cheats::buddyangry)
1750
        {
1751
 
1752
                if (cheats.buddyangry)
1753
                {
1754
                        HUD_init_message(HM_DEFAULT, "%s gets angry!", static_cast<const char *>(PlayerCfg.GuidebotName));
1755
                        PlayerCfg.GuidebotName = "Wingnut";
1756
                }
1757
                else
1758
                {
1759
                        PlayerCfg.GuidebotName = PlayerCfg.GuidebotNameReal;
1760
                        HUD_init_message(HM_DEFAULT, "%s calms down", static_cast<const char *>(PlayerCfg.GuidebotName));
1761
                }
1762
        }
1763
#endif
1764
 
1765
        if (gotcha == &game_cheats::acid)
1766
        {
1767
                HUD_init_message_literal(HM_DEFAULT, cheats.acid?"Going up!":"Coming down!");
1768
        }
1769
 
1770
        return window_event_result::handled;
1771
}
1772
}
1773
 
1774
// Internal Cheat Menu
1775
#ifndef RELEASE
1776
 
1777
namespace {
1778
 
1779
class menu_fix_wrapper
1780
{
1781
        fix &m_value;
1782
public:
1783
        constexpr menu_fix_wrapper(fix &t) :
1784
                m_value(t)
1785
        {
1786
        }
1787
        operator int() const
1788
        {
1789
                return f2i(m_value);
1790
        }
1791
        menu_fix_wrapper &operator=(int n)
1792
        {
1793
                m_value = i2f(n);
1794
                return *this;
1795
        }
1796
};
1797
 
1798
class cheat_menu_bit_invulnerability :
1799
        std::reference_wrapper<player_info>,
1800
        public menu_bit_wrapper_t<player_flags, std::integral_constant<PLAYER_FLAG, PLAYER_FLAGS_INVULNERABLE>>
1801
{
1802
public:
1803
        cheat_menu_bit_invulnerability(object &player) :
1804
                reference_wrapper(player.ctype.player_info),
1805
                menu_bit_wrapper_t(get().powerup_flags, {})
1806
        {
1807
        }
1808
        cheat_menu_bit_invulnerability &operator=(const uint32_t n)
1809
        {
1810
                this->menu_bit_wrapper_t::operator=(n);
1811
                if (n)
1812
                {
1813
                        auto &player_info = get();
1814
                        player_info.FakingInvul = 0;
1815
                        player_info.invulnerable_time = GameTime64+i2f(1000);
1816
                }
1817
                return *this;
1818
        }
1819
};
1820
 
1821
class cheat_menu_bit_cloak :
1822
        std::reference_wrapper<player_info>,
1823
        public menu_bit_wrapper_t<player_flags, std::integral_constant<PLAYER_FLAG, PLAYER_FLAGS_CLOAKED>>
1824
{
1825
public:
1826
        cheat_menu_bit_cloak(object &player) :
1827
                reference_wrapper(player.ctype.player_info),
1828
                menu_bit_wrapper_t(get().powerup_flags, {})
1829
        {
1830
        }
1831
        cheat_menu_bit_cloak &operator=(const uint32_t n)
1832
        {
1833
                this->menu_bit_wrapper_t::operator=(n);
1834
                if (n)
1835
                {
1836
                        if (Game_mode & GM_MULTI)
1837
                                multi_send_cloak();
1838
                        ai_do_cloak_stuff();
1839
                        get().cloak_time = GameTime64;
1840
                }
1841
                return *this;
1842
        }
1843
};
1844
 
1845
}
1846
 
1847
#if defined(DXX_BUILD_DESCENT_I)
1848
#define WIMP_MENU_DXX(VERB)
1849
#elif defined(DXX_BUILD_DESCENT_II)
1850
/* Adding an afterburner like this adds it at 0% charge.  This is OK for
1851
 * a cheat.  The player can change his energy up if he needs more.
1852
 */
1853
#define WIMP_MENU_DXX(VERB)     \
1854
        DXX_MENUITEM(VERB, CHECK, TXT_AFTERBURNER, opt_afterburner, menu_bit_wrapper(player_info.powerup_flags, PLAYER_FLAGS_AFTERBURNER))      \
1855
 
1856
#endif
1857
 
1858
#define DXX_WIMP_MENU(VERB)     \
1859
        DXX_MENUITEM(VERB, CHECK, TXT_INVULNERABILITY, opt_invul, cheat_menu_bit_invulnerability(plrobj))       \
1860
        DXX_MENUITEM(VERB, CHECK, TXT_CLOAKED, opt_cloak, cheat_menu_bit_cloak(plrobj)) \
1861
        DXX_MENUITEM(VERB, CHECK, "BLUE KEY", opt_key_blue, menu_bit_wrapper(player_info.powerup_flags, PLAYER_FLAGS_BLUE_KEY)) \
1862
        DXX_MENUITEM(VERB, CHECK, "GOLD KEY", opt_key_gold, menu_bit_wrapper(player_info.powerup_flags, PLAYER_FLAGS_GOLD_KEY)) \
1863
        DXX_MENUITEM(VERB, CHECK, "RED KEY", opt_key_red, menu_bit_wrapper(player_info.powerup_flags, PLAYER_FLAGS_RED_KEY))    \
1864
        WIMP_MENU_DXX(VERB)     \
1865
        DXX_MENUITEM(VERB, NUMBER, TXT_ENERGY, opt_energy, menu_fix_wrapper(plrobj.ctype.player_info.energy), 0, 200)   \
1866
        DXX_MENUITEM(VERB, NUMBER, "Shields", opt_shields, menu_fix_wrapper(plrobj.shields), 0, 200)    \
1867
        DXX_MENUITEM(VERB, TEXT, TXT_SCORE, opt_txt_score)      \
1868
        DXX_MENUITEM(VERB, INPUT, score_text, opt_score)        \
1869
        DXX_MENUITEM(VERB, NUMBER, "Laser Level", opt_laser_level, menu_number_bias_wrapper<1>(plr_laser_level), LASER_LEVEL_1 + 1, DXX_MAXIMUM_LASER_LEVEL + 1)        \
1870
        DXX_MENUITEM(VERB, NUMBER, "Concussion", opt_concussion, plrobj.ctype.player_info.secondary_ammo[CONCUSSION_INDEX], 0, 200)     \
1871
 
1872
static void do_cheat_menu()
1873
{
1874
        auto &Objects = LevelUniqueObjectState.Objects;
1875
        auto &vmobjptr = Objects.vmptr;
1876
        enum {
1877
                DXX_WIMP_MENU(ENUM)
1878
        };
1879
        int mmn;
1880
        std::array<newmenu_item, DXX_WIMP_MENU(COUNT)> m;
1881
        char score_text[sizeof("2147483647")];
1882
        auto &plrobj = get_local_plrobj();
1883
        auto &player_info = plrobj.ctype.player_info;
1884
        snprintf(score_text, sizeof(score_text), "%d", player_info.mission.score);
1885
        uint8_t plr_laser_level = player_info.laser_level;
1886
        DXX_WIMP_MENU(ADD);
1887
        mmn = newmenu_do("Wimp Menu",NULL,m, unused_newmenu_subfunction, unused_newmenu_userdata);
1888
        if (mmn > -1 )  {
1889
                DXX_WIMP_MENU(READ);
1890
                player_info.laser_level = laser_level_t(plr_laser_level);
1891
                char *p;
1892
                auto ul = strtoul(score_text, &p, 10);
1893
                if (!*p)
1894
                        player_info.mission.score = static_cast<int>(ul);
1895
                init_gauges();
1896
        }
1897
}
1898
#endif
1899
 
1900
 
1901
 
1902
//      Testing functions ----------------------------------------------------------
1903
 
1904
#ifndef NDEBUG
1905
//      Sounds for testing
1906
__attribute_used
1907
static int Test_sound;
1908
 
1909
static void play_test_sound()
1910
{
1911
        digi_play_sample(Test_sound, F1_0);
1912
}
1913
#endif  //ifndef NDEBUG
1914
 
1915
namespace dsx {
1916
 
1917
window_event_result ReadControls(const d_event &event)
1918
{
1919
        auto &LevelUniqueControlCenterState = LevelUniqueObjectState.ControlCenterState;
1920
        auto &Objects = LevelUniqueObjectState.Objects;
1921
        int key;
1922
        static ubyte exploding_flag=0;
1923
 
1924
        Player_fired_laser_this_frame=object_none;
1925
 
1926
        if (Player_dead_state == player_dead_state::exploded)
1927
        {
1928
                if (exploding_flag==0)  {
1929
                        exploding_flag = 1;                     // When player starts exploding, clear all input devices...
1930
                        game_flush_inputs();
1931
                }
1932
        } else {
1933
                exploding_flag=0;
1934
        }
1935
        if (Player_dead_state != player_dead_state::no &&
1936
                !((Game_mode & GM_MULTI) &&
1937
                        (multi_sending_message[Player_num] || multi_defining_message)
1938
                )
1939
        )
1940
        HandleDeathInput(event);
1941
 
1942
        if (Newdemo_state == ND_STATE_PLAYBACK)
1943
                update_vcr_state();
1944
 
1945
        if (event.type == EVENT_KEY_COMMAND)
1946
        {
1947
                key = event_key_get(event);
1948
#if defined(DXX_BUILD_DESCENT_II)
1949
                if (MarkerState.DefiningMarkerMessage())
1950
                {
1951
                        return MarkerInputMessage(key);
1952
                }
1953
#endif
1954
                if ( (Game_mode & GM_MULTI) && (multi_sending_message[Player_num] || multi_defining_message) )
1955
                {
1956
                        return multi_message_input_sub(key);
1957
                }
1958
 
1959
#ifndef RELEASE
1960
                if ((key&KEY_DEBUGGED)&&(Game_mode&GM_MULTI))   {
1961
                        Network_message_reciever = 100;         // Send to everyone...
1962
                        snprintf(Network_message.data(), Network_message.size(), "%s %s", TXT_I_AM_A, TXT_CHEATER);
1963
                }
1964
#endif
1965
 
1966
                if (Endlevel_sequence)
1967
                {
1968
                        auto result = HandleEndlevelKey(key);
1969
                        if (result != window_event_result::ignored)
1970
                                return result;
1971
                }
1972
                else if (Newdemo_state == ND_STATE_PLAYBACK )
1973
                {
1974
                        auto r = HandleDemoKey(key);
1975
                        if (r != window_event_result::ignored)
1976
                                return r;
1977
                }
1978
                else
1979
                {
1980
                        window_event_result r = FinalCheats();
1981
                        if (r == window_event_result::ignored)
1982
                                r = HandleSystemKey(key);
1983
                        if (r == window_event_result::ignored)
1984
                                r = HandleGameKey(key);
1985
                        if (r != window_event_result::ignored)
1986
                                return r;
1987
                }
1988
 
1989
#ifndef RELEASE
1990
                {
1991
                        window_event_result r = HandleTestKey(vmsegptridx, key);
1992
                        if (r != window_event_result::ignored)
1993
                                return r;
1994
                }
1995
#endif
1996
 
1997
                auto result = call_default_handler(event);
1998
                if (result != window_event_result::ignored)
1999
                        return result;
2000
        }
2001
 
2002
        if (!Endlevel_sequence && Newdemo_state != ND_STATE_PLAYBACK)
2003
        {
2004
                kconfig_read_controls(Controls, event, 0);
2005
                const auto Player_is_dead = Player_dead_state;
2006
                if (Player_is_dead != player_dead_state::no && HandleDeathInput(event))
2007
                        return window_event_result::handled;
2008
 
2009
                check_rear_view();
2010
 
2011
                // If automap key pressed, enable automap unless you are in network mode, control center destroyed and < 10 seconds left
2012
                if ( Controls.state.automap )
2013
                {
2014
                        Controls.state.automap = 0;
2015
                        if (Player_is_dead != player_dead_state::no || !((Game_mode & GM_MULTI) && LevelUniqueControlCenterState.Control_center_destroyed && LevelUniqueControlCenterState.Countdown_seconds_left < 10))
2016
                        {
2017
                                do_automap();
2018
                                return window_event_result::handled;
2019
                        }
2020
                }
2021
                if (Player_is_dead != player_dead_state::no)
2022
                        return window_event_result::ignored;
2023
                do_weapon_n_item_stuff(Objects);
2024
        }
2025
 
2026
        if (Controls.state.show_menu)
2027
        {
2028
                Controls.state.show_menu = 0;
2029
                return HandleSystemKey(KEY_ESC);
2030
        }
2031
 
2032
        return window_event_result::ignored;
2033
}
2034
 
2035
}