From df38dcdbca1d030b7ffa72805e3c3061260b87f0 Mon Sep 17 00:00:00 2001 From: Ihar Hubchyk Date: Sat, 19 Oct 2024 17:04:10 +0800 Subject: [PATCH] Fix logic --- src/fheroes2/ai/ai_hero_action.cpp | 4 +-- src/fheroes2/ai/ai_planner_hero.cpp | 4 +-- src/fheroes2/heroes/heroes_action.cpp | 4 +-- src/fheroes2/maps/maps.cpp | 13 +++++-- src/fheroes2/maps/maps.h | 4 ++- src/fheroes2/maps/maps_tiles.cpp | 15 +++++++- src/fheroes2/maps/maps_tiles_helper.cpp | 48 +++++++++++++++++++++++++ src/fheroes2/maps/maps_tiles_helper.h | 5 +++ src/fheroes2/world/world.cpp | 40 +++++++++------------ 9 files changed, 103 insertions(+), 34 deletions(-) diff --git a/src/fheroes2/ai/ai_hero_action.cpp b/src/fheroes2/ai/ai_hero_action.cpp index b0c9733436a..891b36740b8 100644 --- a/src/fheroes2/ai/ai_hero_action.cpp +++ b/src/fheroes2/ai/ai_hero_action.cpp @@ -1728,9 +1728,9 @@ namespace if ( !hero.isObjectTypeVisited( objectType, Visit::GLOBAL ) ) { hero.SetVisited( tileIndex, Visit::GLOBAL ); - const MapsIndexes eyeMagiIndexes = Maps::GetObjectPositions( MP2::OBJ_EYE_OF_MAGI ); + const auto eyeMagiIndexes = Maps::getObjectParts( MP2::OBJ_EYE_OF_MAGI ); const uint32_t distance = GameStatic::getFogDiscoveryDistance( GameStatic::FogDiscoveryType::MAGI_EYES ); - for ( const int32_t index : eyeMagiIndexes ) { + for ( const auto& [index, objectPart] : eyeMagiIndexes ) { Maps::ClearFog( index, distance, hero.GetColor() ); } } diff --git a/src/fheroes2/ai/ai_planner_hero.cpp b/src/fheroes2/ai/ai_planner_hero.cpp index a3682c4a8aa..9ae4c58fe0d 100644 --- a/src/fheroes2/ai/ai_planner_hero.cpp +++ b/src/fheroes2/ai/ai_planner_hero.cpp @@ -1535,12 +1535,12 @@ double AI::Planner::getGeneralObjectValue( const Heroes & hero, const int32_t in } case MP2::OBJ_HUT_OF_MAGI: { // TODO: cache Maps::GetObjectPositions() call as this is a very heavy operation. - const MapsIndexes eyeMagiIndexes = Maps::GetObjectPositions( MP2::OBJ_EYE_OF_MAGI ); + const auto eyeMagiIndexes = Maps::getObjectParts( MP2::OBJ_EYE_OF_MAGI ); int fogCountToUncover = 0; const int heroColor = hero.GetColor(); const int eyeViewDistance = GameStatic::getFogDiscoveryDistance( GameStatic::FogDiscoveryType::MAGI_EYES ); - for ( const int32_t eyeIndex : eyeMagiIndexes ) { + for ( const auto& [eyeIndex, objectPart] : eyeMagiIndexes ) { fogCountToUncover += Maps::getFogTileCountToBeRevealed( eyeIndex, eyeViewDistance, heroColor ); } diff --git a/src/fheroes2/heroes/heroes_action.cpp b/src/fheroes2/heroes/heroes_action.cpp index 4a0fd2701ca..f1d068fd784 100644 --- a/src/fheroes2/heroes/heroes_action.cpp +++ b/src/fheroes2/heroes/heroes_action.cpp @@ -3332,7 +3332,7 @@ namespace if ( !hero.isObjectTypeVisited( objectType, Visit::GLOBAL ) ) { hero.SetVisited( dst_index, Visit::GLOBAL ); - const MapsIndexes eyeMagiIndexes = Maps::GetObjectPositions( MP2::OBJ_EYE_OF_MAGI ); + const auto eyeMagiIndexes = Maps::getObjectParts( MP2::OBJ_EYE_OF_MAGI ); if ( !eyeMagiIndexes.empty() ) { Interface::AdventureMap & I = Interface::AdventureMap::Get(); @@ -3344,7 +3344,7 @@ namespace bool skipAnimation = false; fheroes2::Rect radarRenderArea; - for ( const int32_t eyeIndex : eyeMagiIndexes ) { + for ( const auto& [eyeIndex, objectPart] : eyeMagiIndexes ) { Maps::ClearFog( eyeIndex, scoutRange, hero.GetColor() ); const fheroes2::Point eyePosition = Maps::GetPoint( eyeIndex ); diff --git a/src/fheroes2/maps/maps.cpp b/src/fheroes2/maps/maps.cpp index 3e3bb99aa88..59723f65389 100644 --- a/src/fheroes2/maps/maps.cpp +++ b/src/fheroes2/maps/maps.cpp @@ -479,9 +479,18 @@ bool Maps::doesObjectExistOnMap( const MP2::MapObjectType objectType ) return false; } -Maps::Indexes Maps::GetObjectPositions( const MP2::MapObjectType objectType ) +std::vector> Maps::getObjectParts( const MP2::MapObjectType objectType ) { - return MapsIndexesObject( objectType, true ); + std::vector> result; + const int32_t size = static_cast( world.getSize() ); + for ( int32_t idx = 0; idx < size; ++idx ) { + const Maps::TilesAddon * objectPart = getObjectPartByType( world.GetTiles( idx ), objectType ); + if ( objectPart != nullptr ) { + result.emplace_back( idx, objectPart ); + } + } + + return result; } Maps::Indexes Maps::GetObjectPositions( int32_t center, const MP2::MapObjectType objectType, bool ignoreHeroes ) diff --git a/src/fheroes2/maps/maps.h b/src/fheroes2/maps/maps.h index cafe624b10c..5dd6b99511b 100644 --- a/src/fheroes2/maps/maps.h +++ b/src/fheroes2/maps/maps.h @@ -40,6 +40,8 @@ using MapsIndexes = std::vector; namespace Maps { + struct TilesAddon; + enum mapsize_t : int { ZERO = 0, @@ -89,7 +91,7 @@ namespace Maps bool doesObjectExistOnMap( const MP2::MapObjectType objectType ); // This function always ignores heroes. - Indexes GetObjectPositions( const MP2::MapObjectType objectType ); + std::vector> getObjectParts( const MP2::MapObjectType objectType ); Indexes GetObjectPositions( int32_t center, const MP2::MapObjectType objectType, bool ignoreHeroes ); diff --git a/src/fheroes2/maps/maps_tiles.cpp b/src/fheroes2/maps/maps_tiles.cpp index a514a2d5f9c..085067f9402 100644 --- a/src/fheroes2/maps/maps_tiles.cpp +++ b/src/fheroes2/maps/maps_tiles.cpp @@ -532,8 +532,21 @@ void Maps::Tiles::setHero( Heroes * hero ) hero = getHero(); if ( hero ) { - SetObject( hero->getObjectTypeUnderHero() ); + const MP2::MapObjectType type = hero->getObjectTypeUnderHero(); hero->setObjectTypeUnderHero( MP2::OBJ_NONE ); + // While updating the main object type when a hero moves away from a tile we can have 3 situations: + // - the underlying object type is MP2::OBJ_NONE. In this case we should update the tile object type. + // - the underlying object type is MP2::OBJ_NONE: + // - if the object exists then we do nothing. + // - if the object doesn't exist then we have to update the tile object type. + // + // The last case can happen only when a hero boards a boat which is under another sea object, like whirlpool. + if ( ( type == MP2::OBJ_NONE ) || !doesTileContainObjectType( *this, type ) ) { + updateObjectType(); + } + else { + SetObject( type ); + } } else { updateObjectType(); diff --git a/src/fheroes2/maps/maps_tiles_helper.cpp b/src/fheroes2/maps/maps_tiles_helper.cpp index cb05a8e7ac1..b69dad29be9 100644 --- a/src/fheroes2/maps/maps_tiles_helper.cpp +++ b/src/fheroes2/maps/maps_tiles_helper.cpp @@ -1593,6 +1593,54 @@ namespace Maps return 0; } + bool doesTileContainObjectType( const Tiles & tile, const MP2::MapObjectType type ) + { + MP2::MapObjectType objectType = getObjectTypeByIcn( tile.getMainObjectPart()._objectIcnType, tile.getMainObjectPart()._imageIndex ); + if ( objectType == type ) { + return true; + } + + for ( const auto & objectPart : tile.getBottomLayerAddons() ) { + objectType = getObjectTypeByIcn( objectPart._objectIcnType, objectPart._imageIndex ); + if ( objectType == type ) { + return true; + } + } + + for ( const auto & objectPart : tile.getTopLayerAddons() ) { + objectType = getObjectTypeByIcn( objectPart._objectIcnType, objectPart._imageIndex ); + if ( objectType == type ) { + return true; + } + } + + return false; + } + + const TilesAddon * getObjectPartByType( const Tiles & tile, const MP2::MapObjectType type ) + { + MP2::MapObjectType objectType = getObjectTypeByIcn( tile.getMainObjectPart()._objectIcnType, tile.getMainObjectPart()._imageIndex ); + if ( objectType == type ) { + return &tile.getMainObjectPart(); + } + + for ( const auto & objectPart : tile.getBottomLayerAddons() ) { + objectType = getObjectTypeByIcn( objectPart._objectIcnType, objectPart._imageIndex ); + if ( objectType == type ) { + return &objectPart; + } + } + + for ( const auto & objectPart : tile.getTopLayerAddons() ) { + objectType = getObjectTypeByIcn( objectPart._objectIcnType, objectPart._imageIndex ); + if ( objectType == type ) { + return &objectPart; + } + } + + return nullptr; + } + Monster getMonsterFromTile( const Tiles & tile ) { switch ( tile.GetObject( false ) ) { diff --git a/src/fheroes2/maps/maps_tiles_helper.h b/src/fheroes2/maps/maps_tiles_helper.h index c662646ddae..6c38500e9d4 100644 --- a/src/fheroes2/maps/maps_tiles_helper.h +++ b/src/fheroes2/maps/maps_tiles_helper.h @@ -42,6 +42,7 @@ namespace MP2 namespace Maps { class Tiles; + struct TilesAddon; struct ObjectInfo; @@ -104,6 +105,10 @@ namespace Maps int getColorFromBarrierSprite( const MP2::ObjectIcnType objectIcnType, const uint8_t icnIndex ); int getColorFromTravellerTentSprite( const MP2::ObjectIcnType objectIcnType, const uint8_t icnIndex ); + bool doesTileContainObjectType( const Tiles & tile, const MP2::MapObjectType type ); + + const TilesAddon * getObjectPartByType( const Tiles & tile, const MP2::MapObjectType type ); + Monster getMonsterFromTile( const Tiles & tile ); Artifact getArtifactFromTile( const Tiles & tile ); diff --git a/src/fheroes2/world/world.cpp b/src/fheroes2/world/world.cpp index 64a2b9e662a..53cd4c93883 100644 --- a/src/fheroes2/world/world.cpp +++ b/src/fheroes2/world/world.cpp @@ -792,8 +792,14 @@ MapsIndexes World::GetTeleportEndPoints( const int32_t index ) const return result; } + const Maps::TilesAddon * entranceObjectPart = Maps::getObjectPartByType( entranceTile, MP2::OBJ_STONE_LITHS ); + if ( entranceObjectPart == nullptr ) { + assert( 0 ); + return result; + } + // The type of destination stone liths must match the type of the source stone liths. - for ( const int32_t teleportIndex : _allTeleports.at( entranceTile.getMainObjectPart()._imageIndex ) ) { + for ( const int32_t teleportIndex : _allTeleports.at( entranceObjectPart->_imageIndex ) ) { const Maps::Tiles & teleportTile = GetTiles( teleportIndex ); if ( teleportIndex == index || teleportTile.GetObject() != MP2::OBJ_STONE_LITHS || teleportTile.isWater() != entranceTile.isWater() ) { @@ -829,24 +835,7 @@ MapsIndexes World::GetWhirlpoolEndPoints( const int32_t index ) const return result; } - const auto getWhirlPoolObjectPart = []( const Maps::Tiles & tile ) -> const Maps::TilesAddon * { - // The exit point from the destination whirlpool must match the entry point in the source whirlpool. - // A whirlpool can be as a main addon / object part or bottom part. This is important to get a proper object part. - if ( Maps::getObjectTypeByIcn( tile.getMainObjectPart()._objectIcnType, tile.getMainObjectPart()._imageIndex ) == MP2::OBJ_WHIRLPOOL ) { - return &tile.getMainObjectPart(); - } - - for ( const auto & part : tile.getBottomLayerAddons() ) { - if ( Maps::getObjectTypeByIcn( part._objectIcnType, part._imageIndex ) == MP2::OBJ_WHIRLPOOL ) { - return ∂ - } - } - - assert( 0 ); - return nullptr; - }; - - const Maps::TilesAddon * entranceObjectPart = getWhirlPoolObjectPart( entranceTile ); + const Maps::TilesAddon * entranceObjectPart = Maps::getObjectPartByType( entranceTile, MP2::OBJ_WHIRLPOOL ); if ( entranceObjectPart == nullptr ) { return result; } @@ -857,7 +846,7 @@ MapsIndexes World::GetWhirlpoolEndPoints( const int32_t index ) const continue; } - const Maps::TilesAddon * destinationObjectPart = getWhirlPoolObjectPart( whirlpoolTile ); + const Maps::TilesAddon * destinationObjectPart = Maps::getObjectPartByType( whirlpoolTile, MP2::OBJ_WHIRLPOOL ); if ( destinationObjectPart == nullptr ) { continue; } @@ -1357,15 +1346,18 @@ void World::PostLoad( const bool setTilePassabilities, const bool updateUidCount // Cache all tiles that that contain stone liths of a certain type (depending on object sprite index). _allTeleports.clear(); - for ( const int32_t index : Maps::GetObjectPositions( MP2::OBJ_STONE_LITHS ) ) { - _allTeleports[GetTiles( index ).getMainObjectPart()._imageIndex].push_back( index ); + for ( const auto& [index, objectPart] : Maps::getObjectParts( MP2::OBJ_STONE_LITHS ) ) { + assert( objectPart != nullptr ); + _allTeleports[objectPart->_imageIndex].push_back( index ); } // Cache all tiles that contain a certain part of the whirlpool (depending on object sprite index). _allWhirlpools.clear(); - for ( const int32_t index : Maps::GetObjectPositions( MP2::OBJ_WHIRLPOOL ) ) { - _allWhirlpools[GetTiles( index ).getMainObjectPart()._imageIndex].push_back( index ); + for ( const auto& [index, objectPart] : Maps::getObjectParts( MP2::OBJ_WHIRLPOOL ) ) { + assert( objectPart != nullptr ); + + _allWhirlpools[objectPart->_imageIndex].push_back( index ); } resetPathfinder();