Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Simplify and improve the logic for passability calculations #9404

Draft
wants to merge 10 commits into
base: master
Choose a base branch
from
4 changes: 2 additions & 2 deletions src/fheroes2/gui/interface_gamearea.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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.

Expand Down Expand Up @@ -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();
Expand Down
60 changes: 42 additions & 18 deletions src/fheroes2/maps/maps_tiles.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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 ) ) {
Expand All @@ -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;
}

Expand Down Expand Up @@ -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 ) ) {
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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.
Expand All @@ -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;
}
Expand Down
4 changes: 2 additions & 2 deletions src/fheroes2/maps/maps_tiles.h
Original file line number Diff line number Diff line change
Expand Up @@ -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 );

Expand Down
2 changes: 1 addition & 1 deletion src/fheroes2/world/world_loadmap.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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." )
Expand Down
Loading