From 88b97f0db905bee55b9dfd4c4c9226440e7d2e5c Mon Sep 17 00:00:00 2001 From: Oleg Derevenetz Date: Sun, 18 Aug 2024 04:35:42 +0300 Subject: [PATCH] Turn on the -Wmissing-declarations warning and move all detected global functions to the anonymous namespace (#9048) --- android/app/jni/Android.mk | 1 + src/CMakeLists.txt | 3 +- src/Makefile | 5 +- src/fheroes2/agg/agg_image.cpp | 7795 ++++++++--------- src/fheroes2/battle/battle_board.cpp | 4 +- src/fheroes2/battle/battle_board.h | 8 +- src/fheroes2/battle/battle_cell.cpp | 2 +- src/fheroes2/battle/battle_cell.h | 4 +- src/fheroes2/battle/battle_interface.cpp | 663 +- src/fheroes2/dialog/dialog_giftresources.cpp | 268 +- src/fheroes2/dialog/dialog_levelup.cpp | 301 +- src/fheroes2/dialog/dialog_marketplace.cpp | 519 +- src/fheroes2/dialog/dialog_recruit.cpp | 238 +- src/fheroes2/dialog/dialog_selectitems.cpp | 288 +- src/fheroes2/dialog/dialog_thievesguild.cpp | 275 +- .../editor/editor_save_map_window.cpp | 2 + src/fheroes2/game/game_startgame.cpp | 114 +- src/fheroes2/kingdom/kingdom.cpp | 8 +- src/fheroes2/kingdom/kingdom_overview.cpp | 972 +- src/fheroes2/maps/map_format_info.cpp | 257 +- src/fheroes2/resource/resource.cpp | 25 +- src/fheroes2/system/players.h | 3 + 22 files changed, 5897 insertions(+), 5858 deletions(-) diff --git a/android/app/jni/Android.mk b/android/app/jni/Android.mk index e3aeebdec79..f284a0e7562 100644 --- a/android/app/jni/Android.mk +++ b/android/app/jni/Android.mk @@ -38,6 +38,7 @@ FHEROES2_C_WARN_OPTIONS := \ FHEROES2_CPP_WARN_OPTIONS := \ -Wctor-dtor-privacy \ -Wextra-semi \ + -Wmissing-declarations \ -Woverloaded-virtual \ -Wsuggest-override diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 0515f000fee..dd5a10ff79e 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -25,7 +25,8 @@ set( ) set( GNU_CXX_WARN_OPTS - ${GNU_CC_WARN_OPTS} -Wctor-dtor-privacy -Wextra-semi -Woverloaded-virtual -Wsuggest-override + ${GNU_CC_WARN_OPTS} -Wctor-dtor-privacy -Wextra-semi -Wmissing-declarations -Woverloaded-virtual + -Wsuggest-override ) set( MSVC_CC_WARN_OPTS diff --git a/src/Makefile b/src/Makefile index 1a274484999..c6b11466197 100644 --- a/src/Makefile +++ b/src/Makefile @@ -117,13 +117,12 @@ CCFLAGS := $(CCFLAGS) -DFHEROES2_DATA="$(FHEROES2_DATA)" endif # TODO: Add -Wconversion -Wsign-conversion flags once we fix all the corresponding code smells -# TODO: Add -Wmissing-declarations once there will be no more functions that are used only within a single module, -# TODO: but are not declared static (and are not placed in an anonymous namespace) # TODO: Add -Wdouble-promotion -Wold-style-cast -Wswitch-default flags once SDL headers can be compiled with them CCWARNOPTS := -pedantic -Wall -Wextra -Wcast-align -Wcast-qual -Wfloat-conversion -Wfloat-equal -Wredundant-decls \ -Wshadow -Wundef -Wunused CFLAGS := $(CFLAGS) $(CCWARNOPTS) -CXXFLAGS := $(CXXFLAGS) $(CCWARNOPTS) -Wctor-dtor-privacy -Wextra-semi -Woverloaded-virtual -Wsuggest-override +CXXFLAGS := $(CXXFLAGS) $(CCWARNOPTS) -Wctor-dtor-privacy -Wextra-semi -Wmissing-declarations -Woverloaded-virtual \ + -Wsuggest-override ifdef FHEROES2_STRICT_COMPILATION CCFLAGS := $(CCFLAGS) -Werror diff --git a/src/fheroes2/agg/agg_image.cpp b/src/fheroes2/agg/agg_image.cpp index 4fc36453c4c..397be079121 100644 --- a/src/fheroes2/agg/agg_image.cpp +++ b/src/fheroes2/agg/agg_image.cpp @@ -577,4735 +577,4732 @@ namespace fheroes2::ReplaceTransformIdByColorId( newImage, 6U, 10U ); fheroes2::Blit( newImage, releasedSprite, offsetX + 3, offsetY ); } -} -namespace fheroes2 -{ - namespace AGG - { - void loadICN( const int id ); + void loadICN( const int id ); - void LoadOriginalICN( const int id ) - { - // If this assertion blows up then something wrong with your logic and you load resources more than once! - assert( _icnVsSprite[id].empty() ); + void LoadOriginalICN( const int id ) + { + // If this assertion blows up then something wrong with your logic and you load resources more than once! + assert( _icnVsSprite[id].empty() ); - const std::vector & body = ::AGG::getDataFromAggFile( ICN::GetString( id ) ); + const std::vector & body = ::AGG::getDataFromAggFile( ICN::GetString( id ) ); - if ( body.empty() ) { - return; - } + if ( body.empty() ) { + return; + } - StreamBuf imageStream( body ); + StreamBuf imageStream( body ); - const uint32_t count = imageStream.getLE16(); - const uint32_t blockSize = imageStream.getLE32(); - if ( count == 0 || blockSize == 0 ) { - return; - } + const uint32_t count = imageStream.getLE16(); + const uint32_t blockSize = imageStream.getLE32(); + if ( count == 0 || blockSize == 0 ) { + return; + } - _icnVsSprite[id].resize( count ); + _icnVsSprite[id].resize( count ); - for ( uint32_t i = 0; i < count; ++i ) { - imageStream.seek( headerSize + i * 13 ); + for ( uint32_t i = 0; i < count; ++i ) { + imageStream.seek( headerSize + i * 13 ); - ICNHeader header1; - imageStream >> header1; + fheroes2::ICNHeader header1; + imageStream >> header1; - uint32_t sizeData = 0; - if ( i + 1 != count ) { - ICNHeader header2; - imageStream >> header2; - sizeData = header2.offsetData - header1.offsetData; - } - else { - sizeData = blockSize - header1.offsetData; - } + uint32_t sizeData = 0; + if ( i + 1 != count ) { + fheroes2::ICNHeader header2; + imageStream >> header2; + sizeData = header2.offsetData - header1.offsetData; + } + else { + sizeData = blockSize - header1.offsetData; + } - if ( headerSize + header1.offsetData + sizeData > body.size() ) { - // This is a corrupted AGG file. - throw fheroes2::InvalidDataResources( "ICN Id " + std::to_string( id ) + ", index " + std::to_string( i ) - + " is being corrupted. " - "Make sure that you own an official version of the game." ); - } + if ( headerSize + header1.offsetData + sizeData > body.size() ) { + // This is a corrupted AGG file. + throw fheroes2::InvalidDataResources( "ICN Id " + std::to_string( id ) + ", index " + std::to_string( i ) + + " is being corrupted. " + "Make sure that you own an official version of the game." ); + } - const uint8_t * data = body.data() + headerSize + header1.offsetData; + const uint8_t * data = body.data() + headerSize + header1.offsetData; - _icnVsSprite[id][i] = decodeICNSprite( data, sizeData, header1.width, header1.height, header1.offsetX, header1.offsetY ); - } + _icnVsSprite[id][i] = fheroes2::decodeICNSprite( data, sizeData, header1.width, header1.height, header1.offsetX, header1.offsetY ); } + } - // Helper function for LoadModifiedICN - void CopyICNWithPalette( const int icnId, const int originalIcnId, const PAL::PaletteType paletteType ) - { - assert( icnId != originalIcnId ); + // Helper function for LoadModifiedICN + void CopyICNWithPalette( const int icnId, const int originalIcnId, const PAL::PaletteType paletteType ) + { + assert( icnId != originalIcnId ); - GetICN( originalIcnId, 0 ); // always avoid calling LoadOriginalICN directly + fheroes2::AGG::GetICN( originalIcnId, 0 ); // always avoid calling LoadOriginalICN directly - _icnVsSprite[icnId] = _icnVsSprite[originalIcnId]; - const std::vector & palette = PAL::GetPalette( paletteType ); - for ( fheroes2::Sprite & sprite : _icnVsSprite[icnId] ) { - ApplyPalette( sprite, palette ); - } + _icnVsSprite[icnId] = _icnVsSprite[originalIcnId]; + const std::vector & palette = PAL::GetPalette( paletteType ); + for ( fheroes2::Sprite & sprite : _icnVsSprite[icnId] ) { + ApplyPalette( sprite, palette ); } + } - void generateDefaultImages( const int id ) - { - switch ( id ) { - case ICN::BTNBATTLEONLY: { - _icnVsSprite[id].resize( 2 ); + void generateDefaultImages( const int id ) + { + switch ( id ) { + case ICN::BTNBATTLEONLY: { + _icnVsSprite[id].resize( 2 ); - for ( int32_t i = 0; i < static_cast( _icnVsSprite[id].size() ); ++i ) { - Sprite & out = _icnVsSprite[id][i]; - out = GetICN( ICN::BTNCOM, i ); + for ( int32_t i = 0; i < static_cast( _icnVsSprite[id].size() ); ++i ) { + fheroes2::Sprite & out = _icnVsSprite[id][i]; + out = fheroes2::AGG::GetICN( ICN::BTNCOM, i ); - // clean button. - Fill( out, 13, 11, 113, 31, getButtonFillingColor( i == 0 ) ); - } + // clean button. + Fill( out, 13, 11, 113, 31, getButtonFillingColor( i == 0 ) ); + } + + renderTextOnButton( _icnVsSprite[id][0], _icnVsSprite[id][1], gettext_noop( "BATTLE\nONLY" ), { 12, 5 }, { 11, 6 }, { 117, 47 }, fheroes2::FontColor::WHITE ); + + break; + } + case ICN::BUTTON_NEW_GAME_EVIL: + case ICN::BUTTON_NEW_GAME_GOOD: { + _icnVsSprite[id].resize( 2 ); - renderTextOnButton( _icnVsSprite[id][0], _icnVsSprite[id][1], gettext_noop( "BATTLE\nONLY" ), { 12, 5 }, { 11, 6 }, { 117, 47 }, - fheroes2::FontColor::WHITE ); + const bool isEvilInterface = ( id == ICN::BUTTON_NEW_GAME_EVIL ); + if ( useOriginalResources() ) { + _icnVsSprite[id][0] = fheroes2::AGG::GetICN( isEvilInterface ? ICN::CPANELE : ICN::CPANEL, 0 ); + _icnVsSprite[id][1] = fheroes2::AGG::GetICN( isEvilInterface ? ICN::CPANELE : ICN::CPANEL, 1 ); break; } - case ICN::BUTTON_NEW_GAME_EVIL: - case ICN::BUTTON_NEW_GAME_GOOD: { - _icnVsSprite[id].resize( 2 ); - const bool isEvilInterface = ( id == ICN::BUTTON_NEW_GAME_EVIL ); + const int baseIcnId = isEvilInterface ? ICN::EMPTY_EVIL_MEDIUM_BUTTON : ICN::EMPTY_GOOD_MEDIUM_BUTTON; - if ( useOriginalResources() ) { - _icnVsSprite[id][0] = GetICN( isEvilInterface ? ICN::CPANELE : ICN::CPANEL, 0 ); - _icnVsSprite[id][1] = GetICN( isEvilInterface ? ICN::CPANELE : ICN::CPANEL, 1 ); - break; - } + for ( int32_t i = 0; i < static_cast( _icnVsSprite[id].size() ); ++i ) { + fheroes2::Sprite & out = _icnVsSprite[id][i]; + out = fheroes2::AGG::GetICN( baseIcnId, i ); + } - const int baseIcnId = isEvilInterface ? ICN::EMPTY_EVIL_MEDIUM_BUTTON : ICN::EMPTY_GOOD_MEDIUM_BUTTON; + const fheroes2::FontColor buttonFontColor = isEvilInterface ? fheroes2::FontColor::GRAY : fheroes2::FontColor::WHITE; + renderTextOnButton( _icnVsSprite[id][0], _icnVsSprite[id][1], gettext_noop( "NEW\nGAME" ), { 7, 5 }, { 6, 6 }, { 86, 48 }, buttonFontColor ); - for ( int32_t i = 0; i < static_cast( _icnVsSprite[id].size() ); ++i ) { - Sprite & out = _icnVsSprite[id][i]; - out = GetICN( baseIcnId, i ); - } + break; + } + case ICN::BUTTON_SAVE_GAME_EVIL: + case ICN::BUTTON_SAVE_GAME_GOOD: { + _icnVsSprite[id].resize( 2 ); - const fheroes2::FontColor buttonFontColor = isEvilInterface ? fheroes2::FontColor::GRAY : fheroes2::FontColor::WHITE; - renderTextOnButton( _icnVsSprite[id][0], _icnVsSprite[id][1], gettext_noop( "NEW\nGAME" ), { 7, 5 }, { 6, 6 }, { 86, 48 }, buttonFontColor ); + const bool isEvilInterface = ( id == ICN::BUTTON_SAVE_GAME_EVIL ); + if ( useOriginalResources() ) { + _icnVsSprite[id][0] = fheroes2::AGG::GetICN( isEvilInterface ? ICN::CPANELE : ICN::CPANEL, 4 ); + _icnVsSprite[id][1] = fheroes2::AGG::GetICN( isEvilInterface ? ICN::CPANELE : ICN::CPANEL, 5 ); break; } - case ICN::BUTTON_SAVE_GAME_EVIL: - case ICN::BUTTON_SAVE_GAME_GOOD: { - _icnVsSprite[id].resize( 2 ); - const bool isEvilInterface = ( id == ICN::BUTTON_SAVE_GAME_EVIL ); + const int baseIcnId = isEvilInterface ? ICN::EMPTY_EVIL_MEDIUM_BUTTON : ICN::EMPTY_GOOD_MEDIUM_BUTTON; - if ( useOriginalResources() ) { - _icnVsSprite[id][0] = GetICN( isEvilInterface ? ICN::CPANELE : ICN::CPANEL, 4 ); - _icnVsSprite[id][1] = GetICN( isEvilInterface ? ICN::CPANELE : ICN::CPANEL, 5 ); - break; - } + for ( int32_t i = 0; i < static_cast( _icnVsSprite[id].size() ); ++i ) { + fheroes2::Sprite & out = _icnVsSprite[id][i]; + out = fheroes2::AGG::GetICN( baseIcnId, i ); + } - const int baseIcnId = isEvilInterface ? ICN::EMPTY_EVIL_MEDIUM_BUTTON : ICN::EMPTY_GOOD_MEDIUM_BUTTON; + const fheroes2::FontColor buttonFontColor = isEvilInterface ? fheroes2::FontColor::GRAY : fheroes2::FontColor::WHITE; + renderTextOnButton( _icnVsSprite[id][0], _icnVsSprite[id][1], gettext_noop( "SAVE\nGAME" ), { 7, 5 }, { 6, 6 }, { 86, 48 }, buttonFontColor ); - for ( int32_t i = 0; i < static_cast( _icnVsSprite[id].size() ); ++i ) { - Sprite & out = _icnVsSprite[id][i]; - out = GetICN( baseIcnId, i ); - } + break; + } + case ICN::BUTTON_LOAD_GAME_EVIL: + case ICN::BUTTON_LOAD_GAME_GOOD: { + _icnVsSprite[id].resize( 2 ); - const fheroes2::FontColor buttonFontColor = isEvilInterface ? fheroes2::FontColor::GRAY : fheroes2::FontColor::WHITE; - renderTextOnButton( _icnVsSprite[id][0], _icnVsSprite[id][1], gettext_noop( "SAVE\nGAME" ), { 7, 5 }, { 6, 6 }, { 86, 48 }, buttonFontColor ); + const bool isEvilInterface = ( id == ICN::BUTTON_LOAD_GAME_EVIL ); + if ( useOriginalResources() ) { + _icnVsSprite[id][0] = fheroes2::AGG::GetICN( isEvilInterface ? ICN::CPANELE : ICN::CPANEL, 2 ); + _icnVsSprite[id][1] = fheroes2::AGG::GetICN( isEvilInterface ? ICN::CPANELE : ICN::CPANEL, 3 ); break; } - case ICN::BUTTON_LOAD_GAME_EVIL: - case ICN::BUTTON_LOAD_GAME_GOOD: { - _icnVsSprite[id].resize( 2 ); - const bool isEvilInterface = ( id == ICN::BUTTON_LOAD_GAME_EVIL ); + const int baseIcnId = isEvilInterface ? ICN::EMPTY_EVIL_MEDIUM_BUTTON : ICN::EMPTY_GOOD_MEDIUM_BUTTON; - if ( useOriginalResources() ) { - _icnVsSprite[id][0] = GetICN( isEvilInterface ? ICN::CPANELE : ICN::CPANEL, 2 ); - _icnVsSprite[id][1] = GetICN( isEvilInterface ? ICN::CPANELE : ICN::CPANEL, 3 ); - break; - } + for ( int32_t i = 0; i < static_cast( _icnVsSprite[id].size() ); ++i ) { + fheroes2::Sprite & out = _icnVsSprite[id][i]; + out = fheroes2::AGG::GetICN( baseIcnId, i ); + } - const int baseIcnId = isEvilInterface ? ICN::EMPTY_EVIL_MEDIUM_BUTTON : ICN::EMPTY_GOOD_MEDIUM_BUTTON; + const fheroes2::FontColor buttonFontColor = isEvilInterface ? fheroes2::FontColor::GRAY : fheroes2::FontColor::WHITE; + renderTextOnButton( _icnVsSprite[id][0], _icnVsSprite[id][1], gettext_noop( "LOAD\nGAME" ), { 7, 5 }, { 6, 6 }, { 86, 48 }, buttonFontColor ); - for ( int32_t i = 0; i < static_cast( _icnVsSprite[id].size() ); ++i ) { - Sprite & out = _icnVsSprite[id][i]; - out = GetICN( baseIcnId, i ); - } + break; + } + case ICN::BUTTON_INFO_EVIL: + case ICN::BUTTON_INFO_GOOD: { + _icnVsSprite[id].resize( 2 ); - const fheroes2::FontColor buttonFontColor = isEvilInterface ? fheroes2::FontColor::GRAY : fheroes2::FontColor::WHITE; - renderTextOnButton( _icnVsSprite[id][0], _icnVsSprite[id][1], gettext_noop( "LOAD\nGAME" ), { 7, 5 }, { 6, 6 }, { 86, 48 }, buttonFontColor ); + const bool isEvilInterface = ( id == ICN::BUTTON_INFO_EVIL ); + if ( useOriginalResources() ) { + _icnVsSprite[id][0] = fheroes2::AGG::GetICN( isEvilInterface ? ICN::APANELE : ICN::APANEL, 4 ); + _icnVsSprite[id][1] = fheroes2::AGG::GetICN( isEvilInterface ? ICN::APANELE : ICN::APANEL, 5 ); break; } - case ICN::BUTTON_INFO_EVIL: - case ICN::BUTTON_INFO_GOOD: { - _icnVsSprite[id].resize( 2 ); - const bool isEvilInterface = ( id == ICN::BUTTON_INFO_EVIL ); + const int baseIcnId = isEvilInterface ? ICN::EMPTY_EVIL_MEDIUM_BUTTON : ICN::EMPTY_GOOD_MEDIUM_BUTTON; - if ( useOriginalResources() ) { - _icnVsSprite[id][0] = GetICN( isEvilInterface ? ICN::APANELE : ICN::APANEL, 4 ); - _icnVsSprite[id][1] = GetICN( isEvilInterface ? ICN::APANELE : ICN::APANEL, 5 ); - break; - } + for ( int32_t i = 0; i < static_cast( _icnVsSprite[id].size() ); ++i ) { + fheroes2::Sprite & out = _icnVsSprite[id][i]; + out = fheroes2::AGG::GetICN( baseIcnId, i ); + } - const int baseIcnId = isEvilInterface ? ICN::EMPTY_EVIL_MEDIUM_BUTTON : ICN::EMPTY_GOOD_MEDIUM_BUTTON; + const fheroes2::FontColor buttonFontColor = isEvilInterface ? fheroes2::FontColor::GRAY : fheroes2::FontColor::WHITE; + renderTextOnButton( _icnVsSprite[id][0], _icnVsSprite[id][1], gettext_noop( "INFO" ), { 7, 5 }, { 6, 6 }, { 86, 48 }, buttonFontColor ); - for ( int32_t i = 0; i < static_cast( _icnVsSprite[id].size() ); ++i ) { - Sprite & out = _icnVsSprite[id][i]; - out = GetICN( baseIcnId, i ); - } + break; + } + case ICN::BUTTON_QUIT_EVIL: + case ICN::BUTTON_QUIT_GOOD: { + _icnVsSprite[id].resize( 2 ); - const fheroes2::FontColor buttonFontColor = isEvilInterface ? fheroes2::FontColor::GRAY : fheroes2::FontColor::WHITE; - renderTextOnButton( _icnVsSprite[id][0], _icnVsSprite[id][1], gettext_noop( "INFO" ), { 7, 5 }, { 6, 6 }, { 86, 48 }, buttonFontColor ); + const bool isEvilInterface = ( id == ICN::BUTTON_QUIT_EVIL ); + if ( useOriginalResources() ) { + _icnVsSprite[id][0] = fheroes2::AGG::GetICN( isEvilInterface ? ICN::CPANELE : ICN::CPANEL, 6 ); + _icnVsSprite[id][1] = fheroes2::AGG::GetICN( isEvilInterface ? ICN::CPANELE : ICN::CPANEL, 7 ); break; } - case ICN::BUTTON_QUIT_EVIL: - case ICN::BUTTON_QUIT_GOOD: { - _icnVsSprite[id].resize( 2 ); - const bool isEvilInterface = ( id == ICN::BUTTON_QUIT_EVIL ); + const int baseIcnId = isEvilInterface ? ICN::EMPTY_EVIL_MEDIUM_BUTTON : ICN::EMPTY_GOOD_MEDIUM_BUTTON; - if ( useOriginalResources() ) { - _icnVsSprite[id][0] = GetICN( isEvilInterface ? ICN::CPANELE : ICN::CPANEL, 6 ); - _icnVsSprite[id][1] = GetICN( isEvilInterface ? ICN::CPANELE : ICN::CPANEL, 7 ); - break; - } + for ( int32_t i = 0; i < static_cast( _icnVsSprite[id].size() ); ++i ) { + fheroes2::Sprite & out = _icnVsSprite[id][i]; + out = fheroes2::AGG::GetICN( baseIcnId, i ); + } - const int baseIcnId = isEvilInterface ? ICN::EMPTY_EVIL_MEDIUM_BUTTON : ICN::EMPTY_GOOD_MEDIUM_BUTTON; + const fheroes2::FontColor buttonFontColor = isEvilInterface ? fheroes2::FontColor::GRAY : fheroes2::FontColor::WHITE; + renderTextOnButton( _icnVsSprite[id][0], _icnVsSprite[id][1], gettext_noop( "QUIT" ), { 7, 5 }, { 6, 6 }, { 86, 48 }, buttonFontColor ); - for ( int32_t i = 0; i < static_cast( _icnVsSprite[id].size() ); ++i ) { - Sprite & out = _icnVsSprite[id][i]; - out = GetICN( baseIcnId, i ); - } + break; + } + case ICN::BUTTON_NEW_MAP_EVIL: + case ICN::BUTTON_NEW_MAP_GOOD: { + _icnVsSprite[id].resize( 2 ); - const fheroes2::FontColor buttonFontColor = isEvilInterface ? fheroes2::FontColor::GRAY : fheroes2::FontColor::WHITE; - renderTextOnButton( _icnVsSprite[id][0], _icnVsSprite[id][1], gettext_noop( "QUIT" ), { 7, 5 }, { 6, 6 }, { 86, 48 }, buttonFontColor ); + const bool isEvilInterface = ( id == ICN::BUTTON_NEW_MAP_EVIL ); + if ( useOriginalResources() && !isEvilInterface ) { + _icnVsSprite[id][0] = fheroes2::AGG::GetICN( ICN::ECPANEL, 0 ); + _icnVsSprite[id][1] = fheroes2::AGG::GetICN( ICN::ECPANEL, 1 ); break; } - case ICN::BUTTON_NEW_MAP_EVIL: - case ICN::BUTTON_NEW_MAP_GOOD: { - _icnVsSprite[id].resize( 2 ); - const bool isEvilInterface = ( id == ICN::BUTTON_NEW_MAP_EVIL ); + const int baseIcnId = isEvilInterface ? ICN::EMPTY_EVIL_MEDIUM_BUTTON : ICN::EMPTY_GOOD_MEDIUM_BUTTON; - if ( useOriginalResources() && !isEvilInterface ) { - _icnVsSprite[id][0] = GetICN( ICN::ECPANEL, 0 ); - _icnVsSprite[id][1] = GetICN( ICN::ECPANEL, 1 ); - break; - } + for ( int32_t i = 0; i < static_cast( _icnVsSprite[id].size() ); ++i ) { + fheroes2::Sprite & out = _icnVsSprite[id][i]; + out = fheroes2::AGG::GetICN( baseIcnId, i ); + } - const int baseIcnId = isEvilInterface ? ICN::EMPTY_EVIL_MEDIUM_BUTTON : ICN::EMPTY_GOOD_MEDIUM_BUTTON; + const fheroes2::FontColor buttonFontColor = isEvilInterface ? fheroes2::FontColor::GRAY : fheroes2::FontColor::WHITE; + renderTextOnButton( _icnVsSprite[id][0], _icnVsSprite[id][1], gettext_noop( "NEW\nMAP" ), { 7, 5 }, { 6, 6 }, { 86, 48 }, buttonFontColor ); - for ( int32_t i = 0; i < static_cast( _icnVsSprite[id].size() ); ++i ) { - Sprite & out = _icnVsSprite[id][i]; - out = GetICN( baseIcnId, i ); - } + break; + } + case ICN::BUTTON_SAVE_MAP_EVIL: + case ICN::BUTTON_SAVE_MAP_GOOD: { + _icnVsSprite[id].resize( 2 ); - const fheroes2::FontColor buttonFontColor = isEvilInterface ? fheroes2::FontColor::GRAY : fheroes2::FontColor::WHITE; - renderTextOnButton( _icnVsSprite[id][0], _icnVsSprite[id][1], gettext_noop( "NEW\nMAP" ), { 7, 5 }, { 6, 6 }, { 86, 48 }, buttonFontColor ); + const bool isEvilInterface = ( id == ICN::BUTTON_SAVE_MAP_EVIL ); + if ( useOriginalResources() && !isEvilInterface ) { + _icnVsSprite[id][0] = fheroes2::AGG::GetICN( ICN::ECPANEL, 4 ); + _icnVsSprite[id][1] = fheroes2::AGG::GetICN( ICN::ECPANEL, 5 ); break; } - case ICN::BUTTON_SAVE_MAP_EVIL: - case ICN::BUTTON_SAVE_MAP_GOOD: { - _icnVsSprite[id].resize( 2 ); - const bool isEvilInterface = ( id == ICN::BUTTON_SAVE_MAP_EVIL ); + const int baseIcnId = isEvilInterface ? ICN::EMPTY_EVIL_MEDIUM_BUTTON : ICN::EMPTY_GOOD_MEDIUM_BUTTON; - if ( useOriginalResources() && !isEvilInterface ) { - _icnVsSprite[id][0] = GetICN( ICN::ECPANEL, 4 ); - _icnVsSprite[id][1] = GetICN( ICN::ECPANEL, 5 ); - break; - } + for ( int32_t i = 0; i < static_cast( _icnVsSprite[id].size() ); ++i ) { + fheroes2::Sprite & out = _icnVsSprite[id][i]; + out = fheroes2::AGG::GetICN( baseIcnId, i ); + } - const int baseIcnId = isEvilInterface ? ICN::EMPTY_EVIL_MEDIUM_BUTTON : ICN::EMPTY_GOOD_MEDIUM_BUTTON; + const fheroes2::FontColor buttonFontColor = isEvilInterface ? fheroes2::FontColor::GRAY : fheroes2::FontColor::WHITE; + renderTextOnButton( _icnVsSprite[id][0], _icnVsSprite[id][1], gettext_noop( "SAVE\nMAP" ), { 7, 5 }, { 6, 6 }, { 86, 48 }, buttonFontColor ); - for ( int32_t i = 0; i < static_cast( _icnVsSprite[id].size() ); ++i ) { - Sprite & out = _icnVsSprite[id][i]; - out = GetICN( baseIcnId, i ); - } + break; + } + case ICN::BUTTON_LOAD_MAP_EVIL: + case ICN::BUTTON_LOAD_MAP_GOOD: { + _icnVsSprite[id].resize( 2 ); - const fheroes2::FontColor buttonFontColor = isEvilInterface ? fheroes2::FontColor::GRAY : fheroes2::FontColor::WHITE; - renderTextOnButton( _icnVsSprite[id][0], _icnVsSprite[id][1], gettext_noop( "SAVE\nMAP" ), { 7, 5 }, { 6, 6 }, { 86, 48 }, buttonFontColor ); + const bool isEvilInterface = ( id == ICN::BUTTON_LOAD_MAP_EVIL ); + if ( useOriginalResources() && !isEvilInterface ) { + _icnVsSprite[id][0] = fheroes2::AGG::GetICN( ICN::ECPANEL, 2 ); + _icnVsSprite[id][1] = fheroes2::AGG::GetICN( ICN::ECPANEL, 3 ); break; } - case ICN::BUTTON_LOAD_MAP_EVIL: - case ICN::BUTTON_LOAD_MAP_GOOD: { - _icnVsSprite[id].resize( 2 ); - const bool isEvilInterface = ( id == ICN::BUTTON_LOAD_MAP_EVIL ); + const int baseIcnId = isEvilInterface ? ICN::EMPTY_EVIL_MEDIUM_BUTTON : ICN::EMPTY_GOOD_MEDIUM_BUTTON; - if ( useOriginalResources() && !isEvilInterface ) { - _icnVsSprite[id][0] = GetICN( ICN::ECPANEL, 2 ); - _icnVsSprite[id][1] = GetICN( ICN::ECPANEL, 3 ); - break; - } + for ( int32_t i = 0; i < static_cast( _icnVsSprite[id].size() ); ++i ) { + fheroes2::Sprite & out = _icnVsSprite[id][i]; + out = fheroes2::AGG::GetICN( baseIcnId, i ); + } - const int baseIcnId = isEvilInterface ? ICN::EMPTY_EVIL_MEDIUM_BUTTON : ICN::EMPTY_GOOD_MEDIUM_BUTTON; + const fheroes2::FontColor buttonFontColor = isEvilInterface ? fheroes2::FontColor::GRAY : fheroes2::FontColor::WHITE; + renderTextOnButton( _icnVsSprite[id][0], _icnVsSprite[id][1], gettext_noop( "LOAD\nMAP" ), { 7, 5 }, { 6, 6 }, { 86, 48 }, buttonFontColor ); - for ( int32_t i = 0; i < static_cast( _icnVsSprite[id].size() ); ++i ) { - Sprite & out = _icnVsSprite[id][i]; - out = GetICN( baseIcnId, i ); - } + break; + } + case ICN::BUTTON_SMALL_CANCEL_GOOD: + case ICN::BUTTON_SMALL_CANCEL_EVIL: { + _icnVsSprite[id].resize( 2 ); + + const bool isEvilInterface = ( id == ICN::BUTTON_SMALL_CANCEL_EVIL ); - const fheroes2::FontColor buttonFontColor = isEvilInterface ? fheroes2::FontColor::GRAY : fheroes2::FontColor::WHITE; - renderTextOnButton( _icnVsSprite[id][0], _icnVsSprite[id][1], gettext_noop( "LOAD\nMAP" ), { 7, 5 }, { 6, 6 }, { 86, 48 }, buttonFontColor ); + if ( useOriginalResources() ) { + _icnVsSprite[id][0] = fheroes2::AGG::GetICN( isEvilInterface ? ICN::CPANELE : ICN::CPANEL, 8 ); + _icnVsSprite[id][1] = fheroes2::AGG::GetICN( isEvilInterface ? ICN::CPANELE : ICN::CPANEL, 9 ); + + // To properly generate shadows and Blit the button we need to make transparent pixels in its released state the same as in the pressed state. + CopyTransformLayer( _icnVsSprite[id][0], _icnVsSprite[id][1] ); break; } - case ICN::BUTTON_SMALL_CANCEL_GOOD: - case ICN::BUTTON_SMALL_CANCEL_EVIL: { - _icnVsSprite[id].resize( 2 ); - const bool isEvilInterface = ( id == ICN::BUTTON_SMALL_CANCEL_EVIL ); + getTextAdaptedButton( _icnVsSprite[id][0], _icnVsSprite[id][1], gettext_noop( "CANCEL" ), isEvilInterface ? ICN::EMPTY_EVIL_BUTTON : ICN::EMPTY_GOOD_BUTTON, + isEvilInterface ? ICN::STONEBAK_EVIL : ICN::STONEBAK ); - if ( useOriginalResources() ) { - _icnVsSprite[id][0] = GetICN( isEvilInterface ? ICN::CPANELE : ICN::CPANEL, 8 ); - _icnVsSprite[id][1] = GetICN( isEvilInterface ? ICN::CPANELE : ICN::CPANEL, 9 ); + break; + } + case ICN::BUTTON_SMALL_OKAY_GOOD: + case ICN::BUTTON_SMALL_OKAY_EVIL: + case ICN::BUTTON_SMALLER_OKAY_GOOD: + case ICN::BUTTON_SMALLER_OKAY_EVIL: { + _icnVsSprite[id].resize( 2 ); - // To properly generate shadows and Blit the button we need to make transparent pixels in its released state the same as in the pressed state. - CopyTransformLayer( _icnVsSprite[id][0], _icnVsSprite[id][1] ); + const bool isEvilInterface = ( id == ICN::BUTTON_SMALL_OKAY_EVIL || id == ICN::BUTTON_SMALLER_OKAY_EVIL ); + const bool isSameResourceAsLanguage = useOriginalResources(); - break; - } + if ( isSameResourceAsLanguage && ( id == ICN::BUTTON_SMALL_OKAY_EVIL || id == ICN::BUTTON_SMALL_OKAY_GOOD ) ) { + _icnVsSprite[id][0] = fheroes2::AGG::GetICN( isEvilInterface ? ICN::SPANBTNE : ICN::SPANBTN, 0 ); + _icnVsSprite[id][1] = fheroes2::AGG::GetICN( isEvilInterface ? ICN::SPANBTNE : ICN::SPANBTN, 1 ); - getTextAdaptedButton( _icnVsSprite[id][0], _icnVsSprite[id][1], gettext_noop( "CANCEL" ), - isEvilInterface ? ICN::EMPTY_EVIL_BUTTON : ICN::EMPTY_GOOD_BUTTON, isEvilInterface ? ICN::STONEBAK_EVIL : ICN::STONEBAK ); + // To properly generate shadows and Blit the button we need to make transparent pixels in its released state the same as in the pressed state. + CopyTransformLayer( _icnVsSprite[id][0], _icnVsSprite[id][1] ); break; } - case ICN::BUTTON_SMALL_OKAY_GOOD: - case ICN::BUTTON_SMALL_OKAY_EVIL: - case ICN::BUTTON_SMALLER_OKAY_GOOD: - case ICN::BUTTON_SMALLER_OKAY_EVIL: { - _icnVsSprite[id].resize( 2 ); + if ( isSameResourceAsLanguage && ( id == ICN::BUTTON_SMALLER_OKAY_EVIL || id == ICN::BUTTON_SMALLER_OKAY_GOOD ) ) { + _icnVsSprite[id][0] = fheroes2::AGG::GetICN( isEvilInterface ? ICN::WINCMBBE : ICN::WINCMBTB, 0 ); + _icnVsSprite[id][1] = fheroes2::AGG::GetICN( isEvilInterface ? ICN::WINCMBBE : ICN::WINCMBTB, 1 ); + break; + } - const bool isEvilInterface = ( id == ICN::BUTTON_SMALL_OKAY_EVIL || id == ICN::BUTTON_SMALLER_OKAY_EVIL ); - const bool isSameResourceAsLanguage = useOriginalResources(); + int32_t textWidth = 86; + const char * text = gettext_noop( "OKAY" ); + if ( id == ICN::BUTTON_SMALLER_OKAY_EVIL || id == ICN::BUTTON_SMALLER_OKAY_GOOD ) { + textWidth = 70; + text = gettext_noop( "smallerButton|OKAY" ); + } - if ( isSameResourceAsLanguage && ( id == ICN::BUTTON_SMALL_OKAY_EVIL || id == ICN::BUTTON_SMALL_OKAY_GOOD ) ) { - _icnVsSprite[id][0] = GetICN( isEvilInterface ? ICN::SPANBTNE : ICN::SPANBTN, 0 ); - _icnVsSprite[id][1] = GetICN( isEvilInterface ? ICN::SPANBTNE : ICN::SPANBTN, 1 ); + createNormalButton( _icnVsSprite[id][0], _icnVsSprite[id][1], textWidth, text, isEvilInterface ); - // To properly generate shadows and Blit the button we need to make transparent pixels in its released state the same as in the pressed state. - CopyTransformLayer( _icnVsSprite[id][0], _icnVsSprite[id][1] ); + break; + } + case ICN::BUTTON_SMALL_ACCEPT_GOOD: + case ICN::BUTTON_SMALL_ACCEPT_EVIL: { + _icnVsSprite[id].resize( 2 ); - break; - } - if ( isSameResourceAsLanguage && ( id == ICN::BUTTON_SMALLER_OKAY_EVIL || id == ICN::BUTTON_SMALLER_OKAY_GOOD ) ) { - _icnVsSprite[id][0] = GetICN( isEvilInterface ? ICN::WINCMBBE : ICN::WINCMBTB, 0 ); - _icnVsSprite[id][1] = GetICN( isEvilInterface ? ICN::WINCMBBE : ICN::WINCMBTB, 1 ); - break; - } + const bool isEvilInterface = ( id == ICN::BUTTON_SMALL_ACCEPT_EVIL ); - int32_t textWidth = 86; - const char * text = gettext_noop( "OKAY" ); - if ( id == ICN::BUTTON_SMALLER_OKAY_EVIL || id == ICN::BUTTON_SMALLER_OKAY_GOOD ) { - textWidth = 70; - text = gettext_noop( "smallerButton|OKAY" ); - } + if ( useOriginalResources() ) { + _icnVsSprite[id][0] = fheroes2::AGG::GetICN( isEvilInterface ? ICN::SURRENDE : ICN::SURRENDR, 0 ); + _icnVsSprite[id][1] = fheroes2::AGG::GetICN( isEvilInterface ? ICN::SURRENDE : ICN::SURRENDR, 1 ); - createNormalButton( _icnVsSprite[id][0], _icnVsSprite[id][1], textWidth, text, isEvilInterface ); + // To properly generate shadows and Blit the button we need to make transparent pixels in its released state the same as in the pressed state. + CopyTransformLayer( _icnVsSprite[id][0], _icnVsSprite[id][1] ); break; } - case ICN::BUTTON_SMALL_ACCEPT_GOOD: - case ICN::BUTTON_SMALL_ACCEPT_EVIL: { - _icnVsSprite[id].resize( 2 ); - const bool isEvilInterface = ( id == ICN::BUTTON_SMALL_ACCEPT_EVIL ); + getTextAdaptedButton( _icnVsSprite[id][0], _icnVsSprite[id][1], gettext_noop( "ACCEPT" ), isEvilInterface ? ICN::EMPTY_EVIL_BUTTON : ICN::EMPTY_GOOD_BUTTON, + isEvilInterface ? ICN::STONEBAK_EVIL : ICN::STONEBAK ); - if ( useOriginalResources() ) { - _icnVsSprite[id][0] = GetICN( isEvilInterface ? ICN::SURRENDE : ICN::SURRENDR, 0 ); - _icnVsSprite[id][1] = GetICN( isEvilInterface ? ICN::SURRENDE : ICN::SURRENDR, 1 ); + break; + } + case ICN::BUTTON_SMALL_DECLINE_GOOD: + case ICN::BUTTON_SMALL_DECLINE_EVIL: { + _icnVsSprite[id].resize( 2 ); - // To properly generate shadows and Blit the button we need to make transparent pixels in its released state the same as in the pressed state. - CopyTransformLayer( _icnVsSprite[id][0], _icnVsSprite[id][1] ); + const bool isEvilInterface = ( id == ICN::BUTTON_SMALL_DECLINE_EVIL ); - break; - } + if ( useOriginalResources() ) { + _icnVsSprite[id][0] = fheroes2::AGG::GetICN( isEvilInterface ? ICN::SURRENDE : ICN::SURRENDR, 2 ); + _icnVsSprite[id][1] = fheroes2::AGG::GetICN( isEvilInterface ? ICN::SURRENDE : ICN::SURRENDR, 3 ); - getTextAdaptedButton( _icnVsSprite[id][0], _icnVsSprite[id][1], gettext_noop( "ACCEPT" ), - isEvilInterface ? ICN::EMPTY_EVIL_BUTTON : ICN::EMPTY_GOOD_BUTTON, isEvilInterface ? ICN::STONEBAK_EVIL : ICN::STONEBAK ); + // To properly generate shadows and Blit the button we need to make transparent pixels in its released state the same as in the pressed state. + CopyTransformLayer( _icnVsSprite[id][0], _icnVsSprite[id][1] ); break; } - case ICN::BUTTON_SMALL_DECLINE_GOOD: - case ICN::BUTTON_SMALL_DECLINE_EVIL: { - _icnVsSprite[id].resize( 2 ); - - const bool isEvilInterface = ( id == ICN::BUTTON_SMALL_DECLINE_EVIL ); - if ( useOriginalResources() ) { - _icnVsSprite[id][0] = GetICN( isEvilInterface ? ICN::SURRENDE : ICN::SURRENDR, 2 ); - _icnVsSprite[id][1] = GetICN( isEvilInterface ? ICN::SURRENDE : ICN::SURRENDR, 3 ); + getTextAdaptedButton( _icnVsSprite[id][0], _icnVsSprite[id][1], gettext_noop( "DECLINE" ), isEvilInterface ? ICN::EMPTY_EVIL_BUTTON : ICN::EMPTY_GOOD_BUTTON, + isEvilInterface ? ICN::STONEBAK_EVIL : ICN::STONEBAK ); - // To properly generate shadows and Blit the button we need to make transparent pixels in its released state the same as in the pressed state. - CopyTransformLayer( _icnVsSprite[id][0], _icnVsSprite[id][1] ); - - break; - } + break; + } + case ICN::BUTTON_SMALL_LEARN_GOOD: + case ICN::BUTTON_SMALL_LEARN_EVIL: { + _icnVsSprite[id].resize( 2 ); - getTextAdaptedButton( _icnVsSprite[id][0], _icnVsSprite[id][1], gettext_noop( "DECLINE" ), - isEvilInterface ? ICN::EMPTY_EVIL_BUTTON : ICN::EMPTY_GOOD_BUTTON, isEvilInterface ? ICN::STONEBAK_EVIL : ICN::STONEBAK ); + const bool isEvilInterface = ( id == ICN::BUTTON_SMALL_LEARN_EVIL ); + const int baseIcnID = isEvilInterface ? ICN::SYSTEME : ICN::SYSTEM; + if ( useOriginalResources() ) { + _icnVsSprite[id][0] = fheroes2::AGG::GetICN( baseIcnID, 9 ); + _icnVsSprite[id][1] = fheroes2::AGG::GetICN( baseIcnID, 10 ); break; } - case ICN::BUTTON_SMALL_LEARN_GOOD: - case ICN::BUTTON_SMALL_LEARN_EVIL: { - _icnVsSprite[id].resize( 2 ); - const bool isEvilInterface = ( id == ICN::BUTTON_SMALL_LEARN_EVIL ); - const int baseIcnID = isEvilInterface ? ICN::SYSTEME : ICN::SYSTEM; + for ( int32_t i = 0; i < static_cast( _icnVsSprite[id].size() ); ++i ) { + fheroes2::Sprite & out = _icnVsSprite[id][i]; + out = fheroes2::AGG::GetICN( baseIcnID, 11 + i ); + } - if ( useOriginalResources() ) { - _icnVsSprite[id][0] = GetICN( baseIcnID, 9 ); - _icnVsSprite[id][1] = GetICN( baseIcnID, 10 ); - break; - } + const fheroes2::FontColor buttonFontColor = isEvilInterface ? fheroes2::FontColor::GRAY : fheroes2::FontColor::WHITE; + renderTextOnButton( _icnVsSprite[id][0], _icnVsSprite[id][1], gettext_noop( "LEARN" ), { 7, 5 }, { 5, 6 }, { 86, 16 }, buttonFontColor ); - for ( int32_t i = 0; i < static_cast( _icnVsSprite[id].size() ); ++i ) { - Sprite & out = _icnVsSprite[id][i]; - out = GetICN( baseIcnID, 11 + i ); - } + break; + } + case ICN::BUTTON_SMALL_TRADE_GOOD: + case ICN::BUTTON_SMALL_TRADE_EVIL: { + _icnVsSprite[id].resize( 2 ); - const fheroes2::FontColor buttonFontColor = isEvilInterface ? fheroes2::FontColor::GRAY : fheroes2::FontColor::WHITE; - renderTextOnButton( _icnVsSprite[id][0], _icnVsSprite[id][1], gettext_noop( "LEARN" ), { 7, 5 }, { 5, 6 }, { 86, 16 }, buttonFontColor ); + const bool isEvilInterface = ( id == ICN::BUTTON_SMALL_TRADE_EVIL ); + if ( useOriginalResources() ) { + _icnVsSprite[id][0] = fheroes2::AGG::GetICN( isEvilInterface ? ICN::TRADPOSE : ICN::TRADPOST, 15 ); + _icnVsSprite[id][1] = fheroes2::AGG::GetICN( isEvilInterface ? ICN::TRADPOSE : ICN::TRADPOST, 16 ); break; } - case ICN::BUTTON_SMALL_TRADE_GOOD: - case ICN::BUTTON_SMALL_TRADE_EVIL: { - _icnVsSprite[id].resize( 2 ); - const bool isEvilInterface = ( id == ICN::BUTTON_SMALL_TRADE_EVIL ); + getTextAdaptedButton( _icnVsSprite[id][0], _icnVsSprite[id][1], gettext_noop( "TRADE" ), isEvilInterface ? ICN::EMPTY_EVIL_BUTTON : ICN::EMPTY_GOOD_BUTTON, + isEvilInterface ? ICN::UNIFORMBAK_EVIL : ICN::UNIFORMBAK_GOOD ); + break; + } + case ICN::BUTTON_SMALL_YES_GOOD: + case ICN::BUTTON_SMALL_YES_EVIL: { + _icnVsSprite[id].resize( 2 ); - if ( useOriginalResources() ) { - _icnVsSprite[id][0] = GetICN( isEvilInterface ? ICN::TRADPOSE : ICN::TRADPOST, 15 ); - _icnVsSprite[id][1] = GetICN( isEvilInterface ? ICN::TRADPOSE : ICN::TRADPOST, 16 ); - break; - } + const bool isEvilInterface = ( id == ICN::BUTTON_SMALL_YES_EVIL ); + const int baseIcnID = isEvilInterface ? ICN::SYSTEME : ICN::SYSTEM; - getTextAdaptedButton( _icnVsSprite[id][0], _icnVsSprite[id][1], gettext_noop( "TRADE" ), - isEvilInterface ? ICN::EMPTY_EVIL_BUTTON : ICN::EMPTY_GOOD_BUTTON, isEvilInterface ? ICN::UNIFORMBAK_EVIL : ICN::UNIFORMBAK_GOOD ); + if ( useOriginalResources() ) { + _icnVsSprite[id][0] = fheroes2::AGG::GetICN( baseIcnID, 5 ); + _icnVsSprite[id][1] = fheroes2::AGG::GetICN( baseIcnID, 6 ); break; } - case ICN::BUTTON_SMALL_YES_GOOD: - case ICN::BUTTON_SMALL_YES_EVIL: { - _icnVsSprite[id].resize( 2 ); - const bool isEvilInterface = ( id == ICN::BUTTON_SMALL_YES_EVIL ); - const int baseIcnID = isEvilInterface ? ICN::SYSTEME : ICN::SYSTEM; + for ( int32_t i = 0; i < static_cast( _icnVsSprite[id].size() ); ++i ) { + fheroes2::Sprite & out = _icnVsSprite[id][i]; + out = fheroes2::AGG::GetICN( baseIcnID, 11 + i ); + } - if ( useOriginalResources() ) { - _icnVsSprite[id][0] = GetICN( baseIcnID, 5 ); - _icnVsSprite[id][1] = GetICN( baseIcnID, 6 ); - break; - } + const fheroes2::FontColor buttonFontColor = isEvilInterface ? fheroes2::FontColor::GRAY : fheroes2::FontColor::WHITE; + renderTextOnButton( _icnVsSprite[id][0], _icnVsSprite[id][1], gettext_noop( "YES" ), { 6, 5 }, { 5, 6 }, { 86, 16 }, buttonFontColor ); - for ( int32_t i = 0; i < static_cast( _icnVsSprite[id].size() ); ++i ) { - Sprite & out = _icnVsSprite[id][i]; - out = GetICN( baseIcnID, 11 + i ); - } + break; + } + case ICN::BUTTON_SMALL_NO_GOOD: + case ICN::BUTTON_SMALL_NO_EVIL: { + _icnVsSprite[id].resize( 2 ); - const fheroes2::FontColor buttonFontColor = isEvilInterface ? fheroes2::FontColor::GRAY : fheroes2::FontColor::WHITE; - renderTextOnButton( _icnVsSprite[id][0], _icnVsSprite[id][1], gettext_noop( "YES" ), { 6, 5 }, { 5, 6 }, { 86, 16 }, buttonFontColor ); + const bool isEvilInterface = ( id == ICN::BUTTON_SMALL_NO_EVIL ); + const int baseIcnID = isEvilInterface ? ICN::SYSTEME : ICN::SYSTEM; + if ( useOriginalResources() ) { + _icnVsSprite[id][0] = fheroes2::AGG::GetICN( baseIcnID, 7 ); + _icnVsSprite[id][1] = fheroes2::AGG::GetICN( baseIcnID, 8 ); break; } - case ICN::BUTTON_SMALL_NO_GOOD: - case ICN::BUTTON_SMALL_NO_EVIL: { - _icnVsSprite[id].resize( 2 ); - const bool isEvilInterface = ( id == ICN::BUTTON_SMALL_NO_EVIL ); - const int baseIcnID = isEvilInterface ? ICN::SYSTEME : ICN::SYSTEM; + for ( int32_t i = 0; i < static_cast( _icnVsSprite[id].size() ); ++i ) { + fheroes2::Sprite & out = _icnVsSprite[id][i]; + out = fheroes2::AGG::GetICN( baseIcnID, 11 + i ); + } - if ( useOriginalResources() ) { - _icnVsSprite[id][0] = GetICN( baseIcnID, 7 ); - _icnVsSprite[id][1] = GetICN( baseIcnID, 8 ); - break; - } + const fheroes2::FontColor buttonFontColor = isEvilInterface ? fheroes2::FontColor::GRAY : fheroes2::FontColor::WHITE; + renderTextOnButton( _icnVsSprite[id][0], _icnVsSprite[id][1], gettext_noop( "NO" ), { 6, 5 }, { 5, 6 }, { 86, 16 }, buttonFontColor ); - for ( int32_t i = 0; i < static_cast( _icnVsSprite[id].size() ); ++i ) { - Sprite & out = _icnVsSprite[id][i]; - out = GetICN( baseIcnID, 11 + i ); - } + break; + } + case ICN::BUTTON_SMALL_EXIT_GOOD: + case ICN::BUTTON_SMALL_EXIT_EVIL: { + _icnVsSprite[id].resize( 2 ); - const fheroes2::FontColor buttonFontColor = isEvilInterface ? fheroes2::FontColor::GRAY : fheroes2::FontColor::WHITE; - renderTextOnButton( _icnVsSprite[id][0], _icnVsSprite[id][1], gettext_noop( "NO" ), { 6, 5 }, { 5, 6 }, { 86, 16 }, buttonFontColor ); + const bool isEvilInterface = ( id == ICN::BUTTON_SMALL_EXIT_EVIL ); + if ( useOriginalResources() ) { + _icnVsSprite[id][0] = fheroes2::AGG::GetICN( isEvilInterface ? ICN::VIEWARME : ICN::VIEWARMY, 3 ); + _icnVsSprite[id][1] = fheroes2::AGG::GetICN( isEvilInterface ? ICN::VIEWARME : ICN::VIEWARMY, 4 ); break; } - case ICN::BUTTON_SMALL_EXIT_GOOD: - case ICN::BUTTON_SMALL_EXIT_EVIL: { - _icnVsSprite[id].resize( 2 ); - - const bool isEvilInterface = ( id == ICN::BUTTON_SMALL_EXIT_EVIL ); - - if ( useOriginalResources() ) { - _icnVsSprite[id][0] = GetICN( isEvilInterface ? ICN::VIEWARME : ICN::VIEWARMY, 3 ); - _icnVsSprite[id][1] = GetICN( isEvilInterface ? ICN::VIEWARME : ICN::VIEWARMY, 4 ); - break; - } - getTextAdaptedButton( _icnVsSprite[id][0], _icnVsSprite[id][1], gettext_noop( "EXIT" ), isEvilInterface ? ICN::EMPTY_EVIL_BUTTON : ICN::EMPTY_GOOD_BUTTON, - isEvilInterface ? ICN::STONEBAK_EVIL : ICN::STONEBAK ); + getTextAdaptedButton( _icnVsSprite[id][0], _icnVsSprite[id][1], gettext_noop( "EXIT" ), isEvilInterface ? ICN::EMPTY_EVIL_BUTTON : ICN::EMPTY_GOOD_BUTTON, + isEvilInterface ? ICN::STONEBAK_EVIL : ICN::STONEBAK ); + break; + } + case ICN::BUTTON_EXIT_HEROES_MEETING: { + _icnVsSprite[id].resize( 2 ); + + if ( useOriginalResources() ) { + _icnVsSprite[id][0] = fheroes2::AGG::GetICN( ICN::SWAPBTN, 0 ); + _icnVsSprite[id][1] = fheroes2::AGG::GetICN( ICN::SWAPBTN, 1 ); + // fix some wrong pixels in the original pressed state + setButtonCornersTransparent( _icnVsSprite[id][1] ); break; } - case ICN::BUTTON_EXIT_HEROES_MEETING: { - _icnVsSprite[id].resize( 2 ); - if ( useOriginalResources() ) { - _icnVsSprite[id][0] = GetICN( ICN::SWAPBTN, 0 ); - _icnVsSprite[id][1] = GetICN( ICN::SWAPBTN, 1 ); - // fix some wrong pixels in the original pressed state - setButtonCornersTransparent( _icnVsSprite[id][1] ); - break; - } + // The heroes meeting screen has an embedded shadow so the button needs to be fixed at the same size as the original one. + // TODO: Remove the embedded shadow and button in the heroes meeting screen and use getTextAdaptedButton() instead. + const int32_t textWidth = 70; + createNormalButton( _icnVsSprite[id][0], _icnVsSprite[id][1], textWidth, gettext_noop( "smallerButton|EXIT" ), false ); - // The heroes meeting screen has an embedded shadow so the button needs to be fixed at the same size as the original one. - // TODO: Remove the embedded shadow and button in the heroes meeting screen and use getTextAdaptedButton() instead. - const int32_t textWidth = 70; - createNormalButton( _icnVsSprite[id][0], _icnVsSprite[id][1], textWidth, gettext_noop( "smallerButton|EXIT" ), false ); + break; + } + case ICN::BUTTON_EXIT_TOWN: { + std::vector & buttonStates = _icnVsSprite[id]; + buttonStates.resize( 2 ); + if ( useOriginalResources() ) { + buttonStates[0] = fheroes2::AGG::GetICN( ICN::TREASURY, 1 ); + buttonStates[1] = fheroes2::AGG::GetICN( ICN::TREASURY, 2 ); break; } - case ICN::BUTTON_EXIT_TOWN: { - std::vector & buttonStates = _icnVsSprite[id]; - buttonStates.resize( 2 ); - if ( useOriginalResources() ) { - buttonStates[0] = GetICN( ICN::TREASURY, 1 ); - buttonStates[1] = GetICN( ICN::TREASURY, 2 ); - break; - } + // Needs to be generated from original assets because it needs the black background from the pressed state. + // TODO: Make a way to generate buttons with black background since it is needed for MAX and EXIT in the Well and Guilds. + for ( int32_t i = 0; i < static_cast( buttonStates.size() ); ++i ) { + fheroes2::Sprite & out = buttonStates[i]; + out = fheroes2::AGG::GetICN( ICN::TREASURY, 1 + i ); - // Needs to be generated from original assets because it needs the black background from the pressed state. - // TODO: Make a way to generate buttons with black background since it is needed for MAX and EXIT in the Well and Guilds. - for ( int32_t i = 0; i < static_cast( buttonStates.size() ); ++i ) { - Sprite & out = buttonStates[i]; - out = GetICN( ICN::TREASURY, 1 + i ); + // clean the button. + Fill( out, 6 - i, 4 + i, 70, 17, getButtonFillingColor( i == 0 ) ); + } - // clean the button. - Fill( out, 6 - i, 4 + i, 70, 17, getButtonFillingColor( i == 0 ) ); - } + const int32_t textWidth = 70; + renderTextOnButton( buttonStates[0], buttonStates[1], gettext_noop( "smallerButton|EXIT" ), { 7, 5 }, { 6, 6 }, + { textWidth, fheroes2::getFontHeight( fheroes2::FontSize::BUTTON_RELEASED ) }, fheroes2::FontColor::WHITE ); - const int32_t textWidth = 70; - renderTextOnButton( buttonStates[0], buttonStates[1], gettext_noop( "smallerButton|EXIT" ), { 7, 5 }, { 6, 6 }, - { textWidth, fheroes2::getFontHeight( fheroes2::FontSize::BUTTON_RELEASED ) }, fheroes2::FontColor::WHITE ); + break; + } + case ICN::BUTTON_EXIT_PUZZLE_DIM_DOOR_GOOD: + case ICN::BUTTON_EXIT_PUZZLE_DIM_DOOR_EVIL: { + std::vector & buttonStates = _icnVsSprite[id]; + buttonStates.resize( 2 ); + const bool isEvilInterface = ( id == ICN::BUTTON_EXIT_PUZZLE_DIM_DOOR_EVIL ); + const int originalButtonICN = isEvilInterface ? ICN::LGNDXTRE : ICN::LGNDXTRA; + + if ( useOriginalResources() ) { + buttonStates[0] = fheroes2::AGG::GetICN( originalButtonICN, 4 ); + buttonStates[1] = fheroes2::AGG::GetICN( originalButtonICN, 5 ); break; } - case ICN::BUTTON_EXIT_PUZZLE_DIM_DOOR_GOOD: - case ICN::BUTTON_EXIT_PUZZLE_DIM_DOOR_EVIL: { - std::vector & buttonStates = _icnVsSprite[id]; - buttonStates.resize( 2 ); - const bool isEvilInterface = ( id == ICN::BUTTON_EXIT_PUZZLE_DIM_DOOR_EVIL ); - const int originalButtonICN = isEvilInterface ? ICN::LGNDXTRE : ICN::LGNDXTRA; + // Needs to be generated from original assets because the background has a much darker shadow than normal. + // TODO: Make the button generated as normal after removing the embedded shadow on the background. + for ( int32_t i = 0; i < static_cast( buttonStates.size() ); ++i ) { + fheroes2::Sprite & out = buttonStates[i]; + out = fheroes2::AGG::GetICN( originalButtonICN, 4 + i ); - if ( useOriginalResources() ) { - buttonStates[0] = GetICN( originalButtonICN, 4 ); - buttonStates[1] = GetICN( originalButtonICN, 5 ); - break; - } + // clean the button. + Fill( out, 6 - i, 4 + i, 71 - i, 17, getButtonFillingColor( i == 0, !isEvilInterface ) ); + } - // Needs to be generated from original assets because the background has a much darker shadow than normal. - // TODO: Make the button generated as normal after removing the embedded shadow on the background. - for ( int32_t i = 0; i < static_cast( buttonStates.size() ); ++i ) { - Sprite & out = buttonStates[i]; - out = GetICN( originalButtonICN, 4 + i ); + const int32_t textWidth = 71; + renderTextOnButton( buttonStates[0], buttonStates[1], gettext_noop( "smallerButton|EXIT" ), { 6, 5 }, { 5, 6 }, + { textWidth, fheroes2::getFontHeight( fheroes2::FontSize::BUTTON_RELEASED ) }, + isEvilInterface ? fheroes2::FontColor::GRAY : fheroes2::FontColor::WHITE ); - // clean the button. - Fill( out, 6 - i, 4 + i, 71 - i, 17, getButtonFillingColor( i == 0, !isEvilInterface ) ); - } + break; + } + case ICN::BUTTON_SMALL_DISMISS_GOOD: + case ICN::BUTTON_SMALL_DISMISS_EVIL: { + _icnVsSprite[id].resize( 2 ); - const int32_t textWidth = 71; - renderTextOnButton( buttonStates[0], buttonStates[1], gettext_noop( "smallerButton|EXIT" ), { 6, 5 }, { 5, 6 }, - { textWidth, fheroes2::getFontHeight( fheroes2::FontSize::BUTTON_RELEASED ) }, - isEvilInterface ? fheroes2::FontColor::GRAY : fheroes2::FontColor::WHITE ); + const bool isEvilInterface = ( id == ICN::BUTTON_SMALL_DISMISS_EVIL ); + if ( useOriginalResources() ) { + _icnVsSprite[id][0] = fheroes2::AGG::GetICN( isEvilInterface ? ICN::VIEWARME : ICN::VIEWARMY, 1 ); + _icnVsSprite[id][1] = fheroes2::AGG::GetICN( isEvilInterface ? ICN::VIEWARME : ICN::VIEWARMY, 2 ); break; } - case ICN::BUTTON_SMALL_DISMISS_GOOD: - case ICN::BUTTON_SMALL_DISMISS_EVIL: { - _icnVsSprite[id].resize( 2 ); - const bool isEvilInterface = ( id == ICN::BUTTON_SMALL_DISMISS_EVIL ); + getTextAdaptedButton( _icnVsSprite[id][0], _icnVsSprite[id][1], gettext_noop( "DISMISS" ), isEvilInterface ? ICN::EMPTY_EVIL_BUTTON : ICN::EMPTY_GOOD_BUTTON, + isEvilInterface ? ICN::STONEBAK_EVIL : ICN::STONEBAK ); - if ( useOriginalResources() ) { - _icnVsSprite[id][0] = GetICN( isEvilInterface ? ICN::VIEWARME : ICN::VIEWARMY, 1 ); - _icnVsSprite[id][1] = GetICN( isEvilInterface ? ICN::VIEWARME : ICN::VIEWARMY, 2 ); - break; - } + break; + } + case ICN::BUTTON_SMALL_UPGRADE_GOOD: + case ICN::BUTTON_SMALL_UPGRADE_EVIL: { + _icnVsSprite[id].resize( 2 ); - getTextAdaptedButton( _icnVsSprite[id][0], _icnVsSprite[id][1], gettext_noop( "DISMISS" ), - isEvilInterface ? ICN::EMPTY_EVIL_BUTTON : ICN::EMPTY_GOOD_BUTTON, isEvilInterface ? ICN::STONEBAK_EVIL : ICN::STONEBAK ); + const bool isEvilInterface = ( id == ICN::BUTTON_SMALL_UPGRADE_EVIL ); + if ( useOriginalResources() ) { + _icnVsSprite[id][0] = fheroes2::AGG::GetICN( isEvilInterface ? ICN::VIEWARME : ICN::VIEWARMY, 5 ); + _icnVsSprite[id][1] = fheroes2::AGG::GetICN( isEvilInterface ? ICN::VIEWARME : ICN::VIEWARMY, 6 ); break; } - case ICN::BUTTON_SMALL_UPGRADE_GOOD: - case ICN::BUTTON_SMALL_UPGRADE_EVIL: { - _icnVsSprite[id].resize( 2 ); - const bool isEvilInterface = ( id == ICN::BUTTON_SMALL_UPGRADE_EVIL ); + getTextAdaptedButton( _icnVsSprite[id][0], _icnVsSprite[id][1], gettext_noop( "UPGRADE" ), isEvilInterface ? ICN::EMPTY_EVIL_BUTTON : ICN::EMPTY_GOOD_BUTTON, + isEvilInterface ? ICN::STONEBAK_EVIL : ICN::STONEBAK ); - if ( useOriginalResources() ) { - _icnVsSprite[id][0] = GetICN( isEvilInterface ? ICN::VIEWARME : ICN::VIEWARMY, 5 ); - _icnVsSprite[id][1] = GetICN( isEvilInterface ? ICN::VIEWARME : ICN::VIEWARMY, 6 ); - break; - } + break; + } + case ICN::BUTTON_SMALL_RESTART_GOOD: + case ICN::BUTTON_SMALL_RESTART_EVIL: { + _icnVsSprite[id].resize( 2 ); - getTextAdaptedButton( _icnVsSprite[id][0], _icnVsSprite[id][1], gettext_noop( "UPGRADE" ), - isEvilInterface ? ICN::EMPTY_EVIL_BUTTON : ICN::EMPTY_GOOD_BUTTON, isEvilInterface ? ICN::STONEBAK_EVIL : ICN::STONEBAK ); + const bool isEvilInterface = ( id == ICN::BUTTON_SMALL_RESTART_EVIL ); + if ( useOriginalResources() ) { + _icnVsSprite[id][0] = fheroes2::AGG::GetICN( isEvilInterface ? ICN::NON_UNIFORM_EVIL_RESTART_BUTTON : ICN::NON_UNIFORM_GOOD_RESTART_BUTTON, 0 ); + _icnVsSprite[id][1] = fheroes2::AGG::GetICN( isEvilInterface ? ICN::NON_UNIFORM_EVIL_RESTART_BUTTON : ICN::NON_UNIFORM_GOOD_RESTART_BUTTON, 1 ); break; } - case ICN::BUTTON_SMALL_RESTART_GOOD: - case ICN::BUTTON_SMALL_RESTART_EVIL: { - _icnVsSprite[id].resize( 2 ); - const bool isEvilInterface = ( id == ICN::BUTTON_SMALL_RESTART_EVIL ); + getTextAdaptedButton( _icnVsSprite[id][0], _icnVsSprite[id][1], gettext_noop( "RESTART" ), isEvilInterface ? ICN::EMPTY_EVIL_BUTTON : ICN::EMPTY_GOOD_BUTTON, + isEvilInterface ? ICN::STONEBAK_EVIL : ICN::STONEBAK ); - if ( useOriginalResources() ) { - _icnVsSprite[id][0] = GetICN( isEvilInterface ? ICN::NON_UNIFORM_EVIL_RESTART_BUTTON : ICN::NON_UNIFORM_GOOD_RESTART_BUTTON, 0 ); - _icnVsSprite[id][1] = GetICN( isEvilInterface ? ICN::NON_UNIFORM_EVIL_RESTART_BUTTON : ICN::NON_UNIFORM_GOOD_RESTART_BUTTON, 1 ); - break; - } - - getTextAdaptedButton( _icnVsSprite[id][0], _icnVsSprite[id][1], gettext_noop( "RESTART" ), - isEvilInterface ? ICN::EMPTY_EVIL_BUTTON : ICN::EMPTY_GOOD_BUTTON, isEvilInterface ? ICN::STONEBAK_EVIL : ICN::STONEBAK ); + break; + } + case ICN::BUTTON_KINGDOM_EXIT: { + _icnVsSprite[id].resize( 2 ); + if ( useOriginalResources() ) { + _icnVsSprite[id][0] = fheroes2::AGG::GetICN( ICN::OVERVIEW, 4 ); + _icnVsSprite[id][1] = fheroes2::AGG::GetICN( ICN::OVERVIEW, 5 ); break; } - case ICN::BUTTON_KINGDOM_EXIT: { - _icnVsSprite[id].resize( 2 ); - if ( useOriginalResources() ) { - _icnVsSprite[id][0] = GetICN( ICN::OVERVIEW, 4 ); - _icnVsSprite[id][1] = GetICN( ICN::OVERVIEW, 5 ); - break; - } + // Needs to be generated from original assets because the pressed state is 1px wider than normal. + for ( int32_t i = 0; i < static_cast( _icnVsSprite[id].size() ); ++i ) { + fheroes2::Sprite & out = _icnVsSprite[id][i]; + out = fheroes2::AGG::GetICN( ICN::OVERVIEW, 4 + i ); - // Needs to be generated from original assets because the pressed state is 1px wider than normal. - for ( int32_t i = 0; i < static_cast( _icnVsSprite[id].size() ); ++i ) { - Sprite & out = _icnVsSprite[id][i]; - out = GetICN( ICN::OVERVIEW, 4 + i ); + // clean the button. + Fill( out, 6 - i, 4 + i, 89, 16, getButtonFillingColor( i == 0 ) ); + } - // clean the button. - Fill( out, 6 - i, 4 + i, 89, 16, getButtonFillingColor( i == 0 ) ); - } + renderTextOnButton( _icnVsSprite[id][0], _icnVsSprite[id][1], gettext_noop( "EXIT" ), { 6, 5 }, { 5, 6 }, { 89, 16 }, fheroes2::FontColor::WHITE ); - renderTextOnButton( _icnVsSprite[id][0], _icnVsSprite[id][1], gettext_noop( "EXIT" ), { 6, 5 }, { 5, 6 }, { 89, 16 }, fheroes2::FontColor::WHITE ); + break; + } + case ICN::BUTTON_KINGDOM_HEROES: { + _icnVsSprite[id].resize( 2 ); + if ( useOriginalResources() ) { + _icnVsSprite[id][0] = fheroes2::AGG::GetICN( ICN::OVERVIEW, 0 ); + _icnVsSprite[id][1] = fheroes2::AGG::GetICN( ICN::OVERVIEW, 1 ); break; } - case ICN::BUTTON_KINGDOM_HEROES: { - _icnVsSprite[id].resize( 2 ); - if ( useOriginalResources() ) { - _icnVsSprite[id][0] = GetICN( ICN::OVERVIEW, 0 ); - _icnVsSprite[id][1] = GetICN( ICN::OVERVIEW, 1 ); - break; - } + for ( int32_t i = 0; i < static_cast( _icnVsSprite[id].size() ); ++i ) { + fheroes2::Sprite & out = _icnVsSprite[id][i]; + out = fheroes2::AGG::GetICN( ICN::OVERVIEW, 0 + i ); - for ( int32_t i = 0; i < static_cast( _icnVsSprite[id].size() ); ++i ) { - Sprite & out = _icnVsSprite[id][i]; - out = GetICN( ICN::OVERVIEW, 0 + i ); + // clean the button. + Fill( out, 5, 10 + i, 89, 20, getButtonFillingColor( i == 0 ) ); + } - // clean the button. - Fill( out, 5, 10 + i, 89, 20, getButtonFillingColor( i == 0 ) ); - } + renderTextOnButton( _icnVsSprite[id][0], _icnVsSprite[id][1], gettext_noop( "HEROES" ), { 6, 5 }, { 5, 6 }, { 89, 34 }, fheroes2::FontColor::WHITE ); - renderTextOnButton( _icnVsSprite[id][0], _icnVsSprite[id][1], gettext_noop( "HEROES" ), { 6, 5 }, { 5, 6 }, { 89, 34 }, fheroes2::FontColor::WHITE ); + break; + } + case ICN::BUTTON_KINGDOM_TOWNS: { + _icnVsSprite[id].resize( 2 ); + if ( useOriginalResources() ) { + _icnVsSprite[id][0] = fheroes2::AGG::GetICN( ICN::OVERVIEW, 2 ); + _icnVsSprite[id][1] = fheroes2::AGG::GetICN( ICN::OVERVIEW, 3 ); break; } - case ICN::BUTTON_KINGDOM_TOWNS: { - _icnVsSprite[id].resize( 2 ); - - if ( useOriginalResources() ) { - _icnVsSprite[id][0] = GetICN( ICN::OVERVIEW, 2 ); - _icnVsSprite[id][1] = GetICN( ICN::OVERVIEW, 3 ); - break; - } - - for ( int32_t i = 0; i < static_cast( _icnVsSprite[id].size() ); ++i ) { - Sprite & out = _icnVsSprite[id][i]; - out = GetICN( ICN::OVERVIEW, 2 + i ); - - // clean the button. - Fill( out, 6, 6 + i, 89 - i, 30, getButtonFillingColor( i == 0 ) ); - } - renderTextOnButton( _icnVsSprite[id][0], _icnVsSprite[id][1], gettext_noop( "TOWNS/\nCASTLES" ), { 6, 5 }, { 5, 6 }, { 90, 34 }, - fheroes2::FontColor::WHITE ); + for ( int32_t i = 0; i < static_cast( _icnVsSprite[id].size() ); ++i ) { + fheroes2::Sprite & out = _icnVsSprite[id][i]; + out = fheroes2::AGG::GetICN( ICN::OVERVIEW, 2 + i ); - break; + // clean the button. + Fill( out, 6, 6 + i, 89 - i, 30, getButtonFillingColor( i == 0 ) ); } - case ICN::BUTTON_MAPSIZE_SMALL: { - _icnVsSprite[id].resize( 2 ); + renderTextOnButton( _icnVsSprite[id][0], _icnVsSprite[id][1], gettext_noop( "TOWNS/\nCASTLES" ), { 6, 5 }, { 5, 6 }, { 90, 34 }, fheroes2::FontColor::WHITE ); - if ( useOriginalResources() ) { - _icnVsSprite[id][0] = GetICN( ICN::REQUESTS, 9 ); - _icnVsSprite[id][1] = GetICN( ICN::REQUESTS, 10 ); - break; - } + break; + } - const int32_t textWidth = 46; - createNormalButton( _icnVsSprite[id][0], _icnVsSprite[id][1], textWidth, gettext_noop( "S" ), false ); + case ICN::BUTTON_MAPSIZE_SMALL: { + _icnVsSprite[id].resize( 2 ); + if ( useOriginalResources() ) { + _icnVsSprite[id][0] = fheroes2::AGG::GetICN( ICN::REQUESTS, 9 ); + _icnVsSprite[id][1] = fheroes2::AGG::GetICN( ICN::REQUESTS, 10 ); break; } - case ICN::BUTTON_MAPSIZE_MEDIUM: { - _icnVsSprite[id].resize( 2 ); - if ( useOriginalResources() ) { - _icnVsSprite[id][0] = GetICN( ICN::REQUESTS, 11 ); - _icnVsSprite[id][1] = GetICN( ICN::REQUESTS, 12 ); - break; - } + const int32_t textWidth = 46; + createNormalButton( _icnVsSprite[id][0], _icnVsSprite[id][1], textWidth, gettext_noop( "S" ), false ); - const int32_t textWidth = 46; - createNormalButton( _icnVsSprite[id][0], _icnVsSprite[id][1], textWidth, gettext_noop( "M" ), false ); + break; + } + case ICN::BUTTON_MAPSIZE_MEDIUM: { + _icnVsSprite[id].resize( 2 ); + if ( useOriginalResources() ) { + _icnVsSprite[id][0] = fheroes2::AGG::GetICN( ICN::REQUESTS, 11 ); + _icnVsSprite[id][1] = fheroes2::AGG::GetICN( ICN::REQUESTS, 12 ); break; } - case ICN::BUTTON_MAPSIZE_LARGE: { - _icnVsSprite[id].resize( 2 ); - if ( useOriginalResources() ) { - _icnVsSprite[id][0] = GetICN( ICN::REQUESTS, 13 ); - _icnVsSprite[id][1] = GetICN( ICN::REQUESTS, 14 ); - break; - } + const int32_t textWidth = 46; + createNormalButton( _icnVsSprite[id][0], _icnVsSprite[id][1], textWidth, gettext_noop( "M" ), false ); - const int32_t textWidth = 46; - createNormalButton( _icnVsSprite[id][0], _icnVsSprite[id][1], textWidth, gettext_noop( "L" ), false ); + break; + } + case ICN::BUTTON_MAPSIZE_LARGE: { + _icnVsSprite[id].resize( 2 ); + if ( useOriginalResources() ) { + _icnVsSprite[id][0] = fheroes2::AGG::GetICN( ICN::REQUESTS, 13 ); + _icnVsSprite[id][1] = fheroes2::AGG::GetICN( ICN::REQUESTS, 14 ); break; } - case ICN::BUTTON_MAPSIZE_XLARGE: { - _icnVsSprite[id].resize( 2 ); - if ( useOriginalResources() ) { - _icnVsSprite[id][0] = GetICN( ICN::REQUESTS, 15 ); - _icnVsSprite[id][1] = GetICN( ICN::REQUESTS, 16 ); - break; - } + const int32_t textWidth = 46; + createNormalButton( _icnVsSprite[id][0], _icnVsSprite[id][1], textWidth, gettext_noop( "L" ), false ); - const int32_t textWidth = 46; - createNormalButton( _icnVsSprite[id][0], _icnVsSprite[id][1], textWidth, gettext_noop( "X-L" ), false ); + break; + } + case ICN::BUTTON_MAPSIZE_XLARGE: { + _icnVsSprite[id].resize( 2 ); + if ( useOriginalResources() ) { + _icnVsSprite[id][0] = fheroes2::AGG::GetICN( ICN::REQUESTS, 15 ); + _icnVsSprite[id][1] = fheroes2::AGG::GetICN( ICN::REQUESTS, 16 ); break; } - case ICN::BUTTON_MAPSIZE_ALL: { - _icnVsSprite[id].resize( 2 ); - if ( useOriginalResources() ) { - _icnVsSprite[id][0] = GetICN( ICN::REQUESTS, 17 ); - _icnVsSprite[id][1] = GetICN( ICN::REQUESTS, 18 ); - break; - } + const int32_t textWidth = 46; + createNormalButton( _icnVsSprite[id][0], _icnVsSprite[id][1], textWidth, gettext_noop( "X-L" ), false ); - const int32_t textWidth = 58; - createNormalButton( _icnVsSprite[id][0], _icnVsSprite[id][1], textWidth, gettext_noop( "ALL" ), false ); + break; + } + case ICN::BUTTON_MAPSIZE_ALL: { + _icnVsSprite[id].resize( 2 ); + if ( useOriginalResources() ) { + _icnVsSprite[id][0] = fheroes2::AGG::GetICN( ICN::REQUESTS, 17 ); + _icnVsSprite[id][1] = fheroes2::AGG::GetICN( ICN::REQUESTS, 18 ); break; } - case ICN::BUTTON_MAP_SELECT_EVIL: - case ICN::BUTTON_MAP_SELECT_GOOD: { - _icnVsSprite[id].resize( 2 ); - const bool isEvilInterface = ( id == ICN::BUTTON_MAP_SELECT_EVIL ); - - if ( useOriginalResources() ) { - _icnVsSprite[id][0] = GetICN( ICN::NGEXTRA, 64 ); - _icnVsSprite[id][1] = GetICN( ICN::NGEXTRA, 65 ); - if ( isEvilInterface ) { - const std::vector & goodToEvilPalette = PAL::GetPalette( PAL::PaletteType::GOOD_TO_EVIL_INTERFACE ); - fheroes2::ApplyPalette( _icnVsSprite[id][0], goodToEvilPalette ); - fheroes2::ApplyPalette( _icnVsSprite[id][1], goodToEvilPalette ); - } - break; - } - getTextAdaptedButton( _icnVsSprite[id][0], _icnVsSprite[id][1], gettext_noop( "SELECT" ), ICN::EMPTY_MAP_SELECT_BUTTON, ICN::UNKNOWN ); + const int32_t textWidth = 58; + createNormalButton( _icnVsSprite[id][0], _icnVsSprite[id][1], textWidth, gettext_noop( "ALL" ), false ); - if ( isEvilInterface ) { + break; + } + case ICN::BUTTON_MAP_SELECT_EVIL: + case ICN::BUTTON_MAP_SELECT_GOOD: { + _icnVsSprite[id].resize( 2 ); + const bool isEvilInterface = ( id == ICN::BUTTON_MAP_SELECT_EVIL ); + + if ( useOriginalResources() ) { + _icnVsSprite[id][0] = fheroes2::AGG::GetICN( ICN::NGEXTRA, 64 ); + _icnVsSprite[id][1] = fheroes2::AGG::GetICN( ICN::NGEXTRA, 65 ); + if ( isEvilInterface ) { const std::vector & goodToEvilPalette = PAL::GetPalette( PAL::PaletteType::GOOD_TO_EVIL_INTERFACE ); fheroes2::ApplyPalette( _icnVsSprite[id][0], goodToEvilPalette ); fheroes2::ApplyPalette( _icnVsSprite[id][1], goodToEvilPalette ); } - break; } - case ICN::BUTTON_STANDARD_GAME: { - _icnVsSprite[id].resize( 2 ); - if ( useOriginalResources() ) { - _icnVsSprite[id][0] = GetICN( ICN::BTNNEWGM, 0 ); - _icnVsSprite[id][1] = GetICN( ICN::BTNNEWGM, 1 ); - break; - } + getTextAdaptedButton( _icnVsSprite[id][0], _icnVsSprite[id][1], gettext_noop( "SELECT" ), ICN::EMPTY_MAP_SELECT_BUTTON, ICN::UNKNOWN ); - for ( int32_t i = 0; i < static_cast( _icnVsSprite[id].size() ); ++i ) { - Sprite & out = _icnVsSprite[id][i]; - out = GetICN( ICN::BTNCOM, i ); - // clean button. - Fill( out, 13, 11, 113, 31, getButtonFillingColor( i == 0 ) ); - } + if ( isEvilInterface ) { + const std::vector & goodToEvilPalette = PAL::GetPalette( PAL::PaletteType::GOOD_TO_EVIL_INTERFACE ); + fheroes2::ApplyPalette( _icnVsSprite[id][0], goodToEvilPalette ); + fheroes2::ApplyPalette( _icnVsSprite[id][1], goodToEvilPalette ); + } - renderTextOnButton( _icnVsSprite[id][0], _icnVsSprite[id][1], gettext_noop( "STANDARD\nGAME" ), { 12, 5 }, { 11, 6 }, { 120, 47 }, - fheroes2::FontColor::WHITE ); + break; + } + case ICN::BUTTON_STANDARD_GAME: { + _icnVsSprite[id].resize( 2 ); + if ( useOriginalResources() ) { + _icnVsSprite[id][0] = fheroes2::AGG::GetICN( ICN::BTNNEWGM, 0 ); + _icnVsSprite[id][1] = fheroes2::AGG::GetICN( ICN::BTNNEWGM, 1 ); break; } - case ICN::BUTTON_CAMPAIGN_GAME: { - _icnVsSprite[id].resize( 2 ); - if ( useOriginalResources() ) { - _icnVsSprite[id][0] = GetICN( ICN::BTNNEWGM, 2 ); - _icnVsSprite[id][1] = GetICN( ICN::BTNNEWGM, 3 ); - break; - } + for ( int32_t i = 0; i < static_cast( _icnVsSprite[id].size() ); ++i ) { + fheroes2::Sprite & out = _icnVsSprite[id][i]; + out = fheroes2::AGG::GetICN( ICN::BTNCOM, i ); + // clean button. + Fill( out, 13, 11, 113, 31, getButtonFillingColor( i == 0 ) ); + } - for ( int32_t i = 0; i < static_cast( _icnVsSprite[id].size() ); ++i ) { - Sprite & out = _icnVsSprite[id][i]; - out = GetICN( ICN::BTNCOM, i ); - // clean button. - Fill( out, 13, 11, 113, 31, getButtonFillingColor( i == 0 ) ); - } + renderTextOnButton( _icnVsSprite[id][0], _icnVsSprite[id][1], gettext_noop( "STANDARD\nGAME" ), { 12, 5 }, { 11, 6 }, { 120, 47 }, + fheroes2::FontColor::WHITE ); - renderTextOnButton( _icnVsSprite[id][0], _icnVsSprite[id][1], gettext_noop( "CAMPAIGN\nGAME" ), { 11, 5 }, { 10, 6 }, { 119, 47 }, - fheroes2::FontColor::WHITE ); + break; + } + case ICN::BUTTON_CAMPAIGN_GAME: { + _icnVsSprite[id].resize( 2 ); + if ( useOriginalResources() ) { + _icnVsSprite[id][0] = fheroes2::AGG::GetICN( ICN::BTNNEWGM, 2 ); + _icnVsSprite[id][1] = fheroes2::AGG::GetICN( ICN::BTNNEWGM, 3 ); break; } - case ICN::BUTTON_MULTIPLAYER_GAME: { - _icnVsSprite[id].resize( 2 ); - if ( useOriginalResources() ) { - _icnVsSprite[id][0] = GetICN( ICN::BTNNEWGM, 4 ); - _icnVsSprite[id][1] = GetICN( ICN::BTNNEWGM, 5 ); - break; - } + for ( int32_t i = 0; i < static_cast( _icnVsSprite[id].size() ); ++i ) { + fheroes2::Sprite & out = _icnVsSprite[id][i]; + out = fheroes2::AGG::GetICN( ICN::BTNCOM, i ); + // clean button. + Fill( out, 13, 11, 113, 31, getButtonFillingColor( i == 0 ) ); + } - for ( int32_t i = 0; i < static_cast( _icnVsSprite[id].size() ); ++i ) { - Sprite & out = _icnVsSprite[id][i]; - out = GetICN( ICN::BTNCOM, i ); - // clean button. - Fill( out, 13, 11, 113, 31, getButtonFillingColor( i == 0 ) ); - } + renderTextOnButton( _icnVsSprite[id][0], _icnVsSprite[id][1], gettext_noop( "CAMPAIGN\nGAME" ), { 11, 5 }, { 10, 6 }, { 119, 47 }, + fheroes2::FontColor::WHITE ); - renderTextOnButton( _icnVsSprite[id][0], _icnVsSprite[id][1], gettext_noop( "MULTI-\nPLAYER\nGAME" ), { 12, 5 }, { 11, 6 }, { 117, 47 }, - fheroes2::FontColor::WHITE ); + break; + } + case ICN::BUTTON_MULTIPLAYER_GAME: { + _icnVsSprite[id].resize( 2 ); + if ( useOriginalResources() ) { + _icnVsSprite[id][0] = fheroes2::AGG::GetICN( ICN::BTNNEWGM, 4 ); + _icnVsSprite[id][1] = fheroes2::AGG::GetICN( ICN::BTNNEWGM, 5 ); break; } - case ICN::BUTTON_LARGE_CANCEL: { - _icnVsSprite[id].resize( 2 ); - if ( useOriginalResources() ) { - _icnVsSprite[id][0] = GetICN( ICN::BTNNEWGM, 6 ); - _icnVsSprite[id][1] = GetICN( ICN::BTNNEWGM, 7 ); - break; - } + for ( int32_t i = 0; i < static_cast( _icnVsSprite[id].size() ); ++i ) { + fheroes2::Sprite & out = _icnVsSprite[id][i]; + out = fheroes2::AGG::GetICN( ICN::BTNCOM, i ); + // clean button. + Fill( out, 13, 11, 113, 31, getButtonFillingColor( i == 0 ) ); + } - for ( int32_t i = 0; i < static_cast( _icnVsSprite[id].size() ); ++i ) { - Sprite & out = _icnVsSprite[id][i]; - out = GetICN( ICN::BTNCOM, i ); - // clean button. - Fill( out, 13, 11, 113, 31, getButtonFillingColor( i == 0 ) ); - } + renderTextOnButton( _icnVsSprite[id][0], _icnVsSprite[id][1], gettext_noop( "MULTI-\nPLAYER\nGAME" ), { 12, 5 }, { 11, 6 }, { 117, 47 }, + fheroes2::FontColor::WHITE ); - renderTextOnButton( _icnVsSprite[id][0], _icnVsSprite[id][1], gettext_noop( "CANCEL" ), { 12, 5 }, { 11, 6 }, { 117, 47 }, fheroes2::FontColor::WHITE ); + break; + } + case ICN::BUTTON_LARGE_CANCEL: { + _icnVsSprite[id].resize( 2 ); + if ( useOriginalResources() ) { + _icnVsSprite[id][0] = fheroes2::AGG::GetICN( ICN::BTNNEWGM, 6 ); + _icnVsSprite[id][1] = fheroes2::AGG::GetICN( ICN::BTNNEWGM, 7 ); break; } - case ICN::BUTTON_LARGE_CONFIG: { - _icnVsSprite[id].resize( 2 ); - if ( useOriginalResources() ) { - _icnVsSprite[id][0] = GetICN( ICN::BTNDCCFG, 4 ); - _icnVsSprite[id][1] = GetICN( ICN::BTNDCCFG, 5 ); - break; - } + for ( int32_t i = 0; i < static_cast( _icnVsSprite[id].size() ); ++i ) { + fheroes2::Sprite & out = _icnVsSprite[id][i]; + out = fheroes2::AGG::GetICN( ICN::BTNCOM, i ); + // clean button. + Fill( out, 13, 11, 113, 31, getButtonFillingColor( i == 0 ) ); + } - for ( int32_t i = 0; i < static_cast( _icnVsSprite[id].size() ); ++i ) { - Sprite & out = _icnVsSprite[id][i]; - out = GetICN( ICN::BTNCOM, i ); - // clean button. - Fill( out, 13, 11, 113, 31, getButtonFillingColor( i == 0 ) ); - } + renderTextOnButton( _icnVsSprite[id][0], _icnVsSprite[id][1], gettext_noop( "CANCEL" ), { 12, 5 }, { 11, 6 }, { 117, 47 }, fheroes2::FontColor::WHITE ); - renderTextOnButton( _icnVsSprite[id][0], _icnVsSprite[id][1], gettext_noop( "CONFIG" ), { 12, 5 }, { 11, 6 }, { 117, 47 }, fheroes2::FontColor::WHITE ); + break; + } + case ICN::BUTTON_LARGE_CONFIG: { + _icnVsSprite[id].resize( 2 ); + if ( useOriginalResources() ) { + _icnVsSprite[id][0] = fheroes2::AGG::GetICN( ICN::BTNDCCFG, 4 ); + _icnVsSprite[id][1] = fheroes2::AGG::GetICN( ICN::BTNDCCFG, 5 ); break; } - case ICN::BUTTON_ORIGINAL_CAMPAIGN: { - _icnVsSprite[id].resize( 2 ); - if ( useOriginalResources() ) { - _icnVsSprite[id][0] = GetICN( ICN::X_LOADCM, 0 ); - _icnVsSprite[id][1] = GetICN( ICN::X_LOADCM, 1 ); - break; - } + for ( int32_t i = 0; i < static_cast( _icnVsSprite[id].size() ); ++i ) { + fheroes2::Sprite & out = _icnVsSprite[id][i]; + out = fheroes2::AGG::GetICN( ICN::BTNCOM, i ); + // clean button. + Fill( out, 13, 11, 113, 31, getButtonFillingColor( i == 0 ) ); + } - for ( int32_t i = 0; i < static_cast( _icnVsSprite[id].size() ); ++i ) { - Sprite & out = _icnVsSprite[id][i]; - out = GetICN( ICN::BTNCOM, i ); - // clean button. - Fill( out, 13, 11, 113, 31, getButtonFillingColor( i == 0 ) ); - } + renderTextOnButton( _icnVsSprite[id][0], _icnVsSprite[id][1], gettext_noop( "CONFIG" ), { 12, 5 }, { 11, 6 }, { 117, 47 }, fheroes2::FontColor::WHITE ); - renderTextOnButton( _icnVsSprite[id][0], _icnVsSprite[id][1], gettext_noop( "ORIGINAL\nCAMPAIGN" ), { 12, 5 }, { 11, 6 }, { 117, 47 }, - fheroes2::FontColor::WHITE ); + break; + } + case ICN::BUTTON_ORIGINAL_CAMPAIGN: { + _icnVsSprite[id].resize( 2 ); + if ( useOriginalResources() ) { + _icnVsSprite[id][0] = fheroes2::AGG::GetICN( ICN::X_LOADCM, 0 ); + _icnVsSprite[id][1] = fheroes2::AGG::GetICN( ICN::X_LOADCM, 1 ); break; } - case ICN::BUTTON_EXPANSION_CAMPAIGN: { - _icnVsSprite[id].resize( 2 ); - if ( useOriginalResources() ) { - _icnVsSprite[id][0] = GetICN( ICN::X_LOADCM, 2 ); - _icnVsSprite[id][1] = GetICN( ICN::X_LOADCM, 3 ); - break; - } + for ( int32_t i = 0; i < static_cast( _icnVsSprite[id].size() ); ++i ) { + fheroes2::Sprite & out = _icnVsSprite[id][i]; + out = fheroes2::AGG::GetICN( ICN::BTNCOM, i ); + // clean button. + Fill( out, 13, 11, 113, 31, getButtonFillingColor( i == 0 ) ); + } - for ( int32_t i = 0; i < static_cast( _icnVsSprite[id].size() ); ++i ) { - Sprite & out = _icnVsSprite[id][i]; - out = GetICN( ICN::BTNCOM, i ); - // clean button. - Fill( out, 13, 11, 113, 31, getButtonFillingColor( i == 0 ) ); - } + renderTextOnButton( _icnVsSprite[id][0], _icnVsSprite[id][1], gettext_noop( "ORIGINAL\nCAMPAIGN" ), { 12, 5 }, { 11, 6 }, { 117, 47 }, + fheroes2::FontColor::WHITE ); - renderTextOnButton( _icnVsSprite[id][0], _icnVsSprite[id][1], gettext_noop( "EXPANSION\nCAMPAIGN" ), { 12, 5 }, { 11, 6 }, { 117, 47 }, - fheroes2::FontColor::WHITE ); + break; + } + case ICN::BUTTON_EXPANSION_CAMPAIGN: { + _icnVsSprite[id].resize( 2 ); + if ( useOriginalResources() ) { + _icnVsSprite[id][0] = fheroes2::AGG::GetICN( ICN::X_LOADCM, 2 ); + _icnVsSprite[id][1] = fheroes2::AGG::GetICN( ICN::X_LOADCM, 3 ); break; } - case ICN::BUTTON_HOT_SEAT: { - _icnVsSprite[id].resize( 2 ); - if ( useOriginalResources() ) { - _icnVsSprite[id][0] = GetICN( ICN::BTNMP, 0 ); - _icnVsSprite[id][1] = GetICN( ICN::BTNMP, 1 ); - break; - } + for ( int32_t i = 0; i < static_cast( _icnVsSprite[id].size() ); ++i ) { + fheroes2::Sprite & out = _icnVsSprite[id][i]; + out = fheroes2::AGG::GetICN( ICN::BTNCOM, i ); + // clean button. + Fill( out, 13, 11, 113, 31, getButtonFillingColor( i == 0 ) ); + } - for ( int32_t i = 0; i < static_cast( _icnVsSprite[id].size() ); ++i ) { - Sprite & out = _icnVsSprite[id][i]; - out = GetICN( ICN::BTNCOM, i ); - // clean button. - Fill( out, 13, 11, 113, 31, getButtonFillingColor( i == 0 ) ); - } + renderTextOnButton( _icnVsSprite[id][0], _icnVsSprite[id][1], gettext_noop( "EXPANSION\nCAMPAIGN" ), { 12, 5 }, { 11, 6 }, { 117, 47 }, + fheroes2::FontColor::WHITE ); - renderTextOnButton( _icnVsSprite[id][0], _icnVsSprite[id][1], gettext_noop( "HOT SEAT" ), { 11, 5 }, { 10, 6 }, { 120, 47 }, fheroes2::FontColor::WHITE ); + break; + } + case ICN::BUTTON_HOT_SEAT: { + _icnVsSprite[id].resize( 2 ); + if ( useOriginalResources() ) { + _icnVsSprite[id][0] = fheroes2::AGG::GetICN( ICN::BTNMP, 0 ); + _icnVsSprite[id][1] = fheroes2::AGG::GetICN( ICN::BTNMP, 1 ); break; } - case ICN::BUTTON_2_PLAYERS: { - _icnVsSprite[id].resize( 2 ); - if ( useOriginalResources() ) { - _icnVsSprite[id][0] = GetICN( ICN::BTNHOTST, 0 ); - _icnVsSprite[id][1] = GetICN( ICN::BTNHOTST, 1 ); - break; - } + for ( int32_t i = 0; i < static_cast( _icnVsSprite[id].size() ); ++i ) { + fheroes2::Sprite & out = _icnVsSprite[id][i]; + out = fheroes2::AGG::GetICN( ICN::BTNCOM, i ); + // clean button. + Fill( out, 13, 11, 113, 31, getButtonFillingColor( i == 0 ) ); + } - for ( int32_t i = 0; i < static_cast( _icnVsSprite[id].size() ); ++i ) { - Sprite & out = _icnVsSprite[id][i]; - out = GetICN( ICN::BTNCOM, i ); - // clean button. - Fill( out, 13, 11, 113, 31, getButtonFillingColor( i == 0 ) ); - } + renderTextOnButton( _icnVsSprite[id][0], _icnVsSprite[id][1], gettext_noop( "HOT SEAT" ), { 11, 5 }, { 10, 6 }, { 120, 47 }, fheroes2::FontColor::WHITE ); - renderTextOnButton( _icnVsSprite[id][0], _icnVsSprite[id][1], gettext_noop( "2 PLAYERS" ), { 11, 5 }, { 10, 6 }, { 120, 47 }, - fheroes2::FontColor::WHITE ); + break; + } + case ICN::BUTTON_2_PLAYERS: { + _icnVsSprite[id].resize( 2 ); + if ( useOriginalResources() ) { + _icnVsSprite[id][0] = fheroes2::AGG::GetICN( ICN::BTNHOTST, 0 ); + _icnVsSprite[id][1] = fheroes2::AGG::GetICN( ICN::BTNHOTST, 1 ); break; } - case ICN::BUTTON_3_PLAYERS: { - _icnVsSprite[id].resize( 2 ); - if ( useOriginalResources() ) { - _icnVsSprite[id][0] = GetICN( ICN::BTNHOTST, 2 ); - _icnVsSprite[id][1] = GetICN( ICN::BTNHOTST, 3 ); - break; - } + for ( int32_t i = 0; i < static_cast( _icnVsSprite[id].size() ); ++i ) { + fheroes2::Sprite & out = _icnVsSprite[id][i]; + out = fheroes2::AGG::GetICN( ICN::BTNCOM, i ); + // clean button. + Fill( out, 13, 11, 113, 31, getButtonFillingColor( i == 0 ) ); + } - for ( int32_t i = 0; i < static_cast( _icnVsSprite[id].size() ); ++i ) { - Sprite & out = _icnVsSprite[id][i]; - out = GetICN( ICN::BTNCOM, i ); - // clean button. - Fill( out, 13, 11, 113, 31, getButtonFillingColor( i == 0 ) ); - } + renderTextOnButton( _icnVsSprite[id][0], _icnVsSprite[id][1], gettext_noop( "2 PLAYERS" ), { 11, 5 }, { 10, 6 }, { 120, 47 }, fheroes2::FontColor::WHITE ); - renderTextOnButton( _icnVsSprite[id][0], _icnVsSprite[id][1], gettext_noop( "3 PLAYERS" ), { 11, 5 }, { 10, 6 }, { 120, 47 }, - fheroes2::FontColor::WHITE ); + break; + } + case ICN::BUTTON_3_PLAYERS: { + _icnVsSprite[id].resize( 2 ); + if ( useOriginalResources() ) { + _icnVsSprite[id][0] = fheroes2::AGG::GetICN( ICN::BTNHOTST, 2 ); + _icnVsSprite[id][1] = fheroes2::AGG::GetICN( ICN::BTNHOTST, 3 ); break; } - case ICN::BUTTON_4_PLAYERS: { - _icnVsSprite[id].resize( 2 ); - if ( useOriginalResources() ) { - _icnVsSprite[id][0] = GetICN( ICN::BTNHOTST, 4 ); - _icnVsSprite[id][1] = GetICN( ICN::BTNHOTST, 5 ); - break; - } + for ( int32_t i = 0; i < static_cast( _icnVsSprite[id].size() ); ++i ) { + fheroes2::Sprite & out = _icnVsSprite[id][i]; + out = fheroes2::AGG::GetICN( ICN::BTNCOM, i ); + // clean button. + Fill( out, 13, 11, 113, 31, getButtonFillingColor( i == 0 ) ); + } - for ( int32_t i = 0; i < static_cast( _icnVsSprite[id].size() ); ++i ) { - Sprite & out = _icnVsSprite[id][i]; - out = GetICN( ICN::BTNCOM, i ); - // clean button. - Fill( out, 13, 11, 113, 31, getButtonFillingColor( i == 0 ) ); - } + renderTextOnButton( _icnVsSprite[id][0], _icnVsSprite[id][1], gettext_noop( "3 PLAYERS" ), { 11, 5 }, { 10, 6 }, { 120, 47 }, fheroes2::FontColor::WHITE ); - renderTextOnButton( _icnVsSprite[id][0], _icnVsSprite[id][1], gettext_noop( "4 PLAYERS" ), { 11, 5 }, { 10, 6 }, { 120, 47 }, - fheroes2::FontColor::WHITE ); + break; + } + case ICN::BUTTON_4_PLAYERS: { + _icnVsSprite[id].resize( 2 ); + if ( useOriginalResources() ) { + _icnVsSprite[id][0] = fheroes2::AGG::GetICN( ICN::BTNHOTST, 4 ); + _icnVsSprite[id][1] = fheroes2::AGG::GetICN( ICN::BTNHOTST, 5 ); break; } - case ICN::BUTTON_5_PLAYERS: { - _icnVsSprite[id].resize( 2 ); - if ( useOriginalResources() ) { - _icnVsSprite[id][0] = GetICN( ICN::BTNHOTST, 6 ); - _icnVsSprite[id][1] = GetICN( ICN::BTNHOTST, 7 ); - break; - } + for ( int32_t i = 0; i < static_cast( _icnVsSprite[id].size() ); ++i ) { + fheroes2::Sprite & out = _icnVsSprite[id][i]; + out = fheroes2::AGG::GetICN( ICN::BTNCOM, i ); + // clean button. + Fill( out, 13, 11, 113, 31, getButtonFillingColor( i == 0 ) ); + } - for ( int32_t i = 0; i < static_cast( _icnVsSprite[id].size() ); ++i ) { - Sprite & out = _icnVsSprite[id][i]; - out = GetICN( ICN::BTNCOM, i ); - // clean button. - Fill( out, 13, 11, 113, 31, getButtonFillingColor( i == 0 ) ); - } + renderTextOnButton( _icnVsSprite[id][0], _icnVsSprite[id][1], gettext_noop( "4 PLAYERS" ), { 11, 5 }, { 10, 6 }, { 120, 47 }, fheroes2::FontColor::WHITE ); - renderTextOnButton( _icnVsSprite[id][0], _icnVsSprite[id][1], gettext_noop( "5 PLAYERS" ), { 11, 5 }, { 10, 6 }, { 120, 47 }, - fheroes2::FontColor::WHITE ); + break; + } + case ICN::BUTTON_5_PLAYERS: { + _icnVsSprite[id].resize( 2 ); + if ( useOriginalResources() ) { + _icnVsSprite[id][0] = fheroes2::AGG::GetICN( ICN::BTNHOTST, 6 ); + _icnVsSprite[id][1] = fheroes2::AGG::GetICN( ICN::BTNHOTST, 7 ); break; } - case ICN::BUTTON_6_PLAYERS: { - _icnVsSprite[id].resize( 2 ); - if ( useOriginalResources() ) { - _icnVsSprite[id][0] = GetICN( ICN::BTNHOTST, 8 ); - _icnVsSprite[id][1] = GetICN( ICN::BTNHOTST, 9 ); - break; - } + for ( int32_t i = 0; i < static_cast( _icnVsSprite[id].size() ); ++i ) { + fheroes2::Sprite & out = _icnVsSprite[id][i]; + out = fheroes2::AGG::GetICN( ICN::BTNCOM, i ); + // clean button. + Fill( out, 13, 11, 113, 31, getButtonFillingColor( i == 0 ) ); + } - for ( int32_t i = 0; i < static_cast( _icnVsSprite[id].size() ); ++i ) { - Sprite & out = _icnVsSprite[id][i]; - out = GetICN( ICN::BTNCOM, i ); - // clean button. - Fill( out, 13, 11, 113, 31, getButtonFillingColor( i == 0 ) ); - } + renderTextOnButton( _icnVsSprite[id][0], _icnVsSprite[id][1], gettext_noop( "5 PLAYERS" ), { 11, 5 }, { 10, 6 }, { 120, 47 }, fheroes2::FontColor::WHITE ); - renderTextOnButton( _icnVsSprite[id][0], _icnVsSprite[id][1], gettext_noop( "6 PLAYERS" ), { 11, 5 }, { 10, 6 }, { 120, 47 }, - fheroes2::FontColor::WHITE ); + break; + } + case ICN::BUTTON_6_PLAYERS: { + _icnVsSprite[id].resize( 2 ); + if ( useOriginalResources() ) { + _icnVsSprite[id][0] = fheroes2::AGG::GetICN( ICN::BTNHOTST, 8 ); + _icnVsSprite[id][1] = fheroes2::AGG::GetICN( ICN::BTNHOTST, 9 ); break; } - case ICN::BTNGIFT_GOOD: - case ICN::BTNGIFT_EVIL: { - _icnVsSprite[id].resize( 2 ); - const bool isEvilInterface = ( id == ICN::BTNGIFT_EVIL ); - const int baseIcnId = isEvilInterface ? ICN::SYSTEME : ICN::SYSTEM; + for ( int32_t i = 0; i < static_cast( _icnVsSprite[id].size() ); ++i ) { + fheroes2::Sprite & out = _icnVsSprite[id][i]; + out = fheroes2::AGG::GetICN( ICN::BTNCOM, i ); + // clean button. + Fill( out, 13, 11, 113, 31, getButtonFillingColor( i == 0 ) ); + } - for ( int32_t i = 0; i < static_cast( _icnVsSprite[id].size() ); ++i ) { - Sprite & out = _icnVsSprite[id][i]; - out = GetICN( baseIcnId, 11 + i ); - } + renderTextOnButton( _icnVsSprite[id][0], _icnVsSprite[id][1], gettext_noop( "6 PLAYERS" ), { 11, 5 }, { 10, 6 }, { 120, 47 }, fheroes2::FontColor::WHITE ); - const fheroes2::FontColor buttonFontColor = isEvilInterface ? fheroes2::FontColor::GRAY : fheroes2::FontColor::WHITE; - renderTextOnButton( _icnVsSprite[id][0], _icnVsSprite[id][1], gettext_noop( "GIFT" ), { 5, 5 }, { 4, 6 }, { 88, 16 }, buttonFontColor ); + break; + } + case ICN::BTNGIFT_GOOD: + case ICN::BTNGIFT_EVIL: { + _icnVsSprite[id].resize( 2 ); - break; + const bool isEvilInterface = ( id == ICN::BTNGIFT_EVIL ); + const int baseIcnId = isEvilInterface ? ICN::SYSTEME : ICN::SYSTEM; + + for ( int32_t i = 0; i < static_cast( _icnVsSprite[id].size() ); ++i ) { + fheroes2::Sprite & out = _icnVsSprite[id][i]; + out = fheroes2::AGG::GetICN( baseIcnId, 11 + i ); } - case ICN::BUYMAX: { - _icnVsSprite[id].resize( 2 ); - getTextAdaptedButton( _icnVsSprite[id][0], _icnVsSprite[id][1], gettext_noop( "MAX" ), ICN::EMPTY_GUILDWELL_BUTTON, ICN::UNKNOWN ); + const fheroes2::FontColor buttonFontColor = isEvilInterface ? fheroes2::FontColor::GRAY : fheroes2::FontColor::WHITE; + renderTextOnButton( _icnVsSprite[id][0], _icnVsSprite[id][1], gettext_noop( "GIFT" ), { 5, 5 }, { 4, 6 }, { 88, 16 }, buttonFontColor ); - break; - } - case ICN::BUTTON_GUILDWELL_EXIT: { - _icnVsSprite[id].resize( 2 ); + break; + } + case ICN::BUYMAX: { + _icnVsSprite[id].resize( 2 ); - if ( useOriginalResources() ) { - _icnVsSprite[id][0] = GetICN( ICN::WELLXTRA, 0 ); - _icnVsSprite[id][1] = GetICN( ICN::WELLXTRA, 1 ); - break; - } - fheroes2::getTextAdaptedButton( _icnVsSprite[id][0], _icnVsSprite[id][1], gettext_noop( "EXIT" ), ICN::EMPTY_GUILDWELL_BUTTON, ICN::UNKNOWN ); + getTextAdaptedButton( _icnVsSprite[id][0], _icnVsSprite[id][1], gettext_noop( "MAX" ), ICN::EMPTY_GUILDWELL_BUTTON, ICN::UNKNOWN ); + + break; + } + case ICN::BUTTON_GUILDWELL_EXIT: { + _icnVsSprite[id].resize( 2 ); + if ( useOriginalResources() ) { + _icnVsSprite[id][0] = fheroes2::AGG::GetICN( ICN::WELLXTRA, 0 ); + _icnVsSprite[id][1] = fheroes2::AGG::GetICN( ICN::WELLXTRA, 1 ); break; } - case ICN::BUTTON_VIEWWORLD_EXIT_GOOD: - case ICN::BUTTON_VIEWWORLD_EXIT_EVIL: { - _icnVsSprite[id].resize( 2 ); - - const bool isEvilInterface = ( id == ICN::BUTTON_VIEWWORLD_EXIT_EVIL ); - const int baseIcnId = isEvilInterface ? ICN::LGNDXTRE : ICN::LGNDXTRA; + fheroes2::getTextAdaptedButton( _icnVsSprite[id][0], _icnVsSprite[id][1], gettext_noop( "EXIT" ), ICN::EMPTY_GUILDWELL_BUTTON, ICN::UNKNOWN ); - for ( int32_t i = 0; i < static_cast( _icnVsSprite[id].size() ); ++i ) { - Sprite & out = _icnVsSprite[id][i]; - out = GetICN( baseIcnId, 0 + i ); - // clean the button. - Fill( out, 27 - i, 4 + i, 25, 26, getButtonFillingColor( i == 0, !isEvilInterface ) ); - } + break; + } + case ICN::BUTTON_VIEWWORLD_EXIT_GOOD: + case ICN::BUTTON_VIEWWORLD_EXIT_EVIL: { + _icnVsSprite[id].resize( 2 ); - const fheroes2::FontColor buttonFontColor = isEvilInterface ? fheroes2::FontColor::GRAY : fheroes2::FontColor::WHITE; - renderTextOnButton( _icnVsSprite[id][0], _icnVsSprite[id][1], gettext_noop( "EXIT" ), { 6, 5 }, { 5, 6 }, { 71, 27 }, buttonFontColor ); + const bool isEvilInterface = ( id == ICN::BUTTON_VIEWWORLD_EXIT_EVIL ); + const int baseIcnId = isEvilInterface ? ICN::LGNDXTRE : ICN::LGNDXTRA; - break; + for ( int32_t i = 0; i < static_cast( _icnVsSprite[id].size() ); ++i ) { + fheroes2::Sprite & out = _icnVsSprite[id][i]; + out = fheroes2::AGG::GetICN( baseIcnId, 0 + i ); + // clean the button. + Fill( out, 27 - i, 4 + i, 25, 26, getButtonFillingColor( i == 0, !isEvilInterface ) ); } - case ICN::GOOD_CAMPAIGN_BUTTONS: - case ICN::EVIL_CAMPAIGN_BUTTONS: { - _icnVsSprite[id].resize( 10 ); - - if ( useOriginalResources() ) { - const bool isEvilInterface = id == ICN::EVIL_CAMPAIGN_BUTTONS; - const int originalIcnId = isEvilInterface ? ICN::CAMPXTRE : ICN::CAMPXTRG; - const int buttonBackground = isEvilInterface ? ICN::STONEBAK_EVIL : ICN::STONEBAK; - // The evil buttons' released state are 2 pixels wider. - const int offsetEvilX = isEvilInterface ? 2 : 0; - // remove embedded shadows so that we can generate shadows with our own code later - for ( uint32_t i = 0; i < 8; i += 2 ) { - // released - const Sprite & originalReleased = GetICN( originalIcnId, i ); + const fheroes2::FontColor buttonFontColor = isEvilInterface ? fheroes2::FontColor::GRAY : fheroes2::FontColor::WHITE; + renderTextOnButton( _icnVsSprite[id][0], _icnVsSprite[id][1], gettext_noop( "EXIT" ), { 6, 5 }, { 5, 6 }, { 71, 27 }, buttonFontColor ); - Sprite & released = _icnVsSprite[id][i]; - released.resize( originalReleased.width() - 6 + offsetEvilX, originalReleased.height() - 8 ); - released.reset(); + break; + } + case ICN::GOOD_CAMPAIGN_BUTTONS: + case ICN::EVIL_CAMPAIGN_BUTTONS: { + _icnVsSprite[id].resize( 10 ); + + if ( useOriginalResources() ) { + const bool isEvilInterface = id == ICN::EVIL_CAMPAIGN_BUTTONS; + const int originalIcnId = isEvilInterface ? ICN::CAMPXTRE : ICN::CAMPXTRG; + const int buttonBackground = isEvilInterface ? ICN::STONEBAK_EVIL : ICN::STONEBAK; + + // The evil buttons' released state are 2 pixels wider. + const int offsetEvilX = isEvilInterface ? 2 : 0; + // remove embedded shadows so that we can generate shadows with our own code later + for ( uint32_t i = 0; i < 8; i += 2 ) { + // released + const fheroes2::Sprite & originalReleased = fheroes2::AGG::GetICN( originalIcnId, i ); + + fheroes2::Sprite & released = _icnVsSprite[id][i]; + released.resize( originalReleased.width() - 6 + offsetEvilX, originalReleased.height() - 8 ); + released.reset(); - Copy( originalReleased, 6 - offsetEvilX, 0, released, 0, 0, originalReleased.width() - 1, originalReleased.height() - 8 ); + Copy( originalReleased, 6 - offsetEvilX, 0, released, 0, 0, originalReleased.width() - 1, originalReleased.height() - 8 ); - // pressed - const Sprite & originalPressed = GetICN( originalIcnId, i + 1 ); + // pressed + const fheroes2::Sprite & originalPressed = fheroes2::AGG::GetICN( originalIcnId, i + 1 ); - Sprite & pressed = _icnVsSprite[id][i + 1]; - pressed.resize( originalPressed.width(), originalPressed.height() ); - pressed.reset(); + fheroes2::Sprite & pressed = _icnVsSprite[id][i + 1]; + pressed.resize( originalPressed.width(), originalPressed.height() ); + pressed.reset(); - Copy( originalPressed, 0, 1, pressed, 0, 1, originalPressed.width() - 1, originalPressed.height() ); - setButtonCornersTransparent( released ); - fheroes2::makeTransparentBackground( released, pressed, buttonBackground ); - } - // Generate the DIFFICULTY button because it is not present in the original resources - fheroes2::getTextAdaptedButton( _icnVsSprite[id][8], _icnVsSprite[id][9], gettext_noop( "DIFFICULTY" ), - isEvilInterface ? ICN::EMPTY_EVIL_BUTTON : ICN::EMPTY_GOOD_BUTTON, buttonBackground ); - break; + Copy( originalPressed, 0, 1, pressed, 0, 1, originalPressed.width() - 1, originalPressed.height() ); + setButtonCornersTransparent( released ); + fheroes2::makeTransparentBackground( released, pressed, buttonBackground ); } - createCampaignButtonSet( id, { gettext_noop( "VIEW INTRO" ), gettext_noop( "RESTART" ), gettext_noop( "OKAY" ), gettext_noop( "CANCEL" ), - gettext_noop( "DIFFICULTY" ) } ); - + // Generate the DIFFICULTY button because it is not present in the original resources + fheroes2::getTextAdaptedButton( _icnVsSprite[id][8], _icnVsSprite[id][9], gettext_noop( "DIFFICULTY" ), + isEvilInterface ? ICN::EMPTY_EVIL_BUTTON : ICN::EMPTY_GOOD_BUTTON, buttonBackground ); break; } - case ICN::POL_CAMPAIGN_BUTTONS: { - _icnVsSprite[id].resize( 10 ); - - const int baseIcnId = ICN::X_CMPBTN; - - if ( useOriginalResources() ) { - for ( uint32_t i = 0; i < 8; i += 2 ) { - // released - const Sprite & originalReleased = GetICN( baseIcnId, i ); - - Sprite & released = _icnVsSprite[id][i]; - released.resize( originalReleased.width() + 1, originalReleased.height() + 1 ); - released.reset(); - - Copy( originalReleased, 0, 0, released, 1, 0, originalReleased.width(), originalReleased.height() ); - const Sprite & originalPressed = GetICN( baseIcnId, i + 1 ); - // the released state is missing the darker borders of the pressed state - Copy( originalPressed, 0, 0, released, 0, 1, 1, originalPressed.height() ); - Copy( originalPressed, 0, 2, released, 0, originalPressed.height() - 1, 1, 2 ); - Copy( originalPressed, 1, originalPressed.height() - 1, released, 1, originalPressed.height(), originalPressed.width(), 1 ); - Copy( originalPressed, 0, 2, released, 1, originalPressed.height(), 1, 1 ); - Copy( originalReleased, 0, 2, released, 1, originalPressed.height() - 2, 1, 1 ); - Copy( originalReleased, 0, 2, released, 2, originalPressed.height() - 1, 1, 1 ); - Copy( originalReleased, 0, 2, released, 1, originalPressed.height() - 1, 1, 1 ); - Copy( originalReleased, 1, 2, released, 2, originalPressed.height() - 2, 1, 1 ); - - // pressed state - Sprite & pressed = _icnVsSprite[id][i + 1]; - pressed.resize( originalPressed.width() + 1, originalPressed.height() + 1 ); - pressed.reset(); - - Copy( originalPressed, 0, 0, pressed, 0, 1, originalPressed.width(), originalPressed.height() ); - // pressed state has incomplete lower left corner - Copy( originalPressed, 0, 2, pressed, 0, originalPressed.height() - 1, 1, 2 ); - Copy( originalPressed, 0, 2, pressed, 1, originalPressed.height(), 1, 1 ); - Copy( originalPressed, 1, 2, pressed, 1, originalPressed.height() - 1, 1, 1 ); - _icnVsSprite[id][i + 1].setPosition( 0, 0 ); - - fheroes2::makeTransparentBackground( released, pressed, ICN::STONEBAK_SMALL_POL ); - } - // generate the DIFFICULTY button as it is not present in the original resources - fheroes2::getTextAdaptedButton( _icnVsSprite[id][8], _icnVsSprite[id][9], gettext_noop( "DIFFICULTY" ), ICN::EMPTY_POL_BUTTON, - ICN::STONEBAK_SMALL_POL ); - break; - } - createCampaignButtonSet( id, { gettext_noop( "VIEW INTRO" ), gettext_noop( "RESTART" ), gettext_noop( "OKAY" ), gettext_noop( "CANCEL" ), - gettext_noop( "DIFFICULTY" ) } ); + createCampaignButtonSet( id, { gettext_noop( "VIEW INTRO" ), gettext_noop( "RESTART" ), gettext_noop( "OKAY" ), gettext_noop( "CANCEL" ), + gettext_noop( "DIFFICULTY" ) } ); - break; - } - case ICN::BUTTON_SMALL_MIN_GOOD: { - _icnVsSprite[id].resize( 2 ); + break; + } + case ICN::POL_CAMPAIGN_BUTTONS: { + _icnVsSprite[id].resize( 10 ); - if ( useOriginalResources() ) { - // Write the "MIN" text on original assets ICNs. - for ( int32_t i = 0; i < static_cast( _icnVsSprite[id].size() ); ++i ) { - _icnVsSprite[id][i] = GetICN( ICN::BUTTON_SMALL_MAX_GOOD, 0 + i ); + const int baseIcnId = ICN::X_CMPBTN; - // Clean the button text. - Blit( GetICN( ICN::SYSTEM, 11 + i ), 10 - i, 4 + i, _icnVsSprite[id][i], 6 - i, 4 + i, 50, 16 ); - } + if ( useOriginalResources() ) { + for ( uint32_t i = 0; i < 8; i += 2 ) { + // released + const fheroes2::Sprite & originalReleased = fheroes2::AGG::GetICN( baseIcnId, i ); - renderTextOnButton( _icnVsSprite[id][0], _icnVsSprite[id][1], gettext_noop( "MIN" ), { 6, 5 }, { 5, 6 }, { 52, 16 }, fheroes2::FontColor::WHITE ); + fheroes2::Sprite & released = _icnVsSprite[id][i]; + released.resize( originalReleased.width() + 1, originalReleased.height() + 1 ); + released.reset(); - break; - } + Copy( originalReleased, 0, 0, released, 1, 0, originalReleased.width(), originalReleased.height() ); + const fheroes2::Sprite & originalPressed = fheroes2::AGG::GetICN( baseIcnId, i + 1 ); + // the released state is missing the darker borders of the pressed state + Copy( originalPressed, 0, 0, released, 0, 1, 1, originalPressed.height() ); + Copy( originalPressed, 0, 2, released, 0, originalPressed.height() - 1, 1, 2 ); + Copy( originalPressed, 1, originalPressed.height() - 1, released, 1, originalPressed.height(), originalPressed.width(), 1 ); + Copy( originalPressed, 0, 2, released, 1, originalPressed.height(), 1, 1 ); + Copy( originalReleased, 0, 2, released, 1, originalPressed.height() - 2, 1, 1 ); + Copy( originalReleased, 0, 2, released, 2, originalPressed.height() - 1, 1, 1 ); + Copy( originalReleased, 0, 2, released, 1, originalPressed.height() - 1, 1, 1 ); + Copy( originalReleased, 1, 2, released, 2, originalPressed.height() - 2, 1, 1 ); + + // pressed state + fheroes2::Sprite & pressed = _icnVsSprite[id][i + 1]; + pressed.resize( originalPressed.width() + 1, originalPressed.height() + 1 ); + pressed.reset(); - createNormalButton( _icnVsSprite[id][0], _icnVsSprite[id][1], 61, gettext_noop( "MIN" ), false ); + Copy( originalPressed, 0, 0, pressed, 0, 1, originalPressed.width(), originalPressed.height() ); + // pressed state has incomplete lower left corner + Copy( originalPressed, 0, 2, pressed, 0, originalPressed.height() - 1, 1, 2 ); + Copy( originalPressed, 0, 2, pressed, 1, originalPressed.height(), 1, 1 ); + Copy( originalPressed, 1, 2, pressed, 1, originalPressed.height() - 1, 1, 1 ); + _icnVsSprite[id][i + 1].setPosition( 0, 0 ); + fheroes2::makeTransparentBackground( released, pressed, ICN::STONEBAK_SMALL_POL ); + } + // generate the DIFFICULTY button as it is not present in the original resources + fheroes2::getTextAdaptedButton( _icnVsSprite[id][8], _icnVsSprite[id][9], gettext_noop( "DIFFICULTY" ), ICN::EMPTY_POL_BUTTON, ICN::STONEBAK_SMALL_POL ); break; } - case ICN::BUTTON_SMALL_MIN_EVIL: { - _icnVsSprite[id].resize( 2 ); + createCampaignButtonSet( id, { gettext_noop( "VIEW INTRO" ), gettext_noop( "RESTART" ), gettext_noop( "OKAY" ), gettext_noop( "CANCEL" ), + gettext_noop( "DIFFICULTY" ) } ); + + break; + } + case ICN::BUTTON_SMALL_MIN_GOOD: { + _icnVsSprite[id].resize( 2 ); + + if ( useOriginalResources() ) { + // Write the "MIN" text on original assets ICNs. + for ( int32_t i = 0; i < static_cast( _icnVsSprite[id].size() ); ++i ) { + _icnVsSprite[id][i] = fheroes2::AGG::GetICN( ICN::BUTTON_SMALL_MAX_GOOD, 0 + i ); - createNormalButton( _icnVsSprite[id][0], _icnVsSprite[id][1], 61, gettext_noop( "MIN" ), true ); + // Clean the button text. + Blit( fheroes2::AGG::GetICN( ICN::SYSTEM, 11 + i ), 10 - i, 4 + i, _icnVsSprite[id][i], 6 - i, 4 + i, 50, 16 ); + } + + renderTextOnButton( _icnVsSprite[id][0], _icnVsSprite[id][1], gettext_noop( "MIN" ), { 6, 5 }, { 5, 6 }, { 52, 16 }, fheroes2::FontColor::WHITE ); break; } - case ICN::BUTTON_SMALL_MAX_GOOD: { - _icnVsSprite[id].resize( 2 ); - if ( useOriginalResources() ) { - // The original assets ICN contains button with shadow. We crop only the button. - _icnVsSprite[id][0] = fheroes2::Crop( GetICN( ICN::RECRUIT, 4 ), 5, 0, 60, 25 ); - _icnVsSprite[id][1] = fheroes2::Crop( GetICN( ICN::RECRUIT, 5 ), 5, 0, 60, 25 ); + createNormalButton( _icnVsSprite[id][0], _icnVsSprite[id][1], 61, gettext_noop( "MIN" ), false ); - // To properly generate shadows and Blit the button we need to make some pixels transparent. - for ( fheroes2::Sprite & image : _icnVsSprite[id] ) { - setButtonCornersTransparent( image ); - } + break; + } + case ICN::BUTTON_SMALL_MIN_EVIL: { + _icnVsSprite[id].resize( 2 ); - break; - } + createNormalButton( _icnVsSprite[id][0], _icnVsSprite[id][1], 61, gettext_noop( "MIN" ), true ); - createNormalButton( _icnVsSprite[id][0], _icnVsSprite[id][1], 61, gettext_noop( "MAX" ), false ); + break; + } + case ICN::BUTTON_SMALL_MAX_GOOD: { + _icnVsSprite[id].resize( 2 ); - break; - } - case ICN::BUTTON_SMALL_MAX_EVIL: { - _icnVsSprite[id].resize( 2 ); + if ( useOriginalResources() ) { + // The original assets ICN contains button with shadow. We crop only the button. + _icnVsSprite[id][0] = fheroes2::Crop( fheroes2::AGG::GetICN( ICN::RECRUIT, 4 ), 5, 0, 60, 25 ); + _icnVsSprite[id][1] = fheroes2::Crop( fheroes2::AGG::GetICN( ICN::RECRUIT, 5 ), 5, 0, 60, 25 ); - createNormalButton( _icnVsSprite[id][0], _icnVsSprite[id][1], 61, gettext_noop( "MAX" ), true ); + // To properly generate shadows and Blit the button we need to make some pixels transparent. + for ( fheroes2::Sprite & image : _icnVsSprite[id] ) { + setButtonCornersTransparent( image ); + } break; } - case ICN::BUTTON_EXIT_GOOD: { - _icnVsSprite[id].resize( 2 ); - fheroes2::getTextAdaptedButton( _icnVsSprite[id][0], _icnVsSprite[id][1], gettext_noop( "EXIT" ), ICN::EMPTY_GOOD_BUTTON, ICN::STONEBAK ); + createNormalButton( _icnVsSprite[id][0], _icnVsSprite[id][1], 61, gettext_noop( "MAX" ), false ); - break; - } - case ICN::BUTTON_RESET_GOOD: { - _icnVsSprite[id].resize( 2 ); + break; + } + case ICN::BUTTON_SMALL_MAX_EVIL: { + _icnVsSprite[id].resize( 2 ); - getTextAdaptedButton( _icnVsSprite[id][0], _icnVsSprite[id][1], gettext_noop( "RESET" ), ICN::EMPTY_GOOD_BUTTON, ICN::STONEBAK ); + createNormalButton( _icnVsSprite[id][0], _icnVsSprite[id][1], 61, gettext_noop( "MAX" ), true ); - break; - } - case ICN::BUTTON_START_GOOD: { - _icnVsSprite[id].resize( 2 ); + break; + } + case ICN::BUTTON_EXIT_GOOD: { + _icnVsSprite[id].resize( 2 ); - getTextAdaptedButton( _icnVsSprite[id][0], _icnVsSprite[id][1], gettext_noop( "START" ), ICN::EMPTY_GOOD_BUTTON, ICN::STONEBAK ); + fheroes2::getTextAdaptedButton( _icnVsSprite[id][0], _icnVsSprite[id][1], gettext_noop( "EXIT" ), ICN::EMPTY_GOOD_BUTTON, ICN::STONEBAK ); - break; - } - case ICN::BUTTON_CASTLE_GOOD: - case ICN::BUTTON_CASTLE_EVIL: { - _icnVsSprite[id].resize( 2 ); + break; + } + case ICN::BUTTON_RESET_GOOD: { + _icnVsSprite[id].resize( 2 ); - createNormalButton( _icnVsSprite[id][0], _icnVsSprite[id][1], 80, gettext_noop( "CASTLE" ), id == ICN::BUTTON_CASTLE_EVIL ); + getTextAdaptedButton( _icnVsSprite[id][0], _icnVsSprite[id][1], gettext_noop( "RESET" ), ICN::EMPTY_GOOD_BUTTON, ICN::STONEBAK ); - break; - } - case ICN::BUTTON_TOWN_GOOD: - case ICN::BUTTON_TOWN_EVIL: { - _icnVsSprite[id].resize( 2 ); + break; + } + case ICN::BUTTON_START_GOOD: { + _icnVsSprite[id].resize( 2 ); - createNormalButton( _icnVsSprite[id][0], _icnVsSprite[id][1], 80, gettext_noop( "TOWN" ), id == ICN::BUTTON_TOWN_EVIL ); + getTextAdaptedButton( _icnVsSprite[id][0], _icnVsSprite[id][1], gettext_noop( "START" ), ICN::EMPTY_GOOD_BUTTON, ICN::STONEBAK ); - break; - } - case ICN::BUTTON_RESTRICT_GOOD: - case ICN::BUTTON_RESTRICT_EVIL: { - _icnVsSprite[id].resize( 2 ); + break; + } + case ICN::BUTTON_CASTLE_GOOD: + case ICN::BUTTON_CASTLE_EVIL: { + _icnVsSprite[id].resize( 2 ); - getTextAdaptedButton( _icnVsSprite[id][0], _icnVsSprite[id][1], gettext_noop( "RESTRICT" ), - id == ICN::BUTTON_RESTRICT_EVIL ? ICN::EMPTY_EVIL_BUTTON : ICN::EMPTY_GOOD_BUTTON, - id == ICN::BUTTON_RESTRICT_EVIL ? ICN::STONEBAK_EVIL : ICN::STONEBAK ); + createNormalButton( _icnVsSprite[id][0], _icnVsSprite[id][1], 80, gettext_noop( "CASTLE" ), id == ICN::BUTTON_CASTLE_EVIL ); - break; - } - case ICN::UNIFORM_EVIL_MAX_BUTTON: - case ICN::UNIFORM_EVIL_MIN_BUTTON: - case ICN::UNIFORM_GOOD_MAX_BUTTON: - case ICN::UNIFORM_GOOD_MIN_BUTTON: { - _icnVsSprite[id].resize( 2 ); + break; + } + case ICN::BUTTON_TOWN_GOOD: + case ICN::BUTTON_TOWN_EVIL: { + _icnVsSprite[id].resize( 2 ); - const bool isEvilInterface = ( id == ICN::UNIFORM_EVIL_MAX_BUTTON || id == ICN::UNIFORM_EVIL_MIN_BUTTON ); - const int baseIcnId = isEvilInterface ? ICN::SYSTEME : ICN::SYSTEM; + createNormalButton( _icnVsSprite[id][0], _icnVsSprite[id][1], 80, gettext_noop( "TOWN" ), id == ICN::BUTTON_TOWN_EVIL ); - for ( int32_t i = 0; i < static_cast( _icnVsSprite[id].size() ); ++i ) { - Sprite & out = _icnVsSprite[id][i]; + break; + } + case ICN::BUTTON_RESTRICT_GOOD: + case ICN::BUTTON_RESTRICT_EVIL: { + _icnVsSprite[id].resize( 2 ); - const Sprite & originalButton = GetICN( ICN::RECRUIT, 4 + i ); - out.resize( originalButton.width() + 4, originalButton.height() - 5 + i ); - out.reset(); + getTextAdaptedButton( _icnVsSprite[id][0], _icnVsSprite[id][1], gettext_noop( "RESTRICT" ), + id == ICN::BUTTON_RESTRICT_EVIL ? ICN::EMPTY_EVIL_BUTTON : ICN::EMPTY_GOOD_BUTTON, + id == ICN::BUTTON_RESTRICT_EVIL ? ICN::STONEBAK_EVIL : ICN::STONEBAK ); + + break; + } + case ICN::UNIFORM_EVIL_MAX_BUTTON: + case ICN::UNIFORM_EVIL_MIN_BUTTON: + case ICN::UNIFORM_GOOD_MAX_BUTTON: + case ICN::UNIFORM_GOOD_MIN_BUTTON: { + _icnVsSprite[id].resize( 2 ); - const Sprite & emptyButton = GetICN( baseIcnId, 11 + i ); + const bool isEvilInterface = ( id == ICN::UNIFORM_EVIL_MAX_BUTTON || id == ICN::UNIFORM_EVIL_MIN_BUTTON ); + const int baseIcnId = isEvilInterface ? ICN::SYSTEME : ICN::SYSTEM; - // Copy left main body of button. - fheroes2::Copy( emptyButton, 0, 0, out, 0, 0, originalButton.width() - 3 + i, originalButton.height() - 5 + i ); + for ( int32_t i = 0; i < static_cast( _icnVsSprite[id].size() ); ++i ) { + fheroes2::Sprite & out = _icnVsSprite[id][i]; - // Copy terminating right margin of the button. - fheroes2::Copy( emptyButton, 89 + i, 0, out, 63 + i, 0, 7 - i, originalButton.height() - i ); - } + const fheroes2::Sprite & originalButton = fheroes2::AGG::GetICN( ICN::RECRUIT, 4 + i ); + out.resize( originalButton.width() + 4, originalButton.height() - 5 + i ); + out.reset(); - const char * text( ( id == ICN::UNIFORM_GOOD_MIN_BUTTON || id == ICN::UNIFORM_EVIL_MIN_BUTTON ) ? "MIN" : "MAX" ); + const fheroes2::Sprite & emptyButton = fheroes2::AGG::GetICN( baseIcnId, 11 + i ); - const fheroes2::FontColor buttonFontColor = isEvilInterface ? fheroes2::FontColor::GRAY : fheroes2::FontColor::WHITE; - renderTextOnButton( _icnVsSprite[id][0], _icnVsSprite[id][1], gettext_noop( text ), { 6, 5 }, { 4, 6 }, { 62, 16 }, buttonFontColor ); + // Copy left main body of button. + fheroes2::Copy( emptyButton, 0, 0, out, 0, 0, originalButton.width() - 3 + i, originalButton.height() - 5 + i ); - break; + // Copy terminating right margin of the button. + fheroes2::Copy( emptyButton, 89 + i, 0, out, 63 + i, 0, 7 - i, originalButton.height() - i ); } - case ICN::UNIFORM_GOOD_OKAY_BUTTON: - case ICN::UNIFORM_EVIL_OKAY_BUTTON: { - _icnVsSprite[id].resize( 2 ); - const bool isEvilInterface = ( id == ICN::UNIFORM_EVIL_OKAY_BUTTON ); - const int baseIcnId = isEvilInterface ? ICN::SYSTEME : ICN::SYSTEM; + const char * text( ( id == ICN::UNIFORM_GOOD_MIN_BUTTON || id == ICN::UNIFORM_EVIL_MIN_BUTTON ) ? "MIN" : "MAX" ); - if ( useOriginalResources() ) { - _icnVsSprite[id][0] = GetICN( baseIcnId, 1 ); - _icnVsSprite[id][1] = GetICN( baseIcnId, 2 ); - break; - } + const fheroes2::FontColor buttonFontColor = isEvilInterface ? fheroes2::FontColor::GRAY : fheroes2::FontColor::WHITE; + renderTextOnButton( _icnVsSprite[id][0], _icnVsSprite[id][1], gettext_noop( text ), { 6, 5 }, { 4, 6 }, { 62, 16 }, buttonFontColor ); - for ( int32_t i = 0; i < static_cast( _icnVsSprite[id].size() ); ++i ) { - Sprite & out = _icnVsSprite[id][i]; - out = GetICN( baseIcnId, 11 + i ); - } + break; + } + case ICN::UNIFORM_GOOD_OKAY_BUTTON: + case ICN::UNIFORM_EVIL_OKAY_BUTTON: { + _icnVsSprite[id].resize( 2 ); - const fheroes2::FontColor buttonFontColor = isEvilInterface ? fheroes2::FontColor::GRAY : fheroes2::FontColor::WHITE; - renderTextOnButton( _icnVsSprite[id][0], _icnVsSprite[id][1], gettext_noop( "OKAY" ), { 6, 5 }, { 5, 6 }, { 86, 16 }, buttonFontColor ); + const bool isEvilInterface = ( id == ICN::UNIFORM_EVIL_OKAY_BUTTON ); + const int baseIcnId = isEvilInterface ? ICN::SYSTEME : ICN::SYSTEM; + if ( useOriginalResources() ) { + _icnVsSprite[id][0] = fheroes2::AGG::GetICN( baseIcnId, 1 ); + _icnVsSprite[id][1] = fheroes2::AGG::GetICN( baseIcnId, 2 ); break; } - case ICN::UNIFORM_GOOD_CANCEL_BUTTON: - case ICN::UNIFORM_EVIL_CANCEL_BUTTON: { - _icnVsSprite[id].resize( 2 ); - const bool isEvilInterface = ( id == ICN::UNIFORM_EVIL_CANCEL_BUTTON ); + for ( int32_t i = 0; i < static_cast( _icnVsSprite[id].size() ); ++i ) { + fheroes2::Sprite & out = _icnVsSprite[id][i]; + out = fheroes2::AGG::GetICN( baseIcnId, 11 + i ); + } - if ( useOriginalResources() ) { - const int baseIcnId = isEvilInterface ? ICN::SYSTEME : ICN::SYSTEM; - _icnVsSprite[id][0] = GetICN( baseIcnId, 3 ); - _icnVsSprite[id][1] = GetICN( baseIcnId, 4 ); - break; - } + const fheroes2::FontColor buttonFontColor = isEvilInterface ? fheroes2::FontColor::GRAY : fheroes2::FontColor::WHITE; + renderTextOnButton( _icnVsSprite[id][0], _icnVsSprite[id][1], gettext_noop( "OKAY" ), { 6, 5 }, { 5, 6 }, { 86, 16 }, buttonFontColor ); + + break; + } + case ICN::UNIFORM_GOOD_CANCEL_BUTTON: + case ICN::UNIFORM_EVIL_CANCEL_BUTTON: { + _icnVsSprite[id].resize( 2 ); - getTextAdaptedButton( _icnVsSprite[id][0], _icnVsSprite[id][1], gettext_noop( "CANCEL" ), - isEvilInterface ? ICN::EMPTY_EVIL_BUTTON : ICN::EMPTY_GOOD_BUTTON, isEvilInterface ? ICN::UNIFORMBAK_EVIL : ICN::UNIFORMBAK_GOOD ); + const bool isEvilInterface = ( id == ICN::UNIFORM_EVIL_CANCEL_BUTTON ); + if ( useOriginalResources() ) { + const int baseIcnId = isEvilInterface ? ICN::SYSTEME : ICN::SYSTEM; + _icnVsSprite[id][0] = fheroes2::AGG::GetICN( baseIcnId, 3 ); + _icnVsSprite[id][1] = fheroes2::AGG::GetICN( baseIcnId, 4 ); break; } - case ICN::UNIFORM_GOOD_EXIT_BUTTON: - case ICN::UNIFORM_EVIL_EXIT_BUTTON: { - _icnVsSprite[id].resize( 2 ); - const bool isEvilInterface = ( id == ICN::UNIFORM_EVIL_EXIT_BUTTON ); - const int baseIcnId = isEvilInterface ? ICN::TRADPOSE : ICN::TRADPOST; + getTextAdaptedButton( _icnVsSprite[id][0], _icnVsSprite[id][1], gettext_noop( "CANCEL" ), isEvilInterface ? ICN::EMPTY_EVIL_BUTTON : ICN::EMPTY_GOOD_BUTTON, + isEvilInterface ? ICN::UNIFORMBAK_EVIL : ICN::UNIFORMBAK_GOOD ); - if ( useOriginalResources() ) { - _icnVsSprite[id][0] = GetICN( baseIcnId, 17 ); - _icnVsSprite[id][1] = GetICN( baseIcnId, 18 ); - break; - } + break; + } + case ICN::UNIFORM_GOOD_EXIT_BUTTON: + case ICN::UNIFORM_EVIL_EXIT_BUTTON: { + _icnVsSprite[id].resize( 2 ); - getTextAdaptedButton( _icnVsSprite[id][0], _icnVsSprite[id][1], gettext_noop( "EXIT" ), isEvilInterface ? ICN::EMPTY_EVIL_BUTTON : ICN::EMPTY_GOOD_BUTTON, - isEvilInterface ? ICN::UNIFORMBAK_EVIL : ICN::UNIFORMBAK_GOOD ); + const bool isEvilInterface = ( id == ICN::UNIFORM_EVIL_EXIT_BUTTON ); + const int baseIcnId = isEvilInterface ? ICN::TRADPOSE : ICN::TRADPOST; + if ( useOriginalResources() ) { + _icnVsSprite[id][0] = fheroes2::AGG::GetICN( baseIcnId, 17 ); + _icnVsSprite[id][1] = fheroes2::AGG::GetICN( baseIcnId, 18 ); break; } - case ICN::BUTTON_VERTICAL_DISMISS: { - _icnVsSprite[id].resize( 2 ); - if ( useOriginalResources() ) { - // The original DISMISS button has a broken transform layer and thinner pressed state than released state, - // so we use our empty vertical button as a template - _icnVsSprite[id][0] = GetICN( ICN::EMPTY_VERTICAL_GOOD_BUTTON, 0 ); - _icnVsSprite[id][1] = GetICN( ICN::EMPTY_VERTICAL_GOOD_BUTTON, 1 ); + getTextAdaptedButton( _icnVsSprite[id][0], _icnVsSprite[id][1], gettext_noop( "EXIT" ), isEvilInterface ? ICN::EMPTY_EVIL_BUTTON : ICN::EMPTY_GOOD_BUTTON, + isEvilInterface ? ICN::UNIFORMBAK_EVIL : ICN::UNIFORMBAK_GOOD ); - // Copy the DISMISS text back from the original button - const Sprite & originalReleased = GetICN( ICN::HSBTNS, 0 ); - const Sprite & originalPressed = GetICN( ICN::HSBTNS, 1 ); + break; + } + case ICN::BUTTON_VERTICAL_DISMISS: { + _icnVsSprite[id].resize( 2 ); - Copy( originalReleased, 9, 2, _icnVsSprite[id][0], 5, 2, 21, 112 ); - Copy( originalPressed, 9, 5, _icnVsSprite[id][1], 4, 5, 19, 111 ); + if ( useOriginalResources() ) { + // The original DISMISS button has a broken transform layer and thinner pressed state than released state, + // so we use our empty vertical button as a template + _icnVsSprite[id][0] = fheroes2::AGG::GetICN( ICN::EMPTY_VERTICAL_GOOD_BUTTON, 0 ); + _icnVsSprite[id][1] = fheroes2::AGG::GetICN( ICN::EMPTY_VERTICAL_GOOD_BUTTON, 1 ); - fheroes2::makeTransparentBackground( _icnVsSprite[id][0], _icnVsSprite[id][1], ICN::REDBAK_SMALL_VERTICAL ); + // Copy the DISMISS text back from the original button + const fheroes2::Sprite & originalReleased = fheroes2::AGG::GetICN( ICN::HSBTNS, 0 ); + const fheroes2::Sprite & originalPressed = fheroes2::AGG::GetICN( ICN::HSBTNS, 1 ); - break; - } + Copy( originalReleased, 9, 2, _icnVsSprite[id][0], 5, 2, 21, 112 ); + Copy( originalPressed, 9, 5, _icnVsSprite[id][1], 4, 5, 19, 111 ); - // We need to temporarily remove the letter specific X offsets in the font because if not the letters will - // be off-centered when we are displaying one letter per line - const ButtonFontOffsetRestorer fontReleased( _icnVsSprite[ICN::BUTTON_GOOD_FONT_RELEASED], -1 ); - const ButtonFontOffsetRestorer fontPressed( _icnVsSprite[ICN::BUTTON_GOOD_FONT_PRESSED], -1 ); - getTextAdaptedButton( _icnVsSprite[id][0], _icnVsSprite[id][1], gettext_noop( "D\nI\nS\nM\nI\nS\nS" ), ICN::EMPTY_VERTICAL_GOOD_BUTTON, - ICN::REDBAK_SMALL_VERTICAL ); + fheroes2::makeTransparentBackground( _icnVsSprite[id][0], _icnVsSprite[id][1], ICN::REDBAK_SMALL_VERTICAL ); break; } - case ICN::BUTTON_VERTICAL_EXIT: { - _icnVsSprite[id].resize( 2 ); - if ( useOriginalResources() ) { - // The original EXIT button has a broken transform layer and we need to remove the shadows, - // so we use our empty vertical button as a template - _icnVsSprite[id][0] = GetICN( ICN::EMPTY_VERTICAL_GOOD_BUTTON, 0 ); - _icnVsSprite[id][1] = GetICN( ICN::EMPTY_VERTICAL_GOOD_BUTTON, 1 ); + // We need to temporarily remove the letter specific X offsets in the font because if not the letters will + // be off-centered when we are displaying one letter per line + const ButtonFontOffsetRestorer fontReleased( _icnVsSprite[ICN::BUTTON_GOOD_FONT_RELEASED], -1 ); + const ButtonFontOffsetRestorer fontPressed( _icnVsSprite[ICN::BUTTON_GOOD_FONT_PRESSED], -1 ); + getTextAdaptedButton( _icnVsSprite[id][0], _icnVsSprite[id][1], gettext_noop( "D\nI\nS\nM\nI\nS\nS" ), ICN::EMPTY_VERTICAL_GOOD_BUTTON, + ICN::REDBAK_SMALL_VERTICAL ); - // Copy the EXIT text back from the original button - const Sprite & originalReleased = GetICN( ICN::HSBTNS, 2 ); - const Sprite & originalPressed = GetICN( ICN::HSBTNS, 3 ); + break; + } + case ICN::BUTTON_VERTICAL_EXIT: { + _icnVsSprite[id].resize( 2 ); - Copy( originalReleased, 4, 2, _icnVsSprite[id][0], 5, 2, 21, 112 ); - Copy( originalPressed, 3, 5, _icnVsSprite[id][1], 4, 5, 19, 111 ); + if ( useOriginalResources() ) { + // The original EXIT button has a broken transform layer and we need to remove the shadows, + // so we use our empty vertical button as a template + _icnVsSprite[id][0] = fheroes2::AGG::GetICN( ICN::EMPTY_VERTICAL_GOOD_BUTTON, 0 ); + _icnVsSprite[id][1] = fheroes2::AGG::GetICN( ICN::EMPTY_VERTICAL_GOOD_BUTTON, 1 ); - fheroes2::makeTransparentBackground( _icnVsSprite[id][0], _icnVsSprite[id][1], ICN::REDBAK_SMALL_VERTICAL ); + // Copy the EXIT text back from the original button + const fheroes2::Sprite & originalReleased = fheroes2::AGG::GetICN( ICN::HSBTNS, 2 ); + const fheroes2::Sprite & originalPressed = fheroes2::AGG::GetICN( ICN::HSBTNS, 3 ); - break; - } + Copy( originalReleased, 4, 2, _icnVsSprite[id][0], 5, 2, 21, 112 ); + Copy( originalPressed, 3, 5, _icnVsSprite[id][1], 4, 5, 19, 111 ); - // We need to temporarily remove the letter specific X offsets in the font because if not the letters will - // be off-centered when we are displaying one letter per line - const ButtonFontOffsetRestorer fontReleased( _icnVsSprite[ICN::BUTTON_GOOD_FONT_RELEASED], -1 ); - const ButtonFontOffsetRestorer fontPressed( _icnVsSprite[ICN::BUTTON_GOOD_FONT_PRESSED], -1 ); - getTextAdaptedButton( _icnVsSprite[id][0], _icnVsSprite[id][1], gettext_noop( "E\nX\nI\nT" ), ICN::EMPTY_VERTICAL_GOOD_BUTTON, - ICN::REDBAK_SMALL_VERTICAL ); + fheroes2::makeTransparentBackground( _icnVsSprite[id][0], _icnVsSprite[id][1], ICN::REDBAK_SMALL_VERTICAL ); break; } - case ICN::BUTTON_VERTICAL_PATROL: { - _icnVsSprite[id].resize( 2 ); - // We need to temporarily remove the letter specific X offsets in the font because if not the letters will - // be off-centered when we are displaying one letter per line - const ButtonFontOffsetRestorer fontReleased( _icnVsSprite[ICN::BUTTON_GOOD_FONT_RELEASED], -1 ); - const ButtonFontOffsetRestorer fontPressed( _icnVsSprite[ICN::BUTTON_GOOD_FONT_PRESSED], -1 ); - getTextAdaptedButton( _icnVsSprite[id][0], _icnVsSprite[id][1], gettext_noop( "P\nA\nT\nR\nO\nL" ), ICN::EMPTY_VERTICAL_GOOD_BUTTON, - ICN::REDBAK_SMALL_VERTICAL ); + // We need to temporarily remove the letter specific X offsets in the font because if not the letters will + // be off-centered when we are displaying one letter per line + const ButtonFontOffsetRestorer fontReleased( _icnVsSprite[ICN::BUTTON_GOOD_FONT_RELEASED], -1 ); + const ButtonFontOffsetRestorer fontPressed( _icnVsSprite[ICN::BUTTON_GOOD_FONT_PRESSED], -1 ); + getTextAdaptedButton( _icnVsSprite[id][0], _icnVsSprite[id][1], gettext_noop( "E\nX\nI\nT" ), ICN::EMPTY_VERTICAL_GOOD_BUTTON, ICN::REDBAK_SMALL_VERTICAL ); - break; - } - - case ICN::BUTTON_HSCORES_VERTICAL_CAMPAIGN: - case ICN::BUTTON_HSCORES_VERTICAL_EXIT: - case ICN::BUTTON_HSCORES_VERTICAL_STANDARD: { - _icnVsSprite[id].resize( 2 ); - - const int originalID = ICN::HISCORE; - uint32_t originalICNIndex = 0; - if ( id == ICN::BUTTON_HSCORES_VERTICAL_STANDARD ) { - originalICNIndex = 2; - } - else if ( id == ICN::BUTTON_HSCORES_VERTICAL_EXIT ) { - originalICNIndex = 4; - } - else { - assert( id == ICN::BUTTON_HSCORES_VERTICAL_CAMPAIGN ); - } - - if ( useOriginalResources() ) { - _icnVsSprite[id][0] = GetICN( originalID, originalICNIndex ); - _icnVsSprite[id][1] = GetICN( originalID, originalICNIndex + 1 ); - break; - } - - for ( size_t i = 0; i < _icnVsSprite[id].size(); ++i ) { - const Sprite & originalButton = GetICN( originalID, originalICNIndex + static_cast( i ) ); - Sprite & out = _icnVsSprite[id][i]; + break; + } + case ICN::BUTTON_VERTICAL_PATROL: { + _icnVsSprite[id].resize( 2 ); - out = originalButton; - // Clean the button - Fill( out, 4 - static_cast( i ), 4 + static_cast( i ), 19, 123, getButtonFillingColor( i == 0 ) ); - } + // We need to temporarily remove the letter specific X offsets in the font because if not the letters will + // be off-centered when we are displaying one letter per line + const ButtonFontOffsetRestorer fontReleased( _icnVsSprite[ICN::BUTTON_GOOD_FONT_RELEASED], -1 ); + const ButtonFontOffsetRestorer fontPressed( _icnVsSprite[ICN::BUTTON_GOOD_FONT_PRESSED], -1 ); + getTextAdaptedButton( _icnVsSprite[id][0], _icnVsSprite[id][1], gettext_noop( "P\nA\nT\nR\nO\nL" ), ICN::EMPTY_VERTICAL_GOOD_BUTTON, + ICN::REDBAK_SMALL_VERTICAL ); - const char * buttonText; + break; + } - if ( id == ICN::BUTTON_HSCORES_VERTICAL_CAMPAIGN ) { - buttonText = gettext_noop( "C\nA\nM\nP\nA\nI\nG\nN" ); - } - else if ( id == ICN::BUTTON_HSCORES_VERTICAL_STANDARD ) { - buttonText = gettext_noop( "S\nT\nA\nN\nD\nA\nR\nD" ); - } - else { - buttonText = gettext_noop( "E\nX\nI\nT" ); - } + case ICN::BUTTON_HSCORES_VERTICAL_CAMPAIGN: + case ICN::BUTTON_HSCORES_VERTICAL_EXIT: + case ICN::BUTTON_HSCORES_VERTICAL_STANDARD: { + _icnVsSprite[id].resize( 2 ); - ButtonFontOffsetRestorer fontRestorerReleased( _icnVsSprite[ICN::BUTTON_GOOD_FONT_RELEASED], -1 ); - ButtonFontOffsetRestorer fontRestorerPressed( _icnVsSprite[ICN::BUTTON_GOOD_FONT_PRESSED], -1 ); - renderTextOnButton( _icnVsSprite[id][0], _icnVsSprite[id][1], buttonText, { 4, 4 }, { 3, 5 }, { 21, 124 }, fheroes2::FontColor::WHITE ); + const int originalID = ICN::HISCORE; + uint32_t originalICNIndex = 0; + if ( id == ICN::BUTTON_HSCORES_VERTICAL_STANDARD ) { + originalICNIndex = 2; + } + else if ( id == ICN::BUTTON_HSCORES_VERTICAL_EXIT ) { + originalICNIndex = 4; + } + else { + assert( id == ICN::BUTTON_HSCORES_VERTICAL_CAMPAIGN ); + } + if ( useOriginalResources() ) { + _icnVsSprite[id][0] = fheroes2::AGG::GetICN( originalID, originalICNIndex ); + _icnVsSprite[id][1] = fheroes2::AGG::GetICN( originalID, originalICNIndex + 1 ); break; } - case ICN::BUTTON_RUMORS_GOOD: - case ICN::BUTTON_RUMORS_EVIL: { - _icnVsSprite[id].resize( 2 ); - - const bool isEvilInterface = ( id == ICN::BUTTON_RUMORS_EVIL ); - getTextAdaptedButton( _icnVsSprite[id][0], _icnVsSprite[id][1], gettext_noop( "RUMORS" ), - isEvilInterface ? ICN::EMPTY_EVIL_BUTTON : ICN::EMPTY_GOOD_BUTTON, isEvilInterface ? ICN::STONEBAK_EVIL : ICN::STONEBAK ); + for ( size_t i = 0; i < _icnVsSprite[id].size(); ++i ) { + const fheroes2::Sprite & originalButton = fheroes2::AGG::GetICN( originalID, originalICNIndex + static_cast( i ) ); + fheroes2::Sprite & out = _icnVsSprite[id][i]; - break; + out = originalButton; + // Clean the button + Fill( out, 4 - static_cast( i ), 4 + static_cast( i ), 19, 123, getButtonFillingColor( i == 0 ) ); } - case ICN::BUTTON_EVENTS_GOOD: - case ICN::BUTTON_EVENTS_EVIL: { - _icnVsSprite[id].resize( 2 ); - - const bool isEvilInterface = ( id == ICN::BUTTON_EVENTS_EVIL ); - getTextAdaptedButton( _icnVsSprite[id][0], _icnVsSprite[id][1], gettext_noop( "EVENTS" ), - isEvilInterface ? ICN::EMPTY_EVIL_BUTTON : ICN::EMPTY_GOOD_BUTTON, isEvilInterface ? ICN::STONEBAK_EVIL : ICN::STONEBAK ); + const char * buttonText; - break; + if ( id == ICN::BUTTON_HSCORES_VERTICAL_CAMPAIGN ) { + buttonText = gettext_noop( "C\nA\nM\nP\nA\nI\nG\nN" ); } - default: - // You're calling this function for non-specified ICN id. Check your logic! - // Did you add a new image for one language without generating a default - // for other languages? - assert( 0 ); - break; + else if ( id == ICN::BUTTON_HSCORES_VERTICAL_STANDARD ) { + buttonText = gettext_noop( "S\nT\nA\nN\nD\nA\nR\nD" ); } - } - - bool generateGermanSpecificImages( const int id ) - { - switch ( id ) { - case ICN::BTNBATTLEONLY: - _icnVsSprite[id].resize( 2 ); - for ( int32_t i = 0; i < static_cast( _icnVsSprite[id].size() ); ++i ) { - Sprite & out = _icnVsSprite[id][i]; - out = GetICN( ICN::BTNNEWGM, 6 + i ); - // Clean the button - Fill( out, 25, 18, 88, 23, getButtonFillingColor( i == 0 ) ); - // Add 'K' - Blit( GetICN( ICN::BTNDCCFG, 4 + i ), 34 - i, 23, out, 40 - i, 23, 12, 14 ); - //'Add 'A' - Blit( GetICN( ICN::BTNNEWGM, 4 + i ), 56 - i, 23, out, 52 - i, 23, 13, 14 ); - Blit( out, 20, 20, out, 52 - i + 12, 25, 3, 3 ); - // Add 'M' - Blit( GetICN( ICN::BTNNEWGM, 4 + i ), 39 - i, 8, out, 65 - i, 23, 14, 14 ); - // Add 'F' - Blit( GetICN( ICN::BTNDCCFG, 4 + i ), 70 - i, 23, out, 87 - i, 23, 10, 14 ); - // Add 'P' - Blit( GetICN( ICN::BTNNEWGM, 4 + i ), 36 - i, 23, out, 78 - i, 23, 10, 14 ); - } - return true; - case ICN::BUTTON_SMALL_MIN_GOOD: - _icnVsSprite[id].resize( 2 ); - for ( int32_t i = 0; i < static_cast( _icnVsSprite[id].size() ); ++i ) { - Sprite & out = _icnVsSprite[id][i]; - out = GetICN( ICN::RECRUIT, 4 + i ); - // clean the button - Blit( GetICN( ICN::SYSTEM, 11 + i ), 10, 6 + i, out, 30 - 2 * i, 5 + i, 31, 15 ); - // add 'IN' - Copy( GetICN( ICN::APANEL, 4 + i ), 23 - i, 22 + i, out, 33 - i, 6 + i, 8, 14 ); // letter 'I' - Copy( GetICN( ICN::APANEL, 4 + i ), 31 - i, 22 + i, out, 44 - i, 6 + i, 17, 14 ); // letter 'N' - } - return true; - default: - break; + else { + buttonText = gettext_noop( "E\nX\nI\nT" ); } - return false; - } - bool generateFrenchSpecificImages( const int id ) - { - switch ( id ) { - case ICN::BTNBATTLEONLY: - _icnVsSprite[id].resize( 2 ); - for ( int32_t i = 0; i < static_cast( _icnVsSprite[id].size() ); ++i ) { - Sprite & out = _icnVsSprite[id][i]; - out = GetICN( ICN::BTNNEWGM, 6 + i ); - // Clean the button - Fill( out, 25, 18, 88, 23, getButtonFillingColor( i == 0 ) ); - - const int32_t secondLine = 28; - // Add 'MODE' - Blit( GetICN( ICN::BTNNEWGM, 4 + i ), 40 - i, 13, out, 45 - i, 13, 50, 15 ); - // Clean up 'MODE' - Blit( GetICN( ICN::BTNEMAIN, 0 + i ), 114 - i, 18, out, 94 - i, 18, 1, 10 ); - // Add 'BA' - Blit( GetICN( ICN::BTNBAUD, 2 + i ), 42 - i, 28, out, 28 - i, secondLine, 22, 15 ); - // Clean up 'BA' - Blit( GetICN( ICN::BTNBAUD, 2 + i ), 42 - i, 31, out, 39 - i, secondLine, 1, 1 ); - Blit( GetICN( ICN::BTNBAUD, 2 + i ), 39 - i, 31, out, 49 - i, secondLine + 4, 1, 2 ); - // Add 'T' - Blit( GetICN( ICN::BTNDC, 2 + i ), 89 - i, 21, out, 50 - i, secondLine, 12, 15 ); - // Clean up 'AT' - Blit( GetICN( ICN::BTNDC, 2 + i ), 89 - i, 18, out, 50 - i, secondLine, 1, 1 ); - Blit( GetICN( ICN::BTNDC, 2 + i ), 92 - ( 5 * i ), 27 - i, out, 49 - i, secondLine + 4 + i, 1, 3 ); - // Add 'AI'. - Blit( GetICN( ICN::BTNMP, 6 + i ), 56 - i, 13, out, 62 - i, secondLine, 18, 15 ); - // Clean up 'TA' - Blit( GetICN( ICN::BTNBAUD, 2 + i ), 51 - i, 40, out, 60 - i, secondLine + 12, 3, 3 ); - // Add 'LLE' - Blit( GetICN( ICN::BTNEMAIN, 0 + i ), 85 - i, 13, out, 81 - i, secondLine, 31, 15 ); - // Clean up "IL" - Blit( GetICN( ICN::BTNEMAIN, 0 + i ), 85 - i, 18, out, 81 - i, secondLine + 7, 1, 1 ); - Blit( GetICN( ICN::BTNEMAIN, 0 + i ), 94 - i, 17, out, 80 - i, secondLine + 4, 2, 2 ); - Blit( GetICN( ICN::BTNEMAIN, 0 + i ), 93 - i, 25, out, 79 - i, secondLine + 12, 3, 3 ); - Blit( GetICN( ICN::BTNDC, 4 + i ), 23 - i, 8, out, 79 - i, secondLine + 5, 1, 10 ); - Blit( GetICN( ICN::BTNMP, 6 + i ), 73 - i, 22, out, 79 - i, secondLine + 9, 1, 1 ); - } - return true; - case ICN::BTNGIFT_GOOD: - _icnVsSprite[id].resize( 2 ); - for ( int32_t i = 0; i < static_cast( _icnVsSprite[id].size() ); ++i ) { - Sprite & out = _icnVsSprite[id][i]; - out = GetICN( ICN::TRADPOST, 17 + i ); - // clean the button - Fill( out, 33, 5, 31, 16, getButtonFillingColor( i == 0 ) ); - - const int32_t offsetY = 5; - // Add 'D' - const int32_t offsetXD = 14; - Blit( GetICN( ICN::CPANEL, 4 + i ), 48 - i, 28 + i, out, offsetXD - i, offsetY + i, 10, 15 ); - // Clean up 'D' and restore button ornament - Blit( GetICN( ICN::CPANEL, 4 + i ), 48 - i, 36, out, offsetXD - 1 - i, offsetY + 4 + i, 1, 1 ); - Blit( GetICN( ICN::CPANEL, 4 + i ), 48 - i, 35, out, offsetXD - i, offsetY + 9 + i, 1, 2 ); - Blit( GetICN( ICN::CPANEL, 4 + i ), 48 - i, 35, out, offsetXD - 1 - i, offsetY + 13 + i, 1, 1 ); - Fill( out, offsetXD + 9 - i, offsetY + 13 + i, 1, 1, getButtonFillingColor( i == 0 ) ); - Blit( GetICN( ICN::TRADPOST, 17 + i ), offsetXD, offsetY, out, offsetXD, offsetY, 1, 1 ); - // Add 'O' - const int32_t offsetXO = 10; - Blit( GetICN( ICN::CAMPXTRG, i ), 40 - ( 7 * i ), 5 + i, out, offsetXD + offsetXO + 1 - i, offsetY + i, 13 - i, 15 ); - // Clean up 'DO' - Blit( GetICN( ICN::CPANEL, 4 + i ), 51 - i, 34, out, offsetXD + offsetXO - i, offsetY + 5, 2, 2 ); - Blit( GetICN( ICN::CPANEL, 4 + i ), 51 - i, 34, out, offsetXD + offsetXO - i, offsetY + 7, 1, 1 + i ); - Blit( GetICN( ICN::CPANEL, 4 + i ), 55 - i, 28 + i, out, offsetXD + 9 - i, offsetY + 2 + i, 3, 3 ); - Fill( out, offsetXD + 11 - i, offsetY + i, 2, 2, getButtonFillingColor( i == 0 ) ); - // Add 'N' - const int32_t offsetXN = 13; - Blit( GetICN( ICN::TRADPOST, 17 + i ), 50 - i, 5, out, offsetXD + offsetXO + offsetXN - i, offsetY, 14, 15 ); - // Clean up 'ON' - Fill( out, offsetXD + offsetXO + offsetXN, offsetY, 1, 1, getButtonFillingColor( i == 0 ) ); - Fill( out, offsetXD + offsetXO + offsetXN - i, offsetY + 9, 1, 1, getButtonFillingColor( i == 0 ) ); - // Add 'N' - Blit( GetICN( ICN::TRADPOST, 17 + i ), 50 - i, 5, out, offsetXD + 10 + offsetXN + offsetXN - i, offsetY, 14, 15 ); - // Clean up 'NN' - Fill( out, offsetXD + offsetXO + offsetXN + offsetXN - i, offsetY + 9, 1, 1, getButtonFillingColor( i == 0 ) ); - // Add 'ER' - Blit( GetICN( ICN::CAMPXTRG, 2 + i ), 75 - ( 8 * i ), 5, out, offsetXD + offsetXO + offsetXN + offsetXN + offsetXN - ( 2 * i ), offsetY, 23, 15 ); - // Restore button ornament - Blit( GetICN( ICN::TRADPOST, 17 + i ), offsetXD + offsetXO + offsetXN + offsetXN + offsetXN + 20, offsetY, out, - offsetXD + offsetXO + offsetXN + offsetXN + offsetXN + 20, offsetY, 1, 1 ); - Blit( GetICN( ICN::TRADPOST, 17 + i ), offsetXD + offsetXO + offsetXN + offsetXN + offsetXN + 21, offsetY + 1, out, - offsetXD + offsetXO + offsetXN + offsetXN + offsetXN + 21, offsetY + 1, 2, 3 ); - Blit( GetICN( ICN::TRADPOST, 17 + i ), offsetXD + offsetXO + offsetXN + offsetXN + offsetXN + 20, offsetY, out, - offsetXD + offsetXO + offsetXN + offsetXN + offsetXN + 21, offsetY + 4, 1, 1 ); - } - return true; - case ICN::BTNGIFT_EVIL: - _icnVsSprite[id].resize( 2 ); - for ( int32_t i = 0; i < static_cast( _icnVsSprite[id].size() ); ++i ) { - Sprite & out = _icnVsSprite[id][i]; - out = GetICN( ICN::TRADPOSE, 17 + i ); - // clean the button - Fill( out, 33, 5, 31, 16, getButtonFillingColor( i == 0, false ) ); - - const int32_t offsetY = 5; - // Add 'D' - const int32_t offsetXD = 14; - Blit( GetICN( ICN::CPANELE, 4 + i ), 48 - i, 28 + i, out, offsetXD - i, offsetY + i, 10, 15 ); - // Clean up 'D' and restore button ornament - Blit( GetICN( ICN::CPANELE, 4 + i ), 48 - i, 36, out, offsetXD - 1 - i, offsetY + 4 + i, 1, 1 ); - Blit( GetICN( ICN::CPANELE, 4 + i ), 48 - i, 35, out, offsetXD - i, offsetY + 9 + i, 1, 2 ); - Blit( GetICN( ICN::CPANELE, 4 + i ), 48 - i, 35, out, offsetXD - 1 - i, offsetY + 13 + i, 1, 1 ); - Fill( out, offsetXD + 9 - i, offsetY + 13 + i, 1, 1, getButtonFillingColor( i == 0, false ) ); - Blit( GetICN( ICN::TRADPOSE, 17 + i ), offsetXD, offsetY, out, offsetXD, offsetY, 1, 1 ); - Fill( out, offsetXD + 9 - i, offsetY + i, 1, 1, getButtonFillingColor( i == 0, false ) ); - // Add 'O' - const int32_t offsetXO = 10; - Blit( GetICN( ICN::APANELE, 4 + i ), 50 - i, 20 + i, out, offsetXD + offsetXO + 1 - i, offsetY + i, 13 - i, 14 ); - // Clean up 'DO' - Blit( GetICN( ICN::CPANELE, 4 + i ), 51 - i, 34, out, offsetXD + offsetXO - i, offsetY + 5, 2, 2 ); - Blit( GetICN( ICN::CPANELE, 4 + i ), 51 - i, 34, out, offsetXD + offsetXO - i, offsetY + 7, 1, 1 + i ); - Blit( GetICN( ICN::CPANELE, 4 + i ), 56 - i, 28 + i, out, offsetXD + 10 - i, offsetY + 2 + i, 1, 3 ); - Blit( GetICN( ICN::CPANELE, 4 + i ), 56 - i, 28 + i, out, offsetXD + 11 - i, offsetY + 3 + i, 1, 2 ); - Fill( out, offsetXD + 11 - i, offsetY + i, 3, 3, getButtonFillingColor( i == 0, false ) ); - Fill( out, offsetXD + 12 - i, offsetY + 3 + i, 1, 2, getButtonFillingColor( i == 0, false ) ); - // Add 'N' - const int32_t offsetXN = 13; - Blit( GetICN( ICN::TRADPOSE, 17 + i ), 50 - i, 5, out, offsetXD + offsetXO + offsetXN - i, offsetY, 14, 15 ); - // Clean up 'ON' - Fill( out, offsetXD + offsetXO + offsetXN - 1 - i, offsetY + 11 + i, 1, 3, getButtonFillingColor( i == 0, false ) ); - Fill( out, offsetXD + offsetXO + offsetXN - i, offsetY, 1, 1, getButtonFillingColor( i == 0, false ) ); - Fill( out, offsetXD + offsetXO + offsetXN - i, offsetY + 9, 1, 1, getButtonFillingColor( i == 0, false ) ); - // Add 'N' - Blit( GetICN( ICN::TRADPOSE, 17 + i ), 50 - i, 5, out, offsetXD + offsetXO + offsetXN + offsetXN - i, offsetY, 13, 15 ); - // Clean up 'NN' - Fill( out, offsetXD + offsetXO + offsetXN + offsetXN - i, offsetY + 9, 1, 1, getButtonFillingColor( i == 0, false ) ); - // Add 'ER' - Blit( GetICN( ICN::APANELE, 8 + i ), 66 - ( 3 * i ), 5 + ( 2 * i ), out, offsetXD + offsetXO + offsetXN + offsetXN + offsetXN - ( 2 * i ), - offsetY + ( 2 * i ), 23, 14 - i ); - // Clean up 'NE' - Blit( GetICN( ICN::TRADPOSE, 17 + i ), 50 - i, 5, out, offsetXD + offsetXO + offsetXN + offsetXN + offsetXN - i, offsetY, 2, 10 ); - Fill( out, offsetXD + offsetXO + offsetXN + offsetXN + offsetXN - ( 2 * i ), offsetY + 9 + i, 1 + i, 2 + i, getButtonFillingColor( i == 0, false ) ); - // Restore button ornament - Blit( GetICN( ICN::TRADPOSE, 17 + i ), offsetXD + offsetXO + offsetXN + offsetXN + offsetXN + 20, offsetY, out, - offsetXD + offsetXO + offsetXN + offsetXN + offsetXN + 20, offsetY, 1, 1 ); - Blit( GetICN( ICN::TRADPOSE, 17 + i ), offsetXD + offsetXO + offsetXN + offsetXN + offsetXN + 21, offsetY + 1, out, - offsetXD + offsetXO + offsetXN + offsetXN + offsetXN + 21, offsetY + 1, 2, 3 ); - Blit( GetICN( ICN::TRADPOSE, 17 + i ), offsetXD + offsetXO + offsetXN + offsetXN + offsetXN + 20, offsetY, out, - offsetXD + offsetXO + offsetXN + offsetXN + offsetXN + 21, offsetY + 4, 1, 1 ); - } - return true; - case ICN::BUTTON_SMALL_MIN_GOOD: - _icnVsSprite[id].resize( 2 ); - for ( int32_t i = 0; i < static_cast( _icnVsSprite[id].size() ); ++i ) { - Sprite & out = _icnVsSprite[id][i]; - out = GetICN( ICN::RECRUIT, 4 + i ); - // Clean the button and leave 'M' - Fill( out, 31 - 2 * i, 5 + i, 25, 15, getButtonFillingColor( i == 0 ) ); - Fill( out, 29 - 2 * i, 17 + i, 2, 2, getButtonFillingColor( i == 0 ) ); - // Add 'I' - Blit( GetICN( ICN::APANEL, 4 + i ), 25 - i, 19 + i, out, 32 - i, 4 + i, 7 - i, 15 ); - Blit( GetICN( ICN::RECRUIT, 4 + i ), 28 - i, 7 + i, out, 36 - i, 7 + i, 3, 9 ); - Fill( out, 37 - i, 16 + i, 2, 3, getButtonFillingColor( i == 0 ) ); - // Add 'N' - Blit( GetICN( ICN::TRADPOST, 17 + i ), 50 - i, 5, out, 41 - i, 5, 14, 15 ); - Fill( out, 41 - i, 5, 1, 1, getButtonFillingColor( i == 0 ) ); - Fill( out, 41 - i, 5 + 9, 1, 1, getButtonFillingColor( i == 0 ) ); - } - return true; - default: - break; - } - return false; - } + const ButtonFontOffsetRestorer fontRestorerReleased( _icnVsSprite[ICN::BUTTON_GOOD_FONT_RELEASED], -1 ); + const ButtonFontOffsetRestorer fontRestorerPressed( _icnVsSprite[ICN::BUTTON_GOOD_FONT_PRESSED], -1 ); + renderTextOnButton( _icnVsSprite[id][0], _icnVsSprite[id][1], buttonText, { 4, 4 }, { 3, 5 }, { 21, 124 }, fheroes2::FontColor::WHITE ); - bool generatePolishSpecificImages( const int id ) - { - switch ( id ) { - case ICN::BTNBATTLEONLY: - _icnVsSprite[id].resize( 2 ); - for ( int32_t i = 0; i < static_cast( _icnVsSprite[id].size() ); ++i ) { - Sprite & out = _icnVsSprite[id][i]; - out = GetICN( ICN::BTNNEWGM, 6 + i ); - // clean the button - Fill( out, 25, 18, 88, 23, getButtonFillingColor( i == 0 ) ); - const int32_t offsetX = 46; - const int32_t offsetY = 23; - // Add 'BI' - Blit( GetICN( ICN::BTNMCFG, 2 + i ), 58 - i, 29, out, offsetX - i, offsetY, 14, 11 ); - // Add 'T' - Blit( GetICN( ICN::BTNNEWGM, 0 + i ), 24 - i, 29, out, offsetX + 14 - i, offsetY, 9, 11 ); - // Add 'WA' - Blit( GetICN( ICN::BTNEMAIN, 0 + i ), 45 - i, 23, out, offsetX + 23 - i, offsetY, 24, 11 ); - // Add pixel to 'W' - Blit( GetICN( ICN::BTNEMAIN, 0 + i ), 47 - i, 23 + i, out, offsetX + 38 - i, offsetY + i, 1, 1 ); - } - return true; - default: - break; - } - return false; + break; } + case ICN::BUTTON_RUMORS_GOOD: + case ICN::BUTTON_RUMORS_EVIL: { + _icnVsSprite[id].resize( 2 ); - bool generateItalianSpecificImages( const int id ) - { - switch ( id ) { - case ICN::BTNBATTLEONLY: - _icnVsSprite[id].resize( 2 ); - for ( int32_t i = 0; i < static_cast( _icnVsSprite[id].size() ); ++i ) { - Sprite & out = _icnVsSprite[id][i]; - out = GetICN( ICN::BTNNEWGM, 6 + i ); - // clean the button - const uint8_t buttonFillingColor = getButtonFillingColor( i == 0 ); - Fill( out, 25, 18, 88, 23, buttonFillingColor ); - const int32_t offsetX = 16; - const int32_t offsetY = 21; - // Add 'B' - Blit( GetICN( ICN::BTNBAUD, 0 + i ), 42 - i, 28, out, offsetX - i, offsetY, 13, 15 ); - Fill( out, offsetX + 11, offsetY + 13, 1, 2, buttonFillingColor ); - // Add 'A' - Blit( GetICN( ICN::BTNNEWGM, 0 + i ), 80 - i, 28, out, offsetX + 13 - i, offsetY, 14, 15 ); - Fill( out, offsetX + 13 - i, offsetY + 3, 1, 4, buttonFillingColor ); - // Add 'T' - Blit( GetICN( ICN::BTNMP, 0 + i ), 74 - i, 5, out, offsetX + 27 - 2 * i, offsetY, 12, 15 ); - // Add 'T' - Blit( GetICN( ICN::BTNMP, 0 + i ), 74 - i, 5, out, offsetX + 39 - 2 * i, offsetY, 12, 15 ); - // Add 'A' - Blit( GetICN( ICN::BTNNEWGM, 0 + i ), 80 - i, 28, out, offsetX + 50 - i, offsetY, 14, 15 ); - Fill( out, offsetX + 65 - i, offsetY + 5, 1, 2, buttonFillingColor ); - Fill( out, offsetX + 65 - i, offsetY + 14, 1, 3, buttonFillingColor ); - Fill( out, offsetX + 50 - i, offsetY + 3, 1, 4, buttonFillingColor ); - // Add 'G' - Blit( GetICN( ICN::BTNNEWGM, 0 + i ), 44 - i, 12, out, offsetX + 65 - i, offsetY, 11, 15 ); - // Add 'L' - Blit( GetICN( ICN::BTNDC, 4 + i ), 77 - i, 21, out, offsetX + 77 - 2 * i, offsetY, 9, 15 ); - // Add 'I' - Blit( GetICN( ICN::BTNNEWGM, 0 + i ), 56 - i, 12, out, offsetX + 86 - i, offsetY, 7, 15 ); - // Add 'A' - Blit( GetICN( ICN::BTNNEWGM, 0 + i ), 80 - i, 28, out, offsetX + 93 - i, offsetY, 14, 15 ); - Fill( out, offsetX + 109 - i, offsetY + 5, 1, 2, buttonFillingColor ); - Fill( out, offsetX + 93 - i, offsetY + 3, 1, 4, buttonFillingColor ); - } - return true; - default: - break; - } - return false; + const bool isEvilInterface = ( id == ICN::BUTTON_RUMORS_EVIL ); + + getTextAdaptedButton( _icnVsSprite[id][0], _icnVsSprite[id][1], gettext_noop( "RUMORS" ), isEvilInterface ? ICN::EMPTY_EVIL_BUTTON : ICN::EMPTY_GOOD_BUTTON, + isEvilInterface ? ICN::STONEBAK_EVIL : ICN::STONEBAK ); + + break; } + case ICN::BUTTON_EVENTS_GOOD: + case ICN::BUTTON_EVENTS_EVIL: { + _icnVsSprite[id].resize( 2 ); - void generateLanguageSpecificImages( int id ) - { - assert( isLanguageDependentIcnId( id ) ); + const bool isEvilInterface = ( id == ICN::BUTTON_EVENTS_EVIL ); - const fheroes2::SupportedLanguage resourceLanguage = fheroes2::getResourceLanguage(); + getTextAdaptedButton( _icnVsSprite[id][0], _icnVsSprite[id][1], gettext_noop( "EVENTS" ), isEvilInterface ? ICN::EMPTY_EVIL_BUTTON : ICN::EMPTY_GOOD_BUTTON, + isEvilInterface ? ICN::STONEBAK_EVIL : ICN::STONEBAK ); - // Language-specific image generators, may fail - if ( fheroes2::getCurrentLanguage() == resourceLanguage ) { - switch ( resourceLanguage ) { - case fheroes2::SupportedLanguage::German: - if ( generateGermanSpecificImages( id ) ) { - return; - } - break; - case fheroes2::SupportedLanguage::French: - if ( generateFrenchSpecificImages( id ) ) { - return; - } - break; - case fheroes2::SupportedLanguage::Polish: - if ( generatePolishSpecificImages( id ) ) { - return; - } - break; - case fheroes2::SupportedLanguage::Italian: - if ( generateItalianSpecificImages( id ) ) { - return; - } - break; - default: - break; - } - } - // Image generator of a last resort, must provide the generation of the "default" variant - // for all image ids for which this function can be called, and must not fail. - generateDefaultImages( id ); + break; } + default: + // You're calling this function for non-specified ICN id. Check your logic! + // Did you add a new image for one language without generating a default + // for other languages? + assert( 0 ); + break; + } + } - // This function must return true is resources have been modified, false otherwise. - bool LoadModifiedICN( const int id ) - { - // If this assertion blows up then you are calling this function in a recursion. Check your code! - assert( _icnVsSprite[id].empty() ); + bool generateGermanSpecificImages( const int id ) + { + switch ( id ) { + case ICN::BTNBATTLEONLY: + _icnVsSprite[id].resize( 2 ); + for ( int32_t i = 0; i < static_cast( _icnVsSprite[id].size() ); ++i ) { + fheroes2::Sprite & out = _icnVsSprite[id][i]; + out = fheroes2::AGG::GetICN( ICN::BTNNEWGM, 6 + i ); + // Clean the button + Fill( out, 25, 18, 88, 23, getButtonFillingColor( i == 0 ) ); + // Add 'K' + Blit( fheroes2::AGG::GetICN( ICN::BTNDCCFG, 4 + i ), 34 - i, 23, out, 40 - i, 23, 12, 14 ); + //'Add 'A' + Blit( fheroes2::AGG::GetICN( ICN::BTNNEWGM, 4 + i ), 56 - i, 23, out, 52 - i, 23, 13, 14 ); + Blit( out, 20, 20, out, 52 - i + 12, 25, 3, 3 ); + // Add 'M' + Blit( fheroes2::AGG::GetICN( ICN::BTNNEWGM, 4 + i ), 39 - i, 8, out, 65 - i, 23, 14, 14 ); + // Add 'F' + Blit( fheroes2::AGG::GetICN( ICN::BTNDCCFG, 4 + i ), 70 - i, 23, out, 87 - i, 23, 10, 14 ); + // Add 'P' + Blit( fheroes2::AGG::GetICN( ICN::BTNNEWGM, 4 + i ), 36 - i, 23, out, 78 - i, 23, 10, 14 ); + } + return true; + case ICN::BUTTON_SMALL_MIN_GOOD: + _icnVsSprite[id].resize( 2 ); + for ( int32_t i = 0; i < static_cast( _icnVsSprite[id].size() ); ++i ) { + fheroes2::Sprite & out = _icnVsSprite[id][i]; + out = fheroes2::AGG::GetICN( ICN::RECRUIT, 4 + i ); + // clean the button + Blit( fheroes2::AGG::GetICN( ICN::SYSTEM, 11 + i ), 10, 6 + i, out, 30 - 2 * i, 5 + i, 31, 15 ); + // add 'IN' + Copy( fheroes2::AGG::GetICN( ICN::APANEL, 4 + i ), 23 - i, 22 + i, out, 33 - i, 6 + i, 8, 14 ); // letter 'I' + Copy( fheroes2::AGG::GetICN( ICN::APANEL, 4 + i ), 31 - i, 22 + i, out, 44 - i, 6 + i, 17, 14 ); // letter 'N' + } + return true; + default: + break; + } + return false; + } - // IMPORTANT!!! - // Call LoadOriginalICN() function only if you are handling the same ICN. - // If you need to load a different ICN use loadICN() function. + bool generateFrenchSpecificImages( const int id ) + { + switch ( id ) { + case ICN::BTNBATTLEONLY: + _icnVsSprite[id].resize( 2 ); + for ( int32_t i = 0; i < static_cast( _icnVsSprite[id].size() ); ++i ) { + fheroes2::Sprite & out = _icnVsSprite[id][i]; + out = fheroes2::AGG::GetICN( ICN::BTNNEWGM, 6 + i ); + // Clean the button + Fill( out, 25, 18, 88, 23, getButtonFillingColor( i == 0 ) ); + + const int32_t secondLine = 28; + // Add 'MODE' + Blit( fheroes2::AGG::GetICN( ICN::BTNNEWGM, 4 + i ), 40 - i, 13, out, 45 - i, 13, 50, 15 ); + // Clean up 'MODE' + Blit( fheroes2::AGG::GetICN( ICN::BTNEMAIN, 0 + i ), 114 - i, 18, out, 94 - i, 18, 1, 10 ); + // Add 'BA' + Blit( fheroes2::AGG::GetICN( ICN::BTNBAUD, 2 + i ), 42 - i, 28, out, 28 - i, secondLine, 22, 15 ); + // Clean up 'BA' + Blit( fheroes2::AGG::GetICN( ICN::BTNBAUD, 2 + i ), 42 - i, 31, out, 39 - i, secondLine, 1, 1 ); + Blit( fheroes2::AGG::GetICN( ICN::BTNBAUD, 2 + i ), 39 - i, 31, out, 49 - i, secondLine + 4, 1, 2 ); + // Add 'T' + Blit( fheroes2::AGG::GetICN( ICN::BTNDC, 2 + i ), 89 - i, 21, out, 50 - i, secondLine, 12, 15 ); + // Clean up 'AT' + Blit( fheroes2::AGG::GetICN( ICN::BTNDC, 2 + i ), 89 - i, 18, out, 50 - i, secondLine, 1, 1 ); + Blit( fheroes2::AGG::GetICN( ICN::BTNDC, 2 + i ), 92 - ( 5 * i ), 27 - i, out, 49 - i, secondLine + 4 + i, 1, 3 ); + // Add 'AI'. + Blit( fheroes2::AGG::GetICN( ICN::BTNMP, 6 + i ), 56 - i, 13, out, 62 - i, secondLine, 18, 15 ); + // Clean up 'TA' + Blit( fheroes2::AGG::GetICN( ICN::BTNBAUD, 2 + i ), 51 - i, 40, out, 60 - i, secondLine + 12, 3, 3 ); + // Add 'LLE' + Blit( fheroes2::AGG::GetICN( ICN::BTNEMAIN, 0 + i ), 85 - i, 13, out, 81 - i, secondLine, 31, 15 ); + // Clean up "IL" + Blit( fheroes2::AGG::GetICN( ICN::BTNEMAIN, 0 + i ), 85 - i, 18, out, 81 - i, secondLine + 7, 1, 1 ); + Blit( fheroes2::AGG::GetICN( ICN::BTNEMAIN, 0 + i ), 94 - i, 17, out, 80 - i, secondLine + 4, 2, 2 ); + Blit( fheroes2::AGG::GetICN( ICN::BTNEMAIN, 0 + i ), 93 - i, 25, out, 79 - i, secondLine + 12, 3, 3 ); + Blit( fheroes2::AGG::GetICN( ICN::BTNDC, 4 + i ), 23 - i, 8, out, 79 - i, secondLine + 5, 1, 10 ); + Blit( fheroes2::AGG::GetICN( ICN::BTNMP, 6 + i ), 73 - i, 22, out, 79 - i, secondLine + 9, 1, 1 ); + } + return true; + case ICN::BTNGIFT_GOOD: + _icnVsSprite[id].resize( 2 ); + for ( int32_t i = 0; i < static_cast( _icnVsSprite[id].size() ); ++i ) { + fheroes2::Sprite & out = _icnVsSprite[id][i]; + out = fheroes2::AGG::GetICN( ICN::TRADPOST, 17 + i ); + // clean the button + Fill( out, 33, 5, 31, 16, getButtonFillingColor( i == 0 ) ); + + const int32_t offsetY = 5; + // Add 'D' + const int32_t offsetXD = 14; + Blit( fheroes2::AGG::GetICN( ICN::CPANEL, 4 + i ), 48 - i, 28 + i, out, offsetXD - i, offsetY + i, 10, 15 ); + // Clean up 'D' and restore button ornament + Blit( fheroes2::AGG::GetICN( ICN::CPANEL, 4 + i ), 48 - i, 36, out, offsetXD - 1 - i, offsetY + 4 + i, 1, 1 ); + Blit( fheroes2::AGG::GetICN( ICN::CPANEL, 4 + i ), 48 - i, 35, out, offsetXD - i, offsetY + 9 + i, 1, 2 ); + Blit( fheroes2::AGG::GetICN( ICN::CPANEL, 4 + i ), 48 - i, 35, out, offsetXD - 1 - i, offsetY + 13 + i, 1, 1 ); + Fill( out, offsetXD + 9 - i, offsetY + 13 + i, 1, 1, getButtonFillingColor( i == 0 ) ); + Blit( fheroes2::AGG::GetICN( ICN::TRADPOST, 17 + i ), offsetXD, offsetY, out, offsetXD, offsetY, 1, 1 ); + // Add 'O' + const int32_t offsetXO = 10; + Blit( fheroes2::AGG::GetICN( ICN::CAMPXTRG, i ), 40 - ( 7 * i ), 5 + i, out, offsetXD + offsetXO + 1 - i, offsetY + i, 13 - i, 15 ); + // Clean up 'DO' + Blit( fheroes2::AGG::GetICN( ICN::CPANEL, 4 + i ), 51 - i, 34, out, offsetXD + offsetXO - i, offsetY + 5, 2, 2 ); + Blit( fheroes2::AGG::GetICN( ICN::CPANEL, 4 + i ), 51 - i, 34, out, offsetXD + offsetXO - i, offsetY + 7, 1, 1 + i ); + Blit( fheroes2::AGG::GetICN( ICN::CPANEL, 4 + i ), 55 - i, 28 + i, out, offsetXD + 9 - i, offsetY + 2 + i, 3, 3 ); + Fill( out, offsetXD + 11 - i, offsetY + i, 2, 2, getButtonFillingColor( i == 0 ) ); + // Add 'N' + const int32_t offsetXN = 13; + Blit( fheroes2::AGG::GetICN( ICN::TRADPOST, 17 + i ), 50 - i, 5, out, offsetXD + offsetXO + offsetXN - i, offsetY, 14, 15 ); + // Clean up 'ON' + Fill( out, offsetXD + offsetXO + offsetXN, offsetY, 1, 1, getButtonFillingColor( i == 0 ) ); + Fill( out, offsetXD + offsetXO + offsetXN - i, offsetY + 9, 1, 1, getButtonFillingColor( i == 0 ) ); + // Add 'N' + Blit( fheroes2::AGG::GetICN( ICN::TRADPOST, 17 + i ), 50 - i, 5, out, offsetXD + 10 + offsetXN + offsetXN - i, offsetY, 14, 15 ); + // Clean up 'NN' + Fill( out, offsetXD + offsetXO + offsetXN + offsetXN - i, offsetY + 9, 1, 1, getButtonFillingColor( i == 0 ) ); + // Add 'ER' + Blit( fheroes2::AGG::GetICN( ICN::CAMPXTRG, 2 + i ), 75 - ( 8 * i ), 5, out, offsetXD + offsetXO + offsetXN + offsetXN + offsetXN - ( 2 * i ), offsetY, + 23, 15 ); + // Restore button ornament + Blit( fheroes2::AGG::GetICN( ICN::TRADPOST, 17 + i ), offsetXD + offsetXO + offsetXN + offsetXN + offsetXN + 20, offsetY, out, + offsetXD + offsetXO + offsetXN + offsetXN + offsetXN + 20, offsetY, 1, 1 ); + Blit( fheroes2::AGG::GetICN( ICN::TRADPOST, 17 + i ), offsetXD + offsetXO + offsetXN + offsetXN + offsetXN + 21, offsetY + 1, out, + offsetXD + offsetXO + offsetXN + offsetXN + offsetXN + 21, offsetY + 1, 2, 3 ); + Blit( fheroes2::AGG::GetICN( ICN::TRADPOST, 17 + i ), offsetXD + offsetXO + offsetXN + offsetXN + offsetXN + 20, offsetY, out, + offsetXD + offsetXO + offsetXN + offsetXN + offsetXN + 21, offsetY + 4, 1, 1 ); + } + return true; + case ICN::BTNGIFT_EVIL: + _icnVsSprite[id].resize( 2 ); + for ( int32_t i = 0; i < static_cast( _icnVsSprite[id].size() ); ++i ) { + fheroes2::Sprite & out = _icnVsSprite[id][i]; + out = fheroes2::AGG::GetICN( ICN::TRADPOSE, 17 + i ); + // clean the button + Fill( out, 33, 5, 31, 16, getButtonFillingColor( i == 0, false ) ); + + const int32_t offsetY = 5; + // Add 'D' + const int32_t offsetXD = 14; + Blit( fheroes2::AGG::GetICN( ICN::CPANELE, 4 + i ), 48 - i, 28 + i, out, offsetXD - i, offsetY + i, 10, 15 ); + // Clean up 'D' and restore button ornament + Blit( fheroes2::AGG::GetICN( ICN::CPANELE, 4 + i ), 48 - i, 36, out, offsetXD - 1 - i, offsetY + 4 + i, 1, 1 ); + Blit( fheroes2::AGG::GetICN( ICN::CPANELE, 4 + i ), 48 - i, 35, out, offsetXD - i, offsetY + 9 + i, 1, 2 ); + Blit( fheroes2::AGG::GetICN( ICN::CPANELE, 4 + i ), 48 - i, 35, out, offsetXD - 1 - i, offsetY + 13 + i, 1, 1 ); + Fill( out, offsetXD + 9 - i, offsetY + 13 + i, 1, 1, getButtonFillingColor( i == 0, false ) ); + Blit( fheroes2::AGG::GetICN( ICN::TRADPOSE, 17 + i ), offsetXD, offsetY, out, offsetXD, offsetY, 1, 1 ); + Fill( out, offsetXD + 9 - i, offsetY + i, 1, 1, getButtonFillingColor( i == 0, false ) ); + // Add 'O' + const int32_t offsetXO = 10; + Blit( fheroes2::AGG::GetICN( ICN::APANELE, 4 + i ), 50 - i, 20 + i, out, offsetXD + offsetXO + 1 - i, offsetY + i, 13 - i, 14 ); + // Clean up 'DO' + Blit( fheroes2::AGG::GetICN( ICN::CPANELE, 4 + i ), 51 - i, 34, out, offsetXD + offsetXO - i, offsetY + 5, 2, 2 ); + Blit( fheroes2::AGG::GetICN( ICN::CPANELE, 4 + i ), 51 - i, 34, out, offsetXD + offsetXO - i, offsetY + 7, 1, 1 + i ); + Blit( fheroes2::AGG::GetICN( ICN::CPANELE, 4 + i ), 56 - i, 28 + i, out, offsetXD + 10 - i, offsetY + 2 + i, 1, 3 ); + Blit( fheroes2::AGG::GetICN( ICN::CPANELE, 4 + i ), 56 - i, 28 + i, out, offsetXD + 11 - i, offsetY + 3 + i, 1, 2 ); + Fill( out, offsetXD + 11 - i, offsetY + i, 3, 3, getButtonFillingColor( i == 0, false ) ); + Fill( out, offsetXD + 12 - i, offsetY + 3 + i, 1, 2, getButtonFillingColor( i == 0, false ) ); + // Add 'N' + const int32_t offsetXN = 13; + Blit( fheroes2::AGG::GetICN( ICN::TRADPOSE, 17 + i ), 50 - i, 5, out, offsetXD + offsetXO + offsetXN - i, offsetY, 14, 15 ); + // Clean up 'ON' + Fill( out, offsetXD + offsetXO + offsetXN - 1 - i, offsetY + 11 + i, 1, 3, getButtonFillingColor( i == 0, false ) ); + Fill( out, offsetXD + offsetXO + offsetXN - i, offsetY, 1, 1, getButtonFillingColor( i == 0, false ) ); + Fill( out, offsetXD + offsetXO + offsetXN - i, offsetY + 9, 1, 1, getButtonFillingColor( i == 0, false ) ); + // Add 'N' + Blit( fheroes2::AGG::GetICN( ICN::TRADPOSE, 17 + i ), 50 - i, 5, out, offsetXD + offsetXO + offsetXN + offsetXN - i, offsetY, 13, 15 ); + // Clean up 'NN' + Fill( out, offsetXD + offsetXO + offsetXN + offsetXN - i, offsetY + 9, 1, 1, getButtonFillingColor( i == 0, false ) ); + // Add 'ER' + Blit( fheroes2::AGG::GetICN( ICN::APANELE, 8 + i ), 66 - ( 3 * i ), 5 + ( 2 * i ), out, offsetXD + offsetXO + offsetXN + offsetXN + offsetXN - ( 2 * i ), + offsetY + ( 2 * i ), 23, 14 - i ); + // Clean up 'NE' + Blit( fheroes2::AGG::GetICN( ICN::TRADPOSE, 17 + i ), 50 - i, 5, out, offsetXD + offsetXO + offsetXN + offsetXN + offsetXN - i, offsetY, 2, 10 ); + Fill( out, offsetXD + offsetXO + offsetXN + offsetXN + offsetXN - ( 2 * i ), offsetY + 9 + i, 1 + i, 2 + i, getButtonFillingColor( i == 0, false ) ); + // Restore button ornament + Blit( fheroes2::AGG::GetICN( ICN::TRADPOSE, 17 + i ), offsetXD + offsetXO + offsetXN + offsetXN + offsetXN + 20, offsetY, out, + offsetXD + offsetXO + offsetXN + offsetXN + offsetXN + 20, offsetY, 1, 1 ); + Blit( fheroes2::AGG::GetICN( ICN::TRADPOSE, 17 + i ), offsetXD + offsetXO + offsetXN + offsetXN + offsetXN + 21, offsetY + 1, out, + offsetXD + offsetXO + offsetXN + offsetXN + offsetXN + 21, offsetY + 1, 2, 3 ); + Blit( fheroes2::AGG::GetICN( ICN::TRADPOSE, 17 + i ), offsetXD + offsetXO + offsetXN + offsetXN + offsetXN + 20, offsetY, out, + offsetXD + offsetXO + offsetXN + offsetXN + offsetXN + 21, offsetY + 4, 1, 1 ); + } + return true; + case ICN::BUTTON_SMALL_MIN_GOOD: + _icnVsSprite[id].resize( 2 ); + for ( int32_t i = 0; i < static_cast( _icnVsSprite[id].size() ); ++i ) { + fheroes2::Sprite & out = _icnVsSprite[id][i]; + out = fheroes2::AGG::GetICN( ICN::RECRUIT, 4 + i ); + // Clean the button and leave 'M' + Fill( out, 31 - 2 * i, 5 + i, 25, 15, getButtonFillingColor( i == 0 ) ); + Fill( out, 29 - 2 * i, 17 + i, 2, 2, getButtonFillingColor( i == 0 ) ); + // Add 'I' + Blit( fheroes2::AGG::GetICN( ICN::APANEL, 4 + i ), 25 - i, 19 + i, out, 32 - i, 4 + i, 7 - i, 15 ); + Blit( fheroes2::AGG::GetICN( ICN::RECRUIT, 4 + i ), 28 - i, 7 + i, out, 36 - i, 7 + i, 3, 9 ); + Fill( out, 37 - i, 16 + i, 2, 3, getButtonFillingColor( i == 0 ) ); + // Add 'N' + Blit( fheroes2::AGG::GetICN( ICN::TRADPOST, 17 + i ), 50 - i, 5, out, 41 - i, 5, 14, 15 ); + Fill( out, 41 - i, 5, 1, 1, getButtonFillingColor( i == 0 ) ); + Fill( out, 41 - i, 5 + 9, 1, 1, getButtonFillingColor( i == 0 ) ); + } + return true; + default: + break; + } + return false; + } - switch ( id ) { - case ICN::ROUTERED: - CopyICNWithPalette( id, ICN::ROUTE, PAL::PaletteType::RED ); - return true; - case ICN::FONT: - case ICN::SMALFONT: { - LoadOriginalICN( id ); - - auto & imageArray = _icnVsSprite[id]; - if ( imageArray.size() < 96 ) { - // 96 symbols is the minimum requirement for English. - throw std::logic_error( "The game resources are corrupted. Please use resources from a licensed version of Heroes of Might and Magic II." ); - } + bool generatePolishSpecificImages( const int id ) + { + switch ( id ) { + case ICN::BTNBATTLEONLY: + _icnVsSprite[id].resize( 2 ); + for ( int32_t i = 0; i < static_cast( _icnVsSprite[id].size() ); ++i ) { + fheroes2::Sprite & out = _icnVsSprite[id][i]; + out = fheroes2::AGG::GetICN( ICN::BTNNEWGM, 6 + i ); + // clean the button + Fill( out, 25, 18, 88, 23, getButtonFillingColor( i == 0 ) ); + const int32_t offsetX = 46; + const int32_t offsetY = 23; + // Add 'BI' + Blit( fheroes2::AGG::GetICN( ICN::BTNMCFG, 2 + i ), 58 - i, 29, out, offsetX - i, offsetY, 14, 11 ); + // Add 'T' + Blit( fheroes2::AGG::GetICN( ICN::BTNNEWGM, 0 + i ), 24 - i, 29, out, offsetX + 14 - i, offsetY, 9, 11 ); + // Add 'WA' + Blit( fheroes2::AGG::GetICN( ICN::BTNEMAIN, 0 + i ), 45 - i, 23, out, offsetX + 23 - i, offsetY, 24, 11 ); + // Add pixel to 'W' + Blit( fheroes2::AGG::GetICN( ICN::BTNEMAIN, 0 + i ), 47 - i, 23 + i, out, offsetX + 38 - i, offsetY + i, 1, 1 ); + } + return true; + default: + break; + } + return false; + } - // Compare '(' and ')' symbols. By size they are always the same. However, we play safe and fail if both dimensions are different. - if ( ( imageArray[8].width() != imageArray[9].width() ) && ( imageArray[8].height() != imageArray[9].height() ) ) { - // This is most likely a corrupted font or a pirated translation to a non-English language which causes all sorts of rendering issues. - throw std::logic_error( "The game resources are corrupted. Please use resources from a licensed version of Heroes of Might and Magic II." ); - } + bool generateItalianSpecificImages( const int id ) + { + switch ( id ) { + case ICN::BTNBATTLEONLY: + _icnVsSprite[id].resize( 2 ); + for ( int32_t i = 0; i < static_cast( _icnVsSprite[id].size() ); ++i ) { + fheroes2::Sprite & out = _icnVsSprite[id][i]; + out = fheroes2::AGG::GetICN( ICN::BTNNEWGM, 6 + i ); + // clean the button + const uint8_t buttonFillingColor = getButtonFillingColor( i == 0 ); + Fill( out, 25, 18, 88, 23, buttonFillingColor ); + const int32_t offsetX = 16; + const int32_t offsetY = 21; + // Add 'B' + Blit( fheroes2::AGG::GetICN( ICN::BTNBAUD, 0 + i ), 42 - i, 28, out, offsetX - i, offsetY, 13, 15 ); + Fill( out, offsetX + 11, offsetY + 13, 1, 2, buttonFillingColor ); + // Add 'A' + Blit( fheroes2::AGG::GetICN( ICN::BTNNEWGM, 0 + i ), 80 - i, 28, out, offsetX + 13 - i, offsetY, 14, 15 ); + Fill( out, offsetX + 13 - i, offsetY + 3, 1, 4, buttonFillingColor ); + // Add 'T' + Blit( fheroes2::AGG::GetICN( ICN::BTNMP, 0 + i ), 74 - i, 5, out, offsetX + 27 - 2 * i, offsetY, 12, 15 ); + // Add 'T' + Blit( fheroes2::AGG::GetICN( ICN::BTNMP, 0 + i ), 74 - i, 5, out, offsetX + 39 - 2 * i, offsetY, 12, 15 ); + // Add 'A' + Blit( fheroes2::AGG::GetICN( ICN::BTNNEWGM, 0 + i ), 80 - i, 28, out, offsetX + 50 - i, offsetY, 14, 15 ); + Fill( out, offsetX + 65 - i, offsetY + 5, 1, 2, buttonFillingColor ); + Fill( out, offsetX + 65 - i, offsetY + 14, 1, 3, buttonFillingColor ); + Fill( out, offsetX + 50 - i, offsetY + 3, 1, 4, buttonFillingColor ); + // Add 'G' + Blit( fheroes2::AGG::GetICN( ICN::BTNNEWGM, 0 + i ), 44 - i, 12, out, offsetX + 65 - i, offsetY, 11, 15 ); + // Add 'L' + Blit( fheroes2::AGG::GetICN( ICN::BTNDC, 4 + i ), 77 - i, 21, out, offsetX + 77 - 2 * i, offsetY, 9, 15 ); + // Add 'I' + Blit( fheroes2::AGG::GetICN( ICN::BTNNEWGM, 0 + i ), 56 - i, 12, out, offsetX + 86 - i, offsetY, 7, 15 ); + // Add 'A' + Blit( fheroes2::AGG::GetICN( ICN::BTNNEWGM, 0 + i ), 80 - i, 28, out, offsetX + 93 - i, offsetY, 14, 15 ); + Fill( out, offsetX + 109 - i, offsetY + 5, 1, 2, buttonFillingColor ); + Fill( out, offsetX + 93 - i, offsetY + 3, 1, 4, buttonFillingColor ); + } + return true; + default: + break; + } + return false; + } - const std::vector & body = ::AGG::getDataFromAggFile( ICN::GetString( id ) ); - const uint32_t crc32 = fheroes2::calculateCRC32( body.data(), body.size() ); + void generateLanguageSpecificImages( int id ) + { + assert( isLanguageDependentIcnId( id ) ); - if ( id == ICN::SMALFONT ) { - // Small font in official Polish GoG version has all letters shifted 1 pixel down. - if ( crc32 == 0xE9EC7A63 ) { - for ( Sprite & letter : imageArray ) { - letter.setPosition( letter.x(), letter.y() - 1 ); - } - } - modifyBaseSmallFont( _icnVsSprite[id] ); - } - else { - assert( id == ICN::FONT ); - // The original images contain an issue: image layer has value 50 which is '2' in UTF-8. We must correct these (only 3) places - for ( fheroes2::Sprite & fontImage : imageArray ) { - ReplaceColorIdByTransformId( fontImage, 50, 2 ); - } - modifyBaseNormalFont( _icnVsSprite[id] ); - } + const fheroes2::SupportedLanguage resourceLanguage = fheroes2::getResourceLanguage(); - // Some checks that we really have CP1251 font - const int32_t verifiedFontWidth = ( id == ICN::FONT ) ? 19 : 12; - if ( imageArray.size() == 162 && imageArray[121].width() == verifiedFontWidth ) { - // Engine expects that letter indexes correspond to charcode - 0x20. - // In case CP1251 font.icn contains sprites for chars 0x20-0x7F, 0xC0-0xDF, 0xA8, 0xE0-0xFF, 0xB8 (in that order). - // We rearrange sprites array for corresponding sprite indexes to charcode - 0x20. - const Sprite firstSprite{ imageArray[0] }; - imageArray.insert( imageArray.begin() + 96, 64, firstSprite ); - std::swap( imageArray[136], imageArray[192] ); // Move sprites for chars 0xA8 - std::swap( imageArray[152], imageArray[225] ); // and 0xB8 to it's places. - imageArray.pop_back(); - imageArray.erase( imageArray.begin() + 192 ); + // Language-specific image generators, may fail + if ( fheroes2::getCurrentLanguage() == resourceLanguage ) { + switch ( resourceLanguage ) { + case fheroes2::SupportedLanguage::German: + if ( generateGermanSpecificImages( id ) ) { + return; } - // German version uses CP1252 - if ( crc32 == 0x04745D1D || crc32 == 0xD0F0D852 ) { - const Sprite firstSprite{ imageArray[0] }; - imageArray.insert( imageArray.begin() + 96, 124, firstSprite ); - std::swap( imageArray[164], imageArray[224] ); - std::swap( imageArray[182], imageArray[225] ); - std::swap( imageArray[188], imageArray[226] ); - std::swap( imageArray[191], imageArray[223] ); - std::swap( imageArray[196], imageArray[220] ); - std::swap( imageArray[214], imageArray[221] ); - std::swap( imageArray[220], imageArray[222] ); - imageArray.erase( imageArray.begin() + 221, imageArray.end() ); + break; + case fheroes2::SupportedLanguage::French: + if ( generateFrenchSpecificImages( id ) ) { + return; } - // French version has its own special encoding but should conform to CP1252 too - if ( crc32 == 0xD9556567 || crc32 == 0x406967B9 ) { - const Sprite firstSprite{ imageArray[0] }; - imageArray.insert( imageArray.begin() + 96, 160 - 32, firstSprite ); - imageArray[192 - 32] = imageArray[33]; - imageArray[199 - 32] = imageArray[35]; - imageArray[201 - 32] = imageArray[37]; - imageArray[202 - 32] = imageArray[37]; - imageArray[244 - 32] = imageArray[3]; - imageArray[251 - 32] = imageArray[4]; - imageArray[249 - 32] = imageArray[6]; - imageArray[226 - 32] = imageArray[10]; - imageArray[239 - 32] = imageArray[28]; - imageArray[238 - 32] = imageArray[30]; - imageArray[224 - 32] = imageArray[32]; - imageArray[231 - 32] = imageArray[62]; - imageArray[232 - 32] = imageArray[64]; - imageArray[239 - 32] = imageArray[91]; - imageArray[234 - 32] = imageArray[92]; - imageArray[238 - 32] = imageArray[93]; - imageArray[233 - 32] = imageArray[94]; - imageArray[238 - 32] = imageArray[95]; - imageArray.erase( imageArray.begin() + 252 - 32, imageArray.end() ); + break; + case fheroes2::SupportedLanguage::Polish: + if ( generatePolishSpecificImages( id ) ) { + return; } - // Italian version uses CP1252 - if ( crc32 == 0x219B3124 || crc32 == 0x1F3C3C74 ) { - const Sprite firstSprite{ imageArray[0] }; - imageArray.insert( imageArray.begin() + 101, 155 - 32, firstSprite ); - imageArray[192 - 32] = imageArray[33]; - imageArray[200 - 32] = imageArray[37]; - imageArray[201 - 32] = imageArray[37]; - imageArray[204 - 32] = imageArray[41]; - imageArray[210 - 32] = imageArray[47]; - imageArray[217 - 32] = imageArray[53]; - imageArray[224 - 32] = imageArray[96]; - imageArray[232 - 32] = imageArray[97]; - imageArray[233 - 32] = imageArray[69]; - imageArray[236 - 32] = imageArray[98]; - imageArray[242 - 32] = imageArray[99]; - imageArray[249 - 32] = imageArray[100]; - imageArray.erase( imageArray.begin() + 250 - 32, imageArray.end() ); + break; + case fheroes2::SupportedLanguage::Italian: + if ( generateItalianSpecificImages( id ) ) { + return; } - return true; + break; + default: + break; } - case ICN::YELLOW_FONT: - CopyICNWithPalette( id, ICN::FONT, PAL::PaletteType::YELLOW_FONT ); - return true; - case ICN::YELLOW_SMALLFONT: - CopyICNWithPalette( id, ICN::SMALFONT, PAL::PaletteType::YELLOW_FONT ); - return true; - case ICN::GRAY_FONT: - CopyICNWithPalette( id, ICN::FONT, PAL::PaletteType::GRAY_FONT ); - return true; - case ICN::GRAY_SMALL_FONT: - CopyICNWithPalette( id, ICN::SMALFONT, PAL::PaletteType::GRAY_FONT ); - return true; - case ICN::SPELLS: - LoadOriginalICN( id ); - if ( _icnVsSprite[id].size() != 60 ) { - return true; - } - - _icnVsSprite[id].resize( 73 ); - - for ( uint32_t i = 60; i < 66; ++i ) { - // Mass Cure spell. ( when i == 60 ). - size_t originalIndex = 6; - if ( i == 61 ) { - // Mass Haste spell. - originalIndex = 14; - } - else if ( i == 62 ) { - // Mass Slow spell. - originalIndex = 1; - } - else if ( i == 63 ) { - // Mass Bless spell. - originalIndex = 7; - } - else if ( i == 64 ) { - // Mass Curse spell. - originalIndex = 3; - } - else if ( i == 65 ) { - // Mass Shield spell. - originalIndex = 15; - } - - const Sprite & originalImage = _icnVsSprite[id][originalIndex]; - Sprite & image = _icnVsSprite[id][i]; - - image.resize( originalImage.width() + 8, originalImage.height() + 8 ); - image.setPosition( originalImage.x() + 4, originalImage.y() + 4 ); - image.fill( 1 ); + } + // Image generator of a last resort, must provide the generation of the "default" variant + // for all image ids for which this function can be called, and must not fail. + generateDefaultImages( id ); + } - AlphaBlit( originalImage, image, 0, 0, 128 ); - AlphaBlit( originalImage, image, 4, 4, 192 ); - Blit( originalImage, image, 8, 8 ); + // This function must return true is resources have been modified, false otherwise. + bool LoadModifiedICN( const int id ) + { + // If this assertion blows up then you are calling this function in a recursion. Check your code! + assert( _icnVsSprite[id].empty() ); - AddTransparency( image, 1 ); - } + // IMPORTANT!!! + // Call LoadOriginalICN() function only if you are handling the same ICN. + // If you need to load a different ICN use loadICN() function. - // The Petrification spell does not have its own icon in the original game. - h2d::readImage( "petrification_spell_icon.image", _icnVsSprite[id][66] ); + switch ( id ) { + case ICN::ROUTERED: + CopyICNWithPalette( id, ICN::ROUTE, PAL::PaletteType::RED ); + return true; + case ICN::FONT: + case ICN::SMALFONT: { + LoadOriginalICN( id ); - // Generate random spell image for Editor. - { - const Sprite & randomSpellImage = _icnVsSprite[id][2]; - const int32_t imageWidth = randomSpellImage.width(); + auto & imageArray = _icnVsSprite[id]; + if ( imageArray.size() < 96 ) { + // 96 symbols is the minimum requirement for English. + throw std::logic_error( "The game resources are corrupted. Please use resources from a licensed version of Heroes of Might and Magic II." ); + } - Copy( randomSpellImage, _icnVsSprite[id][67] ); + // Compare '(' and ')' symbols. By size they are always the same. However, we play safe and fail if both dimensions are different. + if ( ( imageArray[8].width() != imageArray[9].width() ) && ( imageArray[8].height() != imageArray[9].height() ) ) { + // This is most likely a corrupted font or a pirated translation to a non-English language which causes all sorts of rendering issues. + throw std::logic_error( "The game resources are corrupted. Please use resources from a licensed version of Heroes of Might and Magic II." ); + } - // Add text on random spell images. - for ( uint32_t i = 1; i < 6; ++i ) { - Sprite & originalImage = _icnVsSprite[id][i + 67]; - Copy( randomSpellImage, originalImage ); + const std::vector & body = ::AGG::getDataFromAggFile( ICN::GetString( id ) ); + const uint32_t crc32 = fheroes2::calculateCRC32( body.data(), body.size() ); - const Text text( std::to_string( i ), FontType::normalWhite() ); - text.draw( ( imageWidth - text.width() ) / 2, 22, originalImage ); - } - } - return true; - case ICN::CSLMARKER: - _icnVsSprite[id].resize( 3 ); - for ( uint32_t i = 0; i < 3; ++i ) { - _icnVsSprite[id][i] = GetICN( ICN::LOCATORS, 24 ); - if ( i == 1 ) { - ReplaceColorId( _icnVsSprite[id][i], 0x0A, 0xD6 ); - } - else if ( i == 2 ) { - ReplaceColorId( _icnVsSprite[id][i], 0x0A, 0xDE ); - } - } - return true; - case ICN::BUYMAX: - case ICN::BUTTON_NEW_GAME_GOOD: - case ICN::BUTTON_NEW_GAME_EVIL: - case ICN::BUTTON_SAVE_GAME_GOOD: - case ICN::BUTTON_SAVE_GAME_EVIL: - case ICN::BUTTON_LOAD_GAME_GOOD: - case ICN::BUTTON_LOAD_GAME_EVIL: - case ICN::BUTTON_INFO_GOOD: - case ICN::BUTTON_INFO_EVIL: - case ICN::BUTTON_QUIT_GOOD: - case ICN::BUTTON_QUIT_EVIL: - case ICN::BUTTON_NEW_MAP_EVIL: - case ICN::BUTTON_NEW_MAP_GOOD: - case ICN::BUTTON_SAVE_MAP_EVIL: - case ICN::BUTTON_SAVE_MAP_GOOD: - case ICN::BUTTON_LOAD_MAP_EVIL: - case ICN::BUTTON_LOAD_MAP_GOOD: - case ICN::BUTTON_SMALL_CANCEL_GOOD: - case ICN::BUTTON_SMALL_CANCEL_EVIL: - case ICN::BUTTON_SMALL_OKAY_GOOD: - case ICN::BUTTON_SMALL_OKAY_EVIL: - case ICN::BUTTON_SMALLER_OKAY_GOOD: - case ICN::BUTTON_SMALLER_OKAY_EVIL: - case ICN::BUTTON_SMALL_ACCEPT_GOOD: - case ICN::BUTTON_SMALL_ACCEPT_EVIL: - case ICN::BUTTON_SMALL_DECLINE_GOOD: - case ICN::BUTTON_SMALL_DECLINE_EVIL: - case ICN::BUTTON_SMALL_LEARN_GOOD: - case ICN::BUTTON_SMALL_LEARN_EVIL: - case ICN::BUTTON_SMALL_TRADE_GOOD: - case ICN::BUTTON_SMALL_TRADE_EVIL: - case ICN::BUTTON_SMALL_YES_GOOD: - case ICN::BUTTON_SMALL_YES_EVIL: - case ICN::BUTTON_SMALL_NO_GOOD: - case ICN::BUTTON_SMALL_NO_EVIL: - case ICN::BUTTON_SMALL_EXIT_GOOD: - case ICN::BUTTON_SMALL_EXIT_EVIL: - case ICN::BUTTON_EXIT_HEROES_MEETING: - case ICN::BUTTON_EXIT_TOWN: - case ICN::BUTTON_EXIT_PUZZLE_DIM_DOOR_EVIL: - case ICN::BUTTON_EXIT_PUZZLE_DIM_DOOR_GOOD: - case ICN::BUTTON_SMALL_DISMISS_GOOD: - case ICN::BUTTON_SMALL_DISMISS_EVIL: - case ICN::BUTTON_SMALL_UPGRADE_GOOD: - case ICN::BUTTON_SMALL_UPGRADE_EVIL: - case ICN::BUTTON_SMALL_RESTART_GOOD: - case ICN::BUTTON_SMALL_RESTART_EVIL: - case ICN::BUTTON_KINGDOM_EXIT: - case ICN::BUTTON_KINGDOM_HEROES: - case ICN::BUTTON_KINGDOM_TOWNS: - case ICN::BUTTON_MAPSIZE_SMALL: - case ICN::BUTTON_MAPSIZE_MEDIUM: - case ICN::BUTTON_MAPSIZE_LARGE: - case ICN::BUTTON_MAPSIZE_XLARGE: - case ICN::BUTTON_MAPSIZE_ALL: - case ICN::BUTTON_MAP_SELECT_GOOD: - case ICN::BUTTON_MAP_SELECT_EVIL: - case ICN::BUTTON_STANDARD_GAME: - case ICN::BUTTON_CAMPAIGN_GAME: - case ICN::BUTTON_MULTIPLAYER_GAME: - case ICN::BUTTON_LARGE_CANCEL: - case ICN::BUTTON_LARGE_CONFIG: - case ICN::BUTTON_ORIGINAL_CAMPAIGN: - case ICN::BUTTON_EXPANSION_CAMPAIGN: - case ICN::BUTTON_HOT_SEAT: - case ICN::BUTTON_2_PLAYERS: - case ICN::BUTTON_3_PLAYERS: - case ICN::BUTTON_4_PLAYERS: - case ICN::BUTTON_5_PLAYERS: - case ICN::BUTTON_6_PLAYERS: - case ICN::BTNBATTLEONLY: - case ICN::BTNGIFT_GOOD: - case ICN::BTNGIFT_EVIL: - case ICN::UNIFORM_EVIL_MAX_BUTTON: - case ICN::UNIFORM_EVIL_MIN_BUTTON: - case ICN::UNIFORM_GOOD_MAX_BUTTON: - case ICN::UNIFORM_GOOD_MIN_BUTTON: - case ICN::UNIFORM_GOOD_OKAY_BUTTON: - case ICN::UNIFORM_EVIL_OKAY_BUTTON: - case ICN::UNIFORM_GOOD_CANCEL_BUTTON: - case ICN::UNIFORM_EVIL_CANCEL_BUTTON: - case ICN::UNIFORM_GOOD_EXIT_BUTTON: - case ICN::UNIFORM_EVIL_EXIT_BUTTON: - case ICN::BUTTON_SMALL_MIN_GOOD: - case ICN::BUTTON_SMALL_MIN_EVIL: - case ICN::BUTTON_SMALL_MAX_GOOD: - case ICN::BUTTON_SMALL_MAX_EVIL: - case ICN::BUTTON_EXIT_GOOD: - case ICN::BUTTON_RESET_GOOD: - case ICN::BUTTON_START_GOOD: - case ICN::BUTTON_CASTLE_GOOD: - case ICN::BUTTON_CASTLE_EVIL: - case ICN::BUTTON_TOWN_GOOD: - case ICN::BUTTON_TOWN_EVIL: - case ICN::BUTTON_RESTRICT_GOOD: - case ICN::BUTTON_RESTRICT_EVIL: - case ICN::BUTTON_GUILDWELL_EXIT: - case ICN::GOOD_CAMPAIGN_BUTTONS: - case ICN::EVIL_CAMPAIGN_BUTTONS: - case ICN::POL_CAMPAIGN_BUTTONS: - case ICN::BUTTON_VIEWWORLD_EXIT_GOOD: - case ICN::BUTTON_VIEWWORLD_EXIT_EVIL: - case ICN::BUTTON_VERTICAL_DISMISS: - case ICN::BUTTON_VERTICAL_EXIT: - case ICN::BUTTON_VERTICAL_PATROL: - case ICN::BUTTON_HSCORES_VERTICAL_CAMPAIGN: - case ICN::BUTTON_HSCORES_VERTICAL_EXIT: - case ICN::BUTTON_HSCORES_VERTICAL_STANDARD: - case ICN::BUTTON_RUMORS_GOOD: - case ICN::BUTTON_RUMORS_EVIL: - case ICN::BUTTON_EVENTS_GOOD: - case ICN::BUTTON_EVENTS_EVIL: - generateLanguageSpecificImages( id ); - return true; - case ICN::PHOENIX: - LoadOriginalICN( id ); - // First sprite has cropped shadow. We copy missing part from another 'almost' identical frame - if ( _icnVsSprite[id].size() >= 32 ) { - const Sprite & in = _icnVsSprite[id][32]; - Copy( in, 60, 73, _icnVsSprite[id][1], 60, 73, 14, 13 ); - Copy( in, 56, 72, _icnVsSprite[id][30], 56, 72, 18, 9 ); - } - return true; - case ICN::MONH0028: // phoenix - LoadOriginalICN( id ); - if ( _icnVsSprite[id].size() == 1 ) { - const Sprite & correctFrame = GetICN( ICN::PHOENIX, 32 ); - Copy( correctFrame, 60, 73, _icnVsSprite[id][0], 58, 70, 14, 13 ); - } - return true; - case ICN::CAVALRYR: - LoadOriginalICN( id ); - // Sprite 23 has incorrect colors, we need to replace them - if ( _icnVsSprite[id].size() >= 23 ) { - Sprite & out = _icnVsSprite[id][23]; - - std::vector indexes( 256 ); - std::iota( indexes.begin(), indexes.end(), static_cast( 0 ) ); - - indexes[69] = 187; - indexes[71] = 195; - indexes[73] = 188; - indexes[74] = 190; - indexes[75] = 193; - indexes[76] = 191; - indexes[77] = 195; - indexes[80] = 195; - indexes[81] = 196; - indexes[83] = 196; - indexes[84] = 197; - indexes[151] = 197; - - ApplyPalette( out, indexes ); - } - return true; - case ICN::TITANMSL: - LoadOriginalICN( id ); - if ( _icnVsSprite[id].size() == 7 ) { - // We need to shift Titan lightning arrow sprite position to correctly render it. - _icnVsSprite[id][0].setPosition( _icnVsSprite[id][0].x(), _icnVsSprite[id][0].y() - 5 ); - _icnVsSprite[id][1].setPosition( _icnVsSprite[id][1].x() - 5, _icnVsSprite[id][1].y() - 5 ); - _icnVsSprite[id][2].setPosition( _icnVsSprite[id][2].x() - 10, _icnVsSprite[id][2].y() ); - _icnVsSprite[id][3].setPosition( _icnVsSprite[id][3].x() - 15, _icnVsSprite[id][3].y() ); - _icnVsSprite[id][4].setPosition( _icnVsSprite[id][4].x() - 10, _icnVsSprite[id][2].y() ); - _icnVsSprite[id][5].setPosition( _icnVsSprite[id][5].x() - 5, _icnVsSprite[id][5].y() - 5 ); - _icnVsSprite[id][6].setPosition( _icnVsSprite[id][6].x(), _icnVsSprite[id][6].y() - 5 ); - } - return true; - case ICN::TROLLMSL: - LoadOriginalICN( id ); - if ( _icnVsSprite[id].size() == 1 ) { - Sprite & out = _icnVsSprite[id][0]; - // The original sprite contains 2 pixels which are empty - if ( out.width() * out.height() > 188 && out.transform()[147] == 1 && out.transform()[188] == 1 ) { - out.transform()[147] = 0; - out.image()[147] = 22; - - out.transform()[188] = 0; - out.image()[188] = 24; + if ( id == ICN::SMALFONT ) { + // Small font in official Polish GoG version has all letters shifted 1 pixel down. + if ( crc32 == 0xE9EC7A63 ) { + for ( fheroes2::Sprite & letter : imageArray ) { + letter.setPosition( letter.x(), letter.y() - 1 ); } } + modifyBaseSmallFont( _icnVsSprite[id] ); + } + else { + assert( id == ICN::FONT ); + // The original images contain an issue: image layer has value 50 which is '2' in UTF-8. We must correct these (only 3) places + for ( fheroes2::Sprite & fontImage : imageArray ) { + ReplaceColorIdByTransformId( fontImage, 50, 2 ); + } + modifyBaseNormalFont( _icnVsSprite[id] ); + } + + // Some checks that we really have CP1251 font + const int32_t verifiedFontWidth = ( id == ICN::FONT ) ? 19 : 12; + if ( imageArray.size() == 162 && imageArray[121].width() == verifiedFontWidth ) { + // Engine expects that letter indexes correspond to charcode - 0x20. + // In case CP1251 font.icn contains sprites for chars 0x20-0x7F, 0xC0-0xDF, 0xA8, 0xE0-0xFF, 0xB8 (in that order). + // We rearrange sprites array for corresponding sprite indexes to charcode - 0x20. + const fheroes2::Sprite firstSprite{ imageArray[0] }; + imageArray.insert( imageArray.begin() + 96, 64, firstSprite ); + std::swap( imageArray[136], imageArray[192] ); // Move sprites for chars 0xA8 + std::swap( imageArray[152], imageArray[225] ); // and 0xB8 to it's places. + imageArray.pop_back(); + imageArray.erase( imageArray.begin() + 192 ); + } + // German version uses CP1252 + if ( crc32 == 0x04745D1D || crc32 == 0xD0F0D852 ) { + const fheroes2::Sprite firstSprite{ imageArray[0] }; + imageArray.insert( imageArray.begin() + 96, 124, firstSprite ); + std::swap( imageArray[164], imageArray[224] ); + std::swap( imageArray[182], imageArray[225] ); + std::swap( imageArray[188], imageArray[226] ); + std::swap( imageArray[191], imageArray[223] ); + std::swap( imageArray[196], imageArray[220] ); + std::swap( imageArray[214], imageArray[221] ); + std::swap( imageArray[220], imageArray[222] ); + imageArray.erase( imageArray.begin() + 221, imageArray.end() ); + } + // French version has its own special encoding but should conform to CP1252 too + if ( crc32 == 0xD9556567 || crc32 == 0x406967B9 ) { + const fheroes2::Sprite firstSprite{ imageArray[0] }; + imageArray.insert( imageArray.begin() + 96, 160 - 32, firstSprite ); + imageArray[192 - 32] = imageArray[33]; + imageArray[199 - 32] = imageArray[35]; + imageArray[201 - 32] = imageArray[37]; + imageArray[202 - 32] = imageArray[37]; + imageArray[244 - 32] = imageArray[3]; + imageArray[251 - 32] = imageArray[4]; + imageArray[249 - 32] = imageArray[6]; + imageArray[226 - 32] = imageArray[10]; + imageArray[239 - 32] = imageArray[28]; + imageArray[238 - 32] = imageArray[30]; + imageArray[224 - 32] = imageArray[32]; + imageArray[231 - 32] = imageArray[62]; + imageArray[232 - 32] = imageArray[64]; + imageArray[239 - 32] = imageArray[91]; + imageArray[234 - 32] = imageArray[92]; + imageArray[238 - 32] = imageArray[93]; + imageArray[233 - 32] = imageArray[94]; + imageArray[238 - 32] = imageArray[95]; + imageArray.erase( imageArray.begin() + 252 - 32, imageArray.end() ); + } + // Italian version uses CP1252 + if ( crc32 == 0x219B3124 || crc32 == 0x1F3C3C74 ) { + const fheroes2::Sprite firstSprite{ imageArray[0] }; + imageArray.insert( imageArray.begin() + 101, 155 - 32, firstSprite ); + imageArray[192 - 32] = imageArray[33]; + imageArray[200 - 32] = imageArray[37]; + imageArray[201 - 32] = imageArray[37]; + imageArray[204 - 32] = imageArray[41]; + imageArray[210 - 32] = imageArray[47]; + imageArray[217 - 32] = imageArray[53]; + imageArray[224 - 32] = imageArray[96]; + imageArray[232 - 32] = imageArray[97]; + imageArray[233 - 32] = imageArray[69]; + imageArray[236 - 32] = imageArray[98]; + imageArray[242 - 32] = imageArray[99]; + imageArray[249 - 32] = imageArray[100]; + imageArray.erase( imageArray.begin() + 250 - 32, imageArray.end() ); + } + return true; + } + case ICN::YELLOW_FONT: + CopyICNWithPalette( id, ICN::FONT, PAL::PaletteType::YELLOW_FONT ); + return true; + case ICN::YELLOW_SMALLFONT: + CopyICNWithPalette( id, ICN::SMALFONT, PAL::PaletteType::YELLOW_FONT ); + return true; + case ICN::GRAY_FONT: + CopyICNWithPalette( id, ICN::FONT, PAL::PaletteType::GRAY_FONT ); + return true; + case ICN::GRAY_SMALL_FONT: + CopyICNWithPalette( id, ICN::SMALFONT, PAL::PaletteType::GRAY_FONT ); + return true; + case ICN::SPELLS: + LoadOriginalICN( id ); + if ( _icnVsSprite[id].size() != 60 ) { return true; - case ICN::TROLL2MSL: - loadICN( ICN::TROLLMSL ); - if ( _icnVsSprite[ICN::TROLLMSL].size() == 1 ) { - _icnVsSprite[id].resize( 1 ); - - Sprite & out = _icnVsSprite[id][0]; - out = _icnVsSprite[ICN::TROLLMSL][0]; - - // The original sprite contains 2 pixels which are empty - if ( out.width() * out.height() > 188 && out.transform()[147] == 1 && out.transform()[188] == 1 ) { - out.transform()[147] = 0; - out.image()[147] = 22; - - out.transform()[188] = 0; - out.image()[188] = 24; - } + } - std::vector indexes( 256 ); - std::iota( indexes.begin(), indexes.end(), static_cast( 0 ) ); - - indexes[10] = 152; - indexes[11] = 153; - indexes[12] = 154; - indexes[13] = 155; - indexes[14] = 155; - indexes[15] = 156; - indexes[16] = 157; - indexes[17] = 158; - indexes[18] = 159; - indexes[19] = 160; - indexes[20] = 160; - indexes[21] = 161; - indexes[22] = 162; - indexes[23] = 163; - indexes[24] = 164; - indexes[25] = 165; - indexes[26] = 166; - indexes[27] = 166; - indexes[28] = 167; - indexes[29] = 168; - indexes[30] = 169; - indexes[31] = 170; - indexes[32] = 171; - indexes[33] = 172; - indexes[34] = 172; - indexes[35] = 173; - - ApplyPalette( out, indexes ); - } - return true; - case ICN::GOLEM: - case ICN::GOLEM2: - LoadOriginalICN( id ); - // Original Golem ICN contains 40 frames. We make the corrections only for original sprite. - if ( _icnVsSprite[id].size() == 40 ) { - // Movement animation fix for Iron and Steel Golem: its 'MOVE_MAIN' animation is missing 1/4 of animation start. - // The 'MOVE_START' (for first and one cell move) has this 1/4 of animation, but 'MOVE_TILE_START` is empty, - // so we make a copy of 'MOVE_MAIN' frames to the end of sprite vector and correct their 'x' coordinate - // to cover the whole cell except the last frame, that has correct coordinates. - const size_t golemICNSize = _icnVsSprite[id].size(); - // 'MOVE_MAIN' has 7 frames and we copy only first 6. - const int32_t copyFramesNum = 6; - - _icnVsSprite[id].reserve( golemICNSize + copyFramesNum ); - // 'MOVE_MAIN' frames starts from the 6th frame in Golem ICN sprites. - size_t copyFrame = 6; - - for ( int32_t i = 0; i < copyFramesNum; ++i, ++copyFrame ) { - // IMPORTANT: we MUST do a copy of a vector element if we want to insert it to the same vector. - fheroes2::Sprite originalFrame = _icnVsSprite[id][copyFrame]; - _icnVsSprite[id].emplace_back( std::move( originalFrame ) ); - - const size_t frameID = golemICNSize + i; - // We have 7 'MOVE_MAIN' frames and 1/4 of cell to expand the horizontal movement, so we shift the first copied frame by "6*CELLW/(4*7)" to the - // left and reduce this shift every next frame by "CELLW/(7*4)". - _icnVsSprite[id][frameID].setPosition( _icnVsSprite[id][frameID].x() - ( copyFramesNum - i ) * CELLW / 28, _icnVsSprite[id][frameID].y() ); - } - } - return true; - case ICN::LOCATORE: - case ICN::LOCATORS: - LoadOriginalICN( id ); - if ( _icnVsSprite[id].size() > 15 ) { - if ( _icnVsSprite[id][12].width() == 47 ) { - Sprite & out = _icnVsSprite[id][12]; - out = Crop( out, 0, 0, out.width() - 1, out.height() ); - } - if ( _icnVsSprite[id][15].width() == 47 ) { - Sprite & out = _icnVsSprite[id][15]; - out = Crop( out, 0, 0, out.width() - 1, out.height() ); - } - } + _icnVsSprite[id].resize( 73 ); + + for ( uint32_t i = 60; i < 66; ++i ) { + // Mass Cure spell. ( when i == 60 ). + size_t originalIndex = 6; + if ( i == 61 ) { + // Mass Haste spell. + originalIndex = 14; + } + else if ( i == 62 ) { + // Mass Slow spell. + originalIndex = 1; + } + else if ( i == 63 ) { + // Mass Bless spell. + originalIndex = 7; + } + else if ( i == 64 ) { + // Mass Curse spell. + originalIndex = 3; + } + else if ( i == 65 ) { + // Mass Shield spell. + originalIndex = 15; + } + + const fheroes2::Sprite & originalImage = _icnVsSprite[id][originalIndex]; + fheroes2::Sprite & image = _icnVsSprite[id][i]; + + image.resize( originalImage.width() + 8, originalImage.height() + 8 ); + image.setPosition( originalImage.x() + 4, originalImage.y() + 4 ); + image.fill( 1 ); + + AlphaBlit( originalImage, image, 0, 0, 128 ); + AlphaBlit( originalImage, image, 4, 4, 192 ); + Blit( originalImage, image, 8, 8 ); + + AddTransparency( image, 1 ); + } + + // The Petrification spell does not have its own icon in the original game. + fheroes2::h2d::readImage( "petrification_spell_icon.image", _icnVsSprite[id][66] ); + + // Generate random spell image for Editor. + { + const fheroes2::Sprite & randomSpellImage = _icnVsSprite[id][2]; + const int32_t imageWidth = randomSpellImage.width(); + + Copy( randomSpellImage, _icnVsSprite[id][67] ); + + // Add text on random spell images. + for ( uint32_t i = 1; i < 6; ++i ) { + fheroes2::Sprite & originalImage = _icnVsSprite[id][i + 67]; + Copy( randomSpellImage, originalImage ); + + const fheroes2::Text text( std::to_string( i ), fheroes2::FontType::normalWhite() ); + text.draw( ( imageWidth - text.width() ) / 2, 22, originalImage ); + } + } + return true; + case ICN::CSLMARKER: + _icnVsSprite[id].resize( 3 ); + for ( uint32_t i = 0; i < 3; ++i ) { + _icnVsSprite[id][i] = fheroes2::AGG::GetICN( ICN::LOCATORS, 24 ); + if ( i == 1 ) { + ReplaceColorId( _icnVsSprite[id][i], 0x0A, 0xD6 ); + } + else if ( i == 2 ) { + ReplaceColorId( _icnVsSprite[id][i], 0x0A, 0xDE ); + } + } + return true; + case ICN::BUYMAX: + case ICN::BUTTON_NEW_GAME_GOOD: + case ICN::BUTTON_NEW_GAME_EVIL: + case ICN::BUTTON_SAVE_GAME_GOOD: + case ICN::BUTTON_SAVE_GAME_EVIL: + case ICN::BUTTON_LOAD_GAME_GOOD: + case ICN::BUTTON_LOAD_GAME_EVIL: + case ICN::BUTTON_INFO_GOOD: + case ICN::BUTTON_INFO_EVIL: + case ICN::BUTTON_QUIT_GOOD: + case ICN::BUTTON_QUIT_EVIL: + case ICN::BUTTON_NEW_MAP_EVIL: + case ICN::BUTTON_NEW_MAP_GOOD: + case ICN::BUTTON_SAVE_MAP_EVIL: + case ICN::BUTTON_SAVE_MAP_GOOD: + case ICN::BUTTON_LOAD_MAP_EVIL: + case ICN::BUTTON_LOAD_MAP_GOOD: + case ICN::BUTTON_SMALL_CANCEL_GOOD: + case ICN::BUTTON_SMALL_CANCEL_EVIL: + case ICN::BUTTON_SMALL_OKAY_GOOD: + case ICN::BUTTON_SMALL_OKAY_EVIL: + case ICN::BUTTON_SMALLER_OKAY_GOOD: + case ICN::BUTTON_SMALLER_OKAY_EVIL: + case ICN::BUTTON_SMALL_ACCEPT_GOOD: + case ICN::BUTTON_SMALL_ACCEPT_EVIL: + case ICN::BUTTON_SMALL_DECLINE_GOOD: + case ICN::BUTTON_SMALL_DECLINE_EVIL: + case ICN::BUTTON_SMALL_LEARN_GOOD: + case ICN::BUTTON_SMALL_LEARN_EVIL: + case ICN::BUTTON_SMALL_TRADE_GOOD: + case ICN::BUTTON_SMALL_TRADE_EVIL: + case ICN::BUTTON_SMALL_YES_GOOD: + case ICN::BUTTON_SMALL_YES_EVIL: + case ICN::BUTTON_SMALL_NO_GOOD: + case ICN::BUTTON_SMALL_NO_EVIL: + case ICN::BUTTON_SMALL_EXIT_GOOD: + case ICN::BUTTON_SMALL_EXIT_EVIL: + case ICN::BUTTON_EXIT_HEROES_MEETING: + case ICN::BUTTON_EXIT_TOWN: + case ICN::BUTTON_EXIT_PUZZLE_DIM_DOOR_EVIL: + case ICN::BUTTON_EXIT_PUZZLE_DIM_DOOR_GOOD: + case ICN::BUTTON_SMALL_DISMISS_GOOD: + case ICN::BUTTON_SMALL_DISMISS_EVIL: + case ICN::BUTTON_SMALL_UPGRADE_GOOD: + case ICN::BUTTON_SMALL_UPGRADE_EVIL: + case ICN::BUTTON_SMALL_RESTART_GOOD: + case ICN::BUTTON_SMALL_RESTART_EVIL: + case ICN::BUTTON_KINGDOM_EXIT: + case ICN::BUTTON_KINGDOM_HEROES: + case ICN::BUTTON_KINGDOM_TOWNS: + case ICN::BUTTON_MAPSIZE_SMALL: + case ICN::BUTTON_MAPSIZE_MEDIUM: + case ICN::BUTTON_MAPSIZE_LARGE: + case ICN::BUTTON_MAPSIZE_XLARGE: + case ICN::BUTTON_MAPSIZE_ALL: + case ICN::BUTTON_MAP_SELECT_GOOD: + case ICN::BUTTON_MAP_SELECT_EVIL: + case ICN::BUTTON_STANDARD_GAME: + case ICN::BUTTON_CAMPAIGN_GAME: + case ICN::BUTTON_MULTIPLAYER_GAME: + case ICN::BUTTON_LARGE_CANCEL: + case ICN::BUTTON_LARGE_CONFIG: + case ICN::BUTTON_ORIGINAL_CAMPAIGN: + case ICN::BUTTON_EXPANSION_CAMPAIGN: + case ICN::BUTTON_HOT_SEAT: + case ICN::BUTTON_2_PLAYERS: + case ICN::BUTTON_3_PLAYERS: + case ICN::BUTTON_4_PLAYERS: + case ICN::BUTTON_5_PLAYERS: + case ICN::BUTTON_6_PLAYERS: + case ICN::BTNBATTLEONLY: + case ICN::BTNGIFT_GOOD: + case ICN::BTNGIFT_EVIL: + case ICN::UNIFORM_EVIL_MAX_BUTTON: + case ICN::UNIFORM_EVIL_MIN_BUTTON: + case ICN::UNIFORM_GOOD_MAX_BUTTON: + case ICN::UNIFORM_GOOD_MIN_BUTTON: + case ICN::UNIFORM_GOOD_OKAY_BUTTON: + case ICN::UNIFORM_EVIL_OKAY_BUTTON: + case ICN::UNIFORM_GOOD_CANCEL_BUTTON: + case ICN::UNIFORM_EVIL_CANCEL_BUTTON: + case ICN::UNIFORM_GOOD_EXIT_BUTTON: + case ICN::UNIFORM_EVIL_EXIT_BUTTON: + case ICN::BUTTON_SMALL_MIN_GOOD: + case ICN::BUTTON_SMALL_MIN_EVIL: + case ICN::BUTTON_SMALL_MAX_GOOD: + case ICN::BUTTON_SMALL_MAX_EVIL: + case ICN::BUTTON_EXIT_GOOD: + case ICN::BUTTON_RESET_GOOD: + case ICN::BUTTON_START_GOOD: + case ICN::BUTTON_CASTLE_GOOD: + case ICN::BUTTON_CASTLE_EVIL: + case ICN::BUTTON_TOWN_GOOD: + case ICN::BUTTON_TOWN_EVIL: + case ICN::BUTTON_RESTRICT_GOOD: + case ICN::BUTTON_RESTRICT_EVIL: + case ICN::BUTTON_GUILDWELL_EXIT: + case ICN::GOOD_CAMPAIGN_BUTTONS: + case ICN::EVIL_CAMPAIGN_BUTTONS: + case ICN::POL_CAMPAIGN_BUTTONS: + case ICN::BUTTON_VIEWWORLD_EXIT_GOOD: + case ICN::BUTTON_VIEWWORLD_EXIT_EVIL: + case ICN::BUTTON_VERTICAL_DISMISS: + case ICN::BUTTON_VERTICAL_EXIT: + case ICN::BUTTON_VERTICAL_PATROL: + case ICN::BUTTON_HSCORES_VERTICAL_CAMPAIGN: + case ICN::BUTTON_HSCORES_VERTICAL_EXIT: + case ICN::BUTTON_HSCORES_VERTICAL_STANDARD: + case ICN::BUTTON_RUMORS_GOOD: + case ICN::BUTTON_RUMORS_EVIL: + case ICN::BUTTON_EVENTS_GOOD: + case ICN::BUTTON_EVENTS_EVIL: + generateLanguageSpecificImages( id ); + return true; + case ICN::PHOENIX: + LoadOriginalICN( id ); + // First sprite has cropped shadow. We copy missing part from another 'almost' identical frame + if ( _icnVsSprite[id].size() >= 32 ) { + const fheroes2::Sprite & in = _icnVsSprite[id][32]; + Copy( in, 60, 73, _icnVsSprite[id][1], 60, 73, 14, 13 ); + Copy( in, 56, 72, _icnVsSprite[id][30], 56, 72, 18, 9 ); + } + return true; + case ICN::MONH0028: // phoenix + LoadOriginalICN( id ); + if ( _icnVsSprite[id].size() == 1 ) { + const fheroes2::Sprite & correctFrame = fheroes2::AGG::GetICN( ICN::PHOENIX, 32 ); + Copy( correctFrame, 60, 73, _icnVsSprite[id][0], 58, 70, 14, 13 ); + } + return true; + case ICN::CAVALRYR: + LoadOriginalICN( id ); + // fheroes2::Sprite 23 has incorrect colors, we need to replace them + if ( _icnVsSprite[id].size() >= 23 ) { + fheroes2::Sprite & out = _icnVsSprite[id][23]; + + std::vector indexes( 256 ); + std::iota( indexes.begin(), indexes.end(), static_cast( 0 ) ); + + indexes[69] = 187; + indexes[71] = 195; + indexes[73] = 188; + indexes[74] = 190; + indexes[75] = 193; + indexes[76] = 191; + indexes[77] = 195; + indexes[80] = 195; + indexes[81] = 196; + indexes[83] = 196; + indexes[84] = 197; + indexes[151] = 197; + + ApplyPalette( out, indexes ); + } + return true; + case ICN::TITANMSL: + LoadOriginalICN( id ); + if ( _icnVsSprite[id].size() == 7 ) { + // We need to shift Titan lightning arrow sprite position to correctly render it. + _icnVsSprite[id][0].setPosition( _icnVsSprite[id][0].x(), _icnVsSprite[id][0].y() - 5 ); + _icnVsSprite[id][1].setPosition( _icnVsSprite[id][1].x() - 5, _icnVsSprite[id][1].y() - 5 ); + _icnVsSprite[id][2].setPosition( _icnVsSprite[id][2].x() - 10, _icnVsSprite[id][2].y() ); + _icnVsSprite[id][3].setPosition( _icnVsSprite[id][3].x() - 15, _icnVsSprite[id][3].y() ); + _icnVsSprite[id][4].setPosition( _icnVsSprite[id][4].x() - 10, _icnVsSprite[id][2].y() ); + _icnVsSprite[id][5].setPosition( _icnVsSprite[id][5].x() - 5, _icnVsSprite[id][5].y() - 5 ); + _icnVsSprite[id][6].setPosition( _icnVsSprite[id][6].x(), _icnVsSprite[id][6].y() - 5 ); + } + return true; + case ICN::TROLLMSL: + LoadOriginalICN( id ); + if ( _icnVsSprite[id].size() == 1 ) { + fheroes2::Sprite & out = _icnVsSprite[id][0]; + // The original sprite contains 2 pixels which are empty + if ( out.width() * out.height() > 188 && out.transform()[147] == 1 && out.transform()[188] == 1 ) { + out.transform()[147] = 0; + out.image()[147] = 22; + + out.transform()[188] = 0; + out.image()[188] = 24; + } + } + return true; + case ICN::TROLL2MSL: + loadICN( ICN::TROLLMSL ); + if ( _icnVsSprite[ICN::TROLLMSL].size() == 1 ) { + _icnVsSprite[id].resize( 1 ); - if ( _icnVsSprite[id].size() == 25 ) { - // Add random town and castle icons for Editor. - // A temporary solution: blurred Wizard castle/town in purple palette. - _icnVsSprite[id].resize( 27 ); - _icnVsSprite[id][25] = CreateHolyShoutEffect( _icnVsSprite[id][13], 1, 0 ); - _icnVsSprite[id][26] = CreateHolyShoutEffect( _icnVsSprite[id][19], 1, 0 ); - ApplyPalette( _icnVsSprite[id][25], PAL::GetPalette( PAL::PaletteType::PURPLE ) ); - ApplyPalette( _icnVsSprite[id][26], PAL::GetPalette( PAL::PaletteType::PURPLE ) ); - - // Add the '?' mark above the image. - const Text text( "? ? ?", FontType::normalWhite() ); - text.draw( ( _icnVsSprite[id][25].width() - text.width() ) / 2, 6, _icnVsSprite[id][25] ); - text.draw( ( _icnVsSprite[id][26].width() - text.width() ) / 2, 6, _icnVsSprite[id][26] ); - } - return true; - case ICN::TOWNBKG2: - LoadOriginalICN( id ); - if ( _icnVsSprite[id].size() == 1 ) { - Sprite & out = _icnVsSprite[id][0]; - // The first pixel of the original sprite has incorrect color. - if ( !out.empty() ) { - out._disableTransformLayer(); - out.image()[0] = 10; - } - } - return true; - case ICN::HSICONS: - LoadOriginalICN( id ); - if ( _icnVsSprite[id].size() > 7 ) { - Sprite & out = _icnVsSprite[id][7]; - if ( out.width() == 34 && out.height() == 19 ) { - Sprite temp; - std::swap( temp, out ); - - out.resize( temp.width() + 1, temp.height() ); - out.reset(); - Copy( temp, 0, 0, out, 1, 0, temp.width(), temp.height() ); - Copy( temp, temp.width() - 1, 10, out, 0, 10, 1, 3 ); + fheroes2::Sprite & out = _icnVsSprite[id][0]; + out = _icnVsSprite[ICN::TROLLMSL][0]; + + // The original sprite contains 2 pixels which are empty + if ( out.width() * out.height() > 188 && out.transform()[147] == 1 && out.transform()[188] == 1 ) { + out.transform()[147] = 0; + out.image()[147] = 22; + + out.transform()[188] = 0; + out.image()[188] = 24; + } + + std::vector indexes( 256 ); + std::iota( indexes.begin(), indexes.end(), static_cast( 0 ) ); + + indexes[10] = 152; + indexes[11] = 153; + indexes[12] = 154; + indexes[13] = 155; + indexes[14] = 155; + indexes[15] = 156; + indexes[16] = 157; + indexes[17] = 158; + indexes[18] = 159; + indexes[19] = 160; + indexes[20] = 160; + indexes[21] = 161; + indexes[22] = 162; + indexes[23] = 163; + indexes[24] = 164; + indexes[25] = 165; + indexes[26] = 166; + indexes[27] = 166; + indexes[28] = 167; + indexes[29] = 168; + indexes[30] = 169; + indexes[31] = 170; + indexes[32] = 171; + indexes[33] = 172; + indexes[34] = 172; + indexes[35] = 173; + + ApplyPalette( out, indexes ); + } + return true; + case ICN::GOLEM: + case ICN::GOLEM2: + LoadOriginalICN( id ); + // Original Golem ICN contains 40 frames. We make the corrections only for original sprite. + if ( _icnVsSprite[id].size() == 40 ) { + // Movement animation fix for Iron and Steel Golem: its 'MOVE_MAIN' animation is missing 1/4 of animation start. + // The 'MOVE_START' (for first and one cell move) has this 1/4 of animation, but 'MOVE_TILE_START` is empty, + // so we make a copy of 'MOVE_MAIN' frames to the end of sprite vector and correct their 'x' coordinate + // to cover the whole cell except the last frame, that has correct coordinates. + const size_t golemICNSize = _icnVsSprite[id].size(); + // 'MOVE_MAIN' has 7 frames and we copy only first 6. + const int32_t copyFramesNum = 6; + + _icnVsSprite[id].reserve( golemICNSize + copyFramesNum ); + // 'MOVE_MAIN' frames starts from the 6th frame in Golem ICN sprites. + size_t copyFrame = 6; + + for ( int32_t i = 0; i < copyFramesNum; ++i, ++copyFrame ) { + // IMPORTANT: we MUST do a copy of a vector element if we want to insert it to the same vector. + fheroes2::Sprite originalFrame = _icnVsSprite[id][copyFrame]; + _icnVsSprite[id].emplace_back( std::move( originalFrame ) ); + + const size_t frameID = golemICNSize + i; + // We have 7 'MOVE_MAIN' frames and 1/4 of cell to expand the horizontal movement, so we shift the first copied frame by "6*CELLW/(4*7)" to the + // left and reduce this shift every next frame by "CELLW/(7*4)". + _icnVsSprite[id][frameID].setPosition( _icnVsSprite[id][frameID].x() - ( copyFramesNum - i ) * CELLW / 28, _icnVsSprite[id][frameID].y() ); + } + } + return true; + case ICN::LOCATORE: + case ICN::LOCATORS: + LoadOriginalICN( id ); + if ( _icnVsSprite[id].size() > 15 ) { + if ( _icnVsSprite[id][12].width() == 47 ) { + fheroes2::Sprite & out = _icnVsSprite[id][12]; + out = Crop( out, 0, 0, out.width() - 1, out.height() ); + } + if ( _icnVsSprite[id][15].width() == 47 ) { + fheroes2::Sprite & out = _icnVsSprite[id][15]; + out = Crop( out, 0, 0, out.width() - 1, out.height() ); + } + } + + if ( _icnVsSprite[id].size() == 25 ) { + // Add random town and castle icons for Editor. + // A temporary solution: blurred Wizard castle/town in purple palette. + _icnVsSprite[id].resize( 27 ); + _icnVsSprite[id][25] = CreateHolyShoutEffect( _icnVsSprite[id][13], 1, 0 ); + _icnVsSprite[id][26] = CreateHolyShoutEffect( _icnVsSprite[id][19], 1, 0 ); + ApplyPalette( _icnVsSprite[id][25], PAL::GetPalette( PAL::PaletteType::PURPLE ) ); + ApplyPalette( _icnVsSprite[id][26], PAL::GetPalette( PAL::PaletteType::PURPLE ) ); + + // Add the '?' mark above the image. + const fheroes2::Text text( "? ? ?", fheroes2::FontType::normalWhite() ); + text.draw( ( _icnVsSprite[id][25].width() - text.width() ) / 2, 6, _icnVsSprite[id][25] ); + text.draw( ( _icnVsSprite[id][26].width() - text.width() ) / 2, 6, _icnVsSprite[id][26] ); + } + return true; + case ICN::TOWNBKG2: + LoadOriginalICN( id ); + if ( _icnVsSprite[id].size() == 1 ) { + fheroes2::Sprite & out = _icnVsSprite[id][0]; + // The first pixel of the original sprite has incorrect color. + if ( !out.empty() ) { + out._disableTransformLayer(); + out.image()[0] = 10; + } + } + return true; + case ICN::HSICONS: + LoadOriginalICN( id ); + if ( _icnVsSprite[id].size() > 7 ) { + fheroes2::Sprite & out = _icnVsSprite[id][7]; + if ( out.width() == 34 && out.height() == 19 ) { + fheroes2::Sprite temp; + std::swap( temp, out ); + + out.resize( temp.width() + 1, temp.height() ); + out.reset(); + Copy( temp, 0, 0, out, 1, 0, temp.width(), temp.height() ); + Copy( temp, temp.width() - 1, 10, out, 0, 10, 1, 3 ); + } + } + return true; + case ICN::MONS32: + LoadOriginalICN( id ); + + if ( _icnVsSprite[id].size() > 4 ) { // Veteran Pikeman + fheroes2::Sprite & modified = _icnVsSprite[id][4]; + + fheroes2::Sprite temp( modified.width(), modified.height() + 1 ); + temp.reset(); + Blit( modified, 0, 0, temp, 0, 1, modified.width(), modified.height() ); + modified = std::move( temp ); + Fill( modified, 7, 0, 4, 1, 36 ); + } + if ( _icnVsSprite[id].size() > 6 ) { // Master Swordsman + fheroes2::Sprite & modified = _icnVsSprite[id][6]; + + fheroes2::Sprite temp( modified.width(), modified.height() + 1 ); + temp.reset(); + Blit( modified, 0, 0, temp, 0, 1, modified.width(), modified.height() ); + modified = std::move( temp ); + Fill( modified, 2, 0, 5, 1, 36 ); + } + if ( _icnVsSprite[id].size() > 8 ) { // Champion + fheroes2::Sprite & modified = _icnVsSprite[id][8]; + + fheroes2::Sprite temp( modified.width(), modified.height() + 1 ); + temp.reset(); + Blit( modified, 0, 0, temp, 0, 1, modified.width(), modified.height() ); + modified = std::move( temp ); + Fill( modified, 12, 0, 5, 1, 36 ); + } + if ( _icnVsSprite[id].size() > 33 ) { + // Minotaur King original mini sprite has blue armlets. We make them gold to correspond the ICN::MINOTAU2. + fheroes2::Sprite & modified = _icnVsSprite[id][33]; + + if ( modified.width() == 20 && modified.height() == 36 ) { + // We update these pixels: 6x16, 7x16, 8x16, 5x17, 6x17, 7x17, 8x17, 6x18, 7x18, 14x18, 14x19. + for ( const uint32_t pixelNumber : { 326, 327, 328, 345, 346, 347, 348, 366, 367, 374, 394 } ) { + // The gold color gradient has -42 offset from blue color gradient. + modified.image()[pixelNumber] -= 42; } } - return true; - case ICN::MONS32: - LoadOriginalICN( id ); - - if ( _icnVsSprite[id].size() > 4 ) { // Veteran Pikeman - Sprite & modified = _icnVsSprite[id][4]; - - Sprite temp( modified.width(), modified.height() + 1 ); - temp.reset(); - Blit( modified, 0, 0, temp, 0, 1, modified.width(), modified.height() ); - modified = std::move( temp ); - Fill( modified, 7, 0, 4, 1, 36 ); - } - if ( _icnVsSprite[id].size() > 6 ) { // Master Swordsman - Sprite & modified = _icnVsSprite[id][6]; - - Sprite temp( modified.width(), modified.height() + 1 ); - temp.reset(); - Blit( modified, 0, 0, temp, 0, 1, modified.width(), modified.height() ); - modified = std::move( temp ); - Fill( modified, 2, 0, 5, 1, 36 ); - } - if ( _icnVsSprite[id].size() > 8 ) { // Champion - Sprite & modified = _icnVsSprite[id][8]; + } + if ( _icnVsSprite[id].size() > 62 ) { + const fheroes2::Point shadowOffset( -1, 2 ); + for ( size_t i = 0; i < 62; ++i ) { + fheroes2::Sprite & modified = _icnVsSprite[id][i]; + const fheroes2::Point originalOffset( modified.x(), modified.y() ); + fheroes2::Sprite temp = addShadow( modified, { -1, 2 }, 2 ); + temp.setPosition( originalOffset.x - 1, originalOffset.y + 2 ); - Sprite temp( modified.width(), modified.height() + 1 ); - temp.reset(); - Blit( modified, 0, 0, temp, 0, 1, modified.width(), modified.height() ); - modified = std::move( temp ); - Fill( modified, 12, 0, 5, 1, 36 ); - } - if ( _icnVsSprite[id].size() > 33 ) { - // Minotaur King original mini sprite has blue armlets. We make them gold to correspond the ICN::MINOTAU2. - Sprite & modified = _icnVsSprite[id][33]; - - if ( modified.width() == 20 && modified.height() == 36 ) { - // We update these pixels: 6x16, 7x16, 8x16, 5x17, 6x17, 7x17, 8x17, 6x18, 7x18, 14x18, 14x19. - for ( const uint32_t pixelNumber : { 326, 327, 328, 345, 346, 347, 348, 366, 367, 374, 394 } ) { - // The gold color gradient has -42 offset from blue color gradient. - modified.image()[pixelNumber] -= 42; - } + const fheroes2::Rect area = GetActiveROI( temp, 2 ); + if ( area.x > 0 || area.height != temp.height() ) { + const fheroes2::Point offset( temp.x() - area.x, temp.y() - temp.height() + area.y + area.height ); + modified = Crop( temp, area.x, area.y, area.width, area.height ); + modified.setPosition( offset.x, offset.y ); } - } - if ( _icnVsSprite[id].size() > 62 ) { - const Point shadowOffset( -1, 2 ); - for ( size_t i = 0; i < 62; ++i ) { - Sprite & modified = _icnVsSprite[id][i]; - const Point originalOffset( modified.x(), modified.y() ); - Sprite temp = addShadow( modified, { -1, 2 }, 2 ); - temp.setPosition( originalOffset.x - 1, originalOffset.y + 2 ); - - const Rect area = GetActiveROI( temp, 2 ); - if ( area.x > 0 || area.height != temp.height() ) { - const Point offset( temp.x() - area.x, temp.y() - temp.height() + area.y + area.height ); - modified = Crop( temp, area.x, area.y, area.width, area.height ); - modified.setPosition( offset.x, offset.y ); - } - else { - modified = std::move( temp ); - } + else { + modified = std::move( temp ); } } - if ( _icnVsSprite[id].size() > 63 && _icnVsSprite[id][63].width() == 19 && _icnVsSprite[id][63].height() == 37 ) { // Air Elemental - Sprite & modified = _icnVsSprite[id][63]; - modified.image()[19 * 9 + 9] = modified.image()[19 * 5 + 11]; - modified.transform()[19 * 9 + 9] = modified.transform()[19 * 5 + 11]; - } + } + if ( _icnVsSprite[id].size() > 63 && _icnVsSprite[id][63].width() == 19 && _icnVsSprite[id][63].height() == 37 ) { // Air Elemental + fheroes2::Sprite & modified = _icnVsSprite[id][63]; + modified.image()[19 * 9 + 9] = modified.image()[19 * 5 + 11]; + modified.transform()[19 * 9 + 9] = modified.transform()[19 * 5 + 11]; + } + + return true; + case ICN::MONSTER_SWITCH_LEFT_ARROW: + _icnVsSprite[id].resize( 2 ); + for ( uint32_t i = 0; i < 2; ++i ) { + const fheroes2::Sprite & source = fheroes2::AGG::GetICN( ICN::RECRUIT, i ); + fheroes2::Sprite & out = _icnVsSprite[id][i]; + out.resize( source.height(), source.width() ); + Transpose( source, out ); + out = Flip( out, false, true ); + out.setPosition( source.y() - static_cast( i ), source.x() ); + } + return true; + case ICN::MONSTER_SWITCH_RIGHT_ARROW: + _icnVsSprite[id].resize( 2 ); + for ( uint32_t i = 0; i < 2; ++i ) { + const fheroes2::Sprite & source = fheroes2::AGG::GetICN( ICN::RECRUIT, i + 2 ); + fheroes2::Sprite & out = _icnVsSprite[id][i]; + out.resize( source.height(), source.width() ); + Transpose( source, out ); + out = Flip( out, false, true ); + out.setPosition( source.y(), source.x() ); + } + return true; + case ICN::SURRENDR: + case ICN::SURRENDE: + LoadOriginalICN( id ); + if ( _icnVsSprite[id].size() >= 4 ) { + if ( id == ICN::SURRENDR ) { + // Fix incorrect font color on good ACCEPT button. + ReplaceColorId( _icnVsSprite[id][0], 28, 56 ); + } + // Fix pressed buttons background. + for ( const uint32_t i : { 0, 2 } ) { + fheroes2::Sprite & out = _icnVsSprite[id][i + 1]; + + fheroes2::Sprite tmp( out.width(), out.height() ); + tmp.reset(); + Copy( out, 0, 1, tmp, 1, 0, tmp.width() - 1, tmp.height() - 1 ); + CopyTransformLayer( _icnVsSprite[id][i], tmp ); - return true; - case ICN::MONSTER_SWITCH_LEFT_ARROW: - _icnVsSprite[id].resize( 2 ); - for ( uint32_t i = 0; i < 2; ++i ) { - const Sprite & source = GetICN( ICN::RECRUIT, i ); - Sprite & out = _icnVsSprite[id][i]; - out.resize( source.height(), source.width() ); - Transpose( source, out ); - out = Flip( out, false, true ); - out.setPosition( source.y() - static_cast( i ), source.x() ); - } - return true; - case ICN::MONSTER_SWITCH_RIGHT_ARROW: - _icnVsSprite[id].resize( 2 ); - for ( uint32_t i = 0; i < 2; ++i ) { - const Sprite & source = GetICN( ICN::RECRUIT, i + 2 ); - Sprite & out = _icnVsSprite[id][i]; - out.resize( source.height(), source.width() ); - Transpose( source, out ); - out = Flip( out, false, true ); - out.setPosition( source.y(), source.x() ); + out.reset(); + Copy( tmp, 1, 0, out, 0, 1, tmp.width() - 1, tmp.height() - 1 ); } - return true; - case ICN::SURRENDR: - case ICN::SURRENDE: - LoadOriginalICN( id ); - if ( _icnVsSprite[id].size() >= 4 ) { - if ( id == ICN::SURRENDR ) { - // Fix incorrect font color on good ACCEPT button. - ReplaceColorId( _icnVsSprite[id][0], 28, 56 ); - } - // Fix pressed buttons background. - for ( const uint32_t i : { 0, 2 } ) { - Sprite & out = _icnVsSprite[id][i + 1]; + } + return true; + case ICN::NON_UNIFORM_GOOD_RESTART_BUTTON: + _icnVsSprite[id].resize( 2 ); + _icnVsSprite[id][0] = Crop( fheroes2::AGG::GetICN( ICN::CAMPXTRG, 2 ), 6, 0, 108, 25 ); + _icnVsSprite[id][0].setPosition( 0, 0 ); - Sprite tmp( out.width(), out.height() ); - tmp.reset(); - Copy( out, 0, 1, tmp, 1, 0, tmp.width() - 1, tmp.height() - 1 ); - CopyTransformLayer( _icnVsSprite[id][i], tmp ); + _icnVsSprite[id][1] = fheroes2::AGG::GetICN( ICN::CAMPXTRG, 3 ); + _icnVsSprite[id][1].setPosition( 0, 0 ); - out.reset(); - Copy( tmp, 1, 0, out, 0, 1, tmp.width() - 1, tmp.height() - 1 ); - } - } - return true; - case ICN::NON_UNIFORM_GOOD_RESTART_BUTTON: - _icnVsSprite[id].resize( 2 ); - _icnVsSprite[id][0] = Crop( GetICN( ICN::CAMPXTRG, 2 ), 6, 0, 108, 25 ); - _icnVsSprite[id][0].setPosition( 0, 0 ); + // fix transparent corners + CopyTransformLayer( _icnVsSprite[id][1], _icnVsSprite[id][0] ); + return true; + case ICN::NON_UNIFORM_EVIL_RESTART_BUTTON: + _icnVsSprite[id].resize( 2 ); + _icnVsSprite[id][0] = Crop( fheroes2::AGG::GetICN( ICN::CAMPXTRE, 2 ), 4, 0, 108, 25 ); + _icnVsSprite[id][0].setPosition( 0, 0 ); - _icnVsSprite[id][1] = GetICN( ICN::CAMPXTRG, 3 ); - _icnVsSprite[id][1].setPosition( 0, 0 ); + _icnVsSprite[id][1] = fheroes2::AGG::GetICN( ICN::CAMPXTRE, 3 ); + _icnVsSprite[id][1].setPosition( 0, 0 ); - // fix transparent corners - CopyTransformLayer( _icnVsSprite[id][1], _icnVsSprite[id][0] ); - return true; - case ICN::NON_UNIFORM_EVIL_RESTART_BUTTON: - _icnVsSprite[id].resize( 2 ); - _icnVsSprite[id][0] = Crop( GetICN( ICN::CAMPXTRE, 2 ), 4, 0, 108, 25 ); - _icnVsSprite[id][0].setPosition( 0, 0 ); + // fix transparent corners + CopyTransformLayer( _icnVsSprite[id][1], _icnVsSprite[id][0] ); + return true; + case ICN::WHITE_LARGE_FONT: { + fheroes2::AGG::GetICN( ICN::FONT, 0 ); + const std::vector & original = _icnVsSprite[ICN::FONT]; + _icnVsSprite[id].resize( original.size() ); + for ( size_t i = 0; i < _icnVsSprite[id].size(); ++i ) { + const fheroes2::Sprite & in = original[i]; + fheroes2::Sprite & out = _icnVsSprite[id][i]; + out.resize( in.width() * 2, in.height() * 2 ); + SubpixelResize( in, out ); + out.setPosition( in.x() * 2, in.y() * 2 ); + } + return true; + } + case ICN::SWAP_ARROW_LEFT_TO_RIGHT: + case ICN::SWAP_ARROW_RIGHT_TO_LEFT: { + // Since the original game does not have such resources we could generate it from hero meeting sprite. + const fheroes2::Sprite & original = fheroes2::AGG::GetICN( ICN::SWAPWIN, 0 ); + std::array input; - _icnVsSprite[id][1] = GetICN( ICN::CAMPXTRE, 3 ); - _icnVsSprite[id][1].setPosition( 0, 0 ); + const int32_t width = 45; + const int32_t height = 20; - // fix transparent corners - CopyTransformLayer( _icnVsSprite[id][1], _icnVsSprite[id][0] ); - return true; - case ICN::WHITE_LARGE_FONT: { - GetICN( ICN::FONT, 0 ); - const std::vector & original = _icnVsSprite[ICN::FONT]; - _icnVsSprite[id].resize( original.size() ); - for ( size_t i = 0; i < _icnVsSprite[id].size(); ++i ) { - const Sprite & in = original[i]; - Sprite & out = _icnVsSprite[id][i]; - out.resize( in.width() * 2, in.height() * 2 ); - SubpixelResize( in, out ); - out.setPosition( in.x() * 2, in.y() * 2 ); - } - return true; + for ( fheroes2::Image & image : input ) { + image.resize( width, height ); } - case ICN::SWAP_ARROW_LEFT_TO_RIGHT: - case ICN::SWAP_ARROW_RIGHT_TO_LEFT: { - // Since the original game does not have such resources we could generate it from hero meeting sprite. - const Sprite & original = GetICN( ICN::SWAPWIN, 0 ); - std::array input; - const int32_t width = 45; - const int32_t height = 20; + Copy( original, 295, 270, input[0], 0, 0, width, height ); + Copy( original, 295, 291, input[1], 0, 0, width, height ); + Copy( original, 295, 363, input[2], 0, 0, width, height ); + Copy( original, 295, 384, input[3], 0, 0, width, height ); - for ( Image & image : input ) { - image.resize( width, height ); - } + input[1] = Flip( input[1], true, false ); + input[3] = Flip( input[3], true, false ); - Copy( original, 295, 270, input[0], 0, 0, width, height ); - Copy( original, 295, 291, input[1], 0, 0, width, height ); - Copy( original, 295, 363, input[2], 0, 0, width, height ); - Copy( original, 295, 384, input[3], 0, 0, width, height ); + fheroes2::Image out = fheroes2::ExtractCommonPattern( { &input[0], &input[1], &input[2], &input[3] } ); - input[1] = Flip( input[1], true, false ); - input[3] = Flip( input[3], true, false ); + // Here are 2 pixels which should be removed. + if ( out.width() == width && out.height() == height ) { + out.image()[40] = 0; + out.transform()[40] = 1; - Image out = ExtractCommonPattern( { &input[0], &input[1], &input[2], &input[3] } ); + out.image()[30 + 3 * width] = 0; + out.transform()[30 + 3 * width] = 1; + } - // Here are 2 pixels which should be removed. - if ( out.width() == width && out.height() == height ) { - out.image()[40] = 0; - out.transform()[40] = 1; + _icnVsSprite[id].resize( 2 ); + _icnVsSprite[id][0] = ( id == ICN::SWAP_ARROW_LEFT_TO_RIGHT ) ? out : Flip( out, true, false ); - out.image()[30 + 3 * width] = 0; - out.transform()[30 + 3 * width] = 1; - } + _icnVsSprite[id][1] = _icnVsSprite[id][0]; + _icnVsSprite[id][1].setPosition( -1, 1 ); + ApplyPalette( _icnVsSprite[id][1], 4 ); - _icnVsSprite[id].resize( 2 ); - _icnVsSprite[id][0] = ( id == ICN::SWAP_ARROW_LEFT_TO_RIGHT ) ? out : Flip( out, true, false ); - - _icnVsSprite[id][1] = _icnVsSprite[id][0]; - _icnVsSprite[id][1].setPosition( -1, 1 ); - ApplyPalette( _icnVsSprite[id][1], 4 ); + return true; + } + case ICN::EDITBTNS: + LoadOriginalICN( id ); + if ( _icnVsSprite[id].size() == 35 ) { + // We add three buttons for new object groups: Adventure, Kingdom, Monsters. + _icnVsSprite[id].resize( 41 ); + + // First make clean button sprites (pressed and released). + fheroes2::Sprite released = fheroes2::AGG::GetICN( ICN::EDITBTNS, 4 ); + fheroes2::Sprite pressed = fheroes2::AGG::GetICN( ICN::EDITBTNS, 5 ); + // Clean the image from the button. + Fill( released, 16, 6, 18, 24, 41U ); + Fill( pressed, 16, 7, 17, 23, 46U ); + + for ( size_t i = 0; i < 4; i += 2 ) { + _icnVsSprite[id][35 + i] = released; + _icnVsSprite[id][35 + 1 + i] = pressed; + } + _icnVsSprite[id][39] = std::move( released ); + _icnVsSprite[id][40] = std::move( pressed ); + + // Adventure objects button. + drawImageOnButton( fheroes2::AGG::GetICN( ICN::X_LOC1, 0 ), 39, 29, _icnVsSprite[id][35], _icnVsSprite[id][36] ); + + // Kingdom objects button. + drawImageOnButton( fheroes2::AGG::GetICN( ICN::OBJNARTI, 13 ), 39, 29, _icnVsSprite[id][37], _icnVsSprite[id][38] ); + + // Monsters objects button. + drawImageOnButton( fheroes2::AGG::GetICN( ICN::MONS32, 11 ), 39, 29, _icnVsSprite[id][39], _icnVsSprite[id][40] ); + } + return true; + case ICN::EDITBTNS_EVIL: { + loadICN( ICN::EDITBTNS ); + _icnVsSprite[id] = _icnVsSprite[ICN::EDITBTNS]; + for ( auto & image : _icnVsSprite[id] ) { + convertToEvilInterface( image, { 0, 0, image.width(), image.height() } ); + } + return true; + } + case ICN::DROPLISL_EVIL: { + loadICN( ICN::DROPLISL ); + _icnVsSprite[id] = _icnVsSprite[ICN::DROPLISL]; - return true; + // To convert the yellow borders of the drop list the combination of good-to-evil and gray palettes is used here. + const std::vector palette + = PAL::CombinePalettes( PAL::GetPalette( PAL::PaletteType::GOOD_TO_EVIL_INTERFACE ), PAL::GetPalette( PAL::PaletteType::GRAY ) ); + for ( auto & image : _icnVsSprite[id] ) { + fheroes2::ApplyPalette( image, 0, 0, image, 0, 0, image.width(), image.height(), palette ); } - case ICN::EDITBTNS: - LoadOriginalICN( id ); - if ( _icnVsSprite[id].size() == 35 ) { - // We add three buttons for new object groups: Adventure, Kingdom, Monsters. - _icnVsSprite[id].resize( 41 ); - - // First make clean button sprites (pressed and released). - Sprite released = GetICN( ICN::EDITBTNS, 4 ); - Sprite pressed = GetICN( ICN::EDITBTNS, 5 ); - // Clean the image from the button. - Fill( released, 16, 6, 18, 24, 41U ); - Fill( pressed, 16, 7, 17, 23, 46U ); - - for ( size_t i = 0; i < 4; i += 2 ) { - _icnVsSprite[id][35 + i] = released; - _icnVsSprite[id][35 + 1 + i] = pressed; - } - _icnVsSprite[id][39] = std::move( released ); - _icnVsSprite[id][40] = std::move( pressed ); - - // Adventure objects button. - drawImageOnButton( GetICN( ICN::X_LOC1, 0 ), 39, 29, _icnVsSprite[id][35], _icnVsSprite[id][36] ); - - // Kingdom objects button. - drawImageOnButton( GetICN( ICN::OBJNARTI, 13 ), 39, 29, _icnVsSprite[id][37], _icnVsSprite[id][38] ); + return true; + } + case ICN::CELLWIN_EVIL: { + loadICN( ICN::CELLWIN ); - // Monsters objects button. - drawImageOnButton( GetICN( ICN::MONS32, 11 ), 39, 29, _icnVsSprite[id][39], _icnVsSprite[id][40] ); - } - return true; - case ICN::EDITBTNS_EVIL: { - loadICN( ICN::EDITBTNS ); - _icnVsSprite[id] = _icnVsSprite[ICN::EDITBTNS]; - for ( auto & image : _icnVsSprite[id] ) { - convertToEvilInterface( image, { 0, 0, image.width(), image.height() } ); - } - return true; - } - case ICN::DROPLISL_EVIL: { - loadICN( ICN::DROPLISL ); - _icnVsSprite[id] = _icnVsSprite[ICN::DROPLISL]; + if ( _icnVsSprite[ICN::CELLWIN].size() > 18 ) { + // Convert to Evil only the first 19 images. The rest are not standard buttons and are player color related settings used in original editor. + _icnVsSprite[ICN::CELLWIN_EVIL].resize( 19 ); + std::copy( _icnVsSprite[ICN::CELLWIN].begin(), _icnVsSprite[ICN::CELLWIN].begin() + 19, _icnVsSprite[ICN::CELLWIN_EVIL].begin() ); - // To convert the yellow borders of the drop list the combination of good-to-evil and gray palettes is used here. + // To convert the yellow borders of some items the combination of good-to-evil and gray palettes is used here. const std::vector palette = PAL::CombinePalettes( PAL::GetPalette( PAL::PaletteType::GOOD_TO_EVIL_INTERFACE ), PAL::GetPalette( PAL::PaletteType::GRAY ) ); - for ( auto & image : _icnVsSprite[id] ) { + for ( fheroes2::Sprite & image : _icnVsSprite[ICN::CELLWIN_EVIL] ) { fheroes2::ApplyPalette( image, 0, 0, image, 0, 0, image.width(), image.height(), palette ); } - return true; } - case ICN::CELLWIN_EVIL: { - loadICN( ICN::CELLWIN ); - - if ( _icnVsSprite[ICN::CELLWIN].size() > 18 ) { - // Convert to Evil only the first 19 images. The rest are not standard buttons and are player color related settings used in original editor. - _icnVsSprite[ICN::CELLWIN_EVIL].resize( 19 ); - std::copy( _icnVsSprite[ICN::CELLWIN].begin(), _icnVsSprite[ICN::CELLWIN].begin() + 19, _icnVsSprite[ICN::CELLWIN_EVIL].begin() ); + return true; + } + case ICN::EDITPANL: + LoadOriginalICN( id ); + if ( _icnVsSprite[id].size() == 6 ) { + _icnVsSprite[id].resize( 18 ); - // To convert the yellow borders of some items the combination of good-to-evil and gray palettes is used here. - const std::vector palette - = PAL::CombinePalettes( PAL::GetPalette( PAL::PaletteType::GOOD_TO_EVIL_INTERFACE ), PAL::GetPalette( PAL::PaletteType::GRAY ) ); - for ( Sprite & image : _icnVsSprite[ICN::CELLWIN_EVIL] ) { - fheroes2::ApplyPalette( image, 0, 0, image, 0, 0, image.width(), image.height(), palette ); - } + // Make empty buttons for object types. + _icnVsSprite[id][6].resize( 27, 27 ); + _icnVsSprite[id][6]._disableTransformLayer(); + _icnVsSprite[id][6].reset(); + Fill( _icnVsSprite[id][6], 1, 1, 24, 24, 65U ); + for ( size_t i = 7; i < _icnVsSprite[id].size(); ++i ) { + _icnVsSprite[id][i] = _icnVsSprite[id][6]; } - return true; - } - case ICN::EDITPANL: - LoadOriginalICN( id ); - if ( _icnVsSprite[id].size() == 6 ) { - _icnVsSprite[id].resize( 18 ); - - // Make empty buttons for object types. - _icnVsSprite[id][6].resize( 27, 27 ); - _icnVsSprite[id][6]._disableTransformLayer(); - _icnVsSprite[id][6].reset(); - Fill( _icnVsSprite[id][6], 1, 1, 24, 24, 65U ); - for ( size_t i = 7; i < _icnVsSprite[id].size(); ++i ) { - _icnVsSprite[id][i] = _icnVsSprite[id][6]; - } - // Make Mountains objects button. - Blit( GetICN( ICN::MTNCRCK, 3 ), 4, 0, _icnVsSprite[id][6], 1, 1, 24, 24 ); + // Make Mountains objects button. + Blit( fheroes2::AGG::GetICN( ICN::MTNCRCK, 3 ), 4, 0, _icnVsSprite[id][6], 1, 1, 24, 24 ); - // Make Rocks objects button. - Blit( GetICN( ICN::OBJNGRAS, 41 ), 1, 0, _icnVsSprite[id][7], 1, 9, 23, 12 ); + // Make Rocks objects button. + Blit( fheroes2::AGG::GetICN( ICN::OBJNGRAS, 41 ), 1, 0, _icnVsSprite[id][7], 1, 9, 23, 12 ); - // Make Trees object button. Also used as erase terrain objects button image. - Copy( GetICN( ICN::EDITBTNS, 2 ), 13, 4, _icnVsSprite[id][8], 1, 1, 24, 24 ); + // Make Trees object button. Also used as erase terrain objects button image. + Copy( fheroes2::AGG::GetICN( ICN::EDITBTNS, 2 ), 13, 4, _icnVsSprite[id][8], 1, 1, 24, 24 ); - // Replace image contour colors with the background color. - std::vector indexes( 256 ); - std::iota( indexes.begin(), indexes.end(), static_cast( 0 ) ); + // Replace image contour colors with the background color. + std::vector indexes( 256 ); + std::iota( indexes.begin(), indexes.end(), static_cast( 0 ) ); - indexes[10] = 65U; - indexes[38] = 65U; - indexes[39] = 65U; - indexes[40] = 65U; - indexes[41] = 65U; - indexes[46] = 65U; + indexes[10] = 65U; + indexes[38] = 65U; + indexes[39] = 65U; + indexes[40] = 65U; + indexes[41] = 65U; + indexes[46] = 65U; - ApplyPalette( _icnVsSprite[id][8], indexes ); + ApplyPalette( _icnVsSprite[id][8], indexes ); - // Make Landscape Water objects button. - Blit( GetICN( ICN::OBJNWAT2, 0 ), 0, 3, _icnVsSprite[id][9], 5, 1, 13, 3 ); - Blit( GetICN( ICN::OBJNWAT2, 2 ), 5, 0, _icnVsSprite[id][9], 1, 4, 24, 21 ); + // Make Landscape Water objects button. + Blit( fheroes2::AGG::GetICN( ICN::OBJNWAT2, 0 ), 0, 3, _icnVsSprite[id][9], 5, 1, 13, 3 ); + Blit( fheroes2::AGG::GetICN( ICN::OBJNWAT2, 2 ), 5, 0, _icnVsSprite[id][9], 1, 4, 24, 21 ); - // Make Landscape Miscellaneous objects button. - Blit( GetICN( ICN::OBJNDIRT, 73 ), 8, 0, _icnVsSprite[id][10], 1, 1, 24, 24 ); + // Make Landscape Miscellaneous objects button. + Blit( fheroes2::AGG::GetICN( ICN::OBJNDIRT, 73 ), 8, 0, _icnVsSprite[id][10], 1, 1, 24, 24 ); - // Make Dwellings button image. - Blit( GetICN( ICN::OBJNMULT, 114 ), 7, 0, _icnVsSprite[id][11], 1, 1, 24, 24 ); + // Make Dwellings button image. + Blit( fheroes2::AGG::GetICN( ICN::OBJNMULT, 114 ), 7, 0, _icnVsSprite[id][11], 1, 1, 24, 24 ); - // Make Mines button image. - Blit( GetICN( ICN::MTNMULT, 82 ), 8, 4, _icnVsSprite[id][12], 1, 1, 24, 24 ); + // Make Mines button image. + Blit( fheroes2::AGG::GetICN( ICN::MTNMULT, 82 ), 8, 4, _icnVsSprite[id][12], 1, 1, 24, 24 ); - // Make Power-ups button image. - Blit( GetICN( ICN::OBJNMULT, 72 ), 0, 6, _icnVsSprite[id][13], 1, 1, 24, 24 ); + // Make Power-ups button image. + Blit( fheroes2::AGG::GetICN( ICN::OBJNMULT, 72 ), 0, 6, _icnVsSprite[id][13], 1, 1, 24, 24 ); - // Make Adventure Water objects button. - Blit( GetICN( ICN::OBJNWATR, 24 ), 3, 0, _icnVsSprite[id][14], 1, 1, 24, 24 ); + // Make Adventure Water objects button. + Blit( fheroes2::AGG::GetICN( ICN::OBJNWATR, 24 ), 3, 0, _icnVsSprite[id][14], 1, 1, 24, 24 ); - // Make Adventure Miscellaneous objects button. - Blit( GetICN( ICN::OBJNMUL2, 198 ), 2, 0, _icnVsSprite[id][15], 1, 1, 24, 24 ); + // Make Adventure Miscellaneous objects button. + Blit( fheroes2::AGG::GetICN( ICN::OBJNMUL2, 198 ), 2, 0, _icnVsSprite[id][15], 1, 1, 24, 24 ); - // Make erase Roads button image. - Blit( GetICN( ICN::ROAD, 2 ), 0, 0, _icnVsSprite[id][16], 1, 8, 24, 5 ); - Blit( GetICN( ICN::ROAD, 1 ), 1, 0, _icnVsSprite[id][16], 1, 13, 24, 5 ); + // Make erase Roads button image. + Blit( fheroes2::AGG::GetICN( ICN::ROAD, 2 ), 0, 0, _icnVsSprite[id][16], 1, 8, 24, 5 ); + Blit( fheroes2::AGG::GetICN( ICN::ROAD, 1 ), 1, 0, _icnVsSprite[id][16], 1, 13, 24, 5 ); - // Make erase Streams button image. - Blit( GetICN( ICN::STREAM, 2 ), 0, 0, _icnVsSprite[id][17], 1, 8, 24, 11 ); - } - return true; - case ICN::TWNWUP_5: - case ICN::EDITOR: - LoadOriginalICN( id ); - if ( !_icnVsSprite[id].empty() ) { - // Fix the cycling colors in original editor main menu background and Red Tower (Warlock castle screen). - ApplyPalette( _icnVsSprite[id].front(), PAL::GetPalette( PAL::PaletteType::NO_CYCLE ) ); - } - return true; - case ICN::MINIHERO: - LoadOriginalICN( id ); - if ( _icnVsSprite[id].size() == 42 ) { - const auto & noCyclePalette = PAL::GetPalette( PAL::PaletteType::NO_CYCLE ); - - // Fix cycling colors on the Green heroes' flag for Knight, Sorceress and Warlock. - ApplyPalette( _icnVsSprite[id][7], noCyclePalette ); - ApplyPalette( _icnVsSprite[id][9], noCyclePalette ); - ApplyPalette( _icnVsSprite[id][10], noCyclePalette ); - - // Fix cycling colors on the Yellow heroes' flag. - for ( size_t i = 21; i < 28; ++i ) { - ApplyPalette( _icnVsSprite[id][i], noCyclePalette ); - } + // Make erase Streams button image. + Blit( fheroes2::AGG::GetICN( ICN::STREAM, 2 ), 0, 0, _icnVsSprite[id][17], 1, 8, 24, 11 ); + } + return true; + case ICN::TWNWUP_5: + case ICN::EDITOR: + LoadOriginalICN( id ); + if ( !_icnVsSprite[id].empty() ) { + // Fix the cycling colors in original editor main menu background and Red Tower (Warlock castle screen). + ApplyPalette( _icnVsSprite[id].front(), PAL::GetPalette( PAL::PaletteType::NO_CYCLE ) ); + } + return true; + case ICN::MINIHERO: + LoadOriginalICN( id ); + if ( _icnVsSprite[id].size() == 42 ) { + const auto & noCyclePalette = PAL::GetPalette( PAL::PaletteType::NO_CYCLE ); - // Fix Blue Random hero flag. - Copy( _icnVsSprite[id][5], 1, 4, _icnVsSprite[id][6], 1, 4, 17, 7 ); - - // Fix Orange Necromancer hero flag. - Copy( _icnVsSprite[id][32], 5, 4, _icnVsSprite[id][33], 5, 4, 14, 7 ); - - // Fix Orange Random hero flag (in original assets he has a purple flag). - Copy( _icnVsSprite[id][32], 2, 4, _icnVsSprite[id][34], 2, 4, 16, 7 ); - - // Fix Knight heroes missing 2 leftmost sprite columns. - for ( size_t i = 0; i < 6; ++i ) { - Sprite & kinght = _icnVsSprite[id][i * 7]; - const Sprite & barbarian = _icnVsSprite[id][i * 7 + 1]; - const int32_t width = kinght.width(); - const int32_t height = kinght.height(); - Sprite fixed( width + 2, height ); - Copy( kinght, 1, 0, fixed, 3, 0, width, height ); - fixed.setPosition( kinght.x(), kinght.y() ); - Copy( barbarian, 0, 0, fixed, 0, 0, 3, height ); - Copy( barbarian, 3, 28, fixed, 3, 28, 1, 1 ); - kinght = std::move( fixed ); - } + // Fix cycling colors on the Green heroes' flag for Knight, Sorceress and Warlock. + ApplyPalette( _icnVsSprite[id][7], noCyclePalette ); + ApplyPalette( _icnVsSprite[id][9], noCyclePalette ); + ApplyPalette( _icnVsSprite[id][10], noCyclePalette ); + + // Fix cycling colors on the Yellow heroes' flag. + for ( size_t i = 21; i < 28; ++i ) { + ApplyPalette( _icnVsSprite[id][i], noCyclePalette ); } - return true; - case ICN::HEROES: - LoadOriginalICN( id ); - if ( !_icnVsSprite[id].empty() ) { - Sprite & original = _icnVsSprite[id][0]; - // This is the main menu image which shouldn't have any transform layer. - original._disableTransformLayer(); - if ( original.width() == 640 && original.height() == 480 ) { - // Fix incorrect pixel at position 260x305. - original.image()[195460] = 31; - } - // Since we cannot access game settings from here we are checking an existence - // of one of POL resources as an indicator for this version. - if ( !::AGG::getDataFromAggFile( ICN::GetString( ICN::X_TRACK1 ) ).empty() ) { - Sprite editorIcon; - h2d::readImage( "main_menu_editor_icon.image", editorIcon ); + // Fix Blue Random hero flag. + Copy( _icnVsSprite[id][5], 1, 4, _icnVsSprite[id][6], 1, 4, 17, 7 ); - Blit( editorIcon, 0, 0, original, editorIcon.x(), editorIcon.y(), editorIcon.width(), editorIcon.height() ); - } - } - return true; - case ICN::BTNSHNGL: - LoadOriginalICN( id ); - if ( _icnVsSprite[id].size() == 20 ) { - _icnVsSprite[id].resize( 23 ); - - h2d::readImage( "main_menu_editor_released_button.image", _icnVsSprite[id][20] ); - h2d::readImage( "main_menu_editor_highlighted_button.image", _icnVsSprite[id][21] ); - h2d::readImage( "main_menu_editor_pressed_button.image", _icnVsSprite[id][22] ); - } - return true; - case ICN::TOWNBKG3: - // Warlock town background image contains 'empty' pixels leading to appear them as black. - LoadOriginalICN( id ); - if ( !_icnVsSprite[id].empty() ) { - Sprite & original = _icnVsSprite[id][0]; - if ( original.width() == 640 && original.height() == 256 ) { - original._disableTransformLayer(); - uint8_t * imageData = original.image(); - imageData[51945] = 17; - imageData[61828] = 25; - imageData[64918] = 164; - imageData[77685] = 18; - imageData[84618] = 19; - } - } - return true; - case ICN::MINIPORT: - // Some heroes portraits have incorrect transparent pixels. - LoadOriginalICN( id ); - if ( _icnVsSprite[id].size() > 60 ) { - Sprite & original = _icnVsSprite[id][60]; - if ( original.width() == 30 && original.height() == 22 ) { - original._disableTransformLayer(); - uint8_t * imageData = original.image(); - imageData[5] = 75; - imageData[310] = 48; - imageData[358] = 64; - imageData[424] = 65; - } - } - if ( _icnVsSprite[id].size() > 61 ) { - Sprite & original = _icnVsSprite[id][61]; - if ( original.width() == 30 && original.height() == 22 ) { - original._disableTransformLayer(); - uint8_t * imageData = original.image(); - imageData[51] = 30; - imageData[80] = 28; - imageData[81] = 30; - imageData[383] = 24; - imageData[445] = 24; - } - } - if ( _icnVsSprite[id].size() > 65 ) { - Sprite & original = _icnVsSprite[id][65]; - if ( original.width() == 30 && original.height() == 22 ) { - original._disableTransformLayer(); - uint8_t * imageData = original.image(); - imageData[499] = 60; - imageData[601] = 24; - imageData[631] = 28; - } - } - if ( _icnVsSprite[id].size() > 67 ) { - Sprite & original = _icnVsSprite[id][67]; - if ( original.width() == 30 && original.height() == 22 ) { - original._disableTransformLayer(); - original.image()[42] = 28; - } + // Fix Orange Necromancer hero flag. + Copy( _icnVsSprite[id][32], 5, 4, _icnVsSprite[id][33], 5, 4, 14, 7 ); + + // Fix Orange Random hero flag (in original assets he has a purple flag). + Copy( _icnVsSprite[id][32], 2, 4, _icnVsSprite[id][34], 2, 4, 16, 7 ); + + // Fix Knight heroes missing 2 leftmost sprite columns. + for ( size_t i = 0; i < 6; ++i ) { + fheroes2::Sprite & kinght = _icnVsSprite[id][i * 7]; + const fheroes2::Sprite & barbarian = _icnVsSprite[id][i * 7 + 1]; + const int32_t width = kinght.width(); + const int32_t height = kinght.height(); + fheroes2::Sprite fixed( width + 2, height ); + Copy( kinght, 1, 0, fixed, 3, 0, width, height ); + fixed.setPosition( kinght.x(), kinght.y() ); + Copy( barbarian, 0, 0, fixed, 0, 0, 3, height ); + Copy( barbarian, 3, 28, fixed, 3, 28, 1, 1 ); + kinght = std::move( fixed ); + } + } + return true; + case ICN::HEROES: + LoadOriginalICN( id ); + if ( !_icnVsSprite[id].empty() ) { + fheroes2::Sprite & original = _icnVsSprite[id][0]; + // This is the main menu image which shouldn't have any transform layer. + original._disableTransformLayer(); + if ( original.width() == 640 && original.height() == 480 ) { + // Fix incorrect pixel at position 260x305. + original.image()[195460] = 31; } - return true; - case ICN::MINICAPT: - // Barbarian captain mini icon has bad pixel at position 22x2. - LoadOriginalICN( id ); - if ( _icnVsSprite[id].size() > 1 ) { - Sprite & original = _icnVsSprite[id][1]; - if ( original.width() == 30 && original.height() == 22 ) { - original._disableTransformLayer(); - original.image()[82] = 244; - } + + // Since we cannot access game settings from here we are checking an existence + // of one of POL resources as an indicator for this version. + if ( !::AGG::getDataFromAggFile( ICN::GetString( ICN::X_TRACK1 ) ).empty() ) { + fheroes2::Sprite editorIcon; + fheroes2::h2d::readImage( "main_menu_editor_icon.image", editorIcon ); + + Blit( editorIcon, 0, 0, original, editorIcon.x(), editorIcon.y(), editorIcon.width(), editorIcon.height() ); } - return true; - case ICN::PORT0091: - // Barbarian captain has one bad pixel. - LoadOriginalICN( id ); - if ( !_icnVsSprite[id].empty() ) { - Sprite & original = _icnVsSprite[id][0]; - if ( original.width() == 101 && original.height() == 93 ) { - original._disableTransformLayer(); - original.image()[9084] = 77; - } + } + return true; + case ICN::BTNSHNGL: + LoadOriginalICN( id ); + if ( _icnVsSprite[id].size() == 20 ) { + _icnVsSprite[id].resize( 23 ); + + fheroes2::h2d::readImage( "main_menu_editor_released_button.image", _icnVsSprite[id][20] ); + fheroes2::h2d::readImage( "main_menu_editor_highlighted_button.image", _icnVsSprite[id][21] ); + fheroes2::h2d::readImage( "main_menu_editor_pressed_button.image", _icnVsSprite[id][22] ); + } + return true; + case ICN::TOWNBKG3: + // Warlock town background image contains 'empty' pixels leading to appear them as black. + LoadOriginalICN( id ); + if ( !_icnVsSprite[id].empty() ) { + fheroes2::Sprite & original = _icnVsSprite[id][0]; + if ( original.width() == 640 && original.height() == 256 ) { + original._disableTransformLayer(); + uint8_t * imageData = original.image(); + imageData[51945] = 17; + imageData[61828] = 25; + imageData[64918] = 164; + imageData[77685] = 18; + imageData[84618] = 19; + } + } + return true; + case ICN::MINIPORT: + // Some heroes portraits have incorrect transparent pixels. + LoadOriginalICN( id ); + if ( _icnVsSprite[id].size() > 60 ) { + fheroes2::Sprite & original = _icnVsSprite[id][60]; + if ( original.width() == 30 && original.height() == 22 ) { + original._disableTransformLayer(); + uint8_t * imageData = original.image(); + imageData[5] = 75; + imageData[310] = 48; + imageData[358] = 64; + imageData[424] = 65; } - return true; - case ICN::PORT0090: - // Knight captain has multiple bad pixels. - LoadOriginalICN( id ); - if ( !_icnVsSprite[id].empty() ) { - Sprite & original = _icnVsSprite[id][0]; - if ( original.width() == 101 && original.height() == 93 ) { - original._disableTransformLayer(); - uint8_t * imageData = original.image(); - imageData[2314] = 70; - imageData[5160] = 71; - imageData[5827] = 18; - imageData[7474] = 167; - } + } + if ( _icnVsSprite[id].size() > 61 ) { + fheroes2::Sprite & original = _icnVsSprite[id][61]; + if ( original.width() == 30 && original.height() == 22 ) { + original._disableTransformLayer(); + uint8_t * imageData = original.image(); + imageData[51] = 30; + imageData[80] = 28; + imageData[81] = 30; + imageData[383] = 24; + imageData[445] = 24; } - return true; - case ICN::PORT0092: - // Sorceress captain has two bad transparent pixels (8x20 and 8x66). - LoadOriginalICN( id ); - if ( !_icnVsSprite[id].empty() ) { - Sprite & original = _icnVsSprite[id][0]; - if ( original.width() == 101 && original.height() == 93 ) { - original._disableTransformLayer(); - uint8_t * imageData = original.image(); - imageData[2028] = 42; - imageData[6674] = 100; - } + } + if ( _icnVsSprite[id].size() > 65 ) { + fheroes2::Sprite & original = _icnVsSprite[id][65]; + if ( original.width() == 30 && original.height() == 22 ) { + original._disableTransformLayer(); + uint8_t * imageData = original.image(); + imageData[499] = 60; + imageData[601] = 24; + imageData[631] = 28; } - return true; - case ICN::PORT0095: - // Necromancer captain have incorrect transparent pixel at position 8x22. - LoadOriginalICN( id ); - if ( !_icnVsSprite[id].empty() ) { - Sprite & original = _icnVsSprite[id][0]; - if ( original.width() == 101 && original.height() == 93 ) { - original._disableTransformLayer(); - original.image()[2230] = 212; - } + } + if ( _icnVsSprite[id].size() > 67 ) { + fheroes2::Sprite & original = _icnVsSprite[id][67]; + if ( original.width() == 30 && original.height() == 22 ) { + original._disableTransformLayer(); + original.image()[42] = 28; } - return true; - case ICN::CSTLWZRD: - LoadOriginalICN( id ); - if ( _icnVsSprite[id].size() >= 8 ) { - // Statue image has bad pixels. - Sprite & original = _icnVsSprite[id][7]; - if ( original.width() == 135 && original.height() == 57 ) { - original._disableTransformLayer(); - uint8_t * imageData = original.image(); - imageData[3687] = 50; - imageData[5159] = 108; - imageData[5294] = 108; - } + } + return true; + case ICN::MINICAPT: + // Barbarian captain mini icon has bad pixel at position 22x2. + LoadOriginalICN( id ); + if ( _icnVsSprite[id].size() > 1 ) { + fheroes2::Sprite & original = _icnVsSprite[id][1]; + if ( original.width() == 30 && original.height() == 22 ) { + original._disableTransformLayer(); + original.image()[82] = 244; } - if ( _icnVsSprite[id].size() > 22 ) { - // Cliff nest image has a glowing pixel. - ApplyPalette( _icnVsSprite[id][22], PAL::GetPalette( PAL::PaletteType::NO_CYCLE ) ); + } + return true; + case ICN::PORT0091: + // Barbarian captain has one bad pixel. + LoadOriginalICN( id ); + if ( !_icnVsSprite[id].empty() ) { + fheroes2::Sprite & original = _icnVsSprite[id][0]; + if ( original.width() == 101 && original.height() == 93 ) { + original._disableTransformLayer(); + original.image()[9084] = 77; } - if ( _icnVsSprite[id].size() > 28 ) { - // Mage tower image has a bad pixel. - for ( const uint32_t index : { 23, 28 } ) { - Sprite & original = _icnVsSprite[id][index]; - if ( original.width() == 135 && original.height() == 57 ) { - original._disableTransformLayer(); - original.image()[4333] = 23; - } - } + } + return true; + case ICN::PORT0090: + // Knight captain has multiple bad pixels. + LoadOriginalICN( id ); + if ( !_icnVsSprite[id].empty() ) { + fheroes2::Sprite & original = _icnVsSprite[id][0]; + if ( original.width() == 101 && original.height() == 93 ) { + original._disableTransformLayer(); + uint8_t * imageData = original.image(); + imageData[2314] = 70; + imageData[5160] = 71; + imageData[5827] = 18; + imageData[7474] = 167; } - return true; - case ICN::CSTLCAPK: - // Knight captain has a bad pixel. - LoadOriginalICN( id ); - if ( _icnVsSprite[id].size() >= 2 ) { - Sprite & original = _icnVsSprite[id][1]; - if ( original.width() == 84 && original.height() == 81 ) { - original._disableTransformLayer(); - original.image()[4934] = 18; - } + } + return true; + case ICN::PORT0092: + // Sorceress captain has two bad transparent pixels (8x20 and 8x66). + LoadOriginalICN( id ); + if ( !_icnVsSprite[id].empty() ) { + fheroes2::Sprite & original = _icnVsSprite[id][0]; + if ( original.width() == 101 && original.height() == 93 ) { + original._disableTransformLayer(); + uint8_t * imageData = original.image(); + imageData[2028] = 42; + imageData[6674] = 100; } - return true; - case ICN::CSTLCAPW: - // Warlock captain quarters have bad pixels. - LoadOriginalICN( id ); - if ( !_icnVsSprite[id].empty() ) { - Sprite & original = _icnVsSprite[id][0]; - if ( original.width() == 84 && original.height() == 81 ) { - original._disableTransformLayer(); - uint8_t * imageData = original.image(); - imageData[1692] = 26; - imageData[2363] = 32; - imageData[2606] = 21; - imageData[2608] = 21; - } + } + return true; + case ICN::PORT0095: + // Necromancer captain have incorrect transparent pixel at position 8x22. + LoadOriginalICN( id ); + if ( !_icnVsSprite[id].empty() ) { + fheroes2::Sprite & original = _icnVsSprite[id][0]; + if ( original.width() == 101 && original.height() == 93 ) { + original._disableTransformLayer(); + original.image()[2230] = 212; } - return true; - case ICN::CSTLSORC: - LoadOriginalICN( id ); - if ( _icnVsSprite[id].size() >= 14 ) { - // Rainbow has bad pixels. - Sprite & original = _icnVsSprite[id][13]; - if ( original.width() == 135 && original.height() == 57 ) { - original._disableTransformLayer(); - uint8_t * imageData = original.image(); - imageData[2047] = 160; - imageData[2052] = 159; - imageData[2055] = 160; - imageData[2060] = 67; - imageData[2063] = 159; - imageData[2067] = 67; - imageData[2184] = 67; - imageData[2192] = 158; - imageData[3508] = 67; - imageData[3641] = 67; - imageData[3773] = 69; - imageData[3910] = 67; - imageData[4039] = 69; - imageData[4041] = 67; - imageData[4172] = 67; - imageData[4578] = 69; - } + } + return true; + case ICN::CSTLWZRD: + LoadOriginalICN( id ); + if ( _icnVsSprite[id].size() >= 8 ) { + // Statue image has bad pixels. + fheroes2::Sprite & original = _icnVsSprite[id][7]; + if ( original.width() == 135 && original.height() == 57 ) { + original._disableTransformLayer(); + uint8_t * imageData = original.image(); + imageData[3687] = 50; + imageData[5159] = 108; + imageData[5294] = 108; } - if ( _icnVsSprite[id].size() >= 25 ) { - // Red tower has bad pixels. - Sprite & original = _icnVsSprite[id][24]; + } + if ( _icnVsSprite[id].size() > 22 ) { + // Cliff nest image has a glowing pixel. + ApplyPalette( _icnVsSprite[id][22], PAL::GetPalette( PAL::PaletteType::NO_CYCLE ) ); + } + if ( _icnVsSprite[id].size() > 28 ) { + // Mage tower image has a bad pixel. + for ( const uint32_t index : { 23, 28 } ) { + fheroes2::Sprite & original = _icnVsSprite[id][index]; if ( original.width() == 135 && original.height() == 57 ) { original._disableTransformLayer(); - uint8_t * imageData = original.image(); - imageData[2830] = 165; - imageData[3101] = 165; - imageData[3221] = 69; + original.image()[4333] = 23; } } - return true; - case ICN::COLOR_CURSOR_ADVENTURE_MAP: - case ICN::MONO_CURSOR_ADVENTURE_MAP: { - // Create needed digits. - const std::vector twoPoints = { { 2, 1 }, { 3, 1 }, { 1, 2 }, { 4, 2 }, { 3, 3 }, { 2, 4 }, { 1, 5 }, { 2, 5 }, { 3, 5 }, { 4, 5 } }; - const std::vector threePoints = { { 1, 1 }, { 2, 1 }, { 3, 1 }, { 4, 2 }, { 1, 3 }, { 2, 3 }, { 3, 3 }, { 4, 4 }, { 1, 5 }, { 2, 5 }, { 3, 5 } }; - const std::vector fourPoints = { { 1, 1 }, { 3, 1 }, { 1, 2 }, { 3, 2 }, { 1, 3 }, { 2, 3 }, { 3, 3 }, { 4, 3 }, { 3, 4 }, { 3, 5 } }; - const std::vector fivePoints - = { { 1, 1 }, { 2, 1 }, { 3, 1 }, { 4, 1 }, { 1, 2 }, { 1, 3 }, { 2, 3 }, { 3, 3 }, { 4, 4 }, { 1, 5 }, { 2, 5 }, { 3, 5 } }; - const std::vector sixPoints = { { 2, 1 }, { 3, 1 }, { 1, 2 }, { 1, 3 }, { 2, 3 }, { 3, 3 }, { 1, 4 }, { 4, 4 }, { 2, 5 }, { 3, 5 } }; - const std::vector sevenPoints = { { 1, 1 }, { 2, 1 }, { 3, 1 }, { 4, 1 }, { 4, 2 }, { 3, 3 }, { 2, 4 }, { 2, 5 } }; - const std::vector plusPoints = { { 2, 1 }, { 1, 2 }, { 2, 2 }, { 3, 2 }, { 2, 3 } }; - - const bool isColorCursor = ( id == ICN::COLOR_CURSOR_ADVENTURE_MAP ); - const uint8_t digitColor = isColorCursor ? 115 : 11; - - std::vector digits( 7 ); - digits[0] = createDigit( 6, 7, twoPoints, digitColor ); - digits[1] = createDigit( 6, 7, threePoints, digitColor ); - digits[2] = createDigit( 6, 7, fourPoints, digitColor ); - digits[3] = createDigit( 6, 7, fivePoints, digitColor ); - digits[4] = createDigit( 6, 7, sixPoints, digitColor ); - digits[5] = createDigit( 6, 7, sevenPoints, digitColor ); - digits[6] = addDigit( digits[5], createDigit( 5, 5, plusPoints, digitColor ), { -1, -1 } ); - - _icnVsSprite[id].reserve( 7 * 8 ); - - const int originalCursorId = isColorCursor ? ICN::ADVMCO : ICN::MONO_CURSOR_ADVMBW; - - populateCursorIcons( _icnVsSprite[id], GetICN( originalCursorId, 4 ), digits, isColorCursor ? Point( -2, 1 ) : Point( -4, -6 ) ); - populateCursorIcons( _icnVsSprite[id], GetICN( originalCursorId, 5 ), digits, isColorCursor ? Point( 1, 1 ) : Point( -6, -6 ) ); - populateCursorIcons( _icnVsSprite[id], GetICN( originalCursorId, 6 ), digits, isColorCursor ? Point( 0, 1 ) : Point( -8, -7 ) ); - populateCursorIcons( _icnVsSprite[id], GetICN( originalCursorId, 7 ), digits, isColorCursor ? Point( -2, 1 ) : Point( -15, -8 ) ); - populateCursorIcons( _icnVsSprite[id], GetICN( originalCursorId, 8 ), digits, isColorCursor ? Point( 1, 1 ) : Point( -16, -11 ) ); - populateCursorIcons( _icnVsSprite[id], GetICN( originalCursorId, 9 ), digits, isColorCursor ? Point( -6, 1 ) : Point( -8, -1 ) ); - populateCursorIcons( _icnVsSprite[id], GetICN( originalCursorId, 28 ), digits, isColorCursor ? Point( 0, 1 ) : Point( -8, -7 ) ); - - return true; - } - case ICN::DISMISS_HERO_DISABLED_BUTTON: - case ICN::NEW_CAMPAIGN_DISABLED_BUTTON: { - _icnVsSprite[id].resize( 1 ); - - int buttonIcnId = ( id == ICN::DISMISS_HERO_DISABLED_BUTTON ) ? ICN::BUTTON_VERTICAL_DISMISS : ICN::BUTTON_CAMPAIGN_GAME; - - const Sprite & released = GetICN( buttonIcnId, 0 ); - const Sprite & pressed = GetICN( buttonIcnId, 1 ); - - Sprite & output = _icnVsSprite[id][0]; - output = released; - - ApplyPalette( output, PAL::GetPalette( PAL::PaletteType::DARKENING ) ); - - Image common = ExtractCommonPattern( { &released, &pressed } ); - common = FilterOnePixelNoise( common ); - common = FilterOnePixelNoise( common ); - common = FilterOnePixelNoise( common ); - - Blit( common, output ); - return true; } - case ICN::KNIGHT_CASTLE_RIGHT_FARM: { - _icnVsSprite[id].resize( 1 ); - Sprite & output = _icnVsSprite[id][0]; - output = GetICN( ICN::TWNKWEL2, 0 ); - - ApplyPalette( output, 28, 21, output, 28, 21, 39, 1, 8 ); - ApplyPalette( output, 0, 22, output, 0, 22, 69, 1, 8 ); - ApplyPalette( output, 0, 23, output, 0, 23, 53, 1, 8 ); - ApplyPalette( output, 0, 24, output, 0, 24, 54, 1, 8 ); - ApplyPalette( output, 0, 25, output, 0, 25, 62, 1, 8 ); - return true; + return true; + case ICN::CSTLCAPK: + // Knight captain has a bad pixel. + LoadOriginalICN( id ); + if ( _icnVsSprite[id].size() >= 2 ) { + fheroes2::Sprite & original = _icnVsSprite[id][1]; + if ( original.width() == 84 && original.height() == 81 ) { + original._disableTransformLayer(); + original.image()[4934] = 18; + } } - case ICN::KNIGHT_CASTLE_LEFT_FARM: - _icnVsSprite[id].resize( 1 ); - h2d::readImage( "knight_castle_left_farm.image", _icnVsSprite[id][0] ); - return true; - case ICN::BARBARIAN_CASTLE_CAPTAIN_QUARTERS_LEFT_SIDE: - _icnVsSprite[id].resize( 1 ); - h2d::readImage( "barbarian_castle_captain_quarter_left_side.image", _icnVsSprite[id][0] ); - return true; - case ICN::SORCERESS_CASTLE_CAPTAIN_QUARTERS_LEFT_SIDE: - _icnVsSprite[id].resize( 1 ); - h2d::readImage( "sorceress_castle_captain_quarter_left_side.image", _icnVsSprite[id][0] ); - return true; - case ICN::NECROMANCER_CASTLE_STANDALONE_CAPTAIN_QUARTERS: { - _icnVsSprite[id].resize( 1 ); - Sprite & output = _icnVsSprite[id][0]; - const Sprite & original = GetICN( ICN::TWNNCAPT, 0 ); + return true; + case ICN::CSTLCAPW: + // Warlock captain quarters have bad pixels. + LoadOriginalICN( id ); + if ( !_icnVsSprite[id].empty() ) { + fheroes2::Sprite & original = _icnVsSprite[id][0]; + if ( original.width() == 84 && original.height() == 81 ) { + original._disableTransformLayer(); + uint8_t * imageData = original.image(); + imageData[1692] = 26; + imageData[2363] = 32; + imageData[2606] = 21; + imageData[2608] = 21; + } + } + return true; + case ICN::CSTLSORC: + LoadOriginalICN( id ); + if ( _icnVsSprite[id].size() >= 14 ) { + // Rainbow has bad pixels. + fheroes2::Sprite & original = _icnVsSprite[id][13]; + if ( original.width() == 135 && original.height() == 57 ) { + original._disableTransformLayer(); + uint8_t * imageData = original.image(); + imageData[2047] = 160; + imageData[2052] = 159; + imageData[2055] = 160; + imageData[2060] = 67; + imageData[2063] = 159; + imageData[2067] = 67; + imageData[2184] = 67; + imageData[2192] = 158; + imageData[3508] = 67; + imageData[3641] = 67; + imageData[3773] = 69; + imageData[3910] = 67; + imageData[4039] = 69; + imageData[4041] = 67; + imageData[4172] = 67; + imageData[4578] = 69; + } + } + if ( _icnVsSprite[id].size() >= 25 ) { + // Red tower has bad pixels. + fheroes2::Sprite & original = _icnVsSprite[id][24]; + if ( original.width() == 135 && original.height() == 57 ) { + original._disableTransformLayer(); + uint8_t * imageData = original.image(); + imageData[2830] = 165; + imageData[3101] = 165; + imageData[3221] = 69; + } + } + return true; + case ICN::COLOR_CURSOR_ADVENTURE_MAP: + case ICN::MONO_CURSOR_ADVENTURE_MAP: { + // Create needed digits. + const std::vector twoPoints = { { 2, 1 }, { 3, 1 }, { 1, 2 }, { 4, 2 }, { 3, 3 }, { 2, 4 }, { 1, 5 }, { 2, 5 }, { 3, 5 }, { 4, 5 } }; + const std::vector threePoints + = { { 1, 1 }, { 2, 1 }, { 3, 1 }, { 4, 2 }, { 1, 3 }, { 2, 3 }, { 3, 3 }, { 4, 4 }, { 1, 5 }, { 2, 5 }, { 3, 5 } }; + const std::vector fourPoints = { { 1, 1 }, { 3, 1 }, { 1, 2 }, { 3, 2 }, { 1, 3 }, { 2, 3 }, { 3, 3 }, { 4, 3 }, { 3, 4 }, { 3, 5 } }; + const std::vector fivePoints + = { { 1, 1 }, { 2, 1 }, { 3, 1 }, { 4, 1 }, { 1, 2 }, { 1, 3 }, { 2, 3 }, { 3, 3 }, { 4, 4 }, { 1, 5 }, { 2, 5 }, { 3, 5 } }; + const std::vector sixPoints = { { 2, 1 }, { 3, 1 }, { 1, 2 }, { 1, 3 }, { 2, 3 }, { 3, 3 }, { 1, 4 }, { 4, 4 }, { 2, 5 }, { 3, 5 } }; + const std::vector sevenPoints = { { 1, 1 }, { 2, 1 }, { 3, 1 }, { 4, 1 }, { 4, 2 }, { 3, 3 }, { 2, 4 }, { 2, 5 } }; + const std::vector plusPoints = { { 2, 1 }, { 1, 2 }, { 2, 2 }, { 3, 2 }, { 2, 3 } }; + + const bool isColorCursor = ( id == ICN::COLOR_CURSOR_ADVENTURE_MAP ); + const uint8_t digitColor = isColorCursor ? 115 : 11; + + std::vector digits( 7 ); + digits[0] = createDigit( 6, 7, twoPoints, digitColor ); + digits[1] = createDigit( 6, 7, threePoints, digitColor ); + digits[2] = createDigit( 6, 7, fourPoints, digitColor ); + digits[3] = createDigit( 6, 7, fivePoints, digitColor ); + digits[4] = createDigit( 6, 7, sixPoints, digitColor ); + digits[5] = createDigit( 6, 7, sevenPoints, digitColor ); + digits[6] = addDigit( digits[5], createDigit( 5, 5, plusPoints, digitColor ), { -1, -1 } ); + + _icnVsSprite[id].reserve( 7 * 8 ); + + const int originalCursorId = isColorCursor ? ICN::ADVMCO : ICN::MONO_CURSOR_ADVMBW; + + populateCursorIcons( _icnVsSprite[id], fheroes2::AGG::GetICN( originalCursorId, 4 ), digits, + isColorCursor ? fheroes2::Point( -2, 1 ) : fheroes2::Point( -4, -6 ) ); + populateCursorIcons( _icnVsSprite[id], fheroes2::AGG::GetICN( originalCursorId, 5 ), digits, + isColorCursor ? fheroes2::Point( 1, 1 ) : fheroes2::Point( -6, -6 ) ); + populateCursorIcons( _icnVsSprite[id], fheroes2::AGG::GetICN( originalCursorId, 6 ), digits, + isColorCursor ? fheroes2::Point( 0, 1 ) : fheroes2::Point( -8, -7 ) ); + populateCursorIcons( _icnVsSprite[id], fheroes2::AGG::GetICN( originalCursorId, 7 ), digits, + isColorCursor ? fheroes2::Point( -2, 1 ) : fheroes2::Point( -15, -8 ) ); + populateCursorIcons( _icnVsSprite[id], fheroes2::AGG::GetICN( originalCursorId, 8 ), digits, + isColorCursor ? fheroes2::Point( 1, 1 ) : fheroes2::Point( -16, -11 ) ); + populateCursorIcons( _icnVsSprite[id], fheroes2::AGG::GetICN( originalCursorId, 9 ), digits, + isColorCursor ? fheroes2::Point( -6, 1 ) : fheroes2::Point( -8, -1 ) ); + populateCursorIcons( _icnVsSprite[id], fheroes2::AGG::GetICN( originalCursorId, 28 ), digits, + isColorCursor ? fheroes2::Point( 0, 1 ) : fheroes2::Point( -8, -7 ) ); + + return true; + } + case ICN::DISMISS_HERO_DISABLED_BUTTON: + case ICN::NEW_CAMPAIGN_DISABLED_BUTTON: { + _icnVsSprite[id].resize( 1 ); - output = Crop( original, 21, 0, original.width() - 21, original.height() ); - output.setPosition( original.x() + 21, original.y() ); + const int buttonIcnId = ( id == ICN::DISMISS_HERO_DISABLED_BUTTON ) ? ICN::BUTTON_VERTICAL_DISMISS : ICN::BUTTON_CAMPAIGN_GAME; - for ( int32_t y = 47; y < output.height(); ++y ) { - SetTransformPixel( output, 0, y, 1 ); - } + const fheroes2::Sprite & released = fheroes2::AGG::GetICN( buttonIcnId, 0 ); + const fheroes2::Sprite & pressed = fheroes2::AGG::GetICN( buttonIcnId, 1 ); - const Sprite & castle = GetICN( ICN::TWNNCSTL, 0 ); - Copy( castle, 402, 123, output, 1, 56, 2, 11 ); + fheroes2::Sprite & output = _icnVsSprite[id][0]; + output = released; - return true; - } - case ICN::NECROMANCER_CASTLE_CAPTAIN_QUARTERS_BRIDGE: { - _icnVsSprite[id].resize( 1 ); - Sprite & output = _icnVsSprite[id][0]; - const Sprite & original = GetICN( ICN::TWNNCAPT, 0 ); + ApplyPalette( output, PAL::GetPalette( PAL::PaletteType::DARKENING ) ); - output = Crop( original, 0, 0, 23, original.height() ); - output.setPosition( original.x(), original.y() ); + fheroes2::Image common = fheroes2::ExtractCommonPattern( { &released, &pressed } ); + common = FilterOnePixelNoise( common ); + common = FilterOnePixelNoise( common ); + common = FilterOnePixelNoise( common ); - return true; - } - case ICN::ESCROLL: - LoadOriginalICN( id ); - if ( _icnVsSprite[id].size() > 4 ) { - // fix missing black border on the right side of the "up" button - Sprite & out = _icnVsSprite[id][4]; - if ( out.width() == 16 && out.height() == 16 ) { - out._disableTransformLayer(); - Copy( out, 0, 0, out, 15, 0, 1, 16 ); - } - } - return true; - case ICN::MAP_TYPE_ICON: { - _icnVsSprite[id].resize( 3 ); - for ( Sprite & icon : _icnVsSprite[id] ) { - icon._disableTransformLayer(); - icon.resize( 17, 17 ); - icon.fill( 0 ); - } + Blit( common, output ); + return true; + } + case ICN::KNIGHT_CASTLE_RIGHT_FARM: { + _icnVsSprite[id].resize( 1 ); + fheroes2::Sprite & output = _icnVsSprite[id][0]; + output = fheroes2::AGG::GetICN( ICN::TWNKWEL2, 0 ); + + ApplyPalette( output, 28, 21, output, 28, 21, 39, 1, 8 ); + ApplyPalette( output, 0, 22, output, 0, 22, 69, 1, 8 ); + ApplyPalette( output, 0, 23, output, 0, 23, 53, 1, 8 ); + ApplyPalette( output, 0, 24, output, 0, 24, 54, 1, 8 ); + ApplyPalette( output, 0, 25, output, 0, 25, 62, 1, 8 ); + return true; + } + case ICN::KNIGHT_CASTLE_LEFT_FARM: + _icnVsSprite[id].resize( 1 ); + fheroes2::h2d::readImage( "knight_castle_left_farm.image", _icnVsSprite[id][0] ); + return true; + case ICN::BARBARIAN_CASTLE_CAPTAIN_QUARTERS_LEFT_SIDE: + _icnVsSprite[id].resize( 1 ); + fheroes2::h2d::readImage( "barbarian_castle_captain_quarter_left_side.image", _icnVsSprite[id][0] ); + return true; + case ICN::SORCERESS_CASTLE_CAPTAIN_QUARTERS_LEFT_SIDE: + _icnVsSprite[id].resize( 1 ); + fheroes2::h2d::readImage( "sorceress_castle_captain_quarter_left_side.image", _icnVsSprite[id][0] ); + return true; + case ICN::NECROMANCER_CASTLE_STANDALONE_CAPTAIN_QUARTERS: { + _icnVsSprite[id].resize( 1 ); + fheroes2::Sprite & output = _icnVsSprite[id][0]; + const fheroes2::Sprite & original = fheroes2::AGG::GetICN( ICN::TWNNCAPT, 0 ); + + output = Crop( original, 21, 0, original.width() - 21, original.height() ); + output.setPosition( original.x() + 21, original.y() ); + + for ( int32_t y = 47; y < output.height(); ++y ) { + SetTransformPixel( output, 0, y, 1 ); + } + + const fheroes2::Sprite & castle = fheroes2::AGG::GetICN( ICN::TWNNCSTL, 0 ); + Copy( castle, 402, 123, output, 1, 56, 2, 11 ); + + return true; + } + case ICN::NECROMANCER_CASTLE_CAPTAIN_QUARTERS_BRIDGE: { + _icnVsSprite[id].resize( 1 ); + fheroes2::Sprite & output = _icnVsSprite[id][0]; + const fheroes2::Sprite & original = fheroes2::AGG::GetICN( ICN::TWNNCAPT, 0 ); - const Sprite & successionWarsIcon = GetICN( ICN::ARTFX, 6 ); - const Sprite & priceOfLoyaltyIcon = GetICN( ICN::ARTFX, 90 ); - const Sprite & resurrectionIcon = GetICN( ICN::ARTFX, 101 ); + output = Crop( original, 0, 0, 23, original.height() ); + output.setPosition( original.x(), original.y() ); - if ( !successionWarsIcon.empty() ) { - Resize( successionWarsIcon, 0, 0, successionWarsIcon.width(), successionWarsIcon.height(), _icnVsSprite[id][0], 1, 1, 15, 15 ); + return true; + } + case ICN::ESCROLL: + LoadOriginalICN( id ); + if ( _icnVsSprite[id].size() > 4 ) { + // fix missing black border on the right side of the "up" button + fheroes2::Sprite & out = _icnVsSprite[id][4]; + if ( out.width() == 16 && out.height() == 16 ) { + out._disableTransformLayer(); + Copy( out, 0, 0, out, 15, 0, 1, 16 ); } + } + return true; + case ICN::MAP_TYPE_ICON: { + _icnVsSprite[id].resize( 3 ); + for ( fheroes2::Sprite & icon : _icnVsSprite[id] ) { + icon._disableTransformLayer(); + icon.resize( 17, 17 ); + icon.fill( 0 ); + } - if ( !priceOfLoyaltyIcon.empty() ) { - Resize( priceOfLoyaltyIcon, 0, 0, priceOfLoyaltyIcon.width(), priceOfLoyaltyIcon.height(), _icnVsSprite[id][1], 1, 1, 15, 15 ); - } + const fheroes2::Sprite & successionWarsIcon = fheroes2::AGG::GetICN( ICN::ARTFX, 6 ); + const fheroes2::Sprite & priceOfLoyaltyIcon = fheroes2::AGG::GetICN( ICN::ARTFX, 90 ); + const fheroes2::Sprite & resurrectionIcon = fheroes2::AGG::GetICN( ICN::ARTFX, 101 ); - if ( !resurrectionIcon.empty() ) { - Resize( resurrectionIcon, 0, 0, resurrectionIcon.width(), resurrectionIcon.height(), _icnVsSprite[id][2], 1, 1, 15, 15 ); - } + if ( !successionWarsIcon.empty() ) { + Resize( successionWarsIcon, 0, 0, successionWarsIcon.width(), successionWarsIcon.height(), _icnVsSprite[id][0], 1, 1, 15, 15 ); + } - return true; + if ( !priceOfLoyaltyIcon.empty() ) { + Resize( priceOfLoyaltyIcon, 0, 0, priceOfLoyaltyIcon.width(), priceOfLoyaltyIcon.height(), _icnVsSprite[id][1], 1, 1, 15, 15 ); } - case ICN::TWNWWEL2: { - LoadOriginalICN( id ); - if ( _icnVsSprite[id].size() == 7 ) { - if ( _icnVsSprite[id][0].width() == 122 && _icnVsSprite[id][0].height() == 226 ) { - FillTransform( _icnVsSprite[id][0], 0, 57, 56, 62, 1 ); - } - for ( size_t i = 1; i < 7; ++i ) { - Sprite & original = _icnVsSprite[id][i]; - if ( original.width() == 121 && original.height() == 151 ) { - FillTransform( original, 0, 0, 64, 39, 1 ); - } + if ( !resurrectionIcon.empty() ) { + Resize( resurrectionIcon, 0, 0, resurrectionIcon.width(), resurrectionIcon.height(), _icnVsSprite[id][2], 1, 1, 15, 15 ); + } + + return true; + } + case ICN::TWNWWEL2: { + LoadOriginalICN( id ); + if ( _icnVsSprite[id].size() == 7 ) { + if ( _icnVsSprite[id][0].width() == 122 && _icnVsSprite[id][0].height() == 226 ) { + FillTransform( _icnVsSprite[id][0], 0, 57, 56, 62, 1 ); + } + + for ( size_t i = 1; i < 7; ++i ) { + fheroes2::Sprite & original = _icnVsSprite[id][i]; + if ( original.width() == 121 && original.height() == 151 ) { + FillTransform( original, 0, 0, 64, 39, 1 ); } } - return true; } - case ICN::TWNWCAPT: { - LoadOriginalICN( id ); - if ( !_icnVsSprite[id].empty() ) { - Sprite & original = _icnVsSprite[id][0]; - if ( original.width() == 118 && original.height() ) { - // Remove shadow from left side. - FillTransform( original, 85, 84, 33, 26, 1 ); - - // Remove extra terrain at the bottom. - FillTransform( original, 0, 114, 40, 4, 1 ); - FillTransform( original, 9, 112, 51, 2, 1 ); - FillTransform( original, 35, 110, 47, 2, 1 ); - FillTransform( original, 57, 108, 51, 2, 1 ); - } + return true; + } + case ICN::TWNWCAPT: { + LoadOriginalICN( id ); + if ( !_icnVsSprite[id].empty() ) { + fheroes2::Sprite & original = _icnVsSprite[id][0]; + if ( original.width() == 118 && original.height() ) { + // Remove shadow from left side. + FillTransform( original, 85, 84, 33, 26, 1 ); + + // Remove extra terrain at the bottom. + FillTransform( original, 0, 114, 40, 4, 1 ); + FillTransform( original, 9, 112, 51, 2, 1 ); + FillTransform( original, 35, 110, 47, 2, 1 ); + FillTransform( original, 57, 108, 51, 2, 1 ); } - return true; } - case ICN::GOOD_ARMY_BUTTON: - case ICN::GOOD_MARKET_BUTTON: { - _icnVsSprite[id].resize( 2 ); + return true; + } + case ICN::GOOD_ARMY_BUTTON: + case ICN::GOOD_MARKET_BUTTON: { + _icnVsSprite[id].resize( 2 ); - loadICN( ICN::ADVBTNS ); + loadICN( ICN::ADVBTNS ); - const int releasedIndex = ( id == ICN::GOOD_ARMY_BUTTON ) ? 0 : 4; - Copy( GetICN( ICN::ADVBTNS, releasedIndex ), _icnVsSprite[id][0] ); - Copy( GetICN( ICN::ADVBTNS, releasedIndex + 1 ), _icnVsSprite[id][1] ); + const int releasedIndex = ( id == ICN::GOOD_ARMY_BUTTON ) ? 0 : 4; + Copy( fheroes2::AGG::GetICN( ICN::ADVBTNS, releasedIndex ), _icnVsSprite[id][0] ); + Copy( fheroes2::AGG::GetICN( ICN::ADVBTNS, releasedIndex + 1 ), _icnVsSprite[id][1] ); - // Make all black pixels transparent. - AddTransparency( _icnVsSprite[id][0], 36 ); - AddTransparency( _icnVsSprite[id][1], 36 ); - AddTransparency( _icnVsSprite[id][1], 61 ); // remove the extra brown border + // Make all black pixels transparent. + AddTransparency( _icnVsSprite[id][0], 36 ); + AddTransparency( _icnVsSprite[id][1], 36 ); + AddTransparency( _icnVsSprite[id][1], 61 ); // remove the extra brown border - return true; - } - case ICN::EVIL_ARMY_BUTTON: - case ICN::EVIL_MARKET_BUTTON: { - _icnVsSprite[id].resize( 2 ); + return true; + } + case ICN::EVIL_ARMY_BUTTON: + case ICN::EVIL_MARKET_BUTTON: { + _icnVsSprite[id].resize( 2 ); - loadICN( ICN::ADVEBTNS ); + loadICN( ICN::ADVEBTNS ); - const int releasedIndex = ( id == ICN::EVIL_ARMY_BUTTON ) ? 0 : 4; - Copy( GetICN( ICN::ADVEBTNS, releasedIndex ), _icnVsSprite[id][0] ); - Copy( GetICN( ICN::ADVEBTNS, releasedIndex + 1 ), _icnVsSprite[id][1] ); + const int releasedIndex = ( id == ICN::EVIL_ARMY_BUTTON ) ? 0 : 4; + Copy( fheroes2::AGG::GetICN( ICN::ADVEBTNS, releasedIndex ), _icnVsSprite[id][0] ); + Copy( fheroes2::AGG::GetICN( ICN::ADVEBTNS, releasedIndex + 1 ), _icnVsSprite[id][1] ); - // Make all black pixels transparent. - AddTransparency( _icnVsSprite[id][0], 36 ); - AddTransparency( _icnVsSprite[id][1], 36 ); + // Make all black pixels transparent. + AddTransparency( _icnVsSprite[id][0], 36 ); + AddTransparency( _icnVsSprite[id][1], 36 ); - // Add the bottom-left dark border. - Fill( _icnVsSprite[id][1], 1, 4, 1, 30, 36 ); - Fill( _icnVsSprite[id][1], 1, 34, 31, 1, 36 ); + // Add the bottom-left dark border. + Fill( _icnVsSprite[id][1], 1, 4, 1, 30, 36 ); + Fill( _icnVsSprite[id][1], 1, 34, 31, 1, 36 ); - // Restore back black pixels in the middle of the image. - Copy( _icnVsSprite[ICN::ADVEBTNS][releasedIndex], 9, 6, _icnVsSprite[id][0], 9, 6, 20, 22 ); - Copy( _icnVsSprite[ICN::ADVEBTNS][releasedIndex + 1], 8, 7, _icnVsSprite[id][1], 8, 7, 20, 22 ); + // Restore back black pixels in the middle of the image. + Copy( _icnVsSprite[ICN::ADVEBTNS][releasedIndex], 9, 6, _icnVsSprite[id][0], 9, 6, 20, 22 ); + Copy( _icnVsSprite[ICN::ADVEBTNS][releasedIndex + 1], 8, 7, _icnVsSprite[id][1], 8, 7, 20, 22 ); - return true; + return true; + } + case ICN::SPANBTN: + case ICN::SPANBTNE: + case ICN::CSPANBTN: + case ICN::CSPANBTE: { + LoadOriginalICN( id ); + if ( !_icnVsSprite[id].empty() ) { + // add missing part of the released button state on the left + fheroes2::Sprite & out = _icnVsSprite[id][0]; + + fheroes2::Sprite released( out.width() + 1, out.height() ); + released.reset(); + const uint8_t color = id == ICN::SPANBTN || id == ICN::CSPANBTN ? 57 : 32; + DrawLine( released, { 0, 3 }, { 0, out.height() - 1 }, color ); + Blit( out, released, 1, 0 ); + + out = std::move( released ); } - case ICN::SPANBTN: - case ICN::SPANBTNE: - case ICN::CSPANBTN: - case ICN::CSPANBTE: { - LoadOriginalICN( id ); - if ( !_icnVsSprite[id].empty() ) { - // add missing part of the released button state on the left - Sprite & out = _icnVsSprite[id][0]; + return true; + } + case ICN::TRADPOSE: { + LoadOriginalICN( id ); + if ( _icnVsSprite[id].size() >= 19 ) { + // fix background for TRADE and EXIT buttons + for ( const uint32_t i : { 16, 18 } ) { + fheroes2::Sprite pressed; + std::swap( pressed, _icnVsSprite[id][i] ); + AddTransparency( pressed, 25 ); // remove too dark background - Sprite released( out.width() + 1, out.height() ); - released.reset(); - const uint8_t color = id == ICN::SPANBTN || id == ICN::CSPANBTN ? 57 : 32; - DrawLine( released, { 0, 3 }, { 0, out.height() - 1 }, color ); - Blit( out, released, 1, 0 ); + // take background from the empty system button + _icnVsSprite[id][i] = fheroes2::AGG::GetICN( ICN::SYSTEME, 12 ); - out = std::move( released ); + // put back dark-gray pixels in the middle of the button + Fill( _icnVsSprite[id][i], 5, 5, 86, 17, 25 ); + Blit( pressed, _icnVsSprite[id][i] ); } - return true; } - case ICN::TRADPOSE: { - LoadOriginalICN( id ); - if ( _icnVsSprite[id].size() >= 19 ) { - // fix background for TRADE and EXIT buttons - for ( const uint32_t i : { 16, 18 } ) { - Sprite pressed; - std::swap( pressed, _icnVsSprite[id][i] ); - AddTransparency( pressed, 25 ); // remove too dark background - - // take background from the empty system button - _icnVsSprite[id][i] = GetICN( ICN::SYSTEME, 12 ); - - // put back dark-gray pixels in the middle of the button - Fill( _icnVsSprite[id][i], 5, 5, 86, 17, 25 ); - Blit( pressed, _icnVsSprite[id][i] ); - } - } - return true; + return true; + } + case ICN::RECRUIT: { + LoadOriginalICN( id ); + if ( _icnVsSprite[id].size() >= 10 ) { + // fix transparent corners on released OKAY button + CopyTransformLayer( _icnVsSprite[id][9], _icnVsSprite[id][8] ); } - case ICN::RECRUIT: { - LoadOriginalICN( id ); - if ( _icnVsSprite[id].size() >= 10 ) { - // fix transparent corners on released OKAY button - CopyTransformLayer( _icnVsSprite[id][9], _icnVsSprite[id][8] ); - } + return true; + } + case ICN::NGEXTRA: { + LoadOriginalICN( id ); + + std::vector & images = _icnVsSprite[id]; + + if ( images.size() != 82 ) { + // The game assets are wrong, skip modifications. return true; } - case ICN::NGEXTRA: { - LoadOriginalICN( id ); - - std::vector & images = _icnVsSprite[id]; - if ( images.size() != 82 ) { - // The game assets are wrong, skip modifications. - return true; + // Fix extra column at the end of AI controlled player. + for ( size_t i = 27; i < 34; ++i ) { + if ( images[i].width() == 62 && images[i].height() == 58 ) { + Copy( images[i], 58, 44, images[i], 59, 44, 1, 11 ); } + } - // Fix extra column at the end of AI controlled player. - for ( size_t i = 27; i < 34; ++i ) { - if ( images[i].width() == 62 && images[i].height() == 58 ) { - Copy( images[i], 58, 44, images[i], 59, 44, 1, 11 ); - } + for ( size_t i = 39; i < 45; ++i ) { + if ( images[i].width() == 62 && images[i].height() == 58 ) { + Copy( images[i], 58, 44, images[i], 59, 44, 1, 11 ); } + } - for ( size_t i = 39; i < 45; ++i ) { - if ( images[i].width() == 62 && images[i].height() == 58 ) { - Copy( images[i], 58, 44, images[i], 59, 44, 1, 11 ); - } + // fix transparent corners on pressed OKAY and CANCEL buttons + CopyTransformLayer( images[66], images[67] ); + CopyTransformLayer( images[68], images[69] ); + + // Add 6 special icons for the Editor. + images.resize( 82 + 6 ); + + for ( size_t i = 0; i < 6; ++i ) { + if ( images[i + 3].width() != 62 || images[i + 3].height() != 45 ) { + continue; } - // fix transparent corners on pressed OKAY and CANCEL buttons - CopyTransformLayer( images[66], images[67] ); - CopyTransformLayer( images[68], images[69] ); + fheroes2::Sprite & humonOrAiImage = images[i + 82]; + Copy( images[i + 3], humonOrAiImage ); - // Add 6 special icons for the Editor. - images.resize( 82 + 6 ); + // Fill the icon with the player's color. + Fill( humonOrAiImage, 15, 8, 32, 30, images[i + 82].image()[252] ); - for ( size_t i = 0; i < 6; ++i ) { - if ( images[i + 3].width() != 62 || images[i + 3].height() != 45 ) { - continue; - } + // Make a temporary image to cut human icon's background. + fheroes2::Image temp( 33, 35 ); + Copy( images[i + 9], 15, 5, temp, 0, 0, 33, 35 ); + ReplaceColorIdByTransformId( temp, images[i + 82].image()[252], 1U ); - Sprite & humonOrAiImage = images[i + 82]; - Copy( images[i + 3], humonOrAiImage ); + Copy( images[i + 3], 15, 8, humonOrAiImage, 27, 8, 31, 30 ); + Blit( temp, humonOrAiImage, 4, 5 ); + } - // Fill the icon with the player's color. - Fill( humonOrAiImage, 15, 8, 32, 30, images[i + 82].image()[252] ); + return true; + } + case ICN::DIFFICULTY_ICON_EASY: + case ICN::DIFFICULTY_ICON_NORMAL: + case ICN::DIFFICULTY_ICON_HARD: + case ICN::DIFFICULTY_ICON_EXPERT: + case ICN::DIFFICULTY_ICON_IMPOSSIBLE: { + const int originalIcnId = ICN::NGHSBKG; - // Make a temporary image to cut human icon's background. - Image temp( 33, 35 ); - Copy( images[i + 9], 15, 5, temp, 0, 0, 33, 35 ); - ReplaceColorIdByTransformId( temp, images[i + 82].image()[252], 1U ); + const fheroes2::Sprite & originalBackground = fheroes2::AGG::GetICN( originalIcnId, 0 ); - Copy( images[i + 3], 15, 8, humonOrAiImage, 27, 8, 31, 30 ); - Blit( temp, humonOrAiImage, 4, 5 ); - } + if ( !originalBackground.empty() ) { + _icnVsSprite[id].resize( 2 ); - return true; - } - case ICN::DIFFICULTY_ICON_EASY: - case ICN::DIFFICULTY_ICON_NORMAL: - case ICN::DIFFICULTY_ICON_HARD: - case ICN::DIFFICULTY_ICON_EXPERT: - case ICN::DIFFICULTY_ICON_IMPOSSIBLE: { - const int originalIcnId = ICN::NGHSBKG; - - const fheroes2::Sprite & originalBackground = GetICN( originalIcnId, 0 ); - - if ( !originalBackground.empty() ) { - _icnVsSprite[id].resize( 2 ); - - int32_t iconOffsetX = 0; - - switch ( id ) { - case ICN::DIFFICULTY_ICON_EASY: - iconOffsetX = 24; - break; - case ICN::DIFFICULTY_ICON_NORMAL: - iconOffsetX = 101; - break; - case ICN::DIFFICULTY_ICON_HARD: - iconOffsetX = 177; - break; - case ICN::DIFFICULTY_ICON_EXPERT: - iconOffsetX = 254; - break; - case ICN::DIFFICULTY_ICON_IMPOSSIBLE: - iconOffsetX = 331; - break; - default: - // Did you add a new difficulty? - assert( 0 ); - } + int32_t iconOffsetX = 0; - const int32_t iconSideLength = 65; - const int32_t iconOffsetY = 94; + switch ( id ) { + case ICN::DIFFICULTY_ICON_EASY: + iconOffsetX = 24; + break; + case ICN::DIFFICULTY_ICON_NORMAL: + iconOffsetX = 101; + break; + case ICN::DIFFICULTY_ICON_HARD: + iconOffsetX = 177; + break; + case ICN::DIFFICULTY_ICON_EXPERT: + iconOffsetX = 254; + break; + case ICN::DIFFICULTY_ICON_IMPOSSIBLE: + iconOffsetX = 331; + break; + default: + // Did you add a new difficulty? + assert( 0 ); + } - _icnVsSprite[id][0] = Crop( originalBackground, iconOffsetX, iconOffsetY, iconSideLength, iconSideLength ); - _icnVsSprite[id][0].setPosition( 0, 0 ); + const int32_t iconSideLength = 65; + const int32_t iconOffsetY = 94; - // Generate Evil Icons - _icnVsSprite[id][1] = _icnVsSprite[id][0]; + _icnVsSprite[id][0] = Crop( originalBackground, iconOffsetX, iconOffsetY, iconSideLength, iconSideLength ); + _icnVsSprite[id][0].setPosition( 0, 0 ); - const std::vector & goodToEvilPalette = PAL::GetPalette( PAL::PaletteType::GOOD_TO_EVIL_INTERFACE ); - fheroes2::ApplyPalette( _icnVsSprite[id][0], _icnVsSprite[id][1], goodToEvilPalette ); - } + // Generate Evil Icons + _icnVsSprite[id][1] = _icnVsSprite[id][0]; - return true; + const std::vector & goodToEvilPalette = PAL::GetPalette( PAL::PaletteType::GOOD_TO_EVIL_INTERFACE ); + fheroes2::ApplyPalette( _icnVsSprite[id][0], _icnVsSprite[id][1], goodToEvilPalette ); } - case ICN::METALLIC_BORDERED_TEXTBOX_GOOD: { - const int originalIcnId = ICN::NGHSBKG; - const fheroes2::Sprite & originalBackground = GetICN( originalIcnId, 0 ); + return true; + } + case ICN::METALLIC_BORDERED_TEXTBOX_GOOD: { + const int originalIcnId = ICN::NGHSBKG; - if ( !originalBackground.empty() ) { - _icnVsSprite[id].resize( 1 ); + const fheroes2::Sprite & originalBackground = fheroes2::AGG::GetICN( originalIcnId, 0 ); - const int32_t boxWidth = 371; - const int32_t boxHeight = 30; - const int32_t goodOriginalBoxOffsetX = 24; - const int32_t goodOriginalBoxOffsetY = 40; + if ( !originalBackground.empty() ) { + _icnVsSprite[id].resize( 1 ); - _icnVsSprite[id][0] = Crop( originalBackground, goodOriginalBoxOffsetX, goodOriginalBoxOffsetY, boxWidth, boxHeight ); - _icnVsSprite[id][0].setPosition( 0, 0 ); + const int32_t boxWidth = 371; + const int32_t boxHeight = 30; + const int32_t goodOriginalBoxOffsetX = 24; + const int32_t goodOriginalBoxOffsetY = 40; - // Copy red pattern and cover up embedded button. - const fheroes2::Sprite & redPart = fheroes2::Flip( Crop( originalBackground, 80, 45, 81, 19 ), true, false ); - Copy( redPart, 0, 0, _icnVsSprite[id][0], 284, 5, 81, 19 ); - } + _icnVsSprite[id][0] = Crop( originalBackground, goodOriginalBoxOffsetX, goodOriginalBoxOffsetY, boxWidth, boxHeight ); + _icnVsSprite[id][0].setPosition( 0, 0 ); - return true; + // Copy red pattern and cover up embedded button. + const fheroes2::Sprite & redPart = fheroes2::Flip( Crop( originalBackground, 80, 45, 81, 19 ), true, false ); + Copy( redPart, 0, 0, _icnVsSprite[id][0], 284, 5, 81, 19 ); } - case ICN::METALLIC_BORDERED_TEXTBOX_EVIL: { - const fheroes2::Sprite & originalEvilBackground = GetICN( ICN::CAMPBKGE, 0 ); - if ( !originalEvilBackground.empty() ) { - _icnVsSprite[id].resize( 1 ); + return true; + } + case ICN::METALLIC_BORDERED_TEXTBOX_EVIL: { + const fheroes2::Sprite & originalEvilBackground = fheroes2::AGG::GetICN( ICN::CAMPBKGE, 0 ); - const int32_t boxWidth = 371; - const int32_t boxHeight = 30; - _icnVsSprite[id][0].resize( boxWidth, boxHeight ); - _icnVsSprite[id][0].reset(); + if ( !originalEvilBackground.empty() ) { + _icnVsSprite[id].resize( 1 ); - const int32_t evilOriginalBoxOffsetX = 26; - const int32_t evilOriginalBoxOffsetY = 27; - const int32_t upperPartHeight = 20; + const int32_t boxWidth = 371; + const int32_t boxHeight = 30; + _icnVsSprite[id][0].resize( boxWidth, boxHeight ); + _icnVsSprite[id][0].reset(); - // The metallic box frame in campbkge is slightly taller than the one in nghsbkg. The width is the same. - Copy( originalEvilBackground, evilOriginalBoxOffsetX, evilOriginalBoxOffsetY, _icnVsSprite[id][0], 0, 0, boxWidth, upperPartHeight ); + const int32_t evilOriginalBoxOffsetX = 26; + const int32_t evilOriginalBoxOffsetY = 27; + const int32_t upperPartHeight = 20; - Copy( originalEvilBackground, evilOriginalBoxOffsetX, evilOriginalBoxOffsetY + upperPartHeight + 14, _icnVsSprite[id][0], 0, upperPartHeight, - boxWidth, 10 ); + // The metallic box frame in campbkge is slightly taller than the one in nghsbkg. The width is the same. + Copy( originalEvilBackground, evilOriginalBoxOffsetX, evilOriginalBoxOffsetY, _icnVsSprite[id][0], 0, 0, boxWidth, upperPartHeight ); - // Copy red central part. - const fheroes2::Sprite goodBox = GetICN( ICN::METALLIC_BORDERED_TEXTBOX_GOOD, 0 ); - Copy( goodBox, 6, 5, _icnVsSprite[id][0], 6, 5, 359, 19 ); - } + Copy( originalEvilBackground, evilOriginalBoxOffsetX, evilOriginalBoxOffsetY + upperPartHeight + 14, _icnVsSprite[id][0], 0, upperPartHeight, boxWidth, + 10 ); - return true; + // Copy red central part. + const fheroes2::Sprite goodBox = fheroes2::AGG::GetICN( ICN::METALLIC_BORDERED_TEXTBOX_GOOD, 0 ); + Copy( goodBox, 6, 5, _icnVsSprite[id][0], 6, 5, 359, 19 ); } - case ICN::MONO_CURSOR_ADVMBW: { - loadICN( ICN::ADVMCO ); - _icnVsSprite[id].resize( _icnVsSprite[ICN::ADVMCO].size() ); - for ( size_t i = 0; i < _icnVsSprite[id].size(); ++i ) { - std::string digit; - if ( i < 9 ) { - digit += '0'; - } - digit += std::to_string( i + 1 ); + return true; + } + case ICN::MONO_CURSOR_ADVMBW: { + loadICN( ICN::ADVMCO ); - _icnVsSprite[id][i] = loadBMPFile( std::string( "ADVMBW" ) + digit + ".BMP" ); + _icnVsSprite[id].resize( _icnVsSprite[ICN::ADVMCO].size() ); + for ( size_t i = 0; i < _icnVsSprite[id].size(); ++i ) { + std::string digit; + if ( i < 9 ) { + digit += '0'; } - return true; - } - case ICN::MONO_CURSOR_SPELBW: { - loadICN( ICN::SPELCO ); + digit += std::to_string( i + 1 ); - _icnVsSprite[id].resize( _icnVsSprite[ICN::SPELCO].size() ); - for ( size_t i = 0; i < _icnVsSprite[id].size(); ++i ) { - std::string digit; - if ( i < 10 ) { - digit += '0'; - } - digit += std::to_string( i ); + _icnVsSprite[id][i] = loadBMPFile( std::string( "ADVMBW" ) + digit + ".BMP" ); + } + return true; + } + case ICN::MONO_CURSOR_SPELBW: { + loadICN( ICN::SPELCO ); - _icnVsSprite[id][i] = loadBMPFile( std::string( "SPELBW" ) + digit + ".BMP" ); + _icnVsSprite[id].resize( _icnVsSprite[ICN::SPELCO].size() ); + for ( size_t i = 0; i < _icnVsSprite[id].size(); ++i ) { + std::string digit; + if ( i < 10 ) { + digit += '0'; } - return true; - } - case ICN::MONO_CURSOR_CMSSBW: { - loadICN( ICN::CMSECO ); + digit += std::to_string( i ); - _icnVsSprite[id].resize( _icnVsSprite[ICN::CMSECO].size() ); - for ( size_t i = 0; i < _icnVsSprite[id].size(); ++i ) { - std::string digit; - if ( i < 9 ) { - digit += '0'; - } - digit += std::to_string( i + 1 ); + _icnVsSprite[id][i] = loadBMPFile( std::string( "SPELBW" ) + digit + ".BMP" ); + } + return true; + } + case ICN::MONO_CURSOR_CMSSBW: { + loadICN( ICN::CMSECO ); - _icnVsSprite[id][i] = loadBMPFile( std::string( "CMSEBW" ) + digit + ".BMP" ); + _icnVsSprite[id].resize( _icnVsSprite[ICN::CMSECO].size() ); + for ( size_t i = 0; i < _icnVsSprite[id].size(); ++i ) { + std::string digit; + if ( i < 9 ) { + digit += '0'; } - return true; + digit += std::to_string( i + 1 ); + + _icnVsSprite[id][i] = loadBMPFile( std::string( "CMSEBW" ) + digit + ".BMP" ); } - case ICN::ADVBTNS: - case ICN::ADVEBTNS: - LoadOriginalICN( id ); - if ( _icnVsSprite[id].size() == 16 && _icnVsSprite[id][2].width() == 36 && _icnVsSprite[id][2].height() == 36 && _icnVsSprite[id][3].width() == 36 - && _icnVsSprite[id][3].height() == 36 ) { - // Add hero action button released and pressed. - _icnVsSprite[id].resize( 18 ); - Copy( _icnVsSprite[id][2], _icnVsSprite[id][16] ); - Copy( _icnVsSprite[id][3], _icnVsSprite[id][17] ); - - // Get the button's icon colors. - const uint8_t mainReleasedColor = _icnVsSprite[id][2].image()[7 * 36 + 26]; - const uint8_t mainPressedColor = _icnVsSprite[id][3].image()[8 * 36 + 25]; - const uint8_t backgroundReleasedColor = _icnVsSprite[id][2].image()[1 * 36 + 5]; - const uint8_t backgroundPressedColor = _icnVsSprite[id][3].image()[5 * 36 + 6]; - - // Clean-up the buttons' background - Fill( _icnVsSprite[id][16], 23, 5, 8, 5, backgroundReleasedColor ); - Fill( _icnVsSprite[id][16], 8, 10, 24, 8, backgroundReleasedColor ); - Fill( _icnVsSprite[id][16], 6, 18, 24, 10, backgroundReleasedColor ); - Fill( _icnVsSprite[id][17], 22, 6, 8, 5, backgroundPressedColor ); - Fill( _icnVsSprite[id][17], 7, 11, 24, 8, backgroundPressedColor ); - Fill( _icnVsSprite[id][17], 5, 19, 24, 10, backgroundPressedColor ); - - // Get the action cursor and prepare it for button. We make it a little smaller. - const Sprite & originalActionCursor = GetICN( ICN::ADVMCO, 9 ); - const int32_t actionCursorWidth = originalActionCursor.width() - 1; - const int32_t actionCursorHeight = originalActionCursor.height() - 3; - Image actionCursor( actionCursorWidth, actionCursorHeight ); - actionCursor.reset(); - - // Head. - Copy( originalActionCursor, 19, 1, actionCursor, 17, 2, 8, 5 ); - Copy( originalActionCursor, 16, 7, actionCursor, 14, 7, 12, 2 ); - actionCursor.transform()[15 + 7 * actionCursorWidth] = 1U; - // Tail. - Copy( originalActionCursor, 1, 10, actionCursor, 1, 9, 12, 11 ); - // Middle part. - Copy( originalActionCursor, 14, 10, actionCursor, 13, 9, 1, 11 ); - // Front legs. - Copy( originalActionCursor, 16, 10, actionCursor, 14, 9, 13, 11 ); - // Hind legs. - Copy( originalActionCursor, 7, 22, actionCursor, 7, 19, 7, 7 ); - - // Make contour transparent and the horse figure filled with solid color. - const int32_t actionCursorSize = actionCursorWidth * actionCursorHeight; - for ( int32_t i = 0; i < actionCursorSize; ++i ) { - if ( actionCursor.transform()[i] == 1U ) { - // Skip transparent pixel. - continue; - } - if ( actionCursor.image()[i] < 152U ) { - // It is the contour color, make it transparent. - actionCursor.transform()[i] = 1U; - } - else { - actionCursor.image()[i] = mainPressedColor; - } + return true; + } + case ICN::ADVBTNS: + case ICN::ADVEBTNS: + LoadOriginalICN( id ); + if ( _icnVsSprite[id].size() == 16 && _icnVsSprite[id][2].width() == 36 && _icnVsSprite[id][2].height() == 36 && _icnVsSprite[id][3].width() == 36 + && _icnVsSprite[id][3].height() == 36 ) { + // Add hero action button released and pressed. + _icnVsSprite[id].resize( 18 ); + Copy( _icnVsSprite[id][2], _icnVsSprite[id][16] ); + Copy( _icnVsSprite[id][3], _icnVsSprite[id][17] ); + + // Get the button's icon colors. + const uint8_t mainReleasedColor = _icnVsSprite[id][2].image()[7 * 36 + 26]; + const uint8_t mainPressedColor = _icnVsSprite[id][3].image()[8 * 36 + 25]; + const uint8_t backgroundReleasedColor = _icnVsSprite[id][2].image()[1 * 36 + 5]; + const uint8_t backgroundPressedColor = _icnVsSprite[id][3].image()[5 * 36 + 6]; + + // Clean-up the buttons' background + Fill( _icnVsSprite[id][16], 23, 5, 8, 5, backgroundReleasedColor ); + Fill( _icnVsSprite[id][16], 8, 10, 24, 8, backgroundReleasedColor ); + Fill( _icnVsSprite[id][16], 6, 18, 24, 10, backgroundReleasedColor ); + Fill( _icnVsSprite[id][17], 22, 6, 8, 5, backgroundPressedColor ); + Fill( _icnVsSprite[id][17], 7, 11, 24, 8, backgroundPressedColor ); + Fill( _icnVsSprite[id][17], 5, 19, 24, 10, backgroundPressedColor ); + + // Get the action cursor and prepare it for button. We make it a little smaller. + const fheroes2::Sprite & originalActionCursor = fheroes2::AGG::GetICN( ICN::ADVMCO, 9 ); + const int32_t actionCursorWidth = originalActionCursor.width() - 1; + const int32_t actionCursorHeight = originalActionCursor.height() - 3; + fheroes2::Image actionCursor( actionCursorWidth, actionCursorHeight ); + actionCursor.reset(); + + // Head. + Copy( originalActionCursor, 19, 1, actionCursor, 17, 2, 8, 5 ); + Copy( originalActionCursor, 16, 7, actionCursor, 14, 7, 12, 2 ); + actionCursor.transform()[15 + 7 * actionCursorWidth] = 1U; + // Tail. + Copy( originalActionCursor, 1, 10, actionCursor, 1, 9, 12, 11 ); + // Middle part. + Copy( originalActionCursor, 14, 10, actionCursor, 13, 9, 1, 11 ); + // Front legs. + Copy( originalActionCursor, 16, 10, actionCursor, 14, 9, 13, 11 ); + // Hind legs. + Copy( originalActionCursor, 7, 22, actionCursor, 7, 19, 7, 7 ); + + // Make contour transparent and the horse figure filled with solid color. + const int32_t actionCursorSize = actionCursorWidth * actionCursorHeight; + for ( int32_t i = 0; i < actionCursorSize; ++i ) { + if ( actionCursor.transform()[i] == 1U ) { + // Skip transparent pixel. + continue; } - - // Add shadows to the horse image. - updateShadow( actionCursor, { 1, -1 }, 2, true ); - updateShadow( actionCursor, { -1, 1 }, 6, true ); - updateShadow( actionCursor, { 2, -2 }, 4, true ); - Blit( actionCursor, _icnVsSprite[id][17], 4, 4 ); - - // Replace colors for the released button. - for ( int32_t i = 0; i < actionCursorSize; ++i ) { - if ( actionCursor.transform()[i] == 6U ) { - // Disable whitening transform and set white color. - actionCursor.transform()[i] = 0U; - actionCursor.image()[i] = 10U; - } + if ( actionCursor.image()[i] < 152U ) { + // It is the contour color, make it transparent. + actionCursor.transform()[i] = 1U; } - ReplaceColorId( actionCursor, mainPressedColor, mainReleasedColor ); - Blit( actionCursor, _icnVsSprite[id][16], 5, 3 ); - } - return true; - case ICN::ARTFX: - LoadOriginalICN( id ); - if ( _icnVsSprite[id].size() > 82 ) { - // Make a sprite for EDITOR_ANY_ULTIMATE_ARTIFACT used only in Editor for the special victory condition. - // A temporary solution is below. - const Sprite & originalImage = GetICN( ICN::ARTIFACT, 83 ); - SubpixelResize( originalImage, _icnVsSprite[id][82] ); - } - return true; - case ICN::ARTIFACT: - LoadOriginalICN( id ); - if ( _icnVsSprite[id].size() > 99 ) { - // This fixes "Arm of the Martyr" (#88) and " Sphere of Negation" (#99) artifacts rendering which initially has some incorrect transparent pixels. - for ( const int32_t index : { 88, 99 } ) { - Sprite & originalImage = _icnVsSprite[id][index]; - Sprite temp( originalImage.width(), originalImage.height() ); - temp.setPosition( originalImage.x(), originalImage.y() ); - temp._disableTransformLayer(); - temp.fill( 0 ); - Blit( originalImage, temp ); - originalImage = std::move( temp ); + else { + actionCursor.image()[i] = mainPressedColor; } - - // Make a sprite for EDITOR_ANY_ULTIMATE_ARTIFACT used only in Editor for the special victory condition. - // A temporary solution: apply the blur effect originally used for the Holy Shout spell and the purple palette. - Sprite & targetImage = _icnVsSprite[id][83]; - targetImage = CreateHolyShoutEffect( _icnVsSprite[id][91], 1, 0 ); - ApplyPalette( targetImage, PAL::GetPalette( PAL::PaletteType::PURPLE ) ); } - return true; - case ICN::OBJNARTI: - LoadOriginalICN( id ); - if ( _icnVsSprite[id].size() == 206 ) { - // If we have the Price of Loyalty assets we make a map sprite for the Magic Book artifact. - _icnVsSprite[id].resize( 208 ); - - // Magic book sprite shadow. - _icnVsSprite[id][206] = _icnVsSprite[id][162]; - FillTransform( _icnVsSprite[id][206], 0, 0, 5, 1, 1U ); - FillTransform( _icnVsSprite[id][206], 0, 1, 2, 1, 1U ); - FillTransform( _icnVsSprite[id][206], 2, 1, 4, 1, 3U ); - FillTransform( _icnVsSprite[id][206], 0, 2, 3, 1, 3U ); - FillTransform( _icnVsSprite[id][206], 18, 1, 2, 1, 1U ); - FillTransform( _icnVsSprite[id][206], 17, 2, 3, 1, 3U ); - FillTransform( _icnVsSprite[id][206], 20, 2, 1, 1, 1U ); - FillTransform( _icnVsSprite[id][206], 19, 3, 2, 1, 3U ); - - // Magic Book main sprite. We use sprite from the info dialog to make the map sprite. - _icnVsSprite[id][207].resize( 21, 32 ); - Copy( GetICN( ICN::ARTFX, 81 ), 6, 0, _icnVsSprite[id][207], 0, 0, 21, 32 ); - FillTransform( _icnVsSprite[id][207], 0, 0, 12, 1, 1U ); - FillTransform( _icnVsSprite[id][207], 15, 0, 6, 1, 1U ); - FillTransform( _icnVsSprite[id][207], 0, 1, 9, 1, 1U ); - FillTransform( _icnVsSprite[id][207], 16, 1, 5, 1, 1U ); - FillTransform( _icnVsSprite[id][207], 0, 2, 6, 1, 1U ); - FillTransform( _icnVsSprite[id][207], 20, 2, 1, 1, 1U ); - FillTransform( _icnVsSprite[id][207], 0, 4, 1, 1, 1U ); - FillTransform( _icnVsSprite[id][207], 0, 3, 3, 1, 1U ); - FillTransform( _icnVsSprite[id][207], 20, 25, 1, 1, 1U ); - FillTransform( _icnVsSprite[id][207], 18, 26, 3, 1, 1U ); - FillTransform( _icnVsSprite[id][207], 16, 27, 5, 1, 1U ); - FillTransform( _icnVsSprite[id][207], 14, 28, 9, 1, 1U ); - FillTransform( _icnVsSprite[id][207], 0, 29, 1, 1, 1U ); - FillTransform( _icnVsSprite[id][207], 12, 29, 11, 1, 1U ); - FillTransform( _icnVsSprite[id][207], 0, 30, 3, 1, 1U ); - FillTransform( _icnVsSprite[id][207], 10, 30, 13, 1, 1U ); - FillTransform( _icnVsSprite[id][207], 0, 31, 5, 1, 1U ); - FillTransform( _icnVsSprite[id][207], 8, 31, 15, 1, 1U ); + + // Add shadows to the horse image. + updateShadow( actionCursor, { 1, -1 }, 2, true ); + updateShadow( actionCursor, { -1, 1 }, 6, true ); + updateShadow( actionCursor, { 2, -2 }, 4, true ); + Blit( actionCursor, _icnVsSprite[id][17], 4, 4 ); + + // Replace colors for the released button. + for ( int32_t i = 0; i < actionCursorSize; ++i ) { + if ( actionCursor.transform()[i] == 6U ) { + // Disable whitening transform and set white color. + actionCursor.transform()[i] = 0U; + actionCursor.image()[i] = 10U; + } } - return true; - case ICN::TWNSDW_5: - LoadOriginalICN( id ); - if ( !_icnVsSprite[id].empty() && _icnVsSprite[id][0].width() == 140 && _icnVsSprite[id][0].height() == 165 ) { - Sprite & image = _icnVsSprite[id][0]; - // Red Tower has multiple defects. - // First one is the area between columns in middle of the Tower is prerendered. We need to remove it. - const int32_t windowBottom = 88; - FillTransform( image, 39, 68, 1, windowBottom - 68, 1 ); - FillTransform( image, 40, 67, 1, windowBottom - 67, 1 ); - FillTransform( image, 41, 66, 1, windowBottom - 66, 1 ); - FillTransform( image, 42, 65, 1, windowBottom - 65, 1 ); - FillTransform( image, 43, 66, 1, windowBottom - 66, 1 ); - FillTransform( image, 44, 67, 1, windowBottom - 67, 1 ); - FillTransform( image, 45, 71, 1, windowBottom - 71, 1 ); - FillTransform( image, 49, 70, 1, windowBottom - 70, 1 ); - FillTransform( image, 50, 68, 2, windowBottom - 68, 1 ); - FillTransform( image, 52, 69, 1, windowBottom - 69, 1 ); - FillTransform( image, 53, 74, 1, windowBottom - 74, 1 ); - FillTransform( image, 57, 70, 1, windowBottom - 70, 1 ); - FillTransform( image, 58, 67, 1, windowBottom - 67, 1 ); - FillTransform( image, 59, 66, 1, windowBottom - 66, 1 ); - FillTransform( image, 60, 65, 2, windowBottom - 65, 1 ); - FillTransform( image, 62, 67, 1, windowBottom - 67, 1 ); - FillTransform( image, 63, 69, 1, windowBottom - 69, 1 ); - FillTransform( image, 64, 72, 1, windowBottom - 72, 1 ); - - // The lower part of the tower is truncated and blocked by partial castle's sprite. The fix is done in multiple stages. - // Fix right red part of the building by copying a piece of the same wall. - Copy( image, 67, 135, image, 67, 119, 1, 1 ); - Copy( image, 67, 144, image, 67, 120, 2, 2 ); - Copy( image, 67, 134, image, 67, 122, 3, 2 ); - Copy( image, 67, 148, image, 67, 125, 1, 4 ); - - // Remove a part of the castle at the bottom left part of the image. - FillTransform( image, 62, 157, 3, 8, 1 ); - - // Top part of the castle's tower touches Red Tower level separation part. - Copy( image, 61, 101, image, 57, 101, 2, 1 ); - Copy( image, 52, 100, image, 57, 100, 2, 1 ); - - // Generate programmatically the left part of the building. - std::mt19937 seededGen( 751 ); // 751 is and ID of this sprite. To keep the changes constant we need to hardcode this value. - - fillRandomPixelsFromImage( image, { 33, 105, 4, 7 }, image, { 33, 117, 4, 39 }, seededGen ); - fillRandomPixelsFromImage( image, { 41, 105, 5, 9 }, image, { 41, 121, 5, 36 }, seededGen ); - fillRandomPixelsFromImage( image, { 46, 104, 4, 13 }, image, { 46, 118, 4, 39 }, seededGen ); - - Copy( image, 37, 113, image, 37, 115, 1, 2 ); - Copy( image, 37, 104, image, 37, 117, 1, 2 ); - Copy( image, 38, 104, image, 38, 118, 2, 1 ); - Copy( image, 37, 113, image, 38, 117, 1, 1 ); - - // Create a temporary image to be a holder of pixels. - Sprite temp( 4 * 2, 8 ); - Copy( image, 33, 105, temp, 0, 0, 4, 8 ); - Copy( image, 41, 105, temp, 4, 0, 4, 8 ); - fillRandomPixelsFromImage( temp, { 0, 0, temp.width(), temp.height() }, image, { 37, 119, 4, 37 }, seededGen ); - - Copy( image, 35, 131, image, 35, 113, 2, 4 ); - - Copy( image, 43, 133, image, 43, 115, 3, 6 ); - - // Fix the main arc. - Copy( image, 61, 102, image, 56, 102, 3, 1 ); - - // TODO: the distribution of light inside Red Tower is actually not uniform and follows the window on from th left. - // However, generating such complex image requires a lot of code so we simply make the rest of the arc uniformed filled. - fillRandomPixelsFromImage( image, { 61, 104, 2, 3 }, image, { 50, 110, 12, 47 }, seededGen ); - fillRandomPixelsFromImage( image, { 61, 104, 2, 3 }, image, { 52, 107, 9, 3 }, seededGen ); - fillRandomPixelsFromImage( image, { 61, 104, 2, 3 }, image, { 62, 111, 1, 46 }, seededGen ); - fillRandomPixelsFromImage( image, { 61, 104, 2, 3 }, image, { 63, 113, 1, 20 }, seededGen ); - fillRandomPixelsFromImage( image, { 61, 104, 2, 3 }, image, { 63, 141, 1, 16 }, seededGen ); - fillRandomPixelsFromImage( image, { 61, 104, 2, 3 }, image, { 64, 115, 1, 17 }, seededGen ); - fillRandomPixelsFromImage( image, { 61, 104, 2, 3 }, image, { 64, 152, 1, 5 }, seededGen ); - fillRandomPixelsFromImage( image, { 61, 104, 2, 3 }, image, { 65, 116, 1, 15 }, seededGen ); - fillRandomPixelsFromImage( image, { 61, 104, 2, 3 }, image, { 66, 118, 1, 12 }, seededGen ); - fillRandomPixelsFromImage( image, { 61, 104, 2, 3 }, image, { 51, 108, 1, 2 }, seededGen ); - fillRandomPixelsFromImage( image, { 61, 104, 2, 3 }, image, { 55, 103, 5, 4 }, seededGen ); - fillRandomPixelsFromImage( image, { 61, 104, 2, 3 }, image, { 61, 109, 1, 1 }, seededGen ); - fillRandomPixelsFromImage( image, { 61, 104, 2, 3 }, image, { 52, 106, 1, 1 }, seededGen ); + ReplaceColorId( actionCursor, mainPressedColor, mainReleasedColor ); + Blit( actionCursor, _icnVsSprite[id][16], 5, 3 ); + } + return true; + case ICN::ARTFX: + LoadOriginalICN( id ); + if ( _icnVsSprite[id].size() > 82 ) { + // Make a sprite for EDITOR_ANY_ULTIMATE_ARTIFACT used only in Editor for the special victory condition. + // A temporary solution is below. + const fheroes2::Sprite & originalImage = fheroes2::AGG::GetICN( ICN::ARTIFACT, 83 ); + SubpixelResize( originalImage, _icnVsSprite[id][82] ); + } + return true; + case ICN::ARTIFACT: + LoadOriginalICN( id ); + if ( _icnVsSprite[id].size() > 99 ) { + // This fixes "Arm of the Martyr" (#88) and " Sphere of Negation" (#99) artifacts rendering which initially has some incorrect transparent pixels. + for ( const int32_t index : { 88, 99 } ) { + fheroes2::Sprite & originalImage = _icnVsSprite[id][index]; + fheroes2::Sprite temp( originalImage.width(), originalImage.height() ); + temp.setPosition( originalImage.x(), originalImage.y() ); + temp._disableTransformLayer(); + temp.fill( 0 ); + Blit( originalImage, temp ); + originalImage = std::move( temp ); + } + + // Make a sprite for EDITOR_ANY_ULTIMATE_ARTIFACT used only in Editor for the special victory condition. + // A temporary solution: apply the blur effect originally used for the Holy Shout spell and the purple palette. + fheroes2::Sprite & targetImage = _icnVsSprite[id][83]; + targetImage = CreateHolyShoutEffect( _icnVsSprite[id][91], 1, 0 ); + ApplyPalette( targetImage, PAL::GetPalette( PAL::PaletteType::PURPLE ) ); + } + return true; + case ICN::OBJNARTI: + LoadOriginalICN( id ); + if ( _icnVsSprite[id].size() == 206 ) { + // If we have the Price of Loyalty assets we make a map sprite for the Magic Book artifact. + _icnVsSprite[id].resize( 208 ); + + // Magic book sprite shadow. + _icnVsSprite[id][206] = _icnVsSprite[id][162]; + FillTransform( _icnVsSprite[id][206], 0, 0, 5, 1, 1U ); + FillTransform( _icnVsSprite[id][206], 0, 1, 2, 1, 1U ); + FillTransform( _icnVsSprite[id][206], 2, 1, 4, 1, 3U ); + FillTransform( _icnVsSprite[id][206], 0, 2, 3, 1, 3U ); + FillTransform( _icnVsSprite[id][206], 18, 1, 2, 1, 1U ); + FillTransform( _icnVsSprite[id][206], 17, 2, 3, 1, 3U ); + FillTransform( _icnVsSprite[id][206], 20, 2, 1, 1, 1U ); + FillTransform( _icnVsSprite[id][206], 19, 3, 2, 1, 3U ); + + // Magic Book main sprite. We use sprite from the info dialog to make the map sprite. + _icnVsSprite[id][207].resize( 21, 32 ); + Copy( fheroes2::AGG::GetICN( ICN::ARTFX, 81 ), 6, 0, _icnVsSprite[id][207], 0, 0, 21, 32 ); + FillTransform( _icnVsSprite[id][207], 0, 0, 12, 1, 1U ); + FillTransform( _icnVsSprite[id][207], 15, 0, 6, 1, 1U ); + FillTransform( _icnVsSprite[id][207], 0, 1, 9, 1, 1U ); + FillTransform( _icnVsSprite[id][207], 16, 1, 5, 1, 1U ); + FillTransform( _icnVsSprite[id][207], 0, 2, 6, 1, 1U ); + FillTransform( _icnVsSprite[id][207], 20, 2, 1, 1, 1U ); + FillTransform( _icnVsSprite[id][207], 0, 4, 1, 1, 1U ); + FillTransform( _icnVsSprite[id][207], 0, 3, 3, 1, 1U ); + FillTransform( _icnVsSprite[id][207], 20, 25, 1, 1, 1U ); + FillTransform( _icnVsSprite[id][207], 18, 26, 3, 1, 1U ); + FillTransform( _icnVsSprite[id][207], 16, 27, 5, 1, 1U ); + FillTransform( _icnVsSprite[id][207], 14, 28, 9, 1, 1U ); + FillTransform( _icnVsSprite[id][207], 0, 29, 1, 1, 1U ); + FillTransform( _icnVsSprite[id][207], 12, 29, 11, 1, 1U ); + FillTransform( _icnVsSprite[id][207], 0, 30, 3, 1, 1U ); + FillTransform( _icnVsSprite[id][207], 10, 30, 13, 1, 1U ); + FillTransform( _icnVsSprite[id][207], 0, 31, 5, 1, 1U ); + FillTransform( _icnVsSprite[id][207], 8, 31, 15, 1, 1U ); + } + return true; + case ICN::TWNSDW_5: + LoadOriginalICN( id ); + if ( !_icnVsSprite[id].empty() && _icnVsSprite[id][0].width() == 140 && _icnVsSprite[id][0].height() == 165 ) { + fheroes2::Sprite & image = _icnVsSprite[id][0]; + // Red Tower has multiple defects. + // First one is the area between columns in middle of the Tower is prerendered. We need to remove it. + const int32_t windowBottom = 88; + FillTransform( image, 39, 68, 1, windowBottom - 68, 1 ); + FillTransform( image, 40, 67, 1, windowBottom - 67, 1 ); + FillTransform( image, 41, 66, 1, windowBottom - 66, 1 ); + FillTransform( image, 42, 65, 1, windowBottom - 65, 1 ); + FillTransform( image, 43, 66, 1, windowBottom - 66, 1 ); + FillTransform( image, 44, 67, 1, windowBottom - 67, 1 ); + FillTransform( image, 45, 71, 1, windowBottom - 71, 1 ); + FillTransform( image, 49, 70, 1, windowBottom - 70, 1 ); + FillTransform( image, 50, 68, 2, windowBottom - 68, 1 ); + FillTransform( image, 52, 69, 1, windowBottom - 69, 1 ); + FillTransform( image, 53, 74, 1, windowBottom - 74, 1 ); + FillTransform( image, 57, 70, 1, windowBottom - 70, 1 ); + FillTransform( image, 58, 67, 1, windowBottom - 67, 1 ); + FillTransform( image, 59, 66, 1, windowBottom - 66, 1 ); + FillTransform( image, 60, 65, 2, windowBottom - 65, 1 ); + FillTransform( image, 62, 67, 1, windowBottom - 67, 1 ); + FillTransform( image, 63, 69, 1, windowBottom - 69, 1 ); + FillTransform( image, 64, 72, 1, windowBottom - 72, 1 ); + + // The lower part of the tower is truncated and blocked by partial castle's sprite. The fix is done in multiple stages. + // Fix right red part of the building by copying a piece of the same wall. + Copy( image, 67, 135, image, 67, 119, 1, 1 ); + Copy( image, 67, 144, image, 67, 120, 2, 2 ); + Copy( image, 67, 134, image, 67, 122, 3, 2 ); + Copy( image, 67, 148, image, 67, 125, 1, 4 ); + + // Remove a part of the castle at the bottom left part of the image. + FillTransform( image, 62, 157, 3, 8, 1 ); + + // Top part of the castle's tower touches Red Tower level separation part. + Copy( image, 61, 101, image, 57, 101, 2, 1 ); + Copy( image, 52, 100, image, 57, 100, 2, 1 ); + + // Generate programmatically the left part of the building. + std::mt19937 seededGen( 751 ); // 751 is and ID of this sprite. To keep the changes constant we need to hardcode this value. + + fillRandomPixelsFromImage( image, { 33, 105, 4, 7 }, image, { 33, 117, 4, 39 }, seededGen ); + fillRandomPixelsFromImage( image, { 41, 105, 5, 9 }, image, { 41, 121, 5, 36 }, seededGen ); + fillRandomPixelsFromImage( image, { 46, 104, 4, 13 }, image, { 46, 118, 4, 39 }, seededGen ); + + Copy( image, 37, 113, image, 37, 115, 1, 2 ); + Copy( image, 37, 104, image, 37, 117, 1, 2 ); + Copy( image, 38, 104, image, 38, 118, 2, 1 ); + Copy( image, 37, 113, image, 38, 117, 1, 1 ); + + // Create a temporary image to be a holder of pixels. + fheroes2::Sprite temp( 4 * 2, 8 ); + Copy( image, 33, 105, temp, 0, 0, 4, 8 ); + Copy( image, 41, 105, temp, 4, 0, 4, 8 ); + fillRandomPixelsFromImage( temp, { 0, 0, temp.width(), temp.height() }, image, { 37, 119, 4, 37 }, seededGen ); + + Copy( image, 35, 131, image, 35, 113, 2, 4 ); + + Copy( image, 43, 133, image, 43, 115, 3, 6 ); + + // Fix the main arc. + Copy( image, 61, 102, image, 56, 102, 3, 1 ); + + // TODO: the distribution of light inside Red Tower is actually not uniform and follows the window on from th left. + // However, generating such complex image requires a lot of code so we simply make the rest of the arc uniformed filled. + fillRandomPixelsFromImage( image, { 61, 104, 2, 3 }, image, { 50, 110, 12, 47 }, seededGen ); + fillRandomPixelsFromImage( image, { 61, 104, 2, 3 }, image, { 52, 107, 9, 3 }, seededGen ); + fillRandomPixelsFromImage( image, { 61, 104, 2, 3 }, image, { 62, 111, 1, 46 }, seededGen ); + fillRandomPixelsFromImage( image, { 61, 104, 2, 3 }, image, { 63, 113, 1, 20 }, seededGen ); + fillRandomPixelsFromImage( image, { 61, 104, 2, 3 }, image, { 63, 141, 1, 16 }, seededGen ); + fillRandomPixelsFromImage( image, { 61, 104, 2, 3 }, image, { 64, 115, 1, 17 }, seededGen ); + fillRandomPixelsFromImage( image, { 61, 104, 2, 3 }, image, { 64, 152, 1, 5 }, seededGen ); + fillRandomPixelsFromImage( image, { 61, 104, 2, 3 }, image, { 65, 116, 1, 15 }, seededGen ); + fillRandomPixelsFromImage( image, { 61, 104, 2, 3 }, image, { 66, 118, 1, 12 }, seededGen ); + fillRandomPixelsFromImage( image, { 61, 104, 2, 3 }, image, { 51, 108, 1, 2 }, seededGen ); + fillRandomPixelsFromImage( image, { 61, 104, 2, 3 }, image, { 55, 103, 5, 4 }, seededGen ); + fillRandomPixelsFromImage( image, { 61, 104, 2, 3 }, image, { 61, 109, 1, 1 }, seededGen ); + fillRandomPixelsFromImage( image, { 61, 104, 2, 3 }, image, { 52, 106, 1, 1 }, seededGen ); + } + return true; + case ICN::SCENIBKG: + LoadOriginalICN( id ); + if ( !_icnVsSprite[id].empty() && _icnVsSprite[id][0].width() == 436 && _icnVsSprite[id][0].height() == 476 ) { + const fheroes2::Sprite & helper = fheroes2::AGG::GetICN( ICN::CSPANBKE, 1 ); + if ( !helper.empty() ) { + fheroes2::Sprite & original = _icnVsSprite[id][0]; + fheroes2::Sprite temp( original.width(), original.height() + 12 ); + temp.reset(); + Copy( original, 0, 0, temp, 0, 0, original.width(), original.height() ); + Copy( helper, 0, helper.height() - 12, temp, 0, temp.height() - 12, 300, 12 ); + Copy( helper, helper.width() - ( temp.width() - 300 ), helper.height() - 12, temp, 300 - 16, temp.height() - 12, temp.width() - 300, 12 ); + original = std::move( temp ); } - return true; - case ICN::SCENIBKG: - LoadOriginalICN( id ); - if ( !_icnVsSprite[id].empty() && _icnVsSprite[id][0].width() == 436 && _icnVsSprite[id][0].height() == 476 ) { - const Sprite & helper = GetICN( ICN::CSPANBKE, 1 ); - if ( !helper.empty() ) { - Sprite & original = _icnVsSprite[id][0]; - Sprite temp( original.width(), original.height() + 12 ); - temp.reset(); - Copy( original, 0, 0, temp, 0, 0, original.width(), original.height() ); - Copy( helper, 0, helper.height() - 12, temp, 0, temp.height() - 12, 300, 12 ); - Copy( helper, helper.width() - ( temp.width() - 300 ), helper.height() - 12, temp, 300 - 16, temp.height() - 12, temp.width() - 300, 12 ); - original = std::move( temp ); - } + } + return true; + case ICN::CSTLCAPS: + LoadOriginalICN( id ); + if ( !_icnVsSprite[id].empty() && _icnVsSprite[id][0].width() == 84 && _icnVsSprite[id][0].height() == 81 ) { + const fheroes2::Sprite & castle = fheroes2::AGG::GetICN( ICN::TWNSCSTL, 0 ); + if ( !castle.empty() ) { + Blit( castle, 206, 106, _icnVsSprite[id][0], 2, 2, 33, 67 ); } - return true; - case ICN::CSTLCAPS: - LoadOriginalICN( id ); - if ( !_icnVsSprite[id].empty() && _icnVsSprite[id][0].width() == 84 && _icnVsSprite[id][0].height() == 81 ) { - const Sprite & castle = GetICN( ICN::TWNSCSTL, 0 ); - if ( !castle.empty() ) { - Blit( castle, 206, 106, _icnVsSprite[id][0], 2, 2, 33, 67 ); - } + } + return true; + case ICN::LGNDXTRA: + // Exit button is too huge due to 1 pixel presence at the bottom of the image. + LoadOriginalICN( id ); + if ( _icnVsSprite[id].size() >= 6 ) { + auto & original = _icnVsSprite[id]; + if ( original[4].height() == 142 ) { + const fheroes2::Point offset( original[4].x(), original[4].y() ); + original[4] = Crop( original[4], 0, 0, original[4].width(), 25 ); + original[4].setPosition( offset.x, offset.y ); } - return true; - case ICN::LGNDXTRA: - // Exit button is too huge due to 1 pixel presence at the bottom of the image. - LoadOriginalICN( id ); - if ( _icnVsSprite[id].size() >= 6 ) { - auto & original = _icnVsSprite[id]; - if ( original[4].height() == 142 ) { - const Point offset( original[4].x(), original[4].y() ); - original[4] = Crop( original[4], 0, 0, original[4].width(), 25 ); - original[4].setPosition( offset.x, offset.y ); - } - if ( original[5].height() == 142 ) { - const Point offset( original[5].x(), original[5].y() ); - original[5] = Crop( original[5], 0, 0, original[5].width(), 25 ); - original[5].setPosition( offset.x, offset.y ); - } + if ( original[5].height() == 142 ) { + const fheroes2::Point offset( original[5].x(), original[5].y() ); + original[5] = Crop( original[5], 0, 0, original[5].width(), 25 ); + original[5].setPosition( offset.x, offset.y ); } - return true; - case ICN::LGNDXTRE: - // Exit button is too huge due to 1 pixel presence at the bottom of the image. - LoadOriginalICN( id ); - if ( _icnVsSprite[id].size() >= 6 ) { - auto & original = _icnVsSprite[id]; - if ( original[4].height() == 142 ) { - const Point offset( original[4].x(), original[4].y() ); - original[4] = Crop( original[4], 0, 0, original[4].width(), 25 ); - original[4].setPosition( offset.x, offset.y ); - } + } + return true; + case ICN::LGNDXTRE: + // Exit button is too huge due to 1 pixel presence at the bottom of the image. + LoadOriginalICN( id ); + if ( _icnVsSprite[id].size() >= 6 ) { + auto & original = _icnVsSprite[id]; + if ( original[4].height() == 142 ) { + const fheroes2::Point offset( original[4].x(), original[4].y() ); + original[4] = Crop( original[4], 0, 0, original[4].width(), 25 ); + original[4].setPosition( offset.x, offset.y ); } - return true; - case ICN::ESPANBKG_EVIL: { - _icnVsSprite[id].resize( 2 ); - - const Rect roi{ 28, 28, 265, 206 }; + } + return true; + case ICN::ESPANBKG_EVIL: { + _icnVsSprite[id].resize( 2 ); - Sprite & output = _icnVsSprite[id][0]; - _icnVsSprite[id][0] = GetICN( ICN::CSPANBKE, 0 ); - Copy( GetICN( ICN::ESPANBKG, 0 ), roi.x, roi.y, output, roi.x, roi.y, roi.width, roi.height ); + const fheroes2::Rect roi{ 28, 28, 265, 206 }; - convertToEvilInterface( output, roi ); + fheroes2::Sprite & output = _icnVsSprite[id][0]; + _icnVsSprite[id][0] = fheroes2::AGG::GetICN( ICN::CSPANBKE, 0 ); + Copy( fheroes2::AGG::GetICN( ICN::ESPANBKG, 0 ), roi.x, roi.y, output, roi.x, roi.y, roi.width, roi.height ); - _icnVsSprite[id][1] = GetICN( ICN::ESPANBKG, 1 ); + convertToEvilInterface( output, roi ); - return true; - } - case ICN::STONEBAK_EVIL: { - GetICN( ICN::STONEBAK, 0 ); - _icnVsSprite[id] = _icnVsSprite[ICN::STONEBAK]; - if ( !_icnVsSprite[id].empty() ) { - const Rect roi( 0, 0, _icnVsSprite[id][0].width(), _icnVsSprite[id][0].height() ); - convertToEvilInterface( _icnVsSprite[id][0], roi ); - } + _icnVsSprite[id][1] = fheroes2::AGG::GetICN( ICN::ESPANBKG, 1 ); - return true; - } - case ICN::STONEBAK_SMALL_POL: { - _icnVsSprite[id].resize( 1 ); - const fheroes2::Sprite & original = GetICN( ICN::X_CMPBKG, 0 ); - if ( !original.empty() ) { - _icnVsSprite[id][0] = Crop( original, original.width() - 272, original.height() - 52, 244, 28 ); - } - return true; - } - case ICN::REDBAK_SMALL_VERTICAL: { - _icnVsSprite[id].resize( 1 ); - const fheroes2::Sprite & original = GetICN( ICN::HEROBKG, 0 ); - if ( !original.empty() ) { - _icnVsSprite[id][0] = Crop( original, 0, 0, 37, 230 ); - } - return true; + return true; + } + case ICN::STONEBAK_EVIL: { + fheroes2::AGG::GetICN( ICN::STONEBAK, 0 ); + _icnVsSprite[id] = _icnVsSprite[ICN::STONEBAK]; + if ( !_icnVsSprite[id].empty() ) { + const fheroes2::Rect roi( 0, 0, _icnVsSprite[id][0].width(), _icnVsSprite[id][0].height() ); + convertToEvilInterface( _icnVsSprite[id][0], roi ); } - case ICN::UNIFORMBAK_GOOD: - case ICN::UNIFORMBAK_EVIL: { - _icnVsSprite[id].resize( 1 ); - const bool isEvilInterface = ( id == ICN::UNIFORMBAK_EVIL ); - const fheroes2::Sprite & original = GetICN( isEvilInterface ? ICN::BUYBUILE : ICN::BUYBUILD, 1 ); - if ( !original.empty() ) { - _icnVsSprite[id][0].resize( 246, 45 ); - _icnVsSprite[id][0].reset(); - Copy( original, 0, 0, _icnVsSprite[id][0], 0, 0, 123, 45 ); - Copy( original, 0, 0, _icnVsSprite[id][0], 123, 0, 123, 45 ); - } - return true; + return true; + } + case ICN::STONEBAK_SMALL_POL: { + _icnVsSprite[id].resize( 1 ); + const fheroes2::Sprite & original = fheroes2::AGG::GetICN( ICN::X_CMPBKG, 0 ); + if ( !original.empty() ) { + _icnVsSprite[id][0] = Crop( original, original.width() - 272, original.height() - 52, 244, 28 ); } - case ICN::WELLBKG_EVIL: { - GetICN( ICN::WELLBKG, 0 ); - _icnVsSprite[id] = _icnVsSprite[ICN::WELLBKG]; - if ( !_icnVsSprite[id].empty() ) { - const Rect roi( 0, 0, _icnVsSprite[id][0].width(), _icnVsSprite[id][0].height() - 19 ); - convertToEvilInterface( _icnVsSprite[id][0], roi ); - } - - return true; + return true; + } + case ICN::REDBAK_SMALL_VERTICAL: { + _icnVsSprite[id].resize( 1 ); + const fheroes2::Sprite & original = fheroes2::AGG::GetICN( ICN::HEROBKG, 0 ); + if ( !original.empty() ) { + _icnVsSprite[id][0] = Crop( original, 0, 0, 37, 230 ); } - case ICN::CASLWIND_EVIL: { - GetICN( ICN::CASLWIND, 0 ); - _icnVsSprite[id] = _icnVsSprite[ICN::CASLWIND]; - if ( !_icnVsSprite[id].empty() ) { - const Rect roi( 0, 0, _icnVsSprite[id][0].width(), _icnVsSprite[id][0].height() ); - convertToEvilInterface( _icnVsSprite[id][0], roi ); - } - - return true; + return true; + } + case ICN::UNIFORMBAK_GOOD: + case ICN::UNIFORMBAK_EVIL: { + _icnVsSprite[id].resize( 1 ); + const bool isEvilInterface = ( id == ICN::UNIFORMBAK_EVIL ); + const fheroes2::Sprite & original = fheroes2::AGG::GetICN( isEvilInterface ? ICN::BUYBUILE : ICN::BUYBUILD, 1 ); + if ( !original.empty() ) { + _icnVsSprite[id][0].resize( 246, 45 ); + _icnVsSprite[id][0].reset(); + Copy( original, 0, 0, _icnVsSprite[id][0], 0, 0, 123, 45 ); + Copy( original, 0, 0, _icnVsSprite[id][0], 123, 0, 123, 45 ); + } + + return true; + } + case ICN::WELLBKG_EVIL: { + fheroes2::AGG::GetICN( ICN::WELLBKG, 0 ); + _icnVsSprite[id] = _icnVsSprite[ICN::WELLBKG]; + if ( !_icnVsSprite[id].empty() ) { + const fheroes2::Rect roi( 0, 0, _icnVsSprite[id][0].width(), _icnVsSprite[id][0].height() - 19 ); + convertToEvilInterface( _icnVsSprite[id][0], roi ); } - case ICN::CASLXTRA_EVIL: { - GetICN( ICN::CASLXTRA, 0 ); - _icnVsSprite[id] = _icnVsSprite[ICN::CASLXTRA]; - if ( !_icnVsSprite[id].empty() ) { - const Rect roi( 0, 0, _icnVsSprite[id][0].width(), _icnVsSprite[id][0].height() ); - convertToEvilInterface( _icnVsSprite[id][0], roi ); - } - return true; + return true; + } + case ICN::CASLWIND_EVIL: { + fheroes2::AGG::GetICN( ICN::CASLWIND, 0 ); + _icnVsSprite[id] = _icnVsSprite[ICN::CASLWIND]; + if ( !_icnVsSprite[id].empty() ) { + const fheroes2::Rect roi( 0, 0, _icnVsSprite[id][0].width(), _icnVsSprite[id][0].height() ); + convertToEvilInterface( _icnVsSprite[id][0], roi ); } - case ICN::STRIP_BACKGROUND_EVIL: { - _icnVsSprite[id].resize( 1 ); - _icnVsSprite[id][0] = GetICN( ICN::STRIP, 11 ); - const Rect roi( 0, 0, _icnVsSprite[id][0].width(), _icnVsSprite[id][0].height() - 7 ); + return true; + } + case ICN::CASLXTRA_EVIL: { + fheroes2::AGG::GetICN( ICN::CASLXTRA, 0 ); + _icnVsSprite[id] = _icnVsSprite[ICN::CASLXTRA]; + if ( !_icnVsSprite[id].empty() ) { + const fheroes2::Rect roi( 0, 0, _icnVsSprite[id][0].width(), _icnVsSprite[id][0].height() ); convertToEvilInterface( _icnVsSprite[id][0], roi ); - - return true; } - case ICN::B_BFLG32: - case ICN::G_BFLG32: - case ICN::R_BFLG32: - case ICN::Y_BFLG32: - case ICN::O_BFLG32: - case ICN::P_BFLG32: - LoadOriginalICN( id ); - if ( _icnVsSprite[id].size() > 31 && _icnVsSprite[id][31].height() == 248 ) { - Sprite & original = _icnVsSprite[id][31]; - Sprite temp = Crop( original, 0, 0, original.width(), 4 ); - temp.setPosition( original.x(), original.y() ); - - original = std::move( temp ); - } - return true; - case ICN::FLAG32: - LoadOriginalICN( id ); - // Only first 14 images are properly aligned within an Adventure Map tile. The rest of images should be rendered on multiple tiles. - // To keep proper rendering logic we are creating new images by diving existing ones into 2 parts and setting up new sprite offsets. - // This helps to solve the problem with rendering order. - _icnVsSprite[id].resize( 128 + _icnVsSprite[id].size() * 2 ); - for ( int32_t i = 14; i < 14 + 7; ++i ) { - const Sprite & original = _icnVsSprite[id][i]; - - _icnVsSprite[id][i + 128] = Crop( original, 0, 0, 32 - original.x(), original.height() ); - _icnVsSprite[id][i + 128].setPosition( original.x(), 32 + original.y() ); - - _icnVsSprite[id][i + 128 + 7] = Crop( original, 32 - original.x(), 0, original.width(), original.height() ); - _icnVsSprite[id][i + 128 + 7].setPosition( 0, 32 + original.y() ); - } - - for ( int32_t i = 42; i < 42 + 7; ++i ) { - const Sprite & original = _icnVsSprite[id][i]; - - _icnVsSprite[id][i + 128] = Crop( original, 0, 0, -original.x(), original.height() ); - _icnVsSprite[id][i + 128].setPosition( 32 + original.x(), original.y() ); - _icnVsSprite[id][i + 128 + 7] = Crop( original, -original.x(), 0, original.width(), original.height() ); - _icnVsSprite[id][i + 128 + 7].setPosition( 0, original.y() ); - } - return true; - case ICN::SHADOW32: - LoadOriginalICN( id ); - // The shadow sprite of hero needs to be shifted to match the hero sprite. - if ( _icnVsSprite[id].size() == 86 ) { - // Direction: TOP (0-8), TOP_RIGHT (9-17), RIGHT (18-26), BOTTOM_RIGHT (27-35), BOTTOM (36-44) - for ( int32_t i = 0; i < 45; ++i ) { - Sprite & original = _icnVsSprite[id][i]; - original.setPosition( original.x(), original.y() - 3 ); - } + return true; + } + case ICN::STRIP_BACKGROUND_EVIL: { + _icnVsSprite[id].resize( 1 ); + _icnVsSprite[id][0] = fheroes2::AGG::GetICN( ICN::STRIP, 11 ); - // Direction:TOP_LEFT - for ( int32_t i = 59; i < 68; ++i ) { - Sprite & original = _icnVsSprite[id][i]; - original.setPosition( original.x() + 1, original.y() - 3 ); - } + const fheroes2::Rect roi( 0, 0, _icnVsSprite[id][0].width(), _icnVsSprite[id][0].height() - 7 ); + convertToEvilInterface( _icnVsSprite[id][0], roi ); - // Direction:LEFT - for ( int32_t i = 68; i < 77; ++i ) { - Sprite & original = _icnVsSprite[id][i]; + return true; + } + case ICN::B_BFLG32: + case ICN::G_BFLG32: + case ICN::R_BFLG32: + case ICN::Y_BFLG32: + case ICN::O_BFLG32: + case ICN::P_BFLG32: + LoadOriginalICN( id ); + if ( _icnVsSprite[id].size() > 31 && _icnVsSprite[id][31].height() == 248 ) { + fheroes2::Sprite & original = _icnVsSprite[id][31]; + fheroes2::Sprite temp = Crop( original, 0, 0, original.width(), 4 ); + temp.setPosition( original.x(), original.y() ); + + original = std::move( temp ); + } + return true; + case ICN::FLAG32: + LoadOriginalICN( id ); + // Only first 14 images are properly aligned within an Adventure Map tile. The rest of images should be rendered on multiple tiles. + // To keep proper rendering logic we are creating new images by diving existing ones into 2 parts and setting up new sprite offsets. + // This helps to solve the problem with rendering order. + _icnVsSprite[id].resize( 128 + _icnVsSprite[id].size() * 2 ); + for ( int32_t i = 14; i < 14 + 7; ++i ) { + const fheroes2::Sprite & original = _icnVsSprite[id][i]; + + _icnVsSprite[id][i + 128] = Crop( original, 0, 0, 32 - original.x(), original.height() ); + _icnVsSprite[id][i + 128].setPosition( original.x(), 32 + original.y() ); + + _icnVsSprite[id][i + 128 + 7] = Crop( original, 32 - original.x(), 0, original.width(), original.height() ); + _icnVsSprite[id][i + 128 + 7].setPosition( 0, 32 + original.y() ); + } + + for ( int32_t i = 42; i < 42 + 7; ++i ) { + const fheroes2::Sprite & original = _icnVsSprite[id][i]; + + _icnVsSprite[id][i + 128] = Crop( original, 0, 0, -original.x(), original.height() ); + _icnVsSprite[id][i + 128].setPosition( 32 + original.x(), original.y() ); + + _icnVsSprite[id][i + 128 + 7] = Crop( original, -original.x(), 0, original.width(), original.height() ); + _icnVsSprite[id][i + 128 + 7].setPosition( 0, original.y() ); + } + return true; + case ICN::SHADOW32: + LoadOriginalICN( id ); + // The shadow sprite of hero needs to be shifted to match the hero sprite. + if ( _icnVsSprite[id].size() == 86 ) { + // Direction: TOP (0-8), TOP_RIGHT (9-17), RIGHT (18-26), BOTTOM_RIGHT (27-35), BOTTOM (36-44) + for ( int32_t i = 0; i < 45; ++i ) { + fheroes2::Sprite & original = _icnVsSprite[id][i]; + original.setPosition( original.x(), original.y() - 3 ); + } + + // Direction:TOP_LEFT + for ( int32_t i = 59; i < 68; ++i ) { + fheroes2::Sprite & original = _icnVsSprite[id][i]; + original.setPosition( original.x() + 1, original.y() - 3 ); + } + + // Direction:LEFT + for ( int32_t i = 68; i < 77; ++i ) { + fheroes2::Sprite & original = _icnVsSprite[id][i]; + original.setPosition( original.x() - 5, original.y() - 3 ); + } + + // Direction:BOTTOM_LEFT + for ( int32_t i = 77; i < 86; ++i ) { + fheroes2::Sprite & original = _icnVsSprite[id][i]; + if ( i == 80 ) { + // This sprite needs extra fix. original.setPosition( original.x() - 5, original.y() - 3 ); } - - // Direction:BOTTOM_LEFT - for ( int32_t i = 77; i < 86; ++i ) { - Sprite & original = _icnVsSprite[id][i]; - if ( i == 80 ) { - // This sprite needs extra fix. - original.setPosition( original.x() - 5, original.y() - 3 ); - } - else { - original.setPosition( original.x() - 10, original.y() - 3 ); - } + else { + original.setPosition( original.x() - 10, original.y() - 3 ); } } - return true; - case ICN::MINI_MONSTER_IMAGE: - case ICN::MINI_MONSTER_SHADOW: { - // It doesn't matter which image is being called. We are generating both of them at the same time. - loadICN( ICN::MINIMON ); - - // Minotaur King original Adventure map sprite has blue armlets. We make them gold to correspond the ICN::MINOTAU2. - if ( _icnVsSprite[ICN::MINIMON].size() > 303 ) { - // The gold color gradient has -42 offset from blue color gradient. - if ( _icnVsSprite[ICN::MINIMON][297].width() == 38 && _icnVsSprite[ICN::MINIMON][297].height() == 34 ) { - // We update these pixels: 29x15, 30x15, 31x15, 30x16. - for ( const uint32_t pixelNumber : { 599, 600, 601, 638 } ) { - _icnVsSprite[ICN::MINIMON][297].image()[pixelNumber] -= 42; - } + } + return true; + case ICN::MINI_MONSTER_IMAGE: + case ICN::MINI_MONSTER_SHADOW: { + // It doesn't matter which image is being called. We are generating both of them at the same time. + loadICN( ICN::MINIMON ); + + // Minotaur King original Adventure map sprite has blue armlets. We make them gold to correspond the ICN::MINOTAU2. + if ( _icnVsSprite[ICN::MINIMON].size() > 303 ) { + // The gold color gradient has -42 offset from blue color gradient. + if ( _icnVsSprite[ICN::MINIMON][297].width() == 38 && _icnVsSprite[ICN::MINIMON][297].height() == 34 ) { + // We update these pixels: 29x15, 30x15, 31x15, 30x16. + for ( const uint32_t pixelNumber : { 599, 600, 601, 638 } ) { + _icnVsSprite[ICN::MINIMON][297].image()[pixelNumber] -= 42; } - for ( uint32_t icnNumber = 298; icnNumber < 300; ++icnNumber ) { - if ( _icnVsSprite[ICN::MINIMON][icnNumber].width() == 44 && _icnVsSprite[ICN::MINIMON][icnNumber].height() == 32 ) { - // We update these pixels: 29x17, 30x17, 32x17, 30x18, 31x18, 38x18, 38x19, 38x20. - for ( const uint32_t pixelNumber : { 777, 778, 780, 822, 823, 830, 874, 918 } ) { - _icnVsSprite[ICN::MINIMON][icnNumber].image()[pixelNumber] -= 42; - } + } + for ( uint32_t icnNumber = 298; icnNumber < 300; ++icnNumber ) { + if ( _icnVsSprite[ICN::MINIMON][icnNumber].width() == 44 && _icnVsSprite[ICN::MINIMON][icnNumber].height() == 32 ) { + // We update these pixels: 29x17, 30x17, 32x17, 30x18, 31x18, 38x18, 38x19, 38x20. + for ( const uint32_t pixelNumber : { 777, 778, 780, 822, 823, 830, 874, 918 } ) { + _icnVsSprite[ICN::MINIMON][icnNumber].image()[pixelNumber] -= 42; } } - if ( _icnVsSprite[ICN::MINIMON][300].width() == 45 && _icnVsSprite[ICN::MINIMON][300].height() == 32 ) { - // We update these pixels: 30x17, 31x17, 33x17, 31x18, 32x18, 39x18, 39x19, 39x20 - for ( const uint32_t pixelNumber : { 795, 796, 798, 841, 842, 849, 894, 939 } ) { - _icnVsSprite[ICN::MINIMON][300].image()[pixelNumber] -= 42; - } + } + if ( _icnVsSprite[ICN::MINIMON][300].width() == 45 && _icnVsSprite[ICN::MINIMON][300].height() == 32 ) { + // We update these pixels: 30x17, 31x17, 33x17, 31x18, 32x18, 39x18, 39x19, 39x20 + for ( const uint32_t pixelNumber : { 795, 796, 798, 841, 842, 849, 894, 939 } ) { + _icnVsSprite[ICN::MINIMON][300].image()[pixelNumber] -= 42; } - if ( _icnVsSprite[ICN::MINIMON][301].width() == 45 && _icnVsSprite[ICN::MINIMON][301].height() == 32 ) { - // We update these pixels: 29x17, 30x17, 32x17, 30x18, 31x18, 39x18, 39x19, 39x20 - for ( const uint32_t pixelNumber : { 794, 795, 797, 840, 841, 849, 894, 939 } ) { - _icnVsSprite[ICN::MINIMON][301].image()[pixelNumber] -= 42; - } + } + if ( _icnVsSprite[ICN::MINIMON][301].width() == 45 && _icnVsSprite[ICN::MINIMON][301].height() == 32 ) { + // We update these pixels: 29x17, 30x17, 32x17, 30x18, 31x18, 39x18, 39x19, 39x20 + for ( const uint32_t pixelNumber : { 794, 795, 797, 840, 841, 849, 894, 939 } ) { + _icnVsSprite[ICN::MINIMON][301].image()[pixelNumber] -= 42; } - if ( _icnVsSprite[ICN::MINIMON][302].width() == 45 && _icnVsSprite[ICN::MINIMON][302].height() == 32 ) { - // We update these pixels: 35x16, 29x17, 30x17, 32x17, 33x17, 34x17, 30x18, 31x18, 31x19, 32x20. - for ( const uint32_t pixelNumber : { 755, 794, 795, 797, 798, 799, 840, 841, 886, 932 } ) { - _icnVsSprite[ICN::MINIMON][302].image()[pixelNumber] -= 42; - } + } + if ( _icnVsSprite[ICN::MINIMON][302].width() == 45 && _icnVsSprite[ICN::MINIMON][302].height() == 32 ) { + // We update these pixels: 35x16, 29x17, 30x17, 32x17, 33x17, 34x17, 30x18, 31x18, 31x19, 32x20. + for ( const uint32_t pixelNumber : { 755, 794, 795, 797, 798, 799, 840, 841, 886, 932 } ) { + _icnVsSprite[ICN::MINIMON][302].image()[pixelNumber] -= 42; } - if ( _icnVsSprite[ICN::MINIMON][303].width() == 44 && _icnVsSprite[ICN::MINIMON][303].height() == 32 ) { - // We update these pixels: 29x17, 30x17, 30x18, 31x18, 31x19. - for ( const uint32_t pixelNumber : { 777, 778, 822, 823, 867 } ) { - _icnVsSprite[ICN::MINIMON][303].image()[pixelNumber] -= 42; - } + } + if ( _icnVsSprite[ICN::MINIMON][303].width() == 44 && _icnVsSprite[ICN::MINIMON][303].height() == 32 ) { + // We update these pixels: 29x17, 30x17, 30x18, 31x18, 31x19. + for ( const uint32_t pixelNumber : { 777, 778, 822, 823, 867 } ) { + _icnVsSprite[ICN::MINIMON][303].image()[pixelNumber] -= 42; } } + } - // TODO: optimize image sizes. - _icnVsSprite[ICN::MINI_MONSTER_IMAGE] = _icnVsSprite[ICN::MINIMON]; - _icnVsSprite[ICN::MINI_MONSTER_SHADOW] = _icnVsSprite[ICN::MINIMON]; + // TODO: optimize image sizes. + _icnVsSprite[ICN::MINI_MONSTER_IMAGE] = _icnVsSprite[ICN::MINIMON]; + _icnVsSprite[ICN::MINI_MONSTER_SHADOW] = _icnVsSprite[ICN::MINIMON]; - for ( Sprite & image : _icnVsSprite[ICN::MINI_MONSTER_IMAGE] ) { - uint8_t * transform = image.transform(); - const uint8_t * transformEnd = transform + image.width() * image.height(); - for ( ; transform != transformEnd; ++transform ) { - if ( *transform > 1 ) { - *transform = 1; - } + for ( fheroes2::Sprite & image : _icnVsSprite[ICN::MINI_MONSTER_IMAGE] ) { + uint8_t * transform = image.transform(); + const uint8_t * transformEnd = transform + image.width() * image.height(); + for ( ; transform != transformEnd; ++transform ) { + if ( *transform > 1 ) { + *transform = 1; } } + } - for ( Sprite & image : _icnVsSprite[ICN::MINI_MONSTER_SHADOW] ) { - uint8_t * transform = image.transform(); - const uint8_t * transformEnd = transform + image.width() * image.height(); - for ( ; transform != transformEnd; ++transform ) { - if ( *transform == 0 ) { - *transform = 1; - } + for ( fheroes2::Sprite & image : _icnVsSprite[ICN::MINI_MONSTER_SHADOW] ) { + uint8_t * transform = image.transform(); + const uint8_t * transformEnd = transform + image.width() * image.height(); + for ( ; transform != transformEnd; ++transform ) { + if ( *transform == 0 ) { + *transform = 1; } } - - return true; } - case ICN::BUTTON_GOOD_FONT_RELEASED: - case ICN::BUTTON_GOOD_FONT_PRESSED: - case ICN::BUTTON_EVIL_FONT_RELEASED: - case ICN::BUTTON_EVIL_FONT_PRESSED: { - generateBaseButtonFont( _icnVsSprite[ICN::BUTTON_GOOD_FONT_RELEASED], _icnVsSprite[ICN::BUTTON_GOOD_FONT_PRESSED], - _icnVsSprite[ICN::BUTTON_EVIL_FONT_RELEASED], _icnVsSprite[ICN::BUTTON_EVIL_FONT_PRESSED] ); - return true; + + return true; + } + case ICN::BUTTON_GOOD_FONT_RELEASED: + case ICN::BUTTON_GOOD_FONT_PRESSED: + case ICN::BUTTON_EVIL_FONT_RELEASED: + case ICN::BUTTON_EVIL_FONT_PRESSED: { + generateBaseButtonFont( _icnVsSprite[ICN::BUTTON_GOOD_FONT_RELEASED], _icnVsSprite[ICN::BUTTON_GOOD_FONT_PRESSED], + _icnVsSprite[ICN::BUTTON_EVIL_FONT_RELEASED], _icnVsSprite[ICN::BUTTON_EVIL_FONT_PRESSED] ); + return true; + } + case ICN::HISCORE: { + LoadOriginalICN( id ); + if ( _icnVsSprite[id].size() == 9 ) { + // Campaign title bar needs to include rating. + const int32_t imageHeight = _icnVsSprite[id][7].height(); + const fheroes2::Sprite temp = Crop( _icnVsSprite[id][7], 215, 0, 300, imageHeight ); + + Copy( temp, 0, 0, _icnVsSprite[id][7], 215 - 57, 0, temp.width(), imageHeight ); + Copy( _icnVsSprite[id][6], 324, 0, _icnVsSprite[id][7], 324, 0, _icnVsSprite[id][6].width() - 324, imageHeight ); } - case ICN::HISCORE: { - LoadOriginalICN( id ); - if ( _icnVsSprite[id].size() == 9 ) { - // Campaign title bar needs to include rating. - const int32_t imageHeight = _icnVsSprite[id][7].height(); - const Sprite temp = Crop( _icnVsSprite[id][7], 215, 0, 300, imageHeight ); + return true; + } + case ICN::SPELLINL: { + LoadOriginalICN( id ); - Copy( temp, 0, 0, _icnVsSprite[id][7], 215 - 57, 0, temp.width(), imageHeight ); - Copy( _icnVsSprite[id][6], 324, 0, _icnVsSprite[id][7], 324, 0, _icnVsSprite[id][6].width() - 324, imageHeight ); - } - return true; + if ( _icnVsSprite[id].size() > 11 ) { + // Replace petrification spell mini-icon. + fheroes2::h2d::readImage( "petrification_spell_icon_mini.image", _icnVsSprite[id][11] ); } - case ICN::SPELLINL: { - LoadOriginalICN( id ); - if ( _icnVsSprite[id].size() > 11 ) { - // Replace petrification spell mini-icon. - h2d::readImage( "petrification_spell_icon_mini.image", _icnVsSprite[id][11] ); - } + return true; + } + case ICN::EMPTY_GOOD_BUTTON: + case ICN::EMPTY_EVIL_BUTTON: { + const bool isGoodInterface = ( id == ICN::EMPTY_GOOD_BUTTON ); + const int32_t originalId = isGoodInterface ? ICN::SYSTEM : ICN::SYSTEME; + loadICN( originalId ); + if ( _icnVsSprite[originalId].size() < 13 ) { return true; } - case ICN::EMPTY_GOOD_BUTTON: - case ICN::EMPTY_EVIL_BUTTON: { - const bool isGoodInterface = ( id == ICN::EMPTY_GOOD_BUTTON ); - const int32_t originalId = isGoodInterface ? ICN::SYSTEM : ICN::SYSTEME; - loadICN( originalId ); - if ( _icnVsSprite[originalId].size() < 13 ) { - return true; - } + _icnVsSprite[id].resize( 2 ); - _icnVsSprite[id].resize( 2 ); + fheroes2::Sprite & released = _icnVsSprite[id][0]; + fheroes2::Sprite & pressed = _icnVsSprite[id][1]; - Sprite & released = _icnVsSprite[id][0]; - Sprite & pressed = _icnVsSprite[id][1]; + released = _icnVsSprite[originalId][11]; - released = _icnVsSprite[originalId][11]; + // fix single bright pixel in the left part of the text area of the released state buttons + Fill( released, 8, 7, 1, 1, getButtonFillingColor( true, isGoodInterface ) ); - // fix single bright pixel in the left part of the text area of the released state buttons - Fill( released, 8, 7, 1, 1, getButtonFillingColor( true, isGoodInterface ) ); + const fheroes2::Sprite & originalPressed = fheroes2::AGG::GetICN( originalId, 12 ); - const Sprite & originalPressed = GetICN( originalId, 12 ); + if ( originalPressed.width() > 2 && originalPressed.height() > 2 ) { + pressed.resize( originalPressed.width(), originalPressed.height() ); + // copy the original pressed button but add the missing darker leftside border from the released state + Copy( released, 0, 0, pressed, 0, 0, 1, released.height() ); + Copy( originalPressed, 0, 0, pressed, 1, 0, originalPressed.width() - 1, originalPressed.height() ); - if ( originalPressed.width() > 2 && originalPressed.height() > 2 ) { - pressed.resize( originalPressed.width(), originalPressed.height() ); - // copy the original pressed button but add the missing darker leftside border from the released state - Copy( released, 0, 0, pressed, 0, 0, 1, released.height() ); - Copy( originalPressed, 0, 0, pressed, 1, 0, originalPressed.width() - 1, originalPressed.height() ); + // Make the background transparent. + FillTransform( pressed, 1, 0, pressed.width() - 1, 1, 1 ); + FillTransform( pressed, pressed.width() - 1, 1, 1, pressed.height() - 1, 1 ); - // Make the background transparent. - FillTransform( pressed, 1, 0, pressed.width() - 1, 1, 1 ); - FillTransform( pressed, pressed.width() - 1, 1, 1, pressed.height() - 1, 1 ); + FillTransform( pressed, 1, 1, 2, 1, 1 ); + FillTransform( pressed, 1, 2, 1, 1, 1 ); - FillTransform( pressed, 1, 1, 2, 1, 1 ); - FillTransform( pressed, 1, 2, 1, 1, 1 ); + FillTransform( pressed, pressed.width() - 3, 1, 2, 1, 1 ); + FillTransform( pressed, pressed.width() - 2, 2, 1, 1, 1 ); - FillTransform( pressed, pressed.width() - 3, 1, 2, 1, 1 ); - FillTransform( pressed, pressed.width() - 2, 2, 1, 1, 1 ); + FillTransform( pressed, pressed.width() - 4, pressed.height() - 1, 3, 1, 1 ); + FillTransform( pressed, pressed.width() - 3, pressed.height() - 2, 2, 1, 1 ); + FillTransform( pressed, pressed.width() - 2, pressed.height() - 3, 1, 1, 1 ); + } - FillTransform( pressed, pressed.width() - 4, pressed.height() - 1, 3, 1, 1 ); - FillTransform( pressed, pressed.width() - 3, pressed.height() - 2, 2, 1, 1 ); - FillTransform( pressed, pressed.width() - 2, pressed.height() - 3, 1, 1, 1 ); - } + return true; + } + case ICN::EMPTY_POL_BUTTON: { + const int originalID = ICN::X_CMPBTN; + loadICN( originalID ); + if ( _icnVsSprite[originalID].size() < 8 ) { return true; } - case ICN::EMPTY_POL_BUTTON: { - const int originalID = ICN::X_CMPBTN; - loadICN( originalID ); - if ( _icnVsSprite[originalID].size() < 8 ) { - return true; - } + _icnVsSprite[id].resize( 2 ); + // move dark border to new released state from original pressed state button + const fheroes2::Sprite & originalReleased = fheroes2::AGG::GetICN( originalID, 4 ); + const fheroes2::Sprite & originalPressed = fheroes2::AGG::GetICN( originalID, 5 ); + if ( originalReleased.width() != 94 && originalPressed.width() != 94 && originalReleased.height() < 5 && originalPressed.height() < 5 ) { + return true; + } + fheroes2::Sprite & releasedWithDarkBorder = _icnVsSprite[id][0]; + releasedWithDarkBorder.resize( originalReleased.width() + 2, originalReleased.height() + 1 ); + releasedWithDarkBorder.reset(); + + Copy( originalReleased, 0, 0, releasedWithDarkBorder, 1, 0, originalReleased.width(), originalReleased.height() ); + Copy( originalReleased, 0, 2, releasedWithDarkBorder, 1, 21, 1, 1 ); + Copy( originalReleased, 0, 2, releasedWithDarkBorder, 2, 22, 1, 1 ); + Copy( originalPressed, 0, 2, releasedWithDarkBorder, 0, 3, 1, 19 ); + Copy( originalPressed, 0, originalPressed.height() - 1, releasedWithDarkBorder, 0, originalPressed.height(), originalPressed.width(), 1 ); + Copy( originalPressed, 0, 2, releasedWithDarkBorder, 1, 22, 1, 1 ); + + // pressed state + fheroes2::Sprite & pressed = _icnVsSprite[id][1]; + pressed.resize( originalPressed.width() + 2, originalPressed.height() + 1 ); + pressed.reset(); + Copy( originalPressed, 0, 0, pressed, 0, 1, originalPressed.width(), originalPressed.height() ); + + // the empty buttons need to be widened by 1 px so that they can be evenly divided by 3 in resizeButton() in ui_tools.cpp + Copy( originalReleased, originalReleased.width() - 5, 0, releasedWithDarkBorder, releasedWithDarkBorder.width() - 5, 0, 5, originalReleased.height() ); + Copy( originalPressed, originalPressed.width() - 5, 0, pressed, pressed.width() - 6, 1, 5, originalPressed.height() ); + + const int32_t pixelPosition = 4 * 94 + 6; + Fill( releasedWithDarkBorder, 5, 3, 88, 18, originalReleased.image()[pixelPosition] ); + Fill( pressed, 4, 5, 87, 17, originalPressed.image()[pixelPosition] ); + + return true; + } + case ICN::EMPTY_GUILDWELL_BUTTON: { + const int originalID = ICN::WELLXTRA; + loadICN( originalID ); + + if ( _icnVsSprite[originalID].size() < 3 ) { + return true; + } + _icnVsSprite[id].resize( 2 ); + + for ( int32_t i = 0; i < static_cast( _icnVsSprite[id].size() ); ++i ) { + const fheroes2::Sprite & original = fheroes2::AGG::GetICN( originalID, 0 + i ); + + fheroes2::Sprite & out = _icnVsSprite[id][i]; + // the empty button needs to shortened by 1 px so that when it is divided by 3 in resizeButton() in ui_tools.h it will give an integer result + out.resize( original.width() - 1, original.height() ); - _icnVsSprite[id].resize( 2 ); - // move dark border to new released state from original pressed state button - const Sprite & originalReleased = GetICN( originalID, 4 ); - const Sprite & originalPressed = GetICN( originalID, 5 ); - if ( originalReleased.width() != 94 && originalPressed.width() != 94 && originalReleased.height() < 5 && originalPressed.height() < 5 ) { - return true; - } - Sprite & releasedWithDarkBorder = _icnVsSprite[id][0]; - releasedWithDarkBorder.resize( originalReleased.width() + 2, originalReleased.height() + 1 ); - releasedWithDarkBorder.reset(); - - Copy( originalReleased, 0, 0, releasedWithDarkBorder, 1, 0, originalReleased.width(), originalReleased.height() ); - Copy( originalReleased, 0, 2, releasedWithDarkBorder, 1, 21, 1, 1 ); - Copy( originalReleased, 0, 2, releasedWithDarkBorder, 2, 22, 1, 1 ); - Copy( originalPressed, 0, 2, releasedWithDarkBorder, 0, 3, 1, 19 ); - Copy( originalPressed, 0, originalPressed.height() - 1, releasedWithDarkBorder, 0, originalPressed.height(), originalPressed.width(), 1 ); - Copy( originalPressed, 0, 2, releasedWithDarkBorder, 1, 22, 1, 1 ); - - // pressed state - Sprite & pressed = _icnVsSprite[id][1]; - pressed.resize( originalPressed.width() + 2, originalPressed.height() + 1 ); - pressed.reset(); - Copy( originalPressed, 0, 0, pressed, 0, 1, originalPressed.width(), originalPressed.height() ); + Copy( original, 0, 0, out, 0, 0, original.width() - 4, original.height() ); + Copy( original, original.width() - 3, 0, out, original.width() - 4, 0, 3, original.height() ); - // the empty buttons need to be widened by 1 px so that they can be evenly divided by 3 in resizeButton() in ui_tools.cpp - Copy( originalReleased, originalReleased.width() - 5, 0, releasedWithDarkBorder, releasedWithDarkBorder.width() - 5, 0, 5, originalReleased.height() ); - Copy( originalPressed, originalPressed.width() - 5, 0, pressed, pressed.width() - 6, 1, 5, originalPressed.height() ); + Fill( out, 7 - i * 2, 2 + i, 50 + i, 14, getButtonFillingColor( i == 0 ) ); + } - const int32_t pixelPosition = 4 * 94 + 6; - Fill( releasedWithDarkBorder, 5, 3, 88, 18, originalReleased.image()[pixelPosition] ); - Fill( pressed, 4, 5, 87, 17, originalPressed.image()[pixelPosition] ); + return true; + } + case ICN::EMPTY_GOOD_MEDIUM_BUTTON: + case ICN::EMPTY_EVIL_MEDIUM_BUTTON: { + const bool isGoodInterface = ( id == ICN::EMPTY_GOOD_MEDIUM_BUTTON ); + const int32_t originalId = isGoodInterface ? ICN::APANEL : ICN::APANELE; + loadICN( originalId ); + if ( _icnVsSprite[originalId].size() < 10 ) { return true; } - case ICN::EMPTY_GUILDWELL_BUTTON: { - const int originalID = ICN::WELLXTRA; - loadICN( originalID ); - if ( _icnVsSprite[originalID].size() < 3 ) { - return true; - } - _icnVsSprite[id].resize( 2 ); + _icnVsSprite[id].resize( 2 ); - for ( int32_t i = 0; i < static_cast( _icnVsSprite[id].size() ); ++i ) { - const Sprite & original = GetICN( originalID, 0 + i ); + fheroes2::Sprite & released = _icnVsSprite[id][0]; + fheroes2::Sprite & pressed = _icnVsSprite[id][1]; - Sprite & out = _icnVsSprite[id][i]; - // the empty button needs to shortened by 1 px so that when it is divided by 3 in resizeButton() in ui_tools.h it will give an integer result - out.resize( original.width() - 1, original.height() ); + released = _icnVsSprite[originalId][2]; + pressed = _icnVsSprite[originalId][3]; - Copy( original, 0, 0, out, 0, 0, original.width() - 4, original.height() ); - Copy( original, original.width() - 3, 0, out, original.width() - 4, 0, 3, original.height() ); + if ( released.width() > 2 && released.height() > 2 && pressed.width() > 2 && pressed.height() > 2 ) { + // Clean the buttons. + Fill( released, 28, 15, 42, 27, getButtonFillingColor( true, isGoodInterface ) ); + Fill( pressed, 27, 16, 42, 27, getButtonFillingColor( false, isGoodInterface ) ); + } - Fill( out, 7 - i * 2, 2 + i, 50 + i, 14, getButtonFillingColor( i == 0 ) ); - } + return true; + } + case ICN::EMPTY_VERTICAL_GOOD_BUTTON: { + const int32_t originalId = ICN::HSBTNS; + loadICN( originalId ); + if ( _icnVsSprite[originalId].size() < 9 ) { return true; } - case ICN::EMPTY_GOOD_MEDIUM_BUTTON: - case ICN::EMPTY_EVIL_MEDIUM_BUTTON: { - const bool isGoodInterface = ( id == ICN::EMPTY_GOOD_MEDIUM_BUTTON ); - const int32_t originalId = isGoodInterface ? ICN::APANEL : ICN::APANELE; - loadICN( originalId ); - if ( _icnVsSprite[originalId].size() < 10 ) { - return true; - } + _icnVsSprite[id].resize( 2 ); + const fheroes2::Sprite & originalReleased = fheroes2::AGG::GetICN( originalId, 2 ); + const fheroes2::Sprite & originalPressed = fheroes2::AGG::GetICN( originalId, 3 ); - _icnVsSprite[id].resize( 2 ); + fheroes2::Sprite & released = _icnVsSprite[id][0]; + fheroes2::Sprite & pressed = _icnVsSprite[id][1]; - Sprite & released = _icnVsSprite[id][0]; - Sprite & pressed = _icnVsSprite[id][1]; + if ( originalReleased.width() > 2 && originalReleased.height() > 2 && originalPressed.width() > 2 && originalPressed.height() > 2 ) { + released.resize( originalReleased.width() + 1, originalReleased.height() + 1 ); + pressed.resize( originalPressed.width() + 1, originalPressed.height() + 1 ); + released.reset(); + pressed.reset(); - released = _icnVsSprite[originalId][2]; - pressed = _icnVsSprite[originalId][3]; + Copy( originalReleased, 0, 0, released, 1, 0, originalReleased.width(), originalReleased.height() ); + Copy( originalPressed, 0, 0, pressed, 1, 0, originalPressed.width(), originalPressed.height() ); + FillTransform( released, 1, 4, 1, released.height() - 4, 1 ); - if ( released.width() > 2 && released.height() > 2 && pressed.width() > 2 && pressed.height() > 2 ) { - // Clean the buttons. - Fill( released, 28, 15, 42, 27, getButtonFillingColor( true, isGoodInterface ) ); - Fill( pressed, 27, 16, 42, 27, getButtonFillingColor( false, isGoodInterface ) ); - } + // Fix the carried over broken transform layer of the original vertical button that is being used. + fheroes2::Image exitCommonMask = fheroes2::ExtractCommonPattern( { &released, &pressed } ); + // Fix wrong non-transparent pixels of the transform layer that ExtractCommonPattern() missed. + FillTransform( exitCommonMask, 4, 2, 1, 115, 1 ); + FillTransform( exitCommonMask, 5, 116, 1, 1, 1 ); + FillTransform( exitCommonMask, 5, 117, 18, 1, 1 ); + FillTransform( exitCommonMask, exitCommonMask.width() - 4, 114, 1, 2, 1 ); - return true; - } - case ICN::EMPTY_VERTICAL_GOOD_BUTTON: { - const int32_t originalId = ICN::HSBTNS; - loadICN( originalId ); + invertTransparency( exitCommonMask ); + // Make the extended width and height lines transparent. + FillTransform( exitCommonMask, 0, 0, 1, exitCommonMask.height(), 1 ); + FillTransform( exitCommonMask, exitCommonMask.width() - 4, exitCommonMask.height() - 1, 4, 1, 1 ); - if ( _icnVsSprite[originalId].size() < 9 ) { - return true; - } + CopyTransformLayer( exitCommonMask, released ); + CopyTransformLayer( exitCommonMask, pressed ); - _icnVsSprite[id].resize( 2 ); - const Sprite & originalReleased = GetICN( originalId, 2 ); - const Sprite & originalPressed = GetICN( originalId, 3 ); + // Restore dark-brown lines on the left and bottom borders of the button backgrounds. + const fheroes2::Sprite & originalDismiss = fheroes2::AGG::GetICN( ICN::HSBTNS, 0 ); - Sprite & released = _icnVsSprite[id][0]; - Sprite & pressed = _icnVsSprite[id][1]; + Copy( originalReleased, 0, 4, released, 1, 4, 1, released.height() - 4 ); + Copy( originalDismiss, 6, originalDismiss.height() - 7, released, 2, released.height() - 1, 22, 1 ); + Copy( originalDismiss, 6, originalDismiss.height() - 7, released, 1, released.height() - 1, 1, 1 ); - if ( originalReleased.width() > 2 && originalReleased.height() > 2 && originalPressed.width() > 2 && originalPressed.height() > 2 ) { - released.resize( originalReleased.width() + 1, originalReleased.height() + 1 ); - pressed.resize( originalPressed.width() + 1, originalPressed.height() + 1 ); - released.reset(); - pressed.reset(); + Copy( originalPressed, 0, 4, pressed, 1, 4, 1, pressed.height() - 4 ); + Copy( originalDismiss, 6, originalDismiss.height() - 7, pressed, 2, pressed.height() - 1, 22, 1 ); + Copy( originalDismiss, 6, originalDismiss.height() - 7, pressed, 1, pressed.height() - 1, 1, 1 ); - Copy( originalReleased, 0, 0, released, 1, 0, originalReleased.width(), originalReleased.height() ); - Copy( originalPressed, 0, 0, pressed, 1, 0, originalPressed.width(), originalPressed.height() ); - FillTransform( released, 1, 4, 1, released.height() - 4, 1 ); - - // Fix the carried over broken transform layer of the original vertical button that is being used. - Image exitCommonMask = ExtractCommonPattern( { &released, &pressed } ); - // Fix wrong non-transparent pixels of the transform layer that ExtractCommonPattern() missed. - FillTransform( exitCommonMask, 4, 2, 1, 115, 1 ); - FillTransform( exitCommonMask, 5, 116, 1, 1, 1 ); - FillTransform( exitCommonMask, 5, 117, 18, 1, 1 ); - FillTransform( exitCommonMask, exitCommonMask.width() - 4, 114, 1, 2, 1 ); - - invertTransparency( exitCommonMask ); - // Make the extended width and height lines transparent. - FillTransform( exitCommonMask, 0, 0, 1, exitCommonMask.height(), 1 ); - FillTransform( exitCommonMask, exitCommonMask.width() - 4, exitCommonMask.height() - 1, 4, 1, 1 ); - - CopyTransformLayer( exitCommonMask, released ); - CopyTransformLayer( exitCommonMask, pressed ); - - // Restore dark-brown lines on the left and bottom borders of the button backgrounds. - const Sprite & originalDismiss = GetICN( ICN::HSBTNS, 0 ); - - Copy( originalReleased, 0, 4, released, 1, 4, 1, released.height() - 4 ); - Copy( originalDismiss, 6, originalDismiss.height() - 7, released, 2, released.height() - 1, 22, 1 ); - Copy( originalDismiss, 6, originalDismiss.height() - 7, released, 1, released.height() - 1, 1, 1 ); - - Copy( originalPressed, 0, 4, pressed, 1, 4, 1, pressed.height() - 4 ); - Copy( originalDismiss, 6, originalDismiss.height() - 7, pressed, 2, pressed.height() - 1, 22, 1 ); - Copy( originalDismiss, 6, originalDismiss.height() - 7, pressed, 1, pressed.height() - 1, 1, 1 ); - - // Clean the button states' text areas. - Fill( released, 6, 4, 18, 111, getButtonFillingColor( true ) ); - Fill( pressed, 5, 5, 18, 111, getButtonFillingColor( false ) ); - - // Make the background transparent by removing remaining red background pieces. - FillTransform( pressed, 5, 0, 21, 1, 1 ); - FillTransform( pressed, pressed.width() - 3, 1, 2, 1, 1 ); - FillTransform( pressed, pressed.width() - 2, 2, 2, 1, 1 ); - FillTransform( pressed, pressed.width() - 1, 3, 1, pressed.height() - 6, 1 ); - } + // Clean the button states' text areas. + Fill( released, 6, 4, 18, 111, getButtonFillingColor( true ) ); + Fill( pressed, 5, 5, 18, 111, getButtonFillingColor( false ) ); - return true; + // Make the background transparent by removing remaining red background pieces. + FillTransform( pressed, 5, 0, 21, 1, 1 ); + FillTransform( pressed, pressed.width() - 3, 1, 2, 1, 1 ); + FillTransform( pressed, pressed.width() - 2, 2, 2, 1, 1 ); + FillTransform( pressed, pressed.width() - 1, 3, 1, pressed.height() - 6, 1 ); } - case ICN::EMPTY_MAP_SELECT_BUTTON: { - const int32_t originalId = ICN::NGEXTRA; - loadICN( originalId ); - if ( _icnVsSprite[originalId].size() < 80 ) { - return true; - } + return true; + } + case ICN::EMPTY_MAP_SELECT_BUTTON: { + const int32_t originalId = ICN::NGEXTRA; + loadICN( originalId ); - _icnVsSprite[id].resize( 2 ); + if ( _icnVsSprite[originalId].size() < 80 ) { + return true; + } - for ( int32_t i = 0; i < static_cast( _icnVsSprite[id].size() ); ++i ) { - const Sprite & original = GetICN( originalId, 64 + i ); + _icnVsSprite[id].resize( 2 ); - Sprite & out = _icnVsSprite[id][i]; - // the empty button needs to widened by 1 px so that when it is divided by 3 in resizeButton() in ui_tools.h it will - // give an integer result - out.resize( original.width() + 1, original.height() ); + for ( int32_t i = 0; i < static_cast( _icnVsSprite[id].size() ); ++i ) { + const fheroes2::Sprite & original = fheroes2::AGG::GetICN( originalId, 64 + i ); - Copy( original, 0, 0, out, 0, 0, original.width() - 5, original.height() ); - Copy( original, original.width() - 6, 0, out, original.width() - 5, 0, 6, original.height() ); + fheroes2::Sprite & out = _icnVsSprite[id][i]; + // the empty button needs to widened by 1 px so that when it is divided by 3 in resizeButton() in ui_tools.h it will + // give an integer result + out.resize( original.width() + 1, original.height() ); - Fill( out, 6 - i, 2 + 2 * i, 72, 15 - i, getButtonFillingColor( i == 0 ) ); - } + Copy( original, 0, 0, out, 0, 0, original.width() - 5, original.height() ); + Copy( original, original.width() - 6, 0, out, original.width() - 5, 0, 6, original.height() ); - return true; + Fill( out, 6 - i, 2 + 2 * i, 72, 15 - i, getButtonFillingColor( i == 0 ) ); } - case ICN::BRCREST: { - LoadOriginalICN( id ); - // First sprite in this ICN has incorrect transparent pixel at position 30x5. - if ( !_icnVsSprite[id].empty() ) { - Sprite & original = _icnVsSprite[id][0]; - if ( original.width() == 50 && original.height() == 47 ) { - original._disableTransformLayer(); - original.image()[280] = 117; - } + + return true; + } + case ICN::BRCREST: { + LoadOriginalICN( id ); + // First sprite in this ICN has incorrect transparent pixel at position 30x5. + if ( !_icnVsSprite[id].empty() ) { + fheroes2::Sprite & original = _icnVsSprite[id][0]; + if ( original.width() == 50 && original.height() == 47 ) { + original._disableTransformLayer(); + original.image()[280] = 117; } + } - // An extra image for the neutral color (for Editor). - if ( _icnVsSprite[id].size() == 7 ) { - Sprite neutralShield( GetICN( ICN::SPELLS, 15 ) ); - if ( neutralShield.width() < 2 || neutralShield.height() < 2 ) { - // We can not make a new image if there is no original shield image. - return true; - } + // An extra image for the neutral color (for Editor). + if ( _icnVsSprite[id].size() == 7 ) { + fheroes2::Sprite neutralShield( fheroes2::AGG::GetICN( ICN::SPELLS, 15 ) ); + if ( neutralShield.width() < 2 || neutralShield.height() < 2 ) { + // We can not make a new image if there is no original shield image. + return true; + } - // Make original shield image contour transparent. - ReplaceColorIdByTransformId( neutralShield, neutralShield.image()[1], 1U ); - - Sprite neutralColorSprite( _icnVsSprite[id][0].width(), _icnVsSprite[id][0].height() ); - neutralColorSprite.reset(); - Blit( neutralShield, neutralColorSprite, 8, 4 ); - - // Make the background. - uint8_t * imageData = neutralColorSprite.image(); - const uint8_t * transformData = neutralColorSprite.transform(); - const int32_t imageWidth = neutralColorSprite.width(); - const int32_t imageHeight = neutralColorSprite.height(); - const int32_t imageSize = imageWidth * imageHeight; - const int32_t startValueX = 12 * imageWidth; - const int32_t startValueY = 12 * imageHeight; - - for ( int32_t y = 0; y < imageHeight; ++y ) { - const int32_t offsetY = y * imageWidth; - const int32_t offsetValueY = y * startValueX; - for ( int32_t x = 0; x < imageWidth; ++x ) { - if ( transformData[x + offsetY] == 0U ) { - // Skip pixels with image. - continue; - } - - const uint8_t colorValue = static_cast( 10 + ( offsetValueY + ( imageWidth - x ) * startValueY ) / imageSize + ( x + y ) % 2 ); - imageData[x + offsetY] = ( ( imageWidth - x - 1 ) * imageHeight > offsetY ) ? colorValue : 44U - colorValue; + // Make original shield image contour transparent. + ReplaceColorIdByTransformId( neutralShield, neutralShield.image()[1], 1U ); + + fheroes2::Sprite neutralColorSprite( _icnVsSprite[id][0].width(), _icnVsSprite[id][0].height() ); + neutralColorSprite.reset(); + Blit( neutralShield, neutralColorSprite, 8, 4 ); + + // Make the background. + uint8_t * imageData = neutralColorSprite.image(); + const uint8_t * transformData = neutralColorSprite.transform(); + const int32_t imageWidth = neutralColorSprite.width(); + const int32_t imageHeight = neutralColorSprite.height(); + const int32_t imageSize = imageWidth * imageHeight; + const int32_t startValueX = 12 * imageWidth; + const int32_t startValueY = 12 * imageHeight; + + for ( int32_t y = 0; y < imageHeight; ++y ) { + const int32_t offsetY = y * imageWidth; + const int32_t offsetValueY = y * startValueX; + for ( int32_t x = 0; x < imageWidth; ++x ) { + if ( transformData[x + offsetY] == 0U ) { + // Skip pixels with image. + continue; } + + const uint8_t colorValue = static_cast( 10 + ( offsetValueY + ( imageWidth - x ) * startValueY ) / imageSize + ( x + y ) % 2 ); + imageData[x + offsetY] = ( ( imageWidth - x - 1 ) * imageHeight > offsetY ) ? colorValue : 44U - colorValue; } + } - // Make all image non-transparent. - neutralColorSprite._disableTransformLayer(); - // We add shadow twice to make it more dark. - addGradientShadow( neutralShield, neutralColorSprite, { 8, 4 }, { -2, 5 } ); - addGradientShadow( neutralShield, neutralColorSprite, { 8, 4 }, { -2, 5 } ); + // Make all image non-transparent. + neutralColorSprite._disableTransformLayer(); + // We add shadow twice to make it more dark. + addGradientShadow( neutralShield, neutralColorSprite, { 8, 4 }, { -2, 5 } ); + addGradientShadow( neutralShield, neutralColorSprite, { 8, 4 }, { -2, 5 } ); - _icnVsSprite[id].push_back( std::move( neutralColorSprite ) ); - } - return true; + _icnVsSprite[id].push_back( std::move( neutralColorSprite ) ); } - case ICN::CBKGWATR: { - // Ship battlefield background has incorrect transparent pixel at position 125x36. - LoadOriginalICN( id ); - if ( !_icnVsSprite[id].empty() ) { - Sprite & original = _icnVsSprite[id][0]; - if ( original.width() == 640 && original.height() == 443 ) { - original._disableTransformLayer(); - original.image()[23165] = 24; - } + return true; + } + case ICN::CBKGWATR: { + // Ship battlefield background has incorrect transparent pixel at position 125x36. + LoadOriginalICN( id ); + if ( !_icnVsSprite[id].empty() ) { + fheroes2::Sprite & original = _icnVsSprite[id][0]; + if ( original.width() == 640 && original.height() == 443 ) { + original._disableTransformLayer(); + original.image()[23165] = 24; } - return true; } - case ICN::SWAPWIN: - case ICN::WELLBKG: { - // Hero Meeting dialog and Castle Well images can be used with disabled transform layer. - LoadOriginalICN( id ); - if ( !_icnVsSprite[id].empty() ) { - _icnVsSprite[id][0]._disableTransformLayer(); - } - return true; + return true; + } + case ICN::SWAPWIN: + case ICN::WELLBKG: { + // Hero Meeting dialog and Castle Well images can be used with disabled transform layer. + LoadOriginalICN( id ); + if ( !_icnVsSprite[id].empty() ) { + _icnVsSprite[id][0]._disableTransformLayer(); } - case ICN::GAME_OPTION_ICON: { - _icnVsSprite[id].resize( 2 ); + return true; + } + case ICN::GAME_OPTION_ICON: { + _icnVsSprite[id].resize( 2 ); - h2d::readImage( "hotkeys_icon.image", _icnVsSprite[id][0] ); - h2d::readImage( "graphics_icon.image", _icnVsSprite[id][1] ); + fheroes2::h2d::readImage( "hotkeys_icon.image", _icnVsSprite[id][0] ); + fheroes2::h2d::readImage( "graphics_icon.image", _icnVsSprite[id][1] ); - return true; - } - case ICN::COVR0010: - case ICN::COVR0011: - case ICN::COVR0012: { - // The original image contains some foreign pixels that do not belong to the image. - LoadOriginalICN( id ); - - if ( !_icnVsSprite[id].empty() ) { - Sprite & sprite = _icnVsSprite[id][0]; - const uint8_t * image = sprite.image(); - const uint8_t * imageEnd = image + static_cast( sprite.width() ) * sprite.height(); - uint8_t * transform = sprite.transform(); - - for ( ; image != imageEnd; ++image, ++transform ) { - // Mask all non white/black or brown pixels. - if ( *transform == 0 && *image > 36 && ( *image < 108 || *image > 130 ) ) { - *transform = 1; - } + return true; + } + case ICN::COVR0010: + case ICN::COVR0011: + case ICN::COVR0012: { + // The original image contains some foreign pixels that do not belong to the image. + LoadOriginalICN( id ); + + if ( !_icnVsSprite[id].empty() ) { + fheroes2::Sprite & sprite = _icnVsSprite[id][0]; + const uint8_t * image = sprite.image(); + const uint8_t * imageEnd = image + static_cast( sprite.width() ) * sprite.height(); + uint8_t * transform = sprite.transform(); + + for ( ; image != imageEnd; ++image, ++transform ) { + // Mask all non white/black or brown pixels. + if ( *transform == 0 && *image > 36 && ( *image < 108 || *image > 130 ) ) { + *transform = 1; } } - - return true; } - case ICN::OBJNDSRT: { - LoadOriginalICN( id ); - if ( _icnVsSprite[id].size() == 131 ) { - _icnVsSprite[id].resize( 132 ); - h2d::readImage( "missing_sphinx_part.image", _icnVsSprite[id][131] ); - } - return true; + return true; + } + case ICN::OBJNDSRT: { + LoadOriginalICN( id ); + if ( _icnVsSprite[id].size() == 131 ) { + _icnVsSprite[id].resize( 132 ); + fheroes2::h2d::readImage( "missing_sphinx_part.image", _icnVsSprite[id][131] ); } - case ICN::OBJNGRAS: { - LoadOriginalICN( id ); - if ( _icnVsSprite[id].size() == 151 ) { - _icnVsSprite[id].resize( 153 ); - loadICN( ICN::OBJNSNOW ); + return true; + } + case ICN::OBJNGRAS: { + LoadOriginalICN( id ); + if ( _icnVsSprite[id].size() == 151 ) { + _icnVsSprite[id].resize( 153 ); - if ( _icnVsSprite[ICN::OBJNSNOW].size() > 210 ) { - fheroes2::Sprite temp; + loadICN( ICN::OBJNSNOW ); - h2d::readImage( "adventure-map-grass-cave-diff-01.image", temp ); - _icnVsSprite[id][151] = _icnVsSprite[ICN::OBJNSNOW][2]; - Blit( temp, _icnVsSprite[id][151] ); + if ( _icnVsSprite[ICN::OBJNSNOW].size() > 210 ) { + fheroes2::Sprite temp; - ReplaceColorIdByTransformId( _icnVsSprite[id][151], 255, 1 ); + fheroes2::h2d::readImage( "adventure-map-grass-cave-diff-01.image", temp ); + _icnVsSprite[id][151] = _icnVsSprite[ICN::OBJNSNOW][2]; + Blit( temp, _icnVsSprite[id][151] ); - h2d::readImage( "adventure-map-grass-cave-diff-02.image", temp ); - _icnVsSprite[id][152] = _icnVsSprite[ICN::OBJNSNOW][3]; - Blit( temp, _icnVsSprite[id][152] ); + ReplaceColorIdByTransformId( _icnVsSprite[id][151], 255, 1 ); - ReplaceColorIdByTransformId( _icnVsSprite[id][152], 255, 1 ); - } - } + fheroes2::h2d::readImage( "adventure-map-grass-cave-diff-02.image", temp ); + _icnVsSprite[id][152] = _icnVsSprite[ICN::OBJNSNOW][3]; + Blit( temp, _icnVsSprite[id][152] ); - return true; - } - case ICN::OBJNMUL2: { - LoadOriginalICN( id ); - auto & images = _icnVsSprite[id]; - if ( images.size() == 218 ) { - // Add 2 extra river deltas. Each delta has 7 parts. - images.resize( 218 + 14 ); - - for ( size_t i = 0; i < 14; ++i ) { - images[218 + i].resize( images[i].height(), images[i].width() ); - fheroes2::Transpose( images[i], images[218 + i] ); - images[218 + i].setPosition( images[i].y(), images[i].x() ); - } + ReplaceColorIdByTransformId( _icnVsSprite[id][152], 255, 1 ); } - - return true; - } - default: - break; } - return false; + return true; } + case ICN::OBJNMUL2: { + LoadOriginalICN( id ); + auto & images = _icnVsSprite[id]; + if ( images.size() == 218 ) { + // Add 2 extra river deltas. Each delta has 7 parts. + images.resize( 218 + 14 ); - void loadICN( const int id ) - { - if ( !_icnVsSprite[id].empty() ) { - // The images have been loaded. - return; - } - - if ( !LoadModifiedICN( id ) ) { - LoadOriginalICN( id ); + for ( size_t i = 0; i < 14; ++i ) { + images[218 + i].resize( images[i].height(), images[i].width() ); + fheroes2::Transpose( images[i], images[218 + i] ); + images[218 + i].setPosition( images[i].y(), images[i].x() ); + } } - if ( _icnVsSprite[id].empty() ) { - // This could happen by one reason: asking to render an ICN that simply doesn't exist within the resources. - // In order to avoid subsequent attempts to get resources from this ICN we are making it as non-empty. - _icnVsSprite[id].resize( 1 ); - } + return true; + } + default: + break; } - size_t GetMaximumICNIndex( int id ) - { - loadICN( id ); + return false; + } - return _icnVsSprite[id].size(); + void loadICN( const int id ) + { + if ( !_icnVsSprite[id].empty() ) { + // The images have been loaded. + return; } - size_t GetMaximumTILIndex( const int id ) - { - auto & tilImages = _tilVsImage[id]; + if ( !LoadModifiedICN( id ) ) { + LoadOriginalICN( id ); + } - if ( tilImages.empty() ) { - tilImages.resize( 4 ); // 4 possible sides + if ( _icnVsSprite[id].empty() ) { + // This could happen by one reason: asking to render an ICN that simply doesn't exist within the resources. + // In order to avoid subsequent attempts to get resources from this ICN we are making it as non-empty. + _icnVsSprite[id].resize( 1 ); + } + } - const std::vector & data = ::AGG::getDataFromAggFile( tilFileName[id] ); - if ( data.size() < headerSize ) { - // The important resource is absent! Make sure that you are using the correct version of the game. - assert( 0 ); - return 0; - } + size_t GetMaximumICNIndex( int id ) + { + loadICN( id ); - StreamBuf buffer( data ); + return _icnVsSprite[id].size(); + } - const size_t count = buffer.getLE16(); - const int32_t width = buffer.getLE16(); - const int32_t height = buffer.getLE16(); - if ( count < 1 || width < 1 || height < 1 || ( headerSize + count * width * height ) != data.size() ) { - return 0; - } + size_t GetMaximumTILIndex( const int id ) + { + auto & tilImages = _tilVsImage[id]; - std::vector & originalTIL = tilImages[0]; - decodeTILImages( data.data() + headerSize, count, width, height, originalTIL ); + if ( tilImages.empty() ) { + tilImages.resize( 4 ); // 4 possible sides - for ( uint32_t shapeId = 1; shapeId < 4; ++shapeId ) { - tilImages[shapeId].resize( count ); - } + const std::vector & data = ::AGG::getDataFromAggFile( tilFileName[id] ); + if ( data.size() < headerSize ) { + // The important resource is absent! Make sure that you are using the correct version of the game. + assert( 0 ); + return 0; + } - for ( size_t i = 0; i < count; ++i ) { - for ( uint32_t shapeId = 1; shapeId < 4; ++shapeId ) { - Image & image = tilImages[shapeId][i]; + StreamBuf buffer( data ); - const bool horizontalFlip = ( shapeId & 2 ) != 0; - const bool verticalFlip = ( shapeId & 1 ) != 0; + const size_t count = buffer.getLE16(); + const int32_t width = buffer.getLE16(); + const int32_t height = buffer.getLE16(); + if ( count < 1 || width < 1 || height < 1 || ( headerSize + count * width * height ) != data.size() ) { + return 0; + } - image._disableTransformLayer(); - image.resize( width, height ); + std::vector & originalTIL = tilImages[0]; + decodeTILImages( data.data() + headerSize, count, width, height, originalTIL ); - Flip( originalTIL[i], 0, 0, image, 0, 0, width, height, horizontalFlip, verticalFlip ); - } - } + for ( uint32_t shapeId = 1; shapeId < 4; ++shapeId ) { + tilImages[shapeId].resize( count ); } - return tilImages[0].size(); - } + for ( size_t i = 0; i < count; ++i ) { + for ( uint32_t shapeId = 1; shapeId < 4; ++shapeId ) { + fheroes2::Image & image = tilImages[shapeId][i]; - // We have few ICNs which we need to scale like some related to main screen - bool IsScalableICN( const int id ) - { - switch ( id ) { - case ICN::EDITOR: - case ICN::HEROES: - case ICN::BTNSHNGL: - case ICN::SHNGANIM: - return true; - default: - return false; + const bool horizontalFlip = ( shapeId & 2 ) != 0; + const bool verticalFlip = ( shapeId & 1 ) != 0; + + image._disableTransformLayer(); + image.resize( width, height ); + + Flip( originalTIL[i], 0, 0, image, 0, 0, width, height, horizontalFlip, verticalFlip ); + } } } - const Sprite & GetScaledICN( const int icnId, const uint32_t index ) - { - const Sprite & originalIcn = _icnVsSprite[icnId][index]; - const Display & display = Display::instance(); + return tilImages[0].size(); + } - if ( display.width() == Display::DEFAULT_WIDTH && display.height() == Display::DEFAULT_HEIGHT ) { - return originalIcn; - } + // We have few ICNs which we need to scale like some related to main screen + bool IsScalableICN( const int id ) + { + switch ( id ) { + case ICN::EDITOR: + case ICN::HEROES: + case ICN::BTNSHNGL: + case ICN::SHNGANIM: + return true; + default: + return false; + } + } - if ( _icnVsScaledSprite[icnId].empty() ) { - _icnVsScaledSprite[icnId].resize( _icnVsSprite[icnId].size() ); - } + const fheroes2::Sprite & GetScaledICN( const int icnId, const uint32_t index ) + { + const fheroes2::Sprite & originalIcn = _icnVsSprite[icnId][index]; + const fheroes2::Display & display = fheroes2::Display::instance(); - Sprite & resizedIcn = _icnVsScaledSprite[icnId][index]; + if ( display.width() == fheroes2::Display::DEFAULT_WIDTH && display.height() == fheroes2::Display::DEFAULT_HEIGHT ) { + return originalIcn; + } - if ( originalIcn.singleLayer() && !resizedIcn.singleLayer() ) { - resizedIcn._disableTransformLayer(); - } + if ( _icnVsScaledSprite[icnId].empty() ) { + _icnVsScaledSprite[icnId].resize( _icnVsSprite[icnId].size() ); + } - const double scaleFactorX = static_cast( display.width() ) / Display::DEFAULT_WIDTH; - const double scaleFactorY = static_cast( display.height() ) / Display::DEFAULT_HEIGHT; + fheroes2::Sprite & resizedIcn = _icnVsScaledSprite[icnId][index]; - const double scaleFactor = std::min( scaleFactorX, scaleFactorY ); - const int32_t resizedWidth = static_cast( std::lround( originalIcn.width() * scaleFactor ) ); - const int32_t resizedHeight = static_cast( std::lround( originalIcn.height() * scaleFactor ) ); - const int32_t offsetX = static_cast( std::lround( display.width() - Display::DEFAULT_WIDTH * scaleFactor ) ) / 2; - const int32_t offsetY = static_cast( std::lround( display.height() - Display::DEFAULT_HEIGHT * scaleFactor ) ) / 2; - assert( offsetX >= 0 && offsetY >= 0 ); + if ( originalIcn.singleLayer() && !resizedIcn.singleLayer() ) { + resizedIcn._disableTransformLayer(); + } - // Resize only if needed - if ( resizedIcn.height() != resizedHeight || resizedIcn.width() != resizedWidth ) { - resizedIcn.resize( resizedWidth, resizedHeight ); - resizedIcn.setPosition( static_cast( std::lround( originalIcn.x() * scaleFactor ) ) + offsetX, - static_cast( std::lround( originalIcn.y() * scaleFactor ) ) + offsetY ); - Resize( originalIcn, resizedIcn ); - } - else { - // No need to resize but we have to update the offset. - resizedIcn.setPosition( static_cast( std::lround( originalIcn.x() * scaleFactor ) ) + offsetX, - static_cast( std::lround( originalIcn.y() * scaleFactor ) ) + offsetY ); - } + const double scaleFactorX = static_cast( display.width() ) / fheroes2::Display::DEFAULT_WIDTH; + const double scaleFactorY = static_cast( display.height() ) / fheroes2::Display::DEFAULT_HEIGHT; + + const double scaleFactor = std::min( scaleFactorX, scaleFactorY ); + const int32_t resizedWidth = static_cast( std::lround( originalIcn.width() * scaleFactor ) ); + const int32_t resizedHeight = static_cast( std::lround( originalIcn.height() * scaleFactor ) ); + const int32_t offsetX = static_cast( std::lround( display.width() - fheroes2::Display::DEFAULT_WIDTH * scaleFactor ) ) / 2; + const int32_t offsetY = static_cast( std::lround( display.height() - fheroes2::Display::DEFAULT_HEIGHT * scaleFactor ) ) / 2; + assert( offsetX >= 0 && offsetY >= 0 ); + + // Resize only if needed + if ( resizedIcn.height() != resizedHeight || resizedIcn.width() != resizedWidth ) { + resizedIcn.resize( resizedWidth, resizedHeight ); + resizedIcn.setPosition( static_cast( std::lround( originalIcn.x() * scaleFactor ) ) + offsetX, + static_cast( std::lround( originalIcn.y() * scaleFactor ) ) + offsetY ); + Resize( originalIcn, resizedIcn ); + } + else { + // No need to resize but we have to update the offset. + resizedIcn.setPosition( static_cast( std::lround( originalIcn.x() * scaleFactor ) ) + offsetX, + static_cast( std::lround( originalIcn.y() * scaleFactor ) ) + offsetY ); + } + + return resizedIcn; + } +} - return resizedIcn; +namespace fheroes2::AGG +{ + const Sprite & GetICN( int icnId, uint32_t index ) + { + if ( !IsValidICNId( icnId ) ) { + return errorImage; } - const Sprite & GetICN( int icnId, uint32_t index ) - { - if ( !IsValidICNId( icnId ) ) { - return errorImage; - } + if ( index >= GetMaximumICNIndex( icnId ) ) { + return errorImage; + } - if ( index >= GetMaximumICNIndex( icnId ) ) { - return errorImage; - } + if ( IsScalableICN( icnId ) ) { + return GetScaledICN( icnId, index ); + } - if ( IsScalableICN( icnId ) ) { - return GetScaledICN( icnId, index ); - } + return _icnVsSprite[icnId][index]; + } - return _icnVsSprite[icnId][index]; + uint32_t GetICNCount( int icnId ) + { + if ( !IsValidICNId( icnId ) ) { + return 0; } - uint32_t GetICNCount( int icnId ) - { - if ( !IsValidICNId( icnId ) ) { - return 0; - } + return static_cast( GetMaximumICNIndex( icnId ) ); + } - return static_cast( GetMaximumICNIndex( icnId ) ); + const Image & GetTIL( int tilId, uint32_t index, uint32_t shapeId ) + { + if ( shapeId > 3 ) { + return errorImage; } - const Image & GetTIL( int tilId, uint32_t index, uint32_t shapeId ) - { - if ( shapeId > 3 ) { - return errorImage; - } + if ( !IsValidTILId( tilId ) ) { + return errorImage; + } - if ( !IsValidTILId( tilId ) ) { - return errorImage; - } + const size_t maxTILIndex = GetMaximumTILIndex( tilId ); + if ( index >= maxTILIndex ) { + return errorImage; + } - const size_t maxTILIndex = GetMaximumTILIndex( tilId ); - if ( index >= maxTILIndex ) { - return errorImage; - } + return _tilVsImage[tilId][shapeId][index]; + } - return _tilVsImage[tilId][shapeId][index]; + int32_t GetAbsoluteICNHeight( int icnId ) + { + const uint32_t frameCount = GetICNCount( icnId ); + if ( frameCount == 0 ) { + return 0; } - int32_t GetAbsoluteICNHeight( int icnId ) - { - const uint32_t frameCount = GetICNCount( icnId ); - if ( frameCount == 0 ) { - return 0; + int32_t height = 0; + for ( uint32_t i = 0; i < frameCount; ++i ) { + const int32_t offset = -GetICN( icnId, i ).y(); + if ( offset > height ) { + height = offset; } + } - int32_t height = 0; - for ( uint32_t i = 0; i < frameCount; ++i ) { - const int32_t offset = -GetICN( icnId, i ).y(); - if ( offset > height ) { - height = offset; - } - } + return height; + } - return height; + uint32_t getCharacterLimit( const FontSize fontSize ) + { + switch ( fontSize ) { + case FontSize::SMALL: + return static_cast( GetMaximumICNIndex( ICN::SMALFONT ) ) + 0x20 - 1; + case FontSize::NORMAL: + case FontSize::LARGE: + return static_cast( GetMaximumICNIndex( ICN::FONT ) ) + 0x20 - 1; + case FontSize::BUTTON_RELEASED: + case FontSize::BUTTON_PRESSED: + return static_cast( GetMaximumICNIndex( ICN::BUTTON_GOOD_FONT_RELEASED ) ) + 0x20 - 1; + default: + assert( 0 ); // Did you add a new font size? Please add implementation. } - uint32_t getCharacterLimit( const FontSize fontSize ) - { - switch ( fontSize ) { - case FontSize::SMALL: - return static_cast( GetMaximumICNIndex( ICN::SMALFONT ) ) + 0x20 - 1; - case FontSize::NORMAL: - case FontSize::LARGE: - return static_cast( GetMaximumICNIndex( ICN::FONT ) ) + 0x20 - 1; - case FontSize::BUTTON_RELEASED: - case FontSize::BUTTON_PRESSED: - return static_cast( GetMaximumICNIndex( ICN::BUTTON_GOOD_FONT_RELEASED ) ) + 0x20 - 1; - default: - assert( 0 ); // Did you add a new font size? Please add implementation. - } + return 0; + } - return 0; + const Sprite & getChar( const uint8_t character, const FontType & fontType ) + { + if ( character < 0x21 ) { + return errorImage; } - const Sprite & getChar( const uint8_t character, const FontType & fontType ) - { - if ( character < 0x21 ) { - return errorImage; - } - - switch ( fontType.size ) { - case FontSize::SMALL: - switch ( fontType.color ) { - case FontColor::WHITE: - return GetICN( ICN::SMALFONT, character - 0x20 ); - case FontColor::GRAY: - return GetICN( ICN::GRAY_SMALL_FONT, character - 0x20 ); - case FontColor::YELLOW: - return GetICN( ICN::YELLOW_SMALLFONT, character - 0x20 ); - default: - // Did you add a new font color? Add the corresponding logic for it! - assert( 0 ); - break; - } - break; - case FontSize::NORMAL: - switch ( fontType.color ) { - case FontColor::WHITE: - return GetICN( ICN::FONT, character - 0x20 ); - case FontColor::GRAY: - return GetICN( ICN::GRAY_FONT, character - 0x20 ); - case FontColor::YELLOW: - return GetICN( ICN::YELLOW_FONT, character - 0x20 ); - default: - // Did you add a new font color? Add the corresponding logic for it! - assert( 0 ); - break; - } + switch ( fontType.size ) { + case FontSize::SMALL: + switch ( fontType.color ) { + case FontColor::WHITE: + return GetICN( ICN::SMALFONT, character - 0x20 ); + case FontColor::GRAY: + return GetICN( ICN::GRAY_SMALL_FONT, character - 0x20 ); + case FontColor::YELLOW: + return GetICN( ICN::YELLOW_SMALLFONT, character - 0x20 ); + default: + // Did you add a new font color? Add the corresponding logic for it! + assert( 0 ); break; - case FontSize::LARGE: - switch ( fontType.color ) { - case FontColor::WHITE: - return GetICN( ICN::WHITE_LARGE_FONT, character - 0x20 ); - default: - // Did you add a new font color? Add the corresponding logic for it! - assert( 0 ); - break; - } + } + break; + case FontSize::NORMAL: + switch ( fontType.color ) { + case FontColor::WHITE: + return GetICN( ICN::FONT, character - 0x20 ); + case FontColor::GRAY: + return GetICN( ICN::GRAY_FONT, character - 0x20 ); + case FontColor::YELLOW: + return GetICN( ICN::YELLOW_FONT, character - 0x20 ); + default: + // Did you add a new font color? Add the corresponding logic for it! + assert( 0 ); break; - case FontSize::BUTTON_RELEASED: - switch ( fontType.color ) { - case FontColor::WHITE: - return GetICN( ICN::BUTTON_GOOD_FONT_RELEASED, character - 0x20 ); - case FontColor::GRAY: - return GetICN( ICN::BUTTON_EVIL_FONT_RELEASED, character - 0x20 ); - default: - // Did you add a new font color? Add the corresponding logic for it! - assert( 0 ); - break; - } + } + break; + case FontSize::LARGE: + switch ( fontType.color ) { + case FontColor::WHITE: + return GetICN( ICN::WHITE_LARGE_FONT, character - 0x20 ); + default: + // Did you add a new font color? Add the corresponding logic for it! + assert( 0 ); break; - case FontSize::BUTTON_PRESSED: - switch ( fontType.color ) { - case FontColor::WHITE: - return GetICN( ICN::BUTTON_GOOD_FONT_PRESSED, character - 0x20 ); - case FontColor::GRAY: - return GetICN( ICN::BUTTON_EVIL_FONT_PRESSED, character - 0x20 ); - default: - // Did you add a new font color? Add the corresponding logic for it! - assert( 0 ); - break; - } + } + break; + case FontSize::BUTTON_RELEASED: + switch ( fontType.color ) { + case FontColor::WHITE: + return GetICN( ICN::BUTTON_GOOD_FONT_RELEASED, character - 0x20 ); + case FontColor::GRAY: + return GetICN( ICN::BUTTON_EVIL_FONT_RELEASED, character - 0x20 ); + default: + // Did you add a new font color? Add the corresponding logic for it! + assert( 0 ); break; + } + break; + case FontSize::BUTTON_PRESSED: + switch ( fontType.color ) { + case FontColor::WHITE: + return GetICN( ICN::BUTTON_GOOD_FONT_PRESSED, character - 0x20 ); + case FontColor::GRAY: + return GetICN( ICN::BUTTON_EVIL_FONT_PRESSED, character - 0x20 ); default: - // Did you add a new font size? Add the corresponding logic for it! + // Did you add a new font color? Add the corresponding logic for it! assert( 0 ); break; } + break; + default: + // Did you add a new font size? Add the corresponding logic for it! + assert( 0 ); + break; + } - assert( 0 ); // Did you add a new font size? Please add implementation. + assert( 0 ); // Did you add a new font size? Please add implementation. - return errorImage; - } + return errorImage; + } - void updateLanguageDependentResources( const SupportedLanguage language, const bool loadOriginalAlphabet ) - { - if ( loadOriginalAlphabet || !isAlphabetSupported( language ) ) { - if ( !alphabetPreserver.isPreserved() ) { - // This can happen when we try to change a language without loading assets. - alphabetPreserver.preserve(); - } - else { - alphabetPreserver.restore(); - } + void updateLanguageDependentResources( const SupportedLanguage language, const bool loadOriginalAlphabet ) + { + if ( loadOriginalAlphabet || !isAlphabetSupported( language ) ) { + if ( !alphabetPreserver.isPreserved() ) { + // This can happen when we try to change a language without loading assets. + alphabetPreserver.preserve(); } else { - alphabetPreserver.preserve(); - // Restore original letters when changing language to avoid changes to them being carried over. alphabetPreserver.restore(); - generateAlphabet( language, _icnVsSprite ); } - generateButtonAlphabet( language, _icnVsSprite ); + } + else { + alphabetPreserver.preserve(); + // Restore original letters when changing language to avoid changes to them being carried over. + alphabetPreserver.restore(); + generateAlphabet( language, _icnVsSprite ); + } + generateButtonAlphabet( language, _icnVsSprite ); - // Clear language dependent resources. - for ( const int id : languageDependentIcnId ) { - _icnVsSprite[id].clear(); - } + // Clear language dependent resources. + for ( const int id : languageDependentIcnId ) { + _icnVsSprite[id].clear(); } } } diff --git a/src/fheroes2/battle/battle_board.cpp b/src/fheroes2/battle/battle_board.cpp index 7d92aa5b31c..09393d7d2f9 100644 --- a/src/fheroes2/battle/battle_board.cpp +++ b/src/fheroes2/battle/battle_board.cpp @@ -228,7 +228,7 @@ int Battle::Board::GetDirection( const int32_t index1, const int32_t index2 ) return CENTER; } - for ( direction_t dir = TOP_LEFT; dir < CENTER; ++dir ) { + for ( CellDirection dir = TOP_LEFT; dir < CENTER; ++dir ) { if ( isValidDirection( index1, dir ) && index2 == GetIndexDirection( index1, dir ) ) { return dir; } @@ -729,7 +729,7 @@ Battle::Indexes Battle::Board::GetAroundIndexes( const int32_t center ) Indexes result; result.reserve( 6 ); - for ( direction_t dir = TOP_LEFT; dir < CENTER; ++dir ) { + for ( CellDirection dir = TOP_LEFT; dir < CENTER; ++dir ) { if ( !isValidDirection( center, dir ) ) { continue; } diff --git a/src/fheroes2/battle/battle_board.h b/src/fheroes2/battle/battle_board.h index 8d361d5ca64..78aa24f11de 100644 --- a/src/fheroes2/battle/battle_board.h +++ b/src/fheroes2/battle/battle_board.h @@ -46,13 +46,13 @@ namespace Battle { class Unit; - inline direction_t & operator++( direction_t & d ) + inline CellDirection & operator++( CellDirection & d ) { - return d = ( CENTER == d ? TOP_LEFT : direction_t( d << 1 ) ); + return d = ( CENTER == d ? TOP_LEFT : CellDirection( d << 1 ) ); } - inline direction_t & operator--( direction_t & d ) + inline CellDirection & operator--( CellDirection & d ) { - return d = ( TOP_LEFT == d ? CENTER : direction_t( d >> 1 ) ); + return d = ( TOP_LEFT == d ? CENTER : CellDirection( d >> 1 ) ); } using Indexes = std::vector; diff --git a/src/fheroes2/battle/battle_cell.cpp b/src/fheroes2/battle/battle_cell.cpp index b8024b5c86a..d7375e4f87a 100644 --- a/src/fheroes2/battle/battle_cell.cpp +++ b/src/fheroes2/battle/battle_cell.cpp @@ -279,7 +279,7 @@ void Battle::Cell::SetArea( const fheroes2::Rect & area ) _coord[6] = { infl * _pos.x, infl * _pos.y + infl * _pos.height - infl * ( _pos.height - cellHeightVerSide ) / 2 }; } -Battle::direction_t Battle::Cell::GetTriangleDirection( const fheroes2::Point & dst ) const +Battle::CellDirection Battle::Cell::GetTriangleDirection( const fheroes2::Point & dst ) const { const fheroes2::Point pt( infl * dst.x, infl * dst.y ); diff --git a/src/fheroes2/battle/battle_cell.h b/src/fheroes2/battle/battle_cell.h index 8b8723aaca8..f817b08182e 100644 --- a/src/fheroes2/battle/battle_cell.h +++ b/src/fheroes2/battle/battle_cell.h @@ -38,7 +38,7 @@ namespace Battle { class Unit; - enum direction_t + enum CellDirection : int { UNKNOWN = 0x00, TOP_LEFT = 0x01, @@ -72,7 +72,7 @@ namespace Battle const Unit * GetUnit() const; Unit * GetUnit(); - direction_t GetTriangleDirection( const fheroes2::Point & dst ) const; + CellDirection GetTriangleDirection( const fheroes2::Point & dst ) const; bool isPositionIncludePoint( const fheroes2::Point & pt ) const; diff --git a/src/fheroes2/battle/battle_interface.cpp b/src/fheroes2/battle/battle_interface.cpp index 567c30ee711..66f1a970088 100644 --- a/src/fheroes2/battle/battle_interface.cpp +++ b/src/fheroes2/battle/battle_interface.cpp @@ -357,15 +357,337 @@ namespace return result; } + + int matchHeroType( const HeroBase * base ) + { + if ( base->isCaptain() ) { + return Battle::CAPTAIN; + } + + switch ( base->GetRace() ) { + case Race::KNGT: + return Battle::KNIGHT; + case Race::BARB: + return Battle::BARBARIAN; + case Race::SORC: + return Battle::SORCERESS; + case Race::WRLK: + return Battle::WARLOCK; + case Race::WZRD: + return Battle::WIZARD; + case Race::NECR: + return Battle::NECROMANCER; + default: + break; + } + + // Have you added a new race? Update the logic above! + assert( 0 ); + + return Battle::KNIGHT; + } + + const std::vector & getHeroAnimation( const HeroBase * hero, int animation ) + { + static std::vector staticAnim; + if ( staticAnim.empty() ) { + staticAnim.push_back( 1 ); + } + + if ( !hero || animation == Battle::OP_STATIC ) + return staticAnim; + + const int heroType = matchHeroType( hero ); + + if ( animation == Battle::OP_SORROW ) { + static const std::vector sorrowAnim{ 2, 3, 4, 5, 4, 5, 4, 3, 2 }; + + return ( heroType == Battle::CAPTAIN ) ? staticAnim : sorrowAnim; + } + + static std::vector heroTypeAnim[7][9]; + + if ( heroTypeAnim[heroType][animation].empty() ) { + const int sourceArray[7][9][9] = { + // JOY CAST_MASS CAST_UP CAST_DOWN IDLE + { { 6, 7, 8, 9, 8, 9, 8, 7, 6 }, { 10, 11 }, { 10 }, { 6, 12, 13 }, { 12, 6 }, { 2, 14 }, { 2 }, { 15, 16, 17 }, { 18, 19 } }, // KNIGHT + { { 6, 7, 8, 9, 9, 8, 7, 6 }, { 6, 10, 11 }, { 10, 6 }, { 6, 12, 13 }, { 12, 6 }, { 6, 14 }, { 6 }, { 15, 16, 17 }, { 18 } }, // BARBARIAN + { { 6, 7, 8, 7, 6 }, { 6, 7, 9 }, { 7, 6 }, { 6, 10, 11 }, { 10, 6 }, { 6, 12 }, { 6 }, { 13, 14, 15 }, { 16 } }, // SORCERESS + { { 6, 7, 8, 9, 10, 9, 8, 7, 6 }, { 6, 7, 11, 12 }, { 11, 6 }, { 6, 7, 13 }, { 6 }, { 6, 14 }, { 6 }, { 15, 16 }, { 6 } }, // WARLOCK + { { 6, 7, 8, 9, 8, 7, 6 }, { 6, 10, 11, 12, 13 }, { 12, 11, 10, 6 }, { 6, 14 }, { 6 }, { 6, 15 }, { 6 }, { 16, 17 }, { 18 } }, // WIZARD + { { 6, 7, 6, 7, 6, 7 }, + { 7, 8, 9, 10, 11 }, + { 10, 9, 7 }, + { 7, 12, 13, 14, 15 }, + { 7 }, + { 7, 12, 13, 14, 16 }, + { 7 }, + { 17 }, + { 18, 19 } }, // NECROMANCER + { { 1 }, { 2, 3, 4 }, { 3, 2 }, { 5, 6 }, { 5 }, { 5, 7 }, { 5 }, { 8, 9 }, { 10 } } // CAPTAIN + }; + + for ( const int frame : sourceArray[heroType][animation] ) { + if ( frame == 0 ) { + break; + } + + heroTypeAnim[heroType][animation].push_back( frame ); + } + } + + return heroTypeAnim[heroType][animation]; + } + + bool CursorAttack( uint32_t theme ) + { + switch ( theme ) { + case Cursor::WAR_ARROW: + case Cursor::WAR_BROKENARROW: + case Cursor::SWORD_TOPRIGHT: + case Cursor::SWORD_RIGHT: + case Cursor::SWORD_BOTTOMRIGHT: + case Cursor::SWORD_BOTTOMLEFT: + case Cursor::SWORD_LEFT: + case Cursor::SWORD_TOPLEFT: + case Cursor::SWORD_TOP: + case Cursor::SWORD_BOTTOM: + return true; + default: + break; + } + + return false; + } + + fheroes2::Image DrawHexagon( const uint8_t colorId ) + { + const int32_t r = 22; + const int32_t l = 10; + const int32_t w = CELLW; + const int32_t h = CELLH; + + fheroes2::Image sf( w + 1, h + 1 ); + sf.reset(); + + fheroes2::DrawLine( sf, { r - 1, 1 }, { 0, l + 1 }, colorId ); + fheroes2::SetPixel( sf, r, 1, colorId ); + fheroes2::DrawLine( sf, { r + 1, 1 }, { w, l + 1 }, colorId ); + + fheroes2::DrawLine( sf, { 0, l + 1 }, { 0, h - l }, colorId ); + fheroes2::DrawLine( sf, { w, l + 1 }, { w, h - l }, colorId ); + + fheroes2::DrawLine( sf, { r - 1, h }, { 0, h - l }, colorId ); + fheroes2::SetPixel( sf, r, h, colorId ); + fheroes2::DrawLine( sf, { r + 1, h }, { w, h - l }, colorId ); + + return sf; + } + + fheroes2::Image DrawHexagonShadow( const uint8_t alphaValue, const int32_t horizSpace ) + { + const int32_t l = 13; + const int32_t w = CELLW; + const int32_t h = CELLH; + + fheroes2::Image sf( w, h ); + sf.reset(); + fheroes2::Rect rt( horizSpace, l - 1, w + 1 - horizSpace * 2, 2 * l + 4 ); + for ( int32_t i = 0; i < w / 2; i += 2 ) { + for ( int32_t x = 0; x < rt.width; ++x ) { + for ( int32_t y = 0; y < rt.height; ++y ) { + fheroes2::SetTransformPixel( sf, rt.x + x, rt.y + y, alphaValue ); + } + } + --rt.y; + rt.height += 2; + rt.x += ( i == 0 ) ? 1 : 2; + rt.width -= ( i == 0 ) ? 2 : 4; + } + + return sf; + } + + fheroes2::Point GetTroopPosition( const Battle::Unit & unit, const fheroes2::Sprite & sprite ) + { + const fheroes2::Rect & rt = unit.GetRectPosition(); + + int32_t offsetX = 0; + if ( unit.isReflect() ) { + if ( unit.isWide() ) { + offsetX = rt.x + ( rt.width / 2 + rt.width / 4 ) - sprite.width() - sprite.x() + 1; + } + else { + offsetX = rt.x + ( rt.width / 2 ) - sprite.width() - sprite.x() + 1; + } + } + else { + if ( unit.isWide() ) { + offsetX = rt.x + ( rt.width / 4 ) + sprite.x(); + } + else { + offsetX = rt.x + ( rt.width / 2 ) + sprite.x(); + } + } + + const int32_t offsetY = rt.y + rt.height + sprite.y() + cellYOffset; + + return { offsetX, offsetY }; + } + + int GetIndexIndicator( const Battle::Unit & unit ) + { + if ( unit.Modes( Battle::IS_GOOD_MAGIC | Battle::SP_ANTIMAGIC ) ) { + if ( unit.Modes( Battle::IS_BAD_MAGIC ) ) { + // ICN::TEXTBAR index for yellow indicator background color. + return 13; + } + + // ICN::TEXTBAR index for green indicator background color. + return 12; + } + + if ( unit.Modes( Battle::IS_BAD_MAGIC ) ) { + // ICN::TEXTBAR index for red indicator background color. + return 14; + } + + // ICN::TEXTBAR index for purple indicator background color. + return 10; + } + + int GetCursorFromSpell( const int spell ) + { + switch ( spell ) { + case Spell::SLOW: + return Cursor::SP_SLOW; + case Spell::CURSE: + return Cursor::SP_CURSE; + case Spell::CURE: + return Cursor::SP_CURE; + case Spell::BLESS: + return Cursor::SP_BLESS; + case Spell::FIREBALL: + return Cursor::SP_FIREBALL; + case Spell::FIREBLAST: + return Cursor::SP_FIREBLAST; + case Spell::TELEPORT: + return Cursor::SP_TELEPORT; + case Spell::RESURRECT: + return Cursor::SP_RESURRECT; + case Spell::HASTE: + return Cursor::SP_HASTE; + case Spell::SHIELD: + return Cursor::SP_SHIELD; + case Spell::ARMAGEDDON: + return Cursor::SP_ARMAGEDDON; + case Spell::ANTIMAGIC: + return Cursor::SP_ANTIMAGIC; + case Spell::BERSERKER: + return Cursor::SP_BERSERKER; + case Spell::PARALYZE: + return Cursor::SP_PARALYZE; + case Spell::BLIND: + return Cursor::SP_BLIND; + + case Spell::LIGHTNINGBOLT: + return Cursor::SP_LIGHTNINGBOLT; + case Spell::CHAINLIGHTNING: + return Cursor::SP_CHAINLIGHTNING; + case Spell::ELEMENTALSTORM: + return Cursor::SP_ELEMENTALSTORM; + case Spell::RESURRECTTRUE: + return Cursor::SP_RESURRECTTRUE; + case Spell::DISPEL: + return Cursor::SP_DISPEL; + case Spell::HOLYWORD: + return Cursor::SP_HOLYWORD; + case Spell::HOLYSHOUT: + return Cursor::SP_HOLYSHOUT; + case Spell::METEORSHOWER: + return Cursor::SP_METEORSHOWER; + + case Spell::ANIMATEDEAD: + return Cursor::SP_ANIMATEDEAD; + case Spell::MIRRORIMAGE: + return Cursor::SP_MIRRORIMAGE; + case Spell::BLOODLUST: + return Cursor::SP_BLOODLUST; + case Spell::DEATHRIPPLE: + return Cursor::SP_DEATHRIPPLE; + case Spell::DEATHWAVE: + return Cursor::SP_DEATHWAVE; + case Spell::STEELSKIN: + return Cursor::SP_STEELSKIN; + case Spell::STONESKIN: + return Cursor::SP_STONESKIN; + case Spell::DRAGONSLAYER: + return Cursor::SP_DRAGONSLAYER; + case Spell::EARTHQUAKE: + return Cursor::SP_EARTHQUAKE; + case Spell::DISRUPTINGRAY: + return Cursor::SP_DISRUPTINGRAY; + case Spell::COLDRING: + return Cursor::SP_COLDRING; + case Spell::COLDRAY: + return Cursor::SP_COLDRAY; + case Spell::HYPNOTIZE: + return Cursor::SP_HYPNOTIZE; + case Spell::ARROW: + return Cursor::SP_ARROW; + + default: + break; + } + return Cursor::WAR_NONE; + } + + int GetSwordCursorDirection( const int direction ) + { + switch ( direction ) { + case Battle::BOTTOM_RIGHT: + return Cursor::SWORD_TOPLEFT; + case Battle::BOTTOM_LEFT: + return Cursor::SWORD_TOPRIGHT; + case Battle::RIGHT: + return Cursor::SWORD_LEFT; + case Battle::TOP_RIGHT: + return Cursor::SWORD_BOTTOMLEFT; + case Battle::TOP_LEFT: + return Cursor::SWORD_BOTTOMRIGHT; + case Battle::LEFT: + return Cursor::SWORD_RIGHT; + default: + break; + } + return 0; + } + + int GetDirectionFromCursorSword( const uint32_t sword ) + { + switch ( sword ) { + case Cursor::SWORD_TOPLEFT: + return Battle::BOTTOM_RIGHT; + case Cursor::SWORD_TOPRIGHT: + return Battle::BOTTOM_LEFT; + case Cursor::SWORD_LEFT: + return Battle::RIGHT; + case Cursor::SWORD_BOTTOMLEFT: + return Battle::TOP_RIGHT; + case Cursor::SWORD_BOTTOMRIGHT: + return Battle::TOP_LEFT; + case Cursor::SWORD_RIGHT: + return Battle::LEFT; + default: + break; + } + + return Battle::UNKNOWN; + } } namespace Battle { - int GetIndexIndicator( const Unit & unit ); - int GetSwordCursorDirection( const int direction ); - int GetDirectionFromCursorSword( const uint32_t sword ); - int GetCursorFromSpell( const int spell ); - class StatusListBox final : public ::Interface::ListBox { public: @@ -504,156 +826,6 @@ namespace Battle int32_t _scrollbarSliderAreaLength{ 0 }; bool _isLogOpened{ false }; }; - - int matchHeroType( const HeroBase * base ) - { - if ( base->isCaptain() ) { - return CAPTAIN; - } - - switch ( base->GetRace() ) { - case Race::KNGT: - return KNIGHT; - case Race::BARB: - return BARBARIAN; - case Race::SORC: - return SORCERESS; - case Race::WRLK: - return WARLOCK; - case Race::WZRD: - return WIZARD; - case Race::NECR: - return NECROMANCER; - default: - break; - } - - // Have you added a new race? Update the logic above! - assert( 0 ); - - return KNIGHT; - } - - const std::vector & getHeroAnimation( const HeroBase * hero, int animation ) - { - static std::vector staticAnim; - if ( staticAnim.empty() ) { - staticAnim.push_back( 1 ); - } - - if ( !hero || animation == OP_STATIC ) - return staticAnim; - - const int heroType = matchHeroType( hero ); - - if ( animation == OP_SORROW ) { - static const std::vector sorrowAnim{ 2, 3, 4, 5, 4, 5, 4, 3, 2 }; - - return ( heroType == CAPTAIN ) ? staticAnim : sorrowAnim; - } - - static std::vector heroTypeAnim[7][9]; - - if ( heroTypeAnim[heroType][animation].empty() ) { - const int sourceArray[7][9][9] = { - // JOY CAST_MASS CAST_UP CAST_DOWN IDLE - { { 6, 7, 8, 9, 8, 9, 8, 7, 6 }, { 10, 11 }, { 10 }, { 6, 12, 13 }, { 12, 6 }, { 2, 14 }, { 2 }, { 15, 16, 17 }, { 18, 19 } }, // KNIGHT - { { 6, 7, 8, 9, 9, 8, 7, 6 }, { 6, 10, 11 }, { 10, 6 }, { 6, 12, 13 }, { 12, 6 }, { 6, 14 }, { 6 }, { 15, 16, 17 }, { 18 } }, // BARBARIAN - { { 6, 7, 8, 7, 6 }, { 6, 7, 9 }, { 7, 6 }, { 6, 10, 11 }, { 10, 6 }, { 6, 12 }, { 6 }, { 13, 14, 15 }, { 16 } }, // SORCERESS - { { 6, 7, 8, 9, 10, 9, 8, 7, 6 }, { 6, 7, 11, 12 }, { 11, 6 }, { 6, 7, 13 }, { 6 }, { 6, 14 }, { 6 }, { 15, 16 }, { 6 } }, // WARLOCK - { { 6, 7, 8, 9, 8, 7, 6 }, { 6, 10, 11, 12, 13 }, { 12, 11, 10, 6 }, { 6, 14 }, { 6 }, { 6, 15 }, { 6 }, { 16, 17 }, { 18 } }, // WIZARD - { { 6, 7, 6, 7, 6, 7 }, - { 7, 8, 9, 10, 11 }, - { 10, 9, 7 }, - { 7, 12, 13, 14, 15 }, - { 7 }, - { 7, 12, 13, 14, 16 }, - { 7 }, - { 17 }, - { 18, 19 } }, // NECROMANCER - { { 1 }, { 2, 3, 4 }, { 3, 2 }, { 5, 6 }, { 5 }, { 5, 7 }, { 5 }, { 8, 9 }, { 10 } } // CAPTAIN - }; - - for ( const int frame : sourceArray[heroType][animation] ) { - if ( frame == 0 ) { - break; - } - - heroTypeAnim[heroType][animation].push_back( frame ); - } - } - - return heroTypeAnim[heroType][animation]; - } -} - -bool CursorAttack( uint32_t theme ) -{ - switch ( theme ) { - case Cursor::WAR_ARROW: - case Cursor::WAR_BROKENARROW: - case Cursor::SWORD_TOPRIGHT: - case Cursor::SWORD_RIGHT: - case Cursor::SWORD_BOTTOMRIGHT: - case Cursor::SWORD_BOTTOMLEFT: - case Cursor::SWORD_LEFT: - case Cursor::SWORD_TOPLEFT: - case Cursor::SWORD_TOP: - case Cursor::SWORD_BOTTOM: - return true; - default: - break; - } - - return false; -} - -fheroes2::Image DrawHexagon( const uint8_t colorId ) -{ - const int32_t r = 22; - const int32_t l = 10; - const int32_t w = CELLW; - const int32_t h = CELLH; - - fheroes2::Image sf( w + 1, h + 1 ); - sf.reset(); - - fheroes2::DrawLine( sf, { r - 1, 1 }, { 0, l + 1 }, colorId ); - fheroes2::SetPixel( sf, r, 1, colorId ); - fheroes2::DrawLine( sf, { r + 1, 1 }, { w, l + 1 }, colorId ); - - fheroes2::DrawLine( sf, { 0, l + 1 }, { 0, h - l }, colorId ); - fheroes2::DrawLine( sf, { w, l + 1 }, { w, h - l }, colorId ); - - fheroes2::DrawLine( sf, { r - 1, h }, { 0, h - l }, colorId ); - fheroes2::SetPixel( sf, r, h, colorId ); - fheroes2::DrawLine( sf, { r + 1, h }, { w, h - l }, colorId ); - - return sf; -} - -fheroes2::Image DrawHexagonShadow( const uint8_t alphaValue, const int32_t horizSpace ) -{ - const int32_t l = 13; - const int32_t w = CELLW; - const int32_t h = CELLH; - - fheroes2::Image sf( w, h ); - sf.reset(); - fheroes2::Rect rt( horizSpace, l - 1, w + 1 - horizSpace * 2, 2 * l + 4 ); - for ( int32_t i = 0; i < w / 2; i += 2 ) { - for ( int32_t x = 0; x < rt.width; ++x ) { - for ( int32_t y = 0; y < rt.height; ++y ) { - fheroes2::SetTransformPixel( sf, rt.x + x, rt.y + y, alphaValue ); - } - } - --rt.y; - rt.height += 2; - rt.x += ( i == 0 ) ? 1 : 2; - rt.width -= ( i == 0 ) ? 2 : 4; - } - - return sf; } bool Battle::TargetInfo::isFinishAnimFrame( const TargetInfo & info ) @@ -661,135 +833,6 @@ bool Battle::TargetInfo::isFinishAnimFrame( const TargetInfo & info ) return info.defender && info.defender->isFinishAnimFrame(); } -int Battle::GetCursorFromSpell( const int spell ) -{ - switch ( spell ) { - case Spell::SLOW: - return Cursor::SP_SLOW; - case Spell::CURSE: - return Cursor::SP_CURSE; - case Spell::CURE: - return Cursor::SP_CURE; - case Spell::BLESS: - return Cursor::SP_BLESS; - case Spell::FIREBALL: - return Cursor::SP_FIREBALL; - case Spell::FIREBLAST: - return Cursor::SP_FIREBLAST; - case Spell::TELEPORT: - return Cursor::SP_TELEPORT; - case Spell::RESURRECT: - return Cursor::SP_RESURRECT; - case Spell::HASTE: - return Cursor::SP_HASTE; - case Spell::SHIELD: - return Cursor::SP_SHIELD; - case Spell::ARMAGEDDON: - return Cursor::SP_ARMAGEDDON; - case Spell::ANTIMAGIC: - return Cursor::SP_ANTIMAGIC; - case Spell::BERSERKER: - return Cursor::SP_BERSERKER; - case Spell::PARALYZE: - return Cursor::SP_PARALYZE; - case Spell::BLIND: - return Cursor::SP_BLIND; - - case Spell::LIGHTNINGBOLT: - return Cursor::SP_LIGHTNINGBOLT; - case Spell::CHAINLIGHTNING: - return Cursor::SP_CHAINLIGHTNING; - case Spell::ELEMENTALSTORM: - return Cursor::SP_ELEMENTALSTORM; - case Spell::RESURRECTTRUE: - return Cursor::SP_RESURRECTTRUE; - case Spell::DISPEL: - return Cursor::SP_DISPEL; - case Spell::HOLYWORD: - return Cursor::SP_HOLYWORD; - case Spell::HOLYSHOUT: - return Cursor::SP_HOLYSHOUT; - case Spell::METEORSHOWER: - return Cursor::SP_METEORSHOWER; - - case Spell::ANIMATEDEAD: - return Cursor::SP_ANIMATEDEAD; - case Spell::MIRRORIMAGE: - return Cursor::SP_MIRRORIMAGE; - case Spell::BLOODLUST: - return Cursor::SP_BLOODLUST; - case Spell::DEATHRIPPLE: - return Cursor::SP_DEATHRIPPLE; - case Spell::DEATHWAVE: - return Cursor::SP_DEATHWAVE; - case Spell::STEELSKIN: - return Cursor::SP_STEELSKIN; - case Spell::STONESKIN: - return Cursor::SP_STONESKIN; - case Spell::DRAGONSLAYER: - return Cursor::SP_DRAGONSLAYER; - case Spell::EARTHQUAKE: - return Cursor::SP_EARTHQUAKE; - case Spell::DISRUPTINGRAY: - return Cursor::SP_DISRUPTINGRAY; - case Spell::COLDRING: - return Cursor::SP_COLDRING; - case Spell::COLDRAY: - return Cursor::SP_COLDRAY; - case Spell::HYPNOTIZE: - return Cursor::SP_HYPNOTIZE; - case Spell::ARROW: - return Cursor::SP_ARROW; - - default: - break; - } - return Cursor::WAR_NONE; -} - -int Battle::GetSwordCursorDirection( const int direction ) -{ - switch ( direction ) { - case BOTTOM_RIGHT: - return Cursor::SWORD_TOPLEFT; - case BOTTOM_LEFT: - return Cursor::SWORD_TOPRIGHT; - case RIGHT: - return Cursor::SWORD_LEFT; - case TOP_RIGHT: - return Cursor::SWORD_BOTTOMLEFT; - case TOP_LEFT: - return Cursor::SWORD_BOTTOMRIGHT; - case LEFT: - return Cursor::SWORD_RIGHT; - default: - break; - } - return 0; -} - -int Battle::GetDirectionFromCursorSword( const uint32_t sword ) -{ - switch ( sword ) { - case Cursor::SWORD_TOPLEFT: - return BOTTOM_RIGHT; - case Cursor::SWORD_TOPRIGHT: - return BOTTOM_LEFT; - case Cursor::SWORD_LEFT: - return RIGHT; - case Cursor::SWORD_BOTTOMLEFT: - return TOP_RIGHT; - case Cursor::SWORD_BOTTOMRIGHT: - return TOP_LEFT; - case Cursor::SWORD_RIGHT: - return LEFT; - default: - break; - } - - return UNKNOWN; -} - Battle::OpponentSprite::OpponentSprite( const fheroes2::Rect & area, const HeroBase * hero, const bool isReflect ) : _heroBase( hero ) , _currentAnim( getHeroAnimation( hero, OP_STATIC ) ) @@ -1781,33 +1824,6 @@ void Battle::Interface::RedrawOpponentsFlags() } } -fheroes2::Point GetTroopPosition( const Battle::Unit & unit, const fheroes2::Sprite & sprite ) -{ - const fheroes2::Rect & rt = unit.GetRectPosition(); - - int32_t offsetX = 0; - if ( unit.isReflect() ) { - if ( unit.isWide() ) { - offsetX = rt.x + ( rt.width / 2 + rt.width / 4 ) - sprite.width() - sprite.x() + 1; - } - else { - offsetX = rt.x + ( rt.width / 2 ) - sprite.width() - sprite.x() + 1; - } - } - else { - if ( unit.isWide() ) { - offsetX = rt.x + ( rt.width / 4 ) + sprite.x(); - } - else { - offsetX = rt.x + ( rt.width / 2 ) + sprite.x(); - } - } - - const int32_t offsetY = rt.y + rt.height + sprite.y() + cellYOffset; - - return { offsetX, offsetY }; -} - void Battle::Interface::RedrawTroopSprite( const Unit & unit ) { if ( b_current_sprite && _currentUnit == &unit ) { @@ -2590,8 +2606,8 @@ int Battle::Interface::GetBattleCursor( std::string & statusMsg ) const } else { // First search clockwise. - direction_t clockWiseDirection = static_cast( currentDirection ); - direction_t antiClockWiseDirection = static_cast( currentDirection ); + CellDirection clockWiseDirection = static_cast( currentDirection ); + CellDirection antiClockWiseDirection = static_cast( currentDirection ); while ( true ) { ++clockWiseDirection; @@ -3131,27 +3147,6 @@ void Battle::Interface::FadeArena( const bool clearMessageLog ) display.render(); } -int Battle::GetIndexIndicator( const Unit & unit ) -{ - if ( unit.Modes( IS_GOOD_MAGIC | SP_ANTIMAGIC ) ) { - if ( unit.Modes( IS_BAD_MAGIC ) ) { - // ICN::TEXTBAR index for yellow indicator background color. - return 13; - } - - // ICN::TEXTBAR index for green indicator background color. - return 12; - } - - if ( unit.Modes( IS_BAD_MAGIC ) ) { - // ICN::TEXTBAR index for red indicator background color. - return 14; - } - - // ICN::TEXTBAR index for purple indicator background color. - return 10; -} - void Battle::Interface::_openBattleSettingsDialog() { const Settings & conf = Settings::Get(); diff --git a/src/fheroes2/dialog/dialog_giftresources.cpp b/src/fheroes2/dialog/dialog_giftresources.cpp index 0eaa917dc5b..7bd040a9f98 100644 --- a/src/fheroes2/dialog/dialog_giftresources.cpp +++ b/src/fheroes2/dialog/dialog_giftresources.cpp @@ -53,177 +53,177 @@ namespace { const fheroes2::Size giftDialogSize( 320, 234 ); -} -int32_t GetIndexClickRects( const std::vector & rects ) -{ - LocalEvent & le = LocalEvent::Get(); + int32_t GetIndexClickRects( const std::vector & rects ) + { + LocalEvent & le = LocalEvent::Get(); - const fheroes2::Point & pos = le.getMouseCursorPos(); - const fheroes2::Point position( pos.x, pos.y ); + const fheroes2::Point & pos = le.getMouseCursorPos(); + const fheroes2::Point position( pos.x, pos.y ); + + for ( size_t i = 0; i < rects.size(); ++i ) { + if ( rects[i] & position ) { + if ( le.MouseClickLeft() ) { + return static_cast( i ); + } - for ( size_t i = 0; i < rects.size(); ++i ) { - if ( rects[i] & position ) { - if ( le.MouseClickLeft() ) - return static_cast( i ); - else return -1; + } } - } - return -1; -} + return -1; + } -struct SelectRecipientsColors -{ - static constexpr int recipientSpacing = 22; - const Colors colors; - int recipients; - std::vector positions; - - SelectRecipientsColors( const fheroes2::Point & pos, int senderColor ) - : colors( Settings::Get().GetPlayers().GetActualColors() & ~senderColor ) - , recipients( 0 ) + struct SelectRecipientsColors { - positions.reserve( colors.size() ); - const fheroes2::Display & display = fheroes2::Display::instance(); - const fheroes2::Rect box( ( display.width() - giftDialogSize.width ) / 2, ( display.height() - giftDialogSize.height ) / 2, giftDialogSize.width, - giftDialogSize.height ); - - const fheroes2::Sprite & sprite = fheroes2::AGG::GetICN( ICN::CELLWIN, 43 ); - const int32_t colorCount = static_cast( colors.size() ); // safe to cast as the number of players <= 8. - const int32_t playerContainerWidth = colorCount * sprite.width() + ( colorCount - 1 ) * recipientSpacing; - const int32_t startX = box.x + ( giftDialogSize.width - playerContainerWidth ) / 2; - for ( int32_t i = 0; i < colorCount; ++i ) { - const int32_t posX = startX + i * ( recipientSpacing + sprite.width() ); - positions.emplace_back( posX, pos.y, sprite.width(), sprite.height() ); + static constexpr int recipientSpacing = 22; + const Colors colors; + int recipients{ 0 }; + std::vector positions; + + SelectRecipientsColors( const fheroes2::Point & pos, int senderColor ) + : colors( Settings::Get().GetPlayers().GetActualColors() & ~senderColor ) + { + positions.reserve( colors.size() ); + const fheroes2::Display & display = fheroes2::Display::instance(); + const fheroes2::Rect box( ( display.width() - giftDialogSize.width ) / 2, ( display.height() - giftDialogSize.height ) / 2, giftDialogSize.width, + giftDialogSize.height ); + + const fheroes2::Sprite & sprite = fheroes2::AGG::GetICN( ICN::CELLWIN, 43 ); + const int32_t colorCount = static_cast( colors.size() ); // safe to cast as the number of players <= 8. + const int32_t playerContainerWidth = colorCount * sprite.width() + ( colorCount - 1 ) * recipientSpacing; + const int32_t startX = box.x + ( giftDialogSize.width - playerContainerWidth ) / 2; + for ( int32_t i = 0; i < colorCount; ++i ) { + const int32_t posX = startX + i * ( recipientSpacing + sprite.width() ); + positions.emplace_back( posX, pos.y, sprite.width(), sprite.height() ); + } } - } - int32_t GetIndexClick() const - { - return GetIndexClickRects( positions ); - } + int32_t GetIndexClick() const + { + return GetIndexClickRects( positions ); + } - void Redraw() const - { - fheroes2::Display & display = fheroes2::Display::instance(); + void Redraw() const + { + fheroes2::Display & display = fheroes2::Display::instance(); - for ( Colors::const_iterator it = colors.begin(); it != colors.end(); ++it ) { - const fheroes2::Rect & pos = positions[std::distance( colors.begin(), it )]; + for ( Colors::const_iterator it = colors.begin(); it != colors.end(); ++it ) { + const fheroes2::Rect & pos = positions[std::distance( colors.begin(), it )]; - fheroes2::Blit( fheroes2::AGG::GetICN( ICN::CELLWIN, 43 + Color::GetIndex( *it ) ), display, pos.x, pos.y ); - if ( recipients & *it ) - fheroes2::Blit( fheroes2::AGG::GetICN( ICN::CELLWIN, 2 ), display, pos.x + 2, pos.y + 2 ); + fheroes2::Blit( fheroes2::AGG::GetICN( ICN::CELLWIN, 43 + Color::GetIndex( *it ) ), display, pos.x, pos.y ); + if ( recipients & *it ) + fheroes2::Blit( fheroes2::AGG::GetICN( ICN::CELLWIN, 2 ), display, pos.x + 2, pos.y + 2 ); + } } - } - - bool QueueEventProcessing() - { - const int32_t index = GetIndexClick(); - if ( index >= 0 ) { - const int cols = colors[index]; + bool QueueEventProcessing() + { + const int32_t index = GetIndexClick(); - if ( recipients & cols ) - recipients &= ~cols; - else - recipients |= cols; + if ( index >= 0 ) { + const int cols = colors[index]; - return true; - } + if ( recipients & cols ) + recipients &= ~cols; + else + recipients |= cols; - return false; - } -}; + return true; + } -struct ResourceBar -{ - Funds & resource; - std::vector positions; + return false; + } + }; - ResourceBar( Funds & funds, int32_t posx, int32_t posy ) - : resource( funds ) + struct ResourceBar { - positions.reserve( 7 ); - const fheroes2::Sprite & sprite = fheroes2::AGG::GetICN( ICN::TRADPOST, 7 ); - - positions.emplace_back( posx, posy, sprite.width(), sprite.height() ); - positions.emplace_back( posx + 40, posy, sprite.width(), sprite.height() ); - positions.emplace_back( posx + 80, posy, sprite.width(), sprite.height() ); - positions.emplace_back( posx + 120, posy, sprite.width(), sprite.height() ); - positions.emplace_back( posx + 160, posy, sprite.width(), sprite.height() ); - positions.emplace_back( posx + 200, posy, sprite.width(), sprite.height() ); - positions.emplace_back( posx + 240, posy, sprite.width(), sprite.height() ); - } + Funds & resource; + std::vector positions; + + ResourceBar( Funds & funds, int32_t posx, int32_t posy ) + : resource( funds ) + { + positions.reserve( 7 ); + const fheroes2::Sprite & sprite = fheroes2::AGG::GetICN( ICN::TRADPOST, 7 ); + + positions.emplace_back( posx, posy, sprite.width(), sprite.height() ); + positions.emplace_back( posx + 40, posy, sprite.width(), sprite.height() ); + positions.emplace_back( posx + 80, posy, sprite.width(), sprite.height() ); + positions.emplace_back( posx + 120, posy, sprite.width(), sprite.height() ); + positions.emplace_back( posx + 160, posy, sprite.width(), sprite.height() ); + positions.emplace_back( posx + 200, posy, sprite.width(), sprite.height() ); + positions.emplace_back( posx + 240, posy, sprite.width(), sprite.height() ); + } - static void RedrawResource( int type, int32_t count, int32_t posx, int32_t posy ) - { - fheroes2::Display & display = fheroes2::Display::instance(); - fheroes2::Text text( std::to_string( count ), fheroes2::FontType::smallWhite() ); - const fheroes2::Sprite & sprite = fheroes2::AGG::GetICN( ICN::TRADPOST, 7 + Resource::getIconIcnIndex( type ) ); - fheroes2::Blit( sprite, display, posx, posy ); - text.draw( posx + ( sprite.width() - text.width() ) / 2, posy + sprite.height() - 10, display ); - } + static void RedrawResource( int type, int32_t count, int32_t posx, int32_t posy ) + { + fheroes2::Display & display = fheroes2::Display::instance(); + const fheroes2::Text text( std::to_string( count ), fheroes2::FontType::smallWhite() ); + const fheroes2::Sprite & sprite = fheroes2::AGG::GetICN( ICN::TRADPOST, 7 + Resource::getIconIcnIndex( type ) ); + fheroes2::Blit( sprite, display, posx, posy ); + text.draw( posx + ( sprite.width() - text.width() ) / 2, posy + sprite.height() - 10, display ); + } - void Redraw( const Funds * res = nullptr ) const - { - if ( !res ) - res = &resource; + void Redraw( const Funds * res = nullptr ) const + { + if ( !res ) + res = &resource; - for ( size_t i = 0; i < positions.size(); ++i ) { - const int rs = Resource::getResourceTypeFromIconIndex( static_cast( i ) ); - RedrawResource( rs, res->Get( rs ), positions[i].x, positions[i].y ); + for ( size_t i = 0; i < positions.size(); ++i ) { + const int rs = Resource::getResourceTypeFromIconIndex( static_cast( i ) ); + RedrawResource( rs, res->Get( rs ), positions[i].x, positions[i].y ); + } } - } - int32_t GetIndexClick() const - { - return GetIndexClickRects( positions ); - } + int32_t GetIndexClick() const + { + return GetIndexClickRects( positions ); + } - bool QueueEventProcessing( Funds & funds, uint32_t mul ) - { - const int32_t index = GetIndexClick(); + bool QueueEventProcessing( Funds & funds, uint32_t mul ) + { + const int32_t index = GetIndexClick(); - if ( index >= 0 ) { - int rs = Resource::getResourceTypeFromIconIndex( index ); - const int32_t step = ( rs == Resource::GOLD ) ? 100 : 1; + if ( index >= 0 ) { + const int rs = Resource::getResourceTypeFromIconIndex( index ); + const int32_t step = ( rs == Resource::GOLD ) ? 100 : 1; - int32_t cur = resource.Get( rs ); - int32_t sel = cur; - const int32_t max = mul > 1 ? ( funds.Get( rs ) + resource.Get( rs ) ) / static_cast( mul ) : funds.Get( rs ) + resource.Get( rs ); - if ( 0 == mul ) { - fheroes2::showStandardTextMessage( {}, _( "First select recipients!" ), Dialog::OK ); - } - else if ( 0 == max ) { - std::string msg = _( "You cannot select %{resource}!" ); - StringReplace( msg, "%{resource}", Resource::String( rs ) ); - fheroes2::showStandardTextMessage( {}, std::move( msg ), Dialog::OK ); - } - else { - std::string msg = _( "Select count %{resource}:" ); - StringReplace( msg, "%{resource}", Resource::String( rs ) ); + const int32_t cur = resource.Get( rs ); + int32_t sel = cur; + const int32_t max = mul > 1 ? ( funds.Get( rs ) + resource.Get( rs ) ) / static_cast( mul ) : funds.Get( rs ) + resource.Get( rs ); + if ( 0 == mul ) { + fheroes2::showStandardTextMessage( {}, _( "First select recipients!" ), Dialog::OK ); + } + else if ( 0 == max ) { + std::string msg = _( "You cannot select %{resource}!" ); + StringReplace( msg, "%{resource}", Resource::String( rs ) ); + fheroes2::showStandardTextMessage( {}, std::move( msg ), Dialog::OK ); + } + else { + std::string msg = _( "Select count %{resource}:" ); + StringReplace( msg, "%{resource}", Resource::String( rs ) ); - if ( Dialog::SelectCount( std::move( msg ), 0, max, sel, step ) && cur != sel ) { - int32_t * from = funds.GetPtr( rs ); - int32_t * to = resource.GetPtr( rs ); + if ( Dialog::SelectCount( std::move( msg ), 0, max, sel, step ) && cur != sel ) { + int32_t * from = funds.GetPtr( rs ); + int32_t * to = resource.GetPtr( rs ); - if ( from && to ) { - int32_t count = sel - cur; + if ( from && to ) { + const int32_t count = sel - cur; - *from -= mul > 1 ? count * static_cast( mul ) : count; - *to += count; + *from -= mul > 1 ? count * static_cast( mul ) : count; + *to += count; - return true; + return true; + } } } } - } - return false; - } -}; + return false; + } + }; +} void Dialog::MakeGiftResource( Kingdom & kingdom ) { diff --git a/src/fheroes2/dialog/dialog_levelup.cpp b/src/fheroes2/dialog/dialog_levelup.cpp index a35f26f7c59..59863129281 100644 --- a/src/fheroes2/dialog/dialog_levelup.cpp +++ b/src/fheroes2/dialog/dialog_levelup.cpp @@ -44,168 +44,171 @@ #include "ui_dialog.h" #include "ui_text.h" -void DialogPrimaryOnly( const std::string & name, const int primarySkillType ) +namespace { - std::string message = _( "%{name} has gained a level." ); - message.append( "\n\n" ); - message.append( _( "%{skill} +1" ) ); - StringReplace( message, "%{name}", name ); - StringReplace( message, "%{skill}", Skill::Primary::String( primarySkillType ) ); + void DialogPrimaryOnly( const std::string & name, const int primarySkillType ) + { + std::string message = _( "%{name} has gained a level." ); + message.append( "\n\n" ); + message.append( _( "%{skill} +1" ) ); + StringReplace( message, "%{name}", name ); + StringReplace( message, "%{skill}", Skill::Primary::String( primarySkillType ) ); - const fheroes2::PrimarySkillDialogElement primarySkillUI( primarySkillType, "+1" ); + const fheroes2::PrimarySkillDialogElement primarySkillUI( primarySkillType, "+1" ); - fheroes2::showStandardTextMessage( {}, std::move( message ), Dialog::OK, { &primarySkillUI } ); -} - -int DialogOneSecondary( const Heroes & hero, const std::string & name, const int primarySkillType, const Skill::Secondary & sec ) -{ - std::string message = _( "%{name} has gained a level." ); - message.append( "\n\n" ); - message.append( _( "%{skill} +1" ) ); - StringReplace( message, "%{name}", name ); - StringReplace( message, "%{skill}", Skill::Primary::String( primarySkillType ) ); - - message.append( "\n\n" ); - message.append( _( "You have learned %{skill}." ) ); - StringReplace( message, "%{skill}", sec.GetName() ); - - const fheroes2::SecondarySkillDialogElement secondarySkillUI( sec, hero ); - - fheroes2::showStandardTextMessage( {}, std::move( message ), Dialog::OK, { &secondarySkillUI } ); - - return sec.Skill(); -} - -int DialogSelectSecondary( const std::string & name, const int primarySkillType, const Skill::Secondary & sec1, const Skill::Secondary & sec2, Heroes & hero ) -{ - std::string header = _( "%{name} has gained a level.\n\n%{skill} +1" ); - StringReplace( header, "%{name}", name ); - StringReplace( header, "%{skill}", Skill::Primary::String( primarySkillType ) ); - - fheroes2::Display & display = fheroes2::Display::instance(); - - // setup cursor - const CursorRestorer cursorRestorer( true, Cursor::POINTER ); - - const fheroes2::Sprite & sprite_frame = fheroes2::AGG::GetICN( ICN::SECSKILL, 15 ); - const fheroes2::Sprite & sprite_skill1 = fheroes2::AGG::GetICN( ICN::SECSKILL, sec1.GetIndexSprite1() ); - const fheroes2::Sprite & sprite_skill2 = fheroes2::AGG::GetICN( ICN::SECSKILL, sec2.GetIndexSprite1() ); - - std::string message = _( "You may learn either:\n%{skill1}\nor\n%{skill2}" ); - StringReplace( message, "%{skill1}", sec1.GetName() ); - StringReplace( message, "%{skill2}", sec2.GetName() ); - - fheroes2::Text box1( std::move( header ), fheroes2::FontType::normalWhite() ); - fheroes2::Text box2( std::move( message ), fheroes2::FontType::normalWhite() ); - const int spacer = 10; - - Dialog::FrameBox box( box1.height( BOXAREA_WIDTH ) + spacer + box2.height( BOXAREA_WIDTH ) + 10 + sprite_frame.height(), true ); - - const bool isEvilInterface = Settings::Get().isEvilInterfaceEnabled(); - const int buttonLearnIcnID = isEvilInterface ? ICN::BUTTON_SMALL_LEARN_EVIL : ICN::BUTTON_SMALL_LEARN_GOOD; - - fheroes2::Point pt; - pt.x = box.GetArea().x + box.GetArea().width / 2 - fheroes2::AGG::GetICN( buttonLearnIcnID, 0 ).width() - 20; - pt.y = box.GetArea().y + box.GetArea().height - fheroes2::AGG::GetICN( buttonLearnIcnID, 0 ).height(); - fheroes2::Button button_learn1( pt.x, pt.y, buttonLearnIcnID, 0, 1 ); - - pt.x = box.GetArea().x + box.GetArea().width / 2 + 20; - pt.y = box.GetArea().y + box.GetArea().height - fheroes2::AGG::GetICN( buttonLearnIcnID, 0 ).height(); - fheroes2::Button button_learn2( pt.x, pt.y, buttonLearnIcnID, 0, 1 ); - - const fheroes2::Rect & boxArea = box.GetArea(); - fheroes2::Point pos( boxArea.x, boxArea.y ); - - box1.draw( pos.x, pos.y + 2, BOXAREA_WIDTH, display ); - pos.y += box1.height( BOXAREA_WIDTH ) + spacer; - - box2.draw( pos.x, pos.y + 2, BOXAREA_WIDTH, display ); - pos.y += box2.height( BOXAREA_WIDTH ) + spacer; - - // sprite1 - pos.x = box.GetArea().x + box.GetArea().width / 2 - sprite_frame.width() - 20; - fheroes2::Blit( sprite_frame, display, pos.x, pos.y ); - pos.x += 3; - fheroes2::Rect rect_image1( pos.x, pos.y, sprite_skill1.width(), sprite_skill1.height() ); - fheroes2::Blit( sprite_skill1, display, pos.x, pos.y + 3 ); - - fheroes2::Text text{ Skill::Secondary::String( sec1.Skill() ), fheroes2::FontType::smallWhite() }; - text.draw( pos.x + ( sprite_skill1.width() - text.width() ) / 2, pos.y + 7, display ); - text.set( Skill::Level::String( sec1.Level() ), fheroes2::FontType::smallWhite() ); - text.draw( pos.x + ( sprite_skill1.width() - text.width() ) / 2, pos.y + sprite_skill1.height() - 10, display ); - - // sprite2 - pos.x = box.GetArea().x + box.GetArea().width / 2 + 20; - fheroes2::Blit( sprite_frame, display, pos.x, pos.y ); - pos.x += 3; - - fheroes2::Rect rect_image2( pos.x, pos.y, sprite_skill2.width(), sprite_skill2.height() ); - fheroes2::Blit( sprite_skill2, display, pos.x, pos.y + 3 ); - // text - fheroes2::Text name_skill2( Skill::Secondary::String( sec2.Skill() ), fheroes2::FontType::smallWhite() ); - name_skill2.draw( pos.x + ( sprite_skill2.width() - name_skill2.width() ) / 2, pos.y + 7, display ); - fheroes2::Text name_level2( Skill::Level::String( sec2.Level() ), fheroes2::FontType::smallWhite() ); - name_level2.draw( pos.x + ( sprite_skill2.width() - name_level2.width() ) / 2, pos.y + sprite_skill2.height() - 10, display ); - - // hero button - pt.x = box.GetArea().x + box.GetArea().width / 2 - 18; - pt.y = box.GetArea().y + box.GetArea().height - 35; - - const int icnHeroes = isEvilInterface ? ICN::EVIL_ARMY_BUTTON : ICN::GOOD_ARMY_BUTTON; - fheroes2::ButtonSprite button_hero - = fheroes2::makeButtonWithBackground( pt.x, pt.y, fheroes2::AGG::GetICN( icnHeroes, 0 ), fheroes2::AGG::GetICN( icnHeroes, 1 ), display ); - - text.set( std::to_string( hero.GetSecondarySkills().Count() ) + "/" + std::to_string( HEROESMAXSKILL ), fheroes2::FontType::normalWhite() ); - text.draw( box.GetArea().x + ( box.GetArea().width - text.width() ) / 2, pt.y - 13, display ); + fheroes2::showStandardTextMessage( {}, std::move( message ), Dialog::OK, { &primarySkillUI } ); + } - button_learn1.draw(); - button_learn2.draw(); - button_hero.draw(); + int DialogOneSecondary( const Heroes & hero, const std::string & name, const int primarySkillType, const Skill::Secondary & sec ) + { + std::string message = _( "%{name} has gained a level." ); + message.append( "\n\n" ); + message.append( _( "%{skill} +1" ) ); + StringReplace( message, "%{name}", name ); + StringReplace( message, "%{skill}", Skill::Primary::String( primarySkillType ) ); - display.render(); - LocalEvent & le = LocalEvent::Get(); + message.append( "\n\n" ); + message.append( _( "You have learned %{skill}." ) ); + StringReplace( message, "%{skill}", sec.GetName() ); - // message loop - while ( le.HandleEvents() ) { - le.isMouseLeftButtonPressedInArea( button_learn1.area() ) ? button_learn1.drawOnPress() : button_learn1.drawOnRelease(); - le.isMouseLeftButtonPressedInArea( button_learn2.area() ) ? button_learn2.drawOnPress() : button_learn2.drawOnRelease(); - le.isMouseLeftButtonPressedInArea( button_hero.area() ) ? button_hero.drawOnPress() : button_hero.drawOnRelease(); + const fheroes2::SecondarySkillDialogElement secondarySkillUI( sec, hero ); - if ( le.MouseClickLeft( button_learn1.area() ) || Game::HotKeyPressEvent( Game::HotKeyEvent::DEFAULT_LEFT ) ) { - return sec1.Skill(); - } + fheroes2::showStandardTextMessage( {}, std::move( message ), Dialog::OK, { &secondarySkillUI } ); - if ( le.MouseClickLeft( button_learn2.area() ) || Game::HotKeyPressEvent( Game::HotKeyEvent::DEFAULT_RIGHT ) ) { - return sec2.Skill(); - } + return sec.Skill(); + } - if ( le.MouseClickLeft( button_hero.area() ) || Game::HotKeyPressEvent( Game::HotKeyEvent::DEFAULT_OKAY ) ) { - LocalEvent::Get().reset(); - hero.OpenDialog( false, true, true, true, true, false ); - display.render(); - } + int DialogSelectSecondary( const std::string & name, const int primarySkillType, const Skill::Secondary & sec1, const Skill::Secondary & sec2, Heroes & hero ) + { + std::string header = _( "%{name} has gained a level.\n\n%{skill} +1" ); + StringReplace( header, "%{name}", name ); + StringReplace( header, "%{skill}", Skill::Primary::String( primarySkillType ) ); - if ( le.MouseClickLeft( rect_image1 ) ) { - fheroes2::SecondarySkillDialogElement( sec1, hero ).showPopup( Dialog::OK ); - } - else if ( le.MouseClickLeft( rect_image2 ) ) { - fheroes2::SecondarySkillDialogElement( sec2, hero ).showPopup( Dialog::OK ); + fheroes2::Display & display = fheroes2::Display::instance(); + + // setup cursor + const CursorRestorer cursorRestorer( true, Cursor::POINTER ); + + const fheroes2::Sprite & sprite_frame = fheroes2::AGG::GetICN( ICN::SECSKILL, 15 ); + const fheroes2::Sprite & sprite_skill1 = fheroes2::AGG::GetICN( ICN::SECSKILL, sec1.GetIndexSprite1() ); + const fheroes2::Sprite & sprite_skill2 = fheroes2::AGG::GetICN( ICN::SECSKILL, sec2.GetIndexSprite1() ); + + std::string message = _( "You may learn either:\n%{skill1}\nor\n%{skill2}" ); + StringReplace( message, "%{skill1}", sec1.GetName() ); + StringReplace( message, "%{skill2}", sec2.GetName() ); + + const fheroes2::Text box1( std::move( header ), fheroes2::FontType::normalWhite() ); + const fheroes2::Text box2( std::move( message ), fheroes2::FontType::normalWhite() ); + const int spacer = 10; + + const Dialog::FrameBox box( box1.height( BOXAREA_WIDTH ) + spacer + box2.height( BOXAREA_WIDTH ) + 10 + sprite_frame.height(), true ); + + const bool isEvilInterface = Settings::Get().isEvilInterfaceEnabled(); + const int buttonLearnIcnID = isEvilInterface ? ICN::BUTTON_SMALL_LEARN_EVIL : ICN::BUTTON_SMALL_LEARN_GOOD; + + fheroes2::Point pt; + pt.x = box.GetArea().x + box.GetArea().width / 2 - fheroes2::AGG::GetICN( buttonLearnIcnID, 0 ).width() - 20; + pt.y = box.GetArea().y + box.GetArea().height - fheroes2::AGG::GetICN( buttonLearnIcnID, 0 ).height(); + fheroes2::Button button_learn1( pt.x, pt.y, buttonLearnIcnID, 0, 1 ); + + pt.x = box.GetArea().x + box.GetArea().width / 2 + 20; + pt.y = box.GetArea().y + box.GetArea().height - fheroes2::AGG::GetICN( buttonLearnIcnID, 0 ).height(); + fheroes2::Button button_learn2( pt.x, pt.y, buttonLearnIcnID, 0, 1 ); + + const fheroes2::Rect & boxArea = box.GetArea(); + fheroes2::Point pos( boxArea.x, boxArea.y ); + + box1.draw( pos.x, pos.y + 2, BOXAREA_WIDTH, display ); + pos.y += box1.height( BOXAREA_WIDTH ) + spacer; + + box2.draw( pos.x, pos.y + 2, BOXAREA_WIDTH, display ); + pos.y += box2.height( BOXAREA_WIDTH ) + spacer; + + // sprite1 + pos.x = box.GetArea().x + box.GetArea().width / 2 - sprite_frame.width() - 20; + fheroes2::Blit( sprite_frame, display, pos.x, pos.y ); + pos.x += 3; + const fheroes2::Rect rect_image1( pos.x, pos.y, sprite_skill1.width(), sprite_skill1.height() ); + fheroes2::Blit( sprite_skill1, display, pos.x, pos.y + 3 ); + + fheroes2::Text text{ Skill::Secondary::String( sec1.Skill() ), fheroes2::FontType::smallWhite() }; + text.draw( pos.x + ( sprite_skill1.width() - text.width() ) / 2, pos.y + 7, display ); + text.set( Skill::Level::String( sec1.Level() ), fheroes2::FontType::smallWhite() ); + text.draw( pos.x + ( sprite_skill1.width() - text.width() ) / 2, pos.y + sprite_skill1.height() - 10, display ); + + // sprite2 + pos.x = box.GetArea().x + box.GetArea().width / 2 + 20; + fheroes2::Blit( sprite_frame, display, pos.x, pos.y ); + pos.x += 3; + + const fheroes2::Rect rect_image2( pos.x, pos.y, sprite_skill2.width(), sprite_skill2.height() ); + fheroes2::Blit( sprite_skill2, display, pos.x, pos.y + 3 ); + // text + const fheroes2::Text name_skill2( Skill::Secondary::String( sec2.Skill() ), fheroes2::FontType::smallWhite() ); + name_skill2.draw( pos.x + ( sprite_skill2.width() - name_skill2.width() ) / 2, pos.y + 7, display ); + const fheroes2::Text name_level2( Skill::Level::String( sec2.Level() ), fheroes2::FontType::smallWhite() ); + name_level2.draw( pos.x + ( sprite_skill2.width() - name_level2.width() ) / 2, pos.y + sprite_skill2.height() - 10, display ); + + // hero button + pt.x = box.GetArea().x + box.GetArea().width / 2 - 18; + pt.y = box.GetArea().y + box.GetArea().height - 35; + + const int icnHeroes = isEvilInterface ? ICN::EVIL_ARMY_BUTTON : ICN::GOOD_ARMY_BUTTON; + fheroes2::ButtonSprite button_hero + = fheroes2::makeButtonWithBackground( pt.x, pt.y, fheroes2::AGG::GetICN( icnHeroes, 0 ), fheroes2::AGG::GetICN( icnHeroes, 1 ), display ); + + text.set( std::to_string( hero.GetSecondarySkills().Count() ) + "/" + std::to_string( HEROESMAXSKILL ), fheroes2::FontType::normalWhite() ); + text.draw( box.GetArea().x + ( box.GetArea().width - text.width() ) / 2, pt.y - 13, display ); + + button_learn1.draw(); + button_learn2.draw(); + button_hero.draw(); + + display.render(); + LocalEvent & le = LocalEvent::Get(); + + // message loop + while ( le.HandleEvents() ) { + le.isMouseLeftButtonPressedInArea( button_learn1.area() ) ? button_learn1.drawOnPress() : button_learn1.drawOnRelease(); + le.isMouseLeftButtonPressedInArea( button_learn2.area() ) ? button_learn2.drawOnPress() : button_learn2.drawOnRelease(); + le.isMouseLeftButtonPressedInArea( button_hero.area() ) ? button_hero.drawOnPress() : button_hero.drawOnRelease(); + + if ( le.MouseClickLeft( button_learn1.area() ) || Game::HotKeyPressEvent( Game::HotKeyEvent::DEFAULT_LEFT ) ) { + return sec1.Skill(); + } + + if ( le.MouseClickLeft( button_learn2.area() ) || Game::HotKeyPressEvent( Game::HotKeyEvent::DEFAULT_RIGHT ) ) { + return sec2.Skill(); + } + + if ( le.MouseClickLeft( button_hero.area() ) || Game::HotKeyPressEvent( Game::HotKeyEvent::DEFAULT_OKAY ) ) { + LocalEvent::Get().reset(); + hero.OpenDialog( false, true, true, true, true, false ); + display.render(); + } + + if ( le.MouseClickLeft( rect_image1 ) ) { + fheroes2::SecondarySkillDialogElement( sec1, hero ).showPopup( Dialog::OK ); + } + else if ( le.MouseClickLeft( rect_image2 ) ) { + fheroes2::SecondarySkillDialogElement( sec2, hero ).showPopup( Dialog::OK ); + } + + if ( le.isMouseRightButtonPressedInArea( rect_image1 ) ) { + fheroes2::SecondarySkillDialogElement( sec1, hero ).showPopup( Dialog::ZERO ); + display.render(); + } + else if ( le.isMouseRightButtonPressedInArea( rect_image2 ) ) { + fheroes2::SecondarySkillDialogElement( sec2, hero ).showPopup( Dialog::ZERO ); + display.render(); + } + else if ( le.isMouseRightButtonPressedInArea( button_hero.area() ) ) { + fheroes2::showStandardTextMessage( "", _( "View Hero" ), Dialog::ZERO ); + } } - if ( le.isMouseRightButtonPressedInArea( rect_image1 ) ) { - fheroes2::SecondarySkillDialogElement( sec1, hero ).showPopup( Dialog::ZERO ); - display.render(); - } - else if ( le.isMouseRightButtonPressedInArea( rect_image2 ) ) { - fheroes2::SecondarySkillDialogElement( sec2, hero ).showPopup( Dialog::ZERO ); - display.render(); - } - else if ( le.isMouseRightButtonPressedInArea( button_hero.area() ) ) { - fheroes2::showStandardTextMessage( "", _( "View Hero" ), Dialog::ZERO ); - } + return Skill::Secondary::UNKNOWN; } - - return Skill::Secondary::UNKNOWN; } int Dialog::LevelUpSelectSkill( const std::string & name, const int primarySkillType, const Skill::Secondary & sec1, const Skill::Secondary & sec2, Heroes & hero ) diff --git a/src/fheroes2/dialog/dialog_marketplace.cpp b/src/fheroes2/dialog/dialog_marketplace.cpp index 869ef01f69c..77cd1841060 100644 --- a/src/fheroes2/dialog/dialog_marketplace.cpp +++ b/src/fheroes2/dialog/dialog_marketplace.cpp @@ -52,216 +52,293 @@ #include "ui_tool.h" #include "world.h" -void RedrawFromResource( const fheroes2::Point &, const Funds & ); -void RedrawToResource( const fheroes2::Point & pt, bool showcost, const Kingdom & kingdom, bool tradingPost, int from_resource = 0 ); -std::string GetStringTradeCosts( const Kingdom & kingdom, int rs_from, int rs_to, bool tradingPost ); -uint32_t GetTradeCosts( const Kingdom & kingdom, int rs_from, int rs_to, bool tradingPost ); - -class TradeWindowGUI +namespace { -public: - explicit TradeWindowGUI( const fheroes2::Rect & rt ) - : pos_rt( rt ) - , back( fheroes2::Display::instance() ) - , tradpostIcnId( Settings::Get().isEvilInterfaceEnabled() ? ICN::TRADPOSE : ICN::TRADPOST ) - , textSell( fheroes2::Display::instance() ) - , textBuy( fheroes2::Display::instance() ) - , _singlePlayer( false ) + uint32_t GetTradeCosts( const Kingdom & kingdom, int rs_from, int rs_to, bool tradingPost ) { - Settings & conf = Settings::Get(); + return fheroes2::getTradeCost( ( tradingPost ? 3 : kingdom.GetCountMarketplace() ), rs_from, rs_to ); + } + + std::string GetStringTradeCosts( const Kingdom & kingdom, int rs_from, int rs_to, bool tradingPost ) + { + std::string res; + + if ( rs_from == rs_to ) { + res = _( "n/a" ); + } + else { + res = "1/"; + res.append( std::to_string( GetTradeCosts( kingdom, rs_from, rs_to, tradingPost ) ) ); + } + + return res; + } - back.update( rt.x - 5, rt.y + 15, rt.width + 10, 160 ); + class TradeWindowGUI + { + public: + explicit TradeWindowGUI( const fheroes2::Rect & rt ) + : pos_rt( rt ) + , back( fheroes2::Display::instance() ) + , tradpostIcnId( Settings::Get().isEvilInterfaceEnabled() ? ICN::TRADPOSE : ICN::TRADPOST ) + , textSell( fheroes2::Display::instance() ) + , textBuy( fheroes2::Display::instance() ) + { + Settings & conf = Settings::Get(); + + back.update( rt.x - 5, rt.y + 15, rt.width + 10, 160 ); + + const bool isEvilInterface = conf.isEvilInterfaceEnabled(); + + const int tradeButtonIcnID = isEvilInterface ? ICN::BUTTON_SMALL_TRADE_EVIL : ICN::BUTTON_SMALL_TRADE_GOOD; + const int giftButtonIcnID = isEvilInterface ? ICN::BTNGIFT_EVIL : ICN::BTNGIFT_GOOD; + + buttonGift.setICNInfo( giftButtonIcnID, 0, 1 ); + buttonTrade.setICNInfo( tradeButtonIcnID, 0, 1 ); + buttonLeft.setICNInfo( tradpostIcnId, 3, 4 ); + buttonRight.setICNInfo( tradpostIcnId, 5, 6 ); + + const fheroes2::Sprite & spriteGift = fheroes2::AGG::GetICN( giftButtonIcnID, 0 ); + const fheroes2::Sprite & spriteTrade = fheroes2::AGG::GetICN( tradeButtonIcnID, 0 ); + + buttonGift.setPosition( pos_rt.x - 68 + ( pos_rt.width - spriteGift.width() ) / 2, pos_rt.y + pos_rt.height - spriteGift.height() ); + buttonTrade.setPosition( pos_rt.x + ( pos_rt.width - spriteTrade.width() ) / 2, pos_rt.y + 150 ); + buttonLeft.setPosition( pos_rt.x + 11, pos_rt.y + 129 ); + buttonRight.setPosition( pos_rt.x + 220, pos_rt.y + 129 ); + _scrollbar.setImage( fheroes2::AGG::GetICN( tradpostIcnId, 2 ) ); + _scrollbar.setArea( { pos_rt.x + ( pos_rt.width - fheroes2::AGG::GetICN( tradpostIcnId, 1 ).width() ) / 2 + 22, pos_rt.y + 131, 187, 11 } ); + _scrollbar.hide(); + + const fheroes2::Text text( _( "Please inspect our fine wares. If you feel like offering a trade, click on the items you wish to trade with and for." ), + fheroes2::FontType::normalWhite() ); + text.draw( pos_rt.x, pos_rt.y + 32, pos_rt.width, fheroes2::Display::instance() ); + + const Players & players = conf.GetPlayers(); + + // If there are less than two active players in the game, then there is no one to give resources to + const bool isGiftDisabled = ( std::count_if( players.begin(), players.end(), + []( const Player * player ) { + if ( player == nullptr ) { + return false; + } + + return world.GetKingdom( player->GetColor() ).isPlay(); + } ) + < 2 ); + + isGiftDisabled ? buttonGift.disable() : buttonGift.enable(); + } - const bool isEvilInterface = conf.isEvilInterfaceEnabled(); + void RedrawInfoBuySell( uint32_t count_sell, uint32_t count_buy, uint32_t max_sell, uint32_t orig_buy ); + void ShowTradeArea( const Kingdom & kingdom, int resourceFrom, int resourceTo, uint32_t max_buy, uint32_t max_sell, uint32_t count_buy, uint32_t count_sell, + const bool fromTradingPost, const bool firstExchange ); + + fheroes2::Rect buttonMax; + fheroes2::Rect buttonMin; + fheroes2::Button buttonTrade; + fheroes2::Button buttonLeft; + fheroes2::Button buttonRight; + fheroes2::Button buttonGift; + fheroes2::Scrollbar _scrollbar; + + private: + fheroes2::Rect pos_rt; + fheroes2::ImageRestorer back; + const int tradpostIcnId; + + fheroes2::MovableText textSell; + fheroes2::MovableText textBuy; + }; + + void TradeWindowGUI::ShowTradeArea( const Kingdom & kingdom, int resourceFrom, int resourceTo, uint32_t max_buy, uint32_t max_sell, uint32_t count_buy, + uint32_t count_sell, const bool fromTradingPost, const bool firstExchange ) + { + fheroes2::Display & display = fheroes2::Display::instance(); + const bool disable = kingdom.GetFunds().Get( resourceFrom ) <= 0; - const int tradeButtonIcnID = isEvilInterface ? ICN::BUTTON_SMALL_TRADE_EVIL : ICN::BUTTON_SMALL_TRADE_GOOD; - const int giftButtonIcnID = isEvilInterface ? ICN::BTNGIFT_EVIL : ICN::BTNGIFT_GOOD; + if ( disable || resourceFrom == resourceTo || ( Resource::GOLD != resourceTo && 0 == max_buy ) ) { + _scrollbar.hide(); + back.restore(); + const fheroes2::Rect dst_rt( pos_rt.x, pos_rt.y + 30, pos_rt.width, 100 ); + std::string message = firstExchange && ( resourceFrom == resourceTo || 0 == max_buy ) + ? _( "Please inspect our fine wares. If you feel like offering a trade, click on the items you wish to trade with and for." ) + : _( "You have received quite a bargain. I expect to make no profit on the deal. Can I interest you in any of my other wares?" ); - buttonGift.setICNInfo( giftButtonIcnID, 0, 1 ); - buttonTrade.setICNInfo( tradeButtonIcnID, 0, 1 ); - buttonLeft.setICNInfo( tradpostIcnId, 3, 4 ); - buttonRight.setICNInfo( tradpostIcnId, 5, 6 ); + const fheroes2::Text displayMessage( std::move( message ), fheroes2::FontType::normalWhite() ); + displayMessage.draw( dst_rt.x, dst_rt.y + 2, dst_rt.width, display ); - const fheroes2::Sprite & spriteGift = fheroes2::AGG::GetICN( giftButtonIcnID, 0 ); - const fheroes2::Sprite & spriteTrade = fheroes2::AGG::GetICN( tradeButtonIcnID, 0 ); + buttonTrade.disable(); + buttonLeft.disable(); + buttonRight.disable(); - buttonGift.setPosition( pos_rt.x - 68 + ( pos_rt.width - spriteGift.width() ) / 2, pos_rt.y + pos_rt.height - spriteGift.height() ); - buttonTrade.setPosition( pos_rt.x + ( pos_rt.width - spriteTrade.width() ) / 2, pos_rt.y + 150 ); - buttonLeft.setPosition( pos_rt.x + 11, pos_rt.y + 129 ); - buttonRight.setPosition( pos_rt.x + 220, pos_rt.y + 129 ); - _scrollbar.setImage( fheroes2::AGG::GetICN( tradpostIcnId, 2 ) ); - _scrollbar.setArea( { pos_rt.x + ( pos_rt.width - fheroes2::AGG::GetICN( tradpostIcnId, 1 ).width() ) / 2 + 22, pos_rt.y + 131, 187, 11 } ); - _scrollbar.hide(); + buttonGift.draw(); - const fheroes2::Text text( _( "Please inspect our fine wares. If you feel like offering a trade, click on the items you wish to trade with and for." ), - fheroes2::FontType::normalWhite() ); - text.draw( pos_rt.x, pos_rt.y + 32, pos_rt.width, fheroes2::Display::instance() ); - - const Players & players = conf.GetPlayers(); - int playerCount = 0; - for ( const Player * player : players ) { - if ( player != nullptr ) { - const Kingdom & kingdom = world.GetKingdom( player->GetColor() ); - if ( kingdom.isPlay() ) - ++playerCount; - } + buttonMax = fheroes2::Rect(); + buttonMin = fheroes2::Rect(); } + else { + back.restore(); - _singlePlayer = playerCount == 1; - } + const fheroes2::Sprite & bar = fheroes2::AGG::GetICN( tradpostIcnId, 1 ); + fheroes2::Point dst_pt( pos_rt.x + ( pos_rt.width - bar.width() ) / 2 - 2, pos_rt.y + 128 ); + fheroes2::Blit( bar, display, dst_pt.x, dst_pt.y ); - void RedrawInfoBuySell( uint32_t count_sell, uint32_t count_buy, uint32_t max_sell, uint32_t orig_buy ); - void ShowTradeArea( const Kingdom & kingdom, int resourceFrom, int resourceTo, uint32_t max_buy, uint32_t max_sell, uint32_t count_buy, uint32_t count_sell, - const bool fromTradingPost, const bool firstExchange ); - - fheroes2::Rect buttonMax; - fheroes2::Rect buttonMin; - fheroes2::Button buttonTrade; - fheroes2::Button buttonLeft; - fheroes2::Button buttonRight; - fheroes2::Button buttonGift; - fheroes2::Scrollbar _scrollbar; - -private: - fheroes2::Rect pos_rt; - fheroes2::ImageRestorer back; - const int tradpostIcnId; - - fheroes2::MovableText textSell; - fheroes2::MovableText textBuy; - bool _singlePlayer; -}; - -void TradeWindowGUI::ShowTradeArea( const Kingdom & kingdom, int resourceFrom, int resourceTo, uint32_t max_buy, uint32_t max_sell, uint32_t count_buy, - uint32_t count_sell, const bool fromTradingPost, const bool firstExchange ) -{ - fheroes2::Display & display = fheroes2::Display::instance(); - bool disable = kingdom.GetFunds().Get( resourceFrom ) <= 0; + const uint32_t maximumValue = ( Resource::GOLD == resourceTo ) ? max_sell : max_buy; - if ( disable || resourceFrom == resourceTo || ( Resource::GOLD != resourceTo && 0 == max_buy ) ) { - _scrollbar.hide(); - back.restore(); - fheroes2::Rect dst_rt( pos_rt.x, pos_rt.y + 30, pos_rt.width, 100 ); - std::string message = firstExchange && ( resourceFrom == resourceTo || 0 == max_buy ) - ? _( "Please inspect our fine wares. If you feel like offering a trade, click on the items you wish to trade with and for." ) - : _( "You have received quite a bargain. I expect to make no profit on the deal. Can I interest you in any of my other wares?" ); + const fheroes2::Sprite & originalSlider = fheroes2::AGG::GetICN( tradpostIcnId, 2 ); + const fheroes2::Image scrollbarSlider = fheroes2::generateScrollbarSlider( originalSlider, true, 187, 1, static_cast( maximumValue + 1 ), + { 0, 0, 2, originalSlider.height() }, { 2, 0, 8, originalSlider.height() } ); + _scrollbar.setImage( scrollbarSlider ); - const fheroes2::Text displayMessage( std::move( message ), fheroes2::FontType::normalWhite() ); - displayMessage.draw( dst_rt.x, dst_rt.y + 2, dst_rt.width, display ); + _scrollbar.setRange( 0, maximumValue ); - if ( !_singlePlayer ) { - buttonGift.enable(); + const uint32_t exchange_rate = GetTradeCosts( kingdom, resourceFrom, resourceTo, fromTradingPost ); + std::string message; + if ( Resource::GOLD == resourceTo ) { + message = _( "I can offer you %{count} for 1 unit of %{resfrom}." ); + StringReplace( message, "%{count}", exchange_rate ); + StringReplace( message, "%{resfrom}", Resource::String( resourceFrom ) ); + } + else { + message = _( "I can offer you 1 unit of %{resto} for %{count} units of %{resfrom}." ); + StringReplace( message, "%{resto}", Resource::String( resourceTo ) ); + StringReplace( message, "%{resfrom}", Resource::String( resourceFrom ) ); + StringReplace( message, "%{count}", exchange_rate ); + } + + const fheroes2::Text displayMessage( std::move( message ), fheroes2::FontType::normalWhite() ); + displayMessage.draw( pos_rt.x, pos_rt.y + 32, pos_rt.width, display ); + + const fheroes2::Sprite & sprite_from = fheroes2::AGG::GetICN( ICN::RESOURCE, Resource::getIconIcnIndex( resourceFrom ) ); + dst_pt.x = pos_rt.x + ( pos_rt.width - sprite_from.width() + 1 ) / 2 - 70; + dst_pt.y = pos_rt.y + 115 - sprite_from.height(); + fheroes2::Blit( sprite_from, display, dst_pt.x, dst_pt.y ); + const fheroes2::Sprite & sprite_to = fheroes2::AGG::GetICN( ICN::RESOURCE, Resource::getIconIcnIndex( resourceTo ) ); + dst_pt.x = pos_rt.x + ( pos_rt.width - sprite_to.width() + 1 ) / 2 + 70; + dst_pt.y = pos_rt.y + 115 - sprite_to.height(); + fheroes2::Blit( sprite_to, display, dst_pt.x, dst_pt.y ); + const fheroes2::Sprite & sprite_fromto = fheroes2::AGG::GetICN( tradpostIcnId, 0 ); + dst_pt.x = pos_rt.x + ( pos_rt.width - sprite_fromto.width() ) / 2; + dst_pt.y = pos_rt.y + 90; + fheroes2::Blit( sprite_fromto, display, dst_pt.x, dst_pt.y ); + fheroes2::Text text( _( "Max" ), fheroes2::FontType::smallYellow() ); + dst_pt.x = pos_rt.x + ( pos_rt.width - text.width() ) / 2 - 5; + dst_pt.y = pos_rt.y + 80; + buttonMax = fheroes2::Rect( dst_pt.x, dst_pt.y, text.width(), text.height() ); + text.draw( dst_pt.x, dst_pt.y + 2, display ); + text.set( _( "Min" ), fheroes2::FontType::smallYellow() ); + dst_pt.x = pos_rt.x + ( pos_rt.width - text.width() ) / 2 - 5; + dst_pt.y = pos_rt.y + 103; + buttonMin = fheroes2::Rect( dst_pt.x, dst_pt.y, text.width(), text.height() ); + text.draw( dst_pt.x, dst_pt.y + 2, display ); + text.set( _( "Qty to trade" ), fheroes2::FontType::smallWhite() ); + dst_pt.x = pos_rt.x + ( pos_rt.width - text.width() ) / 2; + dst_pt.y = pos_rt.y + 115; + text.draw( dst_pt.x, dst_pt.y + 2, display ); + + buttonTrade.enable(); + buttonLeft.enable(); + buttonRight.enable(); + + buttonTrade.draw(); + buttonLeft.draw(); + buttonRight.draw(); + + RedrawInfoBuySell( count_sell, count_buy, max_sell, kingdom.GetFunds().Get( resourceTo ) ); + _scrollbar.show(); } - buttonTrade.disable(); - buttonLeft.disable(); - buttonRight.disable(); - buttonGift.draw(); - buttonMax = fheroes2::Rect(); - buttonMin = fheroes2::Rect(); - } - else { - back.restore(); - const fheroes2::Sprite & bar = fheroes2::AGG::GetICN( tradpostIcnId, 1 ); - fheroes2::Point dst_pt( pos_rt.x + ( pos_rt.width - bar.width() ) / 2 - 2, pos_rt.y + 128 ); - fheroes2::Blit( bar, display, dst_pt.x, dst_pt.y ); + display.render(); + } - const uint32_t maximumValue = ( Resource::GOLD == resourceTo ) ? max_sell : max_buy; + void TradeWindowGUI::RedrawInfoBuySell( uint32_t count_sell, uint32_t count_buy, uint32_t max_sell, uint32_t orig_buy ) + { + fheroes2::Point dst_pt; - const fheroes2::Sprite & originalSlider = fheroes2::AGG::GetICN( tradpostIcnId, 2 ); - const fheroes2::Image scrollbarSlider = fheroes2::generateScrollbarSlider( originalSlider, true, 187, 1, static_cast( maximumValue + 1 ), - { 0, 0, 2, originalSlider.height() }, { 2, 0, 8, originalSlider.height() } ); - _scrollbar.setImage( scrollbarSlider ); + _scrollbar.hide(); - _scrollbar.setRange( 0, maximumValue ); + auto text = std::make_unique( std::string( "-" ) + std::to_string( count_sell ) + " " + "(" + std::to_string( max_sell - count_sell ) + ")", + fheroes2::FontType::smallWhite() ); - const uint32_t exchange_rate = GetTradeCosts( kingdom, resourceFrom, resourceTo, fromTradingPost ); - std::string message; - if ( Resource::GOLD == resourceTo ) { - message = _( "I can offer you %{count} for 1 unit of %{resfrom}." ); - StringReplace( message, "%{count}", exchange_rate ); - StringReplace( message, "%{resfrom}", Resource::String( resourceFrom ) ); - } - else { - message = _( "I can offer you 1 unit of %{resto} for %{count} units of %{resfrom}." ); - StringReplace( message, "%{resto}", Resource::String( resourceTo ) ); - StringReplace( message, "%{resfrom}", Resource::String( resourceFrom ) ); - StringReplace( message, "%{count}", exchange_rate ); - } + int32_t textWidth = text->width(); - const fheroes2::Text displayMessage( std::move( message ), fheroes2::FontType::normalWhite() ); - displayMessage.draw( pos_rt.x, pos_rt.y + 32, pos_rt.width, display ); - - const fheroes2::Sprite & sprite_from = fheroes2::AGG::GetICN( ICN::RESOURCE, Resource::getIconIcnIndex( resourceFrom ) ); - dst_pt.x = pos_rt.x + ( pos_rt.width - sprite_from.width() + 1 ) / 2 - 70; - dst_pt.y = pos_rt.y + 115 - sprite_from.height(); - fheroes2::Blit( sprite_from, display, dst_pt.x, dst_pt.y ); - const fheroes2::Sprite & sprite_to = fheroes2::AGG::GetICN( ICN::RESOURCE, Resource::getIconIcnIndex( resourceTo ) ); - dst_pt.x = pos_rt.x + ( pos_rt.width - sprite_to.width() + 1 ) / 2 + 70; - dst_pt.y = pos_rt.y + 115 - sprite_to.height(); - fheroes2::Blit( sprite_to, display, dst_pt.x, dst_pt.y ); - const fheroes2::Sprite & sprite_fromto = fheroes2::AGG::GetICN( tradpostIcnId, 0 ); - dst_pt.x = pos_rt.x + ( pos_rt.width - sprite_fromto.width() ) / 2; - dst_pt.y = pos_rt.y + 90; - fheroes2::Blit( sprite_fromto, display, dst_pt.x, dst_pt.y ); - fheroes2::Text text( _( "Max" ), fheroes2::FontType::smallYellow() ); - dst_pt.x = pos_rt.x + ( pos_rt.width - text.width() ) / 2 - 5; - dst_pt.y = pos_rt.y + 80; - buttonMax = fheroes2::Rect( dst_pt.x, dst_pt.y, text.width(), text.height() ); - text.draw( dst_pt.x, dst_pt.y + 2, display ); - text.set( _( "Min" ), fheroes2::FontType::smallYellow() ); - dst_pt.x = pos_rt.x + ( pos_rt.width - text.width() ) / 2 - 5; - dst_pt.y = pos_rt.y + 103; - buttonMin = fheroes2::Rect( dst_pt.x, dst_pt.y, text.width(), text.height() ); - text.draw( dst_pt.x, dst_pt.y + 2, display ); - text.set( _( "Qty to trade" ), fheroes2::FontType::smallWhite() ); - dst_pt.x = pos_rt.x + ( pos_rt.width - text.width() ) / 2; - dst_pt.y = pos_rt.y + 115; - text.draw( dst_pt.x, dst_pt.y + 2, display ); - - buttonGift.enable(); - buttonTrade.enable(); - buttonLeft.enable(); - buttonRight.enable(); - - buttonTrade.draw(); - buttonLeft.draw(); - buttonRight.draw(); - - RedrawInfoBuySell( count_sell, count_buy, max_sell, kingdom.GetFunds().Get( resourceTo ) ); - _scrollbar.show(); - } + textSell.update( std::move( text ) ); + dst_pt.x = pos_rt.x + pos_rt.width / 2 - 70 - ( textWidth + 1 ) / 2; + dst_pt.y = pos_rt.y + 116; + textSell.draw( dst_pt.x, dst_pt.y ); - display.render(); -} + text = std::make_unique( std::string( "+" ) + std::to_string( count_buy ) + " " + "(" + std::to_string( orig_buy + count_buy ) + ")", + fheroes2::FontType::smallWhite() ); -void TradeWindowGUI::RedrawInfoBuySell( uint32_t count_sell, uint32_t count_buy, uint32_t max_sell, uint32_t orig_buy ) -{ - fheroes2::Point dst_pt; + textWidth = text->width(); - _scrollbar.hide(); + textBuy.update( std::move( text ) ); + dst_pt.x = pos_rt.x + pos_rt.width / 2 + 70 - ( textWidth + 1 ) / 2; + dst_pt.y = pos_rt.y + 116; + textBuy.draw( dst_pt.x, dst_pt.y ); - auto text = std::make_unique( std::string( "-" ) + std::to_string( count_sell ) + " " + "(" + std::to_string( max_sell - count_sell ) + ")", - fheroes2::FontType::smallWhite() ); + _scrollbar.show(); + } - int32_t textWidth = text->width(); + void RedrawResourceSprite( const fheroes2::Image & sf, int32_t px, int32_t py, int32_t value ) + { + fheroes2::Display & display = fheroes2::Display::instance(); + fheroes2::Blit( sf, display, px, py ); - textSell.update( std::move( text ) ); - dst_pt.x = pos_rt.x + pos_rt.width / 2 - 70 - ( textWidth + 1 ) / 2; - dst_pt.y = pos_rt.y + 116; - textSell.draw( dst_pt.x, dst_pt.y ); + const fheroes2::Text text( std::to_string( value ), fheroes2::FontType::smallWhite() ); + text.draw( px + ( 34 - text.width() ) / 2, py + 23, display ); + } - text = std::make_unique( std::string( "+" ) + std::to_string( count_buy ) + " " + "(" + std::to_string( orig_buy + count_buy ) + ")", - fheroes2::FontType::smallWhite() ); + void RedrawFromResource( const fheroes2::Point & pt, const Funds & rs ) + { + const int tradpost = Settings::Get().isEvilInterfaceEnabled() ? ICN::TRADPOSE : ICN::TRADPOST; + + // wood + RedrawResourceSprite( fheroes2::AGG::GetICN( tradpost, 7 ), pt.x, pt.y, rs.wood ); + // mercury + RedrawResourceSprite( fheroes2::AGG::GetICN( tradpost, 8 ), pt.x + 37, pt.y, rs.mercury ); + // ore + RedrawResourceSprite( fheroes2::AGG::GetICN( tradpost, 9 ), pt.x + 74, pt.y, rs.ore ); + // sulfur + RedrawResourceSprite( fheroes2::AGG::GetICN( tradpost, 10 ), pt.x, pt.y + 37, rs.sulfur ); + // crystal + RedrawResourceSprite( fheroes2::AGG::GetICN( tradpost, 11 ), pt.x + 37, pt.y + 37, rs.crystal ); + // gems + RedrawResourceSprite( fheroes2::AGG::GetICN( tradpost, 12 ), pt.x + 74, pt.y + 37, rs.gems ); + // gold + RedrawResourceSprite( fheroes2::AGG::GetICN( tradpost, 13 ), pt.x + 37, pt.y + 74, rs.gold ); + } - textWidth = text->width(); + void RedrawResourceSprite2( const fheroes2::Image & sf, int32_t px, int32_t py, bool show, const Kingdom & kingdom, int from, int res, bool trading ) + { + fheroes2::Display & display = fheroes2::Display::instance(); + fheroes2::Blit( sf, display, px, py ); - textBuy.update( std::move( text ) ); - dst_pt.x = pos_rt.x + pos_rt.width / 2 + 70 - ( textWidth + 1 ) / 2; - dst_pt.y = pos_rt.y + 116; - textBuy.draw( dst_pt.x, dst_pt.y ); + if ( show ) { + const fheroes2::Text text( GetStringTradeCosts( kingdom, from, res, trading ), fheroes2::FontType::smallWhite() ); + text.draw( px + ( 34 - text.width() ) / 2, py + 23, display ); + } + } - _scrollbar.show(); + void RedrawToResource( const fheroes2::Point & pt, bool showcost, const Kingdom & kingdom, bool tradingPost, int from_resource ) + { + const int tradpost = Settings::Get().isEvilInterfaceEnabled() ? ICN::TRADPOSE : ICN::TRADPOST; + + // wood + RedrawResourceSprite2( fheroes2::AGG::GetICN( tradpost, 7 ), pt.x, pt.y, showcost, kingdom, from_resource, Resource::WOOD, tradingPost ); + // mercury + RedrawResourceSprite2( fheroes2::AGG::GetICN( tradpost, 8 ), pt.x + 37, pt.y, showcost, kingdom, from_resource, Resource::MERCURY, tradingPost ); + // ore + RedrawResourceSprite2( fheroes2::AGG::GetICN( tradpost, 9 ), pt.x + 74, pt.y, showcost, kingdom, from_resource, Resource::ORE, tradingPost ); + // sulfur + RedrawResourceSprite2( fheroes2::AGG::GetICN( tradpost, 10 ), pt.x, pt.y + 37, showcost, kingdom, from_resource, Resource::SULFUR, tradingPost ); + // crystal + RedrawResourceSprite2( fheroes2::AGG::GetICN( tradpost, 11 ), pt.x + 37, pt.y + 37, showcost, kingdom, from_resource, Resource::CRYSTAL, tradingPost ); + // gems + RedrawResourceSprite2( fheroes2::AGG::GetICN( tradpost, 12 ), pt.x + 74, pt.y + 37, showcost, kingdom, from_resource, Resource::GEMS, tradingPost ); + // gold + RedrawResourceSprite2( fheroes2::AGG::GetICN( tradpost, 13 ), pt.x + 37, pt.y + 74, showcost, kingdom, from_resource, Resource::GOLD, tradingPost ); + } } void Dialog::Marketplace( Kingdom & kingdom, bool fromTradingPost ) @@ -325,7 +402,7 @@ void Dialog::Marketplace( Kingdom & kingdom, bool fromTradingPost ) dst_pt.x = pt2.x + ( 108 - text.width() ) / 2; dst_pt.y = pt2.y - 15; text.draw( dst_pt.x, dst_pt.y + 2, display ); - RedrawToResource( pt2, false, kingdom, fromTradingPost ); + RedrawToResource( pt2, false, kingdom, fromTradingPost, 0 ); uint32_t count_sell = 0; uint32_t count_buy = 0; @@ -355,9 +432,11 @@ void Dialog::Marketplace( Kingdom & kingdom, bool fromTradingPost ) dst_pt.y = pos_rt.y + pos_rt.height - spriteExit.height(); fheroes2::Button buttonExit( dst_pt.x, dst_pt.y, exitButtonIcnID, 0, 1 ); + buttonTrade.disable(); + buttonGift.draw(); buttonExit.draw(); - buttonTrade.disable(); + display.render(); LocalEvent & le = LocalEvent::Get(); @@ -548,83 +627,3 @@ void Dialog::Marketplace( Kingdom & kingdom, bool fromTradingPost ) } } } - -void RedrawResourceSprite( const fheroes2::Image & sf, int32_t px, int32_t py, int32_t value ) -{ - fheroes2::Display & display = fheroes2::Display::instance(); - fheroes2::Blit( sf, display, px, py ); - - const fheroes2::Text text( std::to_string( value ), fheroes2::FontType::smallWhite() ); - text.draw( px + ( 34 - text.width() ) / 2, py + 23, display ); -} - -void RedrawFromResource( const fheroes2::Point & pt, const Funds & rs ) -{ - const int tradpost = Settings::Get().isEvilInterfaceEnabled() ? ICN::TRADPOSE : ICN::TRADPOST; - - // wood - RedrawResourceSprite( fheroes2::AGG::GetICN( tradpost, 7 ), pt.x, pt.y, rs.wood ); - // mercury - RedrawResourceSprite( fheroes2::AGG::GetICN( tradpost, 8 ), pt.x + 37, pt.y, rs.mercury ); - // ore - RedrawResourceSprite( fheroes2::AGG::GetICN( tradpost, 9 ), pt.x + 74, pt.y, rs.ore ); - // sulfur - RedrawResourceSprite( fheroes2::AGG::GetICN( tradpost, 10 ), pt.x, pt.y + 37, rs.sulfur ); - // crystal - RedrawResourceSprite( fheroes2::AGG::GetICN( tradpost, 11 ), pt.x + 37, pt.y + 37, rs.crystal ); - // gems - RedrawResourceSprite( fheroes2::AGG::GetICN( tradpost, 12 ), pt.x + 74, pt.y + 37, rs.gems ); - // gold - RedrawResourceSprite( fheroes2::AGG::GetICN( tradpost, 13 ), pt.x + 37, pt.y + 74, rs.gold ); -} - -void RedrawResourceSprite2( const fheroes2::Image & sf, int32_t px, int32_t py, bool show, const Kingdom & kingdom, int from, int res, bool trading ) -{ - fheroes2::Display & display = fheroes2::Display::instance(); - fheroes2::Blit( sf, display, px, py ); - - if ( show ) { - const fheroes2::Text text( GetStringTradeCosts( kingdom, from, res, trading ), fheroes2::FontType::smallWhite() ); - text.draw( px + ( 34 - text.width() ) / 2, py + 23, display ); - } -} - -void RedrawToResource( const fheroes2::Point & pt, bool showcost, const Kingdom & kingdom, bool tradingPost, int from_resource ) -{ - const int tradpost = Settings::Get().isEvilInterfaceEnabled() ? ICN::TRADPOSE : ICN::TRADPOST; - - // wood - RedrawResourceSprite2( fheroes2::AGG::GetICN( tradpost, 7 ), pt.x, pt.y, showcost, kingdom, from_resource, Resource::WOOD, tradingPost ); - // mercury - RedrawResourceSprite2( fheroes2::AGG::GetICN( tradpost, 8 ), pt.x + 37, pt.y, showcost, kingdom, from_resource, Resource::MERCURY, tradingPost ); - // ore - RedrawResourceSprite2( fheroes2::AGG::GetICN( tradpost, 9 ), pt.x + 74, pt.y, showcost, kingdom, from_resource, Resource::ORE, tradingPost ); - // sulfur - RedrawResourceSprite2( fheroes2::AGG::GetICN( tradpost, 10 ), pt.x, pt.y + 37, showcost, kingdom, from_resource, Resource::SULFUR, tradingPost ); - // crystal - RedrawResourceSprite2( fheroes2::AGG::GetICN( tradpost, 11 ), pt.x + 37, pt.y + 37, showcost, kingdom, from_resource, Resource::CRYSTAL, tradingPost ); - // gems - RedrawResourceSprite2( fheroes2::AGG::GetICN( tradpost, 12 ), pt.x + 74, pt.y + 37, showcost, kingdom, from_resource, Resource::GEMS, tradingPost ); - // gold - RedrawResourceSprite2( fheroes2::AGG::GetICN( tradpost, 13 ), pt.x + 37, pt.y + 74, showcost, kingdom, from_resource, Resource::GOLD, tradingPost ); -} - -std::string GetStringTradeCosts( const Kingdom & kingdom, int rs_from, int rs_to, bool tradingPost ) -{ - std::string res; - - if ( rs_from == rs_to ) { - res = _( "n/a" ); - } - else { - res = "1/"; - res.append( std::to_string( GetTradeCosts( kingdom, rs_from, rs_to, tradingPost ) ) ); - } - - return res; -} - -uint32_t GetTradeCosts( const Kingdom & kingdom, int rs_from, int rs_to, bool tradingPost ) -{ - return fheroes2::getTradeCost( ( tradingPost ? 3 : kingdom.GetCountMarketplace() ), rs_from, rs_to ); -} diff --git a/src/fheroes2/dialog/dialog_recruit.cpp b/src/fheroes2/dialog/dialog_recruit.cpp index d6d65fdffdf..5840155464b 100644 --- a/src/fheroes2/dialog/dialog_recruit.cpp +++ b/src/fheroes2/dialog/dialog_recruit.cpp @@ -91,162 +91,162 @@ namespace fheroes2::Blit( recruitWindow, output, offset.x, offset.y ); fheroes2::addGradientShadow( recruitWindow, output, { offset.x, offset.y }, { -5, 5 } ); } -} -void RedrawCurrentInfo( const fheroes2::Point & pos, const uint32_t result, const Funds & paymentMonster, const Funds & paymentCosts, const Funds & funds, - const std::string & label, const fheroes2::Image & background = {} ) -{ - fheroes2::Display & display = fheroes2::Display::instance(); + void RedrawCurrentInfo( const fheroes2::Point & pos, const uint32_t result, const Funds & paymentMonster, const Funds & paymentCosts, const Funds & funds, + const std::string & label, const fheroes2::Image & background = {} ) + { + fheroes2::Display & display = fheroes2::Display::instance(); - fheroes2::Text text( std::to_string( result ), fheroes2::FontType::normalWhite() ); + fheroes2::Text text( std::to_string( result ), fheroes2::FontType::normalWhite() ); - // Restore the background of the text before rendering it. - fheroes2::Copy( background, 118, 147, display, pos.x + 118, pos.y + 147, 68, text.height() ); + // Restore the background of the text before rendering it. + fheroes2::Copy( background, 118, 147, display, pos.x + 118, pos.y + 147, 68, text.height() ); - text.draw( pos.x + 151 - text.width() / 2, pos.y + 147, display ); + text.draw( pos.x + 151 - text.width() / 2, pos.y + 147, display ); - std::string sgold = std::to_string( paymentCosts.gold ) + " " + "(" + std::to_string( funds.gold - paymentCosts.gold ) + ")"; - const int rsext = paymentMonster.GetValidItems() & ~Resource::GOLD; + std::string sgold = std::to_string( paymentCosts.gold ) + " " + "(" + std::to_string( funds.gold - paymentCosts.gold ) + ")"; + const int rsext = paymentMonster.GetValidItems() & ~Resource::GOLD; - text.set( std::move( sgold ), fheroes2::FontType::smallWhite() ); + text.set( std::move( sgold ), fheroes2::FontType::smallWhite() ); - // Restore the background of the text before rendering it. - fheroes2::Copy( background, 0, 214, display, pos.x, pos.y + 214, background.width(), text.height() ); + // Restore the background of the text before rendering it. + fheroes2::Copy( background, 0, 214, display, pos.x, pos.y + 214, background.width(), text.height() ); - if ( rsext ) { - text.draw( pos.x + 117 - text.width() / 2, pos.y + 214, display ); + if ( rsext ) { + text.draw( pos.x + 117 - text.width() / 2, pos.y + 214, display ); - text.set( std::to_string( paymentCosts.Get( rsext ) ) + " " + "(" + std::to_string( funds.Get( rsext ) - paymentCosts.Get( rsext ) ) + ")", - fheroes2::FontType::smallWhite() ); - text.draw( pos.x + 179 - text.width() / 2, pos.y + 214, display ); - } - else { - text.draw( pos.x + 144 - text.width() / 2, pos.y + 214, display ); - } + text.set( std::to_string( paymentCosts.Get( rsext ) ) + " " + "(" + std::to_string( funds.Get( rsext ) - paymentCosts.Get( rsext ) ) + ")", + fheroes2::FontType::smallWhite() ); + text.draw( pos.x + 179 - text.width() / 2, pos.y + 214, display ); + } + else { + text.draw( pos.x + 144 - text.width() / 2, pos.y + 214, display ); + } - // Restore the background of the text before rendering it or to leave it blank if there is no text. - fheroes2::Copy( background, 0, 166, display, pos.x, pos.y + 166, background.width(), text.height() ); + // Restore the background of the text before rendering it or to leave it blank if there is no text. + fheroes2::Copy( background, 0, 166, display, pos.x, pos.y + 166, background.width(), text.height() ); - if ( !label.empty() ) { - text.set( label, fheroes2::FontType::smallWhite() ); - text.draw( pos.x + 151 - text.width() / 2, pos.y + 166, display ); + if ( !label.empty() ) { + text.set( label, fheroes2::FontType::smallWhite() ); + text.draw( pos.x + 151 - text.width() / 2, pos.y + 166, display ); + } } -} -void RedrawResourceInfo( const int resourceIcnIndex, const fheroes2::Point & pos, const int32_t value, const int32_t px1, const int32_t py1, const int32_t px2, - const int32_t py2, const bool showTotalSum ) -{ - fheroes2::Display & display = fheroes2::Display::instance(); + void RedrawResourceInfo( const int resourceIcnIndex, const fheroes2::Point & pos, const int32_t value, const int32_t px1, const int32_t py1, const int32_t px2, + const int32_t py2, const bool showTotalSum ) + { + fheroes2::Display & display = fheroes2::Display::instance(); - // In recruit dialog (where the total sum is also shown) the resource info is shifted by 10 pixels to the right. - const int32_t offsetX = showTotalSum ? 10 : 0; + // In recruit dialog (where the total sum is also shown) the resource info is shifted by 10 pixels to the right. + const int32_t offsetX = showTotalSum ? 10 : 0; - const fheroes2::Sprite & sres = fheroes2::AGG::GetICN( ICN::RESOURCE, Resource::getIconIcnIndex( resourceIcnIndex ) ); - fheroes2::Blit( sres, fheroes2::Display::instance(), pos.x + px1 + offsetX, pos.y + py1 ); + const fheroes2::Sprite & sres = fheroes2::AGG::GetICN( ICN::RESOURCE, Resource::getIconIcnIndex( resourceIcnIndex ) ); + fheroes2::Blit( sres, fheroes2::Display::instance(), pos.x + px1 + offsetX, pos.y + py1 ); - const fheroes2::Text text( std::to_string( value ), fheroes2::FontType::smallWhite() ); - text.draw( pos.x + px2 - text.width() / 2 + offsetX, pos.y + py2, display ); + const fheroes2::Text text( std::to_string( value ), fheroes2::FontType::smallWhite() ); + text.draw( pos.x + px2 - text.width() / 2 + offsetX, pos.y + py2, display ); - if ( showTotalSum ) { - fheroes2::Blit( sres, display, pos.x + px1 - 45, pos.y + py1 + 125 ); + if ( showTotalSum ) { + fheroes2::Blit( sres, display, pos.x + px1 - 45, pos.y + py1 + 125 ); + } } -} -void RedrawMonsterInfo( const fheroes2::Rect & pos, const Monster & monster, const uint32_t available, const bool showTotalSum ) -{ - fheroes2::Display & display = fheroes2::Display::instance(); - const Funds paymentMonster = monster.GetCost(); - const bool needExtraResources = 2 == paymentMonster.GetValidItemsCount(); - - // Recruit monster text. - std::string str = _( "Recruit %{name}" ); - StringReplace( str, "%{name}", monster.GetMultiName() ); - fheroes2::Text text( std::move( str ), fheroes2::FontType::normalYellow() ); - fheroes2::Point dst_pt( pos.x + ( pos.width - text.width() ) / 2, pos.y + 11 ); - text.draw( dst_pt.x, dst_pt.y, display ); - - // Monster sprite. - const int monsterId = monster.GetID(); - const Bin_Info::MonsterAnimInfo & monsterInfo = Bin_Info::GetMonsterInfo( monsterId ); - assert( !monsterInfo.animationFrames[Bin_Info::MonsterAnimInfo::STATIC].empty() ); - - const fheroes2::Sprite & smon = fheroes2::AGG::GetICN( monster.GetMonsterSprite(), monsterInfo.animationFrames[Bin_Info::MonsterAnimInfo::STATIC][0] ); - dst_pt.x = pos.x + 64 + smon.x() - ( monster.isWide() ? 22 : 0 ); - const int32_t monsterExtraOffsetY = std::max( 0, smon.height() - 96 ); - dst_pt.y = pos.y + 119 - smon.height() + monsterExtraOffsetY; - - if ( monsterId == Monster::CHAMPION ) { - ++dst_pt.x; - } + void RedrawMonsterInfo( const fheroes2::Rect & pos, const Monster & monster, const uint32_t available, const bool showTotalSum ) + { + fheroes2::Display & display = fheroes2::Display::instance(); + const Funds paymentMonster = monster.GetCost(); + const bool needExtraResources = 2 == paymentMonster.GetValidItemsCount(); - fheroes2::Blit( smon, display, dst_pt.x, dst_pt.y ); + // Recruit monster text. + std::string str = _( "Recruit %{name}" ); + StringReplace( str, "%{name}", monster.GetMultiName() ); + fheroes2::Text text( std::move( str ), fheroes2::FontType::normalYellow() ); + fheroes2::Point dst_pt( pos.x + ( pos.width - text.width() ) / 2, pos.y + 11 ); + text.draw( dst_pt.x, dst_pt.y, display ); - // Resources needed to buy monster. - if ( needExtraResources ) { - RedrawResourceInfo( Resource::GOLD, pos.getPosition(), paymentMonster.gold, 134, 59, 167, 89, showTotalSum ); + // Monster sprite. + const int monsterId = monster.GetID(); + const Bin_Info::MonsterAnimInfo & monsterInfo = Bin_Info::GetMonsterInfo( monsterId ); + assert( !monsterInfo.animationFrames[Bin_Info::MonsterAnimInfo::STATIC].empty() ); - if ( paymentMonster.crystal > 0 ) { - RedrawResourceInfo( Resource::CRYSTAL, pos.getPosition(), paymentMonster.crystal, 206, 53, 224, 89, showTotalSum ); - } - else if ( paymentMonster.mercury > 0 ) { - RedrawResourceInfo( Resource::MERCURY, pos.getPosition(), paymentMonster.mercury, 209, 56, 224, 89, showTotalSum ); - } - else if ( paymentMonster.wood > 0 ) { - RedrawResourceInfo( Resource::WOOD, pos.getPosition(), paymentMonster.wood, 209, 56, 224, 89, showTotalSum ); + const fheroes2::Sprite & smon = fheroes2::AGG::GetICN( monster.GetMonsterSprite(), monsterInfo.animationFrames[Bin_Info::MonsterAnimInfo::STATIC][0] ); + dst_pt.x = pos.x + 64 + smon.x() - ( monster.isWide() ? 22 : 0 ); + const int32_t monsterExtraOffsetY = std::max( 0, smon.height() - 96 ); + dst_pt.y = pos.y + 119 - smon.height() + monsterExtraOffsetY; + + if ( monsterId == Monster::CHAMPION ) { + ++dst_pt.x; } - else if ( paymentMonster.ore > 0 ) { - RedrawResourceInfo( Resource::ORE, pos.getPosition(), paymentMonster.ore, 209, 56, 224, 89, showTotalSum ); + + fheroes2::Blit( smon, display, dst_pt.x, dst_pt.y ); + + // Resources needed to buy monster. + if ( needExtraResources ) { + RedrawResourceInfo( Resource::GOLD, pos.getPosition(), paymentMonster.gold, 134, 59, 167, 89, showTotalSum ); + + if ( paymentMonster.crystal > 0 ) { + RedrawResourceInfo( Resource::CRYSTAL, pos.getPosition(), paymentMonster.crystal, 206, 53, 224, 89, showTotalSum ); + } + else if ( paymentMonster.mercury > 0 ) { + RedrawResourceInfo( Resource::MERCURY, pos.getPosition(), paymentMonster.mercury, 209, 56, 224, 89, showTotalSum ); + } + else if ( paymentMonster.wood > 0 ) { + RedrawResourceInfo( Resource::WOOD, pos.getPosition(), paymentMonster.wood, 209, 56, 224, 89, showTotalSum ); + } + else if ( paymentMonster.ore > 0 ) { + RedrawResourceInfo( Resource::ORE, pos.getPosition(), paymentMonster.ore, 209, 56, 224, 89, showTotalSum ); + } + else if ( paymentMonster.sulfur > 0 ) { + RedrawResourceInfo( Resource::SULFUR, pos.getPosition(), paymentMonster.sulfur, 209, 59, 224, 89, showTotalSum ); + } + else if ( paymentMonster.gems > 0 ) { + RedrawResourceInfo( Resource::GEMS, pos.getPosition(), paymentMonster.gems, 209, 59, 224, 89, showTotalSum ); + } } - else if ( paymentMonster.sulfur > 0 ) { - RedrawResourceInfo( Resource::SULFUR, pos.getPosition(), paymentMonster.sulfur, 209, 59, 224, 89, showTotalSum ); + else { + // Only gold is needed. + RedrawResourceInfo( Resource::GOLD, pos.getPosition(), paymentMonster.gold, 159, 59, 189, 89, showTotalSum ); } - else if ( paymentMonster.gems > 0 ) { - RedrawResourceInfo( Resource::GEMS, pos.getPosition(), paymentMonster.gems, 209, 59, 224, 89, showTotalSum ); + + str = _( "Available: %{count}" ); + StringReplace( str, "%{count}", available ); + text.set( std::move( str ), fheroes2::FontType::smallWhite() ); + text.draw( pos.x + 64 - text.width() / 2, pos.y + 120 + std::max( monsterExtraOffsetY, 2 ), display ); + + if ( showTotalSum ) { + text.set( _( "Number to buy:" ), fheroes2::FontType::smallWhite() ); + text.draw( pos.x + 107 - text.width(), pos.y + 149, display ); } } - else { - // Only gold is needed. - RedrawResourceInfo( Resource::GOLD, pos.getPosition(), paymentMonster.gold, 159, 59, 189, 89, showTotalSum ); - } - str = _( "Available: %{count}" ); - StringReplace( str, "%{count}", available ); - text.set( std::move( str ), fheroes2::FontType::smallWhite() ); - text.draw( pos.x + 64 - text.width() / 2, pos.y + 120 + std::max( monsterExtraOffsetY, 2 ), display ); + const char * SwitchMaxMinButtons( fheroes2::ButtonBase & btnMax, fheroes2::ButtonBase & btnMin, bool max ) + { + if ( btnMax.isEnabled() || btnMin.isEnabled() ) { + if ( max ) { + btnMax.disable(); + btnMin.enable(); - if ( showTotalSum ) { - text.set( _( "Number to buy:" ), fheroes2::FontType::smallWhite() ); - text.draw( pos.x + 107 - text.width(), pos.y + 149, display ); - } -} + return _( "Max" ); + } -const char * SwitchMaxMinButtons( fheroes2::ButtonBase & btnMax, fheroes2::ButtonBase & btnMin, bool max ) -{ - if ( btnMax.isEnabled() || btnMin.isEnabled() ) { - if ( max ) { - btnMax.disable(); - btnMin.enable(); + btnMin.disable(); + btnMax.enable(); - return _( "Max" ); + return _( "Min" ); } - btnMin.disable(); - btnMax.enable(); - - return _( "Min" ); + return ""; } - return ""; -} + uint32_t CalculateMax( const Monster & monster, const Kingdom & kingdom, const uint32_t available ) + { + uint32_t max = 0; + while ( kingdom.AllowPayment( monster.GetCost() * ( max + 1 ) ) && ( max + 1 ) <= available ) { + ++max; + } -uint32_t CalculateMax( const Monster & monster, const Kingdom & kingdom, const uint32_t available ) -{ - uint32_t max = 0; - while ( kingdom.AllowPayment( monster.GetCost() * ( max + 1 ) ) && ( max + 1 ) <= available ) { - ++max; + return max; } - - return max; } Troop Dialog::RecruitMonster( const Monster & monster0, const uint32_t available, const bool allowDowngradedMonster, const int32_t windowOffsetY ) diff --git a/src/fheroes2/dialog/dialog_selectitems.cpp b/src/fheroes2/dialog/dialog_selectitems.cpp index ba479946eab..0b94475350c 100644 --- a/src/fheroes2/dialog/dialog_selectitems.cpp +++ b/src/fheroes2/dialog/dialog_selectitems.cpp @@ -72,200 +72,198 @@ namespace // NOTICE: This calculation should be consistent with the number of KINGDOM_TOWNS objects. return townRace * 2 + ( isCastle ? 0 : 1 ); } -} -class SelectEnumMonster final : public Dialog::ItemSelectionWindow -{ -public: - explicit SelectEnumMonster( const fheroes2::Size & rt, std::string title ) - : Dialog::ItemSelectionWindow( rt, std::move( title ) ) + class SelectEnumMonster final : public Dialog::ItemSelectionWindow { - SetAreaMaxItems( rtAreaItems.height / _offsetY ); - } - - using Dialog::ItemSelectionWindow::ActionListPressRight; + public: + explicit SelectEnumMonster( const fheroes2::Size & rt, std::string title ) + : Dialog::ItemSelectionWindow( rt, std::move( title ) ) + { + SetAreaMaxItems( rtAreaItems.height / _offsetY ); + } - void RedrawItem( const int & index, int32_t dstx, int32_t dsty, bool current ) override - { - const Monster mons( index ); - const fheroes2::Sprite & monsterSprite = fheroes2::AGG::GetICN( ICN::MONS32, mons.GetSpriteIndex() ); + using Dialog::ItemSelectionWindow::ActionListPressRight; - renderItem( monsterSprite, mons.GetName(), { dstx, dsty }, 45 / 2, 50, _offsetY / 2, current ); - } + void RedrawItem( const int & index, int32_t dstx, int32_t dsty, bool current ) override + { + const Monster mons( index ); + const fheroes2::Sprite & monsterSprite = fheroes2::AGG::GetICN( ICN::MONS32, mons.GetSpriteIndex() ); - void ActionListPressRight( int & index ) override - { - const Monster monster( index ); - if ( !monster.isValid() ) { - fheroes2::showStandardTextMessage( monster.GetName(), "", Dialog::ZERO ); - return; + renderItem( monsterSprite, mons.GetName(), { dstx, dsty }, 45 / 2, 50, _offsetY / 2, current ); } - Dialog::ArmyInfo( Troop( monster, 0 ), Dialog::ZERO ); - } + void ActionListPressRight( int & index ) override + { + const Monster monster( index ); + if ( !monster.isValid() ) { + fheroes2::showStandardTextMessage( monster.GetName(), "", Dialog::ZERO ); + return; + } -private: - static const int32_t _offsetY{ 43 }; -}; + Dialog::ArmyInfo( Troop( monster, 0 ), Dialog::ZERO ); + } -class SelectEnumHeroes final : public Dialog::ItemSelectionWindow -{ -public: - explicit SelectEnumHeroes( const fheroes2::Size & rt, std::string title ) - : Dialog::ItemSelectionWindow( rt, std::move( title ) ) + private: + static const int32_t _offsetY{ 43 }; + }; + + class SelectEnumHeroes final : public Dialog::ItemSelectionWindow { - SetAreaMaxItems( rtAreaItems.height / _offsetY ); - } + public: + explicit SelectEnumHeroes( const fheroes2::Size & rt, std::string title ) + : Dialog::ItemSelectionWindow( rt, std::move( title ) ) + { + SetAreaMaxItems( rtAreaItems.height / _offsetY ); + } - using Dialog::ItemSelectionWindow::ActionListPressRight; + using Dialog::ItemSelectionWindow::ActionListPressRight; - void RedrawItem( const int & index, int32_t dstx, int32_t dsty, bool current ) override - { - const fheroes2::Sprite & port = Heroes::GetPortrait( index, PORT_SMALL ); + void RedrawItem( const int & index, int32_t dstx, int32_t dsty, bool current ) override + { + const fheroes2::Sprite & port = Heroes::GetPortrait( index, PORT_SMALL ); - renderItem( port, Heroes::GetName( index ), { dstx, dsty }, 45 / 2, 50, _offsetY / 2, current ); - } + renderItem( port, Heroes::GetName( index ), { dstx, dsty }, 45 / 2, 50, _offsetY / 2, current ); + } - void ActionListPressRight( int & index ) override - { - Dialog::QuickInfo( *world.GetHeroes( index ) ); - } + void ActionListPressRight( int & index ) override + { + Dialog::QuickInfo( *world.GetHeroes( index ) ); + } -private: - static const int32_t _offsetY{ 35 }; -}; + private: + static const int32_t _offsetY{ 35 }; + }; -class SelectEnumArtifact final : public Dialog::ItemSelectionWindow -{ -public: - explicit SelectEnumArtifact( const fheroes2::Size & rt, std::string title ) - : Dialog::ItemSelectionWindow( rt, std::move( title ) ) + class SelectEnumArtifact final : public Dialog::ItemSelectionWindow { - SetAreaMaxItems( rtAreaItems.height / _offsetY ); - } + public: + explicit SelectEnumArtifact( const fheroes2::Size & rt, std::string title ) + : Dialog::ItemSelectionWindow( rt, std::move( title ) ) + { + SetAreaMaxItems( rtAreaItems.height / _offsetY ); + } - using Dialog::ItemSelectionWindow::ActionListPressRight; + using Dialog::ItemSelectionWindow::ActionListPressRight; - void RedrawItem( const int & index, int32_t dstx, int32_t dsty, bool current ) override - { - const Artifact art( index ); - const fheroes2::Sprite & artifactSprite = fheroes2::AGG::GetICN( ICN::ARTFX, art.IndexSprite32() ); + void RedrawItem( const int & index, int32_t dstx, int32_t dsty, bool current ) override + { + const Artifact art( index ); + const fheroes2::Sprite & artifactSprite = fheroes2::AGG::GetICN( ICN::ARTFX, art.IndexSprite32() ); - renderItem( artifactSprite, art.GetName(), { dstx, dsty }, 45 / 2, 50, _offsetY / 2, current ); - } + renderItem( artifactSprite, art.GetName(), { dstx, dsty }, 45 / 2, 50, _offsetY / 2, current ); + } - void ActionListPressRight( int & index ) override - { - fheroes2::ArtifactDialogElement( Artifact( index ) ).showPopup( Dialog::ZERO ); - } + void ActionListPressRight( int & index ) override + { + fheroes2::ArtifactDialogElement( Artifact( index ) ).showPopup( Dialog::ZERO ); + } -private: - static const int32_t _offsetY{ 42 }; -}; + private: + static const int32_t _offsetY{ 42 }; + }; -class SelectEnumSpell final : public Dialog::ItemSelectionWindow -{ -public: - explicit SelectEnumSpell( const fheroes2::Size & rt, std::string title ) - : Dialog::ItemSelectionWindow( rt, std::move( title ) ) + class SelectEnumSpell final : public Dialog::ItemSelectionWindow { - SetAreaMaxItems( rtAreaItems.height / _offsetY ); - } + public: + explicit SelectEnumSpell( const fheroes2::Size & rt, std::string title ) + : Dialog::ItemSelectionWindow( rt, std::move( title ) ) + { + SetAreaMaxItems( rtAreaItems.height / _offsetY ); + } - using Dialog::ItemSelectionWindow::ActionListPressRight; + using Dialog::ItemSelectionWindow::ActionListPressRight; - void RedrawItem( const int & index, int32_t dstx, int32_t dsty, bool current ) override - { - const Spell spell( index ); - const fheroes2::Sprite & spellSprite = fheroes2::AGG::GetICN( ICN::SPELLS, spell.IndexSprite() ); + void RedrawItem( const int & index, int32_t dstx, int32_t dsty, bool current ) override + { + const Spell spell( index ); + const fheroes2::Sprite & spellSprite = fheroes2::AGG::GetICN( ICN::SPELLS, spell.IndexSprite() ); - renderItem( spellSprite, spell.GetName(), { dstx, dsty }, 75 / 2, 80, _offsetY / 2, current ); - } + renderItem( spellSprite, spell.GetName(), { dstx, dsty }, 75 / 2, 80, _offsetY / 2, current ); + } - void ActionListPressRight( int & index ) override - { - fheroes2::SpellDialogElement( Spell( index ), nullptr ).showPopup( Dialog::ZERO ); - } + void ActionListPressRight( int & index ) override + { + fheroes2::SpellDialogElement( Spell( index ), nullptr ).showPopup( Dialog::ZERO ); + } -private: - static const int32_t _offsetY{ 55 }; -}; + private: + static const int32_t _offsetY{ 55 }; + }; -class SelectEnumSecSkill final : public Dialog::ItemSelectionWindow -{ -public: - static int getSkillFromListIndex( int index ) + class SelectEnumSecSkill final : public Dialog::ItemSelectionWindow { - return 1 + index / 3; - } + public: + static int getSkillFromListIndex( int index ) + { + return 1 + index / 3; + } - static int getLevelFromListIndex( int index ) - { - return 1 + ( index % 3 ); - } + static int getLevelFromListIndex( int index ) + { + return 1 + ( index % 3 ); + } - explicit SelectEnumSecSkill( const fheroes2::Size & rt, std::string title ) - : Dialog::ItemSelectionWindow( rt, std::move( title ) ) - { - SetAreaMaxItems( rtAreaItems.height / _offsetY ); - } + explicit SelectEnumSecSkill( const fheroes2::Size & rt, std::string title ) + : Dialog::ItemSelectionWindow( rt, std::move( title ) ) + { + SetAreaMaxItems( rtAreaItems.height / _offsetY ); + } - using Dialog::ItemSelectionWindow::ActionListPressRight; + using Dialog::ItemSelectionWindow::ActionListPressRight; - void RedrawItem( const int & index, int32_t dstx, int32_t dsty, bool current ) override - { - const Skill::Secondary skill( getSkillFromListIndex( index ), getLevelFromListIndex( index ) ); - const fheroes2::Sprite & skillSprite = fheroes2::AGG::GetICN( ICN::MINISS, skill.GetIndexSprite2() ); + void RedrawItem( const int & index, int32_t dstx, int32_t dsty, bool current ) override + { + const Skill::Secondary skill( getSkillFromListIndex( index ), getLevelFromListIndex( index ) ); + const fheroes2::Sprite & skillSprite = fheroes2::AGG::GetICN( ICN::MINISS, skill.GetIndexSprite2() ); - renderItem( skillSprite, skill.GetName(), { dstx, dsty }, 45 / 2, 50, _offsetY / 2, current ); - } + renderItem( skillSprite, skill.GetName(), { dstx, dsty }, 45 / 2, 50, _offsetY / 2, current ); + } - void ActionListPressRight( int & index ) override - { - fheroes2::SecondarySkillDialogElement( Skill::Secondary( getSkillFromListIndex( index ), getLevelFromListIndex( index ) ), Heroes() ).showPopup( Dialog::ZERO ); - } + void ActionListPressRight( int & index ) override + { + fheroes2::SecondarySkillDialogElement( Skill::Secondary( getSkillFromListIndex( index ), getLevelFromListIndex( index ) ), Heroes() ) + .showPopup( Dialog::ZERO ); + } -private: - static const int32_t _offsetY{ 42 }; -}; + private: + static const int32_t _offsetY{ 42 }; + }; -class SelectKingdomCastle final : public Dialog::ItemSelectionWindow -{ -public: - explicit SelectKingdomCastle( const fheroes2::Size & rt, std::string title, std::string description ) - : Dialog::ItemSelectionWindow( rt, std::move( title ), std::move( description ) ) - , _townFrameIcnId( Settings::Get().isEvilInterfaceEnabled() ? ICN::LOCATORE : ICN::LOCATORS ) + class SelectKingdomCastle final : public Dialog::ItemSelectionWindow { - SetAreaMaxItems( rtAreaItems.height / itemsOffsetY ); - } + public: + explicit SelectKingdomCastle( const fheroes2::Size & rt, std::string title, std::string description ) + : Dialog::ItemSelectionWindow( rt, std::move( title ), std::move( description ) ) + , _townFrameIcnId( Settings::Get().isEvilInterfaceEnabled() ? ICN::LOCATORE : ICN::LOCATORS ) + { + SetAreaMaxItems( rtAreaItems.height / itemsOffsetY ); + } - using Dialog::ItemSelectionWindow::ActionListPressRight; + using Dialog::ItemSelectionWindow::ActionListPressRight; - void RedrawItem( const int & index, int32_t dstx, int32_t dsty, bool current ) override - { - const Castle * castle = world.getCastleEntrance( Maps::GetPoint( index ) ); + void RedrawItem( const int & index, int32_t dstx, int32_t dsty, bool current ) override + { + const Castle * castle = world.getCastleEntrance( Maps::GetPoint( index ) ); - assert( castle != nullptr ); + assert( castle != nullptr ); - fheroes2::Sprite castleIcon( fheroes2::AGG::GetICN( _townFrameIcnId, 23 ) ); - fheroes2::drawCastleIcon( *castle, castleIcon, { 4, 4 } ); + fheroes2::Sprite castleIcon( fheroes2::AGG::GetICN( _townFrameIcnId, 23 ) ); + fheroes2::drawCastleIcon( *castle, castleIcon, { 4, 4 } ); - renderItem( castleIcon, castle->GetName(), { dstx, dsty }, 35, 75, itemsOffsetY / 2, current ); - } + renderItem( castleIcon, castle->GetName(), { dstx, dsty }, 35, 75, itemsOffsetY / 2, current ); + } - void ActionListPressRight( int & index ) override - { - Dialog::QuickInfoWithIndicationOnRadar( *world.getCastleEntrance( Maps::GetPoint( index ) ), getBackgroundArea() ); - } + void ActionListPressRight( int & index ) override + { + Dialog::QuickInfoWithIndicationOnRadar( *world.getCastleEntrance( Maps::GetPoint( index ) ), getBackgroundArea() ); + } - static const int32_t itemsOffsetY{ 35 }; + static const int32_t itemsOffsetY{ 35 }; -private: - const int _townFrameIcnId; -}; + private: + const int _townFrameIcnId; + }; -namespace -{ // This is a base class for items used in the Editor and they rely on Maps::ObjectInfo structures. class ObjectTypeSelection : public Dialog::ItemSelectionWindow { diff --git a/src/fheroes2/dialog/dialog_thievesguild.cpp b/src/fheroes2/dialog/dialog_thievesguild.cpp index 3f60ba91b7f..e43eff2f08e 100644 --- a/src/fheroes2/dialog/dialog_thievesguild.cpp +++ b/src/fheroes2/dialog/dialog_thievesguild.cpp @@ -59,180 +59,183 @@ #include "ui_window.h" #include "world.h" -struct ValueColors : std::pair +namespace { - ValueColors( int value, int color ) - : std::pair( value, color ) - {} + struct ValueColors : std::pair + { + ValueColors( int value, int color ) + : std::pair( value, color ) + {} + + static bool SortValueGreat( const ValueColors & v1, const ValueColors & v2 ) + { + return v1.first > v2.first; + } + }; - static bool SortValueGreat( const ValueColors & v1, const ValueColors & v2 ) + void UpdateValuesColors( std::vector & v, int value, int color ) { - return v1.first > v2.first; + const auto it = std::find_if( v.begin(), v.end(), [value]( const ValueColors & vc ) { return vc.first == value; } ); + + if ( it == v.end() ) { + v.emplace_back( value, color ); + } + else { + ( *it ).second |= color; + } } -}; -void UpdateValuesColors( std::vector & v, int value, int color ) -{ - const auto it = std::find_if( v.begin(), v.end(), [value]( const ValueColors & vc ) { return vc.first == value; } ); + void getInfo( std::vector & v, const Colors & colors, const std::function & getValue ) + { + // 'getValue' should contain a callable function. + assert( getValue ); - if ( it == v.end() ) { - v.emplace_back( value, color ); - } - else { - ( *it ).second |= color; - } -} + v.clear(); -void getInfo( std::vector & v, const Colors & colors, const std::function & getValue ) -{ - // 'getValue' should contain a callable function. - assert( getValue ); + for ( const int color : colors ) { + UpdateValuesColors( v, getValue( color ), color ); + } - v.clear(); + std::sort( v.begin(), v.end(), ValueColors::SortValueGreat ); + } - for ( const int color : colors ) { - UpdateValuesColors( v, getValue( color ), color ); + int getWoodOreValue( const int color ) + { + const Funds & funds = world.GetKingdom( color ).GetFunds(); + return funds.Get( Resource::WOOD ) + funds.Get( Resource::ORE ); } - std::sort( v.begin(), v.end(), ValueColors::SortValueGreat ); -} + int getGemsCrSlfMerValue( const int color ) + { + const Funds & funds = world.GetKingdom( color ).GetFunds(); + return funds.Get( Resource::GEMS ) + funds.Get( Resource::CRYSTAL ) + funds.Get( Resource::SULFUR ) + funds.Get( Resource::MERCURY ); + } -int getWoodOreValue( const int color ) -{ - const Funds & funds = world.GetKingdom( color ).GetFunds(); - return funds.Get( Resource::WOOD ) + funds.Get( Resource::ORE ); -} + void drawFlags( const std::vector & v, const fheroes2::Point & pos, const int32_t step, const size_t count, fheroes2::Image & output ) + { + const size_t flagGroups = std::min( count, v.size() ); -int getGemsCrSlfMerValue( const int color ) -{ - const Funds & funds = world.GetKingdom( color ).GetFunds(); - return funds.Get( Resource::GEMS ) + funds.Get( Resource::CRYSTAL ) + funds.Get( Resource::SULFUR ) + funds.Get( Resource::MERCURY ); -} + if ( flagGroups == 0 ) { + return; + } -void drawFlags( const std::vector & v, const fheroes2::Point & pos, const int32_t step, const size_t count, fheroes2::Image & output ) -{ - const size_t flagGroups = std::min( count, v.size() ); + const int32_t sptireWidth = fheroes2::AGG::GetICN( ICN::TOWNWIND, 22 ).width(); + const int32_t offsetY = pos.y - 4; - if ( flagGroups == 0 ) { - return; - } + for ( size_t i = 0; i < flagGroups; ++i ) { + const Colors colors( v[i].second ); + + int32_t offsetX = pos.x + static_cast( i ) * step - ( static_cast( colors.size() ) * sptireWidth ) / 2 + 3; - const int32_t sptireWidth = fheroes2::AGG::GetICN( ICN::TOWNWIND, 22 ).width(); - const int32_t offsetY = pos.y - 4; + for ( const int color : colors ) { + const fheroes2::Sprite & flag = fheroes2::AGG::GetICN( ICN::TOWNWIND, 22 + Color::GetIndex( color ) ); + fheroes2::Blit( flag, output, offsetX, offsetY ); + offsetX += sptireWidth; + } + } + } - for ( size_t i = 0; i < flagGroups; ++i ) { - const Colors colors( v[i].second ); + void drawHeroStats( const Heroes * hero, const int32_t offsetX, int32_t offsetY, fheroes2::Image & output ) + { + fheroes2::Text text( _( "Att." ), fheroes2::FontType::smallWhite() ); + text.draw( offsetX, offsetY, output ); + text.set( std::to_string( hero->GetAttack() ), fheroes2::FontType::smallWhite() ); + text.draw( offsetX + 50 - text.width(), offsetY, output ); + + offsetY += 11; + text.set( _( "Def." ), fheroes2::FontType::smallWhite() ); + text.draw( offsetX, offsetY, output ); + text.set( std::to_string( hero->GetDefense() ), fheroes2::FontType::smallWhite() ); + text.draw( offsetX + 50 - text.width(), offsetY, output ); + + offsetY += 11; + text.set( _( "Power" ), fheroes2::FontType::smallWhite() ); + text.draw( offsetX, offsetY, output ); + text.set( std::to_string( hero->GetPower() ), fheroes2::FontType::smallWhite() ); + text.draw( offsetX + 50 - text.width(), offsetY, output ); + + offsetY += 11; + text.set( _( "Knowl" ), fheroes2::FontType::smallWhite() ); + text.draw( offsetX, offsetY, output ); + text.set( std::to_string( hero->GetKnowledge() ), fheroes2::FontType::smallWhite() ); + text.draw( offsetX + 50 - text.width(), offsetY, output ); + } - int32_t offsetX = pos.x + static_cast( i ) * step - ( static_cast( colors.size() ) * sptireWidth ) / 2 + 3; + void drawHeroIcons( const Colors & colors, const bool drawStats, const fheroes2::Point & pos, const int32_t step, const int frameIcnID, fheroes2::Image & output ) + { + int32_t offsetX = pos.x + 1; for ( const int color : colors ) { - const fheroes2::Sprite & flag = fheroes2::AGG::GetICN( ICN::TOWNWIND, 22 + Color::GetIndex( color ) ); - fheroes2::Blit( flag, output, offsetX, offsetY ); - offsetX += sptireWidth; - } - } -} + const Heroes * hero = world.GetKingdom( color ).GetBestHero(); + if ( hero == nullptr ) { + offsetX += step; + continue; + } -void drawHeroStats( const Heroes * hero, const int32_t offsetX, int32_t offsetY, fheroes2::Image & output ) -{ - fheroes2::Text text( _( "Att." ), fheroes2::FontType::smallWhite() ); - text.draw( offsetX, offsetY, output ); - text.set( std::to_string( hero->GetAttack() ), fheroes2::FontType::smallWhite() ); - text.draw( offsetX + 50 - text.width(), offsetY, output ); - - offsetY += 11; - text.set( _( "Def." ), fheroes2::FontType::smallWhite() ); - text.draw( offsetX, offsetY, output ); - text.set( std::to_string( hero->GetDefense() ), fheroes2::FontType::smallWhite() ); - text.draw( offsetX + 50 - text.width(), offsetY, output ); - - offsetY += 11; - text.set( _( "Power" ), fheroes2::FontType::smallWhite() ); - text.draw( offsetX, offsetY, output ); - text.set( std::to_string( hero->GetPower() ), fheroes2::FontType::smallWhite() ); - text.draw( offsetX + 50 - text.width(), offsetY, output ); - - offsetY += 11; - text.set( _( "Knowl" ), fheroes2::FontType::smallWhite() ); - text.draw( offsetX, offsetY, output ); - text.set( std::to_string( hero->GetKnowledge() ), fheroes2::FontType::smallWhite() ); - text.draw( offsetX + 50 - text.width(), offsetY, output ); -} + const fheroes2::Sprite & window = fheroes2::AGG::GetICN( frameIcnID, 22 ); + fheroes2::Blit( window, output, offsetX - window.width() / 2, pos.y - 4 ); -void drawHeroIcons( const Colors & colors, const bool drawStats, const fheroes2::Point & pos, const int32_t step, const int frameIcnID, fheroes2::Image & output ) -{ - int32_t offsetX = pos.x + 1; + const fheroes2::Sprite & icon = hero->GetPortrait( PORT_SMALL ); + fheroes2::Copy( icon, 0, 0, output, offsetX - icon.width() / 2, pos.y, icon.width(), icon.height() ); + + if ( drawStats ) { + drawHeroStats( hero, offsetX - 26, pos.y + 34, output ); + } - for ( const int color : colors ) { - const Heroes * hero = world.GetKingdom( color ).GetBestHero(); - if ( hero == nullptr ) { offsetX += step; - continue; } + } - const fheroes2::Sprite & window = fheroes2::AGG::GetICN( frameIcnID, 22 ); - fheroes2::Blit( window, output, offsetX - window.width() / 2, pos.y - 4 ); + void drawPersonality( const Colors & colors, const fheroes2::Point & pos, const int32_t step, fheroes2::Image & output ) + { + int32_t offsetX = pos.x; - const fheroes2::Sprite & icon = hero->GetPortrait( PORT_SMALL ); - fheroes2::Copy( icon, 0, 0, output, offsetX - icon.width() / 2, pos.y, icon.width(), icon.height() ); + for ( const int color : colors ) { + const Player * player = Players::Get( color ); + const fheroes2::Text text( player->isControlHuman() ? _( "Human" ) : player->GetPersonalityString(), fheroes2::FontType::smallWhite() ); + text.draw( offsetX - text.width() / 2, pos.y, output ); - if ( drawStats ) { - drawHeroStats( hero, offsetX - 26, pos.y + 34, output ); + offsetX += step; } - - offsetX += step; } -} -void drawPersonality( const Colors & colors, const fheroes2::Point & pos, const int32_t step, fheroes2::Image & output ) -{ - int32_t offsetX = pos.x; + void drawBestMonsterIcons( const Colors & colors, const fheroes2::Point & pos, const int32_t step, fheroes2::Image & output ) + { + int32_t offsetX = pos.x; - for ( const int color : colors ) { - const Player * player = Players::Get( color ); - const fheroes2::Text text( player->isControlHuman() ? _( "Human" ) : player->GetPersonalityString(), fheroes2::FontType::smallWhite() ); - text.draw( offsetX - text.width() / 2, pos.y, output ); + for ( const int color : colors ) { + const Monster monster = world.GetKingdom( color ).GetStrongestMonster(); + if ( monster.isValid() ) { + const fheroes2::Sprite & sprite = fheroes2::AGG::GetICN( ICN::MONS32, monster.GetSpriteIndex() ); + fheroes2::Blit( sprite, output, offsetX - sprite.width() / 2, pos.y - sprite.height() / 2 ); + } - offsetX += step; + offsetX += step; + } } -} - -void drawBestMonsterIcons( const Colors & colors, const fheroes2::Point & pos, const int32_t step, fheroes2::Image & output ) -{ - int32_t offsetX = pos.x; - for ( const int color : colors ) { - const Monster monster = world.GetKingdom( color ).GetStrongestMonster(); - if ( monster.isValid() ) { - const fheroes2::Sprite & sprite = fheroes2::AGG::GetICN( ICN::MONS32, monster.GetSpriteIndex() ); - fheroes2::Blit( sprite, output, offsetX - sprite.width() / 2, pos.y - sprite.height() / 2 ); + const char * getPlayerOrderString( const size_t player ) + { + switch ( player ) { + case 0: + return _( "1st" ); + case 1: + return _( "2nd" ); + case 2: + return _( "3rd" ); + case 3: + return _( "4th" ); + case 4: + return _( "5th" ); + case 5: + return _( "6th" ); + default: + // The engine supports up to 6 players. Check your logic! + assert( 0 ); } - offsetX += step; - } -} - -const char * getPlayerOrderString( const size_t player ) -{ - switch ( player ) { - case 0: - return _( "1st" ); - case 1: - return _( "2nd" ); - case 2: - return _( "3rd" ); - case 3: - return _( "4th" ); - case 4: - return _( "5th" ); - case 5: - return _( "6th" ); - default: - // The engine supports up to 6 players. Check your logic! - assert( 0 ); + return {}; } - - return {}; } void Dialog::ThievesGuild( const bool oracle ) diff --git a/src/fheroes2/editor/editor_save_map_window.cpp b/src/fheroes2/editor/editor_save_map_window.cpp index fb3290d0799..6d46812700c 100644 --- a/src/fheroes2/editor/editor_save_map_window.cpp +++ b/src/fheroes2/editor/editor_save_map_window.cpp @@ -18,6 +18,8 @@ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * ***************************************************************************/ +#include "editor_save_map_window.h" + #include #include #include diff --git a/src/fheroes2/game/game_startgame.cpp b/src/fheroes2/game/game_startgame.cpp index 7891d881a59..fea9e7aadee 100644 --- a/src/fheroes2/game/game_startgame.cpp +++ b/src/fheroes2/game/game_startgame.cpp @@ -128,6 +128,63 @@ namespace return friendColors; } + + void ShowNewWeekDialog() + { + // restore the original music on exit + const AudioManager::MusicRestorer musicRestorer; + + const bool isNewMonth = world.BeginMonth(); + + AudioManager::PlayMusic( isNewMonth ? MUS::NEW_MONTH : MUS::NEW_WEEK, Music::PlaybackMode::PLAY_ONCE ); + + const Week & week = world.GetWeekType(); + + // head + std::string message = isNewMonth ? _( "Astrologers proclaim the Month of the %{name}." ) : _( "Astrologers proclaim the Week of the %{name}." ); + StringReplace( message, "%{name}", week.GetName() ); + message += "\n\n"; + + if ( week.GetType() == WeekName::MONSTERS ) { + const Monster monster( week.GetMonster() ); + const uint32_t count = isNewMonth ? Castle::GetGrownMonthOf() : Castle::GetGrownWeekOf(); + + if ( monster.isValid() && count ) { + if ( isNewMonth ) + message += 100 == Castle::GetGrownMonthOf() ? _( "After regular growth, the population of %{monster} is doubled!" ) + : _n( "After regular growth, the population of %{monster} increases by %{count} percent!", + "After regular growth, the population of %{monster} increases by %{count} percent!", count ); + else + message += _( "%{monster} growth +%{count}." ); + StringReplaceWithLowercase( message, "%{monster}", monster.GetMultiName() ); + StringReplace( message, "%{count}", count ); + message += "\n\n"; + } + } + + if ( week.GetType() == WeekName::PLAGUE ) + message += _( " All populations are halved." ); + else + message += _( " All dwellings increase population." ); + + fheroes2::showStandardTextMessage( isNewMonth ? _( "New Month!" ) : _( "New Week!" ), message, Dialog::OK ); + } + + void ShowWarningLostTownsDialog() + { + const Kingdom & myKingdom = world.GetKingdom( Settings::Get().CurrentColor() ); + const uint32_t lostTownDays = myKingdom.GetLostTownDays(); + + if ( lostTownDays == 1 ) { + Game::DialogPlayers( myKingdom.GetColor(), _( "Beware!" ), + _( "%{color} player, this is your last day to capture a town, or you will be banished from this land." ) ); + } + else if ( lostTownDays > 0 && lostTownDays <= Game::GetLostTownDays() ) { + std::string str = _( "%{color} player, you only have %{day} days left to capture a town, or you will be banished from this land." ); + StringReplace( str, "%{day}", lostTownDays ); + Game::DialogPlayers( myKingdom.GetColor(), _( "Beware!" ), str ); + } + } } fheroes2::GameMode Game::StartBattleOnly() @@ -380,63 +437,6 @@ void Game::OpenHeroesDialog( Heroes & hero, bool updateFocus, const bool renderB } } -void ShowNewWeekDialog() -{ - // restore the original music on exit - const AudioManager::MusicRestorer musicRestorer; - - const bool isNewMonth = world.BeginMonth(); - - AudioManager::PlayMusic( isNewMonth ? MUS::NEW_MONTH : MUS::NEW_WEEK, Music::PlaybackMode::PLAY_ONCE ); - - const Week & week = world.GetWeekType(); - - // head - std::string message = isNewMonth ? _( "Astrologers proclaim the Month of the %{name}." ) : _( "Astrologers proclaim the Week of the %{name}." ); - StringReplace( message, "%{name}", week.GetName() ); - message += "\n\n"; - - if ( week.GetType() == WeekName::MONSTERS ) { - const Monster monster( week.GetMonster() ); - const uint32_t count = isNewMonth ? Castle::GetGrownMonthOf() : Castle::GetGrownWeekOf(); - - if ( monster.isValid() && count ) { - if ( isNewMonth ) - message += 100 == Castle::GetGrownMonthOf() ? _( "After regular growth, the population of %{monster} is doubled!" ) - : _n( "After regular growth, the population of %{monster} increases by %{count} percent!", - "After regular growth, the population of %{monster} increases by %{count} percent!", count ); - else - message += _( "%{monster} growth +%{count}." ); - StringReplaceWithLowercase( message, "%{monster}", monster.GetMultiName() ); - StringReplace( message, "%{count}", count ); - message += "\n\n"; - } - } - - if ( week.GetType() == WeekName::PLAGUE ) - message += _( " All populations are halved." ); - else - message += _( " All dwellings increase population." ); - - fheroes2::showStandardTextMessage( isNewMonth ? _( "New Month!" ) : _( "New Week!" ), message, Dialog::OK ); -} - -void ShowWarningLostTownsDialog() -{ - const Kingdom & myKingdom = world.GetKingdom( Settings::Get().CurrentColor() ); - const uint32_t lostTownDays = myKingdom.GetLostTownDays(); - - if ( lostTownDays == 1 ) { - Game::DialogPlayers( myKingdom.GetColor(), _( "Beware!" ), - _( "%{color} player, this is your last day to capture a town, or you will be banished from this land." ) ); - } - else if ( lostTownDays > 0 && lostTownDays <= Game::GetLostTownDays() ) { - std::string str = _( "%{color} player, you only have %{day} days left to capture a town, or you will be banished from this land." ); - StringReplace( str, "%{day}", lostTownDays ); - Game::DialogPlayers( myKingdom.GetColor(), _( "Beware!" ), str ); - } -} - int Interface::AdventureMap::GetCursorFocusCastle( const Castle & castle, const Maps::Tiles & tile ) { switch ( tile.GetObject() ) { diff --git a/src/fheroes2/kingdom/kingdom.cpp b/src/fheroes2/kingdom/kingdom.cpp index 8c4eee36c3a..4f09116e160 100644 --- a/src/fheroes2/kingdom/kingdom.cpp +++ b/src/fheroes2/kingdom/kingdom.cpp @@ -96,11 +96,11 @@ namespace return corrected; } -} -bool HeroesStrongestArmy( const Heroes * h1, const Heroes * h2 ) -{ - return h1 && h2 && h2->GetArmy().isStrongerThan( h1->GetArmy() ); + bool HeroesStrongestArmy( const Heroes * h1, const Heroes * h2 ) + { + return h1 && h2 && h2->GetArmy().isStrongerThan( h1->GetArmy() ); + } } Kingdom::Kingdom() diff --git a/src/fheroes2/kingdom/kingdom_overview.cpp b/src/fheroes2/kingdom/kingdom_overview.cpp index b350743087d..2d849adb934 100644 --- a/src/fheroes2/kingdom/kingdom_overview.cpp +++ b/src/fheroes2/kingdom/kingdom_overview.cpp @@ -117,646 +117,646 @@ namespace // Copy one vertical line in case of previous army selection. fheroes2::Copy( overback, 29, 12, output, overbackOffsetX, dst.y + 12, 1, 357 ); } -} - -struct HeroRow -{ - Heroes * hero{ nullptr }; - std::unique_ptr armyBar; - std::unique_ptr artifactsBar; - std::unique_ptr secSkillsBar; - std::unique_ptr primSkillsBar; - explicit HeroRow( Heroes * ptr = nullptr ) + struct HeroRow { - assert( ptr != nullptr ); - Init( ptr ); - } - - HeroRow( const HeroRow & ) = delete; - HeroRow & operator=( const HeroRow & ) = delete; - HeroRow( HeroRow && ) = default; - HeroRow & operator=( HeroRow && ) = default; - ~HeroRow() = default; - - void Init( Heroes * ptr ) + Heroes * hero{ nullptr }; + std::unique_ptr armyBar; + std::unique_ptr artifactsBar; + std::unique_ptr secSkillsBar; + std::unique_ptr primSkillsBar; + + explicit HeroRow( Heroes * ptr = nullptr ) + { + assert( ptr != nullptr ); + Init( ptr ); + } + + HeroRow( const HeroRow & ) = delete; + HeroRow & operator=( const HeroRow & ) = delete; + HeroRow( HeroRow && ) = default; + HeroRow & operator=( HeroRow && ) = default; + ~HeroRow() = default; + + void Init( Heroes * ptr ) + { + hero = ptr; + + armyBar = std::make_unique( &hero->GetArmy(), true, false ); + armyBar->SetBackground( { 41, 53 }, fheroes2::GetColorId( 72, 28, 0 ) ); + armyBar->setTableSize( { 5, 1 } ); + armyBar->setInBetweenItemsOffset( { -1, 0 } ); + armyBar->setTroopWindowOffsetY( -60 ); + + artifactsBar = std::make_unique( hero, true, false, false, true, nullptr ); + artifactsBar->setTableSize( { 7, 2 } ); + artifactsBar->setInBetweenItemsOffset( { 1, 8 } ); + artifactsBar->SetContent( hero->GetBagArtifacts() ); + + secSkillsBar = std::make_unique( *hero ); + secSkillsBar->setTableSize( { 4, 2 } ); + secSkillsBar->setInBetweenItemsOffset( { -1, 8 } ); + secSkillsBar->SetContent( hero->GetSecondarySkills().ToVector() ); + + primSkillsBar = std::make_unique( hero, true, false, false ); + primSkillsBar->setTableSize( { 4, 1 } ); + primSkillsBar->setInBetweenItemsOffset( { 2, 0 } ); + primSkillsBar->SetTextOff( 20, -13 ); + } + }; + + class StatsHeroesList : public Interface::ListBox { - hero = ptr; - - armyBar = std::make_unique( &hero->GetArmy(), true, false ); - armyBar->SetBackground( { 41, 53 }, fheroes2::GetColorId( 72, 28, 0 ) ); - armyBar->setTableSize( { 5, 1 } ); - armyBar->setInBetweenItemsOffset( { -1, 0 } ); - armyBar->setTroopWindowOffsetY( -60 ); - - artifactsBar = std::make_unique( hero, true, false, false, true, nullptr ); - artifactsBar->setTableSize( { 7, 2 } ); - artifactsBar->setInBetweenItemsOffset( { 1, 8 } ); - artifactsBar->SetContent( hero->GetBagArtifacts() ); - - secSkillsBar = std::make_unique( *hero ); - secSkillsBar->setTableSize( { 4, 2 } ); - secSkillsBar->setInBetweenItemsOffset( { -1, 8 } ); - secSkillsBar->SetContent( hero->GetSecondarySkills().ToVector() ); - - primSkillsBar = std::make_unique( hero, true, false, false ); - primSkillsBar->setTableSize( { 4, 1 } ); - primSkillsBar->setInBetweenItemsOffset( { 2, 0 } ); - primSkillsBar->SetTextOff( 20, -13 ); - } -}; + public: + StatsHeroesList( const fheroes2::Rect & windowArea, const fheroes2::Point & offset, const VecHeroes & heroes ); -class StatsHeroesList : public Interface::ListBox -{ -public: - StatsHeroesList( const fheroes2::Rect & windowArea, const fheroes2::Point & offset, const VecHeroes & heroes ); - - bool Refresh( VecHeroes & heroes ); + bool Refresh( VecHeroes & heroes ); - void RedrawItem( const HeroRow & row, int32_t dstx, int32_t dsty, bool current ) override; - void RedrawBackground( const fheroes2::Point & dst ) override; + void RedrawItem( const HeroRow & row, int32_t dstx, int32_t dsty, bool current ) override; + void RedrawBackground( const fheroes2::Point & dst ) override; - void ActionCurrentUp() override - { - // Do nothing. - } + void ActionCurrentUp() override + { + // Do nothing. + } - void ActionCurrentDn() override - { - // Do nothing. - } + void ActionCurrentDn() override + { + // Do nothing. + } - void ActionListSingleClick( HeroRow & /* unused */ ) override - { - // Do nothing. - } + void ActionListSingleClick( HeroRow & /* unused */ ) override + { + // Do nothing. + } - void ActionListDoubleClick( HeroRow & /* unused */ ) override - { - // Do nothing. - } + void ActionListDoubleClick( HeroRow & /* unused */ ) override + { + // Do nothing. + } - void ActionListPressRight( HeroRow & /* unused */ ) override - { - // Do nothing. - } + void ActionListPressRight( HeroRow & /* unused */ ) override + { + // Do nothing. + } - void ActionListSingleClick( HeroRow & row, const fheroes2::Point & cursor, int32_t ox, int32_t oy ) override; - void ActionListDoubleClick( HeroRow & row, const fheroes2::Point & cursor, int32_t ox, int32_t oy ) override; - void ActionListPressRight( HeroRow & row, const fheroes2::Point & cursor, int32_t ox, int32_t oy ) override; - bool ActionListCursor( HeroRow & row, const fheroes2::Point & cursor ) override; + void ActionListSingleClick( HeroRow & row, const fheroes2::Point & cursor, int32_t ox, int32_t oy ) override; + void ActionListDoubleClick( HeroRow & row, const fheroes2::Point & cursor, int32_t ox, int32_t oy ) override; + void ActionListPressRight( HeroRow & row, const fheroes2::Point & cursor, int32_t ox, int32_t oy ) override; + bool ActionListCursor( HeroRow & row, const fheroes2::Point & cursor ) override; -private: - void SetContent( const VecHeroes & heroes ); + private: + void SetContent( const VecHeroes & heroes ); - std::vector content; - const fheroes2::Rect _windowArea; -}; + std::vector content; + const fheroes2::Rect _windowArea; + }; -StatsHeroesList::StatsHeroesList( const fheroes2::Rect & windowArea, const fheroes2::Point & offset, const VecHeroes & heroes ) - : Interface::ListBox( offset ) - , _windowArea( windowArea ) -{ - const fheroes2::Sprite & back = fheroes2::AGG::GetICN( ICN::OVERVIEW, 13 ); - const int32_t backHeight = back.height(); - - SetTopLeft( offset ); - const int32_t offsetX = offset.x + scrollbarOffset; - setScrollBarArea( { offsetX + 2, offset.y + 18, back.width(), backHeight - 2 } ); - - const fheroes2::Sprite & originalSlider = fheroes2::AGG::GetICN( ICN::SCROLL, 4 ); - const fheroes2::Image scrollbarSlider = fheroes2::generateScrollbarSlider( originalSlider, false, backHeight - 2, 4, static_cast( heroes.size() ), - { 0, 0, originalSlider.width(), 8 }, { 0, 7, originalSlider.width(), 8 } ); - - setScrollBarImage( scrollbarSlider ); - SetScrollButtonUp( ICN::SCROLL, 0, 1, { offsetX, offset.y } ); - SetScrollButtonDn( ICN::SCROLL, 2, 3, { offsetX, offset.y + 20 + backHeight } ); - SetAreaMaxItems( 4 ); - SetAreaItems( { offset.x + 30, offset.y + 17, 594, 344 } ); - SetContent( heroes ); -} - -void StatsHeroesList::SetContent( const VecHeroes & heroes ) -{ - content.clear(); - content.reserve( heroes.size() ); - for ( Heroes * hero : heroes ) { - content.emplace_back( hero ); - } - SetListContent( content ); -} + StatsHeroesList::StatsHeroesList( const fheroes2::Rect & windowArea, const fheroes2::Point & offset, const VecHeroes & heroes ) + : Interface::ListBox( offset ) + , _windowArea( windowArea ) + { + const fheroes2::Sprite & back = fheroes2::AGG::GetICN( ICN::OVERVIEW, 13 ); + const int32_t backHeight = back.height(); -// Updates the UI list according to current list of kingdom heroes. -// Returns true if we updated something -bool StatsHeroesList::Refresh( VecHeroes & heroes ) -{ - const size_t contentSize = content.size(); + SetTopLeft( offset ); + const int32_t offsetX = offset.x + scrollbarOffset; + setScrollBarArea( { offsetX + 2, offset.y + 18, back.width(), backHeight - 2 } ); - if ( heroes.size() != contentSize ) { - const fheroes2::Sprite & back = fheroes2::AGG::GetICN( ICN::OVERVIEW, 13 ); const fheroes2::Sprite & originalSlider = fheroes2::AGG::GetICN( ICN::SCROLL, 4 ); - const fheroes2::Image scrollbarSlider = fheroes2::generateScrollbarSlider( originalSlider, false, back.height() - 2, 4, static_cast( heroes.size() ), + const fheroes2::Image scrollbarSlider = fheroes2::generateScrollbarSlider( originalSlider, false, backHeight - 2, 4, static_cast( heroes.size() ), { 0, 0, originalSlider.width(), 8 }, { 0, 7, originalSlider.width(), 8 } ); - setScrollBarImage( scrollbarSlider ); + setScrollBarImage( scrollbarSlider ); + SetScrollButtonUp( ICN::SCROLL, 0, 1, { offsetX, offset.y } ); + SetScrollButtonDn( ICN::SCROLL, 2, 3, { offsetX, offset.y + 20 + backHeight } ); + SetAreaMaxItems( 4 ); + SetAreaItems( { offset.x + 30, offset.y + 17, 594, 344 } ); SetContent( heroes ); - - return true; } - for ( size_t i = 0; i < contentSize; ++i ) { - if ( heroes[i] != content[i].hero ) { - SetContent( heroes ); - return true; + + void StatsHeroesList::SetContent( const VecHeroes & heroes ) + { + content.clear(); + content.reserve( heroes.size() ); + for ( Heroes * hero : heroes ) { + content.emplace_back( hero ); } + SetListContent( content ); } - return false; -} -void StatsHeroesList::ActionListDoubleClick( HeroRow & row, const fheroes2::Point & cursor, int32_t ox, int32_t oy ) -{ - ActionListSingleClick( row, cursor, ox, oy ); -} + // Updates the UI list according to current list of kingdom heroes. + // Returns true if we updated something + bool StatsHeroesList::Refresh( VecHeroes & heroes ) + { + const size_t contentSize = content.size(); -void StatsHeroesList::ActionListSingleClick( HeroRow & row, const fheroes2::Point & cursor, int32_t ox, int32_t oy ) -{ - if ( row.hero && ( fheroes2::Rect( ox + 5, oy + 4, Interface::IconsBar::GetItemWidth(), Interface::IconsBar::GetItemHeight() ) & cursor ) ) { - Game::OpenHeroesDialog( *row.hero, false, false ); + if ( heroes.size() != contentSize ) { + const fheroes2::Sprite & back = fheroes2::AGG::GetICN( ICN::OVERVIEW, 13 ); + const fheroes2::Sprite & originalSlider = fheroes2::AGG::GetICN( ICN::SCROLL, 4 ); + const fheroes2::Image scrollbarSlider = fheroes2::generateScrollbarSlider( originalSlider, false, back.height() - 2, 4, static_cast( heroes.size() ), + { 0, 0, originalSlider.width(), 8 }, { 0, 7, originalSlider.width(), 8 } ); + setScrollBarImage( scrollbarSlider ); - needFadeIn = true; + SetContent( heroes ); + + return true; + } + for ( size_t i = 0; i < contentSize; ++i ) { + if ( heroes[i] != content[i].hero ) { + SetContent( heroes ); + return true; + } + } + return false; } -} -void StatsHeroesList::ActionListPressRight( HeroRow & row, const fheroes2::Point & cursor, int32_t ox, int32_t oy ) -{ - if ( row.hero && ( fheroes2::Rect( ox + 5, oy + 4, Interface::IconsBar::GetItemWidth(), Interface::IconsBar::GetItemHeight() ) & cursor ) ) { - Dialog::QuickInfoWithIndicationOnRadar( *row.hero, _windowArea ); + void StatsHeroesList::ActionListDoubleClick( HeroRow & row, const fheroes2::Point & cursor, int32_t ox, int32_t oy ) + { + ActionListSingleClick( row, cursor, ox, oy ); } -} -bool StatsHeroesList::ActionListCursor( HeroRow & row, const fheroes2::Point & cursor ) -{ - const fheroes2::Point cursorPos( cursor.x, cursor.y ); + void StatsHeroesList::ActionListSingleClick( HeroRow & row, const fheroes2::Point & cursor, int32_t ox, int32_t oy ) + { + if ( row.hero && ( fheroes2::Rect( ox + 5, oy + 4, Interface::IconsBar::GetItemWidth(), Interface::IconsBar::GetItemHeight() ) & cursor ) ) { + Game::OpenHeroesDialog( *row.hero, false, false ); - if ( ( row.armyBar->GetArea() & cursorPos ) && row.armyBar->QueueEventProcessing() ) { - if ( row.artifactsBar->isSelected() ) { - row.artifactsBar->ResetSelected(); + needFadeIn = true; } - return true; } - if ( ( row.artifactsBar->GetArea() & cursorPos ) && row.artifactsBar->QueueEventProcessing() ) { - if ( row.armyBar->isSelected() ) { - row.armyBar->ResetSelected(); + void StatsHeroesList::ActionListPressRight( HeroRow & row, const fheroes2::Point & cursor, int32_t ox, int32_t oy ) + { + if ( row.hero && ( fheroes2::Rect( ox + 5, oy + 4, Interface::IconsBar::GetItemWidth(), Interface::IconsBar::GetItemHeight() ) & cursor ) ) { + Dialog::QuickInfoWithIndicationOnRadar( *row.hero, _windowArea ); } - return true; } - if ( ( row.primSkillsBar->GetArea() & cursorPos ) && row.primSkillsBar->QueueEventProcessing() ) { - return true; - } + bool StatsHeroesList::ActionListCursor( HeroRow & row, const fheroes2::Point & cursor ) + { + const fheroes2::Point cursorPos( cursor.x, cursor.y ); - if ( ( row.secSkillsBar->GetArea() & cursorPos ) && row.secSkillsBar->QueueEventProcessing() ) { - return true; - } + if ( ( row.armyBar->GetArea() & cursorPos ) && row.armyBar->QueueEventProcessing() ) { + if ( row.artifactsBar->isSelected() ) { + row.artifactsBar->ResetSelected(); + } + return true; + } - return false; -} + if ( ( row.artifactsBar->GetArea() & cursorPos ) && row.artifactsBar->QueueEventProcessing() ) { + if ( row.armyBar->isSelected() ) { + row.armyBar->ResetSelected(); + } + return true; + } -void StatsHeroesList::RedrawItem( const HeroRow & row, int32_t dstx, int32_t dsty, bool current ) -{ - (void)current; + if ( ( row.primSkillsBar->GetArea() & cursorPos ) && row.primSkillsBar->QueueEventProcessing() ) { + return true; + } + + if ( ( row.secSkillsBar->GetArea() & cursorPos ) && row.secSkillsBar->QueueEventProcessing() ) { + return true; + } - if ( row.hero == nullptr ) { - // No hero to draw. - return; + return false; } - fheroes2::Display & display = fheroes2::Display::instance(); - fheroes2::Blit( fheroes2::AGG::GetICN( ICN::OVERVIEW, 10 ), display, dstx, dsty ); + void StatsHeroesList::RedrawItem( const HeroRow & row, int32_t dstx, int32_t dsty, bool current ) + { + (void)current; - // base info - Interface::RedrawHeroesIcon( *row.hero, dstx + 5, dsty + 4 ); + if ( row.hero == nullptr ) { + // No hero to draw. + return; + } - int32_t offsetX = dstx + 90; - const int32_t offsetY = dsty + 22; + fheroes2::Display & display = fheroes2::Display::instance(); + fheroes2::Blit( fheroes2::AGG::GetICN( ICN::OVERVIEW, 10 ), display, dstx, dsty ); - fheroes2::Text text( std::to_string( row.hero->GetAttack() ), fheroes2::FontType::smallWhite() ); - text.draw( offsetX - text.width(), offsetY, display ); + // base info + Interface::RedrawHeroesIcon( *row.hero, dstx + 5, dsty + 4 ); - offsetX += 35; - text.set( std::to_string( row.hero->GetDefense() ), fheroes2::FontType::smallWhite() ); - text.draw( offsetX - text.width(), offsetY, display ); + int32_t offsetX = dstx + 90; + const int32_t offsetY = dsty + 22; - offsetX += 35; - text.set( std::to_string( row.hero->GetPower() ), fheroes2::FontType::smallWhite() ); - text.draw( offsetX - text.width(), offsetY, display ); + fheroes2::Text text( std::to_string( row.hero->GetAttack() ), fheroes2::FontType::smallWhite() ); + text.draw( offsetX - text.width(), offsetY, display ); - offsetX += 35; - text.set( std::to_string( row.hero->GetKnowledge() ), fheroes2::FontType::smallWhite() ); - text.draw( offsetX - text.width(), offsetY, display ); + offsetX += 35; + text.set( std::to_string( row.hero->GetDefense() ), fheroes2::FontType::smallWhite() ); + text.draw( offsetX - text.width(), offsetY, display ); - // primary skills info - row.primSkillsBar->setRenderingOffset( { dstx + 56, dsty - 3 } ); - row.primSkillsBar->Redraw( display ); + offsetX += 35; + text.set( std::to_string( row.hero->GetPower() ), fheroes2::FontType::smallWhite() ); + text.draw( offsetX - text.width(), offsetY, display ); - // secondary skills info - row.secSkillsBar->setRenderingOffset( { dstx + 206, dsty + 3 } ); - row.secSkillsBar->Redraw( display ); + offsetX += 35; + text.set( std::to_string( row.hero->GetKnowledge() ), fheroes2::FontType::smallWhite() ); + text.draw( offsetX - text.width(), offsetY, display ); - // artifacts info - row.artifactsBar->setRenderingOffset( { dstx + 348, dsty + 3 } ); - row.artifactsBar->Redraw( display ); + // primary skills info + row.primSkillsBar->setRenderingOffset( { dstx + 56, dsty - 3 } ); + row.primSkillsBar->Redraw( display ); - // army info - row.armyBar->setRenderingOffset( { dstx - 1, dsty + 30 } ); - row.armyBar->Redraw( display ); -} + // secondary skills info + row.secSkillsBar->setRenderingOffset( { dstx + 206, dsty + 3 } ); + row.secSkillsBar->Redraw( display ); -void StatsHeroesList::RedrawBackground( const fheroes2::Point & dst ) -{ - fheroes2::Display & display = fheroes2::Display::instance(); + // artifacts info + row.artifactsBar->setRenderingOffset( { dstx + 348, dsty + 3 } ); + row.artifactsBar->Redraw( display ); - const fheroes2::Sprite & header = fheroes2::AGG::GetICN( ICN::OVERVIEW, 6 ); - fheroes2::Copy( header, 0, 0, display, dst.x + 30, dst.y, header.width(), header.height() ); + // army info + row.armyBar->setRenderingOffset( { dstx - 1, dsty + 30 } ); + row.armyBar->Redraw( display ); + } - int32_t offsetY = dst.y + 3; + void StatsHeroesList::RedrawBackground( const fheroes2::Point & dst ) + { + fheroes2::Display & display = fheroes2::Display::instance(); - fheroes2::Text text( _( "Hero/Stats" ), fheroes2::FontType::smallWhite() ); - text.draw( dst.x + 130 - text.width() / 2, offsetY, display ); + const fheroes2::Sprite & header = fheroes2::AGG::GetICN( ICN::OVERVIEW, 6 ); + fheroes2::Copy( header, 0, 0, display, dst.x + 30, dst.y, header.width(), header.height() ); - text.set( _( "Skills" ), fheroes2::FontType::smallWhite() ); - text.draw( dst.x + 300 - text.width() / 2, offsetY, display ); + const int32_t offsetY = dst.y + 3; - text.set( _( "Artifacts" ), fheroes2::FontType::smallWhite() ); - text.draw( dst.x + 500 - text.width() / 2, offsetY, display ); + fheroes2::Text text( _( "Hero/Stats" ), fheroes2::FontType::smallWhite() ); + text.draw( dst.x + 130 - text.width() / 2, offsetY, display ); - redrawCommonBackground( dst, VisibleItemCount(), display ); -} + text.set( _( "Skills" ), fheroes2::FontType::smallWhite() ); + text.draw( dst.x + 300 - text.width() / 2, offsetY, display ); -struct CstlRow -{ - Castle * castle{ nullptr }; - std::unique_ptr garrisonArmyBar; - std::unique_ptr heroArmyBar; - std::unique_ptr dwellingsBar; + text.set( _( "Artifacts" ), fheroes2::FontType::smallWhite() ); + text.draw( dst.x + 500 - text.width() / 2, offsetY, display ); - explicit CstlRow( Castle * ptr = nullptr ) - { - assert( ptr != nullptr ); - Init( ptr ); + redrawCommonBackground( dst, VisibleItemCount(), display ); } - CstlRow( const CstlRow & ) = delete; - CstlRow & operator=( const CstlRow & ) = delete; - CstlRow( CstlRow && ) = default; - CstlRow & operator=( CstlRow && ) = default; - ~CstlRow() = default; - - void Init( Castle * ptr ) + struct CstlRow { - castle = ptr; + Castle * castle{ nullptr }; + std::unique_ptr garrisonArmyBar; + std::unique_ptr heroArmyBar; + std::unique_ptr dwellingsBar; - garrisonArmyBar = std::make_unique( &castle->GetArmy(), true, false ); - garrisonArmyBar->SetBackground( { 41, 41 }, fheroes2::GetColorId( 40, 12, 0 ) ); - garrisonArmyBar->setTableSize( { 5, 1 } ); - garrisonArmyBar->setInBetweenItemsOffset( { -1, 0 } ); - garrisonArmyBar->setTroopWindowOffsetY( -60 ); + explicit CstlRow( Castle * ptr = nullptr ) + { + assert( ptr != nullptr ); + Init( ptr ); + } - updateHeroArmyBar(); + CstlRow( const CstlRow & ) = delete; + CstlRow & operator=( const CstlRow & ) = delete; + CstlRow( CstlRow && ) = default; + CstlRow & operator=( CstlRow && ) = default; + ~CstlRow() = default; - dwellingsBar = std::make_unique( *castle, fheroes2::Size{ 39, 52 } ); - dwellingsBar->setTableSize( { 6, 1 } ); - dwellingsBar->setInBetweenItemsOffset( { 2, 0 } ); - } + void Init( Castle * ptr ) + { + castle = ptr; - void updateHeroArmyBar() - { - Heroes * hero = world.GetHero( *castle ); + garrisonArmyBar = std::make_unique( &castle->GetArmy(), true, false ); + garrisonArmyBar->SetBackground( { 41, 41 }, fheroes2::GetColorId( 40, 12, 0 ) ); + garrisonArmyBar->setTableSize( { 5, 1 } ); + garrisonArmyBar->setInBetweenItemsOffset( { -1, 0 } ); + garrisonArmyBar->setTroopWindowOffsetY( -60 ); - if ( hero ) { - heroArmyBar = std::make_unique( &hero->GetArmy(), true, false ); - heroArmyBar->SetBackground( { 41, 41 }, fheroes2::GetColorId( 40, 12, 0 ) ); - heroArmyBar->setTableSize( { 5, 1 } ); - heroArmyBar->setInBetweenItemsOffset( { -1, 0 } ); - } - else { - heroArmyBar.reset(); + updateHeroArmyBar(); + + dwellingsBar = std::make_unique( *castle, fheroes2::Size{ 39, 52 } ); + dwellingsBar->setTableSize( { 6, 1 } ); + dwellingsBar->setInBetweenItemsOffset( { 2, 0 } ); } - } -}; -class StatsCastlesList : public Interface::ListBox -{ -public: - StatsCastlesList( const fheroes2::Rect & windowArea, const fheroes2::Point & offset, const VecCastles & castles ); + void updateHeroArmyBar() + { + Heroes * hero = world.GetHero( *castle ); - void RedrawItem( const CstlRow & row, int32_t dstx, int32_t dsty, bool current ) override; - void RedrawBackground( const fheroes2::Point & dst ) override; + if ( hero ) { + heroArmyBar = std::make_unique( &hero->GetArmy(), true, false ); + heroArmyBar->SetBackground( { 41, 41 }, fheroes2::GetColorId( 40, 12, 0 ) ); + heroArmyBar->setTableSize( { 5, 1 } ); + heroArmyBar->setInBetweenItemsOffset( { -1, 0 } ); + } + else { + heroArmyBar.reset(); + } + } + }; - void ActionCurrentUp() override + class StatsCastlesList : public Interface::ListBox { - // Do nothing. - } + public: + StatsCastlesList( const fheroes2::Rect & windowArea, const fheroes2::Point & offset, const VecCastles & castles ); - void ActionCurrentDn() override - { - // Do nothing. - } + void RedrawItem( const CstlRow & row, int32_t dstx, int32_t dsty, bool current ) override; + void RedrawBackground( const fheroes2::Point & dst ) override; - void ActionListDoubleClick( CstlRow & /* unused */ ) override - { - // Do nothing. - } + void ActionCurrentUp() override + { + // Do nothing. + } - void ActionListSingleClick( CstlRow & /* unused */ ) override - { - // Do nothing. - } + void ActionCurrentDn() override + { + // Do nothing. + } - void ActionListPressRight( CstlRow & /* unused */ ) override - { - // Do nothing. - } + void ActionListDoubleClick( CstlRow & /* unused */ ) override + { + // Do nothing. + } - void ActionListSingleClick( CstlRow & row, const fheroes2::Point & cursor, int32_t ox, int32_t oy ) override; - void ActionListDoubleClick( CstlRow & row, const fheroes2::Point & cursor, int32_t ox, int32_t oy ) override; - void ActionListPressRight( CstlRow & row, const fheroes2::Point & cursor, int32_t ox, int32_t oy ) override; - bool ActionListCursor( CstlRow & row, const fheroes2::Point & cursor ) override; + void ActionListSingleClick( CstlRow & /* unused */ ) override + { + // Do nothing. + } - void updateHeroArmyBars() - { - for ( CstlRow & row : content ) { - row.updateHeroArmyBar(); + void ActionListPressRight( CstlRow & /* unused */ ) override + { + // Do nothing. } - } -private: - std::vector content; - const fheroes2::Rect _windowArea; -}; + void ActionListSingleClick( CstlRow & row, const fheroes2::Point & cursor, int32_t ox, int32_t oy ) override; + void ActionListDoubleClick( CstlRow & row, const fheroes2::Point & cursor, int32_t ox, int32_t oy ) override; + void ActionListPressRight( CstlRow & row, const fheroes2::Point & cursor, int32_t ox, int32_t oy ) override; + bool ActionListCursor( CstlRow & row, const fheroes2::Point & cursor ) override; -StatsCastlesList::StatsCastlesList( const fheroes2::Rect & windowArea, const fheroes2::Point & offset, const VecCastles & castles ) - : Interface::ListBox( offset ) - , _windowArea( windowArea ) -{ - const fheroes2::Sprite & back = fheroes2::AGG::GetICN( ICN::OVERVIEW, 13 ); - const int32_t backHeight = back.height(); + void updateHeroArmyBars() + { + for ( CstlRow & row : content ) { + row.updateHeroArmyBar(); + } + } - SetTopLeft( offset ); - const int32_t offsetX = offset.x + scrollbarOffset; - setScrollBarArea( { offsetX + 2, offset.y + 18, back.width(), backHeight - 2 } ); + private: + std::vector content; + const fheroes2::Rect _windowArea; + }; - const fheroes2::Sprite & originalSlider = fheroes2::AGG::GetICN( ICN::SCROLL, 4 ); - const fheroes2::Image scrollbarSlider = fheroes2::generateScrollbarSlider( originalSlider, false, back.height() - 2, 4, static_cast( castles.size() ), - { 0, 0, originalSlider.width(), 8 }, { 0, 7, originalSlider.width(), 8 } ); + StatsCastlesList::StatsCastlesList( const fheroes2::Rect & windowArea, const fheroes2::Point & offset, const VecCastles & castles ) + : Interface::ListBox( offset ) + , _windowArea( windowArea ) + { + const fheroes2::Sprite & back = fheroes2::AGG::GetICN( ICN::OVERVIEW, 13 ); + const int32_t backHeight = back.height(); - setScrollBarImage( scrollbarSlider ); - SetScrollButtonUp( ICN::SCROLL, 0, 1, { offsetX, offset.y } ); - SetScrollButtonDn( ICN::SCROLL, 2, 3, { offsetX, offset.y + 20 + backHeight } ); - SetAreaMaxItems( 4 ); - SetAreaItems( { offset.x + 30, offset.y + 17, 594, 344 } ); + SetTopLeft( offset ); + const int32_t offsetX = offset.x + scrollbarOffset; + setScrollBarArea( { offsetX + 2, offset.y + 18, back.width(), backHeight - 2 } ); - content.reserve( castles.size() ); + const fheroes2::Sprite & originalSlider = fheroes2::AGG::GetICN( ICN::SCROLL, 4 ); + const fheroes2::Image scrollbarSlider = fheroes2::generateScrollbarSlider( originalSlider, false, back.height() - 2, 4, static_cast( castles.size() ), + { 0, 0, originalSlider.width(), 8 }, { 0, 7, originalSlider.width(), 8 } ); + + setScrollBarImage( scrollbarSlider ); + SetScrollButtonUp( ICN::SCROLL, 0, 1, { offsetX, offset.y } ); + SetScrollButtonDn( ICN::SCROLL, 2, 3, { offsetX, offset.y + 20 + backHeight } ); + SetAreaMaxItems( 4 ); + SetAreaItems( { offset.x + 30, offset.y + 17, 594, 344 } ); + + content.reserve( castles.size() ); + + for ( Castle * castle : castles ) { + content.emplace_back( castle ); + } - for ( Castle * castle : castles ) { - content.emplace_back( castle ); + SetListContent( content ); } - SetListContent( content ); -} + void StatsCastlesList::ActionListDoubleClick( CstlRow & row, const fheroes2::Point & cursor, int32_t ox, int32_t oy ) + { + ActionListSingleClick( row, cursor, ox, oy ); + } -void StatsCastlesList::ActionListDoubleClick( CstlRow & row, const fheroes2::Point & cursor, int32_t ox, int32_t oy ) -{ - ActionListSingleClick( row, cursor, ox, oy ); -} + void StatsCastlesList::ActionListSingleClick( CstlRow & row, const fheroes2::Point & cursor, int32_t ox, int32_t oy ) + { + if ( row.castle ) { + // click castle icon + if ( fheroes2::Rect( ox + 17, oy + 19, Interface::IconsBar::GetItemWidth(), Interface::IconsBar::GetItemHeight() ) & cursor ) { + Game::OpenCastleDialog( *row.castle, false, false ); + } -void StatsCastlesList::ActionListSingleClick( CstlRow & row, const fheroes2::Point & cursor, int32_t ox, int32_t oy ) -{ - if ( row.castle ) { - // click castle icon - if ( fheroes2::Rect( ox + 17, oy + 19, Interface::IconsBar::GetItemWidth(), Interface::IconsBar::GetItemHeight() ) & cursor ) { - Game::OpenCastleDialog( *row.castle, false, false ); - } + // click hero icon + else if ( fheroes2::Rect( ox + 82, oy + 19, Interface::IconsBar::GetItemWidth(), Interface::IconsBar::GetItemHeight() ) & cursor ) { + Heroes * hero = row.castle->GetHero(); - // click hero icon - else if ( fheroes2::Rect( ox + 82, oy + 19, Interface::IconsBar::GetItemWidth(), Interface::IconsBar::GetItemHeight() ) & cursor ) { - Heroes * hero = row.castle->GetHero(); + if ( !hero ) { + return; + } - if ( !hero ) { + Game::OpenHeroesDialog( *hero, false, false ); + } + else { return; } - Game::OpenHeroesDialog( *hero, false, false ); - } - else { - return; + needFadeIn = true; } - - needFadeIn = true; } -} -void StatsCastlesList::ActionListPressRight( CstlRow & row, const fheroes2::Point & cursor, int32_t ox, int32_t oy ) -{ - if ( row.castle ) { - if ( fheroes2::Rect( ox + 17, oy + 19, Interface::IconsBar::GetItemWidth(), Interface::IconsBar::GetItemHeight() ) & cursor ) { - Dialog::QuickInfoWithIndicationOnRadar( *row.castle, _windowArea ); - } - else if ( fheroes2::Rect( ox + 82, oy + 19, Interface::IconsBar::GetItemWidth(), Interface::IconsBar::GetItemHeight() ) & cursor ) { - const Heroes * hero = row.castle->GetHero(); - if ( hero ) { - Dialog::QuickInfoWithIndicationOnRadar( *hero, _windowArea ); + void StatsCastlesList::ActionListPressRight( CstlRow & row, const fheroes2::Point & cursor, int32_t ox, int32_t oy ) + { + if ( row.castle ) { + if ( fheroes2::Rect( ox + 17, oy + 19, Interface::IconsBar::GetItemWidth(), Interface::IconsBar::GetItemHeight() ) & cursor ) { + Dialog::QuickInfoWithIndicationOnRadar( *row.castle, _windowArea ); } - else if ( row.castle->isBuild( BUILD_CAPTAIN ) ) { - Dialog::QuickInfoWithIndicationOnRadar( row.castle->GetCaptain(), _windowArea ); + else if ( fheroes2::Rect( ox + 82, oy + 19, Interface::IconsBar::GetItemWidth(), Interface::IconsBar::GetItemHeight() ) & cursor ) { + const Heroes * hero = row.castle->GetHero(); + if ( hero ) { + Dialog::QuickInfoWithIndicationOnRadar( *hero, _windowArea ); + } + else if ( row.castle->isBuild( BUILD_CAPTAIN ) ) { + Dialog::QuickInfoWithIndicationOnRadar( row.castle->GetCaptain(), _windowArea ); + } } } } -} -bool StatsCastlesList::ActionListCursor( CstlRow & row, const fheroes2::Point & cursor ) -{ - const fheroes2::Point cursorPos( cursor.x, cursor.y ); + bool StatsCastlesList::ActionListCursor( CstlRow & row, const fheroes2::Point & cursor ) + { + const fheroes2::Point cursorPos( cursor.x, cursor.y ); - if ( row.garrisonArmyBar && ( row.garrisonArmyBar->GetArea() & cursorPos ) - && ( row.heroArmyBar ? row.garrisonArmyBar->QueueEventProcessing( *row.heroArmyBar ) : row.garrisonArmyBar->QueueEventProcessing() ) ) { - if ( row.heroArmyBar && row.heroArmyBar->isSelected() ) { - row.heroArmyBar->ResetSelected(); + if ( row.garrisonArmyBar && ( row.garrisonArmyBar->GetArea() & cursorPos ) + && ( row.heroArmyBar ? row.garrisonArmyBar->QueueEventProcessing( *row.heroArmyBar ) : row.garrisonArmyBar->QueueEventProcessing() ) ) { + if ( row.heroArmyBar && row.heroArmyBar->isSelected() ) { + row.heroArmyBar->ResetSelected(); + } + return true; } - return true; - } - if ( row.heroArmyBar && ( row.heroArmyBar->GetArea() & cursorPos ) - && ( row.garrisonArmyBar ? row.heroArmyBar->QueueEventProcessing( *row.garrisonArmyBar ) : row.heroArmyBar->QueueEventProcessing() ) ) { - if ( row.garrisonArmyBar && row.garrisonArmyBar->isSelected() ) { - row.garrisonArmyBar->ResetSelected(); + if ( row.heroArmyBar && ( row.heroArmyBar->GetArea() & cursorPos ) + && ( row.garrisonArmyBar ? row.heroArmyBar->QueueEventProcessing( *row.garrisonArmyBar ) : row.heroArmyBar->QueueEventProcessing() ) ) { + if ( row.garrisonArmyBar && row.garrisonArmyBar->isSelected() ) { + row.garrisonArmyBar->ResetSelected(); + } + return true; } - return true; - } - if ( row.dwellingsBar && ( row.dwellingsBar->GetArea() & cursorPos ) && row.dwellingsBar->QueueEventProcessing() ) { - if ( row.heroArmyBar && row.heroArmyBar->isSelected() ) { - row.heroArmyBar->ResetSelected(); - } - if ( row.garrisonArmyBar && row.garrisonArmyBar->isSelected() ) { - row.garrisonArmyBar->ResetSelected(); + if ( row.dwellingsBar && ( row.dwellingsBar->GetArea() & cursorPos ) && row.dwellingsBar->QueueEventProcessing() ) { + if ( row.heroArmyBar && row.heroArmyBar->isSelected() ) { + row.heroArmyBar->ResetSelected(); + } + if ( row.garrisonArmyBar && row.garrisonArmyBar->isSelected() ) { + row.garrisonArmyBar->ResetSelected(); + } + return true; } - return true; + + return false; } - return false; -} + void StatsCastlesList::RedrawItem( const CstlRow & row, int32_t dstx, int32_t dsty, bool current ) + { + (void)current; -void StatsCastlesList::RedrawItem( const CstlRow & row, int32_t dstx, int32_t dsty, bool current ) -{ - (void)current; + if ( row.castle == nullptr ) { + // No castle to draw. + return; + } - if ( row.castle == nullptr ) { - // No castle to draw. - return; - } + fheroes2::Display & display = fheroes2::Display::instance(); - fheroes2::Display & display = fheroes2::Display::instance(); + fheroes2::Blit( fheroes2::AGG::GetICN( ICN::OVERVIEW, 11 ), display, dstx, dsty ); + + // base info + Interface::RedrawCastleIcon( *row.castle, dstx + 17, dsty + 19 ); - fheroes2::Blit( fheroes2::AGG::GetICN( ICN::OVERVIEW, 11 ), display, dstx, dsty ); + const Heroes * hero = row.castle->GetHero(); - // base info - Interface::RedrawCastleIcon( *row.castle, dstx + 17, dsty + 19 ); + if ( hero ) { + Interface::RedrawHeroesIcon( *hero, dstx + 82, dsty + 19 ); + const std::string sep = "-"; - const Heroes * hero = row.castle->GetHero(); + const fheroes2::Text text( std::to_string( hero->GetAttack() ) + sep + std::to_string( hero->GetDefense() ) + sep + std::to_string( hero->GetPower() ) + sep + + std::to_string( hero->GetKnowledge() ), + fheroes2::FontType::smallWhite() ); + text.draw( dstx + 104 - text.width() / 2, dsty + 45, display ); + } + else if ( row.castle->GetCaptain().isValid() ) { + const Captain & captain = row.castle->GetCaptain(); + captain.PortraitRedraw( dstx + 82, dsty + 19, PORT_SMALL, display ); + const std::string sep = "-"; - if ( hero ) { - Interface::RedrawHeroesIcon( *hero, dstx + 82, dsty + 19 ); - const std::string sep = "-"; + const fheroes2::Text text( std::to_string( captain.GetAttack() ) + sep + std::to_string( captain.GetDefense() ) + sep + std::to_string( captain.GetPower() ) + + sep + std::to_string( captain.GetKnowledge() ), + fheroes2::FontType::smallWhite() ); + text.draw( dstx + 104 - text.width() / 2, dsty + 45, display ); + } - const fheroes2::Text text( std::to_string( hero->GetAttack() ) + sep + std::to_string( hero->GetDefense() ) + sep + std::to_string( hero->GetPower() ) + sep - + std::to_string( hero->GetKnowledge() ), - fheroes2::FontType::smallWhite() ); - text.draw( dstx + 104 - text.width() / 2, dsty + 45, display ); - } - else if ( row.castle->GetCaptain().isValid() ) { - const Captain & captain = row.castle->GetCaptain(); - captain.PortraitRedraw( dstx + 82, dsty + 19, PORT_SMALL, display ); - const std::string sep = "-"; - - const fheroes2::Text text( std::to_string( captain.GetAttack() ) + sep + std::to_string( captain.GetDefense() ) + sep + std::to_string( captain.GetPower() ) + sep - + std::to_string( captain.GetKnowledge() ), - fheroes2::FontType::smallWhite() ); - text.draw( dstx + 104 - text.width() / 2, dsty + 45, display ); - } + const fheroes2::Text text( row.castle->GetName(), fheroes2::FontType::smallWhite() ); + text.draw( dstx + 72 - text.width() / 2, dsty + 63, display ); - const fheroes2::Text text( row.castle->GetName(), fheroes2::FontType::smallWhite() ); - text.draw( dstx + 72 - text.width() / 2, dsty + 63, display ); + // army info + if ( row.garrisonArmyBar ) { + row.garrisonArmyBar->setRenderingOffset( { dstx + 146, row.heroArmyBar ? dsty : dsty + 20 } ); + row.garrisonArmyBar->Redraw( display ); + } - // army info - if ( row.garrisonArmyBar ) { - row.garrisonArmyBar->setRenderingOffset( { dstx + 146, row.heroArmyBar ? dsty : dsty + 20 } ); - row.garrisonArmyBar->Redraw( display ); - } + if ( row.heroArmyBar ) { + row.heroArmyBar->setRenderingOffset( { dstx + 146, row.garrisonArmyBar ? dsty + 41 : dsty + 20 } ); + row.heroArmyBar->Redraw( display ); + } - if ( row.heroArmyBar ) { - row.heroArmyBar->setRenderingOffset( { dstx + 146, row.garrisonArmyBar ? dsty + 41 : dsty + 20 } ); - row.heroArmyBar->Redraw( display ); + row.dwellingsBar->setRenderingOffset( { dstx + 349, dsty + 15 } ); + row.dwellingsBar->Redraw( display ); } - row.dwellingsBar->setRenderingOffset( { dstx + 349, dsty + 15 } ); - row.dwellingsBar->Redraw( display ); -} - -void StatsCastlesList::RedrawBackground( const fheroes2::Point & dst ) -{ - fheroes2::Display & display = fheroes2::Display::instance(); + void StatsCastlesList::RedrawBackground( const fheroes2::Point & dst ) + { + fheroes2::Display & display = fheroes2::Display::instance(); - const fheroes2::Sprite & header = fheroes2::AGG::GetICN( ICN::OVERVIEW, 7 ); - fheroes2::Copy( header, 0, 0, display, dst.x + 30, dst.y, header.width(), header.height() ); + const fheroes2::Sprite & header = fheroes2::AGG::GetICN( ICN::OVERVIEW, 7 ); + fheroes2::Copy( header, 0, 0, display, dst.x + 30, dst.y, header.width(), header.height() ); - int32_t offsetY = dst.y + 3; + const int32_t offsetY = dst.y + 3; - fheroes2::Text text( _( "Town/Castle" ), fheroes2::FontType::smallWhite() ); - text.draw( dst.x + 105 - text.width() / 2, offsetY, display ); + fheroes2::Text text( _( "Town/Castle" ), fheroes2::FontType::smallWhite() ); + text.draw( dst.x + 105 - text.width() / 2, offsetY, display ); - text.set( _( "Garrison" ), fheroes2::FontType::smallWhite() ); - text.draw( dst.x + 275 - text.width() / 2, offsetY, display ); + text.set( _( "Garrison" ), fheroes2::FontType::smallWhite() ); + text.draw( dst.x + 275 - text.width() / 2, offsetY, display ); - text.set( _( "Available" ), fheroes2::FontType::smallWhite() ); - text.draw( dst.x + 500 - text.width() / 2, offsetY, display ); + text.set( _( "Available" ), fheroes2::FontType::smallWhite() ); + text.draw( dst.x + 500 - text.width() / 2, offsetY, display ); - redrawCommonBackground( dst, VisibleItemCount(), display ); -} + redrawCommonBackground( dst, VisibleItemCount(), display ); + } -void RedrawIncomeInfo( const fheroes2::Point & pt, const Kingdom & myKingdom ) -{ - const Funds income = myKingdom.GetIncome( Kingdom::INCOME_ARTIFACTS | Kingdom::INCOME_HERO_SKILLS | Kingdom::INCOME_CAMPAIGN_BONUS ); - const int32_t offsetY = pt.y + 410; - fheroes2::Display & display = fheroes2::Display::instance(); + void RedrawIncomeInfo( const fheroes2::Point & pt, const Kingdom & myKingdom ) + { + const Funds income = myKingdom.GetIncome( Kingdom::INCOME_ARTIFACTS | Kingdom::INCOME_HERO_SKILLS | Kingdom::INCOME_CAMPAIGN_BONUS ); + const int32_t offsetY = pt.y + 410; + fheroes2::Display & display = fheroes2::Display::instance(); - fheroes2::Text text( CapturedExtInfoString( Resource::WOOD, myKingdom.GetColor(), income ), fheroes2::FontType::smallWhite() ); - text.draw( pt.x + 54 - text.width() / 2, offsetY, display ); + fheroes2::Text text( CapturedExtInfoString( Resource::WOOD, myKingdom.GetColor(), income ), fheroes2::FontType::smallWhite() ); + text.draw( pt.x + 54 - text.width() / 2, offsetY, display ); - text.set( CapturedExtInfoString( Resource::MERCURY, myKingdom.GetColor(), income ), fheroes2::FontType::smallWhite() ); - text.draw( pt.x + 146 - text.width() / 2, offsetY, display ); + text.set( CapturedExtInfoString( Resource::MERCURY, myKingdom.GetColor(), income ), fheroes2::FontType::smallWhite() ); + text.draw( pt.x + 146 - text.width() / 2, offsetY, display ); - text.set( CapturedExtInfoString( Resource::ORE, myKingdom.GetColor(), income ), fheroes2::FontType::smallWhite() ); - text.draw( pt.x + 228 - text.width() / 2, offsetY, display ); + text.set( CapturedExtInfoString( Resource::ORE, myKingdom.GetColor(), income ), fheroes2::FontType::smallWhite() ); + text.draw( pt.x + 228 - text.width() / 2, offsetY, display ); - text.set( CapturedExtInfoString( Resource::SULFUR, myKingdom.GetColor(), income ), fheroes2::FontType::smallWhite() ); - text.draw( pt.x + 294 - text.width() / 2, offsetY, display ); + text.set( CapturedExtInfoString( Resource::SULFUR, myKingdom.GetColor(), income ), fheroes2::FontType::smallWhite() ); + text.draw( pt.x + 294 - text.width() / 2, offsetY, display ); - text.set( CapturedExtInfoString( Resource::CRYSTAL, myKingdom.GetColor(), income ), fheroes2::FontType::smallWhite() ); - text.draw( pt.x + 360 - text.width() / 2, offsetY, display ); + text.set( CapturedExtInfoString( Resource::CRYSTAL, myKingdom.GetColor(), income ), fheroes2::FontType::smallWhite() ); + text.draw( pt.x + 360 - text.width() / 2, offsetY, display ); - text.set( CapturedExtInfoString( Resource::GEMS, myKingdom.GetColor(), income ), fheroes2::FontType::smallWhite() ); - text.draw( pt.x + 428 - text.width() / 2, offsetY, display ); + text.set( CapturedExtInfoString( Resource::GEMS, myKingdom.GetColor(), income ), fheroes2::FontType::smallWhite() ); + text.draw( pt.x + 428 - text.width() / 2, offsetY, display ); - text.set( CapturedExtInfoString( Resource::GOLD, myKingdom.GetColor(), income ), fheroes2::FontType::smallWhite() ); - text.draw( pt.x + 494 - text.width() / 2, offsetY, display ); -} + text.set( CapturedExtInfoString( Resource::GOLD, myKingdom.GetColor(), income ), fheroes2::FontType::smallWhite() ); + text.draw( pt.x + 494 - text.width() / 2, offsetY, display ); + } -void RedrawFundsInfo( const fheroes2::Point & pt, const Kingdom & myKingdom ) -{ - const Funds & funds = myKingdom.GetFunds(); - int32_t offsetY = pt.y + 450; + void RedrawFundsInfo( const fheroes2::Point & pt, const Kingdom & myKingdom ) + { + const Funds & funds = myKingdom.GetFunds(); + int32_t offsetY = pt.y + 450; - fheroes2::Display & display = fheroes2::Display::instance(); - fheroes2::Blit( fheroes2::AGG::GetICN( ICN::OVERBACK, 0 ), 4, 422, display, pt.x + 4, pt.y + 422, 530, 56 ); + fheroes2::Display & display = fheroes2::Display::instance(); + fheroes2::Blit( fheroes2::AGG::GetICN( ICN::OVERBACK, 0 ), 4, 422, display, pt.x + 4, pt.y + 422, 530, 56 ); - fheroes2::Text text( std::to_string( funds.wood ), fheroes2::FontType::smallWhite() ); - text.draw( pt.x + 56 - text.width() / 2, offsetY, display ); + fheroes2::Text text( std::to_string( funds.wood ), fheroes2::FontType::smallWhite() ); + text.draw( pt.x + 56 - text.width() / 2, offsetY, display ); - text.set( std::to_string( funds.mercury ), fheroes2::FontType::smallWhite() ); - text.draw( pt.x + 146 - text.width() / 2, offsetY, display ); + text.set( std::to_string( funds.mercury ), fheroes2::FontType::smallWhite() ); + text.draw( pt.x + 146 - text.width() / 2, offsetY, display ); - text.set( std::to_string( funds.ore ), fheroes2::FontType::smallWhite() ); - text.draw( pt.x + 226 - text.width() / 2, offsetY, display ); + text.set( std::to_string( funds.ore ), fheroes2::FontType::smallWhite() ); + text.draw( pt.x + 226 - text.width() / 2, offsetY, display ); - text.set( std::to_string( funds.sulfur ), fheroes2::FontType::smallWhite() ); - text.draw( pt.x + 294 - text.width() / 2, offsetY, display ); + text.set( std::to_string( funds.sulfur ), fheroes2::FontType::smallWhite() ); + text.draw( pt.x + 294 - text.width() / 2, offsetY, display ); - text.set( std::to_string( funds.crystal ), fheroes2::FontType::smallWhite() ); - text.draw( pt.x + 362 - text.width() / 2, offsetY, display ); + text.set( std::to_string( funds.crystal ), fheroes2::FontType::smallWhite() ); + text.draw( pt.x + 362 - text.width() / 2, offsetY, display ); - text.set( std::to_string( funds.gems ), fheroes2::FontType::smallWhite() ); - text.draw( pt.x + 428 - text.width() / 2, offsetY, display ); + text.set( std::to_string( funds.gems ), fheroes2::FontType::smallWhite() ); + text.draw( pt.x + 428 - text.width() / 2, offsetY, display ); - text.set( std::to_string( funds.gold ), fheroes2::FontType::smallWhite() ); - text.draw( pt.x + 496 - text.width() / 2, offsetY, display ); + text.set( std::to_string( funds.gold ), fheroes2::FontType::smallWhite() ); + text.draw( pt.x + 496 - text.width() / 2, offsetY, display ); - offsetY += 14; - text.set( _( "Gold Per Day:" ) + std::string( " " ) + std::to_string( myKingdom.GetIncome().Get( Resource::GOLD ) ), fheroes2::FontType::smallWhite() ); - text.draw( pt.x + 180, offsetY, display ); + offsetY += 14; + text.set( _( "Gold Per Day:" ) + std::string( " " ) + std::to_string( myKingdom.GetIncome().Get( Resource::GOLD ) ), fheroes2::FontType::smallWhite() ); + text.draw( pt.x + 180, offsetY, display ); - std::string msg = _( "Day: %{day}" ); - StringReplace( msg, "%{day}", world.GetDay() ); - text.set( msg, fheroes2::FontType::smallWhite() ); - text.draw( pt.x + 360, offsetY, display ); + std::string msg = _( "Day: %{day}" ); + StringReplace( msg, "%{day}", world.GetDay() ); + text.set( msg, fheroes2::FontType::smallWhite() ); + text.draw( pt.x + 360, offsetY, display ); - // Show Lighthouse count - const uint32_t lighthouseCount = world.CountCapturedObject( MP2::OBJ_LIGHTHOUSE, myKingdom.GetColor() ); - text.set( std::to_string( lighthouseCount ), fheroes2::FontType::smallWhite() ); - text.draw( pt.x + 105, offsetY, display ); + // Show Lighthouse count + const uint32_t lighthouseCount = world.CountCapturedObject( MP2::OBJ_LIGHTHOUSE, myKingdom.GetColor() ); + text.set( std::to_string( lighthouseCount ), fheroes2::FontType::smallWhite() ); + text.draw( pt.x + 105, offsetY, display ); - const fheroes2::Sprite & lighthouse = fheroes2::AGG::GetICN( ICN::OVERVIEW, 14 ); - fheroes2::Blit( lighthouse, 0, 0, display, pt.x + 100 - lighthouse.width(), pt.y + 459, lighthouse.width(), lighthouse.height() ); + const fheroes2::Sprite & lighthouse = fheroes2::AGG::GetICN( ICN::OVERVIEW, 14 ); + fheroes2::Blit( lighthouse, 0, 0, display, pt.x + 100 - lighthouse.width(), pt.y + 459, lighthouse.width(), lighthouse.height() ); + } } void Kingdom::openOverviewDialog() diff --git a/src/fheroes2/maps/map_format_info.cpp b/src/fheroes2/maps/map_format_info.cpp index 567d314bc8c..049409fd4fd 100644 --- a/src/fheroes2/maps/map_format_info.cpp +++ b/src/fheroes2/maps/map_format_info.cpp @@ -27,6 +27,41 @@ #include "serialize.h" #include "zzlib.h" +namespace Maps::Map_Format +{ + // The following operators are used only inside this module, but they cannot be declared in an anonymous namespace due to the way ADL works + + StreamBase & operator<<( StreamBase & msg, const TileObjectInfo & object ); + StreamBase & operator>>( StreamBase & msg, TileObjectInfo & object ); + + StreamBase & operator<<( StreamBase & msg, const TileInfo & tile ); + StreamBase & operator>>( StreamBase & msg, TileInfo & tile ); + + StreamBase & operator<<( StreamBase & msg, const DailyEvent & eventInfo ); + StreamBase & operator>>( StreamBase & msg, DailyEvent & eventInfo ); + + StreamBase & operator<<( StreamBase & msg, const StandardObjectMetadata & metadata ); + StreamBase & operator>>( StreamBase & msg, StandardObjectMetadata & metadata ); + + StreamBase & operator<<( StreamBase & msg, const CastleMetadata & metadata ); + StreamBase & operator>>( StreamBase & msg, CastleMetadata & metadata ); + + StreamBase & operator<<( StreamBase & msg, const HeroMetadata & metadata ); + StreamBase & operator>>( StreamBase & msg, HeroMetadata & metadata ); + + StreamBase & operator<<( StreamBase & msg, const SphinxMetadata & metadata ); + StreamBase & operator>>( StreamBase & msg, SphinxMetadata & metadata ); + + StreamBase & operator<<( StreamBase & msg, const SignMetadata & metadata ); + StreamBase & operator>>( StreamBase & msg, SignMetadata & metadata ); + + StreamBase & operator<<( StreamBase & msg, const AdventureMapEventMetadata & metadata ); + StreamBase & operator>>( StreamBase & msg, AdventureMapEventMetadata & metadata ); + + StreamBase & operator<<( StreamBase & msg, const ShrineMetadata & metadata ); + StreamBase & operator>>( StreamBase & msg, ShrineMetadata & metadata ); +} + namespace { const std::array magicWord{ 'h', '2', 'm', 'a', 'p', '\0' }; @@ -125,6 +160,117 @@ namespace } } } + + bool saveToStream( StreamBase & msg, const Maps::Map_Format::BaseMapFormat & map ) + { + using LanguageUnderlyingType = std::underlying_type_t; + + msg << currentSupportedVersion << map.isCampaign << map.difficulty << map.availablePlayerColors << map.humanPlayerColors << map.computerPlayerColors + << map.alliances << map.playerRace << map.victoryConditionType << map.isVictoryConditionApplicableForAI << map.allowNormalVictory + << map.victoryConditionMetadata << map.lossConditionType << map.lossConditionMetadata << map.size << static_cast( map.language ) + << map.name << map.description; + + return !msg.fail(); + } + + bool loadFromStream( StreamBase & msg, Maps::Map_Format::BaseMapFormat & map ) + { + msg >> map.version; + if ( map.version < minimumSupportedVersion || map.version > currentSupportedVersion ) { + return false; + } + + msg >> map.isCampaign >> map.difficulty >> map.availablePlayerColors >> map.humanPlayerColors >> map.computerPlayerColors >> map.alliances >> map.playerRace + >> map.victoryConditionType >> map.isVictoryConditionApplicableForAI >> map.allowNormalVictory >> map.victoryConditionMetadata >> map.lossConditionType + >> map.lossConditionMetadata >> map.size; + + if ( map.size <= 0 ) { + // This is not a correct map size. + return false; + } + + using LanguageUnderlyingType = std::underlying_type_t; + static_assert( std::is_same_v, "Type of language has been changed, check the logic below" ); + LanguageUnderlyingType language; + + msg >> language; + map.language = static_cast( language ); + + msg >> map.name >> map.description; + + return !msg.fail(); + } + + bool saveToStream( StreamBase & msg, const Maps::Map_Format::MapFormat & map ) + { + // Only the base map information is not encoded. + // The rest of data must be compressed to prevent manual corruption of the file. + if ( !saveToStream( msg, static_cast( map ) ) ) { + return false; + } + + StreamBuf compressed; + compressed.setbigendian( true ); + + compressed << map.additionalInfo << map.tiles << map.dailyEvents << map.rumors << map.standardMetadata << map.castleMetadata << map.heroMetadata + << map.sphinxMetadata << map.signMetadata << map.adventureMapEventMetadata << map.shrineMetadata; + + const std::vector temp = Compression::compressData( compressed.data(), compressed.size() ); + + msg.putRaw( temp.data(), temp.size() ); + + return !msg.fail(); + } + + bool loadFromStream( StreamBase & msg, Maps::Map_Format::MapFormat & map ) + { + // TODO: verify the correctness of metadata. + if ( !loadFromStream( msg, static_cast( map ) ) ) { + map = {}; + return false; + } + + StreamBuf decompressed; + decompressed.setbigendian( true ); + + { + std::vector temp = msg.getRaw(); + if ( temp.empty() ) { + // This is a corrupted file. + map = {}; + return false; + } + + const std::vector decompressedData = Compression::decompressData( temp.data(), temp.size() ); + if ( decompressedData.empty() ) { + // This is a corrupted file. + map = {}; + return false; + } + + // Let's try to free up some memory + temp = std::vector{}; + + decompressed.putRaw( decompressedData.data(), decompressedData.size() ); + } + + decompressed >> map.additionalInfo >> map.tiles; + + if ( map.tiles.size() != static_cast( map.size ) * map.size ) { + map = {}; + return false; + } + + decompressed >> map.dailyEvents >> map.rumors >> map.standardMetadata >> map.castleMetadata >> map.heroMetadata >> map.sphinxMetadata >> map.signMetadata + >> map.adventureMapEventMetadata >> map.shrineMetadata; + + static_assert( minimumSupportedVersion <= 2, "Remove the following function call." ); + convertFromV2ToV3( map ); + convertFromV3ToV4( map ); + convertFromV4ToV5( map ); + + return !msg.fail(); + } } namespace Maps::Map_Format @@ -253,117 +399,6 @@ namespace Maps::Map_Format return msg >> metadata.allowedSpells; } - bool saveToStream( StreamBase & msg, const BaseMapFormat & map ) - { - using LanguageUnderlyingType = std::underlying_type_t; - - msg << currentSupportedVersion << map.isCampaign << map.difficulty << map.availablePlayerColors << map.humanPlayerColors << map.computerPlayerColors - << map.alliances << map.playerRace << map.victoryConditionType << map.isVictoryConditionApplicableForAI << map.allowNormalVictory - << map.victoryConditionMetadata << map.lossConditionType << map.lossConditionMetadata << map.size << static_cast( map.language ) - << map.name << map.description; - - return !msg.fail(); - } - - bool loadFromStream( StreamBase & msg, BaseMapFormat & map ) - { - msg >> map.version; - if ( map.version < minimumSupportedVersion || map.version > currentSupportedVersion ) { - return false; - } - - msg >> map.isCampaign >> map.difficulty >> map.availablePlayerColors >> map.humanPlayerColors >> map.computerPlayerColors >> map.alliances >> map.playerRace - >> map.victoryConditionType >> map.isVictoryConditionApplicableForAI >> map.allowNormalVictory >> map.victoryConditionMetadata >> map.lossConditionType - >> map.lossConditionMetadata >> map.size; - - if ( map.size <= 0 ) { - // This is not a correct map size. - return false; - } - - using LanguageUnderlyingType = std::underlying_type_t; - static_assert( std::is_same_v, "Type of language has been changed, check the logic below" ); - LanguageUnderlyingType language; - - msg >> language; - map.language = static_cast( language ); - - msg >> map.name >> map.description; - - return !msg.fail(); - } - - bool saveToStream( StreamBase & msg, const MapFormat & map ) - { - // Only the base map information is not encoded. - // The rest of data must be compressed to prevent manual corruption of the file. - if ( !saveToStream( msg, static_cast( map ) ) ) { - return false; - } - - StreamBuf compressed; - compressed.setbigendian( true ); - - compressed << map.additionalInfo << map.tiles << map.dailyEvents << map.rumors << map.standardMetadata << map.castleMetadata << map.heroMetadata - << map.sphinxMetadata << map.signMetadata << map.adventureMapEventMetadata << map.shrineMetadata; - - const std::vector temp = Compression::compressData( compressed.data(), compressed.size() ); - - msg.putRaw( temp.data(), temp.size() ); - - return !msg.fail(); - } - - bool loadFromStream( StreamBase & msg, MapFormat & map ) - { - // TODO: verify the correctness of metadata. - if ( !loadFromStream( msg, static_cast( map ) ) ) { - map = {}; - return false; - } - - StreamBuf decompressed; - decompressed.setbigendian( true ); - - { - std::vector temp = msg.getRaw(); - if ( temp.empty() ) { - // This is a corrupted file. - map = {}; - return false; - } - - const std::vector decompressedData = Compression::decompressData( temp.data(), temp.size() ); - if ( decompressedData.empty() ) { - // This is a corrupted file. - map = {}; - return false; - } - - // Let's try to free up some memory - temp = std::vector{}; - - decompressed.putRaw( decompressedData.data(), decompressedData.size() ); - } - - decompressed >> map.additionalInfo >> map.tiles; - - if ( map.tiles.size() != static_cast( map.size ) * map.size ) { - map = {}; - return false; - } - - decompressed >> map.dailyEvents >> map.rumors >> map.standardMetadata >> map.castleMetadata >> map.heroMetadata >> map.sphinxMetadata >> map.signMetadata - >> map.adventureMapEventMetadata >> map.shrineMetadata; - - static_assert( minimumSupportedVersion <= 2, "Remove the following function call." ); - convertFromV2ToV3( map ); - convertFromV3ToV4( map ); - convertFromV4ToV5( map ); - - return !msg.fail(); - } - bool loadBaseMap( const std::string & path, BaseMapFormat & map ) { if ( path.empty() ) { diff --git a/src/fheroes2/resource/resource.cpp b/src/fheroes2/resource/resource.cpp index 49ef7b9382e..b638940077f 100644 --- a/src/fheroes2/resource/resource.cpp +++ b/src/fheroes2/resource/resource.cpp @@ -41,6 +41,20 @@ #include "translations.h" #include "ui_text.h" +namespace +{ + void RedrawResourceSprite( const fheroes2::Image & sf, const fheroes2::Point & pos, int32_t count, int32_t width, int32_t offset, int32_t value ) + { + fheroes2::Display & display = fheroes2::Display::instance(); + + const fheroes2::Point dst_pt( pos.x + width / 2 + count * width, pos.y + offset ); + fheroes2::Blit( sf, display, dst_pt.x - sf.width() / 2, dst_pt.y - sf.height() ); + + const fheroes2::Text text{ std::to_string( value ), fheroes2::FontType::smallWhite() }; + text.draw( dst_pt.x - text.width() / 2, dst_pt.y + 4, display ); + } +} + Funds::Funds( const int type, const uint32_t count ) { if ( count == 0 ) { @@ -607,17 +621,6 @@ void Resource::BoxSprite::SetPos( int32_t px, int32_t py ) y = py; } -void RedrawResourceSprite( const fheroes2::Image & sf, const fheroes2::Point & pos, int32_t count, int32_t width, int32_t offset, int32_t value ) -{ - fheroes2::Display & display = fheroes2::Display::instance(); - - const fheroes2::Point dst_pt( pos.x + width / 2 + count * width, pos.y + offset ); - fheroes2::Blit( sf, display, dst_pt.x - sf.width() / 2, dst_pt.y - sf.height() ); - - const fheroes2::Text text{ std::to_string( value ), fheroes2::FontType::smallWhite() }; - text.draw( dst_pt.x - text.width() / 2, dst_pt.y + 4, display ); -} - void Resource::BoxSprite::Redraw() const { std::vector> valueVsSprite; diff --git a/src/fheroes2/system/players.h b/src/fheroes2/system/players.h index bfee2d57359..15f5651564f 100644 --- a/src/fheroes2/system/players.h +++ b/src/fheroes2/system/players.h @@ -91,6 +91,9 @@ struct Focus : std::pair } }; +StreamBase & operator<<( StreamBase &, const Focus & ); +StreamBase & operator>>( StreamBase &, Focus & ); + struct Control { virtual int GetControl() const = 0;