Files
Justin Marshall 101266ab72 Initial commit.
2026-04-27 10:35:40 -07:00

400 lines
10 KiB
C++

/*-----------------------------------------------------------------------**
** Diablo
**
** Path file
**
** (C)1995 Condor, Inc. All rights reserved.
**
**-----------------------------------------------------------------------**
** $Header: /Diablo/PATH.CPP 2 1/23/97 12:21p Jmorin $
**-----------------------------------------------------------------------**
**
** A* Path Search Routines
**-----------------------------------------------------------------------*/
#include "diablo.h"
#pragma hdrstop
#include "gendung.h"
#include "items.h"
#include "player.h"
#include "path.h"
PATHNODE PNodePool[MAXPNODES];
PATHNODE *OPEN;
PATHNODE *CLOSED;
int tempath[MAXPATHLEN];
PATHNODE pathnodes[MAXPNODES]; // node cache
int numpnodes; // index to next pathnode
PATHNODE *Stack[MAXPNODES]; // stack of nodes to process
int numstack; // index to next stack item
static char DirCmd[] =
{ PCMD_WALKU, PCMD_WALKUR, PCMD_WALKR, PCMD_WALKUL, 0,
PCMD_WALKDR, PCMD_WALKL, PCMD_WALKDL, PCMD_WALKD
};
int PATHDIST(int x1, int y1, int x2, int y2);
PATHNODE *GetPathNode();
PATHNODE *ReturnBestNode(void);
BOOL GenerateSuccessors(CHECKFUNC1 PosOk, int PosOkArg, PATHNODE *BestNode,int dx,int dy);
BOOL GenerateSucc(PATHNODE *BestNode,int x, int y, int dx, int dy);
PATHNODE *CheckOPEN(int x, int y);
PATHNODE *CheckCLOSED(int x, int y);
void Insert(PATHNODE *Successor);
void PropagateDown(PATHNODE *Old);
void Push(PATHNODE *Node);
PATHNODE *Pop(void);
/*-----------------------------------------------------------------------*
**-----------------------------------------------------------------------*/
int FindPath(CHECKFUNC1 PosOk, int PosOkArg, int sx,int sy,int dx,int dy, char path[])
{
PATHNODE *Node, *BestNode;
int i,len;
numpnodes = 0; // clear node chache
OPEN=GetPathNode();
CLOSED=GetPathNode();
numstack = 0; // clear stack
Node=GetPathNode();
Node->g=0;
Node->h=PATHDIST(sx,sy,dx,dy);
Node->f=Node->g+Node->h;
Node->x=sx;
Node->y=sy;
OPEN->NextNode=Node; /* make Open List point to first node */
for (;;)
{
BestNode=(PATHNODE *)ReturnBestNode();
if(!BestNode)
return 0;
if (BestNode->x == dx && BestNode->y == dy) /* if we've found the end, break and finish */
break;
if(!GenerateSuccessors(PosOk, PosOkArg, BestNode,dx,dy))
return 0;
}
// generate list of moves
Node = BestNode;
len = 0;
while(Node->Parent && len < MAXPATHLEN)
{
tempath[len++] = DirCmd[(Node->x - Node->Parent->x + 1) + (Node->y - Node->Parent->y + 1)*3];
Node = Node->Parent;
}
if(len == MAXPATHLEN)
return 0;
for(i = 0; i < len; i++)
path[i] = tempath[len - i - 1];
return i;
}
/*-----------------------------------------------------------------------*
**-----------------------------------------------------------------------*/
int PATHDIST(int x1, int y1, int x2, int y2)
{
int dx,dy;
int mind,maxd;
dx = abs(x1-x2);
dy = abs(y1-y2);
mind = min(dx,dy);
maxd = max(dx,dy);
// Assign dist of 3 for diagonal, 2 for straight
// Therefore, 3*(mind) + 2*(maxd-mind)
// = mind + 2*maxd
return mind + maxd<<1;
}
/*-----------------------------------------------------------------------*
**-----------------------------------------------------------------------*/
int PathVal(PATHNODE *Node, int x, int y)
{
if(Node->x == x || Node->y == y)
return 2;
return 3;
}
/*-----------------------------------------------------------------------*
**-----------------------------------------------------------------------*/
PATHNODE *ReturnBestNode(void)
{
PATHNODE *tmp;
if (OPEN->NextNode == NULL)
{
return NULL;
}
/* Pick Node with lowest f, in this case it's the first node in list
because we sort the OPEN list wrt lowest f. Call it BESTNODE. */
tmp=OPEN->NextNode; // point to first node on OPEN
OPEN->NextNode=tmp->NextNode; // Make OPEN point to nextnode or NULL.
/* Next take BESTNODE (or temp in this case) and put it on CLOSED */
tmp->NextNode=CLOSED->NextNode;
CLOSED->NextNode=tmp;
return(tmp);
}
/*-----------------------------------------------------------------------*
**-----------------------------------------------------------------------*/
BOOL PathDirOk(PATHNODE *Node, int x, int y)
{
BOOL ok = TRUE;
switch(DirCmd[(x - Node->x + 1) + (y - Node->y + 1)*3])
{
case PCMD_WALKU:
ok = !(nSolidTable[dPiece[x][y+1]] || nSolidTable[dPiece[x+1][y]]);
break;
case PCMD_WALKR:
ok = !(nSolidTable[dPiece[x][y+1]] || nSolidTable[dPiece[x-1][y]]);
break;
case PCMD_WALKD:
ok = !(nSolidTable[dPiece[x][y-1]] || nSolidTable[dPiece[x-1][y]]);
break;
case PCMD_WALKL:
ok = !(nSolidTable[dPiece[x+1][y]] || nSolidTable[dPiece[x][y-1]]);
break;
}
return ok;
}
/*-----------------------------------------------------------------------*
**-----------------------------------------------------------------------*/
BOOL GenerateSuccessors(CHECKFUNC1 PosOk, int PosOkArg, PATHNODE *BestNode,int dx,int dy)
{
int x,y;
int i;
BOOL posok;
static const char nextx[] = { -1, -1, 1, 1, -1, 0, 1, 0 };
static const char nexty[] = { -1, 1, -1, 1, 0, -1, 0, 1 };
for(i = 0; i < 8; i++)
{
x = BestNode->x + nextx[i];
y = BestNode->y + nexty[i];
posok = PosOk(PosOkArg, x, y);
if((posok && PathDirOk(BestNode, x, y))
|| (!posok && x==dx && y==dy)) // allow user to click on walls
if(!GenerateSucc(BestNode,x,y,dx,dy))
return FALSE;
}
return TRUE;
}
/*-----------------------------------------------------------------------*
**-----------------------------------------------------------------------*/
BOOL GenerateSucc(PATHNODE *BestNode,int x, int y, int dx, int dy)
{
int g,c=0;
PATHNODE *Old,*Successor;
g=BestNode->g+PathVal(BestNode,x,y); /* g(Successor)=g(BestNode)+cost of getting from BestNode to Successor */
if ((Old=CheckOPEN(x, y)) != NULL) /* if equal to NULL then not in OPEN list, else it returns the Node in Old */
{
for(c=0;c<8;c++)
if(BestNode->Child[c] == NULL) /* Add Old to the list of BestNode's Children (or Successors). */
break;
BestNode->Child[c]=Old;
if (g < Old->g && PathDirOk(BestNode, x, y)) /* if our new g value is < Old's then reset Old's parent to point to BestNode */
{
Old->Parent=BestNode;
Old->g=g;
Old->f=g+Old->h;
}
}
else if ((Old=CheckCLOSED(x, y)) != NULL) /* if equal to NULL then not in OPEN list, else it returns the Node in Old */
{
for(c=0;c<8;c++)
if (BestNode->Child[c] == NULL) /* Add Old to the list of BestNode's Children (or Successors). */
break;
BestNode->Child[c]=Old;
if (g < Old->g && PathDirOk(BestNode, x, y)) /* if our new g value is < Old's then reset Old's parent to point to BestNode */
{
Old->Parent=BestNode;
Old->g=g;
Old->f=g+Old->h;
PropagateDown(Old); /* Since we changed the g value of Old, we need
to propagate this new value downwards, i.e.
do a Depth-First traversal of the tree! */
}
}
else
{
if(!(Successor=GetPathNode()))
return FALSE;
else
{
Successor->Parent=BestNode;
Successor->g=g;
Successor->h=PATHDIST(x,y,dx,dy);
Successor->f=g+Successor->h;
Successor->x=x;
Successor->y=y;
Insert(Successor); /* Insert Successor on OPEN list wrt f */
for(c=0;c<8;c++)
if (BestNode->Child[c] == NULL) /* Add Successor to the list of BestNode's Children (or Successors). */
break;
BestNode->Child[c]=Successor;
}
}
return TRUE;
}
/*-----------------------------------------------------------------------*
**-----------------------------------------------------------------------*/
PATHNODE *CheckOPEN(int x, int y)
{
PATHNODE *tmp;
tmp=OPEN->NextNode;
while (tmp != NULL)
{
if (tmp->x == x && tmp->y == y)
return (tmp);
else
tmp=tmp->NextNode;
}
return (NULL);
}
/*-----------------------------------------------------------------------*
**-----------------------------------------------------------------------*/
PATHNODE *CheckCLOSED(int x, int y)
{
PATHNODE *tmp;
tmp=CLOSED->NextNode;
while (tmp != NULL)
{
if (tmp->x == x && tmp->y == y)
return (tmp);
else
tmp=tmp->NextNode;
}
return (NULL);
}
/*-----------------------------------------------------------------------*
**-----------------------------------------------------------------------*/
void Insert(PATHNODE *Successor)
{
PATHNODE *tmp1,*tmp2;
int f;
if (OPEN->NextNode == NULL)
{
OPEN->NextNode=Successor;
return;
}
/* insert into OPEN successor wrt f */
f=Successor->f;
tmp1=OPEN;
tmp2=OPEN->NextNode;
while ((tmp2 != NULL) && (tmp2->f < f))
{
tmp1=tmp2;
tmp2=tmp2->NextNode;
}
Successor->NextNode=tmp2;
tmp1->NextNode=Successor;
}
/*-----------------------------------------------------------------------*
**-----------------------------------------------------------------------*/
void PropagateDown(PATHNODE *Old)
{
int c;
PATHNODE *Child, *Father;
Push(Old);
while (numstack)
{
Father=Pop();
for(c=0;c<8;c++)
{
if ((Child=Father->Child[c])==NULL) /* we may stop the propagation 2 ways: either */
break;
if (Father->g+PathVal(Father, Child->x, Child->y) < Child->g && PathDirOk(Father, Child->x, Child->y)) /* there are no children, or that the g value of */
{ /* the child is equal or better than the cost we're propagating */
Child->Parent=Father;
Child->g=Father->g+PathVal(Father, Child->x, Child->y);
Child->f=Child->g+Child->h;
Push(Child);
}
}
}
}
/**************************************************************************/
/* STACK FUNCTIONS */
/**************************************************************************/
/*-----------------------------------------------------------------------*
**-----------------------------------------------------------------------*/
void Push(PATHNODE *Node)
{
Stack[numstack++] = Node;
}
/*-----------------------------------------------------------------------*
**-----------------------------------------------------------------------*/
PATHNODE *Pop()
{
return Stack[--numstack];
}
PATHNODE *GetPathNode()
{
PATHNODE *Node;
if(numpnodes == MAXPNODES)
return NULL;
else
{
Node = &pathnodes[numpnodes++];
memset((void *)Node, 0, sizeof(PATHNODE));
return Node;
}
}