3f59fc8b77
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
1530 lines
36 KiB
C
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;
|
|
}
|