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 /*
30 =============================================================================
31 
32  LOCAL CONSTANTS
33 
34 =============================================================================
35 */
36 
37 #define LOCATION_TEXT_COLOR (0xAF)
38 
39 extern char prep_msg[];
40 extern int8_t LS_current;
41 extern int8_t LS_total;
42 
43 
44 void Died();
45 
46 void PM_SetMainMemPurge(
47     int16_t level);
48 
49 void InitGoldsternInfo();
50 void InitDoorList();
51 void InitStaticList();
52 void ConnectBarriers();
53 void DrawHealth();
54 void DrawKeys();
55 void DrawWeapon();
56 void DrawScore();
57 void InitInfoArea();
58 void ForceUpdateStatusBar();
59 void UpdateStatusBar();
60 
61 bool LoadLevel(
62     int levelnum);
63 
64 void SetPlaneViewSize();
65 
66 int16_t CalcAngle(
67     objtype* from_obj,
68     objtype* to_obj);
69 
70 void FinishPaletteShifts();
71 
72 void CA_CacheScreen(
73     int16_t chunk);
74 
75 void VH_UpdateScreen();
76 
77 void DoActor(
78     objtype* ob);
79 
80 bool LevelInPlaytemp(
81     int level_index);
82 
83 void PreloadUpdate(
84     uint16_t current,
85     uint16_t total);
86 
87 void PreloadGraphics();
88 
89 bool SaveLevel(
90     int level_index);
91 
92 void CheckHighScore(
93     int32_t score,
94     uint16_t other);
95 
96 
97 /*
98 =============================================================================
99 
100  GLOBAL VARIABLES
101 
102 =============================================================================
103 */
104 
105 fargametype gamestuff;
106 gametype gamestate;
107 bool ingame;
108 bool fizzlein;
109 int latchpics[NUMLATCHPICS];
110 eaWallInfo eaList[MAXEAWALLS];
111 int8_t NumEAWalls;
112 
113 tilecoord_t GoldieList[GOLDIE_MAX_SPAWNS];
114 GoldsternInfo_t GoldsternInfo;
115 
116 extern uint16_t scan_value;
117 
118 int NUMWEAPONS = 0;
119 
120 
121 void ScanInfoPlane();
122 void SetupGameLevel();
123 
124 void DrawPlayScreen(
125     bool InitInfoMsg);
126 
127 void LoadLatchMem();
128 void GameLoop();
129 
130 // BBi
131 static void fix_level_inplace();
132 
133 
134 /*
135 =============================================================================
136 
137  LOCAL VARIABLES
138 
139 =============================================================================
140 */
141 
142 //
143 // NOTE: This array indexs the "statinfo" array in ACT1.C and is indexed
144 //                 upon tile number/values.
145 //
146 
147 int8_t ExpCrateShapes[] = {
148     42, // Chicken Leg
149     44, // Ham/Steak
150     26, // Clip
151     24, // Pistol
152     27, // Pulse
153     28, // ION
154     46, // Grenade
155     62, // Money Bag
156     63, // Loot
157     64, // Gold
158     65, // Bonus
159     71, // Gore 1
160     74, // Gore 2
161     32, // red key
162     33, // yel key
163     34, // grn key
164     35, // blu key
165     36, // gld key
166 };
167 
168 
169 /*
170 ==========================
171 =
172 = SetSoundLoc - Given the location of an object (in terms of global
173 =   coordinates, held in globalsoundx and globalsoundy), munges the values
174 =   for an approximate distance from the left and right ear, and puts
175 =   those values into leftchannel and rightchannel.
176 =
177 = JAB
178 =
179 ==========================
180 */
181 
182 fixed globalsoundx;
183 fixed globalsoundy;
184 int16_t leftchannel;
185 int16_t rightchannel;
186 
187 #define ATABLEMAX (15)
188 
189 uint8_t righttable[ATABLEMAX][ATABLEMAX * 2] = {
190     { 8, 8, 8, 8, 8, 8, 8, 7, 7, 7, 7, 7, 7, 6, 0, 0, 0, 0, 0, 1, 3, 5, 8, 8, 8, 8, 8, 8, 8, 8 },
191     { 8, 8, 8, 8, 8, 8, 8, 7, 7, 7, 7, 7, 6, 4, 0, 0, 0, 0, 0, 2, 4, 6, 8, 8, 8, 8, 8, 8, 8, 8 },
192     { 8, 8, 8, 8, 8, 8, 8, 7, 7, 7, 7, 6, 6, 4, 1, 0, 0, 0, 1, 2, 4, 6, 8, 8, 8, 8, 8, 8, 8, 8 },
193     { 8, 8, 8, 8, 8, 8, 8, 7, 7, 7, 7, 6, 5, 4, 2, 1, 0, 1, 2, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8 },
194     { 8, 8, 8, 8, 8, 8, 8, 8, 7, 7, 7, 6, 5, 4, 3, 2, 2, 3, 3, 5, 6, 8, 8, 8, 8, 8, 8, 8, 8, 8 },
195     { 8, 8, 8, 8, 8, 8, 8, 8, 7, 7, 7, 6, 6, 5, 4, 4, 4, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8 },
196     { 8, 8, 8, 8, 8, 8, 8, 8, 8, 7, 7, 7, 6, 6, 5, 5, 5, 6, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8 },
197     { 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 7, 7, 7, 6, 6, 7, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8 },
198     { 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8 },
199     { 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8 },
200     { 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8 },
201     { 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8 },
202     { 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8 },
203     { 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8 },
204     { 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8 }
205 };
206 
207 uint8_t lefttable[ATABLEMAX][ATABLEMAX * 2] = {
208     { 8, 8, 8, 8, 8, 8, 8, 8, 5, 3, 1, 0, 0, 0, 0, 0, 6, 7, 7, 7, 7, 7, 7, 8, 8, 8, 8, 8, 8, 8 },
209     { 8, 8, 8, 8, 8, 8, 8, 8, 6, 4, 2, 0, 0, 0, 0, 0, 4, 6, 7, 7, 7, 7, 7, 8, 8, 8, 8, 8, 8, 8 },
210     { 8, 8, 8, 8, 8, 8, 8, 8, 6, 4, 2, 1, 0, 0, 0, 1, 4, 6, 6, 7, 7, 7, 7, 8, 8, 8, 8, 8, 8, 8 },
211     { 8, 8, 8, 8, 8, 8, 8, 8, 7, 5, 3, 2, 1, 0, 1, 2, 4, 5, 6, 7, 7, 7, 7, 8, 8, 8, 8, 8, 8, 8 },
212     { 8, 8, 8, 8, 8, 8, 8, 8, 8, 6, 5, 3, 3, 2, 2, 3, 4, 5, 6, 7, 7, 7, 8, 8, 8, 8, 8, 8, 8, 8 },
213     { 8, 8, 8, 8, 8, 8, 8, 8, 8, 7, 6, 5, 4, 4, 4, 4, 5, 6, 6, 7, 7, 7, 8, 8, 8, 8, 8, 8, 8, 8 },
214     { 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 7, 6, 6, 5, 5, 5, 6, 6, 7, 7, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8 },
215     { 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 7, 7, 6, 6, 7, 7, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8 },
216     { 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8 },
217     { 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8 },
218     { 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8 },
219     { 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8 },
220     { 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8 },
221     { 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8 },
222     { 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8 }
223 };
224 
SetSoundLoc(fixed gx,fixed gy)225 void SetSoundLoc(
226     fixed gx,
227     fixed gy)
228 {
229     fixed xt, yt;
230     int16_t x, y;
231 
232 //
233 // translate point to view centered coordinates
234 //
235     gx -= viewx;
236     gy -= viewy;
237 
238 //
239 // calculate newx
240 //
241     xt = FixedByFrac(gx, viewcos);
242     yt = FixedByFrac(gy, viewsin);
243     x = (xt - yt) >> TILESHIFT;
244 
245 //
246 // calculate newy
247 //
248     xt = FixedByFrac(gx, viewsin);
249     yt = FixedByFrac(gy, viewcos);
250     y = (yt + xt) >> TILESHIFT;
251 
252     if (y >= ATABLEMAX) {
253         y = ATABLEMAX - 1;
254     } else if (y <= -ATABLEMAX) {
255         y = -ATABLEMAX;
256     }
257     if (x < 0) {
258         x = -x;
259     }
260     if (x >= ATABLEMAX) {
261         x = ATABLEMAX - 1;
262     }
263     leftchannel = lefttable[x][y + ATABLEMAX];
264     rightchannel = righttable[x][y + ATABLEMAX];
265 }
266 
267 /*
268 ==========================
269 =
270 = SetSoundLocGlobal - Sets up globalsoundx & globalsoundy and then calls
271 =       UpdateSoundLoc() to transform that into relative channel volumes. Those
272 =       values are then passed to the Sound Manager so that they'll be used for
273 =       the next sound played (if possible).
274 =
275 = JAB
276 =
277 ==========================
278 */
279 
UpdateSoundLoc()280 void UpdateSoundLoc()
281 {
282     ::sd_update_positions();
283 }
284 
285 /*
286 **      JAB End
287 */
288 
289 
ClearMemory()290 void ClearMemory()
291 {
292 }
293 
294 
295 #define INVALID_ACTOR_ERR Quit("Invalid actor: {} {}", x, y)
296 
297 
298 /*
299 ==========================
300 =
301 = ScanInfoPlane
302 =
303 = Spawn all actors and mark down special places
304 =
305 ==========================
306 */
ScanInfoPlane()307 void ScanInfoPlane()
308 {
309     uint16_t x, y;
310     int16_t tile;
311     uint16_t* start;
312     bool gottextures = false;
313     bool gotcolors = false;
314 
315     detonators_spawned = 0;
316 
317     new_actor = nullptr;
318     start = mapsegs[1];
319     for (y = 0; y < mapheight; y++) {
320         for (x = 0; x < mapwidth; x++) {
321             sci_mCacheInfo* ci;
322             scientist_t* st = nullptr;
323             uint8_t tilehi, tilelo, block = 0;
324 
325 
326             tile = *start++;
327             //
328             // Check for tiles/icons to ignore...
329             //
330             switch ((uint16_t) * (mapsegs[0] + farmapylookup[y] + x)) {
331             case SMART_OFF_TRIGGER:
332             case SMART_ON_TRIGGER:
333                 if (!::is_ps()) {
334                     ::Quit("Smart trigger (PS) at ({}, {}).", x, y);
335                 }
336                 continue;
337 
338             case DOORTRIGGERTILE:
339                 // Ignore all values/icons on top of these tiles...
340                 continue;
341             }
342             tilehi = (tile & 0xff00) >> 8;
343             tilelo = (tile & 0xff);
344 
345             if (y < 63 && x < 63 && (*start & 0xff00) == 0xfa00) {
346                 scan_value = *start & 0x00ff;
347             } else {
348                 scan_value = 0xffff;
349             }
350 
351             switch (tilehi) {
352             case 0xfe: // Top/Bottom colors
353                 if (gotcolors) {
354                     break;
355                 }
356                 x++;
357                 tile = *start++;
358                 TopColor = tile & 0xff00;
359                 TopColor |= TopColor >> 8;
360                 BottomColor = tile & 0xff;
361                 BottomColor |= BottomColor << 8;
362                 gotcolors = true;
363                 continue;
364                 break;
365 
366             case 0xFB: // Global Ceiling/Floor textures
367                 if (gottextures) {
368                     break;
369                 }
370                 x++;
371                 tile = *start++;
372 
373                 CeilingTile = START_TEXTURES + ((tile & 0xff00) >> 8);
374                 if (CeilingTile > NUM_TILES - 1) {
375                     ::Quit("Ceiling tile/texture is out of range.");
376                 }
377 
378                 FloorTile = START_TEXTURES + (tile & 0xff);
379                 if (FloorTile > NUM_TILES - 1) {
380                     ::Quit("Floor tile/texture is out of range.");
381                 }
382 
383                 gottextures = true;
384                 continue;
385                 break;
386 
387 
388             case 0xf5: // IntraLevel warp
389                 *(start - 1) = *start; // Move Coord right on top
390                 *start = 0;
391                 continue;
392                 break;
393 
394             case 0xfa:
395                 continue;
396 
397             case 0xf1: // Informant messages
398             case 0xf2: // "Nice" scientist messages
399             case 0xf3: // "Mean" scientist messages
400                 switch (tilehi) {
401                 case 0xf1:
402                     block = static_cast<uint8_t>(INFORMANT_HINTS);
403                     st = &InfHintList;
404                     break;
405 
406                 case 0xf2:
407                     block = static_cast<uint8_t>(NICE_SCIE_HINTS);
408                     st = &NiceSciList;
409                     break;
410 
411                 case 0xf3:
412                     block = static_cast<uint8_t>(MEAN_SCIE_HINTS);
413                     st = &MeanSciList;
414                     break;
415                 }
416 
417                 ci = &st->smInfo[st->NumMsgs];
418                 ci->mInfo.local_val = 0xff;
419                 ci->mInfo.global_val = tilelo;
420                 if (!ReuseMsg((mCacheInfo*)ci, st->NumMsgs, sizeof(sci_mCacheInfo))) {
421                     CacheMsg((mCacheInfo*)ci, block, ci->mInfo.global_val);
422                     ci->mInfo.local_val = static_cast<uint8_t>(InfHintList.NumMsgs);
423                 }
424 
425                 if (++st->NumMsgs > MAX_CACHE_MSGS) {
426                     ::Quit("(INFORMANTS) Too many 'cached msgs' loaded.");
427                 }
428 
429                 ci->areanumber = GetAreaNumber(static_cast<int8_t>(x), static_cast<int8_t>(y));
430 
431                 if (ci->areanumber == 0 || ci->areanumber >= NUMAREAS) {
432                     ci->areanumber = 0xff;
433                 }
434                 continue;
435                 break;
436 
437             case 0:
438                 if (!tilelo) {
439                     continue;
440                 }
441                 break;
442             }
443 
444             //
445             // SPECIAL SPAWN CODING FOR BLASTABLE CRATES...
446             //
447 
448             if (tile >= 432 && tile <= 485) {
449                 if (!::is_aog_sw() && tile >= 468) {
450                     SpawnOffsetObj(en_crate3, x, y);
451                     new_actor->temp2 = ExpCrateShapes[tile - 468];
452                     new_actor->temp3 = static_object_to_ui16(ReserveStatic());
453 
454                     if (tile >= 475 && tile <= 478) {
455                         tile = (tile - 475) + bo_money_bag;
456                     } else {
457                         tile = 0;
458                     }
459                 } else if (!::is_aog_sw() && tile >= 450) {
460                     SpawnOffsetObj(en_crate2, x, y);
461                     new_actor->temp2 = ExpCrateShapes[tile - 450];
462                     new_actor->temp3 = static_object_to_ui16(ReserveStatic());
463 
464                     if (tile >= 457 && tile <= 460) {
465                         tile = (tile - 457) + bo_money_bag;
466                     } else {
467                         tile = 0;
468                     }
469                 } else if (tile >= 432) {
470                     SpawnOffsetObj(en_crate1, x, y);
471                     new_actor->temp2 = ExpCrateShapes[tile - 432];
472                     new_actor->temp3 = static_object_to_ui16(ReserveStatic());
473 
474                     if (tile >= 439 && tile <= 442) {
475                         tile = (tile - 439) + bo_money_bag;
476                     } else {
477                         tile = 0;
478                     }
479                 }
480 
481                 if (tile) {
482                     if (tile > bo_loot) {
483                         tile += 3;
484                     }
485                     tile -= bo_money_bag;
486                     AddTotalPoints(static_points[tile]);
487                 }
488 
489                 continue;
490             }
491 
492             switch (tile) {
493             case 19:
494             case 20:
495             case 21:
496             case 22:
497                 SpawnPlayer(x, y, NORTH + tile - 19);
498                 break;
499 
500             case 30: // Yellow Puddle
501                 if (::is_aog_sw()) {
502                     ::Quit("Yellow puddle (AOG full/PS) at ({}, {}).", x, y);
503                 }
504                 SpawnStatic(x, y, tile - 23);
505                 break;
506 
507             case 71: // BFG Weapon
508                 if (!::is_ps()) {
509                     ::Quit("BFG (PS) at ({}, {}).", x, y);
510                 }
511                 SpawnStatic(x, y, tile - 23);
512                 break;
513 
514             case 85: // Money bag
515             case 86: // Loot
516             case 87: // Gold
517             case 88: // Bonus
518                 AddTotalPoints(static_points[statinfo[tile - 23].type - bo_money_bag]);
519 
520             case 53:
521 
522             case 23:
523             case 24:
524             case 25:
525             case 26:
526             case 27:
527             case 28:
528             case 29:
529 
530             case 31:
531             case 32:
532             case 33:
533             case 34:
534             case 35:
535             case 36:
536             case 37:
537             case 38:
538 
539             case 39:
540             case 40:
541             case 41:
542             case 42:
543             case 43:
544             case 44:
545             case 45:
546             case 46:
547 
548             case 47:
549             case 48:
550             case 49:
551             case 50:
552             case 51:
553             case 52:
554             case 54:
555 
556             case 55:
557             case 56:
558             case 57:
559             case 58:
560             case 59:
561             case 60:
562             case 61:
563             case 62:
564 
565             case 63:
566             case 64:
567             case 65:
568             case 66:
569             case 67:
570             case 68:
571             case 69:
572             case 70:
573 
574             case 72: // Gurney Mutant
575             case 73: // Large Canister
576             case 74: // Small Canister
577             case 75: // Empty Gurney
578             case 76: // Empty Large Canister
579             case 77: // Empty Small Canister
580             case 78: // Dead Gen. Sci.
581 
582             case 80:
583             case 83: // Floor Grate
584             case 84: // Floor Pipe
585                 SpawnStatic(x, y, tile - 23);
586                 break;
587 
588             case 399: // gold 1
589             case 400: // gold 2
590             case 401: // gold 3
591                 AddTotalPoints(static_points[statinfo[tile - 315].type - bo_money_bag]);
592 
593             case 381:
594             case 382:
595             case 383:
596             case 384:
597             case 385:
598             case 386:
599             case 387:
600             case 388:
601             case 390: // candy bar
602             case 391: // sandwich
603 
604             case 395: // Table
605             case 396: // Chair
606             case 397: // Stool
607             case 398: // Gore
608 
609             case 402: //
610             case 403: //
611             case 404: //
612             case 405: //
613             case 406: //
614             case 407: //
615             case 408: //
616             case 409: //
617             case 410: //
618             case 411: //
619             case 412: //
620             case 413: //
621             case 414: //
622             case 415: //
623             case 416: //
624             case 417: //
625             case 418: //
626             case 419: //
627             case 420: //
628             case 421: //
629             case 422: //
630             case 423: // bo_coin
631             case 424: // bo_coin5
632                 SpawnStatic(x, y, tile - 315);
633                 break;
634 
635             case 486: // Plasma Detonator
636                 if (!::is_ps()) {
637                     ::Quit("Plasma detonator (PS) at ({}, {}).", x, y);
638                 }
639 
640                 SpawnHiddenOfs(en_plasma_detonator_reserve, x, y); // Spawn a reserve
641                 SpawnStatic(x, y, 486 - 375);
642                 break;
643 
644             case 487: // Door rubble
645             case 488: // AutoMapper Bonus #1
646             case 489: // BonziTree
647             case 490: // Yellow Potted plant
648             case 491: // Tube Plant
649             case 492: // HiTech Chair
650             case 493: // AOG: Rent A Cop - Dead.
651             case 494: // AOG: Pro Guard - Dead.
652             case 495: // AOG: Swat Guard - Dead.
653                 SpawnStatic(x, y, tile - 375);
654                 break;
655 
656 
657             case 393: // crate 2
658             case 394: // crate 3
659             case 392: // crate 1
660                 SpawnStatic(x, y, tile - 315);
661                 break;
662 
663             case 81:
664             case 82:
665                 SpawnOffsetObj(static_cast<enemy_t>(en_bloodvent + tile - 81), x, y);
666                 break;
667 
668 
669             //
670             // GREEN OOZE
671             //
672 
673             case 208:
674                 if (gamestate.difficulty < gd_hard) {
675                     break;
676                 }
677             case 207:
678                 if (gamestate.difficulty < gd_medium) {
679                     break;
680                 }
681             case 206:
682                 SpawnOffsetObj(en_green_ooze, x, y);
683                 break;
684 
685 
686             //
687             // BLACK OOZE
688             //
689 
690             case 212:
691                 if (gamestate.difficulty < gd_hard) {
692                     break;
693                 }
694             case 211:
695                 if (gamestate.difficulty < gd_medium) {
696                     break;
697                 }
698             case 210:
699                 SpawnOffsetObj(en_black_ooze, x, y);
700                 break;
701 
702 
703 
704             // Flickering Light
705             //
706             case 79:
707                 SpawnOffsetObj(en_flickerlight, x, y);
708                 new_actor->lighting = LAMP_ON_SHADING;
709                 break;
710 
711 
712             case 174:
713                 if (::is_ps()) {
714                     ::SpawnBarrier(en_post_barrier, x, y, false);
715                 } else {
716                     ::SpawnBarrier(en_arc_barrier, x, y, true);
717                 }
718                 break;
719 
720             case 175:
721                 //
722                 // 174=off,175=on
723                 //
724                 SpawnBarrier(en_post_barrier, x, y, (tile - 174) != 0);
725                 break;
726 
727             case 138:
728             case 139:
729                 if (!::is_ps()) {
730                     ::Quit("Switchable arc barrier (PS) at ({}, {}).", x, y);
731                 }
732 
733                 //
734                 // 138=off,139=on
735                 //
736                 SpawnBarrier(en_arc_barrier, x, y, (tile - 138) != 0);
737                 break;
738 
739             //
740             // VPOST Barrier
741             //
742 
743             //
744             // Switchable
745             //
746             case 563: // On
747             case 562: // Off
748                 if (!::is_ps()) {
749                     ::Quit("Switchable post barrier (PS) at ({}, {}).", x, y);
750                 }
751 
752                 SpawnBarrier(en_vpost_barrier, x, y, (tile - 562) != 0);
753                 break;
754 
755 
756             //
757             // Cycle
758             //
759             case 567:
760                 if (gamestate.difficulty < gd_hard) {
761                     break;
762                 }
763             case 566:
764                 if (gamestate.difficulty < gd_medium) {
765                     break;
766                 }
767             case 565:
768                 if (!::is_ps()) {
769                     ::Quit("Cyclic post barrier (PS) at ({}, {}).", x, y);
770                 }
771 
772                 SpawnBarrier(en_vpost_barrier, x, y, 0);
773                 break;
774 
775             //
776             // VSPIKE Barrier
777             //
778 
779             //
780             // Switchable
781             //
782             case 426: // On
783             case 425: // Off
784                 if (!::is_ps()) {
785                     ::Quit("Spike barrier (PS) at ({}, {}).", x, y);
786                 }
787 
788                 SpawnBarrier(en_vspike_barrier, x, y, (tile - 425) != 0);
789                 break;
790 
791 
792             //
793             // Cycle
794             //
795             case 430:
796                 if (gamestate.difficulty < gd_hard) {
797                     break;
798                 }
799             case 429:
800                 if (gamestate.difficulty < gd_medium) {
801                     break;
802                 }
803             case 428:
804                 if (!::is_ps()) {
805                     ::Quit("Cyclic spike barrier (PS) at ({}, {}).", x, y);
806                 }
807                 SpawnBarrier(en_vspike_barrier, x, y, 0);
808                 break;
809 
810             //
811             // STEAM GRATE
812             //
813 
814             case 178:
815                 SpawnStand(en_steamgrate, x, y, 0);
816                 break;
817 
818             //
819             // STEAM PIPE
820             //
821 
822             case 179:
823                 SpawnStand(en_steampipe, x, y, 0);
824                 break;
825 
826 
827             //
828             // GOLDFIRE SPAWN SITES
829             //
830             case 124:
831                 if (!loadedgame) {
832                     if (GoldsternInfo.SpawnCnt == GOLDIE_MAX_SPAWNS) {
833                         ::Quit("Too many Dr. Goldfire Spawn sites in level.");
834                     }
835                     GoldsternInfo.flags = GS_FIRSTTIME;
836                     if (gamestate.mapon == 9) {
837                         GoldsternInfo.WaitTime = 60;
838                     } else {
839                         GoldsternInfo.WaitTime = MIN_GOLDIE_FIRST_WAIT + Random(MAX_GOLDIE_FIRST_WAIT - MIN_GOLDIE_FIRST_WAIT);
840                     }
841                     GoldieList[GoldsternInfo.SpawnCnt].tilex = static_cast<uint8_t>(x);
842                     GoldieList[GoldsternInfo.SpawnCnt].tiley = static_cast<uint8_t>(y);
843                     GoldsternInfo.SpawnCnt++;
844 
845                     if (::is_ps() && gamestate.mapon == GOLD_MORPH_LEVEL) {
846                         AddTotalPoints(actor_points[goldsternobj - rentacopobj]);
847                         AddTotalEnemy(1);
848                     }
849                 }
850                 break;
851 
852             //
853             // GOLDFIRE SPAWN - IMMEDEATLY
854             //
855 
856             case 141:
857                 if (!::is_ps()) {
858                     ::Quit("Goldstern spawn (PS) at ({}, {}).", x, y);
859                 }
860 
861                 if (!loadedgame) {
862                     if (GoldsternInfo.GoldSpawned) {
863                         ::Quit("Too many FAST Goldfire spawn sites in map.");
864                     }
865 
866                     if (GoldsternInfo.SpawnCnt == GOLDIE_MAX_SPAWNS) {
867                         ::Quit("Too many Dr. Goldfire Spawn sites in level.");
868                     }
869 
870                     GoldieList[GoldsternInfo.SpawnCnt].tilex = static_cast<uint8_t>(x);
871                     GoldieList[GoldsternInfo.SpawnCnt].tiley = static_cast<uint8_t>(y);
872 
873                     GoldsternInfo.LastIndex = GoldsternInfo.SpawnCnt++;
874                     GoldsternInfo.flags = GS_COORDFOUND;
875 
876                     SpawnStand(en_goldstern, x, y, 0);
877                     GoldsternInfo.GoldSpawned = true;
878                     new_actor = nullptr;
879                 }
880                 break;
881 
882 
883             //
884             // SECURITY LIGHT
885             //
886             case 160:
887                 SpawnOffsetObj(en_security_light, x, y);
888                 break;
889 
890 
891             //
892             // Projection Generator (AOG) / Rotating Cube (PS)
893             //
894 
895             case 177:
896                 ::SpawnOffsetObj(en_rotating_cube, x, y);
897 
898                 if (::is_ps()) {
899                     ::new_actor = nullptr;
900                 }
901 
902                 break;
903 
904             //
905             // RENT-A-COP
906             //
907             case 180:
908             case 181:
909             case 182:
910             case 183:
911                 if (gamestate.difficulty < gd_hard) {
912                     break;
913                 }
914                 tile -= 36;
915             case 144:
916             case 145:
917             case 146:
918             case 147:
919                 if (gamestate.difficulty < gd_medium) {
920                     break;
921                 }
922                 tile -= 36;
923             case 108:
924             case 109:
925             case 110:
926             case 111:
927                 SpawnStand(en_rentacop, x, y, tile - 108);
928                 break;
929 
930 
931             case 184:
932             case 185:
933             case 186:
934             case 187:
935                 if (gamestate.difficulty < gd_hard) {
936                     break;
937                 }
938                 tile -= 36;
939             case 148:
940             case 149:
941             case 150:
942             case 151:
943                 if (gamestate.difficulty < gd_medium) {
944                     break;
945                 }
946                 tile -= 36;
947             case 112:
948             case 113:
949             case 114:
950             case 115:
951                 SpawnPatrol(en_rentacop, x, y, tile - 112);
952                 break;
953 
954 
955             //
956             // officer
957             //
958             case 188:
959             case 189:
960             case 190:
961             case 191:
962                 if (gamestate.difficulty < gd_hard) {
963                     break;
964                 }
965                 tile -= 36;
966             case 152:
967             case 153:
968             case 154:
969             case 155:
970                 if (gamestate.difficulty < gd_medium) {
971                     break;
972                 }
973                 tile -= 36;
974             case 116:
975             case 117:
976             case 118:
977             case 119:
978                 SpawnStand(en_gen_scientist, x, y, tile - 116);
979                 if (new_actor->flags & FL_INFORMANT) {
980                     AddTotalInformants(1);
981                     new_actor = nullptr;
982                 }
983                 break;
984 
985 
986             case 192:
987             case 193:
988             case 194:
989             case 195:
990                 if (gamestate.difficulty < gd_hard) {
991                     break;
992                 }
993                 tile -= 36;
994             case 156:
995             case 157:
996             case 158:
997             case 159:
998                 if (gamestate.difficulty < gd_medium) {
999                     break;
1000                 }
1001                 tile -= 36;
1002             case 120:
1003             case 121:
1004             case 122:
1005             case 123:
1006                 SpawnPatrol(en_gen_scientist, x, y, tile - 120);
1007                 if (new_actor->flags & FL_INFORMANT) {
1008                     AddTotalInformants(1);
1009                     new_actor = nullptr;
1010                 }
1011                 break;
1012 
1013 
1014             //
1015             //  PROGUARD
1016             //
1017             case 198:
1018             case 199:
1019             case 200:
1020             case 201:
1021                 if (gamestate.difficulty < gd_hard) {
1022                     break;
1023                 }
1024                 tile -= 36;
1025             case 162:
1026             case 163:
1027             case 164:
1028             case 165:
1029                 if (gamestate.difficulty < gd_medium) {
1030                     break;
1031                 }
1032                 tile -= 36;
1033             case 126:
1034             case 127:
1035             case 128:
1036             case 129:
1037                 SpawnStand(en_proguard, x, y, tile - 126);
1038                 break;
1039 
1040 
1041             case 202:
1042             case 203:
1043             case 204:
1044             case 205:
1045                 if (gamestate.difficulty < gd_hard) {
1046                     break;
1047                 }
1048                 tile -= 36;
1049             case 166:
1050             case 167:
1051             case 168:
1052             case 169:
1053                 if (gamestate.difficulty < gd_medium) {
1054                     break;
1055                 }
1056                 tile -= 36;
1057             case 130:
1058             case 131:
1059             case 132:
1060             case 133:
1061                 SpawnPatrol(en_proguard, x, y, tile - 130);
1062                 break;
1063 
1064 
1065 
1066             case 312:
1067                 if (gamestate.difficulty < gd_hard) {
1068                     break;
1069                 }
1070 
1071             case 311:
1072                 if (gamestate.difficulty < gd_medium) {
1073                     break;
1074                 }
1075 
1076             case 310:
1077                 SpawnStand(en_electro_alien, x, y, 0);
1078                 new_actor = nullptr;
1079                 break;
1080 
1081 
1082             //
1083             // FLOATING BOMB - Stationary
1084             //
1085 
1086             case 364:
1087             case 365:
1088             case 366:
1089             case 367:
1090                 if (gamestate.difficulty < gd_hard) {
1091                     break;
1092                 }
1093                 tile -= 18;
1094             case 346:
1095             case 347:
1096             case 348:
1097             case 349:
1098                 if (gamestate.difficulty < gd_medium) {
1099                     break;
1100                 }
1101                 tile -= 18;
1102             case 328:
1103             case 329:
1104             case 330:
1105             case 331:
1106                 SpawnStand(en_floatingbomb, x, y, tile - 328);
1107                 new_actor->flags |= FL_STATIONARY;
1108                 break;
1109 
1110 
1111             //
1112             // FLOATING BOMB - Start Stationary
1113             //
1114 
1115             case 296:
1116             case 297:
1117             case 298:
1118             case 299:
1119                 if (gamestate.difficulty < gd_hard) {
1120                     break;
1121                 }
1122                 tile -= 18;
1123             case 278:
1124             case 279:
1125             case 280:
1126             case 281:
1127                 if (gamestate.difficulty < gd_medium) {
1128                     break;
1129                 }
1130                 tile -= 18;
1131             case 260:
1132             case 261:
1133             case 262:
1134             case 263:
1135                 SpawnStand(en_floatingbomb, x, y, tile - 260);
1136                 break;
1137 
1138 
1139             //
1140             // FLOATING BOMB - Start Moving
1141             //
1142 
1143             case 300:
1144             case 301:
1145             case 302:
1146             case 303:
1147                 if (gamestate.difficulty < gd_hard) {
1148                     break;
1149                 }
1150                 tile -= 18;
1151             case 282:
1152             case 283:
1153             case 284:
1154             case 285:
1155                 if (gamestate.difficulty < gd_medium) {
1156                     break;
1157                 }
1158                 tile -= 18;
1159             case 264:
1160             case 265:
1161             case 266:
1162             case 267:
1163                 SpawnPatrol(en_floatingbomb, x, y, tile - 264);
1164                 break;
1165 
1166 
1167                 //
1168                 // VOLATILE MAT. TRANSPORT - Stationary
1169                 //
1170             case 350:
1171             case 351:
1172             case 352:
1173             case 353:
1174                 if (gamestate.difficulty < gd_hard) {
1175                     break;
1176                 }
1177                 tile -= 18;
1178             case 332:
1179             case 333:
1180             case 334:
1181             case 335:
1182                 if (gamestate.difficulty < gd_medium) {
1183                     break;
1184                 }
1185                 tile -= 18;
1186             case 314:
1187             case 315:
1188             case 316:
1189             case 317:
1190                 SpawnStand(en_volatiletransport, x, y, tile - 314);
1191                 break;
1192 
1193 
1194             //
1195             // Black Ooze
1196             //
1197             case 313:
1198                 if (!::is_ps()) {
1199                     ::Quit("Black ooze (PS) at ({}, {}).", x, y);
1200                 }
1201 
1202                 if (gamestate.difficulty < gd_hard) {
1203                     break;
1204                 }
1205                 tile -= 18;
1206             case 295:
1207                 if (!::is_ps()) {
1208                     ::Quit("Black ooze (PS) at ({}, {}).", x, y);
1209                 }
1210 
1211                 if (gamestate.difficulty < gd_medium) {
1212                     break;
1213                 }
1214                 tile -= 18;
1215             case 277:
1216                 if (!::is_ps()) {
1217                     ::Quit("Black ooze (PS) at ({}, {}).", x, y);
1218                 }
1219 
1220                 SpawnOffsetObj(en_black2_ooze, x, y);
1221                 break;
1222 
1223 
1224 
1225             //
1226             // Green Ooze
1227             //
1228             case 322:
1229                 if (!::is_ps()) {
1230                     ::Quit("Green ooze (PS) at ({}, {}).", x, y);
1231                 }
1232 
1233                 if (gamestate.difficulty < gd_hard) {
1234                     break;
1235                 }
1236                 tile -= 18;
1237             case 304:
1238                 if (!::is_ps()) {
1239                     ::Quit("Green ooze (PS) at ({}, {}).", x, y);
1240                 }
1241 
1242                 if (gamestate.difficulty < gd_medium) {
1243                     break;
1244                 }
1245                 tile -= 18;
1246             case 286:
1247                 if (!::is_ps()) {
1248                     ::Quit("Green ooze (PS) at ({}, {}).", x, y);
1249                 }
1250 
1251                 SpawnOffsetObj(en_green2_ooze, x, y);
1252                 break;
1253 
1254 
1255                 //
1256                 // VOLATILE MAT. TRANSPORT - Moving
1257                 //
1258             case 354:
1259             case 355:
1260             case 356:
1261             case 357:
1262                 if (::is_aog_sw()) {
1263                     INVALID_ACTOR_ERR;
1264                 }
1265 
1266                 if (gamestate.difficulty < gd_hard) {
1267                     break;
1268                 }
1269                 tile -= 18;
1270             case 336:
1271             case 337:
1272             case 338:
1273             case 339:
1274                 if (::is_aog_sw()) {
1275                     INVALID_ACTOR_ERR;
1276                 }
1277 
1278                 if (gamestate.difficulty < gd_medium) {
1279                     break;
1280                 }
1281                 tile -= 18;
1282             case 318:
1283             case 319:
1284             case 320:
1285             case 321:
1286                 if (::is_aog_sw()) {
1287                     INVALID_ACTOR_ERR;
1288                 }
1289 
1290                 SpawnPatrol(en_volatiletransport, x, y, tile - 318);
1291                 break;
1292 
1293             //
1294             // Genetic Guard
1295             //
1296 
1297             case 143:
1298                 if (gamestate.difficulty < gd_hard) {
1299                     break;
1300                 }
1301             case 142:
1302                 if (gamestate.difficulty < gd_medium) {
1303                     break;
1304                 }
1305             case 214:
1306                 SpawnOffsetObj(en_genetic_guard, x, y);
1307                 break;
1308 
1309 
1310                 //
1311                 // Cyborg Warrior
1312                 //
1313             case 603:
1314                 if (!::is_ps()) {
1315                     INVALID_ACTOR_ERR;
1316                 }
1317 
1318                 if (gamestate.difficulty < gd_hard) {
1319                     break;
1320                 }
1321             case 585:
1322                 if (!::is_ps()) {
1323                     INVALID_ACTOR_ERR;
1324                 }
1325 
1326                 if (gamestate.difficulty < gd_medium) {
1327                     break;
1328                 }
1329             case 250:
1330                 SpawnOffsetObj(en_cyborg_warrior, x, y);
1331                 break;
1332 
1333 
1334                 //
1335                 // Spider Mutant
1336                 //
1337             case 601:
1338                 if (!::is_ps()) {
1339                     INVALID_ACTOR_ERR;
1340                 }
1341 
1342                 if (gamestate.difficulty < gd_hard) {
1343                     break;
1344                 }
1345             case 583:
1346                 if (!::is_ps()) {
1347                     INVALID_ACTOR_ERR;
1348                 }
1349 
1350                 if (gamestate.difficulty < gd_medium) {
1351                     break;
1352                 }
1353             case 232:
1354                 SpawnOffsetObj(en_spider_mutant, x, y);
1355                 break;
1356 
1357                 //
1358                 // Acid Dragon
1359                 //
1360             case 605:
1361                 if (!::is_ps()) {
1362                     INVALID_ACTOR_ERR;
1363                 }
1364 
1365                 if (gamestate.difficulty < gd_hard) {
1366                     break;
1367                 }
1368             case 587:
1369                 if (!::is_ps()) {
1370                     INVALID_ACTOR_ERR;
1371                 }
1372 
1373                 if (gamestate.difficulty < gd_medium) {
1374                     break;
1375                 }
1376 
1377             case 268:
1378                 SpawnOffsetObj(en_acid_dragon, x, y);
1379                 break;
1380 
1381                 //
1382                 // Breather beast
1383                 //
1384             case 602:
1385                 if (!::is_ps()) {
1386                     INVALID_ACTOR_ERR;
1387                 }
1388 
1389                 if (gamestate.difficulty < gd_hard) {
1390                     break;
1391                 }
1392             case 584:
1393                 if (!::is_ps()) {
1394                     INVALID_ACTOR_ERR;
1395                 }
1396 
1397                 if (gamestate.difficulty < gd_medium) {
1398                     break;
1399                 }
1400 
1401             case 233:
1402                 SpawnOffsetObj(en_breather_beast, x, y);
1403                 break;
1404 
1405             //
1406             // Mech Guardian
1407             //
1408             case 606:
1409                 if (!::is_ps()) {
1410                     INVALID_ACTOR_ERR;
1411                 }
1412 
1413                 if (gamestate.difficulty < gd_hard) {
1414                     break;
1415                 }
1416             case 588:
1417                 if (!::is_ps()) {
1418                     INVALID_ACTOR_ERR;
1419                 }
1420 
1421                 if (gamestate.difficulty < gd_medium) {
1422                     break;
1423                 }
1424 
1425             case 269:
1426                 SpawnOffsetObj(en_mech_guardian, x, y);
1427                 break;
1428 
1429             //
1430             // Reptilian Warrior
1431             //
1432             case 604:
1433                 if (!::is_ps()) {
1434                     INVALID_ACTOR_ERR;
1435                 }
1436 
1437                 if (gamestate.difficulty < gd_hard) {
1438                     break;
1439                 }
1440             case 586:
1441                 if (!::is_ps()) {
1442                     INVALID_ACTOR_ERR;
1443                 }
1444 
1445                 if (gamestate.difficulty < gd_medium) {
1446                     break;
1447                 }
1448 
1449             case 251:
1450                 SpawnOffsetObj(en_reptilian_warrior, x, y);
1451                 break;
1452 
1453 
1454             //
1455             // Mutant Human type 1
1456             //
1457 
1458             case 105:
1459                 if (gamestate.difficulty < gd_hard) {
1460                     break;
1461                 }
1462             case 104:
1463                 if (gamestate.difficulty < gd_medium) {
1464                     break;
1465                 }
1466             case 103:
1467                 SpawnOffsetObj(en_mutant_human1, x, y);
1468                 break;
1469 
1470 
1471                 //
1472                 // Mutant Human type 2
1473                 //
1474             case 125:
1475                 if (gamestate.difficulty < gd_hard) {
1476                     break;
1477                 }
1478             case 107:
1479                 if (gamestate.difficulty < gd_medium) {
1480                     break;
1481                 }
1482             case 106:
1483                 SpawnOffsetObj(en_mutant_human2, x, y);
1484                 break;
1485 
1486 
1487             //
1488             // Small Canister Alien - CONTAINED
1489             //
1490 
1491             case 136:
1492                 if (gamestate.difficulty < gd_hard) {
1493                     SpawnStatic(x, y, 74 - 23);
1494                     break;
1495                 }
1496             case 135:
1497                 if (gamestate.difficulty < gd_medium) {
1498                     SpawnStatic(x, y, 74 - 23);
1499                     break;
1500                 }
1501             case 134:
1502                 SpawnOffsetObj(en_scan_wait_alien, x, y);
1503                 break;
1504 
1505 
1506 
1507                 //
1508                 // Large Canister Alien - CONTAINED
1509                 //
1510             case 172:
1511                 if (gamestate.difficulty < gd_hard) {
1512                     SpawnStatic(x, y, 73 - 23);
1513                     break;
1514                 }
1515             case 171:
1516                 if (gamestate.difficulty < gd_medium) {
1517                     SpawnStatic(x, y, 73 - 23);
1518                     break;
1519                 }
1520             case 170:
1521                 SpawnOffsetObj(en_lcan_wait_alien, x, y);
1522                 break;
1523 
1524 
1525                 //
1526                 // Gurney Mutant - ASLEEP
1527                 //
1528 
1529             case 161:
1530                 if (gamestate.difficulty < gd_hard) {
1531                     SpawnStatic(x, y, 72 - 23);
1532                     break;
1533                 }
1534             case 173:
1535                 if (gamestate.difficulty < gd_medium) {
1536                     SpawnStatic(x, y, 72 - 23);
1537                     break;
1538                 }
1539             case 137:
1540                 SpawnOffsetObj(en_gurney_wait, x, y);
1541                 break;
1542 
1543             //
1544             // Small Canister Alien - ACTIVE/WALKING
1545             //
1546 
1547             case 288:
1548                 if (gamestate.difficulty < gd_hard) {
1549                     break;
1550                 }
1551             case 289:
1552                 if (gamestate.difficulty < gd_medium) {
1553                     break;
1554                 }
1555             case 290:
1556                 SpawnOffsetObj(en_scan_alien, x, y);
1557                 break;
1558 
1559 
1560                 //
1561                 // Large Canister Alien - ACTIVE/WALKING
1562                 //
1563             case 270:
1564                 if (gamestate.difficulty < gd_hard) {
1565                     break;
1566                 }
1567             case 271:
1568                 if (gamestate.difficulty < gd_medium) {
1569                     break;
1570                 }
1571             case 272:
1572                 SpawnOffsetObj(en_lcan_alien, x, y);
1573                 break;
1574 
1575 
1576                 //
1577                 // Gurney Mutant - AWAKE
1578                 //
1579             case 275:
1580                 if (gamestate.difficulty < gd_hard) {
1581                     break;
1582                 }
1583             case 274:
1584                 if (gamestate.difficulty < gd_medium) {
1585                     break;
1586                 }
1587             case 273:
1588                 SpawnOffsetObj(en_gurney, x, y);
1589                 break;
1590 
1591 
1592             case 293:
1593                 if (gamestate.difficulty < gd_hard) {
1594                     break;
1595                 }
1596             case 292:
1597                 if (gamestate.difficulty < gd_medium) {
1598                     break;
1599                 }
1600             case 291:
1601                 SpawnStand(en_liquid, x, y, 0);
1602                 break;
1603 
1604 
1605             // P.O.D. Alien Egg
1606             //
1607             case 294:
1608                 if (gamestate.difficulty < gd_hard) {
1609                     scan_value = 0xff;
1610                 }
1611 
1612             case 276:
1613                 if (gamestate.difficulty < gd_medium) {
1614                     scan_value = 0xff;
1615                 }
1616 
1617             case 306:
1618                 SpawnOffsetObj(en_podegg, x, y);
1619                 if (scan_value == 0xff) {
1620                     new_actor->obclass = deadobj;
1621                 } else {
1622                     AddTotalPoints(actor_points[podobj - rentacopobj]);
1623                     AddTotalEnemy(1);
1624                 }
1625                 scan_value = 0xffff;
1626                 break;
1627 
1628             // Morphing Brown/LBlue Post -> Spider Mutant
1629             //
1630             case 610:
1631                 if (!::is_ps()) {
1632                     INVALID_ACTOR_ERR;
1633                 }
1634 
1635                 if (gamestate.difficulty < gd_hard) {
1636                     scan_value = 0xff;
1637                 }
1638 
1639             case 609:
1640                 if (!::is_ps()) {
1641                     INVALID_ACTOR_ERR;
1642                 }
1643 
1644                 if (gamestate.difficulty < gd_medium) {
1645                     scan_value = 0xff;
1646                 }
1647 
1648             case 608:
1649                 if (!::is_ps()) {
1650                     INVALID_ACTOR_ERR;
1651                 }
1652 
1653                 if (scan_value == 0xff) {
1654                     SpawnStatic(x, y, 402 - 315);
1655                 } else {
1656                     AddTotalPoints(actor_points[en_spider_mutant]);
1657                     AddTotalEnemy(1);
1658                     SpawnOffsetObj(en_morphing_spider_mutant, x, y);
1659                 }
1660                 scan_value = 0xffff;
1661                 break;
1662 
1663 
1664             // Morphing Gray/Green Post -> Reptilian Warrior
1665             //
1666             case 592:
1667                 if (!::is_ps()) {
1668                     INVALID_ACTOR_ERR;
1669                 }
1670 
1671                 if (gamestate.difficulty < gd_hard) {
1672                     scan_value = 0xff;
1673                 }
1674 
1675             case 591:
1676                 if (!::is_ps()) {
1677                     INVALID_ACTOR_ERR;
1678                 }
1679 
1680                 if (gamestate.difficulty < gd_medium) {
1681                     scan_value = 0xff;
1682                 }
1683 
1684             case 590:
1685                 if (!::is_ps()) {
1686                     INVALID_ACTOR_ERR;
1687                 }
1688 
1689                 if (scan_value == 0xff) {
1690                     SpawnStatic(x, y, 403 - 315);
1691                 } else {
1692                     AddTotalPoints(actor_points[en_reptilian_warrior]);
1693                     AddTotalEnemy(1);
1694                     SpawnOffsetObj(en_morphing_reptilian_warrior, x, y);
1695                 }
1696                 scan_value = 0xffff;
1697                 break;
1698 
1699 
1700 
1701             // Morphing Statue -> Blue Boy
1702             //
1703             case 628:
1704                 if (!::is_ps()) {
1705                     INVALID_ACTOR_ERR;
1706                 }
1707 
1708                 if (gamestate.difficulty < gd_hard) {
1709                     scan_value = 0xff;
1710                 }
1711 
1712             case 627:
1713                 if (!::is_ps()) {
1714                     INVALID_ACTOR_ERR;
1715                 }
1716 
1717                 if (gamestate.difficulty < gd_medium) {
1718                     scan_value = 0xff;
1719                 }
1720 
1721             case 626:
1722                 if (!::is_ps()) {
1723                     INVALID_ACTOR_ERR;
1724                 }
1725 
1726                 if (scan_value == 0xff) {
1727                     SpawnStatic(x, y, 48 - 23);
1728                 } else {
1729                     AddTotalPoints(actor_points[en_mutant_human2]);
1730                     AddTotalEnemy(1);
1731                     SpawnOffsetObj(en_morphing_mutanthuman2, x, y);
1732                 }
1733                 scan_value = 0xffff;
1734                 break;
1735 
1736 
1737             // P.O.D. Alien
1738             //
1739             case 309:
1740                 if (gamestate.difficulty < gd_hard) {
1741                     break;
1742                 }
1743 
1744             case 308:
1745                 if (gamestate.difficulty < gd_medium) {
1746                     break;
1747                 }
1748 
1749             case 307:
1750                 SpawnOffsetObj(en_pod, x, y);
1751                 break;
1752 
1753 
1754             // Electro-Sphere - Vertical Hover
1755             //
1756             case 360:
1757                 if (gamestate.difficulty < gd_hard) {
1758                     break;
1759                 }
1760 
1761             case 342:
1762                 if (gamestate.difficulty < gd_medium) {
1763                     break;
1764                 }
1765 
1766             case 324:
1767                 SpawnOffsetObj(en_vertsphere, x, y);
1768                 break;
1769 
1770             // Electro-Sphere - Horizontal Hover
1771             //
1772             case 361:
1773                 if (gamestate.difficulty < gd_hard) {
1774                     break;
1775                 }
1776 
1777             case 343:
1778                 if (gamestate.difficulty < gd_medium) {
1779                     break;
1780                 }
1781 
1782             case 325:
1783                 SpawnOffsetObj(en_horzsphere, x, y);
1784                 break;
1785 
1786             // Electro-Sphere - Diagonal Hover
1787             //
1788             case 362:
1789                 if (gamestate.difficulty < gd_hard) {
1790                     break;
1791                 }
1792 
1793             case 344:
1794                 if (gamestate.difficulty < gd_medium) {
1795                     break;
1796                 }
1797 
1798             case 326:
1799                 SpawnOffsetObj(en_diagsphere, x, y);
1800                 break;
1801 
1802 
1803 
1804 
1805             //
1806             // Stationary SWAT Guards
1807             //
1808 
1809             case 252:
1810             case 253:
1811             case 254:
1812             case 255:
1813                 if (gamestate.difficulty < gd_hard) {
1814                     break;
1815                 }
1816                 tile -= 18;
1817             case 234:
1818             case 235:
1819             case 236:
1820             case 237:
1821                 if (gamestate.difficulty < gd_medium) {
1822                     break;
1823                 }
1824                 tile -= 18;
1825             case 216:
1826             case 217:
1827             case 218:
1828             case 219:
1829                 SpawnStand(en_swat, x, y, tile - 216);
1830                 break;
1831 
1832 
1833             //
1834             // Roaming SWAT Guards
1835             //
1836 
1837             case 256:
1838             case 257:
1839             case 258:
1840             case 259:
1841                 if (gamestate.difficulty < gd_hard) {
1842                     break;
1843                 }
1844                 tile -= 18;
1845             case 238:
1846             case 239:
1847             case 240:
1848             case 241:
1849                 if (gamestate.difficulty < gd_medium) {
1850                     break;
1851                 }
1852                 tile -= 18;
1853             case 220:
1854             case 221:
1855             case 222:
1856             case 223:
1857                 SpawnPatrol(en_swat, x, y, tile - 220);
1858                 break;
1859 
1860             //
1861             // STATIONARY HANGING TURRETS
1862             //
1863             case 368:
1864             case 369:
1865             case 370:
1866             case 371:
1867                 if (gamestate.difficulty < gd_hard) {
1868                     break;
1869                 }
1870                 tile -= 126;
1871             case 242:
1872             case 243:
1873             case 244:
1874             case 245:
1875                 if (gamestate.difficulty < gd_medium) {
1876                     break;
1877                 }
1878                 tile -= 18;
1879             case 224:
1880             case 225:
1881             case 226:
1882             case 227:
1883                 SpawnStand(en_hang_terrot, x, y, tile - 224);
1884                 new_actor->flags |= FL_STATIONARY;
1885                 break;
1886 
1887 
1888             //
1889             // ROTATING HANGING TURRETS
1890             //
1891             case 372:
1892             case 373:
1893             case 374:
1894             case 375:
1895                 if (gamestate.difficulty < gd_hard) {
1896                     break;
1897                 }
1898                 tile -= 126;
1899             case 246:
1900             case 247:
1901             case 248:
1902             case 249:
1903                 if (gamestate.difficulty < gd_medium) {
1904                     break;
1905                 }
1906                 tile -= 18;
1907             case 228:
1908             case 229:
1909             case 230:
1910             case 231:
1911                 SpawnStand(en_hang_terrot, x, y, tile - 228);
1912                 break;
1913 
1914 
1915 
1916 // --------------------------
1917 // PATH OBJECTS
1918 // --------------------------
1919             //
1920             // Swat Guards
1921             //
1922 
1923             case 540:
1924             case 541:
1925             case 542:
1926             case 543:
1927                 if (gamestate.difficulty < gd_hard) {
1928                     break;
1929                 }
1930                 tile -= 18;
1931             case 522:
1932             case 523:
1933             case 524:
1934             case 525:
1935                 if (gamestate.difficulty < gd_medium) {
1936                     break;
1937                 }
1938                 tile -= 18;
1939             case 504:
1940             case 505:
1941             case 506:
1942             case 507:
1943                 SpawnPatrol(en_swat, x, y, tile - 504);
1944                 new_actor->flags &= ~FL_RANDOM_TURN;
1945                 break;
1946 
1947             //
1948             // VOLATILE MAT. TRANSPORT
1949             //
1950 
1951             case 548:
1952             case 549:
1953             case 550:
1954             case 551:
1955                 if (gamestate.difficulty < gd_hard) {
1956                     break;
1957                 }
1958                 tile -= 18;
1959             case 530:
1960             case 531:
1961             case 532:
1962             case 533:
1963                 if (gamestate.difficulty < gd_medium) {
1964                     break;
1965                 }
1966                 tile -= 18;
1967             case 512:
1968             case 513:
1969             case 514:
1970             case 515:
1971                 SpawnPatrol(en_volatiletransport, x, y, tile - 512);
1972                 new_actor->flags &= ~FL_RANDOM_TURN;
1973                 break;
1974 
1975             //
1976             //  FLOATING BOMB -
1977             //
1978 
1979             case 544:
1980             case 545:
1981             case 546:
1982             case 547:
1983                 if (gamestate.difficulty < gd_hard) {
1984                     break;
1985                 }
1986                 tile -= 18;
1987             case 526:
1988             case 527:
1989             case 528:
1990             case 529:
1991                 if (gamestate.difficulty < gd_medium) {
1992                     break;
1993                 }
1994                 tile -= 18;
1995             case 508:
1996             case 509:
1997             case 510:
1998             case 511:
1999                 SpawnPatrol(en_floatingbomb, x, y, tile - 508);
2000                 new_actor->flags &= ~FL_RANDOM_TURN;
2001                 break;
2002 
2003             //
2004             // PRO GUARD
2005             //
2006 
2007             case 594:
2008             case 595:
2009             case 596:
2010             case 597:
2011                 if (gamestate.difficulty < gd_hard) {
2012                     break;
2013                 }
2014                 tile -= 18;
2015             case 576:
2016             case 577:
2017             case 578:
2018             case 579:
2019                 if (gamestate.difficulty < gd_medium) {
2020                     break;
2021                 }
2022                 tile -= 18;
2023             case 558:
2024             case 559:
2025             case 560:
2026             case 561:
2027                 SpawnPatrol(en_proguard, x, y, tile - 558);
2028                 new_actor->flags &= ~FL_RANDOM_TURN;
2029                 break;
2030 
2031 
2032             //
2033             // RENT-A-COP
2034             //
2035 
2036             case 552:
2037             case 553:
2038             case 554:
2039             case 555:
2040                 if (gamestate.difficulty < gd_hard) {
2041                     break;
2042                 }
2043                 tile -= 18;
2044             case 534:
2045             case 535:
2046             case 536:
2047             case 537:
2048                 if (gamestate.difficulty < gd_medium) {
2049                     break;
2050                 }
2051                 tile -= 18;
2052             case 516:
2053             case 517:
2054             case 518:
2055             case 519:
2056                 SpawnPatrol(en_rentacop, x, y, tile - 516);
2057                 new_actor->flags &= ~FL_RANDOM_TURN;
2058                 break;
2059 
2060 // -----------------------
2061 // BOSS ACTORS
2062 // -----------------------
2063 
2064             case 630: // FINAL BOSS 1
2065             case 631: // FINAL BOSS 2
2066             case 632: // FINAL BOSS 3
2067             case 633: // FINAL BOSS 4
2068                 if (!::is_ps()) {
2069                     INVALID_ACTOR_ERR;
2070                 }
2071 
2072                 SpawnOffsetObj(static_cast<enemy_t>(en_final_boss1 + tile - 630), x, y);
2073                 break;
2074             }
2075 
2076             // If "new_actor" is an object that gives points, add those points to level total...
2077             //
2078             // "new_actor" is cleared to keep from re-adding points from the previous actor!
2079             //
2080             if (new_actor && (new_actor->obclass >= rentacopobj) && (new_actor->obclass < crate1obj)) {
2081                 classtype obclass = new_actor->obclass;
2082 
2083                 switch (obclass) {
2084                 case lcan_wait_alienobj:
2085                 case scan_wait_alienobj:
2086                 case gurney_waitobj:
2087                     obclass++;
2088                     break;
2089 
2090                 default:
2091                     break;
2092                 }
2093 
2094                 AddTotalPoints(actor_points[obclass - rentacopobj]);
2095                 AddTotalEnemy(1);
2096                 new_actor = nullptr;
2097             }
2098 
2099             // Skip past FA code...
2100             //
2101             if (scan_value != 0xffff) {
2102                 x++;
2103                 start++;
2104             }
2105         }
2106     }
2107 
2108     if (!loadedgame) {
2109         gamestuff.level[gamestate.mapon].stats.accum_inf = gamestuff.level[gamestate.mapon].stats.total_inf;
2110     }
2111 }
2112 
AddTotalPoints(uint16_t points)2113 void AddTotalPoints(
2114     uint16_t points)
2115 {
2116     if (loadedgame) {
2117         return;
2118     }
2119 
2120     gamestuff.level[gamestate.mapon].stats.total_points += points;
2121 }
2122 
AddTotalInformants(int8_t informants)2123 void AddTotalInformants(
2124     int8_t informants)
2125 {
2126     if (loadedgame) {
2127         return;
2128     }
2129 
2130     gamestuff.level[gamestate.mapon].stats.total_inf += informants;
2131 }
2132 
AddTotalEnemy(uint16_t enemies)2133 void AddTotalEnemy(
2134     uint16_t enemies)
2135 {
2136     if (loadedgame) {
2137         return;
2138     }
2139 
2140     gamestuff.level[gamestate.mapon].stats.total_enemy +=
2141         static_cast<uint8_t>(enemies);
2142 }
2143 
SetupGameLevel()2144 void SetupGameLevel()
2145 {
2146     bool switchon = false;
2147     sci_mCacheInfo* ci = InfHintList.smInfo;
2148     int16_t x, y;
2149     uint16_t* map, tile, icon;
2150     keytype lock;
2151     uint16_t* map1, * map2;
2152     int16_t count;
2153 
2154     if (!loadedgame) {
2155         gamestate.flags |= GS_CLIP_WALLS;
2156         InitGoldsternInfo();
2157     }
2158 
2159     US_InitRndT(true);
2160 
2161 //
2162 // load the level
2163 //
2164     CA_CacheMap(static_cast<int16_t>(
2165         gamestate.mapon + MAPS_PER_EPISODE * gamestate.episode));
2166     mapon = static_cast<int16_t>(mapon - (gamestate.episode * MAPS_PER_EPISODE));
2167 
2168     mapwidth = mapheaderseg[mapon]->width;
2169     mapheight = mapheaderseg[mapon]->height;
2170 
2171     if (mapwidth != 64 || mapheight != 64) {
2172         ::Quit("Map not 64 x 64.");
2173     }
2174 
2175     // BBi
2176     fix_level_inplace();
2177 
2178     LoadLocationText(static_cast<int16_t>(
2179         gamestate.mapon + MAPS_PER_EPISODE * gamestate.episode));
2180 
2181 //
2182 // copy the wall data to a data segment array
2183 //
2184     memset(TravelTable, 0, sizeof(TravelTable));
2185     ::gamestate.initialize_local_barriers();
2186     memset(tilemap, 0, sizeof(tilemap));
2187     memset(actorat, 0, sizeof(actorat));
2188 
2189     std::uninitialized_fill(
2190         ::wallheight.begin(),
2191         ::wallheight.end(),
2192         0);
2193 
2194     map = mapsegs[0];
2195     map2 = mapsegs[1];
2196     for (y = 0; y < mapheight; y++) {
2197         for (x = 0; x < mapwidth; x++) {
2198             icon = *map2++;
2199             tile = *map++;
2200 
2201             if (tile < AREATILE) {
2202                 // solid wall
2203                 tilemap[x][y] = static_cast<uint8_t>(tile);
2204 
2205                 switch (tile) {
2206                 case RKEY_TILE:
2207                 case YKEY_TILE:
2208                 case BKEY_TILE:
2209                 case BFG_TILE:
2210                 case ION_TILE:
2211                 case DETONATOR_TILE:
2212                 case CLOAK_TILE:
2213                 case LINC_TILE:
2214                 case CLOAK_AMBUSH_TILE:
2215                     if (!::is_ps()) {
2216                         INVALID_ACTOR_ERR;
2217                     }
2218                 case AMBUSHTILE:
2219                     break;
2220 
2221                 default:
2222                     actorat[x][y] = reinterpret_cast<objtype*>(tile);
2223                     break;
2224                 }
2225             }
2226 
2227             if ((::is_ps() && tile < 64) || icon == PUSHABLETILE) {
2228                 TravelTable[x][y] |= TT_TRAVELED;
2229             }
2230         }
2231     }
2232 
2233 //
2234 // spawn doors
2235 //
2236     InitActorList(); // start spawning things with a clean slate
2237     InitDoorList();
2238 
2239     InitMsgCache((mCacheList*)&ConHintList, sizeof(ConHintList), sizeof(ConHintList.cmInfo[0]));
2240     InitMsgCache((mCacheList*)&InfHintList, sizeof(InfHintList), sizeof(InfHintList.smInfo[0]));
2241     InitMsgCache((mCacheList*)&NiceSciList, sizeof(NiceSciList), sizeof(InfHintList.smInfo[0]));
2242     InitMsgCache((mCacheList*)&MeanSciList, sizeof(MeanSciList), sizeof(InfHintList.smInfo[0]));
2243 
2244     InitStaticList();
2245 
2246     map = mapsegs[0];
2247     map1 = mapsegs[1];
2248 
2249     NumEAWalls = 0;
2250     alerted = 0;
2251     LastInfoAttacker = nothing;
2252 
2253     // BBi
2254     bool is_red_key_present = false;
2255     bool is_projection_generator_present = false;
2256     // BBi
2257 
2258     for (y = 0; y < mapheight; y++) {
2259         for (x = 0; x < mapwidth; x++) {
2260             tile = *map++;
2261             lock = static_cast<keytype>(*map1);
2262 
2263             if (y < 63 && x < 63 && *map == 30) {
2264                 gamestate.wintilex = x + 1;
2265                 gamestate.wintiley = y;
2266             }
2267 
2268             if (tile >= 88 && tile <= 105) {
2269                 //
2270                 // KEYS
2271                 //
2272 
2273                 switch (static_cast<int>(lock)) {
2274                 case 55:
2275                 case 56:
2276                     lock = static_cast<keytype>(kt_red + lock - 55);
2277                     *map1 = 0;
2278                     break;
2279 
2280                 case 58:
2281                     lock = kt_blue;
2282                     *map1 = 0;
2283                     break;
2284 
2285                 case 57:
2286                     if (::is_ps()) {
2287                         lock = kt_none;
2288                         break;
2289                     }
2290 
2291                     lock = kt_green;
2292                     *map1 = 0;
2293                     break;
2294 
2295                 case 59:
2296                     if (::is_ps()) {
2297                         lock = kt_none;
2298                         break;
2299                     }
2300 
2301                     lock = kt_gold;
2302                     *map1 = 0;
2303                     break;
2304 
2305                 default:
2306                     lock = kt_none;
2307                 }
2308 
2309                 //
2310                 // DOOR
2311                 //
2312 
2313                 switch (tile) {
2314 
2315                 case 88:
2316                 case 89:
2317                     SpawnDoor(x, y, !(tile % 2), lock, dr_bio);
2318                     break;
2319 
2320                 case 90:
2321                 case 91:
2322                     SpawnDoor(x, y, !(tile % 2), lock, dr_normal);
2323                     break;
2324 
2325                 case 92:
2326                 case 93:
2327                     SpawnDoor(x, y, !(tile % 2), lock, dr_prison);
2328                     break;
2329 
2330                 case 94:
2331                 case 95:
2332                     SpawnDoor(x, y, !(tile % 2), lock, dr_elevator);
2333                     break;
2334 
2335                 case 96:
2336                 case 97:
2337                     SpawnDoor(x, y, !(tile % 2), lock, dr_high_security);
2338                     break;
2339 
2340                 case 98: // oneway left  - Vert
2341                 case 99: // oneway up    - Horz
2342                 case 100: // oneway right - Vert
2343                 case 101: // oneway down  - Horz
2344                     SpawnDoor(x, y, !(tile % 2), lock, static_cast<door_t>(dr_oneway_left + (tile - 98)));
2345                     break;
2346 
2347                 case 102:
2348                 case 103:
2349                     SpawnDoor(x, y, !(tile % 2), lock, dr_office);
2350                     break;
2351 
2352                 case 104:
2353                 case 105:
2354                     SpawnDoor(x, y, !(tile % 2), lock, dr_space);
2355                     break;
2356 
2357 
2358                 }
2359             } else {
2360                 switch (tile) {
2361                 case SODATILE:
2362                     if (!loadedgame) {
2363                         SpawnConcession(x, y, static_cast<uint16_t>(lock), CT_BEVS);
2364                         *map1 = 0;
2365                     }
2366                     break;
2367 
2368 
2369 
2370                 case FOODTILE:
2371                     if (!loadedgame) {
2372                         SpawnConcession(x, y, static_cast<uint16_t>(lock), CT_FOOD);
2373                         *map1 = 0;
2374                     }
2375                     break;
2376 
2377                 case EATILE:
2378                     eaList[static_cast<int>(NumEAWalls)].tilex = static_cast<int8_t>(x);
2379                     eaList[static_cast<int>(NumEAWalls)].tiley = static_cast<int8_t>(y);
2380                     eaList[static_cast<int>(NumEAWalls)].aliens_out = 0;
2381                     if ((lock & 0xff00) == 0xfa00) {
2382                         eaList[static_cast<int>(NumEAWalls)].delay = 60 * (lock & 0xff);
2383                     } else {
2384                         eaList[static_cast<int>(NumEAWalls)].delay = 60 * 8 + Random(60 * 22);
2385                     }
2386                     if (NumEAWalls++ == MAXEAWALLS) {
2387                         ::Quit("Too many Electro-Alien walls in level.");
2388                     }
2389                     break;
2390 
2391                 case ON_SWITCH:
2392                     switchon = true;
2393                 case OFF_SWITCH: {
2394                     if (::is_aog()) {
2395                         if (map1[1] != 0) {
2396                             uint8_t level = 0xFF;
2397 
2398                             if (map1[0] != 0xF8FF) {
2399                                 level = static_cast<uint8_t>(map1[0] & 0xFF);
2400                             }
2401 
2402                             if (level == ::gamestate.mapon) {
2403                                 level = 0xFF;
2404                             }
2405 
2406                             auto switch_x = static_cast<uint8_t>((map1[1] / 256) & 0xFF);
2407                             auto switch_y = static_cast<uint8_t>(map1[1] & 0xFF);
2408 
2409                             map1[1] = 0;
2410                             map1[0] = 0xF800 | UpdateBarrierTable(level, switch_x, switch_y, switchon);
2411 
2412                             if (level != 0xFF) {
2413                                 ::store_cross_barrier(level, switch_x, switch_y, switchon);
2414                             }
2415                         }
2416                     } else {
2417                         auto switch_x = static_cast<uint8_t>((map1[0] / 256) & 0xFF);
2418                         auto switch_y = static_cast<uint8_t>(map1[0] & 0xFF);
2419 
2420                         map1[0] = 0xF800 | UpdateBarrierTable(0xFF, switch_x, switch_y, switchon);
2421                     }
2422 
2423                     // Init for next time.
2424 
2425                     switchon = false;
2426                     break;
2427                 }
2428                 }
2429             }
2430 
2431             // BBi
2432             if (map1[0] == 55) {
2433                 is_red_key_present = true;
2434             }
2435 
2436             if (map1[0] == 177) {
2437                 is_projection_generator_present = true;
2438             }
2439 
2440             switch (map1[0]) {
2441             case 445:
2442             case 463:
2443             case 481:
2444                 is_red_key_present = true;
2445                 break;
2446 
2447             default:
2448                 break;
2449             }
2450             // BBi
2451 
2452             map1++;
2453         }
2454     }
2455 
2456 
2457     // BBi
2458     ::apply_cross_barriers();
2459     // BBi
2460 
2461 //
2462 // spawn actors
2463 //
2464 
2465     ScanInfoPlane();
2466     ConnectBarriers();
2467 
2468 // Init informant stuff
2469 //
2470     count = InfHintList.NumMsgs;
2471     LastInfArea = 0xff;
2472     FirstGenInfMsg = 0;
2473     for (; (ci->areanumber != 0xff) && (count--); ci++) {
2474         FirstGenInfMsg++;
2475     }
2476     TotalGenInfMsgs = InfHintList.NumMsgs - FirstGenInfMsg;
2477 
2478 
2479     //
2480     // Take out the special tiles that were not used...
2481     //
2482 
2483     map = mapsegs[0];
2484     for (y = 0; y < mapheight; y++) {
2485         for (x = 0; x < mapwidth; x++) {
2486             tile = *map++;
2487             switch (tile) {
2488             case RKEY_TILE:
2489             case YKEY_TILE:
2490             case BKEY_TILE:
2491             case BFG_TILE:
2492             case ION_TILE:
2493             case DETONATOR_TILE:
2494             case CLOAK_TILE:
2495             case LINC_TILE:
2496             case CLOAK_AMBUSH_TILE:
2497                 if (!::is_ps()) {
2498                     break;
2499                 }
2500             case AMBUSHTILE:
2501                 tilemap[x][y] = 0;
2502                 if (actorat[x][y] == (objtype*)AMBUSHTILE) {
2503                     actorat[x][y] = nullptr;
2504                 }
2505                 *(map - 1) = GetAreaNumber(static_cast<int8_t>(x), static_cast<int8_t>(y));
2506                 break;
2507             }
2508         }
2509     }
2510 
2511 //
2512 // have the caching manager load and purge stuff to make sure all marks
2513 // are in memory
2514 //
2515     CA_LoadAllSounds();
2516 
2517     if (::is_aog()) {
2518         if (!is_red_key_present &&
2519             gamestate.mapon > 0 &&
2520             gamestate.mapon < 10 &&
2521             gamestuff.level[gamestate.mapon + 1].locked)
2522         {
2523             ::Quit("No red key on floor {}.", gamestate.mapon);
2524         }
2525 
2526         if (::is_aog_full() &&
2527             gamestate.episode == 5 &&
2528             gamestate.mapon == 9 &&
2529             !is_projection_generator_present)
2530         {
2531             ::Quit("No projection generator(s) on floor 10 episode 6.");
2532         }
2533     } else {
2534         //
2535         // Check and make sure a detonator is in a 'locked' level.
2536         //
2537 
2538         if (gamestate.mapon < 20 &&
2539             !detonators_spawned &&
2540             gamestuff.level[gamestate.mapon + 1].locked)
2541         {
2542             ::Quit("No Fision/Plasma Detonator in level!");
2543         }
2544     }
2545 }
2546 
2547 
2548 // ------------------------------------------------------------------------
2549 // LoadLocationText()
2550 // ------------------------------------------------------------------------
LoadLocationText(int16_t textNum)2551 void LoadLocationText(
2552     int16_t textNum)
2553 {
2554     char* temp;
2555 
2556     LoadMsg(LocationText, LEVEL_DESCS, textNum + 1, MAX_LOCATION_DESC_LEN);
2557     temp = strstr(LocationText, "^XX");
2558     if (temp) {
2559         *temp = 0;
2560     }
2561 }
2562 
DrawPlayBorder()2563 void DrawPlayBorder()
2564 {
2565     ::vid_set_ui_mask_3d(
2566         ::playstate == ex_transported);
2567 
2568     ::VL_Bar(
2569         0,
2570         ::ref_view_top,
2571         ::vga_ref_width,
2572         ::ref_3d_margin,
2573         BLACK);
2574 
2575     ::VL_Bar(
2576         0,
2577         ::ref_3d_view_bottom,
2578         ::vga_ref_width,
2579         ::ref_3d_margin,
2580         BLACK);
2581 }
2582 
2583 // --------------------------------------------------------------------------
2584 // BMAmsg() - These messages are displayed by the Text Presenter!
2585 // --------------------------------------------------------------------------
BMAmsg(const char * msg)2586 void BMAmsg(
2587     const char* msg)
2588 {
2589     const int16_t BMAx1 = 0; // outer bevel
2590     const int16_t BMAy1 = 152;
2591     const int16_t BMAw1 = 320;
2592     const int16_t BMAh1 = 48;
2593 
2594     const int16_t BMAx2 = BMAx1 + 7; // inner bevel
2595     const int16_t BMAy2 = BMAy1 + 4;
2596     const int16_t BMAw2 = BMAw1 - 14;
2597     const int16_t BMAh2 = BMAh1 - 8;
2598 
2599     BevelBox(BMAx1, BMAy1, BMAw1, BMAh1, BORDER_HI_COLOR, BORDER_MED_COLOR, BORDER_LO_COLOR);
2600     BevelBox(BMAx2, BMAy2, BMAw2, BMAh2, BORDER_LO_COLOR, BORDER_MED_COLOR, BORDER_HI_COLOR);
2601 
2602     if (msg) {
2603         PresenterInfo pi;
2604         fontstruct* font = (fontstruct*)grsegs[STARTFONT + fontnumber];
2605         int8_t numlines = 1;
2606         const char* p = msg;
2607         int16_t cheight;
2608 
2609         memset(&pi, 0, sizeof(pi));
2610         pi.flags = TPF_CACHE_NO_GFX;
2611         pi.script[0] = p;
2612         while (*p) {
2613             if (*p++ == TP_RETURN_CHAR) {
2614                 numlines++;
2615             }
2616         }
2617         cheight = font->height * numlines + 1 + (TP_MARGIN * 2);
2618 
2619         pi.xl = BMAx2 + 1;
2620         pi.yl = BMAy2 + (BMAh2 - cheight) / 2;
2621         pi.xh = pi.xl + BMAw2 - 3;
2622         pi.yh = pi.yl + cheight - 1;
2623         pi.bgcolor = BORDER_MED_COLOR;
2624         pi.ltcolor = BORDER_HI_COLOR;
2625         fontcolor = BORDER_TEXT_COLOR;
2626         pi.shcolor = pi.dkcolor = BORDER_LO_COLOR;
2627         pi.fontnumber = static_cast<int8_t>(fontnumber);
2628         TP_InitScript(&pi);
2629         TP_Presenter(&pi);
2630     }
2631 }
2632 
2633 // ----------------------------------------------------------------------
2634 // CacheBMAmsg() - Caches in a Message Number and displays it using
2635 //      BMAmsg()
2636 // ----------------------------------------------------------------------
CacheBMAmsg(uint16_t MsgNum)2637 void CacheBMAmsg(
2638     uint16_t MsgNum)
2639 {
2640     char* string, * pos;
2641 
2642     CA_CacheGrChunk(MsgNum);
2643     string = (char*)grsegs[MsgNum];
2644 
2645     pos = strstr(string, "^XX");
2646     *(pos + 3) = 0;
2647 
2648     BMAmsg(string);
2649 
2650     UNCACHEGRCHUNK(MsgNum);
2651 }
2652 
BevelBox(int16_t xl,int16_t yl,int16_t w,int16_t h,uint8_t hi,uint8_t med,uint8_t lo)2653 void BevelBox(
2654     int16_t xl,
2655     int16_t yl,
2656     int16_t w,
2657     int16_t h,
2658     uint8_t hi,
2659     uint8_t med,
2660     uint8_t lo)
2661 {
2662     int16_t xh = xl + w - 1, yh = yl + h - 1;
2663     uint8_t hc;
2664 
2665     VWB_Bar(xl, yl, w, h, med); // inside
2666 
2667     hc = med + 1;
2668 
2669     VWB_Hlin(xl, xh, yl, hi); // top
2670     VWB_Hlin(xl, xh, yh, lo); // bottom
2671     VWB_Vlin(yl, yh, xl, hi); // left
2672     VWB_Vlin(yl, yh, xh, lo); // right
2673     VWB_Plot(xl, yh, hc); // lower-left
2674     VWB_Plot(xh, yl, hc); // upper-right
2675 }
2676 
ShadowPrintLocationText(sp_type type)2677 void ShadowPrintLocationText(
2678     sp_type type)
2679 {
2680     const char* DebugText = "-- DEBUG MODE ENABLED --";
2681     const char* s = nullptr, * ls_text[3] = { "-- LOADING --", "-- SAVING --", "-- CHANGE VIEW SIZE --" };
2682     int w, h;
2683 
2684 // Used for all fields...
2685 //
2686     py = 5;
2687     fontcolor = 0xaf;
2688 
2689 // Print LOCATION info...
2690 //
2691     switch (type) {
2692     case sp_normal:
2693         // Print LEVEL info...
2694         //
2695 
2696         if (::is_aog()) {
2697             ::px = 17;
2698         } else {
2699             ::px = 13;
2700         }
2701 
2702         if ((!::is_ps() && (gamestate.mapon % 10) == 0) ||
2703             (::is_ps() && gamestate.mapon > 19))
2704         {
2705             ::ShPrint(" SECRET ", 0, false);
2706         } else {
2707             if (!::is_ps()) {
2708                 ShPrint("FLOOR: ", 0, false);
2709             } else {
2710                 ShPrint(" AREA: ", 0, false);
2711             }
2712             if (!type) {
2713                 auto map_string = std::to_string(
2714                     ::is_aog() ? gamestate.mapon : gamestate.mapon + 1);
2715 
2716                 ::ShPrint(map_string.c_str(), 0, false);
2717             }
2718         }
2719 
2720         // Print LIVES info...
2721         //
2722         px = 267;
2723         ShPrint("LIVES: ", 0, false);
2724         if (!type) {
2725             auto lives_string = std::to_string(gamestate.lives);
2726             ::ShPrint(lives_string.c_str(), 0, false);
2727         }
2728 
2729         // Print location text
2730         //
2731 
2732         if (DebugOk || (gamestate.flags & (GS_QUICKRUN | GS_STARTLEVEL | GS_TICS_FOR_SCORE | GS_MUSIC_TEST | GS_SHOW_OVERHEAD)))
2733         {
2734             s = DebugText;
2735         } else {
2736             s = LocationText;
2737         }
2738         break;
2739 
2740     case sp_changeview:
2741     case sp_loading:
2742     case sp_saving:
2743         s = ls_text[type - sp_loading];
2744         break;
2745 
2746     default:
2747         break;
2748     }
2749 
2750     VW_MeasurePropString(s, &w, &h);
2751     px = static_cast<int16_t>(160 - w / 2);
2752     ShPrint(s, 0, false);
2753 }
2754 
DrawTopInfo(sp_type type)2755 void DrawTopInfo(
2756     sp_type type)
2757 {
2758     auto old = static_cast<int8_t>(fontnumber);
2759 
2760     LatchDrawPic(0, 0, TOP_STATUSBARPIC);
2761     fontnumber = 2;
2762     ShadowPrintLocationText(type);
2763     fontnumber = old;
2764 }
2765 
DrawPlayScreen(bool InitInfoMsg)2766 void DrawPlayScreen(
2767     bool InitInfoMsg)
2768 {
2769     if (::loadedgame) {
2770         return;
2771     }
2772 
2773     if (::playstate != ex_transported) {
2774         VW_FadeOut();
2775     }
2776 
2777     ::WindowW = 253;
2778     ::WindowH = 8;
2779     ::fontnumber = 2;
2780 
2781     ::DrawPlayBorder();
2782 
2783     ::LatchDrawPic(0, 200 - STATUSLINES, STATUSBARPIC);
2784     ::LatchDrawPic(0, 0, TOP_STATUSBARPIC);
2785 
2786     ::ShadowPrintLocationText(sp_normal);
2787 
2788     ::DrawHealth();
2789     ::DrawKeys();
2790     ::DrawWeapon();
2791     ::DrawScore();
2792 
2793     ::InitInfoArea();
2794 
2795     if (InitInfoMsg) {
2796         DISPLAY_MSG("R.E.B.A.\rAGENT: BLAKE STONE\rALL SYSTEMS READY.", MP_max_val, MT_NOTHING);
2797     } else {
2798         ::DisplayNoMoMsgs();
2799     }
2800 
2801     ::ForceUpdateStatusBar();
2802 }
2803 
DrawWarpIn()2804 void DrawWarpIn()
2805 {
2806     ::InitInfoArea();
2807 
2808     ::DisplayInfoMsg(
2809         "\r\r    TRANSPORTING...",
2810         MP_POWERUP,
2811         2 * 60,
2812         MT_GENERAL);
2813 
2814     ::DrawHealth();
2815     ::DrawKeys();
2816     ::DrawWeapon();
2817     ::DrawScore();
2818     ::WindowW = 253;
2819     ::WindowH = 8;
2820     ::fontnumber = 2;
2821 
2822     VW_Bar(
2823         0,
2824         ::ref_view_top,
2825         ::vga_ref_width,
2826         ::ref_view_height,
2827         BLACK);
2828 
2829     ::LatchDrawPic(0, ::ref_view_bottom, ::STATUSBARPIC);
2830     ::LatchDrawPic(0, 0, ::TOP_STATUSBARPIC);
2831 
2832     ::ShadowPrintLocationText(sp_normal);
2833     ::UpdateStatusBar();
2834 
2835     ::sd_play_player_sound(::WARPINSND, bstone::AC_ITEM);
2836 
2837     ::fizzlein = true;
2838 
2839     ::ThreeDRefresh();
2840 }
2841 
Warped()2842 void Warped()
2843 {
2844     int16_t iangle;
2845 
2846     DisplayInfoMsg("\r\r\r   TRANSPORTING OUT", MP_POWERUP, 7 * 60, MT_GENERAL);
2847     gamestate.old_weapons[3] = gamestate.weapon;
2848     gamestate.weapon = -1; // take away weapon
2849 
2850     ThreeDRefresh();
2851 
2852     if (screenfaded) {
2853         VW_FadeIn();
2854     }
2855 
2856     iangle = (((player->dir + 4) % 8) >> 1) * 90;
2857 
2858     RotateView(iangle, 2);
2859 
2860     gamestate.weapon = gamestate.old_weapons[3];
2861     gamestate.attackframe = gamestate.attackcount = gamestate.weaponframe = 0;
2862 
2863     IN_ClearKeysDown();
2864 
2865     ::sd_play_player_sound(WARPINSND, bstone::AC_ITEM);
2866 
2867     bstone::GenericFizzleFX fizzle(
2868             BLACK,
2869             true);
2870 
2871     fizzle.initialize();
2872 
2873     static_cast<void>(fizzle.present());
2874 
2875     IN_UserInput(100);
2876     SD_WaitSoundDone();
2877 }
2878 
2879 
Died()2880 void Died()
2881 {
2882     const uint8_t DEATHROTATE = 2;
2883 
2884     int16_t iangle;
2885 
2886     gamestate.weapon = -1; // take away weapon
2887 
2888     ::sd_play_player_sound(PLAYERDEATHSND, bstone::AC_VOICE);
2889 
2890     iangle = CalcAngle(player, killerobj);
2891 
2892     RotateView(iangle, DEATHROTATE);
2893 
2894 //
2895 // fade to red
2896 //
2897     FinishPaletteShifts();
2898 
2899     bstone::GenericFizzleFX fizzle_fx(
2900         0x17,
2901         true);
2902 
2903     fizzle_fx.initialize();
2904 
2905     static_cast<void>(fizzle_fx.present());
2906 
2907     IN_UserInput(100);
2908 
2909     SD_WaitSoundDone();
2910     StopMusic();
2911 
2912     gamestate.lives--;
2913 
2914     if (gamestate.lives > -1) {
2915         gamestate.health = 100;
2916         gamestate.weapons = 1 << wp_autocharge; // |1<<wp_plasma_detonators;
2917         gamestate.weapon = gamestate.chosenweapon = wp_autocharge;
2918 
2919         gamestate.ammo = STARTAMMO;
2920         gamestate.attackframe = gamestate.attackcount =
2921                                     gamestate.weaponframe = 0;
2922 
2923         gamestate.flags |= (GS_CLIP_WALLS | GS_ATTACK_INFOAREA);
2924 
2925         DrawHealth();
2926         DrawKeys();
2927         DrawWeapon();
2928         DrawScore();
2929         DrawKeys();
2930         ForceUpdateStatusBar();
2931     }
2932 }
2933 
2934 // --------------------------------------------------------------------------
2935 // LoseScreen()   - Displays the Goldstern/DamagedReba message...
2936 // --------------------------------------------------------------------------
LoseScreen()2937 void LoseScreen()
2938 {
2939     PresenterInfo pi;
2940 
2941     VW_FadeOut();
2942 
2943     memset(&pi, 0, sizeof(pi));
2944     pi.flags = TPF_USE_CURRENT | TPF_SHOW_CURSOR | TPF_SCROLL_REGION | TPF_CONTINUE | TPF_TERM_SOUND | TPF_ABORTABLE;
2945     pi.xl = 14;
2946     pi.yl = 141;
2947     pi.xh = 14 + 293;
2948     pi.yh = 141 + 32;
2949     pi.ltcolor = 15;
2950     pi.bgcolor = 0;
2951     pi.dkcolor = 1;
2952     pi.shcolor = 1;
2953     pi.fontnumber = 2;
2954     pi.cur_x = static_cast<uint16_t>(-1);
2955     pi.print_delay = 2;
2956 
2957     ClearMemory();
2958     StopMusic();
2959 
2960     CA_CacheScreen(LOSEPIC);
2961     VW_UpdateScreen();
2962 
2963     TP_LoadScript(nullptr, &pi, LOSETEXT);
2964 
2965     // Now Presenting... The Loser Prize.. I nice message directly from Dr.
2966     // ==============    Goldstern himself!  Oooo Ohhhhh <clap> <clap> ...
2967     //
2968 
2969     VW_FadeIn();
2970     TP_Presenter(&pi);
2971     VW_FadeOut();
2972 
2973     TP_FreeScript(&pi, LOSETEXT);
2974 
2975     screenfaded = true;
2976 
2977     IN_ClearKeysDown();
2978 }
2979 
2980 // --------------------------------------------------------------------------
2981 // RotateView()
2982 //
2983 // PARAMETERS:
2984 //      DestAngle - Destination angle to rotate player->angle to.
2985 //      RotSpeed  - Rotation Speed
2986 // --------------------------------------------------------------------------
RotateView(int16_t DestAngle,uint8_t RotSpeed)2987 void RotateView(
2988     int16_t DestAngle,
2989     uint8_t RotSpeed)
2990 {
2991     int16_t curangle, clockwise, counter, change;
2992     objtype* obj;
2993     bool old_godmode = godmode;
2994 
2995     if (player->angle > DestAngle) {
2996         counter = player->angle - DestAngle;
2997         clockwise = ANGLES - player->angle + DestAngle;
2998     } else {
2999         clockwise = DestAngle - player->angle;
3000         counter = player->angle + ANGLES - DestAngle;
3001     }
3002 
3003     godmode = true;
3004     curangle = player->angle;
3005 
3006     controly = 0;
3007     if (clockwise < counter) {
3008         //
3009         // rotate clockwise
3010         //
3011         if (curangle > DestAngle) {
3012             curangle -= ANGLES;
3013         }
3014         controlx = -1;
3015         do {
3016             change = tics * RotSpeed;
3017             if (curangle + change > DestAngle) {
3018                 change = DestAngle - curangle;
3019             }
3020 
3021             curangle += change;
3022             player->angle += change;
3023             if (player->angle >= ANGLES) {
3024                 player->angle -= ANGLES;
3025             }
3026 
3027             for (obj = player->next; obj; obj = obj->next) {
3028                 DoActor(obj);
3029             }
3030             ThreeDRefresh();
3031             CalcTics();
3032         } while (curangle != DestAngle);
3033     } else {
3034         //
3035         // rotate counterclockwise
3036         //
3037         if (curangle < DestAngle) {
3038             curangle += ANGLES;
3039         }
3040         controlx = 1;
3041         do {
3042             change = -tics * RotSpeed;
3043             if (curangle + change < DestAngle) {
3044                 change = DestAngle - curangle;
3045             }
3046 
3047             curangle += change;
3048             player->angle += change;
3049             if (player->angle < 0) {
3050                 player->angle += ANGLES;
3051             }
3052 
3053             for (obj = player->next; obj; obj = obj->next) {
3054                 DoActor(obj);
3055             }
3056             ThreeDRefresh();
3057             CalcTics();
3058         } while (curangle != DestAngle);
3059     }
3060 
3061     controlx = 0;
3062     player->dir = static_cast<dirtype>(((player->angle + 22) % 360) / 45);
3063     godmode = old_godmode;
3064 
3065 }
3066 
GameLoop()3067 void GameLoop()
3068 {
3069     // BBi
3070     ::vid_is_hud = true;
3071     ::vid_set_ui_mask_3d(false);
3072     // BBi
3073 
3074     bool quit = false;
3075 
3076     extern bool sqActive;
3077 
3078     char Score[13];
3079     bool died;
3080 
3081 restartgame:
3082 
3083     ClearMemory();
3084     SETFONTCOLOR(0, 15);
3085     DrawPlayScreen(true);
3086 
3087     died = false;
3088     do {
3089         extern int16_t pickquick;
3090 
3091         ingame = true;
3092 
3093         if (died && pickquick) {
3094             char string[] = "  Auto Quick Load?  ";
3095 
3096             WindowX = WindowY = 0;
3097             WindowW = 320;
3098             WindowH = 152;
3099 
3100             if (Confirm(string)) {
3101                 playstate = ex_stillplaying;
3102                 DrawPlayBorder();
3103                 VW_UpdateScreen();
3104                 US_ControlPanel(ScanCode::sc_f9);
3105             }
3106 
3107             DrawPlayBorder();
3108             VW_UpdateScreen();
3109         }
3110 
3111         if (!sqActive) {
3112             StartMusic(false);
3113         }
3114 
3115         if (!(loadedgame || LevelInPlaytemp(gamestate.mapon))) {
3116             ::gamestate.score = ::gamestate.oldscore;
3117             ::gamestate.tic_score = ::gamestate.oldscore;
3118             ::memcpy(::gamestate.numkeys, ::gamestate.old_numkeys, sizeof(::gamestate.numkeys));
3119             ::gamestate.restore_local_barriers();
3120             ::gamestate.rpower = ::gamestate.old_rpower;
3121             ::gamestate.tokens = ::gamestate.old_tokens;
3122             ::gamestate.weapons = ::gamestate.old_weapons[0];
3123             ::gamestate.weapon = ::gamestate.old_weapons[1];
3124             ::gamestate.chosenweapon = ::gamestate.old_weapons[2];
3125             ::gamestate.ammo = ::gamestate.old_ammo;
3126             ::gamestate.plasma_detonators = ::gamestate.old_plasma_detonators;
3127             ::gamestate.boss_key_dropped = ::gamestate.old_boss_key_dropped;
3128             ::gamestuff.level = ::gamestuff.old_levelinfo;
3129             ::DrawKeys();
3130             ::DrawScore();
3131         }
3132 
3133         startgame = false;
3134         if (!loadedgame) {
3135             if (LS_current == -1) {
3136                 // BBi
3137                 ::vid_clear_3d();
3138                 // BBi
3139 
3140                 DrawTopInfo(sp_loading);
3141                 DisplayPrepingMsg(prep_msg);
3142                 LS_current = 1;
3143                 LS_total = 20;
3144             }
3145             LoadLevel(gamestate.mapon);
3146         }
3147 
3148         LS_current = LS_total = -1;
3149 
3150         SetPlaneViewSize();
3151         if (loadedgame) {
3152             loadedgame = false;
3153         }
3154 
3155         if (died) {
3156             WindowY = 188;
3157             PreloadUpdate(1, 1);
3158             died = false;
3159             DrawPlayScreen(true);
3160         } else {
3161             PreloadGraphics();
3162             if (playstate == ex_transported) {
3163                 DrawWarpIn();
3164             } else {
3165                 DrawPlayScreen(false);
3166             }
3167         }
3168 
3169         if (!sqActive) {
3170             StartMusic(false);
3171         }
3172 
3173         PlayLoop();
3174         LS_current = LS_total = -1;
3175         died = false;
3176 
3177         StopMusic();
3178         ingame = false;
3179 
3180         if (startgame || loadedgame) {
3181             goto restartgame;
3182         }
3183 
3184         switch (playstate) {
3185 
3186         case ex_transported: // Same as ex_completed
3187             Warped();
3188 
3189         case ex_completed:
3190         case ex_secretlevel:
3191         case ex_warped:
3192             ClearMemory();
3193             gamestate.mapon++;
3194             ClearNClose();
3195             DrawTopInfo(sp_loading);
3196             DisplayPrepingMsg(prep_msg);
3197             WindowY = 181;
3198             LS_current = 1;
3199             LS_total = 38;
3200             StartMusic(false);
3201             SaveLevel(gamestate.lastmapon);
3202 
3203             gamestate.old_rpower = gamestate.rpower;
3204             gamestate.oldscore = gamestate.score;
3205             memcpy(gamestate.old_numkeys, gamestate.numkeys, sizeof(gamestate.old_numkeys));
3206             gamestate.old_tokens = gamestate.tokens;
3207             ::gamestate.store_local_barriers();
3208             gamestate.old_weapons[0] = gamestate.weapons;
3209             gamestate.old_weapons[1] = gamestate.weapon;
3210             gamestate.old_weapons[2] = gamestate.chosenweapon;
3211             gamestate.old_ammo = gamestate.ammo;
3212             gamestate.old_boss_key_dropped = gamestate.boss_key_dropped;
3213             gamestuff.old_levelinfo = gamestuff.level;
3214             break;
3215 
3216         case ex_died:
3217             if (InstantQuit) {
3218                 InstantQuit = false;
3219             } else {
3220                 Died();
3221 
3222                 died = true; // don't "get psyched!"
3223 
3224                 if (gamestate.lives > -1) {
3225                     ClearMemory();
3226                     break; // more lives left
3227                 }
3228 
3229                 LoseScreen();
3230             }
3231 
3232 
3233         case ex_victorious:
3234             MainMenu[MM_SAVE_MISSION].active = AT_DISABLED;
3235             MainMenu[MM_VIEW_SCORES].routine = &CP_ViewScores;
3236             strcpy(MainMenu[MM_VIEW_SCORES].string, "HIGH SCORES");
3237 
3238             if (playstate == ex_victorious) {
3239                 ThreeDRefresh();
3240                 ThreeDRefresh();
3241             }
3242 
3243             ClearMemory();
3244 
3245             if (playstate == ex_victorious) {
3246                 fontnumber = 1;
3247                 CA_CacheGrChunk(STARTFONT + 1);
3248                 memset(update, 0, sizeof(update));
3249                 CacheBMAmsg(YOUWIN_TEXT);
3250 
3251 // BBi
3252 #if 0
3253                 VW_ScreenToScreen(PAGE1START, ::bufferofs, 320, 200);
3254 #endif
3255 
3256                 UNCACHEGRCHUNK(STARTFONT + 1);
3257 
3258                 ::sd_play_player_sound(BONUS1SND, bstone::AC_ITEM);
3259 
3260                 SD_WaitSoundDone();
3261                 IN_UserInput(5 * 60);
3262                 ClearMemory();
3263             }
3264 
3265             VW_FadeOut();
3266 
3267             sprintf(Score, "%d", gamestate.score);
3268             piStringTable[0] = Score;
3269 
3270             if (playstate == ex_victorious) {
3271                 if (!::is_ps()) {
3272                     movie_t movie = mv_intro;
3273 
3274                     switch (gamestate.episode) {
3275                     case 0:
3276                     case 1:
3277                     case 3:
3278                         movie = mv_final2;
3279                         break;
3280 
3281                     case 2:
3282                     case 4:
3283                         movie = mv_final3;
3284                         break;
3285 
3286                     case 5:
3287                         movie = mv_final;
3288                         break;
3289                     }
3290 
3291                     ::DoMovie(movie, nullptr);
3292                 } else {
3293                     CA_CacheGrChunk(ENDINGPALETTE);
3294 
3295                     DoMovie(mv_final, grsegs[ENDINGPALETTE]);
3296 
3297                     UNCACHEGRCHUNK(ENDINGPALETTE);
3298                 }
3299 
3300                 NewViewSize(); // Recreates & Allocs the ScaleDirectory
3301                 Breifing(BT_WIN, gamestate.episode);
3302             }
3303 
3304             CheckHighScore(gamestate.score, gamestate.mapon + 1);
3305 
3306             return;
3307 
3308         case ex_abort:
3309             quit = true;
3310             break;
3311 
3312         default:
3313             ClearMemory();
3314             break;
3315         }
3316     } while (!quit);
3317 
3318 
3319     // BBi
3320     ::vid_is_hud = false;
3321     // BBi
3322 }
3323 
3324 // BBi
is_map_sha1_match(const std::vector<std::string> & sha1s)3325 static bool is_map_sha1_match(
3326     const std::vector<std::string>& sha1s)
3327 {
3328     return std::any_of(
3329         sha1s.cbegin(),
3330         sha1s.cend(),
3331         [] (const std::string& sha1_string)
3332         {
3333             return ::map_sha1_string == sha1_string;
3334         }
3335     );
3336 }
3337 
is_map_compressed_size_match(const std::vector<int> & sizes)3338 static bool is_map_compressed_size_match(
3339     const std::vector<int>& sizes)
3340 {
3341     return std::any_of(
3342         sizes.cbegin(),
3343         sizes.cend(),
3344         [] (int size)
3345         {
3346             return ::map_compressed_size == size;
3347         }
3348     );
3349 }
3350 
fix_level_inplace()3351 static void fix_level_inplace()
3352 {
3353     static const std::vector<int> e2m6_sizes = {
3354         // v1.0
3355         6412,
3356 
3357         // v2.0-v3.0
3358         6414,
3359     };
3360 
3361     static const std::vector<std::string> e2m6_sha1s = {
3362         // v1.0
3363         "b0444bf3de386e4cac7654b996bf5341090cc1b5",
3364 
3365         // v2.0-v3.0
3366         "e60d5674dcfb5f450e3a936885e361fe61cb7725",
3367     };
3368 
3369 
3370     // Fix standing bio-tech near volatile containers
3371     // (E2M6; x: 38; y: 26)
3372     // (E2M6; x: 55; y: 33)
3373     //
3374     if (::is_aog_full() &&
3375         !::loadedgame &&
3376         ::gamestate.episode == 1 &&
3377         ::gamestate.mapon == 6 &&
3378         ::is_map_compressed_size_match(e2m6_sizes) &&
3379         ::is_map_sha1_match(e2m6_sha1s))
3380     {
3381         // Replace standing bio-tech with a moving one.
3382         ::mapsegs[1][(26 * MAPSIZE) + 38] = 157;
3383         ::mapsegs[1][(33 * MAPSIZE) + 55] = 157;
3384     }
3385 }
3386