rockbox/apps/plugins/sdl/progs/wolf3d/wl_state.c
Franklin Wei 3f59fc8b77 Wolfenstein 3-D!
This is a port of Wolf4SDL, which is derived from the original id
software source release. The port runs on top of the SDL plugin
runtime and is loaded as an overlay.

Licensing of the game code is not an issue, as discussed below
(essentially, the Debian project treats Wolf4SDL as GPLv2, with an
email from John Carmack backing it up):

  http://forums.rockbox.org/index.php?topic=52872

Included is a copy of MAME's Yamaha OPL sound chip emulator
(fmopl_gpl.c).  This file was not part of the original Wolf4SDL source
(which includes a non-GPL'd version), but was rather rebased from from
a later MAME source which had been relicensed to GPLv2.

Change-Id: I64c2ba035e0be7e2f49252f40640641416613439
2019-07-09 11:20:55 -04:00

1530 lines
36 KiB
C

// WL_STATE.C
#include "wl_def.h"
#pragma hdrstop
/*
=============================================================================
LOCAL CONSTANTS
=============================================================================
*/
/*
=============================================================================
GLOBAL VARIABLES
=============================================================================
*/
static const dirtype opposite[9] =
{west,southwest,south,southeast,east,northeast,north,northwest,nodir};
static const dirtype diagonal[9][9] =
{
/* east */ {nodir,nodir,northeast,nodir,nodir,nodir,southeast,nodir,nodir},
{nodir,nodir,nodir,nodir,nodir,nodir,nodir,nodir,nodir},
/* north */ {northeast,nodir,nodir,nodir,northwest,nodir,nodir,nodir,nodir},
{nodir,nodir,nodir,nodir,nodir,nodir,nodir,nodir,nodir},
/* west */ {nodir,nodir,northwest,nodir,nodir,nodir,southwest,nodir,nodir},
{nodir,nodir,nodir,nodir,nodir,nodir,nodir,nodir,nodir},
/* south */ {southeast,nodir,nodir,nodir,southwest,nodir,nodir,nodir,nodir},
{nodir,nodir,nodir,nodir,nodir,nodir,nodir,nodir,nodir},
{nodir,nodir,nodir,nodir,nodir,nodir,nodir,nodir,nodir}
};
void SpawnNewObj (unsigned tilex, unsigned tiley, statetype *state);
void NewState (objtype *ob, statetype *state);
boolean TryWalk (objtype *ob);
void MoveObj (objtype *ob, int32_t move);
void KillActor (objtype *ob);
void DamageActor (objtype *ob, unsigned damage);
boolean CheckLine (objtype *ob);
void FirstSighting (objtype *ob);
boolean CheckSight (objtype *ob);
/*
=============================================================================
LOCAL VARIABLES
=============================================================================
*/
//===========================================================================
/*
===================
=
= SpawnNewObj
=
= Spaws a new actor at the given TILE coordinates, with the given state, and
= the given size in GLOBAL units.
=
= newobj = a pointer to an initialized new actor
=
===================
*/
void SpawnNewObj (unsigned tilex, unsigned tiley, statetype *state)
{
GetNewActor ();
newobj->state = state;
if (state->tictime)
newobj->ticcount = DEMOCHOOSE_ORIG_SDL(
US_RndT () % state->tictime,
US_RndT () % state->tictime + 1); // Chris' moonwalk bugfix ;D
else
newobj->ticcount = 0;
newobj->tilex = (short) tilex;
newobj->tiley = (short) tiley;
newobj->x = ((int32_t)tilex<<TILESHIFT)+TILEGLOBAL/2;
newobj->y = ((int32_t)tiley<<TILESHIFT)+TILEGLOBAL/2;
newobj->dir = nodir;
actorat[tilex][tiley] = newobj;
newobj->areanumber =
*(mapsegs[0] + (newobj->tiley<<mapshift)+newobj->tilex) - AREATILE;
}
/*
===================
=
= NewState
=
= Changes ob to a new state, setting ticcount to the max for that state
=
===================
*/
void NewState (objtype *ob, statetype *state)
{
ob->state = state;
ob->ticcount = state->tictime;
}
/*
=============================================================================
ENEMY TILE WORLD MOVEMENT CODE
=============================================================================
*/
/*
==================================
=
= TryWalk
=
= Attempts to move ob in its current (ob->dir) direction.
=
= If blocked by either a wall or an actor returns FALSE
=
= If move is either clear or blocked only by a door, returns TRUE and sets
=
= ob->tilex = new destination
= ob->tiley
= ob->areanumber = the floor tile number (0-(NUMAREAS-1)) of destination
= ob->distance = TILEGLOBAl, or -doornumber if a door is blocking the way
=
= If a door is in the way, an OpenDoor call is made to start it opening.
= The actor code should wait until
= doorobjlist[-ob->distance].action = dr_open, meaning the door has been
= fully opened
=
==================================
*/
#define CHECKDIAG(x,y) \
{ \
temp=(uintptr_t)actorat[x][y]; \
if (temp) \
{ \
if (temp<256) \
return false; \
if (((objtype *)temp)->flags&FL_SHOOTABLE) \
return false; \
} \
}
#ifdef PLAYDEMOLIKEORIGINAL
#define DOORCHECK \
if(DEMOCOND_ORIG) \
doornum = temp&63; \
else \
{ \
doornum = (int) temp & 127; \
OpenDoor(doornum); \
ob->distance = -doornum - 1; \
return true; \
}
#else
#define DOORCHECK \
doornum = (int) temp & 127; \
OpenDoor(doornum); \
ob->distance = -doornum - 1; \
return true;
#endif
#define CHECKSIDE(x,y) \
{ \
temp=(uintptr_t)actorat[x][y]; \
if (temp) \
{ \
if (temp<128) \
return false; \
if (temp<256) \
{ \
DOORCHECK \
} \
else if (((objtype *)temp)->flags&FL_SHOOTABLE) \
return false; \
} \
}
boolean TryWalk (objtype *ob)
{
int doornum = -1;
uintptr_t temp;
if (ob->obclass == inertobj)
{
switch (ob->dir)
{
case north:
ob->tiley--;
break;
case northeast:
ob->tilex++;
ob->tiley--;
break;
case east:
ob->tilex++;
break;
case southeast:
ob->tilex++;
ob->tiley++;
break;
case south:
ob->tiley++;
break;
case southwest:
ob->tilex--;
ob->tiley++;
break;
case west:
ob->tilex--;
break;
case northwest:
ob->tilex--;
ob->tiley--;
break;
}
}
else
{
switch (ob->dir)
{
case north:
if (ob->obclass == dogobj || ob->obclass == fakeobj
|| ob->obclass == ghostobj || ob->obclass == spectreobj)
{
CHECKDIAG(ob->tilex,ob->tiley-1);
}
else
{
CHECKSIDE(ob->tilex,ob->tiley-1);
}
ob->tiley--;
break;
case northeast:
CHECKDIAG(ob->tilex+1,ob->tiley-1);
CHECKDIAG(ob->tilex+1,ob->tiley);
CHECKDIAG(ob->tilex,ob->tiley-1);
ob->tilex++;
ob->tiley--;
break;
case east:
if (ob->obclass == dogobj || ob->obclass == fakeobj
|| ob->obclass == ghostobj || ob->obclass == spectreobj)
{
CHECKDIAG(ob->tilex+1,ob->tiley);
}
else
{
CHECKSIDE(ob->tilex+1,ob->tiley);
}
ob->tilex++;
break;
case southeast:
CHECKDIAG(ob->tilex+1,ob->tiley+1);
CHECKDIAG(ob->tilex+1,ob->tiley);
CHECKDIAG(ob->tilex,ob->tiley+1);
ob->tilex++;
ob->tiley++;
break;
case south:
if (ob->obclass == dogobj || ob->obclass == fakeobj
|| ob->obclass == ghostobj || ob->obclass == spectreobj)
{
CHECKDIAG(ob->tilex,ob->tiley+1);
}
else
{
CHECKSIDE(ob->tilex,ob->tiley+1);
}
ob->tiley++;
break;
case southwest:
CHECKDIAG(ob->tilex-1,ob->tiley+1);
CHECKDIAG(ob->tilex-1,ob->tiley);
CHECKDIAG(ob->tilex,ob->tiley+1);
ob->tilex--;
ob->tiley++;
break;
case west:
if (ob->obclass == dogobj || ob->obclass == fakeobj
|| ob->obclass == ghostobj || ob->obclass == spectreobj)
{
CHECKDIAG(ob->tilex-1,ob->tiley);
}
else
{
CHECKSIDE(ob->tilex-1,ob->tiley);
}
ob->tilex--;
break;
case northwest:
CHECKDIAG(ob->tilex-1,ob->tiley-1);
CHECKDIAG(ob->tilex-1,ob->tiley);
CHECKDIAG(ob->tilex,ob->tiley-1);
ob->tilex--;
ob->tiley--;
break;
case nodir:
return false;
default:
Quit ("Walk: Bad dir");
}
}
#ifdef PLAYDEMOLIKEORIGINAL
if (DEMOCOND_ORIG && doornum != -1)
{
OpenDoor(doornum);
ob->distance = -doornum-1;
return true;
}
#endif
ob->areanumber =
*(mapsegs[0] + (ob->tiley<<mapshift)+ob->tilex) - AREATILE;
ob->distance = TILEGLOBAL;
return true;
}
/*
==================================
=
= SelectDodgeDir
=
= Attempts to choose and initiate a movement for ob that sends it towards
= the player while dodging
=
= If there is no possible move (ob is totally surrounded)
=
= ob->dir = nodir
=
= Otherwise
=
= ob->dir = new direction to follow
= ob->distance = TILEGLOBAL or -doornumber
= ob->tilex = new destination
= ob->tiley
= ob->areanumber = the floor tile number (0-(NUMAREAS-1)) of destination
=
==================================
*/
void SelectDodgeDir (objtype *ob)
{
int deltax,deltay,i;
unsigned absdx,absdy;
dirtype dirtry[5];
dirtype turnaround,tdir;
if (ob->flags & FL_FIRSTATTACK)
{
//
// turning around is only ok the very first time after noticing the
// player
//
turnaround = nodir;
ob->flags &= ~FL_FIRSTATTACK;
}
else
turnaround=opposite[ob->dir];
deltax = player->tilex - ob->tilex;
deltay = player->tiley - ob->tiley;
//
// arange 5 direction choices in order of preference
// the four cardinal directions plus the diagonal straight towards
// the player
//
if (deltax>0)
{
dirtry[1]= east;
dirtry[3]= west;
}
else
{
dirtry[1]= west;
dirtry[3]= east;
}
if (deltay>0)
{
dirtry[2]= south;
dirtry[4]= north;
}
else
{
dirtry[2]= north;
dirtry[4]= south;
}
//
// randomize a bit for dodging
//
absdx = abs(deltax);
absdy = abs(deltay);
if (absdx > absdy)
{
tdir = dirtry[1];
dirtry[1] = dirtry[2];
dirtry[2] = tdir;
tdir = dirtry[3];
dirtry[3] = dirtry[4];
dirtry[4] = tdir;
}
if (US_RndT() < 128)
{
tdir = dirtry[1];
dirtry[1] = dirtry[2];
dirtry[2] = tdir;
tdir = dirtry[3];
dirtry[3] = dirtry[4];
dirtry[4] = tdir;
}
dirtry[0] = diagonal [ dirtry[1] ] [ dirtry[2] ];
//
// try the directions util one works
//
for (i=0;i<5;i++)
{
if ( dirtry[i] == nodir || dirtry[i] == turnaround)
continue;
ob->dir = dirtry[i];
if (TryWalk(ob))
return;
}
//
// turn around only as a last resort
//
if (turnaround != nodir)
{
ob->dir = turnaround;
if (TryWalk(ob))
return;
}
ob->dir = nodir;
}
/*
============================
=
= SelectChaseDir
=
= As SelectDodgeDir, but doesn't try to dodge
=
============================
*/
void SelectChaseDir (objtype *ob)
{
int deltax,deltay;
dirtype d[3];
dirtype tdir, olddir, turnaround;
olddir=ob->dir;
turnaround=opposite[olddir];
deltax=player->tilex - ob->tilex;
deltay=player->tiley - ob->tiley;
d[1]=nodir;
d[2]=nodir;
if (deltax>0)
d[1]= east;
else if (deltax<0)
d[1]= west;
if (deltay>0)
d[2]=south;
else if (deltay<0)
d[2]=north;
if (abs(deltay)>abs(deltax))
{
tdir=d[1];
d[1]=d[2];
d[2]=tdir;
}
if (d[1]==turnaround)
d[1]=nodir;
if (d[2]==turnaround)
d[2]=nodir;
if (d[1]!=nodir)
{
ob->dir=d[1];
if (TryWalk(ob))
return; /*either moved forward or attacked*/
}
if (d[2]!=nodir)
{
ob->dir=d[2];
if (TryWalk(ob))
return;
}
/* there is no direct path to the player, so pick another direction */
if (olddir!=nodir)
{
ob->dir=olddir;
if (TryWalk(ob))
return;
}
if (US_RndT()>128) /*randomly determine direction of search*/
{
for (tdir=north; tdir<=west; tdir=(dirtype)(tdir+1))
{
if (tdir!=turnaround)
{
ob->dir=tdir;
if ( TryWalk(ob) )
return;
}
}
}
else
{
for (tdir=west; tdir>=north; tdir=(dirtype)(tdir-1))
{
if (tdir!=turnaround)
{
ob->dir=tdir;
if ( TryWalk(ob) )
return;
}
}
}
if (turnaround != nodir)
{
ob->dir=turnaround;
if (ob->dir != nodir)
{
if ( TryWalk(ob) )
return;
}
}
ob->dir = nodir; // can't move
}
/*
============================
=
= SelectRunDir
=
= Run Away from player
=
============================
*/
void SelectRunDir (objtype *ob)
{
int deltax,deltay;
dirtype d[3];
dirtype tdir;
deltax=player->tilex - ob->tilex;
deltay=player->tiley - ob->tiley;
if (deltax<0)
d[1]= east;
else
d[1]= west;
if (deltay<0)
d[2]=south;
else
d[2]=north;
if (abs(deltay)>abs(deltax))
{
tdir=d[1];
d[1]=d[2];
d[2]=tdir;
}
ob->dir=d[1];
if (TryWalk(ob))
return; /*either moved forward or attacked*/
ob->dir=d[2];
if (TryWalk(ob))
return;
/* there is no direct path to the player, so pick another direction */
if (US_RndT()>128) /*randomly determine direction of search*/
{
for (tdir=north; tdir<=west; tdir=(dirtype)(tdir+1))
{
ob->dir=tdir;
if ( TryWalk(ob) )
return;
}
}
else
{
for (tdir=west; tdir>=north; tdir=(dirtype)(tdir-1))
{
ob->dir=tdir;
if ( TryWalk(ob) )
return;
}
}
ob->dir = nodir; // can't move
}
/*
=================
=
= MoveObj
=
= Moves ob be move global units in ob->dir direction
= Actors are not allowed to move inside the player
= Does NOT check to see if the move is tile map valid
=
= ob->x = adjusted for new position
= ob->y
=
=================
*/
void MoveObj (objtype *ob, int32_t move)
{
int32_t deltax,deltay;
switch (ob->dir)
{
case north:
ob->y -= move;
break;
case northeast:
ob->x += move;
ob->y -= move;
break;
case east:
ob->x += move;
break;
case southeast:
ob->x += move;
ob->y += move;
break;
case south:
ob->y += move;
break;
case southwest:
ob->x -= move;
ob->y += move;
break;
case west:
ob->x -= move;
break;
case northwest:
ob->x -= move;
ob->y -= move;
break;
case nodir:
return;
default:
Quit ("MoveObj: bad dir!");
}
//
// check to make sure it's not on top of player
//
if (areabyplayer[ob->areanumber])
{
deltax = ob->x - player->x;
if (deltax < -MINACTORDIST || deltax > MINACTORDIST)
goto moveok;
deltay = ob->y - player->y;
if (deltay < -MINACTORDIST || deltay > MINACTORDIST)
goto moveok;
if (ob->hidden) // move closer until he meets CheckLine
goto moveok;
if (ob->obclass == ghostobj || ob->obclass == spectreobj)
TakeDamage (tics*2,ob);
//
// back up
//
switch (ob->dir)
{
case north:
ob->y += move;
break;
case northeast:
ob->x -= move;
ob->y += move;
break;
case east:
ob->x -= move;
break;
case southeast:
ob->x -= move;
ob->y -= move;
break;
case south:
ob->y -= move;
break;
case southwest:
ob->x += move;
ob->y -= move;
break;
case west:
ob->x += move;
break;
case northwest:
ob->x += move;
ob->y += move;
break;
case nodir:
return;
}
return;
}
moveok:
ob->distance -=move;
}
/*
=============================================================================
STUFF
=============================================================================
*/
/*
===============
=
= DropItem
=
= Tries to drop a bonus item somewhere in the tiles surrounding the
= given tilex/tiley
=
===============
*/
void DropItem (wl_stat_t itemtype, int tilex, int tiley)
{
int x,y,xl,xh,yl,yh;
//
// find a free spot to put it in
//
if (!actorat[tilex][tiley])
{
PlaceItemType (itemtype, tilex,tiley);
return;
}
xl = tilex-1;
xh = tilex+1;
yl = tiley-1;
yh = tiley+1;
for (x=xl ; x<= xh ; x++)
{
for (y=yl ; y<= yh ; y++)
{
if (!actorat[x][y])
{
PlaceItemType (itemtype, x,y);
return;
}
}
}
}
/*
===============
=
= KillActor
=
===============
*/
void KillActor (objtype *ob)
{
int tilex,tiley;
tilex = ob->tilex = (word)(ob->x >> TILESHIFT); // drop item on center
tiley = ob->tiley = (word)(ob->y >> TILESHIFT);
switch (ob->obclass)
{
case guardobj:
GivePoints (100);
NewState (ob,&s_grddie1);
PlaceItemType (bo_clip2,tilex,tiley);
break;
case officerobj:
GivePoints (400);
NewState (ob,&s_ofcdie1);
PlaceItemType (bo_clip2,tilex,tiley);
break;
case mutantobj:
GivePoints (700);
NewState (ob,&s_mutdie1);
PlaceItemType (bo_clip2,tilex,tiley);
break;
case ssobj:
GivePoints (500);
NewState (ob,&s_ssdie1);
if (gamestate.bestweapon < wp_machinegun)
PlaceItemType (bo_machinegun,tilex,tiley);
else
PlaceItemType (bo_clip2,tilex,tiley);
break;
case dogobj:
GivePoints (200);
NewState (ob,&s_dogdie1);
break;
#ifndef SPEAR
case bossobj:
GivePoints (5000);
NewState (ob,&s_bossdie1);
PlaceItemType (bo_key1,tilex,tiley);
break;
case gretelobj:
GivePoints (5000);
NewState (ob,&s_greteldie1);
PlaceItemType (bo_key1,tilex,tiley);
break;
case giftobj:
GivePoints (5000);
gamestate.killx = player->x;
gamestate.killy = player->y;
NewState (ob,&s_giftdie1);
break;
case fatobj:
GivePoints (5000);
gamestate.killx = player->x;
gamestate.killy = player->y;
NewState (ob,&s_fatdie1);
break;
case schabbobj:
GivePoints (5000);
gamestate.killx = player->x;
gamestate.killy = player->y;
NewState (ob,&s_schabbdie1);
break;
case fakeobj:
GivePoints (2000);
NewState (ob,&s_fakedie1);
break;
case mechahitlerobj:
GivePoints (5000);
NewState (ob,&s_mechadie1);
break;
case realhitlerobj:
GivePoints (5000);
gamestate.killx = player->x;
gamestate.killy = player->y;
NewState (ob,&s_hitlerdie1);
break;
#else
case spectreobj:
if (ob->flags&FL_BONUS)
{
GivePoints (200); // Get points once for each
ob->flags &= ~FL_BONUS;
}
NewState (ob,&s_spectredie1);
break;
case angelobj:
GivePoints (5000);
NewState (ob,&s_angeldie1);
break;
case transobj:
GivePoints (5000);
NewState (ob,&s_transdie0);
PlaceItemType (bo_key1,tilex,tiley);
break;
case uberobj:
GivePoints (5000);
NewState (ob,&s_uberdie0);
PlaceItemType (bo_key1,tilex,tiley);
break;
case willobj:
GivePoints (5000);
NewState (ob,&s_willdie1);
PlaceItemType (bo_key1,tilex,tiley);
break;
case deathobj:
GivePoints (5000);
NewState (ob,&s_deathdie1);
PlaceItemType (bo_key1,tilex,tiley);
break;
#endif
}
gamestate.killcount++;
ob->flags &= ~FL_SHOOTABLE;
actorat[ob->tilex][ob->tiley] = NULL;
ob->flags |= FL_NONMARK;
}
/*
===================
=
= DamageActor
=
= Called when the player succesfully hits an enemy.
=
= Does damage points to enemy ob, either putting it into a stun frame or
= killing it.
=
===================
*/
void DamageActor (objtype *ob, unsigned damage)
{
madenoise = true;
//
// do double damage if shooting a non attack mode actor
//
if ( !(ob->flags & FL_ATTACKMODE) )
damage <<= 1;
ob->hitpoints -= (short)damage;
if (ob->hitpoints<=0)
KillActor (ob);
else
{
if (! (ob->flags & FL_ATTACKMODE) )
FirstSighting (ob); // put into combat mode
switch (ob->obclass) // dogs only have one hit point
{
case guardobj:
if (ob->hitpoints&1)
NewState (ob,&s_grdpain);
else
NewState (ob,&s_grdpain1);
break;
case officerobj:
if (ob->hitpoints&1)
NewState (ob,&s_ofcpain);
else
NewState (ob,&s_ofcpain1);
break;
case mutantobj:
if (ob->hitpoints&1)
NewState (ob,&s_mutpain);
else
NewState (ob,&s_mutpain1);
break;
case ssobj:
if (ob->hitpoints&1)
NewState (ob,&s_sspain);
else
NewState (ob,&s_sspain1);
break;
}
}
}
/*
=============================================================================
CHECKSIGHT
=============================================================================
*/
/*
=====================
=
= CheckLine
=
= Returns true if a straight line between the player and ob is unobstructed
=
=====================
*/
boolean CheckLine (objtype *ob)
{
int x1,y1,xt1,yt1,x2,y2,xt2,yt2;
int x,y;
int xdist,ydist,xstep,ystep;
int partial,delta;
int32_t ltemp;
int xfrac,yfrac,deltafrac;
unsigned value,intercept;
x1 = ob->x >> UNSIGNEDSHIFT; // 1/256 tile precision
y1 = ob->y >> UNSIGNEDSHIFT;
xt1 = x1 >> 8;
yt1 = y1 >> 8;
x2 = plux;
y2 = pluy;
xt2 = player->tilex;
yt2 = player->tiley;
xdist = abs(xt2-xt1);
if (xdist > 0)
{
if (xt2 > xt1)
{
partial = 256-(x1&0xff);
xstep = 1;
}
else
{
partial = x1&0xff;
xstep = -1;
}
deltafrac = abs(x2-x1);
delta = y2-y1;
ltemp = ((int32_t)delta<<8)/deltafrac;
if (ltemp > 0x7fffl)
ystep = 0x7fff;
else if (ltemp < -0x7fffl)
ystep = -0x7fff;
else
ystep = ltemp;
yfrac = y1 + (((int32_t)ystep*partial) >>8);
x = xt1+xstep;
xt2 += xstep;
do
{
y = yfrac>>8;
yfrac += ystep;
value = (unsigned)tilemap[x][y];
x += xstep;
if (!value)
continue;
if (value<128 || value>256)
return false;
//
// see if the door is open enough
//
value &= ~0x80;
intercept = yfrac-ystep/2;
if (intercept>doorposition[value])
return false;
} while (x != xt2);
}
ydist = abs(yt2-yt1);
if (ydist > 0)
{
if (yt2 > yt1)
{
partial = 256-(y1&0xff);
ystep = 1;
}
else
{
partial = y1&0xff;
ystep = -1;
}
deltafrac = abs(y2-y1);
delta = x2-x1;
ltemp = ((int32_t)delta<<8)/deltafrac;
if (ltemp > 0x7fffl)
xstep = 0x7fff;
else if (ltemp < -0x7fffl)
xstep = -0x7fff;
else
xstep = ltemp;
xfrac = x1 + (((int32_t)xstep*partial) >>8);
y = yt1 + ystep;
yt2 += ystep;
do
{
x = xfrac>>8;
xfrac += xstep;
value = (unsigned)tilemap[x][y];
y += ystep;
if (!value)
continue;
if (value<128 || value>256)
return false;
//
// see if the door is open enough
//
value &= ~0x80;
intercept = xfrac-xstep/2;
if (intercept>doorposition[value])
return false;
} while (y != yt2);
}
return true;
}
/*
================
=
= CheckSight
=
= Checks a straight line between player and current object
=
= If the sight is ok, check alertness and angle to see if they notice
=
= returns true if the player has been spoted
=
================
*/
#define MINSIGHT 0x18000l
boolean CheckSight (objtype *ob)
{
int32_t deltax,deltay;
//
// don't bother tracing a line if the area isn't connected to the player's
//
if (!areabyplayer[ob->areanumber])
return false;
//
// if the player is real close, sight is automatic
//
deltax = player->x - ob->x;
deltay = player->y - ob->y;
if (deltax > -MINSIGHT && deltax < MINSIGHT
&& deltay > -MINSIGHT && deltay < MINSIGHT)
return true;
//
// see if they are looking in the right direction
//
switch (ob->dir)
{
case north:
if (deltay > 0)
return false;
break;
case east:
if (deltax < 0)
return false;
break;
case south:
if (deltay < 0)
return false;
break;
case west:
if (deltax > 0)
return false;
break;
// check diagonal moving guards fix
case northwest:
if (DEMOCOND_SDL && deltay > -deltax)
return false;
break;
case northeast:
if (DEMOCOND_SDL && deltay > deltax)
return false;
break;
case southwest:
if (DEMOCOND_SDL && deltax > deltay)
return false;
break;
case southeast:
if (DEMOCOND_SDL && -deltax > deltay)
return false;
break;
}
//
// trace a line to check for blocking tiles (corners)
//
return CheckLine (ob);
}
/*
===============
=
= FirstSighting
=
= Puts an actor into attack mode and possibly reverses the direction
= if the player is behind it
=
===============
*/
void FirstSighting (objtype *ob)
{
//
// react to the player
//
switch (ob->obclass)
{
case guardobj:
PlaySoundLocActor(HALTSND,ob);
NewState (ob,&s_grdchase1);
ob->speed *= 3; // go faster when chasing player
break;
case officerobj:
PlaySoundLocActor(SPIONSND,ob);
NewState (ob,&s_ofcchase1);
ob->speed *= 5; // go faster when chasing player
break;
case mutantobj:
NewState (ob,&s_mutchase1);
ob->speed *= 3; // go faster when chasing player
break;
case ssobj:
PlaySoundLocActor(SCHUTZADSND,ob);
NewState (ob,&s_sschase1);
ob->speed *= 4; // go faster when chasing player
break;
case dogobj:
PlaySoundLocActor(DOGBARKSND,ob);
NewState (ob,&s_dogchase1);
ob->speed *= 2; // go faster when chasing player
break;
#ifndef SPEAR
case bossobj:
SD_PlaySound(GUTENTAGSND);
NewState (ob,&s_bosschase1);
ob->speed = SPDPATROL*3; // go faster when chasing player
break;
#ifndef APOGEE_1_0
case gretelobj:
SD_PlaySound(KEINSND);
NewState (ob,&s_gretelchase1);
ob->speed *= 3; // go faster when chasing player
break;
case giftobj:
SD_PlaySound(EINESND);
NewState (ob,&s_giftchase1);
ob->speed *= 3; // go faster when chasing player
break;
case fatobj:
SD_PlaySound(ERLAUBENSND);
NewState (ob,&s_fatchase1);
ob->speed *= 3; // go faster when chasing player
break;
#endif
case schabbobj:
SD_PlaySound(SCHABBSHASND);
NewState (ob,&s_schabbchase1);
ob->speed *= 3; // go faster when chasing player
break;
case fakeobj:
SD_PlaySound(TOT_HUNDSND);
NewState (ob,&s_fakechase1);
ob->speed *= 3; // go faster when chasing player
break;
case mechahitlerobj:
SD_PlaySound(DIESND);
NewState (ob,&s_mechachase1);
ob->speed *= 3; // go faster when chasing player
break;
case realhitlerobj:
SD_PlaySound(DIESND);
NewState (ob,&s_hitlerchase1);
ob->speed *= 5; // go faster when chasing player
break;
case ghostobj:
NewState (ob,&s_blinkychase1);
ob->speed *= 2; // go faster when chasing player
break;
#else
case spectreobj:
SD_PlaySound(GHOSTSIGHTSND);
NewState (ob,&s_spectrechase1);
ob->speed = 800; // go faster when chasing player
break;
case angelobj:
SD_PlaySound(ANGELSIGHTSND);
NewState (ob,&s_angelchase1);
ob->speed = 1536; // go faster when chasing player
break;
case transobj:
SD_PlaySound(TRANSSIGHTSND);
NewState (ob,&s_transchase1);
ob->speed = 1536; // go faster when chasing player
break;
case uberobj:
NewState (ob,&s_uberchase1);
ob->speed = 3000; // go faster when chasing player
break;
case willobj:
SD_PlaySound(WILHELMSIGHTSND);
NewState (ob,&s_willchase1);
ob->speed = 2048; // go faster when chasing player
break;
case deathobj:
SD_PlaySound(KNIGHTSIGHTSND);
NewState (ob,&s_deathchase1);
ob->speed = 2048; // go faster when chasing player
break;
#endif
}
if (ob->distance < 0)
ob->distance = 0; // ignore the door opening command
ob->flags |= FL_ATTACKMODE|FL_FIRSTATTACK;
}
/*
===============
=
= SightPlayer
=
= Called by actors that ARE NOT chasing the player. If the player
= is detected (by sight, noise, or proximity), the actor is put into
= it's combat frame and true is returned.
=
= Incorporates a random reaction delay
=
===============
*/
boolean SightPlayer (objtype *ob)
{
if (ob->flags & FL_ATTACKMODE)
Quit ("An actor in ATTACKMODE called SightPlayer!");
if (ob->temp2)
{
//
// count down reaction time
//
ob->temp2 -= (short) tics;
if (ob->temp2 > 0)
return false;
ob->temp2 = 0; // time to react
}
else
{
if (!areabyplayer[ob->areanumber])
return false;
if (ob->flags & FL_AMBUSH)
{
if (!CheckSight (ob))
return false;
ob->flags &= ~FL_AMBUSH;
}
else
{
if (!madenoise && !CheckSight (ob))
return false;
}
switch (ob->obclass)
{
case guardobj:
ob->temp2 = 1+US_RndT()/4;
break;
case officerobj:
ob->temp2 = 2;
break;
case mutantobj:
ob->temp2 = 1+US_RndT()/6;
break;
case ssobj:
ob->temp2 = 1+US_RndT()/6;
break;
case dogobj:
ob->temp2 = 1+US_RndT()/8;
break;
case bossobj:
case schabbobj:
case fakeobj:
case mechahitlerobj:
case realhitlerobj:
case gretelobj:
case giftobj:
case fatobj:
case spectreobj:
case angelobj:
case transobj:
case uberobj:
case willobj:
case deathobj:
ob->temp2 = 1;
break;
}
return false;
}
FirstSighting (ob);
return true;
}