diff --git a/VisualStudio/fheroes2/sources.props b/VisualStudio/fheroes2/sources.props
index 85ef8521bf1..78ffea884ec 100644
--- a/VisualStudio/fheroes2/sources.props
+++ b/VisualStudio/fheroes2/sources.props
@@ -186,6 +186,7 @@
+
@@ -370,6 +371,7 @@
+
diff --git a/src/fheroes2/gui/interface_gamearea.cpp b/src/fheroes2/gui/interface_gamearea.cpp
index 78474bb02c8..9febc73057e 100644
--- a/src/fheroes2/gui/interface_gamearea.cpp
+++ b/src/fheroes2/gui/interface_gamearea.cpp
@@ -47,6 +47,7 @@
#include "logging.h"
#include "maps.h"
#include "maps_tiles.h"
+#include "maps_tiles_render.h"
#include "pal.h"
#include "players.h"
#include "route.h"
@@ -404,9 +405,9 @@ void Interface::GameArea::Redraw( fheroes2::Image & dst, int flag, bool isPuzzle
int32_t maxY = tileROI.y + tileROI.height;
#ifdef WITH_DEBUG
- const bool drawFog = ( ( flag & LEVEL_FOG ) == LEVEL_FOG ) && !IS_DEVEL();
+ const bool renderFog = ( ( flag & LEVEL_FOG ) == LEVEL_FOG ) && !IS_DEVEL();
#else
- const bool drawFog = ( flag & LEVEL_FOG ) == LEVEL_FOG;
+ const bool renderFog = ( flag & LEVEL_FOG ) == LEVEL_FOG;
#endif
// Render terrain.
@@ -415,19 +416,19 @@ void Interface::GameArea::Redraw( fheroes2::Image & dst, int flag, bool isPuzzle
if ( offset.y < 0 || offset.y >= world.h() ) {
for ( ; offset.x < maxX; ++offset.x ) {
- Maps::Tiles::RedrawEmptyTile( dst, offset, *this );
+ Maps::redrawEmptyTile( dst, offset, *this );
}
}
else {
for ( ; offset.x < maxX; ++offset.x ) {
if ( offset.x < 0 || offset.x >= world.w() ) {
- Maps::Tiles::RedrawEmptyTile( dst, offset, *this );
+ Maps::redrawEmptyTile( dst, offset, *this );
}
else {
const Maps::Tiles & tile = world.GetTiles( offset.x, offset.y );
// Do not render terrain on the tiles fully covered with the fog.
- if ( tile.getFogDirection() != DIRECTION_ALL || !drawFog ) {
- DrawTile( dst, tile.GetTileSurface(), offset );
+ if ( tile.getFogDirection() != DIRECTION_ALL || !renderFog ) {
+ DrawTile( dst, getTileSurface( tile ), offset );
}
}
}
@@ -487,7 +488,7 @@ void Interface::GameArea::Redraw( fheroes2::Image & dst, int flag, bool isPuzzle
MP2::MapObjectType objectType = tile.GetObject();
// We will skip objects which are fully under the fog.
- const bool isTileUnderFog = ( tile.getFogDirection() == DIRECTION_ALL ) && drawFog;
+ const bool isTileUnderFog = ( tile.getFogDirection() == DIRECTION_ALL ) && renderFog;
switch ( objectType ) {
case MP2::OBJ_HEROES: {
@@ -528,8 +529,8 @@ void Interface::GameArea::Redraw( fheroes2::Image & dst, int flag, bool isPuzzle
const uint8_t alphaValue = getObjectAlphaValue( tile.GetIndex(), MP2::OBJ_MONSTER );
- auto spriteInfo = tile.getMonsterSpritesPerTile();
- auto spriteShadowInfo = tile.getMonsterShadowSpritesPerTile();
+ auto spriteInfo = getMonsterSpritesPerTile( tile );
+ auto spriteShadowInfo = getMonsterShadowSpritesPerTile( tile );
populateStaticTileUnfitObjectInfo( tileUnfit, spriteInfo, spriteShadowInfo, tile.GetCenter(), alphaValue );
@@ -547,8 +548,8 @@ void Interface::GameArea::Redraw( fheroes2::Image & dst, int flag, bool isPuzzle
const uint8_t alphaValue = getObjectAlphaValue( tile.GetIndex(), MP2::OBJ_BOAT );
- auto spriteInfo = tile.getBoatSpritesPerTile();
- auto spriteShadowInfo = tile.getBoatShadowSpritesPerTile();
+ auto spriteInfo = getBoatSpritesPerTile( tile );
+ auto spriteShadowInfo = getBoatShadowSpritesPerTile( tile );
populateStaticTileUnfitObjectInfo( tileUnfit, spriteInfo, spriteShadowInfo, tile.GetCenter(), alphaValue );
@@ -561,7 +562,7 @@ void Interface::GameArea::Redraw( fheroes2::Image & dst, int flag, bool isPuzzle
// These are parts of original action objects which must be rendered under heroes.
if ( objectType == MP2::OBJ_MINES ) {
- auto spriteInfo = tile.getMineGuardianSpritesPerTile();
+ auto spriteInfo = getMineGuardianSpritesPerTile( tile );
if ( !spriteInfo.empty() ) {
const uint8_t alphaValue = getObjectAlphaValue( tile.GetObjectUID() );
populateStaticTileUnfitBackgroundObjectInfo( tileUnfit, spriteInfo, tile.GetCenter(), alphaValue );
@@ -575,14 +576,14 @@ void Interface::GameArea::Redraw( fheroes2::Image & dst, int flag, bool isPuzzle
for ( int32_t x = minX; x < maxX; ++x ) {
const Maps::Tiles & tile = world.GetTiles( x, y );
- if ( tile.getFogDirection() == DIRECTION_ALL && drawFog ) {
+ if ( tile.getFogDirection() == DIRECTION_ALL && renderFog ) {
continue;
}
// Draw roads, rivers and cracks.
- tile.redrawBottomLayerObjects( dst, isPuzzleDraw, *this, Maps::TERRAIN_LAYER );
+ redrawBottomLayerObjects( tile, dst, isPuzzleDraw, *this, Maps::TERRAIN_LAYER );
- tile.redrawBottomLayerObjects( dst, isPuzzleDraw, *this, Maps::BACKGROUND_LAYER );
+ redrawBottomLayerObjects( tile, dst, isPuzzleDraw, *this, Maps::BACKGROUND_LAYER );
}
}
@@ -593,11 +594,11 @@ void Interface::GameArea::Redraw( fheroes2::Image & dst, int flag, bool isPuzzle
for ( int32_t x = minX; x < maxX; ++x ) {
const Maps::Tiles & tile = world.GetTiles( x, y );
- if ( tile.getFogDirection() == DIRECTION_ALL && drawFog ) {
+ if ( tile.getFogDirection() == DIRECTION_ALL && renderFog ) {
continue;
}
- tile.redrawBottomLayerObjects( dst, isPuzzleDraw, *this, Maps::SHADOW_LAYER );
+ redrawBottomLayerObjects( tile, dst, isPuzzleDraw, *this, Maps::SHADOW_LAYER );
}
}
@@ -614,12 +615,12 @@ void Interface::GameArea::Redraw( fheroes2::Image & dst, int flag, bool isPuzzle
for ( int32_t x = minX; x < maxX; ++x ) {
const Maps::Tiles & tile = world.GetTiles( x, y );
- if ( tile.getFogDirection() == DIRECTION_ALL && drawFog ) {
+ if ( tile.getFogDirection() == DIRECTION_ALL && renderFog ) {
continue;
}
// TODO: some action objects have tiles above which are still on bottom layer. These images must be drawn last.
- tile.redrawBottomLayerObjects( dst, isPuzzleDraw, *this, Maps::OBJECT_LAYER );
+ redrawBottomLayerObjects( tile, dst, isPuzzleDraw, *this, Maps::OBJECT_LAYER );
}
}
@@ -637,7 +638,7 @@ void Interface::GameArea::Redraw( fheroes2::Image & dst, int flag, bool isPuzzle
for ( int32_t x = roiToRenderMinX; x < roiExtraObjectsMaxX; ++x ) {
const Maps::Tiles & tile = world.GetTiles( x, y );
- const bool isTileUnderFog = ( tile.getFogDirection() == DIRECTION_ALL ) && drawFog;
+ const bool isTileUnderFog = ( tile.getFogDirection() == DIRECTION_ALL ) && renderFog;
if ( isTileUnderFog ) {
// To correctly render tall extra objects (ghosts over abandoned mine) it is needed to analyze one tile to the bottom direction under fog.
const bool isUpperTileUnderFog = ( y > 0 ) ? ( world.GetTiles( x, y - 1 ).getFogDirection() == DIRECTION_ALL ) : true;
@@ -646,7 +647,7 @@ void Interface::GameArea::Redraw( fheroes2::Image & dst, int flag, bool isPuzzle
continue;
}
- tile.redrawTopLayerExtraObjects( dst, isPuzzleDraw, *this );
+ redrawTopLayerExtraObjects( tile, dst, isPuzzleDraw, *this );
continue;
}
@@ -660,14 +661,14 @@ void Interface::GameArea::Redraw( fheroes2::Image & dst, int flag, bool isPuzzle
topLayerTallObjects.emplace_back( &addon );
}
else {
- tile.redrawTopLayerObject( dst, isPuzzleDraw, *this, addon );
+ redrawTopLayerObject( tile, dst, isPuzzleDraw, *this, addon );
}
}
- tile.redrawTopLayerExtraObjects( dst, isPuzzleDraw, *this );
+ redrawTopLayerExtraObjects( tile, dst, isPuzzleDraw, *this );
for ( const Maps::TilesAddon * addon : topLayerTallObjects ) {
- tile.redrawTopLayerObject( dst, isPuzzleDraw, *this, *addon );
+ redrawTopLayerObject( tile, dst, isPuzzleDraw, *this, *addon );
}
}
}
@@ -732,7 +733,7 @@ void Interface::GameArea::Redraw( fheroes2::Image & dst, int flag, bool isPuzzle
for ( int32_t y = minY; y < maxY; ++y ) {
for ( int32_t x = minX; x < maxX; ++x ) {
- world.GetTiles( x, y ).RedrawPassable( dst, friendColors, *this );
+ redrawPassable( world.GetTiles( x, y ), dst, friendColors, *this );
}
}
}
@@ -740,20 +741,20 @@ void Interface::GameArea::Redraw( fheroes2::Image & dst, int flag, bool isPuzzle
else
#endif
// redraw fog
- if ( drawFog ) {
+ if ( renderFog ) {
for ( int32_t y = minY; y < maxY; ++y ) {
for ( int32_t x = minX; x < maxX; ++x ) {
const Maps::Tiles & tile = world.GetTiles( x, y );
if ( tile.getFogDirection() != Direction::UNKNOWN ) {
- tile.drawFog( dst, *this );
+ drawFog( tile, dst, *this );
if ( drawTowns ) {
- tile.drawByObjectIcnType( dst, *this, MP2::OBJ_ICN_TYPE_OBJNTWBA );
+ drawByObjectIcnType( tile, dst, *this, MP2::OBJ_ICN_TYPE_OBJNTWBA );
const MP2::MapObjectType objectType = tile.GetObject( false );
if ( objectType == MP2::OBJ_CASTLE || objectType == MP2::OBJ_NON_ACTION_CASTLE ) {
- tile.drawByObjectIcnType( dst, *this, MP2::OBJ_ICN_TYPE_OBJNTOWN );
+ drawByObjectIcnType( tile, dst, *this, MP2::OBJ_ICN_TYPE_OBJNTOWN );
}
}
}
diff --git a/src/fheroes2/maps/maps_tiles.cpp b/src/fheroes2/maps/maps_tiles.cpp
index 6d2ca332566..062cb8297bd 100644
--- a/src/fheroes2/maps/maps_tiles.cpp
+++ b/src/fheroes2/maps/maps_tiles.cpp
@@ -47,7 +47,6 @@
#include "heroes.h"
#include "icn.h"
#include "image.h"
-#include "interface_gamearea.h"
#include "logging.h"
#include "maps.h"
#include "maps_tiles_helper.h" // TODO: This file should not be included
@@ -77,7 +76,6 @@
#include "til.h"
#include "tools.h"
#include "trees.h"
-#include "ui_object_rendering.h"
#include "world.h"
namespace
@@ -266,113 +264,6 @@ namespace
}
#endif
- bool contains( const int base, const int value )
- {
- return ( base & value ) == value;
- }
-
-#ifdef WITH_DEBUG
- const fheroes2::Image & PassableViewSurface( const int passable )
- {
- static std::map imageMap;
-
- auto iter = imageMap.find( passable );
- if ( iter != imageMap.end() ) {
- return iter->second;
- }
-
- const int32_t size = 31;
- const uint8_t red = 0xBA;
- const uint8_t green = 0x5A;
-
- fheroes2::Image sf( size, size );
- sf.reset();
-
- if ( 0 == passable || Direction::CENTER == passable ) {
- fheroes2::DrawBorder( sf, red );
- }
- else if ( DIRECTION_ALL == passable ) {
- fheroes2::DrawBorder( sf, green );
- }
- else {
- const uint8_t topLeftColor = ( ( passable & Direction::TOP_LEFT ) != 0 ) ? green : red;
- const uint8_t bottomRightColor = ( ( passable & Direction::BOTTOM_RIGHT ) != 0 ) ? green : red;
- const uint8_t topRightColor = ( ( passable & Direction::TOP_RIGHT ) != 0 ) ? green : red;
- const uint8_t bottomLeftColor = ( ( passable & Direction::BOTTOM_LEFT ) != 0 ) ? green : red;
- const uint8_t topColor = ( ( passable & Direction::TOP ) != 0 ) ? green : red;
- const uint8_t bottomColor = ( ( passable & Direction::BOTTOM ) != 0 ) ? green : red;
- const uint8_t leftColor = ( ( passable & Direction::LEFT ) != 0 ) ? green : red;
- const uint8_t rightColor = ( ( passable & Direction::RIGHT ) != 0 ) ? green : red;
-
- uint8_t * image = sf.image();
- uint8_t * transform = sf.transform();
-
- // Horizontal
- for ( int32_t i = 0; i < 10; ++i ) {
- *( image + i ) = topLeftColor;
- *( transform + i ) = 0;
-
- *( image + i + ( size - 1 ) * size ) = bottomLeftColor;
- *( transform + i + ( size - 1 ) * size ) = 0;
- }
-
- for ( int32_t i = 10; i < 21; ++i ) {
- *( image + i ) = topColor;
- *( transform + i ) = 0;
-
- *( image + i + ( size - 1 ) * size ) = bottomColor;
- *( transform + i + ( size - 1 ) * size ) = 0;
- }
-
- for ( int32_t i = 21; i < size; ++i ) {
- *( image + i ) = topRightColor;
- *( transform + i ) = 0;
-
- *( image + i + ( size - 1 ) * size ) = bottomRightColor;
- *( transform + i + ( size - 1 ) * size ) = 0;
- }
-
- // Vertical
- for ( int32_t i = 0; i < 10; ++i ) {
- *( image + i * size ) = topLeftColor;
- *( transform + i * size ) = 0;
-
- *( image + size - 1 + i * size ) = topRightColor;
- *( transform + size - 1 + i * size ) = 0;
- }
-
- for ( int32_t i = 10; i < 21; ++i ) {
- *( image + i * size ) = leftColor;
- *( transform + i * size ) = 0;
-
- *( image + size - 1 + i * size ) = rightColor;
- *( transform + size - 1 + i * size ) = 0;
- }
-
- for ( int32_t i = 21; i < size; ++i ) {
- *( image + i * size ) = bottomLeftColor;
- *( transform + i * size ) = 0;
-
- *( image + size - 1 + i * size ) = bottomRightColor;
- *( transform + size - 1 + i * size ) = 0;
- }
- }
-
- return imageMap.try_emplace( passable, std::move( sf ) ).first->second;
- }
-
- const fheroes2::Image & getDebugFogImage()
- {
- static const fheroes2::Image fog = []() {
- fheroes2::Image temp( 32, 32 );
- fheroes2::FillTransform( temp, 0, 0, temp.width(), temp.height(), 2 );
- return temp;
- }();
-
- return fog;
- }
-#endif
-
bool isShortObject( const MP2::MapObjectType objectType )
{
// Some objects allow middle moves even being attached to the bottom.
@@ -450,22 +341,6 @@ namespace
return false;
}
- bool isDirectRenderingRestricted( const int icnId )
- {
- switch ( icnId ) {
- case ICN::UNKNOWN:
- case ICN::MONS32:
- case ICN::BOAT32:
- case ICN::MINIHERO:
- // Either it is an invalid sprite or a sprite which needs to be divided into tiles in order to properly render it.
- return true;
- default:
- break;
- }
-
- return false;
- }
-
const char * getObjectLayerName( const uint8_t level )
{
switch ( level ) {
@@ -857,11 +732,6 @@ int Maps::Tiles::getBoatDirection() const
return Direction::UNKNOWN;
}
-const fheroes2::Image & Maps::Tiles::GetTileSurface() const
-{
- return fheroes2::AGG::GetTIL( TIL::GROUND32, _terrainImageIndex, ( _terrainFlags & 0x3 ) );
-}
-
int Maps::Tiles::getOriginalPassability() const
{
const MP2::MapObjectType objectType = GetObject( false );
@@ -1160,414 +1030,6 @@ int Maps::Tiles::GetGround() const
return Maps::Ground::BEACH;
}
-void Maps::Tiles::RedrawEmptyTile( fheroes2::Image & dst, const fheroes2::Point & mp, const Interface::GameArea & area )
-{
- if ( mp.y == -1 && mp.x >= 0 && mp.x < world.w() ) { // top first row
- area.DrawTile( dst, fheroes2::AGG::GetTIL( TIL::STON, 20 + ( mp.x % 4 ), 0 ), mp );
- }
- else if ( mp.x == world.w() && mp.y >= 0 && mp.y < world.h() ) { // right first row
- area.DrawTile( dst, fheroes2::AGG::GetTIL( TIL::STON, 24 + ( mp.y % 4 ), 0 ), mp );
- }
- else if ( mp.y == world.h() && mp.x >= 0 && mp.x < world.w() ) { // bottom first row
- area.DrawTile( dst, fheroes2::AGG::GetTIL( TIL::STON, 28 + ( mp.x % 4 ), 0 ), mp );
- }
- else if ( mp.x == -1 && mp.y >= 0 && mp.y < world.h() ) { // left first row
- area.DrawTile( dst, fheroes2::AGG::GetTIL( TIL::STON, 32 + ( mp.y % 4 ), 0 ), mp );
- }
- else {
- area.DrawTile( dst, fheroes2::AGG::GetTIL( TIL::STON, ( std::abs( mp.y ) % 4 ) * 4 + std::abs( mp.x ) % 4, 0 ), mp );
- }
-}
-
-void Maps::Tiles::RedrawPassable( fheroes2::Image & dst, const int friendColors, const Interface::GameArea & area ) const
-{
-#ifdef WITH_DEBUG
- if ( isFog( friendColors ) ) {
- area.BlitOnTile( dst, getDebugFogImage(), 0, 0, Maps::GetPoint( _index ), false, 255 );
- }
- if ( 0 == tilePassable || DIRECTION_ALL != tilePassable ) {
- area.BlitOnTile( dst, PassableViewSurface( tilePassable ), 0, 0, Maps::GetPoint( _index ), false, 255 );
- }
-#else
- (void)dst;
- (void)area;
- (void)friendColors;
-#endif
-}
-
-void Maps::Tiles::redrawBottomLayerObjects( fheroes2::Image & dst, bool isPuzzleDraw, const Interface::GameArea & area, const uint8_t level ) const
-{
- assert( level <= 0x03 );
-
- const fheroes2::Point & mp = Maps::GetPoint( _index );
-
- // Since the original game stores information about objects in a very weird way and this is how it is implemented for us we need to do the following procedure:
- // - run through all bottom objects first which are stored in the addon stack
- // - check the main object which is on the tile
-
- // Some addons must be rendered after the main object on the tile. This applies for flags.
- // Since this method is called intensively during rendering we have to avoid memory allocation on heap.
- const size_t maxPostRenderAddons = 16;
- std::array postRenderingAddon{};
- size_t postRenderAddonCount = 0;
-
- for ( const TilesAddon & addon : addons_level1 ) {
- if ( ( addon._layerType & 0x03 ) != level ) {
- continue;
- }
-
- if ( isPuzzleDraw && MP2::isHiddenForPuzzle( GetGround(), addon._objectIcnType, addon._imageIndex ) ) {
- continue;
- }
-
- if ( addon._objectIcnType == MP2::OBJ_ICN_TYPE_FLAG32 ) {
- // Based on logically thinking it is impossible to have more than 16 flags on a single tile.
- assert( postRenderAddonCount < maxPostRenderAddons );
-
- postRenderingAddon[postRenderAddonCount] = &addon;
- ++postRenderAddonCount;
- continue;
- }
-
- renderAddonObject( dst, area, mp, addon );
- }
-
- if ( _objectIcnType != MP2::OBJ_ICN_TYPE_UNKNOWN && ( _layerType & 0x03 ) == level
- && ( !isPuzzleDraw || !MP2::isHiddenForPuzzle( GetGround(), _objectIcnType, _imageIndex ) ) ) {
- renderMainObject( dst, area, mp );
- }
-
- for ( size_t i = 0; i < postRenderAddonCount; ++i ) {
- assert( postRenderingAddon[i] != nullptr );
-
- renderAddonObject( dst, area, mp, *postRenderingAddon[i] );
- }
-}
-
-void Maps::Tiles::renderAddonObject( fheroes2::Image & output, const Interface::GameArea & area, const fheroes2::Point & offset, const TilesAddon & addon )
-{
- assert( addon._objectIcnType != MP2::OBJ_ICN_TYPE_UNKNOWN && addon._imageIndex != 255 );
-
- const int icn = MP2::getIcnIdFromObjectIcnType( addon._objectIcnType );
- if ( isDirectRenderingRestricted( icn ) ) {
- return;
- }
-
- const uint8_t alphaValue = area.getObjectAlphaValue( addon._uid );
-
- const fheroes2::Sprite & sprite = fheroes2::AGG::GetICN( icn, addon._imageIndex );
-
- // Ideally we need to check that the image is within a tile area. However, flags are among those for which this rule doesn't apply.
- if ( icn == ICN::FLAG32 ) {
- assert( sprite.width() <= TILEWIDTH && sprite.height() <= TILEWIDTH );
- }
- else {
- assert( sprite.x() >= 0 && sprite.width() + sprite.x() <= TILEWIDTH && sprite.y() >= 0 && sprite.height() + sprite.y() <= TILEWIDTH );
- }
-
- area.BlitOnTile( output, sprite, sprite.x(), sprite.y(), offset, false, alphaValue );
-
- const uint32_t animationIndex = ICN::AnimationFrame( icn, addon._imageIndex, Game::getAdventureMapAnimationIndex() );
- if ( animationIndex > 0 ) {
- const fheroes2::Sprite & animationSprite = fheroes2::AGG::GetICN( icn, animationIndex );
-
- // If this assertion blows up we are trying to render an image bigger than a tile. Render this object properly as heroes or monsters!
- assert( animationSprite.x() >= 0 && animationSprite.width() + animationSprite.x() <= TILEWIDTH && animationSprite.y() >= 0
- && animationSprite.height() + animationSprite.y() <= TILEWIDTH );
-
- area.BlitOnTile( output, animationSprite, animationSprite.x(), animationSprite.y(), offset, false, alphaValue );
- }
-}
-
-void Maps::Tiles::renderMainObject( fheroes2::Image & output, const Interface::GameArea & area, const fheroes2::Point & offset ) const
-{
- assert( _objectIcnType != MP2::OBJ_ICN_TYPE_UNKNOWN && _imageIndex != 255 );
-
- const int mainObjectIcn = MP2::getIcnIdFromObjectIcnType( _objectIcnType );
- if ( isDirectRenderingRestricted( mainObjectIcn ) ) {
- return;
- }
-
- const uint8_t mainObjectAlphaValue = area.getObjectAlphaValue( _uid );
-
- const fheroes2::Sprite & mainObjectSprite = fheroes2::AGG::GetICN( mainObjectIcn, _imageIndex );
-
- // If this assertion blows up we are trying to render an image bigger than a tile. Render this object properly as heroes or monsters!
- assert( mainObjectSprite.x() >= 0 && mainObjectSprite.width() + mainObjectSprite.x() <= TILEWIDTH && mainObjectSprite.y() >= 0
- && mainObjectSprite.height() + mainObjectSprite.y() <= TILEWIDTH );
-
- area.BlitOnTile( output, mainObjectSprite, mainObjectSprite.x(), mainObjectSprite.y(), offset, false, mainObjectAlphaValue );
-
- // Render possible animation image.
- // TODO: quantity2 is used in absolutely incorrect way! Fix all the logic for it. As of now (quantity2 != 0) expression is used only for Magic Garden.
- const uint32_t mainObjectAnimationIndex = ICN::AnimationFrame( mainObjectIcn, _imageIndex, Game::getAdventureMapAnimationIndex(), metadata()[1] != 0 );
- if ( mainObjectAnimationIndex > 0 ) {
- const fheroes2::Sprite & animationSprite = fheroes2::AGG::GetICN( mainObjectIcn, mainObjectAnimationIndex );
-
- // If this assertion blows up we are trying to render an image bigger than a tile. Render this object properly as heroes or monsters!
- assert( animationSprite.x() >= 0 && animationSprite.width() + animationSprite.x() <= TILEWIDTH && animationSprite.y() >= 0
- && animationSprite.height() + animationSprite.y() <= TILEWIDTH );
-
- area.BlitOnTile( output, animationSprite, animationSprite.x(), animationSprite.y(), offset, false, mainObjectAlphaValue );
- }
-}
-
-void Maps::Tiles::drawByObjectIcnType( fheroes2::Image & output, const Interface::GameArea & area, const MP2::ObjectIcnType objectIcnType ) const
-{
- const fheroes2::Point & tileOffset = Maps::GetPoint( _index );
-
- for ( const TilesAddon & addon : addons_level1 ) {
- if ( addon._objectIcnType == objectIcnType ) {
- renderAddonObject( output, area, tileOffset, addon );
- }
- }
-
- if ( _objectIcnType == objectIcnType ) {
- renderMainObject( output, area, tileOffset );
- }
-
- for ( const TilesAddon & addon : addons_level2 ) {
- if ( addon._objectIcnType == objectIcnType ) {
- renderAddonObject( output, area, tileOffset, addon );
- }
- }
-}
-
-std::vector Maps::Tiles::getMonsterSpritesPerTile() const
-{
- assert( GetObject() == MP2::OBJ_MONSTER );
-
- const Monster & monster = getMonsterFromTile( *this );
- const std::pair spriteIndices = GetMonsterSpriteIndices( *this, monster.GetSpriteIndex() );
-
- const int icnId{ ICN::MINI_MONSTER_IMAGE };
- const fheroes2::Sprite & monsterSprite = fheroes2::AGG::GetICN( icnId, spriteIndices.first );
- const fheroes2::Point monsterSpriteOffset( monsterSprite.x() + 16, monsterSprite.y() + 30 );
-
- std::vector outputSquareInfo;
- std::vector> outputImageInfo;
- fheroes2::DivideImageBySquares( monsterSpriteOffset, monsterSprite, TILEWIDTH, outputSquareInfo, outputImageInfo );
-
- assert( outputSquareInfo.size() == outputImageInfo.size() );
-
- std::vector objectInfo;
- for ( size_t i = 0; i < outputSquareInfo.size(); ++i ) {
- objectInfo.emplace_back( outputSquareInfo[i], outputImageInfo[i].first, outputImageInfo[i].second, icnId, spriteIndices.first, false,
- static_cast( 255 ) );
- }
-
- outputSquareInfo.clear();
- outputImageInfo.clear();
-
- if ( spriteIndices.second > 0 ) {
- const fheroes2::Sprite & secondaryMonsterSprite = fheroes2::AGG::GetICN( icnId, spriteIndices.second );
- const fheroes2::Point secondaryMonsterSpriteOffset( secondaryMonsterSprite.x() + 16, secondaryMonsterSprite.y() + 30 );
-
- fheroes2::DivideImageBySquares( secondaryMonsterSpriteOffset, secondaryMonsterSprite, TILEWIDTH, outputSquareInfo, outputImageInfo );
-
- assert( outputSquareInfo.size() == outputImageInfo.size() );
-
- for ( size_t i = 0; i < outputSquareInfo.size(); ++i ) {
- objectInfo.emplace_back( outputSquareInfo[i], outputImageInfo[i].first, outputImageInfo[i].second, icnId, spriteIndices.second, false,
- static_cast( 255 ) );
- }
- }
-
- return objectInfo;
-}
-
-std::vector Maps::Tiles::getMonsterShadowSpritesPerTile() const
-{
- assert( GetObject() == MP2::OBJ_MONSTER );
-
- const Monster & monster = getMonsterFromTile( *this );
- const std::pair spriteIndices = GetMonsterSpriteIndices( *this, monster.GetSpriteIndex() );
-
- const int icnId{ ICN::MINI_MONSTER_SHADOW };
- const fheroes2::Sprite & monsterSprite = fheroes2::AGG::GetICN( icnId, spriteIndices.first );
- const fheroes2::Point monsterSpriteOffset( monsterSprite.x() + 16, monsterSprite.y() + 30 );
-
- std::vector outputSquareInfo;
- std::vector> outputImageInfo;
- fheroes2::DivideImageBySquares( monsterSpriteOffset, monsterSprite, TILEWIDTH, outputSquareInfo, outputImageInfo );
-
- assert( outputSquareInfo.size() == outputImageInfo.size() );
-
- std::vector objectInfo;
- for ( size_t i = 0; i < outputSquareInfo.size(); ++i ) {
- objectInfo.emplace_back( outputSquareInfo[i], outputImageInfo[i].first, outputImageInfo[i].second, icnId, spriteIndices.first, false,
- static_cast( 255 ) );
- }
-
- outputSquareInfo.clear();
- outputImageInfo.clear();
-
- if ( spriteIndices.second > 0 ) {
- const fheroes2::Sprite & secondaryMonsterSprite = fheroes2::AGG::GetICN( icnId, spriteIndices.second );
- const fheroes2::Point secondaryMonsterSpriteOffset( secondaryMonsterSprite.x() + 16, secondaryMonsterSprite.y() + 30 );
-
- fheroes2::DivideImageBySquares( secondaryMonsterSpriteOffset, secondaryMonsterSprite, TILEWIDTH, outputSquareInfo, outputImageInfo );
-
- assert( outputSquareInfo.size() == outputImageInfo.size() );
-
- for ( size_t i = 0; i < outputSquareInfo.size(); ++i ) {
- objectInfo.emplace_back( outputSquareInfo[i], outputImageInfo[i].first, outputImageInfo[i].second, icnId, spriteIndices.second, false,
- static_cast( 255 ) );
- }
- }
-
- return objectInfo;
-}
-
-std::vector Maps::Tiles::getBoatSpritesPerTile() const
-{
- // TODO: combine both boat image generation for heroes and empty boats.
- assert( GetObject() == MP2::OBJ_BOAT );
-
- const uint32_t spriteIndex = ( _imageIndex == 255 ) ? 18 : _imageIndex;
-
- const bool isReflected = ( spriteIndex > 128 );
-
- const int icnId{ ICN::BOAT32 };
- const uint32_t icnIndex = spriteIndex % 128;
- const fheroes2::Sprite & boatSprite = fheroes2::AGG::GetICN( icnId, icnIndex );
-
- const fheroes2::Point boatSpriteOffset( ( isReflected ? ( TILEWIDTH + 1 - boatSprite.x() - boatSprite.width() ) : boatSprite.x() ), boatSprite.y() + TILEWIDTH - 11 );
-
- std::vector outputSquareInfo;
- std::vector> outputImageInfo;
- fheroes2::DivideImageBySquares( boatSpriteOffset, boatSprite, TILEWIDTH, outputSquareInfo, outputImageInfo );
-
- assert( outputSquareInfo.size() == outputImageInfo.size() );
-
- std::vector objectInfo;
- for ( size_t i = 0; i < outputSquareInfo.size(); ++i ) {
- objectInfo.emplace_back( outputSquareInfo[i], outputImageInfo[i].first, outputImageInfo[i].second, icnId, icnIndex, isReflected, static_cast( 255 ) );
- }
-
- return objectInfo;
-}
-
-std::vector Maps::Tiles::getBoatShadowSpritesPerTile() const
-{
- assert( GetObject() == MP2::OBJ_BOAT );
-
- // TODO: boat shadow logic is more complex than this and it is not directly depend on spriteIndex. Find the proper logic and fix it!
- const uint32_t spriteIndex = ( _imageIndex == 255 ) ? 18 : _imageIndex;
-
- const int icnId{ ICN::BOATSHAD };
- const uint32_t icnIndex = spriteIndex % 128;
- const fheroes2::Sprite & boatShadowSprite = fheroes2::AGG::GetICN( icnId, icnIndex );
- const fheroes2::Point boatShadowSpriteOffset( boatShadowSprite.x(), TILEWIDTH + boatShadowSprite.y() - 11 );
-
- // Shadows cannot be flipped so flip flag is always false.
- std::vector outputSquareInfo;
- std::vector> outputImageInfo;
- fheroes2::DivideImageBySquares( boatShadowSpriteOffset, boatShadowSprite, TILEWIDTH, outputSquareInfo, outputImageInfo );
-
- assert( outputSquareInfo.size() == outputImageInfo.size() );
-
- std::vector objectInfo;
- for ( size_t i = 0; i < outputSquareInfo.size(); ++i ) {
- objectInfo.emplace_back( outputSquareInfo[i], outputImageInfo[i].first, outputImageInfo[i].second, icnId, icnIndex, false, static_cast( 255 ) );
- }
-
- return objectInfo;
-}
-
-std::vector Maps::Tiles::getMineGuardianSpritesPerTile() const
-{
- assert( GetObject( false ) == MP2::OBJ_MINES );
-
- std::vector objectInfo;
-
- const int32_t spellID = Maps::getMineSpellIdFromTile( *this );
- switch ( spellID ) {
- case Spell::SETEGUARDIAN:
- case Spell::SETAGUARDIAN:
- case Spell::SETFGUARDIAN:
- case Spell::SETWGUARDIAN: {
- static_assert( Spell::SETAGUARDIAN - Spell::SETEGUARDIAN == 1 && Spell::SETFGUARDIAN - Spell::SETEGUARDIAN == 2 && Spell::SETWGUARDIAN - Spell::SETEGUARDIAN == 3,
- "Why are you changing the order of spells?! Be extremely careful of what you are doing" );
-
- const int icnId{ ICN::OBJNXTRA };
- const uint32_t icnIndex = spellID - Spell::SETEGUARDIAN;
- const fheroes2::Sprite & image = fheroes2::AGG::GetICN( icnId, icnIndex );
-
- std::vector outputSquareInfo;
- std::vector> outputImageInfo;
- fheroes2::DivideImageBySquares( { image.x(), image.y() }, image, TILEWIDTH, outputSquareInfo, outputImageInfo );
-
- assert( outputSquareInfo.size() == outputImageInfo.size() );
-
- for ( size_t i = 0; i < outputSquareInfo.size(); ++i ) {
- objectInfo.emplace_back( outputSquareInfo[i], outputImageInfo[i].first, outputImageInfo[i].second, icnId, icnIndex, false, static_cast( 255 ) );
- }
- break;
- }
- default:
- break;
- }
-
- return objectInfo;
-}
-
-void Maps::Tiles::redrawTopLayerExtraObjects( fheroes2::Image & dst, const bool isPuzzleDraw, const Interface::GameArea & area ) const
-{
- if ( isPuzzleDraw ) {
- // Extra objects should not be shown on Puzzle Map as they are temporary objects appearing under specific conditions like flags.
- return;
- }
-
- // Ghost animation is unique and can be rendered in multiple cases.
- bool renderFlyingGhosts = false;
-
- const MP2::MapObjectType objectType = GetObject( false );
- if ( objectType == MP2::OBJ_ABANDONED_MINE ) {
- renderFlyingGhosts = true;
- }
- else if ( objectType == MP2::OBJ_MINES ) {
- const int32_t spellID = Maps::getMineSpellIdFromTile( *this );
-
- switch ( spellID ) {
- case Spell::NONE:
- // No spell exists. Nothing we need to render.
- case Spell::SETEGUARDIAN:
- case Spell::SETAGUARDIAN:
- case Spell::SETFGUARDIAN:
- case Spell::SETWGUARDIAN:
- // The logic for these spells is done while rendering the bottom layer. Nothing should be done here.
- break;
- case Spell::HAUNT:
- renderFlyingGhosts = true;
- break;
- default:
- // Did you add a new spell for mines? Add the rendering for it above!
- assert( 0 );
- break;
- }
- }
-
- if ( renderFlyingGhosts ) {
- // This sprite is bigger than TILEWIDTH but rendering is correct for heroes and boats.
- // TODO: consider adding this sprite as a part of an addon.
- const fheroes2::Sprite & image = fheroes2::AGG::GetICN( ICN::OBJNHAUN, Game::getAdventureMapAnimationIndex() % 15 );
-
- const uint8_t alphaValue = area.getObjectAlphaValue( _uid );
-
- area.BlitOnTile( dst, image, image.x(), image.y(), Maps::GetPoint( _index ), false, alphaValue );
- }
-}
-
-void Maps::Tiles::redrawTopLayerObject( fheroes2::Image & dst, const bool isPuzzleDraw, const Interface::GameArea & area, const TilesAddon & addon ) const
-{
- if ( isPuzzleDraw && MP2::isHiddenForPuzzle( GetGround(), addon._objectIcnType, addon._imageIndex ) ) {
- return;
- }
-
- renderAddonObject( dst, area, Maps::GetPoint( _index ), addon );
-}
-
Maps::TilesAddon * Maps::Tiles::FindAddonLevel1( uint32_t uniq1 )
{
Addons::iterator it = std::find_if( addons_level1.begin(), addons_level1.end(), [uniq1]( const TilesAddon & v ) { return v.isUniq( uniq1 ); } );
@@ -2351,47 +1813,6 @@ void Maps::Tiles::RestoreAbandonedMine( Tiles & tile, const int resource )
}
}
-std::pair Maps::Tiles::GetMonsterSpriteIndices( const Tiles & tile, uint32_t monsterIndex )
-{
- const int tileIndex = tile._index;
- int attackerIndex = -1;
-
- // scan for a hero around
- for ( const int32_t idx : ScanAroundObject( tileIndex, MP2::OBJ_HEROES, false ) ) {
- const Heroes * hero = world.GetTiles( idx ).GetHeroes();
- assert( hero != nullptr );
-
- // hero is going to attack monsters on this tile
- if ( hero->GetAttackedMonsterTileIndex() == tileIndex ) {
- attackerIndex = idx;
- break;
- }
- }
-
- std::pair spriteIndices( monsterIndex * 9, 0 );
-
- // draw an attacking sprite if there is an attacking hero nearby
- if ( attackerIndex != -1 ) {
- spriteIndices.first += 7;
-
- switch ( Maps::GetDirection( tileIndex, attackerIndex ) ) {
- case Direction::TOP_LEFT:
- case Direction::LEFT:
- case Direction::BOTTOM_LEFT:
- spriteIndices.first += 1;
- break;
- default:
- break;
- }
- }
- else {
- const fheroes2::Point & mp = Maps::GetPoint( tileIndex );
- const std::array & monsterAnimationSequence = fheroes2::getMonsterAnimationSequence();
- spriteIndices.second = monsterIndex * 9 + 1 + monsterAnimationSequence[( Game::getAdventureMapAnimationIndex() + mp.x * mp.y ) % monsterAnimationSequence.size()];
- }
- return spriteIndices;
-}
-
void Maps::Tiles::ClearFog( const int colors )
{
_fogColors &= ~colors;
@@ -2530,229 +1951,6 @@ void Maps::Tiles::updateFogDirectionsInArea( const fheroes2::Point & minPos, con
}
}
-void Maps::Tiles::drawFog( fheroes2::Image & dst, const Interface::GameArea & area ) const
-{
- // This method should not be called for a tile without fog.
- assert( _fogDirection & Direction::CENTER );
-
- const fheroes2::Point & mp = Maps::GetPoint( _index );
-
- // TODO: Cache all directions into a map: have an array which represents all conditions within this method. The index can be '_fogDirection', the value is index.
- // And another array to store revert flag.
-
- if ( DIRECTION_ALL == _fogDirection ) {
- const fheroes2::Image & sf = fheroes2::AGG::GetTIL( TIL::CLOF32, ( mp.x + mp.y ) % 4, 0 );
- area.DrawTile( dst, sf, mp );
- }
- else {
- uint32_t index = 0;
- bool revert = false;
-
- if ( !( _fogDirection & ( Direction::TOP | Direction::BOTTOM | Direction::LEFT | Direction::RIGHT ) ) ) {
- index = 10;
- }
- else if ( ( contains( _fogDirection, Direction::TOP ) ) && !( _fogDirection & ( Direction::BOTTOM | Direction::LEFT | Direction::RIGHT ) ) ) {
- index = 6;
- }
- else if ( ( contains( _fogDirection, Direction::RIGHT ) ) && !( _fogDirection & ( Direction::TOP | Direction::BOTTOM | Direction::LEFT ) ) ) {
- index = 7;
- }
- else if ( ( contains( _fogDirection, Direction::LEFT ) ) && !( _fogDirection & ( Direction::TOP | Direction::BOTTOM | Direction::RIGHT ) ) ) {
- index = 7;
- revert = true;
- }
- else if ( ( contains( _fogDirection, Direction::BOTTOM ) ) && !( _fogDirection & ( Direction::TOP | Direction::LEFT | Direction::RIGHT ) ) ) {
- index = 8;
- }
- else if ( ( contains( _fogDirection, DIRECTION_CENTER_COL ) ) && !( _fogDirection & ( Direction::LEFT | Direction::RIGHT ) ) ) {
- index = 9;
- }
- else if ( ( contains( _fogDirection, DIRECTION_CENTER_ROW ) ) && !( _fogDirection & ( Direction::TOP | Direction::BOTTOM ) ) ) {
- index = 29;
- }
- else if ( _fogDirection == ( DIRECTION_ALL & ( ~Direction::TOP_RIGHT ) ) ) {
- index = 15;
- }
- else if ( _fogDirection == ( DIRECTION_ALL & ( ~Direction::TOP_LEFT ) ) ) {
- index = 15;
- revert = true;
- }
- else if ( _fogDirection == ( DIRECTION_ALL & ( ~Direction::BOTTOM_RIGHT ) ) ) {
- index = 22;
- }
- else if ( _fogDirection == ( DIRECTION_ALL & ( ~Direction::BOTTOM_LEFT ) ) ) {
- index = 22;
- revert = true;
- }
- else if ( _fogDirection == ( DIRECTION_ALL & ( ~( Direction::TOP_RIGHT | Direction::BOTTOM_RIGHT ) ) ) ) {
- index = 16;
- }
- else if ( _fogDirection == ( DIRECTION_ALL & ( ~( Direction::TOP_LEFT | Direction::BOTTOM_LEFT ) ) ) ) {
- index = 16;
- revert = true;
- }
- else if ( _fogDirection == ( DIRECTION_ALL & ( ~( Direction::TOP_RIGHT | Direction::BOTTOM_LEFT ) ) ) ) {
- index = 17;
- }
- else if ( _fogDirection == ( DIRECTION_ALL & ( ~( Direction::TOP_LEFT | Direction::BOTTOM_RIGHT ) ) ) ) {
- index = 17;
- revert = true;
- }
- else if ( _fogDirection == ( DIRECTION_ALL & ( ~( Direction::TOP_LEFT | Direction::TOP_RIGHT ) ) ) ) {
- index = 18;
- }
- else if ( _fogDirection == ( DIRECTION_ALL & ( ~( Direction::BOTTOM_LEFT | Direction::BOTTOM_RIGHT ) ) ) ) {
- index = 23;
- }
- else if ( _fogDirection == ( DIRECTION_ALL & ( ~DIRECTION_TOP_RIGHT_CORNER ) ) ) {
- index = 13;
- }
- else if ( _fogDirection == ( DIRECTION_ALL & ( ~DIRECTION_TOP_LEFT_CORNER ) ) ) {
- index = 13;
- revert = true;
- }
- else if ( _fogDirection == ( DIRECTION_ALL & ( ~DIRECTION_BOTTOM_RIGHT_CORNER ) ) ) {
- index = 14;
- }
- else if ( _fogDirection == ( DIRECTION_ALL & ( ~DIRECTION_BOTTOM_LEFT_CORNER ) ) ) {
- index = 14;
- revert = true;
- }
- else if ( contains( _fogDirection, Direction::LEFT | Direction::BOTTOM_LEFT | Direction::BOTTOM )
- && !( _fogDirection & ( Direction::TOP | Direction::RIGHT ) ) ) {
- index = 11;
- }
- else if ( contains( _fogDirection, Direction::RIGHT | Direction::BOTTOM_RIGHT | Direction::BOTTOM )
- && !( _fogDirection & ( Direction::TOP | Direction::LEFT ) ) ) {
- index = 11;
- revert = true;
- }
- else if ( contains( _fogDirection, Direction::LEFT | Direction::TOP_LEFT | Direction::TOP ) && !( _fogDirection & ( Direction::BOTTOM | Direction::RIGHT ) ) ) {
- index = 12;
- }
- else if ( contains( _fogDirection, Direction::RIGHT | Direction::TOP_RIGHT | Direction::TOP ) && !( _fogDirection & ( Direction::BOTTOM | Direction::LEFT ) ) ) {
- index = 12;
- revert = true;
- }
- else if ( contains( _fogDirection, DIRECTION_CENTER_ROW | Direction::BOTTOM | Direction::TOP | Direction::TOP_LEFT )
- && !( _fogDirection & ( Direction::BOTTOM_LEFT | Direction::BOTTOM_RIGHT | Direction::TOP_RIGHT ) ) ) {
- index = 19;
- }
- else if ( contains( _fogDirection, DIRECTION_CENTER_ROW | Direction::BOTTOM | Direction::TOP | Direction::TOP_RIGHT )
- && !( _fogDirection & ( Direction::BOTTOM_LEFT | Direction::BOTTOM_RIGHT | Direction::TOP_LEFT ) ) ) {
- index = 19;
- revert = true;
- }
- else if ( contains( _fogDirection, DIRECTION_CENTER_ROW | Direction::BOTTOM | Direction::TOP | Direction::BOTTOM_LEFT )
- && !( _fogDirection & ( Direction::TOP_RIGHT | Direction::BOTTOM_RIGHT | Direction::TOP_LEFT ) ) ) {
- index = 20;
- }
- else if ( contains( _fogDirection, DIRECTION_CENTER_ROW | Direction::BOTTOM | Direction::TOP | Direction::BOTTOM_RIGHT )
- && !( _fogDirection & ( Direction::TOP_RIGHT | Direction::BOTTOM_LEFT | Direction::TOP_LEFT ) ) ) {
- index = 20;
- revert = true;
- }
- else if ( contains( _fogDirection, DIRECTION_CENTER_ROW | Direction::BOTTOM | Direction::TOP )
- && !( _fogDirection & ( Direction::TOP_RIGHT | Direction::BOTTOM_RIGHT | Direction::BOTTOM_LEFT | Direction::TOP_LEFT ) ) ) {
- index = 22;
- }
- else if ( contains( _fogDirection, DIRECTION_CENTER_ROW | Direction::BOTTOM | Direction::BOTTOM_LEFT )
- && !( _fogDirection & ( Direction::TOP | Direction::BOTTOM_RIGHT ) ) ) {
- index = 24;
- }
- else if ( contains( _fogDirection, DIRECTION_CENTER_ROW | Direction::BOTTOM | Direction::BOTTOM_RIGHT )
- && !( _fogDirection & ( Direction::TOP | Direction::BOTTOM_LEFT ) ) ) {
- index = 24;
- revert = true;
- }
- else if ( contains( _fogDirection, DIRECTION_CENTER_COL | Direction::LEFT | Direction::TOP_LEFT )
- && !( _fogDirection & ( Direction::RIGHT | Direction::BOTTOM_LEFT ) ) ) {
- index = 25;
- }
- else if ( contains( _fogDirection, DIRECTION_CENTER_COL | Direction::RIGHT | Direction::TOP_RIGHT )
- && !( _fogDirection & ( Direction::LEFT | Direction::BOTTOM_RIGHT ) ) ) {
- index = 25;
- revert = true;
- }
- else if ( contains( _fogDirection, DIRECTION_CENTER_COL | Direction::BOTTOM_LEFT | Direction::LEFT )
- && !( _fogDirection & ( Direction::RIGHT | Direction::TOP_LEFT ) ) ) {
- index = 26;
- }
- else if ( contains( _fogDirection, DIRECTION_CENTER_COL | Direction::BOTTOM_RIGHT | Direction::RIGHT )
- && !( _fogDirection & ( Direction::LEFT | Direction::TOP_RIGHT ) ) ) {
- index = 26;
- revert = true;
- }
- else if ( contains( _fogDirection, DIRECTION_CENTER_ROW | Direction::TOP_LEFT | Direction::TOP )
- && !( _fogDirection & ( Direction::BOTTOM | Direction::TOP_RIGHT ) ) ) {
- index = 30;
- }
- else if ( contains( _fogDirection, DIRECTION_CENTER_ROW | Direction::TOP_RIGHT | Direction::TOP )
- && !( _fogDirection & ( Direction::BOTTOM | Direction::TOP_LEFT ) ) ) {
- index = 30;
- revert = true;
- }
- else if ( contains( _fogDirection, Direction::BOTTOM | Direction::LEFT )
- && !( _fogDirection & ( Direction::TOP | Direction::RIGHT | Direction::BOTTOM_LEFT ) ) ) {
- index = 27;
- }
- else if ( contains( _fogDirection, Direction::BOTTOM | Direction::RIGHT )
- && !( _fogDirection & ( Direction::TOP | Direction::TOP_LEFT | Direction::LEFT | Direction::BOTTOM_RIGHT ) ) ) {
- index = 27;
- revert = true;
- }
- else if ( contains( _fogDirection, Direction::LEFT | Direction::TOP )
- && !( _fogDirection & ( Direction::TOP_LEFT | Direction::RIGHT | Direction::BOTTOM | Direction::BOTTOM_RIGHT ) ) ) {
- index = 28;
- }
- else if ( contains( _fogDirection, Direction::RIGHT | Direction::TOP )
- && !( _fogDirection & ( Direction::TOP_RIGHT | Direction::LEFT | Direction::BOTTOM | Direction::BOTTOM_LEFT ) ) ) {
- index = 28;
- revert = true;
- }
- else if ( contains( _fogDirection, DIRECTION_CENTER_ROW | Direction::TOP )
- && !( _fogDirection & ( Direction::BOTTOM | Direction::TOP_LEFT | Direction::TOP_RIGHT ) ) ) {
- index = 31;
- }
- else if ( contains( _fogDirection, DIRECTION_CENTER_COL | Direction::RIGHT )
- && !( _fogDirection & ( Direction::LEFT | Direction::TOP_RIGHT | Direction::BOTTOM_RIGHT ) ) ) {
- index = 32;
- }
- else if ( contains( _fogDirection, DIRECTION_CENTER_COL | Direction::LEFT )
- && !( _fogDirection & ( Direction::RIGHT | Direction::TOP_LEFT | Direction::BOTTOM_LEFT ) ) ) {
- index = 32;
- revert = true;
- }
- else if ( contains( _fogDirection, DIRECTION_CENTER_ROW | Direction::BOTTOM )
- && !( _fogDirection & ( Direction::TOP | Direction::BOTTOM_LEFT | Direction::BOTTOM_RIGHT ) ) ) {
- index = 33;
- }
- else if ( contains( _fogDirection, DIRECTION_CENTER_ROW | DIRECTION_BOTTOM_ROW ) && !( _fogDirection & Direction::TOP ) ) {
- index = ( _index % 2 ) ? 0 : 1;
- }
- else if ( contains( _fogDirection, DIRECTION_CENTER_ROW | DIRECTION_TOP_ROW ) && !( _fogDirection & Direction::BOTTOM ) ) {
- index = ( _index % 2 ) ? 4 : 5;
- }
- else if ( contains( _fogDirection, DIRECTION_CENTER_COL | DIRECTION_LEFT_COL ) && !( _fogDirection & Direction::RIGHT ) ) {
- index = ( _index % 2 ) ? 2 : 3;
- }
- else if ( contains( _fogDirection, DIRECTION_CENTER_COL | DIRECTION_RIGHT_COL ) && !( _fogDirection & Direction::LEFT ) ) {
- index = ( _index % 2 ) ? 2 : 3;
- revert = true;
- }
- else {
- // unknown
- DEBUG_LOG( DBG_GAME, DBG_WARN, "Invalid direction for fog: " << _fogDirection << ". Tile index: " << _index )
- const fheroes2::Image & sf = fheroes2::AGG::GetTIL( TIL::CLOF32, ( mp.x + mp.y ) % 4, 0 );
- area.DrawTile( dst, sf, mp );
- return;
- }
-
- const fheroes2::Sprite & sprite = fheroes2::AGG::GetICN( ICN::CLOP32, index );
- area.BlitOnTile( dst, sprite, ( revert ? TILEWIDTH - sprite.x() - sprite.width() : sprite.x() ), sprite.y(), mp, revert, 255 );
- }
-}
-
void Maps::Tiles::updateTileById( Maps::Tiles & tile, const uint32_t uid, const uint8_t newIndex )
{
Maps::TilesAddon * addon = tile.FindAddonLevel1( uid );
diff --git a/src/fheroes2/maps/maps_tiles.h b/src/fheroes2/maps/maps_tiles.h
index 590d2352b7e..20f54688ab2 100644
--- a/src/fheroes2/maps/maps_tiles.h
+++ b/src/fheroes2/maps/maps_tiles.h
@@ -39,17 +39,6 @@
class Heroes;
class StreamBase;
-namespace fheroes2
-{
- class Image;
- struct ObjectRenderingInfo;
-}
-
-namespace Interface
-{
- class GameArea;
-}
-
namespace Maps
{
enum ObjectLayerType : uint8_t
@@ -178,8 +167,6 @@ namespace Maps
return 30 > _terrainImageIndex;
}
- const fheroes2::Image & GetTileSurface() const;
-
bool isSameMainObject( const MP2::MapObjectType objectType ) const
{
return objectType == _mainObjectType;
@@ -262,10 +249,6 @@ namespace Maps
void removeOwnershipFlag( const MP2::MapObjectType objectType );
- static void RedrawEmptyTile( fheroes2::Image & dst, const fheroes2::Point & mp, const Interface::GameArea & area );
- void redrawTopLayerExtraObjects( fheroes2::Image & dst, const bool isPuzzleDraw, const Interface::GameArea & area ) const;
- void redrawTopLayerObject( fheroes2::Image & dst, const bool isPuzzleDraw, const Interface::GameArea & area, const TilesAddon & addon ) const;
-
// Determine the fog direction in the area between min and max positions for given player(s) color code and store it in corresponding tile data.
static void updateFogDirectionsInArea( const fheroes2::Point & minPos, const fheroes2::Point & maxPos, const int32_t color );
// Return fog direction of tile. A tile without fog returns "Direction::UNKNOWN".
@@ -274,18 +257,6 @@ namespace Maps
return _fogDirection;
}
- void drawFog( fheroes2::Image & dst, const Interface::GameArea & area ) const;
- void RedrawPassable( fheroes2::Image & dst, const int friendColors, const Interface::GameArea & area ) const;
- void redrawBottomLayerObjects( fheroes2::Image & dst, bool isPuzzleDraw, const Interface::GameArea & area, const uint8_t level ) const;
-
- void drawByObjectIcnType( fheroes2::Image & output, const Interface::GameArea & area, const MP2::ObjectIcnType objectIcnType ) const;
-
- std::vector getMonsterSpritesPerTile() const;
- std::vector getMonsterShadowSpritesPerTile() const;
- std::vector getBoatSpritesPerTile() const;
- std::vector getBoatShadowSpritesPerTile() const;
- std::vector getMineGuardianSpritesPerTile() const;
-
void AddonsPushLevel1( const MP2::mp2tile_t & mt );
void AddonsPushLevel1( const MP2::mp2addon_t & ma );
@@ -345,6 +316,16 @@ namespace Maps
return _metadata;
}
+ uint8_t getTerrainFlags() const
+ {
+ return _terrainFlags;
+ }
+
+ uint16_t getTerrainImageIndex() const
+ {
+ return _terrainImageIndex;
+ }
+
Heroes * GetHeroes() const;
void SetHeroes( Heroes * );
@@ -363,7 +344,6 @@ namespace Maps
bool containsSprite( const MP2::ObjectIcnType objectIcnType, const uint32_t imageIdx ) const;
static std::pair ColorRaceFromHeroSprite( const uint32_t heroSpriteIndex );
- static std::pair GetMonsterSpriteIndices( const Tiles & tile, const uint32_t monsterIndex );
// Restores an abandoned mine whose main tile is 'tile', turning it into an ordinary mine that brings
// resources of type 'resource'. This method updates all sprites and sets object types for non-action
@@ -399,9 +379,6 @@ namespace Maps
friend StreamBase & operator<<( StreamBase &, const Tiles & );
friend StreamBase & operator>>( StreamBase &, Tiles & );
- static void renderAddonObject( fheroes2::Image & output, const Interface::GameArea & area, const fheroes2::Point & offset, const TilesAddon & addon );
- void renderMainObject( fheroes2::Image & output, const Interface::GameArea & area, const fheroes2::Point & offset ) const;
-
static uint8_t convertOldMainObjectType( const uint8_t mainObjectType );
// The old code was using weird quantity based values which were very hard to understand.
diff --git a/src/fheroes2/maps/maps_tiles_render.cpp b/src/fheroes2/maps/maps_tiles_render.cpp
new file mode 100644
index 00000000000..2d7bc2c058c
--- /dev/null
+++ b/src/fheroes2/maps/maps_tiles_render.cpp
@@ -0,0 +1,843 @@
+/***************************************************************************
+ * fheroes2: https://github.com/ihhub/fheroes2 *
+ * Copyright (C) 2023 *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
+ ***************************************************************************/
+
+#include "maps_tiles_render.h"
+
+#include "agg_image.h"
+#include "game.h"
+#include "icn.h"
+#include "interface_gamearea.h"
+#include "logging.h"
+#include "maps.h"
+#include "maps_tiles.h"
+#include "maps_tiles_helper.h"
+#include "monster_anim.h"
+#include "til.h"
+#include "ui_object_rendering.h"
+#include "world.h"
+
+namespace
+{
+ bool contains( const int base, const int value )
+ {
+ return ( base & value ) == value;
+ }
+
+ bool isDirectRenderingRestricted( const int icnId )
+ {
+ switch ( icnId ) {
+ case ICN::UNKNOWN:
+ case ICN::MONS32:
+ case ICN::BOAT32:
+ case ICN::MINIHERO:
+ // Either it is an invalid sprite or a sprite which needs to be divided into tiles in order to properly render it.
+ return true;
+ default:
+ break;
+ }
+
+ return false;
+ }
+
+ void renderAddonObject( fheroes2::Image & output, const Interface::GameArea & area, const fheroes2::Point & offset, const Maps::TilesAddon & addon )
+ {
+ assert( addon._objectIcnType != MP2::OBJ_ICN_TYPE_UNKNOWN && addon._imageIndex != 255 );
+
+ const int icn = MP2::getIcnIdFromObjectIcnType( addon._objectIcnType );
+ if ( isDirectRenderingRestricted( icn ) ) {
+ return;
+ }
+
+ const uint8_t alphaValue = area.getObjectAlphaValue( addon._uid );
+
+ const fheroes2::Sprite & sprite = fheroes2::AGG::GetICN( icn, addon._imageIndex );
+
+ // Ideally we need to check that the image is within a tile area. However, flags are among those for which this rule doesn't apply.
+ if ( icn == ICN::FLAG32 ) {
+ assert( sprite.width() <= TILEWIDTH && sprite.height() <= TILEWIDTH );
+ }
+ else {
+ assert( sprite.x() >= 0 && sprite.width() + sprite.x() <= TILEWIDTH && sprite.y() >= 0 && sprite.height() + sprite.y() <= TILEWIDTH );
+ }
+
+ area.BlitOnTile( output, sprite, sprite.x(), sprite.y(), offset, false, alphaValue );
+
+ const uint32_t animationIndex = ICN::AnimationFrame( icn, addon._imageIndex, Game::getAdventureMapAnimationIndex() );
+ if ( animationIndex > 0 ) {
+ const fheroes2::Sprite & animationSprite = fheroes2::AGG::GetICN( icn, animationIndex );
+
+ // If this assertion blows up we are trying to render an image bigger than a tile. Render this object properly as heroes or monsters!
+ assert( animationSprite.x() >= 0 && animationSprite.width() + animationSprite.x() <= TILEWIDTH && animationSprite.y() >= 0
+ && animationSprite.height() + animationSprite.y() <= TILEWIDTH );
+
+ area.BlitOnTile( output, animationSprite, animationSprite.x(), animationSprite.y(), offset, false, alphaValue );
+ }
+ }
+
+ void renderMainObject( fheroes2::Image & output, const Interface::GameArea & area, const fheroes2::Point & offset, const Maps::Tiles & tile )
+ {
+ assert( tile.getObjectIcnType() != MP2::OBJ_ICN_TYPE_UNKNOWN && tile.GetObjectSpriteIndex() != 255 );
+
+ const int mainObjectIcn = MP2::getIcnIdFromObjectIcnType( tile.getObjectIcnType() );
+ if ( isDirectRenderingRestricted( mainObjectIcn ) ) {
+ return;
+ }
+
+ const uint8_t mainObjectAlphaValue = area.getObjectAlphaValue( tile.GetObjectUID() );
+
+ const fheroes2::Sprite & mainObjectSprite = fheroes2::AGG::GetICN( mainObjectIcn, tile.GetObjectSpriteIndex() );
+
+ // If this assertion blows up we are trying to render an image bigger than a tile. Render this object properly as heroes or monsters!
+ assert( mainObjectSprite.x() >= 0 && mainObjectSprite.width() + mainObjectSprite.x() <= TILEWIDTH && mainObjectSprite.y() >= 0
+ && mainObjectSprite.height() + mainObjectSprite.y() <= TILEWIDTH );
+
+ area.BlitOnTile( output, mainObjectSprite, mainObjectSprite.x(), mainObjectSprite.y(), offset, false, mainObjectAlphaValue );
+
+ // Render possible animation image.
+ // TODO: quantity2 is used in absolutely incorrect way! Fix all the logic for it. As of now (quantity2 != 0) expression is used only for Magic Garden.
+ const uint32_t mainObjectAnimationIndex = ICN::AnimationFrame( mainObjectIcn, tile.GetObjectSpriteIndex(), Game::getAdventureMapAnimationIndex(),
+ tile.metadata()[1] != 0 );
+ if ( mainObjectAnimationIndex > 0 ) {
+ const fheroes2::Sprite & animationSprite = fheroes2::AGG::GetICN( mainObjectIcn, mainObjectAnimationIndex );
+
+ // If this assertion blows up we are trying to render an image bigger than a tile. Render this object properly as heroes or monsters!
+ assert( animationSprite.x() >= 0 && animationSprite.width() + animationSprite.x() <= TILEWIDTH && animationSprite.y() >= 0
+ && animationSprite.height() + animationSprite.y() <= TILEWIDTH );
+
+ area.BlitOnTile( output, animationSprite, animationSprite.x(), animationSprite.y(), offset, false, mainObjectAlphaValue );
+ }
+ }
+
+#ifdef WITH_DEBUG
+ const fheroes2::Image & PassableViewSurface( const int passable )
+ {
+ static std::map imageMap;
+
+ auto iter = imageMap.find( passable );
+ if ( iter != imageMap.end() ) {
+ return iter->second;
+ }
+
+ const int32_t size = 31;
+ const uint8_t red = 0xBA;
+ const uint8_t green = 0x5A;
+
+ fheroes2::Image sf( size, size );
+ sf.reset();
+
+ if ( 0 == passable || Direction::CENTER == passable ) {
+ fheroes2::DrawBorder( sf, red );
+ }
+ else if ( DIRECTION_ALL == passable ) {
+ fheroes2::DrawBorder( sf, green );
+ }
+ else {
+ const uint8_t topLeftColor = ( ( passable & Direction::TOP_LEFT ) != 0 ) ? green : red;
+ const uint8_t bottomRightColor = ( ( passable & Direction::BOTTOM_RIGHT ) != 0 ) ? green : red;
+ const uint8_t topRightColor = ( ( passable & Direction::TOP_RIGHT ) != 0 ) ? green : red;
+ const uint8_t bottomLeftColor = ( ( passable & Direction::BOTTOM_LEFT ) != 0 ) ? green : red;
+ const uint8_t topColor = ( ( passable & Direction::TOP ) != 0 ) ? green : red;
+ const uint8_t bottomColor = ( ( passable & Direction::BOTTOM ) != 0 ) ? green : red;
+ const uint8_t leftColor = ( ( passable & Direction::LEFT ) != 0 ) ? green : red;
+ const uint8_t rightColor = ( ( passable & Direction::RIGHT ) != 0 ) ? green : red;
+
+ uint8_t * image = sf.image();
+ uint8_t * transform = sf.transform();
+
+ // Horizontal
+ for ( int32_t i = 0; i < 10; ++i ) {
+ *( image + i ) = topLeftColor;
+ *( transform + i ) = 0;
+
+ *( image + i + ( size - 1 ) * size ) = bottomLeftColor;
+ *( transform + i + ( size - 1 ) * size ) = 0;
+ }
+
+ for ( int32_t i = 10; i < 21; ++i ) {
+ *( image + i ) = topColor;
+ *( transform + i ) = 0;
+
+ *( image + i + ( size - 1 ) * size ) = bottomColor;
+ *( transform + i + ( size - 1 ) * size ) = 0;
+ }
+
+ for ( int32_t i = 21; i < size; ++i ) {
+ *( image + i ) = topRightColor;
+ *( transform + i ) = 0;
+
+ *( image + i + ( size - 1 ) * size ) = bottomRightColor;
+ *( transform + i + ( size - 1 ) * size ) = 0;
+ }
+
+ // Vertical
+ for ( int32_t i = 0; i < 10; ++i ) {
+ *( image + i * size ) = topLeftColor;
+ *( transform + i * size ) = 0;
+
+ *( image + size - 1 + i * size ) = topRightColor;
+ *( transform + size - 1 + i * size ) = 0;
+ }
+
+ for ( int32_t i = 10; i < 21; ++i ) {
+ *( image + i * size ) = leftColor;
+ *( transform + i * size ) = 0;
+
+ *( image + size - 1 + i * size ) = rightColor;
+ *( transform + size - 1 + i * size ) = 0;
+ }
+
+ for ( int32_t i = 21; i < size; ++i ) {
+ *( image + i * size ) = bottomLeftColor;
+ *( transform + i * size ) = 0;
+
+ *( image + size - 1 + i * size ) = bottomRightColor;
+ *( transform + size - 1 + i * size ) = 0;
+ }
+ }
+
+ return imageMap.try_emplace( passable, std::move( sf ) ).first->second;
+ }
+
+ const fheroes2::Image & getDebugFogImage()
+ {
+ static const fheroes2::Image fog = []() {
+ fheroes2::Image temp( 32, 32 );
+ fheroes2::FillTransform( temp, 0, 0, temp.width(), temp.height(), 2 );
+ return temp;
+ }();
+
+ return fog;
+ }
+#endif
+
+ std::pair GetMonsterSpriteIndices( const Maps::Tiles & tile, uint32_t monsterIndex )
+ {
+ const int tileIndex = tile.GetIndex();
+ int attackerIndex = -1;
+
+ // scan for a hero around
+ for ( const int32_t idx : Maps::ScanAroundObject( tileIndex, MP2::OBJ_HEROES, false ) ) {
+ const Heroes * hero = world.GetTiles( idx ).GetHeroes();
+ assert( hero != nullptr );
+
+ // hero is going to attack monsters on this tile
+ if ( hero->GetAttackedMonsterTileIndex() == tileIndex ) {
+ attackerIndex = idx;
+ break;
+ }
+ }
+
+ std::pair spriteIndices( monsterIndex * 9, 0 );
+
+ // draw an attacking sprite if there is an attacking hero nearby
+ if ( attackerIndex != -1 ) {
+ spriteIndices.first += 7;
+
+ switch ( Maps::GetDirection( tileIndex, attackerIndex ) ) {
+ case Direction::TOP_LEFT:
+ case Direction::LEFT:
+ case Direction::BOTTOM_LEFT:
+ spriteIndices.first += 1;
+ break;
+ default:
+ break;
+ }
+ }
+ else {
+ const fheroes2::Point & mp = Maps::GetPoint( tileIndex );
+ const std::array & monsterAnimationSequence = fheroes2::getMonsterAnimationSequence();
+ spriteIndices.second = monsterIndex * 9 + 1 + monsterAnimationSequence[( Game::getAdventureMapAnimationIndex() + mp.x * mp.y ) % monsterAnimationSequence.size()];
+ }
+ return spriteIndices;
+ }
+}
+
+namespace Maps
+{
+ void redrawEmptyTile( fheroes2::Image & dst, const fheroes2::Point & mp, const Interface::GameArea & area )
+ {
+ if ( mp.y == -1 && mp.x >= 0 && mp.x < world.w() ) { // top first row
+ area.DrawTile( dst, fheroes2::AGG::GetTIL( TIL::STON, 20 + ( mp.x % 4 ), 0 ), mp );
+ }
+ else if ( mp.x == world.w() && mp.y >= 0 && mp.y < world.h() ) { // right first row
+ area.DrawTile( dst, fheroes2::AGG::GetTIL( TIL::STON, 24 + ( mp.y % 4 ), 0 ), mp );
+ }
+ else if ( mp.y == world.h() && mp.x >= 0 && mp.x < world.w() ) { // bottom first row
+ area.DrawTile( dst, fheroes2::AGG::GetTIL( TIL::STON, 28 + ( mp.x % 4 ), 0 ), mp );
+ }
+ else if ( mp.x == -1 && mp.y >= 0 && mp.y < world.h() ) { // left first row
+ area.DrawTile( dst, fheroes2::AGG::GetTIL( TIL::STON, 32 + ( mp.y % 4 ), 0 ), mp );
+ }
+ else {
+ area.DrawTile( dst, fheroes2::AGG::GetTIL( TIL::STON, ( std::abs( mp.y ) % 4 ) * 4 + std::abs( mp.x ) % 4, 0 ), mp );
+ }
+ }
+
+ void redrawTopLayerExtraObjects( const Tiles & tile, fheroes2::Image & dst, const bool isPuzzleDraw, const Interface::GameArea & area )
+ {
+ if ( isPuzzleDraw ) {
+ // Extra objects should not be shown on Puzzle Map as they are temporary objects appearing under specific conditions like flags.
+ return;
+ }
+
+ // Ghost animation is unique and can be rendered in multiple cases.
+ bool renderFlyingGhosts = false;
+
+ const MP2::MapObjectType objectType = tile.GetObject( false );
+ if ( objectType == MP2::OBJ_ABANDONED_MINE ) {
+ renderFlyingGhosts = true;
+ }
+ else if ( objectType == MP2::OBJ_MINES ) {
+ const int32_t spellID = Maps::getMineSpellIdFromTile( tile );
+
+ switch ( spellID ) {
+ case Spell::NONE:
+ // No spell exists. Nothing we need to render.
+ case Spell::SETEGUARDIAN:
+ case Spell::SETAGUARDIAN:
+ case Spell::SETFGUARDIAN:
+ case Spell::SETWGUARDIAN:
+ // The logic for these spells is done while rendering the bottom layer. Nothing should be done here.
+ break;
+ case Spell::HAUNT:
+ renderFlyingGhosts = true;
+ break;
+ default:
+ // Did you add a new spell for mines? Add the rendering for it above!
+ assert( 0 );
+ break;
+ }
+ }
+
+ if ( renderFlyingGhosts ) {
+ // This sprite is bigger than TILEWIDTH but rendering is correct for heroes and boats.
+ // TODO: consider adding this sprite as a part of an addon.
+ const fheroes2::Sprite & image = fheroes2::AGG::GetICN( ICN::OBJNHAUN, Game::getAdventureMapAnimationIndex() % 15 );
+
+ const uint8_t alphaValue = area.getObjectAlphaValue( tile.GetObjectUID() );
+
+ area.BlitOnTile( dst, image, image.x(), image.y(), Maps::GetPoint( tile.GetIndex() ), false, alphaValue );
+ }
+ }
+
+ void redrawTopLayerObject( const Tiles & tile, fheroes2::Image & dst, const bool isPuzzleDraw, const Interface::GameArea & area, const TilesAddon & addon )
+ {
+ if ( isPuzzleDraw && MP2::isHiddenForPuzzle( tile.GetGround(), addon._objectIcnType, addon._imageIndex ) ) {
+ return;
+ }
+
+ renderAddonObject( dst, area, Maps::GetPoint( tile.GetIndex() ), addon );
+ }
+
+ void drawFog( const Tiles & tile, fheroes2::Image & dst, const Interface::GameArea & area )
+ {
+ const uint16_t fogDirection = tile.getFogDirection();
+ // This method should not be called for a tile without fog.
+ assert( fogDirection & Direction::CENTER );
+
+ const fheroes2::Point & mp = Maps::GetPoint( tile.GetIndex() );
+
+ // TODO: Cache all directions into a map: have an array which represents all conditions within this method. The index can be 'fogDirection', the value is index.
+ // And another array to store revert flag.
+
+
+ if ( DIRECTION_ALL == fogDirection ) {
+ const fheroes2::Image & sf = fheroes2::AGG::GetTIL( TIL::CLOF32, ( mp.x + mp.y ) % 4, 0 );
+ area.DrawTile( dst, sf, mp );
+ }
+ else {
+ uint32_t index = 0;
+ bool revert = false;
+
+ if ( !( fogDirection & ( Direction::TOP | Direction::BOTTOM | Direction::LEFT | Direction::RIGHT ) ) ) {
+ index = 10;
+ }
+ else if ( ( contains( fogDirection, Direction::TOP ) ) && !( fogDirection & ( Direction::BOTTOM | Direction::LEFT | Direction::RIGHT ) ) ) {
+ index = 6;
+ }
+ else if ( ( contains( fogDirection, Direction::RIGHT ) ) && !( fogDirection & ( Direction::TOP | Direction::BOTTOM | Direction::LEFT ) ) ) {
+ index = 7;
+ }
+ else if ( ( contains( fogDirection, Direction::LEFT ) ) && !( fogDirection & ( Direction::TOP | Direction::BOTTOM | Direction::RIGHT ) ) ) {
+ index = 7;
+ revert = true;
+ }
+ else if ( ( contains( fogDirection, Direction::BOTTOM ) ) && !( fogDirection & ( Direction::TOP | Direction::LEFT | Direction::RIGHT ) ) ) {
+ index = 8;
+ }
+ else if ( ( contains( fogDirection, DIRECTION_CENTER_COL ) ) && !( fogDirection & ( Direction::LEFT | Direction::RIGHT ) ) ) {
+ index = 9;
+ }
+ else if ( ( contains( fogDirection, DIRECTION_CENTER_ROW ) ) && !( fogDirection & ( Direction::TOP | Direction::BOTTOM ) ) ) {
+ index = 29;
+ }
+ else if ( fogDirection == ( DIRECTION_ALL & ( ~Direction::TOP_RIGHT ) ) ) {
+ index = 15;
+ }
+ else if ( fogDirection == ( DIRECTION_ALL & ( ~Direction::TOP_LEFT ) ) ) {
+ index = 15;
+ revert = true;
+ }
+ else if ( fogDirection == ( DIRECTION_ALL & ( ~Direction::BOTTOM_RIGHT ) ) ) {
+ index = 22;
+ }
+ else if ( fogDirection == ( DIRECTION_ALL & ( ~Direction::BOTTOM_LEFT ) ) ) {
+ index = 22;
+ revert = true;
+ }
+ else if ( fogDirection == ( DIRECTION_ALL & ( ~( Direction::TOP_RIGHT | Direction::BOTTOM_RIGHT ) ) ) ) {
+ index = 16;
+ }
+ else if ( fogDirection == ( DIRECTION_ALL & ( ~( Direction::TOP_LEFT | Direction::BOTTOM_LEFT ) ) ) ) {
+ index = 16;
+ revert = true;
+ }
+ else if ( fogDirection == ( DIRECTION_ALL & ( ~( Direction::TOP_RIGHT | Direction::BOTTOM_LEFT ) ) ) ) {
+ index = 17;
+ }
+ else if ( fogDirection == ( DIRECTION_ALL & ( ~( Direction::TOP_LEFT | Direction::BOTTOM_RIGHT ) ) ) ) {
+ index = 17;
+ revert = true;
+ }
+ else if ( fogDirection == ( DIRECTION_ALL & ( ~( Direction::TOP_LEFT | Direction::TOP_RIGHT ) ) ) ) {
+ index = 18;
+ }
+ else if ( fogDirection == ( DIRECTION_ALL & ( ~( Direction::BOTTOM_LEFT | Direction::BOTTOM_RIGHT ) ) ) ) {
+ index = 23;
+ }
+ else if ( fogDirection == ( DIRECTION_ALL & ( ~DIRECTION_TOP_RIGHT_CORNER ) ) ) {
+ index = 13;
+ }
+ else if ( fogDirection == ( DIRECTION_ALL & ( ~DIRECTION_TOP_LEFT_CORNER ) ) ) {
+ index = 13;
+ revert = true;
+ }
+ else if ( fogDirection == ( DIRECTION_ALL & ( ~DIRECTION_BOTTOM_RIGHT_CORNER ) ) ) {
+ index = 14;
+ }
+ else if ( fogDirection == ( DIRECTION_ALL & ( ~DIRECTION_BOTTOM_LEFT_CORNER ) ) ) {
+ index = 14;
+ revert = true;
+ }
+ else if ( contains( fogDirection, Direction::LEFT | Direction::BOTTOM_LEFT | Direction::BOTTOM )
+ && !( fogDirection & ( Direction::TOP | Direction::RIGHT ) ) ) {
+ index = 11;
+ }
+ else if ( contains( fogDirection, Direction::RIGHT | Direction::BOTTOM_RIGHT | Direction::BOTTOM )
+ && !( fogDirection & ( Direction::TOP | Direction::LEFT ) ) ) {
+ index = 11;
+ revert = true;
+ }
+ else if ( contains( fogDirection, Direction::LEFT | Direction::TOP_LEFT | Direction::TOP ) && !( fogDirection & ( Direction::BOTTOM | Direction::RIGHT ) ) ) {
+ index = 12;
+ }
+ else if ( contains( fogDirection, Direction::RIGHT | Direction::TOP_RIGHT | Direction::TOP ) && !( fogDirection & ( Direction::BOTTOM | Direction::LEFT ) ) ) {
+ index = 12;
+ revert = true;
+ }
+ else if ( contains( fogDirection, DIRECTION_CENTER_ROW | Direction::BOTTOM | Direction::TOP | Direction::TOP_LEFT )
+ && !( fogDirection & ( Direction::BOTTOM_LEFT | Direction::BOTTOM_RIGHT | Direction::TOP_RIGHT ) ) ) {
+ index = 19;
+ }
+ else if ( contains( fogDirection, DIRECTION_CENTER_ROW | Direction::BOTTOM | Direction::TOP | Direction::TOP_RIGHT )
+ && !( fogDirection & ( Direction::BOTTOM_LEFT | Direction::BOTTOM_RIGHT | Direction::TOP_LEFT ) ) ) {
+ index = 19;
+ revert = true;
+ }
+ else if ( contains( fogDirection, DIRECTION_CENTER_ROW | Direction::BOTTOM | Direction::TOP | Direction::BOTTOM_LEFT )
+ && !( fogDirection & ( Direction::TOP_RIGHT | Direction::BOTTOM_RIGHT | Direction::TOP_LEFT ) ) ) {
+ index = 20;
+ }
+ else if ( contains( fogDirection, DIRECTION_CENTER_ROW | Direction::BOTTOM | Direction::TOP | Direction::BOTTOM_RIGHT )
+ && !( fogDirection & ( Direction::TOP_RIGHT | Direction::BOTTOM_LEFT | Direction::TOP_LEFT ) ) ) {
+ index = 20;
+ revert = true;
+ }
+ else if ( contains( fogDirection, DIRECTION_CENTER_ROW | Direction::BOTTOM | Direction::TOP )
+ && !( fogDirection & ( Direction::TOP_RIGHT | Direction::BOTTOM_RIGHT | Direction::BOTTOM_LEFT | Direction::TOP_LEFT ) ) ) {
+ index = 22;
+ }
+ else if ( contains( fogDirection, DIRECTION_CENTER_ROW | Direction::BOTTOM | Direction::BOTTOM_LEFT )
+ && !( fogDirection & ( Direction::TOP | Direction::BOTTOM_RIGHT ) ) ) {
+ index = 24;
+ }
+ else if ( contains( fogDirection, DIRECTION_CENTER_ROW | Direction::BOTTOM | Direction::BOTTOM_RIGHT )
+ && !( fogDirection & ( Direction::TOP | Direction::BOTTOM_LEFT ) ) ) {
+ index = 24;
+ revert = true;
+ }
+ else if ( contains( fogDirection, DIRECTION_CENTER_COL | Direction::LEFT | Direction::TOP_LEFT )
+ && !( fogDirection & ( Direction::RIGHT | Direction::BOTTOM_LEFT ) ) ) {
+ index = 25;
+ }
+ else if ( contains( fogDirection, DIRECTION_CENTER_COL | Direction::RIGHT | Direction::TOP_RIGHT )
+ && !( fogDirection & ( Direction::LEFT | Direction::BOTTOM_RIGHT ) ) ) {
+ index = 25;
+ revert = true;
+ }
+ else if ( contains( fogDirection, DIRECTION_CENTER_COL | Direction::BOTTOM_LEFT | Direction::LEFT )
+ && !( fogDirection & ( Direction::RIGHT | Direction::TOP_LEFT ) ) ) {
+ index = 26;
+ }
+ else if ( contains( fogDirection, DIRECTION_CENTER_COL | Direction::BOTTOM_RIGHT | Direction::RIGHT )
+ && !( fogDirection & ( Direction::LEFT | Direction::TOP_RIGHT ) ) ) {
+ index = 26;
+ revert = true;
+ }
+ else if ( contains( fogDirection, DIRECTION_CENTER_ROW | Direction::TOP_LEFT | Direction::TOP )
+ && !( fogDirection & ( Direction::BOTTOM | Direction::TOP_RIGHT ) ) ) {
+ index = 30;
+ }
+ else if ( contains( fogDirection, DIRECTION_CENTER_ROW | Direction::TOP_RIGHT | Direction::TOP )
+ && !( fogDirection & ( Direction::BOTTOM | Direction::TOP_LEFT ) ) ) {
+ index = 30;
+ revert = true;
+ }
+ else if ( contains( fogDirection, Direction::BOTTOM | Direction::LEFT )
+ && !( fogDirection & ( Direction::TOP | Direction::RIGHT | Direction::BOTTOM_LEFT ) ) ) {
+ index = 27;
+ }
+ else if ( contains( fogDirection, Direction::BOTTOM | Direction::RIGHT )
+ && !( fogDirection & ( Direction::TOP | Direction::TOP_LEFT | Direction::LEFT | Direction::BOTTOM_RIGHT ) ) ) {
+ index = 27;
+ revert = true;
+ }
+ else if ( contains( fogDirection, Direction::LEFT | Direction::TOP )
+ && !( fogDirection & ( Direction::TOP_LEFT | Direction::RIGHT | Direction::BOTTOM | Direction::BOTTOM_RIGHT ) ) ) {
+ index = 28;
+ }
+ else if ( contains( fogDirection, Direction::RIGHT | Direction::TOP )
+ && !( fogDirection & ( Direction::TOP_RIGHT | Direction::LEFT | Direction::BOTTOM | Direction::BOTTOM_LEFT ) ) ) {
+ index = 28;
+ revert = true;
+ }
+ else if ( contains( fogDirection, DIRECTION_CENTER_ROW | Direction::TOP )
+ && !( fogDirection & ( Direction::BOTTOM | Direction::TOP_LEFT | Direction::TOP_RIGHT ) ) ) {
+ index = 31;
+ }
+ else if ( contains( fogDirection, DIRECTION_CENTER_COL | Direction::RIGHT )
+ && !( fogDirection & ( Direction::LEFT | Direction::TOP_RIGHT | Direction::BOTTOM_RIGHT ) ) ) {
+ index = 32;
+ }
+ else if ( contains( fogDirection, DIRECTION_CENTER_COL | Direction::LEFT )
+ && !( fogDirection & ( Direction::RIGHT | Direction::TOP_LEFT | Direction::BOTTOM_LEFT ) ) ) {
+ index = 32;
+ revert = true;
+ }
+ else if ( contains( fogDirection, DIRECTION_CENTER_ROW | Direction::BOTTOM )
+ && !( fogDirection & ( Direction::TOP | Direction::BOTTOM_LEFT | Direction::BOTTOM_RIGHT ) ) ) {
+ index = 33;
+ }
+ else if ( contains( fogDirection, DIRECTION_CENTER_ROW | DIRECTION_BOTTOM_ROW ) && !( fogDirection & Direction::TOP ) ) {
+ index = ( tile.GetIndex() % 2 ) ? 0 : 1;
+ }
+ else if ( contains( fogDirection, DIRECTION_CENTER_ROW | DIRECTION_TOP_ROW ) && !( fogDirection & Direction::BOTTOM ) ) {
+ index = ( tile.GetIndex() % 2 ) ? 4 : 5;
+ }
+ else if ( contains( fogDirection, DIRECTION_CENTER_COL | DIRECTION_LEFT_COL ) && !( fogDirection & Direction::RIGHT ) ) {
+ index = ( tile.GetIndex() % 2 ) ? 2 : 3;
+ }
+ else if ( contains( fogDirection, DIRECTION_CENTER_COL | DIRECTION_RIGHT_COL ) && !( fogDirection & Direction::LEFT ) ) {
+ index = ( tile.GetIndex() % 2 ) ? 2 : 3;
+ revert = true;
+ }
+ else {
+ // unknown
+ DEBUG_LOG( DBG_GAME, DBG_WARN, "Invalid direction for fog: " << fogDirection << ". Tile index: " << tile.GetIndex() )
+ const fheroes2::Image & sf = fheroes2::AGG::GetTIL( TIL::CLOF32, ( mp.x + mp.y ) % 4, 0 );
+ area.DrawTile( dst, sf, mp );
+ return;
+ }
+
+ const fheroes2::Sprite & sprite = fheroes2::AGG::GetICN( ICN::CLOP32, index );
+ area.BlitOnTile( dst, sprite, ( revert ? TILEWIDTH - sprite.x() - sprite.width() : sprite.x() ), sprite.y(), mp, revert, 255 );
+ }
+ }
+
+ void redrawPassable( const Tiles & tile, fheroes2::Image & dst, const int friendColors, const Interface::GameArea & area )
+ {
+ #ifdef WITH_DEBUG
+ if ( tile.isFog( friendColors ) ) {
+ area.BlitOnTile( dst, getDebugFogImage(), 0, 0, Maps::GetPoint( tile.GetIndex() ), false, 255 );
+ }
+ if ( 0 == tile.GetPassable() || DIRECTION_ALL != tile.GetPassable() ) {
+ area.BlitOnTile( dst, PassableViewSurface( tile.GetPassable() ), 0, 0, Maps::GetPoint( tile.GetIndex() ), false, 255 );
+ }
+ #else
+ (void)dst;
+ (void)area;
+ (void)friendColors;
+ #endif
+ }
+
+ void redrawBottomLayerObjects( const Tiles & tile, fheroes2::Image & dst, bool isPuzzleDraw, const Interface::GameArea & area, const uint8_t level )
+ {
+ assert( level <= 0x03 );
+
+ const fheroes2::Point & mp = Maps::GetPoint( tile.GetIndex() );
+
+ // Since the original game stores information about objects in a very weird way and this is how it is implemented for us we need to do the following procedure:
+ // - run through all bottom objects first which are stored in the addon stack
+ // - check the main object which is on the tile
+
+ // Some addons must be rendered after the main object on the tile. This applies for flags.
+ // Since this method is called intensively during rendering we have to avoid memory allocation on heap.
+ const size_t maxPostRenderAddons = 16;
+ std::array postRenderingAddon{};
+ size_t postRenderAddonCount = 0;
+
+ for ( const TilesAddon & addon : tile.getLevel1Addons() ) {
+ if ( ( addon._layerType & 0x03 ) != level ) {
+ continue;
+ }
+
+ if ( isPuzzleDraw && MP2::isHiddenForPuzzle( tile.GetGround(), addon._objectIcnType, addon._imageIndex ) ) {
+ continue;
+ }
+
+ if ( addon._objectIcnType == MP2::OBJ_ICN_TYPE_FLAG32 ) {
+ // Based on logically thinking it is impossible to have more than 16 flags on a single tile.
+ assert( postRenderAddonCount < maxPostRenderAddons );
+
+ postRenderingAddon[postRenderAddonCount] = &addon;
+ ++postRenderAddonCount;
+ continue;
+ }
+
+ renderAddonObject( dst, area, mp, addon );
+ }
+
+ if ( tile.getObjectIcnType() != MP2::OBJ_ICN_TYPE_UNKNOWN && ( tile.getLayerType() & 0x03 ) == level
+ && ( !isPuzzleDraw || !MP2::isHiddenForPuzzle( tile.GetGround(), tile.getObjectIcnType(), tile.GetObjectSpriteIndex() ) ) ) {
+ renderMainObject( dst, area, mp, tile );
+ }
+
+ for ( size_t i = 0; i < postRenderAddonCount; ++i ) {
+ assert( postRenderingAddon[i] != nullptr );
+
+ renderAddonObject( dst, area, mp, *postRenderingAddon[i] );
+ }
+ }
+
+ void drawByObjectIcnType( const Tiles & tile, fheroes2::Image & output, const Interface::GameArea & area, const MP2::ObjectIcnType objectIcnType )
+ {
+ const fheroes2::Point & tileOffset = Maps::GetPoint( tile.GetIndex() );
+
+ for ( const TilesAddon & addon : tile.getLevel1Addons() ) {
+ if ( addon._objectIcnType == objectIcnType ) {
+ renderAddonObject( output, area, tileOffset, addon );
+ }
+ }
+
+ if ( tile.getObjectIcnType() == objectIcnType ) {
+ renderMainObject( output, area, tileOffset, tile );
+ }
+
+ for ( const TilesAddon & addon : tile.getLevel2Addons() ) {
+ if ( addon._objectIcnType == objectIcnType ) {
+ renderAddonObject( output, area, tileOffset, addon );
+ }
+ }
+ }
+
+ std::vector getMonsterSpritesPerTile( const Tiles & tile )
+ {
+ assert( tile.GetObject() == MP2::OBJ_MONSTER );
+
+ const Monster & monster = getMonsterFromTile( tile );
+ const std::pair spriteIndices = GetMonsterSpriteIndices( tile, monster.GetSpriteIndex() );
+
+ const int icnId{ ICN::MINI_MONSTER_IMAGE };
+ const fheroes2::Sprite & monsterSprite = fheroes2::AGG::GetICN( icnId, spriteIndices.first );
+ const fheroes2::Point monsterSpriteOffset( monsterSprite.x() + 16, monsterSprite.y() + 30 );
+
+ std::vector outputSquareInfo;
+ std::vector> outputImageInfo;
+ fheroes2::DivideImageBySquares( monsterSpriteOffset, monsterSprite, TILEWIDTH, outputSquareInfo, outputImageInfo );
+
+ assert( outputSquareInfo.size() == outputImageInfo.size() );
+
+ std::vector objectInfo;
+ for ( size_t i = 0; i < outputSquareInfo.size(); ++i ) {
+ objectInfo.emplace_back( outputSquareInfo[i], outputImageInfo[i].first, outputImageInfo[i].second, icnId, spriteIndices.first, false,
+ static_cast( 255 ) );
+ }
+
+ outputSquareInfo.clear();
+ outputImageInfo.clear();
+
+ if ( spriteIndices.second > 0 ) {
+ const fheroes2::Sprite & secondaryMonsterSprite = fheroes2::AGG::GetICN( icnId, spriteIndices.second );
+ const fheroes2::Point secondaryMonsterSpriteOffset( secondaryMonsterSprite.x() + 16, secondaryMonsterSprite.y() + 30 );
+
+ fheroes2::DivideImageBySquares( secondaryMonsterSpriteOffset, secondaryMonsterSprite, TILEWIDTH, outputSquareInfo, outputImageInfo );
+
+ assert( outputSquareInfo.size() == outputImageInfo.size() );
+
+ for ( size_t i = 0; i < outputSquareInfo.size(); ++i ) {
+ objectInfo.emplace_back( outputSquareInfo[i], outputImageInfo[i].first, outputImageInfo[i].second, icnId, spriteIndices.second, false,
+ static_cast( 255 ) );
+ }
+ }
+
+ return objectInfo;
+ }
+
+ std::vector getMonsterShadowSpritesPerTile( const Tiles & tile )
+ {
+ assert( tile.GetObject() == MP2::OBJ_MONSTER );
+
+ const Monster & monster = getMonsterFromTile( tile );
+ const std::pair spriteIndices = GetMonsterSpriteIndices( tile, monster.GetSpriteIndex() );
+
+ const int icnId{ ICN::MINI_MONSTER_SHADOW };
+ const fheroes2::Sprite & monsterSprite = fheroes2::AGG::GetICN( icnId, spriteIndices.first );
+ const fheroes2::Point monsterSpriteOffset( monsterSprite.x() + 16, monsterSprite.y() + 30 );
+
+ std::vector outputSquareInfo;
+ std::vector> outputImageInfo;
+ fheroes2::DivideImageBySquares( monsterSpriteOffset, monsterSprite, TILEWIDTH, outputSquareInfo, outputImageInfo );
+
+ assert( outputSquareInfo.size() == outputImageInfo.size() );
+
+ std::vector objectInfo;
+ for ( size_t i = 0; i < outputSquareInfo.size(); ++i ) {
+ objectInfo.emplace_back( outputSquareInfo[i], outputImageInfo[i].first, outputImageInfo[i].second, icnId, spriteIndices.first, false,
+ static_cast( 255 ) );
+ }
+
+ outputSquareInfo.clear();
+ outputImageInfo.clear();
+
+ if ( spriteIndices.second > 0 ) {
+ const fheroes2::Sprite & secondaryMonsterSprite = fheroes2::AGG::GetICN( icnId, spriteIndices.second );
+ const fheroes2::Point secondaryMonsterSpriteOffset( secondaryMonsterSprite.x() + 16, secondaryMonsterSprite.y() + 30 );
+
+ fheroes2::DivideImageBySquares( secondaryMonsterSpriteOffset, secondaryMonsterSprite, TILEWIDTH, outputSquareInfo, outputImageInfo );
+
+ assert( outputSquareInfo.size() == outputImageInfo.size() );
+
+ for ( size_t i = 0; i < outputSquareInfo.size(); ++i ) {
+ objectInfo.emplace_back( outputSquareInfo[i], outputImageInfo[i].first, outputImageInfo[i].second, icnId, spriteIndices.second, false,
+ static_cast( 255 ) );
+ }
+ }
+
+ return objectInfo;
+ }
+
+ std::vector getBoatSpritesPerTile( const Tiles & tile )
+ {
+ // TODO: combine both boat image generation for heroes and empty boats.
+ assert( tile.GetObject() == MP2::OBJ_BOAT );
+
+ const uint32_t spriteIndex = ( tile.GetObjectSpriteIndex() == 255 ) ? 18 : tile.GetObjectSpriteIndex();
+
+ const bool isReflected = ( spriteIndex > 128 );
+
+ const int icnId{ ICN::BOAT32 };
+ const uint32_t icnIndex = spriteIndex % 128;
+ const fheroes2::Sprite & boatSprite = fheroes2::AGG::GetICN( icnId, icnIndex );
+
+ const fheroes2::Point boatSpriteOffset( ( isReflected ? ( TILEWIDTH + 1 - boatSprite.x() - boatSprite.width() ) : boatSprite.x() ), boatSprite.y() + TILEWIDTH - 11 );
+
+ std::vector outputSquareInfo;
+ std::vector> outputImageInfo;
+ fheroes2::DivideImageBySquares( boatSpriteOffset, boatSprite, TILEWIDTH, outputSquareInfo, outputImageInfo );
+
+ assert( outputSquareInfo.size() == outputImageInfo.size() );
+
+ std::vector objectInfo;
+ for ( size_t i = 0; i < outputSquareInfo.size(); ++i ) {
+ objectInfo.emplace_back( outputSquareInfo[i], outputImageInfo[i].first, outputImageInfo[i].second, icnId, icnIndex, isReflected, static_cast( 255 ) );
+ }
+
+ return objectInfo;
+ }
+
+ std::vector getBoatShadowSpritesPerTile( const Tiles & tile )
+ {
+ assert( tile.GetObject() == MP2::OBJ_BOAT );
+
+ // TODO: boat shadow logic is more complex than this and it is not directly depend on spriteIndex. Find the proper logic and fix it!
+ const uint32_t spriteIndex = ( tile.GetObjectSpriteIndex() == 255 ) ? 18 : tile.GetObjectSpriteIndex();
+
+ const int icnId{ ICN::BOATSHAD };
+ const uint32_t icnIndex = spriteIndex % 128;
+ const fheroes2::Sprite & boatShadowSprite = fheroes2::AGG::GetICN( icnId, icnIndex );
+ const fheroes2::Point boatShadowSpriteOffset( boatShadowSprite.x(), TILEWIDTH + boatShadowSprite.y() - 11 );
+
+ // Shadows cannot be flipped so flip flag is always false.
+ std::vector outputSquareInfo;
+ std::vector> outputImageInfo;
+ fheroes2::DivideImageBySquares( boatShadowSpriteOffset, boatShadowSprite, TILEWIDTH, outputSquareInfo, outputImageInfo );
+
+ assert( outputSquareInfo.size() == outputImageInfo.size() );
+
+ std::vector objectInfo;
+ for ( size_t i = 0; i < outputSquareInfo.size(); ++i ) {
+ objectInfo.emplace_back( outputSquareInfo[i], outputImageInfo[i].first, outputImageInfo[i].second, icnId, icnIndex, false, static_cast( 255 ) );
+ }
+
+ return objectInfo;
+ }
+
+ std::vector getMineGuardianSpritesPerTile( const Tiles & tile )
+ {
+ assert( tile.GetObject( false ) == MP2::OBJ_MINES );
+
+ std::vector objectInfo;
+
+ const int32_t spellID = Maps::getMineSpellIdFromTile( tile );
+ switch ( spellID ) {
+ case Spell::SETEGUARDIAN:
+ case Spell::SETAGUARDIAN:
+ case Spell::SETFGUARDIAN:
+ case Spell::SETWGUARDIAN: {
+ static_assert( Spell::SETAGUARDIAN - Spell::SETEGUARDIAN == 1 && Spell::SETFGUARDIAN - Spell::SETEGUARDIAN == 2 && Spell::SETWGUARDIAN - Spell::SETEGUARDIAN == 3,
+ "Why are you changing the order of spells?! Be extremely careful of what you are doing" );
+
+ const int icnId{ ICN::OBJNXTRA };
+ const uint32_t icnIndex = spellID - Spell::SETEGUARDIAN;
+ const fheroes2::Sprite & image = fheroes2::AGG::GetICN( icnId, icnIndex );
+
+ std::vector outputSquareInfo;
+ std::vector> outputImageInfo;
+ fheroes2::DivideImageBySquares( { image.x(), image.y() }, image, TILEWIDTH, outputSquareInfo, outputImageInfo );
+
+ assert( outputSquareInfo.size() == outputImageInfo.size() );
+
+ for ( size_t i = 0; i < outputSquareInfo.size(); ++i ) {
+ objectInfo.emplace_back( outputSquareInfo[i], outputImageInfo[i].first, outputImageInfo[i].second, icnId, icnIndex, false, static_cast( 255 ) );
+ }
+ break;
+ }
+ default:
+ break;
+ }
+
+ return objectInfo;
+ }
+
+ const fheroes2::Image & getTileSurface( const Tiles & tile )
+ {
+ return fheroes2::AGG::GetTIL( TIL::GROUND32, tile.getTerrainImageIndex(), ( tile.getTerrainFlags() & 0x3 ) );
+ }
+}
diff --git a/src/fheroes2/maps/maps_tiles_render.h b/src/fheroes2/maps/maps_tiles_render.h
new file mode 100644
index 00000000000..d50fa9e84df
--- /dev/null
+++ b/src/fheroes2/maps/maps_tiles_render.h
@@ -0,0 +1,63 @@
+/***************************************************************************
+ * fheroes2: https://github.com/ihhub/fheroes2 *
+ * Copyright (C) 2023 *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
+ ***************************************************************************/
+#pragma once
+
+#include
+
+#include "math_base.h"
+#include "mp2.h"
+
+namespace fheroes2
+{
+ class Image;
+ struct ObjectRenderingInfo;
+}
+
+namespace Interface
+{
+ class GameArea;
+}
+
+namespace Maps
+{
+ class Tiles;
+ struct TilesAddon;
+
+ void redrawEmptyTile( fheroes2::Image & dst, const fheroes2::Point & mp, const Interface::GameArea & area );
+
+ void redrawTopLayerExtraObjects( const Tiles & tile, fheroes2::Image & dst, const bool isPuzzleDraw, const Interface::GameArea & area );
+ void redrawTopLayerObject( const Tiles & tile, fheroes2::Image & dst, const bool isPuzzleDraw, const Interface::GameArea & area, const TilesAddon & addon );
+
+ void drawFog( const Tiles & tile, fheroes2::Image & dst, const Interface::GameArea & area );
+
+ void redrawPassable( const Tiles & tile, fheroes2::Image & dst, const int friendColors, const Interface::GameArea & area );
+
+ void redrawBottomLayerObjects( const Tiles & tile, fheroes2::Image & dst, bool isPuzzleDraw, const Interface::GameArea & area, const uint8_t level );
+
+ void drawByObjectIcnType( const Tiles & tile, fheroes2::Image & output, const Interface::GameArea & area, const MP2::ObjectIcnType objectIcnType );
+
+ std::vector getMonsterSpritesPerTile( const Tiles & tile );
+ std::vector getMonsterShadowSpritesPerTile( const Tiles & tile );
+ std::vector getBoatSpritesPerTile( const Tiles & tile );
+ std::vector getBoatShadowSpritesPerTile( const Tiles & tile );
+ std::vector getMineGuardianSpritesPerTile( const Tiles & tile );
+
+ const fheroes2::Image & getTileSurface( const Tiles & tile );
+}