From 7ba271edf12bfab416ba39e22776b9e687b0844c Mon Sep 17 00:00:00 2001 From: DjWarmonger Date: Sun, 18 Sep 2022 16:39:10 +0200 Subject: [PATCH] Rotation rebase2 (#912) * Instead of [x][y][z] coordinates, map will be stored as [z][x][y]. * Nullkiller AI can get it too. * Use boost::multi_array instead of nested vectors * In MapHandler too * Rotate foreach algorithms, too * VCAI gets rotated, too --- AI/Nullkiller/AIUtility.h | 36 +- AI/Nullkiller/Pathfinding/AINodeStorage.cpp | 37 +- AI/Nullkiller/Pathfinding/AINodeStorage.h | 12 +- AI/VCAI/AIUtility.cpp | 49 --- AI/VCAI/AIUtility.h | 63 ++- AI/VCAI/CMakeLists.txt | 4 - AI/VCAI/Goals/AbstractGoal.h | 1 + AI/VCAI/Goals/Explore.cpp | 35 +- AI/VCAI/Pathfinding/AINodeStorage.cpp | 20 +- AI/VCAI/Pathfinding/AINodeStorage.h | 8 +- AI/VCAI/SectorMap.cpp | 431 -------------------- AI/VCAI/SectorMap.h | 70 ---- CCallback.cpp | 2 +- client/CPlayerInterface.cpp | 162 ++++---- client/mapHandler.cpp | 142 +++---- client/mapHandler.h | 9 +- client/windows/CAdvmapInterface.cpp | 6 +- client/windows/GUIClasses.cpp | 2 +- lib/CGameInfoCallback.cpp | 25 +- lib/CGameInfoCallback.h | 4 +- lib/CGameState.cpp | 38 +- lib/CPathfinder.cpp | 14 +- lib/CPathfinder.h | 4 +- lib/CPlayerState.h | 2 +- lib/IGameCallback.cpp | 9 +- lib/NetPacksLib.cpp | 8 +- lib/PathfinderUtil.h | 6 +- lib/battle/BattleInfo.cpp | 13 +- lib/mapObjects/ObjectTemplate.cpp | 6 +- lib/mapping/CDrawRoadsOperation.cpp | 10 +- lib/mapping/CMap.cpp | 76 ++-- lib/mapping/CMap.h | 41 +- lib/mapping/CMapDefines.h | 2 +- lib/mapping/MapFormatH3M.cpp | 12 +- lib/rmg/CMapGenerator.cpp | 4 +- lib/rmg/CZonePlacer.cpp | 23 +- lib/rmg/Functions.cpp | 42 -- lib/rmg/ObjectManager.h | 17 +- lib/rmg/RmgMap.cpp | 6 +- lib/rmg/Zone.cpp | 8 +- lib/serializer/BinaryDeserializer.h | 14 + lib/serializer/BinarySerializer.h | 12 + lib/spells/AdventureSpellMechanics.cpp | 4 +- server/CGameHandler.cpp | 28 +- 44 files changed, 502 insertions(+), 1015 deletions(-) delete mode 100644 AI/VCAI/SectorMap.cpp delete mode 100644 AI/VCAI/SectorMap.h diff --git a/AI/Nullkiller/AIUtility.h b/AI/Nullkiller/AIUtility.h index 65c23628a..cdc19c1b5 100644 --- a/AI/Nullkiller/AIUtility.h +++ b/AI/Nullkiller/AIUtility.h @@ -205,12 +205,14 @@ void foreach_tile_pos(const Func & foo) // some micro-optimizations since this function gets called a LOT // callback pointer is thread-specific and slow to retrieve -> read map size only once int3 mapSize = cb->getMapSize(); - for(int i = 0; i < mapSize.x; i++) + for(int z = 0; z < mapSize.z; z++) { - for(int j = 0; j < mapSize.y; j++) + for(int x = 0; x < mapSize.x; x++) { - for(int k = 0; k < mapSize.z; k++) - foo(int3(i, j, k)); + for(int y = 0; y < mapSize.y; y++) + { + foo(int3(x, y, z)); + } } } } @@ -219,12 +221,14 @@ template void foreach_tile_pos(CCallback * cbp, const Func & foo) // avoid costly retrieval of thread-specific pointer { int3 mapSize = cbp->getMapSize(); - for(int i = 0; i < mapSize.x; i++) + for(int z = 0; z < mapSize.z; z++) { - for(int j = 0; j < mapSize.y; j++) + for(int x = 0; x < mapSize.x; x++) { - for(int k = 0; k < mapSize.z; k++) - foo(cbp, int3(i, j, k)); + for(int y = 0; y < mapSize.y; y++) + { + foo(cbp, int3(x, y, z)); + } } } } @@ -276,21 +280,21 @@ bool shouldVisit(const Nullkiller * ai, const CGHeroInstance * h, const CGObject template void pforeachTilePos(crint3 mapSize, TFunc fn) { - parallel_for(blocked_range(0, mapSize.x), [&](const blocked_range& r) + for(int z = 0; z < mapSize.z; ++z) { - int3 pos; - - for(pos.x = r.begin(); pos.x != r.end(); ++pos.x) + parallel_for(blocked_range(0, mapSize.x), [&](const blocked_range& r) { - for(pos.y = 0; pos.y < mapSize.y; ++pos.y) + int3 pos(0, 0, z); + + for(pos.x = r.begin(); pos.x != r.end(); ++pos.x) { - for(pos.z = 0; pos.z < mapSize.z; ++pos.z) + for(pos.y = 0; pos.y < mapSize.y; ++pos.y) { fn(pos); } } - } - }); + }); + } } class CDistanceSorter diff --git a/AI/Nullkiller/Pathfinding/AINodeStorage.cpp b/AI/Nullkiller/Pathfinding/AINodeStorage.cpp index a52457ea3..a5e302557 100644 --- a/AI/Nullkiller/Pathfinding/AINodeStorage.cpp +++ b/AI/Nullkiller/Pathfinding/AINodeStorage.cpp @@ -39,7 +39,7 @@ AISharedStorage::AISharedStorage(int3 sizes) { if(!shared){ shared.reset(new boost::multi_array( - boost::extents[sizes.x][sizes.y][sizes.z][EPathfindingLayer::NUM_LAYERS][NUM_CHAINS])); + boost::extents[EPathfindingLayer::NUM_LAYERS][sizes.z][sizes.x][sizes.y][NUM_CHAINS])); } nodes = shared; @@ -69,40 +69,41 @@ void AINodeStorage::initialize(const PathfinderOptions & options, const CGameSta //TODO: fix this code duplication with NodeStorage::initialize, problem is to keep `resetTile` inline const PlayerColor fowPlayer = ai->playerID; - const auto & fow = static_cast(gs)->getPlayerTeam(fowPlayer)->fogOfWarMap; + const auto fow = static_cast(gs)->getPlayerTeam(fowPlayer)->fogOfWarMap; const int3 sizes = gs->getMapSize(); + //Each thread gets different x, but an array of y located next to each other in memory + parallel_for(blocked_range(0, sizes.x), [&](const blocked_range& r) { - //make 200% sure that these are loop invariants (also a bit shorter code), let compiler do the rest(loop unswitching) - const bool useFlying = options.useFlying; - const bool useWaterWalking = options.useWaterWalking; - const PlayerColor player = playerID; - int3 pos; - for(pos.x = r.begin(); pos.x != r.end(); ++pos.x) + for(pos.z = 0; pos.z < sizes.z; ++pos.z) { - for(pos.y = 0; pos.y < sizes.y; ++pos.y) + const bool useFlying = options.useFlying; + const bool useWaterWalking = options.useWaterWalking; + const PlayerColor player = playerID; + + for(pos.x = r.begin(); pos.x != r.end(); ++pos.x) { - for(pos.z = 0; pos.z < sizes.z; ++pos.z) + for(pos.y = 0; pos.y < sizes.y; ++pos.y) { - const TerrainTile * tile = &gs->map->getTile(pos); - if(!tile->terType.isPassable()) + const TerrainTile* tile = &gs->map->getTile(pos); + if (!tile->terType.isPassable()) continue; - - if(tile->terType.isWater()) + + if (tile->terType.isWater()) { resetTile(pos, ELayer::SAIL, PathfinderUtil::evaluateAccessibility(pos, tile, fow, player, gs)); - if(useFlying) + if (useFlying) resetTile(pos, ELayer::AIR, PathfinderUtil::evaluateAccessibility(pos, tile, fow, player, gs)); - if(useWaterWalking) + if (useWaterWalking) resetTile(pos, ELayer::WATER, PathfinderUtil::evaluateAccessibility(pos, tile, fow, player, gs)); } else { resetTile(pos, ELayer::LAND, PathfinderUtil::evaluateAccessibility(pos, tile, fow, player, gs)); - if(useFlying) + if (useFlying) resetTile(pos, ELayer::AIR, PathfinderUtil::evaluateAccessibility(pos, tile, fow, player, gs)); } } @@ -140,7 +141,7 @@ boost::optional AINodeStorage::getOrCreateNode( { int bucketIndex = ((uintptr_t)actor) % BUCKET_COUNT; int bucketOffset = bucketIndex * BUCKET_SIZE; - auto chains = nodes.get(pos, layer); + auto chains = nodes.get(pos, layer); //FIXME: chain was the innermost layer if(chains[0].blocked()) { diff --git a/AI/Nullkiller/Pathfinding/AINodeStorage.h b/AI/Nullkiller/Pathfinding/AINodeStorage.h index cf9e910cc..bd97f88b6 100644 --- a/AI/Nullkiller/Pathfinding/AINodeStorage.h +++ b/AI/Nullkiller/Pathfinding/AINodeStorage.h @@ -120,23 +120,19 @@ enum EHeroChainPass class AISharedStorage { - /// 1-3 - position on map, 4 - layer (air, water, land), 5 - chain (normal, battle, spellcast and combinations) + // 1 - layer (air, water, land) + // 2-4 - position on map[z][x][y] + // 5 - chain (normal, battle, spellcast and combinations) static std::shared_ptr> shared; std::shared_ptr> nodes; public: AISharedStorage(int3 mapSize); ~AISharedStorage(); - /*STRONG_INLINE - boost::detail::multi_array::sub_array get(int3 tile, EPathfindingLayer layer) - { - return (*nodes)[tile.x][tile.y][tile.z][layer]; - }*/ - STRONG_INLINE boost::detail::multi_array::sub_array get(int3 tile, EPathfindingLayer layer) const { - return (*nodes)[tile.x][tile.y][tile.z][layer]; + return (*nodes)[layer][tile.z][tile.x][tile.y]; } }; diff --git a/AI/VCAI/AIUtility.cpp b/AI/VCAI/AIUtility.cpp index 352a13e68..ffee1b621 100644 --- a/AI/VCAI/AIUtility.cpp +++ b/AI/VCAI/AIUtility.cpp @@ -137,55 +137,6 @@ bool HeroPtr::operator==(const HeroPtr & rhs) const return h == rhs.get(true); } -void foreach_tile_pos(std::function foo) -{ - // some micro-optimizations since this function gets called a LOT - // callback pointer is thread-specific and slow to retrieve -> read map size only once - int3 mapSize = cb->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(int3(i, j, k)); - } - } -} - -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 - for(const int3 & dir : int3::getDirs()) - { - const int3 n = pos + dir; - if(cbp->isInTheMap(n)) - foo(pos + dir); - } -} - -void foreach_neighbour(CCallback * cbp, const int3 & pos, std::function foo) -{ - for(const int3 & dir : int3::getDirs()) - { - const int3 n = pos + dir; - if(cbp->isInTheMap(n)) - foo(cbp, pos + dir); - } -} - bool CDistanceSorter::operator()(const CGObjectInstance * lhs, const CGObjectInstance * rhs) const { const CGPathNode * ln = ai->myCb->getPathsInfo(hero)->getPathInfo(lhs->visitablePos()); diff --git a/AI/VCAI/AIUtility.h b/AI/VCAI/AIUtility.h index 0a7ce2853..9b78499e8 100644 --- a/AI/VCAI/AIUtility.h +++ b/AI/VCAI/AIUtility.h @@ -18,6 +18,7 @@ #include "../../lib/mapObjects/CObjectHandler.h" #include "../../lib/mapObjects/CGHeroInstance.h" #include "../../lib/CPathfinder.h" +#include "../../CCallback.h" class CCallback; struct creInfo; @@ -34,6 +35,8 @@ const int ALLOWED_ROAMING_HEROES = 8; extern const double SAFE_ATTACK_CONSTANT; extern const int GOLD_RESERVE; +extern boost::thread_specific_ptr cb; + //provisional class for AI to store a reference to an owned hero object //checks if it's valid on access, should be used in place of const CGHeroInstance* @@ -154,10 +157,62 @@ struct creInfo }; creInfo infoFromDC(const dwellingContent & dc); -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 +template +void foreach_tile_pos(const Func & foo) +{ + // some micro-optimizations since this function gets called a LOT + // callback pointer is thread-specific and slow to retrieve -> read map size only once + int3 mapSize = cb->getMapSize(); + for(int z = 0; z < mapSize.z; z++) + { + for(int x = 0; x < mapSize.x; x++) + { + for(int y = 0; y < mapSize.y; y++) + { + foo(int3(x, y, z)); + } + } + } +} + +template +void foreach_tile_pos(CCallback * cbp, const Func & foo) // avoid costly retrieval of thread-specific pointer +{ + int3 mapSize = cbp->getMapSize(); + for(int z = 0; z < mapSize.z; z++) + { + for(int x = 0; x < mapSize.x; x++) + { + for(int y = 0; y < mapSize.y; y++) + { + foo(cbp, int3(x, y, z)); + } + } + } +} + +template +void foreach_neighbour(const int3 & pos, const Func & foo) +{ + CCallback * cbp = cb.get(); // avoid costly retrieval of thread-specific pointer + for(const int3 & dir : int3::getDirs()) + { + const int3 n = pos + dir; + if(cbp->isInTheMap(n)) + foo(pos + dir); + } +} + +template +void foreach_neighbour(CCallback * cbp, const int3 & pos, const Func & foo) // avoid costly retrieval of thread-specific pointer +{ + for(const int3 & dir : int3::getDirs()) + { + const int3 n = pos + dir; + if(cbp->isInTheMap(n)) + foo(cbp, pos + dir); + } +} bool canBeEmbarkmentPoint(const TerrainTile * t, bool fromWater); bool isBlockedBorderGate(int3 tileToHit); diff --git a/AI/VCAI/CMakeLists.txt b/AI/VCAI/CMakeLists.txt index 96bef4317..e96423834 100644 --- a/AI/VCAI/CMakeLists.txt +++ b/AI/VCAI/CMakeLists.txt @@ -17,8 +17,6 @@ set(VCAI_SRCS ArmyManager.cpp ResourceManager.cpp BuildingManager.cpp - SectorMap.cpp - BuildingManager.cpp MapObjectsEvaluator.cpp FuzzyEngines.cpp FuzzyHelper.cpp @@ -68,8 +66,6 @@ set(VCAI_HEADERS ArmyManager.h ResourceManager.h BuildingManager.h - SectorMap.h - BuildingManager.h MapObjectsEvaluator.h FuzzyEngines.h FuzzyHelper.h diff --git a/AI/VCAI/Goals/AbstractGoal.h b/AI/VCAI/Goals/AbstractGoal.h index 5b0567da7..677a53a18 100644 --- a/AI/VCAI/Goals/AbstractGoal.h +++ b/AI/VCAI/Goals/AbstractGoal.h @@ -18,6 +18,7 @@ struct HeroPtr; class VCAI; class FuzzyHelper; +class CCallback; namespace Goals { diff --git a/AI/VCAI/Goals/Explore.cpp b/AI/VCAI/Goals/Explore.cpp index 4a3a59359..eee2e61b9 100644 --- a/AI/VCAI/Goals/Explore.cpp +++ b/AI/VCAI/Goals/Explore.cpp @@ -57,13 +57,16 @@ namespace Goals void scanSector(int scanRadius) { - for(int x = ourPos.x - scanRadius; x <= ourPos.x + scanRadius; x++) - { - for(int y = ourPos.y - scanRadius; y <= ourPos.y + scanRadius; y++) - { - int3 tile = int3(x, y, ourPos.z); + int3 tile = int3(0, 0, ourPos.z); - if(cbp->isInTheMap(tile) && ts->fogOfWarMap[tile.x][tile.y][tile.z]) + const auto & slice = (*(ts->fogOfWarMap))[ourPos.z]; + + for(tile.x = ourPos.x - scanRadius; tile.x <= ourPos.x + scanRadius; tile.x++) + { + for(tile.y = ourPos.y - scanRadius; tile.y <= ourPos.y + scanRadius; tile.y++) + { + + if(cbp->isInTheMap(tile) && slice[tile.x][tile.y]) { scanTile(tile); } @@ -84,13 +87,13 @@ namespace Goals foreach_tile_pos([&](const int3 & pos) { - if(ts->fogOfWarMap[pos.x][pos.y][pos.z]) + if((*(ts->fogOfWarMap))[pos.z][pos.x][pos.y]) { bool hasInvisibleNeighbor = false; foreach_neighbour(cbp, pos, [&](CCallback * cbp, int3 neighbour) { - if(!ts->fogOfWarMap[neighbour.x][neighbour.y][neighbour.z]) + if(!(*(ts->fogOfWarMap))[neighbour.z][neighbour.x][neighbour.y]) { hasInvisibleNeighbor = true; } @@ -174,7 +177,7 @@ namespace Goals { foreach_neighbour(cbp, tile, [&](CCallback * cbp, int3 neighbour) { - if(ts->fogOfWarMap[neighbour.x][neighbour.y][neighbour.z]) + if((*(ts->fogOfWarMap))[neighbour.z][neighbour.x][neighbour.y]) { out.push_back(neighbour); } @@ -182,18 +185,20 @@ namespace Goals } } - int howManyTilesWillBeDiscovered( - const int3 & pos) const + int howManyTilesWillBeDiscovered(const int3 & pos) const { int ret = 0; - for(int x = pos.x - sightRadius; x <= pos.x + sightRadius; x++) + int3 npos = int3(0, 0, pos.z); + + const auto & slice = (*(ts->fogOfWarMap))[pos.z]; + + for(npos.x = pos.x - sightRadius; npos.x <= pos.x + sightRadius; npos.x++) { - for(int y = pos.y - sightRadius; y <= pos.y + sightRadius; y++) + for(npos.y = pos.y - sightRadius; npos.y <= pos.y + sightRadius; npos.y++) { - int3 npos = int3(x, y, pos.z); if(cbp->isInTheMap(npos) && pos.dist2d(npos) - 0.5 < sightRadius - && !ts->fogOfWarMap[npos.x][npos.y][npos.z]) + && !slice[npos.x][npos.y]) { if(allowDeadEndCancellation && !hasReachableNeighbor(npos)) diff --git a/AI/VCAI/Pathfinding/AINodeStorage.cpp b/AI/VCAI/Pathfinding/AINodeStorage.cpp index 7c2fe609b..ed43721be 100644 --- a/AI/VCAI/Pathfinding/AINodeStorage.cpp +++ b/AI/VCAI/Pathfinding/AINodeStorage.cpp @@ -20,7 +20,7 @@ AINodeStorage::AINodeStorage(const int3 & Sizes) : sizes(Sizes) { - nodes.resize(boost::extents[sizes.x][sizes.y][sizes.z][EPathfindingLayer::NUM_LAYERS][NUM_CHAINS]); + nodes.resize(boost::extents[EPathfindingLayer::NUM_LAYERS][sizes.z][sizes.x][sizes.y][NUM_CHAINS]); dangerEvaluator.reset(new FuzzyHelper()); } @@ -28,8 +28,6 @@ AINodeStorage::~AINodeStorage() = default; void AINodeStorage::initialize(const PathfinderOptions & options, const CGameState * gs) { - //TODO: fix this code duplication with NodeStorage::initialize, problem is to keep `resetTile` inline - int3 pos; const int3 sizes = gs->getMapSize(); const auto & fow = static_cast(gs)->getPlayerTeam(hero->tempOwner)->fogOfWarMap; @@ -39,11 +37,11 @@ void AINodeStorage::initialize(const PathfinderOptions & options, const CGameSta const bool useFlying = options.useFlying; const bool useWaterWalking = options.useWaterWalking; - for(pos.x=0; pos.x < sizes.x; ++pos.x) + for(pos.z=0; pos.z < sizes.z; ++pos.z) { - for(pos.y=0; pos.y < sizes.y; ++pos.y) + for(pos.x=0; pos.x < sizes.x; ++pos.x) { - for(pos.z=0; pos.z < sizes.z; ++pos.z) + for(pos.y=0; pos.y < sizes.y; ++pos.y) { const TerrainTile * tile = &gs->map->getTile(pos); if(!tile->terType.isPassable()) @@ -87,7 +85,7 @@ bool AINodeStorage::isBattleNode(const CGPathNode * node) const boost::optional AINodeStorage::getOrCreateNode(const int3 & pos, const EPathfindingLayer layer, int chainNumber) { - auto chains = nodes[pos.x][pos.y][pos.z][layer]; + auto chains = nodes[layer][pos.z][pos.x][pos.y]; for(AIPathNode & node : chains) { @@ -126,7 +124,7 @@ void AINodeStorage::resetTile(const int3 & coord, EPathfindingLayer layer, CGPat { for(int i = 0; i < NUM_CHAINS; i++) { - AIPathNode & heroNode = nodes[coord.x][coord.y][coord.z][layer][i]; + AIPathNode & heroNode = nodes[layer][coord.z][coord.x][coord.y][i]; heroNode.chainMask = 0; heroNode.danger = 0; @@ -290,7 +288,7 @@ void AINodeStorage::calculateTownPortalTeleportations( bool AINodeStorage::hasBetterChain(const PathNodeInfo & source, CDestinationNodeInfo & destination) const { auto pos = destination.coord; - auto chains = nodes[pos.x][pos.y][pos.z][EPathfindingLayer::LAND]; + auto chains = nodes[EPathfindingLayer::LAND][pos.z][pos.x][pos.y]; auto destinationNode = getAINode(destination.node); for(const AIPathNode & node : chains) @@ -323,7 +321,7 @@ bool AINodeStorage::hasBetterChain(const PathNodeInfo & source, CDestinationNode bool AINodeStorage::isTileAccessible(const int3 & pos, const EPathfindingLayer layer) const { - const AIPathNode & node = nodes[pos.x][pos.y][pos.z][layer][0]; + const AIPathNode & node = nodes[layer][pos.z][pos.x][pos.y][0]; return node.action != CGPathNode::ENodeAction::UNKNOWN; } @@ -331,7 +329,7 @@ bool AINodeStorage::isTileAccessible(const int3 & pos, const EPathfindingLayer l std::vector AINodeStorage::getChainInfo(const int3 & pos, bool isOnLand) const { std::vector paths; - auto chains = nodes[pos.x][pos.y][pos.z][isOnLand ? EPathfindingLayer::LAND : EPathfindingLayer::SAIL]; + auto chains = nodes[isOnLand ? EPathfindingLayer::LAND : EPathfindingLayer::SAIL][pos.z][pos.x][pos.y]; auto initialPos = hero->visitablePos(); for(const AIPathNode & node : chains) diff --git a/AI/VCAI/Pathfinding/AINodeStorage.h b/AI/VCAI/Pathfinding/AINodeStorage.h index 0989809ca..b5696578d 100644 --- a/AI/VCAI/Pathfinding/AINodeStorage.h +++ b/AI/VCAI/Pathfinding/AINodeStorage.h @@ -17,6 +17,10 @@ #include "../Goals/AbstractGoal.h" #include "Actions/ISpecialAction.h" +class CCallback; + +extern boost::thread_specific_ptr cb; //for templates + struct AIPathNode : public CGPathNode { uint32_t chainMask; @@ -57,7 +61,9 @@ class AINodeStorage : public INodeStorage private: int3 sizes; - /// 1-3 - position on map, 4 - layer (air, water, land), 5 - chain (normal, battle, spellcast and combinations) + // 1 - layer (air, water, land) + // 2-4 - position on map[z][x][y] + // 5 - chain (normal, battle, spellcast and combinations) boost::multi_array nodes; const CPlayerSpecificInfoCallback * cb; const VCAI * ai; diff --git a/AI/VCAI/SectorMap.cpp b/AI/VCAI/SectorMap.cpp deleted file mode 100644 index 7840be9ed..000000000 --- a/AI/VCAI/SectorMap.cpp +++ /dev/null @@ -1,431 +0,0 @@ -/* -* SectorMap.cpp, 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 "StdInc.h" -#include "SectorMap.h" -#include "VCAI.h" - -#include "../../CCallback.h" -#include "../../lib/mapping/CMap.h" -#include "../../lib/mapObjects/MapObjects.h" -#include "../../lib/CPathfinder.h" -#include "../../lib/CGameState.h" - -extern boost::thread_specific_ptr cb; -extern boost::thread_specific_ptr ai; - -SectorMap::SectorMap() -{ - update(); -} - -SectorMap::SectorMap(HeroPtr h) -{ - update(); - makeParentBFS(h->visitablePos()); -} - -bool SectorMap::markIfBlocked(TSectorID & sec, crint3 pos, const TerrainTile * t) -{ - if (t->blocked && !t->visitable) - { - sec = NOT_AVAILABLE; - return true; - } - - return false; -} - -bool SectorMap::markIfBlocked(TSectorID & sec, crint3 pos) -{ - return markIfBlocked(sec, pos, getTile(pos)); -} - -void SectorMap::update() -{ - visibleTiles = cb->getAllVisibleTiles(); - auto shape = visibleTiles->shape(); - sector.resize(boost::extents[shape[0]][shape[1]][shape[2]]); - - clear(); - int curSector = 3; //0 is invisible, 1 is not explored - - CCallback * cbp = cb.get(); //optimization - foreach_tile_pos([&](crint3 pos) - { - if (retrieveTile(pos) == NOT_CHECKED) - { - if (!markIfBlocked(retrieveTile(pos), pos)) - exploreNewSector(pos, curSector++, cbp); - } - }); - valid = true; -} - -SectorMap::TSectorID & SectorMap::retrieveTileN(SectorMap::TSectorArray & a, const int3 & pos) -{ - return a[pos.x][pos.y][pos.z]; -} - -const SectorMap::TSectorID & SectorMap::retrieveTileN(const SectorMap::TSectorArray & a, const int3 & pos) -{ - return a[pos.x][pos.y][pos.z]; -} - -void SectorMap::clear() -{ - //TODO: rotate to [z][x][y] - auto fow = cb->getVisibilityMap(); - //TODO: any magic to automate this? will need array->array conversion - //std::transform(fow.begin(), fow.end(), sector.begin(), [](const ui8 &f) -> unsigned short - //{ - // return f; //type conversion - //}); - auto width = fow.size(); - auto height = fow.front().size(); - auto depth = fow.front().front().size(); - for (size_t x = 0; x < width; x++) - { - for (size_t y = 0; y < height; y++) - { - for (size_t z = 0; z < depth; z++) - sector[x][y][z] = fow[x][y][z]; - } - } - valid = false; -} - -void SectorMap::exploreNewSector(crint3 pos, int num, CCallback * cbp) -{ - Sector & s = infoOnSectors[num]; - s.id = num; - s.water = getTile(pos)->isWater(); - - std::queue toVisit; - toVisit.push(pos); - while (!toVisit.empty()) - { - int3 curPos = toVisit.front(); - toVisit.pop(); - TSectorID & sec = retrieveTile(curPos); - if (sec == NOT_CHECKED) - { - const TerrainTile * t = 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(cbp, curPos, [&](CCallback * cbp, crint3 neighPos) - { - if (retrieveTile(neighPos) == NOT_CHECKED) - { - toVisit.push(neighPos); - //parent[neighPos] = curPos; - } - const TerrainTile * nt = getTile(neighPos); - if (nt && nt->isWater() != s.water && canBeEmbarkmentPoint(nt, s.water)) - { - s.embarkmentPoints.push_back(neighPos); - } - }); - - if (t->visitable) - { - auto obj = t->visitableObjects.front(); - if (cb->getObj(obj->id, false)) // FIXME: we have to filter invisible objcts like events, but probably TerrainTile shouldn't be used in SectorMap at all - s.visitableObjs.push_back(obj); - } - } - } - } - } - - vstd::removeDuplicates(s.embarkmentPoints); -} - -void SectorMap::write(crstring fname) -{ - std::ofstream out(fname); - for (int k = 0; k < cb->getMapSize().z; k++) - { - for (int j = 0; j < cb->getMapSize().y; j++) - { - for (int i = 0; i < cb->getMapSize().x; i++) - { - out << (int)sector[i][j][k] << '\t'; - } - out << std::endl; - } - out << std::endl; - } -} - -/* -this functions returns one target tile or invalid tile. We will use it to poll possible destinations -For ship construction etc, another function (goal?) is needed -*/ -int3 SectorMap::firstTileToGet(HeroPtr h, crint3 dst) -{ - int3 ret(-1, -1, -1); - - int sourceSector = retrieveTile(h->visitablePos()); - int destinationSector = retrieveTile(dst); - - const Sector * src = &infoOnSectors[sourceSector]; - const Sector * dest = &infoOnSectors[destinationSector]; - - if (sourceSector != destinationSector) //use ships, shipyards etc.. - { - if (ai->isAccessibleForHero(dst, h)) //pathfinder can find a way using ships and gates if tile is not blocked by objects - return dst; - - std::map preds; - std::queue sectorQueue; - sectorQueue.push(src); - while (!sectorQueue.empty()) - { - const Sector * s = sectorQueue.front(); - sectorQueue.pop(); - - for (int3 ep : s->embarkmentPoints) - { - Sector * neigh = &infoOnSectors[retrieveTile(ep)]; - //preds[s].push_back(neigh); - if (!preds[neigh]) - { - preds[neigh] = s; - sectorQueue.push(neigh); - } - } - } - - if (!preds[dest]) - { - //write("test.txt"); - - return ret; - //throw cannotFulfillGoalException(boost::str(boost::format("Cannot find connection between sectors %d and %d") % src->id % dst->id)); - } - - std::vector toTraverse; - toTraverse.push_back(dest); - while (toTraverse.back() != src) - { - toTraverse.push_back(preds[toTraverse.back()]); - } - - if (preds[dest]) - { - //TODO: would be nice to find sectors in loop - const Sector * sectorToReach = toTraverse.at(toTraverse.size() - 2); - - if (!src->water && sectorToReach->water) //embark - { - //embark on ship -> look for an EP with a boat - auto firstEP = boost::find_if(src->embarkmentPoints, [=](crint3 pos) -> bool - { - const TerrainTile * t = getTile(pos); - if (t && t->visitableObjects.size() == 1 && t->topVisitableId() == Obj::BOAT) - { - if (retrieveTile(pos) == sectorToReach->id) - return true; - } - return false; - }); - - if (firstEP != src->embarkmentPoints.end()) - { - return *firstEP; - } - else - { - //we need to find a shipyard with an access to the desired sector's EP - //TODO what about Summon Boat spell? - std::vector shipyards; - for (const CGTownInstance * t : cb->getTownsInfo()) - { - if (t->hasBuilt(BuildingID::SHIPYARD)) - shipyards.push_back(t); - } - - for (const CGObjectInstance * obj : ai->getFlaggedObjects()) - { - if (obj->ID != Obj::TOWN) //towns were handled in the previous loop - { - if (const IShipyard * shipyard = IShipyard::castFrom(obj)) - shipyards.push_back(shipyard); - } - } - - shipyards.erase(boost::remove_if(shipyards, [=](const IShipyard * shipyard) -> bool - { - return shipyard->shipyardStatus() != 0 || retrieveTile(shipyard->bestLocation()) != sectorToReach->id; - }), shipyards.end()); - - if (!shipyards.size()) - { - //TODO consider possibility of building shipyard in a town - return ret; - - //throw cannotFulfillGoalException("There is no known shipyard!"); - } - - //we have only shipyards that possibly can build ships onto the appropriate EP - auto ownedGoodShipyard = boost::find_if(shipyards, [](const IShipyard * s) -> bool - { - return s->o->tempOwner == ai->playerID; - }); - - if (ownedGoodShipyard != shipyards.end()) - { - const IShipyard * s = *ownedGoodShipyard; - TResources shipCost; - s->getBoatCost(shipCost); - if (cb->getResourceAmount().canAfford(shipCost)) - { - int3 ret = s->bestLocation(); - cb->buildBoat(s); //TODO: move actions elsewhere - return ret; - } - else - { - //TODO gather res - return ret; - - //throw cannotFulfillGoalException("Not enough resources to build a boat"); - } - } - else - { - //TODO pick best shipyard to take over - return shipyards.front()->o->visitablePos(); - } - } - } - else if (src->water && !sectorToReach->water) - { - //TODO - //disembark - return ret; - } - else //use subterranean gates - not needed since gates are now handled via Pathfinder - { - return ret; - //throw cannotFulfillGoalException("Land-land and water-water inter-sector transitions are not implemented!"); - } - } - else - { - return ret; - //throw cannotFulfillGoalException("Inter-sector route detection failed: not connected sectors?"); - } - } - else //tiles are in same sector - { - return findFirstVisitableTile(h, dst); - } -} - -int3 SectorMap::findFirstVisitableTile(HeroPtr h, crint3 dst) -{ - int3 ret(-1, -1, -1); - int3 curtile = dst; - - while (curtile != h->visitablePos()) - { - auto topObj = cb->getTopObj(curtile); - if (topObj && topObj->ID == Obj::HERO && topObj != h.h) - { - if (cb->getPlayerRelations(h->tempOwner, topObj->tempOwner) != PlayerRelations::ENEMIES) - { - logAi->warn("Another allied hero stands in our way"); - return ret; - } - } - if (ai->myCb->getPathsInfo(h.get())->getPathInfo(curtile)->reachable()) - { - return curtile; - } - else - { - auto i = parent.find(curtile); - if (i != parent.end()) - { - assert(curtile != i->second); - curtile = i->second; - } - else - { - return ret; - //throw cannotFulfillGoalException("Unreachable tile in sector? Should not happen!"); - } - } - } - return ret; -} - -void SectorMap::makeParentBFS(crint3 source) -{ - parent.clear(); - - int mySector = retrieveTile(source); - std::queue toVisit; - toVisit.push(source); - while (!toVisit.empty()) - { - int3 curPos = toVisit.front(); - toVisit.pop(); - TSectorID & sec = retrieveTile(curPos); - assert(sec == mySector); //consider only tiles from the same sector - UNUSED(sec); - - foreach_neighbour(curPos, [&](crint3 neighPos) - { - if (retrieveTile(neighPos) == mySector && !vstd::contains(parent, neighPos)) - { - if (cb->canMoveBetween(curPos, neighPos)) - { - toVisit.push(neighPos); - parent[neighPos] = curPos; - } - } - }); - } -} - -SectorMap::TSectorID & SectorMap::retrieveTile(crint3 pos) -{ - return retrieveTileN(sector, pos); -} - -TerrainTile * SectorMap::getTile(crint3 pos) const -{ - //out of bounds access should be handled by boost::multi_array - //still we cached this array to avoid any checks - return visibleTiles->operator[](pos.x)[pos.y][pos.z]; -} - -std::vector SectorMap::getNearbyObjs(HeroPtr h, bool sectorsAround) -{ - const Sector * heroSector = &infoOnSectors[retrieveTile(h->visitablePos())]; - if (sectorsAround) - { - std::vector ret; - for (auto embarkPoint : heroSector->embarkmentPoints) - { - const Sector * embarkSector = &infoOnSectors[retrieveTile(embarkPoint)]; - range::copy(embarkSector->visitableObjs, std::back_inserter(ret)); - } - return ret; - } - return heroSector->visitableObjs; -} \ No newline at end of file diff --git a/AI/VCAI/SectorMap.h b/AI/VCAI/SectorMap.h deleted file mode 100644 index 14c8a75ca..000000000 --- a/AI/VCAI/SectorMap.h +++ /dev/null @@ -1,70 +0,0 @@ -/* -* SectorMap.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 - -#include "AIUtility.h" - -enum -{ - NOT_VISIBLE = 0, - NOT_CHECKED = 1, - NOT_AVAILABLE -}; - -struct SectorMap -{ - //a sector is set of tiles that would be mutually reachable if all visitable objs would be passable (incl monsters) - struct Sector - { - int id; - std::vector tiles; - std::vector embarkmentPoints; //tiles of other sectors onto which we can (dis)embark - std::vector visitableObjs; - bool water; //all tiles of sector are land or water - Sector() - { - id = -1; - water = false; - } - }; - - typedef unsigned short TSectorID; //smaller than int to allow -1 value. Max number of sectors 65K should be enough for any proper map. - typedef boost::multi_array TSectorArray; - - bool valid; //some kind of lazy eval - std::map parent; - TSectorArray sector; - //std::vector>> pathfinderSector; - - std::map infoOnSectors; - std::shared_ptr> visibleTiles; - - SectorMap(); - SectorMap(HeroPtr h); - void update(); - void clear(); - void exploreNewSector(crint3 pos, int num, CCallback * cbp); - void write(crstring fname); - - bool markIfBlocked(TSectorID & sec, crint3 pos, const TerrainTile * t); - bool markIfBlocked(TSectorID & sec, crint3 pos); - TSectorID & retrieveTile(crint3 pos); - TSectorID & retrieveTileN(TSectorArray & vectors, const int3 & pos); - const TSectorID & retrieveTileN(const TSectorArray & vectors, const int3 & pos); - TerrainTile * getTile(crint3 pos) const; - std::vector getNearbyObjs(HeroPtr h, bool sectorsAround); - - void makeParentBFS(crint3 source); - - int3 firstTileToGet(HeroPtr h, crint3 dst); //if h wants to reach tile dst, which tile he should visit to clear the way? - int3 findFirstVisitableTile(HeroPtr h, crint3 dst); -}; diff --git a/CCallback.cpp b/CCallback.cpp index 7b50c3079..cc1714849 100644 --- a/CCallback.cpp +++ b/CCallback.cpp @@ -324,7 +324,7 @@ int3 CCallback::getGuardingCreaturePosition(int3 tile) if (!gs->map->isInTheMap(tile)) return int3(-1,-1,-1); - return gs->map->guardingCreaturePositions[tile.x][tile.y][tile.z]; + return gs->map->guardingCreaturePositions[tile.z][tile.x][tile.y]; } void CCallback::calculatePaths( const CGHeroInstance *hero, CPathsInfo &out) diff --git a/client/CPlayerInterface.cpp b/client/CPlayerInterface.cpp index 779f973b9..62463f048 100644 --- a/client/CPlayerInterface.cpp +++ b/client/CPlayerInterface.cpp @@ -218,7 +218,7 @@ void CPlayerInterface::yourTurn() STRONG_INLINE void subRect(const int & x, const int & y, const int & z, const SDL_Rect & r, const ObjectInstanceID & hid) { - TerrainTile2 & hlp = CGI->mh->ttiles[x][y][z]; + TerrainTile2 & hlp = CGI->mh->ttiles[z][x][y]; for (auto & elem : hlp.objects) if (elem.obj && elem.obj->id == hid) { @@ -229,7 +229,7 @@ STRONG_INLINE void subRect(const int & x, const int & y, const int & z, const SD STRONG_INLINE void delObjRect(const int & x, const int & y, const int & z, const ObjectInstanceID & hid) { - TerrainTile2 & hlp = CGI->mh->ttiles[x][y][z]; + TerrainTile2 & hlp = CGI->mh->ttiles[z][x][y]; for (int h=0; hid == hid) { @@ -244,6 +244,7 @@ void CPlayerInterface::heroMoved(const TryMoveHero & details, bool verbose) if(LOCPLINT != this) return; + //FIXME: read once and store if(settings["session"]["spectate"].Bool() && settings["session"]["spectate-ignore-hero"].Bool()) return; @@ -255,7 +256,7 @@ void CPlayerInterface::heroMoved(const TryMoveHero & details, bool verbose) //AI hero left the visible area (we can't obtain info) //TODO very evil workaround -> retrieve pointer to hero so we could animate it // TODO -> we should not need full CGHeroInstance structure to display animation or it should not be handled by playerint (but by the client itself) - const TerrainTile2 & tile = CGI->mh->ttiles[hp.x - 1][hp.y][hp.z]; + const TerrainTile2 & tile = CGI->mh->ttiles[hp.z][hp.x - 1][hp.y]; for(auto & elem : tile.objects) if(elem.obj && elem.obj->id == details.id) hero = dynamic_cast(elem.obj); @@ -1738,41 +1739,43 @@ int CPlayerInterface::getLastIndex( std::string namePrefix) void CPlayerInterface::initMovement( const TryMoveHero &details, const CGHeroInstance * ho, const int3 &hp ) { + auto subArr = (CGI->mh->ttiles)[hp.z]; + if (details.end.x+1 == details.start.x && details.end.y+1 == details.start.y) //tl { //ho->moveDir = 1; ho->isStanding = false; - CGI->mh->ttiles[hp.x-3][hp.y-2][hp.z].objects.push_back(TerrainTileObject(ho, genRect(32, 32, -31, -31))); - CGI->mh->ttiles[hp.x-2][hp.y-2][hp.z].objects.push_back(TerrainTileObject(ho, genRect(32, 32, 1, -31))); - CGI->mh->ttiles[hp.x-1][hp.y-2][hp.z].objects.push_back(TerrainTileObject(ho, genRect(32, 32, 33, -31))); - CGI->mh->ttiles[hp.x][hp.y-2][hp.z].objects.push_back(TerrainTileObject(ho, genRect(32, 32, 65, -31))); + subArr[hp.x-3][hp.y-2].objects.push_back(TerrainTileObject(ho, genRect(32, 32, -31, -31))); + subArr[hp.x-2][hp.y-2].objects.push_back(TerrainTileObject(ho, genRect(32, 32, 1, -31))); + subArr[hp.x-1][hp.y-2].objects.push_back(TerrainTileObject(ho, genRect(32, 32, 33, -31))); + subArr[hp.x][hp.y-2].objects.push_back(TerrainTileObject(ho, genRect(32, 32, 65, -31))); - CGI->mh->ttiles[hp.x-3][hp.y-1][hp.z].objects.push_back(TerrainTileObject(ho, genRect(32, 32, -31, 1))); + subArr[hp.x-3][hp.y-1].objects.push_back(TerrainTileObject(ho, genRect(32, 32, -31, 1))); subRect(hp.x-2, hp.y-1, hp.z, genRect(32, 32, 1, 1), ho->id); subRect(hp.x-1, hp.y-1, hp.z, genRect(32, 32, 33, 1), ho->id); subRect(hp.x, hp.y-1, hp.z, genRect(32, 32, 65, 1), ho->id); - CGI->mh->ttiles[hp.x-3][hp.y][hp.z].objects.push_back(TerrainTileObject(ho, genRect(32, 32, -31, 33))); + subArr[hp.x-3][hp.y].objects.push_back(TerrainTileObject(ho, genRect(32, 32, -31, 33))); subRect(hp.x-2, hp.y, hp.z, genRect(32, 32, 1, 33), ho->id); subRect(hp.x-1, hp.y, hp.z, genRect(32, 32, 33, 33), ho->id); subRect(hp.x, hp.y, hp.z, genRect(32, 32, 65, 33), ho->id); - std::stable_sort(CGI->mh->ttiles[hp.x-3][hp.y-2][hp.z].objects.begin(), CGI->mh->ttiles[hp.x-3][hp.y-2][hp.z].objects.end(), objectBlitOrderSorter); - std::stable_sort(CGI->mh->ttiles[hp.x-2][hp.y-2][hp.z].objects.begin(), CGI->mh->ttiles[hp.x-2][hp.y-2][hp.z].objects.end(), objectBlitOrderSorter); - std::stable_sort(CGI->mh->ttiles[hp.x-1][hp.y-2][hp.z].objects.begin(), CGI->mh->ttiles[hp.x-1][hp.y-2][hp.z].objects.end(), objectBlitOrderSorter); - std::stable_sort(CGI->mh->ttiles[hp.x][hp.y-2][hp.z].objects.begin(), CGI->mh->ttiles[hp.x][hp.y-2][hp.z].objects.end(), objectBlitOrderSorter); + std::stable_sort(subArr[hp.x-3][hp.y-2].objects.begin(), subArr[hp.x-3][hp.y-2].objects.end(), objectBlitOrderSorter); + std::stable_sort(subArr[hp.x-2][hp.y-2].objects.begin(), subArr[hp.x-2][hp.y-2].objects.end(), objectBlitOrderSorter); + std::stable_sort(subArr[hp.x-1][hp.y-2].objects.begin(), subArr[hp.x-1][hp.y-2].objects.end(), objectBlitOrderSorter); + std::stable_sort(subArr[hp.x][hp.y-2].objects.begin(), subArr[hp.x][hp.y-2].objects.end(), objectBlitOrderSorter); - std::stable_sort(CGI->mh->ttiles[hp.x-3][hp.y-1][hp.z].objects.begin(), CGI->mh->ttiles[hp.x-3][hp.y-1][hp.z].objects.end(), objectBlitOrderSorter); + std::stable_sort(subArr[hp.x-3][hp.y-1].objects.begin(), subArr[hp.x-3][hp.y-1].objects.end(), objectBlitOrderSorter); - std::stable_sort(CGI->mh->ttiles[hp.x-3][hp.y][hp.z].objects.begin(), CGI->mh->ttiles[hp.x-3][hp.y][hp.z].objects.end(), objectBlitOrderSorter); + std::stable_sort(subArr[hp.x-3][hp.y].objects.begin(), subArr[hp.x-3][hp.y].objects.end(), objectBlitOrderSorter); } else if (details.end.x == details.start.x && details.end.y+1 == details.start.y) //t { //ho->moveDir = 2; ho->isStanding = false; - CGI->mh->ttiles[hp.x-2][hp.y-2][hp.z].objects.push_back(TerrainTileObject(ho, genRect(32, 32, 0, -31))); - CGI->mh->ttiles[hp.x-1][hp.y-2][hp.z].objects.push_back(TerrainTileObject(ho, genRect(32, 32, 32, -31))); - CGI->mh->ttiles[hp.x][hp.y-2][hp.z].objects.push_back(TerrainTileObject(ho, genRect(32, 32, 64, -31))); + subArr[hp.x-2][hp.y-2].objects.push_back(TerrainTileObject(ho, genRect(32, 32, 0, -31))); + subArr[hp.x-1][hp.y-2].objects.push_back(TerrainTileObject(ho, genRect(32, 32, 32, -31))); + subArr[hp.x][hp.y-2].objects.push_back(TerrainTileObject(ho, genRect(32, 32, 64, -31))); subRect(hp.x-2, hp.y-1, hp.z, genRect(32, 32, 0, 1), ho->id); subRect(hp.x-1, hp.y-1, hp.z, genRect(32, 32, 32, 1), ho->id); @@ -1782,37 +1785,37 @@ void CPlayerInterface::initMovement( const TryMoveHero &details, const CGHeroIns subRect(hp.x-1, hp.y, hp.z, genRect(32, 32, 32, 33), ho->id); subRect(hp.x, hp.y, hp.z, genRect(32, 32, 64, 33), ho->id); - std::stable_sort(CGI->mh->ttiles[hp.x-2][hp.y-2][hp.z].objects.begin(), CGI->mh->ttiles[hp.x-2][hp.y-2][hp.z].objects.end(), objectBlitOrderSorter); - std::stable_sort(CGI->mh->ttiles[hp.x-1][hp.y-2][hp.z].objects.begin(), CGI->mh->ttiles[hp.x-1][hp.y-2][hp.z].objects.end(), objectBlitOrderSorter); - std::stable_sort(CGI->mh->ttiles[hp.x][hp.y-2][hp.z].objects.begin(), CGI->mh->ttiles[hp.x][hp.y-2][hp.z].objects.end(), objectBlitOrderSorter); + std::stable_sort(subArr[hp.x-2][hp.y-2].objects.begin(), subArr[hp.x-2][hp.y-2].objects.end(), objectBlitOrderSorter); + std::stable_sort(subArr[hp.x-1][hp.y-2].objects.begin(), subArr[hp.x-1][hp.y-2].objects.end(), objectBlitOrderSorter); + std::stable_sort(subArr[hp.x][hp.y-2].objects.begin(), subArr[hp.x][hp.y-2].objects.end(), objectBlitOrderSorter); } else if (details.end.x-1 == details.start.x && details.end.y+1 == details.start.y) //tr { //ho->moveDir = 3; ho->isStanding = false; - CGI->mh->ttiles[hp.x-2][hp.y-2][hp.z].objects.push_back(TerrainTileObject(ho, genRect(32, 32, -1, -31))); - CGI->mh->ttiles[hp.x-1][hp.y-2][hp.z].objects.push_back(TerrainTileObject(ho, genRect(32, 32, 31, -31))); - CGI->mh->ttiles[hp.x][hp.y-2][hp.z].objects.push_back(TerrainTileObject(ho, genRect(32, 32, 63, -31))); - CGI->mh->ttiles[hp.x+1][hp.y-2][hp.z].objects.push_back(TerrainTileObject(ho, genRect(32, 32, 95, -31))); + subArr[hp.x-2][hp.y-2].objects.push_back(TerrainTileObject(ho, genRect(32, 32, -1, -31))); + subArr[hp.x-1][hp.y-2].objects.push_back(TerrainTileObject(ho, genRect(32, 32, 31, -31))); + subArr[hp.x][hp.y-2].objects.push_back(TerrainTileObject(ho, genRect(32, 32, 63, -31))); + subArr[hp.x+1][hp.y-2].objects.push_back(TerrainTileObject(ho, genRect(32, 32, 95, -31))); subRect(hp.x-2, hp.y-1, hp.z, genRect(32, 32, -1, 1), ho->id); subRect(hp.x-1, hp.y-1, hp.z, genRect(32, 32, 31, 1), ho->id); subRect(hp.x, hp.y-1, hp.z, genRect(32, 32, 63, 1), ho->id); - CGI->mh->ttiles[hp.x+1][hp.y-1][hp.z].objects.push_back(TerrainTileObject(ho, genRect(32, 32, 95, 1))); + subArr[hp.x+1][hp.y-1].objects.push_back(TerrainTileObject(ho, genRect(32, 32, 95, 1))); subRect(hp.x-2, hp.y, hp.z, genRect(32, 32, -1, 33), ho->id); subRect(hp.x-1, hp.y, hp.z, genRect(32, 32, 31, 33), ho->id); subRect(hp.x, hp.y, hp.z, genRect(32, 32, 63, 33), ho->id); - CGI->mh->ttiles[hp.x+1][hp.y][hp.z].objects.push_back(TerrainTileObject(ho, genRect(32, 32, 95, 33))); + subArr[hp.x+1][hp.y].objects.push_back(TerrainTileObject(ho, genRect(32, 32, 95, 33))); - std::stable_sort(CGI->mh->ttiles[hp.x-2][hp.y-2][hp.z].objects.begin(), CGI->mh->ttiles[hp.x-2][hp.y-2][hp.z].objects.end(), objectBlitOrderSorter); - std::stable_sort(CGI->mh->ttiles[hp.x-1][hp.y-2][hp.z].objects.begin(), CGI->mh->ttiles[hp.x-1][hp.y-2][hp.z].objects.end(), objectBlitOrderSorter); - std::stable_sort(CGI->mh->ttiles[hp.x][hp.y-2][hp.z].objects.begin(), CGI->mh->ttiles[hp.x][hp.y-2][hp.z].objects.end(), objectBlitOrderSorter); - std::stable_sort(CGI->mh->ttiles[hp.x+1][hp.y-2][hp.z].objects.begin(), CGI->mh->ttiles[hp.x+1][hp.y-2][hp.z].objects.end(), objectBlitOrderSorter); + std::stable_sort(subArr[hp.x-2][hp.y-2].objects.begin(), subArr[hp.x-2][hp.y-2].objects.end(), objectBlitOrderSorter); + std::stable_sort(subArr[hp.x-1][hp.y-2].objects.begin(), subArr[hp.x-1][hp.y-2].objects.end(), objectBlitOrderSorter); + std::stable_sort(subArr[hp.x][hp.y-2].objects.begin(), subArr[hp.x][hp.y-2].objects.end(), objectBlitOrderSorter); + std::stable_sort(subArr[hp.x+1][hp.y-2].objects.begin(), subArr[hp.x+1][hp.y-2].objects.end(), objectBlitOrderSorter); - std::stable_sort(CGI->mh->ttiles[hp.x+1][hp.y-1][hp.z].objects.begin(), CGI->mh->ttiles[hp.x+1][hp.y-1][hp.z].objects.end(), objectBlitOrderSorter); + std::stable_sort(subArr[hp.x+1][hp.y-1].objects.begin(), subArr[hp.x+1][hp.y-1].objects.end(), objectBlitOrderSorter); - std::stable_sort(CGI->mh->ttiles[hp.x+1][hp.y][hp.z].objects.begin(), CGI->mh->ttiles[hp.x+1][hp.y][hp.z].objects.end(), objectBlitOrderSorter); + std::stable_sort(subArr[hp.x+1][hp.y].objects.begin(), subArr[hp.x+1][hp.y].objects.end(), objectBlitOrderSorter); } else if (details.end.x-1 == details.start.x && details.end.y == details.start.y) //r { @@ -1821,16 +1824,16 @@ void CPlayerInterface::initMovement( const TryMoveHero &details, const CGHeroIns subRect(hp.x-2, hp.y-1, hp.z, genRect(32, 32, -1, 0), ho->id); subRect(hp.x-1, hp.y-1, hp.z, genRect(32, 32, 31, 0), ho->id); subRect(hp.x, hp.y-1, hp.z, genRect(32, 32, 63, 0), ho->id); - CGI->mh->ttiles[hp.x+1][hp.y-1][hp.z].objects.push_back(TerrainTileObject(ho, genRect(32, 32, 95, 0))); + subArr[hp.x+1][hp.y-1].objects.push_back(TerrainTileObject(ho, genRect(32, 32, 95, 0))); subRect(hp.x-2, hp.y, hp.z, genRect(32, 32, -1, 32), ho->id); subRect(hp.x-1, hp.y, hp.z, genRect(32, 32, 31, 32), ho->id); subRect(hp.x, hp.y, hp.z, genRect(32, 32, 63, 32), ho->id); - CGI->mh->ttiles[hp.x+1][hp.y][hp.z].objects.push_back(TerrainTileObject(ho, genRect(32, 32, 95, 32))); + subArr[hp.x+1][hp.y].objects.push_back(TerrainTileObject(ho, genRect(32, 32, 95, 32))); - std::stable_sort(CGI->mh->ttiles[hp.x+1][hp.y-1][hp.z].objects.begin(), CGI->mh->ttiles[hp.x+1][hp.y-1][hp.z].objects.end(), objectBlitOrderSorter); + std::stable_sort(subArr[hp.x+1][hp.y-1].objects.begin(), subArr[hp.x+1][hp.y-1].objects.end(), objectBlitOrderSorter); - std::stable_sort(CGI->mh->ttiles[hp.x+1][hp.y][hp.z].objects.begin(), CGI->mh->ttiles[hp.x+1][hp.y][hp.z].objects.end(), objectBlitOrderSorter); + std::stable_sort(subArr[hp.x+1][hp.y].objects.begin(), subArr[hp.x+1][hp.y].objects.end(), objectBlitOrderSorter); } else if (details.end.x-1 == details.start.x && details.end.y-1 == details.start.y) //br { @@ -1839,26 +1842,26 @@ void CPlayerInterface::initMovement( const TryMoveHero &details, const CGHeroIns subRect(hp.x-2, hp.y-1, hp.z, genRect(32, 32, -1, -1), ho->id); subRect(hp.x-1, hp.y-1, hp.z, genRect(32, 32, 31, -1), ho->id); subRect(hp.x, hp.y-1, hp.z, genRect(32, 32, 63, -1), ho->id); - CGI->mh->ttiles[hp.x+1][hp.y-1][hp.z].objects.push_back(TerrainTileObject(ho, genRect(32, 32, 95, -1))); + subArr[hp.x+1][hp.y-1].objects.push_back(TerrainTileObject(ho, genRect(32, 32, 95, -1))); subRect(hp.x-2, hp.y, hp.z, genRect(32, 32, -1, 31), ho->id); subRect(hp.x-1, hp.y, hp.z, genRect(32, 32, 31, 31), ho->id); subRect(hp.x, hp.y, hp.z, genRect(32, 32, 63, 31), ho->id); - CGI->mh->ttiles[hp.x+1][hp.y][hp.z].objects.push_back(TerrainTileObject(ho, genRect(32, 32, 95, 31))); + subArr[hp.x+1][hp.y].objects.push_back(TerrainTileObject(ho, genRect(32, 32, 95, 31))); - CGI->mh->ttiles[hp.x-2][hp.y+1][hp.z].objects.push_back(TerrainTileObject(ho, genRect(32, 32, -1, 63))); - CGI->mh->ttiles[hp.x-1][hp.y+1][hp.z].objects.push_back(TerrainTileObject(ho, genRect(32, 32, 31, 63))); - CGI->mh->ttiles[hp.x][hp.y+1][hp.z].objects.push_back(TerrainTileObject(ho, genRect(32, 32, 63, 63))); - CGI->mh->ttiles[hp.x+1][hp.y+1][hp.z].objects.push_back(TerrainTileObject(ho, genRect(32, 32, 95, 63))); + subArr[hp.x-2][hp.y+1].objects.push_back(TerrainTileObject(ho, genRect(32, 32, -1, 63))); + subArr[hp.x-1][hp.y+1].objects.push_back(TerrainTileObject(ho, genRect(32, 32, 31, 63))); + subArr[hp.x][hp.y+1].objects.push_back(TerrainTileObject(ho, genRect(32, 32, 63, 63))); + subArr[hp.x+1][hp.y+1].objects.push_back(TerrainTileObject(ho, genRect(32, 32, 95, 63))); - std::stable_sort(CGI->mh->ttiles[hp.x+1][hp.y-1][hp.z].objects.begin(), CGI->mh->ttiles[hp.x+1][hp.y-1][hp.z].objects.end(), objectBlitOrderSorter); + std::stable_sort(subArr[hp.x+1][hp.y-1].objects.begin(), subArr[hp.x+1][hp.y-1].objects.end(), objectBlitOrderSorter); - std::stable_sort(CGI->mh->ttiles[hp.x+1][hp.y][hp.z].objects.begin(), CGI->mh->ttiles[hp.x+1][hp.y][hp.z].objects.end(), objectBlitOrderSorter); + std::stable_sort(subArr[hp.x+1][hp.y].objects.begin(), subArr[hp.x+1][hp.y].objects.end(), objectBlitOrderSorter); - std::stable_sort(CGI->mh->ttiles[hp.x-2][hp.y+1][hp.z].objects.begin(), CGI->mh->ttiles[hp.x-2][hp.y+1][hp.z].objects.end(), objectBlitOrderSorter); - std::stable_sort(CGI->mh->ttiles[hp.x-1][hp.y+1][hp.z].objects.begin(), CGI->mh->ttiles[hp.x-1][hp.y+1][hp.z].objects.end(), objectBlitOrderSorter); - std::stable_sort(CGI->mh->ttiles[hp.x][hp.y+1][hp.z].objects.begin(), CGI->mh->ttiles[hp.x][hp.y+1][hp.z].objects.end(), objectBlitOrderSorter); - std::stable_sort(CGI->mh->ttiles[hp.x+1][hp.y+1][hp.z].objects.begin(), CGI->mh->ttiles[hp.x+1][hp.y+1][hp.z].objects.end(), objectBlitOrderSorter); + std::stable_sort(subArr[hp.x-2][hp.y+1].objects.begin(), subArr[hp.x-2][hp.y+1].objects.end(), objectBlitOrderSorter); + std::stable_sort(subArr[hp.x-1][hp.y+1].objects.begin(), subArr[hp.x-1][hp.y+1].objects.end(), objectBlitOrderSorter); + std::stable_sort(subArr[hp.x][hp.y+1].objects.begin(), subArr[hp.x][hp.y+1].objects.end(), objectBlitOrderSorter); + std::stable_sort(subArr[hp.x+1][hp.y+1].objects.begin(), subArr[hp.x+1][hp.y+1].objects.end(), objectBlitOrderSorter); } else if (details.end.x == details.start.x && details.end.y-1 == details.start.y) //b { @@ -1872,59 +1875,59 @@ void CPlayerInterface::initMovement( const TryMoveHero &details, const CGHeroIns subRect(hp.x-1, hp.y, hp.z, genRect(32, 32, 32, 31), ho->id); subRect(hp.x, hp.y, hp.z, genRect(32, 32, 64, 31), ho->id); - CGI->mh->ttiles[hp.x-2][hp.y+1][hp.z].objects.push_back(TerrainTileObject(ho, genRect(32, 32, 0, 63))); - CGI->mh->ttiles[hp.x-1][hp.y+1][hp.z].objects.push_back(TerrainTileObject(ho, genRect(32, 32, 32, 63))); - CGI->mh->ttiles[hp.x][hp.y+1][hp.z].objects.push_back(TerrainTileObject(ho, genRect(32, 32, 64, 63))); + subArr[hp.x-2][hp.y+1].objects.push_back(TerrainTileObject(ho, genRect(32, 32, 0, 63))); + subArr[hp.x-1][hp.y+1].objects.push_back(TerrainTileObject(ho, genRect(32, 32, 32, 63))); + subArr[hp.x][hp.y+1].objects.push_back(TerrainTileObject(ho, genRect(32, 32, 64, 63))); - std::stable_sort(CGI->mh->ttiles[hp.x-2][hp.y+1][hp.z].objects.begin(), CGI->mh->ttiles[hp.x-2][hp.y+1][hp.z].objects.end(), objectBlitOrderSorter); - std::stable_sort(CGI->mh->ttiles[hp.x-1][hp.y+1][hp.z].objects.begin(), CGI->mh->ttiles[hp.x-1][hp.y+1][hp.z].objects.end(), objectBlitOrderSorter); - std::stable_sort(CGI->mh->ttiles[hp.x][hp.y+1][hp.z].objects.begin(), CGI->mh->ttiles[hp.x][hp.y+1][hp.z].objects.end(), objectBlitOrderSorter); + std::stable_sort(subArr[hp.x-2][hp.y+1].objects.begin(), subArr[hp.x-2][hp.y+1].objects.end(), objectBlitOrderSorter); + std::stable_sort(subArr[hp.x-1][hp.y+1].objects.begin(), subArr[hp.x-1][hp.y+1].objects.end(), objectBlitOrderSorter); + std::stable_sort(subArr[hp.x][hp.y+1].objects.begin(), subArr[hp.x][hp.y+1].objects.end(), objectBlitOrderSorter); } else if (details.end.x+1 == details.start.x && details.end.y-1 == details.start.y) //bl { //ho->moveDir = 7; ho->isStanding = false; - CGI->mh->ttiles[hp.x-3][hp.y-1][hp.z].objects.push_back(TerrainTileObject(ho, genRect(32, 32, -31, -1))); + subArr[hp.x-3][hp.y-1].objects.push_back(TerrainTileObject(ho, genRect(32, 32, -31, -1))); subRect(hp.x-2, hp.y-1, hp.z, genRect(32, 32, 1, -1), ho->id); subRect(hp.x-1, hp.y-1, hp.z, genRect(32, 32, 33, -1), ho->id); subRect(hp.x, hp.y-1, hp.z, genRect(32, 32, 65, -1), ho->id); - CGI->mh->ttiles[hp.x-3][hp.y][hp.z].objects.push_back(TerrainTileObject(ho, genRect(32, 32, -31, 31))); + subArr[hp.x-3][hp.y].objects.push_back(TerrainTileObject(ho, genRect(32, 32, -31, 31))); subRect(hp.x-2, hp.y, hp.z, genRect(32, 32, 1, 31), ho->id); subRect(hp.x-1, hp.y, hp.z, genRect(32, 32, 33, 31), ho->id); subRect(hp.x, hp.y, hp.z, genRect(32, 32, 65, 31), ho->id); - CGI->mh->ttiles[hp.x-3][hp.y+1][hp.z].objects.push_back(TerrainTileObject(ho, genRect(32, 32, -31, 63))); - CGI->mh->ttiles[hp.x-2][hp.y+1][hp.z].objects.push_back(TerrainTileObject(ho, genRect(32, 32, 1, 63))); - CGI->mh->ttiles[hp.x-1][hp.y+1][hp.z].objects.push_back(TerrainTileObject(ho, genRect(32, 32, 33, 63))); - CGI->mh->ttiles[hp.x][hp.y+1][hp.z].objects.push_back(TerrainTileObject(ho, genRect(32, 32, 65, 63))); + subArr[hp.x-3][hp.y+1].objects.push_back(TerrainTileObject(ho, genRect(32, 32, -31, 63))); + subArr[hp.x-2][hp.y+1].objects.push_back(TerrainTileObject(ho, genRect(32, 32, 1, 63))); + subArr[hp.x-1][hp.y+1].objects.push_back(TerrainTileObject(ho, genRect(32, 32, 33, 63))); + subArr[hp.x][hp.y+1].objects.push_back(TerrainTileObject(ho, genRect(32, 32, 65, 63))); - std::stable_sort(CGI->mh->ttiles[hp.x-3][hp.y-1][hp.z].objects.begin(), CGI->mh->ttiles[hp.x-3][hp.y-1][hp.z].objects.end(), objectBlitOrderSorter); + std::stable_sort(subArr[hp.x-3][hp.y-1].objects.begin(), subArr[hp.x-3][hp.y-1].objects.end(), objectBlitOrderSorter); - std::stable_sort(CGI->mh->ttiles[hp.x-3][hp.y][hp.z].objects.begin(), CGI->mh->ttiles[hp.x-3][hp.y][hp.z].objects.end(), objectBlitOrderSorter); + std::stable_sort(subArr[hp.x-3][hp.y].objects.begin(), subArr[hp.x-3][hp.y].objects.end(), objectBlitOrderSorter); - std::stable_sort(CGI->mh->ttiles[hp.x-3][hp.y+1][hp.z].objects.begin(), CGI->mh->ttiles[hp.x-3][hp.y+1][hp.z].objects.end(), objectBlitOrderSorter); - std::stable_sort(CGI->mh->ttiles[hp.x-2][hp.y+1][hp.z].objects.begin(), CGI->mh->ttiles[hp.x-2][hp.y+1][hp.z].objects.end(), objectBlitOrderSorter); - std::stable_sort(CGI->mh->ttiles[hp.x-1][hp.y+1][hp.z].objects.begin(), CGI->mh->ttiles[hp.x-1][hp.y+1][hp.z].objects.end(), objectBlitOrderSorter); - std::stable_sort(CGI->mh->ttiles[hp.x][hp.y+1][hp.z].objects.begin(), CGI->mh->ttiles[hp.x][hp.y+1][hp.z].objects.end(), objectBlitOrderSorter); + std::stable_sort(subArr[hp.x-3][hp.y+1].objects.begin(), subArr[hp.x-3][hp.y+1].objects.end(), objectBlitOrderSorter); + std::stable_sort(subArr[hp.x-2][hp.y+1].objects.begin(), subArr[hp.x-2][hp.y+1].objects.end(), objectBlitOrderSorter); + std::stable_sort(subArr[hp.x-1][hp.y+1].objects.begin(), subArr[hp.x-1][hp.y+1].objects.end(), objectBlitOrderSorter); + std::stable_sort(subArr[hp.x][hp.y+1].objects.begin(), subArr[hp.x][hp.y+1].objects.end(), objectBlitOrderSorter); } else if (details.end.x+1 == details.start.x && details.end.y == details.start.y) //l { //ho->moveDir = 8; ho->isStanding = false; - CGI->mh->ttiles[hp.x-3][hp.y-1][hp.z].objects.push_back(TerrainTileObject(ho, genRect(32, 32, -31, 0))); + subArr[hp.x-3][hp.y-1].objects.push_back(TerrainTileObject(ho, genRect(32, 32, -31, 0))); subRect(hp.x-2, hp.y-1, hp.z, genRect(32, 32, 1, 0), ho->id); subRect(hp.x-1, hp.y-1, hp.z, genRect(32, 32, 33, 0), ho->id); subRect(hp.x, hp.y-1, hp.z, genRect(32, 32, 65, 0), ho->id); - CGI->mh->ttiles[hp.x-3][hp.y][hp.z].objects.push_back(TerrainTileObject(ho, genRect(32, 32, -31, 32))); + subArr[hp.x-3][hp.y].objects.push_back(TerrainTileObject(ho, genRect(32, 32, -31, 32))); subRect(hp.x-2, hp.y, hp.z, genRect(32, 32, 1, 32), ho->id); subRect(hp.x-1, hp.y, hp.z, genRect(32, 32, 33, 32), ho->id); subRect(hp.x, hp.y, hp.z, genRect(32, 32, 65, 32), ho->id); - std::stable_sort(CGI->mh->ttiles[hp.x-3][hp.y-1][hp.z].objects.begin(), CGI->mh->ttiles[hp.x-3][hp.y-1][hp.z].objects.end(), objectBlitOrderSorter); + std::stable_sort(subArr[hp.x-3][hp.y-1].objects.begin(), subArr[hp.x-3][hp.y-1].objects.end(), objectBlitOrderSorter); - std::stable_sort(CGI->mh->ttiles[hp.x-3][hp.y][hp.z].objects.begin(), CGI->mh->ttiles[hp.x-3][hp.y][hp.z].objects.end(), objectBlitOrderSorter); + std::stable_sort(subArr[hp.x-3][hp.y].objects.begin(), subArr[hp.x-3][hp.y].objects.end(), objectBlitOrderSorter); } } @@ -2154,13 +2157,16 @@ void CPlayerInterface::finishMovement( const TryMoveHero &details, const int3 &h subRect(details.end.x, details.end.y, details.end.z, genRect(32, 32, 64, 32), ho->id); //restoring good order of objects - std::stable_sort(CGI->mh->ttiles[details.end.x-2][details.end.y-1][details.end.z].objects.begin(), CGI->mh->ttiles[details.end.x-2][details.end.y-1][details.end.z].objects.end(), objectBlitOrderSorter); - std::stable_sort(CGI->mh->ttiles[details.end.x-1][details.end.y-1][details.end.z].objects.begin(), CGI->mh->ttiles[details.end.x-1][details.end.y-1][details.end.z].objects.end(), objectBlitOrderSorter); - std::stable_sort(CGI->mh->ttiles[details.end.x][details.end.y-1][details.end.z].objects.begin(), CGI->mh->ttiles[details.end.x][details.end.y-1][details.end.z].objects.end(), objectBlitOrderSorter); - std::stable_sort(CGI->mh->ttiles[details.end.x-2][details.end.y][details.end.z].objects.begin(), CGI->mh->ttiles[details.end.x-2][details.end.y][details.end.z].objects.end(), objectBlitOrderSorter); - std::stable_sort(CGI->mh->ttiles[details.end.x-1][details.end.y][details.end.z].objects.begin(), CGI->mh->ttiles[details.end.x-1][details.end.y][details.end.z].objects.end(), objectBlitOrderSorter); - std::stable_sort(CGI->mh->ttiles[details.end.x][details.end.y][details.end.z].objects.begin(), CGI->mh->ttiles[details.end.x][details.end.y][details.end.z].objects.end(), objectBlitOrderSorter); + boost::detail::multi_array::sub_array subArr = (CGI->mh->ttiles)[details.end.z]; + + std::stable_sort(subArr[details.end.x-2][details.end.y-1].objects.begin(), subArr[details.end.x-2][details.end.y-1].objects.end(), objectBlitOrderSorter); + std::stable_sort(subArr[details.end.x-1][details.end.y-1].objects.begin(), subArr[details.end.x-1][details.end.y-1].objects.end(), objectBlitOrderSorter); + std::stable_sort(subArr[details.end.x][details.end.y-1].objects.begin(), subArr[details.end.x][details.end.y-1].objects.end(), objectBlitOrderSorter); + + std::stable_sort(subArr[details.end.x-2][details.end.y].objects.begin(), subArr[details.end.x-2][details.end.y].objects.end(), objectBlitOrderSorter); + std::stable_sort(subArr[details.end.x-1][details.end.y].objects.begin(), subArr[details.end.x-1][details.end.y].objects.end(), objectBlitOrderSorter); + std::stable_sort(subArr[details.end.x][details.end.y].objects.begin(), subArr[details.end.x][details.end.y].objects.end(), objectBlitOrderSorter); } void CPlayerInterface::gameOver(PlayerColor player, const EVictoryLossCheckResult & victoryLossCheckResult ) @@ -2882,7 +2888,7 @@ void CPlayerInterface::updateAmbientSounds(bool resetAll) { int dist = pos.dist(tile, int3::DIST_CHEBYSHEV); // We want sound for every special terrain on tile and not just one on top - for(auto & ttObj : CGI->mh->ttiles[tile.x][tile.y][tile.z].objects) + for(auto & ttObj : CGI->mh->ttiles[tile.z][tile.x][tile.y].objects) { if(ttObj.ambientSound) updateSounds(ttObj.ambientSound.get(), dist); diff --git a/client/mapHandler.cpp b/client/mapHandler.cpp index c9da73647..973143854 100644 --- a/client/mapHandler.cpp +++ b/client/mapHandler.cpp @@ -52,20 +52,22 @@ struct NeighborTilesInfo d1, d2, d3; - NeighborTilesInfo(const int3 & pos, const int3 & sizes, const std::vector< std::vector< std::vector > > & visibilityMap) + NeighborTilesInfo(const int3 & pos, const int3 & sizes, std::shared_ptr> visibilityMap) { auto getTile = [&](int dx, int dy)->bool { if ( dx + pos.x < 0 || dx + pos.x >= sizes.x || dy + pos.y < 0 || dy + pos.y >= sizes.y) return false; - return settings["session"]["spectate"].Bool() ? true : visibilityMap[dx+pos.x][dy+pos.y][pos.z]; + + //FIXME: please do not read settings for every tile... + return settings["session"]["spectate"].Bool() ? true : (*visibilityMap)[pos.z][dx+pos.x][dy+pos.y]; }; d7 = getTile(-1, -1); //789 d8 = getTile( 0, -1); //456 d9 = getTile(+1, -1); //123 d4 = getTile(-1, 0); - d5 = visibilityMap[pos.x][pos.y][pos.z]; + d5 = (*visibilityMap)[pos.z][pos.x][pos.y]; d6 = getTile(+1, 0); d1 = getTile(-1, +1); d2 = getTile( 0, +1); @@ -113,22 +115,9 @@ void CMapHandler::prepareFOWDefs() FoWfullHide[frame] = graphics->fogOfWarFullHide->getImage(frame); //initialization of type of full-hide image - hideBitmap.resize(sizes.x); - for (auto & elem : hideBitmap) - { - elem.resize(sizes.y); - } - for (auto & elem : hideBitmap) - { - for (int j = 0; j < sizes.y; ++j) - { - elem[j].resize(sizes.z); - for(int k = 0; k < sizes.z; ++k) - { - elem[j][k] = CRandomGenerator::getDefault().nextInt((int)size - 1); - } - } - } + hideBitmap.resize(boost::extents[sizes.z][sizes.x][sizes.y]); + for (int i = 0; i < hideBitmap.num_elements(); i++) + hideBitmap.data()[i] = CRandomGenerator::getDefault().nextInt(size - 1); size = graphics->fogOfWarPartialHide->size(0); FoWpartialHide.resize(size); @@ -211,16 +200,12 @@ void CMapHandler::initTerrainGraphics() // Create enough room for the whole map and its frame - ttiles.resize(sizes.x, frameW, frameW); - for (int i=0-frameW;i{ 0, -frameW, -frameH }); //need to move starting coordinates so that used index is always positive } void CMapHandler::initBorderGraphics() @@ -321,19 +306,21 @@ void CMapHandler::initObjectRects() obj->coveringAt(currTile.x, currTile.y) // object is visible here ) { - ttiles[currTile.x][currTile.y][currTile.z].objects.push_back(toAdd); + ttiles[currTile.z][currTile.x][currTile.y].objects.push_back(toAdd); } } } } - for(int ix=0; ixwidth; sizes.y = map->height; - sizes.z = map->twoLevel ? 2 : 1; + sizes.z = map->levels(); // Total number of visible tiles. Subtract the center tile, then // compute the number of tiles on each side, and reassemble. @@ -557,7 +544,9 @@ void CMapHandler::CMapWorldViewBlitter::drawTileOverlay(SDL_Surface * targetSurf const CGObjectInstance * obj = object.obj; const bool sameLevel = obj->pos.z == pos.z; - const bool isVisible = settings["session"]["spectate"].Bool() ? true : (*info->visibilityMap)[pos.x][pos.y][pos.z]; + + //FIXME: Don't read options in a loop :v + const bool isVisible = settings["session"]["spectate"].Bool() ? true : (*info->visibilityMap)[pos.z][pos.x][pos.y]; const bool isVisitable = obj->visitableAt(pos.x, pos.y); if(sameLevel && isVisible && isVisitable) @@ -826,11 +815,11 @@ void CMapHandler::CMapBlitter::drawRiver(SDL_Surface * targetSurf, const Terrain void CMapHandler::CMapBlitter::drawFow(SDL_Surface * targetSurf) const { - const NeighborTilesInfo neighborInfo(pos, parent->sizes, *info->visibilityMap); + const NeighborTilesInfo neighborInfo(pos, parent->sizes, info->visibilityMap); int retBitmapID = neighborInfo.getBitmapID();// >=0 -> partial hide, <0 - full hide if (retBitmapID < 0) - retBitmapID = - parent->hideBitmap[pos.x][pos.y][pos.z] - 1; //fully hidden + retBitmapID = - parent->hideBitmap[pos.z][pos.x][pos.y] - 1; //fully hidden std::shared_ptr image; @@ -865,7 +854,7 @@ void CMapHandler::CMapBlitter::blit(SDL_Surface * targetSurf, const MapDrawingIn realTileRect.x = realPos.x; realTileRect.y = realPos.y; - const TerrainTile2 & tile = parent->ttiles[pos.x][pos.y][pos.z]; + const TerrainTile2 & tile = parent->ttiles[pos.z][pos.x][pos.y]; const TerrainTile & tinfo = parent->map->getTile(pos); const TerrainTile * tinfoUpper = pos.y > 0 ? &parent->map->getTile(int3(pos.x, pos.y - 1, pos.z)) : nullptr; @@ -896,9 +885,9 @@ void CMapHandler::CMapBlitter::blit(SDL_Surface * targetSurf, const MapDrawingIn } else { - const TerrainTile2 & tile = parent->ttiles[pos.x][pos.y][pos.z]; + const TerrainTile2 & tile = parent->ttiles[pos.z][pos.x][pos.y]; - if(!settings["session"]["spectate"].Bool() && !(*info->visibilityMap)[pos.x][pos.y][topTile.z] && !info->showAllTerrain) + if(!settings["session"]["spectate"].Bool() && !(*info->visibilityMap)[topTile.z][pos.x][pos.y] && !info->showAllTerrain) drawFow(targetSurf); // overlay needs to be drawn over fow, because of artifacts-aura-like spells @@ -1105,7 +1094,7 @@ bool CMapHandler::CMapBlitter::canDrawCurrentTile() const if(settings["session"]["spectate"].Bool()) return true; - const NeighborTilesInfo neighbors(pos, parent->sizes, *info->visibilityMap); + const NeighborTilesInfo neighbors(pos, parent->sizes, info->visibilityMap); return !neighbors.areAllHidden(); } @@ -1136,7 +1125,7 @@ bool CMapHandler::updateObjectsFade() ++iter; else // fade finished { - auto &objs = ttiles[pos.x][pos.y][pos.z].objects; + auto &objs = ttiles[pos.z][pos.x][pos.y].objects; for (auto objIter = objs.begin(); objIter != objs.end(); ++objIter) { if ((*objIter).fadeAnimKey == (*iter).first) @@ -1210,6 +1199,9 @@ bool CMapHandler::printObject(const CGObjectInstance * obj, bool fadein) const int tilesW = bitmap->width()/32; const int tilesH = bitmap->height()/32; + auto ttilesWidth = ttiles.shape()[1]; + auto ttilesHeight = ttiles.shape()[2]; + for(int fx=0; fxpos.x + fx - tilesW+1)>=0 && (obj->pos.x + fx - tilesW+1)pos.y + fy - tilesH+1)>=0 && (obj->pos.y + fy - tilesH+1)pos.x + fx - tilesW + 1) >= 0 && + (obj->pos.x + fx - tilesW + 1) < ttilesWidth - frameW && + (obj->pos.y + fy - tilesH + 1) >= 0 && + (obj->pos.y + fy - tilesH + 1) < ttilesHeight - frameH) { int3 pos(obj->pos.x + fx - tilesW + 1, obj->pos.y + fy - tilesH + 1, obj->pos.z); - TerrainTile2 & curt = ttiles[pos.x][pos.y][pos.z]; + TerrainTile2 & curt = ttiles[pos.z][pos.x][pos.y]; TerrainTileObject toAdd(obj, cr, obj->visitableAt(pos.x, pos.y)); if (fadein && ADVOPT.objectFading) @@ -1252,59 +1247,26 @@ bool CMapHandler::printObject(const CGObjectInstance * obj, bool fadein) bool CMapHandler::hideObject(const CGObjectInstance * obj, bool fadeout) { - //optimized version which reveals weird bugs with missing def name - //auto pos = obj->pos; - - //for (size_t i = pos.x; i > pos.x - obj->getWidth(); i--) - //{ - // for (size_t j = pos.y; j > pos.y - obj->getHeight(); j--) - // { - // int3 t(i, j, pos.z); - // if (!map->isInTheMap(t)) - // continue; - - // auto &objs = ttiles[i][j][pos.z].objects; - // for (size_t x = 0; x < objs.size(); x++) - // { - // auto ourObj = objs[x].obj; - // if (ourObj && ourObj->id == obj->id) - // { - // if (fadeout && ADVOPT.objectFading) // object should be faded == erase is delayed until the end of fadeout - // { - // if (startObjectFade(objs[x], false, t)) - // objs[x].obj = nullptr; //set original pointer to null - // else - // objs.erase(objs.begin() + x); - // } - // else - // objs.erase(objs.begin() + x); - // break; - // } - // } - // } - - //} - - for (size_t i = 0; iwidth; i++) + for(size_t z = 0; z < map->levels(); z++) { - for (size_t j = 0; jheight; j++) + for(size_t x = 0; x < map->width; x++) { - for (size_t k = 0; k<(map->twoLevel ? 2 : 1); k++) + for(size_t y = 0; y < map->height; y++) { - auto &objs = ttiles[(int)i][(int)j][(int)k].objects; - for (size_t x = 0; x < objs.size(); x++) + auto &objs = ttiles[(int)z][(int)x][(int)y].objects; + for(size_t i = 0; i < objs.size(); i++) { - if (objs[x].obj && objs[x].obj->id == obj->id) + if (objs[i].obj && objs[i].obj->id == obj->id) { if (fadeout && ADVOPT.objectFading) // object should be faded == erase is delayed until the end of fadeout { - if (startObjectFade(objs[x], false, int3((si32)i, (si32)j, (si32)k))) - objs[x].obj = nullptr; + if (startObjectFade(objs[i], false, int3((si32)x, (si32)y, (si32)z))) + objs[i].obj = nullptr; else - objs.erase(objs.begin() + x); + objs.erase(objs.begin() + i); } else - objs.erase(objs.begin() + x); + objs.erase(objs.begin() + i); break; } } @@ -1392,7 +1354,7 @@ CMapHandler::CMapHandler() bool CMapHandler::hasObjectHole(const int3 & pos) const { - const TerrainTile2 & tt = ttiles[pos.x][pos.y][pos.z]; + const TerrainTile2 & tt = ttiles[pos.z][pos.x][pos.y]; for(auto & elem : tt.objects) { @@ -1411,7 +1373,7 @@ void CMapHandler::getTerrainDescr(const int3 & pos, std::string & out, bool isRM out = CGI->objtypeh->getObjectName(Obj::FAVORABLE_WINDS); return; } - const TerrainTile2 & tt = ttiles[pos.x][pos.y][pos.z]; + const TerrainTile2 & tt = ttiles[pos.z][pos.x][pos.y]; bool isTile2Terrain = false; out.clear(); diff --git a/client/mapHandler.h b/client/mapHandler.h index 5206e0613..4c440d8ce 100644 --- a/client/mapHandler.h +++ b/client/mapHandler.h @@ -92,7 +92,7 @@ struct MapDrawingInfo { bool scaled; int3 &topTile; // top-left tile in viewport [in tiles] - const std::vector< std::vector< std::vector > > * visibilityMap; + std::shared_ptr> visibilityMap; SDL_Rect * drawBounds; // map rect drawing bounds on screen std::shared_ptr icons; // holds overlay icons for world view mode float scale; // map scale for world view mode (only if scaled == true) @@ -110,9 +110,10 @@ struct MapDrawingInfo bool showAllTerrain; //for expert viewEarth - MapDrawingInfo(int3 &topTile_, const std::vector< std::vector< std::vector > > * visibilityMap_, SDL_Rect * drawBounds_, std::shared_ptr icons_ = nullptr) + MapDrawingInfo(int3 &topTile_, std::shared_ptr> visibilityMap_, SDL_Rect * drawBounds_, std::shared_ptr icons_ = nullptr) : scaled(false), topTile(topTile_), + visibilityMap(visibilityMap_), drawBounds(drawBounds_), icons(icons_), @@ -332,7 +333,7 @@ class CMapHandler void initTerrainGraphics(); void prepareFOWDefs(); public: - PseudoV< PseudoV< PseudoV > > ttiles; //informations about map tiles + boost::multi_array ttiles; //informations about map tiles [z][x][y] int3 sizes; //map size (x = width, y = height, z = number of levels) const CMap * map; @@ -368,7 +369,7 @@ public: //Fog of War cache (not owned) std::vector> FoWfullHide; - std::vector > > hideBitmap; //frame indexes (in FoWfullHide) of graphic that should be used to fully hide a tile + boost::multi_array hideBitmap; //frame indexes (in FoWfullHide) of graphic that should be used to fully hide a tile std::vector> FoWpartialHide; diff --git a/client/windows/CAdvmapInterface.cpp b/client/windows/CAdvmapInterface.cpp index fbf5e9c76..a060780b2 100644 --- a/client/windows/CAdvmapInterface.cpp +++ b/client/windows/CAdvmapInterface.cpp @@ -380,7 +380,7 @@ void CTerrainRect::show(SDL_Surface * to) { if (adventureInt->mode == EAdvMapMode::NORMAL) { - MapDrawingInfo info(adventureInt->position, &LOCPLINT->cb->getVisibilityMap(), &pos); + MapDrawingInfo info(adventureInt->position, LOCPLINT->cb->getVisibilityMap(), &pos); info.otherheroAnim = true; info.anim = adventureInt->anim; info.heroAnim = adventureInt->heroAnim; @@ -407,7 +407,7 @@ void CTerrainRect::showAll(SDL_Surface * to) // world view map is static and doesn't need redraw every frame if (adventureInt->mode == EAdvMapMode::WORLD_VIEW) { - MapDrawingInfo info(adventureInt->position, &LOCPLINT->cb->getVisibilityMap(), &pos, adventureInt->worldViewIcons); + MapDrawingInfo info(adventureInt->position, LOCPLINT->cb->getVisibilityMap(), &pos, adventureInt->worldViewIcons); info.scaled = true; info.scale = adventureInt->worldViewScale; adventureInt->worldViewOptions.adjustDrawingInfo(info); @@ -757,7 +757,7 @@ void CAdvMapInt::fworldViewScale4x() void CAdvMapInt::fswitchLevel() { // with support for future multi-level maps :) - int maxLevels = CGI->mh->map->twoLevel ? 2 : 1; + int maxLevels = CGI->mh->map->levels(); if (maxLevels < 2) return; diff --git a/client/windows/GUIClasses.cpp b/client/windows/GUIClasses.cpp index 537e454fb..867a7abf0 100644 --- a/client/windows/GUIClasses.cpp +++ b/client/windows/GUIClasses.cpp @@ -1514,7 +1514,7 @@ void CPuzzleWindow::showAll(SDL_Surface * to) Rect mapRect = genRect(544, 591, pos.x + 8, pos.y + 7); int3 topTile = grailPos - moveInt; - MapDrawingInfo info(topTile, &LOCPLINT->cb->getVisibilityMap(), &mapRect); + MapDrawingInfo info(topTile, LOCPLINT->cb->getVisibilityMap(), &mapRect); info.puzzleMode = true; info.grailPos = grailPos; CGI->mh->drawTerrainRectNew(to, &info); diff --git a/lib/CGameInfoCallback.cpp b/lib/CGameInfoCallback.cpp index b089ac138..0d0a8d6b0 100644 --- a/lib/CGameInfoCallback.cpp +++ b/lib/CGameInfoCallback.cpp @@ -502,28 +502,29 @@ const TerrainTile * CGameInfoCallback::getTile( int3 tile, bool verbose) const } //TODO: typedef? -std::shared_ptr> CGameInfoCallback::getAllVisibleTiles() const +std::shared_ptr> CGameInfoCallback::getAllVisibleTiles() const { assert(player.is_initialized()); auto team = getPlayerTeam(player.get()); size_t width = gs->map->width; size_t height = gs->map->height; - size_t levels = (gs->map->twoLevel ? 2 : 1); + size_t levels = gs->map->levels(); + auto ptr = new boost::multi_array(boost::extents[levels][width][height]); - boost::multi_array tileArray(boost::extents[width][height][levels]); - - for (size_t x = 0; x < width; x++) - for (size_t y = 0; y < height; y++) - for (size_t z = 0; z < levels; z++) + int3 tile; + for(tile.z = 0; tile.z < levels; tile.z++) + for(tile.x = 0; tile.x < width; tile.x++) + for(tile.y = 0; tile.y < height; tile.y++) { - if (team->fogOfWarMap[x][y][z]) - tileArray[x][y][z] = &gs->map->getTile(int3((si32)x, (si32)y, (si32)z)); + if ((*team->fogOfWarMap)[tile.z][tile.x][tile.y]) + (*ptr)[tile.z][tile.x][tile.y] = &gs->map->getTile(tile); else - tileArray[x][y][z] = nullptr; + (*ptr)[tile.z][tile.x][tile.y] = nullptr; } - return std::make_shared>(tileArray); + + return std::shared_ptr>(ptr); } EBuildingState::EBuildingState CGameInfoCallback::canBuildStructure( const CGTownInstance *t, BuildingID ID ) @@ -694,7 +695,7 @@ CGameInfoCallback::CGameInfoCallback(CGameState *GS, boost::optional > > & CPlayerSpecificInfoCallback::getVisibilityMap() const +std::shared_ptr> CPlayerSpecificInfoCallback::getVisibilityMap() const { //boost::shared_lock lock(*gs->mx); return gs->getPlayerTeam(*player)->fogOfWarMap; diff --git a/lib/CGameInfoCallback.h b/lib/CGameInfoCallback.h index 99ca789c9..53aa5524f 100644 --- a/lib/CGameInfoCallback.h +++ b/lib/CGameInfoCallback.h @@ -185,7 +185,7 @@ public: virtual const CMapHeader * getMapHeader()const; virtual int3 getMapSize() const; //returns size of map - z is 1 for one - level map and 2 for two level map virtual const TerrainTile * getTile(int3 tile, bool verbose = true) const; - virtual std::shared_ptr> getAllVisibleTiles() const; + virtual std::shared_ptr> getAllVisibleTiles() const; virtual bool isInTheMap(const int3 &pos) const; virtual void getVisibleTilesInRange(std::unordered_set &tiles, int3 pos, int radious, int3::EDistanceFormula distanceFormula = int3::DIST_2D) const; virtual void calculatePaths(std::shared_ptr config); @@ -235,7 +235,7 @@ public: virtual int getResourceAmount(Res::ERes type) const; virtual TResources getResourceAmount() const; - virtual const std::vector< std::vector< std::vector > > & getVisibilityMap()const; //returns visibility map + virtual std::shared_ptr> getVisibilityMap() const; //returns visibility map //virtual const PlayerSettings * getPlayerSettings(PlayerColor color) const; }; diff --git a/lib/CGameState.cpp b/lib/CGameState.cpp index cb20200c3..c81a760c6 100644 --- a/lib/CGameState.cpp +++ b/lib/CGameState.cpp @@ -954,19 +954,20 @@ void CGameState::initGrailPosition() static const int BORDER_WIDTH = 9; // grail must be at least 9 tiles away from border // add all not blocked tiles in range - for (int i = BORDER_WIDTH; i < map->width - BORDER_WIDTH ; i++) + + for (int z = 0; z < map->levels(); z++) { - for (int j = BORDER_WIDTH; j < map->height - BORDER_WIDTH; j++) + for(int x = BORDER_WIDTH; x < map->width - BORDER_WIDTH ; x++) { - for (int k = 0; k < (map->twoLevel ? 2 : 1); k++) + for(int y = BORDER_WIDTH; y < map->height - BORDER_WIDTH; y++) { - const TerrainTile &t = map->getTile(int3(i, j, k)); + const TerrainTile &t = map->getTile(int3(x, y, z)); if(!t.blocked && !t.visitable && t.terType.isLand() && t.terType.isPassable() - && (int)map->grailPos.dist2dSQ(int3(i, j, k)) <= (map->grailRadius * map->grailRadius)) - allowedPos.push_back(int3(i,j,k)); + && (int)map->grailPos.dist2dSQ(int3(x, y, z)) <= (map->grailRadius * map->grailRadius)) + allowedPos.push_back(int3(x,y,z)); } } } @@ -1593,20 +1594,13 @@ void CGameState::giveCampaignBonusToHero(CGHeroInstance * hero) void CGameState::initFogOfWar() { logGlobal->debug("\tFog of war"); //FIXME: should be initialized after all bonuses are set + + int layers = map->levels(); for(auto & elem : teams) { - elem.second.fogOfWarMap.resize(map->width); - for(int g=0; gwidth; ++g) - elem.second.fogOfWarMap[g].resize(map->height); - - for(int g=-0; gwidth; ++g) - for(int h=0; hheight; ++h) - elem.second.fogOfWarMap[g][h].resize(map->twoLevel ? 2 : 1, 0); - - for(int g=0; gwidth; ++g) - for(int h=0; hheight; ++h) - for(int v = 0; v < (map->twoLevel ? 2 : 1); ++v) - elem.second.fogOfWarMap[g][h][v] = 0; + auto fow = elem.second.fogOfWarMap; + fow->resize(boost::extents[layers][map->width][map->height]); + std::fill(fow->data(), fow->data() + fow->num_elements(), 0); for(CGObjectInstance *obj : map->objects) { @@ -1616,7 +1610,7 @@ void CGameState::initFogOfWar() getTilesInRange(tiles, obj->getSightCenter(), obj->getSightRadius(), obj->tempOwner, 1); for(int3 tile : tiles) { - elem.second.fogOfWarMap[tile.x][tile.y][tile.z] = 1; + (*elem.second.fogOfWarMap)[tile.z][tile.x][tile.y] = 1; } } } @@ -2019,6 +2013,7 @@ void CGameState::calculatePaths(const CGHeroInstance *hero, CPathsInfo &out) void CGameState::calculatePaths(std::shared_ptr config) { + //FIXME: creating pathfinder is costly, maybe reset / clear is enough? CPathfinder pathfinder(this, config); pathfinder.calculatePaths(); } @@ -2080,7 +2075,7 @@ std::vector CGameState::guardingCreatures (int3 pos) const int3 CGameState::guardingCreaturePosition (int3 pos) const { - return gs->map->guardingCreaturePositions[pos.x][pos.y][pos.z]; + return gs->map->guardingCreaturePositions[pos.z][pos.x][pos.y]; } void CGameState::updateRumor() @@ -2164,7 +2159,7 @@ bool CGameState::isVisible(int3 pos, PlayerColor player) if(player.isSpectator()) return true; - return getPlayerTeam(player)->fogOfWarMap[pos.x][pos.y][pos.z]; + return (*getPlayerTeam(player)->fogOfWarMap)[pos.z][pos.x][pos.y]; } bool CGameState::isVisible( const CGObjectInstance *obj, boost::optional player ) @@ -3096,6 +3091,7 @@ int ArmyDescriptor::getStrength() const TeamState::TeamState() { setNodeType(TEAM); + fogOfWarMap = std::make_shared>(); } TeamState::TeamState(TeamState && other): diff --git a/lib/CPathfinder.cpp b/lib/CPathfinder.cpp index 4365b1554..6ae919db0 100644 --- a/lib/CPathfinder.cpp +++ b/lib/CPathfinder.cpp @@ -33,17 +33,17 @@ void NodeStorage::initialize(const PathfinderOptions & options, const CGameState int3 pos; const PlayerColor player = out.hero->tempOwner; const int3 sizes = gs->getMapSize(); - const auto & fow = static_cast(gs)->getPlayerTeam(player)->fogOfWarMap; + const auto fow = static_cast(gs)->getPlayerTeam(player)->fogOfWarMap; //make 200% sure that these are loop invariants (also a bit shorter code), let compiler do the rest(loop unswitching) const bool useFlying = options.useFlying; const bool useWaterWalking = options.useWaterWalking; - for(pos.x=0; pos.x < sizes.x; ++pos.x) + for(pos.z=0; pos.z < sizes.z; ++pos.z) { - for(pos.y=0; pos.y < sizes.y; ++pos.y) + for(pos.x=0; pos.x < sizes.x; ++pos.x) { - for(pos.z=0; pos.z < sizes.z; ++pos.z) + for(pos.y=0; pos.y < sizes.y; ++pos.y) { const TerrainTile * tile = &gs->map->getTile(pos); if(tile->terType.isWater()) @@ -1304,7 +1304,7 @@ void CGPath::convert(ui8 mode) CPathsInfo::CPathsInfo(const int3 & Sizes, const CGHeroInstance * hero_) : sizes(Sizes), hero(hero_) { - nodes.resize(boost::extents[sizes.x][sizes.y][sizes.z][ELayer::NUM_LAYERS]); + nodes.resize(boost::extents[ELayer::NUM_LAYERS][sizes.z][sizes.x][sizes.y]); } CPathsInfo::~CPathsInfo() = default; @@ -1336,11 +1336,11 @@ bool CPathsInfo::getPath(CGPath & out, const int3 & dst) const const CGPathNode * CPathsInfo::getNode(const int3 & coord) const { - auto landNode = &nodes[coord.x][coord.y][coord.z][ELayer::LAND]; + auto landNode = &nodes[ELayer::LAND][coord.z][coord.x][coord.y]; if(landNode->reachable()) return landNode; else - return &nodes[coord.x][coord.y][coord.z][ELayer::SAIL]; + return &nodes[ELayer::SAIL][coord.z][coord.x][coord.y]; } PathNodeInfo::PathNodeInfo() diff --git a/lib/CPathfinder.h b/lib/CPathfinder.h index 0d9aac277..2bd7e5790 100644 --- a/lib/CPathfinder.h +++ b/lib/CPathfinder.h @@ -178,7 +178,7 @@ struct DLL_LINKAGE CPathsInfo const CGHeroInstance * hero; int3 hpos; int3 sizes; - boost::multi_array nodes; //[w][h][level][layer] + boost::multi_array nodes; //[layer][level][w][h] CPathsInfo(const int3 & Sizes, const CGHeroInstance * hero_); ~CPathsInfo(); @@ -189,7 +189,7 @@ struct DLL_LINKAGE CPathsInfo STRONG_INLINE CGPathNode * getNode(const int3 & coord, const ELayer layer) { - return &nodes[coord.x][coord.y][coord.z][layer]; + return &nodes[layer][coord.z][coord.x][coord.y]; } }; diff --git a/lib/CPlayerState.h b/lib/CPlayerState.h index 5a2921ff0..1caafa314 100644 --- a/lib/CPlayerState.h +++ b/lib/CPlayerState.h @@ -81,7 +81,7 @@ public: TeamID id; //position in gameState::teams std::set players; // members of this team //TODO: boost::array, bool if possible - std::vector > > fogOfWarMap; //true - visible, false - hidden + std::shared_ptr> fogOfWarMap; //[z][x][y] true - visible, false - hidden TeamState(); TeamState(TeamState && other); diff --git a/lib/IGameCallback.cpp b/lib/IGameCallback.cpp index 337eae901..cf1d235f3 100644 --- a/lib/IGameCallback.cpp +++ b/lib/IGameCallback.cpp @@ -38,14 +38,13 @@ void CPrivilegedInfoCallback::getFreeTiles(std::vector & tiles) const { std::vector floors; - for (int b = 0; b < (gs->map->twoLevel ? 2 : 1); ++b) + for(int b = 0; b < gs->map->levels(); ++b) { floors.push_back(b); } const TerrainTile *tinfo; for (auto zd : floors) { - for (int xd = 0; xd < gs->map->width; xd++) { for (int yd = 0; yd < gs->map->height; yd++) @@ -80,8 +79,8 @@ void CPrivilegedInfoCallback::getTilesInRange(std::unordered_setfogOfWarMap[xd][yd][pos.z]==0) - || (mode == -1 && team->fogOfWarMap[xd][yd][pos.z]==1) + || (mode == 1 && (*team->fogOfWarMap)[pos.z][xd][yd] == 0) + || (mode == -1 && (*team->fogOfWarMap)[pos.z][xd][yd] == 1) ) tiles.insert(int3(xd,yd,pos.z)); } @@ -103,7 +102,7 @@ void CPrivilegedInfoCallback::getAllTiles(std::unordered_set & std::vector floors; if(level == -1) { - for(int b = 0; b < (gs->map->twoLevel ? 2 : 1); ++b) + for(int b = 0; b < gs->map->levels(); ++b) { floors.push_back(b); } diff --git a/lib/NetPacksLib.cpp b/lib/NetPacksLib.cpp index 7efc87096..3fa981434 100644 --- a/lib/NetPacksLib.cpp +++ b/lib/NetPacksLib.cpp @@ -177,8 +177,9 @@ DLL_LINKAGE void SetMovePoints::applyGs(CGameState *gs) DLL_LINKAGE void FoWChange::applyGs(CGameState *gs) { TeamState * team = gs->getPlayerTeam(player); + auto fogOfWarMap = team->fogOfWarMap; for(int3 t : tiles) - team->fogOfWarMap[t.x][t.y][t.z] = mode; + (*fogOfWarMap)[t.z][t.x][t.y] = mode; if (mode == 0) //do not hide too much { std::unordered_set tilesRevealed; @@ -200,7 +201,7 @@ DLL_LINKAGE void FoWChange::applyGs(CGameState *gs) } } for(int3 t : tilesRevealed) //probably not the most optimal solution ever - team->fogOfWarMap[t.x][t.y][t.z] = 1; + (*fogOfWarMap)[t.z][t.x][t.y] = 1; } } @@ -561,8 +562,9 @@ void TryMoveHero::applyGs(CGameState *gs) gs->map->addBlockVisTiles(h); } + auto fogOfWarMap = gs->getPlayerTeam(h->getOwner())->fogOfWarMap; for(int3 t : fowRevealed) - gs->getPlayerTeam(h->getOwner())->fogOfWarMap[t.x][t.y][t.z] = 1; + (*fogOfWarMap)[t.z][t.x][t.y] = 1; } DLL_LINKAGE void NewStructures::applyGs(CGameState *gs) diff --git a/lib/PathfinderUtil.h b/lib/PathfinderUtil.h index 7070d82c5..0e750e9d4 100644 --- a/lib/PathfinderUtil.h +++ b/lib/PathfinderUtil.h @@ -14,13 +14,13 @@ namespace PathfinderUtil { - using FoW = std::vector > >; + using FoW = std::shared_ptr>; using ELayer = EPathfindingLayer; template - CGPathNode::EAccessibility evaluateAccessibility(const int3 & pos, const TerrainTile * tinfo, const FoW & fow, const PlayerColor player, const CGameState * gs) + CGPathNode::EAccessibility evaluateAccessibility(const int3 & pos, const TerrainTile * tinfo, FoW fow, const PlayerColor player, const CGameState * gs) { - if(!fow[pos.x][pos.y][pos.z]) + if(!(*fow)[pos.z][pos.x][pos.y]) return CGPathNode::BLOCKED; switch(layer) diff --git a/lib/battle/BattleInfo.cpp b/lib/battle/BattleInfo.cpp index 9ffbbd2c5..f49d1e9a6 100644 --- a/lib/battle/BattleInfo.cpp +++ b/lib/battle/BattleInfo.cpp @@ -522,10 +522,15 @@ CStack * BattleInfo::getStack(int stackID, bool onlyAlive) return const_cast(battleGetStackByID(stackID, onlyAlive)); } -BattleInfo::BattleInfo() - : round(-1), activeStack(-1), town(nullptr), tile(-1,-1,-1), - battlefieldType(BattleField::NONE), terrainType(), - tacticsSide(0), tacticDistance(0) +BattleInfo::BattleInfo(): + round(-1), + activeStack(-1), + town(nullptr), + tile(-1,-1,-1), + battlefieldType(BattleField::NONE), + terrainType(), + tacticsSide(0), + tacticDistance(0) { setBattle(this); setNodeType(BATTLE); diff --git a/lib/mapObjects/ObjectTemplate.cpp b/lib/mapObjects/ObjectTemplate.cpp index 53b410de4..40f9693df 100644 --- a/lib/mapObjects/ObjectTemplate.cpp +++ b/lib/mapObjects/ObjectTemplate.cpp @@ -322,15 +322,15 @@ void ObjectTemplate::readJson(const JsonNode &node, const bool withTerrain) size_t height = mask.size(); size_t width = 0; - for (auto & line : mask) + for(auto & line : mask) vstd::amax(width, line.String().size()); setSize((ui32)width, (ui32)height); - for (size_t i=0; igetWidth(); ++fx) + const int zVal = obj->pos.z; + for(int fx = 0; fx < obj->getWidth(); ++fx) { - for(int fy=0; fygetHeight(); ++fy) + int xVal = obj->pos.x - fx; + for(int fy = 0; fy < obj->getHeight(); ++fy) { - int xVal = obj->pos.x - fx; int yVal = obj->pos.y - fy; - int zVal = obj->pos.z; - if(xVal>=0 && xVal=0 && yVal=0 && xVal < width && yVal>=0 && yVal < height) { - TerrainTile & curt = terrain[xVal][yVal][zVal]; + TerrainTile & curt = terrain[zVal][xVal][yVal]; if(total || obj->visitableAt(xVal, yVal)) { curt.visitableObjects -= obj; @@ -298,22 +302,22 @@ void CMap::removeBlockVisTiles(CGObjectInstance * obj, bool total) void CMap::addBlockVisTiles(CGObjectInstance * obj) { - for(int fx=0; fxgetWidth(); ++fx) + const int zVal = obj->pos.z; + for(int fx = 0; fx < obj->getWidth(); ++fx) { - for(int fy=0; fygetHeight(); ++fy) + int xVal = obj->pos.x - fx; + for(int fy = 0; fy < obj->getHeight(); ++fy) { - int xVal = obj->pos.x - fx; int yVal = obj->pos.y - fy; - int zVal = obj->pos.z; - if(xVal>=0 && xVal=0 && yVal=0 && xVal < width && yVal >= 0 && yVal < height) { - TerrainTile & curt = terrain[xVal][yVal][zVal]; - if( obj->visitableAt(xVal, yVal)) + TerrainTile & curt = terrain[zVal][xVal][yVal]; + if(obj->visitableAt(xVal, yVal)) { curt.visitableObjects.push_back(obj); curt.visitable = true; } - if( obj->blockingAt(xVal, yVal)) + if(obj->blockingAt(xVal, yVal)) { curt.blockingObjects.push_back(obj); curt.blocked = true; @@ -326,12 +330,14 @@ void CMap::addBlockVisTiles(CGObjectInstance * obj) void CMap::calculateGuardingGreaturePositions() { int levels = twoLevel ? 2 : 1; - for (int i=0; iinitTerrain(); // Read terrain - for(int a = 0; a < 2; ++a) + int3 pos; + for(pos.z = 0; pos.z < 2; ++pos.z) { - if(a == 1 && !map->twoLevel) + if(pos.z == 1 && !map->twoLevel) { break; } - for(int c = 0; c < map->width; c++) + //OH3 format is [z][y][x] + for(pos.y = 0; pos.y < map->height; pos.y++) { - for(int z = 0; z < map->height; z++) + for(pos.x = 0; pos.x < map->width; pos.x++) { - auto & tile = map->getTile(int3(z, c, a)); + auto & tile = map->getTile(pos); tile.terType = Terrain::createTerrainTypeH3M(reader.readUInt8()); tile.terView = reader.readUInt8(); tile.riverType = RIVER_NAMES[reader.readUInt8()]; diff --git a/lib/rmg/CMapGenerator.cpp b/lib/rmg/CMapGenerator.cpp index 2fd6fdc41..60f54eee1 100644 --- a/lib/rmg/CMapGenerator.cpp +++ b/lib/rmg/CMapGenerator.cpp @@ -162,8 +162,8 @@ std::string CMapGenerator::getMapDescription() const std::stringstream ss; ss << boost::str(boost::format(std::string("Map created by the Random Map Generator.\nTemplate was %s, Random seed was %d, size %dx%d") + - ", levels %s, players %d, computers %d, water %s, monster %s, VCMI map") % mapTemplate->getName() % - randomSeed % map->map().width % map->map().height % (map->map().twoLevel ? "2" : "1") % static_cast(mapGenOptions.getPlayerCount()) % + ", levels %d, players %d, computers %d, water %s, monster %s, VCMI map") % mapTemplate->getName() % + randomSeed % map->map().width % map->map().height % map->map().levels() % static_cast(mapGenOptions.getPlayerCount()) % static_cast(mapGenOptions.getCompOnlyPlayerCount()) % waterContentStr[mapGenOptions.getWaterContent()] % monsterStrengthStr[monsterStrengthIndex]); diff --git a/lib/rmg/CZonePlacer.cpp b/lib/rmg/CZonePlacer.cpp index c95b4b359..8200895a7 100644 --- a/lib/rmg/CZonePlacer.cpp +++ b/lib/rmg/CZonePlacer.cpp @@ -498,24 +498,24 @@ void CZonePlacer::assignZones(CRandomGenerator * rand) zone->setPos(int3(total.x / size, total.y / size, total.z / size)); }; - int levels = map.map().twoLevel ? 2 : 1; + int levels = map.map().levels(); /* 1. Create Voronoi diagram 2. find current center of mass for each zone. Move zone to that center to balance zones sizes */ - for (int i = 0; igetPos().z == k) + if (zone.second->getPos().z == pos.z) distances.push_back(std::make_pair(zone.second, (float)pos.dist2dSQ(zone.second->getPos()))); else distances.push_back(std::make_pair(zone.second, std::numeric_limits::max())); @@ -533,17 +533,16 @@ void CZonePlacer::assignZones(CRandomGenerator * rand) for (auto zone : zones) zone.second->clearTiles(); //now populate them again - for (int i=0; igetPos().z == k) + if (zone.second->getPos().z == pos.z) distances.push_back (std::make_pair(zone.second, metric(pos, zone.second->getPos()))); else distances.push_back (std::make_pair(zone.second, std::numeric_limits::max())); diff --git a/lib/rmg/Functions.cpp b/lib/rmg/Functions.cpp index 9afd30c22..210bf9fe2 100644 --- a/lib/rmg/Functions.cpp +++ b/lib/rmg/Functions.cpp @@ -175,46 +175,4 @@ void createObstaclesCommon2(RmgMap & map, CRandomGenerator & generator) } } } - - //tighten obstacles to improve visuals - - /*for (int i = 0; i < 3; ++i) - { - int blockedTiles = 0; - int freeTiles = 0; - - for (int z = 0; z < (map.map().twoLevel ? 2 : 1); z++) - { - for (int x = 0; x < map.map().width; x++) - { - for (int y = 0; y < map.map().height; y++) - { - int3 tile(x, y, z); - if (!map.isPossible(tile)) //only possible tiles can change - continue; - - int blockedNeighbours = 0; - int freeNeighbours = 0; - map.foreach_neighbour(tile, [&map, &blockedNeighbours, &freeNeighbours](int3 &pos) - { - if (map.isBlocked(pos)) - blockedNeighbours++; - if (map.isFree(pos)) - freeNeighbours++; - }); - if (blockedNeighbours > 4) - { - map.setOccupied(tile, ETileType::BLOCKED); - blockedTiles++; - } - else if (freeNeighbours > 4) - { - map.setOccupied(tile, ETileType::FREE); - freeTiles++; - } - } - } - } - logGlobal->trace("Set %d tiles to BLOCKED and %d tiles to FREE", blockedTiles, freeTiles); - }*/ } diff --git a/lib/rmg/ObjectManager.h b/lib/rmg/ObjectManager.h index ff3148af6..071b3ab8a 100644 --- a/lib/rmg/ObjectManager.h +++ b/lib/rmg/ObjectManager.h @@ -39,29 +39,30 @@ public: public: MODIFICATOR(ObjectManager); - + + void process() override; void init() override; - + void addRequiredObject(CGObjectInstance * obj, si32 guardStrength=0); void addCloseObject(CGObjectInstance * obj, si32 guardStrength = 0); void addNearbyObject(CGObjectInstance * obj, CGObjectInstance * nearbyTarget); - + bool createRequiredObjects(); - + int3 findPlaceForObject(const rmg::Area & searchArea, rmg::Object & obj, si32 min_dist, OptimizeType optimizer) const; int3 findPlaceForObject(const rmg::Area & searchArea, rmg::Object & obj, std::function weightFunction, OptimizeType optimizer) const; - + rmg::Path placeAndConnectObject(const rmg::Area & searchArea, rmg::Object & obj, si32 min_dist, bool isGuarded, bool onlyStraight, OptimizeType optimizer) const; rmg::Path placeAndConnectObject(const rmg::Area & searchArea, rmg::Object & obj, std::function weightFunction, bool isGuarded, bool onlyStraight, OptimizeType optimizer) const; - + CGCreature * chooseGuard(si32 strength, bool zoneGuard = false); bool addGuard(rmg::Object & object, si32 strength, bool zoneGuard = false); void placeObject(rmg::Object & object, bool guarded, bool updateDistance); - + void updateDistances(const rmg::Object & obj); void createDistancesPriorityQueue(); - + const rmg::Area & getVisitableArea() const; std::vector getMines() const; diff --git a/lib/rmg/RmgMap.cpp b/lib/rmg/RmgMap.cpp index 732bb59af..95c40acbb 100644 --- a/lib/rmg/RmgMap.cpp +++ b/lib/rmg/RmgMap.cpp @@ -73,8 +73,8 @@ void RmgMap::initTiles(CMapGenerator & generator) { mapInstance->initTerrain(); - tiles.resize(boost::extents[mapInstance->width][mapInstance->height][mapInstance->twoLevel ? 2 : 1]); - zoneColouring.resize(boost::extents[mapInstance->width][mapInstance->height][mapInstance->twoLevel ? 2 : 1]); + tiles.resize(boost::extents[mapInstance->width][mapInstance->height][mapInstance->levels()]); + zoneColouring.resize(boost::extents[mapInstance->width][mapInstance->height][mapInstance->levels()]); //init native town count with 0 for (auto faction : VLC->townh->getAllowedFactions()) @@ -302,7 +302,7 @@ void RmgMap::dump(bool zoneId) const { static int id = 0; std::ofstream out(boost::to_string(boost::format("zone_%d.txt") % id++)); - int levels = mapInstance->twoLevel ? 2 : 1; + int levels = mapInstance->levels(); int width = mapInstance->width; int height = mapInstance->height; for (int k = 0; k < levels; k++) diff --git a/lib/rmg/Zone.cpp b/lib/rmg/Zone.cpp index d859cf449..7323a27fd 100644 --- a/lib/rmg/Zone.cpp +++ b/lib/rmg/Zone.cpp @@ -369,16 +369,16 @@ void Modificator::dump() { std::ofstream out(boost::to_string(boost::format("seed_%d_modzone_%d_%s.txt") % generator.getRandomSeed() % zone.getId() % getName())); auto & mapInstance = map.map(); - int levels = mapInstance.twoLevel ? 2 : 1; + int levels = mapInstance.levels(); int width = mapInstance.width; int height = mapInstance.height; - for (int k = 0; k < levels; k++) + for(int z = 0; z < levels; z++) { for(int j=0; j(); } } + + template + void load(boost::multi_array & data) + { + ui32 length = readAndCheckLength(); + ui32 x, y, z; + load(x); + load(y); + load(z); + data.resize(boost::extents[x][y][z]); + assert(length == data.num_elements()); //x*y*z should be equal to number of elements + for(ui32 i = 0; i < length; i++) + load(data.data()[i]); + } }; class DLL_LINKAGE CLoadFile : public IBinaryReader diff --git a/lib/serializer/BinarySerializer.h b/lib/serializer/BinarySerializer.h index 7d3914205..4b6df7d8d 100644 --- a/lib/serializer/BinarySerializer.h +++ b/lib/serializer/BinarySerializer.h @@ -350,6 +350,18 @@ public: save((ui8)0); } } + + template + void save(const boost::multi_array &data) + { + ui32 length = data.num_elements(); + *this & length; + auto shape = data.shape(); + ui32 x = shape[0], y = shape[1], z = shape[2]; + *this & x & y & z; + for(ui32 i = 0; i < length; i++) + save(data.data()[i]); + } }; class DLL_LINKAGE CSaveFile : public IBinaryWriter diff --git a/lib/spells/AdventureSpellMechanics.cpp b/lib/spells/AdventureSpellMechanics.cpp index 3093f300d..b428db9e3 100644 --- a/lib/spells/AdventureSpellMechanics.cpp +++ b/lib/spells/AdventureSpellMechanics.cpp @@ -571,7 +571,7 @@ ESpellCastResult ViewMechanics::applyAdventureEffects(SpellCastEnvironment * env const auto spellLevel = parameters.caster->getSpellSchoolLevel(owner); - const auto & fowMap = env->getCb()->getPlayerTeam(parameters.caster->getOwner())->fogOfWarMap; + const auto fowMap = env->getCb()->getPlayerTeam(parameters.caster->getOwner())->fogOfWarMap; for(const CGObjectInstance * obj : env->getMap()->objects) { @@ -580,7 +580,7 @@ ESpellCastResult ViewMechanics::applyAdventureEffects(SpellCastEnvironment * env { ObjectPosInfo posInfo(obj); - if(fowMap[posInfo.pos.x][posInfo.pos.y][posInfo.pos.z] == 0) + if((*fowMap)[posInfo.pos.x][posInfo.pos.y][posInfo.pos.z] == 0) pack.objectPositions.push_back(posInfo); } } diff --git a/server/CGameHandler.cpp b/server/CGameHandler.cpp index bab26bbc9..5dcd2dcea 100644 --- a/server/CGameHandler.cpp +++ b/server/CGameHandler.cpp @@ -1993,12 +1993,14 @@ void CGameHandler::newTurn() fw.mode = 1; fw.player = player; // find all hidden tiles - const auto & fow = getPlayerTeam(player)->fogOfWarMap; - for (size_t i=0; ifogOfWarMap; + + auto shape = fow->shape(); + for(size_t z = 0; z < shape[0]; z++) + for(size_t x = 0; x < shape[1]; x++) + for(size_t y = 0; y < shape[2]; y++) + if (!(*fow)[z][x][y]) + fw.tiles.insert(int3(x, y, z)); sendAndApply (&fw); } @@ -7012,13 +7014,15 @@ void CGameHandler::handleCheatCode(std::string & cheat, PlayerColor player, cons fc.mode = (cheat == "vcmieagles" ? 1 : 0); fc.player = player; const auto & fowMap = gs->getPlayerTeam(player)->fogOfWarMap; - auto hlp_tab = new int3[gs->map->width * gs->map->height * (gs->map->twoLevel ? 2 : 1)]; + auto hlp_tab = new int3[gs->map->width * gs->map->height * (gs->map->levels())]; int lastUnc = 0; - for (int i = 0; i < gs->map->width; i++) - for (int j = 0; j < gs->map->height; j++) - for (int k = 0; k < (gs->map->twoLevel ? 2 : 1); k++) - if (!fowMap.at(i).at(j).at(k) || !fc.mode) - hlp_tab[lastUnc++] = int3(i, j, k); + + for(int z = 0; z < gs->map->levels(); z++) + for(int x = 0; x < gs->map->width; x++) + for(int y = 0; y < gs->map->height; y++) + if(!(*fowMap)[z][x][y] || !fc.mode) + hlp_tab[lastUnc++] = int3(x, y, z); + fc.tiles.insert(hlp_tab, hlp_tab + lastUnc); delete [] hlp_tab; sendAndApply(&fc);