diff --git a/client/CPlayerInterface.cpp b/client/CPlayerInterface.cpp index c70505fce..4543e2ce7 100644 --- a/client/CPlayerInterface.cpp +++ b/client/CPlayerInterface.cpp @@ -756,7 +756,7 @@ void CPlayerInterface::heroMoved(const TryMoveHero & details) stillMoveHero.setn(STOP_MOVE); break; case SDL_KEYDOWN: - if(ev->key.keysym.sym < SDLK_F1) + if(ev->key.keysym.sym < SDLK_F1 || ev->key.keysym.sym > SDLK_F15) stillMoveHero.setn(STOP_MOVE); break; } diff --git a/client/Graphics.cpp b/client/Graphics.cpp index b5d4040f4..1ad1afce4 100644 --- a/client/Graphics.cpp +++ b/client/Graphics.cpp @@ -326,7 +326,7 @@ void Graphics::loadHeroAnims() void Graphics::loadHeroAnim( const std::string &name, const std::vector > &rotations, std::vector Graphics::*dst ) { CDefEssential *anim = CDefHandler::giveDefEss(name); - heroAnims.push_back(anim); + (this->*dst).push_back(anim); int pom = 0; //how many groups has been rotated for(int o=7; pom<6; ++o) { diff --git a/client/NetPacksClient.cpp b/client/NetPacksClient.cpp index 99c9da690..758acbd33 100644 --- a/client/NetPacksClient.cpp +++ b/client/NetPacksClient.cpp @@ -140,7 +140,7 @@ void RemoveObject::applyCl( CClient *cl ) void TryMoveHero::applyFirstCl( CClient *cl ) { - if(result == TELEPORTATION) + if(result == TELEPORTATION || result == EMBARK) CGI->mh->removeObject(GS(cl)->getHero(id)); } @@ -148,7 +148,7 @@ void TryMoveHero::applyCl( CClient *cl ) { const CGHeroInstance *h = cl->getHero(id); - if(result == TELEPORTATION) + if(result == TELEPORTATION || result == EMBARK) CGI->mh->printObject(h); int player = h->tempOwner; diff --git a/config/TERCOSTS.TXT b/config/TERCOSTS.TXT index af3dda32d..a06c315ac 100644 --- a/config/TERCOSTS.TXT +++ b/config/TERCOSTS.TXT @@ -1,10 +1,10 @@ 9 -10 100 150 100 150 175 125 100 100 -1 -1 -10 100 150 100 150 175 125 100 100 -1 -1 -10 100 150 100 100 175 125 100 100 -1 -1 -10 100 150 100 150 175 125 100 100 -1 -1 -10 100 150 100 150 175 125 100 100 -1 -1 -10 100 150 100 150 175 125 100 100 -1 -1 -10 100 150 100 150 175 100 100 100 -1 -1 -10 100 150 100 150 100 125 100 100 -1 -1 -10 100 150 100 150 175 125 100 100 -1 -1 \ No newline at end of file +10 100 150 100 150 175 125 100 100 100 -1 +10 100 150 100 150 175 125 100 100 100 -1 +10 100 150 100 100 175 125 100 100 100 -1 +10 100 150 100 150 175 125 100 100 100 -1 +10 100 150 100 150 175 125 100 100 100 -1 +10 100 150 100 150 175 125 100 100 100 -1 +10 100 150 100 150 175 100 100 100 100 -1 +10 100 150 100 150 100 125 100 100 100 -1 +10 100 150 100 150 175 125 100 100 100 -1 \ No newline at end of file diff --git a/hch/CObjectHandler.cpp b/hch/CObjectHandler.cpp index d50e7fb16..15959d406 100644 --- a/hch/CObjectHandler.cpp +++ b/hch/CObjectHandler.cpp @@ -373,7 +373,7 @@ si32 CGHeroInstance::manaLimit() const bool CGHeroInstance::canWalkOnSea() const { - //TODO: write it - it should check if hero is flying, or something similiar + //TODO: write it - it should check if hero is flying, or something similar return false; } int CGHeroInstance::getPrimSkillLevel(int id) const @@ -492,6 +492,7 @@ CGHeroInstance::CGHeroInstance() exp = 0xffffffff; visitedTown = NULL; type = NULL; + boat = NULL; secSkills.push_back(std::make_pair(-1, -1)); } @@ -3207,3 +3208,9 @@ void CGOnceVisitable::searchTomb(const CGHeroInstance *h, ui32 accept) const cb->setObjProperty(id,10,h->getOwner()); } } + +void CGBoat::initObj() +{ + defInfo->visitDir = 0xff; + hero = NULL; +} \ No newline at end of file diff --git a/hch/CObjectHandler.h b/hch/CObjectHandler.h index 64a78d5ec..df2d48241 100644 --- a/hch/CObjectHandler.h +++ b/hch/CObjectHandler.h @@ -42,6 +42,7 @@ class CGDefInfo; class CSpecObjInfo; struct TerrainTile; struct InfoWindow; +class CGBoat; class DLL_EXPORT CCastleEvent { @@ -203,6 +204,7 @@ public: ui8 sex; ui8 inTownGarrison; // if hero is in town garrison CGTownInstance * visitedTown; //set if hero is visiting town or in the town garrison + CGBoat *boat; //set to CGBoat when sailing std::vector artifacts; //hero's artifacts from bag std::map artifWorn; //map; positions: 0 - head; 1 - shoulders; 2 - neck; 3 - right hand; 4 - left hand; 5 - torso; 6 - right ring; 7 - left ring; 8 - feet; 9 - misc1; 10 - misc2; 11 - misc3; 12 - misc4; 13 - mach1; 14 - mach2; 15 - mach3; 16 - mach4; 17 - spellbook; 18 - misc5 std::set spells; //known spells (spell IDs) @@ -722,6 +724,24 @@ public: } }; +class DLL_EXPORT CGBoat : public CGObjectInstance +{ +public: + ui8 direction; + const CGHeroInstance *hero; //hero on board + + void initObj(); + + CGBoat() + { + direction = 4; + } + template void serialize(Handler &h, const int version) + { + h & static_cast(*this) & direction; + } +}; + class DLL_EXPORT CGOnceVisitable : public CPlayersVisited //wagon, corpse, lean to, warriors tomb { public: diff --git a/lib/CGameState.cpp b/lib/CGameState.cpp index 615b221f2..e029bc107 100644 --- a/lib/CGameState.cpp +++ b/lib/CGameState.cpp @@ -1484,61 +1484,21 @@ void CGameState::loadTownDInfos() void CGameState::getNeighbours(int3 tile, std::vector &vec, const boost::logic::tribool &onLand) { - /* notation: - * 1 2 3 - * 4 5 6 - * 7 8 9 - */ + 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(); - int3 hlp; - bool weAreOnLand = (map->getTile(tile).tertype != 8); - if(tile.y < map->height-1) //8 + for (size_t i = 0; i < ARRAY_COUNT(dirs); i++) { - hlp = int3(tile.x,tile.y+1,tile.z); - if((weAreOnLand == (map->getTile(hlp).tertype!=8)) && map->getTile(hlp).tertype!=9) - vec.push_back(hlp); - } - if(tile.y > 0) //2 - { - hlp = int3(tile.x,tile.y-1,tile.z); - if((weAreOnLand == (map->getTile(hlp).tertype!=8)) && map->getTile(hlp).tertype!=9) - vec.push_back(hlp); - } - if(tile.x > 0) //4 - { - hlp = int3(tile.x-1,tile.y,tile.z); - if((weAreOnLand == (map->getTile(hlp).tertype!=8)) && map->getTile(hlp).tertype!=9) - vec.push_back(hlp); - } - if(tile.x < map->width-1) //6 - { - hlp = int3(tile.x+1,tile.y,tile.z); - if((weAreOnLand == (map->getTile(hlp).tertype!=8)) && map->getTile(hlp).tertype!=9) - vec.push_back(hlp); - } - if(tile.x > 0 && tile.y > 0) //1 - { - hlp = int3(tile.x-1,tile.y-1,tile.z); - if((weAreOnLand == (map->getTile(hlp).tertype!=8)) && map->getTile(hlp).tertype!=9) - vec.push_back(hlp); - } - if(tile.x > 0 && tile.y < map->height-1) //7 - { - hlp = int3(tile.x-1,tile.y+1,tile.z); - if((weAreOnLand == (map->getTile(hlp).tertype!=8)) && map->getTile(hlp).tertype!=9) - vec.push_back(hlp); - } - if(tile.x < map->width-1 && tile.y > 0) //3 - { - hlp = int3(tile.x+1,tile.y-1,tile.z); - if((weAreOnLand == (map->getTile(hlp).tertype!=8)) && map->getTile(hlp).tertype!=9) - vec.push_back(hlp); - } - if(tile.x < map->width-1 && tile.y < map->height-1) //9 - { - hlp = int3(tile.x+1,tile.y+1,tile.z); - if((weAreOnLand == (map->getTile(hlp).tertype!=8)) && map->getTile(hlp).tertype!=9) + int3 hlp = tile + dirs[i]; + if(!map->isInTheMap(hlp)) + continue; + + if((indeterminate(onLand) || onLand == (map->getTile(hlp).tertype!=8) ) + && map->getTile(hlp).tertype!=9) + { vec.push_back(hlp); + } } } @@ -1557,7 +1517,7 @@ int CGameState::getMovementCost(const CGHeroInstance *h, int3 src, int3 dest, in { int old = ret; ret *= 1.414; - //diagonal move costs too much but normal move is possible - allow diagonal move + //diagonal move costs too much but normal move is possible - allow diagonal move for remaining move points if(ret > remainingMovePoints && remainingMovePoints > old) { return remainingMovePoints; @@ -1569,7 +1529,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,true); + getNeighbours(dest, vec, s.tertype != TerrainTile::water); for(size_t i=0; i < vec.size(); i++) { int fcost = getMovementCost(h,dest,vec[i],left,false); @@ -1676,10 +1636,6 @@ bool CGameState::getPath(int3 src, int3 dest, const CGHeroInstance * hero, CPath CPathNode &node = graph[i][j]; node.accesible = !tinfo->blocked; - if(i==dest.x && j==dest.y && tinfo->visitable) - { - node.accesible = true; //for allowing visiting objects - } node.dist = -1; node.theNodeBefore = NULL; node.visited = false; @@ -1697,6 +1653,35 @@ bool CGameState::getPath(int3 src, int3 dest, const CGHeroInstance * hero, CPath } } } + + + //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.accesible = true; //for allowing visiting objects + } + + if(blockLandSea && 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) //it's a Boat + break; + + d.accesible = (i < t->visitableObjects.size()); //dest is accessible only if there is boat + } + else if(!blockLandSea && t->tertype != TerrainTile::water) //hero is moving by water + { + d.accesible = (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 @@ -1726,7 +1711,7 @@ bool CGameState::getPath(int3 src, int3 dest, const CGHeroInstance * hero, CPath } //add accessible neighbouring nodes to the queue - getNeighbours(cp.coord,neighbours,blockLandSea); + getNeighbours(cp.coord, neighbours, boost::logic::indeterminate); for(unsigned int i=0; i < neighbours.size(); i++) { CPathNode & dp = graph[neighbours[i].x][neighbours[i].y]; diff --git a/lib/NetPacks.h b/lib/NetPacks.h index 814623d6e..2c08242d5 100644 --- a/lib/NetPacks.h +++ b/lib/NetPacks.h @@ -394,7 +394,7 @@ struct TryMoveHero : public CPackForClient //501 enum EResult { - FAILED, SUCCESS, TELEPORTATION, RESERVED___, BLOCKING_VISIT + FAILED, SUCCESS, TELEPORTATION, RESERVED___, BLOCKING_VISIT, EMBARK, DISEMBARK }; ui32 id, movePoints; diff --git a/lib/NetPacksLib.cpp b/lib/NetPacksLib.cpp index fcb2c9b72..56f13974c 100644 --- a/lib/NetPacksLib.cpp +++ b/lib/NetPacksLib.cpp @@ -287,7 +287,19 @@ void TryMoveHero::applyGs( CGameState *gs ) { CGHeroInstance *h = gs->getHero(id); h->movement = movePoints; - if(start!=end && (result == SUCCESS || result == TELEPORTATION)) + + if(result == EMBARK) //hero enters boat at dest tile + { + const TerrainTile &tt = gs->map->getTile(CGHeroInstance::convertPosition(end, false)); + assert(tt.visitableObjects.size() == 1 && tt.visitableObjects.front()->ID == 8); //the only vis obj at dest is Boat + CGBoat *boat = static_cast(tt.visitableObjects.front()); + + gs->map->removeBlockVisTiles(boat); //hero blockvis mask will be used, we don't need to duplicate it with boat + h->boat = boat; + boat->hero = h; + } + + if(start!=end && (result == SUCCESS || result == TELEPORTATION || result == EMBARK)) { gs->map->removeBlockVisTiles(h); h->pos = end; @@ -297,7 +309,7 @@ void TryMoveHero::applyGs( CGameState *gs ) BOOST_FOREACH(int3 t, fowRevealed) gs->getPlayer(h->getOwner())->fogOfWarMap[t.x][t.y][t.z] = 1; - if(result == SUCCESS || result == BLOCKING_VISIT) + if(result == SUCCESS || result == BLOCKING_VISIT || result == EMBARK) h->moveDir = getDir(start,end); } diff --git a/lib/RegisterTypes.cpp b/lib/RegisterTypes.cpp index 7a89454df..6076c35c8 100644 --- a/lib/RegisterTypes.cpp +++ b/lib/RegisterTypes.cpp @@ -42,6 +42,7 @@ void registerTypes1(Serializer &s) s.template registerType(); s.template registerType(); s.template registerType(); + s.template registerType(); s.template registerType(); s.template registerType(); } diff --git a/lib/map.cpp b/lib/map.cpp index 60ead7fab..b2762ac4c 100644 --- a/lib/map.cpp +++ b/lib/map.cpp @@ -1860,6 +1860,11 @@ void Mapa::readObjects( unsigned char * bufor, int &i) nobj = new CGOnceVisitable(); break; } + case 8: //Boat + { + nobj = new CGBoat(); + break; + } case 214: //hero placeholder { i+=3; //TODO: handle it more properly diff --git a/mapHandler.cpp b/mapHandler.cpp index 4b4c934fe..77fd17986 100644 --- a/mapHandler.cpp +++ b/mapHandler.cpp @@ -784,7 +784,9 @@ void CMapHandler::terrainRect(int3 top_tile, unsigned char anim, std::vector< st SDL_Surface * tb; if(themp->type==NULL) continue; - std::vector & iv = graphics->heroAnims[themp->type->heroType]->ourImages; + std::vector & iv = (themp->boat) + ? graphics->boatAnims[themp->boat->subID]->ourImages + : graphics->heroAnims[themp->type->heroType]->ourImages; if(!themp->isStanding) //hero is moving { diff --git a/server/CGameHandler.cpp b/server/CGameHandler.cpp index 518568225..9203d2885 100644 --- a/server/CGameHandler.cpp +++ b/server/CGameHandler.cpp @@ -1161,16 +1161,31 @@ bool CGameHandler::moveHero( si32 hid, int3 dst, ui8 instant, ui8 asker /*= 255* tmh.movePoints = h->movement; //check if destination tile is available - if( t.tertype == TerrainTile::rock - || (!h->canWalkOnSea() && t.tertype == TerrainTile::water) - || (t.blocked && !t.visitable) //tile is blocked andnot visitable - ) + + //it's a rock or blocked and not visitable tile + //OR hero is on land and dest is water and (there is not present only one object - boat) + if((t.tertype == TerrainTile::rock || (t.blocked && !t.visitable)) + && complain("Cannot move hero, destination tile is blocked!") + || (!h->boat && !h->canWalkOnSea() && t.tertype == TerrainTile::water && (t.visitableObjects.size() != 1 || t.visitableObjects.front()->ID != 8)) + && complain("Cannot move hero, destination tile is on water!")) { - tlog2 << "Cannot move hero, destination tile is blocked!\n"; + //send info about movement failure sendAndApply(&tmh); return false; } + //hero enters the boat + if(!h->boat && t.visitableObjects.size() && t.visitableObjects.front()->ID == 8) + { + tmh.result = TryMoveHero::EMBARK; + tmh.movePoints = 0; //embarking takes all move points + //TODO: check for bonus that removes that penalty + + getTilesInRange(tmh.fowRevealed,h->getSightCenter()+(tmh.end-tmh.start),h->getSightRadious(),h->tempOwner,1); + sendAndApply(&tmh); + return true; + } + //checks for standard movement if(!instant) {