2017-07-13 10:26:03 +02:00
/*
* MiscWidgets . 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
*
*/
2014-07-13 18:40:13 +03:00
# include "StdInc.h"
# include "MiscWidgets.h"
2014-07-15 10:14:49 +03:00
# include "CComponent.h"
2014-07-13 20:53:37 +03:00
# include "../gui/CGuiHandler.h"
2023-01-05 19:34:37 +02:00
# include "../gui/CursorHandler.h"
2014-07-13 18:40:13 +03:00
# include "../CPlayerInterface.h"
# include "../CGameInfo.h"
2023-08-21 14:42:41 +02:00
# include "../PlayerLocalState.h"
2023-10-13 00:21:58 +02:00
# include "../gui/WindowHandler.h"
2023-10-13 23:36:51 +02:00
# include "../eventsSDL/InputHandler.h"
2023-10-13 00:21:58 +02:00
# include "../windows/CTradeWindow.h"
2023-02-01 20:42:06 +02:00
# include "../widgets/TextControls.h"
2023-08-05 13:25:57 +02:00
# include "../widgets/CGarrisonInt.h"
2014-07-13 20:53:37 +03:00
# include "../windows/CCastleInterface.h"
2014-07-15 10:14:49 +03:00
# include "../windows/InfoWindows.h"
2023-06-02 15:42:18 +02:00
# include "../render/Canvas.h"
2023-09-08 17:49:06 +02:00
# include "../render/Graphics.h"
2014-07-13 18:40:13 +03:00
# include "../../CCallback.h"
2023-02-12 23:52:35 +02:00
# include "../../lib/CConfigHandler.h"
2023-06-23 17:02:48 +02:00
# include "../../lib/gameState/InfoAboutArmy.h"
2014-07-13 18:40:13 +03:00
# include "../../lib/CGeneralTextHandler.h"
2023-03-15 21:34:29 +02:00
# include "../../lib/GameSettings.h"
2023-02-12 23:52:35 +02:00
# include "../../lib/TextOperations.h"
2023-08-21 14:42:41 +02:00
# include "../../lib/mapObjects/CGCreature.h"
2023-02-12 23:52:35 +02:00
# include "../../lib/mapObjects/CGHeroInstance.h"
# include "../../lib/mapObjects/CGTownInstance.h"
2014-07-13 18:40:13 +03:00
2014-07-15 10:14:49 +03:00
void CHoverableArea : : hover ( bool on )
2014-07-13 18:40:13 +03:00
{
2014-07-15 10:14:49 +03:00
if ( on )
2023-05-16 17:34:23 +02:00
GH . statusbar ( ) - > write ( hoverText ) ;
2022-11-18 17:54:10 +02:00
else
2023-05-16 17:34:23 +02:00
GH . statusbar ( ) - > clearIfMatching ( hoverText ) ;
2014-07-13 18:40:13 +03:00
}
2014-07-15 10:14:49 +03:00
CHoverableArea : : CHoverableArea ( )
2014-07-13 18:40:13 +03:00
{
2014-07-15 10:14:49 +03:00
addUsedEvents ( HOVER ) ;
2014-07-13 18:40:13 +03:00
}
2014-07-15 10:14:49 +03:00
CHoverableArea : : ~ CHoverableArea ( )
2014-07-13 18:40:13 +03:00
{
}
2023-07-09 16:48:25 +02:00
void LRClickableAreaWText : : clickPressed ( const Point & cursorPosition )
2014-07-13 18:40:13 +03:00
{
2023-07-08 13:33:04 +02:00
if ( ! text . empty ( ) )
2014-07-15 10:14:49 +03:00
LOCPLINT - > showInfoDialog ( text ) ;
2014-07-13 18:40:13 +03:00
}
2023-07-08 13:33:04 +02:00
void LRClickableAreaWText : : showPopupWindow ( const Point & cursorPosition )
2014-07-13 18:40:13 +03:00
{
2023-06-11 17:20:10 +02:00
if ( ! text . empty ( ) )
2023-02-10 15:50:46 +02:00
CRClickPopup : : createAndPush ( text ) ;
2014-07-13 18:40:13 +03:00
}
2014-07-15 10:14:49 +03:00
LRClickableAreaWText : : LRClickableAreaWText ( )
2014-07-13 18:40:13 +03:00
{
2014-07-15 10:14:49 +03:00
init ( ) ;
2014-07-13 18:40:13 +03:00
}
2017-07-15 13:08:20 +02:00
LRClickableAreaWText : : LRClickableAreaWText ( const Rect & Pos , const std : : string & HoverText , const std : : string & ClickText )
2014-07-13 18:40:13 +03:00
{
2014-07-15 10:14:49 +03:00
init ( ) ;
2022-12-15 23:24:03 +02:00
pos = Pos + pos . topLeft ( ) ;
2014-07-15 10:14:49 +03:00
hoverText = HoverText ;
text = ClickText ;
2014-07-13 18:40:13 +03:00
}
2014-07-15 10:14:49 +03:00
LRClickableAreaWText : : ~ LRClickableAreaWText ( )
2014-07-13 18:40:13 +03:00
{
}
2014-07-15 10:14:49 +03:00
void LRClickableAreaWText : : init ( )
2014-07-13 18:40:13 +03:00
{
2023-06-13 18:33:35 +02:00
addUsedEvents ( LCLICK | SHOW_POPUP | HOVER ) ;
2014-07-13 18:40:13 +03:00
}
2023-07-09 16:48:25 +02:00
void LRClickableAreaWTextComp : : clickPressed ( const Point & cursorPosition )
2014-07-13 18:40:13 +03:00
{
2023-07-08 13:33:04 +02:00
std : : vector < std : : shared_ptr < CComponent > > comp ( 1 , createComponent ( ) ) ;
LOCPLINT - > showInfoDialog ( text , comp ) ;
2014-07-13 18:40:13 +03:00
}
2023-10-31 11:09:56 +02:00
LRClickableAreaWTextComp : : LRClickableAreaWTextComp ( const Rect & Pos , ComponentType BaseType )
: LRClickableAreaWText ( Pos )
2014-07-13 18:40:13 +03:00
{
2023-10-31 11:09:56 +02:00
component . type = BaseType ;
2014-07-13 18:40:13 +03:00
}
2018-04-07 13:34:11 +02:00
std : : shared_ptr < CComponent > LRClickableAreaWTextComp : : createComponent ( ) const
2014-07-13 18:40:13 +03:00
{
2023-10-31 11:09:56 +02:00
if ( component . type ! = ComponentType : : NONE )
return std : : make_shared < CComponent > ( component ) ;
2014-07-13 18:40:13 +03:00
else
2018-04-07 13:34:11 +02:00
return std : : shared_ptr < CComponent > ( ) ;
2014-07-13 18:40:13 +03:00
}
2023-07-08 13:33:04 +02:00
void LRClickableAreaWTextComp : : showPopupWindow ( const Point & cursorPosition )
2014-07-13 18:40:13 +03:00
{
2023-06-11 17:20:10 +02:00
if ( auto comp = createComponent ( ) )
2014-07-13 18:40:13 +03:00
{
2023-06-11 17:20:10 +02:00
CRClickPopup : : createAndPush ( text , CInfoWindow : : TCompsInfo ( 1 , comp ) ) ;
return ;
2014-07-13 18:40:13 +03:00
}
2023-07-08 13:33:04 +02:00
LRClickableAreaWText : : showPopupWindow ( cursorPosition ) ; //only if with-component variant not occurred
2014-07-13 18:40:13 +03:00
}
2023-09-03 20:42:22 +02:00
CHeroArea : : CHeroArea ( int x , int y , const CGHeroInstance * hero )
2023-06-11 17:20:10 +02:00
: CIntObject ( LCLICK | HOVER ) ,
2023-09-03 20:42:22 +02:00
hero ( hero ) ,
clickFunctor ( nullptr )
2014-07-13 18:40:13 +03:00
{
2018-04-07 13:34:11 +02:00
OBJECT_CONSTRUCTION_CAPTURING ( 255 - DISPOSE ) ;
2014-07-13 18:40:13 +03:00
2018-04-07 13:34:11 +02:00
pos . x + = x ;
pos . w = 58 ;
pos . y + = y ;
pos . h = 64 ;
2014-07-13 18:40:13 +03:00
2018-04-07 13:34:11 +02:00
if ( hero )
2023-09-03 20:42:22 +02:00
{
2023-09-28 18:43:04 +02:00
portrait = std : : make_shared < CAnimImage > ( AnimationPath : : builtin ( " PortraitsLarge " ) , hero - > getIconIndex ( ) ) ;
2023-09-03 20:42:22 +02:00
clickFunctor = [ hero ] ( ) - > void
{
LOCPLINT - > openHeroWindow ( hero ) ;
} ;
}
}
void CHeroArea : : addClickCallback ( ClickFunctor callback )
{
clickFunctor = callback ;
2014-07-13 18:40:13 +03:00
}
2023-07-09 16:48:25 +02:00
void CHeroArea : : clickPressed ( const Point & cursorPosition )
2014-07-13 18:40:13 +03:00
{
2023-09-03 20:42:22 +02:00
if ( clickFunctor )
clickFunctor ( ) ;
2014-07-13 18:40:13 +03:00
}
void CHeroArea : : hover ( bool on )
{
if ( on & & hero )
2023-05-16 17:34:23 +02:00
GH . statusbar ( ) - > write ( hero - > getObjectName ( ) ) ;
2014-07-13 18:40:13 +03:00
else
2023-05-16 17:34:23 +02:00
GH . statusbar ( ) - > clear ( ) ;
2014-07-13 18:40:13 +03:00
}
2023-07-09 16:48:25 +02:00
void LRClickableAreaOpenTown : : clickPressed ( const Point & cursorPosition )
2014-07-13 18:40:13 +03:00
{
2023-07-08 13:33:04 +02:00
if ( town )
2014-07-13 18:40:13 +03:00
LOCPLINT - > openTownWindow ( town ) ;
}
2016-11-27 17:17:20 +02:00
LRClickableAreaOpenTown : : LRClickableAreaOpenTown ( const Rect & Pos , const CGTownInstance * Town )
2023-10-31 11:09:56 +02:00
: LRClickableAreaWTextComp ( Pos ) , town ( Town )
2014-07-13 18:40:13 +03:00
{
}
2023-10-13 00:21:58 +02:00
void LRClickableArea : : clickPressed ( const Point & cursorPosition )
{
if ( onClick )
2023-10-13 23:36:51 +02:00
{
2023-10-13 00:21:58 +02:00
onClick ( ) ;
2023-10-13 23:36:51 +02:00
GH . input ( ) . hapticFeedback ( ) ;
}
2023-10-13 00:21:58 +02:00
}
void LRClickableArea : : showPopupWindow ( const Point & cursorPosition )
{
if ( onPopup )
onPopup ( ) ;
}
LRClickableArea : : LRClickableArea ( const Rect & Pos , std : : function < void ( ) > onClick , std : : function < void ( ) > onPopup )
: CIntObject ( LCLICK | SHOW_POPUP ) , onClick ( onClick ) , onPopup ( onPopup )
{
pos = Pos + pos . topLeft ( ) ;
}
2023-06-02 15:42:18 +02:00
void CMinorResDataBar : : show ( Canvas & to )
2014-07-13 18:40:13 +03:00
{
}
2023-02-03 18:55:25 +02:00
std : : string CMinorResDataBar : : buildDateString ( )
{
std : : string pattern = " %s: %d, %s: %d, %s: %d " ;
auto formatted = boost : : format ( pattern )
2023-02-08 13:56:09 +02:00
% CGI - > generaltexth - > translate ( " core.genrltxt.62 " ) % LOCPLINT - > cb - > getDate ( Date : : MONTH )
% CGI - > generaltexth - > translate ( " core.genrltxt.63 " ) % LOCPLINT - > cb - > getDate ( Date : : WEEK )
% CGI - > generaltexth - > translate ( " core.genrltxt.64 " ) % LOCPLINT - > cb - > getDate ( Date : : DAY_OF_WEEK ) ;
2023-02-03 18:55:25 +02:00
return boost : : str ( formatted ) ;
}
2023-06-02 15:42:18 +02:00
void CMinorResDataBar : : showAll ( Canvas & to )
2014-07-13 18:40:13 +03:00
{
2018-04-07 13:34:11 +02:00
CIntObject : : showAll ( to ) ;
2023-04-27 23:29:16 +02:00
for ( GameResID i = EGameResID : : WOOD ; i < = EGameResID : : GOLD ; + + i )
2014-07-13 18:40:13 +03:00
{
2023-03-09 15:36:46 +02:00
std : : string text = std : : to_string ( LOCPLINT - > cb - > getResourceAmount ( i ) ) ;
2014-07-13 18:40:13 +03:00
2023-06-02 15:42:18 +02:00
Point target ( pos . x + 50 + 76 * GameResID ( i ) , pos . y + pos . h / 2 ) ;
to . drawText ( target , FONT_SMALL , Colors : : WHITE , ETextAlignment : : CENTER , text ) ;
2014-07-13 18:40:13 +03:00
}
2023-06-02 15:42:18 +02:00
Point target ( pos . x + 545 + ( pos . w - 545 ) / 2 , pos . y + pos . h / 2 ) ;
to . drawText ( target , FONT_SMALL , Colors : : WHITE , ETextAlignment : : CENTER , buildDateString ( ) ) ;
2014-07-13 18:40:13 +03:00
}
CMinorResDataBar : : CMinorResDataBar ( )
{
2018-04-07 13:34:11 +02:00
OBJECT_CONSTRUCTION_CAPTURING ( 255 - DISPOSE ) ;
2014-07-13 18:40:13 +03:00
pos . x = 7 ;
pos . y = 575 ;
2023-08-23 14:07:50 +02:00
background = std : : make_shared < CPicture > ( ImagePath : : builtin ( " KRESBAR.bmp " ) ) ;
2018-04-07 13:34:11 +02:00
background - > colorize ( LOCPLINT - > playerID ) ;
pos . w = background - > pos . w ;
pos . h = background - > pos . h ;
2014-07-13 18:40:13 +03:00
}
2018-04-07 13:34:11 +02:00
CMinorResDataBar : : ~ CMinorResDataBar ( ) = default ;
2014-07-13 18:40:13 +03:00
void CArmyTooltip : : init ( const InfoAboutArmy & army )
{
2018-04-07 13:34:11 +02:00
OBJECT_CONSTRUCTION_CAPTURING ( 255 - DISPOSE ) ;
2014-07-13 18:40:13 +03:00
2022-11-26 23:12:20 +02:00
title = std : : make_shared < CLabel > ( 66 , 2 , FONT_SMALL , ETextAlignment : : TOPLEFT , Colors : : WHITE , army . name ) ;
2014-07-13 18:40:13 +03:00
std : : vector < Point > slotsPos ;
2018-04-07 13:34:11 +02:00
slotsPos . push_back ( Point ( 36 , 73 ) ) ;
slotsPos . push_back ( Point ( 72 , 73 ) ) ;
slotsPos . push_back ( Point ( 108 , 73 ) ) ;
slotsPos . push_back ( Point ( 18 , 122 ) ) ;
slotsPos . push_back ( Point ( 54 , 122 ) ) ;
slotsPos . push_back ( Point ( 90 , 122 ) ) ;
slotsPos . push_back ( Point ( 126 , 122 ) ) ;
2014-07-13 18:40:13 +03:00
for ( auto & slot : army . army )
{
if ( slot . first . getNum ( ) > = GameConstants : : ARMY_SIZE )
{
2017-08-11 13:38:10 +02:00
logGlobal - > warn ( " %s has stack in slot %d " , army . name , slot . first . getNum ( ) ) ;
2014-07-13 18:40:13 +03:00
continue ;
}
2023-08-23 14:07:50 +02:00
icons . push_back ( std : : make_shared < CAnimImage > ( AnimationPath : : builtin ( " CPRSMALL " ) , slot . second . type - > getIconIndex ( ) , 0 , slotsPos [ slot . first . getNum ( ) ] . x , slotsPos [ slot . first . getNum ( ) ] . y ) ) ;
2014-07-13 18:40:13 +03:00
std : : string subtitle ;
if ( army . army . isDetailed )
2018-04-07 13:34:11 +02:00
{
2023-02-12 23:52:35 +02:00
subtitle = TextOperations : : formatMetric ( slot . second . count , 4 ) ;
2018-04-07 13:34:11 +02:00
}
2014-07-13 18:40:13 +03:00
else
{
//if =0 - we have no information about stack size at all
2018-04-07 13:34:11 +02:00
if ( slot . second . count )
2023-01-14 16:55:08 +02:00
{
2023-02-16 00:36:09 +02:00
if ( settings [ " gameTweaks " ] [ " numericCreaturesQuantities " ] . Bool ( ) )
2023-01-14 16:55:08 +02:00
{
subtitle = CCreature : : getQuantityRangeStringForId ( ( CCreature : : CreatureQuantityId ) slot . second . count ) ;
}
else
{
subtitle = CGI - > generaltexth - > arraytxt [ 171 + 3 * ( slot . second . count ) ] ;
}
}
2014-07-13 18:40:13 +03:00
}
2023-03-07 03:19:07 +02:00
subtitles . push_back ( std : : make_shared < CLabel > ( slotsPos [ slot . first . getNum ( ) ] . x + 17 , slotsPos [ slot . first . getNum ( ) ] . y + 39 , FONT_TINY , ETextAlignment : : CENTER , Colors : : WHITE , subtitle ) ) ;
2014-07-13 18:40:13 +03:00
}
}
2018-04-07 13:34:11 +02:00
CArmyTooltip : : CArmyTooltip ( Point pos , const InfoAboutArmy & army ) :
2014-07-13 18:40:13 +03:00
CIntObject ( 0 , pos )
{
init ( army ) ;
}
CArmyTooltip : : CArmyTooltip ( Point pos , const CArmedInstance * army ) :
CIntObject ( 0 , pos )
{
init ( InfoAboutArmy ( army , true ) ) ;
}
2018-04-07 13:34:11 +02:00
void CHeroTooltip : : init ( const InfoAboutHero & hero )
2014-07-13 18:40:13 +03:00
{
2018-04-07 13:34:11 +02:00
OBJECT_CONSTRUCTION_CAPTURING ( 255 - DISPOSE ) ;
2023-09-28 18:43:04 +02:00
portrait = std : : make_shared < CAnimImage > ( AnimationPath : : builtin ( " PortraitsLarge " ) , hero . getIconIndex ( ) , 0 , 3 , 2 ) ;
2014-07-13 18:40:13 +03:00
if ( hero . details )
{
2018-04-07 13:34:11 +02:00
for ( size_t i = 0 ; i < hero . details - > primskills . size ( ) ; i + + )
2022-11-26 23:12:20 +02:00
labels . push_back ( std : : make_shared < CLabel > ( 75 + 28 * ( int ) i , 58 , FONT_SMALL , ETextAlignment : : CENTER , Colors : : WHITE ,
2023-03-09 15:36:46 +02:00
std : : to_string ( hero . details - > primskills [ i ] ) ) ) ;
2014-07-13 18:40:13 +03:00
2023-03-09 15:36:46 +02:00
labels . push_back ( std : : make_shared < CLabel > ( 158 , 98 , FONT_TINY , ETextAlignment : : CENTER , Colors : : WHITE , std : : to_string ( hero . details - > mana ) ) ) ;
2014-07-13 18:40:13 +03:00
2023-08-23 14:07:50 +02:00
morale = std : : make_shared < CAnimImage > ( AnimationPath : : builtin ( " IMRL22 " ) , hero . details - > morale + 3 , 0 , 5 , 74 ) ;
luck = std : : make_shared < CAnimImage > ( AnimationPath : : builtin ( " ILCK22 " ) , hero . details - > luck + 3 , 0 , 5 , 91 ) ;
2014-07-13 18:40:13 +03:00
}
}
CHeroTooltip : : CHeroTooltip ( Point pos , const InfoAboutHero & hero ) :
CArmyTooltip ( pos , hero )
{
init ( hero ) ;
}
CHeroTooltip : : CHeroTooltip ( Point pos , const CGHeroInstance * hero ) :
2016-09-28 13:22:33 +02:00
CArmyTooltip ( pos , InfoAboutHero ( hero , InfoAboutHero : : EInfoLevel : : DETAILED ) )
2014-07-13 18:40:13 +03:00
{
2016-09-28 13:22:33 +02:00
init ( InfoAboutHero ( hero , InfoAboutHero : : EInfoLevel : : DETAILED ) ) ;
2014-07-13 18:40:13 +03:00
}
2023-08-05 13:25:57 +02:00
CInteractableHeroTooltip : : CInteractableHeroTooltip ( Point pos , const CGHeroInstance * hero )
2023-07-15 23:14:59 +02:00
{
init ( InfoAboutHero ( hero , InfoAboutHero : : EInfoLevel : : DETAILED ) ) ;
2023-08-05 13:25:57 +02:00
OBJECT_CONSTRUCTION_CAPTURING ( 255 - DISPOSE ) ;
garrison = std : : make_shared < CGarrisonInt > ( pos + Point ( 0 , 73 ) , 4 , Point ( 0 , 0 ) , hero , nullptr , true , true , CGarrisonInt : : ESlotsLayout : : REVERSED_TWO_ROWS ) ;
2023-07-15 23:14:59 +02:00
}
void CInteractableHeroTooltip : : init ( const InfoAboutHero & hero )
{
OBJECT_CONSTRUCTION_CAPTURING ( 255 - DISPOSE ) ;
2023-09-28 18:43:04 +02:00
portrait = std : : make_shared < CAnimImage > ( AnimationPath : : builtin ( " PortraitsLarge " ) , hero . getIconIndex ( ) , 0 , 3 , 2 ) ;
2023-08-05 13:25:57 +02:00
title = std : : make_shared < CLabel > ( 66 , 2 , FONT_SMALL , ETextAlignment : : TOPLEFT , Colors : : WHITE , hero . name ) ;
2023-07-15 23:14:59 +02:00
if ( hero . details )
{
for ( size_t i = 0 ; i < hero . details - > primskills . size ( ) ; i + + )
2023-08-05 13:25:57 +02:00
labels . push_back ( std : : make_shared < CLabel > ( 75 + 28 * ( int ) i , 58 , FONT_SMALL , ETextAlignment : : CENTER , Colors : : WHITE ,
2023-07-15 23:14:59 +02:00
std : : to_string ( hero . details - > primskills [ i ] ) ) ) ;
2023-08-05 13:25:57 +02:00
labels . push_back ( std : : make_shared < CLabel > ( 158 , 98 , FONT_TINY , ETextAlignment : : CENTER , Colors : : WHITE , std : : to_string ( hero . details - > mana ) ) ) ;
2023-07-15 23:14:59 +02:00
2023-08-23 14:07:50 +02:00
morale = std : : make_shared < CAnimImage > ( AnimationPath : : builtin ( " IMRL22 " ) , hero . details - > morale + 3 , 0 , 5 , 74 ) ;
luck = std : : make_shared < CAnimImage > ( AnimationPath : : builtin ( " ILCK22 " ) , hero . details - > luck + 3 , 0 , 5 , 91 ) ;
2023-07-15 23:14:59 +02:00
}
}
2018-04-07 13:34:11 +02:00
void CTownTooltip : : init ( const InfoAboutTown & town )
2014-07-13 18:40:13 +03:00
{
2018-04-07 13:34:11 +02:00
OBJECT_CONSTRUCTION_CAPTURING ( 255 - DISPOSE ) ;
2014-07-13 18:40:13 +03:00
//order of icons in def: fort, citadel, castle, no fort
size_t fortIndex = town . fortLevel ? town . fortLevel - 1 : 3 ;
2023-08-23 14:07:50 +02:00
fort = std : : make_shared < CAnimImage > ( AnimationPath : : builtin ( " ITMCLS " ) , fortIndex , 0 , 105 , 31 ) ;
2014-07-13 18:40:13 +03:00
assert ( town . tType ) ;
2023-03-15 23:47:26 +02:00
size_t iconIndex = town . tType - > clientInfo . icons [ town . fortLevel > 0 ] [ town . built > = CGI - > settings ( ) - > getInteger ( EGameSettings : : TOWNS_BUILDINGS_PER_TURN_CAP ) ] ;
2014-07-13 18:40:13 +03:00
2023-08-23 14:07:50 +02:00
build = std : : make_shared < CAnimImage > ( AnimationPath : : builtin ( " itpt " ) , iconIndex , 0 , 3 , 2 ) ;
2014-07-13 18:40:13 +03:00
if ( town . details )
{
2023-08-23 14:07:50 +02:00
hall = std : : make_shared < CAnimImage > ( AnimationPath : : builtin ( " ITMTLS " ) , town . details - > hallLevel , 0 , 67 , 31 ) ;
2014-07-13 18:40:13 +03:00
2018-04-07 13:34:11 +02:00
if ( town . details - > goldIncome )
{
2022-11-26 23:12:20 +02:00
income = std : : make_shared < CLabel > ( 157 , 58 , FONT_TINY , ETextAlignment : : CENTER , Colors : : WHITE ,
2023-03-09 15:36:46 +02:00
std : : to_string ( town . details - > goldIncome ) ) ;
2018-04-07 13:34:11 +02:00
}
2014-07-13 18:40:13 +03:00
if ( town . details - > garrisonedHero ) //garrisoned hero icon
2023-08-23 14:07:50 +02:00
garrisonedHero = std : : make_shared < CPicture > ( ImagePath : : builtin ( " TOWNQKGH " ) , 149 , 76 ) ;
2014-07-13 18:40:13 +03:00
if ( town . details - > customRes ) //silo is built
{
2023-04-05 02:26:29 +02:00
if ( town . tType - > primaryRes = = EGameResID : : WOOD_AND_ORE ) // wood & ore
2014-07-13 18:40:13 +03:00
{
2023-08-23 14:07:50 +02:00
res1 = std : : make_shared < CAnimImage > ( AnimationPath : : builtin ( " SMALRES " ) , GameResID ( EGameResID : : WOOD ) , 0 , 7 , 75 ) ;
res2 = std : : make_shared < CAnimImage > ( AnimationPath : : builtin ( " SMALRES " ) , GameResID ( EGameResID : : ORE ) , 0 , 7 , 88 ) ;
2014-07-13 18:40:13 +03:00
}
else
2018-04-07 13:34:11 +02:00
{
2023-08-23 14:07:50 +02:00
res1 = std : : make_shared < CAnimImage > ( AnimationPath : : builtin ( " SMALRES " ) , town . tType - > primaryRes , 0 , 7 , 81 ) ;
2018-04-07 13:34:11 +02:00
}
2014-07-13 18:40:13 +03:00
}
}
}
2018-04-07 13:34:11 +02:00
CTownTooltip : : CTownTooltip ( Point pos , const InfoAboutTown & town )
: CArmyTooltip ( pos , town )
2014-07-13 18:40:13 +03:00
{
init ( town ) ;
}
2018-04-07 13:34:11 +02:00
CTownTooltip : : CTownTooltip ( Point pos , const CGTownInstance * town )
: CArmyTooltip ( pos , InfoAboutTown ( town , true ) )
2014-07-13 18:40:13 +03:00
{
init ( InfoAboutTown ( town , true ) ) ;
}
2023-07-16 10:17:37 +02:00
CInteractableTownTooltip : : CInteractableTownTooltip ( Point pos , const CGTownInstance * town )
{
2023-10-13 00:21:58 +02:00
init ( town ) ;
2023-08-05 13:25:57 +02:00
OBJECT_CONSTRUCTION_CAPTURING ( 255 - DISPOSE ) ;
garrison = std : : make_shared < CGarrisonInt > ( pos + Point ( 0 , 73 ) , 4 , Point ( 0 , 0 ) , town - > getUpperArmy ( ) , nullptr , true , true , CGarrisonInt : : ESlotsLayout : : REVERSED_TWO_ROWS ) ;
2023-07-16 10:17:37 +02:00
}
2023-10-13 00:21:58 +02:00
void CInteractableTownTooltip : : init ( const CGTownInstance * town )
2023-07-16 10:17:37 +02:00
{
OBJECT_CONSTRUCTION_CAPTURING ( 255 - DISPOSE ) ;
2023-10-13 00:21:58 +02:00
const InfoAboutTown townInfo = InfoAboutTown ( town , true ) ;
int townId = town - > id ;
2023-07-16 10:17:37 +02:00
//order of icons in def: fort, citadel, castle, no fort
2023-10-13 00:21:58 +02:00
size_t fortIndex = townInfo . fortLevel ? townInfo . fortLevel - 1 : 3 ;
2023-07-16 10:17:37 +02:00
2023-08-23 14:07:50 +02:00
fort = std : : make_shared < CAnimImage > ( AnimationPath : : builtin ( " ITMCLS " ) , fortIndex , 0 , 105 , 31 ) ;
2023-10-13 00:21:58 +02:00
fastArmyPurchase = std : : make_shared < LRClickableArea > ( Rect ( 105 , 31 , 34 , 34 ) , [ townId ] ( )
{
std : : vector < const CGTownInstance * > towns = LOCPLINT - > cb - > getTownsInfo ( true ) ;
for ( auto & town : towns )
{
if ( town - > id = = townId )
std : : make_shared < CCastleBuildings > ( town ) - > enterToTheQuickRecruitmentWindow ( ) ;
}
} ) ;
2023-10-13 23:36:51 +02:00
fastTavern = std : : make_shared < LRClickableArea > ( Rect ( 3 , 2 , 58 , 64 ) , [ townId ] ( )
{
std : : vector < const CGTownInstance * > towns = LOCPLINT - > cb - > getTownsInfo ( true ) ;
for ( auto & town : towns )
{
if ( town - > id = = townId & & town - > builtBuildings . count ( BuildingID : : TAVERN ) )
LOCPLINT - > showTavernWindow ( town , nullptr , QueryID : : NONE ) ;
}
} ) ;
2023-10-13 01:49:02 +02:00
fastMarket = std : : make_shared < LRClickableArea > ( Rect ( 143 , 31 , 30 , 34 ) , [ ] ( )
2023-10-13 00:21:58 +02:00
{
std : : vector < const CGTownInstance * > towns = LOCPLINT - > cb - > getTownsInfo ( true ) ;
for ( auto & town : towns )
{
if ( town - > builtBuildings . count ( BuildingID : : MARKETPLACE ) )
{
GH . windows ( ) . createAndPushWindow < CMarketplaceWindow > ( town , nullptr , nullptr , EMarketMode : : RESOURCE_RESOURCE ) ;
return ;
}
}
LOCPLINT - > showInfoDialog ( CGI - > generaltexth - > translate ( " vcmi.adventureMap.noTownWithMarket " ) ) ;
} ) ;
2023-07-16 10:17:37 +02:00
2023-10-13 00:21:58 +02:00
assert ( townInfo . tType ) ;
2023-07-16 10:17:37 +02:00
2023-10-13 00:21:58 +02:00
size_t iconIndex = townInfo . tType - > clientInfo . icons [ townInfo . fortLevel > 0 ] [ townInfo . built > = CGI - > settings ( ) - > getInteger ( EGameSettings : : TOWNS_BUILDINGS_PER_TURN_CAP ) ] ;
2023-07-16 10:17:37 +02:00
2023-08-23 14:07:50 +02:00
build = std : : make_shared < CAnimImage > ( AnimationPath : : builtin ( " itpt " ) , iconIndex , 0 , 3 , 2 ) ;
2023-10-13 00:21:58 +02:00
title = std : : make_shared < CLabel > ( 66 , 2 , FONT_SMALL , ETextAlignment : : TOPLEFT , Colors : : WHITE , townInfo . name ) ;
2023-07-16 10:17:37 +02:00
2023-10-13 00:21:58 +02:00
if ( townInfo . details )
2023-07-16 10:17:37 +02:00
{
2023-10-13 00:21:58 +02:00
hall = std : : make_shared < CAnimImage > ( AnimationPath : : builtin ( " ITMTLS " ) , townInfo . details - > hallLevel , 0 , 67 , 31 ) ;
fastTownHall = std : : make_shared < LRClickableArea > ( Rect ( 67 , 31 , 34 , 34 ) , [ townId ] ( )
{
std : : vector < const CGTownInstance * > towns = LOCPLINT - > cb - > getTownsInfo ( true ) ;
for ( auto & town : towns )
{
if ( town - > id = = townId )
std : : make_shared < CCastleBuildings > ( town ) - > enterTownHall ( ) ;
}
} ) ;
2023-07-16 10:17:37 +02:00
2023-10-13 00:21:58 +02:00
if ( townInfo . details - > goldIncome )
2023-07-16 10:17:37 +02:00
{
2023-08-05 13:25:57 +02:00
income = std : : make_shared < CLabel > ( 157 , 58 , FONT_TINY , ETextAlignment : : CENTER , Colors : : WHITE ,
2023-10-13 00:21:58 +02:00
std : : to_string ( townInfo . details - > goldIncome ) ) ;
2023-07-16 10:17:37 +02:00
}
2023-10-13 00:21:58 +02:00
if ( townInfo . details - > garrisonedHero ) //garrisoned hero icon
2023-08-23 14:07:50 +02:00
garrisonedHero = std : : make_shared < CPicture > ( ImagePath : : builtin ( " TOWNQKGH " ) , 149 , 76 ) ;
2023-07-16 10:17:37 +02:00
2023-10-13 00:21:58 +02:00
if ( townInfo . details - > customRes ) //silo is built
2023-07-16 10:17:37 +02:00
{
2023-10-13 00:21:58 +02:00
if ( townInfo . tType - > primaryRes = = EGameResID : : WOOD_AND_ORE ) // wood & ore
2023-07-16 10:17:37 +02:00
{
2023-08-23 14:07:50 +02:00
res1 = std : : make_shared < CAnimImage > ( AnimationPath : : builtin ( " SMALRES " ) , GameResID ( EGameResID : : WOOD ) , 0 , 7 , 75 ) ;
res2 = std : : make_shared < CAnimImage > ( AnimationPath : : builtin ( " SMALRES " ) , GameResID ( EGameResID : : ORE ) , 0 , 7 , 88 ) ;
2023-07-16 10:17:37 +02:00
}
else
{
2023-10-13 00:21:58 +02:00
res1 = std : : make_shared < CAnimImage > ( AnimationPath : : builtin ( " SMALRES " ) , townInfo . tType - > primaryRes , 0 , 7 , 81 ) ;
2023-07-16 10:17:37 +02:00
}
}
}
}
2023-08-21 14:42:41 +02:00
CreatureTooltip : : CreatureTooltip ( Point pos , const CGCreature * creature )
{
OBJ_CONSTRUCTION_CAPTURING_ALL_NO_DISPOSE ;
2023-11-02 15:49:21 +02:00
auto creatureID = creature - > getCreature ( ) ;
int32_t creatureIconIndex = CGI - > creatures ( ) - > getById ( creatureID ) - > getIconIndex ( ) ;
creatureImage = std : : make_shared < CAnimImage > ( graphics - > getAnimation ( AnimationPath : : builtin ( " TWCRPORT " ) ) , creatureIconIndex ) ;
2023-08-21 14:42:41 +02:00
creatureImage - > center ( Point ( parent - > pos . x + parent - > pos . w / 2 , parent - > pos . y + creatureImage - > pos . h / 2 + 11 ) ) ;
bool isHeroSelected = LOCPLINT - > localState - > getCurrentHero ( ) ! = nullptr ;
std : : string textContent = isHeroSelected
2023-11-02 15:49:21 +02:00
? creature - > getPopupText ( LOCPLINT - > localState - > getCurrentHero ( ) )
: creature - > getPopupText ( LOCPLINT - > playerID ) ;
2023-08-21 14:42:41 +02:00
//TODO: window is bigger than OH3
//TODO: vertical alignment does not match H3. Commented below example that matches H3 for creatures count but supports only 1 line:
/*std::shared_ptr<CLabel> = std::make_shared<CLabel>(parent->pos.w / 2, 103,
FONT_SMALL , ETextAlignment : : CENTER , Colors : : WHITE , creature - > getHoverText ( LOCPLINT - > playerID ) ) ; */
2023-08-30 00:35:31 +02:00
tooltipTextbox = std : : make_shared < CTextBox > ( textContent , Rect ( 15 , 95 , 230 , 150 ) , 0 , FONT_SMALL , ETextAlignment : : TOPCENTER , Colors : : WHITE ) ;
2023-08-21 14:42:41 +02:00
}
2023-04-30 17:21:02 +02:00
void MoraleLuckBox : : set ( const AFactionMember * node )
2014-07-13 18:40:13 +03:00
{
2018-04-07 13:34:11 +02:00
OBJECT_CONSTRUCTION_CUSTOM_CAPTURING ( 255 - DISPOSE ) ;
2023-10-31 11:09:56 +02:00
const std : : array textId = { 62 , 88 } ; //eg %s \n\n\n {Current Luck Modifiers:}
2014-07-13 18:40:13 +03:00
const int noneTxtId = 108 ; //Russian version uses same text for neutral morale\luck
2023-10-31 11:09:56 +02:00
const std : : array neutralDescr = { 60 , 86 } ; //eg {Neutral Morale} \n\n Neutral morale means your armies will neither be blessed with extra attacks or freeze in combat.
const std : : array componentType = { ComponentType : : LUCK , ComponentType : : MORALE } ;
const std : : array hoverTextBase = { 7 , 4 } ;
2020-11-11 21:43:40 +02:00
TConstBonusListPtr modifierList = std : : make_shared < const BonusList > ( ) ;
2023-10-31 11:09:56 +02:00
component . value = 0 ;
2014-07-13 18:40:13 +03:00
2018-04-07 13:34:11 +02:00
if ( node )
2023-10-31 11:09:56 +02:00
component . value = morale ? node - > moraleValAndBonusList ( modifierList ) : node - > luckValAndBonusList ( modifierList ) ;
2014-07-13 18:40:13 +03:00
2023-10-31 11:09:56 +02:00
int mrlt = ( component . value > 0 ) - ( component . value < 0 ) ; //signum: -1 - bad luck / morale, 0 - neutral, 1 - good
2014-07-13 18:40:13 +03:00
hoverText = CGI - > generaltexth - > heroscrn [ hoverTextBase [ morale ] - mrlt ] ;
2023-10-31 11:09:56 +02:00
component . type = componentType [ morale ] ;
2014-07-13 18:40:13 +03:00
text = CGI - > generaltexth - > arraytxt [ textId [ morale ] ] ;
boost : : algorithm : : replace_first ( text , " %s " , CGI - > generaltexth - > arraytxt [ neutralDescr [ morale ] - mrlt ] ) ;
2016-11-27 17:17:20 +02:00
2023-05-01 00:20:01 +02:00
if ( morale & & node & & ( node - > getBonusBearer ( ) - > hasBonusOfType ( BonusType : : UNDEAD )
| | node - > getBonusBearer ( ) - > hasBonusOfType ( BonusType : : NON_LIVING ) ) )
2015-12-24 21:35:32 +02:00
{
text + = CGI - > generaltexth - > arraytxt [ 113 ] ; //unaffected by morale
2023-10-31 11:09:56 +02:00
component . value = 0 ;
2015-12-24 21:35:32 +02:00
}
2023-05-01 00:20:01 +02:00
else if ( morale & & node & & node - > getBonusBearer ( ) - > hasBonusOfType ( BonusType : : NO_MORALE ) )
2017-10-05 17:13:49 +02:00
{
2023-05-01 00:20:01 +02:00
auto noMorale = node - > getBonusBearer ( ) - > getBonus ( Selector : : type ( ) ( BonusType : : NO_MORALE ) ) ;
2017-10-05 17:13:49 +02:00
text + = " \n " + noMorale - > Description ( ) ;
2023-10-31 11:09:56 +02:00
component . value = 0 ;
2017-10-05 17:13:49 +02:00
}
2023-05-01 00:20:01 +02:00
else if ( ! morale & & node & & node - > getBonusBearer ( ) - > hasBonusOfType ( BonusType : : NO_LUCK ) )
2017-10-05 17:13:49 +02:00
{
2023-05-01 00:20:01 +02:00
auto noLuck = node - > getBonusBearer ( ) - > getBonus ( Selector : : type ( ) ( BonusType : : NO_LUCK ) ) ;
2017-10-05 17:13:49 +02:00
text + = " \n " + noLuck - > Description ( ) ;
2023-10-31 11:09:56 +02:00
component . value = 0 ;
2017-10-05 17:13:49 +02:00
}
2014-07-13 18:40:13 +03:00
else
{
2021-02-03 19:19:56 +02:00
std : : string addInfo = " " ;
for ( auto & bonus : * modifierList )
2014-07-13 18:40:13 +03:00
{
2023-11-10 16:11:35 +02:00
if ( bonus - > val ) {
const std : : string & description = bonus - > Description ( ) ;
//arraytxt already contains \n
if ( description . size ( ) & & description [ 0 ] ! = ' \n ' )
addInfo + = ' \n ' ;
addInfo + = description ;
}
2014-07-13 18:40:13 +03:00
}
2021-09-12 13:30:54 +02:00
text = addInfo . empty ( )
? text + CGI - > generaltexth - > arraytxt [ noneTxtId ]
: text + addInfo ;
2014-07-13 18:40:13 +03:00
}
std : : string imageName ;
if ( small )
imageName = morale ? " IMRL30 " : " ILCK30 " ;
else
imageName = morale ? " IMRL42 " : " ILCK42 " ;
2023-10-31 11:09:56 +02:00
image = std : : make_shared < CAnimImage > ( AnimationPath : : builtin ( imageName ) , * component . value + 3 ) ;
2014-07-13 18:40:13 +03:00
image - > moveBy ( Point ( pos . w / 2 - image - > pos . w / 2 , pos . h / 2 - image - > pos . h / 2 ) ) ; //center icon
}
2018-04-07 13:34:11 +02:00
MoraleLuckBox : : MoraleLuckBox ( bool Morale , const Rect & r , bool Small )
: morale ( Morale ) ,
2014-07-13 18:40:13 +03:00
small ( Small )
{
2022-12-15 23:24:03 +02:00
pos = r + pos . topLeft ( ) ;
2018-04-07 13:34:11 +02:00
defActions = 255 - DISPOSE ;
2014-07-13 18:40:13 +03:00
}
2018-04-07 13:34:11 +02:00
CCreaturePic : : CCreaturePic ( int x , int y , const CCreature * cre , bool Big , bool Animated )
2014-07-13 18:40:13 +03:00
{
2018-04-07 13:34:11 +02:00
OBJECT_CONSTRUCTION_CAPTURING ( 255 - DISPOSE ) ;
2014-07-13 18:40:13 +03:00
pos . x + = x ;
pos . y + = y ;
2023-04-09 17:26:32 +02:00
auto faction = cre - > getFaction ( ) ;
2014-07-13 18:40:13 +03:00
Entities redesign and a few ERM features
* Made most Handlers derived from CHandlerBase and moved service API there.
* Declared existing Entity APIs.
* Added basic script context caching
* Started Lua script module
* Started Lua spell effect API
* Started script state persistence
* Started battle info callback binding
* CommitPackage removed
* Extracted spells::Caster to own header; Expanded Spell API.
* implemented !!MC:S, !!FU:E, !!FU:P, !!MA, !!VR:H, !!VR:C
* !!BU:C, !!BU:E, !!BU:G, !!BU:M implemented
* Allow use of "MC:S@varName@" to declare normal variable (technically v-variable with string key)
* Re-enabled VERM macros.
* !?GM0 added
* !?TM implemented
* Added !!MF:N
* Started !?OB, !!BM, !!HE, !!OW, !!UN
* Added basic support of w-variables
* Added support for ERM indirect variables
* Made !?FU regular trigger
* !!re (ERA loop receiver) implemented
* Fixed ERM receivers with zero args.
2018-03-17 16:58:30 +02:00
assert ( CGI - > townh - > size ( ) > faction ) ;
2014-07-13 18:40:13 +03:00
if ( Big )
Entities redesign and a few ERM features
* Made most Handlers derived from CHandlerBase and moved service API there.
* Declared existing Entity APIs.
* Added basic script context caching
* Started Lua script module
* Started Lua spell effect API
* Started script state persistence
* Started battle info callback binding
* CommitPackage removed
* Extracted spells::Caster to own header; Expanded Spell API.
* implemented !!MC:S, !!FU:E, !!FU:P, !!MA, !!VR:H, !!VR:C
* !!BU:C, !!BU:E, !!BU:G, !!BU:M implemented
* Allow use of "MC:S@varName@" to declare normal variable (technically v-variable with string key)
* Re-enabled VERM macros.
* !?GM0 added
* !?TM implemented
* Added !!MF:N
* Started !?OB, !!BM, !!HE, !!OW, !!UN
* Added basic support of w-variables
* Added support for ERM indirect variables
* Made !?FU regular trigger
* !!re (ERA loop receiver) implemented
* Fixed ERM receivers with zero args.
2018-03-17 16:58:30 +02:00
bg = std : : make_shared < CPicture > ( ( * CGI - > townh ) [ faction ] - > creatureBg130 ) ;
2014-07-13 18:40:13 +03:00
else
Entities redesign and a few ERM features
* Made most Handlers derived from CHandlerBase and moved service API there.
* Declared existing Entity APIs.
* Added basic script context caching
* Started Lua script module
* Started Lua spell effect API
* Started script state persistence
* Started battle info callback binding
* CommitPackage removed
* Extracted spells::Caster to own header; Expanded Spell API.
* implemented !!MC:S, !!FU:E, !!FU:P, !!MA, !!VR:H, !!VR:C
* !!BU:C, !!BU:E, !!BU:G, !!BU:M implemented
* Allow use of "MC:S@varName@" to declare normal variable (technically v-variable with string key)
* Re-enabled VERM macros.
* !?GM0 added
* !?TM implemented
* Added !!MF:N
* Started !?OB, !!BM, !!HE, !!OW, !!UN
* Added basic support of w-variables
* Added support for ERM indirect variables
* Made !?FU regular trigger
* !!re (ERA loop receiver) implemented
* Fixed ERM receivers with zero args.
2018-03-17 16:58:30 +02:00
bg = std : : make_shared < CPicture > ( ( * CGI - > townh ) [ faction ] - > creatureBg120 ) ;
2018-07-25 00:36:48 +02:00
anim = std : : make_shared < CCreatureAnim > ( 0 , 0 , cre - > animDefName ) ;
2014-07-13 18:40:13 +03:00
anim - > clipRect ( cre - > isDoubleWide ( ) ? 170 : 150 , 155 , bg - > pos . w , bg - > pos . h ) ;
2023-05-01 00:20:01 +02:00
anim - > startPreview ( cre - > hasBonusOfType ( BonusType : : SIEGE_WEAPON ) ) ;
2014-07-13 18:40:13 +03:00
2022-11-26 23:12:20 +02:00
amount = std : : make_shared < CLabel > ( bg - > pos . w , bg - > pos . h , FONT_MEDIUM , ETextAlignment : : BOTTOMRIGHT , Colors : : WHITE ) ;
2014-09-19 16:31:01 +03:00
2014-07-13 18:40:13 +03:00
pos . w = bg - > pos . w ;
pos . h = bg - > pos . h ;
}
2014-09-19 16:31:01 +03:00
2023-06-02 15:42:18 +02:00
void CCreaturePic : : show ( Canvas & to )
2014-09-19 16:31:01 +03:00
{
// redraw everything in a proper order
bg - > showAll ( to ) ;
anim - > show ( to ) ;
amount - > showAll ( to ) ;
}
void CCreaturePic : : setAmount ( int newAmount )
{
2018-04-07 13:34:11 +02:00
if ( newAmount ! = 0 )
2023-03-09 15:36:46 +02:00
amount - > setText ( std : : to_string ( newAmount ) ) ;
2014-09-19 16:31:01 +03:00
else
amount - > setText ( " " ) ;
}
2023-09-17 20:34:31 +02:00
TransparentFilledRectangle : : TransparentFilledRectangle ( Rect position , ColorRGBA color ) :
color ( color ) , colorLine ( ColorRGBA ( ) ) , drawLine ( false )
{
pos = position + pos . topLeft ( ) ;
}
TransparentFilledRectangle : : TransparentFilledRectangle ( Rect position , ColorRGBA color , ColorRGBA colorLine ) :
color ( color ) , colorLine ( colorLine ) , drawLine ( true )
{
pos = position + pos . topLeft ( ) ;
}
void TransparentFilledRectangle : : showAll ( Canvas & to )
{
to . drawColorBlended ( pos , color ) ;
if ( drawLine )
to . drawBorder ( pos , colorLine ) ;
}
SimpleLine : : SimpleLine ( Point pos1 , Point pos2 , ColorRGBA color ) :
pos1 ( pos1 ) , pos2 ( pos2 ) , color ( color )
{ }
void SimpleLine : : showAll ( Canvas & to )
{
to . drawLine ( pos1 + pos . topLeft ( ) , pos2 + pos . topLeft ( ) , color , color ) ;
2023-09-28 18:43:04 +02:00
}