7 =============================================================================
\r
11 =============================================================================
\r
16 =============================================================================
\r
20 =============================================================================
\r
24 dirtype opposite[9] =
\r
25 {west,southwest,south,southeast,east,northeast,north,northwest,nodir};
\r
27 dirtype diagonal[9][9] =
\r
29 /* east */ {nodir,nodir,northeast,nodir,nodir,nodir,southeast,nodir,nodir},
\r
30 {nodir,nodir,nodir,nodir,nodir,nodir,nodir,nodir,nodir},
\r
31 /* north */ {northeast,nodir,nodir,nodir,northwest,nodir,nodir,nodir,nodir},
\r
32 {nodir,nodir,nodir,nodir,nodir,nodir,nodir,nodir,nodir},
\r
33 /* west */ {nodir,nodir,northwest,nodir,nodir,nodir,southwest,nodir,nodir},
\r
34 {nodir,nodir,nodir,nodir,nodir,nodir,nodir,nodir,nodir},
\r
35 /* south */ {southeast,nodir,nodir,nodir,southwest,nodir,nodir,nodir,nodir},
\r
36 {nodir,nodir,nodir,nodir,nodir,nodir,nodir,nodir,nodir},
\r
37 {nodir,nodir,nodir,nodir,nodir,nodir,nodir,nodir,nodir}
\r
42 void SpawnNewObj (unsigned tilex, unsigned tiley, statetype *state);
\r
43 void NewState (objtype *ob, statetype *state);
\r
45 boolean TryWalk (objtype *ob);
\r
46 void MoveObj (objtype *ob, long move);
\r
48 void KillActor (objtype *ob);
\r
49 void DamageActor (objtype *ob, unsigned damage);
\r
51 boolean CheckLine (objtype *ob);
\r
52 void FirstSighting (objtype *ob);
\r
53 boolean CheckSight (objtype *ob);
\r
56 =============================================================================
\r
60 =============================================================================
\r
65 //===========================================================================
\r
73 = Spaws a new actor at the given TILE coordinates, with the given state, and
\r
74 = the given size in GLOBAL units.
\r
76 = new = a pointer to an initialized new actor
\r
81 void SpawnNewObj (unsigned tilex, unsigned tiley, statetype *state)
\r
86 new->ticcount = US_RndT () % state->tictime;
\r
92 new->x = ((long)tilex<<TILESHIFT)+TILEGLOBAL/2;
\r
93 new->y = ((long)tiley<<TILESHIFT)+TILEGLOBAL/2;
\r
96 actorat[tilex][tiley] = new;
\r
98 *(mapsegs[0] + farmapylookup[new->tiley]+new->tilex) - AREATILE;
\r
104 ===================
\r
108 = Changes ob to a new state, setting ticcount to the max for that state
\r
110 ===================
\r
113 void NewState (objtype *ob, statetype *state)
\r
116 ob->ticcount = state->tictime;
\r
122 =============================================================================
\r
124 ENEMY TILE WORLD MOVEMENT CODE
\r
126 =============================================================================
\r
131 ==================================
\r
135 = Attempts to move ob in its current (ob->dir) direction.
\r
137 = If blocked by either a wall or an actor returns FALSE
\r
139 = If move is either clear or blocked only by a door, returns TRUE and sets
\r
141 = ob->tilex = new destination
\r
143 = ob->areanumber = the floor tile number (0-(NUMAREAS-1)) of destination
\r
144 = ob->distance = TILEGLOBAl, or -doornumber if a door is blocking the way
\r
146 = If a door is in the way, an OpenDoor call is made to start it opening.
\r
147 = The actor code should wait until
\r
148 = doorobjlist[-ob->distance].action = dr_open, meaning the door has been
\r
151 ==================================
\r
154 #define CHECKDIAG(x,y) \
\r
156 temp=(unsigned)actorat[x][y]; \
\r
161 if (((objtype *)temp)->flags&FL_SHOOTABLE) \
\r
166 #define CHECKSIDE(x,y) \
\r
168 temp=(unsigned)actorat[x][y]; \
\r
174 doornum = temp&63; \
\r
175 else if (((objtype *)temp)->flags&FL_SHOOTABLE)\
\r
181 boolean TryWalk (objtype *ob)
\r
188 if (ob->obclass == inertobj)
\r
233 if (ob->obclass == dogobj || ob->obclass == fakeobj)
\r
235 CHECKDIAG(ob->tilex,ob->tiley-1);
\r
239 CHECKSIDE(ob->tilex,ob->tiley-1);
\r
245 CHECKDIAG(ob->tilex+1,ob->tiley-1);
\r
246 CHECKDIAG(ob->tilex+1,ob->tiley);
\r
247 CHECKDIAG(ob->tilex,ob->tiley-1);
\r
253 if (ob->obclass == dogobj || ob->obclass == fakeobj)
\r
255 CHECKDIAG(ob->tilex+1,ob->tiley);
\r
259 CHECKSIDE(ob->tilex+1,ob->tiley);
\r
265 CHECKDIAG(ob->tilex+1,ob->tiley+1);
\r
266 CHECKDIAG(ob->tilex+1,ob->tiley);
\r
267 CHECKDIAG(ob->tilex,ob->tiley+1);
\r
273 if (ob->obclass == dogobj || ob->obclass == fakeobj)
\r
275 CHECKDIAG(ob->tilex,ob->tiley+1);
\r
279 CHECKSIDE(ob->tilex,ob->tiley+1);
\r
285 CHECKDIAG(ob->tilex-1,ob->tiley+1);
\r
286 CHECKDIAG(ob->tilex-1,ob->tiley);
\r
287 CHECKDIAG(ob->tilex,ob->tiley+1);
\r
293 if (ob->obclass == dogobj || ob->obclass == fakeobj)
\r
295 CHECKDIAG(ob->tilex-1,ob->tiley);
\r
299 CHECKSIDE(ob->tilex-1,ob->tiley);
\r
305 CHECKDIAG(ob->tilex-1,ob->tiley-1);
\r
306 CHECKDIAG(ob->tilex-1,ob->tiley);
\r
307 CHECKDIAG(ob->tilex,ob->tiley-1);
\r
316 Quit ("Walk: Bad dir");
\r
321 OpenDoor (doornum);
\r
322 ob->distance = -doornum-1;
\r
328 *(mapsegs[0] + farmapylookup[ob->tiley]+ob->tilex) - AREATILE;
\r
330 ob->distance = TILEGLOBAL;
\r
337 ==================================
\r
341 = Attempts to choose and initiate a movement for ob that sends it towards
\r
342 = the player while dodging
\r
344 = If there is no possible move (ob is totally surrounded)
\r
350 = ob->dir = new direction to follow
\r
351 = ob->distance = TILEGLOBAL or -doornumber
\r
352 = ob->tilex = new destination
\r
354 = ob->areanumber = the floor tile number (0-(NUMAREAS-1)) of destination
\r
356 ==================================
\r
359 void SelectDodgeDir (objtype *ob)
\r
361 int deltax,deltay,i;
\r
362 unsigned absdx,absdy;
\r
364 dirtype turnaround,tdir;
\r
366 if (ob->flags & FL_FIRSTATTACK)
\r
369 // turning around is only ok the very first time after noticing the
\r
372 turnaround = nodir;
\r
373 ob->flags &= ~FL_FIRSTATTACK;
\r
376 turnaround=opposite[ob->dir];
\r
378 deltax = player->tilex - ob->tilex;
\r
379 deltay = player->tiley - ob->tiley;
\r
382 // arange 5 direction choices in order of preference
\r
383 // the four cardinal directions plus the diagonal straight towards
\r
410 // randomize a bit for dodging
\r
412 absdx = abs(deltax);
\r
413 absdy = abs(deltay);
\r
418 dirtry[1] = dirtry[2];
\r
421 dirtry[3] = dirtry[4];
\r
425 if (US_RndT() < 128)
\r
428 dirtry[1] = dirtry[2];
\r
431 dirtry[3] = dirtry[4];
\r
435 dirtry[0] = diagonal [ dirtry[1] ] [ dirtry[2] ];
\r
438 // try the directions util one works
\r
442 if ( dirtry[i] == nodir || dirtry[i] == turnaround)
\r
445 ob->dir = dirtry[i];
\r
451 // turn around only as a last resort
\r
453 if (turnaround != nodir)
\r
455 ob->dir = turnaround;
\r
466 ============================
\r
470 = As SelectDodgeDir, but doesn't try to dodge
\r
472 ============================
\r
475 void SelectChaseDir (objtype *ob)
\r
477 int deltax,deltay,i;
\r
479 dirtype tdir, olddir, turnaround;
\r
483 turnaround=opposite[olddir];
\r
485 deltax=player->tilex - ob->tilex;
\r
486 deltay=player->tiley - ob->tiley;
\r
500 if (abs(deltay)>abs(deltax))
\r
507 if (d[1]==turnaround)
\r
509 if (d[2]==turnaround)
\r
517 return; /*either moved forward or attacked*/
\r
527 /* there is no direct path to the player, so pick another direction */
\r
536 if (US_RndT()>128) /*randomly determine direction of search*/
\r
538 for (tdir=north;tdir<=west;tdir++)
\r
540 if (tdir!=turnaround)
\r
550 for (tdir=west;tdir>=north;tdir--)
\r
552 if (tdir!=turnaround)
\r
561 if (turnaround != nodir)
\r
563 ob->dir=turnaround;
\r
564 if (ob->dir != nodir)
\r
571 ob->dir = nodir; // can't move
\r
576 ============================
\r
580 = Run Away from player
\r
582 ============================
\r
585 void SelectRunDir (objtype *ob)
\r
587 int deltax,deltay,i;
\r
589 dirtype tdir, olddir, turnaround;
\r
592 deltax=player->tilex - ob->tilex;
\r
593 deltay=player->tiley - ob->tiley;
\r
604 if (abs(deltay)>abs(deltax))
\r
613 return; /*either moved forward or attacked*/
\r
619 /* there is no direct path to the player, so pick another direction */
\r
621 if (US_RndT()>128) /*randomly determine direction of search*/
\r
623 for (tdir=north;tdir<=west;tdir++)
\r
632 for (tdir=west;tdir>=north;tdir--)
\r
640 ob->dir = nodir; // can't move
\r
649 = Moves ob be move global units in ob->dir direction
\r
650 = Actors are not allowed to move inside the player
\r
651 = Does NOT check to see if the move is tile map valid
\r
653 = ob->x = adjusted for new position
\r
659 void MoveObj (objtype *ob, long move)
\r
661 long deltax,deltay;
\r
698 Quit ("MoveObj: bad dir!");
\r
702 // check to make sure it's not on top of player
\r
704 if (areabyplayer[ob->areanumber])
\r
706 deltax = ob->x - player->x;
\r
707 if (deltax < -MINACTORDIST || deltax > MINACTORDIST)
\r
709 deltay = ob->y - player->y;
\r
710 if (deltay < -MINACTORDIST || deltay > MINACTORDIST)
\r
713 if (ob->obclass == ghostobj || ob->obclass == spectreobj)
\r
714 TakeDamage (tics*2,ob);
\r
756 ob->distance -=move;
\r
760 =============================================================================
\r
764 =============================================================================
\r
772 = Tries to drop a bonus item somewhere in the tiles surrounding the
\r
773 = given tilex/tiley
\r
778 void DropItem (stat_t itemtype, int tilex, int tiley)
\r
780 int x,y,xl,xh,yl,yh;
\r
783 // find a free spot to put it in
\r
785 if (!actorat[tilex][tiley])
\r
787 PlaceItemType (itemtype, tilex,tiley);
\r
796 for (x=xl ; x<= xh ; x++)
\r
797 for (y=yl ; y<= yh ; y++)
\r
798 if (!actorat[x][y])
\r
800 PlaceItemType (itemtype, x,y);
\r
815 void KillActor (objtype *ob)
\r
819 tilex = ob->tilex = ob->x >> TILESHIFT; // drop item on center
\r
820 tiley = ob->tiley = ob->y >> TILESHIFT;
\r
822 switch (ob->obclass)
\r
826 NewState (ob,&s_grddie1);
\r
827 PlaceItemType (bo_clip2,tilex,tiley);
\r
832 NewState (ob,&s_ofcdie1);
\r
833 PlaceItemType (bo_clip2,tilex,tiley);
\r
838 NewState (ob,&s_mutdie1);
\r
839 PlaceItemType (bo_clip2,tilex,tiley);
\r
844 NewState (ob,&s_ssdie1);
\r
845 if (gamestate.bestweapon < wp_machinegun)
\r
846 PlaceItemType (bo_machinegun,tilex,tiley);
\r
848 PlaceItemType (bo_clip2,tilex,tiley);
\r
853 NewState (ob,&s_dogdie1);
\r
859 NewState (ob,&s_bossdie1);
\r
860 PlaceItemType (bo_key1,tilex,tiley);
\r
865 NewState (ob,&s_greteldie1);
\r
866 PlaceItemType (bo_key1,tilex,tiley);
\r
871 gamestate.killx = player->x;
\r
872 gamestate.killy = player->y;
\r
873 NewState (ob,&s_giftdie1);
\r
878 gamestate.killx = player->x;
\r
879 gamestate.killy = player->y;
\r
880 NewState (ob,&s_fatdie1);
\r
885 gamestate.killx = player->x;
\r
886 gamestate.killy = player->y;
\r
887 NewState (ob,&s_schabbdie1);
\r
892 NewState (ob,&s_fakedie1);
\r
895 case mechahitlerobj:
\r
897 NewState (ob,&s_mechadie1);
\r
899 case realhitlerobj:
\r
901 gamestate.killx = player->x;
\r
902 gamestate.killy = player->y;
\r
903 NewState (ob,&s_hitlerdie1);
\r
909 NewState (ob,&s_spectredie1);
\r
914 NewState (ob,&s_angeldie1);
\r
919 NewState (ob,&s_transdie0);
\r
920 PlaceItemType (bo_key1,tilex,tiley);
\r
925 NewState (ob,&s_uberdie0);
\r
926 PlaceItemType (bo_key1,tilex,tiley);
\r
931 NewState (ob,&s_willdie1);
\r
932 PlaceItemType (bo_key1,tilex,tiley);
\r
937 NewState (ob,&s_deathdie1);
\r
938 PlaceItemType (bo_key1,tilex,tiley);
\r
943 gamestate.killcount++;
\r
944 ob->flags &= ~FL_SHOOTABLE;
\r
945 actorat[ob->tilex][ob->tiley] = NULL;
\r
946 ob->flags |= FL_NONMARK;
\r
952 ===================
\r
956 = Called when the player succesfully hits an enemy.
\r
958 = Does damage points to enemy ob, either putting it into a stun frame or
\r
961 ===================
\r
964 void DamageActor (objtype *ob, unsigned damage)
\r
969 // do double damage if shooting a non attack mode actor
\r
971 if ( !(ob->flags & FL_ATTACKMODE) )
\r
974 ob->hitpoints -= damage;
\r
976 if (ob->hitpoints<=0)
\r
980 if (! (ob->flags & FL_ATTACKMODE) )
\r
981 FirstSighting (ob); // put into combat mode
\r
983 switch (ob->obclass) // dogs only have one hit point
\r
986 if (ob->hitpoints&1)
\r
987 NewState (ob,&s_grdpain);
\r
989 NewState (ob,&s_grdpain1);
\r
993 if (ob->hitpoints&1)
\r
994 NewState (ob,&s_ofcpain);
\r
996 NewState (ob,&s_ofcpain1);
\r
1000 if (ob->hitpoints&1)
\r
1001 NewState (ob,&s_mutpain);
\r
1003 NewState (ob,&s_mutpain1);
\r
1007 if (ob->hitpoints&1)
\r
1008 NewState (ob,&s_sspain);
\r
1010 NewState (ob,&s_sspain1);
\r
1019 =============================================================================
\r
1023 =============================================================================
\r
1028 =====================
\r
1032 = Returns true if a straight line between the player and ob is unobstructed
\r
1034 =====================
\r
1037 boolean CheckLine (objtype *ob)
\r
1039 int x1,y1,xt1,yt1,x2,y2,xt2,yt2;
\r
1041 int xdist,ydist,xstep,ystep;
\r
1043 int partial,delta;
\r
1045 int xfrac,yfrac,deltafrac;
\r
1046 unsigned value,intercept;
\r
1048 x1 = ob->x >> UNSIGNEDSHIFT; // 1/256 tile precision
\r
1049 y1 = ob->y >> UNSIGNEDSHIFT;
\r
1055 xt2 = player->tilex;
\r
1056 yt2 = player->tiley;
\r
1059 xdist = abs(xt2-xt1);
\r
1065 partial = 256-(x1&0xff);
\r
1070 partial = x1&0xff;
\r
1074 deltafrac = abs(x2-x1);
\r
1076 ltemp = ((long)delta<<8)/deltafrac;
\r
1077 if (ltemp > 0x7fffl)
\r
1079 else if (ltemp < -0x7fffl)
\r
1083 yfrac = y1 + (((long)ystep*partial) >>8);
\r
1092 value = (unsigned)tilemap[x][y];
\r
1098 if (value<128 || value>256)
\r
1102 // see if the door is open enough
\r
1105 intercept = yfrac-ystep/2;
\r
1107 if (intercept>doorposition[value])
\r
1110 } while (x != xt2);
\r
1113 ydist = abs(yt2-yt1);
\r
1119 partial = 256-(y1&0xff);
\r
1124 partial = y1&0xff;
\r
1128 deltafrac = abs(y2-y1);
\r
1130 ltemp = ((long)delta<<8)/deltafrac;
\r
1131 if (ltemp > 0x7fffl)
\r
1133 else if (ltemp < -0x7fffl)
\r
1137 xfrac = x1 + (((long)xstep*partial) >>8);
\r
1146 value = (unsigned)tilemap[x][y];
\r
1152 if (value<128 || value>256)
\r
1156 // see if the door is open enough
\r
1159 intercept = xfrac-xstep/2;
\r
1161 if (intercept>doorposition[value])
\r
1163 } while (y != yt2);
\r
1176 = Checks a straight line between player and current object
\r
1178 = If the sight is ok, check alertness and angle to see if they notice
\r
1180 = returns true if the player has been spoted
\r
1185 #define MINSIGHT 0x18000l
\r
1187 boolean CheckSight (objtype *ob)
\r
1189 long deltax,deltay;
\r
1192 // don't bother tracing a line if the area isn't connected to the player's
\r
1194 if (!areabyplayer[ob->areanumber])
\r
1198 // if the player is real close, sight is automatic
\r
1200 deltax = player->x - ob->x;
\r
1201 deltay = player->y - ob->y;
\r
1203 if (deltax > -MINSIGHT && deltax < MINSIGHT
\r
1204 && deltay > -MINSIGHT && deltay < MINSIGHT)
\r
1208 // see if they are looking in the right direction
\r
1234 // trace a line to check for blocking tiles (corners)
\r
1236 return CheckLine (ob);
\r
1247 = Puts an actor into attack mode and possibly reverses the direction
\r
1248 = if the player is behind it
\r
1253 void FirstSighting (objtype *ob)
\r
1256 // react to the player
\r
1258 switch (ob->obclass)
\r
1261 PlaySoundLocActor(HALTSND,ob);
\r
1262 NewState (ob,&s_grdchase1);
\r
1263 ob->speed *= 3; // go faster when chasing player
\r
1267 PlaySoundLocActor(SPIONSND,ob);
\r
1268 NewState (ob,&s_ofcchase1);
\r
1269 ob->speed *= 5; // go faster when chasing player
\r
1273 NewState (ob,&s_mutchase1);
\r
1274 ob->speed *= 3; // go faster when chasing player
\r
1278 PlaySoundLocActor(SCHUTZADSND,ob);
\r
1279 NewState (ob,&s_sschase1);
\r
1280 ob->speed *= 4; // go faster when chasing player
\r
1284 PlaySoundLocActor(DOGBARKSND,ob);
\r
1285 NewState (ob,&s_dogchase1);
\r
1286 ob->speed *= 2; // go faster when chasing player
\r
1291 SD_PlaySound(GUTENTAGSND);
\r
1292 NewState (ob,&s_bosschase1);
\r
1293 ob->speed = SPDPATROL*3; // go faster when chasing player
\r
1297 SD_PlaySound(KEINSND);
\r
1298 NewState (ob,&s_gretelchase1);
\r
1299 ob->speed *= 3; // go faster when chasing player
\r
1303 SD_PlaySound(EINESND);
\r
1304 NewState (ob,&s_giftchase1);
\r
1305 ob->speed *= 3; // go faster when chasing player
\r
1309 SD_PlaySound(ERLAUBENSND);
\r
1310 NewState (ob,&s_fatchase1);
\r
1311 ob->speed *= 3; // go faster when chasing player
\r
1315 SD_PlaySound(SCHABBSHASND);
\r
1316 NewState (ob,&s_schabbchase1);
\r
1317 ob->speed *= 3; // go faster when chasing player
\r
1321 SD_PlaySound(TOT_HUNDSND);
\r
1322 NewState (ob,&s_fakechase1);
\r
1323 ob->speed *= 3; // go faster when chasing player
\r
1326 case mechahitlerobj:
\r
1327 SD_PlaySound(DIESND);
\r
1328 NewState (ob,&s_mechachase1);
\r
1329 ob->speed *= 3; // go faster when chasing player
\r
1332 case realhitlerobj:
\r
1333 SD_PlaySound(DIESND);
\r
1334 NewState (ob,&s_hitlerchase1);
\r
1335 ob->speed *= 5; // go faster when chasing player
\r
1339 NewState (ob,&s_blinkychase1);
\r
1340 ob->speed *= 2; // go faster when chasing player
\r
1345 SD_PlaySound(GHOSTSIGHTSND);
\r
1346 NewState (ob,&s_spectrechase1);
\r
1347 ob->speed = 800; // go faster when chasing player
\r
1351 SD_PlaySound(ANGELSIGHTSND);
\r
1352 NewState (ob,&s_angelchase1);
\r
1353 ob->speed = 1536; // go faster when chasing player
\r
1357 SD_PlaySound(TRANSSIGHTSND);
\r
1358 NewState (ob,&s_transchase1);
\r
1359 ob->speed = 1536; // go faster when chasing player
\r
1363 NewState (ob,&s_uberchase1);
\r
1364 ob->speed = 3000; // go faster when chasing player
\r
1368 SD_PlaySound(WILHELMSIGHTSND);
\r
1369 NewState (ob,&s_willchase1);
\r
1370 ob->speed = 2048; // go faster when chasing player
\r
1374 SD_PlaySound(KNIGHTSIGHTSND);
\r
1375 NewState (ob,&s_deathchase1);
\r
1376 ob->speed = 2048; // go faster when chasing player
\r
1382 if (ob->distance < 0)
\r
1383 ob->distance = 0; // ignore the door opening command
\r
1385 ob->flags |= FL_ATTACKMODE|FL_FIRSTATTACK;
\r
1395 = Called by actors that ARE NOT chasing the player. If the player
\r
1396 = is detected (by sight, noise, or proximity), the actor is put into
\r
1397 = it's combat frame and true is returned.
\r
1399 = Incorporates a random reaction delay
\r
1404 boolean SightPlayer (objtype *ob)
\r
1406 if (ob->flags & FL_ATTACKMODE)
\r
1407 Quit ("An actor in ATTACKMODE called SightPlayer!");
\r
1412 // count down reaction time
\r
1414 ob->temp2 -= tics;
\r
1415 if (ob->temp2 > 0)
\r
1417 ob->temp2 = 0; // time to react
\r
1421 if (!areabyplayer[ob->areanumber])
\r
1424 if (ob->flags & FL_AMBUSH)
\r
1426 if (!CheckSight (ob))
\r
1428 ob->flags &= ~FL_AMBUSH;
\r
1432 if (!madenoise && !CheckSight (ob))
\r
1437 switch (ob->obclass)
\r
1440 ob->temp2 = 1+US_RndT()/4;
\r
1446 ob->temp2 = 1+US_RndT()/6;
\r
1449 ob->temp2 = 1+US_RndT()/6;
\r
1452 ob->temp2 = 1+US_RndT()/8;
\r
1458 case mechahitlerobj:
\r
1459 case realhitlerobj:
\r
1475 FirstSighting (ob);
\r