From 53bab85ed36a62fabb15075dc9ab70d902acbf64 Mon Sep 17 00:00:00 2001
From: frossi <>
Date: Mon, 3 Apr 2000 12:23:02 +0000
Subject: [PATCH] Added a first implementation of the new pathfinder, based on
 the a* algorithm.

---
 src/include/pathfinder.h             |  12 +
 src/pathfinder/Makefile              |   2 +-
 src/pathfinder/astar.cpp             | 484 +++++++++++++++++++++++----
 src/pathfinder/script_pathfinder.cpp | 117 +++++++
 src/stratagus/script.cpp             |   4 +
 5 files changed, 557 insertions(+), 62 deletions(-)
 create mode 100644 src/pathfinder/script_pathfinder.cpp

diff --git a/src/include/pathfinder.h b/src/include/pathfinder.h
index fedc81043..3fbe44707 100644
--- a/src/include/pathfinder.h
+++ b/src/include/pathfinder.h
@@ -47,6 +47,12 @@ enum _move_return_ {
 
 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
@@ -64,6 +70,12 @@ extern int UnitReachable(Unit* unit,Unit* dest);
     /// Returns the next element of the path
 extern int NextPathElement(Unit*,int* xdp,int* ydp);
 
+//
+//	in ccl_pathfinder.c
+//
+    /// register ccl features
+extern void PathfinderCclRegister(void);
+
 //@}
 
 #endif	// !__PATH_FINDER_H__
diff --git a/src/pathfinder/Makefile b/src/pathfinder/Makefile
index 09989911e..13efc75e9 100644
--- a/src/pathfinder/Makefile
+++ b/src/pathfinder/Makefile
@@ -14,6 +14,6 @@ include $(TOPDIR)/Rules.make
 
 MODULE  = pathfinder
 
-OBJS	= pathfinder.$(OE) astar.$(OE)
+OBJS	= pathfinder.$(OE) astar.$(OE) ccl_pathfinder.$(OE)
 
 include $(TOPDIR)/Common.mk
diff --git a/src/pathfinder/astar.cpp b/src/pathfinder/astar.cpp
index e49ed0e09..2026774b6 100644
--- a/src/pathfinder/astar.cpp
+++ b/src/pathfinder/astar.cpp
@@ -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. */
 /*
-**	(c) Copyright 1999 by Lutz Sammer
+**	(c) Copyright 1999-2000 by Lutz Sammer and Fabrice Rossi
 **
 **	$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>
@@ -19,7 +29,8 @@
 #include "pathfinder.h"
 
 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		CostToGoal;	/// Aproximated costs until goal
 } Node;
@@ -31,9 +42,19 @@ typedef struct _open_ {
     int		Costs;		/// complete costs to goal
 } Open;
 
+/// heuristic cost fonction for a star
 #define AStarCosts(sx,sy,ex,ey)	max(abs(sx-ex),abs(sy-ey))
 
+/// cost matrix
 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.
@@ -43,66 +64,287 @@ local void AStarPrepare(void)
     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.	
 */
 local int AStarFindPath(Unit* unit,int* pxd,int* pyd)
 {
-    int i;
+    int i,j;
     int o;
     int x;
     int y;
     int ex;
     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 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 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),
 	    unit->X,unit->Y,
 	    unit->Command.Data.Move.DX,unit->Command.Data.Move.DY);
-
-    AStarPrepare();
+ 
+    OpenSetSize=0;
+   /*    AStarPrepare();*/
     x=unit->X;
     y=unit->Y;
-    ex=unit->Command.Data.Move.DX;
-    ey=unit->Command.Data.Move.DY;
-
-    OpenSet[0].X=x;			// place start point in open
-    OpenSet[0].Y=y;
-    OpenSet[0].O=x*TheMap.Width+y;
-    OpenSet[0].Costs=AStarCosts(x,y,ex,ey);
-
-    AStarMatrix[OpenSet[0].O].CostFromStart=0;	// mark in matrix
-    AStarMatrix[OpenSet[0].O].CostToGoal=OpenSet[0].Costs;
-
-    num_in_open=1;
+    r=unit->Command.Data.Move.Range;
+    i=0;
+    // Let's first mark goal
+    if(unit->Command.Data.Move.Goal) {
+	goal=unit->Command.Data.Move.Goal;
+	j=goal->Type->Type;
+	cx=goal->X;
+	cy=goal->Y;
+	ey=UnitTypes[j].TileHeight+r-1;
+	sx=UnitTypes[j].TileWidth+r-1;
+	// approximate goal for A*
+	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
 
     for( ;; ) {
 	//
 	//	Find the best node of from the open set
 	//
-	for( i=shortest=0; i<num_in_open; ++i ) {
-	    if( OpenSet[i].Costs<OpenSet[shortest].Costs ) {
-		shortest=i;
-	    }
-	}
+	shortest=AStarFindMinimum();
 	x=OpenSet[shortest].X;
 	y=OpenSet[shortest].Y;
 	o=OpenSet[shortest].O;
-
-	// remove by inserting the last
-	OpenSet[shortest]=OpenSet[num_in_open--];
+	
+	AStarRemoveMinimum(shortest);
 
 	//
 	//	If we have reached the goal, then exit.
-	//
-	if( x==ex && y==ey ) {
+	if( AStarMatrix[o].InGoal==1 ) {
+	    ex=x;
+	    ey=y;
+	    DebugLevel3(__FUNCTION__": a star goal reached\n");
 	    break;
 	}
 
@@ -113,20 +355,23 @@ local int AStarFindPath(Unit* unit,int* pxd,int* pyd)
 	    //
 	    //	Select a "good" point from the open set.
 	    //		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));
-	    return 0;
+	    ex=b_x;
+	    ey=b_y;
 	}
 
 	//
 	//	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 ) {
 	    ex=x+xoffset[i];
 	    ey=y+yoffset[i];
@@ -140,16 +385,96 @@ local int AStarFindPath(Unit* unit,int* pxd,int* pyd)
 	    if( ey<0 || ey>=TheMap.Height ) {
 		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( !num_in_open ) {		// no new nodes generated
-	    DebugLevel0(__FUNCTION__": %Zd unreachable\n",UnitNumber(unit));
-	    return 0;
+	if( OpenSetSize<=0 ) {		// no new nodes generated
+	    // we go to the best node
+	    ex=b_x;
+	    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;
 	}
     }
-
-    DebugLevel0(__FUNCTION__": %Zd\n",UnitNumber(unit));
-    return 0;
+    // now we need to backtrack
+    path_length=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)
 {
     // FIXME: Cache for often used pathes, like peons to goldmine.
-    AStarFindPath(unit,pxd,pyd);
-
-    switch( NewPath(unit,pxd,pyd) ) {
-	case 0:
-	    return 999;
-	case 1:
+    // FIXME: (fabrice) I've copied here the code from NewPath. Is it really
+    // needed?
+    int x;
+    int y;
+    int r=unit->Command.Data.Move.Range;
+    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;
-	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)
 {
     // Convert old version to new version
-    if( 1 || unit!=Selected[0] ) {
+    if(AStarOn) {
+	return AStarNextPathElement(unit,pxd,pyd);
+    } else {
 	switch( NewPath(unit,pxd,pyd) ) {
 	    case 0:
 		return 999;
@@ -200,10 +566,6 @@ global int NextPathElement(Unit* unit,int* pxd,int *pyd)
 		return -2;
 	}
     }
-
-    DebugLevel0(__FUNCTION__": %Zd#%s\n",UnitNumber(unit),unit->Type->Ident);
-
-    return AStarNextPathElement(unit,pxd,pyd);
 }
 
 //@}
diff --git a/src/pathfinder/script_pathfinder.cpp b/src/pathfinder/script_pathfinder.cpp
new file mode 100644
index 000000000..c6bef1476
--- /dev/null
+++ b/src/pathfinder/script_pathfinder.cpp
@@ -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)
+
+//@}
diff --git a/src/stratagus/script.cpp b/src/stratagus/script.cpp
index 61a5f7b48..f374d2c7d 100644
--- a/src/stratagus/script.cpp
+++ b/src/stratagus/script.cpp
@@ -47,6 +47,7 @@
 #include "ccl_sound.h"
 #include "ccl.h"
 #include "font.h"
+#include "pathfinder.h" 
 
 #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 "ui.h"
 #include "font.h"
+#include "pathfinder.h" 
 #include "ai.h"
 
 #endif // USE_CCL2
@@ -702,6 +704,7 @@ local void gh_main_prog(int argc,char* argv[])
     gh_new_procedureN("missile-type",CclMissileType);
 
     MapCclRegister();
+    PathfinderCclRegister(); 
     UnitButtonCclRegister();
     UnitTypeCclRegister();
     SoundCclRegister();
@@ -812,6 +815,7 @@ global void CclInit(void)
     init_lsubr("missile-type",CclMissileType);
 
     MapCclRegister();
+    PathfinderCclRegister(); 
     UnitButtonCclRegister();
     UnitTypeCclRegister();
     UpgradesCclRegister();