diff --git a/src/fheroes2/gui/interface_gamearea.cpp b/src/fheroes2/gui/interface_gamearea.cpp index d3f37917a4..cd34034025 100644 --- a/src/fheroes2/gui/interface_gamearea.cpp +++ b/src/fheroes2/gui/interface_gamearea.cpp @@ -473,8 +473,6 @@ void Interface::GameArea::Redraw( fheroes2::Image & dst, int flag, bool isPuzzle TileUnfitRenderObjectInfo tileUnfit; - const Heroes * currentHero = drawHeroes ? GetFocusHeroes() : nullptr; - // TODO: Dragon City with Object ICN Type OBJ_ICN_TYPE_OBJNMUL2 and object index 46 is a bottom layer sprite. // TODO: When a hero standing besides this turns a part of the hero is visible. This can be fixed only by some hack. @@ -695,6 +693,8 @@ void Interface::GameArea::Redraw( fheroes2::Image & dst, int flag, bool isPuzzle // Draw hero's route. It should be drawn on top of everything. const bool drawRoutes = ( flag & LEVEL_ROUTES ) != 0; + const Heroes * currentHero = drawHeroes ? GetFocusHeroes() : nullptr; + if ( drawRoutes && ( currentHero != nullptr ) && currentHero->GetPath().isShow() ) { const Route::Path & path = currentHero->GetPath(); int32_t greenColorSteps = path.GetAllowedSteps(); diff --git a/src/fheroes2/maps/maps_tiles.cpp b/src/fheroes2/maps/maps_tiles.cpp index e508dc2abb..91a6db4ba1 100644 --- a/src/fheroes2/maps/maps_tiles.cpp +++ b/src/fheroes2/maps/maps_tiles.cpp @@ -254,6 +254,8 @@ namespace case MP2::OBJ_BARRIER: case MP2::OBJ_MAGIC_WELL: case MP2::OBJ_NOTHING_SPECIAL: + case MP2::OBJ_OASIS: + case MP2::OBJ_NON_ACTION_OASIS: return true; default: break; @@ -694,6 +696,9 @@ void Maps::Tile::updatePassability() return; } + // If this assertion blows up then you are calling this method more than once! + assert( _tilePassabilityDirections == getTileIndependentPassability() ); + // Verify the neighboring tiles. // If a tile contains a tall object then it affects the passability of diagonal moves to the top from the current tile. if ( ( _tilePassabilityDirections & Direction::TOP_LEFT ) && isValidDirection( _index, Direction::LEFT ) ) { @@ -712,26 +717,21 @@ void Maps::Tile::updatePassability() } } - // Get object type but ignore heroes as they are "temporary" objects. - const MP2::MapObjectType objectType = getMainObjectType( false ); - if ( MP2::isOffGameActionObject( objectType ) ) { - // This is an action object. Action object passability is not affected by other objects. - return; - } - - if ( isShadow() ) { - // The whole tile contains only shadow object parts. All shadows do not affect passability. + if ( getTileIndependentPassability() == DIRECTION_ALL ) { + // This tile is free of objects. Its passability must not be affected by any other objects. + // + // The original game does not consider this case and it is actually a bug + // as some tiles in the original game are marked inaccessible while no object exists on them. return; } - if ( _mainObjectPart.icnType == MP2::OBJ_ICN_TYPE_UNKNOWN ) { - // The main object part is not set. Ignore the tile. - // TODO: this is wrong as tiles can have object parts at the ground layer. Fix it! - return; - } + // If this assertion blows up then something is really off for the passability logic! + assert( !isShadow() && !isPassabilityTransparent() ); - if ( _mainObjectPart.isPassabilityTransparent() ) { - // This object does not affect passability. + // Get object type but ignore heroes as they are "temporary" objects. + const MP2::MapObjectType objectType = getMainObjectType( false ); + if ( MP2::isOffGameActionObject( objectType ) ) { + // This is an action object. Action object passability is not affected by other objects. return; } @@ -772,6 +772,10 @@ void Maps::Tile::updatePassability() } } + // Even thought the passability of the tile below can be not blocked from the top, + // in the original game such a requirement is ignored. + // So, we have to follow the same logic. + // Count how many objects are there excluding shadows, roads and river streams. const std::ptrdiff_t validBottomLayerObjects = std::count_if( _groundObjectPart.begin(), _groundObjectPart.end(), []( const auto & part ) { if ( isObjectPartShadow( part ) ) { @@ -1041,7 +1045,7 @@ bool Maps::Tile::isPassabilityTransparent() const } } - return _mainObjectPart.isPassabilityTransparent(); + return _mainObjectPart.icnType == MP2::OBJ_ICN_TYPE_UNKNOWN || _mainObjectPart.isPassabilityTransparent(); } bool Maps::Tile::isPassableFrom( const int direction, const bool fromWater, const bool ignoreFog, const int heroColor ) const @@ -1288,8 +1292,26 @@ void Maps::Tile::_updateRoadFlag() } } -void Maps::Tile::fixMP2MapTileObjectType( Tile & tile ) +void Maps::Tile::fixMP2MapTileObjects( Tile & tile ) { + const auto fixMandrakeObjectPart = []( Maps::ObjectPart & part ) { + if ( part.icnType != MP2::OBJ_ICN_TYPE_OBJNSWMP ) { + return; + } + + if ( part.icnIndex == 131 || part.icnIndex == 137 ) { + part.layerType = Maps::OBJECT_LAYER; + } + else if ( part.icnIndex == 130 || part.icnIndex == 136 ) { + part.layerType = Maps::SHADOW_LAYER; + } + }; + + fixMandrakeObjectPart( tile.getMainObjectPart() ); + for ( auto & part : tile.getGroundObjectParts() ) { + fixMandrakeObjectPart( part ); + } + const MP2::MapObjectType originalObjectType = tile.getMainObjectType( false ); // Left tile of a skeleton on Desert should be marked as non-action tile. @@ -1305,6 +1327,8 @@ void Maps::Tile::fixMP2MapTileObjectType( Tile & tile ) && ( tile._mainObjectPart.icnIndex == 105 || tile._mainObjectPart.icnIndex == 106 ) ) { tile._topObjectPart.emplace_back(); std::swap( tile._topObjectPart.back(), tile._mainObjectPart ); + // Sorting ground object parts is a must after making the main object part empty! + tile.sortObjectParts(); return; } diff --git a/src/fheroes2/maps/maps_tiles.h b/src/fheroes2/maps/maps_tiles.h index 14378b50be..ae5c6387bc 100644 --- a/src/fheroes2/maps/maps_tiles.h +++ b/src/fheroes2/maps/maps_tiles.h @@ -328,8 +328,8 @@ namespace Maps _fogDirection = fogDirection; } - // Some tiles have incorrect object type. This is due to original Editor issues. - static void fixMP2MapTileObjectType( Tile & tile ); + // Some tiles have objects being set. This is due to original Editor issues. + static void fixMP2MapTileObjects( Tile & tile ); static int32_t getIndexOfMainTile( const Tile & tile ); diff --git a/src/fheroes2/world/world_loadmap.cpp b/src/fheroes2/world/world_loadmap.cpp index 4158b6bfc5..8c02ef57ea 100644 --- a/src/fheroes2/world/world_loadmap.cpp +++ b/src/fheroes2/world/world_loadmap.cpp @@ -1258,7 +1258,7 @@ bool World::loadResurrectionMap( const std::string & filename ) bool World::ProcessNewMP2Map( const std::string & filename, const bool checkPoLObjects ) { for ( Maps::Tile & tile : vec_tiles ) { - Maps::Tile::fixMP2MapTileObjectType( tile ); + Maps::Tile::fixMP2MapTileObjects( tile ); if ( !updateTileMetadata( tile, tile.getMainObjectType(), checkPoLObjects ) ) { ERROR_LOG( "Failed to load The Price of Loyalty map '" << filename << "' which is not supported by this version of the game." )