1 /*
2 BStone: A Source port of
3 Blake Stone: Aliens of Gold and Blake Stone: Planet Strike
4 
5 Copyright (c) 1992-2013 Apogee Entertainment, LLC
6 Copyright (c) 2013-2015 Boris I. Bendovsky (bibendovsky@hotmail.com)
7 
8 This program is free software; you can redistribute it and/or
9 modify it under the terms of the GNU General Public License
10 as published by the Free Software Foundation; either version 2
11 of the License, or (at your option) any later version.
12 
13 This program is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16 GNU General Public License for more details.
17 
18 You should have received a copy of the GNU General Public License
19 along with this program; if not, write to the
20 Free Software Foundation, Inc.,
21 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
22 */
23 
24 
25 #include <map>
26 #include "3d_def.h"
27 #include "jm_lzh.h"
28 
29 
30 void CA_CacheScreen(
31     int16_t chunk);
32 
33 void VH_UpdateScreen();
34 void DrawHighScores();
35 void ClearMemory();
36 
37 void DrawTopInfo(
38     sp_type type);
39 
40 void PreloadUpdate(
41     uint16_t current,
42     uint16_t total);
43 
44 void INL_GetJoyDelta(
45     uint16_t joy,
46     int* dx,
47     int* dy);
48 
49 bool LoadTheGame(
50     const std::string& file_name);
51 
52 bool IN_CheckAck();
53 
54 
55 //
56 // End game message
57 //
58 
59 char EndGameStr[] = { "    End current game?\n"
60                       " Are you sure (Y or N)?" };
61 
62 
63 #define ENDGAMESTR (EndGameStr)
64 
65 char QuitToDosStr[] = { "      Quit to DOS?\n"
66                         " Are you sure (Y or N)?" };
67 
68 #define FREEFONT(fontnum) { if (fontnum != STARTFONT + 2 && grsegs[fontnum]) { UNCACHEGRCHUNK(fontnum); } }
69 
70 
71 static const char* const CURGAME =
72     "   Continuing past this\n"
73     "  point will end the game\n"
74     " you're currently playing.\n"
75     "\n"
76     " Start a NEW game? (Y/N)";
77 
78 static const char* const GAMESVD =
79     "There's already a game\n"
80     "saved at this position.\n"
81     "\n"
82     "    Overwrite? (Y/N)";
83 
84 
85 bool EscPressed = false;
86 
87 int16_t lastmenumusic;
88 
89 int16_t MENUSONG = 0;
90 int16_t ROSTER_MUS = 0;
91 int16_t TEXTSONG = 0;
92 
93 
94 // ===========================================================================
95 //
96 // PRIVATE PROTOTYPES
97 //
98 // ===========================================================================
99 
100 void CP_ReadThis(
101     int16_t temp1);
102 
103 void CP_OrderingInfo(
104     int16_t temp1);
105 
106 void DrawEpisodePic(
107     int16_t w);
108 
109 void DrawAllSoundLights(
110     int16_t which);
111 
112 void ReadGameNames();
113 void FreeMusic();
114 
115 void CP_GameOptions(
116     int16_t temp1);
117 
118 void DrawGopMenu();
119 void CalibrateJoystick();
120 void ExitGame();
121 
122 void CP_Switches(
123     int16_t temp1);
124 
125 void DrawSwitchMenu();
126 
127 void DrawAllSwitchLights(
128     int16_t which);
129 
130 void DrawSwitchDescription(
131     int16_t which);
132 
133 // BBi
134 void cp_sound_volume(
135     int16_t);
136 
137 void cp_video(
138     int16_t);
139 // BBi
140 
141 
142 extern bool refresh_screen;
143 
144 
145 // ===========================================================================
146 //
147 // LOCAL DATA...
148 //
149 // ===========================================================================
150 
151 CP_iteminfo MainItems = { MENU_X, MENU_Y, 12, MM_READ_THIS, 0, 9, { 77, 1, 154, 9, 1 } };
152 CP_iteminfo GopItems = { MENU_X, MENU_Y + 30, 5, 0, 0, 9, { 77, 1, 154, 9, 1 } };
153 CP_iteminfo SndItems = { SM_X, SM_Y, 6, 0, 0, 7, { 87, -1, 144, 7, 1 } };
154 CP_iteminfo LSItems = { LSM_X, LSM_Y, 10, 0, 0, 8, { 86, -1, 144, 8, 1 } };
155 CP_iteminfo CtlItems = { CTL_X, CTL_Y, 7, -1, 0, 9, { 87, 1, 174, 9, 1 } };
156 CP_iteminfo CusItems = { CST_X, CST_Y + 7, 6, -1, 0, 15, { 54, -1, 203, 7, 1 } };
157 CP_iteminfo NewEitems = { NE_X, NE_Y, 6, 0, 0, 16, { 43, -2, 119, 16, 1 } };
158 CP_iteminfo NewItems = { NM_X, NM_Y, 4, 1, 0, 16, { 60, -2, 105, 16, 1 } };
159 CP_iteminfo SwitchItems = { MENU_X, 0, 0, 0, 0, 9, { 87, -1, 132, 7, 1 } };
160 
161 // BBi
162 CP_iteminfo video_items = { MENU_X, MENU_Y + 30, 1, 0, 0, 9, { 77, -1, 154, 7, 1 } };
163 // BBi
164 
165 
166 CP_itemtype MainMenu[] = {
167     { AT_ENABLED, "NEW MISSION", CP_NewGame, static_cast<uint8_t>(COAL_FONT) },
168     { AT_READIT, "ORDERING INFO", CP_OrderingInfo },
169     { AT_READIT, "INSTRUCTIONS", CP_ReadThis },
170     { AT_ENABLED, "STORY", CP_BlakeStoneSaga },
171     { AT_DISABLED, "", 0 },
172     { AT_ENABLED, "GAME OPTIONS", CP_GameOptions },
173     { AT_ENABLED, "HIGH SCORES", CP_ViewScores },
174     { AT_ENABLED, "LOAD MISSION", reinterpret_cast<void (*)(int16_t)>(CP_LoadGame) },
175     { AT_DISABLED, "SAVE MISSION", reinterpret_cast<void (*)(int16_t)>(CP_SaveGame) },
176     { AT_DISABLED, "", 0 },
177     { AT_ENABLED, "BACK TO DEMO", CP_ExitOptions },
178     { AT_ENABLED, "LOGOFF", 0 }
179 };
180 
181 CP_itemtype GopMenu[] = {
182     // BBi
183     { AT_ENABLED, "VIDEO", cp_video },
184     // BBi
185 
186     { AT_ENABLED, "SOUND", CP_Sound },
187 
188     // BBi
189     { AT_ENABLED, "SOUND VOLUME", cp_sound_volume },
190     // BBi
191 
192     { AT_ENABLED, "CONTROLS", CP_Control },
193     { AT_ENABLED, "SWITCHES", CP_Switches }
194 };
195 
196 CP_itemtype SndMenu[] = {
197     { AT_ENABLED, "NONE", 0 },
198     { AT_ENABLED, "ADLIB/SOUND BLASTER", 0 },
199     { AT_DISABLED, "", 0 },
200     { AT_DISABLED, "", 0 },
201     { AT_ENABLED, "NONE", 0 },
202     { AT_ENABLED, "ADLIB/SOUND BLASTER", 0 }
203 };
204 
205 CP_itemtype CtlMenu[] = {
206     { AT_DISABLED, "MOUSE ENABLED", 0 },
207     { AT_DISABLED, "JOYSTICK ENABLED", 0 },
208     { AT_DISABLED, "USE JOYSTICK PORT 2", 0 },
209     { AT_DISABLED, "GRAVIS GAMEPAD ENABLED", 0 },
210     { AT_DISABLED, "CALIBRATE JOYSTICK", 0 },
211     { AT_DISABLED, "MOUSE SENSITIVITY", MouseSensitivity },
212     { AT_ENABLED, "CUSTOMIZE CONTROLS", CustomControls }
213 };
214 
215 CP_itemtype SwitchMenu[] = {
216     { AT_ENABLED, "LIGHTING", 0 },
217     { AT_ENABLED, "REBA ATTACK INFO", 0 },
218     { AT_ENABLED, "SHOW CEILINGS", 0 },
219     { AT_ENABLED, "SHOW FLOORS", 0 },
220 
221     // BBi
222     { AT_ENABLED, "NO WALL HIT SOUND", 0 },
223     { AT_ENABLED, "MODERN CONTROLS", 0 },
224     { AT_ENABLED, "ALWAYS RUN", 0 },
225     { AT_ENABLED, "HEART BEAT SOUND", 0 },
226     { AT_ENABLED, "ROTATED AUTOMAP", 0 },
227 };
228 
229 
230 CP_itemtype NewEmenu[] = {
231     { AT_ENABLED, "MISSION 1:\n"
232       "STAR INSTITUTE", 0 },
233 
234     { AT_NON_SELECTABLE, "MISSION 2:\n"
235       "FLOATING FORTRESS", 0 },
236 
237     { AT_NON_SELECTABLE, "MISSION 3:\n"
238       "UNDERGROUND NETWORK", 0 },
239 
240     { AT_NON_SELECTABLE, "MISSION 4:\n"
241       "STAR PORT", 0 },
242 
243     { AT_NON_SELECTABLE, "MISSION 5:\n"
244       "HABITAT II", 0 },
245 
246     { AT_NON_SELECTABLE, "MISSION 6:\n"
247       "SATELLITE DEFENSE", 0 },
248 };
249 
250 CP_itemtype NewMenu[] = {
251     { AT_ENABLED, "LEVEL 1:\n"
252       "NOVICE AGENT", 0 },
253     { AT_ENABLED, "LEVEL 2:\n"
254       "SKILLED AGENT", 0 },
255     { AT_ENABLED, "LEVEL 3:\n"
256       "EXPERT AGENT", 0 },
257     { AT_ENABLED, "LEVEL 4:\n"
258       "VETERAN AGENT", 0 }
259 };
260 
261 CP_itemtype LSMenu[] = {
262     { AT_ENABLED, "", 0 },
263     { AT_ENABLED, "", 0 },
264     { AT_ENABLED, "", 0 },
265     { AT_ENABLED, "", 0 },
266     { AT_ENABLED, "", 0 },
267     { AT_ENABLED, "", 0 },
268     { AT_ENABLED, "", 0 },
269     { AT_ENABLED, "", 0 },
270     { AT_ENABLED, "", 0 },
271     { AT_ENABLED, "", 0 }
272 };
273 
274 CP_itemtype CusMenu[] = {
275     { AT_ENABLED, "", 0 },
276     { AT_DISABLED, "", 0 },
277     { AT_ENABLED, "", 0 },
278     { AT_DISABLED, "", 0 },
279     { AT_ENABLED, "", 0 },
280     { AT_ENABLED, "", 0 }
281 };
282 
283 // BBi
284 CP_itemtype video_menu[] = {
285     { AT_ENABLED, "TOGGLE WIDESCREEN", nullptr },
286 };
287 // BBi
288 
289 
290 int16_t color_hlite[] = {
291     HIGHLIGHT_DISABLED_COLOR,
292     HIGHLIGHT_TEXT_COLOR,
293     READHCOLOR,
294     HIGHLIGHT_DEACTIAVED_COLOR,
295 };
296 
297 int16_t color_norml[] = {
298     DISABLED_TEXT_COLOR,
299     ENABLED_TEXT_COLOR,
300     READCOLOR,
301     DEACTIAVED_TEXT_COLOR,
302 };
303 
304 int16_t EpisodeSelect[6] = {
305     1,
306     0,
307     0,
308     0,
309     0,
310     0,
311 };
312 
313 int16_t SaveGamesAvail[10];
314 int16_t StartGame;
315 int16_t SoundStatus = 1;
316 int16_t pickquick;
317 char SaveGameNames[10][GAME_DESCRIPTION_LEN + 1];
318 
319 static uint8_t menu_background_color = 0x00;
320 
321 
get_saved_game_base_name()322 static const std::string& get_saved_game_base_name()
323 {
324     static auto base_name = std::string();
325     static auto is_initialized = false;
326 
327     if (!is_initialized) {
328         is_initialized = true;
329 
330         base_name = "bstone_";
331 
332         if (::is_aog_sw()) {
333             base_name += "aog_sw";
334         } else if (::is_aog_full()) {
335             base_name += "aog_full";
336         } else if (::is_ps()) {
337             base_name += "ps";
338         } else {
339             throw std::runtime_error("Invalid game type.");
340         }
341 
342         base_name += "_saved_game_";
343     }
344 
345     return base_name;
346 }
347 
348 ////////////////////////////////////////////////////////////////////
349 //
350 // INPUT MANAGER SCANCODE TABLES
351 //
352 ////////////////////////////////////////////////////////////////////
353 
354 #ifndef CACHE_KEY_DATA
355 
356 using ScanCodes = std::vector<ScanCode>;
357 using ScanNames = std::vector<std::string>;
358 
359 // Scan code names with single chars
360 static ScanNames scan_names = {
361     "?", "?", "1", "2", "3", "4", "5", "6", "7", "8", "9", "0", "-", "+", "?", "?",
362     "Q", "W", "E", "R", "T", "Y", "U", "I", "O", "P", "[", "]", "|", "?", "A", "S",
363     "D", "F", "G", "H", "J", "K", "L", ";", "\"", "?", "?", "?", "Z", "X", "C", "V",
364     "B", "N", "M", ",", ".", "/", "?", "?", "?", "?", "?", "?", "?", "?", "?", "?",
365     "?", "?", "?", "?", "?", "?", "?", "?", "\xF", "?", "-", "\x15", "5", "\x11", "+", "?",
366     "\x13", "?", "?", "?", "?", "?", "?", "?", "?", "?", "?", "?", "?", "?", "?", "?",
367     "?", "?", "?", "?", "?", "?", "?", "?", "?", "?", "?", "?", "?", "?", "?", "?",
368     "?", "?", "?", "?", "?", "?", "?", "?", "?", "?", "?", "?", "?", "?", "?", "?",
369 }; // scan_names
370 
371 // Scan codes with >1 char names
372 static ScanCodes ext_scan_codes = {
373     ScanCode::sc_escape,
374     ScanCode::sc_backspace,
375     ScanCode::sc_tab,
376     ScanCode::sc_control,
377     ScanCode::sc_left_shift,
378     ScanCode::sc_space,
379     ScanCode::sc_caps_lock,
380     ScanCode::sc_f1,
381     ScanCode::sc_f2,
382     ScanCode::sc_f3,
383     ScanCode::sc_f4,
384     ScanCode::sc_f5,
385     ScanCode::sc_f6,
386     ScanCode::sc_f7,
387     ScanCode::sc_f8,
388     ScanCode::sc_f9,
389     ScanCode::sc_f10,
390     ScanCode::sc_f11,
391     ScanCode::sc_f12,
392     ScanCode::sc_scroll_lock,
393     ScanCode::sc_return,
394     ScanCode::sc_right_shift,
395     ScanCode::sc_print_screen,
396     ScanCode::sc_alt,
397     ScanCode::sc_home,
398     ScanCode::sc_page_up,
399     ScanCode::sc_end,
400     ScanCode::sc_page_down,
401     ScanCode::sc_insert,
402     ScanCode::sc_delete,
403     ScanCode::sc_num_lock,
404     ScanCode::sc_up_arrow,
405     ScanCode::sc_down_arrow,
406     ScanCode::sc_left_arrow,
407     ScanCode::sc_right_arrow,
408     ScanCode::sc_none,
409 }; // ExtScanCodes
410 
411 static ScanNames ext_scan_names = {
412     "ESC", "BKSP", "TAB", "CTRL", "LSHFT", "SPACE", "CAPSLK", "F1",
413     "F2", "F3", "F4", "F5", "F6", "F7", "F8", "F9",
414     "F10", "F11", "F12", "SCRLK", "ENTER", "RSHFT", "PRTSC", "ALT",
415     "HOME", "PGUP", "END", "PGDN", "INS", "DEL", "NUMLK", "UP",
416     "DOWN", "LEFT", "RIGHT", "",
417 }; // ext_scan_names
418 
419 #else
420 
421 uint8_t* ScanNames, * ExtScanNames;
422 ScanCode* ExtScanCodes;
423 
424 #endif
425 
426 using SpecialKeys = std::vector<ScanCode>;
427 
428 static SpecialKeys special_keys = {
429     ScanCode::sc_back_quote,
430     ScanCode::sc_equals,
431     ScanCode::sc_minus,
432     ScanCode::sc_l,
433     ScanCode::sc_p,
434     ScanCode::sc_m,
435     ScanCode::sc_s,
436     ScanCode::sc_i,
437     ScanCode::sc_q,
438     ScanCode::sc_w,
439     ScanCode::sc_e,
440     ScanCode::sc_return,
441     ScanCode::sc_1,
442     ScanCode::sc_2,
443     ScanCode::sc_3,
444     ScanCode::sc_4,
445     ScanCode::sc_5,
446     ScanCode::sc_tab,
447 }; // special_keys
448 
449 
450 // BBi
451 namespace {
452 
453 
454 using BindsNames = std::map<ScanCode, const char*>;
455 
456 
457 enum BindsFindDirection {
458     e_bfd_forward,
459     e_bfd_backward
460 }; // BindsFindDirection
461 
462 enum BindsFindFrom {
463     e_bff_current,
464     e_bff_not_current
465 }; // BindingFindFrom
466 
467 
468 class BindsItem {
469 public:
470     std::string name;
471     int name_width;
472     Binding* binding;
473 
BindsItem(std::string new_name=std::string (),int new_name_width=0,Binding * new_binding=nullptr)474     BindsItem(
475         std::string new_name = std::string(),
476         int new_name_width = 0,
477         Binding* new_binding = nullptr) :
478             name(new_name),
479             name_width(new_name_width),
480             binding(new_binding)
481     {
482     }
483 }; // BindsItem
484 
485 using BindsItems = std::vector<BindsItem>;
486 
487 static BindsItems binds = {
488     { "MOVEMENT", 0, nullptr, },
489     { "FORWARD", 0, &in_bindings[e_bi_forward], },
490     { "BACKWARD", 0, &in_bindings[e_bi_backward], },
491     { "LEFT", 0, &in_bindings[e_bi_left], },
492     { "RIGHT", 0, &in_bindings[e_bi_right], },
493     { "STRAFE", 0, &in_bindings[e_bi_strafe], },
494     { "STRAFE LEFT", 0, &in_bindings[e_bi_strafe_left], },
495     { "STRAFE RIGHT", 0, &in_bindings[e_bi_strafe_right], },
496     { "QUICK LEFT", 0, &in_bindings[e_bi_quick_left], },
497     { "QUICK RIGHT", 0, &in_bindings[e_bi_quick_right], },
498     { "TURN AROUND", 0, &in_bindings[e_bi_turn_around], },
499     { "RUN", 0, &in_bindings[e_bi_run], },
500     { "", 0, nullptr, },
501 
502     { "WEAPONS", 0, nullptr, },
503     { "ATTACK", 0, &in_bindings[e_bi_attack], },
504     { "AUTO CHARGE PISTOL", 0, &in_bindings[e_bi_weapon_1], },
505     { "SLOW FIRE PROTECTOR", 0, &in_bindings[e_bi_weapon_2], },
506     { "RAPID ASSAULT WEAPON", 0, &in_bindings[e_bi_weapon_3], },
507     { "DUAL NEUTRON DISRUPTOR", 0, &in_bindings[e_bi_weapon_4], },
508     { "PLASMA DISCHARGE UNIT", 0, &in_bindings[e_bi_weapon_5], },
509     { "ANTI-PLASMA CANNON (PS)", 0, &in_bindings[e_bi_weapon_6], },
510     { "FISSION DETONATOR (PS)", 0, &in_bindings[e_bi_weapon_7], },
511     { "", 0, nullptr, },
512 
513     { "INTERACTION", 0, nullptr, },
514     { "USE", 0, &in_bindings[e_bi_use], },
515     { "", 0, nullptr, },
516 
517     { "HUD", 0, nullptr, },
518     { "STATS", 0, &in_bindings[e_bi_stats], },
519     { "MAGNIFY RADAR (PS)", 0, &in_bindings[e_bi_radar_magnify], },
520     { "MINIFY RADAR (PS)", 0, &in_bindings[e_bi_radar_minify], },
521     { "", 0, nullptr, },
522 
523     { "MENU", 0, nullptr, },
524     { "HELP", 0, &in_bindings[e_bi_help], },
525     { "SAVE", 0, &in_bindings[e_bi_save], },
526     { "LOAD", 0, &in_bindings[e_bi_load], },
527     { "SOUND OPTIONS", 0, &in_bindings[e_bi_sound], },
528     { "CONTROLS", 0, &in_bindings[e_bi_controls], },
529     { "END GAME", 0, &in_bindings[e_bi_end_game], },
530     { "QUICK SAVE", 0, &in_bindings[e_bi_quick_save], },
531     { "QUICK LOAD", 0, &in_bindings[e_bi_quick_load], },
532     { "QUICK EXIT", 0, &in_bindings[e_bi_quick_exit], },
533     { "", 0, nullptr, },
534 
535     { "OPTIONS", 0, nullptr, },
536     { "ATTACK INFO", 0, &in_bindings[e_bi_attack_info], },
537     { "LIGHTNING", 0, &in_bindings[e_bi_lightning], },
538     { "SOUND", 0, &in_bindings[e_bi_sfx], },
539     { "MUSIC", 0, &in_bindings[e_bi_music], },
540     { "CEILING", 0, &in_bindings[e_bi_ceiling], },
541     { "FLOORING", 0, &in_bindings[e_bi_flooring], },
542     { "HEART BEAT (AOG)", 0, &in_bindings[e_bi_heart_beat], },
543     { "", 0, nullptr, },
544 
545     { "MISC", 0, nullptr, },
546     { "PAUSE", 0, &in_bindings[e_bi_pause], },
547     { "(UN)GRAB MOUSE", 0, &in_bindings[e_bi_grab_mouse], },
548 }; // binds
549 
550 
551 const int k_binds_max_per_window = 14;
552 const int k_binds_text_keys_gap = 3;
553 const int k_binds_line_spacing = 1;
554 const int k_binds_top = 28;
555 
556 const uint8_t k_binds_category_color = 0x4A;
557 const uint8_t k_binds_text_color = 0x56;
558 const uint8_t k_binds_key_text_color = 0x7F;
559 const uint8_t k_binds_key_bar_default_color = 0x03;
560 const uint8_t k_binds_key_bar_active_color = 0x31;
561 const uint8_t k_binds_key_bar_assign_color = 0x14;
562 
563 int binds_count;
564 int binds_window;
565 int binds_window_offset;
566 int binds_key_index;
567 int binds_key_width;
568 int binds_max_text_width;
569 int binds_text_height;
570 int binds_text_x;
571 int binds_key_x[k_max_binding_keys];
572 bool binds_is_assigning = false;
573 
574 BindsNames binds_names;
575 
576 
binds_initialize_menu()577 void binds_initialize_menu()
578 {
579     static bool is_initialized = false;
580 
581     if (is_initialized) {
582         return;
583     }
584 
585     binds_count = 0;
586     binds_window = 0;
587     binds_window_offset = 0;
588     binds_key_index = 0;
589     binds_key_width = 0;
590     binds_max_text_width = 0;
591     binds_text_height = 0;
592     binds_is_assigning = false;
593 
594     bool has_bindings = false;
595 
596     fontnumber = 2;
597 
598     for (auto& bind : binds) {
599         ++binds_count;
600 
601         if (!bind.name.empty()) {
602             int width = 0;
603             int height = 0;
604             VW_MeasurePropString(bind.name.c_str(), &width, &height);
605 
606             bind.name_width = width;
607 
608             if (width > binds_max_text_width) {
609                 binds_max_text_width = width;
610             }
611 
612             if (height > binds_text_height) {
613                 binds_text_height = height;
614             }
615 
616             has_bindings = true;
617         }
618     }
619 
620 
621     if (!has_bindings) {
622         ::Quit("No bindings.");
623     }
624 
625     binds_names.clear();
626     binds_names[ScanCode::sc_return] = "ENTER";
627     binds_names[ScanCode::sc_space] = "SPACE";
628     binds_names[ScanCode::sc_minus] = "-";
629     binds_names[ScanCode::sc_equals] = "=";
630     binds_names[ScanCode::sc_backspace] = "BACKSPACE";
631     binds_names[ScanCode::sc_tab] = "TAB";
632     binds_names[ScanCode::sc_alt] = "ALT";
633     binds_names[ScanCode::sc_left_bracket] = "[";
634     binds_names[ScanCode::sc_right_bracket] = "]";
635     binds_names[ScanCode::sc_control] = "CTRL";
636     binds_names[ScanCode::sc_caps_lock] = "CAPS LOCK";
637     binds_names[ScanCode::sc_num_lock] = "NUM LOCK";
638     binds_names[ScanCode::sc_scroll_lock] = "SCROLL LOCK";
639     binds_names[ScanCode::sc_left_shift] = "L-SHIFT";
640     binds_names[ScanCode::sc_right_shift] = "R-SHIFT";
641     binds_names[ScanCode::sc_up_arrow] = "UP";
642     binds_names[ScanCode::sc_down_arrow] = "DOWN";
643     binds_names[ScanCode::sc_left_arrow] = "LEFT";
644     binds_names[ScanCode::sc_right_arrow] = "RIGHT";
645     binds_names[ScanCode::sc_insert] = "INS";
646     binds_names[ScanCode::sc_delete] = "DEL";
647     binds_names[ScanCode::sc_home] = "HOME";
648     binds_names[ScanCode::sc_end] = "END";
649     binds_names[ScanCode::sc_page_up] = "PGUP";
650     binds_names[ScanCode::sc_page_down] = "PGDN";
651     binds_names[ScanCode::sc_slash] = "/";
652     binds_names[ScanCode::sc_f1] = "F1";
653     binds_names[ScanCode::sc_f2] = "F2";
654     binds_names[ScanCode::sc_f3] = "F3";
655     binds_names[ScanCode::sc_f4] = "F4";
656     binds_names[ScanCode::sc_f5] = "F5";
657     binds_names[ScanCode::sc_f6] = "F6";
658     binds_names[ScanCode::sc_f7] = "F7";
659     binds_names[ScanCode::sc_f8] = "F8";
660     binds_names[ScanCode::sc_f9] = "F9";
661     binds_names[ScanCode::sc_f10] = "F10";
662     binds_names[ScanCode::sc_f11] = "F11";
663     binds_names[ScanCode::sc_f12] = "F12";
664     binds_names[ScanCode::sc_print_screen] = "PRT SCR";
665     binds_names[ScanCode::sc_pause] = "PAUSE";
666     binds_names[ScanCode::sc_back_quote] = "BACK QUOTE";
667     binds_names[ScanCode::sc_semicolon] = ";";
668     binds_names[ScanCode::sc_quote] = "'";
669     binds_names[ScanCode::sc_backslash] = "\\";
670     binds_names[ScanCode::sc_comma] = ",";
671     binds_names[ScanCode::sc_period] = ".";
672 
673     binds_names[ScanCode::sc_1] = "1";
674     binds_names[ScanCode::sc_2] = "2";
675     binds_names[ScanCode::sc_3] = "3";
676     binds_names[ScanCode::sc_4] = "4";
677     binds_names[ScanCode::sc_5] = "5";
678     binds_names[ScanCode::sc_6] = "6";
679     binds_names[ScanCode::sc_7] = "7";
680     binds_names[ScanCode::sc_8] = "8";
681     binds_names[ScanCode::sc_9] = "9";
682     binds_names[ScanCode::sc_0] = "0";
683 
684     binds_names[ScanCode::sc_a] = "A";
685     binds_names[ScanCode::sc_b] = "B";
686     binds_names[ScanCode::sc_c] = "C";
687     binds_names[ScanCode::sc_d] = "D";
688     binds_names[ScanCode::sc_e] = "E";
689     binds_names[ScanCode::sc_f] = "F";
690     binds_names[ScanCode::sc_g] = "G";
691     binds_names[ScanCode::sc_h] = "H";
692     binds_names[ScanCode::sc_i] = "I";
693     binds_names[ScanCode::sc_j] = "J";
694     binds_names[ScanCode::sc_k] = "K";
695     binds_names[ScanCode::sc_l] = "L";
696     binds_names[ScanCode::sc_m] = "M";
697     binds_names[ScanCode::sc_n] = "N";
698     binds_names[ScanCode::sc_o] = "O";
699     binds_names[ScanCode::sc_p] = "P";
700     binds_names[ScanCode::sc_q] = "Q";
701     binds_names[ScanCode::sc_r] = "R";
702     binds_names[ScanCode::sc_s] = "S";
703     binds_names[ScanCode::sc_t] = "T";
704     binds_names[ScanCode::sc_u] = "U";
705     binds_names[ScanCode::sc_v] = "V";
706     binds_names[ScanCode::sc_w] = "W";
707     binds_names[ScanCode::sc_x] = "X";
708     binds_names[ScanCode::sc_y] = "Y";
709     binds_names[ScanCode::sc_z] = "Z";
710 
711     binds_names[ScanCode::sc_kp_minus] = "KP MINUS";
712     binds_names[ScanCode::sc_kp_plus] = "KP PLUS";
713 
714     binds_names[ScanCode::sc_mouse_left] = "MOUSE 1";
715     binds_names[ScanCode::sc_mouse_middle] = "MOUSE 2";
716     binds_names[ScanCode::sc_mouse_right] = "MOUSE 3";
717     binds_names[ScanCode::sc_mouse_x1] = "MOUSE 4";
718     binds_names[ScanCode::sc_mouse_x2] = "MOUSE 5";
719 
720     for (const auto& binds_name : binds_names) {
721         int width = 0;
722         int height = 0;
723         VW_MeasurePropString(binds_name.second, &width, &height);
724 
725         if (width > binds_key_width) {
726             binds_key_width = width;
727         }
728     }
729 
730     int max_keys_width = k_max_binding_keys * (binds_key_width + 1);
731     int max_text_width = 2 + binds_max_text_width;
732     int max_width = max_keys_width + k_binds_text_keys_gap + max_text_width;
733 
734     int text_x = (SCREEN_W - max_width) / 2;
735 
736     int base_key_x = text_x + max_text_width + k_binds_text_keys_gap;
737 
738     binds_text_x = text_x;
739 
740     for (int i = 0; i < k_max_binding_keys; ++i) {
741         binds_key_x[i] = base_key_x + (i * (binds_key_width + 1));
742     }
743 
744     is_initialized = true;
745 }
746 
binds_advance_to_item(BindsFindDirection direction)747 bool binds_advance_to_item(
748     BindsFindDirection direction)
749 {
750     switch (direction) {
751     case e_bfd_forward:
752         if ((binds_window + binds_window_offset + 1) < binds_count) {
753             ++binds_window_offset;
754 
755             if (binds_window_offset == k_binds_max_per_window) {
756                 ++binds_window;
757                 --binds_window_offset;
758             }
759 
760             return true;
761         } else {
762             return false;
763         }
764 
765     case e_bfd_backward:
766         if ((binds_window + binds_window_offset) > 0) {
767             --binds_window_offset;
768 
769             if (binds_window_offset < 0) {
770                 --binds_window;
771                 binds_window_offset = 0;
772             }
773 
774             return true;
775         } else {
776             return false;
777         }
778 
779     default:
780         return false;
781     }
782 }
783 
binds_find_item(BindsFindDirection direction,BindsFindFrom from)784 bool binds_find_item(
785     BindsFindDirection direction,
786     BindsFindFrom from)
787 {
788     if (from == e_bff_not_current) {
789         if (!binds_advance_to_item(direction)) {
790             return false;
791         }
792     }
793 
794     while (true) {
795         if (binds[binds_window + binds_window_offset].binding) {
796             return true;
797         }
798 
799         if (!binds_advance_to_item(direction)) {
800             return false;
801         }
802     }
803 
804     return false;
805 }
806 
binds_assign_new_key(ScanCode key,Binding & binding)807 bool binds_assign_new_key(
808     ScanCode key,
809     Binding& binding)
810 {
811     auto it = binds_names.find(LastScan);
812 
813     if (it == binds_names.end()) {
814         return false;
815     }
816 
817     for (int b = 0; b < k_max_bindings; ++b) {
818         for (int k = 0; k < k_max_binding_keys; ++k) {
819             if (in_bindings[b][k] == key) {
820                 in_bindings[b][k] = ScanCode::sc_none;
821             }
822         }
823     }
824 
825     binding[binds_key_index] = key;
826 
827     return true;
828 }
829 
binds_remove_binding()830 void binds_remove_binding()
831 {
832     auto& item = binds[binds_window + binds_window_offset];
833     (*item.binding)[binds_key_index] = ScanCode::sc_none;
834 }
835 
binds_draw_item_text(int item_index)836 void binds_draw_item_text(
837     int item_index)
838 {
839     auto& item = binds[binds_window + item_index];
840 
841     if (item.name.empty()) {
842         return;
843     }
844 
845     int x = SCREEN_X + binds_text_x;
846     int y = SCREEN_Y + k_binds_top +
847             (item_index * (binds_text_height + k_binds_line_spacing));
848 
849     int text_left_offset = 0;
850     int text_width = 0;
851 
852     if (item.binding) {
853         text_width = item.name_width;
854         text_left_offset = binds_max_text_width - item.name_width;
855 
856         if (text_left_offset < 0) {
857             text_left_offset = 0;
858             text_width = binds_max_text_width;
859         }
860     } else {
861         text_width = SCREEN_W;
862     }
863 
864     PrintX = static_cast<int16_t>(x + text_left_offset);
865     PrintY = static_cast<int16_t>(y);
866     WindowX = PrintX;
867     WindowY = PrintY;
868     WindowW = static_cast<int16_t>(text_width);
869     WindowH = static_cast<int16_t>(binds_text_height);
870 
871     if (item.binding) {
872         fontcolor = k_binds_text_color;
873         US_Print(item.name.c_str());
874     } else {
875         fontcolor = k_binds_category_color;
876         US_PrintCentered(item.name.c_str());
877     }
878 }
879 
binds_draw_keys(int item_index)880 void binds_draw_keys(
881     int item_index)
882 {
883     const auto& item = binds[binds_window + item_index];
884 
885     if (!item.binding) {
886         return;
887     }
888 
889     int y = SCREEN_Y + k_binds_top +
890         (item_index * (binds_text_height + k_binds_line_spacing));
891 
892     bool is_current = (item_index == binds_window_offset);
893 
894     for (int k = 0; k < k_max_binding_keys; ++k) {
895         uint8_t color;
896         ScanCode key;
897         const char* key_name;
898 
899         bool is_active = is_current && (binds_key_index == k);
900 
901         if (is_active) {
902             color =
903                 binds_is_assigning ?
904                 k_binds_key_bar_assign_color :
905                 k_binds_key_bar_active_color;
906         } else {
907             color = k_binds_key_bar_default_color;
908         }
909 
910         int x = SCREEN_X + binds_key_x[k];
911 
912         VWB_Bar(
913             x,
914             y,
915             binds_key_width,
916             binds_text_height,
917             color);
918 
919         PrintX = static_cast<int16_t>(x);
920         PrintY = static_cast<int16_t>(y);
921         WindowX = PrintX;
922         WindowY = PrintY;
923         WindowW = static_cast<int16_t>(binds_key_width);
924         WindowH = static_cast<int16_t>(binds_text_height);
925 
926         if (!(is_active && binds_is_assigning)) {
927             key = (*item.binding)[k];
928 
929             if (key != ScanCode::sc_none) {
930                 key_name = "???";
931 
932                 auto name_it = binds_names.find(key);
933 
934                 if (name_it != binds_names.end()) {
935                     key_name = name_it->second;
936                 }
937 
938                 fontcolor = k_binds_key_text_color;
939                 US_PrintCentered(key_name);
940             }
941         }
942     }
943 }
944 
binds_draw()945 void binds_draw()
946 {
947     bool found_item = false;
948 
949     found_item = binds_find_item(e_bfd_forward, e_bff_current);
950 
951     if (!found_item) {
952         found_item = binds_find_item(e_bfd_backward, e_bff_current);
953     }
954 
955     ClearMScreen();
956     DrawMenuTitle("CUSTOMIZE CONTROLS");
957 
958     DrawInstructions(
959         binds_is_assigning ? IT_CONTROLS_ASSIGNING_KEY : IT_CONTROLS);
960 
961     fontnumber = 2;
962 
963     for (int i = 0; i < k_binds_max_per_window; ++i) {
964         int item_index = binds_window + i;
965 
966         if (item_index == binds_count) {
967             break;
968         }
969 
970         binds_draw_item_text(i);
971         binds_draw_keys(i);
972     }
973 }
974 
binds_draw_menu()975 void binds_draw_menu()
976 {
977     bool is_up_pressed = false;
978     bool is_down_pressed = false;
979     bool is_left_pressed = false;
980     bool is_right_pressed = false;
981     bool is_pgdn_pressed = false;
982     bool is_pgup_pressed = false;
983     bool is_enter_pressed = false;
984     bool is_delete_pressed = false;
985     bool is_escape_pressed = false;
986 
987     bool handle_up = false;
988     bool handle_down = false;
989     bool handle_left = false;
990     bool handle_right = false;
991     bool handle_pgdn = false;
992     bool handle_pgup = false;
993     bool handle_enter = false;
994     bool handle_delete = false;
995     bool handle_escape = false;
996 
997     bool fade_in = true;
998 
999     CA_CacheGrChunk(STARTFONT + 2);
1000     CA_CacheGrChunk(STARTFONT + 4);
1001     binds_initialize_menu();
1002 
1003     binds_is_assigning = false;
1004 
1005     while (true) {
1006         binds_draw();
1007         VW_UpdateScreen();
1008 
1009         if (fade_in) {
1010             fade_in = false;
1011             MenuFadeIn();
1012         }
1013 
1014         in_handle_events();
1015 
1016         if (binds_is_assigning) {
1017             LastScan = ScanCode::sc_none;
1018             bool quit = false;
1019 
1020             while (!quit) {
1021                 LastScan = ScanCode::sc_none;
1022                 in_handle_events();
1023 
1024                 if (Keyboard[ScanCode::sc_escape]) {
1025                     quit = true;
1026                     sd_play_player_sound(ESCPRESSEDSND, bstone::AC_ITEM);
1027                 } else if (LastScan != ScanCode::sc_none) {
1028                     auto& item = binds[binds_window + binds_window_offset];
1029 
1030                     if (binds_assign_new_key(LastScan, *item.binding)) {
1031                         ShootSnd();
1032                         quit = true;
1033                     } else {
1034                         sd_play_player_sound(NOWAYSND, bstone::AC_ITEM);
1035                     }
1036                 }
1037             }
1038 
1039             is_escape_pressed = true;
1040             binds_is_assigning = false;
1041         } else {
1042             if (Keyboard[ScanCode::sc_up_arrow]) {
1043                 if (!is_up_pressed) {
1044                     handle_up = true;
1045                     is_up_pressed = true;
1046                 }
1047             } else {
1048                 is_up_pressed = false;
1049             }
1050 
1051             if (Keyboard[ScanCode::sc_down_arrow]) {
1052                 if (!is_down_pressed) {
1053                     handle_down = true;
1054                     is_down_pressed = true;
1055                 }
1056             } else {
1057                 is_down_pressed = false;
1058             }
1059 
1060             if (Keyboard[ScanCode::sc_left_arrow]) {
1061                 if (!is_left_pressed) {
1062                     handle_left = true;
1063                     is_left_pressed = true;
1064                 }
1065             } else {
1066                 is_left_pressed = false;
1067             }
1068 
1069             if (Keyboard[ScanCode::sc_right_arrow]) {
1070                 if (!is_right_pressed) {
1071                     handle_right = true;
1072                     is_right_pressed = true;
1073                 }
1074             } else {
1075                 is_right_pressed = false;
1076             }
1077 
1078             if (Keyboard[ScanCode::sc_page_down]) {
1079                 if (!is_pgdn_pressed) {
1080                     handle_pgdn = true;
1081                     is_pgdn_pressed = true;
1082                 }
1083             } else {
1084                 is_pgdn_pressed = false;
1085             }
1086 
1087             if (Keyboard[ScanCode::sc_page_up]) {
1088                 if (!is_pgup_pressed) {
1089                     handle_pgup = true;
1090                     is_pgup_pressed = true;
1091                 }
1092             } else {
1093                 is_pgup_pressed = false;
1094             }
1095 
1096             if (Keyboard[ScanCode::sc_return]) {
1097                 if (!is_enter_pressed) {
1098                     handle_enter = true;
1099                     is_enter_pressed = true;
1100                 }
1101             } else {
1102                 is_enter_pressed = false;
1103             }
1104 
1105             if (Keyboard[ScanCode::sc_delete]) {
1106                 if (!is_delete_pressed) {
1107                     handle_delete = true;
1108                     is_delete_pressed = true;
1109                 }
1110             } else {
1111                 is_delete_pressed = false;
1112             }
1113 
1114             if (Keyboard[ScanCode::sc_escape]) {
1115                 if (!is_escape_pressed) {
1116                     handle_escape = true;
1117                     is_escape_pressed = true;
1118                 }
1119             } else {
1120                 is_escape_pressed = false;
1121             }
1122 
1123             if (handle_up) {
1124                 handle_up = false;
1125                 binds_find_item(e_bfd_backward, e_bff_not_current);
1126             }
1127 
1128             if (handle_down) {
1129                 handle_down = false;
1130                 binds_find_item(e_bfd_forward, e_bff_not_current);
1131             }
1132 
1133             if (handle_left) {
1134                 handle_left = false;
1135 
1136                 if (binds_key_index == 1) {
1137                     binds_key_index = 0;
1138                 }
1139             }
1140 
1141             if (handle_right) {
1142                 handle_right = false;
1143 
1144                 if (binds_key_index == 0) {
1145                     binds_key_index = 1;
1146                 }
1147             }
1148 
1149             if (handle_pgdn) {
1150                 handle_pgdn = false;
1151 
1152                 for (int i = 0; i < k_binds_max_per_window; ++i) {
1153                     binds_find_item(e_bfd_forward, e_bff_not_current);
1154                 }
1155             }
1156 
1157             if (handle_pgup) {
1158                 handle_pgup = false;
1159 
1160                 for (int i = 0; i < k_binds_max_per_window; ++i) {
1161                     binds_find_item(e_bfd_backward, e_bff_not_current);
1162                 }
1163             }
1164 
1165             if (handle_enter) {
1166                 handle_enter = false;
1167                 binds_is_assigning = true;
1168             }
1169 
1170             if (handle_delete) {
1171                 handle_delete = false;
1172                 binds_remove_binding();
1173                 ShootSnd();
1174             }
1175 
1176             if (handle_escape) {
1177                 handle_escape = false;
1178                 sd_play_player_sound(ESCPRESSEDSND, bstone::AC_ITEM);
1179                 break;
1180             }
1181         }
1182     }
1183 
1184     ::MenuFadeOut();
1185 }
1186 
1187 
1188 } // namespace
1189 // BBi
1190 
HelpScreens()1191 void HelpScreens()
1192 {
1193     HelpPresenter(nullptr, false, HELPTEXT, true);
1194 }
1195 
HelpPresenter(const char * fname,bool continue_keys,uint16_t id_cache,bool startmusic)1196 void HelpPresenter(
1197     const char* fname,
1198     bool continue_keys,
1199     uint16_t id_cache,
1200     bool startmusic)
1201 {
1202     const int FULL_VIEW_WIDTH = 19;
1203 
1204     PresenterInfo pi;
1205     int oldwidth;
1206 
1207     memset(&pi, 0, sizeof(pi));
1208 
1209     pi.flags = TPF_SHOW_PAGES;
1210     if (continue_keys) {
1211         pi.flags |= TPF_CONTINUE;
1212     }
1213 
1214     VW_FadeOut();
1215 
1216 // Change view size to MAX! (scaler clips shapes on smaller views)
1217 //
1218     oldwidth = viewwidth / 16;
1219     if (oldwidth != FULL_VIEW_WIDTH) {
1220         NewViewSize();
1221     }
1222 
1223 // Draw help border
1224 //
1225     CacheLump(H_TOPWINDOWPIC, H_BOTTOMINFOPIC);
1226     VWB_DrawPic(0, 0, H_TOPWINDOWPIC);
1227     VWB_DrawPic(0, 8, H_LEFTWINDOWPIC);
1228     VWB_DrawPic(312, 8, H_RIGHTWINDOWPIC);
1229     VWB_DrawPic(8, 176, H_BOTTOMINFOPIC);
1230     UnCacheLump(H_TOPWINDOWPIC, H_BOTTOMINFOPIC);
1231 
1232 // Setup for text presenter
1233 //
1234     pi.xl = 8;
1235     pi.yl = 8;
1236     pi.xh = 311;
1237     pi.yh = 175;
1238     pi.ltcolor = 0x7b;
1239     pi.bgcolor = 0x7d;
1240     pi.dkcolor = 0x7f;
1241     pi.shcolor = 0x00;
1242     pi.fontnumber = 4;
1243 
1244     if (continue_keys) {
1245         pi.infoline = (char*)" UP / DN - PAGES       ENTER - CONTINUES         ESC - EXITS";
1246     } else {
1247         pi.infoline = (char*)"           UP / DN - PAGES            ESC - EXITS";
1248     }
1249 
1250     if (startmusic) {
1251         ::StartCPMusic(static_cast<int16_t>(TEXTSONG));
1252     }
1253 
1254 // Load, present, and free help text.
1255 //
1256     TP_LoadScript(fname, &pi, id_cache);
1257     TP_Presenter(&pi);
1258     TP_FreeScript(&pi, id_cache);
1259 
1260     ::MenuFadeOut();
1261 
1262 // Reset view size
1263 //
1264     if (oldwidth != FULL_VIEW_WIDTH) {
1265         NewViewSize();
1266     }
1267 
1268     if (startmusic && TPscan == ScanCode::sc_escape) {
1269         ::StartCPMusic(MENUSONG);
1270     }
1271 
1272     IN_ClearKeysDown();
1273 }
1274 
US_ControlPanel(ScanCode scancode)1275 void US_ControlPanel(
1276     ScanCode scancode)
1277 {
1278     // BBi
1279     menu_background_color = (
1280         (::is_aog_sw() | ::is_aog_full_v3_0()) ?
1281         0x04 :
1282         TERM_BACK_COLOR);
1283 
1284 
1285     int16_t which;
1286 
1287     if (ingame) {
1288         if (CP_CheckQuick(scancode)) {
1289             return;
1290         }
1291     }
1292 
1293     SetupControlPanel();
1294 
1295     ::StartCPMusic(MENUSONG);
1296 
1297     //
1298     // F-KEYS FROM WITHIN GAME
1299     //
1300     auto finish = false;
1301 
1302     switch (scancode) {
1303     case ScanCode::sc_f1:
1304         ::CleanupControlPanel();
1305         ::HelpScreens();
1306         return;
1307 
1308     case ScanCode::sc_f2:
1309         ::CP_SaveGame(0);
1310         finish = true;
1311         break;
1312 
1313     case ScanCode::sc_f3:
1314         ::CP_LoadGame(0);
1315         finish = true;
1316         break;
1317 
1318     case ScanCode::sc_f4:
1319         ::CP_Sound(0);
1320         finish = true;
1321         break;
1322 
1323     case ScanCode::sc_f6:
1324         CP_Control(0);
1325         finish = true;
1326         break;
1327 
1328     default:
1329         break;
1330     }
1331 
1332     if (finish) {
1333         ::CleanupControlPanel();
1334         return;
1335     }
1336 
1337     DrawMainMenu();
1338     MenuFadeIn();
1339     StartGame = 0;
1340 
1341     //
1342     // MAIN MENU LOOP
1343     //
1344     do {
1345         which = HandleMenu(&MainItems, &MainMenu[0], nullptr);
1346 
1347         switch (which) {
1348         case MM_VIEW_SCORES:
1349             if (!MainMenu[MM_VIEW_SCORES].routine) {
1350                 if (CP_EndGame()) {
1351                     StartGame = 1;
1352                 }
1353             }
1354 
1355             DrawMainMenu();
1356             MenuFadeIn();
1357             break;
1358 
1359         case -1:
1360         case MM_LOGOFF:
1361             CP_Quit();
1362             break;
1363 
1364         default:
1365             if (!StartGame) {
1366                 DrawMainMenu();
1367                 MenuFadeIn();
1368             }
1369         }
1370 
1371         //
1372         // "EXIT OPTIONS" OR "NEW GAME" EXITS
1373         //
1374     } while (!StartGame);
1375 
1376     //
1377     // DEALLOCATE EVERYTHING
1378     //
1379     CleanupControlPanel();
1380     if (!loadedgame) {
1381         StopMusic();
1382     }
1383 
1384 
1385     //
1386     // CHANGE MAINMENU ITEM
1387     //
1388     if (startgame || loadedgame) {
1389         MainMenu[MM_VIEW_SCORES].routine = nullptr;
1390         strcpy(MainMenu[MM_VIEW_SCORES].string, "END GAME");
1391     }
1392 
1393     if (ingame && loadedgame) {
1394         refresh_screen = false;
1395     }
1396 
1397 
1398 #ifdef CACHE_KEY_DATA
1399     FREEFONT(SCANNAMES_DATA);
1400     FREEFONT(EXTSCANNAMES_DATA);
1401     FREEFONT(EXTSCANCODES_DATA);
1402 #endif
1403 }
1404 
DrawMainMenu()1405 void DrawMainMenu()
1406 {
1407     ControlPanelFree();
1408     CA_CacheScreen(BACKGROUND_SCREENPIC);
1409     ControlPanelAlloc();
1410 
1411     ClearMScreen();
1412     DrawMenuTitle("MAIN OPTIONS");
1413     DrawInstructions(IT_STANDARD);
1414 
1415     //
1416     // CHANGE "MISSION" AND "DEMO"
1417     //
1418     if (ingame) {
1419         strcpy(&MainMenu[MM_BACK_TO_DEMO].string[8], "MISSION");
1420         MainMenu[MM_BACK_TO_DEMO].active = AT_READIT;
1421     } else {
1422         strcpy(&MainMenu[MM_BACK_TO_DEMO].string[8], "DEMO");
1423         MainMenu[MM_BACK_TO_DEMO].active = AT_ENABLED;
1424     }
1425 
1426     fontnumber = 4; // COAL
1427 
1428     DrawMenu(&MainItems, &MainMenu[0]);
1429 
1430     VW_UpdateScreen();
1431 }
1432 
CP_ReadThis(int16_t)1433 void CP_ReadThis(
1434     int16_t)
1435 {
1436     ControlPanelFree();
1437     HelpScreens();
1438     ControlPanelAlloc();
1439 }
1440 
CP_OrderingInfo(int16_t)1441 void CP_OrderingInfo(
1442     int16_t)
1443 {
1444     ControlPanelFree();
1445     HelpPresenter("", false, ORDERTEXT, true);
1446     ControlPanelAlloc();
1447 }
1448 
CP_BlakeStoneSaga(int16_t)1449 void CP_BlakeStoneSaga(
1450     int16_t)
1451 {
1452     ControlPanelFree();
1453     HelpPresenter("", false, SAGATEXT, true);
1454     ControlPanelAlloc();
1455 }
1456 
1457 // --------------------------------------------------------------------------
1458 // CP_CheckQuick() - CHECK QUICK-KEYS & QUIT (WHILE IN A GAME)
1459 // --------------------------------------------------------------------------
CP_CheckQuick(ScanCode scancode)1460 bool CP_CheckQuick(
1461     ScanCode scancode)
1462 {
1463     switch (scancode) {
1464     // END GAME
1465     //
1466     case ScanCode::sc_f7:
1467 // BBi
1468 #if 0
1469         VW_ScreenToScreen(PAGE1START, ::bufferofs, 320, 160);
1470 #endif
1471 
1472         CA_CacheGrChunk(STARTFONT + 1);
1473 
1474         WindowH = 160;
1475         if (Confirm(ENDGAMESTR)) {
1476             playstate = ex_died;
1477             pickquick = gamestate.lives = 0;
1478         }
1479 
1480         WindowH = 200;
1481         fontnumber = 4;
1482         return true;
1483 
1484     // QUICKSAVE
1485     //
1486     case ScanCode::sc_f8:
1487         if (SaveGamesAvail[static_cast<int>(LSItems.curpos)] && pickquick) {
1488             char string[100] = "Quick Save will overwrite:\n\"";
1489 
1490             CA_CacheGrChunk(STARTFONT + 1);
1491 
1492             strcat(string, SaveGameNames[static_cast<int>(LSItems.curpos)]);
1493             strcat(string, "\"?");
1494 
1495 // BBi
1496 #if 0
1497             VW_ScreenToScreen(PAGE1START, ::bufferofs, 320, 160);
1498 #endif
1499 
1500             if (Confirm(string)) {
1501                 CA_CacheGrChunk(STARTFONT + 1);
1502                 CP_SaveGame(1);
1503                 fontnumber = 4;
1504             } else {
1505                 refresh_screen = false;
1506             }
1507         } else {
1508             CA_CacheGrChunk(STARTFONT + 1);
1509 
1510             VW_FadeOut();
1511 
1512             ::StartCPMusic(MENUSONG);
1513 
1514             pickquick = CP_SaveGame(0);
1515 
1516             lasttimecount = TimeCount;
1517             ::in_clear_mouse_deltas();
1518         }
1519 
1520         return true;
1521 
1522     // QUICKLOAD
1523     //
1524     case ScanCode::sc_f9:
1525         if (SaveGamesAvail[static_cast<int>(LSItems.curpos)] && pickquick) {
1526             char string[100] = "Quick Load:\n\"";
1527 
1528             CA_CacheGrChunk(STARTFONT + 1);
1529 
1530             strcat(string, SaveGameNames[static_cast<int>(LSItems.curpos)]);
1531             strcat(string, "\"?");
1532 
1533 // BBi
1534 #if 0
1535             VW_ScreenToScreen(PAGE1START, ::bufferofs, 320, 160);
1536 #endif
1537 
1538             if (Confirm(string)) {
1539                 CP_LoadGame(1);
1540             } else {
1541                 refresh_screen = false;
1542                 return true;
1543             }
1544 
1545             fontnumber = 4;
1546         } else {
1547             CA_CacheGrChunk(STARTFONT + 1);
1548 
1549             VW_FadeOut();
1550 
1551             ::StartCPMusic(MENUSONG);
1552 
1553             pickquick = CP_LoadGame(0);
1554 
1555             lasttimecount = TimeCount;
1556             ::in_clear_mouse_deltas();
1557         }
1558 
1559         if (pickquick) {
1560             refresh_screen = false;
1561         }
1562         return true;
1563 
1564     // QUIT
1565     //
1566     case ScanCode::sc_f10:
1567         CA_CacheGrChunk(STARTFONT + 1);
1568 
1569 // BBi
1570 #if 0
1571         VW_ScreenToScreen(PAGE1START, ::bufferofs, 320, 160);
1572 #endif
1573 
1574         WindowX = WindowY = 0;
1575         WindowW = 320;
1576         WindowH = 160;
1577         if (Confirm(QuitToDosStr)) {
1578             ExitGame();
1579         }
1580 
1581         refresh_screen = false;
1582         WindowH = 200;
1583         fontnumber = 4;
1584 
1585         return true;
1586 
1587     default:
1588         return false;
1589     }
1590 }
1591 
CP_EndGame()1592 int16_t CP_EndGame()
1593 {
1594     if (!Confirm(ENDGAMESTR)) {
1595         return 0;
1596     }
1597 
1598     pickquick = gamestate.lives = 0;
1599     playstate = ex_died;
1600     InstantQuit = 1;
1601 
1602     return 1;
1603 }
1604 
CP_ViewScores(int16_t)1605 void CP_ViewScores(
1606     int16_t)
1607 {
1608     fontnumber = 4;
1609 
1610     ::StartCPMusic(static_cast<int16_t>(ROSTER_MUS));
1611 
1612     DrawHighScores();
1613     VW_UpdateScreen();
1614     MenuFadeIn();
1615     fontnumber = 1;
1616 
1617     IN_Ack();
1618 
1619     ::StartCPMusic(MENUSONG);
1620 
1621     ::MenuFadeOut();
1622 }
1623 
CP_NewGame(int16_t)1624 void CP_NewGame(
1625     int16_t)
1626 {
1627     int16_t which, episode = 0;
1628 
1629     DrawMenuTitle("Difficulty Level");
1630     DrawInstructions(IT_STANDARD);
1631 
1632 
1633 firstpart:
1634 
1635     if (!::is_ps()) {
1636         DrawNewEpisode();
1637         do {
1638             which = HandleMenu(&NewEitems, &NewEmenu[0], DrawEpisodePic);
1639             switch (which) {
1640             case -1:
1641                 ::MenuFadeOut();
1642                 return;
1643 
1644             default:
1645                 if (!EpisodeSelect[which]) {
1646                     ::sd_play_player_sound(NOWAYSND, bstone::AC_ITEM);
1647                     CacheMessage(READTHIS_TEXT);
1648                     IN_ClearKeysDown();
1649                     IN_Ack();
1650                     VL_Bar(35, 69, 250, 62, ::menu_background_color);
1651                     DrawNewEpisode();
1652                     which = 0;
1653                 } else {
1654                     episode = which;
1655                     which = 1;
1656                 }
1657                 break;
1658             }
1659 
1660         } while (!which);
1661 
1662         ShootSnd();
1663     } else {
1664         episode = 0;
1665     }
1666 
1667     //
1668     // ALREADY IN A GAME?
1669     //
1670     if (!::is_ps() && ingame) {
1671         if (!Confirm(CURGAME)) {
1672             ::MenuFadeOut();
1673             return;
1674         }
1675     }
1676 
1677 secondpart:
1678 
1679     ::MenuFadeOut();
1680     if (ingame) {
1681         CA_CacheScreen(BACKGROUND_SCREENPIC);
1682     }
1683     DrawNewGame();
1684     which = HandleMenu(&NewItems, &NewMenu[0], DrawNewGameDiff);
1685 
1686     if (which < 0) {
1687         ::MenuFadeOut();
1688 
1689         if (!::is_ps()) {
1690             goto firstpart;
1691         } else {
1692             return;
1693         }
1694     }
1695 
1696     ShootSnd();
1697     ::MenuFadeOut();
1698     ControlPanelFree();
1699 
1700     if (Breifing(BT_INTRO, episode)) {
1701         CA_CacheScreen(BACKGROUND_SCREENPIC);
1702         ControlPanelAlloc();
1703         goto secondpart;
1704     }
1705 
1706     StartGame = 1;
1707     NewGame(which, episode);
1708 
1709     //
1710     // CHANGE "READ THIS!" TO NORMAL COLOR
1711     //
1712     MainMenu[MM_READ_THIS].active = AT_ENABLED;
1713 }
1714 
DrawMenuTitle(const char * title)1715 void DrawMenuTitle(
1716     const char* title)
1717 {
1718 
1719     fontnumber = 3;
1720     CA_CacheGrChunk(STARTFONT + 3);
1721 
1722     PrintX = WindowX = 32;
1723     PrintY = WindowY = 32;
1724     WindowW = 244;
1725     WindowH = 20;
1726 
1727     SETFONTCOLOR(TERM_SHADOW_COLOR, TERM_BACK_COLOR);
1728     US_PrintCentered(title);
1729 
1730     WindowX = 32 - 1;
1731     WindowY = 32 - 1;
1732 
1733     SETFONTCOLOR(ENABLED_TEXT_COLOR, TERM_BACK_COLOR);
1734     US_PrintCentered(title);
1735 
1736     FREEFONT(STARTFONT + 3);
1737 
1738 }
1739 
1740 const int16_t INSTRUCTIONS_Y_POS = 154 + 10;
1741 
1742 // ---------------------------------------------------------------------------
1743 // DrawInstructions() - Draws instructions centered at the bottom of
1744 //      the view screen.
1745 //
1746 // NOTES: Orginal font number or font color is not maintained.
1747 // ---------------------------------------------------------------------------
DrawInstructions(inst_type Type)1748 void DrawInstructions(
1749     inst_type Type)
1750 {
1751     const char* instr[MAX_INSTRUCTIONS] = {
1752         "UP/DN SELECTS - ENTER CHOOSES - ESC EXITS",
1753         "PRESS ANY KEY TO CONTINUE",
1754         "ENTER YOUR NAME AND PRESS ENTER",
1755         "RT/LF ARROW SELECTS - ENTER CHOOSES",
1756 
1757         // BBi
1758         "UP/DN SELECTS - LF/RT CHANGES - ESC EXITS",
1759         "ARROWS SELECTS - ENTER CHOOSES - DEL REMOVES",
1760         "ESC EXITS"
1761     };
1762 
1763     fontnumber = 2;
1764 
1765     WindowX = 48;
1766     WindowY = INSTRUCTIONS_Y_POS;
1767     WindowW = 236;
1768     WindowH = 8;
1769 
1770     VWB_Bar(WindowX, WindowY - 1, WindowW, WindowH, ::menu_background_color);
1771 
1772     SETFONTCOLOR(TERM_SHADOW_COLOR, TERM_BACK_COLOR);
1773     US_PrintCentered(instr[Type]);
1774 
1775     WindowX--;
1776     WindowY--;
1777 
1778     SETFONTCOLOR(INSTRUCTIONS_TEXT_COLOR, TERM_BACK_COLOR);
1779     US_PrintCentered(instr[Type]);
1780 }
1781 
DrawNewEpisode()1782 void DrawNewEpisode()
1783 {
1784     ClearMScreen();
1785 
1786     DrawMenuTitle("CHOOSE A MISSION");
1787     DrawInstructions(IT_STANDARD);
1788 
1789     PrintY = 51;
1790     WindowX = 58;
1791 
1792     fontnumber = 2; // six point font
1793     DrawMenu(&NewEitems, &NewEmenu[0]);
1794 
1795     DrawEpisodePic(NewEitems.curpos);
1796 
1797     VW_UpdateScreen();
1798     MenuFadeIn();
1799     WaitKeyUp();
1800 
1801 }
1802 
DrawNewGame()1803 void DrawNewGame()
1804 {
1805     ClearMScreen();
1806     DrawMenuTitle("DIFFICULTY LEVEL");
1807     DrawInstructions(IT_STANDARD);
1808 
1809     fontnumber = 2; // six point font
1810     DrawMenu(&NewItems, &NewMenu[0]);
1811 
1812     DrawNewGameDiff(NewItems.curpos);
1813 
1814     px = 48;
1815     py = INSTRUCTIONS_Y_POS - 24;
1816     ShPrint("        HIGHER DIFFICULTY LEVELS CONTAIN", TERM_SHADOW_COLOR, false);
1817 
1818     px = 48;
1819     py += 6;
1820     ShPrint("            MORE, STRONGER ENEMIES", TERM_SHADOW_COLOR, false);
1821 
1822 
1823     VW_UpdateScreen();
1824 
1825     MenuFadeIn();
1826     WaitKeyUp();
1827 }
1828 
DrawNewGameDiff(int16_t w)1829 void DrawNewGameDiff(
1830     int16_t w)
1831 {
1832     VWB_DrawPic(192, 77, w + C_BABYMODEPIC);
1833 }
1834 
DrawEpisodePic(int16_t w)1835 void DrawEpisodePic(
1836     int16_t w)
1837 {
1838     VWB_DrawPic(176, 72, w + C_EPISODE1PIC);
1839 }
1840 
CP_GameOptions(int16_t)1841 void CP_GameOptions(
1842     int16_t)
1843 {
1844     int16_t which;
1845 
1846     CA_CacheScreen(BACKGROUND_SCREENPIC);
1847     DrawGopMenu();
1848     MenuFadeIn();
1849     WaitKeyUp();
1850 
1851     do {
1852         which = HandleMenu(&GopItems, &GopMenu[0], nullptr);
1853 
1854         if (which != -1) {
1855             DrawGopMenu();
1856             MenuFadeIn();
1857         }
1858 
1859     } while (which >= 0);
1860 
1861     ::MenuFadeOut();
1862 }
1863 
DrawGopMenu()1864 void DrawGopMenu()
1865 {
1866     CA_CacheScreen(BACKGROUND_SCREENPIC);
1867 
1868     ClearMScreen();
1869     DrawMenuTitle("GAME OPTIONS");
1870     DrawInstructions(IT_STANDARD);
1871 
1872     fontnumber = 4; // COAL
1873 
1874     DrawMenu(&GopItems, &GopMenu[0]);
1875 
1876     VW_UpdateScreen();
1877 }
1878 
ChangeSwaps()1879 void ChangeSwaps()
1880 {
1881     WindowX = WindowY = 0;
1882     WindowW = 320;
1883     WindowH = 200;
1884     Message(Computing);
1885 
1886     PM_Shutdown();
1887     PM_Startup();
1888     ClearMemory();
1889     ControlPanelAlloc();
1890 
1891     IN_UserInput(50);
1892     IN_ClearKeysDown();
1893 
1894 }
1895 
CP_Switches(int16_t)1896 void CP_Switches(
1897     int16_t)
1898 {
1899     int16_t which;
1900 
1901     CA_CacheScreen(BACKGROUND_SCREENPIC);
1902     DrawSwitchMenu();
1903     MenuFadeIn();
1904     WaitKeyUp();
1905 
1906     do {
1907         which = HandleMenu(&SwitchItems, &SwitchMenu[0], DrawAllSwitchLights);
1908 
1909         switch (which) {
1910         case SW_LIGHTING:
1911             gamestate.flags ^= GS_LIGHTING;
1912             ShootSnd();
1913             DrawSwitchMenu();
1914             break;
1915 
1916         case SW_REBA_ATTACK_INFO:
1917             gamestate.flags ^= GS_ATTACK_INFOAREA;
1918             ShootSnd();
1919             DrawSwitchMenu();
1920             break;
1921 
1922         case SW_CEILING:
1923             gamestate.flags ^= GS_DRAW_CEILING;
1924             ShootSnd();
1925             DrawSwitchMenu();
1926             break;
1927 
1928         case SW_FLOORS:
1929             gamestate.flags ^= GS_DRAW_FLOOR;
1930             ShootSnd();
1931             DrawSwitchMenu();
1932             break;
1933 
1934         // BBi
1935         case SW_NO_WALL_HIT_SOUND:
1936             g_no_wall_hit_sound = !g_no_wall_hit_sound;
1937             ShootSnd();
1938             DrawSwitchMenu();
1939             break;
1940 
1941         case SW_MODERN_CONTROLS:
1942             in_use_modern_bindings = !in_use_modern_bindings;
1943             ShootSnd();
1944             DrawSwitchMenu();
1945             break;
1946 
1947         case SW_ALWAYS_RUN:
1948             g_always_run = !g_always_run;
1949             ShootSnd();
1950             DrawSwitchMenu();
1951             break;
1952 
1953         case SW_HEART_BEAT_SOUND:
1954             g_heart_beat_sound = !g_heart_beat_sound;
1955             ShootSnd();
1956             DrawSwitchMenu();
1957             break;
1958 
1959         case SW_ROTATED_AUTOMAP:
1960             g_rotated_automap = !g_rotated_automap;
1961             ShootSnd();
1962             DrawSwitchMenu();
1963             break;
1964         }
1965     } while (which >= 0);
1966 
1967     ::MenuFadeOut();
1968 }
1969 
DrawSwitchMenu()1970 void DrawSwitchMenu()
1971 {
1972     CA_CacheScreen(BACKGROUND_SCREENPIC);
1973 
1974     ClearMScreen();
1975     DrawMenuTitle("GAME SWITCHES");
1976     DrawInstructions(IT_STANDARD);
1977 
1978     fontnumber = 2;
1979 
1980     DrawMenu(&SwitchItems, &SwitchMenu[0]);
1981     DrawAllSwitchLights(SwitchItems.curpos);
1982 
1983     VW_UpdateScreen();
1984 }
1985 
DrawAllSwitchLights(int16_t which)1986 void DrawAllSwitchLights(
1987     int16_t which)
1988 {
1989     int16_t i;
1990     uint16_t Shape;
1991 
1992     for (i = 0; i < SwitchItems.amount; i++) {
1993         if (SwitchMenu[i].string[0]) {
1994             Shape = C_NOTSELECTEDPIC;
1995 
1996             //
1997             // DRAW SELECTED/NOT SELECTED GRAPHIC BUTTONS
1998             //
1999 
2000             if (SwitchItems.cursor.on) {
2001                 if (i == which) { // Is the cursor sitting on this pic?
2002                     Shape += 2;
2003                 }
2004             }
2005 
2006             switch (i) {
2007             case SW_LIGHTING:
2008                 if (gamestate.flags & GS_LIGHTING) {
2009                     Shape++;
2010                 }
2011                 break;
2012 
2013             case SW_REBA_ATTACK_INFO:
2014                 if (gamestate.flags & GS_ATTACK_INFOAREA) {
2015                     Shape++;
2016                 }
2017                 break;
2018 
2019             case SW_CEILING:
2020                 if (gamestate.flags & GS_DRAW_CEILING) {
2021                     Shape++;
2022                 }
2023                 break;
2024 
2025             case SW_FLOORS:
2026                 if (gamestate.flags & GS_DRAW_FLOOR) {
2027                     Shape++;
2028                 }
2029                 break;
2030 
2031             // BBi
2032             case SW_NO_WALL_HIT_SOUND:
2033                 if (g_no_wall_hit_sound) {
2034                     ++Shape;
2035                 }
2036                 break;
2037 
2038             case SW_MODERN_CONTROLS:
2039                 if (in_use_modern_bindings) {
2040                     ++Shape;
2041                 }
2042                 break;
2043 
2044             case SW_ALWAYS_RUN:
2045                 if (g_always_run) {
2046                     ++Shape;
2047                 }
2048                 break;
2049 
2050             case SW_HEART_BEAT_SOUND:
2051                 if (g_heart_beat_sound) {
2052                     ++Shape;
2053                 }
2054                 break;
2055 
2056             case SW_ROTATED_AUTOMAP:
2057                 if (g_rotated_automap) {
2058                     ++Shape;
2059                 }
2060                 break;
2061             }
2062 
2063             VWB_DrawPic(SwitchItems.x - 16, SwitchItems.y + i * SwitchItems.y_spacing - 1, Shape);
2064         }
2065     }
2066 
2067     DrawSwitchDescription(which);
2068 
2069 }
2070 
DrawSwitchDescription(int16_t which)2071 void DrawSwitchDescription(
2072     int16_t which)
2073 {
2074     const char* instr[] = {
2075         "TOGGLES LIGHT SOURCING IN HALLWAYS",
2076         "TOGGLES DETAILED ATTACKER INFO",
2077         "TOGGLES CEILING MAPPING",
2078         "TOGGLES FLOOR MAPPING",
2079 
2080         // BBi
2081         "TOGGLES WALL HIT SOUND",
2082         "TOGGLES BETWEEN CLASSIC AND MODERN CONTROLS",
2083         "TOGGLES ALWAYS RUN MODE",
2084         "TOGGLES HEART BEAT SOUND WITH EKG",
2085         "TOGGLES <TAB>/<SHIFT+TAB> FUNCTIONS",
2086     };
2087 
2088     fontnumber = 2;
2089 
2090     WindowX = 48;
2091     WindowY = (::is_ps() ? 134 : 144);
2092     WindowW = 236;
2093     WindowH = 8;
2094 
2095     VWB_Bar(WindowX, WindowY - 1, WindowW, WindowH, ::menu_background_color);
2096 
2097     SETFONTCOLOR(TERM_SHADOW_COLOR, TERM_BACK_COLOR);
2098     US_PrintCentered(instr[which]);
2099 
2100     WindowX--;
2101     WindowY--;
2102 
2103     SETFONTCOLOR(INSTRUCTIONS_TEXT_COLOR, TERM_BACK_COLOR);
2104     US_PrintCentered(instr[which]);
2105 }
2106 
CP_Sound(int16_t)2107 void CP_Sound(
2108     int16_t)
2109 {
2110     int16_t which;
2111 
2112     CA_CacheScreen(BACKGROUND_SCREENPIC);
2113     DrawSoundMenu();
2114     MenuFadeIn();
2115     WaitKeyUp();
2116 
2117     do {
2118         which = HandleMenu(&SndItems, &SndMenu[0], DrawAllSoundLights);
2119         //
2120         // HANDLE MENU CHOICES
2121         //
2122         switch (which) {
2123         //
2124         // SOUND EFFECTS / DIGITIZED SOUND
2125         //
2126         case 0:
2127             if (::sd_is_sound_enabled) {
2128                 ::SD_WaitSoundDone();
2129                 ::SD_EnableSound(false);
2130                 ::DrawSoundMenu();
2131             }
2132             break;
2133 
2134         case 1:
2135             if (!::sd_is_sound_enabled) {
2136                 ::SD_WaitSoundDone();
2137                 ::SD_EnableSound(true);
2138                 ::CA_LoadAllSounds();
2139                 ::DrawSoundMenu();
2140                 ::ShootSnd();
2141             }
2142             break;
2143 
2144         //
2145         // MUSIC
2146         //
2147         case 4:
2148             if (::sd_is_music_enabled) {
2149                 ::SD_EnableMusic(false);
2150                 ::DrawSoundMenu();
2151                 ::ShootSnd();
2152             }
2153             break;
2154 
2155         case 5:
2156             if (!::sd_is_music_enabled) {
2157                 ::SD_EnableMusic(true);
2158                 ::DrawSoundMenu();
2159                 ::ShootSnd();
2160                 ::StartCPMusic(MENUSONG);
2161             }
2162             break;
2163         }
2164     } while (which >= 0);
2165 
2166     ::MenuFadeOut();
2167 }
2168 
DrawSoundMenu()2169 void DrawSoundMenu()
2170 {
2171     //
2172     // DRAW SOUND MENU
2173     //
2174 
2175     ClearMScreen();
2176     DrawMenuTitle("SOUND SETTINGS");
2177     DrawInstructions(IT_STANDARD);
2178 
2179     //
2180     // IF NO ADLIB, NON-CHOOSENESS!
2181     //
2182 
2183     if (!::sd_has_audio) {
2184         ::SndMenu[1].active = AT_DISABLED;
2185         ::SndMenu[5].active = AT_DISABLED;
2186     }
2187 
2188     fontnumber = 4;
2189 
2190     SETFONTCOLOR(DISABLED_TEXT_COLOR, TERM_BACK_COLOR);
2191     ShadowPrint("SOUND EFFECTS", 105, 72);
2192     ShadowPrint("BACKGROUND MUSIC", 105, 100);
2193 
2194     fontnumber = 2;
2195     DrawMenu(&SndItems, &SndMenu[0]);
2196 
2197 
2198     DrawAllSoundLights(SndItems.curpos);
2199 
2200     VW_UpdateScreen();
2201 }
2202 
DrawAllSoundLights(int16_t which)2203 void DrawAllSoundLights(
2204     int16_t which)
2205 {
2206     int16_t i;
2207     uint16_t Shape;
2208 
2209     for (i = 0; i < SndItems.amount; i++) {
2210         if (SndMenu[i].string[0]) {
2211             Shape = C_NOTSELECTEDPIC;
2212 
2213             //
2214             // DRAW SELECTED/NOT SELECTED GRAPHIC BUTTONS
2215             //
2216 
2217             if (SndItems.cursor.on) {
2218                 if (i == which) { // Is the cursor sitting on this pic?
2219                     Shape += 2;
2220                 }
2221             }
2222 
2223             switch (i) {
2224             //
2225             // SOUND EFFECTS / DIGITIZED SOUND
2226             //
2227             case 0:
2228                 if (!::sd_is_sound_enabled) {
2229                     ++Shape;
2230                 }
2231                 break;
2232 
2233             case 1:
2234                 if (::sd_is_sound_enabled) {
2235                     ++Shape;
2236                 }
2237                 break;
2238 
2239             //
2240             // MUSIC
2241             //
2242             case 4:
2243                 if (!::sd_is_music_enabled) {
2244                     ++Shape;
2245                 }
2246                 break;
2247 
2248             case 5:
2249                 if (::sd_is_music_enabled) {
2250                     ++Shape;
2251                 }
2252                 break;
2253             }
2254 
2255             VWB_DrawPic(SndItems.x - 16, SndItems.y + i * SndItems.y_spacing - 1, Shape);
2256         }
2257     }
2258 }
2259 
2260 char LOADSAVE_GAME_MSG[2][25] = { "^ST1^CELoading Game\r^XX",
2261                                   "^ST1^CESaving Game\r^XX" };
2262 
2263 extern int8_t LS_current, LS_total;
2264 
2265 // --------------------------------------------------------------------------
2266 // DrawLSAction() - DRAW LOAD/SAVE IN PROGRESS
2267 // --------------------------------------------------------------------------
DrawLSAction(int16_t which)2268 void DrawLSAction(
2269     int16_t which)
2270 {
2271     int8_t total[] = { 19, 19 };
2272 
2273     VW_FadeOut();
2274     screenfaded = true;
2275     DrawTopInfo(static_cast<sp_type>(sp_loading + which));
2276 
2277     ::VL_Bar(
2278         0,
2279         ::ref_view_top,
2280         ::vga_ref_width,
2281         ::ref_view_height,
2282         BLACK);
2283 
2284     DisplayPrepingMsg(LOADSAVE_GAME_MSG[which]);
2285 
2286     if (which) {
2287         PreloadUpdate(1, 1); // GFX: bar is full when saving...
2288 
2289     }
2290     LS_current = 1;
2291     LS_total = total[which];
2292     WindowY = 181;
2293 }
2294 
CP_LoadGame(int16_t quick)2295 int16_t CP_LoadGame(
2296     int16_t quick)
2297 {
2298     int16_t which;
2299     int16_t exit = 0;
2300 
2301     //
2302     // QUICKLOAD?
2303     //
2304     if (quick) {
2305         which = LSItems.curpos;
2306 
2307         if (SaveGamesAvail[which]) {
2308             auto name = ::get_saved_game_base_name();
2309             name += static_cast<char>('0' + which);
2310 
2311             DrawLSAction(0); // Testing...
2312 
2313             auto name_path = ::get_profile_dir() + name;
2314 
2315             loadedgame = ::LoadTheGame(name_path);
2316 
2317             if (!loadedgame) {
2318                 LS_current = -1; // clean up
2319             }
2320 
2321             return loadedgame;
2322         }
2323     }
2324 
2325 restart:
2326 
2327     DrawLoadSaveScreen(0);
2328 
2329     do {
2330         which = HandleMenu(&LSItems, &LSMenu[0], TrackWhichGame);
2331 
2332         if (which >= 0 && SaveGamesAvail[which]) {
2333             ShootSnd();
2334 
2335             auto name = ::get_saved_game_base_name();
2336             name += static_cast<char>('0' + which);
2337 
2338             auto name_path = ::get_profile_dir() + name;
2339 
2340             DrawLSAction(0);
2341 
2342             if (!::LoadTheGame(name_path)) {
2343                 exit = 0;
2344                 StartGame = 0;
2345                 loadedgame = 0;
2346                 LS_current = -1; // Clean up
2347                 ::playstate = ex_abort;
2348                 goto restart;
2349             }
2350 
2351             loadedgame = true;
2352             StartGame = true;
2353 
2354             ::ShootSnd();
2355 
2356             //
2357             // CHANGE "READ THIS!" TO NORMAL COLOR
2358             //
2359             MainMenu[MM_READ_THIS].active = AT_ENABLED;
2360             exit = 1;
2361             break;
2362         }
2363     } while (which >= 0);
2364 
2365     if (which == -1) {
2366         ::MenuFadeOut();
2367     }
2368 
2369     if (loadedgame) {
2370         refresh_screen = false;
2371     }
2372 
2373     return exit;
2374 }
2375 
2376 
2377 ///////////////////////////////////
2378 //
2379 // HIGHLIGHT CURRENT SELECTED ENTRY
2380 //
TrackWhichGame(int16_t w)2381 void TrackWhichGame(
2382     int16_t w)
2383 {
2384     static int16_t lastgameon = 0;
2385 
2386     PrintLSEntry(lastgameon, ENABLED_TEXT_COLOR);
2387     PrintLSEntry(w, HIGHLIGHT_TEXT_COLOR);
2388 
2389     lastgameon = w;
2390 }
2391 
DrawLoadSaveScreen(int16_t loadsave)2392 void DrawLoadSaveScreen(
2393     int16_t loadsave)
2394 {
2395     int16_t i;
2396 
2397     CA_CacheScreen(BACKGROUND_SCREENPIC);
2398     ClearMScreen();
2399 
2400     fontnumber = 1;
2401 
2402     if (!loadsave) {
2403         DrawMenuTitle("Load Mission");
2404     } else {
2405         DrawMenuTitle("Save Mission");
2406     }
2407 
2408     DrawInstructions(IT_STANDARD);
2409 
2410     for (i = 0; i < 10; i++) {
2411         PrintLSEntry(i, ENABLED_TEXT_COLOR);
2412     }
2413 
2414     fontnumber = 4;
2415     DrawMenu(&LSItems, &LSMenu[0]);
2416 
2417     VW_UpdateScreen();
2418     MenuFadeIn();
2419     WaitKeyUp();
2420 }
2421 
2422 // --------------------------------------------------------------------------
2423 // PRINT LOAD/SAVE GAME ENTRY W/BOX OUTLINE
2424 // --------------------------------------------------------------------------
PrintLSEntry(int16_t w,int16_t color)2425 void PrintLSEntry(
2426     int16_t w,
2427     int16_t color)
2428 {
2429     SETFONTCOLOR(color, BKGDCOLOR);
2430     DrawOutline(LSM_X + LSItems.indent, LSM_Y + w * LSItems.y_spacing - 2, LSM_W - LSItems.indent, 8, color, color);
2431 
2432     fontnumber = 2;
2433 
2434     PrintX = LSM_X + LSItems.indent + 2;
2435     PrintY = LSM_Y + w * LSItems.y_spacing;
2436 
2437     if (SaveGamesAvail[w]) {
2438         US_Print(SaveGameNames[w]);
2439     } else {
2440         US_Print("       ----- EMPTY -----");
2441     }
2442 
2443     fontnumber = 1;
2444 }
2445 
CP_SaveGame(int16_t quick)2446 int16_t CP_SaveGame(
2447     int16_t quick)
2448 {
2449     int16_t which, exit = 0;
2450     char input[GAME_DESCRIPTION_LEN + 1];
2451     bool temp_caps = allcaps;
2452     US_CursorStruct TermCursor = { '@', 0, HIGHLIGHT_TEXT_COLOR, 2 };
2453 
2454     allcaps = true;
2455     use_custom_cursor = true;
2456     US_CustomCursor = TermCursor;
2457 
2458     //
2459     // QUICKSAVE?
2460     //
2461     if (quick) {
2462         which = LSItems.curpos;
2463 
2464         if (SaveGamesAvail[which]) {
2465             DrawLSAction(1); // Testing...
2466             auto name = ::get_saved_game_base_name();
2467             name += static_cast<char>('0' + which);
2468 
2469             auto name_path = ::get_profile_dir() + name;
2470 
2471             ::SaveTheGame(name_path, &SaveGameNames[which][0]);
2472 
2473             return 1;
2474         }
2475     }
2476 
2477     DrawLoadSaveScreen(1);
2478 
2479     do {
2480         which = HandleMenu(&LSItems, &LSMenu[0], TrackWhichGame);
2481         if (which >= 0) {
2482             //
2483             // OVERWRITE EXISTING SAVEGAME?
2484             //
2485             if (SaveGamesAvail[which]) {
2486                 if (!Confirm(GAMESVD)) {
2487                     DrawLoadSaveScreen(1);
2488                     continue;
2489                 } else {
2490                     DrawLoadSaveScreen(1);
2491                     PrintLSEntry(which, HIGHLIGHT_TEXT_COLOR);
2492                     VW_UpdateScreen();
2493                 }
2494             }
2495 
2496             ShootSnd();
2497 
2498             strcpy(input, &SaveGameNames[which][0]);
2499 
2500             auto name = ::get_saved_game_base_name();
2501             name += static_cast<char>('0' + which);
2502 
2503             fontnumber = 2;
2504             VWB_Bar(LSM_X + LSItems.indent + 1, LSM_Y + which * LSItems.y_spacing - 1, LSM_W - LSItems.indent - 1, 7, HIGHLIGHT_BOX_COLOR);
2505             SETFONTCOLOR(HIGHLIGHT_TEXT_COLOR, HIGHLIGHT_BOX_COLOR);
2506             VW_UpdateScreen();
2507 
2508 
2509             if (US_LineInput(LSM_X + LSItems.indent + 2, LSM_Y + which * LSItems.y_spacing, input, input, true, GAME_DESCRIPTION_LEN, LSM_W - LSItems.indent - 10)) {
2510                 SaveGamesAvail[which] = 1;
2511                 strcpy(&SaveGameNames[which][0], input);
2512 
2513                 DrawLSAction(1);
2514 
2515                 auto name_path = ::get_profile_dir() + name;
2516                 ::SaveTheGame(name_path, input);
2517 
2518                 ShootSnd();
2519                 exit = 1;
2520             } else {
2521                 VWB_Bar(
2522                     LSM_X + LSItems.indent + 1,
2523                     LSM_Y + which * LSItems.y_spacing - 1,
2524                     LSM_W - LSItems.indent - 1,
2525                     7,
2526                     ::menu_background_color);
2527 
2528                 PrintLSEntry(which, HIGHLIGHT_TEXT_COLOR);
2529                 VW_UpdateScreen();
2530                 ::sd_play_player_sound(ESCPRESSEDSND, bstone::AC_ITEM);
2531                 continue;
2532             }
2533 
2534             fontnumber = 1;
2535             break;
2536         }
2537 
2538     } while (which >= 0);
2539 
2540     ::MenuFadeOut();
2541     use_custom_cursor = false;
2542     allcaps = temp_caps;
2543     return exit;
2544 }
2545 
CP_ExitOptions(int16_t)2546 void CP_ExitOptions(
2547     int16_t)
2548 {
2549     StartGame = 1;
2550 }
2551 
CP_Control(int16_t)2552 void CP_Control(
2553     int16_t)
2554 {
2555     enum {MOUSEENABLE, JOYENABLE, USEPORT2, PADENABLE, CALIBRATEJOY, MOUSESENS, CUSTOMIZE};
2556 
2557     int16_t which;
2558 
2559     CA_CacheScreen(BACKGROUND_SCREENPIC);
2560 
2561     DrawCtlScreen();
2562     MenuFadeIn();
2563     WaitKeyUp();
2564 
2565     do {
2566         which = HandleMenu(&CtlItems, &CtlMenu[0], nullptr);
2567         switch (which) {
2568         case MOUSEENABLE:
2569             ::mouseenabled = !::mouseenabled;
2570 
2571             DrawCtlScreen();
2572             CusItems.curpos = -1;
2573             ShootSnd();
2574             break;
2575 
2576         case JOYENABLE:
2577             ::joystickenabled = !::joystickenabled;
2578             if (joystickenabled) {
2579                 CalibrateJoystick();
2580             }
2581             DrawCtlScreen();
2582             CusItems.curpos = -1;
2583             ShootSnd();
2584             break;
2585 
2586         case USEPORT2:
2587             joystickport ^= 1;
2588             DrawCtlScreen();
2589             ShootSnd();
2590             break;
2591 
2592         case PADENABLE:
2593             ::joypadenabled = !::joypadenabled;
2594             DrawCtlScreen();
2595             ShootSnd();
2596             break;
2597 
2598         case CALIBRATEJOY:
2599             CalibrateJoystick();
2600             DrawCtlScreen();
2601             break;
2602 
2603 
2604         case MOUSESENS:
2605         case CUSTOMIZE:
2606             DrawCtlScreen();
2607             MenuFadeIn();
2608             WaitKeyUp();
2609             break;
2610         }
2611     } while (which >= 0);
2612 
2613     ::MenuFadeOut();
2614 }
2615 
DrawMousePos()2616 void DrawMousePos()
2617 {
2618     const int thumb_width = 16;
2619     const int track_width = 160;
2620     const int slide_width = track_width - thumb_width;
2621     const int max_mouse_delta = ::max_mouse_sensitivity - ::min_mouse_sensitivity;
2622 
2623     ::VWB_Bar(
2624         74,
2625         92,
2626         track_width,
2627         8,
2628         HIGHLIGHT_BOX_COLOR);
2629 
2630     ::DrawOutline(
2631         73,
2632         91,
2633         track_width + 1,
2634         9,
2635         ENABLED_TEXT_COLOR,
2636         ENABLED_TEXT_COLOR);
2637 
2638     ::VWB_Bar(
2639         74 + ((slide_width * ::mouseadjustment) / max_mouse_delta),
2640         92,
2641         thumb_width,
2642         8,
2643         HIGHLIGHT_TEXT_COLOR);
2644 }
2645 
DrawMouseSens()2646 void DrawMouseSens()
2647 {
2648     ClearMScreen();
2649     DrawMenuTitle("MOUSE SENSITIVITY");
2650     DrawInstructions(IT_MOUSE_SEN);
2651 
2652     fontnumber = 4;
2653 
2654     SETFONTCOLOR(HIGHLIGHT_TEXT_COLOR, TERM_BACK_COLOR);
2655     PrintX = 36;
2656     PrintY = 91;
2657     US_Print("SLOW");
2658     PrintX = 242;
2659     US_Print("FAST");
2660 
2661     DrawMousePos();
2662 
2663     VW_UpdateScreen();
2664     MenuFadeIn();
2665 }
2666 
CalibrateJoystick()2667 void CalibrateJoystick()
2668 {
2669     uint16_t minx, maxx, miny, maxy;
2670 
2671     CacheMessage(CALJOY1_TEXT);
2672     VW_UpdateScreen();
2673 
2674     while (IN_GetJoyButtonsDB(joystickport)) {
2675     }
2676     while ((LastScan != ScanCode::sc_escape) && !IN_GetJoyButtonsDB(joystickport)) {
2677     }
2678     if (LastScan == ScanCode::sc_escape) {
2679         return;
2680     }
2681 
2682     IN_GetJoyAbs(joystickport, &minx, &miny);
2683     while (IN_GetJoyButtonsDB(joystickport)) {
2684     }
2685 
2686     CacheMessage(CALJOY2_TEXT);
2687     VW_UpdateScreen();
2688 
2689     while ((LastScan != ScanCode::sc_escape) && !IN_GetJoyButtonsDB(joystickport)) {
2690     }
2691     if (LastScan == ScanCode::sc_escape) {
2692         return;
2693     }
2694 
2695     IN_GetJoyAbs(joystickport, &maxx, &maxy);
2696     if ((minx == maxx) || (miny == maxy)) {
2697         return;
2698     }
2699 
2700     IN_SetupJoy(joystickport, minx, maxx, miny, maxy);
2701     while (IN_GetJoyButtonsDB(joystickport)) {
2702     }
2703 
2704     IN_ClearKeysDown();
2705     JoystickCalibrated = true;
2706 }
2707 
MouseSensitivity(int16_t)2708 void MouseSensitivity(
2709     int16_t)
2710 {
2711     ControlInfo ci;
2712     int16_t exit = 0;
2713 
2714     const auto oldMA = ::mouseadjustment;
2715 
2716     DrawMouseSens();
2717     do {
2718         ReadAnyControl(&ci);
2719         switch (ci.dir) {
2720         case dir_North:
2721         case dir_West:
2722             if (::mouseadjustment > 0) {
2723                 ::mouseadjustment -= 1;
2724                 DrawMousePos();
2725                 VW_UpdateScreen();
2726                 ::sd_play_player_sound(MOVEGUN1SND, bstone::AC_ITEM);
2727 
2728                 while (Keyboard[ScanCode::sc_left_arrow]) {
2729                     ::in_handle_events();
2730                 }
2731 
2732                 WaitKeyUp();
2733             }
2734             break;
2735 
2736         case dir_South:
2737         case dir_East:
2738             if (::mouseadjustment < ::max_mouse_sensitivity)
2739             {
2740                 ::mouseadjustment += 1;
2741                 DrawMousePos();
2742                 VW_UpdateScreen();
2743                 ::sd_play_player_sound(MOVEGUN1SND, bstone::AC_ITEM);
2744 
2745                 while (Keyboard[ScanCode::sc_right_arrow]) {
2746                     ::in_handle_events();
2747                 }
2748 
2749                 WaitKeyUp();
2750             }
2751             break;
2752 
2753         default:
2754             break;
2755         }
2756 
2757         if (ci.button0 || Keyboard[ScanCode::sc_space] || Keyboard[ScanCode::sc_return]) {
2758             exit = 1;
2759         } else if (ci.button1 || Keyboard[ScanCode::sc_escape]) {
2760             exit = 2;
2761         }
2762 
2763     } while (!exit);
2764 
2765     if (exit == 2) {
2766         ::mouseadjustment = oldMA;
2767         ::sd_play_player_sound(ESCPRESSEDSND, bstone::AC_ITEM);
2768     } else {
2769         ::sd_play_player_sound(SHOOTSND, bstone::AC_ITEM);
2770     }
2771 
2772     WaitKeyUp();
2773     ::MenuFadeOut();
2774 }
2775 
2776 // --------------------------------------------------------------------------
2777 // DrawCtlScreen() - DRAW CONTROL MENU SCREEN
2778 // --------------------------------------------------------------------------
DrawCtlScreen()2779 void DrawCtlScreen()
2780 {
2781     const int16_t Y_CTL_PIC_OFS = 3;
2782 
2783     int16_t i;
2784     int16_t x;
2785     int16_t y;
2786 
2787     ClearMScreen();
2788     DrawMenuTitle("CONTROL");
2789     DrawInstructions(IT_STANDARD);
2790 
2791     WindowX = 0;
2792     WindowW = 320;
2793     SETFONTCOLOR(TEXTCOLOR, BKGDCOLOR);
2794 
2795     if (JoysPresent[0]) {
2796         CtlMenu[1].active = AT_ENABLED;
2797         CtlMenu[2].active = AT_ENABLED;
2798         CtlMenu[3].active = AT_ENABLED;
2799         CtlMenu[4].active = AT_ENABLED;
2800     }
2801 
2802     CtlMenu[2].active = CtlMenu[3].active = CtlMenu[4].active = static_cast<activetypes>(joystickenabled);
2803 
2804     if (MousePresent) {
2805         CtlMenu[0].active = AT_ENABLED;
2806         CtlMenu[5].active = AT_ENABLED;
2807     }
2808 
2809     CtlMenu[5].active = static_cast<activetypes>(mouseenabled);
2810 
2811     fontnumber = 4;
2812     DrawMenu(&CtlItems, &CtlMenu[0]);
2813 
2814     x = CTL_X + CtlItems.indent - 24;
2815     y = CTL_Y + Y_CTL_PIC_OFS;
2816     if (mouseenabled) {
2817         VWB_DrawPic(x, y, C_SELECTEDPIC);
2818     } else {
2819         VWB_DrawPic(x, y, C_NOTSELECTEDPIC);
2820     }
2821 
2822     y = CTL_Y + 9 + Y_CTL_PIC_OFS;
2823     if (joystickenabled) {
2824         VWB_DrawPic(x, y, C_SELECTEDPIC);
2825     } else {
2826         VWB_DrawPic(x, y, C_NOTSELECTEDPIC);
2827     }
2828 
2829     y = CTL_Y + 9 * 2 + Y_CTL_PIC_OFS;
2830     if (joystickport) {
2831         VWB_DrawPic(x, y, C_SELECTEDPIC);
2832     } else {
2833         VWB_DrawPic(x, y, C_NOTSELECTEDPIC);
2834     }
2835 
2836     y = CTL_Y + 9 * 3 + Y_CTL_PIC_OFS;
2837     if (joypadenabled) {
2838         VWB_DrawPic(x, y, C_SELECTEDPIC);
2839     } else {
2840         VWB_DrawPic(x, y, C_NOTSELECTEDPIC);
2841     }
2842 
2843     //
2844     // PICK FIRST AVAILABLE SPOT
2845     //
2846 
2847     if (CtlItems.curpos < 0 || !CtlMenu[static_cast<int>(CtlItems.curpos)].active) {
2848         for (i = 0; i < CtlItems.amount; ++i) {
2849             if (CtlMenu[i].active) {
2850                 CtlItems.curpos = static_cast<int8_t>(i);
2851                 break;
2852             }
2853         }
2854     }
2855 
2856     DrawMenuGun(&CtlItems);
2857     VW_UpdateScreen();
2858 }
2859 
2860 enum ControlButton1 {
2861     FIRE,
2862     STRAFE,
2863     RUN,
2864     OPEN
2865 }; // ControlButton1
2866 
2867 char mbarray[4][3] = { "B0", "B1", "B2", "B3" };
2868 int order[4] = { RUN, OPEN, FIRE, STRAFE, };
2869 
CustomControls(int16_t)2870 void CustomControls(
2871     int16_t)
2872 {
2873     if (in_use_modern_bindings) {
2874         binds_draw_menu();
2875         return;
2876     }
2877 
2878     int16_t which;
2879 
2880     DrawCustomScreen();
2881 
2882     do {
2883         which = HandleMenu(&CusItems, &CusMenu[0], FixupCustom);
2884 
2885         switch (which) {
2886         case 0:
2887             DefineMouseBtns();
2888             DrawCustMouse(1);
2889             break;
2890 
2891         case 2:
2892             DefineJoyBtns();
2893             DrawCustJoy(0);
2894             break;
2895 
2896         case 4:
2897             DefineKeyBtns();
2898             DrawCustKeybd(0);
2899             break;
2900 
2901         case 5:
2902             DefineKeyMove();
2903             DrawCustKeys(0);
2904         }
2905     } while (which >= 0);
2906 
2907 
2908 
2909     ::MenuFadeOut();
2910 }
2911 
DefineMouseBtns()2912 void DefineMouseBtns()
2913 {
2914     CustomCtrls mouseallowed = { 1, 1, 1, 1 };
2915     EnterCtrlData(2, &mouseallowed, DrawCustMouse, PrintCustMouse, MOUSE);
2916 }
2917 
DefineJoyBtns()2918 void DefineJoyBtns()
2919 {
2920     CustomCtrls joyallowed = { 1, 1, 1, 1 };
2921     EnterCtrlData(5, &joyallowed, DrawCustJoy, PrintCustJoy, JOYSTICK);
2922 }
2923 
DefineKeyBtns()2924 void DefineKeyBtns()
2925 {
2926     CustomCtrls keyallowed = { 1, 1, 1, 1 };
2927     EnterCtrlData(8, &keyallowed, DrawCustKeybd, PrintCustKeybd, KEYBOARDBTNS);
2928 }
2929 
DefineKeyMove()2930 void DefineKeyMove()
2931 {
2932     CustomCtrls keyallowed = { 1, 1, 1, 1 };
2933     EnterCtrlData(10, &keyallowed, DrawCustKeys, PrintCustKeys, KEYBOARDMOVE);
2934 }
2935 
TestForValidKey(ScanCode Scan)2936 bool TestForValidKey(
2937     ScanCode Scan)
2938 {
2939     auto found = false;
2940 
2941     auto it = std::find(buttonscan.begin(), buttonscan.end(), Scan);
2942 
2943     if (it == buttonscan.end()) {
2944         it = std::find(dirscan.begin(), dirscan.end(), Scan);
2945 
2946         found = (it != dirscan.end());
2947     }
2948 
2949     if (found) {
2950         *it = ScanCode::sc_none;
2951         ::sd_play_player_sound(SHOOTDOORSND, bstone::AC_ITEM);
2952         ::DrawCustomScreen();
2953     }
2954 
2955     return !found;
2956 }
2957 
2958 
2959 enum ControlButton2 {
2960     FWRD,
2961     RIGHT,
2962     BKWD,
2963     LEFT
2964 }; // ControlButton2
2965 
2966 int16_t moveorder[4] = { LEFT, RIGHT, FWRD, BKWD };
2967 
2968 // --------------------------------------------------------------------------
2969 // EnterCtrlData() - ENTER CONTROL DATA FOR ANY TYPE OF CONTROL
2970 // --------------------------------------------------------------------------
EnterCtrlData(int16_t index,CustomCtrls * cust,void (* DrawRtn)(int16_t),void (* PrintRtn)(int16_t),int16_t type)2971 void EnterCtrlData(
2972     int16_t index,
2973     CustomCtrls* cust,
2974     void (* DrawRtn)(int16_t),
2975     void (* PrintRtn)(int16_t),
2976     int16_t type)
2977 {
2978     int16_t j;
2979     int16_t exit;
2980     int16_t tick;
2981     int16_t redraw;
2982     int16_t which = 0;
2983     int16_t x = 0;
2984     int16_t picked;
2985     ControlInfo ci;
2986     bool clean_display = true;
2987 
2988     ShootSnd();
2989     PrintY = CST_Y + 13 * index;
2990     IN_ClearKeysDown();
2991     exit = 0;
2992     redraw = 1;
2993 
2994     CA_CacheGrChunk(STARTFONT + fontnumber);
2995 
2996     //
2997     // FIND FIRST SPOT IN ALLOWED ARRAY
2998     //
2999     for (j = 0; j < 4; j++) {
3000         if (cust->allowed[j]) {
3001             which = j;
3002             break;
3003         }
3004     }
3005 
3006     do {
3007         if (redraw) {
3008             x = CST_START + CST_SPC * which;
3009             DrawRtn(1);
3010 
3011             VWB_Bar(x - 1, PrintY - 1, CST_SPC, 7, HIGHLIGHT_BOX_COLOR);
3012             SETFONTCOLOR(HIGHLIGHT_TEXT_COLOR, HIGHLIGHT_BOX_COLOR);
3013             PrintRtn(which);
3014             PrintX = x;
3015             SETFONTCOLOR(HIGHLIGHT_TEXT_COLOR, TERM_BACK_COLOR);
3016             VW_UpdateScreen();
3017             WaitKeyUp();
3018             redraw = 0;
3019         }
3020 
3021         ReadAnyControl(&ci);
3022 
3023         if (type == MOUSE || type == JOYSTICK) {
3024             if (IN_KeyDown(ScanCode::sc_return) || IN_KeyDown(ScanCode::sc_control) || IN_KeyDown(ScanCode::sc_alt)) {
3025                 IN_ClearKeysDown();
3026                 ci.button0 = ci.button1 = false;
3027             }
3028         }
3029 
3030         //
3031         // CHANGE BUTTON VALUE?
3032         //
3033 
3034         if ((ci.button0 | ci.button1 | ci.button2 | ci.button3) ||
3035             ((type == KEYBOARDBTNS || type == KEYBOARDMOVE) && LastScan == ScanCode::sc_return))
3036         {
3037             tick = 0;
3038             TimeCount = 0;
3039             picked = 0;
3040             SETFONTCOLOR(HIGHLIGHT_TEXT_COLOR, HIGHLIGHT_BOX_COLOR);
3041 
3042             do {
3043                 int16_t button, result = 0;
3044 
3045                 if (type == KEYBOARDBTNS || type == KEYBOARDMOVE) {
3046                     IN_ClearKeysDown();
3047                 }
3048 
3049                 // BBi
3050                 ::in_handle_events();
3051 
3052                 //
3053                 // FLASH CURSOR
3054                 //
3055 
3056                 if (TimeCount > 10) {
3057                     switch (tick) {
3058                     case 0:
3059                         VWB_Bar(x - 1, PrintY - 1, CST_SPC, 7, HIGHLIGHT_BOX_COLOR);
3060                         break;
3061 
3062                     case 1:
3063                         PrintX = x;
3064                         US_Print("?");
3065 
3066                         ::sd_play_player_sound(
3067                             HITWALLSND, bstone::AC_ITEM);
3068                     }
3069 
3070                     tick ^= 1;
3071                     TimeCount = 0;
3072                     VW_UpdateScreen();
3073                 }
3074 
3075                 //
3076                 // WHICH TYPE OF INPUT DO WE PROCESS?
3077                 //
3078 
3079                 switch (type) {
3080                 case MOUSE:
3081                     button = ::IN_MouseButtons();
3082 
3083                     switch (button) {
3084                     case 1: result = 1;
3085                         break;
3086                     case 2: result = 2;
3087                         break;
3088                     case 4: result = 3;
3089                         break;
3090                     }
3091 
3092                     if (result) {
3093                         int16_t z;
3094 
3095                         for (z = 0; z < 4; z++) {
3096                             if (order[which] == buttonmouse[z]) {
3097                                 buttonmouse[z] = bt_nobutton;
3098                                 break;
3099                             }
3100                         }
3101 
3102                         buttonmouse[result - 1] = static_cast<int16_t>(order[which]);
3103                         picked = 1;
3104 
3105                         ::sd_play_player_sound(
3106                             SHOOTDOORSND, bstone::AC_ITEM);
3107 
3108                         clean_display = false;
3109                     }
3110                     break;
3111 
3112                 case JOYSTICK:
3113                     if (ci.button0) {
3114                         result = 1;
3115                     } else if (ci.button1) {
3116                         result = 2;
3117                     } else if (ci.button2) {
3118                         result = 3;
3119                     } else if (ci.button3) {
3120                         result = 4;
3121                     }
3122 
3123                     if (result) {
3124                         int16_t z;
3125 
3126                         for (z = 0; z < 4; z++) {
3127                             if (order[which] == buttonjoy[z]) {
3128                                 buttonjoy[z] = bt_nobutton;
3129                                 break;
3130                             }
3131                         }
3132 
3133                         buttonjoy[result - 1] = static_cast<int16_t>(order[which]);
3134                         picked = 1;
3135 
3136                         ::sd_play_player_sound(SHOOTDOORSND, bstone::AC_ITEM);
3137 
3138                         clean_display = false;
3139                     }
3140                     break;
3141 
3142                 case KEYBOARDBTNS:
3143                     if (LastScan != ScanCode::sc_none) {
3144                         if (LastScan == ScanCode::sc_escape) {
3145                             break;
3146                         }
3147 
3148                         auto it = std::find(
3149                             special_keys.cbegin(),
3150                             special_keys.cend(),
3151                             LastScan);
3152 
3153                         if (it != special_keys.cend()) {
3154                             ::sd_play_player_sound(NOWAYSND, bstone::AC_ITEM);
3155                         } else {
3156                             clean_display = TestForValidKey(LastScan);
3157 
3158                             if (clean_display) {
3159                                 ShootSnd();
3160                             }
3161 
3162                             buttonscan[order[which]] = LastScan;
3163 
3164                             picked = 1;
3165                         }
3166                         IN_ClearKeysDown();
3167                     }
3168                     break;
3169 
3170 
3171                 case KEYBOARDMOVE:
3172                     if (LastScan != ScanCode::sc_none) {
3173                         if (LastScan == ScanCode::sc_escape) {
3174                             break;
3175                         }
3176 
3177                         auto it = std::find(
3178                             special_keys.cbegin(),
3179                             special_keys.cend(),
3180                             LastScan);
3181 
3182                         if (it != special_keys.cend()) {
3183                             ::sd_play_player_sound(NOWAYSND, bstone::AC_ITEM);
3184                         } else {
3185                             clean_display = TestForValidKey(LastScan);
3186 
3187                             if (clean_display) {
3188                                 ShootSnd();
3189                             }
3190 
3191                             dirscan[moveorder[which]] = LastScan;
3192                             picked = 1;
3193                         }
3194                         IN_ClearKeysDown();
3195                     }
3196                     break;
3197                 }
3198 
3199 
3200                 //
3201                 // EXIT INPUT?
3202                 //
3203 
3204                 if (IN_KeyDown(ScanCode::sc_escape)) {
3205                     picked = 1;
3206                     continue;
3207                 }
3208 
3209             } while (!picked);
3210 
3211             if (!clean_display) {
3212                 DrawCustomScreen();
3213             }
3214 
3215             SETFONTCOLOR(HIGHLIGHT_TEXT_COLOR, TERM_BACK_COLOR);
3216             redraw = 1;
3217             WaitKeyUp();
3218             continue;
3219         }
3220 
3221         if (ci.button1 || IN_KeyDown(ScanCode::sc_escape)) {
3222             exit = 1;
3223         }
3224 
3225         //
3226         // MOVE TO ANOTHER SPOT?
3227         //
3228         switch (ci.dir) {
3229 
3230         case dir_West:
3231             VWB_Bar(x - 1, PrintY - 1, CST_SPC, 7, ::menu_background_color);
3232             SETFONTCOLOR(HIGHLIGHT_TEXT_COLOR, TERM_BACK_COLOR);
3233             PrintRtn(which);
3234             do {
3235                 which--;
3236                 if (which < 0) {
3237                     which = 3;
3238                 }
3239             } while (!cust->allowed[which]);
3240 
3241             redraw = 1;
3242 
3243             ::sd_play_player_sound(MOVEGUN1SND, bstone::AC_ITEM);
3244 
3245             while (ReadAnyControl(&ci), ci.dir != dir_None) {
3246             }
3247             IN_ClearKeysDown();
3248             break;
3249 
3250 
3251 
3252         case dir_East:
3253             VWB_Bar(x - 1, PrintY - 1, CST_SPC, 7, ::menu_background_color);
3254             SETFONTCOLOR(HIGHLIGHT_TEXT_COLOR, TERM_BACK_COLOR);
3255             PrintRtn(which);
3256             do {
3257                 which++;
3258                 if (which > 3) {
3259                     which = 0;
3260                 }
3261             } while (!cust->allowed[which]);
3262 
3263             redraw = 1;
3264 
3265             ::sd_play_player_sound(MOVEGUN1SND, bstone::AC_ITEM);
3266 
3267             while (ReadAnyControl(&ci), ci.dir != dir_None) {
3268             }
3269 
3270             IN_ClearKeysDown();
3271             break;
3272 
3273         case dir_North:
3274         case dir_South:
3275             exit = 1;
3276 
3277         default:
3278             break;
3279         }
3280 
3281     } while (!exit);
3282 
3283     FREEFONT(STARTFONT + fontnumber);
3284 
3285     ::sd_play_player_sound(ESCPRESSEDSND, bstone::AC_ITEM);
3286 
3287     WaitKeyUp();
3288 }
3289 
3290 // --------------------------------------------------------------------------
3291 // FIXUP GUN CURSOR OVERDRAW SHIT
3292 // --------------------------------------------------------------------------
FixupCustom(int16_t w)3293 void FixupCustom(
3294     int16_t w)
3295 {
3296     static int16_t lastwhich = -1;
3297 
3298     switch (w) {
3299     case 0: DrawCustMouse(1);
3300         break;
3301     case 2: DrawCustJoy(1);
3302         break;
3303     case 4: DrawCustKeybd(1);
3304         break;
3305     case 5: DrawCustKeys(1);
3306     }
3307 
3308 
3309     if (lastwhich >= 0) {
3310         if (lastwhich != w) {
3311             switch (lastwhich) {
3312             case 0: DrawCustMouse(0);
3313                 break;
3314             case 2: DrawCustJoy(0);
3315                 break;
3316             case 4: DrawCustKeybd(0);
3317                 break;
3318             case 5: DrawCustKeys(0);
3319             }
3320         }
3321     }
3322 
3323     lastwhich = w;
3324 }
3325 
DrawCustomScreen()3326 void DrawCustomScreen()
3327 {
3328     int16_t i;
3329 
3330     ClearMScreen();
3331     DrawMenuTitle("CUSTOMIZE");
3332     DrawInstructions(IT_STANDARD);
3333 
3334     //
3335     // MOUSE
3336     //
3337 
3338     WindowX = 32;
3339     WindowW = 244;
3340 
3341     fontnumber = 4;
3342 
3343     SETFONTCOLOR(0x0C, TERM_BACK_COLOR);
3344 
3345 
3346     PrintY = 49;
3347     US_CPrint("MOUSE\n");
3348     PrintY = 79;
3349     US_CPrint("JOYSTICK/GRAVIS GAMEPAD\n");
3350     PrintY = 109;
3351     US_CPrint("KEYBOARD\n");
3352 
3353     fontnumber = 2;
3354 
3355     SETFONTCOLOR(DISABLED_TEXT_COLOR, TERM_BACK_COLOR);
3356 
3357     for (i = 60; i <= 120; i += 30) {
3358         ShadowPrint("RUN", CST_START, i);
3359         ShadowPrint("OPEN", CST_START + CST_SPC * 1, i);
3360         ShadowPrint("FIRE", CST_START + CST_SPC * 2, i);
3361         ShadowPrint("STRAFE", CST_START + CST_SPC * 3, i);
3362     }
3363 
3364     ShadowPrint("LEFT", CST_START, 135);
3365     ShadowPrint("RIGHT", CST_START + CST_SPC * 1, 135);
3366     ShadowPrint("FWRD", CST_START + CST_SPC * 2, 135);
3367     ShadowPrint("BKWRD", CST_START + CST_SPC * 3, 135);
3368 
3369 
3370     DrawCustMouse(0);
3371     DrawCustJoy(0);
3372     DrawCustKeybd(0);
3373     DrawCustKeys(0);
3374 
3375     //
3376     // PICK STARTING POINT IN MENU
3377     //
3378     if (CusItems.curpos < 0) {
3379         for (i = 0; i < CusItems.amount; i++) {
3380             if (CusMenu[i].active) {
3381                 CusItems.curpos = static_cast<int8_t>(i);
3382                 break;
3383             }
3384         }
3385     }
3386 
3387     VW_UpdateScreen();
3388     MenuFadeIn();
3389 }
3390 
PrintCustMouse(int16_t i)3391 void PrintCustMouse(
3392     int16_t i)
3393 {
3394     int16_t j;
3395 
3396     for (j = 0; j < 4; j++) {
3397         if (order[i] == buttonmouse[j]) {
3398             PrintX = CST_START + CST_SPC * i;
3399             US_Print(mbarray[j]);
3400             break;
3401         }
3402     }
3403 }
3404 
DrawCustMouse(int16_t hilight)3405 void DrawCustMouse(
3406     int16_t hilight)
3407 {
3408     int16_t i, color;
3409 
3410     color = ENABLED_TEXT_COLOR;
3411 
3412     if (hilight) {
3413         color = HIGHLIGHT_TEXT_COLOR;
3414     }
3415 
3416     SETFONTCOLOR(color, TERM_BACK_COLOR);
3417 
3418     if (!mouseenabled) {
3419         SETFONTCOLOR(DISABLED_TEXT_COLOR, TERM_BACK_COLOR);
3420         CusMenu[0].active = AT_DISABLED;
3421     } else {
3422         CusMenu[0].active = AT_ENABLED;
3423     }
3424 
3425     PrintY = CST_Y + 7;
3426     for (i = 0; i < 4; i++) {
3427         PrintCustMouse(i);
3428     }
3429 }
3430 
PrintCustJoy(int16_t i)3431 void PrintCustJoy(
3432     int16_t i)
3433 {
3434     int16_t j;
3435 
3436     for (j = 0; j < 4; j++) {
3437         if (order[i] == buttonjoy[j]) {
3438             PrintX = CST_START + CST_SPC * i;
3439             US_Print(mbarray[j]);
3440             break;
3441         }
3442     }
3443 }
3444 
DrawCustJoy(int16_t hilight)3445 void DrawCustJoy(
3446     int16_t hilight)
3447 {
3448     int16_t i, color;
3449 
3450 
3451     color = ENABLED_TEXT_COLOR;
3452     if (hilight) {
3453         color = HIGHLIGHT_TEXT_COLOR;
3454     }
3455 
3456     SETFONTCOLOR(color, TERM_BACK_COLOR);
3457 
3458     if (!joystickenabled) {
3459         SETFONTCOLOR(DISABLED_TEXT_COLOR, TERM_BACK_COLOR);
3460         CusMenu[2].active = AT_DISABLED;
3461     } else {
3462         CusMenu[2].active = AT_ENABLED;
3463     }
3464 
3465     PrintY = CST_Y + 37;
3466     for (i = 0; i < 4; i++) {
3467         PrintCustJoy(i);
3468     }
3469 }
3470 
PrintCustKeybd(int16_t i)3471 void PrintCustKeybd(
3472     int16_t i)
3473 {
3474     PrintX = CST_START + CST_SPC * i;
3475     US_Print(IN_GetScanName(buttonscan[order[i]]).c_str());
3476 }
3477 
DrawCustKeybd(int16_t hilight)3478 void DrawCustKeybd(
3479     int16_t hilight)
3480 {
3481     int16_t i, color;
3482 
3483     if (hilight) {
3484         color = HIGHLIGHT_TEXT_COLOR;
3485     } else {
3486         color = ENABLED_TEXT_COLOR;
3487     }
3488 
3489     SETFONTCOLOR(color, TERM_BACK_COLOR);
3490 
3491     PrintY = CST_Y + 67;
3492 
3493     for (i = 0; i < 4; i++) {
3494         PrintCustKeybd(i);
3495     }
3496 }
3497 
PrintCustKeys(int16_t i)3498 void PrintCustKeys(
3499     int16_t i)
3500 {
3501     PrintX = CST_START + CST_SPC * i;
3502     US_Print(IN_GetScanName(dirscan[moveorder[i]]).c_str());
3503 }
3504 
DrawCustKeys(int16_t hilight)3505 void DrawCustKeys(
3506     int16_t hilight)
3507 {
3508     int16_t i, color;
3509 
3510     color = ENABLED_TEXT_COLOR;
3511 
3512     if (hilight) {
3513         color = HIGHLIGHT_TEXT_COLOR;
3514     }
3515 
3516     SETFONTCOLOR(color, TERM_BACK_COLOR);
3517 
3518     PrintY = CST_Y + 82;
3519     for (i = 0; i < 4; i++) {
3520         PrintCustKeys(i);
3521     }
3522 }
3523 
CP_Quit()3524 void CP_Quit()
3525 {
3526     if (Confirm(QuitToDosStr)) {
3527         ExitGame();
3528     }
3529 
3530     DrawMainMenu();
3531 }
3532 
3533 // ---------------------------------------------------------------------------
3534 // Clear Menu screens to dark red
3535 // ---------------------------------------------------------------------------
ClearMScreen()3536 void ClearMScreen()
3537 {
3538     VWB_Bar(SCREEN_X, SCREEN_Y, SCREEN_W, SCREEN_H, ::menu_background_color);
3539 }
3540 
3541 // ---------------------------------------------------------------------------
3542 // Un/Cache a LUMP of graphics
3543 // ---------------------------------------------------------------------------
CacheLump(int16_t lumpstart,int16_t lumpend)3544 void CacheLump(
3545     int16_t lumpstart,
3546     int16_t lumpend)
3547 {
3548     int16_t i;
3549 
3550     for (i = lumpstart; i <= lumpend; i++) {
3551         CA_CacheGrChunk(i);
3552     }
3553 }
3554 
UnCacheLump(int16_t lumpstart,int16_t lumpend)3555 void UnCacheLump(
3556     int16_t lumpstart,
3557     int16_t lumpend)
3558 {
3559     int16_t i;
3560 
3561     for (i = lumpstart; i <= lumpend; i++) {
3562         FREEFONT(i);
3563     }
3564 }
3565 
DrawWindow(int16_t x,int16_t y,int16_t w,int16_t h,int16_t wcolor)3566 void DrawWindow(
3567     int16_t x,
3568     int16_t y,
3569     int16_t w,
3570     int16_t h,
3571     int16_t wcolor)
3572 {
3573     VWB_Bar(x, y, w, h, static_cast<uint8_t>(wcolor));
3574     DrawOutline(x, y, w, h, BORD2COLOR, DEACTIVE);
3575 }
3576 
DrawOutline(int16_t x,int16_t y,int16_t w,int16_t h,int16_t color1,int16_t color2)3577 void DrawOutline(
3578     int16_t x,
3579     int16_t y,
3580     int16_t w,
3581     int16_t h,
3582     int16_t color1,
3583     int16_t color2)
3584 {
3585     VWB_Hlin(x, x + w, y, static_cast<uint8_t>(color2));
3586     VWB_Vlin(y, y + h, x, static_cast<uint8_t>(color2));
3587     VWB_Hlin(x, x + w, y + h, static_cast<uint8_t>(color1));
3588     VWB_Vlin(y, y + h, x + w, static_cast<uint8_t>(color1));
3589 }
3590 
SetupControlPanel()3591 void SetupControlPanel()
3592 {
3593     // BBi
3594     SwitchItems.amount = (::is_ps() ? 7 : 9);
3595     SwitchItems.y = MENU_Y + (::is_ps() ? 15 : 7);
3596     // BBi
3597 
3598     ControlPanelAlloc();
3599 
3600     fontnumber = 2;
3601 
3602     WindowH = 200;
3603 
3604     if (!ingame) {
3605         CA_LoadAllSounds();
3606     } else {
3607         MainMenu[MM_SAVE_MISSION].active = AT_ENABLED;
3608     }
3609 
3610     ReadGameNames();
3611 }
3612 
ReadGameNames()3613 void ReadGameNames()
3614 {
3615     for (int i = 0; i < 10; ++i) {
3616         auto name = ::get_saved_game_base_name();
3617         name += static_cast<char>('0' + i);
3618 
3619         auto name_path = ::get_profile_dir() + name;
3620 
3621         bstone::FileStream stream(name_path);
3622 
3623         if (!stream.is_open()) {
3624             continue;
3625         }
3626 
3627         SaveGamesAvail[i] = 1;
3628 
3629         int chunk_size = ::FindChunk(&stream, "DESC");
3630 
3631         if (chunk_size > 0) {
3632             char temp[GAME_DESCRIPTION_LEN + 1];
3633 
3634             std::uninitialized_fill_n(
3635                 temp,
3636                 GAME_DESCRIPTION_LEN,
3637                 '\0');
3638 
3639             auto temp_size = std::min(GAME_DESCRIPTION_LEN, chunk_size);
3640 
3641             stream.read(temp, temp_size);
3642 
3643             ::strcpy(&SaveGameNames[i][0], temp);
3644         } else {
3645             ::strcpy(&SaveGameNames[i][0], "DESCRIPTION LOST");
3646         }
3647     }
3648 }
3649 
CleanupControlPanel()3650 void CleanupControlPanel()
3651 {
3652     if (!loadedgame) {
3653         FreeMusic();
3654     }
3655     ControlPanelFree();
3656     fontnumber = 4;
3657 }
3658 
3659 // ---------------------------------------------------------------------------
3660 // ControlPanelFree() - This FREES the control panel lump from memory
3661 //      and REALLOCS the ScaledDirectory
3662 // ---------------------------------------------------------------------------
ControlPanelFree()3663 void ControlPanelFree()
3664 {
3665     UnCacheLump(CONTROLS_LUMP_START, CONTROLS_LUMP_END);
3666     NewViewSize();
3667 }
3668 
3669 // ---------------------------------------------------------------------------
3670 // ControlPanelAlloc() - This CACHEs the control panel lump into memory
3671 //      and FREEs the ScaledDirectory.
3672 // ---------------------------------------------------------------------------
ControlPanelAlloc()3673 void ControlPanelAlloc()
3674 {
3675     CacheLump(CONTROLS_LUMP_START, CONTROLS_LUMP_END);
3676 }
3677 
3678 // ---------------------------------------------------------------------------
3679 // ShadowPrint() - Shadow Prints given text @ a given x & y in default font
3680 //
3681 // NOTE: Font MUST already be loaded
3682 // ---------------------------------------------------------------------------
ShadowPrint(const char * strng,int16_t x,int16_t y)3683 void ShadowPrint(
3684     const char* strng,
3685     int16_t x,
3686     int16_t y)
3687 {
3688     int16_t old_bc, old_fc;
3689 
3690     old_fc = fontcolor;
3691     old_bc = backcolor;
3692 
3693     PrintX = x + 1;
3694     PrintY = y + 1;
3695 
3696     SETFONTCOLOR(TERM_SHADOW_COLOR, TERM_BACK_COLOR);
3697     US_Print(strng);
3698 
3699     PrintX = x;
3700     PrintY = y;
3701     SETFONTCOLOR(old_fc, old_bc);
3702     US_Print(strng);
3703 }
3704 
3705 // ---------------------------------------------------------------------------
3706 // HandleMenu() - Handle moving gun around a menu
3707 // ---------------------------------------------------------------------------
HandleMenu(CP_iteminfo * item_i,CP_itemtype * items,void (* routine)(int16_t w))3708 int16_t HandleMenu(
3709     CP_iteminfo* item_i,
3710     CP_itemtype* items,
3711     void (* routine)(int16_t w))
3712 {
3713 #define box_on item_i->cursor.on
3714     int8_t key;
3715     static int16_t redrawitem = 1;
3716 
3717     int16_t i, x, y, basey, exit, which, flash_tics;
3718     ControlInfo ci;
3719 
3720     which = item_i->curpos;
3721     x = item_i->x;
3722     basey = item_i->y;
3723     y = basey + which * item_i->y_spacing;
3724     box_on = 1;
3725     DrawGun(item_i, items, x, &y, which, basey, routine);
3726 
3727     SetTextColor(items + which, 1);
3728 
3729     if (redrawitem) {
3730         ShadowPrint((items + which)->string, item_i->x + item_i->indent, item_i->y + which * item_i->y_spacing);
3731     }
3732 
3733     //
3734     // CALL CUSTOM ROUTINE IF IT IS NEEDED
3735     //
3736 
3737     if (routine) {
3738         routine(which);
3739     }
3740 
3741     VW_UpdateScreen();
3742 
3743     flash_tics = 40;
3744     exit = 0;
3745     TimeCount = 0;
3746     IN_ClearKeysDown();
3747 
3748     do {
3749         CalcTics();
3750         flash_tics -= tics;
3751 
3752         CycleColors();
3753 
3754         //
3755         // CHANGE GUN SHAPE
3756         //
3757 
3758         if (flash_tics <= 0) {
3759             flash_tics = 40;
3760 
3761             box_on ^= 1;
3762 
3763             if (box_on) {
3764                 DrawGun(item_i, items, x, &y, which, basey, routine);
3765             } else {
3766                 EraseGun(item_i, items, x, y, which);
3767                 if (routine) {
3768                     routine(which);
3769                 }
3770             }
3771 
3772 
3773             VW_UpdateScreen();
3774         }
3775 
3776         CheckPause();
3777 
3778 
3779         //
3780         // SEE IF ANY KEYS ARE PRESSED FOR INITIAL CHAR FINDING
3781         //
3782 
3783         key = LastASCII;
3784         if (key) {
3785             int16_t ok = 0;
3786 
3787             if (key >= 'a') {
3788                 key -= 'a' - 'A';
3789             }
3790 
3791             for (i = which + 1; i < item_i->amount; i++) {
3792                 if ((items + i)->active && (items + i)->string[0] == key) {
3793                     EraseGun(item_i, items, x, y, which);
3794                     which = i;
3795                     item_i->curpos = static_cast<int8_t>(which); // jtr -testing
3796                     box_on = 1;
3797                     DrawGun(item_i, items, x, &y, which, basey, routine);
3798                     VW_UpdateScreen();
3799 
3800                     ok = 1;
3801                     IN_ClearKeysDown();
3802                     break;
3803                 }
3804             }
3805 
3806             //
3807             // DIDN'T FIND A MATCH FIRST TIME THRU. CHECK AGAIN.
3808             //
3809 
3810             if (!ok) {
3811                 for (i = 0; i < which; i++) {
3812                     if ((items + i)->active && (items + i)->string[0] == key) {
3813                         EraseGun(item_i, items, x, y, which);
3814                         which = i;
3815                         item_i->curpos = static_cast<int8_t>(which); // jtr -testing
3816                         box_on = 1;
3817                         DrawGun(item_i, items, x, &y, which, basey, routine);
3818                         VW_UpdateScreen();
3819 
3820                         IN_ClearKeysDown();
3821                         break;
3822                     }
3823                 }
3824             }
3825         }
3826 
3827         //
3828         // GET INPUT
3829         //
3830 
3831         ReadAnyControl(&ci);
3832 
3833         switch (ci.dir) {
3834         // ------------------------
3835         // MOVE UP
3836         //
3837         case dir_North:
3838             EraseGun(item_i, items, x, y, which);
3839 
3840             do {
3841                 if (!which) {
3842                     which = item_i->amount - 1;
3843                 } else {
3844                     which--;
3845                 }
3846 
3847             } while (!(items + which)->active);
3848 
3849             item_i->curpos = static_cast<int8_t>(which); // jtr -testing
3850 
3851             box_on = 1;
3852             DrawGun(item_i, items, x, &y, which, basey, routine);
3853 
3854             VW_UpdateScreen();
3855 
3856             TicDelay(20);
3857             break;
3858 
3859         // --------------------------
3860         // MOVE DOWN
3861         //
3862         case dir_South:
3863             EraseGun(item_i, items, x, y, which);
3864 
3865             do {
3866                 if (which == item_i->amount - 1) {
3867                     which = 0;
3868                 } else {
3869                     which++;
3870                 }
3871             } while (!(items + which)->active);
3872 
3873             item_i->curpos = static_cast<int8_t>(which); // jtr -testing
3874 
3875             box_on = 1;
3876             DrawGun(item_i, items, x, &y, which, basey, routine);
3877 
3878             VW_UpdateScreen();
3879 
3880             TicDelay(20);
3881             break;
3882 
3883         default:
3884             break;
3885         }
3886 
3887         if (ci.button0 || Keyboard[ScanCode::sc_space] || Keyboard[ScanCode::sc_return]) {
3888             exit = 1;
3889         }
3890 
3891         if (ci.button1 || Keyboard[ScanCode::sc_escape]) {
3892             exit = 2;
3893         }
3894 
3895     } while (!exit);
3896 
3897     IN_ClearKeysDown();
3898 
3899     //
3900     // ERASE EVERYTHING
3901     //
3902 
3903     box_on = 0;
3904     redrawitem = 1;
3905     EraseGun(item_i, items, x, y, which);
3906 
3907     if (routine) {
3908         routine(which);
3909     }
3910 
3911     VW_UpdateScreen();
3912 
3913     item_i->curpos = static_cast<int8_t>(which);
3914 
3915     switch (exit) {
3916     case 1:
3917         //
3918         // CALL THE ROUTINE
3919         //
3920         if ((items + which)->routine) {
3921             // Make sure there's room to save when CP_SaveGame() is called.
3922             //
3923             if (reinterpret_cast<size_t>(items[which].routine) == reinterpret_cast<size_t>(CP_SaveGame)) {
3924                 if (!CheckDiskSpace(DISK_SPACE_NEEDED, CANT_SAVE_GAME_TXT, cds_menu_print)) {
3925                     return which;
3926                 }
3927             }
3928 
3929             //
3930             // ALREADY IN A GAME?
3931             //
3932             if (::is_ps() && ingame && ((items + which)->routine == CP_NewGame)) {
3933                 if (!Confirm(CURGAME)) {
3934                     ::MenuFadeOut();
3935                     return 0;
3936                 }
3937             }
3938 
3939             ShootSnd();
3940             ::MenuFadeOut();
3941             (items + which)->routine(0);
3942         }
3943         return which;
3944 
3945     case 2:
3946         ::sd_play_player_sound(ESCPRESSEDSND, bstone::AC_ITEM);
3947 
3948         return -1;
3949     }
3950 
3951     return 0;     // JUST TO SHUT UP THE ERROR MESSAGES!
3952 }
3953 
3954 // ---------------------------------------------------------------------------
3955 // ERASE GUN & DE-HIGHLIGHT STRING
3956 // ---------------------------------------------------------------------------
EraseGun(CP_iteminfo * item_i,CP_itemtype * items,int16_t x,int16_t y,int16_t which)3957 void EraseGun(
3958     CP_iteminfo* item_i,
3959     CP_itemtype* items,
3960     int16_t x,
3961     int16_t y,
3962     int16_t which)
3963 {
3964     static_cast<void>(x);
3965 
3966     VWB_Bar(item_i->cursor.x, y + item_i->cursor.y_ofs, item_i->cursor.width, item_i->cursor.height, ::menu_background_color);
3967     SetTextColor(items + which, 0);
3968 
3969     ShadowPrint((items + which)->string, item_i->x + item_i->indent, y);
3970 }
3971 
3972 // ---------------------------------------------------------------------------
3973 // DrawGun() - DRAW GUN AT NEW POSITION
3974 // ---------------------------------------------------------------------------
DrawGun(CP_iteminfo * item_i,CP_itemtype * items,int16_t x,int16_t * y,int16_t which,int16_t basey,void (* routine)(int16_t w))3975 void DrawGun(
3976     CP_iteminfo* item_i,
3977     CP_itemtype* items,
3978     int16_t x,
3979     int16_t* y,
3980     int16_t which,
3981     int16_t basey,
3982     void (* routine)(int16_t w))
3983 {
3984     static_cast<void>(x);
3985 
3986     *y = basey + which * item_i->y_spacing;
3987 
3988     VWB_Bar(item_i->cursor.x, *y + item_i->cursor.y_ofs, item_i->cursor.width, item_i->cursor.height, HIGHLIGHT_BOX_COLOR);
3989     SetTextColor(items + which, 1);
3990 
3991     ShadowPrint((items + which)->string, item_i->x + item_i->indent, item_i->y + which * item_i->y_spacing);
3992 
3993     //
3994     // CALL CUSTOM ROUTINE IF IT IS NEEDED
3995     //
3996 
3997     if (routine) {
3998         routine(which);
3999     }
4000 }
4001 
4002 // ---------------------------------------------------------------------------
4003 // TicDelay() - DELAY FOR AN AMOUNT OF TICS OR UNTIL CONTROLS ARE INACTIVE
4004 // ---------------------------------------------------------------------------
TicDelay(int16_t count)4005 void TicDelay(
4006     int16_t count)
4007 {
4008     ControlInfo ci;
4009 
4010     TimeCount = 0;
4011 
4012     do {
4013         ReadAnyControl(&ci);
4014     } while (TimeCount < static_cast<uint32_t>(count) && ci.dir != dir_None);
4015 }
4016 
4017 // ---------------------------------------------------------------------------
4018 // DrawMenu() - Draw a menu
4019 //
4020 //       This also calculates the Y position of the current items in the
4021 //                      CP_itemtype structures.
4022 // ---------------------------------------------------------------------------
DrawMenu(CP_iteminfo * item_i,CP_itemtype * items)4023 void DrawMenu(
4024     CP_iteminfo* item_i,
4025     CP_itemtype* items)
4026 {
4027     int16_t i, which = item_i->curpos;
4028 
4029     WindowX = PrintX = item_i->x + item_i->indent;
4030     WindowY = PrintY = item_i->y;
4031 
4032     WindowW = 320;
4033     WindowH = 200;
4034 
4035     for (i = 0; i < item_i->amount; i++) {
4036         SetTextColor(items + i, which == i);
4037         ShadowPrint((items + i)->string, WindowX, item_i->y + i * item_i->y_spacing);
4038     }
4039 }
4040 
4041 // ---------------------------------------------------------------------------
4042 // SET TEXT COLOR (HIGHLIGHT OR NO)
4043 // ---------------------------------------------------------------------------
SetTextColor(CP_itemtype * items,int16_t hlight)4044 void SetTextColor(
4045     CP_itemtype* items,
4046     int16_t hlight)
4047 {
4048     if (hlight) {
4049         SETFONTCOLOR(color_hlite[items->active], TERM_BACK_COLOR);
4050     } else {
4051         SETFONTCOLOR(color_norml[items->active], TERM_BACK_COLOR);
4052     }
4053 }
4054 
4055 // ---------------------------------------------------------------------------
4056 // WAIT FOR CTRLKEY-UP OR BUTTON-UP
4057 // ---------------------------------------------------------------------------
WaitKeyUp()4058 void WaitKeyUp()
4059 {
4060     for (auto quit = false; !quit; ) {
4061         ControlInfo ci;
4062 
4063         ::ReadAnyControl(&ci);
4064 
4065         quit = !(
4066             ci.button0 != 0 ||
4067             ci.button1 != 0 ||
4068             ci.button2 != 0 ||
4069             ci.button3 != 0 ||
4070             Keyboard[ScanCode::sc_space] ||
4071             Keyboard[ScanCode::sc_return] ||
4072             Keyboard[ScanCode::sc_escape]);
4073     }
4074 }
4075 
4076 // ---------------------------------------------------------------------------
4077 // READ KEYBOARD, JOYSTICK AND MOUSE FOR INPUT
4078 // ---------------------------------------------------------------------------
ReadAnyControl(ControlInfo * ci)4079 void ReadAnyControl(
4080     ControlInfo* ci)
4081 {
4082     bool mouseactive = false;
4083 
4084     ::IN_ReadControl(0, ci);
4085 
4086     //
4087     // UNDO some of the ControlInfo vars that were init
4088     // with IN_ReadControl() for the mouse...
4089     //
4090     if (ControlTypeUsed == ctrl_Mouse) {
4091         //
4092         // Clear directions & buttons (if enabled or not)
4093         //
4094         ci->dir = dir_None;
4095         ci->button0 = 0;
4096         ci->button1 = 0;
4097         ci->button2 = 0;
4098         ci->button3 = 0;
4099     }
4100 
4101     if (mouseenabled) {
4102         int mousex;
4103         int mousey;
4104 
4105         // READ MOUSE MOTION COUNTERS
4106         // RETURN DIRECTION
4107         // HOME MOUSE
4108         // CHECK MOUSE BUTTONS
4109 
4110         ::in_get_mouse_deltas(mousex, mousey);
4111         ::in_clear_mouse_deltas();
4112 
4113         const int DELTA_THRESHOLD = 10;
4114 
4115         if (mousey < -DELTA_THRESHOLD) {
4116             ci->dir = dir_North;
4117             mouseactive = true;
4118         } else if (mousey > DELTA_THRESHOLD) {
4119             ci->dir = dir_South;
4120             mouseactive = true;
4121         }
4122 
4123         if (mousex < -DELTA_THRESHOLD) {
4124             ci->dir = dir_West;
4125             mouseactive = true;
4126         } else if (mousex > DELTA_THRESHOLD) {
4127             ci->dir = dir_East;
4128             mouseactive = true;
4129         }
4130 
4131         int buttons = ::IN_MouseButtons();
4132 
4133         if (buttons != 0) {
4134             ci->button0 = buttons & 1;
4135             ci->button1 = buttons & 2;
4136             ci->button2 = buttons & 4;
4137             ci->button3 = false;
4138             mouseactive = true;
4139         }
4140     }
4141 
4142     if (joystickenabled && !mouseactive) {
4143         int jx;
4144         int jy;
4145         int16_t jb;
4146 
4147         ::INL_GetJoyDelta(joystickport, &jx, &jy);
4148 
4149         if (jy < -SENSITIVE) {
4150             ci->dir = dir_North;
4151         } else if (jy > SENSITIVE) {
4152             ci->dir = dir_South;
4153         }
4154 
4155         if (jx < -SENSITIVE) {
4156             ci->dir = dir_West;
4157         } else if (jx > SENSITIVE) {
4158             ci->dir = dir_East;
4159         }
4160 
4161         jb = ::IN_JoyButtons();
4162 
4163         if (jb != 0) {
4164             ci->button0 = jb & 1;
4165             ci->button1 = jb & 2;
4166 
4167             if (joypadenabled) {
4168                 ci->button2 = jb & 4;
4169                 ci->button3 = jb & 8;
4170             } else {
4171                 ci->button2 = false;
4172                 ci->button3 = false;
4173             }
4174         }
4175     }
4176 }
4177 
4178 ////////////////////////////////////////////////////////////////////
4179 //
4180 // DRAW DIALOG AND CONFIRM YES OR NO TO QUESTION
4181 //
4182 ////////////////////////////////////////////////////////////////////
Confirm(const char * string)4183 int16_t Confirm(
4184     const char* string)
4185 {
4186     int16_t xit = 0, x, y, tick = 0, whichsnd[2] = { ESCPRESSEDSND, SHOOTSND };
4187 
4188 
4189     Message(string);
4190 
4191 // Next two lines needed for flashing cursor ...
4192 //
4193     SETFONTCOLOR(BORDER_TEXT_COLOR, BORDER_MED_COLOR);
4194     CA_CacheGrChunk(STARTFONT + fontnumber);
4195 
4196     IN_ClearKeysDown();
4197 
4198     //
4199     // BLINK CURSOR
4200     //
4201     x = PrintX;
4202     y = PrintY;
4203     TimeCount = 0;
4204     do {
4205         if (TimeCount >= 10) {
4206             switch (tick) {
4207             case 0:
4208                 VWB_Bar(x, y, 8, 13, BORDER_MED_COLOR);
4209                 break;
4210 
4211             case 1:
4212                 PrintX = x;
4213                 PrintY = y;
4214                 US_Print("_");
4215             }
4216 
4217             VW_UpdateScreen();
4218             tick ^= 1;
4219             TimeCount = 0;
4220         }
4221 
4222         // BBi
4223         IN_CheckAck();
4224     } while (!Keyboard[ScanCode::sc_y] && !Keyboard[ScanCode::sc_n] && !Keyboard[ScanCode::sc_escape]);
4225 
4226 
4227     if (Keyboard[ScanCode::sc_y]) {
4228         xit = 1;
4229         ShootSnd();
4230     }
4231 
4232     while (Keyboard[ScanCode::sc_y] || Keyboard[ScanCode::sc_n] || Keyboard[ScanCode::sc_escape]) {
4233         IN_CheckAck();
4234     }
4235 
4236     IN_ClearKeysDown();
4237 
4238     ::sd_play_player_sound(
4239         whichsnd[xit],
4240         bstone::AC_ITEM);
4241 
4242     FREEFONT(STARTFONT + fontnumber);
4243 
4244     return xit;
4245 }
4246 
4247 // ---------------------------------------------------------------------------
4248 // PRINT A MESSAGE IN A WINDOW
4249 // ---------------------------------------------------------------------------
Message(const char * string)4250 void Message(
4251     const char* string)
4252 {
4253     int16_t h = 0, w = 0, mw = 0;
4254     size_t i;
4255     fontstruct* font;
4256     uint16_t OldPrintX, OldPrintY;
4257 
4258     fontnumber = 1;
4259     CA_CacheGrChunk(STARTFONT + 1);
4260 
4261     font = static_cast<fontstruct*>(grsegs[STARTFONT + fontnumber]);
4262 
4263     h = font->height;
4264     for (i = 0; i < strlen(string); i++) {
4265         if (string[i] == '\n') {
4266             if (w > mw) {
4267                 mw = w;
4268             }
4269             w = 0;
4270             h += font->height;
4271         } else {
4272             w += font->width[static_cast<int>(string[i])];
4273         }
4274     }
4275 
4276     if (w + 10 > mw) {
4277         mw = w + 10;
4278     }
4279 
4280     OldPrintY = PrintY = (WindowH / 2) - h / 2;
4281     OldPrintX = PrintX = WindowX = 160 - mw / 2;
4282 
4283     // bump down and to right for shadow
4284 
4285     PrintX++;
4286     PrintY++;
4287     WindowX++;
4288 
4289     BevelBox(WindowX - 6, PrintY - 6, mw + 10, h + 10, BORDER_HI_COLOR, BORDER_MED_COLOR, BORDER_LO_COLOR);
4290 
4291     SETFONTCOLOR(BORDER_LO_COLOR, BORDER_MED_COLOR);
4292     US_Print(string);
4293 
4294     PrintY = OldPrintY;
4295     WindowX = PrintX = OldPrintX;
4296 
4297     SETFONTCOLOR(BORDER_TEXT_COLOR, BORDER_MED_COLOR);
4298     US_Print(string);
4299 
4300     FREEFONT(STARTFONT + 1);
4301 
4302     VW_UpdateScreen();
4303 }
4304 
4305 // --------------------------------------------------------------------------
4306 // Searches for an "^XX" and replaces with a 0 (NULL)
4307 // --------------------------------------------------------------------------
TerminateStr(char * pos)4308 void TerminateStr(
4309     char* pos)
4310 {
4311     pos = strstr(pos, "^XX");
4312     *pos = 0;
4313 }
4314 
4315 // ---------------------------------------------------------------------------
4316 // Caches and prints a message in a window.
4317 // ---------------------------------------------------------------------------
CacheMessage(uint16_t MessageNum)4318 void CacheMessage(
4319     uint16_t MessageNum)
4320 {
4321     char* string;
4322 
4323     CA_CacheGrChunk(MessageNum);
4324     string = (char*)grsegs[MessageNum];
4325 
4326     TerminateStr(string);
4327 
4328     Message(string);
4329 
4330     FREEFONT(MessageNum);
4331 }
4332 
4333 // ---------------------------------------------------------------------------
4334 // CacheCompData() - Caches and Decompresses data from the VGAGRAPH
4335 //
4336 // NOTE: - User is responsible for freeing loaded data
4337 //       - Returns the size of the data
4338 //       - Does not call TerminateStr() for loaded TEXT data
4339 //
4340 // RETURNS: Lenght of loaded (decompressed) data
4341 //
4342 // ---------------------------------------------------------------------------
CacheCompData(uint16_t item_number,void ** dst_ptr)4343 uint32_t CacheCompData(
4344     uint16_t item_number,
4345     void** dst_ptr)
4346 {
4347     char* chunk;
4348     char* dst;
4349     CompHeader_t CompHeader {};
4350     uint32_t data_length;
4351 
4352     // Load compressed data
4353     CA_CacheGrChunk(item_number);
4354     chunk = (char*)grsegs[item_number];
4355 
4356     if (!::is_ps()) {
4357         data_length = ::ca_gr_last_expanded_size;
4358     } else {
4359         memcpy(CompHeader.NameId, &chunk[0], 4);
4360         CompHeader.OriginalLen = ((uint32_t*)&chunk[4])[0];
4361         CompHeader.CompType = (ct_TYPES)((int16_t*)&chunk[8])[0];
4362         CompHeader.CompressLen = ((uint32_t*)&chunk[10])[0];
4363 
4364         data_length = CompHeader.OriginalLen;
4365 
4366         chunk += 14;
4367     }
4368 
4369     // Allocate Dest Memory
4370 
4371     dst = new char[data_length];
4372     *dst_ptr = dst;
4373 
4374     if (!::is_ps()) {
4375         std::copy(
4376             chunk,
4377             &chunk[data_length],
4378             dst);
4379     } else {
4380         // Decompress and terminate string
4381 
4382         if (!LZH_Startup()) {
4383             Quit("out of memory");
4384         }
4385 
4386         ::LZH_Decompress(
4387             chunk,
4388             dst,
4389             data_length,
4390             CompHeader.CompressLen);
4391 
4392         LZH_Shutdown();
4393     }
4394 
4395     // Free compressed data
4396     UNCACHEGRCHUNK(item_number);
4397 
4398     // Return loaded size
4399     return data_length;
4400 }
4401 
StartCPMusic(int16_t song)4402 void StartCPMusic(
4403     int16_t song)
4404 {
4405     int chunk;
4406 
4407     lastmenumusic = song;
4408 
4409     SD_MusicOff();
4410     chunk = song;
4411     CA_CacheAudioChunk(static_cast<int16_t>(STARTMUSIC + chunk));
4412     ::SD_StartMusic(chunk);
4413 }
4414 
FreeMusic()4415 void FreeMusic()
4416 {
4417     SD_MusicOff();
4418 }
4419 
4420 
4421 #ifdef CACHE_KEY_DATA
4422 // ---------------------------------------------------------------------------
4423 // IN_GetScanName() - Returns a string containing the name of the
4424 //      specified scan code
4425 // ---------------------------------------------------------------------------
IN_GetScanName(ScanCode scan)4426 uint8_t far* IN_GetScanName(
4427     ScanCode scan)
4428 {
4429     uint8_t* p;
4430     ScanCode* s;
4431 
4432     for (s = ExtScanCodes, p = ExtScanNames; *s; p += 7, s++) {
4433         if (*s == scan) {
4434             return (uint8_t*)p;
4435         }
4436     }
4437 
4438     return (uint8_t*)(ScanNames + (scan << 1));
4439 }
4440 #else
4441 // ---------------------------------------------------------------------------
4442 // IN_GetScanName() - Returns a string containing the name of the
4443 //      specified scan code
4444 // ---------------------------------------------------------------------------
IN_GetScanName(ScanCode scan)4445 const std::string& IN_GetScanName(
4446     ScanCode scan)
4447 {
4448     for (auto i = 0; ext_scan_codes[i] != ScanCode::sc_none; ++i) {
4449         if (ext_scan_codes[i] == scan) {
4450             return ext_scan_names[i];
4451         }
4452     }
4453 
4454     return scan_names[static_cast<int>(scan)];
4455 }
4456 #endif
4457 
4458 // ---------------------------------------------------------------------------
4459 // CHECK FOR PAUSE KEY (FOR MUSIC ONLY)
4460 // ---------------------------------------------------------------------------
CheckPause()4461 void CheckPause()
4462 {
4463     if (Paused) {
4464         switch (SoundStatus) {
4465         case 0:
4466             SD_MusicOn();
4467             break;
4468 
4469         case 1:
4470             SD_MusicOff();
4471             break;
4472         }
4473 
4474         SoundStatus ^= 1;
4475         VW_WaitVBL(3);
4476         IN_ClearKeysDown();
4477         Paused = false;
4478     }
4479 }
4480 
4481 // -------------------------------------------------------------------------
4482 // DRAW GUN CURSOR AT CORRECT POSITION IN MENU
4483 // -------------------------------------------------------------------------
DrawMenuGun(CP_iteminfo * iteminfo)4484 void DrawMenuGun(
4485     CP_iteminfo* iteminfo)
4486 {
4487     int16_t x, y;
4488 
4489     x = iteminfo->cursor.x;
4490     y = iteminfo->y + iteminfo->curpos * iteminfo->y_spacing + iteminfo->cursor.y_ofs;
4491 
4492     VWB_Bar(x, y, iteminfo->cursor.width, iteminfo->cursor.height, HIGHLIGHT_BOX_COLOR);
4493 }
4494 
ShootSnd()4495 void ShootSnd()
4496 {
4497     ::sd_play_player_sound(SHOOTSND, bstone::AC_ITEM);
4498 }
4499 
ShowPromo()4500 void ShowPromo()
4501 {
4502     const auto PROMO_MUSIC = HIDINGA_MUS;
4503 
4504 // Load and start music
4505 //
4506     ::CA_CacheAudioChunk(STARTMUSIC + PROMO_MUSIC);
4507     ::SD_StartMusic(PROMO_MUSIC);
4508 
4509 // Show promo screen 1
4510 //
4511     ::MenuFadeOut();
4512     ::CA_CacheScreen(PROMO1PIC);
4513     VW_UpdateScreen();
4514     MenuFadeIn();
4515     ::IN_UserInput(TickBase * 20);
4516 
4517 // Show promo screen 2
4518 //
4519     ::MenuFadeOut();
4520     ::CA_CacheScreen(PROMO2PIC);
4521     VW_UpdateScreen();
4522     MenuFadeIn();
4523     ::IN_UserInput(TickBase * 20);
4524 
4525 // Music off and freed!
4526 //
4527     ::StopMusic();
4528 }
4529 
ExitGame()4530 void ExitGame()
4531 {
4532     VW_FadeOut();
4533 
4534     if (::is_aog_sw() && !::no_screens) {
4535         ::ShowPromo();
4536     }
4537 
4538     SD_MusicOff();
4539     SD_StopSound();
4540     Quit();
4541 }
4542 
4543 // BBi
4544 int volume_index = 0;
4545 int* const volumes[2] = { &sd_sfx_volume, &sd_music_volume };
4546 
draw_volume_control(int index,int volume,bool is_enabled)4547 void draw_volume_control(
4548     int index,
4549     int volume,
4550     bool is_enabled)
4551 {
4552     int16_t slider_color =
4553         is_enabled ? ENABLED_TEXT_COLOR : DISABLED_TEXT_COLOR;
4554 
4555     int16_t outline_color =
4556         is_enabled ? HIGHLIGHT_TEXT_COLOR : DEACTIAVED_TEXT_COLOR;
4557 
4558     int y = 82 + (index * 40);
4559 
4560     VWB_Bar(74, static_cast<int16_t>(y), 160, 8, HIGHLIGHT_BOX_COLOR);
4561     DrawOutline(73, static_cast<int16_t>(y - 1), 161, 9,
4562                 outline_color, outline_color);
4563     VWB_Bar(static_cast<int16_t>(74 + ((160 * volume) / (::sd_max_volume + 1))),
4564             static_cast<int16_t>(y), 16, 8, static_cast<uint8_t>(slider_color));
4565 }
4566 
draw_volume_controls()4567 void draw_volume_controls()
4568 {
4569     for (int i = 0; i < 2; ++i) {
4570         draw_volume_control(i, *(volumes[i]), i == volume_index);
4571     }
4572 }
4573 
cp_sound_volume(int16_t)4574 void cp_sound_volume(
4575     int16_t)
4576 {
4577     ClearMScreen();
4578     DrawMenuTitle("SOUND VOLUME");
4579     DrawInstructions(IT_SOUND_VOLUME);
4580 
4581     fontnumber = 4;
4582 
4583     SETFONTCOLOR(HIGHLIGHT_TEXT_COLOR, TERM_BACK_COLOR);
4584 
4585     PrintX = 150;
4586     PrintY = 60;
4587     US_Print("SFX");
4588 
4589     PrintX = 145;
4590     PrintY = 105;
4591     US_Print("MUSIC");
4592 
4593     for (int i = 0; i < 2; ++i) {
4594         PrintX = 36;
4595         PrintY = static_cast<uint16_t>(81 + (i * 40));
4596         US_Print("MUTE");
4597 
4598         PrintX = 242;
4599         US_Print("LOUD");
4600     }
4601 
4602     draw_volume_controls();
4603 
4604     VW_UpdateScreen();
4605     MenuFadeIn();
4606 
4607     ControlInfo ci;
4608 
4609     int old_volumes[2];
4610     for (int i = 0; i < 2; ++i) {
4611         old_volumes[i] = -1;
4612     }
4613 
4614     for (bool quit = false; !quit; ) {
4615         bool update_volumes = false;
4616         bool redraw_controls = false;
4617 
4618         ReadAnyControl(&ci);
4619 
4620         switch (ci.dir) {
4621         case dir_North:
4622             if (volume_index == 1) {
4623                 redraw_controls = true;
4624                 volume_index = 0;
4625                 draw_volume_controls();
4626                 VW_UpdateScreen();
4627             }
4628 
4629             while (Keyboard[ScanCode::sc_up_arrow]) {
4630                 ::in_handle_events();
4631             }
4632             break;
4633 
4634         case dir_South:
4635             if (volume_index == 0) {
4636                 redraw_controls = true;
4637                 volume_index = 1;
4638                 draw_volume_controls();
4639                 VW_UpdateScreen();
4640             }
4641 
4642             while (Keyboard[ScanCode::sc_down_arrow]) {
4643                 ::in_handle_events();
4644             }
4645             break;
4646 
4647         case dir_West:
4648             if (*volumes[volume_index] > ::sd_min_volume) {
4649                 redraw_controls = true;
4650                 update_volumes = true;
4651                 --(*volumes[volume_index]);
4652                 draw_volume_controls();
4653                 VW_UpdateScreen();
4654             }
4655 
4656             while (Keyboard[ScanCode::sc_left_arrow]) {
4657                 ::in_handle_events();
4658             }
4659             break;
4660 
4661         case dir_East:
4662             if (*volumes[volume_index] < ::sd_max_volume) {
4663                 redraw_controls = true;
4664                 update_volumes = true;
4665                 ++(*volumes[volume_index]);
4666             }
4667 
4668             while (Keyboard[ScanCode::sc_right_arrow]) {
4669                 ::in_handle_events();
4670             }
4671             break;
4672 
4673         default:
4674             break;
4675         }
4676 
4677         if (update_volumes) {
4678             update_volumes = false;
4679 
4680             if (old_volumes[0] != *volumes[0]) {
4681                 sd_set_sfx_volume(sd_sfx_volume);
4682                 sd_play_player_sound(MOVEGUN1SND, bstone::AC_ITEM);
4683             }
4684 
4685             if (old_volumes[1] != *volumes[1]) {
4686                 sd_set_music_volume(sd_music_volume);
4687             }
4688         }
4689 
4690         if (redraw_controls) {
4691             redraw_controls = false;
4692             draw_volume_controls();
4693             VW_UpdateScreen();
4694         }
4695 
4696         quit = (ci.button1 || Keyboard[ScanCode::sc_escape]);
4697     }
4698 
4699     sd_play_player_sound(ESCPRESSEDSND, bstone::AC_ITEM);
4700 
4701     WaitKeyUp();
4702     MenuFadeIn();
4703 }
4704 
4705 ///
draw_video_descriptions(int16_t which)4706 void draw_video_descriptions(
4707     int16_t which)
4708 {
4709     const char* instructions[] = {
4710         "STRETCHES RENDERED IMAGE TO THE WHOLE WINDOW",
4711     };
4712 
4713     ::fontnumber = 2;
4714 
4715     ::WindowX = 48;
4716     ::WindowY = 144;
4717     ::WindowW = 236;
4718     ::WindowH = 8;
4719 
4720     ::VWB_Bar(
4721         ::WindowX,
4722         ::WindowY - 1,
4723         ::WindowW,
4724         ::WindowH,
4725         ::menu_background_color);
4726 
4727     ::SETFONTCOLOR(TERM_SHADOW_COLOR, TERM_BACK_COLOR);
4728     ::US_PrintCentered(instructions[which]);
4729 
4730     --::WindowX;
4731     --::WindowY;
4732 
4733     SETFONTCOLOR(INSTRUCTIONS_TEXT_COLOR, TERM_BACK_COLOR);
4734     ::US_PrintCentered(instructions[which]);
4735 }
4736 
video_draw_menu()4737 void video_draw_menu()
4738 {
4739     ::CA_CacheScreen(BACKGROUND_SCREENPIC);
4740     ::ClearMScreen();
4741     ::DrawMenuTitle("VIDEO SETTINGS");
4742     ::DrawInstructions(IT_STANDARD);
4743     ::DrawMenu(&video_items, video_menu);
4744     VW_UpdateScreen();
4745 }
4746 
video_draw_switch(int16_t which)4747 void video_draw_switch(
4748     int16_t which)
4749 {
4750     uint16_t Shape;
4751 
4752     for (int i = 0; i < video_items.amount; i++) {
4753         if (video_menu[i].string[0]) {
4754             Shape = ::C_NOTSELECTEDPIC;
4755 
4756             if (video_items.cursor.on) {
4757                 if (i == which) {
4758                     Shape += 2;
4759                 }
4760             }
4761 
4762             switch (i) {
4763             case mvl_stretch_to_window:
4764                 if (::vid_widescreen) {
4765                     Shape++;
4766                 }
4767                 break;
4768 
4769             default:
4770                 break;
4771             }
4772 
4773             ::VWB_DrawPic(
4774                 video_items.x - 16,
4775                 video_items.y + i * video_items.y_spacing - 1,
4776                 Shape);
4777         }
4778     }
4779 
4780     draw_video_descriptions(which);
4781 }
4782 
cp_video(int16_t)4783 void cp_video(
4784     int16_t)
4785 {
4786     int16_t which;
4787 
4788     ::CA_CacheScreen(BACKGROUND_SCREENPIC);
4789     ::video_draw_menu();
4790     ::MenuFadeIn();
4791     ::WaitKeyUp();
4792 
4793     do {
4794         which = ::HandleMenu(&video_items, video_menu, video_draw_switch);
4795 
4796         switch (which) {
4797         case mvl_stretch_to_window:
4798             ::vid_widescreen = !::vid_widescreen;
4799             ::ShootSnd();
4800             ::video_draw_switch(video_items.curpos);
4801             ::vl_update_widescreen();
4802             ::SetupWalls();
4803             ::NewViewSize();
4804             ::SetPlaneViewSize();
4805             ::VL_RefreshScreen();
4806             break;
4807 
4808         default:
4809             break;
4810         }
4811     } while (which >= 0);
4812 
4813     ::MenuFadeOut();
4814 }
4815 ///
4816 
MenuFadeOut()4817 void MenuFadeOut()
4818 {
4819     if (::is_aog()) {
4820         ::VL_FadeOut(0, 255, 44, 0, 0, 10);
4821     } else {
4822         ::VL_FadeOut(0, 255, 40, 44, 44, 10);
4823     }
4824 }
4825 // BBi
4826