1
0
mirror of https://github.com/vcmi/vcmi.git synced 2024-12-24 22:14:36 +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);
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;
}

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 )
{
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)
{

View File

@ -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;

View File

@ -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

View File

@ -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;
}

View File

@ -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:

View File

@ -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++)
{
int3 hlp = tile + dirs[i];
if(!map->isInTheMap(hlp))
continue;
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.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)
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];

View File

@ -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;

View File

@ -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);
}

View File

@ -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>();
}

View File

@ -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

View File

@ -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
{

View File

@ -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)
{