2015-12-02 21:05:10 +02:00
/*
2014-06-05 20:26:50 +03:00
* CGTownInstance . cpp , part of VCMI engine
2014-06-05 19:52:14 +03:00
*
* 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 "CGTownInstance.h"
2014-06-22 17:26:08 +03:00
# include "CObjectClassesHandler.h"
2016-02-25 21:59:17 +02:00
# include "../spells/CSpellHandler.h"
2014-06-05 19:52:14 +03:00
2014-06-05 20:26:50 +03:00
# include "../NetPacks.h"
2014-06-05 19:52:14 +03:00
# include "../CGeneralTextHandler.h"
2014-06-25 17:11:07 +03:00
# include "../CModHandler.h"
# include "../IGameCallback.h"
# include "../CGameState.h"
2017-05-28 15:23:42 +02:00
# include "../mapping/CMap.h"
2015-12-02 21:39:53 +02:00
# include "../CPlayerState.h"
2016-02-22 01:37:19 +02:00
# include "../serializer/JsonSerializeFormat.h"
2021-01-14 00:02:13 +02:00
# include "../HeroBonus.h"
2014-06-05 19:52:14 +03:00
2022-07-26 15:07:42 +02:00
VCMI_LIB_NAMESPACE_BEGIN
2014-06-05 19:52:14 +03:00
std : : vector < const CArtifact * > CGTownInstance : : merchantArtifacts ;
std : : vector < int > CGTownInstance : : universitySkills ;
2017-05-28 13:23:02 +02:00
CSpecObjInfo : : CSpecObjInfo ( ) :
owner ( nullptr )
{
}
CCreGenAsCastleInfo : : CCreGenAsCastleInfo ( ) :
CSpecObjInfo ( ) , asCastle ( false ) , identifier ( 0 )
{
}
2016-11-13 12:38:42 +02:00
void CCreGenAsCastleInfo : : serializeJson ( JsonSerializeFormat & handler )
{
handler . serializeString ( " sameAsTown " , instanceId ) ;
if ( ! handler . saving )
{
2022-11-15 02:20:55 +02:00
asCastle = ! instanceId . empty ( ) ;
2016-11-13 12:38:42 +02:00
allowedFactions . clear ( ) ;
}
if ( ! asCastle )
{
std : : vector < bool > standard ;
Entities redesign and a few ERM features
* Made most Handlers derived from CHandlerBase and moved service API there.
* Declared existing Entity APIs.
* Added basic script context caching
* Started Lua script module
* Started Lua spell effect API
* Started script state persistence
* Started battle info callback binding
* CommitPackage removed
* Extracted spells::Caster to own header; Expanded Spell API.
* implemented !!MC:S, !!FU:E, !!FU:P, !!MA, !!VR:H, !!VR:C
* !!BU:C, !!BU:E, !!BU:G, !!BU:M implemented
* Allow use of "MC:S@varName@" to declare normal variable (technically v-variable with string key)
* Re-enabled VERM macros.
* !?GM0 added
* !?TM implemented
* Added !!MF:N
* Started !?OB, !!BM, !!HE, !!OW, !!UN
* Added basic support of w-variables
* Added support for ERM indirect variables
* Made !?FU regular trigger
* !!re (ERA loop receiver) implemented
* Fixed ERM receivers with zero args.
2018-03-17 16:58:30 +02:00
standard . resize ( VLC - > townh - > size ( ) , true ) ;
2016-11-13 12:38:42 +02:00
Entities redesign and a few ERM features
* Made most Handlers derived from CHandlerBase and moved service API there.
* Declared existing Entity APIs.
* Added basic script context caching
* Started Lua script module
* Started Lua spell effect API
* Started script state persistence
* Started battle info callback binding
* CommitPackage removed
* Extracted spells::Caster to own header; Expanded Spell API.
* implemented !!MC:S, !!FU:E, !!FU:P, !!MA, !!VR:H, !!VR:C
* !!BU:C, !!BU:E, !!BU:G, !!BU:M implemented
* Allow use of "MC:S@varName@" to declare normal variable (technically v-variable with string key)
* Re-enabled VERM macros.
* !?GM0 added
* !?TM implemented
* Added !!MF:N
* Started !?OB, !!BM, !!HE, !!OW, !!UN
* Added basic support of w-variables
* Added support for ERM indirect variables
* Made !?FU regular trigger
* !!re (ERA loop receiver) implemented
* Fixed ERM receivers with zero args.
2018-03-17 16:58:30 +02:00
JsonSerializeFormat : : LIC allowedLIC ( standard , & FactionID : : decode , & FactionID : : encode ) ;
2016-11-13 12:38:42 +02:00
allowedLIC . any = allowedFactions ;
handler . serializeLIC ( " allowedFactions " , allowedLIC ) ;
if ( ! handler . saving )
{
allowedFactions = allowedLIC . any ;
}
}
}
2017-05-28 13:23:02 +02:00
CCreGenLeveledInfo : : CCreGenLeveledInfo ( ) :
CSpecObjInfo ( ) ,
minLevel ( 0 ) , maxLevel ( 7 )
{
}
2016-11-13 12:38:42 +02:00
void CCreGenLeveledInfo : : serializeJson ( JsonSerializeFormat & handler )
{
handler . serializeInt ( " minLevel " , minLevel , ui8 ( 1 ) ) ;
handler . serializeInt ( " maxLevel " , maxLevel , ui8 ( 7 ) ) ;
if ( ! handler . saving )
{
//todo: safely allow any level > 7
vstd : : amax ( minLevel , 1 ) ;
vstd : : amin ( minLevel , 7 ) ;
vstd : : abetween ( maxLevel , minLevel , 7 ) ;
}
}
void CCreGenLeveledCastleInfo : : serializeJson ( JsonSerializeFormat & handler )
{
CCreGenAsCastleInfo : : serializeJson ( handler ) ;
CCreGenLeveledInfo : : serializeJson ( handler ) ;
}
CGDwelling : : CGDwelling ( ) :
CArmedInstance ( )
{
info = nullptr ;
}
CGDwelling : : ~ CGDwelling ( )
{
vstd : : clear_pointer ( info ) ;
}
2016-09-09 19:30:36 +02:00
void CGDwelling : : initObj ( CRandomGenerator & rand )
2014-06-05 19:52:14 +03:00
{
switch ( ID )
{
case Obj : : CREATURE_GENERATOR1 :
2014-06-15 19:43:01 +03:00
case Obj : : CREATURE_GENERATOR4 :
2014-06-05 19:52:14 +03:00
{
2016-09-09 19:30:36 +02:00
VLC - > objtypeh - > getHandlerFor ( ID , subID ) - > configureObject ( this , rand ) ;
2014-06-05 19:52:14 +03:00
if ( getOwner ( ) ! = PlayerColor : : NEUTRAL )
cb - > gameState ( ) - > players [ getOwner ( ) ] . dwellings . push_back ( this ) ;
2014-08-19 13:16:25 +03:00
assert ( ! creatures . empty ( ) ) ;
assert ( ! creatures [ 0 ] . second . empty ( ) ) ;
2014-06-15 19:43:01 +03:00
break ;
2014-08-19 13:16:25 +03:00
}
2014-06-05 19:52:14 +03:00
case Obj : : REFUGEE_CAMP :
//is handled within newturn func
break ;
case Obj : : WAR_MACHINE_FACTORY :
creatures . resize ( 3 ) ;
creatures [ 0 ] . second . push_back ( CreatureID : : BALLISTA ) ;
creatures [ 1 ] . second . push_back ( CreatureID : : FIRST_AID_TENT ) ;
creatures [ 2 ] . second . push_back ( CreatureID : : AMMO_CART ) ;
break ;
default :
assert ( 0 ) ;
break ;
}
}
2016-11-13 12:38:42 +02:00
void CGDwelling : : initRandomObjectInfo ( )
{
vstd : : clear_pointer ( info ) ;
switch ( ID )
{
case Obj : : RANDOM_DWELLING : info = new CCreGenLeveledCastleInfo ( ) ;
break ;
case Obj : : RANDOM_DWELLING_LVL : info = new CCreGenAsCastleInfo ( ) ;
break ;
case Obj : : RANDOM_DWELLING_FACTION : info = new CCreGenLeveledInfo ( ) ;
break ;
}
if ( info )
info - > owner = this ;
}
2014-06-24 20:39:36 +03:00
void CGDwelling : : setPropertyDer ( ui8 what , ui32 val )
2014-06-05 19:52:14 +03:00
{
switch ( what )
{
case ObjProperty : : OWNER : //change owner
2017-10-07 16:42:33 +02:00
if ( ID = = Obj : : CREATURE_GENERATOR1 | | ID = = Obj : : CREATURE_GENERATOR2
| | ID = = Obj : : CREATURE_GENERATOR3 | | ID = = Obj : : CREATURE_GENERATOR4 )
2014-06-05 19:52:14 +03:00
{
if ( tempOwner ! = PlayerColor : : NEUTRAL )
{
std : : vector < ConstTransitivePtr < CGDwelling > > * dwellings = & cb - > gameState ( ) - > players [ tempOwner ] . dwellings ;
dwellings - > erase ( std : : find ( dwellings - > begin ( ) , dwellings - > end ( ) , this ) ) ;
}
if ( PlayerColor ( val ) ! = PlayerColor : : NEUTRAL ) //can new owner be neutral?
cb - > gameState ( ) - > players [ PlayerColor ( val ) ] . dwellings . push_back ( this ) ;
}
break ;
case ObjProperty : : AVAILABLE_CREATURE :
creatures . resize ( 1 ) ;
creatures [ 0 ] . second . resize ( 1 ) ;
creatures [ 0 ] . second [ 0 ] = CreatureID ( val ) ;
break ;
}
}
2014-06-24 20:39:36 +03:00
2014-06-05 19:52:14 +03:00
void CGDwelling : : onHeroVisit ( const CGHeroInstance * h ) const
{
if ( ID = = Obj : : REFUGEE_CAMP & & ! creatures [ 0 ] . first ) //Refugee Camp, no available cres
{
InfoWindow iw ;
iw . player = h - > tempOwner ;
iw . text . addTxt ( MetaString : : ADVOB_TXT , 44 ) ; //{%s} \n\n The camp is deserted. Perhaps you should try next week.
iw . text . addReplacement ( MetaString : : OBJ_NAMES , ID ) ;
cb - > sendAndApply ( & iw ) ;
return ;
}
PlayerRelations : : PlayerRelations relations = cb - > gameState ( ) - > getPlayerRelations ( h - > tempOwner , tempOwner ) ;
if ( relations = = PlayerRelations : : ALLIES )
return ; //do not allow recruiting or capturing
if ( ! relations & & stacksCount ( ) > 0 ) //object is guarded, owned by enemy
{
BlockingDialog bd ( true , false ) ;
bd . player = h - > tempOwner ;
bd . text . addTxt ( MetaString : : GENERAL_TXT , 421 ) ; //Much to your dismay, the %s is guarded by %s %s. Do you wish to fight the guards?
bd . text . addReplacement ( ID = = Obj : : CREATURE_GENERATOR1 ? MetaString : : CREGENS : MetaString : : CREGENS4 , subID ) ;
2017-07-25 23:00:29 +02:00
bd . text . addReplacement ( MetaString : : ARRAY_TXT , 173 + Slots ( ) . begin ( ) - > second - > getQuantityID ( ) * 3 ) ;
2014-06-05 19:52:14 +03:00
bd . text . addReplacement ( * Slots ( ) . begin ( ) - > second ) ;
cb - > showBlockingDialog ( & bd ) ;
return ;
}
2016-02-13 18:26:47 +02:00
// TODO this shouldn't be hardcoded
if ( ! relations & & ID ! = Obj : : WAR_MACHINE_FACTORY & & ID ! = Obj : : REFUGEE_CAMP )
2014-06-05 19:52:14 +03:00
{
cb - > setOwner ( this , h - > tempOwner ) ;
}
BlockingDialog bd ( true , false ) ;
bd . player = h - > tempOwner ;
if ( ID = = Obj : : CREATURE_GENERATOR1 | | ID = = Obj : : CREATURE_GENERATOR4 )
{
bd . text . addTxt ( MetaString : : 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?
bd . text . addReplacement ( ID = = Obj : : CREATURE_GENERATOR1 ? MetaString : : CREGENS : MetaString : : CREGENS4 , subID ) ;
for ( auto & elem : creatures )
bd . text . addReplacement ( MetaString : : CRE_PL_NAMES , elem . second [ 0 ] ) ;
}
else if ( ID = = Obj : : REFUGEE_CAMP )
{
bd . text . addTxt ( MetaString : : ADVOB_TXT , 35 ) ; //{%s} Would you like to recruit %s?
bd . text . addReplacement ( MetaString : : OBJ_NAMES , ID ) ;
for ( auto & elem : creatures )
bd . text . addReplacement ( MetaString : : CRE_PL_NAMES , elem . second [ 0 ] ) ;
}
else if ( ID = = Obj : : WAR_MACHINE_FACTORY )
bd . text . addTxt ( MetaString : : ADVOB_TXT , 157 ) ; //{War Machine Factory} Would you like to purchase War Machines?
else
throw std : : runtime_error ( " Illegal dwelling! " ) ;
cb - > showBlockingDialog ( & bd ) ;
}
2016-09-09 19:30:36 +02:00
void CGDwelling : : newTurn ( CRandomGenerator & rand ) const
2014-06-05 19:52:14 +03: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
{
2016-09-09 19:30:36 +02:00
cb - > setObjProperty ( id , ObjProperty : : AVAILABLE_CREATURE , VLC - > creh - > pickRandomMonster ( rand ) ) ;
2014-06-05 19:52:14 +03: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 . size ( ) )
{
Entities redesign and a few ERM features
* Made most Handlers derived from CHandlerBase and moved service API there.
* Declared existing Entity APIs.
* Added basic script context caching
* Started Lua script module
* Started Lua spell effect API
* Started script state persistence
* Started battle info callback binding
* CommitPackage removed
* Extracted spells::Caster to own header; Expanded Spell API.
* implemented !!MC:S, !!FU:E, !!FU:P, !!MA, !!VR:H, !!VR:C
* !!BU:C, !!BU:E, !!BU:G, !!BU:M implemented
* Allow use of "MC:S@varName@" to declare normal variable (technically v-variable with string key)
* Re-enabled VERM macros.
* !?GM0 added
* !?TM implemented
* Added !!MF:N
* Started !?OB, !!BM, !!HE, !!OW, !!UN
* Added basic support of w-variables
* Added support for ERM indirect variables
* Made !?FU regular trigger
* !!re (ERA loop receiver) implemented
* Fixed ERM receivers with zero args.
2018-03-17 16:58:30 +02:00
CCreature * cre = VLC - > creh - > objects [ creatures [ i ] . second [ 0 ] ] ;
2014-06-05 19:52:14 +03:00
TQuantity amount = cre - > growth * ( 1 + cre - > valOfBonuses ( Bonus : : CREATURE_GROWTH_PERCENT ) / 100 ) + cre - > valOfBonuses ( Bonus : : CREATURE_GROWTH ) ;
if ( VLC - > modh - > settings . DWELLINGS_ACCUMULATE_CREATURES & & 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 ) ;
2015-06-05 11:28:14 +02:00
updateGuards ( ) ;
}
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 )
{
Entities redesign and a few ERM features
* Made most Handlers derived from CHandlerBase and moved service API there.
* Declared existing Entity APIs.
* Added basic script context caching
* Started Lua script module
* Started Lua spell effect API
* Started script state persistence
* Started battle info callback binding
* CommitPackage removed
* Extracted spells::Caster to own header; Expanded Spell API.
* implemented !!MC:S, !!FU:E, !!FU:P, !!MA, !!VR:H, !!VR:C
* !!BU:C, !!BU:E, !!BU:G, !!BU:M implemented
* Allow use of "MC:S@varName@" to declare normal variable (technically v-variable with string key)
* Re-enabled VERM macros.
* !?GM0 added
* !?TM implemented
* Added !!MF:N
* Started !?OB, !!BM, !!HE, !!OW, !!UN
* Added basic support of w-variables
* Added support for ERM indirect variables
* Made !?FU regular trigger
* !!re (ERA loop receiver) implemented
* Fixed ERM receivers with zero args.
2018-03-17 16:58:30 +02:00
if ( VLC - > creh - > objects [ creatureEntry . second . at ( 0 ) ] - > level > = 5 & & ID ! = Obj : : REFUGEE_CAMP )
2015-06-05 11:28:14 +02:00
{
guarded = true ;
break ;
}
}
if ( guarded )
{
for ( auto creatureEntry : creatures )
{
Entities redesign and a few ERM features
* Made most Handlers derived from CHandlerBase and moved service API there.
* Declared existing Entity APIs.
* Added basic script context caching
* Started Lua script module
* Started Lua spell effect API
* Started script state persistence
* Started battle info callback binding
* CommitPackage removed
* Extracted spells::Caster to own header; Expanded Spell API.
* implemented !!MC:S, !!FU:E, !!FU:P, !!MA, !!VR:H, !!VR:C
* !!BU:C, !!BU:E, !!BU:G, !!BU:M implemented
* Allow use of "MC:S@varName@" to declare normal variable (technically v-variable with string key)
* Re-enabled VERM macros.
* !?GM0 added
* !?TM implemented
* Added !!MF:N
* Started !?OB, !!BM, !!HE, !!OW, !!UN
* Added basic support of w-variables
* Added support for ERM indirect variables
* Made !?FU regular trigger
* !!re (ERA loop receiver) implemented
* Fixed ERM receivers with zero args.
2018-03-17 16:58:30 +02:00
const CCreature * crea = VLC - > creh - > objects [ creatureEntry . second . at ( 0 ) ] ;
2015-06-05 11:28:14 +02:00
SlotID slot = getSlotFor ( crea - > idNumber ) ;
2018-03-10 23:19:36 +02:00
2015-06-05 11:28:14 +02:00
if ( hasStackAtSlot ( slot ) ) //stack already exists, overwrite it
{
ChangeStackCount csc ;
2018-03-10 23:19:36 +02:00
csc . army = this - > id ;
csc . slot = slot ;
2015-06-05 11:28:14 +02:00
csc . count = crea - > growth * 3 ;
csc . absoluteValue = true ;
cb - > sendAndApply ( & csc ) ;
}
else //slot is empty, create whole new stack
{
InsertNewStack ns ;
2018-03-10 23:19:36 +02:00
ns . army = this - > id ;
ns . slot = slot ;
ns . type = crea - > idNumber ;
ns . count = crea - > growth * 3 ;
2015-06-05 11:28:14 +02:00
cb - > sendAndApply ( & ns ) ;
}
}
}
2014-06-05 19:52:14 +03:00
}
void CGDwelling : : heroAcceptsCreatures ( const CGHeroInstance * h ) const
{
CreatureID crid = creatures [ 0 ] . second [ 0 ] ;
Entities redesign and a few ERM features
* Made most Handlers derived from CHandlerBase and moved service API there.
* Declared existing Entity APIs.
* Added basic script context caching
* Started Lua script module
* Started Lua spell effect API
* Started script state persistence
* Started battle info callback binding
* CommitPackage removed
* Extracted spells::Caster to own header; Expanded Spell API.
* implemented !!MC:S, !!FU:E, !!FU:P, !!MA, !!VR:H, !!VR:C
* !!BU:C, !!BU:E, !!BU:G, !!BU:M implemented
* Allow use of "MC:S@varName@" to declare normal variable (technically v-variable with string key)
* Re-enabled VERM macros.
* !?GM0 added
* !?TM implemented
* Added !!MF:N
* Started !?OB, !!BM, !!HE, !!OW, !!UN
* Added basic support of w-variables
* Added support for ERM indirect variables
* Made !?FU regular trigger
* !!re (ERA loop receiver) implemented
* Fixed ERM receivers with zero args.
2018-03-17 16:58:30 +02:00
CCreature * crs = VLC - > creh - > objects [ crid ] ;
2014-06-05 19:52:14 +03:00
TQuantity count = creatures [ 0 ] . first ;
if ( crs - > level = = 1 & & ID ! = Obj : : REFUGEE_CAMP ) //first level - creatures are for free
{
if ( count ) //there are available creatures
{
SlotID slot = h - > getSlotFor ( crid ) ;
if ( ! slot . validSlot ( ) ) //no available slot
{
InfoWindow iw ;
iw . player = h - > tempOwner ;
iw . text . addTxt ( MetaString : : GENERAL_TXT , 425 ) ; //The %s would join your hero, but there aren't enough provisions to support them.
iw . text . addReplacement ( MetaString : : CRE_PL_NAMES , crid ) ;
cb - > showInfoDialog ( & iw ) ;
}
else //give creatures
{
SetAvailableCreatures sac ;
sac . tid = id ;
sac . creatures = creatures ;
sac . creatures [ 0 ] . first = 0 ;
InfoWindow iw ;
iw . player = h - > tempOwner ;
iw . text . addTxt ( MetaString : : GENERAL_TXT , 423 ) ; //%d %s join your army.
iw . text . addReplacement ( count ) ;
iw . text . addReplacement ( MetaString : : CRE_PL_NAMES , crid ) ;
cb - > showInfoDialog ( & iw ) ;
cb - > sendAndApply ( & sac ) ;
cb - > addToSlot ( StackLocation ( h , slot ) , crs , count ) ;
}
}
else //there no creatures
{
InfoWindow iw ;
iw . text . addTxt ( MetaString : : GENERAL_TXT , 422 ) ; //There are no %s here to recruit.
iw . text . addReplacement ( MetaString : : CRE_PL_NAMES , crid ) ;
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 ) ;
}
OpenWindow ow ;
ow . id1 = id . getNum ( ) ;
ow . id2 = h - > id . getNum ( ) ;
ow . window = ( ID = = Obj : : CREATURE_GENERATOR1 | | ID = = Obj : : REFUGEE_CAMP )
? OpenWindow : : RECRUITMENT_FIRST
: OpenWindow : : RECRUITMENT_ALL ;
cb - > sendAndApply ( & ow ) ;
}
}
void CGDwelling : : battleFinished ( const CGHeroInstance * hero , const BattleResult & result ) const
{
if ( result . winner = = 0 )
{
onHeroVisit ( hero ) ;
}
}
void CGDwelling : : blockingDialogAnswered ( const CGHeroInstance * hero , ui32 answer ) const
{
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 ) ;
}
}
2016-02-22 01:37:19 +02:00
void CGDwelling : : serializeJsonOptions ( JsonSerializeFormat & handler )
2016-01-23 18:53:02 +02:00
{
2016-11-13 12:38:42 +02:00
if ( ! handler . saving )
initRandomObjectInfo ( ) ;
switch ( ID )
{
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 :
info - > serializeJson ( handler ) ;
//fall through
default :
2016-02-22 01:37:19 +02:00
serializeJsonOwner ( handler ) ;
2016-11-13 12:38:42 +02:00
break ;
}
2016-01-23 18:53:02 +02:00
}
2016-01-31 17:01:58 +02:00
int CGTownInstance : : getSightRadius ( ) const //returns sight distance
2014-06-05 19:52:14 +03:00
{
2020-10-02 23:55:46 +02:00
auto ret = CBuilding : : HEIGHT_NO_TOWER ;
for ( const auto & bid : builtBuildings )
2014-06-05 19:52:14 +03:00
{
2020-10-07 14:12:32 +02:00
if ( bid . IsSpecialOrGrail ( ) )
2020-10-02 23:55:46 +02:00
{
auto height = town - > buildings . at ( bid ) - > height ;
if ( ret < height )
ret = height ;
2014-06-05 19:52:14 +03:00
}
Entities redesign and a few ERM features
* Made most Handlers derived from CHandlerBase and moved service API there.
* Declared existing Entity APIs.
* Added basic script context caching
* Started Lua script module
* Started Lua spell effect API
* Started script state persistence
* Started battle info callback binding
* CommitPackage removed
* Extracted spells::Caster to own header; Expanded Spell API.
* implemented !!MC:S, !!FU:E, !!FU:P, !!MA, !!VR:H, !!VR:C
* !!BU:C, !!BU:E, !!BU:G, !!BU:M implemented
* Allow use of "MC:S@varName@" to declare normal variable (technically v-variable with string key)
* Re-enabled VERM macros.
* !?GM0 added
* !?TM implemented
* Added !!MF:N
* Started !?OB, !!BM, !!HE, !!OW, !!UN
* Added basic support of w-variables
* Added support for ERM indirect variables
* Made !?FU regular trigger
* !!re (ERA loop receiver) implemented
* Fixed ERM receivers with zero args.
2018-03-17 16:58:30 +02:00
}
2020-10-02 23:55:46 +02:00
return ret ;
2014-06-05 19:52:14 +03:00
}
void CGTownInstance : : setPropertyDer ( ui8 what , ui32 val )
{
///this is freakin' overcomplicated solution
switch ( what )
{
case ObjProperty : : STRUCTURE_ADD_VISITING_HERO :
bonusingBuildings [ val ] - > setProperty ( ObjProperty : : VISITORS , visitingHero - > id . getNum ( ) ) ;
break ;
case ObjProperty : : STRUCTURE_CLEAR_VISITORS :
bonusingBuildings [ val ] - > setProperty ( ObjProperty : : STRUCTURE_CLEAR_VISITORS , 0 ) ;
break ;
case ObjProperty : : STRUCTURE_ADD_GARRISONED_HERO : //add garrisoned hero to visitors
bonusingBuildings [ val ] - > setProperty ( ObjProperty : : VISITORS , garrisonHero - > id . getNum ( ) ) ;
break ;
case ObjProperty : : BONUS_VALUE_FIRST :
bonusValue . first = val ;
break ;
case ObjProperty : : BONUS_VALUE_SECOND :
bonusValue . second = val ;
break ;
}
}
CGTownInstance : : EFortLevel CGTownInstance : : fortLevel ( ) const //0 - none, 1 - fort, 2 - citadel, 3 - castle
{
if ( hasBuilt ( BuildingID : : CASTLE ) )
return CASTLE ;
if ( hasBuilt ( BuildingID : : CITADEL ) )
return CITADEL ;
if ( hasBuilt ( BuildingID : : FORT ) )
return FORT ;
return NONE ;
}
int CGTownInstance : : hallLevel ( ) const // -1 - none, 0 - village, 1 - town, 2 - city, 3 - capitol
{
if ( hasBuilt ( BuildingID : : CAPITOL ) )
return 3 ;
if ( hasBuilt ( BuildingID : : CITY_HALL ) )
return 2 ;
if ( hasBuilt ( BuildingID : : TOWN_HALL ) )
return 1 ;
if ( hasBuilt ( BuildingID : : VILLAGE_HALL ) )
return 0 ;
return - 1 ;
}
2016-11-26 21:12:52 +02:00
2014-06-05 19:52:14 +03:00
int CGTownInstance : : mageGuildLevel ( ) const
{
if ( hasBuilt ( BuildingID : : MAGES_GUILD_5 ) )
return 5 ;
if ( hasBuilt ( BuildingID : : MAGES_GUILD_4 ) )
return 4 ;
if ( hasBuilt ( BuildingID : : MAGES_GUILD_3 ) )
return 3 ;
if ( hasBuilt ( BuildingID : : MAGES_GUILD_2 ) )
return 2 ;
if ( hasBuilt ( BuildingID : : MAGES_GUILD_1 ) )
return 1 ;
return 0 ;
}
int CGTownInstance : : getHordeLevel ( const int & HID ) const //HID - 0 or 1; returns creature level or -1 if that horde structure is not present
{
return town - > hordeLvl . at ( HID ) ;
}
int CGTownInstance : : creatureGrowth ( const int & level ) const
{
return getGrowthInfo ( level ) . totalGrowth ( ) ;
}
GrowthInfo CGTownInstance : : getGrowthInfo ( int level ) const
{
GrowthInfo ret ;
if ( level < 0 | | level > = GameConstants : : CREATURES_PER_TOWN )
return ret ;
if ( creatures [ level ] . second . empty ( ) )
return ret ; //no dwelling
Entities redesign and a few ERM features
* Made most Handlers derived from CHandlerBase and moved service API there.
* Declared existing Entity APIs.
* Added basic script context caching
* Started Lua script module
* Started Lua spell effect API
* Started script state persistence
* Started battle info callback binding
* CommitPackage removed
* Extracted spells::Caster to own header; Expanded Spell API.
* implemented !!MC:S, !!FU:E, !!FU:P, !!MA, !!VR:H, !!VR:C
* !!BU:C, !!BU:E, !!BU:G, !!BU:M implemented
* Allow use of "MC:S@varName@" to declare normal variable (technically v-variable with string key)
* Re-enabled VERM macros.
* !?GM0 added
* !?TM implemented
* Added !!MF:N
* Started !?OB, !!BM, !!HE, !!OW, !!UN
* Added basic support of w-variables
* Added support for ERM indirect variables
* Made !?FU regular trigger
* !!re (ERA loop receiver) implemented
* Fixed ERM receivers with zero args.
2018-03-17 16:58:30 +02:00
const CCreature * creature = VLC - > creh - > objects [ creatures [ level ] . second . back ( ) ] ;
2014-06-05 19:52:14 +03:00
const int base = creature - > growth ;
int castleBonus = 0 ;
ret . entries . push_back ( GrowthInfo : : Entry ( VLC - > generaltexth - > allTexts [ 590 ] , base ) ) ; // \n\nBasic growth %d"
if ( hasBuilt ( BuildingID : : CASTLE ) )
ret . entries . push_back ( GrowthInfo : : Entry ( subID , BuildingID : : CASTLE , castleBonus = base ) ) ;
else if ( hasBuilt ( BuildingID : : CITADEL ) )
ret . entries . push_back ( GrowthInfo : : Entry ( subID , BuildingID : : CITADEL , castleBonus = base / 2 ) ) ;
if ( town - > hordeLvl . at ( 0 ) = = level ) //horde 1
if ( hasBuilt ( BuildingID : : HORDE_1 ) )
ret . entries . push_back ( GrowthInfo : : Entry ( subID , BuildingID : : HORDE_1 , creature - > hordeGrowth ) ) ;
if ( town - > hordeLvl . at ( 1 ) = = level ) //horde 2
if ( hasBuilt ( BuildingID : : HORDE_2 ) )
ret . entries . push_back ( GrowthInfo : : Entry ( subID , BuildingID : : HORDE_2 , creature - > hordeGrowth ) ) ;
int dwellingBonus = 0 ;
Entities redesign and a few ERM features
* Made most Handlers derived from CHandlerBase and moved service API there.
* Declared existing Entity APIs.
* Added basic script context caching
* Started Lua script module
* Started Lua spell effect API
* Started script state persistence
* Started battle info callback binding
* CommitPackage removed
* Extracted spells::Caster to own header; Expanded Spell API.
* implemented !!MC:S, !!FU:E, !!FU:P, !!MA, !!VR:H, !!VR:C
* !!BU:C, !!BU:E, !!BU:G, !!BU:M implemented
* Allow use of "MC:S@varName@" to declare normal variable (technically v-variable with string key)
* Re-enabled VERM macros.
* !?GM0 added
* !?TM implemented
* Added !!MF:N
* Started !?OB, !!BM, !!HE, !!OW, !!UN
* Added basic support of w-variables
* Added support for ERM indirect variables
* Made !?FU regular trigger
* !!re (ERA loop receiver) implemented
* Fixed ERM receivers with zero args.
2018-03-17 16:58:30 +02:00
if ( const PlayerState * p = cb - > getPlayerState ( tempOwner , false ) )
2014-06-05 19:52:14 +03:00
{
2017-10-07 16:42:33 +02:00
dwellingBonus = getDwellingBonus ( creatures [ level ] . second , p - > dwellings ) ;
2014-06-05 19:52:14 +03:00
}
if ( dwellingBonus )
ret . entries . push_back ( GrowthInfo : : Entry ( VLC - > generaltexth - > allTexts [ 591 ] , dwellingBonus ) ) ; // \nExternal dwellings %+d
//other *-of-legion-like bonuses (%d to growth cumulative with grail)
2020-11-11 21:43:40 +02:00
TConstBonusListPtr bonuses = getBonuses ( Selector : : type ( ) ( Bonus : : CREATURE_GROWTH ) . And ( Selector : : subtype ( ) ( level ) ) ) ;
2020-10-01 07:55:41 +02:00
for ( const auto & b : * bonuses )
2015-09-04 18:38:42 +02:00
ret . entries . push_back ( GrowthInfo : : Entry ( b - > val , b - > Description ( ) ) ) ;
2014-06-05 19:52:14 +03:00
//statue-of-legion-like bonus: % to base+castle
2020-11-11 21:43:40 +02:00
TConstBonusListPtr bonuses2 = getBonuses ( Selector : : type ( ) ( Bonus : : CREATURE_GROWTH_PERCENT ) ) ;
2020-10-01 07:55:41 +02:00
for ( const auto & b : * bonuses2 )
2015-09-04 18:38:42 +02:00
ret . entries . push_back ( GrowthInfo : : Entry ( b - > val * ( base + castleBonus ) / 100 , b - > Description ( ) ) ) ;
2014-06-05 19:52:14 +03:00
if ( hasBuilt ( BuildingID : : GRAIL ) ) //grail - +50% to ALL (so far added) growth
ret . entries . push_back ( GrowthInfo : : Entry ( subID , BuildingID : : GRAIL , ret . totalGrowth ( ) / 2 ) ) ;
return ret ;
}
2017-10-07 16:42:33 +02:00
int CGTownInstance : : getDwellingBonus ( const std : : vector < CreatureID > & creatureIds , const std : : vector < ConstTransitivePtr < CGDwelling > > & dwellings ) const
{
int totalBonus = 0 ;
for ( const auto & dwelling : dwellings )
{
for ( const auto & creature : dwelling - > creatures )
{
totalBonus + = vstd : : contains ( creatureIds , creature . second [ 0 ] ) ? 1 : 0 ;
}
}
return totalBonus ;
}
2014-06-05 19:52:14 +03:00
TResources CGTownInstance : : dailyIncome ( ) const
{
TResources ret ;
2016-02-15 12:34:37 +02:00
for ( auto & p : town - > buildings )
{
2014-06-05 19:52:14 +03:00
BuildingID buildingUpgrade ;
2016-02-15 12:34:37 +02:00
for ( auto & p2 : town - > buildings )
{
2014-06-05 19:52:14 +03:00
if ( p2 . second - > upgrade = = p . first )
{
buildingUpgrade = p2 . first ;
}
}
if ( ! hasBuilt ( buildingUpgrade ) & & ( hasBuilt ( p . first ) ) )
{
ret + = p . second - > produce ;
}
}
return ret ;
}
2021-03-23 16:47:07 +02:00
2014-06-05 19:52:14 +03:00
bool CGTownInstance : : hasFort ( ) const
{
return hasBuilt ( BuildingID : : FORT ) ;
}
2021-03-23 16:47:07 +02:00
2014-06-05 19:52:14 +03:00
bool CGTownInstance : : hasCapitol ( ) const
{
return hasBuilt ( BuildingID : : CAPITOL ) ;
}
2021-03-23 16:47:07 +02:00
2014-06-05 19:52:14 +03:00
CGTownInstance : : CGTownInstance ( )
2016-11-13 12:38:42 +02:00
: CGDwelling ( ) , IShipyard ( this ) , IMarket ( this ) , town ( nullptr ) , builded ( 0 ) , destroyed ( 0 ) , identifier ( 0 ) , alignment ( 0xff )
2014-06-05 19:52:14 +03:00
{
2021-03-23 16:47:07 +02:00
this - > setNodeType ( CBonusSystemNode : : TOWN ) ;
2014-06-05 19:52:14 +03:00
}
CGTownInstance : : ~ CGTownInstance ( )
{
for ( auto & elem : bonusingBuildings )
delete elem ;
}
int CGTownInstance : : spellsAtLevel ( int level , bool checkGuild ) const
{
if ( checkGuild & & mageGuildLevel ( ) < level )
return 0 ;
int ret = 6 - level ; //how many spells are available at this level
2020-10-02 23:55:46 +02:00
if ( hasBuilt ( BuildingSubID : : LIBRARY ) )
2014-06-05 19:52:14 +03:00
ret + + ;
return ret ;
}
bool CGTownInstance : : needsLastStack ( ) const
{
if ( garrisonHero )
return true ;
else return false ;
}
2021-03-23 16:47:07 +02:00
void CGTownInstance : : setOwner ( const PlayerColor player ) const
{
removeCapitols ( player ) ;
cb - > setOwner ( this , player ) ;
}
2014-06-05 19:52:14 +03:00
void CGTownInstance : : onHeroVisit ( const CGHeroInstance * h ) const
{
2021-03-23 16:47:07 +02:00
if ( ! cb - > gameState ( ) - > getPlayerRelations ( getOwner ( ) , h - > getOwner ( ) ) ) //if this is enemy
2014-06-05 19:52:14 +03:00
{
if ( armedGarrison ( ) | | visitingHero )
{
2021-03-23 16:47:07 +02:00
const CGHeroInstance * defendingHero = visitingHero ? visitingHero : garrisonHero ;
2021-09-12 13:30:54 +02:00
const CArmedInstance * defendingArmy = defendingHero ? ( CArmedInstance * ) defendingHero : this ;
const bool isBattleOutside = isBattleOutsideTown ( defendingHero ) ;
2014-06-05 19:52:14 +03:00
2021-03-23 16:47:07 +02:00
if ( ! isBattleOutside & & visitingHero & & defendingHero = = visitingHero )
{
//we have two approaches to merge armies: mergeGarrisonOnSiege() and used in the CGameHandler::garrisonSwap(ObjectInstanceID tid)
auto nodeSiege = defendingHero - > whereShouldBeAttachedOnSiege ( isBattleOutside ) ;
2014-06-05 19:52:14 +03:00
2021-03-23 16:47:07 +02:00
if ( nodeSiege = = ( CBonusSystemNode * ) this )
2021-09-12 13:30:54 +02:00
cb - > swapGarrisonOnSiege ( this - > id ) ;
2014-06-05 19:52:14 +03:00
2021-03-23 16:47:07 +02:00
const_cast < CGHeroInstance * > ( defendingHero ) - > inTownGarrison = false ; //hack to return visitor from garrison after battle
2016-02-21 21:23:47 +02:00
}
2021-03-23 16:47:07 +02:00
cb - > startBattlePrimary ( h , defendingArmy , getSightCenter ( ) , h , defendingHero , false , ( isBattleOutside ? nullptr : this ) ) ;
2014-06-05 19:52:14 +03:00
}
else
{
2021-03-23 16:47:07 +02:00
auto heroColor = h - > getOwner ( ) ;
2021-10-29 10:45:10 +02:00
onTownCaptured ( heroColor ) ;
2021-03-23 16:47:07 +02:00
if ( cb - > gameState ( ) - > getPlayerStatus ( heroColor ) = = EPlayerStatus : : WINNER )
2019-04-23 20:04:07 +02:00
{
return ; //we just won game, we do not need to perform any extra actions
//TODO: check how does H3 behave, visiting town on victory can affect campaigns (spells learned, +1 stat building visited)
}
2014-06-05 19:52:14 +03:00
cb - > heroVisitCastle ( this , h ) ;
}
}
else if ( h - > visitablePos ( ) = = visitablePos ( ) )
{
2016-10-29 17:00:42 +02:00
bool commander_recover = h - > commander & & ! h - > commander - > alive ;
if ( commander_recover ) // rise commander from dead
2014-06-05 19:52:14 +03:00
{
SetCommanderProperty scp ;
scp . heroid = h - > id ;
scp . which = SetCommanderProperty : : ALIVE ;
scp . amount = 1 ;
2016-10-29 17:00:42 +02:00
cb - > sendAndApply ( & scp ) ;
2014-06-05 19:52:14 +03:00
}
cb - > heroVisitCastle ( this , h ) ;
2016-10-29 17:00:42 +02:00
// TODO(vmarkovtsev): implement payment for rising the commander
if ( commander_recover ) // info window about commander
{
InfoWindow iw ;
iw . player = h - > tempOwner ;
iw . text < < h - > commander - > getName ( ) ;
iw . components . push_back ( Component ( * h - > commander ) ) ;
cb - > showInfoDialog ( & iw ) ;
}
2014-06-05 19:52:14 +03:00
}
else
{
2017-08-11 19:03:05 +02:00
logGlobal - > error ( " %s visits allied town of %s from different pos? " , h - > name , name ) ;
2014-06-05 19:52:14 +03:00
}
}
void CGTownInstance : : onHeroLeave ( const CGHeroInstance * h ) const
{
2014-07-01 13:07:53 +03:00
//FIXME: find out why this issue appears on random maps
2017-08-11 19:03:05 +02:00
if ( visitingHero = = h )
2014-07-01 13:07:53 +03:00
{
cb - > stopHeroVisitCastle ( this , h ) ;
2017-08-11 19:03:05 +02:00
logGlobal - > trace ( " %s correctly left town %s " , h - > name , name ) ;
2014-07-01 13:07:53 +03:00
}
else
2017-08-11 19:03:05 +02:00
logGlobal - > warn ( " Warning, %s tries to leave the town %s but hero is not inside. " , h - > name , name ) ;
2014-06-05 19:52:14 +03:00
}
2014-06-24 20:39:36 +03:00
std : : string CGTownInstance : : getObjectName ( ) const
{
return name + " , " + town - > faction - > name ;
}
2020-10-15 14:03:01 +02:00
bool CGTownInstance : : townEnvisagesBuilding ( BuildingSubID : : EBuildingSubID subId ) const
2020-10-02 23:55:46 +02:00
{
2020-10-15 14:03:01 +02:00
return town - > getBuildingType ( subId ) ! = BuildingID : : NONE ;
}
2021-01-14 00:02:13 +02:00
void CGTownInstance : : initOverriddenBids ( )
{
for ( const auto & bid : builtBuildings )
{
auto & overrideThem = town - > buildings . at ( bid ) - > overrideBids ;
for ( auto & overrideIt : overrideThem )
overriddenBuildings . insert ( overrideIt ) ;
}
}
2021-09-12 13:30:54 +02:00
bool CGTownInstance : : isBonusingBuildingAdded ( BuildingID : : EBuildingID bid ) const
{
auto present = std : : find_if ( bonusingBuildings . begin ( ) , bonusingBuildings . end ( ) , [ & ] ( CGTownBuilding * building )
{
return building - > getBuildingType ( ) . num = = bid ;
} ) ;
return present ! = bonusingBuildings . end ( ) ;
}
//it does not check hasBuilt because this check is in the OnHeroVisit handler
void CGTownInstance : : tryAddOnePerWeekBonus ( BuildingSubID : : EBuildingSubID subID )
{
auto bid = town - > getBuildingType ( subID ) ;
if ( bid ! = BuildingID : : NONE & & ! isBonusingBuildingAdded ( bid ) )
bonusingBuildings . push_back ( new COPWBonus ( bid , subID , this ) ) ;
}
2020-10-19 21:38:06 +02:00
void CGTownInstance : : tryAddVisitingBonus ( BuildingSubID : : EBuildingSubID subID )
{
auto bid = town - > getBuildingType ( subID ) ;
2021-09-12 13:30:54 +02:00
if ( bid ! = BuildingID : : NONE & & ! isBonusingBuildingAdded ( bid ) )
2020-10-19 21:38:06 +02:00
bonusingBuildings . push_back ( new CTownBonus ( bid , subID , this ) ) ;
}
void CGTownInstance : : addTownBonuses ( )
{
for ( const auto & kvp : town - > buildings )
{
2021-01-14 00:02:13 +02:00
if ( vstd : : contains ( overriddenBuildings , kvp . first ) )
continue ;
2020-10-19 21:38:06 +02:00
if ( kvp . second - > IsVisitingBonus ( ) )
bonusingBuildings . push_back ( new CTownBonus ( kvp . second - > bid , kvp . second - > subId , this ) ) ;
if ( kvp . second - > IsWeekBonus ( ) )
bonusingBuildings . push_back ( new COPWBonus ( kvp . second - > bid , kvp . second - > subId , this ) ) ;
}
2020-10-02 23:55:46 +02:00
}
2021-01-14 00:02:13 +02:00
void CGTownInstance : : deleteTownBonus ( BuildingID : : EBuildingID bid )
{
size_t i = 0 ;
CGTownBuilding * freeIt = nullptr ;
for ( i = 0 ; i ! = bonusingBuildings . size ( ) ; i + + )
{
if ( bonusingBuildings [ i ] - > getBuildingType ( ) = = bid )
{
freeIt = bonusingBuildings [ i ] ;
break ;
}
}
if ( freeIt = = nullptr )
return ;
auto building = town - > buildings . at ( bid ) ;
auto isVisitingBonus = building - > IsVisitingBonus ( ) ;
auto isWeekBonus = building - > IsWeekBonus ( ) ;
if ( ! isVisitingBonus & & ! isWeekBonus )
return ;
bonusingBuildings . erase ( bonusingBuildings . begin ( ) + i ) ;
if ( isVisitingBonus )
delete ( CTownBonus * ) freeIt ;
else if ( isWeekBonus )
delete ( COPWBonus * ) freeIt ;
}
2020-10-19 21:38:06 +02:00
2020-10-15 14:03:01 +02:00
void CGTownInstance : : initObj ( CRandomGenerator & rand ) ///initialize town structures
2014-06-05 19:52:14 +03:00
{
blockVisit = true ;
2020-10-15 14:03:01 +02:00
if ( townEnvisagesBuilding ( BuildingSubID : : PORTAL_OF_SUMMONING ) ) //Dungeon for example
creatures . resize ( GameConstants : : CREATURES_PER_TOWN + 1 ) ;
2014-06-05 19:52:14 +03:00
else
creatures . resize ( GameConstants : : CREATURES_PER_TOWN ) ;
2020-10-02 23:55:46 +02:00
2014-06-05 19:52:14 +03:00
for ( int level = 0 ; level < GameConstants : : CREATURES_PER_TOWN ; level + + )
{
BuildingID buildID = BuildingID ( BuildingID : : DWELL_FIRST ) . advance ( level ) ;
int upgradeNum = 0 ;
for ( ; town - > buildings . count ( buildID ) ; upgradeNum + + , buildID . advance ( GameConstants : : CREATURES_PER_TOWN ) )
{
if ( hasBuilt ( buildID ) & & town - > creatures . at ( level ) . size ( ) > upgradeNum )
creatures [ level ] . second . push_back ( town - > creatures [ level ] [ upgradeNum ] ) ;
}
}
2021-01-14 00:02:13 +02:00
initOverriddenBids ( ) ;
2020-10-19 21:38:06 +02:00
addTownBonuses ( ) ; //add special bonuses from buildings to the bonusingBuildings vector.
2014-06-05 19:52:14 +03:00
recreateBuildingsBonuses ( ) ;
2014-06-16 19:27:26 +03:00
updateAppearance ( ) ;
2014-06-05 19:52:14 +03:00
}
2020-10-07 14:12:32 +02:00
bool CGTownInstance : : hasBuiltInOldWay ( ETownType : : ETownType type , BuildingID bid ) const
{
return ( this - > town - > faction ! = nullptr & & this - > town - > faction - > index = = type & & hasBuilt ( bid ) ) ;
}
2016-09-09 19:30:36 +02:00
void CGTownInstance : : newTurn ( CRandomGenerator & rand ) const
2014-06-05 19:52:14 +03:00
{
if ( cb - > getDate ( Date : : DAY_OF_WEEK ) = = 1 ) //reset on new week
{
2020-10-02 23:55:46 +02:00
//give resources if there's a Mystic Pond
if ( hasBuilt ( BuildingSubID : : MYSTIC_POND )
2021-02-15 14:03:32 +02:00
& & cb - > getDate ( Date : : DAY ) ! = 1
2020-10-02 23:55:46 +02:00
& & ( tempOwner < PlayerColor : : PLAYER_LIMIT )
)
2014-06-05 19:52:14 +03:00
{
int resID = rand . nextInt ( 2 , 5 ) ; //bonus to random rare resource
resID = ( resID = = 2 ) ? 1 : resID ;
int resVal = rand . nextInt ( 1 , 4 ) ; //with size 1..4
cb - > giveResource ( tempOwner , static_cast < Res : : ERes > ( resID ) , resVal ) ;
cb - > setObjProperty ( id , ObjProperty : : BONUS_VALUE_FIRST , resID ) ;
cb - > setObjProperty ( id , ObjProperty : : BONUS_VALUE_SECOND , resVal ) ;
}
2020-10-02 23:55:46 +02:00
auto manaVortex = getBonusingBuilding ( BuildingSubID : : MANA_VORTEX ) ;
if ( manaVortex ! = nullptr )
cb - > setObjProperty ( id , ObjProperty : : STRUCTURE_CLEAR_VISITORS , manaVortex - > indexOnTV ) ; //reset visitors for Mana Vortex
2014-06-05 19:52:14 +03:00
2020-09-28 00:32:33 +02:00
//get Mana Vortex or Stables bonuses
//same code is in the CGameHandler::buildStructure method
if ( visitingHero ! = nullptr )
cb - > visitCastleObjects ( this , visitingHero ) ;
if ( garrisonHero ! = nullptr )
cb - > visitCastleObjects ( this , garrisonHero ) ;
2014-06-05 19:52:14 +03:00
if ( tempOwner = = PlayerColor : : NEUTRAL ) //garrison growth for neutral towns
{
std : : vector < SlotID > nativeCrits ; //slots
for ( auto & elem : Slots ( ) )
{
if ( elem . second - > type - > faction = = subID ) //native
{
nativeCrits . push_back ( elem . first ) ; //collect matching slots
}
}
if ( nativeCrits . size ( ) )
{
SlotID pos = * RandomGeneratorUtil : : nextItem ( nativeCrits , rand ) ;
StackLocation sl ( this , pos ) ;
const CCreature * c = getCreature ( pos ) ;
if ( rand . nextInt ( 99 ) < 90 | | c - > upgrades . empty ( ) ) //increase number if no upgrade available
{
cb - > changeStackCount ( sl , c - > growth ) ;
}
else //upgrade
{
Entities redesign and a few ERM features
* Made most Handlers derived from CHandlerBase and moved service API there.
* Declared existing Entity APIs.
* Added basic script context caching
* Started Lua script module
* Started Lua spell effect API
* Started script state persistence
* Started battle info callback binding
* CommitPackage removed
* Extracted spells::Caster to own header; Expanded Spell API.
* implemented !!MC:S, !!FU:E, !!FU:P, !!MA, !!VR:H, !!VR:C
* !!BU:C, !!BU:E, !!BU:G, !!BU:M implemented
* Allow use of "MC:S@varName@" to declare normal variable (technically v-variable with string key)
* Re-enabled VERM macros.
* !?GM0 added
* !?TM implemented
* Added !!MF:N
* Started !?OB, !!BM, !!HE, !!OW, !!UN
* Added basic support of w-variables
* Added support for ERM indirect variables
* Made !?FU regular trigger
* !!re (ERA loop receiver) implemented
* Fixed ERM receivers with zero args.
2018-03-17 16:58:30 +02:00
cb - > changeStackType ( sl , VLC - > creh - > objects [ * c - > upgrades . begin ( ) ] ) ;
2014-06-05 19:52:14 +03:00
}
}
if ( ( stacksCount ( ) < GameConstants : : ARMY_SIZE & & rand . nextInt ( 99 ) < 25 ) | | Slots ( ) . empty ( ) ) //add new stack
{
int i = rand . nextInt ( std : : min ( GameConstants : : CREATURES_PER_TOWN , cb - > getDate ( Date : : MONTH ) < < 1 ) - 1 ) ;
if ( ! town - > creatures [ i ] . empty ( ) )
{
CreatureID c = town - > creatures [ i ] [ 0 ] ;
SlotID n ;
TQuantity count = creatureGrowth ( i ) ;
if ( ! count ) // no dwelling
Entities redesign and a few ERM features
* Made most Handlers derived from CHandlerBase and moved service API there.
* Declared existing Entity APIs.
* Added basic script context caching
* Started Lua script module
* Started Lua spell effect API
* Started script state persistence
* Started battle info callback binding
* CommitPackage removed
* Extracted spells::Caster to own header; Expanded Spell API.
* implemented !!MC:S, !!FU:E, !!FU:P, !!MA, !!VR:H, !!VR:C
* !!BU:C, !!BU:E, !!BU:G, !!BU:M implemented
* Allow use of "MC:S@varName@" to declare normal variable (technically v-variable with string key)
* Re-enabled VERM macros.
* !?GM0 added
* !?TM implemented
* Added !!MF:N
* Started !?OB, !!BM, !!HE, !!OW, !!UN
* Added basic support of w-variables
* Added support for ERM indirect variables
* Made !?FU regular trigger
* !!re (ERA loop receiver) implemented
* Fixed ERM receivers with zero args.
2018-03-17 16:58:30 +02:00
count = VLC - > creh - > objects [ c ] - > growth ;
2014-06-05 19:52:14 +03:00
{ //no lower tiers or above current month
if ( ( n = getSlotFor ( c ) ) . validSlot ( ) )
{
StackLocation sl ( this , n ) ;
if ( slotEmpty ( n ) )
Entities redesign and a few ERM features
* Made most Handlers derived from CHandlerBase and moved service API there.
* Declared existing Entity APIs.
* Added basic script context caching
* Started Lua script module
* Started Lua spell effect API
* Started script state persistence
* Started battle info callback binding
* CommitPackage removed
* Extracted spells::Caster to own header; Expanded Spell API.
* implemented !!MC:S, !!FU:E, !!FU:P, !!MA, !!VR:H, !!VR:C
* !!BU:C, !!BU:E, !!BU:G, !!BU:M implemented
* Allow use of "MC:S@varName@" to declare normal variable (technically v-variable with string key)
* Re-enabled VERM macros.
* !?GM0 added
* !?TM implemented
* Added !!MF:N
* Started !?OB, !!BM, !!HE, !!OW, !!UN
* Added basic support of w-variables
* Added support for ERM indirect variables
* Made !?FU regular trigger
* !!re (ERA loop receiver) implemented
* Fixed ERM receivers with zero args.
2018-03-17 16:58:30 +02:00
cb - > insertNewStack ( sl , VLC - > creh - > objects [ c ] , count ) ;
2014-06-05 19:52:14 +03:00
else //add to existing
cb - > changeStackCount ( sl , count ) ;
}
}
}
}
}
}
}
2014-06-24 02:26:36 +03:00
/*
2014-06-05 19:52:14 +03:00
int3 CGTownInstance : : getSightCenter ( ) const
{
return pos - int3 ( 2 , 0 , 0 ) ;
}
2014-06-24 02:26:36 +03:00
*/
bool CGTownInstance : : passableFor ( PlayerColor color ) const
2014-06-05 19:52:14 +03:00
{
if ( ! armedGarrison ( ) ) //empty castle - anyone can visit
2014-06-24 02:26:36 +03:00
return true ;
2014-06-05 19:52:14 +03:00
if ( tempOwner = = PlayerColor : : NEUTRAL ) //neutral guarded - no one can visit
2014-06-24 02:26:36 +03:00
return false ;
2014-06-05 19:52:14 +03:00
2019-03-20 21:58:15 +02:00
return cb - > getPlayerRelations ( tempOwner , color ) ! = PlayerRelations : : ENEMIES ;
2014-06-05 19:52:14 +03:00
}
void CGTownInstance : : getOutOffsets ( std : : vector < int3 > & offsets ) const
{
2014-10-03 23:34:13 +03:00
offsets = { int3 ( - 1 , 2 , 0 ) , int3 ( - 3 , 2 , 0 ) } ;
2014-06-05 19:52:14 +03:00
}
2016-02-21 21:23:47 +02:00
void CGTownInstance : : mergeGarrisonOnSiege ( ) const
{
2020-10-02 23:55:46 +02:00
auto getWeakestStackSlot = [ & ] ( ui64 powerLimit )
2016-02-21 21:23:47 +02:00
{
std : : vector < SlotID > weakSlots ;
auto stacksList = visitingHero - > stacks ;
std : : pair < SlotID , CStackInstance * > pair ;
while ( stacksList . size ( ) )
{
pair = * vstd : : minElementByFun ( stacksList , [ & ] ( std : : pair < SlotID , CStackInstance * > elem )
{
return elem . second - > getPower ( ) ;
} ) ;
if ( powerLimit > pair . second - > getPower ( ) & &
( weakSlots . empty ( ) | | pair . second - > getPower ( ) = = visitingHero - > getStack ( weakSlots . front ( ) ) . getPower ( ) ) )
{
weakSlots . push_back ( pair . first ) ;
stacksList . erase ( pair . first ) ;
}
else
break ;
}
if ( weakSlots . size ( ) )
return * std : : max_element ( weakSlots . begin ( ) , weakSlots . end ( ) ) ;
return SlotID ( ) ;
} ;
2020-10-02 23:55:46 +02:00
auto count = static_cast < int > ( stacks . size ( ) ) ;
2016-02-21 21:23:47 +02:00
for ( int i = 0 ; i < count ; i + + )
{
auto pair = * vstd : : maxElementByFun ( stacks , [ & ] ( std : : pair < SlotID , CStackInstance * > elem )
{
ui64 power = elem . second - > getPower ( ) ;
auto dst = visitingHero - > getSlotFor ( elem . second - > getCreatureID ( ) ) ;
if ( dst . validSlot ( ) & & visitingHero - > hasStackAtSlot ( dst ) )
power + = visitingHero - > getStack ( dst ) . getPower ( ) ;
return power ;
} ) ;
auto dst = visitingHero - > getSlotFor ( pair . second - > getCreatureID ( ) ) ;
if ( dst . validSlot ( ) )
cb - > moveStack ( StackLocation ( this , pair . first ) , StackLocation ( visitingHero , dst ) , - 1 ) ;
else
{
2020-10-01 10:38:06 +02:00
dst = getWeakestStackSlot ( static_cast < int > ( pair . second - > getPower ( ) ) ) ;
2016-02-21 21:23:47 +02:00
if ( dst . validSlot ( ) )
cb - > swapStacks ( StackLocation ( this , pair . first ) , StackLocation ( visitingHero , dst ) ) ;
}
}
}
2014-06-05 19:52:14 +03:00
void CGTownInstance : : removeCapitols ( PlayerColor owner ) const
{
if ( hasCapitol ( ) ) // search if there's an older capitol
{
Entities redesign and a few ERM features
* Made most Handlers derived from CHandlerBase and moved service API there.
* Declared existing Entity APIs.
* Added basic script context caching
* Started Lua script module
* Started Lua spell effect API
* Started script state persistence
* Started battle info callback binding
* CommitPackage removed
* Extracted spells::Caster to own header; Expanded Spell API.
* implemented !!MC:S, !!FU:E, !!FU:P, !!MA, !!VR:H, !!VR:C
* !!BU:C, !!BU:E, !!BU:G, !!BU:M implemented
* Allow use of "MC:S@varName@" to declare normal variable (technically v-variable with string key)
* Re-enabled VERM macros.
* !?GM0 added
* !?TM implemented
* Added !!MF:N
* Started !?OB, !!BM, !!HE, !!OW, !!UN
* Added basic support of w-variables
* Added support for ERM indirect variables
* Made !?FU regular trigger
* !!re (ERA loop receiver) implemented
* Fixed ERM receivers with zero args.
2018-03-17 16:58:30 +02:00
PlayerState * state = cb - > gameState ( ) - > getPlayerState ( owner ) ; //get all towns owned by player
2014-06-05 19:52:14 +03:00
for ( auto i = state - > towns . cbegin ( ) ; i < state - > towns . cend ( ) ; + + i )
{
if ( * i ! = this & & ( * i ) - > hasCapitol ( ) )
{
RazeStructures rs ;
rs . tid = id ;
rs . bid . insert ( BuildingID : : CAPITOL ) ;
rs . destroyed = destroyed ;
cb - > sendAndApply ( & rs ) ;
return ;
}
}
}
}
2016-02-22 02:33:15 +02:00
void CGTownInstance : : clearArmy ( ) const
{
while ( ! stacks . empty ( ) )
{
cb - > eraseStack ( StackLocation ( this , stacks . begin ( ) - > first ) ) ;
}
}
2014-06-05 19:52:14 +03:00
int CGTownInstance : : getBoatType ( ) const
{
switch ( town - > faction - > alignment )
{
case EAlignment : : EVIL : return 0 ;
case EAlignment : : GOOD : return 1 ;
case EAlignment : : NEUTRAL : return 2 ;
}
assert ( 0 ) ;
return - 1 ;
}
int CGTownInstance : : getMarketEfficiency ( ) const
{
2020-10-15 14:03:01 +02:00
if ( ! hasBuiltSomeTradeBuilding ( ) )
2014-06-05 19:52:14 +03:00
return 0 ;
Entities redesign and a few ERM features
* Made most Handlers derived from CHandlerBase and moved service API there.
* Declared existing Entity APIs.
* Added basic script context caching
* Started Lua script module
* Started Lua spell effect API
* Started script state persistence
* Started battle info callback binding
* CommitPackage removed
* Extracted spells::Caster to own header; Expanded Spell API.
* implemented !!MC:S, !!FU:E, !!FU:P, !!MA, !!VR:H, !!VR:C
* !!BU:C, !!BU:E, !!BU:G, !!BU:M implemented
* Allow use of "MC:S@varName@" to declare normal variable (technically v-variable with string key)
* Re-enabled VERM macros.
* !?GM0 added
* !?TM implemented
* Added !!MF:N
* Started !?OB, !!BM, !!HE, !!OW, !!UN
* Added basic support of w-variables
* Added support for ERM indirect variables
* Made !?FU regular trigger
* !!re (ERA loop receiver) implemented
* Fixed ERM receivers with zero args.
2018-03-17 16:58:30 +02:00
const PlayerState * p = cb - > getPlayerState ( tempOwner ) ;
2014-06-05 19:52:14 +03:00
assert ( p ) ;
int marketCount = 0 ;
for ( const CGTownInstance * t : p - > towns )
2020-10-15 14:03:01 +02:00
if ( t - > hasBuiltSomeTradeBuilding ( ) )
2014-06-05 19:52:14 +03:00
marketCount + + ;
return marketCount ;
}
bool CGTownInstance : : allowsTrade ( EMarketMode : : EMarketMode mode ) const
{
switch ( mode )
{
case EMarketMode : : RESOURCE_RESOURCE :
case EMarketMode : : RESOURCE_PLAYER :
return hasBuilt ( BuildingID : : MARKETPLACE ) ;
case EMarketMode : : ARTIFACT_RESOURCE :
case EMarketMode : : RESOURCE_ARTIFACT :
2020-10-15 14:03:01 +02:00
return hasBuilt ( BuildingSubID : : ARTIFACT_MERCHANT ) ;
2014-06-05 19:52:14 +03:00
case EMarketMode : : CREATURE_RESOURCE :
2020-10-15 14:03:01 +02:00
return hasBuilt ( BuildingSubID : : FREELANCERS_GUILD ) ;
2014-06-05 19:52:14 +03:00
case EMarketMode : : CREATURE_UNDEAD :
2020-10-15 14:03:01 +02:00
return hasBuilt ( BuildingSubID : : CREATURE_TRANSFORMER ) ;
2014-06-05 19:52:14 +03:00
case EMarketMode : : RESOURCE_SKILL :
2020-10-15 14:03:01 +02:00
return hasBuilt ( BuildingSubID : : MAGIC_UNIVERSITY ) ;
2014-06-05 19:52:14 +03:00
default :
assert ( 0 ) ;
return false ;
}
}
std : : vector < int > CGTownInstance : : availableItemsIds ( EMarketMode : : EMarketMode mode ) const
{
if ( mode = = EMarketMode : : RESOURCE_ARTIFACT )
{
std : : vector < int > ret ;
for ( const CArtifact * a : merchantArtifacts )
if ( a )
ret . push_back ( a - > id ) ;
else
ret . push_back ( - 1 ) ;
return ret ;
}
else if ( mode = = EMarketMode : : RESOURCE_SKILL )
{
return universitySkills ;
}
else
return IMarket : : availableItemsIds ( mode ) ;
}
void CGTownInstance : : setType ( si32 ID , si32 subID )
{
assert ( ID = = Obj : : TOWN ) ; // just in case
CGObjectInstance : : setType ( ID , subID ) ;
Entities redesign and a few ERM features
* Made most Handlers derived from CHandlerBase and moved service API there.
* Declared existing Entity APIs.
* Added basic script context caching
* Started Lua script module
* Started Lua spell effect API
* Started script state persistence
* Started battle info callback binding
* CommitPackage removed
* Extracted spells::Caster to own header; Expanded Spell API.
* implemented !!MC:S, !!FU:E, !!FU:P, !!MA, !!VR:H, !!VR:C
* !!BU:C, !!BU:E, !!BU:G, !!BU:M implemented
* Allow use of "MC:S@varName@" to declare normal variable (technically v-variable with string key)
* Re-enabled VERM macros.
* !?GM0 added
* !?TM implemented
* Added !!MF:N
* Started !?OB, !!BM, !!HE, !!OW, !!UN
* Added basic support of w-variables
* Added support for ERM indirect variables
* Made !?FU regular trigger
* !!re (ERA loop receiver) implemented
* Fixed ERM receivers with zero args.
2018-03-17 16:58:30 +02:00
town = ( * VLC - > townh ) [ subID ] - > town ;
2014-06-05 19:52:14 +03:00
randomizeArmy ( subID ) ;
updateAppearance ( ) ;
}
void CGTownInstance : : updateAppearance ( )
{
2022-09-21 11:34:23 +02:00
auto terrain = cb - > gameState ( ) - > getTile ( visitablePos ( ) ) - > terType - > id ;
2014-06-15 19:43:01 +03:00
//FIXME: not the best way to do this
2022-09-21 11:34:23 +02:00
auto app = VLC - > objtypeh - > getHandlerFor ( ID , subID ) - > getOverride ( terrain , this ) ;
2014-06-15 19:43:01 +03:00
if ( app )
2022-09-11 15:12:35 +02:00
appearance = app ;
2014-06-05 19:52:14 +03:00
}
std : : string CGTownInstance : : nodeName ( ) const
{
return " Town ( " + ( town ? town - > faction - > name : " unknown " ) + " ) of " + name ;
}
void CGTownInstance : : deserializationFix ( )
{
2022-11-06 01:26:13 +02:00
attachTo ( townAndVis ) ;
2014-06-05 19:52:14 +03:00
//Hero is already handled by CGameState::attachArmedObjects
// if(visitingHero)
// visitingHero->attachTo(&townAndVis);
// if(garrisonHero)
// garrisonHero->attachTo(this);
}
void CGTownInstance : : updateMoraleBonusFromArmy ( )
{
2020-11-11 21:43:40 +02:00
auto b = getExportedBonusList ( ) . getFirst ( Selector : : sourceType ( ) ( Bonus : : ARMY ) . And ( Selector : : type ( ) ( Bonus : : MORALE ) ) ) ;
2014-06-05 19:52:14 +03:00
if ( ! b )
{
2016-09-19 23:36:35 +02:00
b = std : : make_shared < Bonus > ( Bonus : : PERMANENT , Bonus : : MORALE , Bonus : : ARMY , 0 , - 1 ) ;
2014-06-05 19:52:14 +03:00
addNewBonus ( b ) ;
}
if ( garrisonHero )
2015-12-11 15:13:18 +02:00
{
2014-06-05 19:52:14 +03:00
b - > val = 0 ;
2015-12-11 15:13:18 +02:00
CBonusSystemNode : : treeHasChanged ( ) ;
}
2014-06-05 19:52:14 +03:00
else
CArmedInstance : : updateMoraleBonusFromArmy ( ) ;
}
void CGTownInstance : : recreateBuildingsBonuses ( )
{
BonusList bl ;
2020-11-11 21:43:40 +02:00
getExportedBonusList ( ) . getBonuses ( bl , Selector : : sourceType ( ) ( Bonus : : TOWN_STRUCTURE ) ) ;
2021-01-14 00:02:13 +02:00
2016-09-19 23:36:35 +02:00
for ( auto b : bl )
2014-06-05 19:52:14 +03:00
removeBonus ( b ) ;
2022-05-19 14:14:50 +02:00
for ( const auto & bid : builtBuildings )
2014-06-05 19:52:14 +03:00
{
2021-01-14 00:02:13 +02:00
if ( vstd : : contains ( overriddenBuildings , bid ) ) //tricky! -> checks tavern only if no bratherhood of sword
continue ;
2020-12-10 03:05:37 +02:00
2021-01-14 00:02:13 +02:00
auto building = town - > buildings . at ( bid ) ;
2020-10-02 23:55:46 +02:00
2021-01-14 00:02:13 +02:00
if ( building - > buildingBonuses . empty ( ) )
continue ;
2020-10-02 23:55:46 +02:00
2021-03-23 16:47:07 +02:00
for ( auto & bonus : building - > buildingBonuses )
2021-02-20 03:57:50 +02:00
{
2021-03-23 16:47:07 +02:00
if ( bonus - > limiter & & bonus - > effectRange = = Bonus : : ONLY_ENEMY_ARMY ) //ONLY_ENEMY_ARMY is only mark for OppositeSide limiter to avoid extra dynamic_cast.
{
auto bCopy = std : : make_shared < Bonus > ( * bonus ) ; //just a copy of the shared_ptr has been changed and reassigned.
bCopy - > limiter = std : : make_shared < OppositeSideLimiter > ( this - > getOwner ( ) ) ;
addNewBonus ( bCopy ) ;
2021-02-03 19:19:56 +02:00
continue ;
2021-03-23 16:47:07 +02:00
}
2021-01-14 00:02:13 +02:00
if ( bonus - > propagator ! = nullptr & & bonus - > propagator - > getPropagatorType ( ) = = ALL_CREATURES )
VLC - > creh - > addBonusForAllCreatures ( bonus ) ;
else
addNewBonus ( bonus ) ;
2021-02-20 03:57:50 +02:00
}
2014-06-05 19:52:14 +03:00
}
}
void CGTownInstance : : setVisitingHero ( CGHeroInstance * h )
{
2014-09-21 21:57:42 +03:00
assert ( ! ! visitingHero = = ! h ) ;
2014-06-05 19:52:14 +03:00
if ( h )
{
Entities redesign and a few ERM features
* Made most Handlers derived from CHandlerBase and moved service API there.
* Declared existing Entity APIs.
* Added basic script context caching
* Started Lua script module
* Started Lua spell effect API
* Started script state persistence
* Started battle info callback binding
* CommitPackage removed
* Extracted spells::Caster to own header; Expanded Spell API.
* implemented !!MC:S, !!FU:E, !!FU:P, !!MA, !!VR:H, !!VR:C
* !!BU:C, !!BU:E, !!BU:G, !!BU:M implemented
* Allow use of "MC:S@varName@" to declare normal variable (technically v-variable with string key)
* Re-enabled VERM macros.
* !?GM0 added
* !?TM implemented
* Added !!MF:N
* Started !?OB, !!BM, !!HE, !!OW, !!UN
* Added basic support of w-variables
* Added support for ERM indirect variables
* Made !?FU regular trigger
* !!re (ERA loop receiver) implemented
* Fixed ERM receivers with zero args.
2018-03-17 16:58:30 +02:00
PlayerState * p = cb - > gameState ( ) - > getPlayerState ( h - > tempOwner ) ;
2014-06-05 19:52:14 +03:00
assert ( p ) ;
2022-11-06 01:26:13 +02:00
h - > detachFrom ( * p ) ;
h - > attachTo ( townAndVis ) ;
2014-06-05 19:52:14 +03:00
visitingHero = h ;
h - > visitedTown = this ;
h - > inTownGarrison = false ;
}
else
{
Entities redesign and a few ERM features
* Made most Handlers derived from CHandlerBase and moved service API there.
* Declared existing Entity APIs.
* Added basic script context caching
* Started Lua script module
* Started Lua spell effect API
* Started script state persistence
* Started battle info callback binding
* CommitPackage removed
* Extracted spells::Caster to own header; Expanded Spell API.
* implemented !!MC:S, !!FU:E, !!FU:P, !!MA, !!VR:H, !!VR:C
* !!BU:C, !!BU:E, !!BU:G, !!BU:M implemented
* Allow use of "MC:S@varName@" to declare normal variable (technically v-variable with string key)
* Re-enabled VERM macros.
* !?GM0 added
* !?TM implemented
* Added !!MF:N
* Started !?OB, !!BM, !!HE, !!OW, !!UN
* Added basic support of w-variables
* Added support for ERM indirect variables
* Made !?FU regular trigger
* !!re (ERA loop receiver) implemented
* Fixed ERM receivers with zero args.
2018-03-17 16:58:30 +02:00
PlayerState * p = cb - > gameState ( ) - > getPlayerState ( visitingHero - > tempOwner ) ;
2014-06-05 19:52:14 +03:00
visitingHero - > visitedTown = nullptr ;
2022-11-06 01:26:13 +02:00
visitingHero - > detachFrom ( townAndVis ) ;
visitingHero - > attachTo ( * p ) ;
2014-06-05 19:52:14 +03:00
visitingHero = nullptr ;
}
}
void CGTownInstance : : setGarrisonedHero ( CGHeroInstance * h )
{
assert ( ! ! garrisonHero = = ! h ) ;
if ( h )
{
Entities redesign and a few ERM features
* Made most Handlers derived from CHandlerBase and moved service API there.
* Declared existing Entity APIs.
* Added basic script context caching
* Started Lua script module
* Started Lua spell effect API
* Started script state persistence
* Started battle info callback binding
* CommitPackage removed
* Extracted spells::Caster to own header; Expanded Spell API.
* implemented !!MC:S, !!FU:E, !!FU:P, !!MA, !!VR:H, !!VR:C
* !!BU:C, !!BU:E, !!BU:G, !!BU:M implemented
* Allow use of "MC:S@varName@" to declare normal variable (technically v-variable with string key)
* Re-enabled VERM macros.
* !?GM0 added
* !?TM implemented
* Added !!MF:N
* Started !?OB, !!BM, !!HE, !!OW, !!UN
* Added basic support of w-variables
* Added support for ERM indirect variables
* Made !?FU regular trigger
* !!re (ERA loop receiver) implemented
* Fixed ERM receivers with zero args.
2018-03-17 16:58:30 +02:00
PlayerState * p = cb - > gameState ( ) - > getPlayerState ( h - > tempOwner ) ;
2014-06-05 19:52:14 +03:00
assert ( p ) ;
2022-11-06 01:26:13 +02:00
h - > detachFrom ( * p ) ;
h - > attachTo ( * this ) ;
2014-06-05 19:52:14 +03:00
garrisonHero = h ;
h - > visitedTown = this ;
h - > inTownGarrison = true ;
}
else
{
Entities redesign and a few ERM features
* Made most Handlers derived from CHandlerBase and moved service API there.
* Declared existing Entity APIs.
* Added basic script context caching
* Started Lua script module
* Started Lua spell effect API
* Started script state persistence
* Started battle info callback binding
* CommitPackage removed
* Extracted spells::Caster to own header; Expanded Spell API.
* implemented !!MC:S, !!FU:E, !!FU:P, !!MA, !!VR:H, !!VR:C
* !!BU:C, !!BU:E, !!BU:G, !!BU:M implemented
* Allow use of "MC:S@varName@" to declare normal variable (technically v-variable with string key)
* Re-enabled VERM macros.
* !?GM0 added
* !?TM implemented
* Added !!MF:N
* Started !?OB, !!BM, !!HE, !!OW, !!UN
* Added basic support of w-variables
* Added support for ERM indirect variables
* Made !?FU regular trigger
* !!re (ERA loop receiver) implemented
* Fixed ERM receivers with zero args.
2018-03-17 16:58:30 +02:00
PlayerState * p = cb - > gameState ( ) - > getPlayerState ( garrisonHero - > tempOwner ) ;
2014-06-05 19:52:14 +03:00
garrisonHero - > visitedTown = nullptr ;
garrisonHero - > inTownGarrison = false ;
2022-11-06 01:26:13 +02:00
garrisonHero - > detachFrom ( * this ) ;
garrisonHero - > attachTo ( * p ) ;
2014-06-05 19:52:14 +03:00
garrisonHero = nullptr ;
}
updateMoraleBonusFromArmy ( ) ; //avoid giving morale bonus for same army twice
}
bool CGTownInstance : : armedGarrison ( ) const
{
2021-10-29 10:45:10 +02:00
return ! stacks . empty ( ) | | garrisonHero ;
2014-06-05 19:52:14 +03:00
}
2016-11-13 12:38:42 +02:00
const CTown * CGTownInstance : : getTown ( ) const
{
if ( ID = = Obj : : RANDOM_TOWN )
return VLC - > townh - > randomTown ;
else
{
if ( nullptr = = town )
{
Entities redesign and a few ERM features
* Made most Handlers derived from CHandlerBase and moved service API there.
* Declared existing Entity APIs.
* Added basic script context caching
* Started Lua script module
* Started Lua spell effect API
* Started script state persistence
* Started battle info callback binding
* CommitPackage removed
* Extracted spells::Caster to own header; Expanded Spell API.
* implemented !!MC:S, !!FU:E, !!FU:P, !!MA, !!VR:H, !!VR:C
* !!BU:C, !!BU:E, !!BU:G, !!BU:M implemented
* Allow use of "MC:S@varName@" to declare normal variable (technically v-variable with string key)
* Re-enabled VERM macros.
* !?GM0 added
* !?TM implemented
* Added !!MF:N
* Started !?OB, !!BM, !!HE, !!OW, !!UN
* Added basic support of w-variables
* Added support for ERM indirect variables
* Made !?FU regular trigger
* !!re (ERA loop receiver) implemented
* Fixed ERM receivers with zero args.
2018-03-17 16:58:30 +02:00
return ( * VLC - > townh ) [ subID ] - > town ;
2016-11-13 12:38:42 +02:00
}
else
return town ;
}
}
2014-06-05 19:52:14 +03:00
int CGTownInstance : : getTownLevel ( ) const
{
// count all buildings that are not upgrades
2020-10-02 23:55:46 +02:00
int level = 0 ;
2021-02-20 03:57:50 +02:00
for ( const auto & bid : builtBuildings )
2014-06-05 19:52:14 +03:00
{
2020-10-02 23:55:46 +02:00
if ( town - > buildings . at ( bid ) - > upgrade = = BuildingID : : NONE )
level + + ;
2021-02-20 03:57:50 +02:00
}
2020-10-02 23:55:46 +02:00
return level ;
2014-06-05 19:52:14 +03:00
}
2022-11-06 01:26:13 +02:00
CBonusSystemNode & CGTownInstance : : whatShouldBeAttached ( )
2014-06-05 19:52:14 +03:00
{
2022-11-06 01:26:13 +02:00
return townAndVis ;
2014-06-05 19:52:14 +03:00
}
const CArmedInstance * CGTownInstance : : getUpperArmy ( ) const
{
if ( garrisonHero )
return garrisonHero ;
return this ;
}
2020-10-02 23:55:46 +02:00
const CGTownBuilding * CGTownInstance : : getBonusingBuilding ( BuildingSubID : : EBuildingSubID subId ) const
{
for ( const auto building : bonusingBuildings )
{
if ( building - > getBuildingSubtype ( ) = = subId )
return building ;
}
return nullptr ;
}
2020-10-15 14:03:01 +02:00
bool CGTownInstance : : hasBuiltSomeTradeBuilding ( ) const
{
2021-03-23 16:47:07 +02:00
for ( const auto & bid : builtBuildings )
2020-10-15 14:03:01 +02:00
{
if ( town - > buildings . at ( bid ) - > IsTradeBuilding ( ) )
return true ;
}
return false ;
}
2020-10-02 23:55:46 +02:00
bool CGTownInstance : : hasBuilt ( BuildingSubID : : EBuildingSubID buildingID ) const
{
for ( const auto & bid : builtBuildings )
{
if ( town - > buildings . at ( bid ) - > subId = = buildingID )
return true ;
}
return false ;
}
bool CGTownInstance : : hasBuilt ( BuildingID buildingID ) const
{
return vstd : : contains ( builtBuildings , buildingID ) ;
}
2014-06-05 19:52:14 +03:00
bool CGTownInstance : : hasBuilt ( BuildingID buildingID , int townID ) const
{
if ( townID = = town - > faction - > index | | townID = = ETownType : : ANY )
return hasBuilt ( buildingID ) ;
return false ;
}
2018-07-26 12:06:55 +02:00
TResources CGTownInstance : : getBuildingCost ( BuildingID buildingID ) const
Entities redesign and a few ERM features
* Made most Handlers derived from CHandlerBase and moved service API there.
* Declared existing Entity APIs.
* Added basic script context caching
* Started Lua script module
* Started Lua spell effect API
* Started script state persistence
* Started battle info callback binding
* CommitPackage removed
* Extracted spells::Caster to own header; Expanded Spell API.
* implemented !!MC:S, !!FU:E, !!FU:P, !!MA, !!VR:H, !!VR:C
* !!BU:C, !!BU:E, !!BU:G, !!BU:M implemented
* Allow use of "MC:S@varName@" to declare normal variable (technically v-variable with string key)
* Re-enabled VERM macros.
* !?GM0 added
* !?TM implemented
* Added !!MF:N
* Started !?OB, !!BM, !!HE, !!OW, !!UN
* Added basic support of w-variables
* Added support for ERM indirect variables
* Made !?FU regular trigger
* !!re (ERA loop receiver) implemented
* Fixed ERM receivers with zero args.
2018-03-17 16:58:30 +02:00
{
2018-07-26 12:06:55 +02:00
if ( vstd : : contains ( town - > buildings , buildingID ) )
return town - > buildings . at ( buildingID ) - > resources ;
else
{
logGlobal - > error ( " Town %s at %s has no possible building %d! " , name , pos . toString ( ) , buildingID . toEnum ( ) ) ;
return TResources ( ) ;
}
}
2016-09-10 19:38:49 +02:00
CBuilding : : TRequired CGTownInstance : : genBuildingRequirements ( BuildingID buildID , bool deep ) const
2014-08-07 19:40:22 +03:00
{
const CBuilding * building = town - > buildings . at ( buildID ) ;
2016-10-15 03:19:47 +02:00
//TODO: find better solution to prevent infinite loops
std : : set < BuildingID > processed ;
2016-10-14 22:46:24 +02:00
2014-08-07 19:40:22 +03:00
std : : function < CBuilding : : TRequired : : Variant ( const BuildingID & ) > dependTest =
[ & ] ( const BuildingID & id ) - > CBuilding : : TRequired : : Variant
{
const CBuilding * build = town - > buildings . at ( id ) ;
2016-09-10 19:38:49 +02:00
CBuilding : : TRequired : : OperatorAll requirements ;
2014-08-07 19:40:22 +03:00
if ( ! hasBuilt ( id ) )
2016-09-10 19:38:49 +02:00
{
2016-10-14 22:46:24 +02:00
if ( deep )
requirements . expressions . push_back ( id ) ;
else
return id ;
2016-09-10 19:38:49 +02:00
}
2014-08-07 19:40:22 +03:00
2016-10-15 03:19:47 +02:00
if ( ! vstd : : contains ( processed , id ) )
{
processed . insert ( id ) ;
if ( build - > upgrade ! = BuildingID : : NONE )
requirements . expressions . push_back ( dependTest ( build - > upgrade ) ) ;
2016-08-30 20:53:56 +02:00
2016-10-15 03:19:47 +02:00
requirements . expressions . push_back ( build - > requirements . morph ( dependTest ) ) ;
}
2016-08-30 20:53:56 +02:00
return requirements ;
2014-08-07 19:40:22 +03:00
} ;
CBuilding : : TRequired : : OperatorAll requirements ;
if ( building - > upgrade ! = BuildingID : : NONE )
{
const CBuilding * upgr = town - > buildings . at ( building - > upgrade ) ;
2016-08-30 20:53:56 +02:00
requirements . expressions . push_back ( dependTest ( upgr - > bid ) ) ;
2016-10-15 03:19:47 +02:00
processed . clear ( ) ;
2014-08-07 19:40:22 +03:00
}
requirements . expressions . push_back ( building - > requirements . morph ( dependTest ) ) ;
CBuilding : : TRequired : : Variant variant ( requirements ) ;
CBuilding : : TRequired ret ( variant ) ;
ret . minimize ( ) ;
return ret ;
}
2020-10-07 14:12:32 +02:00
void CGTownInstance : : addHeroToStructureVisitors ( const CGHeroInstance * h , si64 structureInstanceID ) const
2014-06-05 19:52:14 +03:00
{
if ( visitingHero = = h )
cb - > setObjProperty ( id , ObjProperty : : STRUCTURE_ADD_VISITING_HERO , structureInstanceID ) ; //add to visitors
else if ( garrisonHero = = h )
cb - > setObjProperty ( id , ObjProperty : : STRUCTURE_ADD_GARRISONED_HERO , structureInstanceID ) ; //then it must be garrisoned hero
else
{
//should never ever happen
2017-08-11 19:03:05 +02:00
logGlobal - > error ( " Cannot add hero %s to visitors of structure # %d " , h - > name , structureInstanceID ) ;
throw std : : runtime_error ( " internal error " ) ;
2014-06-05 19:52:14 +03:00
}
}
2021-03-23 16:47:07 +02:00
void CGTownInstance : : battleFinished ( const CGHeroInstance * hero , const BattleResult & result ) const
2014-06-05 19:52:14 +03:00
{
2021-03-23 16:47:07 +02:00
if ( result . winner = = BattleSide : : ATTACKER )
2014-06-05 19:52:14 +03:00
{
2016-02-22 02:33:15 +02:00
clearArmy ( ) ;
2021-10-29 10:45:10 +02:00
onTownCaptured ( hero - > getOwner ( ) ) ;
2014-06-05 19:52:14 +03:00
}
}
2021-10-29 10:45:10 +02:00
void CGTownInstance : : onTownCaptured ( const PlayerColor winner ) const
{
setOwner ( winner ) ;
FoWChange fw ;
fw . player = winner ;
fw . mode = 1 ;
cb - > getTilesInRange ( fw . tiles , getSightCenter ( ) , getSightRadius ( ) , winner , 1 ) ;
cb - > sendAndApply ( & fw ) ;
}
2017-05-28 15:23:42 +02:00
void CGTownInstance : : afterAddToMap ( CMap * map )
{
2017-05-28 17:42:36 +02:00
if ( ID = = Obj : : TOWN )
2017-05-28 17:40:13 +02:00
map - > towns . push_back ( this ) ;
2017-05-28 15:23:42 +02:00
}
2022-09-17 13:04:01 +02:00
void CGTownInstance : : afterRemoveFromMap ( CMap * map )
{
if ( ID = = Obj : : TOWN )
vstd : : erase_if_present ( map - > towns , this ) ;
}
2018-01-05 19:21:07 +02:00
void CGTownInstance : : reset ( )
{
CGTownInstance : : merchantArtifacts . clear ( ) ;
CGTownInstance : : universitySkills . clear ( ) ;
}
2016-02-23 15:36:21 +02:00
void CGTownInstance : : serializeJsonOptions ( JsonSerializeFormat & handler )
2016-02-22 01:37:19 +02:00
{
CGObjectInstance : : serializeJsonOwner ( handler ) ;
2016-11-13 12:38:42 +02:00
CCreatureSet : : serializeJson ( handler , " army " , 7 ) ;
handler . serializeBool < ui8 > ( " tightFormation " , formation , 1 , 0 , 0 ) ;
2016-02-23 15:36:21 +02:00
handler . serializeString ( " name " , name ) ;
2016-11-13 12:38:42 +02:00
{
auto decodeBuilding = [ this ] ( const std : : string & identifier ) - > si32
{
2022-12-21 19:00:36 +02:00
auto rawId = VLC - > modh - > identifiers . getIdentifier ( CModHandler : : scopeMap ( ) , getTown ( ) - > getBuildingScope ( ) , identifier ) ;
2016-02-23 15:36:21 +02:00
2016-11-13 12:38:42 +02:00
if ( rawId )
return rawId . get ( ) ;
else
return - 1 ;
} ;
2016-02-23 15:36:21 +02:00
2016-11-13 12:38:42 +02:00
auto encodeBuilding = [ this ] ( si32 index ) - > std : : string
{
return getTown ( ) - > buildings . at ( BuildingID ( index ) ) - > identifier ;
} ;
const std : : set < si32 > standard = getTown ( ) - > getAllBuildings ( ) ; //by default all buildings are allowed
JsonSerializeFormat : : LICSet buildingsLIC ( standard , decodeBuilding , encodeBuilding ) ;
if ( handler . saving )
{
bool customBuildings = false ;
boost : : logic : : tribool hasFort ( false ) ;
2022-05-19 14:14:50 +02:00
for ( const BuildingID & id : forbiddenBuildings )
2016-11-13 12:38:42 +02:00
{
buildingsLIC . none . insert ( id ) ;
customBuildings = true ;
}
2022-05-19 14:14:50 +02:00
for ( const BuildingID & id : builtBuildings )
2016-11-13 12:38:42 +02:00
{
if ( id = = BuildingID : : DEFAULT )
continue ;
2016-02-23 15:36:21 +02:00
2016-11-13 12:38:42 +02:00
const CBuilding * building = getTown ( ) - > buildings . at ( id ) ;
if ( building - > mode = = CBuilding : : BUILD_AUTO )
continue ;
if ( id = = BuildingID : : FORT )
hasFort = true ;
buildingsLIC . all . insert ( id ) ;
customBuildings = true ;
}
if ( customBuildings )
handler . serializeLIC ( " buildings " , buildingsLIC ) ;
else
handler . serializeBool ( " hasFort " , hasFort ) ;
}
else
{
handler . serializeLIC ( " buildings " , buildingsLIC ) ;
builtBuildings . insert ( BuildingID : : VILLAGE_HALL ) ;
if ( buildingsLIC . none . empty ( ) & & buildingsLIC . all . empty ( ) )
{
builtBuildings . insert ( BuildingID : : DEFAULT ) ;
bool hasFort = false ;
handler . serializeBool ( " hasFort " , hasFort ) ;
if ( hasFort )
builtBuildings . insert ( BuildingID : : FORT ) ;
}
else
{
for ( const si32 item : buildingsLIC . none )
forbiddenBuildings . insert ( BuildingID ( item ) ) ;
for ( const si32 item : buildingsLIC . all )
builtBuildings . insert ( BuildingID ( item ) ) ;
}
}
}
2016-02-22 01:37:19 +02:00
2016-02-25 21:59:17 +02:00
{
2016-11-13 12:38:42 +02:00
std : : vector < bool > standard = VLC - > spellh - > getDefaultAllowed ( ) ;
2017-07-20 06:08:49 +02:00
JsonSerializeFormat : : LIC spellsLIC ( standard , SpellID : : decode , SpellID : : encode ) ;
2016-02-25 21:59:17 +02:00
2016-11-13 12:38:42 +02:00
if ( handler . saving )
{
for ( SpellID id : possibleSpells )
spellsLIC . any [ id . num ] = true ;
2016-02-25 21:59:17 +02:00
2016-11-13 12:38:42 +02:00
for ( SpellID id : obligatorySpells )
spellsLIC . all [ id . num ] = true ;
}
2016-02-25 21:59:17 +02:00
handler . serializeLIC ( " spells " , spellsLIC ) ;
if ( ! handler . saving )
{
possibleSpells . clear ( ) ;
for ( si32 idx = 0 ; idx < spellsLIC . any . size ( ) ; idx + + )
{
if ( spellsLIC . any [ idx ] )
possibleSpells . push_back ( SpellID ( idx ) ) ;
}
obligatorySpells . clear ( ) ;
for ( si32 idx = 0 ; idx < spellsLIC . all . size ( ) ; idx + + )
{
if ( spellsLIC . all [ idx ] )
obligatorySpells . push_back ( SpellID ( idx ) ) ;
}
}
}
2016-02-22 01:37:19 +02:00
}
Entities redesign and a few ERM features
* Made most Handlers derived from CHandlerBase and moved service API there.
* Declared existing Entity APIs.
* Added basic script context caching
* Started Lua script module
* Started Lua spell effect API
* Started script state persistence
* Started battle info callback binding
* CommitPackage removed
* Extracted spells::Caster to own header; Expanded Spell API.
* implemented !!MC:S, !!FU:E, !!FU:P, !!MA, !!VR:H, !!VR:C
* !!BU:C, !!BU:E, !!BU:G, !!BU:M implemented
* Allow use of "MC:S@varName@" to declare normal variable (technically v-variable with string key)
* Re-enabled VERM macros.
* !?GM0 added
* !?TM implemented
* Added !!MF:N
* Started !?OB, !!BM, !!HE, !!OW, !!UN
* Added basic support of w-variables
* Added support for ERM indirect variables
* Made !?FU regular trigger
* !!re (ERA loop receiver) implemented
* Fixed ERM receivers with zero args.
2018-03-17 16:58:30 +02:00
PlayerColor CGTownBuilding : : getOwner ( ) const
{
return town - > getOwner ( ) ;
}
int32_t CGTownBuilding : : getObjGroupIndex ( ) const
{
return - 1 ;
}
int32_t CGTownBuilding : : getObjTypeIndex ( ) const
{
return 0 ;
}
int3 CGTownBuilding : : visitablePos ( ) const
{
return town - > visitablePos ( ) ;
}
int3 CGTownBuilding : : getPosition ( ) const
{
return town - > getPosition ( ) ;
}
2020-10-25 00:04:34 +02:00
COPWBonus : : COPWBonus ( BuildingID bid , BuildingSubID : : EBuildingSubID subId , CGTownInstance * cgTown )
2014-06-05 19:52:14 +03:00
{
2020-10-02 23:55:46 +02:00
bID = bid ;
bType = subId ;
2020-10-25 00:04:34 +02:00
town = cgTown ;
2020-10-02 23:55:46 +02:00
indexOnTV = static_cast < si32 > ( town - > bonusingBuildings . size ( ) ) ;
2014-06-05 19:52:14 +03:00
}
2020-10-02 23:55:46 +02:00
2014-06-05 19:52:14 +03:00
void COPWBonus : : setProperty ( ui8 what , ui32 val )
{
switch ( what )
{
case ObjProperty : : VISITORS :
visitors . insert ( val ) ;
break ;
case ObjProperty : : STRUCTURE_CLEAR_VISITORS :
visitors . clear ( ) ;
break ;
}
}
2020-10-02 23:55:46 +02:00
Entities redesign and a few ERM features
* Made most Handlers derived from CHandlerBase and moved service API there.
* Declared existing Entity APIs.
* Added basic script context caching
* Started Lua script module
* Started Lua spell effect API
* Started script state persistence
* Started battle info callback binding
* CommitPackage removed
* Extracted spells::Caster to own header; Expanded Spell API.
* implemented !!MC:S, !!FU:E, !!FU:P, !!MA, !!VR:H, !!VR:C
* !!BU:C, !!BU:E, !!BU:G, !!BU:M implemented
* Allow use of "MC:S@varName@" to declare normal variable (technically v-variable with string key)
* Re-enabled VERM macros.
* !?GM0 added
* !?TM implemented
* Added !!MF:N
* Started !?OB, !!BM, !!HE, !!OW, !!UN
* Added basic support of w-variables
* Added support for ERM indirect variables
* Made !?FU regular trigger
* !!re (ERA loop receiver) implemented
* Fixed ERM receivers with zero args.
2018-03-17 16:58:30 +02:00
void COPWBonus : : onHeroVisit ( const CGHeroInstance * h ) const
2014-06-05 19:52:14 +03:00
{
ObjectInstanceID heroID = h - > id ;
2021-02-20 03:57:50 +02:00
if ( town - > hasBuilt ( bID ) )
2014-06-05 19:52:14 +03:00
{
InfoWindow iw ;
iw . player = h - > tempOwner ;
2020-10-02 23:55:46 +02:00
switch ( this - > bType )
2014-06-05 19:52:14 +03:00
{
2020-10-02 23:55:46 +02:00
case BuildingSubID : : STABLES :
2021-02-20 03:57:50 +02:00
if ( ! h - > hasBonusFrom ( Bonus : : OBJECT , Obj : : STABLES ) ) //does not stack with advMap Stables
{
GiveBonus gb ;
gb . bonus = Bonus ( Bonus : : ONE_WEEK , Bonus : : LAND_MOVEMENT , Bonus : : OBJECT , 600 , 94 , VLC - > generaltexth - > arraytxt [ 100 ] ) ;
gb . id = heroID . getNum ( ) ;
cb - > giveHeroBonus ( & gb ) ;
SetMovePoints mp ;
mp . val = 600 ;
mp . absolute = false ;
mp . hid = heroID ;
cb - > setMovePoints ( & mp ) ;
iw . text < < VLC - > generaltexth - > allTexts [ 580 ] ;
cb - > showInfoDialog ( & iw ) ;
}
break ;
2020-10-02 23:55:46 +02:00
case BuildingSubID : : MANA_VORTEX :
2021-02-20 03:57:50 +02:00
if ( visitors . empty ( ) )
{
if ( h - > mana < h - > manaLimit ( ) * 2 )
Entities redesign and a few ERM features
* Made most Handlers derived from CHandlerBase and moved service API there.
* Declared existing Entity APIs.
* Added basic script context caching
* Started Lua script module
* Started Lua spell effect API
* Started script state persistence
* Started battle info callback binding
* CommitPackage removed
* Extracted spells::Caster to own header; Expanded Spell API.
* implemented !!MC:S, !!FU:E, !!FU:P, !!MA, !!VR:H, !!VR:C
* !!BU:C, !!BU:E, !!BU:G, !!BU:M implemented
* Allow use of "MC:S@varName@" to declare normal variable (technically v-variable with string key)
* Re-enabled VERM macros.
* !?GM0 added
* !?TM implemented
* Added !!MF:N
* Started !?OB, !!BM, !!HE, !!OW, !!UN
* Added basic support of w-variables
* Added support for ERM indirect variables
* Made !?FU regular trigger
* !!re (ERA loop receiver) implemented
* Fixed ERM receivers with zero args.
2018-03-17 16:58:30 +02:00
cb - > setManaPoints ( heroID , 2 * h - > manaLimit ( ) ) ;
2021-02-20 03:57:50 +02:00
//TODO: investigate line below
//cb->setObjProperty (town->id, ObjProperty::VISITED, true);
iw . text < < getVisitingBonusGreeting ( ) ;
cb - > showInfoDialog ( & iw ) ;
//extra visit penalty if hero alredy had double mana points (or even more?!)
town - > addHeroToStructureVisitors ( h , indexOnTV ) ;
}
break ;
2014-06-05 19:52:14 +03:00
}
}
}
2020-10-25 00:04:34 +02:00
CTownBonus : : CTownBonus ( BuildingID index , BuildingSubID : : EBuildingSubID subId , CGTownInstance * cgTown )
2014-06-05 19:52:14 +03:00
{
2020-10-02 23:55:46 +02:00
bID = index ;
2020-10-19 21:38:06 +02:00
bType = subId ;
2020-10-25 00:04:34 +02:00
town = cgTown ;
2020-10-02 23:55:46 +02:00
indexOnTV = static_cast < si32 > ( town - > bonusingBuildings . size ( ) ) ;
2014-06-05 19:52:14 +03:00
}
2020-10-02 23:55:46 +02:00
2014-06-05 19:52:14 +03:00
void CTownBonus : : setProperty ( ui8 what , ui32 val )
{
if ( what = = ObjProperty : : VISITORS )
visitors . insert ( ObjectInstanceID ( val ) ) ;
}
2020-10-02 23:55:46 +02:00
Entities redesign and a few ERM features
* Made most Handlers derived from CHandlerBase and moved service API there.
* Declared existing Entity APIs.
* Added basic script context caching
* Started Lua script module
* Started Lua spell effect API
* Started script state persistence
* Started battle info callback binding
* CommitPackage removed
* Extracted spells::Caster to own header; Expanded Spell API.
* implemented !!MC:S, !!FU:E, !!FU:P, !!MA, !!VR:H, !!VR:C
* !!BU:C, !!BU:E, !!BU:G, !!BU:M implemented
* Allow use of "MC:S@varName@" to declare normal variable (technically v-variable with string key)
* Re-enabled VERM macros.
* !?GM0 added
* !?TM implemented
* Added !!MF:N
* Started !?OB, !!BM, !!HE, !!OW, !!UN
* Added basic support of w-variables
* Added support for ERM indirect variables
* Made !?FU regular trigger
* !!re (ERA loop receiver) implemented
* Fixed ERM receivers with zero args.
2018-03-17 16:58:30 +02:00
void CTownBonus : : onHeroVisit ( const CGHeroInstance * h ) const
2014-06-05 19:52:14 +03:00
{
ObjectInstanceID heroID = h - > id ;
2021-01-14 00:02:13 +02:00
if ( town - > hasBuilt ( bID ) & & visitors . find ( heroID ) = = visitors . end ( ) )
2014-06-05 19:52:14 +03:00
{
2020-10-02 23:55:46 +02:00
si64 val = 0 ;
2014-06-05 19:52:14 +03:00
InfoWindow iw ;
2021-01-14 00:02:13 +02:00
PrimarySkill : : PrimarySkill what = PrimarySkill : : NONE ;
2020-10-02 23:55:46 +02:00
2021-02-20 03:57:50 +02:00
switch ( bType )
2014-06-05 19:52:14 +03:00
{
2020-10-19 21:38:06 +02:00
case BuildingSubID : : KNOWLEDGE_VISITING_BONUS : //wall of knowledge
2021-02-20 03:57:50 +02:00
what = PrimarySkill : : KNOWLEDGE ;
val = 1 ;
iw . components . push_back ( Component ( Component : : PRIM_SKILL , 3 , 1 , 0 ) ) ;
break ;
2020-10-19 21:38:06 +02:00
case BuildingSubID : : SPELL_POWER_VISITING_BONUS : //order of fire
2021-02-20 03:57:50 +02:00
what = PrimarySkill : : SPELL_POWER ;
val = 1 ;
iw . components . push_back ( Component ( Component : : PRIM_SKILL , 2 , 1 , 0 ) ) ;
break ;
2020-10-19 21:38:06 +02:00
case BuildingSubID : : ATTACK_VISITING_BONUS : //hall of Valhalla
2021-02-20 03:57:50 +02:00
what = PrimarySkill : : ATTACK ;
val = 1 ;
iw . components . push_back ( Component ( Component : : PRIM_SKILL , 0 , 1 , 0 ) ) ;
break ;
2020-10-19 21:38:06 +02:00
case BuildingSubID : : EXPERIENCE_VISITING_BONUS : //academy of battle scholars
2021-02-20 03:57:50 +02:00
what = PrimarySkill : : EXPERIENCE ;
2020-10-19 21:38:06 +02:00
val = static_cast < int > ( h - > calculateXp ( 1000 ) ) ;
2021-02-20 03:57:50 +02:00
iw . components . push_back ( Component ( Component : : EXPERIENCE , 0 , val , 0 ) ) ;
break ;
2020-10-19 21:38:06 +02:00
case BuildingSubID : : DEFENSE_VISITING_BONUS : //cage of warlords
2021-02-20 03:57:50 +02:00
what = PrimarySkill : : DEFENSE ;
val = 1 ;
iw . components . push_back ( Component ( Component : : PRIM_SKILL , 1 , 1 , 0 ) ) ;
break ;
2021-01-14 00:02:13 +02:00
case BuildingSubID : : CUSTOM_VISITING_BONUS :
const auto building = town - > town - > buildings . at ( bID ) ;
if ( ! h - > hasBonusFrom ( Bonus : : TOWN_STRUCTURE , Bonus : : getSid32 ( building - > town - > faction - > index , building - > bid ) ) )
{
const auto & bonuses = building - > onVisitBonuses ;
applyBonuses ( const_cast < CGHeroInstance * > ( h ) , bonuses ) ;
2021-02-20 03:57:50 +02:00
}
break ;
2021-01-14 00:02:13 +02:00
}
2021-02-20 03:57:50 +02:00
2021-01-14 00:02:13 +02:00
if ( what ! = PrimarySkill : : NONE )
{
2021-02-20 03:57:50 +02:00
iw . player = cb - > getOwner ( heroID ) ;
iw . text < < getVisitingBonusGreeting ( ) ;
cb - > showInfoDialog ( & iw ) ;
cb - > changePrimSkill ( cb - > getHero ( heroID ) , what , val ) ;
town - > addHeroToStructureVisitors ( h , indexOnTV ) ;
}
2021-01-14 00:02:13 +02:00
}
}
void CTownBonus : : applyBonuses ( CGHeroInstance * h , const BonusList & bonuses ) const
{
auto addToVisitors = false ;
for ( auto bonus : bonuses )
{
GiveBonus gb ;
InfoWindow iw ;
2021-02-01 17:35:15 +02:00
if ( bonus - > type = = Bonus : : TOWN_MAGIC_WELL )
{
if ( h - > mana > = h - > manaLimit ( ) )
return ;
cb - > setManaPoints ( h - > id , h - > manaLimit ( ) ) ;
bonus - > duration = Bonus : : ONE_DAY ;
}
2021-01-14 00:02:13 +02:00
gb . bonus = * bonus ;
gb . id = h - > id . getNum ( ) ;
cb - > giveHeroBonus ( & gb ) ;
if ( bonus - > duration = = Bonus : : PERMANENT )
addToVisitors = true ;
iw . player = cb - > getOwner ( h - > id ) ;
iw . text < < getCustomBonusGreeting ( gb . bonus ) ;
2014-06-05 19:52:14 +03:00
cb - > showInfoDialog ( & iw ) ;
}
2021-01-14 00:02:13 +02:00
if ( addToVisitors )
town - > addHeroToStructureVisitors ( h , indexOnTV ) ;
2014-06-05 19:52:14 +03:00
}
GrowthInfo : : Entry : : Entry ( const std : : string & format , int _count )
: count ( _count )
{
description = boost : : str ( boost : : format ( format ) % count ) ;
}
GrowthInfo : : Entry : : Entry ( int subID , BuildingID building , int _count )
: count ( _count )
{
Entities redesign and a few ERM features
* Made most Handlers derived from CHandlerBase and moved service API there.
* Declared existing Entity APIs.
* Added basic script context caching
* Started Lua script module
* Started Lua spell effect API
* Started script state persistence
* Started battle info callback binding
* CommitPackage removed
* Extracted spells::Caster to own header; Expanded Spell API.
* implemented !!MC:S, !!FU:E, !!FU:P, !!MA, !!VR:H, !!VR:C
* !!BU:C, !!BU:E, !!BU:G, !!BU:M implemented
* Allow use of "MC:S@varName@" to declare normal variable (technically v-variable with string key)
* Re-enabled VERM macros.
* !?GM0 added
* !?TM implemented
* Added !!MF:N
* Started !?OB, !!BM, !!HE, !!OW, !!UN
* Added basic support of w-variables
* Added support for ERM indirect variables
* Made !?FU regular trigger
* !!re (ERA loop receiver) implemented
* Fixed ERM receivers with zero args.
2018-03-17 16:58:30 +02:00
description = boost : : str ( boost : : format ( " %s %+d " ) % ( * VLC - > townh ) [ subID ] - > town - > buildings . at ( building ) - > Name ( ) % count ) ;
2014-06-05 19:52:14 +03:00
}
2015-09-04 18:38:42 +02:00
GrowthInfo : : Entry : : Entry ( int _count , const std : : string & fullDescription )
: count ( _count )
{
description = fullDescription ;
}
2014-06-05 19:52:14 +03:00
CTownAndVisitingHero : : CTownAndVisitingHero ( )
{
setNodeType ( TOWN_AND_VISITOR ) ;
}
int GrowthInfo : : totalGrowth ( ) const
{
int ret = 0 ;
for ( const Entry & entry : entries )
ret + = entry . count ;
return ret ;
}
2020-10-25 00:04:34 +02:00
const std : : string CGTownBuilding : : getVisitingBonusGreeting ( ) const
{
auto bonusGreeting = town - > town - > getGreeting ( bType ) ;
if ( ! bonusGreeting . empty ( ) )
return bonusGreeting ;
switch ( bType )
{
case BuildingSubID : : MANA_VORTEX :
bonusGreeting = std : : string ( VLC - > generaltexth - > localizedTexts [ " townHall " ] [ " greetingManaVortex " ] . String ( ) ) ;
break ;
case BuildingSubID : : KNOWLEDGE_VISITING_BONUS :
bonusGreeting = std : : string ( VLC - > generaltexth - > localizedTexts [ " townHall " ] [ " greetingKnowledge " ] . String ( ) ) ;
break ;
case BuildingSubID : : SPELL_POWER_VISITING_BONUS :
bonusGreeting = std : : string ( VLC - > generaltexth - > localizedTexts [ " townHall " ] [ " greetingSpellPower " ] . String ( ) ) ;
break ;
case BuildingSubID : : ATTACK_VISITING_BONUS :
bonusGreeting = std : : string ( VLC - > generaltexth - > localizedTexts [ " townHall " ] [ " greetingAttack " ] . String ( ) ) ;
break ;
case BuildingSubID : : EXPERIENCE_VISITING_BONUS :
bonusGreeting = std : : string ( VLC - > generaltexth - > localizedTexts [ " townHall " ] [ " greetingExperience " ] . String ( ) ) ;
break ;
case BuildingSubID : : DEFENSE_VISITING_BONUS :
bonusGreeting = std : : string ( VLC - > generaltexth - > localizedTexts [ " townHall " ] [ " greetingDefence " ] . String ( ) ) ;
break ;
}
auto buildingName = town - > town - > getSpecialBuilding ( bType ) - > Name ( ) ;
2021-12-20 01:34:35 +02:00
if ( bonusGreeting . empty ( ) )
{
bonusGreeting = " Error: Bonus greeting for '%s' is not localized. " ;
logGlobal - > error ( " '%s' building of '%s' faction has not localized bonus greeting. " , buildingName , town - > town - > getLocalizedFactionName ( ) ) ;
}
2020-10-25 00:04:34 +02:00
boost : : algorithm : : replace_first ( bonusGreeting , " %s " , buildingName ) ;
town - > town - > setGreeting ( bType , bonusGreeting ) ;
return bonusGreeting ;
}
2021-01-14 00:02:13 +02:00
2021-02-01 17:35:15 +02:00
const std : : string CGTownBuilding : : getCustomBonusGreeting ( const Bonus & bonus ) const
2021-01-14 00:02:13 +02:00
{
2021-02-01 17:35:15 +02:00
if ( bonus . type = = Bonus : : TOWN_MAGIC_WELL )
{
auto bonusGreeting = std : : string ( VLC - > generaltexth - > localizedTexts [ " townHall " ] [ " greetingInTownMagicWell " ] . String ( ) ) ;
auto buildingName = town - > town - > getSpecialBuilding ( bType ) - > Name ( ) ;
boost : : algorithm : : replace_first ( bonusGreeting , " %s " , buildingName ) ;
return bonusGreeting ;
}
2021-01-14 00:02:13 +02:00
auto bonusGreeting = std : : string ( VLC - > generaltexth - > localizedTexts [ " townHall " ] [ " greetingCustomBonus " ] . String ( ) ) ; //"%s gives you +%d %s%s"
std : : string param = " " ;
std : : string until = " " ;
if ( bonus . type = = Bonus : : MORALE )
param = VLC - > generaltexth - > allTexts [ 384 ] ;
else if ( bonus . type = = Bonus : : LUCK )
param = VLC - > generaltexth - > allTexts [ 385 ] ;
until = bonus . duration = = ( ui16 ) Bonus : : ONE_BATTLE
? VLC - > generaltexth - > localizedTexts [ " townHall " ] [ " greetingCustomUntil " ] . String ( ) : " . " ;
boost : : format fmt = boost : : format ( bonusGreeting ) % bonus . description % bonus . val % param % until ;
std : : string greeting = fmt . str ( ) ;
return greeting ;
}
2022-07-26 15:07:42 +02:00
VCMI_LIB_NAMESPACE_END