From 118039a368102d2c97462e8d0552624f994d2e0f Mon Sep 17 00:00:00 2001 From: ArseniyShestakov Date: Mon, 2 Nov 2015 11:05:26 +0300 Subject: [PATCH 001/168] Pathfinding: add copy-pasted EPathfindingLayer --- lib/GameConstants.h | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/lib/GameConstants.h b/lib/GameConstants.h index 0a1f74945..5cf6c1ccb 100644 --- a/lib/GameConstants.h +++ b/lib/GameConstants.h @@ -745,6 +745,25 @@ DLL_LINKAGE std::ostream & operator<<(std::ostream & os, const ETerrainType acti ID_LIKE_OPERATORS_DECLS(ETerrainType, ETerrainType::EETerrainType) +class DLL_LINKAGE EPathfindingLayer +{ +public: + enum EEPathfindingLayer + { + AUTO = -1, LAND = 0, SAIL = 1, WATER, AIR, NUM_LAYERS + }; + + EPathfindingLayer(EEPathfindingLayer _num = AUTO) : num(_num) + {} + + ID_LIKE_CLASS_COMMON(EPathfindingLayer, EEPathfindingLayer) + + EEPathfindingLayer num; +}; + +DLL_LINKAGE std::ostream & operator<<(std::ostream & os, const EPathfindingLayer actionType); + +ID_LIKE_OPERATORS_DECLS(EPathfindingLayer, EPathfindingLayer::EEPathfindingLayer) class BFieldType { From b8253206c734e53e0ca78ccc65a30b5c1baeae34 Mon Sep 17 00:00:00 2001 From: ArseniyShestakov Date: Mon, 2 Nov 2015 11:06:06 +0300 Subject: [PATCH 002/168] Pathfinding: make related code aware that layers exist --- lib/CPathfinder.cpp | 100 +++++++++++++++++++++++++++++++------------- lib/CPathfinder.h | 13 +++--- 2 files changed, 80 insertions(+), 33 deletions(-) diff --git a/lib/CPathfinder.cpp b/lib/CPathfinder.cpp index 2eae0500c..6d35f0157 100644 --- a/lib/CPathfinder.cpp +++ b/lib/CPathfinder.cpp @@ -79,7 +79,7 @@ void CPathfinder::calculatePaths() //logGlobal->infoStream() << boost::format("Calculating paths for hero %s (adress %d) of player %d") % hero->name % hero % hero->tempOwner; //initial tile - set cost on 0 and add to the queue - CGPathNode &initialNode = *getNode(out.hpos); + CGPathNode &initialNode = *getNode(out.hpos, hero->boat ? EPathfindingLayer::SAIL : EPathfindingLayer::LAND); initialNode.turns = 0; initialNode.moveRemains = hero->movement; mq.push_back(&initialNode); @@ -100,7 +100,7 @@ void CPathfinder::calculatePaths() addNeighbours(cp->coord); for(auto & neighbour : neighbours) { - dp = getNode(neighbour); + dp = getNode(neighbour, EPathfindingLayer::LAND); dt = &gs->map->getTile(neighbour); useEmbarkCost = 0; //0 - usual movement; 1 - embark; 2 - disembark @@ -138,12 +138,12 @@ void CPathfinder::calculatePaths() } //neighbours loop //just add all passable teleport exits - if(sTileObj) + if(sTileObj && canVisitObject()) { addTeleportExits(); for(auto & neighbour : neighbours) { - dp = getNode(neighbour); + dp = getNode(neighbour, EPathfindingLayer::LAND); if(isBetterWay(movement, turn)) { dp->moveRemains = movement; @@ -164,13 +164,18 @@ void CPathfinder::addNeighbours(const int3 &coord) std::vector tiles; gs->getNeighbours(*ct, coord, tiles, boost::logic::indeterminate, !cp->land); sTileObj = ct->topVisitableObj(coord == CGHeroInstance::convertPosition(hero->pos, false)); - if(sTileObj) + if(canVisitObject()) { - for(int3 tile: tiles) + if(sTileObj) { - if(canMoveBetween(tile, sTileObj->visitablePos())) - neighbours.push_back(tile); + for(int3 tile: tiles) + { + if(canMoveBetween(tile, sTileObj->visitablePos())) + neighbours.push_back(tile); + } } + else + vstd::concatenate(neighbours, tiles); } else vstd::concatenate(neighbours, tiles); @@ -310,7 +315,20 @@ bool CPathfinder::isDestinationGuardian() void CPathfinder::initializeGraph() { int3 pos; - CGPathNode ***graph = out.nodes; + CGPathNode ****graph = out.nodes; + const TerrainTile *tinfo; + auto addNode = [&](EPathfindingLayer layer) + { + CGPathNode &node = graph[pos.x][pos.y][pos.z][layer]; + node.accessible = evaluateAccessibility(pos, tinfo); + node.turns = 0xff; + node.moveRemains = 0; + node.coord = pos; + node.land = tinfo->terType != ETerrainType::WATER; + node.theNodeBefore = nullptr; + node.layer = layer; + }; + for(pos.x=0; pos.x < out.sizes.x; ++pos.x) { for(pos.y=0; pos.y < out.sizes.y; ++pos.y) @@ -318,21 +336,33 @@ void CPathfinder::initializeGraph() for(pos.z=0; pos.z < out.sizes.z; ++pos.z) { const TerrainTile *tinfo = &gs->map->getTile(pos); - CGPathNode &node = graph[pos.x][pos.y][pos.z]; - node.accessible = evaluateAccessibility(pos, tinfo); - node.turns = 0xff; - node.moveRemains = 0; - node.coord = pos; - node.land = tinfo->terType != ETerrainType::WATER; - node.theNodeBefore = nullptr; + switch (tinfo->terType) + { + case ETerrainType::WRONG: + case ETerrainType::BORDER: + case ETerrainType::ROCK: + break; + case ETerrainType::WATER: + addNode(EPathfindingLayer::SAIL); + if(options.useFlying) + addNode(EPathfindingLayer::AIR); + if(options.useWaterWalking) + addNode(EPathfindingLayer::WATER); + break; + default: + addNode(EPathfindingLayer::LAND); + if(options.useFlying) + addNode(EPathfindingLayer::AIR); + break; + } } } } } -CGPathNode *CPathfinder::getNode(const int3 &coord) +CGPathNode *CPathfinder::getNode(const int3 &coord, const EPathfindingLayer &layer) { - return &out.nodes[coord.x][coord.y][coord.z]; + return &out.nodes[coord.x][coord.y][coord.z][layer]; } CGPathNode::EAccessibility CPathfinder::evaluateAccessibility(const int3 &pos, const TerrainTile *tinfo) const @@ -415,6 +445,12 @@ bool CPathfinder::addTeleportWhirlpool(const CGWhirlpool * obj) const return options.useTeleportWhirlpool && obj; } +bool CPathfinder::canVisitObject() const +{ + //hero can't visit objects while walking on water or flying + return cp->layer == EPathfindingLayer::LAND || cp->layer == EPathfindingLayer::SAIL; +} + CGPathNode::CGPathNode() :coord(-1,-1,-1) { @@ -455,13 +491,17 @@ CPathsInfo::CPathsInfo( const int3 &Sizes ) :sizes(Sizes) { hero = nullptr; - nodes = new CGPathNode**[sizes.x]; + nodes = new CGPathNode***[sizes.x]; for(int i = 0; i < sizes.x; i++) { - nodes[i] = new CGPathNode*[sizes.y]; + nodes[i] = new CGPathNode**[sizes.y]; for(int j = 0; j < sizes.y; j++) { - nodes[i][j] = new CGPathNode[sizes.z]; + nodes[i][j] = new CGPathNode*[sizes.z]; + for (int z = 0; z < sizes.z; z++) + { + nodes[i][j][z] = new CGPathNode[EPathfindingLayer::NUM_LAYERS]; + } } } } @@ -472,6 +512,10 @@ CPathsInfo::~CPathsInfo() { for(int j = 0; j < sizes.y; j++) { + for (int z = 0; z < sizes.z; z++) + { + delete [] nodes[i][j][z]; + } delete [] nodes[i][j]; } delete [] nodes[i]; @@ -479,21 +523,21 @@ CPathsInfo::~CPathsInfo() delete [] nodes; } -const CGPathNode * CPathsInfo::getPathInfo( int3 tile ) const +const CGPathNode * CPathsInfo::getPathInfo(const int3 &tile, const EPathfindingLayer &layer) const { boost::unique_lock pathLock(pathMx); - if(tile.x >= sizes.x || tile.y >= sizes.y || tile.z >= sizes.z) + if(tile.x >= sizes.x || tile.y >= sizes.y || tile.z >= sizes.z || layer >= EPathfindingLayer::NUM_LAYERS) return nullptr; - return &nodes[tile.x][tile.y][tile.z]; + return &nodes[tile.x][tile.y][tile.z][layer]; } -bool CPathsInfo::getPath( const int3 &dst, CGPath &out ) const +bool CPathsInfo::getPath(const int3 &dst, const EPathfindingLayer &layer, CGPath &out) const { boost::unique_lock pathLock(pathMx); out.nodes.clear(); - const CGPathNode *curnode = &nodes[dst.x][dst.y][dst.z]; + const CGPathNode *curnode = &nodes[dst.x][dst.y][dst.z][layer]; if(!curnode->theNodeBefore) return false; @@ -507,12 +551,12 @@ bool CPathsInfo::getPath( const int3 &dst, CGPath &out ) const return true; } -int CPathsInfo::getDistance( int3 tile ) const +int CPathsInfo::getDistance(const int3 &tile, const EPathfindingLayer &layer) const { boost::unique_lock pathLock(pathMx); CGPath ret; - if(getPath(tile, ret)) + if(getPath(tile, layer, ret)) return ret.nodes.size(); else return 255; diff --git a/lib/CPathfinder.h b/lib/CPathfinder.h index 38d13821e..71f47e3bd 100644 --- a/lib/CPathfinder.h +++ b/lib/CPathfinder.h @@ -36,6 +36,7 @@ struct DLL_LINKAGE CGPathNode ui32 moveRemains; //remaining tiles after hero reaches the tile CGPathNode * theNodeBefore; int3 coord; //coordinates + EPathfindingLayer layer; CGPathNode(); bool reachable() const; @@ -57,13 +58,13 @@ struct DLL_LINKAGE CPathsInfo const CGHeroInstance *hero; int3 hpos; int3 sizes; - CGPathNode ***nodes; //[w][h][level] + CGPathNode ****nodes; //[w][h][level][layer] 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 EPathfindingLayer &layer) const; + bool getPath(const int3 &dst, const EPathfindingLayer &layer, CGPath &out) const; + int getDistance(const int3 &tile, const EPathfindingLayer &layer) const; }; class CPathfinder : private CGameInfoCallback @@ -113,7 +114,7 @@ private: void initializeGraph(); - CGPathNode *getNode(const int3 &coord); + CGPathNode *getNode(const int3 &coord, const EPathfindingLayer &layer); 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) @@ -121,4 +122,6 @@ private: bool addTeleportOneWay(const CGTeleport * obj) const; bool addTeleportOneWayRandom(const CGTeleport * obj) const; bool addTeleportWhirlpool(const CGWhirlpool * obj) const; + + bool canVisitObject() const; }; From 2b6e1498d2c43655dbc7811f511b7445902d46ca Mon Sep 17 00:00:00 2001 From: ArseniyShestakov Date: Mon, 2 Nov 2015 11:14:32 +0300 Subject: [PATCH 003/168] Pathfinding: change argument order for getPath and AUTO layer as default This still need investigation, but likely most of external code shouldn't be aware of layers in order to work properly because only LAND and SAIL can be targeted and single tile can't have both of these layers. --- AI/VCAI/AIUtility.cpp | 2 +- AI/VCAI/VCAI.cpp | 4 ++-- client/CPlayerInterface.cpp | 4 ++-- client/windows/CAdvmapInterface.cpp | 4 ++-- lib/CPathfinder.cpp | 4 ++-- lib/CPathfinder.h | 6 +++--- 6 files changed, 12 insertions(+), 12 deletions(-) diff --git a/AI/VCAI/AIUtility.cpp b/AI/VCAI/AIUtility.cpp index f05867d61..e55879ce1 100644 --- a/AI/VCAI/AIUtility.cpp +++ b/AI/VCAI/AIUtility.cpp @@ -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, ops); if (p.nodes.size() && p.endPos() == op && p.nodes.size() <= DIST_LIMIT) if (ai->isGoodForVisit(obj, h, sm)) nearbyVisitableObjs.push_back(obj); diff --git a/AI/VCAI/VCAI.cpp b/AI/VCAI/VCAI.cpp index dfa906580..4ef6f169d 100644 --- a/AI/VCAI/VCAI.cpp +++ b/AI/VCAI/VCAI.cpp @@ -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; @@ -2519,7 +2519,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 diff --git a/client/CPlayerInterface.cpp b/client/CPlayerInterface.cpp index 1bfe4ae98..a6109b0d6 100644 --- a/client/CPlayerInterface.cpp +++ b/client/CPlayerInterface.cpp @@ -1288,7 +1288,7 @@ template 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); diff --git a/client/windows/CAdvmapInterface.cpp b/client/windows/CAdvmapInterface.cpp index e1b96e049..ab3ab31b7 100644 --- a/client/windows/CAdvmapInterface.cpp +++ b/client/windows/CAdvmapInterface.cpp @@ -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); diff --git a/lib/CPathfinder.cpp b/lib/CPathfinder.cpp index 6d35f0157..7d052494a 100644 --- a/lib/CPathfinder.cpp +++ b/lib/CPathfinder.cpp @@ -532,7 +532,7 @@ const CGPathNode * CPathsInfo::getPathInfo(const int3 &tile, const EPathfindingL return &nodes[tile.x][tile.y][tile.z][layer]; } -bool CPathsInfo::getPath(const int3 &dst, const EPathfindingLayer &layer, CGPath &out) const +bool CPathsInfo::getPath(CGPath &out, const int3 &dst, const EPathfindingLayer &layer) const { boost::unique_lock pathLock(pathMx); @@ -556,7 +556,7 @@ int CPathsInfo::getDistance(const int3 &tile, const EPathfindingLayer &layer) co boost::unique_lock pathLock(pathMx); CGPath ret; - if(getPath(tile, layer, ret)) + if(getPath(ret, tile, layer)) return ret.nodes.size(); else return 255; diff --git a/lib/CPathfinder.h b/lib/CPathfinder.h index 71f47e3bd..d62e1cb78 100644 --- a/lib/CPathfinder.h +++ b/lib/CPathfinder.h @@ -62,9 +62,9 @@ struct DLL_LINKAGE CPathsInfo CPathsInfo(const int3 &Sizes); ~CPathsInfo(); - const CGPathNode * getPathInfo(const int3 &tile, const EPathfindingLayer &layer) const; - bool getPath(const int3 &dst, const EPathfindingLayer &layer, CGPath &out) const; - int getDistance(const int3 &tile, const EPathfindingLayer &layer) const; + const CGPathNode * getPathInfo(const int3 &tile, const EPathfindingLayer &layer = EPathfindingLayer::AUTO) const; + bool getPath(CGPath &out, const int3 &dst, const EPathfindingLayer &layer = EPathfindingLayer::AUTO) const; + int getDistance(const int3 &tile, const EPathfindingLayer &layer = EPathfindingLayer::AUTO) const; }; class CPathfinder : private CGameInfoCallback From 4b64bec7112898f115c1d5e76df4d7254b6a0c74 Mon Sep 17 00:00:00 2001 From: ArseniyShestakov Date: Mon, 2 Nov 2015 13:25:01 +0300 Subject: [PATCH 004/168] EPathfindingLayer: copy other code from ETerrainType for debugging --- AI/VCAI/AIUtility.cpp | 2 +- lib/CPathfinder.cpp | 1 + lib/GameConstants.cpp | 29 +++++++++++++++++++++++++++++ lib/GameConstants.h | 6 ++++-- 4 files changed, 35 insertions(+), 3 deletions(-) diff --git a/AI/VCAI/AIUtility.cpp b/AI/VCAI/AIUtility.cpp index e55879ce1..fb2c35bcb 100644 --- a/AI/VCAI/AIUtility.cpp +++ b/AI/VCAI/AIUtility.cpp @@ -372,7 +372,7 @@ int3 whereToExplore(HeroPtr h) { int3 op = obj->visitablePos(); CGPath p; - ai->myCb->getPathsInfo(h.get())->getPath(p, ops); + 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); diff --git a/lib/CPathfinder.cpp b/lib/CPathfinder.cpp index 7d052494a..efa1446f5 100644 --- a/lib/CPathfinder.cpp +++ b/lib/CPathfinder.cpp @@ -459,6 +459,7 @@ CGPathNode::CGPathNode() moveRemains = 0; turns = 255; theNodeBefore = nullptr; + layer = EPathfindingLayer::WRONG; } bool CGPathNode::reachable() const diff --git a/lib/GameConstants.cpp b/lib/GameConstants.cpp index 9a48b476f..d21f3f40c 100644 --- a/lib/GameConstants.cpp +++ b/lib/GameConstants.cpp @@ -63,6 +63,8 @@ ID_LIKE_OPERATORS(Obj, Obj::EObj) ID_LIKE_OPERATORS(ETerrainType, ETerrainType::EETerrainType) +ID_LIKE_OPERATORS(EPathfindingLayer, EPathfindingLayer::EEPathfindingLayer) + ID_LIKE_OPERATORS(ArtifactID, ArtifactID::EArtifactID) ID_LIKE_OPERATORS(ArtifactPosition, ArtifactPosition::EArtifactPosition) @@ -160,3 +162,30 @@ std::string ETerrainType::toString() const ss << *this; return ss.str(); } + +std::ostream & operator<<(std::ostream & os, const EPathfindingLayer actionType) +{ + static const std::map 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) + + }; + + auto it = pathfinderLayerToString.find(actionType.num); + if (it == pathfinderLayerToString.end()) return os << ""; + else return os << it->second; +} + +std::string EPathfindingLayer::toString() const +{ + std::stringstream ss; + ss << *this; + return ss.str(); +} diff --git a/lib/GameConstants.h b/lib/GameConstants.h index 5cf6c1ccb..e46c482d1 100644 --- a/lib/GameConstants.h +++ b/lib/GameConstants.h @@ -750,15 +750,17 @@ class DLL_LINKAGE EPathfindingLayer public: enum EEPathfindingLayer { - AUTO = -1, LAND = 0, SAIL = 1, WATER, AIR, NUM_LAYERS + WRONG = -2, AUTO = -1, LAND = 0, SAIL = 1, WATER, AIR, NUM_LAYERS }; - EPathfindingLayer(EEPathfindingLayer _num = AUTO) : num(_num) + EPathfindingLayer(EEPathfindingLayer _num = WRONG) : num(_num) {} ID_LIKE_CLASS_COMMON(EPathfindingLayer, EEPathfindingLayer) EEPathfindingLayer num; + + std::string toString() const; }; DLL_LINKAGE std::ostream & operator<<(std::ostream & os, const EPathfindingLayer actionType); From 9c1c7d0caf45c94d108ed845f156959f88ed1a8d Mon Sep 17 00:00:00 2001 From: ArseniyShestakov Date: Mon, 2 Nov 2015 14:04:26 +0300 Subject: [PATCH 005/168] Pathfinding: move getNode into CPathsInfo --- lib/CPathfinder.cpp | 27 +++++++++++++++++---------- lib/CPathfinder.h | 2 +- 2 files changed, 18 insertions(+), 11 deletions(-) diff --git a/lib/CPathfinder.cpp b/lib/CPathfinder.cpp index efa1446f5..79b6a4f06 100644 --- a/lib/CPathfinder.cpp +++ b/lib/CPathfinder.cpp @@ -79,7 +79,7 @@ void CPathfinder::calculatePaths() //logGlobal->infoStream() << boost::format("Calculating paths for hero %s (adress %d) of player %d") % hero->name % hero % hero->tempOwner; //initial tile - set cost on 0 and add to the queue - CGPathNode &initialNode = *getNode(out.hpos, hero->boat ? EPathfindingLayer::SAIL : EPathfindingLayer::LAND); + CGPathNode &initialNode = *out.getNode(out.hpos, hero->boat ? EPathfindingLayer::SAIL : EPathfindingLayer::LAND); initialNode.turns = 0; initialNode.moveRemains = hero->movement; mq.push_back(&initialNode); @@ -100,7 +100,7 @@ void CPathfinder::calculatePaths() addNeighbours(cp->coord); for(auto & neighbour : neighbours) { - dp = getNode(neighbour, EPathfindingLayer::LAND); + dp = out.getNode(neighbour, EPathfindingLayer::LAND); dt = &gs->map->getTile(neighbour); useEmbarkCost = 0; //0 - usual movement; 1 - embark; 2 - disembark @@ -143,7 +143,7 @@ void CPathfinder::calculatePaths() addTeleportExits(); for(auto & neighbour : neighbours) { - dp = getNode(neighbour, EPathfindingLayer::LAND); + dp = out.getNode(neighbour, EPathfindingLayer::LAND); if(isBetterWay(movement, turn)) { dp->moveRemains = movement; @@ -360,11 +360,6 @@ void CPathfinder::initializeGraph() } } -CGPathNode *CPathfinder::getNode(const int3 &coord, const EPathfindingLayer &layer) -{ - return &out.nodes[coord.x][coord.y][coord.z][layer]; -} - CGPathNode::EAccessibility CPathfinder::evaluateAccessibility(const int3 &pos, const TerrainTile *tinfo) const { CGPathNode::EAccessibility ret = (tinfo->blocked ? CGPathNode::BLOCKED : CGPathNode::ACCESSIBLE); @@ -530,7 +525,7 @@ const CGPathNode * CPathsInfo::getPathInfo(const int3 &tile, const EPathfindingL if(tile.x >= sizes.x || tile.y >= sizes.y || tile.z >= sizes.z || layer >= EPathfindingLayer::NUM_LAYERS) return nullptr; - return &nodes[tile.x][tile.y][tile.z][layer]; + return getNode(tile, layer); } bool CPathsInfo::getPath(CGPath &out, const int3 &dst, const EPathfindingLayer &layer) const @@ -538,7 +533,7 @@ bool CPathsInfo::getPath(CGPath &out, const int3 &dst, const EPathfindingLayer & boost::unique_lock pathLock(pathMx); out.nodes.clear(); - const CGPathNode *curnode = &nodes[dst.x][dst.y][dst.z][layer]; + const CGPathNode *curnode = getNode(dst, layer); if(!curnode->theNodeBefore) return false; @@ -562,3 +557,15 @@ int CPathsInfo::getDistance(const int3 &tile, const EPathfindingLayer &layer) co else return 255; } + +CGPathNode *CPathsInfo::getNode(const int3 &coord, const EPathfindingLayer &layer) const +{ + if(layer != EPathfindingLayer::AUTO) + return &nodes[coord.x][coord.y][coord.z][layer]; + + auto landNode = &nodes[coord.x][coord.y][coord.z][EPathfindingLayer::LAND]; + if(landNode->theNodeBefore) + return landNode; + else + return &nodes[coord.x][coord.y][coord.z][EPathfindingLayer::SAIL]; +} diff --git a/lib/CPathfinder.h b/lib/CPathfinder.h index d62e1cb78..2e3be5160 100644 --- a/lib/CPathfinder.h +++ b/lib/CPathfinder.h @@ -65,6 +65,7 @@ struct DLL_LINKAGE CPathsInfo const CGPathNode * getPathInfo(const int3 &tile, const EPathfindingLayer &layer = EPathfindingLayer::AUTO) const; bool getPath(CGPath &out, const int3 &dst, const EPathfindingLayer &layer = EPathfindingLayer::AUTO) const; int getDistance(const int3 &tile, const EPathfindingLayer &layer = EPathfindingLayer::AUTO) const; + CGPathNode *getNode(const int3 &coord, const EPathfindingLayer &layer) const; }; class CPathfinder : private CGameInfoCallback @@ -114,7 +115,6 @@ private: void initializeGraph(); - CGPathNode *getNode(const int3 &coord, const EPathfindingLayer &layer); 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) From 460227ec2390adddf7e395ef0356d2c05ead0696 Mon Sep 17 00:00:00 2001 From: edeksumo Date: Mon, 2 Nov 2015 12:08:42 +0100 Subject: [PATCH 006/168] GitIgnore --- .gitignore | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.gitignore b/.gitignore index eb820ec6b..da2f1d79a 100644 --- a/.gitignore +++ b/.gitignore @@ -32,3 +32,6 @@ build-* CMakeLists.txt.user.* Doxyfile doc/* +VCMI_VS11.sdf +*.ipch +VCMI_VS11.opensdf From 400152caee7c7f9995a765b0c2378680248771a8 Mon Sep 17 00:00:00 2001 From: ArseniyShestakov Date: Mon, 2 Nov 2015 16:03:03 +0300 Subject: [PATCH 007/168] CPathfinder: draft implementation of layers logic; not yet works --- lib/CPathfinder.cpp | 176 +++++++++++++++++++++++++++----------------- lib/CPathfinder.h | 2 + 2 files changed, 110 insertions(+), 68 deletions(-) diff --git a/lib/CPathfinder.cpp b/lib/CPathfinder.cpp index 79b6a4f06..901c7bad5 100644 --- a/lib/CPathfinder.cpp +++ b/lib/CPathfinder.cpp @@ -100,40 +100,44 @@ void CPathfinder::calculatePaths() addNeighbours(cp->coord); for(auto & neighbour : neighbours) { - dp = out.getNode(neighbour, EPathfindingLayer::LAND); dt = &gs->map->getTile(neighbour); - useEmbarkCost = 0; //0 - usual movement; 1 - embark; 2 - disembark - - if(!isMovementPossible()) - continue; - - int cost = gs->getMovementCost(hero, cp->coord, dp->coord, movement); - int remains = movement - cost; - if(useEmbarkCost) + for(EPathfindingLayer i = EPathfindingLayer::LAND; i <= EPathfindingLayer::AIR; i.advance(1)) { - remains = hero->movementPointsAfterEmbark(movement, cost, useEmbarkCost - 1); - cost = movement - remains; - } + useEmbarkCost = 0; //0 - usual movement; 1 - embark; 2 - disembark + dp = out.getNode(neighbour, i); + if(cp->layer != i && isLayerTransitionPossible()) + continue; - int turnAtNextTile = turn; - if(remains < 0) - { - //occurs rarely, when hero with low movepoints tries to leave the road - turnAtNextTile++; - int moveAtNextTile = maxMovePoints(cp); - cost = gs->getMovementCost(hero, cp->coord, dp->coord, moveAtNextTile); //cost must be updated, movement points changed :( - remains = moveAtNextTile - cost; - } + if(!isMovementPossible()) + continue; + int cost = gs->getMovementCost(hero, cp->coord, dp->coord, movement); + int remains = movement - cost; + if(useEmbarkCost) + { + remains = hero->movementPointsAfterEmbark(movement, cost, useEmbarkCost - 1); + cost = movement - remains; + } - if(isBetterWay(remains, turnAtNextTile)) - { - assert(dp != cp->theNodeBefore); //two tiles can't point to each other - dp->moveRemains = remains; - dp->turns = turnAtNextTile; - dp->theNodeBefore = cp; + int turnAtNextTile = turn; + if(remains < 0) + { + //occurs rarely, when hero with low movepoints tries to leave the road + turnAtNextTile++; + int moveAtNextTile = maxMovePoints(cp); + cost = gs->getMovementCost(hero, cp->coord, dp->coord, moveAtNextTile); //cost must be updated, movement points changed :( + remains = moveAtNextTile - cost; + } - if(checkDestinationTile()) - mq.push_back(dp); + if(isBetterWay(remains, turnAtNextTile)) + { + assert(dp != cp->theNodeBefore); //two tiles can't point to each other + dp->moveRemains = remains; + dp->turns = turnAtNextTile; + dp->theNodeBefore = cp; + + if(checkDestinationTile()) + mq.push_back(dp); + } } } //neighbours loop @@ -143,7 +147,7 @@ void CPathfinder::calculatePaths() addTeleportExits(); for(auto & neighbour : neighbours) { - dp = out.getNode(neighbour, EPathfindingLayer::LAND); + dp = out.getNode(neighbour, cp->layer); if(isBetterWay(movement, turn)) { dp->moveRemains = movement; @@ -220,38 +224,42 @@ void CPathfinder::addTeleportExits(bool noTeleportExcludes) bool CPathfinder::isMovementPossible() { - if(!canMoveBetween(cp->coord, dp->coord) || dp->accessible == CGPathNode::BLOCKED) - return false; - - Obj destTopVisObjID = dt->topVisitableId(); - if(cp->land != dp->land) //hero can traverse land<->sea only in special circumstances + switch (dp->layer) { - if(cp->land) //from land to sea -> embark or assault hero on boat - { - if(dp->accessible == CGPathNode::ACCESSIBLE || destTopVisObjID < 0) //cannot enter empty water tile from land -> it has to be visitable + case EPathfindingLayer::LAND: + if(!canMoveBetween(cp->coord, dp->coord) || dp->accessible == CGPathNode::BLOCKED) return false; - if(destTopVisObjID != Obj::HERO && destTopVisObjID != Obj::BOAT) //only boat or hero can be accessed from land - return false; - if(destTopVisObjID == Obj::BOAT) - useEmbarkCost = 1; - } - else //disembark - { - //can disembark only on coastal tiles - if(!dt->isCoastal()) + if(isSourceGuarded() && !isDestinationGuardian()) // Can step into tile of guard return false; - //tile must be accessible -> exception: unblocked blockvis tiles -> clear but guarded by nearby monster coast - if((dp->accessible != CGPathNode::ACCESSIBLE && (dp->accessible != CGPathNode::BLOCKVIS || dt->blocked)) - || dt->visitable) //TODO: passableness problem -> town says it's passable (thus accessible) but we obviously can't disembark onto town gate - return false;; + break; + case EPathfindingLayer::SAIL: + if(!canMoveBetween(cp->coord, dp->coord) || dp->accessible == CGPathNode::BLOCKED) + return false; + if(isSourceGuarded() && !isDestinationGuardian()) // Can step into tile of guard + return false; - useEmbarkCost = 2; - } + break; + + case EPathfindingLayer::AIR: + if(!options.useFlying) + return false; + if(!canMoveBetween(cp->coord, dp->coord)) + return false; + + break; + + case EPathfindingLayer::WATER: + if(!options.useWaterWalking) + return false; + if(!canMoveBetween(cp->coord, dp->coord) || dp->accessible == CGPathNode::BLOCKED) + return false; + if(isDestinationGuarded()) + return false; + + break; } - if(isSourceGuarded() && !isDestinationGuardian()) // Can step into tile of guard - return false; return true; } @@ -314,12 +322,9 @@ bool CPathfinder::isDestinationGuardian() void CPathfinder::initializeGraph() { - int3 pos; - CGPathNode ****graph = out.nodes; - const TerrainTile *tinfo; - auto addNode = [&](EPathfindingLayer layer) + auto addNode = [&](EPathfindingLayer layer, const TerrainTile *tinfo, int3 pos) { - CGPathNode &node = graph[pos.x][pos.y][pos.z][layer]; + CGPathNode &node = out.nodes[pos.x][pos.y][pos.z][layer]; node.accessible = evaluateAccessibility(pos, tinfo); node.turns = 0xff; node.moveRemains = 0; @@ -329,6 +334,7 @@ void CPathfinder::initializeGraph() node.layer = layer; }; + int3 pos; for(pos.x=0; pos.x < out.sizes.x; ++pos.x) { for(pos.y=0; pos.y < out.sizes.y; ++pos.y) @@ -343,16 +349,16 @@ void CPathfinder::initializeGraph() case ETerrainType::ROCK: break; case ETerrainType::WATER: - addNode(EPathfindingLayer::SAIL); - if(options.useFlying) - addNode(EPathfindingLayer::AIR); - if(options.useWaterWalking) - addNode(EPathfindingLayer::WATER); + addNode(EPathfindingLayer::SAIL, tinfo, pos); +// if(options.useFlying) + addNode(EPathfindingLayer::AIR, tinfo, pos); +// if(options.useWaterWalking) + addNode(EPathfindingLayer::WATER, tinfo, pos); break; default: - addNode(EPathfindingLayer::LAND); - if(options.useFlying) - addNode(EPathfindingLayer::AIR); + addNode(EPathfindingLayer::LAND, tinfo, pos); +// if(options.useFlying) + addNode(EPathfindingLayer::AIR, tinfo, pos); break; } } @@ -446,6 +452,40 @@ bool CPathfinder::canVisitObject() const return cp->layer == EPathfindingLayer::LAND || cp->layer == EPathfindingLayer::SAIL; } +bool CPathfinder::isLayerTransitionPossible() +{ + Obj destTopVisObjID = dt->topVisitableId(); + if((cp->layer == EPathfindingLayer::AIR || EPathfindingLayer::WATER) + && dp->layer != EPathfindingLayer::LAND) + { + return false; + } + else if(cp->layer == EPathfindingLayer::SAIL && dp->layer != EPathfindingLayer::LAND) + return false; + else if(cp->layer == EPathfindingLayer::SAIL && dp->layer == EPathfindingLayer::LAND) + { + if(!dt->isCoastal()) + return false; + + //tile must be accessible -> exception: unblocked blockvis tiles -> clear but guarded by nearby monster coast + if((dp->accessible != CGPathNode::ACCESSIBLE && (dp->accessible != CGPathNode::BLOCKVIS || dt->blocked)) + || dt->visitable) //TODO: passableness problem -> town says it's passable (thus accessible) but we obviously can't disembark onto town gate + return false; + + useEmbarkCost = 2; + } + else if(cp->layer == EPathfindingLayer::LAND && dp->layer == EPathfindingLayer::SAIL) + { + if(dp->accessible == CGPathNode::ACCESSIBLE || destTopVisObjID < 0) //cannot enter empty water tile from land -> it has to be visitable + return false; + if(destTopVisObjID != Obj::HERO && destTopVisObjID != Obj::BOAT) //only boat or hero can be accessed from land + return false; + if(destTopVisObjID == Obj::BOAT) + useEmbarkCost = 1; + } + return true; +} + CGPathNode::CGPathNode() :coord(-1,-1,-1) { diff --git a/lib/CPathfinder.h b/lib/CPathfinder.h index 2e3be5160..0626ffe24 100644 --- a/lib/CPathfinder.h +++ b/lib/CPathfinder.h @@ -124,4 +124,6 @@ private: bool addTeleportWhirlpool(const CGWhirlpool * obj) const; bool canVisitObject() const; + + bool isLayerTransitionPossible(); }; From c047fea44ca518d85d069c8b6498339d448c785d Mon Sep 17 00:00:00 2001 From: DjWarmonger Date: Mon, 2 Nov 2015 16:39:57 +0100 Subject: [PATCH 008/168] Updated changelog. To be released as VCMI 0.98e. --- ChangeLog | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/ChangeLog b/ChangeLog index 52d54291b..0c22d23f9 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,9 @@ 0.98 -> 0.next +GENERAL: +* New Bonus NO_TERRAIN_PENALTY +* Nomads will remove Sand movement penalty from army + ADVETURE AI: * Fixed AI trying to go through underground rock * Fixed several cases causing AI wandering aimlessly From 349f16a35914afbd867700faa7686e35a179b9c8 Mon Sep 17 00:00:00 2001 From: DjWarmonger Date: Mon, 2 Nov 2015 17:28:54 +0100 Subject: [PATCH 009/168] Version bump. --- lib/GameConstants.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/GameConstants.h b/lib/GameConstants.h index 0a1f74945..05d3128eb 100644 --- a/lib/GameConstants.h +++ b/lib/GameConstants.h @@ -14,7 +14,7 @@ namespace GameConstants { - const std::string VCMI_VERSION = "VCMI 0.98e"; + const std::string VCMI_VERSION = "VCMI 0.98f"; const int BFIELD_WIDTH = 17; const int BFIELD_HEIGHT = 11; From c85c7f4b61fc54e87626acd3ce6a70ea08587a4b Mon Sep 17 00:00:00 2001 From: ArseniyShestakov Date: Tue, 3 Nov 2015 01:29:43 +0300 Subject: [PATCH 010/168] CPathfinder: always initialize all nodes within initializeGraph --- lib/CPathfinder.cpp | 43 +++++++++++++++++++++++-------------------- 1 file changed, 23 insertions(+), 20 deletions(-) diff --git a/lib/CPathfinder.cpp b/lib/CPathfinder.cpp index 901c7bad5..0efbc4efb 100644 --- a/lib/CPathfinder.cpp +++ b/lib/CPathfinder.cpp @@ -42,8 +42,6 @@ CPathfinder::CPathfinder(CPathsInfo &_out, CGameState *_gs, const CGHeroInstance throw std::runtime_error("Wrong checksum"); } - initializeGraph(); - if(hero->canFly()) options.useFlying = true; if(hero->canWalkOnSea()) @@ -51,6 +49,7 @@ CPathfinder::CPathfinder(CPathsInfo &_out, CGameState *_gs, const CGHeroInstance if(CGWhirlpool::isProtected(hero)) options.useTeleportWhirlpool = true; + initializeGraph(); neighbours.reserve(16); } @@ -79,10 +78,10 @@ void CPathfinder::calculatePaths() //logGlobal->infoStream() << boost::format("Calculating paths for hero %s (adress %d) of player %d") % hero->name % hero % hero->tempOwner; //initial tile - set cost on 0 and add to the queue - CGPathNode &initialNode = *out.getNode(out.hpos, hero->boat ? EPathfindingLayer::SAIL : EPathfindingLayer::LAND); - initialNode.turns = 0; - initialNode.moveRemains = hero->movement; - mq.push_back(&initialNode); + CGPathNode *initialNode = out.getNode(out.hpos, hero->boat ? EPathfindingLayer::SAIL : EPathfindingLayer::LAND); + initialNode->turns = 0; + initialNode->moveRemains = hero->movement; + mq.push_back(initialNode); while(!mq.empty()) { @@ -322,16 +321,16 @@ bool CPathfinder::isDestinationGuardian() void CPathfinder::initializeGraph() { - auto addNode = [&](EPathfindingLayer layer, const TerrainTile *tinfo, int3 pos) + auto initializeNode = [&](int3 pos, EPathfindingLayer layer, const TerrainTile *tinfo) { - CGPathNode &node = out.nodes[pos.x][pos.y][pos.z][layer]; - node.accessible = evaluateAccessibility(pos, tinfo); - node.turns = 0xff; - node.moveRemains = 0; - node.coord = pos; - node.land = tinfo->terType != ETerrainType::WATER; - node.theNodeBefore = nullptr; - node.layer = layer; + auto node = out.getNode(pos, layer); + node->accessible = evaluateAccessibility(pos, tinfo); + node->turns = 0xff; + node->moveRemains = 0; + node->coord = pos; + node->land = tinfo->terType != ETerrainType::WATER; + node->theNodeBefore = nullptr; + node->layer = layer; }; int3 pos; @@ -342,6 +341,10 @@ void CPathfinder::initializeGraph() for(pos.z=0; pos.z < out.sizes.z; ++pos.z) { const TerrainTile *tinfo = &gs->map->getTile(pos); + for(EPathfindingLayer i = EPathfindingLayer::LAND; i <= EPathfindingLayer::AIR; i.advance(1)) + { + initializeNode(pos, i, tinfo); + } switch (tinfo->terType) { case ETerrainType::WRONG: @@ -349,16 +352,16 @@ void CPathfinder::initializeGraph() case ETerrainType::ROCK: break; case ETerrainType::WATER: - addNode(EPathfindingLayer::SAIL, tinfo, pos); +// initializeNode(EPathfindingLayer::SAIL, tinfo, pos); // if(options.useFlying) - addNode(EPathfindingLayer::AIR, tinfo, pos); +// initializeNode(EPathfindingLayer::AIR, tinfo, pos); // if(options.useWaterWalking) - addNode(EPathfindingLayer::WATER, tinfo, pos); +// initializeNode(EPathfindingLayer::WATER, tinfo, pos); break; default: - addNode(EPathfindingLayer::LAND, tinfo, pos); +// initializeNode(EPathfindingLayer::LAND, tinfo, pos); // if(options.useFlying) - addNode(EPathfindingLayer::AIR, tinfo, pos); +// initializeNode(EPathfindingLayer::AIR, tinfo, pos); break; } } From b024237e8ade279301b5a9686409684788168986 Mon Sep 17 00:00:00 2001 From: AlexVinS Date: Wed, 14 Oct 2015 11:47:45 +0300 Subject: [PATCH 011/168] Remove stack instancies of removed stacks --- server/CGameHandler.cpp | 31 +++++++++++++++++++++++++++++-- 1 file changed, 29 insertions(+), 2 deletions(-) diff --git a/server/CGameHandler.cpp b/server/CGameHandler.cpp index 4634ae5c7..f6fc0ae6f 100644 --- a/server/CGameHandler.cpp +++ b/server/CGameHandler.cpp @@ -3854,12 +3854,13 @@ bool CGameHandler::makeBattleAction( BattleAction &ba ) const CStack *summoner = gs->curB->battleGetStackByID(ba.stackNumber), *destStack = gs->curB->battleGetStackByPos(ba.destinationTile, false); + CreatureID summonedType(summoner->getBonusLocalFirst(Selector::type(Bonus::DAEMON_SUMMONING))->subtype);//in case summoner can summon more than one type of monsters... scream! BattleStackAdded bsa; bsa.attacker = summoner->attackerOwned; - bsa.creID = CreatureID(summoner->getBonusLocalFirst(Selector::type(Bonus::DAEMON_SUMMONING))->subtype); //in case summoner can summon more than one type of monsters... scream! + bsa.creID = summonedType; ui64 risedHp = summoner->count * summoner->valOfBonuses(Bonus::DAEMON_SUMMONING, bsa.creID.toEnum()); - ui64 targetHealth = destStack->getCreature()->MaxHealth() * destStack->baseAmount; + ui64 targetHealth = destStack->getCreature()->MaxHealth() * destStack->baseAmount;//todo: ignore AGE effect ui64 canRiseHp = std::min(targetHealth, risedHp); ui32 canRiseAmount = canRiseHp / VLC->creh->creatures.at(bsa.creID)->MaxHealth(); @@ -5812,6 +5813,32 @@ CasualtiesAfterBattle::CasualtiesAfterBattle(const CArmedInstance *army, BattleI if(color == PlayerColor::UNFLAGGABLE) color = PlayerColor::NEUTRAL; + //1. Find removed stacks. + for(const auto & slotInfo : army->stacks) + { + const SlotID slot = slotInfo.first; + const CStackInstance * instance = slotInfo.second; + + if(nullptr != instance)//just in case + { + bool found = false; + for(const CStack * sta : bat->stacks) + { + if(sta->base == instance) + { + found = true; + break; + } + } + //stack in this slot was removed == it is dead + if(!found) + { + StackLocation sl(army, slot); + newStackCounts.push_back(TStackAndItsNewCount(sl, 0)); + } + } + } + for(CStack *st : bat->stacks) { if(vstd::contains(st->state, EBattleStackState::SUMMONED)) //don't take into account summoned stacks From c94bea51e30c01125d03e3feed64e73de0afa0b3 Mon Sep 17 00:00:00 2001 From: AlexVinS Date: Thu, 15 Oct 2015 17:03:22 +0300 Subject: [PATCH 012/168] Update army in case of summoned creatures --- server/CGameHandler.cpp | 77 +++++++++++++++++++++++++++++------------ server/CGameHandler.h | 9 +++-- 2 files changed, 60 insertions(+), 26 deletions(-) diff --git a/server/CGameHandler.cpp b/server/CGameHandler.cpp index f6fc0ae6f..dcf61666c 100644 --- a/server/CGameHandler.cpp +++ b/server/CGameHandler.cpp @@ -651,8 +651,8 @@ void CGameHandler::endBattle(int3 tile, const CGHeroInstance *hero1, const CGHer sendAndApply(&cs); } - cab1.takeFromArmy(this); - cab2.takeFromArmy(this); //take casualties after battle is deleted + cab1.updateArmy(this); + cab2.updateArmy(this); //take casualties after battle is deleted //if one hero has lost we will erase him if(battleResult.data->winner!=0 && hero1) @@ -5805,13 +5805,29 @@ void CGameHandler::duelFinished() return; } -CasualtiesAfterBattle::CasualtiesAfterBattle(const CArmedInstance *army, BattleInfo *bat) +CasualtiesAfterBattle::CasualtiesAfterBattle(const CArmedInstance * _army, BattleInfo *bat): + army(_army) { heroWithDeadCommander = ObjectInstanceID(); PlayerColor color = army->tempOwner; if(color == PlayerColor::UNFLAGGABLE) color = PlayerColor::NEUTRAL; + + auto killStack = [&, this](const SlotID slot, const CStackInstance * instance) + { + StackLocation sl(army, slot); + newStackCounts.push_back(TStackAndItsNewCount(sl, 0)); + if(nullptr == instance) + return; + auto c = dynamic_cast (instance); + if (c) //switch commander status to dead + { + auto h = dynamic_cast (army); + if (h && h->commander == c) + heroWithDeadCommander = army->id; //TODO: unify commander handling + } + }; //1. Find removed stacks. for(const auto & slotInfo : army->stacks) @@ -5832,10 +5848,7 @@ CasualtiesAfterBattle::CasualtiesAfterBattle(const CArmedInstance *army, BattleI } //stack in this slot was removed == it is dead if(!found) - { - StackLocation sl(army, slot); - newStackCounts.push_back(TStackAndItsNewCount(sl, 0)); - } + killStack(slot, instance); } } @@ -5859,29 +5872,32 @@ CasualtiesAfterBattle::CasualtiesAfterBattle(const CArmedInstance *army, BattleI removedWarMachines.push_back (ArtifactLocation(hero, hero->getArtPos(warMachine, true))); } } - - if(!army->slotEmpty(st->slot) && st->count < army->getStackCount(st->slot)) + + if(army->slotEmpty(st->slot)) { - StackLocation sl(army, st->slot); - if(st->alive()) - newStackCounts.push_back(std::pair(sl, st->count)); - else - newStackCounts.push_back(std::pair(sl, 0)); - } - if (st->base && !st->count) - { - auto c = dynamic_cast (st->base); - if (c) //switch commander status to dead + if(st->slot == SlotID(255) && !vstd::contains(st->state, EBattleStackState::SUMMONED) && st->alive()) { - auto h = dynamic_cast (army); - if (h && h->commander == c) - heroWithDeadCommander = army->id; //TODO: unify commander handling + //this stack was permanently summoned + const CreatureID summonedType = st->type->idNumber; + summoned[summonedType] += st->count; + } + } + else + { + if(st->count == 0 || !st->alive()) + { + killStack(st->slot, st->base); + } + else if(st->count < army->getStackCount(st->slot)) + { + StackLocation sl(army, st->slot); + newStackCounts.push_back(TStackAndItsNewCount(sl, st->count)); } } } } -void CasualtiesAfterBattle::takeFromArmy(CGameHandler *gh) +void CasualtiesAfterBattle::updateArmy(CGameHandler *gh) { for(TStackAndItsNewCount &ncount : newStackCounts) { @@ -5890,6 +5906,21 @@ void CasualtiesAfterBattle::takeFromArmy(CGameHandler *gh) else gh->eraseStack(ncount.first, true); } + for(auto summoned_iter : summoned) + { + SlotID slot = army->getSlotFor(summoned_iter.first); + if(slot.validSlot()) + { + StackLocation location(army, slot); + gh->addToSlot(location, summoned_iter.first.toCreature(), summoned_iter.second); + } + else + { + //even if it will be possible to summon anything permanently it should be checked for free slot + //necromancy is handled separately + gh->complain("No free slot to put summoned creature"); + } + } for (auto al : removedWarMachines) { gh->removeArtifact(al); diff --git a/server/CGameHandler.h b/server/CGameHandler.h index acf1698a2..bc2a336a7 100644 --- a/server/CGameHandler.h +++ b/server/CGameHandler.h @@ -71,13 +71,16 @@ public: struct CasualtiesAfterBattle { typedef std::pair TStackAndItsNewCount; + typedef std::map TSummoned; enum {ERASE = -1}; + const CArmedInstance * army; std::vector newStackCounts; std::vector removedWarMachines; - ObjectInstanceID heroWithDeadCommander; //TODO: unify stack loactions + TSummoned summoned; + ObjectInstanceID heroWithDeadCommander; //TODO: unify stack locations - CasualtiesAfterBattle(const CArmedInstance *army, BattleInfo *bat); - void takeFromArmy(CGameHandler *gh); + CasualtiesAfterBattle(const CArmedInstance * _army, BattleInfo *bat); + void updateArmy(CGameHandler *gh); }; class CGameHandler : public IGameCallback, CBattleInfoCallback From f6b9015324c5cb38adc6b4eb5d8ef4e814065141 Mon Sep 17 00:00:00 2001 From: AlexVinS Date: Thu, 15 Oct 2015 17:43:03 +0300 Subject: [PATCH 013/168] Use constant for summoned creature slot --- lib/GameConstants.cpp | 1 + lib/GameConstants.h | 1 + lib/NetPacksLib.cpp | 2 +- server/CGameHandler.cpp | 4 ++-- 4 files changed, 5 insertions(+), 3 deletions(-) diff --git a/lib/GameConstants.cpp b/lib/GameConstants.cpp index 9a48b476f..3b5acc577 100644 --- a/lib/GameConstants.cpp +++ b/lib/GameConstants.cpp @@ -19,6 +19,7 @@ #include "spells/CSpellHandler.h" const SlotID SlotID::COMMANDER_SLOT_PLACEHOLDER = SlotID(-2); +const SlotID SlotID::SUMMONED_SLOT_PLACEHOLDER = SlotID(255); const PlayerColor PlayerColor::CANNOT_DETERMINE = PlayerColor(253); const PlayerColor PlayerColor::UNFLAGGABLE = PlayerColor(254); const PlayerColor PlayerColor::NEUTRAL = PlayerColor(255); diff --git a/lib/GameConstants.h b/lib/GameConstants.h index 05d3128eb..98e344d6b 100644 --- a/lib/GameConstants.h +++ b/lib/GameConstants.h @@ -216,6 +216,7 @@ class SlotID : public BaseForID friend class CNonConstInfoCallback; DLL_LINKAGE static const SlotID COMMANDER_SLOT_PLACEHOLDER; + DLL_LINKAGE static const SlotID SUMMONED_SLOT_PLACEHOLDER; ///curB->generateNewStack(csbd, attacker, SlotID(255), pos); //TODO: netpacks? + CStack * addedStack = gs->curB->generateNewStack(csbd, attacker, SlotID::SUMMONED_SLOT_PLACEHOLDER, pos); //TODO: netpacks? if (summoned) addedStack->state.insert(EBattleStackState::SUMMONED); diff --git a/server/CGameHandler.cpp b/server/CGameHandler.cpp index dcf61666c..bd546edc5 100644 --- a/server/CGameHandler.cpp +++ b/server/CGameHandler.cpp @@ -5862,7 +5862,7 @@ CasualtiesAfterBattle::CasualtiesAfterBattle(const CArmedInstance * _army, Battl //FIXME: this info is also used in BattleInfo::calculateCasualties, refactor st->count = std::max (0, st->count - st->resurrected); - if (!st->count && !st->base) //we can imagine stacks of war mahcines that are not spawned by artifacts? + if (!st->count && !st->base) //we can imagine stacks of war machines that are not spawned by artifacts? { auto warMachine = VLC->arth->creatureToMachineID(st->type->idNumber); if (warMachine != ArtifactID::NONE) @@ -5875,7 +5875,7 @@ CasualtiesAfterBattle::CasualtiesAfterBattle(const CArmedInstance * _army, Battl if(army->slotEmpty(st->slot)) { - if(st->slot == SlotID(255) && !vstd::contains(st->state, EBattleStackState::SUMMONED) && st->alive()) + if(st->slot == SlotID::SUMMONED_SLOT_PLACEHOLDER && !vstd::contains(st->state, EBattleStackState::SUMMONED) && st->alive()) { //this stack was permanently summoned const CreatureID summonedType = st->type->idNumber; From fbebbf39f3642823628312d44dbb46132d2cd85c Mon Sep 17 00:00:00 2001 From: AlexVinS Date: Fri, 16 Oct 2015 09:11:11 +0300 Subject: [PATCH 014/168] Fix zero size stack summoned in case of basic resurrection. --- server/CGameHandler.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/CGameHandler.cpp b/server/CGameHandler.cpp index bd546edc5..051c778d1 100644 --- a/server/CGameHandler.cpp +++ b/server/CGameHandler.cpp @@ -5875,7 +5875,7 @@ CasualtiesAfterBattle::CasualtiesAfterBattle(const CArmedInstance * _army, Battl if(army->slotEmpty(st->slot)) { - if(st->slot == SlotID::SUMMONED_SLOT_PLACEHOLDER && !vstd::contains(st->state, EBattleStackState::SUMMONED) && st->alive()) + if(st->slot == SlotID::SUMMONED_SLOT_PLACEHOLDER && !vstd::contains(st->state, EBattleStackState::SUMMONED) && st->alive() && st->count > 0) { //this stack was permanently summoned const CreatureID summonedType = st->type->idNumber; From 6a9dc78dcd9735a6c9290f91f8f4d0b21370f51e Mon Sep 17 00:00:00 2001 From: AlexVinS Date: Sat, 24 Oct 2015 00:29:45 +0300 Subject: [PATCH 015/168] [WiP]Added some checks for RISE_DEMONS action. --- client/battle/CBattleInterface.cpp | 14 ++++++++++++-- server/CGameHandler.cpp | 10 +++++----- 2 files changed, 17 insertions(+), 7 deletions(-) diff --git a/client/battle/CBattleInterface.cpp b/client/battle/CBattleInterface.cpp index 452b41d76..a914e623e 100644 --- a/client/battle/CBattleInterface.cpp +++ b/client/battle/CBattleInterface.cpp @@ -2163,7 +2163,14 @@ void CBattleInterface::handleHex(BattleHex myNumber, int eventType) break; case RISE_DEMONS: if (shere && ourStack && !shere->alive()) - legalAction = true; + { + if(!(shere->hasBonusOfType(Bonus::UNDEAD) + || shere->hasBonusOfType(Bonus::NON_LIVING) + || vstd::contains(shere->state, EBattleStackState::SUMMONED) + || vstd::contains(shere->state, EBattleStackState::CLONED) + )) + legalAction = true; + } break; } if (legalAction) @@ -2320,7 +2327,10 @@ void CBattleInterface::handleHex(BattleHex myNumber, int eventType) break; case RISE_DEMONS: cursorType = ECursor::SPELLBOOK; - realizeAction = [=]{ giveCommand(Battle::DAEMON_SUMMONING, myNumber, activeStack->ID); }; + realizeAction = [=] + { + giveCommand(Battle::DAEMON_SUMMONING, myNumber, activeStack->ID); + }; break; case CATAPULT: cursorFrame = ECursor::COMBAT_SHOOT_CATAPULT; diff --git a/server/CGameHandler.cpp b/server/CGameHandler.cpp index 051c778d1..f747a4bc5 100644 --- a/server/CGameHandler.cpp +++ b/server/CGameHandler.cpp @@ -3848,9 +3848,6 @@ bool CGameHandler::makeBattleAction( BattleAction &ba ) //TODO: From Strategija: //Summon Demon is a level 2 spell. { - StartAction start_action(ba); - sendAndApply(&start_action); - const CStack *summoner = gs->curB->battleGetStackByID(ba.stackNumber), *destStack = gs->curB->battleGetStackByPos(ba.destinationTile, false); @@ -3872,6 +3869,9 @@ bool CGameHandler::makeBattleAction( BattleAction &ba ) if (bsa.amount) //there's rare possibility single creature cannot rise desired type { + StartAction start_action(ba); + sendAndApply(&start_action); + BattleStacksRemoved bsr; //remove body bsr.stackIDs.insert(destStack->ID); sendAndApply(&bsr); @@ -3883,9 +3883,9 @@ bool CGameHandler::makeBattleAction( BattleAction &ba ) ssp.val = -1; ssp.absolute = false; sendAndApply(&ssp); - } - sendAndApply(&end_action); + sendAndApply(&end_action); + } break; } case Battle::MONSTER_SPELL: From 4b5cb3599a9d6495e3fafd732262896d2e89601a Mon Sep 17 00:00:00 2001 From: AlexVinS Date: Sun, 1 Nov 2015 01:05:31 +0300 Subject: [PATCH 016/168] Do not try to rise demons from war machines --- client/battle/CBattleInterface.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/client/battle/CBattleInterface.cpp b/client/battle/CBattleInterface.cpp index a914e623e..c1ea5ec45 100644 --- a/client/battle/CBattleInterface.cpp +++ b/client/battle/CBattleInterface.cpp @@ -2168,6 +2168,7 @@ void CBattleInterface::handleHex(BattleHex myNumber, int eventType) || shere->hasBonusOfType(Bonus::NON_LIVING) || vstd::contains(shere->state, EBattleStackState::SUMMONED) || vstd::contains(shere->state, EBattleStackState::CLONED) + || shere->hasBonusOfType(Bonus::SIEGE_WEAPON) )) legalAction = true; } From dfd70849e91a85785c13d851d7c0ef74cc861c95 Mon Sep 17 00:00:00 2001 From: ArseniyShestakov Date: Tue, 3 Nov 2015 03:25:12 +0300 Subject: [PATCH 017/168] CPathfinder: restore selective tile initialization to initializeGraph This way we can avoid layer checks when calculating paths by ignoring unitialized tiles entirely. Also at this point pathfinder and movement actually works for everything except flying. --- lib/CPathfinder.cpp | 39 +++++++++++++++++---------------------- 1 file changed, 17 insertions(+), 22 deletions(-) diff --git a/lib/CPathfinder.cpp b/lib/CPathfinder.cpp index 0efbc4efb..ac60c842f 100644 --- a/lib/CPathfinder.cpp +++ b/lib/CPathfinder.cpp @@ -104,11 +104,15 @@ void CPathfinder::calculatePaths() { useEmbarkCost = 0; //0 - usual movement; 1 - embark; 2 - disembark dp = out.getNode(neighbour, i); - if(cp->layer != i && isLayerTransitionPossible()) + if(dp->accessible == CGPathNode::NOT_SET) + continue; + + if(cp->layer != i && !isLayerTransitionPossible()) continue; if(!isMovementPossible()) continue; + int cost = gs->getMovementCost(hero, cp->coord, dp->coord, movement); int remains = movement - cost; if(useEmbarkCost) @@ -241,16 +245,12 @@ bool CPathfinder::isMovementPossible() break; case EPathfindingLayer::AIR: - if(!options.useFlying) - return false; - if(!canMoveBetween(cp->coord, dp->coord)) - return false; + //if(!canMoveBetween(cp->coord, dp->coord)) + // return false; break; case EPathfindingLayer::WATER: - if(!options.useWaterWalking) - return false; if(!canMoveBetween(cp->coord, dp->coord) || dp->accessible == CGPathNode::BLOCKED) return false; if(isDestinationGuarded()) @@ -341,10 +341,6 @@ void CPathfinder::initializeGraph() for(pos.z=0; pos.z < out.sizes.z; ++pos.z) { const TerrainTile *tinfo = &gs->map->getTile(pos); - for(EPathfindingLayer i = EPathfindingLayer::LAND; i <= EPathfindingLayer::AIR; i.advance(1)) - { - initializeNode(pos, i, tinfo); - } switch (tinfo->terType) { case ETerrainType::WRONG: @@ -352,16 +348,16 @@ void CPathfinder::initializeGraph() case ETerrainType::ROCK: break; case ETerrainType::WATER: -// initializeNode(EPathfindingLayer::SAIL, tinfo, pos); -// if(options.useFlying) -// initializeNode(EPathfindingLayer::AIR, tinfo, pos); -// if(options.useWaterWalking) -// initializeNode(EPathfindingLayer::WATER, tinfo, pos); + initializeNode(pos, EPathfindingLayer::SAIL, tinfo); + if(options.useFlying) + initializeNode(pos, EPathfindingLayer::AIR, tinfo); + if(options.useWaterWalking) + initializeNode(pos, EPathfindingLayer::WATER, tinfo); break; default: -// initializeNode(EPathfindingLayer::LAND, tinfo, pos); -// if(options.useFlying) -// initializeNode(EPathfindingLayer::AIR, tinfo, pos); + initializeNode(pos, EPathfindingLayer::LAND, tinfo); + if(options.useFlying) + initializeNode(pos, EPathfindingLayer::AIR, tinfo); break; } } @@ -457,8 +453,7 @@ bool CPathfinder::canVisitObject() const bool CPathfinder::isLayerTransitionPossible() { - Obj destTopVisObjID = dt->topVisitableId(); - if((cp->layer == EPathfindingLayer::AIR || EPathfindingLayer::WATER) + if((cp->layer == EPathfindingLayer::AIR || cp->layer == EPathfindingLayer::WATER) && dp->layer != EPathfindingLayer::LAND) { return false; @@ -479,6 +474,7 @@ bool CPathfinder::isLayerTransitionPossible() } else if(cp->layer == EPathfindingLayer::LAND && dp->layer == EPathfindingLayer::SAIL) { + Obj destTopVisObjID = dt->topVisitableId(); if(dp->accessible == CGPathNode::ACCESSIBLE || destTopVisObjID < 0) //cannot enter empty water tile from land -> it has to be visitable return false; if(destTopVisObjID != Obj::HERO && destTopVisObjID != Obj::BOAT) //only boat or hero can be accessed from land @@ -580,7 +576,6 @@ bool CPathsInfo::getPath(CGPath &out, const int3 &dst, const EPathfindingLayer & if(!curnode->theNodeBefore) return false; - while(curnode) { CGPathNode cpn = *curnode; From 8b6b4e2e0b81169444a1879b3ac505ca346925f3 Mon Sep 17 00:00:00 2001 From: ArseniyShestakov Date: Tue, 3 Nov 2015 14:40:36 +0300 Subject: [PATCH 018/168] Fix header case for Linux to Windows cross compilation using MingGW --- lib/CConsoleHandler.cpp | 2 +- lib/VCMIDirs.cpp | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/CConsoleHandler.cpp b/lib/CConsoleHandler.cpp index 649df26aa..20a992f68 100644 --- a/lib/CConsoleHandler.cpp +++ b/lib/CConsoleHandler.cpp @@ -27,7 +27,7 @@ DLL_LINKAGE CConsoleHandler * console = nullptr; #define CONSOLE_GRAY "\x1b[1;30m" #define CONSOLE_TEAL "\x1b[1;36m" #else - #include + #include #include #ifndef __MINGW32__ #pragma comment(lib, "dbghelp.lib") diff --git a/lib/VCMIDirs.cpp b/lib/VCMIDirs.cpp index 679b68fd0..7c2110afa 100644 --- a/lib/VCMIDirs.cpp +++ b/lib/VCMIDirs.cpp @@ -34,9 +34,9 @@ void IVCMIDirs::init() #endif #endif // __MINGW32__ -#include -#include -#include +#include +#include +#include // Generates script file named _temp.bat in 'to' directory and runs it // Script will: From 75a76c0bf0a540b5f7ee52a35d212d114bd8f021 Mon Sep 17 00:00:00 2001 From: edeksumo Date: Wed, 4 Nov 2015 00:19:40 +0100 Subject: [PATCH 019/168] FixDefenceAni --- client/battle/CBattleAnimations.cpp | 6 ++---- lib/GameConstants.h | 2 +- lib/NetPacksLib.cpp | 6 +++++- 3 files changed, 8 insertions(+), 6 deletions(-) diff --git a/client/battle/CBattleAnimations.cpp b/client/battle/CBattleAnimations.cpp index 37b9d722f..911b9caad 100644 --- a/client/battle/CBattleAnimations.cpp +++ b/client/battle/CBattleAnimations.cpp @@ -233,7 +233,7 @@ std::string CDefenceAnimation::getMySound() if(killed) return battle_sound(stack->getCreature(), killed); - if (stack->valOfBonuses(Bonus::UntilGetsTurn)) + if (vstd::contains(stack->state, EBattleStackState::DEFENDING_ANI)) return battle_sound(stack->getCreature(), defend); return battle_sound(stack->getCreature(), wince); } @@ -243,9 +243,7 @@ CCreatureAnim::EAnimType CDefenceAnimation::getMyAnimType() if(killed) return CCreatureAnim::DEATH; - auto selector = CSelector(Bonus::UntilGetsTurn).And(Selector::typeSubtype(Bonus::PRIMARY_SKILL, PrimarySkill::DEFENSE)); - - if(stack->valOfBonuses(selector)) + if (vstd::contains(stack->state, EBattleStackState::DEFENDING_ANI)) return CCreatureAnim::DEFENCE; return CCreatureAnim::HITTED; } diff --git a/lib/GameConstants.h b/lib/GameConstants.h index 0a1f74945..8e12ccc34 100644 --- a/lib/GameConstants.h +++ b/lib/GameConstants.h @@ -433,7 +433,7 @@ namespace EMarketMode namespace EBattleStackState { enum EBattleStackState{ALIVE = 180, SUMMONED, CLONED, DEAD_CLONE, HAD_MORALE, WAITING, MOVED, DEFENDING, FEAR, - DRAINED_MANA /*remember to drain mana only once per turn*/}; + DRAINED_MANA /*remember to drain mana only once per turn*/, DEFENDING_ANI/*only for animation*/}; } namespace ECommander diff --git a/lib/NetPacksLib.cpp b/lib/NetPacksLib.cpp index ede41bd8b..742663b79 100644 --- a/lib/NetPacksLib.cpp +++ b/lib/NetPacksLib.cpp @@ -1304,6 +1304,9 @@ DLL_LINKAGE void StartAction::applyGs( CGameState *gs ) { CStack *st = gs->curB->getStack(ba.stackNumber); + if(vstd::contains(st->state, EBattleStackState::DEFENDING_ANI)) + st->state -= EBattleStackState::DEFENDING_ANI; + if(ba.actionType == Battle::END_TACTIC_PHASE) { gs->curB->tacticDistance = 0; @@ -1329,7 +1332,8 @@ DLL_LINKAGE void StartAction::applyGs( CGameState *gs ) switch(ba.actionType) { case Battle::DEFEND: - st->state.insert(EBattleStackState::DEFENDING); + st->state.insert(EBattleStackState::DEFENDING); + st->state.insert(EBattleStackState::DEFENDING_ANI); break; case Battle::WAIT: st->state.insert(EBattleStackState::WAITING); From 595deda2707d9093a1dd45e167570ee66909127d Mon Sep 17 00:00:00 2001 From: ArseniyShestakov Date: Wed, 4 Nov 2015 11:47:43 +0300 Subject: [PATCH 020/168] CPathfinder: rename functions to better represent what they doing --- lib/CPathfinder.cpp | 8 ++++---- lib/CPathfinder.h | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/lib/CPathfinder.cpp b/lib/CPathfinder.cpp index ac60c842f..95cd4c615 100644 --- a/lib/CPathfinder.cpp +++ b/lib/CPathfinder.cpp @@ -110,7 +110,7 @@ void CPathfinder::calculatePaths() if(cp->layer != i && !isLayerTransitionPossible()) continue; - if(!isMovementPossible()) + if(!isMovementToDestPossible()) continue; int cost = gs->getMovementCost(hero, cp->coord, dp->coord, movement); @@ -138,7 +138,7 @@ void CPathfinder::calculatePaths() dp->turns = turnAtNextTile; dp->theNodeBefore = cp; - if(checkDestinationTile()) + if(isMovementAfterDestPossible()) mq.push_back(dp); } } @@ -225,7 +225,7 @@ void CPathfinder::addTeleportExits(bool noTeleportExcludes) } } -bool CPathfinder::isMovementPossible() +bool CPathfinder::isMovementToDestPossible() { switch (dp->layer) { @@ -263,7 +263,7 @@ bool CPathfinder::isMovementPossible() return true; } -bool CPathfinder::checkDestinationTile() +bool CPathfinder::isMovementAfterDestPossible() { if(dp->accessible == CGPathNode::ACCESSIBLE) return true; diff --git a/lib/CPathfinder.h b/lib/CPathfinder.h index 0626ffe24..4a7b9f790 100644 --- a/lib/CPathfinder.h +++ b/lib/CPathfinder.h @@ -105,8 +105,8 @@ private: void addNeighbours(const int3 &coord); void addTeleportExits(bool noTeleportExcludes = false); - 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 isMovementToDestPossible(); //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 isMovementAfterDestPossible(); int3 getSourceGuardPosition(); bool isSourceGuarded(); From 934c68273392be2cce2943b15a0c80e78c934b1e Mon Sep 17 00:00:00 2001 From: ArseniyShestakov Date: Wed, 4 Nov 2015 11:53:52 +0300 Subject: [PATCH 021/168] CPathfinder: always add air and water layer nodes to queue It's should be possible to go into air layer from visitable object (but opposite isn't allowed). And when walking on water player can't really interact with any object at all so future movement always possible. --- lib/CPathfinder.cpp | 32 ++++++++++++++++++++++---------- 1 file changed, 22 insertions(+), 10 deletions(-) diff --git a/lib/CPathfinder.cpp b/lib/CPathfinder.cpp index 95cd4c615..0647981d1 100644 --- a/lib/CPathfinder.cpp +++ b/lib/CPathfinder.cpp @@ -265,16 +265,28 @@ bool CPathfinder::isMovementToDestPossible() bool CPathfinder::isMovementAfterDestPossible() { - if(dp->accessible == CGPathNode::ACCESSIBLE) - return true; - if(dp->coord == CGHeroInstance::convertPosition(hero->pos, false)) - return true; // This one is tricky, we can ignore fact that tile is not ACCESSIBLE in case if it's our hero block it. Though this need investigation - if(dp->accessible == CGPathNode::VISITABLE && CGTeleport::isTeleport(dt->topVisitableObj())) - return true; // For now we'll always allow transit for teleporters - if(useEmbarkCost && options.useEmbarkAndDisembark) - return true; - if(isDestinationGuarded() && !isSourceGuarded()) - return true; // Can step into a hostile tile once + switch (dp->layer) + { + case EPathfindingLayer::LAND: + case EPathfindingLayer::SAIL: + if(dp->accessible == CGPathNode::ACCESSIBLE) + return true; + if(dp->coord == CGHeroInstance::convertPosition(hero->pos, false)) + return true; // This one is tricky, we can ignore fact that tile is not ACCESSIBLE in case if it's our hero block it. Though this need investigation + if(dp->accessible == CGPathNode::VISITABLE && CGTeleport::isTeleport(dt->topVisitableObj())) + return true; // For now we'll always allow transit for teleporters + if(useEmbarkCost && options.useEmbarkAndDisembark) + return true; + if(isDestinationGuarded() && !isSourceGuarded()) + return true; // Can step into a hostile tile once + break; + + case EPathfindingLayer::AIR: + case EPathfindingLayer::WATER: + return true; + + break; + } return false; } From 330c1666fccd666ba507fbed9d0bf9941c3af954 Mon Sep 17 00:00:00 2001 From: ArseniyShestakov Date: Wed, 4 Nov 2015 12:29:51 +0300 Subject: [PATCH 022/168] CPathfinder: move isLayerTransitionPossible and remove outdated comment --- lib/CPathfinder.cpp | 68 ++++++++++++++++++++++----------------------- lib/CPathfinder.h | 4 +-- 2 files changed, 36 insertions(+), 36 deletions(-) diff --git a/lib/CPathfinder.cpp b/lib/CPathfinder.cpp index 0647981d1..9f4911d91 100644 --- a/lib/CPathfinder.cpp +++ b/lib/CPathfinder.cpp @@ -225,6 +225,40 @@ void CPathfinder::addTeleportExits(bool noTeleportExcludes) } } +bool CPathfinder::isLayerTransitionPossible() +{ + if((cp->layer == EPathfindingLayer::AIR || cp->layer == EPathfindingLayer::WATER) + && dp->layer != EPathfindingLayer::LAND) + { + return false; + } + else if(cp->layer == EPathfindingLayer::SAIL && dp->layer != EPathfindingLayer::LAND) + return false; + else if(cp->layer == EPathfindingLayer::SAIL && dp->layer == EPathfindingLayer::LAND) + { + if(!dt->isCoastal()) + return false; + + //tile must be accessible -> exception: unblocked blockvis tiles -> clear but guarded by nearby monster coast + if((dp->accessible != CGPathNode::ACCESSIBLE && (dp->accessible != CGPathNode::BLOCKVIS || dt->blocked)) + || dt->visitable) //TODO: passableness problem -> town says it's passable (thus accessible) but we obviously can't disembark onto town gate + return false; + + useEmbarkCost = 2; + } + else if(cp->layer == EPathfindingLayer::LAND && dp->layer == EPathfindingLayer::SAIL) + { + Obj destTopVisObjID = dt->topVisitableId(); + if(dp->accessible == CGPathNode::ACCESSIBLE || destTopVisObjID < 0) //cannot enter empty water tile from land -> it has to be visitable + return false; + if(destTopVisObjID != Obj::HERO && destTopVisObjID != Obj::BOAT) //only boat or hero can be accessed from land + return false; + if(destTopVisObjID == Obj::BOAT) + useEmbarkCost = 1; + } + return true; +} + bool CPathfinder::isMovementToDestPossible() { switch (dp->layer) @@ -463,40 +497,6 @@ bool CPathfinder::canVisitObject() const return cp->layer == EPathfindingLayer::LAND || cp->layer == EPathfindingLayer::SAIL; } -bool CPathfinder::isLayerTransitionPossible() -{ - if((cp->layer == EPathfindingLayer::AIR || cp->layer == EPathfindingLayer::WATER) - && dp->layer != EPathfindingLayer::LAND) - { - return false; - } - else if(cp->layer == EPathfindingLayer::SAIL && dp->layer != EPathfindingLayer::LAND) - return false; - else if(cp->layer == EPathfindingLayer::SAIL && dp->layer == EPathfindingLayer::LAND) - { - if(!dt->isCoastal()) - return false; - - //tile must be accessible -> exception: unblocked blockvis tiles -> clear but guarded by nearby monster coast - if((dp->accessible != CGPathNode::ACCESSIBLE && (dp->accessible != CGPathNode::BLOCKVIS || dt->blocked)) - || dt->visitable) //TODO: passableness problem -> town says it's passable (thus accessible) but we obviously can't disembark onto town gate - return false; - - useEmbarkCost = 2; - } - else if(cp->layer == EPathfindingLayer::LAND && dp->layer == EPathfindingLayer::SAIL) - { - Obj destTopVisObjID = dt->topVisitableId(); - if(dp->accessible == CGPathNode::ACCESSIBLE || destTopVisObjID < 0) //cannot enter empty water tile from land -> it has to be visitable - return false; - if(destTopVisObjID != Obj::HERO && destTopVisObjID != Obj::BOAT) //only boat or hero can be accessed from land - return false; - if(destTopVisObjID == Obj::BOAT) - useEmbarkCost = 1; - } - return true; -} - CGPathNode::CGPathNode() :coord(-1,-1,-1) { diff --git a/lib/CPathfinder.h b/lib/CPathfinder.h index 4a7b9f790..660e472cc 100644 --- a/lib/CPathfinder.h +++ b/lib/CPathfinder.h @@ -105,7 +105,8 @@ private: void addNeighbours(const int3 &coord); void addTeleportExits(bool noTeleportExcludes = false); - bool isMovementToDestPossible(); //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 isLayerTransitionPossible(); + bool isMovementToDestPossible(); bool isMovementAfterDestPossible(); int3 getSourceGuardPosition(); @@ -125,5 +126,4 @@ private: bool canVisitObject() const; - bool isLayerTransitionPossible(); }; From 1bc335323d8a4618eea31341bdf61474e7127816 Mon Sep 17 00:00:00 2001 From: ArseniyShestakov Date: Wed, 4 Nov 2015 15:05:22 +0300 Subject: [PATCH 023/168] CPathfinder: add lightweightFlyingMode option suggested by @alexvins --- lib/CPathfinder.cpp | 7 +++++++ lib/CPathfinder.h | 5 +++++ 2 files changed, 12 insertions(+) diff --git a/lib/CPathfinder.cpp b/lib/CPathfinder.cpp index 9f4911d91..da35e6e0b 100644 --- a/lib/CPathfinder.cpp +++ b/lib/CPathfinder.cpp @@ -27,6 +27,8 @@ CPathfinder::PathfinderOptions::PathfinderOptions() useTeleportOneWay = true; useTeleportOneWayRandom = false; useTeleportWhirlpool = false; + + lightweightFlyingMode = false; } CPathfinder::CPathfinder(CPathsInfo &_out, CGameState *_gs, const CGHeroInstance *_hero) : CGameInfoCallback(_gs, boost::optional()), out(_out), hero(_hero), FoW(getPlayerTeam(hero->tempOwner)->fogOfWarMap) @@ -232,6 +234,11 @@ bool CPathfinder::isLayerTransitionPossible() { return false; } + else if(cp->layer == EPathfindingLayer::LAND && dp->layer == EPathfindingLayer::AIR) + { + if(options.lightweightFlyingMode && cp->coord != hero->getPosition(false)) + return false; + } else if(cp->layer == EPathfindingLayer::SAIL && dp->layer != EPathfindingLayer::LAND) return false; else if(cp->layer == EPathfindingLayer::SAIL && dp->layer == EPathfindingLayer::LAND) diff --git a/lib/CPathfinder.h b/lib/CPathfinder.h index 660e472cc..e6b1f6791 100644 --- a/lib/CPathfinder.h +++ b/lib/CPathfinder.h @@ -85,6 +85,11 @@ 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) + /// 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. + bool lightweightFlyingMode; + PathfinderOptions(); } options; From f4dea88e3b76db093dbdb5fce91e7f614332cd27 Mon Sep 17 00:00:00 2001 From: ArseniyShestakov Date: Wed, 4 Nov 2015 15:38:15 +0300 Subject: [PATCH 024/168] CPathfinder: get rid of hero object usage when it's not needed --- lib/CPathfinder.cpp | 14 +++++++++----- lib/CPathfinder.h | 1 + 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/lib/CPathfinder.cpp b/lib/CPathfinder.cpp index da35e6e0b..f1ab61629 100644 --- a/lib/CPathfinder.cpp +++ b/lib/CPathfinder.cpp @@ -172,7 +172,7 @@ void CPathfinder::addNeighbours(const int3 &coord) std::vector tiles; gs->getNeighbours(*ct, coord, tiles, boost::logic::indeterminate, !cp->land); - sTileObj = ct->topVisitableObj(coord == CGHeroInstance::convertPosition(hero->pos, false)); + sTileObj = ct->topVisitableObj(coord == out.hpos); if(canVisitObject()) { if(sTileObj) @@ -236,7 +236,7 @@ bool CPathfinder::isLayerTransitionPossible() } else if(cp->layer == EPathfindingLayer::LAND && dp->layer == EPathfindingLayer::AIR) { - if(options.lightweightFlyingMode && cp->coord != hero->getPosition(false)) + if(options.lightweightFlyingMode && !isSourceInitialPosition()) return false; } else if(cp->layer == EPathfindingLayer::SAIL && dp->layer != EPathfindingLayer::LAND) @@ -312,7 +312,7 @@ bool CPathfinder::isMovementAfterDestPossible() case EPathfindingLayer::SAIL: if(dp->accessible == CGPathNode::ACCESSIBLE) return true; - if(dp->coord == CGHeroInstance::convertPosition(hero->pos, false)) + if(dp->coord == out.hpos) return true; // This one is tricky, we can ignore fact that tile is not ACCESSIBLE in case if it's our hero block it. Though this need investigation if(dp->accessible == CGPathNode::VISITABLE && CGTeleport::isTeleport(dt->topVisitableObj())) return true; // For now we'll always allow transit for teleporters @@ -332,6 +332,11 @@ bool CPathfinder::isMovementAfterDestPossible() return false; } +bool CPathfinder::isSourceInitialPosition() +{ + return cp->coord == out.hpos; +} + int3 CPathfinder::getSourceGuardPosition() { return gs->map->guardingCreaturePositions[cp->coord.x][cp->coord.y][cp->coord.z]; @@ -341,8 +346,7 @@ bool CPathfinder::isSourceGuarded() { //map can start with hero on guarded tile or teleport there using dimension door //so threat tile hero standing on like it's not guarded because it's should be possible to move out of here - if(getSourceGuardPosition() != int3(-1, -1, -1) - && cp->coord != hero->getPosition(false)) + if(getSourceGuardPosition() != int3(-1, -1, -1) && !isSourceInitialPosition()) { //special case -> hero embarked a boat standing on a guarded tile -> we must allow to move away from that tile if(cp->accessible != CGPathNode::VISITABLE diff --git a/lib/CPathfinder.h b/lib/CPathfinder.h index e6b1f6791..7d591cdbb 100644 --- a/lib/CPathfinder.h +++ b/lib/CPathfinder.h @@ -114,6 +114,7 @@ private: bool isMovementToDestPossible(); bool isMovementAfterDestPossible(); + bool isSourceInitialPosition(); int3 getSourceGuardPosition(); bool isSourceGuarded(); bool isDestinationGuarded(); From dd525da15332128cb049ed91a6d915764a2948b0 Mon Sep 17 00:00:00 2001 From: edeksumo Date: Wed, 4 Nov 2015 23:37:53 +0100 Subject: [PATCH 025/168] Formatting --- client/battle/CBattleAnimations.cpp | 2 ++ lib/GameConstants.h | 17 +++++++++++++++-- lib/NetPacksLib.cpp | 2 +- 3 files changed, 18 insertions(+), 3 deletions(-) diff --git a/client/battle/CBattleAnimations.cpp b/client/battle/CBattleAnimations.cpp index 911b9caad..77599d74a 100644 --- a/client/battle/CBattleAnimations.cpp +++ b/client/battle/CBattleAnimations.cpp @@ -235,6 +235,7 @@ std::string CDefenceAnimation::getMySound() if (vstd::contains(stack->state, EBattleStackState::DEFENDING_ANI)) return battle_sound(stack->getCreature(), defend); + return battle_sound(stack->getCreature(), wince); } @@ -245,6 +246,7 @@ CCreatureAnim::EAnimType CDefenceAnimation::getMyAnimType() if (vstd::contains(stack->state, EBattleStackState::DEFENDING_ANI)) return CCreatureAnim::DEFENCE; + return CCreatureAnim::HITTED; } diff --git a/lib/GameConstants.h b/lib/GameConstants.h index 8e12ccc34..d1fd781fd 100644 --- a/lib/GameConstants.h +++ b/lib/GameConstants.h @@ -432,8 +432,21 @@ namespace EMarketMode namespace EBattleStackState { - enum EBattleStackState{ALIVE = 180, SUMMONED, CLONED, DEAD_CLONE, HAD_MORALE, WAITING, MOVED, DEFENDING, FEAR, - DRAINED_MANA /*remember to drain mana only once per turn*/, DEFENDING_ANI/*only for animation*/}; + enum EBattleStackState + { + ALIVE = 180, + SUMMONED, CLONED, + DEAD_CLONE, + HAD_MORALE, + WAITING, + MOVED, + DEFENDING, + FEAR, + //remember to drain mana only once per turn + DRAINED_MANA, + //only for defending animation + DEFENDING_ANI + }; } namespace ECommander diff --git a/lib/NetPacksLib.cpp b/lib/NetPacksLib.cpp index 742663b79..3cce6063b 100644 --- a/lib/NetPacksLib.cpp +++ b/lib/NetPacksLib.cpp @@ -1332,7 +1332,7 @@ DLL_LINKAGE void StartAction::applyGs( CGameState *gs ) switch(ba.actionType) { case Battle::DEFEND: - st->state.insert(EBattleStackState::DEFENDING); + st->state.insert(EBattleStackState::DEFENDING); st->state.insert(EBattleStackState::DEFENDING_ANI); break; case Battle::WAIT: From ac12a0735e22f421cb4b5dfb642d9c8ad137810a Mon Sep 17 00:00:00 2001 From: ArseniyShestakov Date: Thu, 5 Nov 2015 10:02:13 +0300 Subject: [PATCH 026/168] Plumbing on client and server to make flying actually work --- AI/VCAI/VCAI.cpp | 2 ++ client/CPlayerInterface.cpp | 2 ++ server/CGameHandler.cpp | 24 ++++++++++++++++++------ 3 files changed, 22 insertions(+), 6 deletions(-) diff --git a/AI/VCAI/VCAI.cpp b/AI/VCAI/VCAI.cpp index 4ef6f169d..73ac5dab2 100644 --- a/AI/VCAI/VCAI.cpp +++ b/AI/VCAI/VCAI.cpp @@ -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); diff --git a/client/CPlayerInterface.cpp b/client/CPlayerInterface.cpp index a6109b0d6..9c55ce471 100644 --- a/client/CPlayerInterface.cpp +++ b/client/CPlayerInterface.cpp @@ -2689,6 +2689,8 @@ void CPlayerInterface::doMoveHero(const CGHeroInstance * h, CGPath path) { // 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); diff --git a/server/CGameHandler.cpp b/server/CGameHandler.cpp index 4634ae5c7..b1060fa62 100644 --- a/server/CGameHandler.cpp +++ b/server/CGameHandler.cpp @@ -1782,9 +1782,9 @@ bool CGameHandler::moveHero( ObjectInstanceID hid, int3 dst, ui8 teleporting, bo //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 && !h->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 && !h->canWalkOnSea() && !h->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!")) @@ -1843,8 +1843,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); @@ -1905,10 +1904,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(h->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; } } From 3de94a8b9972d5d74a7a6d81cef18de144671b58 Mon Sep 17 00:00:00 2001 From: ArseniyShestakov Date: Thu, 5 Nov 2015 10:22:38 +0300 Subject: [PATCH 027/168] CPathfinder: don't allow future movement after guarded tile There is two exceptions: - Hero start movement from guarded tile. - Hero that embarking into boat that standing on guarded tile. In other cases future movement is impossible. --- lib/CPathfinder.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/lib/CPathfinder.cpp b/lib/CPathfinder.cpp index f1ab61629..992c94991 100644 --- a/lib/CPathfinder.cpp +++ b/lib/CPathfinder.cpp @@ -318,8 +318,6 @@ bool CPathfinder::isMovementAfterDestPossible() return true; // For now we'll always allow transit for teleporters if(useEmbarkCost && options.useEmbarkAndDisembark) return true; - if(isDestinationGuarded() && !isSourceGuarded()) - return true; // Can step into a hostile tile once break; case EPathfindingLayer::AIR: From c188ff0a9291308e398b62d3115aba728f5b537d Mon Sep 17 00:00:00 2001 From: ArseniyShestakov Date: Thu, 5 Nov 2015 10:37:22 +0300 Subject: [PATCH 028/168] doMoveHero: dont stop movement on guarded tiles when transit used --- client/CPlayerInterface.cpp | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/client/CPlayerInterface.cpp b/client/CPlayerInterface.cpp index 9c55ce471..7e3b5a243 100644 --- a/client/CPlayerInterface.cpp +++ b/client/CPlayerInterface.cpp @@ -2683,20 +2683,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 if(path.nodes[i-1].layer == EPathfindingLayer::AIR) - doMovement(endpos, true); - else - doMovement(endpos, false); + 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; } From 62dc070c0aabb317530a7df1e554a1ed3c6b1c90 Mon Sep 17 00:00:00 2001 From: ArseniyShestakov Date: Thu, 5 Nov 2015 10:50:47 +0300 Subject: [PATCH 029/168] moveHero: add transit validation and avoid embarking on transit Hero shouldn't embark into boat when attempt to transit over air layer. --- server/CGameHandler.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/server/CGameHandler.cpp b/server/CGameHandler.cpp index b1060fa62..c9c8ffb32 100644 --- a/server/CGameHandler.cpp +++ b/server/CGameHandler.cpp @@ -1794,6 +1794,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 && !h->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"))*/) { @@ -1866,7 +1868,7 @@ bool CGameHandler::moveHero( ObjectInstanceID hid, int3 dst, ui8 teleporting, bo }; - if(embarking) + if(!transit && embarking) { tmh.movePoints = h->movementPointsAfterEmbark(h->movement, cost, false); return doMove(TryMoveHero::EMBARK, IGNORE_GUARDS, DONT_VISIT_DEST, LEAVING_TILE); From a1290f548babb9fa92e862c0b64b6423666fafcf Mon Sep 17 00:00:00 2001 From: ArseniyShestakov Date: Thu, 5 Nov 2015 11:25:41 +0300 Subject: [PATCH 030/168] CPathfinder: only allow water walking over accessible tiles --- lib/CPathfinder.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/CPathfinder.cpp b/lib/CPathfinder.cpp index 992c94991..b4cd10d1f 100644 --- a/lib/CPathfinder.cpp +++ b/lib/CPathfinder.cpp @@ -292,7 +292,7 @@ bool CPathfinder::isMovementToDestPossible() break; case EPathfindingLayer::WATER: - if(!canMoveBetween(cp->coord, dp->coord) || dp->accessible == CGPathNode::BLOCKED) + if(!canMoveBetween(cp->coord, dp->coord) || dp->accessible != CGPathNode::ACCESSIBLE) return false; if(isDestinationGuarded()) return false; From 5f3e9deda768e140ff5d64c8410c78f1532cc634 Mon Sep 17 00:00:00 2001 From: ArseniyShestakov Date: Thu, 5 Nov 2015 11:34:01 +0300 Subject: [PATCH 031/168] CPathfinder: deny transit over whirlpools when hero not protected --- lib/CPathfinder.cpp | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/lib/CPathfinder.cpp b/lib/CPathfinder.cpp index b4cd10d1f..2aa6ba39e 100644 --- a/lib/CPathfinder.cpp +++ b/lib/CPathfinder.cpp @@ -315,7 +315,13 @@ bool CPathfinder::isMovementAfterDestPossible() if(dp->coord == out.hpos) return true; // This one is tricky, we can ignore fact that tile is not ACCESSIBLE in case if it's our hero block it. Though this need investigation if(dp->accessible == CGPathNode::VISITABLE && CGTeleport::isTeleport(dt->topVisitableObj())) - return true; // For now we'll always allow transit for teleporters + { + /// For now we'll always allow transit over teleporters + /// Transit over whirlpools only allowed when hero protected + auto whirlpool = dynamic_cast(dt->topVisitableObj()); + if(!whirlpool || options.useTeleportWhirlpool) + return true; + } if(useEmbarkCost && options.useEmbarkAndDisembark) return true; break; From 9fe442537b39982eb8a01d9bf8196c8254e54369 Mon Sep 17 00:00:00 2001 From: ArseniyShestakov Date: Thu, 5 Nov 2015 12:46:44 +0300 Subject: [PATCH 032/168] Pass on EPathfindingLayer and small fixes for code around it --- lib/GameConstants.cpp | 20 +++++++------------- lib/GameConstants.h | 6 ++---- 2 files changed, 9 insertions(+), 17 deletions(-) diff --git a/lib/GameConstants.cpp b/lib/GameConstants.cpp index d21f3f40c..2b9556091 100644 --- a/lib/GameConstants.cpp +++ b/lib/GameConstants.cpp @@ -132,7 +132,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 terrainTypeToString = { @@ -149,9 +149,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 << ""; else return os << it->second; } @@ -163,9 +164,9 @@ std::string ETerrainType::toString() const return ss.str(); } -std::ostream & operator<<(std::ostream & os, const EPathfindingLayer actionType) +std::ostream & operator<<(std::ostream & os, const EPathfindingLayer pathfindingLayer) { - static const std::map pathfinderLayerToString = + static const std::map pathfinderLayerToString { #define DEFINE_ELEMENT(element) {EPathfindingLayer::element, #element} DEFINE_ELEMENT(WRONG), @@ -175,17 +176,10 @@ std::ostream & operator<<(std::ostream & os, const EPathfindingLayer actionType) DEFINE_ELEMENT(WATER), DEFINE_ELEMENT(AIR), DEFINE_ELEMENT(NUM_LAYERS) - + #undef DEFINE_ELEMENT }; - auto it = pathfinderLayerToString.find(actionType.num); + auto it = pathfinderLayerToString.find(pathfindingLayer.num); if (it == pathfinderLayerToString.end()) return os << ""; else return os << it->second; } - -std::string EPathfindingLayer::toString() const -{ - std::stringstream ss; - ss << *this; - return ss.str(); -} diff --git a/lib/GameConstants.h b/lib/GameConstants.h index e46c482d1..4cb33d0db 100644 --- a/lib/GameConstants.h +++ b/lib/GameConstants.h @@ -741,7 +741,7 @@ 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) @@ -759,11 +759,9 @@ public: ID_LIKE_CLASS_COMMON(EPathfindingLayer, EEPathfindingLayer) EEPathfindingLayer num; - - std::string toString() const; }; -DLL_LINKAGE std::ostream & operator<<(std::ostream & os, const EPathfindingLayer actionType); +DLL_LINKAGE std::ostream & operator<<(std::ostream & os, const EPathfindingLayer pathfindingLayer); ID_LIKE_OPERATORS_DECLS(EPathfindingLayer, EPathfindingLayer::EEPathfindingLayer) From f80c1410b31d758adc2b3ec3ada3e27133eb41a4 Mon Sep 17 00:00:00 2001 From: Andreas Cadhalpun Date: Thu, 5 Nov 2015 10:53:44 +0100 Subject: [PATCH 033/168] Replace deprecated FFmpeg API for compatibility with ffmpeg 2.9 --- client/CVideoHandler.cpp | 31 ++++++++++++++++++++++++++++++- 1 file changed, 30 insertions(+), 1 deletion(-) diff --git a/client/CVideoHandler.cpp b/client/CVideoHandler.cpp index c33472517..1218fc0d2 100644 --- a/client/CVideoHandler.cpp +++ b/client/CVideoHandler.cpp @@ -151,7 +151,11 @@ bool CVideoPlayer::open(std::string fname, bool loop, bool useOverlay, bool scal } // Allocate video frame +#if LIBAVUTIL_VERSION_MAJOR > 52 + frame = av_alloc_frame(); +#else frame = avcodec_alloc_frame(); +#endif //setup scaling @@ -185,21 +189,36 @@ bool CVideoPlayer::open(std::string fname, bool loop, bool useOverlay, bool scal if (texture) { // Convert the image into YUV format that SDL uses sws = sws_getContext(codecContext->width, codecContext->height, codecContext->pix_fmt, - pos.w, pos.h, PIX_FMT_YUV420P, + pos.w, pos.h, +#if LIBAVUTIL_VERSION_MAJOR > 51 + AV_PIX_FMT_YUV420P, +#else + PIX_FMT_YUV420P, +#endif SWS_BICUBIC, nullptr, nullptr, nullptr); } else { +#if LIBAVUTIL_VERSION_MAJOR > 51 + AVPixelFormat screenFormat = AV_PIX_FMT_NONE; +#else PixelFormat screenFormat = PIX_FMT_NONE; +#endif if (screen->format->Bshift > screen->format->Rshift) { // this a BGR surface switch (screen->format->BytesPerPixel) { +#if LIBAVUTIL_VERSION_MAJOR > 51 + case 2: screenFormat = AV_PIX_FMT_BGR565; break; + case 3: screenFormat = AV_PIX_FMT_BGR24; break; + case 4: screenFormat = AV_PIX_FMT_BGR32; break; +#else case 2: screenFormat = PIX_FMT_BGR565; break; case 3: screenFormat = PIX_FMT_BGR24; break; case 4: screenFormat = PIX_FMT_BGR32; break; +#endif default: return false; } } @@ -208,9 +227,15 @@ bool CVideoPlayer::open(std::string fname, bool loop, bool useOverlay, bool scal // this a RGB surface switch (screen->format->BytesPerPixel) { +#if LIBAVUTIL_VERSION_MAJOR > 51 + case 2: screenFormat = AV_PIX_FMT_RGB565; break; + case 3: screenFormat = AV_PIX_FMT_RGB24; break; + case 4: screenFormat = AV_PIX_FMT_RGB32; break; +#else case 2: screenFormat = PIX_FMT_RGB565; break; case 3: screenFormat = PIX_FMT_RGB24; break; case 4: screenFormat = PIX_FMT_RGB32; break; +#endif default: return false; } } @@ -367,7 +392,11 @@ void CVideoPlayer::close() if (frame) { +#if LIBAVUTIL_VERSION_MAJOR > 52 + av_frame_free(frame); +#else av_free(frame); +#endif frame = nullptr; } From 148355908d968e4e87375a3b727fc8c3a03d8451 Mon Sep 17 00:00:00 2001 From: ArseniyShestakov Date: Thu, 5 Nov 2015 15:04:56 +0300 Subject: [PATCH 034/168] CPathfinder: get rid of FoW variable and bunch of small fixes --- lib/CPathfinder.cpp | 34 ++++++++++++++++------------------ lib/CPathfinder.h | 1 - 2 files changed, 16 insertions(+), 19 deletions(-) diff --git a/lib/CPathfinder.cpp b/lib/CPathfinder.cpp index 2aa6ba39e..70be1c2fc 100644 --- a/lib/CPathfinder.cpp +++ b/lib/CPathfinder.cpp @@ -31,7 +31,8 @@ CPathfinder::PathfinderOptions::PathfinderOptions() lightweightFlyingMode = false; } -CPathfinder::CPathfinder(CPathsInfo &_out, CGameState *_gs, const CGHeroInstance *_hero) : CGameInfoCallback(_gs, boost::optional()), out(_out), hero(_hero), FoW(getPlayerTeam(hero->tempOwner)->fogOfWarMap) +CPathfinder::CPathfinder(CPathsInfo &_out, CGameState *_gs, const CGHeroInstance *_hero) + : CGameInfoCallback(_gs, boost::optional()), out(_out), hero(_hero) { assert(hero); assert(hero == getHero(hero->id)); @@ -268,7 +269,7 @@ bool CPathfinder::isLayerTransitionPossible() bool CPathfinder::isMovementToDestPossible() { - switch (dp->layer) + switch(dp->layer) { case EPathfindingLayer::LAND: if(!canMoveBetween(cp->coord, dp->coord) || dp->accessible == CGPathNode::BLOCKED) @@ -306,7 +307,7 @@ bool CPathfinder::isMovementToDestPossible() bool CPathfinder::isMovementAfterDestPossible() { - switch (dp->layer) + switch(dp->layer) { case EPathfindingLayer::LAND: case EPathfindingLayer::SAIL: @@ -382,7 +383,7 @@ bool CPathfinder::isDestinationGuardian() void CPathfinder::initializeGraph() { - auto initializeNode = [&](int3 pos, EPathfindingLayer layer, const TerrainTile *tinfo) + auto updateNode = [&](int3 pos, EPathfindingLayer layer, const TerrainTile *tinfo) { auto node = out.getNode(pos, layer); node->accessible = evaluateAccessibility(pos, tinfo); @@ -402,23 +403,21 @@ void CPathfinder::initializeGraph() for(pos.z=0; pos.z < out.sizes.z; ++pos.z) { const TerrainTile *tinfo = &gs->map->getTile(pos); - switch (tinfo->terType) + switch(tinfo->terType) { - case ETerrainType::WRONG: - case ETerrainType::BORDER: case ETerrainType::ROCK: break; case ETerrainType::WATER: - initializeNode(pos, EPathfindingLayer::SAIL, tinfo); + updateNode(pos, EPathfindingLayer::SAIL, tinfo); if(options.useFlying) - initializeNode(pos, EPathfindingLayer::AIR, tinfo); + updateNode(pos, EPathfindingLayer::AIR, tinfo); if(options.useWaterWalking) - initializeNode(pos, EPathfindingLayer::WATER, tinfo); + updateNode(pos, EPathfindingLayer::WATER, tinfo); break; default: - initializeNode(pos, EPathfindingLayer::LAND, tinfo); + updateNode(pos, EPathfindingLayer::LAND, tinfo); if(options.useFlying) - initializeNode(pos, EPathfindingLayer::AIR, tinfo); + updateNode(pos, EPathfindingLayer::AIR, tinfo); break; } } @@ -430,8 +429,7 @@ CGPathNode::EAccessibility CPathfinder::evaluateAccessibility(const int3 &pos, c { CGPathNode::EAccessibility ret = (tinfo->blocked ? CGPathNode::BLOCKED : CGPathNode::ACCESSIBLE); - - if(tinfo->terType == ETerrainType::ROCK || !FoW[pos.x][pos.y][pos.z]) + if(tinfo->terType == ETerrainType::ROCK || !isVisible(pos, hero->tempOwner)) return CGPathNode::BLOCKED; if(tinfo->visitable) @@ -513,7 +511,7 @@ bool CPathfinder::canVisitObject() const } CGPathNode::CGPathNode() -:coord(-1,-1,-1) + : coord(-1,-1,-1) { accessible = NOT_SET; land = 0; @@ -538,7 +536,7 @@ int3 CGPath::endPos() const return nodes[0].coord; } -void CGPath::convert( ui8 mode ) +void CGPath::convert(ui8 mode) { if(mode==0) { @@ -549,8 +547,8 @@ void CGPath::convert( ui8 mode ) } } -CPathsInfo::CPathsInfo( const int3 &Sizes ) -:sizes(Sizes) +CPathsInfo::CPathsInfo(const int3 &Sizes) + : sizes(Sizes) { hero = nullptr; nodes = new CGPathNode***[sizes.x]; diff --git a/lib/CPathfinder.h b/lib/CPathfinder.h index 7d591cdbb..d6bf2483c 100644 --- a/lib/CPathfinder.h +++ b/lib/CPathfinder.h @@ -95,7 +95,6 @@ private: CPathsInfo &out; const CGHeroInstance *hero; - const std::vector > > &FoW; std::list mq; //BFS queue -> nodes to be checked From bd12989ad69be16ebcbb8e995b1f1f5c194fcdae Mon Sep 17 00:00:00 2001 From: ArseniyShestakov Date: Fri, 6 Nov 2015 21:54:51 +0300 Subject: [PATCH 035/168] Artifacts: use ArtifactID instead of ints --- lib/CArtHandler.cpp | 4 ++-- server/CGameHandler.cpp | 36 ++++++++++++++++++------------------ 2 files changed, 20 insertions(+), 20 deletions(-) diff --git a/lib/CArtHandler.cpp b/lib/CArtHandler.cpp index 84abf0173..ac4a310a9 100644 --- a/lib/CArtHandler.cpp +++ b/lib/CArtHandler.cpp @@ -738,8 +738,8 @@ std::string CArtifactInstance::nodeName() const CArtifactInstance * CArtifactInstance::createScroll( const CSpell *s) { - auto ret = new CArtifactInstance(VLC->arth->artifacts[1]); - auto b = new Bonus(Bonus::PERMANENT, Bonus::SPELL, Bonus::ARTIFACT_INSTANCE, -1, 1, s->id); + auto ret = new CArtifactInstance(VLC->arth->artifacts[ArtifactID::SPELL_SCROLL]); + auto b = new Bonus(Bonus::PERMANENT, Bonus::SPELL, Bonus::ARTIFACT_INSTANCE, -1, ArtifactID::SPELL_SCROLL, s->id); ret->addNewBonus(b); return ret; } diff --git a/server/CGameHandler.cpp b/server/CGameHandler.cpp index f747a4bc5..ba81d0f77 100644 --- a/server/CGameHandler.cpp +++ b/server/CGameHandler.cpp @@ -2772,14 +2772,14 @@ bool CGameHandler::recruitCreatures(ObjectInstanceID objid, ObjectInstanceID dst switch(crid) { - case 146: - giveHeroNewArtifact(h, VLC->arth->artifacts[4], ArtifactPosition::MACH1); + case CreatureID::BALLISTA: + giveHeroNewArtifact(h, VLC->arth->artifacts[ArtifactID::BALLISTA], ArtifactPosition::MACH1); break; - case 147: - giveHeroNewArtifact(h, VLC->arth->artifacts[6], ArtifactPosition::MACH3); + case CreatureID::FIRST_AID_TENT: + giveHeroNewArtifact(h, VLC->arth->artifacts[ArtifactID::FIRST_AID_TENT], ArtifactPosition::MACH3); break; - case 148: - giveHeroNewArtifact(h, VLC->arth->artifacts[5], ArtifactPosition::MACH2); + case CreatureID::AMMO_CART: + giveHeroNewArtifact(h, VLC->arth->artifacts[ArtifactID::AMMO_CART], ArtifactPosition::MACH2); break; default: complain("This war machine cannot be recruited!"); @@ -2998,7 +2998,7 @@ bool CGameHandler::assembleArtifacts (ObjectInstanceID heroID, ArtifactPosition if(assemble) { - CArtifact *combinedArt = VLC->arth->artifacts.at(assembleTo); + CArtifact *combinedArt = VLC->arth->artifacts[assembleTo]; if(!combinedArt->constituents) COMPLAIN_RET("assembleArtifacts: Artifact being attempted to assemble is not a combined artifacts!"); if(!vstd::contains(destArtifact->assemblyPossibilities(hero), combinedArt)) @@ -3042,7 +3042,7 @@ bool CGameHandler::buyArtifact( ObjectInstanceID hid, ArtifactID aid ) } else if(aid < 7 && aid > 3) //war machine { - int price = VLC->arth->artifacts.at(aid)->price; + int price = VLC->arth->artifacts[aid]->price; if(( hero->getArt(ArtifactPosition(9+aid)) && complain("Hero already has this machine!")) || (gs->getPlayer(hero->getOwner())->resources.at(Res::GOLD) < price && complain("Not enough gold!"))) @@ -3053,7 +3053,7 @@ bool CGameHandler::buyArtifact( ObjectInstanceID hid, ArtifactID aid ) || ((town->hasBuilt(BuildingID::BALLISTA_YARD, ETownType::STRONGHOLD)) && aid == ArtifactID::BALLISTA)) { giveResource(hero->getOwner(),Res::GOLD,-price); - giveHeroNewArtifact(hero, VLC->arth->artifacts.at(aid), ArtifactPosition(9+aid)); + giveHeroNewArtifact(hero, VLC->arth->artifacts[aid], ArtifactPosition(9+aid)); return true; } else @@ -3110,7 +3110,7 @@ bool CGameHandler::buyArtifact(const IMarket *m, const CGHeroInstance *h, Res::E sendAndApply(&saa); - giveHeroNewArtifact(h, VLC->arth->artifacts.at(aid), ArtifactPosition::FIRST_AVAILABLE); + giveHeroNewArtifact(h, VLC->arth->artifacts[aid], ArtifactPosition::FIRST_AVAILABLE); return true; } @@ -3962,7 +3962,7 @@ void CGameHandler::playerMessage( PlayerColor player, const std::string &message sm.absolute = true; if(!h->hasSpellbook()) //hero doesn't have spellbook - giveHeroNewArtifact(h, VLC->arth->artifacts.at(0), ArtifactPosition::SPELLBOOK); //give spellbook + giveHeroNewArtifact(h, VLC->arth->artifacts[ArtifactID::SPELLBOOK], ArtifactPosition::SPELLBOOK); //give spellbook sendAndApply(&sm); } @@ -4015,18 +4015,18 @@ void CGameHandler::playerMessage( PlayerColor player, const std::string &message if(!hero) return; if(!hero->getArt(ArtifactPosition::MACH1)) - giveHeroNewArtifact(hero, VLC->arth->artifacts.at(4), ArtifactPosition::MACH1); + giveHeroNewArtifact(hero, VLC->arth->artifacts[ArtifactID::BALLISTA], ArtifactPosition::MACH1); if(!hero->getArt(ArtifactPosition::MACH2)) - giveHeroNewArtifact(hero, VLC->arth->artifacts.at(5), ArtifactPosition::MACH2); + giveHeroNewArtifact(hero, VLC->arth->artifacts[ArtifactID::AMMO_CART], ArtifactPosition::MACH2); if(!hero->getArt(ArtifactPosition::MACH3)) - giveHeroNewArtifact(hero, VLC->arth->artifacts.at(6), ArtifactPosition::MACH3); + giveHeroNewArtifact(hero, VLC->arth->artifacts[ArtifactID::FIRST_AID_TENT], ArtifactPosition::MACH3); } else if (message == "vcmiforgeofnoldorking") //hero gets all artifacts except war machines, spell scrolls and spell book { CGHeroInstance *hero = gs->getHero(currObj); if(!hero) return; for (int g = 7; g < VLC->arth->artifacts.size(); ++g) //including artifacts from mods - giveHeroNewArtifact(hero, VLC->arth->artifacts.at(g), ArtifactPosition::PRE_FIRST); + giveHeroNewArtifact(hero, VLC->arth->artifacts[g], ArtifactPosition::PRE_FIRST); } else if(message == "vcmiglorfindel") //selected hero gains a new level { @@ -4900,14 +4900,14 @@ bool CGameHandler::dig( const CGHeroInstance *h ) if(gs->map->grailPos == h->getPosition()) { iw.text.addTxt(MetaString::GENERAL_TXT, 58); //"Congratulations! After spending many hours digging here, your hero has uncovered the " - iw.text.addTxt(MetaString::ART_NAMES, 2); + iw.text.addTxt(MetaString::ART_NAMES, ArtifactID::GRAIL); iw.soundID = soundBase::ULTIMATEARTIFACT; - giveHeroNewArtifact(h, VLC->arth->artifacts.at(2), ArtifactPosition::PRE_FIRST); //give grail + giveHeroNewArtifact(h, VLC->arth->artifacts[ArtifactID::GRAIL], ArtifactPosition::PRE_FIRST); //give grail sendAndApply(&iw); iw.soundID = soundBase::invalid; iw.text.clear(); - iw.text.addTxt(MetaString::ART_DESCR, 2); + iw.text.addTxt(MetaString::ART_DESCR, ArtifactID::GRAIL); sendAndApply(&iw); } else From e645b46aed8f58042d3ee9b812d68559b471a1c8 Mon Sep 17 00:00:00 2001 From: AlexVinS Date: Sat, 7 Nov 2015 11:35:02 +0300 Subject: [PATCH 036/168] Revert "Merge pull request #124 from vcmi/issue/1372" This reverts commit da01af319b8e803bc737587fdf88b83bf412f2d1, reversing changes made to 8b6b4e2e0b81169444a1879b3ac505ca346925f3. --- client/battle/CBattleInterface.cpp | 15 +--- lib/GameConstants.cpp | 1 - lib/GameConstants.h | 1 - lib/NetPacksLib.cpp | 2 +- server/CGameHandler.cpp | 108 +++++++---------------------- server/CGameHandler.h | 9 +-- 6 files changed, 31 insertions(+), 105 deletions(-) diff --git a/client/battle/CBattleInterface.cpp b/client/battle/CBattleInterface.cpp index c1ea5ec45..452b41d76 100644 --- a/client/battle/CBattleInterface.cpp +++ b/client/battle/CBattleInterface.cpp @@ -2163,15 +2163,7 @@ void CBattleInterface::handleHex(BattleHex myNumber, int eventType) break; case RISE_DEMONS: if (shere && ourStack && !shere->alive()) - { - if(!(shere->hasBonusOfType(Bonus::UNDEAD) - || shere->hasBonusOfType(Bonus::NON_LIVING) - || vstd::contains(shere->state, EBattleStackState::SUMMONED) - || vstd::contains(shere->state, EBattleStackState::CLONED) - || shere->hasBonusOfType(Bonus::SIEGE_WEAPON) - )) - legalAction = true; - } + legalAction = true; break; } if (legalAction) @@ -2328,10 +2320,7 @@ void CBattleInterface::handleHex(BattleHex myNumber, int eventType) break; case RISE_DEMONS: cursorType = ECursor::SPELLBOOK; - realizeAction = [=] - { - giveCommand(Battle::DAEMON_SUMMONING, myNumber, activeStack->ID); - }; + realizeAction = [=]{ giveCommand(Battle::DAEMON_SUMMONING, myNumber, activeStack->ID); }; break; case CATAPULT: cursorFrame = ECursor::COMBAT_SHOOT_CATAPULT; diff --git a/lib/GameConstants.cpp b/lib/GameConstants.cpp index 3b5acc577..9a48b476f 100644 --- a/lib/GameConstants.cpp +++ b/lib/GameConstants.cpp @@ -19,7 +19,6 @@ #include "spells/CSpellHandler.h" const SlotID SlotID::COMMANDER_SLOT_PLACEHOLDER = SlotID(-2); -const SlotID SlotID::SUMMONED_SLOT_PLACEHOLDER = SlotID(255); const PlayerColor PlayerColor::CANNOT_DETERMINE = PlayerColor(253); const PlayerColor PlayerColor::UNFLAGGABLE = PlayerColor(254); const PlayerColor PlayerColor::NEUTRAL = PlayerColor(255); diff --git a/lib/GameConstants.h b/lib/GameConstants.h index 98e344d6b..05d3128eb 100644 --- a/lib/GameConstants.h +++ b/lib/GameConstants.h @@ -216,7 +216,6 @@ class SlotID : public BaseForID friend class CNonConstInfoCallback; DLL_LINKAGE static const SlotID COMMANDER_SLOT_PLACEHOLDER; - DLL_LINKAGE static const SlotID SUMMONED_SLOT_PLACEHOLDER; ///curB->generateNewStack(csbd, attacker, SlotID::SUMMONED_SLOT_PLACEHOLDER, pos); //TODO: netpacks? + CStack * addedStack = gs->curB->generateNewStack(csbd, attacker, SlotID(255), pos); //TODO: netpacks? if (summoned) addedStack->state.insert(EBattleStackState::SUMMONED); diff --git a/server/CGameHandler.cpp b/server/CGameHandler.cpp index ba81d0f77..88f43533e 100644 --- a/server/CGameHandler.cpp +++ b/server/CGameHandler.cpp @@ -651,8 +651,8 @@ void CGameHandler::endBattle(int3 tile, const CGHeroInstance *hero1, const CGHer sendAndApply(&cs); } - cab1.updateArmy(this); - cab2.updateArmy(this); //take casualties after battle is deleted + cab1.takeFromArmy(this); + cab2.takeFromArmy(this); //take casualties after battle is deleted //if one hero has lost we will erase him if(battleResult.data->winner!=0 && hero1) @@ -3848,16 +3848,18 @@ bool CGameHandler::makeBattleAction( BattleAction &ba ) //TODO: From Strategija: //Summon Demon is a level 2 spell. { + StartAction start_action(ba); + sendAndApply(&start_action); + const CStack *summoner = gs->curB->battleGetStackByID(ba.stackNumber), *destStack = gs->curB->battleGetStackByPos(ba.destinationTile, false); - CreatureID summonedType(summoner->getBonusLocalFirst(Selector::type(Bonus::DAEMON_SUMMONING))->subtype);//in case summoner can summon more than one type of monsters... scream! BattleStackAdded bsa; bsa.attacker = summoner->attackerOwned; - bsa.creID = summonedType; + bsa.creID = CreatureID(summoner->getBonusLocalFirst(Selector::type(Bonus::DAEMON_SUMMONING))->subtype); //in case summoner can summon more than one type of monsters... scream! ui64 risedHp = summoner->count * summoner->valOfBonuses(Bonus::DAEMON_SUMMONING, bsa.creID.toEnum()); - ui64 targetHealth = destStack->getCreature()->MaxHealth() * destStack->baseAmount;//todo: ignore AGE effect + ui64 targetHealth = destStack->getCreature()->MaxHealth() * destStack->baseAmount; ui64 canRiseHp = std::min(targetHealth, risedHp); ui32 canRiseAmount = canRiseHp / VLC->creh->creatures.at(bsa.creID)->MaxHealth(); @@ -3869,9 +3871,6 @@ bool CGameHandler::makeBattleAction( BattleAction &ba ) if (bsa.amount) //there's rare possibility single creature cannot rise desired type { - StartAction start_action(ba); - sendAndApply(&start_action); - BattleStacksRemoved bsr; //remove body bsr.stackIDs.insert(destStack->ID); sendAndApply(&bsr); @@ -3883,9 +3882,9 @@ bool CGameHandler::makeBattleAction( BattleAction &ba ) ssp.val = -1; ssp.absolute = false; sendAndApply(&ssp); - - sendAndApply(&end_action); } + + sendAndApply(&end_action); break; } case Battle::MONSTER_SPELL: @@ -5805,52 +5804,13 @@ void CGameHandler::duelFinished() return; } -CasualtiesAfterBattle::CasualtiesAfterBattle(const CArmedInstance * _army, BattleInfo *bat): - army(_army) +CasualtiesAfterBattle::CasualtiesAfterBattle(const CArmedInstance *army, BattleInfo *bat) { heroWithDeadCommander = ObjectInstanceID(); PlayerColor color = army->tempOwner; if(color == PlayerColor::UNFLAGGABLE) color = PlayerColor::NEUTRAL; - - auto killStack = [&, this](const SlotID slot, const CStackInstance * instance) - { - StackLocation sl(army, slot); - newStackCounts.push_back(TStackAndItsNewCount(sl, 0)); - if(nullptr == instance) - return; - auto c = dynamic_cast (instance); - if (c) //switch commander status to dead - { - auto h = dynamic_cast (army); - if (h && h->commander == c) - heroWithDeadCommander = army->id; //TODO: unify commander handling - } - }; - - //1. Find removed stacks. - for(const auto & slotInfo : army->stacks) - { - const SlotID slot = slotInfo.first; - const CStackInstance * instance = slotInfo.second; - - if(nullptr != instance)//just in case - { - bool found = false; - for(const CStack * sta : bat->stacks) - { - if(sta->base == instance) - { - found = true; - break; - } - } - //stack in this slot was removed == it is dead - if(!found) - killStack(slot, instance); - } - } for(CStack *st : bat->stacks) { @@ -5862,7 +5822,7 @@ CasualtiesAfterBattle::CasualtiesAfterBattle(const CArmedInstance * _army, Battl //FIXME: this info is also used in BattleInfo::calculateCasualties, refactor st->count = std::max (0, st->count - st->resurrected); - if (!st->count && !st->base) //we can imagine stacks of war machines that are not spawned by artifacts? + if (!st->count && !st->base) //we can imagine stacks of war mahcines that are not spawned by artifacts? { auto warMachine = VLC->arth->creatureToMachineID(st->type->idNumber); if (warMachine != ArtifactID::NONE) @@ -5872,32 +5832,29 @@ CasualtiesAfterBattle::CasualtiesAfterBattle(const CArmedInstance * _army, Battl removedWarMachines.push_back (ArtifactLocation(hero, hero->getArtPos(warMachine, true))); } } - - if(army->slotEmpty(st->slot)) + + if(!army->slotEmpty(st->slot) && st->count < army->getStackCount(st->slot)) { - if(st->slot == SlotID::SUMMONED_SLOT_PLACEHOLDER && !vstd::contains(st->state, EBattleStackState::SUMMONED) && st->alive() && st->count > 0) - { - //this stack was permanently summoned - const CreatureID summonedType = st->type->idNumber; - summoned[summonedType] += st->count; - } + StackLocation sl(army, st->slot); + if(st->alive()) + newStackCounts.push_back(std::pair(sl, st->count)); + else + newStackCounts.push_back(std::pair(sl, 0)); } - else + if (st->base && !st->count) { - if(st->count == 0 || !st->alive()) + auto c = dynamic_cast (st->base); + if (c) //switch commander status to dead { - killStack(st->slot, st->base); - } - else if(st->count < army->getStackCount(st->slot)) - { - StackLocation sl(army, st->slot); - newStackCounts.push_back(TStackAndItsNewCount(sl, st->count)); + auto h = dynamic_cast (army); + if (h && h->commander == c) + heroWithDeadCommander = army->id; //TODO: unify commander handling } } } } -void CasualtiesAfterBattle::updateArmy(CGameHandler *gh) +void CasualtiesAfterBattle::takeFromArmy(CGameHandler *gh) { for(TStackAndItsNewCount &ncount : newStackCounts) { @@ -5906,21 +5863,6 @@ void CasualtiesAfterBattle::updateArmy(CGameHandler *gh) else gh->eraseStack(ncount.first, true); } - for(auto summoned_iter : summoned) - { - SlotID slot = army->getSlotFor(summoned_iter.first); - if(slot.validSlot()) - { - StackLocation location(army, slot); - gh->addToSlot(location, summoned_iter.first.toCreature(), summoned_iter.second); - } - else - { - //even if it will be possible to summon anything permanently it should be checked for free slot - //necromancy is handled separately - gh->complain("No free slot to put summoned creature"); - } - } for (auto al : removedWarMachines) { gh->removeArtifact(al); diff --git a/server/CGameHandler.h b/server/CGameHandler.h index bc2a336a7..acf1698a2 100644 --- a/server/CGameHandler.h +++ b/server/CGameHandler.h @@ -71,16 +71,13 @@ public: struct CasualtiesAfterBattle { typedef std::pair TStackAndItsNewCount; - typedef std::map TSummoned; enum {ERASE = -1}; - const CArmedInstance * army; std::vector newStackCounts; std::vector removedWarMachines; - TSummoned summoned; - ObjectInstanceID heroWithDeadCommander; //TODO: unify stack locations + ObjectInstanceID heroWithDeadCommander; //TODO: unify stack loactions - CasualtiesAfterBattle(const CArmedInstance * _army, BattleInfo *bat); - void updateArmy(CGameHandler *gh); + CasualtiesAfterBattle(const CArmedInstance *army, BattleInfo *bat); + void takeFromArmy(CGameHandler *gh); }; class CGameHandler : public IGameCallback, CBattleInfoCallback From 7eebcb9d60922ad2633cd4a4866abb57877292ec Mon Sep 17 00:00:00 2001 From: ArseniyShestakov Date: Sat, 7 Nov 2015 11:46:58 +0300 Subject: [PATCH 037/168] Artifacts: replace few more ints by ArtifactID --- lib/CGameState.cpp | 2 +- lib/GameConstants.h | 1 + lib/mapObjects/CGHeroInstance.cpp | 4 ++-- lib/mapping/MapFormatH3M.cpp | 3 +-- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/lib/CGameState.cpp b/lib/CGameState.cpp index 3b4217ac5..57bb16597 100644 --- a/lib/CGameState.cpp +++ b/lib/CGameState.cpp @@ -907,7 +907,7 @@ void CGameState::initDuel() if(!ss.spells.empty()) { - h->putArtifact(ArtifactPosition::SPELLBOOK, CArtifactInstance::createNewArtifactInstance(0)); + h->putArtifact(ArtifactPosition::SPELLBOOK, CArtifactInstance::createNewArtifactInstance(ArtifactID::SPELLBOOK)); boost::copy(ss.spells, std::inserter(h->spells, h->spells.begin())); } diff --git a/lib/GameConstants.h b/lib/GameConstants.h index 05d3128eb..dfbc789e8 100644 --- a/lib/GameConstants.h +++ b/lib/GameConstants.h @@ -824,6 +824,7 @@ public: FIRST_AID_TENT = 6, //CENTAUR_AXE = 7, //BLACKSHARD_OF_THE_DEAD_KNIGHT = 8, + ARMAGEDDONS_BLADE = 128, TITANS_THUNDER = 135, //CORNUCOPIA = 140, //FIXME: the following is only true if WoG is enabled. Otherwise other mod artifacts will take these slots. diff --git a/lib/mapObjects/CGHeroInstance.cpp b/lib/mapObjects/CGHeroInstance.cpp index 3042e35df..c50055bcc 100644 --- a/lib/mapObjects/CGHeroInstance.cpp +++ b/lib/mapObjects/CGHeroInstance.cpp @@ -261,10 +261,10 @@ void CGHeroInstance::initHero() spells -= SpellID::PRESET; if(!getArt(ArtifactPosition::MACH4) && !getArt(ArtifactPosition::SPELLBOOK) && type->haveSpellBook) //no catapult means we haven't read pre-existent set -> use default rules for spellbook - putArtifact(ArtifactPosition::SPELLBOOK, CArtifactInstance::createNewArtifactInstance(0)); + putArtifact(ArtifactPosition::SPELLBOOK, CArtifactInstance::createNewArtifactInstance(ArtifactID::SPELLBOOK)); if(!getArt(ArtifactPosition::MACH4)) - putArtifact(ArtifactPosition::MACH4, CArtifactInstance::createNewArtifactInstance(3)); //everyone has a catapult + putArtifact(ArtifactPosition::MACH4, CArtifactInstance::createNewArtifactInstance(ArtifactID::CATAPULT)); //everyone has a catapult if(portrait < 0 || portrait == 255) portrait = type->imageIndex; diff --git a/lib/mapping/MapFormatH3M.cpp b/lib/mapping/MapFormatH3M.cpp index eba380123..1831e373a 100644 --- a/lib/mapping/MapFormatH3M.cpp +++ b/lib/mapping/MapFormatH3M.cpp @@ -668,8 +668,7 @@ void CMapLoaderH3M::readAllowedArtifacts() } if (map->version == EMapFormat::ROE) { - // Armageddon's Blade - map->allowedArtifact[128] = false; + map->allowedArtifact[ArtifactID::ARMAGEDDONS_BLADE] = false; } } From 3f2cdf31379ca12be7630e4b0c0477574fe16972 Mon Sep 17 00:00:00 2001 From: ArseniyShestakov Date: Sat, 7 Nov 2015 21:11:07 +0300 Subject: [PATCH 038/168] CPathfinder: implement priority queue and node locking --- lib/CPathfinder.cpp | 21 +++++++++++++++------ lib/CPathfinder.h | 17 ++++++++++++++++- 2 files changed, 31 insertions(+), 7 deletions(-) diff --git a/lib/CPathfinder.cpp b/lib/CPathfinder.cpp index 70be1c2fc..74dd2dfa6 100644 --- a/lib/CPathfinder.cpp +++ b/lib/CPathfinder.cpp @@ -84,12 +84,13 @@ void CPathfinder::calculatePaths() CGPathNode *initialNode = out.getNode(out.hpos, hero->boat ? EPathfindingLayer::SAIL : EPathfindingLayer::LAND); initialNode->turns = 0; initialNode->moveRemains = hero->movement; - mq.push_back(initialNode); + pq.push(initialNode); - while(!mq.empty()) + while(!pq.empty()) { - cp = mq.front(); - mq.pop_front(); + cp = pq.top(); + pq.pop(); + cp->locked = true; int movement = cp->moveRemains, turn = cp->turns; if(!movement) @@ -110,6 +111,9 @@ void CPathfinder::calculatePaths() if(dp->accessible == CGPathNode::NOT_SET) continue; + if(dp->locked) + continue; + if(cp->layer != i && !isLayerTransitionPossible()) continue; @@ -142,7 +146,7 @@ void CPathfinder::calculatePaths() dp->theNodeBefore = cp; if(isMovementAfterDestPossible()) - mq.push_back(dp); + pq.push(dp); } } } //neighbours loop @@ -154,12 +158,15 @@ void CPathfinder::calculatePaths() for(auto & neighbour : neighbours) { dp = out.getNode(neighbour, cp->layer); + if(dp->locked) + continue; + if(isBetterWay(movement, turn)) { dp->moveRemains = movement; dp->turns = turn; dp->theNodeBefore = cp; - mq.push_back(dp); + pq.push(dp); } } } @@ -386,6 +393,7 @@ void CPathfinder::initializeGraph() auto updateNode = [&](int3 pos, EPathfindingLayer layer, const TerrainTile *tinfo) { auto node = out.getNode(pos, layer); + node->locked = false; node->accessible = evaluateAccessibility(pos, tinfo); node->turns = 0xff; node->moveRemains = 0; @@ -513,6 +521,7 @@ bool CPathfinder::canVisitObject() const CGPathNode::CGPathNode() : coord(-1,-1,-1) { + locked = false; accessible = NOT_SET; land = 0; moveRemains = 0; diff --git a/lib/CPathfinder.h b/lib/CPathfinder.h index d6bf2483c..96e6c72ac 100644 --- a/lib/CPathfinder.h +++ b/lib/CPathfinder.h @@ -5,6 +5,8 @@ #include "IGameCallback.h" #include "int3.h" +#include + /* * CPathfinder.h, part of VCMI engine * @@ -30,6 +32,7 @@ struct DLL_LINKAGE CGPathNode BLOCKED //tile can't be entered nor visited }; + bool locked; EAccessibility accessible; ui8 land; ui8 turns; //how many turns we have to wait before reachng the tile - 0 means current turn @@ -96,7 +99,19 @@ private: CPathsInfo &out; const CGHeroInstance *hero; - std::list 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 > pq; std::vector neighbours; From d8a612f5d64a8727c63959cbca9fe4a71f7a3f6c Mon Sep 17 00:00:00 2001 From: ArseniyShestakov Date: Sat, 7 Nov 2015 22:00:31 +0300 Subject: [PATCH 039/168] CPathsInfo: use boost::multi_array for storing graph of nodes --- lib/CPathfinder.cpp | 39 +++++++++------------------------------ lib/CPathfinder.h | 4 ++-- 2 files changed, 11 insertions(+), 32 deletions(-) diff --git a/lib/CPathfinder.cpp b/lib/CPathfinder.cpp index 74dd2dfa6..6ace8f104 100644 --- a/lib/CPathfinder.cpp +++ b/lib/CPathfinder.cpp @@ -518,8 +518,8 @@ bool CPathfinder::canVisitObject() const return cp->layer == EPathfindingLayer::LAND || cp->layer == EPathfindingLayer::SAIL; } -CGPathNode::CGPathNode() - : coord(-1,-1,-1) +CGPathNode::CGPathNode(int3 Coord, EPathfindingLayer Layer) + : coord(Coord), layer(Layer) { locked = false; accessible = NOT_SET; @@ -527,7 +527,6 @@ CGPathNode::CGPathNode() moveRemains = 0; turns = 255; theNodeBefore = nullptr; - layer = EPathfindingLayer::WRONG; } bool CGPathNode::reachable() const @@ -560,36 +559,16 @@ CPathsInfo::CPathsInfo(const int3 &Sizes) : sizes(Sizes) { hero = nullptr; - nodes = new CGPathNode***[sizes.x]; + nodes.resize(boost::extents[sizes.x][sizes.y][sizes.z][EPathfindingLayer::NUM_LAYERS]); for(int i = 0; i < sizes.x; i++) - { - nodes[i] = new CGPathNode**[sizes.y]; for(int j = 0; j < sizes.y; j++) - { - nodes[i][j] = new CGPathNode*[sizes.z]; - for (int z = 0; z < sizes.z; z++) - { - nodes[i][j][z] = new CGPathNode[EPathfindingLayer::NUM_LAYERS]; - } - } - } + for(int z = 0; z < sizes.z; z++) + for(int l = 0; l < EPathfindingLayer::NUM_LAYERS; l++) + nodes[i][j][z][l] = new CGPathNode(int3(i, j, z), static_cast(l)); } CPathsInfo::~CPathsInfo() { - for(int i = 0; i < sizes.x; i++) - { - for(int j = 0; j < sizes.y; j++) - { - for (int z = 0; z < sizes.z; z++) - { - delete [] nodes[i][j][z]; - } - delete [] nodes[i][j]; - } - delete [] nodes[i]; - } - delete [] nodes; } const CGPathNode * CPathsInfo::getPathInfo(const int3 &tile, const EPathfindingLayer &layer) const @@ -633,11 +612,11 @@ int CPathsInfo::getDistance(const int3 &tile, const EPathfindingLayer &layer) co CGPathNode *CPathsInfo::getNode(const int3 &coord, const EPathfindingLayer &layer) const { if(layer != EPathfindingLayer::AUTO) - return &nodes[coord.x][coord.y][coord.z][layer]; + return nodes[coord.x][coord.y][coord.z][layer]; - auto landNode = &nodes[coord.x][coord.y][coord.z][EPathfindingLayer::LAND]; + auto landNode = nodes[coord.x][coord.y][coord.z][EPathfindingLayer::LAND]; if(landNode->theNodeBefore) return landNode; else - return &nodes[coord.x][coord.y][coord.z][EPathfindingLayer::SAIL]; + return nodes[coord.x][coord.y][coord.z][EPathfindingLayer::SAIL]; } diff --git a/lib/CPathfinder.h b/lib/CPathfinder.h index 96e6c72ac..b4c7aea17 100644 --- a/lib/CPathfinder.h +++ b/lib/CPathfinder.h @@ -41,7 +41,7 @@ struct DLL_LINKAGE CGPathNode int3 coord; //coordinates EPathfindingLayer layer; - CGPathNode(); + CGPathNode(int3 Coord, EPathfindingLayer Layer); bool reachable() const; }; @@ -61,7 +61,7 @@ struct DLL_LINKAGE CPathsInfo const CGHeroInstance *hero; int3 hpos; int3 sizes; - CGPathNode ****nodes; //[w][h][level][layer] + boost::multi_array nodes; //[w][h][level][layer] CPathsInfo(const int3 &Sizes); ~CPathsInfo(); From bd8eec7fb8c1c29a243fa994478172f3d05788ff Mon Sep 17 00:00:00 2001 From: ArseniyShestakov Date: Sat, 7 Nov 2015 22:16:45 +0300 Subject: [PATCH 040/168] CGPathNode: move resetting code into separate function --- lib/CPathfinder.cpp | 12 ++++++------ lib/CPathfinder.h | 1 + 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/lib/CPathfinder.cpp b/lib/CPathfinder.cpp index 6ace8f104..918a12d15 100644 --- a/lib/CPathfinder.cpp +++ b/lib/CPathfinder.cpp @@ -393,14 +393,9 @@ void CPathfinder::initializeGraph() auto updateNode = [&](int3 pos, EPathfindingLayer layer, const TerrainTile *tinfo) { auto node = out.getNode(pos, layer); - node->locked = false; + node->reset(); node->accessible = evaluateAccessibility(pos, tinfo); - node->turns = 0xff; - node->moveRemains = 0; - node->coord = pos; node->land = tinfo->terType != ETerrainType::WATER; - node->theNodeBefore = nullptr; - node->layer = layer; }; int3 pos; @@ -520,6 +515,11 @@ bool CPathfinder::canVisitObject() const CGPathNode::CGPathNode(int3 Coord, EPathfindingLayer Layer) : coord(Coord), layer(Layer) +{ + reset(); +} + +void CGPathNode::reset() { locked = false; accessible = NOT_SET; diff --git a/lib/CPathfinder.h b/lib/CPathfinder.h index b4c7aea17..2d3616f42 100644 --- a/lib/CPathfinder.h +++ b/lib/CPathfinder.h @@ -42,6 +42,7 @@ struct DLL_LINKAGE CGPathNode EPathfindingLayer layer; CGPathNode(int3 Coord, EPathfindingLayer Layer); + void reset(); bool reachable() const; }; From 82048cbf2dc8c6305f59bbf65d5d06572076e049 Mon Sep 17 00:00:00 2001 From: ArseniyShestakov Date: Sun, 8 Nov 2015 00:26:41 +0300 Subject: [PATCH 041/168] Pathfinder: implement new feature - node action No action going to simplify isMovementAfterDestPossible and should be as well useful for cost calculations. It's can be also used by client interface to show appropriate cursors and path. --- lib/CPathfinder.cpp | 57 +++++++++++++++++++++++++++++++++++---------- lib/CPathfinder.h | 14 ++++++++++- 2 files changed, 58 insertions(+), 13 deletions(-) diff --git a/lib/CPathfinder.cpp b/lib/CPathfinder.cpp index 918a12d15..b54d915c8 100644 --- a/lib/CPathfinder.cpp +++ b/lib/CPathfinder.cpp @@ -106,7 +106,6 @@ void CPathfinder::calculatePaths() dt = &gs->map->getTile(neighbour); for(EPathfindingLayer i = EPathfindingLayer::LAND; i <= EPathfindingLayer::AIR; i.advance(1)) { - useEmbarkCost = 0; //0 - usual movement; 1 - embark; 2 - disembark dp = out.getNode(neighbour, i); if(dp->accessible == CGPathNode::NOT_SET) continue; @@ -117,14 +116,15 @@ void CPathfinder::calculatePaths() if(cp->layer != i && !isLayerTransitionPossible()) continue; + destAction = CGPathNode::UNKNOWN; if(!isMovementToDestPossible()) continue; int cost = gs->getMovementCost(hero, cp->coord, dp->coord, movement); int remains = movement - cost; - if(useEmbarkCost) + if(destAction == CGPathNode::EMBARK || destAction == CGPathNode::DISEMBARK) { - remains = hero->movementPointsAfterEmbark(movement, cost, useEmbarkCost - 1); + remains = hero->movementPointsAfterEmbark(movement, cost, destAction - 1); cost = movement - remains; } @@ -144,6 +144,7 @@ void CPathfinder::calculatePaths() dp->moveRemains = remains; dp->turns = turnAtNextTile; dp->theNodeBefore = cp; + dp->action = destAction; if(isMovementAfterDestPossible()) pq.push(dp); @@ -166,6 +167,7 @@ void CPathfinder::calculatePaths() dp->moveRemains = movement; dp->turns = turn; dp->theNodeBefore = cp; + dp->action = CGPathNode::NORMAL; pq.push(dp); } } @@ -258,24 +260,18 @@ bool CPathfinder::isLayerTransitionPossible() if((dp->accessible != CGPathNode::ACCESSIBLE && (dp->accessible != CGPathNode::BLOCKVIS || dt->blocked)) || dt->visitable) //TODO: passableness problem -> town says it's passable (thus accessible) but we obviously can't disembark onto town gate return false; - - useEmbarkCost = 2; } else if(cp->layer == EPathfindingLayer::LAND && dp->layer == EPathfindingLayer::SAIL) { - Obj destTopVisObjID = dt->topVisitableId(); - if(dp->accessible == CGPathNode::ACCESSIBLE || destTopVisObjID < 0) //cannot enter empty water tile from land -> it has to be visitable + if(dp->accessible == CGPathNode::ACCESSIBLE) //cannot enter empty water tile from land -> it has to be visitable return false; - if(destTopVisObjID != Obj::HERO && destTopVisObjID != Obj::BOAT) //only boat or hero can be accessed from land - return false; - if(destTopVisObjID == Obj::BOAT) - useEmbarkCost = 1; } return true; } bool CPathfinder::isMovementToDestPossible() { + auto obj = dt->topVisitableObj(); switch(dp->layer) { case EPathfindingLayer::LAND: @@ -284,6 +280,9 @@ bool CPathfinder::isMovementToDestPossible() if(isSourceGuarded() && !isDestinationGuardian()) // Can step into tile of guard return false; + if(cp->layer == EPathfindingLayer::SAIL) + destAction = CGPathNode::DISEMBARK; + break; case EPathfindingLayer::SAIL: if(!canMoveBetween(cp->coord, dp->coord) || dp->accessible == CGPathNode::BLOCKED) @@ -291,6 +290,16 @@ bool CPathfinder::isMovementToDestPossible() if(isSourceGuarded() && !isDestinationGuardian()) // Can step into tile of guard return false; + if(cp->layer == EPathfindingLayer::LAND) + { + if(!obj) + return false; + + if(obj->ID == Obj::BOAT) + destAction = CGPathNode::EMBARK; + else if(obj->ID != Obj::HERO) + return false; + } break; case EPathfindingLayer::AIR: @@ -308,6 +317,29 @@ bool CPathfinder::isMovementToDestPossible() break; } + if(destAction == CGPathNode::UNKNOWN) + { + destAction = CGPathNode::NORMAL; + if(dp->layer == EPathfindingLayer::LAND || dp->layer == EPathfindingLayer::SAIL) + { + if(obj) + { + if(obj->ID == Obj::HERO || obj->ID == Obj::TOWN) + { + if(getPlayerRelations(obj->tempOwner, hero->tempOwner) == PlayerRelations::ENEMIES) + destAction = CGPathNode::BATTLE; + else + destAction = CGPathNode::BLOCKING_VISIT; // TODO: Probably you should be able to go into air from town too + } + else if(obj->blockVisit) + destAction = CGPathNode::BLOCKING_VISIT; + else + destAction = CGPathNode::VISIT; + } + else if(isDestinationGuarded()) + destAction = CGPathNode::BATTLE; + } + } return true; } @@ -330,7 +362,7 @@ bool CPathfinder::isMovementAfterDestPossible() if(!whirlpool || options.useTeleportWhirlpool) return true; } - if(useEmbarkCost && options.useEmbarkAndDisembark) + if((destAction == CGPathNode::EMBARK || destAction == CGPathNode::DISEMBARK) && options.useEmbarkAndDisembark) return true; break; @@ -527,6 +559,7 @@ void CGPathNode::reset() moveRemains = 0; turns = 255; theNodeBefore = nullptr; + action = UNKNOWN; } bool CGPathNode::reachable() const diff --git a/lib/CPathfinder.h b/lib/CPathfinder.h index 2d3616f42..f9afcd698 100644 --- a/lib/CPathfinder.h +++ b/lib/CPathfinder.h @@ -23,6 +23,17 @@ struct TerrainTile; struct DLL_LINKAGE CGPathNode { + enum ENodeAction + { + UNKNOWN = -1, + NORMAL = 0, + EMBARK = 1, + DISEMBARK, //2 + BATTLE,//3 + VISIT,//4 + BLOCKING_VISIT//5 + }; + enum EAccessibility { NOT_SET = 0, @@ -40,6 +51,7 @@ struct DLL_LINKAGE CGPathNode CGPathNode * theNodeBefore; int3 coord; //coordinates EPathfindingLayer layer; + ENodeAction action; CGPathNode(int3 Coord, EPathfindingLayer Layer); void reset(); @@ -120,7 +132,7 @@ private: 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::ENodeAction destAction; void addNeighbours(const int3 &coord); void addTeleportExits(bool noTeleportExcludes = false); From ba1062ed3c7684240787d3996f895d80ea010143 Mon Sep 17 00:00:00 2001 From: edeksumo Date: Sat, 7 Nov 2015 23:30:01 +0100 Subject: [PATCH 042/168] Formating2 Change ani to anim --- client/battle/CBattleAnimations.cpp | 4 ++-- lib/GameConstants.h | 2 +- lib/NetPacksLib.cpp | 6 +++--- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/client/battle/CBattleAnimations.cpp b/client/battle/CBattleAnimations.cpp index 77599d74a..212bdc9e7 100644 --- a/client/battle/CBattleAnimations.cpp +++ b/client/battle/CBattleAnimations.cpp @@ -233,7 +233,7 @@ std::string CDefenceAnimation::getMySound() if(killed) return battle_sound(stack->getCreature(), killed); - if (vstd::contains(stack->state, EBattleStackState::DEFENDING_ANI)) + if (vstd::contains(stack->state, EBattleStackState::DEFENDING_ANIM)) return battle_sound(stack->getCreature(), defend); return battle_sound(stack->getCreature(), wince); @@ -244,7 +244,7 @@ CCreatureAnim::EAnimType CDefenceAnimation::getMyAnimType() if(killed) return CCreatureAnim::DEATH; - if (vstd::contains(stack->state, EBattleStackState::DEFENDING_ANI)) + if (vstd::contains(stack->state, EBattleStackState::DEFENDING_ANIM)) return CCreatureAnim::DEFENCE; return CCreatureAnim::HITTED; diff --git a/lib/GameConstants.h b/lib/GameConstants.h index d1fd781fd..5f53d9902 100644 --- a/lib/GameConstants.h +++ b/lib/GameConstants.h @@ -445,7 +445,7 @@ namespace EBattleStackState //remember to drain mana only once per turn DRAINED_MANA, //only for defending animation - DEFENDING_ANI + DEFENDING_ANIM }; } diff --git a/lib/NetPacksLib.cpp b/lib/NetPacksLib.cpp index 3cce6063b..505fb087e 100644 --- a/lib/NetPacksLib.cpp +++ b/lib/NetPacksLib.cpp @@ -1304,8 +1304,8 @@ DLL_LINKAGE void StartAction::applyGs( CGameState *gs ) { CStack *st = gs->curB->getStack(ba.stackNumber); - if(vstd::contains(st->state, EBattleStackState::DEFENDING_ANI)) - st->state -= EBattleStackState::DEFENDING_ANI; + if(vstd::contains(st->state, EBattleStackState::DEFENDING_ANIM)) + st->state -= EBattleStackState::DEFENDING_ANIM; if(ba.actionType == Battle::END_TACTIC_PHASE) { @@ -1333,7 +1333,7 @@ DLL_LINKAGE void StartAction::applyGs( CGameState *gs ) { case Battle::DEFEND: st->state.insert(EBattleStackState::DEFENDING); - st->state.insert(EBattleStackState::DEFENDING_ANI); + st->state.insert(EBattleStackState::DEFENDING_ANIM); break; case Battle::WAIT: st->state.insert(EBattleStackState::WAITING); From 160fa382544824c8e94617f7b0bc7c04cd1d3b5b Mon Sep 17 00:00:00 2001 From: ArseniyShestakov Date: Sun, 8 Nov 2015 03:10:48 +0300 Subject: [PATCH 043/168] Client: change cursor using node action information of pathfinder --- client/windows/CAdvmapInterface.cpp | 147 ++++++++-------------------- lib/CPathfinder.cpp | 29 ++++-- lib/CPathfinder.h | 2 +- 3 files changed, 65 insertions(+), 113 deletions(-) diff --git a/client/windows/CAdvmapInterface.cpp b/client/windows/CAdvmapInterface.cpp index ab3ab31b7..65d5b0324 100644 --- a/client/windows/CAdvmapInterface.cpp +++ b/client/windows/CAdvmapInterface.cpp @@ -1477,8 +1477,10 @@ void CAdvMapInt::tileHovered(const int3 &mapPos) } const CGObjectInstance *objAtTile = getActiveObject(mapPos); - if (objAtTile) + auto objRelations = PlayerRelations::ALLIES; + 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); @@ -1516,8 +1518,6 @@ void CAdvMapInt::tileHovered(const int3 &mapPos) } } - const bool guardingCreature = CGI->mh->map->isInTheMap(LOCPLINT->cb->getGuardingCreaturePosition(mapPos)); - if(selection->ID == Obj::TOWN) { if(objAtTile) @@ -1534,125 +1534,60 @@ void CAdvMapInt::tileHovered(const int3 &mapPos) } 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) { + 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->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(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); + if(selection == objAtTile) + CCS->curh->changeGraphic(ECursor::ADVENTURE, 2); else - CCS->curh->changeGraphic(ECursor::ADVENTURE, 0); - } - else if (objAtTile->ID == Obj::GARRISON || objAtTile->ID == Obj::GARRISON2) - { - if (accessible) - { - const CGGarrison* garrObj = dynamic_cast(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); + CCS->curh->changeGraphic(ECursor::ADVENTURE, 8 + turns*6); } + 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(accessible) + if(objAtTile->ID == Obj::TOWN) { - if(pnode->land) - CCS->curh->changeGraphic(ECursor::ADVENTURE, 9 + turns*6); - else - CCS->curh->changeGraphic(ECursor::ADVENTURE, 28 + turns); + CCS->curh->changeGraphic(ECursor::ADVENTURE, 3); } - else - CCS->curh->changeGraphic(ECursor::ADVENTURE, 0); - } - } - else //no objs - { - if(accessible/* && pnode->accessible != CGPathNode::FLYABLE*/) - { - if (guardingCreature) + else if(objAtTile->ID == Obj::HERO && objRelations == PlayerRelations::SAME_PLAYER) { - 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); + CCS->curh->changeGraphic(ECursor::ADVENTURE, 2); } } else CCS->curh->changeGraphic(ECursor::ADVENTURE, 0); + break; } } diff --git a/lib/CPathfinder.cpp b/lib/CPathfinder.cpp index b54d915c8..2f6cdef6a 100644 --- a/lib/CPathfinder.cpp +++ b/lib/CPathfinder.cpp @@ -324,16 +324,33 @@ bool CPathfinder::isMovementToDestPossible() { if(obj) { - if(obj->ID == Obj::HERO || obj->ID == Obj::TOWN) + auto objRel = getPlayerRelations(obj->tempOwner, hero->tempOwner); + if(obj->ID == Obj::HERO) { - if(getPlayerRelations(obj->tempOwner, hero->tempOwner) == PlayerRelations::ENEMIES) + if(objRel == PlayerRelations::ENEMIES) destAction = CGPathNode::BATTLE; else - destAction = CGPathNode::BLOCKING_VISIT; // TODO: Probably you should be able to go into air from town too + destAction = CGPathNode::BLOCKING_VISIT; } + else if(obj->ID == Obj::TOWN && objRel == PlayerRelations::ENEMIES) + { + const CGTownInstance * townObj = dynamic_cast(obj); + if (townObj->armedGarrison()) + destAction = CGPathNode::BATTLE; + } + else if(obj->ID == Obj::GARRISON || obj->ID == Obj::GARRISON2) + { + const CGGarrison * garrisonObj = dynamic_cast(obj); + if((garrisonObj->stacksCount() && objRel == PlayerRelations::ENEMIES) || isDestinationGuarded(true)) + destAction = CGPathNode::BATTLE; + } + else if(isDestinationGuardian()) + destAction = CGPathNode::BATTLE; else if(obj->blockVisit) destAction = CGPathNode::BLOCKING_VISIT; - else + + + if(destAction == CGPathNode::NORMAL) destAction = CGPathNode::VISIT; } else if(isDestinationGuarded()) @@ -404,10 +421,10 @@ bool CPathfinder::isSourceGuarded() return false; } -bool CPathfinder::isDestinationGuarded() +bool CPathfinder::isDestinationGuarded(bool ignoreAccessibility) { if(gs->map->guardingCreaturePositions[dp->coord.x][dp->coord.y][dp->coord.z].valid() - && dp->accessible == CGPathNode::BLOCKVIS) + && (ignoreAccessibility || dp->accessible == CGPathNode::BLOCKVIS)) { return true; } diff --git a/lib/CPathfinder.h b/lib/CPathfinder.h index f9afcd698..69b7f91a8 100644 --- a/lib/CPathfinder.h +++ b/lib/CPathfinder.h @@ -144,7 +144,7 @@ private: bool isSourceInitialPosition(); int3 getSourceGuardPosition(); bool isSourceGuarded(); - bool isDestinationGuarded(); + bool isDestinationGuarded(bool ignoreAccessibility = true); bool isDestinationGuardian(); void initializeGraph(); From 842da69a3e2132ad049d08d75ca6190f5312ece3 Mon Sep 17 00:00:00 2001 From: ArseniyShestakov Date: Sun, 8 Nov 2015 03:33:01 +0300 Subject: [PATCH 044/168] CAdvMapInt::tileHovered cleanup function a bit more --- client/windows/CAdvmapInterface.cpp | 23 ++++++++--------------- 1 file changed, 8 insertions(+), 15 deletions(-) diff --git a/client/windows/CAdvmapInterface.cpp b/client/windows/CAdvmapInterface.cpp index 65d5b0324..4899732c0 100644 --- a/client/windows/CAdvmapInterface.cpp +++ b/client/windows/CAdvmapInterface.cpp @@ -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,9 +1476,8 @@ void CAdvMapInt::tileHovered(const int3 &mapPos) statusbar.clear(); return; } - const CGObjectInstance *objAtTile = getActiveObject(mapPos); - auto objRelations = PlayerRelations::ALLIES; + const CGObjectInstance *objAtTile = getActiveObject(mapPos); if(objAtTile) { objRelations = LOCPLINT->cb->getPlayerRelations(LOCPLINT->playerID, objAtTile->tempOwner); @@ -1492,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) @@ -1507,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); @@ -1522,9 +1519,9 @@ void CAdvMapInt::tileHovered(const int3 &mapPos) { 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,7 +1529,7 @@ 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); int turns = pnode->turns; @@ -1577,13 +1574,9 @@ void CAdvMapInt::tileHovered(const int3 &mapPos) if(objAtTile && objRelations != PlayerRelations::ENEMIES) { 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); From 4973a1ec90b7df2c48619fa6f90b4402d6b22e3b Mon Sep 17 00:00:00 2001 From: ArseniyShestakov Date: Sun, 8 Nov 2015 04:01:58 +0300 Subject: [PATCH 045/168] CGPathNode: get rid of land member as it's now obsolete CTerrainRect::showPath behaviour changed so it's will only add cross path graphics on embark/disembark and path ending. We want continuous paths for flying and water walking even when land<-> water transition occur. --- client/windows/CAdvmapInterface.cpp | 2 +- lib/CPathfinder.cpp | 8 +++----- lib/CPathfinder.h | 1 - 3 files changed, 4 insertions(+), 7 deletions(-) diff --git a/client/windows/CAdvmapInterface.cpp b/client/windows/CAdvmapInterface.cpp index 4899732c0..44274fa3c 100644 --- a/client/windows/CAdvmapInterface.cpp +++ b/client/windows/CAdvmapInterface.cpp @@ -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 diff --git a/lib/CPathfinder.cpp b/lib/CPathfinder.cpp index 2f6cdef6a..7994ec902 100644 --- a/lib/CPathfinder.cpp +++ b/lib/CPathfinder.cpp @@ -63,7 +63,7 @@ void CPathfinder::calculatePaths() auto maxMovePoints = [&](CGPathNode *cp) -> int { - return cp->land ? maxMovePointsLand : maxMovePointsWater; + return cp->layer == EPathfindingLayer::SAIL ? maxMovePointsWater : maxMovePointsLand; }; auto isBetterWay = [&](int remains, int turn) -> bool @@ -181,7 +181,7 @@ void CPathfinder::addNeighbours(const int3 &coord) ct = &gs->map->getTile(coord); std::vector tiles; - gs->getNeighbours(*ct, coord, tiles, boost::logic::indeterminate, !cp->land); + gs->getNeighbours(*ct, coord, tiles, boost::logic::indeterminate, cp->layer == EPathfindingLayer::SAIL); // TODO: find out if we still need "limitCoastSailing" option sTileObj = ct->topVisitableObj(coord == out.hpos); if(canVisitObject()) { @@ -411,7 +411,7 @@ bool CPathfinder::isSourceGuarded() { //special case -> hero embarked a boat standing on a guarded tile -> we must allow to move away from that tile if(cp->accessible != CGPathNode::VISITABLE - || !cp->theNodeBefore->land + || !cp->theNodeBefore->layer != EPathfindingLayer::LAND || ct->topVisitableId() != Obj::BOAT) { return true; @@ -444,7 +444,6 @@ void CPathfinder::initializeGraph() auto node = out.getNode(pos, layer); node->reset(); node->accessible = evaluateAccessibility(pos, tinfo); - node->land = tinfo->terType != ETerrainType::WATER; }; int3 pos; @@ -572,7 +571,6 @@ void CGPathNode::reset() { locked = false; accessible = NOT_SET; - land = 0; moveRemains = 0; turns = 255; theNodeBefore = nullptr; diff --git a/lib/CPathfinder.h b/lib/CPathfinder.h index 69b7f91a8..e0bd8cea5 100644 --- a/lib/CPathfinder.h +++ b/lib/CPathfinder.h @@ -45,7 +45,6 @@ struct DLL_LINKAGE CGPathNode bool locked; 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; From 2fb73c55e1b98fcd26dd8b5daacccb20bca44829 Mon Sep 17 00:00:00 2001 From: ArseniyShestakov Date: Sun, 8 Nov 2015 04:41:06 +0300 Subject: [PATCH 046/168] CPathfinder: use node action in isMovementAfterDestPossible checks --- lib/CPathfinder.cpp | 42 +++++++++++++++++++++--------------------- 1 file changed, 21 insertions(+), 21 deletions(-) diff --git a/lib/CPathfinder.cpp b/lib/CPathfinder.cpp index 7994ec902..209dfee29 100644 --- a/lib/CPathfinder.cpp +++ b/lib/CPathfinder.cpp @@ -363,31 +363,31 @@ bool CPathfinder::isMovementToDestPossible() bool CPathfinder::isMovementAfterDestPossible() { - switch(dp->layer) + switch(destAction) { - case EPathfindingLayer::LAND: - case EPathfindingLayer::SAIL: - if(dp->accessible == CGPathNode::ACCESSIBLE) + /// TODO: Investigate what kind of limitation is possible to apply on movement from visitable tiles + /// Likely in many cases we don't need to add visitable tile to queue when hero don't fly + case CGPathNode::VISIT: + if(CGTeleport::isTeleport(dt->topVisitableObj())) + { + /// For now we'll always allow transit over teleporters + /// Transit over whirlpools only allowed when hero protected + auto whirlpool = dynamic_cast(dt->topVisitableObj()); + if(!whirlpool || options.useTeleportWhirlpool) return true; - if(dp->coord == out.hpos) - return true; // This one is tricky, we can ignore fact that tile is not ACCESSIBLE in case if it's our hero block it. Though this need investigation - if(dp->accessible == CGPathNode::VISITABLE && CGTeleport::isTeleport(dt->topVisitableObj())) - { - /// For now we'll always allow transit over teleporters - /// Transit over whirlpools only allowed when hero protected - auto whirlpool = dynamic_cast(dt->topVisitableObj()); - if(!whirlpool || options.useTeleportWhirlpool) - return true; - } - if((destAction == CGPathNode::EMBARK || destAction == CGPathNode::DISEMBARK) && options.useEmbarkAndDisembark) - return true; - break; + } + else + return true; + case CGPathNode::NORMAL: + return true; - case EPathfindingLayer::AIR: - case EPathfindingLayer::WATER: - return true; + case CGPathNode::EMBARK: + if(options.useEmbarkAndDisembark) + return true; - break; + case CGPathNode::DISEMBARK: + if(options.useEmbarkAndDisembark && !isDestinationGuarded()) + return true; } return false; From 4af9c7c29dd849dc97571df3ef82a0f5342f7ac2 Mon Sep 17 00:00:00 2001 From: ArseniyShestakov Date: Sun, 8 Nov 2015 07:44:00 +0300 Subject: [PATCH 047/168] CPathfinder: add one turn boundary for flying and water walking Now when oneTurnSpecialLayersLimit is enabled hero won't be able to stop on blocked or water tiles between turns. It's default H3 mechanics so it's enabled by default. --- lib/CPathfinder.cpp | 41 ++++++++++++++++++++++++++++++++--------- lib/CPathfinder.h | 7 +++++++ 2 files changed, 39 insertions(+), 9 deletions(-) diff --git a/lib/CPathfinder.cpp b/lib/CPathfinder.cpp index 209dfee29..9e19f1854 100644 --- a/lib/CPathfinder.cpp +++ b/lib/CPathfinder.cpp @@ -29,6 +29,7 @@ CPathfinder::PathfinderOptions::PathfinderOptions() useTeleportWhirlpool = false; lightweightFlyingMode = false; + oneTurnSpecialLayersLimit = true; } CPathfinder::CPathfinder(CPathsInfo &_out, CGameState *_gs, const CGHeroInstance *_hero) @@ -66,6 +67,19 @@ void CPathfinder::calculatePaths() return cp->layer == EPathfindingLayer::SAIL ? maxMovePointsWater : maxMovePointsLand; }; + auto passOneTurnLimitCheck = [&](bool shouldCheck) -> bool + { + if(options.oneTurnSpecialLayersLimit && shouldCheck) + { + if((cp->layer == EPathfindingLayer::AIR || cp->layer == EPathfindingLayer::WATER) + && cp->accessible != CGPathNode::ACCESSIBLE) + { + return false; + } + } + return true; + }; + auto isBetterWay = [&](int remains, int turn) -> bool { if(dp->turns == 0xff) //we haven't been here before @@ -113,9 +127,13 @@ void CPathfinder::calculatePaths() if(dp->locked) continue; + if(!passOneTurnLimitCheck(cp->turns != turn)) + continue; + if(cp->layer != i && !isLayerTransitionPossible()) continue; + destAction = CGPathNode::UNKNOWN; if(!isMovementToDestPossible()) continue; @@ -127,7 +145,6 @@ void CPathfinder::calculatePaths() remains = hero->movementPointsAfterEmbark(movement, cost, destAction - 1); cost = movement - remains; } - int turnAtNextTile = turn; if(remains < 0) { @@ -138,7 +155,8 @@ void CPathfinder::calculatePaths() remains = moveAtNextTile - cost; } - if(isBetterWay(remains, turnAtNextTile)) + if(isBetterWay(remains, turnAtNextTile) + && passOneTurnLimitCheck(cp->turns != turnAtNextTile || !remains)) { assert(dp != cp->theNodeBefore); //two tiles can't point to each other dp->moveRemains = remains; @@ -439,11 +457,16 @@ bool CPathfinder::isDestinationGuardian() void CPathfinder::initializeGraph() { - auto updateNode = [&](int3 pos, EPathfindingLayer layer, const TerrainTile *tinfo) + auto updateNode = [&](int3 pos, EPathfindingLayer layer, const TerrainTile *tinfo, bool blockNotAccessible) { auto node = out.getNode(pos, layer); node->reset(); - node->accessible = evaluateAccessibility(pos, tinfo); + + auto accessibility = evaluateAccessibility(pos, tinfo); + if(blockNotAccessible + && (accessibility != CGPathNode::ACCESSIBLE || tinfo->terType == ETerrainType::WATER)) + accessibility = CGPathNode::BLOCKED; + node->accessible = accessibility; }; int3 pos; @@ -459,16 +482,16 @@ void CPathfinder::initializeGraph() case ETerrainType::ROCK: break; case ETerrainType::WATER: - updateNode(pos, EPathfindingLayer::SAIL, tinfo); + updateNode(pos, EPathfindingLayer::SAIL, tinfo, false); if(options.useFlying) - updateNode(pos, EPathfindingLayer::AIR, tinfo); + updateNode(pos, EPathfindingLayer::AIR, tinfo, true); if(options.useWaterWalking) - updateNode(pos, EPathfindingLayer::WATER, tinfo); + updateNode(pos, EPathfindingLayer::WATER, tinfo, true); break; default: - updateNode(pos, EPathfindingLayer::LAND, tinfo); + updateNode(pos, EPathfindingLayer::LAND, tinfo, false); if(options.useFlying) - updateNode(pos, EPathfindingLayer::AIR, tinfo); + updateNode(pos, EPathfindingLayer::AIR, tinfo, true); break; } } diff --git a/lib/CPathfinder.h b/lib/CPathfinder.h index e0bd8cea5..209e89493 100644 --- a/lib/CPathfinder.h +++ b/lib/CPathfinder.h @@ -105,6 +105,13 @@ private: /// Downside is less MP effective paths calculation. 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. + bool oneTurnSpecialLayersLimit; + PathfinderOptions(); } options; From f590b364c53c16ac212a77abe9b8afe9aa991fe8 Mon Sep 17 00:00:00 2001 From: ArseniyShestakov Date: Sun, 8 Nov 2015 08:27:51 +0300 Subject: [PATCH 048/168] Pathfinder: shorten EPathfindingLayer to ELayer --- lib/CPathfinder.cpp | 76 ++++++++++++++++++++++----------------------- lib/CPathfinder.h | 18 +++++++---- 2 files changed, 50 insertions(+), 44 deletions(-) diff --git a/lib/CPathfinder.cpp b/lib/CPathfinder.cpp index 9e19f1854..1bd34d30d 100644 --- a/lib/CPathfinder.cpp +++ b/lib/CPathfinder.cpp @@ -64,14 +64,14 @@ void CPathfinder::calculatePaths() auto maxMovePoints = [&](CGPathNode *cp) -> int { - return cp->layer == EPathfindingLayer::SAIL ? maxMovePointsWater : maxMovePointsLand; + return cp->layer == ELayer::SAIL ? maxMovePointsWater : maxMovePointsLand; }; auto passOneTurnLimitCheck = [&](bool shouldCheck) -> bool { if(options.oneTurnSpecialLayersLimit && shouldCheck) { - if((cp->layer == EPathfindingLayer::AIR || cp->layer == EPathfindingLayer::WATER) + if((cp->layer == ELayer::AIR || cp->layer == ELayer::WATER) && cp->accessible != CGPathNode::ACCESSIBLE) { return false; @@ -95,7 +95,7 @@ void CPathfinder::calculatePaths() //logGlobal->infoStream() << boost::format("Calculating paths for hero %s (adress %d) of player %d") % hero->name % hero % hero->tempOwner; //initial tile - set cost on 0 and add to the queue - CGPathNode *initialNode = out.getNode(out.hpos, hero->boat ? EPathfindingLayer::SAIL : EPathfindingLayer::LAND); + CGPathNode *initialNode = out.getNode(out.hpos, hero->boat ? ELayer::SAIL : ELayer::LAND); initialNode->turns = 0; initialNode->moveRemains = hero->movement; pq.push(initialNode); @@ -118,7 +118,7 @@ void CPathfinder::calculatePaths() for(auto & neighbour : neighbours) { dt = &gs->map->getTile(neighbour); - for(EPathfindingLayer i = EPathfindingLayer::LAND; i <= EPathfindingLayer::AIR; i.advance(1)) + for(ELayer i = ELayer::LAND; i <= ELayer::AIR; i.advance(1)) { dp = out.getNode(neighbour, i); if(dp->accessible == CGPathNode::NOT_SET) @@ -199,7 +199,7 @@ void CPathfinder::addNeighbours(const int3 &coord) ct = &gs->map->getTile(coord); std::vector tiles; - gs->getNeighbours(*ct, coord, tiles, boost::logic::indeterminate, cp->layer == EPathfindingLayer::SAIL); // TODO: find out if we still need "limitCoastSailing" option + gs->getNeighbours(*ct, coord, tiles, boost::logic::indeterminate, cp->layer == ELayer::SAIL); // TODO: find out if we still need "limitCoastSailing" option sTileObj = ct->topVisitableObj(coord == out.hpos); if(canVisitObject()) { @@ -257,19 +257,19 @@ void CPathfinder::addTeleportExits(bool noTeleportExcludes) bool CPathfinder::isLayerTransitionPossible() { - if((cp->layer == EPathfindingLayer::AIR || cp->layer == EPathfindingLayer::WATER) - && dp->layer != EPathfindingLayer::LAND) + if((cp->layer == ELayer::AIR || cp->layer == ELayer::WATER) + && dp->layer != ELayer::LAND) { return false; } - else if(cp->layer == EPathfindingLayer::LAND && dp->layer == EPathfindingLayer::AIR) + else if(cp->layer == ELayer::LAND && dp->layer == ELayer::AIR) { if(options.lightweightFlyingMode && !isSourceInitialPosition()) return false; } - else if(cp->layer == EPathfindingLayer::SAIL && dp->layer != EPathfindingLayer::LAND) + else if(cp->layer == ELayer::SAIL && dp->layer != ELayer::LAND) return false; - else if(cp->layer == EPathfindingLayer::SAIL && dp->layer == EPathfindingLayer::LAND) + else if(cp->layer == ELayer::SAIL && dp->layer == ELayer::LAND) { if(!dt->isCoastal()) return false; @@ -279,7 +279,7 @@ bool CPathfinder::isLayerTransitionPossible() || dt->visitable) //TODO: passableness problem -> town says it's passable (thus accessible) but we obviously can't disembark onto town gate return false; } - else if(cp->layer == EPathfindingLayer::LAND && dp->layer == EPathfindingLayer::SAIL) + else if(cp->layer == ELayer::LAND && dp->layer == ELayer::SAIL) { if(dp->accessible == CGPathNode::ACCESSIBLE) //cannot enter empty water tile from land -> it has to be visitable return false; @@ -292,23 +292,23 @@ bool CPathfinder::isMovementToDestPossible() auto obj = dt->topVisitableObj(); switch(dp->layer) { - case EPathfindingLayer::LAND: + case ELayer::LAND: if(!canMoveBetween(cp->coord, dp->coord) || dp->accessible == CGPathNode::BLOCKED) return false; if(isSourceGuarded() && !isDestinationGuardian()) // Can step into tile of guard return false; - if(cp->layer == EPathfindingLayer::SAIL) + if(cp->layer == ELayer::SAIL) destAction = CGPathNode::DISEMBARK; break; - case EPathfindingLayer::SAIL: + case ELayer::SAIL: if(!canMoveBetween(cp->coord, dp->coord) || dp->accessible == CGPathNode::BLOCKED) return false; if(isSourceGuarded() && !isDestinationGuardian()) // Can step into tile of guard return false; - if(cp->layer == EPathfindingLayer::LAND) + if(cp->layer == ELayer::LAND) { if(!obj) return false; @@ -320,13 +320,13 @@ bool CPathfinder::isMovementToDestPossible() } break; - case EPathfindingLayer::AIR: + case ELayer::AIR: //if(!canMoveBetween(cp->coord, dp->coord)) // return false; break; - case EPathfindingLayer::WATER: + case ELayer::WATER: if(!canMoveBetween(cp->coord, dp->coord) || dp->accessible != CGPathNode::ACCESSIBLE) return false; if(isDestinationGuarded()) @@ -338,7 +338,7 @@ bool CPathfinder::isMovementToDestPossible() if(destAction == CGPathNode::UNKNOWN) { destAction = CGPathNode::NORMAL; - if(dp->layer == EPathfindingLayer::LAND || dp->layer == EPathfindingLayer::SAIL) + if(dp->layer == ELayer::LAND || dp->layer == ELayer::SAIL) { if(obj) { @@ -429,7 +429,7 @@ bool CPathfinder::isSourceGuarded() { //special case -> hero embarked a boat standing on a guarded tile -> we must allow to move away from that tile if(cp->accessible != CGPathNode::VISITABLE - || !cp->theNodeBefore->layer != EPathfindingLayer::LAND + || !cp->theNodeBefore->layer != ELayer::LAND || ct->topVisitableId() != Obj::BOAT) { return true; @@ -457,7 +457,7 @@ bool CPathfinder::isDestinationGuardian() void CPathfinder::initializeGraph() { - auto updateNode = [&](int3 pos, EPathfindingLayer layer, const TerrainTile *tinfo, bool blockNotAccessible) + auto updateNode = [&](int3 pos, ELayer layer, const TerrainTile *tinfo, bool blockNotAccessible) { auto node = out.getNode(pos, layer); node->reset(); @@ -482,16 +482,16 @@ void CPathfinder::initializeGraph() case ETerrainType::ROCK: break; case ETerrainType::WATER: - updateNode(pos, EPathfindingLayer::SAIL, tinfo, false); + updateNode(pos, ELayer::SAIL, tinfo, false); if(options.useFlying) - updateNode(pos, EPathfindingLayer::AIR, tinfo, true); + updateNode(pos, ELayer::AIR, tinfo, true); if(options.useWaterWalking) - updateNode(pos, EPathfindingLayer::WATER, tinfo, true); + updateNode(pos, ELayer::WATER, tinfo, true); break; default: - updateNode(pos, EPathfindingLayer::LAND, tinfo, false); + updateNode(pos, ELayer::LAND, tinfo, false); if(options.useFlying) - updateNode(pos, EPathfindingLayer::AIR, tinfo, true); + updateNode(pos, ELayer::AIR, tinfo, true); break; } } @@ -581,10 +581,10 @@ bool CPathfinder::addTeleportWhirlpool(const CGWhirlpool * obj) const bool CPathfinder::canVisitObject() const { //hero can't visit objects while walking on water or flying - return cp->layer == EPathfindingLayer::LAND || cp->layer == EPathfindingLayer::SAIL; + return cp->layer == ELayer::LAND || cp->layer == ELayer::SAIL; } -CGPathNode::CGPathNode(int3 Coord, EPathfindingLayer Layer) +CGPathNode::CGPathNode(int3 Coord, ELayer Layer) : coord(Coord), layer(Layer) { reset(); @@ -630,28 +630,28 @@ CPathsInfo::CPathsInfo(const int3 &Sizes) : sizes(Sizes) { hero = nullptr; - nodes.resize(boost::extents[sizes.x][sizes.y][sizes.z][EPathfindingLayer::NUM_LAYERS]); + nodes.resize(boost::extents[sizes.x][sizes.y][sizes.z][ELayer::NUM_LAYERS]); for(int i = 0; i < sizes.x; i++) for(int j = 0; j < sizes.y; j++) for(int z = 0; z < sizes.z; z++) - for(int l = 0; l < EPathfindingLayer::NUM_LAYERS; l++) - nodes[i][j][z][l] = new CGPathNode(int3(i, j, z), static_cast(l)); + for(int l = 0; l < ELayer::NUM_LAYERS; l++) + nodes[i][j][z][l] = new CGPathNode(int3(i, j, z), static_cast(l)); } CPathsInfo::~CPathsInfo() { } -const CGPathNode * CPathsInfo::getPathInfo(const int3 &tile, const EPathfindingLayer &layer) const +const CGPathNode * CPathsInfo::getPathInfo(const int3 &tile, const ELayer &layer) const { boost::unique_lock pathLock(pathMx); - if(tile.x >= sizes.x || tile.y >= sizes.y || tile.z >= sizes.z || layer >= EPathfindingLayer::NUM_LAYERS) + if(tile.x >= sizes.x || tile.y >= sizes.y || tile.z >= sizes.z || layer >= ELayer::NUM_LAYERS) return nullptr; return getNode(tile, layer); } -bool CPathsInfo::getPath(CGPath &out, const int3 &dst, const EPathfindingLayer &layer) const +bool CPathsInfo::getPath(CGPath &out, const int3 &dst, const ELayer &layer) const { boost::unique_lock pathLock(pathMx); @@ -669,7 +669,7 @@ bool CPathsInfo::getPath(CGPath &out, const int3 &dst, const EPathfindingLayer & return true; } -int CPathsInfo::getDistance(const int3 &tile, const EPathfindingLayer &layer) const +int CPathsInfo::getDistance(const int3 &tile, const ELayer &layer) const { boost::unique_lock pathLock(pathMx); @@ -680,14 +680,14 @@ int CPathsInfo::getDistance(const int3 &tile, const EPathfindingLayer &layer) co return 255; } -CGPathNode *CPathsInfo::getNode(const int3 &coord, const EPathfindingLayer &layer) const +CGPathNode *CPathsInfo::getNode(const int3 &coord, const ELayer &layer) const { - if(layer != EPathfindingLayer::AUTO) + if(layer != ELayer::AUTO) return nodes[coord.x][coord.y][coord.z][layer]; - auto landNode = nodes[coord.x][coord.y][coord.z][EPathfindingLayer::LAND]; + auto landNode = nodes[coord.x][coord.y][coord.z][ELayer::LAND]; if(landNode->theNodeBefore) return landNode; else - return nodes[coord.x][coord.y][coord.z][EPathfindingLayer::SAIL]; + return nodes[coord.x][coord.y][coord.z][ELayer::SAIL]; } diff --git a/lib/CPathfinder.h b/lib/CPathfinder.h index 209e89493..0f748f4fe 100644 --- a/lib/CPathfinder.h +++ b/lib/CPathfinder.h @@ -23,6 +23,8 @@ struct TerrainTile; struct DLL_LINKAGE CGPathNode { + typedef EPathfindingLayer ELayer; + enum ENodeAction { UNKNOWN = -1, @@ -49,10 +51,10 @@ struct DLL_LINKAGE CGPathNode ui32 moveRemains; //remaining tiles after hero reaches the tile CGPathNode * theNodeBefore; int3 coord; //coordinates - EPathfindingLayer layer; + ELayer layer; ENodeAction action; - CGPathNode(int3 Coord, EPathfindingLayer Layer); + CGPathNode(int3 Coord, ELayer Layer); void reset(); bool reachable() const; }; @@ -68,6 +70,8 @@ struct DLL_LINKAGE CGPath struct DLL_LINKAGE CPathsInfo { + typedef EPathfindingLayer ELayer; + mutable boost::mutex pathMx; const CGHeroInstance *hero; @@ -77,10 +81,10 @@ struct DLL_LINKAGE CPathsInfo CPathsInfo(const int3 &Sizes); ~CPathsInfo(); - const CGPathNode * getPathInfo(const int3 &tile, const EPathfindingLayer &layer = EPathfindingLayer::AUTO) const; - bool getPath(CGPath &out, const int3 &dst, const EPathfindingLayer &layer = EPathfindingLayer::AUTO) const; - int getDistance(const int3 &tile, const EPathfindingLayer &layer = EPathfindingLayer::AUTO) const; - CGPathNode *getNode(const int3 &coord, const EPathfindingLayer &layer) const; + const CGPathNode * getPathInfo(const int3 &tile, const ELayer &layer = ELayer::AUTO) const; + bool getPath(CGPath &out, const int3 &dst, const ELayer &layer = ELayer::AUTO) const; + int getDistance(const int3 &tile, const ELayer &layer = ELayer::AUTO) const; + CGPathNode *getNode(const int3 &coord, const ELayer &layer) const; }; class CPathfinder : private CGameInfoCallback @@ -90,6 +94,8 @@ public: 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; From be37e1cd8bb380a8b29b5c386d59046d930b64ce Mon Sep 17 00:00:00 2001 From: ArseniyShestakov Date: Sun, 8 Nov 2015 08:39:00 +0300 Subject: [PATCH 049/168] CPathfinder: add const to appropriate methods --- lib/CPathfinder.cpp | 16 ++++++++-------- lib/CPathfinder.h | 14 +++++++------- 2 files changed, 15 insertions(+), 15 deletions(-) diff --git a/lib/CPathfinder.cpp b/lib/CPathfinder.cpp index 1bd34d30d..427f12dfd 100644 --- a/lib/CPathfinder.cpp +++ b/lib/CPathfinder.cpp @@ -255,7 +255,7 @@ void CPathfinder::addTeleportExits(bool noTeleportExcludes) } } -bool CPathfinder::isLayerTransitionPossible() +bool CPathfinder::isLayerTransitionPossible() const { if((cp->layer == ELayer::AIR || cp->layer == ELayer::WATER) && dp->layer != ELayer::LAND) @@ -379,7 +379,7 @@ bool CPathfinder::isMovementToDestPossible() return true; } -bool CPathfinder::isMovementAfterDestPossible() +bool CPathfinder::isMovementAfterDestPossible() const { switch(destAction) { @@ -411,17 +411,17 @@ bool CPathfinder::isMovementAfterDestPossible() return false; } -bool CPathfinder::isSourceInitialPosition() +bool CPathfinder::isSourceInitialPosition() const { return cp->coord == out.hpos; } -int3 CPathfinder::getSourceGuardPosition() +int3 CPathfinder::getSourceGuardPosition() const { return gs->map->guardingCreaturePositions[cp->coord.x][cp->coord.y][cp->coord.z]; } -bool CPathfinder::isSourceGuarded() +bool CPathfinder::isSourceGuarded() const { //map can start with hero on guarded tile or teleport there using dimension door //so threat tile hero standing on like it's not guarded because it's should be possible to move out of here @@ -429,7 +429,7 @@ bool CPathfinder::isSourceGuarded() { //special case -> hero embarked a boat standing on a guarded tile -> we must allow to move away from that tile if(cp->accessible != CGPathNode::VISITABLE - || !cp->theNodeBefore->layer != ELayer::LAND + || cp->theNodeBefore->layer == ELayer::LAND || ct->topVisitableId() != Obj::BOAT) { return true; @@ -439,7 +439,7 @@ bool CPathfinder::isSourceGuarded() return false; } -bool CPathfinder::isDestinationGuarded(bool ignoreAccessibility) +bool CPathfinder::isDestinationGuarded(const bool ignoreAccessibility) const { if(gs->map->guardingCreaturePositions[dp->coord.x][dp->coord.y][dp->coord.z].valid() && (ignoreAccessibility || dp->accessible == CGPathNode::BLOCKVIS)) @@ -450,7 +450,7 @@ bool CPathfinder::isDestinationGuarded(bool ignoreAccessibility) return false; } -bool CPathfinder::isDestinationGuardian() +bool CPathfinder::isDestinationGuardian() const { return getSourceGuardPosition() == dp->coord; } diff --git a/lib/CPathfinder.h b/lib/CPathfinder.h index 0f748f4fe..e3946a6d9 100644 --- a/lib/CPathfinder.h +++ b/lib/CPathfinder.h @@ -149,15 +149,15 @@ private: void addNeighbours(const int3 &coord); void addTeleportExits(bool noTeleportExcludes = false); - bool isLayerTransitionPossible(); + bool isLayerTransitionPossible() const; bool isMovementToDestPossible(); - bool isMovementAfterDestPossible(); + bool isMovementAfterDestPossible() const; - bool isSourceInitialPosition(); - int3 getSourceGuardPosition(); - bool isSourceGuarded(); - bool isDestinationGuarded(bool ignoreAccessibility = true); - bool isDestinationGuardian(); + bool isSourceInitialPosition() const; + int3 getSourceGuardPosition() const; + bool isSourceGuarded() const; + bool isDestinationGuarded(const bool ignoreAccessibility = true) const; + bool isDestinationGuardian() const; void initializeGraph(); From 9cf35d1bfd8110502c4653697281c3b8ee6b5b49 Mon Sep 17 00:00:00 2001 From: ArseniyShestakov Date: Sun, 8 Nov 2015 10:06:24 +0300 Subject: [PATCH 050/168] CPathfinder: support for Castle Gate No support for client / server implemented yet. --- lib/CPathfinder.cpp | 21 ++++++++++++++++++++- lib/CPathfinder.h | 5 +++++ 2 files changed, 25 insertions(+), 1 deletion(-) diff --git a/lib/CPathfinder.cpp b/lib/CPathfinder.cpp index 427f12dfd..732261152 100644 --- a/lib/CPathfinder.cpp +++ b/lib/CPathfinder.cpp @@ -28,6 +28,8 @@ CPathfinder::PathfinderOptions::PathfinderOptions() useTeleportOneWayRandom = false; useTeleportWhirlpool = false; + useCastleGate = false; + lightweightFlyingMode = false; oneTurnSpecialLayersLimit = true; } @@ -253,6 +255,23 @@ void CPathfinder::addTeleportExits(bool noTeleportExcludes) neighbours.push_back(obj->visitablePos()); } } + + if(options.useCastleGate + && (sTileObj->ID == Obj::TOWN && sTileObj->subID == ETownType::INFERNO + && getPlayerRelations(hero->tempOwner, sTileObj->tempOwner) != PlayerRelations::ENEMIES)) + { + /// TODO: Find way to reuse CPlayerSpecificInfoCallback::getTownsInfo + /// This may be handy if we allow to use teleportation to friendly towns + auto towns = gs->getPlayer(hero->tempOwner)->towns; + for(const auto & town : towns) + { + if(town->id != sTileObj->id && town->visitingHero == nullptr + && town->hasBuilt(BuildingID::CASTLE_GATE, ETownType::INFERNO)) + { + neighbours.push_back(town->visitablePos()); + } + } + } } bool CPathfinder::isLayerTransitionPossible() const @@ -364,7 +383,7 @@ bool CPathfinder::isMovementToDestPossible() } else if(isDestinationGuardian()) destAction = CGPathNode::BATTLE; - else if(obj->blockVisit) + else if(obj->blockVisit && (!options.useCastleGate || obj->ID != Obj::TOWN)) destAction = CGPathNode::BLOCKING_VISIT; diff --git a/lib/CPathfinder.h b/lib/CPathfinder.h index e3946a6d9..e6c346dee 100644 --- a/lib/CPathfinder.h +++ b/lib/CPathfinder.h @@ -106,6 +106,11 @@ 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. From f376b2799941638fc3ac8f0ea4cb57da7d38ae12 Mon Sep 17 00:00:00 2001 From: ArseniyShestakov Date: Sun, 8 Nov 2015 19:19:48 +0300 Subject: [PATCH 051/168] Pathfinding: do path calculation in separate thread --- lib/CGameState.cpp | 5 ++++- lib/CGameState.h | 2 ++ lib/CPathfinder.cpp | 21 +++++++++++++++++++++ lib/CPathfinder.h | 1 + 4 files changed, 28 insertions(+), 1 deletion(-) diff --git a/lib/CGameState.cpp b/lib/CGameState.cpp index 3b4217ac5..575601fce 100644 --- a/lib/CGameState.cpp +++ b/lib/CGameState.cpp @@ -2164,8 +2164,11 @@ void CGameState::apply(CPack *pack) void CGameState::calculatePaths(const CGHeroInstance *hero, CPathsInfo &out) { + if(pathfinderWorking) + pathfinderWorking->interrupt(); + CPathfinder pathfinder(out, this, hero); - pathfinder.calculatePaths(); + pathfinderWorking = make_unique(&CPathfinder::startPathfinder, pathfinder); } /** diff --git a/lib/CGameState.h b/lib/CGameState.h index 6702616bf..d0cd983ab 100644 --- a/lib/CGameState.h +++ b/lib/CGameState.h @@ -314,6 +314,8 @@ public: boost::shared_mutex *mx; + unique_ptr pathfinderWorking; + void giveHeroArtifact(CGHeroInstance *h, ArtifactID aid); void apply(CPack *pack); diff --git a/lib/CPathfinder.cpp b/lib/CPathfinder.cpp index 732261152..6fa558eaf 100644 --- a/lib/CPathfinder.cpp +++ b/lib/CPathfinder.cpp @@ -7,6 +7,7 @@ #include "mapObjects/CGHeroInstance.h" #include "GameConstants.h" #include "CStopWatch.h" +#include "CThreadHelper.h" /* * CPathfinder.cpp, part of VCMI engine @@ -59,6 +60,24 @@ CPathfinder::CPathfinder(CPathsInfo &_out, CGameState *_gs, const CGHeroInstance neighbours.reserve(16); } +void CPathfinder::startPathfinder() +{ + try + { + setThreadName("CPathfinder::startPathfinder"); + + calculatePaths(); + } + catch(boost::thread_interrupted &e) + { + gs->pathfinderWorking.reset(); + return; + } + + gs->pathfinderWorking.reset(); +} + + void CPathfinder::calculatePaths() { int maxMovePointsLand = hero->maxMovePoints(true); @@ -192,6 +211,8 @@ void CPathfinder::calculatePaths() } } } + + boost::this_thread::interruption_point(); } //queue loop } diff --git a/lib/CPathfinder.h b/lib/CPathfinder.h index e6c346dee..0238110cb 100644 --- a/lib/CPathfinder.h +++ b/lib/CPathfinder.h @@ -91,6 +91,7 @@ class CPathfinder : private CGameInfoCallback { public: CPathfinder(CPathsInfo &_out, CGameState *_gs, const CGHeroInstance *_hero); + void startPathfinder(); 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: From a49950e958d896c02f20cb2332ee748a1a0ca356 Mon Sep 17 00:00:00 2001 From: ArseniyShestakov Date: Sun, 8 Nov 2015 22:16:58 +0300 Subject: [PATCH 052/168] Fix 2292. Fix chat on adventure map, increase maxOutputPerLine --- client/CMT.cpp | 10 ++++++---- client/NetPacksClient.cpp | 6 +++--- client/windows/CAdvmapInterface.cpp | 4 ++-- config/resolutions.json | 2 +- 4 files changed, 12 insertions(+), 10 deletions(-) diff --git a/client/CMT.cpp b/client/CMT.cpp index 318c60c9d..53d154ffb 100644 --- a/client/CMT.cpp +++ b/client/CMT.cpp @@ -529,8 +529,9 @@ void processCommand(const std::string &message) std::string cn; //command name readed >> cn; - if(LOCPLINT && LOCPLINT->cingconsole) - LOCPLINT->cingconsole->print(message); +// Check mantis issue 2292 for details +// if(LOCPLINT && LOCPLINT->cingconsole) +// LOCPLINT->cingconsole->print(message); if(ermInteractiveMode) { @@ -789,11 +790,12 @@ void processCommand(const std::string &message) Settings session = settings.write["session"]; session["autoSkip"].Bool() = !session["autoSkip"].Bool(); } - else if(client && client->serv && client->serv->connected && LOCPLINT) //send to server + // Check mantis issue 2292 for details +/* else if(client && client->serv && client->serv->connected && LOCPLINT) //send to server { boost::unique_lock un(*LOCPLINT->pim); LOCPLINT->cb->sendMessage(message); - } + }*/ } //plays intro, ends when intro is over or button has been pressed (handles events) diff --git a/client/NetPacksClient.cpp b/client/NetPacksClient.cpp index 5453fbc22..eb95d44b9 100644 --- a/client/NetPacksClient.cpp +++ b/client/NetPacksClient.cpp @@ -819,10 +819,10 @@ void SaveGame::applyCl(CClient *cl) void PlayerMessage::applyCl(CClient *cl) { - std::ostringstream str; - str << "Player "<< player <<" sends a message: " << text; + logNetwork->debugStream() << "Player "<< player <<" sends a message: " << text; - logNetwork->debugStream() << str.str(); + std::ostringstream str; + str << cl->getPlayer(player)->nodeName() <<": " << text; if(LOCPLINT) LOCPLINT->cingconsole->print(str.str()); } diff --git a/client/windows/CAdvmapInterface.cpp b/client/windows/CAdvmapInterface.cpp index e1b96e049..2f9958a9c 100644 --- a/client/windows/CAdvmapInterface.cpp +++ b/client/windows/CAdvmapInterface.cpp @@ -881,7 +881,7 @@ void CAdvMapInt::showAll(SDL_Surface * to) statusbar.show(to); - LOCPLINT->cingconsole->showAll(to); + LOCPLINT->cingconsole->show(to); } bool CAdvMapInt::isHeroSleeping(const CGHeroInstance *hero) @@ -958,7 +958,7 @@ void CAdvMapInt::show(SDL_Surface * to) for(int i=0;i<4;i++) blitAt(gems[i]->ourImages[LOCPLINT->playerID.getNum()].bitmap,ADVOPT.gemX[i],ADVOPT.gemY[i],to); updateScreen=false; - LOCPLINT->cingconsole->showAll(to); + LOCPLINT->cingconsole->show(to); } else if (terrain.needsAnimUpdate()) { diff --git a/config/resolutions.json b/config/resolutions.json index 1917a62da..e0e83be0b 100644 --- a/config/resolutions.json +++ b/config/resolutions.json @@ -3,7 +3,7 @@ [ { "resolution": { "x": 800, "y": 600 }, - "InGameConsole": { "maxInputPerLine": 60, "maxOutputPerLine": 39 }, + "InGameConsole": { "maxInputPerLine": 60, "maxOutputPerLine": 60 }, "AdvMap": { "x": 7, "y": 7, "width": 594, "height": 546, "smoothMove": 1, "puzzleSepia": 1, "objectFading" : 1, "screenFading" : 1 }, "InfoBox": { "x": 605, "y": 389 }, "gem0": { "x": 6, "y": 508, "graphic": "agemLL.def" }, From 866a0a1fc021f8131796dbaa9865b63bcd66d68f Mon Sep 17 00:00:00 2001 From: ArseniyShestakov Date: Sun, 8 Nov 2015 23:02:59 +0300 Subject: [PATCH 053/168] VCAI: little improvement for 2a59cb619137cb443adce6aba009ecf37a6a57da --- AI/VCAI/VCAI.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/AI/VCAI/VCAI.cpp b/AI/VCAI/VCAI.cpp index dfa906580..399d1a5ac 100644 --- a/AI/VCAI/VCAI.cpp +++ b/AI/VCAI/VCAI.cpp @@ -3372,7 +3372,8 @@ int3 SectorMap::findFirstVisitableTile (HeroPtr h, crint3 dst) while(curtile != h->visitablePos()) { auto topObj = cb->getTopObj(curtile); - if (topObj && topObj->ID == Obj::HERO && h->tempOwner == topObj->tempOwner && topObj != h.h) + if(topObj && topObj->ID == Obj::HERO && topObj != h.h && + cb->getPlayerRelations(h->tempOwner, topObj->tempOwner) != PlayerRelations::ENEMIES) { logAi->warnStream() << ("Another allied hero stands in our way"); return ret; From 74198a537b2dddac062d5bcaf167da87cfa0a087 Mon Sep 17 00:00:00 2001 From: ArseniyShestakov Date: Mon, 9 Nov 2015 00:54:59 +0300 Subject: [PATCH 054/168] CGameHandler::setOwner: fix InfoWindow text on last town loss --- server/CGameHandler.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/server/CGameHandler.cpp b/server/CGameHandler.cpp index 88f43533e..fb649b1ac 100644 --- a/server/CGameHandler.cpp +++ b/server/CGameHandler.cpp @@ -1961,7 +1961,8 @@ void CGameHandler::setOwner(const CGObjectInstance * obj, PlayerColor owner) { InfoWindow iw; iw.player = oldOwner; - iw.text.addTxt (MetaString::GENERAL_TXT, 6); //%s, you have lost your last town. If you do not conquer another town in the next week, you will be eliminated. + iw.text.addTxt(MetaString::GENERAL_TXT, 6); //%s, you have lost your last town. If you do not conquer another town in the next week, you will be eliminated. + iw.text.addReplacement(MetaString::COLOR, oldOwner.getNum()); sendAndApply(&iw); } } From 45e4cf354ef1d5a7fc2a5468db2cccdc6612356f Mon Sep 17 00:00:00 2001 From: ArseniyShestakov Date: Mon, 9 Nov 2015 09:35:56 +0300 Subject: [PATCH 055/168] Revert "Pathfinding: do path calculation in separate thread" This reverts commit f376b2799941638fc3ac8f0ea4cb57da7d38ae12. --- lib/CGameState.cpp | 5 +---- lib/CGameState.h | 2 -- lib/CPathfinder.cpp | 21 --------------------- lib/CPathfinder.h | 1 - 4 files changed, 1 insertion(+), 28 deletions(-) diff --git a/lib/CGameState.cpp b/lib/CGameState.cpp index 575601fce..3b4217ac5 100644 --- a/lib/CGameState.cpp +++ b/lib/CGameState.cpp @@ -2164,11 +2164,8 @@ void CGameState::apply(CPack *pack) void CGameState::calculatePaths(const CGHeroInstance *hero, CPathsInfo &out) { - if(pathfinderWorking) - pathfinderWorking->interrupt(); - CPathfinder pathfinder(out, this, hero); - pathfinderWorking = make_unique(&CPathfinder::startPathfinder, pathfinder); + pathfinder.calculatePaths(); } /** diff --git a/lib/CGameState.h b/lib/CGameState.h index d0cd983ab..6702616bf 100644 --- a/lib/CGameState.h +++ b/lib/CGameState.h @@ -314,8 +314,6 @@ public: boost::shared_mutex *mx; - unique_ptr pathfinderWorking; - void giveHeroArtifact(CGHeroInstance *h, ArtifactID aid); void apply(CPack *pack); diff --git a/lib/CPathfinder.cpp b/lib/CPathfinder.cpp index 6fa558eaf..732261152 100644 --- a/lib/CPathfinder.cpp +++ b/lib/CPathfinder.cpp @@ -7,7 +7,6 @@ #include "mapObjects/CGHeroInstance.h" #include "GameConstants.h" #include "CStopWatch.h" -#include "CThreadHelper.h" /* * CPathfinder.cpp, part of VCMI engine @@ -60,24 +59,6 @@ CPathfinder::CPathfinder(CPathsInfo &_out, CGameState *_gs, const CGHeroInstance neighbours.reserve(16); } -void CPathfinder::startPathfinder() -{ - try - { - setThreadName("CPathfinder::startPathfinder"); - - calculatePaths(); - } - catch(boost::thread_interrupted &e) - { - gs->pathfinderWorking.reset(); - return; - } - - gs->pathfinderWorking.reset(); -} - - void CPathfinder::calculatePaths() { int maxMovePointsLand = hero->maxMovePoints(true); @@ -211,8 +192,6 @@ void CPathfinder::calculatePaths() } } } - - boost::this_thread::interruption_point(); } //queue loop } diff --git a/lib/CPathfinder.h b/lib/CPathfinder.h index 0238110cb..e6c346dee 100644 --- a/lib/CPathfinder.h +++ b/lib/CPathfinder.h @@ -91,7 +91,6 @@ class CPathfinder : private CGameInfoCallback { public: CPathfinder(CPathsInfo &_out, CGameState *_gs, const CGHeroInstance *_hero); - void startPathfinder(); 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: From 5bfbd8526bfd7b68ac518d1dca9255a70aa99fc1 Mon Sep 17 00:00:00 2001 From: ArseniyShestakov Date: Mon, 9 Nov 2015 14:18:36 +0300 Subject: [PATCH 056/168] Bonus system: fix N_DAYS bonus duration --- lib/HeroBonus.cpp | 18 ++++++++++++++++++ lib/HeroBonus.h | 1 + lib/NetPacksLib.cpp | 1 + 3 files changed, 20 insertions(+) diff --git a/lib/HeroBonus.cpp b/lib/HeroBonus.cpp index 0380d0144..765d6dc46 100644 --- a/lib/HeroBonus.cpp +++ b/lib/HeroBonus.cpp @@ -763,6 +763,24 @@ void CBonusSystemNode::popBonuses(const CSelector &s) child->popBonuses(s); } +void CBonusSystemNode::updateBonuses(const CSelector &s) +{ + BonusList bl; + exportedBonuses.getBonuses(bl, s); + for(Bonus *b : bl) + { + if(b->duration & Bonus::N_DAYS) + { + b->turnsRemain--; + if(b->turnsRemain <= 0) + removeBonus(b); + } + } + + for(CBonusSystemNode *child : children) + child->updateBonuses(s); +} + void CBonusSystemNode::addNewBonus(Bonus *b) { assert(!vstd::contains(exportedBonuses,b)); diff --git a/lib/HeroBonus.h b/lib/HeroBonus.h index 75f1b956c..f930f63e0 100644 --- a/lib/HeroBonus.h +++ b/lib/HeroBonus.h @@ -686,6 +686,7 @@ public: //bool isLimitedOnUs(Bonus *b) const; //if bonus should be removed from list acquired from this node void popBonuses(const CSelector &s); + void updateBonuses(const CSelector &s); virtual std::string bonusToString(const Bonus *bonus, bool description) const {return "";}; //description or bonus name virtual std::string nodeName() const; diff --git a/lib/NetPacksLib.cpp b/lib/NetPacksLib.cpp index ede41bd8b..a298b0641 100644 --- a/lib/NetPacksLib.cpp +++ b/lib/NetPacksLib.cpp @@ -1026,6 +1026,7 @@ DLL_LINKAGE void NewTurn::applyGs( CGameState *gs ) if(gs->getDate(Date::DAY_OF_WEEK) == 1) //new week gs->globalEffects.popBonuses(Bonus::OneWeek); //works for children -> all game objs + gs->globalEffects.updateBonuses(Bonus::NDays); //TODO not really a single root hierarchy, what about bonuses placed elsewhere? [not an issue with H3 mechanics but in the future...] for(CGTownInstance* t : gs->map->towns) From ee1c5e452f39bfe227c7aaffc5c5c0375877b5ba Mon Sep 17 00:00:00 2001 From: ArseniyShestakov Date: Mon, 9 Nov 2015 15:01:58 +0300 Subject: [PATCH 057/168] Bonus system: add CWillLastDays selector --- lib/HeroBonus.cpp | 1 + lib/HeroBonus.h | 26 ++++++++++++++++++++++++++ 2 files changed, 27 insertions(+) diff --git a/lib/HeroBonus.cpp b/lib/HeroBonus.cpp index 765d6dc46..e9ac989a8 100644 --- a/lib/HeroBonus.cpp +++ b/lib/HeroBonus.cpp @@ -1201,6 +1201,7 @@ namespace Selector DLL_LINKAGE CSelectFieldEqual sourceType(&Bonus::source); DLL_LINKAGE CSelectFieldEqual effectRange(&Bonus::effectRange); DLL_LINKAGE CWillLastTurns turns; + DLL_LINKAGE CWillLastDays days; DLL_LINKAGE CSelectFieldAny anyRange(&Bonus::effectRange); CSelector DLL_LINKAGE typeSubtype(Bonus::BonusType Type, TBonusSubtype Subtype) diff --git a/lib/HeroBonus.h b/lib/HeroBonus.h index f930f63e0..188442ff9 100644 --- a/lib/HeroBonus.h +++ b/lib/HeroBonus.h @@ -821,6 +821,31 @@ public: } }; +class DLL_LINKAGE CWillLastDays +{ +public: + int daysRequested; + + bool operator()(const Bonus *bonus) const + { + if(daysRequested <= 0) + return true; + else if(bonus->duration & Bonus::ONE_DAY) + return false; + else if(bonus->duration & Bonus::N_DAYS) + { + return bonus->turnsRemain > daysRequested; + } + + return false; // TODO: ONE_WEEK need support for turnsRemain, but for now we'll exclude all unhandled durations + } + CWillLastDays& operator()(const int &setVal) + { + daysRequested = setVal; + return *this; + } +}; + //Stores multiple limiters. If any of them fails -> bonus is dropped. class DLL_LINKAGE LimiterList : public ILimiter { @@ -959,6 +984,7 @@ namespace Selector extern DLL_LINKAGE CSelectFieldEqual sourceType; extern DLL_LINKAGE CSelectFieldEqual effectRange; extern DLL_LINKAGE CWillLastTurns turns; + extern DLL_LINKAGE CWillLastDays days; extern DLL_LINKAGE CSelectFieldAny anyRange; CSelector DLL_LINKAGE typeSubtype(Bonus::BonusType Type, TBonusSubtype Subtype); From 7f0b0bd1b5daddbb4809ab39ff7340ee7d29a37d Mon Sep 17 00:00:00 2001 From: ArseniyShestakov Date: Mon, 9 Nov 2015 15:48:07 +0300 Subject: [PATCH 058/168] Bonus system: use updateBonuses in battleTurnPasseed --- lib/HeroBonus.cpp | 22 ++++------------------ 1 file changed, 4 insertions(+), 18 deletions(-) diff --git a/lib/HeroBonus.cpp b/lib/HeroBonus.cpp index e9ac989a8..9b89111db 100644 --- a/lib/HeroBonus.cpp +++ b/lib/HeroBonus.cpp @@ -769,12 +769,9 @@ void CBonusSystemNode::updateBonuses(const CSelector &s) exportedBonuses.getBonuses(bl, s); for(Bonus *b : bl) { - if(b->duration & Bonus::N_DAYS) - { - b->turnsRemain--; - if(b->turnsRemain <= 0) - removeBonus(b); - } + b->turnsRemain--; + if(b->turnsRemain <= 0) + removeBonus(b); } for(CBonusSystemNode *child : children) @@ -968,18 +965,7 @@ void CBonusSystemNode::getRedDescendants(TNodes &out) void CBonusSystemNode::battleTurnPassed() { - BonusList bonusesCpy = exportedBonuses; //copy, because removing bonuses invalidates iters - for (auto & elem : bonusesCpy) - { - Bonus *b = elem; - - if(b->duration & Bonus::N_TURNS) - { - b->turnsRemain--; - if(b->turnsRemain <= 0) - removeBonus(b); - } - } + updateBonuses(Bonus::NTurns); } void CBonusSystemNode::exportBonus(Bonus * b) From 96897acc46d51f7e4b9c37ec708aba41975ece25 Mon Sep 17 00:00:00 2001 From: ArseniyShestakov Date: Mon, 9 Nov 2015 19:05:22 +0300 Subject: [PATCH 059/168] CWillLastDays: add condition for permanent durations Accidentally missed them in pull request --- lib/HeroBonus.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lib/HeroBonus.h b/lib/HeroBonus.h index 188442ff9..a95e2c9b6 100644 --- a/lib/HeroBonus.h +++ b/lib/HeroBonus.h @@ -832,6 +832,8 @@ public: return true; else if(bonus->duration & Bonus::ONE_DAY) return false; + else if(bonus->duration & Bonus::PERMANENT || bonus->duration & Bonus::ONE_BATTLE) + return true; else if(bonus->duration & Bonus::N_DAYS) { return bonus->turnsRemain > daysRequested; From d3c8ca7c1c3e2c526a3bf02f37a80ca989b49fca Mon Sep 17 00:00:00 2001 From: ArseniyShestakov Date: Mon, 9 Nov 2015 19:57:26 +0300 Subject: [PATCH 060/168] Pathfinding: implement duration checking for fly and water walking Now pathfinder take into account different bonuses for different tuns. So if you only have FLYING_MOVEMENT bonus from Fly spell for one turn then pathfinder will only let you use air layer within one turn only. That work for cost calculations too. Let's say you have two bonuses: - FLYING_MOVEMENT with 20% penalty for next 2 turns - FLYING_MOVEMENT with 40% penalty for 5 turns Now pathfinder using correct penalty for each turn so movements in air layer going to be more expensive on 3-5 turns. --- lib/CGameState.cpp | 16 +++++++++------- lib/CGameState.h | 2 +- lib/CPathfinder.cpp | 26 ++++++++++++++++++++++++-- lib/CPathfinder.h | 1 + lib/mapObjects/CGHeroInstance.cpp | 13 ++++--------- lib/mapObjects/CGHeroInstance.h | 5 ++--- server/CGameHandler.cpp | 10 ++++++---- 7 files changed, 47 insertions(+), 26 deletions(-) diff --git a/lib/CGameState.cpp b/lib/CGameState.cpp index 57bb16597..db3bd9fbd 100644 --- a/lib/CGameState.cpp +++ b/lib/CGameState.cpp @@ -2100,7 +2100,7 @@ void CGameState::getNeighbours(const TerrainTile &srct, int3 tile, std::vectorgetTile(dest); //get basic cost - int ret = h->getTileCost(d,s); + int ret = h->getTileCost(d, s, turn); - if(d.blocked && h->canFly()) + auto flyBonus = h->getBonusAtTurn(Bonus::FLYING_MOVEMENT, turn); + auto waterWalkingBonus = h->getBonusAtTurn(Bonus::WATER_WALKING, turn); + if(d.blocked && flyBonus) { - ret *= (100.0 + h->valOfBonuses(Bonus::FLYING_MOVEMENT)) / 100.0; + ret *= (100.0 + flyBonus->val) / 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()) + else if(!h->boat && waterWalkingBonus) { - ret *= (100.0 + h->valOfBonuses(Bonus::WATER_WALKING)) / 100.0; + ret *= (100.0 + waterWalkingBonus->val) / 100.0; } } @@ -2145,7 +2147,7 @@ int CGameState::getMovementCost(const CGHeroInstance *h, const int3 &src, const getNeighbours(d, dest, vec, s.terType != ETerrainType::WATER, true); for(auto & elem : vec) { - int fcost = getMovementCost(h, dest, elem, left, false); + int fcost = getMovementCost(h, dest, elem, left, turn, false); if(fcost <= left) { return ret; diff --git a/lib/CGameState.h b/lib/CGameState.h index 6702616bf..6f03191a0 100644 --- a/lib/CGameState.h +++ b/lib/CGameState.h @@ -340,7 +340,7 @@ public: bool isVisible(const CGObjectInstance *obj, boost::optional player); void getNeighbours(const TerrainTile &srct, int3 tile, std::vector &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 getMovementCost(const CGHeroInstance * h, const int3 &src, const int3 &dest, int remainingMovePoints =- 1, const int &turn = 0, 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 ----- diff --git a/lib/CPathfinder.cpp b/lib/CPathfinder.cpp index 732261152..9f8a1b4a1 100644 --- a/lib/CPathfinder.cpp +++ b/lib/CPathfinder.cpp @@ -48,9 +48,9 @@ CPathfinder::CPathfinder(CPathsInfo &_out, CGameState *_gs, const CGHeroInstance throw std::runtime_error("Wrong checksum"); } - if(hero->canFly()) + if(hero->getBonusAtTurn(Bonus::FLYING_MOVEMENT)) options.useFlying = true; - if(hero->canWalkOnSea()) + if(hero->getBonusAtTurn(Bonus::WATER_WALKING)) options.useWaterWalking = true; if(CGWhirlpool::isProtected(hero)) options.useTeleportWhirlpool = true; @@ -132,6 +132,9 @@ void CPathfinder::calculatePaths() if(!passOneTurnLimitCheck(cp->turns != turn)) continue; + if(!isLayerAvailable(i, turn)) + continue; + if(cp->layer != i && !isLayerTransitionPossible()) continue; @@ -274,6 +277,25 @@ void CPathfinder::addTeleportExits(bool noTeleportExcludes) } } +bool CPathfinder::isLayerAvailable(const ELayer &layer, const int &turn) const +{ + switch(layer) + { + case ELayer::AIR: + if(!hero->getBonusAtTurn(Bonus::FLYING_MOVEMENT, turn)) + return false; + + break; + + case ELayer::WATER: + if(!hero->getBonusAtTurn(Bonus::WATER_WALKING, turn)) + return false; + break; + } + + return true; +} + bool CPathfinder::isLayerTransitionPossible() const { if((cp->layer == ELayer::AIR || cp->layer == ELayer::WATER) diff --git a/lib/CPathfinder.h b/lib/CPathfinder.h index e6c346dee..06b4d993e 100644 --- a/lib/CPathfinder.h +++ b/lib/CPathfinder.h @@ -154,6 +154,7 @@ private: void addNeighbours(const int3 &coord); void addTeleportExits(bool noTeleportExcludes = false); + bool isLayerAvailable(const ELayer &layer, const int &turn) const; bool isLayerTransitionPossible() const; bool isMovementToDestPossible(); bool isMovementAfterDestPossible() const; diff --git a/lib/mapObjects/CGHeroInstance.cpp b/lib/mapObjects/CGHeroInstance.cpp index c50055bcc..71bcf089e 100644 --- a/lib/mapObjects/CGHeroInstance.cpp +++ b/lib/mapObjects/CGHeroInstance.cpp @@ -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 int &turn) const { unsigned ret = GameConstants::BASE_MOVEMENT_COST; @@ -80,7 +80,7 @@ ui32 CGHeroInstance::getTileCost(const TerrainTile &dest, const TerrainTile &fro break; } } - else if(!hasBonusOfType(Bonus::NO_TERRAIN_PENALTY, from.terType)) + else if(!getBonusAtTurn(Bonus::NO_TERRAIN_PENALTY, turn, 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. @@ -129,14 +129,9 @@ int3 CGHeroInstance::getPosition(bool h3m) const //h3m=true - returns position o } } -bool CGHeroInstance::canFly() const +const Bonus * CGHeroInstance::getBonusAtTurn(const Bonus::BonusType &type, const int &turn, const TBonusSubtype &subType) const { - return hasBonusOfType(Bonus::FLYING_MOVEMENT); -} - -bool CGHeroInstance::canWalkOnSea() const -{ - return hasBonusOfType(Bonus::WATER_WALKING); + return getBonus(Selector::type(type).And(Selector::days(turn)).And(Selector::subtype(subType))); } ui8 CGHeroInstance::getSecSkillLevel(SecondarySkill skill) const diff --git a/lib/mapObjects/CGHeroInstance.h b/lib/mapObjects/CGHeroInstance.h index c8338fb79..d05b035df 100644 --- a/lib/mapObjects/CGHeroInstance.h +++ b/lib/mapObjects/CGHeroInstance.h @@ -129,12 +129,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 int &turn) const; //move cost - applying pathfinding skill, road and terrain modifiers. NOT includes diagonal move penalty, last move levelling 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; + const Bonus * getBonusAtTurn(const Bonus::BonusType &type, const int &turn = 0, const TBonusSubtype &subType = -1) 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 diff --git a/server/CGameHandler.cpp b/server/CGameHandler.cpp index 8971a4c8d..ea2ca3310 100644 --- a/server/CGameHandler.cpp +++ b/server/CGameHandler.cpp @@ -1779,12 +1779,14 @@ bool CGameHandler::moveHero( ObjectInstanceID hid, int3 dst, ui8 teleporting, bo tmh.movePoints = h->movement; //check if destination tile is available + bool canFly = h->getBonusAtTurn(Bonus::FLYING_MOVEMENT); + bool canWalkOnSea = h->getBonusAtTurn(Bonus::WATER_WALKING); //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->canFly() )) + if(((t.terType == ETerrainType::ROCK || (t.blocked && !t.visitable && !canFly)) && complain("Cannot move hero, destination tile is blocked!")) - || ((!h->boat && !h->canWalkOnSea() && !h->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) + || ((!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,7 +1796,7 @@ 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 && !h->canFly() && !CGTeleport::isTeleport(t.topVisitableObj())) + || ((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"))*/) @@ -1913,7 +1915,7 @@ bool CGameHandler::moveHero( ObjectInstanceID hid, int3 dst, ui8 teleporting, bo if(CGTeleport::isTeleport(t.topVisitableObj())) visitDest = DONT_VISIT_DEST; - if(h->canFly()) + if(canFly) { lookForGuards = IGNORE_GUARDS; visitDest = DONT_VISIT_DEST; From b2e1ee53634de5ed0a3ba07d11b67b30d552c13e Mon Sep 17 00:00:00 2001 From: ArseniyShestakov Date: Tue, 10 Nov 2015 02:15:27 +0300 Subject: [PATCH 061/168] CGameState: move two pathfinding-related functions to CPathfinderHelper Both getMovementCost and getNeighbours have nothing to do with gamestate. --- AI/VCAI/Fuzzy.cpp | 4 +- CCallback.cpp | 5 -- CCallback.h | 1 - lib/CGameState.cpp | 97 -------------------------------------- lib/CGameState.h | 2 - lib/CPathfinder.cpp | 100 ++++++++++++++++++++++++++++++++++++++-- lib/CPathfinder.h | 9 ++++ server/CGameHandler.cpp | 6 +-- 8 files changed, 111 insertions(+), 113 deletions(-) diff --git a/AI/VCAI/Fuzzy.cpp b/AI/VCAI/Fuzzy.cpp index f91b61963..04cf3d22a 100644 --- a/AI/VCAI/Fuzzy.cpp +++ b/AI/VCAI/Fuzzy.cpp @@ -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::getCost(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 -} \ No newline at end of file +} diff --git a/CCallback.cpp b/CCallback.cpp index 605dd2afa..e48197d63 100644 --- a/CCallback.cpp +++ b/CCallback.cpp @@ -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); diff --git a/CCallback.h b/CCallback.h index 73167a1b3..46afc2adf 100644 --- a/CCallback.h +++ b/CCallback.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); diff --git a/lib/CGameState.cpp b/lib/CGameState.cpp index db3bd9fbd..411628370 100644 --- a/lib/CGameState.cpp +++ b/lib/CGameState.cpp @@ -2061,103 +2061,6 @@ PlayerRelations::PlayerRelations CGameState::getPlayerRelations( PlayerColor col return PlayerRelations::ENEMIES; } -void CGameState::getNeighbours(const TerrainTile &srct, int3 tile, std::vector &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, const int &turn, 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, turn); - - auto flyBonus = h->getBonusAtTurn(Bonus::FLYING_MOVEMENT, turn); - auto waterWalkingBonus = h->getBonusAtTurn(Bonus::WATER_WALKING, turn); - if(d.blocked && flyBonus) - { - ret *= (100.0 + flyBonus->val) / 100.0; - } - else if(d.terType == ETerrainType::WATER) - { - if(h->boat && s.hasFavourableWinds() && d.hasFavourableWinds()) //Favourable Winds - ret *= 0.666; - else if(!h->boat && waterWalkingBonus) - { - ret *= (100.0 + waterWalkingBonus->val) / 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 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, turn, false); - if(fcost <= left) - { - return ret; - } - } - ret = remainingMovePoints; - } - return ret; -} - void CGameState::apply(CPack *pack) { ui16 typ = typeList.getTypeID(pack); diff --git a/lib/CGameState.h b/lib/CGameState.h index 6f03191a0..c60a9aa34 100644 --- a/lib/CGameState.h +++ b/lib/CGameState.h @@ -339,8 +339,6 @@ public: bool isVisible(int3 pos, PlayerColor player); bool isVisible(const CGObjectInstance *obj, boost::optional player); - void getNeighbours(const TerrainTile &srct, int3 tile, std::vector &vec, const boost::logic::tribool &onLand, bool limitCoastSailing); - int getMovementCost(const CGHeroInstance * h, const int3 &src, const int3 &dest, int remainingMovePoints =- 1, const int &turn = 0, 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 ----- diff --git a/lib/CPathfinder.cpp b/lib/CPathfinder.cpp index 9f8a1b4a1..b62c8b858 100644 --- a/lib/CPathfinder.cpp +++ b/lib/CPathfinder.cpp @@ -143,7 +143,7 @@ void CPathfinder::calculatePaths() if(!isMovementToDestPossible()) continue; - int cost = gs->getMovementCost(hero, cp->coord, dp->coord, movement); + int cost = CPathfinderHelper::getCost(hero, cp->coord, dp->coord, movement); int remains = movement - cost; if(destAction == CGPathNode::EMBARK || destAction == CGPathNode::DISEMBARK) { @@ -156,7 +156,7 @@ void CPathfinder::calculatePaths() //occurs rarely, when hero with low movepoints tries to leave the road turnAtNextTile++; int moveAtNextTile = maxMovePoints(cp); - cost = gs->getMovementCost(hero, cp->coord, dp->coord, moveAtNextTile); //cost must be updated, movement points changed :( + cost = CPathfinderHelper::getCost(hero, cp->coord, dp->coord, moveAtNextTile); //cost must be updated, movement points changed :( remains = moveAtNextTile - cost; } @@ -204,7 +204,7 @@ void CPathfinder::addNeighbours(const int3 &coord) ct = &gs->map->getTile(coord); std::vector tiles; - gs->getNeighbours(*ct, coord, tiles, boost::logic::indeterminate, cp->layer == ELayer::SAIL); // TODO: find out if we still need "limitCoastSailing" option + CPathfinderHelper::getNeighbours(gs, *ct, coord, tiles, boost::logic::indeterminate, cp->layer == ELayer::SAIL); // TODO: find out if we still need "limitCoastSailing" option sTileObj = ct->topVisitableObj(coord == out.hpos); if(canVisitObject()) { @@ -625,6 +625,100 @@ bool CPathfinder::canVisitObject() const return cp->layer == ELayer::LAND || cp->layer == ELayer::SAIL; } +void CPathfinderHelper::getNeighbours(CGameState * gs, const TerrainTile &srct, const int3 &tile, std::vector &vec, const boost::logic::tribool &onLand, const 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(!gs->isInTheMap(hlp)) + continue; + + const TerrainTile &hlpt = gs->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(gs->map->getTile(hlp1).terType != ETerrainType::WATER || gs->map->getTile(hlp2).terType != ETerrainType::WATER) + continue; + } + + if((indeterminate(onLand) || onLand == (hlpt.terType!=ETerrainType::WATER) ) + && hlpt.terType != ETerrainType::ROCK) + { + vec.push_back(hlp); + } + } +} + +int CPathfinderHelper::getCost(const CGHeroInstance * h, const int3 &src, const int3 &dst, const int &remainingMovePoints, const int &turn, const bool &checkLast) +{ + if(src == dst) //same tile + return 0; + + auto s = h->cb->getTile(src), d = h->cb->getTile(dst); + int ret = h->getTileCost(*d, *s, turn); + + auto flyBonus = h->getBonusAtTurn(Bonus::FLYING_MOVEMENT, turn); + auto waterWalkingBonus = h->getBonusAtTurn(Bonus::WATER_WALKING, turn); + if(d->blocked && flyBonus) + { + ret *= (100.0 + flyBonus->val) / 100.0; + } + else if(d->terType == ETerrainType::WATER) + { + if(h->boat && s->hasFavourableWinds() && d->hasFavourableWinds()) //Favourable Winds + ret *= 0.666; + else if(!h->boat && waterWalkingBonus) + { + ret *= (100.0 + waterWalkingBonus->val) / 100.0; + } + } + + if(src.x != dst.x && src.y != dst.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 vec; + vec.reserve(8); //optimization + getNeighbours(h->cb->gameState(), *d, dst, vec, s->terType != ETerrainType::WATER, true); + for(auto & elem : vec) + { + int fcost = getCost(h, dst, elem, left, turn, false); + if(fcost <= left) + return ret; + } + ret = remainingMovePoints; + } + return ret; +} + +int CPathfinderHelper::getCost(const CGHeroInstance * h, const int3 &dst) +{ + return getCost(h, h->visitablePos(), dst, h->movement); +} + CGPathNode::CGPathNode(int3 Coord, ELayer Layer) : coord(Coord), layer(Layer) { diff --git a/lib/CPathfinder.h b/lib/CPathfinder.h index 06b4d993e..cbf0c75dd 100644 --- a/lib/CPathfinder.h +++ b/lib/CPathfinder.h @@ -178,3 +178,12 @@ private: bool canVisitObject() const; }; + +class DLL_LINKAGE CPathfinderHelper +{ +public: + static void getNeighbours(CGameState * gs, const TerrainTile &srct, const int3 &tile, std::vector &vec, const boost::logic::tribool &onLand, const bool &limitCoastSailing); + + static int getCost(const CGHeroInstance * h, const int3 &src, const int3 &dst, const int &remainingMovePoints =- 1, const int &turn = 0, const bool &checkLast = true); + static int getCost(const CGHeroInstance * h, const int3 &dst); +}; diff --git a/server/CGameHandler.cpp b/server/CGameHandler.cpp index ea2ca3310..008f72496 100644 --- a/server/CGameHandler.cpp +++ b/server/CGameHandler.cpp @@ -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,8 +1778,9 @@ bool CGameHandler::moveHero( ObjectInstanceID hid, int3 dst, ui8 teleporting, bo tmh.movePoints = h->movement; //check if destination tile is available - bool canFly = h->getBonusAtTurn(Bonus::FLYING_MOVEMENT); - bool canWalkOnSea = h->getBonusAtTurn(Bonus::WATER_WALKING); + const bool canFly = h->getBonusAtTurn(Bonus::FLYING_MOVEMENT); + const bool canWalkOnSea = h->getBonusAtTurn(Bonus::WATER_WALKING); + const int cost = CPathfinderHelper::getCost(h, h->getPosition(), hmpos, h->movement); //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) From 2ef9d7c3ec6b2221cd8d15fe765a01a27223cb2a Mon Sep 17 00:00:00 2001 From: ArseniyShestakov Date: Tue, 10 Nov 2015 02:30:05 +0300 Subject: [PATCH 062/168] Rename getCost back to getMovementCost Initially wanter to name main class differently and back then getCost make sense. Then renamed class to CPathfinderHelper, but forgot to rename function back. --- AI/VCAI/Fuzzy.cpp | 2 +- lib/CPathfinder.cpp | 12 ++++++------ lib/CPathfinder.h | 4 ++-- server/CGameHandler.cpp | 2 +- 4 files changed, 10 insertions(+), 10 deletions(-) diff --git a/AI/VCAI/Fuzzy.cpp b/AI/VCAI/Fuzzy.cpp index 04cf3d22a..bc7b60cfe 100644 --- a/AI/VCAI/Fuzzy.cpp +++ b/AI/VCAI/Fuzzy.cpp @@ -421,7 +421,7 @@ float FuzzyHelper::evaluate (Goals::VisitTile & g) //assert(cb->isInTheMap(g.tile)); float turns = 0; - float distance = CPathfinderHelper::getCost(g.hero.h, g.tile); + float distance = CPathfinderHelper::getMovementCost(g.hero.h, g.tile); if (!distance) //we stand on that tile turns = 0; else diff --git a/lib/CPathfinder.cpp b/lib/CPathfinder.cpp index b62c8b858..e1150e886 100644 --- a/lib/CPathfinder.cpp +++ b/lib/CPathfinder.cpp @@ -143,7 +143,7 @@ void CPathfinder::calculatePaths() if(!isMovementToDestPossible()) continue; - int cost = CPathfinderHelper::getCost(hero, cp->coord, dp->coord, movement); + int cost = CPathfinderHelper::getMovementCost(hero, cp->coord, dp->coord, movement); int remains = movement - cost; if(destAction == CGPathNode::EMBARK || destAction == CGPathNode::DISEMBARK) { @@ -156,7 +156,7 @@ void CPathfinder::calculatePaths() //occurs rarely, when hero with low movepoints tries to leave the road turnAtNextTile++; int moveAtNextTile = maxMovePoints(cp); - cost = CPathfinderHelper::getCost(hero, cp->coord, dp->coord, moveAtNextTile); //cost must be updated, movement points changed :( + cost = CPathfinderHelper::getMovementCost(hero, cp->coord, dp->coord, moveAtNextTile); //cost must be updated, movement points changed :( remains = moveAtNextTile - cost; } @@ -664,7 +664,7 @@ void CPathfinderHelper::getNeighbours(CGameState * gs, const TerrainTile &srct, } } -int CPathfinderHelper::getCost(const CGHeroInstance * h, const int3 &src, const int3 &dst, const int &remainingMovePoints, const int &turn, const bool &checkLast) +int CPathfinderHelper::getMovementCost(const CGHeroInstance * h, const int3 &src, const int3 &dst, const int &remainingMovePoints, const int &turn, const bool &checkLast) { if(src == dst) //same tile return 0; @@ -705,7 +705,7 @@ int CPathfinderHelper::getCost(const CGHeroInstance * h, const int3 &src, const getNeighbours(h->cb->gameState(), *d, dst, vec, s->terType != ETerrainType::WATER, true); for(auto & elem : vec) { - int fcost = getCost(h, dst, elem, left, turn, false); + int fcost = getMovementCost(h, dst, elem, left, turn, false); if(fcost <= left) return ret; } @@ -714,9 +714,9 @@ int CPathfinderHelper::getCost(const CGHeroInstance * h, const int3 &src, const return ret; } -int CPathfinderHelper::getCost(const CGHeroInstance * h, const int3 &dst) +int CPathfinderHelper::getMovementCost(const CGHeroInstance * h, const int3 &dst) { - return getCost(h, h->visitablePos(), dst, h->movement); + return getMovementCost(h, h->visitablePos(), dst, h->movement); } CGPathNode::CGPathNode(int3 Coord, ELayer Layer) diff --git a/lib/CPathfinder.h b/lib/CPathfinder.h index cbf0c75dd..60d17132b 100644 --- a/lib/CPathfinder.h +++ b/lib/CPathfinder.h @@ -184,6 +184,6 @@ class DLL_LINKAGE CPathfinderHelper public: static void getNeighbours(CGameState * gs, const TerrainTile &srct, const int3 &tile, std::vector &vec, const boost::logic::tribool &onLand, const bool &limitCoastSailing); - static int getCost(const CGHeroInstance * h, const int3 &src, const int3 &dst, const int &remainingMovePoints =- 1, const int &turn = 0, const bool &checkLast = true); - static int getCost(const CGHeroInstance * h, const int3 &dst); + static int getMovementCost(const CGHeroInstance * h, const int3 &src, const int3 &dst, const int &remainingMovePoints =- 1, const int &turn = 0, const bool &checkLast = true); + static int getMovementCost(const CGHeroInstance * h, const int3 &dst); }; diff --git a/server/CGameHandler.cpp b/server/CGameHandler.cpp index 008f72496..9032cc181 100644 --- a/server/CGameHandler.cpp +++ b/server/CGameHandler.cpp @@ -1780,7 +1780,7 @@ bool CGameHandler::moveHero( ObjectInstanceID hid, int3 dst, ui8 teleporting, bo //check if destination tile is available const bool canFly = h->getBonusAtTurn(Bonus::FLYING_MOVEMENT); const bool canWalkOnSea = h->getBonusAtTurn(Bonus::WATER_WALKING); - const int cost = CPathfinderHelper::getCost(h, h->getPosition(), hmpos, h->movement); + const int cost = CPathfinderHelper::getMovementCost(h, h->getPosition(), hmpos, h->movement); //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) From 9a63735c24d35b509a7ea50889cf11c9df55dcc5 Mon Sep 17 00:00:00 2001 From: ArseniyShestakov Date: Tue, 10 Nov 2015 14:26:45 +0300 Subject: [PATCH 063/168] CPathfinderHelper: implement TurnInfo to avoid bonus re-checking TurnInfo contain information about what movement-related bonuses and MP hero going to have on certain turn. This way we can collect this information once for each turn so it's huge performance boost. --- lib/CPathfinder.cpp | 88 +++++++++++++++++++++---------- lib/CPathfinder.h | 24 ++++++++- lib/mapObjects/CGHeroInstance.cpp | 4 +- lib/mapObjects/CGHeroInstance.h | 3 +- 4 files changed, 87 insertions(+), 32 deletions(-) diff --git a/lib/CPathfinder.cpp b/lib/CPathfinder.cpp index e1150e886..3df8c5757 100644 --- a/lib/CPathfinder.cpp +++ b/lib/CPathfinder.cpp @@ -48,9 +48,10 @@ CPathfinder::CPathfinder(CPathsInfo &_out, CGameState *_gs, const CGHeroInstance throw std::runtime_error("Wrong checksum"); } - if(hero->getBonusAtTurn(Bonus::FLYING_MOVEMENT)) + hlp = new CPathfinderHelper(hero); + if(hlp->ti->bonusFlying) options.useFlying = true; - if(hero->getBonusAtTurn(Bonus::WATER_WALKING)) + if(hlp->ti->bonusWaterWalking) options.useWaterWalking = true; if(CGWhirlpool::isProtected(hero)) options.useTeleportWhirlpool = true; @@ -61,14 +62,6 @@ CPathfinder::CPathfinder(CPathsInfo &_out, CGameState *_gs, const CGHeroInstance void CPathfinder::calculatePaths() { - int maxMovePointsLand = hero->maxMovePoints(true); - int maxMovePointsWater = hero->maxMovePoints(false); - - auto maxMovePoints = [&](CGPathNode *cp) -> int - { - return cp->layer == ELayer::SAIL ? maxMovePointsWater : maxMovePointsLand; - }; - auto passOneTurnLimitCheck = [&](bool shouldCheck) -> bool { if(options.oneTurnSpecialLayersLimit && shouldCheck) @@ -109,10 +102,11 @@ void CPathfinder::calculatePaths() cp->locked = true; int movement = cp->moveRemains, turn = cp->turns; + hlp->updateTurnInfo(turn); if(!movement) { - movement = maxMovePoints(cp); - turn++; + hlp->updateTurnInfo(++turn); + movement = hlp->getMaxMovePoints(cp->layer); } //add accessible neighbouring nodes to the queue @@ -143,7 +137,7 @@ void CPathfinder::calculatePaths() if(!isMovementToDestPossible()) continue; - int cost = CPathfinderHelper::getMovementCost(hero, cp->coord, dp->coord, movement); + int cost = CPathfinderHelper::getMovementCost(hero, cp->coord, dp->coord, movement, hlp->ti); int remains = movement - cost; if(destAction == CGPathNode::EMBARK || destAction == CGPathNode::DISEMBARK) { @@ -154,9 +148,9 @@ void CPathfinder::calculatePaths() if(remains < 0) { //occurs rarely, when hero with low movepoints tries to leave the road - turnAtNextTile++; - int moveAtNextTile = maxMovePoints(cp); - cost = CPathfinderHelper::getMovementCost(hero, cp->coord, dp->coord, moveAtNextTile); //cost must be updated, movement points changed :( + hlp->updateTurnInfo(++turnAtNextTile); + int moveAtNextTile = hlp->getMaxMovePoints(i); + cost = CPathfinderHelper::getMovementCost(hero, cp->coord, dp->coord, moveAtNextTile, hlp->ti); //cost must be updated, movement points changed :( remains = moveAtNextTile - cost; } @@ -282,13 +276,13 @@ bool CPathfinder::isLayerAvailable(const ELayer &layer, const int &turn) const switch(layer) { case ELayer::AIR: - if(!hero->getBonusAtTurn(Bonus::FLYING_MOVEMENT, turn)) + if(!hlp->ti->bonusFlying) return false; break; case ELayer::WATER: - if(!hero->getBonusAtTurn(Bonus::WATER_WALKING, turn)) + if(!hlp->ti->bonusWaterWalking) return false; break; } @@ -625,6 +619,43 @@ bool CPathfinder::canVisitObject() const return cp->layer == ELayer::LAND || cp->layer == ELayer::SAIL; } +CPathfinderHelper::CPathfinderHelper(const CGHeroInstance * Hero) + : ti(nullptr), hero(Hero) +{ + turnsInfo.reserve(16); + updateTurnInfo(); +} + +void CPathfinderHelper::updateTurnInfo(const int &turn) +{ + if(!ti || ti->turn != turn) + { + if(turn < turnsInfo.size()) + ti = turnsInfo[turn]; + else + { + ti = getTurnInfo(hero, turn); + turnsInfo.push_back(ti); + } + } +} + +int CPathfinderHelper::getMaxMovePoints(const EPathfindingLayer &layer) const +{ + return layer == EPathfindingLayer::SAIL ? ti->maxMovePointsWater : ti->maxMovePointsLand; +} + +TurnInfo * CPathfinderHelper::getTurnInfo(const CGHeroInstance * h, const int &turn) +{ + auto turnInfo = new TurnInfo; + turnInfo->turn = turn; + turnInfo->maxMovePointsLand = h->maxMovePoints(true); + turnInfo->maxMovePointsWater = h->maxMovePoints(false); + turnInfo->bonusFlying = h->getBonusAtTurn(Bonus::FLYING_MOVEMENT, turn); + turnInfo->bonusWaterWalking = h->getBonusAtTurn(Bonus::WATER_WALKING, turn); + return turnInfo; +} + void CPathfinderHelper::getNeighbours(CGameState * gs, const TerrainTile &srct, const int3 &tile, std::vector &vec, const boost::logic::tribool &onLand, const bool &limitCoastSailing) { static const int3 dirs[] = { int3(0,1,0),int3(0,-1,0),int3(-1,0,0),int3(+1,0,0), @@ -664,27 +695,28 @@ void CPathfinderHelper::getNeighbours(CGameState * gs, const TerrainTile &srct, } } -int CPathfinderHelper::getMovementCost(const CGHeroInstance * h, const int3 &src, const int3 &dst, const int &remainingMovePoints, const int &turn, const bool &checkLast) +int CPathfinderHelper::getMovementCost(const CGHeroInstance * h, const int3 &src, const int3 &dst, const int &remainingMovePoints, const TurnInfo * ti, const bool &checkLast) { if(src == dst) //same tile return 0; - auto s = h->cb->getTile(src), d = h->cb->getTile(dst); - int ret = h->getTileCost(*d, *s, turn); + if(!ti) + ti = getTurnInfo(h); - auto flyBonus = h->getBonusAtTurn(Bonus::FLYING_MOVEMENT, turn); - auto waterWalkingBonus = h->getBonusAtTurn(Bonus::WATER_WALKING, turn); - if(d->blocked && flyBonus) + auto s = h->cb->getTile(src), d = h->cb->getTile(dst); + int ret = h->getTileCost(*d, *s, ti); + + if(d->blocked && ti->bonusFlying) { - ret *= (100.0 + flyBonus->val) / 100.0; + ret *= (100.0 + ti->bonusFlying->val) / 100.0; } else if(d->terType == ETerrainType::WATER) { if(h->boat && s->hasFavourableWinds() && d->hasFavourableWinds()) //Favourable Winds ret *= 0.666; - else if(!h->boat && waterWalkingBonus) + else if(!h->boat && ti->bonusWaterWalking) { - ret *= (100.0 + waterWalkingBonus->val) / 100.0; + ret *= (100.0 + ti->bonusWaterWalking->val) / 100.0; } } @@ -705,7 +737,7 @@ int CPathfinderHelper::getMovementCost(const CGHeroInstance * h, const int3 &src getNeighbours(h->cb->gameState(), *d, dst, vec, s->terType != ETerrainType::WATER, true); for(auto & elem : vec) { - int fcost = getMovementCost(h, dst, elem, left, turn, false); + int fcost = getMovementCost(h, dst, elem, left, ti, false); if(fcost <= left) return ret; } diff --git a/lib/CPathfinder.h b/lib/CPathfinder.h index 60d17132b..b774724b9 100644 --- a/lib/CPathfinder.h +++ b/lib/CPathfinder.h @@ -20,6 +20,7 @@ class CGHeroInstance; class CGObjectInstance; struct TerrainTile; +class CPathfinderHelper; struct DLL_LINKAGE CGPathNode { @@ -128,6 +129,7 @@ private: CPathsInfo &out; const CGHeroInstance *hero; + CPathfinderHelper * hlp; struct NodeComparer { @@ -179,11 +181,31 @@ private: }; +struct TurnInfo +{ + int turn; + int maxMovePointsLand; + int maxMovePointsWater; + const Bonus * bonusFlying; + const Bonus * bonusWaterWalking; +}; + class DLL_LINKAGE CPathfinderHelper { public: + TurnInfo * ti; + const CGHeroInstance * hero; + + CPathfinderHelper(const CGHeroInstance * Hero); + void updateTurnInfo(const int &turn = 0); + int getMaxMovePoints(const EPathfindingLayer &layer) const; + static TurnInfo * getTurnInfo(const CGHeroInstance * h, const int &turn = 0); + static void getNeighbours(CGameState * gs, const TerrainTile &srct, const int3 &tile, std::vector &vec, const boost::logic::tribool &onLand, const bool &limitCoastSailing); - static int getMovementCost(const CGHeroInstance * h, const int3 &src, const int3 &dst, const int &remainingMovePoints =- 1, const int &turn = 0, const bool &checkLast = true); + static int getMovementCost(const CGHeroInstance * h, const int3 &src, const int3 &dst, const int &remainingMovePoints =- 1, const TurnInfo * ti = nullptr, const bool &checkLast = true); static int getMovementCost(const CGHeroInstance * h, const int3 &dst); + +private: + std::vector turnsInfo; }; diff --git a/lib/mapObjects/CGHeroInstance.cpp b/lib/mapObjects/CGHeroInstance.cpp index 71bcf089e..a031d080c 100644 --- a/lib/mapObjects/CGHeroInstance.cpp +++ b/lib/mapObjects/CGHeroInstance.cpp @@ -56,7 +56,7 @@ static int lowestSpeed(const CGHeroInstance * chi) return ret; } -ui32 CGHeroInstance::getTileCost(const TerrainTile &dest, const TerrainTile &from, const int &turn) const +ui32 CGHeroInstance::getTileCost(const TerrainTile &dest, const TerrainTile &from, const TurnInfo * ti) const { unsigned ret = GameConstants::BASE_MOVEMENT_COST; @@ -80,7 +80,7 @@ ui32 CGHeroInstance::getTileCost(const TerrainTile &dest, const TerrainTile &fro break; } } - else if(!getBonusAtTurn(Bonus::NO_TERRAIN_PENALTY, turn, from.terType)) + else if(!getBonusAtTurn(Bonus::NO_TERRAIN_PENALTY, ti->turn, 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. diff --git a/lib/mapObjects/CGHeroInstance.h b/lib/mapObjects/CGHeroInstance.h index d05b035df..0c6056664 100644 --- a/lib/mapObjects/CGHeroInstance.h +++ b/lib/mapObjects/CGHeroInstance.h @@ -21,6 +21,7 @@ class CHero; class CGBoat; class CGTownInstance; struct TerrainTile; +struct TurnInfo; class CGHeroPlaceholder : public CGObjectInstance { @@ -129,7 +130,7 @@ public: EAlignment::EAlignment getAlignment() const; const std::string &getBiography() const; bool needsLastStack()const override; - ui32 getTileCost(const TerrainTile &dest, const TerrainTile &from, const int &turn) 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 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 From 2b6851b3d2de2fb79d45b43f120953f5a0858964 Mon Sep 17 00:00:00 2001 From: ArseniyShestakov Date: Tue, 10 Nov 2015 19:15:59 +0300 Subject: [PATCH 064/168] Pathfinding: use unique_ptr for hlp and fix few rules --- lib/CPathfinder.cpp | 28 +++++++++++++++++++++++++--- lib/CPathfinder.h | 2 +- 2 files changed, 26 insertions(+), 4 deletions(-) diff --git a/lib/CPathfinder.cpp b/lib/CPathfinder.cpp index 3df8c5757..f90c0164c 100644 --- a/lib/CPathfinder.cpp +++ b/lib/CPathfinder.cpp @@ -48,7 +48,7 @@ CPathfinder::CPathfinder(CPathsInfo &_out, CGameState *_gs, const CGHeroInstance throw std::runtime_error("Wrong checksum"); } - hlp = new CPathfinderHelper(hero); + hlp = make_unique(hero); if(hlp->ti->bonusFlying) options.useFlying = true; if(hlp->ti->bonusWaterWalking) @@ -292,11 +292,25 @@ bool CPathfinder::isLayerAvailable(const ELayer &layer, const int &turn) const bool CPathfinder::isLayerTransitionPossible() const { + /// No layer transition allowed when previous node action is BATTLE + if(cp->action == CGPathNode::BATTLE) + return false; + if((cp->layer == ELayer::AIR || cp->layer == ELayer::WATER) && dp->layer != ELayer::LAND) { + /// Hero that fly or walking on water can only go into ground layer return false; } + else if(cp->layer == ELayer::AIR && dp->layer == ELayer::LAND) + { + /// Hero that fly can only land on accessible tiles + if(cp->accessible != CGPathNode::ACCESSIBLE && + dp->accessible != CGPathNode::ACCESSIBLE) + { + return false; + } + } else if(cp->layer == ELayer::LAND && dp->layer == ELayer::AIR) { if(options.lightweightFlyingMode && !isSourceInitialPosition()) @@ -421,6 +435,8 @@ bool CPathfinder::isMovementAfterDestPossible() const /// TODO: Investigate what kind of limitation is possible to apply on movement from visitable tiles /// Likely in many cases we don't need to add visitable tile to queue when hero don't fly case CGPathNode::VISIT: + /// For now we only add visitable tile into queue when it's teleporter that allow transit + /// Movement from visitable tile when hero is standing on it is possible into any layer if(CGTeleport::isTeleport(dt->topVisitableObj())) { /// For now we'll always allow transit over teleporters @@ -430,7 +446,7 @@ bool CPathfinder::isMovementAfterDestPossible() const return true; } else - return true; + return false; case CGPathNode::NORMAL: return true; @@ -441,6 +457,11 @@ bool CPathfinder::isMovementAfterDestPossible() const case CGPathNode::DISEMBARK: if(options.useEmbarkAndDisembark && !isDestinationGuarded()) return true; + + case CGPathNode::BATTLE: + /// Movement after BATTLE action only possible to guardian tile + if(isDestinationGuarded()) + return true; } return false; @@ -498,6 +519,7 @@ void CPathfinder::initializeGraph() node->reset(); auto accessibility = evaluateAccessibility(pos, tinfo); + /// TODO: Probably this shouldn't be handled by initializeGraph if(blockNotAccessible && (accessibility != CGPathNode::ACCESSIBLE || tinfo->terType == ETerrainType::WATER)) accessibility = CGPathNode::BLOCKED; @@ -521,7 +543,7 @@ void CPathfinder::initializeGraph() if(options.useFlying) updateNode(pos, ELayer::AIR, tinfo, true); if(options.useWaterWalking) - updateNode(pos, ELayer::WATER, tinfo, true); + updateNode(pos, ELayer::WATER, tinfo, false); break; default: updateNode(pos, ELayer::LAND, tinfo, false); diff --git a/lib/CPathfinder.h b/lib/CPathfinder.h index b774724b9..8b757401c 100644 --- a/lib/CPathfinder.h +++ b/lib/CPathfinder.h @@ -129,7 +129,7 @@ private: CPathsInfo &out; const CGHeroInstance *hero; - CPathfinderHelper * hlp; + unique_ptr hlp; struct NodeComparer { From 4aaf6191a599c81d77ad771c0b2f343c63318a4c Mon Sep 17 00:00:00 2001 From: ArseniyShestakov Date: Tue, 10 Nov 2015 21:07:27 +0300 Subject: [PATCH 065/168] CPathfinder: implemented originalMovementRules option --- lib/CPathfinder.cpp | 34 +++++++++++++++++++++++++++------- lib/CPathfinder.h | 7 +++++++ 2 files changed, 34 insertions(+), 7 deletions(-) diff --git a/lib/CPathfinder.cpp b/lib/CPathfinder.cpp index f90c0164c..e7ee51658 100644 --- a/lib/CPathfinder.cpp +++ b/lib/CPathfinder.cpp @@ -32,6 +32,7 @@ CPathfinder::PathfinderOptions::PathfinderOptions() lightweightFlyingMode = false; oneTurnSpecialLayersLimit = true; + originalMovementRules = true; } CPathfinder::CPathfinder(CPathsInfo &_out, CGameState *_gs, const CGHeroInstance *_hero) @@ -304,10 +305,19 @@ bool CPathfinder::isLayerTransitionPossible() const } else if(cp->layer == ELayer::AIR && dp->layer == ELayer::LAND) { - /// Hero that fly can only land on accessible tiles - if(cp->accessible != CGPathNode::ACCESSIBLE && - dp->accessible != CGPathNode::ACCESSIBLE) + if(options.originalMovementRules) { + if ((cp->accessible != CGPathNode::ACCESSIBLE && + cp->accessible != CGPathNode::VISITABLE) && + (dp->accessible != CGPathNode::VISITABLE && + dp->accessible != CGPathNode::ACCESSIBLE)) + { + return false; + } + } + else if(cp->accessible != CGPathNode::ACCESSIBLE && dp->accessible != CGPathNode::ACCESSIBLE) + { + /// Hero that fly can only land on accessible tiles return false; } } @@ -344,9 +354,14 @@ bool CPathfinder::isMovementToDestPossible() case ELayer::LAND: if(!canMoveBetween(cp->coord, dp->coord) || dp->accessible == CGPathNode::BLOCKED) return false; - if(isSourceGuarded() && !isDestinationGuardian()) // Can step into tile of guard - return false; - + if(isSourceGuarded()) + { + if(!(options.originalMovementRules && cp->layer == ELayer::AIR) && + !isDestinationGuardian()) // Can step into tile of guard + { + return false; + } + } if(cp->layer == ELayer::SAIL) destAction = CGPathNode::DISEMBARK; @@ -418,7 +433,12 @@ bool CPathfinder::isMovementToDestPossible() if(destAction == CGPathNode::NORMAL) - destAction = CGPathNode::VISIT; + { + if(options.originalMovementRules && isDestinationGuarded()) + destAction = CGPathNode::BATTLE; + else + destAction = CGPathNode::VISIT; + } } else if(isDestinationGuarded()) destAction = CGPathNode::BATTLE; diff --git a/lib/CPathfinder.h b/lib/CPathfinder.h index 8b757401c..448024974 100644 --- a/lib/CPathfinder.h +++ b/lib/CPathfinder.h @@ -124,6 +124,13 @@ private: /// After all this limit should benefit performance on maps with tons of water or blocked tiles. 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 + bool originalMovementRules; + PathfinderOptions(); } options; From d95a999e231f89bb62c28a028049870e095f7c37 Mon Sep 17 00:00:00 2001 From: DjWarmonger Date: Wed, 11 Nov 2015 09:41:28 +0100 Subject: [PATCH 066/168] Fixed ffmpeg compilation on Visual Studio. --- client/CVideoHandler.cpp | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/client/CVideoHandler.cpp b/client/CVideoHandler.cpp index 1218fc0d2..88e320496 100644 --- a/client/CVideoHandler.cpp +++ b/client/CVideoHandler.cpp @@ -151,10 +151,14 @@ bool CVideoPlayer::open(std::string fname, bool loop, bool useOverlay, bool scal } // Allocate video frame +#ifdef _MSC_VER + frame = av_frame_alloc(); +#else #if LIBAVUTIL_VERSION_MAJOR > 52 frame = av_alloc_frame(); #else frame = avcodec_alloc_frame(); +#endif #endif //setup scaling @@ -392,11 +396,14 @@ void CVideoPlayer::close() if (frame) { +#ifndef _MSC_VER #if LIBAVUTIL_VERSION_MAJOR > 52 av_frame_free(frame); +#endif #else av_free(frame); #endif + frame = nullptr; } From 4b94efe6b574830314f424ce2fba1363601124fd Mon Sep 17 00:00:00 2001 From: AlexVinS Date: Wed, 11 Nov 2015 12:41:28 +0300 Subject: [PATCH 067/168] Hopefully proper fix for ffmpeg on MSVS --- client/CVideoHandler.cpp | 16 +++++----------- 1 file changed, 5 insertions(+), 11 deletions(-) diff --git a/client/CVideoHandler.cpp b/client/CVideoHandler.cpp index 88e320496..34586e29e 100644 --- a/client/CVideoHandler.cpp +++ b/client/CVideoHandler.cpp @@ -151,15 +151,12 @@ bool CVideoPlayer::open(std::string fname, bool loop, bool useOverlay, bool scal } // Allocate video frame -#ifdef _MSC_VER +#if LIBAVUTIL_VERSION_MAJOR > 51 frame = av_frame_alloc(); -#else -#if LIBAVUTIL_VERSION_MAJOR > 52 - frame = av_alloc_frame(); #else frame = avcodec_alloc_frame(); #endif -#endif + //setup scaling @@ -396,15 +393,12 @@ void CVideoPlayer::close() if (frame) { -#ifndef _MSC_VER -#if LIBAVUTIL_VERSION_MAJOR > 52 - av_frame_free(frame); -#endif +#if LIBAVUTIL_VERSION_MAJOR > 51 + av_frame_free(&frame);//will be set to null #else av_free(frame); + frame = nullptr; #endif - - frame = nullptr; } if (codec) From 33c933c2086e4226d927fd84849f1ec41da71180 Mon Sep 17 00:00:00 2001 From: AlexVinS Date: Wed, 11 Nov 2015 13:07:56 +0300 Subject: [PATCH 068/168] All "old" APIs are deprecated in ffmgeg 2.2.3 - remove them entirely --- client/CVideoHandler.cpp | 44 ---------------------------------------- client/CVideoHandler.h | 12 ----------- 2 files changed, 56 deletions(-) diff --git a/client/CVideoHandler.cpp b/client/CVideoHandler.cpp index 34586e29e..b155bc0b9 100644 --- a/client/CVideoHandler.cpp +++ b/client/CVideoHandler.cpp @@ -104,11 +104,7 @@ bool CVideoPlayer::open(std::string fname, bool loop, bool useOverlay, bool scal return false; } // Retrieve stream information -#if LIBAVCODEC_VERSION_INT < AV_VERSION_INT(53, 17, 0) - if (av_find_stream_info(format) < 0) -#else if (avformat_find_stream_info(format, nullptr) < 0) -#endif return false; // Find the first video stream @@ -139,11 +135,7 @@ bool CVideoPlayer::open(std::string fname, bool loop, bool useOverlay, bool scal } // Open codec -#if LIBAVCODEC_VERSION_INT < AV_VERSION_INT(53, 6, 0) - if ( avcodec_open(codecContext, codec) < 0 ) -#else if ( avcodec_open2(codecContext, codec, nullptr) < 0 ) -#endif { // Could not open codec codec = nullptr; @@ -151,12 +143,7 @@ bool CVideoPlayer::open(std::string fname, bool loop, bool useOverlay, bool scal } // Allocate video frame -#if LIBAVUTIL_VERSION_MAJOR > 51 frame = av_frame_alloc(); -#else - frame = avcodec_alloc_frame(); -#endif - //setup scaling @@ -191,35 +178,20 @@ bool CVideoPlayer::open(std::string fname, bool loop, bool useOverlay, bool scal { // Convert the image into YUV format that SDL uses sws = sws_getContext(codecContext->width, codecContext->height, codecContext->pix_fmt, pos.w, pos.h, -#if LIBAVUTIL_VERSION_MAJOR > 51 AV_PIX_FMT_YUV420P, -#else - PIX_FMT_YUV420P, -#endif SWS_BICUBIC, nullptr, nullptr, nullptr); } else { - -#if LIBAVUTIL_VERSION_MAJOR > 51 AVPixelFormat screenFormat = AV_PIX_FMT_NONE; -#else - PixelFormat screenFormat = PIX_FMT_NONE; -#endif if (screen->format->Bshift > screen->format->Rshift) { // this a BGR surface switch (screen->format->BytesPerPixel) { -#if LIBAVUTIL_VERSION_MAJOR > 51 case 2: screenFormat = AV_PIX_FMT_BGR565; break; case 3: screenFormat = AV_PIX_FMT_BGR24; break; case 4: screenFormat = AV_PIX_FMT_BGR32; break; -#else - case 2: screenFormat = PIX_FMT_BGR565; break; - case 3: screenFormat = PIX_FMT_BGR24; break; - case 4: screenFormat = PIX_FMT_BGR32; break; -#endif default: return false; } } @@ -228,15 +200,9 @@ bool CVideoPlayer::open(std::string fname, bool loop, bool useOverlay, bool scal // this a RGB surface switch (screen->format->BytesPerPixel) { -#if LIBAVUTIL_VERSION_MAJOR > 51 case 2: screenFormat = AV_PIX_FMT_RGB565; break; case 3: screenFormat = AV_PIX_FMT_RGB24; break; case 4: screenFormat = AV_PIX_FMT_RGB32; break; -#else - case 2: screenFormat = PIX_FMT_RGB565; break; - case 3: screenFormat = PIX_FMT_RGB24; break; - case 4: screenFormat = PIX_FMT_RGB32; break; -#endif default: return false; } } @@ -393,12 +359,7 @@ void CVideoPlayer::close() if (frame) { -#if LIBAVUTIL_VERSION_MAJOR > 51 av_frame_free(&frame);//will be set to null -#else - av_free(frame); - frame = nullptr; -#endif } if (codec) @@ -410,12 +371,7 @@ void CVideoPlayer::close() if (format) { -#if LIBAVCODEC_VERSION_INT < AV_VERSION_INT(53, 17, 0) - av_close_input_file(format); - format = nullptr; -#else avformat_close_input(&format); -#endif } if (context) diff --git a/client/CVideoHandler.h b/client/CVideoHandler.h index d7f67394c..bec0f05a0 100644 --- a/client/CVideoHandler.h +++ b/client/CVideoHandler.h @@ -51,18 +51,6 @@ public: extern "C" { #include #include - -// compatibility with different versions od libavutil -#if (LIBAVUTIL_VERSION_INT < AV_VERSION_INT(51, 42, 0)) || \ - (LIBAVUTIL_VERSION_INT == AV_VERSION_INT(51, 73, 101)) - -#define AV_PIX_FMT_NONE PIX_FMT_NONE -#define AV_PIX_FMT_NV12 PIX_FMT_NV12 -#define AV_PIX_FMT_YUV420P PIX_FMT_YUV420P -#define AV_PIX_FMT_UYVY422 PIX_FMT_UYVY422 -#define AV_PIX_FMT_YUYV422 PIX_FMT_YUYV422 - -#endif } class CVideoPlayer : public IMainVideoPlayer From f9dfa8d0c2acf86f8c90f0e7b4fea3f0844b470a Mon Sep 17 00:00:00 2001 From: AlexVinS Date: Wed, 11 Nov 2015 15:18:05 +0300 Subject: [PATCH 069/168] try to fix libav 9.18 --- client/CVideoHandler.cpp | 12 ++++++++++-- client/CVideoHandler.h | 5 +++++ 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/client/CVideoHandler.cpp b/client/CVideoHandler.cpp index b155bc0b9..4c2cdb123 100644 --- a/client/CVideoHandler.cpp +++ b/client/CVideoHandler.cpp @@ -141,9 +141,12 @@ bool CVideoPlayer::open(std::string fname, bool loop, bool useOverlay, bool scal codec = nullptr; return false; } - // Allocate video frame +#ifdef VCMI_USE_OLD_AVUTIL + frame = avcodec_alloc_frame(); +#else frame = av_frame_alloc(); +#endif //setup scaling @@ -359,7 +362,12 @@ void CVideoPlayer::close() if (frame) { - av_frame_free(&frame);//will be set to null +#ifdef VCMI_USE_OLD_AVUTIL + av_free(frame); + frame = nullptr; +#else + av_frame_free(&frame);//will be set to null +#endif } if (codec) diff --git a/client/CVideoHandler.h b/client/CVideoHandler.h index bec0f05a0..86a94b3e8 100644 --- a/client/CVideoHandler.h +++ b/client/CVideoHandler.h @@ -53,6 +53,11 @@ extern "C" { #include } +//compatibility for libav 9.18 in ubuntu 14.04, 52.66.100 is ffmpeg 2.2.3 +#if LIBAVUTIL_VERSION_INT < AV_VERSION_INT(52, 66, 100) +#define VCMI_USE_OLD_AVUTIL +#endif // LIBSWSCALE_VERSION_INT + class CVideoPlayer : public IMainVideoPlayer { int stream; // stream index in video From e20f861727332264fe9a8758e32acc486a02c642 Mon Sep 17 00:00:00 2001 From: AlexVinS Date: Wed, 11 Nov 2015 15:44:17 +0300 Subject: [PATCH 070/168] try to fix travis build. What an ancient version of ffmpeg does it have? --- client/CVideoHandler.cpp | 10 ---------- client/CVideoHandler.h | 24 ++++++++++++++++++++++++ 2 files changed, 24 insertions(+), 10 deletions(-) diff --git a/client/CVideoHandler.cpp b/client/CVideoHandler.cpp index 4c2cdb123..2be6eed85 100644 --- a/client/CVideoHandler.cpp +++ b/client/CVideoHandler.cpp @@ -142,14 +142,9 @@ bool CVideoPlayer::open(std::string fname, bool loop, bool useOverlay, bool scal return false; } // Allocate video frame -#ifdef VCMI_USE_OLD_AVUTIL - frame = avcodec_alloc_frame(); -#else frame = av_frame_alloc(); -#endif //setup scaling - if(scale) { pos.w = screen->w; @@ -362,12 +357,7 @@ void CVideoPlayer::close() if (frame) { -#ifdef VCMI_USE_OLD_AVUTIL - av_free(frame); - frame = nullptr; -#else av_frame_free(&frame);//will be set to null -#endif } if (codec) diff --git a/client/CVideoHandler.h b/client/CVideoHandler.h index 86a94b3e8..df3da0984 100644 --- a/client/CVideoHandler.h +++ b/client/CVideoHandler.h @@ -58,6 +58,30 @@ extern "C" { #define VCMI_USE_OLD_AVUTIL #endif // LIBSWSCALE_VERSION_INT +#ifdef VCMI_USE_OLD_AVUTIL + +#define AVPixelFormat PixelFormat +#define AV_PIX_FMT_YUV420P PIX_FMT_YUV420P +#define AV_PIX_FMT_BGR565 PIX_FMT_BGR565 +#define AV_PIX_FMT_BGR24 PIX_FMT_BGR24 +#define AV_PIX_FMT_BGR32 PIX_FMT_BGR32 +#define AV_PIX_FMT_RGB565 PIX_FMT_RGB565 +#define AV_PIX_FMT_RGB24 PIX_FMT_RGB24 +#define AV_PIX_FMT_RGB32 PIX_FMT_RGB32 + +inline AVFrame * av_frame_alloc() +{ + return avcodec_alloc_frame(); +} + +inline void av_frame_free(AVFrame ** frame) +{ + av_free(*frame); + *frame = nullptr; +} + +#endif // VCMI_USE_OLD_AVUTIL + class CVideoPlayer : public IMainVideoPlayer { int stream; // stream index in video From c9ad7167d1efe2b2b7242ec385ff5a22fa6e8124 Mon Sep 17 00:00:00 2001 From: AlexVinS Date: Wed, 11 Nov 2015 16:27:04 +0300 Subject: [PATCH 071/168] another try to fix travis build. --- client/CVideoHandler.h | 1 + 1 file changed, 1 insertion(+) diff --git a/client/CVideoHandler.h b/client/CVideoHandler.h index df3da0984..daa89cf05 100644 --- a/client/CVideoHandler.h +++ b/client/CVideoHandler.h @@ -61,6 +61,7 @@ extern "C" { #ifdef VCMI_USE_OLD_AVUTIL #define AVPixelFormat PixelFormat +#define AV_PIX_FMT_NONE PIX_FMT_NONE #define AV_PIX_FMT_YUV420P PIX_FMT_YUV420P #define AV_PIX_FMT_BGR565 PIX_FMT_BGR565 #define AV_PIX_FMT_BGR24 PIX_FMT_BGR24 From f1311abd0bd1b9ded15099725c85b2a5f6e0ef3d Mon Sep 17 00:00:00 2001 From: ArseniyShestakov Date: Wed, 11 Nov 2015 19:51:08 +0300 Subject: [PATCH 072/168] CPathfinder: move node action code into getDestAction --- lib/CPathfinder.cpp | 147 +++++++++++++++++++++++--------------------- lib/CPathfinder.h | 5 +- 2 files changed, 79 insertions(+), 73 deletions(-) diff --git a/lib/CPathfinder.cpp b/lib/CPathfinder.cpp index e7ee51658..227f26845 100644 --- a/lib/CPathfinder.cpp +++ b/lib/CPathfinder.cpp @@ -101,6 +101,8 @@ void CPathfinder::calculatePaths() cp = pq.top(); pq.pop(); cp->locked = true; + ct = &gs->map->getTile(cp->coord); + cObj = ct->topVisitableObj(cp->coord == out.hpos); int movement = cp->moveRemains, turn = cp->turns; hlp->updateTurnInfo(turn); @@ -115,6 +117,7 @@ void CPathfinder::calculatePaths() for(auto & neighbour : neighbours) { dt = &gs->map->getTile(neighbour); + dObj = dt->topVisitableObj(); for(ELayer i = ELayer::LAND; i <= ELayer::AIR; i.advance(1)) { dp = out.getNode(neighbour, i); @@ -133,11 +136,10 @@ void CPathfinder::calculatePaths() if(cp->layer != i && !isLayerTransitionPossible()) continue; - - destAction = CGPathNode::UNKNOWN; if(!isMovementToDestPossible()) continue; + destAction = getDestAction(); int cost = CPathfinderHelper::getMovementCost(hero, cp->coord, dp->coord, movement, hlp->ti); int remains = movement - cost; if(destAction == CGPathNode::EMBARK || destAction == CGPathNode::DISEMBARK) @@ -171,7 +173,7 @@ void CPathfinder::calculatePaths() } //neighbours loop //just add all passable teleport exits - if(sTileObj && canVisitObject()) + if(cObj && canVisitObject()) { addTeleportExits(); for(auto & neighbour : neighbours) @@ -196,18 +198,15 @@ void CPathfinder::calculatePaths() void CPathfinder::addNeighbours(const int3 &coord) { neighbours.clear(); - ct = &gs->map->getTile(coord); - std::vector tiles; CPathfinderHelper::getNeighbours(gs, *ct, coord, tiles, boost::logic::indeterminate, cp->layer == ELayer::SAIL); // TODO: find out if we still need "limitCoastSailing" option - sTileObj = ct->topVisitableObj(coord == out.hpos); if(canVisitObject()) { - if(sTileObj) + if(cObj) { for(int3 tile: tiles) { - if(canMoveBetween(tile, sTileObj->visitablePos())) + if(canMoveBetween(tile, cObj->visitablePos())) neighbours.push_back(tile); } } @@ -220,7 +219,7 @@ void CPathfinder::addNeighbours(const int3 &coord) void CPathfinder::addTeleportExits(bool noTeleportExcludes) { - assert(sTileObj); + assert(cObj); neighbours.clear(); auto isAllowedTeleportEntrance = [&](const CGTeleport * obj) -> bool @@ -243,7 +242,7 @@ void CPathfinder::addTeleportExits(bool noTeleportExcludes) return false; }; - const CGTeleport *sTileTeleport = dynamic_cast(sTileObj); + const CGTeleport *sTileTeleport = dynamic_cast(cObj); if(isAllowedTeleportEntrance(sTileTeleport)) { for(auto objId : gs->getTeleportChannelExits(sTileTeleport->channel, hero->tempOwner)) @@ -255,15 +254,15 @@ void CPathfinder::addTeleportExits(bool noTeleportExcludes) } if(options.useCastleGate - && (sTileObj->ID == Obj::TOWN && sTileObj->subID == ETownType::INFERNO - && getPlayerRelations(hero->tempOwner, sTileObj->tempOwner) != PlayerRelations::ENEMIES)) + && (cObj->ID == Obj::TOWN && cObj->subID == ETownType::INFERNO + && getPlayerRelations(hero->tempOwner, cObj->tempOwner) != PlayerRelations::ENEMIES)) { /// TODO: Find way to reuse CPlayerSpecificInfoCallback::getTownsInfo /// This may be handy if we allow to use teleportation to friendly towns auto towns = gs->getPlayer(hero->tempOwner)->towns; for(const auto & town : towns) { - if(town->id != sTileObj->id && town->visitingHero == nullptr + if(town->id != cObj->id && town->visitingHero == nullptr && town->hasBuilt(BuildingID::CASTLE_GATE, ETownType::INFERNO)) { neighbours.push_back(town->visitablePos()); @@ -346,7 +345,7 @@ bool CPathfinder::isLayerTransitionPossible() const return true; } -bool CPathfinder::isMovementToDestPossible() +bool CPathfinder::isMovementToDestPossible() const { auto obj = dt->topVisitableObj(); switch(dp->layer) @@ -362,10 +361,9 @@ bool CPathfinder::isMovementToDestPossible() return false; } } - if(cp->layer == ELayer::SAIL) - destAction = CGPathNode::DISEMBARK; break; + case ELayer::SAIL: if(!canMoveBetween(cp->coord, dp->coord) || dp->accessible == CGPathNode::BLOCKED) return false; @@ -377,19 +375,11 @@ bool CPathfinder::isMovementToDestPossible() if(!obj) return false; - if(obj->ID == Obj::BOAT) - destAction = CGPathNode::EMBARK; - else if(obj->ID != Obj::HERO) + if(obj->ID != Obj::BOAT && obj->ID != Obj::HERO) return false; } break; - case ELayer::AIR: - //if(!canMoveBetween(cp->coord, dp->coord)) - // return false; - - break; - case ELayer::WATER: if(!canMoveBetween(cp->coord, dp->coord) || dp->accessible != CGPathNode::ACCESSIBLE) return false; @@ -399,52 +389,6 @@ bool CPathfinder::isMovementToDestPossible() break; } - if(destAction == CGPathNode::UNKNOWN) - { - destAction = CGPathNode::NORMAL; - if(dp->layer == ELayer::LAND || dp->layer == ELayer::SAIL) - { - if(obj) - { - auto objRel = getPlayerRelations(obj->tempOwner, hero->tempOwner); - if(obj->ID == Obj::HERO) - { - if(objRel == PlayerRelations::ENEMIES) - destAction = CGPathNode::BATTLE; - else - destAction = CGPathNode::BLOCKING_VISIT; - } - else if(obj->ID == Obj::TOWN && objRel == PlayerRelations::ENEMIES) - { - const CGTownInstance * townObj = dynamic_cast(obj); - if (townObj->armedGarrison()) - destAction = CGPathNode::BATTLE; - } - else if(obj->ID == Obj::GARRISON || obj->ID == Obj::GARRISON2) - { - const CGGarrison * garrisonObj = dynamic_cast(obj); - if((garrisonObj->stacksCount() && objRel == PlayerRelations::ENEMIES) || isDestinationGuarded(true)) - destAction = CGPathNode::BATTLE; - } - else if(isDestinationGuardian()) - destAction = CGPathNode::BATTLE; - else if(obj->blockVisit && (!options.useCastleGate || obj->ID != Obj::TOWN)) - destAction = CGPathNode::BLOCKING_VISIT; - - - if(destAction == CGPathNode::NORMAL) - { - if(options.originalMovementRules && isDestinationGuarded()) - destAction = CGPathNode::BATTLE; - else - destAction = CGPathNode::VISIT; - } - } - else if(isDestinationGuarded()) - destAction = CGPathNode::BATTLE; - } - } - return true; } @@ -487,6 +431,67 @@ bool CPathfinder::isMovementAfterDestPossible() const return false; } +CGPathNode::ENodeAction CPathfinder::getDestAction() const +{ + CGPathNode::ENodeAction action = CGPathNode::NORMAL; + switch(dp->layer) + { + case ELayer::LAND: + if(cp->layer == ELayer::SAIL) + { + // TODO: Handle dismebark into guarded areaa + action = CGPathNode::DISEMBARK; + break; + } + + case ELayer::SAIL: + if(dObj) + { + auto objRel = getPlayerRelations(dObj->tempOwner, hero->tempOwner); + + if(dObj->ID == Obj::BOAT) + action = CGPathNode::EMBARK; + else if(dObj->ID == Obj::HERO) + { + if(objRel == PlayerRelations::ENEMIES) + action = CGPathNode::BATTLE; + else + action = CGPathNode::BLOCKING_VISIT; + } + else if(dObj->ID == Obj::TOWN && objRel == PlayerRelations::ENEMIES) + { + const CGTownInstance * townObj = dynamic_cast(dObj); + if (townObj->armedGarrison()) + action = CGPathNode::BATTLE; + } + else if(dObj->ID == Obj::GARRISON || dObj->ID == Obj::GARRISON2) + { + const CGGarrison * garrisonObj = dynamic_cast(dObj); + if((garrisonObj->stacksCount() && objRel == PlayerRelations::ENEMIES) || isDestinationGuarded(true)) + action = CGPathNode::BATTLE; + } + else if(isDestinationGuardian()) + action = CGPathNode::BATTLE; + else if(dObj->blockVisit && (!options.useCastleGate || dObj->ID != Obj::TOWN)) + action = CGPathNode::BLOCKING_VISIT; + + if(action == CGPathNode::NORMAL) + { + if(options.originalMovementRules && isDestinationGuarded()) + action = CGPathNode::BATTLE; + else + action = CGPathNode::VISIT; + } + } + else if(isDestinationGuarded()) + action = CGPathNode::BATTLE; + + break; + } + + return action; +} + bool CPathfinder::isSourceInitialPosition() const { return cp->coord == out.hpos; diff --git a/lib/CPathfinder.h b/lib/CPathfinder.h index 448024974..e900cc998 100644 --- a/lib/CPathfinder.h +++ b/lib/CPathfinder.h @@ -157,7 +157,7 @@ private: 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; + const CGObjectInstance * cObj, * dObj; CGPathNode::ENodeAction destAction; void addNeighbours(const int3 &coord); @@ -165,8 +165,9 @@ private: bool isLayerAvailable(const ELayer &layer, const int &turn) const; bool isLayerTransitionPossible() const; - bool isMovementToDestPossible(); + bool isMovementToDestPossible() const; bool isMovementAfterDestPossible() const; + CGPathNode::ENodeAction getDestAction() const; bool isSourceInitialPosition() const; int3 getSourceGuardPosition() const; From 46b923713b69e4239848a028790f1dcf45654c28 Mon Sep 17 00:00:00 2001 From: ArseniyShestakov Date: Wed, 11 Nov 2015 22:08:15 +0300 Subject: [PATCH 073/168] Pathfinding: rework isLayerTransitionPossible and fix formatting --- lib/CPathfinder.cpp | 228 ++++++++++++++++++++++++-------------------- lib/CPathfinder.h | 44 ++++----- 2 files changed, 147 insertions(+), 125 deletions(-) diff --git a/lib/CPathfinder.cpp b/lib/CPathfinder.cpp index 227f26845..f4e03bcaf 100644 --- a/lib/CPathfinder.cpp +++ b/lib/CPathfinder.cpp @@ -35,7 +35,7 @@ CPathfinder::PathfinderOptions::PathfinderOptions() originalMovementRules = true; } -CPathfinder::CPathfinder(CPathsInfo &_out, CGameState *_gs, const CGHeroInstance *_hero) +CPathfinder::CPathfinder(CPathsInfo & _out, CGameState * _gs, const CGHeroInstance * _hero) : CGameInfoCallback(_gs, boost::optional()), out(_out), hero(_hero) { assert(hero); @@ -82,7 +82,7 @@ void CPathfinder::calculatePaths() return true; else if(dp->turns > turn) return true; - else if(dp->turns >= turn && dp->moveRemains < remains) //this route is faster + else if(dp->turns >= turn && dp->moveRemains < remains) //this route is faster return true; return false; @@ -91,7 +91,7 @@ void CPathfinder::calculatePaths() //logGlobal->infoStream() << boost::format("Calculating paths for hero %s (adress %d) of player %d") % hero->name % hero % hero->tempOwner; //initial tile - set cost on 0 and add to the queue - CGPathNode *initialNode = out.getNode(out.hpos, hero->boat ? ELayer::SAIL : ELayer::LAND); + CGPathNode * initialNode = out.getNode(out.hpos, hero->boat ? ELayer::SAIL : ELayer::LAND); initialNode->turns = 0; initialNode->moveRemains = hero->movement; pq.push(initialNode); @@ -195,7 +195,7 @@ void CPathfinder::calculatePaths() } //queue loop } -void CPathfinder::addNeighbours(const int3 &coord) +void CPathfinder::addNeighbours(const int3 & coord) { neighbours.clear(); std::vector tiles; @@ -242,7 +242,7 @@ void CPathfinder::addTeleportExits(bool noTeleportExcludes) return false; }; - const CGTeleport *sTileTeleport = dynamic_cast(cObj); + const CGTeleport * sTileTeleport = dynamic_cast(cObj); if(isAllowedTeleportEntrance(sTileTeleport)) { for(auto objId : gs->getTeleportChannelExits(sTileTeleport->channel, hero->tempOwner)) @@ -271,20 +271,21 @@ void CPathfinder::addTeleportExits(bool noTeleportExcludes) } } -bool CPathfinder::isLayerAvailable(const ELayer &layer, const int &turn) const +bool CPathfinder::isLayerAvailable(const ELayer layer, const int turn) const { switch(layer) { - case ELayer::AIR: - if(!hlp->ti->bonusFlying) - return false; + case ELayer::AIR: + if(!hlp->ti->bonusFlying) + return false; - break; + break; - case ELayer::WATER: - if(!hlp->ti->bonusWaterWalking) - return false; - break; + case ELayer::WATER: + if(!hlp->ti->bonusWaterWalking) + return false; + + break; } return true; @@ -296,17 +297,45 @@ bool CPathfinder::isLayerTransitionPossible() const if(cp->action == CGPathNode::BATTLE) return false; - if((cp->layer == ELayer::AIR || cp->layer == ELayer::WATER) - && dp->layer != ELayer::LAND) - { - /// Hero that fly or walking on water can only go into ground layer - return false; - } - else if(cp->layer == ELayer::AIR && dp->layer == ELayer::LAND) + switch(cp->layer) { + case ELayer::LAND: + if(options.lightweightFlyingMode && dp->layer == ELayer::AIR) + { + if(!isSourceInitialPosition()) + return false; + } + else if(dp->layer == ELayer::SAIL) + { + /// Cannot enter empty water tile from land -> it has to be visitable + if(dp->accessible == CGPathNode::ACCESSIBLE) + return false; + } + + break; + + case ELayer::SAIL: + if(dp->layer != ELayer::LAND) + return false; + + if(!dt->isCoastal()) + return false; + + //tile must be accessible -> exception: unblocked blockvis tiles -> clear but guarded by nearby monster coast + if((dp->accessible != CGPathNode::ACCESSIBLE && (dp->accessible != CGPathNode::BLOCKVIS || dt->blocked)) + || dt->visitable) //TODO: passableness problem -> town says it's passable (thus accessible) but we obviously can't disembark onto town gate + { + return false; + } + break; + + case ELayer::AIR: + if(dp->layer != ELayer::LAND) + return false; + if(options.originalMovementRules) { - if ((cp->accessible != CGPathNode::ACCESSIBLE && + if((cp->accessible != CGPathNode::ACCESSIBLE && cp->accessible != CGPathNode::VISITABLE) && (dp->accessible != CGPathNode::VISITABLE && dp->accessible != CGPathNode::ACCESSIBLE)) @@ -319,74 +348,60 @@ bool CPathfinder::isLayerTransitionPossible() const /// Hero that fly can only land on accessible tiles return false; } - } - else if(cp->layer == ELayer::LAND && dp->layer == ELayer::AIR) - { - if(options.lightweightFlyingMode && !isSourceInitialPosition()) - return false; - } - else if(cp->layer == ELayer::SAIL && dp->layer != ELayer::LAND) - return false; - else if(cp->layer == ELayer::SAIL && dp->layer == ELayer::LAND) - { - if(!dt->isCoastal()) + + break; + + case ELayer::WATER: + if(dp->layer != ELayer::LAND) return false; - //tile must be accessible -> exception: unblocked blockvis tiles -> clear but guarded by nearby monster coast - if((dp->accessible != CGPathNode::ACCESSIBLE && (dp->accessible != CGPathNode::BLOCKVIS || dt->blocked)) - || dt->visitable) //TODO: passableness problem -> town says it's passable (thus accessible) but we obviously can't disembark onto town gate - return false; - } - else if(cp->layer == ELayer::LAND && dp->layer == ELayer::SAIL) - { - if(dp->accessible == CGPathNode::ACCESSIBLE) //cannot enter empty water tile from land -> it has to be visitable - return false; + break; } + return true; } bool CPathfinder::isMovementToDestPossible() const { - auto obj = dt->topVisitableObj(); switch(dp->layer) { - case ELayer::LAND: - if(!canMoveBetween(cp->coord, dp->coord) || dp->accessible == CGPathNode::BLOCKED) - return false; - if(isSourceGuarded()) + case ELayer::LAND: + if(!canMoveBetween(cp->coord, dp->coord) || dp->accessible == CGPathNode::BLOCKED) + return false; + if(isSourceGuarded()) + { + if(!(options.originalMovementRules && cp->layer == ELayer::AIR) && + !isDestinationGuardian()) // Can step into tile of guard { - if(!(options.originalMovementRules && cp->layer == ELayer::AIR) && - !isDestinationGuardian()) // Can step into tile of guard - { - return false; - } + return false; } + } - break; + break; - case ELayer::SAIL: - if(!canMoveBetween(cp->coord, dp->coord) || dp->accessible == CGPathNode::BLOCKED) - return false; - if(isSourceGuarded() && !isDestinationGuardian()) // Can step into tile of guard + case ELayer::SAIL: + if(!canMoveBetween(cp->coord, dp->coord) || dp->accessible == CGPathNode::BLOCKED) + return false; + if(isSourceGuarded() && !isDestinationGuardian()) // Can step into tile of guard + return false; + + if(cp->layer == ELayer::LAND) + { + if(!dObj) return false; - if(cp->layer == ELayer::LAND) - { - if(!obj) - return false; - - if(obj->ID != Obj::BOAT && obj->ID != Obj::HERO) - return false; - } - break; - - case ELayer::WATER: - if(!canMoveBetween(cp->coord, dp->coord) || dp->accessible != CGPathNode::ACCESSIBLE) - return false; - if(isDestinationGuarded()) + if(dObj->ID != Obj::BOAT && dObj->ID != Obj::HERO) return false; + } + break; - break; + case ELayer::WATER: + if(!canMoveBetween(cp->coord, dp->coord) || dp->accessible != CGPathNode::ACCESSIBLE) + return false; + if(isDestinationGuarded()) + return false; + + break; } return true; @@ -401,16 +416,17 @@ bool CPathfinder::isMovementAfterDestPossible() const case CGPathNode::VISIT: /// For now we only add visitable tile into queue when it's teleporter that allow transit /// Movement from visitable tile when hero is standing on it is possible into any layer - if(CGTeleport::isTeleport(dt->topVisitableObj())) + if(CGTeleport::isTeleport(dObj)) { /// For now we'll always allow transit over teleporters /// Transit over whirlpools only allowed when hero protected - auto whirlpool = dynamic_cast(dt->topVisitableObj()); + auto whirlpool = dynamic_cast(dObj); if(!whirlpool || options.useTeleportWhirlpool) return true; } - else - return false; + + break; + case CGPathNode::NORMAL: return true; @@ -418,14 +434,20 @@ bool CPathfinder::isMovementAfterDestPossible() const if(options.useEmbarkAndDisembark) return true; + break; + case CGPathNode::DISEMBARK: if(options.useEmbarkAndDisembark && !isDestinationGuarded()) return true; + break; + case CGPathNode::BATTLE: /// Movement after BATTLE action only possible to guardian tile - if(isDestinationGuarded()) + if(isDestinationGuardian()) return true; + + break; } return false; @@ -461,7 +483,7 @@ CGPathNode::ENodeAction CPathfinder::getDestAction() const else if(dObj->ID == Obj::TOWN && objRel == PlayerRelations::ENEMIES) { const CGTownInstance * townObj = dynamic_cast(dObj); - if (townObj->armedGarrison()) + if(townObj->armedGarrison()) action = CGPathNode::BATTLE; } else if(dObj->ID == Obj::GARRISON || dObj->ID == Obj::GARRISON2) @@ -509,9 +531,9 @@ bool CPathfinder::isSourceGuarded() const if(getSourceGuardPosition() != int3(-1, -1, -1) && !isSourceInitialPosition()) { //special case -> hero embarked a boat standing on a guarded tile -> we must allow to move away from that tile - if(cp->accessible != CGPathNode::VISITABLE - || cp->theNodeBefore->layer == ELayer::LAND - || ct->topVisitableId() != Obj::BOAT) + if(cp->accessible != CGPathNode::VISITABLE || + cp->theNodeBefore->layer == ELayer::LAND || + cObj->ID != Obj::BOAT) { return true; } @@ -538,7 +560,7 @@ bool CPathfinder::isDestinationGuardian() const void CPathfinder::initializeGraph() { - auto updateNode = [&](int3 pos, ELayer layer, const TerrainTile *tinfo, bool blockNotAccessible) + auto updateNode = [&](int3 pos, ELayer layer, const TerrainTile * tinfo, bool blockNotAccessible) { auto node = out.getNode(pos, layer); node->reset(); @@ -558,7 +580,7 @@ void CPathfinder::initializeGraph() { for(pos.z=0; pos.z < out.sizes.z; ++pos.z) { - const TerrainTile *tinfo = &gs->map->getTile(pos); + const TerrainTile * tinfo = &gs->map->getTile(pos); switch(tinfo->terType) { case ETerrainType::ROCK: @@ -581,7 +603,7 @@ void CPathfinder::initializeGraph() } } -CGPathNode::EAccessibility CPathfinder::evaluateAccessibility(const int3 &pos, const TerrainTile *tinfo) const +CGPathNode::EAccessibility CPathfinder::evaluateAccessibility(const int3 & pos, const TerrainTile * tinfo) const { CGPathNode::EAccessibility ret = (tinfo->blocked ? CGPathNode::BLOCKED : CGPathNode::ACCESSIBLE); @@ -596,7 +618,7 @@ CGPathNode::EAccessibility CPathfinder::evaluateAccessibility(const int3 &pos, c } else { - for(const CGObjectInstance *obj : tinfo->visitableObjects) + for(const CGObjectInstance * obj : tinfo->visitableObjects) { if(obj->passableFor(hero->tempOwner)) { @@ -608,7 +630,7 @@ CGPathNode::EAccessibility CPathfinder::evaluateAccessibility(const int3 &pos, c } else if(obj->ID != Obj::EVENT) //pathfinder should ignore placed events { - ret = CGPathNode::VISITABLE; + ret = CGPathNode::VISITABLE; } } } @@ -623,7 +645,7 @@ CGPathNode::EAccessibility CPathfinder::evaluateAccessibility(const int3 &pos, c return ret; } -bool CPathfinder::canMoveBetween(const int3 &a, const int3 &b) const +bool CPathfinder::canMoveBetween(const int3 & a, const int3 & b) const { return gs->checkForVisitableDir(a, b); } @@ -673,7 +695,7 @@ CPathfinderHelper::CPathfinderHelper(const CGHeroInstance * Hero) updateTurnInfo(); } -void CPathfinderHelper::updateTurnInfo(const int &turn) +void CPathfinderHelper::updateTurnInfo(const int turn) { if(!ti || ti->turn != turn) { @@ -687,12 +709,12 @@ void CPathfinderHelper::updateTurnInfo(const int &turn) } } -int CPathfinderHelper::getMaxMovePoints(const EPathfindingLayer &layer) const +int CPathfinderHelper::getMaxMovePoints(const EPathfindingLayer layer) const { return layer == EPathfindingLayer::SAIL ? ti->maxMovePointsWater : ti->maxMovePointsLand; } -TurnInfo * CPathfinderHelper::getTurnInfo(const CGHeroInstance * h, const int &turn) +TurnInfo * CPathfinderHelper::getTurnInfo(const CGHeroInstance * h, const int turn) { auto turnInfo = new TurnInfo; turnInfo->turn = turn; @@ -703,19 +725,19 @@ TurnInfo * CPathfinderHelper::getTurnInfo(const CGHeroInstance * h, const int &t return turnInfo; } -void CPathfinderHelper::getNeighbours(CGameState * gs, const TerrainTile &srct, const int3 &tile, std::vector &vec, const boost::logic::tribool &onLand, const bool &limitCoastSailing) +void CPathfinderHelper::getNeighbours(CGameState * gs, const TerrainTile & srct, const int3 & tile, std::vector & vec, const boost::logic::tribool & onLand, const 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) + for(auto & dir : dirs) { const int3 hlp = tile + dir; if(!gs->isInTheMap(hlp)) continue; - const TerrainTile &hlpt = gs->map->getTile(hlp); + const TerrainTile & hlpt = gs->map->getTile(hlp); // //we cannot visit things from blocked tiles // if(srct.blocked && !srct.visitable && hlpt.visitable && srct.blockingObjects.front()->ID != HEROI_TYPE) @@ -734,7 +756,7 @@ void CPathfinderHelper::getNeighbours(CGameState * gs, const TerrainTile &srct, continue; } - if((indeterminate(onLand) || onLand == (hlpt.terType!=ETerrainType::WATER) ) + if((indeterminate(onLand) || onLand == (hlpt.terType!=ETerrainType::WATER) ) && hlpt.terType != ETerrainType::ROCK) { vec.push_back(hlp); @@ -742,7 +764,7 @@ void CPathfinderHelper::getNeighbours(CGameState * gs, const TerrainTile &srct, } } -int CPathfinderHelper::getMovementCost(const CGHeroInstance * h, const int3 &src, const int3 &dst, const int &remainingMovePoints, const TurnInfo * ti, const bool &checkLast) +int CPathfinderHelper::getMovementCost(const CGHeroInstance * h, const int3 & src, const int3 & dst, const int remainingMovePoints, const TurnInfo * ti, const bool checkLast) { if(src == dst) //same tile return 0; @@ -793,7 +815,7 @@ int CPathfinderHelper::getMovementCost(const CGHeroInstance * h, const int3 &src return ret; } -int CPathfinderHelper::getMovementCost(const CGHeroInstance * h, const int3 &dst) +int CPathfinderHelper::getMovementCost(const CGHeroInstance * h, const int3 & dst) { return getMovementCost(h, h->visitablePos(), dst, h->movement); } @@ -840,7 +862,7 @@ void CGPath::convert(ui8 mode) } } -CPathsInfo::CPathsInfo(const int3 &Sizes) +CPathsInfo::CPathsInfo(const int3 & Sizes) : sizes(Sizes) { hero = nullptr; @@ -856,7 +878,7 @@ CPathsInfo::~CPathsInfo() { } -const CGPathNode * CPathsInfo::getPathInfo(const int3 &tile, const ELayer &layer) const +const CGPathNode * CPathsInfo::getPathInfo(const int3 & tile, const ELayer layer) const { boost::unique_lock pathLock(pathMx); @@ -865,25 +887,25 @@ const CGPathNode * CPathsInfo::getPathInfo(const int3 &tile, const ELayer &layer return getNode(tile, layer); } -bool CPathsInfo::getPath(CGPath &out, const int3 &dst, const ELayer &layer) const +bool CPathsInfo::getPath(CGPath & out, const int3 & dst, const ELayer layer) const { boost::unique_lock pathLock(pathMx); out.nodes.clear(); - const CGPathNode *curnode = getNode(dst, layer); + const CGPathNode * curnode = getNode(dst, layer); if(!curnode->theNodeBefore) return false; while(curnode) { - CGPathNode cpn = *curnode; + CGPathNode cpn = * curnode; curnode = curnode->theNodeBefore; out.nodes.push_back(cpn); } return true; } -int CPathsInfo::getDistance(const int3 &tile, const ELayer &layer) const +int CPathsInfo::getDistance(const int3 & tile, const ELayer layer) const { boost::unique_lock pathLock(pathMx); @@ -894,7 +916,7 @@ int CPathsInfo::getDistance(const int3 &tile, const ELayer &layer) const return 255; } -CGPathNode *CPathsInfo::getNode(const int3 &coord, const ELayer &layer) const +CGPathNode * CPathsInfo::getNode(const int3 & coord, const ELayer layer) const { if(layer != ELayer::AUTO) return nodes[coord.x][coord.y][coord.z][layer]; diff --git a/lib/CPathfinder.h b/lib/CPathfinder.h index e900cc998..b0cfef049 100644 --- a/lib/CPathfinder.h +++ b/lib/CPathfinder.h @@ -75,23 +75,23 @@ struct DLL_LINKAGE CPathsInfo mutable boost::mutex pathMx; - const CGHeroInstance *hero; + const CGHeroInstance * hero; int3 hpos; int3 sizes; boost::multi_array nodes; //[w][h][level][layer] - CPathsInfo(const int3 &Sizes); + CPathsInfo(const int3 & Sizes); ~CPathsInfo(); - const CGPathNode * getPathInfo(const int3 &tile, const ELayer &layer = ELayer::AUTO) const; - bool getPath(CGPath &out, const int3 &dst, const ELayer &layer = ELayer::AUTO) const; - int getDistance(const int3 &tile, const ELayer &layer = ELayer::AUTO) const; - CGPathNode *getNode(const int3 &coord, const ELayer &layer) const; + const CGPathNode * getPathInfo(const int3 & tile, const ELayer layer = ELayer::AUTO) const; + bool getPath(CGPath & out, const int3 & dst, const ELayer layer = ELayer::AUTO) const; + int getDistance(const int3 & tile, const ELayer layer = ELayer::AUTO) const; + CGPathNode * getNode(const int3 & coord, const ELayer layer) const; }; class CPathfinder : private CGameInfoCallback { public: - CPathfinder(CPathsInfo &_out, CGameState *_gs, const CGHeroInstance *_hero); + 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: @@ -134,8 +134,8 @@ private: PathfinderOptions(); } options; - CPathsInfo &out; - const CGHeroInstance *hero; + CPathsInfo & out; + const CGHeroInstance * hero; unique_ptr hlp; struct NodeComparer @@ -154,16 +154,16 @@ private: std::vector 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 + 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 * cObj, * dObj; CGPathNode::ENodeAction destAction; - void addNeighbours(const int3 &coord); + void addNeighbours(const int3 & coord); void addTeleportExits(bool noTeleportExcludes = false); - bool isLayerAvailable(const ELayer &layer, const int &turn) const; + bool isLayerAvailable(const ELayer layer, const int turn) const; bool isLayerTransitionPossible() const; bool isMovementToDestPossible() const; bool isMovementAfterDestPossible() const; @@ -177,8 +177,8 @@ private: void initializeGraph(); - 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; + 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 addTeleportTwoWay(const CGTeleport * obj) const; bool addTeleportOneWay(const CGTeleport * obj) const; @@ -205,14 +205,14 @@ public: const CGHeroInstance * hero; CPathfinderHelper(const CGHeroInstance * Hero); - void updateTurnInfo(const int &turn = 0); - int getMaxMovePoints(const EPathfindingLayer &layer) const; - static TurnInfo * getTurnInfo(const CGHeroInstance * h, const int &turn = 0); + void updateTurnInfo(const int turn = 0); + int getMaxMovePoints(const EPathfindingLayer layer) const; + static TurnInfo * getTurnInfo(const CGHeroInstance * h, const int turn = 0); - static void getNeighbours(CGameState * gs, const TerrainTile &srct, const int3 &tile, std::vector &vec, const boost::logic::tribool &onLand, const bool &limitCoastSailing); + static void getNeighbours(CGameState * gs, const TerrainTile & srct, const int3 & tile, std::vector & vec, const boost::logic::tribool & onLand, const bool limitCoastSailing); - static int getMovementCost(const CGHeroInstance * h, const int3 &src, const int3 &dst, const int &remainingMovePoints =- 1, const TurnInfo * ti = nullptr, const bool &checkLast = true); - static int getMovementCost(const CGHeroInstance * h, const int3 &dst); + static int getMovementCost(const CGHeroInstance * h, const int3 & src, const int3 & dst, const int remainingMovePoints =- 1, const TurnInfo * ti = nullptr, const bool checkLast = true); + static int getMovementCost(const CGHeroInstance * h, const int3 & dst); private: std::vector turnsInfo; From 09473f6648d4244434129ab73e90b311b5e2a34d Mon Sep 17 00:00:00 2001 From: ArseniyShestakov Date: Wed, 11 Nov 2015 22:29:20 +0300 Subject: [PATCH 074/168] CPathfinder: move embark special case to isMovementToDestPossible --- lib/CPathfinder.cpp | 25 ++++++++++++++----------- 1 file changed, 14 insertions(+), 11 deletions(-) diff --git a/lib/CPathfinder.cpp b/lib/CPathfinder.cpp index f4e03bcaf..758804767 100644 --- a/lib/CPathfinder.cpp +++ b/lib/CPathfinder.cpp @@ -327,6 +327,7 @@ bool CPathfinder::isLayerTransitionPossible() const { return false; } + break; case ELayer::AIR: @@ -382,8 +383,12 @@ bool CPathfinder::isMovementToDestPossible() const case ELayer::SAIL: if(!canMoveBetween(cp->coord, dp->coord) || dp->accessible == CGPathNode::BLOCKED) return false; - if(isSourceGuarded() && !isDestinationGuardian()) // Can step into tile of guard - return false; + if(isSourceGuarded()) + { + // Hero embarked a boat standing on a guarded tile -> we must allow to move away from that tile + if(cp->action != CGPathNode::EMBARK && !isDestinationGuardian()) + return false; + } if(cp->layer == ELayer::LAND) { @@ -393,6 +398,7 @@ bool CPathfinder::isMovementToDestPossible() const if(dObj->ID != Obj::BOAT && dObj->ID != Obj::HERO) return false; } + break; case ELayer::WATER: @@ -526,17 +532,14 @@ int3 CPathfinder::getSourceGuardPosition() const bool CPathfinder::isSourceGuarded() const { - //map can start with hero on guarded tile or teleport there using dimension door - //so threat tile hero standing on like it's not guarded because it's should be possible to move out of here + /// Hero can move from guarded tile if movement started on that tile + /// It's possible at least in these cases: + /// - Map start with hero on guarded tile + /// - Dimention door used + /// TODO: check what happen when there is several guards if(getSourceGuardPosition() != int3(-1, -1, -1) && !isSourceInitialPosition()) { - //special case -> hero embarked a boat standing on a guarded tile -> we must allow to move away from that tile - if(cp->accessible != CGPathNode::VISITABLE || - cp->theNodeBefore->layer == ELayer::LAND || - cObj->ID != Obj::BOAT) - { - return true; - } + return true; } return false; From d76b2b7ca886a6fbf3b2b73a4135710cd040aff8 Mon Sep 17 00:00:00 2001 From: ArseniyShestakov Date: Thu, 12 Nov 2015 00:05:20 +0300 Subject: [PATCH 075/168] CPathsInfo: rework nodes multi_array allocation --- lib/CPathfinder.cpp | 64 +++++++++++++++++++++++++++------------------ lib/CPathfinder.h | 15 ++++++----- 2 files changed, 47 insertions(+), 32 deletions(-) diff --git a/lib/CPathfinder.cpp b/lib/CPathfinder.cpp index 758804767..749c2d008 100644 --- a/lib/CPathfinder.cpp +++ b/lib/CPathfinder.cpp @@ -566,14 +566,15 @@ void CPathfinder::initializeGraph() auto updateNode = [&](int3 pos, ELayer layer, const TerrainTile * tinfo, bool blockNotAccessible) { auto node = out.getNode(pos, layer); - node->reset(); auto accessibility = evaluateAccessibility(pos, tinfo); /// TODO: Probably this shouldn't be handled by initializeGraph if(blockNotAccessible && (accessibility != CGPathNode::ACCESSIBLE || tinfo->terType == ETerrainType::WATER)) + { accessibility = CGPathNode::BLOCKED; - node->accessible = accessibility; + } + node->update(pos, layer, accessibility); }; int3 pos; @@ -823,8 +824,8 @@ int CPathfinderHelper::getMovementCost(const CGHeroInstance * h, const int3 & ds return getMovementCost(h, h->visitablePos(), dst, h->movement); } -CGPathNode::CGPathNode(int3 Coord, ELayer Layer) - : coord(Coord), layer(Layer) +CGPathNode::CGPathNode() + : coord(int3(-1, -1, -1)), layer(ELayer::WRONG) { reset(); } @@ -839,6 +840,19 @@ void CGPathNode::reset() action = UNKNOWN; } +void CGPathNode::update(const int3 & Coord, const ELayer Layer, const EAccessibility Accessible) +{ + if(layer == ELayer::WRONG) + { + coord = Coord; + layer = Layer; + } + else + reset(); + + accessible = Accessible; +} + bool CGPathNode::reachable() const { return turns < 255; @@ -870,63 +884,61 @@ CPathsInfo::CPathsInfo(const int3 & Sizes) { hero = nullptr; nodes.resize(boost::extents[sizes.x][sizes.y][sizes.z][ELayer::NUM_LAYERS]); - for(int i = 0; i < sizes.x; i++) - for(int j = 0; j < sizes.y; j++) - for(int z = 0; z < sizes.z; z++) - for(int l = 0; l < ELayer::NUM_LAYERS; l++) - nodes[i][j][z][l] = new CGPathNode(int3(i, j, z), static_cast(l)); } CPathsInfo::~CPathsInfo() { } -const CGPathNode * CPathsInfo::getPathInfo(const int3 & tile, const ELayer layer) const +const CGPathNode * CPathsInfo::getPathInfo(const int3 & tile) const { - boost::unique_lock pathLock(pathMx); + assert(vstd::iswithin(tile.x, 0, sizes.x)); + assert(vstd::iswithin(tile.y, 0, sizes.y)); + assert(vstd::iswithin(tile.z, 0, sizes.z)); - if(tile.x >= sizes.x || tile.y >= sizes.y || tile.z >= sizes.z || layer >= ELayer::NUM_LAYERS) - return nullptr; - return getNode(tile, layer); + boost::unique_lock pathLock(pathMx); + return getNode(tile); } -bool CPathsInfo::getPath(CGPath & out, const int3 & dst, const ELayer layer) const +bool CPathsInfo::getPath(CGPath & out, const int3 & dst) const { boost::unique_lock pathLock(pathMx); out.nodes.clear(); - const CGPathNode * curnode = getNode(dst, layer); + const CGPathNode * curnode = getNode(dst); if(!curnode->theNodeBefore) return false; while(curnode) { - CGPathNode cpn = * curnode; + const CGPathNode cpn = * curnode; curnode = curnode->theNodeBefore; out.nodes.push_back(cpn); } return true; } -int CPathsInfo::getDistance(const int3 & tile, const ELayer layer) const +int CPathsInfo::getDistance(const int3 & tile) const { boost::unique_lock pathLock(pathMx); CGPath ret; - if(getPath(ret, tile, layer)) + if(getPath(ret, tile)) return ret.nodes.size(); else return 255; } -CGPathNode * CPathsInfo::getNode(const int3 & coord, const ELayer layer) const +const CGPathNode * CPathsInfo::getNode(const int3 & coord) const { - if(layer != ELayer::AUTO) - return nodes[coord.x][coord.y][coord.z][layer]; - - auto landNode = nodes[coord.x][coord.y][coord.z][ELayer::LAND]; - if(landNode->theNodeBefore) + auto landNode = &nodes[coord.x][coord.y][coord.z][ELayer::LAND]; + if(landNode->reachable()) return landNode; else - return nodes[coord.x][coord.y][coord.z][ELayer::SAIL]; + return &nodes[coord.x][coord.y][coord.z][ELayer::SAIL]; +} + +CGPathNode * CPathsInfo::getNode(const int3 & coord, const ELayer layer) +{ + return &nodes[coord.x][coord.y][coord.z][layer]; } diff --git a/lib/CPathfinder.h b/lib/CPathfinder.h index b0cfef049..3c386308c 100644 --- a/lib/CPathfinder.h +++ b/lib/CPathfinder.h @@ -55,8 +55,9 @@ struct DLL_LINKAGE CGPathNode ELayer layer; ENodeAction action; - CGPathNode(int3 Coord, ELayer Layer); + CGPathNode(); void reset(); + void update(const int3 & Coord, const ELayer Layer, const EAccessibility Accessible); bool reachable() const; }; @@ -78,14 +79,16 @@ struct DLL_LINKAGE CPathsInfo const CGHeroInstance * hero; int3 hpos; int3 sizes; - boost::multi_array nodes; //[w][h][level][layer] + boost::multi_array nodes; //[w][h][level][layer] CPathsInfo(const int3 & Sizes); ~CPathsInfo(); - const CGPathNode * getPathInfo(const int3 & tile, const ELayer layer = ELayer::AUTO) const; - bool getPath(CGPath & out, const int3 & dst, const ELayer layer = ELayer::AUTO) const; - int getDistance(const int3 & tile, const ELayer layer = ELayer::AUTO) const; - CGPathNode * getNode(const int3 & coord, const ELayer layer) 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 From 942c0cd718a94806cd848b812b6095314cdd1944 Mon Sep 17 00:00:00 2001 From: ArseniyShestakov Date: Thu, 12 Nov 2015 01:16:06 +0300 Subject: [PATCH 076/168] CPathfinder: improve support for whirlpools --- lib/CPathfinder.cpp | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/lib/CPathfinder.cpp b/lib/CPathfinder.cpp index 749c2d008..574f2bf31 100644 --- a/lib/CPathfinder.cpp +++ b/lib/CPathfinder.cpp @@ -248,7 +248,16 @@ void CPathfinder::addTeleportExits(bool noTeleportExcludes) for(auto objId : gs->getTeleportChannelExits(sTileTeleport->channel, hero->tempOwner)) { auto obj = getObj(objId); - if(CGTeleport::isExitPassable(gs, hero, obj)) + if(dynamic_cast(obj)) + { + auto pos = obj->getBlockedPos(); + for(auto p : pos) + { + if(gs->getTile(p)->topVisitableId() == obj->ID) + neighbours.push_back(p); + } + } + else if(CGTeleport::isExitPassable(gs, hero, obj)) neighbours.push_back(obj->visitablePos()); } } From 4e11d816737461f47c695f6d0832f7f97c48d240 Mon Sep 17 00:00:00 2001 From: ArseniyShestakov Date: Thu, 12 Nov 2015 01:23:27 +0300 Subject: [PATCH 077/168] CGWhirlpool: fix random exit chooser --- lib/mapObjects/MiscObjects.cpp | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/lib/mapObjects/MiscObjects.cpp b/lib/mapObjects/MiscObjects.cpp index ba56179cd..87b0d8671 100644 --- a/lib/mapObjects/MiscObjects.cpp +++ b/lib/mapObjects/MiscObjects.cpp @@ -1106,9 +1106,7 @@ void CGWhirlpool::teleportDialogAnswered(const CGHeroInstance *hero, ui32 answer { std::set tiles = obj->getBlockedPos(); auto & tile = *RandomGeneratorUtil::nextItem(tiles, cb->gameState()->getRandomGenerator()); - cb->moveHero(hero->id, tile + int3(1,0,0), true); - - cb->moveHero(hero->id,CGHeroInstance::convertPosition(obj->pos,true) - getVisitableOffset(), true); + cb->moveHero(hero->id, CGHeroInstance::convertPosition(tile, true), true); } } From 9ed9d94009930315fcda8c289047ebf080076294 Mon Sep 17 00:00:00 2001 From: ArseniyShestakov Date: Thu, 12 Nov 2015 05:20:32 +0300 Subject: [PATCH 078/168] TurnInfo: first step towards better abstraction --- lib/CPathfinder.cpp | 40 ++++++++++++++++++++++++---------------- lib/CPathfinder.h | 7 +++++-- 2 files changed, 29 insertions(+), 18 deletions(-) diff --git a/lib/CPathfinder.cpp b/lib/CPathfinder.cpp index 574f2bf31..b0fa4f9cb 100644 --- a/lib/CPathfinder.cpp +++ b/lib/CPathfinder.cpp @@ -109,7 +109,7 @@ void CPathfinder::calculatePaths() if(!movement) { hlp->updateTurnInfo(++turn); - movement = hlp->getMaxMovePoints(cp->layer); + movement = hlp->getMaxMovePoints(cp->layer, turn); } //add accessible neighbouring nodes to the queue @@ -140,7 +140,7 @@ void CPathfinder::calculatePaths() continue; destAction = getDestAction(); - int cost = CPathfinderHelper::getMovementCost(hero, cp->coord, dp->coord, movement, hlp->ti); + int cost = CPathfinderHelper::getMovementCost(hero, cp->coord, dp->coord, movement, hlp->getTurnInfo(turn)); int remains = movement - cost; if(destAction == CGPathNode::EMBARK || destAction == CGPathNode::DISEMBARK) { @@ -152,8 +152,8 @@ void CPathfinder::calculatePaths() { //occurs rarely, when hero with low movepoints tries to leave the road hlp->updateTurnInfo(++turnAtNextTile); - int moveAtNextTile = hlp->getMaxMovePoints(i); - cost = CPathfinderHelper::getMovementCost(hero, cp->coord, dp->coord, moveAtNextTile, hlp->ti); //cost must be updated, movement points changed :( + int moveAtNextTile = hlp->getMaxMovePoints(i, turnAtNextTile); + cost = CPathfinderHelper::getMovementCost(hero, cp->coord, dp->coord, moveAtNextTile, hlp->getTurnInfo(turnAtNextTile)); //cost must be updated, movement points changed :( remains = moveAtNextTile - cost; } @@ -701,6 +701,20 @@ bool CPathfinder::canVisitObject() const return cp->layer == ELayer::LAND || cp->layer == ELayer::SAIL; } +TurnInfo::TurnInfo(const CGHeroInstance * h, const int Turn) + : turn(Turn) +{ + maxMovePointsLand = h->maxMovePoints(true); + maxMovePointsWater = h->maxMovePoints(false); + bonusFlying = h->getBonusAtTurn(Bonus::FLYING_MOVEMENT, turn); + bonusWaterWalking = h->getBonusAtTurn(Bonus::WATER_WALKING, turn); +} + +int TurnInfo::getMaxMovePoints(const EPathfindingLayer layer) const +{ + return layer == EPathfindingLayer::SAIL ? maxMovePointsWater : maxMovePointsLand; +} + CPathfinderHelper::CPathfinderHelper(const CGHeroInstance * Hero) : ti(nullptr), hero(Hero) { @@ -716,26 +730,20 @@ void CPathfinderHelper::updateTurnInfo(const int turn) ti = turnsInfo[turn]; else { - ti = getTurnInfo(hero, turn); + ti = new TurnInfo(hero, turn); turnsInfo.push_back(ti); } } } -int CPathfinderHelper::getMaxMovePoints(const EPathfindingLayer layer) const +const TurnInfo * CPathfinderHelper::getTurnInfo(const int turn) const { - return layer == EPathfindingLayer::SAIL ? ti->maxMovePointsWater : ti->maxMovePointsLand; + return turnsInfo[turn]; } -TurnInfo * CPathfinderHelper::getTurnInfo(const CGHeroInstance * h, const int turn) +int CPathfinderHelper::getMaxMovePoints(const EPathfindingLayer layer, const int turn) const { - auto turnInfo = new TurnInfo; - turnInfo->turn = turn; - turnInfo->maxMovePointsLand = h->maxMovePoints(true); - turnInfo->maxMovePointsWater = h->maxMovePoints(false); - turnInfo->bonusFlying = h->getBonusAtTurn(Bonus::FLYING_MOVEMENT, turn); - turnInfo->bonusWaterWalking = h->getBonusAtTurn(Bonus::WATER_WALKING, turn); - return turnInfo; + return turnsInfo[turn]->getMaxMovePoints(layer); } void CPathfinderHelper::getNeighbours(CGameState * gs, const TerrainTile & srct, const int3 & tile, std::vector & vec, const boost::logic::tribool & onLand, const bool limitCoastSailing) @@ -783,7 +791,7 @@ int CPathfinderHelper::getMovementCost(const CGHeroInstance * h, const int3 & sr return 0; if(!ti) - ti = getTurnInfo(h); + ti = new TurnInfo(h); auto s = h->cb->getTile(src), d = h->cb->getTile(dst); int ret = h->getTileCost(*d, *s, ti); diff --git a/lib/CPathfinder.h b/lib/CPathfinder.h index 3c386308c..cdd263d28 100644 --- a/lib/CPathfinder.h +++ b/lib/CPathfinder.h @@ -199,6 +199,9 @@ struct TurnInfo int maxMovePointsWater; const Bonus * bonusFlying; const Bonus * bonusWaterWalking; + + TurnInfo(const CGHeroInstance * h, const int Turn = 0); + int getMaxMovePoints(const EPathfindingLayer layer) const; }; class DLL_LINKAGE CPathfinderHelper @@ -209,8 +212,8 @@ public: CPathfinderHelper(const CGHeroInstance * Hero); void updateTurnInfo(const int turn = 0); - int getMaxMovePoints(const EPathfindingLayer layer) const; - static TurnInfo * getTurnInfo(const CGHeroInstance * h, const int turn = 0); + const TurnInfo * getTurnInfo(const int turn) const; + int getMaxMovePoints(const EPathfindingLayer layer, const int turn) const; static void getNeighbours(CGameState * gs, const TerrainTile & srct, const int3 & tile, std::vector & vec, const boost::logic::tribool & onLand, const bool limitCoastSailing); From abc4ea272fc7f75614c96528767a21fe47da892e Mon Sep 17 00:00:00 2001 From: ArseniyShestakov Date: Thu, 12 Nov 2015 14:04:33 +0300 Subject: [PATCH 079/168] TurnInfo: store all bonuses and use TileInfo for everything Currently this going to break ONE_WEEK bonuses because those don't work with CWillLastDays selector. --- lib/CPathfinder.cpp | 74 +++++++++++++++++++------------ lib/CPathfinder.h | 22 ++++----- lib/mapObjects/CGHeroInstance.cpp | 23 +++++----- lib/mapObjects/CGHeroInstance.h | 5 +-- server/CGameHandler.cpp | 11 ++--- 5 files changed, 77 insertions(+), 58 deletions(-) diff --git a/lib/CPathfinder.cpp b/lib/CPathfinder.cpp index b0fa4f9cb..fc85d4118 100644 --- a/lib/CPathfinder.cpp +++ b/lib/CPathfinder.cpp @@ -50,11 +50,11 @@ CPathfinder::CPathfinder(CPathsInfo & _out, CGameState * _gs, const CGHeroInstan } hlp = make_unique(hero); - if(hlp->ti->bonusFlying) + if(hlp->hasBonusOfType(Bonus::FLYING_MOVEMENT)) options.useFlying = true; - if(hlp->ti->bonusWaterWalking) + if(hlp->hasBonusOfType(Bonus::WATER_WALKING)) options.useWaterWalking = true; - if(CGWhirlpool::isProtected(hero)) + if(hlp->hasBonusOfType(Bonus::WHIRLPOOL_PROTECTION)) options.useTeleportWhirlpool = true; initializeGraph(); @@ -109,7 +109,7 @@ void CPathfinder::calculatePaths() if(!movement) { hlp->updateTurnInfo(++turn); - movement = hlp->getMaxMovePoints(cp->layer, turn); + movement = hlp->getMaxMovePoints(cp->layer); } //add accessible neighbouring nodes to the queue @@ -140,11 +140,11 @@ void CPathfinder::calculatePaths() continue; destAction = getDestAction(); - int cost = CPathfinderHelper::getMovementCost(hero, cp->coord, dp->coord, movement, hlp->getTurnInfo(turn)); + int cost = CPathfinderHelper::getMovementCost(hero, cp->coord, dp->coord, movement, hlp->getTurnInfo()); int remains = movement - cost; if(destAction == CGPathNode::EMBARK || destAction == CGPathNode::DISEMBARK) { - remains = hero->movementPointsAfterEmbark(movement, cost, destAction - 1); + remains = hero->movementPointsAfterEmbark(movement, cost, destAction - 1, hlp->getTurnInfo()); cost = movement - remains; } int turnAtNextTile = turn; @@ -152,8 +152,8 @@ void CPathfinder::calculatePaths() { //occurs rarely, when hero with low movepoints tries to leave the road hlp->updateTurnInfo(++turnAtNextTile); - int moveAtNextTile = hlp->getMaxMovePoints(i, turnAtNextTile); - cost = CPathfinderHelper::getMovementCost(hero, cp->coord, dp->coord, moveAtNextTile, hlp->getTurnInfo(turnAtNextTile)); //cost must be updated, movement points changed :( + int moveAtNextTile = hlp->getMaxMovePoints(i); + cost = CPathfinderHelper::getMovementCost(hero, cp->coord, dp->coord, moveAtNextTile, hlp->getTurnInfo()); //cost must be updated, movement points changed :( remains = moveAtNextTile - cost; } @@ -285,13 +285,13 @@ bool CPathfinder::isLayerAvailable(const ELayer layer, const int turn) const switch(layer) { case ELayer::AIR: - if(!hlp->ti->bonusFlying) + if(!hlp->hasBonusOfType(Bonus::FLYING_MOVEMENT)) return false; break; case ELayer::WATER: - if(!hlp->ti->bonusWaterWalking) + if(!hlp->hasBonusOfType(Bonus::WATER_WALKING)) return false; break; @@ -701,47 +701,63 @@ bool CPathfinder::canVisitObject() const return cp->layer == ELayer::LAND || cp->layer == ELayer::SAIL; } -TurnInfo::TurnInfo(const CGHeroInstance * h, const int Turn) - : turn(Turn) +TurnInfo::TurnInfo(const CGHeroInstance * Hero, const int turn) + : hero(Hero), maxMovePointsLand(-1), maxMovePointsWater(-1) { - maxMovePointsLand = h->maxMovePoints(true); - maxMovePointsWater = h->maxMovePoints(false); - bonusFlying = h->getBonusAtTurn(Bonus::FLYING_MOVEMENT, turn); - bonusWaterWalking = h->getBonusAtTurn(Bonus::WATER_WALKING, turn); + bonuses = hero->getAllBonuses(Selector::days(turn), nullptr); +} + +bool TurnInfo::hasBonusOfType(Bonus::BonusType type, int subtype) const +{ + return bonuses->getFirst(Selector::type(type).And(Selector::subtype(subtype))); +} + +int TurnInfo::valOfBonuses(Bonus::BonusType type, int subtype) const +{ + return bonuses->valOfBonuses(Selector::type(type).And(Selector::subtype(subtype))); } int TurnInfo::getMaxMovePoints(const EPathfindingLayer layer) const { + if(maxMovePointsLand == -1) + maxMovePointsLand = hero->maxMovePoints(true, this); + if(maxMovePointsWater == -1) + maxMovePointsWater = hero->maxMovePoints(false, this); + return layer == EPathfindingLayer::SAIL ? maxMovePointsWater : maxMovePointsLand; } CPathfinderHelper::CPathfinderHelper(const CGHeroInstance * Hero) - : ti(nullptr), hero(Hero) + : turn(0), hero(Hero) { turnsInfo.reserve(16); updateTurnInfo(); } -void CPathfinderHelper::updateTurnInfo(const int turn) +void CPathfinderHelper::updateTurnInfo(const int Turn) { - if(!ti || ti->turn != turn) + if(turn != Turn) { - if(turn < turnsInfo.size()) - ti = turnsInfo[turn]; - else + turn = Turn; + if(turn >= turnsInfo.size()) { - ti = new TurnInfo(hero, turn); + auto ti = new TurnInfo(hero, turn); turnsInfo.push_back(ti); } } } -const TurnInfo * CPathfinderHelper::getTurnInfo(const int turn) const +const TurnInfo * CPathfinderHelper::getTurnInfo() const { return turnsInfo[turn]; } -int CPathfinderHelper::getMaxMovePoints(const EPathfindingLayer layer, const int turn) const +bool CPathfinderHelper::hasBonusOfType(const Bonus::BonusType type, const int subtype) const +{ + return turnsInfo[turn]->hasBonusOfType(type, subtype); +} + +int CPathfinderHelper::getMaxMovePoints(const EPathfindingLayer layer) const { return turnsInfo[turn]->getMaxMovePoints(layer); } @@ -796,17 +812,17 @@ int CPathfinderHelper::getMovementCost(const CGHeroInstance * h, const int3 & sr auto s = h->cb->getTile(src), d = h->cb->getTile(dst); int ret = h->getTileCost(*d, *s, ti); - if(d->blocked && ti->bonusFlying) + if(d->blocked && ti->hasBonusOfType(Bonus::FLYING_MOVEMENT)) { - ret *= (100.0 + ti->bonusFlying->val) / 100.0; + ret *= (100.0 + ti->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 && ti->bonusWaterWalking) + else if(!h->boat && ti->hasBonusOfType(Bonus::WATER_WALKING)) { - ret *= (100.0 + ti->bonusWaterWalking->val) / 100.0; + ret *= (100.0 + ti->valOfBonuses(Bonus::WATER_WALKING)) / 100.0; } } diff --git a/lib/CPathfinder.h b/lib/CPathfinder.h index cdd263d28..0a7083dc8 100644 --- a/lib/CPathfinder.h +++ b/lib/CPathfinder.h @@ -192,28 +192,30 @@ private: }; -struct TurnInfo +struct DLL_LINKAGE TurnInfo { - int turn; - int maxMovePointsLand; - int maxMovePointsWater; - const Bonus * bonusFlying; - const Bonus * bonusWaterWalking; + const CGHeroInstance * hero; + TBonusListPtr bonuses; + mutable int maxMovePointsLand; + mutable int maxMovePointsWater; - TurnInfo(const CGHeroInstance * h, const int Turn = 0); + TurnInfo(const CGHeroInstance * Hero, const int Turn = 0); + 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: - TurnInfo * ti; + int turn; const CGHeroInstance * hero; CPathfinderHelper(const CGHeroInstance * Hero); void updateTurnInfo(const int turn = 0); - const TurnInfo * getTurnInfo(const int turn) const; - int getMaxMovePoints(const EPathfindingLayer layer, const int turn) 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(CGameState * gs, const TerrainTile & srct, const int3 & tile, std::vector & vec, const boost::logic::tribool & onLand, const bool limitCoastSailing); diff --git a/lib/mapObjects/CGHeroInstance.cpp b/lib/mapObjects/CGHeroInstance.cpp index a031d080c..e6156b9bd 100644 --- a/lib/mapObjects/CGHeroInstance.cpp +++ b/lib/mapObjects/CGHeroInstance.cpp @@ -80,7 +80,7 @@ ui32 CGHeroInstance::getTileCost(const TerrainTile &dest, const TerrainTile &fro break; } } - else if(!getBonusAtTurn(Bonus::NO_TERRAIN_PENALTY, ti->turn, from.terType)) + else if(!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. @@ -129,11 +129,6 @@ int3 CGHeroInstance::getPosition(bool h3m) const //h3m=true - returns position o } } -const Bonus * CGHeroInstance::getBonusAtTurn(const Bonus::BonusType &type, const int &turn, const TBonusSubtype &subType) const -{ - return getBonus(Selector::type(type).And(Selector::days(turn)).And(Selector::subtype(subType))); -} - ui8 CGHeroInstance::getSecSkillLevel(SecondarySkill skill) const { for(auto & elem : secSkills) @@ -176,8 +171,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) @@ -196,10 +194,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; } @@ -1166,9 +1164,12 @@ 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)) + if(!ti) + ti = new TurnInfo(this); + + if(ti->hasBonusOfType(Bonus::FREE_SHIP_BOARDING)) return (MPsBefore - basicCost) * static_cast(maxMovePoints(disembark)) / maxMovePoints(!disembark); return 0; //take all MPs otherwise diff --git a/lib/mapObjects/CGHeroInstance.h b/lib/mapObjects/CGHeroInstance.h index 0c6056664..9b9b59d23 100644 --- a/lib/mapObjects/CGHeroInstance.h +++ b/lib/mapObjects/CGHeroInstance.h @@ -134,7 +134,6 @@ public: 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 - const Bonus * getBonusAtTurn(const Bonus::BonusType &type, const int &turn = 0, const TBonusSubtype &subType = -1) 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 +160,8 @@ public: void setSecSkillLevel(SecondarySkill which, int val, bool abs);// abs == 0 - changes by value; 1 - sets to value void levelUp(std::vector 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 diff --git a/server/CGameHandler.cpp b/server/CGameHandler.cpp index 9032cc181..4fa958575 100644 --- a/server/CGameHandler.cpp +++ b/server/CGameHandler.cpp @@ -1778,9 +1778,10 @@ bool CGameHandler::moveHero( ObjectInstanceID hid, int3 dst, ui8 teleporting, bo tmh.movePoints = h->movement; //check if destination tile is available - const bool canFly = h->getBonusAtTurn(Bonus::FLYING_MOVEMENT); - const bool canWalkOnSea = h->getBonusAtTurn(Bonus::WATER_WALKING); - const int cost = CPathfinderHelper::getMovementCost(h, h->getPosition(), hmpos, h->movement); + 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, 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) @@ -1872,14 +1873,14 @@ bool CGameHandler::moveHero( ObjectInstanceID hid, int3 dst, ui8 teleporting, bo 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); } From d2baa5b0d0d057f1a0eec8ea2eeb2283582865e0 Mon Sep 17 00:00:00 2001 From: ArseniyShestakov Date: Thu, 12 Nov 2015 14:39:22 +0300 Subject: [PATCH 080/168] Pathfinding: move isLayerAvailable into TurnInfo --- lib/CPathfinder.cpp | 49 +++++++++++++++++++++++++-------------------- lib/CPathfinder.h | 8 ++++---- 2 files changed, 31 insertions(+), 26 deletions(-) diff --git a/lib/CPathfinder.cpp b/lib/CPathfinder.cpp index fc85d4118..014066bad 100644 --- a/lib/CPathfinder.cpp +++ b/lib/CPathfinder.cpp @@ -130,7 +130,7 @@ void CPathfinder::calculatePaths() if(!passOneTurnLimitCheck(cp->turns != turn)) continue; - if(!isLayerAvailable(i, turn)) + if(!hlp->isLayerAvailable(i)) continue; if(cp->layer != i && !isLayerTransitionPossible()) @@ -280,26 +280,6 @@ void CPathfinder::addTeleportExits(bool noTeleportExcludes) } } -bool CPathfinder::isLayerAvailable(const ELayer layer, const int turn) const -{ - switch(layer) - { - case ELayer::AIR: - if(!hlp->hasBonusOfType(Bonus::FLYING_MOVEMENT)) - return false; - - break; - - case ELayer::WATER: - if(!hlp->hasBonusOfType(Bonus::WATER_WALKING)) - return false; - - break; - } - - return true; -} - bool CPathfinder::isLayerTransitionPossible() const { /// No layer transition allowed when previous node action is BATTLE @@ -707,6 +687,26 @@ TurnInfo::TurnInfo(const CGHeroInstance * Hero, const int turn) bonuses = hero->getAllBonuses(Selector::days(turn), nullptr); } +bool TurnInfo::isLayerAvailable(const EPathfindingLayer layer) const +{ + switch(layer) + { + case EPathfindingLayer::AIR: + if(!hasBonusOfType(Bonus::FLYING_MOVEMENT)) + return false; + + break; + + case EPathfindingLayer::WATER: + if(!hasBonusOfType(Bonus::WATER_WALKING)) + return false; + + break; + } + + return true; +} + bool TurnInfo::hasBonusOfType(Bonus::BonusType type, int subtype) const { return bonuses->getFirst(Selector::type(type).And(Selector::subtype(subtype))); @@ -728,7 +728,7 @@ int TurnInfo::getMaxMovePoints(const EPathfindingLayer layer) const } CPathfinderHelper::CPathfinderHelper(const CGHeroInstance * Hero) - : turn(0), hero(Hero) + : turn(-1), hero(Hero) { turnsInfo.reserve(16); updateTurnInfo(); @@ -747,6 +747,11 @@ void CPathfinderHelper::updateTurnInfo(const int Turn) } } +bool CPathfinderHelper::isLayerAvailable(const EPathfindingLayer layer) const +{ + return turnsInfo[turn]->isLayerAvailable(layer); +} + const TurnInfo * CPathfinderHelper::getTurnInfo() const { return turnsInfo[turn]; diff --git a/lib/CPathfinder.h b/lib/CPathfinder.h index 0a7083dc8..5c2377e48 100644 --- a/lib/CPathfinder.h +++ b/lib/CPathfinder.h @@ -166,7 +166,6 @@ private: void addNeighbours(const int3 & coord); void addTeleportExits(bool noTeleportExcludes = false); - bool isLayerAvailable(const ELayer layer, const int turn) const; bool isLayerTransitionPossible() const; bool isMovementToDestPossible() const; bool isMovementAfterDestPossible() const; @@ -200,6 +199,7 @@ struct DLL_LINKAGE TurnInfo mutable int maxMovePointsWater; 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; @@ -208,11 +208,9 @@ struct DLL_LINKAGE TurnInfo class DLL_LINKAGE CPathfinderHelper { public: - int turn; - const CGHeroInstance * hero; - CPathfinderHelper(const CGHeroInstance * Hero); 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; @@ -223,5 +221,7 @@ public: static int getMovementCost(const CGHeroInstance * h, const int3 & dst); private: + int turn; + const CGHeroInstance * hero; std::vector turnsInfo; }; From 0be9d21132a85a3ef12a88e4c26f01f30b7eeecc Mon Sep 17 00:00:00 2001 From: ArseniyShestakov Date: Fri, 13 Nov 2015 10:28:44 +0300 Subject: [PATCH 081/168] CPathfinder: add rule that hero in boat can't visit empty boats --- lib/CPathfinder.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/lib/CPathfinder.cpp b/lib/CPathfinder.cpp index 014066bad..9c5b0ed8a 100644 --- a/lib/CPathfinder.cpp +++ b/lib/CPathfinder.cpp @@ -387,6 +387,11 @@ bool CPathfinder::isMovementToDestPossible() const if(dObj->ID != Obj::BOAT && dObj->ID != Obj::HERO) return false; } + else if(dObj && dObj->ID == Obj::BOAT) + { + /// Hero in boat can't visit empty boats + return false; + } break; From 324cf5490c7d745d89952db4667cc70f464720bc Mon Sep 17 00:00:00 2001 From: ArseniyShestakov Date: Fri, 13 Nov 2015 23:07:56 +0300 Subject: [PATCH 082/168] CPathfinder: fix movement into guardian tile --- lib/CPathfinder.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/CPathfinder.cpp b/lib/CPathfinder.cpp index 9c5b0ed8a..906281556 100644 --- a/lib/CPathfinder.cpp +++ b/lib/CPathfinder.cpp @@ -443,8 +443,8 @@ bool CPathfinder::isMovementAfterDestPossible() const break; case CGPathNode::BATTLE: - /// Movement after BATTLE action only possible to guardian tile - if(isDestinationGuardian()) + /// Movement after BATTLE action only possible from guarded tile to guardian tile + if(isDestinationGuarded()) return true; break; From aa59ad05eda62062aa971071dfe3b98e06cd6220 Mon Sep 17 00:00:00 2001 From: ArseniyShestakov Date: Fri, 13 Nov 2015 23:32:52 +0300 Subject: [PATCH 083/168] CPathfinder: don't access map->guardingCreaturePositions directly --- lib/CPathfinder.cpp | 14 ++++---------- lib/CPathfinder.h | 1 - 2 files changed, 4 insertions(+), 11 deletions(-) diff --git a/lib/CPathfinder.cpp b/lib/CPathfinder.cpp index 906281556..042d7d79b 100644 --- a/lib/CPathfinder.cpp +++ b/lib/CPathfinder.cpp @@ -519,11 +519,6 @@ bool CPathfinder::isSourceInitialPosition() const return cp->coord == out.hpos; } -int3 CPathfinder::getSourceGuardPosition() const -{ - return gs->map->guardingCreaturePositions[cp->coord.x][cp->coord.y][cp->coord.z]; -} - bool CPathfinder::isSourceGuarded() const { /// Hero can move from guarded tile if movement started on that tile @@ -531,7 +526,7 @@ bool CPathfinder::isSourceGuarded() const /// - Map start with hero on guarded tile /// - Dimention door used /// TODO: check what happen when there is several guards - if(getSourceGuardPosition() != int3(-1, -1, -1) && !isSourceInitialPosition()) + if(gs->guardingCreaturePosition(cp->coord) != int3(-1, -1, -1) && !isSourceInitialPosition()) { return true; } @@ -541,7 +536,7 @@ bool CPathfinder::isSourceGuarded() const bool CPathfinder::isDestinationGuarded(const bool ignoreAccessibility) const { - if(gs->map->guardingCreaturePositions[dp->coord.x][dp->coord.y][dp->coord.z].valid() + if(gs->guardingCreaturePosition(dp->coord).valid() && (ignoreAccessibility || dp->accessible == CGPathNode::BLOCKVIS)) { return true; @@ -552,7 +547,7 @@ bool CPathfinder::isDestinationGuarded(const bool ignoreAccessibility) const bool CPathfinder::isDestinationGuardian() const { - return getSourceGuardPosition() == dp->coord; + return gs->guardingCreaturePosition(cp->coord) == dp->coord; } void CPathfinder::initializeGraph() @@ -633,8 +628,7 @@ CGPathNode::EAccessibility CPathfinder::evaluateAccessibility(const int3 & pos, } } } - else if(gs->map->guardingCreaturePositions[pos.x][pos.y][pos.z].valid() - && !tinfo->blocked) + else if(gs->guardingCreaturePosition(pos).valid() && !tinfo->blocked) { // Monster close by; blocked visit for battle. return CGPathNode::BLOCKVIS; diff --git a/lib/CPathfinder.h b/lib/CPathfinder.h index 5c2377e48..97164cee8 100644 --- a/lib/CPathfinder.h +++ b/lib/CPathfinder.h @@ -172,7 +172,6 @@ private: CGPathNode::ENodeAction getDestAction() const; bool isSourceInitialPosition() const; - int3 getSourceGuardPosition() const; bool isSourceGuarded() const; bool isDestinationGuarded(const bool ignoreAccessibility = true) const; bool isDestinationGuardian() const; From d6bb599bc89435f7d7b6d1a8423a5b29c26e87a8 Mon Sep 17 00:00:00 2001 From: AlexVinS Date: Sat, 14 Nov 2015 03:59:16 +0300 Subject: [PATCH 084/168] Another ffmpeg api fix --- client/CVideoHandler.h | 35 ++++++++++++++++------------------- 1 file changed, 16 insertions(+), 19 deletions(-) diff --git a/client/CVideoHandler.h b/client/CVideoHandler.h index daa89cf05..a52600125 100644 --- a/client/CVideoHandler.h +++ b/client/CVideoHandler.h @@ -54,22 +54,7 @@ extern "C" { } //compatibility for libav 9.18 in ubuntu 14.04, 52.66.100 is ffmpeg 2.2.3 -#if LIBAVUTIL_VERSION_INT < AV_VERSION_INT(52, 66, 100) -#define VCMI_USE_OLD_AVUTIL -#endif // LIBSWSCALE_VERSION_INT - -#ifdef VCMI_USE_OLD_AVUTIL - -#define AVPixelFormat PixelFormat -#define AV_PIX_FMT_NONE PIX_FMT_NONE -#define AV_PIX_FMT_YUV420P PIX_FMT_YUV420P -#define AV_PIX_FMT_BGR565 PIX_FMT_BGR565 -#define AV_PIX_FMT_BGR24 PIX_FMT_BGR24 -#define AV_PIX_FMT_BGR32 PIX_FMT_BGR32 -#define AV_PIX_FMT_RGB565 PIX_FMT_RGB565 -#define AV_PIX_FMT_RGB24 PIX_FMT_RGB24 -#define AV_PIX_FMT_RGB32 PIX_FMT_RGB32 - +#if (LIBAVUTIL_VERSION_INT < AV_VERSION_INT(52, 66, 100)) inline AVFrame * av_frame_alloc() { return avcodec_alloc_frame(); @@ -80,16 +65,28 @@ inline void av_frame_free(AVFrame ** frame) av_free(*frame); *frame = nullptr; } - #endif // VCMI_USE_OLD_AVUTIL +//fix for travis-ci +#if (LIBAVUTIL_VERSION_INT < AV_VERSION_INT(52, 0, 0)) + #define AVPixelFormat PixelFormat + #define AV_PIX_FMT_NONE PIX_FMT_NONE + #define AV_PIX_FMT_YUV420P PIX_FMT_YUV420P + #define AV_PIX_FMT_BGR565 PIX_FMT_BGR565 + #define AV_PIX_FMT_BGR24 PIX_FMT_BGR24 + #define AV_PIX_FMT_BGR32 PIX_FMT_BGR32 + #define AV_PIX_FMT_RGB565 PIX_FMT_RGB565 + #define AV_PIX_FMT_RGB24 PIX_FMT_RGB24 + #define AV_PIX_FMT_RGB32 PIX_FMT_RGB32 +#endif + class CVideoPlayer : public IMainVideoPlayer { int stream; // stream index in video AVFormatContext *format; AVCodecContext *codecContext; // codec context for stream AVCodec *codec; - AVFrame *frame; + AVFrame *frame; struct SwsContext *sws; AVIOContext * context; @@ -120,7 +117,7 @@ public: void show(int x, int y, SDL_Surface *dst, bool update = true) override; //blit current frame void redraw(int x, int y, SDL_Surface *dst, bool update = true) override; //reblits buffer void update(int x, int y, SDL_Surface *dst, bool forceRedraw, bool update = true) override; //moves to next frame if appropriate, and blits it or blits only if redraw parameter is set true - + // Opens video, calls playVideo, closes video; returns playVideo result (if whole video has been played) bool openAndPlayVideo(std::string name, int x, int y, SDL_Surface *dst, bool stopOnKey = false, bool scale = false) override; From 6dd957264409e1f71122a899eca7780965f09c57 Mon Sep 17 00:00:00 2001 From: ArseniyShestakov Date: Mon, 16 Nov 2015 17:36:58 +0300 Subject: [PATCH 085/168] CPathfinder: cleanup checks for source node visitable object --- lib/CPathfinder.cpp | 59 ++++++++++++++++++++------------------------- lib/CPathfinder.h | 3 +-- 2 files changed, 27 insertions(+), 35 deletions(-) diff --git a/lib/CPathfinder.cpp b/lib/CPathfinder.cpp index 042d7d79b..1c4640d05 100644 --- a/lib/CPathfinder.cpp +++ b/lib/CPathfinder.cpp @@ -102,7 +102,7 @@ void CPathfinder::calculatePaths() pq.pop(); cp->locked = true; ct = &gs->map->getTile(cp->coord); - cObj = ct->topVisitableObj(cp->coord == out.hpos); + cObj = ct->topVisitableObj(isSourceInitialPosition()); int movement = cp->moveRemains, turn = cp->turns; hlp->updateTurnInfo(turn); @@ -173,23 +173,20 @@ void CPathfinder::calculatePaths() } //neighbours loop //just add all passable teleport exits - if(cObj && canVisitObject()) + addTeleportExits(); + for(auto & neighbour : neighbours) { - addTeleportExits(); - for(auto & neighbour : neighbours) - { - dp = out.getNode(neighbour, cp->layer); - if(dp->locked) - continue; + dp = out.getNode(neighbour, cp->layer); + if(dp->locked) + continue; - if(isBetterWay(movement, turn)) - { - dp->moveRemains = movement; - dp->turns = turn; - dp->theNodeBefore = cp; - dp->action = CGPathNode::NORMAL; - pq.push(dp); - } + if(isBetterWay(movement, turn)) + { + dp->moveRemains = movement; + dp->turns = turn; + dp->theNodeBefore = cp; + dp->action = CGPathNode::NORMAL; + pq.push(dp); } } } //queue loop @@ -200,18 +197,13 @@ void CPathfinder::addNeighbours(const int3 & coord) neighbours.clear(); std::vector tiles; CPathfinderHelper::getNeighbours(gs, *ct, coord, tiles, boost::logic::indeterminate, cp->layer == ELayer::SAIL); // TODO: find out if we still need "limitCoastSailing" option - if(canVisitObject()) + if(isSourceVisitableObj()) { - if(cObj) + for(int3 tile: tiles) { - for(int3 tile: tiles) - { - if(canMoveBetween(tile, cObj->visitablePos())) - neighbours.push_back(tile); - } + if(canMoveBetween(tile, cObj->visitablePos())) + neighbours.push_back(tile); } - else - vstd::concatenate(neighbours, tiles); } else vstd::concatenate(neighbours, tiles); @@ -219,9 +211,10 @@ void CPathfinder::addNeighbours(const int3 & coord) void CPathfinder::addTeleportExits(bool noTeleportExcludes) { - assert(cObj); - neighbours.clear(); + if(!isSourceVisitableObj()) + return; + auto isAllowedTeleportEntrance = [&](const CGTeleport * obj) -> bool { if(!gs->isTeleportEntrancePassable(obj, hero->tempOwner)) @@ -519,6 +512,12 @@ bool CPathfinder::isSourceInitialPosition() const return cp->coord == out.hpos; } +bool CPathfinder::isSourceVisitableObj() const +{ + /// Hero can't visit objects while walking on water or flying + return cObj != nullptr && (cp->layer == ELayer::LAND || cp->layer == ELayer::SAIL); +} + bool CPathfinder::isSourceGuarded() const { /// Hero can move from guarded tile if movement started on that tile @@ -674,12 +673,6 @@ bool CPathfinder::addTeleportWhirlpool(const CGWhirlpool * obj) const return options.useTeleportWhirlpool && obj; } -bool CPathfinder::canVisitObject() const -{ - //hero can't visit objects while walking on water or flying - return cp->layer == ELayer::LAND || cp->layer == ELayer::SAIL; -} - TurnInfo::TurnInfo(const CGHeroInstance * Hero, const int turn) : hero(Hero), maxMovePointsLand(-1), maxMovePointsWater(-1) { diff --git a/lib/CPathfinder.h b/lib/CPathfinder.h index 97164cee8..c29223e6a 100644 --- a/lib/CPathfinder.h +++ b/lib/CPathfinder.h @@ -172,6 +172,7 @@ private: CGPathNode::ENodeAction getDestAction() const; bool isSourceInitialPosition() const; + bool isSourceVisitableObj() const; bool isSourceGuarded() const; bool isDestinationGuarded(const bool ignoreAccessibility = true) const; bool isDestinationGuardian() const; @@ -186,8 +187,6 @@ private: bool addTeleportOneWayRandom(const CGTeleport * obj) const; bool addTeleportWhirlpool(const CGWhirlpool * obj) const; - bool canVisitObject() const; - }; struct DLL_LINKAGE TurnInfo From 3185b64fb006424b911a044d9a0ca9b37fdfb1e4 Mon Sep 17 00:00:00 2001 From: ArseniyShestakov Date: Mon, 16 Nov 2015 17:41:23 +0300 Subject: [PATCH 086/168] CPathfinder: rename cObj and dObjt to ctObj and dtObj This way it's more clear that it's pointer to object on tile and not path node. It's important because air layer nodes don't have visitable objects while tile under them can have one. --- lib/CPathfinder.cpp | 44 ++++++++++++++++++++++---------------------- lib/CPathfinder.h | 2 +- 2 files changed, 23 insertions(+), 23 deletions(-) diff --git a/lib/CPathfinder.cpp b/lib/CPathfinder.cpp index 1c4640d05..b31b40601 100644 --- a/lib/CPathfinder.cpp +++ b/lib/CPathfinder.cpp @@ -102,7 +102,7 @@ void CPathfinder::calculatePaths() pq.pop(); cp->locked = true; ct = &gs->map->getTile(cp->coord); - cObj = ct->topVisitableObj(isSourceInitialPosition()); + ctObj = ct->topVisitableObj(isSourceInitialPosition()); int movement = cp->moveRemains, turn = cp->turns; hlp->updateTurnInfo(turn); @@ -117,7 +117,7 @@ void CPathfinder::calculatePaths() for(auto & neighbour : neighbours) { dt = &gs->map->getTile(neighbour); - dObj = dt->topVisitableObj(); + dtObj = dt->topVisitableObj(); for(ELayer i = ELayer::LAND; i <= ELayer::AIR; i.advance(1)) { dp = out.getNode(neighbour, i); @@ -201,7 +201,7 @@ void CPathfinder::addNeighbours(const int3 & coord) { for(int3 tile: tiles) { - if(canMoveBetween(tile, cObj->visitablePos())) + if(canMoveBetween(tile, ctObj->visitablePos())) neighbours.push_back(tile); } } @@ -235,7 +235,7 @@ void CPathfinder::addTeleportExits(bool noTeleportExcludes) return false; }; - const CGTeleport * sTileTeleport = dynamic_cast(cObj); + const CGTeleport * sTileTeleport = dynamic_cast(ctObj); if(isAllowedTeleportEntrance(sTileTeleport)) { for(auto objId : gs->getTeleportChannelExits(sTileTeleport->channel, hero->tempOwner)) @@ -256,15 +256,15 @@ void CPathfinder::addTeleportExits(bool noTeleportExcludes) } if(options.useCastleGate - && (cObj->ID == Obj::TOWN && cObj->subID == ETownType::INFERNO - && getPlayerRelations(hero->tempOwner, cObj->tempOwner) != PlayerRelations::ENEMIES)) + && (ctObj->ID == Obj::TOWN && ctObj->subID == ETownType::INFERNO + && getPlayerRelations(hero->tempOwner, ctObj->tempOwner) != PlayerRelations::ENEMIES)) { /// TODO: Find way to reuse CPlayerSpecificInfoCallback::getTownsInfo /// This may be handy if we allow to use teleportation to friendly towns auto towns = gs->getPlayer(hero->tempOwner)->towns; for(const auto & town : towns) { - if(town->id != cObj->id && town->visitingHero == nullptr + if(town->id != ctObj->id && town->visitingHero == nullptr && town->hasBuilt(BuildingID::CASTLE_GATE, ETownType::INFERNO)) { neighbours.push_back(town->visitablePos()); @@ -374,13 +374,13 @@ bool CPathfinder::isMovementToDestPossible() const if(cp->layer == ELayer::LAND) { - if(!dObj) + if(!dtObj) return false; - if(dObj->ID != Obj::BOAT && dObj->ID != Obj::HERO) + if(dtObj->ID != Obj::BOAT && dtObj->ID != Obj::HERO) return false; } - else if(dObj && dObj->ID == Obj::BOAT) + else if(dtObj && dtObj->ID == Obj::BOAT) { /// Hero in boat can't visit empty boats return false; @@ -409,11 +409,11 @@ bool CPathfinder::isMovementAfterDestPossible() const case CGPathNode::VISIT: /// For now we only add visitable tile into queue when it's teleporter that allow transit /// Movement from visitable tile when hero is standing on it is possible into any layer - if(CGTeleport::isTeleport(dObj)) + if(CGTeleport::isTeleport(dtObj)) { /// For now we'll always allow transit over teleporters /// Transit over whirlpools only allowed when hero protected - auto whirlpool = dynamic_cast(dObj); + auto whirlpool = dynamic_cast(dtObj); if(!whirlpool || options.useTeleportWhirlpool) return true; } @@ -460,34 +460,34 @@ CGPathNode::ENodeAction CPathfinder::getDestAction() const } case ELayer::SAIL: - if(dObj) + if(dtObj) { - auto objRel = getPlayerRelations(dObj->tempOwner, hero->tempOwner); + auto objRel = getPlayerRelations(dtObj->tempOwner, hero->tempOwner); - if(dObj->ID == Obj::BOAT) + if(dtObj->ID == Obj::BOAT) action = CGPathNode::EMBARK; - else if(dObj->ID == Obj::HERO) + else if(dtObj->ID == Obj::HERO) { if(objRel == PlayerRelations::ENEMIES) action = CGPathNode::BATTLE; else action = CGPathNode::BLOCKING_VISIT; } - else if(dObj->ID == Obj::TOWN && objRel == PlayerRelations::ENEMIES) + else if(dtObj->ID == Obj::TOWN && objRel == PlayerRelations::ENEMIES) { - const CGTownInstance * townObj = dynamic_cast(dObj); + const CGTownInstance * townObj = dynamic_cast(dtObj); if(townObj->armedGarrison()) action = CGPathNode::BATTLE; } - else if(dObj->ID == Obj::GARRISON || dObj->ID == Obj::GARRISON2) + else if(dtObj->ID == Obj::GARRISON || dtObj->ID == Obj::GARRISON2) { - const CGGarrison * garrisonObj = dynamic_cast(dObj); + const CGGarrison * garrisonObj = dynamic_cast(dtObj); if((garrisonObj->stacksCount() && objRel == PlayerRelations::ENEMIES) || isDestinationGuarded(true)) action = CGPathNode::BATTLE; } else if(isDestinationGuardian()) action = CGPathNode::BATTLE; - else if(dObj->blockVisit && (!options.useCastleGate || dObj->ID != Obj::TOWN)) + else if(dtObj->blockVisit && (!options.useCastleGate || dtObj->ID != Obj::TOWN)) action = CGPathNode::BLOCKING_VISIT; if(action == CGPathNode::NORMAL) @@ -515,7 +515,7 @@ bool CPathfinder::isSourceInitialPosition() const bool CPathfinder::isSourceVisitableObj() const { /// Hero can't visit objects while walking on water or flying - return cObj != nullptr && (cp->layer == ELayer::LAND || cp->layer == ELayer::SAIL); + return ctObj != nullptr && (cp->layer == ELayer::LAND || cp->layer == ELayer::SAIL); } bool CPathfinder::isSourceGuarded() const diff --git a/lib/CPathfinder.h b/lib/CPathfinder.h index c29223e6a..cf9c0e42b 100644 --- a/lib/CPathfinder.h +++ b/lib/CPathfinder.h @@ -160,7 +160,7 @@ private: 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 * cObj, * dObj; + const CGObjectInstance * ctObj, * dtObj; CGPathNode::ENodeAction destAction; void addNeighbours(const int3 & coord); From 0949283cb9f66ea3cfcf6ba908079c0deffa9f17 Mon Sep 17 00:00:00 2001 From: ArseniyShestakov Date: Mon, 16 Nov 2015 18:43:02 +0300 Subject: [PATCH 087/168] Pathfinding: pass PathfinderOptions to helper and avoid changing them Original idea behind options is that options should only be set on pathfinder object creation (or via some special method in case pathfinder become persistent). --- lib/CPathfinder.cpp | 33 +++++++++++++++++++++------------ lib/CPathfinder.h | 5 ++++- 2 files changed, 25 insertions(+), 13 deletions(-) diff --git a/lib/CPathfinder.cpp b/lib/CPathfinder.cpp index b31b40601..be96bbeb6 100644 --- a/lib/CPathfinder.cpp +++ b/lib/CPathfinder.cpp @@ -20,13 +20,13 @@ CPathfinder::PathfinderOptions::PathfinderOptions() { - useFlying = false; - useWaterWalking = false; + useFlying = true; + useWaterWalking = true; useEmbarkAndDisembark = true; useTeleportTwoWay = true; useTeleportOneWay = true; useTeleportOneWayRandom = false; - useTeleportWhirlpool = false; + useTeleportWhirlpool = true; useCastleGate = false; @@ -49,13 +49,7 @@ CPathfinder::CPathfinder(CPathsInfo & _out, CGameState * _gs, const CGHeroInstan throw std::runtime_error("Wrong checksum"); } - hlp = make_unique(hero); - if(hlp->hasBonusOfType(Bonus::FLYING_MOVEMENT)) - options.useFlying = true; - if(hlp->hasBonusOfType(Bonus::WATER_WALKING)) - options.useWaterWalking = true; - if(hlp->hasBonusOfType(Bonus::WHIRLPOOL_PROTECTION)) - options.useTeleportWhirlpool = true; + hlp = make_unique(hero, options); initializeGraph(); neighbours.reserve(16); @@ -719,8 +713,8 @@ int TurnInfo::getMaxMovePoints(const EPathfindingLayer layer) const return layer == EPathfindingLayer::SAIL ? maxMovePointsWater : maxMovePointsLand; } -CPathfinderHelper::CPathfinderHelper(const CGHeroInstance * Hero) - : turn(-1), hero(Hero) +CPathfinderHelper::CPathfinderHelper(const CGHeroInstance * Hero, const CPathfinder::PathfinderOptions & Options) + : turn(-1), hero(Hero), options(Options) { turnsInfo.reserve(16); updateTurnInfo(); @@ -741,6 +735,21 @@ void CPathfinderHelper::updateTurnInfo(const int Turn) bool CPathfinderHelper::isLayerAvailable(const EPathfindingLayer layer) const { + switch(layer) + { + case EPathfindingLayer::AIR: + if(!options.useFlying) + return false; + + break; + + case EPathfindingLayer::WATER: + if(!options.useWaterWalking) + return false; + + break; + } + return turnsInfo[turn]->isLayerAvailable(layer); } diff --git a/lib/CPathfinder.h b/lib/CPathfinder.h index cf9c0e42b..6f325d204 100644 --- a/lib/CPathfinder.h +++ b/lib/CPathfinder.h @@ -94,6 +94,8 @@ struct DLL_LINKAGE CPathsInfo class CPathfinder : private CGameInfoCallback { public: + 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 @@ -206,7 +208,7 @@ struct DLL_LINKAGE TurnInfo class DLL_LINKAGE CPathfinderHelper { public: - CPathfinderHelper(const CGHeroInstance * Hero); + CPathfinderHelper(const CGHeroInstance * Hero, const CPathfinder::PathfinderOptions & Options); void updateTurnInfo(const int turn = 0); bool isLayerAvailable(const EPathfindingLayer layer) const; const TurnInfo * getTurnInfo() const; @@ -222,4 +224,5 @@ private: int turn; const CGHeroInstance * hero; std::vector turnsInfo; + const CPathfinder::PathfinderOptions & options; }; From 8f72d7324132c12639190d68af79903132f842f7 Mon Sep 17 00:00:00 2001 From: ArseniyShestakov Date: Mon, 16 Nov 2015 19:14:18 +0300 Subject: [PATCH 088/168] CPathfinder: update teleport code and use TurnInfo for whirlpools --- lib/CPathfinder.cpp | 56 ++++++++++++++++++++++----------------------- lib/CPathfinder.h | 3 ++- 2 files changed, 29 insertions(+), 30 deletions(-) diff --git a/lib/CPathfinder.cpp b/lib/CPathfinder.cpp index be96bbeb6..e94479fbc 100644 --- a/lib/CPathfinder.cpp +++ b/lib/CPathfinder.cpp @@ -203,36 +203,16 @@ void CPathfinder::addNeighbours(const int3 & coord) vstd::concatenate(neighbours, tiles); } -void CPathfinder::addTeleportExits(bool noTeleportExcludes) +void CPathfinder::addTeleportExits() { neighbours.clear(); if(!isSourceVisitableObj()) return; - auto isAllowedTeleportEntrance = [&](const CGTeleport * obj) -> bool + const CGTeleport * objTeleport = dynamic_cast(ctObj); + if(isAllowedTeleportEntrance(objTeleport)) { - if(!gs->isTeleportEntrancePassable(obj, hero->tempOwner)) - return false; - - if(noTeleportExcludes) - return true; - - auto whirlpool = dynamic_cast(obj); - if(whirlpool) - { - if(addTeleportWhirlpool(whirlpool)) - return true; - } - else if(addTeleportTwoWay(obj) || addTeleportOneWay(obj) || addTeleportOneWayRandom(obj)) - return true; - - return false; - }; - - const CGTeleport * sTileTeleport = dynamic_cast(ctObj); - if(isAllowedTeleportEntrance(sTileTeleport)) - { - for(auto objId : gs->getTeleportChannelExits(sTileTeleport->channel, hero->tempOwner)) + for(auto objId : gs->getTeleportChannelExits(objTeleport->channel, hero->tempOwner)) { auto obj = getObj(objId); if(dynamic_cast(obj)) @@ -401,18 +381,19 @@ bool CPathfinder::isMovementAfterDestPossible() const /// TODO: Investigate what kind of limitation is possible to apply on movement from visitable tiles /// Likely in many cases we don't need to add visitable tile to queue when hero don't fly case CGPathNode::VISIT: + { /// For now we only add visitable tile into queue when it's teleporter that allow transit /// Movement from visitable tile when hero is standing on it is possible into any layer - if(CGTeleport::isTeleport(dtObj)) + const CGTeleport * objTeleport = dynamic_cast(dtObj); + if(isAllowedTeleportEntrance(objTeleport)) { /// For now we'll always allow transit over teleporters /// Transit over whirlpools only allowed when hero protected - auto whirlpool = dynamic_cast(dtObj); - if(!whirlpool || options.useTeleportWhirlpool) - return true; + return true; } break; + } case CGPathNode::NORMAL: return true; @@ -635,6 +616,23 @@ bool CPathfinder::canMoveBetween(const int3 & a, const int3 & b) const return gs->checkForVisitableDir(a, b); } +bool CPathfinder::isAllowedTeleportEntrance(const CGTeleport * obj) const +{ + if(!obj || !gs->isTeleportEntrancePassable(obj, hero->tempOwner)) + return false; + + auto whirlpool = dynamic_cast(obj); + if(whirlpool) + { + if(addTeleportWhirlpool(whirlpool)) + return true; + } + else if(addTeleportTwoWay(obj) || addTeleportOneWay(obj) || addTeleportOneWayRandom(obj)) + return true; + + return false; +} + bool CPathfinder::addTeleportTwoWay(const CGTeleport * obj) const { return options.useTeleportTwoWay && gs->isTeleportChannelBidirectional(obj->channel, hero->tempOwner); @@ -664,7 +662,7 @@ bool CPathfinder::addTeleportOneWayRandom(const CGTeleport * obj) const bool CPathfinder::addTeleportWhirlpool(const CGWhirlpool * obj) const { - return options.useTeleportWhirlpool && obj; + return options.useTeleportWhirlpool && hlp->hasBonusOfType(Bonus::WHIRLPOOL_PROTECTION) && obj; } TurnInfo::TurnInfo(const CGHeroInstance * Hero, const int turn) diff --git a/lib/CPathfinder.h b/lib/CPathfinder.h index 6f325d204..f3cd2f8c0 100644 --- a/lib/CPathfinder.h +++ b/lib/CPathfinder.h @@ -166,7 +166,7 @@ private: CGPathNode::ENodeAction destAction; void addNeighbours(const int3 & coord); - void addTeleportExits(bool noTeleportExcludes = false); + void addTeleportExits(); bool isLayerTransitionPossible() const; bool isMovementToDestPossible() const; @@ -184,6 +184,7 @@ private: 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) + bool isAllowedTeleportEntrance(const CGTeleport * obj) const; bool addTeleportTwoWay(const CGTeleport * obj) const; bool addTeleportOneWay(const CGTeleport * obj) const; bool addTeleportOneWayRandom(const CGTeleport * obj) const; From d524b4eabe9d60ac2685a144d080966e5655734b Mon Sep 17 00:00:00 2001 From: ArseniyShestakov Date: Mon, 16 Nov 2015 19:36:15 +0300 Subject: [PATCH 089/168] CPathfinder: get rid of addNeighbours coord argument --- lib/CPathfinder.cpp | 6 +++--- lib/CPathfinder.h | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/CPathfinder.cpp b/lib/CPathfinder.cpp index e94479fbc..13a33ee21 100644 --- a/lib/CPathfinder.cpp +++ b/lib/CPathfinder.cpp @@ -107,7 +107,7 @@ void CPathfinder::calculatePaths() } //add accessible neighbouring nodes to the queue - addNeighbours(cp->coord); + addNeighbours(); for(auto & neighbour : neighbours) { dt = &gs->map->getTile(neighbour); @@ -186,11 +186,11 @@ void CPathfinder::calculatePaths() } //queue loop } -void CPathfinder::addNeighbours(const int3 & coord) +void CPathfinder::addNeighbours() { neighbours.clear(); std::vector tiles; - CPathfinderHelper::getNeighbours(gs, *ct, coord, tiles, boost::logic::indeterminate, cp->layer == ELayer::SAIL); // TODO: find out if we still need "limitCoastSailing" option + CPathfinderHelper::getNeighbours(gs, *ct, cp->coord, tiles, boost::logic::indeterminate, cp->layer == ELayer::SAIL); // TODO: find out if we still need "limitCoastSailing" option if(isSourceVisitableObj()) { for(int3 tile: tiles) diff --git a/lib/CPathfinder.h b/lib/CPathfinder.h index f3cd2f8c0..d3a0c777b 100644 --- a/lib/CPathfinder.h +++ b/lib/CPathfinder.h @@ -165,7 +165,7 @@ private: const CGObjectInstance * ctObj, * dtObj; CGPathNode::ENodeAction destAction; - void addNeighbours(const int3 & coord); + void addNeighbours(); void addTeleportExits(); bool isLayerTransitionPossible() const; From 578aa2acd46676b5c12de66df55bc829af63dcdc Mon Sep 17 00:00:00 2001 From: ArseniyShestakov Date: Mon, 16 Nov 2015 21:22:11 +0300 Subject: [PATCH 090/168] Pathfinding: don't use gamestate directly as it's not needed --- lib/CPathfinder.cpp | 42 +++++++++++++++++++++--------------------- lib/CPathfinder.h | 2 +- 2 files changed, 22 insertions(+), 22 deletions(-) diff --git a/lib/CPathfinder.cpp b/lib/CPathfinder.cpp index 13a33ee21..6204fcad5 100644 --- a/lib/CPathfinder.cpp +++ b/lib/CPathfinder.cpp @@ -43,7 +43,7 @@ CPathfinder::CPathfinder(CPathsInfo & _out, CGameState * _gs, const CGHeroInstan out.hero = hero; out.hpos = hero->getPosition(false); - if(!gs->map->isInTheMap(out.hpos)/* || !gs->map->isInTheMap(dest)*/) //check input + if(!isInTheMap(out.hpos)/* || !gs->map->isInTheMap(dest)*/) //check input { logGlobal->errorStream() << "CGameState::calculatePaths: Hero outside the gs->map? How dare you..."; throw std::runtime_error("Wrong checksum"); @@ -95,7 +95,7 @@ void CPathfinder::calculatePaths() cp = pq.top(); pq.pop(); cp->locked = true; - ct = &gs->map->getTile(cp->coord); + ct = getTile(cp->coord); ctObj = ct->topVisitableObj(isSourceInitialPosition()); int movement = cp->moveRemains, turn = cp->turns; @@ -110,7 +110,7 @@ void CPathfinder::calculatePaths() addNeighbours(); for(auto & neighbour : neighbours) { - dt = &gs->map->getTile(neighbour); + dt = getTile(neighbour); dtObj = dt->topVisitableObj(); for(ELayer i = ELayer::LAND; i <= ELayer::AIR; i.advance(1)) { @@ -190,7 +190,7 @@ void CPathfinder::addNeighbours() { neighbours.clear(); std::vector tiles; - CPathfinderHelper::getNeighbours(gs, *ct, cp->coord, tiles, boost::logic::indeterminate, cp->layer == ELayer::SAIL); // TODO: find out if we still need "limitCoastSailing" option + CPathfinderHelper::getNeighbours(gs->map, *ct, cp->coord, tiles, boost::logic::indeterminate, cp->layer == ELayer::SAIL); // TODO: find out if we still need "limitCoastSailing" option if(isSourceVisitableObj()) { for(int3 tile: tiles) @@ -212,7 +212,7 @@ void CPathfinder::addTeleportExits() const CGTeleport * objTeleport = dynamic_cast(ctObj); if(isAllowedTeleportEntrance(objTeleport)) { - for(auto objId : gs->getTeleportChannelExits(objTeleport->channel, hero->tempOwner)) + for(auto objId : getTeleportChannelExits(objTeleport->channel, hero->tempOwner)) { auto obj = getObj(objId); if(dynamic_cast(obj)) @@ -220,7 +220,7 @@ void CPathfinder::addTeleportExits() auto pos = obj->getBlockedPos(); for(auto p : pos) { - if(gs->getTile(p)->topVisitableId() == obj->ID) + if(getTile(p)->topVisitableId() == obj->ID) neighbours.push_back(p); } } @@ -235,7 +235,7 @@ void CPathfinder::addTeleportExits() { /// TODO: Find way to reuse CPlayerSpecificInfoCallback::getTownsInfo /// This may be handy if we allow to use teleportation to friendly towns - auto towns = gs->getPlayer(hero->tempOwner)->towns; + auto towns = getPlayer(hero->tempOwner)->towns; for(const auto & town : towns) { if(town->id != ctObj->id && town->visitingHero == nullptr @@ -500,7 +500,7 @@ bool CPathfinder::isSourceGuarded() const /// - Map start with hero on guarded tile /// - Dimention door used /// TODO: check what happen when there is several guards - if(gs->guardingCreaturePosition(cp->coord) != int3(-1, -1, -1) && !isSourceInitialPosition()) + if(guardingCreaturePosition(cp->coord) != int3(-1, -1, -1) && !isSourceInitialPosition()) { return true; } @@ -510,7 +510,7 @@ bool CPathfinder::isSourceGuarded() const bool CPathfinder::isDestinationGuarded(const bool ignoreAccessibility) const { - if(gs->guardingCreaturePosition(dp->coord).valid() + if(guardingCreaturePosition(dp->coord).valid() && (ignoreAccessibility || dp->accessible == CGPathNode::BLOCKVIS)) { return true; @@ -521,7 +521,7 @@ bool CPathfinder::isDestinationGuarded(const bool ignoreAccessibility) const bool CPathfinder::isDestinationGuardian() const { - return gs->guardingCreaturePosition(cp->coord) == dp->coord; + return guardingCreaturePosition(cp->coord) == dp->coord; } void CPathfinder::initializeGraph() @@ -547,7 +547,7 @@ void CPathfinder::initializeGraph() { for(pos.z=0; pos.z < out.sizes.z; ++pos.z) { - const TerrainTile * tinfo = &gs->map->getTile(pos); + const TerrainTile * tinfo = getTile(pos); switch(tinfo->terType) { case ETerrainType::ROCK: @@ -602,7 +602,7 @@ CGPathNode::EAccessibility CPathfinder::evaluateAccessibility(const int3 & pos, } } } - else if(gs->guardingCreaturePosition(pos).valid() && !tinfo->blocked) + else if(guardingCreaturePosition(pos).valid() && !tinfo->blocked) { // Monster close by; blocked visit for battle. return CGPathNode::BLOCKVIS; @@ -618,7 +618,7 @@ bool CPathfinder::canMoveBetween(const int3 & a, const int3 & b) const bool CPathfinder::isAllowedTeleportEntrance(const CGTeleport * obj) const { - if(!obj || !gs->isTeleportEntrancePassable(obj, hero->tempOwner)) + if(!obj || !isTeleportEntrancePassable(obj, hero->tempOwner)) return false; auto whirlpool = dynamic_cast(obj); @@ -635,14 +635,14 @@ bool CPathfinder::isAllowedTeleportEntrance(const CGTeleport * obj) const bool CPathfinder::addTeleportTwoWay(const CGTeleport * obj) const { - return options.useTeleportTwoWay && gs->isTeleportChannelBidirectional(obj->channel, hero->tempOwner); + return options.useTeleportTwoWay && isTeleportChannelBidirectional(obj->channel, hero->tempOwner); } bool CPathfinder::addTeleportOneWay(const CGTeleport * obj) const { if(options.useTeleportOneWay && isTeleportChannelUnidirectional(obj->channel, hero->tempOwner)) { - auto passableExits = CGTeleport::getPassableExits(gs, hero, gs->getTeleportChannelExits(obj->channel, hero->tempOwner)); + auto passableExits = CGTeleport::getPassableExits(gs, hero, getTeleportChannelExits(obj->channel, hero->tempOwner)); if(passableExits.size() == 1) return true; } @@ -653,7 +653,7 @@ bool CPathfinder::addTeleportOneWayRandom(const CGTeleport * obj) const { if(options.useTeleportOneWayRandom && isTeleportChannelUnidirectional(obj->channel, hero->tempOwner)) { - auto passableExits = CGTeleport::getPassableExits(gs, hero, gs->getTeleportChannelExits(obj->channel, hero->tempOwner)); + auto passableExits = CGTeleport::getPassableExits(gs, hero, getTeleportChannelExits(obj->channel, hero->tempOwner)); if(passableExits.size() > 1) return true; } @@ -766,7 +766,7 @@ int CPathfinderHelper::getMaxMovePoints(const EPathfindingLayer layer) const return turnsInfo[turn]->getMaxMovePoints(layer); } -void CPathfinderHelper::getNeighbours(CGameState * gs, const TerrainTile & srct, const int3 & tile, std::vector & vec, const boost::logic::tribool & onLand, const bool limitCoastSailing) +void CPathfinderHelper::getNeighbours(const CMap * map, const TerrainTile & srct, const int3 & tile, std::vector & vec, const boost::logic::tribool & onLand, const 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) }; @@ -775,10 +775,10 @@ void CPathfinderHelper::getNeighbours(CGameState * gs, const TerrainTile & srct, for(auto & dir : dirs) { const int3 hlp = tile + dir; - if(!gs->isInTheMap(hlp)) + if(!map->isInTheMap(hlp)) continue; - const TerrainTile & hlpt = gs->map->getTile(hlp); + 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) @@ -793,7 +793,7 @@ void CPathfinderHelper::getNeighbours(CGameState * gs, const TerrainTile & srct, hlp1.x += dir.x; hlp2.y += dir.y; - if(gs->map->getTile(hlp1).terType != ETerrainType::WATER || gs->map->getTile(hlp2).terType != ETerrainType::WATER) + if(map->getTile(hlp1).terType != ETerrainType::WATER || map->getTile(hlp2).terType != ETerrainType::WATER) continue; } @@ -844,7 +844,7 @@ int CPathfinderHelper::getMovementCost(const CGHeroInstance * h, const int3 & sr { std::vector vec; vec.reserve(8); //optimization - getNeighbours(h->cb->gameState(), *d, dst, vec, s->terType != ETerrainType::WATER, true); + getNeighbours(h->cb->gameState()->map, *d, dst, vec, s->terType != ETerrainType::WATER, true); for(auto & elem : vec) { int fcost = getMovementCost(h, dst, elem, left, ti, false); diff --git a/lib/CPathfinder.h b/lib/CPathfinder.h index d3a0c777b..64c80dee5 100644 --- a/lib/CPathfinder.h +++ b/lib/CPathfinder.h @@ -216,7 +216,7 @@ public: bool hasBonusOfType(const Bonus::BonusType type, const int subtype = -1) const; int getMaxMovePoints(const EPathfindingLayer layer) const; - static void getNeighbours(CGameState * gs, const TerrainTile & srct, const int3 & tile, std::vector & vec, const boost::logic::tribool & onLand, const bool limitCoastSailing); + static void getNeighbours(const CMap * map, const TerrainTile & srct, const int3 & tile, std::vector & vec, const boost::logic::tribool & onLand, const bool limitCoastSailing); static int getMovementCost(const CGHeroInstance * h, const int3 & src, const int3 & dst, const int remainingMovePoints =- 1, const TurnInfo * ti = nullptr, const bool checkLast = true); static int getMovementCost(const CGHeroInstance * h, const int3 & dst); From 73d86877854b673bd779090f071ddb312237e3bb Mon Sep 17 00:00:00 2001 From: ArseniyShestakov Date: Mon, 16 Nov 2015 23:07:36 +0300 Subject: [PATCH 091/168] CPathfinder: add some comments and TODO for future changes --- lib/CPathfinder.cpp | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/lib/CPathfinder.cpp b/lib/CPathfinder.cpp index 6204fcad5..b37da32ce 100644 --- a/lib/CPathfinder.cpp +++ b/lib/CPathfinder.cpp @@ -434,6 +434,8 @@ CGPathNode::ENodeAction CPathfinder::getDestAction() const break; } + /// don't break - next case shared for both land and sail layers + case ELayer::SAIL: if(dtObj) { @@ -510,6 +512,8 @@ bool CPathfinder::isSourceGuarded() const bool CPathfinder::isDestinationGuarded(const bool ignoreAccessibility) const { + /// isDestinationGuarded is exception needed for garrisons. + /// When monster standing behind garrison it's visitable and guarded at the same time. if(guardingCreaturePosition(dp->coord).valid() && (ignoreAccessibility || dp->accessible == CGPathNode::BLOCKVIS)) { @@ -814,7 +818,14 @@ int CPathfinderHelper::getMovementCost(const CGHeroInstance * h, const int3 & sr ti = new TurnInfo(h); auto s = h->cb->getTile(src), d = h->cb->getTile(dst); + + /// TODO: by the original game rules hero shouldn't be affected by terrain penalty while flying. + /// Also flying movement only has penalty when player moving over blocked tiles. + /// So if you only have base flying with 40% penalty you can still ignore terrain penalty while having zero flying penalty. int ret = h->getTileCost(*d, *s, ti); + /// Unfortunately this can't be implemented yet as server don't know when player flying and when he's not. + /// Difference in cost calculation on client and server is much worse than incorrect cost. + /// So this one is waiting till server going to use pathfinder rules for path validation. if(d->blocked && ti->hasBonusOfType(Bonus::FLYING_MOVEMENT)) { @@ -839,6 +850,8 @@ int CPathfinderHelper::getMovementCost(const CGHeroInstance * h, const int3 & sr return remainingMovePoints; } + /// TODO: This part need rework in order to work properly with flying and water walking + /// Currently it's only work properly for normal movement or sailing 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 { From c2ba3e3faf63f9ca7a427c819a218ebdeb757b68 Mon Sep 17 00:00:00 2001 From: ArseniyShestakov Date: Tue, 17 Nov 2015 02:41:31 +0300 Subject: [PATCH 092/168] CPathfinderHelper: very firts pass over getNeighbours --- lib/CPathfinder.cpp | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/lib/CPathfinder.cpp b/lib/CPathfinder.cpp index b37da32ce..f57a08af2 100644 --- a/lib/CPathfinder.cpp +++ b/lib/CPathfinder.cpp @@ -190,7 +190,8 @@ void CPathfinder::addNeighbours() { neighbours.clear(); std::vector tiles; - CPathfinderHelper::getNeighbours(gs->map, *ct, cp->coord, tiles, boost::logic::indeterminate, cp->layer == ELayer::SAIL); // TODO: find out if we still need "limitCoastSailing" option + tiles.reserve(8); + CPathfinderHelper::getNeighbours(gs->map, *ct, cp->coord, tiles, boost::logic::indeterminate, cp->layer == ELayer::SAIL); if(isSourceVisitableObj()) { for(int3 tile: tiles) @@ -464,7 +465,7 @@ CGPathNode::ENodeAction CPathfinder::getDestAction() const } else if(isDestinationGuardian()) action = CGPathNode::BATTLE; - else if(dtObj->blockVisit && (!options.useCastleGate || dtObj->ID != Obj::TOWN)) + else if(dtObj->blockVisit && !(options.useCastleGate && dtObj->ID == Obj::TOWN)) action = CGPathNode::BLOCKING_VISIT; if(action == CGPathNode::NORMAL) @@ -772,10 +773,12 @@ int CPathfinderHelper::getMaxMovePoints(const EPathfindingLayer layer) const void CPathfinderHelper::getNeighbours(const CMap * map, const TerrainTile & srct, const int3 & tile, std::vector & vec, const boost::logic::tribool & onLand, const 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) }; + static const int3 dirs[] = { + int3(-1, +1, +0), int3(0, +1, +0), int3(+1, +1, +0), + int3(-1, +0, +0), /* source pos */ int3(+1, +0, +0), + int3(-1, -1, +0), int3(0, -1, +0), int3(+1, -1, +0) + }; - //vec.reserve(8); //optimization for(auto & dir : dirs) { const int3 hlp = tile + dir; @@ -783,6 +786,8 @@ void CPathfinderHelper::getNeighbours(const CMap * map, const TerrainTile & srct continue; const TerrainTile & hlpt = map->getTile(hlp); + if(hlpt.terType == ETerrainType::ROCK) + continue; // //we cannot visit things from blocked tiles // if(srct.blocked && !srct.visitable && hlpt.visitable && srct.blockingObjects.front()->ID != HEROI_TYPE) @@ -790,6 +795,7 @@ void CPathfinderHelper::getNeighbours(const CMap * map, const TerrainTile & srct // continue; // } + /// Following condition let us avoid diagonal movement over coast when sailing if(srct.terType == ETerrainType::WATER && limitCoastSailing && hlpt.terType == ETerrainType::WATER && dir.x && dir.y) //diagonal move through water { int3 hlp1 = tile, @@ -801,8 +807,7 @@ void CPathfinderHelper::getNeighbours(const CMap * map, const TerrainTile & srct continue; } - if((indeterminate(onLand) || onLand == (hlpt.terType!=ETerrainType::WATER) ) - && hlpt.terType != ETerrainType::ROCK) + if(indeterminate(onLand) || onLand == (hlpt.terType != ETerrainType::WATER)) { vec.push_back(hlp); } From fe12b8f664a6f38b71cf914a6326d6cb81dc1253 Mon Sep 17 00:00:00 2001 From: ArseniyShestakov Date: Tue, 17 Nov 2015 03:59:02 +0300 Subject: [PATCH 093/168] Pathfinding: re-introduce EAccessibility::FLYABLE That let us get rid of really hacky code in initializeGraph and also fix flying over tiles that aren't visible. --- lib/CPathfinder.cpp | 119 +++++++++++++++++++++++++------------------- lib/CPathfinder.h | 3 +- 2 files changed, 70 insertions(+), 52 deletions(-) diff --git a/lib/CPathfinder.cpp b/lib/CPathfinder.cpp index f57a08af2..ab2146796 100644 --- a/lib/CPathfinder.cpp +++ b/lib/CPathfinder.cpp @@ -321,10 +321,13 @@ bool CPathfinder::isLayerTransitionPossible() const bool CPathfinder::isMovementToDestPossible() const { + if(dp->accessible == CGPathNode::BLOCKED) + return false; + switch(dp->layer) { case ELayer::LAND: - if(!canMoveBetween(cp->coord, dp->coord) || dp->accessible == CGPathNode::BLOCKED) + if(!canMoveBetween(cp->coord, dp->coord)) return false; if(isSourceGuarded()) { @@ -338,7 +341,7 @@ bool CPathfinder::isMovementToDestPossible() const break; case ELayer::SAIL: - if(!canMoveBetween(cp->coord, dp->coord) || dp->accessible == CGPathNode::BLOCKED) + if(!canMoveBetween(cp->coord, dp->coord)) return false; if(isSourceGuarded()) { @@ -531,17 +534,10 @@ bool CPathfinder::isDestinationGuardian() const void CPathfinder::initializeGraph() { - auto updateNode = [&](int3 pos, ELayer layer, const TerrainTile * tinfo, bool blockNotAccessible) + auto updateNode = [&](int3 pos, ELayer layer, const TerrainTile * tinfo) { auto node = out.getNode(pos, layer); - - auto accessibility = evaluateAccessibility(pos, tinfo); - /// TODO: Probably this shouldn't be handled by initializeGraph - if(blockNotAccessible - && (accessibility != CGPathNode::ACCESSIBLE || tinfo->terType == ETerrainType::WATER)) - { - accessibility = CGPathNode::BLOCKED; - } + auto accessibility = evaluateAccessibility(pos, tinfo, layer); node->update(pos, layer, accessibility); }; @@ -555,65 +551,86 @@ void CPathfinder::initializeGraph() const TerrainTile * tinfo = getTile(pos); switch(tinfo->terType) { - case ETerrainType::ROCK: - break; - case ETerrainType::WATER: - updateNode(pos, ELayer::SAIL, tinfo, false); - if(options.useFlying) - updateNode(pos, ELayer::AIR, tinfo, true); - if(options.useWaterWalking) - updateNode(pos, ELayer::WATER, tinfo, false); - break; - default: - updateNode(pos, ELayer::LAND, tinfo, false); - if(options.useFlying) - updateNode(pos, ELayer::AIR, tinfo, true); - break; + case ETerrainType::ROCK: + break; + + case ETerrainType::WATER: + updateNode(pos, ELayer::SAIL, tinfo); + if(options.useFlying) + updateNode(pos, ELayer::AIR, tinfo); + if(options.useWaterWalking) + updateNode(pos, ELayer::WATER, tinfo); + break; + + default: + updateNode(pos, ELayer::LAND, tinfo); + if(options.useFlying) + updateNode(pos, ELayer::AIR, tinfo); + break; } } } } } -CGPathNode::EAccessibility CPathfinder::evaluateAccessibility(const int3 & pos, const TerrainTile * tinfo) const +CGPathNode::EAccessibility CPathfinder::evaluateAccessibility(const int3 & pos, const TerrainTile * tinfo, const ELayer layer) const { - CGPathNode::EAccessibility ret = (tinfo->blocked ? CGPathNode::BLOCKED : CGPathNode::ACCESSIBLE); - if(tinfo->terType == ETerrainType::ROCK || !isVisible(pos, hero->tempOwner)) return CGPathNode::BLOCKED; - if(tinfo->visitable) + switch(layer) { - if(tinfo->visitableObjects.front()->ID == Obj::SANCTUARY && tinfo->visitableObjects.back()->ID == Obj::HERO && tinfo->visitableObjects.back()->tempOwner != hero->tempOwner) //non-owned hero stands on Sanctuary + case ELayer::LAND: + case ELayer::SAIL: + if(tinfo->visitable) { - return CGPathNode::BLOCKED; - } - else - { - for(const CGObjectInstance * obj : tinfo->visitableObjects) + if(tinfo->visitableObjects.front()->ID == Obj::SANCTUARY && tinfo->visitableObjects.back()->ID == Obj::HERO && tinfo->visitableObjects.back()->tempOwner != hero->tempOwner) //non-owned hero stands on Sanctuary { - if(obj->passableFor(hero->tempOwner)) + return CGPathNode::BLOCKED; + } + else + { + for(const CGObjectInstance * obj : tinfo->visitableObjects) { - ret = CGPathNode::ACCESSIBLE; - } - else if(obj->blockVisit) - { - return CGPathNode::BLOCKVIS; - } - else if(obj->ID != Obj::EVENT) //pathfinder should ignore placed events - { - ret = CGPathNode::VISITABLE; + if(obj->passableFor(hero->tempOwner)) + { + return CGPathNode::ACCESSIBLE; + } + else if(obj->blockVisit) + { + return CGPathNode::BLOCKVIS; + } + else if(obj->ID != Obj::EVENT) //pathfinder should ignore placed events + { + return CGPathNode::VISITABLE; + } } } } - } - else if(guardingCreaturePosition(pos).valid() && !tinfo->blocked) - { - // Monster close by; blocked visit for battle. - return CGPathNode::BLOCKVIS; + else if(guardingCreaturePosition(pos).valid() && !tinfo->blocked) + { + // Monster close by; blocked visit for battle + return CGPathNode::BLOCKVIS; + } + else if(tinfo->blocked) + return CGPathNode::BLOCKED; + + break; + + case ELayer::WATER: + if(tinfo->blocked || tinfo->terType != ETerrainType::WATER) + return CGPathNode::BLOCKED; + + break; + + case ELayer::AIR: + if(tinfo->blocked || tinfo->terType == ETerrainType::WATER) + return CGPathNode::FLYABLE; + + break; } - return ret; + return CGPathNode::ACCESSIBLE; } bool CPathfinder::canMoveBetween(const int3 & a, const int3 & b) const diff --git a/lib/CPathfinder.h b/lib/CPathfinder.h index 64c80dee5..51a47af41 100644 --- a/lib/CPathfinder.h +++ b/lib/CPathfinder.h @@ -43,6 +43,7 @@ struct DLL_LINKAGE CGPathNode 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 }; @@ -181,7 +182,7 @@ private: void initializeGraph(); - CGPathNode::EAccessibility evaluateAccessibility(const int3 & pos, const TerrainTile * tinfo) const; + CGPathNode::EAccessibility evaluateAccessibility(const int3 & pos, const TerrainTile * tinfo, const ELayer layer) 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; From e9636a8d37e6a9e5675ddc82739acb9f56c2f066 Mon Sep 17 00:00:00 2001 From: ArseniyShestakov Date: Tue, 17 Nov 2015 04:33:21 +0300 Subject: [PATCH 094/168] CPathfinder: add TODO and ideas for available options --- lib/CPathfinder.h | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/lib/CPathfinder.h b/lib/CPathfinder.h index 51a47af41..f035383af 100644 --- a/lib/CPathfinder.h +++ b/lib/CPathfinder.h @@ -121,6 +121,13 @@ private: /// 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. @@ -135,6 +142,10 @@ private: /// - 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(); From ab9680a7d9610e048b1c5b1aebb70a45372288e0 Mon Sep 17 00:00:00 2001 From: ArseniyShestakov Date: Tue, 17 Nov 2015 07:09:01 +0300 Subject: [PATCH 095/168] CPathfinder: handle event object properly everywhere Also add forgoted check for AdvmapInterface to avoid possible crash. --- client/windows/CAdvmapInterface.cpp | 2 +- lib/CPathfinder.cpp | 30 ++++++++++++++++++++++------- lib/CPathfinder.h | 3 +++ 3 files changed, 27 insertions(+), 8 deletions(-) diff --git a/client/windows/CAdvmapInterface.cpp b/client/windows/CAdvmapInterface.cpp index c0a208631..e48daf709 100644 --- a/client/windows/CAdvmapInterface.cpp +++ b/client/windows/CAdvmapInterface.cpp @@ -1545,7 +1545,7 @@ void CAdvMapInt::tileHovered(const int3 &mapPos) case CGPathNode::VISIT: case CGPathNode::BLOCKING_VISIT: - if(objAtTile->ID == Obj::HERO) + if(objAtTile && objAtTile->ID == Obj::HERO) { if(selection == objAtTile) CCS->curh->changeGraphic(ECursor::ADVENTURE, 2); diff --git a/lib/CPathfinder.cpp b/lib/CPathfinder.cpp index ab2146796..49cb006f8 100644 --- a/lib/CPathfinder.cpp +++ b/lib/CPathfinder.cpp @@ -352,13 +352,13 @@ bool CPathfinder::isMovementToDestPossible() const if(cp->layer == ELayer::LAND) { - if(!dtObj) + if(!isDestVisitableObj()) return false; if(dtObj->ID != Obj::BOAT && dtObj->ID != Obj::HERO) return false; } - else if(dtObj && dtObj->ID == Obj::BOAT) + else if(isDestVisitableObj() && dtObj->ID == Obj::BOAT) { /// Hero in boat can't visit empty boats return false; @@ -441,7 +441,7 @@ CGPathNode::ENodeAction CPathfinder::getDestAction() const /// don't break - next case shared for both land and sail layers case ELayer::SAIL: - if(dtObj) + if(isDestVisitableObj()) { auto objRel = getPlayerRelations(dtObj->tempOwner, hero->tempOwner); @@ -495,8 +495,7 @@ bool CPathfinder::isSourceInitialPosition() const bool CPathfinder::isSourceVisitableObj() const { - /// Hero can't visit objects while walking on water or flying - return ctObj != nullptr && (cp->layer == ELayer::LAND || cp->layer == ELayer::SAIL); + return isVisitableObj(ctObj, cp->layer); } bool CPathfinder::isSourceGuarded() const @@ -505,7 +504,7 @@ bool CPathfinder::isSourceGuarded() const /// It's possible at least in these cases: /// - Map start with hero on guarded tile /// - Dimention door used - /// TODO: check what happen when there is several guards + /// TODO: check what happen when there is several guards if(guardingCreaturePosition(cp->coord) != int3(-1, -1, -1) && !isSourceInitialPosition()) { return true; @@ -514,6 +513,11 @@ bool CPathfinder::isSourceGuarded() const return false; } +bool CPathfinder::isDestVisitableObj() const +{ + return isVisitableObj(dtObj, dp->layer); +} + bool CPathfinder::isDestinationGuarded(const bool ignoreAccessibility) const { /// isDestinationGuarded is exception needed for garrisons. @@ -600,7 +604,7 @@ CGPathNode::EAccessibility CPathfinder::evaluateAccessibility(const int3 & pos, { return CGPathNode::BLOCKVIS; } - else if(obj->ID != Obj::EVENT) //pathfinder should ignore placed events + else if(canSeeObj(obj)) { return CGPathNode::VISITABLE; } @@ -633,6 +637,18 @@ CGPathNode::EAccessibility CPathfinder::evaluateAccessibility(const int3 & pos, return CGPathNode::ACCESSIBLE; } +bool CPathfinder::isVisitableObj(const CGObjectInstance * obj, const ELayer layer) const +{ + /// Hero can't visit objects while walking on water or flying + return canSeeObj(obj) && (layer == ELayer::LAND || layer == ELayer::SAIL); +} + +bool CPathfinder::canSeeObj(const CGObjectInstance * obj) const +{ + /// Pathfinder should ignore placed events + return obj != nullptr && obj->ID != Obj::EVENT; +} + bool CPathfinder::canMoveBetween(const int3 & a, const int3 & b) const { return gs->checkForVisitableDir(a, b); diff --git a/lib/CPathfinder.h b/lib/CPathfinder.h index f035383af..143502eab 100644 --- a/lib/CPathfinder.h +++ b/lib/CPathfinder.h @@ -188,12 +188,15 @@ private: 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::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; From 511bb5464447ad2c7ce535fe98fa69290ccee910 Mon Sep 17 00:00:00 2001 From: ArseniyShestakov Date: Tue, 17 Nov 2015 17:46:26 +0300 Subject: [PATCH 096/168] doMoveHero: only allow to stop at accessible or land/sail nodes --- client/CPlayerInterface.cpp | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/client/CPlayerInterface.cpp b/client/CPlayerInterface.cpp index 7e3b5a243..3b5bdf7f2 100644 --- a/client/CPlayerInterface.cpp +++ b/client/CPlayerInterface.cpp @@ -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; From 7101083a24a66e8a55b961056d599b4129bad969 Mon Sep 17 00:00:00 2001 From: ArseniyShestakov Date: Wed, 18 Nov 2015 00:07:25 +0300 Subject: [PATCH 097/168] CPathfinder: restore transit via garrisons --- lib/CPathfinder.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/lib/CPathfinder.cpp b/lib/CPathfinder.cpp index 49cb006f8..547422cd8 100644 --- a/lib/CPathfinder.cpp +++ b/lib/CPathfinder.cpp @@ -395,6 +395,11 @@ bool CPathfinder::isMovementAfterDestPossible() const /// Transit over whirlpools only allowed when hero protected return true; } + else if(dtObj->ID == Obj::GARRISON || dtObj->ID == Obj::GARRISON2) + { + /// Transit via unguarded garrisons is always possible + return true; + } break; } From def0f0ef0a506e428a7e2c761ddc4fbc350f6ba1 Mon Sep 17 00:00:00 2001 From: ArseniyShestakov Date: Wed, 18 Nov 2015 03:14:58 +0300 Subject: [PATCH 098/168] CTerrainRect::mouseMoved: don't pass curHoveredTile by reference This does cause problems because curHoveredTile can change while reference still being used by CAdvMapInt::tileHovered. --- client/windows/CAdvmapInterface.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/client/windows/CAdvmapInterface.cpp b/client/windows/CAdvmapInterface.cpp index e48daf709..940a1a655 100644 --- a/client/windows/CAdvmapInterface.cpp +++ b/client/windows/CAdvmapInterface.cpp @@ -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) { From adeefe903a2353a22f2e3b76e86047af47f42bec Mon Sep 17 00:00:00 2001 From: ArseniyShestakov Date: Thu, 19 Nov 2015 03:08:57 +0300 Subject: [PATCH 099/168] CGPathNode: apply suggested optimizations Also make EPathfindingLayers ui8 too --- lib/CPathfinder.h | 24 ++++++++++++------------ lib/GameConstants.h | 4 ++-- 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/lib/CPathfinder.h b/lib/CPathfinder.h index 143502eab..a1c546e13 100644 --- a/lib/CPathfinder.h +++ b/lib/CPathfinder.h @@ -26,18 +26,18 @@ struct DLL_LINKAGE CGPathNode { typedef EPathfindingLayer ELayer; - enum ENodeAction + enum ENodeAction : ui8 { - UNKNOWN = -1, - NORMAL = 0, + UNKNOWN = 0, EMBARK = 1, - DISEMBARK, //2 - BATTLE,//3 - VISIT,//4 - BLOCKING_VISIT//5 + DISEMBARK, + NORMAL, + BATTLE, + VISIT, + BLOCKING_VISIT }; - enum EAccessibility + enum EAccessibility : ui8 { NOT_SET = 0, ACCESSIBLE = 1, //tile can be entered and passed @@ -47,14 +47,14 @@ struct DLL_LINKAGE CGPathNode BLOCKED //tile can't be entered nor visited }; - bool locked; - EAccessibility accessible; - 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(); diff --git a/lib/GameConstants.h b/lib/GameConstants.h index f695be746..d2cd05280 100644 --- a/lib/GameConstants.h +++ b/lib/GameConstants.h @@ -748,9 +748,9 @@ ID_LIKE_OPERATORS_DECLS(ETerrainType, ETerrainType::EETerrainType) class DLL_LINKAGE EPathfindingLayer { public: - enum EEPathfindingLayer + enum EEPathfindingLayer : ui8 { - WRONG = -2, AUTO = -1, LAND = 0, SAIL = 1, WATER, AIR, NUM_LAYERS + WRONG = 0, AUTO = 1, LAND = 2, SAIL = 3, WATER, AIR, NUM_LAYERS }; EPathfindingLayer(EEPathfindingLayer _num = WRONG) : num(_num) From d19cb5ce895071da856c7b408c5f8d0d8b5a08b1 Mon Sep 17 00:00:00 2001 From: ArseniyShestakov Date: Thu, 19 Nov 2015 04:01:56 +0300 Subject: [PATCH 100/168] EPathfindingLayer: fix obvious layer over-allocation in CPathsInfo --- lib/GameConstants.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/GameConstants.h b/lib/GameConstants.h index d2cd05280..3607ae5bf 100644 --- a/lib/GameConstants.h +++ b/lib/GameConstants.h @@ -750,7 +750,7 @@ class DLL_LINKAGE EPathfindingLayer public: enum EEPathfindingLayer : ui8 { - WRONG = 0, AUTO = 1, LAND = 2, SAIL = 3, WATER, AIR, NUM_LAYERS + LAND = 0, SAIL = 1, WATER, AIR, NUM_LAYERS, WRONG, AUTO }; EPathfindingLayer(EEPathfindingLayer _num = WRONG) : num(_num) From 6d395bcba7236ab435f6f16fadfaf118f06a2bb5 Mon Sep 17 00:00:00 2001 From: ArseniyShestakov Date: Fri, 20 Nov 2015 12:11:35 +0300 Subject: [PATCH 101/168] Bonus system: add turnsRemain support for ONE_WEEK duration --- lib/HeroBonus.h | 8 +++----- lib/NetPacksLib.cpp | 7 ++++--- 2 files changed, 7 insertions(+), 8 deletions(-) diff --git a/lib/HeroBonus.h b/lib/HeroBonus.h index a95e2c9b6..10b140746 100644 --- a/lib/HeroBonus.h +++ b/lib/HeroBonus.h @@ -294,7 +294,7 @@ struct DLL_LINKAGE Bonus }; ui16 duration; //uses BonusDuration values - si16 turnsRemain; //used if duration is N_TURNS or N_DAYS + si16 turnsRemain; //used if duration is N_TURNS, N_DAYS or ONE_WEEK BonusType type; //uses BonusType values - says to what is this bonus - 1 byte TBonusSubtype subtype; //-1 if not applicable - 4 bytes @@ -828,13 +828,11 @@ public: bool operator()(const Bonus *bonus) const { - if(daysRequested <= 0) + if(daysRequested <= 0 || bonus->duration & Bonus::PERMANENT || bonus->duration & Bonus::ONE_BATTLE) return true; else if(bonus->duration & Bonus::ONE_DAY) return false; - else if(bonus->duration & Bonus::PERMANENT || bonus->duration & Bonus::ONE_BATTLE) - return true; - else if(bonus->duration & Bonus::N_DAYS) + else if(bonus->duration & Bonus::N_DAYS || bonus->duration & Bonus::ONE_WEEK) { return bonus->turnsRemain > daysRequested; } diff --git a/lib/NetPacksLib.cpp b/lib/NetPacksLib.cpp index 56b246d5f..0efa1f466 100644 --- a/lib/NetPacksLib.cpp +++ b/lib/NetPacksLib.cpp @@ -263,6 +263,9 @@ DLL_LINKAGE void GiveBonus::applyGs( CGameState *gs ) assert(cbsn); + if(Bonus::OneWeek(&bonus)) + bonus.turnsRemain = 8 - gs->getDate(Date::DAY_OF_WEEK); // set correct number of days before adding bonus + auto b = new Bonus(bonus); cbsn->addNewBonus(b); @@ -1023,10 +1026,8 @@ DLL_LINKAGE void NewTurn::applyGs( CGameState *gs ) creatureSet.second.applyGs(gs); gs->globalEffects.popBonuses(Bonus::OneDay); //works for children -> all game objs - if(gs->getDate(Date::DAY_OF_WEEK) == 1) //new week - gs->globalEffects.popBonuses(Bonus::OneWeek); //works for children -> all game objs - gs->globalEffects.updateBonuses(Bonus::NDays); + gs->globalEffects.updateBonuses(Bonus::OneWeek); //TODO not really a single root hierarchy, what about bonuses placed elsewhere? [not an issue with H3 mechanics but in the future...] for(CGTownInstance* t : gs->map->towns) From cb61572878ff1460d0b7d59575715dd47b778570 Mon Sep 17 00:00:00 2001 From: ArseniyShestakov Date: Fri, 20 Nov 2015 12:28:35 +0300 Subject: [PATCH 102/168] TurnInfo: add cache for bonuses selection --- lib/CPathfinder.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/lib/CPathfinder.cpp b/lib/CPathfinder.cpp index 547422cd8..ec9d0d425 100644 --- a/lib/CPathfinder.cpp +++ b/lib/CPathfinder.cpp @@ -711,7 +711,10 @@ bool CPathfinder::addTeleportWhirlpool(const CGWhirlpool * obj) const TurnInfo::TurnInfo(const CGHeroInstance * Hero, const int turn) : hero(Hero), maxMovePointsLand(-1), maxMovePointsWater(-1) { - bonuses = hero->getAllBonuses(Selector::days(turn), nullptr); + std::stringstream cachingStr; + cachingStr << "days_" << turn; + + bonuses = hero->getAllBonuses(Selector::days(turn), nullptr, nullptr, cachingStr.str()); } bool TurnInfo::isLayerAvailable(const EPathfindingLayer layer) const From f99ffbd074986431600d6e31d6f4f5cea22df86c Mon Sep 17 00:00:00 2001 From: ArseniyShestakov Date: Fri, 20 Nov 2015 13:24:48 +0300 Subject: [PATCH 103/168] Bonus system: use duration helpers in selecters --- lib/HeroBonus.h | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/HeroBonus.h b/lib/HeroBonus.h index 10b140746..2121263de 100644 --- a/lib/HeroBonus.h +++ b/lib/HeroBonus.h @@ -811,7 +811,7 @@ public: bool operator()(const Bonus *bonus) const { return turnsRequested <= 0 //every present effect will last zero (or "less") turns - || !(bonus->duration & Bonus::N_TURNS) //so do every not expriing after N-turns effect + || !Bonus::NTurns(bonus) //so do every not expriing after N-turns effect || bonus->turnsRemain > turnsRequested; } CWillLastTurns& operator()(const int &setVal) @@ -828,11 +828,11 @@ public: bool operator()(const Bonus *bonus) const { - if(daysRequested <= 0 || bonus->duration & Bonus::PERMANENT || bonus->duration & Bonus::ONE_BATTLE) + if(daysRequested <= 0 || Bonus::Permanent(bonus) || Bonus::OneBattle(bonus)) return true; - else if(bonus->duration & Bonus::ONE_DAY) + else if(Bonus::OneDay(bonus)) return false; - else if(bonus->duration & Bonus::N_DAYS || bonus->duration & Bonus::ONE_WEEK) + else if(Bonus::NDays(bonus) || Bonus::OneWeek(bonus)) { return bonus->turnsRemain > daysRequested; } From b48d65ad5724643ac7b293aabc9d596b378ba4ee Mon Sep 17 00:00:00 2001 From: ArseniyShestakov Date: Fri, 20 Nov 2015 14:37:57 +0300 Subject: [PATCH 104/168] addNewBonus: add turnsRemain check for N_TURNS, N_DAYS and ONE_WEEK --- lib/HeroBonus.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/lib/HeroBonus.cpp b/lib/HeroBonus.cpp index 9b89111db..c054d586a 100644 --- a/lib/HeroBonus.cpp +++ b/lib/HeroBonus.cpp @@ -780,6 +780,12 @@ void CBonusSystemNode::updateBonuses(const CSelector &s) void CBonusSystemNode::addNewBonus(Bonus *b) { + //turnsRemain shouldn't be zero for following durations + if(Bonus::NTurns(b) || Bonus::NDays(b) || Bonus::OneWeek(b)) + { + assert(b->turnsRemain); + } + assert(!vstd::contains(exportedBonuses,b)); exportedBonuses.push_back(b); exportBonus(b); From e91d79414bd35d797824b3e1ea07e645d0161709 Mon Sep 17 00:00:00 2001 From: ArseniyShestakov Date: Sat, 21 Nov 2015 00:32:23 +0300 Subject: [PATCH 105/168] PathfinderOptions: use settings and move all defaults into schema --- config/schemas/settings.json | 70 +++++++++++++++++++++++++++++++++++- lib/CPathfinder.cpp | 23 ++++++------ 2 files changed, 81 insertions(+), 12 deletions(-) diff --git a/config/schemas/settings.json b/config/schemas/settings.json index 5b78fab89..fccc17f74 100644 --- a/config/schemas/settings.json +++ b/config/schemas/settings.json @@ -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, diff --git a/lib/CPathfinder.cpp b/lib/CPathfinder.cpp index ec9d0d425..d1297693a 100644 --- a/lib/CPathfinder.cpp +++ b/lib/CPathfinder.cpp @@ -7,6 +7,7 @@ #include "mapObjects/CGHeroInstance.h" #include "GameConstants.h" #include "CStopWatch.h" +#include "CConfigHandler.h" /* * CPathfinder.cpp, part of VCMI engine @@ -20,19 +21,19 @@ CPathfinder::PathfinderOptions::PathfinderOptions() { - useFlying = true; - useWaterWalking = true; - useEmbarkAndDisembark = true; - useTeleportTwoWay = true; - useTeleportOneWay = true; - useTeleportOneWayRandom = false; - useTeleportWhirlpool = true; + useFlying = settings["pathfinder"]["layers"]["flying"].Bool(); + useWaterWalking = settings["pathfinder"]["layers"]["waterWalking"].Bool(); + useEmbarkAndDisembark = settings["pathfinder"]["layers"]["sailing"].Bool(); + useTeleportTwoWay = settings["pathfinder"]["teleports"]["twoWay"].Bool(); + useTeleportOneWay = settings["pathfinder"]["teleports"]["oneWay"].Bool(); + useTeleportOneWayRandom = settings["pathfinder"]["teleports"]["oneWayRandom"].Bool(); + useTeleportWhirlpool = settings["pathfinder"]["teleports"]["whirlpool"].Bool(); - useCastleGate = false; + useCastleGate = settings["pathfinder"]["teleports"]["castleGate"].Bool(); - lightweightFlyingMode = false; - oneTurnSpecialLayersLimit = true; - originalMovementRules = true; + lightweightFlyingMode = settings["pathfinder"]["lightweightFlyingMode"].Bool(); + oneTurnSpecialLayersLimit = settings["pathfinder"]["oneTurnSpecialLayersLimit"].Bool(); + originalMovementRules = settings["pathfinder"]["originalMovementRules"].Bool(); } CPathfinder::CPathfinder(CPathsInfo & _out, CGameState * _gs, const CGHeroInstance * _hero) From 5ae6225ebcb38f693dab8824d8983730d82d20ab Mon Sep 17 00:00:00 2001 From: ArseniyShestakov Date: Sat, 21 Nov 2015 10:00:09 +0300 Subject: [PATCH 106/168] TurnInfo: implement internal bonus cache for most used bonuses Bonus system even with caching add too big overhead so we'll only use it once for these bonuses. Still I'm like it to be transparent for external code so it's a bit hacky code. --- lib/CPathfinder.cpp | 36 ++++++++++++++++++++++++++++++++++++ lib/CPathfinder.h | 14 ++++++++++++++ 2 files changed, 50 insertions(+) diff --git a/lib/CPathfinder.cpp b/lib/CPathfinder.cpp index d1297693a..28d165538 100644 --- a/lib/CPathfinder.cpp +++ b/lib/CPathfinder.cpp @@ -709,6 +709,21 @@ bool CPathfinder::addTeleportWhirlpool(const CGWhirlpool * obj) const return options.useTeleportWhirlpool && hlp->hasBonusOfType(Bonus::WHIRLPOOL_PROTECTION) && obj; } +TurnInfo::BonusCache::BonusCache(TBonusListPtr bl) +{ + noTerrainPenalty.reserve(ETerrainType::ROCK); + for(int i = 0; i < ETerrainType::ROCK; i++) + { + noTerrainPenalty.push_back(bl->getFirst(Selector::type(Bonus::NO_TERRAIN_PENALTY).And(Selector::subtype(i)))); + } + + freeShipBoarding = bl->getFirst(Selector::type(Bonus::FREE_SHIP_BOARDING)); + flyingMovement = bl->getFirst(Selector::type(Bonus::FLYING_MOVEMENT)); + flyingMovementVal = bl->valOfBonuses(Selector::type(Bonus::FLYING_MOVEMENT)); + waterWalking = bl->getFirst(Selector::type(Bonus::WATER_WALKING)); + waterWalkingVal = bl->valOfBonuses(Selector::type(Bonus::WATER_WALKING)); +} + TurnInfo::TurnInfo(const CGHeroInstance * Hero, const int turn) : hero(Hero), maxMovePointsLand(-1), maxMovePointsWater(-1) { @@ -716,6 +731,7 @@ TurnInfo::TurnInfo(const CGHeroInstance * Hero, const int turn) cachingStr << "days_" << turn; bonuses = hero->getAllBonuses(Selector::days(turn), nullptr, nullptr, cachingStr.str()); + bonusCache = make_unique(bonuses); } bool TurnInfo::isLayerAvailable(const EPathfindingLayer layer) const @@ -740,11 +756,31 @@ bool TurnInfo::isLayerAvailable(const EPathfindingLayer layer) const bool TurnInfo::hasBonusOfType(Bonus::BonusType type, int subtype) const { + switch(type) + { + case Bonus::FREE_SHIP_BOARDING: + return bonusCache->freeShipBoarding; + case Bonus::FLYING_MOVEMENT: + return bonusCache->flyingMovement; + case Bonus::WATER_WALKING: + return bonusCache->waterWalking; + case Bonus::NO_TERRAIN_PENALTY: + return bonusCache->noTerrainPenalty[subtype]; + } + return bonuses->getFirst(Selector::type(type).And(Selector::subtype(subtype))); } int TurnInfo::valOfBonuses(Bonus::BonusType type, int subtype) const { + switch(type) + { + case Bonus::FLYING_MOVEMENT: + return bonusCache->flyingMovementVal; + case Bonus::WATER_WALKING: + return bonusCache->waterWalkingVal; + } + return bonuses->valOfBonuses(Selector::type(type).And(Selector::subtype(subtype))); } diff --git a/lib/CPathfinder.h b/lib/CPathfinder.h index a1c546e13..52409ce08 100644 --- a/lib/CPathfinder.h +++ b/lib/CPathfinder.h @@ -209,6 +209,20 @@ private: 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 noTerrainPenalty; + bool freeShipBoarding; + bool flyingMovement; + int flyingMovementVal; + bool waterWalking; + int waterWalkingVal; + + BonusCache(TBonusListPtr bonusList); + }; + unique_ptr bonusCache; + const CGHeroInstance * hero; TBonusListPtr bonuses; mutable int maxMovePointsLand; From a375c86ea2985086533e7f8597a2202bc82609eb Mon Sep 17 00:00:00 2001 From: ArseniyShestakov Date: Sat, 21 Nov 2015 12:46:59 +0300 Subject: [PATCH 107/168] movementPointsAfterEmbark: get max move points from TurnInfo Missed that when originally implemented TurnInfo. Not pretties code, but boost performance a lot. --- lib/mapObjects/CGHeroInstance.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/lib/mapObjects/CGHeroInstance.cpp b/lib/mapObjects/CGHeroInstance.cpp index e6156b9bd..4e1e003d0 100644 --- a/lib/mapObjects/CGHeroInstance.cpp +++ b/lib/mapObjects/CGHeroInstance.cpp @@ -1169,8 +1169,10 @@ int CGHeroInstance::movementPointsAfterEmbark(int MPsBefore, int basicCost, bool 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(maxMovePoints(disembark)) / maxMovePoints(!disembark); + return (MPsBefore - basicCost) * static_cast(mp1) / mp2; return 0; //take all MPs otherwise } From 438a444443bc77c15b805490e1f987772b6f7135 Mon Sep 17 00:00:00 2001 From: ArseniyShestakov Date: Sat, 21 Nov 2015 13:30:39 +0300 Subject: [PATCH 108/168] CGHeroInstance: move native terrain check into getNativeTerrain That make it easier to use that code independently in TurnInfo --- lib/CPathfinder.cpp | 1 + lib/CPathfinder.h | 1 + lib/mapObjects/CGHeroInstance.cpp | 48 +++++++++++++++++++------------ lib/mapObjects/CGHeroInstance.h | 1 + 4 files changed, 32 insertions(+), 19 deletions(-) diff --git a/lib/CPathfinder.cpp b/lib/CPathfinder.cpp index 28d165538..24a4a023b 100644 --- a/lib/CPathfinder.cpp +++ b/lib/CPathfinder.cpp @@ -732,6 +732,7 @@ TurnInfo::TurnInfo(const CGHeroInstance * Hero, const int turn) bonuses = hero->getAllBonuses(Selector::days(turn), nullptr, nullptr, cachingStr.str()); bonusCache = make_unique(bonuses); + nativeTerrain = hero->getNativeTerrain(); } bool TurnInfo::isLayerAvailable(const EPathfindingLayer layer) const diff --git a/lib/CPathfinder.h b/lib/CPathfinder.h index 52409ce08..a5beef562 100644 --- a/lib/CPathfinder.h +++ b/lib/CPathfinder.h @@ -227,6 +227,7 @@ struct DLL_LINKAGE TurnInfo TBonusListPtr bonuses; mutable int maxMovePointsLand; mutable int maxMovePointsWater; + int nativeTerrain; TurnInfo(const CGHeroInstance * Hero, const int Turn = 0); bool isLayerAvailable(const EPathfindingLayer layer) const; diff --git a/lib/mapObjects/CGHeroInstance.cpp b/lib/mapObjects/CGHeroInstance.cpp index 4e1e003d0..a959334c5 100644 --- a/lib/mapObjects/CGHeroInstance.cpp +++ b/lib/mapObjects/CGHeroInstance.cpp @@ -80,30 +80,40 @@ ui32 CGHeroInstance::getTileCost(const TerrainTile &dest, const TerrainTile &fro break; } } - else if(!ti->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) diff --git a/lib/mapObjects/CGHeroInstance.h b/lib/mapObjects/CGHeroInstance.h index 9b9b59d23..748f84685 100644 --- a/lib/mapObjects/CGHeroInstance.h +++ b/lib/mapObjects/CGHeroInstance.h @@ -131,6 +131,7 @@ public: const std::string &getBiography() const; bool needsLastStack()const override; 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 From a1fe2ebc447fbebecd67f7bb0372f9df9e01753d Mon Sep 17 00:00:00 2001 From: ArseniyShestakov Date: Sat, 21 Nov 2015 14:31:30 +0300 Subject: [PATCH 109/168] Pathfinding: restore gamestate usage to avoid overhead Also when possible pass TerrainTile pointers to getMovementCost instead of using getTile. --- lib/CPathfinder.cpp | 40 ++++++++++++++++++++++------------------ lib/CPathfinder.h | 2 +- server/CGameHandler.cpp | 2 +- 3 files changed, 24 insertions(+), 20 deletions(-) diff --git a/lib/CPathfinder.cpp b/lib/CPathfinder.cpp index 24a4a023b..88a2877cb 100644 --- a/lib/CPathfinder.cpp +++ b/lib/CPathfinder.cpp @@ -96,7 +96,7 @@ void CPathfinder::calculatePaths() cp = pq.top(); pq.pop(); cp->locked = true; - ct = getTile(cp->coord); + ct = &gs->map->getTile(cp->coord); ctObj = ct->topVisitableObj(isSourceInitialPosition()); int movement = cp->moveRemains, turn = cp->turns; @@ -111,7 +111,7 @@ void CPathfinder::calculatePaths() addNeighbours(); for(auto & neighbour : neighbours) { - dt = getTile(neighbour); + dt = &gs->map->getTile(neighbour); dtObj = dt->topVisitableObj(); for(ELayer i = ELayer::LAND; i <= ELayer::AIR; i.advance(1)) { @@ -135,7 +135,7 @@ void CPathfinder::calculatePaths() continue; destAction = getDestAction(); - int cost = CPathfinderHelper::getMovementCost(hero, cp->coord, dp->coord, movement, hlp->getTurnInfo()); + int cost = CPathfinderHelper::getMovementCost(hero, cp->coord, dp->coord, ct, dt, movement, hlp->getTurnInfo()); int remains = movement - cost; if(destAction == CGPathNode::EMBARK || destAction == CGPathNode::DISEMBARK) { @@ -148,7 +148,7 @@ void CPathfinder::calculatePaths() //occurs rarely, when hero with low movepoints tries to leave the road hlp->updateTurnInfo(++turnAtNextTile); int moveAtNextTile = hlp->getMaxMovePoints(i); - cost = CPathfinderHelper::getMovementCost(hero, cp->coord, dp->coord, moveAtNextTile, hlp->getTurnInfo()); //cost must be updated, movement points changed :( + cost = CPathfinderHelper::getMovementCost(hero, cp->coord, dp->coord, ct, dt, moveAtNextTile, hlp->getTurnInfo()); //cost must be updated, movement points changed :( remains = moveAtNextTile - cost; } @@ -222,7 +222,7 @@ void CPathfinder::addTeleportExits() auto pos = obj->getBlockedPos(); for(auto p : pos) { - if(getTile(p)->topVisitableId() == obj->ID) + if(gs->map->getTile(p).topVisitableId() == obj->ID) neighbours.push_back(p); } } @@ -511,7 +511,7 @@ bool CPathfinder::isSourceGuarded() const /// - Map start with hero on guarded tile /// - Dimention door used /// TODO: check what happen when there is several guards - if(guardingCreaturePosition(cp->coord) != int3(-1, -1, -1) && !isSourceInitialPosition()) + if(gs->guardingCreaturePosition(cp->coord).valid() && !isSourceInitialPosition()) { return true; } @@ -528,7 +528,7 @@ bool CPathfinder::isDestinationGuarded(const bool ignoreAccessibility) const { /// isDestinationGuarded is exception needed for garrisons. /// When monster standing behind garrison it's visitable and guarded at the same time. - if(guardingCreaturePosition(dp->coord).valid() + if(gs->guardingCreaturePosition(dp->coord).valid() && (ignoreAccessibility || dp->accessible == CGPathNode::BLOCKVIS)) { return true; @@ -539,7 +539,7 @@ bool CPathfinder::isDestinationGuarded(const bool ignoreAccessibility) const bool CPathfinder::isDestinationGuardian() const { - return guardingCreaturePosition(cp->coord) == dp->coord; + return gs->guardingCreaturePosition(cp->coord) == dp->coord; } void CPathfinder::initializeGraph() @@ -558,7 +558,7 @@ void CPathfinder::initializeGraph() { for(pos.z=0; pos.z < out.sizes.z; ++pos.z) { - const TerrainTile * tinfo = getTile(pos); + const TerrainTile * tinfo = &gs->map->getTile(pos); switch(tinfo->terType) { case ETerrainType::ROCK: @@ -893,7 +893,7 @@ void CPathfinderHelper::getNeighbours(const CMap * map, const TerrainTile & srct } } -int CPathfinderHelper::getMovementCost(const CGHeroInstance * h, const int3 & src, const int3 & dst, const int remainingMovePoints, const TurnInfo * ti, const bool checkLast) +int CPathfinderHelper::getMovementCost(const CGHeroInstance * h, const int3 & src, const int3 & dst, const TerrainTile * ct, const TerrainTile * dt, const int remainingMovePoints, const TurnInfo * ti, const bool checkLast) { if(src == dst) //same tile return 0; @@ -901,23 +901,27 @@ int CPathfinderHelper::getMovementCost(const CGHeroInstance * h, const int3 & sr if(!ti) ti = new TurnInfo(h); - auto s = h->cb->getTile(src), d = h->cb->getTile(dst); + if(ct == nullptr || dt == nullptr) + { + ct = h->cb->getTile(src); + dt = h->cb->getTile(dst); + } /// TODO: by the original game rules hero shouldn't be affected by terrain penalty while flying. /// Also flying movement only has penalty when player moving over blocked tiles. /// So if you only have base flying with 40% penalty you can still ignore terrain penalty while having zero flying penalty. - int ret = h->getTileCost(*d, *s, ti); + int ret = h->getTileCost(*dt, *ct, ti); /// Unfortunately this can't be implemented yet as server don't know when player flying and when he's not. /// Difference in cost calculation on client and server is much worse than incorrect cost. /// So this one is waiting till server going to use pathfinder rules for path validation. - if(d->blocked && ti->hasBonusOfType(Bonus::FLYING_MOVEMENT)) + if(dt->blocked && ti->hasBonusOfType(Bonus::FLYING_MOVEMENT)) { ret *= (100.0 + ti->valOfBonuses(Bonus::FLYING_MOVEMENT)) / 100.0; } - else if(d->terType == ETerrainType::WATER) + else if(dt->terType == ETerrainType::WATER) { - if(h->boat && s->hasFavourableWinds() && d->hasFavourableWinds()) //Favourable Winds + if(h->boat && ct->hasFavourableWinds() && dt->hasFavourableWinds()) //Favourable Winds ret *= 0.666; else if(!h->boat && ti->hasBonusOfType(Bonus::WATER_WALKING)) { @@ -941,10 +945,10 @@ int CPathfinderHelper::getMovementCost(const CGHeroInstance * h, const int3 & sr { std::vector vec; vec.reserve(8); //optimization - getNeighbours(h->cb->gameState()->map, *d, dst, vec, s->terType != ETerrainType::WATER, true); + getNeighbours(h->cb->gameState()->map, *dt, dst, vec, ct->terType != ETerrainType::WATER, true); for(auto & elem : vec) { - int fcost = getMovementCost(h, dst, elem, left, ti, false); + int fcost = getMovementCost(h, dst, elem, nullptr, nullptr, left, ti, false); if(fcost <= left) return ret; } @@ -955,7 +959,7 @@ int CPathfinderHelper::getMovementCost(const CGHeroInstance * h, const int3 & sr int CPathfinderHelper::getMovementCost(const CGHeroInstance * h, const int3 & dst) { - return getMovementCost(h, h->visitablePos(), dst, h->movement); + return getMovementCost(h, h->visitablePos(), dst, nullptr, nullptr, h->movement); } CGPathNode::CGPathNode() diff --git a/lib/CPathfinder.h b/lib/CPathfinder.h index a5beef562..d94ccb6e8 100644 --- a/lib/CPathfinder.h +++ b/lib/CPathfinder.h @@ -248,7 +248,7 @@ public: static void getNeighbours(const CMap * map, const TerrainTile & srct, const int3 & tile, std::vector & vec, const boost::logic::tribool & onLand, const bool limitCoastSailing); - static int getMovementCost(const CGHeroInstance * h, const int3 & src, const int3 & dst, const int remainingMovePoints =- 1, const TurnInfo * ti = nullptr, const bool checkLast = true); + 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: diff --git a/server/CGameHandler.cpp b/server/CGameHandler.cpp index 4fa958575..7be5428e6 100644 --- a/server/CGameHandler.cpp +++ b/server/CGameHandler.cpp @@ -1781,7 +1781,7 @@ bool CGameHandler::moveHero( ObjectInstanceID hid, int3 dst, ui8 teleporting, bo 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, h->movement, ti); + 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) From 649ebfad2f413728d79403ab37100ea0f30ef177 Mon Sep 17 00:00:00 2001 From: ArseniyShestakov Date: Sun, 22 Nov 2015 05:34:37 +0300 Subject: [PATCH 110/168] TerrainTile::topVisitableObj: avoid costly vector copying --- lib/mapping/CMap.cpp | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/lib/mapping/CMap.cpp b/lib/mapping/CMap.cpp index 644589df1..e4b7b11f1 100644 --- a/lib/mapping/CMap.cpp +++ b/lib/mapping/CMap.cpp @@ -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 From 8217eb3a82594f895456f56161eb9ce3d6c1e193 Mon Sep 17 00:00:00 2001 From: ArseniyShestakov Date: Sun, 22 Nov 2015 06:16:16 +0300 Subject: [PATCH 111/168] CPathfinder: split isLayerTransitionPossible into two functions This way we can avoid usage of getNode for layers that clearly can't be used. --- lib/CPathfinder.cpp | 68 ++++++++++++++++++++++++++++----------------- lib/CPathfinder.h | 1 + 2 files changed, 44 insertions(+), 25 deletions(-) diff --git a/lib/CPathfinder.cpp b/lib/CPathfinder.cpp index 88a2877cb..ecfd292b4 100644 --- a/lib/CPathfinder.cpp +++ b/lib/CPathfinder.cpp @@ -115,6 +115,12 @@ void CPathfinder::calculatePaths() dtObj = dt->topVisitableObj(); for(ELayer i = ELayer::LAND; i <= ELayer::AIR; i.advance(1)) { + if(!hlp->isLayerAvailable(i)) + continue; + + if(cp->layer != i && !isLayerTransitionPossible(i)) + continue; + dp = out.getNode(neighbour, i); if(dp->accessible == CGPathNode::NOT_SET) continue; @@ -125,9 +131,6 @@ void CPathfinder::calculatePaths() if(!passOneTurnLimitCheck(cp->turns != turn)) continue; - if(!hlp->isLayerAvailable(i)) - continue; - if(cp->layer != i && !isLayerTransitionPossible()) continue; @@ -249,7 +252,7 @@ void CPathfinder::addTeleportExits() } } -bool CPathfinder::isLayerTransitionPossible() const +bool CPathfinder::isLayerTransitionPossible(const ELayer destLayer) const { /// No layer transition allowed when previous node action is BATTLE if(cp->action == CGPathNode::BATTLE) @@ -258,12 +261,42 @@ bool CPathfinder::isLayerTransitionPossible() const switch(cp->layer) { case ELayer::LAND: - if(options.lightweightFlyingMode && dp->layer == ELayer::AIR) - { - if(!isSourceInitialPosition()) - return false; - } - else if(dp->layer == ELayer::SAIL) + if(destLayer != ELayer::AIR) + return true; + + if(!options.lightweightFlyingMode || isSourceInitialPosition()) + return true; + + break; + + case ELayer::SAIL: + if(destLayer == ELayer::LAND && dt->isCoastal()) + return true; + + break; + + case ELayer::AIR: + if(destLayer == ELayer::LAND) + return true; + + break; + + case ELayer::WATER: + if(destLayer == ELayer::LAND) + return true; + + break; + } + + return false; +} + +bool CPathfinder::isLayerTransitionPossible() const +{ + switch(cp->layer) + { + case ELayer::LAND: + if(dp->layer == ELayer::SAIL) { /// Cannot enter empty water tile from land -> it has to be visitable if(dp->accessible == CGPathNode::ACCESSIBLE) @@ -273,12 +306,6 @@ bool CPathfinder::isLayerTransitionPossible() const break; case ELayer::SAIL: - if(dp->layer != ELayer::LAND) - return false; - - if(!dt->isCoastal()) - return false; - //tile must be accessible -> exception: unblocked blockvis tiles -> clear but guarded by nearby monster coast if((dp->accessible != CGPathNode::ACCESSIBLE && (dp->accessible != CGPathNode::BLOCKVIS || dt->blocked)) || dt->visitable) //TODO: passableness problem -> town says it's passable (thus accessible) but we obviously can't disembark onto town gate @@ -289,9 +316,6 @@ bool CPathfinder::isLayerTransitionPossible() const break; case ELayer::AIR: - if(dp->layer != ELayer::LAND) - return false; - if(options.originalMovementRules) { if((cp->accessible != CGPathNode::ACCESSIBLE && @@ -309,12 +333,6 @@ bool CPathfinder::isLayerTransitionPossible() const } break; - - case ELayer::WATER: - if(dp->layer != ELayer::LAND) - return false; - - break; } return true; diff --git a/lib/CPathfinder.h b/lib/CPathfinder.h index d94ccb6e8..2e942935c 100644 --- a/lib/CPathfinder.h +++ b/lib/CPathfinder.h @@ -180,6 +180,7 @@ private: void addNeighbours(); void addTeleportExits(); + bool isLayerTransitionPossible(const ELayer dstLayer) const; bool isLayerTransitionPossible() const; bool isMovementToDestPossible() const; bool isMovementAfterDestPossible() const; From 5a87f58e09b0acfd5aa296e668fd712094b89321 Mon Sep 17 00:00:00 2001 From: ArseniyShestakov Date: Sun, 22 Nov 2015 06:23:54 +0300 Subject: [PATCH 112/168] CPathfinder: optimize checks order in calculatePaths --- lib/CPathfinder.cpp | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/lib/CPathfinder.cpp b/lib/CPathfinder.cpp index ecfd292b4..4fa2412b0 100644 --- a/lib/CPathfinder.cpp +++ b/lib/CPathfinder.cpp @@ -96,8 +96,6 @@ void CPathfinder::calculatePaths() cp = pq.top(); pq.pop(); cp->locked = true; - ct = &gs->map->getTile(cp->coord); - ctObj = ct->topVisitableObj(isSourceInitialPosition()); int movement = cp->moveRemains, turn = cp->turns; hlp->updateTurnInfo(turn); @@ -105,7 +103,11 @@ void CPathfinder::calculatePaths() { hlp->updateTurnInfo(++turn); movement = hlp->getMaxMovePoints(cp->layer); + if(!passOneTurnLimitCheck(true)) + continue; } + ct = &gs->map->getTile(cp->coord); + ctObj = ct->topVisitableObj(isSourceInitialPosition()); //add accessible neighbouring nodes to the queue addNeighbours(); @@ -122,13 +124,10 @@ void CPathfinder::calculatePaths() continue; dp = out.getNode(neighbour, i); - if(dp->accessible == CGPathNode::NOT_SET) - continue; - if(dp->locked) continue; - if(!passOneTurnLimitCheck(cp->turns != turn)) + if(dp->accessible == CGPathNode::NOT_SET) continue; if(cp->layer != i && !isLayerTransitionPossible()) From fc06db4c4f6a7311c132b1621f4c727b0d590c6a Mon Sep 17 00:00:00 2001 From: ArseniyShestakov Date: Sun, 22 Nov 2015 07:14:52 +0300 Subject: [PATCH 113/168] Move STRONG_INLINE define into Global.h --- Global.h | 12 ++++++++++++ client/gui/SDL_Extensions.h | 9 --------- 2 files changed, 12 insertions(+), 9 deletions(-) diff --git a/Global.h b/Global.h index 297d0ac76..56a35a9f4 100644 --- a/Global.h +++ b/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 . Use std::[min/max] from 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 diff --git a/client/gui/SDL_Extensions.h b/client/gui/SDL_Extensions.h index 0aa691015..0f9a5e7d4 100644 --- a/client/gui/SDL_Extensions.h +++ b/client/gui/SDL_Extensions.h @@ -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; From e1a360408df66bda1f9e6d95e26b230321d26d21 Mon Sep 17 00:00:00 2001 From: ArseniyShestakov Date: Sun, 22 Nov 2015 07:16:20 +0300 Subject: [PATCH 114/168] GameConstants: move operators into header and always inline them --- lib/GameConstants.cpp | 52 --------------------------------- lib/GameConstants.h | 67 +++++++++++++++++++++++++++---------------- 2 files changed, 42 insertions(+), 77 deletions(-) diff --git a/lib/GameConstants.cpp b/lib/GameConstants.cpp index 2b9556091..864b1aba2 100644 --- a/lib/GameConstants.cpp +++ b/lib/GameConstants.cpp @@ -25,58 +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(EPathfindingLayer, EPathfindingLayer::EEPathfindingLayer) - -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]; diff --git a/lib/GameConstants.h b/lib/GameConstants.h index 3607ae5bf..20262fee0 100644 --- a/lib/GameConstants.h +++ b/lib/GameConstants.h @@ -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 { @@ -651,7 +670,7 @@ public: EObj num; }; -ID_LIKE_OPERATORS_DECLS(Obj, Obj::EObj) +ID_LIKE_OPERATORS(Obj, Obj::EObj) namespace SecSkillLevel { @@ -743,7 +762,7 @@ public: 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 { @@ -763,7 +782,7 @@ public: DLL_LINKAGE std::ostream & operator<<(std::ostream & os, const EPathfindingLayer pathfindingLayer); -ID_LIKE_OPERATORS_DECLS(EPathfindingLayer, EPathfindingLayer::EEPathfindingLayer) +ID_LIKE_OPERATORS(EPathfindingLayer, EPathfindingLayer::EEPathfindingLayer) class BFieldType { @@ -786,7 +805,7 @@ public: EBFieldType num; }; -ID_LIKE_OPERATORS_DECLS(BFieldType, BFieldType::EBFieldType) +ID_LIKE_OPERATORS(BFieldType, BFieldType::EBFieldType) namespace EPlayerStatus { @@ -826,7 +845,7 @@ public: EArtifactPosition num; }; -ID_LIKE_OPERATORS_DECLS(ArtifactPosition, ArtifactPosition::EArtifactPosition) +ID_LIKE_OPERATORS(ArtifactPosition, ArtifactPosition::EArtifactPosition) class ArtifactID { @@ -871,7 +890,7 @@ public: EArtifactID num; }; -ID_LIKE_OPERATORS_DECLS(ArtifactID, ArtifactID::EArtifactID) +ID_LIKE_OPERATORS(ArtifactID, ArtifactID::EArtifactID) class CreatureID { @@ -915,7 +934,7 @@ public: ECreatureID num; }; -ID_LIKE_OPERATORS_DECLS(CreatureID, CreatureID::ECreatureID) +ID_LIKE_OPERATORS(CreatureID, CreatureID::ECreatureID) class SpellID { @@ -959,7 +978,7 @@ public: ESpellID num; }; -ID_LIKE_OPERATORS_DECLS(SpellID, SpellID::ESpellID) +ID_LIKE_OPERATORS(SpellID, SpellID::ESpellID) enum class ESpellSchool: ui8 { @@ -969,8 +988,6 @@ enum class ESpellSchool: ui8 EARTH = 3 }; -ID_LIKE_OPERATORS_DECLS(SpellID, SpellID::ESpellID) - // Typedef declarations typedef ui8 TFaction; typedef si64 TExpType; @@ -981,8 +998,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 From 6dacb84404c25b7a5baadf793eceb3f0197359e8 Mon Sep 17 00:00:00 2001 From: ArseniyShestakov Date: Sun, 22 Nov 2015 07:32:33 +0300 Subject: [PATCH 115/168] CPathfinder::addNeighbours: avoid allocating new vector each time --- lib/CPathfinder.cpp | 10 +++++----- lib/CPathfinder.h | 1 + 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/lib/CPathfinder.cpp b/lib/CPathfinder.cpp index 4fa2412b0..dd68fa838 100644 --- a/lib/CPathfinder.cpp +++ b/lib/CPathfinder.cpp @@ -53,6 +53,7 @@ CPathfinder::CPathfinder(CPathsInfo & _out, CGameState * _gs, const CGHeroInstan hlp = make_unique(hero, options); initializeGraph(); + neighbourTiles.reserve(8); neighbours.reserve(16); } @@ -192,19 +193,18 @@ void CPathfinder::calculatePaths() void CPathfinder::addNeighbours() { neighbours.clear(); - std::vector tiles; - tiles.reserve(8); - CPathfinderHelper::getNeighbours(gs->map, *ct, cp->coord, tiles, boost::logic::indeterminate, cp->layer == ELayer::SAIL); + neighbourTiles.clear(); + CPathfinderHelper::getNeighbours(gs->map, *ct, cp->coord, neighbourTiles, boost::logic::indeterminate, cp->layer == ELayer::SAIL); if(isSourceVisitableObj()) { - for(int3 tile: tiles) + for(int3 tile: neighbourTiles) { if(canMoveBetween(tile, ctObj->visitablePos())) neighbours.push_back(tile); } } else - vstd::concatenate(neighbours, tiles); + vstd::concatenate(neighbours, neighbourTiles); } void CPathfinder::addTeleportExits() diff --git a/lib/CPathfinder.h b/lib/CPathfinder.h index 2e942935c..a6015d974 100644 --- a/lib/CPathfinder.h +++ b/lib/CPathfinder.h @@ -169,6 +169,7 @@ private: }; boost::heap::priority_queue > pq; + std::vector neighbourTiles; std::vector neighbours; CGPathNode * cp; //current (source) path node -> we took it from the queue From 2632389aaf7104ac08fa270123f82da72c66f3a1 Mon Sep 17 00:00:00 2001 From: ArseniyShestakov Date: Sun, 22 Nov 2015 21:06:37 +0300 Subject: [PATCH 116/168] CPathfinder: bring FoW reference back to avoid performance loss This one is revert 148355908d968e4e87375a3b727fc8c3a03d8451 --- lib/CPathfinder.cpp | 4 ++-- lib/CPathfinder.h | 1 + 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/lib/CPathfinder.cpp b/lib/CPathfinder.cpp index dd68fa838..fa2f30145 100644 --- a/lib/CPathfinder.cpp +++ b/lib/CPathfinder.cpp @@ -37,7 +37,7 @@ CPathfinder::PathfinderOptions::PathfinderOptions() } CPathfinder::CPathfinder(CPathsInfo & _out, CGameState * _gs, const CGHeroInstance * _hero) - : CGameInfoCallback(_gs, boost::optional()), out(_out), hero(_hero) + : CGameInfoCallback(_gs, boost::optional()), out(_out), hero(_hero), FoW(getPlayerTeam(hero->tempOwner)->fogOfWarMap) { assert(hero); assert(hero == getHero(hero->id)); @@ -602,7 +602,7 @@ void CPathfinder::initializeGraph() CGPathNode::EAccessibility CPathfinder::evaluateAccessibility(const int3 & pos, const TerrainTile * tinfo, const ELayer layer) const { - if(tinfo->terType == ETerrainType::ROCK || !isVisible(pos, hero->tempOwner)) + if(tinfo->terType == ETerrainType::ROCK || !FoW[pos.x][pos.y][pos.z]) return CGPathNode::BLOCKED; switch(layer) diff --git a/lib/CPathfinder.h b/lib/CPathfinder.h index a6015d974..af5cea733 100644 --- a/lib/CPathfinder.h +++ b/lib/CPathfinder.h @@ -153,6 +153,7 @@ private: CPathsInfo & out; const CGHeroInstance * hero; + const std::vector > > &FoW; unique_ptr hlp; struct NodeComparer From f4c06660e413dd8da0f06344c53ed01349d1237e Mon Sep 17 00:00:00 2001 From: ArseniyShestakov Date: Sun, 22 Nov 2015 21:31:47 +0300 Subject: [PATCH 117/168] CPathfinder::evaluateAccessibility: apply more optimizations --- lib/CPathfinder.cpp | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/lib/CPathfinder.cpp b/lib/CPathfinder.cpp index fa2f30145..4cd40b404 100644 --- a/lib/CPathfinder.cpp +++ b/lib/CPathfinder.cpp @@ -619,14 +619,14 @@ CGPathNode::EAccessibility CPathfinder::evaluateAccessibility(const int3 & pos, { for(const CGObjectInstance * obj : tinfo->visitableObjects) { - if(obj->passableFor(hero->tempOwner)) - { - return CGPathNode::ACCESSIBLE; - } - else if(obj->blockVisit) + if(obj->blockVisit) { return CGPathNode::BLOCKVIS; } + else if(obj->passableFor(hero->tempOwner)) + { + return CGPathNode::ACCESSIBLE; + } else if(canSeeObj(obj)) { return CGPathNode::VISITABLE; @@ -634,13 +634,15 @@ CGPathNode::EAccessibility CPathfinder::evaluateAccessibility(const int3 & pos, } } } - else if(guardingCreaturePosition(pos).valid() && !tinfo->blocked) + else if(tinfo->blocked) + { + return CGPathNode::BLOCKED; + } + else if(gs->guardingCreaturePosition(pos).valid()) { // Monster close by; blocked visit for battle return CGPathNode::BLOCKVIS; } - else if(tinfo->blocked) - return CGPathNode::BLOCKED; break; From df4515901d6da7c16a6404f43977df32c304d705 Mon Sep 17 00:00:00 2001 From: ArseniyShestakov Date: Mon, 23 Nov 2015 14:11:08 +0300 Subject: [PATCH 118/168] CPathfinder: fix special movement boundary check Forgot to fix it for water walking after fe12b8f664a6f38b71cf914a6326d6cb81dc1253 --- lib/CPathfinder.cpp | 23 ++++++++++++++--------- lib/CPathfinder.h | 3 +++ 2 files changed, 17 insertions(+), 9 deletions(-) diff --git a/lib/CPathfinder.cpp b/lib/CPathfinder.cpp index 4cd40b404..8be71f212 100644 --- a/lib/CPathfinder.cpp +++ b/lib/CPathfinder.cpp @@ -59,16 +59,21 @@ CPathfinder::CPathfinder(CPathsInfo & _out, CGameState * _gs, const CGHeroInstan void CPathfinder::calculatePaths() { - auto passOneTurnLimitCheck = [&](bool shouldCheck) -> bool + auto passOneTurnLimitCheck = [&]() -> bool { - if(options.oneTurnSpecialLayersLimit && shouldCheck) + if(!options.oneTurnSpecialLayersLimit) + return true; + + if(cp->layer == ELayer::WATER) + return false; + if(cp->layer == ELayer::AIR) { - if((cp->layer == ELayer::AIR || cp->layer == ELayer::WATER) - && cp->accessible != CGPathNode::ACCESSIBLE) - { + if(options.originalMovementRules && cp->accessible == CGPathNode::ACCESSIBLE) + return true; + else return false; - } } + return true; }; @@ -104,7 +109,7 @@ void CPathfinder::calculatePaths() { hlp->updateTurnInfo(++turn); movement = hlp->getMaxMovePoints(cp->layer); - if(!passOneTurnLimitCheck(true)) + if(!passOneTurnLimitCheck()) continue; } ct = &gs->map->getTile(cp->coord); @@ -155,8 +160,8 @@ void CPathfinder::calculatePaths() remains = moveAtNextTile - cost; } - if(isBetterWay(remains, turnAtNextTile) - && passOneTurnLimitCheck(cp->turns != turnAtNextTile || !remains)) + if(isBetterWay(remains, turnAtNextTile) && + ((cp->turns == turnAtNextTile && remains) || passOneTurnLimitCheck())) { assert(dp != cp->theNodeBefore); //two tiles can't point to each other dp->moveRemains = remains; diff --git a/lib/CPathfinder.h b/lib/CPathfinder.h index af5cea733..29c5bd35e 100644 --- a/lib/CPathfinder.h +++ b/lib/CPathfinder.h @@ -135,6 +135,9 @@ private: /// /// 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. From 0b9a9ab26d28e45c75e0c01fb9fd2cc60aa6134e Mon Sep 17 00:00:00 2001 From: ArseniyShestakov Date: Tue, 24 Nov 2015 13:36:51 +0300 Subject: [PATCH 119/168] Add description of new pathfinder features into changelog --- ChangeLog | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/ChangeLog b/ChangeLog index 0c22d23f9..698c2b1e2 100644 --- a/ChangeLog +++ b/ChangeLog @@ -3,6 +3,10 @@ GENERAL: * New Bonus NO_TERRAIN_PENALTY * Nomads will remove Sand movement penalty from army +* Flying and water walking is now supported in pathfinder +* New artifacts supported +- Angel Wings +- Boots of Levitation ADVETURE AI: * Fixed AI trying to go through underground rock From aab5a05036360cd631a9cd55ed4471cf046eff1c Mon Sep 17 00:00:00 2001 From: edeksumo Date: Tue, 24 Nov 2015 20:55:11 +0100 Subject: [PATCH 120/168] Fix crash during auto battle --- lib/NetPacksLib.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/NetPacksLib.cpp b/lib/NetPacksLib.cpp index 505fb087e..50c02a6f5 100644 --- a/lib/NetPacksLib.cpp +++ b/lib/NetPacksLib.cpp @@ -1304,9 +1304,6 @@ DLL_LINKAGE void StartAction::applyGs( CGameState *gs ) { CStack *st = gs->curB->getStack(ba.stackNumber); - if(vstd::contains(st->state, EBattleStackState::DEFENDING_ANIM)) - st->state -= EBattleStackState::DEFENDING_ANIM; - if(ba.actionType == Battle::END_TACTIC_PHASE) { gs->curB->tacticDistance = 0; @@ -1332,15 +1329,18 @@ DLL_LINKAGE void StartAction::applyGs( CGameState *gs ) switch(ba.actionType) { case Battle::DEFEND: + st->state -= EBattleStackState::DEFENDING_ANIM; st->state.insert(EBattleStackState::DEFENDING); st->state.insert(EBattleStackState::DEFENDING_ANIM); break; case Battle::WAIT: + st->state -= EBattleStackState::DEFENDING_ANIM; st->state.insert(EBattleStackState::WAITING); return; case Battle::HERO_SPELL: //no change in current stack state return; default: //any active stack action - attack, catapult, heal, spell... + st->state -= EBattleStackState::DEFENDING_ANIM; st->state.insert(EBattleStackState::MOVED); break; } From 2276af70dcd848387d31422797c9a8e5bdda89a9 Mon Sep 17 00:00:00 2001 From: ArseniyShestakov Date: Wed, 25 Nov 2015 07:25:44 +0300 Subject: [PATCH 121/168] PreGame: avoid crashing on random map options Options is still broken, but at least we shouldn't crash there. --- client/CPreGame.cpp | 8 ++++---- lib/rmg/CMapGenOptions.cpp | 7 ++++++- lib/rmg/CMapGenOptions.h | 2 ++ 3 files changed, 12 insertions(+), 5 deletions(-) diff --git a/client/CPreGame.cpp b/client/CPreGame.cpp index 51c1d1ea6..a0f7808fe 100644 --- a/client/CPreGame.cpp +++ b/client/CPreGame.cpp @@ -1875,9 +1875,9 @@ void CRandomMapTab::updateMapInfo() // Generate player information mapInfo->mapHeader->players.clear(); - int playersToGen = (mapGenOptions.getPlayerCount() == CMapGenOptions::RANDOM_SIZE - || mapGenOptions.getCompOnlyPlayerCount() == CMapGenOptions::RANDOM_SIZE) - ? 8 : mapGenOptions.getPlayerCount() + mapGenOptions.getCompOnlyPlayerCount(); + int playersToGen = PlayerColor::PLAYER_LIMIT_I; + if(mapGenOptions.getPlayerCount() != CMapGenOptions::RANDOM_SIZE) + playersToGen = mapGenOptions.getPlayerCount(); mapInfo->mapHeader->howManyTeams = playersToGen; for(int i = 0; i < playersToGen; ++i) @@ -1885,7 +1885,7 @@ void CRandomMapTab::updateMapInfo() PlayerInfo player; player.isFactionRandom = true; player.canComputerPlay = true; - if(i >= mapGenOptions.getPlayerCount() && mapGenOptions.getPlayerCount() != CMapGenOptions::RANDOM_SIZE) + if(i >= mapGenOptions.getHumanOnlyPlayerCount()) { player.canHumanPlay = false; } diff --git a/lib/rmg/CMapGenOptions.cpp b/lib/rmg/CMapGenOptions.cpp index 2ef064f45..657865df3 100644 --- a/lib/rmg/CMapGenOptions.cpp +++ b/lib/rmg/CMapGenOptions.cpp @@ -78,6 +78,11 @@ void CMapGenOptions::setPlayerCount(si8 value) resetPlayersMap(); } +si8 CMapGenOptions::getHumanOnlyPlayerCount() const +{ + return humanPlayersCount; +} + si8 CMapGenOptions::getTeamCount() const { return teamCount; @@ -96,7 +101,7 @@ si8 CMapGenOptions::getCompOnlyPlayerCount() const void CMapGenOptions::setCompOnlyPlayerCount(si8 value) { - assert(value == RANDOM_SIZE || (value >= 0 && value <= getPlayerCount())); + assert(value == RANDOM_SIZE || (getPlayerCount() == RANDOM_SIZE || (value >= 0 && value <= getPlayerCount()))); compOnlyPlayerCount = value; if (getPlayerCount() != RANDOM_SIZE && getCompOnlyPlayerCount() != RANDOM_SIZE) diff --git a/lib/rmg/CMapGenOptions.h b/lib/rmg/CMapGenOptions.h index 7975c54f9..7846148cb 100644 --- a/lib/rmg/CMapGenOptions.h +++ b/lib/rmg/CMapGenOptions.h @@ -108,6 +108,8 @@ public: si8 getPlayerCount() const; void setPlayerCount(si8 value); + si8 getHumanOnlyPlayerCount() const; + /// The count of the teams ranging from 0 to or RANDOM_SIZE for random. si8 getTeamCount() const; void setTeamCount(si8 value); From 745ab7c7df1b54fc75d05883b7d329049dca150a Mon Sep 17 00:00:00 2001 From: ArseniyShestakov Date: Wed, 25 Nov 2015 07:40:35 +0300 Subject: [PATCH 122/168] One more fix for options --- client/CPreGame.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/client/CPreGame.cpp b/client/CPreGame.cpp index a0f7808fe..68b14df70 100644 --- a/client/CPreGame.cpp +++ b/client/CPreGame.cpp @@ -1885,7 +1885,8 @@ void CRandomMapTab::updateMapInfo() PlayerInfo player; player.isFactionRandom = true; player.canComputerPlay = true; - if(i >= mapGenOptions.getHumanOnlyPlayerCount()) + if(mapGenOptions.getCompOnlyPlayerCount() != CMapGenOptions::RANDOM_SIZE && + i >= mapGenOptions.getHumanOnlyPlayerCount()) { player.canHumanPlay = false; } From 3d8f67f864cf882d91fedbed75833676a8ce7a11 Mon Sep 17 00:00:00 2001 From: ArseniyShestakov Date: Wed, 25 Nov 2015 09:12:09 +0300 Subject: [PATCH 123/168] getFreeTiles: filter ROCK tiles. Fix issue 1870 --- lib/IGameCallback.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/IGameCallback.cpp b/lib/IGameCallback.cpp index f71d2fa15..8f8f3b30c 100644 --- a/lib/IGameCallback.cpp +++ b/lib/IGameCallback.cpp @@ -38,7 +38,7 @@ void CPrivilagedInfoCallback::getFreeTiles (std::vector &tiles) const for (int yd = 0; yd < gs->map->height; yd++) { tinfo = getTile(int3 (xd,yd,zd)); - if (tinfo->terType != ETerrainType::WATER && !tinfo->blocked) //land and free + if (tinfo->terType != ETerrainType::WATER && tinfo->terType != ETerrainType::ROCK && !tinfo->blocked) //land and free tiles.push_back (int3 (xd,yd,zd)); } } From 64c6d3c9509e755296e311cfc4756d0dafd3c5ea Mon Sep 17 00:00:00 2001 From: ArseniyShestakov Date: Wed, 25 Nov 2015 09:34:49 +0300 Subject: [PATCH 124/168] CGCreature::takenAction: add neverFlee check. Fix issue 2242 --- lib/mapObjects/MiscObjects.cpp | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/lib/mapObjects/MiscObjects.cpp b/lib/mapObjects/MiscObjects.cpp index 87b0d8671..4f2566960 100644 --- a/lib/mapObjects/MiscObjects.cpp +++ b/lib/mapObjects/MiscObjects.cpp @@ -164,7 +164,7 @@ void CGCreature::onHeroVisit( const CGHeroInstance * h ) const case FIGHT: fight(h); break; - case FLEE: //flee + case FLEE: { flee(h); break; @@ -322,13 +322,13 @@ int CGCreature::takenAction(const CGHeroInstance *h, bool allowJoin) const int charisma = powerFactor + h->getSecSkillLevel(SecondarySkill::DIPLOMACY) + sympathy; - if(charisma < character) //creatures will fight - return -2; + if(charisma < character) + return FIGHT; if (allowJoin) { if(h->getSecSkillLevel(SecondarySkill::DIPLOMACY) + sympathy + 1 >= character) - return 0; //join for free + return JOIN_FOR_FREE; else if(h->getSecSkillLevel(SecondarySkill::DIPLOMACY) * 2 + sympathy + 1 >= character) return VLC->creh->creatures[subID]->cost[6] * getStackCount(SlotID(0)); //join for gold @@ -336,10 +336,10 @@ int CGCreature::takenAction(const CGHeroInstance *h, bool allowJoin) const //we are still here - creatures have not joined hero, flee or fight - if (charisma > character) - return -1; //flee + if (charisma > character && !neverFlees) + return FLEE; else - return -2; //fight + return FIGHT; } void CGCreature::fleeDecision(const CGHeroInstance *h, ui32 pursue) const @@ -361,7 +361,7 @@ void CGCreature::joinDecision(const CGHeroInstance *h, int cost, ui32 accept) co { if(!accept) { - if(takenAction(h,false) == -1) //they flee + if(takenAction(h,false) == FLEE) { cb->setObjProperty(id, ObjProperty::MONSTER_REFUSED_JOIN, true); flee(h); From c45cd1f4c284232275a9662e7f519d707d1af555 Mon Sep 17 00:00:00 2001 From: ArseniyShestakov Date: Thu, 26 Nov 2015 10:55:02 +0300 Subject: [PATCH 125/168] CGHeroInstance::getOutOffsets: temporary fix for boat summoning Appropriate fix would require to get rid of convertPosition everywhere. Check issue 515 for more details. --- lib/mapObjects/CGHeroInstance.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/lib/mapObjects/CGHeroInstance.cpp b/lib/mapObjects/CGHeroInstance.cpp index a959334c5..1892c356e 100644 --- a/lib/mapObjects/CGHeroInstance.cpp +++ b/lib/mapObjects/CGHeroInstance.cpp @@ -1102,9 +1102,11 @@ int CGHeroInstance::getBoatType() const void CGHeroInstance::getOutOffsets(std::vector &offsets) const { + // FIXME: Offsets need to be fixed once we get rid of convertPosition + // Check issue 515 for details offsets = { - 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) + int3(-1,1,0), int3(-1,-1,0), int3(-2,0,0), int3(0,0,0), int3(0,1,0), int3(-2,1,0), int3(0,-1,0), int3(-2,-1,0) }; } From 5d2e47fabac6be04f7ab95811bd9de35bb4591d1 Mon Sep 17 00:00:00 2001 From: ArseniyShestakov Date: Fri, 27 Nov 2015 11:04:01 +0300 Subject: [PATCH 126/168] CGHeroInstance::Patrol: add initialPos --- lib/mapObjects/CGHeroInstance.h | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/lib/mapObjects/CGHeroInstance.h b/lib/mapObjects/CGHeroInstance.h index 748f84685..3e995682d 100644 --- a/lib/mapObjects/CGHeroInstance.h +++ b/lib/mapObjects/CGHeroInstance.h @@ -76,12 +76,13 @@ public: struct DLL_LINKAGE Patrol { - Patrol(){patrolling=false;patrolRadious=-1;}; + Patrol(){patrolling=false;initialPos=int3();patrolRadious=-1;}; bool patrolling; + int3 initialPos; ui32 patrolRadious; template void serialize(Handler &h, const int version) { - h & patrolling & patrolRadious; + h & patrolling & initialPos & patrolRadious; } } patrol; From d6a142ca3d34a49a954616cb76c9048c0243f917 Mon Sep 17 00:00:00 2001 From: ArseniyShestakov Date: Fri, 27 Nov 2015 11:34:03 +0300 Subject: [PATCH 127/168] CMapLoaderH3M: support for patrol initialPos in readHero --- lib/mapping/MapFormatH3M.cpp | 5 +++-- lib/mapping/MapFormatH3M.h | 2 +- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/lib/mapping/MapFormatH3M.cpp b/lib/mapping/MapFormatH3M.cpp index 1831e373a..869681360 100644 --- a/lib/mapping/MapFormatH3M.cpp +++ b/lib/mapping/MapFormatH3M.cpp @@ -1060,7 +1060,7 @@ void CMapLoaderH3M::readObjects() case Obj::RANDOM_HERO: case Obj::PRISON: { - nobj = readHero(idToBeGiven); + nobj = readHero(idToBeGiven, objPos); break; } case Obj::MONSTER: //Monster @@ -1549,7 +1549,7 @@ void CMapLoaderH3M::readCreatureSet(CCreatureSet * out, int number) out->validTypes(true); } -CGObjectInstance * CMapLoaderH3M::readHero(ObjectInstanceID idToBeGiven) +CGObjectInstance * CMapLoaderH3M::readHero(ObjectInstanceID idToBeGiven, const int3 & initialPos) { auto nhi = new CGHeroInstance(); @@ -1658,6 +1658,7 @@ CGObjectInstance * CMapLoaderH3M::readHero(ObjectInstanceID idToBeGiven) else { nhi->patrol.patrolling = true; + nhi->patrol.initialPos = initialPos; } if(map->version > EMapFormat::ROE) diff --git a/lib/mapping/MapFormatH3M.h b/lib/mapping/MapFormatH3M.h index b37fd5640..5d306bc06 100644 --- a/lib/mapping/MapFormatH3M.h +++ b/lib/mapping/MapFormatH3M.h @@ -172,7 +172,7 @@ private: * @param idToBeGiven the object id which should be set for the hero * @return a object instance */ - CGObjectInstance * readHero(ObjectInstanceID idToBeGiven); + CGObjectInstance * readHero(ObjectInstanceID idToBeGiven, const int3 & initialPos); /** * Reads a seer hut. From f6de3f94ca6a9a593fc11d4b6957133a85012a5f Mon Sep 17 00:00:00 2001 From: ArseniyShestakov Date: Sat, 28 Nov 2015 02:41:30 +0300 Subject: [PATCH 128/168] Teleports: use TTeleportExitsList typedef for exits list There is several ideas that teleportation code have to be shared between object/spells and this way we can avoid changing showTeleportDialog declaration every time. --- AI/EmptyAI/CEmptyAI.cpp | 2 +- AI/EmptyAI/CEmptyAI.h | 2 +- AI/VCAI/VCAI.cpp | 2 +- AI/VCAI/VCAI.h | 2 +- lib/CGameInterface.h | 4 +++- lib/NetPacks.h | 2 +- lib/mapObjects/MiscObjects.h | 2 ++ 7 files changed, 10 insertions(+), 6 deletions(-) diff --git a/AI/EmptyAI/CEmptyAI.cpp b/AI/EmptyAI/CEmptyAI.cpp index f46bc1e6e..58d8fa946 100644 --- a/AI/EmptyAI/CEmptyAI.cpp +++ b/AI/EmptyAI/CEmptyAI.cpp @@ -30,7 +30,7 @@ void CEmptyAI::showBlockingDialog(const std::string &text, const std::vectorselectionMade(0, askID); } -void CEmptyAI::showTeleportDialog(TeleportChannelID channel, std::vector exits, bool impassable, QueryID askID) +void CEmptyAI::showTeleportDialog(TeleportChannelID channel, TTeleportExitsList exits, bool impassable, QueryID askID) { cb->selectionMade(0, askID); } diff --git a/AI/EmptyAI/CEmptyAI.h b/AI/EmptyAI/CEmptyAI.h index eb5e43a32..262511a5d 100644 --- a/AI/EmptyAI/CEmptyAI.h +++ b/AI/EmptyAI/CEmptyAI.h @@ -15,7 +15,7 @@ public: void heroGotLevel(const CGHeroInstance *hero, PrimarySkill::PrimarySkill pskill, std::vector &skills, QueryID queryID) override; void commanderGotLevel (const CCommanderInstance * commander, std::vector skills, QueryID queryID) override; void showBlockingDialog(const std::string &text, const std::vector &components, QueryID askID, const int soundID, bool selection, bool cancel) override; - void showTeleportDialog(TeleportChannelID channel, std::vector exits, bool impassable, QueryID askID) override; + void showTeleportDialog(TeleportChannelID channel, TTeleportExitsList exits, bool impassable, QueryID askID) override; void showGarrisonDialog(const CArmedInstance *up, const CGHeroInstance *down, bool removableUnits, QueryID queryID) override; }; diff --git a/AI/VCAI/VCAI.cpp b/AI/VCAI/VCAI.cpp index a93695e94..18a1801ec 100644 --- a/AI/VCAI/VCAI.cpp +++ b/AI/VCAI/VCAI.cpp @@ -614,7 +614,7 @@ void VCAI::showBlockingDialog(const std::string &text, const std::vector exits, bool impassable, QueryID askID) +void VCAI::showTeleportDialog(TeleportChannelID channel, TTeleportExitsList exits, bool impassable, QueryID askID) { LOG_TRACE_PARAMS(logAi, "askID '%i', exits '%s'", askID % exits); NET_EVENT_HANDLER; diff --git a/AI/VCAI/VCAI.h b/AI/VCAI/VCAI.h index 23967df64..15fd7c7d4 100644 --- a/AI/VCAI/VCAI.h +++ b/AI/VCAI/VCAI.h @@ -202,7 +202,7 @@ public: virtual void commanderGotLevel (const CCommanderInstance * commander, std::vector skills, QueryID queryID) override; //TODO virtual void showBlockingDialog(const std::string &text, const std::vector &components, QueryID askID, const int soundID, bool selection, bool cancel) override; //Show a dialog, player must take decision. If selection then he has to choose between one of given components, if cancel he is allowed to not choose. After making choice, CCallback::selectionMade should be called with number of selected component (1 - n) or 0 for cancel (if allowed) and askID. virtual void showGarrisonDialog(const CArmedInstance *up, const CGHeroInstance *down, bool removableUnits, QueryID queryID) override; //all stacks operations between these objects become allowed, interface has to call onEnd when done - virtual void showTeleportDialog(TeleportChannelID channel, std::vector exits, bool impassable, QueryID askID) override; + virtual void showTeleportDialog(TeleportChannelID channel, TTeleportExitsList exits, bool impassable, QueryID askID) override; virtual void saveGame(COSer & h, const int version) override; //saving virtual void loadGame(CISer & h, const int version) override; //loading virtual void finish() override; diff --git a/lib/CGameInterface.h b/lib/CGameInterface.h index c35d52985..b120d95d7 100644 --- a/lib/CGameInterface.h +++ b/lib/CGameInterface.h @@ -7,6 +7,8 @@ #include "spells/ViewSpellInt.h" +#include "mapObjects/MiscObjects.h" + /* * CGameInterface.h, part of VCMI engine * @@ -94,7 +96,7 @@ public: // all stacks operations between these objects become allowed, interface has to call onEnd when done virtual void showGarrisonDialog(const CArmedInstance *up, const CGHeroInstance *down, bool removableUnits, QueryID queryID) = 0; - virtual void showTeleportDialog(TeleportChannelID channel, std::vector exits, bool impassable, QueryID askID) = 0; + virtual void showTeleportDialog(TeleportChannelID channel, TTeleportExitsList exits, bool impassable, QueryID askID) = 0; virtual void finish(){}; //if for some reason we want to end virtual void showWorldViewEx(const std::vector & objectPositions){}; diff --git a/lib/NetPacks.h b/lib/NetPacks.h index cdbe3ebe9..5417438f1 100644 --- a/lib/NetPacks.h +++ b/lib/NetPacks.h @@ -1221,7 +1221,7 @@ struct TeleportDialog : public Query//2006 const CGHeroInstance *hero; TeleportChannelID channel; - std::vector exits; + TTeleportExitsList exits; bool impassable; template void serialize(Handler &h, const int version) diff --git a/lib/mapObjects/MiscObjects.h b/lib/mapObjects/MiscObjects.h index 21990b688..402deb1c4 100644 --- a/lib/mapObjects/MiscObjects.h +++ b/lib/mapObjects/MiscObjects.h @@ -247,6 +247,8 @@ public: ui32 defaultResProduction(); }; +typedef std::vector TTeleportExitsList; + struct DLL_LINKAGE TeleportChannel { enum EPassability {UNKNOWN, IMPASSABLE, PASSABLE}; From 56c6785bd0ffdbadd2f7ba4885adcc3739cabf44 Mon Sep 17 00:00:00 2001 From: ArseniyShestakov Date: Sat, 28 Nov 2015 17:58:59 +0300 Subject: [PATCH 129/168] Add backward compatability for patrol and bump save format version --- lib/Connection.h | 2 +- lib/mapObjects/CGHeroInstance.h | 12 +++++++++++- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/lib/Connection.h b/lib/Connection.h index ee07bf9aa..e56d71640 100644 --- a/lib/Connection.h +++ b/lib/Connection.h @@ -27,7 +27,7 @@ #include "mapping/CCampaignHandler.h" //for CCampaignState #include "rmg/CMapGenerator.h" // for CMapGenOptions -const ui32 version = 754; +const ui32 version = 755; const ui32 minSupportedVersion = 753; class CISer; diff --git a/lib/mapObjects/CGHeroInstance.h b/lib/mapObjects/CGHeroInstance.h index 3e995682d..688c83b22 100644 --- a/lib/mapObjects/CGHeroInstance.h +++ b/lib/mapObjects/CGHeroInstance.h @@ -82,7 +82,17 @@ public: ui32 patrolRadious; template void serialize(Handler &h, const int version) { - h & patrolling & initialPos & patrolRadious; + h & patrolling; + if(version >= 755) + { + h & initialPos; + } + else if(!h.saving) + { + patrolling = false; + initialPos = int3(); + } + h & patrolRadious; } } patrol; From 6bb205b15b81d5f66a9a16da7be73fcefe82434e Mon Sep 17 00:00:00 2001 From: ArseniyShestakov Date: Sat, 28 Nov 2015 20:34:50 +0300 Subject: [PATCH 130/168] CPathfinder: patrol support using getTilesInRange For now we use getTilesInRange which isn't really correct for patrol, but good enough for first version. --- lib/CPathfinder.cpp | 47 +++++++++++++++++++++++++++++++++--- lib/CPathfinder.h | 11 +++++++++ lib/mapping/MapFormatH3M.cpp | 2 +- 3 files changed, 56 insertions(+), 4 deletions(-) diff --git a/lib/CPathfinder.cpp b/lib/CPathfinder.cpp index 8be71f212..8046f461d 100644 --- a/lib/CPathfinder.cpp +++ b/lib/CPathfinder.cpp @@ -37,7 +37,7 @@ CPathfinder::PathfinderOptions::PathfinderOptions() } CPathfinder::CPathfinder(CPathsInfo & _out, CGameState * _gs, const CGHeroInstance * _hero) - : CGameInfoCallback(_gs, boost::optional()), out(_out), hero(_hero), FoW(getPlayerTeam(hero->tempOwner)->fogOfWarMap) + : CGameInfoCallback(_gs, boost::optional()), out(_out), hero(_hero), FoW(getPlayerTeam(hero->tempOwner)->fogOfWarMap), patrolTiles({}) { assert(hero); assert(hero == getHero(hero->id)); @@ -52,6 +52,7 @@ CPathfinder::CPathfinder(CPathsInfo & _out, CGameState * _gs, const CGHeroInstan hlp = make_unique(hero, options); + initializePatrol(); initializeGraph(); neighbourTiles.reserve(8); neighbours.reserve(16); @@ -95,8 +96,10 @@ void CPathfinder::calculatePaths() CGPathNode * initialNode = out.getNode(out.hpos, hero->boat ? ELayer::SAIL : ELayer::LAND); initialNode->turns = 0; initialNode->moveRemains = hero->movement; - pq.push(initialNode); + if(isHeroPatrolLocked()) + return; + pq.push(initialNode); while(!pq.empty()) { cp = pq.top(); @@ -119,6 +122,9 @@ void CPathfinder::calculatePaths() addNeighbours(); for(auto & neighbour : neighbours) { + if(!isPatrolMovementAllowed(neighbour)) + continue; + dt = &gs->map->getTile(neighbour); dtObj = dt->topVisitableObj(); for(ELayer i = ELayer::LAND; i <= ELayer::AIR; i.advance(1)) @@ -215,7 +221,9 @@ void CPathfinder::addNeighbours() void CPathfinder::addTeleportExits() { neighbours.clear(); - if(!isSourceVisitableObj()) + /// For now we disable teleports usage for patrol movement + /// VCAI not aware about patrol and may stuck while attempt to use teleport + if(!isSourceVisitableObj() || patrolState == PATROL_RADIUS) return; const CGTeleport * objTeleport = dynamic_cast(ctObj); @@ -256,6 +264,22 @@ void CPathfinder::addTeleportExits() } } +bool CPathfinder::isHeroPatrolLocked() const +{ + return patrolState == PATROL_LOCKED; +} + +bool CPathfinder::isPatrolMovementAllowed(const int3 & dst) const +{ + if(patrolState == PATROL_RADIUS) + { + if(!vstd::contains(patrolTiles, dst)) + return false; + } + + return true; +} + bool CPathfinder::isLayerTransitionPossible(const ELayer destLayer) const { /// No layer transition allowed when previous node action is BATTLE @@ -564,6 +588,23 @@ bool CPathfinder::isDestinationGuardian() const return gs->guardingCreaturePosition(cp->coord) == dp->coord; } +void CPathfinder::initializePatrol() +{ + auto state = PATROL_NONE; + if(hero->patrol.patrolling && !getPlayer(hero->tempOwner)->human) + { + if(hero->patrol.patrolRadious) + { + state = PATROL_RADIUS; + gs->getTilesInRange(patrolTiles, hero->patrol.initialPos, hero->patrol.patrolRadious); + } + else + state = PATROL_LOCKED; + } + + patrolState = state; +} + void CPathfinder::initializeGraph() { auto updateNode = [&](int3 pos, ELayer layer, const TerrainTile * tinfo) diff --git a/lib/CPathfinder.h b/lib/CPathfinder.h index 29c5bd35e..e3e533864 100644 --- a/lib/CPathfinder.h +++ b/lib/CPathfinder.h @@ -159,6 +159,13 @@ private: const std::vector > > &FoW; unique_ptr hlp; + enum EPatrolState { + PATROL_NONE = 0, + PATROL_LOCKED = 1, + PATROL_RADIUS + } patrolState; + std::unordered_set patrolTiles; + struct NodeComparer { bool operator()(const CGPathNode * lhs, const CGPathNode * rhs) const @@ -185,6 +192,9 @@ private: void addNeighbours(); void addTeleportExits(); + bool isHeroPatrolLocked() const; + bool isPatrolMovementAllowed(const int3 & dst) const; + bool isLayerTransitionPossible(const ELayer dstLayer) const; bool isLayerTransitionPossible() const; bool isMovementToDestPossible() const; @@ -198,6 +208,7 @@ private: bool isDestinationGuarded(const bool ignoreAccessibility = true) const; bool isDestinationGuardian() const; + void initializePatrol(); void initializeGraph(); CGPathNode::EAccessibility evaluateAccessibility(const int3 & pos, const TerrainTile * tinfo, const ELayer layer) const; diff --git a/lib/mapping/MapFormatH3M.cpp b/lib/mapping/MapFormatH3M.cpp index 869681360..859abc110 100644 --- a/lib/mapping/MapFormatH3M.cpp +++ b/lib/mapping/MapFormatH3M.cpp @@ -1658,7 +1658,7 @@ CGObjectInstance * CMapLoaderH3M::readHero(ObjectInstanceID idToBeGiven, const i else { nhi->patrol.patrolling = true; - nhi->patrol.initialPos = initialPos; + nhi->patrol.initialPos = CGHeroInstance::convertPosition(initialPos, false); } if(map->version > EMapFormat::ROE) From ce5b7c73be320c742b4c2f0464eb7e6b911c8190 Mon Sep 17 00:00:00 2001 From: ArseniyShestakov Date: Sat, 28 Nov 2015 23:03:26 +0300 Subject: [PATCH 131/168] CGEvent: avoid permanent event removal after visit. Fix issue 2339 --- lib/mapObjects/CGPandoraBox.cpp | 17 ++++++++++++++++- lib/mapObjects/CGPandoraBox.h | 4 +++- 2 files changed, 19 insertions(+), 2 deletions(-) diff --git a/lib/mapObjects/CGPandoraBox.cpp b/lib/mapObjects/CGPandoraBox.cpp index e9532ae46..2d0b4d321 100644 --- a/lib/mapObjects/CGPandoraBox.cpp +++ b/lib/mapObjects/CGPandoraBox.cpp @@ -51,7 +51,7 @@ void CGPandoraBox::onHeroVisit(const CGHeroInstance * h) const void CGPandoraBox::giveContentsUpToExp(const CGHeroInstance *h) const { - cb->removeAfterVisit(this); + afterSuccessfulVisit(); InfoWindow iw; iw.player = h->getOwner(); @@ -331,6 +331,11 @@ void CGPandoraBox::heroLevelUpDone(const CGHeroInstance *hero) const giveContentsAfterExp(hero); } +void CGPandoraBox::afterSuccessfulVisit() const +{ + cb->removeAfterVisit(this); +} + void CGEvent::onHeroVisit( const CGHeroInstance * h ) const { if(!(availableFor & (1 << h->tempOwner.getNum()))) @@ -362,3 +367,13 @@ void CGEvent::activated( const CGHeroInstance * h ) const giveContentsUpToExp(h); } } + +void CGEvent::afterSuccessfulVisit() const +{ + if(removeAfterVisit) + { + cb->removeAfterVisit(this); + } + else if(hasGuardians) + hasGuardians = false; +} diff --git a/lib/mapObjects/CGPandoraBox.h b/lib/mapObjects/CGPandoraBox.h index 79ab0a417..13be53add 100644 --- a/lib/mapObjects/CGPandoraBox.h +++ b/lib/mapObjects/CGPandoraBox.h @@ -20,7 +20,7 @@ class DLL_LINKAGE CGPandoraBox : public CArmedInstance { public: std::string message; - bool hasGuardians; //helper - after battle even though we have no stacks, allows us to know that there was battle + mutable bool hasGuardians; //helper - after battle even though we have no stacks, allows us to know that there was battle //gained things: ui32 gainedExp; @@ -54,6 +54,7 @@ protected: private: void getText( InfoWindow &iw, bool &afterBattle, int val, int negative, int positive, const CGHeroInstance * h ) const; void getText( InfoWindow &iw, bool &afterBattle, int text, const CGHeroInstance * h ) const; + virtual void afterSuccessfulVisit() const; }; class DLL_LINKAGE CGEvent : public CGPandoraBox //event objects @@ -74,4 +75,5 @@ public: void onHeroVisit(const CGHeroInstance * h) const override; private: void activated(const CGHeroInstance * h) const; + void afterSuccessfulVisit() const override; }; From f940e3ed429a2cbe33b32e9e5b2fd8b78b7412d7 Mon Sep 17 00:00:00 2001 From: ArseniyShestakov Date: Sun, 29 Nov 2015 01:56:18 +0300 Subject: [PATCH 132/168] Fix issue 2239. Pillarbox when restored after alt-tab in fullscreen This isn't best solution and all SDL-related code need refactoring, but it's works. --- client/CMT.cpp | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/client/CMT.cpp b/client/CMT.cpp index 53d154ffb..f9e5a1a08 100644 --- a/client/CMT.cpp +++ b/client/CMT.cpp @@ -1073,6 +1073,15 @@ static void handleEvent(SDL_Event & ev) return; } + else if(ev.type == SDL_WINDOWEVENT) + { + switch (ev.window.event) { + case SDL_WINDOWEVENT_RESTORED: + fullScreenChanged(); + break; + } + return; + } { boost::unique_lock lock(eventsM); events.push(ev); From f55bfe41d6a2a538308d9e2656660a131377e7c9 Mon Sep 17 00:00:00 2001 From: ArseniyShestakov Date: Sun, 29 Nov 2015 12:32:06 +0300 Subject: [PATCH 133/168] Digging: implement digging status on right click. Fix issue 401 This is also fix possibility to dig on some non-blockable objects like event. --- AI/VCAI/VCAI.cpp | 2 +- client/CPlayerInterface.cpp | 15 +++++++-------- client/mapHandler.cpp | 6 ++++++ lib/GameConstants.h | 22 ++++++++++++++++++++++ lib/mapObjects/CGHeroInstance.cpp | 18 ++++-------------- lib/mapObjects/CGHeroInstance.h | 6 +----- lib/mapping/CMap.cpp | 12 ++++++++++++ lib/mapping/CMap.h | 1 + server/CGameHandler.cpp | 2 +- 9 files changed, 55 insertions(+), 29 deletions(-) diff --git a/AI/VCAI/VCAI.cpp b/AI/VCAI/VCAI.cpp index a93695e94..c1b6a43bd 100644 --- a/AI/VCAI/VCAI.cpp +++ b/AI/VCAI/VCAI.cpp @@ -2036,7 +2036,7 @@ void VCAI::tryRealize(Goals::BuildThis & g) void VCAI::tryRealize(Goals::DigAtTile & g) { assert(g.hero->visitablePos() == g.tile); //surely we want to crash here? - if (g.hero->diggingStatus() == CGHeroInstance::CAN_DIG) + if (g.hero->diggingStatus() == EDiggingStatus::CAN_DIG) { cb->dig(g.hero.get()); completeGoal(sptr(g)); // finished digging diff --git a/client/CPlayerInterface.cpp b/client/CPlayerInterface.cpp index 3b5bdf7f2..61280f419 100644 --- a/client/CPlayerInterface.cpp +++ b/client/CPlayerInterface.cpp @@ -2315,23 +2315,22 @@ void CPlayerInterface::tryDiggging(const CGHeroInstance *h) { std::string hlp; CGI->mh->getTerrainDescr(h->getPosition(false), hlp, false); + auto isDiggingPossible = h->diggingStatus(); + if(hlp.length()) + isDiggingPossible = EDiggingStatus::TILE_OCCUPIED; //TODO integrate with canDig int msgToShow = -1; - CGHeroInstance::ECanDig isDiggingPossible = h->diggingStatus(); - if(hlp.length()) - isDiggingPossible = CGHeroInstance::TILE_OCCUPIED; //TODO integrate with canDig - switch(isDiggingPossible) { - case CGHeroInstance::CAN_DIG: + case EDiggingStatus::CAN_DIG: break; - case CGHeroInstance::LACK_OF_MOVEMENT: + case EDiggingStatus::LACK_OF_MOVEMENT: msgToShow = 56; //"Digging for artifacts requires a whole day, try again tomorrow." break; - case CGHeroInstance::TILE_OCCUPIED: + case EDiggingStatus::TILE_OCCUPIED: msgToShow = 97; //Try searching on clear ground. break; - case CGHeroInstance::WRONG_TERRAIN: + case EDiggingStatus::WRONG_TERRAIN: msgToShow = 60; ////Try looking on land! break; default: diff --git a/client/mapHandler.cpp b/client/mapHandler.cpp index f78d65a17..e50d4a959 100644 --- a/client/mapHandler.cpp +++ b/client/mapHandler.cpp @@ -1579,7 +1579,13 @@ void CMapHandler::getTerrainDescr( const int3 &pos, std::string & out, bool terN if(t.hasFavourableWinds()) out = CGI->objtypeh->getObjectName(Obj::FAVORABLE_WINDS); else if(terName) + { out = CGI->generaltexth->terrainNames[t.terType]; + if(t.getDiggingStatus(false) == EDiggingStatus::CAN_DIG) + { + out = boost::str(boost::format("%s %s") % out % CGI->generaltexth->allTexts[330]); /// digging ok + } + } } void CMapHandler::discardWorldViewCache() diff --git a/lib/GameConstants.h b/lib/GameConstants.h index 1a0670225..8224f7ca5 100644 --- a/lib/GameConstants.h +++ b/lib/GameConstants.h @@ -777,6 +777,28 @@ DLL_LINKAGE std::ostream & operator<<(std::ostream & os, const ETerrainType terr ID_LIKE_OPERATORS(ETerrainType, ETerrainType::EETerrainType) +class DLL_LINKAGE EDiggingStatus +{ +public: + enum EEDiggingStatus + { + UNKNOWN = -1, + CAN_DIG = 0, + LACK_OF_MOVEMENT, + WRONG_TERRAIN, + TILE_OCCUPIED + }; + + EDiggingStatus(EEDiggingStatus _num = UNKNOWN) : num(_num) + {} + + ID_LIKE_CLASS_COMMON(EDiggingStatus, EEDiggingStatus) + + EEDiggingStatus num; +}; + +ID_LIKE_OPERATORS(EDiggingStatus, EDiggingStatus::EEDiggingStatus) + class DLL_LINKAGE EPathfindingLayer { public: diff --git a/lib/mapObjects/CGHeroInstance.cpp b/lib/mapObjects/CGHeroInstance.cpp index 1892c356e..5b4ef4822 100644 --- a/lib/mapObjects/CGHeroInstance.cpp +++ b/lib/mapObjects/CGHeroInstance.cpp @@ -1189,22 +1189,12 @@ int CGHeroInstance::movementPointsAfterEmbark(int MPsBefore, int basicCost, bool return 0; //take all MPs otherwise } -CGHeroInstance::ECanDig CGHeroInstance::diggingStatus() const +EDiggingStatus CGHeroInstance::diggingStatus() const { if(movement < maxMovePoints(true)) - return LACK_OF_MOVEMENT; - else if(cb->getTile(getPosition(false))->terType == ETerrainType::WATER) - return WRONG_TERRAIN; - else - { - const TerrainTile *t = cb->getTile(getPosition()); - //TODO look for hole - //CGI->mh->getTerrainDescr(h->getPosition(false), hlp, false); - if(/*hlp.length() || */t->blockingObjects.size() > 1) - return TILE_OCCUPIED; - else - return CAN_DIG; - } + return EDiggingStatus::LACK_OF_MOVEMENT; + + return cb->getTile(getPosition(false))->getDiggingStatus(); } ArtBearer::ArtBearer CGHeroInstance::bearerType() const diff --git a/lib/mapObjects/CGHeroInstance.h b/lib/mapObjects/CGHeroInstance.h index 748f84685..4101666fc 100644 --- a/lib/mapObjects/CGHeroInstance.h +++ b/lib/mapObjects/CGHeroInstance.h @@ -40,10 +40,6 @@ public: class DLL_LINKAGE CGHeroInstance : public CArmedInstance, public IBoatGenerator, public CArtifactSet, public ISpellCaster { public: - enum ECanDig - { - CAN_DIG, LACK_OF_MOVEMENT, WRONG_TERRAIN, TILE_OCCUPIED - }; ////////////////////////////////////////////////////////////////////////// ui8 moveDir; //format: 123 @@ -174,7 +170,7 @@ public: bool canCastThisSpell(const CSpell * spell) const; //determines if this hero can cast given spell; takes into account existing spell in spellbook, existing spellbook and artifact bonuses CStackBasicDescriptor calculateNecromancy (const BattleResult &battleResult) const; void showNecromancyDialog(const CStackBasicDescriptor &raisedStack) const; - ECanDig diggingStatus() const; //0 - can dig; 1 - lack of movement; 2 - + EDiggingStatus diggingStatus() const; ////////////////////////////////////////////////////////////////////////// diff --git a/lib/mapping/CMap.cpp b/lib/mapping/CMap.cpp index e4b7b11f1..ddc620332 100644 --- a/lib/mapping/CMap.cpp +++ b/lib/mapping/CMap.cpp @@ -146,6 +146,18 @@ bool TerrainTile::isCoastal() const return extTileFlags & 64; } +EDiggingStatus TerrainTile::getDiggingStatus(const bool excludeTop) const +{ + if(terType == ETerrainType::WATER || terType == ETerrainType::ROCK) + return EDiggingStatus::WRONG_TERRAIN; + + int allowedBlocked = excludeTop ? 1 : 0; + if(blockingObjects.size() > allowedBlocked || topVisitableObj(excludeTop)) + return EDiggingStatus::TILE_OCCUPIED; + else + return EDiggingStatus::CAN_DIG; +} + bool TerrainTile::hasFavourableWinds() const { return extTileFlags & 128; diff --git a/lib/mapping/CMap.h b/lib/mapping/CMap.h index df15775df..438e997cd 100644 --- a/lib/mapping/CMap.h +++ b/lib/mapping/CMap.h @@ -277,6 +277,7 @@ struct DLL_LINKAGE TerrainTile CGObjectInstance * topVisitableObj(bool excludeTop = false) const; bool isWater() const; bool isCoastal() const; + EDiggingStatus getDiggingStatus(const bool excludeTop = true) const; bool hasFavourableWinds() const; ETerrainType terType; diff --git a/server/CGameHandler.cpp b/server/CGameHandler.cpp index 7be5428e6..9c3b8ba5d 100644 --- a/server/CGameHandler.cpp +++ b/server/CGameHandler.cpp @@ -4896,7 +4896,7 @@ bool CGameHandler::dig( const CGHeroInstance *h ) } } - if(h->diggingStatus() != CGHeroInstance::CAN_DIG) //checks for terrain and movement + if(h->diggingStatus() != EDiggingStatus::CAN_DIG) //checks for terrain and movement COMPLAIN_RETF("Hero cannot dig (error code %d)!", h->diggingStatus()); //create a hole From 6704ea0cff605fc29feb43fccf4f49857a0589e5 Mon Sep 17 00:00:00 2001 From: ArseniyShestakov Date: Mon, 30 Nov 2015 17:51:28 +0300 Subject: [PATCH 134/168] Rumors: rename getTavernGossip to getTavernRumor for consistency Original game used "Rumors" and not "Gossips" and we already using rumor in CMap. --- client/windows/GUIClasses.cpp | 4 +++- lib/CGameInfoCallback.cpp | 2 +- lib/CGameInfoCallback.h | 2 +- 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/client/windows/GUIClasses.cpp b/client/windows/GUIClasses.cpp index 756027ab6..3a52a6262 100644 --- a/client/windows/GUIClasses.cpp +++ b/client/windows/GUIClasses.cpp @@ -705,7 +705,9 @@ CTavernWindow::CTavernWindow(const CGObjectInstance *TavernObj): new CLabel(200, 35, FONT_BIG, CENTER, Colors::YELLOW, CGI->generaltexth->jktexts[37]); new CLabel(320, 328, FONT_SMALL, CENTER, Colors::WHITE, "2500"); - new CTextBox(LOCPLINT->cb->getTavernGossip(tavernObj), Rect(32, 190, 330, 68), 0, FONT_SMALL, CENTER, Colors::WHITE); + + auto rumorText = boost::str(boost::format(CGI->generaltexth->allTexts[216]) % LOCPLINT->cb->getTavernRumor(tavernObj)); + new CTextBox(rumorText, Rect(32, 190, 330, 68), 0, FONT_SMALL, CENTER, Colors::WHITE); new CGStatusBar(new CPicture(*background, Rect(8, pos.h - 26, pos.w - 16, 19), 8, pos.h - 26)); cancel = new CButton(Point(310, 428), "ICANCEL.DEF", CButton::tooltip(CGI->generaltexth->tavernInfo[7]), std::bind(&CTavernWindow::close, this), SDLK_ESCAPE); diff --git a/lib/CGameInfoCallback.cpp b/lib/CGameInfoCallback.cpp index 56401514b..2beb8e384 100644 --- a/lib/CGameInfoCallback.cpp +++ b/lib/CGameInfoCallback.cpp @@ -564,7 +564,7 @@ EPlayerStatus::EStatus CGameInfoCallback::getPlayerStatus(PlayerColor player, bo return ps->status; } -std::string CGameInfoCallback::getTavernGossip(const CGObjectInstance * townOrTavern) const +std::string CGameInfoCallback::getTavernRumor(const CGObjectInstance * townOrTavern) const { return "GOSSIP TEST"; } diff --git a/lib/CGameInfoCallback.h b/lib/CGameInfoCallback.h index 7899acb10..20c94db30 100644 --- a/lib/CGameInfoCallback.h +++ b/lib/CGameInfoCallback.h @@ -99,7 +99,7 @@ public: int howManyTowns(PlayerColor Player) const; const CGTownInstance * getTownInfo(int val, bool mode)const; //mode = 0 -> val = player town serial; mode = 1 -> val = object id (serial) std::vector getAvailableHeroes(const CGObjectInstance * townOrTavern) const; //heroes that can be recruited - std::string getTavernGossip(const CGObjectInstance * townOrTavern) const; + std::string getTavernRumor(const CGObjectInstance * townOrTavern) const; EBuildingState::EBuildingState canBuildStructure(const CGTownInstance *t, BuildingID ID);//// 0 - no more than one capitol, 1 - lack of water, 2 - forbidden, 3 - Add another level to Mage Guild, 4 - already built, 5 - cannot build, 6 - cannot afford, 7 - build, 8 - lack of requirements virtual bool getTownInfo(const CGObjectInstance * town, InfoAboutTown & dest, const CGObjectInstance * selectedObject = nullptr) const; const CTown *getNativeTown(PlayerColor color) const; From 879eaa47f865fd30c351c2f3b26036840e77cf45 Mon Sep 17 00:00:00 2001 From: ArseniyShestakov Date: Mon, 30 Nov 2015 20:52:15 +0300 Subject: [PATCH 135/168] Rumors: client-side proof of concept implementation Actually information about this week rumor should be stored in gamestate and updated weekly --- lib/CGameInfoCallback.cpp | 66 ++++++++++++++++++++++++++++++++++++- lib/CGeneralTextHandler.cpp | 1 + lib/CGeneralTextHandler.h | 1 + 3 files changed, 67 insertions(+), 1 deletion(-) diff --git a/lib/CGameInfoCallback.cpp b/lib/CGameInfoCallback.cpp index 2beb8e384..cb534fb1f 100644 --- a/lib/CGameInfoCallback.cpp +++ b/lib/CGameInfoCallback.cpp @@ -12,6 +12,7 @@ #include "CGameInfoCallback.h" #include "CGameState.h" // PlayerState +#include "CGeneralTextHandler.h" #include "mapObjects/CObjectHandler.h" // for CGObjectInstance #include "StartInfo.h" // for StartInfo #include "BattleState.h" // for BattleInfo @@ -566,7 +567,70 @@ EPlayerStatus::EStatus CGameInfoCallback::getPlayerStatus(PlayerColor player, bo std::string CGameInfoCallback::getTavernRumor(const CGObjectInstance * townOrTavern) const { - return "GOSSIP TEST"; + std::string text = ""; + auto & rand = gs->getRandomGenerator(); + + static std::vector rumorTypes = {0, 1, 2, 2}; + auto & rumorType = *RandomGeneratorUtil::nextItem(rumorTypes, rand); + switch(rumorType) + { + case 0: + { + SThievesGuildInfo tgi; + gs->obtainPlayersStats(tgi, 20); + static std::vector statRumorTypes = {208, 209, 210};// 211, 212}; + std::vector players = {}; + auto statRumorType = *RandomGeneratorUtil::nextItem(statRumorTypes, rand); + switch(statRumorType) + { + case 208: + players = tgi.obelisks[0]; + break; + + case 209: + players = tgi.artifacts[0]; + break; + + case 210: + players = tgi.army[0]; + break; + + case 211: + /// TODO: not implemented in obtainPlayersStats + players = tgi.income[0]; + break; + + case 212: + /// TODO: Check that ultimate artifact (grail) found + break; + } + auto & playerId = *RandomGeneratorUtil::nextItem(players, rand); + std::string playerName = VLC->generaltexth->colors[playerId.getNum()]; + text = boost::str(boost::format(VLC->generaltexth->allTexts[statRumorType]) % playerName); + + break; + } + case 1: + if(gs->map->rumors.size()) + { + auto & mapRumor = *RandomGeneratorUtil::nextItem(gs->map->rumors, rand); + text = mapRumor.text; + break; + } + + /// don't break - if map don't have rumors we show predefined instead + + case 2: + do + { + text = *RandomGeneratorUtil::nextItem(VLC->generaltexth->tavernRumors, rand); + } + while(!text.length()); + + break; + } + + return text; } PlayerRelations::PlayerRelations CGameInfoCallback::getPlayerRelations( PlayerColor color1, PlayerColor color2 ) const diff --git a/lib/CGeneralTextHandler.cpp b/lib/CGeneralTextHandler.cpp index 2481fb669..03465d888 100644 --- a/lib/CGeneralTextHandler.cpp +++ b/lib/CGeneralTextHandler.cpp @@ -327,6 +327,7 @@ CGeneralTextHandler::CGeneralTextHandler() readToVector("DATA/PRISKILL.TXT", primarySkillNames); readToVector("DATA/JKTEXT.TXT", jktexts); readToVector("DATA/TVRNINFO.TXT", tavernInfo); + readToVector("DATA/RANDTVRN.TXT", tavernRumors); readToVector("DATA/TURNDUR.TXT", turnDurations); readToVector("DATA/HEROSCRN.TXT", heroscrn); readToVector("DATA/TENTCOLR.TXT", tentColors); diff --git a/lib/CGeneralTextHandler.h b/lib/CGeneralTextHandler.h index a80531cfd..bc04c3afc 100644 --- a/lib/CGeneralTextHandler.h +++ b/lib/CGeneralTextHandler.h @@ -109,6 +109,7 @@ public: //towns std::vector tcommands, hcommands, fcommands; //texts for town screen, town hall screen and fort screen std::vector tavernInfo; + std::vector tavernRumors; std::vector > zelp; std::vector lossCondtions; From 98582d628c9095e92f2c927a843b742b24b00c43 Mon Sep 17 00:00:00 2001 From: ArseniyShestakov Date: Tue, 1 Dec 2015 00:44:58 +0300 Subject: [PATCH 136/168] Rumors: move code into CGameState and add backward compatability --- lib/CGameInfoCallback.cpp | 65 +++++++----------------------------- lib/CGameState.cpp | 69 +++++++++++++++++++++++++++++++++++++++ lib/CGameState.h | 27 +++++++++++++++ lib/Connection.h | 2 +- lib/NetPacksLib.cpp | 3 ++ 5 files changed, 111 insertions(+), 55 deletions(-) diff --git a/lib/CGameInfoCallback.cpp b/lib/CGameInfoCallback.cpp index cb534fb1f..c5aa3399c 100644 --- a/lib/CGameInfoCallback.cpp +++ b/lib/CGameInfoCallback.cpp @@ -568,65 +568,22 @@ EPlayerStatus::EStatus CGameInfoCallback::getPlayerStatus(PlayerColor player, bo std::string CGameInfoCallback::getTavernRumor(const CGObjectInstance * townOrTavern) const { std::string text = ""; - auto & rand = gs->getRandomGenerator(); + if(gs->rumor.type == RumorState::RUMOR_NONE) // (version < 755 backward compatability + return text; - static std::vector rumorTypes = {0, 1, 2, 2}; - auto & rumorType = *RandomGeneratorUtil::nextItem(rumorTypes, rand); - switch(rumorType) + auto rumor = gs->rumor.last[gs->rumor.type]; + switch(gs->rumor.type) { - case 0: - { - SThievesGuildInfo tgi; - gs->obtainPlayersStats(tgi, 20); - static std::vector statRumorTypes = {208, 209, 210};// 211, 212}; - std::vector players = {}; - auto statRumorType = *RandomGeneratorUtil::nextItem(statRumorTypes, rand); - switch(statRumorType) - { - case 208: - players = tgi.obelisks[0]; - break; - - case 209: - players = tgi.artifacts[0]; - break; - - case 210: - players = tgi.army[0]; - break; - - case 211: - /// TODO: not implemented in obtainPlayersStats - players = tgi.income[0]; - break; - - case 212: - /// TODO: Check that ultimate artifact (grail) found - break; - } - auto & playerId = *RandomGeneratorUtil::nextItem(players, rand); - std::string playerName = VLC->generaltexth->colors[playerId.getNum()]; - text = boost::str(boost::format(VLC->generaltexth->allTexts[statRumorType]) % playerName); - + case RumorState::RUMOR_STATS: + text = boost::str(boost::format(VLC->generaltexth->allTexts[rumor.first]) % VLC->generaltexth->colors[rumor.second]); break; - } - case 1: - if(gs->map->rumors.size()) - { - auto & mapRumor = *RandomGeneratorUtil::nextItem(gs->map->rumors, rand); - text = mapRumor.text; - break; - } - /// don't break - if map don't have rumors we show predefined instead - - case 2: - do - { - text = *RandomGeneratorUtil::nextItem(VLC->generaltexth->tavernRumors, rand); - } - while(!text.length()); + case RumorState::RUMOR_MAP: + text = gs->map->rumors[rumor.first].text; + break; + case RumorState::RUMOR_RAND: + text = VLC->generaltexth->tavernRumors[rumor.first]; break; } diff --git a/lib/CGameState.cpp b/lib/CGameState.cpp index 411628370..e50caeb6e 100644 --- a/lib/CGameState.cpp +++ b/lib/CGameState.cpp @@ -2133,6 +2133,75 @@ int3 CGameState::guardingCreaturePosition (int3 pos) const return gs->map->guardingCreaturePositions[pos.x][pos.y][pos.z]; } +void CGameState::updateRumor() +{ + static std::vector rumorTypes = {RumorState::RUMOR_MAP, RumorState::RUMOR_STATS, RumorState::RUMOR_RAND, RumorState::RUMOR_RAND}; + static std::vector statsRumorTypes = {208, 209, 210};// 211, 212}; + + int rumorId = -1; + int rumorPlayer = PlayerColor::CANNOT_DETERMINE.getNum(); + auto & rand = gs->getRandomGenerator(); + rumor.type = *RandomGeneratorUtil::nextItem(rumorTypes, rand); + if(!gs->map->rumors.size() && rumor.type == RumorState::RUMOR_MAP) + rumor.type = RumorState::RUMOR_RAND; + + switch(rumor.type) + { + case RumorState::RUMOR_STATS: + { + SThievesGuildInfo tgi; + gs->obtainPlayersStats(tgi, 20); + std::vector players = {}; + rumorId = *RandomGeneratorUtil::nextItem(statsRumorTypes, rand); + switch(rumorId) + { + case 208: + players = tgi.obelisks[0]; + break; + + case 209: + players = tgi.artifacts[0]; + break; + + case 210: + players = tgi.army[0]; + break; + + case 211: + /// TODO: not implemented in obtainPlayersStats + players = tgi.income[0]; + break; + + case 212: + /// TODO: Check that ultimate artifact (grail) found + break; + } + rumorPlayer = RandomGeneratorUtil::nextItem(players, rand)->getNum(); + + break; + } + case RumorState::RUMOR_MAP: + rumorId = rand.nextInt(gs->map->rumors.size() - 1); + + break; + + case RumorState::RUMOR_RAND: + do + { + rumorId = rand.nextInt(VLC->generaltexth->tavernRumors.size() - 1); + } + while(!VLC->generaltexth->tavernRumors[rumorId].length()); + + break; + } + + if(vstd::contains(rumor.last, rumor.type)) + { + rumor.last.erase(rumor.type); + } + rumor.last[rumor.type] = std::make_pair(rumorId, rumorPlayer); +} + bool CGameState::isVisible(int3 pos, PlayerColor player) { if(player == PlayerColor::NEUTRAL) diff --git a/lib/CGameState.h b/lib/CGameState.h index c60a9aa34..61ad827da 100644 --- a/lib/CGameState.h +++ b/lib/CGameState.h @@ -208,6 +208,24 @@ public: }; +struct DLL_LINKAGE RumorState +{ + enum ERumorType : ui8 + { + RUMOR_NONE = 0, RUMOR_RAND, RUMOR_STATS, RUMOR_MAP + }; + + ERumorType type; + std::map> last; + + RumorState(){type = RUMOR_NONE; last = {};}; + + template void serialize(Handler &h, const int version) + { + h & type & last; + } +}; + struct UpgradeInfo { CreatureID oldID; //creature to be upgraded @@ -311,6 +329,7 @@ public: std::map players; std::map teams; CBonusSystemNode globalEffects; + RumorState rumor; boost::shared_mutex *mx; @@ -324,6 +343,7 @@ public: void calculatePaths(const CGHeroInstance *hero, CPathsInfo &out); //calculates possible paths for hero, by default uses current hero position and movement left; returns pointer to newly allocated CPath or nullptr if path does not exists int3 guardingCreaturePosition (int3 pos) const; std::vector guardingCreatures (int3 pos) const; + void updateRumor(); // ----- victory, loss condition checks ----- @@ -347,6 +367,13 @@ public: template void serialize(Handler &h, const int version) { h & scenarioOps & initialOpts & currentPlayer & day & map & players & teams & hpool & globalEffects & rand; + if(version >= 755) + { + h & rumor; + } + else + rumor = RumorState(); + BONUS_TREE_DESERIALIZATION_FIX } diff --git a/lib/Connection.h b/lib/Connection.h index ee07bf9aa..e56d71640 100644 --- a/lib/Connection.h +++ b/lib/Connection.h @@ -27,7 +27,7 @@ #include "mapping/CCampaignHandler.h" //for CCampaignState #include "rmg/CMapGenerator.h" // for CMapGenOptions -const ui32 version = 754; +const ui32 version = 755; const ui32 minSupportedVersion = 753; class CISer; diff --git a/lib/NetPacksLib.cpp b/lib/NetPacksLib.cpp index 5483c2936..e7235da68 100644 --- a/lib/NetPacksLib.cpp +++ b/lib/NetPacksLib.cpp @@ -1032,6 +1032,9 @@ DLL_LINKAGE void NewTurn::applyGs( CGameState *gs ) for(CGTownInstance* t : gs->map->towns) t->builded = 0; + + if(gs->getDate(Date::DAY_OF_WEEK) == 1) + gs->updateRumor(); } DLL_LINKAGE void SetObjectProperty::applyGs( CGameState *gs ) From 15273ac0f7ecfb2f0d139d93c6bc03b112865a9d Mon Sep 17 00:00:00 2001 From: ArseniyShestakov Date: Tue, 1 Dec 2015 02:14:11 +0300 Subject: [PATCH 137/168] Rumors: avoid repeating of same rumor twice in a row Multiple rumors of same type can go in a row, but not identical rumors. --- lib/CGameInfoCallback.cpp | 2 +- lib/CGameState.cpp | 101 ++++++++++++++++++++++---------------- lib/CGameState.h | 3 +- 3 files changed, 62 insertions(+), 44 deletions(-) diff --git a/lib/CGameInfoCallback.cpp b/lib/CGameInfoCallback.cpp index c5aa3399c..1a968ab81 100644 --- a/lib/CGameInfoCallback.cpp +++ b/lib/CGameInfoCallback.cpp @@ -575,7 +575,7 @@ std::string CGameInfoCallback::getTavernRumor(const CGObjectInstance * townOrTav switch(gs->rumor.type) { case RumorState::RUMOR_STATS: - text = boost::str(boost::format(VLC->generaltexth->allTexts[rumor.first]) % VLC->generaltexth->colors[rumor.second]); + text = boost::str(boost::format(VLC->generaltexth->allTexts[rumor.first]) % VLC->generaltexth->capColors[rumor.second]); break; case RumorState::RUMOR_MAP: diff --git a/lib/CGameState.cpp b/lib/CGameState.cpp index e50caeb6e..97e57bafa 100644 --- a/lib/CGameState.cpp +++ b/lib/CGameState.cpp @@ -2145,61 +2145,59 @@ void CGameState::updateRumor() if(!gs->map->rumors.size() && rumor.type == RumorState::RUMOR_MAP) rumor.type = RumorState::RUMOR_RAND; - switch(rumor.type) + do { - case RumorState::RUMOR_STATS: - { - SThievesGuildInfo tgi; - gs->obtainPlayersStats(tgi, 20); - std::vector players = {}; - rumorId = *RandomGeneratorUtil::nextItem(statsRumorTypes, rand); - switch(rumorId) + switch(rumor.type) { - case 208: - players = tgi.obelisks[0]; - break; + case RumorState::RUMOR_STATS: + { + SThievesGuildInfo tgi; + gs->obtainPlayersStats(tgi, 20); + std::vector players = {}; + rumorId = *RandomGeneratorUtil::nextItem(statsRumorTypes, rand); + switch(rumorId) + { + case 208: + players = tgi.obelisks[0]; + break; - case 209: - players = tgi.artifacts[0]; - break; + case 209: + players = tgi.artifacts[0]; + break; - case 210: - players = tgi.army[0]; - break; + case 210: + players = tgi.army[0]; + break; - case 211: - /// TODO: not implemented in obtainPlayersStats - players = tgi.income[0]; - break; + case 211: + /// TODO: not implemented in obtainPlayersStats + players = tgi.income[0]; + break; + + case 212: + /// TODO: Check that ultimate artifact (grail) found + break; + } + rumorPlayer = RandomGeneratorUtil::nextItem(players, rand)->getNum(); - case 212: - /// TODO: Check that ultimate artifact (grail) found break; } - rumorPlayer = RandomGeneratorUtil::nextItem(players, rand)->getNum(); + case RumorState::RUMOR_MAP: + rumorId = rand.nextInt(gs->map->rumors.size() - 1); - break; - } - case RumorState::RUMOR_MAP: - rumorId = rand.nextInt(gs->map->rumors.size() - 1); + break; - break; + case RumorState::RUMOR_RAND: + do + { + rumorId = rand.nextInt(VLC->generaltexth->tavernRumors.size() - 1); + } + while(!VLC->generaltexth->tavernRumors[rumorId].length()); - case RumorState::RUMOR_RAND: - do - { - rumorId = rand.nextInt(VLC->generaltexth->tavernRumors.size() - 1); + break; } - while(!VLC->generaltexth->tavernRumors[rumorId].length()); - - break; } - - if(vstd::contains(rumor.last, rumor.type)) - { - rumor.last.erase(rumor.type); - } - rumor.last[rumor.type] = std::make_pair(rumorId, rumorPlayer); + while(!rumor.update(rumorId, rumorPlayer)); } bool CGameState::isVisible(int3 pos, PlayerColor player) @@ -2868,6 +2866,25 @@ std::string PlayerState::nodeName() const return "Player " + (color.getNum() < VLC->generaltexth->capColors.size() ? VLC->generaltexth->capColors[color.getNum()] : boost::lexical_cast(color)); } + +bool RumorState::update(int id, int player) +{ + if(vstd::contains(last, type)) + { + if(last[type].first != id) + { + last[type].first = id; + last[type].second = player; + } + else + return false; + } + else + last[type] = std::make_pair(id, player); + + return true; +} + InfoAboutArmy::InfoAboutArmy(): owner(PlayerColor::NEUTRAL) {} diff --git a/lib/CGameState.h b/lib/CGameState.h index 61ad827da..90e8c9962 100644 --- a/lib/CGameState.h +++ b/lib/CGameState.h @@ -219,6 +219,7 @@ struct DLL_LINKAGE RumorState std::map> last; RumorState(){type = RUMOR_NONE; last = {};}; + bool update(int id, int player); template void serialize(Handler &h, const int version) { @@ -371,7 +372,7 @@ public: { h & rumor; } - else + else if(!h.saving) rumor = RumorState(); BONUS_TREE_DESERIALIZATION_FIX From c5485ceede72fb527162f101f28845ad958c717c Mon Sep 17 00:00:00 2001 From: ArseniyShestakov Date: Tue, 1 Dec 2015 02:23:43 +0300 Subject: [PATCH 138/168] Rumors: update changelog --- ChangeLog | 1 + 1 file changed, 1 insertion(+) diff --git a/ChangeLog b/ChangeLog index 698c2b1e2..8308ec4e3 100644 --- a/ChangeLog +++ b/ChangeLog @@ -7,6 +7,7 @@ GENERAL: * New artifacts supported - Angel Wings - Boots of Levitation +* Implemented rumors in tavern window ADVETURE AI: * Fixed AI trying to go through underground rock From ffcc5ccdd27570a37b00c5d0a1dff27bac9358b7 Mon Sep 17 00:00:00 2001 From: ArseniyShestakov Date: Tue, 1 Dec 2015 04:57:52 +0300 Subject: [PATCH 139/168] Rumors: implement support for grail terrain rumor --- lib/CGameInfoCallback.cpp | 11 ++++++++--- lib/CGameState.cpp | 27 ++++++++++++++------------- lib/CGameState.h | 2 +- 3 files changed, 23 insertions(+), 17 deletions(-) diff --git a/lib/CGameInfoCallback.cpp b/lib/CGameInfoCallback.cpp index 1a968ab81..d51e9ac65 100644 --- a/lib/CGameInfoCallback.cpp +++ b/lib/CGameInfoCallback.cpp @@ -567,7 +567,7 @@ EPlayerStatus::EStatus CGameInfoCallback::getPlayerStatus(PlayerColor player, bo std::string CGameInfoCallback::getTavernRumor(const CGObjectInstance * townOrTavern) const { - std::string text = ""; + std::string text = "", extraText = ""; if(gs->rumor.type == RumorState::RUMOR_NONE) // (version < 755 backward compatability return text; @@ -575,9 +575,14 @@ std::string CGameInfoCallback::getTavernRumor(const CGObjectInstance * townOrTav switch(gs->rumor.type) { case RumorState::RUMOR_STATS: - text = boost::str(boost::format(VLC->generaltexth->allTexts[rumor.first]) % VLC->generaltexth->capColors[rumor.second]); - break; + if(rumor.first == 212) + extraText = VLC->generaltexth->arraytxt[158 + rumor.second]; + else + extraText = VLC->generaltexth->capColors[rumor.second]; + text = boost::str(boost::format(VLC->generaltexth->allTexts[rumor.first]) % extraText); + + break; case RumorState::RUMOR_MAP: text = gs->map->rumors[rumor.first].text; break; diff --git a/lib/CGameState.cpp b/lib/CGameState.cpp index 97e57bafa..e48133a3a 100644 --- a/lib/CGameState.cpp +++ b/lib/CGameState.cpp @@ -2136,10 +2136,9 @@ int3 CGameState::guardingCreaturePosition (int3 pos) const void CGameState::updateRumor() { static std::vector rumorTypes = {RumorState::RUMOR_MAP, RumorState::RUMOR_STATS, RumorState::RUMOR_RAND, RumorState::RUMOR_RAND}; - static std::vector statsRumorTypes = {208, 209, 210};// 211, 212}; + static std::vector statsRumorTypes = {208, 209, 210, 212};// 211}; - int rumorId = -1; - int rumorPlayer = PlayerColor::CANNOT_DETERMINE.getNum(); + int rumorId = -1, rumorExtra = -1; auto & rand = gs->getRandomGenerator(); rumor.type = *RandomGeneratorUtil::nextItem(rumorTypes, rand); if(!gs->map->rumors.size() && rumor.type == RumorState::RUMOR_MAP) @@ -2153,8 +2152,14 @@ void CGameState::updateRumor() { SThievesGuildInfo tgi; gs->obtainPlayersStats(tgi, 20); - std::vector players = {}; rumorId = *RandomGeneratorUtil::nextItem(statsRumorTypes, rand); + if(rumorId == 212) + { + rumorExtra = getTile(map->grailPos)->terType; + break; + } + + std::vector players = {}; switch(rumorId) { case 208: @@ -2173,12 +2178,8 @@ void CGameState::updateRumor() /// TODO: not implemented in obtainPlayersStats players = tgi.income[0]; break; - - case 212: - /// TODO: Check that ultimate artifact (grail) found - break; } - rumorPlayer = RandomGeneratorUtil::nextItem(players, rand)->getNum(); + rumorExtra = RandomGeneratorUtil::nextItem(players, rand)->getNum(); break; } @@ -2197,7 +2198,7 @@ void CGameState::updateRumor() break; } } - while(!rumor.update(rumorId, rumorPlayer)); + while(!rumor.update(rumorId, rumorExtra)); } bool CGameState::isVisible(int3 pos, PlayerColor player) @@ -2867,20 +2868,20 @@ std::string PlayerState::nodeName() const } -bool RumorState::update(int id, int player) +bool RumorState::update(int id, int extra) { if(vstd::contains(last, type)) { if(last[type].first != id) { last[type].first = id; - last[type].second = player; + last[type].second = extra; } else return false; } else - last[type] = std::make_pair(id, player); + last[type] = std::make_pair(id, extra); return true; } diff --git a/lib/CGameState.h b/lib/CGameState.h index 90e8c9962..cab3f572b 100644 --- a/lib/CGameState.h +++ b/lib/CGameState.h @@ -219,7 +219,7 @@ struct DLL_LINKAGE RumorState std::map> last; RumorState(){type = RUMOR_NONE; last = {};}; - bool update(int id, int player); + bool update(int id, int extra); template void serialize(Handler &h, const int version) { From a62ee65d72a655d18c7f2d07c2a472d4438a6db4 Mon Sep 17 00:00:00 2001 From: ArseniyShestakov Date: Tue, 1 Dec 2015 06:28:08 +0300 Subject: [PATCH 140/168] Rumors: implement income checking via statsHLP::getIncome This also fix income ranking in Thieves' Guild --- lib/CGameState.cpp | 57 +++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 54 insertions(+), 3 deletions(-) diff --git a/lib/CGameState.cpp b/lib/CGameState.cpp index e48133a3a..846ee32c7 100644 --- a/lib/CGameState.cpp +++ b/lib/CGameState.cpp @@ -2136,7 +2136,7 @@ int3 CGameState::guardingCreaturePosition (int3 pos) const void CGameState::updateRumor() { static std::vector rumorTypes = {RumorState::RUMOR_MAP, RumorState::RUMOR_STATS, RumorState::RUMOR_RAND, RumorState::RUMOR_RAND}; - static std::vector statsRumorTypes = {208, 209, 210, 212};// 211}; + static std::vector statsRumorTypes = {208, 209, 210, 211, 212}; int rumorId = -1, rumorExtra = -1; auto & rand = gs->getRandomGenerator(); @@ -2175,7 +2175,6 @@ void CGameState::updateRumor() break; case 211: - /// TODO: not implemented in obtainPlayersStats players = tgi.income[0]; break; } @@ -2522,6 +2521,58 @@ struct statsHLP } return str; } + + // get total gold income + static int getIncome(const PlayerState * ps) + { + int totalIncome = 0; + const CGObjectInstance * heroOrTown = nullptr; + + //Heroes can produce gold as well - skill, specialty or arts + for(auto & h : ps->heroes) + { + totalIncome += h->valOfBonuses(Selector::typeSubtype(Bonus::SECONDARY_SKILL_PREMY, SecondarySkill::ESTATES)); + totalIncome += h->valOfBonuses(Selector::typeSubtype(Bonus::GENERATE_RESOURCE, Res::GOLD)); + + if(!heroOrTown) + heroOrTown = h; + } + + //Add town income of all towns + for(auto & t : ps->towns) + { + totalIncome += t->dailyIncome()[Res::GOLD]; + + if(!heroOrTown) + heroOrTown = t; + } + + /// FIXME: Dirty dirty hack + /// Stats helper need some access to gamestate. + std::vector ownedObjects; + for(const CGObjectInstance * obj : heroOrTown->cb->gameState()->map->objects) + { + if(obj && obj->tempOwner == ps->color) + ownedObjects.push_back(obj); + } + /// This is code from CPlayerSpecificInfoCallback::getMyObjects + /// I'm really need to find out about callback interface design... + + for(auto object : ownedObjects) + { + //Mines + if ( object->ID == Obj::MINE ) + { + const CGMine *mine = dynamic_cast(object); + assert(mine); + + if (mine->producedResource == Res::GOLD) + totalIncome += mine->producedQuantity; + } + } + + return totalIncome; + } }; void CGameState::obtainPlayersStats(SThievesGuildInfo & tgi, int level) @@ -2596,7 +2647,7 @@ void CGameState::obtainPlayersStats(SThievesGuildInfo & tgi, int level) } if(level >= 7) //income { - //TODO:obtainPlayersStats - income + FILL_FIELD(income, statsHLP::getIncome(&g->second)) } if(level >= 8) //best hero's stats { From b5100bee94e28d33dcf9d14835ad895b40f1d57b Mon Sep 17 00:00:00 2001 From: ArseniyShestakov Date: Wed, 2 Dec 2015 17:56:26 +0300 Subject: [PATCH 141/168] Teleport: rework code to support exit point selection for whirlpools --- AI/VCAI/VCAI.cpp | 20 +++++++----- AI/VCAI/VCAI.h | 9 ++++++ client/CPlayerInterface.cpp | 14 ++++++--- client/CPlayerInterface.h | 3 +- lib/Connection.h | 2 +- lib/mapObjects/MiscObjects.cpp | 57 +++++++++++++++++++++------------- lib/mapObjects/MiscObjects.h | 8 ++--- 7 files changed, 72 insertions(+), 41 deletions(-) diff --git a/AI/VCAI/VCAI.cpp b/AI/VCAI/VCAI.cpp index 18a1801ec..e84694703 100644 --- a/AI/VCAI/VCAI.cpp +++ b/AI/VCAI/VCAI.cpp @@ -96,6 +96,7 @@ VCAI::VCAI(void) LOG_TRACE(logAi); makingTurn = nullptr; destinationTeleport = ObjectInstanceID(); + destinationTeleportPos = int3(); } VCAI::~VCAI(void) @@ -616,31 +617,32 @@ void VCAI::showBlockingDialog(const std::string &text, const std::vectorpassability = TeleportChannel::IMPASSABLE; else { - if(destinationTeleport != ObjectInstanceID() && vstd::contains(exits, destinationTeleport)) - choosenExit = destinationTeleport; + auto neededExit = std::make_pair(destinationTeleport, destinationTeleportPos); + if(destinationTeleport != ObjectInstanceID() && vstd::contains(exits, neededExit)) + choosenExit = vstd::find_pos(exits, neededExit); - if(!status.channelProbing()) +/* if(!status.channelProbing()) { vstd::copy_if(exits, vstd::set_inserter(teleportChannelProbingList), [&](ObjectInstanceID id) -> bool { return !(vstd::contains(visitableObjs, cb->getObj(id)) || id == choosenExit); }); - } + }*/ } requestActionASAP([=]() { - answerQuery(askID, choosenExit.getNum()); + answerQuery(askID, choosenExit); }); } @@ -1863,8 +1865,10 @@ bool VCAI::moveHeroToTile(int3 dst, HeroPtr h) auto doTeleportMovement = [&](int3 dst, ObjectInstanceID exitId) { destinationTeleport = exitId; - cb->moveHero(*h, CGHeroInstance::convertPosition(dst, true)); + destinationTeleportPos = CGHeroInstance::convertPosition(dst, true); + cb->moveHero(*h, destinationTeleportPos); destinationTeleport = ObjectInstanceID(); + destinationTeleportPos = int3(); afterMovementCheck(); }; diff --git a/AI/VCAI/VCAI.h b/AI/VCAI/VCAI.h index 15fd7c7d4..aa5eb521b 100644 --- a/AI/VCAI/VCAI.h +++ b/AI/VCAI/VCAI.h @@ -148,6 +148,7 @@ public: std::map > knownTeleportChannels; std::map knownSubterraneanGates; ObjectInstanceID destinationTeleport; + int3 destinationTeleportPos; std::vector teleportChannelProbingList; //list of teleport channel exits that not visible and need to be (re-)explored //std::vector visitedThisWeek; //only OPWs std::map > townVisitsThisWeek; @@ -360,6 +361,14 @@ public: template void serializeInternal(Handler &h, const int version) { h & knownTeleportChannels & knownSubterraneanGates & destinationTeleport; + if(version >= 755) + { + h & destinationTeleportPos; + } + else if(!h.saving) + { + destinationTeleportPos = int3(); + } h & townVisitsThisWeek & lockedHeroes & reservedHeroesMap; //FIXME: cannot instantiate abstract class h & visitableObjs & alreadyVisited & reservedObjs; h & saving & status & battlename; diff --git a/client/CPlayerInterface.cpp b/client/CPlayerInterface.cpp index 3b5bdf7f2..6132d8035 100644 --- a/client/CPlayerInterface.cpp +++ b/client/CPlayerInterface.cpp @@ -97,6 +97,7 @@ CPlayerInterface::CPlayerInterface(PlayerColor Player) { logGlobal->traceStream() << "\tHuman player interface for player " << Player << " being constructed"; destinationTeleport = ObjectInstanceID(); + destinationTeleportPos = int3(); observerInDuelMode = false; howManyPeople++; GH.defActionsDef = 0; @@ -1147,14 +1148,15 @@ void CPlayerInterface::showBlockingDialog( const std::string &text, const std::v } -void CPlayerInterface::showTeleportDialog(TeleportChannelID channel, std::vector exits, bool impassable, QueryID askID) +void CPlayerInterface::showTeleportDialog(TeleportChannelID channel, TTeleportExitsList exits, bool impassable, QueryID askID) { EVENT_HANDLER_CALLED_BY_CLIENT; - ObjectInstanceID choosenExit; - if(destinationTeleport != ObjectInstanceID() && vstd::contains(exits, destinationTeleport)) - choosenExit = destinationTeleport; + int choosenExit = -1; + auto neededExit = std::make_pair(destinationTeleport, destinationTeleportPos); + if(destinationTeleport != ObjectInstanceID() && vstd::contains(exits, neededExit)) + choosenExit = vstd::find_pos(exits, neededExit); - cb->selectionMade(choosenExit.getNum(), askID); + cb->selectionMade(choosenExit, askID); } void CPlayerInterface::tileRevealed(const std::unordered_set &pos) @@ -1414,6 +1416,7 @@ void CPlayerInterface::requestRealized( PackageApplied *pa ) && stillMoveHero.get() == DURING_MOVE) { // After teleportation via CGTeleport object is finished destinationTeleport = ObjectInstanceID(); + destinationTeleportPos = int3(); stillMoveHero.setn(CONTINUE_MOVE); } } @@ -2663,6 +2666,7 @@ void CPlayerInterface::doMoveHero(const CGHeroInstance * h, CGPath path) { CCS->soundh->stopSound(sh); destinationTeleport = nextObject->id; + destinationTeleportPos = nextCoord; doMovement(h->pos, false); sh = CCS->soundh->playSound(CCS->soundh->horseSounds[currentTerrain], -1); continue; diff --git a/client/CPlayerInterface.h b/client/CPlayerInterface.h index 2e19aae30..ad9e43e85 100644 --- a/client/CPlayerInterface.h +++ b/client/CPlayerInterface.h @@ -90,6 +90,7 @@ class CPlayerInterface : public CGameInterface, public IUpdateable public: bool observerInDuelMode; ObjectInstanceID destinationTeleport; //contain -1 or object id if teleportation + int3 destinationTeleportPos; //minor interfaces CondSh *showingDialog; //indicates if dialog box is displayed @@ -168,7 +169,7 @@ public: void showRecruitmentDialog(const CGDwelling *dwelling, const CArmedInstance *dst, int level) override; void showShipyardDialog(const IShipyard *obj) override; //obj may be town or shipyard; void showBlockingDialog(const std::string &text, const std::vector &components, QueryID askID, int soundID, bool selection, bool cancel) override; //Show a dialog, player must take decision. If selection then he has to choose between one of given components, if cancel he is allowed to not choose. After making choice, CCallback::selectionMade should be called with number of selected component (1 - n) or 0 for cancel (if allowed) and askID. - void showTeleportDialog(TeleportChannelID channel, std::vector exits, bool impassable, QueryID askID) override; + void showTeleportDialog(TeleportChannelID channel, TTeleportExitsList exits, bool impassable, QueryID askID) override; void showGarrisonDialog(const CArmedInstance *up, const CGHeroInstance *down, bool removableUnits, QueryID queryID) override; void showPuzzleMap() override; void viewWorldMap() override; diff --git a/lib/Connection.h b/lib/Connection.h index ee07bf9aa..e56d71640 100644 --- a/lib/Connection.h +++ b/lib/Connection.h @@ -27,7 +27,7 @@ #include "mapping/CCampaignHandler.h" //for CCampaignState #include "rmg/CMapGenerator.h" // for CMapGenOptions -const ui32 version = 754; +const ui32 version = 755; const ui32 minSupportedVersion = 753; class CISer; diff --git a/lib/mapObjects/MiscObjects.cpp b/lib/mapObjects/MiscObjects.cpp index 4f2566960..8c0db729d 100644 --- a/lib/mapObjects/MiscObjects.cpp +++ b/lib/mapObjects/MiscObjects.cpp @@ -834,7 +834,7 @@ bool CGTeleport::isTeleport(const CGObjectInstance * obj) bool CGTeleport::isConnected(const CGTeleport * src, const CGTeleport * dst) { - return src && dst && src != dst && src->isChannelExit(dst->id); + return src && dst && src->isChannelExit(dst->id); } bool CGTeleport::isConnected(const CGObjectInstance * src, const CGObjectInstance * dst) @@ -913,7 +913,13 @@ void CGMonolith::onHeroVisit( const CGHeroInstance * h ) const if(isEntrance()) { if(cb->isTeleportChannelBidirectional(channel) && 1 < cb->getTeleportChannelExits(channel).size()) - td.exits = cb->getTeleportChannelExits(channel); + { + auto exits = cb->getTeleportChannelExits(channel); + for(auto exit : exits) + { + td.exits.push_back(std::make_pair(exit, CGHeroInstance::convertPosition(cb->getObj(exit)->visitablePos(), true))); + } + } if(cb->isTeleportChannelImpassable(channel)) { @@ -929,9 +935,9 @@ void CGMonolith::onHeroVisit( const CGHeroInstance * h ) const cb->showTeleportDialog(&td); } -void CGMonolith::teleportDialogAnswered(const CGHeroInstance *hero, ui32 answer, std::vector exits) const +void CGMonolith::teleportDialogAnswered(const CGHeroInstance *hero, ui32 answer, TTeleportExitsList exits) const { - ObjectInstanceID objId(answer); + int3 dPos; auto realExits = getAllExits(true); if(!isEntrance() // Do nothing if hero visited exit only object || (!exits.size() && !realExits.size()) // Do nothing if there no exits on this channel @@ -939,14 +945,12 @@ void CGMonolith::teleportDialogAnswered(const CGHeroInstance *hero, ui32 answer, { return; } - else if(objId == ObjectInstanceID()) - objId = getRandomExit(hero); + else if(vstd::isValidIndex(exits, answer)) + dPos = exits[answer].second; else - assert(vstd::contains(exits, objId)); // Likely cheating attempt: not random teleporter choosen, but it's not from provided list + dPos = CGHeroInstance::convertPosition(cb->getObj(getRandomExit(hero))->visitablePos(), true); - auto obj = cb->getObj(objId); - if(obj) - cb->moveHero(hero->id,CGHeroInstance::convertPosition(obj->pos,true) - getVisitableOffset(), true); + cb->moveHero(hero->id, dPos, true); } void CGMonolith::initObj() @@ -986,7 +990,10 @@ void CGSubterraneanGate::onHeroVisit( const CGHeroInstance * h ) const td.impassable = true; } else - td.exits.push_back(getRandomExit(h)); + { + auto exit = getRandomExit(h); + td.exits.push_back(std::make_pair(exit, CGHeroInstance::convertPosition(cb->getObj(exit)->visitablePos(), true))); + } cb->showTeleportDialog(&td); } @@ -1085,29 +1092,35 @@ void CGWhirlpool::onHeroVisit( const CGHeroInstance * h ) const cb->changeStackCount(StackLocation(h, targetstack), -countToTake); } else - td.exits = getAllExits(true); + { + auto exits = getAllExits(); + for(auto exit : exits) + { + auto blockedPosList = cb->getObj(exit)->getBlockedPos(); + for(auto bPos : blockedPosList) + td.exits.push_back(std::make_pair(exit, CGHeroInstance::convertPosition(bPos, true))); + } + } cb->showTeleportDialog(&td); } -void CGWhirlpool::teleportDialogAnswered(const CGHeroInstance *hero, ui32 answer, std::vector exits) const +void CGWhirlpool::teleportDialogAnswered(const CGHeroInstance *hero, ui32 answer, TTeleportExitsList exits) const { - ObjectInstanceID objId(answer); + int3 dPos; auto realExits = getAllExits(); if(!exits.size() && !realExits.size()) return; - else if(objId == ObjectInstanceID()) - objId = getRandomExit(hero); + else if(vstd::isValidIndex(exits, answer)) + dPos = exits[answer].second; else - assert(vstd::contains(exits, objId)); // Likely cheating attempt: not random teleporter choosen, but it's not from provided list - - auto obj = cb->getObj(objId); - if(obj) { + auto obj = cb->getObj(getRandomExit(hero)); std::set tiles = obj->getBlockedPos(); - auto & tile = *RandomGeneratorUtil::nextItem(tiles, cb->gameState()->getRandomGenerator()); - cb->moveHero(hero->id, CGHeroInstance::convertPosition(tile, true), true); + dPos = CGHeroInstance::convertPosition(*RandomGeneratorUtil::nextItem(tiles, cb->gameState()->getRandomGenerator()), true); } + + cb->moveHero(hero->id, dPos, true); } bool CGWhirlpool::isProtected( const CGHeroInstance * h ) diff --git a/lib/mapObjects/MiscObjects.h b/lib/mapObjects/MiscObjects.h index 402deb1c4..161fe38e7 100644 --- a/lib/mapObjects/MiscObjects.h +++ b/lib/mapObjects/MiscObjects.h @@ -247,7 +247,7 @@ public: ui32 defaultResProduction(); }; -typedef std::vector TTeleportExitsList; +typedef std::vector> TTeleportExitsList; struct DLL_LINKAGE TeleportChannel { @@ -282,7 +282,7 @@ public: std::vector getAllExits(bool excludeCurrent = false) const; ObjectInstanceID getRandomExit(const CGHeroInstance * h) const; - virtual void teleportDialogAnswered(const CGHeroInstance *hero, ui32 answer, std::vector exits) const = 0; + virtual void teleportDialogAnswered(const CGHeroInstance *hero, ui32 answer, TTeleportExitsList exits) const = 0; static bool isTeleport(const CGObjectInstance * dst); static bool isConnected(const CGTeleport * src, const CGTeleport * dst); @@ -303,7 +303,7 @@ class DLL_LINKAGE CGMonolith : public CGTeleport public: void onHeroVisit(const CGHeroInstance * h) const override; - void teleportDialogAnswered(const CGHeroInstance *hero, ui32 answer, std::vector exits) const override; + void teleportDialogAnswered(const CGHeroInstance *hero, ui32 answer, TTeleportExitsList exits) const override; void initObj() override; template void serialize(Handler &h, const int version) @@ -329,7 +329,7 @@ class DLL_LINKAGE CGWhirlpool : public CGMonolith { public: void onHeroVisit(const CGHeroInstance * h) const override; - void teleportDialogAnswered(const CGHeroInstance *hero, ui32 answer, std::vector exits) const override; + void teleportDialogAnswered(const CGHeroInstance *hero, ui32 answer, TTeleportExitsList exits) const override; static bool isProtected( const CGHeroInstance * h ); template void serialize(Handler &h, const int version) From eb9f29e3684bc311e11ae49f6b5d8d607d50b609 Mon Sep 17 00:00:00 2001 From: ArseniyShestakov Date: Wed, 2 Dec 2015 19:26:24 +0300 Subject: [PATCH 142/168] VCAI: restoring teleport probing feature for updated mechanics --- AI/VCAI/VCAI.cpp | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/AI/VCAI/VCAI.cpp b/AI/VCAI/VCAI.cpp index e84694703..80c2f7f10 100644 --- a/AI/VCAI/VCAI.cpp +++ b/AI/VCAI/VCAI.cpp @@ -631,13 +631,18 @@ void VCAI::showTeleportDialog(TeleportChannelID channel, TTeleportExitsList exit if(destinationTeleport != ObjectInstanceID() && vstd::contains(exits, neededExit)) choosenExit = vstd::find_pos(exits, neededExit); -/* if(!status.channelProbing()) + if(!status.channelProbing()) { - vstd::copy_if(exits, vstd::set_inserter(teleportChannelProbingList), [&](ObjectInstanceID id) -> bool + for(auto exit : exits) { - return !(vstd::contains(visitableObjs, cb->getObj(id)) || id == choosenExit); - }); - }*/ + if(!vstd::contains(visitableObjs, cb->getObj(exit.first)) && + !vstd::contains(teleportChannelProbingList, exit.first) && + exit.first != destinationTeleport) + { + teleportChannelProbingList.push_back(exit.first); + } + } + } } requestActionASAP([=]() From f9d9a38e53dabbb716b38cdfad5ea8f3f8e0818e Mon Sep 17 00:00:00 2001 From: Ivan Savenko Date: Wed, 2 Dec 2015 20:59:38 +0200 Subject: [PATCH 143/168] Fixed gcc warnings --- lib/CTownHandler.cpp | 8 ++------ lib/rmg/CRmgTemplateZone.cpp | 10 +++++----- 2 files changed, 7 insertions(+), 11 deletions(-) diff --git a/lib/CTownHandler.cpp b/lib/CTownHandler.cpp index fb76a2523..340deab4c 100644 --- a/lib/CTownHandler.cpp +++ b/lib/CTownHandler.cpp @@ -774,12 +774,8 @@ std::set CTownHandler::getAllowedFactions(bool withTown /*=true*/) con if (withTown) allowed = getDefaultAllowed(); else - { - for (auto town : factions) - { - allowed.push_back (true); - } - } + allowed.resize( factions.size(), true); + for (size_t i=0; i cameFrom; // The map of navigated nodes. std::map distances; - int3 currentNode = src; + //int3 currentNode = src; gen->setRoad (src, ERoadType::NO_ROAD); //just in case zone guard already has road under it. Road under nodes will be added at very end cameFrom[src] = int3(-1, -1, -1); //first node points to finish condition @@ -824,7 +824,7 @@ bool CRmgTemplateZone::connectPath(CMapGenerator* gen, const int3& src, bool onl std::map cameFrom; // The map of navigated nodes. std::map distances; - int3 currentNode = src; + //int3 currentNode = src; cameFrom[src] = int3(-1, -1, -1); //first node points to finish condition distances[src] = 0; @@ -866,7 +866,7 @@ bool CRmgTemplateZone::connectPath(CMapGenerator* gen, const int3& src, bool onl return; if (distance < bestDistanceSoFar || !vstd::contains(closed, pos)) { - auto obj = gen->map->getTile(pos).topVisitableObj(); + //auto obj = gen->map->getTile(pos).topVisitableObj(); if (vstd::contains(this->tileinfo, pos)) { cameFrom[pos] = currentNode; @@ -902,7 +902,7 @@ bool CRmgTemplateZone::connectWithCenter(CMapGenerator* gen, const int3& src, bo std::map cameFrom; // The map of navigated nodes. std::map distances; - int3 currentNode = src; + //int3 currentNode = src; cameFrom[src] = int3(-1, -1, -1); //first node points to finish condition distances[src] = 0; @@ -950,7 +950,7 @@ bool CRmgTemplateZone::connectWithCenter(CMapGenerator* gen, const int3& src, bo if (distance < bestDistanceSoFar || !vstd::contains(closed, pos)) { - auto obj = gen->map->getTile(pos).topVisitableObj(); + //auto obj = gen->map->getTile(pos).topVisitableObj(); if (vstd::contains(this->tileinfo, pos)) { cameFrom[pos] = currentNode; From bef70484600fa54402aebb14faa08acb8aa6f111 Mon Sep 17 00:00:00 2001 From: Ivan Savenko Date: Wed, 2 Dec 2015 21:01:43 +0200 Subject: [PATCH 144/168] minimized visibility of members of teleportation-related classes --- lib/mapObjects/MiscObjects.h | 36 +++++++++++++++++++++--------------- 1 file changed, 21 insertions(+), 15 deletions(-) diff --git a/lib/mapObjects/MiscObjects.h b/lib/mapObjects/MiscObjects.h index 21990b688..77a36e04d 100644 --- a/lib/mapObjects/MiscObjects.h +++ b/lib/mapObjects/MiscObjects.h @@ -265,29 +265,33 @@ struct DLL_LINKAGE TeleportChannel class DLL_LINKAGE CGTeleport : public CGObjectInstance { -public: - enum EType {UNKNOWN, ENTRANCE, EXIT, BOTH}; - - EType type; - TeleportChannelID channel; - - CGTeleport(); - bool isEntrance() const; - bool isExit() const; bool isChannelEntrance(ObjectInstanceID id) const; bool isChannelExit(ObjectInstanceID id) const; + std::vector getAllEntrances(bool excludeCurrent = false) const; - std::vector getAllExits(bool excludeCurrent = false) const; + +protected: + enum EType {UNKNOWN, ENTRANCE, EXIT, BOTH}; + EType type; + + CGTeleport(); ObjectInstanceID getRandomExit(const CGHeroInstance * h) const; + std::vector getAllExits(bool excludeCurrent = false) const; + +public: + TeleportChannelID channel; + + bool isEntrance() const; + bool isExit() const; virtual void teleportDialogAnswered(const CGHeroInstance *hero, ui32 answer, std::vector exits) const = 0; static bool isTeleport(const CGObjectInstance * dst); static bool isConnected(const CGTeleport * src, const CGTeleport * dst); static bool isConnected(const CGObjectInstance * src, const CGObjectInstance * dst); - static bool isExitPassable(CGameState * gs, const CGHeroInstance * h, const CGObjectInstance * obj); - static std::vector getPassableExits(CGameState * gs, const CGHeroInstance * h, std::vector exits); static void addToChannel(std::map > &channelsList, const CGTeleport * obj); + static std::vector getPassableExits(CGameState * gs, const CGHeroInstance * h, std::vector exits); + static bool isExitPassable(CGameState * gs, const CGHeroInstance * h, const CGObjectInstance * obj); template void serialize(Handler &h, const int version) { @@ -299,11 +303,12 @@ class DLL_LINKAGE CGMonolith : public CGTeleport { TeleportChannelID findMeChannel(std::vector IDs, int SubID) const; -public: +protected: void onHeroVisit(const CGHeroInstance * h) const override; void teleportDialogAnswered(const CGHeroInstance *hero, ui32 answer, std::vector exits) const override; void initObj() override; +public: template void serialize(Handler &h, const int version) { h & static_cast(*this); @@ -312,9 +317,10 @@ public: class DLL_LINKAGE CGSubterraneanGate : public CGMonolith { -public: void onHeroVisit(const CGHeroInstance * h) const override; void initObj() override; + +public: static void postInit(); template void serialize(Handler &h, const int version) @@ -325,11 +331,11 @@ public: class DLL_LINKAGE CGWhirlpool : public CGMonolith { -public: void onHeroVisit(const CGHeroInstance * h) const override; void teleportDialogAnswered(const CGHeroInstance *hero, ui32 answer, std::vector exits) const override; static bool isProtected( const CGHeroInstance * h ); +public: template void serialize(Handler &h, const int version) { h & static_cast(*this); From c3ce4b25df13906d704f6f54749358b6651a1824 Mon Sep 17 00:00:00 2001 From: Ivan Savenko Date: Wed, 2 Dec 2015 21:05:10 +0200 Subject: [PATCH 145/168] Removed all #include's of CMap.h from headers. To all - please, avoid #include's in headers as much as possible This kills incremental build compile times --- AI/VCAI/AIUtility.cpp | 2 + AI/VCAI/AIUtility.h | 1 - AI/VCAI/VCAI.h | 2 +- client/Graphics.cpp | 1 + client/battle/CBattleInterfaceClasses.cpp | 1 + client/windows/CQuestLog.cpp | 2 +- lib/BattleState.cpp | 3 +- lib/CBattleCallback.cpp | 1 + lib/CGameInfoCallback.cpp | 1 + lib/CGameState.h | 5 -- lib/CMakeLists.txt | 1 + lib/CPathfinder.h | 4 +- lib/Connection.cpp | 1 + lib/GameConstants.h | 12 +++ lib/IGameCallback.cpp | 1 + lib/NetPacks.h | 2 +- lib/mapObjects/CGHeroInstance.cpp | 4 +- lib/mapObjects/CGMarket.cpp | 3 +- lib/mapObjects/CGTownInstance.cpp | 3 +- lib/mapObjects/CObjectHandler.cpp | 4 +- lib/mapObjects/CQuest.cpp | 1 + lib/mapObjects/MiscObjects.cpp | 1 + lib/mapping/CDrawRoadsOperation.cpp | 1 + lib/mapping/CDrawRoadsOperation.h | 2 +- lib/mapping/CMap.h | 101 +--------------------- lib/mapping/CMapDefines.h | 99 +++++++++++++++++++++ lib/mapping/CMapEditManager.cpp | 1 + lib/mapping/CMapEditManager.h | 4 +- lib/rmg/CZonePlacer.cpp | 1 + lib/rmg/CZonePlacer.h | 2 - lib/spells/AdventureSpellMechanics.cpp | 1 + lib/spells/BattleSpellMechanics.cpp | 1 + server/CQuery.cpp | 1 + 33 files changed, 151 insertions(+), 119 deletions(-) create mode 100644 lib/mapping/CMapDefines.h diff --git a/AI/VCAI/AIUtility.cpp b/AI/VCAI/AIUtility.cpp index fb2c35bcb..8de8aa9f5 100644 --- a/AI/VCAI/AIUtility.cpp +++ b/AI/VCAI/AIUtility.cpp @@ -7,6 +7,8 @@ #include "../../lib/CConfigHandler.h" #include "../../lib/CHeroHandler.h" #include "../../lib/mapObjects/CBank.h" +#include "../../lib/mapObjects/CGTownInstance.h" +#include "../../lib/mapObjects/CQuest.h" /* * AIUtility.cpp, part of VCMI engine diff --git a/AI/VCAI/AIUtility.h b/AI/VCAI/AIUtility.h index 34d299906..3ec1f8110 100644 --- a/AI/VCAI/AIUtility.h +++ b/AI/VCAI/AIUtility.h @@ -7,7 +7,6 @@ #include "../../lib/spells/CSpellHandler.h" #include "../../lib/Connection.h" #include "../../lib/CGameState.h" -#include "../../lib/mapping/CMap.h" #include "../../lib/NetPacks.h" #include "../../lib/CStopWatch.h" diff --git a/AI/VCAI/VCAI.h b/AI/VCAI/VCAI.h index 23967df64..0da2ee61c 100644 --- a/AI/VCAI/VCAI.h +++ b/AI/VCAI/VCAI.h @@ -12,10 +12,10 @@ #include "../../lib/CBuildingHandler.h" #include "../../lib/CCreatureHandler.h" #include "../../lib/CTownHandler.h" +#include "../../lib/mapObjects/MiscObjects.h" #include "../../lib/spells/CSpellHandler.h" #include "../../lib/Connection.h" #include "../../lib/CGameState.h" -#include "../../lib/mapping/CMap.h" #include "../../lib/NetPacks.h" #include "../../lib/CondSh.h" diff --git a/client/Graphics.cpp b/client/Graphics.cpp index a6900056b..f71f238b5 100644 --- a/client/Graphics.cpp +++ b/client/Graphics.cpp @@ -22,6 +22,7 @@ #include "../lib/GameConstants.h" #include "../lib/CStopWatch.h" #include "../lib/mapObjects/CObjectClassesHandler.h" +#include "../lib/mapObjects/CObjectHandler.h" using namespace CSDL_Ext; #ifdef min diff --git a/client/battle/CBattleInterfaceClasses.cpp b/client/battle/CBattleInterfaceClasses.cpp index 4f2e3cdbd..f14f0a8c6 100644 --- a/client/battle/CBattleInterfaceClasses.cpp +++ b/client/battle/CBattleInterfaceClasses.cpp @@ -29,6 +29,7 @@ #include "../../lib/NetPacks.h" #include "../../lib/StartInfo.h" #include "../../lib/CondSh.h" +#include "../../lib/mapObjects/CGTownInstance.h" /* * CBattleInterfaceClasses.cpp, part of VCMI engine diff --git a/client/windows/CQuestLog.cpp b/client/windows/CQuestLog.cpp index e9d5783f6..43f547386 100644 --- a/client/windows/CQuestLog.cpp +++ b/client/windows/CQuestLog.cpp @@ -19,7 +19,7 @@ #include "../../lib/CGameState.h" #include "../../lib/CGeneralTextHandler.h" #include "../../lib/NetPacksBase.h" - +#include "../../lib/mapObjects/CQuest.h" /* * CQuestLog.cpp, part of VCMI engine * diff --git a/lib/BattleState.cpp b/lib/BattleState.cpp index cd6d2b0b5..e0cf02c99 100644 --- a/lib/BattleState.cpp +++ b/lib/BattleState.cpp @@ -1,4 +1,4 @@ -/* +/* * BattleState.cpp, part of VCMI engine * * Authors: listed in file AUTHORS in main folder @@ -22,6 +22,7 @@ #include "JsonNode.h" #include "filesystem/Filesystem.h" #include "CRandomGenerator.h" +#include "mapObjects/CGTownInstance.h" const CStack * BattleInfo::getNextStack() const { diff --git a/lib/CBattleCallback.cpp b/lib/CBattleCallback.cpp index 4a5c3193a..cb522f0e6 100644 --- a/lib/CBattleCallback.cpp +++ b/lib/CBattleCallback.cpp @@ -6,6 +6,7 @@ #include "spells/CSpellHandler.h" #include "VCMI_Lib.h" #include "CTownHandler.h" +#include "mapObjects/CGTownInstance.h" /* * CBattleCallback.cpp, part of VCMI engine diff --git a/lib/CGameInfoCallback.cpp b/lib/CGameInfoCallback.cpp index 56401514b..9b030df9e 100644 --- a/lib/CGameInfoCallback.cpp +++ b/lib/CGameInfoCallback.cpp @@ -18,6 +18,7 @@ #include "NetPacks.h" // for InfoWindow #include "CModHandler.h" #include "spells/CSpellHandler.h" +#include "mapping/CMap.h" //TODO make clean #define ERROR_VERBOSE_OR_NOT_RET_VAL_IF(cond, verbose, txt, retVal) do {if(cond){if(verbose)logGlobal->errorStream() << BOOST_CURRENT_FUNCTION << ": " << txt; return retVal;}} while(0) diff --git a/lib/CGameState.h b/lib/CGameState.h index c60a9aa34..458a4b714 100644 --- a/lib/CGameState.h +++ b/lib/CGameState.h @@ -1,12 +1,7 @@ #pragma once - - -//#ifndef _MSC_VER #include "CCreatureHandler.h" #include "VCMI_Lib.h" -#include "mapping/CMap.h" -//#endif #include "HeroBonus.h" #include "CCreatureSet.h" diff --git a/lib/CMakeLists.txt b/lib/CMakeLists.txt index 521a24668..0faf961e7 100644 --- a/lib/CMakeLists.txt +++ b/lib/CMakeLists.txt @@ -117,6 +117,7 @@ set(lib_HEADERS filesystem/ISimpleResourceLoader.h mapObjects/MapObjects.h + mapping/CMapDefines.h CSoundBase.h AI_Base.h diff --git a/lib/CPathfinder.h b/lib/CPathfinder.h index 29c5bd35e..08f92971f 100644 --- a/lib/CPathfinder.h +++ b/lib/CPathfinder.h @@ -1,8 +1,8 @@ #pragma once #include "VCMI_Lib.h" -#include "mapping/CMap.h" #include "IGameCallback.h" +#include "HeroBonus.h" #include "int3.h" #include @@ -21,6 +21,8 @@ class CGHeroInstance; class CGObjectInstance; struct TerrainTile; class CPathfinderHelper; +class CMap; +class CGWhirlpool; struct DLL_LINKAGE CGPathNode { diff --git a/lib/Connection.cpp b/lib/Connection.cpp index 0f6ca2ee2..24eeb6642 100644 --- a/lib/Connection.cpp +++ b/lib/Connection.cpp @@ -2,6 +2,7 @@ #include "Connection.h" #include "registerTypes/RegisterTypes.h" +#include "mapping/CMap.h" #include diff --git a/lib/GameConstants.h b/lib/GameConstants.h index 8224f7ca5..ad13b9a7c 100644 --- a/lib/GameConstants.h +++ b/lib/GameConstants.h @@ -405,6 +405,18 @@ public: ID_LIKE_OPERATORS(BuildingID, BuildingID::EBuildingID) +namespace EAiTactic +{ +enum EAiTactic +{ + NONE = -1, + RANDOM, + WARRIOR, + BUILDER, + EXPLORER +}; +} + namespace EBuildingState { enum EBuildingState diff --git a/lib/IGameCallback.cpp b/lib/IGameCallback.cpp index 8f8f3b30c..d562c7410 100644 --- a/lib/IGameCallback.cpp +++ b/lib/IGameCallback.cpp @@ -21,6 +21,7 @@ #include "mapObjects/CObjectClassesHandler.h" #include "StartInfo.h" #include "CGameState.h" +#include "mapping/CMap.h" void CPrivilagedInfoCallback::getFreeTiles (std::vector &tiles) const { diff --git a/lib/NetPacks.h b/lib/NetPacks.h index cdbe3ebe9..7e54d1e2f 100644 --- a/lib/NetPacks.h +++ b/lib/NetPacks.h @@ -13,7 +13,7 @@ #include "ResourceSet.h" //#include "CObstacleInstance.h" #include "CGameStateFwd.h" -#include "mapping/CMap.h" +#include "mapping/CMapDefines.h" #include "CObstacleInstance.h" #include "spells/ViewSpellInt.h" diff --git a/lib/mapObjects/CGHeroInstance.cpp b/lib/mapObjects/CGHeroInstance.cpp index 5b4ef4822..cf29848ae 100644 --- a/lib/mapObjects/CGHeroInstance.cpp +++ b/lib/mapObjects/CGHeroInstance.cpp @@ -1,4 +1,4 @@ -/* +/* * CGHeroInstance.cpp, part of VCMI engine * * Authors: listed in file AUTHORS in main folder @@ -22,6 +22,8 @@ #include "../CGameState.h" #include "../CCreatureHandler.h" #include "../BattleState.h" +#include "../CTownHandler.h" +#include "CGTownInstance.h" ///helpers static void showInfoDialog(const PlayerColor playerID, const ui32 txtID, const ui16 soundID) diff --git a/lib/mapObjects/CGMarket.cpp b/lib/mapObjects/CGMarket.cpp index 4add987fd..addd493d1 100644 --- a/lib/mapObjects/CGMarket.cpp +++ b/lib/mapObjects/CGMarket.cpp @@ -1,4 +1,4 @@ -/* +/* * * CGMarket.cpp, part of VCMI engine * @@ -17,6 +17,7 @@ #include "../IGameCallback.h" #include "../CCreatureHandler.h" #include "../CGameState.h" +#include "CGTownInstance.h" ///helpers static void openWindow(const OpenWindow::EWindow type, const int id1, const int id2 = -1) diff --git a/lib/mapObjects/CGTownInstance.cpp b/lib/mapObjects/CGTownInstance.cpp index 5dead32a0..e640eaa0b 100644 --- a/lib/mapObjects/CGTownInstance.cpp +++ b/lib/mapObjects/CGTownInstance.cpp @@ -1,4 +1,4 @@ -/* +/* * CGTownInstance.cpp, part of VCMI engine * * Authors: listed in file AUTHORS in main folder @@ -17,6 +17,7 @@ #include "../CModHandler.h" #include "../IGameCallback.h" #include "../CGameState.h" +#include "../mapping/CMapDefines.h" std::vector CGTownInstance::merchantArtifacts; std::vector CGTownInstance::universitySkills; diff --git a/lib/mapObjects/CObjectHandler.cpp b/lib/mapObjects/CObjectHandler.cpp index 9b491d3b1..88c7e6bd0 100644 --- a/lib/mapObjects/CObjectHandler.cpp +++ b/lib/mapObjects/CObjectHandler.cpp @@ -1,4 +1,4 @@ -/* +/* * CObjectHandler.cpp, part of VCMI engine * * Authors: listed in file AUTHORS in main folder @@ -18,8 +18,10 @@ #include "../filesystem/ResourceID.h" #include "../IGameCallback.h" #include "../CGameState.h" +#include "../mapping/CMap.h" #include "CObjectClassesHandler.h" +#include "CGTownInstance.h" IGameCallback * IObjectInterface::cb = nullptr; diff --git a/lib/mapObjects/CQuest.cpp b/lib/mapObjects/CQuest.cpp index d610a588a..b8964a342 100644 --- a/lib/mapObjects/CQuest.cpp +++ b/lib/mapObjects/CQuest.cpp @@ -17,6 +17,7 @@ #include "../CGeneralTextHandler.h" #include "../CHeroHandler.h" #include "CObjectClassesHandler.h" +#include "MiscObjects.h" #include "../IGameCallback.h" #include "../CGameState.h" diff --git a/lib/mapObjects/MiscObjects.cpp b/lib/mapObjects/MiscObjects.cpp index 4f2566960..eb04fd0c1 100644 --- a/lib/mapObjects/MiscObjects.cpp +++ b/lib/mapObjects/MiscObjects.cpp @@ -20,6 +20,7 @@ #include "../spells/CSpellHandler.h" #include "../IGameCallback.h" #include "../CGameState.h" +#include "../mapping/CMap.h" std::map > CGMagi::eyelist; ui8 CGObelisk::obeliskCount; //how many obelisks are on map diff --git a/lib/mapping/CDrawRoadsOperation.cpp b/lib/mapping/CDrawRoadsOperation.cpp index ca1f4cd05..c8fc0cbe1 100644 --- a/lib/mapping/CDrawRoadsOperation.cpp +++ b/lib/mapping/CDrawRoadsOperation.cpp @@ -10,6 +10,7 @@ #include "StdInc.h" #include "CDrawRoadsOperation.h" +#include "CMap.h" const std::vector CDrawRoadsOperation::patterns = { diff --git a/lib/mapping/CDrawRoadsOperation.h b/lib/mapping/CDrawRoadsOperation.h index 8f5bede85..de74a0c23 100644 --- a/lib/mapping/CDrawRoadsOperation.h +++ b/lib/mapping/CDrawRoadsOperation.h @@ -11,9 +11,9 @@ #pragma once #include "../CRandomGenerator.h" -#include "CMap.h" #include "CMapEditManager.h" +struct TerrainTile; class CDrawRoadsOperation : public CMapOperation { diff --git a/lib/mapping/CMap.h b/lib/mapping/CMap.h index 438e997cd..8fea38cf3 100644 --- a/lib/mapping/CMap.h +++ b/lib/mapping/CMap.h @@ -19,6 +19,7 @@ #include "../int3.h" #include "../GameConstants.h" #include "../LogicalExpression.h" +#include "CMapDefines.h" class CArtifactInstance; class CGObjectInstance; @@ -47,18 +48,6 @@ struct DLL_LINKAGE SHeroName } }; -namespace EAiTactic -{ -enum EAiTactic -{ - NONE = -1, - RANDOM, - WARRIOR, - BUILDER, - EXPLORER -}; -} - /// The player info constains data about which factions are allowed, AI tactical settings, /// the main hero name, where to generate the hero, whether the faction should be selected randomly,... struct DLL_LINKAGE PlayerInfo @@ -216,94 +205,6 @@ struct DLL_LINKAGE DisposedHero } }; -/// The map event is an event which e.g. gives or takes resources of a specific -/// amount to/from players and can appear regularly or once a time. -class DLL_LINKAGE CMapEvent -{ -public: - CMapEvent(); - - bool earlierThan(const CMapEvent & other) const; - bool earlierThanOrEqual(const CMapEvent & other) const; - - std::string name; - std::string message; - TResources resources; - ui8 players; // affected players, bit field? - ui8 humanAffected; - ui8 computerAffected; - ui32 firstOccurence; - ui32 nextOccurence; /// specifies after how many days the event will occur the next time; 0 if event occurs only one time - - template - void serialize(Handler & h, const int version) - { - h & name & message & resources - & players & humanAffected & computerAffected & firstOccurence & nextOccurence; - } -}; - -/// The castle event builds/adds buildings/creatures for a specific town. -class DLL_LINKAGE CCastleEvent: public CMapEvent -{ -public: - CCastleEvent(); - - std::set buildings; - std::vector creatures; - CGTownInstance * town; - - template - void serialize(Handler & h, const int version) - { - h & static_cast(*this); - h & buildings & creatures; - } -}; - -/// The terrain tile describes the terrain type and the visual representation of the terrain. -/// Furthermore the struct defines whether the tile is visitable or/and blocked and which objects reside in it. -struct DLL_LINKAGE TerrainTile -{ - TerrainTile(); - - /// Gets true if the terrain is not a rock. If from is water/land, same type is also required. - bool entrableTerrain(const TerrainTile * from = nullptr) const; - bool entrableTerrain(bool allowLand, bool allowSea) const; - /// Checks for blocking objects and terraint type (water / land). - bool isClear(const TerrainTile * from = nullptr) const; - /// Gets the ID of the top visitable object or -1 if there is none. - Obj topVisitableId(bool excludeTop = false) const; - CGObjectInstance * topVisitableObj(bool excludeTop = false) const; - bool isWater() const; - bool isCoastal() const; - EDiggingStatus getDiggingStatus(const bool excludeTop = true) const; - bool hasFavourableWinds() const; - - ETerrainType terType; - ui8 terView; - ERiverType::ERiverType riverType; - ui8 riverDir; - ERoadType::ERoadType roadType; - ui8 roadDir; - /// first two bits - how to rotate terrain graphic (next two - river graphic, next two - road); - /// 7th bit - whether tile is coastal (allows disembarking if land or block movement if water); 8th bit - Favourable Winds effect - ui8 extTileFlags; - bool visitable; - bool blocked; - - std::vector visitableObjects; - std::vector blockingObjects; - - template - void serialize(Handler & h, const int version) - { - h & terType & terView & riverType & riverDir & roadType &roadDir & extTileFlags; - h & visitable & blocked; - h & visitableObjects & blockingObjects; - } -}; - namespace EMapFormat { enum EMapFormat diff --git a/lib/mapping/CMapDefines.h b/lib/mapping/CMapDefines.h new file mode 100644 index 000000000..77f9bf0be --- /dev/null +++ b/lib/mapping/CMapDefines.h @@ -0,0 +1,99 @@ +/* + * CMapDefines.h, part of VCMI engine + * + * Authors: listed in file AUTHORS in main folder + * + * License: GNU General Public License v2.0 or later + * Full text of license available in license.txt file, in main folder + * + */ + +#pragma once + +/// The map event is an event which e.g. gives or takes resources of a specific +/// amount to/from players and can appear regularly or once a time. +class DLL_LINKAGE CMapEvent +{ +public: + CMapEvent(); + + bool earlierThan(const CMapEvent & other) const; + bool earlierThanOrEqual(const CMapEvent & other) const; + + std::string name; + std::string message; + TResources resources; + ui8 players; // affected players, bit field? + ui8 humanAffected; + ui8 computerAffected; + ui32 firstOccurence; + ui32 nextOccurence; /// specifies after how many days the event will occur the next time; 0 if event occurs only one time + + template + void serialize(Handler & h, const int version) + { + h & name & message & resources + & players & humanAffected & computerAffected & firstOccurence & nextOccurence; + } +}; + +/// The castle event builds/adds buildings/creatures for a specific town. +class DLL_LINKAGE CCastleEvent: public CMapEvent +{ +public: + CCastleEvent(); + + std::set buildings; + std::vector creatures; + CGTownInstance * town; + + template + void serialize(Handler & h, const int version) + { + h & static_cast(*this); + h & buildings & creatures; + } +}; + +/// The terrain tile describes the terrain type and the visual representation of the terrain. +/// Furthermore the struct defines whether the tile is visitable or/and blocked and which objects reside in it. +struct DLL_LINKAGE TerrainTile +{ + TerrainTile(); + + /// Gets true if the terrain is not a rock. If from is water/land, same type is also required. + bool entrableTerrain(const TerrainTile * from = nullptr) const; + bool entrableTerrain(bool allowLand, bool allowSea) const; + /// Checks for blocking objects and terraint type (water / land). + bool isClear(const TerrainTile * from = nullptr) const; + /// Gets the ID of the top visitable object or -1 if there is none. + Obj topVisitableId(bool excludeTop = false) const; + CGObjectInstance * topVisitableObj(bool excludeTop = false) const; + bool isWater() const; + bool isCoastal() const; + EDiggingStatus getDiggingStatus(const bool excludeTop = true) const; + bool hasFavourableWinds() const; + + ETerrainType terType; + ui8 terView; + ERiverType::ERiverType riverType; + ui8 riverDir; + ERoadType::ERoadType roadType; + ui8 roadDir; + /// first two bits - how to rotate terrain graphic (next two - river graphic, next two - road); + /// 7th bit - whether tile is coastal (allows disembarking if land or block movement if water); 8th bit - Favourable Winds effect + ui8 extTileFlags; + bool visitable; + bool blocked; + + std::vector visitableObjects; + std::vector blockingObjects; + + template + void serialize(Handler & h, const int version) + { + h & terType & terView & riverType & riverDir & roadType &roadDir & extTileFlags; + h & visitable & blocked; + h & visitableObjects & blockingObjects; + } +}; diff --git a/lib/mapping/CMapEditManager.cpp b/lib/mapping/CMapEditManager.cpp index f48a63197..8f5648c83 100644 --- a/lib/mapping/CMapEditManager.cpp +++ b/lib/mapping/CMapEditManager.cpp @@ -7,6 +7,7 @@ #include "../mapObjects/CGHeroInstance.h" #include "../VCMI_Lib.h" #include "CDrawRoadsOperation.h" +#include "../mapping/CMap.h" MapRect::MapRect() : x(0), y(0), z(0), width(0), height(0) { diff --git a/lib/mapping/CMapEditManager.h b/lib/mapping/CMapEditManager.h index fa8f16706..97e5ecd8f 100644 --- a/lib/mapping/CMapEditManager.h +++ b/lib/mapping/CMapEditManager.h @@ -12,11 +12,13 @@ #pragma once #include "../CRandomGenerator.h" -#include "CMap.h" +#include "../int3.h" +#include "../GameConstants.h" class CGObjectInstance; class CTerrainViewPatternConfig; struct TerrainViewPattern; +class CMap; /// Represents a map rectangle. struct DLL_LINKAGE MapRect diff --git a/lib/rmg/CZonePlacer.cpp b/lib/rmg/CZonePlacer.cpp index d89887494..118826fb2 100644 --- a/lib/rmg/CZonePlacer.cpp +++ b/lib/rmg/CZonePlacer.cpp @@ -13,6 +13,7 @@ #include "../CRandomGenerator.h" #include "CZonePlacer.h" #include "CRmgTemplateZone.h" +#include "../mapping/CMap.h" #include "CZoneGraphGenerator.h" diff --git a/lib/rmg/CZonePlacer.h b/lib/rmg/CZonePlacer.h index 092307269..2bd35d407 100644 --- a/lib/rmg/CZonePlacer.h +++ b/lib/rmg/CZonePlacer.h @@ -12,8 +12,6 @@ #pragma once #include "CMapGenerator.h" -#include "../mapping/CMap.h" - #include "float3.h" #include "../int3.h" diff --git a/lib/spells/AdventureSpellMechanics.cpp b/lib/spells/AdventureSpellMechanics.cpp index 85ec3df99..8ba05d3b4 100644 --- a/lib/spells/AdventureSpellMechanics.cpp +++ b/lib/spells/AdventureSpellMechanics.cpp @@ -18,6 +18,7 @@ #include "../BattleState.h" #include "../CGameState.h" #include "../CGameInfoCallback.h" +#include "../mapping/CMap.h" ///SummonBoatMechanics ESpellCastResult SummonBoatMechanics::applyAdventureEffects(const SpellCastEnvironment * env, AdventureSpellCastParameters & parameters) const diff --git a/lib/spells/BattleSpellMechanics.cpp b/lib/spells/BattleSpellMechanics.cpp index 065ad4513..14b946c56 100644 --- a/lib/spells/BattleSpellMechanics.cpp +++ b/lib/spells/BattleSpellMechanics.cpp @@ -14,6 +14,7 @@ #include "../NetPacks.h" #include "../BattleState.h" #include "../mapObjects/CGHeroInstance.h" +#include "../mapObjects/CGTownInstance.h" ///HealingSpellMechanics void HealingSpellMechanics::applyBattleEffects(const SpellCastEnvironment * env, const BattleSpellCastParameters & parameters, SpellCastContext & ctx) const diff --git a/server/CQuery.cpp b/server/CQuery.cpp index f03e50e63..badbd2e74 100644 --- a/server/CQuery.cpp +++ b/server/CQuery.cpp @@ -2,6 +2,7 @@ #include "CQuery.h" #include "CGameHandler.h" #include "../lib/BattleState.h" +#include "../lib/mapObjects/MiscObjects.h" boost::mutex Queries::mx; From 7b5a7f43ad646a8e60c43ba23bc809eb0eeb5acf Mon Sep 17 00:00:00 2001 From: Ivan Savenko Date: Wed, 2 Dec 2015 21:39:53 +0200 Subject: [PATCH 146/168] Removed includes of CGameState from headers --- AI/VCAI/AIUtility.cpp | 1 + AI/VCAI/AIUtility.h | 4 +- AI/VCAI/Fuzzy.cpp | 2 + AI/VCAI/Goals.cpp | 1 + AI/VCAI/VCAI.cpp | 1 + AI/VCAI/VCAI.h | 2 +- CCallback.cpp | 7 +- client/CPlayerInterface.cpp | 1 + client/CPlayerInterface.h | 1 - client/NetPacksClient.cpp | 1 + client/gui/Geometries.h | 7 -- lib/CGameInfoCallback.cpp | 1 + lib/CGameState.h | 123 ------------------------- lib/CGameStateFwd.h | 73 +++++++++++++++ lib/CMakeLists.txt | 1 + lib/CPathfinder.cpp | 1 + lib/CPlayerState.h | 66 +++++++++++++ lib/Connection.cpp | 1 + lib/IGameCallback.cpp | 1 + lib/NetPacksLib.cpp | 1 + lib/mapObjects/CArmedInstance.cpp | 3 +- lib/mapObjects/CGTownInstance.cpp | 1 + lib/mapObjects/CRewardableObject.cpp | 1 + lib/mapObjects/MiscObjects.cpp | 1 + lib/registerTypes/RegisterTypes.h | 2 +- lib/spells/AdventureSpellMechanics.cpp | 1 + server/CGameHandler.h | 2 +- 27 files changed, 165 insertions(+), 142 deletions(-) create mode 100644 lib/CPlayerState.h diff --git a/AI/VCAI/AIUtility.cpp b/AI/VCAI/AIUtility.cpp index 8de8aa9f5..4ffbef8c6 100644 --- a/AI/VCAI/AIUtility.cpp +++ b/AI/VCAI/AIUtility.cpp @@ -9,6 +9,7 @@ #include "../../lib/mapObjects/CBank.h" #include "../../lib/mapObjects/CGTownInstance.h" #include "../../lib/mapObjects/CQuest.h" +#include "../../lib/CPathfinder.h" /* * AIUtility.cpp, part of VCMI engine diff --git a/AI/VCAI/AIUtility.h b/AI/VCAI/AIUtility.h index 3ec1f8110..2d3577202 100644 --- a/AI/VCAI/AIUtility.h +++ b/AI/VCAI/AIUtility.h @@ -6,7 +6,7 @@ #include "../../lib/CTownHandler.h" #include "../../lib/spells/CSpellHandler.h" #include "../../lib/Connection.h" -#include "../../lib/CGameState.h" +//#include "../../lib/CGameState.h" #include "../../lib/NetPacks.h" #include "../../lib/CStopWatch.h" @@ -20,6 +20,8 @@ * */ +class CCallback; + typedef const int3& crint3; typedef const std::string& crstring; diff --git a/AI/VCAI/Fuzzy.cpp b/AI/VCAI/Fuzzy.cpp index bc7b60cfe..59e959924 100644 --- a/AI/VCAI/Fuzzy.cpp +++ b/AI/VCAI/Fuzzy.cpp @@ -5,6 +5,8 @@ #include "../../lib/mapObjects/MapObjects.h" #include "../../lib/mapObjects/CommonConstructors.h" #include "../../lib/CCreatureHandler.h" +#include "../../lib/CPathfinder.h" +#include "../../lib/CGameStateFwd.h" #include "../../lib/VCMI_Lib.h" #include "../../CCallback.h" #include "VCAI.h" diff --git a/AI/VCAI/Goals.cpp b/AI/VCAI/Goals.cpp index 3fb441c5a..6b15add0d 100644 --- a/AI/VCAI/Goals.cpp +++ b/AI/VCAI/Goals.cpp @@ -3,6 +3,7 @@ #include "VCAI.h" #include "Fuzzy.h" #include "../../lib/mapping/CMap.h" //for victory conditions +#include "../../lib/CPathfinder.h" /* * Goals.cpp, part of VCMI engine diff --git a/AI/VCAI/VCAI.cpp b/AI/VCAI/VCAI.cpp index c1b6a43bd..143e2b849 100644 --- a/AI/VCAI/VCAI.cpp +++ b/AI/VCAI/VCAI.cpp @@ -8,6 +8,7 @@ #include "../../lib/CConfigHandler.h" #include "../../lib/CHeroHandler.h" #include "../../lib/CModHandler.h" +#include "../../lib/CGameState.h" /* diff --git a/AI/VCAI/VCAI.h b/AI/VCAI/VCAI.h index 0da2ee61c..19c638cc2 100644 --- a/AI/VCAI/VCAI.h +++ b/AI/VCAI/VCAI.h @@ -15,7 +15,7 @@ #include "../../lib/mapObjects/MiscObjects.h" #include "../../lib/spells/CSpellHandler.h" #include "../../lib/Connection.h" -#include "../../lib/CGameState.h" +//#include "../../lib/CGameState.h" #include "../../lib/NetPacks.h" #include "../../lib/CondSh.h" diff --git a/CCallback.cpp b/CCallback.cpp index e48197d63..6b8a28958 100644 --- a/CCallback.cpp +++ b/CCallback.cpp @@ -18,12 +18,7 @@ #include "lib/spells/CSpellHandler.h" #include "lib/CArtHandler.h" #include "lib/GameConstants.h" -#ifdef min -#undef min -#endif -#ifdef max -#undef max -#endif +#include "lib/CPlayerState.h" #include "lib/UnlockGuard.h" /* diff --git a/client/CPlayerInterface.cpp b/client/CPlayerInterface.cpp index 61280f419..f263423f4 100644 --- a/client/CPlayerInterface.cpp +++ b/client/CPlayerInterface.cpp @@ -37,6 +37,7 @@ #include "../lib/CStopWatch.h" #include "../lib/StartInfo.h" #include "../lib/CGameState.h" +#include "../lib/CPlayerState.h" #include "../lib/GameConstants.h" #include "gui/CGuiHandler.h" #include "windows/InfoWindows.h" diff --git a/client/CPlayerInterface.h b/client/CPlayerInterface.h index 2e19aae30..80891f205 100644 --- a/client/CPlayerInterface.h +++ b/client/CPlayerInterface.h @@ -5,7 +5,6 @@ #include "../lib/CGameInterface.h" #include "../lib/NetPacksBase.h" #include "gui/CIntObject.h" -//#include "../lib/CGameState.h" #ifdef __GNUC__ #define sprintf_s snprintf diff --git a/client/NetPacksClient.cpp b/client/NetPacksClient.cpp index eb95d44b9..b8382868f 100644 --- a/client/NetPacksClient.cpp +++ b/client/NetPacksClient.cpp @@ -25,6 +25,7 @@ #include "../lib/CGameState.h" #include "../lib/BattleState.h" #include "../lib/GameConstants.h" +#include "../lib/CPlayerState.h" #include "gui/CGuiHandler.h" #include "widgets/MiscWidgets.h" #include "widgets/AdventureMapClasses.h" diff --git a/client/gui/Geometries.h b/client/gui/Geometries.h index af67813ec..c6ee4df7a 100644 --- a/client/gui/Geometries.h +++ b/client/gui/Geometries.h @@ -13,13 +13,6 @@ * */ -#ifdef max -#undef max -#endif -#ifdef min -#undef min -#endif - struct SDL_MouseMotionEvent; // A point with x/y coordinate, used mostly for graphic rendering diff --git a/lib/CGameInfoCallback.cpp b/lib/CGameInfoCallback.cpp index 9b030df9e..e7000ad46 100644 --- a/lib/CGameInfoCallback.cpp +++ b/lib/CGameInfoCallback.cpp @@ -19,6 +19,7 @@ #include "CModHandler.h" #include "spells/CSpellHandler.h" #include "mapping/CMap.h" +#include "CPlayerState.h" //TODO make clean #define ERROR_VERBOSE_OR_NOT_RET_VAL_IF(cond, verbose, txt, retVal) do {if(cond){if(verbose)logGlobal->errorStream() << BOOST_CURRENT_FUNCTION << ": " << txt; return retVal;}} while(0) diff --git a/lib/CGameState.h b/lib/CGameState.h index 458a4b714..f2642262f 100644 --- a/lib/CGameState.h +++ b/lib/CGameState.h @@ -62,79 +62,6 @@ namespace boost class shared_mutex; } -//numbers of creatures are exact numbers if detailed else they are quantity ids (1 - a few, 2 - several and so on; additionally 0 - unknown) -struct ArmyDescriptor : public std::map -{ - bool isDetailed; - DLL_LINKAGE ArmyDescriptor(const CArmedInstance *army, bool detailed); //not detailed -> quantity ids as count - DLL_LINKAGE ArmyDescriptor(); - - DLL_LINKAGE int getStrength() const; -}; - -struct DLL_LINKAGE InfoAboutArmy -{ - PlayerColor owner; - std::string name; - - ArmyDescriptor army; - - InfoAboutArmy(); - InfoAboutArmy(const CArmedInstance *Army, bool detailed); - - void initFromArmy(const CArmedInstance *Army, bool detailed); -}; - -struct DLL_LINKAGE InfoAboutHero : public InfoAboutArmy -{ -private: - void assign(const InfoAboutHero & iah); -public: - struct DLL_LINKAGE Details - { - std::vector primskills; - si32 mana, luck, morale; - } *details; - - const CHeroClass *hclass; - int portrait; - - InfoAboutHero(); - InfoAboutHero(const InfoAboutHero & iah); - InfoAboutHero(const CGHeroInstance *h, bool detailed); - ~InfoAboutHero(); - - InfoAboutHero & operator=(const InfoAboutHero & iah); - - void initFromHero(const CGHeroInstance *h, bool detailed); -}; - -/// Struct which holds a int information about a town -struct DLL_LINKAGE InfoAboutTown : public InfoAboutArmy -{ - struct DLL_LINKAGE Details - { - si32 hallLevel, goldIncome; - bool customRes; - bool garrisonedHero; - - } *details; - - const CTown *tType; - - si32 built; - si32 fortLevel; //0 - none - - InfoAboutTown(); - InfoAboutTown(const CGTownInstance *t, bool detailed); - ~InfoAboutTown(); - void initFromTown(const CGTownInstance *t, bool detailed); -}; - -// typedef si32 TResourceUnit; -// typedef std::vector TResourceVector; -// typedef std::set TResourceSet; - struct DLL_LINKAGE SThievesGuildInfo { std::vector playerColors; //colors of players that are in-game @@ -154,55 +81,6 @@ struct DLL_LINKAGE SThievesGuildInfo }; -struct DLL_LINKAGE PlayerState : public CBonusSystemNode -{ -public: - PlayerColor color; - bool human; //true if human controlled player, false for AI - TeamID team; - TResources resources; - std::set visitedObjects; // as a std::set, since most accesses here will be from visited status checks - std::vector > heroes; - std::vector > towns; - std::vector > availableHeroes; //heroes available in taverns - std::vector > dwellings; //used for town growth - std::vector quests; //store info about all received quests - - bool enteredWinningCheatCode, enteredLosingCheatCode; //if true, this player has entered cheat codes for loss / victory - EPlayerStatus::EStatus status; - boost::optional daysWithoutCastle; - - PlayerState(); - std::string nodeName() const override; - - template void serialize(Handler &h, const int version) - { - h & color & human & team & resources & status; - h & heroes & towns & availableHeroes & dwellings & quests & visitedObjects; - h & getBonusList(); //FIXME FIXME FIXME - h & status & daysWithoutCastle; - h & enteredLosingCheatCode & enteredWinningCheatCode; - h & static_cast(*this); - } -}; - -struct DLL_LINKAGE TeamState : public CBonusSystemNode -{ -public: - TeamID id; //position in gameState::teams - std::set players; // members of this team - std::vector > > fogOfWarMap; //true - visible, false - hidden - - TeamState(); - - template void serialize(Handler &h, const int version) - { - h & id & players & fogOfWarMap; - h & static_cast(*this); - } - -}; - struct UpgradeInfo { CreatureID oldID; //creature to be upgraded @@ -271,7 +149,6 @@ struct DLL_EXPORT DuelParameters } }; - struct BattleInfo; DLL_LINKAGE std::ostream & operator<<(std::ostream & os, const EVictoryLossCheckResult & victoryLossCheckResult); diff --git a/lib/CGameStateFwd.h b/lib/CGameStateFwd.h index d676e11c2..268300e02 100644 --- a/lib/CGameStateFwd.h +++ b/lib/CGameStateFwd.h @@ -10,8 +10,81 @@ * */ +#include "CCreatureSet.h" + class CQuest; class CGObjectInstance; +class CHeroClass; +class CTown; + +//numbers of creatures are exact numbers if detailed else they are quantity ids (1 - a few, 2 - several and so on; additionally 0 - unknown) +struct ArmyDescriptor : public std::map +{ + bool isDetailed; + DLL_LINKAGE ArmyDescriptor(const CArmedInstance *army, bool detailed); //not detailed -> quantity ids as count + DLL_LINKAGE ArmyDescriptor(); + + DLL_LINKAGE int getStrength() const; +}; + +struct DLL_LINKAGE InfoAboutArmy +{ + PlayerColor owner; + std::string name; + + ArmyDescriptor army; + + InfoAboutArmy(); + InfoAboutArmy(const CArmedInstance *Army, bool detailed); + + void initFromArmy(const CArmedInstance *Army, bool detailed); +}; + +struct DLL_LINKAGE InfoAboutHero : public InfoAboutArmy +{ +private: + void assign(const InfoAboutHero & iah); +public: + struct DLL_LINKAGE Details + { + std::vector primskills; + si32 mana, luck, morale; + } *details; + + const CHeroClass *hclass; + int portrait; + + InfoAboutHero(); + InfoAboutHero(const InfoAboutHero & iah); + InfoAboutHero(const CGHeroInstance *h, bool detailed); + ~InfoAboutHero(); + + InfoAboutHero & operator=(const InfoAboutHero & iah); + + void initFromHero(const CGHeroInstance *h, bool detailed); +}; + +/// Struct which holds a int information about a town +struct DLL_LINKAGE InfoAboutTown : public InfoAboutArmy +{ + struct DLL_LINKAGE Details + { + si32 hallLevel, goldIncome; + bool customRes; + bool garrisonedHero; + + } *details; + + const CTown *tType; + + si32 built; + si32 fortLevel; //0 - none + + InfoAboutTown(); + InfoAboutTown(const CGTownInstance *t, bool detailed); + ~InfoAboutTown(); + void initFromTown(const CGTownInstance *t, bool detailed); +}; class DLL_LINKAGE EVictoryLossCheckResult { diff --git a/lib/CMakeLists.txt b/lib/CMakeLists.txt index 0faf961e7..6657e0b91 100644 --- a/lib/CMakeLists.txt +++ b/lib/CMakeLists.txt @@ -132,6 +132,7 @@ set(lib_HEADERS IGameEventsReceiver.h int3.h CGameStateFwd.h + CPlayerState.h Interprocess.h NetPacks.h NetPacksBase.h diff --git a/lib/CPathfinder.cpp b/lib/CPathfinder.cpp index 8be71f212..8858fdc3a 100644 --- a/lib/CPathfinder.cpp +++ b/lib/CPathfinder.cpp @@ -8,6 +8,7 @@ #include "GameConstants.h" #include "CStopWatch.h" #include "CConfigHandler.h" +#include "../lib/CPlayerState.h" /* * CPathfinder.cpp, part of VCMI engine diff --git a/lib/CPlayerState.h b/lib/CPlayerState.h new file mode 100644 index 000000000..f676379a3 --- /dev/null +++ b/lib/CPlayerState.h @@ -0,0 +1,66 @@ +#pragma once + +/* + * CPlayerState.h, part of VCMI engine + * + * Authors: listed in file AUTHORS in main folder + * + * License: GNU General Public License v2.0 or later + * Full text of license available in license.txt file, in main folder + * + */ + +#include "HeroBonus.h" + +class CGHeroInstance; +class CGTownInstance; +class CGDwelling; + +struct DLL_LINKAGE PlayerState : public CBonusSystemNode +{ +public: + PlayerColor color; + bool human; //true if human controlled player, false for AI + TeamID team; + TResources resources; + std::set visitedObjects; // as a std::set, since most accesses here will be from visited status checks + std::vector > heroes; + std::vector > towns; + std::vector > availableHeroes; //heroes available in taverns + std::vector > dwellings; //used for town growth + std::vector quests; //store info about all received quests + + bool enteredWinningCheatCode, enteredLosingCheatCode; //if true, this player has entered cheat codes for loss / victory + EPlayerStatus::EStatus status; + boost::optional daysWithoutCastle; + + PlayerState(); + std::string nodeName() const override; + + template void serialize(Handler &h, const int version) + { + h & color & human & team & resources & status; + h & heroes & towns & availableHeroes & dwellings & quests & visitedObjects; + h & getBonusList(); //FIXME FIXME FIXME + h & status & daysWithoutCastle; + h & enteredLosingCheatCode & enteredWinningCheatCode; + h & static_cast(*this); + } +}; + +struct DLL_LINKAGE TeamState : public CBonusSystemNode +{ +public: + TeamID id; //position in gameState::teams + std::set players; // members of this team + std::vector > > fogOfWarMap; //true - visible, false - hidden + + TeamState(); + + template void serialize(Handler &h, const int version) + { + h & id & players & fogOfWarMap; + h & static_cast(*this); + } + +}; diff --git a/lib/Connection.cpp b/lib/Connection.cpp index 24eeb6642..7bc772093 100644 --- a/lib/Connection.cpp +++ b/lib/Connection.cpp @@ -3,6 +3,7 @@ #include "registerTypes/RegisterTypes.h" #include "mapping/CMap.h" +#include "CGameState.h" #include diff --git a/lib/IGameCallback.cpp b/lib/IGameCallback.cpp index d562c7410..561e9cd11 100644 --- a/lib/IGameCallback.cpp +++ b/lib/IGameCallback.cpp @@ -22,6 +22,7 @@ #include "StartInfo.h" #include "CGameState.h" #include "mapping/CMap.h" +#include "CPlayerState.h" void CPrivilagedInfoCallback::getFreeTiles (std::vector &tiles) const { diff --git a/lib/NetPacksLib.cpp b/lib/NetPacksLib.cpp index 5483c2936..72b4ecff5 100644 --- a/lib/NetPacksLib.cpp +++ b/lib/NetPacksLib.cpp @@ -16,6 +16,7 @@ #include "CTownHandler.h" #include "mapping/CMapInfo.h" #include "StartInfo.h" +#include "CPlayerState.h" /* * NetPacksLib.cpp, part of VCMI engine diff --git a/lib/mapObjects/CArmedInstance.cpp b/lib/mapObjects/CArmedInstance.cpp index d6d879b7e..adc47a7a5 100644 --- a/lib/mapObjects/CArmedInstance.cpp +++ b/lib/mapObjects/CArmedInstance.cpp @@ -1,4 +1,4 @@ -/* +/* * CArmedInstance.cpp, part of VCMI engine * * Authors: listed in file AUTHORS in main folder @@ -15,6 +15,7 @@ #include "../CCreatureHandler.h" #include "../CGeneralTextHandler.h" #include "../CGameState.h" +#include "../CPlayerState.h" void CArmedInstance::randomizeArmy(int type) { diff --git a/lib/mapObjects/CGTownInstance.cpp b/lib/mapObjects/CGTownInstance.cpp index e640eaa0b..ba1b8baca 100644 --- a/lib/mapObjects/CGTownInstance.cpp +++ b/lib/mapObjects/CGTownInstance.cpp @@ -18,6 +18,7 @@ #include "../IGameCallback.h" #include "../CGameState.h" #include "../mapping/CMapDefines.h" +#include "../CPlayerState.h" std::vector CGTownInstance::merchantArtifacts; std::vector CGTownInstance::universitySkills; diff --git a/lib/mapObjects/CRewardableObject.cpp b/lib/mapObjects/CRewardableObject.cpp index 5c6d80cbd..bb454007c 100644 --- a/lib/mapObjects/CRewardableObject.cpp +++ b/lib/mapObjects/CRewardableObject.cpp @@ -17,6 +17,7 @@ #include "../NetPacks.h" #include "../IGameCallback.h" #include "../CGameState.h" +#include "../CPlayerState.h" #include "CObjectClassesHandler.h" diff --git a/lib/mapObjects/MiscObjects.cpp b/lib/mapObjects/MiscObjects.cpp index eb04fd0c1..025920192 100644 --- a/lib/mapObjects/MiscObjects.cpp +++ b/lib/mapObjects/MiscObjects.cpp @@ -21,6 +21,7 @@ #include "../IGameCallback.h" #include "../CGameState.h" #include "../mapping/CMap.h" +#include "../CPlayerState.h" std::map > CGMagi::eyelist; ui8 CGObelisk::obeliskCount; //how many obelisks are on map diff --git a/lib/registerTypes/RegisterTypes.h b/lib/registerTypes/RegisterTypes.h index 173b08116..e3d3a9654 100644 --- a/lib/registerTypes/RegisterTypes.h +++ b/lib/registerTypes/RegisterTypes.h @@ -4,7 +4,7 @@ #include "../NetPacks.h" #include "../VCMI_Lib.h" #include "../CArtHandler.h" -#include "../CGameState.h" +#include "../CPlayerState.h" #include "../CHeroHandler.h" #include "../CTownHandler.h" #include "../CModHandler.h" //needed? diff --git a/lib/spells/AdventureSpellMechanics.cpp b/lib/spells/AdventureSpellMechanics.cpp index 8ba05d3b4..c8ac2f9d0 100644 --- a/lib/spells/AdventureSpellMechanics.cpp +++ b/lib/spells/AdventureSpellMechanics.cpp @@ -19,6 +19,7 @@ #include "../CGameState.h" #include "../CGameInfoCallback.h" #include "../mapping/CMap.h" +#include "../CPlayerState.h" ///SummonBoatMechanics ESpellCastResult SummonBoatMechanics::applyAdventureEffects(const SpellCastEnvironment * env, AdventureSpellCastParameters & parameters) const diff --git a/server/CGameHandler.h b/server/CGameHandler.h index acf1698a2..fb1fff251 100644 --- a/server/CGameHandler.h +++ b/server/CGameHandler.h @@ -2,7 +2,7 @@ #include "../lib/FunctionList.h" -#include "../lib/CGameState.h" +//#include "../lib/CGameState.h" #include "../lib/Connection.h" #include "../lib/IGameCallback.h" #include "../lib/BattleAction.h" From 7189a12df223bc8a94401ffea98f98b726fdd640 Mon Sep 17 00:00:00 2001 From: Ivan Savenko Date: Wed, 2 Dec 2015 22:10:46 +0200 Subject: [PATCH 147/168] removed few more includes from headers --- AI/VCAI/AIUtility.cpp | 1 + AI/VCAI/AIUtility.h | 2 -- AI/VCAI/VCAI.cpp | 1 + AI/VCAI/VCAI.h | 2 -- lib/BattleState.h | 3 --- lib/Connection.h | 2 +- lib/registerTypes/RegisterTypes.h | 2 -- server/CGameHandler.h | 2 -- 8 files changed, 3 insertions(+), 12 deletions(-) diff --git a/AI/VCAI/AIUtility.cpp b/AI/VCAI/AIUtility.cpp index 4ffbef8c6..f3892ef49 100644 --- a/AI/VCAI/AIUtility.cpp +++ b/AI/VCAI/AIUtility.cpp @@ -10,6 +10,7 @@ #include "../../lib/mapObjects/CGTownInstance.h" #include "../../lib/mapObjects/CQuest.h" #include "../../lib/CPathfinder.h" +#include "../../lib/mapping/CMapDefines.h" /* * AIUtility.cpp, part of VCMI engine diff --git a/AI/VCAI/AIUtility.h b/AI/VCAI/AIUtility.h index 2d3577202..bb551e51b 100644 --- a/AI/VCAI/AIUtility.h +++ b/AI/VCAI/AIUtility.h @@ -6,8 +6,6 @@ #include "../../lib/CTownHandler.h" #include "../../lib/spells/CSpellHandler.h" #include "../../lib/Connection.h" -//#include "../../lib/CGameState.h" -#include "../../lib/NetPacks.h" #include "../../lib/CStopWatch.h" /* diff --git a/AI/VCAI/VCAI.cpp b/AI/VCAI/VCAI.cpp index 143e2b849..17cefc5e3 100644 --- a/AI/VCAI/VCAI.cpp +++ b/AI/VCAI/VCAI.cpp @@ -9,6 +9,7 @@ #include "../../lib/CHeroHandler.h" #include "../../lib/CModHandler.h" #include "../../lib/CGameState.h" +#include "../../lib/NetPacks.h" /* diff --git a/AI/VCAI/VCAI.h b/AI/VCAI/VCAI.h index 19c638cc2..c90c25dbf 100644 --- a/AI/VCAI/VCAI.h +++ b/AI/VCAI/VCAI.h @@ -15,8 +15,6 @@ #include "../../lib/mapObjects/MiscObjects.h" #include "../../lib/spells/CSpellHandler.h" #include "../../lib/Connection.h" -//#include "../../lib/CGameState.h" -#include "../../lib/NetPacks.h" #include "../../lib/CondSh.h" struct QuestInfo; diff --git a/lib/BattleState.h b/lib/BattleState.h index 44260ad32..a06b517cd 100644 --- a/lib/BattleState.h +++ b/lib/BattleState.h @@ -11,12 +11,9 @@ #pragma once #include "BattleHex.h" -#include "HeroBonus.h" -#include "CCreatureSet.h" #include "mapObjects/CArmedInstance.h" // for army serialization #include "mapObjects/CGHeroInstance.h" // for commander serialization #include "CCreatureHandler.h" -#include "CObstacleInstance.h" #include "ConstTransitivePtr.h" #include "GameConstants.h" #include "CBattleCallback.h" diff --git a/lib/Connection.h b/lib/Connection.h index ee07bf9aa..49acb9c14 100644 --- a/lib/Connection.h +++ b/lib/Connection.h @@ -11,7 +11,7 @@ #pragma once -#include //XXX this is in namespace std if you want w/o use typeinfo.h? +#include #include #include diff --git a/lib/registerTypes/RegisterTypes.h b/lib/registerTypes/RegisterTypes.h index e3d3a9654..13ab1b028 100644 --- a/lib/registerTypes/RegisterTypes.h +++ b/lib/registerTypes/RegisterTypes.h @@ -24,8 +24,6 @@ * */ - - template void registerTypesMapObjects1(Serializer &s) { diff --git a/server/CGameHandler.h b/server/CGameHandler.h index fb1fff251..4370e6aec 100644 --- a/server/CGameHandler.h +++ b/server/CGameHandler.h @@ -2,11 +2,9 @@ #include "../lib/FunctionList.h" -//#include "../lib/CGameState.h" #include "../lib/Connection.h" #include "../lib/IGameCallback.h" #include "../lib/BattleAction.h" -#include "../lib/NetPacks.h" #include "CQuery.h" From 90dfe3e782dd58bf8347665fb3741424af620b7e Mon Sep 17 00:00:00 2001 From: Ivan Savenko Date: Wed, 2 Dec 2015 22:34:13 +0200 Subject: [PATCH 148/168] reduced visibility for members of Mine and Dwellings --- lib/mapObjects/CGTownInstance.h | 15 ++++++++------- lib/mapObjects/MiscObjects.h | 4 +++- 2 files changed, 11 insertions(+), 8 deletions(-) diff --git a/lib/mapObjects/CGTownInstance.h b/lib/mapObjects/CGTownInstance.h index c283000d7..b636b16a5 100644 --- a/lib/mapObjects/CGTownInstance.h +++ b/lib/mapObjects/CGTownInstance.h @@ -1,4 +1,4 @@ -#pragma once +#pragma once #include "CObjectHandler.h" #include "CGMarket.h" // For IMarket interface @@ -52,11 +52,7 @@ public: CSpecObjInfo * info; //h3m info about dewlling TCreaturesSet creatures; //creatures[level] -> - template void serialize(Handler &h, const int version) - { - h & static_cast(*this) & creatures; - } - +private: void initObj() override; void onHeroVisit(const CGHeroInstance * h) const override; void newTurn() const override; @@ -64,9 +60,14 @@ public: void battleFinished(const CGHeroInstance *hero, const BattleResult &result) const override; void blockingDialogAnswered(const CGHeroInstance *hero, ui32 answer) const override; -private: void updateGuards() const; void heroAcceptsCreatures(const CGHeroInstance *h) const; + +public: + template void serialize(Handler &h, const int version) + { + h & static_cast(*this) & creatures; + } }; class DLL_LINKAGE CGTownBuilding : public IObjectInterface diff --git a/lib/mapObjects/MiscObjects.h b/lib/mapObjects/MiscObjects.h index 77a36e04d..bff8630c4 100644 --- a/lib/mapObjects/MiscObjects.h +++ b/lib/mapObjects/MiscObjects.h @@ -227,7 +227,8 @@ class DLL_LINKAGE CGMine : public CArmedInstance public: Res::ERes producedResource; ui32 producedQuantity; - + +private: void onHeroVisit(const CGHeroInstance * h) const override; void battleFinished(const CGHeroInstance *hero, const BattleResult &result) const override; void blockingDialogAnswered(const CGHeroInstance *hero, ui32 answer) const override; @@ -239,6 +240,7 @@ public: std::string getObjectName() const override; std::string getHoverText(PlayerColor player) const override; +public: template void serialize(Handler &h, const int version) { h & static_cast(*this); From 2f9ca778b27f7790bc25cbfd81eedbc836d417f0 Mon Sep 17 00:00:00 2001 From: ArseniyShestakov Date: Thu, 3 Dec 2015 17:20:03 +0300 Subject: [PATCH 149/168] VCAI: add channel probing support for teleporters with multiple exits --- AI/VCAI/VCAI.cpp | 38 +++++++++++++++++++++++--------------- server/CGameHandler.cpp | 2 +- 2 files changed, 24 insertions(+), 16 deletions(-) diff --git a/AI/VCAI/VCAI.cpp b/AI/VCAI/VCAI.cpp index 80c2f7f10..ee87c0f5d 100644 --- a/AI/VCAI/VCAI.cpp +++ b/AI/VCAI/VCAI.cpp @@ -625,22 +625,29 @@ void VCAI::showTeleportDialog(TeleportChannelID channel, TTeleportExitsList exit int choosenExit = -1; if(impassable) knownTeleportChannels[channel]->passability = TeleportChannel::IMPASSABLE; - else + else if(destinationTeleport != ObjectInstanceID() && destinationTeleportPos.valid()) { auto neededExit = std::make_pair(destinationTeleport, destinationTeleportPos); if(destinationTeleport != ObjectInstanceID() && vstd::contains(exits, neededExit)) choosenExit = vstd::find_pos(exits, neededExit); + } - if(!status.channelProbing()) + for(auto exit : exits) + { + if(status.channelProbing() && exit.first == destinationTeleport) { - for(auto exit : exits) + choosenExit = vstd::find_pos(exits, exit); + break; + } + else + { + // FIXME: This code generate "Object is not visible." errors + // What is better way to check that certain teleport exit wasn't visited yet or not visible? + if(!vstd::contains(visitableObjs, cb->getObj(exit.first)) && + !vstd::contains(teleportChannelProbingList, exit.first) && + exit.first != destinationTeleport) { - if(!vstd::contains(visitableObjs, cb->getObj(exit.first)) && - !vstd::contains(teleportChannelProbingList, exit.first) && - exit.first != destinationTeleport) - { - teleportChannelProbingList.push_back(exit.first); - } + teleportChannelProbingList.push_back(exit.first); } } } @@ -1867,11 +1874,11 @@ bool VCAI::moveHeroToTile(int3 dst, HeroPtr h) cb->moveHero(*h, CGHeroInstance::convertPosition(dst, true), transit); }; - auto doTeleportMovement = [&](int3 dst, ObjectInstanceID exitId) + auto doTeleportMovement = [&](ObjectInstanceID exitId, int3 exitPos) { destinationTeleport = exitId; - destinationTeleportPos = CGHeroInstance::convertPosition(dst, true); - cb->moveHero(*h, destinationTeleportPos); + destinationTeleportPos = CGHeroInstance::convertPosition(exitPos, true); + cb->moveHero(*h, h->pos); destinationTeleport = ObjectInstanceID(); destinationTeleportPos = int3(); afterMovementCheck(); @@ -1880,13 +1887,14 @@ bool VCAI::moveHeroToTile(int3 dst, HeroPtr h) auto doChannelProbing = [&]() -> void { auto currentExit = getObj(CGHeroInstance::convertPosition(h->pos,false), false); + auto currentExitPos = CGHeroInstance::convertPosition(h->pos,false); assert(currentExit); status.setChannelProbing(true); for(auto exit : teleportChannelProbingList) - doTeleportMovement(CGHeroInstance::convertPosition(h->pos,false), exit); + doTeleportMovement(exit, int3()); teleportChannelProbingList.clear(); - doTeleportMovement(CGHeroInstance::convertPosition(h->pos,false), currentExit->id); + doTeleportMovement(currentExit->id, currentExitPos); status.setChannelProbing(false); }; @@ -1900,7 +1908,7 @@ bool VCAI::moveHeroToTile(int3 dst, HeroPtr h) auto nextObject = getObj(nextCoord, false); if(CGTeleport::isConnected(currentObject, nextObject)) { //we use special login if hero standing on teleporter it's mean we need - doTeleportMovement(currentCoord, nextObject->id); + doTeleportMovement(nextObject->id, nextCoord); if(teleportChannelProbingList.size()) doChannelProbing(); diff --git a/server/CGameHandler.cpp b/server/CGameHandler.cpp index 7be5428e6..645a90b25 100644 --- a/server/CGameHandler.cpp +++ b/server/CGameHandler.cpp @@ -1755,7 +1755,7 @@ bool CGameHandler::moveHero( ObjectInstanceID hid, int3 dst, ui8 teleporting, bo } logGlobal->traceStream() << "Player " << asker << " wants to move hero "<< hid.getNum() << " from "<< h->pos << " to " << dst; - const int3 hmpos = dst + int3(-1,0,0); + const int3 hmpos = CGHeroInstance::convertPosition(dst, false); if(!gs->map->isInTheMap(hmpos)) { From ee0874974328c8d45c116c6bfc91937788aceda8 Mon Sep 17 00:00:00 2001 From: ArseniyShestakov Date: Thu, 3 Dec 2015 21:18:40 +0300 Subject: [PATCH 150/168] VCAI: more work on teleport exit probing --- AI/VCAI/VCAI.cpp | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/AI/VCAI/VCAI.cpp b/AI/VCAI/VCAI.cpp index ee87c0f5d..cf99d0697 100644 --- a/AI/VCAI/VCAI.cpp +++ b/AI/VCAI/VCAI.cpp @@ -641,10 +641,10 @@ void VCAI::showTeleportDialog(TeleportChannelID channel, TTeleportExitsList exit } else { - // FIXME: This code generate "Object is not visible." errors - // What is better way to check that certain teleport exit wasn't visited yet or not visible? - if(!vstd::contains(visitableObjs, cb->getObj(exit.first)) && - !vstd::contains(teleportChannelProbingList, exit.first) && + // TODO: Implement checking if visiting that teleport will uncovert any FoW + // So far this is the best option to handle decision about probing + auto obj = cb->getObj(exit.first, false); + if(obj == nullptr && !vstd::contains(teleportChannelProbingList, exit.first) && exit.first != destinationTeleport) { teleportChannelProbingList.push_back(exit.first); @@ -1877,7 +1877,8 @@ bool VCAI::moveHeroToTile(int3 dst, HeroPtr h) auto doTeleportMovement = [&](ObjectInstanceID exitId, int3 exitPos) { destinationTeleport = exitId; - destinationTeleportPos = CGHeroInstance::convertPosition(exitPos, true); + if(exitPos.valid()) + destinationTeleportPos = CGHeroInstance::convertPosition(exitPos, true); cb->moveHero(*h, h->pos); destinationTeleport = ObjectInstanceID(); destinationTeleportPos = int3(); From 73b4188fab89ef24d8e4fd8d6f4395199392a399 Mon Sep 17 00:00:00 2001 From: Ivan Savenko Date: Thu, 3 Dec 2015 21:28:01 +0200 Subject: [PATCH 151/168] Fixed access to unitialized memory causing StupidAI to act stupid --- AI/StupidAI/StupidAI.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/AI/StupidAI/StupidAI.cpp b/AI/StupidAI/StupidAI.cpp index 7fbda2d42..b87b14a8d 100644 --- a/AI/StupidAI/StupidAI.cpp +++ b/AI/StupidAI/StupidAI.cpp @@ -153,6 +153,12 @@ BattleAction CStupidAI::activeStack( const CStack * stack ) } } + for ( auto & enemy : enemiesReachable ) + enemy.calcDmg( stack ); + + for ( auto & enemy : enemiesShootable ) + enemy.calcDmg( stack ); + if(enemiesShootable.size()) { const EnemyInfo &ei= *std::max_element(enemiesShootable.begin(), enemiesShootable.end(), isMoreProfitable); From 2e56b547ee8c1df30bf63319594a0b23e7f1029c Mon Sep 17 00:00:00 2001 From: Ivan Savenko Date: Fri, 4 Dec 2015 00:10:51 +0200 Subject: [PATCH 152/168] replaced references to SectorMap with shared_ptr to avoid data races in AI code --- AI/VCAI/AIUtility.cpp | 4 ++-- AI/VCAI/Fuzzy.cpp | 2 +- AI/VCAI/Goals.cpp | 16 ++++++++-------- AI/VCAI/VCAI.cpp | 22 +++++++++++----------- AI/VCAI/VCAI.h | 4 ++-- 5 files changed, 24 insertions(+), 24 deletions(-) diff --git a/AI/VCAI/AIUtility.cpp b/AI/VCAI/AIUtility.cpp index f3892ef49..049d1d9c5 100644 --- a/AI/VCAI/AIUtility.cpp +++ b/AI/VCAI/AIUtility.cpp @@ -363,7 +363,7 @@ int3 whereToExplore(HeroPtr h) int radius = h->getSightRadious(); int3 hpos = h->visitablePos(); - SectorMap &sm = ai->getCachedSectorMap(h); + auto sm = ai->getCachedSectorMap(h); //look for nearby objs -> visit them if they're close enouh const int DIST_LIMIT = 3; @@ -378,7 +378,7 @@ int3 whereToExplore(HeroPtr h) CGPath 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)) + if (ai->isGoodForVisit(obj, h, *sm)) nearbyVisitableObjs.push_back(obj); } } diff --git a/AI/VCAI/Fuzzy.cpp b/AI/VCAI/Fuzzy.cpp index 59e959924..098899b64 100644 --- a/AI/VCAI/Fuzzy.cpp +++ b/AI/VCAI/Fuzzy.cpp @@ -484,7 +484,7 @@ float FuzzyHelper::evaluate (Goals::ClearWayTo & g) if (!g.hero.h) throw cannotFulfillGoalException("ClearWayTo called without hero!"); - int3 t = ai->getCachedSectorMap(g.hero).firstTileToGet(g.hero, g.tile); + int3 t = ai->getCachedSectorMap(g.hero)->firstTileToGet(g.hero, g.tile); if (t.valid()) { diff --git a/AI/VCAI/Goals.cpp b/AI/VCAI/Goals.cpp index 6b15add0d..9d4597940 100644 --- a/AI/VCAI/Goals.cpp +++ b/AI/VCAI/Goals.cpp @@ -484,9 +484,9 @@ TGoalVec ClearWayTo::getAllPossibleSubgoals() //if our hero is trapped, make sure we request clearing the way from OUR perspective - SectorMap &sm = ai->getCachedSectorMap(h); + auto sm = ai->getCachedSectorMap(h); - int3 tileToHit = sm.firstTileToGet(h, tile); + int3 tileToHit = sm->firstTileToGet(h, tile); if (!tileToHit.valid()) continue; @@ -634,11 +634,11 @@ TGoalVec Explore::getAllPossibleSubgoals() for (auto h : heroes) { - SectorMap &sm = ai->getCachedSectorMap(h); + auto sm = ai->getCachedSectorMap(h); for (auto obj : objs) //double loop, performance risk? { - auto t = sm.firstTileToGet(h, obj->visitablePos()); //we assume that no more than one tile on the way is guarded + auto t = sm->firstTileToGet(h, obj->visitablePos()); //we assume that no more than one tile on the way is guarded if (ai->isTileNotReserved(h, t)) ret.push_back (sptr(Goals::ClearWayTo(obj->visitablePos(), h).setisAbstract(true))); } @@ -964,7 +964,7 @@ TGoalVec Conquer::getAllPossibleSubgoals() for (auto h : cb->getHeroesInfo()) { - SectorMap &sm = ai->getCachedSectorMap(h); + auto sm = ai->getCachedSectorMap(h); std::vector ourObjs(objs); //copy common objects for (auto obj : ai->reservedHeroesMap[h]) //add objects reserved by this hero @@ -975,7 +975,7 @@ TGoalVec Conquer::getAllPossibleSubgoals() for (auto obj : ourObjs) { int3 dest = obj->visitablePos(); - auto t = sm.firstTileToGet(h, dest); //we assume that no more than one tile on the way is guarded + auto t = sm->firstTileToGet(h, dest); //we assume that no more than one tile on the way is guarded if (t.valid()) //we know any path at all { if (ai->isTileNotReserved(h, t)) //no other hero wants to conquer that tile @@ -1094,11 +1094,11 @@ TGoalVec GatherArmy::getAllPossibleSubgoals() } for(auto h : cb->getHeroesInfo()) { - SectorMap &sm = ai->getCachedSectorMap(h); + auto sm = ai->getCachedSectorMap(h); for (auto obj : objs) { //find safe dwelling auto pos = obj->visitablePos(); - if (ai->isGoodForVisit(obj, h, sm)) + if (ai->isGoodForVisit(obj, h, *sm)) ret.push_back (sptr (Goals::VisitTile(pos).sethero(h))); } } diff --git a/AI/VCAI/VCAI.cpp b/AI/VCAI/VCAI.cpp index 17cefc5e3..09ef4b8dc 100644 --- a/AI/VCAI/VCAI.cpp +++ b/AI/VCAI/VCAI.cpp @@ -845,9 +845,9 @@ void VCAI::makeTurnInternal() bool VCAI::goVisitObj(const CGObjectInstance * obj, HeroPtr h) { int3 dst = obj->visitablePos(); - SectorMap &sm = getCachedSectorMap(h); + auto sm = getCachedSectorMap(h); logAi->debugStream() << boost::format("%s will try to visit %s at (%s)") % h->name % obj->getObjectName() % strFromInt3(dst); - int3 pos = sm.firstTileToGet(h, dst); + int3 pos = sm->firstTileToGet(h, dst); if (!pos.valid()) //rare case when we are already standing on one of potential objects return false; return moveHeroToTile(pos, h); @@ -1383,10 +1383,10 @@ std::vector VCAI::getPossibleDestinations(HeroPtr h) { validateVisitableObjs(); std::vector possibleDestinations; - SectorMap &sm = getCachedSectorMap(h); + auto sm = getCachedSectorMap(h); for(const CGObjectInstance *obj : visitableObjs) { - if (isGoodForVisit(obj, h, sm)) + if (isGoodForVisit(obj, h, *sm)) { possibleDestinations.push_back(obj); } @@ -1441,12 +1441,12 @@ void VCAI::wander(HeroPtr h) validateVisitableObjs(); std::vector dests, tmp; - SectorMap &sm = getCachedSectorMap(h); + auto sm = getCachedSectorMap(h); range::copy(reservedHeroesMap[h], std::back_inserter(tmp)); //also visit our reserved objects - but they are not prioritized to avoid running back and forth for (auto obj : tmp) { - int3 pos = sm.firstTileToGet(h, obj->visitablePos()); + int3 pos = sm->firstTileToGet(h, obj->visitablePos()); if (pos.valid()) if (isAccessibleForHero (pos, h)) //even nearby objects could be blocked by other heroes :( dests.push_back(obj); //can't use lambda for member function :( @@ -1455,7 +1455,7 @@ void VCAI::wander(HeroPtr h) range::copy(getPossibleDestinations(h), std::back_inserter(dests)); erase_if(dests, [&](ObjectIdRef obj) -> bool { - return !isSafeToVisit(h, sm.firstTileToGet(h, obj->visitablePos())); + return !isSafeToVisit(h, sm->firstTileToGet(h, obj->visitablePos())); }); if(!dests.size()) @@ -2541,7 +2541,7 @@ int3 VCAI::explorationNewPoint(HeroPtr h) int3 VCAI::explorationDesperate(HeroPtr h) { - SectorMap &sm = getCachedSectorMap(h); + auto sm = getCachedSectorMap(h); int radius = h->getSightRadious(); std::vector > tiles; //tiles[distance_to_fow] @@ -2570,7 +2570,7 @@ int3 VCAI::explorationDesperate(HeroPtr h) if (!howManyTilesWillBeDiscovered(tile, radius, cbp)) //avoid costly checks of tiles that don't reveal much continue; - auto t = sm.firstTileToGet(h, tile); + auto t = sm->firstTileToGet(h, tile); if (t.valid()) { ui64 ourDanger = evaluateDanger(t, h.h); @@ -2759,14 +2759,14 @@ TResources VCAI::freeResources() const return myRes; } -SectorMap& VCAI::getCachedSectorMap(HeroPtr h) +std::shared_ptr VCAI::getCachedSectorMap(HeroPtr h) { auto it = cachedSectorMaps.find(h); if (it != cachedSectorMaps.end()) return it->second; else { - cachedSectorMaps.insert(std::make_pair(h, SectorMap(h))); + cachedSectorMaps[h] = std::make_shared(h); return cachedSectorMaps[h]; } } diff --git a/AI/VCAI/VCAI.h b/AI/VCAI/VCAI.h index c90c25dbf..4da33bc71 100644 --- a/AI/VCAI/VCAI.h +++ b/AI/VCAI/VCAI.h @@ -159,7 +159,7 @@ public: std::set alreadyVisited; std::set reservedObjs; //to be visited by specific hero - std::map cachedSectorMaps; //TODO: serialize? not necessary + std::map > cachedSectorMaps; //TODO: serialize? not necessary TResources saving; @@ -312,7 +312,7 @@ public: const CGObjectInstance *getUnvisitedObj(const std::function &predicate); bool isAccessibleForHero(const int3 & pos, HeroPtr h, bool includeAllies = false) const; //optimization - use one SM for every hero call - SectorMap& getCachedSectorMap(HeroPtr h); + std::shared_ptr getCachedSectorMap(HeroPtr h); const CGTownInstance *findTownWithTavern() const; bool canRecruitAnyHero(const CGTownInstance * t = NULL) const; From f02e553b706ca3b60812cc4877a44068b7737903 Mon Sep 17 00:00:00 2001 From: Ivan Savenko Date: Fri, 4 Dec 2015 00:11:44 +0200 Subject: [PATCH 153/168] Fixed another uninitialized field possiblity causing issues with StupidAI --- lib/CBattleCallback.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/CBattleCallback.cpp b/lib/CBattleCallback.cpp index cb522f0e6..3116bf818 100644 --- a/lib/CBattleCallback.cpp +++ b/lib/CBattleCallback.cpp @@ -2246,6 +2246,7 @@ BattleAttackInfo::BattleAttackInfo(const CStack *Attacker, const CStack *Defende chargedFields = 0; luckyHit = false; + unluckyHit = false; deathBlow = false; ballistaDoubleDamage = false; } From c2f4991e99f04a6fabf4712031fb1b7849232416 Mon Sep 17 00:00:00 2001 From: Ivan Savenko Date: Fri, 4 Dec 2015 00:12:49 +0200 Subject: [PATCH 154/168] Replaced barrier with mutex due to data races: Destruction of barrier while one of the threads is still in barrier.wait() is illegal. This may happen if caller thread reaches wait() after helper thread and immediately return's from the function destroying barrier which is still in use by helper thread --- AI/VCAI/VCAI.cpp | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/AI/VCAI/VCAI.cpp b/AI/VCAI/VCAI.cpp index 09ef4b8dc..d1ae4c3ec 100644 --- a/AI/VCAI/VCAI.cpp +++ b/AI/VCAI/VCAI.cpp @@ -2673,19 +2673,24 @@ void VCAI::finish() void VCAI::requestActionASAP(std::function whatToDo) { -// static boost::mutex m; -// boost::unique_lock mylock(m); + boost::mutex mutex; + mutex.lock(); - boost::barrier b(2); - boost::thread newThread([&b,this,whatToDo]() + boost::thread newThread([&mutex,this,whatToDo]() { setThreadName("VCAI::requestActionASAP::helper"); SET_GLOBAL_STATE(this); boost::shared_lock gsLock(cb->getGsMutex()); - b.wait(); + // unlock mutex and allow parent function to exit + mutex.unlock(); whatToDo(); }); - b.wait(); + + // wait for mutex to unlock and for thread to initialize properly + mutex.lock(); + + // unlock mutex - boost dislikes destruction of locked mutexes + mutex.unlock(); } void VCAI::lostHero(HeroPtr h) From 3800bd45b7f03396412ccfdff88d677efbdc8270 Mon Sep 17 00:00:00 2001 From: ArseniyShestakov Date: Fri, 4 Dec 2015 01:54:25 +0300 Subject: [PATCH 155/168] Movement: initialize destinationTeleportPos with invalid int3 position --- AI/VCAI/VCAI.cpp | 8 ++++---- AI/VCAI/VCAI.h | 2 +- client/CPlayerInterface.cpp | 4 ++-- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/AI/VCAI/VCAI.cpp b/AI/VCAI/VCAI.cpp index cf99d0697..bd0b195b4 100644 --- a/AI/VCAI/VCAI.cpp +++ b/AI/VCAI/VCAI.cpp @@ -96,7 +96,7 @@ VCAI::VCAI(void) LOG_TRACE(logAi); makingTurn = nullptr; destinationTeleport = ObjectInstanceID(); - destinationTeleportPos = int3(); + destinationTeleportPos = int3(-1); } VCAI::~VCAI(void) @@ -1881,7 +1881,7 @@ bool VCAI::moveHeroToTile(int3 dst, HeroPtr h) destinationTeleportPos = CGHeroInstance::convertPosition(exitPos, true); cb->moveHero(*h, h->pos); destinationTeleport = ObjectInstanceID(); - destinationTeleportPos = int3(); + destinationTeleportPos = int3(-1); afterMovementCheck(); }; @@ -1893,7 +1893,7 @@ bool VCAI::moveHeroToTile(int3 dst, HeroPtr h) status.setChannelProbing(true); for(auto exit : teleportChannelProbingList) - doTeleportMovement(exit, int3()); + doTeleportMovement(exit, int3(-1)); teleportChannelProbingList.clear(); doTeleportMovement(currentExit->id, currentExitPos); status.setChannelProbing(false); @@ -2931,7 +2931,7 @@ void AIStatus::setMove(bool ongoing) void AIStatus::setChannelProbing(bool ongoing) { boost::unique_lock lock(mx); - ongoingHeroMovement = ongoing; + ongoingChannelProbing = ongoing; cv.notify_all(); } diff --git a/AI/VCAI/VCAI.h b/AI/VCAI/VCAI.h index aa5eb521b..25eb312cd 100644 --- a/AI/VCAI/VCAI.h +++ b/AI/VCAI/VCAI.h @@ -367,7 +367,7 @@ public: } else if(!h.saving) { - destinationTeleportPos = int3(); + destinationTeleportPos = int3(-1); } h & townVisitsThisWeek & lockedHeroes & reservedHeroesMap; //FIXME: cannot instantiate abstract class h & visitableObjs & alreadyVisited & reservedObjs; diff --git a/client/CPlayerInterface.cpp b/client/CPlayerInterface.cpp index 6132d8035..089d94b88 100644 --- a/client/CPlayerInterface.cpp +++ b/client/CPlayerInterface.cpp @@ -97,7 +97,7 @@ CPlayerInterface::CPlayerInterface(PlayerColor Player) { logGlobal->traceStream() << "\tHuman player interface for player " << Player << " being constructed"; destinationTeleport = ObjectInstanceID(); - destinationTeleportPos = int3(); + destinationTeleportPos = int3(-1); observerInDuelMode = false; howManyPeople++; GH.defActionsDef = 0; @@ -1416,7 +1416,7 @@ void CPlayerInterface::requestRealized( PackageApplied *pa ) && stillMoveHero.get() == DURING_MOVE) { // After teleportation via CGTeleport object is finished destinationTeleport = ObjectInstanceID(); - destinationTeleportPos = int3(); + destinationTeleportPos = int3(-1); stillMoveHero.setn(CONTINUE_MOVE); } } From faa7cbff187118579f2856d5c42325d7ed1c4e9b Mon Sep 17 00:00:00 2001 From: Ivan Savenko Date: Fri, 4 Dec 2015 01:06:02 +0200 Subject: [PATCH 156/168] Fixed gcc/clang warnings --- AI/VCAI/AIUtility.cpp | 4 ++-- AI/VCAI/Goals.cpp | 4 ++++ AI/VCAI/VCAI.cpp | 16 +++++++++++++++- AI/VCAI/VCAI.h | 14 -------------- launcher/modManager/cmodlistmodel_moc.h | 2 +- lib/CModHandler.cpp | 2 +- lib/int3.h | 11 +++++++---- lib/mapping/CCampaignHandler.cpp | 4 ++-- lib/mapping/CMapService.cpp | 8 ++++---- lib/rmg/CMapGenerator.cpp | 2 +- lib/rmg/CRmgTemplateZone.cpp | 3 ++- lib/spells/CSpellHandler.cpp | 2 +- server/CGameHandler.cpp | 2 +- 13 files changed, 41 insertions(+), 33 deletions(-) diff --git a/AI/VCAI/AIUtility.cpp b/AI/VCAI/AIUtility.cpp index 049d1d9c5..7cc22fe38 100644 --- a/AI/VCAI/AIUtility.cpp +++ b/AI/VCAI/AIUtility.cpp @@ -154,7 +154,7 @@ void foreach_neighbour(const int3 &pos, std::function foo { CCallback * cbp = cb.get(); // avoid costly retrieval of thread-specific pointer - for(const int3 &dir : dirs) + for(const int3 &dir : int3::getDirs()) { const int3 n = pos + dir; if(cbp->isInTheMap(n)) @@ -164,7 +164,7 @@ void foreach_neighbour(const int3 &pos, std::function foo void foreach_neighbour(CCallback * cbp, const int3 &pos, std::function foo) { - for(const int3 &dir : dirs) + for(const int3 &dir : int3::getDirs()) { const int3 n = pos + dir; if(cbp->isInTheMap(n)) diff --git a/AI/VCAI/Goals.cpp b/AI/VCAI/Goals.cpp index 9d4597940..40406f214 100644 --- a/AI/VCAI/Goals.cpp +++ b/AI/VCAI/Goals.cpp @@ -1028,6 +1028,10 @@ TSubgoal GatherArmy::whatToDoToAchieve() return fh->chooseSolution (getAllPossibleSubgoals()); //find dwelling. use current hero to prevent him from doing nothing. } + +static const BuildingID unitsSource[] = { BuildingID::DWELL_LVL_1, BuildingID::DWELL_LVL_2, BuildingID::DWELL_LVL_3, + BuildingID::DWELL_LVL_4, BuildingID::DWELL_LVL_5, BuildingID::DWELL_LVL_6, BuildingID::DWELL_LVL_7}; + TGoalVec GatherArmy::getAllPossibleSubgoals() { //get all possible towns, heroes and dwellings we may use diff --git a/AI/VCAI/VCAI.cpp b/AI/VCAI/VCAI.cpp index d1ae4c3ec..d45a3e3c2 100644 --- a/AI/VCAI/VCAI.cpp +++ b/AI/VCAI/VCAI.cpp @@ -1303,6 +1303,20 @@ bool VCAI::tryBuildNextStructure(const CGTownInstance * t, std::vectorconvertPosition(h->pos, false); std::map dstToRevealedTiles; - for(crint3 dir : dirs) + for(crint3 dir : int3::getDirs()) if(cb->isInTheMap(hpos+dir)) if (ourPos != dir) //don't stand in place if (isSafeToVisit(h, hpos + dir) && isAccessibleForHero (hpos + dir, h)) diff --git a/AI/VCAI/VCAI.h b/AI/VCAI/VCAI.h index 4da33bc71..bb3e5b7d5 100644 --- a/AI/VCAI/VCAI.h +++ b/AI/VCAI/VCAI.h @@ -113,20 +113,6 @@ struct SectorMap int3 findFirstVisitableTile(HeroPtr h, crint3 dst); }; -//Set of buildings for different goals. Does not include any prerequisites. -const BuildingID essential[] = {BuildingID::TAVERN, BuildingID::TOWN_HALL}; -const BuildingID goldSource[] = {BuildingID::TOWN_HALL, BuildingID::CITY_HALL, BuildingID::CAPITOL}; -const BuildingID unitsSource[] = { BuildingID::DWELL_LVL_1, BuildingID::DWELL_LVL_2, BuildingID::DWELL_LVL_3, - BuildingID::DWELL_LVL_4, BuildingID::DWELL_LVL_5, BuildingID::DWELL_LVL_6, BuildingID::DWELL_LVL_7}; -const BuildingID unitsUpgrade[] = { BuildingID::DWELL_LVL_1_UP, BuildingID::DWELL_LVL_2_UP, BuildingID::DWELL_LVL_3_UP, - BuildingID::DWELL_LVL_4_UP, BuildingID::DWELL_LVL_5_UP, BuildingID::DWELL_LVL_6_UP, BuildingID::DWELL_LVL_7_UP}; -const BuildingID unitGrowth[] = { BuildingID::FORT, BuildingID::CITADEL, BuildingID::CASTLE, BuildingID::HORDE_1, - BuildingID::HORDE_1_UPGR, BuildingID::HORDE_2, BuildingID::HORDE_2_UPGR}; -const BuildingID spells[] = {BuildingID::MAGES_GUILD_1, BuildingID::MAGES_GUILD_2, BuildingID::MAGES_GUILD_3, - BuildingID::MAGES_GUILD_4, BuildingID::MAGES_GUILD_5}; -const BuildingID extra[] = {BuildingID::RESOURCE_SILO, BuildingID::SPECIAL_1, BuildingID::SPECIAL_2, BuildingID::SPECIAL_3, - BuildingID::SPECIAL_4, BuildingID::SHIPYARD}; // all remaining buildings - class VCAI : public CAdventureAI { public: diff --git a/launcher/modManager/cmodlistmodel_moc.h b/launcher/modManager/cmodlistmodel_moc.h index 72430db69..dce474ded 100644 --- a/launcher/modManager/cmodlistmodel_moc.h +++ b/launcher/modManager/cmodlistmodel_moc.h @@ -52,7 +52,7 @@ public: /// CModListContainer overrides void resetRepositories() override; void addRepository(QVariantMap data) override; - void modChanged(QString modID); + void modChanged(QString modID) override; QVariant data(const QModelIndex &index, int role) const override; QVariant headerData(int section, Qt::Orientation orientation, int role) const override; diff --git a/lib/CModHandler.cpp b/lib/CModHandler.cpp index 31a953292..571d5c10b 100644 --- a/lib/CModHandler.cpp +++ b/lib/CModHandler.cpp @@ -467,7 +467,7 @@ static JsonNode loadModSettings(std::string path) JsonNode addMeta(JsonNode config, std::string meta) { config.setMeta(meta); - return std::move(config); + return config; } CModInfo::CModInfo(std::string identifier,const JsonNode & local, const JsonNode & config): diff --git a/lib/int3.h b/lib/int3.h index 7ac595183..2631b4929 100644 --- a/lib/int3.h +++ b/lib/int3.h @@ -131,6 +131,12 @@ public: { h & x & y & z; } + + static std::array getDirs() + { + return { { 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) } }; + } }; inline std::ostream & operator<<(std::ostream & str, const int3 & sth) @@ -154,9 +160,6 @@ struct ShashInt3 } }; -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) }; - template int3 findClosestTile (Container & container, int3 dest) { @@ -175,4 +178,4 @@ int3 findClosestTile (Container & container, int3 dest) } } return result; -} \ No newline at end of file +} diff --git a/lib/mapping/CCampaignHandler.cpp b/lib/mapping/CCampaignHandler.cpp index 9935ed153..93f40e524 100644 --- a/lib/mapping/CCampaignHandler.cpp +++ b/lib/mapping/CCampaignHandler.cpp @@ -292,7 +292,7 @@ CScenarioTravel CCampaignHandler::readScenarioTravelFromMemory(CBinaryReader & r std::vector< std::vector > CCampaignHandler::getFile(const std::string & name, bool headerOnly) { - CCompressedStream stream(std::move(CResourceHandler::get()->load(ResourceID(name, EResType::CAMPAIGN))), true); + CCompressedStream stream(CResourceHandler::get()->load(ResourceID(name, EResType::CAMPAIGN)), true); std::vector< std::vector > ret; do @@ -363,7 +363,7 @@ std::vector CCampaignScenario::getLostCrossoverHeroes() const } } } - return std::move(lostCrossoverHeroes); + return lostCrossoverHeroes; } bool CScenarioTravel::STravelBonus::isBonusForHero() const diff --git a/lib/mapping/CMapService.cpp b/lib/mapping/CMapService.cpp index 4810ac492..fc4efb2e3 100644 --- a/lib/mapping/CMapService.cpp +++ b/lib/mapping/CMapService.cpp @@ -20,7 +20,7 @@ std::unique_ptr CMapService::loadMap(const std::string & name) getMapPatcher(name)->patchMapHeader(header); header.release(); - return std::move(map); + return map; } std::unique_ptr CMapService::loadMapHeader(const std::string & name) @@ -28,7 +28,7 @@ std::unique_ptr CMapService::loadMapHeader(const std::string & name) auto stream = getStreamFromFS(name); std::unique_ptr header = getMapLoader(stream)->loadMapHeader(); getMapPatcher(name)->patchMapHeader(header); - return std::move(header); + return header; } std::unique_ptr CMapService::loadMap(const ui8 * buffer, int size, const std::string & name) @@ -40,7 +40,7 @@ std::unique_ptr CMapService::loadMap(const ui8 * buffer, int size, const s getMapPatcher(name)->patchMapHeader(header); header.release(); - return std::move(map); + return map; } std::unique_ptr CMapService::loadMapHeader(const ui8 * buffer, int size, const std::string & name) @@ -48,7 +48,7 @@ std::unique_ptr CMapService::loadMapHeader(const ui8 * buffer, int s auto stream = getStreamFromMem(buffer, size); std::unique_ptr header = getMapLoader(stream)->loadMapHeader(); getMapPatcher(name)->patchMapHeader(header); - return std::move(header); + return header; } std::unique_ptr CMapService::getStreamFromFS(const std::string & name) diff --git a/lib/rmg/CMapGenerator.cpp b/lib/rmg/CMapGenerator.cpp index 48621d32f..0c7f6c963 100644 --- a/lib/rmg/CMapGenerator.cpp +++ b/lib/rmg/CMapGenerator.cpp @@ -17,7 +17,7 @@ static const int3 dirs4[] = {int3(0,1,0),int3(0,-1,0),int3(-1,0,0),int3(+1,0,0)} void CMapGenerator::foreach_neighbour(const int3 &pos, std::function foo) { - for(const int3 &dir : dirs) + for(const int3 &dir : int3::getDirs()) { int3 n = pos + dir; if(map->isInTheMap(n)) diff --git a/lib/rmg/CRmgTemplateZone.cpp b/lib/rmg/CRmgTemplateZone.cpp index 3db773d8b..9da6366be 100644 --- a/lib/rmg/CRmgTemplateZone.cpp +++ b/lib/rmg/CRmgTemplateZone.cpp @@ -2336,7 +2336,8 @@ ObjectInfo CRmgTemplateZone::getRandomObject(CMapGenerator* gen, CTreasurePileIn } assert (0); //we should never be here } - //FIXME: control reaches end of non-void function. Missing return? + + return ObjectInfo(); // unreachable } void CRmgTemplateZone::addAllPossibleObjects(CMapGenerator* gen) diff --git a/lib/spells/CSpellHandler.cpp b/lib/spells/CSpellHandler.cpp index 4b4a220ed..1beaac6e3 100644 --- a/lib/spells/CSpellHandler.cpp +++ b/lib/spells/CSpellHandler.cpp @@ -640,7 +640,7 @@ std::string CSpell::AnimationInfo::selectProjectile(const double angle) const } } - return std::move(res); + return res; } ///CSpell::TargetInfo diff --git a/server/CGameHandler.cpp b/server/CGameHandler.cpp index 9c3b8ba5d..ffedf5af3 100644 --- a/server/CGameHandler.cpp +++ b/server/CGameHandler.cpp @@ -1665,7 +1665,7 @@ std::list CGameHandler::generatePlayerTurnOrder() const if(!player.second.human) playerTurnOrder.push_back(player.first); } - return std::move(playerTurnOrder); + return playerTurnOrder; } void CGameHandler::setupBattle( int3 tile, const CArmedInstance *armies[2], const CGHeroInstance *heroes[2], bool creatureBank, const CGTownInstance *town ) From 791d1e7ab479b0edb5ed81acbfb6b2e53521a41d Mon Sep 17 00:00:00 2001 From: ArseniyShestakov Date: Fri, 4 Dec 2015 05:30:43 +0300 Subject: [PATCH 157/168] VCAI: finish fixing of teleport probing for whirlpools --- AI/VCAI/VCAI.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/AI/VCAI/VCAI.cpp b/AI/VCAI/VCAI.cpp index bd0b195b4..e94b5e49a 100644 --- a/AI/VCAI/VCAI.cpp +++ b/AI/VCAI/VCAI.cpp @@ -1887,16 +1887,16 @@ bool VCAI::moveHeroToTile(int3 dst, HeroPtr h) auto doChannelProbing = [&]() -> void { - auto currentExit = getObj(CGHeroInstance::convertPosition(h->pos,false), false); - auto currentExitPos = CGHeroInstance::convertPosition(h->pos,false); - assert(currentExit); + auto currentPos = CGHeroInstance::convertPosition(h->pos,false); + auto currentExit = getObj(currentPos, true)->id; status.setChannelProbing(true); for(auto exit : teleportChannelProbingList) doTeleportMovement(exit, int3(-1)); teleportChannelProbingList.clear(); - doTeleportMovement(currentExit->id, currentExitPos); status.setChannelProbing(false); + + doTeleportMovement(currentExit, currentPos); }; int i=path.nodes.size()-1; From eced16945e41c9dd4ebd0be4ea1f323e5f96ea0a Mon Sep 17 00:00:00 2001 From: ArseniyShestakov Date: Fri, 4 Dec 2015 05:43:55 +0300 Subject: [PATCH 158/168] getVisibleTeleportObjects: use getObj as not verbose We checking visibility in this function so there no need to send information about that into errorStream. --- lib/CGameInfoCallback.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/CGameInfoCallback.cpp b/lib/CGameInfoCallback.cpp index 56401514b..9d61db81f 100644 --- a/lib/CGameInfoCallback.cpp +++ b/lib/CGameInfoCallback.cpp @@ -853,7 +853,7 @@ std::vector CGameInfoCallback::getVisibleTeleportObjects(std:: { vstd::erase_if(ids, [&](ObjectInstanceID id) -> bool { - auto obj = getObj(id); + auto obj = getObj(id, false); return player != PlayerColor::UNFLAGGABLE && (!obj || !isVisible(obj->pos, player)); }); return ids; From 3b3c49420f85a9cf70c81237f98192469ba26286 Mon Sep 17 00:00:00 2001 From: ArseniyShestakov Date: Fri, 4 Dec 2015 17:37:19 +0300 Subject: [PATCH 159/168] Thieves Guild: correct number of taverns to access information --- lib/CGameState.cpp | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/lib/CGameState.cpp b/lib/CGameState.cpp index 846ee32c7..21865b270 100644 --- a/lib/CGameState.cpp +++ b/lib/CGameState.cpp @@ -2616,7 +2616,7 @@ void CGameState::obtainPlayersStats(SThievesGuildInfo & tgi, int level) continue; const CGHeroInstance * best = statsHLP::findBestHero(this, g->second.color); InfoAboutHero iah; - iah.initFromHero(best, level >= 8); + iah.initFromHero(best, level >= 2); iah.army.clear(); tgi.colorToBestHero[g->second.color] = iah; } @@ -2633,27 +2633,27 @@ void CGameState::obtainPlayersStats(SThievesGuildInfo & tgi, int level) { FILL_FIELD(mercSulfCrystGems, g->second.resources[Res::MERCURY] + g->second.resources[Res::SULFUR] + g->second.resources[Res::CRYSTAL] + g->second.resources[Res::GEMS]) } - if(level >= 4) //obelisks found + if(level >= 3) //obelisks found { FILL_FIELD(obelisks, CGObelisk::visited[gs->getPlayerTeam(g->second.color)->id]) } - if(level >= 5) //artifacts + if(level >= 4) //artifacts { FILL_FIELD(artifacts, statsHLP::getNumberOfArts(&g->second)) } - if(level >= 6) //army strength + if(level >= 4) //army strength { FILL_FIELD(army, statsHLP::getArmyStrength(&g->second)) } - if(level >= 7) //income + if(level >= 5) //income { FILL_FIELD(income, statsHLP::getIncome(&g->second)) } - if(level >= 8) //best hero's stats + if(level >= 2) //best hero's stats { //already set in lvl 1 handling } - if(level >= 9) //personality + if(level >= 3) //personality { for(auto g = players.cbegin(); g != players.cend(); ++g) { @@ -2670,7 +2670,7 @@ void CGameState::obtainPlayersStats(SThievesGuildInfo & tgi, int level) } } - if(level >= 10) //best creature + if(level >= 4) //best creature { //best creatures belonging to player (highest AI value) for(auto g = players.cbegin(); g != players.cend(); ++g) From 2a63ba148ae9cdaa660b64e6e87812893bd0fef4 Mon Sep 17 00:00:00 2001 From: ArseniyShestakov Date: Fri, 4 Dec 2015 18:38:57 +0300 Subject: [PATCH 160/168] Thieves Guild: fix tavern map object, lvl 0 when no taverns owned We also now check not number of towns, but only towns that has tavern built. Also according to original mechanics all taverns always display information based on your number of taverns and not number of taverns of object owner. --- client/windows/GUIClasses.cpp | 2 +- lib/CGameInfoCallback.cpp | 9 ++++++++- lib/CGameState.cpp | 6 ++++-- 3 files changed, 13 insertions(+), 4 deletions(-) diff --git a/client/windows/GUIClasses.cpp b/client/windows/GUIClasses.cpp index 3a52a6262..79456e908 100644 --- a/client/windows/GUIClasses.cpp +++ b/client/windows/GUIClasses.cpp @@ -1630,9 +1630,9 @@ CThievesGuildWindow::CThievesGuildWindow(const CGObjectInstance * _owner): int counter = 0; for(auto & iter : tgi.colorToBestHero) { + new CPicture(colorToBox[iter.first.getNum()], 253 + 66 * counter, 334); if(iter.second.portrait >= 0) { - new CPicture(colorToBox[iter.first.getNum()], 253 + 66 * counter, 334); new CAnimImage("PortraitsSmall", iter.second.portrait, 0, 260 + 66 * counter, 360); //TODO: r-click info: // - r-click on hero diff --git a/lib/CGameInfoCallback.cpp b/lib/CGameInfoCallback.cpp index d51e9ac65..818f79ca3 100644 --- a/lib/CGameInfoCallback.cpp +++ b/lib/CGameInfoCallback.cpp @@ -197,7 +197,14 @@ void CGameInfoCallback::getThievesGuildInfo(SThievesGuildInfo & thi, const CGObj if(obj->ID == Obj::TOWN || obj->ID == Obj::TAVERN) { - gs->obtainPlayersStats(thi, gs->players[obj->tempOwner].towns.size()); + int taverns = 0; + for(auto town : gs->players[*player].towns) + { + if(town->hasBuilt(BuildingID::TAVERN)) + taverns++; + } + + gs->obtainPlayersStats(thi, taverns); } else if(obj->ID == Obj::DEN_OF_THIEVES) { diff --git a/lib/CGameState.cpp b/lib/CGameState.cpp index 21865b270..ac3fccc0c 100644 --- a/lib/CGameState.cpp +++ b/lib/CGameState.cpp @@ -2603,13 +2603,15 @@ void CGameState::obtainPlayersStats(SThievesGuildInfo & tgi, int level) tgi.playerColors.push_back(elem.second.color); } - if(level >= 1) //num of towns & num of heroes + if(level >= 0) //num of towns & num of heroes { //num of towns FILL_FIELD(numOfTowns, g->second.towns.size()) //num of heroes FILL_FIELD(numOfHeroes, g->second.heroes.size()) - //best hero's portrait + } + if(level >= 1) //best hero's portrait + { for(auto g = players.cbegin(); g != players.cend(); ++g) { if(playerInactive(g->second.color)) From bdc369ffba4b5d69474510af4d4c2bed41cb2e02 Mon Sep 17 00:00:00 2001 From: ArseniyShestakov Date: Fri, 4 Dec 2015 21:08:09 +0300 Subject: [PATCH 161/168] Patrol: use manhattan distance for getting tiles in radius --- lib/CPathfinder.cpp | 2 +- lib/IGameCallback.cpp | 10 ++++++++-- lib/IGameCallback.h | 2 +- lib/int3.h | 7 ++++++- 4 files changed, 16 insertions(+), 5 deletions(-) diff --git a/lib/CPathfinder.cpp b/lib/CPathfinder.cpp index 8046f461d..7f77683e9 100644 --- a/lib/CPathfinder.cpp +++ b/lib/CPathfinder.cpp @@ -596,7 +596,7 @@ void CPathfinder::initializePatrol() if(hero->patrol.patrolRadious) { state = PATROL_RADIUS; - gs->getTilesInRange(patrolTiles, hero->patrol.initialPos, hero->patrol.patrolRadious); + gs->getTilesInRange(patrolTiles, hero->patrol.initialPos, hero->patrol.patrolRadious, hero->tempOwner, 0, true); } else state = PATROL_LOCKED; diff --git a/lib/IGameCallback.cpp b/lib/IGameCallback.cpp index 8f8f3b30c..cf335ba1b 100644 --- a/lib/IGameCallback.cpp +++ b/lib/IGameCallback.cpp @@ -45,7 +45,7 @@ void CPrivilagedInfoCallback::getFreeTiles (std::vector &tiles) const } } -void CPrivilagedInfoCallback::getTilesInRange( std::unordered_set &tiles, int3 pos, int radious, boost::optional player/*=uninit*/, int mode/*=0*/ ) const +void CPrivilagedInfoCallback::getTilesInRange( std::unordered_set &tiles, int3 pos, int radious, boost::optional player/*=uninit*/, int mode/*=0*/, bool patrolDistance/*=false*/) const { if(!!player && *player >= PlayerColor::PLAYER_LIMIT) { @@ -61,7 +61,13 @@ void CPrivilagedInfoCallback::getTilesInRange( std::unordered_set(pos.y - radious, 0); yd <= std::min(pos.y + radious, gs->map->height - 1); yd++) { - double distance = pos.dist2d(int3(xd,yd,pos.z)) - 0.5; + int3 tilePos(xd,yd,pos.z); + double distance; + if(patrolDistance) + distance = pos.mandist2d(tilePos); + else + distance = pos.dist2d(tilePos) - 0.5; + if(distance <= radious) { if(!player diff --git a/lib/IGameCallback.h b/lib/IGameCallback.h index b2ca42030..43c24d05c 100644 --- a/lib/IGameCallback.h +++ b/lib/IGameCallback.h @@ -30,7 +30,7 @@ class DLL_LINKAGE CPrivilagedInfoCallback : public CGameInfoCallback public: CGameState * gameState(); void getFreeTiles (std::vector &tiles) const; //used for random spawns - void getTilesInRange(std::unordered_set &tiles, int3 pos, int radious, boost::optional player = boost::optional(), int mode=0) const; //mode 1 - only unrevealed tiles; mode 0 - all, mode -1 - only unrevealed + void getTilesInRange(std::unordered_set &tiles, int3 pos, int radious, boost::optional player = boost::optional(), int mode = 0, bool patrolDistance = false) const; //mode 1 - only unrevealed tiles; mode 0 - all, mode -1 - only unrevealed void getAllTiles (std::unordered_set &tiles, boost::optional player = boost::optional(), int level=-1, int surface=0) const; //returns all tiles on given level (-1 - both levels, otherwise number of level); surface: 0 - land and water, 1 - only land, 2 - only water void pickAllowedArtsSet(std::vector &out); //gives 3 treasures, 3 minors, 1 major -> used by Black Market and Artifact Merchant void getAllowedSpells(std::vector &out, ui16 level); diff --git a/lib/int3.h b/lib/int3.h index 7ac595183..a1b40ce55 100644 --- a/lib/int3.h +++ b/lib/int3.h @@ -105,6 +105,11 @@ public: { return std::sqrt((double)dist2dSQ(o)); } + //manhattan distance used for patrol radius (z coord is not used) + double mandist2d(const int3 & o) const + { + return abs(o.x - x) + abs(o.y - y); + } bool areNeighbours(const int3 & o) const { @@ -175,4 +180,4 @@ int3 findClosestTile (Container & container, int3 dest) } } return result; -} \ No newline at end of file +} From 82c4c2f859dc8b8947fa7984e6ab634e5a590caf Mon Sep 17 00:00:00 2001 From: ArseniyShestakov Date: Fri, 4 Dec 2015 21:36:45 +0300 Subject: [PATCH 162/168] Patrol: update changelog --- ChangeLog | 1 + 1 file changed, 1 insertion(+) diff --git a/ChangeLog b/ChangeLog index 698c2b1e2..0dbdfb992 100644 --- a/ChangeLog +++ b/ChangeLog @@ -12,6 +12,7 @@ ADVETURE AI: * Fixed AI trying to go through underground rock * Fixed several cases causing AI wandering aimlessly * AI can again pick best artifacts and exchange artifacts between heroes +* AI heroes with patrol enabled won't leave patrol area anymore RANDOM MAP GENERATOR: * Changed fractalization algorithm so it can create cycles From d856fde73f3ff1ad863cb2b21787ff750cc7d8b7 Mon Sep 17 00:00:00 2001 From: ArseniyShestakov Date: Fri, 4 Dec 2015 22:58:14 +0300 Subject: [PATCH 163/168] Rumors: use enum instead of magic numbers and avoid potential crash --- lib/CGameInfoCallback.cpp | 10 +++++----- lib/CGameState.cpp | 35 +++++++++++++++++++---------------- lib/CGameState.h | 13 +++++++++++-- 3 files changed, 35 insertions(+), 23 deletions(-) diff --git a/lib/CGameInfoCallback.cpp b/lib/CGameInfoCallback.cpp index 818f79ca3..14062fa18 100644 --- a/lib/CGameInfoCallback.cpp +++ b/lib/CGameInfoCallback.cpp @@ -575,14 +575,14 @@ EPlayerStatus::EStatus CGameInfoCallback::getPlayerStatus(PlayerColor player, bo std::string CGameInfoCallback::getTavernRumor(const CGObjectInstance * townOrTavern) const { std::string text = "", extraText = ""; - if(gs->rumor.type == RumorState::RUMOR_NONE) // (version < 755 backward compatability + if(gs->rumor.type == RumorState::TYPE_NONE) // (version < 755 backward compatability return text; auto rumor = gs->rumor.last[gs->rumor.type]; switch(gs->rumor.type) { - case RumorState::RUMOR_STATS: - if(rumor.first == 212) + case RumorState::TYPE_SPECIAL: + if(rumor.first == RumorState::RUMOR_GRAIL) extraText = VLC->generaltexth->arraytxt[158 + rumor.second]; else extraText = VLC->generaltexth->capColors[rumor.second]; @@ -590,11 +590,11 @@ std::string CGameInfoCallback::getTavernRumor(const CGObjectInstance * townOrTav text = boost::str(boost::format(VLC->generaltexth->allTexts[rumor.first]) % extraText); break; - case RumorState::RUMOR_MAP: + case RumorState::TYPE_MAP: text = gs->map->rumors[rumor.first].text; break; - case RumorState::RUMOR_RAND: + case RumorState::TYPE_RAND: text = VLC->generaltexth->tavernRumors[rumor.first]; break; } diff --git a/lib/CGameState.cpp b/lib/CGameState.cpp index ac3fccc0c..bad05ba3a 100644 --- a/lib/CGameState.cpp +++ b/lib/CGameState.cpp @@ -2135,25 +2135,28 @@ int3 CGameState::guardingCreaturePosition (int3 pos) const void CGameState::updateRumor() { - static std::vector rumorTypes = {RumorState::RUMOR_MAP, RumorState::RUMOR_STATS, RumorState::RUMOR_RAND, RumorState::RUMOR_RAND}; - static std::vector statsRumorTypes = {208, 209, 210, 211, 212}; + static std::vector rumorTypes = {RumorState::TYPE_MAP, RumorState::TYPE_SPECIAL, RumorState::TYPE_RAND, RumorState::TYPE_RAND}; + std::vector sRumorTypes = { + RumorState::RUMOR_OBELISKS, RumorState::RUMOR_ARTIFACTS, RumorState::RUMOR_ARMY, RumorState::RUMOR_INCOME}; + if(map->grailPos.valid()) // Grail should always be on map, but I had related crash I didn't manage to reproduce + sRumorTypes.push_back(RumorState::RUMOR_GRAIL); int rumorId = -1, rumorExtra = -1; - auto & rand = gs->getRandomGenerator(); + auto & rand = getRandomGenerator(); rumor.type = *RandomGeneratorUtil::nextItem(rumorTypes, rand); - if(!gs->map->rumors.size() && rumor.type == RumorState::RUMOR_MAP) - rumor.type = RumorState::RUMOR_RAND; + if(!map->rumors.size() && rumor.type == RumorState::TYPE_MAP) + rumor.type = RumorState::TYPE_RAND; do { switch(rumor.type) { - case RumorState::RUMOR_STATS: + case RumorState::TYPE_SPECIAL: { SThievesGuildInfo tgi; - gs->obtainPlayersStats(tgi, 20); - rumorId = *RandomGeneratorUtil::nextItem(statsRumorTypes, rand); - if(rumorId == 212) + obtainPlayersStats(tgi, 20); + rumorId = *RandomGeneratorUtil::nextItem(sRumorTypes, rand); + if(rumorId == RumorState::RUMOR_GRAIL) { rumorExtra = getTile(map->grailPos)->terType; break; @@ -2162,19 +2165,19 @@ void CGameState::updateRumor() std::vector players = {}; switch(rumorId) { - case 208: + case RumorState::RUMOR_OBELISKS: players = tgi.obelisks[0]; break; - case 209: + case RumorState::RUMOR_ARTIFACTS: players = tgi.artifacts[0]; break; - case 210: + case RumorState::RUMOR_ARMY: players = tgi.army[0]; break; - case 211: + case RumorState::RUMOR_INCOME: players = tgi.income[0]; break; } @@ -2182,12 +2185,12 @@ void CGameState::updateRumor() break; } - case RumorState::RUMOR_MAP: - rumorId = rand.nextInt(gs->map->rumors.size() - 1); + case RumorState::TYPE_MAP: + rumorId = rand.nextInt(map->rumors.size() - 1); break; - case RumorState::RUMOR_RAND: + case RumorState::TYPE_RAND: do { rumorId = rand.nextInt(VLC->generaltexth->tavernRumors.size() - 1); diff --git a/lib/CGameState.h b/lib/CGameState.h index cab3f572b..76c36363a 100644 --- a/lib/CGameState.h +++ b/lib/CGameState.h @@ -212,13 +212,22 @@ struct DLL_LINKAGE RumorState { enum ERumorType : ui8 { - RUMOR_NONE = 0, RUMOR_RAND, RUMOR_STATS, RUMOR_MAP + TYPE_NONE = 0, TYPE_RAND, TYPE_SPECIAL, TYPE_MAP + }; + + enum ERumorTypeSpecial : ui8 + { + RUMOR_OBELISKS = 208, + RUMOR_ARTIFACTS = 209, + RUMOR_ARMY = 210, + RUMOR_INCOME = 211, + RUMOR_GRAIL = 212 }; ERumorType type; std::map> last; - RumorState(){type = RUMOR_NONE; last = {};}; + RumorState(){type = TYPE_NONE; last = {};}; bool update(int id, int extra); template void serialize(Handler &h, const int version) From 01c5bc25e519209991fb3b49967d5a1af88848d5 Mon Sep 17 00:00:00 2001 From: ArseniyShestakov Date: Sat, 5 Dec 2015 01:14:03 +0300 Subject: [PATCH 164/168] Move TTeleportExitsList to CObjectHandler.h Not a perfect solution, but this is the best location for now. --- lib/CGameInterface.h | 2 +- lib/mapObjects/CObjectHandler.h | 4 ++++ lib/mapObjects/MiscObjects.h | 2 -- 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/lib/CGameInterface.h b/lib/CGameInterface.h index b120d95d7..a49b5894f 100644 --- a/lib/CGameInterface.h +++ b/lib/CGameInterface.h @@ -7,7 +7,7 @@ #include "spells/ViewSpellInt.h" -#include "mapObjects/MiscObjects.h" +#include "mapObjects/CObjectHandler.h" /* * CGameInterface.h, part of VCMI engine diff --git a/lib/mapObjects/CObjectHandler.h b/lib/mapObjects/CObjectHandler.h index adc71b3d1..b159482c8 100644 --- a/lib/mapObjects/CObjectHandler.h +++ b/lib/mapObjects/CObjectHandler.h @@ -22,6 +22,10 @@ class CGObjectInstance; struct MetaString; struct BattleResult; +// This one teleport-specific, but has to be available everywhere in callbacks and netpacks +// For now it's will be there till teleports code refactored and moved into own file +typedef std::vector> TTeleportExitsList; + class DLL_LINKAGE IObjectInterface { public: diff --git a/lib/mapObjects/MiscObjects.h b/lib/mapObjects/MiscObjects.h index 161fe38e7..6ae8650de 100644 --- a/lib/mapObjects/MiscObjects.h +++ b/lib/mapObjects/MiscObjects.h @@ -247,8 +247,6 @@ public: ui32 defaultResProduction(); }; -typedef std::vector> TTeleportExitsList; - struct DLL_LINKAGE TeleportChannel { enum EPassability {UNKNOWN, IMPASSABLE, PASSABLE}; From 82ce58eceb0ae9f9104622cfbb34980adade84e8 Mon Sep 17 00:00:00 2001 From: ArseniyShestakov Date: Sat, 5 Dec 2015 01:45:10 +0300 Subject: [PATCH 165/168] CPathfinder::initializePatrol: don't pass valid PlayerColor It's break getTilesInRange with mode 0 and we don't really need to check visibility anyway. --- lib/CPathfinder.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/CPathfinder.cpp b/lib/CPathfinder.cpp index 7f77683e9..55ac78edb 100644 --- a/lib/CPathfinder.cpp +++ b/lib/CPathfinder.cpp @@ -596,7 +596,7 @@ void CPathfinder::initializePatrol() if(hero->patrol.patrolRadious) { state = PATROL_RADIUS; - gs->getTilesInRange(patrolTiles, hero->patrol.initialPos, hero->patrol.patrolRadious, hero->tempOwner, 0, true); + gs->getTilesInRange(patrolTiles, hero->patrol.initialPos, hero->patrol.patrolRadious, boost::optional(), 0, true); } else state = PATROL_LOCKED; From 8a3feb1e48dc0ac7a338ef83af978714bc0fe6d2 Mon Sep 17 00:00:00 2001 From: Ivan Savenko Date: Sat, 5 Dec 2015 01:39:14 +0200 Subject: [PATCH 166/168] Added assert to hopefully catch invalid GiveBonus package at some point --- lib/NetPacks.h | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/NetPacks.h b/lib/NetPacks.h index 7e54d1e2f..f4a2163ae 100644 --- a/lib/NetPacks.h +++ b/lib/NetPacks.h @@ -366,6 +366,7 @@ struct GiveBonus : public CPackForClient //115 template void serialize(Handler &h, const int version) { h & bonus & id & bdescr & who; + assert( id != -1); } }; From 89bf3592e3c4ed06dcfdf66d19e8a6f623e0e408 Mon Sep 17 00:00:00 2001 From: Ivan Savenko Date: Sat, 5 Dec 2015 01:40:23 +0200 Subject: [PATCH 167/168] Likely fixed duplicated random heroes --- lib/CGameState.cpp | 12 ++++++++---- lib/StartInfo.h | 16 ++++++++-------- 2 files changed, 16 insertions(+), 12 deletions(-) diff --git a/lib/CGameState.cpp b/lib/CGameState.cpp index 411628370..1bb5bb809 100644 --- a/lib/CGameState.cpp +++ b/lib/CGameState.cpp @@ -1383,7 +1383,8 @@ void CGameState::placeStartingHeroes() } int heroTypeId = pickNextHeroType(playerColor); - if(playerSettingPair.second.hero == -1) playerSettingPair.second.hero = heroTypeId; + if(playerSettingPair.second.hero == -1) + playerSettingPair.second.hero = heroTypeId; placeStartingHero(playerColor, HeroTypeID(heroTypeId), playerInfo.posOfMainTown); } @@ -2770,7 +2771,7 @@ CGHeroInstance * CGameState::getUsedHero(HeroTypeID hid) const { for(auto hero : map->heroesOnMap) //heroes instances initialization { - if(hero->subID == hid.getNum()) + if(hero->type && hero->type->ID == hid) { return hero; } @@ -2778,9 +2779,12 @@ CGHeroInstance * CGameState::getUsedHero(HeroTypeID hid) const for(auto obj : map->objects) //prisons { - if(obj && obj->ID == Obj::PRISON && obj->subID == hid.getNum()) + if(obj && obj->ID == Obj::PRISON ) { - return dynamic_cast(obj.get()); + auto hero = dynamic_cast(obj.get()); + assert(hero); + if ( hero->type && hero->type->ID == hid ) + return hero; } } diff --git a/lib/StartInfo.h b/lib/StartInfo.h index e254a1053..8d54faa70 100644 --- a/lib/StartInfo.h +++ b/lib/StartInfo.h @@ -31,7 +31,7 @@ struct PlayerSettings Ebonus bonus; si16 castle; si32 hero, - heroPortrait; //-1 if default, else ID + heroPortrait; //-1 if default, else ID std::string heroName; PlayerColor color; //from 0 - @@ -70,13 +70,13 @@ struct StartInfo { enum EMode {NEW_GAME, LOAD_GAME, CAMPAIGN, DUEL, INVALID = 255}; - EMode mode; - ui8 difficulty; //0=easy; 4=impossible - - typedef std::map TPlayerInfos; - TPlayerInfos playerInfos; //color indexed - - ui32 seedToBeUsed; //0 if not sure (client requests server to decide, will be send in reply pack) + EMode mode; + ui8 difficulty; //0=easy; 4=impossible + + typedef std::map TPlayerInfos; + TPlayerInfos playerInfos; //color indexed + + ui32 seedToBeUsed; //0 if not sure (client requests server to decide, will be send in reply pack) ui32 seedPostInit; //so we know that game is correctly synced at the start; 0 if not known yet ui32 mapfileChecksum; //0 if not relevant ui8 turnTime; //in minutes, 0=unlimited From 77088101483508577b0970a353923515f046dfd8 Mon Sep 17 00:00:00 2001 From: ArseniyShestakov Date: Sat, 5 Dec 2015 08:08:02 +0300 Subject: [PATCH 168/168] VCAI: don't serialize destinationTeleportPos to avoid crash This won't affect AI functionality except if game saved while AI moving through teleporters. Serialization for some reason don't work properly and cause save loading to fail. --- AI/VCAI/VCAI.h | 8 -------- 1 file changed, 8 deletions(-) diff --git a/AI/VCAI/VCAI.h b/AI/VCAI/VCAI.h index a208d0785..6963b8c79 100644 --- a/AI/VCAI/VCAI.h +++ b/AI/VCAI/VCAI.h @@ -345,14 +345,6 @@ public: template void serializeInternal(Handler &h, const int version) { h & knownTeleportChannels & knownSubterraneanGates & destinationTeleport; - if(version >= 755) - { - h & destinationTeleportPos; - } - else if(!h.saving) - { - destinationTeleportPos = int3(-1); - } h & townVisitsThisWeek & lockedHeroes & reservedHeroesMap; //FIXME: cannot instantiate abstract class h & visitableObjs & alreadyVisited & reservedObjs; h & saving & status & battlename;