Wolf3D-Mac/EnMove.c

1 line
10 KiB
C

/* enmove.c*/
#include "wolfdef.h"
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}
};
/**********************************
Changes actor to a new state, setting ticcount to the max for that state
**********************************/
void NewState(actor_t *ActorPtr,stateindex_t state)
{
state_t *StatePtr;
StatePtr = &states[state]; /* Get the state record pointer */
ActorPtr->state = state; /* Set the actor's state */
ActorPtr->ticcount = StatePtr->tictime; /* Set the initial tick value */
ActorPtr->pic = StatePtr->shapenum; /* Set the current shape number */
}
/**********************************
Attempts to move actor in its current (ActorPtr->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
ActorPtr->tilex = new destination
ActorPtr->tiley
ActorPtr->areanumber = the floor tile number (0-(MAXAREAS-1)) of destination
ActorPtr->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[ActorPtr->distance].action = dr_open, meaning the door has been
fully opened
**********************************/
/**********************************
Check if I can move in a diagonal direction
**********************************/
Boolean CheckDiag(Word x,Word y)
{
/* anything blocking stops diagonal move*/
if (tilemap[y][x]&(TI_BLOCKMOVE|TI_ACTOR)) {
return FALSE; /* It's blocked */
}
return TRUE; /* It's open! */
}
/**********************************
Check if I can move in a sideways direction
also do the code for an actor to open a door
return 0 if blocked, 1 if open, 2 if I need to open a door
**********************************/
Word CheckSide(Word x,Word y,actor_t *ActorPtr)
{
Word temp;
temp=tilemap[y][x]; /* Get the tile */
if (temp & TI_DOOR) { /* Door? */
if (!(temp&TI_BLOCKMOVE)) { /* Not blocked? */
return 1; /* door is open*/
}
if (ActorPtr->class == CL_DOG) {
return 0; /* dogs can't open doors */
}
return 2; /* I have to open the door */
}
if (temp&(TI_BLOCKMOVE|TI_ACTOR)) { /* Normally blocked? */
return 0; /* Can't go this way */
}
return 1; /* I can go! */
}
/**********************************
Try to move the actor around
**********************************/
Boolean TryWalk (actor_t *ActorPtr)
{
Word x,y;
Word Temp;
Word *TileMapPtr;
Word tile;
x = ActorPtr->goalx; /* Where is my goal x,y? */
y = ActorPtr->goaly;
switch (ActorPtr->dir) { /* Go in my direction */
case north: /* Go n,s,e,w */
--y;
goto DoSide;
case east:
++x;
goto DoSide;
case south:
++y;
goto DoSide;
case west:
--x;
DoSide:
Temp = CheckSide(x,y,ActorPtr); /* See if I can move this way */
if (!Temp) { /* Not this way? */
return FALSE; /* Exit */
}
if (Temp==2) { /* Door? */
OpenDoor(&doors[tilemap[y][x]&TI_NUMMASK]); /* Open the door */
ActorPtr->flags |= (FL_WAITDOOR|FL_NOTMOVING); /* Force the actor to pause */
return TRUE; /* I'm ok! */
}
break; /* Continue */
case northeast:
if (!CheckDiag(x+1,y)) {
return FALSE;
}
y--;
goto FixEast;
case southeast:
if (!CheckDiag(x+1,y)) {
return FALSE;
}
y++;
FixEast:
if (!CheckDiag(x,y)) {
return FALSE;
}
x++;
if (!CheckDiag(x,y)) {
return FALSE;
}
break;
case southwest:
if (!CheckDiag(x-1,y)) {
return FALSE;
}
y++;
goto FixWest;
case northwest:
if (!CheckDiag(x-1,y)) {
return FALSE;
}
y--;
FixWest:
if (!CheckDiag(x,y)) {
return FALSE;
}
x--;
if (!CheckDiag(x,y)) {
return FALSE;
}
}
/* invalidate the move if moving onto the player */
if (areabyplayer[MapPtr->areasoundnum[ActorPtr->areanumber]]) {
if (w_abs(((x<<FRACBITS)|0x80) - actors[0].x) < MINACTORDIST
&& w_abs(((y<<FRACBITS)|0x80) - actors[0].y) < MINACTORDIST) {
return FALSE;
}
}
/* remove old actor marker*/
tilemap[ActorPtr->goaly][ActorPtr->goalx] &= ~TI_ACTOR;
/* place new actor marker*/
TileMapPtr = &tilemap[y][x]; /* Get pointer to cell */
tile = TileMapPtr[0]; /* Get the cell */
TileMapPtr[0] = tile | TI_ACTOR; /* Mark with an actor */
Temp = ActorPtr - &actors[0];
MapPtr->tilemap[y][x] = Temp; /* Save the current actor # */
ActorPtr->goalx = x;
ActorPtr->goaly = y;
if (!(tile&TI_DOOR) ) { /* doorways are not areas*/
ActorPtr->areanumber = tile&TI_NUMMASK;
}
ActorPtr->distance = TILEGLOBAL; /* Move across 1 whole tile */
ActorPtr->flags &= ~(FL_WAITDOOR|FL_NOTMOVING); /* I'm not waiting and I'm moving */
return TRUE; /* It's ok! */
}
/**********************************
Attempts to choose and initiate a movement for actor that sends it towards
the player while dodging
**********************************/
void SelectDodgeDir(actor_t *ActorPtr)
{
int deltax,deltay;
Word i;
Word absdx,absdy;
dirtype dirtry[5];
dirtype turnaround,tdir;
turnaround=opposite[ActorPtr->dir];
deltax = actors[0].x - ActorPtr->x;
deltay = actors[0].y - ActorPtr->y;
/* 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 = w_abs(deltax);
absdy = w_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 (w_rnd() & 1) {
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 until one works*/
i = 0;
do {
tdir = dirtry[i];
if (tdir != nodir && tdir != turnaround) {
ActorPtr->dir = tdir;
if (TryWalk(ActorPtr)) { /* Can I go this way? */
return; /* Yep! */
}
}
} while(++i<5); /* All tries done? */
/* turn around only as a last resort*/
if (turnaround != nodir) {
ActorPtr->dir = turnaround;
if (TryWalk(ActorPtr)) {
return;
}
}
ActorPtr->dir = nodir; /* Stop the motion */
ActorPtr->flags |= FL_NOTMOVING; /* Kill the logic! */
}
/**********************************
Attempts to choose and initiate a movement for actor that sends it towards
the player but doesn't try to dodge
**********************************/
void SelectChaseDir(actor_t *ActorPtr)
{
int deltax,deltay;
dirtype d[3];
dirtype tdir, olddir, turnaround;
olddir = (dirtype) ActorPtr->dir; /* Save the current direction */
turnaround=opposite[olddir]; /* What's the opposite direction */
deltax = actors[0].x - ActorPtr->x; /* Which way to travel? */
deltay = actors[0].y - ActorPtr->y;
if (deltax>0) {
d[1]= east;
} else if (!deltax) {
d[1]= nodir;
} else {
d[1]= west;
}
if (deltay>0) {
d[2]=south;
} else if (!deltay) {
d[2]=nodir;
} else {
d[2]=north;
}
if (w_abs(deltay)>w_abs(deltax)) { /* Swap if Y is greater */
tdir=d[1];
d[1]=d[2];
d[2]=tdir;
}
if (d[1]==turnaround) { /* Try not to turn around */
d[1]=nodir;
}
if (d[2]==turnaround) { /* Try not to turn around */
d[2]=nodir;
}
if (d[1]!=nodir) { /* East/West movement? */
ActorPtr->dir = d[1]; /* Try to move */
if (TryWalk(ActorPtr)) {
return; /*either moved forward or attacked*/
}
}
if (d[2]!=nodir) { /* North/South movement? */
ActorPtr->dir =d[2];
if (TryWalk(ActorPtr)) {
return;
}
}
/* there is no direct path to the player, so pick another direction */
if (olddir!=nodir) {
ActorPtr->dir =olddir; /* Continue in the old direction */
if (TryWalk(ActorPtr)) {
return;
}
}
if (w_rnd()&1) { /*randomly determine direction of search*/
tdir = north;
do {
if (tdir!=turnaround) {
ActorPtr->dir =tdir;
if (TryWalk(ActorPtr)) {
return;
}
}
} while(++tdir<(west+1));
} else {
tdir = west;
do {
if (tdir!=turnaround) {
ActorPtr->dir =tdir;
if ( TryWalk(ActorPtr)) {
return;
}
}
} while(--tdir>=north);
}
if (turnaround != nodir) { /* Alright, try backwards now */
ActorPtr->dir =turnaround;
if ( TryWalk(ActorPtr) ) {
return;
}
}
ActorPtr->dir = nodir; /* Can't move, I give up */
ActorPtr->flags |= FL_NOTMOVING;
}
/**********************************
Moves actor <move> global units in actor->dir direction
Actors are not allowed to move inside the player
Does NOT check to see if the move is tile map valid
**********************************/
void MoveActor(actor_t *ActorPtr,Word move)
{
Word tryx,tryy;
tryx = ActorPtr->x; /* Get the x and y */
tryy = ActorPtr->y;
switch (ActorPtr->dir) {;
case northeast: /* Move to the new x,y */
tryx += move;
case north:
tryy -= move;
break;
case southeast:
tryy += move;
case east:
tryx += move;
break;
case southwest:
tryx -= move;
case south:
tryy += move;
break;
case northwest:
tryy -= move;
case west:
tryx -= move;
}
/* check to make sure it's not moving on top of player*/
if (areabyplayer[MapPtr->areasoundnum[ActorPtr->areanumber]]) {
if (w_abs(tryx - actors[0].x)<MINACTORDIST) {
if (w_abs(tryy - actors[0].y)<MINACTORDIST) {
return;
}
}
}
ActorPtr->distance-= move; /* Remove the distance */
ActorPtr->x = tryx; /* Save the new x,y */
ActorPtr->y = tryy;
}