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