Added a first implementation of the new pathfinder, based on the a* algorithm.
This commit is contained in:
parent
9241274f33
commit
53bab85ed3
5 changed files with 557 additions and 62 deletions
|
@ -47,6 +47,12 @@ enum _move_return_ {
|
||||||
|
|
||||||
extern unsigned char Matrix[(MaxMapWidth+1)*(MaxMapHeight+1)]; /// Path matrix
|
extern unsigned char Matrix[(MaxMapWidth+1)*(MaxMapHeight+1)]; /// Path matrix
|
||||||
|
|
||||||
|
extern int AStarOn; /// are we using a* or the old path finder
|
||||||
|
extern int AStarFixedUnitCrossingCost; /// cost associated to move on a tile
|
||||||
|
/// occupied by a fixed unit
|
||||||
|
extern int AStarMovingUnitCrossingCost; /// cost associated to move on a tile
|
||||||
|
/// occupied by a moving unit
|
||||||
|
|
||||||
|
|
||||||
/*----------------------------------------------------------------------------
|
/*----------------------------------------------------------------------------
|
||||||
-- Functions
|
-- Functions
|
||||||
|
@ -64,6 +70,12 @@ extern int UnitReachable(Unit* unit,Unit* dest);
|
||||||
/// Returns the next element of the path
|
/// Returns the next element of the path
|
||||||
extern int NextPathElement(Unit*,int* xdp,int* ydp);
|
extern int NextPathElement(Unit*,int* xdp,int* ydp);
|
||||||
|
|
||||||
|
//
|
||||||
|
// in ccl_pathfinder.c
|
||||||
|
//
|
||||||
|
/// register ccl features
|
||||||
|
extern void PathfinderCclRegister(void);
|
||||||
|
|
||||||
//@}
|
//@}
|
||||||
|
|
||||||
#endif // !__PATH_FINDER_H__
|
#endif // !__PATH_FINDER_H__
|
||||||
|
|
|
@ -14,6 +14,6 @@ include $(TOPDIR)/Rules.make
|
||||||
|
|
||||||
MODULE = pathfinder
|
MODULE = pathfinder
|
||||||
|
|
||||||
OBJS = pathfinder.$(OE) astar.$(OE)
|
OBJS = pathfinder.$(OE) astar.$(OE) ccl_pathfinder.$(OE)
|
||||||
|
|
||||||
include $(TOPDIR)/Common.mk
|
include $(TOPDIR)/Common.mk
|
||||||
|
|
|
@ -1,13 +1,23 @@
|
||||||
/*
|
// ___________ _________ _____ __
|
||||||
** A clone of a famous game.
|
// \_ _____/______ ____ ____ \_ ___ \____________ _/ ____\/ |_
|
||||||
*/
|
// | __) \_ __ \_/ __ \_/ __ \/ \ \/\_ __ \__ \\ __\\ __\
|
||||||
|
// | \ | | \/\ ___/\ ___/\ \____| | \// __ \| | | |
|
||||||
|
// \___ / |__| \___ >\___ >\______ /|__| (____ /__| |__|
|
||||||
|
// \/ \/ \/ \/ \/
|
||||||
|
// ______________________ ______________________
|
||||||
|
// T H E W A R B E G I N S
|
||||||
|
// FreeCraft - A free fantasy real time strategy game engine
|
||||||
|
//
|
||||||
/**@name astar.c - The a* path finder routines. */
|
/**@name astar.c - The a* path finder routines. */
|
||||||
/*
|
/*
|
||||||
** (c) Copyright 1999 by Lutz Sammer
|
** (c) Copyright 1999-2000 by Lutz Sammer and Fabrice Rossi
|
||||||
**
|
**
|
||||||
** $Id$
|
** $Id$
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
//FIXME: I don't fully understand the range parameter in the Unit
|
||||||
|
// structure. Something might be broken because of this misunderstanding
|
||||||
|
|
||||||
//@{
|
//@{
|
||||||
|
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
|
@ -19,7 +29,8 @@
|
||||||
#include "pathfinder.h"
|
#include "pathfinder.h"
|
||||||
|
|
||||||
typedef struct _node_ {
|
typedef struct _node_ {
|
||||||
int Direction; /// Direction for trace back
|
short Direction; /// Direction for trace back
|
||||||
|
short InGoal; /// is this point in the goal
|
||||||
int CostFromStart; /// Real costs to reach this point
|
int CostFromStart; /// Real costs to reach this point
|
||||||
int CostToGoal; /// Aproximated costs until goal
|
int CostToGoal; /// Aproximated costs until goal
|
||||||
} Node;
|
} Node;
|
||||||
|
@ -31,9 +42,19 @@ typedef struct _open_ {
|
||||||
int Costs; /// complete costs to goal
|
int Costs; /// complete costs to goal
|
||||||
} Open;
|
} Open;
|
||||||
|
|
||||||
|
/// heuristic cost fonction for a star
|
||||||
#define AStarCosts(sx,sy,ex,ey) max(abs(sx-ex),abs(sy-ey))
|
#define AStarCosts(sx,sy,ex,ey) max(abs(sx-ex),abs(sy-ey))
|
||||||
|
|
||||||
|
/// cost matrix
|
||||||
local Node AStarMatrix[(MaxMapWidth+1)*(MaxMapHeight+1)];
|
local Node AStarMatrix[(MaxMapWidth+1)*(MaxMapHeight+1)];
|
||||||
|
/// a list of close nodes, helps to speed up the matrix cleaning
|
||||||
|
local int CloseSet[(MaxMapWidth+1)*(MaxMapHeight+1)];
|
||||||
|
local int Threshold=(MaxMapWidth+1)*(MaxMapHeight+1)/4;
|
||||||
|
|
||||||
|
/// see pathfinder.h
|
||||||
|
global int AStarFixedUnitCrossingCost=MaxMapWidth*MaxMapHeight;
|
||||||
|
global int AStarMovingUnitCrossingCost=2;
|
||||||
|
global int AStarOn=0;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
** Prepare path finder.
|
** Prepare path finder.
|
||||||
|
@ -43,66 +64,287 @@ local void AStarPrepare(void)
|
||||||
memset(AStarMatrix,0,sizeof(AStarMatrix));
|
memset(AStarMatrix,0,sizeof(AStarMatrix));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
** Clean up the AStarMatrix
|
||||||
|
*/
|
||||||
|
local void AStarCleanUp(int num_in_close)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
if(num_in_close>Threshold) {
|
||||||
|
AStarPrepare();
|
||||||
|
} else {
|
||||||
|
for(i=0;i<num_in_close;i++) {
|
||||||
|
AStarMatrix[CloseSet[i]].CostFromStart=0;
|
||||||
|
AStarMatrix[CloseSet[i]].InGoal=0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
** The Open set is handled by a Heap stored in a table
|
||||||
|
** 0 is the root
|
||||||
|
** node i has left son is at 2*i+1 and right son is at 2*i+2
|
||||||
|
*/
|
||||||
|
|
||||||
|
/// The set of Open nodes
|
||||||
|
local Open OpenSet[MaxMapWidth*MaxMapHeight/64];
|
||||||
|
/// The size of the open node set
|
||||||
|
local int OpenSetSize;
|
||||||
|
|
||||||
|
/**
|
||||||
|
** Find the best node in the current open node set
|
||||||
|
** Returns the position of this node in the open node set (always 0 in the
|
||||||
|
** current heap based implementation)
|
||||||
|
*/
|
||||||
|
local int AStarFindMinimum()
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
** Remove the minimum from the open node set (and update the heap)
|
||||||
|
** pos is the position of the minimum (0 in the heap based implementation)
|
||||||
|
*/
|
||||||
|
local void AStarRemoveMinimum(int pos)
|
||||||
|
{
|
||||||
|
int i,j,end;
|
||||||
|
Open swap;
|
||||||
|
if(--OpenSetSize) {
|
||||||
|
OpenSet[pos]=OpenSet[OpenSetSize];
|
||||||
|
// now we exchange the new root with its smallest child until the
|
||||||
|
// order is correct
|
||||||
|
i=0;
|
||||||
|
end=(OpenSetSize>>1)-1;
|
||||||
|
while(i<=end) {
|
||||||
|
j=(i<<1)+1;
|
||||||
|
if(j<OpenSetSize-1 && OpenSet[j].Costs>=OpenSet[j+1].Costs)
|
||||||
|
j++;
|
||||||
|
if(OpenSet[i].Costs>OpenSet[j].Costs) {
|
||||||
|
swap=OpenSet[i];
|
||||||
|
OpenSet[i]=OpenSet[j];
|
||||||
|
OpenSet[j]=swap;
|
||||||
|
i=j;
|
||||||
|
} else {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
** Add a new node to the open set (and update the heap structure)
|
||||||
|
*/
|
||||||
|
local void AStarAddNode(int x,int y,int o,int costs)
|
||||||
|
{
|
||||||
|
int i=OpenSetSize;
|
||||||
|
int j;
|
||||||
|
Open swap;
|
||||||
|
OpenSet[i].X=x;
|
||||||
|
OpenSet[i].Y=y;
|
||||||
|
OpenSet[i].O=o;
|
||||||
|
OpenSet[i].Costs=costs;
|
||||||
|
OpenSetSize++;
|
||||||
|
while(i>0) {
|
||||||
|
j=(i-1)>>1;
|
||||||
|
if(OpenSet[i].Costs<OpenSet[j].Costs) {
|
||||||
|
swap=OpenSet[i];
|
||||||
|
OpenSet[i]=OpenSet[j];
|
||||||
|
OpenSet[j]=swap;
|
||||||
|
i=j;
|
||||||
|
} else {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
** Change the cost associated to an open node. The new cost MUST BE LOWER
|
||||||
|
** than the old one in the current heap based implementation.
|
||||||
|
*/
|
||||||
|
local void AStarReplaceNode(int pos,int costs)
|
||||||
|
{
|
||||||
|
int i=pos;
|
||||||
|
int j;
|
||||||
|
Open swap;
|
||||||
|
OpenSet[pos].Costs=costs;
|
||||||
|
// we need to go up, as the cost can only decrease
|
||||||
|
while(i>0) {
|
||||||
|
j=(i-1)>>1;
|
||||||
|
if(OpenSet[i].Costs<OpenSet[j].Costs) {
|
||||||
|
swap=OpenSet[i];
|
||||||
|
OpenSet[i]=OpenSet[j];
|
||||||
|
OpenSet[j]=swap;
|
||||||
|
i=j;
|
||||||
|
} else {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
** Check if a node is already in the open set.
|
||||||
|
** Return -1 if not found and the position of the node in the table is found.
|
||||||
|
*/
|
||||||
|
local int AStarFindNode(int eo)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
for( i=0; i<OpenSetSize; ++i ) {
|
||||||
|
if( OpenSet[i].O==eo ) {
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
** Compute the cost of crossing tile (dx,dy)
|
||||||
|
** -1 -> impossible to cross
|
||||||
|
** 0 -> no induced cost, except move
|
||||||
|
** >0 -> costly tile
|
||||||
|
*/
|
||||||
|
local int CostMoveTo(int ex,int ey,int mask,int current_cost) {
|
||||||
|
int j;
|
||||||
|
Unit* goal;
|
||||||
|
|
||||||
|
j=TheMap.Fields[ex+ey*TheMap.Width].Flags&mask;
|
||||||
|
if (j) {
|
||||||
|
if(j&~(MapFieldLandUnit|MapFieldAirUnit|MapFieldSeaUnit)) {
|
||||||
|
// we can't cross fixed units and other unpassable things
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
if(current_cost>=AStarFixedUnitCrossingCost) {
|
||||||
|
// we are already crossing a fixed unit. We don't need details
|
||||||
|
return AStarMovingUnitCrossingCost;
|
||||||
|
} else {
|
||||||
|
goal=UnitOnMapTile(ex,ey);
|
||||||
|
if( !goal ) {
|
||||||
|
return -1;//FIXME: is this a bug?
|
||||||
|
}
|
||||||
|
if( goal->Moving ) {
|
||||||
|
// moving unit are crossable
|
||||||
|
return AStarMovingUnitCrossingCost;
|
||||||
|
}
|
||||||
|
// for non moving unit
|
||||||
|
return AStarFixedUnitCrossingCost;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// empty tile
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
** Find path.
|
** Find path.
|
||||||
*/
|
*/
|
||||||
local int AStarFindPath(Unit* unit,int* pxd,int* pyd)
|
local int AStarFindPath(Unit* unit,int* pxd,int* pyd)
|
||||||
{
|
{
|
||||||
int i;
|
int i,j;
|
||||||
int o;
|
int o;
|
||||||
int x;
|
int x;
|
||||||
int y;
|
int y;
|
||||||
int ex;
|
int ex;
|
||||||
int ey;
|
int ey;
|
||||||
int num_in_open;
|
int dx,dy;
|
||||||
|
int eo,gx,gy,cx,cy,sx;
|
||||||
|
int b_x,b_y,b_d,b_c;
|
||||||
|
int r;
|
||||||
int shortest;
|
int shortest;
|
||||||
int counter;
|
int counter;
|
||||||
Open OpenSet[MaxMapWidth*MaxMapHeight/64];
|
int new_cost;
|
||||||
|
int last_dir;
|
||||||
|
int path_length;
|
||||||
|
int num_in_close=0;
|
||||||
|
int mask=UnitMovementMask(unit);
|
||||||
|
int base_mask=mask&~(MapFieldLandUnit|MapFieldAirUnit|MapFieldSeaUnit);
|
||||||
|
Unit* goal;
|
||||||
static int xoffset[]={ 0,-1,+1, 0, -1,+1,-1,+1 };
|
static int xoffset[]={ 0,-1,+1, 0, -1,+1,-1,+1 };
|
||||||
static int yoffset[]={ -1, 0, 0,+1, -1,-1,+1,+1 };
|
static int yoffset[]={ -1, 0, 0,+1, -1,-1,+1,+1 };
|
||||||
|
|
||||||
DebugLevel0(__FUNCTION__": %Zd %d,%d->%d,%d\n",
|
DebugLevel3(__FUNCTION__": %Zd %d,%d->%d,%d\n",
|
||||||
UnitNumber(unit),
|
UnitNumber(unit),
|
||||||
unit->X,unit->Y,
|
unit->X,unit->Y,
|
||||||
unit->Command.Data.Move.DX,unit->Command.Data.Move.DY);
|
unit->Command.Data.Move.DX,unit->Command.Data.Move.DY);
|
||||||
|
|
||||||
AStarPrepare();
|
OpenSetSize=0;
|
||||||
|
/* AStarPrepare();*/
|
||||||
x=unit->X;
|
x=unit->X;
|
||||||
y=unit->Y;
|
y=unit->Y;
|
||||||
ex=unit->Command.Data.Move.DX;
|
r=unit->Command.Data.Move.Range;
|
||||||
ey=unit->Command.Data.Move.DY;
|
i=0;
|
||||||
|
// Let's first mark goal
|
||||||
OpenSet[0].X=x; // place start point in open
|
if(unit->Command.Data.Move.Goal) {
|
||||||
OpenSet[0].Y=y;
|
goal=unit->Command.Data.Move.Goal;
|
||||||
OpenSet[0].O=x*TheMap.Width+y;
|
j=goal->Type->Type;
|
||||||
OpenSet[0].Costs=AStarCosts(x,y,ex,ey);
|
cx=goal->X;
|
||||||
|
cy=goal->Y;
|
||||||
AStarMatrix[OpenSet[0].O].CostFromStart=0; // mark in matrix
|
ey=UnitTypes[j].TileHeight+r-1;
|
||||||
AStarMatrix[OpenSet[0].O].CostToGoal=OpenSet[0].Costs;
|
sx=UnitTypes[j].TileWidth+r-1;
|
||||||
|
// approximate goal for A*
|
||||||
num_in_open=1;
|
gx=goal->X+UnitTypes[j].TileHeight/2;
|
||||||
|
gy=goal->Y+UnitTypes[j].TileWidth/2;
|
||||||
|
} else {
|
||||||
|
cx=gx=unit->Command.Data.Move.DX;
|
||||||
|
cy=gy=unit->Command.Data.Move.DY;
|
||||||
|
ey=r;
|
||||||
|
sx=r;
|
||||||
|
r=0;
|
||||||
|
}
|
||||||
|
for(;ey>=-r;ey--){
|
||||||
|
dy=cy+ey;
|
||||||
|
if( dy<0 || dy>=TheMap.Height ) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
for(ex=sx;ex>=-r;ex--) {
|
||||||
|
dx=cx+ex;
|
||||||
|
if( dx<0 || dx>=TheMap.Width ) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if(CostMoveTo(dx,dy,mask,AStarFixedUnitCrossingCost)>=0) {
|
||||||
|
eo=dx*TheMap.Width+dy;
|
||||||
|
AStarMatrix[eo].InGoal=1;
|
||||||
|
CloseSet[num_in_close]=eo;
|
||||||
|
num_in_close++;
|
||||||
|
i=1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(i) {
|
||||||
|
eo=x*TheMap.Width+y;
|
||||||
|
AStarMatrix[eo].CostToGoal=AStarCosts(x,y,gx,gy);
|
||||||
|
// it is quite important to start from 1 rather than 0, because we use
|
||||||
|
// 0 as a way to represent nodes that we have not visited yet.
|
||||||
|
AStarMatrix[eo].CostFromStart=1;
|
||||||
|
// place start point in open
|
||||||
|
AStarAddNode(x,y,eo,AStarMatrix[eo].CostFromStart+1);
|
||||||
|
CloseSet[num_in_close]=OpenSet[0].O;
|
||||||
|
num_in_close++;
|
||||||
|
b_c=1;
|
||||||
|
b_d=AStarMatrix[eo].CostToGoal;
|
||||||
|
b_x=x;
|
||||||
|
b_y=y;
|
||||||
|
} else {
|
||||||
|
AStarCleanUp(num_in_close);
|
||||||
|
return -2;
|
||||||
|
}
|
||||||
counter=MaxMapWidth*MaxMapHeight; // how many tries
|
counter=MaxMapWidth*MaxMapHeight; // how many tries
|
||||||
|
|
||||||
for( ;; ) {
|
for( ;; ) {
|
||||||
//
|
//
|
||||||
// Find the best node of from the open set
|
// Find the best node of from the open set
|
||||||
//
|
//
|
||||||
for( i=shortest=0; i<num_in_open; ++i ) {
|
shortest=AStarFindMinimum();
|
||||||
if( OpenSet[i].Costs<OpenSet[shortest].Costs ) {
|
|
||||||
shortest=i;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
x=OpenSet[shortest].X;
|
x=OpenSet[shortest].X;
|
||||||
y=OpenSet[shortest].Y;
|
y=OpenSet[shortest].Y;
|
||||||
o=OpenSet[shortest].O;
|
o=OpenSet[shortest].O;
|
||||||
|
|
||||||
// remove by inserting the last
|
AStarRemoveMinimum(shortest);
|
||||||
OpenSet[shortest]=OpenSet[num_in_open--];
|
|
||||||
|
|
||||||
//
|
//
|
||||||
// If we have reached the goal, then exit.
|
// If we have reached the goal, then exit.
|
||||||
//
|
if( AStarMatrix[o].InGoal==1 ) {
|
||||||
if( x==ex && y==ey ) {
|
ex=x;
|
||||||
|
ey=y;
|
||||||
|
DebugLevel3(__FUNCTION__": a star goal reached\n");
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -113,20 +355,23 @@ local int AStarFindPath(Unit* unit,int* pxd,int* pyd)
|
||||||
//
|
//
|
||||||
// Select a "good" point from the open set.
|
// Select a "good" point from the open set.
|
||||||
// Nearest point to goal.
|
// Nearest point to goal.
|
||||||
/*
|
|
||||||
for( i=shortest=0; i<num_in_open; ++i ) {
|
|
||||||
if( OpenSet[i].Costs<OpenSet[shortest].Costs ) {
|
|
||||||
shortest=i;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
DebugLevel0(__FUNCTION__": %Zd way too long\n",UnitNumber(unit));
|
DebugLevel0(__FUNCTION__": %Zd way too long\n",UnitNumber(unit));
|
||||||
return 0;
|
ex=b_x;
|
||||||
|
ey=b_y;
|
||||||
}
|
}
|
||||||
|
|
||||||
//
|
//
|
||||||
// Generate successors of this node.
|
// Generate successors of this node.
|
||||||
//
|
//
|
||||||
|
if(AStarMatrix[o].CostToGoal<b_d
|
||||||
|
|| (AStarMatrix[o].CostToGoal==b_d
|
||||||
|
&& AStarMatrix[o].CostFromStart<b_c)) {
|
||||||
|
b_d=AStarMatrix[o].CostToGoal;
|
||||||
|
b_c=AStarMatrix[o].CostFromStart;
|
||||||
|
b_x=x;
|
||||||
|
b_y=y;
|
||||||
|
}
|
||||||
|
DebugLevel3("Cost from start: %d %d %d\n",x,y,new_cost);
|
||||||
for( i=0; i<8; ++i ) {
|
for( i=0; i<8; ++i ) {
|
||||||
ex=x+xoffset[i];
|
ex=x+xoffset[i];
|
||||||
ey=y+yoffset[i];
|
ey=y+yoffset[i];
|
||||||
|
@ -140,16 +385,96 @@ local int AStarFindPath(Unit* unit,int* pxd,int* pyd)
|
||||||
if( ey<0 || ey>=TheMap.Height ) {
|
if( ey<0 || ey>=TheMap.Height ) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
// if the point is "move to"-able an
|
||||||
|
// if we have not reached this point before,
|
||||||
|
// or if we have a better path to it, we add it to open set
|
||||||
|
new_cost=CostMoveTo(ex,ey,mask,AStarMatrix[o].CostFromStart);
|
||||||
|
if(new_cost==-1) {
|
||||||
|
// uncrossable tile
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
eo=ex*TheMap.Width+ey;
|
||||||
|
new_cost+=AStarMatrix[o].CostFromStart+1;
|
||||||
|
if(AStarMatrix[eo].CostFromStart==0) {
|
||||||
|
// we are sure the current node has not been already visited
|
||||||
|
AStarMatrix[eo].CostFromStart=new_cost;
|
||||||
|
AStarMatrix[eo].CostToGoal=AStarCosts(ex,ey,gx,gy);
|
||||||
|
AStarMatrix[eo].Direction=i;
|
||||||
|
AStarAddNode(ex,ey,eo,
|
||||||
|
AStarMatrix[eo].CostFromStart+
|
||||||
|
AStarMatrix[eo].CostToGoal);
|
||||||
|
// we add the point to the close set
|
||||||
|
CloseSet[num_in_close++]=eo;
|
||||||
|
} else if(new_cost<AStarMatrix[eo].CostFromStart) {
|
||||||
|
// Already visited node, but we have here a better path
|
||||||
|
// I know, it's redundant (but simpler like this)
|
||||||
|
AStarMatrix[eo].CostFromStart=new_cost;
|
||||||
|
// CostToGoal has not to be updated has it depends only on the
|
||||||
|
// position, not on the path
|
||||||
|
// AStarMatrix[eo].CostToGoal=AStarCosts(ex,ey,gx,gy);
|
||||||
|
AStarMatrix[eo].Direction=i;
|
||||||
|
// this point might be already in the OpenSet
|
||||||
|
j=AStarFindNode(eo);
|
||||||
|
if(j==-1) {
|
||||||
|
AStarAddNode(ex,ey,eo,
|
||||||
|
AStarMatrix[eo].CostFromStart+
|
||||||
|
AStarMatrix[eo].CostToGoal);
|
||||||
|
} else {
|
||||||
|
AStarReplaceNode(j,AStarMatrix[eo].CostFromStart+
|
||||||
|
AStarMatrix[eo].CostToGoal);
|
||||||
|
}
|
||||||
|
// we don't have to add this point to the close set
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
if( OpenSetSize<=0 ) { // no new nodes generated
|
||||||
if( !num_in_open ) { // no new nodes generated
|
// we go to the best node
|
||||||
DebugLevel0(__FUNCTION__": %Zd unreachable\n",UnitNumber(unit));
|
ex=b_x;
|
||||||
return 0;
|
ey=b_y;
|
||||||
|
if(ex==x && ey==y) {
|
||||||
|
DebugLevel3(__FUNCTION__": %Zd unreachable\n",UnitNumber(unit));
|
||||||
|
AStarCleanUp(num_in_close);
|
||||||
|
return -2;
|
||||||
|
}
|
||||||
|
DebugLevel3(__FUNCTION__": %Zd unreachable: going to closest\n",UnitNumber(unit));
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// now we need to backtrack
|
||||||
DebugLevel0(__FUNCTION__": %Zd\n",UnitNumber(unit));
|
path_length=0;
|
||||||
return 0;
|
x=unit->X;
|
||||||
|
y=unit->Y;
|
||||||
|
DebugLevel3("%d %d %d %d\n",x,y,ex,ey);
|
||||||
|
while(ex != x || ey !=y) {
|
||||||
|
eo=ex*TheMap.Width+ey;
|
||||||
|
i=AStarMatrix[eo].Direction;
|
||||||
|
ex-=xoffset[i];
|
||||||
|
ey-=yoffset[i];
|
||||||
|
DebugLevel3("%d %d %d %d\n",x,y,ex,ey);
|
||||||
|
path_length++;
|
||||||
|
}
|
||||||
|
*pxd=xoffset[i];
|
||||||
|
*pyd=yoffset[i];
|
||||||
|
j=CostMoveTo(ex+*pxd,ey+*pyd,mask,0);
|
||||||
|
if(j!=0) {
|
||||||
|
if(j==AStarMovingUnitCrossingCost) {
|
||||||
|
// we should wait, we are blocked by a moving unit
|
||||||
|
//FIXME: this might lead to a deadlock, or something similar
|
||||||
|
DebugLevel3("Unit %Zd waiting. Proposed move: %d %d\n",
|
||||||
|
UnitNumber(unit),*pxd,*pyd);
|
||||||
|
path_length=0;
|
||||||
|
} else {
|
||||||
|
// j==-1 is a bug, so we should have only
|
||||||
|
// j==AStarFixedUnitCrossingCost, which means
|
||||||
|
// the way is blocked by a non moving unit. Waiting is here
|
||||||
|
// pointless.
|
||||||
|
path_length=-2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// let's clean up the matrix now
|
||||||
|
AStarCleanUp(num_in_close);
|
||||||
|
DebugLevel3(__FUNCTION__": %Zd\n",UnitNumber(unit));
|
||||||
|
DebugLevel3(__FUNCTION__": proposed move: %d %d (%d)\n",*pxd,*pyd,path_length);
|
||||||
|
return path_length;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -165,16 +490,55 @@ local int AStarFindPath(Unit* unit,int* pxd,int* pyd)
|
||||||
global int AStarNextPathElement(Unit* unit,int* pxd,int *pyd)
|
global int AStarNextPathElement(Unit* unit,int* pxd,int *pyd)
|
||||||
{
|
{
|
||||||
// FIXME: Cache for often used pathes, like peons to goldmine.
|
// FIXME: Cache for often used pathes, like peons to goldmine.
|
||||||
AStarFindPath(unit,pxd,pyd);
|
// FIXME: (fabrice) I've copied here the code from NewPath. Is it really
|
||||||
|
// needed?
|
||||||
switch( NewPath(unit,pxd,pyd) ) {
|
int x;
|
||||||
case 0:
|
int y;
|
||||||
return 999;
|
int r=unit->Command.Data.Move.Range;
|
||||||
case 1:
|
Unit* goal=unit->Command.Data.Move.Goal;
|
||||||
|
UnitType* type;
|
||||||
|
|
||||||
|
x=unit->X;
|
||||||
|
y=unit->Y;
|
||||||
|
if( goal ) { // goal unit
|
||||||
|
type=goal->Type;
|
||||||
|
DebugLevel3(__FUNCTION__": Unit %d,%d Goal %d,%d - %d,%d\n"
|
||||||
|
,x,y
|
||||||
|
,goal->X-r,goal->Y-r
|
||||||
|
,goal->X+type->TileWidth+r
|
||||||
|
,goal->Y+type->TileHeight+r);
|
||||||
|
if( x>=goal->X-r && x<goal->X+type->TileWidth+r
|
||||||
|
&& y>=goal->Y-r && y<goal->Y+type->TileHeight+r ) {
|
||||||
|
DebugLevel3(__FUNCTION__": Goal reached\n");
|
||||||
|
*pxd=*pyd=0;
|
||||||
return -1;
|
return -1;
|
||||||
default:
|
}
|
||||||
return -2;
|
} else { // goal map field
|
||||||
|
if( x>=unit->Command.Data.Move.DX
|
||||||
|
&& x<=unit->Command.Data.Move.DX+r
|
||||||
|
&& y>=unit->Command.Data.Move.DY
|
||||||
|
&& y<=unit->Command.Data.Move.DY+r ) {
|
||||||
|
DebugLevel3(__FUNCTION__": Field reached\n");
|
||||||
|
*pxd=*pyd=0;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
// This reduces the processor use,
|
||||||
|
// If target isn't reachable and were beside it
|
||||||
|
if( r==0 && x>=unit->Command.Data.Move.DX-1
|
||||||
|
&& x<=unit->Command.Data.Move.DX+1
|
||||||
|
&& y>=unit->Command.Data.Move.DY-1
|
||||||
|
&& y<=unit->Command.Data.Move.DY+1 ) {
|
||||||
|
if( !CheckedCanMoveToMask(unit->Command.Data.Move.DX
|
||||||
|
,unit->Command.Data.Move.DY
|
||||||
|
,UnitMovementMask(unit)) ) { // blocked
|
||||||
|
DebugLevel3(__FUNCTION__": Field unreached\n");
|
||||||
|
*pxd=*pyd=0;
|
||||||
|
return -2;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return AStarFindPath(unit,pxd,pyd);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -190,7 +554,9 @@ global int AStarNextPathElement(Unit* unit,int* pxd,int *pyd)
|
||||||
global int NextPathElement(Unit* unit,int* pxd,int *pyd)
|
global int NextPathElement(Unit* unit,int* pxd,int *pyd)
|
||||||
{
|
{
|
||||||
// Convert old version to new version
|
// Convert old version to new version
|
||||||
if( 1 || unit!=Selected[0] ) {
|
if(AStarOn) {
|
||||||
|
return AStarNextPathElement(unit,pxd,pyd);
|
||||||
|
} else {
|
||||||
switch( NewPath(unit,pxd,pyd) ) {
|
switch( NewPath(unit,pxd,pyd) ) {
|
||||||
case 0:
|
case 0:
|
||||||
return 999;
|
return 999;
|
||||||
|
@ -200,10 +566,6 @@ global int NextPathElement(Unit* unit,int* pxd,int *pyd)
|
||||||
return -2;
|
return -2;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
DebugLevel0(__FUNCTION__": %Zd#%s\n",UnitNumber(unit),unit->Type->Ident);
|
|
||||||
|
|
||||||
return AStarNextPathElement(unit,pxd,pyd);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//@}
|
//@}
|
||||||
|
|
117
src/pathfinder/script_pathfinder.cpp
Normal file
117
src/pathfinder/script_pathfinder.cpp
Normal file
|
@ -0,0 +1,117 @@
|
||||||
|
// ___________ _________ _____ __
|
||||||
|
// \_ _____/______ ____ ____ \_ ___ \____________ _/ ____\/ |_
|
||||||
|
// | __) \_ __ \_/ __ \_/ __ \/ \ \/\_ __ \__ \\ __\\ __\
|
||||||
|
// | \ | | \/\ ___/\ ___/\ \____| | \// __ \| | | |
|
||||||
|
// \___ / |__| \___ >\___ >\______ /|__| (____ /__| |__|
|
||||||
|
// \/ \/ \/ \/ \/
|
||||||
|
// ______________________ ______________________
|
||||||
|
// T H E W A R B E G I N S
|
||||||
|
// FreeCraft - A free fantasy real time strategy game engine
|
||||||
|
//
|
||||||
|
/**@name ccl_pathfinder.c - pathfinder ccl functions. */
|
||||||
|
/*
|
||||||
|
** (c) Copyright 2000 by Lutz Sammer and Fabrice Rossi
|
||||||
|
**
|
||||||
|
** $Id$
|
||||||
|
*/
|
||||||
|
|
||||||
|
//@{
|
||||||
|
|
||||||
|
/*----------------------------------------------------------------------------
|
||||||
|
-- Includes
|
||||||
|
----------------------------------------------------------------------------*/
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#include "clone.h"
|
||||||
|
|
||||||
|
#if defined(USE_CCL) || defined(USE_CCL2) // {
|
||||||
|
|
||||||
|
#include "video.h"
|
||||||
|
#include "tileset.h"
|
||||||
|
#include "map.h"
|
||||||
|
#include "sound_id.h"
|
||||||
|
#include "unitsound.h"
|
||||||
|
#include "unittype.h"
|
||||||
|
#include "player.h"
|
||||||
|
#include "unit.h"
|
||||||
|
#include "ccl.h"
|
||||||
|
#include "pathfinder.h"
|
||||||
|
|
||||||
|
/*----------------------------------------------------------------------------
|
||||||
|
-- Functions
|
||||||
|
----------------------------------------------------------------------------*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
** Enable a*.
|
||||||
|
*/
|
||||||
|
local SCM CclAStar(void)
|
||||||
|
{
|
||||||
|
AStarOn=1;
|
||||||
|
|
||||||
|
return SCM_UNSPECIFIED;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
** Disable a*.
|
||||||
|
*/
|
||||||
|
local SCM CclNoAStar(void)
|
||||||
|
{
|
||||||
|
AStarOn=0;
|
||||||
|
|
||||||
|
return SCM_UNSPECIFIED;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
** Set a* parameter (cost of FIXED unit tile crossing).
|
||||||
|
*/
|
||||||
|
local SCM CclAStarSetFixedUCC(SCM cost)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
|
||||||
|
i=gh_scm2int(cost);
|
||||||
|
if( i<=0) {
|
||||||
|
fprintf(stderr,__FUNCTION__": Fixed unit crossing cost must be strictly positive\n");
|
||||||
|
i=MaxMapWidth*MaxMapHeight;
|
||||||
|
}
|
||||||
|
AStarFixedUnitCrossingCost=i;
|
||||||
|
|
||||||
|
return SCM_UNSPECIFIED;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
** Set a* parameter (cost of MOVING unit tile crossing).
|
||||||
|
*/
|
||||||
|
local SCM CclAStarSetMovingUCC(SCM cost)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
|
||||||
|
i=gh_scm2int(cost);
|
||||||
|
if( i<=0) {
|
||||||
|
fprintf(stderr,__FUNCTION__": Moving unit crossing cost must be strictly positive\n");
|
||||||
|
i=1;
|
||||||
|
}
|
||||||
|
AStarMovingUnitCrossingCost=i;
|
||||||
|
|
||||||
|
return SCM_UNSPECIFIED;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
** Register CCL features for pathfinder.
|
||||||
|
*/
|
||||||
|
global void PathfinderCclRegister(void)
|
||||||
|
{
|
||||||
|
gh_new_procedure0_0("a-star",CclAStar);
|
||||||
|
gh_new_procedure0_0("no-a-star",CclNoAStar);
|
||||||
|
gh_new_procedure1_0("a-star-fixed-unit-cost",CclAStarSetFixedUCC);
|
||||||
|
gh_new_procedure1_0("a-star-moving-unit-cost",CclAStarSetMovingUCC);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#endif // } defined(USE_CCL) || defined(USE_CCL2)
|
||||||
|
|
||||||
|
//@}
|
|
@ -47,6 +47,7 @@
|
||||||
#include "ccl_sound.h"
|
#include "ccl_sound.h"
|
||||||
#include "ccl.h"
|
#include "ccl.h"
|
||||||
#include "font.h"
|
#include "font.h"
|
||||||
|
#include "pathfinder.h"
|
||||||
|
|
||||||
#include <guile/gh.h> // I use guile for a quick hack
|
#include <guile/gh.h> // I use guile for a quick hack
|
||||||
|
|
||||||
|
@ -79,6 +80,7 @@ extern void sgtk_init_gtk_gdk_glue();
|
||||||
#include "ccl_sound.h"
|
#include "ccl_sound.h"
|
||||||
#include "ui.h"
|
#include "ui.h"
|
||||||
#include "font.h"
|
#include "font.h"
|
||||||
|
#include "pathfinder.h"
|
||||||
#include "ai.h"
|
#include "ai.h"
|
||||||
|
|
||||||
#endif // USE_CCL2
|
#endif // USE_CCL2
|
||||||
|
@ -702,6 +704,7 @@ local void gh_main_prog(int argc,char* argv[])
|
||||||
gh_new_procedureN("missile-type",CclMissileType);
|
gh_new_procedureN("missile-type",CclMissileType);
|
||||||
|
|
||||||
MapCclRegister();
|
MapCclRegister();
|
||||||
|
PathfinderCclRegister();
|
||||||
UnitButtonCclRegister();
|
UnitButtonCclRegister();
|
||||||
UnitTypeCclRegister();
|
UnitTypeCclRegister();
|
||||||
SoundCclRegister();
|
SoundCclRegister();
|
||||||
|
@ -812,6 +815,7 @@ global void CclInit(void)
|
||||||
init_lsubr("missile-type",CclMissileType);
|
init_lsubr("missile-type",CclMissileType);
|
||||||
|
|
||||||
MapCclRegister();
|
MapCclRegister();
|
||||||
|
PathfinderCclRegister();
|
||||||
UnitButtonCclRegister();
|
UnitButtonCclRegister();
|
||||||
UnitTypeCclRegister();
|
UnitTypeCclRegister();
|
||||||
UpgradesCclRegister();
|
UpgradesCclRegister();
|
||||||
|
|
Loading…
Add table
Reference in a new issue