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 "3d_def.h"
26
27
28 void INL_GetJoyDelta(
29 uint16_t joy,
30 int* dx,
31 int* dy);
32
33 void UpdateRadarGuage();
34 void ClearMemory();
35
36 void GiveWeapon(
37 int weapon);
38
39 void DrawWeapon();
40 void DrawHealth();
41 void DrawKeys();
42 void DrawScore();
43 void ForceUpdateStatusBar();
44 void ClearSplitVWB();
45 void RedrawStatusAreas();
46 void PreloadGraphics();
47 void TryDropPlasmaDetonator();
48 void IN_StartAck();
49 bool IN_CheckAck();
50 void MoveDoors();
51 void MovePWalls();
52 void ConnectAreas();
53 void UpdateSoundLoc();
54
55
56 /*
57 =============================================================================
58
59 GLOBAL VARIABLES
60
61 =============================================================================
62 */
63
64
65 uint8_t music_num = 0;
66
67 #if LOOK_FOR_DEAD_GUYS
68 objtype* DeadGuys[MAXACTORS];
69 uint8_t NumDeadGuys;
70 #endif
71
72 bool madenoise; // true when shooting or screaming
73 uint8_t alerted = 0;
74 uint8_t alerted_areanum;
75
76
77 exit_t playstate;
78
79 int16_t bordertime;
80 bool DebugOk = false;
81 int16_t InstantWin = 0;
82 int16_t InstantQuit = 0;
83
84 uint16_t ExtraRadarFlags = 0;
85
86
87 objtype objlist[MAXACTORS];
88 objtype* new_actor;
89 objtype* player;
90 objtype* lastobj;
91 objtype* objfreelist;
92 objtype* killerobj;
93
94 uint16_t farmapylookup[MAPSIZE];
95 uint8_t* nearmapylookup[MAPSIZE];
96
97 bool singlestep = false;
98 bool godmode; // ,noclip;
99
100 uint8_t tilemap[MAPSIZE][MAPSIZE]; // wall values only
101 uint8_t spotvis[MAPSIZE][MAPSIZE];
102 objtype* actorat[MAPSIZE][MAPSIZE];
103
104
105 //
106 // replacing refresh manager
107 //
108 uint16_t mapwidth, mapheight, tics, realtics;
109 bool compatability;
110 bool usedummy = false;
111 bool nevermark = false;
112 uint8_t* updateptr;
113 uint16_t mapwidthtable[64];
114 uint16_t uwidthtable[UPDATEHIGH];
115 uint16_t blockstarts[UPDATEWIDE * UPDATEHIGH];
116 uint8_t update[UPDATESIZE];
117
118 //
119 // control info
120 //
121 bool mouseenabled;
122 bool joystickenabled;
123 bool joypadenabled;
124 bool joystickprogressive;
125 int16_t joystickport;
126
127 ScanCodes dirscan;
128 ScanCodes buttonscan;
129 Buttons buttonmouse;
130 Buttons buttonjoy;
131
132 const int viewsize = 20;
133
134 bool buttonheld[NUMBUTTONS];
135
136 bool demorecord;
137 bool demoplayback;
138 char* demoptr;
139 char* lastdemoptr;
140 void* demobuffer;
141
142 // Light sourcing flag
143
144 uint8_t lightson;
145
146 //
147 // curent user input
148 //
149 int controlx;
150 int controly; // range from -100 to 100 per tic
151 bool buttonstate[NUMBUTTONS];
152 int strafe_value = 0;
153
154
155 void CenterWindow(
156 uint16_t w,
157 uint16_t h);
158
159 void InitObjList();
160
161 void RemoveObj(
162 objtype* gone);
163
164 void PollControls();
165 void StopMusic();
166
167 void StartMusic(
168 bool preload);
169
170 void PlayLoop();
171
172 void SpaceEntryExit(
173 bool entry);
174
175 void FinishPaletteShifts();
176 void ShowQuickInstructions();
177 void CleanDrawPlayBorder();
178
179 void PopupAutoMap(
180 bool is_shift_pressed);
181
182
183 /*
184 =============================================================================
185
186 LOCAL VARIABLES
187
188 =============================================================================
189 */
190
191
192 objtype dummyobj;
193
194 //
195 // LIST OF SONGS FOR EACH LEVEL
196 //
197
198 using Songs = std::vector<int>;
199
200 Songs songs;
201
initialize_songs()202 void initialize_songs()
203 {
204 if (!::is_ps()) {
205 songs = {
206 // Episode 1
207 INCNRATN_MUS,
208 DRKHALLA_MUS,
209 JUNGLEA_MUS,
210 RACSHUFL_MUS,
211 DRKHALLA_MUS,
212 HIDINGA_MUS,
213 JUNGLEA_MUS,
214 RACSHUFL_MUS,
215 HIDINGA_MUS,
216 DRKHALLA_MUS,
217 INCNRATN_MUS,
218
219 // Episode 2
220 FREEDOMA_MUS,
221 DRKHALLA_MUS,
222 STRUTA_MUS,
223 INTRIGEA_MUS,
224 MEETINGA_MUS,
225 DRKHALLA_MUS,
226 INCNRATN_MUS,
227 RACSHUFL_MUS,
228 JUNGLEA_MUS,
229 GENEFUNK_MUS,
230 THEME_MUS,
231
232 // Episode 3
233 LEVELA_MUS,
234 HIDINGA_MUS,
235 STRUTA_MUS,
236 THEME_MUS,
237 RACSHUFL_MUS,
238 INCNRATN_MUS,
239 GOLDA_MUS,
240 JUNGLEA_MUS,
241 DRKHALLA_MUS,
242 THEWAYA_MUS,
243 FREEDOMA_MUS,
244
245 // Episode 4
246 HIDINGA_MUS,
247 DRKHALLA_MUS,
248 GENEFUNK_MUS,
249 JUNGLEA_MUS,
250 INCNRATN_MUS,
251 GOLDA_MUS,
252 HIDINGA_MUS,
253 JUNGLEA_MUS,
254 DRKHALLA_MUS,
255 THEWAYA_MUS,
256 RUMBAA_MUS,
257
258 // Episode 5
259 RACSHUFL_MUS,
260 SEARCHNA_MUS,
261 JUNGLEA_MUS,
262 HIDINGA_MUS,
263 GENEFUNK_MUS,
264 MEETINGA_MUS,
265 S2100A_MUS,
266 THEME_MUS,
267 INCNRATN_MUS,
268 DRKHALLA_MUS,
269 THEWAYA_MUS,
270
271 // Episode 6
272 TIMEA_MUS,
273 RACSHUFL_MUS,
274 GENEFUNK_MUS,
275 HIDINGA_MUS,
276 S2100A_MUS,
277 THEME_MUS,
278 THEWAYA_MUS,
279 JUNGLEA_MUS,
280 MEETINGA_MUS,
281 DRKHALLA_MUS,
282 INCNRATN_MUS,
283 };
284 } else {
285 songs = {
286 MAJMIN_MUS,
287 STICKS_MUS,
288 MOURNING_MUS,
289 LURKING_MUS,
290 CIRCLES_MUS,
291 TIME_MUS,
292 TOHELL_MUS,
293 FORTRESS_MUS,
294 GIVING_MUS,
295 HARTBEAT_MUS,
296 MOURNING_MUS,
297 MAJMIN_MUS,
298 VACCINAP_MUS,
299 LURKING_MUS,
300 MONASTRY_MUS,
301 TOMBP_MUS,
302 DARKNESS_MUS,
303 MOURNING_MUS,
304 SERPENT_MUS,
305 TIME_MUS,
306 CATACOMB_MUS,
307 PLOT_MUS,
308 GIVING_MUS,
309 VACCINAP_MUS,
310 };
311 }
312 }
313
314
315 /*
316 =============================================================================
317
318 USER CONTROL
319
320 =============================================================================
321 */
322
323 const int BASEMOVE = 35;
324 const int RUNMOVE = 70;
325 const int BASETURN = 35;
326 const int RUNTURN = 70;
327
328 const int JOYSCALE = 2;
329
330
PollKeyboardButtons()331 void PollKeyboardButtons()
332 {
333 if (in_use_modern_bindings) {
334 if (in_is_binding_pressed(e_bi_attack)) {
335 buttonstate[bt_attack] = true;
336 }
337
338 if (in_is_binding_pressed(e_bi_strafe)) {
339 buttonstate[bt_strafe] = true;
340 }
341
342 if (in_is_binding_pressed(e_bi_run)) {
343 buttonstate[bt_run] = true;
344 }
345
346 if (in_is_binding_pressed(e_bi_use)) {
347 buttonstate[bt_use] = true;
348 }
349
350 if (in_is_binding_pressed(e_bi_weapon_1)) {
351 buttonstate[bt_ready_autocharge] = true;
352 }
353
354 if (in_is_binding_pressed(e_bi_weapon_2)) {
355 buttonstate[bt_ready_pistol] = true;
356 }
357
358 if (in_is_binding_pressed(e_bi_weapon_3)) {
359 buttonstate[bt_ready_burst_rifle] = true;
360 }
361
362 if (in_is_binding_pressed(e_bi_weapon_4)) {
363 buttonstate[bt_ready_ion_cannon] = true;
364 }
365
366 if (in_is_binding_pressed(e_bi_weapon_5)) {
367 buttonstate[bt_ready_grenade] = true;
368 }
369
370 if (::is_ps()) {
371 if (in_is_binding_pressed(e_bi_weapon_6)) {
372 buttonstate[bt_ready_bfg_cannon] = true;
373 }
374
375 if (in_is_binding_pressed(e_bi_weapon_7)) {
376 buttonstate[bt_ready_plasma_detonators] = true;
377 }
378 }
379 } else {
380 for (int i = 0; i < NUMBUTTONS; ++i) {
381 if (Keyboard[buttonscan[i]]) {
382 buttonstate[i] = true;
383 }
384 }
385 }
386 }
387
PollMouseButtons()388 void PollMouseButtons()
389 {
390 if (in_use_modern_bindings) {
391 return;
392 }
393
394 uint8_t buttons = IN_MouseButtons();
395
396 if ((buttons & 1) != 0) {
397 buttonstate[buttonmouse[0]] = true;
398 }
399
400 if ((buttons & 2) != 0) {
401 buttonstate[buttonmouse[1]] = true;
402 }
403
404 if ((buttons & 4) != 0) {
405 buttonstate[buttonmouse[2]] = true;
406 }
407 }
408
PollJoystickButtons()409 void PollJoystickButtons()
410 {
411 int16_t buttons;
412
413 buttons = IN_JoyButtons();
414
415 if (joystickport && !joypadenabled) {
416 if (buttons & 4) {
417 buttonstate[buttonjoy[0]] = true;
418 }
419 if (buttons & 8) {
420 buttonstate[buttonjoy[1]] = true;
421 }
422 } else {
423 if (buttons & 1) {
424 buttonstate[buttonjoy[0]] = true;
425 }
426 if (buttons & 2) {
427 buttonstate[buttonjoy[1]] = true;
428 }
429 if (joypadenabled) {
430 if (buttons & 4) {
431 buttonstate[buttonjoy[2]] = true;
432 }
433 if (buttons & 8) {
434 buttonstate[buttonjoy[3]] = true;
435 }
436 }
437 }
438 }
439
PollKeyboardMove()440 void PollKeyboardMove()
441 {
442 bool is_running = in_is_binding_pressed(e_bi_run);
443
444 if (g_always_run) {
445 is_running = !is_running;
446 }
447
448 int value = tics * (is_running ? RUNMOVE : BASEMOVE);
449
450 if (in_is_binding_pressed(e_bi_forward)) {
451 controly -= value;
452 }
453
454 if (in_is_binding_pressed(e_bi_backward)) {
455 controly += value;
456 }
457
458 if (in_is_binding_pressed(e_bi_left)) {
459 controlx -= value;
460 }
461
462 if (in_is_binding_pressed(e_bi_right)) {
463 controlx += value;
464 }
465
466 strafe_value = 0;
467
468 if (in_is_binding_pressed(e_bi_strafe)) {
469 if (in_is_binding_pressed(e_bi_left)) {
470 strafe_value = -value;
471 } else if (in_is_binding_pressed(e_bi_right)) {
472 strafe_value = value;
473 }
474 } else if (in_is_binding_pressed(e_bi_strafe_left)) {
475 strafe_value = -value;
476 } else if (in_is_binding_pressed(e_bi_strafe_right)) {
477 strafe_value = value;
478 }
479 }
480
PollMouseMove()481 void PollMouseMove()
482 {
483 int mousexmove;
484 int mouseymove;
485
486 ::in_get_mouse_deltas(mousexmove, mouseymove);
487 ::in_clear_mouse_deltas();
488
489 auto is_running = ::in_is_binding_pressed(e_bi_run);
490
491 if (g_always_run)
492 {
493 is_running = !is_running;
494 }
495
496 const auto move_scale = 1.0F + (::mouseadjustment / 6.0F);
497
498 auto delta_x = static_cast<float>(mousexmove);
499 auto delta_y = static_cast<float>(mouseymove);
500
501 if (is_running)
502 {
503 delta_x *= 1.5F;
504 delta_y *= 5.0F;
505 }
506
507 if (::in_use_modern_bindings)
508 {
509 delta_y = 0.0F;
510 }
511
512 delta_x *= move_scale;
513 delta_y *= move_scale;
514
515 ::controlx += static_cast<int>(delta_x);
516 ::controly += static_cast<int>(delta_y);
517 }
518
PollJoystickMove()519 void PollJoystickMove()
520 {
521 int joyx;
522 int joyy;
523
524 INL_GetJoyDelta(joystickport, &joyx, &joyy);
525
526 if (joystickprogressive) {
527 if (joyx > 64) {
528 controlx += (joyx - 64) * JOYSCALE * tics;
529 } else if (joyx < -64) {
530 controlx -= (-joyx - 64) * JOYSCALE * tics;
531 }
532 if (joyy > 64) {
533 controlx += (joyy - 64) * JOYSCALE * tics;
534 } else if (joyy < -64) {
535 controly -= (-joyy - 64) * JOYSCALE * tics;
536 }
537 } else if (buttonstate[bt_run]) {
538 if (joyx > 64) {
539 controlx += RUNMOVE * tics;
540 } else if (joyx < -64) {
541 controlx -= RUNMOVE * tics;
542 }
543 if (joyy > 64) {
544 controly += RUNMOVE * tics;
545 } else if (joyy < -64) {
546 controly -= RUNMOVE * tics;
547 }
548 } else {
549 if (joyx > 64) {
550 controlx += BASEMOVE * tics;
551 } else if (joyx < -64) {
552 controlx -= BASEMOVE * tics;
553 }
554 if (joyy > 64) {
555 controly += BASEMOVE * tics;
556 } else if (joyy < -64) {
557 controly -= BASEMOVE * tics;
558 }
559 }
560 }
561
562 /*
563 ===================
564 =
565 = PollControls
566 =
567 = Gets user or demo input, call once each frame
568 =
569 = controlx set between -100 and 100 per tic
570 = controly
571 = buttonheld[] the state of the buttons LAST frame
572 = buttonstate[] the state of the buttons THIS frame
573 =
574 ===================
575 */
PollControls()576 void PollControls()
577 {
578 uint8_t buttonbits;
579
580 controlx = 0;
581 controly = 0;
582 memcpy(buttonheld, buttonstate, sizeof(buttonstate));
583 memset(buttonstate, 0, sizeof(buttonstate));
584
585 if (demoplayback) {
586 //
587 // read commands from demo buffer
588 //
589 buttonbits = *demoptr++;
590 for (int i = 0; i < NUMBUTTONS; ++i)
591 {
592 buttonstate[i] = ((buttonbits & 1) != 0);
593 buttonbits >>= 1;
594 }
595
596 controlx = *demoptr++;
597 controly = *demoptr++;
598 tics = *demoptr++;
599
600 while (TimeCount - lasttimecount < tics) {
601 }
602 lasttimecount = TimeCount;
603
604 if (demoptr == lastdemoptr) {
605 playstate = ex_completed; // demo is done
606
607 }
608 controlx *= tics;
609 controly *= tics;
610
611
612 return;
613 }
614
615 //
616 // get timing info for last frame
617 //
618 CalcTics();
619
620 // BBi
621 ::in_handle_events();
622 //
623 // get button states
624 //
625 PollKeyboardButtons();
626
627 if (mouseenabled) {
628 PollMouseButtons();
629 }
630
631 if (joystickenabled) {
632 PollJoystickButtons();
633 }
634
635 //
636 // get movements
637 //
638 PollKeyboardMove();
639
640 if (mouseenabled) {
641 PollMouseMove();
642 }
643
644 if (joystickenabled) {
645 PollJoystickMove();
646 }
647
648 //
649 // bound movement to a maximum
650 //
651
652 const auto max_control = 1000000;
653 const auto min_control = -max_control;
654
655 if (controlx > max_control)
656 {
657 controlx = max_control;
658 }
659 else if (controlx < min_control)
660 {
661 controlx = min_control;
662 }
663
664 if (controly > max_control)
665 {
666 controly = max_control;
667 }
668 else if (controly < min_control)
669 {
670 controly = min_control;
671 }
672 }
673
674 ///////////////////////////////////////////////////////////////////////////
675 //
676 // CenterWindow() - Generates a window of a given width & height in the
677 // middle of the screen
678 //
679 ///////////////////////////////////////////////////////////////////////////
680
CenterWindow(uint16_t w,uint16_t h)681 void CenterWindow(
682 uint16_t w,
683 uint16_t h)
684 {
685 const auto MAXX = ::vga_ref_width;
686 const auto MAXY = MAXX / 2;
687
688 ::US_DrawWindow(
689 ((MAXX / 8) - w) / 2,
690 ((MAXY / 8) - h) / 2,
691 w,
692 h);
693 }
694
695
696 extern bool PP_step;
697 extern bool sqActive;
698 extern int16_t pickquick;
699
700 bool refresh_screen;
701
702 using JamBuffCmp = std::vector<ScanCode>;
703
704 JamBuffCmp jam_buff_cmp = {
705 ScanCode::sc_j,
706 ScanCode::sc_a,
707 ScanCode::sc_m,
708 }; // jam_buff_cmp
709
710 JamBuffCmp jam_buff = {
711 ScanCode::sc_none,
712 ScanCode::sc_none,
713 ScanCode::sc_none,
714 }; // jam_buff
715
716 char PAUSED_MSG[] = "^ST1^CEGame Paused\r^CEPress any key to resume.^XX";
717
718
719 namespace {
720
721
check_heart_beat_key()722 void check_heart_beat_key()
723 {
724 if (!::is_aog()) {
725 return;
726 }
727
728
729 static bool is_key_released;
730
731 if (::in_is_binding_pressed(e_bi_heart_beat)) {
732 if (is_key_released) {
733 ::g_heart_beat_sound = !::g_heart_beat_sound;
734
735 const auto& message = (
736 ::g_heart_beat_sound ?
737 ::ekg_heartbeat_enabled :
738 ::ekg_heartbeat_disabled);
739
740 DISPLAY_TIMED_MSG(message.c_str(), MP_BONUS, MT_GENERAL);
741 is_key_released = false;
742 }
743 } else {
744 is_key_released = true;
745 }
746 }
747
748
749 } // namespace
750
751
CheckKeys()752 void CheckKeys()
753 {
754 bool one_eighty = false;
755 ScanCode scan;
756 static bool Plus_KeyReleased;
757 static bool Minus_KeyReleased;
758 static bool I_KeyReleased;
759 static bool S_KeyReleased;
760
761 if (screenfaded || demoplayback) { // don't do anything with a faded screen
762 return;
763 }
764
765 scan = LastScan;
766
767 //
768 // SECRET CHEAT CODE: 'JAM'
769 //
770
771 if (!::is_aog_sw()) {
772 if (Keyboard[ScanCode::sc_j] || Keyboard[ScanCode::sc_a] || Keyboard[ScanCode::sc_m]) {
773 if (jam_buff.back() != LastScan) {
774 for (auto i = 1; i < 3; ++i) {
775 jam_buff[i - 1] = jam_buff[i];
776 }
777
778 jam_buff.back() = LastScan;
779 }
780 }
781 }
782
783 CheckMusicToggle();
784
785 if (gamestate.rpower) {
786 if (in_is_binding_pressed(e_bi_radar_magnify)) {
787 if (Plus_KeyReleased && gamestate.rzoom < 2) {
788 UpdateRadarGuage();
789 gamestate.rzoom++;
790 Plus_KeyReleased = false;
791 }
792 } else {
793 Plus_KeyReleased = true;
794 }
795
796 if (in_is_binding_pressed(e_bi_radar_minify)) {
797 if (Minus_KeyReleased && gamestate.rzoom) {
798 UpdateRadarGuage();
799 gamestate.rzoom--;
800 Minus_KeyReleased = false;
801 }
802 } else {
803 Minus_KeyReleased = true;
804 }
805 }
806
807 if (in_is_binding_pressed(e_bi_sfx)) {
808 if (S_KeyReleased) {
809 bool is_enabled = false;
810
811 if (::sd_is_sound_enabled) {
812 ::SD_WaitSoundDone();
813 ::SD_EnableSound(false);
814
815 is_enabled = false;
816 } else {
817 ::ClearMemory();
818
819 if (::sd_has_audio) {
820 ::SD_EnableSound(true);
821 } else {
822 ::SD_EnableSound(false);
823 }
824
825 ::CA_LoadAllSounds();
826
827 is_enabled = true;
828 }
829
830 DISPLAY_TIMED_MSG(
831 is_enabled ? ::SoundOn : ::SoundOff,
832 MP_BONUS,
833 MT_GENERAL);
834
835 S_KeyReleased = false;
836 }
837 } else {
838 S_KeyReleased = true;
839 }
840
841 if (!::is_aog_sw() && Keyboard[ScanCode::sc_return]) {
842 int8_t loop;
843
844 if (jam_buff == jam_buff_cmp) {
845 jam_buff[0] = ScanCode::sc_none;
846
847 for (loop = 0; loop < NUMKEYS; loop++) {
848 if (gamestate.numkeys[static_cast<int>(loop)] < MAXKEYS) {
849 gamestate.numkeys[static_cast<int>(loop)] = 1;
850 }
851 }
852
853 gamestate.health = 100;
854 gamestate.ammo = MAX_AMMO;
855 gamestate.rpower = MAX_RADAR_ENERGY;
856
857 if (!DebugOk) {
858 gamestate.score = 0;
859 gamestate.nextextra = EXTRAPOINTS;
860 }
861
862 gamestate.TimeCount += 42000L;
863
864 for (loop = 0; loop < NUMWEAPONS - 1; loop++) {
865 GiveWeapon(loop);
866 }
867
868 DrawWeapon();
869 DrawHealth();
870 DrawKeys();
871 DrawScore();
872 DISPLAY_TIMED_MSG("\r\r YOU CHEATER!", MP_INTERROGATE, MT_GENERAL);
873 ForceUpdateStatusBar();
874
875 ClearMemory();
876 ClearSplitVWB();
877
878 // BBi
879 #if 0
880 VW_ScreenToScreen(PAGE1START, ::bufferofs, 320, 160);
881 #endif
882
883 Message("\n NOW you're jammin'!! \n");
884
885 IN_ClearKeysDown();
886 IN_Ack();
887
888 CleanDrawPlayBorder();
889 } else if (!in_use_modern_bindings) {
890 one_eighty = true;
891 }
892 }
893
894 // Handle quick turning!
895 //
896 if (!gamestate.turn_around) {
897 // 90 degrees left
898 //
899 if (in_is_binding_pressed(e_bi_quick_left)) {
900 gamestate.turn_around = -90;
901 gamestate.turn_angle = player->angle + 90;
902 if (gamestate.turn_angle > 359) {
903 gamestate.turn_angle -= ANGLES;
904 }
905 }
906
907 // 180 degrees right
908 //
909 if (in_is_binding_pressed(e_bi_turn_around) || one_eighty) {
910 gamestate.turn_around = 180;
911 gamestate.turn_angle = player->angle + 180;
912 if (gamestate.turn_angle > 359) {
913 gamestate.turn_angle -= ANGLES;
914 }
915 }
916
917 // 90 degrees right
918 //
919 if (in_is_binding_pressed(e_bi_quick_right)) {
920 gamestate.turn_around = 90;
921 gamestate.turn_angle = player->angle - 90;
922 if (gamestate.turn_angle < 0) {
923 gamestate.turn_angle += ANGLES;
924 }
925 }
926 }
927
928 //
929 // pause key weirdness can't be checked as a scan code
930 //
931 if (in_is_binding_pressed(e_bi_pause)) {
932 SD_MusicOff();
933 fontnumber = 4;
934 BMAmsg(PAUSED_MSG);
935 IN_Ack();
936 IN_ClearKeysDown();
937 fontnumber = 2;
938 RedrawStatusAreas();
939 SD_MusicOn();
940 Paused = false;
941 ::in_clear_mouse_deltas();
942 return;
943 }
944
945 scan = ScanCode::sc_none;
946
947 if (Keyboard[ScanCode::sc_escape]) {
948 scan = ScanCode::sc_escape;
949 } else if (in_is_binding_pressed(e_bi_help)) {
950 scan = ScanCode::sc_f1;
951 }
952 if (in_is_binding_pressed(e_bi_save)) {
953 scan = ScanCode::sc_f2;
954 } else if (in_is_binding_pressed(e_bi_load)) {
955 scan = ScanCode::sc_f3;
956 } else if (in_is_binding_pressed(e_bi_sound)) {
957 scan = ScanCode::sc_f4;
958 } else if (in_is_binding_pressed(e_bi_controls)) {
959 scan = ScanCode::sc_f6;
960 } else if (in_is_binding_pressed(e_bi_end_game)) {
961 scan = ScanCode::sc_f7;
962 } else if (in_is_binding_pressed(e_bi_quick_save)) {
963 scan = ScanCode::sc_f8;
964 } else if (in_is_binding_pressed(e_bi_quick_load)) {
965 scan = ScanCode::sc_f9;
966 } else if (in_is_binding_pressed(e_bi_quick_exit)) {
967 scan = ScanCode::sc_f10;
968 }
969
970 switch (scan) {
971 case ScanCode::sc_f7: // END GAME
972 case ScanCode::sc_f10: // QUIT TO DOS
973 FinishPaletteShifts();
974 ClearMemory();
975 US_ControlPanel(scan);
976 CleanDrawPlayBorder();
977 return;
978
979 case ScanCode::sc_f2: // SAVE MISSION
980 case ScanCode::sc_f8: // QUICK SAVE
981 // Make sure there's room to save...
982 //
983 ClearMemory();
984 FinishPaletteShifts();
985 if (!CheckDiskSpace(DISK_SPACE_NEEDED, CANT_SAVE_GAME_TXT, cds_id_print)) {
986 CleanDrawPlayBorder();
987 break;
988 }
989
990 case ScanCode::sc_f1: // HELP
991 case ScanCode::sc_f3: // LOAD MISSION
992 case ScanCode::sc_f4: // SOUND MENU
993 case ScanCode::sc_f5: // RESIZE VIEW
994 case ScanCode::sc_f6: // CONTROLS MENU
995 case ScanCode::sc_f9: // QUICK LOAD
996 case ScanCode::sc_escape: // MAIN MENU
997 refresh_screen = true;
998 if (scan < ScanCode::sc_f8) {
999 VW_FadeOut();
1000 }
1001 StopMusic();
1002 ClearMemory();
1003 ClearSplitVWB();
1004 US_ControlPanel(scan);
1005 if (refresh_screen) {
1006 bool old = loadedgame;
1007
1008 loadedgame = false;
1009 DrawPlayScreen(false);
1010 loadedgame = old;
1011 }
1012 ClearMemory();
1013 if (!sqActive || !loadedgame) {
1014 StartMusic(false);
1015 }
1016 IN_ClearKeysDown();
1017 if (loadedgame) {
1018 PreloadGraphics();
1019 loadedgame = false;
1020 DrawPlayScreen(false);
1021 } else if (!refresh_screen) {
1022 CleanDrawPlayBorder();
1023 }
1024 if (!sqActive) {
1025 StartMusic(false);
1026 }
1027 return;
1028
1029 default:
1030 break;
1031 }
1032
1033 scan = ScanCode::sc_none;
1034
1035 if (in_is_binding_pressed(e_bi_stats)) {
1036 PopupAutoMap(Keyboard[ScanCode::sc_left_shift] || Keyboard[ScanCode::sc_right_shift]);
1037 }
1038
1039 if (Keyboard[ScanCode::sc_back_quote]) {
1040 Keyboard[ScanCode::sc_back_quote] = 0;
1041
1042 if (::is_ps()) {
1043 ::TryDropPlasmaDetonator();
1044 }
1045 }
1046
1047
1048 if ((DebugOk || gamestate.flags & GS_MUSIC_TEST) && (Keyboard[ScanCode::sc_backspace])) {
1049 uint8_t old_num = music_num;
1050
1051 if (gamestate.flags & GS_MUSIC_TEST) {
1052 if (Keyboard[ScanCode::sc_left_arrow]) {
1053 if (music_num) {
1054 music_num--;
1055 }
1056 Keyboard[ScanCode::sc_left_arrow] = false;
1057 } else if (Keyboard[ScanCode::sc_right_arrow]) {
1058 if (music_num < LASTMUSIC - 1) {
1059 music_num++;
1060 }
1061 Keyboard[ScanCode::sc_right_arrow] = false;
1062 }
1063
1064 if (old_num != music_num) {
1065 ClearMemory();
1066
1067 delete [] audiosegs[STARTMUSIC + old_num];
1068 audiosegs[STARTMUSIC + old_num] = nullptr;
1069
1070 StartMusic(false);
1071 DrawScore();
1072 }
1073 }
1074
1075 if (old_num == music_num) {
1076 fontnumber = 4;
1077 SETFONTCOLOR(0, 15);
1078 if (DebugKeys()) {
1079 CleanDrawPlayBorder();
1080 }
1081
1082 ::in_clear_mouse_deltas();
1083
1084 lasttimecount = TimeCount;
1085 return;
1086 }
1087 }
1088
1089 if (in_is_binding_pressed(e_bi_attack_info)) {
1090 if (I_KeyReleased) {
1091 gamestate.flags ^= GS_ATTACK_INFOAREA;
1092 if (gamestate.flags & GS_ATTACK_INFOAREA) {
1093 DISPLAY_TIMED_MSG(attacker_info_enabled, MP_ATTACK_INFO, MT_GENERAL);
1094 } else {
1095 DISPLAY_TIMED_MSG(attacker_info_disabled, MP_ATTACK_INFO, MT_GENERAL);
1096 }
1097 I_KeyReleased = false;
1098 }
1099 } else {
1100 I_KeyReleased = true;
1101 }
1102
1103 if (in_is_binding_pressed(e_bi_ceiling)) {
1104 gamestate.flags ^= GS_DRAW_CEILING;
1105 in_reset_binding_state(e_bi_ceiling);
1106 }
1107
1108 if (in_is_binding_pressed(e_bi_flooring)) {
1109 ThreeDRefresh();
1110 ThreeDRefresh();
1111
1112 gamestate.flags ^= GS_DRAW_FLOOR;
1113
1114 in_reset_binding_state(e_bi_flooring);
1115 }
1116
1117 if (in_is_binding_pressed(e_bi_lightning)) {
1118 in_reset_binding_state(e_bi_lightning);
1119 gamestate.flags ^= GS_LIGHTING;
1120 }
1121
1122 check_heart_beat_key();
1123 }
1124
1125
1126 // -------------------------------------------------------------------------
1127 // CheckMusicToggle()
1128 // -------------------------------------------------------------------------
CheckMusicToggle()1129 void CheckMusicToggle()
1130 {
1131 static bool M_KeyReleased;
1132
1133 if (in_is_binding_pressed(e_bi_music)) {
1134 bool toggle = M_KeyReleased;
1135
1136 if (!::is_aog_sw()) {
1137 if (jam_buff[0] == ScanCode::sc_j &&
1138 jam_buff[1] == ScanCode::sc_a &&
1139 jam_buff[2] == ScanCode::sc_m)
1140 {
1141 toggle = false;
1142 }
1143 }
1144
1145 if (toggle) {
1146 bool is_enabled = false;
1147
1148 if (!::sd_has_audio) {
1149 DISPLAY_TIMED_MSG(NoAdLibCard, MP_BONUS, MT_GENERAL);
1150
1151 ::sd_play_player_sound(NOWAYSND, bstone::AC_NO_WAY);
1152 return;
1153 } else if (::sd_is_music_enabled) {
1154 ::SD_EnableMusic(false);
1155 is_enabled = false;
1156 } else {
1157 ::SD_EnableMusic(true);
1158 StartMusic(false);
1159 is_enabled = true;
1160 }
1161
1162 DISPLAY_TIMED_MSG(
1163 is_enabled ? MusicOn : MusicOff,
1164 MP_BONUS,
1165 MT_GENERAL);
1166
1167 M_KeyReleased = false;
1168 }
1169 } else {
1170 M_KeyReleased = true;
1171 }
1172 }
1173
1174
1175 char Computing[] = { "Computing..." };
1176
1177
PopupAutoMap(bool is_shift_pressed)1178 void PopupAutoMap(
1179 bool is_shift_pressed)
1180 {
1181 const int16_t BASE_X = (::is_ps() ? 64 : 40);
1182 const int16_t BASE_Y = 44;
1183
1184 ThreeDRefresh();
1185 ThreeDRefresh();
1186
1187 SD_StopSound();
1188 ClearMemory();
1189 CacheDrawPic(BASE_X, BASE_Y, AUTOMAPPIC);
1190
1191 if (::is_aog()) {
1192 bool show_whole_map = true;
1193 int overlay_flags = OV_KEYS;
1194
1195 if (is_shift_pressed) {
1196 show_whole_map = !show_whole_map;
1197 }
1198
1199 if (g_rotated_automap) {
1200 show_whole_map = !show_whole_map;
1201 }
1202
1203 if (show_whole_map) {
1204 overlay_flags |= OV_WHOLE_MAP;
1205 }
1206
1207 ::ShowOverhead(BASE_X + 4, BASE_Y + 4, 32, 0, overlay_flags);
1208
1209 ShowStats(BASE_X + 157, BASE_Y + 25, ss_quick, &gamestuff.level[gamestate.mapon].stats);
1210 } else {
1211 ShowStats(BASE_X + 101, BASE_Y + 22, ss_quick, &gamestuff.level[gamestate.mapon].stats);
1212 }
1213
1214 while (Keyboard[ScanCode::sc_back_quote]) {
1215 CalcTics();
1216
1217 if (!::is_ps()) {
1218 ::CycleColors();
1219 ::in_handle_events();
1220 }
1221 }
1222
1223 IN_StartAck();
1224 while (!IN_CheckAck()) {
1225 CalcTics();
1226
1227 if (!::is_ps()) {
1228 ::CycleColors();
1229 ::in_handle_events();
1230 }
1231 }
1232
1233 CleanDrawPlayBorder();
1234 IN_ClearKeysDown();
1235 }
1236
1237
1238 /*
1239 #############################################################################
1240
1241 The objlist data structure
1242
1243 #############################################################################
1244
1245 objlist containt structures for every actor currently playing. The structure
1246 is accessed as a linked list starting at *player, ending when ob->next ==
1247 NULL. GetNewObj inserts a new object at the end of the list, meaning that
1248 if an actor spawn another actor, the new one WILL get to think and react the
1249 same frame. RemoveObj unlinks the given object and returns it to the free
1250 list, but does not damage the objects ->next pointer, so if the current object
1251 removes itself, a linked list following loop can still safely get to the
1252 next element.
1253
1254 <backwardly linked free list>
1255
1256 #############################################################################
1257 */
1258
1259
1260 int objcount;
1261
1262 /*
1263 =========================
1264 =
1265 = InitActorList
1266 =
1267 = Call to clear out the actor object lists returning them all to the free
1268 = list. Allocates a special spot for the player.
1269 =
1270 =========================
1271 */
InitActorList()1272 void InitActorList()
1273 {
1274 int16_t i;
1275
1276 //
1277 // init the actor lists
1278 //
1279 #if LOOK_FOR_DEAD_GUYS
1280 NumDeadGuys = 0;
1281 memset(DeadGuys, 0, sizeof(DeadGuys));
1282 #endif
1283
1284 memset(statobjlist, 0, sizeof(statobjlist));
1285 for (i = 0; i < MAXACTORS; i++) {
1286 objlist[i].prev = &objlist[i + 1];
1287 objlist[i].next = nullptr;
1288 }
1289
1290 objlist[MAXACTORS - 1].prev = nullptr;
1291
1292 objfreelist = &objlist[0];
1293 lastobj = nullptr;
1294
1295 objcount = 0;
1296
1297 //
1298 // give the player the first free spots
1299 //
1300 GetNewActor();
1301 player = new_actor;
1302 }
1303
1304 /*
1305 =========================
1306 =
1307 = GetNewActor
1308 =
1309 = Sets the global variable new to point to a free spot in objlist.
1310 = The free spot is inserted at the end of the liked list
1311 =
1312 = When the object list is full, the caller can either have it bomb out ot
1313 = return a dummy object pointer that will never get used
1314 =
1315 =========================
1316 */
GetNewActor()1317 void GetNewActor()
1318 {
1319 if (objcount >= MAXACTORS - 1) {
1320 objtype* obj = player->next;
1321
1322 while (obj) {
1323 if ((obj->flags & (FL_DEADGUY | FL_VISABLE)) == FL_DEADGUY) {
1324 RemoveObj(obj);
1325 obj = nullptr;
1326 } else {
1327 obj = obj->next;
1328 }
1329 }
1330 }
1331
1332 if (!objfreelist) {
1333 if (usedummy) {
1334 new_actor = &dummyobj;
1335 memset(new_actor, 0, sizeof(*new_actor));
1336 } else {
1337 ::Quit("No free spots in objlist.");
1338 }
1339 } else {
1340 new_actor = objfreelist;
1341 objfreelist = new_actor->prev;
1342
1343 memset(new_actor, 0, sizeof(*new_actor));
1344
1345 if (lastobj) {
1346 lastobj->next = new_actor;
1347 }
1348
1349 new_actor->prev = lastobj; // new_actor->next is allready nullptr from memset
1350
1351 lastobj = new_actor;
1352
1353 objcount++;
1354 }
1355 }
1356
1357 /*
1358 =========================
1359 =
1360 = RemoveObj
1361 =
1362 = Add the given object back into the free list, and unlink it from it's
1363 = neighbors
1364 =
1365 =========================
1366 */
RemoveObj(objtype * gone)1367 void RemoveObj(
1368 objtype* gone)
1369 {
1370 if (gone == &dummyobj) {
1371 return;
1372 }
1373
1374 if (gone == player) {
1375 ::Quit("Tried to remove the player.");
1376 }
1377
1378 gone->state = nullptr;
1379
1380 //
1381 // fix the next object's back link
1382 //
1383 if (gone == lastobj) {
1384 lastobj = (objtype*)gone->prev;
1385 } else {
1386 gone->next->prev = gone->prev;
1387 }
1388
1389 //
1390 // fix the previous object's forward link
1391 //
1392 gone->prev->next = gone->next;
1393
1394 //
1395 // add it back in to the free list
1396 //
1397 gone->prev = objfreelist;
1398 objfreelist = gone;
1399
1400 objcount--;
1401 }
1402
StopMusic()1403 void StopMusic()
1404 {
1405 SD_MusicOff();
1406 }
1407
1408 // -------------------------------------------------------------------------
1409 // StartMusic()
1410 // o preload = true, music is cached but not started
1411 // -------------------------------------------------------------------------
StartMusic(bool preload)1412 void StartMusic(
1413 bool preload)
1414 {
1415 int musicchunk;
1416
1417 SD_MusicOff();
1418
1419 if (!::is_ps()) {
1420 musicchunk = songs[gamestate.mapon + gamestate.episode * MAPS_WITH_STATS];
1421 } else {
1422 if (playstate == ex_victorious) {
1423 musicchunk = FORTRESS_MUS;
1424 } else {
1425 musicchunk = songs[gamestate.mapon + gamestate.episode * MAPS_PER_EPISODE];
1426 }
1427 }
1428
1429 if (!audiosegs[STARTMUSIC + musicchunk]) {
1430 CA_CacheAudioChunk(static_cast<int16_t>(STARTMUSIC + musicchunk));
1431 }
1432
1433 {
1434 if (!preload) {
1435 ::SD_StartMusic(musicchunk);
1436 }
1437 }
1438 }
1439
1440
1441 const int NUMREDSHIFTS = 6;
1442 const int REDSTEPS = 8;
1443
1444 const int NUMWHITESHIFTS = 3;
1445 const int WHITESTEPS = 20;
1446 const int WHITETICS = 6;
1447
1448
1449 uint8_t redshifts[NUMREDSHIFTS][768];
1450 uint8_t whiteshifts[NUMREDSHIFTS][768];
1451
1452 int damagecount;
1453 int bonuscount;
1454 bool palshifted;
1455
1456
InitRedShifts()1457 void InitRedShifts()
1458 {
1459 //
1460 // fade through intermediate frames
1461 //
1462 for (int i = 1; i <= NUMREDSHIFTS; i++) {
1463 uint8_t* workptr = &redshifts[i - 1][0];
1464 const uint8_t* baseptr = vgapal;
1465
1466 for (int j = 0; j <= 255; ++j) {
1467 int delta = 64 - baseptr[0];
1468
1469 workptr[0] = static_cast<uint8_t>(
1470 baseptr[0] + ((delta * i) / REDSTEPS));
1471
1472 delta = -baseptr[1];
1473
1474 workptr[1] = static_cast<uint8_t>(
1475 baseptr[1] + ((delta * i) / REDSTEPS));
1476
1477 delta = -baseptr[2];
1478
1479 workptr[2] = static_cast<uint8_t>(
1480 baseptr[2] + ((delta * i) / REDSTEPS));
1481
1482 baseptr += 3;
1483 workptr += 3;
1484 }
1485 }
1486
1487 for (int i = 1; i <= NUMWHITESHIFTS; i++) {
1488 uint8_t* workptr = &whiteshifts[i - 1][0];
1489 const uint8_t* baseptr = vgapal;
1490
1491 for (int j = 0; j <= 255; ++j) {
1492 int delta = 64 - baseptr[0];
1493
1494 workptr[0] = static_cast<uint8_t>(
1495 baseptr[0] + ((delta * i) / WHITESTEPS));
1496
1497 delta = 62 - baseptr[1];
1498
1499 workptr[1] = static_cast<uint8_t>(
1500 baseptr[1] + ((delta * i) / WHITESTEPS));
1501
1502 delta = 0 - baseptr[2];
1503
1504 workptr[2] = static_cast<uint8_t>(
1505 baseptr[2] + ((delta * i) / WHITESTEPS));
1506
1507 baseptr += 3;
1508 workptr += 3;
1509 }
1510 }
1511 }
1512
ClearPaletteShifts()1513 void ClearPaletteShifts()
1514 {
1515 bonuscount = 0;
1516 damagecount = 0;
1517 }
1518
StartBonusFlash()1519 void StartBonusFlash()
1520 {
1521 // white shift palette
1522 bonuscount = NUMWHITESHIFTS * WHITETICS;
1523 }
1524
StartDamageFlash(int damage)1525 void StartDamageFlash(
1526 int damage)
1527 {
1528 damagecount += damage;
1529 }
1530
UpdatePaletteShifts()1531 void UpdatePaletteShifts()
1532 {
1533 int red = 0;
1534 int white = 0;
1535
1536 if (bonuscount > 0) {
1537 white = (bonuscount / WHITETICS) + 1;
1538
1539 if (white > NUMWHITESHIFTS) {
1540 white = NUMWHITESHIFTS;
1541 }
1542
1543 bonuscount -= tics;
1544
1545 if (bonuscount < 0) {
1546 bonuscount = 0;
1547 }
1548 } else {
1549 white = 0;
1550 }
1551
1552 if (damagecount > 0) {
1553 red = (damagecount / 10) + 1;
1554
1555 if (red > NUMREDSHIFTS) {
1556 red = NUMREDSHIFTS;
1557 }
1558
1559 damagecount -= tics;
1560
1561 if (damagecount < 0) {
1562 damagecount = 0;
1563 }
1564 } else {
1565 red = 0;
1566 }
1567
1568 if (red > 0) {
1569 // BBi
1570 #if 0
1571 VW_WaitVBL(1);
1572 #endif
1573 VL_SetPalette(0, 256, redshifts[red - 1]);
1574 palshifted = true;
1575 } else if (white > 0) {
1576 // BBi
1577 #if 0
1578 VW_WaitVBL(1);
1579 #endif
1580 VL_SetPalette(0, 256, whiteshifts[white - 1]);
1581 palshifted = true;
1582 } else if (palshifted) {
1583 // BBi
1584 #if 0
1585 VW_WaitVBL(1);
1586 #endif
1587 VL_SetPalette(0, 256, vgapal); // back to normal
1588 palshifted = false;
1589 }
1590 }
1591
FinishPaletteShifts()1592 void FinishPaletteShifts()
1593 {
1594 if (palshifted) {
1595 palshifted = false;
1596 // BBi
1597 #if 0
1598 VW_WaitVBL(1);
1599 #endif
1600 VL_SetPalette(0, 256, vgapal);
1601 ::VL_RefreshScreen();
1602 }
1603 }
1604
DoActor(objtype * ob)1605 void DoActor(
1606 objtype* ob)
1607 {
1608 void (* think)(
1609 objtype*);
1610
1611 objtype* actor;
1612
1613
1614 if (ob->flags & FL_FREEZE) {
1615 return;
1616 }
1617
1618 if (ob->flags & FL_BARRIER) {
1619 actor = actorat[ob->tilex][ob->tiley];
1620 if (BARRIER_STATE(ob) == bt_ON) {
1621 if (actor) {
1622 int16_t damage = 0;
1623
1624 actor->flags |= FL_BARRIER_DAMAGE;
1625 if ((US_RndT() < 0x7f) && (actor->flags & FL_SHOOTABLE)) {
1626 switch (ob->obclass) {
1627 case arc_barrierobj: // arc barrier - Mild Damage
1628 damage = 500; // 100
1629 break;
1630
1631 case post_barrierobj: // post barrier - Butt kicker
1632 damage = 500;
1633 break;
1634
1635 default:
1636 break;
1637 }
1638 DamageActor(actor, damage, ob);
1639 }
1640 }
1641 } else if (actor) {
1642 actor->flags &= ~FL_BARRIER_DAMAGE;
1643 }
1644 }
1645
1646 if (!ob->active && !areabyplayer[ob->areanumber]) {
1647 return;
1648 }
1649
1650 if (!(ob->flags & (FL_NONMARK | FL_NEVERMARK))) {
1651 actorat[ob->tilex][ob->tiley] = nullptr;
1652 }
1653
1654 //
1655 // non transitional object
1656 //
1657
1658 if (!ob->ticcount) {
1659 think = ob->state->think;
1660 if (think) {
1661 think(ob);
1662 if (!ob->state) {
1663 RemoveObj(ob);
1664 return;
1665 }
1666 }
1667
1668 if (!(ob->flags & FL_NEVERMARK)) {
1669 if (ob->flags & FL_NONMARK) {
1670 if (actorat[ob->tilex][ob->tiley]) {
1671 return;
1672 }
1673 }
1674 actorat[ob->tilex][ob->tiley] = ob;
1675 }
1676 return;
1677 }
1678
1679 //
1680 // transitional object
1681 //
1682 ob->ticcount -= tics;
1683 while (ob->ticcount <= 0) {
1684 think = ob->state->action; // end of state action
1685 if (think) {
1686 think(ob);
1687 if (!ob->state) {
1688 RemoveObj(ob);
1689 return;
1690 }
1691 }
1692
1693 ob->state = ob->state->next;
1694
1695 if (!ob->state) {
1696 RemoveObj(ob);
1697 return;
1698 }
1699
1700 if (!ob->state->tictime) {
1701 ob->ticcount = 0;
1702 goto think;
1703 }
1704
1705 ob->ticcount = static_cast<int16_t>(ob->ticcount + ob->state->tictime);
1706 }
1707
1708 think:
1709 //
1710 // think
1711 //
1712 think = ob->state->think;
1713 if (think) {
1714 think(ob);
1715 if (!ob->state) {
1716 RemoveObj(ob);
1717 return;
1718 }
1719 }
1720
1721 if (!(ob->flags & FL_NEVERMARK)) {
1722 if (ob->flags & FL_NONMARK) {
1723 if (actorat[ob->tilex][ob->tiley]) {
1724 return;
1725 }
1726 }
1727 actorat[ob->tilex][ob->tiley] = ob;
1728 }
1729 return;
1730 }
1731
1732
1733 extern bool ShowQuickMsg;
1734
1735
PlayLoop()1736 void PlayLoop()
1737 {
1738 bool reset_areas = false;
1739 objtype* obj;
1740
1741 lasttimecount = 0;
1742 TimeCount = 0;
1743 playstate = ex_stillplaying;
1744
1745 framecount = frameon = 0;
1746 pwallstate = anglefrac = 0;
1747 memset(buttonstate, 0, sizeof(buttonstate));
1748 ClearPaletteShifts();
1749 ForceUpdateStatusBar();
1750
1751 ::in_clear_mouse_deltas();
1752
1753 tics = 1; // for first time through
1754 if (demoplayback) {
1755 IN_StartAck();
1756 }
1757
1758 do {
1759 PollControls();
1760
1761 //
1762 // actor thinking
1763 //
1764 madenoise = false;
1765
1766 if (alerted) {
1767 alerted--;
1768 }
1769
1770 MoveDoors();
1771 MovePWalls();
1772
1773 for (obj = player; obj; obj = obj->next) {
1774 if ((obj != player) && (Keyboard[ScanCode::sc_6] || Keyboard[ScanCode::sc_7]) && Keyboard[ScanCode::sc_8] && DebugOk) {
1775 if (!reset_areas) {
1776 memset(areabyplayer, 1, sizeof(areabyplayer));
1777 }
1778 reset_areas = true;
1779
1780 if ((((!(obj->flags & FL_INFORMANT)) && (obj->flags & FL_SHOOTABLE))) ||
1781 (obj->obclass == liquidobj && !(obj->flags & FL_DEADGUY)))
1782 {
1783 DamageActor(obj, 1000, player);
1784 }
1785 } else if (reset_areas) {
1786 ConnectAreas();
1787 reset_areas = false;
1788 }
1789 DoActor(obj);
1790 }
1791
1792 if (NumEAWalls) {
1793 CheckSpawnEA();
1794 }
1795
1796 if ((!GoldsternInfo.GoldSpawned) && GoldsternInfo.SpawnCnt) {
1797 CheckSpawnGoldstern();
1798 }
1799
1800 UpdatePaletteShifts();
1801
1802
1803 ThreeDRefresh();
1804
1805 gamestate.TimeCount += tics;
1806
1807 UpdateSoundLoc(); // JAB
1808
1809 if (screenfaded & !playstate) {
1810 VW_FadeIn();
1811 }
1812
1813 // Display first-time instructions.
1814 //
1815 if (ShowQuickMsg) {
1816 ShowQuickInstructions();
1817 }
1818
1819 CheckKeys();
1820
1821 if (demoplayback && demoptr == lastdemoptr) {
1822 playstate = ex_title;
1823 }
1824
1825 //
1826 // debug aids
1827 //
1828 if (singlestep) {
1829 VW_WaitVBL(14);
1830 lasttimecount = TimeCount;
1831 }
1832
1833 if ((demoplayback) && (IN_CheckAck())) {
1834 IN_ClearKeysDown();
1835 playstate = ex_abort;
1836 }
1837
1838
1839 } while (!playstate && !startgame);
1840
1841 if (playstate != ex_died) {
1842 FinishPaletteShifts();
1843 }
1844
1845 gamestate.flags &= ~GS_VIRGIN_LEVEL;
1846 }
1847
ShowQuickInstructions()1848 void ShowQuickInstructions()
1849 {
1850 ::ShowQuickMsg = false;
1851
1852 if (::demoplayback ||
1853 (::is_ps() && (::gamestate.mapon > 0)) ||
1854 (::gamestate.flags & GS_QUICKRUN) != 0)
1855 {
1856 return;
1857 }
1858
1859 ::ThreeDRefresh();
1860 ::ThreeDRefresh();
1861 ::ClearMemory();
1862 ::WindowX = 0;
1863 ::WindowY = 16;
1864 ::WindowW = 320;
1865 ::WindowH = 168;
1866 ::CacheMessage(QUICK_INFO1_TEXT);
1867
1868 if (!::IN_UserInput(120)) {
1869 ::CacheMessage(QUICK_INFO2_TEXT);
1870 ::IN_Ack();
1871 }
1872
1873 ::IN_ClearKeysDown();
1874 ::CleanDrawPlayBorder();
1875 }
1876
CleanDrawPlayBorder()1877 void CleanDrawPlayBorder()
1878 {
1879 DrawPlayBorder();
1880 ThreeDRefresh();
1881 DrawPlayBorder();
1882 ThreeDRefresh();
1883 DrawPlayBorder();
1884 }
1885