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