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 #include "bstone_generic_fizzle_fx.h"
27 
28 
29 #define MASKABLE_DOORS (0)
30 #define MASKABLE_POSTS (MASKABLE_DOORS)
31 
32 
33 /*
34 =============================================================================
35 
36  LOCAL CONSTANTS
37 
38 =============================================================================
39 */
40 
41 // the door is the last picture before the sprites
42 #define DOORWALL (PMSpriteStart - (NUMDOORTYPES))
43 
44 #define ACTORSIZE (0x4000)
45 
46 
47 void DrawRadar();
48 void DrawLSPost();
49 void DrawPost();
50 
51 void GetBonus(
52     statobj_t* check);
53 
54 void ScaleLSShape(
55     int xcenter,
56     int shapenum,
57     int height,
58     int8_t lighting);
59 
60 void DrawAmmoPic();
61 void DrawScoreNum();
62 void DrawWeaponPic();
63 void DrawAmmoNum();
64 void DrawKeyPics();
65 void DrawHealthNum();
66 void UpdateStatusBar();
67 int16_t NextBuffer();
68 void UpdateRadarGuage();
69 
70 
71 /*
72 =============================================================================
73 
74  GLOBAL VARIABLES
75 
76 =============================================================================
77 */
78 
79 //
80 // player interface stuff
81 //
82 int16_t weaponchangetics;
83 int16_t itemchangetics;
84 int16_t bodychangetics;
85 int16_t plaqueon;
86 int16_t plaquetime
87 ;int16_t getpic;
88 
89 star_t* firststar;
90 star_t* laststar;
91 
92 
93 int32_t lasttimecount;
94 int32_t frameon;
95 int32_t framecount;
96 
97 WallHeight wallheight;
98 
99 fixed mindist = MINDIST;
100 
101 
102 //
103 // math tables
104 //
105 std::vector<int> pixelangle;
106 int finetangent[FINEANGLES / 4];
107 int sintable[ANGLES + (ANGLES / 4) + 1];
108 int* costable = &sintable[ANGLES / 4];
109 
110 //
111 // refresh variables
112 //
113 int viewx;
114 int viewy; // the focal point
115 int viewangle;
116 int viewsin;
117 int viewcos;
118 
119 char thetile[64];
120 uint8_t* mytile;
121 
122 
123 fixed FixedByFrac(
124     fixed a,
125     fixed b);
126 
127 void TransformActor(
128     objtype* ob);
129 
130 void BuildTables();
131 void ClearScreen();
132 
133 int16_t CalcRotate(
134     objtype* ob);
135 
136 void DrawScaleds();
137 void CalcTics();
138 void ThreeDRefresh();
139 
140 
141 //
142 // wall optimization variables
143 //
144 int lastside; // true for vertical
145 int lastintercept;
146 int lasttilehit;
147 
148 //
149 // ray tracing variables
150 //
151 int focaltx;
152 int focalty;
153 int viewtx;
154 int viewty;
155 
156 int midangle;
157 int angle;
158 int xpartial;
159 int ypartial;
160 int xpartialup;
161 int xpartialdown;
162 int ypartialup;
163 int ypartialdown;
164 int xinttile;
165 int yinttile;
166 
167 int tilehit;
168 int pixx;
169 
170 int xtile;
171 int ytile;
172 int xtilestep;
173 int ytilestep;
174 int xintercept;
175 int yintercept;
176 int xstep;
177 int ystep;
178 
179 int16_t horizwall[MAXWALLTILES];
180 int16_t vertwall[MAXWALLTILES];
181 
182 
183 uint16_t viewflags;
184 extern uint8_t lightson;
185 extern const uint8_t rndtable[256];
186 
187 // Global Cloaked Shape flag.
188 bool cloaked_shape = false;
189 
190 
191 /*
192 =============================================================================
193 
194  LOCAL VARIABLES
195 
196 =============================================================================
197 */
198 
199 void AsmRefresh();
200 void NoWallAsmRefresh();
201 
202 
203 // BBi
204 static int last_texture_offset = -1;
205 static const uint8_t* last_texture_data = nullptr;
206 
207 
208 /*
209 ========================
210 =
211 = FixedByFrac
212 =
213 = multiply a 16/16 bit, 2's complement fixed point number by a 16 bit
214 = fraction, passed as a signed magnitude 32 bit number
215 =
216 ========================
217 */
FixedByFrac(fixed a,fixed b)218 fixed FixedByFrac(
219     fixed a,
220     fixed b)
221 {
222     int b_sign;
223     uint32_t ub;
224     int32_t fracs;
225     int32_t ints;
226     int32_t result;
227 
228     b_sign = (b < 0) ? -1 : 1;
229 
230     if (b_sign < 0) {
231         a = -a;
232         b_sign = -b_sign;
233     }
234 
235     ub = (uint32_t)b & 0xFFFF;
236     fracs = (((uint32_t)a & 0xFFFF) * ub) >> 16;
237     ints = (a >> 16) * ub;
238     result = ints + fracs;
239     result *= b_sign;
240 
241     return result;
242 }
243 
244 /*
245 ========================
246 =
247 = TransformActor
248 =
249 = Takes paramaters:
250 =   gx,gy : globalx/globaly of point
251 =
252 = globals:
253 =   viewx,viewy         : point of view
254 =   viewcos,viewsin     : sin/cos of viewangle
255 =   scale               : conversion from global value to screen value
256 =
257 = sets:
258 =   screenx,transx,transy,screenheight: projected edge location and size
259 =
260 ========================
261 */
TransformActor(objtype * ob)262 void TransformActor(
263     objtype* ob)
264 {
265     fixed gx, gy, gxt, gyt, nx, ny;
266     int32_t temp;
267     int32_t q;
268     int32_t r;
269 
270 //
271 // translate point to view centered coordinates
272 //
273     gx = ob->x - viewx;
274     gy = ob->y - viewy;
275 
276 //
277 // calculate newx
278 //
279     gxt = FixedByFrac(gx, viewcos);
280     gyt = FixedByFrac(gy, viewsin);
281     nx = gxt - gyt - ACTORSIZE; // fudge the shape forward a bit, because
282     // the midpoint could put parts of the shape
283     // into an adjacent wall
284 
285 //
286 // calculate newy
287 //
288     gxt = FixedByFrac(gx, viewsin);
289     gyt = FixedByFrac(gy, viewcos);
290     ny = gyt + gxt;
291 
292 //
293 // calculate perspective ratio
294 //
295     ob->transx = nx;
296     ob->transy = ny;
297 
298     if (nx < mindist) { // too close, don't overflow the divide
299         ob->viewheight = 0;
300         return;
301     }
302 
303     ob->viewx = static_cast<int16_t>(centerx + ny * scale / nx); // DEBUG: use assembly divide
304 
305     q = (heightnumerator / (nx >> 8)) & 0xFFFF;
306     r = (heightnumerator % (nx >> 8)) & 0xFFFF;
307     temp = (r << 16) | q;
308 
309     ob->viewheight = static_cast<uint16_t>(temp);
310 }
311 
312 /*
313 ========================
314 =
315 = TransformTile
316 =
317 = Takes paramaters:
318 =   tx,ty               : tile the object is centered in
319 =
320 = globals:
321 =   viewx,viewy         : point of view
322 =   viewcos,viewsin     : sin/cos of viewangle
323 =   scale               : conversion from global value to screen value
324 =
325 = sets:
326 =   screenx,transx,transy,screenheight: projected edge location and size
327 =
328 = Returns true if the tile is withing getting distance
329 =
330 ========================
331 */
TransformTile(int16_t tx,int16_t ty,int16_t * dispx,int16_t * dispheight)332 bool TransformTile(
333     int16_t tx,
334     int16_t ty,
335     int16_t* dispx,
336     int16_t* dispheight)
337 {
338     fixed gx, gy, gxt, gyt, nx, ny;
339     int32_t temp;
340     int32_t q;
341     int32_t r;
342 
343 //
344 // translate point to view centered coordinates
345 //
346     gx = ((int32_t)tx << TILESHIFT) + 0x8000 - viewx;
347     gy = ((int32_t)ty << TILESHIFT) + 0x8000 - viewy;
348 
349 //
350 // calculate newx
351 //
352     gxt = FixedByFrac(gx, viewcos);
353     gyt = FixedByFrac(gy, viewsin);
354     nx = gxt - gyt - 0x2000; // 0x2000 is size of object
355 
356 //
357 // calculate newy
358 //
359     gxt = FixedByFrac(gx, viewsin);
360     gyt = FixedByFrac(gy, viewcos);
361     ny = gyt + gxt;
362 
363 
364 //
365 // calculate perspective ratio
366 //
367     if (nx < mindist) { // too close, don't overflow the divide
368         *dispheight = 0;
369         return false;
370     }
371 
372     *dispx = static_cast<int16_t>(centerx + ny * scale / nx); // DEBUG: use assembly divide
373 
374     q = (heightnumerator / (nx >> 8)) & 0xFFFF;
375     r = (heightnumerator % (nx >> 8)) & 0xFFFF;
376     temp = (r << 16) | q;
377 
378     *dispheight = static_cast<int16_t>(temp);
379 
380 //
381 // see if it should be grabbed
382 //
383     if (nx < TILEGLOBAL && ny > -TILEGLOBAL / 2 && ny < TILEGLOBAL / 2) {
384         return true;
385     } else {
386         return false;
387     }
388 }
389 
390 /*
391 ====================
392 =
393 = CalcHeight
394 =
395 = Calculates the height of xintercept,yintercept from viewx,viewy
396 =
397 ====================
398 */
CalcHeight()399 int CalcHeight()
400 {
401     int gx = xintercept - viewx;
402     int gxt = FixedByFrac(gx, viewcos);
403 
404     int gy = yintercept - viewy;
405     int gyt = FixedByFrac(gy, viewsin);
406 
407     int nx = gxt - gyt;
408 
409     //
410     // calculate perspective ratio (heightnumerator/(nx>>8))
411     //
412 
413     if (nx < mindist) {
414         nx = mindist; // don't let divide overflow
415 
416     }
417     int result = heightnumerator / (nx / 256);
418 
419     if (result < 8) {
420         result = 8;
421     }
422 
423     return result;
424 }
425 
426 
427 const uint8_t* postsource;
428 int postx;
429 
430 // BBi
431 int posty;
432 
433 int postheight;
434 const uint8_t* shadingtable;
435 extern const uint8_t* lightsource;
436 
ScalePost()437 void ScalePost()
438 {
439     int height = wallheight[postx] / 8;
440     postheight = height;
441 
442     if ((gamestate.flags & GS_LIGHTING) != 0) {
443         int i = shade_max - ((63 * height) / normalshade);
444 
445         if (i < 0) {
446             i = 0;
447         } else if (i > 63) {
448             i = 63;
449         }
450 
451         shadingtable = &lightsource[i * 256];
452 
453         DrawLSPost();
454     } else {
455         DrawPost();
456     }
457 }
458 
FarScalePost()459 void FarScalePost() // just so other files can call
460 {
461     ScalePost();
462 }
463 
464 
465 uint16_t DoorJamsShade[] = {
466     BIO_JAM_SHADE, // dr_bio
467     SPACE_JAM_2_SHADE, // dr_normal
468     STEEL_JAM_SHADE, // dr_prison
469     SPACE_JAM_2_SHADE, // dr_elevator
470     STEEL_JAM_SHADE, // dr_high_sec
471     OFFICE_JAM_SHADE, // dr_office
472     STEEL_JAM_SHADE, // dr_oneway_left
473     STEEL_JAM_SHADE, // dr_oneway_up
474     STEEL_JAM_SHADE, // dr_oneway_right
475     STEEL_JAM_SHADE, // dr_oneway_down
476     SPACE_JAM_SHADE, // dr_space
477 };
478 
479 uint16_t DoorJams[] = {
480     BIO_JAM, // dr_bio
481     SPACE_JAM_2, // dr_normal
482     STEEL_JAM, // dr_prison
483     SPACE_JAM_2, // dr_elevator
484     STEEL_JAM, // dr_high_sec
485     OFFICE_JAM, // dr_office
486     STEEL_JAM, // dr_oneway_left
487     STEEL_JAM, // dr_oneway_up
488     STEEL_JAM, // dr_oneway_right
489     STEEL_JAM, // dr_oneway_down
490     SPACE_JAM, // dr_space
491 };
492 
493 /*
494 ====================
495 =
496 = HitVertWall
497 =
498 = tilehit bit 7 is 0, because it's not a door tile
499 = if bit 6 is 1 and the adjacent tile is a door tile, use door side pic
500 =
501 ====================
502 */
HitVertWall()503 void HitVertWall()
504 {
505     int16_t wallpic;
506     uint16_t texture;
507 
508     texture = (yintercept >> 4) & 0xfc0;
509     if (xtilestep == -1) {
510         texture = 0xfc0 - texture;
511         xintercept += TILEGLOBAL;
512     }
513 
514     wallheight[pixx] = CalcHeight();
515 
516     if (lastside == 1 && lastintercept == xtile && lasttilehit == tilehit) {
517         ScalePost();
518 
519         last_texture_offset = texture;
520         postsource = &last_texture_data[last_texture_offset];
521 
522         postx = pixx;
523     } else {
524         // new wall
525 
526         if (lastside != -1) { // if not the first scaled post
527             ScalePost();
528         }
529 
530         lastside = true;
531         lastintercept = xtile;
532 
533         lasttilehit = tilehit;
534         postx = pixx;
535 
536         if (tilehit & 0x40) {
537             // check for adjacent doors
538             //
539 
540             ytile = yintercept >> TILESHIFT;
541 
542             auto door_index = ::tilemap[::xtile - ::xtilestep][::ytile];
543 
544             if ((door_index & 0x80) != 0 && (door_index & 0xC0) != 0xC0) {
545                 auto door = ::doorobjlist[door_index & 0x3F];
546 
547                 wallpic = static_cast<int16_t>(DOORWALL + ::DoorJamsShade[door.type]);
548             } else {
549                 wallpic = vertwall[tilehit & ~0x40];
550             }
551         } else {
552             wallpic = vertwall[tilehit];
553         }
554 
555         last_texture_data = (const uint8_t*)PM_GetPage(wallpic);
556         last_texture_offset = texture;
557         postsource = &last_texture_data[last_texture_offset];
558     }
559 }
560 
561 /*
562 ====================
563 =
564 = HitHorizWall
565 =
566 = tilehit bit 7 is 0, because it's not a door tile
567 = if bit 6 is 1 and the adjacent tile is a door tile, use door side pic
568 =
569 ====================
570 */
HitHorizWall()571 void HitHorizWall()
572 {
573     int16_t wallpic;
574     uint16_t texture;
575 
576     texture = (xintercept >> 4) & 0xfc0;
577     if (ytilestep == -1) {
578         yintercept += TILEGLOBAL;
579     } else {
580         texture = 0xfc0 - texture;
581     }
582     wallheight[pixx] = CalcHeight();
583 
584     if (lastside == 0 && lastintercept == ytile && lasttilehit == tilehit) {
585         ScalePost();
586 
587         last_texture_offset = texture;
588         postsource = &last_texture_data[last_texture_offset];
589 
590         postx = pixx;
591     } else {
592         // new wall
593         if (lastside != -1) { // if not the first scaled post
594             ScalePost();
595         }
596 
597         lastside = 0;
598         lastintercept = ytile;
599 
600         lasttilehit = tilehit;
601         postx = pixx;
602 
603         if (tilehit & 0x40) { // check for adjacent doors
604 
605             xtile = xintercept >> TILESHIFT;
606 
607             auto door_index = ::tilemap[::xtile][::ytile - ::ytilestep];
608 
609             if ((door_index & 0x80) != 0 && (door_index & 0xC0) != 0xC0) {
610                 auto door = ::doorobjlist[door_index & 0x3F];
611 
612                 wallpic = static_cast<int16_t>(DOORWALL + ::DoorJams[door.type]);
613             } else {
614                 wallpic = horizwall[tilehit & ~0x40];
615             }
616         } else {
617             wallpic = horizwall[tilehit];
618         }
619 
620         last_texture_data = (const uint8_t*)PM_GetPage(wallpic);
621         last_texture_offset = texture;
622         postsource = &last_texture_data[last_texture_offset];
623     }
624 
625 }
626 
HitHorizDoor()627 void HitHorizDoor()
628 {
629     uint16_t texture;
630     uint16_t doorpage = static_cast<uint16_t>(-1);
631     uint16_t xint;
632     bool lockable = true;
633 
634     int door_index = tilehit & 0x7F;
635 
636     if (doorobjlist[door_index].action == dr_jammed) {
637         return;
638     }
639 
640     xint = xintercept & 0xFFFF;
641 
642     if (xint > 0x7FFF) {
643         texture = ((xint - (uint16_t)(doorposition[door_index] >> 1)) >> 4) & 0xFC0;
644     } else {
645         texture = ((xint + (uint16_t)(doorposition[door_index] >> 1)) >> 4) & 0xFC0;
646     }
647 
648     wallheight[pixx] = CalcHeight();
649 
650     if (lasttilehit == tilehit) {
651 #if MASKABLE_DOORS
652         ScaleMPost();
653 #else
654         ScalePost();
655 #endif
656 
657         last_texture_offset = texture;
658         postsource = &last_texture_data[last_texture_offset];
659 
660         postx = pixx;
661     } else {
662         if (lastside != -1) // if not the first scaled post
663 #if MASKABLE_DOORS
664         { ScaleMPost();
665         }
666 #else
667         { ScalePost();
668         }
669 #endif
670 
671         // first pixel in this door
672 
673         lastside = 2;
674         lasttilehit = tilehit;
675         postx = pixx;
676 
677         switch (doorobjlist[door_index].type) {
678         case dr_normal:
679             doorpage = static_cast<int16_t>(DOORWALL + L_METAL);
680             break;
681 
682         case dr_elevator:
683             doorpage = static_cast<int16_t>(DOORWALL + L_ELEVATOR);
684             break;
685 
686         case dr_prison:
687             doorpage = static_cast<int16_t>(DOORWALL + L_PRISON);
688             break;
689 
690         case dr_space:
691             doorpage = static_cast<int16_t>(DOORWALL + L_SPACE);
692             break;
693 
694         case dr_bio:
695             doorpage = static_cast<int16_t>(DOORWALL + L_BIO);
696             break;
697 
698         case dr_high_security:
699             doorpage = static_cast<int16_t>(DOORWALL + L_HIGH_SECURITY); // Reverse View
700             break;
701 
702         case dr_oneway_up:
703         case dr_oneway_left:
704             if (player->tiley > doorobjlist[door_index].tiley) {
705                 doorpage = static_cast<int16_t>(DOORWALL + L_ENTER_ONLY); // normal view
706             } else {
707                 doorpage = static_cast<int16_t>(DOORWALL + NOEXIT); // Reverse View
708                 lockable = false;
709             }
710             break;
711 
712         case dr_oneway_right:
713         case dr_oneway_down:
714             if (player->tiley > doorobjlist[door_index].tiley) {
715                 doorpage = static_cast<int16_t>(DOORWALL + NOEXIT); // normal view
716                 lockable = false;
717             } else {
718                 doorpage = static_cast<int16_t>(DOORWALL + L_ENTER_ONLY); // Reverse View
719             }
720             break;
721 
722         case dr_office:
723             doorpage = static_cast<int16_t>(DOORWALL + L_HIGH_TECH);
724             break;
725         }
726 
727 
728         //
729         // If door is unlocked, Inc shape ptr to unlocked door shapes
730         //
731 
732         if (lockable && doorobjlist[door_index].lock == kt_none) {
733             doorpage += UL_METAL;
734         }
735 
736         last_texture_data = (const uint8_t*)PM_GetPage(doorpage);
737         last_texture_offset = texture;
738         postsource = &last_texture_data[last_texture_offset];
739     }
740 }
741 
HitVertDoor()742 void HitVertDoor()
743 {
744     uint16_t texture;
745     uint16_t doorpage = static_cast<uint16_t>(DOORWALL);
746     uint16_t yint;
747     bool lockable = true;
748 
749     int door_index = tilehit & 0x7F;
750 
751     if (doorobjlist[door_index].action == dr_jammed) {
752         return;
753     }
754 
755     yint = yintercept & 0xFFFF;
756     if (yint > 0x7FFF) {
757         texture = ((yint - (uint16_t)(doorposition[door_index] >> 1)) >> 4) & 0xFC0;
758     } else {
759         texture = ((yint + (uint16_t)(doorposition[door_index] >> 1)) >> 4) & 0xFC0;
760     }
761 
762     wallheight[pixx] = CalcHeight();
763 
764     if (lasttilehit == tilehit) {
765 #if MASKABLE_DOORS
766         ScaleMPost();
767 #else
768         ScalePost();
769 #endif
770 
771         last_texture_offset = texture;
772         postsource = &last_texture_data[last_texture_offset];
773 
774         postx = pixx;
775     } else {
776         if (lastside != -1) // if not the first scaled post
777 #if MASKABLE_DOORS
778         { ScaleMPost();
779         }
780 #else
781         { ScalePost();
782         }
783 #endif
784 
785         // first pixel in this door
786 
787         lastside = 2;
788         lasttilehit = tilehit;
789         postx = pixx;
790 
791         switch (doorobjlist[door_index].type) {
792         case dr_normal:
793             doorpage = static_cast<int16_t>(DOORWALL + L_METAL_SHADE);
794             break;
795 
796         case dr_elevator:
797             doorpage = static_cast<int16_t>(DOORWALL + L_ELEVATOR_SHADE);
798             break;
799 
800         case dr_prison:
801             doorpage = static_cast<int16_t>(DOORWALL + L_PRISON_SHADE);
802             break;
803 
804         case dr_space:
805             doorpage = static_cast<int16_t>(DOORWALL + L_SPACE_SHADE);
806             break;
807 
808         case dr_bio:
809             doorpage = static_cast<int16_t>(DOORWALL + L_BIO);
810             break;
811 
812         case dr_high_security:
813             doorpage = static_cast<int16_t>(DOORWALL + L_HIGH_SECURITY_SHADE);
814             break;
815 
816         case dr_oneway_left:
817         case dr_oneway_up:
818             if (player->tilex > doorobjlist[door_index].tilex) {
819                 doorpage = static_cast<int16_t>(DOORWALL + L_ENTER_ONLY_SHADE); // Reverse View
820             } else {
821                 doorpage = static_cast<int16_t>(DOORWALL + NOEXIT_SHADE); // Normal view
822                 lockable = false;
823             }
824             break;
825 
826         case dr_oneway_right:
827         case dr_oneway_down:
828             if (player->tilex > doorobjlist[door_index].tilex) {
829                 doorpage = static_cast<int16_t>(DOORWALL + NOEXIT_SHADE); // Reverse View
830                 lockable = false;
831             } else {
832                 doorpage = static_cast<int16_t>(DOORWALL + L_ENTER_ONLY_SHADE); // Normal View
833             }
834             break;
835 
836 
837         case dr_office:
838             doorpage = static_cast<int16_t>(DOORWALL + L_HIGH_TECH_SHADE);
839             break;
840 
841         }
842 
843         //
844         // If door is unlocked, Inc shape ptr to unlocked door shapes
845         //
846 
847         if (lockable && doorobjlist[door_index].lock == kt_none) {
848             doorpage += UL_METAL;
849         }
850 
851         last_texture_data = (const uint8_t*)PM_GetPage(doorpage);
852         last_texture_offset = texture;
853         postsource = &last_texture_data[last_texture_offset];
854     }
855 }
856 
857 /*
858 ====================
859 =
860 = HitHorizPWall
861 =
862 = A pushable wall in action has been hit
863 =
864 ====================
865 */
HitHorizPWall()866 void HitHorizPWall()
867 {
868     int16_t wallpic;
869     uint16_t texture, offset;
870 
871     texture = (xintercept >> 4) & 0xfc0;
872     offset = pwallpos << 10;
873     if (ytilestep == -1) {
874         yintercept += TILEGLOBAL - offset;
875     } else {
876         texture = 0xfc0 - texture;
877         yintercept += offset;
878     }
879 
880     wallheight[pixx] = CalcHeight();
881 
882     if (lasttilehit == tilehit) {
883         ScalePost();
884 
885         last_texture_offset = texture;
886         postsource = &last_texture_data[last_texture_offset];
887 
888         postx = pixx;
889     } else {
890         // new wall
891         if (lastside != -1) { // if not the first scaled post
892             ScalePost();
893         }
894 
895         lasttilehit = tilehit;
896         postx = pixx;
897 
898         wallpic = horizwall[tilehit & 63];
899 
900         last_texture_data = (const uint8_t*)PM_GetPage(wallpic);
901         last_texture_offset = texture;
902         postsource = &last_texture_data[last_texture_offset];
903     }
904 }
905 
906 /*
907 ====================
908 =
909 = HitVertPWall
910 =
911 = A pushable wall in action has been hit
912 =
913 ====================
914 */
HitVertPWall()915 void HitVertPWall()
916 {
917     int16_t wallpic;
918     uint16_t texture, offset;
919 
920     texture = (yintercept >> 4) & 0xfc0;
921     offset = pwallpos << 10;
922     if (xtilestep == -1) {
923         xintercept += TILEGLOBAL - offset;
924         texture = 0xfc0 - texture;
925     } else {
926         xintercept += offset;
927     }
928 
929     wallheight[pixx] = CalcHeight();
930 
931     if (lasttilehit == tilehit) {
932         ScalePost();
933 
934         last_texture_offset = texture;
935         postsource = &last_texture_data[last_texture_offset];
936 
937         postx = pixx;
938     } else {
939         // new wall
940         if (lastside != -1) { // if not the first scaled post
941             ScalePost();
942         }
943 
944         lasttilehit = tilehit;
945         postx = pixx;
946 
947         wallpic = vertwall[tilehit & 63];
948 
949         last_texture_data = (const uint8_t*)PM_GetPage(wallpic);
950         last_texture_offset = texture;
951         postsource = &last_texture_data[last_texture_offset];
952     }
953 
954 }
955 
956 /*
957 =====================
958 =
959 = VGAClearScreen
960 =
961 = NOTE: Before calling this function - Check to see if there even needs
962 = ====  to be a solid floor or solid ceiling color drawn.
963 =
964 =====================
965 */
966 
967 
968 // BBi
969 namespace {
970 
971 
vga_clear_screen(int y_offset,int height,int color)972 void vga_clear_screen(
973     int y_offset,
974     int height,
975     int color)
976 {
977     int pixel_offset = ::vl_get_offset(::bufferofs, 0, y_offset);
978 
979     if (::viewwidth == ::vga_width) {
980         std::uninitialized_fill_n(
981             &::vga_memory[pixel_offset],
982             height * ::vga_width,
983             static_cast<uint8_t>(color));
984     } else {
985         for (int y = 0; y < height; ++y) {
986             std::uninitialized_fill_n(
987                 &::vga_memory[pixel_offset],
988                 ::viewwidth,
989                 static_cast<uint8_t>(color));
990 
991             pixel_offset += ::vga_width;
992         }
993     }
994 }
995 
996 
997 } // namespace
998 // BBi
999 
1000 
VGAClearScreen()1001 void VGAClearScreen()
1002 {
1003     viewflags = gamestate.flags;
1004 
1005     int half_height = viewheight / 2;
1006 
1007     if ((viewflags & GS_DRAW_CEILING) == 0) {
1008         vga_clear_screen(0, half_height, TopColor);
1009     }
1010 
1011     if ((viewflags & GS_DRAW_FLOOR) == 0) {
1012         vga_clear_screen(
1013             viewheight - half_height, half_height, BottomColor);
1014     }
1015 }
1016 
CalcRotate(objtype * ob)1017 int16_t CalcRotate(
1018     objtype* ob)
1019 {
1020     dirtype dir = ob->dir;
1021 
1022     // this isn't exactly correct, as it should vary by a trig value,
1023     // but it is close enough with only eight rotations
1024 
1025     int view_angle = ::player->angle + ((::centerx - ob->viewx) / 8);
1026 
1027     if (dir == nodir) {
1028         dir = static_cast<dirtype>(ob->trydir & 127);
1029     }
1030 
1031     int target_angle = (view_angle - 180) - dirangle[dir];
1032 
1033     target_angle += ANGLES / 16;
1034 
1035     while (target_angle >= ANGLES) {
1036         target_angle -= ANGLES;
1037     }
1038 
1039     while (target_angle < 0) {
1040         target_angle += ANGLES;
1041     }
1042 
1043     if ((ob->state->flags & SF_PAINFRAME) != 0) { // 2 rotation pain frame
1044         return static_cast<int16_t>(4 * (target_angle / (ANGLES / 2))); // seperated by 3 (art layout...)
1045 
1046     }
1047     return static_cast<int16_t>(target_angle / (ANGLES / 8));
1048 }
1049 
1050 
1051 
1052 const int MAXVISABLE = 50;
1053 visobj_t vislist[MAXVISABLE];
1054 visobj_t* visptr;
1055 visobj_t* visstep;
1056 visobj_t* farthest;
1057 
1058 
1059 /*
1060 =====================
1061 =
1062 = DrawScaleds
1063 =
1064 = Draws all objects that are visable
1065 =
1066 =====================
1067 */
DrawScaleds()1068 void DrawScaleds()
1069 {
1070     int16_t i, least, numvisable, height;
1071     uint8_t* tilespot, * visspot;
1072     uint16_t spotloc;
1073 
1074     statobj_t* statptr;
1075     objtype* obj;
1076 
1077     visptr = &vislist[0];
1078 
1079 //
1080 // place static objects
1081 //
1082     for (statptr = &statobjlist[0]; statptr != laststatobj; statptr++) {
1083         if ((visptr->shapenum = statptr->shapenum) == -1) {
1084             continue; // object has been deleted
1085 
1086         }
1087         if ((Keyboard[ScanCode::sc_6] && (Keyboard[ScanCode::sc_7] || Keyboard[ScanCode::sc_8]) && DebugOk) && (statptr->flags & FL_BONUS)) {
1088             GetBonus(statptr);
1089             continue;
1090         }
1091 
1092         if (!*statptr->visspot) {
1093             continue; // not visable
1094 
1095 
1096         }
1097         if (TransformTile(statptr->tilex, statptr->tiley, &visptr->viewx, &visptr->viewheight) &&
1098             (statptr->flags & FL_BONUS))
1099         {
1100             GetBonus(statptr);
1101             continue;
1102         }
1103 
1104         if (!visptr->viewheight) {
1105             continue; // to close to the object
1106 
1107         }
1108         visptr->cloaked = false;
1109         visptr->lighting = statptr->lighting; // Could add additional
1110         // flashing/lighting
1111         if (visptr < &vislist[MAXVISABLE - 1]) { // don't let it overflow
1112             visptr++;
1113         }
1114     }
1115 
1116 //
1117 // place active objects
1118 //
1119     for (obj = player->next; obj; obj = obj->next) {
1120 
1121 
1122         if (obj->flags & FL_OFFSET_STATES) {
1123             visptr->shapenum = static_cast<int16_t>(
1124                 obj->temp1 + obj->state->shapenum);
1125 
1126             if (visptr->shapenum == 0) {
1127                 continue; // no shape
1128             }
1129         } else {
1130             visptr->shapenum = static_cast<int16_t>(obj->state->shapenum);
1131 
1132             if (visptr->shapenum == 0) {
1133                 continue; // no shape
1134             }
1135         }
1136         spotloc = (obj->tilex << 6) + obj->tiley; // optimize: keep in struct?
1137 
1138         // BBi Do not draw detonator if it's not visible.
1139         if (spotloc == 0) {
1140             continue;
1141         }
1142 
1143         visspot = &spotvis[0][0] + spotloc;
1144         tilespot = &tilemap[0][0] + spotloc;
1145 
1146         //
1147         // could be in any of the nine surrounding tiles
1148         //
1149 
1150         if (*visspot
1151             || (*(visspot - 1) && !*(tilespot - 1))
1152             || (*(visspot + 1) && !*(tilespot + 1))
1153             || (*(visspot - 65) && !*(tilespot - 65))
1154             || (*(visspot - 64) && !*(tilespot - 64))
1155             || (*(visspot - 63) && !*(tilespot - 63))
1156             || (*(visspot + 65) && !*(tilespot + 65))
1157             || (*(visspot + 64) && !*(tilespot + 64))
1158             || (*(visspot + 63) && !*(tilespot + 63)))
1159         {
1160             obj->active = ac_yes;
1161 
1162             TransformActor(obj);
1163 
1164             if (!obj->viewheight) {
1165                 continue; // too close or far away
1166 
1167             }
1168 
1169             if (::is_ps() &&
1170                 (obj->flags2 & (FL2_CLOAKED | FL2_DAMAGE_CLOAK)) == FL2_CLOAKED)
1171             {
1172                 visptr->cloaked = true;
1173                 visptr->lighting = 0;
1174             } else {
1175                 visptr->cloaked = false;
1176                 visptr->lighting = obj->lighting;
1177             }
1178 
1179             if (::is_ps() && (obj->flags & FL_DEADGUY) == 0) {
1180                 obj->flags2 &= ~FL2_DAMAGE_CLOAK;
1181             }
1182 
1183             visptr->viewx = obj->viewx;
1184             visptr->viewheight = obj->viewheight;
1185 
1186             if (visptr->shapenum == -1) {
1187                 visptr->shapenum = obj->temp1; // special shape
1188 
1189             }
1190             if (obj->state->flags & SF_ROTATE) {
1191                 visptr->shapenum += CalcRotate(obj);
1192             }
1193 
1194             if (visptr < &vislist[MAXVISABLE - 1]) { // don't let it overflow
1195                 visptr++;
1196             }
1197             obj->flags |= FL_VISABLE;
1198         } else {
1199             obj->flags &= ~FL_VISABLE;
1200         }
1201     }
1202 
1203 //
1204 // draw from back to front
1205 //
1206     numvisable = static_cast<int16_t>(visptr - &vislist[0]);
1207 
1208     if (!numvisable) {
1209         return; // no visable objects
1210 
1211     }
1212     for (i = 0; i < numvisable; i++) {
1213         least = 32000;
1214         for (visstep = &vislist[0]; visstep < visptr; visstep++) {
1215             height = visstep->viewheight;
1216             if (height < least) {
1217                 least = height;
1218                 farthest = visstep;
1219             }
1220         }
1221 
1222         //
1223         // Init our global flag...
1224         //
1225 
1226         cloaked_shape = farthest->cloaked;
1227 
1228         //
1229         // draw farthest
1230         //
1231         if (((gamestate.flags & GS_LIGHTING) != 0 && farthest->lighting != NO_SHADING) || cloaked_shape) {
1232             ScaleLSShape(farthest->viewx, farthest->shapenum, farthest->viewheight, farthest->lighting);
1233         } else {
1234             ScaleShape(farthest->viewx, farthest->shapenum, farthest->viewheight);
1235         }
1236 
1237         farthest->viewheight = 32000;
1238     }
1239 
1240     cloaked_shape = false;
1241 
1242 }
1243 
1244 
1245 using WeaponScale = std::vector<int16_t>;
1246 
1247 WeaponScale weaponscale;
1248 
initialize_weapon_constants()1249 void initialize_weapon_constants()
1250 {
1251     NUMWEAPONS = ::is_ps() ? 7 : 6;
1252 
1253     weaponscale = {
1254         SPR_KNIFEREADY,
1255         SPR_PISTOLREADY,
1256         SPR_MACHINEGUNREADY,
1257         SPR_CHAINREADY,
1258         SPR_GRENADEREADY,
1259         SPR_BFG_WEAPON1,
1260         0,
1261     };
1262 }
1263 
1264 
1265 bool useBounceOffset = false;
1266 
DrawPlayerWeapon()1267 void DrawPlayerWeapon()
1268 {
1269     if (::playstate == ex_victorious)
1270     {
1271         return;
1272     }
1273 
1274     if (::gamestate.weapon != -1)
1275     {
1276         const auto shapenum =
1277             ::weaponscale[static_cast<int>(::gamestate.weapon)] +
1278             ::gamestate.weaponframe;
1279 
1280         if (shapenum != 0)
1281         {
1282             const auto height = ::is_aog() ? 128 : 88;
1283 
1284             ::useBounceOffset = ::is_ps();
1285             ::scale_player_weapon(shapenum, height);
1286             ::useBounceOffset = false;
1287         }
1288     }
1289 }
1290 
CalcTics()1291 void CalcTics()
1292 {
1293     int32_t newtime;
1294 
1295 //
1296 // calculate tics since last refresh for adaptive timing
1297 //
1298     if (static_cast<uint32_t>(lasttimecount) > TimeCount) {
1299         TimeCount = lasttimecount; // if the game was paused a LONG time
1300 
1301 
1302     }
1303 
1304     {
1305 //
1306 // non demo, so report actual time
1307 //
1308         do {
1309             newtime = TimeCount;
1310             auto diff = newtime - lasttimecount;
1311             if (diff <= 0) {
1312                 tics = 0;
1313             } else {
1314                 tics = static_cast<uint16_t>(diff);
1315             }
1316         } while (tics == 0); // make sure at least one tic passes
1317 
1318         lasttimecount = newtime;
1319         framecount++;
1320 
1321 #ifdef FILEPROFILE
1322         strcpy(scratch, "\tTics:");
1323         itoa(tics, str, 10);
1324         strcat(scratch, str);
1325         strcat(scratch, "\n");
1326         write(profilehandle, scratch, strlen(scratch));
1327 #endif
1328 
1329 #ifdef DEBUGTICS
1330         VW_SetAtrReg(ATR_OVERSCAN, tics);
1331 #endif
1332 
1333         realtics = tics;
1334         if (tics > MAXTICS) {
1335             TimeCount -= (tics - MAXTICS);
1336             tics = MAXTICS;
1337         }
1338     }
1339 }
1340 
WallRefresh()1341 void WallRefresh()
1342 {
1343 //
1344 // set up variables for this view
1345 //
1346 
1347     viewangle = player->angle;
1348     midangle = viewangle * (FINEANGLES / ANGLES);
1349     viewsin = sintable[viewangle];
1350     viewcos = costable[viewangle];
1351     viewx = player->x - FixedByFrac(focallength, viewcos);
1352     viewy = player->y + FixedByFrac(focallength, viewsin);
1353 
1354     focaltx = viewx >> TILESHIFT;
1355     focalty = viewy >> TILESHIFT;
1356 
1357     viewtx = player->x >> TILESHIFT;
1358     viewty = player->y >> TILESHIFT;
1359 
1360     xpartialdown = viewx & (TILEGLOBAL - 1);
1361     xpartialup = static_cast<uint16_t>(TILEGLOBAL - xpartialdown);
1362     ypartialdown = viewy & (TILEGLOBAL - 1);
1363     ypartialup = static_cast<uint16_t>(TILEGLOBAL - ypartialdown);
1364 
1365     lastside = -1; // the first pixel is on a new wall
1366 
1367     AsmRefresh();
1368     ScalePost(); // no more optimization on last post
1369 }
1370 
1371 
1372 extern int16_t MsgTicsRemain;
1373 extern uint16_t LastMsgPri;
1374 
RedrawStatusAreas()1375 void RedrawStatusAreas()
1376 {
1377     ::DrawInfoArea_COUNT = 3;
1378     ::InitInfoArea_COUNT = 3;
1379 
1380     ::LatchDrawPic(0, 0, TOP_STATUSBARPIC);
1381     ::ShadowPrintLocationText(sp_normal);
1382 
1383     ::LatchDrawPic(0, 200 - STATUSLINES, STATUSBARPIC);
1384     ::DrawAmmoPic();
1385     ::DrawScoreNum();
1386     ::DrawWeaponPic();
1387     ::DrawAmmoNum();
1388     ::DrawKeyPics();
1389     ::DrawHealthNum();
1390 }
1391 
1392 void F_MapLSRow();
1393 void C_MapLSRow();
1394 void MapLSRow();
1395 
ThreeDRefresh()1396 void ThreeDRefresh()
1397 {
1398     ::memset(::spotvis, 0, sizeof(::spotvis));
1399 
1400     ::bufferofs = 0;
1401 
1402     ::UpdateInfoAreaClock();
1403     ::UpdateStatusBar();
1404 
1405     // BBi
1406     ::vid_is_3d = true;
1407 
1408     ::bufferofs += ::screenofs;
1409 
1410 //
1411 // follow the walls from there to the right, drawwing as we go
1412 //
1413 
1414     if ((::gamestate.flags & GS_LIGHTING) != 0) {
1415         switch (::gamestate.flags & (GS_DRAW_FLOOR | GS_DRAW_CEILING)) {
1416         case GS_DRAW_FLOOR | GS_DRAW_CEILING:
1417             ::MapRowPtr = ::MapLSRow;
1418             ::WallRefresh();
1419             ::DrawPlanes();
1420             break;
1421 
1422         case GS_DRAW_FLOOR:
1423             ::MapRowPtr = ::F_MapLSRow;
1424             ::VGAClearScreen();
1425             ::WallRefresh();
1426             ::DrawPlanes();
1427             break;
1428 
1429         case GS_DRAW_CEILING:
1430             ::MapRowPtr = ::C_MapLSRow;
1431             ::VGAClearScreen();
1432             ::WallRefresh();
1433             ::DrawPlanes();
1434             break;
1435 
1436         default:
1437             ::VGAClearScreen();
1438             ::WallRefresh();
1439             break;
1440         }
1441     } else {
1442         switch (::gamestate.flags & (GS_DRAW_FLOOR | GS_DRAW_CEILING)) {
1443         case GS_DRAW_FLOOR | GS_DRAW_CEILING:
1444             ::MapRowPtr = ::MapRow;
1445             ::WallRefresh();
1446             ::DrawPlanes();
1447             break;
1448 
1449         case GS_DRAW_FLOOR:
1450             ::MapRowPtr = ::F_MapRow;
1451             ::VGAClearScreen();
1452             ::WallRefresh();
1453             ::DrawPlanes();
1454             break;
1455 
1456         case GS_DRAW_CEILING:
1457             ::MapRowPtr = ::C_MapRow;
1458             ::VGAClearScreen();
1459             ::WallRefresh();
1460             ::DrawPlanes();
1461             break;
1462 
1463         default:
1464             ::VGAClearScreen();
1465             ::WallRefresh();
1466             break;
1467         }
1468     }
1469 
1470     ::UpdateTravelTable();
1471 
1472 //
1473 // draw all the scaled images
1474 //
1475 
1476     ::DrawScaleds(); // draw scaled stuf
1477 
1478     // BBi
1479     ::vid_is_3d = false;
1480 
1481     ::DrawPlayerWeapon(); // draw player's hands
1482 
1483 //
1484 // show screen and time last cycle
1485 //
1486     if (::fizzlein) {
1487         ::fizzlein = false;
1488 
1489         ::vid_set_ui_mask_3d(false);
1490 
1491         bstone::GenericFizzleFX fizzle(BLACK, false);
1492 
1493         fizzle.initialize();
1494 
1495         static_cast<void>(fizzle.present());
1496 
1497         ::lasttimecount = ::TimeCount; // don't make a big tic count
1498     }
1499 
1500     ::bufferofs = 0;
1501 
1502     if (::is_ps()) {
1503         ::DrawRadar();
1504     }
1505 
1506     // BBi
1507     ::VL_RefreshScreen();
1508 
1509     ::frameon++;
1510 }
1511 
1512 uint8_t TravelTable[MAPSIZE][MAPSIZE];
1513 
UpdateTravelTable()1514 void UpdateTravelTable()
1515 {
1516     for (int i = 0; i < MAPSIZE; ++i) {
1517         for (int j = 0; j < MAPSIZE; ++j) {
1518             TravelTable[i][j] |= spotvis[i][j];
1519         }
1520     }
1521 }
1522 
DrawRadar()1523 void DrawRadar()
1524 {
1525     int8_t zoom = gamestate.rzoom;
1526     uint8_t flags = OV_KEYS | OV_PUSHWALLS | OV_ACTORS;
1527 
1528     if (gamestate.rpower) {
1529         if ((frameon & 1) && (!godmode)) {
1530             if (zoom) {
1531                 gamestate.rpower -= tics << zoom;
1532             }
1533         }
1534 
1535         if (gamestate.rpower < 0) {
1536             gamestate.rpower = 0;
1537             DISPLAY_TIMED_MSG(RadarEnergyGoneMsg, MP_WEAPON_AVAIL, MT_GENERAL);
1538         }
1539         UpdateRadarGuage();
1540     } else {
1541         zoom = 0;
1542     }
1543 
1544     ShowOverhead(192, 156, 16, zoom, flags);
1545 }
1546 
1547 uint16_t tc_time;
1548 
show_pwalls_on_automap(int x,int y)1549 static bool show_pwalls_on_automap(
1550     int x,
1551     int y)
1552 {
1553     if (::is_ps()) {
1554         return true;
1555     }
1556 
1557     static const int x_spots[8] = {
1558         -1, 0, 1, 1, 1, 0, -1, -1,
1559     }; // x_spots
1560 
1561     static const int y_spots[8] = {
1562         1, 1, 1, 0, -1, -1, -1, 0,
1563     }; // y_spots
1564 
1565     for (auto i = 0; i < 8; ++i) {
1566         auto new_x = x + x_spots[i];
1567         auto new_y = y + y_spots[i];
1568 
1569         if (new_x < 0 || new_x >= 64 || new_y < 0 || new_y >= 64) {
1570             continue;
1571         }
1572 
1573         auto iconnum = *(mapsegs[1] + farmapylookup[new_y] + new_x);
1574 
1575         if (iconnum == PUSHABLETILE) {
1576             continue;
1577         }
1578 
1579         if ((TravelTable[new_x][new_y] & TT_TRAVELED) != 0) {
1580             return true;
1581         }
1582     }
1583 
1584     return false;
1585 }
1586 
ShowOverhead(int bx,int by,int radius,int zoom,int flags)1587 void ShowOverhead(
1588     int bx,
1589     int by,
1590     int radius,
1591     int zoom,
1592     int flags)
1593 {
1594     const uint8_t PWALL_COLOR = 0xF6;
1595     const uint8_t PLAYER_COLOR = 0xF0;
1596     const uint8_t UNMAPPED_COLOR = (::is_ps() ? 0x52 : 0x06);
1597     const uint8_t MAPPED_COLOR = 0x55;
1598     const uint8_t HIDDEN_COLOR = 0x52;
1599 
1600     bool snow = false;
1601     uint8_t rndindex = 0;
1602     bool drawplayerok = true;
1603 
1604     // -zoom == make it snow!
1605     //
1606     if (zoom < 0) {
1607         zoom = 0;
1608         snow = true;
1609         rndindex = static_cast<uint8_t>(US_RndT());
1610     }
1611 
1612     zoom = 1 << zoom;
1613     radius /= zoom;
1614 
1615     int player_angle = player->angle;
1616     int player_x = player->x;
1617     int player_y = player->y;
1618 
1619     if ((flags & OV_WHOLE_MAP) != 0) {
1620         player_angle = 90;
1621         player_x = ((int32_t)32 << TILESHIFT) + (TILEGLOBAL / 2);
1622         player_y = player_x;
1623     }
1624 
1625     // Get sin/cos values
1626     //
1627     int psin = sintable[player_angle];
1628     int pcos = costable[player_angle];
1629 
1630     // Convert radius to fixed integer and calc rotation.
1631     //
1632     int dx = radius << TILESHIFT;
1633     int dy = dx;
1634 
1635     int baselmx = player_x + (FixedByFrac(dx, pcos) - FixedByFrac(dy, psin));
1636     int baselmy = player_y - (FixedByFrac(dx, psin) + FixedByFrac(dy, pcos));
1637 
1638     // Carmack's sin/cos tables use one's complement for negative numbers --
1639     // convert it to two's complement!
1640     //
1641     if ((pcos & 0x80000000) != 0) {
1642         pcos = -(pcos & 0xFFFF);
1643     }
1644 
1645     if ((psin & 0x80000000) != 0) {
1646         psin = -(psin & 0xFFFF);
1647     }
1648 
1649     // Get x/y increment values.
1650     //
1651     int xinc = -pcos;
1652     int yinc = psin;
1653 
1654     int diameter = radius * 2;
1655 
1656     // Draw rotated radar.
1657     //
1658 
1659     for (int x = 0; x < diameter; ++x) {
1660         int lmx = baselmx;
1661         int lmy = baselmy;
1662 
1663         for (int y = 0; y < diameter; ++y) {
1664             uint8_t color = 0x00;
1665             bool go_to_draw = false;
1666 
1667             if (snow) {
1668                 color = 0x42 + (rndtable[rndindex] & 3);
1669                 rndindex++;
1670                 go_to_draw = true;
1671             }
1672 
1673             // Don't evaluate if point is outside of map.
1674             //
1675             int mx = 0;
1676             int my = 0;
1677 
1678             if (!go_to_draw) {
1679                 color = UNMAPPED_COLOR;
1680                 mx = lmx >> 16;
1681                 my = lmy >> 16;
1682 
1683                 if (mx < 0 || mx > 63 || my < 0 || my > 63) {
1684                     go_to_draw = true;
1685                 }
1686             }
1687 
1688             // SHOW PLAYER
1689             //
1690             if (!go_to_draw &&
1691                 drawplayerok &&
1692                 player->tilex == mx &&
1693                 player->tiley == my)
1694             {
1695                 color = PLAYER_COLOR;
1696                 drawplayerok = false;
1697             } else if (!go_to_draw) {
1698                 // SHOW TRAVELED
1699                 //
1700                 if ((TravelTable[mx][my] & TT_TRAVELED) != 0 ||
1701                     (flags & OV_SHOWALL) != 0)
1702                 {
1703                     // What's at this map location?
1704                     //
1705                     uint8_t tile = tilemap[mx][my];
1706                     uint8_t door = tile & 0x3F;
1707 
1708                     bool check_for_hidden_area = false;
1709 
1710                     // Evaluate wall or floor?
1711                     //
1712                     if (tile != 0) {
1713                         // SHOW DOORS
1714                         //
1715                         if ((tile & 0x80) != 0) {
1716                             if (::is_aog() && doorobjlist[door].type == dr_elevator) {
1717                                 color = 0xFD;
1718                             } else if (doorobjlist[door].lock != kt_none) {
1719                                 color = 0x18; // locked!
1720                             } else {
1721                                 if (doorobjlist[door].action == dr_closed) {
1722                                     color = 0x58; // closed!
1723                                 } else {
1724                                     color = MAPPED_COLOR; // floor!
1725                                     check_for_hidden_area = ::is_aog();
1726                                 }
1727                             }
1728                         }
1729                     } else {
1730                         color = MAPPED_COLOR; // floor!
1731                         check_for_hidden_area = ::is_aog();
1732                     }
1733 
1734                     if (check_for_hidden_area) {
1735                         static_cast<void>(::GetAreaNumber(mx, my));
1736 
1737                         if (::GAN_HiddenArea) {
1738                             color = HIDDEN_COLOR;
1739                         }
1740                     }
1741 
1742                     // SHOW KEYS
1743                     //
1744                     if ((flags & OV_KEYS) != 0 &&
1745                         (TravelTable[mx][my] & TT_KEYS) != 0)
1746                     {
1747                         color = 0xF3;
1748                     }
1749 
1750                     if ((ExtraRadarFlags & OV_ACTORS) != 0 ||
1751                         (::is_ps() && zoom > 1 && (flags & OV_ACTORS) != 0))
1752                     {
1753                         const auto ob = actorat[mx][my];
1754 
1755                         // SHOW ACTORS
1756                         //
1757                         if (ob >= objlist &&
1758                             (ob->flags & FL_DEADGUY) == 0 &&
1759                             ob->obclass > deadobj &&
1760                             ob->obclass < SPACER1_OBJ)
1761                         {
1762                             color = static_cast<uint8_t>(0x10 + ob->obclass);
1763                         }
1764                     }
1765 
1766                     if ((ExtraRadarFlags & OV_PUSHWALLS) != 0 ||
1767                         (::is_ps() && zoom == 4 && (flags & OV_PUSHWALLS) != 0))
1768                     {
1769                         auto iconnum = *(mapsegs[1] + farmapylookup[my] + mx);
1770 
1771                         // SHOW PUSHWALLS
1772                         //
1773                         if (iconnum == PUSHABLETILE) {
1774                             if (::show_pwalls_on_automap(mx, my)) {
1775                                 color = (::is_aog() ? PWALL_COLOR : 0x79);
1776                             }
1777                         }
1778                     }
1779                 } else {
1780                     color = UNMAPPED_COLOR;
1781                 }
1782             }
1783 
1784             VL_Bar(bx + (x * zoom), by + (y * zoom), zoom, zoom, color);
1785 
1786             lmx += xinc;
1787             lmy += yinc;
1788         }
1789 
1790         baselmx += yinc;
1791         baselmy -= xinc;
1792     }
1793 }
1794