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