2015-12-02 21:39:53 +02:00
/*
2014-06-05 20:26:50 +03:00
* CArmedInstance . 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"
2014-06-05 20:26:50 +03:00
# include "CArmedInstance.h"
2014-06-05 19:52:14 +03:00
# include "../CCreatureHandler.h"
2015-12-02 21:39:53 +02:00
# include "../CPlayerState.h"
2024-07-21 10:49:40 +00:00
# include "../entities/faction/CFaction.h"
# include "../entities/faction/CTown.h"
# include "../entities/faction/CTownHandler.h"
# include "../gameState/CGameState.h"
# include "../texts/CGeneralTextHandler.h"
2014-06-05 19:52:14 +03:00
2022-07-26 16:07:42 +03:00
VCMI_LIB_NAMESPACE_BEGIN
2023-11-05 18:58:07 +02:00
void CArmedInstance : : randomizeArmy ( FactionID type )
2014-06-05 19:52:14 +03:00
{
for ( auto & elem : stacks )
{
2023-04-02 19:56:10 +03:00
if ( elem . second - > randomStack )
2014-06-05 19:52:14 +03:00
{
2023-04-02 19:56:10 +03:00
int level = elem . second - > randomStack - > level ;
int upgrade = elem . second - > randomStack - > 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 17:58:30 +03:00
elem . second - > setType ( ( * VLC - > townh ) [ type ] - > town - > creatures [ level ] [ upgrade ] ) ;
2014-06-05 19:52:14 +03:00
2023-04-16 20:42:56 +03:00
elem . second - > randomStack = std : : nullopt ;
2014-06-05 19:52:14 +03:00
}
assert ( elem . second - > valid ( false ) ) ;
assert ( elem . second - > armyObj = = this ) ;
}
}
2024-01-01 16:37:48 +02:00
CArmedInstance : : CArmedInstance ( IGameCallback * cb )
: CArmedInstance ( cb , false )
2021-05-23 14:28:43 +03:00
{
}
2024-01-01 16:37:48 +02:00
CArmedInstance : : CArmedInstance ( IGameCallback * cb , bool isHypothetic ) :
CGObjectInstance ( cb ) ,
2023-12-21 18:09:33 +01:00
CBonusSystemNode ( isHypothetic ) ,
2024-12-26 16:57:05 +00:00
nonEvilAlignmentMix ( this , Selector : : type ( ) ( BonusType : : NONEVIL_ALIGNMENT_MIX ) ) , // Take Angelic Alliance troop-mixing freedom of non-evil units into account.
2023-02-12 23:39:17 +03:00
battle ( nullptr )
2014-06-05 19:52:14 +03:00
{
}
void CArmedInstance : : updateMoraleBonusFromArmy ( )
{
if ( ! validTypes ( false ) ) //object not randomized, don't bother
return ;
2023-05-01 01:20:01 +03:00
auto b = getExportedBonusList ( ) . getFirst ( Selector : : sourceType ( ) ( BonusSource : : ARMY ) . And ( Selector : : type ( ) ( BonusType : : MORALE ) ) ) ;
2016-09-19 23:36:35 +02:00
if ( ! b )
2014-06-05 19:52:14 +03:00
{
2023-10-21 14:50:42 +03:00
b = std : : make_shared < Bonus > ( BonusDuration : : PERMANENT , BonusType : : MORALE , BonusSource : : ARMY , 0 , BonusSourceID ( ) ) ;
2014-06-05 19:52:14 +03:00
addNewBonus ( b ) ;
}
//number of alignments and presence of undead
2023-04-09 18:26:32 +03:00
std : : set < FactionID > factions ;
2014-06-05 19:52:14 +03:00
bool hasUndead = false ;
2020-06-28 16:19:27 +03:00
const std : : string undeadCacheKey = " type_UNDEAD " ;
2023-05-01 01:20:01 +03:00
static const CSelector undeadSelector = Selector : : type ( ) ( BonusType : : UNDEAD ) ;
2020-06-28 16:19:27 +03:00
2023-02-12 23:39:17 +03:00
for ( const auto & slot : Slots ( ) )
2014-06-05 19:52:14 +03:00
{
const CStackInstance * inst = slot . second ;
2023-11-04 17:04:53 +02:00
const auto * creature = inst - > getCreatureID ( ) . toEntity ( VLC ) ;
2014-06-05 19:52:14 +03:00
2024-10-05 19:37:52 +00:00
factions . insert ( creature - > getFactionID ( ) ) ;
2014-06-05 19:52:14 +03:00
// Check for undead flag instead of faction (undead mummies are neutral)
2022-09-14 11:00:40 +02:00
if ( ! hasUndead )
{
//this is costly check, let's skip it at first undead
hasUndead | = inst - > hasBonus ( undeadSelector , undeadCacheKey ) ;
}
2014-06-05 19:52:14 +03:00
}
size_t factionsInArmy = factions . size ( ) ; //town garrison seems to take both sets into account
2024-12-26 16:57:05 +00:00
if ( nonEvilAlignmentMix . hasBonus ( ) )
2014-06-05 19:52:14 +03:00
{
size_t mixableFactions = 0 ;
2023-04-09 18:26:32 +03:00
for ( auto f : factions )
2014-06-05 19:52:14 +03:00
{
2023-11-02 16:56:02 +02:00
if ( VLC - > factions ( ) - > getById ( f ) - > getAlignment ( ) ! = EAlignment : : EVIL )
2014-06-05 19:52:14 +03:00
mixableFactions + + ;
}
if ( mixableFactions > 0 )
factionsInArmy - = mixableFactions - 1 ;
}
2024-04-07 19:57:49 +03:00
MetaString bonusDescription ;
2021-05-16 20:53:11 +03:00
2014-06-05 19:52:14 +03:00
if ( factionsInArmy = = 1 )
{
b - > val = + 1 ;
2024-04-07 19:57:49 +03:00
bonusDescription . appendTextID ( " core.arraytxt.115 " ) ; //All troops of one alignment +1
2014-06-05 19:52:14 +03:00
}
else if ( ! factions . empty ( ) ) // no bonus from empty garrison
{
2023-02-12 23:39:17 +03:00
b - > val = 2 - static_cast < si32 > ( factionsInArmy ) ;
2024-04-07 19:57:49 +03:00
bonusDescription . appendTextID ( " core.arraytxt.114 " ) ; //Troops of %d alignments %d
bonusDescription . replaceNumber ( factionsInArmy ) ;
2014-06-05 19:52:14 +03:00
}
2021-05-16 20:53:11 +03:00
2024-04-07 19:57:49 +03:00
b - > description = bonusDescription ;
2021-05-16 20:53:11 +03:00
2025-01-10 23:45:02 +00:00
nodeHasChanged ( ) ;
2014-06-05 19:52:14 +03:00
//-1 modifier for any Undead unit in army
2023-10-21 14:50:42 +03:00
auto undeadModifier = getExportedBonusList ( ) . getFirst ( Selector : : source ( BonusSource : : ARMY , BonusCustomSource : : undeadMoraleDebuff ) ) ;
2014-06-05 19:52:14 +03:00
if ( hasUndead )
{
if ( ! undeadModifier )
2015-09-04 19:38:42 +03:00
{
2024-04-07 19:57:49 +03:00
undeadModifier = std : : make_shared < Bonus > ( BonusDuration : : PERMANENT , BonusType : : MORALE , BonusSource : : ARMY , - 1 , BonusCustomSource : : undeadMoraleDebuff ) ;
undeadModifier - > description . appendTextID ( " core.arraytxt.116 " ) ;
2015-09-04 19:38:42 +03:00
addNewBonus ( undeadModifier ) ;
2015-11-16 16:30:40 +03:00
}
2014-06-05 19:52:14 +03:00
}
else if ( undeadModifier )
removeBonus ( undeadModifier ) ;
}
void CArmedInstance : : armyChanged ( )
{
updateMoraleBonusFromArmy ( ) ;
}
2022-11-06 03:26:13 +04:00
CBonusSystemNode & CArmedInstance : : whereShouldBeAttached ( CGameState * gs )
2014-06-05 19:52:14 +03:00
{
2023-08-27 01:35:38 +03:00
if ( tempOwner . isValidPlayer ( ) )
2022-11-06 03:26:13 +04:00
if ( auto * where = gs - > getPlayerState ( tempOwner ) )
return * where ;
return gs - > globalEffects ;
2014-06-05 19:52:14 +03:00
}
2022-11-06 03:26:13 +04:00
CBonusSystemNode & CArmedInstance : : whatShouldBeAttached ( )
2014-06-05 19:52:14 +03:00
{
2022-11-06 03:26:13 +04:00
return * this ;
2014-06-05 19:52:14 +03:00
}
2022-07-26 16:07:42 +03:00
2023-04-09 18:26:32 +03:00
const IBonusBearer * CArmedInstance : : getBonusBearer ( ) const
{
return this ;
}
2023-09-17 22:19:45 +02:00
void CArmedInstance : : serializeJsonOptions ( JsonSerializeFormat & handler )
{
CGObjectInstance : : serializeJsonOptions ( handler ) ;
CCreatureSet : : serializeJson ( handler , " army " , 7 ) ;
}
2022-07-26 16:07:42 +03:00
VCMI_LIB_NAMESPACE_END