mirror of
				https://github.com/vcmi/vcmi.git
				synced 2025-10-31 00:07:39 +02:00 
			
		
		
		
	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]
This commit is contained in:
		| @@ -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; | ||||
| 			} | ||||
|   | ||||
| @@ -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) | ||||
| 	{ | ||||
|   | ||||
| @@ -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; | ||||
|  | ||||
|   | ||||
| @@ -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 | ||||
| @@ -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; | ||||
| } | ||||
| @@ -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: | ||||
|   | ||||
| @@ -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 | ||||
| 	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<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]; | ||||
|   | ||||
| @@ -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; | ||||
|   | ||||
| @@ -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); | ||||
| } | ||||
|  | ||||
|   | ||||
| @@ -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>(); | ||||
| } | ||||
|   | ||||
| @@ -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 | ||||
|   | ||||
| @@ -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 | ||||
| 					{ | ||||
|   | ||||
| @@ -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) | ||||
| 	{ | ||||
|   | ||||
		Reference in New Issue
	
	Block a user