2023-10-19 16:19:09 +02:00
/*
* CCastleInterface . 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 "CCastleInterface.h"
# include "CHeroWindow.h"
2024-02-03 13:26:24 +02:00
# include "CMarketWindow.h"
2023-10-19 16:19:09 +02:00
# include "InfoWindows.h"
# include "GUIClasses.h"
# include "QuickRecruitmentWindow.h"
# include "CCreatureWindow.h"
# include "../CGameInfo.h"
# include "../CPlayerInterface.h"
# include "../PlayerLocalState.h"
# include "../gui/CGuiHandler.h"
# include "../gui/Shortcut.h"
# include "../gui/WindowHandler.h"
2024-05-02 14:55:20 +02:00
# include "../media/IMusicPlayer.h"
2023-10-19 16:19:09 +02:00
# include "../widgets/MiscWidgets.h"
# include "../widgets/CComponent.h"
# include "../widgets/CGarrisonInt.h"
# include "../widgets/Buttons.h"
# include "../widgets/TextControls.h"
2023-10-22 17:36:41 +02:00
# include "../widgets/RadialMenu.h"
# include "../widgets/CExchangeController.h"
2023-10-19 16:19:09 +02:00
# include "../render/Canvas.h"
# include "../render/IImage.h"
# include "../render/IRenderHandler.h"
2024-01-31 03:30:10 +02:00
# include "../render/CAnimation.h"
2023-10-19 16:19:09 +02:00
# include "../render/ColorFilter.h"
# include "../adventureMap/AdventureMapInterface.h"
# include "../adventureMap/CList.h"
# include "../adventureMap/CResDataBar.h"
# include "../../CCallback.h"
# include "../../lib/CArtHandler.h"
2024-05-02 14:55:20 +02:00
# include "../../lib/CConfigHandler.h"
# include "../../lib/CSoundBase.h"
2023-10-19 16:19:09 +02:00
# include "../../lib/CCreatureHandler.h"
2024-07-20 14:55:17 +02:00
# include "../../lib/texts/CGeneralTextHandler.h"
2024-08-31 13:00:36 +02:00
# include "../../lib/IGameSettings.h"
2023-10-19 16:19:09 +02:00
# include "../../lib/spells/CSpellHandler.h"
# include "../../lib/GameConstants.h"
# include "../../lib/StartInfo.h"
# include "../../lib/campaign/CampaignState.h"
2024-07-21 12:49:40 +02:00
# include "../../lib/entities/building/CBuilding.h"
2023-10-19 16:19:09 +02:00
# include "../../lib/mapObjects/CGHeroInstance.h"
# include "../../lib/mapObjects/CGTownInstance.h"
2024-09-16 21:51:10 +02:00
# include "../../lib/mapObjects/TownBuildingInstance.h"
2023-10-19 16:19:09 +02:00
static bool useCompactCreatureBox ( )
{
return settings [ " gameTweaks " ] [ " compactTownCreatureInfo " ] . Bool ( ) ;
}
static bool useAvailableAmountAsCreatureLabel ( )
{
return settings [ " gameTweaks " ] [ " availableCreaturesAsDwellingLabel " ] . Bool ( ) ;
}
CBuildingRect : : CBuildingRect ( CCastleBuildings * Par , const CGTownInstance * Town , const CStructure * Str )
: CShowableAnim ( 0 , 0 , Str - > defName , CShowableAnim : : BASE , BUILDING_FRAME_TIME ) ,
parent ( Par ) ,
town ( Town ) ,
str ( Str ) ,
border ( nullptr ) ,
area ( nullptr ) ,
stateTimeCounter ( BUILD_ANIMATION_FINISHED_TIMEPOINT )
{
addUsedEvents ( LCLICK | SHOW_POPUP | MOVE | HOVER | TIME ) ;
pos . x + = str - > pos . x ;
pos . y + = str - > pos . y ;
// special animation frame manipulation for castle shipyard with and without ship
// done due to .def used in special way, not to animate building - first image is for shipyard without citadel moat, 2nd image is for including moat
if ( Town - > town - > faction - > getId ( ) = = FactionID : : CASTLE & & Str - > building & &
( Str - > building - > bid = = BuildingID : : SHIPYARD | | Str - > building - > bid = = BuildingID : : SHIP ) )
{
if ( Town - > hasBuilt ( BuildingID : : CITADEL ) )
{
this - > first = 1 ;
this - > frame = 1 ;
}
else
this - > last = 0 ;
}
if ( ! str - > borderName . empty ( ) )
2024-07-19 12:46:20 +02:00
border = GH . renderHandler ( ) . loadImage ( str - > borderName , EImageBlitMode : : COLORKEY ) ;
2023-10-19 16:19:09 +02:00
if ( ! str - > areaName . empty ( ) )
area = GH . renderHandler ( ) . loadImage ( str - > areaName , EImageBlitMode : : ALPHA ) ;
}
const CBuilding * CBuildingRect : : getBuilding ( )
{
if ( ! str - > building )
return nullptr ;
if ( str - > hiddenUpgrade ) // hidden upgrades, e.g. hordes - return base (dwelling for hordes)
return town - > town - > buildings . at ( str - > building - > getBase ( ) ) ;
return str - > building ;
}
bool CBuildingRect : : operator < ( const CBuildingRect & p2 ) const
{
return ( str - > pos . z ) < ( p2 . str - > pos . z ) ;
}
void CBuildingRect : : hover ( bool on )
{
if ( ! area )
return ;
if ( on )
{
if ( ! parent - > selectedBuilding //no building hovered
| | ( * parent - > selectedBuilding ) < ( * this ) ) //or we are on top
{
parent - > selectedBuilding = this ;
GH . statusbar ( ) - > write ( getSubtitle ( ) ) ;
}
}
else
{
if ( parent - > selectedBuilding = = this )
{
parent - > selectedBuilding = nullptr ;
GH . statusbar ( ) - > clear ( ) ;
}
}
}
void CBuildingRect : : clickPressed ( const Point & cursorPosition )
{
if ( getBuilding ( ) & & area & & ( parent - > selectedBuilding = = this ) )
{
auto building = getBuilding ( ) ;
2024-08-26 23:25:39 +02:00
parent - > buildingClicked ( building - > bid ) ;
2023-10-19 16:19:09 +02:00
}
}
void CBuildingRect : : showPopupWindow ( const Point & cursorPosition )
{
if ( ( ! area ) | | ( this ! = parent - > selectedBuilding ) | | getBuilding ( ) = = nullptr )
return ;
BuildingID bid = getBuilding ( ) - > bid ;
const CBuilding * bld = town - > town - > buildings . at ( bid ) ;
if ( bid < BuildingID : : DWELL_FIRST )
{
CRClickPopup : : createAndPush ( CInfoWindow : : genText ( bld - > getNameTranslated ( ) , bld - > getDescriptionTranslated ( ) ) ,
2023-10-31 11:09:56 +02:00
std : : make_shared < CComponent > ( ComponentType : : BUILDING , BuildingTypeUniqueID ( bld - > town - > faction - > getId ( ) , bld - > bid ) ) ) ;
2023-10-19 16:19:09 +02:00
}
else
{
2024-08-06 23:28:45 +02:00
int level = BuildingID : : getLevelFromDwelling ( bid ) ;
2023-10-19 16:19:09 +02:00
GH . windows ( ) . createAndPushWindow < CDwellingInfoBox > ( parent - > pos . x + parent - > pos . w / 2 , parent - > pos . y + parent - > pos . h / 2 , town , level ) ;
}
}
void CBuildingRect : : show ( Canvas & to )
{
uint32_t stageDelay = BUILDING_APPEAR_TIMEPOINT ;
if ( stateTimeCounter < BUILDING_APPEAR_TIMEPOINT )
{
setAlpha ( 255 * stateTimeCounter / stageDelay ) ;
CShowableAnim : : show ( to ) ;
}
else
{
setAlpha ( 255 ) ;
CShowableAnim : : show ( to ) ;
}
if ( border & & stateTimeCounter > BUILDING_APPEAR_TIMEPOINT )
{
if ( stateTimeCounter > = BUILD_ANIMATION_FINISHED_TIMEPOINT )
{
if ( parent - > selectedBuilding = = this )
to . draw ( border , pos . topLeft ( ) ) ;
return ;
}
auto darkBorder = ColorFilter : : genRangeShifter ( 0.f , 0.f , 0.f , 0.5f , 0.5f , 0.5f ) ;
auto lightBorder = ColorFilter : : genRangeShifter ( 0.f , 0.f , 0.f , 2.0f , 2.0f , 2.0f ) ;
auto baseBorder = ColorFilter : : genEmptyShifter ( ) ;
float progress = float ( stateTimeCounter % stageDelay ) / stageDelay ;
if ( stateTimeCounter < BUILDING_WHITE_BORDER_TIMEPOINT )
border - > adjustPalette ( ColorFilter : : genInterpolated ( lightBorder , darkBorder , progress ) , 0 ) ;
else
if ( stateTimeCounter < BUILDING_YELLOW_BORDER_TIMEPOINT )
border - > adjustPalette ( ColorFilter : : genInterpolated ( darkBorder , baseBorder , progress ) , 0 ) ;
else
border - > adjustPalette ( baseBorder , 0 ) ;
to . draw ( border , pos . topLeft ( ) ) ;
}
}
void CBuildingRect : : tick ( uint32_t msPassed )
{
CShowableAnim : : tick ( msPassed ) ;
stateTimeCounter + = msPassed ;
}
void CBuildingRect : : showAll ( Canvas & to )
{
if ( stateTimeCounter = = 0 )
return ;
CShowableAnim : : showAll ( to ) ;
if ( ! isActive ( ) & & parent - > selectedBuilding = = this & & border )
to . draw ( border , pos . topLeft ( ) ) ;
}
std : : string CBuildingRect : : getSubtitle ( ) //hover text for building
{
if ( ! getBuilding ( ) )
return " " ;
int bid = getBuilding ( ) - > bid ;
2024-06-24 03:23:26 +02:00
if ( bid < 30 ) //non-dwellings - only building name
2023-10-19 16:19:09 +02:00
return town - > town - > buildings . at ( getBuilding ( ) - > bid ) - > getNameTranslated ( ) ;
else //dwellings - recruit %creature%
{
2024-08-05 23:03:19 +02:00
auto & availableCreatures = town - > creatures [ ( bid - 30 ) % town - > town - > creatures . size ( ) ] . second ;
2023-10-19 16:19:09 +02:00
if ( availableCreatures . size ( ) )
{
int creaID = availableCreatures . back ( ) ; //taking last of available creatures
return CGI - > generaltexth - > allTexts [ 16 ] + " " + CGI - > creh - > objects . at ( creaID ) - > getNamePluralTranslated ( ) ;
}
else
{
logGlobal - > warn ( " Dwelling with id %d offers no creatures! " , bid ) ;
return " #ERROR# " ;
}
}
}
void CBuildingRect : : mouseMoved ( const Point & cursorPosition , const Point & lastUpdateDistance )
{
hover ( true ) ;
}
bool CBuildingRect : : receiveEvent ( const Point & position , int eventType ) const
{
if ( ! pos . isInside ( position . x , position . y ) )
return false ;
if ( area & & area - > isTransparent ( position - pos . topLeft ( ) ) )
return false ;
return CIntObject : : receiveEvent ( position , eventType ) ;
}
CDwellingInfoBox : : CDwellingInfoBox ( int centerX , int centerY , const CGTownInstance * Town , int level )
: CWindowObject ( RCLICK_POPUP , ImagePath : : builtin ( " CRTOINFO " ) , Point ( centerX , centerY ) )
{
2024-08-09 17:30:04 +02:00
OBJECT_CONSTRUCTION ;
2024-06-05 17:50:50 +02:00
background - > setPlayerColor ( Town - > tempOwner ) ;
2023-10-19 16:19:09 +02:00
2024-05-17 00:05:51 +02:00
const CCreature * creature = Town - > creatures . at ( level ) . second . back ( ) . toCreature ( ) ;
2023-10-19 16:19:09 +02:00
title = std : : make_shared < CLabel > ( 80 , 30 , FONT_SMALL , ETextAlignment : : CENTER , Colors : : WHITE , creature - > getNamePluralTranslated ( ) ) ;
animation = std : : make_shared < CCreaturePic > ( 30 , 44 , creature , true , true ) ;
std : : string text = std : : to_string ( Town - > creatures . at ( level ) . first ) ;
available = std : : make_shared < CLabel > ( 80 , 190 , FONT_SMALL , ETextAlignment : : CENTER , Colors : : WHITE , CGI - > generaltexth - > allTexts [ 217 ] + text ) ;
costPerTroop = std : : make_shared < CLabel > ( 80 , 227 , FONT_SMALL , ETextAlignment : : CENTER , Colors : : WHITE , CGI - > generaltexth - > allTexts [ 346 ] ) ;
for ( int i = 0 ; i < GameConstants : : RESOURCE_QUANTITY ; i + + )
{
auto res = static_cast < EGameResID > ( i ) ;
if ( creature - > getRecruitCost ( res ) )
{
resPicture . push_back ( std : : make_shared < CAnimImage > ( AnimationPath : : builtin ( " RESOURCE " ) , i , 0 , 0 , 0 ) ) ;
resAmount . push_back ( std : : make_shared < CLabel > ( 0 , 0 , FONT_SMALL , ETextAlignment : : CENTER , Colors : : WHITE , std : : to_string ( creature - > getRecruitCost ( res ) ) ) ) ;
}
}
int posY = 238 ;
int posX = pos . w / 2 - ( int ) resAmount . size ( ) * 25 + 5 ;
for ( size_t i = 0 ; i < resAmount . size ( ) ; i + + )
{
resPicture [ i ] - > moveBy ( Point ( posX , posY ) ) ;
resAmount [ i ] - > moveBy ( Point ( posX + 16 , posY + 43 ) ) ;
posX + = 50 ;
}
}
CDwellingInfoBox : : ~ CDwellingInfoBox ( ) = default ;
CHeroGSlot : : CHeroGSlot ( int x , int y , int updown , const CGHeroInstance * h , HeroSlots * Owner )
{
2024-08-09 17:30:04 +02:00
OBJECT_CONSTRUCTION ;
2023-10-19 16:19:09 +02:00
owner = Owner ;
pos . x + = x ;
pos . y + = y ;
pos . w = 58 ;
pos . h = 64 ;
upg = updown ;
portrait = std : : make_shared < CAnimImage > ( AnimationPath : : builtin ( " PortraitsLarge " ) , 0 , 0 , 0 , 0 ) ;
portrait - > visible = false ;
flag = std : : make_shared < CAnimImage > ( AnimationPath : : builtin ( " CREST58 " ) , 0 , 0 , 0 , 0 ) ;
flag - > visible = false ;
selection = std : : make_shared < CAnimImage > ( AnimationPath : : builtin ( " TWCRPORT " ) , 1 , 0 ) ;
selection - > visible = false ;
set ( h ) ;
2023-10-22 17:36:41 +02:00
addUsedEvents ( LCLICK | SHOW_POPUP | GESTURE | HOVER ) ;
2023-10-19 16:19:09 +02:00
}
CHeroGSlot : : ~ CHeroGSlot ( ) = default ;
2023-10-22 17:36:41 +02:00
void CHeroGSlot : : gesture ( bool on , const Point & initialPosition , const Point & finalPosition )
{
if ( ! on )
return ;
if ( ! hero )
return ;
if ( ! settings [ " input " ] [ " radialWheelGarrisonSwipe " ] . Bool ( ) )
return ;
std : : shared_ptr < CHeroGSlot > other = upg ? owner - > garrisonedHero : owner - > visitingHero ;
bool twoHeroes = hero & & other - > hero ;
ObjectInstanceID heroId = hero - > id ;
ObjectInstanceID heroOtherId = twoHeroes ? other - > hero - > id : ObjectInstanceID : : NONE ;
std : : vector < RadialMenuConfig > menuElements = {
{ RadialMenuConfig : : ITEM_NW , twoHeroes , " moveTroops " , " vcmi.radialWheel.heroGetArmy " , [ heroId , heroOtherId ] ( ) { CExchangeController ( heroId , heroOtherId ) . moveArmy ( false , std : : nullopt ) ; } } ,
{ RadialMenuConfig : : ITEM_NE , twoHeroes , " stackSplitDialog " , " vcmi.radialWheel.heroSwapArmy " , [ heroId , heroOtherId ] ( ) { CExchangeController ( heroId , heroOtherId ) . swapArmy ( ) ; } } ,
{ RadialMenuConfig : : ITEM_EE , twoHeroes , " tradeHeroes " , " vcmi.radialWheel.heroExchange " , [ heroId , heroOtherId ] ( ) { LOCPLINT - > showHeroExchange ( heroId , heroOtherId ) ; } } ,
{ RadialMenuConfig : : ITEM_SW , twoHeroes , " moveArtifacts " , " vcmi.radialWheel.heroGetArtifacts " , [ heroId , heroOtherId ] ( ) { CExchangeController ( heroId , heroOtherId ) . moveArtifacts ( false , true , true ) ; } } ,
{ RadialMenuConfig : : ITEM_SE , twoHeroes , " swapArtifacts " , " vcmi.radialWheel.heroSwapArtifacts " , [ heroId , heroOtherId ] ( ) { CExchangeController ( heroId , heroOtherId ) . swapArtifacts ( true , true ) ; } } ,
{ RadialMenuConfig : : ITEM_WW , true , " dismissHero " , " vcmi.radialWheel.heroDismiss " , [ this ] ( )
{
CFunctionList < void ( ) > ony = [ = ] ( ) { } ;
ony + = [ = ] ( ) { LOCPLINT - > cb - > dismissHero ( hero ) ; } ;
LOCPLINT - > showYesNoDialog ( CGI - > generaltexth - > allTexts [ 22 ] , ony , nullptr ) ;
} } ,
} ;
GH . windows ( ) . createAndPushWindow < RadialMenu > ( pos . center ( ) , menuElements ) ;
}
2023-10-19 16:19:09 +02:00
void CHeroGSlot : : hover ( bool on )
{
if ( ! on )
{
GH . statusbar ( ) - > clear ( ) ;
return ;
}
std : : shared_ptr < CHeroGSlot > other = upg ? owner - > garrisonedHero : owner - > visitingHero ;
std : : string temp ;
if ( hero )
{
if ( isSelected ( ) ) //view NNN
{
temp = CGI - > generaltexth - > tcommands [ 4 ] ;
boost : : algorithm : : replace_first ( temp , " %s " , hero - > getNameTranslated ( ) ) ;
}
else if ( other - > hero & & other - > isSelected ( ) ) //exchange
{
temp = CGI - > generaltexth - > tcommands [ 7 ] ;
boost : : algorithm : : replace_first ( temp , " %s " , hero - > getNameTranslated ( ) ) ;
boost : : algorithm : : replace_first ( temp , " %s " , other - > hero - > getNameTranslated ( ) ) ;
}
else // select NNN (in ZZZ)
{
if ( upg ) //down - visiting
{
temp = CGI - > generaltexth - > tcommands [ 32 ] ;
boost : : algorithm : : replace_first ( temp , " %s " , hero - > getNameTranslated ( ) ) ;
}
else //up - garrison
{
temp = CGI - > generaltexth - > tcommands [ 12 ] ;
boost : : algorithm : : replace_first ( temp , " %s " , hero - > getNameTranslated ( ) ) ;
}
}
}
else //we are empty slot
{
if ( other - > isSelected ( ) & & other - > hero ) //move NNNN
{
temp = CGI - > generaltexth - > tcommands [ 6 ] ;
boost : : algorithm : : replace_first ( temp , " %s " , other - > hero - > getNameTranslated ( ) ) ;
}
else //empty
{
temp = CGI - > generaltexth - > allTexts [ 507 ] ;
}
}
if ( temp . size ( ) )
GH . statusbar ( ) - > write ( temp ) ;
}
void CHeroGSlot : : clickPressed ( const Point & cursorPosition )
{
std : : shared_ptr < CHeroGSlot > other = upg ? owner - > garrisonedHero : owner - > visitingHero ;
owner - > garr - > setSplittingMode ( false ) ;
owner - > garr - > selectSlot ( nullptr ) ;
if ( hero & & isSelected ( ) )
{
setHighlight ( false ) ;
2024-02-18 21:02:24 +02:00
2024-07-15 20:22:19 +02:00
if ( other - > hero & & ! GH . isKeyboardShiftDown ( ) )
2024-02-18 21:02:24 +02:00
LOCPLINT - > showHeroExchange ( hero - > id , other - > hero - > id ) ;
else
LOCPLINT - > openHeroWindow ( hero ) ;
2023-10-19 16:19:09 +02:00
}
else if ( other - > hero & & other - > isSelected ( ) )
{
owner - > swapArmies ( ) ;
}
else if ( hero )
{
setHighlight ( true ) ;
owner - > garr - > selectSlot ( nullptr ) ;
redraw ( ) ;
}
//refresh statusbar
hover ( false ) ;
hover ( true ) ;
}
void CHeroGSlot : : showPopupWindow ( const Point & cursorPosition )
{
if ( hero )
{
GH . windows ( ) . createAndPushWindow < CInfoBoxPopup > ( Point ( pos . x + 175 , pos . y + 100 ) , hero ) ;
}
}
void CHeroGSlot : : deactivate ( )
{
selection - > visible = false ;
CIntObject : : deactivate ( ) ;
}
bool CHeroGSlot : : isSelected ( ) const
{
return selection - > visible ;
}
void CHeroGSlot : : setHighlight ( bool on )
{
selection - > visible = on ;
if ( owner - > garrisonedHero - > hero & & owner - > visitingHero - > hero ) //two heroes in town
{
2024-06-24 03:23:26 +02:00
for ( auto & elem : owner - > garr - > splitButtons ) //splitting enabled when slot highlighted
2023-10-19 16:19:09 +02:00
elem - > block ( ! on ) ;
}
}
void CHeroGSlot : : set ( const CGHeroInstance * newHero )
{
2024-08-09 17:30:04 +02:00
OBJECT_CONSTRUCTION ;
2023-10-19 16:19:09 +02:00
hero = newHero ;
selection - > visible = false ;
portrait - > visible = false ;
flag - > visible = false ;
if ( newHero )
{
portrait - > visible = true ;
portrait - > setFrame ( newHero - > getIconIndex ( ) ) ;
}
else if ( ! upg & & owner - > showEmpty ) //up garrison
{
flag - > visible = true ;
flag - > setFrame ( LOCPLINT - > castleInt - > town - > getOwner ( ) . getNum ( ) ) ;
}
}
HeroSlots : : HeroSlots ( const CGTownInstance * Town , Point garrPos , Point visitPos , std : : shared_ptr < CGarrisonInt > Garrison , bool ShowEmpty ) :
showEmpty ( ShowEmpty ) ,
town ( Town ) ,
garr ( Garrison )
{
2024-08-09 17:30:04 +02:00
OBJECT_CONSTRUCTION ;
2023-10-19 16:19:09 +02:00
garrisonedHero = std : : make_shared < CHeroGSlot > ( garrPos . x , garrPos . y , 0 , town - > garrisonHero , this ) ;
visitingHero = std : : make_shared < CHeroGSlot > ( visitPos . x , visitPos . y , 1 , town - > visitingHero , this ) ;
}
HeroSlots : : ~ HeroSlots ( ) = default ;
void HeroSlots : : update ( )
{
garrisonedHero - > set ( town - > garrisonHero ) ;
visitingHero - > set ( town - > visitingHero ) ;
}
void HeroSlots : : swapArmies ( )
{
bool allow = true ;
//moving hero out of town - check if it is allowed
if ( town - > garrisonHero )
{
2024-08-31 13:00:36 +02:00
if ( ! town - > visitingHero & & LOCPLINT - > cb - > howManyHeroes ( false ) > = LOCPLINT - > cb - > getSettings ( ) . getInteger ( EGameSettings : : HEROES_PER_PLAYER_ON_MAP_CAP ) )
2023-10-19 16:19:09 +02:00
{
std : : string text = CGI - > generaltexth - > translate ( " core.genrltxt.18 " ) ; //You already have %d adventuring heroes under your command.
boost : : algorithm : : replace_first ( text , " %d " , std : : to_string ( LOCPLINT - > cb - > howManyHeroes ( false ) ) ) ;
LOCPLINT - > showInfoDialog ( text , std : : vector < std : : shared_ptr < CComponent > > ( ) , soundBase : : sound_todo ) ;
allow = false ;
}
else if ( town - > garrisonHero - > stacksCount ( ) = = 0 )
{
//This hero has no creatures. A hero must have creatures before he can brave the dangers of the countryside.
LOCPLINT - > showInfoDialog ( CGI - > generaltexth - > translate ( " core.genrltxt.19 " ) , { } , soundBase : : sound_todo ) ;
allow = false ;
}
}
if ( ! town - > garrisonHero & & town - > visitingHero ) //visiting => garrison, merge armies: town army => hero army
{
if ( ! town - > visitingHero - > canBeMergedWith ( * town ) )
{
LOCPLINT - > showInfoDialog ( CGI - > generaltexth - > allTexts [ 275 ] , std : : vector < std : : shared_ptr < CComponent > > ( ) , soundBase : : sound_todo ) ;
allow = false ;
}
}
garrisonedHero - > setHighlight ( false ) ;
visitingHero - > setHighlight ( false ) ;
if ( allow )
LOCPLINT - > cb - > swapGarrisonHero ( town ) ;
}
CCastleBuildings : : CCastleBuildings ( const CGTownInstance * Town ) :
town ( Town ) ,
selectedBuilding ( nullptr )
{
2024-08-09 17:30:04 +02:00
OBJECT_CONSTRUCTION ;
2023-10-19 16:19:09 +02:00
background = std : : make_shared < CPicture > ( town - > town - > clientInfo . townBackground ) ;
background - > needRefresh = true ;
background - > getSurface ( ) - > setBlitMode ( EImageBlitMode : : OPAQUE ) ;
pos . w = background - > pos . w ;
pos . h = background - > pos . h ;
recreate ( ) ;
}
CCastleBuildings : : ~ CCastleBuildings ( ) = default ;
void CCastleBuildings : : recreate ( )
{
selectedBuilding = nullptr ;
2024-08-09 17:30:04 +02:00
OBJECT_CONSTRUCTION ;
2023-10-19 16:19:09 +02:00
buildings . clear ( ) ;
groups . clear ( ) ;
//Generate buildings list
2024-08-17 21:06:48 +02:00
auto buildingsCopy = town - > getBuildings ( ) ; // a bit modified copy of built buildings
2023-10-19 16:19:09 +02:00
2024-08-17 21:06:48 +02:00
if ( town - > hasBuilt ( BuildingID : : SHIPYARD ) )
2023-10-19 16:19:09 +02:00
{
auto bayPos = town - > bestLocation ( ) ;
if ( ! bayPos . valid ( ) )
logGlobal - > warn ( " Shipyard in non-coastal town! " ) ;
std : : vector < const CGObjectInstance * > vobjs = LOCPLINT - > cb - > getVisitableObjs ( bayPos , false ) ;
//there is visitable obj at shipyard output tile and it's a boat or hero (on boat)
if ( ! vobjs . empty ( ) & & ( vobjs . front ( ) - > ID = = Obj : : BOAT | | vobjs . front ( ) - > ID = = Obj : : HERO ) )
{
buildingsCopy . insert ( BuildingID : : SHIP ) ;
}
}
for ( const CStructure * structure : town - > town - > clientInfo . structures )
{
if ( ! structure - > building )
{
buildings . push_back ( std : : make_shared < CBuildingRect > ( this , town , structure ) ) ;
continue ;
}
if ( vstd : : contains ( buildingsCopy , structure - > building - > bid ) )
{
groups [ structure - > building - > getBase ( ) ] . push_back ( structure ) ;
}
}
for ( auto & entry : groups )
{
const CBuilding * build = town - > town - > buildings . at ( entry . first ) ;
const CStructure * toAdd = * boost : : max_element ( entry . second , [ = ] ( const CStructure * a , const CStructure * b )
{
return build - > getDistance ( a - > building - > bid ) < build - > getDistance ( b - > building - > bid ) ;
} ) ;
buildings . push_back ( std : : make_shared < CBuildingRect > ( this , town , toAdd ) ) ;
}
2024-04-27 12:17:45 +02:00
const auto & buildSorter = [ ] ( const CIntObject * a , const CIntObject * b )
2024-02-12 13:49:45 +02:00
{
auto b1 = dynamic_cast < const CBuildingRect * > ( a ) ;
auto b2 = dynamic_cast < const CBuildingRect * > ( b ) ;
if ( ! b1 & & ! b2 )
return intptr_t ( a ) < intptr_t ( b ) ;
if ( b1 & & ! b2 )
return false ;
if ( ! b1 & & b2 )
return true ;
return ( * b1 ) < ( * b2 ) ;
} ;
2023-10-19 16:19:09 +02:00
boost : : sort ( children , buildSorter ) ; //TODO: create building in blit order
}
void CCastleBuildings : : addBuilding ( BuildingID building )
{
//FIXME: implement faster method without complete recreation of town
BuildingID base = town - > town - > buildings . at ( building ) - > getBase ( ) ;
recreate ( ) ;
auto & structures = groups . at ( base ) ;
for ( auto buildingRect : buildings )
{
if ( vstd : : contains ( structures , buildingRect - > str ) )
{
//reset animation
if ( structures . size ( ) = = 1 )
buildingRect - > stateTimeCounter = 0 ; // transparency -> fully visible stage
else
buildingRect - > stateTimeCounter = CBuildingRect : : BUILDING_APPEAR_TIMEPOINT ; // already in fully visible stage
break ;
}
}
}
void CCastleBuildings : : removeBuilding ( BuildingID building )
{
//FIXME: implement faster method without complete recreation of town
recreate ( ) ;
}
const CGHeroInstance * CCastleBuildings : : getHero ( )
{
if ( town - > visitingHero )
return town - > visitingHero ;
else
return town - > garrisonHero ;
}
2024-08-26 23:25:39 +02:00
void CCastleBuildings : : buildingClicked ( BuildingID building )
2023-10-19 16:19:09 +02:00
{
2024-08-26 23:25:39 +02:00
BuildingID buildingToEnter = building ;
for ( ; ; )
{
const CBuilding * b = town - > town - > buildings . find ( buildingToEnter ) - > second ;
if ( buildingTryActivateCustomUI ( buildingToEnter , building ) )
return ;
if ( ! b - > upgrade . hasValue ( ) )
{
enterBuilding ( building ) ;
return ;
}
2023-10-19 16:19:09 +02:00
2024-08-26 23:25:39 +02:00
buildingToEnter = b - > upgrade ;
}
}
bool CCastleBuildings : : buildingTryActivateCustomUI ( BuildingID buildingToTest , BuildingID buildingTarget )
{
logGlobal - > trace ( " You've clicked on %d " , ( int ) buildingToTest . toEnum ( ) ) ;
const CBuilding * b = town - > town - > buildings . at ( buildingToTest ) ;
if ( town - > getWarMachineInBuilding ( buildingToTest ) . hasValue ( ) )
2024-08-23 17:29:47 +02:00
{
2024-08-26 23:25:39 +02:00
enterBlacksmith ( buildingTarget , town - > getWarMachineInBuilding ( buildingToTest ) ) ;
return true ;
2024-08-23 17:29:47 +02:00
}
2024-08-26 23:25:39 +02:00
if ( ! b - > marketModes . empty ( ) )
2023-10-19 16:19:09 +02:00
{
2024-08-26 23:25:39 +02:00
switch ( * b - > marketModes . begin ( ) )
{
case EMarketMode : : CREATURE_UNDEAD :
GH . windows ( ) . createAndPushWindow < CTransformerWindow > ( town , getHero ( ) , nullptr ) ;
return true ;
case EMarketMode : : RESOURCE_SKILL :
if ( getHero ( ) )
GH . windows ( ) . createAndPushWindow < CUniversityWindow > ( getHero ( ) , buildingTarget , town , nullptr ) ;
return true ;
case EMarketMode : : RESOURCE_RESOURCE :
// can't use allied marketplace
if ( town - > getOwner ( ) = = LOCPLINT - > playerID )
{
GH . windows ( ) . createAndPushWindow < CMarketWindow > ( town , getHero ( ) , nullptr , * b - > marketModes . begin ( ) ) ;
return true ;
}
else
return false ;
default :
if ( getHero ( ) )
GH . windows ( ) . createAndPushWindow < CMarketWindow > ( town , getHero ( ) , nullptr , * b - > marketModes . begin ( ) ) ;
else
LOCPLINT - > showInfoDialog ( boost : : str ( boost : : format ( CGI - > generaltexth - > allTexts [ 273 ] ) % b - > getNameTranslated ( ) ) ) ; //Only visiting heroes may use the %s.
return true ;
}
}
2024-09-03 18:31:07 +02:00
if ( town - > rewardableBuildings . count ( buildingToTest ) & & town - > town - > buildings . at ( buildingToTest ) - > manualHeroVisit )
{
enterRewardable ( buildingToTest ) ;
return true ;
}
2024-08-26 23:25:39 +02:00
if ( buildingToTest > = BuildingID : : DWELL_FIRST )
{
enterDwelling ( ( BuildingID : : getLevelFromDwelling ( buildingToTest ) ) ) ;
return true ;
2023-10-19 16:19:09 +02:00
}
else
{
2024-08-26 23:25:39 +02:00
switch ( buildingToTest )
2023-10-19 16:19:09 +02:00
{
case BuildingID : : MAGES_GUILD_1 :
case BuildingID : : MAGES_GUILD_2 :
case BuildingID : : MAGES_GUILD_3 :
case BuildingID : : MAGES_GUILD_4 :
case BuildingID : : MAGES_GUILD_5 :
enterMagesGuild ( ) ;
2024-08-26 23:25:39 +02:00
return true ;
2023-10-19 16:19:09 +02:00
case BuildingID : : TAVERN :
LOCPLINT - > showTavernWindow ( town , nullptr , QueryID : : NONE ) ;
2024-08-26 23:25:39 +02:00
return true ;
2023-10-19 16:19:09 +02:00
case BuildingID : : SHIPYARD :
if ( town - > shipyardStatus ( ) = = IBoatGenerator : : GOOD )
2024-08-26 23:25:39 +02:00
{
2023-10-19 16:19:09 +02:00
LOCPLINT - > showShipyardDialog ( town ) ;
2024-08-26 23:25:39 +02:00
return true ;
}
2023-10-19 16:19:09 +02:00
else if ( town - > shipyardStatus ( ) = = IBoatGenerator : : BOAT_ALREADY_BUILT )
2024-08-26 23:25:39 +02:00
{
2023-10-19 16:19:09 +02:00
LOCPLINT - > showInfoDialog ( CGI - > generaltexth - > allTexts [ 51 ] ) ;
2024-08-26 23:25:39 +02:00
return true ;
}
return false ;
2023-10-19 16:19:09 +02:00
case BuildingID : : FORT :
case BuildingID : : CITADEL :
case BuildingID : : CASTLE :
GH . windows ( ) . createAndPushWindow < CFortScreen > ( town ) ;
2024-08-26 23:25:39 +02:00
return true ;
2023-10-19 16:19:09 +02:00
case BuildingID : : VILLAGE_HALL :
case BuildingID : : CITY_HALL :
case BuildingID : : TOWN_HALL :
case BuildingID : : CAPITOL :
enterTownHall ( ) ;
2024-08-26 23:25:39 +02:00
return true ;
2023-10-19 16:19:09 +02:00
case BuildingID : : SHIP :
LOCPLINT - > showInfoDialog ( CGI - > generaltexth - > allTexts [ 51 ] ) ; //Cannot build another boat
2024-08-26 23:25:39 +02:00
return true ;
2023-10-19 16:19:09 +02:00
case BuildingID : : SPECIAL_1 :
case BuildingID : : SPECIAL_2 :
case BuildingID : : SPECIAL_3 :
2024-05-30 21:58:35 +02:00
case BuildingID : : SPECIAL_4 :
2024-08-26 23:25:39 +02:00
switch ( b - > subId )
2023-10-19 16:19:09 +02:00
{
case BuildingSubID : : MYSTIC_POND :
2024-08-26 23:25:39 +02:00
enterFountain ( buildingToTest , b - > subId , buildingTarget ) ;
return true ;
2023-10-19 16:19:09 +02:00
case BuildingSubID : : CASTLE_GATE :
2024-04-28 14:58:54 +02:00
if ( LOCPLINT - > makingTurn )
2024-08-26 23:25:39 +02:00
{
2024-04-28 14:58:54 +02:00
enterCastleGate ( ) ;
2024-08-26 23:25:39 +02:00
return true ;
}
return false ;
2023-10-19 16:19:09 +02:00
case BuildingSubID : : PORTAL_OF_SUMMONING :
2024-08-05 23:03:19 +02:00
if ( town - > creatures [ town - > town - > creatures . size ( ) ] . second . empty ( ) ) //No creatures
2023-10-19 16:19:09 +02:00
LOCPLINT - > showInfoDialog ( CGI - > generaltexth - > tcommands [ 30 ] ) ;
else
2024-08-05 23:03:19 +02:00
enterDwelling ( town - > town - > creatures . size ( ) ) ;
2024-08-26 23:25:39 +02:00
return true ;
2023-10-19 16:19:09 +02:00
2024-08-04 15:44:57 +02:00
case BuildingSubID : : BANK :
2024-09-03 18:31:07 +02:00
enterBank ( buildingTarget ) ;
2024-08-26 23:25:39 +02:00
return true ;
2023-10-19 16:19:09 +02:00
}
}
}
2024-08-28 16:04:47 +02:00
for ( auto const & bonus : b - > buildingBonuses )
{
if ( bonus - > type = = BonusType : : THIEVES_GUILD_ACCESS )
{
enterAnyThievesGuild ( ) ;
return true ;
}
}
return false ;
2023-10-19 16:19:09 +02:00
}
2024-09-03 18:31:07 +02:00
void CCastleBuildings : : enterRewardable ( BuildingID building )
{
2024-09-15 12:36:31 +02:00
if ( town - > visitingHero = = nullptr )
{
MetaString message ;
message . appendTextID ( " core.genrltxt.273 " ) ; // only visiting heroes may visit %s
message . replaceTextID ( town - > town - > buildings . at ( building ) - > getNameTextID ( ) ) ;
LOCPLINT - > showInfoDialog ( message . toString ( ) ) ;
}
else
{
2024-09-16 21:51:10 +02:00
if ( town - > rewardableBuildings . at ( building ) - > wasVisited ( town - > visitingHero ) )
enterBuilding ( building ) ;
else
LOCPLINT - > cb - > visitTownBuilding ( town , building ) ;
2024-09-15 12:36:31 +02:00
}
2024-09-03 18:31:07 +02:00
}
2024-08-23 17:29:47 +02:00
void CCastleBuildings : : enterBlacksmith ( BuildingID building , ArtifactID artifactID )
2023-10-19 16:19:09 +02:00
{
const CGHeroInstance * hero = town - > visitingHero ;
if ( ! hero )
{
2024-08-23 17:29:47 +02:00
LOCPLINT - > showInfoDialog ( boost : : str ( boost : : format ( CGI - > generaltexth - > allTexts [ 273 ] ) % town - > town - > buildings . find ( building ) - > second - > getNameTranslated ( ) ) ) ;
2023-10-19 16:19:09 +02:00
return ;
}
auto art = artifactID . toArtifact ( ) ;
int price = art - > getPrice ( ) ;
bool possible = LOCPLINT - > cb - > getResourceAmount ( EGameResID : : GOLD ) > = price ;
if ( possible )
{
for ( auto slot : art - > getPossibleSlots ( ) . at ( ArtBearer : : HERO ) )
{
2024-08-23 17:42:23 +02:00
if ( hero - > getArt ( slot ) = = nullptr | | hero - > getArt ( slot ) - > getTypeId ( ) ! = artifactID )
2023-10-19 16:19:09 +02:00
{
possible = true ;
break ;
}
else
{
possible = false ;
}
}
}
2024-08-23 17:29:47 +02:00
CreatureID creatureID = artifactID . toArtifact ( ) - > getWarMachine ( ) ;
GH . windows ( ) . createAndPushWindow < CBlacksmithDialog > ( possible , creatureID , artifactID , hero - > id ) ;
2023-10-19 16:19:09 +02:00
}
void CCastleBuildings : : enterBuilding ( BuildingID building )
{
2023-10-31 11:09:56 +02:00
std : : vector < std : : shared_ptr < CComponent > > comps ( 1 , std : : make_shared < CComponent > ( ComponentType : : BUILDING , BuildingTypeUniqueID ( town - > getFaction ( ) , building ) ) ) ;
2023-10-19 16:19:09 +02:00
LOCPLINT - > showInfoDialog ( town - > town - > buildings . find ( building ) - > second - > getDescriptionTranslated ( ) , comps ) ;
}
void CCastleBuildings : : enterCastleGate ( )
{
if ( ! town - > visitingHero )
{
LOCPLINT - > showInfoDialog ( CGI - > generaltexth - > allTexts [ 126 ] ) ;
return ; //only visiting hero can use castle gates
}
std : : vector < int > availableTowns ;
2024-04-16 00:54:04 +02:00
std : : vector < std : : shared_ptr < IImage > > images ;
2024-01-31 22:29:04 +02:00
std : : vector < const CGTownInstance * > Towns = LOCPLINT - > localState - > getOwnedTowns ( ) ;
2023-10-19 16:19:09 +02:00
for ( auto & Town : Towns )
{
const CGTownInstance * t = Town ;
if ( t - > id ! = this - > town - > id & & t - > visitingHero = = nullptr & & //another town, empty and this is
t - > town - > faction - > getId ( ) = = town - > town - > faction - > getId ( ) & & //the town of the same faction
t - > hasBuilt ( BuildingSubID : : CASTLE_GATE ) ) //and the town has a castle gate
{
availableTowns . push_back ( t - > id . getNum ( ) ) ; //add to the list
2024-04-16 00:54:04 +02:00
if ( settings [ " general " ] [ " enableUiEnhancements " ] . Bool ( ) )
{
2024-06-08 09:35:13 +02:00
auto image = GH . renderHandler ( ) . loadImage ( AnimationPath : : builtin ( " ITPA " ) , t - > town - > clientInfo . icons [ t - > hasFort ( ) ] [ false ] + 2 , 0 , EImageBlitMode : : OPAQUE ) ;
2024-07-25 20:22:27 +02:00
image - > scaleTo ( Point ( 35 , 23 ) ) ;
2024-06-04 13:46:45 +02:00
images . push_back ( image ) ;
2024-04-16 00:54:04 +02:00
}
2023-10-19 16:19:09 +02:00
}
}
2024-01-31 03:30:10 +02:00
2023-10-19 16:19:09 +02:00
auto gateIcon = std : : make_shared < CAnimImage > ( town - > town - > clientInfo . buildingsIcons , BuildingID : : CASTLE_GATE ) ; //will be deleted by selection window
2024-01-31 03:30:10 +02:00
auto wnd = std : : make_shared < CObjectListWindow > ( availableTowns , gateIcon , CGI - > generaltexth - > jktexts [ 40 ] ,
CGI - > generaltexth - > jktexts [ 41 ] , std : : bind ( & CCastleInterface : : castleTeleport , LOCPLINT - > castleInt , _1 ) , 0 , images ) ;
2024-04-16 00:54:04 +02:00
wnd - > onPopup = [ availableTowns ] ( int index ) { CRClickPopup : : createAndPush ( LOCPLINT - > cb - > getObjInstance ( ObjectInstanceID ( availableTowns [ index ] ) ) , GH . getCursorPosition ( ) ) ; } ;
2024-01-31 03:30:10 +02:00
GH . windows ( ) . pushWindow ( wnd ) ;
2023-10-19 16:19:09 +02:00
}
void CCastleBuildings : : enterDwelling ( int level )
{
if ( level < 0 | | level > = town - > creatures . size ( ) | | town - > creatures [ level ] . second . empty ( ) )
{
assert ( 0 ) ;
logGlobal - > error ( " Attempt to enter into invalid dwelling of level %d in town %s (%s) " , level , town - > getNameTranslated ( ) , town - > town - > faction - > getNameTranslated ( ) ) ;
return ;
}
auto recruitCb = [ = ] ( CreatureID id , int count )
{
LOCPLINT - > cb - > recruitCreatures ( town , town - > getUpperArmy ( ) , id , count , level ) ;
} ;
2023-11-28 16:25:11 +02:00
GH . windows ( ) . createAndPushWindow < CRecruitmentWindow > ( town , level , town - > getUpperArmy ( ) , recruitCb , nullptr , - 87 ) ;
2023-10-19 16:19:09 +02:00
}
void CCastleBuildings : : enterToTheQuickRecruitmentWindow ( )
{
const auto beginIt = town - > creatures . cbegin ( ) ;
2024-08-05 23:03:19 +02:00
const auto afterLastIt = town - > creatures . size ( ) > town - > town - > creatures . size ( )
? std : : next ( beginIt , town - > town - > creatures . size ( ) )
2023-10-19 16:19:09 +02:00
: town - > creatures . cend ( ) ;
const auto hasSomeoneToRecruit = std : : any_of ( beginIt , afterLastIt ,
[ ] ( const auto & creatureInfo ) { return creatureInfo . first > 0 ; } ) ;
if ( hasSomeoneToRecruit )
GH . windows ( ) . createAndPushWindow < QuickRecruitmentWindow > ( town , pos ) ;
else
CInfoWindow : : showInfoDialog ( CGI - > generaltexth - > translate ( " vcmi.townHall.noCreaturesToRecruit " ) , { } ) ;
}
void CCastleBuildings : : enterFountain ( const BuildingID & building , BuildingSubID : : EBuildingSubID subID , BuildingID upgrades )
{
2023-10-31 11:09:56 +02:00
std : : vector < std : : shared_ptr < CComponent > > comps ( 1 , std : : make_shared < CComponent > ( ComponentType : : BUILDING , BuildingTypeUniqueID ( town - > getFaction ( ) , building ) ) ) ;
2023-10-19 16:19:09 +02:00
std : : string descr = town - > town - > buildings . find ( building ) - > second - > getDescriptionTranslated ( ) ;
std : : string hasNotProduced ;
std : : string hasProduced ;
2024-08-17 16:41:26 +02:00
hasNotProduced = CGI - > generaltexth - > allTexts [ 677 ] ;
hasProduced = CGI - > generaltexth - > allTexts [ 678 ] ;
2023-10-19 16:19:09 +02:00
bool isMysticPondOrItsUpgrade = subID = = BuildingSubID : : MYSTIC_POND
| | ( upgrades ! = BuildingID : : NONE
& & town - > town - > buildings . find ( BuildingID ( upgrades ) ) - > second - > subId = = BuildingSubID : : MYSTIC_POND ) ;
if ( upgrades ! = BuildingID : : NONE )
descr + = " \n \n " + town - > town - > buildings . find ( BuildingID ( upgrades ) ) - > second - > getDescriptionTranslated ( ) ;
if ( isMysticPondOrItsUpgrade ) //for vanila Rampart like towns
{
if ( town - > bonusValue . first = = 0 ) //Mystic Pond produced nothing;
descr + = " \n \n " + hasNotProduced ;
else //Mystic Pond produced something;
{
descr + = " \n \n " + hasProduced ;
boost : : algorithm : : replace_first ( descr , " %s " , CGI - > generaltexth - > restypes [ town - > bonusValue . first ] ) ;
boost : : algorithm : : replace_first ( descr , " %d " , std : : to_string ( town - > bonusValue . second ) ) ;
}
}
LOCPLINT - > showInfoDialog ( descr , comps ) ;
}
void CCastleBuildings : : enterMagesGuild ( )
{
const CGHeroInstance * hero = getHero ( ) ;
2024-04-28 14:58:54 +02:00
// hero doesn't have spellbok
// or it is not our turn and we can't make actions
if ( hero & & ! hero - > hasSpellbook ( ) & & LOCPLINT - > makingTurn )
2023-10-19 16:19:09 +02:00
{
2024-01-30 23:33:58 +02:00
if ( hero - > isCampaignYog ( ) )
2023-10-19 16:19:09 +02:00
{
// "Yog has given up magic in all its forms..."
LOCPLINT - > showInfoDialog ( CGI - > generaltexth - > allTexts [ 736 ] ) ;
}
else if ( LOCPLINT - > cb - > getResourceAmount ( EGameResID : : GOLD ) < 500 ) //not enough gold to buy spellbook
{
openMagesGuild ( ) ;
LOCPLINT - > showInfoDialog ( CGI - > generaltexth - > allTexts [ 213 ] ) ;
}
else
{
CFunctionList < void ( ) > onYes = [ this ] ( ) { openMagesGuild ( ) ; } ;
CFunctionList < void ( ) > onNo = onYes ;
onYes + = [ hero ] ( ) { LOCPLINT - > cb - > buyArtifact ( hero , ArtifactID : : SPELLBOOK ) ; } ;
2023-10-31 11:09:56 +02:00
std : : vector < std : : shared_ptr < CComponent > > components ( 1 , std : : make_shared < CComponent > ( ComponentType : : ARTIFACT , ArtifactID ( ArtifactID : : SPELLBOOK ) ) ) ;
2023-10-19 16:19:09 +02:00
LOCPLINT - > showYesNoDialog ( CGI - > generaltexth - > allTexts [ 214 ] , onYes , onNo , components ) ;
}
}
else
{
openMagesGuild ( ) ;
}
}
void CCastleBuildings : : enterTownHall ( )
{
if ( town - > visitingHero & & town - > visitingHero - > hasArt ( ArtifactID : : GRAIL ) & &
2024-08-17 21:06:48 +02:00
! town - > hasBuilt ( BuildingID : : GRAIL ) ) //hero has grail, but town does not have it
2023-10-19 16:19:09 +02:00
{
if ( ! vstd : : contains ( town - > forbiddenBuildings , BuildingID : : GRAIL ) )
{
LOCPLINT - > showYesNoDialog ( CGI - > generaltexth - > allTexts [ 597 ] , //Do you wish this to be the permanent home of the Grail?
[ & ] ( ) { LOCPLINT - > cb - > buildBuilding ( town , BuildingID : : GRAIL ) ; } ,
[ & ] ( ) { openTownHall ( ) ; } ) ;
}
else
{
LOCPLINT - > showInfoDialog ( CGI - > generaltexth - > allTexts [ 673 ] ) ;
assert ( GH . windows ( ) . topWindow < CInfoWindow > ( ) ! = nullptr ) ;
GH . windows ( ) . topWindow < CInfoWindow > ( ) - > buttons [ 0 ] - > addCallback ( std : : bind ( & CCastleBuildings : : openTownHall , this ) ) ;
}
}
else
{
openTownHall ( ) ;
}
}
void CCastleBuildings : : openMagesGuild ( )
{
auto mageGuildBackground = LOCPLINT - > castleInt - > town - > town - > clientInfo . guildBackground ;
GH . windows ( ) . createAndPushWindow < CMageGuildScreen > ( LOCPLINT - > castleInt , mageGuildBackground ) ;
}
void CCastleBuildings : : openTownHall ( )
{
GH . windows ( ) . createAndPushWindow < CHallInterface > ( town ) ;
}
2024-05-18 22:45:06 +02:00
void CCastleBuildings : : enterAnyThievesGuild ( )
{
std : : vector < const CGTownInstance * > towns = LOCPLINT - > cb - > getTownsInfo ( true ) ;
2024-06-01 13:48:30 +02:00
for ( auto & ownedTown : towns )
2024-05-18 22:45:06 +02:00
{
2024-08-17 21:06:48 +02:00
if ( ownedTown - > hasBuilt ( BuildingID : : TAVERN ) )
2024-05-18 22:45:06 +02:00
{
2024-06-01 13:48:30 +02:00
LOCPLINT - > showThievesGuildWindow ( ownedTown ) ;
2024-05-18 22:45:06 +02:00
return ;
}
}
LOCPLINT - > showInfoDialog ( CGI - > generaltexth - > translate ( " vcmi.adventureMap.noTownWithTavern " ) ) ;
}
2024-09-03 18:31:07 +02:00
void CCastleBuildings : : enterBank ( BuildingID building )
2024-08-04 15:44:57 +02:00
{
2024-08-04 17:52:40 +02:00
std : : vector < std : : shared_ptr < CComponent > > components ;
2024-08-04 18:25:21 +02:00
if ( town - > bonusValue . second > 0 )
{
2024-08-09 23:35:47 +02:00
components . push_back ( std : : make_shared < CComponent > ( ComponentType : : RESOURCE_PER_DAY , GameResID ( GameResID : : GOLD ) , - 500 ) ) ;
2024-08-04 18:25:21 +02:00
LOCPLINT - > showInfoDialog ( CGI - > generaltexth - > translate ( " vcmi.townStructure.bank.payBack " ) , components ) ;
}
else {
components . push_back ( std : : make_shared < CComponent > ( ComponentType : : RESOURCE , GameResID ( GameResID : : GOLD ) , 2500 ) ) ;
2024-09-03 18:31:07 +02:00
LOCPLINT - > showYesNoDialog ( CGI - > generaltexth - > translate ( " vcmi.townStructure.bank.borrow " ) , [ this , building ] ( ) { LOCPLINT - > cb - > visitTownBuilding ( town , building ) ; } , nullptr , components ) ;
2024-08-04 18:25:21 +02:00
}
2024-08-04 15:44:57 +02:00
}
2024-05-18 22:45:06 +02:00
void CCastleBuildings : : enterAnyMarket ( )
{
2024-08-17 21:06:48 +02:00
if ( town - > hasBuilt ( BuildingID : : MARKETPLACE ) )
2024-05-18 22:45:06 +02:00
{
GH . windows ( ) . createAndPushWindow < CMarketWindow > ( town , nullptr , nullptr , EMarketMode : : RESOURCE_RESOURCE ) ;
return ;
}
std : : vector < const CGTownInstance * > towns = LOCPLINT - > cb - > getTownsInfo ( true ) ;
for ( auto & town : towns )
{
2024-08-17 21:06:48 +02:00
if ( town - > hasBuilt ( BuildingID : : MARKETPLACE ) )
2024-05-18 22:45:06 +02:00
{
GH . windows ( ) . createAndPushWindow < CMarketWindow > ( town , nullptr , nullptr , EMarketMode : : RESOURCE_RESOURCE ) ;
return ;
}
}
LOCPLINT - > showInfoDialog ( CGI - > generaltexth - > translate ( " vcmi.adventureMap.noTownWithMarket " ) ) ;
}
2023-10-19 16:19:09 +02:00
CCreaInfo : : CCreaInfo ( Point position , const CGTownInstance * Town , int Level , bool compact , bool _showAvailable ) :
town ( Town ) ,
level ( Level ) ,
showAvailable ( _showAvailable )
{
2024-08-09 17:30:04 +02:00
OBJECT_CONSTRUCTION ;
2023-10-19 16:19:09 +02:00
pos + = position ;
if ( town - > creatures . size ( ) < = level | | town - > creatures [ level ] . second . empty ( ) )
{
level = - 1 ;
return ; //No creature
}
addUsedEvents ( LCLICK | SHOW_POPUP | HOVER ) ;
2024-06-03 15:37:35 +02:00
creature = town - > creatures [ level ] . second . back ( ) ;
2023-10-19 16:19:09 +02:00
2024-06-03 15:37:35 +02:00
picture = std : : make_shared < CAnimImage > ( AnimationPath : : builtin ( " CPRSMALL " ) , creature . toEntity ( VLC ) - > getIconIndex ( ) , 0 , 8 , 0 ) ;
2023-10-19 16:19:09 +02:00
std : : string value ;
if ( showAvailable )
value = std : : to_string ( town - > creatures [ level ] . first ) ;
else
value = std : : string ( " + " ) + std : : to_string ( town - > creatureGrowth ( level ) ) ;
if ( compact )
{
label = std : : make_shared < CLabel > ( 40 , 32 , FONT_TINY , ETextAlignment : : BOTTOMRIGHT , Colors : : WHITE , value ) ;
pos . x + = 8 ;
pos . w = 32 ;
pos . h = 32 ;
}
else
{
label = std : : make_shared < CLabel > ( 24 , 40 , FONT_SMALL , ETextAlignment : : CENTER , Colors : : WHITE , value ) ;
pos . w = 48 ;
pos . h = 48 ;
}
}
void CCreaInfo : : update ( )
{
if ( label )
{
std : : string value ;
if ( showAvailable )
value = std : : to_string ( town - > creatures [ level ] . first ) ;
else
value = std : : string ( " + " ) + std : : to_string ( town - > creatureGrowth ( level ) ) ;
if ( value ! = label - > getText ( ) )
label - > setText ( value ) ;
}
}
void CCreaInfo : : hover ( bool on )
{
2024-06-03 15:37:35 +02:00
MetaString message ;
message . appendTextID ( " core.genrltxt.588 " ) ;
message . replaceNameSingular ( creature ) ;
2023-10-19 16:19:09 +02:00
if ( on )
{
2024-06-03 15:37:35 +02:00
GH . statusbar ( ) - > write ( message . toString ( ) ) ;
2023-10-19 16:19:09 +02:00
}
else
{
2024-06-03 15:37:35 +02:00
GH . statusbar ( ) - > clearIfMatching ( message . toString ( ) ) ;
2023-10-19 16:19:09 +02:00
}
}
void CCreaInfo : : clickPressed ( const Point & cursorPosition )
{
int offset = LOCPLINT - > castleInt ? ( - 87 ) : 0 ;
auto recruitCb = [ = ] ( CreatureID id , int count )
{
LOCPLINT - > cb - > recruitCreatures ( town , town - > getUpperArmy ( ) , id , count , level ) ;
} ;
2023-11-28 16:25:11 +02:00
GH . windows ( ) . createAndPushWindow < CRecruitmentWindow > ( town , level , town - > getUpperArmy ( ) , recruitCb , nullptr , offset ) ;
2023-10-19 16:19:09 +02:00
}
std : : string CCreaInfo : : genGrowthText ( )
{
GrowthInfo gi = town - > getGrowthInfo ( level ) ;
2024-06-03 15:37:35 +02:00
MetaString descr ;
descr . appendTextID ( " core.genrltxt.589 " ) ;
descr . replaceNameSingular ( creature ) ;
descr . replaceNumber ( gi . totalGrowth ( ) ) ;
2023-10-19 16:19:09 +02:00
for ( const GrowthInfo : : Entry & entry : gi . entries )
2024-06-03 15:37:35 +02:00
{
descr . appendEOL ( ) ;
descr . appendRawString ( entry . description ) ;
}
2023-10-19 16:19:09 +02:00
2024-06-03 15:37:35 +02:00
return descr . toString ( ) ;
2023-10-19 16:19:09 +02:00
}
void CCreaInfo : : showPopupWindow ( const Point & cursorPosition )
{
if ( showAvailable )
GH . windows ( ) . createAndPushWindow < CDwellingInfoBox > ( GH . screenDimensions ( ) . x / 2 , GH . screenDimensions ( ) . y / 2 , town , level ) ;
else
2024-06-03 15:37:35 +02:00
CRClickPopup : : createAndPush ( genGrowthText ( ) , std : : make_shared < CComponent > ( ComponentType : : CREATURE , creature ) ) ;
2023-10-19 16:19:09 +02:00
}
bool CCreaInfo : : getShowAvailable ( )
{
return showAvailable ;
}
CTownInfo : : CTownInfo ( int posX , int posY , const CGTownInstance * Town , bool townHall )
: town ( Town ) ,
building ( nullptr )
{
2024-08-09 17:30:04 +02:00
OBJECT_CONSTRUCTION ;
2023-10-19 16:19:09 +02:00
addUsedEvents ( SHOW_POPUP | HOVER ) ;
pos . x + = posX ;
pos . y + = posY ;
int buildID ;
if ( townHall )
{
buildID = 10 + town - > hallLevel ( ) ;
picture = std : : make_shared < CAnimImage > ( AnimationPath : : builtin ( " ITMTL.DEF " ) , town - > hallLevel ( ) ) ;
}
else
{
buildID = 6 + town - > fortLevel ( ) ;
if ( buildID = = 6 )
return ; //FIXME: suspicious statement, fix or comment
picture = std : : make_shared < CAnimImage > ( AnimationPath : : builtin ( " ITMCL.DEF " ) , town - > fortLevel ( ) - 1 ) ;
}
building = town - > town - > buildings . at ( BuildingID ( buildID ) ) ;
pos = picture - > pos ;
}
void CTownInfo : : hover ( bool on )
{
if ( on )
{
if ( building )
GH . statusbar ( ) - > write ( building - > getNameTranslated ( ) ) ;
}
else
{
GH . statusbar ( ) - > clear ( ) ;
}
}
void CTownInfo : : showPopupWindow ( const Point & cursorPosition )
{
if ( building )
{
2023-10-31 11:09:56 +02:00
auto c = std : : make_shared < CComponent > ( ComponentType : : BUILDING , BuildingTypeUniqueID ( building - > town - > faction - > getId ( ) , building - > bid ) ) ;
2023-10-19 16:19:09 +02:00
CRClickPopup : : createAndPush ( CInfoWindow : : genText ( building - > getNameTranslated ( ) , building - > getDescriptionTranslated ( ) ) , c ) ;
}
}
CCastleInterface : : CCastleInterface ( const CGTownInstance * Town , const CGTownInstance * from ) :
2024-04-23 15:21:45 +02:00
CWindowObject ( PLAYER_COLORED | BORDERED ) ,
2023-10-19 16:19:09 +02:00
town ( Town )
{
2024-08-09 17:30:04 +02:00
OBJECT_CONSTRUCTION ;
2023-10-19 16:19:09 +02:00
LOCPLINT - > castleInt = this ;
addUsedEvents ( KEYBOARD ) ;
builds = std : : make_shared < CCastleBuildings > ( town ) ;
panel = std : : make_shared < CPicture > ( ImagePath : : builtin ( " TOWNSCRN " ) , 0 , builds - > pos . h ) ;
2024-06-05 17:50:50 +02:00
panel - > setPlayerColor ( LOCPLINT - > playerID ) ;
2023-10-19 16:19:09 +02:00
pos . w = panel - > pos . w ;
pos . h = builds - > pos . h + panel - > pos . h ;
center ( ) ;
updateShadow ( ) ;
garr = std : : make_shared < CGarrisonInt > ( Point ( 305 , 387 ) , 4 , Point ( 0 , 96 ) , town - > getUpperArmy ( ) , town - > visitingHero ) ;
garr - > setRedrawParent ( true ) ;
heroes = std : : make_shared < HeroSlots > ( town , Point ( 241 , 387 ) , Point ( 241 , 483 ) , garr , true ) ;
title = std : : make_shared < CLabel > ( 85 , 387 , FONT_MEDIUM , ETextAlignment : : TOPLEFT , Colors : : WHITE , town - > getNameTranslated ( ) ) ;
income = std : : make_shared < CLabel > ( 195 , 443 , FONT_SMALL , ETextAlignment : : CENTER ) ;
icon = std : : make_shared < CAnimImage > ( AnimationPath : : builtin ( " ITPT " ) , 0 , 0 , 15 , 387 ) ;
exit = std : : make_shared < CButton > ( Point ( 744 , 544 ) , AnimationPath : : builtin ( " TSBTNS " ) , CButton : : tooltip ( CGI - > generaltexth - > tcommands [ 8 ] ) , [ & ] ( ) { close ( ) ; } , EShortcut : : GLOBAL_RETURN ) ;
exit - > setImageOrder ( 4 , 5 , 6 , 7 ) ;
2024-05-18 21:59:28 +02:00
auto split = std : : make_shared < CButton > ( Point ( 744 , 382 ) , AnimationPath : : builtin ( " TSBTNS " ) , CButton : : tooltip ( CGI - > generaltexth - > tcommands [ 3 ] ) , [ this ] ( ) { garr - > splitClick ( ) ; } , EShortcut : : HERO_ARMY_SPLIT ) ;
2023-10-19 16:19:09 +02:00
garr - > addSplitBtn ( split ) ;
Rect barRect ( 9 , 182 , 732 , 18 ) ;
auto statusbarBackground = std : : make_shared < CPicture > ( panel - > getSurface ( ) , barRect , 9 , 555 ) ;
statusbar = CGStatusBar : : create ( statusbarBackground ) ;
resdatabar = std : : make_shared < CResDataBar > ( ImagePath : : builtin ( " ARESBAR " ) , 3 , 575 , 37 , 3 , 84 , 78 ) ;
townlist = std : : make_shared < CTownList > ( 3 , Rect ( Point ( 743 , 414 ) , Point ( 48 , 128 ) ) , Point ( 1 , 16 ) , Point ( 0 , 32 ) , LOCPLINT - > localState - > getOwnedTowns ( ) . size ( ) ) ;
2024-07-03 18:52:50 +02:00
townlist - > setScrollUpButton ( std : : make_shared < CButton > ( Point ( 744 , 414 ) , AnimationPath : : builtin ( " IAM014 " ) , CButton : : tooltipLocalized ( " core.help.306 " ) , 0 ) ) ;
townlist - > setScrollDownButton ( std : : make_shared < CButton > ( Point ( 744 , 526 ) , AnimationPath : : builtin ( " IAM015 " ) , CButton : : tooltipLocalized ( " core.help.307 " ) , 0 ) ) ;
2023-10-19 16:19:09 +02:00
if ( from )
townlist - > select ( from ) ;
townlist - > select ( town ) ; //this will scroll list to select current town
townlist - > onSelect = std : : bind ( & CCastleInterface : : townChange , this ) ;
recreateIcons ( ) ;
if ( ! from )
adventureInt - > onAudioPaused ( ) ;
2024-07-15 23:45:51 +02:00
CCS - > musich - > playMusicFromSet ( " faction " , town - > town - > faction - > getJsonKey ( ) , true , false ) ;
2023-10-19 16:19:09 +02:00
}
CCastleInterface : : ~ CCastleInterface ( )
{
// resume map audio if:
// adventureInt exists (may happen on exiting client with open castle interface)
// castleInt has not been replaced (happens on switching between towns inside castle interface)
if ( adventureInt & & LOCPLINT - > castleInt = = this )
adventureInt - > onAudioResumed ( ) ;
if ( LOCPLINT - > castleInt = = this )
LOCPLINT - > castleInt = nullptr ;
}
void CCastleInterface : : updateGarrisons ( )
{
garr - > setArmy ( town - > getUpperArmy ( ) , EGarrisonType : : UPPER ) ;
garr - > setArmy ( town - > visitingHero , EGarrisonType : : LOWER ) ;
garr - > recreateSlots ( ) ;
heroes - > update ( ) ;
redraw ( ) ;
}
2023-11-09 00:01:48 +02:00
bool CCastleInterface : : holdsGarrison ( const CArmedInstance * army )
{
return army = = town | | army = = town - > getUpperArmy ( ) | | army = = town - > visitingHero ;
}
2023-10-19 16:19:09 +02:00
void CCastleInterface : : close ( )
{
if ( town - > tempOwner = = LOCPLINT - > playerID ) //we may have opened window for an allied town
{
if ( town - > visitingHero & & town - > visitingHero - > tempOwner = = LOCPLINT - > playerID )
LOCPLINT - > localState - > setSelection ( town - > visitingHero ) ;
else
LOCPLINT - > localState - > setSelection ( town ) ;
}
CWindowObject : : close ( ) ;
}
void CCastleInterface : : castleTeleport ( int where )
{
const CGTownInstance * dest = LOCPLINT - > cb - > getTown ( ObjectInstanceID ( where ) ) ;
LOCPLINT - > localState - > setSelection ( town - > visitingHero ) ; //according to assert(ho == adventureInt->selection) in the eraseCurrentPathOf
LOCPLINT - > cb - > teleportHero ( town - > visitingHero , dest ) ;
LOCPLINT - > localState - > erasePath ( town - > visitingHero ) ;
}
void CCastleInterface : : townChange ( )
{
//TODO: do not recreate window
const CGTownInstance * dest = LOCPLINT - > localState - > getOwnedTown ( townlist - > getSelectedIndex ( ) ) ;
const CGTownInstance * town = this - > town ; // "this" is going to be deleted
if ( dest = = town )
return ;
close ( ) ;
GH . windows ( ) . createAndPushWindow < CCastleInterface > ( dest , town ) ;
}
void CCastleInterface : : addBuilding ( BuildingID bid )
{
deactivate ( ) ;
builds - > addBuilding ( bid ) ;
recreateIcons ( ) ;
activate ( ) ;
redraw ( ) ;
}
void CCastleInterface : : removeBuilding ( BuildingID bid )
{
deactivate ( ) ;
builds - > removeBuilding ( bid ) ;
recreateIcons ( ) ;
activate ( ) ;
redraw ( ) ;
}
void CCastleInterface : : recreateIcons ( )
{
2024-08-09 17:30:04 +02:00
OBJECT_CONSTRUCTION ;
2024-08-31 13:00:36 +02:00
size_t iconIndex = town - > town - > clientInfo . icons [ town - > hasFort ( ) ] [ town - > built > = LOCPLINT - > cb - > getSettings ( ) . getInteger ( EGameSettings : : TOWNS_BUILDINGS_PER_TURN_CAP ) ] ;
2023-10-19 16:19:09 +02:00
icon - > setFrame ( iconIndex ) ;
TResources townIncome = town - > dailyIncome ( ) ;
income - > setText ( std : : to_string ( townIncome [ EGameResID : : GOLD ] ) ) ;
hall = std : : make_shared < CTownInfo > ( 80 , 413 , town , true ) ;
fort = std : : make_shared < CTownInfo > ( 122 , 413 , town , false ) ;
2024-05-18 21:59:28 +02:00
fastTownHall = std : : make_shared < CButton > ( Point ( 80 , 413 ) , AnimationPath : : builtin ( " castleInterfaceQuickAccess " ) , CButton : : tooltip ( ) , [ this ] ( ) { builds - > enterTownHall ( ) ; } , EShortcut : : TOWN_OPEN_HALL ) ;
2024-02-28 22:24:19 +02:00
fastTownHall - > setOverlay ( std : : make_shared < CAnimImage > ( AnimationPath : : builtin ( " ITMTL " ) , town - > hallLevel ( ) ) ) ;
2023-10-19 16:19:09 +02:00
2023-11-25 03:58:06 +02:00
int imageIndex = town - > fortLevel ( ) = = CGTownInstance : : EFortLevel : : NONE ? 3 : town - > fortLevel ( ) - 1 ;
2024-05-18 21:59:28 +02:00
fastArmyPurchase = std : : make_shared < CButton > ( Point ( 122 , 413 ) , AnimationPath : : builtin ( " castleInterfaceQuickAccess " ) , CButton : : tooltip ( ) , [ this ] ( ) { builds - > enterToTheQuickRecruitmentWindow ( ) ; } , EShortcut : : TOWN_OPEN_RECRUITMENT ) ;
2024-02-28 22:24:19 +02:00
fastArmyPurchase - > setOverlay ( std : : make_shared < CAnimImage > ( AnimationPath : : builtin ( " itmcl " ) , imageIndex ) ) ;
2023-10-19 16:19:09 +02:00
2024-05-18 22:45:06 +02:00
fastMarket = std : : make_shared < LRClickableArea > ( Rect ( 163 , 410 , 64 , 42 ) , [ this ] ( ) { builds - > enterAnyMarket ( ) ; } ) ;
2023-10-19 16:19:09 +02:00
fastTavern = std : : make_shared < LRClickableArea > ( Rect ( 15 , 387 , 58 , 64 ) , [ & ] ( )
{
2024-08-17 21:06:48 +02:00
if ( town - > hasBuilt ( BuildingID : : TAVERN ) )
2023-10-19 16:19:09 +02:00
LOCPLINT - > showTavernWindow ( town , nullptr , QueryID : : NONE ) ;
2024-05-12 22:24:09 +02:00
} , [ this ] {
2024-04-12 23:35:39 +02:00
if ( ! town - > town - > faction - > getDescriptionTranslated ( ) . empty ( ) )
CRClickPopup : : createAndPush ( town - > town - > faction - > getDescriptionTranslated ( ) ) ;
2023-10-19 16:19:09 +02:00
} ) ;
creainfo . clear ( ) ;
bool compactCreatureInfo = useCompactCreatureBox ( ) ;
bool useAvailableCreaturesForLabel = useAvailableAmountAsCreatureLabel ( ) ;
for ( size_t i = 0 ; i < 4 ; i + + )
creainfo . push_back ( std : : make_shared < CCreaInfo > ( Point ( 14 + 55 * ( int ) i , 459 ) , town , ( int ) i , compactCreatureInfo , useAvailableCreaturesForLabel ) ) ;
for ( size_t i = 0 ; i < 4 ; i + + )
creainfo . push_back ( std : : make_shared < CCreaInfo > ( Point ( 14 + 55 * ( int ) i , 507 ) , town , ( int ) i + 4 , compactCreatureInfo , useAvailableCreaturesForLabel ) ) ;
}
void CCastleInterface : : keyPressed ( EShortcut key )
{
switch ( key )
{
2024-07-03 18:52:50 +02:00
case EShortcut : : MOVE_UP :
townlist - > selectPrev ( ) ;
break ;
case EShortcut : : MOVE_DOWN :
townlist - > selectNext ( ) ;
break ;
2024-05-18 22:45:06 +02:00
case EShortcut : : TOWN_OPEN_FORT :
GH . windows ( ) . createAndPushWindow < CFortScreen > ( town ) ;
break ;
case EShortcut : : TOWN_OPEN_MARKET :
builds - > enterAnyMarket ( ) ;
break ;
case EShortcut : : TOWN_OPEN_MAGE_GUILD :
if ( town - > hasBuilt ( BuildingID : : MAGES_GUILD_1 ) )
builds - > enterMagesGuild ( ) ;
break ;
case EShortcut : : TOWN_OPEN_THIEVES_GUILD :
break ;
case EShortcut : : TOWN_OPEN_HERO_EXCHANGE :
if ( town - > visitingHero & & town - > garrisonHero )
LOCPLINT - > showHeroExchange ( town - > visitingHero - > id , town - > garrisonHero - > id ) ;
break ;
case EShortcut : : TOWN_OPEN_HERO :
if ( town - > visitingHero )
LOCPLINT - > openHeroWindow ( town - > visitingHero ) ;
else if ( town - > garrisonHero )
LOCPLINT - > openHeroWindow ( town - > garrisonHero ) ;
break ;
case EShortcut : : TOWN_OPEN_VISITING_HERO :
if ( town - > visitingHero )
LOCPLINT - > openHeroWindow ( town - > visitingHero ) ;
2023-10-19 16:19:09 +02:00
break ;
2024-05-18 22:45:06 +02:00
case EShortcut : : TOWN_OPEN_GARRISONED_HERO :
if ( town - > garrisonHero )
LOCPLINT - > openHeroWindow ( town - > garrisonHero ) ;
2023-10-19 16:19:09 +02:00
break ;
case EShortcut : : TOWN_SWAP_ARMIES :
heroes - > swapArmies ( ) ;
break ;
case EShortcut : : TOWN_OPEN_TAVERN :
if ( town - > hasBuilt ( BuildingID : : TAVERN ) )
LOCPLINT - > showTavernWindow ( town , nullptr , QueryID : : NONE ) ;
break ;
default :
break ;
}
}
void CCastleInterface : : creaturesChangedEventHandler ( )
{
for ( auto creatureInfoBox : creainfo )
{
if ( creatureInfoBox - > getShowAvailable ( ) )
{
creatureInfoBox - > update ( ) ;
}
}
}
CHallInterface : : CBuildingBox : : CBuildingBox ( int x , int y , const CGTownInstance * Town , const CBuilding * Building ) :
town ( Town ) ,
building ( Building )
{
2024-08-09 17:30:04 +02:00
OBJECT_CONSTRUCTION ;
2023-10-19 16:19:09 +02:00
addUsedEvents ( LCLICK | SHOW_POPUP | HOVER ) ;
pos . x + = x ;
pos . y + = y ;
pos . w = 154 ;
pos . h = 92 ;
state = LOCPLINT - > cb - > canBuildStructure ( town , building - > bid ) ;
2024-02-12 13:22:54 +02:00
constexpr std : : array panelIndex =
2023-10-19 16:19:09 +02:00
{
3 , 3 , 3 , 0 , 0 , 2 , 2 , 1 , 2 , 2 , 3 , 3
} ;
2024-02-12 13:22:54 +02:00
constexpr std : : array iconIndex =
2023-10-19 16:19:09 +02:00
{
- 1 , - 1 , - 1 , 0 , 0 , 1 , 2 , - 1 , 1 , 1 , - 1 , - 1
} ;
icon = std : : make_shared < CAnimImage > ( town - > town - > clientInfo . buildingsIcons , building - > bid , 0 , 2 , 2 ) ;
header = std : : make_shared < CAnimImage > ( AnimationPath : : builtin ( " TPTHBAR " ) , panelIndex [ static_cast < int > ( state ) ] , 0 , 1 , 73 ) ;
if ( iconIndex [ static_cast < int > ( state ) ] > = 0 )
mark = std : : make_shared < CAnimImage > ( AnimationPath : : builtin ( " TPTHCHK " ) , iconIndex [ static_cast < int > ( state ) ] , 0 , 136 , 56 ) ;
2024-01-08 00:42:48 +02:00
name = std : : make_shared < CLabel > ( 78 , 81 , FONT_SMALL , ETextAlignment : : CENTER , Colors : : WHITE , building - > getNameTranslated ( ) , 150 ) ;
2023-10-19 16:19:09 +02:00
//todo: add support for all possible states
if ( state > = EBuildingState : : BUILDING_ERROR )
state = EBuildingState : : FORBIDDEN ;
}
void CHallInterface : : CBuildingBox : : hover ( bool on )
{
if ( on )
{
std : : string toPrint ;
if ( state = = EBuildingState : : PREREQUIRES | | state = = EBuildingState : : MISSING_BASE )
toPrint = CGI - > generaltexth - > hcommands [ 5 ] ;
else if ( state = = EBuildingState : : CANT_BUILD_TODAY )
toPrint = CGI - > generaltexth - > allTexts [ 223 ] ;
else
toPrint = CGI - > generaltexth - > hcommands [ static_cast < int > ( state ) ] ;
boost : : algorithm : : replace_first ( toPrint , " %s " , building - > getNameTranslated ( ) ) ;
GH . statusbar ( ) - > write ( toPrint ) ;
}
else
{
GH . statusbar ( ) - > clear ( ) ;
}
}
void CHallInterface : : CBuildingBox : : clickPressed ( const Point & cursorPosition )
{
GH . windows ( ) . createAndPushWindow < CBuildWindow > ( town , building , state , 0 ) ;
}
void CHallInterface : : CBuildingBox : : showPopupWindow ( const Point & cursorPosition )
{
GH . windows ( ) . createAndPushWindow < CBuildWindow > ( town , building , state , 1 ) ;
}
CHallInterface : : CHallInterface ( const CGTownInstance * Town ) :
2024-04-23 15:21:45 +02:00
CWindowObject ( PLAYER_COLORED | BORDERED , Town - > town - > clientInfo . hallBackground ) ,
2023-10-19 16:19:09 +02:00
town ( Town )
{
2024-08-09 17:30:04 +02:00
OBJECT_CONSTRUCTION ;
2023-10-19 16:19:09 +02:00
resdatabar = std : : make_shared < CMinorResDataBar > ( ) ;
resdatabar - > moveBy ( pos . topLeft ( ) , true ) ;
Rect barRect ( 5 , 556 , 740 , 18 ) ;
auto statusbarBackground = std : : make_shared < CPicture > ( background - > getSurface ( ) , barRect , 5 , 556 ) ;
statusbar = CGStatusBar : : create ( statusbarBackground ) ;
title = std : : make_shared < CLabel > ( 399 , 12 , FONT_MEDIUM , ETextAlignment : : CENTER , Colors : : WHITE , town - > town - > buildings . at ( BuildingID ( town - > hallLevel ( ) + BuildingID : : VILLAGE_HALL ) ) - > getNameTranslated ( ) ) ;
exit = std : : make_shared < CButton > ( Point ( 748 , 556 ) , AnimationPath : : builtin ( " TPMAGE1.DEF " ) , CButton : : tooltip ( CGI - > generaltexth - > hcommands [ 8 ] ) , [ & ] ( ) { close ( ) ; } , EShortcut : : GLOBAL_RETURN ) ;
auto & boxList = town - > town - > clientInfo . hallSlots ;
boxes . resize ( boxList . size ( ) ) ;
for ( size_t row = 0 ; row < boxList . size ( ) ; row + + ) //for each row
{
for ( size_t col = 0 ; col < boxList [ row ] . size ( ) ; col + + ) //for each box
{
const CBuilding * building = nullptr ;
for ( auto & buildingID : boxList [ row ] [ col ] ) //we are looking for the first not built structure
{
2024-06-26 13:18:24 +02:00
if ( ! buildingID . hasValue ( ) )
{
logMod - > warn ( " Invalid building ID found in hallSlots of town '%s' " , town - > town - > faction - > getJsonKey ( ) ) ;
continue ;
}
2024-06-20 12:08:49 +02:00
2023-10-19 16:19:09 +02:00
const CBuilding * current = town - > town - > buildings . at ( buildingID ) ;
2024-08-17 21:06:48 +02:00
if ( town - > hasBuilt ( buildingID ) )
2023-10-19 16:19:09 +02:00
{
building = current ;
}
else
{
if ( current - > mode = = CBuilding : : BUILD_NORMAL )
{
building = current ;
break ;
}
}
}
2024-01-10 00:38:54 +02:00
int posX = pos . w / 2 - ( int ) boxList [ row ] . size ( ) * 154 / 2 - ( ( int ) boxList [ row ] . size ( ) - 1 ) * 20 + 194 * ( int ) col ;
int posY = 35 + 104 * ( int ) row ;
2023-10-19 16:19:09 +02:00
if ( building )
boxes [ row ] . push_back ( std : : make_shared < CBuildingBox > ( posX , posY , town , building ) ) ;
}
}
}
CBuildWindow : : CBuildWindow ( const CGTownInstance * Town , const CBuilding * Building , EBuildingState state , bool rightClick ) :
2024-04-23 15:21:45 +02:00
CWindowObject ( PLAYER_COLORED | ( rightClick ? RCLICK_POPUP : 0 ) , ImagePath : : builtin ( " TPUBUILD " ) ) ,
2023-10-19 16:19:09 +02:00
town ( Town ) ,
building ( Building )
{
2024-08-09 17:30:04 +02:00
OBJECT_CONSTRUCTION ;
2023-10-19 16:19:09 +02:00
icon = std : : make_shared < CAnimImage > ( town - > town - > clientInfo . buildingsIcons , building - > bid , 0 , 125 , 50 ) ;
auto statusbarBackground = std : : make_shared < CPicture > ( background - > getSurface ( ) , Rect ( 8 , pos . h - 26 , pos . w - 16 , 19 ) , 8 , pos . h - 26 ) ;
statusbar = CGStatusBar : : create ( statusbarBackground ) ;
MetaString nameString ;
nameString . appendTextID ( " core.hallinfo.7 " ) ;
nameString . replaceTextID ( building - > getNameTextID ( ) ) ;
name = std : : make_shared < CLabel > ( 197 , 30 , FONT_MEDIUM , ETextAlignment : : CENTER , Colors : : WHITE , nameString . toString ( ) ) ;
description = std : : make_shared < CTextBox > ( building - > getDescriptionTranslated ( ) , Rect ( 33 , 135 , 329 , 67 ) , 0 , FONT_MEDIUM , ETextAlignment : : CENTER ) ;
stateText = std : : make_shared < CTextBox > ( getTextForState ( state ) , Rect ( 33 , 216 , 329 , 67 ) , 0 , FONT_SMALL , ETextAlignment : : CENTER ) ;
//Create components for all required resources
std : : vector < std : : shared_ptr < CComponent > > components ;
2023-10-31 11:09:56 +02:00
for ( GameResID i : GameResID : : ALL_RESOURCES ( ) )
2023-10-19 16:19:09 +02:00
{
if ( building - > resources [ i ] )
{
2023-10-31 11:09:56 +02:00
MetaString message ;
int resourceAmount = LOCPLINT - > cb - > getResourceAmount ( i ) ;
bool canAfford = resourceAmount > = building - > resources [ i ] ;
if ( ! canAfford & & state ! = EBuildingState : : ALREADY_PRESENT & & settings [ " general " ] [ " enableUiEnhancements " ] . Bool ( ) )
2023-11-02 13:18:40 +02:00
{
2023-10-31 11:09:56 +02:00
message . appendRawString ( " {H3Red|%d}/%d " ) ;
2023-11-02 13:18:40 +02:00
message . replaceNumber ( resourceAmount ) ;
}
2023-10-31 11:09:56 +02:00
else
message . appendRawString ( " %d " ) ;
message . replaceNumber ( building - > resources [ i ] ) ;
components . push_back ( std : : make_shared < CComponent > ( ComponentType : : RESOURCE , i , message . toString ( ) , CComponent : : small ) ) ;
2023-10-19 16:19:09 +02:00
}
}
cost = std : : make_shared < CComponentBox > ( components , Rect ( 25 , 300 , pos . w - 50 , 130 ) ) ;
if ( ! rightClick )
{ //normal window
MetaString tooltipYes ;
tooltipYes . appendTextID ( " core.genrltxt.595 " ) ;
tooltipYes . replaceTextID ( building - > getNameTextID ( ) ) ;
MetaString tooltipNo ;
tooltipNo . appendTextID ( " core.genrltxt.596 " ) ;
tooltipNo . replaceTextID ( building - > getNameTextID ( ) ) ;
buy = std : : make_shared < CButton > ( Point ( 45 , 446 ) , AnimationPath : : builtin ( " IBUY30 " ) , CButton : : tooltip ( tooltipYes . toString ( ) ) , [ & ] ( ) { buyFunc ( ) ; } , EShortcut : : GLOBAL_ACCEPT ) ;
buy - > setBorderColor ( Colors : : METALLIC_GOLD ) ;
2024-04-27 12:17:45 +02:00
buy - > block ( state ! = EBuildingState : : ALLOWED | | LOCPLINT - > playerID ! = town - > tempOwner | | ! LOCPLINT - > makingTurn ) ;
2023-10-19 16:19:09 +02:00
cancel = std : : make_shared < CButton > ( Point ( 290 , 445 ) , AnimationPath : : builtin ( " ICANCEL " ) , CButton : : tooltip ( tooltipNo . toString ( ) ) , [ & ] ( ) { close ( ) ; } , EShortcut : : GLOBAL_CANCEL ) ;
cancel - > setBorderColor ( Colors : : METALLIC_GOLD ) ;
}
}
void CBuildWindow : : buyFunc ( )
{
LOCPLINT - > cb - > buildBuilding ( town , building - > bid ) ;
GH . windows ( ) . popWindows ( 2 ) ; //we - build window and hall screen
}
std : : string CBuildWindow : : getTextForState ( EBuildingState state )
{
std : : string ret ;
if ( state < EBuildingState : : ALLOWED )
ret = CGI - > generaltexth - > hcommands [ static_cast < int > ( state ) ] ;
switch ( state )
{
case EBuildingState : : ALREADY_PRESENT :
case EBuildingState : : CANT_BUILD_TODAY :
case EBuildingState : : NO_RESOURCES :
ret . replace ( ret . find_first_of ( " %s " ) , 2 , building - > getNameTranslated ( ) ) ;
break ;
case EBuildingState : : ALLOWED :
return CGI - > generaltexth - > allTexts [ 219 ] ; //all prereq. are met
case EBuildingState : : PREREQUIRES :
{
auto toStr = [ & ] ( const BuildingID build ) - > std : : string
{
return town - > town - > buildings . at ( build ) - > getNameTranslated ( ) ;
} ;
ret = CGI - > generaltexth - > allTexts [ 52 ] ;
ret + = " \n " + town - > genBuildingRequirements ( building - > bid ) . toString ( toStr ) ;
break ;
}
case EBuildingState : : MISSING_BASE :
{
std : : string msg = CGI - > generaltexth - > translate ( " vcmi.townHall.missingBase " ) ;
ret = boost : : str ( boost : : format ( msg ) % town - > town - > buildings . at ( building - > upgrade ) - > getNameTranslated ( ) ) ;
break ;
}
}
return ret ;
}
LabeledValue : : LabeledValue ( Rect size , std : : string name , std : : string descr , int min , int max )
{
2024-08-09 17:30:04 +02:00
OBJECT_CONSTRUCTION ;
2023-10-19 16:19:09 +02:00
pos . x + = size . x ;
pos . y + = size . y ;
pos . w = size . w ;
pos . h = size . h ;
init ( name , descr , min , max ) ;
}
LabeledValue : : LabeledValue ( Rect size , std : : string name , std : : string descr , int val )
{
2024-08-09 17:30:04 +02:00
OBJECT_CONSTRUCTION ;
2023-10-19 16:19:09 +02:00
pos . x + = size . x ;
pos . y + = size . y ;
pos . w = size . w ;
pos . h = size . h ;
init ( name , descr , val , val ) ;
}
void LabeledValue : : init ( std : : string nameText , std : : string descr , int min , int max )
{
addUsedEvents ( HOVER ) ;
hoverText = descr ;
std : : string valueText ;
if ( min & & max )
{
valueText = std : : to_string ( min ) ;
if ( min ! = max )
valueText + = ' - ' + std : : to_string ( max ) ;
}
name = std : : make_shared < CLabel > ( 3 , 0 , FONT_SMALL , ETextAlignment : : TOPLEFT , Colors : : WHITE , nameText ) ;
value = std : : make_shared < CLabel > ( pos . w - 3 , pos . h - 2 , FONT_SMALL , ETextAlignment : : BOTTOMRIGHT , Colors : : WHITE , valueText ) ;
}
void LabeledValue : : hover ( bool on )
{
if ( on )
{
GH . statusbar ( ) - > write ( hoverText ) ;
}
else
{
GH . statusbar ( ) - > clear ( ) ;
}
}
CFortScreen : : CFortScreen ( const CGTownInstance * town ) :
2024-04-23 15:21:45 +02:00
CWindowObject ( PLAYER_COLORED | BORDERED , getBgName ( town ) )
2023-10-19 16:19:09 +02:00
{
2024-08-09 17:30:04 +02:00
OBJECT_CONSTRUCTION ;
2023-10-19 16:19:09 +02:00
ui32 fortSize = static_cast < ui32 > ( town - > creatures . size ( ) ) ;
2024-08-05 21:15:47 +02:00
if ( fortSize > town - > town - > creatures . size ( ) & & town - > creatures . back ( ) . second . empty ( ) )
2023-10-19 16:19:09 +02:00
fortSize - - ;
const CBuilding * fortBuilding = town - > town - > buildings . at ( BuildingID ( town - > fortLevel ( ) + 6 ) ) ;
title = std : : make_shared < CLabel > ( 400 , 12 , FONT_BIG , ETextAlignment : : CENTER , Colors : : WHITE , fortBuilding - > getNameTranslated ( ) ) ;
std : : string text = boost : : str ( boost : : format ( CGI - > generaltexth - > fcommands [ 6 ] ) % fortBuilding - > getNameTranslated ( ) ) ;
exit = std : : make_shared < CButton > ( Point ( 748 , 556 ) , AnimationPath : : builtin ( " TPMAGE1 " ) , CButton : : tooltip ( text ) , [ & ] ( ) { close ( ) ; } , EShortcut : : GLOBAL_RETURN ) ;
std : : vector < Point > positions =
{
Point ( 10 , 22 ) , Point ( 404 , 22 ) ,
Point ( 10 , 155 ) , Point ( 404 , 155 ) ,
Point ( 10 , 288 ) , Point ( 404 , 288 )
} ;
if ( fortSize = = GameConstants : : CREATURES_PER_TOWN )
{
2024-08-05 21:15:47 +02:00
positions . push_back ( Point ( 10 , 421 ) ) ;
positions . push_back ( Point ( 404 , 421 ) ) ;
2023-10-19 16:19:09 +02:00
}
else
{
2024-08-05 21:15:47 +02:00
positions . push_back ( Point ( 206 , 421 ) ) ;
2023-10-19 16:19:09 +02:00
}
for ( ui32 i = 0 ; i < fortSize ; i + + )
{
BuildingID buildingID ;
2024-08-05 21:15:47 +02:00
if ( fortSize = = town - > town - > creatures . size ( ) )
2023-10-19 16:19:09 +02:00
{
2024-08-10 00:29:30 +02:00
BuildingID dwelling = BuildingID : : getDwellingFromLevel ( i , 1 ) ;
2023-10-19 16:19:09 +02:00
2024-08-17 21:06:48 +02:00
if ( town - > hasBuilt ( dwelling ) )
2024-08-10 00:29:30 +02:00
buildingID = BuildingID ( BuildingID : : getDwellingFromLevel ( i , 1 ) ) ;
2023-10-19 16:19:09 +02:00
else
2024-08-10 00:29:30 +02:00
buildingID = BuildingID ( BuildingID : : getDwellingFromLevel ( i , 0 ) ) ;
2023-10-19 16:19:09 +02:00
}
else
{
buildingID = BuildingID : : SPECIAL_3 ;
}
recAreas . push_back ( std : : make_shared < RecruitArea > ( positions [ i ] . x , positions [ i ] . y , town , i ) ) ;
}
resdatabar = std : : make_shared < CMinorResDataBar > ( ) ;
resdatabar - > moveBy ( pos . topLeft ( ) , true ) ;
Rect barRect ( 4 , 554 , 740 , 18 ) ;
auto statusbarBackground = std : : make_shared < CPicture > ( background - > getSurface ( ) , barRect , 4 , 554 ) ;
statusbar = CGStatusBar : : create ( statusbarBackground ) ;
}
ImagePath CFortScreen : : getBgName ( const CGTownInstance * town )
{
ui32 fortSize = static_cast < ui32 > ( town - > creatures . size ( ) ) ;
2024-08-05 21:15:47 +02:00
if ( fortSize > town - > town - > creatures . size ( ) & & town - > creatures . back ( ) . second . empty ( ) )
2023-10-19 16:19:09 +02:00
fortSize - - ;
if ( fortSize = = GameConstants : : CREATURES_PER_TOWN )
return ImagePath : : builtin ( " TPCASTL8 " ) ;
2024-08-05 21:15:47 +02:00
else
return ImagePath : : builtin ( " TPCASTL7 " ) ;
2023-10-19 16:19:09 +02:00
}
void CFortScreen : : creaturesChangedEventHandler ( )
{
for ( auto & elem : recAreas )
elem - > creaturesChangedEventHandler ( ) ;
LOCPLINT - > castleInt - > creaturesChangedEventHandler ( ) ;
}
CFortScreen : : RecruitArea : : RecruitArea ( int posX , int posY , const CGTownInstance * Town , int Level ) :
town ( Town ) ,
level ( Level ) ,
availableCount ( nullptr )
{
2024-08-09 17:30:04 +02:00
OBJECT_CONSTRUCTION ;
2023-10-19 16:19:09 +02:00
pos . x + = posX ;
pos . y + = posY ;
pos . w = 386 ;
pos . h = 126 ;
if ( ! town - > creatures [ level ] . second . empty ( ) )
addUsedEvents ( LCLICK | HOVER ) ; //Activate only if dwelling is present
addUsedEvents ( SHOW_POPUP ) ;
icons = std : : make_shared < CPicture > ( ImagePath : : builtin ( " TPCAINFO " ) , 261 , 3 ) ;
if ( getMyBuilding ( ) ! = nullptr )
{
buildingIcon = std : : make_shared < CAnimImage > ( town - > town - > clientInfo . buildingsIcons , getMyBuilding ( ) - > bid , 0 , 4 , 21 ) ;
2024-01-08 00:42:48 +02:00
buildingName = std : : make_shared < CLabel > ( 78 , 101 , FONT_SMALL , ETextAlignment : : CENTER , Colors : : WHITE , getMyBuilding ( ) - > getNameTranslated ( ) , 152 ) ;
2023-10-19 16:19:09 +02:00
2024-08-17 21:06:48 +02:00
if ( town - > hasBuilt ( getMyBuilding ( ) - > bid ) )
2023-10-19 16:19:09 +02:00
{
ui32 available = town - > creatures [ level ] . first ;
std : : string availableText = CGI - > generaltexth - > allTexts [ 217 ] + std : : to_string ( available ) ;
availableCount = std : : make_shared < CLabel > ( 78 , 119 , FONT_SMALL , ETextAlignment : : CENTER , Colors : : WHITE , availableText ) ;
}
}
if ( getMyCreature ( ) ! = nullptr )
{
hoverText = boost : : str ( boost : : format ( CGI - > generaltexth - > tcommands [ 21 ] ) % getMyCreature ( ) - > getNamePluralTranslated ( ) ) ;
new CCreaturePic ( 159 , 4 , getMyCreature ( ) , false ) ;
2024-01-08 00:42:48 +02:00
new CLabel ( 78 , 11 , FONT_SMALL , ETextAlignment : : CENTER , Colors : : WHITE , getMyCreature ( ) - > getNamePluralTranslated ( ) , 152 ) ;
2023-10-19 16:19:09 +02:00
Rect sizes ( 287 , 4 , 96 , 18 ) ;
values . push_back ( std : : make_shared < LabeledValue > ( sizes , CGI - > generaltexth - > allTexts [ 190 ] , CGI - > generaltexth - > fcommands [ 0 ] , getMyCreature ( ) - > getAttack ( false ) ) ) ;
sizes . y + = 20 ;
values . push_back ( std : : make_shared < LabeledValue > ( sizes , CGI - > generaltexth - > allTexts [ 191 ] , CGI - > generaltexth - > fcommands [ 1 ] , getMyCreature ( ) - > getDefense ( false ) ) ) ;
sizes . y + = 21 ;
values . push_back ( std : : make_shared < LabeledValue > ( sizes , CGI - > generaltexth - > allTexts [ 199 ] , CGI - > generaltexth - > fcommands [ 2 ] , getMyCreature ( ) - > getMinDamage ( false ) , getMyCreature ( ) - > getMaxDamage ( false ) ) ) ;
sizes . y + = 20 ;
values . push_back ( std : : make_shared < LabeledValue > ( sizes , CGI - > generaltexth - > allTexts [ 388 ] , CGI - > generaltexth - > fcommands [ 3 ] , getMyCreature ( ) - > getMaxHealth ( ) ) ) ;
sizes . y + = 21 ;
values . push_back ( std : : make_shared < LabeledValue > ( sizes , CGI - > generaltexth - > allTexts [ 193 ] , CGI - > generaltexth - > fcommands [ 4 ] , getMyCreature ( ) - > valOfBonuses ( BonusType : : STACKS_SPEED ) ) ) ;
sizes . y + = 20 ;
values . push_back ( std : : make_shared < LabeledValue > ( sizes , CGI - > generaltexth - > allTexts [ 194 ] , CGI - > generaltexth - > fcommands [ 5 ] , town - > creatureGrowth ( level ) ) ) ;
}
}
const CCreature * CFortScreen : : RecruitArea : : getMyCreature ( )
{
if ( ! town - > creatures . at ( level ) . second . empty ( ) ) // built
2024-05-17 00:05:51 +02:00
return town - > creatures . at ( level ) . second . back ( ) . toCreature ( ) ;
2023-10-19 16:19:09 +02:00
if ( ! town - > town - > creatures . at ( level ) . empty ( ) ) // there are creatures on this level
2024-05-17 00:05:51 +02:00
return town - > town - > creatures . at ( level ) . front ( ) . toCreature ( ) ;
2023-10-19 16:19:09 +02:00
return nullptr ;
}
const CBuilding * CFortScreen : : RecruitArea : : getMyBuilding ( )
{
2024-08-10 00:29:30 +02:00
BuildingID myID = BuildingID ( BuildingID : : getDwellingFromLevel ( level , 0 ) ) ;
2023-10-19 16:19:09 +02:00
2024-08-05 23:03:19 +02:00
if ( level = = town - > town - > creatures . size ( ) )
2023-10-19 16:19:09 +02:00
return town - > town - > getSpecialBuilding ( BuildingSubID : : PORTAL_OF_SUMMONING ) ;
if ( ! town - > town - > buildings . count ( myID ) )
return nullptr ;
const CBuilding * build = town - > town - > buildings . at ( myID ) ;
while ( town - > town - > buildings . count ( myID ) )
{
if ( town - > hasBuilt ( myID ) )
build = town - > town - > buildings . at ( myID ) ;
2024-08-21 21:24:06 +02:00
BuildingID : : advanceDwelling ( myID ) ;
2023-10-19 16:19:09 +02:00
}
2024-08-21 00:17:25 +02:00
2023-10-19 16:19:09 +02:00
return build ;
}
void CFortScreen : : RecruitArea : : hover ( bool on )
{
if ( on )
GH . statusbar ( ) - > write ( hoverText ) ;
else
GH . statusbar ( ) - > clear ( ) ;
}
void CFortScreen : : RecruitArea : : creaturesChangedEventHandler ( )
{
if ( availableCount )
{
std : : string availableText = CGI - > generaltexth - > allTexts [ 217 ] + std : : to_string ( town - > creatures [ level ] . first ) ;
availableCount - > setText ( availableText ) ;
}
}
void CFortScreen : : RecruitArea : : clickPressed ( const Point & cursorPosition )
{
LOCPLINT - > castleInt - > builds - > enterDwelling ( level ) ;
}
void CFortScreen : : RecruitArea : : showPopupWindow ( const Point & cursorPosition )
{
if ( getMyCreature ( ) ! = nullptr )
GH . windows ( ) . createAndPushWindow < CStackWindow > ( getMyCreature ( ) , true ) ;
}
CMageGuildScreen : : CMageGuildScreen ( CCastleInterface * owner , const ImagePath & imagename )
2024-09-27 23:52:33 +02:00
: CWindowObject ( BORDERED , imagename ) , townId ( owner - > town - > id )
2023-10-19 16:19:09 +02:00
{
2024-08-09 17:30:04 +02:00
OBJECT_CONSTRUCTION ;
2023-10-19 16:19:09 +02:00
window = std : : make_shared < CPicture > ( owner - > town - > town - > clientInfo . guildWindow , 332 , 76 ) ;
resdatabar = std : : make_shared < CMinorResDataBar > ( ) ;
resdatabar - > moveBy ( pos . topLeft ( ) , true ) ;
Rect barRect ( 7 , 556 , 737 , 18 ) ;
auto statusbarBackground = std : : make_shared < CPicture > ( background - > getSurface ( ) , barRect , 7 , 556 ) ;
statusbar = CGStatusBar : : create ( statusbarBackground ) ;
exit = std : : make_shared < CButton > ( Point ( 748 , 556 ) , AnimationPath : : builtin ( " TPMAGE1.DEF " ) , CButton : : tooltip ( CGI - > generaltexth - > allTexts [ 593 ] ) , [ & ] ( ) { close ( ) ; } , EShortcut : : GLOBAL_RETURN ) ;
2024-09-27 23:52:33 +02:00
update ( ) ;
}
void CMageGuildScreen : : update ( )
{
OBJECT_CONSTRUCTION ;
2023-10-19 16:19:09 +02:00
static const std : : vector < std : : vector < Point > > positions =
{
{ Point ( 222 , 445 ) , Point ( 312 , 445 ) , Point ( 402 , 445 ) , Point ( 520 , 445 ) , Point ( 610 , 445 ) , Point ( 700 , 445 ) } ,
{ Point ( 48 , 53 ) , Point ( 48 , 147 ) , Point ( 48 , 241 ) , Point ( 48 , 335 ) , Point ( 48 , 429 ) } ,
{ Point ( 570 , 82 ) , Point ( 672 , 82 ) , Point ( 570 , 157 ) , Point ( 672 , 157 ) } ,
{ Point ( 183 , 42 ) , Point ( 183 , 148 ) , Point ( 183 , 253 ) } ,
{ Point ( 491 , 325 ) , Point ( 591 , 325 ) }
} ;
2024-09-27 23:52:33 +02:00
spells . clear ( ) ;
emptyScrolls . clear ( ) ;
const CGTownInstance * town = LOCPLINT - > cb - > getTown ( townId ) ;
for ( size_t i = 0 ; i < town - > town - > mageLevel ; i + + )
2023-10-19 16:19:09 +02:00
{
2024-09-27 23:52:33 +02:00
size_t spellCount = town - > spellsAtLevel ( ( int ) i + 1 , false ) ; //spell at level with -1 hmmm?
2023-10-19 16:19:09 +02:00
for ( size_t j = 0 ; j < spellCount ; j + + )
{
2024-09-27 23:52:33 +02:00
if ( i < town - > mageGuildLevel ( ) & & town - > spells [ i ] . size ( ) > j )
spells . push_back ( std : : make_shared < Scroll > ( positions [ i ] [ j ] , town - > spells [ i ] [ j ] . toSpell ( ) , townId ) ) ;
2023-10-19 16:19:09 +02:00
else
emptyScrolls . push_back ( std : : make_shared < CAnimImage > ( AnimationPath : : builtin ( " TPMAGES.DEF " ) , 1 , 0 , positions [ i ] [ j ] . x , positions [ i ] [ j ] . y ) ) ;
}
}
2024-09-27 23:52:33 +02:00
redraw ( ) ;
2023-10-19 16:19:09 +02:00
}
2024-09-27 23:52:33 +02:00
CMageGuildScreen : : Scroll : : Scroll ( Point position , const CSpell * Spell , ObjectInstanceID townId )
: spell ( Spell ) , townId ( townId )
2023-10-19 16:19:09 +02:00
{
2024-08-09 17:30:04 +02:00
OBJECT_CONSTRUCTION ;
2023-10-19 16:19:09 +02:00
addUsedEvents ( LCLICK | SHOW_POPUP | HOVER ) ;
pos + = position ;
image = std : : make_shared < CAnimImage > ( AnimationPath : : builtin ( " SPELLSCR " ) , spell - > id ) ;
pos = image - > pos ;
}
void CMageGuildScreen : : Scroll : : clickPressed ( const Point & cursorPosition )
{
2024-09-27 23:52:33 +02:00
const CGTownInstance * town = LOCPLINT - > cb - > getTown ( townId ) ;
2024-09-27 22:47:22 +02:00
if ( LOCPLINT - > cb - > getSettings ( ) . getBoolean ( EGameSettings : : TOWNS_SPELL_RESEARCH ) )
2024-09-28 01:47:32 +02:00
{
int daysSinceLastResearch = LOCPLINT - > cb - > getDate ( Date : : DAY ) - town - > lastSpellResearchDay ;
if ( ! daysSinceLastResearch )
{
LOCPLINT - > showInfoDialog ( CGI - > generaltexth - > translate ( " vcmi.spellResearch.comeAgain " ) ) ;
return ;
}
int level = - 1 ;
for ( int i = 0 ; i < town - > spells . size ( ) ; i + + )
if ( vstd : : find_pos ( town - > spells [ i ] , spell - > id ) ! = - 1 )
level = i ;
TResources cost ;
cost [ EGameResID : : GOLD ] = 1000 ;
cost [ EGameResID : : MERCURY ] = ( level + 1 ) * 2 ;
cost [ EGameResID : : SULFUR ] = ( level + 1 ) * 2 ;
cost [ EGameResID : : CRYSTAL ] = ( level + 1 ) * 2 ;
cost [ EGameResID : : GEMS ] = ( level + 1 ) * 2 ;
std : : vector < std : : shared_ptr < CComponent > > resComps ;
for ( TResources : : nziterator i ( cost ) ; i . valid ( ) ; i + + )
{
resComps . push_back ( std : : make_shared < CComponent > ( ComponentType : : RESOURCE , i - > resType , i - > resVal ) ) ;
}
if ( LOCPLINT - > cb - > getResourceAmount ( ) . canAfford ( cost ) )
LOCPLINT - > showYesNoDialog ( CGI - > generaltexth - > translate ( " vcmi.spellResearch.pay " ) , [ this , town ] ( ) { LOCPLINT - > cb - > spellResearch ( town , spell - > id ) ; } , nullptr , resComps ) ;
else
LOCPLINT - > showInfoDialog ( CGI - > generaltexth - > translate ( " vcmi.spellResearch.canNotAfford " ) , resComps ) ;
}
2024-09-27 22:47:22 +02:00
else
LOCPLINT - > showInfoDialog ( spell - > getDescriptionTranslated ( 0 ) , std : : make_shared < CComponent > ( ComponentType : : SPELL , spell - > id ) ) ;
2023-10-19 16:19:09 +02:00
}
void CMageGuildScreen : : Scroll : : showPopupWindow ( const Point & cursorPosition )
{
2023-10-31 11:09:56 +02:00
CRClickPopup : : createAndPush ( spell - > getDescriptionTranslated ( 0 ) , std : : make_shared < CComponent > ( ComponentType : : SPELL , spell - > id ) ) ;
2023-10-19 16:19:09 +02:00
}
void CMageGuildScreen : : Scroll : : hover ( bool on )
{
if ( on )
GH . statusbar ( ) - > write ( spell - > getNameTranslated ( ) ) ;
else
GH . statusbar ( ) - > clear ( ) ;
}
CBlacksmithDialog : : CBlacksmithDialog ( bool possible , CreatureID creMachineID , ArtifactID aid , ObjectInstanceID hid ) :
2024-04-23 15:21:45 +02:00
CWindowObject ( PLAYER_COLORED , ImagePath : : builtin ( " TPSMITH " ) )
2023-10-19 16:19:09 +02:00
{
2024-08-09 17:30:04 +02:00
OBJECT_CONSTRUCTION ;
2023-10-19 16:19:09 +02:00
Rect barRect ( 8 , pos . h - 26 , pos . w - 16 , 19 ) ;
auto statusbarBackground = std : : make_shared < CPicture > ( background - > getSurface ( ) , barRect , 8 , pos . h - 26 ) ;
statusbar = CGStatusBar : : create ( statusbarBackground ) ;
animBG = std : : make_shared < CPicture > ( ImagePath : : builtin ( " TPSMITBK " ) , 64 , 50 ) ;
animBG - > needRefresh = true ;
2024-05-17 00:05:51 +02:00
const CCreature * creature = creMachineID . toCreature ( ) ;
2023-10-19 16:19:09 +02:00
anim = std : : make_shared < CCreatureAnim > ( 64 , 50 , creature - > animDefName ) ;
anim - > clipRect ( 113 , 125 , 200 , 150 ) ;
MetaString titleString ;
titleString . appendTextID ( " core.genrltxt.274 " ) ;
titleString . replaceTextID ( creature - > getNameSingularTextID ( ) ) ;
MetaString buyText ;
buyText . appendTextID ( " core.genrltxt.595 " ) ;
buyText . replaceTextID ( creature - > getNameSingularTextID ( ) ) ;
MetaString cancelText ;
cancelText . appendTextID ( " core.genrltxt.596 " ) ;
cancelText . replaceTextID ( creature - > getNameSingularTextID ( ) ) ;
2023-11-04 17:04:53 +02:00
std : : string costString = std : : to_string ( aid . toEntity ( CGI ) - > getPrice ( ) ) ;
2023-10-19 16:19:09 +02:00
title = std : : make_shared < CLabel > ( 165 , 28 , FONT_BIG , ETextAlignment : : CENTER , Colors : : YELLOW , titleString . toString ( ) ) ;
costText = std : : make_shared < CLabel > ( 165 , 218 , FONT_MEDIUM , ETextAlignment : : CENTER , Colors : : WHITE , CGI - > generaltexth - > jktexts [ 43 ] ) ;
costValue = std : : make_shared < CLabel > ( 165 , 292 , FONT_MEDIUM , ETextAlignment : : CENTER , Colors : : WHITE , costString ) ;
buy = std : : make_shared < CButton > ( Point ( 42 , 312 ) , AnimationPath : : builtin ( " IBUY30.DEF " ) , CButton : : tooltip ( buyText . toString ( ) ) , [ & ] ( ) { close ( ) ; } , EShortcut : : GLOBAL_ACCEPT ) ;
cancel = std : : make_shared < CButton > ( Point ( 224 , 312 ) , AnimationPath : : builtin ( " ICANCEL.DEF " ) , CButton : : tooltip ( cancelText . toString ( ) ) , [ & ] ( ) { close ( ) ; } , EShortcut : : GLOBAL_CANCEL ) ;
if ( possible )
buy - > addCallback ( [ = ] ( ) { LOCPLINT - > cb - > buyArtifact ( LOCPLINT - > cb - > getHero ( hid ) , aid ) ; } ) ;
else
buy - > block ( true ) ;
costIcon = std : : make_shared < CAnimImage > ( AnimationPath : : builtin ( " RESOURCE " ) , GameResID ( EGameResID : : GOLD ) , 0 , 148 , 244 ) ;
}