Browse Source

First part of Boat and sailing support.
[It's possible to enter the boat and move by sea, graphical glitches removal and disembarking will be done soon]

Michał W. Urbańczyk 16 years ago
parent
commit
2ca7cc5b5c

+ 1 - 1
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;
 			}

+ 1 - 1
client/Graphics.cpp

@@ -326,7 +326,7 @@ void Graphics::loadHeroAnims()
 void Graphics::loadHeroAnim( const std::string &name, const std::vector<std::pair<int,int> > &rotations, std::vector<CDefEssential *> 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)
 	{

+ 2 - 2
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;
 

+ 9 - 9
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
+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

+ 8 - 1
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;
+}

+ 20 - 0
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<ui32> artifacts; //hero's artifacts from bag
 	std::map<ui16,ui32> artifWorn; //map<position,artifact_id>; 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<ui32> 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 <typename Handler> void serialize(Handler &h, const int version)
+	{
+		h & static_cast<CGObjectInstance&>(*this) & direction;
+	}
+};
+
 class DLL_EXPORT CGOnceVisitable : public CPlayersVisited //wagon, corpse, lean to, warriors tomb
 {
 public:

+ 44 - 59
lib/CGameState.cpp

@@ -1484,61 +1484,21 @@ void CGameState::loadTownDInfos()
 
 void CGameState::getNeighbours(int3 tile, std::vector<int3> &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
-	{
-		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
+	for (size_t i = 0; i < ARRAY_COUNT(dirs); i++)
 	{
-		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<int3> 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];

+ 1 - 1
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;

+ 14 - 2
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<CGBoat*>(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);
 }
 

+ 1 - 0
lib/RegisterTypes.cpp

@@ -42,6 +42,7 @@ void registerTypes1(Serializer &s)
 	s.template registerType<CGBonusingObject>();
 	s.template registerType<CGMagicWell>();
 	s.template registerType<CGObservatory>();
+	s.template registerType<CGBoat>();
 	s.template registerType<CGOnceVisitable>();
 	s.template registerType<CGObjectInstance>();
 }

+ 5 - 0
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

+ 3 - 1
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<Cimage> & iv = graphics->heroAnims[themp->type->heroType]->ourImages;
+					std::vector<Cimage> & iv = (themp->boat) 
+												? graphics->boatAnims[themp->boat->subID]->ourImages
+												: graphics->heroAnims[themp->type->heroType]->ourImages;
 
 					if(!themp->isStanding) //hero is moving
 					{

+ 20 - 5
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)
 	{