diff --git a/AI/VCAI/AIUtility.cpp b/AI/VCAI/AIUtility.cpp index b7f8886ff..a19274394 100644 --- a/AI/VCAI/AIUtility.cpp +++ b/AI/VCAI/AIUtility.cpp @@ -135,6 +135,16 @@ void foreach_tile_pos(std::function foo) } +void foreach_tile_pos(CCallback * cbp, std::function foo) +{ + int3 mapSize = cbp->getMapSize(); + + for(int i = 0; i < mapSize.x; i++) + for(int j = 0; j < mapSize.y; j++) + for(int k = 0; k < mapSize.z; k++) + foo(cbp, int3(i,j,k)); +} + void foreach_neighbour(const int3 &pos, std::function foo) { CCallback * cbp = cb.get(); // avoid costly retrieval of thread-specific pointer @@ -147,6 +157,16 @@ 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) + { + const int3 n = pos + dir; + if(cbp->isInTheMap(n)) + foo(cbp, pos+dir); + } +} + std::string strFromInt3(int3 pos) { std::ostringstream oss; @@ -180,7 +200,7 @@ ui64 evaluateDanger(crint3 tile) if(visObjs.size()) objectDanger = evaluateDanger(visObjs.back()); - int3 guardPos = cb->guardingCreaturePosition(tile); + int3 guardPos = cb->getGuardingCreaturePosition(tile); if(guardPos.x >= 0 && guardPos != tile) guardDanger = evaluateDanger(guardPos); @@ -378,7 +398,7 @@ bool isBlockedBorderGate(int3 tileToHit) && cb->getPathInfo(tileToHit)->accessible != CGPathNode::ACCESSIBLE; } -int howManyTilesWillBeDiscovered(const int3 &pos, int radious) +int howManyTilesWillBeDiscovered(const int3 &pos, int radious, CCallback * cbp) { //TODO: do not explore dead-end boundaries int ret = 0; for(int x = pos.x - radious; x <= pos.x + radious; x++) @@ -386,9 +406,9 @@ int howManyTilesWillBeDiscovered(const int3 &pos, int radious) for(int y = pos.y - radious; y <= pos.y + radious; y++) { int3 npos = int3(x,y,pos.z); - if(cb->isInTheMap(npos) && pos.dist2d(npos) - 0.5 < radious && !cb->isVisible(npos)) + if(cbp->isInTheMap(npos) && pos.dist2d(npos) - 0.5 < radious && !cbp->isVisible(npos)) { - if (!boundaryBetweenTwoPoints (pos, npos)) + if (!boundaryBetweenTwoPoints (pos, npos, cbp)) ret++; } } @@ -397,7 +417,7 @@ int howManyTilesWillBeDiscovered(const int3 &pos, int radious) return ret; } -bool boundaryBetweenTwoPoints (int3 pos1, int3 pos2) //determines if two points are separated by known barrier +bool boundaryBetweenTwoPoints (int3 pos1, int3 pos2, CCallback * cbp) //determines if two points are separated by known barrier { int xMin = std::min (pos1.x, pos2.x); int xMax = std::max (pos1.x, pos2.x); @@ -411,7 +431,7 @@ bool boundaryBetweenTwoPoints (int3 pos1, int3 pos2) //determines if two points int3 tile = int3(x, y, pos1.z); //use only on same level, ofc if (abs(pos1.dist2d(tile) - pos2.dist2d(tile)) < 1.5) { - if (!(cb->isVisible(tile) && cb->getTile(tile)->blocked)) //if there's invisible or unblocked tile between, it's good + if (!(cbp->isVisible(tile) && cbp->getTile(tile)->blocked)) //if there's invisible or unblocked tile between, it's good return false; } } @@ -421,7 +441,7 @@ bool boundaryBetweenTwoPoints (int3 pos1, int3 pos2) //determines if two points int howManyTilesWillBeDiscovered(int radious, int3 pos, crint3 dir) { - return howManyTilesWillBeDiscovered(pos + dir, radious); + return howManyTilesWillBeDiscovered(pos + dir, radious, cb.get()); } void getVisibleNeighbours(const std::vector &tiles, std::vector &out) diff --git a/AI/VCAI/AIUtility.h b/AI/VCAI/AIUtility.h index a131b5f9d..7a9a29c9c 100644 --- a/AI/VCAI/AIUtility.h +++ b/AI/VCAI/AIUtility.h @@ -175,9 +175,11 @@ void removeDuplicates(std::vector &vec) std::string strFromInt3(int3 pos); void foreach_tile_pos(std::function foo); +void foreach_tile_pos(CCallback * cbp, std::function foo); // avoid costly retrieval of thread-specific pointer void foreach_neighbour(const int3 &pos, std::function foo); +void foreach_neighbour(CCallback * cbp, const int3 &pos, std::function foo); // avoid costly retrieval of thread-specific pointer -int howManyTilesWillBeDiscovered(const int3 &pos, int radious); +int howManyTilesWillBeDiscovered(const int3 &pos, int radious, CCallback * cbp); int howManyTilesWillBeDiscovered(int radious, int3 pos, crint3 dir); void getVisibleNeighbours(const std::vector &tiles, std::vector &out); @@ -192,7 +194,7 @@ bool shouldVisit (HeroPtr h, const CGObjectInstance * obj); ui64 evaluateDanger(const CGObjectInstance *obj); ui64 evaluateDanger(crint3 tile, const CGHeroInstance *visitor); bool isSafeToVisit(HeroPtr h, crint3 tile); -bool boundaryBetweenTwoPoints (int3 pos1, int3 pos2); +bool boundaryBetweenTwoPoints (int3 pos1, int3 pos2, CCallback * cbp); bool compareMovement(HeroPtr lhs, HeroPtr rhs); bool compareHeroStrength(HeroPtr h1, HeroPtr h2); diff --git a/AI/VCAI/VCAI.cpp b/AI/VCAI/VCAI.cpp index 7a089d312..2618a8dd1 100644 --- a/AI/VCAI/VCAI.cpp +++ b/AI/VCAI/VCAI.cpp @@ -2245,9 +2245,11 @@ int3 VCAI::explorationNewPoint(HeroPtr h) std::vector > tiles; //tiles[distance_to_fow] tiles.resize(radius); + CCallback * cbp = cb.get(); + foreach_tile_pos([&](const int3 &pos) { - if(!cb->isVisible(pos)) + if(!cbp->isVisible(pos)) tiles[0].push_back(pos); }); @@ -2261,14 +2263,14 @@ int3 VCAI::explorationNewPoint(HeroPtr h) for(const int3 &tile : tiles[i]) { - if (cb->getTile(tile)->blocked) //does it shorten the time? + if (cbp->getTile(tile)->blocked) //does it shorten the time? continue; - if (!cb->getPathInfo(tile)->reachable()) //this will remove tiles that are guarded by monsters (or removable objects) + if (!cbp->getPathInfo(tile)->reachable()) //this will remove tiles that are guarded by monsters (or removable objects) continue; CGPath path; - cb->getPath2(tile, path); - float ourValue = (float)howManyTilesWillBeDiscovered(tile, radius) / (path.nodes.size() + 1); //+1 prevents erratic jumps + cbp->getPath2(tile, path); + 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 { @@ -2292,9 +2294,11 @@ int3 VCAI::explorationDesperate(HeroPtr h) std::vector > tiles; //tiles[distance_to_fow] tiles.resize(radius); + CCallback * cbp = cb.get(); + foreach_tile_pos([&](const int3 &pos) { - if(!cb->isVisible(pos)) + if(!cbp->isVisible(pos)) tiles[0].push_back(pos); }); @@ -2308,9 +2312,9 @@ int3 VCAI::explorationDesperate(HeroPtr h) for(const int3 &tile : tiles[i]) { - if (cb->getTile(tile)->blocked) //does it shorten the time? + if (cbp->getTile(tile)->blocked) //does it shorten the time? continue; - if (!howManyTilesWillBeDiscovered(tile, radius)) //avoid costly checks of tiles that don't reveal much + if (!howManyTilesWillBeDiscovered(tile, radius, cbp)) //avoid costly checks of tiles that don't reveal much continue; auto t = sm.firstTileToGet(h, tile); @@ -2682,12 +2686,14 @@ void SectorMap::update() { clear(); int curSector = 3; //0 is invisible, 1 is not explored + + CCallback * cbp = cb.get(); //optimization foreach_tile_pos([&](crint3 pos) { if(retreiveTile(pos) == NOT_CHECKED) { if(!markIfBlocked(retreiveTile(pos), pos)) - exploreNewSector(pos, curSector++); + exploreNewSector(pos, curSector++, cbp); } }); valid = true; @@ -2699,11 +2705,11 @@ void SectorMap::clear() valid = false; } -void SectorMap::exploreNewSector(crint3 pos, int num) +void SectorMap::exploreNewSector(crint3 pos, int num, CCallback * cbp) { Sector &s = infoOnSectors[num]; s.id = num; - s.water = cb->getTile(pos)->isWater(); + s.water = cbp->getTile(pos)->isWater(); std::queue toVisit; toVisit.push(pos); @@ -2714,21 +2720,21 @@ void SectorMap::exploreNewSector(crint3 pos, int num) ui8 &sec = retreiveTile(curPos); if(sec == NOT_CHECKED) { - const TerrainTile *t = cb->getTile(curPos); + const TerrainTile *t = cbp->getTile(curPos); if(!markIfBlocked(sec, curPos, t)) { if(t->isWater() == s.water) //sector is only-water or only-land { sec = num; s.tiles.push_back(curPos); - foreach_neighbour(curPos, [&](crint3 neighPos) + foreach_neighbour(cbp, curPos, [&](CCallback * cbp, crint3 neighPos) { if(retreiveTile(neighPos) == NOT_CHECKED) { toVisit.push(neighPos); //parent[neighPos] = curPos; } - const TerrainTile *nt = cb->getTile(neighPos, false); + const TerrainTile *nt = cbp->getTile(neighPos, false); if(nt && nt->isWater() != s.water && canBeEmbarkmentPoint(nt)) { s.embarkmentPoints.push_back(neighPos); diff --git a/AI/VCAI/VCAI.h b/AI/VCAI/VCAI.h index 54bd62fbf..1faedb19d 100644 --- a/AI/VCAI/VCAI.h +++ b/AI/VCAI/VCAI.h @@ -102,7 +102,7 @@ struct SectorMap SectorMap(HeroPtr h); void update(); void clear(); - void exploreNewSector(crint3 pos, int num); + void exploreNewSector(crint3 pos, int num, CCallback * cbp); void write(crstring fname); unsigned char &retreiveTile(crint3 pos); diff --git a/CCallback.cpp b/CCallback.cpp index 59606d7df..fb5e807d2 100644 --- a/CCallback.cpp +++ b/CCallback.cpp @@ -356,6 +356,17 @@ int CCallback::getMovementCost(const CGHeroInstance * hero, int3 dest) return gs->getMovementCost(hero, hero->visitablePos(), dest, hero->hasBonusOfType (Bonus::FLYING_MOVEMENT), hero->movement); } +int3 CCallback::getGuardingCreaturePosition(int3 tile) +{ + if (!gs->map->isInTheMap(tile)) + return int3(-1,-1,-1); + + validatePaths(); + + boost::unique_lock pathLock(cl->pathMx); + return gs->map->guardingCreaturePositions[tile.x][tile.y][tile.z]; +} + void CCallback::recalculatePaths() { cl->calculatePaths(cl->IGameCallback::getSelectedHero(*player)); diff --git a/CCallback.h b/CCallback.h index 7d5a80e55..34475f6b6 100644 --- a/CCallback.h +++ b/CCallback.h @@ -112,6 +112,7 @@ public: virtual bool getPath2(int3 dest, CGPath &ret); //uses main, client pathfinder info virtual bool canMoveBetween(const int3 &a, const int3 &b); virtual int getMovementCost(const CGHeroInstance * hero, int3 dest); + virtual int3 getGuardingCreaturePosition(int3 tile); //uses main, client pathfinder info virtual void calculatePaths(const CGHeroInstance *hero, CPathsInfo &out, int3 src = int3(-1,-1,-1), int movement = -1); virtual void recalculatePaths(); //updates main, client pathfinder info (should be called when moving hero is over) diff --git a/client/CAdvmapInterface.cpp b/client/CAdvmapInterface.cpp index 2948416f2..eab31d8e1 100644 --- a/client/CAdvmapInterface.cpp +++ b/client/CAdvmapInterface.cpp @@ -1279,7 +1279,7 @@ void CAdvMapInt::tileHovered(const int3 &mapPos) } } - const bool guardingCreature = CGI->mh->map->isInTheMap(LOCPLINT->cb->guardingCreaturePosition(mapPos)); + const bool guardingCreature = CGI->mh->map->isInTheMap(LOCPLINT->cb->getGuardingCreaturePosition(mapPos)); if(selection->ID == Obj::TOWN) { diff --git a/client/CPlayerInterface.cpp b/client/CPlayerInterface.cpp index 904178e1d..789c49a1e 100644 --- a/client/CPlayerInterface.cpp +++ b/client/CPlayerInterface.cpp @@ -1344,7 +1344,7 @@ bool CPlayerInterface::moveHero( const CGHeroInstance *h, CGPath path ) stillMoveHero.data = WAITING_MOVE; int3 endpos(path.nodes[i-1].coord.x, path.nodes[i-1].coord.y, h->pos.z); - bool guarded = CGI->mh->map->isInTheMap(cb->guardingCreaturePosition(endpos - int3(1, 0, 0))); + bool guarded = CGI->mh->map->isInTheMap(cb->getGuardingCreaturePosition(endpos - int3(1, 0, 0))); logGlobal->traceStream() << "Requesting hero movement to " << endpos; cb->moveHero(h,endpos); diff --git a/lib/CGameState.cpp b/lib/CGameState.cpp index b2955900f..5c87ec1b6 100644 --- a/lib/CGameState.cpp +++ b/lib/CGameState.cpp @@ -2237,7 +2237,7 @@ std::vector CGameState::guardingCreatures (int3 pos) const { for (CGObjectInstance* obj : tile.visitableObjects) { - if (obj->ID == Obj::MONSTER && checkForVisitableDir(pos, &map->getTile(originalPos), originalPos)) // Monster being able to attack investigated tile + if (obj->ID == Obj::MONSTER && map->checkForVisitableDir(pos, &map->getTile(originalPos), originalPos)) // Monster being able to attack investigated tile { guards.push_back(obj); } @@ -2256,53 +2256,7 @@ std::vector CGameState::guardingCreatures (int3 pos) const int3 CGameState::guardingCreaturePosition (int3 pos) const { - const int3 originalPos = pos; - // Give monster at position priority. - if (!map->isInTheMap(pos)) - return int3(-1, -1, -1); - const TerrainTile &posTile = map->getTile(pos); - if (posTile.visitable) - { - for (CGObjectInstance* obj : posTile.visitableObjects) - { - if(obj->blockVisit) - { - if (obj->ID == Obj::MONSTER) // Monster - return pos; - else - return int3(-1, -1, -1); //blockvis objects are not guarded by neighbouring creatures - } - } - } - - // See if there are any monsters adjacent. - pos -= int3(1, 1, 0); // Start with top left. - for (int dx = 0; dx < 3; dx++) - { - for (int dy = 0; dy < 3; dy++) - { - if (map->isInTheMap(pos)) - { - const auto & tile = map->getTile(pos); - if (tile.visitable && (tile.isWater() == posTile.isWater())) - { - for (CGObjectInstance* obj : tile.visitableObjects) - { - if (obj->ID == Obj::MONSTER && checkForVisitableDir(pos, &posTile, originalPos)) // Monster being able to attack investigated tile - { - return pos; - } - } - } - } - - pos.y++; - } - pos.y -= 3; - pos.x++; - } - - return int3(-1, -1, -1); + return gs->map->guardingCreaturePositions[pos.x][pos.y][pos.z]; } bool CGameState::isVisible(int3 pos, PlayerColor player) @@ -2338,22 +2292,7 @@ bool CGameState::isVisible( const CGObjectInstance *obj, boost::optionalgetTile(dst); - return checkForVisitableDir(src, pom, dst); -} - -bool CGameState::checkForVisitableDir( const int3 & src, const TerrainTile *pom, const int3 & dst ) const -{ - for(ui32 b=0; bvisitableObjects.size(); ++b) //checking destination tile - { - if(!vstd::contains(pom->blockingObjects, pom->visitableObjects[b])) //this visitable object is not blocking, ignore - continue; - - const CGObjectInstance * obj = pom->visitableObjects[b]; - - if (!obj->appearance.isVisitableFrom(src.x - dst.x, src.y - dst.y)) - return false; - } - return true; + return map->checkForVisitableDir(src, pom, dst); } EVictoryLossCheckResult CGameState::checkForVictoryAndLoss(PlayerColor player) const @@ -3350,6 +3289,7 @@ void CPathfinder::initializeGraph() curPos = int3(i,j,k); const TerrainTile *tinfo = &gs->map->getTile(int3(i, j, k)); CGPathNode &node = graph[i][j][k]; + node.accessible = evaluateAccessibility(tinfo); node.turns = 0xff; node.moveRemains = 0; @@ -3405,7 +3345,7 @@ void CPathfinder::calculatePaths(int3 src /*= int3(-1,-1,-1)*/, int movement /*= cp = mq.front(); mq.pop_front(); - const int3 sourceGuardPosition = guardingCreaturePosition(cp->coord); + const int3 sourceGuardPosition = gs->map->guardingCreaturePositions[cp->coord.x][cp->coord.y][cp->coord.z]; bool guardedSource = (sourceGuardPosition != int3(-1, -1, -1) && cp->coord != src); ct = &gs->map->getTile(cp->coord); @@ -3498,7 +3438,7 @@ void CPathfinder::calculatePaths(int3 src /*= int3(-1,-1,-1)*/, int movement /*= dp->turns = turnAtNextTile; dp->theNodeBefore = cp; - const bool guardedDst = guardingCreaturePosition(dp->coord) != int3(-1, -1, -1) + const bool guardedDst = gs->map->guardingCreaturePositions[dp->coord.x][dp->coord.y][dp->coord.z].valid() && dp->accessible == CGPathNode::BLOCKVIS; if (dp->accessible == CGPathNode::ACCESSIBLE @@ -3558,7 +3498,7 @@ CGPathNode::EAccessibility CPathfinder::evaluateAccessibility(const TerrainTile } } } - else if (gs->map->isInTheMap(guardingCreaturePosition(curPos)) + else if (gs->map->guardingCreaturePositions[curPos.x][curPos.y][curPos.z].valid() && !tinfo->blocked) { // Monster close by; blocked visit for battle. diff --git a/lib/CGameState.h b/lib/CGameState.h index 699590065..d93156d9d 100644 --- a/lib/CGameState.h +++ b/lib/CGameState.h @@ -431,7 +431,6 @@ public: UpgradeInfo getUpgradeInfo(const CStackInstance &stack); PlayerRelations::PlayerRelations getPlayerRelations(PlayerColor color1, PlayerColor color2); bool checkForVisitableDir(const int3 & src, const int3 & dst) const; //check if src tile is visitable from dst tile - bool checkForVisitableDir(const int3 & src, const TerrainTile *pom, const int3 & dst) const; //check if src tile is visitable from dst tile void calculatePaths(const CGHeroInstance *hero, CPathsInfo &out, int3 src = int3(-1,-1,-1), int movement = -1); //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; diff --git a/lib/IGameCallback.cpp b/lib/IGameCallback.cpp index 97d25c516..cd47cc630 100644 --- a/lib/IGameCallback.cpp +++ b/lib/IGameCallback.cpp @@ -406,7 +406,7 @@ bool CGameInfoCallback::getTownInfo( const CGObjectInstance *town, InfoAboutTown return true; } -int3 CGameInfoCallback::guardingCreaturePosition (int3 pos) const +int3 CGameInfoCallback::guardingCreaturePosition (int3 pos) const //FIXME: redundant? { ERROR_RET_VAL_IF(!isVisible(pos), "Tile is not visible!", int3(-1,-1,-1)); return gs->guardingCreaturePosition(pos); diff --git a/lib/NetPacksLib.cpp b/lib/NetPacksLib.cpp index 7164e9a0a..7ab351079 100644 --- a/lib/NetPacksLib.cpp +++ b/lib/NetPacksLib.cpp @@ -380,6 +380,7 @@ DLL_LINKAGE void RemoveObject::applyGs( CGameState *gs ) } gs->map->objects[id.getNum()].dellNull(); + gs->map->calculateGuardingGreaturePositions(); } static int getDir(int3 src, int3 dst) @@ -613,6 +614,7 @@ DLL_LINKAGE void NewObject::applyGs( CGameState *gs ) gs->map->objects.push_back(o); gs->map->addBlockVisTiles(o); o->initObj(); + gs->map->calculateGuardingGreaturePositions(); logGlobal->debugStream() << "added object id=" << id << "; address=" << (intptr_t)o << "; name=" << o->getHoverText(); } diff --git a/lib/mapping/CMap.cpp b/lib/mapping/CMap.cpp index c61748911..c6b190fce 100644 --- a/lib/mapping/CMap.cpp +++ b/lib/mapping/CMap.cpp @@ -216,13 +216,18 @@ CMap::~CMap() { if(terrain) { - for(int ii=0;iivisitableObjects.size(); ++b) //checking destination tile + { + if(!vstd::contains(pom->blockingObjects, pom->visitableObjects[b])) //this visitable object is not blocking, ignore + continue; + + const CGObjectInstance * obj = pom->visitableObjects[b]; + + if (!obj->appearance.isVisitableFrom(src.x - dst.x, src.y - dst.y)) + return false; + } + return true; +} + +int3 CMap::guardingCreaturePosition (int3 pos) const +{ + + const int3 originalPos = pos; + // Give monster at position priority. + if (!isInTheMap(pos)) + return int3(-1, -1, -1); + const TerrainTile &posTile = getTile(pos); + if (posTile.visitable) + { + for (CGObjectInstance* obj : posTile.visitableObjects) + { + if(obj->blockVisit) + { + if (obj->ID == Obj::MONSTER) // Monster + return pos; + else + return int3(-1, -1, -1); //blockvis objects are not guarded by neighbouring creatures + } + } + } + + // See if there are any monsters adjacent. + bool water = posTile.isWater(); + + pos -= int3(1, 1, 0); // Start with top left. + for (int dx = 0; dx < 3; dx++) + { + for (int dy = 0; dy < 3; dy++) + { + if (isInTheMap(pos)) + { + const auto & tile = getTile(pos); + if (tile.visitable && (tile.isWater() == water)) + { + for (CGObjectInstance* obj : tile.visitableObjects) + { + if (obj->ID == Obj::MONSTER && checkForVisitableDir(pos, &posTile, originalPos)) // Monster being able to attack investigated tile + { + return pos; + } + } + } + } + + pos.y++; + } + pos.y -= 3; + pos.x++; + } + + return int3(-1, -1, -1); +} + const CGObjectInstance * CMap::getObjectiveObjectFrom(int3 pos, Obj::EObj type) { for (CGObjectInstance * object : getTile(pos).visitableObjects) @@ -432,13 +519,17 @@ void CMap::addQuest(CGObjectInstance * quest) void CMap::initTerrain() { + int level = twoLevel ? 2 : 1; terrain = new TerrainTile**[width]; - for(int i = 0; i < width; ++i) + guardingCreaturePositions = new int3**[width]; + for (int i = 0; i < width; ++i) { terrain[i] = new TerrainTile*[height]; - for(int j = 0; j < height; ++j) + guardingCreaturePositions[i] = new int3*[height]; + for (int j = 0; j < height; ++j) { - terrain[i][j] = new TerrainTile[twoLevel ? 2 : 1]; + terrain[i][j] = new TerrainTile[level]; + guardingCreaturePositions[i][j] = new int3[level]; } } } diff --git a/lib/mapping/CMap.h b/lib/mapping/CMap.h index eb2743c0f..f71c4df06 100644 --- a/lib/mapping/CMap.h +++ b/lib/mapping/CMap.h @@ -391,9 +391,12 @@ public: const TerrainTile & getTile(const int3 & tile) const; bool isInTheMap(const int3 & pos) const; bool isWaterTile(const int3 & pos) const; + bool checkForVisitableDir( const int3 & src, const TerrainTile *pom, const int3 & dst ) const; + int3 guardingCreaturePosition (int3 pos) const; void addBlockVisTiles(CGObjectInstance * obj); void removeBlockVisTiles(CGObjectInstance * obj, bool total = false); + void calculateGuardingGreaturePositions(); void addNewArtifactInstance(CArtifactInstance * art); void eraseArtifactInstance(CArtifactInstance * art); @@ -433,6 +436,8 @@ public: unique_ptr editManager; + int3 ***guardingCreaturePositions; + private: /// a 3-dimensional array of terrain tiles, access is as follows: x, y, level. where level=1 is underground TerrainTile*** terrain; @@ -447,6 +452,7 @@ public: h & questIdentifierToId; //TODO: viccondetails + int level = twoLevel ? 2 : 1; if(h.saving) { // Save terrain @@ -454,9 +460,10 @@ public: { for(int j = 0; j < height ; ++j) { - for(int k = 0; k < (twoLevel ? 2 : 1); ++k) + for(int k = 0; k < level; ++k) { h & terrain[i][j][k]; + h & guardingCreaturePositions[i][j][k]; } } } @@ -465,21 +472,25 @@ public: { // Load terrain terrain = new TerrainTile**[width]; - for(int ii = 0; ii < width; ++ii) + guardingCreaturePositions = new int3**[width]; + for(int i = 0; i < width; ++i) { - terrain[ii] = new TerrainTile*[height]; - for(int jj = 0; jj < height; ++jj) + terrain[i] = new TerrainTile*[height]; + guardingCreaturePositions[i] = new int3*[height]; + for(int j = 0; j < height; ++j) { - terrain[ii][jj] = new TerrainTile[twoLevel ? 2 : 1]; + terrain[i][j] = new TerrainTile[level]; + guardingCreaturePositions[i][j] = new int3[level]; } } for(int i = 0; i < width ; ++i) { for(int j = 0; j < height ; ++j) { - for(int k = 0; k < (twoLevel ? 2 : 1); ++k) + for(int k = 0; k < level; ++k) { h & terrain[i][j][k]; + h & guardingCreaturePositions[i][j][k]; } } } diff --git a/lib/mapping/MapFormatH3M.cpp b/lib/mapping/MapFormatH3M.cpp index fdf67e0ff..9fcbb30ee 100644 --- a/lib/mapping/MapFormatH3M.cpp +++ b/lib/mapping/MapFormatH3M.cpp @@ -134,6 +134,7 @@ void CMapLoaderH3M::init() logGlobal->debugStream() << "\tReading " << mlt.name << " took " << mlt.time << " ms."; } } + map->calculateGuardingGreaturePositions(); } void CMapLoaderH3M::readHeader()