2023-09-04 18:01:44 +03:00
/*
* RenderHandler . cpp , part of VCMI engine
*
* Authors : listed in file AUTHORS in main folder
*
* License : GNU General Public License v2 .0 or later
* Full text of license available in license . txt file , in main folder
*
*/
# include "StdInc.h"
# include "RenderHandler.h"
# include "SDLImage.h"
2025-01-15 17:05:45 +00:00
# include "ScalableImage.h"
2024-09-24 10:41:39 +00:00
# include "FontChain.h"
2023-09-04 18:01:44 +03:00
2024-08-03 20:14:51 +00:00
# include "../gui/CGuiHandler.h"
2025-01-29 10:03:58 +00:00
# include "../render/AssetGenerator.h"
2024-05-22 11:28:13 +00:00
# include "../render/CAnimation.h"
2025-01-16 15:09:03 +00:00
# include "../render/CanvasImage.h"
2024-05-22 11:28:13 +00:00
# include "../render/CDefFile.h"
2024-07-25 10:38:48 +00:00
# include "../render/Colors.h"
# include "../render/ColorFilter.h"
2024-08-03 20:14:51 +00:00
# include "../render/IScreenHandler.h"
2025-01-26 14:38:17 +00:00
# include "../../lib/CConfigHandler.h"
2025-01-15 17:05:45 +00:00
# include "../../lib/CThreadHelper.h"
2025-01-28 20:21:33 +00:00
# include "../../lib/ExceptionsCommon.h"
2024-09-13 12:26:31 +00:00
# include "../../lib/VCMIDirs.h"
2025-01-24 15:36:13 +00:00
# include "../../lib/constants/StringConstants.h"
2025-01-26 14:38:17 +00:00
# include "../../lib/entities/building/CBuilding.h"
# include "../../lib/entities/faction/CTown.h"
# include "../../lib/entities/faction/CTownHandler.h"
# include "../../lib/filesystem/Filesystem.h"
# include "../../lib/json/JsonUtils.h"
2024-05-22 11:28:13 +00:00
2024-06-05 14:31:37 +00:00
# include <vcmi/ArtifactService.h>
# include <vcmi/CreatureService.h>
# include <vcmi/Entity.h>
# include <vcmi/FactionService.h>
# include <vcmi/HeroTypeService.h>
# include <vcmi/Services.h>
# include <vcmi/SkillService.h>
# include <vcmi/spells/Service.h>
2025-01-29 10:03:58 +00:00
RenderHandler : : RenderHandler ( )
: assetGenerator ( std : : make_unique < AssetGenerator > ( ) )
{
}
RenderHandler : : ~ RenderHandler ( ) = default ;
2024-05-22 11:28:13 +00:00
std : : shared_ptr < CDefFile > RenderHandler : : getAnimationFile ( const AnimationPath & path )
{
2024-06-02 18:33:51 +00:00
AnimationPath actualPath = boost : : starts_with ( path . getName ( ) , " SPRITES " ) ? path : path . addPrefix ( " SPRITES/ " ) ;
auto it = animationFiles . find ( actualPath ) ;
2024-05-22 11:28:13 +00:00
if ( it ! = animationFiles . end ( ) )
return it - > second ;
2024-06-02 18:33:51 +00:00
if ( ! CResourceHandler : : get ( ) - > existsResource ( actualPath ) )
{
animationFiles [ actualPath ] = nullptr ;
return nullptr ;
}
auto result = std : : make_shared < CDefFile > ( actualPath ) ;
2024-05-22 11:28:13 +00:00
2024-06-02 18:33:51 +00:00
animationFiles [ actualPath ] = result ;
2024-05-22 11:28:13 +00:00
return result ;
}
2025-01-15 17:05:45 +00:00
void RenderHandler : : initFromJson ( AnimationLayoutMap & source , const JsonNode & config , EImageBlitMode mode )
2024-06-02 18:33:51 +00:00
{
std : : string basepath ;
basepath = config [ " basepath " ] . String ( ) ;
JsonNode base ;
base [ " margins " ] = config [ " margins " ] ;
base [ " width " ] = config [ " width " ] ;
base [ " height " ] = config [ " height " ] ;
for ( const JsonNode & group : config [ " sequences " ] . Vector ( ) )
{
size_t groupID = group [ " group " ] . Integer ( ) ; //TODO: string-to-value conversion("moving" -> MOVING)
source [ groupID ] . clear ( ) ;
for ( const JsonNode & frame : group [ " frames " ] . Vector ( ) )
{
2024-07-16 20:14:49 +00:00
JsonNode toAdd = frame ;
2024-06-02 18:33:51 +00:00
JsonUtils : : inherit ( toAdd , base ) ;
toAdd [ " file " ] . String ( ) = basepath + frame . String ( ) ;
2025-01-15 17:05:45 +00:00
source [ groupID ] . emplace_back ( toAdd , mode ) ;
2024-06-02 18:33:51 +00:00
}
}
for ( const JsonNode & node : config [ " images " ] . Vector ( ) )
{
size_t group = node [ " group " ] . Integer ( ) ;
size_t frame = node [ " frame " ] . Integer ( ) ;
if ( source [ group ] . size ( ) < = frame )
source [ group ] . resize ( frame + 1 ) ;
2024-07-16 20:14:49 +00:00
JsonNode toAdd = node ;
2024-06-02 18:33:51 +00:00
JsonUtils : : inherit ( toAdd , base ) ;
2024-11-24 19:26:06 +00:00
if ( toAdd . Struct ( ) . count ( " file " ) )
toAdd [ " file " ] . String ( ) = basepath + node [ " file " ] . String ( ) ;
if ( toAdd . Struct ( ) . count ( " defFile " ) )
toAdd [ " defFile " ] . String ( ) = basepath + node [ " defFile " ] . String ( ) ;
2025-01-15 17:05:45 +00:00
source [ group ] [ frame ] = ImageLocator ( toAdd , mode ) ;
2024-06-02 18:33:51 +00:00
}
}
2025-01-15 17:05:45 +00:00
RenderHandler : : AnimationLayoutMap & RenderHandler : : getAnimationLayout ( const AnimationPath & path , int scalingFactor , EImageBlitMode mode )
2024-05-22 11:28:13 +00:00
{
2025-01-15 17:05:45 +00:00
static constexpr std : : array scaledSpritesPath = {
" " , // 0x
" SPRITES/ " ,
" SPRITES2X/ " ,
" SPRITES3X/ " ,
" SPRITES4X/ " ,
} ;
std : : string pathString = path . getName ( ) ;
if ( boost : : starts_with ( pathString , " SPRITES/ " ) )
pathString = pathString . substr ( std : : string ( " SPRITES/ " ) . length ( ) ) ;
AnimationPath actualPath = AnimationPath : : builtin ( scaledSpritesPath . at ( scalingFactor ) + pathString ) ;
2024-06-05 14:31:37 +00:00
auto it = animationLayouts . find ( actualPath ) ;
2024-05-22 11:28:13 +00:00
2024-06-02 18:33:51 +00:00
if ( it ! = animationLayouts . end ( ) )
2024-05-22 11:28:13 +00:00
return it - > second ;
2024-06-02 18:33:51 +00:00
AnimationLayoutMap result ;
2024-05-22 11:28:13 +00:00
2024-06-05 14:31:37 +00:00
auto defFile = getAnimationFile ( actualPath ) ;
2024-06-02 18:33:51 +00:00
if ( defFile )
{
const std : : map < size_t , size_t > defEntries = defFile - > getEntries ( ) ;
2024-06-05 20:16:12 +00:00
for ( const auto & defEntry : defEntries )
2024-06-02 18:33:51 +00:00
result [ defEntry . first ] . resize ( defEntry . second ) ;
}
2024-06-05 14:31:37 +00:00
auto jsonResource = actualPath . toType < EResType : : JSON > ( ) ;
auto configList = CResourceHandler : : get ( ) - > getResourcesWithName ( jsonResource ) ;
2024-06-02 18:33:51 +00:00
for ( auto & loader : configList )
{
2025-01-28 20:21:33 +00:00
try {
auto stream = loader - > load ( jsonResource ) ;
std : : unique_ptr < ui8 [ ] > textData ( new ui8 [ stream - > getSize ( ) ] ) ;
stream - > read ( textData . get ( ) , stream - > getSize ( ) ) ;
2024-06-02 18:33:51 +00:00
2025-01-28 20:21:33 +00:00
const JsonNode config ( reinterpret_cast < const std : : byte * > ( textData . get ( ) ) , stream - > getSize ( ) , path . getOriginalName ( ) ) ;
2024-06-02 18:33:51 +00:00
2025-01-28 20:21:33 +00:00
initFromJson ( result , config , mode ) ;
}
catch ( const DataLoadingException & e )
{
// FIXME: sometimes triggered by generated animation assets, e.g. lava/water tiles
logGlobal - > error ( " Failed to load animation file! Reason: %s " , e . what ( ) ) ;
}
2024-06-02 18:33:51 +00:00
}
2024-06-05 14:31:37 +00:00
animationLayouts [ actualPath ] = result ;
return animationLayouts [ actualPath ] ;
2024-05-22 11:28:13 +00:00
}
2024-07-21 21:11:02 +00:00
int RenderHandler : : getScalingFactor ( ) const
{
2024-08-03 20:14:51 +00:00
return GH . screenHandler ( ) . getScalingFactor ( ) ;
2024-07-21 21:11:02 +00:00
}
2025-01-17 12:17:29 +00:00
ImageLocator RenderHandler : : getLocatorForAnimationFrame ( const AnimationPath & path , int frame , int group , int scaling , EImageBlitMode mode )
2024-06-05 20:16:12 +00:00
{
2025-01-17 12:17:29 +00:00
const auto & layout = getAnimationLayout ( path , scaling , mode ) ;
2024-06-05 14:31:37 +00:00
if ( ! layout . count ( group ) )
2025-01-17 12:17:29 +00:00
return ImageLocator ( ) ;
2024-05-22 11:28:13 +00:00
2024-06-05 14:31:37 +00:00
if ( frame > = layout . at ( group ) . size ( ) )
2025-01-17 12:17:29 +00:00
return ImageLocator ( ) ;
2024-06-05 14:31:37 +00:00
2024-07-16 19:51:00 +00:00
const auto & locator = layout . at ( group ) . at ( frame ) ;
2024-07-25 10:38:48 +00:00
if ( locator . image | | locator . defFile )
return locator ;
2025-01-15 17:05:45 +00:00
return ImageLocator ( path , frame , group , mode ) ;
2024-07-25 10:38:48 +00:00
}
2025-01-15 17:05:45 +00:00
std : : shared_ptr < ScalableImageShared > RenderHandler : : loadImageImpl ( const ImageLocator & locator )
2024-07-25 10:38:48 +00:00
{
auto it = imageFiles . find ( locator ) ;
if ( it ! = imageFiles . end ( ) )
return it - > second ;
2025-01-15 17:05:45 +00:00
auto sdlImage = loadImageFromFileUncached ( locator ) ;
auto scaledImage = std : : make_shared < ScalableImageShared > ( locator , sdlImage ) ;
2024-07-25 10:38:48 +00:00
2025-01-15 17:05:45 +00:00
storeCachedImage ( locator , scaledImage ) ;
2024-07-25 10:38:48 +00:00
return scaledImage ;
}
2025-01-29 10:03:58 +00:00
std : : shared_ptr < ISharedImage > RenderHandler : : loadImageFromFileUncached ( const ImageLocator & locator )
2024-07-25 10:38:48 +00:00
{
2024-11-27 23:07:45 +01:00
if ( locator . image )
2024-06-05 14:31:37 +00:00
{
2025-01-29 10:03:58 +00:00
auto imagePath = * locator . image ;
auto imagePathSprites = imagePath . addPrefix ( " SPRITES/ " ) ;
auto imagePathData = imagePath . addPrefix ( " DATA/ " ) ;
if ( CResourceHandler : : get ( ) - > existsResource ( imagePathSprites ) )
return std : : make_shared < SDLImageShared > ( imagePathSprites ) ;
if ( CResourceHandler : : get ( ) - > existsResource ( imagePathData ) )
return std : : make_shared < SDLImageShared > ( imagePathData ) ;
if ( CResourceHandler : : get ( ) - > existsResource ( imagePath ) )
return std : : make_shared < SDLImageShared > ( imagePath ) ;
auto generated = assetGenerator - > generateImage ( imagePath ) ;
if ( generated )
return generated ;
2025-02-03 11:44:21 +00:00
logGlobal - > error ( " Failed to load image %s " , locator . image - > getOriginalName ( ) ) ;
2025-01-29 10:03:58 +00:00
return std : : make_shared < SDLImageShared > ( ImagePath : : builtin ( " DEFAULT " ) ) ;
2024-06-05 20:16:12 +00:00
}
2024-07-25 10:38:48 +00:00
2024-12-15 21:32:23 +01:00
if ( locator . defFile )
2024-06-05 20:16:12 +00:00
{
2024-07-25 10:38:48 +00:00
auto defFile = getAnimationFile ( * locator . defFile ) ;
2024-11-27 23:07:45 +01:00
if ( defFile - > hasFrame ( locator . defFrame , locator . defGroup ) )
2025-01-15 17:05:45 +00:00
return std : : make_shared < SDLImageShared > ( defFile . get ( ) , locator . defFrame , locator . defGroup ) ;
2024-11-27 23:07:45 +01:00
else
{
logGlobal - > error ( " Frame %d in group %d not found in file: %s " ,
locator . defFrame , locator . defGroup , locator . defFile - > getName ( ) . c_str ( ) ) ;
2025-01-15 17:05:45 +00:00
return std : : make_shared < SDLImageShared > ( ImagePath : : builtin ( " DEFAULT " ) ) ;
2024-11-27 23:07:45 +01:00
}
2024-06-05 14:31:37 +00:00
}
2024-07-25 10:38:48 +00:00
throw std : : runtime_error ( " Invalid image locator received! " ) ;
2024-06-05 20:16:12 +00:00
}
2025-01-15 17:05:45 +00:00
void RenderHandler : : storeCachedImage ( const ImageLocator & locator , std : : shared_ptr < ScalableImageShared > image )
2024-09-13 12:26:31 +00:00
{
imageFiles [ locator ] = image ;
}
2025-01-17 12:17:29 +00:00
std : : shared_ptr < SDLImageShared > RenderHandler : : loadScaledImage ( const ImageLocator & locator )
2024-06-05 20:16:12 +00:00
{
2025-01-15 17:05:45 +00:00
static constexpr std : : array scaledDataPath = {
" " , // 0x
" DATA/ " ,
" DATA2X/ " ,
" DATA3X/ " ,
" DATA4X/ " ,
} ;
static constexpr std : : array scaledSpritesPath = {
" " , // 0x
" SPRITES/ " ,
" SPRITES2X/ " ,
" SPRITES3X/ " ,
" SPRITES4X/ " ,
} ;
2024-07-25 10:38:48 +00:00
2025-01-17 12:17:29 +00:00
ImagePath pathToLoad ;
if ( locator . defFile )
2025-01-15 17:05:45 +00:00
{
2025-01-17 12:17:29 +00:00
auto remappedLocator = getLocatorForAnimationFrame ( * locator . defFile , locator . defFrame , locator . defGroup , locator . scalingFactor , locator . layer ) ;
// we expect that .def's are only used for 1x data, upscaled assets should use standalone images
if ( ! remappedLocator . image )
return nullptr ;
2024-06-05 20:16:12 +00:00
2025-01-17 12:17:29 +00:00
pathToLoad = * remappedLocator . image ;
}
2024-06-05 14:31:37 +00:00
2025-01-21 16:36:57 +00:00
if ( locator . image )
pathToLoad = * locator . image ;
2024-06-05 20:16:12 +00:00
2025-01-21 16:36:57 +00:00
if ( pathToLoad . empty ( ) )
return nullptr ;
2024-06-05 20:16:12 +00:00
2025-01-17 12:17:29 +00:00
std : : string imagePathString = pathToLoad . getName ( ) ;
2024-07-25 10:38:48 +00:00
2025-02-03 11:44:21 +00:00
if ( locator . layer = = EImageBlitMode : : ONLY_FLAG_COLOR | | locator . layer = = EImageBlitMode : : ONLY_SELECTION )
2025-01-17 12:17:29 +00:00
imagePathString + = " -OVERLAY " ;
2025-02-03 11:44:21 +00:00
if ( locator . layer = = EImageBlitMode : : ONLY_SHADOW_HIDE_SELECTION | | locator . layer = = EImageBlitMode : : ONLY_SHADOW_HIDE_FLAG_COLOR )
2025-01-17 12:17:29 +00:00
imagePathString + = " -SHADOW " ;
2025-01-24 15:36:13 +00:00
if ( locator . playerColored . isValidPlayer ( ) )
imagePathString + = " - " + boost : : to_upper_copy ( GameConstants : : PLAYER_COLOR_NAMES [ locator . playerColored . getNum ( ) ] ) ;
if ( locator . playerColored = = PlayerColor : : NEUTRAL )
imagePathString + = " -NEUTRAL " ;
2024-07-25 10:38:48 +00:00
2025-01-17 12:17:29 +00:00
auto imagePath = ImagePath : : builtin ( imagePathString ) ;
auto imagePathSprites = ImagePath : : builtin ( imagePathString ) . addPrefix ( scaledSpritesPath . at ( locator . scalingFactor ) ) ;
auto imagePathData = ImagePath : : builtin ( imagePathString ) . addPrefix ( scaledDataPath . at ( locator . scalingFactor ) ) ;
2024-07-25 10:38:48 +00:00
2025-01-17 12:17:29 +00:00
if ( CResourceHandler : : get ( ) - > existsResource ( imagePathSprites ) )
return std : : make_shared < SDLImageShared > ( imagePathSprites ) ;
if ( CResourceHandler : : get ( ) - > existsResource ( imagePathData ) )
return std : : make_shared < SDLImageShared > ( imagePathData ) ;
if ( CResourceHandler : : get ( ) - > existsResource ( imagePath ) )
return std : : make_shared < SDLImageShared > ( imagePath ) ;
2024-07-25 10:38:48 +00:00
2025-01-15 17:05:45 +00:00
return nullptr ;
2024-07-25 10:38:48 +00:00
}
2025-01-15 17:05:45 +00:00
std : : shared_ptr < IImage > RenderHandler : : loadImage ( const ImageLocator & locator )
2024-06-05 20:16:12 +00:00
{
2024-11-15 19:09:34 +01:00
ImageLocator adjustedLocator = locator ;
2024-11-17 17:09:13 +01:00
2025-01-15 17:05:45 +00:00
std : : shared_ptr < ScalableImageInstance > result ;
2024-09-12 14:26:05 +00:00
2024-11-15 19:09:34 +01:00
if ( adjustedLocator . scalingFactor = = 0 )
2024-09-12 14:26:05 +00:00
{
2024-11-15 19:09:34 +01:00
auto scaledLocator = adjustedLocator ;
2024-09-12 14:26:05 +00:00
scaledLocator . scalingFactor = getScalingFactor ( ) ;
2025-01-15 17:05:45 +00:00
result = loadImageImpl ( scaledLocator ) - > createImageReference ( ) ;
2024-09-12 14:26:05 +00:00
}
else
2025-01-15 17:05:45 +00:00
result = loadImageImpl ( adjustedLocator ) - > createImageReference ( ) ;
if ( locator . horizontalFlip )
result - > horizontalFlip ( ) ;
if ( locator . verticalFlip )
result - > verticalFlip ( ) ;
return result ;
2024-06-05 20:16:12 +00:00
}
2024-06-08 07:35:13 +00:00
std : : shared_ptr < IImage > RenderHandler : : loadImage ( const AnimationPath & path , int frame , int group , EImageBlitMode mode )
2024-06-05 20:16:12 +00:00
{
2025-01-17 12:17:29 +00:00
ImageLocator locator = getLocatorForAnimationFrame ( path , frame , group , 1 , mode ) ;
if ( ! locator . empty ( ) )
return loadImage ( locator ) ;
else
2025-02-03 11:44:21 +00:00
{
logGlobal - > error ( " Failed to load non-existing image " ) ;
2025-01-17 12:17:29 +00:00
return loadImage ( ImageLocator ( ImagePath : : builtin ( " DEFAULT " ) , mode ) ) ;
2025-02-03 11:44:21 +00:00
}
2023-09-04 18:01:44 +03:00
}
std : : shared_ptr < IImage > RenderHandler : : loadImage ( const ImagePath & path , EImageBlitMode mode )
{
2025-01-15 17:05:45 +00:00
ImageLocator locator ( path , mode ) ;
return loadImage ( locator ) ;
2023-09-04 18:01:44 +03:00
}
2025-01-16 15:09:03 +00:00
std : : shared_ptr < CanvasImage > RenderHandler : : createImage ( const Point & size , CanvasScalingPolicy scalingPolicy )
2023-09-04 18:01:44 +03:00
{
2025-01-16 15:09:03 +00:00
return std : : make_shared < CanvasImage > ( size , scalingPolicy ) ;
2023-09-04 18:01:44 +03:00
}
2024-06-08 07:35:13 +00:00
std : : shared_ptr < CAnimation > RenderHandler : : loadAnimation ( const AnimationPath & path , EImageBlitMode mode )
2023-09-04 18:01:44 +03:00
{
2025-01-15 17:05:45 +00:00
return std : : make_shared < CAnimation > ( path , getAnimationLayout ( path , 1 , mode ) , mode ) ;
2023-09-04 18:01:44 +03:00
}
2024-06-05 14:31:37 +00:00
void RenderHandler : : addImageListEntries ( const EntityService * service )
{
service - > forEachBase ( [ this ] ( const Entity * entity , bool & stop )
{
entity - > registerIcons ( [ this ] ( size_t index , size_t group , const std : : string & listName , const std : : string & imageName )
{
if ( imageName . empty ( ) )
return ;
2025-02-04 16:41:15 +00:00
auto & layout = getAnimationLayout ( AnimationPath : : builtin ( " SPRITES/ " + listName ) , 1 , EImageBlitMode : : COLORKEY ) ;
2024-06-05 14:31:37 +00:00
JsonNode entry ;
entry [ " file " ] . String ( ) = imageName ;
if ( index > = layout [ group ] . size ( ) )
layout [ group ] . resize ( index + 1 ) ;
2025-01-15 17:05:45 +00:00
layout [ group ] [ index ] = ImageLocator ( entry , EImageBlitMode : : SIMPLE ) ;
2024-06-05 14:31:37 +00:00
} ) ;
} ) ;
}
2025-01-26 14:38:17 +00:00
static void detectOverlappingBuildings ( RenderHandler * renderHandler , const Faction * factionBase )
{
if ( ! factionBase - > hasTown ( ) )
return ;
auto faction = dynamic_cast < const CFaction * > ( factionBase ) ;
for ( const auto & left : faction - > town - > clientInfo . structures )
{
for ( const auto & right : faction - > town - > clientInfo . structures )
{
if ( left - > identifier < = right - > identifier )
continue ; // only a<->b comparison is needed, not a<->a or b<->a
if ( left - > building & & right - > building & & left - > building - > getBase ( ) = = right - > building - > getBase ( ) )
2025-01-29 23:11:53 +00:00
{
if ( left - > pos . z ! = right - > pos . z )
logMod - > warn ( " Town %s: Upgrades of same building have different z-index: '%s' and '%s' " , faction - > getJsonKey ( ) , left - > identifier , right - > identifier ) ;
2025-01-26 14:38:17 +00:00
continue ; // upgrades of the same buildings are expected to overlap
2025-01-29 23:11:53 +00:00
}
2025-01-26 14:38:17 +00:00
if ( left - > pos . z ! = right - > pos . z )
continue ; // buildings already have different z-index and have well-defined overlap logic
2025-02-04 16:41:15 +00:00
auto leftImage = renderHandler - > loadImage ( left - > defName , 0 , 0 , EImageBlitMode : : COLORKEY ) ;
auto rightImage = renderHandler - > loadImage ( right - > defName , 0 , 0 , EImageBlitMode : : COLORKEY ) ;
2025-01-26 14:38:17 +00:00
Rect leftRect ( left - > pos . x , left - > pos . y , leftImage - > width ( ) , leftImage - > height ( ) ) ;
Rect rightRect ( right - > pos . x , right - > pos . y , rightImage - > width ( ) , rightImage - > height ( ) ) ;
Rect intersection = leftRect . intersect ( rightRect ) ;
Point intersectionPosition ;
bool intersectionFound = false ;
for ( int y = 0 ; y < intersection . h & & ! intersectionFound ; + + y )
{
for ( int x = 0 ; x < intersection . w & & ! intersectionFound ; + + x )
{
Point leftPoint = Point ( x , y ) - leftRect . topLeft ( ) + intersection . topLeft ( ) ;
Point rightPoint = Point ( x , y ) - rightRect . topLeft ( ) + intersection . topLeft ( ) ;
if ( ! leftImage - > isTransparent ( leftPoint ) & & ! rightImage - > isTransparent ( rightPoint ) )
{
intersectionFound = true ;
intersectionPosition = intersection . topLeft ( ) + Point ( x , y ) ;
}
}
}
if ( intersectionFound )
logMod - > warn ( " Town %s: Detected overlapping buildings '%s' and '%s' at (%d, %d) with same z-index! " , faction - > getJsonKey ( ) , left - > identifier , right - > identifier , intersectionPosition . x , intersectionPosition . y ) ;
}
}
} ;
2024-06-05 14:31:37 +00:00
void RenderHandler : : onLibraryLoadingFinished ( const Services * services )
{
2025-01-29 10:03:58 +00:00
assert ( animationLayouts . empty ( ) ) ;
assetGenerator - > initialize ( ) ;
animationLayouts = assetGenerator - > generateAllAnimations ( ) ;
2024-06-05 14:31:37 +00:00
addImageListEntries ( services - > creatures ( ) ) ;
addImageListEntries ( services - > heroTypes ( ) ) ;
addImageListEntries ( services - > artifacts ( ) ) ;
addImageListEntries ( services - > factions ( ) ) ;
addImageListEntries ( services - > spells ( ) ) ;
addImageListEntries ( services - > skills ( ) ) ;
2025-01-26 14:38:17 +00:00
if ( settings [ " mods " ] [ " validation " ] . String ( ) = = " full " )
{
services - > factions ( ) - > forEach ( [ this ] ( const Faction * factionBase , bool & stop )
{
detectOverlappingBuildings ( this , factionBase ) ;
} ) ;
}
2024-06-05 14:31:37 +00:00
}
2024-09-23 15:43:11 +00:00
std : : shared_ptr < const IFont > RenderHandler : : loadFont ( EFonts font )
{
if ( fonts . count ( font ) )
return fonts . at ( font ) ;
const int8_t index = static_cast < int8_t > ( font ) ;
2024-09-28 19:30:26 +00:00
logGlobal - > debug ( " Loading font %d " , static_cast < int > ( index ) ) ;
2024-09-24 10:41:39 +00:00
auto configList = CResourceHandler : : get ( ) - > getResourcesWithName ( JsonPath : : builtin ( " config/fonts.json " ) ) ;
std : : shared_ptr < FontChain > loadedFont = std : : make_shared < FontChain > ( ) ;
std : : string bitmapPath ;
2024-09-23 15:43:11 +00:00
2024-09-24 10:41:39 +00:00
for ( auto & loader : configList )
{
auto stream = loader - > load ( JsonPath : : builtin ( " config/fonts.json " ) ) ;
std : : unique_ptr < ui8 [ ] > textData ( new ui8 [ stream - > getSize ( ) ] ) ;
stream - > read ( textData . get ( ) , stream - > getSize ( ) ) ;
const JsonNode config ( reinterpret_cast < const std : : byte * > ( textData . get ( ) ) , stream - > getSize ( ) , " config/fonts.json " ) ;
const JsonVector & bmpConf = config [ " bitmap " ] . Vector ( ) ;
const JsonNode & ttfConf = config [ " trueType " ] ;
2024-09-23 15:43:11 +00:00
2024-09-24 10:41:39 +00:00
bitmapPath = bmpConf [ index ] . String ( ) ;
2024-09-24 12:17:25 +00:00
if ( ! ttfConf [ bitmapPath ] . isNull ( ) )
2025-02-15 19:52:59 +01:00
loadedFont - > addTrueTypeFont ( ttfConf [ bitmapPath ] , ! config [ " lowPriority " ] . Bool ( ) ) ;
2024-09-24 10:41:39 +00:00
}
loadedFont - > addBitmapFont ( bitmapPath ) ;
2024-09-23 15:43:11 +00:00
fonts [ font ] = loadedFont ;
return loadedFont ;
}
2025-01-29 10:03:58 +00:00
void RenderHandler : : exportGeneratedAssets ( )
{
for ( const auto & entry : assetGenerator - > generateAllImages ( ) )
entry . second - > exportBitmap ( VCMIDirs : : get ( ) . userDataPath ( ) / " Generated " / ( entry . first . getOriginalName ( ) + " .png " ) , nullptr ) ;
}