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