mirror of
https://github.com/libretro/libretro-wolfenstein3d.git
synced 2024-11-27 02:30:31 +00:00
1532 lines
37 KiB
C
1532 lines
37 KiB
C
// WL_STATE.C
|
|
|
|
#include "wl_def.h"
|
|
|
|
/*
|
|
=============================================================================
|
|
|
|
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;
|
|
default:
|
|
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;
|
|
}
|