// WL_STATE.C
#include "WL_DEF.H"
#pragma hdrstop
/*
=============================================================================
LOCAL CONSTANTS
=============================================================================
*/
/*
=============================================================================
GLOBAL VARIABLES
=============================================================================
*/
dirtype opposite[9] =
{west,southwest,south,southeast,east,northeast,north,northwest,nodir};
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, long 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.
=
= new = a pointer to an initialized new actor
=
===================
*/
void SpawnNewObj (unsigned tilex, unsigned tiley, statetype *state)
{
GetNewActor ();
new->state = state;
if (state->tictime)
new->ticcount = US_RndT () % state->tictime;
else
new->ticcount = 0;
new->tilex = tilex;
new->tiley = tiley;
new->x = ((long)tilex TILESHIFT)+TILEGLOBAL/2;
new->y = ((long)tiley TILESHIFT)+TILEGLOBAL/2;
new->dir = nodir;
actorat[tilex][tiley] = new;
new->areanumber =
*(mapsegs[0] + farmapylookup[new->tiley]+new->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=(unsigned)actorat[x][y]; \
if (temp) \
{ \
if (temp 256) \
return false; \
if (((objtype *)temp)->flags&FL_SHOOTABLE) \
return false; \
} \
}
#define CHECKSIDE(x,y) \
{ \
temp=(unsigned)actorat[x][y]; \
if (temp) \
{ \
if (temp 128) \
return false; \
if (temp 256) \
doornum = temp&63; \
else if (((objtype *)temp)->flags&FL_SHOOTABLE)\
return false; \
} \
}
boolean TryWalk (objtype *ob)
{
int doornum;
unsigned temp;
doornum = -1;
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)
{
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)
{
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)
{
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)
{
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");
}
if (doornum != -1)
{
OpenDoor (doornum);
ob->distance = -doornum-1;
return true;
}
ob->areanumber =
*(mapsegs[0] + farmapylookup[ob->tiley]+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;i5;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,i;
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++)
{
if (tdir!=turnaround)
{
ob->dir=tdir;
if ( TryWalk(ob) )
return;
}
}
}
else
{
for (tdir=west;tdir>=north;tdir--)
{
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,i;
dirtype d[3];
dirtype tdir, olddir, turnaround;
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++)
{
ob->dir=tdir;
if ( TryWalk(ob) )
return;
}
}
else
{
for (tdir=west;tdir>=north;tdir--)
{
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, long move)
{
long 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->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;
}