From 9df56bfa857e9c8f5c0604c106978e6650de3fa5 Mon Sep 17 00:00:00 2001 From: Ivan Shibanov Date: Sun, 19 May 2024 14:43:40 -0400 Subject: [PATCH] Add a node cache struct --- VisualStudio/fheroes2/sources.props | 2 + fheroes2-vs2019.vcxproj | 6 -- src/fheroes2/maps/map_generator.cpp | 101 +++++++++++++++++++--------- 3 files changed, 73 insertions(+), 36 deletions(-) diff --git a/VisualStudio/fheroes2/sources.props b/VisualStudio/fheroes2/sources.props index 9afb1ece581..ba58199a244 100644 --- a/VisualStudio/fheroes2/sources.props +++ b/VisualStudio/fheroes2/sources.props @@ -200,6 +200,7 @@ + @@ -408,6 +409,7 @@ + diff --git a/fheroes2-vs2019.vcxproj b/fheroes2-vs2019.vcxproj index 8bf51e40055..c7bc18038a5 100644 --- a/fheroes2-vs2019.vcxproj +++ b/fheroes2-vs2019.vcxproj @@ -18,12 +18,6 @@ x64 - - - - - - {DD8F214C-C405-4951-8F98-66B969BA8E08} Win32Proj diff --git a/src/fheroes2/maps/map_generator.cpp b/src/fheroes2/maps/map_generator.cpp index 460bfc9eaa3..ac4b8ad6a42 100644 --- a/src/fheroes2/maps/map_generator.cpp +++ b/src/fheroes2/maps/map_generator.cpp @@ -56,6 +56,12 @@ namespace Maps::Generator const std::vector playerStartingTerrain = { Ground::GRASS, Ground::DIRT, Ground::SNOW, Ground::LAVA, Ground::WASTELAND }; const std::vector neutralTerrain = { Ground::GRASS, Ground::DIRT, Ground::SNOW, Ground::LAVA, Ground::WASTELAND, Ground::BEACH, Ground::SWAMP, Ground::DESERT }; + int convertExtendedIndex( int index, size_t width ) + { + const size_t originalWidth = width - 2; + return static_cast( ( index / originalWidth + 1 ) * width + ( index % originalWidth ) + 1 ); + } + enum { TOP = 0, @@ -91,6 +97,38 @@ namespace Maps::Generator {} }; + struct NodeCache + { + size_t extendedWidth = 0; + std::vector data; + + NodeCache( int32_t width, int32_t height ) + : extendedWidth( width + 2 ) + , data( extendedWidth * ( height + 2 ) ) + { + for ( int y = 0; y < height; ++y ) { + const int rowIndex = y * width; + for ( int x = 0; x < width; ++x ) { + const int index = rowIndex + x; + Node & node = data[convertExtendedIndex( index, extendedWidth )]; + + node.index = index; + node.type = NodeType::OPEN; + } + } + } + + Node & getNode( int32_t index ) + { + return data[convertExtendedIndex( index, extendedWidth )]; + } + + Node & getNode( int32_t index, int32_t offset ) + { + return data[convertExtendedIndex( index, extendedWidth ) + offset]; + } + }; + struct Region { public: @@ -123,7 +161,7 @@ namespace Maps::Generator } }; - std::vector GetDirectionOffsets( const int width ) + std::vector getDirectionOffsets( const int width ) { std::vector offsets( 8 ); offsets[TOP] = -width; @@ -137,18 +175,12 @@ namespace Maps::Generator return offsets; } - uint16_t GetDirectionBitmask( uint8_t direction, bool reflect = false ) + uint16_t getDirectionBitmask( uint8_t direction, bool reflect = false ) { return 1 << ( reflect ? ( direction + 4 ) % 8 : direction ); } - int ConvertExtendedIndex( int index, uint32_t width ) - { - const uint32_t originalWidth = width - 2; - return static_cast( ( index / originalWidth + 1 ) * width + ( index % originalWidth ) + 1 ); - } - - void CheckAdjacentTiles( std::vector & rawData, Region & region, uint32_t rawDataWidth, const std::vector & offsets ) + void checkAdjacentTiles( NodeCache & rawData, Region & region, const std::vector & offsets ) { Node & previousNode = region._nodes[region._lastProcessedNode]; const int nodeIndex = previousNode.index; @@ -163,9 +195,8 @@ namespace Maps::Generator if ( direction > 3 && Rand::Get( 1 ) ) { break; } - const int newIndex = ConvertExtendedIndex( nodeIndex, rawDataWidth ) + offsets[direction]; - Node & newTile = rawData[newIndex]; - if ( newTile.passable & GetDirectionBitmask( direction, true ) ) { + Node & newTile = rawData.getNode( nodeIndex, offsets[direction] ); + if ( newTile.passable & getDirectionBitmask( direction, true ) ) { if ( newTile.region == 0 && newTile.type == NodeType::OPEN ) { newTile.region = region._id; region._nodes.push_back( newTile ); @@ -178,17 +209,38 @@ namespace Maps::Generator } } - void RegionExpansion( std::vector & rawData, uint32_t rawDataWidth, Region & region, const std::vector & offsets ) + void RegionExpansion( NodeCache & rawData, Region & region, const std::vector & offsets ) { // Process only "open" nodes that exist at the start of the loop and ignore what's added const size_t nodesEnd = region._nodes.size(); while ( region._lastProcessedNode < nodesEnd ) { - CheckAdjacentTiles( rawData, region, rawDataWidth, offsets ); + checkAdjacentTiles( rawData, region, offsets ); ++region._lastProcessedNode; } } + bool markObject( NodeCache & data, const ObjectInfo & info, const fheroes2::Point & mainTilePos ) + { + // Active action object parts must be placed on a tile without any other objects. + // Only ground parts should be checked for this condition. + for ( const auto & objectPart : info.groundLevelParts ) { + if ( objectPart.layerType == Maps::SHADOW_LAYER || objectPart.layerType == Maps::TERRAIN_LAYER ) { + // Shadow and terrain layer parts are ignored. + continue; + } + + const fheroes2::Point pos{ mainTilePos.x + objectPart.tileOffset.x, mainTilePos.y + objectPart.tileOffset.y }; + if ( !Maps::isValidAbsPoint( pos.x, pos.y ) ) { + return false; + } + + data.getNode( pos.x ); + } + + return true; + } + bool setObjectOnTile( Map_Format::MapFormat & mapFormat, Tiles & tile, const ObjectGroup groupType, const int32_t objectIndex ) { const auto & objectInfo = Maps::getObjectInfo( groupType, objectIndex ); @@ -302,6 +354,8 @@ namespace Maps::Generator const int32_t width = world.w(); const int32_t height = world.h(); + NodeCache data( width, height ); + auto mapBoundsCheck = [width, height]( int x, int y ) { x = std::max( std::min( x, width - 1 ), 0 ); y = std::max( std::min( y, height - 1 ), 0 ); @@ -316,19 +370,6 @@ namespace Maps::Generator // const int minimumRegionCount = playerCount + 1; const int expectedRegionCount = ( width * height ) / static_cast( config.regionSizeLimit ); - const uint32_t extendedWidth = width + 2; - std::vector data( static_cast( extendedWidth ) * ( height + 2 ) ); - for ( int y = 0; y < height; ++y ) { - const int rowIndex = y * width; - for ( int x = 0; x < width; ++x ) { - const int index = rowIndex + x; - Node & node = data[ConvertExtendedIndex( index, extendedWidth )]; - - node.index = index; - node.type = NodeType::OPEN; - } - } - // Step 2. Determine region layout and placement // Insert empty region that represents water and map edges std::vector mapRegions = { { 0, 0, NEUTRAL_COLOR, Ground::WATER, 0 } }; @@ -363,19 +404,19 @@ namespace Maps::Generator const uint32_t regionID = static_cast( mapRegions.size() ); mapRegions.emplace_back( regionID, centerTile, regionColor, groundType, config.regionSizeLimit ); - data[ConvertExtendedIndex( centerTile, extendedWidth )].region = regionID; + data.getNode( centerTile ).region = regionID; } } // Step 3. Grow all regions one step at the time so they would compete for space - const std::vector & offsets = GetDirectionOffsets( static_cast( extendedWidth ) ); + const std::vector & offsets = getDirectionOffsets( static_cast( width + 2 ) ); bool stillRoomToExpand = true; while ( stillRoomToExpand ) { stillRoomToExpand = false; // Skip the border region for ( size_t regionID = 1; regionID < mapRegions.size(); ++regionID ) { Region & region = mapRegions[regionID]; - RegionExpansion( data, extendedWidth, region, offsets ); + RegionExpansion( data, region, offsets ); if ( region._lastProcessedNode != region._nodes.size() ) stillRoomToExpand = true; }