1
0
mirror of https://github.com/vcmi/vcmi.git synced 2025-07-17 01:32:21 +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:
Michał W. Urbańczyk
2009-07-19 01:00:19 +00:00
parent 601117f440
commit 2ca7cc5b5c
13 changed files with 129 additions and 82 deletions

View File

@ -756,7 +756,7 @@ void CPlayerInterface::heroMoved(const TryMoveHero & details)
stillMoveHero.setn(STOP_MOVE); stillMoveHero.setn(STOP_MOVE);
break; break;
case SDL_KEYDOWN: 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); stillMoveHero.setn(STOP_MOVE);
break; break;
} }

View File

@ -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 ) 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); CDefEssential *anim = CDefHandler::giveDefEss(name);
heroAnims.push_back(anim); (this->*dst).push_back(anim);
int pom = 0; //how many groups has been rotated int pom = 0; //how many groups has been rotated
for(int o=7; pom<6; ++o) for(int o=7; pom<6; ++o)
{ {

View File

@ -140,7 +140,7 @@ void RemoveObject::applyCl( CClient *cl )
void TryMoveHero::applyFirstCl( CClient *cl ) void TryMoveHero::applyFirstCl( CClient *cl )
{ {
if(result == TELEPORTATION) if(result == TELEPORTATION || result == EMBARK)
CGI->mh->removeObject(GS(cl)->getHero(id)); CGI->mh->removeObject(GS(cl)->getHero(id));
} }
@ -148,7 +148,7 @@ void TryMoveHero::applyCl( CClient *cl )
{ {
const CGHeroInstance *h = cl->getHero(id); const CGHeroInstance *h = cl->getHero(id);
if(result == TELEPORTATION) if(result == TELEPORTATION || result == EMBARK)
CGI->mh->printObject(h); CGI->mh->printObject(h);
int player = h->tempOwner; int player = h->tempOwner;

View File

@ -1,10 +1,10 @@
9 9
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 -1 -1 10 100 150 100 150 175 125 100 100 100 -1
10 100 150 100 100 175 125 100 100 -1 -1 10 100 150 100 100 175 125 100 100 100 -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 -1 -1 10 100 150 100 150 175 125 100 100 100 -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 100 100 100 -1 -1 10 100 150 100 150 175 100 100 100 100 -1
10 100 150 100 150 100 125 100 100 -1 -1 10 100 150 100 150 100 125 100 100 100 -1
10 100 150 100 150 175 125 100 100 -1 -1 10 100 150 100 150 175 125 100 100 100 -1

View File

@ -373,7 +373,7 @@ si32 CGHeroInstance::manaLimit() const
bool CGHeroInstance::canWalkOnSea() 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; return false;
} }
int CGHeroInstance::getPrimSkillLevel(int id) const int CGHeroInstance::getPrimSkillLevel(int id) const
@ -492,6 +492,7 @@ CGHeroInstance::CGHeroInstance()
exp = 0xffffffff; exp = 0xffffffff;
visitedTown = NULL; visitedTown = NULL;
type = NULL; type = NULL;
boat = NULL;
secSkills.push_back(std::make_pair(-1, -1)); 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()); cb->setObjProperty(id,10,h->getOwner());
} }
} }
void CGBoat::initObj()
{
defInfo->visitDir = 0xff;
hero = NULL;
}

View File

@ -42,6 +42,7 @@ class CGDefInfo;
class CSpecObjInfo; class CSpecObjInfo;
struct TerrainTile; struct TerrainTile;
struct InfoWindow; struct InfoWindow;
class CGBoat;
class DLL_EXPORT CCastleEvent class DLL_EXPORT CCastleEvent
{ {
@ -203,6 +204,7 @@ public:
ui8 sex; ui8 sex;
ui8 inTownGarrison; // if hero is in town garrison ui8 inTownGarrison; // if hero is in town garrison
CGTownInstance * visitedTown; //set if hero is visiting town or in the 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::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::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) 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 class DLL_EXPORT CGOnceVisitable : public CPlayersVisited //wagon, corpse, lean to, warriors tomb
{ {
public: public:

View File

@ -1484,61 +1484,21 @@ void CGameState::loadTownDInfos()
void CGameState::getNeighbours(int3 tile, std::vector<int3> &vec, const boost::logic::tribool &onLand) void CGameState::getNeighbours(int3 tile, std::vector<int3> &vec, const boost::logic::tribool &onLand)
{ {
/* notation: int3 dirs[] = { int3(0,1,0),int3(0,-1,0),int3(-1,0,0),int3(+1,0,0),
* 1 2 3 int3(1,1,0),int3(-1,1,0),int3(1,-1,0),int3(-1,-1,0) };
* 4 5 6
* 7 8 9
*/
vec.clear(); vec.clear();
int3 hlp; for (size_t i = 0; i < ARRAY_COUNT(dirs); i++)
bool weAreOnLand = (map->getTile(tile).tertype != 8);
if(tile.y < map->height-1) //8
{ {
hlp = int3(tile.x,tile.y+1,tile.z); int3 hlp = tile + dirs[i];
if((weAreOnLand == (map->getTile(hlp).tertype!=8)) && map->getTile(hlp).tertype!=9) if(!map->isInTheMap(hlp))
vec.push_back(hlp); continue;
}
if(tile.y > 0) //2 if((indeterminate(onLand) || onLand == (map->getTile(hlp).tertype!=8) )
{ && map->getTile(hlp).tertype!=9)
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)
vec.push_back(hlp); vec.push_back(hlp);
}
} }
} }
@ -1557,7 +1517,7 @@ int CGameState::getMovementCost(const CGHeroInstance *h, int3 src, int3 dest, in
{ {
int old = ret; int old = ret;
ret *= 1.414; 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) if(ret > remainingMovePoints && remainingMovePoints > old)
{ {
return remainingMovePoints; 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 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; std::vector<int3> vec;
getNeighbours(dest,vec,true); getNeighbours(dest, vec, s.tertype != TerrainTile::water);
for(size_t i=0; i < vec.size(); i++) for(size_t i=0; i < vec.size(); i++)
{ {
int fcost = getMovementCost(h,dest,vec[i],left,false); 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]; CPathNode &node = graph[i][j];
node.accesible = !tinfo->blocked; node.accesible = !tinfo->blocked;
if(i==dest.x && j==dest.y && tinfo->visitable)
{
node.accesible = true; //for allowing visiting objects
}
node.dist = -1; node.dist = -1;
node.theNodeBefore = NULL; node.theNodeBefore = NULL;
node.visited = false; 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 //graph initialized
//initial tile - set cost on 0 and add to the queue //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 //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++) for(unsigned int i=0; i < neighbours.size(); i++)
{ {
CPathNode & dp = graph[neighbours[i].x][neighbours[i].y]; CPathNode & dp = graph[neighbours[i].x][neighbours[i].y];

View File

@ -394,7 +394,7 @@ struct TryMoveHero : public CPackForClient //501
enum EResult enum EResult
{ {
FAILED, SUCCESS, TELEPORTATION, RESERVED___, BLOCKING_VISIT FAILED, SUCCESS, TELEPORTATION, RESERVED___, BLOCKING_VISIT, EMBARK, DISEMBARK
}; };
ui32 id, movePoints; ui32 id, movePoints;

View File

@ -287,7 +287,19 @@ void TryMoveHero::applyGs( CGameState *gs )
{ {
CGHeroInstance *h = gs->getHero(id); CGHeroInstance *h = gs->getHero(id);
h->movement = movePoints; 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); gs->map->removeBlockVisTiles(h);
h->pos = end; h->pos = end;
@ -297,7 +309,7 @@ void TryMoveHero::applyGs( CGameState *gs )
BOOST_FOREACH(int3 t, fowRevealed) BOOST_FOREACH(int3 t, fowRevealed)
gs->getPlayer(h->getOwner())->fogOfWarMap[t.x][t.y][t.z] = 1; 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); h->moveDir = getDir(start,end);
} }

View File

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

View File

@ -1860,6 +1860,11 @@ void Mapa::readObjects( unsigned char * bufor, int &i)
nobj = new CGOnceVisitable(); nobj = new CGOnceVisitable();
break; break;
} }
case 8: //Boat
{
nobj = new CGBoat();
break;
}
case 214: //hero placeholder case 214: //hero placeholder
{ {
i+=3; //TODO: handle it more properly i+=3; //TODO: handle it more properly

View File

@ -784,7 +784,9 @@ void CMapHandler::terrainRect(int3 top_tile, unsigned char anim, std::vector< st
SDL_Surface * tb; SDL_Surface * tb;
if(themp->type==NULL) if(themp->type==NULL)
continue; 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 if(!themp->isStanding) //hero is moving
{ {

View File

@ -1161,16 +1161,31 @@ bool CGameHandler::moveHero( si32 hid, int3 dst, ui8 instant, ui8 asker /*= 255*
tmh.movePoints = h->movement; tmh.movePoints = h->movement;
//check if destination tile is available //check if destination tile is available
if( t.tertype == TerrainTile::rock
|| (!h->canWalkOnSea() && t.tertype == TerrainTile::water) //it's a rock or blocked and not visitable tile
|| (t.blocked && !t.visitable) //tile is blocked andnot visitable //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); sendAndApply(&tmh);
return false; 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 //checks for standard movement
if(!instant) if(!instant)
{ {