mirror of
https://github.com/jmarshall23/Hellfire.git
synced 2026-08-12 07:50:53 +02:00
400 lines
10 KiB
C++
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;
|
|
}
|
|
}
|