mirror of
https://github.com/vcmi/vcmi.git
synced 2024-12-20 20:23:03 +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:
parent
74fafbedbf
commit
6ef44bde5a
@ -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)
|
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
|
int pn=-1;//number of picture
|
||||||
if (i==0) //last tile
|
if (i==0) //last tile
|
||||||
{
|
{
|
||||||
int x = 32*(currentPath->nodes[i].coord.x-adventureInt->position.x)+CGI->mh->offsetX + pos.x,
|
int x = 32*(curPos.x-adventureInt->position.x)+CGI->mh->offsetX + pos.x,
|
||||||
y = 32*(currentPath->nodes[i].coord.y-adventureInt->position.y)+CGI->mh->offsetY + pos.y;
|
y = 32*(curPos.y-adventureInt->position.y)+CGI->mh->offsetY + pos.y;
|
||||||
if (x<0 || y<0 || x>pos.w || y>pos.h)
|
if (x<0 || y<0 || x>pos.w || y>pos.h)
|
||||||
continue;
|
continue;
|
||||||
pn=0;
|
pn=0;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
const int3 &prevPos = currentPath->nodes[i-1].coord;
|
||||||
|
std::vector<CGPathNode> & cv = currentPath->nodes;
|
||||||
|
|
||||||
/* Vector directions
|
/* Vector directions
|
||||||
* 0 1 2
|
* 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])
|
* is id1=7, id2=5 (pns[7][5])
|
||||||
*/
|
*/
|
||||||
std::vector<CGPathNode> & cv = currentPath->nodes;
|
bool pathContinuous = curPos.areNeighbours(nextPos) && curPos.areNeighbours(prevPos);
|
||||||
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
|
if(pathContinuous && cv[i].land == cv[i+1].land)
|
||||||
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
|
{
|
||||||
|
int id1=(curPos.x-nextPos.x+1)+3*(curPos.y-nextPos.y+1); //Direction of entering vector
|
||||||
pn=pns[id1][id2];
|
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)
|
if (currentPath->nodes[i].turns)
|
||||||
pn+=25;
|
pn+=25;
|
||||||
if (pn>=0)
|
if (pn>=0)
|
||||||
{
|
{
|
||||||
CDefEssential * arrows = graphics->heroMoveArrows;
|
CDefEssential * arrows = graphics->heroMoveArrows;
|
||||||
int x = 32*(currentPath->nodes[i].coord.x-adventureInt->position.x)+CGI->mh->offsetX + pos.x,
|
int x = 32*(curPos.x-adventureInt->position.x)+CGI->mh->offsetX + pos.x,
|
||||||
y = 32*(currentPath->nodes[i].coord.y-adventureInt->position.y)+CGI->mh->offsetY + pos.y;
|
y = 32*(curPos.y-adventureInt->position.y)+CGI->mh->offsetY + pos.y;
|
||||||
if (x<0 || y<0 || x>pos.w || y>pos.h)
|
if (x<0 || y<0 || x>pos.w || y>pos.h)
|
||||||
continue;
|
continue;
|
||||||
int hvx = (x+arrows->ourImages[pn].bitmap->w)-(pos.x+pos.w),
|
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_BlitSurface(teren,&genRect(pos.h,pos.w,0,0),screen,&genRect(547,594,7,6));
|
||||||
//SDL_FreeSurface(teren);
|
//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);
|
showPath(&pos, to);
|
||||||
}
|
}
|
||||||
@ -1655,7 +1667,7 @@ void CAdvMapInt::tileLClicked(const int3 &mp)
|
|||||||
LOCPLINT->moveHero(currentHero,*terrain.currentPath);
|
LOCPLINT->moveHero(currentHero,*terrain.currentPath);
|
||||||
return;
|
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];
|
CGPath &path = LOCPLINT->paths[currentHero];
|
||||||
terrain.currentPath = &path;
|
terrain.currentPath = &path;
|
||||||
@ -1840,11 +1852,13 @@ void CAdvMapInt::tileHovered(const int3 &tile)
|
|||||||
}
|
}
|
||||||
else //no objs
|
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);
|
CCS->curh->changeGraphic(0, 5 + turns*6);
|
||||||
} else {
|
} else
|
||||||
|
{
|
||||||
if(pnode->land)
|
if(pnode->land)
|
||||||
{
|
{
|
||||||
if(LOCPLINT->cb->getTile(h->getPosition(false))->tertype != TerrainTile::water)
|
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());
|
music = Mix_LoadMUS(filename.c_str());
|
||||||
|
|
||||||
|
if(!music)
|
||||||
|
{
|
||||||
|
tlog3 << "Warning: Cannot open " << filename << ": " << Mix_GetError() << std::endl;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
//The assertion will fail if old MSVC libraries pack .dll is used
|
//The assertion will fail if old MSVC libraries pack .dll is used
|
||||||
assert(Mix_GetMusicType(music) == MUS_MP3_MAD);
|
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
|
//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]);
|
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)
|
if(adventureInt->terrain.currentPath->nodes.size() && adventureInt->terrain.currentPath->nodes.back().coord == CGHeroInstance::convertPosition(hp, false))
|
||||||
eraseCurrentPathOf(ho);
|
removeLastNodeFromPath(ho);
|
||||||
return; //teleport - no fancy moving animation
|
// else
|
||||||
|
// eraseCurrentPathOf(ho);
|
||||||
|
return; //teleport - no fancy moving animation
|
||||||
//TODO: smooth disappear / appear effect
|
//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.
|
|| directlyAttackingCreature) // or creature was attacked from endangering tile.
|
||||||
{
|
{
|
||||||
eraseCurrentPathOf(ho);
|
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)
|
//remove one node from the path (the one we went)
|
||||||
adventureInt->terrain.currentPath->nodes.erase(adventureInt->terrain.currentPath->nodes.end()-1);
|
removeLastNodeFromPath(ho);
|
||||||
if(!adventureInt->terrain.currentPath->nodes.size()) //if it was the last one, remove entire path
|
|
||||||
eraseCurrentPathOf(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--)
|
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)
|
//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)
|
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;
|
newTerrain = cb->getTile(CGHeroInstance::convertPosition(path.nodes[i].coord, false))->tertype;
|
||||||
|
|
||||||
if (newTerrain != currentTerrain) {
|
if (newTerrain != currentTerrain)
|
||||||
|
{
|
||||||
CCS->soundh->stopSound(sh);
|
CCS->soundh->stopSound(sh);
|
||||||
sh = CCS->soundh->playSound(CCS->soundh->horseSounds[newTerrain], -1);
|
sh = CCS->soundh->playSound(CCS->soundh->horseSounds[newTerrain], -1);
|
||||||
currentTerrain = newTerrain;
|
currentTerrain = newTerrain;
|
||||||
@ -2024,6 +2036,13 @@ void CPlayerInterface::eraseCurrentPathOf( const CGHeroInstance * ho, bool check
|
|||||||
adventureInt->terrain.currentPath = NULL;
|
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)
|
CGPath * CPlayerInterface::getAndVerifyPath(const CGHeroInstance * h)
|
||||||
{
|
{
|
||||||
if(vstd::contains(paths,h)) //hero has assigned path
|
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 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 finishMovement( const TryMoveHero &details, const int3 &hp, const CGHeroInstance * ho ); //finish movement
|
||||||
void eraseCurrentPathOf( const CGHeroInstance * ho, bool checkForExistanceOfPath = true );
|
void eraseCurrentPathOf( const CGHeroInstance * ho, bool checkForExistanceOfPath = true );
|
||||||
|
void removeLastNodeFromPath(const CGHeroInstance *ho);
|
||||||
CGPath *getAndVerifyPath( const CGHeroInstance * h );
|
CGPath *getAndVerifyPath( const CGHeroInstance * h );
|
||||||
void acceptTurn(); //used during hot seat after your turn message is close
|
void acceptTurn(); //used during hot seat after your turn message is close
|
||||||
void tryDiggging(const CGHeroInstance *h);
|
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
|
out = CGI->generaltexth->names[225]; //Favourable Winds
|
||||||
else if(terName)
|
else if(terName)
|
||||||
out = CGI->generaltexth->terrainNames[t.tertype];
|
out = CGI->generaltexth->terrainNames[t.tertype];
|
||||||
|
5
global.h
5
global.h
@ -104,8 +104,11 @@ typedef si32 TQuantity;
|
|||||||
typedef ui32 TCreature; //creature id
|
typedef ui32 TCreature; //creature id
|
||||||
const int ARMY_SIZE = 7;
|
const int ARMY_SIZE = 7;
|
||||||
|
|
||||||
const int HEROI_TYPE = 34,
|
const int
|
||||||
|
BOATI_TYPE = 8,
|
||||||
|
HEROI_TYPE = 34,
|
||||||
TOWNI_TYPE = 98,
|
TOWNI_TYPE = 98,
|
||||||
|
SUBTERRANEAN_GATE_TYPE = 103,
|
||||||
CREI_TYPE = 54,
|
CREI_TYPE = 54,
|
||||||
EVENTI_TYPE = 26;
|
EVENTI_TYPE = 26;
|
||||||
|
|
||||||
|
2
int3.h
2
int3.h
@ -35,6 +35,8 @@ public:
|
|||||||
{return int3(-x,-y,-z);}
|
{return int3(-x,-y,-z);}
|
||||||
inline double dist2d(const int3 other) const //distance (z coord is not used)
|
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));}
|
{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)
|
inline void operator+=(const int3 & i)
|
||||||
{
|
{
|
||||||
x+=i.x;
|
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)
|
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) };
|
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++)
|
for (size_t i = 0; i < ARRAY_COUNT(dirs); i++)
|
||||||
{
|
{
|
||||||
const int3 hlp = tile + 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);
|
const TerrainTile &hlpt = map->getTile(hlp);
|
||||||
|
|
||||||
//we cannot visit things from blocked tiles
|
// //we cannot visit things from blocked tiles
|
||||||
if(srct.blocked && !srct.visitable && hlpt.visitable && srct.blockingObjects.front()->ID != HEROI_TYPE)
|
// if(srct.blocked && !srct.visitable && hlpt.visitable && srct.blockingObjects.front()->ID != HEROI_TYPE)
|
||||||
{
|
// {
|
||||||
continue;
|
// continue;
|
||||||
}
|
// }
|
||||||
|
|
||||||
if(srct.tertype == TerrainTile::water && limitCoastSailing && hlpt.tertype == TerrainTile::water && dirs[i].x && dirs[i].y) //diagonal move through water
|
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)
|
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;
|
ret *= 0.666f;
|
||||||
else if (!h->boat && h->getBonusesCount(Selector::typeSubtype(Bonus::WATER_WALKING, 1)) > 0)
|
else if (!h->boat && h->getBonusesCount(Selector::typeSubtype(Bonus::WATER_WALKING, 1)) > 0)
|
||||||
ret *= 1.4f; //40% penalty for water walking
|
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)
|
bool CGameState::getPath(int3 src, int3 dest, const CGHeroInstance * hero, CPath &ret)
|
||||||
{
|
{
|
||||||
if(!map->isInTheMap(src) || !map->isInTheMap(dest)) //check input
|
//the old pathfinder is not supported anymore!
|
||||||
return false;
|
assert(0);
|
||||||
|
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void CGameState::calculatePaths(const CGHeroInstance *hero, CPathsInfo &out, int3 src, int movement)
|
void CGameState::calculatePaths(const CGHeroInstance *hero, CPathsInfo &out, int3 src, int movement)
|
||||||
{
|
{
|
||||||
assert(hero);
|
CPathfinder pathfinder(out, this, hero);
|
||||||
assert(hero == getHero(hero->id));
|
pathfinder.calculatePaths(src, movement);
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -2830,32 +2492,14 @@ bool CPathsInfo::getPath( const int3 &dst, CGPath &out )
|
|||||||
|
|
||||||
out.nodes.clear();
|
out.nodes.clear();
|
||||||
const CGPathNode *curnode = &nodes[dst.x][dst.y][dst.z];
|
const CGPathNode *curnode = &nodes[dst.x][dst.y][dst.z];
|
||||||
if(!curnode->theNodeBefore || curnode->accessible == CGPathNode::FLYABLE)
|
if(!curnode->theNodeBefore)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
|
|
||||||
//we'll transform number of turns to conform the rule that hero cannot stop on blocked tile
|
|
||||||
bool transition01 = false;
|
|
||||||
while(curnode)
|
while(curnode)
|
||||||
{
|
{
|
||||||
CGPathNode cpn = *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;
|
curnode = curnode->theNodeBefore;
|
||||||
|
|
||||||
out.nodes.push_back(cpn);
|
out.nodes.push_back(cpn);
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
@ -3054,3 +2698,269 @@ TeamState::TeamState()
|
|||||||
{
|
{
|
||||||
setNodeType(TEAM);
|
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
|
struct CGPathNode
|
||||||
{
|
{
|
||||||
enum
|
enum EAccessibility
|
||||||
{
|
{
|
||||||
ACCESSIBLE=1, //tile can be entered and passed
|
ACCESSIBLE=1, //tile can be entered and passed
|
||||||
VISITABLE, //tile can be entered as the last tile in path
|
VISITABLE, //tile can be entered as the last tile in path
|
||||||
BLOCKVIS, //visitable from neighbouring tile but not passable
|
BLOCKVIS, //visitable from neighbouring tile but not passable
|
||||||
BLOCKED, //tile can't be entered nor visited
|
BLOCKED //tile can't be entered nor visited
|
||||||
FLYABLE //if hero flies, he can pass this tile
|
|
||||||
};
|
};
|
||||||
|
|
||||||
ui8 accessible; //the enum above
|
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;
|
struct BattleInfo;
|
||||||
|
|
||||||
|
@ -1479,6 +1479,14 @@ CBonusSystemNode * CGHeroInstance::whereShouldBeAttached(CGameState *gs)
|
|||||||
return CArmedInstance::whereShouldBeAttached(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()
|
void CGDwelling::initObj()
|
||||||
{
|
{
|
||||||
switch(ID)
|
switch(ID)
|
||||||
@ -3573,22 +3581,8 @@ void CGTeleport::onHeroVisit( const CGHeroInstance * h ) const
|
|||||||
break;
|
break;
|
||||||
case 103: //find nearest subterranean gate on the other level
|
case 103: //find nearest subterranean gate on the other level
|
||||||
{
|
{
|
||||||
int i=0;
|
destinationid = getMatchingGate(id);
|
||||||
for(; i < gates.size(); i++)
|
if(destinationid < 0) //no exit
|
||||||
{
|
|
||||||
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
|
|
||||||
{
|
{
|
||||||
InfoWindow iw;
|
InfoWindow iw;
|
||||||
iw.player = h->tempOwner;
|
iw.player = h->tempOwner;
|
||||||
@ -3676,6 +3670,19 @@ void CGTeleport::postInit() //matches subterranean gates into pairs
|
|||||||
objs.erase(103);
|
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()
|
void CGArtifact::initObj()
|
||||||
{
|
{
|
||||||
blockVisit = true;
|
blockVisit = true;
|
||||||
|
@ -354,6 +354,7 @@ public:
|
|||||||
void setSecSkillLevel(SecondarySkill which, int val, bool abs);// abs == 0 - changes by value; 1 - sets to value
|
void setSecSkillLevel(SecondarySkill which, int val, bool abs);// abs == 0 - changes by value; 1 - sets to value
|
||||||
|
|
||||||
int maxMovePoints(bool onLand) const;
|
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* getArtAtPos(ui16 pos) const; //NULL - no artifact
|
||||||
// const CArtifact * getArt(int pos) const;
|
// const CArtifact * getArt(int pos) const;
|
||||||
@ -952,6 +953,7 @@ public:
|
|||||||
void onHeroVisit(const CGHeroInstance * h) const;
|
void onHeroVisit(const CGHeroInstance * h) const;
|
||||||
void initObj();
|
void initObj();
|
||||||
static void postInit();
|
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)
|
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
|
const std::vector< std::vector< std::vector<unsigned char> > > & CPlayerSpecificInfoCallback::getVisibilityMap() const
|
||||||
{
|
{
|
||||||
//boost::shared_lock<boost::shared_mutex> lock(*gs->mx);
|
//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;
|
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(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 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)
|
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
|
/// 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)
|
if(!h->boat && t.visitableObjects.size() && t.visitableObjects.back()->ID == 8)
|
||||||
{
|
{
|
||||||
tmh.result = TryMoveHero::EMBARK;
|
tmh.result = TryMoveHero::EMBARK;
|
||||||
if (h->hasBonusOfType(Bonus::FREE_SHIP_BOARDING))
|
tmh.movePoints = h->movementPointsAfterEmbark(h->movement, cost, false);
|
||||||
{
|
|
||||||
tmh.movePoints = (h->movement - cost)*((float)(h->maxMovePoints(false)) / h->maxMovePoints(true));
|
|
||||||
}
|
|
||||||
else
|
|
||||||
tmh.movePoints = 0; //embarking takes all move points
|
|
||||||
|
|
||||||
getTilesInRange(tmh.fowRevealed,h->getSightCenter()+(tmh.end-tmh.start),h->getSightRadious(),h->tempOwner,1);
|
getTilesInRange(tmh.fowRevealed,h->getSightCenter()+(tmh.end-tmh.start),h->getSightRadious(),h->tempOwner,1);
|
||||||
sendAndApply(&tmh);
|
sendAndApply(&tmh);
|
||||||
return true;
|
return true;
|
||||||
@ -1437,14 +1431,9 @@ bool CGameHandler::moveHero( si32 hid, int3 dst, ui8 instant, ui8 asker /*= 255*
|
|||||||
//hero leaves the boat
|
//hero leaves the boat
|
||||||
else if(h->boat && t.tertype != TerrainTile::water && !t.blocked)
|
else if(h->boat && t.tertype != TerrainTile::water && !t.blocked)
|
||||||
{
|
{
|
||||||
|
//TODO? code similarity with the block above
|
||||||
tmh.result = TryMoveHero::DISEMBARK;
|
tmh.result = TryMoveHero::DISEMBARK;
|
||||||
if (h->hasBonusOfType(Bonus::FREE_SHIP_BOARDING))
|
tmh.movePoints = h->movementPointsAfterEmbark(h->movement, cost, true);
|
||||||
{
|
|
||||||
tmh.movePoints = (h->movement - cost)*((float)(h->maxMovePoints(true)) / h->maxMovePoints(false));
|
|
||||||
}
|
|
||||||
else
|
|
||||||
tmh.movePoints = 0; //disembarking takes all move points
|
|
||||||
|
|
||||||
getTilesInRange(tmh.fowRevealed,h->getSightCenter()+(tmh.end-tmh.start),h->getSightRadious(),h->tempOwner,1);
|
getTilesInRange(tmh.fowRevealed,h->getSightCenter()+(tmh.end-tmh.start),h->getSightRadious(),h->tempOwner,1);
|
||||||
sendAndApply(&tmh);
|
sendAndApply(&tmh);
|
||||||
tryAttackingGuard(guardPos, h);
|
tryAttackingGuard(guardPos, h);
|
||||||
|
Loading…
Reference in New Issue
Block a user