diff --git a/CCallback.cpp b/CCallback.cpp index 542a95102..1485b90a1 100644 --- a/CCallback.cpp +++ b/CCallback.cpp @@ -829,6 +829,16 @@ const CMapHeader * CCallback::getMapHeader() const return gs->map; } +const CGPathNode * CCallback::getPathInfo( int3 tile ) +{ + return &cl->pathInfo->nodes[tile.x][tile.y][tile.z]; +} + +bool CCallback::getPath2( int3 dest, CGPath &ret ) +{ + return cl->pathInfo->getPath(dest, ret); +} + InfoAboutHero::InfoAboutHero() { details = NULL; diff --git a/CCallback.h b/CCallback.h index 808ae502b..8d020a609 100644 --- a/CCallback.h +++ b/CCallback.h @@ -40,6 +40,8 @@ class CHeroClass; class IShipyard; struct CPackForServer; class CMapHeader; +struct CGPathNode; +struct CGPath; struct InfoAboutHero { @@ -135,6 +137,8 @@ public: virtual std::vector < const CGHeroInstance *> getHeroesInfo(bool onlyOur=true)const =0; virtual bool getHeroInfo(const CGObjectInstance *hero, InfoAboutHero &dest) const = 0; virtual bool getPath(int3 src, int3 dest, const CGHeroInstance * hero, CPath &ret)=0; + virtual const CGPathNode *getPathInfo(int3 tile)=0; + virtual bool getPath2(int3 dest, CGPath &ret)=0; //map virtual std::vector < const CGObjectInstance * > getBlockingObjs(int3 pos)const =0; @@ -261,6 +265,8 @@ public: const TerrainTile * getTileInfo(int3 tile) const; int canBuildStructure(const CGTownInstance *t, int ID);//// 0 - no more than one capitol, 1 - lack of water, 2 - forbidden, 3 - Add another level to Mage Guild, 4 - already built, 5 - cannot build, 6 - cannot afford, 7 - build, 8 - lack of requirements bool getPath(int3 src, int3 dest, const CGHeroInstance * hero, CPath &ret); + const CGPathNode *getPathInfo(int3 tile); + bool getPath2(int3 dest, CGPath &ret); bool getHeroInfo(const CGObjectInstance *hero, InfoAboutHero &dest) const; bool getTownInfo(const CGObjectInstance *town, InfoAboutTown &dest) const; diff --git a/client/CAdvmapInterface.cpp b/client/CAdvmapInterface.cpp index d4452ba4e..d1acebd66 100644 --- a/client/CAdvmapInterface.cpp +++ b/client/CAdvmapInterface.cpp @@ -652,32 +652,121 @@ void CTerrainRect::mouseMoved (const SDL_MouseMotionEvent & sEvent) { LOCPLINT->adventureInt->statusbar.clear(); } - std::vector objs = LOCPLINT->cb->getVisitableObjs(pom); - for(int i=0; icb->getPathInfo(pom); + std::vector objs = LOCPLINT->cb->getBlockingObjs(pom); + const CGObjectInstance *obj = objs.size() ? objs.back() : NULL; + bool accessible = pnode->turns < 255; + + int turns = pnode->turns; + amin(turns, 4); + + if(LOCPLINT->adventureInt->selection->ID == TOWNI_TYPE) { - if(objs[i]->ID == TOWNI_TYPE) //town + if(obj) { - CGI->curh->changeGraphic(0,0); - return; + if(obj->ID == TOWNI_TYPE) + { + CGI->curh->changeGraphic(0, 3); + } + else if(obj->ID == HEROI_TYPE) + { + CGI->curh->changeGraphic(0, 2); + } + } + else + { + CGI->curh->changeGraphic(0, 0); } } - objs = LOCPLINT->cb->getBlockingObjs(pom); - for(size_t i=0; i < objs.size(); ++i) + else if(LOCPLINT->adventureInt->selection->ID == HEROI_TYPE) { - if(objs[i]->ID == TOWNI_TYPE && objs[i]->tempOwner == LOCPLINT->playerID) //town + const CGHeroInstance *h = static_cast(LOCPLINT->adventureInt->selection); + if(obj) { - CGI->curh->changeGraphic(0,3); - return; - } - else if(objs[i]->ID == HEROI_TYPE //mouse over hero - && (objs[i]==LOCPLINT->adventureInt->selection || LOCPLINT->adventureInt->selection->ID==TOWNI_TYPE) - && objs[i]->tempOwner == LOCPLINT->playerID) //this hero is selected or we've selected a town + if(obj->ID == HEROI_TYPE) + { + if(obj->tempOwner != LOCPLINT->playerID) //enemy hero TODO: allies + { + if(accessible) + CGI->curh->changeGraphic(0, 5 + turns*6); + else + CGI->curh->changeGraphic(0, 0); + } + else //our hero + { + if(LOCPLINT->adventureInt->selection == obj) + CGI->curh->changeGraphic(0, 2); + else if(accessible) + CGI->curh->changeGraphic(0, 8 + turns*6); + else + CGI->curh->changeGraphic(0, 2); + } + } + else if(obj->ID == TOWNI_TYPE) + { + if(obj->tempOwner != LOCPLINT->playerID) //enemy town TODO: allies + { + if(accessible) + CGI->curh->changeGraphic(0, 5 + turns*6); + else + CGI->curh->changeGraphic(0, 0); + } + else //our town + { + if(accessible) + CGI->curh->changeGraphic(0, 9 + turns*6); + else + CGI->curh->changeGraphic(0, 3); + } + } + else if(obj->ID == 54) //monster + { + if(accessible) + CGI->curh->changeGraphic(0, 5 + turns*6); + else + CGI->curh->changeGraphic(0, 0); + } + else if(obj->ID == 8) //boat + { + if(accessible) + CGI->curh->changeGraphic(0, 6 + turns*6); + else + CGI->curh->changeGraphic(0, 0); + } + else + { + if(accessible) + { + if(pnode->land) + CGI->curh->changeGraphic(0, 9 + turns*6); + else + CGI->curh->changeGraphic(0, 28 + turns); + } + else + CGI->curh->changeGraphic(0, 0); + } + } + else //no objs { - CGI->curh->changeGraphic(0,2); - return; + if(accessible) + { + if(pnode->land) + { + if(LOCPLINT->cb->getTileInfo(h->getPosition(false))->tertype != TerrainTile::water) + CGI->curh->changeGraphic(0, 4 + turns*6); + else + CGI->curh->changeGraphic(0, 7 + turns*6); //anchor + } + else + CGI->curh->changeGraphic(0, 6 + turns*6); + } + else + CGI->curh->changeGraphic(0, 0); } } - CGI->curh->changeGraphic(0,0); + + //tlog1 << "Tile " << pom << ": Turns=" << (int)pnode->turns <<" Move:=" << pnode->moveRemains <0) - { - x-=12; - y-=10; - } - SDL_BlitSurface(screen, &genRect(32,32,x,y), help, &genRect(32,32,0,0)); + shiftPos(x, y); + SDL_BlitSurface(screen, &genRect(40,40,x,y), help, &genRect(40,40,0,0)); blitAt(cursors[mode]->ourImages[number].bitmap,x,y); } void CCursorHandler::draw2() { if(!Show) return; int x = xpos, y = ypos; - if((mode==1 && number!=6) || mode == 3) + shiftPos(x, y); + blitAt(help,x,y); +} + +void CCursorHandler::shiftPos( int &x, int &y ) +{ + if((mode==1 && number!=6) || mode ==3) { x-=16; y-=16; + + // Properly align the melee attack cursors. + if (mode == 1) + { + switch (number) + { + case 7: // Bottom left + x -= 6; + y += 16; + break; + case 8: // Left + x -= 16; + y += 10; + break; + case 9: // Top left + x -= 6; + y -= 6; + break; + case 10: // Top right + x += 16; + y -= 6; + break; + case 11: // Right + x += 16; + y += 11; + break; + case 12: // Bottom right + x += 16; + y += 16; + break; + case 13: // Below + x += 9; + y += 16; + break; + case 14: // Above + x += 9; + y -= 15; + break; + } + } } - else if(mode==0 && number>0) + else if(mode==0) { - x-=12; - y-=10; + if(number == 0); //to exclude + else if(number == 2) + { + x -= 12; + y -= 10; + } + else if(number == 3) + { + x -= 12; + y -= 12; + } + else if(number < 27) + { + int hlpNum = (number - 4)%6; + if(hlpNum == 0) + { + x -= 15; + y -= 13; + } + else if(hlpNum == 1) + { + x -= 13; + y -= 13; + } + else if(hlpNum == 2) + { + x -= 20; + y -= 20; + } + else if(hlpNum == 3) + { + x -= 13; + y -= 16; + } + else if(hlpNum == 4) + { + x -= 8; + y -= 9; + } + else if(hlpNum == 5) + { + x -= 14; + y -= 16; + } + } + else if(number < 31) + { + x -= 20; + y -= 20; + } } - blitAt(help,x,y); -} +} \ No newline at end of file diff --git a/client/CCursorHandler.h b/client/CCursorHandler.h index d4471ea46..c44d43dd3 100644 --- a/client/CCursorHandler.h +++ b/client/CCursorHandler.h @@ -29,6 +29,8 @@ public: void cursorMove(const int & x, const int & y); //change cursor's positions to (x, y) void changeGraphic(const int & type, const int & no); //changes cursor graphic for type type (0 - adventure, 1 - combat, 2 - default, 3 - spellbook) and frame no (not used for type 3) void draw1(); + + void shiftPos( int &x, int &y ); void draw2(); void hide(){Show=0;}; void show(){Show=1;}; diff --git a/client/CPlayerInterface.h b/client/CPlayerInterface.h index ba81faca5..bfe1a1d44 100644 --- a/client/CPlayerInterface.h +++ b/client/CPlayerInterface.h @@ -64,6 +64,7 @@ class KeyInterested; class MotionInterested; class TimeInterested; class IShowable; +struct CPathsInfo; namespace boost { diff --git a/client/Client.cpp b/client/Client.cpp index 130face50..6aad841ce 100644 --- a/client/Client.cpp +++ b/client/Client.cpp @@ -82,6 +82,7 @@ public: void CClient::init() { + pathInfo = NULL; applier = new CCLApplier; IObjectInterface::cb = this; serv = NULL; @@ -107,6 +108,7 @@ CClient::CClient(CConnection *con, StartInfo *si) } CClient::~CClient(void) { + delete pathInfo; delete applier; delete shared; } @@ -232,6 +234,7 @@ void CClient::load( const std::string & fname ) CGI->state = gs; CGI->mh->map = gs->map; + pathInfo = new CPathsInfo(int3(gs->map->width, gs->map->height, gs->map->twoLevel+1)); CGI->mh->init(); tlog0 <<"Initing maphandler: "<mh->map = mapa; tlog0 <<"Creating mapHandler: "<mh->init(); + pathInfo = new CPathsInfo(int3(mapa->width, mapa->height, mapa->twoLevel+1)); tlog0 <<"Initializing mapHandler (together): "<scenarioOps->playerInfos.size();++i) //initializing interfaces for players diff --git a/client/Client.h b/client/Client.h index 0f7404666..e890c02d5 100644 --- a/client/Client.h +++ b/client/Client.h @@ -25,6 +25,8 @@ class CCallback; struct BattleAction; struct SharedMem; class CClient; +struct CPathsInfo; + void processCommand(const std::string &message, CClient *&client); namespace boost { @@ -62,6 +64,7 @@ public: bool must_close; SharedMem *shared; BattleAction *curbaction; + CPathsInfo *pathInfo; CondSh waitingRequest; diff --git a/client/NetPacksClient.cpp b/client/NetPacksClient.cpp index 001e4c4e3..f89cdd9f9 100644 --- a/client/NetPacksClient.cpp +++ b/client/NetPacksClient.cpp @@ -485,7 +485,17 @@ void EndAction::applyCl( CClient *cl ) void PackageApplied::applyCl( CClient *cl ) { - INTERFACE_CALL_IF_PRESENT(GS(cl)->currentPlayer,requestRealized,this); + ui8 player = GS(cl)->currentPlayer; + + if(packType == typeList.getTypeID((MoveHero*)NULL)) + { + //we've finished moving hero - paths info must be updated + const CGHeroInstance *h = cl->IGameCallback::getSelectedHero(player); + if(h) + GS(cl)->calculatePaths(h, *cl->pathInfo); + } + + INTERFACE_CALL_IF_PRESENT(player, requestRealized, this); if(cl->waitingRequest.get()) cl->waitingRequest.setn(false); } @@ -526,6 +536,15 @@ void PlayerMessage::applyCl(CClient *cl) LOCPLINT->cingconsole->print(str.str()); } +void SetSelection::applyCl(CClient *cl) +{ + const CGHeroInstance *h = cl->getHero(id); + if(!h) + return; + + CPackForClient::GS(cl)->calculatePaths(h, *cl->pathInfo); +} + void ShowInInfobox::applyCl(CClient *cl) { SComponent sc(c); diff --git a/lib/CGameState.cpp b/lib/CGameState.cpp index a90c3ea91..118ac93fe 100644 --- a/lib/CGameState.cpp +++ b/lib/CGameState.cpp @@ -1580,7 +1580,7 @@ void CGameState::loadTownDInfos() } } -void CGameState::getNeighbours(int3 tile, std::vector &vec, const boost::logic::tribool &onLand) +void CGameState::getNeighbours( const TerrainTile &srct, int3 tile, std::vector &vec, const boost::logic::tribool &onLand ) { static int3 dirs[] = { int3(0,1,0),int3(0,-1,0),int3(-1,0,0),int3(+1,0,0), int3(1,1,0),int3(-1,1,0),int3(1,-1,0),int3(-1,-1,0) }; @@ -1588,19 +1588,21 @@ void CGameState::getNeighbours(int3 tile, std::vector &vec, const boost::l vec.clear(); for (size_t i = 0; i < ARRAY_COUNT(dirs); i++) { - int3 hlp = tile + dirs[i]; + const int3 hlp = tile + dirs[i]; if(!map->isInTheMap(hlp)) continue; - if((indeterminate(onLand) || onLand == (map->getTile(hlp).tertype!=8) ) - && map->getTile(hlp).tertype!=9) + const TerrainTile &hlpt = map->getTile(hlp); + + if((indeterminate(onLand) || onLand == (hlpt.tertype!=8) ) + && hlpt.tertype!=9) { vec.push_back(hlp); } } } -int CGameState::getMovementCost(const CGHeroInstance *h, int3 src, int3 dest, int remainingMovePoints, bool checkLast) +int CGameState::getMovementCost(const CGHeroInstance *h, const int3 &src, const int3 &dest, int remainingMovePoints, bool checkLast) { if(src == dest) //same tile return 0; @@ -1616,7 +1618,7 @@ int CGameState::getMovementCost(const CGHeroInstance *h, int3 src, int3 dest, in int old = ret; ret *= 1.414213; //diagonal move costs too much but normal move is possible - allow diagonal move for remaining move points - if(ret > remainingMovePoints && remainingMovePoints > old) + if(ret > remainingMovePoints && remainingMovePoints >= old) { return remainingMovePoints; } @@ -1627,7 +1629,7 @@ int CGameState::getMovementCost(const CGHeroInstance *h, int3 src, int3 dest, in if(checkLast && left > 0 && remainingMovePoints-ret < 250) //it might be the last tile - if no further move possible we take all move points { std::vector vec; - getNeighbours(dest, vec, s.tertype != TerrainTile::water); + getNeighbours(d, dest, vec, s.tertype != TerrainTile::water); for(size_t i=0; i < vec.size(); i++) { int fcost = getMovementCost(h,dest,vec[i],left,false); @@ -1818,7 +1820,7 @@ bool CGameState::getPath(int3 src, int3 dest, const CGHeroInstance * hero, CPath } //add accessible neighbouring nodes to the queue - getNeighbours(cp.coord, neighbours, boost::logic::indeterminate); + getNeighbours(map->getTile(cp.coord), cp.coord, neighbours, boost::logic::indeterminate); for(unsigned int i=0; i < neighbours.size(); i++) { CPathNode & dp = graph[neighbours[i].x][neighbours[i].y]; @@ -1852,146 +1854,144 @@ bool CGameState::getPath(int3 src, int3 dest, const CGHeroInstance * hero, CPath return true; } -//void CGameState::calculatePaths(const CGHeroInstance *hero, CPathsInfo &out, const int3 &src) -//{ -// if(!map->isInTheMap(src)/* || !map->isInTheMap(dest)*/) //check input -// //todo: distater -// return; -// -// int3 hpos = hero->getPosition(false); -// tribool blockLandSea; //true - blocks sea, false - blocks land, indeterminate - allows all -// -// if (!hero->canWalkOnSea()) -// blockLandSea = (map->getTile(hpos).tertype != TerrainTile::water); //block land if hero is on water and vice versa -// else -// blockLandSea = boost::logic::indeterminate; -// -// const std::vector > > &FoW = getPlayer(hero->tempOwner)->fogOfWarMap; -// -// //graph initialization -// CGPathNode ***graph = out.nodes; -// for(size_t i=0; i < out.sizes.x; ++i) -// { -// for(size_t j=0; j < out.sizes.y; ++j) -// { -// for(size_t k=0; k < out.sizes.z; ++k) -// { -// const TerrainTile *tinfo = &map->terrain[i][j][k]; -// CGPathNode &node = graph[i][j][k]; -// -// node.accessible = (tinfo->blocked ? CGPathNode::BLOCKED : CGPathNode::ACCESSIBLE); -// node.visited = false; -// node.turns = 0xff; -// node.moveRemains = 0; -// node.coord.x = i; -// node.coord.y = j; -// node.coord.z = k; -// node.land = tinfo->tertype == TerrainTile::water; -// -// if ((tinfo->tertype == TerrainTile::rock) //it's rock -// || ((blockLandSea) && () //it's sea and we cannot walk on sea -// || ((!blockLandSea) && (tinfo->tertype != TerrainTile::water)) //it's land and we cannot walk on land -// || !FoW[i][j][k] //tile is covered by the FoW -// ) -// { -// node.accessible = CGPathNode::BLOCKED; -// } -// else if(tinfo->visitable) -// { -// for(size_t ii = 0; ii < tinfo->visitableObjects.size(); ii++) -// { -// if(tinfo->visitableObjects[ii]->blockVisit) -// { -// node.accessible = CGPathNode::BLOCKVIS; -// break; -// } -// else -// node.accessible = CGPathNode::VISITABLE; -// } -// } -// -// if(blockLandSea && tinfo->tertype == TerrainTile::water) //hero can walk only on land and tile lays on the water -// { -// size_t i = 0; -// for(; i < tinfo->visitableObjects.size(); i++) -// if(tinfo->visitableObjects[i]->ID == 8 || tinfo->visitableObjects[i]->ID == HEROI_TYPE) //it's a Boat -// break; -// -// if(i < tinfo->visitableObjects.size()) -// node.accessible = CGPathNode::BLOCKVIS; //dest is accessible only if there is boat/hero -// } -// else if(!blockLandSea && tinfo->tertype != TerrainTile::water) //hero is moving by water -// { -// if((tinfo->siodmyTajemniczyBajt & 64) && !tinfo->blocked) -// node.accessible = CGPathNode::ACCESSIBLE; //tile is accessible if it's coastal and not blocked -// } -// } -// } -// } -// //graph initialized -// -// -// //initial tile - set cost on 0 and add to the queue -// graph[src.x][src.y][src.z].turns = 0; -// graph[src.x][src.y][src.z].moveRemains = hero->movement; -// std::queue mq; -// mq.push(&graph[src.x][src.y][src.z]); -// -// ui32 curDist = 0xffffffff; //total cost of path - init with max possible val -// -// std::vector neighbours; -// neighbours.reserve(8); -// -// while(!mq.empty()) -// { -// CGPathNode *cp = graph[mq.front()->coord.x][mq.front()->coord.y]; -// mq.pop(); -// -// //add accessible neighbouring nodes to the queue -// getNeighbours(cp->coord, neighbours, boost::logic::indeterminate); -// for(unsigned int i=0; i < neighbours.size(); i++) -// { -// const int3 &n = neighbours[i]; //current neighbour -// CGPathNode & dp = graph[n.x][n.y][n.z]; -// if(!cp->moveRemains) -// { -// cp->turns++; -// cp->moveRemains = hero->maxMovePoints( -// } -// -// -// if(dp.accessible != CGPathNode::BLOCKVIS) -// { -// int cost = getMovementCost(hero,cp->coord,dp.coord,hero->movement - cp->dist); -// if((dp.turns==0xff || (dp.dist > cp->dist + cost)) && dp.accesible && checkForVisitableDir(cp->coord, dp.coord) && checkForVisitableDir(dp.coord, cp->coord)) -// { -// dp.moveRemains = cp.moveRemains - cost; -// dp.theNodeBefore = &cp; -// if(dp.accessible == CGPathNode::ACCESSIBLE) -// { -// mq.push(dp); -// } -// } -// } -// } -// } -// -// CPathNode *curNode = &graph[dest.x][dest.y]; -// if(!curNode->theNodeBefore) //destination is not accessible -// return false; -// -// -// //fill ret with found path -// ret.nodes.clear(); -// while(curNode->coord != graph[src.x][src.y].coord) -// { -// ret.nodes.push_back(*curNode); -// curNode = curNode->theNodeBefore; -// } -// ret.nodes.push_back(graph[src.x][src.y]); -// -// return true; -//} +void CGameState::calculatePaths(const CGHeroInstance *hero, CPathsInfo &out, int3 src, int movement) +{ + if(src.x < 0) + src = hero->getPosition(false); + if(movement < 0) + movement = hero->movement; + + if(!map->isInTheMap(src)/* || !map->isInTheMap(dest)*/) //check input + { + tlog1 << "CGameState::calculatePaths: Hero outside the map? How dare you...\n"; + return; + } + + tribool onLand; //true - blocks sea, false - blocks land, indeterminate - allows all + + if (!hero->canWalkOnSea()) + onLand = (map->getTile(src).tertype != TerrainTile::water); //block land if hero is on water and vice versa + else + onLand = boost::logic::indeterminate; + + const std::vector > > &FoW = getPlayer(hero->tempOwner)->fogOfWarMap; + + //graph initialization + CGPathNode ***graph = out.nodes; + for(size_t i=0; i < out.sizes.x; ++i) + { + for(size_t j=0; j < out.sizes.y; ++j) + { + for(size_t k=0; k < out.sizes.z; ++k) + { + const TerrainTile *tinfo = &map->terrain[i][j][k]; + CGPathNode &node = graph[i][j][k]; + + node.accessible = (tinfo->blocked ? CGPathNode::BLOCKED : CGPathNode::ACCESSIBLE); + node.turns = 0xff; + node.moveRemains = 0; + node.coord.x = i; + node.coord.y = j; + node.coord.z = k; + node.land = tinfo->tertype != TerrainTile::water; + + if ( tinfo->tertype == TerrainTile::rock//it's rock + || onLand && !node.land //it's sea and we cannot walk on sea + || !onLand && node.land //it's land and we cannot walk on land + || !FoW[i][j][k] //tile is covered by the FoW + ) + { + node.accessible = CGPathNode::BLOCKED; + } + else if(tinfo->visitable) + { + for(size_t ii = 0; ii < tinfo->visitableObjects.size(); ii++) + { + if(tinfo->visitableObjects[ii]->blockVisit) + { + node.accessible = CGPathNode::BLOCKVIS; + break; + } + else + node.accessible = CGPathNode::VISITABLE; + } + } + + if(onLand && !node.land) //hero can walk only on land and tile lays on the water + { + size_t i = 0; + for(; i < tinfo->visitableObjects.size(); i++) + if(tinfo->visitableObjects[i]->ID == 8 || tinfo->visitableObjects[i]->ID == HEROI_TYPE) //it's a Boat + break; + if(i < tinfo->visitableObjects.size()) + node.accessible = CGPathNode::BLOCKVIS; //dest is accessible only if there is boat/hero + } + else if(!onLand && tinfo->tertype != TerrainTile::water) //hero is moving by water + { + if((tinfo->siodmyTajemniczyBajt & 64) && !tinfo->blocked) + node.accessible = CGPathNode::ACCESSIBLE; //tile is accessible if it's coastal and not blocked + } + } + } + } + //graph initialized + + + //initial tile - set cost on 0 and add to the queue + graph[src.x][src.y][src.z].turns = 0; + graph[src.x][src.y][src.z].moveRemains = movement; + std::queue mq; + mq.push(&graph[src.x][src.y][src.z]); + + ui32 curDist = 0xffffffff; //total cost of path - init with max possible val + + std::vector neighbours; + neighbours.reserve(8); + + while(!mq.empty()) + { + CGPathNode *cp = mq.front(); + mq.pop(); + + const TerrainTile &ct = map->getTile(cp->coord); + int movement = cp->moveRemains, turn = cp->turns; + if(!movement) + { + movement = hero->maxMovePoints(ct.tertype != TerrainTile::water); + turn++; + } + + //add accessible neighbouring nodes to the queue + getNeighbours(ct, cp->coord, neighbours, boost::logic::indeterminate); + for(unsigned int i=0; i < neighbours.size(); i++) + { + const int3 &n = neighbours[i]; //current neighbor + CGPathNode & dp = graph[n.x][n.y][n.z]; + if( !checkForVisitableDir(cp->coord, dp.coord) + || !checkForVisitableDir(dp.coord, cp->coord) + || dp.accessible == CGPathNode::BLOCKED ) + { + continue; + } + + int cost = getMovementCost(hero, cp->coord, dp.coord, movement); + int remains = movement - cost; + + if(dp.turns==0xff //we haven't been here before + || dp.turns > turn + || (dp.turns >= turn && dp.moveRemains < remains)) //this route is faster + { + dp.moveRemains = remains; + dp.turns = turn; + dp.theNodeBefore = cp; + if(dp.accessible == CGPathNode::ACCESSIBLE) + { + mq.push(&dp); + } + } + } //neighbours loop + } //queue loop +} bool CGameState::isVisible(int3 pos, int player) { @@ -2022,6 +2022,11 @@ bool CGameState::isVisible( const CGObjectInstance *obj, int player ) bool CGameState::checkForVisitableDir(const int3 & src, const int3 & dst) const { const TerrainTile * pom = &map->getTile(dst); + return checkForVisitableDir(src, pom, dst); +} + +bool CGameState::checkForVisitableDir( const int3 & src, const TerrainTile *pom, const int3 & dst ) const +{ for(unsigned int b=0; bvisitableObjects.size(); ++b) //checking destination tile { if(!vstd::contains(pom->blockingObjects, pom->visitableObjects[b])) //this visitable object is not blocking, ignore @@ -2063,7 +2068,6 @@ bool CGameState::checkForVisitableDir(const int3 & src, const int3 & dst) const } return true; } - std::pair BattleInfo::calculateDmgRange(const CStack* attacker, const CStack* defender, const CGHeroInstance * attackerHero, const CGHeroInstance * defendingHero, bool shooting, ui8 charge) { int attackDefenseBonus, @@ -2574,3 +2578,55 @@ int3 CPath::endPos() const { return nodes[0].coord; } + +CGPathNode::CGPathNode() +:coord(-1,-1,-1) +{ + accessible = 0; + land = 0; + moveRemains = 0; + turns = 255; + theNodeBefore = NULL; +} + +bool CPathsInfo::getPath( const int3 &dst, CGPath &out ) +{ + out.nodes.clear(); + const CGPathNode *curnode = &nodes[dst.x][dst.y][dst.z]; + if(!curnode->theNodeBefore) + return false; + + while(curnode->theNodeBefore) + { + out.nodes.push_back(*curnode); + curnode = curnode->theNodeBefore; + } + return true; +} + +CPathsInfo::CPathsInfo( const int3 &Sizes ) +:sizes(Sizes) +{ + nodes = new CGPathNode**[sizes.x]; + for(int i = 0; i < sizes.x; i++) + { + nodes[i] = new CGPathNode*[sizes.y]; + for (int j = 0; j < sizes.y; j++) + { + nodes[i][j] = new CGPathNode[sizes.z]; + } + } +} + +CPathsInfo::~CPathsInfo() +{ + for(int i = 0; i < sizes.x; i++) + { + for (int j = 0; j < sizes.y; j++) + { + delete [] nodes[i][j]; + } + delete [] nodes[i]; + } + delete [] nodes; +} \ No newline at end of file diff --git a/lib/CGameState.h b/lib/CGameState.h index 8536e5db5..1cae2c4c1 100644 --- a/lib/CGameState.h +++ b/lib/CGameState.h @@ -48,7 +48,7 @@ struct SetObjectProperty; struct MetaString; struct CPack; class CSpell; - +struct TerrainTile; namespace boost { @@ -244,13 +244,13 @@ struct CPathNode struct CGPathNode { - enum {ACCESSIBLE=1, VISITABLE, BLOCKVIS, BLOCKED}; //BLOCKVIS - visitable from neighbourign tile but not passable - ui8 land; + enum {ACCESSIBLE=1, VISITABLE, BLOCKVIS, BLOCKED}; //BLOCKVIS - visitable from neighbouring tile but not passable ui8 accessible; //the enum above + ui8 land; ui8 turns; ui32 moveRemains; - CPathNode * theNodeBefore; - int3 coord; //coordiantes + CGPathNode * theNodeBefore; + int3 coord; //coordinates CGPathNode(); }; @@ -264,13 +264,22 @@ struct DLL_EXPORT CPath void convert(ui8 mode); //mode=0 -> from 'manifest' to 'object' }; -struct CPathsInfo +struct DLL_EXPORT CGPath +{ + std::vector nodes; //just get node by node + + int3 startPos() const; // start point + int3 endPos() const; //destination point + void convert(ui8 mode); //mode=0 -> from 'manifest' to 'object' +}; + +struct DLL_EXPORT CPathsInfo { int3 sizes; CGPathNode ***nodes; //[w][h][level] - void getPath(const int3 &src, const int3 &dst, CPath &out); - CPathsInfo(const int3 &sizes); + bool getPath(const int3 &dst, CGPath &out); + CPathsInfo(const int3 &Sizes); ~CPathsInfo(); }; @@ -321,17 +330,18 @@ public: UpgradeInfo getUpgradeInfo(CArmedInstance *obj, int stackPos); float getMarketEfficiency(int player, int mode=0); int canBuildStructure(const CGTownInstance *t, int ID);// 0 - no more than one capitol, 1 - lack of water, 2 - forbidden, 3 - Add another level to Mage Guild, 4 - already built, 5 - cannot build, 6 - cannot afford, 7 - build, 8 - lack of requirements - bool checkForVisitableDir(const int3 & src, const int3 & dst) const; //check if dst tile is visitable from dst tile + bool checkForVisitableDir(const int3 & src, const int3 & dst) const; //check if src tile is visitable from dst tile + bool checkForVisitableDir(const int3 & src, const TerrainTile *pom, const int3 & dst) const; //check if src tile is visitable from dst tile bool getPath(int3 src, int3 dest, const CGHeroInstance * hero, CPath &ret); //calculates path between src and dest; returns pointer to newly allocated CPath or NULL if path does not exists - void calculatePaths(const CGHeroInstance *hero, CPathsInfo &out, const int3 &src = int3(-1,-1,-1)); //calculates path between src and dest; returns pointer to newly allocated CPath or NULL if path does not exists + void calculatePaths(const CGHeroInstance *hero, CPathsInfo &out, int3 src = int3(-1,-1,-1), int movement = -1); //calculates possible paths for hero, by default uses current hero position and movement left; returns pointer to newly allocated CPath or NULL if path does not exists bool isVisible(int3 pos, int player); bool isVisible(const CGObjectInstance *obj, int player); CGameState(); //c-tor ~CGameState(); //d-tor - void getNeighbours(int3 tile, std::vector &vec, const boost::logic::tribool &onLand); - int getMovementCost(const CGHeroInstance *h, int3 src, int3 dest, int remainingMovePoints=-1, bool checkLast=true); + void getNeighbours(const TerrainTile &srct, int3 tile, std::vector &vec, const boost::logic::tribool &onLand); + int getMovementCost(const CGHeroInstance *h, const int3 &src, const int3 &dest, int remainingMovePoints=-1, bool checkLast=true); int getDate(int mode=0) const; //mode=0 - total days in game, mode=1 - day of week, mode=2 - current week, mode=3 - current month template void serialize(Handler &h, const int version) { diff --git a/lib/NetPacks.h b/lib/NetPacks.h index 29c12e5ad..94c1a2714 100644 --- a/lib/NetPacks.h +++ b/lib/NetPacks.h @@ -1302,6 +1302,7 @@ struct SetSelection : public CPackForClient, public CPackForServer //514 SetSelection(){CPackForClient::type = 514;}; DLL_EXPORT void applyGs(CGameState *gs); bool applyGh(CGameHandler *gh); + void applyCl(CClient *cl); ui8 player; ui32 id;