mirror of
				https://github.com/vcmi/vcmi.git
				synced 2025-10-31 00:07:39 +02:00 
			
		
		
		
	Work on pathfinder: torn the code out from CGameState into a separate class. It can use Subterranean Gates and Boats. Removed code for handling Fly spell effect. It didn't work as supposed anyway.
This commit is contained in:
		| @@ -572,17 +572,24 @@ void CTerrainRect::showPath(const SDL_Rect * extRect, SDL_Surface * to) | ||||
|  | ||||
| 	for (size_t i=0; i < currentPath->nodes.size()-1; ++i) | ||||
| 	{ | ||||
| 		const int3 &curPos = currentPath->nodes[i].coord, &nextPos = currentPath->nodes[i+1].coord; | ||||
| 		if(curPos.z != adventureInt->position.z) | ||||
| 			continue; | ||||
|  | ||||
| 		int pn=-1;//number of picture | ||||
| 		if (i==0) //last tile | ||||
| 		{ | ||||
| 			int x = 32*(currentPath->nodes[i].coord.x-adventureInt->position.x)+CGI->mh->offsetX + pos.x, | ||||
| 				y = 32*(currentPath->nodes[i].coord.y-adventureInt->position.y)+CGI->mh->offsetY + pos.y; | ||||
| 			int x = 32*(curPos.x-adventureInt->position.x)+CGI->mh->offsetX + pos.x, | ||||
| 				y = 32*(curPos.y-adventureInt->position.y)+CGI->mh->offsetY + pos.y; | ||||
| 			if (x<0 || y<0 || x>pos.w || y>pos.h) | ||||
| 				continue; | ||||
| 			pn=0; | ||||
| 		} | ||||
| 		else | ||||
| 		{ | ||||
| 			const int3 &prevPos = currentPath->nodes[i-1].coord; | ||||
| 			std::vector<CGPathNode> & cv = currentPath->nodes; | ||||
|  | ||||
| 			/* Vector directions | ||||
| 			 *  0   1   2 | ||||
| 			 *    \ | /  | ||||
| @@ -595,20 +602,25 @@ void CTerrainRect::showPath(const SDL_Rect * extRect, SDL_Surface * to) | ||||
| 			 *     / | ||||
| 			 * is id1=7, id2=5 (pns[7][5]) | ||||
| 			*/  | ||||
| 			std::vector<CGPathNode> & cv = currentPath->nodes; | ||||
| 			int id1=(cv[i].coord.x-cv[i+1].coord.x+1)+3*(cv[i].coord.y-cv[i+1].coord.y+1);   //Direction of entering vector | ||||
| 			int id2=(cv[i-1].coord.x-cv[i].coord.x+1)+3*(cv[i-1].coord.y-cv[i].coord.y+1); //Direction of exiting vector | ||||
|  | ||||
| 			pn=pns[id1][id2]; | ||||
|  | ||||
| 			bool pathContinuous = curPos.areNeighbours(nextPos) && curPos.areNeighbours(prevPos); | ||||
| 			if(pathContinuous && cv[i].land == cv[i+1].land) | ||||
| 			{ | ||||
| 				int id1=(curPos.x-nextPos.x+1)+3*(curPos.y-nextPos.y+1);   //Direction of entering vector | ||||
| 				int id2=(cv[i-1].coord.x-curPos.x+1)+3*(cv[i-1].coord.y-curPos.y+1); //Direction of exiting vector | ||||
| 				pn=pns[id1][id2]; | ||||
| 			} | ||||
| 			else //path discontinuity or sea/land transition (eg. when moving through Subterranean Gate or Boat) | ||||
| 			{ | ||||
| 				pn = 0; | ||||
| 			} | ||||
| 		} | ||||
| 		if (currentPath->nodes[i].turns) | ||||
| 			pn+=25; | ||||
| 		if (pn>=0) | ||||
| 		{ | ||||
| 			CDefEssential * arrows = graphics->heroMoveArrows; | ||||
| 			int x = 32*(currentPath->nodes[i].coord.x-adventureInt->position.x)+CGI->mh->offsetX + pos.x, | ||||
| 				y = 32*(currentPath->nodes[i].coord.y-adventureInt->position.y)+CGI->mh->offsetY + pos.y; | ||||
| 			int x = 32*(curPos.x-adventureInt->position.x)+CGI->mh->offsetX + pos.x, | ||||
| 				y = 32*(curPos.y-adventureInt->position.y)+CGI->mh->offsetY + pos.y; | ||||
| 			if (x<0 || y<0 || x>pos.w || y>pos.h) | ||||
| 				continue; | ||||
| 			int hvx = (x+arrows->ourImages[pn].bitmap->w)-(pos.x+pos.w), | ||||
| @@ -690,7 +702,7 @@ void CTerrainRect::show(SDL_Surface * to) | ||||
| 	 | ||||
| 	//SDL_BlitSurface(teren,&genRect(pos.h,pos.w,0,0),screen,&genRect(547,594,7,6)); | ||||
| 	//SDL_FreeSurface(teren); | ||||
| 	if (currentPath && adventureInt->position.z==currentPath->startPos().z) //drawing path | ||||
| 	if (currentPath/* && adventureInt->position.z==currentPath->startPos().z*/) //drawing path | ||||
| 	{ | ||||
| 		showPath(&pos, to); | ||||
| 	} | ||||
| @@ -1655,7 +1667,7 @@ void CAdvMapInt::tileLClicked(const int3 &mp) | ||||
| 				LOCPLINT->moveHero(currentHero,*terrain.currentPath); | ||||
| 				return; | ||||
| 			} | ||||
| 			else if(mp.z == currentHero->pos.z) //remove old path and find a new one if we clicked on the map level on which hero is present | ||||
| 			else/* if(mp.z == currentHero->pos.z)*/ //remove old path and find a new one if we clicked on the map level on which hero is present | ||||
| 			{ | ||||
| 				CGPath &path = LOCPLINT->paths[currentHero]; | ||||
| 				terrain.currentPath = &path; | ||||
| @@ -1840,11 +1852,13 @@ void CAdvMapInt::tileHovered(const int3 &tile) | ||||
| 		}  | ||||
| 		else //no objs  | ||||
| 		{ | ||||
| 			if(accessible && pnode->accessible != CGPathNode::FLYABLE) | ||||
| 			if(accessible/* && pnode->accessible != CGPathNode::FLYABLE*/) | ||||
| 			{ | ||||
| 				if (guardingCreature) { | ||||
| 				if (guardingCreature)  | ||||
| 				{ | ||||
| 					CCS->curh->changeGraphic(0, 5 + turns*6); | ||||
| 				} else { | ||||
| 				} else  | ||||
| 				{ | ||||
| 					if(pnode->land) | ||||
| 					{ | ||||
| 						if(LOCPLINT->cb->getTile(h->getPosition(false))->tertype != TerrainTile::water) | ||||
|   | ||||
| @@ -486,6 +486,12 @@ void MusicEntry::load(musicBase::musicID ID) | ||||
|  | ||||
| 	music = Mix_LoadMUS(filename.c_str()); | ||||
|  | ||||
| 	if(!music) | ||||
| 	{ | ||||
| 		tlog3 << "Warning: Cannot open " << filename << ": " << Mix_GetError() << std::endl; | ||||
| 		return; | ||||
| 	} | ||||
|  | ||||
| #ifdef _WIN32 | ||||
| 	//The assertion will fail if old MSVC libraries pack .dll is used | ||||
| 	assert(Mix_GetMusicType(music) == MUS_MP3_MAD); | ||||
|   | ||||
| @@ -259,25 +259,32 @@ void CPlayerInterface::heroMoved(const TryMoveHero & details) | ||||
| 	{ | ||||
| 		//We may need to change music - select new track, music handler will change it if needed | ||||
| 		CCS->musich->playMusic(CCS->musich->terrainMusics[LOCPLINT->cb->getTile(ho->visitablePos())->tertype]); | ||||
| 		if(details.result == TryMoveHero::TELEPORTATION	||  details.start == details.end) | ||||
|  | ||||
| 		if(details.result == TryMoveHero::TELEPORTATION) | ||||
| 		{ | ||||
| 			if(adventureInt->terrain.currentPath) | ||||
| 				eraseCurrentPathOf(ho); | ||||
| 			return; //teleport - no fancy moving animation | ||||
| 			if(adventureInt->terrain.currentPath->nodes.size() && adventureInt->terrain.currentPath->nodes.back().coord == CGHeroInstance::convertPosition(hp, false)) | ||||
| 				removeLastNodeFromPath(ho); | ||||
| // 			else | ||||
| // 				eraseCurrentPathOf(ho); | ||||
| 			return;	//teleport - no fancy moving animation | ||||
| 					//TODO: smooth disappear / appear effect | ||||
| 		} | ||||
|  | ||||
| 		if ((details.result != TryMoveHero::SUCCESS && details.result != TryMoveHero::FAILED) //hero didn't change tile but visit succeeded | ||||
| 		if(details.start == details.end) //last step | ||||
| 		{ | ||||
| 			eraseCurrentPathOf(ho); | ||||
| 			return;  | ||||
| 		} | ||||
|  | ||||
| 		if (ho->pos != details.end //hero didn't change tile but visit succeeded | ||||
| 			|| directlyAttackingCreature) // or creature was attacked from endangering tile. | ||||
| 		{ | ||||
| 			eraseCurrentPathOf(ho); | ||||
| 		} | ||||
| 		else if(adventureInt->terrain.currentPath  &&  details.result == TryMoveHero::SUCCESS) //&& hero is moving | ||||
| 		else if(adventureInt->terrain.currentPath  &&  ho->pos == details.end) //&& hero is moving | ||||
| 		{ | ||||
| 			//remove one node from the path (the one we went) | ||||
| 			adventureInt->terrain.currentPath->nodes.erase(adventureInt->terrain.currentPath->nodes.end()-1); | ||||
| 			if(!adventureInt->terrain.currentPath->nodes.size())  //if it was the last one, remove entire path | ||||
| 				eraseCurrentPathOf(ho); | ||||
| 			removeLastNodeFromPath(ho); | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| @@ -1127,6 +1134,10 @@ bool CPlayerInterface::moveHero( const CGHeroInstance *h, CGPath path ) | ||||
|  | ||||
| 		for(int i=path.nodes.size()-1; i>0 && (stillMoveHero.data == CONTINUE_MOVE || curTile->blocked); i--) | ||||
| 		{ | ||||
| 			//changing z coordinate means we're moving through subterranean gate -> it's done automatically upon the visit, so we don't have to request that move here | ||||
| 			if(path.nodes[i-1].coord.z != path.nodes[i].coord.z) | ||||
| 				continue; | ||||
|  | ||||
| 			//stop sending move requests if the next node can't be reached at the current turn (hero exhausted his move points) | ||||
| 			if(path.nodes[i-1].turns) | ||||
| 			{ | ||||
| @@ -1143,7 +1154,8 @@ bool CPlayerInterface::moveHero( const CGHeroInstance *h, CGPath path ) | ||||
| 			{ | ||||
| 				newTerrain = cb->getTile(CGHeroInstance::convertPosition(path.nodes[i].coord, false))->tertype; | ||||
|  | ||||
| 				if (newTerrain != currentTerrain) { | ||||
| 				if (newTerrain != currentTerrain)  | ||||
| 				{ | ||||
| 					CCS->soundh->stopSound(sh); | ||||
| 					sh = CCS->soundh->playSound(CCS->soundh->horseSounds[newTerrain], -1); | ||||
| 					currentTerrain = newTerrain; | ||||
| @@ -2024,6 +2036,13 @@ void CPlayerInterface::eraseCurrentPathOf( const CGHeroInstance * ho, bool check | ||||
| 	adventureInt->terrain.currentPath = NULL; | ||||
| } | ||||
|  | ||||
| void CPlayerInterface::removeLastNodeFromPath(const CGHeroInstance *ho) | ||||
| { | ||||
| 	adventureInt->terrain.currentPath->nodes.erase(adventureInt->terrain.currentPath->nodes.end()-1); | ||||
| 	if(!adventureInt->terrain.currentPath->nodes.size())  //if it was the last one, remove entire path | ||||
| 		eraseCurrentPathOf(ho); | ||||
| } | ||||
|  | ||||
| CGPath * CPlayerInterface::getAndVerifyPath(const CGHeroInstance * h) | ||||
| { | ||||
| 	if(vstd::contains(paths,h)) //hero has assigned path | ||||
|   | ||||
| @@ -256,6 +256,7 @@ public: | ||||
| 	void movementPxStep( const TryMoveHero &details, int i, const int3 &hp, const CGHeroInstance * ho );//performing step of movement | ||||
| 	void finishMovement( const TryMoveHero &details, const int3 &hp, const CGHeroInstance * ho ); //finish movement | ||||
| 	void eraseCurrentPathOf( const CGHeroInstance * ho, bool checkForExistanceOfPath = true ); | ||||
| 	void removeLastNodeFromPath(const CGHeroInstance *ho); | ||||
| 	CGPath *getAndVerifyPath( const CGHeroInstance * h ); | ||||
| 	void acceptTurn(); //used during hot seat after your turn message is close | ||||
| 	void tryDiggging(const CGHeroInstance *h); | ||||
|   | ||||
| @@ -1059,7 +1059,7 @@ void CMapHandler::getTerrainDescr( const int3 &pos, std::string & out, bool terN | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	if(t.siodmyTajemniczyBajt & 128) | ||||
| 	if(t.hasFavourableWinds()) | ||||
| 		out = CGI->generaltexth->names[225]; //Favourable Winds | ||||
| 	else if(terName) | ||||
| 		out = CGI->generaltexth->terrainNames[t.tertype]; | ||||
|   | ||||
							
								
								
									
										5
									
								
								global.h
									
									
									
									
									
								
							
							
						
						
									
										5
									
								
								global.h
									
									
									
									
									
								
							| @@ -104,8 +104,11 @@ typedef si32 TQuantity; | ||||
| typedef ui32 TCreature; //creature id | ||||
| const int ARMY_SIZE = 7; | ||||
|  | ||||
| const int HEROI_TYPE = 34,  | ||||
| const int  | ||||
| 	BOATI_TYPE = 8, | ||||
| 	HEROI_TYPE = 34,  | ||||
| 	TOWNI_TYPE = 98, | ||||
| 	SUBTERRANEAN_GATE_TYPE = 103, | ||||
| 	CREI_TYPE = 54, | ||||
| 	EVENTI_TYPE = 26; | ||||
|  | ||||
|   | ||||
							
								
								
									
										2
									
								
								int3.h
									
									
									
									
									
								
							
							
						
						
									
										2
									
								
								int3.h
									
									
									
									
									
								
							| @@ -35,6 +35,8 @@ public: | ||||
| 		{return int3(-x,-y,-z);} | ||||
| 	inline double dist2d(const int3 other) const //distance (z coord is not used) | ||||
| 		{return std::sqrt((double)(x-other.x)*(x-other.x) + (y-other.y)*(y-other.y));} | ||||
| 	inline bool areNeighbours(const int3 other) const | ||||
| 		{return dist2d(other) < 2. && z == other.z;} | ||||
| 	inline void operator+=(const int3 & i) | ||||
| 	{ | ||||
| 		x+=i.x; | ||||
|   | ||||
| @@ -1762,10 +1762,9 @@ void CGameState::loadTownDInfos() | ||||
|  | ||||
| void CGameState::getNeighbours(const TerrainTile &srct, int3 tile, std::vector<int3> &vec, const boost::logic::tribool &onLand, bool limitCoastSailing) | ||||
| { | ||||
| 	static int3 dirs[] = { int3(0,1,0),int3(0,-1,0),int3(-1,0,0),int3(+1,0,0), | ||||
| 	static const 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) }; | ||||
|  | ||||
| 	vec.clear(); | ||||
| 	for (size_t i = 0; i < ARRAY_COUNT(dirs); i++) | ||||
| 	{ | ||||
| 		const int3 hlp = tile + dirs[i]; | ||||
| @@ -1774,11 +1773,11 @@ void CGameState::getNeighbours(const TerrainTile &srct, int3 tile, std::vector<i | ||||
|  | ||||
| 		const TerrainTile &hlpt = map->getTile(hlp); | ||||
|  | ||||
| 		//we cannot visit things from blocked tiles | ||||
| 		if(srct.blocked && !srct.visitable && hlpt.visitable && srct.blockingObjects.front()->ID != HEROI_TYPE) | ||||
| 		{ | ||||
| 			continue; | ||||
| 		} | ||||
| // 		//we cannot visit things from blocked tiles | ||||
| // 		if(srct.blocked && !srct.visitable && hlpt.visitable && srct.blockingObjects.front()->ID != HEROI_TYPE) | ||||
| // 		{ | ||||
| // 			continue; | ||||
| // 		} | ||||
|  | ||||
| 		if(srct.tertype == TerrainTile::water && limitCoastSailing && hlpt.tertype == TerrainTile::water && dirs[i].x && dirs[i].y) //diagonal move through water | ||||
| 		{ | ||||
| @@ -1821,7 +1820,7 @@ int CGameState::getMovementCost(const CGHeroInstance *h, const int3 &src, const | ||||
| 	} | ||||
| 	else if (d.tertype == TerrainTile::water) | ||||
| 	{ | ||||
| 		if(h->boat && s.siodmyTajemniczyBajt & 128 && d.siodmyTajemniczyBajt & 128) //Favourable Winds | ||||
| 		if(h->boat && s.hasFavourableWinds() && d.hasFavourableWinds()) //Favourable Winds | ||||
| 			ret *= 0.666f; | ||||
| 		else if (!h->boat && h->getBonusesCount(Selector::typeSubtype(Bonus::WATER_WALKING, 1)) > 0) | ||||
| 			ret *= 1.4f; //40% penalty for water walking | ||||
| @@ -1865,352 +1864,15 @@ void CGameState::apply(CPack *pack) | ||||
|  | ||||
| bool CGameState::getPath(int3 src, int3 dest, const CGHeroInstance * hero, CPath &ret) | ||||
| { | ||||
| 	if(!map->isInTheMap(src) || !map->isInTheMap(dest)) //check input | ||||
| 		return false; | ||||
|  | ||||
| 	int3 hpos = hero->getPosition(false); | ||||
|  | ||||
| 	bool flying = false; //hero is under flying effect	TODO | ||||
| 	bool waterWalking = false; //hero is on land and can walk on water TODO | ||||
| 	bool onLand = map->getTile(hpos).tertype != TerrainTile::water; | ||||
| // 	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 = getPlayerTeam(hero->tempOwner)->fogOfWarMap; | ||||
|  | ||||
| 	//graph initialization | ||||
| 	std::vector< std::vector<CPathNode> > graph; | ||||
| 	graph.resize(map->width); | ||||
| 	for(size_t i=0; i<graph.size(); ++i) | ||||
| 	{ | ||||
| 		graph[i].resize(map->height); | ||||
| 		for(size_t j=0; j<graph[i].size(); ++j) | ||||
| 		{ | ||||
| 			const TerrainTile *tinfo = &map->terrain[i][j][src.z]; | ||||
| 			CPathNode &node = graph[i][j]; | ||||
|  | ||||
| 			node.accessible = !tinfo->blocked; | ||||
| 			node.dist = -1; | ||||
| 			node.theNodeBefore = NULL; | ||||
| 			node.visited = false; | ||||
| 			node.coord.x = i; | ||||
| 			node.coord.y = j; | ||||
| 			node.coord.z = dest.z; | ||||
|  | ||||
| 			if(!tinfo->entrableTerrain(onLand, flying || waterWalking) | ||||
| 				|| !FoW[i][j][src.z] //tile is covered by the FoW | ||||
| 			) | ||||
| 			{ | ||||
| 				node.accessible = false; | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
|  | ||||
| 	//Special rules for the destination tile | ||||
| 	{ | ||||
| 		const TerrainTile *t = &map->terrain[dest.x][dest.y][dest.z]; | ||||
| 		CPathNode &d = graph[dest.x][dest.y]; | ||||
|  | ||||
| 		//tile may be blocked by blockvis / normal vis obj but it still must be accessible | ||||
| 		if(t->visitable)  | ||||
| 		{ | ||||
| 			d.accessible = true; //for allowing visiting objects | ||||
| 		} | ||||
|  | ||||
| 		if(onLand && t->tertype == TerrainTile::water) //hero can walk only on land and dst lays on the water | ||||
| 		{ | ||||
| 			size_t i = 0; | ||||
| 			for(; i < t->visitableObjects.size(); i++) | ||||
| 				if(t->visitableObjects[i]->ID == 8  ||  t->visitableObjects[i]->ID == HEROI_TYPE) //it's a Boat | ||||
| 					break; | ||||
|  | ||||
| 			d.accessible = (i < t->visitableObjects.size()); //dest is accessible only if there is boat/hero | ||||
| 		} | ||||
| 		else if(!onLand && t->tertype != TerrainTile::water) //hero is moving by water | ||||
| 		{ | ||||
| 			d.accessible = (t->siodmyTajemniczyBajt & 64) && !t->blocked; //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].dist = 0;  | ||||
| 	std::queue<CPathNode> mq; | ||||
| 	mq.push(graph[src.x][src.y]); | ||||
|  | ||||
| 	ui32 curDist = 0xffffffff; //total cost of path - init with max possible val | ||||
|  | ||||
| 	std::vector<int3> neighbours; | ||||
| 	neighbours.reserve(8); | ||||
|  | ||||
| 	while(!mq.empty()) | ||||
| 	{ | ||||
| 		CPathNode &cp = graph[mq.front().coord.x][mq.front().coord.y]; | ||||
| 		mq.pop(); | ||||
| 		if (cp.coord == dest) //it's destination tile | ||||
| 		{ | ||||
| 			if (cp.dist < curDist) //that path is better than previous one | ||||
| 				curDist = cp.dist; | ||||
| 			continue; | ||||
| 		} | ||||
| 		else | ||||
| 		{ | ||||
| 			if (cp.dist > curDist) //it's not dest and current length is greater than cost of already found path  | ||||
| 				continue; | ||||
| 		} | ||||
|  | ||||
| 		//add accessible neighbouring nodes to the queue | ||||
| 		getNeighbours(map->getTile(cp.coord), cp.coord, neighbours, boost::logic::indeterminate, true); | ||||
| 		for(unsigned int i=0; i < neighbours.size(); i++) | ||||
| 		{ | ||||
| 			CPathNode & dp = graph[neighbours[i].x][neighbours[i].y]; | ||||
| 			if(dp.accessible) | ||||
| 			{ | ||||
| 				int cost = getMovementCost(hero,cp.coord,dp.coord,hero->movement - cp.dist); | ||||
| 				if((dp.dist==-1 || (dp.dist > cp.dist + cost)) && dp.accessible && checkForVisitableDir(cp.coord, dp.coord) && checkForVisitableDir(dp.coord, cp.coord)) | ||||
| 				{ | ||||
| 					dp.dist = cp.dist + cost; | ||||
| 					dp.theNodeBefore = &cp; | ||||
| 					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; | ||||
| 	//the old pathfinder is not supported anymore! | ||||
| 	assert(0); | ||||
| 	return false; | ||||
| } | ||||
|  | ||||
| void CGameState::calculatePaths(const CGHeroInstance *hero, CPathsInfo &out, int3 src, int movement) | ||||
| { | ||||
| 	assert(hero); | ||||
| 	assert(hero == getHero(hero->id)); | ||||
| 	if(src.x < 0) | ||||
| 		src = hero->getPosition(false); | ||||
| 	if(movement < 0) | ||||
| 		movement = hero->movement; | ||||
|  | ||||
| 	out.hero = hero; | ||||
| 	out.hpos = src; | ||||
|  | ||||
| 	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 = getPlayerTeam(hero->tempOwner)->fogOfWarMap; | ||||
|  | ||||
| 	bool flying = hero->hasBonusOfType(Bonus::FLYING_MOVEMENT); | ||||
| 	bool waterWalk = hero->hasBonusOfType(Bonus::WATER_WALKING); | ||||
|  | ||||
| 	//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::FLYABLE : CGPathNode::ACCESSIBLE); | ||||
| 				if(!flying && node.accessible == CGPathNode::FLYABLE) | ||||
| 				{ | ||||
| 					node.accessible = CGPathNode::BLOCKED; | ||||
| 				} | ||||
| 				node.turns = 0xff; | ||||
| 				node.moveRemains = 0; | ||||
| 				node.coord.x = i; | ||||
| 				node.coord.y = j; | ||||
| 				node.coord.z = k; | ||||
| 				node.land = tinfo->tertype != TerrainTile::water; | ||||
| 				node.theNodeBefore = NULL; | ||||
|  | ||||
| 				bool leaveAsBlocked = false; | ||||
|  | ||||
| 				if((onLand || indeterminate(onLand)) && !node.land)//it's sea and we cannot walk on sea | ||||
| 				{ | ||||
| 					if (waterWalk || flying) | ||||
| 					{ | ||||
| 						node.accessible = CGPathNode::FLYABLE; | ||||
| 					} | ||||
| 					else | ||||
| 					{ | ||||
| 						node.accessible = CGPathNode::BLOCKED; | ||||
| 						leaveAsBlocked = true; | ||||
| 					} | ||||
| 				} | ||||
|  | ||||
| 				if ( tinfo->tertype == TerrainTile::rock//it's rock | ||||
| 					|| (!onLand && node.land)		//it's land and we cannot walk on land (complementary condition is handled above) | ||||
| 					|| !FoW[i][j][k]					//tile is covered by the FoW | ||||
| 					|| leaveAsBlocked | ||||
| 				) | ||||
| 				{ | ||||
| 					node.accessible = CGPathNode::BLOCKED; | ||||
| 				} | ||||
| 				else if(tinfo->visitable) | ||||
| 				{ | ||||
| 					//hero is protected in Sanctuary | ||||
| 					if(tinfo->visitableObjects.front()->ID == 80 && tinfo->visitableObjects.back()->ID == HEROI_TYPE && tinfo->visitableObjects.back()->tempOwner != hero->tempOwner) | ||||
| 						node.accessible = CGPathNode::BLOCKED; | ||||
| 					else | ||||
| 					{ | ||||
| 						for(size_t ii = 0; ii < tinfo->visitableObjects.size(); ii++) | ||||
| 						{ | ||||
| 							const CGObjectInstance * const obj = tinfo->visitableObjects[ii]; | ||||
| 							if(obj->getPassableness() & 1<<hero->tempOwner) //special object instance specific passableness flag - overwrites other accessibility flags | ||||
| 							{ | ||||
| 								node.accessible = CGPathNode::ACCESSIBLE; | ||||
| 							} | ||||
| 							else if(obj->blockVisit) | ||||
| 							{ | ||||
| 								node.accessible = CGPathNode::BLOCKVIS; | ||||
| 								break; | ||||
| 							} | ||||
| 							else if(obj->ID != EVENTI_TYPE) //pathfinder should ignore placed events | ||||
| 							{ | ||||
| 								node.accessible = CGPathNode::VISITABLE; | ||||
| 							} | ||||
| 						} | ||||
| 					} | ||||
| 				} | ||||
| 				else if (map->isInTheMap(guardingCreaturePosition(int3(i, j, k))) | ||||
| 					&& tinfo->blockingObjects.size() == 0) | ||||
| 				{ | ||||
| 					// Monster close by; blocked visit for battle. | ||||
| 					node.accessible = CGPathNode::BLOCKVIS; | ||||
| 				} | ||||
|  | ||||
| 				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::VISITABLE; //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 int3 guardPosition = guardingCreaturePosition(cp->coord); | ||||
| 		const bool guardedPosition = (guardPosition != int3(-1, -1, -1) && cp->coord != src); | ||||
| 		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, !onLand); | ||||
| 		for(unsigned int i=0; i < neighbours.size(); i++) | ||||
| 		{ | ||||
| 			int moveAtNextTile = movement; | ||||
| 			int turnAtNextTile = turn; | ||||
|  | ||||
| 			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(remains < 0) | ||||
| 			{ | ||||
| 				//occurs rarely, when hero with low movepoints tries to go leave the road | ||||
| 				turnAtNextTile++; | ||||
| 				moveAtNextTile = hero->maxMovePoints(ct.tertype != TerrainTile::water); | ||||
| 				cost = getMovementCost(hero, cp->coord, dp.coord, moveAtNextTile); //cost must be updated, movement points changed :( | ||||
| 				remains = moveAtNextTile - cost; | ||||
| 			} | ||||
|  | ||||
| 			const bool neighborIsGuard = guardingCreaturePosition(cp->coord) == dp.coord; | ||||
|  | ||||
| 			if((dp.turns==0xff		//we haven't been here before | ||||
| 				|| dp.turns > turnAtNextTile | ||||
| 				|| (dp.turns >= turnAtNextTile  &&  dp.moveRemains < remains)) //this route is faster | ||||
| 				&& (!guardedPosition || neighborIsGuard)) // Can step into tile of guard | ||||
| 			{ | ||||
| 				 | ||||
| 				assert(&dp != cp->theNodeBefore); //two tiles can't point to each other | ||||
| 				dp.moveRemains = remains; | ||||
| 				dp.turns = turnAtNextTile; | ||||
| 				dp.theNodeBefore = cp; | ||||
|  | ||||
| 				const bool guardedNeighbor = guardingCreaturePosition(dp.coord) != int3(-1, -1, -1); | ||||
| 				//const bool positionIsGuard = guardingCreaturePosition(cp->coord) == cp->coord; //can this be true? hero never passes from monster tile... | ||||
|  | ||||
| 				if (dp.accessible == CGPathNode::ACCESSIBLE || dp.accessible == CGPathNode::FLYABLE | ||||
| 					|| (guardedNeighbor && !guardedPosition)) // Can step into a hostile tile once. | ||||
| 				{ | ||||
| 					mq.push(&dp); | ||||
| 				} | ||||
| 			} | ||||
| 		} //neighbours loop | ||||
| 	} //queue loop | ||||
|  | ||||
| 	out.isValid = true; | ||||
| 	CPathfinder pathfinder(out, this, hero); | ||||
| 	pathfinder.calculatePaths(src, movement); | ||||
| } | ||||
|  | ||||
| /** | ||||
| @@ -2830,32 +2492,14 @@ bool CPathsInfo::getPath( const int3 &dst, CGPath &out ) | ||||
|  | ||||
| 	out.nodes.clear(); | ||||
| 	const CGPathNode *curnode = &nodes[dst.x][dst.y][dst.z]; | ||||
| 	if(!curnode->theNodeBefore || curnode->accessible == CGPathNode::FLYABLE) | ||||
| 	if(!curnode->theNodeBefore) | ||||
| 		return false; | ||||
|  | ||||
|  | ||||
| 	//we'll transform number of turns to conform the rule that hero cannot stop on blocked tile | ||||
| 	bool transition01 = false; | ||||
| 	while(curnode) | ||||
| 	{ | ||||
| 		CGPathNode cpn = *curnode; | ||||
| 		if(transition01) | ||||
| 		{ | ||||
| 			if (curnode->accessible == CGPathNode::ACCESSIBLE) | ||||
| 			{ | ||||
| 				transition01 = false; | ||||
| 			} | ||||
| 			else if (curnode->accessible == CGPathNode::FLYABLE) | ||||
| 			{ | ||||
| 				cpn.turns = 1; | ||||
| 			} | ||||
| 		} | ||||
| 		if(curnode->turns == 1 && curnode->theNodeBefore->turns == 0) | ||||
| 		{ | ||||
| 			transition01 = true; | ||||
| 		} | ||||
| 		curnode = curnode->theNodeBefore; | ||||
|  | ||||
| 		out.nodes.push_back(cpn); | ||||
| 	} | ||||
| 	return true; | ||||
| @@ -3054,3 +2698,269 @@ TeamState::TeamState() | ||||
| { | ||||
| 	setNodeType(TEAM); | ||||
| } | ||||
|  | ||||
| void CPathfinder::initializeGraph() | ||||
| { | ||||
| 	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) | ||||
| 			{ | ||||
| 				curPos = int3(i,j,k); | ||||
| 				const TerrainTile *tinfo = &gs->map->terrain[i][j][k]; | ||||
| 				CGPathNode &node = graph[i][j][k]; | ||||
| 				node.accessible = evaluateAccessibility(tinfo); | ||||
| 				node.turns = 0xff; | ||||
| 				node.moveRemains = 0; | ||||
| 				node.coord.x = i; | ||||
| 				node.coord.y = j; | ||||
| 				node.coord.z = k; | ||||
| 				node.land = tinfo->tertype != TerrainTile::water; | ||||
| 				node.theNodeBefore = NULL; | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
|  | ||||
| void CPathfinder::calculatePaths(int3 src /*= int3(-1,-1,-1)*/, int movement /*= -1*/) | ||||
| { | ||||
| 	assert(hero); | ||||
| 	assert(hero == getHero(hero->id)); | ||||
| 	if(src.x < 0) | ||||
| 		src = hero->getPosition(false); | ||||
| 	if(movement < 0) | ||||
| 		movement = hero->movement; | ||||
|  | ||||
| 	out.hero = hero; | ||||
| 	out.hpos = src; | ||||
|  | ||||
| 	if(!gs->map->isInTheMap(src)/* || !gs->map->isInTheMap(dest)*/) //check input | ||||
| 	{ | ||||
| 		tlog1 << "CGameState::calculatePaths: Hero outside the gs->map? How dare you...\n"; | ||||
| 		return; | ||||
| 	} | ||||
|  | ||||
| 	initializeGraph(); | ||||
|  | ||||
|  | ||||
| 	//initial tile - set cost on 0 and add to the queue | ||||
| 	CGPathNode &initialNode = *getNode(src); | ||||
| 	initialNode.turns = 0; | ||||
| 	initialNode.moveRemains = movement; | ||||
| 	mq.push_back(&initialNode); | ||||
|  | ||||
| 	std::vector<int3> neighbours; | ||||
| 	neighbours.reserve(16); | ||||
| 	while(!mq.empty()) | ||||
| 	{ | ||||
| 		cp = mq.front(); | ||||
| 		mq.pop_front(); | ||||
|  | ||||
| 		const int3 sourceGuardPosition = guardingCreaturePosition(cp->coord); | ||||
| 		bool guardedSource = (sourceGuardPosition != int3(-1, -1, -1) && cp->coord != src); | ||||
| 		ct = &gs->map->getTile(cp->coord); | ||||
|  | ||||
| 		int movement = cp->moveRemains, turn = cp->turns; | ||||
| 		if(!movement) | ||||
| 		{ | ||||
| 			movement = hero->maxMovePoints(cp->land); | ||||
| 			turn++; | ||||
| 		} | ||||
|  | ||||
|  | ||||
| 		//add accessible neighbouring nodes to the queue | ||||
| 		neighbours.clear(); | ||||
|  | ||||
| 		//handling subterranean gate => it's exit is the only neighbour | ||||
| 		bool subterraneanEntry = (ct->topVisitableID() == SUBTERRANEAN_GATE_TYPE && useSubterraneanGates); | ||||
| 		if(subterraneanEntry) | ||||
| 		{ | ||||
| 			//try finding the exit gate | ||||
| 			if(const CGObjectInstance *outGate = getObj(CGTeleport::getMatchingGate(ct->visitableObjects.back()->id), false)) | ||||
| 			{ | ||||
| 				const int3 outPos = outGate->visitablePos(); | ||||
| 				//gs->getNeighbours(*getTile(outPos), outPos, neighbours, boost::logic::indeterminate, !cp->land); | ||||
| 				neighbours.push_back(outPos); | ||||
| 			} | ||||
| 			else | ||||
| 			{ | ||||
| 				//gate with no exit (blocked) -> do nothing with this node | ||||
| 				continue; | ||||
| 			} | ||||
| 		} | ||||
|  | ||||
| 		gs->getNeighbours(*ct, cp->coord, neighbours, boost::logic::indeterminate, !cp->land); | ||||
|  | ||||
| 		for(unsigned int i=0; i < neighbours.size(); i++) | ||||
| 		{ | ||||
| 			const int3 &n = neighbours[i]; //current neighbor | ||||
| 			dp = getNode(n); | ||||
| 			dt = &gs->map->getTile(n); | ||||
| 			destTopVisObjID = dt->topVisitableID(); | ||||
|  | ||||
| 			useEmbarkCost = 0; //0 - usual movement; 1 - embark; 2 - disembark | ||||
|  | ||||
| 			int moveAtNextTile = movement; | ||||
| 			int turnAtNextTile = turn; | ||||
|  | ||||
|  | ||||
| 			const bool destIsGuardian = sourceGuardPosition == n; | ||||
|  | ||||
| 			if(!goodForLandSeaTransition()) | ||||
| 				continue; | ||||
|  | ||||
| 			if(!canMoveBetween(cp->coord, dp->coord) || dp->accessible == CGPathNode::BLOCKED ) | ||||
| 				continue; | ||||
|  | ||||
| 			//special case -> hero embarked a boat standing on a guarded tile -> we must allow to move away from that tile | ||||
| 			if(cp->accessible == CGPathNode::VISITABLE && guardedSource && cp->theNodeBefore->land && ct->topVisitableID() == BOATI_TYPE) | ||||
| 				guardedSource = false; | ||||
|  | ||||
| 			int cost = gs->getMovementCost(hero, cp->coord, dp->coord, movement); | ||||
|  | ||||
| 			//special case -> moving from src Subterranean gate to dest gate -> it's free | ||||
| 			if(subterraneanEntry && destTopVisObjID == SUBTERRANEAN_GATE_TYPE && cp->coord.z != dp->coord.z) | ||||
| 				cost = 0; | ||||
|  | ||||
| 			int remains = movement - cost; | ||||
|  | ||||
| 			if(useEmbarkCost) | ||||
| 			{ | ||||
| 				remains = hero->movementPointsAfterEmbark(movement, cost, useEmbarkCost - 1); | ||||
| 				cost = movement - remains; | ||||
| 			} | ||||
|  | ||||
| 			if(remains < 0) | ||||
| 			{ | ||||
| 				//occurs rarely, when hero with low movepoints tries to leave the road | ||||
| 				turnAtNextTile++; | ||||
| 				moveAtNextTile = hero->maxMovePoints(cp->land); | ||||
| 				cost = gs->getMovementCost(hero, cp->coord, dp->coord, moveAtNextTile); //cost must be updated, movement points changed :( | ||||
| 				remains = moveAtNextTile - cost; | ||||
| 			} | ||||
|  | ||||
| 			if((dp->turns==0xff		//we haven't been here before | ||||
| 				|| dp->turns > turnAtNextTile | ||||
| 				|| (dp->turns >= turnAtNextTile  &&  dp->moveRemains < remains)) //this route is faster | ||||
| 				&& (!guardedSource || destIsGuardian)) // Can step into tile of guard | ||||
| 			{ | ||||
|  | ||||
| 				assert(dp != cp->theNodeBefore); //two tiles can't point to each other | ||||
| 				dp->moveRemains = remains; | ||||
| 				dp->turns = turnAtNextTile; | ||||
| 				dp->theNodeBefore = cp; | ||||
|  | ||||
| 				const bool guardedDst = guardingCreaturePosition(dp->coord) != int3(-1, -1, -1)   | ||||
| 										&& dp->accessible == CGPathNode::BLOCKVIS; | ||||
|  | ||||
| 				if (dp->accessible == CGPathNode::ACCESSIBLE | ||||
| 					|| useEmbarkCost && allowEmbarkAndDisembark | ||||
| 					|| destTopVisObjID == SUBTERRANEAN_GATE_TYPE | ||||
| 					|| (guardedDst && !guardedSource)) // Can step into a hostile tile once. | ||||
| 				{ | ||||
| 					mq.push_back(dp); | ||||
| 				} | ||||
| 			} | ||||
| 		} //neighbours loop | ||||
| 	} //queue loop | ||||
|  | ||||
| 	out.isValid = true; | ||||
| } | ||||
|  | ||||
| CGPathNode *CPathfinder::getNode(const int3 &coord) | ||||
| { | ||||
| 	return &out.nodes[coord.x][coord.y][coord.z]; | ||||
| } | ||||
|  | ||||
| bool CPathfinder::canMoveBetween(const int3 &a, const int3 &b) const | ||||
| { | ||||
| 	return gs->checkForVisitableDir(a, b) && gs->checkForVisitableDir(b, a); | ||||
| } | ||||
|  | ||||
| bool CPathfinder::canStepOntoDst() const | ||||
| { | ||||
| 	//TODO remove | ||||
| 	assert(0); | ||||
| 	return false; | ||||
| } | ||||
|  | ||||
| CGPathNode::EAccessibility CPathfinder::evaluateAccessibility(const TerrainTile *tinfo) const | ||||
| { | ||||
| 	CGPathNode::EAccessibility ret = (tinfo->blocked ? CGPathNode::BLOCKED : CGPathNode::ACCESSIBLE); | ||||
|  | ||||
|  | ||||
| 	if(tinfo->tertype == TerrainTile::rock || !FoW[curPos.x][curPos.y][curPos.z]) | ||||
| 		return CGPathNode::BLOCKED; | ||||
| 	 | ||||
| 	if(tinfo->visitable) | ||||
| 	{ | ||||
| 		if(tinfo->visitableObjects.front()->ID == 80 && tinfo->visitableObjects.back()->ID == HEROI_TYPE && tinfo->visitableObjects.back()->tempOwner != hero->tempOwner) //non-owned hero stands on Sanctuary | ||||
| 		{ | ||||
| 			return CGPathNode::BLOCKED; | ||||
| 		} | ||||
| 		else | ||||
| 		{ | ||||
| 			BOOST_FOREACH(const CGObjectInstance *obj, tinfo->visitableObjects) | ||||
| 			{ | ||||
| 				if(obj->getPassableness() & 1<<hero->tempOwner) //special object instance specific passableness flag - overwrites other accessibility flags | ||||
| 				{ | ||||
| 					ret = CGPathNode::ACCESSIBLE; | ||||
| 				} | ||||
| 				else if(obj->blockVisit) | ||||
| 				{ | ||||
| 					return CGPathNode::BLOCKVIS; | ||||
| 				} | ||||
| 				else if(obj->ID != EVENTI_TYPE) //pathfinder should ignore placed events | ||||
| 				{ | ||||
| 					ret =  CGPathNode::VISITABLE; | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| 	else if (gs->map->isInTheMap(guardingCreaturePosition(curPos)) | ||||
| 		&& !tinfo->blocked) | ||||
| 	{ | ||||
| 		// Monster close by; blocked visit for battle. | ||||
| 		return CGPathNode::BLOCKVIS; | ||||
| 	} | ||||
|  | ||||
| 	return ret; | ||||
| } | ||||
|  | ||||
| bool CPathfinder::goodForLandSeaTransition() | ||||
| { | ||||
| 	if(cp->land != dp->land) //hero can traverse land<->sea only in special circumstances | ||||
| 	{ | ||||
| 		if(cp->land) //from land to sea -> embark or assault hero on boat | ||||
| 		{ | ||||
| 			if(dp->accessible == CGPathNode::ACCESSIBLE || destTopVisObjID < 0) //cannot enter empty water tile from land -> it has to be visitable | ||||
| 				return false;  | ||||
| 			if(destTopVisObjID != HEROI_TYPE && destTopVisObjID != BOATI_TYPE) //only boat or hero can be accessed from land | ||||
| 				return false; | ||||
| 			if(destTopVisObjID == BOATI_TYPE) | ||||
| 				useEmbarkCost = 1;  | ||||
| 		} | ||||
| 		else //disembark | ||||
| 		{ | ||||
| 			//can disembark only on coastal tiles | ||||
| 			if(!dt->isCoastal())  | ||||
| 				return false; | ||||
|  | ||||
| 			//tile must be accessible -> exception: unblocked blockvis tiles -> clear but guarded by nearby monster coast | ||||
| 			if(dp->accessible != CGPathNode::ACCESSIBLE && (dp->accessible != CGPathNode::BLOCKVIS || dt->blocked))  | ||||
| 				return false;; | ||||
|  | ||||
| 			useEmbarkCost = 2; | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	return true;  | ||||
| } | ||||
|  | ||||
| CPathfinder::CPathfinder(CPathsInfo &_out, CGameState *_gs, const CGHeroInstance *_hero) : out(_out), CGameInfoCallback(_gs, -1), hero(_hero), FoW(getPlayerTeam(hero->tempOwner)->fogOfWarMap) | ||||
| { | ||||
| 	useSubterraneanGates = true; | ||||
| 	allowEmbarkAndDisembark = true; | ||||
| } | ||||
| @@ -228,13 +228,12 @@ struct CPathNode | ||||
|  | ||||
| struct CGPathNode | ||||
| { | ||||
| 	enum  | ||||
| 	enum EAccessibility | ||||
| 	{ | ||||
| 		ACCESSIBLE=1, //tile can be entered and passed | ||||
| 		VISITABLE, //tile can be entered as the last tile in path | ||||
| 		BLOCKVIS,  //visitable from neighbouring tile but not passable | ||||
| 		BLOCKED, //tile can't be entered nor visited | ||||
| 		FLYABLE //if hero flies, he can pass this tile | ||||
| 		BLOCKED //tile can't be entered nor visited | ||||
| 	}; | ||||
|  | ||||
| 	ui8 accessible; //the enum above | ||||
| @@ -313,6 +312,40 @@ struct DLL_EXPORT DuelParameters | ||||
| 	} | ||||
| }; | ||||
|  | ||||
| class CPathfinder : private CGameInfoCallback | ||||
| { | ||||
| public: | ||||
| 	bool useSubterraneanGates; | ||||
| 	bool allowEmbarkAndDisembark; | ||||
| 	CPathsInfo &out; | ||||
| 	const CGHeroInstance *hero; | ||||
| 	const std::vector<std::vector<std::vector<ui8> > > &FoW; | ||||
|  | ||||
| 	std::list<CGPathNode*> mq; //BFS queue -> nodes to be checked | ||||
|  | ||||
|  | ||||
| 	int3 curPos; | ||||
| 	CGPathNode *cp; //current (source) path node -> we took it from the queue | ||||
| 	CGPathNode *dp; //destination node -> it's a neighbour of cp that we consider | ||||
| 	const TerrainTile *ct, *dt; //tile info for both nodes | ||||
| 	ui8 useEmbarkCost; //0 - usual movement; 1 - embark; 2 - disembark | ||||
| 	int destTopVisObjID; | ||||
|  | ||||
| 	CPathfinder(CPathsInfo &_out, CGameState *_gs, const CGHeroInstance *_hero);; | ||||
|  | ||||
| 	CGPathNode *getNode(const int3 &coord); | ||||
|  | ||||
| 	void initializeGraph(); | ||||
| 	CGPathNode::EAccessibility evaluateAccessibility(const TerrainTile *tinfo) const; | ||||
|  | ||||
| 	void calculatePaths(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 canMoveBetween(const int3 &a, const int3 &b) const; //checks only for visitable objects that may make moving between tiles impossible, not other conditions (like tiles itself accessibility) | ||||
| 	bool canStepOntoDst() const; | ||||
| 	bool goodForLandSeaTransition(); //checks if current move will be between sea<->land. If so, checks it legality (returns false if movement is not possible) and sets useEmbarkCost | ||||
|  | ||||
| }; | ||||
|  | ||||
|  | ||||
| struct BattleInfo; | ||||
|  | ||||
|   | ||||
| @@ -1479,6 +1479,14 @@ CBonusSystemNode * CGHeroInstance::whereShouldBeAttached(CGameState *gs) | ||||
| 		return CArmedInstance::whereShouldBeAttached(gs); | ||||
| } | ||||
|  | ||||
| int CGHeroInstance::movementPointsAfterEmbark(int MPsBefore, int basicCost, bool disembark /*= false*/) const | ||||
| { | ||||
| 	if(hasBonusOfType(Bonus::FREE_SHIP_BOARDING)) | ||||
| 		return (MPsBefore - basicCost) * ((float)(maxMovePoints(disembark)) / maxMovePoints(!disembark)); | ||||
|  | ||||
| 	return 0; //take all MPs otherwise | ||||
| } | ||||
|  | ||||
| void CGDwelling::initObj() | ||||
| { | ||||
| 	switch(ID) | ||||
| @@ -3573,22 +3581,8 @@ void CGTeleport::onHeroVisit( const CGHeroInstance * h ) const | ||||
| 		break; | ||||
| 	case 103: //find nearest subterranean gate on the other level | ||||
| 		{ | ||||
| 			int i=0; | ||||
| 			for(; i < gates.size(); i++) | ||||
| 			{ | ||||
| 				if(gates[i].first == id) | ||||
| 				{ | ||||
| 					destinationid = gates[i].second; | ||||
| 					break; | ||||
| 				} | ||||
| 				else if(gates[i].second == id) | ||||
| 				{ | ||||
| 					destinationid = gates[i].first; | ||||
| 					break; | ||||
| 				} | ||||
| 			} | ||||
|  | ||||
| 			if(destinationid < 0  ||  i == gates.size()) //no exit | ||||
| 			destinationid = getMatchingGate(id); | ||||
| 			if(destinationid < 0) //no exit | ||||
| 			{ | ||||
| 				InfoWindow iw; | ||||
| 				iw.player = h->tempOwner; | ||||
| @@ -3676,6 +3670,19 @@ void CGTeleport::postInit() //matches subterranean gates into pairs | ||||
| 	objs.erase(103); | ||||
| } | ||||
|  | ||||
| int CGTeleport::getMatchingGate(int id) | ||||
| { | ||||
| 	for(int i=0; i < gates.size(); i++) | ||||
| 	{ | ||||
| 		if(gates[i].first == id) | ||||
| 			return gates[i].second; | ||||
| 		if(gates[i].second == id) | ||||
| 			return gates[i].first; | ||||
| 	} | ||||
|  | ||||
| 	return -1; | ||||
| } | ||||
|  | ||||
| void CGArtifact::initObj() | ||||
| { | ||||
| 	blockVisit = true; | ||||
|   | ||||
| @@ -354,6 +354,7 @@ public: | ||||
| 	void setSecSkillLevel(SecondarySkill which, int val, bool abs);// abs == 0 - changes by value; 1 - sets to value | ||||
|  | ||||
| 	int maxMovePoints(bool onLand) const; | ||||
| 	int movementPointsAfterEmbark(int MPsBefore, int basicCost, bool disembark = false) const;  | ||||
|  | ||||
| // 	const CArtifact* getArtAtPos(ui16 pos) const; //NULL - no artifact | ||||
| // 	const CArtifact * getArt(int pos) const; | ||||
| @@ -952,6 +953,7 @@ public: | ||||
| 	void onHeroVisit(const CGHeroInstance * h) const; | ||||
| 	void initObj();	 | ||||
| 	static void postInit(); | ||||
| 	static int getMatchingGate(int id); //receives id of one subterranean gate and returns id of the paired one, -1 if none | ||||
|  | ||||
| 	template <typename Handler> void serialize(Handler &h, const int version) | ||||
| 	{ | ||||
|   | ||||
| @@ -1024,6 +1024,12 @@ CGameInfoCallback::CGameInfoCallback() | ||||
| { | ||||
| } | ||||
|  | ||||
| CGameInfoCallback::CGameInfoCallback(CGameState *GS, int Player) | ||||
| { | ||||
| 	gs = GS; | ||||
| 	player = Player; | ||||
| } | ||||
|  | ||||
| const std::vector< std::vector< std::vector<unsigned char> > > & CPlayerSpecificInfoCallback::getVisibilityMap() const | ||||
| { | ||||
| 	//boost::shared_lock<boost::shared_mutex> lock(*gs->mx); | ||||
|   | ||||
							
								
								
									
										15
									
								
								lib/map.cpp
									
									
									
									
									
								
							
							
						
						
									
										15
									
								
								lib/map.cpp
									
									
									
									
									
								
							| @@ -2164,3 +2164,18 @@ bool TerrainTile::isClear(const TerrainTile *from /*= NULL*/) const | ||||
| { | ||||
| 	return entrableTerrain(from) && !blocked; | ||||
| } | ||||
|  | ||||
| int TerrainTile::topVisitableID() const | ||||
| { | ||||
| 	return visitableObjects.size() ? visitableObjects.back()->ID : -1; | ||||
| } | ||||
|  | ||||
| bool TerrainTile::isCoastal() const | ||||
| { | ||||
| 	return siodmyTajemniczyBajt & 64; | ||||
| } | ||||
|  | ||||
| bool TerrainTile::hasFavourableWinds() const | ||||
| { | ||||
| 	return siodmyTajemniczyBajt & 128; | ||||
| } | ||||
| @@ -71,6 +71,10 @@ struct DLL_EXPORT TerrainTile | ||||
| 	bool entrableTerrain(const TerrainTile *from = NULL) const; //checks if terrain is not a rock. If from is water/land, same type is also required.  | ||||
| 	bool entrableTerrain(bool allowLand, bool allowSea) const; //checks if terrain is not a rock. If from is water/land, same type is also required.  | ||||
| 	bool isClear(const TerrainTile *from = NULL) const; //checks for blocking objs and terraint type (water / land) | ||||
| 	int topVisitableID() const; //returns ID of the top visitable ovject or -1 if there is none | ||||
|  | ||||
| 	bool isCoastal() const; | ||||
| 	bool hasFavourableWinds() const; | ||||
| }; | ||||
|  | ||||
| /// name of starting hero | ||||
|   | ||||
| @@ -1423,13 +1423,7 @@ bool CGameHandler::moveHero( si32 hid, int3 dst, ui8 instant, ui8 asker /*= 255* | ||||
| 	if(!h->boat && t.visitableObjects.size() && t.visitableObjects.back()->ID == 8) | ||||
| 	{ | ||||
| 		tmh.result = TryMoveHero::EMBARK; | ||||
| 		if (h->hasBonusOfType(Bonus::FREE_SHIP_BOARDING)) | ||||
| 		{ | ||||
| 			tmh.movePoints = (h->movement - cost)*((float)(h->maxMovePoints(false)) / h->maxMovePoints(true)); | ||||
| 		} | ||||
| 		else | ||||
| 			tmh.movePoints = 0; //embarking takes all move points | ||||
|  | ||||
| 		tmh.movePoints = h->movementPointsAfterEmbark(h->movement, cost, false); | ||||
| 		getTilesInRange(tmh.fowRevealed,h->getSightCenter()+(tmh.end-tmh.start),h->getSightRadious(),h->tempOwner,1); | ||||
| 		sendAndApply(&tmh); | ||||
| 		return true; | ||||
| @@ -1437,14 +1431,9 @@ bool CGameHandler::moveHero( si32 hid, int3 dst, ui8 instant, ui8 asker /*= 255* | ||||
| 	//hero leaves the boat | ||||
| 	else if(h->boat && t.tertype != TerrainTile::water && !t.blocked) | ||||
| 	{ | ||||
| 		//TODO? code similarity with the block above | ||||
| 		tmh.result = TryMoveHero::DISEMBARK; | ||||
| 		if (h->hasBonusOfType(Bonus::FREE_SHIP_BOARDING)) | ||||
| 		{ | ||||
| 			tmh.movePoints = (h->movement - cost)*((float)(h->maxMovePoints(true)) / h->maxMovePoints(false)); | ||||
| 		} | ||||
| 		else | ||||
| 			tmh.movePoints = 0; //disembarking takes all move points | ||||
|  | ||||
| 		tmh.movePoints = h->movementPointsAfterEmbark(h->movement, cost, true); | ||||
| 		getTilesInRange(tmh.fowRevealed,h->getSightCenter()+(tmh.end-tmh.start),h->getSightRadious(),h->tempOwner,1); | ||||
| 		sendAndApply(&tmh); | ||||
| 		tryAttackingGuard(guardPos, h); | ||||
|   | ||||
		Reference in New Issue
	
	Block a user