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)
|
||||
{
|
||||
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);
|
||||
|
Loading…
Reference in New Issue
Block a user