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 OpenDoor(
29 int16_t door);
30
31 void A_DeathScream(
32 objtype* ob);
33
34 void PlaceItemType(
35 int16_t itemtype,
36 int16_t tilex,
37 int16_t tiley);
38
39 void PlaceItemNearTile(
40 int16_t itemtype,
41 int16_t tilex,
42 int16_t tiley);
43
44 void ChangeShootMode(
45 objtype* ob);
46
47
48 /*
49 =============================================================================
50
51 GLOBAL VARIABLES
52
53 =============================================================================
54 */
55
56
57 dirtype opposite[9] =
58 { west, southwest, south, southeast, east, northeast, north, northwest, nodir };
59
60 dirtype diagonal[9][9] = {
61 /* east */ { nodir, nodir, northeast, nodir, nodir, nodir, southeast, nodir, nodir },
62 { nodir, nodir, nodir, nodir, nodir, nodir, nodir, nodir, nodir },
63 /* north */ { northeast, nodir, nodir, nodir, northwest, nodir, nodir, nodir, nodir },
64 { nodir, nodir, nodir, nodir, nodir, nodir, nodir, nodir, nodir },
65 /* west */ { nodir, nodir, northwest, nodir, nodir, nodir, southwest, nodir, nodir },
66 { nodir, nodir, nodir, nodir, nodir, nodir, nodir, nodir, nodir },
67 /* south */ { southeast, nodir, nodir, nodir, southwest, nodir, nodir, nodir, nodir },
68 { nodir, nodir, nodir, nodir, nodir, nodir, nodir, nodir, nodir },
69 { nodir, nodir, nodir, nodir, nodir, nodir, nodir, nodir, nodir }
70 };
71
72
73 void SpawnNewObj(
74 uint16_t tilex,
75 uint16_t tiley,
76 statetype* state);
77
78 void NewState(
79 objtype* ob,
80 statetype* state);
81
82 bool TryWalk(
83 objtype* ob,
84 bool moveit);
85
86 void MoveObj(
87 objtype* ob,
88 int32_t move);
89
90 void KillActor(
91 objtype* ob);
92
93 bool CheckLine(
94 objtype* from_obj,
95 objtype* to_obj);
96
97 void FirstSighting(
98 objtype* ob);
99
100 bool CheckSight(
101 objtype* from_obj,
102 objtype* to_obj);
103
104 bool ElevatorFloor(
105 int8_t x,
106 int8_t y);
107
108 /*
109 ===================
110 =
111 = SpawnNewObj
112 =
113 = Spaws a new actor at the given TILE coordinates, with the given state, and
114 = the given size in GLOBAL units.
115 =
116 = new = a pointer to an initialized new actor
117 =
118 ===================
119 */
SpawnNewObj(uint16_t tilex,uint16_t tiley,statetype * state)120 void SpawnNewObj(
121 uint16_t tilex,
122 uint16_t tiley,
123 statetype* state)
124 {
125 GetNewActor();
126 new_actor->state = state;
127 new_actor->ticcount = static_cast<int16_t>(Random(static_cast<uint16_t>(
128 state->tictime)) + 1);
129
130 new_actor->tilex = static_cast<uint8_t>(tilex);
131 new_actor->tiley = static_cast<uint8_t>(tiley);
132 new_actor->x = ((int32_t)tilex << TILESHIFT) + TILEGLOBAL / 2;
133 new_actor->y = ((int32_t)tiley << TILESHIFT) + TILEGLOBAL / 2;
134 new_actor->dir = new_actor->trydir = nodir;
135
136 if (!nevermark) {
137 if (!actorat[tilex][tiley]) {
138 actorat[tilex][tiley] = new_actor;
139 }
140 }
141
142 new_actor->areanumber = GetAreaNumber(new_actor->tilex, new_actor->tiley);
143 }
144
145 /*
146 ===================
147 =
148 = NewState
149 =
150 = Changes ob to a new state, setting ticcount to the max for that state
151 =
152 ===================
153 */
NewState(objtype * ob,statetype * state)154 void NewState(
155 objtype* ob,
156 statetype* state)
157 {
158 ob->state = state;
159 ob->ticcount = static_cast<int16_t>(state->tictime);
160 }
161
162
163 /*
164 =============================================================================
165
166 ENEMY TILE WORLD MOVEMENT CODE
167
168 =============================================================================
169 */
170
CHECKDIAG(int x,int y)171 static bool CHECKDIAG(
172 int x,
173 int y)
174 {
175 auto actor = ::actorat[x][y];
176 auto temp = reinterpret_cast<size_t>(actor);
177
178 if (temp != 0) {
179 if (temp < 256) {
180 return false;
181 }
182
183 if ((actor->flags & FL_SOLID) != 0) {
184 return false;
185 }
186 }
187
188 if (::ElevatorFloor(static_cast<int8_t>(x), static_cast<int8_t>(y))) {
189 return false;
190 }
191
192 return true;
193 }
194
CHECKSIDE(int x,int y,int16_t & door_index)195 static bool CHECKSIDE(
196 int x,
197 int y,
198 int16_t& door_index)
199 {
200 auto actor = ::actorat[x][y];
201 auto temp = reinterpret_cast<size_t>(actor);
202
203 if (temp != 0) {
204 if (temp < 128) {
205 return false;
206 }
207
208 if (temp < 256) {
209 door_index = temp & 63;
210
211 if (::doorobjlist[door_index].lock != kt_none) {
212 return false;
213 }
214 } else if ((actor->flags & FL_SOLID) != 0) {
215 return false;
216 }
217 }
218
219 return true;
220 }
221
222 /*
223 ==================================
224 =
225 = TryWalk
226 =
227 = Attempts to move ob in its current (ob->dir) direction.
228 =
229 = If blocked by either a wall or an actor returns FALSE
230 =
231 = If move is either clear or blocked only by a door, returns TRUE and sets
232 =
233 = ob->tilex = new destination
234 = ob->tiley
235 = ob->areanumber = the floor tile number (0-(NUMAREAS-1)) of destination
236 = ob->distance = TILEGLOBAl, or -doornumber if a door is blocking the way
237 =
238 = If a door is in the way, an OpenDoor call is made to start it opening.
239 = The actor code should wait until
240 = doorobjlist[-ob->distance].action = dr_open, meaning the door has been
241 = fully opened
242 =
243 ==================================
244 */
TryWalk(objtype * ob,bool moveit)245 bool TryWalk(
246 objtype* ob,
247 bool moveit)
248 {
249 uint8_t old_tilex = ob->tilex, old_tiley = ob->tiley;
250
251 if (ElevatorFloor(ob->tilex, ob->tiley)) {
252 return false;
253 }
254
255 int16_t door_index = -1;
256
257 switch (ob->dir) {
258 case north:
259 if (!::CHECKSIDE(ob->tilex, ob->tiley - 1, door_index)) {
260 return false;
261 }
262
263 if (ElevatorFloor(ob->tilex, ob->tiley - 1)) {
264 return false;
265 }
266
267 if (!moveit) {
268 return true;
269 }
270
271 ob->tiley--;
272 break;
273
274 case northeast:
275 if (!::CHECKDIAG(ob->tilex + 1, ob->tiley - 1)) {
276 return false;
277 }
278 if (!::CHECKDIAG(ob->tilex + 1, ob->tiley)) {
279 return false;
280 }
281 if (!::CHECKDIAG(ob->tilex, ob->tiley - 1)) {
282 return false;
283 }
284
285 if (!moveit) {
286 return true;
287 }
288
289 ob->tilex++;
290 ob->tiley--;
291 break;
292
293 case east:
294 if (!::CHECKSIDE(ob->tilex + 1, ob->tiley, door_index)) {
295 return false;
296 }
297
298 if (ElevatorFloor(ob->tilex + 1, ob->tiley)) {
299 if ((door_index != -1) && (ob->obclass != electrosphereobj)) {
300 OpenDoor(door_index);
301 }
302
303 return false;
304 }
305
306 if (!moveit) {
307 return true;
308 }
309
310 ob->tilex++;
311 break;
312
313 case southeast:
314 if (!::CHECKDIAG(ob->tilex + 1, ob->tiley + 1)) {
315 return false;
316 }
317 if (!::CHECKDIAG(ob->tilex + 1, ob->tiley)) {
318 return false;
319 }
320 if (!::CHECKDIAG(ob->tilex, ob->tiley + 1)) {
321 return false;
322 }
323
324 if (!moveit) {
325 return true;
326 }
327
328 ob->tilex++;
329 ob->tiley++;
330 break;
331
332 case south:
333 if (!::CHECKSIDE(ob->tilex, ob->tiley + 1, door_index)) {
334 return false;
335 }
336
337 if (ElevatorFloor(ob->tilex, ob->tiley + 1)) {
338 return false;
339 }
340
341 if (!moveit) {
342 return true;
343 }
344
345 ob->tiley++;
346 break;
347
348 case southwest:
349 if (!::CHECKDIAG(ob->tilex - 1, ob->tiley + 1)) {
350 return false;
351 }
352 if (!::CHECKDIAG(ob->tilex - 1, ob->tiley)) {
353 return false;
354 }
355 if (!::CHECKDIAG(ob->tilex, ob->tiley + 1)) {
356 return false;
357 }
358
359 if (!moveit) {
360 return true;
361 }
362
363 ob->tilex--;
364 ob->tiley++;
365 break;
366
367 case west:
368 if (!::CHECKSIDE(ob->tilex - 1, ob->tiley, door_index)) {
369 return false;
370 }
371
372 if (ElevatorFloor(ob->tilex - 1, ob->tiley)) {
373 if ((door_index != -1) && (ob->obclass != electrosphereobj)) {
374 OpenDoor(door_index);
375 }
376
377 return false;
378 }
379
380 if (!moveit) {
381 return true;
382 }
383
384 ob->tilex--;
385 break;
386
387 case northwest:
388 if (!::CHECKDIAG(ob->tilex - 1, ob->tiley - 1)) {
389 return false;
390 }
391 if (!::CHECKDIAG(ob->tilex - 1, ob->tiley)) {
392 return false;
393 }
394 if (!::CHECKDIAG(ob->tilex, ob->tiley - 1)) {
395 return false;
396 }
397
398 if (!moveit) {
399 return true;
400 }
401
402 ob->tilex--;
403 ob->tiley--;
404 break;
405
406 case nodir:
407 return false;
408
409 default:
410 return false; // jam/jdebug
411 }
412
413 // Should actor open this door?
414 //
415 if (door_index != -1) {
416 switch (ob->obclass) {
417 // Actors that don't open doors.
418 //
419 case liquidobj:
420 case electrosphereobj:
421 ob->tilex = old_tilex;
422 ob->tiley = old_tiley;
423 return false;
424
425 // All other actors open doors.
426 //
427 default:
428 OpenDoor(door_index);
429 ob->distance = -door_index - 1;
430 return true;
431 }
432 }
433
434 ob->areanumber = GetAreaNumber(ob->tilex, ob->tiley);
435 ob->distance = TILEGLOBAL;
436 return true;
437 }
438
ElevatorFloor(int8_t x,int8_t y)439 bool ElevatorFloor(
440 int8_t x,
441 int8_t y)
442 {
443 uint8_t tile = static_cast<uint8_t>(*(mapsegs[0] + farmapylookup[static_cast<int>(y)] + x));
444
445 if (tile >= HIDDENAREATILE) {
446 tile -= HIDDENAREATILE;
447 } else {
448 tile -= AREATILE;
449 }
450
451 return tile == 0;
452 }
453
454 /*
455 ==================================
456 =
457 = SelectDodgeDir
458 =
459 = Attempts to choose and initiate a movement for ob that sends it towards
460 = the player while dodging
461 =
462 = If there is no possible move (ob is totally surrounded)
463 =
464 = ob->dir = nodir
465 =
466 = Otherwise
467 =
468 = ob->dir = new direction to follow
469 = ob->distance = TILEGLOBAL or -doornumber
470 = ob->tilex = new destination
471 = ob->tiley
472 = ob->areanumber = the floor tile number (0-(NUMAREAS-1)) of destination
473 =
474 ==================================
475 */
SelectDodgeDir(objtype * ob)476 void SelectDodgeDir(
477 objtype* ob)
478 {
479 int16_t deltax = 0, deltay = 0, i;
480 uint16_t absdx, absdy;
481 dirtype dirtry[5];
482 dirtype turnaround, tdir;
483
484 if (ob->flags & FL_FIRSTATTACK) {
485 //
486 // turning around is only ok the very first time after noticing the
487 // player
488 //
489 turnaround = nodir;
490 ob->flags &= ~FL_FIRSTATTACK;
491 } else {
492 turnaround = opposite[ob->dir];
493 }
494
495 SeekPlayerOrStatic(ob, &deltax, &deltay);
496
497 //
498 // arange 5 direction choices in order of preference
499 // the four cardinal directions plus the diagonal straight towards
500 // the player
501 //
502
503 if (deltax > 0) {
504 dirtry[1] = east;
505 dirtry[3] = west;
506 } else if (deltax <= 0) {
507 dirtry[1] = west;
508 dirtry[3] = east;
509 }
510
511 if (deltay > 0) {
512 dirtry[2] = south;
513 dirtry[4] = north;
514 } else if (deltay <= 0) {
515 dirtry[2] = north;
516 dirtry[4] = south;
517 }
518
519 //
520 // randomize a bit for dodging
521 //
522 absdx = static_cast<uint16_t>(abs(deltax));
523 absdy = static_cast<uint16_t>(abs(deltay));
524
525 if (absdx > absdy) {
526 tdir = dirtry[1];
527 dirtry[1] = dirtry[2];
528 dirtry[2] = tdir;
529 tdir = dirtry[3];
530 dirtry[3] = dirtry[4];
531 dirtry[4] = tdir;
532 }
533
534 if (US_RndT() < 128) {
535 tdir = dirtry[1];
536 dirtry[1] = dirtry[2];
537 dirtry[2] = tdir;
538 tdir = dirtry[3];
539 dirtry[3] = dirtry[4];
540 dirtry[4] = tdir;
541 }
542
543 dirtry[0] = diagonal [ dirtry[1] ] [ dirtry[2] ];
544
545 //
546 // try the directions util one works
547 //
548 for (i = 0; i < 5; i++) {
549 if (dirtry[i] == nodir || dirtry[i] == turnaround) {
550 continue;
551 }
552
553 ob->dir = dirtry[i];
554 if (TryWalk(ob, true)) {
555 return;
556 }
557 }
558
559 //
560 // turn around only as a last resort
561 //
562 if (turnaround != nodir) {
563 ob->dir = turnaround;
564
565 if (TryWalk(ob, true)) {
566 return;
567 }
568 }
569
570 ob->dir = nodir;
571
572 if (ob->obclass == electrosphereobj) {
573 ob->s_tilex = 0;
574 }
575 }
576
577 /*
578 ============================
579 =
580 = SelectChaseDir
581 =
582 = As SelectDodgeDir, but doesn't try to dodge
583 =
584 ============================
585 */
SelectChaseDir(objtype * ob)586 void SelectChaseDir(
587 objtype* ob)
588 {
589 int16_t deltax = 0, deltay = 0;
590 dirtype d[3];
591 dirtype tdir, olddir, turnaround;
592
593
594 olddir = ob->dir;
595 turnaround = opposite[olddir];
596
597 SeekPlayerOrStatic(ob, &deltax, &deltay);
598
599 d[1] = nodir;
600 d[2] = nodir;
601
602 if (deltax > 0) {
603 d[1] = east;
604 } else if (deltax < 0) {
605 d[1] = west;
606 }
607 if (deltay > 0) {
608 d[2] = south;
609 } else if (deltay < 0) {
610 d[2] = north;
611 }
612
613 if (abs(deltay) > abs(deltax)) {
614 tdir = d[1];
615 d[1] = d[2];
616 d[2] = tdir;
617 }
618
619 if (d[1] == turnaround) {
620 d[1] = nodir;
621 }
622 if (d[2] == turnaround) {
623 d[2] = nodir;
624 }
625
626
627 if (d[1] != nodir) {
628 ob->dir = d[1];
629 if (TryWalk(ob, true)) {
630 return; /*either moved forward or attacked*/
631 }
632 }
633
634 if (d[2] != nodir) {
635 ob->dir = d[2];
636 if (TryWalk(ob, true)) {
637 return;
638 }
639 }
640
641 /* there is no direct path to the player, so pick another direction */
642
643 if (olddir != nodir) {
644 ob->dir = olddir;
645 if (TryWalk(ob, true)) {
646 return;
647 }
648 }
649
650 if (US_RndT() > 128) { /*randomly determine direction of search*/
651 for (tdir = north; tdir <= west; tdir++) {
652 if (tdir != turnaround) {
653 ob->dir = tdir;
654 if (TryWalk(ob, true)) {
655 return;
656 }
657 }
658 }
659 } else {
660 for (tdir = west; tdir >= north; tdir--) {
661 if (tdir != turnaround) {
662 ob->dir = tdir;
663 if (TryWalk(ob, true)) {
664 return;
665 }
666 }
667 }
668 }
669
670 if (turnaround != nodir) {
671 ob->dir = turnaround;
672 if (ob->dir != nodir) {
673 if (TryWalk(ob, true)) {
674 return;
675 }
676 }
677 }
678
679 ob->dir = nodir; // can't move
680 if (ob->obclass == electrosphereobj) {
681 ob->s_tilex = 0;
682 }
683 }
684
GetCornerSeek(objtype * ob)685 void GetCornerSeek(
686 objtype* ob)
687 {
688 uint8_t SeekPointX[] = { 32, 63, 32, 1 }; // s_tilex can't seek to 0!
689 uint8_t SeekPointY[] = { 1, 63, 32, 1 };
690 uint8_t seek_tile = US_RndT() & 3;
691
692 ob->flags &= ~FL_RUNTOSTATIC;
693 ob->s_tilex = SeekPointX[seek_tile];
694 ob->s_tiley = SeekPointY[seek_tile];
695 }
696
697
698 extern int32_t last_objy;
699
700 /*
701 =================
702 =
703 = MoveObj
704 =
705 = Moves ob be move global units in ob->dir direction
706 = Actors are not allowed to move inside the player
707 = Does NOT check to see if the move is tile map valid
708 =
709 = ob->x = adjusted for new position
710 = ob->y
711 =
712 =================
713 */
MoveObj(objtype * ob,int32_t move)714 void MoveObj(
715 objtype* ob,
716 int32_t move)
717 {
718 int32_t sign_x = 0;
719 int32_t sign_y = 0;
720
721 switch (ob->dir) {
722 case north:
723 sign_y = -1;
724 break;
725
726 case northeast:
727 sign_x = 1;
728 sign_y = -1;
729 break;
730
731 case east:
732 sign_x = 1;
733 break;
734
735 case southeast:
736 sign_x = 1;
737 sign_y = 1;
738 break;
739
740 case south:
741 sign_y = 1;
742 break;
743
744 case southwest:
745 sign_x = -1;
746 sign_y = 1;
747 break;
748
749 case west:
750 sign_x = -1;
751 break;
752
753 case northwest:
754 sign_x = -1;
755 sign_y = -1;
756 break;
757
758 case nodir:
759 return;
760
761 default:
762 ::Quit("Illegal direction passed.");
763 return;
764 }
765
766 ob->x += sign_x * move;
767 ob->y += sign_y * move;
768
769 //
770 // check to make sure it's not on top of player
771 //
772 if (ob->obclass != electrosphereobj &&
773 ::areabyplayer[ob->areanumber])
774 {
775 auto dx = std::abs(ob->x - player->x);
776 auto dy = std::abs(ob->y - player->y);
777
778 if (!(dx > MINACTORDIST || dy > MINACTORDIST)) {
779 //
780 // back up
781 //
782
783 sign_x = -sign_x;
784 sign_y = -sign_y;
785
786 ob->x += sign_x * move;
787 ob->y += sign_y * move;
788
789 ::PlayerIsBlocking(ob);
790 return;
791 }
792 }
793
794 ob->distance -= move;
795 }
796
797
798 extern statetype s_terrot_die1;
799
800 char dki_msg[] =
801 "^FC39 YOU JUST SHOT AN\r"
802 " INFORMANT!\r"
803 "^FC79 ONLY SHOOT BIO-TECHS\r"
804 " THAT SHOOT AT YOU!\r"
805 "^FC19 DO NOT SHOOT\r"
806 " INFORMANTS!!\r";
807
808 uint16_t actor_points[] = {
809 1025, // rent-a-cop
810 1050, // turret
811 500, // general scientist
812 5075, // pod alien
813 5150, // electric alien
814 2055, // electro-sphere
815 5000, // pro guard
816 10000, // genetic guard
817 5055, // mutant human1
818 6055, // mutant human2
819 0, // large canister wait
820 6050, // large canister alien
821 0, // small canister wait
822 3750, // small canister alien
823 0, // gurney wait
824 3750, // gurney
825 12000, // liquid
826 7025, // swat
827 5000, // goldtern
828 5000, // goldstern Morphed
829 2025, // volatile transport
830 2025, // floating bomb
831 0, // rotating cube
832
833 5000, // spider_mutant
834 6000, // breather_beast
835 7000, // cyborg_warror
836 8000, // reptilian_warrior
837 9000, // acid_dragon
838 9000, // mech_guardian
839 30000, // final boss #1
840 40000, // final_boss #2
841 50000, // final_boss #3
842 60000, // final_boss #4
843
844 0, 0, 0, 0, 0, // blake,crate1/2/3, oozes
845 0, // pod egg
846
847 5000, // morphing_spider_mutant
848 8000, // morphing_reptilian_warrior
849 6055, // morphing_mutant human2
850 };
851
852 // ---------------------------------------------------------------------------
853 // CheckAndReserve() - Checks for room in the obj_list and returns a ptr
854 // to the new object or a nullptr.
855 //
856 // ---------------------------------------------------------------------------
CheckAndReserve()857 objtype* CheckAndReserve()
858 {
859 usedummy = nevermark = true;
860 SpawnNewObj(0, 0, &s_hold);
861 usedummy = nevermark = false;
862
863 if (new_actor == &dummyobj) {
864 return nullptr;
865 } else {
866 return new_actor;
867 }
868 }
869
KillActor(objtype * ob)870 void KillActor(
871 objtype* ob)
872 {
873 int16_t tilex, tiley;
874 bool KeepSolid = false;
875 bool givepoints = true;
876 bool deadguy = true;
877 classtype clas;
878
879 tilex = ob->x >> TILESHIFT; // drop item on center
880 tiley = ob->y >> TILESHIFT;
881
882 ob->flags &= ~(FL_FRIENDLY | FL_SHOOTABLE);
883 clas = ob->obclass;
884
885 switch (clas) {
886 case podeggobj:
887 ::sd_play_actor_sound(PODHATCHSND, ob, bstone::AC_VOICE);
888
889 ::InitSmartSpeedAnim(ob, SPR_POD_HATCH1, 0, 2, at_ONCE, ad_FWD, 7);
890 KeepSolid = true;
891 deadguy = givepoints = false;
892 break;
893
894 case morphing_spider_mutantobj:
895 case morphing_reptilian_warriorobj:
896 case morphing_mutanthuman2obj:
897 ob->flags &= ~FL_SHOOTABLE;
898 ::InitSmartSpeedAnim(ob, ob->temp1, 0, 8, at_ONCE, ad_FWD, 2);
899 KeepSolid = true;
900 deadguy = givepoints = false;
901 break;
902
903 case crate1obj:
904 case crate2obj:
905 case crate3obj:
906 ui16_to_static_object(ob->temp3)->shapenum = -1;
907
908 SpawnStatic(tilex, tiley, ob->temp2);
909 ob->obclass = deadobj;
910 ob->lighting = NO_SHADING; // No Shading
911 ::InitSmartSpeedAnim(ob, SPR_GRENADE_EXPLODE2, 0, 3, at_ONCE, ad_FWD, 3 + (US_RndT() & 7));
912 A_DeathScream(ob);
913 MakeAlertNoise(ob);
914 break;
915
916 case floatingbombobj:
917 ob->lighting = EXPLOSION_SHADING;
918 A_DeathScream(ob);
919 ::InitSmartSpeedAnim(ob, SPR_FSCOUT_DIE1, 0, 7, at_ONCE, ad_FWD, ::is_ps() ? 5 : 17);
920 break;
921
922 case volatiletransportobj:
923 ob->lighting = EXPLOSION_SHADING;
924 A_DeathScream(ob);
925 ::InitSmartSpeedAnim(ob, SPR_GSCOUT_DIE1, 0, 8, at_ONCE, ad_FWD, ::is_ps() ? 5 : 17);
926 break;
927
928 case goldsternobj:
929 NewState(ob, &s_goldwarp_it);
930 GoldsternInfo.flags = GS_NEEDCOORD;
931 GoldsternInfo.GoldSpawned = false;
932
933 // Init timer. Search for a location out of all possible locations.
934
935 GoldsternInfo.WaitTime = MIN_GOLDIE_WAIT + Random(MAX_GOLDIE_WAIT - MIN_GOLDIE_WAIT); // Reinit Delay Timer before spawning on new position
936 clas = goldsternobj;
937
938 if (::gamestate.mapon == 9) {
939 if (!::gamestate.boss_key_dropped) {
940 ::gamestate.boss_key_dropped = true;
941
942 static_cast<void>(::ReserveStatic());
943 ::PlaceReservedItemNearTile(bo_gold_key, ob->tilex, ob->tiley);
944 }
945 }
946 break;
947
948 case gold_morphobj:
949 GoldsternInfo.flags = GS_NO_MORE;
950
951 ::sd_play_actor_sound(PODDEATHSND, ob, bstone::AC_VOICE);
952
953 ob->flags |= FL_OFFSET_STATES;
954 InitAnim(ob, SPR_GOLD_DEATH1, 0, 4, at_ONCE, ad_FWD, 25, 9);
955 break;
956
957 case gen_scientistobj:
958 if (ob->flags & FL_INFORMANT) {
959 givepoints = false;
960 clas = nothing;
961 gamestuff.level[gamestate.mapon].stats.accum_inf--;
962 if (!(gamestate.flags & GS_KILL_INF_WARN) || (US_RndT() < 25)) {
963 DisplayInfoMsg(dki_msg, static_cast<msg_priorities>(MP_INTERROGATE - 1), DISPLAY_MSG_STD_TIME * 3, MT_GENERAL);
964 gamestate.flags |= GS_KILL_INF_WARN;
965 }
966 }
967 NewState(ob, &s_ofcdie1);
968 if ((ob->ammo) && !(ob->flags & FL_INFORMANT)) {
969 if (US_RndT() < 65) {
970 PlaceItemType(bo_coin, tilex, tiley);
971 } else {
972 PlaceItemType(bo_clip2, tilex, tiley);
973 }
974 }
975 break;
976
977 case rentacopobj:
978 NewState(ob, &s_rent_die1);
979 if (!(gamestate.weapons & (1 << wp_pistol))) {
980 PlaceItemType(bo_pistol, tilex, tiley);
981 } else if (US_RndT() < 65 || (!ob->ammo)) {
982 PlaceItemType(bo_coin, tilex, tiley);
983 } else if (ob->ammo) {
984 PlaceItemType(bo_clip2, tilex, tiley);
985 }
986 break;
987
988 case swatobj:
989 NewState(ob, &s_swatdie1);
990 if (!(gamestate.weapons & (1 << wp_burst_rifle))) {
991 PlaceItemType(bo_burst_rifle, tilex, tiley);
992 } else if (US_RndT() < 65 || (!ob->ammo)) {
993 PlaceItemType(bo_coin, tilex, tiley);
994 } else if (ob->ammo) {
995 PlaceItemType(bo_clip2, tilex, tiley);
996 }
997 break;
998
999
1000 case proguardobj:
1001 NewState(ob, &s_prodie1);
1002 if (!(gamestate.weapons & (1 << wp_burst_rifle))) {
1003 PlaceItemType(bo_burst_rifle, tilex, tiley);
1004 } else if (US_RndT() < 65 || (!ob->ammo)) {
1005 PlaceItemType(bo_coin, tilex, tiley);
1006 } else if (ob->ammo) {
1007 PlaceItemType(bo_clip2, tilex, tiley);
1008 }
1009 break;
1010
1011 case electroobj:
1012 NewState(ob, &s_electro_die1);
1013 eaList[ob->temp2].aliens_out--;
1014 ob->obclass = nothing;
1015 actorat[ob->tilex][ob->tiley] = nullptr;
1016 break;
1017
1018 case liquidobj:
1019 NewState(ob, &s_liquid_die1);
1020 ob->obclass = nothing;
1021 actorat[ob->tilex][ob->tiley] = nullptr;
1022 break;
1023
1024 case podobj:
1025 ob->temp1 = SPR_POD_DIE1;
1026 NewState(ob, &s_ofs_pod_death1);
1027 A_DeathScream(ob);
1028 break;
1029
1030 case electrosphereobj:
1031 ob->obclass = nothing;
1032 ob->temp1 = SPR_ELECTRO_SPHERE_DIE1;
1033 NewState(ob, &s_ofs_esphere_death1);
1034 actorat[ob->tilex][ob->tiley] = nullptr;
1035 break;
1036
1037 case mutant_human1obj:
1038 PlaceItemNearTile(bo_clip2, tilex, tiley);
1039 case final_boss3obj:
1040 case final_boss4obj:
1041 case mutant_human2obj:
1042 case scan_alienobj:
1043 case lcan_alienobj:
1044 NewState(ob, &s_ofs_die1);
1045 break;
1046
1047 case cyborg_warriorobj:
1048 case mech_guardianobj:
1049 case reptilian_warriorobj:
1050 if (::is_ps()) {
1051 ::PlaceItemNearTile(bo_clip2, tilex, tiley);
1052 }
1053 case spider_mutantobj:
1054 case breather_beastobj:
1055 case acid_dragonobj:
1056 ::NewState(ob, &s_ofs_die1);
1057
1058 if (!::is_ps()) {
1059 static_cast<void>(::ReserveStatic());
1060 ::PlaceReservedItemNearTile(bo_gold_key, ob->tilex, ob->tiley);
1061 ActivatePinballBonus(B_GALIEN_DESTROYED);
1062 }
1063 break;
1064
1065 case final_boss2obj:
1066 ::sd_play_actor_sound(PODDEATHSND, ob, bstone::AC_VOICE);
1067
1068 InitAnim(ob, SPR_BOSS8_DIE1, 0, 4, at_ONCE, ad_FWD, 25, 9);
1069 break;
1070
1071 case genetic_guardobj:
1072 case final_boss1obj:
1073 case gurneyobj:
1074 if (!(gamestate.weapons & (1 << wp_pistol))) {
1075 PlaceItemNearTile(bo_pistol, tilex, tiley);
1076 } else {
1077 PlaceItemNearTile(bo_clip2, tilex, tiley);
1078 }
1079 NewState(ob, &s_ofs_die1);
1080 break;
1081
1082 case gurney_waitobj: // mutant asleep on gurney
1083 ::InitSmartAnim(ob, SPR_GURNEY_MUT_B1, 0, 3, at_ONCE, ad_FWD);
1084 KeepSolid = true;
1085 givepoints = false;
1086 break;
1087
1088 case scan_wait_alienobj: // Actual Canister - Destroyed
1089 ::InitSmartAnim(ob, SPR_SCAN_ALIEN_B1, 0, 3, at_ONCE, ad_FWD);
1090 KeepSolid = true;
1091 givepoints = false;
1092 break;
1093
1094 case lcan_wait_alienobj: // Actual Canister - Destroyed
1095 ::InitSmartAnim(ob, SPR_LCAN_ALIEN_B1, 0, 3, at_ONCE, ad_FWD);
1096 KeepSolid = true;
1097 givepoints = false;
1098 break;
1099
1100 case hang_terrotobj:
1101 NewState(ob, &s_terrot_die1);
1102 ob->lighting = EXPLOSION_SHADING;
1103 break;
1104
1105 case rotating_cubeobj:
1106 if (::is_ps()) {
1107 break;
1108 }
1109
1110 ::A_DeathScream(ob);
1111 ob->ammo = 0;
1112 ob->lighting = EXPLOSION_SHADING;
1113 ::InitSmartSpeedAnim(ob, SPR_VITAL_DIE_1, 0, 7, at_ONCE, ad_FWD, 7);
1114 break;
1115
1116 default:
1117 break;
1118 }
1119
1120 #if LOOK_FOR_DEAD_GUYS
1121 switch (clas) {
1122 case SMART_ACTORS:
1123 DeadGuys[NumDeadGuys++] = ob;
1124 break;
1125 }
1126 #endif
1127
1128 if (KeepSolid) {
1129 ob->flags &= ~(FL_SHOOTABLE);
1130
1131 if (::is_ps()) {
1132 ob->flags2 &= ~FL2_BFG_SHOOTABLE;
1133 }
1134
1135 if (deadguy) {
1136 ob->flags |= FL_DEADGUY;
1137 }
1138 } else {
1139 if (deadguy) {
1140 ob->flags |= (FL_NONMARK | FL_DEADGUY);
1141 }
1142
1143 if ((clas >= rentacopobj) && (clas < crate1obj) && (clas != electroobj) && (clas != goldsternobj)) {
1144 gamestuff.level[gamestate.mapon].stats.accum_enemy++;
1145 }
1146
1147 if (givepoints) {
1148 if ((clas == electroobj) || (clas == goldsternobj)) {
1149 GivePoints(actor_points[clas - rentacopobj], false);
1150 } else {
1151 GivePoints(actor_points[clas - rentacopobj], true);
1152 }
1153 }
1154
1155 ob->flags &= ~(FL_SHOOTABLE | FL_SOLID | FL_FAKE_STATIC);
1156
1157 if (::is_ps()) {
1158 ob->flags2 &= ~FL2_BFGSHOT_SOLID;
1159 }
1160
1161 if ((actorat[ob->tilex][ob->tiley]) == ob) {
1162 // Clear actor from WHERE IT WAS GOING in actorat[].
1163 //
1164 if (!(tilemap[ob->tilex][ob->tiley] & 0x80)) {
1165 actorat[ob->tilex][ob->tiley] = nullptr;
1166 }
1167
1168 // Set actor WHERE IT DIED in actorat[], IF there's a door!
1169 // Otherwise, just leave it removed!
1170 //
1171 if (tilemap[tilex][tiley] & 0x80) {
1172 actorat[tilex][tiley] = ob;
1173 } else {
1174 ob->flags |= FL_NEVERMARK;
1175 }
1176 }
1177 }
1178
1179 DropCargo(ob);
1180
1181 ob->tilex = static_cast<uint8_t>(tilex);
1182 ob->tiley = static_cast<uint8_t>(tiley);
1183
1184 if ((LastMsgPri == MP_TAKE_DAMAGE) && (LastInfoAttacker == clas)) {
1185 MsgTicsRemain = 1;
1186 }
1187
1188 switch (clas) {
1189 case electroobj:
1190 case liquidobj:
1191 case electrosphereobj:
1192 ob->obclass = clas;
1193 ob->flags |= FL_NEVERMARK;
1194 break;
1195
1196 default:
1197 break;
1198 }
1199 }
1200
1201 void DoAttack(
1202 objtype* ob);
1203
1204 extern statetype s_proshoot2;
1205 extern statetype s_goldmorphwait1;
1206 extern bool barrier_damage;
1207
1208 /*
1209 ===================
1210 =
1211 = DamageActor
1212 =
1213 = Called when the player succesfully hits an enemy.
1214 =
1215 = Does damage points to enemy ob, either putting it into a stun frame or
1216 = killing it.
1217 =
1218 ===================
1219 */
DamageActor(objtype * ob,uint16_t damage,objtype * attacker)1220 void DamageActor(
1221 objtype* ob,
1222 uint16_t damage,
1223 objtype* attacker)
1224 {
1225 int16_t old_hp = ob->hitpoints, wound_mod, mod_before = 0, mod_after = 1;
1226
1227 if (!(ob->flags & FL_SHOOTABLE)) {
1228 return;
1229 }
1230
1231 if (gamestate.weapon != wp_autocharge) {
1232 MakeAlertNoise(player);
1233 }
1234
1235 if (ob->flags & FL_FREEZE) {
1236 return;
1237 }
1238
1239 switch (ob->obclass) {
1240 case hang_terrotobj:
1241 if (gamestate.weapon < wp_burst_rifle) {
1242 return;
1243 }
1244 break;
1245
1246 case gurney_waitobj:
1247 if (ob->temp3) {
1248 return;
1249 }
1250 break;
1251
1252 case arc_barrierobj:
1253 if (attacker->obclass == bfg_shotobj) {
1254 if (BARRIER_STATE(ob) != bt_DISABLING) {
1255 BARRIER_STATE(ob) = bt_DISABLING;
1256 ob->hitpoints = 15;
1257 ob->temp3 = 0;
1258 ob->temp2 = US_RndT() & 0xf;
1259 NewState(ob, &s_barrier_shutdown);
1260 }
1261 }
1262 return;
1263
1264 case rotating_cubeobj:
1265 if (::is_ps()) {
1266 return;
1267 }
1268 break;
1269
1270 case post_barrierobj:
1271 return;
1272
1273 case plasma_detonatorobj:
1274 //
1275 // Detonate 'Em!
1276 //
1277 if (attacker == player) {
1278 ob->temp3 = 1;
1279 } else {
1280 ob->temp3 = damage;
1281 }
1282 return;
1283
1284 default:
1285 break;
1286 }
1287
1288 //
1289 // do double damage if shooting a non attack mode actor
1290 //
1291 if (!(ob->flags & FL_ATTACKMODE)) {
1292 damage <<= 1;
1293 }
1294
1295 ob->hitpoints -= damage;
1296 ob->flags2 |= (::is_ps() ? FL2_DAMAGE_CLOAK : 0);
1297
1298 if (ob->hitpoints <= 0) {
1299 switch (ob->obclass) {
1300 case scan_wait_alienobj: // These actors do not have an ouch!
1301 case lcan_wait_alienobj: // So... RETURN!
1302 case gurney_waitobj:
1303 ob->temp2 = actor_to_ui16(CheckAndReserve());
1304
1305 if (ob->temp2 == 0) {
1306 ob->hitpoints += damage;
1307 return;
1308 }
1309 break;
1310
1311 case goldsternobj:
1312 if (gamestate.mapon == GOLD_MORPH_LEVEL) {
1313 extern int16_t morphWaitTime;
1314 extern bool noShots;
1315
1316 morphWaitTime = 60;
1317 noShots = true;
1318 NewState(ob, &s_goldmorphwait1);
1319 ob->obclass = gold_morphingobj;
1320 ob->flags &= ~FL_SHOOTABLE;
1321 return;
1322 }
1323 break;
1324
1325 default:
1326 break;
1327
1328 }
1329
1330 ob->hitpoints = actor_to_ui16(attacker);
1331
1332 KillActor(ob);
1333 return;
1334 } else {
1335 switch (ob->obclass) {
1336 case swatobj:
1337 // Don't get wounded if it's an arc!
1338 //
1339 if ((attacker->obclass == arc_barrierobj) ||
1340 (attacker->obclass == post_barrierobj))
1341 {
1342 break;
1343 }
1344
1345 // Calculate 'wound boundary' (based on NUM_WOUND_STAGES).
1346 //
1347 wound_mod = starthitpoints[gamestate.difficulty][en_swat] / (ob->temp1 + 1) + 1;
1348 mod_before = old_hp / wound_mod;
1349 mod_after = ob->hitpoints / wound_mod;
1350
1351 // If modulo 'before' and 'after' are different, we've crossed
1352 // a 'wound boundary'!
1353 //
1354 if (mod_before != mod_after) {
1355 if (!::is_aog_sw()) {
1356 ::sd_play_actor_sound(::SWATDEATH2SND, ob, bstone::AC_VOICE);
1357 }
1358
1359 ::NewState(ob, &::s_swatwounded1);
1360 ob->flags &= ~(FL_SHOOTABLE | FL_SOLID);
1361 ob->temp2 = (5 * 60) + ((::US_RndT() % 20) * 60);
1362 return;
1363 }
1364 break;
1365
1366 default:
1367 break;
1368 }
1369
1370 if (ob->flags & FL_LOCKED_STATE) {
1371 return;
1372 }
1373
1374 if (!(ob->flags & FL_ATTACKMODE)) {
1375 if ((ob->obclass == gen_scientistobj) && (ob->flags & FL_INFORMANT)) {
1376 return;
1377 }
1378 FirstSighting(ob); // put into combat mode
1379 }
1380
1381 switch (ob->obclass) {
1382 case volatiletransportobj:
1383 case floatingbombobj:
1384 ::T_PainThink(ob);
1385 break;
1386
1387 case goldsternobj:
1388 NewState(ob, &s_goldpain);
1389 break;
1390
1391 case gold_morphobj:
1392 NewState(ob, &s_mgold_pain);
1393 break;
1394
1395 case liquidobj:
1396 NewState(ob, &s_liquid_ouch);
1397 break;
1398
1399 case rentacopobj:
1400 NewState(ob, &s_rent_pain);
1401 break;
1402
1403 case podobj:
1404 NewState(ob, &s_ofs_pod_ouch);
1405 break;
1406
1407 case spider_mutantobj:
1408 case breather_beastobj:
1409 case cyborg_warriorobj:
1410 case reptilian_warriorobj:
1411 case acid_dragonobj:
1412 case mech_guardianobj:
1413 case final_boss1obj:
1414 case final_boss2obj:
1415 case final_boss3obj:
1416 case final_boss4obj:
1417
1418 case genetic_guardobj:
1419 case mutant_human1obj:
1420 case mutant_human2obj:
1421 case scan_alienobj:
1422 case lcan_alienobj:
1423 case gurneyobj:
1424 NewState(ob, &s_ofs_pain);
1425 break;
1426
1427 case electrosphereobj:
1428 NewState(ob, &s_ofs_ouch);
1429 ob->temp1 = SPR_ELECTRO_SPHERE_OUCH;
1430 break;
1431
1432 case electroobj:
1433 NewState(ob, &s_electro_ouch);
1434 break;
1435
1436 case gen_scientistobj:
1437 NewState(ob, &s_ofcpain);
1438 break;
1439
1440 case swatobj:
1441 NewState(ob, &s_swatpain);
1442 break;
1443
1444 case proguardobj:
1445 NewState(ob, &s_propain);
1446 break;
1447
1448 case rotating_cubeobj:
1449 if (::is_ps()) {
1450 break;
1451 }
1452
1453 // Show 'pain' animation only once
1454 if ((ob->hitpoints + damage) ==
1455 starthitpoints[gamestate.difficulty][en_rotating_cube])
1456 {
1457 ::InitSmartSpeedAnim(ob, SPR_VITAL_OUCH, 0, 0, at_ONCE, ad_FWD, 23);
1458 }
1459 break;
1460
1461 default:
1462 break;
1463 }
1464 }
1465
1466 // Make sure actors aren't sitting ducks!
1467 //
1468
1469 if ((US_RndT() < 192) &&
1470 (!(ob->flags & (FL_LOCKED_STATE | FL_BARRIER_DAMAGE))))
1471 {
1472 ChangeShootMode(ob);
1473 DoAttack(ob);
1474 }
1475
1476 ob->flags |= FL_LOCKED_STATE;
1477 }
1478
1479
1480 /*
1481 =============================================================================
1482
1483 CHECKSIGHT
1484
1485 =============================================================================
1486 */
1487
1488 /*
1489 =====================
1490 =
1491 = CheckLine
1492 =
1493 = Returns true if a straight line between the player and ob is unobstructed
1494 =
1495 =====================
1496 */
CheckLine(objtype * from_obj,objtype * to_obj)1497 bool CheckLine(
1498 objtype* from_obj,
1499 objtype* to_obj)
1500 {
1501 int16_t x1, y1, xt1, yt1, x2, y2, xt2, yt2;
1502 int16_t x, y;
1503 int16_t xdist, ydist, xstep, ystep;
1504 int16_t partial, delta;
1505 int32_t ltemp;
1506 int16_t xfrac, yfrac, deltafrac;
1507 uint16_t value, intercept;
1508
1509
1510
1511 x1 = static_cast<int16_t>(from_obj->x >> UNSIGNEDSHIFT); // 1/256 tile precision
1512 y1 = static_cast<int16_t>(from_obj->y >> UNSIGNEDSHIFT);
1513 xt1 = x1 >> 8;
1514 yt1 = y1 >> 8;
1515
1516 // x2 = plux;
1517 // y2 = pluy;
1518
1519 x2 = static_cast<int16_t>(to_obj->x >> UNSIGNEDSHIFT);
1520 y2 = static_cast<int16_t>(to_obj->y >> UNSIGNEDSHIFT);
1521 xt2 = to_obj->tilex;
1522 yt2 = to_obj->tiley;
1523
1524
1525 xdist = static_cast<int16_t>(abs(xt2 - xt1));
1526
1527 if (xdist > 0) {
1528 if (xt2 > xt1) {
1529 partial = 256 - (x1 & 0xff);
1530 xstep = 1;
1531 } else {
1532 partial = x1 & 0xff;
1533 xstep = -1;
1534 }
1535
1536 deltafrac = static_cast<int16_t>(abs(x2 - x1));
1537 if (!deltafrac) {
1538 deltafrac = 1;
1539 }
1540 delta = y2 - y1;
1541 ltemp = ((int32_t)delta << 8) / deltafrac;
1542 if (ltemp > 0x7fffl) {
1543 ystep = 0x7fff;
1544 } else if (ltemp < -0x7fffl) {
1545 ystep = -0x7fff;
1546 } else {
1547 ystep = static_cast<int16_t>(ltemp);
1548 }
1549 yfrac = y1 + ((static_cast<int32_t>(ystep) * partial) >> 8);
1550
1551 x = xt1 + xstep;
1552 xt2 += xstep;
1553 do {
1554 y = yfrac >> 8;
1555 yfrac += ystep;
1556
1557 value = (uint16_t)tilemap[x][y];
1558 x += xstep;
1559
1560 if (!value) {
1561 continue;
1562 }
1563
1564 if (value < 128 || value > 256) {
1565 return false;
1566 }
1567
1568 //
1569 // see if the door is open enough
1570 //
1571 value &= ~0x80;
1572 intercept = yfrac - ystep / 2;
1573
1574 if (intercept > doorposition[value]) {
1575 return false;
1576 }
1577
1578 } while (x != xt2);
1579 }
1580
1581 ydist = static_cast<int16_t>(abs(yt2 - yt1));
1582
1583 if (ydist > 0) {
1584 if (yt2 > yt1) {
1585 partial = 256 - (y1 & 0xff);
1586 ystep = 1;
1587 } else {
1588 partial = y1 & 0xff;
1589 ystep = -1;
1590 }
1591
1592 deltafrac = static_cast<int16_t>(abs(y2 - y1));
1593 if (!deltafrac) {
1594 deltafrac = 1;
1595 }
1596 delta = x2 - x1;
1597 ltemp = ((int32_t)delta << 8) / deltafrac;
1598 if (ltemp > 0x7fffl) {
1599 xstep = 0x7fff;
1600 } else if (ltemp < -0x7fffl) {
1601 xstep = -0x7fff;
1602 } else {
1603 xstep = static_cast<int16_t>(ltemp);
1604 }
1605 xfrac = x1 + (((int32_t)xstep * partial) >> 8);
1606
1607 y = yt1 + ystep;
1608 yt2 += ystep;
1609 do {
1610 x = xfrac >> 8;
1611 xfrac += xstep;
1612
1613 value = (uint16_t)tilemap[x][y];
1614 y += ystep;
1615
1616 if (!value) {
1617 continue;
1618 }
1619
1620 if (value < 128 || value > 256) {
1621 return false;
1622 }
1623
1624 //
1625 // see if the door is open enough
1626 //
1627 value &= ~0x80;
1628 intercept = xfrac - xstep / 2;
1629
1630 if (intercept > doorposition[value]) {
1631 return false;
1632 }
1633 } while (y != yt2);
1634 }
1635
1636 return true;
1637 }
1638
1639 /*
1640 ================
1641 =
1642 = CheckSight
1643 =
1644 = Checks a straight line between player and current object
1645 =
1646 = If the sight is ok, check alertness and angle to see if they notice
1647 =
1648 = returns true if the player has been spoted
1649 =
1650 ================
1651 */
CheckSight(objtype * from_obj,objtype * to_obj)1652 bool CheckSight(
1653 objtype* from_obj,
1654 objtype* to_obj)
1655 {
1656 const int MINSIGHT = 0x18000L;
1657
1658 int32_t deltax, deltay;
1659
1660 //
1661 // don't bother tracing a line if the area isn't connected to the player's
1662 //
1663 if (!areabyplayer[from_obj->areanumber]) {
1664 return false;
1665 }
1666
1667 //
1668 // if the player is real close, sight is automatic
1669 //
1670 deltax = to_obj->x - from_obj->x;
1671 deltay = to_obj->y - from_obj->y;
1672
1673
1674 if (deltax > -MINSIGHT && deltax < MINSIGHT && deltay > -MINSIGHT && deltay < MINSIGHT) {
1675 return true;
1676 }
1677
1678 //
1679 // see if they are looking in the right direction
1680 //
1681 switch (from_obj->dir) {
1682 case north:
1683 if (deltay > 0) {
1684 return false;
1685 }
1686 break;
1687
1688 case east:
1689 if (deltax < 0) {
1690 return false;
1691 }
1692 break;
1693
1694 case south:
1695 if (deltay < 0) {
1696 return false;
1697 }
1698 break;
1699
1700 case west:
1701 if (deltax > 0) {
1702 return false;
1703 }
1704 break;
1705
1706 default:
1707 break;
1708 }
1709
1710 //
1711 // trace a line to check for blocking tiles (corners)
1712 //
1713 return CheckLine(from_obj, to_obj);
1714 }
1715
1716
1717
1718 /*
1719 ===============
1720 =
1721 = FirstSighting
1722 =
1723 = Puts an actor into attack mode and possibly reverses the direction
1724 = if the player is behind it
1725 =
1726 ===============
1727 */
FirstSighting(objtype * ob)1728 void FirstSighting(
1729 objtype* ob)
1730 {
1731 if (PlayerInvisable) {
1732 return;
1733 }
1734
1735 //
1736 // react to the player
1737 //
1738 switch (ob->obclass) {
1739 case floatingbombobj:
1740 if (ob->flags & FL_STATIONARY) {
1741 return;
1742 }
1743
1744 ::sd_play_actor_sound(SCOUT_ALERTSND, ob, bstone::AC_VOICE);
1745
1746 NewState(ob, &s_scout_run);
1747 ob->speed *= 3; // Haul Ass
1748 break;
1749
1750
1751 case goldsternobj:
1752 ::sd_play_actor_sound(GOLDSTERNHALTSND, ob, bstone::AC_VOICE);
1753
1754 NewState(ob, &s_goldchase1);
1755 ob->speed *= 3; // go faster when chasing player
1756 break;
1757
1758 case rentacopobj:
1759 ::sd_play_actor_sound(HALTSND, ob, bstone::AC_VOICE);
1760
1761 NewState(ob, &s_rent_chase1);
1762 ob->speed *= 3; // go faster when chasing player
1763 break;
1764
1765 case gen_scientistobj:
1766 ::sd_play_actor_sound(SCIENTISTHALTSND, ob, bstone::AC_VOICE);
1767
1768 NewState(ob, &s_ofcchase1);
1769 ob->speed *= 3; // go faster when chasing player
1770 break;
1771
1772 case swatobj:
1773 ::sd_play_actor_sound(SWATHALTSND, ob, bstone::AC_VOICE);
1774
1775 NewState(ob, &s_swatchase1);
1776 ob->speed *= 3; // go faster when chasing player
1777 break;
1778
1779 case breather_beastobj:
1780 case reptilian_warriorobj:
1781 case genetic_guardobj:
1782 case final_boss4obj:
1783 case final_boss2obj:
1784 ::sd_play_actor_sound(GGUARDHALTSND, ob, bstone::AC_VOICE);
1785
1786 NewState(ob, &s_ofs_chase1);
1787 ob->speed *= 3; // go faster when chasing player
1788 break;
1789
1790
1791 case cyborg_warriorobj:
1792 case mech_guardianobj:
1793 case mutant_human1obj:
1794 case final_boss3obj:
1795 case final_boss1obj:
1796 ::sd_play_actor_sound(BLUEBOYHALTSND, ob, bstone::AC_VOICE);
1797
1798 NewState(ob, &s_ofs_chase1);
1799 ob->speed *= 2; // go faster when chasing player
1800 break;
1801
1802 case mutant_human2obj:
1803 ::sd_play_actor_sound(DOGBOYHALTSND, ob, bstone::AC_VOICE);
1804
1805 NewState(ob, &s_ofs_chase1);
1806 ob->speed *= 2; // go faster when chasing player
1807 break;
1808
1809 case liquidobj:
1810 NewState(ob, &s_liquid_move);
1811 break;
1812
1813 case spider_mutantobj:
1814 case scan_alienobj:
1815 ::sd_play_actor_sound(SCANHALTSND, ob, bstone::AC_VOICE);
1816
1817 NewState(ob, &s_ofs_chase1);
1818 ob->speed *= 3; // go faster when chasing player
1819 break;
1820
1821 case lcan_alienobj:
1822 ::sd_play_actor_sound(LCANHALTSND, ob, bstone::AC_VOICE);
1823
1824 NewState(ob, &s_ofs_chase1);
1825 ob->speed *= 3; // go faster when chasing player
1826 break;
1827
1828 case gurneyobj:
1829 ::sd_play_actor_sound(GURNEYSND, ob, bstone::AC_VOICE);
1830
1831 NewState(ob, &s_ofs_chase1);
1832 ob->speed *= 3; // go faster when chasing player
1833 break;
1834
1835 case acid_dragonobj:
1836 case podobj:
1837 ::sd_play_actor_sound(PODHALTSND, ob, bstone::AC_VOICE);
1838
1839 NewState(ob, &s_ofs_chase1);
1840 ob->speed *= 2;
1841 break;
1842
1843 case gurney_waitobj:
1844 if (ob->temp3) {
1845 ob->temp2 = actor_to_ui16(CheckAndReserve());
1846
1847 if (ob->temp2 != 0) {
1848 ob->flags &= ~(FL_SHOOTABLE);
1849 ::InitSmartAnim(ob, SPR_GURNEY_MUT_B1, 0, 3, at_ONCE, ad_FWD);
1850 } else {
1851 return;
1852 }
1853 }
1854 break;
1855
1856 case proguardobj:
1857 ::sd_play_actor_sound(PROHALTSND, ob, bstone::AC_VOICE);
1858
1859 NewState(ob, &s_prochase1);
1860 ob->speed *= 4; // go faster when chasing player
1861 break;
1862
1863 case hang_terrotobj:
1864 ::sd_play_actor_sound(TURRETSND, ob, bstone::AC_VOICE);
1865
1866 NewState(ob, &s_terrot_seek1);
1867 break;
1868
1869 default:
1870 break;
1871
1872 }
1873
1874 ob->flags |= FL_ATTACKMODE | FL_FIRSTATTACK;
1875 }
1876
1877 /*
1878 ===============
1879 =
1880 = SightPlayer
1881 =
1882 = Called by actors that ARE NOT chasing the player. If the player
1883 = is detected (by sight, noise, or proximity), the actor is put into
1884 = it's combat frame and true is returned.
1885 =
1886 = Incorporates a random reaction delay
1887 =
1888 ===============
1889 */
SightPlayer(objtype * ob)1890 bool SightPlayer(
1891 objtype* ob)
1892 {
1893 if (ob->obclass == gen_scientistobj) {
1894 if (ob->flags & FL_INFORMANT) {
1895 return false;
1896 }
1897 }
1898
1899 if (PlayerInvisable) {
1900 return false;
1901 }
1902
1903 if (ob->flags & FL_ATTACKMODE) {
1904 return true;
1905 }
1906
1907 if (ob->temp2) {
1908 //
1909 // count down reaction time
1910 //
1911 ob->temp2 -= tics;
1912 if (ob->temp2 > 0) {
1913 return false;
1914 }
1915 ob->temp2 = 0; // time to react
1916 } else {
1917 if (!areabyplayer[ob->areanumber]) {
1918 return false;
1919 }
1920
1921 if (ob->flags & FL_AMBUSH) {
1922 if (!CheckSight(ob, player)) {
1923 return false;
1924 }
1925 ob->flags &= ~FL_AMBUSH;
1926 } else {
1927 bool sighted = false;
1928
1929 if (madenoise || CheckSight(ob, player)) {
1930 sighted = true;
1931 }
1932
1933 switch (ob->obclass) {
1934 // Actors that look fine while JUST STANDING AROUND should go here.
1935 //
1936 case rentacopobj:
1937 case proguardobj:
1938 case swatobj:
1939 case goldsternobj:
1940 case gen_scientistobj:
1941 case floatingbombobj:
1942 case volatiletransportobj:
1943 break;
1944
1945 // Actors that look funny when just standing around go here...
1946 //
1947 default:
1948 if (ob->flags & FL_VISABLE) {
1949 sighted = true;
1950 }
1951 break;
1952 }
1953
1954 if (!sighted) {
1955 return false;
1956 }
1957 }
1958
1959 switch (ob->obclass) {
1960 case goldsternobj:
1961 ob->temp2 = 1 + US_RndT() / 4;
1962 break;
1963
1964
1965 case rentacopobj:
1966 ob->temp2 = 1 + US_RndT() / 4;
1967 break;
1968
1969 case gen_scientistobj:
1970 ob->temp2 = 2;
1971 break;
1972
1973 case swatobj:
1974 ob->temp2 = 1 + US_RndT() / 6;
1975 break;
1976
1977 case proguardobj:
1978 ob->temp2 = 1 + US_RndT() / 6;
1979 break;
1980
1981 case hang_terrotobj:
1982 ob->temp2 = 1 + US_RndT() / 4;
1983 break;
1984
1985 case gurney_waitobj:
1986 ob->temp2 = ob->temp3;
1987 break;
1988
1989 case liquidobj:
1990 ob->temp2 = 1 + US_RndT() / 6;
1991 break;
1992
1993 case floatingbombobj:
1994 ob->temp2 = 1 + US_RndT() / 4;
1995 break;
1996
1997 case genetic_guardobj:
1998 case mutant_human1obj:
1999 case mutant_human2obj:
2000 case scan_alienobj:
2001 case lcan_alienobj:
2002 case gurneyobj:
2003 case spider_mutantobj:
2004 case breather_beastobj:
2005 case cyborg_warriorobj:
2006 case reptilian_warriorobj:
2007 case acid_dragonobj:
2008 case mech_guardianobj:
2009 case final_boss1obj:
2010 case final_boss2obj:
2011 case final_boss3obj:
2012 case final_boss4obj:
2013 ob->temp2 = 1;
2014 break;
2015
2016 default:
2017 break;
2018 }
2019 ob->flags &= ~FL_FRIENDLY;
2020 return false;
2021 }
2022
2023 FirstSighting(ob);
2024
2025 return true;
2026 }
2027
2028
2029 int16_t AdjAngleTable[2][8] = {
2030 { 225, 270, 315, 360, 45, 90, 135, 180 }, // Upper Bound
2031 { 180, 225, 270, 315, 0, 45, 90, 135 }, // Lower Bound
2032 };
2033
2034 // --------------------------------------------------------------------------
2035 // CheckView() Checks a straight line between player and current object
2036 // If the sight is ok, check angle to see if they notice
2037 // returns true if the player has been spoted
2038 // --------------------------------------------------------------------------
CheckView(objtype * from_obj,objtype * to_obj)2039 bool CheckView(
2040 objtype* from_obj,
2041 objtype* to_obj)
2042 {
2043 int32_t deltax, deltay;
2044 int16_t angle;
2045 float fangle;
2046
2047 //
2048 // don't bother tracing a line if the area isn't connected to the player's
2049 //
2050
2051 if (!areabyplayer[from_obj->areanumber]) {
2052 return false;
2053 }
2054
2055 deltax = from_obj->x - to_obj->x;
2056 deltay = to_obj->y - from_obj->y;
2057
2058
2059 fangle = static_cast<float>(atan2(static_cast<double>(deltay), static_cast<double>(deltax))); // returns -pi to pi
2060 if (fangle < 0) {
2061 fangle = static_cast<float>(::m_pi() * 2 + fangle);
2062 }
2063
2064 angle = static_cast<int16_t>(fangle / (::m_pi() * 2) * ANGLES + 23);
2065
2066 if (angle > 360) {
2067 angle = 360;
2068 }
2069
2070 if ((angle <= AdjAngleTable[1][from_obj->dir]) || (angle >= AdjAngleTable[0][from_obj->dir])) {
2071 return false;
2072 }
2073
2074 //
2075 // trace a line to check for blocking tiles (corners)
2076 //
2077
2078 return CheckLine(from_obj, to_obj);
2079 }
2080
2081 #if LOOK_FOR_DEAD_GUYS
2082 // --------------------------------------------------------------------------
2083 // LookForDeadGuys()
2084 // --------------------------------------------------------------------------
LookForDeadGuys(objtype * obj)2085 bool LookForDeadGuys(
2086 objtype* obj)
2087 {
2088 uint8_t loop;
2089 bool DeadGuyFound = false;
2090
2091 if ((obj->obclass == gen_scientistobj) && (obj->flags & FL_INFORMANT)) {
2092 return false;
2093 }
2094
2095 for (loop = 0; loop < NumDeadGuys; loop++) {
2096 if (CheckSight(obj, DeadGuys[loop])) {
2097 DeadGuyFound = true;
2098 FirstSighting(obj);
2099 break;
2100 }
2101 }
2102
2103 return DeadGuyFound;
2104 }
2105 #endif
2106
LookForGoodies(objtype * ob,uint16_t RunReason)2107 bool LookForGoodies(
2108 objtype* ob,
2109 uint16_t RunReason)
2110 {
2111 statobj_t* statptr;
2112 bool just_find_door = false;
2113
2114 // Don't look for goodies if this actor is simply a non-informant that
2115 // was interrogated. (These actors back away, then attack!)
2116 //
2117 if (RunReason == RR_INTERROGATED) {
2118 just_find_door = true;
2119 if (US_RndT() < 128) {
2120 ob->flags &= ~FL_INTERROGATED; // No longer runs, now attacks!
2121 }
2122 }
2123
2124 // We'll let the computer-controlled actors cheat in some circumstances...
2125 //
2126 if ((player->areanumber != ob->areanumber) && (!(ob->flags & FL_VISABLE))) {
2127 if (!ob->ammo) {
2128 ob->ammo += 8;
2129 }
2130 if (ob->hitpoints <= (starthitpoints[gamestate.difficulty][ob->obclass - rentacopobj] >> 1)) {
2131 ob->hitpoints += 10;
2132 }
2133 return true;
2134 }
2135
2136 // Let's REALLY look for some goodies!
2137 //
2138 if (!just_find_door) {
2139 for (statptr = &statobjlist[0]; statptr != laststatobj; statptr++) {
2140 if ((ob->areanumber == statptr->areanumber) && (statptr->shapenum != -1)) {
2141 switch (statptr->itemnumber) {
2142 case bo_chicken:
2143 case bo_ham:
2144 case bo_water:
2145 case bo_clip:
2146 case bo_clip2:
2147 case bo_candybar:
2148 case bo_sandwich:
2149
2150 // If actor is 'on top' of this object, get it!
2151 //
2152 if ((statptr->tilex == ob->tilex) && (statptr->tiley == ob->tiley)) {
2153 int16_t shapenum = -1;
2154
2155 switch (statptr->itemnumber) {
2156 case bo_clip:
2157 case bo_clip2:
2158 if (ob->ammo) { // this actor has plenty
2159 continue; // of ammo!
2160 }
2161 ob->ammo += 8;
2162 break;
2163
2164 case bo_candybar:
2165 case bo_sandwich:
2166 case bo_chicken:
2167 if (ob->hitpoints > (starthitpoints[gamestate.difficulty][ob->obclass - rentacopobj] >> 1)) {
2168 continue; // actor has plenty of health!
2169 }
2170 ob->hitpoints += 8;
2171 shapenum = statptr->shapenum + 1;
2172 break;
2173
2174 case bo_ham:
2175 if (ob->hitpoints > (starthitpoints[gamestate.difficulty][ob->obclass - rentacopobj] >> 1)) {
2176 continue; // actor has plenty of health!
2177 }
2178 ob->hitpoints += 12;
2179 shapenum = statptr->shapenum + 1;
2180 break;
2181
2182 case bo_water:
2183 if (ob->hitpoints > (starthitpoints[gamestate.difficulty][ob->obclass - rentacopobj] >> 1)) {
2184 continue; // actor has plenty of health!
2185 }
2186 ob->hitpoints += 2;
2187 shapenum = statptr->shapenum + 1;
2188 break;
2189 }
2190
2191 ob->s_tilex = 0; // reset for next search!
2192 statptr->shapenum = shapenum; // remove from list if necessary
2193 statptr->itemnumber = bo_nothing;
2194 statptr->flags &= ~FL_BONUS;
2195 return true;
2196 }
2197
2198 // Give actor a chance to run towards this object.
2199 //
2200 if ((!(ob->flags & FL_RUNTOSTATIC))) { // &&(US_RndT()<25))
2201 if (
2202 ((RunReason & RR_AMMO) &&
2203 ((statptr->itemnumber == bo_clip) ||
2204 (statptr->itemnumber == bo_clip2)))
2205 ||
2206 ((RunReason & RR_HEALTH) &&
2207 ((statptr->itemnumber == bo_firstaid) ||
2208 (statptr->itemnumber == bo_water) ||
2209 (statptr->itemnumber == bo_chicken) ||
2210 (statptr->itemnumber == bo_candybar) ||
2211 (statptr->itemnumber == bo_sandwich) ||
2212 (statptr->itemnumber == bo_ham)))
2213 )
2214 {
2215 ob->flags |= FL_RUNTOSTATIC;
2216 ob->s_tilex = statptr->tilex;
2217 ob->s_tiley = statptr->tiley;
2218 return false;
2219 }
2220 }
2221 }
2222 }
2223 }
2224 }
2225
2226 // Should actor run for a door? (quick escape!)
2227 //
2228 if (areabyplayer[ob->areanumber]) {
2229 const int DOOR_CHOICES = 8;
2230
2231 doorobj_t* door;
2232 doorobj_t* doorlist[DOOR_CHOICES];
2233 int8_t doorsfound = 0;
2234
2235 // If actor is running for a 'goody' or a door -- leave it alone!
2236 //
2237 if (ob->flags & FL_RUNTOSTATIC) {
2238 return false;
2239 }
2240
2241 // Search for all doors in actor's current area.
2242 //
2243 for (door = &doorobjlist[0]; door != lastdoorobj; door++) {
2244 // Is this an elevator door OR a locked door?
2245 //
2246 if ((!(*(mapsegs[0] + farmapylookup[door->tiley] + (door->tilex - 1)) - AREATILE)) ||
2247 (!(*(mapsegs[0] + farmapylookup[door->tiley] + (door->tilex + 1)) - AREATILE)) ||
2248 (door->lock != kt_none))
2249 {
2250 continue;
2251 }
2252
2253 // Does this door connect the area the actor is in with another area?
2254 //
2255 if ((door->areanumber[0] == ob->areanumber) ||
2256 (door->areanumber[1] == ob->areanumber))
2257 {
2258 doorlist[static_cast<int>(doorsfound)] = door; // add to list
2259 if (++doorsfound == DOOR_CHOICES) { // check for max
2260 break;
2261 }
2262 }
2263 }
2264
2265 // Randomly choose a door if any were found.
2266 //
2267 if (doorsfound) {
2268 // Randomly choose a door from the list.
2269 // (Only choose the last door used if it's the only door in this area!)
2270 //
2271 int door_index = Random(doorsfound);
2272 door = doorlist[door_index];
2273
2274 if (door == ui16_to_door_object(ob->temp3) && doorsfound > 1) {
2275 door_index++;
2276 if (door_index >= doorsfound) {
2277 door_index = 0;
2278 }
2279 door = doorlist[door_index];
2280 }
2281
2282 ob->temp3 = door_object_to_ui16(door);
2283
2284 ob->s_tilex = door->tilex;
2285 ob->s_tiley = door->tiley;
2286
2287 ob->flags |= FL_RUNTOSTATIC;
2288 }
2289 } else {
2290 // Either: actor is running to corner (leave it alone) OR
2291 // actor is chasing an object already removed by another actor
2292 // (make this actor run to corner)
2293 //
2294 if (ob->flags & FL_RUNTOSTATIC) {
2295 ob->s_tilex = 0;
2296 }
2297 }
2298
2299 return false;
2300 }
2301
CheckRunChase(objtype * ob)2302 uint16_t CheckRunChase(
2303 objtype* ob)
2304 {
2305 const int RUNAWAY_SPEED = 1000;
2306
2307 uint16_t RunReason = 0;
2308
2309 // Mark the reason for running.
2310 //
2311 if (!ob->ammo) { // Out of ammo!
2312 RunReason |= RR_AMMO;
2313 }
2314
2315 if (ob->hitpoints <= (starthitpoints[gamestate.difficulty][ob->obclass - rentacopobj] >> 1)) {
2316 RunReason |= RR_HEALTH; // Feeling sickly!
2317
2318 }
2319 if ((ob->flags & (FL_FRIENDLY | FL_INTERROGATED)) == FL_INTERROGATED) {
2320 RunReason |= RR_INTERROGATED; // Non-informant was interrogated!
2321
2322 }
2323
2324 // Time to RUN or CHASE?
2325 //
2326 if (RunReason) { // run, Run, RUN!
2327 if (!(ob->flags & FL_RUNAWAY)) {
2328 ob->temp3 = 0;
2329 ob->flags |= FL_RUNAWAY;
2330 ob->speed += RUNAWAY_SPEED;
2331 }
2332 } else { // chase, Chase, CHASE!
2333 if (ob->flags & FL_RUNAWAY) {
2334 ob->flags &= ~FL_RUNAWAY;
2335 ob->speed -= RUNAWAY_SPEED;
2336 }
2337 }
2338
2339 return RunReason;
2340 }
2341
SeekPlayerOrStatic(objtype * ob,int16_t * deltax,int16_t * deltay)2342 void SeekPlayerOrStatic(
2343 objtype* ob,
2344 int16_t* deltax,
2345 int16_t* deltay)
2346 {
2347 uint16_t whyrun = 0;
2348 bool smart = false;
2349
2350 // Is this a "smart" actor?
2351 //
2352 switch (ob->obclass) {
2353 case SMART_ACTORS:
2354 smart = true;
2355 break;
2356
2357 case electrosphereobj:
2358 if (!ob->s_tilex) {
2359 GetCornerSeek(ob);
2360 }
2361 *deltax = ob->s_tilex - ob->tilex;
2362 *deltay = ob->s_tiley - ob->tiley;
2363 return;
2364 break;
2365
2366 default:
2367 break;
2368 }
2369
2370 // Should actor run away (chase static) or chase player?
2371 //
2372 if ((smart) && ((whyrun = CheckRunChase(ob))) != 0) {
2373 // Initilize seek tile?
2374 //
2375 if (!ob->s_tilex) {
2376 GetCornerSeek(ob);
2377 }
2378
2379 // Are there any goodies available?
2380 //
2381 if (!LookForGoodies(ob, whyrun)) {
2382 // Change seek tile when actor reaches it.
2383 //
2384 if ((ob->tilex == ob->s_tilex) && (ob->tiley == ob->s_tiley)) { // don't forget me!
2385 GetCornerSeek(ob);
2386 ob->flags &= ~FL_INTERROGATED;
2387 } // add me, too
2388
2389 // Calculate horizontal / vertical distance to seek point.
2390 //
2391 *deltax = ob->s_tilex - ob->tilex;
2392 *deltay = ob->s_tiley - ob->tiley;
2393 } else {
2394 whyrun = CheckRunChase(ob);
2395 }
2396 }
2397
2398 // Make actor chase player if it's not running.
2399 //
2400 if (!whyrun) {
2401 *deltax = player->tilex - ob->tilex;
2402 *deltay = player->tiley - ob->tiley;
2403 }
2404 }
2405
PlayerIsBlocking(objtype * ob)2406 bool PlayerIsBlocking(
2407 objtype* ob)
2408 {
2409 int8_t opp_off[9][2] = {
2410 { -1, 0 }, { -1, 1 }, { 0, 1 }, { 1, 1 },
2411 { 1, 0 }, { 1, -1 }, { 0, -1 }, { -1, -1 }, { 0, 0 }
2412 };
2413
2414 ob->tilex += opp_off[ob->dir][0];
2415 ob->tiley += opp_off[ob->dir][1];
2416 ob->dir = opposite[ob->dir];
2417 ob->distance = TILEGLOBAL - ob->distance;
2418 return true;
2419 }
2420
MakeAlertNoise(objtype * obj)2421 void MakeAlertNoise(
2422 objtype* obj)
2423 {
2424 madenoise = true;
2425 alerted = 2;
2426 alerted_areanum = obj->areanumber;
2427 }
2428