mirror of
https://github.com/vcmi/vcmi.git
synced 2024-12-24 22:14:36 +02:00
Merge pull request #133 from vcmi/feature/pathfinderLayers
Pathfinder layers and related changes: fly and water walking implemented
This commit is contained in:
commit
a851062891
@ -372,7 +372,7 @@ int3 whereToExplore(HeroPtr h)
|
||||
{
|
||||
int3 op = obj->visitablePos();
|
||||
CGPath p;
|
||||
ai->myCb->getPathsInfo(h.get())->getPath(op, p);
|
||||
ai->myCb->getPathsInfo(h.get())->getPath(p, op);
|
||||
if (p.nodes.size() && p.endPos() == op && p.nodes.size() <= DIST_LIMIT)
|
||||
if (ai->isGoodForVisit(obj, h, sm))
|
||||
nearbyVisitableObjs.push_back(obj);
|
||||
|
@ -421,7 +421,7 @@ float FuzzyHelper::evaluate (Goals::VisitTile & g)
|
||||
|
||||
//assert(cb->isInTheMap(g.tile));
|
||||
float turns = 0;
|
||||
float distance = cb->getMovementCost(g.hero.h, g.tile);
|
||||
float distance = CPathfinderHelper::getMovementCost(g.hero.h, g.tile);
|
||||
if (!distance) //we stand on that tile
|
||||
turns = 0;
|
||||
else
|
||||
@ -530,4 +530,4 @@ float FuzzyHelper::evaluate (Goals::AbstractGoal & g)
|
||||
void FuzzyHelper::setPriority (Goals::TSubgoal & g)
|
||||
{
|
||||
g->setpriority(g->accept(this)); //this enforces returned value is set
|
||||
}
|
||||
}
|
||||
|
@ -1840,7 +1840,7 @@ bool VCAI::moveHeroToTile(int3 dst, HeroPtr h)
|
||||
else
|
||||
{
|
||||
CGPath path;
|
||||
cb->getPathsInfo(h.get())->getPath(dst, path);
|
||||
cb->getPathsInfo(h.get())->getPath(path, dst);
|
||||
if(path.nodes.empty())
|
||||
{
|
||||
logAi->errorStream() << "Hero " << h->name << " cannot reach " << dst;
|
||||
@ -1915,6 +1915,8 @@ bool VCAI::moveHeroToTile(int3 dst, HeroPtr h)
|
||||
{ // Hero should be able to go through object if it's allow transit
|
||||
doMovement(endpos, true);
|
||||
}
|
||||
else if(path.nodes[i-1].layer == EPathfindingLayer::AIR)
|
||||
doMovement(endpos, true);
|
||||
else
|
||||
doMovement(endpos, false);
|
||||
|
||||
@ -2519,7 +2521,7 @@ int3 VCAI::explorationNewPoint(HeroPtr h)
|
||||
continue;
|
||||
|
||||
CGPath path;
|
||||
cb->getPathsInfo(hero)->getPath(tile, path);
|
||||
cb->getPathsInfo(hero)->getPath(path, tile);
|
||||
float ourValue = (float)howManyTilesWillBeDiscovered(tile, radius, cbp) / (path.nodes.size() + 1); //+1 prevents erratic jumps
|
||||
|
||||
if (ourValue > bestValue) //avoid costly checks of tiles that don't reveal much
|
||||
|
@ -290,11 +290,6 @@ bool CCallback::canMoveBetween(const int3 &a, const int3 &b)
|
||||
return gs->checkForVisitableDir(a, b) && gs->checkForVisitableDir(b, a);
|
||||
}
|
||||
|
||||
int CCallback::getMovementCost(const CGHeroInstance * hero, int3 dest)
|
||||
{
|
||||
return gs->getMovementCost(hero, hero->visitablePos(), dest, hero->movement);
|
||||
}
|
||||
|
||||
const CPathsInfo * CCallback::getPathsInfo(const CGHeroInstance *h)
|
||||
{
|
||||
return cl->getPathsInfo(h);
|
||||
|
@ -104,7 +104,6 @@ public:
|
||||
|
||||
//client-specific functionalities (pathfinding)
|
||||
virtual bool canMoveBetween(const int3 &a, const int3 &b);
|
||||
virtual int getMovementCost(const CGHeroInstance * hero, int3 dest);
|
||||
virtual int3 getGuardingCreaturePosition(int3 tile);
|
||||
virtual const CPathsInfo * getPathsInfo(const CGHeroInstance *h);
|
||||
|
||||
|
12
Global.h
12
Global.h
@ -95,6 +95,18 @@ static_assert(sizeof(bool) == 1, "Bool needs to be 1 byte in size.");
|
||||
# define NOMINMAX // Exclude min/max macros from <Windows.h>. Use std::[min/max] from <algorithm> instead.
|
||||
#endif
|
||||
|
||||
/* ---------------------------------------------------------------------------- */
|
||||
/* A macro to force inlining some of our functions */
|
||||
/* ---------------------------------------------------------------------------- */
|
||||
// Compiler (at least MSVC) is not so smart here-> without that displaying is MUCH slower
|
||||
#ifdef _MSC_VER
|
||||
# define STRONG_INLINE __forceinline
|
||||
#elif __GNUC__
|
||||
# define STRONG_INLINE inline __attribute__((always_inline))
|
||||
#else
|
||||
# define STRONG_INLINE inline
|
||||
#endif
|
||||
|
||||
#define _USE_MATH_DEFINES
|
||||
|
||||
#include <cstdio>
|
||||
|
@ -1288,7 +1288,7 @@ template <typename Handler> void CPlayerInterface::serializeTempl( Handler &h, c
|
||||
for(auto &p : pathsMap)
|
||||
{
|
||||
CGPath path;
|
||||
cb->getPathsInfo(p.first)->getPath(p.second, path);
|
||||
cb->getPathsInfo(p.first)->getPath(path, p.second);
|
||||
paths[p.first] = path;
|
||||
logGlobal->traceStream() << boost::format("Restored path for hero %s leading to %s with %d nodes")
|
||||
% p.first->nodeName() % p.second % path.nodes.size();
|
||||
@ -2226,7 +2226,7 @@ CGPath * CPlayerInterface::getAndVerifyPath(const CGHeroInstance * h)
|
||||
{
|
||||
assert(h->getPosition(false) == path.startPos());
|
||||
//update the hero path in case of something has changed on map
|
||||
if(LOCPLINT->cb->getPathsInfo(h)->getPath(path.endPos(), path))
|
||||
if(LOCPLINT->cb->getPathsInfo(h)->getPath(path, path.endPos()))
|
||||
return &path;
|
||||
else
|
||||
paths.erase(h);
|
||||
@ -2642,7 +2642,18 @@ void CPlayerInterface::doMoveHero(const CGHeroInstance * h, CGPath path)
|
||||
ETerrainType newTerrain;
|
||||
int sh = -1;
|
||||
|
||||
for(i=path.nodes.size()-1; i>0 && (stillMoveHero.data == CONTINUE_MOVE); i--)
|
||||
auto canStop = [&](CGPathNode * node) -> bool
|
||||
{
|
||||
if(node->layer == EPathfindingLayer::LAND || node->layer == EPathfindingLayer::SAIL)
|
||||
return true;
|
||||
|
||||
if(node->accessible == CGPathNode::ACCESSIBLE)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
};
|
||||
|
||||
for(i=path.nodes.size()-1; i>0 && (stillMoveHero.data == CONTINUE_MOVE || !canStop(&path.nodes[i])); i--)
|
||||
{
|
||||
int3 currentCoord = path.nodes[i].coord;
|
||||
int3 nextCoord = path.nodes[i-1].coord;
|
||||
@ -2683,18 +2694,21 @@ void CPlayerInterface::doMoveHero(const CGHeroInstance * h, CGPath path)
|
||||
int3 endpos(nextCoord.x, nextCoord.y, h->pos.z);
|
||||
logGlobal->traceStream() << "Requesting hero movement to " << endpos;
|
||||
|
||||
bool useTransit = false;
|
||||
if((i-2 >= 0) // Check there is node after next one; otherwise transit is pointless
|
||||
&& (CGTeleport::isConnected(nextObject, getObj(path.nodes[i-2].coord, false))
|
||||
|| CGTeleport::isTeleport(nextObject)))
|
||||
{ // Hero should be able to go through object if it's allow transit
|
||||
doMovement(endpos, true);
|
||||
useTransit = true;
|
||||
}
|
||||
else
|
||||
doMovement(endpos, false);
|
||||
else if(path.nodes[i-1].layer == EPathfindingLayer::AIR)
|
||||
useTransit = true;
|
||||
|
||||
doMovement(endpos, useTransit);
|
||||
|
||||
logGlobal->traceStream() << "Resuming " << __FUNCTION__;
|
||||
bool guarded = cb->isInTheMap(cb->getGuardingCreaturePosition(endpos - int3(1, 0, 0)));
|
||||
if(guarded || showingDialog->get() == true) // Abort movement if a guard was fought or there is a dialog to display (Mantis #1136)
|
||||
if((!useTransit && guarded) || showingDialog->get() == true) // Abort movement if a guard was fought or there is a dialog to display (Mantis #1136)
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -18,15 +18,6 @@
|
||||
#include "../../lib/GameConstants.h"
|
||||
|
||||
|
||||
//A macro to force inlining some of our functions. Compiler (at least MSVC) is not so smart here-> without that displaying is MUCH slower
|
||||
#ifdef _MSC_VER
|
||||
#define STRONG_INLINE __forceinline
|
||||
#elif __GNUC__
|
||||
#define STRONG_INLINE inline __attribute__((always_inline))
|
||||
#else
|
||||
#define STRONG_INLINE inline
|
||||
#endif
|
||||
|
||||
extern SDL_Window * mainWindow;
|
||||
extern SDL_Renderer * mainRenderer;
|
||||
extern SDL_Texture * screenTexture;
|
||||
|
@ -114,7 +114,7 @@ void CTerrainRect::clickRight(tribool down, bool previousState)
|
||||
adventureInt->tileRClicked(mp);
|
||||
}
|
||||
|
||||
void CTerrainRect::mouseMoved (const SDL_MouseMotionEvent & sEvent)
|
||||
void CTerrainRect::mouseMoved(const SDL_MouseMotionEvent & sEvent)
|
||||
{
|
||||
int3 tHovered = whichTileIsIt(sEvent.x,sEvent.y);
|
||||
int3 pom = adventureInt->verifyPos(tHovered);
|
||||
@ -126,11 +126,11 @@ void CTerrainRect::mouseMoved (const SDL_MouseMotionEvent & sEvent)
|
||||
}
|
||||
|
||||
if (pom != curHoveredTile)
|
||||
curHoveredTile=pom;
|
||||
curHoveredTile = pom;
|
||||
else
|
||||
return;
|
||||
|
||||
adventureInt->tileHovered(curHoveredTile);
|
||||
adventureInt->tileHovered(pom);
|
||||
}
|
||||
void CTerrainRect::hover(bool on)
|
||||
{
|
||||
@ -188,7 +188,7 @@ void CTerrainRect::showPath(const SDL_Rect * extRect, SDL_Surface * to)
|
||||
* is id1=7, id2=5 (pns[7][5])
|
||||
*/
|
||||
bool pathContinuous = curPos.areNeighbours(nextPos) && curPos.areNeighbours(prevPos);
|
||||
if(pathContinuous && cv[i].land == cv[i+1].land)
|
||||
if(pathContinuous && cv[i].action != CGPathNode::EMBARK && cv[i].action != CGPathNode::DISEMBARK)
|
||||
{
|
||||
int id1=(curPos.x-nextPos.x+1)+3*(curPos.y-nextPos.y+1); //Direction of entering vector
|
||||
int id2=(cv[i-1].coord.x-curPos.x+1)+3*(cv[i-1].coord.y-curPos.y+1); //Direction of exiting vector
|
||||
@ -1190,7 +1190,7 @@ void CAdvMapInt::keyPressed(const SDL_KeyboardEvent & key)
|
||||
CGPath &path = LOCPLINT->paths[h];
|
||||
terrain.currentPath = &path;
|
||||
int3 dst = h->getPosition(false) + dir;
|
||||
if(dst != verifyPos(dst) || !LOCPLINT->cb->getPathsInfo(h)->getPath(dst, path))
|
||||
if(dst != verifyPos(dst) || !LOCPLINT->cb->getPathsInfo(h)->getPath(path, dst))
|
||||
{
|
||||
terrain.currentPath = nullptr;
|
||||
return;
|
||||
@ -1445,7 +1445,7 @@ void CAdvMapInt::tileLClicked(const int3 &mapPos)
|
||||
{
|
||||
CGPath &path = LOCPLINT->paths[currentHero];
|
||||
terrain.currentPath = &path;
|
||||
bool gotPath = LOCPLINT->cb->getPathsInfo(currentHero)->getPath(mapPos, path); //try getting path, erase if failed
|
||||
bool gotPath = LOCPLINT->cb->getPathsInfo(currentHero)->getPath(path, mapPos); //try getting path, erase if failed
|
||||
updateMoveHero(currentHero);
|
||||
if (!gotPath)
|
||||
LOCPLINT->eraseCurrentPathOf(currentHero);
|
||||
@ -1467,7 +1467,8 @@ void CAdvMapInt::tileLClicked(const int3 &mapPos)
|
||||
|
||||
void CAdvMapInt::tileHovered(const int3 &mapPos)
|
||||
{
|
||||
if(mode != EAdvMapMode::NORMAL)
|
||||
if(mode != EAdvMapMode::NORMAL //disable in world view
|
||||
|| !selection) //may occur just at the start of game (fake move before full intiialization)
|
||||
return;
|
||||
if(!LOCPLINT->cb->isVisible(mapPos))
|
||||
{
|
||||
@ -1475,10 +1476,11 @@ void CAdvMapInt::tileHovered(const int3 &mapPos)
|
||||
statusbar.clear();
|
||||
return;
|
||||
}
|
||||
auto objRelations = PlayerRelations::ALLIES;
|
||||
const CGObjectInstance *objAtTile = getActiveObject(mapPos);
|
||||
|
||||
if (objAtTile)
|
||||
if(objAtTile)
|
||||
{
|
||||
objRelations = LOCPLINT->cb->getPlayerRelations(LOCPLINT->playerID, objAtTile->tempOwner);
|
||||
std::string text = curHero() ? objAtTile->getHoverText(curHero()) : objAtTile->getHoverText(LOCPLINT->playerID);
|
||||
boost::replace_all(text,"\n"," ");
|
||||
statusbar.setText(text);
|
||||
@ -1490,9 +1492,6 @@ void CAdvMapInt::tileHovered(const int3 &mapPos)
|
||||
statusbar.setText(hlp);
|
||||
}
|
||||
|
||||
if(!selection) //may occur just at the start of game (fake move before full intiialization)
|
||||
return;
|
||||
|
||||
if(spellBeingCasted)
|
||||
{
|
||||
switch(spellBeingCasted->id)
|
||||
@ -1505,9 +1504,9 @@ void CAdvMapInt::tileHovered(const int3 &mapPos)
|
||||
return;
|
||||
case SpellID::DIMENSION_DOOR:
|
||||
{
|
||||
const TerrainTile *t = LOCPLINT->cb->getTile(mapPos, false);
|
||||
const TerrainTile * t = LOCPLINT->cb->getTile(mapPos, false);
|
||||
int3 hpos = selection->getSightCenter();
|
||||
if((!t || t->isClear(LOCPLINT->cb->getTile(hpos))) && isInScreenRange(hpos, mapPos))
|
||||
if((!t || t->isClear(LOCPLINT->cb->getTile(hpos))) && isInScreenRange(hpos, mapPos))
|
||||
CCS->curh->changeGraphic(ECursor::ADVENTURE, 41);
|
||||
else
|
||||
CCS->curh->changeGraphic(ECursor::ADVENTURE, 0);
|
||||
@ -1516,15 +1515,13 @@ void CAdvMapInt::tileHovered(const int3 &mapPos)
|
||||
}
|
||||
}
|
||||
|
||||
const bool guardingCreature = CGI->mh->map->isInTheMap(LOCPLINT->cb->getGuardingCreaturePosition(mapPos));
|
||||
|
||||
if(selection->ID == Obj::TOWN)
|
||||
{
|
||||
if(objAtTile)
|
||||
{
|
||||
if(objAtTile->ID == Obj::TOWN && LOCPLINT->cb->getPlayerRelations(LOCPLINT->playerID, objAtTile->tempOwner) != PlayerRelations::ENEMIES)
|
||||
if(objAtTile->ID == Obj::TOWN && objRelations != PlayerRelations::ENEMIES)
|
||||
CCS->curh->changeGraphic(ECursor::ADVENTURE, 3);
|
||||
else if(objAtTile->ID == Obj::HERO && objAtTile->tempOwner == LOCPLINT->playerID)
|
||||
else if(objAtTile->ID == Obj::HERO && objRelations == PlayerRelations::SAME_PLAYER)
|
||||
CCS->curh->changeGraphic(ECursor::ADVENTURE, 2);
|
||||
else
|
||||
CCS->curh->changeGraphic(ECursor::ADVENTURE, 0);
|
||||
@ -1532,127 +1529,58 @@ void CAdvMapInt::tileHovered(const int3 &mapPos)
|
||||
else
|
||||
CCS->curh->changeGraphic(ECursor::ADVENTURE, 0);
|
||||
}
|
||||
else if(const CGHeroInstance *h = curHero())
|
||||
else if(const CGHeroInstance * h = curHero())
|
||||
{
|
||||
const CGPathNode *pnode = LOCPLINT->cb->getPathsInfo(h)->getPathInfo(mapPos);
|
||||
|
||||
const CGPathNode * pnode = LOCPLINT->cb->getPathsInfo(h)->getPathInfo(mapPos);
|
||||
int turns = pnode->turns;
|
||||
vstd::amin(turns, 3);
|
||||
bool accessible = pnode->turns < 255;
|
||||
|
||||
if(objAtTile)
|
||||
switch(pnode->action)
|
||||
{
|
||||
if(objAtTile->ID == Obj::HERO)
|
||||
{
|
||||
if(!LOCPLINT->cb->getPlayerRelations( LOCPLINT->playerID, objAtTile->tempOwner)) //enemy hero
|
||||
{
|
||||
if(accessible)
|
||||
CCS->curh->changeGraphic(ECursor::ADVENTURE, 5 + turns*6);
|
||||
else
|
||||
CCS->curh->changeGraphic(ECursor::ADVENTURE, 0);
|
||||
}
|
||||
else //our or ally hero
|
||||
{
|
||||
if(selection == objAtTile)
|
||||
CCS->curh->changeGraphic(ECursor::ADVENTURE, 2);
|
||||
else if(accessible)
|
||||
CCS->curh->changeGraphic(ECursor::ADVENTURE, 8 + turns*6);
|
||||
else
|
||||
CCS->curh->changeGraphic(ECursor::ADVENTURE, 2);
|
||||
}
|
||||
}
|
||||
else if(objAtTile->ID == Obj::TOWN)
|
||||
{
|
||||
if(!LOCPLINT->cb->getPlayerRelations( LOCPLINT->playerID, objAtTile->tempOwner)) //enemy town
|
||||
{
|
||||
if(accessible)
|
||||
{
|
||||
const CGTownInstance* townObj = dynamic_cast<const CGTownInstance*>(objAtTile);
|
||||
|
||||
// Show movement cursor for unguarded enemy towns, otherwise attack cursor.
|
||||
if (townObj && !townObj->armedGarrison())
|
||||
CCS->curh->changeGraphic(ECursor::ADVENTURE, 9 + turns*6);
|
||||
else
|
||||
CCS->curh->changeGraphic(ECursor::ADVENTURE, 5 + turns*6);
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
CCS->curh->changeGraphic(ECursor::ADVENTURE, 0);
|
||||
}
|
||||
}
|
||||
else //our or ally town
|
||||
{
|
||||
if(accessible)
|
||||
CCS->curh->changeGraphic(ECursor::ADVENTURE, 9 + turns*6);
|
||||
else
|
||||
CCS->curh->changeGraphic(ECursor::ADVENTURE, 3);
|
||||
}
|
||||
}
|
||||
else if(objAtTile->ID == Obj::BOAT)
|
||||
{
|
||||
if(accessible)
|
||||
CCS->curh->changeGraphic(ECursor::ADVENTURE, 6 + turns*6);
|
||||
else
|
||||
CCS->curh->changeGraphic(ECursor::ADVENTURE, 0);
|
||||
}
|
||||
else if (objAtTile->ID == Obj::GARRISON || objAtTile->ID == Obj::GARRISON2)
|
||||
{
|
||||
if (accessible)
|
||||
{
|
||||
const CGGarrison* garrObj = dynamic_cast<const CGGarrison*>(objAtTile); //TODO evil evil cast!
|
||||
|
||||
// Show battle cursor for guarded enemy garrisons or garrisons have guarding creature behind, otherwise movement cursor.
|
||||
if (garrObj && ((garrObj->stacksCount()
|
||||
&& !LOCPLINT->cb->getPlayerRelations( LOCPLINT->playerID, garrObj->tempOwner))
|
||||
|| guardingCreature))
|
||||
CCS->curh->changeGraphic(ECursor::ADVENTURE, 5 + turns*6);
|
||||
else
|
||||
CCS->curh->changeGraphic(ECursor::ADVENTURE, 9 + turns*6);
|
||||
}
|
||||
else
|
||||
CCS->curh->changeGraphic(ECursor::ADVENTURE, 0);
|
||||
}
|
||||
else if (guardingCreature && accessible) //(objAtTile->ID == 54) //monster
|
||||
{
|
||||
CCS->curh->changeGraphic(ECursor::ADVENTURE, 5 + turns*6);
|
||||
}
|
||||
case CGPathNode::NORMAL:
|
||||
if(pnode->layer == EPathfindingLayer::LAND)
|
||||
CCS->curh->changeGraphic(ECursor::ADVENTURE, 4 + turns*6);
|
||||
else
|
||||
CCS->curh->changeGraphic(ECursor::ADVENTURE, 28 + turns);
|
||||
break;
|
||||
|
||||
case CGPathNode::VISIT:
|
||||
case CGPathNode::BLOCKING_VISIT:
|
||||
if(objAtTile && objAtTile->ID == Obj::HERO)
|
||||
{
|
||||
if(accessible)
|
||||
{
|
||||
if(pnode->land)
|
||||
CCS->curh->changeGraphic(ECursor::ADVENTURE, 9 + turns*6);
|
||||
else
|
||||
CCS->curh->changeGraphic(ECursor::ADVENTURE, 28 + turns);
|
||||
}
|
||||
if(selection == objAtTile)
|
||||
CCS->curh->changeGraphic(ECursor::ADVENTURE, 2);
|
||||
else
|
||||
CCS->curh->changeGraphic(ECursor::ADVENTURE, 0);
|
||||
CCS->curh->changeGraphic(ECursor::ADVENTURE, 8 + turns*6);
|
||||
}
|
||||
}
|
||||
else //no objs
|
||||
{
|
||||
if(accessible/* && pnode->accessible != CGPathNode::FLYABLE*/)
|
||||
else if(pnode->layer == EPathfindingLayer::LAND)
|
||||
CCS->curh->changeGraphic(ECursor::ADVENTURE, 9 + turns*6);
|
||||
else
|
||||
CCS->curh->changeGraphic(ECursor::ADVENTURE, 28 + turns);
|
||||
break;
|
||||
|
||||
case CGPathNode::BATTLE:
|
||||
CCS->curh->changeGraphic(ECursor::ADVENTURE, 5 + turns*6);
|
||||
break;
|
||||
|
||||
case CGPathNode::EMBARK:
|
||||
CCS->curh->changeGraphic(ECursor::ADVENTURE, 6 + turns*6);
|
||||
break;
|
||||
|
||||
case CGPathNode::DISEMBARK:
|
||||
CCS->curh->changeGraphic(ECursor::ADVENTURE, 7 + turns*6);
|
||||
break;
|
||||
|
||||
default:
|
||||
if(objAtTile && objRelations != PlayerRelations::ENEMIES)
|
||||
{
|
||||
if (guardingCreature)
|
||||
{
|
||||
CCS->curh->changeGraphic(ECursor::ADVENTURE, 5 + turns*6);
|
||||
}
|
||||
else
|
||||
{
|
||||
if(pnode->land)
|
||||
{
|
||||
if(LOCPLINT->cb->getTile(h->getPosition(false))->terType != ETerrainType::WATER)
|
||||
CCS->curh->changeGraphic(ECursor::ADVENTURE, 4 + turns*6);
|
||||
else
|
||||
CCS->curh->changeGraphic(ECursor::ADVENTURE, 7 + turns*6); //anchor
|
||||
}
|
||||
else
|
||||
CCS->curh->changeGraphic(ECursor::ADVENTURE, 6 + turns*6);
|
||||
}
|
||||
if(objAtTile->ID == Obj::TOWN)
|
||||
CCS->curh->changeGraphic(ECursor::ADVENTURE, 3);
|
||||
else if(objAtTile->ID == Obj::HERO && objRelations == PlayerRelations::SAME_PLAYER)
|
||||
CCS->curh->changeGraphic(ECursor::ADVENTURE, 2);
|
||||
}
|
||||
else
|
||||
CCS->curh->changeGraphic(ECursor::ADVENTURE, 0);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -3,7 +3,7 @@
|
||||
{
|
||||
"type" : "object",
|
||||
"$schema": "http://json-schema.org/draft-04/schema",
|
||||
"required" : [ "general", "video", "adventure", "battle", "server", "logging", "launcher" ],
|
||||
"required" : [ "general", "video", "adventure", "pathfinder", "battle", "server", "logging", "launcher" ],
|
||||
"definitions" : {
|
||||
"logLevelEnum" : {
|
||||
"type" : "string",
|
||||
@ -108,6 +108,74 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"pathfinder" : {
|
||||
"type" : "object",
|
||||
"additionalProperties" : false,
|
||||
"default": {},
|
||||
"required" : [ "teleports", "layers", "oneTurnSpecialLayersLimit", "originalMovementRules", "lightweightFlyingMode" ],
|
||||
"properties" : {
|
||||
"layers" : {
|
||||
"type" : "object",
|
||||
"additionalProperties" : false,
|
||||
"default": {},
|
||||
"required" : [ "sailing", "waterWalking", "flying" ],
|
||||
"properties" : {
|
||||
"sailing" : {
|
||||
"type" : "boolean",
|
||||
"default" : true
|
||||
},
|
||||
"waterWalking" : {
|
||||
"type" : "boolean",
|
||||
"default" : true
|
||||
},
|
||||
"flying" : {
|
||||
"type" : "boolean",
|
||||
"default" : true
|
||||
}
|
||||
}
|
||||
},
|
||||
"teleports" : {
|
||||
"type" : "object",
|
||||
"additionalProperties" : false,
|
||||
"default": {},
|
||||
"required" : [ "twoWay", "oneWay", "oneWayRandom", "whirlpool", "castleGate" ],
|
||||
"properties" : {
|
||||
"twoWay" : {
|
||||
"type" : "boolean",
|
||||
"default" : true
|
||||
},
|
||||
"oneWay" : {
|
||||
"type" : "boolean",
|
||||
"default" : true
|
||||
},
|
||||
"oneWayRandom" : {
|
||||
"type" : "boolean",
|
||||
"default" : false
|
||||
},
|
||||
"whirlpool" : {
|
||||
"type" : "boolean",
|
||||
"default" : true
|
||||
},
|
||||
"castleGate" : {
|
||||
"type" : "boolean",
|
||||
"default" : false
|
||||
}
|
||||
}
|
||||
},
|
||||
"oneTurnSpecialLayersLimit" : {
|
||||
"type" : "boolean",
|
||||
"default" : true
|
||||
},
|
||||
"originalMovementRules" : {
|
||||
"type" : "boolean",
|
||||
"default" : false
|
||||
},
|
||||
"lightweightFlyingMode" : {
|
||||
"type" : "boolean",
|
||||
"default" : false
|
||||
}
|
||||
}
|
||||
},
|
||||
"battle" : {
|
||||
"type" : "object",
|
||||
"additionalProperties" : false,
|
||||
|
@ -2061,101 +2061,6 @@ PlayerRelations::PlayerRelations CGameState::getPlayerRelations( PlayerColor col
|
||||
return PlayerRelations::ENEMIES;
|
||||
}
|
||||
|
||||
void CGameState::getNeighbours(const TerrainTile &srct, int3 tile, std::vector<int3> &vec, const boost::logic::tribool &onLand, bool limitCoastSailing)
|
||||
{
|
||||
static const 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.reserve(8); //optimization
|
||||
for (auto & dir : dirs)
|
||||
{
|
||||
const int3 hlp = tile + dir;
|
||||
if(!map->isInTheMap(hlp))
|
||||
continue;
|
||||
|
||||
const TerrainTile &hlpt = map->getTile(hlp);
|
||||
|
||||
// //we cannot visit things from blocked tiles
|
||||
// if(srct.blocked && !srct.visitable && hlpt.visitable && srct.blockingObjects.front()->ID != HEROI_TYPE)
|
||||
// {
|
||||
// continue;
|
||||
// }
|
||||
|
||||
if(srct.terType == ETerrainType::WATER && limitCoastSailing && hlpt.terType == ETerrainType::WATER && dir.x && dir.y) //diagonal move through water
|
||||
{
|
||||
int3 hlp1 = tile,
|
||||
hlp2 = tile;
|
||||
hlp1.x += dir.x;
|
||||
hlp2.y += dir.y;
|
||||
|
||||
if(map->getTile(hlp1).terType != ETerrainType::WATER || map->getTile(hlp2).terType != ETerrainType::WATER)
|
||||
continue;
|
||||
}
|
||||
|
||||
if((indeterminate(onLand) || onLand == (hlpt.terType!=ETerrainType::WATER) )
|
||||
&& hlpt.terType != ETerrainType::ROCK)
|
||||
{
|
||||
vec.push_back(hlp);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int CGameState::getMovementCost(const CGHeroInstance *h, const int3 &src, const int3 &dest, int remainingMovePoints, bool checkLast)
|
||||
{
|
||||
if(src == dest) //same tile
|
||||
return 0;
|
||||
|
||||
TerrainTile &s = map->getTile(src),
|
||||
&d = map->getTile(dest);
|
||||
|
||||
//get basic cost
|
||||
int ret = h->getTileCost(d,s);
|
||||
|
||||
if(d.blocked && h->canFly())
|
||||
{
|
||||
ret *= (100.0 + h->valOfBonuses(Bonus::FLYING_MOVEMENT)) / 100.0;
|
||||
}
|
||||
else if(d.terType == ETerrainType::WATER)
|
||||
{
|
||||
if(h->boat && s.hasFavourableWinds() && d.hasFavourableWinds()) //Favourable Winds
|
||||
ret *= 0.666;
|
||||
else if(!h->boat && h->canWalkOnSea())
|
||||
{
|
||||
ret *= (100.0 + h->valOfBonuses(Bonus::WATER_WALKING)) / 100.0;
|
||||
}
|
||||
}
|
||||
|
||||
if(src.x != dest.x && src.y != dest.y) //it's diagonal move
|
||||
{
|
||||
int old = ret;
|
||||
ret *= 1.414213;
|
||||
//diagonal move costs too much but normal move is possible - allow diagonal move for remaining move points
|
||||
if(ret > remainingMovePoints && remainingMovePoints >= old)
|
||||
{
|
||||
return remainingMovePoints;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
int left = remainingMovePoints-ret;
|
||||
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;
|
||||
vec.reserve(8); //optimization
|
||||
getNeighbours(d, dest, vec, s.terType != ETerrainType::WATER, true);
|
||||
for(auto & elem : vec)
|
||||
{
|
||||
int fcost = getMovementCost(h, dest, elem, left, false);
|
||||
if(fcost <= left)
|
||||
{
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
ret = remainingMovePoints;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
void CGameState::apply(CPack *pack)
|
||||
{
|
||||
ui16 typ = typeList.getTypeID(pack);
|
||||
|
@ -339,8 +339,6 @@ public:
|
||||
bool isVisible(int3 pos, PlayerColor player);
|
||||
bool isVisible(const CGObjectInstance *obj, boost::optional<PlayerColor> player);
|
||||
|
||||
void getNeighbours(const TerrainTile &srct, int3 tile, std::vector<int3> &vec, const boost::logic::tribool &onLand, bool limitCoastSailing);
|
||||
int getMovementCost(const CGHeroInstance *h, const int3 &src, const int3 &dest, int remainingMovePoints=-1, bool checkLast=true);
|
||||
int getDate(Date::EDateType mode=Date::DAY) const; //mode=0 - total days in game, mode=1 - day of week, mode=2 - current week, mode=3 - current month
|
||||
|
||||
// ----- getters, setters -----
|
||||
|
1095
lib/CPathfinder.cpp
1095
lib/CPathfinder.cpp
File diff suppressed because it is too large
Load Diff
@ -5,6 +5,8 @@
|
||||
#include "IGameCallback.h"
|
||||
#include "int3.h"
|
||||
|
||||
#include <boost/heap/priority_queue.hpp>
|
||||
|
||||
/*
|
||||
* CPathfinder.h, part of VCMI engine
|
||||
*
|
||||
@ -18,26 +20,45 @@
|
||||
class CGHeroInstance;
|
||||
class CGObjectInstance;
|
||||
struct TerrainTile;
|
||||
class CPathfinderHelper;
|
||||
|
||||
struct DLL_LINKAGE CGPathNode
|
||||
{
|
||||
enum EAccessibility
|
||||
typedef EPathfindingLayer ELayer;
|
||||
|
||||
enum ENodeAction : ui8
|
||||
{
|
||||
UNKNOWN = 0,
|
||||
EMBARK = 1,
|
||||
DISEMBARK,
|
||||
NORMAL,
|
||||
BATTLE,
|
||||
VISIT,
|
||||
BLOCKING_VISIT
|
||||
};
|
||||
|
||||
enum EAccessibility : ui8
|
||||
{
|
||||
NOT_SET = 0,
|
||||
ACCESSIBLE = 1, //tile can be entered and passed
|
||||
VISITABLE, //tile can be entered as the last tile in path
|
||||
BLOCKVIS, //visitable from neighbouring tile but not passable
|
||||
FLYABLE, //can only be accessed in air layer
|
||||
BLOCKED //tile can't be entered nor visited
|
||||
};
|
||||
|
||||
EAccessibility accessible;
|
||||
ui8 land;
|
||||
ui8 turns; //how many turns we have to wait before reachng the tile - 0 means current turn
|
||||
ui32 moveRemains; //remaining tiles after hero reaches the tile
|
||||
CGPathNode * theNodeBefore;
|
||||
int3 coord; //coordinates
|
||||
ui32 moveRemains; //remaining tiles after hero reaches the tile
|
||||
ui8 turns; //how many turns we have to wait before reachng the tile - 0 means current turn
|
||||
ELayer layer;
|
||||
EAccessibility accessible;
|
||||
ENodeAction action;
|
||||
bool locked;
|
||||
|
||||
CGPathNode();
|
||||
void reset();
|
||||
void update(const int3 & Coord, const ELayer Layer, const EAccessibility Accessible);
|
||||
bool reachable() const;
|
||||
};
|
||||
|
||||
@ -52,27 +73,36 @@ struct DLL_LINKAGE CGPath
|
||||
|
||||
struct DLL_LINKAGE CPathsInfo
|
||||
{
|
||||
typedef EPathfindingLayer ELayer;
|
||||
|
||||
mutable boost::mutex pathMx;
|
||||
|
||||
const CGHeroInstance *hero;
|
||||
const CGHeroInstance * hero;
|
||||
int3 hpos;
|
||||
int3 sizes;
|
||||
CGPathNode ***nodes; //[w][h][level]
|
||||
boost::multi_array<CGPathNode, 4> nodes; //[w][h][level][layer]
|
||||
|
||||
CPathsInfo(const int3 &Sizes);
|
||||
CPathsInfo(const int3 & Sizes);
|
||||
~CPathsInfo();
|
||||
const CGPathNode * getPathInfo( int3 tile ) const;
|
||||
bool getPath(const int3 &dst, CGPath &out) const;
|
||||
int getDistance( int3 tile ) const;
|
||||
const CGPathNode * getPathInfo(const int3 & tile) const;
|
||||
bool getPath(CGPath & out, const int3 & dst) const;
|
||||
int getDistance(const int3 & tile) const;
|
||||
const CGPathNode * getNode(const int3 & coord) const;
|
||||
|
||||
CGPathNode * getNode(const int3 & coord, const ELayer layer);
|
||||
};
|
||||
|
||||
class CPathfinder : private CGameInfoCallback
|
||||
{
|
||||
public:
|
||||
CPathfinder(CPathsInfo &_out, CGameState *_gs, const CGHeroInstance *_hero);
|
||||
friend class CPathfinderHelper;
|
||||
|
||||
CPathfinder(CPathsInfo & _out, CGameState * _gs, const CGHeroInstance * _hero);
|
||||
void calculatePaths(); //calculates possible paths for hero, uses current hero position and movement left; returns pointer to newly allocated CPath or nullptr if path does not exists
|
||||
|
||||
private:
|
||||
typedef EPathfindingLayer ELayer;
|
||||
|
||||
struct PathfinderOptions
|
||||
{
|
||||
bool useFlying;
|
||||
@ -83,42 +113,153 @@ private:
|
||||
bool useTeleportOneWayRandom; // One-way monoliths with more than one known exit
|
||||
bool useTeleportWhirlpool; // Force enabled if hero protected or unaffected (have one stack of one creature)
|
||||
|
||||
/// TODO: Find out with client and server code, merge with normal teleporters.
|
||||
/// Likely proper implementation would require some refactoring of CGTeleport.
|
||||
/// So for now this is unfinished and disabled by default.
|
||||
bool useCastleGate;
|
||||
|
||||
/// If true transition into air layer only possible from initial node.
|
||||
/// This is drastically decrease path calculation complexity (and time).
|
||||
/// Downside is less MP effective paths calculation.
|
||||
///
|
||||
/// TODO: If this option end up useful for slow devices it's can be improved:
|
||||
/// - Allow transition into air layer not only from initial position, but also from teleporters.
|
||||
/// Movement into air can be also allowed when hero disembarked.
|
||||
/// - Other idea is to allow transition into air within certain radius of N tiles around hero.
|
||||
/// Patrol support need similar functionality so it's won't be ton of useless code.
|
||||
/// Such limitation could be useful as it's can be scaled depend on device performance.
|
||||
bool lightweightFlyingMode;
|
||||
|
||||
/// This option enable one turn limitation for flying and water walking.
|
||||
/// So if we're out of MP while cp is blocked or water tile we won't add dest tile to queue.
|
||||
///
|
||||
/// Following imitation is default H3 mechanics, but someone may want to disable it in mods.
|
||||
/// After all this limit should benefit performance on maps with tons of water or blocked tiles.
|
||||
///
|
||||
/// TODO:
|
||||
/// - Behavior when option is disabled not implemented and will lead to crashes.
|
||||
bool oneTurnSpecialLayersLimit;
|
||||
|
||||
/// VCMI have different movement rules to solve flaws original engine has.
|
||||
/// If this option enabled you'll able to do following things in fly:
|
||||
/// - Move from blocked tiles to visitable one
|
||||
/// - Move from guarded tiles to blockvis tiles without being attacked
|
||||
/// - Move from guarded tiles to guarded visitable tiles with being attacked after
|
||||
/// TODO:
|
||||
/// - Option should also allow same tile land <-> air layer transitions.
|
||||
/// Current implementation only allow go into (from) air layer only to neighbour tiles.
|
||||
/// I find it's reasonable limitation, but it's will make some movements more expensive than in H3.
|
||||
bool originalMovementRules;
|
||||
|
||||
PathfinderOptions();
|
||||
} options;
|
||||
|
||||
CPathsInfo &out;
|
||||
const CGHeroInstance *hero;
|
||||
CPathsInfo & out;
|
||||
const CGHeroInstance * hero;
|
||||
const std::vector<std::vector<std::vector<ui8> > > &FoW;
|
||||
unique_ptr<CPathfinderHelper> hlp;
|
||||
|
||||
std::list<CGPathNode*> mq; //BFS queue -> nodes to be checked
|
||||
struct NodeComparer
|
||||
{
|
||||
bool operator()(const CGPathNode * lhs, const CGPathNode * rhs) const
|
||||
{
|
||||
if(rhs->turns > lhs->turns)
|
||||
return false;
|
||||
else if(rhs->turns == lhs->turns && rhs->moveRemains < lhs->moveRemains)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
};
|
||||
boost::heap::priority_queue<CGPathNode *, boost::heap::compare<NodeComparer> > pq;
|
||||
|
||||
std::vector<int3> neighbourTiles;
|
||||
std::vector<int3> neighbours;
|
||||
|
||||
CGPathNode *cp; //current (source) path node -> we took it from the queue
|
||||
CGPathNode *dp; //destination node -> it's a neighbour of cp that we consider
|
||||
const TerrainTile *ct, *dt; //tile info for both nodes
|
||||
const CGObjectInstance *sTileObj;
|
||||
ui8 useEmbarkCost; //0 - usual movement; 1 - embark; 2 - disembark
|
||||
CGPathNode * cp; //current (source) path node -> we took it from the queue
|
||||
CGPathNode * dp; //destination node -> it's a neighbour of cp that we consider
|
||||
const TerrainTile * ct, * dt; //tile info for both nodes
|
||||
const CGObjectInstance * ctObj, * dtObj;
|
||||
CGPathNode::ENodeAction destAction;
|
||||
|
||||
void addNeighbours(const int3 &coord);
|
||||
void addTeleportExits(bool noTeleportExcludes = false);
|
||||
void addNeighbours();
|
||||
void addTeleportExits();
|
||||
|
||||
bool isMovementPossible(); //checks if current move will be between sea<->land. If so, checks it legality (returns false if movement is not possible) and sets useEmbarkCost
|
||||
bool checkDestinationTile();
|
||||
bool isLayerTransitionPossible(const ELayer dstLayer) const;
|
||||
bool isLayerTransitionPossible() const;
|
||||
bool isMovementToDestPossible() const;
|
||||
bool isMovementAfterDestPossible() const;
|
||||
CGPathNode::ENodeAction getDestAction() const;
|
||||
|
||||
int3 getSourceGuardPosition();
|
||||
bool isSourceGuarded();
|
||||
bool isDestinationGuarded();
|
||||
bool isDestinationGuardian();
|
||||
bool isSourceInitialPosition() const;
|
||||
bool isSourceVisitableObj() const;
|
||||
bool isSourceGuarded() const;
|
||||
bool isDestVisitableObj() const;
|
||||
bool isDestinationGuarded(const bool ignoreAccessibility = true) const;
|
||||
bool isDestinationGuardian() const;
|
||||
|
||||
void initializeGraph();
|
||||
|
||||
CGPathNode *getNode(const int3 &coord);
|
||||
CGPathNode::EAccessibility evaluateAccessibility(const int3 &pos, const TerrainTile *tinfo) const;
|
||||
bool canMoveBetween(const int3 &a, const int3 &b) const; //checks only for visitable objects that may make moving between tiles impossible, not other conditions (like tiles itself accessibility)
|
||||
CGPathNode::EAccessibility evaluateAccessibility(const int3 & pos, const TerrainTile * tinfo, const ELayer layer) const;
|
||||
bool isVisitableObj(const CGObjectInstance * obj, const ELayer layer) const;
|
||||
bool canSeeObj(const CGObjectInstance * obj) const;
|
||||
bool canMoveBetween(const int3 & a, const int3 & b) const; //checks only for visitable objects that may make moving between tiles impossible, not other conditions (like tiles itself accessibility)
|
||||
|
||||
bool isAllowedTeleportEntrance(const CGTeleport * obj) const;
|
||||
bool addTeleportTwoWay(const CGTeleport * obj) const;
|
||||
bool addTeleportOneWay(const CGTeleport * obj) const;
|
||||
bool addTeleportOneWayRandom(const CGTeleport * obj) const;
|
||||
bool addTeleportWhirlpool(const CGWhirlpool * obj) const;
|
||||
|
||||
};
|
||||
|
||||
struct DLL_LINKAGE TurnInfo
|
||||
{
|
||||
/// This is certainly not the best design ever and certainly can be improved
|
||||
/// Unfortunately for pathfinder that do hundreds of thousands calls onus system add too big overhead
|
||||
struct BonusCache {
|
||||
std::vector<bool> noTerrainPenalty;
|
||||
bool freeShipBoarding;
|
||||
bool flyingMovement;
|
||||
int flyingMovementVal;
|
||||
bool waterWalking;
|
||||
int waterWalkingVal;
|
||||
|
||||
BonusCache(TBonusListPtr bonusList);
|
||||
};
|
||||
unique_ptr<BonusCache> bonusCache;
|
||||
|
||||
const CGHeroInstance * hero;
|
||||
TBonusListPtr bonuses;
|
||||
mutable int maxMovePointsLand;
|
||||
mutable int maxMovePointsWater;
|
||||
int nativeTerrain;
|
||||
|
||||
TurnInfo(const CGHeroInstance * Hero, const int Turn = 0);
|
||||
bool isLayerAvailable(const EPathfindingLayer layer) const;
|
||||
bool hasBonusOfType(const Bonus::BonusType type, const int subtype = -1) const;
|
||||
int valOfBonuses(const Bonus::BonusType type, const int subtype = -1) const;
|
||||
int getMaxMovePoints(const EPathfindingLayer layer) const;
|
||||
};
|
||||
|
||||
class DLL_LINKAGE CPathfinderHelper
|
||||
{
|
||||
public:
|
||||
CPathfinderHelper(const CGHeroInstance * Hero, const CPathfinder::PathfinderOptions & Options);
|
||||
void updateTurnInfo(const int turn = 0);
|
||||
bool isLayerAvailable(const EPathfindingLayer layer) const;
|
||||
const TurnInfo * getTurnInfo() const;
|
||||
bool hasBonusOfType(const Bonus::BonusType type, const int subtype = -1) const;
|
||||
int getMaxMovePoints(const EPathfindingLayer layer) const;
|
||||
|
||||
static void getNeighbours(const CMap * map, const TerrainTile & srct, const int3 & tile, std::vector<int3> & vec, const boost::logic::tribool & onLand, const bool limitCoastSailing);
|
||||
|
||||
static int getMovementCost(const CGHeroInstance * h, const int3 & src, const int3 & dst, const TerrainTile * ct, const TerrainTile * dt, const int remainingMovePoints =- 1, const TurnInfo * ti = nullptr, const bool checkLast = true);
|
||||
static int getMovementCost(const CGHeroInstance * h, const int3 & dst);
|
||||
|
||||
private:
|
||||
int turn;
|
||||
const CGHeroInstance * hero;
|
||||
std::vector<TurnInfo *> turnsInfo;
|
||||
const CPathfinder::PathfinderOptions & options;
|
||||
};
|
||||
|
@ -25,56 +25,6 @@ const PlayerColor PlayerColor::NEUTRAL = PlayerColor(255);
|
||||
const PlayerColor PlayerColor::PLAYER_LIMIT = PlayerColor(PLAYER_LIMIT_I);
|
||||
const TeamID TeamID::NO_TEAM = TeamID(255);
|
||||
|
||||
#define ID_LIKE_OPERATORS_INTERNAL(A, B, AN, BN) \
|
||||
bool operator==(const A & a, const B & b) \
|
||||
{ \
|
||||
return AN == BN ; \
|
||||
} \
|
||||
bool operator!=(const A & a, const B & b) \
|
||||
{ \
|
||||
return AN != BN ; \
|
||||
} \
|
||||
bool operator<(const A & a, const B & b) \
|
||||
{ \
|
||||
return AN < BN ; \
|
||||
} \
|
||||
bool operator<=(const A & a, const B & b) \
|
||||
{ \
|
||||
return AN <= BN ; \
|
||||
} \
|
||||
bool operator>(const A & a, const B & b) \
|
||||
{ \
|
||||
return AN > BN ; \
|
||||
} \
|
||||
bool operator>=(const A & a, const B & b) \
|
||||
{ \
|
||||
return AN >= BN ; \
|
||||
}
|
||||
|
||||
#define ID_LIKE_OPERATORS(CLASS_NAME, ENUM_NAME) \
|
||||
ID_LIKE_OPERATORS_INTERNAL(CLASS_NAME, CLASS_NAME, a.num, b.num) \
|
||||
ID_LIKE_OPERATORS_INTERNAL(CLASS_NAME, ENUM_NAME, a.num, b) \
|
||||
ID_LIKE_OPERATORS_INTERNAL(ENUM_NAME, CLASS_NAME, a, b.num)
|
||||
|
||||
|
||||
ID_LIKE_OPERATORS(SecondarySkill, SecondarySkill::ESecondarySkill)
|
||||
|
||||
ID_LIKE_OPERATORS(Obj, Obj::EObj)
|
||||
|
||||
ID_LIKE_OPERATORS(ETerrainType, ETerrainType::EETerrainType)
|
||||
|
||||
ID_LIKE_OPERATORS(ArtifactID, ArtifactID::EArtifactID)
|
||||
|
||||
ID_LIKE_OPERATORS(ArtifactPosition, ArtifactPosition::EArtifactPosition)
|
||||
|
||||
ID_LIKE_OPERATORS(CreatureID, CreatureID::ECreatureID)
|
||||
|
||||
ID_LIKE_OPERATORS(SpellID, SpellID::ESpellID)
|
||||
|
||||
ID_LIKE_OPERATORS(BuildingID, BuildingID::EBuildingID)
|
||||
|
||||
ID_LIKE_OPERATORS(BFieldType, BFieldType::EBFieldType)
|
||||
|
||||
CArtifact * ArtifactID::toArtifact() const
|
||||
{
|
||||
return VLC->arth->artifacts[*this];
|
||||
@ -130,7 +80,7 @@ std::ostream & operator<<(std::ostream & os, const Battle::ActionType actionType
|
||||
else return os << it->second;
|
||||
}
|
||||
|
||||
std::ostream & operator<<(std::ostream & os, const ETerrainType actionType)
|
||||
std::ostream & operator<<(std::ostream & os, const ETerrainType terrainType)
|
||||
{
|
||||
static const std::map<ETerrainType::EETerrainType, std::string> terrainTypeToString =
|
||||
{
|
||||
@ -147,9 +97,10 @@ std::ostream & operator<<(std::ostream & os, const ETerrainType actionType)
|
||||
DEFINE_ELEMENT(LAVA),
|
||||
DEFINE_ELEMENT(WATER),
|
||||
DEFINE_ELEMENT(ROCK)
|
||||
#undef DEFINE_ELEMENT
|
||||
};
|
||||
|
||||
auto it = terrainTypeToString.find(actionType.num);
|
||||
auto it = terrainTypeToString.find(terrainType.num);
|
||||
if (it == terrainTypeToString.end()) return os << "<Unknown type>";
|
||||
else return os << it->second;
|
||||
}
|
||||
@ -160,3 +111,23 @@ std::string ETerrainType::toString() const
|
||||
ss << *this;
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
std::ostream & operator<<(std::ostream & os, const EPathfindingLayer pathfindingLayer)
|
||||
{
|
||||
static const std::map<EPathfindingLayer::EEPathfindingLayer, std::string> pathfinderLayerToString
|
||||
{
|
||||
#define DEFINE_ELEMENT(element) {EPathfindingLayer::element, #element}
|
||||
DEFINE_ELEMENT(WRONG),
|
||||
DEFINE_ELEMENT(AUTO),
|
||||
DEFINE_ELEMENT(LAND),
|
||||
DEFINE_ELEMENT(SAIL),
|
||||
DEFINE_ELEMENT(WATER),
|
||||
DEFINE_ELEMENT(AIR),
|
||||
DEFINE_ELEMENT(NUM_LAYERS)
|
||||
#undef DEFINE_ELEMENT
|
||||
};
|
||||
|
||||
auto it = pathfinderLayerToString.find(pathfindingLayer.num);
|
||||
if (it == pathfinderLayerToString.end()) return os << "<Unknown type>";
|
||||
else return os << it->second;
|
||||
}
|
||||
|
@ -93,18 +93,37 @@ CLASS_NAME & advance(int i) \
|
||||
}
|
||||
|
||||
|
||||
#define ID_LIKE_OPERATORS_INTERNAL_DECLS(A, B) \
|
||||
bool DLL_LINKAGE operator==(const A & a, const B & b); \
|
||||
bool DLL_LINKAGE operator!=(const A & a, const B & b); \
|
||||
bool DLL_LINKAGE operator<(const A & a, const B & b); \
|
||||
bool DLL_LINKAGE operator<=(const A & a, const B & b); \
|
||||
bool DLL_LINKAGE operator>(const A & a, const B & b); \
|
||||
bool DLL_LINKAGE operator>=(const A & a, const B & b);
|
||||
// Operators are performance-critical and to be inlined they must be in header
|
||||
#define ID_LIKE_OPERATORS_INTERNAL(A, B, AN, BN) \
|
||||
STRONG_INLINE bool operator==(const A & a, const B & b) \
|
||||
{ \
|
||||
return AN == BN ; \
|
||||
} \
|
||||
STRONG_INLINE bool operator!=(const A & a, const B & b) \
|
||||
{ \
|
||||
return AN != BN ; \
|
||||
} \
|
||||
STRONG_INLINE bool operator<(const A & a, const B & b) \
|
||||
{ \
|
||||
return AN < BN ; \
|
||||
} \
|
||||
STRONG_INLINE bool operator<=(const A & a, const B & b) \
|
||||
{ \
|
||||
return AN <= BN ; \
|
||||
} \
|
||||
STRONG_INLINE bool operator>(const A & a, const B & b) \
|
||||
{ \
|
||||
return AN > BN ; \
|
||||
} \
|
||||
STRONG_INLINE bool operator>=(const A & a, const B & b) \
|
||||
{ \
|
||||
return AN >= BN ; \
|
||||
}
|
||||
|
||||
#define ID_LIKE_OPERATORS_DECLS(CLASS_NAME, ENUM_NAME) \
|
||||
ID_LIKE_OPERATORS_INTERNAL_DECLS(CLASS_NAME, CLASS_NAME) \
|
||||
ID_LIKE_OPERATORS_INTERNAL_DECLS(CLASS_NAME, ENUM_NAME) \
|
||||
ID_LIKE_OPERATORS_INTERNAL_DECLS(ENUM_NAME, CLASS_NAME)
|
||||
#define ID_LIKE_OPERATORS(CLASS_NAME, ENUM_NAME) \
|
||||
ID_LIKE_OPERATORS_INTERNAL(CLASS_NAME, CLASS_NAME, a.num, b.num) \
|
||||
ID_LIKE_OPERATORS_INTERNAL(CLASS_NAME, ENUM_NAME, a.num, b) \
|
||||
ID_LIKE_OPERATORS_INTERNAL(ENUM_NAME, CLASS_NAME, a, b.num)
|
||||
|
||||
|
||||
#define OP_DECL_INT(CLASS_NAME, OP) \
|
||||
@ -296,7 +315,7 @@ public:
|
||||
ESecondarySkill num;
|
||||
};
|
||||
|
||||
ID_LIKE_OPERATORS_DECLS(SecondarySkill, SecondarySkill::ESecondarySkill)
|
||||
ID_LIKE_OPERATORS(SecondarySkill, SecondarySkill::ESecondarySkill)
|
||||
|
||||
namespace EAlignment
|
||||
{
|
||||
@ -384,7 +403,7 @@ public:
|
||||
EBuildingID num;
|
||||
};
|
||||
|
||||
ID_LIKE_OPERATORS_DECLS(BuildingID, BuildingID::EBuildingID)
|
||||
ID_LIKE_OPERATORS(BuildingID, BuildingID::EBuildingID)
|
||||
|
||||
namespace EBuildingState
|
||||
{
|
||||
@ -664,7 +683,7 @@ public:
|
||||
EObj num;
|
||||
};
|
||||
|
||||
ID_LIKE_OPERATORS_DECLS(Obj, Obj::EObj)
|
||||
ID_LIKE_OPERATORS(Obj, Obj::EObj)
|
||||
|
||||
namespace SecSkillLevel
|
||||
{
|
||||
@ -754,10 +773,29 @@ public:
|
||||
std::string toString() const;
|
||||
};
|
||||
|
||||
DLL_LINKAGE std::ostream & operator<<(std::ostream & os, const ETerrainType actionType);
|
||||
DLL_LINKAGE std::ostream & operator<<(std::ostream & os, const ETerrainType terrainType);
|
||||
|
||||
ID_LIKE_OPERATORS_DECLS(ETerrainType, ETerrainType::EETerrainType)
|
||||
ID_LIKE_OPERATORS(ETerrainType, ETerrainType::EETerrainType)
|
||||
|
||||
class DLL_LINKAGE EPathfindingLayer
|
||||
{
|
||||
public:
|
||||
enum EEPathfindingLayer : ui8
|
||||
{
|
||||
LAND = 0, SAIL = 1, WATER, AIR, NUM_LAYERS, WRONG, AUTO
|
||||
};
|
||||
|
||||
EPathfindingLayer(EEPathfindingLayer _num = WRONG) : num(_num)
|
||||
{}
|
||||
|
||||
ID_LIKE_CLASS_COMMON(EPathfindingLayer, EEPathfindingLayer)
|
||||
|
||||
EEPathfindingLayer num;
|
||||
};
|
||||
|
||||
DLL_LINKAGE std::ostream & operator<<(std::ostream & os, const EPathfindingLayer pathfindingLayer);
|
||||
|
||||
ID_LIKE_OPERATORS(EPathfindingLayer, EPathfindingLayer::EEPathfindingLayer)
|
||||
|
||||
class BFieldType
|
||||
{
|
||||
@ -780,7 +818,7 @@ public:
|
||||
EBFieldType num;
|
||||
};
|
||||
|
||||
ID_LIKE_OPERATORS_DECLS(BFieldType, BFieldType::EBFieldType)
|
||||
ID_LIKE_OPERATORS(BFieldType, BFieldType::EBFieldType)
|
||||
|
||||
namespace EPlayerStatus
|
||||
{
|
||||
@ -820,7 +858,7 @@ public:
|
||||
EArtifactPosition num;
|
||||
};
|
||||
|
||||
ID_LIKE_OPERATORS_DECLS(ArtifactPosition, ArtifactPosition::EArtifactPosition)
|
||||
ID_LIKE_OPERATORS(ArtifactPosition, ArtifactPosition::EArtifactPosition)
|
||||
|
||||
class ArtifactID
|
||||
{
|
||||
@ -865,7 +903,7 @@ public:
|
||||
EArtifactID num;
|
||||
};
|
||||
|
||||
ID_LIKE_OPERATORS_DECLS(ArtifactID, ArtifactID::EArtifactID)
|
||||
ID_LIKE_OPERATORS(ArtifactID, ArtifactID::EArtifactID)
|
||||
|
||||
class CreatureID
|
||||
{
|
||||
@ -909,7 +947,7 @@ public:
|
||||
ECreatureID num;
|
||||
};
|
||||
|
||||
ID_LIKE_OPERATORS_DECLS(CreatureID, CreatureID::ECreatureID)
|
||||
ID_LIKE_OPERATORS(CreatureID, CreatureID::ECreatureID)
|
||||
|
||||
class SpellID
|
||||
{
|
||||
@ -953,7 +991,7 @@ public:
|
||||
ESpellID num;
|
||||
};
|
||||
|
||||
ID_LIKE_OPERATORS_DECLS(SpellID, SpellID::ESpellID)
|
||||
ID_LIKE_OPERATORS(SpellID, SpellID::ESpellID)
|
||||
|
||||
enum class ESpellSchool: ui8
|
||||
{
|
||||
@ -963,8 +1001,6 @@ enum class ESpellSchool: ui8
|
||||
EARTH = 3
|
||||
};
|
||||
|
||||
ID_LIKE_OPERATORS_DECLS(SpellID, SpellID::ESpellID)
|
||||
|
||||
// Typedef declarations
|
||||
typedef ui8 TFaction;
|
||||
typedef si64 TExpType;
|
||||
@ -975,8 +1011,8 @@ typedef si32 TQuantity;
|
||||
typedef int TRmgTemplateZoneId;
|
||||
|
||||
#undef ID_LIKE_CLASS_COMMON
|
||||
#undef ID_LIKE_OPERATORS_DECLS
|
||||
#undef ID_LIKE_OPERATORS_INTERNAL_DECLS
|
||||
#undef ID_LIKE_OPERATORS
|
||||
#undef ID_LIKE_OPERATORS_INTERNAL
|
||||
#undef INSTID_LIKE_CLASS_COMMON
|
||||
#undef OP_DECL_INT
|
||||
|
||||
|
@ -56,7 +56,7 @@ static int lowestSpeed(const CGHeroInstance * chi)
|
||||
return ret;
|
||||
}
|
||||
|
||||
ui32 CGHeroInstance::getTileCost(const TerrainTile &dest, const TerrainTile &from) const
|
||||
ui32 CGHeroInstance::getTileCost(const TerrainTile &dest, const TerrainTile &from, const TurnInfo * ti) const
|
||||
{
|
||||
unsigned ret = GameConstants::BASE_MOVEMENT_COST;
|
||||
|
||||
@ -80,30 +80,40 @@ ui32 CGHeroInstance::getTileCost(const TerrainTile &dest, const TerrainTile &fro
|
||||
break;
|
||||
}
|
||||
}
|
||||
else if(!hasBonusOfType(Bonus::NO_TERRAIN_PENALTY, from.terType))
|
||||
else if(ti->nativeTerrain != from.terType && !ti->hasBonusOfType(Bonus::NO_TERRAIN_PENALTY, from.terType))
|
||||
{
|
||||
// NOTE: in H3 neutral stacks will ignore terrain penalty only if placed as topmost stack(s) in hero army.
|
||||
// This is clearly bug in H3 however intended behaviour is not clear.
|
||||
// Current VCMI behaviour will ignore neutrals in calculations so army in VCMI
|
||||
// will always have best penalty without any influence from player-defined stacks order
|
||||
|
||||
for(auto stack : stacks)
|
||||
{
|
||||
int nativeTerrain = VLC->townh->factions[stack.second->type->faction]->nativeTerrain;
|
||||
if(nativeTerrain != -1 && nativeTerrain != from.terType)
|
||||
{
|
||||
ret = VLC->heroh->terrCosts[from.terType];
|
||||
ret -= getSecSkillLevel(SecondarySkill::PATHFINDING) * 25;
|
||||
if(ret < GameConstants::BASE_MOVEMENT_COST)
|
||||
ret = GameConstants::BASE_MOVEMENT_COST;
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
ret = VLC->heroh->terrCosts[from.terType];
|
||||
ret -= getSecSkillLevel(SecondarySkill::PATHFINDING) * 25;
|
||||
if(ret < GameConstants::BASE_MOVEMENT_COST)
|
||||
ret = GameConstants::BASE_MOVEMENT_COST;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
int CGHeroInstance::getNativeTerrain() const
|
||||
{
|
||||
// NOTE: in H3 neutral stacks will ignore terrain penalty only if placed as topmost stack(s) in hero army.
|
||||
// This is clearly bug in H3 however intended behaviour is not clear.
|
||||
// Current VCMI behaviour will ignore neutrals in calculations so army in VCMI
|
||||
// will always have best penalty without any influence from player-defined stacks order
|
||||
|
||||
// TODO: What should we do if all hero stacks are neutral creatures?
|
||||
int nativeTerrain = -1;
|
||||
for(auto stack : stacks)
|
||||
{
|
||||
int stackNativeTerrain = VLC->townh->factions[stack.second->type->faction]->nativeTerrain;
|
||||
if(stackNativeTerrain == -1)
|
||||
continue;
|
||||
|
||||
if(nativeTerrain == -1)
|
||||
nativeTerrain = stackNativeTerrain;
|
||||
else if(nativeTerrain != stackNativeTerrain)
|
||||
return -1;
|
||||
}
|
||||
|
||||
return nativeTerrain;
|
||||
}
|
||||
|
||||
int3 CGHeroInstance::convertPosition(int3 src, bool toh3m) //toh3m=true: manifest->h3m; toh3m=false: h3m->manifest
|
||||
{
|
||||
if (toh3m)
|
||||
@ -129,16 +139,6 @@ int3 CGHeroInstance::getPosition(bool h3m) const //h3m=true - returns position o
|
||||
}
|
||||
}
|
||||
|
||||
bool CGHeroInstance::canFly() const
|
||||
{
|
||||
return hasBonusOfType(Bonus::FLYING_MOVEMENT);
|
||||
}
|
||||
|
||||
bool CGHeroInstance::canWalkOnSea() const
|
||||
{
|
||||
return hasBonusOfType(Bonus::WATER_WALKING);
|
||||
}
|
||||
|
||||
ui8 CGHeroInstance::getSecSkillLevel(SecondarySkill skill) const
|
||||
{
|
||||
for(auto & elem : secSkills)
|
||||
@ -181,8 +181,11 @@ bool CGHeroInstance::canLearnSkill() const
|
||||
return secSkills.size() < GameConstants::SKILL_PER_HERO;
|
||||
}
|
||||
|
||||
int CGHeroInstance::maxMovePoints(bool onLand) const
|
||||
int CGHeroInstance::maxMovePoints(bool onLand, const TurnInfo * ti) const
|
||||
{
|
||||
if(!ti)
|
||||
ti = new TurnInfo(this);
|
||||
|
||||
int base;
|
||||
|
||||
if(onLand)
|
||||
@ -201,10 +204,10 @@ int CGHeroInstance::maxMovePoints(bool onLand) const
|
||||
}
|
||||
|
||||
const Bonus::BonusType bt = onLand ? Bonus::LAND_MOVEMENT : Bonus::SEA_MOVEMENT;
|
||||
const int bonus = valOfBonuses(Bonus::MOVEMENT) + valOfBonuses(bt);
|
||||
const int bonus = ti->valOfBonuses(Bonus::MOVEMENT) + ti->valOfBonuses(bt);
|
||||
|
||||
const int subtype = onLand ? SecondarySkill::LOGISTICS : SecondarySkill::NAVIGATION;
|
||||
const double modifier = valOfBonuses(Bonus::SECONDARY_SKILL_PREMY, subtype) / 100.0;
|
||||
const double modifier = ti->valOfBonuses(Bonus::SECONDARY_SKILL_PREMY, subtype) / 100.0;
|
||||
|
||||
return int(base* (1+modifier)) + bonus;
|
||||
}
|
||||
@ -1171,10 +1174,15 @@ CBonusSystemNode * CGHeroInstance::whereShouldBeAttached(CGameState *gs)
|
||||
return CArmedInstance::whereShouldBeAttached(gs);
|
||||
}
|
||||
|
||||
int CGHeroInstance::movementPointsAfterEmbark(int MPsBefore, int basicCost, bool disembark /*= false*/) const
|
||||
int CGHeroInstance::movementPointsAfterEmbark(int MPsBefore, int basicCost, bool disembark /*= false*/, const TurnInfo * ti) const
|
||||
{
|
||||
if(hasBonusOfType(Bonus::FREE_SHIP_BOARDING))
|
||||
return (MPsBefore - basicCost) * static_cast<double>(maxMovePoints(disembark)) / maxMovePoints(!disembark);
|
||||
if(!ti)
|
||||
ti = new TurnInfo(this);
|
||||
|
||||
int mp1 = ti->getMaxMovePoints(disembark ? EPathfindingLayer::LAND : EPathfindingLayer::SAIL);
|
||||
int mp2 = ti->getMaxMovePoints(disembark ? EPathfindingLayer::SAIL : EPathfindingLayer::LAND);
|
||||
if(ti->hasBonusOfType(Bonus::FREE_SHIP_BOARDING))
|
||||
return (MPsBefore - basicCost) * static_cast<double>(mp1) / mp2;
|
||||
|
||||
return 0; //take all MPs otherwise
|
||||
}
|
||||
|
@ -21,6 +21,7 @@ class CHero;
|
||||
class CGBoat;
|
||||
class CGTownInstance;
|
||||
struct TerrainTile;
|
||||
struct TurnInfo;
|
||||
|
||||
class CGHeroPlaceholder : public CGObjectInstance
|
||||
{
|
||||
@ -129,12 +130,11 @@ public:
|
||||
EAlignment::EAlignment getAlignment() const;
|
||||
const std::string &getBiography() const;
|
||||
bool needsLastStack()const override;
|
||||
ui32 getTileCost(const TerrainTile &dest, const TerrainTile &from) const; //move cost - applying pathfinding skill, road and terrain modifiers. NOT includes diagonal move penalty, last move levelling
|
||||
ui32 getTileCost(const TerrainTile &dest, const TerrainTile &from, const TurnInfo * ti) const; //move cost - applying pathfinding skill, road and terrain modifiers. NOT includes diagonal move penalty, last move levelling
|
||||
int getNativeTerrain() const;
|
||||
ui32 getLowestCreatureSpeed() const;
|
||||
int3 getPosition(bool h3m = false) const; //h3m=true - returns position of hero object; h3m=false - returns position of hero 'manifestation'
|
||||
si32 manaRegain() const; //how many points of mana can hero regain "naturally" in one day
|
||||
bool canFly() const;
|
||||
bool canWalkOnSea() const;
|
||||
int getCurrentLuck(int stack=-1, bool town=false) const;
|
||||
int getSpellCost(const CSpell *sp) const; //do not use during battles -> bonuses from army would be ignored
|
||||
|
||||
@ -161,8 +161,8 @@ public:
|
||||
void setSecSkillLevel(SecondarySkill which, int val, bool abs);// abs == 0 - changes by value; 1 - sets to value
|
||||
void levelUp(std::vector<SecondarySkill> skills);
|
||||
|
||||
int maxMovePoints(bool onLand) const;
|
||||
int movementPointsAfterEmbark(int MPsBefore, int basicCost, bool disembark = false) const;
|
||||
int maxMovePoints(bool onLand, const TurnInfo * ti = nullptr) const;
|
||||
int movementPointsAfterEmbark(int MPsBefore, int basicCost, bool disembark = false, const TurnInfo * ti = nullptr) const;
|
||||
|
||||
static int3 convertPosition(int3 src, bool toh3m); //toh3m=true: manifest->h3m; toh3m=false: h3m->manifest
|
||||
double getFightingStrength() const; // takes attack / defense skill into account
|
||||
|
@ -132,11 +132,13 @@ Obj TerrainTile::topVisitableId(bool excludeTop) const
|
||||
|
||||
CGObjectInstance * TerrainTile::topVisitableObj(bool excludeTop) const
|
||||
{
|
||||
auto visitableObj = visitableObjects;
|
||||
if(excludeTop && visitableObj.size())
|
||||
visitableObj.pop_back();
|
||||
if(visitableObjects.empty() || (excludeTop && visitableObjects.size() == 1))
|
||||
return nullptr;
|
||||
|
||||
return visitableObj.size() ? visitableObj.back() : nullptr;
|
||||
if(excludeTop)
|
||||
return visitableObjects[visitableObjects.size()-2];
|
||||
|
||||
return visitableObjects.back();
|
||||
}
|
||||
|
||||
bool TerrainTile::isCoastal() const
|
||||
|
@ -1764,7 +1764,6 @@ bool CGameHandler::moveHero( ObjectInstanceID hid, int3 dst, ui8 teleporting, bo
|
||||
}
|
||||
|
||||
const TerrainTile t = *gs->getTile(hmpos);
|
||||
const int cost = gs->getMovementCost(h, h->getPosition(), hmpos, h->movement);
|
||||
const int3 guardPos = gs->guardingCreaturePosition(hmpos);
|
||||
|
||||
const bool embarking = !h->boat && !t.visitableObjects.empty() && t.visitableObjects.back()->ID == Obj::BOAT;
|
||||
@ -1779,12 +1778,16 @@ bool CGameHandler::moveHero( ObjectInstanceID hid, int3 dst, ui8 teleporting, bo
|
||||
tmh.movePoints = h->movement;
|
||||
|
||||
//check if destination tile is available
|
||||
auto ti = new TurnInfo(h);
|
||||
const bool canFly = ti->hasBonusOfType(Bonus::FLYING_MOVEMENT);
|
||||
const bool canWalkOnSea = ti->hasBonusOfType(Bonus::WATER_WALKING);
|
||||
const int cost = CPathfinderHelper::getMovementCost(h, h->getPosition(), hmpos, nullptr, nullptr, h->movement, ti);
|
||||
|
||||
//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 == ETerrainType::ROCK || (t.blocked && !t.visitable && !h->hasBonusOfType(Bonus::FLYING_MOVEMENT) ))
|
||||
if(((t.terType == ETerrainType::ROCK || (t.blocked && !t.visitable && !canFly))
|
||||
&& complain("Cannot move hero, destination tile is blocked!"))
|
||||
|| ((!h->boat && !h->canWalkOnSea() && t.terType == ETerrainType::WATER && (t.visitableObjects.size() < 1 || (t.visitableObjects.back()->ID != Obj::BOAT && t.visitableObjects.back()->ID != Obj::HERO))) //hero is not on boat/water walking and dst water tile doesn't contain boat/hero (objs visitable from land) -> we test back cause boat may be on top of another object (#276)
|
||||
|| ((!h->boat && !canWalkOnSea && !canFly && t.terType == ETerrainType::WATER && (t.visitableObjects.size() < 1 || (t.visitableObjects.back()->ID != Obj::BOAT && t.visitableObjects.back()->ID != Obj::HERO))) //hero is not on boat/water walking and dst water tile doesn't contain boat/hero (objs visitable from land) -> we test back cause boat may be on top of another object (#276)
|
||||
&& complain("Cannot move hero, destination tile is on water!"))
|
||||
|| ((h->boat && t.terType != ETerrainType::WATER && t.blocked)
|
||||
&& complain("Cannot disembark hero, tile is blocked!"))
|
||||
@ -1794,6 +1797,8 @@ bool CGameHandler::moveHero( ObjectInstanceID hid, int3 dst, ui8 teleporting, bo
|
||||
&& complain("Can not move garrisoned hero!"))
|
||||
|| ((h->movement < cost && dst != h->pos && !teleporting)
|
||||
&& complain("Hero doesn't have any movement points left!"))
|
||||
|| ((transit && !canFly && !CGTeleport::isTeleport(t.topVisitableObj()))
|
||||
&& complain("Hero cannot transit over this tile!"))
|
||||
/*|| (states.checkFlag(h->tempOwner, &PlayerStatus::engagedIntoBattle)
|
||||
&& complain("Cannot move hero during the battle"))*/)
|
||||
{
|
||||
@ -1843,8 +1848,7 @@ bool CGameHandler::moveHero( ObjectInstanceID hid, int3 dst, ui8 teleporting, bo
|
||||
}
|
||||
else if(visitDest == VISIT_DEST)
|
||||
{
|
||||
if(!transit || !CGTeleport::isTeleport(t.topVisitableObj()))
|
||||
visitObjectOnTile(t, h);
|
||||
visitObjectOnTile(t, h);
|
||||
}
|
||||
|
||||
queries.popIfTop(moveQuery);
|
||||
@ -1867,16 +1871,16 @@ bool CGameHandler::moveHero( ObjectInstanceID hid, int3 dst, ui8 teleporting, bo
|
||||
};
|
||||
|
||||
|
||||
if(embarking)
|
||||
if(!transit && embarking)
|
||||
{
|
||||
tmh.movePoints = h->movementPointsAfterEmbark(h->movement, cost, false);
|
||||
tmh.movePoints = h->movementPointsAfterEmbark(h->movement, cost, false, ti);
|
||||
return doMove(TryMoveHero::EMBARK, IGNORE_GUARDS, DONT_VISIT_DEST, LEAVING_TILE);
|
||||
//attack guards on embarking? In H3 creatures on water had no zone of control at all
|
||||
}
|
||||
|
||||
if(disembarking)
|
||||
{
|
||||
tmh.movePoints = h->movementPointsAfterEmbark(h->movement, cost, true);
|
||||
tmh.movePoints = h->movementPointsAfterEmbark(h->movement, cost, true, ti);
|
||||
return doMove(TryMoveHero::DISEMBARK, CHECK_FOR_GUARDS, VISIT_DEST, LEAVING_TILE);
|
||||
}
|
||||
|
||||
@ -1905,10 +1909,23 @@ bool CGameHandler::moveHero( ObjectInstanceID hid, int3 dst, ui8 teleporting, bo
|
||||
? h->movement - cost
|
||||
: 0;
|
||||
|
||||
if(blockingVisit())
|
||||
EGuardLook lookForGuards = CHECK_FOR_GUARDS;
|
||||
EVisitDest visitDest = VISIT_DEST;
|
||||
if(transit)
|
||||
{
|
||||
if(CGTeleport::isTeleport(t.topVisitableObj()))
|
||||
visitDest = DONT_VISIT_DEST;
|
||||
|
||||
if(canFly)
|
||||
{
|
||||
lookForGuards = IGNORE_GUARDS;
|
||||
visitDest = DONT_VISIT_DEST;
|
||||
}
|
||||
}
|
||||
else if(blockingVisit())
|
||||
return true;
|
||||
|
||||
doMove(TryMoveHero::SUCCESS, CHECK_FOR_GUARDS, VISIT_DEST, LEAVING_TILE);
|
||||
doMove(TryMoveHero::SUCCESS, lookForGuards, visitDest, LEAVING_TILE);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user