Pārlūkot izejas kodu

Implemented redesigned pathfinder. Using new info from it, implemented various adventure map cursors.
(Paths are still calculated using the old pathfinder, it will be changed soon)

Michał W. Urbańczyk 16 gadi atpakaļ
vecāks
revīzija
ca7ee8936c

+ 10 - 0
CCallback.cpp

@@ -829,6 +829,16 @@ const CMapHeader * CCallback::getMapHeader() const
 	return gs->map;
 	return gs->map;
 }
 }
 
 
+const CGPathNode * CCallback::getPathInfo( int3 tile )
+{
+	return &cl->pathInfo->nodes[tile.x][tile.y][tile.z];
+}
+
+bool CCallback::getPath2( int3 dest, CGPath &ret )
+{
+	return cl->pathInfo->getPath(dest, ret);
+}
+
 InfoAboutHero::InfoAboutHero()
 InfoAboutHero::InfoAboutHero()
 {
 {
 	details = NULL;
 	details = NULL;

+ 6 - 0
CCallback.h

@@ -40,6 +40,8 @@ class CHeroClass;
 class IShipyard;
 class IShipyard;
 struct CPackForServer;
 struct CPackForServer;
 class CMapHeader;
 class CMapHeader;
+struct CGPathNode;
+struct CGPath;
 
 
 struct InfoAboutHero
 struct InfoAboutHero
 {
 {
@@ -135,6 +137,8 @@ public:
 	virtual std::vector < const CGHeroInstance *> getHeroesInfo(bool onlyOur=true)const =0;
 	virtual std::vector < const CGHeroInstance *> getHeroesInfo(bool onlyOur=true)const =0;
 	virtual bool getHeroInfo(const CGObjectInstance *hero, InfoAboutHero &dest) const = 0;
 	virtual bool getHeroInfo(const CGObjectInstance *hero, InfoAboutHero &dest) const = 0;
 	virtual bool getPath(int3 src, int3 dest, const CGHeroInstance * hero, CPath &ret)=0;
 	virtual bool getPath(int3 src, int3 dest, const CGHeroInstance * hero, CPath &ret)=0;
+	virtual const CGPathNode *getPathInfo(int3 tile)=0;
+	virtual bool getPath2(int3 dest, CGPath &ret)=0;
 	
 	
 	//map
 	//map
 	virtual std::vector < const CGObjectInstance * > getBlockingObjs(int3 pos)const =0;
 	virtual std::vector < const CGObjectInstance * > getBlockingObjs(int3 pos)const =0;
@@ -261,6 +265,8 @@ public:
 	const TerrainTile * getTileInfo(int3 tile) const;
 	const TerrainTile * getTileInfo(int3 tile) const;
 	int canBuildStructure(const CGTownInstance *t, int ID);//// 0 - no more than one capitol, 1 - lack of water, 2 - forbidden, 3 - Add another level to Mage Guild, 4 - already built, 5 - cannot build, 6 - cannot afford, 7 - build, 8 - lack of requirements
 	int canBuildStructure(const CGTownInstance *t, int ID);//// 0 - no more than one capitol, 1 - lack of water, 2 - forbidden, 3 - Add another level to Mage Guild, 4 - already built, 5 - cannot build, 6 - cannot afford, 7 - build, 8 - lack of requirements
 	bool getPath(int3 src, int3 dest, const CGHeroInstance * hero, CPath &ret);
 	bool getPath(int3 src, int3 dest, const CGHeroInstance * hero, CPath &ret);
+	const CGPathNode *getPathInfo(int3 tile);
+	bool getPath2(int3 dest, CGPath &ret);
 	bool getHeroInfo(const CGObjectInstance *hero, InfoAboutHero &dest) const;
 	bool getHeroInfo(const CGObjectInstance *hero, InfoAboutHero &dest) const;
 	bool getTownInfo(const CGObjectInstance *town, InfoAboutTown &dest) const;
 	bool getTownInfo(const CGObjectInstance *town, InfoAboutTown &dest) const;
 
 

+ 106 - 17
client/CAdvmapInterface.cpp

@@ -652,32 +652,121 @@ void CTerrainRect::mouseMoved (const SDL_MouseMotionEvent & sEvent)
 	{
 	{
 		LOCPLINT->adventureInt->statusbar.clear();
 		LOCPLINT->adventureInt->statusbar.clear();
 	}
 	}
-	std::vector<const CGObjectInstance *> objs = LOCPLINT->cb->getVisitableObjs(pom); 
-	for(int i=0; i<objs.size();i++)
+
+	const CGPathNode *pnode = LOCPLINT->cb->getPathInfo(pom);
+	std::vector<const CGObjectInstance *> objs = LOCPLINT->cb->getBlockingObjs(pom); 
+	const CGObjectInstance *obj = objs.size() ? objs.back() : NULL;
+	bool accessible  =  pnode->turns < 255;
+
+	int turns = pnode->turns;
+	amin(turns, 4);
+
+	if(LOCPLINT->adventureInt->selection->ID == TOWNI_TYPE)
 	{
 	{
-		if(objs[i]->ID == TOWNI_TYPE) //town
+		if(obj)
 		{
 		{
-			CGI->curh->changeGraphic(0,0);
-			return;
+			if(obj->ID == TOWNI_TYPE)
+			{
+				CGI->curh->changeGraphic(0, 3);
+			}
+			else if(obj->ID == HEROI_TYPE)
+			{
+				CGI->curh->changeGraphic(0, 2);
+			}
+		}
+		else
+		{
+			CGI->curh->changeGraphic(0, 0);
 		}
 		}
 	}
 	}
-	objs = LOCPLINT->cb->getBlockingObjs(pom);
-	for(size_t i=0; i < objs.size(); ++i)
+	else if(LOCPLINT->adventureInt->selection->ID == HEROI_TYPE)
 	{
 	{
-		if(objs[i]->ID == TOWNI_TYPE && objs[i]->tempOwner == LOCPLINT->playerID) //town
+		const CGHeroInstance *h = static_cast<const CGHeroInstance *>(LOCPLINT->adventureInt->selection);
+		if(obj)
 		{
 		{
-			CGI->curh->changeGraphic(0,3);
-			return;
-		}
-		else if(objs[i]->ID == HEROI_TYPE //mouse over hero
-			&& (objs[i]==LOCPLINT->adventureInt->selection  ||  LOCPLINT->adventureInt->selection->ID==TOWNI_TYPE)
-			&& objs[i]->tempOwner == LOCPLINT->playerID) //this hero is selected or we've selected a town
+			if(obj->ID == HEROI_TYPE)
+			{
+				if(obj->tempOwner != LOCPLINT->playerID) //enemy hero TODO: allies
+				{
+					if(accessible)
+						CGI->curh->changeGraphic(0, 5 + turns*6);
+					else
+						CGI->curh->changeGraphic(0, 0);
+				}
+				else //our hero
+				{
+					if(LOCPLINT->adventureInt->selection == obj)
+						CGI->curh->changeGraphic(0, 2);
+					else if(accessible)
+						CGI->curh->changeGraphic(0, 8 + turns*6);
+					else
+						CGI->curh->changeGraphic(0, 2);
+				}
+			}
+			else if(obj->ID == TOWNI_TYPE)
+			{
+				if(obj->tempOwner != LOCPLINT->playerID) //enemy town TODO: allies
+				{
+					if(accessible)
+						CGI->curh->changeGraphic(0, 5 + turns*6);
+					else
+						CGI->curh->changeGraphic(0, 0);
+				}
+				else //our town
+				{
+					if(accessible)
+						CGI->curh->changeGraphic(0, 9 + turns*6);
+					else
+						CGI->curh->changeGraphic(0, 3);
+				}
+			}
+			else if(obj->ID == 54) //monster
+			{
+				if(accessible)
+					CGI->curh->changeGraphic(0, 5 + turns*6);
+				else
+					CGI->curh->changeGraphic(0, 0);
+			}
+			else if(obj->ID == 8) //boat
+			{
+				if(accessible)
+					CGI->curh->changeGraphic(0, 6 + turns*6);
+				else
+					CGI->curh->changeGraphic(0, 0);
+			}
+			else
+			{
+				if(accessible)
+				{
+					if(pnode->land)
+						CGI->curh->changeGraphic(0, 9 + turns*6);
+					else
+						CGI->curh->changeGraphic(0, 28 + turns);
+				}
+				else
+					CGI->curh->changeGraphic(0, 0);
+			}
+		} 
+		else //no objs 
 		{
 		{
-			CGI->curh->changeGraphic(0,2);
-			return;
+			if(accessible)
+			{
+				if(pnode->land)
+				{
+					if(LOCPLINT->cb->getTileInfo(h->getPosition(false))->tertype != TerrainTile::water)
+						CGI->curh->changeGraphic(0, 4 + turns*6);
+					else
+						CGI->curh->changeGraphic(0, 7 + turns*6); //anchor
+				}
+				else
+					CGI->curh->changeGraphic(0, 6 + turns*6);
+			}
+			else
+				CGI->curh->changeGraphic(0, 0);
 		}
 		}
 	}
 	}
-	CGI->curh->changeGraphic(0,0);
+
+	//tlog1 << "Tile " << pom << ": Turns=" << (int)pnode->turns <<"  Move:=" << pnode->moveRemains <</* " (from  "  << ")" << */std::endl;
 }
 }
 void CTerrainRect::hover(bool on)
 void CTerrainRect::hover(bool on)
 {
 {

+ 102 - 56
client/CCursorHandler.cpp

@@ -20,7 +20,7 @@ extern SDL_Surface * screen;
 void CCursorHandler::initCursor()
 void CCursorHandler::initCursor()
 {
 {
 	mode = number = xpos = ypos = 0;
 	mode = number = xpos = ypos = 0;
-	help = CSDL_Ext::newSurface(32,32);
+	help = CSDL_Ext::newSurface(40,40);
 	cursors.push_back(CDefHandler::giveDef("CRADVNTR.DEF"));
 	cursors.push_back(CDefHandler::giveDef("CRADVNTR.DEF"));
 	cursors.push_back(CDefHandler::giveDef("CRCOMBAT.DEF"));
 	cursors.push_back(CDefHandler::giveDef("CRCOMBAT.DEF"));
 	cursors.push_back(CDefHandler::giveDef("CRDEFLT.DEF"));
 	cursors.push_back(CDefHandler::giveDef("CRDEFLT.DEF"));
@@ -43,70 +43,116 @@ void CCursorHandler::draw1()
 {
 {
 	if(!Show) return;
 	if(!Show) return;
 	int x = xpos, y = ypos;
 	int x = xpos, y = ypos;
-	if((mode==1 && number!=6) || mode ==3)
-	{
-		x-=16;
-		y-=16;
-
-		// Properly align the melee attack cursors.
-		if (mode == 1) {
-			switch (number) {
-				case 7: // Bottom left
-					x -= 6;
-					y += 16;
-					break;
-				case 8: // Left
-					x -= 16;
-					y += 10;
-					break;
-				case 9: // Top left
-					x -= 6;
-					y -= 6;
-					break;
-				case 10: // Top right
-					x += 16;
-					y -= 6;
-					break;
-				case 11: // Right
-					x += 16;
-					y += 11;
-					break;
-				case 12: // Bottom right
-					x += 16;
-					y += 16;
-					break;
-				case 13: // Below
-					x += 9;
-					y += 16;
-					break;
-				case 14: // Above
-					x += 9;
-					y -= 15;
-					break;
-			}
-		}
-	}
-	else if(mode==0 && number>0)
-	{
-		x-=12;
-		y-=10;
-	}
-	SDL_BlitSurface(screen, &genRect(32,32,x,y), help, &genRect(32,32,0,0));
+	shiftPos(x, y);
+	SDL_BlitSurface(screen, &genRect(40,40,x,y), help, &genRect(40,40,0,0));
 	blitAt(cursors[mode]->ourImages[number].bitmap,x,y);
 	blitAt(cursors[mode]->ourImages[number].bitmap,x,y);
 }
 }
 void CCursorHandler::draw2()
 void CCursorHandler::draw2()
 {
 {
 	if(!Show) return;
 	if(!Show) return;
 	int x = xpos, y = ypos;
 	int x = xpos, y = ypos;
-	if((mode==1 && number!=6) || mode == 3)
+	shiftPos(x, y);
+	blitAt(help,x,y);
+}
+
+void CCursorHandler::shiftPos( int &x, int &y )
+{
+	if((mode==1 && number!=6) || mode ==3)
 	{
 	{
 		x-=16;
 		x-=16;
 		y-=16;
 		y-=16;
+
+		// Properly align the melee attack cursors.
+		if (mode == 1) 
+		{
+			switch (number) 
+			{
+			case 7: // Bottom left
+				x -= 6;
+				y += 16;
+				break;
+			case 8: // Left
+				x -= 16;
+				y += 10;
+				break;
+			case 9: // Top left
+				x -= 6;
+				y -= 6;
+				break;
+			case 10: // Top right
+				x += 16;
+				y -= 6;
+				break;
+			case 11: // Right
+				x += 16;
+				y += 11;
+				break;
+			case 12: // Bottom right
+				x += 16;
+				y += 16;
+				break;
+			case 13: // Below
+				x += 9;
+				y += 16;
+				break;
+			case 14: // Above
+				x += 9;
+				y -= 15;
+				break;
+			}
+		}
 	}
 	}
-	else if(mode==0 && number>0)
+	else if(mode==0)
 	{
 	{
-		x-=12;
-		y-=10;
+		if(number == 0); //to exclude
+		else if(number == 2)
+		{
+			x -= 12;
+			y -= 10;
+		}
+		else if(number == 3)
+		{
+			x -= 12;
+			y -= 12;
+		}
+		else if(number < 27)
+		{
+			int hlpNum = (number - 4)%6;
+			if(hlpNum == 0)
+			{
+				x -= 15;
+				y -= 13;
+			}
+			else if(hlpNum == 1)
+			{
+				x -= 13;
+				y -= 13;
+			}
+			else if(hlpNum == 2)
+			{
+				x -= 20;
+				y -= 20;
+			}
+			else if(hlpNum == 3)
+			{
+				x -= 13;
+				y -= 16;
+			}
+			else if(hlpNum == 4)
+			{
+				x -= 8;
+				y -= 9;
+			}
+			else if(hlpNum == 5)
+			{
+				x -= 14;
+				y -= 16;
+			}
+		}
+		else if(number < 31)
+		{
+			x -= 20;
+			y -= 20;
+		}
 	}
 	}
-	blitAt(help,x,y);
-}
+}

+ 2 - 0
client/CCursorHandler.h

@@ -29,6 +29,8 @@ public:
 	void cursorMove(const int & x, const int & y); //change cursor's positions to (x, y)
 	void cursorMove(const int & x, const int & y); //change cursor's positions to (x, y)
 	void changeGraphic(const int & type, const int & no); //changes cursor graphic for type type (0 - adventure, 1 - combat, 2 - default, 3 - spellbook) and frame no (not used for type 3)
 	void changeGraphic(const int & type, const int & no); //changes cursor graphic for type type (0 - adventure, 1 - combat, 2 - default, 3 - spellbook) and frame no (not used for type 3)
 	void draw1();
 	void draw1();
+
+	void shiftPos( int &x, int &y );
 	void draw2();
 	void draw2();
 	void hide(){Show=0;};
 	void hide(){Show=0;};
 	void show(){Show=1;};
 	void show(){Show=1;};

+ 1 - 0
client/CPlayerInterface.h

@@ -64,6 +64,7 @@ class KeyInterested;
 class MotionInterested;
 class MotionInterested;
 class TimeInterested;
 class TimeInterested;
 class IShowable;
 class IShowable;
+struct CPathsInfo;
 
 
 namespace boost
 namespace boost
 {
 {

+ 4 - 0
client/Client.cpp

@@ -82,6 +82,7 @@ public:
 
 
 void CClient::init()
 void CClient::init()
 {
 {
+	pathInfo = NULL;
 	applier = new CCLApplier;
 	applier = new CCLApplier;
 	IObjectInterface::cb = this;
 	IObjectInterface::cb = this;
 	serv = NULL;
 	serv = NULL;
@@ -107,6 +108,7 @@ CClient::CClient(CConnection *con, StartInfo *si)
 }
 }
 CClient::~CClient(void)
 CClient::~CClient(void)
 {
 {
+	delete pathInfo;
 	delete applier;
 	delete applier;
 	delete shared;
 	delete shared;
 }
 }
@@ -232,6 +234,7 @@ void CClient::load( const std::string & fname )
 
 
 		CGI->state = gs;
 		CGI->state = gs;
 		CGI->mh->map = gs->map;
 		CGI->mh->map = gs->map;
+		pathInfo = new CPathsInfo(int3(gs->map->width, gs->map->height, gs->map->twoLevel+1));
 		CGI->mh->init();
 		CGI->mh->init();
 		tlog0 <<"Initing maphandler: "<<tmh.getDif()<<std::endl;
 		tlog0 <<"Initing maphandler: "<<tmh.getDif()<<std::endl;
 	}
 	}
@@ -344,6 +347,7 @@ void CClient::newGame( CConnection *con, StartInfo *si )
 	CGI->mh->map = mapa;
 	CGI->mh->map = mapa;
 	tlog0 <<"Creating mapHandler: "<<tmh.getDif()<<std::endl;
 	tlog0 <<"Creating mapHandler: "<<tmh.getDif()<<std::endl;
 	CGI->mh->init();
 	CGI->mh->init();
+	pathInfo = new CPathsInfo(int3(mapa->width, mapa->height, mapa->twoLevel+1));
 	tlog0 <<"Initializing mapHandler (together): "<<tmh.getDif()<<std::endl;
 	tlog0 <<"Initializing mapHandler (together): "<<tmh.getDif()<<std::endl;
 
 
 	for (size_t i=0; i<gs->scenarioOps->playerInfos.size();++i) //initializing interfaces for players
 	for (size_t i=0; i<gs->scenarioOps->playerInfos.size();++i) //initializing interfaces for players

+ 3 - 0
client/Client.h

@@ -25,6 +25,8 @@ class CCallback;
 struct BattleAction;
 struct BattleAction;
 struct SharedMem;
 struct SharedMem;
 class CClient;
 class CClient;
+struct CPathsInfo;
+
 void processCommand(const std::string &message, CClient *&client);
 void processCommand(const std::string &message, CClient *&client);
 namespace boost
 namespace boost
 {
 {
@@ -62,6 +64,7 @@ public:
 	bool must_close;
 	bool must_close;
 	SharedMem *shared;
 	SharedMem *shared;
 	BattleAction *curbaction;
 	BattleAction *curbaction;
+	CPathsInfo *pathInfo;
 
 
 	CondSh<bool> waitingRequest;
 	CondSh<bool> waitingRequest;
 
 

+ 20 - 1
client/NetPacksClient.cpp

@@ -485,7 +485,17 @@ void EndAction::applyCl( CClient *cl )
 
 
 void PackageApplied::applyCl( CClient *cl )
 void PackageApplied::applyCl( CClient *cl )
 {
 {
-	INTERFACE_CALL_IF_PRESENT(GS(cl)->currentPlayer,requestRealized,this);
+	ui8 player = GS(cl)->currentPlayer;
+
+	if(packType == typeList.getTypeID((MoveHero*)NULL))
+	{
+		//we've finished moving hero - paths info must be updated
+		const CGHeroInstance *h = cl->IGameCallback::getSelectedHero(player);
+		if(h)
+			GS(cl)->calculatePaths(h, *cl->pathInfo);
+	}
+
+	INTERFACE_CALL_IF_PRESENT(player, requestRealized, this);
 	if(cl->waitingRequest.get())
 	if(cl->waitingRequest.get())
 		cl->waitingRequest.setn(false);
 		cl->waitingRequest.setn(false);
 }
 }
@@ -526,6 +536,15 @@ void PlayerMessage::applyCl(CClient *cl)
 		LOCPLINT->cingconsole->print(str.str());
 		LOCPLINT->cingconsole->print(str.str());
 }
 }
 
 
+void SetSelection::applyCl(CClient *cl)
+{
+	const CGHeroInstance *h = cl->getHero(id);
+	if(!h)
+		return;
+
+	CPackForClient::GS(cl)->calculatePaths(h, *cl->pathInfo);
+}
+
 void ShowInInfobox::applyCl(CClient *cl)
 void ShowInInfobox::applyCl(CClient *cl)
 {
 {
 	SComponent sc(c);
 	SComponent sc(c);

+ 205 - 149
lib/CGameState.cpp

@@ -1580,7 +1580,7 @@ void CGameState::loadTownDInfos()
 	}
 	}
 }
 }
 
 
-void CGameState::getNeighbours(int3 tile, std::vector<int3> &vec, const boost::logic::tribool &onLand)
+void CGameState::getNeighbours( const TerrainTile &srct, int3 tile, std::vector<int3> &vec, const boost::logic::tribool &onLand )
 {
 {
 	static int3 dirs[] = { int3(0,1,0),int3(0,-1,0),int3(-1,0,0),int3(+1,0,0),
 	static int3 dirs[] = { int3(0,1,0),int3(0,-1,0),int3(-1,0,0),int3(+1,0,0),
 					int3(1,1,0),int3(-1,1,0),int3(1,-1,0),int3(-1,-1,0) };
 					int3(1,1,0),int3(-1,1,0),int3(1,-1,0),int3(-1,-1,0) };
@@ -1588,19 +1588,21 @@ void CGameState::getNeighbours(int3 tile, std::vector<int3> &vec, const boost::l
 	vec.clear();
 	vec.clear();
 	for (size_t i = 0; i < ARRAY_COUNT(dirs); i++)
 	for (size_t i = 0; i < ARRAY_COUNT(dirs); i++)
 	{
 	{
-		int3 hlp = tile + dirs[i];
+		const int3 hlp = tile + dirs[i];
 		if(!map->isInTheMap(hlp)) 
 		if(!map->isInTheMap(hlp)) 
 			continue;
 			continue;
 
 
-		if((indeterminate(onLand)  ||  onLand == (map->getTile(hlp).tertype!=8) ) 
-			&& map->getTile(hlp).tertype!=9) 
+		const TerrainTile &hlpt = map->getTile(hlp);
+
+		if((indeterminate(onLand)  ||  onLand == (hlpt.tertype!=8) ) 
+			&& hlpt.tertype!=9) 
 		{
 		{
 			vec.push_back(hlp);
 			vec.push_back(hlp);
 		}
 		}
 	}
 	}
 }
 }
 
 
-int CGameState::getMovementCost(const CGHeroInstance *h, int3 src, int3 dest, int remainingMovePoints, bool checkLast)
+int CGameState::getMovementCost(const CGHeroInstance *h, const int3 &src, const int3 &dest, int remainingMovePoints, bool checkLast)
 {
 {
 	if(src == dest) //same tile
 	if(src == dest) //same tile
 		return 0;
 		return 0;
@@ -1616,7 +1618,7 @@ int CGameState::getMovementCost(const CGHeroInstance *h, int3 src, int3 dest, in
 		int old = ret;
 		int old = ret;
 		ret *= 1.414213;
 		ret *= 1.414213;
 		//diagonal move costs too much but normal move is possible - allow diagonal move for remaining move points
 		//diagonal move costs too much but normal move is possible - allow diagonal move for remaining move points
-		if(ret > remainingMovePoints  &&  remainingMovePoints > old)
+		if(ret > remainingMovePoints  &&  remainingMovePoints >= old)
 		{
 		{
 			return remainingMovePoints;
 			return remainingMovePoints;
 		}
 		}
@@ -1627,7 +1629,7 @@ int CGameState::getMovementCost(const CGHeroInstance *h, int3 src, int3 dest, in
 	if(checkLast  &&  left > 0  &&  remainingMovePoints-ret < 250) //it might be the last tile - if no further move possible we take all move points
 	if(checkLast  &&  left > 0  &&  remainingMovePoints-ret < 250) //it might be the last tile - if no further move possible we take all move points
 	{
 	{
 		std::vector<int3> vec;
 		std::vector<int3> vec;
-		getNeighbours(dest, vec, s.tertype != TerrainTile::water);
+		getNeighbours(d, dest, vec, s.tertype != TerrainTile::water);
 		for(size_t i=0; i < vec.size(); i++)
 		for(size_t i=0; i < vec.size(); i++)
 		{
 		{
 			int fcost = getMovementCost(h,dest,vec[i],left,false);
 			int fcost = getMovementCost(h,dest,vec[i],left,false);
@@ -1818,7 +1820,7 @@ bool CGameState::getPath(int3 src, int3 dest, const CGHeroInstance * hero, CPath
 		}
 		}
 
 
 		//add accessible neighbouring nodes to the queue
 		//add accessible neighbouring nodes to the queue
-		getNeighbours(cp.coord, neighbours, boost::logic::indeterminate);
+		getNeighbours(map->getTile(cp.coord), cp.coord, neighbours, boost::logic::indeterminate);
 		for(unsigned int i=0; i < neighbours.size(); i++)
 		for(unsigned int i=0; i < neighbours.size(); i++)
 		{
 		{
 			CPathNode & dp = graph[neighbours[i].x][neighbours[i].y];
 			CPathNode & dp = graph[neighbours[i].x][neighbours[i].y];
@@ -1852,146 +1854,144 @@ bool CGameState::getPath(int3 src, int3 dest, const CGHeroInstance * hero, CPath
 	return true;
 	return true;
 }
 }
 
 
-//void CGameState::calculatePaths(const CGHeroInstance *hero, CPathsInfo &out, const int3 &src)
-//{
-//	if(!map->isInTheMap(src)/* || !map->isInTheMap(dest)*/) //check input
-//		//todo: distater
-//		return;
-//
-//	int3 hpos = hero->getPosition(false);
-//	tribool blockLandSea; //true - blocks sea, false - blocks land, indeterminate - allows all
-//
-//	if (!hero->canWalkOnSea())
-//		blockLandSea = (map->getTile(hpos).tertype != TerrainTile::water); //block land if hero is on water and vice versa
-//	else
-//		blockLandSea = boost::logic::indeterminate;
-//
-//	const std::vector<std::vector<std::vector<ui8> > > &FoW = getPlayer(hero->tempOwner)->fogOfWarMap;
-//
-//	//graph initialization
-//	CGPathNode ***graph = out.nodes;
-//	for(size_t i=0; i < out.sizes.x; ++i)
-//	{
-//		for(size_t j=0; j < out.sizes.y; ++j)
-//		{
-//			for(size_t k=0; k < out.sizes.z; ++k)
-//			{
-//				const TerrainTile *tinfo = &map->terrain[i][j][k];
-//				CGPathNode &node = graph[i][j][k];
-//
-//				node.accessible = (tinfo->blocked ? CGPathNode::BLOCKED : CGPathNode::ACCESSIBLE);
-//				node.visited = false;
-//				node.turns = 0xff;
-//				node.moveRemains = 0;
-//				node.coord.x = i;
-//				node.coord.y = j;
-//				node.coord.z = k;
-//				node.land = tinfo->tertype == TerrainTile::water;
-//
-//				if ((tinfo->tertype == TerrainTile::rock) //it's rock
-//					|| ((blockLandSea) && () //it's sea and we cannot walk on sea
-//					|| ((!blockLandSea) && (tinfo->tertype != TerrainTile::water)) //it's land and we cannot walk on land
-//					|| !FoW[i][j][k] //tile is covered by the FoW
-//				)
-//				{
-//					node.accessible = CGPathNode::BLOCKED;
-//				}
-//				else if(tinfo->visitable)
-//				{
-//					for(size_t ii = 0; ii < tinfo->visitableObjects.size(); ii++)
-//					{
-//						if(tinfo->visitableObjects[ii]->blockVisit)
-//						{
-//							node.accessible = CGPathNode::BLOCKVIS;
-//							break;
-//						}
-//						else
-//							node.accessible = CGPathNode::VISITABLE;
-//					}
-//				}
-//
-//				if(blockLandSea && tinfo->tertype == TerrainTile::water) //hero can walk only on land and tile lays on the water
-//				{
-//					size_t i = 0;
-//					for(; i < tinfo->visitableObjects.size(); i++)
-//						if(tinfo->visitableObjects[i]->ID == 8  ||  tinfo->visitableObjects[i]->ID == HEROI_TYPE) //it's a Boat
-//							break;
-//
-//					if(i < tinfo->visitableObjects.size())
-//						node.accessible = CGPathNode::BLOCKVIS; //dest is accessible only if there is boat/hero
-//				}
-//				else if(!blockLandSea && tinfo->tertype != TerrainTile::water) //hero is moving by water
-//				{
-//					if((tinfo->siodmyTajemniczyBajt & 64) && !tinfo->blocked)
-//						node.accessible = CGPathNode::ACCESSIBLE; //tile is accessible if it's coastal and not blocked
-//				}
-//			}
-//		}
-//	}
-//	//graph initialized
-//
-//
-//	//initial tile - set cost on 0 and add to the queue
-//	graph[src.x][src.y][src.z].turns = 0; 
-//	graph[src.x][src.y][src.z].moveRemains = hero->movement;
-//	std::queue<CGPathNode*> mq;
-//	mq.push(&graph[src.x][src.y][src.z]);
-//
-//	ui32 curDist = 0xffffffff; //total cost of path - init with max possible val
-//
-//	std::vector<int3> neighbours;
-//	neighbours.reserve(8);
-//
-//	while(!mq.empty())
-//	{
-//		CGPathNode *cp = graph[mq.front()->coord.x][mq.front()->coord.y];
-//		mq.pop();
-//
-//		//add accessible neighbouring nodes to the queue
-//		getNeighbours(cp->coord, neighbours, boost::logic::indeterminate);
-//		for(unsigned int i=0; i < neighbours.size(); i++)
-//		{
-//			const int3 &n = neighbours[i]; //current neighbour
-//			CGPathNode & dp = graph[n.x][n.y][n.z];
-//			if(!cp->moveRemains)
-//			{
-//				cp->turns++;
-//				cp->moveRemains = hero->maxMovePoints(
-//			}
-//
-//
-//			if(dp.accessible != CGPathNode::BLOCKVIS)
-//			{
-//				int cost = getMovementCost(hero,cp->coord,dp.coord,hero->movement - cp->dist);
-//				if((dp.turns==0xff || (dp.dist > cp->dist + cost)) && dp.accesible && checkForVisitableDir(cp->coord, dp.coord) && checkForVisitableDir(dp.coord, cp->coord))
-//				{
-//					dp.moveRemains = cp.moveRemains - cost;
-//					dp.theNodeBefore = &cp;
-//					if(dp.accessible == CGPathNode::ACCESSIBLE)
-//					{
-//						mq.push(dp);
-//					}
-//				}
-//			}
-//		}
-//	}
-//
-//	CPathNode *curNode = &graph[dest.x][dest.y];
-//	if(!curNode->theNodeBefore) //destination is not accessible
-//		return false;
-//
-//
-//	//fill ret with found path
-//	ret.nodes.clear();
-//	while(curNode->coord != graph[src.x][src.y].coord)
-//	{
-//		ret.nodes.push_back(*curNode);
-//		curNode = curNode->theNodeBefore;
-//	}
-//	ret.nodes.push_back(graph[src.x][src.y]);
-//
-//	return true;
-//}
+void CGameState::calculatePaths(const CGHeroInstance *hero, CPathsInfo &out, int3 src, int movement)
+{
+	if(src.x < 0)
+		src = hero->getPosition(false);
+	if(movement < 0)
+		movement = hero->movement;
+
+	if(!map->isInTheMap(src)/* || !map->isInTheMap(dest)*/) //check input
+	{
+		tlog1 << "CGameState::calculatePaths: Hero outside the map? How dare you...\n";
+		return;
+	}
+
+	tribool onLand; //true - blocks sea, false - blocks land, indeterminate - allows all
+
+	if (!hero->canWalkOnSea())
+		onLand = (map->getTile(src).tertype != TerrainTile::water); //block land if hero is on water and vice versa
+	else
+		onLand = boost::logic::indeterminate;
+
+	const std::vector<std::vector<std::vector<ui8> > > &FoW = getPlayer(hero->tempOwner)->fogOfWarMap;
+
+	//graph initialization
+	CGPathNode ***graph = out.nodes;
+	for(size_t i=0; i < out.sizes.x; ++i)
+	{
+		for(size_t j=0; j < out.sizes.y; ++j)
+		{
+			for(size_t k=0; k < out.sizes.z; ++k)
+			{
+				const TerrainTile *tinfo = &map->terrain[i][j][k];
+				CGPathNode &node = graph[i][j][k];
+
+				node.accessible = (tinfo->blocked ? CGPathNode::BLOCKED : CGPathNode::ACCESSIBLE);
+				node.turns = 0xff;
+				node.moveRemains = 0;
+				node.coord.x = i;
+				node.coord.y = j;
+				node.coord.z = k;
+				node.land = tinfo->tertype != TerrainTile::water;
+
+				if ( tinfo->tertype == TerrainTile::rock//it's rock
+					|| onLand  && !node.land		//it's sea and we cannot walk on sea
+					|| !onLand && node.land		//it's land and we cannot walk on land
+					|| !FoW[i][j][k]					//tile is covered by the FoW
+				)
+				{
+					node.accessible = CGPathNode::BLOCKED;
+				}
+				else if(tinfo->visitable)
+				{
+					for(size_t ii = 0; ii < tinfo->visitableObjects.size(); ii++)
+					{
+						if(tinfo->visitableObjects[ii]->blockVisit)
+						{
+							node.accessible = CGPathNode::BLOCKVIS;
+							break;
+						}
+						else
+							node.accessible = CGPathNode::VISITABLE;
+					}
+				}
+
+				if(onLand && !node.land) //hero can walk only on land and tile lays on the water
+				{
+					size_t i = 0;
+					for(; i < tinfo->visitableObjects.size(); i++)
+						if(tinfo->visitableObjects[i]->ID == 8  ||  tinfo->visitableObjects[i]->ID == HEROI_TYPE) //it's a Boat
+							break;
+					if(i < tinfo->visitableObjects.size())
+						node.accessible = CGPathNode::BLOCKVIS; //dest is accessible only if there is boat/hero
+				}
+				else if(!onLand && tinfo->tertype != TerrainTile::water) //hero is moving by water
+				{
+					if((tinfo->siodmyTajemniczyBajt & 64) && !tinfo->blocked)
+						node.accessible = CGPathNode::ACCESSIBLE; //tile is accessible if it's coastal and not blocked
+				}
+			}
+		}
+	}
+	//graph initialized
+
+
+	//initial tile - set cost on 0 and add to the queue
+	graph[src.x][src.y][src.z].turns = 0; 
+	graph[src.x][src.y][src.z].moveRemains = movement;
+	std::queue<CGPathNode*> mq;
+	mq.push(&graph[src.x][src.y][src.z]);
+
+	ui32 curDist = 0xffffffff; //total cost of path - init with max possible val
+
+	std::vector<int3> neighbours;
+	neighbours.reserve(8);
+
+	while(!mq.empty())
+	{
+		CGPathNode *cp = mq.front();
+		mq.pop();
+
+		const TerrainTile &ct = map->getTile(cp->coord);
+		int movement = cp->moveRemains, turn = cp->turns;
+		if(!movement)
+		{
+			movement = hero->maxMovePoints(ct.tertype != TerrainTile::water);
+			turn++;
+		}
+
+		//add accessible neighbouring nodes to the queue
+		getNeighbours(ct, cp->coord, neighbours, boost::logic::indeterminate);
+		for(unsigned int i=0; i < neighbours.size(); i++)
+		{
+			const int3 &n = neighbours[i]; //current neighbor
+			CGPathNode & dp = graph[n.x][n.y][n.z];
+			if( !checkForVisitableDir(cp->coord, dp.coord) 
+				|| !checkForVisitableDir(dp.coord, cp->coord)
+				|| dp.accessible == CGPathNode::BLOCKED )
+			{
+				continue;
+			}
+
+			int cost = getMovementCost(hero, cp->coord, dp.coord, movement);
+			int remains = movement - cost;
+
+			if(dp.turns==0xff		//we haven't been here before
+				|| dp.turns > turn
+				|| (dp.turns >= turn  &&  dp.moveRemains < remains)) //this route is faster
+			{
+				dp.moveRemains = remains;
+				dp.turns = turn;
+				dp.theNodeBefore = cp;
+				if(dp.accessible == CGPathNode::ACCESSIBLE)
+				{
+					mq.push(&dp);
+				}
+			}
+		} //neighbours loop
+	} //queue loop
+}
 
 
 bool CGameState::isVisible(int3 pos, int player)
 bool CGameState::isVisible(int3 pos, int player)
 {
 {
@@ -2022,6 +2022,11 @@ bool CGameState::isVisible( const CGObjectInstance *obj, int player )
 bool CGameState::checkForVisitableDir(const int3 & src, const int3 & dst) const
 bool CGameState::checkForVisitableDir(const int3 & src, const int3 & dst) const
 {
 {
 	const TerrainTile * pom = &map->getTile(dst);
 	const TerrainTile * pom = &map->getTile(dst);
+	return checkForVisitableDir(src, pom, dst);
+}
+
+bool CGameState::checkForVisitableDir( const int3 & src, const TerrainTile *pom, const int3 & dst ) const
+{
 	for(unsigned int b=0; b<pom->visitableObjects.size(); ++b) //checking destination tile
 	for(unsigned int b=0; b<pom->visitableObjects.size(); ++b) //checking destination tile
 	{
 	{
 		if(!vstd::contains(pom->blockingObjects, pom->visitableObjects[b])) //this visitable object is not blocking, ignore
 		if(!vstd::contains(pom->blockingObjects, pom->visitableObjects[b])) //this visitable object is not blocking, ignore
@@ -2063,7 +2068,6 @@ bool CGameState::checkForVisitableDir(const int3 & src, const int3 & dst) const
 	}
 	}
 	return true;
 	return true;
 }
 }
-
 std::pair<ui32, ui32> BattleInfo::calculateDmgRange(const CStack* attacker, const CStack* defender, const CGHeroInstance * attackerHero, const CGHeroInstance * defendingHero, bool shooting, ui8 charge)
 std::pair<ui32, ui32> BattleInfo::calculateDmgRange(const CStack* attacker, const CStack* defender, const CGHeroInstance * attackerHero, const CGHeroInstance * defendingHero, bool shooting, ui8 charge)
 {
 {
 	int attackDefenseBonus,
 	int attackDefenseBonus,
@@ -2574,3 +2578,55 @@ int3 CPath::endPos() const
 {
 {
 	return nodes[0].coord;
 	return nodes[0].coord;
 }
 }
+
+CGPathNode::CGPathNode()
+:coord(-1,-1,-1)
+{
+	accessible = 0;
+	land = 0;
+	moveRemains = 0;
+	turns = 255;
+	theNodeBefore = NULL;
+}
+
+bool CPathsInfo::getPath( const int3 &dst, CGPath &out )
+{
+	out.nodes.clear();
+	const CGPathNode *curnode = &nodes[dst.x][dst.y][dst.z];
+	if(!curnode->theNodeBefore)
+		return false;
+
+	while(curnode->theNodeBefore)
+	{
+		out.nodes.push_back(*curnode);
+		curnode = curnode->theNodeBefore;
+	}
+	return true;
+}
+
+CPathsInfo::CPathsInfo( const int3 &Sizes )
+:sizes(Sizes)
+{
+	nodes = new CGPathNode**[sizes.x];
+	for(int i = 0; i < sizes.x; i++)
+	{
+		nodes[i] = new CGPathNode*[sizes.y];
+		for (int j = 0; j < sizes.y; j++)
+		{
+			nodes[i][j] = new CGPathNode[sizes.z];
+		}
+	}
+}
+
+CPathsInfo::~CPathsInfo()
+{
+	for(int i = 0; i < sizes.x; i++)
+	{
+		for (int j = 0; j < sizes.y; j++)
+		{
+			delete [] nodes[i][j];
+		}
+		delete [] nodes[i];
+	}
+	delete [] nodes;
+}

+ 22 - 12
lib/CGameState.h

@@ -48,7 +48,7 @@ struct SetObjectProperty;
 struct MetaString;
 struct MetaString;
 struct CPack;
 struct CPack;
 class CSpell;
 class CSpell;
-
+struct TerrainTile;
 
 
 namespace boost
 namespace boost
 {
 {
@@ -244,13 +244,13 @@ struct CPathNode
 
 
 struct CGPathNode
 struct CGPathNode
 {
 {
-	enum {ACCESSIBLE=1, VISITABLE, BLOCKVIS, BLOCKED}; //BLOCKVIS - visitable from neighbourign tile but not passable
-	ui8 land;
+	enum {ACCESSIBLE=1, VISITABLE, BLOCKVIS, BLOCKED}; //BLOCKVIS - visitable from neighbouring tile but not passable
 	ui8 accessible; //the enum above
 	ui8 accessible; //the enum above
+	ui8 land;
 	ui8 turns;
 	ui8 turns;
 	ui32 moveRemains;
 	ui32 moveRemains;
-	CPathNode * theNodeBefore;
-	int3 coord; //coordiantes
+	CGPathNode * theNodeBefore;
+	int3 coord; //coordinates
 	CGPathNode();
 	CGPathNode();
 };
 };
 
 
@@ -264,13 +264,22 @@ struct DLL_EXPORT CPath
 	void convert(ui8 mode); //mode=0 -> from 'manifest' to 'object'
 	void convert(ui8 mode); //mode=0 -> from 'manifest' to 'object'
 };
 };
 
 
-struct CPathsInfo
+struct DLL_EXPORT CGPath
+{
+	std::vector<CGPathNode> nodes; //just get node by node
+
+	int3 startPos() const; // start point
+	int3 endPos() const; //destination point
+	void convert(ui8 mode); //mode=0 -> from 'manifest' to 'object'
+};
+
+struct DLL_EXPORT CPathsInfo
 {
 {
 	int3 sizes;
 	int3 sizes;
 	CGPathNode ***nodes; //[w][h][level]
 	CGPathNode ***nodes; //[w][h][level]
 
 
-	void getPath(const int3 &src, const int3 &dst, CPath &out);
-	CPathsInfo(const int3 &sizes);
+	bool getPath(const int3 &dst, CGPath &out);
+	CPathsInfo(const int3 &Sizes);
 	~CPathsInfo();
 	~CPathsInfo();
 };
 };
 
 
@@ -321,17 +330,18 @@ public:
 	UpgradeInfo getUpgradeInfo(CArmedInstance *obj, int stackPos);
 	UpgradeInfo getUpgradeInfo(CArmedInstance *obj, int stackPos);
 	float getMarketEfficiency(int player, int mode=0);
 	float getMarketEfficiency(int player, int mode=0);
 	int canBuildStructure(const CGTownInstance *t, int ID);// 0 - no more than one capitol, 1 - lack of water, 2 - forbidden, 3 - Add another level to Mage Guild, 4 - already built, 5 - cannot build, 6 - cannot afford, 7 - build, 8 - lack of requirements
 	int canBuildStructure(const CGTownInstance *t, int ID);// 0 - no more than one capitol, 1 - lack of water, 2 - forbidden, 3 - Add another level to Mage Guild, 4 - already built, 5 - cannot build, 6 - cannot afford, 7 - build, 8 - lack of requirements
-	bool checkForVisitableDir(const int3 & src, const int3 & dst) const; //check if dst tile is visitable from dst tile
+	bool checkForVisitableDir(const int3 & src, const int3 & dst) const; //check if src tile is visitable from dst tile
+	bool checkForVisitableDir(const int3 & src, const TerrainTile *pom, const int3 & dst) const; //check if src tile is visitable from dst tile
 	bool getPath(int3 src, int3 dest, const CGHeroInstance * hero, CPath &ret); //calculates path between src and dest; returns pointer to newly allocated CPath or NULL if path does not exists
 	bool getPath(int3 src, int3 dest, const CGHeroInstance * hero, CPath &ret); //calculates path between src and dest; returns pointer to newly allocated CPath or NULL if path does not exists
-	void calculatePaths(const CGHeroInstance *hero, CPathsInfo &out, const int3 &src = int3(-1,-1,-1)); //calculates path between src and dest; returns pointer to newly allocated CPath or NULL if path does not exists
+	void calculatePaths(const CGHeroInstance *hero, CPathsInfo &out, int3 src = int3(-1,-1,-1), int movement = -1); //calculates possible paths for hero, by default uses current hero position and movement left; returns pointer to newly allocated CPath or NULL if path does not exists
 
 
 	bool isVisible(int3 pos, int player);
 	bool isVisible(int3 pos, int player);
 	bool isVisible(const CGObjectInstance *obj, int player);
 	bool isVisible(const CGObjectInstance *obj, int player);
 
 
 	CGameState(); //c-tor
 	CGameState(); //c-tor
 	~CGameState(); //d-tor
 	~CGameState(); //d-tor
-	void getNeighbours(int3 tile, std::vector<int3> &vec, const boost::logic::tribool &onLand);
-	int getMovementCost(const CGHeroInstance *h, int3 src, int3 dest, int remainingMovePoints=-1, bool checkLast=true);
+	void getNeighbours(const TerrainTile &srct, int3 tile, std::vector<int3> &vec, const boost::logic::tribool &onLand);
+	int getMovementCost(const CGHeroInstance *h, const int3 &src, const int3 &dest, int remainingMovePoints=-1, bool checkLast=true);
 	int getDate(int mode=0) const; //mode=0 - total days in game, mode=1 - day of week, mode=2 - current week, mode=3 - current month
 	int getDate(int mode=0) const; //mode=0 - total days in game, mode=1 - day of week, mode=2 - current week, mode=3 - current month
 	template <typename Handler> void serialize(Handler &h, const int version)
 	template <typename Handler> void serialize(Handler &h, const int version)
 	{
 	{

+ 1 - 0
lib/NetPacks.h

@@ -1302,6 +1302,7 @@ struct SetSelection : public CPackForClient, public CPackForServer //514
 	SetSelection(){CPackForClient::type = 514;};
 	SetSelection(){CPackForClient::type = 514;};
 	DLL_EXPORT void applyGs(CGameState *gs);
 	DLL_EXPORT void applyGs(CGameState *gs);
 	bool applyGh(CGameHandler *gh);
 	bool applyGh(CGameHandler *gh);
+	void applyCl(CClient *cl);
 
 
 	ui8 player;
 	ui8 player;
 	ui32 id;
 	ui32 id;