1 /*
2 BStone: A Source port of
3 Blake Stone: Aliens of Gold and Blake Stone: Planet Strike
4 
5 Copyright (c) 1992-2013 Apogee Entertainment, LLC
6 Copyright (c) 2013-2015 Boris I. Bendovsky (bibendovsky@hotmail.com)
7 
8 This program is free software; you can redistribute it and/or
9 modify it under the terms of the GNU General Public License
10 as published by the Free Software Foundation; either version 2
11 of the License, or (at your option) any later version.
12 
13 This program is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16 GNU General Public License for more details.
17 
18 You should have received a copy of the GNU General Public License
19 along with this program; if not, write to the
20 Free Software Foundation, Inc.,
21 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
22 */
23 
24 
25 #include "3d_def.h"
26 
27 
28 // ===========================================================================
29 //
30 // PROTOTYPES
31 //
32 // ===========================================================================
33 
34 void OpenDoor(
35     int16_t door);
36 
37 void CloseDoor(
38     int16_t door);
39 
40 void PlaceItemNearTile(
41     int16_t itemtype,
42     int16_t tilex,
43     int16_t tiley);
44 
45 void HealSelf(
46     int16_t points);
47 
48 
49 // ===========================================================================
50 //
51 // LOCALS
52 //
53 // ===========================================================================
54 
55 
56 concession_t ConHintList = {};
57 
58 
59 /*
60 =============================================================================
61 
62  STATICS
63 
64 =============================================================================
65 */
66 
67 statobj_t statobjlist[MAXSTATS];
68 statobj_t* laststatobj;
69 StatInfos statinfo;
70 
71 
initialize_static_info_constants()72 void initialize_static_info_constants()
73 {
74     statinfo = {
75         { SPR_STAT_0, bo_water_puddle, }, // Water Puddle SPR1V
76         { SPR_STAT_1, block, }, // Containment Canister
77         { SPR_STAT_2, block, }, // Lunch Table
78         { SPR_STAT_3, block, }, // Floor Lamp
79         { SPR_STAT_4, block, }, // Lab Table
80         { SPR_STAT_5, block, }, // Pillar
81         { SPR_STAT_6, dressing, }, // Blood Puddle
82         { SPR_STAT_7, dressing, }, // Piss Puddle
83 
84         { SPR_STAT_8, block, }, // Ficus Tree SPR2V
85         { SPR_STAT_9, dressing, }, // Half-Eaten Corpse
86         { SPR_STAT_10, block, }, // Water Fountain
87         { SPR_STAT_11, block, }, // Plant 1
88         { SPR_STAT_12, block, }, // Vase
89         { SPR_STAT_13, block, }, // General Table
90         { SPR_STAT_14, dressing, }, // Ceiling Light
91         { SPR_STAT_15, block, }, // General Chair
92 
93         { SPR_STAT_16, block, }, // Kitchen Trash SPR3V
94         { SPR_STAT_17, dressing, }, // Office Trash
95         { SPR_STAT_18, block, }, // Plant 2
96         { SPR_STAT_19, block, }, // Gurney No-Blood
97         { SPR_STAT_20, dressing, }, // Indirect Half-Sphere
98         { SPR_STAT_21, dressing, }, // Exit Sign
99         { SPR_STAT_22, dressing, }, // Transporter
100         { SPR_STAT_23, block, }, // Body Can
101 
102         { SPR_STAT_24, bo_pistol, }, // PISTOL SPR4V
103         { SPR_STAT_25, block, }, // Statue
104 
105         { SPR_STAT_31, bo_clip, }, // Charge Unit
106 
107         { SPR_STAT_27, bo_burst_rifle, }, // Auto-Burst Rifle
108         { SPR_STAT_28, bo_ion_cannon, }, // Particle Charged ION
109         { SPR_STAT_29, bo_firstaid, }, // First Aid
110         { SPR_VSPIKE8, block, }, // Static VSPIKE
111 
112         { SPR_STAT_26, bo_clip2, }, // Big Charge pack/clip
113 
114         { SPR_STAT_32, bo_red_key, }, // Red Key SPR5V
115         { SPR_STAT_33, bo_yellow_key, }, // Yellow Key
116         { SPR_STAT_34, ::is_ps() ? bo_bfg_cannon : bo_green_key, }, // BFG Cannon
117         { SPR_STAT_35, bo_blue_key, }, // Blue Key
118         { SPR_STAT_36, ::is_ps() ? dressing : bo_gold_key, }, // OPEN
119         { SPR_STAT_37, block, }, // Office Desk
120         { SPR_STAT_38, block, }, // Office Chair
121         { SPR_STAT_39, block, }, // Security Desk
122 
123         { SPR_STAT_40, bo_water, }, // Full Water Bowl SPR7V
124         { SPR_STAT_41, dressing, }, // Empty Water Bowl
125         { SPR_STAT_42, bo_chicken, }, // Chicken Leg
126         { SPR_STAT_43, dressing, }, // Chicken Bone
127         { SPR_STAT_44, bo_ham, }, // Ham
128         { SPR_STAT_45, dressing, }, // Ham Bone
129         { SPR_STAT_46, bo_grenade, }, // Grande Launcher
130         { SPR_STAT_47, dressing, }, // Video Game Machine
131 
132         { SPR_VPOST8, block, }, // Static VPOST
133 
134         // -- VARIOUS --
135 
136         { SPR_GURNEY_MUT_READY, block, }, // 49 Gurney Mutant
137         { SPR_LCAN_ALIEN_READY, block, }, // 50 Large Alien Canister
138         { SPR_SCAN_ALIEN_READY, block, }, // 51 Small Alien Canister
139 
140         { SPR_GURNEY_MUT_EMPTY, block, }, // 52 Gurney Mutant
141         { SPR_LCAN_ALIEN_EMPTY, block, }, // 53 Large Alien Canister
142         { SPR_SCAN_ALIEN_EMPTY, block, }, // 54 Small Alien Canister
143 
144         { SPR_OFC_DEAD, dressing, }, // 55 Dead Gen Sci.
145 
146         { SPR_DEMO, dressing, }, // 56 Spacer
147 
148         { SPR_AIR_VENT, bo_plainvent, }, // 57 Plain air vent
149         { SPR_AIR_VENT, bo_bloodvent, }, // 58 Blood air vent
150         { SPR_AIR_VENT, bo_watervent, }, // 59 Water air vent
151         { SPR_GRATE, dressing, }, // 60 Floor Grate
152         { SPR_STEAM_PIPE, dressing, }, // 61 Steam Pipe
153 
154         { SPR_STAT_48, bo_money_bag, }, // 62 money bag
155         { SPR_STAT_49, bo_loot, }, // 63 loot
156         { SPR_STAT_50, bo_gold, }, // 64 gold
157         { SPR_STAT_51, bo_bonus, }, // 65 bonus
158 
159         { SPR_STAT_52, block, }, // 66 Greek Post
160         { SPR_STAT_53, block, }, // 67 Red/Blue post
161         { SPR_STAT_54, block, }, // 68 Red HiTech Post
162         { SPR_STAT_55, dressing, }, // 69 Ceiling Lamp #2
163         { SPR_STAT_56, dressing, }, // 70 Ceiling Lamp #3
164         { SPR_STAT_57, dressing, }, // 71 Body Parts
165         { SPR_STAT_58, dressing, }, // 72 OR Lamp
166         { SPR_STAT_59, block, }, // 73 Office Sink
167         { SPR_STAT_57, dressing, }, // EMPTY - Copy of 71 - Body Parts...
168         { SPR_CANDY_BAR, bo_candybar, }, // 75 candy bar
169         { SPR_SANDWICH, bo_sandwich, }, // 76 sandwich
170         { SPR_CRATE_1, block, }, // 77 Crate #1
171         { SPR_CRATE_2, block, }, // 78 Crate #2
172         { SPR_CRATE_3, block, }, // 79 Crate #3
173         { SPR_STAT_61, block, }, // 80 Table
174         { SPR_STAT_62, block, }, // 81 Chair
175         { SPR_STAT_63, block, }, // 82 Stool
176         { SPR_STAT_64, dressing, }, // 83 Gore
177 
178         { SPR_STAT_65, bo_gold3, }, // Gold 3
179         { SPR_STAT_66, bo_gold2, }, // Gold 2
180         { SPR_STAT_67, bo_gold1, }, // Gold 1
181 
182         { SPR_STAT_68, block, }, //
183         { SPR_STAT_69, block, }, //
184         { SPR_STAT_70, block, }, //
185         { SPR_STAT_71, block, }, //
186         { SPR_STAT_72, block, }, //
187         { SPR_STAT_73, dressing, }, //
188         { SPR_STAT_74, dressing, }, //
189         { SPR_STAT_75, dressing, }, //
190         { SPR_STAT_76, dressing, }, //
191 
192         { SPR_RENT_DEAD, dressing, }, //
193         { SPR_PRO_DEAD, dressing, }, //
194         { SPR_SWAT_DEAD, dressing, }, //
195         { SPR_GSCOUT_DEAD, dressing, }, //
196         { SPR_FSCOUT_DEAD, dressing, }, //
197         { SPR_MUTHUM1_DEAD, dressing, },
198         { SPR_MUTHUM2_DEAD, dressing, },
199         { SPR_LCAN_ALIEN_DEAD, dressing, },
200         { SPR_SCAN_ALIEN_DEAD, dressing, },
201         { SPR_GURNEY_MUT_DEAD, dressing, },
202         { SPR_TERROT_DEAD, dressing, },
203         { SPR_POD_DIE3, dressing, },
204         { SPR_STAT_77, bo_coin, }, // Concession Machine Money
205         { SPR_STAT_78, bo_coin5, }, // Concession Machine Money
206         { SPR_STAT_79, dressing, }, // Auto-Charge Pistol
207 
208         { SPR_DOORBOMB, bo_plasma_detonator, }, // Plasma Detonator
209         { SPR_RUBBLE, dressing, }, // Door Rubble
210         { SPR_AUTOMAPPER, bo_automapper1, }, // Auto Mapper Bonus #1
211         { SPR_BONZI_TREE, block, }, // BonziTree
212         { SPR_POT_PLANT, block, }, // Yellow Potted Plant
213         { SPR_TUBE_PLANT, block, }, // Tube Plant
214         { SPR_HITECH_CHAIR, block, }, // Hi Tech table and chair
215         { SPR_DEAD_RENT, dressing, }, // Dead AOG: Rent A Cop
216         { SPR_DEAD_PRO, dressing, }, // Dead AOG: Pro Guard
217         { SPR_DEAD_SWAT, dressing, }, // Dead AOG: Swat Guad
218 
219         { -1, dressing, }, // terminator
220     };
221 }
222 
InitStaticList()223 void InitStaticList()
224 {
225     laststatobj = &statobjlist[0];
226 }
227 
228 // ---------------------------------------------------------------------------
229 // FindStatic()
230 //
231 // FUNCTION: Searches the stat obj list and returns ptr to a static obj
232 //           at a particular tile x & tile y coords.
233 //
234 // RETURNS: Ptr == Pointer to static obj.
235 //          NULL == No static found.
236 // ---------------------------------------------------------------------------
FindStatic(uint16_t tilex,uint16_t tiley)237 statobj_t* FindStatic(
238     uint16_t tilex,
239     uint16_t tiley)
240 {
241     statobj_t* spot;
242 
243     for (spot = statobjlist; spot != laststatobj; spot++) {
244         if (spot->shapenum != -1 && spot->tilex == tilex && spot->tiley == tiley) {
245             return spot;
246         }
247     }
248 
249     return nullptr;
250 }
251 
252 // ---------------------------------------------------------------------------
253 // FindEmptyStatic()
254 //
255 // FUNCTION: Searches the stat obj list and returns ptr to an empty
256 //          static object.
257 //
258 // RETURNS: Ptr == Pointer to empty static obj.
259 //          NULL == static objlist full.
260 // ---------------------------------------------------------------------------
FindEmptyStatic()261 statobj_t* FindEmptyStatic()
262 {
263     statobj_t* spot;
264 
265     for (spot = &statobjlist[0];; spot++) {
266         if (spot == laststatobj) {
267             if (spot == &statobjlist[MAXSTATS]) {
268                 return nullptr;
269             }
270             laststatobj++; // space at end
271             break;
272         }
273 
274         if (spot->shapenum == -1) { // -1 is a free spot
275             break;
276         }
277     }
278 
279     return spot;
280 }
281 
SpawnStatic(int16_t tilex,int16_t tiley,int16_t type)282 void SpawnStatic(
283     int16_t tilex,
284     int16_t tiley,
285     int16_t type)
286 {
287     statobj_t* spot;
288 
289     spot = FindEmptyStatic();
290 
291     if (!spot) {
292         return;
293     }
294 
295     spot->shapenum = statinfo[type].picnum;
296     spot->tilex = static_cast<uint8_t>(tilex);
297     spot->tiley = static_cast<uint8_t>(tiley);
298     spot->visspot = &spotvis[tilex][tiley];
299     spot->flags = 0;
300 
301     if ((!::is_aog_sw() && spot->shapenum == SPR_STAT_3) || // // floor lamp
302         spot->shapenum == SPR_STAT_14 || // ceiling light
303         (!::is_aog_sw() && spot->shapenum == SPR_STAT_20) ||
304         spot->shapenum == SPR_STAT_47 ||
305         spot->shapenum == SPR_STAT_51 ||
306         spot->shapenum == SPR_STAT_55 ||
307         spot->shapenum == SPR_STAT_56)
308     {
309         spot->lighting = LAMP_ON_SHADING;
310     } else {
311         spot->lighting = 0;
312     }
313 
314 
315     switch (statinfo[type].type) {
316     case block:
317         // consider it a blocking tile
318         actorat[tilex][tiley] = (objtype*)1;
319         break;
320 
321     case bo_green_key:
322     case bo_gold_key:
323         if (::is_ps()) {
324             ::Quit("Green/Gold key (AOG) at ({}, {}).", tilex, tiley);
325         }
326         TravelTable[tilex][tiley] |= TT_KEYS;
327         spot->flags = FL_BONUS;
328         spot->itemnumber = static_cast<uint8_t>(statinfo[type].type);
329         break;
330 
331     case bo_plasma_detonator:
332         if (!::is_ps()) {
333             ::Quit("Plasma detonator (PS) at ({}, {}).", tilex, tiley);
334         }
335         TravelTable[tilex][tiley] |= TT_KEYS;
336         spot->flags = FL_BONUS;
337         spot->itemnumber = static_cast<uint8_t>(statinfo[type].type);
338         break;
339 
340     case bo_red_key:
341     case bo_yellow_key:
342     case bo_blue_key:
343         TravelTable[tilex][tiley] |= TT_KEYS;
344 
345     case bo_gold1:
346     case bo_gold2:
347     case bo_gold3:
348     case bo_gold:
349     case bo_bonus:
350     case bo_money_bag:
351     case bo_loot:
352 
353     case bo_fullheal:
354     case bo_firstaid:
355     case bo_clip:
356     case bo_clip2:
357     case bo_burst_rifle:
358     case bo_ion_cannon:
359     case bo_grenade:
360     case bo_bfg_cannon:
361     case bo_pistol:
362     case bo_chicken:
363     case bo_ham:
364     case bo_water:
365     case bo_water_puddle:
366     case bo_sandwich:
367     case bo_candybar:
368     case bo_coin:
369     case bo_coin5:
370     case bo_automapper1:
371         spot->flags = FL_BONUS;
372         spot->itemnumber = static_cast<uint8_t>(statinfo[type].type);
373         break;
374 
375     default:
376         break;
377     }
378 
379     spot->areanumber = GetAreaNumber(spot->tilex, spot->tiley);
380 
381     spot++;
382 
383     if (spot == &statobjlist[MAXSTATS]) {
384         ::Quit("Too many static objects.");
385     }
386 }
387 
388 // ---------------------------------------------------------------------------
389 // ReserveStatic()
390 //
391 // Reserves a static object at location 0,0 (unseen).  This function is
392 // used to gaurantee that a static will be available.
393 // ---------------------------------------------------------------------------
ReserveStatic()394 statobj_t* ReserveStatic()
395 {
396     auto spot = FindEmptyStatic();
397 
398     if (!spot) {
399         ::Quit("Too many static objects.");
400     }
401 
402     // Mark as Used.
403 
404     spot->shapenum = 1;
405     spot->tilex = 0;
406     spot->tiley = 0;
407     spot->visspot = &spotvis[0][0];
408 
409     return spot;
410 }
411 
412 // ---------------------------------------------------------------------------
413 // FindReservedStatic()
414 //
415 // Finds a Reserved static object at location 0,0 (unseen).  This function is
416 // used to gaurantee that a static will be available.
417 // ---------------------------------------------------------------------------
FindReservedStatic()418 statobj_t* FindReservedStatic()
419 {
420     statobj_t* spot;
421 
422     for (spot = &statobjlist[0]; spot < &statobjlist[MAXSTATS]; spot++) {
423         if (spot->shapenum == 1 && (!spot->tilex) && (!spot->tiley)) { // -1 is a free spot
424             return spot;
425         }
426     }
427 
428     return nullptr;
429 }
430 
431 // ---------------------------------------------------------------------------
432 // UseReservedStatic()
433 //
434 // Finds a Reserved static object and moves it to a new location with new
435 // attributes.
436 //
437 // This function acts like PlaceItemType - But REQUIRES a reserved
438 // static.  Before using this function, make sure that you have already
439 // reserved a static to be used using ReserveStatic();
440 // ---------------------------------------------------------------------------
UseReservedStatic(int16_t itemtype,int16_t tilex,int16_t tiley)441 statobj_t* UseReservedStatic(
442     int16_t itemtype,
443     int16_t tilex,
444     int16_t tiley)
445 {
446     auto spot = FindReservedStatic();
447     int16_t type;
448 
449     if (!spot) {
450         ::Quit("Count not find a reserved static at location (0, 0) with shape #1.");
451     }
452 
453     //
454     // find the item number
455     //
456 
457     for (type = 0;; type++) {
458         if (statinfo[type].picnum == -1) { // End of Static List...
459             ::Quit("Couldn't find type.");
460         }
461 
462         if (statinfo[type].type == itemtype) { // Bingo, Found it!
463             break;
464         }
465     }
466 
467     //
468     // place it
469     //
470 
471     switch (type) {
472     case bo_green_key:
473     case bo_gold_key:
474     case bo_red_key:
475     case bo_yellow_key:
476     case bo_blue_key:
477         TravelTable[tilex][tiley] |= TT_KEYS;
478         break;
479     }
480 
481     spot->shapenum = statinfo[type].picnum;
482     spot->tilex = static_cast<uint8_t>(tilex);
483     spot->tiley = static_cast<uint8_t>(tiley);
484     spot->visspot = &spotvis[tilex][tiley];
485     spot->flags = FL_BONUS;
486     spot->itemnumber = static_cast<uint8_t>(statinfo[type].type);
487 
488     spot->areanumber = GetAreaNumber(spot->tilex, spot->tiley);
489 
490     return spot;
491 }
492 
493 int8_t pint_xy[8][2] = {
494     { -1, -1 }, { -1, 0 }, { -1, 1 }, { 0, -1 },
495     { 0, 1 }, { 1, -1 }, { 1, 0 }, { 1, 1 },
496 };
497 
PlaceReservedItemNearTile(int16_t itemtype,int16_t tilex,int16_t tiley)498 void PlaceReservedItemNearTile(
499     int16_t itemtype,
500     int16_t tilex,
501     int16_t tiley)
502 {
503     int8_t loop;
504 
505     for (loop = 0; loop < 8; loop++) {
506         int8_t x = static_cast<int8_t>(tilex + pint_xy[static_cast<int>(loop)][1]), y = static_cast<int8_t>(tiley + pint_xy[static_cast<int>(loop)][0]);
507 
508         if (!tilemap[static_cast<int>(x)][static_cast<int>(y)]) {
509             if (actorat[static_cast<int>(x)][static_cast<int>(y)] == reinterpret_cast<objtype*>(1)) { // Check for a SOLID static
510                 continue;
511             }
512 
513             UseReservedStatic(itemtype, x, y);
514             return;
515         }
516     }
517 
518     UseReservedStatic(itemtype, tilex, tiley);
519 }
520 
521 /*
522 ===============
523 =
524 = PlaceItemType
525 =
526 = Called during game play to drop actors' items.  It finds the proper
527 = item number based on the item type (bo_???).  If there are no free item
528 = spots, nothing is done.
529 =
530 ===============
531 */
PlaceItemType(int16_t itemtype,int16_t tilex,int16_t tiley)532 void PlaceItemType(
533     int16_t itemtype,
534     int16_t tilex,
535     int16_t tiley)
536 {
537     int16_t type;
538     statobj_t* spot;
539 
540 //
541 // find the item number
542 //
543     for (type = 0;; type++) {
544         if (statinfo[type].picnum == -1) { // end of list
545             ::Quit("Couldn't find type.");
546         }
547         if (statinfo[type].type == itemtype) {
548             break;
549         }
550     }
551 
552 //
553 // find a spot in statobjlist to put it in
554 //
555     spot = FindEmptyStatic();
556 
557     if (!spot) {
558         return;
559     }
560 
561 //
562 // place it
563 //
564     spot->shapenum = statinfo[type].picnum;
565     spot->tilex = static_cast<uint8_t>(tilex);
566     spot->tiley = static_cast<uint8_t>(tiley);
567     spot->visspot = &spotvis[tilex][tiley];
568     spot->flags = FL_BONUS;
569     spot->itemnumber = static_cast<uint8_t>(statinfo[type].type);
570 
571     spot->areanumber = GetAreaNumber(spot->tilex, spot->tiley);
572 }
573 
PlaceItemNearTile(int16_t itemtype,int16_t tilex,int16_t tiley)574 void PlaceItemNearTile(
575     int16_t itemtype,
576     int16_t tilex,
577     int16_t tiley)
578 {
579 // [0] is the y offset
580 // [1] is the x offset
581 //
582     int8_t loop;
583 
584     for (loop = 0; loop < 8; loop++) {
585         int8_t x = static_cast<int8_t>(tilex + pint_xy[static_cast<int>(loop)][1]), y = static_cast<int8_t>(tiley + pint_xy[static_cast<int>(loop)][0]);
586 
587         if (!tilemap[static_cast<int>(x)][static_cast<int>(y)]) {
588             if (actorat[static_cast<int>(x)][static_cast<int>(y)] == reinterpret_cast<objtype*>(1)) { // Check for a SOLID static
589                 continue;
590             }
591 
592             PlaceItemType(itemtype, x, y);
593             return;
594         }
595     }
596 
597     PlaceItemType(itemtype, tilex, tiley);
598 }
599 
600 // --------------------------------------------------------------------------
601 // ExplodeStatics()
602 //
603 //  NOTES: Explodes statics in a one tile radius from a given tile x and tile y
604 //
605 // --------------------------------------------------------------------------
ExplodeStatics(int16_t tilex,int16_t tiley)606 void ExplodeStatics(
607     int16_t tilex,
608     int16_t tiley)
609 {
610     if (!::is_ps()) {
611         return;
612     }
613 
614     statobj_t* spot;
615     int16_t y_diff, x_diff;
616     bool remove;
617 
618     for (spot = &statobjlist[0]; spot != laststatobj; spot++) {
619         if (spot->shapenum != -1) {
620             y_diff = spot->tiley - tiley;
621             y_diff = ABS(y_diff);
622 
623             x_diff = spot->tilex - tilex;
624             x_diff = ABS(x_diff);
625 
626             if (x_diff < 2 && y_diff < 2) {
627                 remove = false;
628 
629                 //
630                 // Test for specific statics..
631                 //
632 
633                 switch (spot->itemnumber) {
634                 //
635                 // Check for Clips
636                 //
637                 case bo_clip:
638                 case bo_clip2:
639                     remove = true;
640                     SpawnCusExplosion((((fixed)spot->tilex) << TILESHIFT) + 0x7FFF,
641                                       (((fixed)spot->tiley) << TILESHIFT) + 0x7FFF,
642                                       SPR_CLIP_EXP1, 7, 3 + (US_RndT() & 0x3), explosionobj);
643                     break;
644                 }
645 
646 
647                 //
648                 // Check to see if we remove it.
649                 //
650                 if (remove) {
651                     // Remove static
652                     spot->shapenum = -1;
653                     spot->itemnumber = bo_nothing;
654                 }
655             }
656         }
657     }
658 }
659 
660 /*
661 =============================================================================
662 
663  DOORS
664 
665 doorobjlist[] holds most of the information for the doors
666 
667 doorposition[] holds the amount the door is open, ranging from 0 to 0xffff
668         this is directly accessed by AsmRefresh during rendering
669 
670 The number of doors is limited to 64 because a spot in tilemap holds the
671         door number in the low 6 bits, with the high bit meaning a door center
672         and bit 6 meaning a door side tile
673 
674 Open doors conect two areas, so sounds will travel between them and sight
675         will be checked when the player is in a connected area.
676 
677 Areaconnect is incremented/decremented by each door. If >0 they connect
678 
679 Every time a door opens or closes the areabyplayer matrix gets recalculated.
680         An area is true if it connects with the player's current spor.
681 
682 =============================================================================
683 */
684 
685 static const int16_t OPENTICS = 300;
686 
687 doorobj_t doorobjlist[MAXDOORS];
688 doorobj_t* lastdoorobj;
689 int16_t doornum;
690 
691 // leading edge of door 0=closed, 0xffff = fully open
692 uint16_t doorposition[MAXDOORS];
693 
694 uint8_t areaconnect[NUMAREAS][NUMAREAS];
695 
696 bool areabyplayer[NUMAREAS];
697 
698 
699 /*
700 ==============
701 =
702 = ConnectAreas
703 =
704 = Scans outward from playerarea, marking all connected areas
705 =
706 ==============
707 */
RecursiveConnect(int16_t areanumber)708 void RecursiveConnect(
709     int16_t areanumber)
710 {
711     int16_t i;
712 
713     for (i = 0; i < NUMAREAS; i++) {
714         if (areaconnect[areanumber][i] && !areabyplayer[i]) {
715             areabyplayer[i] = true;
716             RecursiveConnect(i);
717         }
718     }
719 }
720 
ConnectAreas()721 void ConnectAreas()
722 {
723     memset(areabyplayer, 0, sizeof(areabyplayer));
724     areabyplayer[player->areanumber] = true;
725     RecursiveConnect(player->areanumber);
726 }
727 
InitAreas()728 void InitAreas()
729 {
730     memset(areabyplayer, 0, sizeof(areabyplayer));
731     areabyplayer[player->areanumber] = true;
732 }
733 
InitDoorList()734 void InitDoorList()
735 {
736     memset(areabyplayer, 0, sizeof(areabyplayer));
737     memset(areaconnect, 0, sizeof(areaconnect));
738 
739     lastdoorobj = &doorobjlist[0];
740     doornum = 0;
741 }
742 
SpawnDoor(int16_t tilex,int16_t tiley,bool vertical,keytype lock,door_t type)743 void SpawnDoor(
744     int16_t tilex,
745     int16_t tiley,
746     bool vertical,
747     keytype lock,
748     door_t type)
749 {
750     uint16_t* map[2];
751 
752     map[0] = mapsegs[0] + farmapylookup[tiley] + tilex;
753     map[1] = mapsegs[1] + farmapylookup[tiley] + tilex;
754 
755     if (doornum == 64) {
756         ::Quit("Too many doors in level.");
757     }
758 
759     doorposition[doornum] = 0; // doors start out fully closed
760     lastdoorobj->tilex = static_cast<uint8_t>(tilex);
761     lastdoorobj->tiley = static_cast<uint8_t>(tiley);
762     lastdoorobj->vertical = vertical;
763     lastdoorobj->lock = lock;
764     lastdoorobj->type = type;
765     lastdoorobj->action = dr_closed;
766     lastdoorobj->flags = DR_BLASTABLE; // JIM - Do something with this! jdebug
767 
768     if (vertical) {
769         lastdoorobj->areanumber[0] = GetAreaNumber(static_cast<int8_t>(tilex + 1), static_cast<int8_t>(tiley));
770         lastdoorobj->areanumber[1] = GetAreaNumber(static_cast<int8_t>(tilex - 1), static_cast<int8_t>(tiley));
771     } else {
772         lastdoorobj->areanumber[0] = GetAreaNumber(static_cast<int8_t>(tilex), static_cast<int8_t>(tiley - 1));
773         lastdoorobj->areanumber[1] = GetAreaNumber(static_cast<int8_t>(tilex), static_cast<int8_t>(tiley + 1));
774     }
775 
776     // consider it a solid wall
777     actorat[tilex][tiley] = reinterpret_cast<objtype*>(static_cast<size_t>(doornum | 0x80));
778 
779 //
780 // make the door tile a special tile, and mark the adjacent tiles
781 // for door sides
782 //
783     tilemap[tilex][tiley] = static_cast<uint8_t>(doornum | 0x80);
784 
785     if (vertical) {
786         if (*(map[0] - mapwidth - 1) == TRANSPORTERTILE) {
787             *map[0] = GetAreaNumber(static_cast<int8_t>(tilex + 1), static_cast<int8_t>(tiley));
788         } else {
789             *map[0] = GetAreaNumber(static_cast<int8_t>(tilex - 1), static_cast<int8_t>(tiley));
790         }
791         tilemap[tilex][tiley - 1] |= 0x40;
792         tilemap[tilex][tiley + 1] |= 0x40;
793     } else {
794         *map[0] = GetAreaNumber(static_cast<int8_t>(tilex), static_cast<int8_t>(tiley - 1));
795         tilemap[tilex - 1][tiley] |= 0x40;
796         tilemap[tilex + 1][tiley] |= 0x40;
797     }
798 
799     doornum++;
800     lastdoorobj++;
801 }
802 
CheckLinkedDoors(int16_t door,int16_t door_dir)803 void CheckLinkedDoors(
804     int16_t door,
805     int16_t door_dir)
806 {
807     static int16_t LinkCheck = 0;
808     static int16_t base_tilex;
809     static int16_t base_tiley;
810 
811     int16_t tilex = doorobjlist[door].tilex;
812     int16_t tiley = doorobjlist[door].tiley;
813     int16_t next_tilex = 0;
814     int16_t next_tiley = 0;
815 
816 // Find next door in link.
817 //
818     if (*(mapsegs[1] + (farmapylookup[tiley] + tilex))) {
819         uint16_t value = *(mapsegs[1] + (farmapylookup[tiley] + tilex));
820 
821         // Define the next door in the link.
822         //
823         next_tilex = (value & 0xff00) >> 8;
824         next_tiley = value & 0xff;
825 
826         // Is this the head of the link?
827         //
828         if (!LinkCheck) {
829             base_tilex = tilex;
830             base_tiley = tiley;
831         }
832     }
833 
834     LinkCheck++;
835 
836 // Recursively open/close linked doors.
837 //
838     if ((next_tilex) &&
839         (next_tiley) &&
840         ((next_tilex != base_tilex) || (next_tiley != base_tiley))
841         )
842     {
843         int16_t door_index = tilemap[next_tilex][next_tiley] & ~0x80;
844 
845         switch (door_dir) {
846         case dr_opening:
847             doorobjlist[door_index].lock = kt_none;
848             OpenDoor(door_index);
849             break;
850 
851         case dr_closing:
852             doorobjlist[door_index].lock = kt_none;
853             CloseDoor(door_index);
854             break;
855         }
856     }
857 
858     LinkCheck--;
859 }
860 
OpenDoor(int16_t door)861 void OpenDoor(
862     int16_t door)
863 {
864 
865     if (doorobjlist[door].action == dr_jammed) {
866         return;
867     }
868 
869     if (doorobjlist[door].action == dr_open) {
870         doorobjlist[door].ticcount = 0; // reset open time
871     } else {
872         doorobjlist[door].action = dr_opening; // start it opening
873 
874     }
875     CheckLinkedDoors(door, dr_opening);
876 }
877 
get_actor_near_door(int tile_x,int tile_y)878 objtype* get_actor_near_door(
879     int tile_x,
880     int tile_y)
881 {
882     if (::is_aog()) {
883         for (int i = 0; i < doornum; ++i) {
884             const doorobj_t& door = doorobjlist[i];
885 
886             if (door.tilex == tile_x && door.tiley == tile_y) {
887                 // It's a closing door, not an actor.
888                 return nullptr;
889             }
890         }
891     }
892 
893     return actorat[tile_x][tile_y];
894 }
895 
CloseDoor(int16_t door)896 void CloseDoor(
897     int16_t door)
898 {
899     int16_t tilex, tiley, area;
900     objtype* check;
901 
902     if (doorobjlist[door].action == dr_jammed) {
903         return;
904     }
905 
906 //
907 // don't close on anything solid
908 //
909     tilex = doorobjlist[door].tilex;
910     tiley = doorobjlist[door].tiley;
911 
912     if (actorat[tilex][tiley]) {
913         return;
914     }
915 
916     if (player->tilex == tilex && player->tiley == tiley) {
917         return;
918     }
919 
920     if (doorobjlist[door].vertical) {
921         if (player->tiley == tiley) {
922             if (((player->x + MINDIST) >> TILESHIFT) == tilex) {
923                 return;
924             }
925             if (((player->x - MINDIST) >> TILESHIFT) == tilex) {
926                 return;
927             }
928         }
929         check = ::get_actor_near_door(tilex - 1, tiley);
930         if (check && ((check->x + MINDIST) >> TILESHIFT) == tilex) {
931             return;
932         }
933         check = ::get_actor_near_door(tilex + 1, tiley);
934         if (check && ((check->x - MINDIST) >> TILESHIFT) == tilex) {
935             return;
936         }
937     } else if (!doorobjlist[door].vertical) {
938         if (player->tilex == tilex) {
939             if (((player->y + MINDIST) >> TILESHIFT) == tiley) {
940                 return;
941             }
942             if (((player->y - MINDIST) >> TILESHIFT) == tiley) {
943                 return;
944             }
945         }
946         check = ::get_actor_near_door(tilex, tiley - 1);
947         if (check && ((check->y + MINDIST) >> TILESHIFT) == tiley) {
948             return;
949         }
950         check = ::get_actor_near_door(tilex, tiley + 1);
951         if (check && ((check->y - MINDIST) >> TILESHIFT) == tiley) {
952             return;
953         }
954     }
955 
956 
957 //
958 // play door sound if in a connected area
959 //
960     area = GetAreaNumber(doorobjlist[door].tilex, doorobjlist[door].tiley);
961     if (areabyplayer[area]) {
962         switch (doorobjlist[door].type) {
963         case dr_bio:
964         case dr_office:
965         case dr_space:
966         case dr_normal:
967             ::sd_play_door_sound(
968                 HTECHDOORCLOSESND, &doorobjlist[door]);
969             break;
970 
971         default:
972             ::sd_play_door_sound(CLOSEDOORSND, &doorobjlist[door]);
973             break;
974         }
975     }
976 
977     doorobjlist[door].action = dr_closing;
978 //
979 // make the door space solid
980 //
981 
982     actorat[tilex][tiley] = reinterpret_cast<objtype*>(static_cast<size_t>(door | 0x80));
983 
984     CheckLinkedDoors(door, dr_closing);
985 
986 }
987 
988 /*
989 =====================
990 =
991 = OperateDoor
992 =
993 = The player wants to change the door's direction
994 =
995 =====================
996 */
997 
998 const char* const od_oneway = "\r\r   DOOR LOCKED FROM\r      THIS SIDE.\r^XX";
999 const char* const od_locked = "\r\r   DOOR PERMANENTLY\r        LOCKED.\r^XX";
1000 const char* const od_reddenied = "\r\r      RED LEVEL\r    ACCESS DENIED!\r^XX";
1001 const char* const od_yellowdenied = "\r\r     YELLOW LEVEL\r    ACCESS DENIED!\r^XX";
1002 const char* const od_bluedenied = "\r\r      BLUE LEVEL\r    ACCESS DENIED!\r^XX";
1003 
1004 const char* const od_green_denied =
1005     "\r"
1006     "\r"
1007     "     GREEN LEVEL\r"
1008     "    ACCESS DENIED!\r"
1009     "^XX"
1010 ;
1011 
1012 const char* const od_gold_denied =
1013     "\r"
1014     "\r"
1015     "      GOLD LEVEL\r"
1016     "    ACCESS DENIED!\r"
1017     "^XX"
1018 ;
1019 
1020 const char* const od_granted = "\r\r    ACCESS GRANTED\r    DOOR UNLOCKED.\r^XX";
1021 const char* const od_operating = "\r\r    OPERATING DOOR.\r^XX";
1022 
OperateDoor(int16_t door)1023 void OperateDoor(
1024     int16_t door)
1025 {
1026     int16_t lock;
1027     bool oneway = false;
1028 
1029 
1030     //
1031     // Check for wrong way on a ONEWAY door.
1032     //
1033 
1034     switch (doorobjlist[door].type) {
1035     case dr_oneway_left:
1036         if (player->tilex < doorobjlist[door].tilex) {
1037             oneway = true;
1038         }
1039         break;
1040 
1041     case dr_oneway_right:
1042         if (player->tilex > doorobjlist[door].tilex) {
1043             oneway = true;
1044         }
1045         break;
1046 
1047     case dr_oneway_up:
1048         if (player->tiley < doorobjlist[door].tiley) {
1049             oneway = true;
1050         }
1051         break;
1052 
1053     case dr_oneway_down:
1054         if (player->tiley > doorobjlist[door].tiley) {
1055             oneway = true;
1056         }
1057         break;
1058 
1059     default:
1060         break;
1061     }
1062 
1063     if (oneway) {
1064         if (doorobjlist[door].action == dr_closed) {
1065             DISPLAY_TIMED_MSG(od_oneway, MP_DOOR_OPERATE, MT_GENERAL);
1066             ::sd_play_player_sound(NOWAYSND, bstone::AC_NO_WAY);
1067         }
1068 
1069         return;
1070     }
1071 
1072     //
1073     // Check for possibly being locked
1074     //
1075 
1076     lock = static_cast<int16_t>(doorobjlist[door].lock);
1077     if (lock != kt_none) {
1078         if (!(gamestate.numkeys[lock - kt_red])) {
1079             ::sd_play_player_sound(NOWAYSND, bstone::AC_NO_WAY);
1080 
1081             switch (lock) {
1082             case kt_red:
1083                 DISPLAY_TIMED_MSG(od_reddenied, MP_DOOR_OPERATE, MT_GENERAL);
1084                 break;
1085 
1086             case kt_yellow:
1087                 DISPLAY_TIMED_MSG(od_yellowdenied, MP_DOOR_OPERATE, MT_GENERAL);
1088                 break;
1089 
1090             case kt_blue:
1091                 DISPLAY_TIMED_MSG(od_bluedenied, MP_DOOR_OPERATE, MT_GENERAL);
1092                 break;
1093 
1094             case kt_green:
1095                 DISPLAY_TIMED_MSG(od_green_denied, MP_DOOR_OPERATE, MT_GENERAL);
1096                 break;
1097 
1098             case kt_gold:
1099                 DISPLAY_TIMED_MSG(od_gold_denied, MP_DOOR_OPERATE, MT_GENERAL);
1100                 break;
1101 
1102             default:
1103                 DISPLAY_TIMED_MSG(od_locked, MP_DOOR_OPERATE, MT_GENERAL);
1104                 break;
1105             }
1106 
1107             return;
1108         } else {
1109             TakeKey(lock - kt_red);
1110             DISPLAY_TIMED_MSG(od_granted, MP_DOOR_OPERATE, MT_GENERAL);
1111             doorobjlist[door].lock = kt_none;                           // UnLock door
1112         }
1113     } else {
1114         DISPLAY_TIMED_MSG(od_operating, MP_DOOR_OPERATE, MT_GENERAL);
1115     }
1116 
1117     switch (doorobjlist[door].action) {
1118     case dr_closed:
1119     case dr_closing:
1120         OpenDoor(door);
1121         break;
1122 
1123     case dr_open:
1124     case dr_opening:
1125         CloseDoor(door);
1126         break;
1127     default:
1128         break;
1129     }
1130 }
1131 
BlockDoorOpen(int16_t door)1132 void BlockDoorOpen(
1133     int16_t door)
1134 {
1135     doorobjlist[door].action = dr_jammed;
1136     doorobjlist[door].ticcount = 0;
1137     doorposition[door] = 0xFFFF;
1138     doorobjlist[door].lock = kt_none;
1139     doorobjlist[door].flags &= ~DR_BLASTABLE;
1140 
1141     actorat[doorobjlist[door].tilex][doorobjlist[door].tiley] = 0;
1142 
1143     TransformAreas(doorobjlist[door].tilex, doorobjlist[door].tiley, 1);
1144 }
1145 
TryBlastDoor(int8_t door)1146 void TryBlastDoor(
1147     int8_t door)
1148 {
1149     switch (doorobjlist[static_cast<int>(door)].type) {
1150     case dr_oneway_left:
1151     case dr_oneway_up:
1152     case dr_oneway_right:
1153     case dr_oneway_down:
1154         break;
1155 
1156     default:
1157         if (doorposition[static_cast<int>(door)] < 0x7fff &&
1158             doorobjlist[static_cast<int>(door)].action != dr_jammed &&
1159             doorobjlist[static_cast<int>(door)].lock == kt_none)
1160         {
1161             BlockDoorOpen(door);
1162             SpawnCusExplosion((((fixed)doorobjlist[static_cast<int>(door)].tilex) << TILESHIFT) + 0x7FFF,
1163                               (((fixed)doorobjlist[static_cast<int>(door)].tiley) << TILESHIFT) + 0x7FFF,
1164                               SPR_EXPLOSION_1, 4, 3, doorexplodeobj);
1165         }
1166         break;
1167     }
1168 }
1169 
BlastNearDoors(int16_t tilex,int16_t tiley)1170 void BlastNearDoors(
1171     int16_t tilex,
1172     int16_t tiley)
1173 {
1174     uint8_t door;
1175     char* doorptr;
1176     int16_t x, y;
1177 
1178     doorptr = (char*)&tilemap[tilex][tiley];
1179 
1180     for (x = -1; x < 2; x++) {
1181         for (y = -64; y < 128; y += 64) {
1182             if ((door = *(doorptr + x + y)) & 0x80) {
1183                 door &= ~0x80;
1184                 TryBlastDoor(door);
1185             }
1186         }
1187     }
1188 }
1189 
DropPlasmaDetonator()1190 void DropPlasmaDetonator()
1191 {
1192     auto obj = ::MoveHiddenOfs(
1193         plasma_detonator_reserveobj,
1194         plasma_detonatorobj,
1195         player->x,
1196         player->y);
1197 
1198     if (obj) {
1199         obj->flags |= FL_SHOOTABLE;
1200 
1201         DISPLAY_TIMED_MSG(pd_dropped, MP_DOOR_OPERATE, MT_GENERAL);
1202         ::sd_play_actor_sound(ROBOT_SERVOSND, obj, bstone::AC_VOICE);
1203 
1204         TakePlasmaDetonator(1);
1205         return;
1206     }
1207 
1208     ::Quit("Could not find Fision/Plasma Detonator reserve object.");
1209 }
1210 
1211 // --------------------------------------------------------------------------
1212 // TryDropPlasmaDetonator()  - Will check to see if player is close enough to
1213 //                             drop a detonator.
1214 // --------------------------------------------------------------------------
TryDropPlasmaDetonator()1215 void TryDropPlasmaDetonator()
1216 {
1217     const int16_t MAX_RANGE_DIST = 2;
1218 
1219     objtype* obj;
1220     int16_t distx, disty, distance;
1221 
1222 
1223     if (!gamestuff.level[gamestate.mapon + 1].locked) {
1224         DISPLAY_TIMED_MSG(pd_floornotlocked, MP_DETONATOR, MT_GENERAL);
1225         return;
1226     }
1227 
1228     if (gamestate.mapon > 19) {
1229         DISPLAY_TIMED_MSG(pd_no_computer, MP_DETONATOR, MT_GENERAL);
1230         return;
1231     }
1232 
1233     if (!gamestate.plasma_detonators) {
1234         DISPLAY_TIMED_MSG(pd_donthaveany, MP_DETONATOR, MT_GENERAL);
1235         return;
1236     }
1237 
1238     obj = FindObj(rotating_cubeobj, -1, -1);
1239 
1240     if (!obj) {
1241         ::Quit("Cound not find security cube - Need to have one pal!");
1242     }
1243 
1244     if (obj->areanumber != player->areanumber) {
1245         DISPLAY_TIMED_MSG(pd_notnear, MP_DETONATOR, MT_GENERAL);
1246         return;
1247     }
1248 
1249     distx = player->tilex - obj->tilex;
1250     distx = ABS(distx);
1251     disty = player->tiley - obj->tiley;
1252     disty = ABS(disty);
1253     distance = distx > disty ? distx : disty;
1254 
1255     if (distance > MAX_RANGE_DIST) {
1256         DISPLAY_TIMED_MSG(pd_getcloser, MP_DETONATOR, MT_GENERAL);
1257         return;
1258     } else {
1259         DropPlasmaDetonator();
1260     }
1261 }
1262 
1263 /*
1264 ===============
1265 =
1266 = DoorOpen
1267 =
1268 = Close the door after three seconds
1269 =
1270 ===============
1271 */
DoorOpen(int16_t door)1272 void DoorOpen(
1273     int16_t door)
1274 {
1275     if ((doorobjlist[door].ticcount += tics) >= OPENTICS) {
1276         CloseDoor(door);
1277     }
1278 }
1279 
TransformAreas(int8_t tilex,int8_t tiley,int8_t xform)1280 int16_t TransformAreas(
1281     int8_t tilex,
1282     int8_t tiley,
1283     int8_t xform)
1284 {
1285     int16_t xofs = 0, yofs = 0;
1286     uint8_t area1, area2;
1287 
1288 // Is this walkway:  Horizontal?   Vertical?   Error?
1289 //
1290     if ((tilemap[static_cast<int>(tilex)][static_cast<int>(tiley + 1)]) && (tilemap[static_cast<int>(tilex)][static_cast<int>(tiley - 1)])) {
1291         xofs = 1;
1292         yofs = 0;
1293     } else if ((tilemap[static_cast<int>(tilex + 1)][static_cast<int>(tiley)]) && (tilemap[static_cast<int>(tilex - 1)][static_cast<int>(tiley)])) {
1294         xofs = 0;
1295         yofs = 1;
1296     } else {
1297         ::Quit("Invalid linkable area.");
1298     }
1299 
1300 // Define the two areas...
1301 //
1302     area1 = GetAreaNumber(static_cast<int8_t>(tilex + xofs), static_cast<int8_t>(tiley + yofs));
1303     if (area1 >= NUMAREAS) {
1304         ::Quit("Area1 out of table range.");
1305     }
1306 
1307     area2 = GetAreaNumber(static_cast<int8_t>(tilex - xofs), static_cast<int8_t>(tiley - yofs));
1308     if (area2 >= NUMAREAS) {
1309         ::Quit("Area2 out of table range.");
1310     }
1311 
1312 // Connect these two areas.
1313 //
1314     areaconnect[area1][area2] += xform;
1315     areaconnect[area2][area1] += xform;
1316     ConnectAreas();
1317 
1318     return area1;
1319 }
1320 
DoorOpening(int16_t door)1321 void DoorOpening(
1322     int16_t door)
1323 {
1324     int16_t area1;
1325     int32_t position;
1326 
1327     position = doorposition[door];
1328     if (!position) {
1329         area1 = TransformAreas(doorobjlist[door].tilex, doorobjlist[door].tiley, 1);
1330 
1331         if (areabyplayer[area1]) {
1332             switch (doorobjlist[door].type) {
1333             case dr_bio:
1334             case dr_office:
1335             case dr_space:
1336             case dr_normal:
1337                 ::sd_play_door_sound(
1338                     HTECHDOOROPENSND, &doorobjlist[door]);
1339                 break;
1340 
1341             default:
1342                 ::sd_play_door_sound(OPENDOORSND, &doorobjlist[door]);
1343                 break;
1344             }
1345         }
1346     }
1347 
1348 //
1349 // slide the door by an adaptive amount
1350 //
1351     position += tics << 10;
1352     if (position >= 0xffff) {
1353         //
1354         // door is all the way open
1355         //
1356         position = 0xffff;
1357         doorobjlist[door].ticcount = 0;
1358         doorobjlist[door].action = dr_open;
1359         actorat[doorobjlist[door].tilex][doorobjlist[door].tiley] = 0;
1360     }
1361 
1362     doorposition[door] = static_cast<uint16_t>(position);
1363 }
1364 
DoorClosing(int16_t door)1365 void DoorClosing(
1366     int16_t door)
1367 {
1368     int32_t position;
1369     int16_t tilex, tiley;
1370 
1371     tilex = doorobjlist[door].tilex;
1372     tiley = doorobjlist[door].tiley;
1373 
1374     if ((actorat[tilex][tiley] != reinterpret_cast<objtype*>(static_cast<size_t>(door | 0x80))) ||
1375         (player->tilex == tilex && player->tiley == tiley))
1376     {
1377         // something got inside the door
1378         OpenDoor(door);
1379         return;
1380     }
1381 
1382     position = doorposition[door];
1383 
1384 //
1385 // slide the door by an adaptive amount
1386 //
1387     position -= tics << 10;
1388     if (position <= 0) {
1389         position = 0;
1390         doorobjlist[door].action = dr_closed;
1391         TransformAreas(doorobjlist[door].tilex, doorobjlist[door].tiley, -1);
1392     }
1393 
1394     doorposition[door] = static_cast<uint16_t>(position);
1395 }
1396 
1397 /*
1398 =====================
1399 =
1400 = MoveDoors
1401 =
1402 = Called from PlayLoop
1403 =
1404 =====================
1405 */
MoveDoors()1406 void MoveDoors()
1407 {
1408     int16_t door;
1409 
1410     for (door = 0; door < doornum; door++) {
1411         switch (doorobjlist[door].action) {
1412         case dr_open:
1413             DoorOpen(door);
1414             break;
1415 
1416         case dr_opening:
1417             DoorOpening(door);
1418             break;
1419 
1420         case dr_closing:
1421             DoorClosing(door);
1422             break;
1423 
1424         default:
1425             break;
1426         }
1427     }
1428 }
1429 
1430 
1431 /*
1432 =============================================================================
1433 
1434  PUSHABLE WALLS
1435 
1436 =============================================================================
1437 */
1438 
1439 uint16_t pwallstate;
1440 uint16_t pwallpos; // amount a pushable wall has been moved (0-63)
1441 uint16_t pwallx = 0, pwally = 0;
1442 int16_t pwalldir, pwalldist;
1443 
1444 
PushWall(int16_t checkx,int16_t checky,int16_t dir)1445 void PushWall(
1446     int16_t checkx,
1447     int16_t checky,
1448     int16_t dir)
1449 {
1450     int16_t oldtile;
1451 
1452     if (pwallstate) {
1453         return;
1454     }
1455 
1456     TransformAreas(static_cast<int8_t>(checkx), static_cast<int8_t>(checky), 1);
1457 
1458     oldtile = tilemap[checkx][checky];
1459     if (!oldtile) {
1460         return;
1461     }
1462 
1463     switch (dir) {
1464     case di_north:
1465         if (actorat[checkx][checky - 1]) {
1466             return;
1467         }
1468 
1469         tilemap[checkx][checky - 1] = static_cast<uint8_t>(oldtile);
1470         actorat[checkx][checky - 1] = reinterpret_cast<objtype*>(oldtile);
1471         break;
1472 
1473     case di_east:
1474         if (actorat[checkx + 1][checky]) {
1475             return;
1476         }
1477 
1478         tilemap[checkx + 1][checky] = static_cast<uint8_t>(oldtile);
1479         actorat[checkx + 1][checky] = reinterpret_cast<objtype*>(oldtile);
1480         break;
1481 
1482     case di_south:
1483         if (actorat[checkx][checky + 1]) {
1484             return;
1485         }
1486 
1487         tilemap[checkx][checky + 1] = static_cast<uint8_t>(oldtile);
1488         actorat[checkx][checky + 1] = reinterpret_cast<objtype*>(oldtile);
1489         break;
1490 
1491     case di_west:
1492         if (actorat[checkx - 1][checky]) {
1493             return;
1494         }
1495 
1496         tilemap[checkx - 1][checky] = static_cast<uint8_t>(oldtile);
1497         actorat[checkx - 1][checky] = reinterpret_cast<objtype*>(oldtile);
1498         break;
1499     }
1500 
1501     pwalldist = 2;
1502     pwallx = checkx;
1503     pwally = checky;
1504     pwalldir = dir;
1505     pwallstate = 1;
1506     pwallpos = 0;
1507     tilemap[pwallx][pwally] |= 0xc0;
1508     *(mapsegs[1] + farmapylookup[pwally] + pwallx) = 0; // remove P tile info
1509 
1510     ::sd_play_wall_sound(PUSHWALLSND);
1511 }
1512 
MovePWalls()1513 void MovePWalls()
1514 {
1515     int16_t oldblock, oldtile;
1516 
1517     if (!pwallstate) {
1518         return;
1519     }
1520 
1521     oldblock = pwallstate / 128;
1522 
1523     pwallstate += tics * 4;
1524 
1525     if (pwallstate / 128 != oldblock) {
1526         uint16_t areanumber;
1527 
1528         pwalldist--;
1529 
1530         // block crossed into a new block
1531         oldtile = tilemap[pwallx][pwally] & 63;
1532 
1533         //
1534         // the tile can now be walked into
1535         //
1536         tilemap[pwallx][pwally] = 0;
1537 
1538         actorat[pwallx][pwally] = nullptr;
1539 
1540         areanumber = GetAreaNumber(player->tilex, player->tiley);
1541         if (GAN_HiddenArea) {
1542             areanumber += HIDDENAREATILE;
1543         } else {
1544             areanumber += AREATILE;
1545         }
1546         *(mapsegs[0] + farmapylookup[pwally] + pwallx) = areanumber;
1547 
1548         //
1549         // see if it should be pushed farther
1550         //
1551         if (!pwalldist) {
1552             //
1553             // the block has been pushed two tiles
1554             //
1555             pwallstate = 0;
1556             return;
1557         } else {
1558             switch (pwalldir) {
1559             case di_north:
1560                 pwally--;
1561                 if (actorat[pwallx][pwally - 1]) {
1562                     pwallstate = 0;
1563                     return;
1564                 }
1565 
1566                 tilemap[pwallx][pwally - 1] = static_cast<uint8_t>(oldtile);
1567                 actorat[pwallx][pwally - 1] = reinterpret_cast<objtype*>(oldtile);
1568                 break;
1569 
1570             case di_east:
1571                 pwallx++;
1572                 if (actorat[pwallx + 1][pwally]) {
1573                     pwallstate = 0;
1574                     return;
1575                 }
1576 
1577                 tilemap[pwallx + 1][pwally] = static_cast<uint8_t>(oldtile);
1578                 actorat[pwallx + 1][pwally] = reinterpret_cast<objtype*>(oldtile);
1579                 break;
1580 
1581             case di_south:
1582                 pwally++;
1583                 if (actorat[pwallx][pwally + 1]) {
1584                     pwallstate = 0;
1585                     return;
1586                 }
1587 
1588                 tilemap[pwallx][pwally + 1] = static_cast<uint8_t>(oldtile);
1589                 actorat[pwallx][pwally + 1] = reinterpret_cast<objtype*>(oldtile);
1590                 break;
1591 
1592             case di_west:
1593                 pwallx--;
1594                 if (actorat[pwallx - 1][pwally]) {
1595                     pwallstate = 0;
1596                     return;
1597                 }
1598 
1599                 tilemap[pwallx - 1][pwally] = static_cast<uint8_t>(oldtile);
1600                 actorat[pwallx - 1][pwally] = reinterpret_cast<objtype*>(oldtile);
1601                 break;
1602             }
1603 
1604             tilemap[pwallx][pwally] = static_cast<uint8_t>(oldtile | 0xc0);
1605         }
1606     }
1607 
1608 
1609     pwallpos = (pwallstate / 2) & 63;
1610 }
1611 
1612 // ==========================================================================
1613 //
1614 //                    'SPECIAL MESSAGE' CACHING SYSTEM
1615 //
1616 // When creating special 'types' of message caching structures, make sure
1617 // all 'special data' is placed at the end of the BASIC message structures.
1618 // In memory, BASIC INFO should appear first. ex:
1619 //
1620 // mCacheList
1621 // ---> NumMsgs
1622 // ---> mCacheInfo
1623 //      ---> local_val
1624 //      ---> global_val
1625 //      ---> mSeg
1626 //
1627 // ... all special data follows ...
1628 //
1629 // ==========================================================================
1630 
InitMsgCache(mCacheList * mList,uint16_t listSize,uint16_t infoSize)1631 void InitMsgCache(
1632     mCacheList* mList,
1633     uint16_t listSize,
1634     uint16_t infoSize)
1635 {
1636     FreeMsgCache(mList, infoSize);
1637     memset(mList, 0, listSize);
1638 }
1639 
FreeMsgCache(mCacheList * mList,uint16_t infoSize)1640 void FreeMsgCache(
1641     mCacheList* mList,
1642     uint16_t infoSize)
1643 {
1644     mCacheInfo* ci = mList->mInfo;
1645     char* ch_ptr;
1646 
1647     while (mList->NumMsgs--) {
1648         delete [] ci->mSeg;
1649         ci->mSeg = nullptr;
1650 
1651         ch_ptr = (char*)ci;
1652         ch_ptr += infoSize;
1653         ci = (mCacheInfo*)ch_ptr;
1654     }
1655 }
1656 
1657 extern char int_xx[];
1658 
1659 // ---------------------------------------------------------------------------
1660 // CacheMsg()
1661 //
1662 // Caches the specific message in FROM a given 'grsegs' block TO the
1663 // next available message segment pointer.
1664 // ---------------------------------------------------------------------------
CacheMsg(mCacheInfo * ci,uint16_t SegNum,uint16_t MsgNum)1665 void CacheMsg(
1666     mCacheInfo* ci,
1667     uint16_t SegNum,
1668     uint16_t MsgNum)
1669 {
1670     ci->mSeg = new char[MAX_CACHE_MSG_LEN];
1671     LoadMsg(ci->mSeg, SegNum, MsgNum, MAX_CACHE_MSG_LEN);
1672 }
1673 
1674 // ---------------------------------------------------------------------------
1675 // LoadMsg()
1676 //
1677 // Loads the specific message in FROM a given 'grsegs' block TO the
1678 // the memory address provided.  Memory allocation and handleing prior and
1679 // after this function usage is responsiblity of the calling function(s).
1680 //
1681 // PARAMS:  hint_buffer - Destination address to store message
1682 //          SegNum - GrSeg for messages in VGAGRAPH.BS?
1683 //          MsgNum - Message number to load
1684 //          MaxMsgLen - Max len of cache msg (Len of hint_buffer)
1685 //
1686 // RETURNS : Returns the length of the loaded message
1687 // ---------------------------------------------------------------------------
LoadMsg(char * hint_buffer,uint16_t SegNum,uint16_t MsgNum,uint16_t MaxMsgLen)1688 int16_t LoadMsg(
1689     char* hint_buffer,
1690     uint16_t SegNum,
1691     uint16_t MsgNum,
1692     uint16_t MaxMsgLen)
1693 {
1694     char* Message, * EndOfMsg;
1695     int16_t pos = 0;
1696 
1697     CA_CacheGrChunk(SegNum);
1698     Message = static_cast<char*>(grsegs[SegNum]);
1699 
1700 // Search for end of MsgNum-1 (Start of our message)
1701 //
1702     while (--MsgNum) {
1703         Message = strstr(Message, int_xx);
1704 
1705         if (!Message) {
1706             ::Quit("Invalid 'Cached Message' number");
1707         }
1708 
1709         Message += 3;           // Bump to start of next Message
1710     }
1711 
1712 // Move past LFs and CRs that follow "^XX"
1713 //
1714     while ((*Message == '\n') || (*Message == '\r')) {
1715         Message++;
1716     }
1717 
1718 // Find the end of the message
1719 //
1720     if ((EndOfMsg = strstr(Message, int_xx)) == nullptr) {
1721         ::Quit("Invalid 'Cached Message' number");
1722     }
1723     EndOfMsg += 3;
1724 
1725 // Copy to a temp buffer
1726 //
1727     while (Message != EndOfMsg) {
1728         if (*Message != '\n') {
1729             hint_buffer[pos++] = *Message;
1730         }
1731 
1732         if (pos >= MaxMsgLen) {
1733             ::Quit("Cached Hint Message is to Long for allocated space.");
1734         }
1735 
1736         Message++;
1737     }
1738 
1739     hint_buffer[pos] = 0; // Null Terminate
1740     UNCACHEGRCHUNK(SegNum);
1741 
1742     return pos;
1743 }
1744 
1745 
1746 /*
1747 =============================================================================
1748 
1749  CONCESSION MACHINES
1750 
1751 =============================================================================
1752 */
1753 
1754 // --------------------------------------------------------------------------
1755 // SpawnConcession()
1756 //
1757 // actorat[][] - Holds concession machine number (1 - MAXCONCESSIONS+1)
1758 // --------------------------------------------------------------------------
SpawnConcession(int16_t tilex,int16_t tiley,uint16_t credits,uint16_t machinetype)1759 void SpawnConcession(
1760     int16_t tilex,
1761     int16_t tiley,
1762     uint16_t credits,
1763     uint16_t machinetype)
1764 {
1765     con_mCacheInfo* ci = &ConHintList.cmInfo[ConHintList.NumMsgs];
1766 
1767     if (ConHintList.NumMsgs >= MAXCONCESSIONS) {
1768         ::Quit("Too many concession machines in level.");
1769     }
1770 
1771     if (credits != PUSHABLETILE) {
1772         switch (credits & 0xff00) {
1773         case 0:
1774         case 0xFC00: // Food Id
1775             ci->mInfo.local_val = credits & 0xff;
1776             ci->operate_cnt = 0;
1777             ci->type = static_cast<uint8_t>(machinetype);
1778             break;
1779         }
1780     }
1781 
1782 // Consider it a solid wall (val != 0)
1783 //
1784     if (++ConHintList.NumMsgs > MAX_CACHE_MSGS) {
1785         ::Quit("(CONCESSIONS) Too many 'cached msgs' loaded.");
1786     }
1787 
1788     actorat[static_cast<int>(tilex)][static_cast<int>(tiley)] = reinterpret_cast<objtype*>(ConHintList.NumMsgs);
1789 }
1790 
ReuseMsg(mCacheInfo * ci,int16_t count,int16_t struct_size)1791 bool ReuseMsg(
1792     mCacheInfo* ci,
1793     int16_t count,
1794     int16_t struct_size)
1795 {
1796     char* scan_ch = (char*)ci;
1797     mCacheInfo* scan = (mCacheInfo*)(scan_ch - struct_size);
1798 
1799 // Scan through all loaded messages -- see if we're loading one already
1800 // cached-in.
1801 //
1802     while (count--) {
1803         // Is this message already cached in?
1804         //
1805         if (scan->global_val == ci->global_val) {
1806             ci->local_val = scan->local_val;
1807             return true;
1808         }
1809 
1810         // Funky structure decrement... (structures can be any size...)
1811         //
1812         scan_ch = (char*)scan;
1813         scan_ch -= struct_size;
1814         scan = (mCacheInfo*)scan_ch;
1815     }
1816 
1817     return false;
1818 }
1819 
1820 
1821 extern std::string food_msg1;
1822 extern std::string bevs_msg1;
1823 
1824 extern void writeTokenStr(
1825     std::string& string);
1826 
1827 const char* const OutOrder = "\r\r   FOOD UNIT MACHINE\r    IS OUT OF ORDER.^XX";
1828 
OperateConcession(uint16_t concession)1829 void OperateConcession(
1830     uint16_t concession)
1831 {
1832     con_mCacheInfo* ci;
1833     bool ok = false;
1834 
1835     ci = &ConHintList.cmInfo[concession - 1];
1836 
1837     switch (ci->type) {
1838     case CT_FOOD:
1839     case CT_BEVS:
1840         if (ci->mInfo.local_val) {
1841             if (gamestate.health == 100) {
1842                 DISPLAY_TIMED_MSG(noeat_msg1, MP_CONCESSION_OPERATE, MT_GENERAL);
1843                 ::sd_play_player_sound(NOWAYSND, bstone::AC_ITEM);
1844 
1845                 return;
1846             } else {
1847                 ok = true;
1848             }
1849         }
1850         break;
1851     }
1852 
1853     if (ok) {
1854         // Whada' ya' want?
1855 
1856         switch (ci->type) {
1857         case CT_FOOD:
1858         case CT_BEVS:
1859             // One token please... Thank you.
1860 
1861             if (!gamestate.tokens) {
1862                 DISPLAY_TIMED_MSG(NoFoodTokens, MP_NO_MORE_TOKENS, MT_NO_MO_FOOD_TOKENS);
1863                 ::sd_play_player_sound(NOWAYSND, bstone::AC_ITEM);
1864 
1865                 return;
1866             } else {
1867                 gamestate.tokens--;
1868             }
1869 
1870             ci->mInfo.local_val--;
1871             ::sd_play_player_sound(CONCESSIONSSND, bstone::AC_ITEM);
1872 
1873             switch (ci->type) {
1874             case CT_FOOD:
1875                 if (::is_ps()) {
1876                     ::writeTokenStr(food_msg1);
1877                 }
1878 
1879                 DISPLAY_TIMED_MSG(food_msg1, MP_CONCESSION_OPERATE, MT_GENERAL);
1880                 HealSelf(10);
1881                 break;
1882 
1883             case CT_BEVS:
1884                 if (::is_ps()) {
1885                     ::writeTokenStr(bevs_msg1);
1886                 }
1887 
1888                 DISPLAY_TIMED_MSG(bevs_msg1, MP_CONCESSION_OPERATE, MT_GENERAL);
1889                 HealSelf(7);
1890                 break;
1891             }
1892             break;
1893         }
1894     } else {
1895         DISPLAY_TIMED_MSG(OutOrder, MP_CONCESSION_OUT_ORDER, MT_GENERAL);
1896         ::sd_play_player_sound(NOWAYSND, bstone::AC_ITEM);
1897     }
1898 }
1899 
1900 int8_t xy_offset[8][2] = {
1901     { 0, -1 }, { 0, +1 }, { -1, 0 }, { +1, 0 }, // vert / horz
1902     { -1, -1 }, { +1, +1 }, { -1, +1 }, { +1, -1 }, // diagnals
1903 };
1904 
CheckSpawnEA()1905 void CheckSpawnEA()
1906 {
1907     objtype temp, * ob;
1908     int8_t loop, ofs, x_diff, y_diff;
1909 
1910     if (objcount > MAXACTORS - 8) {
1911         return;
1912     }
1913 
1914     for (loop = 0; loop < NumEAWalls; loop++) {
1915         uint16_t* map1 = mapsegs[1] + farmapylookup[static_cast<int>(eaList[static_cast<int>(loop)].tiley)] + eaList[static_cast<int>(loop)].tilex;
1916 
1917         // Limit the number of aliens spawned by each outlet.
1918         //
1919         if (eaList[static_cast<int>(loop)].aliens_out > gamestate.difficulty) {
1920             continue;
1921         }
1922 
1923         // Decrement 'spawn delay' for current outlet.
1924         //
1925         if (eaList[static_cast<int>(loop)].delay > tics) {
1926             eaList[static_cast<int>(loop)].delay -= tics;
1927             continue;
1928         }
1929 
1930         // Reset to 1 because it's possible that an alien won't be spawned...
1931         // If NOT, we'll try again on the next refresh.
1932         // If SO, the delay is set to a true value below.
1933         //
1934         eaList[static_cast<int>(loop)].delay = 1;
1935 
1936         // Does this wall touch the 'area' that the player is in?
1937         //
1938         for (ofs = 0; ofs < 4; ofs++) {
1939             int8_t nx = eaList[static_cast<int>(loop)].tilex + xy_offset[static_cast<int>(ofs)][0];
1940             int8_t ny = eaList[static_cast<int>(loop)].tiley + xy_offset[static_cast<int>(ofs)][1];
1941             int8_t areanumber = GetAreaNumber(nx, ny);
1942 
1943             if ((nx < 0) || (nx > 63) || (ny < 0) || (ny > 63)) {
1944                 continue;
1945             }
1946 
1947             if (areanumber != 127 && areabyplayer[static_cast<int>(areanumber)]) {
1948                 break;
1949             }
1950         }
1951 
1952         // Wall doesn't touch player 'area'.
1953         //
1954         if (ofs == 4) {
1955             continue;
1956         }
1957 
1958         // Setup tile x,y in temp obj.
1959         //
1960         temp.tilex = eaList[static_cast<int>(loop)].tilex + xy_offset[static_cast<int>(ofs)][0];
1961         temp.tiley = eaList[static_cast<int>(loop)].tiley + xy_offset[static_cast<int>(ofs)][1];
1962 
1963         // Is another actor already on this tile?
1964         // If so, "continue" if it's alive...
1965         //
1966         ob = actorat[temp.tilex][temp.tiley];
1967         if (ob >= objlist) {
1968             if (!(ob->flags & FL_DEADGUY)) {
1969                 continue;
1970             }
1971         }
1972 
1973         // Is player already on this tile?
1974         //
1975         x_diff = player->tilex - temp.tilex;
1976         y_diff = player->tiley - temp.tiley;
1977         if (ABS(x_diff) < 2 && ABS(y_diff) < 2) {
1978             continue;
1979         }
1980 
1981         // Setup x,y in temp obj and see if obj is in player's view.
1982         // Actor is released if it's in player's view   OR
1983         // a random chance to release whether it can be seen or not.
1984         //
1985         temp.x = ((fixed)temp.tilex << TILESHIFT) + ((fixed)TILEGLOBAL / 2);
1986         temp.y = ((fixed)temp.tiley << TILESHIFT) + ((fixed)TILEGLOBAL / 2);
1987         if ((!CheckSight(player, &temp)) && (US_RndT() < 200)) {
1988             continue;
1989         }
1990 
1991         // Spawn Electro-Alien!
1992         //
1993         usedummy = true;
1994         SpawnStand(en_electro_alien, temp.tilex, temp.tiley, 0);
1995 
1996         ::sd_play_actor_sound(ELECAPPEARSND, new_actor, bstone::AC_ITEM);
1997 
1998         usedummy = false;
1999         if (new_actor != &dummyobj) {
2000             eaList[static_cast<int>(loop)].aliens_out++;
2001             new_actor->temp2 = loop;
2002             ::sd_play_actor_sound(ELECAPPEARSND, new_actor, bstone::AC_ITEM);
2003         }
2004 
2005         // Reset spawn delay.
2006         //
2007         if ((*map1 & 0xff00) == 0xfa00) {
2008             eaList[static_cast<int>(loop)].delay = 60 * ((*map1) & 0xff);
2009         } else {
2010             eaList[static_cast<int>(loop)].delay = 60 * 8 + Random(60 * 22);
2011         }
2012 
2013         break;
2014     }
2015 }
2016 
CheckSpawnGoldstern()2017 void CheckSpawnGoldstern()
2018 {
2019     if (GoldsternInfo.WaitTime > tics) {
2020         //
2021         // Count down general timer before doing any Goldie Stuff..
2022         //
2023 
2024         GoldsternInfo.WaitTime -= tics;
2025     } else {
2026         //
2027         // What Kind of Goldie Stuff needs to be done?
2028         //
2029 
2030         if (GoldsternInfo.flags == GS_COORDFOUND) {
2031             uint16_t tilex, tiley;
2032 
2033             // See if we can spawn Dr. Goldstern...
2034 
2035             tilex = GoldieList[GoldsternInfo.LastIndex].tilex;
2036             tiley = GoldieList[GoldsternInfo.LastIndex].tiley;
2037 
2038             if ((!actorat[tilex][tiley]) && ABS(player->tilex - tilex) > 1 && ABS(player->tiley - tiley) > 1) {
2039                 SpawnStand(en_goldstern, tilex, tiley, 0);
2040                 GoldsternInfo.GoldSpawned = true;
2041             }
2042         } else {
2043             // Find a new coord to spawn Goldie (GS_NEEDCOORD or GS_FIRSTTIME)
2044 
2045             FindNewGoldieSpawnSite();
2046         }
2047     }
2048 }
2049 
FindNewGoldieSpawnSite()2050 void FindNewGoldieSpawnSite()
2051 {
2052     objtype temp;
2053     int8_t loop;
2054 
2055     GoldsternInfo.WaitTime = 0;
2056 
2057     for (loop = 0; loop < GoldsternInfo.SpawnCnt; loop++) {
2058         // Test for repeats - And avoid them!
2059         //
2060 
2061         if ((GoldsternInfo.SpawnCnt > 1) && (loop == GoldsternInfo.LastIndex)) {
2062             continue;
2063         }
2064 
2065         // Setup tile x,y in temp obj.
2066         //
2067 
2068         temp.tilex = GoldieList[static_cast<int>(loop)].tilex;
2069         temp.tiley = GoldieList[static_cast<int>(loop)].tiley;
2070 
2071         // Setup x,y in temp obj and see if obj is in player's view.
2072         //
2073 
2074         temp.x = ((fixed)temp.tilex << TILESHIFT) + ((fixed)TILEGLOBAL / 2);
2075         temp.y = ((fixed)temp.tiley << TILESHIFT) + ((fixed)TILEGLOBAL / 2);
2076         if (!CheckSight(player, &temp)) {
2077             continue;
2078         }
2079 
2080         // Mark to spawn Dr Goldstern
2081         //
2082 
2083         GoldsternInfo.LastIndex = loop;
2084         if (gamestate.mapon == 9) {
2085             GoldsternInfo.WaitTime = 60;
2086         } else if (GoldsternInfo.flags == GS_FIRSTTIME) {
2087             GoldsternInfo.WaitTime = MIN_GOLDIE_FIRST_WAIT + Random(MAX_GOLDIE_FIRST_WAIT - MIN_GOLDIE_FIRST_WAIT); // Reinit Delay Timer before spawning on new position
2088         } else {
2089             GoldsternInfo.WaitTime = MIN_GOLDIE_WAIT + Random(MAX_GOLDIE_WAIT - MIN_GOLDIE_WAIT); // Reinit Delay Timer before spawning on new position
2090 
2091         }
2092         GoldsternInfo.flags = GS_COORDFOUND;
2093         break;
2094     }
2095 }
2096