1 /*
2 BStone: A Source port of
3 Blake Stone: Aliens of Gold and Blake Stone: Planet Strike
4 
5 Copyright (c) 1992-2013 Apogee Entertainment, LLC
6 Copyright (c) 2013-2015 Boris I. Bendovsky (bibendovsky@hotmail.com)
7 
8 This program is free software; you can redistribute it and/or
9 modify it under the terms of the GNU General Public License
10 as published by the Free Software Foundation; either version 2
11 of the License, or (at your option) any later version.
12 
13 This program is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16 GNU General Public License for more details.
17 
18 You should have received a copy of the GNU General Public License
19 along with this program; if not, write to the
20 Free Software Foundation, Inc.,
21 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
22 */
23 
24 
25 #include "3d_def.h"
26 
27 
28 void FirstSighting(
29     objtype* ob);
30 
31 bool SightPlayer(
32     objtype* ob);
33 
34 void TakeDamage(
35     int16_t points,
36     objtype* attacker);
37 
38 void OpenDoor(
39     int16_t door);
40 
41 bool CheckView(
42     objtype* from_obj,
43     objtype* to_obj);
44 
45 int16_t CalcAngle(
46     objtype* from_obj,
47     objtype* to_obj);
48 
49 bool ClipMove(
50     objtype* ob,
51     int32_t xmove,
52     int32_t ymove);
53 
54 
55 /*
56 =============================================================================
57 
58  LOCAL CONSTANTS
59 
60 =============================================================================
61 */
62 
63 
64 #define BFG_SHOT_STOPS (1)
65 
66 #define SPDPATROL (512)
67 #define SPDPROJ (7168)
68 #define PROJCHECKSIZE (0x8000L) // scan for actor range
69 #define PROJWALLSIZE (0x2000L) // collision with wall range
70 
71 #define PROJECTILESIZE (0xC000L)
72 
73 #define SEEK_TURN_DELAY (30) // tics
74 
75 #define CLOSE_RANGE (1) // Tiles
76 
77 #define ALIENSPEED (SPDPATROL)
78 #define ALIENAMMOINIT (30 + (::US_RndT() % 60))
79 
80 #define GR_DAMAGE (40 + (::US_RndT() & 0x7F)) // 20 & ... 0x3F
81 #define BFG_DAMAGE (GR_DAMAGE << 1) // Hit Point damage cause by BFG
82 #define PLASMA_DETONATOR_DAMAGE (500)
83 #define DETONATOR_FLASH_RATE (20)
84 
85 #define EXPLODE_DAMAGE (20) // 5
86 
87 #define VPOST_BARRIER_SPEED (7) // Tics per anim step
88 #define VPOST_WAIT_DELAY (90) // Tics delay in cycling
89 
90 // Min number of statics avail
91 // before 50/50 chance of door rubble on exp. doors.
92 #define DR_MIN_STATICS (50)
93 
94 
95 /*
96 =============================================================================
97 
98  GLOBAL VARIABLES
99 
100 =============================================================================
101 */
102 
103 int8_t detonators_spawned = 0;
104 
105 
106 /*
107 =============================================================================
108 
109  LOCAL VARIABLES
110 
111 =============================================================================
112 */
113 
114 int16_t starthitpoints[4][NUMHITENEMIES] = {
115     //
116     // BABY MODE
117     //
118     {
119         4, // Rent-A-Cop
120         4, // Hang Terrot
121         4, // general scientist
122         22, // pod alien
123         13, // Electro-Alien
124         1, // Electro-Sphere
125         33, // ProGuard
126         20, // Genetic Guard
127         24, // Mutant Human Type 1
128         24, // Mutant Human Type 2
129         1, // Large Canister Alien - CONTAINER HP
130         34, // Large Can-Alien - ALIEN HP
131         1, // Small Canister Alien - CONTAINER HP
132         25, // Small Can-Alien - ALIEN HP
133         1, // Gurney Mutant - Waiting HP
134         27, // Gurney Mutant - Spawned HP
135         30, // Liquid Alien -
136         78, // Swat Guards
137         90, // Goldstern
138         4000, // Morphed Goldstern
139         17, // Volatile Material Transport
140         15, // Floating Bomb
141         175, // Vital Defence Obj  - DON'T CHANGE
142         60, // spider_mutant
143         65, // breather_beast
144         70, // cyborg_warror
145         65, // reptilian_warrior
146         60, // acid_dragon
147         65, // mech_guardian
148         1600, // final_boss 1
149         1700, // final_boss 2
150         1800, // final_boss 3
151         1900, // final_boss 4
152         1, // Crate 1
153         1, // Crate 2
154         1, // Crate 3
155         1, // Pod egg
156         1, // morphing_spider_mutant
157         1, // morphing_reptilian_warrior
158         1, // morphing_Mutant Human Type 2
159     },
160 
161     //
162     // DON'T HURT ME MODE
163     //
164 
165     {
166         9, // Rent-A-Cop
167         9, // Hang Terrot
168         9, // general scientist
169         60, // pod alien
170         37, // Electro-Alien
171         1, // Electro-Sphere
172         63, // ProGuard
173         60, // Genetic Guard
174         50, // Mutant Human Type 1
175         50, // Mutant Human Type 2
176         1, // Large Canister Alien - CONTAINER HP
177         75, // Large Can-Alien - ALIEN HP
178         1, // Small Canister Alien - CONTAINER HP
179         60, // Small Can-Alien - ALIEN HP
180         1, // Gurney Mutant - Waiting HP
181         37, // Gurney Mutant - Spawned HP
182         66, // Liquid Alien -
183         112, // SWAT Guards
184         100, // Goldstern
185         4500, // Morphed Goldstern
186         25, // Volatile Material Transport
187         40, // Floating Bomb
188         175, // Vital Defence Obj  - DON'T CHANGE
189         100, // spider_mutant
190         115, // breather_beast
191         100, // cyborg_warror
192         115, // reptilian_warrior
193         100, // acid_dragon
194         115, // mech_guardian
195         1700, // final_boss 1
196         1800, // final_boss 2
197         1900, // final_boss 3
198         2000, // final_boss 4
199         1, // Crate 1
200         1, // Crate 2
201         1, // Crate 3
202         1, // Pod egg
203         1, // morphing_spider_mutant
204         1, // morphing_reptilian_warrior
205         1, // morphing_Mutant Human Type 2
206     },
207 
208     //
209     // BRING 'EM ON MODE
210     //
211 
212     {
213         25, // Rent-A-Cop
214         23, // Hang Terrot
215         23, // general scientist
216         160, // pod alien
217         112, // Electro-Alien
218         1, // Electro-Sphere
219         150, // ProGuard
220         180, // Genetic Guard
221         155, // Mutant Human Type 1
222         155, // Mutant Human Type 2
223         1, // Large Canister Alien - CONTAINER HP
224         225, // Large Can-Alien - ALIEN HP
225         1, // Small Canister Alien - CONTAINER HP
226         180, // Small Can-Alien - ALIEN HP
227         1, // Gurney Mutant - Waiting HP
228         150, // Gurney Mutant - Spawned HP
229         163, // Liquid Alien -
230         325, // SWAT Guards
231         150, // Goldstern
232         4800, // Morphed Goldstern
233         63, // Volatile Material Transport
234         60, // Floating Bomb
235         175, // Vital Defence Obj  - DON'T CHANGE
236         150, // spider_mutant
237         165, // breather_beast
238         150, // cyborg_warror
239         165, // reptilian_warrior
240         150, // acid_dragon
241         165, // mech_guardian
242         1800, // final_boss 1
243         1900, // final_boss 2
244         2000, // final_boss 3
245         2100, // final_boss 4
246         1, // Crate 1
247         1, // Crate 2
248         1, // Crate 3
249         1, // Pod egg
250         1, // morphing_spider_mutant
251         1, // morphing_reptilian_warrior
252         1, // morphing_Mutant Human Type 2
253     },
254 
255     //
256     // DEATH INCARNATE MODE
257     //
258 
259     {
260         38, // Rent-A-Cop
261         28, // Hang Terrot
262         28, // general scientist
263         210, // pod alien
264         150, // Electro-Alien
265         1, // Electro-Sphere
266         175, // ProGuard
267         240, // Genetic Guard
268         210, // Mutant Human Type 1
269         210, // Mutant Human Type 2
270         1, // Large Canister Alien - CONTAINER HP
271         300, // Large Can-Alien - ALIEN HP
272         1, // Small Canister Alien - CONTAINER HP
273         240, // Small Can-Alien - ALIEN HP
274         1, // Gurney Mutant - Waiting HP
275         200, // Gurney Mutant - Spawned HP
276         210, // Liquid Alien -
277         425, // SWAT Gaurds
278         250, // Goldstern
279         5400, // Morphed Goldstern
280         75, // Volatile Material Transport
281         85, // Floating Bomb
282         175, // Vital Defence Obj - DON'T CHANGE
283         200, // spider_mutant
284         225, // breather_beast
285         200, // cyborg_warror
286         225, // reptilian_warrior
287         200, // acid_dragon
288         225, // mech_guardian
289         1900, // final_boss 1
290         2000, // final_boss 2
291         2100, // final_boss 3
292         2200, // final_boss 4
293         1, // Crate 1
294         1, // Crate 2
295         1, // Crate 3
296         1, // Pod egg
297         1, // morphing_spider_mutant
298         1, // morphing_reptilian_warrior
299         1, // morphing_Mutant Human Type 2
300     },
301 };
302 
303 
304 using BossIds = std::vector<int16_t>;
305 
306 BossIds BossShotShapes;
307 BossIds BossShapes;
308 BossIds MorphShapes;
309 BossIds MorphEndShapes;
310 BossIds MorphSounds;
311 
312 uint16_t MorphClass[] = {
313     spider_mutantobj,
314     reptilian_warriorobj,
315     mutant_human2obj,
316 };
317 
318 
initialize_boss_constants()319 void initialize_boss_constants()
320 {
321     BossShotShapes = {
322         SPR_BOSS1_PROJ1,
323         0,
324         0,
325         0,
326         SPR_BOSS5_PROJ1,
327         0,
328         0,
329         ::is_ps() ? SPR_BOSS10_SPIT1 : static_cast<int16_t>(0),
330     };
331 
332     BossShapes = {
333         SPR_BOSS1_W1,
334         ::is_aog_sw() ? static_cast<int16_t>(0) : SPR_BOSS2_W1,
335         ::is_aog_sw() ? static_cast<int16_t>(0) : SPR_BOSS3_W1,
336         ::is_aog_sw() ? static_cast<int16_t>(0) : SPR_BOSS4_W1,
337         ::is_aog_sw() ? static_cast<int16_t>(0) : SPR_BOSS5_W1,
338         ::is_aog_sw() ? static_cast<int16_t>(0) : SPR_BOSS6_W1,
339         ::is_ps() ? SPR_BOSS7_W1 : static_cast<int16_t>(0),
340         ::is_ps() ? SPR_BOSS8_W1 : static_cast<int16_t>(0),
341         ::is_ps() ? SPR_BOSS9_W1 : static_cast<int16_t>(0),
342         ::is_ps() ? SPR_BOSS10_W1 : static_cast<int16_t>(0),
343     };
344 
345     MorphShapes = {
346         SPR_BOSS1_MORPH1,
347         SPR_BOSS4_MORPH1,
348         SPR_MUTHUM2_MORPH1,
349     };
350 
351     MorphEndShapes = {
352         SPR_BOSS1_W1,
353         ::is_aog_sw() ? static_cast<int16_t>(0) : SPR_BOSS4_W1,
354         SPR_MUTHUM2_W1,
355     };
356 
357     MorphSounds = {
358         SCANHALTSND,
359         GGUARDHALTSND,
360         DOGBOYHALTSND,
361     };
362 }
363 
364 
365 uint16_t bars_connected = 0;
366 
367 uint16_t SpecialSpawnFlags[] = {
368     FL2_DROP_RKEY,
369     FL2_DROP_YKEY,
370     FL2_DROP_BKEY,
371     FL2_DROP_BFG,
372     FL2_DROP_ION,
373     FL2_DROP_DETONATOR,
374 };
375 
376 void T_Path(
377     objtype* ob);
378 
379 void T_Shoot(
380     objtype* ob);
381 
382 void T_Shade(
383     objtype* obj);
384 
385 void T_Chase(
386     objtype* ob);
387 
388 void T_Projectile(
389     objtype* ob);
390 
391 void T_Stand(
392     objtype* ob);
393 
394 
395 void T_OfsThink(
396     objtype* obj);
397 
398 void T_OfsChase(
399     objtype* obj);
400 
401 void A_DeathScream(
402     objtype* ob);
403 
404 int16_t RandomSphereDir(
405     enemy_t dir);
406 
407 void CheckForcedMove(
408     objtype* ob);
409 
410 void T_SwatWound(
411     objtype* ob);
412 
413 void T_SpaceShip(
414     objtype* ob);
415 
416 void T_BlowBack(
417     objtype* obj);
418 
419 void DoAttack(
420     objtype* ob);
421 
422 bool MoveTrappedDiag(
423     objtype* ob);
424 
425 bool CheckTrappedDiag(
426     objtype* ob);
427 
428 void ChangeShootMode(
429     objtype* ob);
430 
431 
get_remaining_generators()432 static int get_remaining_generators()
433 {
434     int remaining_generators = 0;
435 
436     for (objtype* o = player->next; o; o = o->next) {
437         if (o->obclass != rotating_cubeobj) {
438             continue;
439         }
440 
441         if ((o->flags & FL_SHOOTABLE) == 0) {
442             continue;
443         }
444 
445         remaining_generators += 1;
446     }
447 
448     return remaining_generators;
449 }
450 
display_remaining_generators()451 static void display_remaining_generators()
452 {
453     static const std::string message_part_1 =
454         "^FC57 PROJECTION GENERATOR\r"
455         "      DESTROYED!\r"
456         "\r"
457         "^FCA6   - "
458     ;
459 
460     static const std::string message_part_2 =
461         " REMAINING -\r"
462         " DESTROY THEM TO WIN!"
463     ;
464 
465 
466     int remaining_generators = ::get_remaining_generators();
467 
468 
469     static std::string message;
470 
471     message =
472         message_part_1 +
473         bstone::StringHelper::lexical_cast<std::string>(remaining_generators) +
474         message_part_2;
475 
476     DISPLAY_TIMED_MSG(message.c_str(), MP_FLOOR_UNLOCKED, MT_GENERAL);
477 }
478 
479 
480 // ===========================================================================
481 //
482 // OFFSET OBJECT ROUTINES
483 //
484 // * NOTES: OffsetObjects require the use of TEMP1 of the object structure!
485 //
486 // * NOTES: THINK function AnimateOfsObj() requires the use of TEMP3 of the
487 //                              object structure!
488 //
489 // ===========================================================================
490 
491 //
492 // Local Externs
493 //
494 extern statetype s_ofs_stand;
495 
496 extern statetype s_ofs_chase1;
497 extern statetype s_ofs_chase1s;
498 extern statetype s_ofs_chase2;
499 extern statetype s_ofs_chase3;
500 extern statetype s_ofs_chase3s;
501 extern statetype s_ofs_chase4;
502 
503 extern statetype s_ofs_pain;
504 
505 extern statetype s_ofs_die1;
506 extern statetype s_ofs_die1s;
507 extern statetype s_ofs_die2;
508 extern statetype s_ofs_die3;
509 extern statetype s_ofs_die4;
510 extern statetype s_ofs_die5;
511 
512 extern statetype s_ofs_attack1;
513 extern statetype s_ofs_attack2;
514 extern statetype s_ofs_attack3;
515 
516 
517 extern statetype s_ofs_spit1;
518 extern statetype s_ofs_spit2;
519 extern statetype s_ofs_spit3;
520 
521 extern statetype s_ofs_shoot1;
522 extern statetype s_ofs_shoot2;
523 extern statetype s_ofs_shoot3;
524 extern statetype s_ofs_shoot4;
525 extern statetype s_ofs_shoot5;
526 extern statetype s_ofs_shoot6;
527 extern statetype s_ofs_shoot7;
528 
529 extern statetype s_ofs_smart_anim;
530 extern statetype s_ofs_smart_anim2;
531 
532 extern statetype s_ofs_projectile1;
533 extern statetype s_ofs_projectile2;
534 
535 extern statetype s_ofs_proj_exp1;
536 extern statetype s_ofs_proj_exp2;
537 
538 extern statetype s_ofs_pod_spit1;
539 extern statetype s_ofs_pod_spit2;
540 extern statetype s_ofs_pod_spit3;
541 
542 extern statetype s_ofs_pod_attack1;
543 extern statetype s_ofs_pod_attack1a;
544 extern statetype s_ofs_pod_attack2;
545 
546 extern statetype s_ofs_pod_death1;
547 extern statetype s_ofs_pod_death2;
548 extern statetype s_ofs_pod_death3;
549 
550 extern statetype s_ofs_roam;
551 
552 extern statetype s_ofs_esphere_death1;
553 extern statetype s_ofs_esphere_death2;
554 extern statetype s_ofs_esphere_death3;
555 
556 
557 
558 //
559 // Local Prototypes
560 //
561 
562 void T_SmartThink(
563     objtype* obj);
564 
565 void T_SmartThought(
566     objtype* obj);
567 
568 void T_Action(
569     objtype* obj);
570 
571 bool ProjectileTryMove(
572     objtype* ob,
573     fixed deltax,
574     fixed deltay);
575 
576 void T_Projectile(
577     objtype* ob);
578 
579 void T_OfsShoot(
580     enemy_t which,
581     objtype* ob);
582 
583 void SphereStartDir(
584     objtype* ob);
585 
586 void T_OfsBounce(
587     objtype* obj);
588 
589 //
590 // Local Offset Anim Structures
591 //
592 statetype s_ofs_stand = { 0, SPR_DEMO, 0, T_OfsThink, nullptr, &s_ofs_stand };
593 
594 statetype s_ofs_chase1 = { 0, SPR_DEMO, 10, T_Chase, nullptr, &s_ofs_chase1s };
595 statetype s_ofs_chase1s = { 0, SPR_DEMO, 5, nullptr, nullptr, &s_ofs_chase2 };
596 statetype s_ofs_chase2 = { 0, SPR_DEMO, 8, T_Chase, nullptr, &s_ofs_chase3 };
597 statetype s_ofs_chase3 = { 0, SPR_DEMO, 10, T_Chase, nullptr, &s_ofs_chase3s };
598 statetype s_ofs_chase3s = { 0, SPR_DEMO, 5, nullptr, nullptr, &s_ofs_chase4 };
599 statetype s_ofs_chase4 = { 0, SPR_DEMO, 8, T_Chase, nullptr, &s_ofs_chase1 };
600 
601 statetype s_ofs_pain = { 0, SPR_DEMO, 15, nullptr, nullptr, &s_ofs_chase1 };
602 
603 statetype s_ofs_die1 = { 0, SPR_DEMO, 18, T_BlowBack, A_DeathScream, &s_ofs_die1s };
604 statetype s_ofs_die1s = { 0, SPR_DEMO, 20, T_BlowBack, nullptr, &s_ofs_die2 };
605 statetype s_ofs_die2 = { 0, SPR_DEMO, 22, T_BlowBack, nullptr, &s_ofs_die3 };
606 statetype s_ofs_die3 = { 0, SPR_DEMO, 20, T_BlowBack, nullptr, &s_ofs_die4 };
607 statetype s_ofs_die4 = { 0, SPR_DEMO, 18, T_BlowBack, nullptr, &s_ofs_die5 };
608 statetype s_ofs_die5 = { 0, SPR_DEMO, 0, nullptr, nullptr, &s_ofs_die5 };
609 
610 statetype s_ofs_attack1 = { 0, SPR_DEMO, 20, nullptr, nullptr, &s_ofs_attack2 };
611 statetype s_ofs_attack2 = { 0, SPR_DEMO, 20, nullptr, T_Hit, &s_ofs_attack3 };
612 statetype s_ofs_attack3 = { 0, SPR_DEMO, 30, nullptr, nullptr, &s_ofs_chase1 };
613 
614 statetype s_ofs_spit1 = { 0, SPR_DEMO, 20, nullptr, nullptr, &s_ofs_spit2 };
615 statetype s_ofs_spit2 = { 0, SPR_DEMO, 20, nullptr, T_Shoot, &s_ofs_spit3 };
616 statetype s_ofs_spit3 = { 0, SPR_DEMO, 10, nullptr, T_Shade, &s_ofs_chase1 };
617 
618 statetype s_ofs_shoot1 = { 0, SPR_DEMO, 10, nullptr, nullptr, &s_ofs_shoot2 };
619 statetype s_ofs_shoot2 = { 0, SPR_DEMO, 10, nullptr, T_Shoot, &s_ofs_shoot3 };
620 statetype s_ofs_shoot3 = { 0, SPR_DEMO, 10, nullptr, T_Shade, &s_ofs_chase1 };
621 
622 // General 'offset-object' states...
623 //
624 
625 statetype s_ofs_pod_attack1 = { 0, SPR_DEMO, 8, nullptr, nullptr, &s_ofs_pod_attack1a };
626 statetype s_ofs_pod_attack1a = { 0, SPR_DEMO, 8, nullptr, nullptr, &s_ofs_attack2 };
627 statetype s_ofs_pod_attack2 = { 0, SPR_DEMO, 8, nullptr, T_Hit, &s_ofs_chase1 };
628 
629 statetype s_ofs_pod_spit1 = { 0, SPR_DEMO, 8, nullptr, nullptr, &s_ofs_pod_spit2 };
630 statetype s_ofs_pod_spit2 = { 0, SPR_DEMO, 8, nullptr, nullptr, &s_ofs_pod_spit3 };
631 statetype s_ofs_pod_spit3 = { 0, SPR_DEMO, 8, nullptr, T_Shoot, &s_ofs_chase1 };
632 
633 statetype s_ofs_pod_death1 = { 0, 0, 60, nullptr, nullptr, &s_ofs_pod_death2 };
634 statetype s_ofs_pod_death2 = { 0, 1, 30, nullptr, nullptr, &s_ofs_pod_death3 };
635 statetype s_ofs_pod_death3 = { 0, 2, 0, nullptr, nullptr, &s_ofs_pod_death3 };
636 
637 statetype s_ofs_pod_ouch = { 0, SPR_DEMO, 11, nullptr, nullptr, &s_ofs_chase1 };
638 
639 statetype s_ofs_bounce = { 0, 0, 10, T_OfsBounce, T_OfsThink, &s_ofs_bounce };
640 
641 statetype s_ofs_ouch = { 0, 0, 15, T_OfsBounce, nullptr, &s_ofs_bounce };
642 
643 statetype s_ofs_esphere_death1 = { 0, 0, 15, nullptr, A_DeathScream, &s_ofs_esphere_death2 };
644 statetype s_ofs_esphere_death2 = { 0, 1, 15, nullptr, nullptr, &s_ofs_esphere_death3 };
645 statetype s_ofs_esphere_death3 = { 0, 2, 15, nullptr, nullptr, nullptr };
646 
647 statetype s_ofs_random = { 0, 0, 1, T_OfsThink, nullptr, &s_ofs_random };
648 
649 statetype s_ofs_static = { 0, 0, 1, nullptr, nullptr, &s_ofs_static };
650 
651 statetype s_hold = { 0, 0, 1, nullptr, nullptr, &s_hold };
652 
653 uint16_t scan_value;
654 
655 
SpawnOffsetObj(enemy_t which,int16_t tilex,int16_t tiley)656 void SpawnOffsetObj(
657     enemy_t which,
658     int16_t tilex,
659     int16_t tiley)
660 {
661     enemy_t dir_which = en_rentacop;
662 
663     switch (which) {
664     case en_vertsphere:
665     case en_horzsphere:
666     case en_diagsphere:
667         dir_which = which;
668         which = en_electrosphere;
669         break;
670 
671     case en_bloodvent:
672     case en_watervent:
673         dir_which = which;
674         which = en_ventdrip;
675 
676     default:
677         break;
678     }
679 
680     SpawnNewObj(tilex, tiley, &s_ofs_stand);
681     new_actor->flags |= FL_SHOOTABLE | FL_SOLID | FL_OFFSET_STATES;
682     new_actor->obclass = static_cast<classtype>(rentacopobj + which);
683 
684     switch (which) {
685     case en_final_boss2:
686         new_actor->lighting = NO_SHADING;
687     case en_final_boss1:
688     case en_final_boss3:
689     case en_final_boss4:
690     case en_spider_mutant:
691     case en_breather_beast:
692     case en_cyborg_warrior:
693     case en_reptilian_warrior:
694     case en_acid_dragon:
695     case en_mech_guardian:
696         new_actor->temp1 = static_cast<int16_t>(BossShapes[which - en_spider_mutant]);
697         new_actor->speed = ALIENSPEED;
698         new_actor->ammo = ALIENAMMOINIT;
699         new_actor->flags |= FL_PROJ_TRANSPARENT;
700 
701         new_actor->flags2 =
702             (::is_ps() ? FL2_BFG_SHOOTABLE | FL2_BFGSHOT_SOLID : 0);
703         break;
704 
705     case en_green_ooze:
706         ::InitSmartSpeedAnim(
707             ::new_actor,
708             ::SPR_GREEN_OOZE1,
709             ::US_RndT() % 3,
710             2,
711             at_CYCLE,
712             ad_FWD,
713             (::is_ps() ? 5 + (::US_RndT() & 2) : 30));
714 
715         new_actor->flags &= ~(FL_SHOOTABLE | FL_SOLID);
716         break;
717 
718     case en_black_ooze:
719         ::InitSmartSpeedAnim(
720             ::new_actor,
721             ::SPR_BLACK_OOZE1,
722             ::US_RndT() % 3,
723             2,
724             at_CYCLE,
725             ad_FWD,
726             (::is_ps() ? 5 + (::US_RndT() & 2) : 30));
727 
728         new_actor->flags &= ~(FL_SHOOTABLE | FL_SOLID);
729         break;
730 
731     case en_green2_ooze:
732         ::InitSmartSpeedAnim(new_actor, SPR_GREEN2_OOZE1, US_RndT() % 3, 2, at_CYCLE, ad_FWD, 5 + (US_RndT() & 2));
733         new_actor->flags &= ~(FL_SHOOTABLE | FL_SOLID);
734         break;
735 
736     case en_black2_ooze:
737         ::InitSmartSpeedAnim(new_actor, SPR_BLACK2_OOZE1, US_RndT() % 3, 2, at_CYCLE, ad_FWD, 5 + (US_RndT() & 2));
738         new_actor->flags &= ~(FL_SHOOTABLE | FL_SOLID);
739         break;
740 
741     case en_crate1:
742     case en_crate2:
743     case en_crate3:
744         new_actor->temp1 = static_cast<int16_t>(SPR_CRATE_1 + which - en_crate1);
745         new_actor->flags |= FL_NO_SLIDE | FL_FAKE_STATIC;
746 
747         // NOTE : TEMP2 is modified in ScanInfoPlane to determine if
748         //                       this object is a "blastable" or not.
749         break;
750 
751     case en_rotating_cube:
752         if (!::is_ps()) {
753             ::InitSmartSpeedAnim(new_actor, SPR_VITAL_STAND, 0, 0, at_NONE, ad_FWD, 0);
754         } else  {
755             ::InitSmartSpeedAnim(new_actor, SPR_CUBE1, 0, 9, at_CYCLE, ad_FWD, 5);
756             new_actor->flags2 = FL2_BFGSHOT_SOLID;
757         }
758         new_actor->lighting = LAMP_ON_SHADING;
759         break;
760 
761     case en_ventdrip:
762         if (dir_which == en_bloodvent) {
763             new_actor->temp1 = SPR_BLOOD_DRIP1;
764         } else {
765             new_actor->temp1 = SPR_WATER_DRIP1;
766         }
767         new_actor->temp2 = 5 + (US_RndT() % 10);
768         new_actor->temp3 = 0;
769         NewState(new_actor, &s_ofs_random);
770         new_actor->flags &= ~(FL_SHOOTABLE | FL_SOLID);
771         break;
772 
773     case en_plasma_detonator:
774     case en_plasma_detonator_reserve:
775         NewState(new_actor, &s_ofs_random);
776         new_actor->temp1 = SPR_DOORBOMB;
777         new_actor->temp3 = PLASMA_DETONATORS_DELAY;
778         new_actor->flags &= ~(FL_SOLID | FL_SHOOTABLE);
779         if (detonators_spawned++) {
780             ::Quit("Too many Fission/Plasma Detonators are placed in this map! You can only have one!");
781         }
782         break;
783 
784     case en_flickerlight:
785         new_actor->temp1 = SPR_DECO_ARC_1;
786         new_actor->temp2 = (2 + (US_RndT() % 3)) * 60;
787         NewState(new_actor, &s_ofs_random);
788         new_actor->flags |= FL_NONMARK | FL_NEVERMARK;
789         new_actor->flags &= ~(FL_SOLID | FL_SHOOTABLE);
790         new_actor->lighting = LAMP_ON_SHADING;
791         break;
792 
793     case en_security_light:
794         new_actor->flags &= ~(FL_SOLID | FL_SHOOTABLE); // ick - this is stupid...
795         NewState(new_actor, &s_security_light);
796         break;
797 
798     case en_electrosphere:
799         new_actor->trydir = static_cast<dirtype>(dir_which);
800         new_actor->flags &= ~FL_SOLID;
801         new_actor->temp1 = SPR_ELECTRO_SPHERE_ROAM1;
802         new_actor->speed = 3096;
803         new_actor->lighting = NO_SHADING; // NO shading
804         NewState(new_actor, &s_ofs_bounce);
805         SphereStartDir(new_actor);
806         break;
807 
808     case en_podegg:
809         new_actor->temp1 = SPR_POD_EGG;
810         if (scan_value == 0xffff) {
811             new_actor->temp2 = 60 * 5 + (60 * (US_RndT() % 20));
812         } else {
813             new_actor->temp2 = scan_value * 60;
814         }
815         new_actor->speed = ALIENSPEED;
816         new_actor->ammo = ALIENAMMOINIT;
817         if (new_actor->temp2 == 0xff * 60) {
818             new_actor->flags &= ~FL_SHOOTABLE;
819         }
820         new_actor->flags |= FL_PROJ_TRANSPARENT | FL_NO_SLIDE | FL_FAKE_STATIC;
821 
822         new_actor->flags2 =
823             (::is_ps() ? FL2_BFGSHOT_SOLID | FL2_BFG_SHOOTABLE : 0);
824         break;
825 
826     case en_pod:
827         new_actor->temp1 = SPR_POD_WALK1;
828         new_actor->speed = SPDPATROL;
829         new_actor->ammo = static_cast<uint8_t>(-1);
830         new_actor->flags |= FL_PROJ_TRANSPARENT | FL_NO_SLIDE;
831         new_actor->flags2 = (::is_ps() ? FL2_BFG_SHOOTABLE : 0);
832         break;
833 
834     case en_genetic_guard:
835         new_actor->temp1 = SPR_GENETIC_W1;
836         new_actor->speed = ALIENSPEED;
837         new_actor->ammo = ALIENAMMOINIT;
838         new_actor->flags |= FL_PROJ_TRANSPARENT | FL_NO_SLIDE;
839         new_actor->flags2 = (::is_ps() ? FL2_BFG_SHOOTABLE : 0);
840         break;
841 
842     case en_mutant_human1:
843         new_actor->temp1 = SPR_MUTHUM1_W1;
844         new_actor->speed = ALIENSPEED;
845         new_actor->ammo = ALIENAMMOINIT;
846         new_actor->flags |= FL_PROJ_TRANSPARENT | FL_NO_SLIDE;
847 
848         new_actor->flags2 =
849             (::is_ps() ? FL2_BFGSHOT_SOLID | FL2_BFG_SHOOTABLE : 0);
850         break;
851 
852     case en_mutant_human2:
853         new_actor->temp1 = SPR_MUTHUM2_W1;
854         new_actor->speed = ALIENSPEED;
855         new_actor->ammo = ALIENAMMOINIT;
856         new_actor->flags |= FL_PROJ_TRANSPARENT | FL_NO_SLIDE;
857 
858         new_actor->flags2 =
859             (::is_ps() ? FL2_BFGSHOT_SOLID | FL2_BFG_SHOOTABLE : 0);
860         break;
861 
862     case en_scan_wait_alien:
863         new_actor->temp1 = SPR_SCAN_ALIEN_READY;
864         new_actor->flags |= FL_STATIONARY | FL_NO_SLIDE | FL_FAKE_STATIC;
865 
866         new_actor->flags2 =
867             (::is_ps() ? FL2_BFGSHOT_SOLID | FL2_BFG_SHOOTABLE : 0);
868         break;
869 
870     case en_lcan_wait_alien:
871         new_actor->temp1 = SPR_LCAN_ALIEN_READY;
872         new_actor->flags |= FL_STATIONARY | FL_NO_SLIDE | FL_FAKE_STATIC;
873 
874         new_actor->flags2 =
875             (::is_ps() ? FL2_BFGSHOT_SOLID | FL2_BFG_SHOOTABLE : 0);
876         break;
877 
878     case en_morphing_spider_mutant:
879     case en_morphing_reptilian_warrior:
880     case en_morphing_mutanthuman2:
881         if (scan_value == 0xffff) {
882             new_actor->temp2 = -1; // set to max! // 60*5+(60*(US_RndT()%20));
883         } else {
884             new_actor->temp2 = scan_value * 60;
885         }
886 
887         if (new_actor->temp2 == 0xff * 60) {
888             new_actor->flags &= ~FL_SHOOTABLE;
889         }
890 
891         new_actor->speed = ALIENSPEED;
892         new_actor->ammo = ALIENAMMOINIT;
893         new_actor->temp1 = MorphShapes[which - en_morphing_spider_mutant];
894         new_actor->flags |= FL_FAKE_STATIC;
895         new_actor->flags2 = FL2_BFGSHOT_SOLID | FL2_BFG_SHOOTABLE;
896         NewState(new_actor, &s_ofs_random);
897         break;
898 
899     case en_gurney_wait:
900         if (scan_value == 0xffff) {
901             new_actor->temp3 = 0;
902         } else {
903             new_actor->temp3 = (scan_value & 0xff) * 60;
904         }
905         new_actor->temp1 = SPR_GURNEY_MUT_READY;
906         new_actor->flags |= FL_STATIONARY | FL_PROJ_TRANSPARENT | FL_NO_SLIDE | FL_FAKE_STATIC;
907 
908         new_actor->flags2 =
909             (::is_ps() ? FL2_BFGSHOT_SOLID | FL2_BFG_SHOOTABLE : 0);
910         break;
911 
912     case en_lcan_alien: // Large Canister Alien - Out of can.
913         new_actor->temp1 = SPR_LCAN_ALIEN_W1;
914         new_actor->speed = ALIENSPEED;
915         new_actor->ammo = ALIENAMMOINIT;
916         new_actor->flags |= FL_PROJ_TRANSPARENT | FL_NO_SLIDE;
917         new_actor->flags2 = (::is_ps() ? FL2_BFG_SHOOTABLE : 0);
918         break;
919 
920     case en_scan_alien: // Small Canister Alien - Out of can.
921         new_actor->temp1 = SPR_SCAN_ALIEN_W1;
922         new_actor->speed = ALIENSPEED;
923         new_actor->ammo = ALIENAMMOINIT;
924         new_actor->flags |= FL_PROJ_TRANSPARENT;
925         new_actor->flags2 = (::is_ps() ? FL2_BFG_SHOOTABLE : 0);
926         break;
927 
928     case en_gurney: // Gurney Mutant - Off of gurney.
929         new_actor->temp1 = SPR_GURNEY_MUT_W1;
930         new_actor->speed = ALIENSPEED;
931         new_actor->flags |= FL_PROJ_TRANSPARENT | FL_NO_SLIDE;
932         new_actor->ammo = ALIENAMMOINIT;
933         new_actor->flags2 = (::is_ps() ? FL2_BFG_SHOOTABLE : 0);
934         break;
935 
936     default:
937         break;
938     }
939 
940     CheckForSpecialTile(new_actor, tilex, tiley);
941 
942     if (which < static_cast<enemy_t>(SPACER1_OBJ)) {
943         new_actor->hitpoints = starthitpoints[gamestate.difficulty][which];
944     }
945 
946     if (!::is_ps()) {
947         switch (which) {
948         case en_spider_mutant:
949         case en_breather_beast:
950         case en_cyborg_warrior:
951         case en_reptilian_warrior:
952         case en_acid_dragon:
953         case en_mech_guardian:
954             ::new_actor->hitpoints *= 15;
955             break;
956 
957         default:
958             break;
959         }
960     }
961 }
962 
963 
964 // ---------------------------------------------------------------------------
965 // T_OfsThink() - Think for general Offset Objects
966 //
967 // NOTE: This think is used for NON-SmartAnim objects
968 // ---------------------------------------------------------------------------
969 
970 using GrenadeShapes = std::vector<int16_t>;
971 
972 GrenadeShapes grenade_shapes;
973 
initialize_grenade_shape_constants()974 void initialize_grenade_shape_constants()
975 {
976     grenade_shapes = {
977         SPR_GRENADE_FLY3,
978         SPR_GRENADE_FLY3,
979         SPR_GRENADE_FLY2,
980         SPR_GRENADE_FLY1,
981         SPR_GRENADE_FLY2,
982         SPR_GRENADE_FLY2,
983         SPR_GRENADE_FLY3,
984         SPR_GRENADE_FLY4,
985         0,
986     };
987 }
988 
T_OfsThink(objtype * obj)989 void T_OfsThink(
990     objtype* obj)
991 {
992     int8_t dx, dy, dist;
993     int8_t oldofs, ofs = 0;
994 
995     switch (obj->obclass) {
996     case plasma_detonator_reserveobj:
997         break;
998 
999     case plasma_detonatorobj:
1000         if (obj->temp3 < tics) {
1001             BlastNearDoors(obj->tilex, obj->tiley);
1002 
1003             obj->lighting = NO_SHADING; // no shading
1004             obj->flags &= ~FL_SHOOTABLE;
1005             obj->obclass = pd_explosionobj;
1006             A_DeathScream(obj);
1007             ::InitSmartSpeedAnim(obj, SPR_DETONATOR_EXP1, 0, 7, at_ONCE, ad_FWD, 3 + (US_RndT() & 3));
1008             return;
1009         } else {
1010             obj->temp3 -= tics;
1011         }
1012 
1013         if (obj->ammo >= tics) {
1014             obj->ammo -= static_cast<uint8_t>(tics);
1015         } else {
1016             obj->ammo = DETONATOR_FLASH_RATE;
1017             if (obj->temp1 == SPR_DOORBOMB) {
1018                 obj->temp1 = SPR_ALT_DOORBOMB;
1019             } else {
1020                 obj->temp1 = SPR_DOORBOMB;
1021             }
1022         }
1023         break;
1024 
1025     case grenadeobj: {
1026         T_Projectile(obj);
1027 
1028         // Check for range and update shape...
1029 
1030         if (obj->obclass == grenadeobj) {
1031             //
1032             // Has not exploded yet...
1033             //
1034 
1035             dx = obj->s_tilex - obj->tilex;
1036             dx = ABS(dx);
1037             dy = obj->s_tiley - obj->tiley;
1038             dy = ABS(dy);
1039             dist = dx > dy ? dx : dy;
1040 
1041             // Reached end of range?
1042 
1043             if ((obj->temp1 = grenade_shapes[static_cast<int>(dist)]) == 0) {
1044                 obj->obclass = gr_explosionobj;
1045                 ::InitSmartSpeedAnim(obj, SPR_GRENADE_EXPLODE1, 0, 4, at_ONCE, ad_FWD, 3 + (US_RndT() & 3));
1046             }
1047         }
1048 
1049         if (obj->obclass != grenadeobj) {
1050             ExplodeRadius(obj, 50 + (US_RndT() & 0x7f), false);
1051             A_DeathScream(obj);
1052         }
1053     }
1054     break;
1055 
1056     case bfg_shotobj: {
1057         T_Projectile(obj);
1058 
1059         if (obj->temp1 != SPR_BFG_WEAPON_SHOT3 && obj->obclass == bfg_shotobj) {
1060             //
1061             // Has not exploded yet...
1062             //
1063 
1064             dx = obj->s_tilex - obj->tilex;
1065             dx = ABS(dx);
1066             dy = obj->s_tiley - obj->tiley;
1067             dy = ABS(dy);
1068             dist = dx > dy ? dx : dy;
1069 
1070             obj->temp1 = SPR_BFG_WEAPON_SHOT2 + (dist >> 2);
1071         }
1072 
1073         if (obj->obclass != bfg_shotobj) {
1074             ExplodeRadius(obj, 50 + (US_RndT() & 0x7f), false);
1075             A_DeathScream(obj);
1076         }
1077     }
1078     break;
1079 
1080     case ventdripobj:
1081         // Decrement timer...
1082         //
1083         if (tics < obj->temp2) {
1084             obj->temp2 -= tics;
1085             break;
1086         }
1087 
1088         // Assign random delay and next shape.
1089         //
1090         obj->temp2 = 5 + (US_RndT() % 10);
1091 
1092         obj->temp1 -= obj->temp3;
1093         obj->temp3 = (obj->temp3 + 1) % 4;
1094         obj->temp1 += obj->temp3;
1095         break;
1096 
1097     case flickerlightobj:
1098         if (tics < obj->temp2) {
1099             obj->temp2 -= tics;
1100             break;
1101         }
1102 
1103         if ((obj->temp1 == SPR_DECO_ARC_1) || (US_RndT() & 15)) {
1104             // Becomes darker....
1105 
1106             auto t = static_cast<int8_t>((US_RndT() & 1));
1107 
1108             obj->lighting = LAMP_ON_SHADING + ((t + 3) << 2);
1109             obj->temp1 = SPR_DECO_ARC_2 + t;
1110             obj->temp2 = 2 + (US_RndT() % 2);
1111         } else {
1112             obj->temp1 = SPR_DECO_ARC_1;
1113             obj->temp2 = (4 + US_RndT() % 10) * 60;
1114             obj->lighting = LAMP_ON_SHADING;
1115         }
1116         break;
1117 
1118     case electrosphereobj:
1119         oldofs = static_cast<int8_t>(obj->temp1 - SPR_ELECTRO_SPHERE_ROAM1);
1120         ofs = US_RndT() & 3;
1121         if (ofs == oldofs) {
1122             ofs = (ofs + 1) & 3;
1123         }
1124         obj->temp1 = SPR_ELECTRO_SPHERE_ROAM1 + ofs;
1125         break;
1126 
1127     case podobj:
1128         if (obj->flags & FL_VISABLE) {
1129             FirstSighting(obj);
1130         }
1131         break;
1132 
1133     case podeggobj:
1134         if (!(obj->flags & FL_VISABLE)) {
1135             break;
1136         }
1137 
1138         if (obj->temp2 == 0xff * 60) {
1139             break;
1140         }
1141 
1142         if (obj->temp2 > tics) {
1143             obj->temp2 -= tics;
1144             break;
1145         }
1146 
1147         ::sd_play_actor_sound(PODHATCHSND, obj, bstone::AC_VOICE);
1148 
1149         ::InitSmartSpeedAnim(obj, SPR_POD_HATCH1, 0, 2, at_ONCE, ad_FWD, 7);
1150         break;
1151 
1152     case morphing_spider_mutantobj:
1153     case morphing_reptilian_warriorobj:
1154     case morphing_mutanthuman2obj:
1155         if (!(obj->flags & FL_VISABLE)) {
1156             break;
1157         }
1158 
1159         if (obj->temp2 > tics) {
1160             obj->temp2 -= tics;
1161             break;
1162         }
1163 
1164         obj->flags &= ~FL_SHOOTABLE;
1165         ::InitSmartSpeedAnim(obj, obj->temp1, 0, 8, at_ONCE, ad_FWD, 2);
1166         break;
1167 
1168     case crate1obj:
1169     case crate2obj:
1170     case crate3obj:
1171     case lcan_wait_alienobj: // These objs wait till they are destroyed
1172     case scan_wait_alienobj: //   before doing anything.
1173         break;
1174 
1175     case gurney_waitobj:
1176         if (obj->flags & FL_VISABLE) {
1177             SightPlayer(obj);
1178         }
1179         break;
1180 
1181     case spider_mutantshotobj:
1182     case final_boss4shotobj:
1183     case acid_dragonshotobj:
1184         obj->temp1 = BossShotShapes[obj->obclass - spider_mutantshotobj] + (US_RndT() % 3);
1185         T_Projectile(obj);
1186         break;
1187 
1188     default:
1189         T_Stand(obj);
1190         break;
1191     }
1192 }
1193 
RandomSphereDir(enemy_t enemy)1194 int16_t RandomSphereDir(
1195     enemy_t enemy)
1196 {
1197     int16_t dir = 0;
1198 
1199     switch (enemy) {
1200     case en_vertsphere:
1201         dir = ((US_RndT() % 2) * 4) + 2;
1202         break;
1203 
1204     case en_horzsphere:
1205         dir = (US_RndT() % 2) * 4;
1206         break;
1207 
1208     case en_diagsphere:
1209         dir = ((US_RndT() % 4) * 2) + 1;
1210         break;
1211 
1212     default:
1213         break;
1214     }
1215 
1216     return dir;
1217 }
1218 
SphereStartDir(objtype * ob)1219 void SphereStartDir(
1220     objtype* ob)
1221 {
1222     int8_t loop;
1223 
1224     actorat[ob->tilex][ob->tiley] = nullptr;
1225     for (loop = 0; loop < 3; loop++) {
1226         ob->dir = static_cast<dirtype>(RandomSphereDir(
1227             static_cast<enemy_t>(en_vertsphere + ((ob->trydir - en_vertsphere + loop) % 3))));
1228 
1229         if (!TryWalk(ob, true)) {
1230             ob->dir = opposite[ob->dir];
1231             if (!TryWalk(ob, true)) {
1232                 ob->dir = nodir;
1233             } else {
1234                 break;
1235             }
1236         } else {
1237             break;
1238         }
1239     }
1240     actorat[ob->tilex][ob->tiley] = ob;
1241 }
1242 
CheckForcedMove(objtype * ob)1243 void CheckForcedMove(
1244     objtype* ob)
1245 {
1246     int16_t olddir;
1247 
1248     olddir = static_cast<int16_t>(ob->dir);
1249     ob->dir = static_cast<dirtype>(RandomSphereDir(static_cast<enemy_t>(ob->trydir)));
1250     if (!TryWalk(ob, false)) {
1251         ob->dir = opposite[ob->dir];
1252         if (!TryWalk(ob, false)) {
1253             ob->dir = static_cast<dirtype>(olddir);
1254         }
1255     }
1256 }
1257 
T_OfsBounce(objtype * ob)1258 void T_OfsBounce(
1259     objtype* ob)
1260 {
1261     int16_t oldtx, oldty;
1262     int32_t move, dx, dy, dist;
1263 
1264 // Should Electro-Sphere decrease player's health?
1265 //
1266 
1267     dx = player->x - ob->x;
1268     dx = LABS(dx);
1269     dy = player->y - ob->y;
1270     dy = LABS(dy);
1271     dist = dx > dy ? dx : dy;
1272 
1273     if (dist < TILEGLOBAL) {
1274         ::sd_play_actor_sound(ELECARCDAMAGESND, ob, bstone::AC_WEAPON);
1275 
1276         TakeDamage(4, ob);
1277     }
1278 
1279 
1280 // Safety net - restart! Try to get a 'nodir' actor moving again.
1281 //
1282     if (ob->dir == nodir) {
1283         SphereStartDir(ob);
1284         if (ob->dir == nodir) {
1285             return;
1286         }
1287     }
1288 
1289 // Make actor bounce around the map!
1290 //
1291     move = ob->speed * tics;
1292 
1293     while (move) {
1294         // Can actor move without reaching destination tile?
1295         //
1296         if (move < ob->distance) {
1297             MoveObj(ob, move); // YES!
1298             break;
1299         }
1300 
1301         // Align actor on destination tile.
1302         //
1303         ob->x = ((int32_t)ob->tilex << TILESHIFT) + TILEGLOBAL / 2;
1304         ob->y = ((int32_t)ob->tiley << TILESHIFT) + TILEGLOBAL / 2;
1305 
1306         // Decrement move distance and reset distance to next tile.
1307         //
1308         move -= ob->distance;
1309         ob->distance = TILEGLOBAL;
1310 
1311         // Can actor continue moving in current direction?
1312         //
1313         oldtx = ob->tilex;
1314         oldty = ob->tiley;
1315         if (!TryWalk(ob, true)) {
1316             bool check_opposite = false;
1317 
1318             // Restore tilex/tiley
1319             //
1320             ob->tilex = static_cast<uint8_t>(oldtx);
1321             ob->tiley = static_cast<uint8_t>(oldty);
1322 
1323             // Direction change is based on current direction.
1324             //
1325             switch (ob->dir) {
1326             case northeast:
1327             case northwest:
1328             case southeast:
1329             case southwest:
1330                 if (ob->trydir != static_cast<dirtype>(en_diagsphere)) {
1331                     if (!MoveTrappedDiag(ob)) {
1332                         SphereStartDir(ob);
1333                     }
1334                     continue;
1335                 }
1336 
1337                 ob->dir = static_cast<dirtype>((ob->dir + 2) % 8); // Try 90 degrees to the left
1338                 if (TryWalk(ob, false)) {
1339                     break;
1340                 }
1341 
1342                 ob->dir = opposite[ob->dir]; // Try 90 degrees to the right
1343                 if (TryWalk(ob, false)) {
1344                     break;
1345                 }
1346 
1347                 ob->dir = static_cast<dirtype>((ob->dir + 2) % 8); // Back to original direction..
1348                 // Must be in a corner...
1349 
1350                 check_opposite = true;
1351                 break;
1352 
1353             case north:
1354             case south:
1355                 if (ob->trydir != static_cast<dirtype>(en_vertsphere)) {
1356                     if (!MoveTrappedDiag(ob)) {
1357                         SphereStartDir(ob);
1358                     }
1359                     continue;
1360                 }
1361 
1362                 check_opposite = true;
1363                 break;
1364 
1365             case east:
1366             case west:
1367                 if (ob->trydir != static_cast<dirtype>(en_horzsphere)) {
1368                     if (!MoveTrappedDiag(ob)) {
1369                         SphereStartDir(ob);
1370                     }
1371                     continue;
1372                 }
1373 
1374                 check_opposite = true;
1375                 break;
1376 
1377             default:
1378                 break;
1379             }
1380 
1381             // Check opposite direction?
1382             //
1383             if (check_opposite) {
1384                 ob->dir = opposite[ob->dir];
1385                 if (!TryWalk(ob, false)) {
1386                     ob->dir = nodir;
1387                 }
1388             }
1389 
1390             // TryWalk  "true"  to set new destination tile.
1391             //
1392             if (!TryWalk(ob, true)) {
1393                 ob->dir = nodir;
1394             }
1395         } else {
1396             int8_t orgx = ob->tilex, orgy = ob->tiley;
1397 
1398             ob->tilex = static_cast<uint8_t>(oldtx);
1399             ob->tiley = static_cast<uint8_t>(oldty);
1400             if (!MoveTrappedDiag(ob)) {
1401                 ob->tilex = orgx;
1402                 ob->tiley = orgy;
1403             }
1404         }
1405     }
1406 }
1407 
MoveTrappedDiag(objtype * ob)1408 bool MoveTrappedDiag(
1409     objtype* ob)
1410 {
1411 // Don't mess with HORZs, VERTs, or normal DIAGs.
1412 //
1413     if ((ob->trydir != static_cast<dirtype>(en_diagsphere)) ||
1414         ((ob->dir >> 1 << 1) != ob->dir))
1415     {
1416         return false;
1417     }
1418 
1419 // When a DIAG gets to an open area, change it to diagonal movement!
1420 //
1421     if (!CheckTrappedDiag(ob)) {
1422         return true;
1423     }
1424 
1425 // DIAGs have a 50/50 chance of changing direction.
1426 //
1427     if (US_RndT() & 1) {
1428         return false;
1429     }
1430 
1431 // Try changing directions.
1432 //
1433     if (ob->dir == north || ob->dir == south) {
1434         ob->trydir = static_cast<dirtype>(en_horzsphere);
1435     } else {
1436         ob->trydir = static_cast<dirtype>(en_vertsphere);
1437     }
1438 
1439     CheckForcedMove(ob);
1440 
1441     ob->trydir = static_cast<dirtype>(en_diagsphere);
1442 
1443     return TryWalk(ob, true);
1444 }
1445 
CheckTrappedDiag(objtype * ob)1446 bool CheckTrappedDiag(
1447     objtype* ob)
1448 {
1449     dirtype orgdir = ob->dir;
1450 
1451     for (ob->dir = northeast; ob->dir <= southeast; ob->dir += 2) {
1452         if (TryWalk(ob, false)) {
1453             break;
1454         }
1455     }
1456 
1457     if (ob->dir > southeast) {
1458         ob->dir = orgdir;
1459         return true;
1460     } else {
1461         TryWalk(ob, true);
1462         return false;
1463     }
1464 }
1465 
1466 // ---------------------------------------------------------------------------
1467 // FindObj() - This function will search the objlist for an object
1468 //              of a given type at a given tilex & tiley coords.
1469 //
1470 // PARAMETERS:
1471 //  which - objtype of actor to look for.
1472 //  tilex - tile x coord of actor looking for (-1 == Dont care)
1473 //  tiley - tile y coord of actor looking for (-1 == Dont care)
1474 //
1475 // ---------------------------------------------------------------------------
FindObj(classtype which,int16_t tilex,int16_t tiley)1476 objtype* FindObj(
1477     classtype which,
1478     int16_t tilex,
1479     int16_t tiley)
1480 {
1481     objtype* obj;
1482 
1483     for (obj = player->next; obj; obj = obj->next) {
1484         if (obj->obclass == which) {
1485             if (tilex != -1) {
1486                 if (tilex != obj->tilex) {
1487                     continue;
1488                 }
1489             }
1490 
1491             if (tiley != -1) {
1492                 if (tiley != obj->tiley) {
1493                     continue;
1494                 }
1495             }
1496 
1497             return obj;
1498         }
1499     }
1500 
1501     return nullptr;
1502 }
1503 
1504 // ---------------------------------------------------------------------------
1505 // SpawnHiddenOfs() - This function will spawn a given offset actor at a passed
1506 //                  x & y coords and then move the actor to (0,0) for hidding
1507 //
1508 // PURPOSE: This function was created mainly for spawning reserve objects that
1509 //          would later be simply moved from their (0,0) coords to a new
1510 //          location.
1511 //
1512 // NOTE: When this actor is moved from it's (0,0) coords, you will need to
1513 //       update the area number that this actor is standing in.
1514 //
1515 // ---------------------------------------------------------------------------
SpawnHiddenOfs(enemy_t which,int16_t tilex,int16_t tiley)1516 void SpawnHiddenOfs(
1517     enemy_t which,
1518     int16_t tilex,
1519     int16_t tiley)
1520 {
1521     nevermark = true;
1522     SpawnOffsetObj(which, tilex, tiley); // Spawn a reserve
1523     nevermark = false;
1524     new_actor->tilex = 0;
1525     new_actor->tiley = 0;
1526     new_actor->x = TILEGLOBAL / 2;
1527     new_actor->y = TILEGLOBAL / 2;
1528 }
1529 
1530 // ---------------------------------------------------------------------------
1531 // FindHiddenOfs() - This function will find a hidden ofs actor which was
1532 //                  hidden using SpawnHiddenOfs() and will return a pointer
1533 //                  to the actor in the objlist.
1534 //
1535 // NOTE: When this actor is moved from it's (0,0) coords, you will need to
1536 //       update the area number that this actor is standing in or use
1537 //       MoveHiddenOfs().
1538 // ---------------------------------------------------------------------------
FindHiddenOfs(classtype which)1539 objtype* FindHiddenOfs(
1540     classtype which)
1541 {
1542     auto obj = FindObj(which, -1, -1);
1543 
1544     if (!obj) {
1545         ::Quit("Unable to find a \"Hidden Actor\" at location (0, 0).");
1546     }
1547 
1548     return obj;
1549 }
1550 
1551 // ---------------------------------------------------------------------------
1552 // MoveHiddenOfs() - This function will find a hidden ofs actor which was
1553 //                  hidden using SpawnHiddenOfs() and will move the actor
1554 //                  to a new passed coords and change the actors class.
1555 //
1556 // RETURNS: Success = Ptr to the object that was moved.
1557 //          Failure = nullptr.
1558 //
1559 // NOTE: This function updates the actors 'areanumber' to the newly changed
1560 //       coords.
1561 // ---------------------------------------------------------------------------
MoveHiddenOfs(classtype which_class,classtype new_class,fixed x,fixed y)1562 objtype* MoveHiddenOfs(
1563     classtype which_class,
1564     classtype new_class,
1565     fixed x,
1566     fixed y)
1567 {
1568     auto obj = FindHiddenOfs(which_class);
1569 
1570     if (obj) {
1571         obj->obclass = new_class;
1572         obj->x = x;
1573         obj->y = y;
1574         obj->tilex = static_cast<uint8_t>(x >> TILESHIFT);
1575         obj->tiley = static_cast<uint8_t>(y >> TILESHIFT);
1576         obj->areanumber = GetAreaNumber(obj->tilex, obj->tiley);
1577 
1578         return obj;
1579     }
1580 
1581     return nullptr;
1582 }
1583 
1584 
1585 // ===========================================================================
1586 //
1587 //  SMART_ANIM ANIMATION ROUTINES
1588 //
1589 //
1590 // * NOTES: OffsetObjects require the use of TEMP1 of the object structure!
1591 //
1592 // * NOTES: THINK function AnimateOfsObj() requires the use of TEMP3 of the
1593 //                              object structure!
1594 //
1595 // ===========================================================================
1596 
1597 // ---------------------------------------------------------------------------
1598 // ::InitSmartAnim() - Sets up an object for a SmartAnimation
1599 //
1600 // PARAMETERS:  Obj  - Ptr to object structure
1601 //              ShapeNum - First shape number in anim
1602 //              StartOfs - Starting offset in the animation to start with.
1603 //              MaxOfs  - Ending offset in the animation.
1604 //              AnimType - Type of animation (at_CYCLE,at_ONCE,or at_REBOUND)
1605 //              AnimDir - Direction of animation to start (ad_FWD, or ad_REV)
1606 //
1607 // ** This function should ALWAY be used to init/start SmartAnimations! **
1608 //
1609 // NOTE : It is the programmers responsiblity to watch bit field ranges on
1610 //                       the passed parameters.
1611 //
1612 // NOTES: THINK function AnimateOfsObj() requires the use of TEMP3 of the
1613 //                  object structure!
1614 //
1615 // ---------------------------------------------------------------------------
1616 
InitSmartAnimStruct(objtype * obj,uint16_t ShapeNum,uint8_t StartOfs,uint8_t MaxOfs,animtype_t AnimType,animdir_t AnimDir)1617 void InitSmartAnimStruct(
1618     objtype* obj,
1619     uint16_t ShapeNum,
1620     uint8_t StartOfs,
1621     uint8_t MaxOfs,
1622     animtype_t AnimType,
1623     animdir_t AnimDir)
1624 {
1625     obj->temp1 = ShapeNum + StartOfs;
1626 
1627     obj->temp3 = 0;
1628     ofs_anim_t::set_curframe(StartOfs, obj);
1629     ofs_anim_t::set_maxframe(MaxOfs, obj);
1630     ofs_anim_t::set_animtype(static_cast<uint16_t>(AnimType), obj);
1631     ofs_anim_t::set_animdir(static_cast<uint16_t>(AnimDir), obj);
1632 
1633 }
1634 
InitAnim(objtype * obj,uint16_t ShapeNum,uint8_t StartOfs,uint8_t MaxOfs,animtype_t AnimType,animdir_t AnimDir,uint16_t Delay,uint16_t WaitDelay)1635 void InitAnim(
1636     objtype* obj,
1637     uint16_t ShapeNum,
1638     uint8_t StartOfs,
1639     uint8_t MaxOfs,
1640     animtype_t AnimType,
1641     animdir_t AnimDir,
1642     uint16_t Delay,
1643     uint16_t WaitDelay)
1644 {
1645     InitSmartAnimStruct(obj, ShapeNum, StartOfs, MaxOfs, AnimType, AnimDir);
1646     obj->s_tilex = static_cast<uint8_t>(WaitDelay);
1647     obj->s_tiley = static_cast<uint8_t>(Delay);
1648     NewState(obj, &s_ofs_smart_anim);
1649 }
1650 
1651 //
1652 // "s_ofs_smart_anim" - The following structure is designed for "Smart
1653 //  Animations."
1654 //
1655 // OVERVIEW:    This structure will allow the flexiblity to perform ANY type
1656 //  of animation, custom, etc. using one single structure.
1657 //  All of the animations are using "Offset" techniques, where
1658 //  the current shapenumber of the object is in TEMP1 of the
1659 //  object structure,  Code is then written to handle changing
1660 //  the shapenumber of each object individually.
1661 //
1662 // FUNCTIONS:  T_SmartThink() - Is a think that is call continusly and is
1663 //  special coded for an object/actor.
1664 //
1665 // T_SmartThought() - Is called only once allowing for other
1666 //  special codeing, (Ex. the spawning of another animation at
1667 //  anypoint in the current animation, death screams, sounds,etc)
1668 //
1669 // * NOTES:     The T_SmartThink could be modified to handle generic animations
1670 //          like cycle, once, rebound, etc. using flags in the object struct.
1671 //
1672 //
1673 
1674 statetype s_ofs_smart_anim = { 0, 0, 1, T_SmartThought, nullptr, &s_ofs_smart_anim };
1675 statetype s_ofs_smart_anim2 = { 0, 0, 1, T_SmartThought, nullptr, &s_ofs_smart_anim2 };
1676 
1677 //
1678 // Functions
1679 //
1680 
1681 // ---------------------------------------------
1682 // T_SmartThought() - A think for ofset objects
1683 //  that is called ONLY ONCE
1684 //  per state change.
1685 // ---------------------------------------------
T_SmartThought(objtype * obj)1686 void T_SmartThought(
1687     objtype* obj)
1688 {
1689     int32_t dx, dy;
1690 
1691     switch (obj->obclass) {
1692     case green_oozeobj:
1693     case black_oozeobj:
1694     case green2_oozeobj:
1695     case black2_oozeobj: {
1696         if (((::US_RndT() & 7) == 7) &&
1697             ofs_anim_t::get_curframe(obj) == 2 &&
1698             obj->tilex == player->tilex &&
1699             obj->tiley == player->tiley)
1700         {
1701             TakeDamage(4, obj);
1702         }
1703     }
1704     break;
1705 
1706     case arc_barrierobj:
1707         if (BARRIER_STATE(obj) == bt_DISABLED) {
1708             return;
1709         }
1710 
1711         if (US_RndT() < 0x10) {
1712             dx = player->x - obj->x;
1713             dx = LABS(dx);
1714             dy = player->y - obj->y;
1715             dy = LABS(dy);
1716 
1717             if (dy <= 0x16000 && dx <= 0x16000) {
1718                 ::sd_play_actor_sound(
1719                     ELECARCDAMAGESND, obj, bstone::AC_WEAPON);
1720 
1721                 TakeDamage(4, obj);
1722             }
1723         }
1724 
1725     case post_barrierobj:
1726         //
1727         // Check for Turn offs
1728         //
1729         if ((uint16_t)obj->temp2 != 0xffff) {
1730             if (!gamestate.barrier_table[obj->temp2].on) {
1731                 ToggleBarrier(obj);
1732             }
1733         }
1734         break;
1735 
1736     case volatiletransportobj:
1737     case floatingbombobj:
1738         if (obj->lighting) {
1739             // Slowly inc back to
1740 
1741             obj->lighting += static_cast<int8_t>(ofs_anim_t::get_curframe(obj));
1742             if (obj->lighting > 0) {
1743                 obj->lighting = 0;
1744             }
1745         }
1746         break;
1747 
1748     default:
1749         break;
1750     }
1751 
1752     if (ofs_anim_t::get_animtype(obj) != 0) {
1753         int old_frame = ofs_anim_t::get_curframe(obj);
1754         bool is_animated = ::AnimateOfsObj(obj);
1755 
1756         if (is_animated) {
1757             switch (obj->obclass) {
1758             case morphing_spider_mutantobj:
1759             case morphing_reptilian_warriorobj:
1760             case morphing_mutanthuman2obj:
1761                 dx = obj->obclass - morphing_spider_mutantobj;
1762                 obj->temp1 = MorphEndShapes[dx];
1763 
1764                 ::sd_play_actor_sound(
1765                     MorphSounds[dx], obj, bstone::AC_VOICE);
1766 
1767                 obj->obclass = static_cast<classtype>(MorphClass[dx]);
1768                 obj->hitpoints = starthitpoints[gamestate.difficulty][MorphClass[dx] - rentacopobj];
1769                 obj->flags &= ~FL_FAKE_STATIC;
1770                 obj->flags |= FL_PROJ_TRANSPARENT | FL_SHOOTABLE;
1771                 NewState(obj, &s_ofs_chase1);
1772                 break;
1773 
1774             case podeggobj:
1775                 obj->flags |= FL_SHOOTABLE;
1776                 obj->obclass = podobj;
1777                 obj->temp1 = SPR_POD_WALK1;
1778                 NewState(obj, &s_ofs_chase1);
1779                 obj->hitpoints = starthitpoints[gamestate.difficulty][en_pod];
1780                 break;
1781 
1782             case rotating_cubeobj:
1783                 if (::is_aog_full()) {
1784                     if (obj->temp1 == SPR_VITAL_OUCH) {
1785                         ::InitSmartSpeedAnim(obj, SPR_VITAL_STAND, 0, 0, at_NONE, ad_FWD, 0);
1786                     } else if (obj->temp1 == SPR_VITAL_DIE_8) {
1787                         ::InitSmartSpeedAnim(obj, SPR_VITAL_DEAD_1, 0, 2, at_CYCLE, ad_FWD, 16);
1788 
1789                         if (::get_remaining_generators() == 0) {
1790                             obj->ammo = 1;
1791                         }
1792                     }
1793                 } else if (::is_ps()) {
1794                     DISPLAY_TIMED_MSG(pd_floorunlocked, MP_FLOOR_UNLOCKED, MT_GENERAL);
1795                     ::sd_play_player_sound(ROLL_SCORESND, bstone::AC_ITEM);
1796                     obj->lighting = 0;
1797                 }
1798                 break;
1799 
1800             case inertobj:
1801             case fixup_inertobj:
1802             case scan_wait_alienobj:
1803             case lcan_wait_alienobj:
1804             case gurney_waitobj:
1805                 break;
1806 
1807             case gold_morphobj:
1808                 //
1809                 // Game completed!
1810                 //
1811                 playstate = ex_victorious;
1812                 obj->state = nullptr;  // Mark to be removed.
1813                 break;
1814 
1815 
1816             case volatiletransportobj:
1817             case floatingbombobj:
1818                 NewState(obj, &s_scout_dead);
1819                 obj->lighting = 0;
1820                 return;
1821 
1822             default:
1823                 obj->state = nullptr;  // Mark to be removed.
1824                 break;
1825             }
1826         }
1827 
1828         int new_frame = ofs_anim_t::get_curframe(obj);
1829         bool is_frame_changed = (old_frame != new_frame);
1830 
1831         if (::is_aog_full() &&
1832             !is_animated &&
1833             is_frame_changed &&
1834             obj->obclass == rotating_cubeobj)
1835         {
1836             if (obj->temp1 == SPR_VITAL_DIE_2) {
1837                 ::display_remaining_generators();
1838             } else if (obj->temp1 == SPR_VITAL_DIE_4) {
1839                 ::display_remaining_generators();
1840                 ::ExplodeRadius(obj, 35 + (::US_RndT() & 15), true);
1841             } else if (obj->temp1 == SPR_VITAL_DEAD_1) {
1842                 if (obj->ammo > 0) {
1843                     obj->ammo -= 1;
1844 
1845                     if (obj->ammo == 0) {
1846                         playstate = ex_victorious;
1847                     }
1848                 }
1849             }
1850         }
1851 
1852         if (ofs_anim_t::get_curframe(obj) == 3) {
1853             switch (obj->obclass) {
1854             case doorexplodeobj:
1855                 if (!obj->temp2) {
1856                     int16_t avail, total, i;
1857 
1858                     // Make sure that there are at least DR_MIN_STATICS
1859 
1860                     avail = MAXSTATS;
1861                     total = static_cast<int16_t>(laststatobj - &statobjlist[0]);
1862                     for (i = 0; i < total; i++) {
1863                         if (statobjlist[i].shapenum != -1) {
1864                             avail--;
1865                         }
1866                     }
1867 
1868                     if ((avail > DR_MIN_STATICS) && (US_RndT() & 1)) {
1869                         SpawnStatic(obj->tilex, obj->tiley, DOOR_RUBBLE_STATNUM);
1870                     }
1871                 }
1872                 break;
1873 
1874             case explosionobj:
1875                 if (!obj->temp2) {
1876                     ExplodeRadius(obj, 20, true);
1877                     MakeAlertNoise(obj);
1878                     obj->temp2 = 1;
1879                 }
1880                 break;
1881 
1882             case pd_explosionobj:
1883                 if (!obj->temp2) {
1884                     ExplodeRadius(obj, PLASMA_DETONATOR_DAMAGE, true);
1885                     MakeAlertNoise(obj);
1886                     obj->temp2 = 1;
1887                 }
1888 
1889             case bfg_explosionobj:
1890                 if (!obj->temp2) {
1891                     ExplodeRadius(obj, BFG_DAMAGE, true);
1892                     MakeAlertNoise(obj);
1893                     obj->temp2 = 1;
1894                 }
1895                 break;
1896 
1897             case gurney_waitobj:
1898                 if (obj->temp2) {
1899                     RemoveObj(ui16_to_actor(obj->temp2));
1900                 }
1901 
1902                 SpawnOffsetObj(en_gurney, obj->tilex, obj->tiley);
1903                 NewState(obj, &s_ofs_static);
1904 // obj->obclass = fixup_inertobj;
1905                 break;
1906 
1907             case scan_wait_alienobj:
1908                 if (obj->temp2) {
1909                     RemoveObj(ui16_to_actor(obj->temp2));
1910                 }
1911 
1912                 SpawnOffsetObj(en_scan_alien, obj->tilex, obj->tiley);
1913                 NewState(obj, &s_ofs_static);
1914 // obj->obclass = fixup_inertobj;
1915                 break;
1916 
1917             case lcan_wait_alienobj:
1918                 if (obj->temp2) {
1919                     RemoveObj(ui16_to_actor(obj->temp2));
1920                 }
1921 
1922                 SpawnOffsetObj(en_lcan_alien, obj->tilex, obj->tiley);
1923                 NewState(obj, &s_ofs_static);
1924 // obj->obclass = fixup_inertobj;
1925                 break;
1926 
1927             default:
1928                 break;
1929             }
1930         }
1931 
1932         if (ofs_anim_t::get_curframe(obj) == 2) {
1933             switch (obj->obclass) {
1934             case volatiletransportobj:
1935                 if (!(obj->flags & FL_INTERROGATED)) {
1936                     if (US_RndT() < 0x7f) {
1937                         usedummy = true;
1938                         SpawnOffsetObj(en_green_ooze, obj->tilex, obj->tiley);
1939                         new_actor->x = obj->x + (US_RndT() << 7);
1940                         new_actor->y = obj->y + (US_RndT() << 7);
1941                         new_actor->tilex = static_cast<uint8_t>(new_actor->x >> TILESHIFT);
1942                         new_actor->tiley = static_cast<uint8_t>(new_actor->y >> TILESHIFT);
1943                         usedummy = false;
1944                     }
1945                 }
1946 
1947             case floatingbombobj:
1948                 if (!(obj->flags & FL_INTERROGATED)) {
1949                     T_ExplodeDamage(obj);
1950                     obj->flags |= FL_INTERROGATED;
1951                 }
1952                 break;
1953 
1954             default:
1955                 break;
1956             }
1957         }
1958     }
1959 }
1960 
1961 // ------------------------------------------------------------------
1962 // AnimateOfsObj() - Animation routine for OffsetObjects.
1963 //
1964 // IMPORTANT NOTE: This does NOT work on the same principle as the old
1965 // """"""""""""""  AnimateObj() function!
1966 //
1967 //
1968 // obj->TEMP1   - Holds the current shape number.
1969 //
1970 // obj->TEMP3.maxframe  - Frames in anim. (0 == 1 Frame, 1 == 2 Frames, etc))
1971 //
1972 // obj->TEMP3.curframe  - Holds the the shape offset which TEMP1 is off from
1973 //      the 1st shape in the anim. ALWAYS POSITIVE NUMBER!
1974 //      REVerse animations have the "curframe" on the last
1975 //      offset and TEMP1-CURFRAME should equ the first frame
1976 //      and MAXFRAME should equ the offset for the first
1977 //      frame.
1978 //
1979 // obj->TEMP3.animdir   - Determines the direction of the animation.
1980 //
1981 //
1982 // obj->S_TILEX - Current number of tics remaining before advancing
1983 //      to next frame.
1984 //
1985 // obj->S_TILEY - Delay Tic value per frame - This value is copied
1986 //      into  S_TILEX upon reaching the end of a frame.
1987 //
1988 //      IF you want to do a reverse animation then you would need to init
1989 // TEMP1 to the shape number of the LAST Shape, "curframe" to the       offset
1990 //      value from the FIRST FRAME in anim and set "animdir" to at_REV.
1991 //
1992 // * NOTES: This is an OffsetObject which requires the use of TEMP1 of the
1993 //      object structure!
1994 //
1995 // * NOTES: The use of a SmartAnim requires the use of TEMP3 of the object
1996 //      structure!!! Therefore, Any other THINKS (like LookForGoodies)
1997 //      CAN NOT be used while this routine is being called also.
1998 //
1999 // * NOTES: ALL SmartAnimations have the SAME animation delay rates!    Sorry!
2000 //
2001 // * NOTES: The SmartAnimations use S_TILEX & S_TILEY for animation delay
2002 //      values - All SMART actors can not be used if they are using
2003 //      a "smart" think!
2004 //
2005 // ------------------------------------------------------------------
AnimateOfsObj(objtype * obj)2006 bool AnimateOfsObj(
2007     objtype* obj)
2008 {
2009     bool Done = false;
2010 
2011     if (obj->s_tilex) { // Check animation delay.
2012         if (obj->s_tilex < tics) {
2013             obj->s_tilex = 0;
2014         } else {
2015             obj->s_tilex -= static_cast<uint8_t>(tics);
2016         }
2017         return false;
2018     }
2019 
2020     switch (ofs_anim_t::get_animtype(obj)) { // Animate this puppy!
2021     case at_ONCE:
2022     case at_CYCLE:
2023         switch (ofs_anim_t::get_animdir(obj)) {
2024         case ad_FWD:
2025             if (ofs_anim_t::get_curframe(obj) < ofs_anim_t::get_maxframe(obj)) {
2026                 AdvanceAnimFWD(obj);
2027             } else if (ofs_anim_t::get_animtype(obj) == at_CYCLE) {
2028                 // Pull shape number back to start...
2029 
2030                 obj->temp1 -= ofs_anim_t::get_curframe(obj);
2031 
2032                 // Reset Cycle Animation
2033 
2034                 ofs_anim_t::set_curframe(0, obj);
2035 
2036                 obj->s_tilex = obj->s_tiley;
2037             } else {
2038                 // Terminate ONCE Anim type
2039 
2040                 ofs_anim_t::set_animtype(at_NONE, obj);
2041                 Done = true;
2042             }
2043             break;
2044         }
2045         break;
2046     }
2047 
2048     return Done;
2049 }
2050 
AdvanceAnimFWD(objtype * obj)2051 void AdvanceAnimFWD(
2052     objtype* obj)
2053 {
2054     auto curframe = ofs_anim_t::get_curframe(obj);
2055     ofs_anim_t::set_curframe(curframe + 1, obj);
2056 
2057     obj->temp1++;
2058     obj->s_tilex = obj->s_tiley;
2059 }
2060 
2061 
2062 // ==========================================================================
2063 //
2064 // WALL SWITCH ACTIVATION
2065 //
2066 // ==========================================================================
2067 
2068 // BBi
2069 namespace {
2070 
2071 
2072 // Returns cross barrier's index by local one or
2073 // -1 if not found.
get_cross_barrier_index(int local_barrier_index)2074 int get_cross_barrier_index(
2075     int local_barrier_index)
2076 {
2077     const auto& local_barrier =
2078         ::gamestate.barrier_table[local_barrier_index];
2079 
2080     for (int i = 0; i < MAX_BARRIER_SWITCHES; ++i) {
2081         const auto& cross_barrier = ::gamestate.cross_barriers[i];
2082 
2083         if (cross_barrier.on == 0xFF) {
2084             return -1;
2085         }
2086 
2087         if (cross_barrier.level == local_barrier.level &&
2088             cross_barrier.coord.tilex == local_barrier.coord.tilex &&
2089             cross_barrier.coord.tiley == local_barrier.coord.tiley)
2090         {
2091             return i;
2092         }
2093     }
2094 
2095     return -1;
2096 }
2097 
2098 // Updates cross barrier's state
set_cross_barrier_state(int local_barrier_index)2099 void set_cross_barrier_state(
2100     int local_barrier_index)
2101 {
2102     if (::is_ps()) {
2103         return;
2104     }
2105 
2106     auto cross_barrier_index = get_cross_barrier_index(
2107         local_barrier_index);
2108 
2109     if (cross_barrier_index < 0) {
2110         return;
2111     }
2112 
2113     const auto& local_barrier =
2114         ::gamestate.barrier_table[local_barrier_index];
2115 
2116     ::gamestate.cross_barriers[cross_barrier_index].on = local_barrier.on;
2117 }
2118 
2119 
2120 } // namespace
2121 
2122 // Inserts (if not exists) a new cross barrier record
store_cross_barrier(uint8_t level,uint8_t x,uint8_t y,bool state)2123 void store_cross_barrier(
2124     uint8_t level,
2125     uint8_t x,
2126     uint8_t y,
2127     bool state)
2128 {
2129     int i;
2130     bool found = false;
2131 
2132     for (i = 0; i < MAX_BARRIER_SWITCHES; ++i) {
2133         auto& barrier = ::gamestate.cross_barriers[i];
2134 
2135         if (barrier.on != 0xFF &&
2136             barrier.coord.tilex == x &&
2137             barrier.coord.tiley == y)
2138         {
2139             if (barrier.level != level) {
2140                 ::Quit("Mismatch cross-barrier at ({}, {})", x, y);
2141             }
2142 
2143             return;
2144         }
2145 
2146         if (barrier.on == 0xFF) {
2147             found = true;
2148             break;
2149         }
2150     }
2151 
2152     if (!found) {
2153         ::Quit("Too many cross-barriers.");
2154     }
2155 
2156     auto& barrier = ::gamestate.cross_barriers[i];
2157 
2158     barrier.level = level;
2159     barrier.coord.tilex = x;
2160     barrier.coord.tiley = y;
2161     barrier.on = state;
2162 }
2163 
2164 // Inserts or updates local barrier record
apply_cross_barriers()2165 void apply_cross_barriers()
2166 {
2167     if (::is_ps()) {
2168         return;
2169     }
2170 
2171     for (int i = 0; i < MAX_BARRIER_SWITCHES; ++i) {
2172         const auto& cross_barrier = ::gamestate.cross_barriers[i];
2173 
2174         if (cross_barrier.on == 0xFF) {
2175             return;
2176         }
2177 
2178         if (cross_barrier.level != ::gamestate.mapon) {
2179             continue;
2180         }
2181 
2182         int j = 0;
2183         bool found_local = false;
2184         barrier_type* local_barrier = nullptr;
2185 
2186         for (j = 0; j < MAX_BARRIER_SWITCHES; ++j) {
2187             local_barrier = &::gamestate.barrier_table[j];
2188 
2189             if (local_barrier->on == 0xFF) {
2190                 break;
2191             }
2192 
2193             if (local_barrier->level != 0xFF ||
2194                 local_barrier->coord.tilex != cross_barrier.coord.tilex ||
2195                 local_barrier->coord.tiley != cross_barrier.coord.tiley)
2196             {
2197                 continue;
2198             }
2199 
2200             found_local = true;
2201             break;
2202         }
2203 
2204         if (!found_local) {
2205             if (j == MAX_BARRIER_SWITCHES) {
2206                 ::Quit("Failed to add local barrier.");
2207             }
2208         }
2209 
2210         local_barrier->level = 0xFF;
2211         local_barrier->coord.tilex = cross_barrier.coord.tilex;
2212         local_barrier->coord.tiley = cross_barrier.coord.tiley;
2213         local_barrier->on = cross_barrier.on;
2214     }
2215 }
2216 // BBi
2217 
2218 // --------------------------------------------------------------------------
2219 // ActivateWallSwitch() - Updates the Map, Actors, and Tables for wall switchs
2220 // --------------------------------------------------------------------------
ActivateWallSwitch(uint16_t iconnum,int16_t x,int16_t y)2221 void ActivateWallSwitch(
2222     uint16_t iconnum,
2223     int16_t x,
2224     int16_t y)
2225 {
2226     const uint16_t states[] = { OFF_SWITCH, ON_SWITCH };
2227 
2228     if ((iconnum & 0xFF00) == 0xF800) {
2229         //
2230         // Update tile map & Display switch'd message
2231         //
2232         auto num = iconnum & 0xFF;
2233         auto barrier = &::gamestate.barrier_table[num];
2234         barrier->on ^= 1;
2235         auto newwall = states[barrier->on];
2236         ::tilemap[x][y] = static_cast<uint8_t>(states[barrier->on]);
2237 
2238         // BBi
2239         ::set_cross_barrier_state(num);
2240         // BBi
2241 
2242         ::DisplaySwitchOperateMsg(num);
2243         ::sd_play_player_sound(::SWITCHSND, bstone::AC_ITEM);
2244 
2245         auto tile = &::tilemap[0][0];
2246         auto actor = reinterpret_cast<size_t*>(&::actorat[0][0]);
2247 
2248         for (int mapx = 0; mapx < MAPSIZE; ++mapx) {
2249             for (int mapy = 0; mapy < MAPSIZE; ++mapy) {
2250                 if (*tile == OFF_SWITCH || *tile == ON_SWITCH) {
2251                     auto icon = *(::mapsegs[1] + ::farmapylookup[mapy] + mapx);
2252 
2253                     if (icon == iconnum) {
2254                         *tile = static_cast<uint8_t>(newwall);
2255                         *actor = newwall;
2256                     }
2257                 }
2258 
2259                 ++tile;
2260                 ++actor;
2261             }
2262         }
2263     } else {
2264         DISPLAY_TIMED_MSG(::SwitchNotActivateMsg, MP_WALLSWITCH_OPERATE, MT_GENERAL);
2265         ::sd_play_player_sound(::NOWAYSND, bstone::AC_NO_WAY);
2266     }
2267 }
2268 
2269 // --------------------------------------------------------------------------
2270 // DisplaySwitchOperateMsg() - Displays the Operating Barrier Switch message
2271 //      for a particular level across the InfoArea.
2272 // --------------------------------------------------------------------------
DisplaySwitchOperateMsg(int coords)2273 void DisplaySwitchOperateMsg(
2274     int coords)
2275 {
2276     const auto barrier = &::gamestate.barrier_table[coords];
2277 
2278     static std::string message;
2279 
2280     if (barrier->on != 0) {
2281         message = "\r\r  ACTIVATING BARRIER";
2282     } else {
2283         message = "\r\r DEACTIVATING BARRIER";
2284     }
2285 
2286     if (!::is_ps()) {
2287         int level = barrier->level;
2288 
2289         if (level == 0xFF) {
2290             level = ::gamestate.mapon;
2291         }
2292 
2293         message += "\r      ON FLOOR " + std::to_string(level);
2294     }
2295 
2296     DISPLAY_TIMED_MSG(message.c_str(), MP_WALLSWITCH_OPERATE, MT_GENERAL);
2297 }
2298 
2299 // --------------------------------------------------------------------------
2300 // UpdateBarrierTable(x,y,level) - Finds/Inserts arc entry in arc list
2301 //
2302 // RETURNS: Offset into barrier_table[] for a particular arc
2303 //
2304 // --------------------------------------------------------------------------
UpdateBarrierTable(uint8_t level,uint8_t x,uint8_t y,bool OnOff)2305 uint16_t UpdateBarrierTable(
2306     uint8_t level,
2307     uint8_t x,
2308     uint8_t y,
2309     bool OnOff)
2310 {
2311     //
2312     // Scan Table...
2313     //
2314 
2315     auto barrier = ::gamestate.barrier_table;
2316 
2317     for (uint16_t num = 0; num < MAX_BARRIER_SWITCHES; ++num, ++barrier) {
2318         if (barrier->coord.tilex == x && barrier->coord.tiley == y) {
2319             return num;
2320         } else {
2321             if (barrier->on == 0xFF) { // Empty?
2322                 // We have hit end of list - Add
2323                 barrier->level = level;
2324                 barrier->coord.tilex = x;
2325                 barrier->coord.tiley = y;
2326                 barrier->on = static_cast<uint8_t>(OnOff);
2327                 return num;
2328             }
2329         }
2330     }
2331 
2332     ::Quit("Too many different (coords) barriers hooked up to switches");
2333 
2334     return 0;
2335 }
2336 
2337 // --------------------------------------------------------------------------
2338 // ScanBarrierTable(x,y) - Scans a switch table for a arc in this level
2339 //
2340 // RETURNS :
2341 //      0xFFFF - Not found in table
2342 //      barrier - barrier_table of the barrier for [num]
2343 //
2344 // --------------------------------------------------------------------------
ScanBarrierTable(uint8_t x,uint8_t y)2345 uint16_t ScanBarrierTable(
2346     uint8_t x,
2347     uint8_t y)
2348 {
2349     auto barrier = ::gamestate.barrier_table;
2350 
2351     for (uint16_t num = 0; num < MAX_BARRIER_SWITCHES; ++num, ++barrier) {
2352         if (barrier->coord.tilex == x && barrier->coord.tiley == y) {
2353             // Found switch...
2354             return num;
2355         }
2356     }
2357 
2358     return 0xFFFF; // Mark as EMPTY
2359 }
2360 
2361 // --------------------------------------------------------------------------
2362 // Checks to see if the Barrier obj is free
2363 // --------------------------------------------------------------------------
CheckActor(objtype * actor,uint16_t code)2364 bool CheckActor(
2365     objtype* actor,
2366     uint16_t code)
2367 {
2368     if (static_cast<uint16_t>(actor->temp2) == 0xFFFF) { // Is this actor free?
2369         //
2370         // Connect actor to barrier switch (code is index into barrier table)
2371         //
2372 
2373         actor->temp2 = static_cast<int16_t>(code); // This actor is NO longer a cycle actor.
2374         return true;
2375     }
2376 
2377     return false;
2378 }
2379 
CheckAndConnect(int8_t x,int8_t y,uint16_t code)2380 int16_t CheckAndConnect(
2381     int8_t x,
2382     int8_t y,
2383     uint16_t code)
2384 {
2385     const int8_t offsets[] = { -1, 0, 1, 0 };
2386 
2387     auto ob = objlist;
2388 
2389     do {
2390         switch (ob->obclass) {
2391         case arc_barrierobj:
2392         case post_barrierobj:
2393         case vpost_barrierobj:
2394         case vspike_barrierobj: {
2395             for (int loop = 0; loop < 4; loop++) {
2396                 if ((ob->tilex == x + offsets[loop]) &&
2397                     (ob->tiley == y + offsets[3 - loop]))
2398                 {
2399                     ++::bars_connected;
2400 
2401                     if (::CheckActor(ob, code)) {
2402                         ::CheckAndConnect(
2403                             x + offsets[loop],
2404                             y + offsets[3 - loop],
2405                             code);
2406                     }
2407                 }
2408             }
2409 
2410             break;
2411         }
2412 
2413         default:
2414             break;
2415         }
2416 
2417         ob = ob->next;
2418     } while (ob);
2419 
2420     return bars_connected;
2421 }
2422 
2423 // --------------------------------------------------------------------------
2424 // ConnectBarriers() - Scans the object list and finds the single barrier
2425 //      that is connected by switch and checks to see if
2426 //      there are any surrounding barriers that need to be
2427 //      connected also.
2428 // --------------------------------------------------------------------------
ConnectBarriers()2429 void ConnectBarriers()
2430 {
2431     auto barrier = ::gamestate.barrier_table;
2432 
2433     for (uint16_t num = 0; num < MAX_BARRIER_SWITCHES; ++num, ++barrier) {
2434         if (barrier->on != 0xFF && barrier->level == 0xFF) {
2435             ::bars_connected = 0;
2436 
2437             if (::CheckAndConnect(
2438                     barrier->coord.tilex,
2439                     barrier->coord.tiley,
2440                     num) == 0)
2441             {
2442                 if (::is_ps()) {
2443                     ::Quit("A barrier switch was not connect to any barriers.");
2444                 }
2445 
2446                 auto actor =
2447                     ::actorat[barrier->coord.tilex][barrier->coord.tiley];
2448 
2449                 if (!actor) {
2450                     ::Quit("A barrier switch was not connect to any barriers.");
2451                 }
2452 
2453                 switch (actor->obclass) {
2454                 case arc_barrierobj:
2455                 case post_barrierobj:
2456                     break;
2457 
2458                 default:
2459                     ::Quit("A barrier switch was not connect to any barriers.");
2460                     break;
2461                 }
2462 
2463                 static_cast<void>(::CheckActor(actor, num));
2464             }
2465         }
2466     }
2467 }
2468 
2469 
2470 /*
2471 =============================================================================
2472 
2473     BARRIERS
2474 
2475 =============================================================================
2476 */
2477 
2478 extern statetype s_barrier_transition;
2479 
2480 void T_BarrierTransition(
2481     objtype* obj);
2482 
2483 void T_BarrierShutdown(
2484     objtype* obj);
2485 
2486 statetype s_barrier_transition = { 0, 0, 15, T_BarrierTransition, nullptr, &s_barrier_transition };
2487 statetype s_vpost_barrier = { 0, SPR_DEMO, 15, T_BarrierTransition, nullptr, &s_vpost_barrier };
2488 statetype s_spike_barrier = { 0, SPR_DEMO, 15, T_BarrierTransition, nullptr, &s_spike_barrier };
2489 statetype s_barrier_shutdown = { 0, 0, 15, T_BarrierShutdown, nullptr, &s_barrier_shutdown };
2490 
2491 
SpawnBarrier(enemy_t which,int16_t tilex,int16_t tiley,bool OnOff)2492 void SpawnBarrier(
2493     enemy_t which,
2494     int16_t tilex,
2495     int16_t tiley,
2496     bool OnOff)
2497 {
2498     nevermark = !OnOff;
2499     SpawnNewObj(tilex, tiley, &s_ofs_stand);
2500     nevermark = false;
2501 
2502     if (OnOff) {
2503         new_actor->flags = FL_OFFSET_STATES | FL_BARRIER | FL_FAKE_STATIC | FL_SOLID;
2504     } else {
2505         new_actor->flags = FL_OFFSET_STATES | FL_BARRIER;
2506     }
2507 
2508     new_actor->obclass = static_cast<classtype>(rentacopobj + which);
2509     new_actor->ammo = static_cast<uint8_t>(OnOff);
2510     new_actor->temp2 = ScanBarrierTable(static_cast<uint8_t>(tilex), static_cast<uint8_t>(tiley));
2511     new_actor->flags2 = (::is_ps() ? FL2_BFGSHOT_SOLID : 0);
2512 
2513     switch (which) {
2514     case en_arc_barrier:
2515         new_actor->flags2 |= (::is_ps() ? FL2_BFG_SHOOTABLE : 0);
2516 
2517         if (OnOff) {
2518             ::InitSmartSpeedAnim(
2519                 new_actor,
2520                 SPR_ELEC_ARC1,
2521                 US_RndT() % 3,
2522                 2,
2523                 at_CYCLE,
2524                 ad_FWD,
2525                 (::is_ps() ? 3 : 20) + (US_RndT() & (::is_ps() ? 3 : 7)));
2526 
2527             new_actor->lighting = LAMP_ON_SHADING;
2528         } else {
2529             NewState(new_actor, &s_barrier_transition);
2530             new_actor->temp3 = 0;
2531             new_actor->flags &= ~(FL_SOLID | FL_FAKE_STATIC);
2532             new_actor->flags |= (FL_NEVERMARK | FL_NONMARK);
2533             new_actor->lighting = 0;
2534             BARRIER_STATE(new_actor) = bt_OFF;
2535             new_actor->temp1 = SPR_ELEC_ARC4;
2536         }
2537         break;
2538 
2539     case en_post_barrier:
2540         if (OnOff) {
2541             ::InitSmartSpeedAnim(
2542                 new_actor,
2543                 SPR_ELEC_POST1,
2544                 US_RndT() % 3,
2545                 2,
2546                 at_CYCLE,
2547                 ad_FWD,
2548                 (::is_ps() ? 3 : 20) + (US_RndT() & (::is_ps() ? 3 : 7)));
2549 
2550             new_actor->lighting = LAMP_ON_SHADING;
2551         } else {
2552             NewState(new_actor, &s_barrier_transition);
2553             new_actor->temp3 = 0;
2554             new_actor->flags &= ~(FL_SOLID | FL_FAKE_STATIC);
2555             new_actor->flags |= (FL_NEVERMARK | FL_NONMARK);
2556             new_actor->lighting = 0;
2557             BARRIER_STATE(new_actor) = bt_OFF;
2558             new_actor->temp1 = SPR_ELEC_POST4;
2559         }
2560         break;
2561 
2562     case en_vpost_barrier:
2563         NewState(new_actor, &s_vpost_barrier);
2564         if (OnOff) {
2565             new_actor->temp1 = SPR_VPOST8 - SPR_VPOST1;
2566         }
2567         break;
2568 
2569 
2570     case en_vspike_barrier:
2571         NewState(new_actor, &s_spike_barrier);
2572         if (OnOff) {
2573             new_actor->temp1 = SPR_VSPIKE8 - SPR_VSPIKE1;
2574         }
2575         break;
2576 
2577     default:
2578         break;
2579     }
2580 
2581 }
2582 
2583 
2584 // ---------------------------------------------------------------------------
2585 // ToggleBarrier()
2586 //
2587 // OBJECT STATES:
2588 //
2589 // bt_ON -> bt_TURNING_OFF and think is changed to T_BarrierTrans
2590 //
2591 // ---------------------------------------------------------------------------
2592 
TurnPostOff(objtype * obj)2593 void TurnPostOff(
2594     objtype* obj)
2595 {
2596     NewState(obj, &s_barrier_transition);
2597     obj->temp3 = 0;
2598     obj->flags &= ~(FL_SOLID | FL_FAKE_STATIC);
2599     obj->flags |= (FL_NEVERMARK | FL_NONMARK);
2600     obj->lighting = 0;
2601     BARRIER_STATE(obj) = bt_OFF;
2602 
2603 }
2604 
TurnPostOn(objtype * obj)2605 void TurnPostOn(
2606     objtype* obj)
2607 {
2608     obj->flags |= (FL_SOLID | FL_FAKE_STATIC);
2609     obj->flags &= ~(FL_NEVERMARK | FL_NONMARK);
2610     obj->lighting = LAMP_ON_SHADING;
2611     BARRIER_STATE(obj) = bt_ON;
2612 }
2613 
ToggleBarrier(objtype * obj)2614 void ToggleBarrier(
2615     objtype* obj)
2616 {
2617     switch (BARRIER_STATE(obj)) {
2618     case bt_ON: // Same as closed
2619     case bt_CLOSING:
2620         //
2621         // Turn OFF/Open
2622         //
2623 
2624         switch (obj->obclass) {
2625         case post_barrierobj:
2626             obj->temp1 = SPR_ELEC_POST4;
2627             TurnPostOff(obj);
2628             break;
2629 
2630         case arc_barrierobj:
2631             obj->temp1 = SPR_ELEC_ARC4;
2632             TurnPostOff(obj);
2633             break;
2634 
2635         case vpost_barrierobj:
2636         case vspike_barrierobj:
2637             BARRIER_STATE(obj) = bt_OPENING;
2638             break;
2639 
2640         default:
2641             break;
2642         }
2643 
2644         break;
2645 
2646     case bt_OFF: // Same as open
2647     case bt_OPENING:
2648         //
2649         // Turn ON/Closed
2650         //
2651 
2652         switch (obj->obclass) {
2653         case post_barrierobj:
2654             ::InitSmartSpeedAnim(
2655                 obj,
2656                 SPR_ELEC_POST1,
2657                 US_RndT() % 3,
2658                 2,
2659                 at_CYCLE,
2660                 ad_FWD,
2661                 (::is_ps() ? 3 : 20) + (US_RndT() & (::is_ps() ? 3 : 7)));
2662 
2663             TurnPostOn(obj);
2664             break;
2665 
2666         case arc_barrierobj:
2667             ::InitSmartSpeedAnim(
2668                 obj,
2669                 SPR_ELEC_ARC1,
2670                 US_RndT() % 3,
2671                 2,
2672                 at_CYCLE,
2673                 ad_FWD,
2674                 (::is_ps() ? 3 : 20) + (US_RndT() & (::is_ps() ? 3 : 7)));
2675 
2676             TurnPostOn(obj);
2677             break;
2678 
2679         case vpost_barrierobj:
2680         case vspike_barrierobj:
2681             BARRIER_STATE(obj) = bt_CLOSING;
2682             break;
2683 
2684         default:
2685             break;
2686         }
2687         break;
2688 
2689     default:
2690         break;
2691     }
2692 }
2693 
2694 // ---------------------------------------------------------------------------
2695 // T_BarrierShutdown() - This is used ONLY for electric arc barriers
2696 //      which "flicker" out when disabled/destroyed.
2697 //
2698 // ---------------------------------------------------------------------------
T_BarrierShutdown(objtype * obj)2699 void T_BarrierShutdown(
2700     objtype* obj)
2701 {
2702     switch (BARRIER_STATE(obj)) {
2703     case bt_DISABLING:
2704         if (obj->hitpoints) {
2705             if (obj->temp2 < tics) {
2706                 if (obj->temp1 == SPR_ELEC_ARC4) {
2707                     obj->temp1 = SPR_ELEC_ARC1 + obj->temp3;
2708                     obj->temp3 = (obj->temp3 + 1) % 4;
2709 
2710                     ::sd_play_actor_sound(
2711                         ELECARCDAMAGESND, obj, bstone::AC_WEAPON);
2712 
2713                     obj->flags |= (FL_SOLID | FL_FAKE_STATIC);
2714                     obj->flags &= ~(FL_NEVERMARK | FL_NONMARK);
2715                     obj->lighting = LAMP_ON_SHADING;
2716                     obj->temp2 = US_RndT() & 0x7;
2717                 } else {
2718                     obj->temp1 = SPR_ELEC_ARC4;
2719                     obj->flags &= ~(FL_SOLID | FL_FAKE_STATIC);
2720                     obj->flags |= (FL_NEVERMARK | FL_NONMARK);
2721                     obj->lighting = 0;
2722                     obj->temp2 = 5 + (US_RndT() & 0xf);
2723                 }
2724 
2725                 obj->hitpoints--;
2726             } else {
2727                 obj->temp2 -= tics;
2728             }
2729         } else {
2730             BARRIER_STATE(obj) = bt_DISABLED;
2731         }
2732         break;
2733 
2734     case bt_DISABLED:
2735         obj->flags |= (FL_NEVERMARK | FL_NONMARK);
2736         obj->flags &= ~(FL_SOLID | FL_FAKE_STATIC);
2737         obj->lighting = 0;
2738         if (obj->obclass == post_barrierobj) {
2739             obj->temp1 = SPR_ELEC_POST4;
2740         } else {
2741             obj->temp1 = SPR_ELEC_ARC4;
2742         }
2743         obj->temp3 = 0;
2744         NewState(obj, &s_ofs_smart_anim);
2745         break;
2746 
2747     }
2748 }
2749 
T_BarrierTransition(objtype * obj)2750 void T_BarrierTransition(
2751     objtype* obj)
2752 {
2753     fixed dx, dy;
2754 
2755     switch (BARRIER_STATE(obj)) {
2756     //
2757     // ON/CLOSED POSITION
2758     //
2759     case bt_ON:
2760         //
2761         // Check for cycle barrier.
2762         //
2763         if ((uint16_t)obj->temp2 == 0xFFFF) {
2764             if (obj->temp3 < tics) {
2765                 ToggleBarrier(obj);
2766             } else {
2767                 obj->temp3 -= tics;
2768             }
2769         } else if (!::gamestate.barrier_table[obj->temp2].on) {
2770             ToggleBarrier(obj);
2771         }
2772         break;
2773 
2774 
2775     //
2776     // OFF/OPEN POSITION
2777     //
2778     case bt_OFF:
2779         //
2780         // Check for cycle barrier.
2781         //
2782         if ((uint16_t)obj->temp2 == 0xFFFF) {
2783             if (obj->temp3 < tics) {
2784                 ToggleBarrier(obj);
2785             } else {
2786                 obj->temp3 -= tics;
2787             }
2788         } else if (::gamestate.barrier_table[obj->temp2].on) {
2789             ToggleBarrier(obj);
2790         }
2791         break;
2792 
2793     //
2794     // CLOSING/TURNING ON
2795     //
2796     case bt_CLOSING:
2797         // Check for damaging the player
2798 
2799         dx = player->x - obj->x;
2800         dx = LABS(dx);
2801         dy = player->y - obj->y;
2802         dy = LABS(dy);
2803 
2804         if (dy <= 0x17FFF && dx <= 0x17FFF) {
2805             if (dy <= 0x7FFF && dx <= 0x7FFF) {
2806                 TakeDamage(2, obj);
2807             }
2808             break;
2809         }
2810 
2811         if (obj->temp3 < tics) {
2812             obj->temp3 = VPOST_BARRIER_SPEED;
2813             obj->temp1++;
2814 
2815             if (obj->temp1 > 7) {
2816                 obj->temp1 = 7; // Errors...
2817 
2818             }
2819             switch (obj->temp1) {
2820             case 3:
2821                 // Closed enough to be solid
2822                 //
2823 
2824                 obj->flags |= (FL_SOLID | FL_FAKE_STATIC);
2825                 obj->flags &= ~(FL_NEVERMARK | FL_NONMARK);
2826                 break;
2827 
2828             case 7:
2829                 // Fully closed dude!
2830                 //
2831 
2832                 BARRIER_STATE(obj) = bt_ON;
2833                 //
2834                 // Check for cycle barrier.
2835                 //
2836                 if ((uint16_t)obj->temp2 == 0xFFFF) {
2837                     obj->temp3 = VPOST_WAIT_DELAY;
2838                 }
2839                 break;
2840             }
2841         } else {
2842             obj->temp3 -= tics;
2843         }
2844 
2845         // Check to see if to was toggled
2846 
2847         if ((uint16_t)obj->temp2 != 0xFFFF) {
2848             if (!::gamestate.barrier_table[obj->temp2].on) {
2849                 ToggleBarrier(obj);
2850             }
2851         }
2852         break;
2853 
2854     //
2855     // OPENING/TURNNING OFF
2856     //
2857     case bt_OPENING:
2858         if (obj->temp3 < tics) {
2859             obj->temp3 = VPOST_BARRIER_SPEED;
2860             obj->temp1--;
2861 
2862             if (obj->temp1 < 0) {
2863                 obj->temp1 = 0; // Errors...
2864 
2865 
2866             }
2867             switch (obj->temp1) {
2868             case 2:
2869                 //
2870                 // Open enough not to be solid.
2871 
2872                 obj->flags &= ~(FL_SOLID | FL_FAKE_STATIC);
2873                 obj->flags |= (FL_NEVERMARK | FL_NONMARK);
2874                 break;
2875 
2876             case 0:
2877                 // Fully Open dude!
2878                 //
2879 
2880                 BARRIER_STATE(obj) = bt_OFF;
2881 
2882                 //
2883                 // Check for cycle barrier.
2884                 //
2885                 if ((uint16_t)obj->temp2 == 0xFFFF) {
2886                     obj->temp3 = VPOST_WAIT_DELAY;
2887                 }
2888                 break;
2889             }
2890         } else {
2891             obj->temp3 -= tics;
2892         }
2893 
2894         // Check to see if to was toggled
2895 
2896         if ((uint16_t)obj->temp2 != 0xFFFF) {
2897             if (::gamestate.barrier_table[obj->temp2].on) {
2898                 ToggleBarrier(obj);
2899             }
2900         }
2901         break;
2902     }
2903 }
2904 
2905 
2906 /*
2907 =============================================================================
2908 
2909  GUARD
2910 
2911 =============================================================================
2912 */
2913 
2914 //
2915 // Rent-A-Cop
2916 //
2917 
2918 extern statetype s_rent_stand;
2919 
2920 extern statetype s_rent_path1;
2921 extern statetype s_rent_path1s;
2922 extern statetype s_rent_path2;
2923 extern statetype s_rent_path3;
2924 extern statetype s_rent_path3s;
2925 extern statetype s_rent_path4;
2926 
2927 extern statetype s_rent_pain;
2928 
2929 extern statetype s_rent_giveup;
2930 
2931 extern statetype s_rent_shoot1;
2932 extern statetype s_rent_shoot2;
2933 extern statetype s_rent_shoot3;
2934 extern statetype s_rent_shoot4;
2935 
2936 extern statetype s_rent_chase1;
2937 extern statetype s_rent_chase1s;
2938 extern statetype s_rent_chase2;
2939 extern statetype s_rent_chase3;
2940 extern statetype s_rent_chase3s;
2941 extern statetype s_rent_chase4;
2942 
2943 extern statetype s_rent_die1;
2944 extern statetype s_rent_die1d;
2945 extern statetype s_rent_die2;
2946 extern statetype s_rent_die3;
2947 extern statetype s_rent_die3s;
2948 extern statetype s_rent_die4;
2949 
2950 statetype s_rent_stand = { 1, SPR_DEMO, 0, T_Stand, nullptr, &s_rent_stand };
2951 
2952 statetype s_rent_path1 = { 1, SPR_DEMO, 20, T_Path, nullptr, &s_rent_path1s };
2953 statetype s_rent_path1s = { 1, SPR_DEMO, 5, nullptr, nullptr, &s_rent_path2 };
2954 statetype s_rent_path2 = { 1, SPR_DEMO, 15, T_Path, nullptr, &s_rent_path3 };
2955 statetype s_rent_path3 = { 1, SPR_DEMO, 20, T_Path, nullptr, &s_rent_path3s };
2956 statetype s_rent_path3s = { 1, SPR_DEMO, 5, nullptr, nullptr, &s_rent_path4 };
2957 statetype s_rent_path4 = { 1, SPR_DEMO, 15, T_Path, nullptr, &s_rent_path1 };
2958 
2959 statetype s_rent_pain = { 2, SPR_DEMO, 15, nullptr, nullptr, &s_rent_chase1 };
2960 
2961 statetype s_rent_shoot1 = { 0, SPR_DEMO, 20, nullptr, nullptr, &s_rent_shoot2 };
2962 statetype s_rent_shoot2 = { 0, SPR_DEMO, 20, nullptr, T_Shoot, &s_rent_shoot3 };
2963 statetype s_rent_shoot3 = { 0, SPR_DEMO, 20, nullptr, T_Shade, &s_rent_chase1 };
2964 
2965 statetype s_rent_chase1 = { 1, SPR_DEMO, 10, T_Chase, nullptr, &s_rent_chase1s };
2966 statetype s_rent_chase1s = { 1, SPR_DEMO, 3, nullptr, nullptr, &s_rent_chase2 };
2967 statetype s_rent_chase2 = { 1, SPR_DEMO, 8, T_Chase, nullptr, &s_rent_chase3 };
2968 statetype s_rent_chase3 = { 1, SPR_DEMO, 10, T_Chase, nullptr, &s_rent_chase3s };
2969 statetype s_rent_chase3s = { 1, SPR_DEMO, 3, nullptr, nullptr, &s_rent_chase4 };
2970 statetype s_rent_chase4 = { 1, SPR_DEMO, 8, T_Chase, nullptr, &s_rent_chase1 };
2971 
2972 statetype s_rent_die1 = { 0, SPR_DEMO, 17, T_BlowBack, A_DeathScream, &s_rent_die2 };
2973 statetype s_rent_die2 = { 0, SPR_DEMO, 21, T_BlowBack, nullptr, &s_rent_die3 };
2974 statetype s_rent_die3 = { 0, SPR_DEMO, 16, T_BlowBack, nullptr, &s_rent_die3s };
2975 statetype s_rent_die3s = { 0, SPR_DEMO, 13, T_BlowBack, nullptr, &s_rent_die4 };
2976 statetype s_rent_die4 = { 0, SPR_DEMO, 0, nullptr, nullptr, &s_rent_die4 };
2977 
2978 
2979 //
2980 // officers
2981 //
2982 
2983 extern statetype s_ofcstand;
2984 
2985 extern statetype s_ofcpath1;
2986 extern statetype s_ofcpath1s;
2987 extern statetype s_ofcpath2;
2988 extern statetype s_ofcpath3;
2989 extern statetype s_ofcpath3s;
2990 extern statetype s_ofcpath4;
2991 
2992 extern statetype s_ofcpain;
2993 
2994 extern statetype s_ofcgiveup;
2995 
2996 extern statetype s_ofcshoot1;
2997 extern statetype s_ofcshoot2;
2998 extern statetype s_ofcshoot3;
2999 extern statetype s_ofcshoot4;
3000 
3001 extern statetype s_ofcchase1;
3002 extern statetype s_ofcchase1s;
3003 extern statetype s_ofcchase2;
3004 extern statetype s_ofcchase3;
3005 extern statetype s_ofcchase3s;
3006 extern statetype s_ofcchase4;
3007 
3008 extern statetype s_ofcdie1;
3009 extern statetype s_ofcdie2;
3010 extern statetype s_ofcdie3;
3011 extern statetype s_ofcdie4;
3012 extern statetype s_ofcdie5;
3013 
3014 statetype s_ofcstand = { 1, SPR_DEMO, 0, T_Stand, nullptr, &s_ofcstand };
3015 
3016 statetype s_ofcpath1 = { 1, SPR_DEMO, 20, T_Path, nullptr, &s_ofcpath1s };
3017 statetype s_ofcpath1s = { 1, SPR_DEMO, 5, nullptr, nullptr, &s_ofcpath2 };
3018 statetype s_ofcpath2 = { 1, SPR_DEMO, 15, T_Path, nullptr, &s_ofcpath3 };
3019 statetype s_ofcpath3 = { 1, SPR_DEMO, 20, T_Path, nullptr, &s_ofcpath3s };
3020 statetype s_ofcpath3s = { 1, SPR_DEMO, 5, nullptr, nullptr, &s_ofcpath4 };
3021 statetype s_ofcpath4 = { 1, SPR_DEMO, 15, T_Path, nullptr, &s_ofcpath1 };
3022 
3023 statetype s_ofcpain = { 2, SPR_DEMO, 15, nullptr, nullptr, &s_ofcchase1 };
3024 
3025 statetype s_ofcshoot1 = { 0, SPR_DEMO, 6, nullptr, nullptr, &s_ofcshoot2 };
3026 statetype s_ofcshoot2 = { 0, SPR_DEMO, 20, nullptr, T_Shoot, &s_ofcshoot3 };
3027 statetype s_ofcshoot3 = { 0, SPR_DEMO, 10, nullptr, T_Shade, &s_ofcchase1 };
3028 
3029 statetype s_ofcchase1 = { 1, SPR_DEMO, 10, T_Chase, nullptr, &s_ofcchase1s };
3030 statetype s_ofcchase1s = { 1, SPR_DEMO, 3, nullptr, nullptr, &s_ofcchase2 };
3031 statetype s_ofcchase2 = { 1, SPR_DEMO, 8, T_Chase, nullptr, &s_ofcchase3 };
3032 statetype s_ofcchase3 = { 1, SPR_DEMO, 10, T_Chase, nullptr, &s_ofcchase3s };
3033 statetype s_ofcchase3s = { 1, SPR_DEMO, 3, nullptr, nullptr, &s_ofcchase4 };
3034 statetype s_ofcchase4 = { 1, SPR_DEMO, 8, T_Chase, nullptr, &s_ofcchase1 };
3035 
3036 statetype s_ofcdie1 = { 0, SPR_DEMO, 15, T_BlowBack, A_DeathScream, &s_ofcdie2 };
3037 statetype s_ofcdie2 = { 0, SPR_DEMO, 15, T_BlowBack, nullptr, &s_ofcdie3 };
3038 statetype s_ofcdie3 = { 0, SPR_DEMO, 15, T_BlowBack, nullptr, &s_ofcdie4 };
3039 statetype s_ofcdie4 = { 0, SPR_DEMO, 15, T_BlowBack, nullptr, &s_ofcdie5 };
3040 statetype s_ofcdie5 = { 0, SPR_DEMO, 0, nullptr, nullptr, &s_ofcdie5 };
3041 
3042 
3043 //
3044 // SWAT
3045 //
3046 
3047 extern statetype s_swatstand;
3048 
3049 extern statetype s_swatpath1;
3050 extern statetype s_swatpath1s;
3051 extern statetype s_swatpath2;
3052 extern statetype s_swatpath3;
3053 extern statetype s_swatpath3s;
3054 extern statetype s_swatpath4;
3055 
3056 extern statetype s_swatpain;
3057 
3058 extern statetype s_swatgiveup;
3059 
3060 extern statetype s_swatshoot1;
3061 extern statetype s_swatshoot2;
3062 extern statetype s_swatshoot3;
3063 extern statetype s_swatshoot4;
3064 extern statetype s_swatshoot5;
3065 extern statetype s_swatshoot6;
3066 extern statetype s_swatshoot7;
3067 
3068 extern statetype s_swatchase1;
3069 extern statetype s_swatchase1s;
3070 extern statetype s_swatchase2;
3071 extern statetype s_swatchase3;
3072 extern statetype s_swatchase3s;
3073 extern statetype s_swatchase4;
3074 
3075 extern statetype s_swatwounded1;
3076 extern statetype s_swatwounded2;
3077 extern statetype s_swatwounded3;
3078 extern statetype s_swatwounded4;
3079 
3080 extern statetype s_swatunwounded1;
3081 extern statetype s_swatunwounded2;
3082 extern statetype s_swatunwounded3;
3083 extern statetype s_swatunwounded4;
3084 
3085 extern statetype s_swatdie1;
3086 extern statetype s_swatdie2;
3087 extern statetype s_swatdie3;
3088 extern statetype s_swatdie4;
3089 extern statetype s_swatdie5;
3090 
3091 
3092 statetype s_swatstand = { 1, SPR_DEMO, 0, T_Stand, nullptr, &s_swatstand };
3093 
3094 statetype s_swatpath1 = { 1, SPR_DEMO, 20, T_Path, nullptr, &s_swatpath1s };
3095 statetype s_swatpath1s = { 1, SPR_DEMO, 5, nullptr, nullptr, &s_swatpath2 };
3096 statetype s_swatpath2 = { 1, SPR_DEMO, 15, T_Path, nullptr, &s_swatpath3 };
3097 statetype s_swatpath3 = { 1, SPR_DEMO, 20, T_Path, nullptr, &s_swatpath3s };
3098 statetype s_swatpath3s = { 1, SPR_DEMO, 5, nullptr, nullptr, &s_swatpath4 };
3099 statetype s_swatpath4 = { 1, SPR_DEMO, 15, T_Path, nullptr, &s_swatpath1 };
3100 
3101 statetype s_swatpain = { 2, SPR_DEMO, 15, nullptr, nullptr, &s_swatshoot1 };
3102 
3103 statetype s_swatshoot1 = { 0, SPR_DEMO, 10, nullptr, nullptr, &s_swatshoot2 };
3104 statetype s_swatshoot2 = { 0, SPR_DEMO, 20, nullptr, T_Shoot, &s_swatshoot3 };
3105 statetype s_swatshoot3 = { 0, SPR_DEMO, 10, nullptr, T_Shade, &s_swatshoot4 };
3106 statetype s_swatshoot4 = { 0, SPR_DEMO, 10, nullptr, T_Shoot, &s_swatshoot5 };
3107 statetype s_swatshoot5 = { 0, SPR_DEMO, 10, nullptr, T_Shade, &s_swatshoot6 };
3108 statetype s_swatshoot6 = { 0, SPR_DEMO, 10, nullptr, T_Shoot, &s_swatshoot7 };
3109 statetype s_swatshoot7 = { 0, SPR_DEMO, 10, nullptr, T_Shade, &s_swatchase1 };
3110 
3111 statetype s_swatchase1 = { 1, SPR_DEMO, 10, T_Chase, nullptr, &s_swatchase1s };
3112 statetype s_swatchase1s = { 1, SPR_DEMO, 3, nullptr, nullptr, &s_swatchase2 };
3113 statetype s_swatchase2 = { 1, SPR_DEMO, 8, T_Chase, nullptr, &s_swatchase3 };
3114 statetype s_swatchase3 = { 1, SPR_DEMO, 10, T_Chase, nullptr, &s_swatchase3s };
3115 statetype s_swatchase3s = { 1, SPR_DEMO, 3, nullptr, nullptr, &s_swatchase4 };
3116 statetype s_swatchase4 = { 1, SPR_DEMO, 8, T_Chase, nullptr, &s_swatchase1 };
3117 
3118 statetype s_swatwounded1 = { 0, SPR_DEMO, 10, nullptr, nullptr, &s_swatwounded2 };
3119 statetype s_swatwounded2 = { 0, SPR_DEMO, 10, nullptr, nullptr, &s_swatwounded3 };
3120 statetype s_swatwounded3 = { 0, SPR_DEMO, 10, nullptr, nullptr, &s_swatwounded4 };
3121 statetype s_swatwounded4 = { 0, SPR_DEMO, 10, T_SwatWound, nullptr, &s_swatwounded4 };
3122 
3123 statetype s_swatunwounded1 = { 0, SPR_DEMO, 10, nullptr, nullptr, &s_swatunwounded2 };
3124 statetype s_swatunwounded2 = { 0, SPR_DEMO, 10, nullptr, nullptr, &s_swatunwounded3 };
3125 statetype s_swatunwounded3 = { 0, SPR_DEMO, 10, nullptr, nullptr, &s_swatunwounded4 };
3126 statetype s_swatunwounded4 = { 0, SPR_DEMO, 10, nullptr, T_SwatWound, &s_swatchase1 };
3127 
3128 statetype s_swatdie1 = { 0, SPR_DEMO, 20, T_BlowBack, A_DeathScream, &s_swatdie2 };
3129 statetype s_swatdie2 = { 0, SPR_DEMO, 20, T_BlowBack, nullptr, &s_swatdie3 };
3130 statetype s_swatdie3 = { 0, SPR_DEMO, 20, T_BlowBack, nullptr, &s_swatdie4 };
3131 statetype s_swatdie4 = { 0, SPR_DEMO, 20, T_BlowBack, nullptr, &s_swatdie5 };
3132 statetype s_swatdie5 = { 0, SPR_DEMO, 0, nullptr, nullptr, &s_swatdie5 };
3133 
3134 
3135 //
3136 // PRO Guard
3137 //
3138 
3139 extern statetype s_prostand;
3140 
3141 extern statetype s_propath1;
3142 extern statetype s_propath1s;
3143 extern statetype s_propath2;
3144 extern statetype s_propath3;
3145 extern statetype s_propath3s;
3146 extern statetype s_propath4;
3147 
3148 extern statetype s_propain;
3149 
3150 extern statetype s_proshoot1;
3151 extern statetype s_proshoot2;
3152 extern statetype s_proshoot3;
3153 extern statetype s_proshoot4;
3154 extern statetype s_proshoot5;
3155 extern statetype s_proshoot6;
3156 extern statetype s_proshoot6a;
3157 
3158 extern statetype s_prochase1;
3159 extern statetype s_prochase1s;
3160 extern statetype s_prochase2;
3161 extern statetype s_prochase3;
3162 extern statetype s_prochase3s;
3163 extern statetype s_prochase4;
3164 
3165 extern statetype s_prodie1;
3166 extern statetype s_prodie2;
3167 extern statetype s_prodie3;
3168 extern statetype s_prodie3a;
3169 extern statetype s_prodie4;
3170 
3171 statetype s_prostand = { 1, SPR_DEMO, 0, T_Stand, nullptr, &s_prostand };
3172 
3173 statetype s_propath1 = { 1, SPR_DEMO, 20, T_Path, nullptr, &s_propath1s };
3174 statetype s_propath1s = { 1, SPR_DEMO, 5, nullptr, nullptr, &s_propath2 };
3175 statetype s_propath2 = { 1, SPR_DEMO, 15, T_Path, nullptr, &s_propath3 };
3176 statetype s_propath3 = { 1, SPR_DEMO, 20, T_Path, nullptr, &s_propath3s };
3177 statetype s_propath3s = { 1, SPR_DEMO, 5, nullptr, nullptr, &s_propath4 };
3178 statetype s_propath4 = { 1, SPR_DEMO, 15, T_Path, nullptr, &s_propath1 };
3179 
3180 statetype s_propain = { 2, SPR_DEMO, 15, nullptr, nullptr, &s_prochase1 };
3181 
3182 statetype s_proshoot1 = { 0, SPR_DEMO, 20, nullptr, nullptr, &s_proshoot2 };
3183 statetype s_proshoot2 = { 0, SPR_DEMO, 20, nullptr, T_Shoot, &s_proshoot3 };
3184 statetype s_proshoot3 = { 0, SPR_DEMO, 10, nullptr, T_Shade, &s_proshoot4 };
3185 statetype s_proshoot4 = { 0, SPR_DEMO, 10, nullptr, T_Shoot, &s_proshoot5 };
3186 statetype s_proshoot5 = { 0, SPR_DEMO, 10, nullptr, T_Shade, &s_proshoot6 };
3187 statetype s_proshoot6 = { 0, SPR_DEMO, 10, nullptr, T_Shoot, &s_proshoot6a };
3188 statetype s_proshoot6a = { 0, SPR_DEMO, 10, nullptr, T_Shade, &s_prochase1 };
3189 
3190 statetype s_prochase1 = { 1, SPR_DEMO, 10, T_Chase, nullptr, &s_prochase1s };
3191 statetype s_prochase1s = { 1, SPR_DEMO, 3, nullptr, nullptr, &s_prochase2 };
3192 statetype s_prochase2 = { 1, SPR_DEMO, 8, T_Chase, nullptr, &s_prochase3 };
3193 statetype s_prochase3 = { 1, SPR_DEMO, 10, T_Chase, nullptr, &s_prochase3s };
3194 statetype s_prochase3s = { 1, SPR_DEMO, 3, nullptr, nullptr, &s_prochase4 };
3195 statetype s_prochase4 = { 1, SPR_DEMO, 8, T_Chase, nullptr, &s_prochase1 };
3196 
3197 statetype s_prodie1 = { 0, SPR_DEMO, 20, T_BlowBack, A_DeathScream, &s_prodie2 };
3198 statetype s_prodie2 = { 0, SPR_DEMO, 20, T_BlowBack, nullptr, &s_prodie3 };
3199 statetype s_prodie3 = { 0, SPR_DEMO, 20, T_BlowBack, nullptr, &s_prodie3a };
3200 statetype s_prodie3a = { 0, SPR_DEMO, 20, T_BlowBack, nullptr, &s_prodie4 };
3201 statetype s_prodie4 = { 0, SPR_DEMO, 0, nullptr, nullptr, &s_prodie4 };
3202 
3203 extern statetype s_electro_appear1;
3204 extern statetype s_electro_appear2;
3205 extern statetype s_electro_appear3;
3206 
3207 extern statetype s_electro_chase1;
3208 extern statetype s_electro_chase2;
3209 extern statetype s_electro_chase3;
3210 extern statetype s_electro_chase4;
3211 
3212 extern statetype s_electro_ouch;
3213 
3214 extern statetype s_electro_shoot1;
3215 extern statetype s_electro_shoot2;
3216 extern statetype s_electro_shoot3;
3217 
3218 extern statetype s_electro_shot1;
3219 extern statetype s_electro_shot2;
3220 
3221 extern statetype s_electro_shot_exp1;
3222 extern statetype s_electro_shot_exp2;
3223 
3224 extern statetype s_ofs_shot1;
3225 extern statetype s_ofs_shot2;
3226 
3227 extern statetype s_ofs_shot_exp1;
3228 extern statetype s_ofs_shot_exp2;
3229 
3230 extern statetype s_electro_die1;
3231 extern statetype s_electro_die2;
3232 extern statetype s_electro_die3;
3233 
3234 statetype s_electro_appear1 = { 0, SPR_DEMO, 14, nullptr, nullptr, &s_electro_appear2 };
3235 statetype s_electro_appear2 = { 0, SPR_DEMO, 14, nullptr, nullptr, &s_electro_appear3 };
3236 statetype s_electro_appear3 = { 0, SPR_DEMO, 14, nullptr, nullptr, &s_electro_chase1 };
3237 
3238 statetype s_electro_chase1 = { 0, SPR_DEMO, 14, T_Chase, nullptr, &s_electro_chase2 };
3239 statetype s_electro_chase2 = { 0, SPR_DEMO, 14, nullptr, nullptr, &s_electro_chase3 };
3240 statetype s_electro_chase3 = { 0, SPR_DEMO, 14, T_Chase, nullptr, &s_electro_chase4 };
3241 statetype s_electro_chase4 = { 0, SPR_DEMO, 14, nullptr, nullptr, &s_electro_chase1 };
3242 
3243 statetype s_electro_ouch = { 0, SPR_DEMO, 14, nullptr, nullptr, &s_electro_chase1 };
3244 
3245 statetype s_electro_shoot1 = { 0, SPR_DEMO, 14, nullptr, nullptr, &s_electro_shoot2 };
3246 statetype s_electro_shoot2 = { 0, SPR_DEMO, 14, T_Shoot, nullptr, &s_electro_shoot3 };
3247 statetype s_electro_shoot3 = { 0, SPR_DEMO, 14, nullptr, nullptr, &s_electro_chase1 };
3248 
3249 statetype s_electro_shot1 = { 0, SPR_DEMO, 1, T_Projectile, nullptr, &s_electro_shot2 };
3250 statetype s_electro_shot2 = { 0, SPR_DEMO, 1, T_Projectile, nullptr, &s_electro_shot1 };
3251 
3252 statetype s_ofs_shot1 = { 0, 0, 1, T_Projectile, nullptr, &s_ofs_shot2 };
3253 statetype s_ofs_shot2 = { 0, 1, 1, T_Projectile, nullptr, &s_ofs_shot1 };
3254 
3255 
3256 statetype s_electro_die1 = { 0, SPR_DEMO, 14, nullptr, A_DeathScream, &s_electro_die2 };
3257 statetype s_electro_die2 = { 0, SPR_DEMO, 14, nullptr, nullptr, &s_electro_die3 };
3258 statetype s_electro_die3 = { 0, SPR_DEMO, 14, nullptr, nullptr, nullptr };
3259 
3260 
3261 extern statetype s_liquid_wait;
3262 extern statetype s_liquid_move;
3263 
3264 extern statetype s_liquid_rise1;
3265 extern statetype s_liquid_rise2;
3266 extern statetype s_liquid_rise3;
3267 extern statetype s_liquid_rise4;
3268 
3269 extern statetype s_liquid_stand;
3270 
3271 extern statetype s_liquid_fall1;
3272 extern statetype s_liquid_fall2;
3273 extern statetype s_liquid_fall3;
3274 extern statetype s_liquid_fall4;
3275 
3276 extern statetype s_liquid_shoot1;
3277 extern statetype s_liquid_shoot2;
3278 extern statetype s_liquid_shoot3;
3279 
3280 extern statetype s_liquid_ouch;
3281 
3282 extern statetype s_liquid_die1;
3283 extern statetype s_liquid_die2;
3284 extern statetype s_liquid_die3;
3285 extern statetype s_liquid_die4;
3286 extern statetype s_liquid_dead;
3287 
3288 extern statetype s_blake1;
3289 extern statetype s_blake2;
3290 extern statetype s_blake3;
3291 extern statetype s_blake4;
3292 
3293 extern statetype s_liquid_shot;
3294 
3295 void T_LiquidStand_Check(
3296     objtype* obj);
3297 
3298 void T_LiquidMove(
3299     objtype* obj);
3300 
3301 void T_Solid(
3302     objtype* obj);
3303 
3304 
3305 statetype s_liquid_wait = { 0, SPR_DEMO, 14, T_Stand, nullptr, &s_liquid_wait };
3306 
3307 statetype s_liquid_move = { 0, SPR_DEMO, 14, T_LiquidMove, T_ChangeShape, &s_liquid_move };
3308 
3309 statetype s_liquid_rise1 = { 0, SPR_DEMO, 12, nullptr, nullptr, &s_liquid_rise2 };
3310 statetype s_liquid_rise2 = { 0, SPR_DEMO, 12, nullptr, nullptr, &s_liquid_rise3 };
3311 statetype s_liquid_rise3 = { 0, SPR_DEMO, 12, nullptr, T_Solid, &s_liquid_shoot1 };
3312 
3313 statetype s_liquid_stand = { 0, SPR_DEMO, 40, T_LiquidStand, nullptr, &s_liquid_stand };
3314 
3315 statetype s_liquid_fall1 = { 0, SPR_DEMO, 15, nullptr, nullptr, &s_liquid_fall2 };
3316 statetype s_liquid_fall2 = { 0, SPR_DEMO, 15, nullptr, nullptr, &s_liquid_fall3 };
3317 statetype s_liquid_fall3 = { 0, SPR_DEMO, 15, nullptr, nullptr, &s_liquid_move };
3318 
3319 statetype s_liquid_shoot1 = { 0, SPR_DEMO, 12, nullptr, nullptr, &s_liquid_shoot2 };
3320 statetype s_liquid_shoot2 = { 0, SPR_DEMO, 12, nullptr, nullptr, &s_liquid_shoot3 };
3321 statetype s_liquid_shoot3 = { 0, SPR_DEMO, 12, nullptr, T_Shoot, &s_liquid_stand };
3322 
3323 statetype s_liquid_ouch = { 0, SPR_DEMO, 7, nullptr, nullptr, &s_liquid_shoot1 };
3324 
3325 statetype s_liquid_die1 = { 0, SPR_DEMO, 20, nullptr, A_DeathScream, &s_liquid_die2 };
3326 statetype s_liquid_die2 = { 0, SPR_DEMO, 20, nullptr, nullptr, &s_liquid_die3 };
3327 statetype s_liquid_die3 = { 0, SPR_DEMO, 20, nullptr, nullptr, &s_liquid_die4 };
3328 statetype s_liquid_die4 = { 0, SPR_DEMO, 20, nullptr, nullptr, &s_liquid_dead };
3329 statetype s_liquid_dead = { 0, SPR_DEMO, 20, nullptr, nullptr, nullptr };
3330 
3331 statetype s_liquid_shot = { 0, 0, 10, T_Projectile, T_ChangeShape, &s_liquid_shot };
3332 
3333 statetype s_blake1 = { 0, SPR_DEMO, 12, nullptr, nullptr, &s_blake2 };
3334 statetype s_blake2 = { 0, SPR_DEMO, 12, nullptr, nullptr, &s_blake3 };
3335 statetype s_blake3 = { 0, SPR_DEMO, 12, nullptr, nullptr, &s_blake4 };
3336 statetype s_blake4 = { 0, SPR_DEMO, 12, nullptr, nullptr, &s_blake1 };
3337 
3338 
T_ChangeShape(objtype * obj)3339 void T_ChangeShape(
3340     objtype* obj)
3341 {
3342     obj->temp1 = obj->temp2 + (US_RndT() % 3);
3343 }
3344 
T_MakeOffset(objtype * obj)3345 void T_MakeOffset(
3346     objtype* obj)
3347 {
3348     obj->flags |= FL_OFFSET_STATES;
3349     obj->flags &= ~(FL_SOLID | FL_SHOOTABLE);
3350 }
3351 
T_Solid(objtype * obj)3352 void T_Solid(
3353     objtype* obj)
3354 {
3355     obj->flags |= (FL_SOLID | FL_SHOOTABLE);
3356 }
3357 
T_LiquidMove(objtype * obj)3358 void T_LiquidMove(
3359     objtype* obj)
3360 {
3361     int16_t dx, dy, dist;
3362 
3363 
3364     //
3365     // Check to see if the Liquid Obj is VERY Close - Then FORCE up and
3366     // start firing...
3367     //
3368 
3369     dx = static_cast<int16_t>(abs(obj->tilex - player->tilex));
3370     dy = static_cast<int16_t>(abs(obj->tiley - player->tiley));
3371     dist = dx > dy ? dx : dy;
3372 
3373     if (dist < 6 && dx > 1 && dy > 1) {
3374         obj->flags &= ~(FL_OFFSET_STATES);
3375         obj->flags |= FL_SOLID;
3376         NewState(obj, &s_liquid_rise1);
3377     } else {
3378         T_Chase(obj);
3379     }
3380 }
3381 
T_LiquidStand(objtype * obj)3382 void T_LiquidStand(
3383     objtype* obj)
3384 {
3385     int16_t dx, dy;
3386 
3387     obj->flags |= FL_SHOOTABLE | FL_SOLID;
3388 
3389     if (US_RndT() < 80 && obj->temp2 < 5) {
3390         obj->temp2++;
3391         NewState(obj, &s_liquid_shoot1);
3392     } else {
3393         dx = static_cast<int16_t>(abs(obj->tilex - player->tilex));
3394         dy = static_cast<int16_t>(abs(obj->tiley - player->tiley));
3395 
3396         if (dx > 1 || dy > 1) {
3397             if (!(obj->flags & FL_VISABLE) || (US_RndT() < 40) || (obj->temp2 == 5)) {
3398                 NewState(obj, &s_liquid_fall1);
3399                 obj->flags |= FL_OFFSET_STATES;
3400                 obj->flags &= ~(FL_SOLID | FL_SHOOTABLE);
3401                 obj->temp2 = 0;
3402             }
3403         } else {
3404             obj->temp2 = 0;
3405         }
3406     }
3407 }
3408 
T_SwatWound(objtype * ob)3409 void T_SwatWound(
3410     objtype* ob)
3411 {
3412     int32_t dx, dy;
3413 
3414     if (ob->state == &s_swatunwounded4) {
3415         ob->flags |= FL_SOLID | FL_SHOOTABLE;
3416     } else {
3417         if (ob->temp2) {
3418             if (tics < ob->temp2) {
3419                 ob->temp2 -= tics;
3420                 return;
3421             }
3422         }
3423 
3424         ob->temp2 = 0;
3425 
3426         dx = player->x - ob->x;
3427         dx = LABS(dx);
3428         dy = player->y - ob->y;
3429         dy = LABS(dy);
3430 
3431         if (dy > TILEGLOBAL || dx > TILEGLOBAL) {
3432             ob->flags |= FL_SOLID | FL_SHOOTABLE;
3433             NewState(ob, &s_swatunwounded1);
3434         }
3435     }
3436 }
3437 
SpawnStand(enemy_t which,int16_t tilex,int16_t tiley,int16_t dir)3438 void SpawnStand(
3439     enemy_t which,
3440     int16_t tilex,
3441     int16_t tiley,
3442     int16_t dir)
3443 {
3444     uint16_t ammo = 8;
3445 
3446     switch (which) {
3447     case en_goldstern:
3448         SpawnNewObj(tilex, tiley, &s_goldwarp_in1);
3449         new_actor->flags = FL_SHOOTABLE | FL_SOLID;
3450         new_actor->flags2 = (::is_ps() ? FL2_BFG_SHOOTABLE : 0);
3451         new_actor->speed = SPDPATROL;
3452         if (gamestate.mapon == 9) {
3453             new_actor->hitpoints = starthitpoints[gamestate.difficulty][which] * 15;
3454         }
3455         ammo = 25;
3456         break;
3457 
3458     case en_electro_alien:
3459         SpawnNewObj(tilex, tiley, &s_electro_appear1);
3460         new_actor->flags = FL_SHOOTABLE | FL_SOLID | FL_PROJ_TRANSPARENT;
3461         new_actor->speed = SPDPATROL;
3462         new_actor->lighting = NO_SHADING; // no shading
3463         break;
3464 
3465     case en_liquid:
3466         SpawnNewObj(tilex, tiley, &s_liquid_wait);
3467         new_actor->flags = FL_OFFSET_STATES | FL_PROJ_TRANSPARENT;
3468         new_actor->flags2 = (::is_ps() ? FL2_BFG_SHOOTABLE : 0);
3469         new_actor->speed = SPDPATROL * 3;
3470         break;
3471 
3472     case en_rentacop:
3473         SpawnNewObj(tilex, tiley, &s_rent_stand);
3474         new_actor->flags = FL_SHOOTABLE | FL_SOLID;
3475         new_actor->flags2 = (::is_ps() ? FL2_BFG_SHOOTABLE : 0);
3476         new_actor->speed = SPDPATROL;
3477         break;
3478 
3479     case en_gen_scientist:
3480         SpawnNewObj(tilex, tiley, &s_ofcstand);
3481         new_actor->flags = FL_SHOOTABLE | FL_SOLID | FL_FRIENDLY | FL_RANDOM_TURN;
3482         new_actor->flags2 = (::is_ps() ? FL2_BFG_SHOOTABLE : 0);
3483 
3484         if (US_RndT() & 1) {
3485             new_actor->flags |= FL_INFORMANT;
3486         }
3487         new_actor->speed = SPDPATROL;
3488         break;
3489 
3490     case en_swat:
3491         SpawnNewObj(tilex, tiley, &s_swatstand);
3492         new_actor->flags = FL_SHOOTABLE | FL_SOLID;
3493         new_actor->flags2 = (::is_ps() ? FL2_BFG_SHOOTABLE : 0);
3494         new_actor->speed = SPDPATROL;
3495         ammo = 30;
3496         if (scan_value == 0xffff) {
3497             new_actor->temp1 = US_RndT() & 1;
3498         } else {
3499             new_actor->temp1 = scan_value;
3500         }
3501         break;
3502 
3503     case en_proguard:
3504         SpawnNewObj(tilex, tiley, &s_prostand);
3505         new_actor->flags = FL_SHOOTABLE | FL_SOLID;
3506         new_actor->flags2 = (::is_ps() ? FL2_BFG_SHOOTABLE : 0);
3507         new_actor->speed = SPDPATROL;
3508         ammo = 25;
3509         break;
3510 
3511     case en_hang_terrot:
3512         SpawnNewObj(tilex, tiley, &s_terrot_wait);
3513         new_actor->flags = FL_SHOOTABLE | FL_NONMARK | FL_NEVERMARK;
3514         new_actor->speed = SPDPATROL;
3515         break;
3516 
3517     case en_floatingbomb:
3518         SpawnNewObj(tilex, tiley, &s_scout_stand);
3519         new_actor->speed = SPDPATROL;
3520         new_actor->temp1 = SPR_FSCOUT_W1_1;
3521         new_actor->flags2 = (::is_ps() ? FL2_BFGSHOT_SOLID | FL2_BFG_SHOOTABLE : 0);
3522         new_actor->flags = FL_SHOOTABLE | FL_SOLID | FL_OFFSET_STATES | FL_FAKE_STATIC;
3523         break;
3524 
3525     case en_volatiletransport:
3526         SpawnNewObj(tilex, tiley, &s_scout_stand);
3527         new_actor->speed = SPDPATROL;
3528         new_actor->temp1 = SPR_GSCOUT_W1_1;
3529         new_actor->flags = FL_SHOOTABLE | FL_SOLID | FL_OFFSET_STATES | FL_STATIONARY | FL_FAKE_STATIC;
3530         new_actor->flags2 = (::is_ps() ? FL2_BFGSHOT_SOLID | FL2_BFG_SHOOTABLE : 0);
3531         break;
3532 
3533     case en_steamgrate:
3534         SpawnNewObj(tilex, tiley, &s_steamgrate);
3535         new_actor->flags = FL_OFFSET_STATES;
3536         new_actor->temp1 = SPR_GRATE;
3537         new_actor->temp2 = 60 * 4;
3538         break;
3539 
3540     case en_steampipe:
3541         nevermark = true;
3542         SpawnNewObj(tilex, tiley, &s_steamgrate);
3543         nevermark = false;
3544         new_actor->flags = FL_OFFSET_STATES | FL_NONMARK | FL_NEVERMARK;
3545         new_actor->temp1 = SPR_STEAM_PIPE;
3546         new_actor->temp2 = 60 * 4;
3547         break;
3548 
3549     default:
3550         break;
3551     }
3552 
3553     CheckForSpecialTile(new_actor, tilex, tiley);
3554 
3555     new_actor->ammo = static_cast<uint8_t>(ammo);
3556     new_actor->obclass = static_cast<classtype>(rentacopobj + which);
3557     new_actor->hitpoints += starthitpoints[gamestate.difficulty][which];
3558     new_actor->dir = static_cast<dirtype>(dir << 1);
3559 
3560     if (new_actor->flags & FL_INFORMANT) {
3561         new_actor->hitpoints = 1;
3562         new_actor->ammo = 0;
3563         new_actor->flags |= FL_HAS_AMMO | FL_HAS_TOKENS;
3564         new_actor->s_tilex = new_actor->s_tiley = 0xff;
3565     }
3566 }
3567 
3568 // ---------------------------------------------------------------------------
3569 // CheckForSpecialTile() - Adds special attributes to actor if standing on
3570 //      special tiles.
3571 // ---------------------------------------------------------------------------
CheckForSpecialTile(objtype * obj,uint16_t tilex,uint16_t tiley)3572 void CheckForSpecialTile(
3573     objtype* obj,
3574     uint16_t tilex,
3575     uint16_t tiley)
3576 {
3577     uint16_t* map, * map1;
3578     objtype* old_new;
3579     bool getarea = false;
3580 
3581     //
3582     // Only shootables can use special tiles...
3583     //
3584     // (This also tests to make sure that the plasma_detonatorobj &
3585     //  plasma_detonator_reservedobj will not enter this function.)
3586     //
3587 
3588     if (!(obj->flags & FL_SHOOTABLE)) {
3589         return;
3590     }
3591 
3592     //
3593     // Check and handle special tiles... Only one per actor... now!
3594     //
3595 
3596     map = mapsegs[0] + farmapylookup[tiley] + tilex;
3597 
3598     switch (*map) {
3599     case CLOAK_AMBUSH_TILE:
3600         if (!::is_ps()) {
3601             break;
3602         }
3603 
3604         obj->flags2 |= FL2_CLOAKED;
3605 
3606     case AMBUSHTILE:
3607         obj->flags |= FL_AMBUSH | FL_SHOOTABLE | FL_SOLID;
3608         getarea = true;
3609         break;
3610 
3611     case DETONATOR_TILE:
3612         if (!::is_ps()) {
3613             break;
3614         }
3615 
3616         old_new = new_actor;
3617         SpawnHiddenOfs(en_plasma_detonator_reserve, tilex, tiley);
3618         new_actor = old_new;
3619         obj->flags &= ~FL_INFORMANT;
3620     case RKEY_TILE:
3621     case YKEY_TILE:
3622     case BKEY_TILE:
3623     case BFG_TILE:
3624     case ION_TILE:
3625         if (!::is_ps()) {
3626             break;
3627         }
3628 
3629         ReserveStatic();
3630         obj->flags2 |= SpecialSpawnFlags[(*map) - RKEY_TILE];
3631         getarea = true;
3632         break;
3633 
3634     case CLOAK_TILE:
3635         if (!::is_ps()) {
3636             break;
3637         }
3638 
3639         obj->flags2 |= FL2_CLOAKED;
3640         getarea = true;
3641         break;
3642 
3643     case LINC_TILE:
3644         if (!::is_ps()) {
3645             break;
3646         }
3647 
3648         obj->flags2 |= FL2_LINC;
3649         obj->flags &= ~FL_INFORMANT; // Make sure informants dont have links
3650         getarea = true;
3651         map1 = mapsegs[1] + farmapylookup[tiley] + tilex + 1;
3652         obj->linc = *map1;
3653         *map1 = 0;
3654         break;
3655     }
3656 
3657     //
3658     // Init areanumbers and tilemaps...
3659     //
3660 
3661     if (getarea) {
3662         tilemap[tilex][tiley] = 0;
3663         *map = obj->areanumber = GetAreaNumber(static_cast<int8_t>(tilex), static_cast<int8_t>(tiley));
3664     }
3665 }
3666 
SpawnPatrol(enemy_t which,int16_t tilex,int16_t tiley,int16_t dir)3667 void SpawnPatrol(
3668     enemy_t which,
3669     int16_t tilex,
3670     int16_t tiley,
3671     int16_t dir)
3672 {
3673     int16_t ammo = 8;
3674 
3675     switch (which) {
3676     case en_blake:
3677         SpawnNewObj(tilex, tiley, &s_blake1);
3678         new_actor->speed = SPDPATROL * 2;
3679         break;
3680 
3681     case en_rentacop:
3682         SpawnNewObj(tilex, tiley, &s_rent_path1);
3683         new_actor->flags2 = (::is_ps() ? FL2_BFG_SHOOTABLE : 0);
3684         new_actor->speed = SPDPATROL;
3685         break;
3686 
3687     case en_gen_scientist:
3688         SpawnNewObj(tilex, tiley, &s_ofcpath1);
3689         new_actor->flags = FL_FRIENDLY | FL_RANDOM_TURN;
3690         new_actor->flags2 = (::is_ps() ? FL2_BFG_SHOOTABLE : 0);
3691 
3692         if (US_RndT() & 1) {
3693             new_actor->flags |= FL_INFORMANT;
3694         }
3695         new_actor->speed = SPDPATROL;
3696         break;
3697 
3698     case en_proguard:
3699         SpawnNewObj(tilex, tiley, &s_propath1);
3700         new_actor->speed = SPDPATROL;
3701         new_actor->flags2 = (::is_ps() ? FL2_BFG_SHOOTABLE : 0);
3702         ammo = 25;
3703         break;
3704 
3705     case en_swat:
3706         SpawnNewObj(tilex, tiley, &s_swatpath1);
3707         new_actor->speed = SPDPATROL;
3708         ammo = 30;
3709         if (scan_value == 0xffff) {
3710             new_actor->temp1 = US_RndT() & 1;
3711         } else {
3712             new_actor->temp1 = scan_value;
3713         }
3714         new_actor->flags2 = (::is_ps() ? FL2_BFG_SHOOTABLE : 0);
3715         break;
3716 
3717     case en_floatingbomb:
3718         SpawnNewObj(tilex, tiley, &s_scout_path1);
3719         new_actor->speed = SPDPATROL;
3720         new_actor->temp1 = SPR_FSCOUT_W1_1;
3721         new_actor->flags = FL_OFFSET_STATES;
3722         new_actor->flags2 = (::is_ps() ? FL2_BFGSHOT_SOLID | FL2_BFG_SHOOTABLE : 0);
3723         break;
3724 
3725     case en_volatiletransport:
3726         SpawnNewObj(tilex, tiley, &s_scout_path1);
3727         new_actor->speed = SPDPATROL;
3728         new_actor->temp1 = SPR_GSCOUT_W1_1;
3729         new_actor->flags = FL_OFFSET_STATES;
3730         new_actor->flags2 = (::is_ps() ? FL2_BFGSHOT_SOLID | FL2_BFG_SHOOTABLE : 0);
3731         break;
3732 
3733     default:
3734         break;
3735     }
3736 
3737     new_actor->ammo = static_cast<uint8_t>(ammo);
3738     new_actor->obclass = static_cast<classtype>(rentacopobj + which);
3739     new_actor->dir = static_cast<dirtype>(dir << 1);
3740     new_actor->hitpoints = starthitpoints[gamestate.difficulty][which];
3741     new_actor->distance = 0;
3742     if (new_actor->obclass != blakeobj) {
3743         new_actor->flags |= FL_SHOOTABLE | FL_SOLID;
3744     }
3745     new_actor->active = ac_yes;
3746     if (new_actor->flags & FL_INFORMANT) {
3747         new_actor->hitpoints = 1;
3748         new_actor->ammo = 0;
3749         new_actor->flags |= FL_HAS_AMMO | FL_HAS_TOKENS;
3750         new_actor->s_tilex = new_actor->s_tiley = 0xff;
3751     }
3752 
3753     CheckForSpecialTile(new_actor, tilex, tiley);
3754 
3755     actorat[new_actor->tilex][new_actor->tiley] = nullptr; // don't use original spot
3756 
3757     TryWalk(new_actor, true);
3758 
3759     actorat[new_actor->tilex][new_actor->tiley] = new_actor;
3760 }
3761 
A_DeathScream(objtype * ob)3762 void A_DeathScream(
3763     objtype* ob)
3764 {
3765     switch (ob->obclass) {
3766 
3767     case swatobj: {
3768         const int sounds[] = {
3769             ::SWATDIESND,
3770             ::is_ps() ? ::SWATDEATH2SND : ::SWATDEATH3SND,
3771         }; // sounds
3772 
3773         auto sound_index = (::is_aog_sw() ? 0 : (::US_RndT() % 2));
3774 
3775         ::sd_play_actor_sound(sounds[sound_index], ob, bstone::AC_VOICE);
3776 
3777         break;
3778     }
3779 
3780     case rentacopobj: {
3781         const int sounds[] = {
3782             RENTDEATH1SND,
3783             RENTDEATH2SND,
3784         }; // sounds
3785 
3786         auto sound_index = (::is_aog_sw() ? 0 : (::US_RndT() % 2));
3787 
3788         ::sd_play_actor_sound(sounds[sound_index], ob, bstone::AC_VOICE);
3789 
3790         break;
3791     }
3792 
3793     case mutant_human1obj:
3794     case hang_terrotobj:
3795     case floatingbombobj:
3796     case volatiletransportobj:
3797     case explosionobj:
3798     case gr_explosionobj:
3799     case bfg_explosionobj:
3800     case pd_explosionobj:
3801     case doorexplodeobj: {
3802         const int sounds[] = {
3803             EXPLODE1SND,
3804             EXPLODE2SND,
3805         }; // sounds
3806 
3807         auto sound_index = (::is_aog_sw() ? 0 : (::US_RndT() % 2));
3808 
3809         ::sd_play_actor_sound(sounds[sound_index], ob, bstone::AC_VOICE);
3810 
3811         break;
3812     }
3813 
3814     case rotating_cubeobj:
3815         ::sd_play_actor_sound(EXPLODE1SND, ob, bstone::AC_VOICE);
3816         ::sd_play_player_sound(VITAL_GONESND, bstone::AC_ITEM);
3817         break;
3818 
3819     case gen_scientistobj: {
3820         auto is_informant = ((ob->flags & FL_INFORMANT) != 0);
3821 
3822         const int sounds[] = {
3823             is_informant ? INFORMANTDEATHSND : SCIENTISTDEATHSND,
3824             is_informant ? INFORMDEATH2SND : SCIDEATH2SND,
3825             is_informant ? INFORMDEATH3SND : SCIDEATH3SND,
3826         }; // sounds
3827 
3828         auto sound_index = ::is_aog_sw() ? 0 : (::US_RndT() % 3);
3829 
3830         ::sd_play_actor_sound(sounds[sound_index], ob, bstone::AC_VOICE);
3831         break;
3832     }
3833 
3834     case genetic_guardobj:
3835         if (::is_aog()) {
3836             ::sd_play_actor_sound(GGUARDDEATHSND, ob, bstone::AC_VOICE);
3837             break;
3838         }
3839 
3840     case breather_beastobj:
3841     case cyborg_warriorobj:
3842     case acid_dragonobj:
3843     case podobj:
3844         ::sd_play_actor_sound(PODDEATHSND, ob, bstone::AC_VOICE);
3845         break;
3846 
3847     case liquidobj:
3848         ::sd_play_actor_sound(LIQUIDDIESND, ob, bstone::AC_VOICE);
3849         break;
3850 
3851     case proguardobj: {
3852         const int sounds[3] = {
3853             PROGUARDDEATHSND,
3854             PRODEATH2SND,
3855         }; // sounds
3856 
3857         auto sound_index = (::is_aog_sw() ? 0 : (::US_RndT() % 2));
3858 
3859         ::sd_play_actor_sound(sounds[sound_index], ob, bstone::AC_VOICE);
3860 
3861         break;
3862     }
3863 
3864     case final_boss1obj:
3865     case spider_mutantobj:
3866         ::sd_play_actor_sound(BLUEBOYDEATHSND, ob, bstone::AC_VOICE);
3867         break;
3868 
3869     case mech_guardianobj:
3870     case final_boss3obj:
3871     case mutant_human2obj:
3872         ::sd_play_actor_sound(DOGBOYDEATHSND, ob, bstone::AC_VOICE);
3873         break;
3874 
3875     case reptilian_warriorobj:
3876     case scan_alienobj:
3877         ::sd_play_actor_sound(SCANDEATHSND, ob, bstone::AC_VOICE);
3878         break;
3879 
3880     case lcan_alienobj:
3881     case final_boss4obj:
3882         ::sd_play_actor_sound(LCANDEATHSND, ob, bstone::AC_VOICE);
3883         break;
3884 
3885     case gurneyobj:
3886         ::sd_play_actor_sound(GURNEYDEATHSND, ob, bstone::AC_VOICE);
3887         break;
3888 
3889     case lcan_wait_alienobj:
3890         ::sd_play_actor_sound(LCANBREAKSND, ob, bstone::AC_VOICE);
3891         break;
3892 
3893     case scan_wait_alienobj:
3894         ::sd_play_actor_sound(SCANBREAKSND, ob, bstone::AC_VOICE);
3895         break;
3896 
3897     default:
3898         break;
3899 
3900     }
3901 }
3902 
3903 
3904 // ============================================================================
3905 //
3906 // DROP
3907 //
3908 // ============================================================================
3909 
DropCargo(objtype * obj)3910 void DropCargo(
3911     objtype* obj)
3912 {
3913     if (!::is_ps()) {
3914         return;
3915     }
3916 
3917     //
3918     // Keep seperate... May later have MULTI "cargo's"
3919     //
3920 
3921     if (obj->flags2 & FL2_DROP_RKEY) {
3922         PlaceReservedItemNearTile(bo_red_key, obj->tilex, obj->tiley);
3923     }
3924 
3925     if (obj->flags2 & FL2_DROP_YKEY) {
3926         PlaceReservedItemNearTile(bo_yellow_key, obj->tilex, obj->tiley);
3927     }
3928 
3929     if (obj->flags2 & FL2_DROP_BKEY) {
3930         PlaceReservedItemNearTile(bo_blue_key, obj->tilex, obj->tiley);
3931     }
3932 
3933     if (obj->flags2 & FL2_DROP_BFG) {
3934         PlaceReservedItemNearTile(bo_bfg_cannon, obj->tilex, obj->tiley);
3935     }
3936 
3937     if (obj->flags2 & FL2_DROP_ION) {
3938         PlaceReservedItemNearTile(bo_ion_cannon, obj->tilex, obj->tiley);
3939     }
3940 
3941     if (obj->flags2 & FL2_DROP_DETONATOR) {
3942         PlaceReservedItemNearTile(bo_plasma_detonator, obj->tilex, obj->tiley);
3943     }
3944 
3945     if ((obj->flags2 & FL2_LINC) && obj->linc) {
3946         OperateSmartSwitch(obj->linc >> 8, obj->linc & 255, ST_TURN_OFF, true);
3947     }
3948 }
3949 
3950 
3951 /*
3952 ============================================================================
3953 
3954  STAND
3955 
3956 ============================================================================
3957 */
3958 
T_Stand(objtype * ob)3959 void T_Stand(
3960     objtype* ob)
3961 {
3962     SightPlayer(ob);
3963 }
3964 
3965 
3966 /*
3967 ============================================================================
3968 
3969  CHASE
3970 
3971 ============================================================================
3972 */
3973 
T_Chase(objtype * ob)3974 void T_Chase(
3975     objtype* ob)
3976 {
3977     int32_t move;
3978     int16_t dx, dy, dist, chance;
3979     bool nearattack = false;
3980 
3981     ob->flags &= ~FL_LOCKED_STATE;
3982 
3983     if (ob->flags & (FL_STATIONARY | FL_BARRIER_DAMAGE)) {
3984         return;
3985     }
3986 
3987     if (ob->ammo) {
3988         if (CheckLine(ob, player) && (!PlayerInvisable)) { // got a shot at player?
3989             dx = static_cast<int16_t>(abs(ob->tilex - player->tilex));
3990             dy = static_cast<int16_t>(abs(ob->tiley - player->tiley));
3991             dist = dx > dy ? dx : dy;
3992             if (!dist) {
3993                 dist = 1;
3994             }
3995 
3996             if (dist == 1 && ob->distance < 0x4000) {
3997                 nearattack = true;
3998             }
3999 
4000             // Time to toggle SHOOTMODE?
4001             //
4002             switch (ob->obclass) {
4003             case mutant_human1obj:
4004             case genetic_guardobj:
4005             case gurneyobj:
4006             case podobj:
4007             case mutant_human2obj:
4008             case scan_alienobj:
4009             case lcan_alienobj:
4010             case spider_mutantobj:
4011             case breather_beastobj:
4012             case cyborg_warriorobj:
4013             case reptilian_warriorobj:
4014             case acid_dragonobj:
4015             case mech_guardianobj:
4016             case gold_morphobj:
4017             case final_boss1obj:
4018             case final_boss2obj:
4019             case final_boss3obj:
4020             case final_boss4obj:
4021                 // Check for mode change
4022                 //
4023                 if (ob->ammo > tics) {
4024                     ob->ammo -= static_cast<uint8_t>(tics);
4025                 } else {
4026                     ChangeShootMode(ob);
4027 
4028                     // Move half as far when doing near attacks...
4029                     //
4030                     if (!(ob->flags & FL_SHOOTMODE)) {
4031                         ob->ammo >>= 1;
4032                     }
4033                 }
4034                 break;
4035 
4036             default:
4037                 break;
4038             }
4039 
4040             if (nearattack) {
4041                 // Always shoot when in SHOOTMODE -- Never shoot when not!
4042                 //
4043                 if (ob->flags & FL_SHOOTMODE) {
4044                     chance = 300;
4045                 } else {
4046                     chance = 0;
4047                 }
4048             } else {
4049                 switch (ob->obclass) {
4050                 case mutant_human1obj:
4051                 case genetic_guardobj:
4052                 case gurneyobj:
4053                 case podobj:
4054                 case mutant_human2obj:
4055                 case scan_alienobj:
4056                 case lcan_alienobj:
4057                 case spider_mutantobj:
4058                 case breather_beastobj:
4059                 case cyborg_warriorobj:
4060                 case reptilian_warriorobj:
4061                 case acid_dragonobj:
4062                 case mech_guardianobj:
4063                 case gold_morphobj:
4064                 case final_boss1obj:
4065                 case final_boss2obj:
4066                 case final_boss3obj:
4067                 case final_boss4obj:
4068                     // Always shoot when in SHOOTMODE -- Never shoot when not!
4069                     //
4070                     if (ob->flags & FL_SHOOTMODE) {
4071                         chance = 300;
4072                     } else {
4073                         chance = 0;
4074                     }
4075                     break;
4076 
4077                 default:
4078                     chance = (tics << 4) / dist;
4079                     break;
4080                 }
4081             }
4082 
4083             if ((US_RndT() < chance) && (ob->ammo) && (!(ob->flags & FL_INTERROGATED))) {
4084                 DoAttack(ob);
4085                 return;
4086             }
4087         } else {
4088             ChangeShootMode(ob);
4089         }
4090     }
4091 
4092     if (ob->dir == nodir) {
4093         switch (ob->obclass) {
4094         case floatingbombobj:
4095             SelectChaseDir(ob);
4096             break;
4097 
4098         default:
4099             SelectDodgeDir(ob);
4100             break;
4101         }
4102 
4103         if (ob->dir == nodir) {
4104             return; // object is blocked in
4105         }
4106     }
4107 
4108     move = ob->speed * tics;
4109 
4110     while (move) {
4111         if (ob->distance < 0) {
4112             //
4113             // waiting for a door to open
4114             //
4115             OpenDoor(static_cast<int16_t>(-ob->distance - 1));
4116             if (doorobjlist[-ob->distance - 1].action != dr_open) {
4117                 return;
4118             }
4119             ob->distance = TILEGLOBAL; // go ahead, the door is now opoen
4120         }
4121 
4122         if (move < ob->distance) {
4123             MoveObj(ob, move);
4124             break;
4125         }
4126 
4127         //
4128         // reached goal tile, so select another one
4129         //
4130 
4131         //
4132         // fix position to account for round off during moving
4133         //
4134         ob->x = ((int32_t)ob->tilex << TILESHIFT) + TILEGLOBAL / 2;
4135         ob->y = ((int32_t)ob->tiley << TILESHIFT) + TILEGLOBAL / 2;
4136 
4137         move -= ob->distance;
4138 
4139         switch (ob->obclass) {
4140         case floatingbombobj:
4141             SelectChaseDir(ob);
4142             break;
4143 
4144         default:
4145             SelectDodgeDir(ob);
4146             break;
4147         }
4148 
4149         if (ob->dir == nodir) {
4150             return; // object is blocked in
4151         }
4152     }
4153 }
4154 
ChangeShootMode(objtype * ob)4155 void ChangeShootMode(
4156     objtype* ob)
4157 {
4158     if (ob->flags & FL_SHOOTMODE) {
4159         ob->flags &= ~FL_SHOOTMODE;
4160         ob->ammo = 60 + (US_RndT() % 60);
4161     } else {
4162         ob->flags |= FL_SHOOTMODE;
4163         ob->ammo = 1 + (US_RndT() % 2);
4164 
4165         if (::is_ps() && ob->obclass == gold_morphobj) {
4166             ob->ammo += 3 + (US_RndT() % 5);
4167         }
4168     }
4169 }
4170 
DoAttack(objtype * ob)4171 void DoAttack(
4172     objtype* ob)
4173 {
4174     int16_t dx, dy, dist;
4175 
4176     dx = static_cast<int16_t>(abs(ob->tilex - player->tilex));
4177     dy = static_cast<int16_t>(abs(ob->tiley - player->tiley));
4178     dist = dx > dy ? dx : dy;
4179     if (!dist) {
4180         dist = 1;
4181     }
4182 
4183     switch (ob->obclass) {
4184     case floatingbombobj:
4185         if (dist <= 1) {
4186             ob->flags &= ~(FL_SHOOTABLE | FL_SOLID);
4187             ob->flags |= FL_NONMARK | FL_DEADGUY;
4188             KillActor(ob);
4189             return;
4190         }
4191         break;
4192 
4193     case goldsternobj:
4194         NewState(ob, &s_goldshoot1);
4195         break;
4196 
4197     case gold_morphobj:
4198         NewState(ob, &s_mgold_shoot1);
4199         break;
4200 
4201     case rentacopobj:
4202         NewState(ob, &s_rent_shoot1);
4203         break;
4204 
4205     case gen_scientistobj:
4206         NewState(ob, &s_ofcshoot1);
4207         break;
4208 
4209     case swatobj:
4210         NewState(ob, &s_swatshoot1);
4211         break;
4212 
4213     case liquidobj:
4214         if ((dx > 2 || dy > 2) && US_RndT() < 30) {
4215             ob->flags &= ~(FL_OFFSET_STATES);
4216             ob->flags |= FL_SOLID;
4217             NewState(ob, &s_liquid_rise1);
4218         }
4219         break;
4220 
4221     case proguardobj:
4222         NewState(ob, &s_proshoot1);
4223         break;
4224 
4225     case electroobj:
4226         NewState(ob, &s_electro_shoot1);
4227         break;
4228 
4229     case podobj:
4230         if (dist > CLOSE_RANGE) {
4231             NewState(ob, &s_ofs_pod_spit1);
4232         } else {
4233             NewState(ob, &s_ofs_pod_attack1);
4234         }
4235         break;
4236 
4237     case spider_mutantobj:
4238     case acid_dragonobj:
4239     case mech_guardianobj:
4240     case breather_beastobj:
4241     case cyborg_warriorobj:
4242     case reptilian_warriorobj:
4243     case gurneyobj:
4244     case mutant_human1obj:
4245     case final_boss1obj:
4246     case final_boss2obj:
4247     case final_boss3obj:
4248     case final_boss4obj:
4249         NewState(ob, &s_ofs_shoot1);
4250         break;
4251 
4252 
4253     case genetic_guardobj:
4254     case mutant_human2obj:
4255     case lcan_alienobj:
4256     case scan_alienobj:
4257         if (dist > CLOSE_RANGE) {
4258             NewState(ob, &s_ofs_spit1);
4259         } else {
4260             NewState(ob, &s_ofs_attack1);
4261         }
4262         break;
4263 
4264     default:
4265         break;
4266     }
4267 }
4268 
4269 
4270 /*
4271 ============================================================================
4272 
4273  PATH
4274 
4275 ============================================================================
4276 */
4277 
SelectPathDir(objtype * ob)4278 dirtype SelectPathDir(
4279     objtype* ob)
4280 {
4281     bool CantWalk = false;
4282     bool RandomTurn = false;
4283     uint16_t spot;
4284 
4285 // Look for directional arrows!
4286 //
4287     spot = MAPSPOT(ob->tilex, ob->tiley, 1) - ICONARROWS;
4288     if (spot < 8) {
4289         ob->dir = static_cast<dirtype>(spot);
4290     }
4291 
4292 // Reset move distance and try to walk/turn.
4293 //
4294     ob->distance = TILEGLOBAL;
4295     if (ob->flags & FL_RANDOM_TURN) {
4296         RandomTurn = US_RndT() > 180;
4297     } else {
4298         RandomTurn = false;
4299     }
4300     CantWalk = !TryWalk(ob, false);
4301 
4302 // Handle random turns and hitting walls
4303 //
4304     if (RandomTurn || CantWalk) {
4305         // Directional arrows have priority!
4306         //
4307         if ((!CantWalk) && (spot < 8)) {
4308             goto exit_func;
4309         }
4310 
4311         // Either: path is blocked   OR   actor is randomly turning.
4312         //
4313         if (ob->trydir == nodir) {
4314             ob->trydir |= US_RndT() & 128;
4315         } else {
4316             ob->dir = static_cast<dirtype>(ob->trydir & 127);
4317         }
4318 
4319         // Turn this actor
4320         //
4321         if (ob->trydir & 128) {
4322             ob->dir--; // turn clockwise
4323             if (ob->dir < east) {
4324                 ob->dir = static_cast<dirtype>(nodir - 1);
4325             }
4326         } else {
4327             ob->dir++; // turn counter-clockwise
4328             if (ob->dir >= nodir) {
4329                 ob->dir = east;
4330             }
4331         }
4332         ob->trydir = static_cast<dirtype>((ob->trydir & 128) | ob->dir);
4333 
4334         // Walk into new direction?
4335         //
4336         if (!TryWalk(ob, false)) {
4337             ob->dir = nodir;
4338         }
4339     }
4340 
4341 exit_func:;
4342     if (ob->dir != nodir) {
4343         TryWalk(ob, true);
4344         ob->trydir = nodir;
4345     }
4346 
4347     return ob->dir;
4348 }
4349 
T_Path(objtype * ob)4350 void T_Path(
4351     objtype* ob)
4352 {
4353     int32_t move;
4354 
4355     if (ob->flags & FL_STATIONARY) {
4356         return;
4357     }
4358 
4359     switch (ob->obclass) {
4360     case volatiletransportobj:
4361         break;
4362 
4363     default:
4364         if ((!(ob->flags & FL_FRIENDLY)) || madenoise) {
4365             if (SightPlayer(ob)) {
4366                 return;
4367             }
4368         }
4369 
4370 #if LOOK_FOR_DEAD_GUYS
4371         if (LookForDeadGuys(ob)) {
4372             return;
4373         }
4374 #endif
4375         break;
4376     }
4377 
4378     if (ob->dir == nodir) {
4379         if (SelectPathDir(ob) == nodir) {
4380             return;
4381         }
4382     }
4383 
4384     move = ob->speed * tics;
4385     while (move) {
4386         if (ob->distance < 0) {
4387             // Actor waiting for door to open
4388             //
4389             OpenDoor(static_cast<int16_t>(-ob->distance - 1));
4390             if (doorobjlist[-ob->distance - 1].action != dr_open) {
4391                 return;
4392             }
4393             ob->distance = TILEGLOBAL; // go ahead, the door is now opoen
4394         }
4395 
4396         if (move < ob->distance) {
4397             MoveObj(ob, move);
4398             break;
4399         }
4400 
4401         if (ob->tilex > MAPSIZE || ob->tiley > MAPSIZE) {
4402             ::Quit("Actor walked out of map.");
4403         }
4404 
4405         ob->x = ((int32_t)ob->tilex << TILESHIFT) + TILEGLOBAL / 2;
4406         ob->y = ((int32_t)ob->tiley << TILESHIFT) + TILEGLOBAL / 2;
4407         move -= ob->distance;
4408 
4409         if (SelectPathDir(ob) == nodir) {
4410             return;
4411         }
4412     }
4413 }
4414 
4415 
4416 /*
4417 =============================================================================
4418 
4419  FIGHT
4420 
4421 =============================================================================
4422 */
4423 
4424 int16_t morph_angle_adj = 0;
4425 
T_Shoot(objtype * ob)4426 void T_Shoot(
4427     objtype* ob)
4428 {
4429     int16_t dx, dy, dist;
4430     int16_t hitchance, damage;
4431     int16_t chance;
4432 
4433     switch (ob->obclass) {
4434     case SMART_ACTORS:
4435         if (!ob->ammo) {
4436             return;
4437         }
4438 
4439     default:
4440         break;
4441     }
4442 
4443     switch (ob->obclass) {
4444     case electroobj:
4445         ::SpawnProjectile(ob, electroshotobj);
4446         break;
4447 
4448     case mutant_human2obj:
4449         SpawnProjectile(ob, ::is_ps() ? electroshotobj : scanshotobj);
4450         break;
4451 
4452     case liquidobj:
4453         SpawnProjectile(ob, liquidshotobj);
4454         break;
4455 
4456     case lcan_alienobj:
4457         SpawnProjectile(ob, lcanshotobj);
4458         break;
4459 
4460     case podobj:
4461         SpawnProjectile(ob, podshotobj);
4462         break;
4463 
4464     case scan_alienobj:
4465         SpawnProjectile(ob, scanshotobj);
4466         break;
4467 
4468     case gold_morphobj:
4469         SpawnProjectile(ob, goldmorphshotobj);
4470 
4471         if (ob->hitpoints < 500) {
4472             chance = 255 / 2;
4473         } else {
4474             chance = 255 / 4;
4475         }
4476 
4477         if (US_RndT() < chance) {
4478             morph_angle_adj = 24;
4479             SpawnProjectile(ob, goldmorphshotobj);
4480             morph_angle_adj = -24;
4481             SpawnProjectile(ob, goldmorphshotobj);
4482             morph_angle_adj = 16;
4483             SpawnProjectile(ob, goldmorphshotobj);
4484             morph_angle_adj = -16;
4485             SpawnProjectile(ob, goldmorphshotobj);
4486             morph_angle_adj = 8;
4487             SpawnProjectile(ob, goldmorphshotobj);
4488             morph_angle_adj = -8;
4489             SpawnProjectile(ob, goldmorphshotobj);
4490             morph_angle_adj = 0;
4491         }
4492         break;
4493 
4494     case spider_mutantobj:
4495     case acid_dragonobj:
4496         SpawnProjectile(ob, static_cast<classtype>(spider_mutantshotobj + (ob->obclass - spider_mutantobj)));
4497         break;
4498 
4499     case final_boss2obj:
4500         SpawnProjectile(ob, final_boss2shotobj);
4501         break;
4502 
4503     case final_boss4obj:
4504         SpawnProjectile(ob, final_boss4shotobj);
4505         break;
4506 
4507     default:
4508         hitchance = 128;
4509 
4510         ob->lighting = -10;
4511 
4512         if (!areabyplayer[ob->areanumber]) {
4513             return;
4514         }
4515 
4516         if (!CheckLine(ob, player)) { // player is behind a wall
4517             return;
4518         }
4519 
4520         dx = static_cast<int16_t>(abs(ob->tilex - player->tilex));
4521         dy = static_cast<int16_t>(abs(ob->tiley - player->tiley));
4522         dist = dx > dy ? dx : dy;
4523 
4524         if (ob->obclass == swatobj) {
4525             if (dist) {
4526                 dist = dist * 2 / 3; // ss are better shots
4527 
4528             }
4529         }
4530         if (thrustspeed >= RUNSPEED) {
4531             if (ob->flags & FL_VISABLE) {
4532                 hitchance = 160 - dist * 16; // player can see to dodge
4533             } else {
4534                 hitchance = 160 - dist * 8;
4535             }
4536         } else {
4537             if (ob->flags & FL_VISABLE) {
4538                 hitchance = 256 - dist * 16; // player can see to dodge
4539             } else {
4540                 hitchance = 256 - dist * 8;
4541             }
4542         }
4543 
4544         // See if the shot was a hit.
4545         //
4546         if (US_RndT() < hitchance) {
4547             if (dist < 2) {
4548                 damage = US_RndT() >> 2;
4549             } else if (dist < 4) {
4550                 damage = US_RndT() >> 3;
4551             } else {
4552                 damage = US_RndT() >> 4;
4553             }
4554 
4555             TakeDamage(damage, ob);
4556         }
4557 
4558         switch (ob->obclass) {
4559 
4560         case proguardobj:
4561         case swatobj:
4562             ::sd_play_actor_sound(
4563                 ATKBURSTRIFLESND, ob, bstone::AC_WEAPON);
4564             break;
4565 
4566         default:
4567             ::sd_play_actor_sound(
4568                 ATKCHARGEDSND, ob, bstone::AC_WEAPON);
4569             break;
4570         }
4571 
4572 #ifdef LIMITED_AMMO
4573         switch (ob->obclass) {
4574         case SMART_ACTORS:
4575             ob->ammo--;
4576             CheckRunChase(ob);
4577             break;
4578 
4579         default:
4580             break;
4581         }
4582 #endif
4583 
4584         MakeAlertNoise(ob);
4585         break;
4586     }
4587 
4588     switch (ob->obclass) {
4589     case proguardobj:
4590     case swatobj:
4591         break;
4592 
4593     default:
4594         ob->flags &= ~FL_LOCKED_STATE;
4595         break;
4596     }
4597 }
4598 
T_Shade(objtype * obj)4599 void T_Shade(
4600     objtype* obj)
4601 {
4602 
4603     switch (obj->obclass) {
4604     case final_boss2obj:
4605         break;
4606 
4607     default:
4608         obj->lighting = 0;
4609         break;
4610     }
4611 }
4612 
T_Hit(objtype * ob)4613 void T_Hit(
4614     objtype* ob)
4615 {
4616     int32_t dx, dy;
4617     int16_t hitchance, damage;
4618 
4619 
4620     switch (ob->obclass) {
4621     case scan_alienobj:
4622     case lcan_alienobj:
4623     case podobj:
4624         hitchance = 220; // Higher - Better Chance (255 max!)
4625         damage = (US_RndT() >> 3) | 1;
4626         ::sd_play_actor_sound(CLAWATTACKSND, ob, bstone::AC_WEAPON);
4627         break;
4628 
4629     case genetic_guardobj:
4630     case mutant_human2obj:
4631         hitchance = 220; // Higher - Better Chance (255 max!)
4632         damage = (US_RndT() >> 3) | 1;
4633         ::sd_play_actor_sound(PUNCHATTACKSND, ob, bstone::AC_WEAPON);
4634         break;
4635 
4636     default:
4637         hitchance = 200; // Higher - Better Chance (255 max!)
4638         damage = US_RndT() >> 4;
4639         break;
4640 
4641     }
4642 
4643     MakeAlertNoise(ob);
4644 
4645     dx = player->x - ob->x;
4646     if (dx < 0) {
4647         dx = -dx;
4648     }
4649     dx -= TILEGLOBAL;
4650     if (dx <= MINACTORDIST) {
4651         dy = player->y - ob->y;
4652         if (dy < 0) {
4653             dy = -dy;
4654         }
4655         dy -= TILEGLOBAL;
4656         if (dy <= MINACTORDIST) {
4657             if (US_RndT() < hitchance) {
4658                 TakeDamage(damage, ob);
4659                 return;
4660             }
4661         }
4662     }
4663 
4664     return;
4665 }
4666 
4667 
4668 /*
4669 ============================================================================
4670 
4671  DR. GOLDSTERN STATES & ROUTINES
4672 
4673 ============================================================================
4674 */
4675 
4676 void A_Beep(
4677     objtype* obj);
4678 
4679 void A_Laugh(
4680     objtype* obj);
4681 
4682 void A_WarpIn(
4683     objtype* obj);
4684 
4685 void A_WarpOut(
4686     objtype* obj);
4687 
4688 void T_GoldMorph(
4689     objtype* obj);
4690 
4691 
4692 extern statetype s_goldstand;
4693 
4694 extern statetype s_goldpath1;
4695 extern statetype s_goldpath1s;
4696 extern statetype s_goldpath2;
4697 extern statetype s_goldpath3;
4698 extern statetype s_goldpath3s;
4699 extern statetype s_goldpath4;
4700 
4701 extern statetype s_goldpain;
4702 
4703 extern statetype s_goldshoot1;
4704 extern statetype s_goldshoot2;
4705 extern statetype s_goldshoot3;
4706 extern statetype s_goldshoot4;
4707 extern statetype s_goldshoot5;
4708 extern statetype s_goldshoot6;
4709 extern statetype s_goldshoot7; // Add Line
4710 
4711 extern statetype s_goldchase1;
4712 extern statetype s_goldchase1s;
4713 extern statetype s_goldchase2;
4714 extern statetype s_goldchase3;
4715 extern statetype s_goldchase3s;
4716 extern statetype s_goldchase4;
4717 
4718 extern statetype s_goldwarp_it;
4719 extern statetype s_goldwarp_it1;
4720 extern statetype s_goldwarp_it2;
4721 extern statetype s_goldwarp_it3;
4722 extern statetype s_goldwarp_it4;
4723 extern statetype s_goldwarp_it5;
4724 
4725 extern statetype s_goldwarp_out1;
4726 extern statetype s_goldwarp_out2;
4727 extern statetype s_goldwarp_out3;
4728 extern statetype s_goldwarp_out4;
4729 extern statetype s_goldwarp_out5;
4730 
4731 extern statetype s_goldwarp_in1;
4732 extern statetype s_goldwarp_in2;
4733 extern statetype s_goldwarp_in3;
4734 extern statetype s_goldwarp_in4;
4735 extern statetype s_goldwarp_in5;
4736 
4737 extern statetype s_goldmorphwait1;
4738 extern statetype s_goldmorphwait2;
4739 
4740 
4741 extern void T_GoldMorphWait(
4742     objtype* obj);
4743 
4744 
4745 statetype s_goldstand = { 1, SPR_DEMO, 20, T_Stand, nullptr, &s_goldpath1 };
4746 
4747 statetype s_goldpath1 = { 1, SPR_DEMO, 20, T_Path, nullptr, &s_goldpath1s };
4748 statetype s_goldpath1s = { 1, SPR_DEMO, 5, nullptr, nullptr, &s_goldpath2 };
4749 statetype s_goldpath2 = { 1, SPR_DEMO, 15, T_Path, nullptr, &s_goldpath3 };
4750 statetype s_goldpath3 = { 1, SPR_DEMO, 20, T_Path, nullptr, &s_goldpath3s };
4751 statetype s_goldpath3s = { 1, SPR_DEMO, 5, nullptr, nullptr, &s_goldpath4 };
4752 statetype s_goldpath4 = { 1, SPR_DEMO, 15, T_Path, nullptr, &s_goldpath1 };
4753 
4754 statetype s_goldpain = { 0, SPR_DEMO, 15, nullptr, nullptr, &s_goldchase1 };
4755 
4756 statetype s_goldshoot1 = { 0, SPR_DEMO, 20, nullptr, nullptr, &s_goldshoot2 };
4757 statetype s_goldshoot2 = { 0, SPR_DEMO, 20, nullptr, T_Shoot, &s_goldshoot3 };
4758 statetype s_goldshoot3 = { 0, SPR_DEMO, 10, nullptr, T_Shade, &s_goldshoot4 };
4759 statetype s_goldshoot4 = { 0, SPR_DEMO, 10, nullptr, T_Shoot, &s_goldshoot5 };
4760 statetype s_goldshoot5 = { 0, SPR_DEMO, 10, nullptr, T_Shade, &s_goldshoot6 };
4761 statetype s_goldshoot6 = { 0, SPR_DEMO, 10, nullptr, T_Shoot, &s_goldshoot7 };
4762 statetype s_goldshoot7 = { 0, SPR_DEMO, 10, nullptr, T_Shade, &s_goldchase1 };
4763 
4764 statetype s_goldchase1 = { 1, SPR_DEMO, 10, T_Chase, nullptr, &s_goldchase1s };
4765 statetype s_goldchase1s = { 1, SPR_DEMO, 3, nullptr, nullptr, &s_goldchase2 };
4766 statetype s_goldchase2 = { 1, SPR_DEMO, 8, T_Chase, nullptr, &s_goldchase3 };
4767 statetype s_goldchase3 = { 1, SPR_DEMO, 10, T_Chase, nullptr, &s_goldchase3s };
4768 statetype s_goldchase3s = { 1, SPR_DEMO, 3, nullptr, nullptr, &s_goldchase4 };
4769 statetype s_goldchase4 = { 1, SPR_DEMO, 8, T_Chase, nullptr, &s_goldchase1 };
4770 
4771 statetype s_goldwarp_it = { 0, SPR_DEMO, 45, nullptr, A_Laugh, &s_goldwarp_it1 };
4772 statetype s_goldwarp_it1 = { 0, SPR_DEMO, 25, nullptr, nullptr, &s_goldwarp_it2 };
4773 statetype s_goldwarp_it2 = { 0, SPR_DEMO, 25, nullptr, A_Beep, &s_goldwarp_it3 };
4774 statetype s_goldwarp_it3 = { 0, SPR_DEMO, 15, nullptr, nullptr, &s_goldwarp_it4 };
4775 statetype s_goldwarp_it4 = { 0, SPR_DEMO, 25, nullptr, A_Beep, &s_goldwarp_it5 };
4776 statetype s_goldwarp_it5 = { 0, SPR_DEMO, 15, nullptr, nullptr, &s_goldwarp_out1 };
4777 
4778 statetype s_goldwarp_out1 = { 0, SPR_DEMO, 30, nullptr, A_WarpOut, &s_goldwarp_out2 };
4779 statetype s_goldwarp_out2 = { 0, SPR_DEMO, 30, nullptr, nullptr, &s_goldwarp_out3 };
4780 statetype s_goldwarp_out3 = { 0, SPR_DEMO, 30, nullptr, nullptr, &s_goldwarp_out4 };
4781 statetype s_goldwarp_out4 = { 0, SPR_DEMO, 30, nullptr, nullptr, &s_goldwarp_out5 };
4782 statetype s_goldwarp_out5 = { 0, SPR_DEMO, 40, nullptr, nullptr, nullptr };
4783 
4784 statetype s_goldwarp_in1 = { 0, SPR_DEMO, 15, nullptr, A_WarpIn, &s_goldwarp_in2 };
4785 statetype s_goldwarp_in2 = { 0, SPR_DEMO, 15, nullptr, nullptr, &s_goldwarp_in3 };
4786 statetype s_goldwarp_in3 = { 0, SPR_DEMO, 15, nullptr, nullptr, &s_goldwarp_in4 };
4787 statetype s_goldwarp_in4 = { 0, SPR_DEMO, 15, nullptr, nullptr, &s_goldwarp_in5 };
4788 statetype s_goldwarp_in5 = { 0, SPR_DEMO, 15, nullptr, nullptr, &s_goldpath1 };
4789 
4790 statetype s_goldmorphwait1 = { 0, SPR_DEMO, 10, nullptr, T_GoldMorphWait, &s_goldmorphwait1 };
4791 
4792 statetype s_goldmorph1 = { 0, SPR_DEMO, 10, nullptr, nullptr, &s_goldmorph2 };
4793 statetype s_goldmorph2 = { 0, SPR_DEMO, 10, nullptr, nullptr, &s_goldmorph3 };
4794 statetype s_goldmorph3 = { 0, SPR_DEMO, 10, nullptr, nullptr, &s_goldmorph4 };
4795 statetype s_goldmorph4 = { 0, SPR_DEMO, 10, nullptr, nullptr, &s_goldmorph5 };
4796 statetype s_goldmorph5 = { 0, SPR_DEMO, 10, nullptr, nullptr, &s_goldmorph6 };
4797 statetype s_goldmorph6 = { 0, SPR_DEMO, 10, nullptr, nullptr, &s_goldmorph7 };
4798 statetype s_goldmorph7 = { 0, SPR_DEMO, 10, nullptr, nullptr, &s_goldmorph8 };
4799 statetype s_goldmorph8 = { 0, SPR_DEMO, 10, nullptr, T_GoldMorph, &s_mgold_chase1 };
4800 
4801 statetype s_mgold_chase1 = { 0, SPR_DEMO, 10, T_Chase, nullptr, &s_mgold_chase2 };
4802 statetype s_mgold_chase2 = { 0, SPR_DEMO, 8, nullptr, nullptr, &s_mgold_chase3 };
4803 statetype s_mgold_chase3 = { 0, SPR_DEMO, 10, T_Chase, nullptr, &s_mgold_chase4 };
4804 statetype s_mgold_chase4 = { 0, SPR_DEMO, 8, nullptr, nullptr, &s_mgold_chase1 };
4805 
4806 statetype s_mgold_shoot1 = { 0, SPR_DEMO, 10, nullptr, nullptr, &s_mgold_shoot2 };
4807 statetype s_mgold_shoot2 = { 0, SPR_DEMO, 20, nullptr, nullptr, &s_mgold_shoot3 };
4808 statetype s_mgold_shoot3 = { 0, SPR_DEMO, 14, T_Shoot, nullptr, &s_mgold_shoot4 };
4809 statetype s_mgold_shoot4 = { 0, SPR_DEMO, 12, T_Shade, nullptr, &s_mgold_chase1 };
4810 
4811 statetype s_mgold_pain = { 0, SPR_DEMO, 25, nullptr, nullptr, &s_mgold_chase1 };
4812 
4813 
4814 bool noShots = false;
4815 
4816 int16_t morphWaitTime;
4817 
4818 
T_GoldMorphWait(objtype * obj)4819 void T_GoldMorphWait(
4820     objtype* obj)
4821 {
4822     morphWaitTime -= tics;
4823     if (morphWaitTime < 0) {
4824         NewState(obj, &s_goldmorph1);
4825     }
4826 }
4827 
T_GoldMorph(objtype * obj)4828 void T_GoldMorph(
4829     objtype* obj)
4830 {
4831     obj->speed = ALIENSPEED << 2;
4832     obj->ammo = ALIENAMMOINIT;
4833     obj->flags |= FL_PROJ_TRANSPARENT | FL_NO_SLIDE | FL_SHOOTABLE | FL_SOLID;
4834     obj->flags2 = FL2_BFGSHOT_SOLID | FL2_BFG_SHOOTABLE;
4835     obj->hitpoints = starthitpoints[gamestate.difficulty][en_gold_morph];
4836     obj->obclass = gold_morphobj;
4837 
4838     noShots = false;
4839 }
4840 
A_Laugh(objtype * obj)4841 void A_Laugh(
4842     objtype* obj)
4843 {
4844     ::sd_play_actor_sound(GOLDSTERNLAUGHSND, obj, bstone::AC_VOICE);
4845 }
4846 
A_WarpIn(objtype *)4847 void A_WarpIn(
4848     objtype*)
4849 {
4850     ::sd_play_player_sound(WARPINSND, bstone::AC_ITEM);
4851 }
4852 
A_WarpOut(objtype *)4853 void A_WarpOut(
4854     objtype*)
4855 {
4856     ::sd_play_player_sound(WARPOUTSND, bstone::AC_ITEM);
4857 }
4858 
A_Beep(objtype *)4859 void A_Beep(
4860     objtype*)
4861 {
4862     ::sd_play_player_sound(ELEV_BUTTONSND, bstone::AC_ITEM);
4863 }
4864 
InitGoldsternInfo()4865 void InitGoldsternInfo()
4866 {
4867     memset(&GoldsternInfo, 0, sizeof(GoldsternInfo));
4868     GoldsternInfo.LastIndex = GOLDIE_MAX_SPAWNS;
4869 }
4870 
4871 
4872 // ===========================================================================
4873 //
4874 //
4875 // RED FLASHING SECURITY LAMPS
4876 //
4877 //
4878 // ===========================================================================
4879 
T_FlipShape(objtype * obj)4880 void T_FlipShape(
4881     objtype* obj)
4882 {
4883     if (obj->flags & FL_ALERTED) {
4884         if (obj->temp1 ^= 1) {
4885             obj->lighting = LAMP_ON_SHADING;
4886         } else {
4887             obj->lighting = 0;
4888         }
4889     }
4890 }
4891 
4892 
4893 statetype s_security_light = { 0, SPR_DEMO, 20, T_Security, T_FlipShape, &s_security_light };
4894 
T_Security(objtype * obj)4895 void T_Security(
4896     objtype* obj)
4897 {
4898 
4899     if (!(obj->flags & FL_ALERTED)) {
4900         if (alerted && areabyplayer[obj->areanumber]) {
4901             obj->flags |= FL_ALERTED;
4902         }
4903     }
4904 }
4905 
4906 
4907 // ==========================================================================
4908 //
4909 //
4910 //                              GROUND & FLOATING SCOUT ROUTINES
4911 //
4912 //
4913 // ==========================================================================
4914 
4915 void A_Scout_Alert(
4916     objtype* obj);
4917 
4918 void T_PainThink(
4919     objtype* obj);
4920 
4921 extern statetype s_scout_path1;
4922 extern statetype s_scout_path1s;
4923 extern statetype s_scout_path2;
4924 extern statetype s_scout_path3;
4925 extern statetype s_scout_path3s;
4926 extern statetype s_scout_path4;
4927 
4928 extern statetype s_scout_pain;
4929 extern statetype s_scout_pain2;
4930 
4931 extern statetype s_scout_run;
4932 extern statetype s_scout_run1s;
4933 extern statetype s_scout_run2;
4934 extern statetype s_scout_run3;
4935 extern statetype s_scout_run3s;
4936 extern statetype s_scout_run4;
4937 
4938 extern statetype s_scout_dead;
4939 
4940 statetype s_scout_stand = { 1, SPR_DEMO, 20, T_Stand, nullptr, &s_scout_stand };
4941 
4942 statetype s_scout_path1 = { 1, SPR_DEMO, 15, T_Path, nullptr, &s_scout_path1 };
4943 statetype s_scout_path2 = { 1, SPR_DEMO, 15, T_Path, nullptr, &s_scout_path2 };
4944 statetype s_scout_path3 = { 1, SPR_DEMO, 15, T_Path, nullptr, &s_scout_path3 };
4945 statetype s_scout_path4 = { 1, SPR_DEMO, 15, T_Path, nullptr, &s_scout_path4 };
4946 
4947 statetype s_scout_run = { 1, SPR_DEMO, 10, T_Chase, nullptr, &s_scout_run };
4948 statetype s_scout_run2 = { 1, SPR_DEMO, 10, T_Chase, nullptr, &s_scout_run2 };
4949 statetype s_scout_run3 = { 1, SPR_DEMO, 10, T_Chase, nullptr, &s_scout_run3 };
4950 statetype s_scout_run4 = { 1, SPR_DEMO, 10, T_Chase, nullptr, &s_scout_run4 };
4951 
4952 statetype s_scout_dead = { 0, SPR_DEMO, 20, nullptr, nullptr, &s_scout_dead };
4953 
4954 
A_Scout_Alert(objtype * obj)4955 void A_Scout_Alert(
4956     objtype* obj)
4957 {
4958     ::sd_play_actor_sound(SCOUT_ALERTSND, obj, bstone::AC_VOICE);
4959 
4960     MakeAlertNoise(obj);
4961 }
4962 
4963 
T_ExplodeScout(objtype * obj)4964 void T_ExplodeScout(
4965     objtype* obj)
4966 {
4967     SpawnExplosion(obj->x + 0x4000 + (US_RndT() << 5), obj->y + 0x4000 + (US_RndT() << 5));
4968     SpawnExplosion(obj->x - 0x4000 - (US_RndT() << 5), obj->y + 0x4000 + (US_RndT() << 5));
4969     SpawnExplosion(obj->x - 0x4000 - (US_RndT() << 5), obj->y - 0x4000 - (US_RndT() << 5));
4970     SpawnExplosion(obj->x + 0x4000 + (US_RndT() << 5), obj->y - 0x4000 - (US_RndT() << 5));
4971 }
4972 
T_ExplodeDamage(objtype * obj)4973 void T_ExplodeDamage(
4974     objtype* obj)
4975 {
4976     ExplodeRadius(obj, EXPLODE_DAMAGE, true);
4977 }
4978 
T_PainThink(objtype * obj)4979 void T_PainThink(
4980     objtype* obj)
4981 {
4982     if (::is_aog_sw()) {
4983         return;
4984     }
4985 
4986     int16_t full_hp = starthitpoints[gamestate.difficulty][obj->obclass - rentacopobj];
4987 
4988     if (obj->hitpoints > (full_hp >> 1) + (full_hp >> 2)) {
4989         //       Orginal HitPoints
4990         //
4991 
4992         switch (obj->obclass) {
4993         case floatingbombobj:
4994             NewState(obj, &s_scout_run);
4995             break;
4996 
4997         case volatiletransportobj:
4998             NewState(obj, &s_scout_path1);
4999             break;
5000 
5001         default:
5002             break;
5003         }
5004     } else if (obj->hitpoints > (full_hp >> 1)) {
5005         //      3/4 Orginal HitPoints
5006         //
5007 
5008         switch (obj->obclass) {
5009         case floatingbombobj:
5010             NewState(obj, &s_scout_run2);
5011             break;
5012 
5013         case volatiletransportobj:
5014             NewState(obj, &s_scout_path2);
5015             break;
5016 
5017         default:
5018             break;
5019         }
5020     } else if (obj->hitpoints > (full_hp >> 2)) {
5021         //      1/2 Orginal HitPoints
5022         //
5023 
5024         switch (obj->obclass) {
5025         case floatingbombobj:
5026             NewState(obj, &s_scout_run3);
5027             break;
5028 
5029         case volatiletransportobj:
5030             NewState(obj, &s_scout_path3);
5031             break;
5032 
5033         default:
5034             break;
5035         }
5036     } else {
5037         //      1/4 Orginal HitPoints
5038         //
5039 
5040         switch (obj->obclass) {
5041         case floatingbombobj:
5042             NewState(obj, &s_scout_run4);
5043             break;
5044 
5045         case volatiletransportobj:
5046             NewState(obj, &s_scout_path4);
5047             break;
5048 
5049         default:
5050             break;
5051         }
5052     }
5053 }
5054 
5055 
5056 // ==========================================================================
5057 //
5058 // EXPLOSION STUFF
5059 //
5060 // ==========================================================================
5061 
5062 // -------------------------------------------------------------------------
5063 // SpawnCusExplosion() - Spawns an explosion at a given x & y.
5064 // -------------------------------------------------------------------------
SpawnCusExplosion(fixed x,fixed y,uint16_t StartFrame,uint16_t NumFrames,uint16_t Delay,uint16_t Class)5065 void SpawnCusExplosion(
5066     fixed x,
5067     fixed y,
5068     uint16_t StartFrame,
5069     uint16_t NumFrames,
5070     uint16_t Delay,
5071     uint16_t Class)
5072 {
5073     int16_t tilex = x >> TILESHIFT, tiley = y >> TILESHIFT;
5074 
5075     usedummy = nevermark = true;
5076     SpawnNewObj(tilex, tiley, &s_ofs_smart_anim);
5077     usedummy = nevermark = false;
5078     new_actor->x = x;
5079     new_actor->y = y;
5080     new_actor->flags = FL_OFFSET_STATES | FL_NONMARK | FL_NEVERMARK;
5081     new_actor->obclass = static_cast<classtype>(Class);
5082     new_actor->lighting = NO_SHADING;
5083     InitAnim(new_actor, StartFrame, 0, static_cast<uint8_t>(NumFrames), at_ONCE, ad_FWD, (US_RndT() & 0x7), Delay);
5084     A_DeathScream(new_actor);
5085     MakeAlertNoise(new_actor);
5086 }
5087 
T_SpawnExplosion(objtype * obj)5088 void T_SpawnExplosion(
5089     objtype* obj)
5090 {
5091     SpawnCusExplosion(obj->x, obj->y, SPR_EXPLOSION_1, 4, 4, explosionobj);
5092 }
5093 
5094 
5095 // ==========================================================================
5096 //
5097 // STEAM OBJECT STUFF
5098 //
5099 // ==========================================================================
5100 
5101 void T_SteamObj(
5102     objtype* obj);
5103 
5104 extern statetype s_steamrelease1;
5105 extern statetype s_steamrelease2;
5106 extern statetype s_steamrelease3;
5107 extern statetype s_steamrelease4;
5108 extern statetype s_steamrelease5;
5109 extern statetype s_steamrelease6;
5110 
5111 statetype s_steamgrate = { 0, 0, 1, T_SteamObj, nullptr, &s_steamgrate };
5112 
5113 statetype s_steamrelease1 = { 0, 1, 1, nullptr, A_DeathScream, &s_steamrelease2 };
5114 statetype s_steamrelease2 = { 0, 2, 14, nullptr, nullptr, &s_steamrelease3 };
5115 statetype s_steamrelease3 = { 0, 3, 14, nullptr, nullptr, &s_steamrelease4 };
5116 statetype s_steamrelease4 = { 0, 2, 14, nullptr, nullptr, &s_steamrelease5 };
5117 statetype s_steamrelease5 = { 0, 3, 14, nullptr, nullptr, &s_steamrelease6 };
5118 statetype s_steamrelease6 = { 0, 4, 16, nullptr, nullptr, &s_steamgrate };
5119 
5120 
T_SteamObj(objtype * obj)5121 void T_SteamObj(
5122     objtype* obj)
5123 {
5124     if (obj->flags & FL_VISABLE) {
5125         if ((obj->temp2 -= tics) <= 0) {
5126             NewState(obj, &s_steamrelease1);
5127             obj->temp2 = US_RndT() << 3; // Up to 34 Seconds
5128         } else {
5129             obj->temp2 -= tics;
5130         }
5131     }
5132 }
5133 
CheckPosition(objtype * ob)5134 bool CheckPosition(
5135     objtype* ob)
5136 {
5137     int16_t x, y, xl, yl, xh, yh;
5138     objtype* check;
5139 
5140     xl = (ob->x - PLAYERSIZE) >> TILESHIFT;
5141     yl = (ob->y - PLAYERSIZE) >> TILESHIFT;
5142 
5143     xh = (ob->x + PLAYERSIZE) >> TILESHIFT;
5144     yh = (ob->y + PLAYERSIZE) >> TILESHIFT;
5145 
5146     //
5147     // check for solid walls
5148     //
5149     for (y = yl; y <= yh; y++) {
5150         for (x = xl; x <= xh; x++) {
5151             check = actorat[x][y];
5152             if (check && check < objlist) {
5153                 return false;
5154             }
5155         }
5156     }
5157 
5158     return true;
5159 }
5160 
5161 
5162 // ===========================================================================
5163 //
5164 //
5165 // HANGING TERROT GUN
5166 //
5167 //
5168 // ===========================================================================
5169 
5170 extern statetype s_terrot_wait;
5171 
5172 extern statetype s_terrot_shoot1;
5173 extern statetype s_terrot_shoot2;
5174 extern statetype s_terrot_shoot3;
5175 extern statetype s_terrot_shoot4;
5176 
5177 extern statetype s_terrot_seek1;
5178 extern statetype s_terrot_seek1s;
5179 
5180 extern statetype s_terrot_die1;
5181 extern statetype s_terrot_die2;
5182 extern statetype s_terrot_die3;
5183 extern statetype s_terrot_die4;
5184 extern statetype s_terrot_die5;
5185 
5186 
5187 
5188 statetype s_terrot_wait = { 1, SPR_DEMO, 1, T_Seek, nullptr, &s_terrot_wait };
5189 statetype s_terrot_found = { 0, SPR_DEMO, 0, T_Seek, nullptr, &s_terrot_found };
5190 
5191 statetype s_terrot_shoot1 = { 0, SPR_DEMO, 10, nullptr, T_Shoot, &s_terrot_shoot2 };
5192 statetype s_terrot_shoot2 = { 0, SPR_DEMO, 20, nullptr, T_Shade, &s_terrot_shoot3 };
5193 statetype s_terrot_shoot3 = { 0, SPR_DEMO, 10, nullptr, T_Shoot, &s_terrot_shoot4 };
5194 statetype s_terrot_shoot4 = { 0, SPR_DEMO, 20, nullptr, T_Shade, &s_terrot_seek1 };
5195 
5196 statetype s_terrot_seek1 = { 1, SPR_DEMO, 10, T_Seek, nullptr, &s_terrot_seek1s };
5197 statetype s_terrot_seek1s = { 1, SPR_DEMO, 3, nullptr, nullptr, &s_terrot_seek1 };
5198 
5199 statetype s_terrot_die1 = { 0, SPR_DEMO, 15, nullptr, A_DeathScream, &s_terrot_die2 };
5200 statetype s_terrot_die2 = { 0, SPR_DEMO, 15, nullptr, nullptr, &s_terrot_die3 };
5201 statetype s_terrot_die3 = { 0, SPR_DEMO, 15, nullptr, nullptr, &s_terrot_die4 };
5202 statetype s_terrot_die4 = { 0, SPR_DEMO, 15, nullptr, T_Shade, &s_terrot_die5 };
5203 
5204 
5205 statetype s_terrot_die5 = { 0, SPR_DEMO, 0, nullptr, nullptr, &s_terrot_die5 };
5206 
5207 
5208 // ============================================================================
5209 //
5210 // SEEK
5211 //
5212 // ============================================================================
5213 
5214 // ---------------------------------------------------------------------------
5215 // T_Seek() - Will rotate an object (not moving) until seeing it is able
5216 //      to see the player in the facing direction.
5217 //
5218 // NOTE : This may change so that it will seek for any object.
5219 // ---------------------------------------------------------------------------
T_Seek(objtype * ob)5220 void T_Seek(
5221     objtype* ob)
5222 {
5223     const int16_t MAX_VIS_DIST = 15;
5224 
5225     int16_t dx, dy, dist, chance;
5226     bool target_found;
5227 
5228     target_found = false;
5229 
5230 
5231     if (((player->tilex != ob->tilex) || (player->tiley != ob->tiley)) && // Can you see
5232         CheckView(ob, player) && (!PlayerInvisable))
5233     {
5234         dx = ob->tilex - player->tilex;
5235         dx = LABS(dx);
5236         dy = ob->tiley - player->tiley;
5237         dy = LABS(dy);
5238 
5239         if (dy < MAX_VIS_DIST && dx < MAX_VIS_DIST) {
5240             dist = dx > dy ? dx : dy;
5241 
5242             if (!dist || (dist == 1 && ob->distance < 0x4000)) {
5243                 chance = 300;
5244             } else {
5245                 chance = US_RndT() / dist;
5246             }
5247 
5248             if (US_RndT() < chance) {
5249                 NewState(ob, &s_terrot_shoot1);
5250                 return;
5251             }
5252             target_found = true;
5253         }
5254     }
5255 
5256     if (!(ob->flags & FL_STATIONARY)) {
5257         if (target_found) {
5258             NewState(ob, &s_terrot_found);
5259         } else if ((ob->temp2 -= tics) <= 0) {
5260             NewState(ob, &s_terrot_seek1);
5261 
5262             ob->dir++;
5263             ob->temp2 = SEEK_TURN_DELAY;
5264             if (ob->dir == nodir) {
5265                 ob->dir = east;
5266             }
5267         }
5268     }
5269 }
5270 
SpawnProjectile(objtype * shooter,classtype class_type)5271 void SpawnProjectile(
5272     objtype* shooter,
5273     classtype class_type)
5274 {
5275     int16_t angle_adj = 0;
5276     uint16_t temp = 0;
5277     fixed x, y;
5278 
5279     x = shooter->x;
5280     y = shooter->y;
5281 
5282     usedummy = nevermark = true;
5283     switch (class_type) {
5284     case spider_mutantshotobj:
5285     case acid_dragonshotobj:
5286     case final_boss4shotobj:
5287         SpawnNewObj(x >> TILESHIFT, y >> TILESHIFT, &s_ofs_random);
5288 
5289         ::sd_play_actor_sound(
5290             SPITATTACKSND, new_actor, bstone::AC_VOICE);
5291 
5292         new_actor->speed = SPDPROJ;
5293         angle_adj = 1 - (US_RndT() & 3);
5294         new_actor->temp1 = BossShotShapes[class_type - spider_mutantshotobj];
5295         new_actor->flags = FL_OFFSET_STATES | FL_PROJ_CHECK_TRANSPARENT | FL_STORED_OBJPTR;
5296         new_actor->temp3 = actor_to_ui16(shooter);
5297 
5298         break;
5299 
5300     case mut_hum1shotobj:
5301     case goldmorphshotobj:
5302     case electroshotobj:
5303     case final_boss2shotobj:
5304         SpawnNewObj(x >> TILESHIFT, y >> TILESHIFT, &s_ofs_shot1);
5305 
5306         ::sd_play_actor_sound(
5307             ELECTSHOTSND, new_actor, bstone::AC_VOICE);
5308 
5309         new_actor->speed = SPDPROJ;
5310         angle_adj = 1 - (US_RndT() & 3);
5311         new_actor->temp1 = SPR_ELEC_SHOT1;
5312         new_actor->flags = FL_OFFSET_STATES | FL_PROJ_CHECK_TRANSPARENT | FL_STORED_OBJPTR;
5313         new_actor->temp3 = actor_to_ui16(shooter);
5314 
5315         switch (class_type) {
5316         case final_boss2shotobj:
5317         case goldmorphshotobj:
5318             new_actor->temp1 = SPR_MGOLD_SHOT1;
5319 
5320         case electroshotobj:
5321             new_actor->lighting = NO_SHADING;
5322 
5323         default:
5324             break;
5325         }
5326         break;
5327 
5328     case lcanshotobj:
5329     case podshotobj:
5330         temp = SPR_SPIT3_1 - SPR_SPIT1_1;
5331 
5332     case scanshotobj:
5333     case dogshotobj:
5334         SpawnNewObj(x >> TILESHIFT, y >> TILESHIFT, &s_liquid_shot);
5335 
5336         ::sd_play_actor_sound(
5337             SPITATTACKSND, new_actor, bstone::AC_VOICE);
5338 
5339         new_actor->temp2 = SPR_SPIT1_1 + temp;
5340         new_actor->flags = FL_OFFSET_STATES | FL_PROJ_CHECK_TRANSPARENT | FL_STORED_OBJPTR;
5341         new_actor->speed = SPDPROJ + US_RndT();
5342         angle_adj = 2 - (US_RndT() % 5);
5343         new_actor->temp3 = actor_to_ui16(shooter);
5344         break;
5345 
5346     case liquidshotobj:
5347         SpawnNewObj(x >> TILESHIFT, y >> TILESHIFT, &s_liquid_shot);
5348         new_actor->temp2 = SPR_LIQUID_SHOT_FLY_1;
5349         new_actor->flags = FL_OFFSET_STATES | FL_PROJ_CHECK_TRANSPARENT | FL_STORED_OBJPTR;
5350         new_actor->speed = SPDPROJ + US_RndT();
5351         angle_adj = 2 - (US_RndT() % 5);
5352         new_actor->s_tilex = new_actor->s_tiley = 0;
5353         new_actor->temp3 = actor_to_ui16(shooter);
5354         break;
5355 
5356     case grenadeobj:
5357         SpawnNewObj(x >> TILESHIFT, y >> TILESHIFT, &s_ofs_random);
5358         new_actor->speed = SPDPROJ + Random(SPDPROJ >> 1);
5359         new_actor->angle = player->angle + 1 - (US_RndT() & 3);
5360         new_actor->temp1 = grenade_shapes[0];
5361         new_actor->flags = FL_OFFSET_STATES; // |FL_PROJ_CHECK_TRANSPARENT;
5362         new_actor->lighting = NO_SHADING; // no shading
5363 
5364         // Store off start tile x & y
5365 
5366         new_actor->s_tilex = static_cast<uint8_t>(x >> TILESHIFT);
5367         new_actor->s_tiley = static_cast<uint8_t>(y >> TILESHIFT);
5368         break;
5369 
5370     case bfg_shotobj:
5371         SpawnNewObj(x >> TILESHIFT, y >> TILESHIFT, &s_ofs_random);
5372         new_actor->speed = SPDPROJ + Random(SPDPROJ);
5373         new_actor->angle = player->angle + 1 - (US_RndT() & 3);
5374         new_actor->temp1 = SPR_BFG_WEAPON_SHOT2;
5375         new_actor->flags = FL_OFFSET_STATES;
5376         new_actor->lighting = NO_SHADING;
5377 
5378         // Store off start tile x & y
5379 
5380         new_actor->s_tilex = static_cast<uint8_t>(x >> TILESHIFT);
5381         new_actor->s_tiley = static_cast<uint8_t>(y >> TILESHIFT);
5382         break;
5383 
5384     default:
5385         break;
5386     }
5387 
5388     usedummy = nevermark = false;
5389     if (new_actor == &dummyobj) {
5390         return;
5391     }
5392 
5393     new_actor->x = x;
5394     new_actor->y = y;
5395     new_actor->active = ac_yes;
5396     new_actor->obclass = class_type;
5397     if (class_type != grenadeobj && class_type != bfg_shotobj) {
5398         new_actor->angle = CalcAngle(new_actor, player) + angle_adj;
5399     }
5400 
5401     if (shooter->obclass == gold_morphobj) {
5402         new_actor->angle += morph_angle_adj;
5403     }
5404 
5405     if (new_actor->angle <= 0) {
5406         new_actor->angle += 359;
5407     } else if (new_actor->angle > 359) {
5408         new_actor->angle -= 359;
5409     }
5410 
5411     new_actor->flags |= (FL_NONMARK | FL_NEVERMARK);
5412 
5413 // Move grenade slightly in front of player so you can see instant
5414 // explosions (ie: when you're face up against a wall).
5415 //
5416     if (class_type == grenadeobj || class_type == bfg_shotobj) {
5417         int32_t deltax, deltay;
5418 
5419         deltax = FixedByFrac(mindist + (mindist >> 3), costable[new_actor->angle]);
5420         deltay = -FixedByFrac(mindist + (mindist >> 3), sintable[new_actor->angle]);
5421 
5422         new_actor->x += deltax;
5423         new_actor->y += deltay;
5424     }
5425 }
5426 
5427 objtype* proj_check;
5428 uint8_t proj_wall;
5429 
5430 // ---------------------------------------------------------------------------
5431 // ProjectileTryMove()
5432 //
5433 //  deltax - 'X' distance to travel
5434 //       deltay - 'Y' distance to travel
5435 //       distance - vectoral distance of travel
5436 //
5437 // ---------------------------------------------------------------------------
ProjectileTryMove(objtype * ob,fixed deltax,fixed deltay)5438 bool ProjectileTryMove(
5439     objtype* ob,
5440     fixed deltax,
5441     fixed deltay)
5442 {
5443 #define PROJECTILE_MAX_STEP PROJWALLSIZE
5444 
5445     uint16_t xl, yl, xh, yh, x, y, steps;
5446     int16_t ydist, xdist;
5447 
5448     proj_wall = 0;
5449     steps = tics;
5450     //
5451     // Move that lil' projectile
5452     //
5453 
5454     while (steps) {
5455         ob->x += deltax;
5456         ob->y += deltay;
5457 
5458         steps--;
5459 
5460         xl = (ob->x - PROJECTILE_MAX_STEP) >> TILESHIFT;
5461         yl = (ob->y - PROJECTILE_MAX_STEP) >> TILESHIFT;
5462 
5463         xh = (ob->x + PROJECTILE_MAX_STEP) >> TILESHIFT;
5464         yh = (ob->y + PROJECTILE_MAX_STEP) >> TILESHIFT;
5465 
5466 
5467         // Check for solid walls and actors.
5468         //
5469 
5470         for (y = yl; y <= yh; y++) {
5471             for (x = xl; x <= xh; x++) {
5472                 if ((proj_check = actorat[x][y]) != nullptr) {
5473                     if (proj_check < objlist) {
5474                         if (proj_check == (objtype*)1 && tilemap[x][y] == 0) {
5475                             // We have a static!
5476                             //
5477                             // Test for collision radius using CENTER of static
5478                             // NOT tile size boundries
5479 
5480                             ydist = static_cast<int16_t>((y << TILESHIFT) + 0x7FFF - ob->y);
5481                             ydist = ABS(ydist);
5482 
5483                             xdist = static_cast<int16_t>((x << TILESHIFT) + 0x7FFF - ob->x);
5484                             xdist = ABS(xdist);
5485 
5486                             if ((uint16_t)xdist < PROJCHECKSIZE && (uint16_t)ydist < PROJCHECKSIZE) {
5487                                 proj_check = nullptr;
5488                                 proj_wall = 0;
5489                                 ob->tilex = static_cast<uint8_t>(ob->x >> TILESHIFT);
5490                                 ob->tiley = static_cast<uint8_t>(ob->y >> TILESHIFT);
5491                                 return false;
5492                             }
5493 
5494                         } else {
5495                             // We have a wall!
5496 
5497                             proj_wall = static_cast<uint8_t>(
5498                                 reinterpret_cast<size_t>(proj_check));
5499 
5500                             proj_check = nullptr;
5501                             ob->tilex = static_cast<uint8_t>(ob->x >> TILESHIFT);
5502                             ob->tiley = static_cast<uint8_t>(ob->y >> TILESHIFT);
5503                             return false;
5504                         }
5505                     } else if (proj_check < &objlist[MAXACTORS]) {
5506                         if ((ob->flags & FL_PROJ_CHECK_TRANSPARENT) && (proj_check->flags & FL_PROJ_TRANSPARENT)) {
5507                             break;
5508                         } else if (proj_check->flags & FL_SOLID) {
5509                             ob->tilex = static_cast<uint8_t>(ob->x >> TILESHIFT);
5510                             ob->tiley = static_cast<uint8_t>(ob->y >> TILESHIFT);
5511                             return false;
5512                         }
5513                     }
5514                 }
5515             }
5516         }
5517     }
5518 
5519     return true;
5520 }
5521 
T_Projectile(objtype * ob)5522 void T_Projectile(
5523     objtype* ob)
5524 {
5525     int32_t deltax, deltay;
5526     int16_t damage = 0;
5527     int32_t speed;
5528     objtype* attacker;
5529 
5530 // Move this object.
5531 //
5532     speed = ob->speed;
5533 
5534     deltax = FixedByFrac(speed, costable[ob->angle]);
5535     deltay = -FixedByFrac(speed, sintable[ob->angle]);
5536 
5537 
5538 // Did movement hit anything solid.
5539 //
5540 
5541     proj_check = nullptr;
5542 
5543     if (!ProjectileTryMove(ob, deltax, deltay)) {
5544         switch (ob->obclass) {
5545         case spider_mutantshotobj:
5546             ::InitSmartSpeedAnim(ob, SPR_BOSS1_EXP1, 0, 2, at_ONCE, ad_FWD, 5);
5547             return;
5548             break;
5549 
5550         case acid_dragonshotobj:
5551             ::InitSmartSpeedAnim(ob, SPR_BOSS5_EXP1, 0, 2, at_ONCE, ad_FWD, 5);
5552             return;
5553             break;
5554 
5555         case mut_hum1shotobj:
5556         case electroshotobj: // Explode on walls
5557             ::InitSmartSpeedAnim(ob, SPR_ELEC_SHOT_EXP1, 0, 1, at_ONCE, ad_FWD, 5 + (US_RndT() & 3));
5558             return;
5559             break;
5560 
5561         case final_boss2shotobj:
5562         case goldmorphshotobj:
5563             ::InitSmartSpeedAnim(ob, SPR_MGOLD_SHOT_EXP1, 0, 1, at_ONCE, ad_FWD, 5 + (US_RndT() & 3));
5564             return;
5565             break;
5566 
5567         case final_boss4shotobj:
5568             ::InitSmartSpeedAnim(ob, SPR_BOSS10_SPIT_EXP1, 0, 1, at_ONCE, ad_FWD, 5 + (US_RndT() & 3));
5569             return;
5570             break;
5571 
5572         case lcanshotobj: // Explode on walls
5573         case podshotobj:
5574             ::InitSmartSpeedAnim(ob, SPR_SPIT_EXP3_1, 0, 2, at_ONCE, ad_FWD, 5 + (US_RndT() & 3));
5575             return;
5576             break;
5577 
5578         case scanshotobj: // Explode on walls
5579         case dogshotobj:
5580             ::InitSmartSpeedAnim(ob, SPR_SPIT_EXP1_1, 0, 2, at_ONCE, ad_FWD, 5 + (US_RndT() & 7));
5581             return;
5582             break;
5583 
5584 
5585         case liquidshotobj: // Explode on walls
5586             ::InitSmartSpeedAnim(ob, SPR_LIQUID_SHOT_BURST_1, 0, 2, at_ONCE, ad_FWD, 5 + (US_RndT() & 7));
5587             return;
5588             break;
5589 
5590         case grenadeobj:
5591             // Hit actor - Hurt 'Em!
5592             if (proj_check) {
5593                 if (proj_check->flags & FL_SHOOTABLE) {
5594                     DamageActor(proj_check, GR_DAMAGE, ob);
5595                 }
5596             }
5597 
5598             //
5599             // Start Anim, Sound, and mark as exploded...
5600             //
5601 
5602             ob->obclass = gr_explosionobj;
5603             ::InitSmartSpeedAnim(ob, SPR_EXPLOSION_1, 0, 4, at_ONCE, ad_FWD, 3 + (US_RndT() & 7));
5604             A_DeathScream(ob);
5605             return;
5606             break;
5607 
5608         case bfg_shotobj:
5609 
5610 #if BFG_SHOT_STOPS
5611             //
5612             // Check to see if a collison has already occured at this
5613             // tilex and tiley
5614             //
5615             if (ob->s_tilex == ob->tilex && ob->s_tiley == ob->tiley) {
5616                 return;
5617             }
5618 
5619             ob->s_tilex = ob->tilex;
5620             ob->s_tilex = ob->tilex;
5621 #endif
5622 
5623             if (proj_wall & 0x80) {
5624                 TryBlastDoor(proj_wall & (~0x80));
5625             }
5626 
5627             if (proj_check) {
5628                 // Hit actor - Hurt 'Em!
5629 
5630                 if (proj_check->flags2 & FL2_BFG_SHOOTABLE) {
5631                     // Damage that actor
5632 
5633                     DamageActor(proj_check, BFG_DAMAGE >> 1, ob); // bfg_damage>>3
5634 
5635                     // Stop on actors that you don't kill.
5636 
5637                 }
5638 
5639 #if BFG_SHOT_STOPS
5640                 if (proj_check->flags2 & FL2_BFGSHOT_SOLID) {
5641                     goto BlowIt;
5642                 }
5643 
5644                 if (proj_check->flags & FL_DEADGUY) {
5645                     return;
5646                 }
5647 #endif
5648             }
5649 
5650 
5651 BlowIt:
5652             //
5653             // Start Anim, Sound, and mark as exploded...
5654             //
5655             ob->obclass = bfg_explosionobj;
5656             InitAnim(ob, SPR_BFG_EXP1, 0, 7, at_ONCE, ad_FWD, (US_RndT() & 7), 5);
5657             A_DeathScream(ob);
5658             return;
5659             break;
5660 
5661         default:
5662             break;
5663         }
5664     }
5665 
5666 // Determine if object hit player.
5667 //
5668     if (ob->obclass != grenadeobj && ob->obclass != bfg_shotobj) {
5669         // Determine distance from player.
5670         //
5671         deltax = ob->x - player->x;
5672         deltax = LABS(deltax);
5673         deltay = ob->y - player->y;
5674         deltay = LABS(deltay);
5675 
5676         if (deltax < PROJECTILESIZE && deltay < PROJECTILESIZE) {
5677             deltax = FixedByFrac(PROJECTILESIZE, costable[ob->angle]);
5678             deltay = -FixedByFrac(PROJECTILESIZE, sintable[ob->angle]);
5679 
5680             ob->x -= deltax;
5681             ob->y -= deltay;
5682 
5683             if (ob->flags & FL_STORED_OBJPTR) {
5684                 attacker = ui16_to_actor(ob->temp3);
5685             } else {
5686                 attacker = ob;
5687             }
5688 
5689             switch (ob->obclass) {
5690             case mut_hum1shotobj:
5691             case electroshotobj:
5692                 damage = (US_RndT() >> 5);
5693                 ::InitSmartSpeedAnim(ob, SPR_ELEC_SHOT_EXP1, 0, 1, at_ONCE, ad_FWD, 3 + (US_RndT() & 7));
5694                 break;
5695 
5696             case final_boss4shotobj:
5697                 damage = (US_RndT() >> 4);
5698                 ::InitSmartSpeedAnim(ob, SPR_BOSS10_SPIT_EXP1, 0, 1, at_ONCE, ad_FWD, 3 + (US_RndT() & 3));
5699                 break;
5700 
5701             case goldmorphshotobj:
5702             case final_boss2shotobj:
5703                 damage = (US_RndT() >> 4);
5704                 ::InitSmartSpeedAnim(ob, SPR_MGOLD_SHOT_EXP1, 0, 1, at_ONCE, ad_FWD, 5 + (US_RndT() & 7));
5705                 break;
5706 
5707             case lcanshotobj:
5708             case podshotobj:
5709                 damage = (US_RndT() >> 4);
5710                 ::InitSmartSpeedAnim(ob, SPR_SPIT_EXP3_1, 0, 2, at_ONCE, ad_FWD, 5 + (US_RndT() & 7));
5711                 break;
5712 
5713             case scanshotobj:
5714             case dogshotobj:
5715                 damage = (US_RndT() >> 4);
5716                 ::InitSmartSpeedAnim(ob, SPR_SPIT_EXP1_1, 0, 2, at_ONCE, ad_FWD, 5 + (US_RndT() & 7));
5717                 break;
5718 
5719             case liquidshotobj:
5720                 damage = (US_RndT() >> 4);
5721                 ::InitSmartSpeedAnim(ob, SPR_LIQUID_SHOT_BURST_1, 0, 2, at_ONCE, ad_FWD, 5 + (US_RndT() & 7));
5722                 break;
5723 
5724             case spider_mutantshotobj:
5725                 damage = (US_RndT() >> 4);
5726                 ::InitSmartSpeedAnim(ob, SPR_BOSS1_EXP1, 0, 2, at_ONCE, ad_FWD, 5 + (US_RndT() & 7));
5727                 break;
5728 
5729             case acid_dragonshotobj:
5730                 damage = (US_RndT() >> 4);
5731                 ::InitSmartSpeedAnim(ob, SPR_BOSS5_EXP1, 0, 2, at_ONCE, ad_FWD, 5 + (US_RndT() & 7));
5732                 break;
5733 
5734             default:
5735                 break;
5736             }
5737 
5738             TakeDamage(damage, attacker);
5739             return;
5740         }
5741     }
5742 
5743 // Align tile coordinates on boundaries.
5744 //
5745     ob->tilex = static_cast<uint8_t>(ob->x >> TILESHIFT);
5746     ob->tiley = static_cast<uint8_t>(ob->y >> TILESHIFT);
5747 }
5748 
5749 
5750 
5751 #define EX_RADIUS 2 // Tiles out from center
5752 
5753 int8_t ff_buffer[EX_RADIUS * 2 + 1][EX_RADIUS * 2 + 1];
5754 int16_t ff_damageplayer, ff_damage;
5755 objtype* ff_obj;
5756 
5757 void ExplodeFill(
5758     int8_t tx,
5759     int8_t ty);
5760 
ExplodeRadius(objtype * obj,int16_t damage,bool damageplayer)5761 void ExplodeRadius(
5762     objtype* obj,
5763     int16_t damage,
5764     bool damageplayer)
5765 {
5766 //
5767 // Did this object start out in a wall?
5768 //
5769     if (tilemap[obj->tilex][obj->tiley]) {
5770         return;
5771     }
5772 
5773 // Setup globals to minimize parameter passing while recursing!
5774 //
5775     ff_obj = obj;
5776     ff_damage = damage;
5777 
5778 // Check to see if play is a Baby and should not be hurt by explosions,
5779 // except from actors that use explosions for attacks (IE. PerScan Drones).
5780 //
5781     if (gamestate.difficulty > gd_baby || obj->obclass == floatingbombobj) {
5782         ff_damageplayer = damageplayer;
5783     } else {
5784         ff_damageplayer = false;
5785     }
5786 
5787     ff_obj = obj;
5788     memset(ff_buffer, 0, sizeof(ff_buffer));
5789     ExplodeFill(obj->tilex, obj->tiley);
5790     ExplodeStatics(obj->tilex, obj->tiley);
5791 }
5792 
ExplodeFill(int8_t tx,int8_t ty)5793 void ExplodeFill(
5794     int8_t tx,
5795     int8_t ty)
5796 {
5797     int8_t bx = tx - ff_obj->tilex + EX_RADIUS,
5798          by = ty - ff_obj->tiley + EX_RADIUS,
5799          door, no_wall;
5800 
5801 // Damage actors on this spot!
5802 //
5803     if (ff_damageplayer && tx == player->tilex && ty == player->tiley) {
5804         TakeDamage(EXPLODE_DAMAGE, ff_obj);
5805     } else {
5806         proj_check = actorat[tx & 63][ty & 63];
5807 
5808         if ((proj_check >= objlist) && (proj_check < &objlist[MAXACTORS])) {
5809             if ((proj_check->flags & FL_SHOOTABLE)) {
5810                 switch (proj_check->obclass) {
5811                 // Detinate all floating bombs & VMTs
5812                 //
5813                 case floatingbombobj:
5814                 case volatiletransportobj:
5815                     DamageActor(proj_check, 500, ff_obj);
5816                     break;
5817 
5818                 // Hanging turrets are not effected by
5819                 // concussion weapons.
5820                 //
5821                 case hang_terrotobj:
5822                 case arc_barrierobj:
5823                 case post_barrierobj:
5824                 case vpost_barrierobj:
5825                 case vspike_barrierobj:
5826                     break;
5827 
5828                 //
5829                 // Test for Level completion object
5830                 //
5831                 case rotating_cubeobj:
5832                     if (!::is_ps()) {
5833                         break;
5834                     }
5835 
5836                     if (ff_obj->obclass == pd_explosionobj) {
5837                         proj_check->lighting = EXPLOSION_SHADING;
5838                         proj_check->flags &= ~(FL_SOLID | FL_SHOOTABLE);
5839                         ::InitSmartSpeedAnim(proj_check, SPR_CUBE_EXP1, 0, 8, at_ONCE, ad_FWD, 5);
5840 
5841                         ::sd_play_actor_sound(
5842                             EXPLODE1SND, proj_check, bstone::AC_VOICE);
5843 
5844                         // Unlock Next floor
5845 
5846                         gamestuff.level[gamestate.mapon + 1].locked = false;
5847                     }
5848                     break;
5849 
5850                 //
5851                 // Plasma/Fision Detonators (already armed)
5852                 //
5853                 case plasma_detonatorobj:
5854                     if (ff_obj == player || // Player shot it with gun
5855                         (ff_obj->tilex == tx && ff_obj->tiley == ty)) // Direct Hit with grenade
5856                     {
5857                         DamageActor(proj_check, 1, ff_obj);
5858                     } else {
5859                         DamageActor(proj_check, 20, ff_obj); // An explosion has started a chain reaction
5860                     }
5861                     break;
5862 
5863 
5864                 // Everyone else gets the shit kicked
5865                 // out of them...
5866                 //
5867                 default:
5868                     if (!::is_ps() || (::is_ps() && !(proj_check->flags2 & FL2_CLOAKED)))
5869                         SpawnFlash(proj_check->x, proj_check->y);
5870                     DamageActor(proj_check, ff_damage, ff_obj);
5871                     break;
5872                 }
5873             }
5874         }
5875     }
5876 
5877 // Mark spot as exploded!
5878 //
5879     ff_buffer[static_cast<int>(bx)][static_cast<int>(by)] = 1;
5880 
5881 // Explode to the EAST!
5882 //
5883     bx += 1;
5884     tx += 1;
5885 
5886     door = tilemap[static_cast<int>(tx)][static_cast<int>(ty)];
5887     if (door & 0x80) {
5888         no_wall = doorobjlist[door & 0x7f].action != dr_closed;
5889     } else {
5890         no_wall = !tilemap[static_cast<int>(tx)][static_cast<int>(ty)];
5891     }
5892 
5893     if ((!ff_buffer[static_cast<int>(bx)][static_cast<int>(by)]) && (no_wall) && (bx <= EX_RADIUS * 2)) {
5894         ExplodeFill(tx, ty);
5895     }
5896 
5897 // Explode to the WEST!
5898 //
5899     bx -= 2;
5900     tx -= 2;
5901 
5902     door = tilemap[static_cast<int>(tx)][static_cast<int>(ty)];
5903     if (door & 0x80) {
5904         no_wall = doorobjlist[door & 0x7f].action != dr_closed;
5905     } else {
5906         no_wall = !tilemap[static_cast<int>(tx)][static_cast<int>(ty)];
5907     }
5908 
5909     if ((!ff_buffer[static_cast<int>(bx)][static_cast<int>(by)]) && (no_wall) && (bx >= 0)) {
5910         ExplodeFill(tx, ty);
5911     }
5912 
5913 // Explode to the SOUTH!
5914 //
5915     bx++;
5916     tx++;
5917     by += 1;
5918     ty += 1;
5919 
5920     door = tilemap[static_cast<int>(tx)][static_cast<int>(ty)];
5921     if (door & 0x80) {
5922         no_wall = doorobjlist[door & 0x7f].action != dr_closed;
5923     } else {
5924         no_wall = !tilemap[static_cast<int>(tx)][static_cast<int>(ty)];
5925     }
5926 
5927     if ((!ff_buffer[static_cast<int>(bx)][static_cast<int>(by)]) && (no_wall) && (by <= EX_RADIUS * 2)) {
5928         ExplodeFill(tx, ty);
5929     }
5930 
5931 // Explode to the NORTH!
5932 //
5933     by -= 2;
5934     ty -= 2;
5935 
5936     door = tilemap[static_cast<int>(tx)][static_cast<int>(ty)];
5937     if (door & 0x80) {
5938         no_wall = doorobjlist[door & 0x7f].action != dr_closed;
5939     } else {
5940         no_wall = !tilemap[static_cast<int>(tx)][static_cast<int>(ty)];
5941     }
5942 
5943     if ((!ff_buffer[static_cast<int>(bx)][static_cast<int>(by)]) && (no_wall) && (by >= 0)) {
5944         ExplodeFill(tx, ty);
5945     }
5946 }
5947 
5948 // ---------------------------------------------------------------------------
5949 // CalcAngle() - Calculates angle from 1st object to 2nd object.
5950 // ---------------------------------------------------------------------------
CalcAngle(objtype * from_obj,objtype * to_obj)5951 int16_t CalcAngle(
5952     objtype* from_obj,
5953     objtype* to_obj)
5954 {
5955     int32_t deltax, deltay, from_x, from_y, to_x, to_y;
5956     float angle;
5957     int16_t iangle;
5958 
5959     from_x = from_obj->x;
5960     from_y = from_obj->y;
5961 
5962     to_x = to_obj->x;
5963     to_y = to_obj->y;
5964 
5965     // Calculate deltas from "from_obj" to "to_obj".
5966     //
5967 
5968     deltax = to_x - from_x;
5969     deltay = from_y - to_y;
5970 
5971     if (!(deltax | deltay)) {
5972         return 1;
5973     }
5974 
5975     // Calc Arc Tan from Obj1 to Obj2 - Returns radians
5976 
5977     angle = static_cast<float>(atan2(static_cast<double>(deltay), static_cast<double>(deltax)));
5978 
5979     if (angle < 0) {
5980         angle = static_cast<float>(::m_pi() * 2 + angle);
5981     }
5982 
5983     // Convert rads to degs
5984 
5985     iangle = static_cast<int16_t>(angle / (::m_pi() * 2) * ANGLES);
5986 
5987     return iangle;
5988 }
5989 
T_BlowBack(objtype * obj)5990 void T_BlowBack(
5991     objtype* obj)
5992 {
5993 #define SLIDE_SPEED 0x2000
5994 
5995     uint16_t dist_table[] = { 0x1000, // wp_autocharge,
5996                             0x2000, // wp_pistol,
5997                             0x3000, // wp_burst_rifle,
5998                             0x4000, // wp_ion_cannon,
5999                             0x5000, // wp_grenade,
6000     };
6001 
6002     int32_t deltax, deltay;
6003     uint16_t dist;
6004     objtype* killer;
6005 
6006     if (obj->flags & FL_NO_SLIDE) {
6007         return;
6008     }
6009 
6010     if (!(obj->flags & FL_SLIDE_INIT)) {
6011         // Check for NULL ptr
6012 
6013         killer = (objtype*)SLIDE_TEMP(obj);
6014         if (!killer) {
6015             obj->flags |= FL_NO_SLIDE;
6016             return;
6017         }
6018 
6019         obj->angle = CalcAngle(killer, obj);
6020 
6021         if ((killer = SLIDE_TEMP(obj)) == player) {
6022             *((uint16_t*)&obj->hitpoints) = dist_table[static_cast<int>(gamestate.weapon)];
6023         } else {
6024             *((uint16_t*)&obj->hitpoints) = dist_table[wp_grenade];
6025         }
6026 
6027         obj->flags |= FL_SLIDE_INIT;
6028     }
6029 
6030     if ((uint16_t)obj->hitpoints > SLIDE_SPEED) {
6031         dist = SLIDE_SPEED;
6032         *((uint16_t*)&obj->hitpoints) -= SLIDE_SPEED;
6033     } else {
6034         dist = (uint16_t)obj->hitpoints;
6035         obj->flags |= FL_NO_SLIDE; // Stop any more sliding
6036     }
6037 
6038     deltax = FixedByFrac(dist, costable[obj->angle]); // Optomize - Store in actor
6039     deltay = -FixedByFrac(dist, sintable[obj->angle]); //
6040 
6041     if (ClipMove(obj, deltax, deltay)) {
6042         obj->flags |= FL_NO_SLIDE;
6043     }
6044 
6045     obj->tilex = static_cast<uint8_t>(obj->x >> TILESHIFT);
6046     obj->tiley = static_cast<uint8_t>(obj->y >> TILESHIFT);
6047 
6048     if (obj->flags & FL_NO_SLIDE) {
6049         // Set actor WHERE IT SLID in actorat[], IF there's a door!
6050         //
6051         if (tilemap[obj->tilex][obj->tiley] & 0x80) {
6052             actorat[obj->tilex][obj->tiley] = obj;
6053             obj->flags &= ~FL_NEVERMARK;
6054         }
6055     }
6056 }
6057