2023-04-26 20:55:56 +02:00
/*
* CGDwelling . 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 "CGDwelling.h"
# include "../serializer/JsonSerializeFormat.h"
2024-07-21 12:49:40 +02:00
# include "../entities/faction/CTownHandler.h"
2023-10-25 12:50:11 +02:00
# include "../mapping/CMap.h"
2023-06-02 20:47:37 +02:00
# include "../mapObjectConstructors/AObjectTypeHandler.h"
# include "../mapObjectConstructors/CObjectClassesHandler.h"
2023-10-25 12:50:11 +02:00
# include "../mapObjectConstructors/DwellingInstanceConstructor.h"
2023-10-23 12:59:15 +02:00
# include "../mapObjects/CGHeroInstance.h"
2024-01-09 16:43:36 +02:00
# include "../mapObjects/CGTownInstance.h"
2023-10-23 12:59:15 +02:00
# include "../networkPacks/StackLocation.h"
# include "../networkPacks/PacksForClient.h"
# include "../networkPacks/PacksForClientBattle.h"
2023-04-26 20:55:56 +02:00
# include "../IGameCallback.h"
2023-06-23 17:02:48 +02:00
# include "../gameState/CGameState.h"
2023-04-26 20:55:56 +02:00
# include "../CPlayerState.h"
# include "../GameSettings.h"
# include "../CConfigHandler.h"
2024-06-01 17:28:17 +02:00
# include <vstd/RNG.h>
2023-04-26 20:55:56 +02:00
VCMI_LIB_NAMESPACE_BEGIN
2023-10-25 12:50:11 +02:00
void CGDwellingRandomizationInfo : : serializeJson ( JsonSerializeFormat & handler )
2023-04-26 20:55:56 +02:00
{
handler . serializeString ( " sameAsTown " , instanceId ) ;
2023-10-25 12:50:11 +02:00
handler . serializeIdArray ( " allowedFactions " , allowedFactions ) ;
handler . serializeInt ( " minLevel " , minLevel , static_cast < uint8_t > ( 1 ) ) ;
handler . serializeInt ( " maxLevel " , maxLevel , static_cast < uint8_t > ( 7 ) ) ;
2023-04-26 20:55:56 +02:00
if ( ! handler . saving )
{
2023-10-25 12:50:11 +02:00
//todo: safely allow any level > 7
vstd : : abetween < uint8_t > ( minLevel , 1 , 7 ) ;
vstd : : abetween < uint8_t > ( maxLevel , minLevel , 7 ) ;
2023-04-26 20:55:56 +02:00
}
2023-10-25 12:50:11 +02:00
}
2023-04-26 20:55:56 +02:00
2024-01-01 16:37:48 +02:00
CGDwelling : : CGDwelling ( IGameCallback * cb ) :
CArmedInstance ( cb )
{ }
2023-10-25 12:50:11 +02:00
CGDwelling : : ~ CGDwelling ( ) = default ;
2024-06-01 17:28:17 +02:00
FactionID CGDwelling : : randomizeFaction ( vstd : : RNG & rand )
2023-10-25 12:50:11 +02:00
{
2023-10-25 20:56:00 +02:00
if ( ID = = Obj : : RANDOM_DWELLING_FACTION )
2023-11-05 19:13:18 +02:00
return FactionID ( subID . getNum ( ) ) ;
2023-10-25 20:56:00 +02:00
assert ( ID = = Obj : : RANDOM_DWELLING | | ID = = Obj : : RANDOM_DWELLING_LVL ) ;
2023-10-25 12:50:11 +02:00
assert ( randomizationInfo . has_value ( ) ) ;
if ( ! randomizationInfo )
return FactionID : : CASTLE ;
2023-04-26 20:55:56 +02:00
2023-10-25 12:50:11 +02:00
CGTownInstance * linkedTown = nullptr ;
2023-04-26 20:55:56 +02:00
2023-10-25 12:50:11 +02:00
if ( ! randomizationInfo - > instanceId . empty ( ) )
{
auto iter = cb - > gameState ( ) - > map - > instanceNames . find ( randomizationInfo - > instanceId ) ;
if ( iter = = cb - > gameState ( ) - > map - > instanceNames . end ( ) )
logGlobal - > error ( " Map object not found: %s " , randomizationInfo - > instanceId ) ;
linkedTown = dynamic_cast < CGTownInstance * > ( iter - > second . get ( ) ) ;
}
2023-04-26 20:55:56 +02:00
2023-10-25 12:50:11 +02:00
if ( randomizationInfo - > identifier ! = 0 )
{
for ( auto & elem : cb - > gameState ( ) - > map - > objects )
2023-04-26 20:55:56 +02:00
{
2023-10-25 12:50:11 +02:00
auto town = dynamic_cast < CGTownInstance * > ( elem . get ( ) ) ;
if ( town & & town - > identifier = = randomizationInfo - > identifier )
{
linkedTown = town ;
break ;
}
2023-04-26 20:55:56 +02:00
}
}
2023-10-25 12:50:11 +02:00
if ( linkedTown )
2023-04-26 20:55:56 +02:00
{
2023-10-25 12:50:11 +02:00
if ( linkedTown - > ID = = Obj : : RANDOM_TOWN )
linkedTown - > pickRandomObject ( rand ) ; //we have to randomize the castle first
assert ( linkedTown - > ID = = Obj : : TOWN ) ;
if ( linkedTown - > ID = = Obj : : TOWN )
return linkedTown - > getFaction ( ) ;
2023-04-26 20:55:56 +02:00
}
2023-10-25 12:50:11 +02:00
if ( ! randomizationInfo - > allowedFactions . empty ( ) )
return * RandomGeneratorUtil : : nextItem ( randomizationInfo - > allowedFactions , rand ) ;
std : : vector < FactionID > potentialPicks ;
2023-11-02 17:12:58 +02:00
for ( FactionID faction ( 0 ) ; faction < FactionID ( VLC - > townh - > size ( ) ) ; + + faction )
2023-10-25 12:50:11 +02:00
if ( VLC - > factions ( ) - > getById ( faction ) - > hasTown ( ) )
potentialPicks . push_back ( faction ) ;
assert ( ! potentialPicks . empty ( ) ) ;
return * RandomGeneratorUtil : : nextItem ( potentialPicks , rand ) ;
2023-04-26 20:55:56 +02:00
}
2024-06-01 17:28:17 +02:00
int CGDwelling : : randomizeLevel ( vstd : : RNG & rand )
2023-04-26 20:55:56 +02:00
{
2023-10-25 20:56:00 +02:00
if ( ID = = Obj : : RANDOM_DWELLING_LVL )
return subID . getNum ( ) ;
assert ( ID = = Obj : : RANDOM_DWELLING | | ID = = Obj : : RANDOM_DWELLING_FACTION ) ;
2023-10-25 12:50:11 +02:00
assert ( randomizationInfo . has_value ( ) ) ;
if ( ! randomizationInfo )
return rand . nextInt ( 1 , 7 ) - 1 ;
if ( randomizationInfo - > minLevel = = randomizationInfo - > maxLevel )
return randomizationInfo - > minLevel - 1 ;
return rand . nextInt ( randomizationInfo - > minLevel , randomizationInfo - > maxLevel ) - 1 ;
2023-04-26 20:55:56 +02:00
}
2024-06-01 17:28:17 +02:00
void CGDwelling : : pickRandomObject ( vstd : : RNG & rand )
2023-04-26 20:55:56 +02:00
{
2023-10-25 12:50:11 +02:00
if ( ID = = Obj : : RANDOM_DWELLING | | ID = = Obj : : RANDOM_DWELLING_LVL | | ID = = Obj : : RANDOM_DWELLING_FACTION )
{
FactionID faction = randomizeFaction ( rand ) ;
int level = randomizeLevel ( rand ) ;
assert ( faction ! = FactionID : : NONE & & faction ! = FactionID : : NEUTRAL ) ;
2023-11-01 16:10:33 +02:00
assert ( level > = 0 & & level < = 6 ) ;
2023-10-25 12:50:11 +02:00
randomizationInfo . reset ( ) ;
CreatureID cid = ( * VLC - > townh ) [ faction ] - > town - > creatures [ level ] [ 0 ] ;
//NOTE: this will pick last dwelling with this creature (Mantis #900)
//check for block map equality is better but more complex solution
auto testID = [ & ] ( const Obj & primaryID ) - > MapObjectSubID
{
auto dwellingIDs = VLC - > objtypeh - > knownSubObjects ( primaryID ) ;
2023-11-05 19:13:18 +02:00
for ( MapObjectSubID entry : dwellingIDs )
2023-10-25 12:50:11 +02:00
{
const auto * handler = dynamic_cast < const DwellingInstanceConstructor * > ( VLC - > objtypeh - > getHandlerFor ( primaryID , entry ) . get ( ) ) ;
2024-04-22 11:35:55 +02:00
if ( ! handler - > isBannedForRandomDwelling ( ) & & handler - > producesCreature ( cid . toCreature ( ) ) )
2023-10-25 12:50:11 +02:00
return MapObjectSubID ( entry ) ;
}
return MapObjectSubID ( ) ;
} ;
ID = Obj : : CREATURE_GENERATOR1 ;
subID = testID ( Obj : : CREATURE_GENERATOR1 ) ;
if ( subID = = MapObjectSubID ( ) )
{
ID = Obj : : CREATURE_GENERATOR4 ;
subID = testID ( Obj : : CREATURE_GENERATOR4 ) ;
}
if ( subID = = MapObjectSubID ( ) )
{
logGlobal - > error ( " Error: failed to find dwelling for %s of level %d " , ( * VLC - > townh ) [ faction ] - > getNameTranslated ( ) , int ( level ) ) ;
2023-12-16 13:42:48 +02:00
ID = Obj : : CREATURE_GENERATOR1 ;
2023-10-25 12:50:11 +02:00
subID = * RandomGeneratorUtil : : nextItem ( VLC - > objtypeh - > knownSubObjects ( Obj : : CREATURE_GENERATOR1 ) , rand ) ;
}
2023-11-01 16:10:33 +02:00
setType ( ID , subID ) ;
2023-10-25 12:50:11 +02:00
}
2023-04-26 20:55:56 +02:00
}
2024-06-01 17:28:17 +02:00
void CGDwelling : : initObj ( vstd : : RNG & rand )
2023-04-26 20:55:56 +02:00
{
2023-11-02 16:29:59 +02:00
switch ( ID . toEnum ( ) )
2023-04-26 20:55:56 +02:00
{
case Obj : : CREATURE_GENERATOR1 :
case Obj : : CREATURE_GENERATOR4 :
{
2023-10-24 16:11:25 +02:00
getObjectHandler ( ) - > configureObject ( this , rand ) ;
2023-04-26 20:55:56 +02:00
assert ( ! creatures . empty ( ) ) ;
assert ( ! creatures [ 0 ] . second . empty ( ) ) ;
break ;
}
case Obj : : REFUGEE_CAMP :
//is handled within newturn func
break ;
case Obj : : WAR_MACHINE_FACTORY :
creatures . resize ( 3 ) ;
creatures [ 0 ] . second . emplace_back ( CreatureID : : BALLISTA ) ;
creatures [ 1 ] . second . emplace_back ( CreatureID : : FIRST_AID_TENT ) ;
creatures [ 2 ] . second . emplace_back ( CreatureID : : AMMO_CART ) ;
break ;
default :
assert ( 0 ) ;
break ;
}
}
2023-11-06 18:27:16 +02:00
void CGDwelling : : setPropertyDer ( ObjProperty what , ObjPropertyID identifier )
2023-04-26 20:55:56 +02:00
{
switch ( what )
{
case ObjProperty : : AVAILABLE_CREATURE :
creatures . resize ( 1 ) ;
creatures [ 0 ] . second . resize ( 1 ) ;
2023-11-06 18:27:16 +02:00
creatures [ 0 ] . second [ 0 ] = identifier . as < CreatureID > ( ) ;
2023-04-26 20:55:56 +02:00
break ;
}
}
void CGDwelling : : onHeroVisit ( const CGHeroInstance * h ) const
{
if ( ID = = Obj : : REFUGEE_CAMP & & ! creatures [ 0 ] . first ) //Refugee Camp, no available cres
{
InfoWindow iw ;
iw . type = EInfoWindowMode : : AUTO ;
iw . player = h - > tempOwner ;
2023-06-18 11:18:25 +02:00
iw . text . appendLocalString ( EMetaText : : ADVOB_TXT , 44 ) ; //{%s} \n\n The camp is deserted. Perhaps you should try next week.
2023-11-02 22:01:49 +02:00
iw . text . replaceName ( ID ) ;
2023-04-26 20:55:56 +02:00
cb - > sendAndApply ( & iw ) ;
return ;
}
2023-08-19 20:43:50 +02:00
PlayerRelations relations = cb - > gameState ( ) - > getPlayerRelations ( h - > tempOwner , tempOwner ) ;
2023-04-26 20:55:56 +02:00
if ( relations = = PlayerRelations : : ALLIES )
return ; //do not allow recruiting or capturing
2023-08-19 20:43:50 +02:00
if ( relations = = PlayerRelations : : ENEMIES & & stacksCount ( ) > 0 ) //object is guarded, owned by enemy
2023-04-26 20:55:56 +02:00
{
BlockingDialog bd ( true , false ) ;
bd . player = h - > tempOwner ;
2023-06-18 11:18:25 +02:00
bd . text . appendLocalString ( EMetaText : : GENERAL_TXT , 421 ) ; //Much to your dismay, the %s is guarded by %s %s. Do you wish to fight the guards?
2023-11-02 22:01:49 +02:00
bd . text . replaceTextID ( getObjectHandler ( ) - > getNameTextID ( ) ) ;
2023-04-26 20:55:56 +02:00
if ( settings [ " gameTweaks " ] [ " numericCreaturesQuantities " ] . Bool ( ) )
2023-06-18 11:18:25 +02:00
bd . text . replaceRawString ( CCreature : : getQuantityRangeStringForId ( Slots ( ) . begin ( ) - > second - > getQuantityID ( ) ) ) ;
2023-04-26 20:55:56 +02:00
else
2023-06-18 11:18:25 +02:00
bd . text . replaceLocalString ( EMetaText : : ARRAY_TXT , 173 + ( int ) Slots ( ) . begin ( ) - > second - > getQuantityID ( ) * 3 ) ;
2023-11-02 22:01:49 +02:00
bd . text . replaceName ( * Slots ( ) . begin ( ) - > second ) ;
2023-04-26 20:55:56 +02:00
cb - > showBlockingDialog ( & bd ) ;
return ;
}
// TODO this shouldn't be hardcoded
2023-08-19 20:43:50 +02:00
if ( relations = = PlayerRelations : : ENEMIES & & ID ! = Obj : : WAR_MACHINE_FACTORY & & ID ! = Obj : : REFUGEE_CAMP )
2023-04-26 20:55:56 +02:00
{
cb - > setOwner ( this , h - > tempOwner ) ;
}
BlockingDialog bd ( true , false ) ;
bd . player = h - > tempOwner ;
if ( ID = = Obj : : CREATURE_GENERATOR1 | | ID = = Obj : : CREATURE_GENERATOR4 )
{
2023-06-18 11:18:25 +02:00
bd . text . appendLocalString ( EMetaText : : ADVOB_TXT , ID = = Obj : : CREATURE_GENERATOR1 ? 35 : 36 ) ; //{%s} Would you like to recruit %s? / {%s} Would you like to recruit %s, %s, %s, or %s?
2023-11-02 22:01:49 +02:00
bd . text . replaceTextID ( getObjectHandler ( ) - > getNameTextID ( ) ) ;
2023-04-26 20:55:56 +02:00
for ( const auto & elem : creatures )
2023-11-02 22:01:49 +02:00
bd . text . replaceNamePlural ( elem . second [ 0 ] ) ;
2023-04-26 20:55:56 +02:00
}
else if ( ID = = Obj : : REFUGEE_CAMP )
{
2023-06-18 11:18:25 +02:00
bd . text . appendLocalString ( EMetaText : : ADVOB_TXT , 35 ) ; //{%s} Would you like to recruit %s?
2023-11-02 22:01:49 +02:00
bd . text . replaceName ( ID ) ;
2023-04-26 20:55:56 +02:00
for ( const auto & elem : creatures )
2023-11-02 22:01:49 +02:00
bd . text . replaceNamePlural ( elem . second [ 0 ] ) ;
2023-04-26 20:55:56 +02:00
}
else if ( ID = = Obj : : WAR_MACHINE_FACTORY )
2023-06-18 11:18:25 +02:00
bd . text . appendLocalString ( EMetaText : : ADVOB_TXT , 157 ) ; //{War Machine Factory} Would you like to purchase War Machines?
2023-04-26 20:55:56 +02:00
else
throw std : : runtime_error ( " Illegal dwelling! " ) ;
2024-04-17 01:08:27 +02:00
if ( ID = = Obj : : REFUGEE_CAMP | | ( ID = = Obj : : CREATURE_GENERATOR1 & & VLC - > creatures ( ) - > getById ( creatures [ 0 ] . second [ 0 ] ) - > getLevel ( ) ! = 1 ) )
{
bd . flags | = BlockingDialog : : SAFE_TO_AUTOACCEPT ;
}
2023-04-26 20:55:56 +02:00
cb - > showBlockingDialog ( & bd ) ;
}
2024-06-01 17:28:17 +02:00
void CGDwelling : : newTurn ( vstd : : RNG & rand ) const
2023-04-26 20:55:56 +02:00
{
if ( cb - > getDate ( Date : : DAY_OF_WEEK ) ! = 1 ) //not first day of week
return ;
//town growths and War Machines Factories are handled separately
if ( ID = = Obj : : TOWN | | ID = = Obj : : WAR_MACHINE_FACTORY )
return ;
if ( ID = = Obj : : REFUGEE_CAMP ) //if it's a refugee camp, we need to pick an available creature
{
2023-11-06 18:27:16 +02:00
cb - > setObjPropertyID ( id , ObjProperty : : AVAILABLE_CREATURE , VLC - > creh - > pickRandomMonster ( rand ) ) ;
2023-04-26 20:55:56 +02:00
}
bool change = false ;
SetAvailableCreatures sac ;
sac . creatures = creatures ;
sac . tid = id ;
for ( size_t i = 0 ; i < creatures . size ( ) ; i + + )
{
if ( ! creatures [ i ] . second . empty ( ) )
{
bool creaturesAccumulate = false ;
if ( tempOwner . isValidPlayer ( ) )
creaturesAccumulate = VLC - > settings ( ) - > getBoolean ( EGameSettings : : DWELLINGS_ACCUMULATE_WHEN_OWNED ) ;
else
creaturesAccumulate = VLC - > settings ( ) - > getBoolean ( EGameSettings : : DWELLINGS_ACCUMULATE_WHEN_NEUTRAL ) ;
2023-11-02 18:45:46 +02:00
const CCreature * cre = creatures [ i ] . second [ 0 ] . toCreature ( ) ;
2024-01-05 16:57:44 +02:00
TQuantity amount = cre - > getGrowth ( ) * ( 1 + cre - > valOfBonuses ( BonusType : : CREATURE_GROWTH_PERCENT ) / 100 ) + cre - > valOfBonuses ( BonusType : : CREATURE_GROWTH , BonusCustomSubtype : : creatureLevel ( cre - > getLevel ( ) ) ) ;
2023-04-26 20:55:56 +02:00
if ( creaturesAccumulate & & ID ! = Obj : : REFUGEE_CAMP ) //camp should not try to accumulate different kinds of creatures
sac . creatures [ i ] . first + = amount ;
else
sac . creatures [ i ] . first = amount ;
change = true ;
}
}
if ( change )
cb - > sendAndApply ( & sac ) ;
updateGuards ( ) ;
}
2023-11-02 15:49:45 +02:00
std : : vector < Component > CGDwelling : : getPopupComponents ( PlayerColor player ) const
{
2024-08-02 17:03:25 +02:00
bool visitedByOwner = getOwner ( ) = = player ;
2023-11-02 15:49:45 +02:00
std : : vector < Component > result ;
if ( ID = = Obj : : CREATURE_GENERATOR1 & & ! creatures . empty ( ) )
{
for ( auto const & creature : creatures . front ( ) . second )
2024-08-02 17:03:25 +02:00
{
if ( visitedByOwner )
result . emplace_back ( ComponentType : : CREATURE , creature , creatures . front ( ) . first ) ;
else
result . emplace_back ( ComponentType : : CREATURE , creature ) ;
}
2023-11-02 15:49:45 +02:00
}
if ( ID = = Obj : : CREATURE_GENERATOR4 )
{
for ( auto const & creatureLevel : creatures )
{
if ( ! creatureLevel . second . empty ( ) )
2024-08-02 17:03:25 +02:00
{
if ( visitedByOwner )
result . emplace_back ( ComponentType : : CREATURE , creatureLevel . second . back ( ) , creatureLevel . first ) ;
else
result . emplace_back ( ComponentType : : CREATURE , creatureLevel . second . back ( ) ) ;
}
2023-11-02 15:49:45 +02:00
}
}
return result ;
}
2023-04-26 20:55:56 +02:00
void CGDwelling : : updateGuards ( ) const
{
//TODO: store custom guard config and use it
//TODO: store boolean flag for guards
bool guarded = false ;
//default condition - creatures are of level 5 or higher
for ( auto creatureEntry : creatures )
{
2023-11-02 17:48:48 +02:00
if ( VLC - > creatures ( ) - > getById ( creatureEntry . second . at ( 0 ) ) - > getLevel ( ) > = 5 & & ID ! = Obj : : REFUGEE_CAMP )
2023-04-26 20:55:56 +02:00
{
guarded = true ;
break ;
}
}
if ( guarded )
{
for ( auto creatureEntry : creatures )
{
2023-11-02 18:45:46 +02:00
const CCreature * crea = creatureEntry . second . at ( 0 ) . toCreature ( ) ;
2023-04-26 20:55:56 +02:00
SlotID slot = getSlotFor ( crea - > getId ( ) ) ;
if ( hasStackAtSlot ( slot ) ) //stack already exists, overwrite it
{
ChangeStackCount csc ;
csc . army = this - > id ;
csc . slot = slot ;
csc . count = crea - > getGrowth ( ) * 3 ;
csc . absoluteValue = true ;
cb - > sendAndApply ( & csc ) ;
}
else //slot is empty, create whole new stack
{
InsertNewStack ns ;
ns . army = this - > id ;
ns . slot = slot ;
ns . type = crea - > getId ( ) ;
ns . count = crea - > getGrowth ( ) * 3 ;
cb - > sendAndApply ( & ns ) ;
}
}
}
}
void CGDwelling : : heroAcceptsCreatures ( const CGHeroInstance * h ) const
{
CreatureID crid = creatures [ 0 ] . second [ 0 ] ;
auto * crs = crid . toCreature ( ) ;
TQuantity count = creatures [ 0 ] . first ;
if ( crs - > getLevel ( ) = = 1 & & ID ! = Obj : : REFUGEE_CAMP ) //first level - creatures are for free
{
if ( count ) //there are available creatures
{
2023-07-13 20:46:55 +02:00
2024-07-27 18:46:46 +02:00
if ( VLC - > settings ( ) - > getBoolean ( EGameSettings : : DWELLINGS_MERGE_ON_RECRUIT ) )
2023-07-13 20:46:55 +02:00
{
SlotID testSlot = h - > getSlotFor ( crid ) ;
if ( ! testSlot . validSlot ( ) ) //no available slot - try merging army of visiting hero
{
std : : pair < SlotID , SlotID > toMerge ;
2024-06-24 03:23:26 +02:00
if ( h - > mergeableStacks ( toMerge ) )
2023-07-13 20:46:55 +02:00
{
cb - > moveStack ( StackLocation ( h , toMerge . first ) , StackLocation ( h , toMerge . second ) , - 1 ) ; //merge toMerge.first into toMerge.second
assert ( ! h - > hasStackAtSlot ( toMerge . first ) ) ; //we have now a new free slot
}
}
}
2023-04-26 20:55:56 +02:00
SlotID slot = h - > getSlotFor ( crid ) ;
if ( ! slot . validSlot ( ) ) //no available slot
{
InfoWindow iw ;
iw . type = EInfoWindowMode : : AUTO ;
iw . player = h - > tempOwner ;
2023-06-18 11:18:25 +02:00
iw . text . appendLocalString ( EMetaText : : GENERAL_TXT , 425 ) ; //The %s would join your hero, but there aren't enough provisions to support them.
2023-11-02 22:01:49 +02:00
iw . text . replaceNamePlural ( crid ) ;
2023-04-26 20:55:56 +02:00
cb - > showInfoDialog ( & iw ) ;
}
else //give creatures
{
SetAvailableCreatures sac ;
sac . tid = id ;
sac . creatures = creatures ;
sac . creatures [ 0 ] . first = 0 ;
InfoWindow iw ;
iw . type = EInfoWindowMode : : AUTO ;
iw . player = h - > tempOwner ;
2023-06-18 11:18:25 +02:00
iw . text . appendLocalString ( EMetaText : : GENERAL_TXT , 423 ) ; //%d %s join your army.
iw . text . replaceNumber ( count ) ;
2023-11-02 22:01:49 +02:00
iw . text . replaceNamePlural ( crid ) ;
2023-04-26 20:55:56 +02:00
cb - > showInfoDialog ( & iw ) ;
cb - > sendAndApply ( & sac ) ;
cb - > addToSlot ( StackLocation ( h , slot ) , crs , count ) ;
}
}
else //there no creatures
{
InfoWindow iw ;
iw . type = EInfoWindowMode : : AUTO ;
2023-06-18 11:18:25 +02:00
iw . text . appendLocalString ( EMetaText : : GENERAL_TXT , 422 ) ; //There are no %s here to recruit.
2023-11-02 22:01:49 +02:00
iw . text . replaceNamePlural ( crid ) ;
2023-04-26 20:55:56 +02:00
iw . player = h - > tempOwner ;
cb - > sendAndApply ( & iw ) ;
}
}
else
{
if ( ID = = Obj : : WAR_MACHINE_FACTORY ) //pick available War Machines
{
//there is 1 war machine available to recruit if hero doesn't have one
SetAvailableCreatures sac ;
sac . tid = id ;
sac . creatures = creatures ;
sac . creatures [ 0 ] . first = ! h - > getArt ( ArtifactPosition : : MACH1 ) ; //ballista
sac . creatures [ 1 ] . first = ! h - > getArt ( ArtifactPosition : : MACH3 ) ; //first aid tent
sac . creatures [ 2 ] . first = ! h - > getArt ( ArtifactPosition : : MACH2 ) ; //ammo cart
cb - > sendAndApply ( & sac ) ;
}
2023-09-28 00:17:05 +02:00
auto windowMode = ( ID = = Obj : : CREATURE_GENERATOR1 | | ID = = Obj : : REFUGEE_CAMP ) ? EOpenWindowMode : : RECRUITMENT_FIRST : EOpenWindowMode : : RECRUITMENT_ALL ;
2023-10-04 16:24:19 +02:00
cb - > showObjectWindow ( this , windowMode , h , true ) ;
2023-04-26 20:55:56 +02:00
}
}
void CGDwelling : : battleFinished ( const CGHeroInstance * hero , const BattleResult & result ) const
{
2024-08-11 22:22:35 +02:00
if ( result . winner = = BattleSide : : ATTACKER )
2023-04-26 20:55:56 +02:00
{
onHeroVisit ( hero ) ;
}
}
2024-08-09 00:28:28 +02:00
void CGDwelling : : blockingDialogAnswered ( const CGHeroInstance * hero , int32_t answer ) const
2023-04-26 20:55:56 +02:00
{
auto relations = cb - > getPlayerRelations ( getOwner ( ) , hero - > getOwner ( ) ) ;
if ( stacksCount ( ) > 0 & & relations = = PlayerRelations : : ENEMIES ) //guards present
{
if ( answer )
cb - > startBattleI ( hero , this ) ;
}
else if ( answer )
{
heroAcceptsCreatures ( hero ) ;
}
}
void CGDwelling : : serializeJsonOptions ( JsonSerializeFormat & handler )
{
2023-11-02 16:29:59 +02:00
switch ( ID . toEnum ( ) )
2023-04-26 20:55:56 +02:00
{
case Obj : : WAR_MACHINE_FACTORY :
case Obj : : REFUGEE_CAMP :
//do nothing
break ;
case Obj : : RANDOM_DWELLING :
case Obj : : RANDOM_DWELLING_LVL :
case Obj : : RANDOM_DWELLING_FACTION :
2023-10-25 12:50:11 +02:00
if ( ! handler . saving )
randomizationInfo = CGDwellingRandomizationInfo ( ) ;
randomizationInfo - > serializeJson ( handler ) ;
[[fallthrough]] ;
2023-04-26 20:55:56 +02:00
default :
serializeJsonOwner ( handler ) ;
break ;
}
}
2024-08-25 17:04:44 +02:00
const IOwnableObject * CGDwelling : : asOwnable ( ) const
{
2024-09-04 21:46:58 +02:00
switch ( ID . toEnum ( ) )
{
case Obj : : WAR_MACHINE_FACTORY :
case Obj : : REFUGEE_CAMP :
return nullptr ; // can't be owned
default :
return this ;
}
2024-08-25 17:04:44 +02:00
}
ResourceSet CGDwelling : : dailyIncome ( ) const
{
return { } ;
}
std : : vector < CreatureID > CGDwelling : : providedCreatures ( ) const
{
if ( ID = = Obj : : WAR_MACHINE_FACTORY | | ID = = Obj : : REFUGEE_CAMP )
return { } ;
std : : vector < CreatureID > result ;
for ( const auto & level : creatures )
result . insert ( result . end ( ) , level . second . begin ( ) , level . second . end ( ) ) ;
return result ;
}
2023-04-26 20:55:56 +02:00
VCMI_LIB_NAMESPACE_END