2015-12-02 21:05:10 +02:00
/*
2014-06-05 20:26:50 +03:00
* CGHeroInstance . 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 "CGHeroInstance.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
# include <vcmi/ServerCallback.h>
# include <vcmi/spells/Spell.h>
2014-06-05 20:26:50 +03:00
# include "../NetPacks.h"
# include "../CGeneralTextHandler.h"
# include "../CHeroHandler.h"
2023-01-09 01:17:37 +02:00
# include "../TerrainHandler.h"
2023-01-11 15:17:24 +02:00
# include "../RoadHandler.h"
2014-06-25 17:11:07 +03:00
# include "../CModHandler.h"
2014-06-05 23:51:24 +03:00
# include "../CSoundBase.h"
2015-02-02 10:25:26 +02:00
# include "../spells/CSpellHandler.h"
2017-08-21 14:35:46 +02:00
# include "../CSkillHandler.h"
2014-06-05 19:52:14 +03:00
# include "CObjectClassesHandler.h"
2014-06-25 17:11:07 +03:00
# include "../IGameCallback.h"
# include "../CGameState.h"
# include "../CCreatureHandler.h"
2015-12-02 21:05:10 +02:00
# include "../CTownHandler.h"
2016-01-27 12:47:42 +02:00
# include "../mapping/CMap.h"
2015-12-02 21:05:10 +02:00
# include "CGTownInstance.h"
2016-02-22 01:37:19 +02:00
# include "../serializer/JsonSerializeFormat.h"
2016-11-13 12:38:42 +02:00
# include "../StringConstants.h"
2017-07-20 06:08:49 +02:00
# include "../battle/Unit.h"
2022-07-26 15:07:42 +02:00
VCMI_LIB_NAMESPACE_BEGIN
2014-06-05 19:52:14 +03:00
static int lowestSpeed ( const CGHeroInstance * chi )
{
2022-04-23 15:45:38 +02:00
static const CSelector selectorSTACKS_SPEED = Selector : : type ( ) ( Bonus : : STACKS_SPEED ) ;
2023-02-12 22:39:17 +02:00
static const std : : string keySTACKS_SPEED = " type_ " + std : : to_string ( static_cast < si32 > ( Bonus : : STACKS_SPEED ) ) ;
2022-04-23 15:45:38 +02:00
2015-12-24 20:30:57 +02:00
if ( ! chi - > stacksCount ( ) )
2014-06-05 19:52:14 +03:00
{
2022-04-23 15:45:38 +02:00
if ( chi - > commander & & chi - > commander - > alive )
{
return chi - > commander - > valOfBonuses ( selectorSTACKS_SPEED , keySTACKS_SPEED ) ;
}
2023-01-02 13:27:03 +02:00
logGlobal - > error ( " Hero %d (%s) has no army! " , chi - > id . getNum ( ) , chi - > getNameTranslated ( ) ) ;
2014-06-05 19:52:14 +03:00
return 20 ;
}
2022-04-23 15:45:38 +02:00
2014-06-05 19:52:14 +03:00
auto i = chi - > Slots ( ) . begin ( ) ;
//TODO? should speed modifiers (eg from artifacts) affect hero movement?
2019-01-15 05:00:00 +02:00
int ret = ( i + + ) - > second - > valOfBonuses ( selectorSTACKS_SPEED , keySTACKS_SPEED ) ;
2017-08-11 19:03:05 +02:00
for ( ; i ! = chi - > Slots ( ) . end ( ) ; i + + )
2019-01-15 05:00:00 +02:00
ret = std : : min ( ret , i - > second - > valOfBonuses ( selectorSTACKS_SPEED , keySTACKS_SPEED ) ) ;
2014-06-05 19:52:14 +03:00
return ret ;
}
2019-01-15 05:00:00 +02:00
ui32 CGHeroInstance : : getTileCost ( const TerrainTile & dest , const TerrainTile & from , const TurnInfo * ti ) const
2014-06-05 19:52:14 +03:00
{
2021-08-21 11:05:28 +02:00
int64_t ret = GameConstants : : BASE_MOVEMENT_COST ;
2014-06-05 19:52:14 +03:00
//if there is road both on dest and src tiles - use road movement cost
2023-01-01 17:10:47 +02:00
if ( dest . roadType - > getId ( ) ! = Road : : NO_ROAD & & from . roadType - > getId ( ) ! = Road : : NO_ROAD )
2014-06-05 19:52:14 +03:00
{
2022-09-23 16:24:01 +02:00
ret = std : : max ( dest . roadType - > movementCost , from . roadType - > movementCost ) ;
2014-06-05 19:52:14 +03:00
}
2023-01-01 17:10:47 +02:00
else if ( ti - > nativeTerrain ! = from . terType - > getId ( ) & & //the terrain is not native
2022-12-20 18:35:40 +02:00
ti - > nativeTerrain ! = ETerrainId : : ANY_TERRAIN & & //no special creature bonus
2023-01-10 20:09:09 +02:00
! ti - > hasBonusOfType ( Bonus : : NO_TERRAIN_PENALTY , from . terType - > getIndex ( ) ) ) //no special movement bonus
2014-06-05 19:52:14 +03:00
{
2019-01-15 05:00:00 +02:00
2023-01-01 17:10:47 +02:00
ret = VLC - > heroh - > terrCosts [ from . terType - > getId ( ) ] ;
2023-02-15 21:01:38 +02:00
ret - = ti - > valOfBonuses ( Bonus : : ROUGH_TERRAIN_DISCOUNT ) ;
2015-11-21 12:30:39 +02:00
if ( ret < GameConstants : : BASE_MOVEMENT_COST )
ret = GameConstants : : BASE_MOVEMENT_COST ;
}
2023-02-12 22:39:17 +02:00
return static_cast < ui32 > ( ret ) ;
2015-11-21 12:30:39 +02:00
}
2014-06-05 19:52:14 +03:00
2022-09-29 11:44:46 +02:00
TerrainId CGHeroInstance : : getNativeTerrain ( ) const
2015-11-21 12:30:39 +02:00
{
// NOTE: in H3 neutral stacks will ignore terrain penalty only if placed as topmost stack(s) in hero army.
// This is clearly bug in H3 however intended behaviour is not clear.
// Current VCMI behaviour will ignore neutrals in calculations so army in VCMI
// will always have best penalty without any influence from player-defined stacks order
2023-01-10 20:09:09 +02:00
// and army that consist solely from neutral will always be considered to be on native terrain
2015-10-16 02:03:40 +02:00
2023-01-10 20:09:09 +02:00
TerrainId nativeTerrain = ETerrainId : : ANY_TERRAIN ;
2020-12-10 03:05:37 +02:00
2023-02-12 22:39:17 +02:00
for ( const auto & stack : stacks )
2015-11-21 12:30:39 +02:00
{
2022-09-29 11:44:46 +02:00
TerrainId stackNativeTerrain = stack . second - > type - > getNativeTerrain ( ) ; //consider terrain bonuses e.g. Lodestar.
2015-11-21 12:30:39 +02:00
2023-01-10 20:09:09 +02:00
if ( stackNativeTerrain = = ETerrainId : : NONE )
2020-12-10 03:05:37 +02:00
continue ;
2023-01-10 20:09:09 +02:00
if ( nativeTerrain = = ETerrainId : : ANY_TERRAIN )
2015-11-21 12:30:39 +02:00
nativeTerrain = stackNativeTerrain ;
else if ( nativeTerrain ! = stackNativeTerrain )
2023-01-10 20:09:09 +02:00
return ETerrainId : : NONE ;
2015-10-15 14:17:21 +02:00
}
2015-11-21 12:30:39 +02:00
return nativeTerrain ;
2014-06-05 19:52:14 +03:00
}
2022-07-09 18:00:03 +02:00
BattleField CGHeroInstance : : getBattlefield ( ) const
{
return BattleField : : NONE ;
}
2023-02-12 22:39:17 +02:00
ui8 CGHeroInstance : : getSecSkillLevel ( const SecondarySkill & skill ) const
2014-06-05 19:52:14 +03:00
{
2023-02-12 22:39:17 +02:00
for ( const auto & elem : secSkills )
2014-06-05 19:52:14 +03:00
if ( elem . first = = skill )
return elem . second ;
return 0 ;
}
2023-02-12 22:39:17 +02:00
void CGHeroInstance : : setSecSkillLevel ( const SecondarySkill & which , int val , bool abs )
2014-06-05 19:52:14 +03:00
{
if ( getSecSkillLevel ( which ) = = 0 )
{
2023-02-12 22:39:17 +02:00
secSkills . emplace_back ( which , val ) ;
2018-03-05 09:02:23 +02:00
updateSkillBonus ( which , val ) ;
2014-06-05 19:52:14 +03:00
}
else
{
for ( auto & elem : secSkills )
{
if ( elem . first = = which )
{
if ( abs )
elem . second = val ;
else
elem . second + = val ;
if ( elem . second > 3 ) //workaround to avoid crashes when same sec skill is given more than once
{
2017-08-12 14:43:41 +02:00
logGlobal - > warn ( " Skill %d increased over limit! Decreasing to Expert. " , static_cast < int > ( which . toEnum ( ) ) ) ;
2014-06-05 19:52:14 +03:00
elem . second = 3 ;
}
2018-03-05 09:02:23 +02:00
updateSkillBonus ( which , elem . second ) ; //when we know final value
2014-06-05 19:52:14 +03:00
}
}
}
}
2022-12-09 14:42:47 +02:00
int3 CGHeroInstance : : convertToVisitablePos ( const int3 & position ) const
{
return position - getVisitableOffset ( ) ;
}
int3 CGHeroInstance : : convertFromVisitablePos ( const int3 & position ) const
{
return position + getVisitableOffset ( ) ;
}
2014-06-05 19:52:14 +03:00
bool CGHeroInstance : : canLearnSkill ( ) const
{
return secSkills . size ( ) < GameConstants : : SKILL_PER_HERO ;
}
2023-02-12 22:39:17 +02:00
bool CGHeroInstance : : canLearnSkill ( const SecondarySkill & which ) const
2022-11-14 19:08:49 +02:00
{
if ( ! canLearnSkill ( ) )
return false ;
if ( ! cb - > isAllowed ( 2 , which ) )
return false ;
if ( getSecSkillLevel ( which ) > 0 )
return false ;
if ( type - > heroClass - > secSkillProbability [ which ] = = 0 )
return false ;
return true ;
}
2019-01-15 07:52:55 +02:00
int CGHeroInstance : : maxMovePoints ( bool onLand ) const
2014-06-05 19:52:14 +03:00
{
2019-01-15 07:52:55 +02:00
TurnInfo ti ( this ) ;
return maxMovePointsCached ( onLand , & ti ) ;
}
2015-11-12 13:04:33 +02:00
2019-01-15 07:52:55 +02:00
int CGHeroInstance : : maxMovePointsCached ( bool onLand , const TurnInfo * ti ) const
{
2023-02-12 22:39:17 +02:00
int base = 0 ;
2014-06-05 19:52:14 +03:00
if ( onLand )
{
// used function is f(x) = 66.6x + 1300, rounded to second digit, where x is lowest speed in army
2023-02-12 22:39:17 +02:00
static constexpr int baseSpeed = 1300 ; // base speed from creature with 0 speed
2014-06-05 19:52:14 +03:00
int armySpeed = lowestSpeed ( this ) * 20 / 3 ;
base = armySpeed * 10 + baseSpeed ; // separate *10 is intentional to receive same rounding as in h3
vstd : : abetween ( base , 1500 , 2000 ) ; // base speed is limited by these values
}
else
{
base = 1500 ; //on water base movement is always 1500 (speed of army doesn't matter)
}
const Bonus : : BonusType bt = onLand ? Bonus : : LAND_MOVEMENT : Bonus : : SEA_MOVEMENT ;
2015-11-12 13:04:33 +02:00
const int bonus = ti - > valOfBonuses ( Bonus : : MOVEMENT ) + ti - > valOfBonuses ( bt ) ;
2014-06-05 19:52:14 +03:00
const int subtype = onLand ? SecondarySkill : : LOGISTICS : SecondarySkill : : NAVIGATION ;
2015-11-12 13:04:33 +02:00
const double modifier = ti - > valOfBonuses ( Bonus : : SECONDARY_SKILL_PREMY , subtype ) / 100.0 ;
2014-06-05 19:52:14 +03:00
2023-02-12 22:39:17 +02:00
return static_cast < int > ( base * ( 1 + modifier ) ) + bonus ;
2014-06-05 19:52:14 +03:00
}
2023-02-12 22:39:17 +02:00
CGHeroInstance : : CGHeroInstance ( ) :
IBoatGenerator ( this ) ,
tacticFormationEnabled ( false ) ,
inTownGarrison ( false ) ,
moveDir ( 4 ) ,
mana ( UNINITIALIZED_MANA ) ,
movement ( UNINITIALIZED_MOVEMENT ) ,
portrait ( UNINITIALIZED_PORTRAIT ) ,
level ( 1 ) ,
2023-02-15 12:10:39 +02:00
exp ( UNINITIALIZED_EXPERIENCE ) ,
2023-02-12 22:39:17 +02:00
sex ( std : : numeric_limits < ui8 > : : max ( ) )
2014-06-05 19:52:14 +03:00
{
setNodeType ( HERO ) ;
ID = Obj : : HERO ;
2023-02-12 22:39:17 +02:00
secSkills . emplace_back ( SecondarySkill : : DEFAULT , - 1 ) ;
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
PlayerColor CGHeroInstance : : getOwner ( ) const
{
return tempOwner ;
}
2023-02-12 22:39:17 +02:00
void CGHeroInstance : : initHero ( CRandomGenerator & rand , const HeroTypeID & SUBID )
2014-06-05 19:52:14 +03:00
{
subID = SUBID . getNum ( ) ;
2016-09-09 19:30:36 +02:00
initHero ( rand ) ;
2014-06-05 19:52:14 +03:00
}
void CGHeroInstance : : setType ( si32 ID , si32 subID )
{
assert ( ID = = Obj : : HERO ) ; // just in case
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
type = VLC - > heroh - > objects [ subID ] ;
2014-06-05 19:52:14 +03:00
portrait = type - > imageIndex ;
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
CGObjectInstance : : setType ( ID , type - > heroClass - > getIndex ( ) ) ; // to find object handler we must use heroClass->id
2016-09-18 09:01:09 +02:00
this - > subID = subID ; // after setType subID used to store unique hero identify id. Check issue 2277 for details
2014-06-05 19:52:14 +03:00
randomizeArmy ( type - > heroClass - > faction ) ;
}
2016-09-09 19:30:36 +02:00
void CGHeroInstance : : initHero ( CRandomGenerator & rand )
2014-06-05 19:52:14 +03:00
{
assert ( validTypes ( true ) ) ;
if ( ! type )
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
type = VLC - > heroh - > objects [ subID ] ;
2014-06-05 19:52:14 +03:00
if ( ID = = Obj : : HERO )
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
appearance = VLC - > objtypeh - > getHandlerFor ( Obj : : HERO , type - > heroClass - > getIndex ( ) ) - > getTemplates ( ) . front ( ) ;
2014-06-05 19:52:14 +03:00
if ( ! vstd : : contains ( spells , SpellID : : PRESET ) ) //hero starts with a spell
{
2023-02-12 22:39:17 +02:00
for ( const auto & spellID : type - > spells )
2014-06-05 19:52:14 +03:00
spells . insert ( spellID ) ;
}
else //remove placeholder
spells - = SpellID : : PRESET ;
if ( ! getArt ( ArtifactPosition : : MACH4 ) & & ! getArt ( ArtifactPosition : : SPELLBOOK ) & & type - > haveSpellBook ) //no catapult means we haven't read pre-existent set -> use default rules for spellbook
2015-11-07 10:46:58 +02:00
putArtifact ( ArtifactPosition : : SPELLBOOK , CArtifactInstance : : createNewArtifactInstance ( ArtifactID : : SPELLBOOK ) ) ;
2014-06-05 19:52:14 +03:00
if ( ! getArt ( ArtifactPosition : : MACH4 ) )
2015-11-07 10:46:58 +02:00
putArtifact ( ArtifactPosition : : MACH4 , CArtifactInstance : : createNewArtifactInstance ( ArtifactID : : CATAPULT ) ) ; //everyone has a catapult
2014-06-05 19:52:14 +03:00
if ( portrait < 0 | | portrait = = 255 )
portrait = type - > imageIndex ;
2020-11-11 21:43:40 +02:00
if ( ! hasBonus ( Selector : : sourceType ( ) ( Bonus : : HERO_BASE_SKILL ) ) )
2014-06-05 19:52:14 +03:00
{
for ( int g = 0 ; g < GameConstants : : PRIMARY_SKILLS ; + + g )
{
pushPrimSkill ( static_cast < PrimarySkill : : PrimarySkill > ( g ) , type - > heroClass - > primarySkillInitial [ g ] ) ;
}
}
if ( secSkills . size ( ) = = 1 & & secSkills [ 0 ] = = std : : pair < SecondarySkill , ui8 > ( SecondarySkill : : DEFAULT , - 1 ) ) //set secondary skills to default
secSkills = type - > secSkillsInit ;
if ( sex = = 0xFF ) //sex is default
sex = type - > sex ;
setFormation ( false ) ;
if ( ! stacksCount ( ) ) //standard army//initial army
{
2016-09-09 19:30:36 +02:00
initArmy ( rand ) ;
2014-06-05 19:52:14 +03:00
}
assert ( validTypes ( ) ) ;
2023-01-06 23:11:53 +02:00
if ( patrol . patrolling )
patrol . initialPos = visitablePos ( ) ;
2023-02-15 12:10:39 +02:00
if ( exp = = UNINITIALIZED_EXPERIENCE )
2014-06-05 19:52:14 +03:00
{
2016-09-09 19:30:36 +02:00
initExp ( rand ) ;
2014-06-05 19:52:14 +03:00
}
else
{
2016-09-09 19:30:36 +02:00
levelUpAutomatically ( rand ) ;
2014-06-05 19:52:14 +03:00
}
2023-02-19 20:42:51 +02:00
// load base hero bonuses, TODO: per-map loading of base hero bonuses
// must be done separately from global bonuses since recruitable heroes in taverns
// are not attached to global bonus node but need access to some global bonuses
// e.g. MANA_PER_KNOWLEDGE for correct preview and initial state after recruit for(const auto & ob : VLC->modh->heroBaseBonuses)
// or MOVEMENT to compute initial movement before recruiting is finished
for ( const auto & ob : VLC - > modh - > heroBaseBonuses )
{
auto bonus = ob ;
bonus - > source = Bonus : : HERO_BASE_SKILL ;
bonus - > sid = id . getNum ( ) ;
bonus - > duration = Bonus : : PERMANENT ;
addNewBonus ( bonus ) ;
}
2014-06-05 19:52:14 +03:00
if ( VLC - > modh - > modules . COMMANDERS & & ! commander )
{
commander = new CCommanderInstance ( type - > heroClass - > commander - > idNumber ) ;
commander - > setArmyObj ( castToArmyObj ( ) ) ; //TODO: separate function for setting commanders
commander - > giveStackExp ( exp ) ; //after our exp is set
}
if ( mana < 0 )
mana = manaLimit ( ) ;
}
2017-07-15 13:08:20 +02:00
void CGHeroInstance : : initArmy ( CRandomGenerator & rand , IArmyDescriptor * dst )
2014-06-05 19:52:14 +03:00
{
if ( ! dst )
dst = this ;
int warMachinesGiven = 0 ;
2023-02-05 00:40:02 +02:00
std : : vector < int32_t > stacksCountChances = VLC - > modh - > settings . HERO_STARTING_ARMY_STACKS_COUNT_CHANCES ;
2023-02-05 01:36:51 +02:00
const int zeroStacksAllowingValue = - 1 ;
bool allowZeroStacksArmy = ! stacksCountChances . empty ( ) & & stacksCountChances . back ( ) = = zeroStacksAllowingValue ;
if ( allowZeroStacksArmy )
stacksCountChances . pop_back ( ) ;
2023-02-05 00:40:02 +02:00
int stacksCountInitRandomNumber = rand . nextInt ( 1 , 100 ) ;
auto stacksCountElementIndex = vstd : : find_pos_if ( stacksCountChances , [ stacksCountInitRandomNumber ] ( int element ) { return stacksCountInitRandomNumber < element ; } ) ;
if ( stacksCountElementIndex = = - 1 )
stacksCountElementIndex = stacksCountChances . size ( ) ;
2014-06-05 19:52:14 +03:00
2023-02-09 20:20:41 +02:00
int howManyStacks = stacksCountElementIndex ;
2023-02-05 01:36:51 +02:00
if ( ! allowZeroStacksArmy )
howManyStacks + + ;
2014-06-05 19:52:14 +03:00
vstd : : amin ( howManyStacks , type - > initialArmy . size ( ) ) ;
for ( int stackNo = 0 ; stackNo < howManyStacks ; stackNo + + )
{
auto & stack = type - > initialArmy [ stackNo ] ;
2016-08-23 07:13:52 +02:00
int count = rand . nextInt ( stack . minAmount , stack . maxAmount ) ;
2014-06-05 19:52:14 +03:00
2017-05-26 18:51:45 +02:00
const CCreature * creature = stack . creature . toCreature ( ) ;
if ( creature = = nullptr )
{
2023-01-02 13:27:03 +02:00
logGlobal - > error ( " Hero %s has invalid creature with id %d in initial army " , getNameTranslated ( ) , stack . creature . toEnum ( ) ) ;
2017-05-26 18:51:45 +02:00
continue ;
}
if ( creature - > warMachine ! = ArtifactID : : NONE ) //war machine
2014-06-05 19:52:14 +03:00
{
warMachinesGiven + + ;
if ( dst ! = this )
continue ;
2017-05-26 18:51:45 +02:00
ArtifactID aid = creature - > warMachine ;
const CArtifact * art = aid . toArtifact ( ) ;
if ( art ! = nullptr & & ! art - > possibleSlots . at ( ArtBearer : : HERO ) . empty ( ) )
2014-06-05 19:52:14 +03:00
{
2017-05-26 18:51:45 +02:00
//TODO: should we try another possible slots?
ArtifactPosition slot = art - > possibleSlots . at ( ArtBearer : : HERO ) . front ( ) ;
if ( ! getArt ( slot ) )
putArtifact ( slot , CArtifactInstance : : createNewArtifactInstance ( aid ) ) ;
else
2023-01-02 13:27:03 +02:00
logGlobal - > warn ( " Hero %s already has artifact at %d, omitting giving artifact %d " , getNameTranslated ( ) , slot . toEnum ( ) , aid . toEnum ( ) ) ;
2014-06-05 19:52:14 +03:00
}
else
2017-05-26 18:51:45 +02:00
{
2023-01-02 13:27:03 +02:00
logGlobal - > error ( " Hero %s has invalid war machine in initial army " , getNameTranslated ( ) ) ;
2017-05-26 18:51:45 +02:00
}
2014-06-05 19:52:14 +03:00
}
else
2017-05-26 18:51:45 +02:00
{
2014-06-05 19:52:14 +03:00
dst - > setCreature ( SlotID ( stackNo - warMachinesGiven ) , stack . creature , count ) ;
2017-05-26 18:51:45 +02:00
}
2014-06-05 19:52:14 +03:00
}
}
CGHeroInstance : : ~ CGHeroInstance ( )
{
commander . dellNull ( ) ;
}
bool CGHeroInstance : : needsLastStack ( ) const
{
return true ;
}
void CGHeroInstance : : onHeroVisit ( const CGHeroInstance * h ) const
{
if ( h = = this ) return ; //exclude potential self-visiting
if ( ID = = Obj : : HERO )
{
if ( cb - > gameState ( ) - > getPlayerRelations ( tempOwner , h - > tempOwner ) ) //our or ally hero
{
//exchange
cb - > heroExchange ( h - > id , id ) ;
}
else //battle
{
if ( visitedTown ) //we're in town
visitedTown - > onHeroVisit ( h ) ; //town will handle attacking
else
cb - > startBattleI ( h , this ) ;
}
}
else if ( ID = = Obj : : PRISON )
{
int txt_id ;
2022-09-09 21:01:46 +02:00
if ( cb - > getHeroCount ( h - > tempOwner , false ) < VLC - > modh - > settings . MAX_HEROES_ON_MAP_PER_PLAYER ) //free hero slot
2014-06-05 19:52:14 +03:00
{
//update hero parameters
SetMovePoints smp ;
smp . hid = id ;
smp . val = maxMovePoints ( true ) ; //TODO: hota prison on water?
cb - > setMovePoints ( & smp ) ;
cb - > setManaPoints ( id , manaLimit ( ) ) ;
cb - > setObjProperty ( id , ObjProperty : : ID , Obj : : HERO ) ; //set ID to 34
cb - > giveHero ( id , h - > tempOwner ) ; //recreates def and adds hero to player
txt_id = 102 ;
}
else //already 8 wandering heroes
{
txt_id = 103 ;
}
2023-03-08 00:32:21 +02:00
h - > showInfoDialog ( txt_id ) ;
2014-06-05 19:52:14 +03:00
}
}
2014-06-24 20:39:36 +03:00
std : : string CGHeroInstance : : getObjectName ( ) const
2014-06-05 19:52:14 +03:00
{
if ( ID ! = Obj : : PRISON )
{
2014-06-24 20:39:36 +03:00
std : : string hoverName = VLC - > generaltexth - > allTexts [ 15 ] ;
2023-01-02 13:27:03 +02:00
boost : : algorithm : : replace_first ( hoverName , " %s " , getNameTranslated ( ) ) ;
boost : : algorithm : : replace_first ( hoverName , " %s " , type - > heroClass - > getNameTranslated ( ) ) ;
2014-06-05 19:52:14 +03:00
return hoverName ;
}
else
2023-01-30 19:00:51 +02:00
return VLC - > objtypeh - > getObjectName ( ID , 0 ) ;
2014-06-05 19:52:14 +03:00
}
ui8 CGHeroInstance : : maxlevelsToMagicSchool ( ) const
{
return type - > heroClass - > isMagicHero ( ) ? 3 : 4 ;
}
ui8 CGHeroInstance : : maxlevelsToWisdom ( ) const
{
return type - > heroClass - > isMagicHero ( ) ? 3 : 6 ;
}
2023-02-12 22:39:17 +02:00
CGHeroInstance : : SecondarySkillsInfo : : SecondarySkillsInfo ( ) :
magicSchoolCounter ( 1 ) ,
wisdomCounter ( 1 )
2016-08-18 17:53:28 +02:00
{
2016-08-19 22:31:54 +02:00
rand . setSeed ( 0 ) ;
2016-08-18 17:53:28 +02:00
}
2014-06-05 19:52:14 +03:00
void CGHeroInstance : : SecondarySkillsInfo : : resetMagicSchoolCounter ( )
{
magicSchoolCounter = 1 ;
}
void CGHeroInstance : : SecondarySkillsInfo : : resetWisdomCounter ( )
{
wisdomCounter = 1 ;
}
2016-09-09 19:30:36 +02:00
void CGHeroInstance : : initObj ( CRandomGenerator & rand )
2014-06-05 19:52:14 +03:00
{
blockVisit = true ;
if ( ! type )
2016-09-09 19:30:36 +02:00
initHero ( rand ) ; //TODO: set up everything for prison before specialties are configured
2014-06-05 19:52:14 +03:00
2016-09-09 19:30:36 +02:00
skillsInfo . rand . setSeed ( rand . nextInt ( ) ) ;
2014-06-05 19:52:14 +03:00
skillsInfo . resetMagicSchoolCounter ( ) ;
skillsInfo . resetWisdomCounter ( ) ;
2014-06-28 17:19:53 +03:00
if ( ID ! = Obj : : PRISON )
{
2023-01-01 17:10:47 +02:00
auto terrain = cb - > gameState ( ) - > getTile ( visitablePos ( ) ) - > terType - > getId ( ) ;
2022-09-21 11:34:23 +02:00
auto customApp = VLC - > objtypeh - > getHandlerFor ( ID , type - > heroClass - > getIndex ( ) ) - > getOverride ( terrain , this ) ;
2014-06-28 17:19:53 +03:00
if ( customApp )
2022-09-11 15:12:35 +02:00
appearance = customApp ;
2014-06-28 17:19:53 +03:00
}
2014-06-16 19:27:26 +03:00
2017-09-10 04:10:50 +02:00
//copy active (probably growing) bonuses from hero prototype to hero object
2023-02-12 22:39:17 +02:00
for ( const std : : shared_ptr < Bonus > & b : type - > specialty )
2017-09-11 03:29:11 +02:00
addNewBonus ( b ) ;
2017-09-10 04:10:50 +02:00
for ( SSpecialtyInfo & spec : type - > specDeprecated )
2023-02-12 22:39:17 +02:00
for ( const std : : shared_ptr < Bonus > & b : SpecialtyInfoToBonuses ( spec , type - > getIndex ( ) ) )
2017-09-11 03:29:11 +02:00
addNewBonus ( b ) ;
2014-06-05 19:52:14 +03:00
//initialize bonuses
recreateSecondarySkillsBonuses ( ) ;
mana = manaLimit ( ) ; //after all bonuses are taken into account, make sure this line is the last one
}
void CGHeroInstance : : recreateSecondarySkillsBonuses ( )
{
2020-11-11 21:43:40 +02:00
auto secondarySkillsBonuses = getBonuses ( Selector : : sourceType ( ) ( Bonus : : SECONDARY_SKILL ) ) ;
2023-02-12 22:39:17 +02:00
for ( const auto & bonus : * secondarySkillsBonuses )
2014-06-05 19:52:14 +03:00
removeBonus ( bonus ) ;
2023-02-12 22:39:17 +02:00
for ( const auto & skill_info : secSkills )
2018-02-28 21:44:05 +02:00
if ( skill_info . second > 0 )
2018-03-05 09:02:23 +02:00
updateSkillBonus ( SecondarySkill ( skill_info . first ) , skill_info . second ) ;
2014-06-05 19:52:14 +03:00
}
2023-02-12 22:39:17 +02:00
void CGHeroInstance : : updateSkillBonus ( const SecondarySkill & which , int val )
2014-06-05 19:52:14 +03:00
{
2018-02-28 21:44:05 +02:00
removeBonuses ( Selector : : source ( Bonus : : SECONDARY_SKILL , which ) ) ;
2018-03-31 07:56:40 +02:00
auto skillBonus = ( * VLC - > skillh ) [ which ] - > at ( val ) . effects ;
2023-02-12 22:39:17 +02:00
for ( const auto & b : skillBonus )
2018-02-28 21:44:05 +02:00
addNewBonus ( std : : make_shared < Bonus > ( * b ) ) ;
2014-06-05 19:52:14 +03:00
}
2018-03-05 09:02:23 +02:00
2014-06-05 19:52:14 +03:00
void CGHeroInstance : : setPropertyDer ( ui8 what , ui32 val )
{
if ( what = = ObjProperty : : PRIMARY_STACK_COUNT )
setStackCount ( SlotID ( 0 ) , val ) ;
}
double CGHeroInstance : : getFightingStrength ( ) const
{
return sqrt ( ( 1.0 + 0.05 * getPrimSkillLevel ( PrimarySkill : : ATTACK ) ) * ( 1.0 + 0.05 * getPrimSkillLevel ( PrimarySkill : : DEFENSE ) ) ) ;
}
double CGHeroInstance : : getMagicStrength ( ) const
{
return sqrt ( ( 1.0 + 0.05 * getPrimSkillLevel ( PrimarySkill : : KNOWLEDGE ) ) * ( 1.0 + 0.05 * getPrimSkillLevel ( PrimarySkill : : SPELL_POWER ) ) ) ;
}
double CGHeroInstance : : getHeroStrength ( ) const
{
return sqrt ( pow ( getFightingStrength ( ) , 2.0 ) * pow ( getMagicStrength ( ) , 2.0 ) ) ;
}
ui64 CGHeroInstance : : getTotalStrength ( ) const
{
double ret = getFightingStrength ( ) * getArmyStrength ( ) ;
2023-02-12 22:39:17 +02:00
return static_cast < ui64 > ( ret ) ;
2014-06-05 19:52:14 +03:00
}
TExpType CGHeroInstance : : calculateXp ( TExpType exp ) const
{
2023-02-12 22:39:17 +02:00
return static_cast < TExpType > ( exp * ( 100 + valOfBonuses ( Bonus : : SECONDARY_SKILL_PREMY , SecondarySkill : : LEARNING ) ) / 100.0 ) ;
2014-06-05 19:52:14 +03:00
}
2018-03-04 10:13:07 +02:00
int32_t CGHeroInstance : : getCasterUnitId ( ) const
{
return - 1 ; //TODO: special value for attacker/defender hero
}
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
int32_t CGHeroInstance : : getSpellSchoolLevel ( const spells : : Spell * spell , int32_t * outSelectedSchool ) const
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
int32_t skill = - 1 ; //skill level
2016-01-24 01:27:14 +02:00
2017-07-20 06:08:49 +02:00
spell - > forEachSchool ( [ & , this ] ( const spells : : SchoolInfo & cnf , bool & stop )
2014-11-13 16:24:30 +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
int32_t thisSchool = std : : max < int32_t > (
2017-08-27 09:37:54 +02:00
valOfBonuses ( Bonus : : SECONDARY_SKILL_PREMY , cnf . skill ) ,
2023-02-12 22:39:17 +02:00
valOfBonuses ( Bonus : : MAGIC_SCHOOL_SKILL , 1 < < ( static_cast < ui8 > ( cnf . id ) ) ) ) ; //FIXME: Bonus shouldn't be additive (Witchking Artifacts : Crown of Skies)
2016-01-24 01:27:14 +02:00
if ( thisSchool > skill )
{
skill = thisSchool ;
if ( outSelectedSchool )
2023-02-12 22:39:17 +02:00
* outSelectedSchool = static_cast < ui8 > ( cnf . id ) ;
2016-01-24 01:27:14 +02:00
}
2014-11-24 18:14:10 +02:00
} ) ;
2016-01-24 01:27:14 +02:00
2014-06-05 19:52:14 +03:00
vstd : : amax ( skill , valOfBonuses ( Bonus : : MAGIC_SCHOOL_SKILL , 0 ) ) ; //any school bonus
2017-07-20 06:08:49 +02:00
vstd : : amax ( skill , valOfBonuses ( Bonus : : SPELL , spell - > getIndex ( ) ) ) ; //given by artifact or other effect
2015-09-15 04:53:10 +02:00
2015-10-31 22:15:40 +02:00
vstd : : amax ( skill , 0 ) ; //in case we don't know any school
vstd : : amin ( skill , 3 ) ;
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 skill ;
2014-06-05 19:52:14 +03:00
}
2017-07-20 06:08:49 +02:00
int64_t CGHeroInstance : : getSpellBonus ( const spells : : Spell * spell , int64_t base , const battle : : Unit * affectedStack ) const
2015-03-18 21:22:52 +02:00
{
//applying sorcery secondary skill
2023-02-12 22:39:17 +02:00
base = static_cast < int64_t > ( base * ( 100 + valOfBonuses ( Bonus : : SECONDARY_SKILL_PREMY , SecondarySkill : : SORCERY ) ) / 100.0 ) ;
base = static_cast < int64_t > ( base * ( 100 + valOfBonuses ( Bonus : : SPELL_DAMAGE ) + valOfBonuses ( Bonus : : SPECIFIC_SPELL_DAMAGE , spell - > getIndex ( ) ) ) / 100.0 ) ;
2015-03-18 21:22:52 +02:00
2018-02-20 13:29:06 +02:00
int maxSchoolBonus = 0 ;
spell - > forEachSchool ( [ & maxSchoolBonus , this ] ( const spells : : SchoolInfo & cnf , bool & stop )
2015-03-18 21:22:52 +02:00
{
2018-02-20 13:29:06 +02:00
vstd : : amax ( maxSchoolBonus , valOfBonuses ( cnf . damagePremyBonus ) ) ;
2015-03-18 21:22:52 +02:00
} ) ;
2023-02-12 22:39:17 +02:00
base = static_cast < int64_t > ( base * ( 100 + maxSchoolBonus ) / 100.0 ) ;
2018-02-20 13:29:06 +02:00
2017-07-20 06:08:49 +02:00
if ( affectedStack & & affectedStack - > creatureLevel ( ) > 0 ) //Hero specials like Solmyr, Deemer
2023-02-12 22:39:17 +02:00
base = static_cast < int64_t > ( base * static_cast < double > ( 100 + valOfBonuses ( Bonus : : SPECIAL_SPELL_LEV , spell - > getIndex ( ) ) / affectedStack - > creatureLevel ( ) ) / 100.0 ) ;
2017-07-20 06:08:49 +02:00
return base ;
}
2015-03-18 21:22:52 +02:00
2017-07-20 06:08:49 +02:00
int64_t CGHeroInstance : : getSpecificSpellBonus ( const spells : : Spell * spell , int64_t base ) const
{
2023-02-12 22:39:17 +02:00
base = static_cast < int64_t > ( base * ( 100 + valOfBonuses ( Bonus : : SPECIFIC_SPELL_DAMAGE , spell - > getIndex ( ) ) ) / 100.0 ) ;
2016-01-24 01:27:14 +02:00
return base ;
2015-03-18 21:22:52 +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
int32_t CGHeroInstance : : getEffectLevel ( const spells : : Spell * spell ) const
2015-09-17 07:42:30 +02:00
{
2023-01-31 10:30:41 +02:00
return getSpellSchoolLevel ( spell ) ;
2015-09-17 07:42:30 +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
int32_t CGHeroInstance : : getEffectPower ( const spells : : Spell * spell ) const
2015-09-17 07:42:30 +02:00
{
return getPrimSkillLevel ( PrimarySkill : : SPELL_POWER ) ;
}
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
int32_t CGHeroInstance : : getEnchantPower ( const spells : : Spell * spell ) const
2015-09-17 07:42:30 +02:00
{
2016-01-24 01:27:14 +02:00
return getPrimSkillLevel ( PrimarySkill : : SPELL_POWER ) + valOfBonuses ( Bonus : : SPELL_DURATION ) ;
2015-09-17 07:42:30 +02:00
}
2017-07-20 06:08:49 +02:00
int64_t CGHeroInstance : : getEffectValue ( const spells : : Spell * spell ) const
2015-09-17 07:42:30 +02:00
{
return 0 ;
}
2015-03-18 21:22:52 +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 CGHeroInstance : : getCasterOwner ( ) const
2015-09-17 08:29:57 +02:00
{
return tempOwner ;
}
2016-09-10 17:23:55 +02:00
void CGHeroInstance : : getCasterName ( MetaString & text ) const
{
//FIXME: use local name, MetaString need access to gamestate as hero name is part of map object
2023-01-02 13:27:03 +02:00
text . addReplacement ( getNameTranslated ( ) ) ;
2017-07-20 06:08:49 +02:00
}
2016-09-10 17:23:55 +02:00
2017-07-20 06:08:49 +02:00
void CGHeroInstance : : getCastDescription ( const spells : : Spell * spell , const std : : vector < const battle : : Unit * > & attacked , MetaString & text ) const
2016-09-17 22:04:23 +02:00
{
const bool singleTarget = attacked . size ( ) = = 1 ;
const int textIndex = singleTarget ? 195 : 196 ;
text . addTxt ( MetaString : : GENERAL_TXT , textIndex ) ;
getCasterName ( text ) ;
2017-07-20 06:08:49 +02:00
text . addReplacement ( MetaString : : SPELL_NAME , spell - > getIndex ( ) ) ;
2016-09-17 22:04:23 +02:00
if ( singleTarget )
2017-07-20 06:08:49 +02:00
attacked . at ( 0 ) - > addNameReplacement ( text , true ) ;
}
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 CGHeroInstance : : spendMana ( ServerCallback * server , const int spellCost ) const
2017-07-20 06:08:49 +02:00
{
if ( spellCost ! = 0 )
{
SetMana sm ;
sm . absolute = false ;
sm . hid = id ;
sm . val = - spellCost ;
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
server - > apply ( & sm ) ;
2017-07-20 06:08:49 +02:00
}
2016-09-17 22:04:23 +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
bool CGHeroInstance : : canCastThisSpell ( const spells : : Spell * spell ) const
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
const bool isAllowed = IObjectInterface : : cb - > isAllowed ( 0 , spell - > getIndex ( ) ) ;
2015-09-16 03:39:44 +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
const bool inSpellBook = vstd : : contains ( spells , spell - > getId ( ) ) & & hasSpellbook ( ) ;
const bool specificBonus = hasBonusOfType ( Bonus : : SPELL , spell - > getIndex ( ) ) ;
2015-09-16 03:39:44 +02:00
bool schoolBonus = false ;
2017-07-20 06:08:49 +02:00
spell - > forEachSchool ( [ this , & schoolBonus ] ( const spells : : SchoolInfo & cnf , bool & stop )
2015-09-16 03:39:44 +02:00
{
if ( hasBonusOfType ( cnf . knoledgeBonus ) )
{
schoolBonus = stop = true ;
}
} ) ;
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 bool levelBonus = hasBonusOfType ( Bonus : : SPELLS_OF_LEVEL , spell - > getLevel ( ) ) ;
2015-09-16 03:39:44 +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
if ( spell - > isSpecial ( ) )
2016-03-12 03:41:27 +02:00
{
if ( inSpellBook )
{ //hero has this spell in spellbook
2023-01-02 00:06:42 +02:00
logGlobal - > error ( " Special spell %s in spellbook. " , spell - > getNameTranslated ( ) ) ;
2016-03-12 03:41:27 +02:00
}
return specificBonus ;
}
else if ( ! isAllowed )
{
if ( inSpellBook )
2016-11-02 13:52:02 +02:00
{
//hero has this spell in spellbook
//it is normal if set in map editor, but trace it to possible debug of magic guild
2023-01-02 00:06:42 +02:00
logGlobal - > trace ( " Banned spell %s in spellbook. " , spell - > getNameTranslated ( ) ) ;
2016-03-12 03:41:27 +02:00
}
2016-11-02 13:52:02 +02:00
return inSpellBook | | specificBonus | | schoolBonus | | levelBonus ;
2016-03-12 03:41:27 +02:00
}
else
{
2015-09-16 03:39:44 +02:00
return inSpellBook | | schoolBonus | | specificBonus | | levelBonus ;
2016-03-12 03:41:27 +02:00
}
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
bool CGHeroInstance : : canLearnSpell ( const spells : : Spell * spell ) const
2016-10-09 12:38:54 +02:00
{
if ( ! hasSpellbook ( ) )
return false ;
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 ( spell - > getLevel ( ) > maxSpellLevel ( ) ) //not enough wisdom
2016-10-09 12:38:54 +02:00
return false ;
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 ( vstd : : contains ( spells , spell - > getId ( ) ) ) //already known
2016-10-09 12:38:54 +02:00
return false ;
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 ( spell - > isSpecial ( ) )
2016-10-09 12:38:54 +02:00
{
2023-01-02 00:06:42 +02:00
logGlobal - > warn ( " Hero %s try to learn special spell %s " , nodeName ( ) , spell - > getNameTranslated ( ) ) ;
2016-10-09 12:38:54 +02:00
return false ; //special spells can not be learned
}
if ( spell - > isCreatureAbility ( ) )
{
2023-01-02 00:06:42 +02:00
logGlobal - > warn ( " Hero %s try to learn creature spell %s " , nodeName ( ) , spell - > getNameTranslated ( ) ) ;
2016-10-09 12:38:54 +02:00
return false ; //creature abilities can not be learned
}
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 ( ! IObjectInterface : : cb - > isAllowed ( 0 , spell - > getIndex ( ) ) )
2016-10-09 12:38:54 +02:00
{
2023-01-02 00:06:42 +02:00
logGlobal - > warn ( " Hero %s try to learn banned spell %s " , nodeName ( ) , spell - > getNameTranslated ( ) ) ;
2016-10-09 12:38:54 +02:00
return false ; //banned spells should not be learned
}
return true ;
}
2014-06-05 19:52:14 +03:00
/**
* Calculates what creatures and how many to be raised from a battle .
* @ param battleResult The results of the battle .
* @ return Returns a pair with the first value indicating the ID of the creature
* type and second value the amount . Both values are returned as - 1 if necromancy
* could not be applied .
*/
CStackBasicDescriptor CGHeroInstance : : calculateNecromancy ( const BattleResult & battleResult ) const
{
const ui8 necromancyLevel = getSecSkillLevel ( SecondarySkill : : NECROMANCY ) ;
2018-03-17 10:46:16 +02:00
// need skill or cloak of undead king - lesser artifacts don't work without skill
2014-06-05 19:52:14 +03:00
if ( necromancyLevel > 0 | | hasBonusOfType ( Bonus : : IMPROVED_NECROMANCY ) )
{
2018-03-17 10:46:16 +02:00
double necromancySkill = valOfBonuses ( Bonus : : SECONDARY_SKILL_PREMY , SecondarySkill : : NECROMANCY ) / 100.0 ;
2014-06-05 19:52:14 +03:00
vstd : : amin ( necromancySkill , 1.0 ) ; //it's impossible to raise more creatures than all...
const std : : map < ui32 , si32 > & casualties = battleResult . casualties [ ! battleResult . winner ] ;
2018-03-17 10:46:16 +02:00
// figure out what to raise - pick strongest creature meeting requirements
CreatureID creatureTypeRaised = CreatureID : : SKELETON ;
int requiredCasualtyLevel = 1 ;
2020-11-11 21:43:40 +02:00
TConstBonusListPtr improvedNecromancy = getBonuses ( Selector : : type ( ) ( Bonus : : IMPROVED_NECROMANCY ) ) ;
2018-03-17 10:46:16 +02:00
if ( ! improvedNecromancy - > empty ( ) )
2014-06-05 19:52:14 +03:00
{
2023-02-12 22:39:17 +02:00
auto getCreatureID = [ necromancyLevel ] ( const std : : shared_ptr < Bonus > & bonus ) - > CreatureID
2018-03-17 10:46:16 +02:00
{
const CreatureID legacyTypes [ ] = { CreatureID : : SKELETON , CreatureID : : WALKING_DEAD , CreatureID : : WIGHTS , CreatureID : : LICHES } ;
return CreatureID ( bonus - > subtype > = 0 ? bonus - > subtype : legacyTypes [ necromancyLevel ] ) ;
} ;
int maxCasualtyLevel = 1 ;
2023-02-12 22:39:17 +02:00
for ( const auto & casualty : casualties )
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
vstd : : amax ( maxCasualtyLevel , VLC - > creh - > objects [ casualty . first ] - > level ) ;
2018-03-17 10:46:16 +02:00
// pick best bonus available
std : : shared_ptr < Bonus > topPick ;
2023-02-12 22:39:17 +02:00
for ( const std : : shared_ptr < Bonus > & newPick : * improvedNecromancy )
2018-03-17 10:46:16 +02:00
{
// addInfo[0] = required necromancy skill, addInfo[1] = required casualty level
if ( newPick - > additionalInfo [ 0 ] > necromancyLevel | | newPick - > additionalInfo [ 1 ] > maxCasualtyLevel )
continue ;
if ( ! topPick )
{
topPick = newPick ;
}
else
{
2023-02-12 22:39:17 +02:00
auto quality = [ getCreatureID ] ( const std : : shared_ptr < Bonus > & pick ) - > std : : tuple < int , int , int >
2018-03-17 10:46:16 +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
const CCreature * c = VLC - > creh - > objects [ getCreatureID ( pick ) ] ;
2022-09-25 08:50:31 +02:00
return std : : tuple < int , int , int > { c - > level , static_cast < int > ( c - > cost . marketValue ( ) ) , - pick - > additionalInfo [ 1 ] } ;
2018-03-17 10:46:16 +02:00
} ;
if ( quality ( topPick ) < quality ( newPick ) )
topPick = newPick ;
}
}
if ( topPick )
{
creatureTypeRaised = getCreatureID ( topPick ) ;
requiredCasualtyLevel = std : : max ( topPick - > additionalInfo [ 1 ] , 1 ) ;
}
2014-06-05 19:52:14 +03:00
}
2018-03-17 10:46:16 +02:00
// raise upgraded creature (at 2/3 rate) if no space available otherwise
if ( getSlotFor ( creatureTypeRaised ) = = SlotID ( ) )
2014-06-05 19:52:14 +03:00
{
2023-02-12 22:39:17 +02:00
for ( const CreatureID & upgraded : VLC - > creh - > objects [ creatureTypeRaised ] - > upgrades )
2018-03-17 10:46:16 +02:00
{
if ( getSlotFor ( upgraded ) ! = SlotID ( ) )
{
creatureTypeRaised = upgraded ;
necromancySkill * = 2 / 3.0 ;
break ;
}
}
2014-06-05 19:52:14 +03:00
}
2018-03-17 10:46:16 +02:00
// calculate number of creatures raised - low level units contribute at 50% rate
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 double raisedUnitHealth = VLC - > creh - > objects [ creatureTypeRaised ] - > MaxHealth ( ) ;
2018-03-17 10:46:16 +02:00
double raisedUnits = 0 ;
2023-02-12 22:39:17 +02:00
for ( const auto & casualty : casualties )
2018-03-17 10:46:16 +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
const CCreature * c = VLC - > creh - > objects [ casualty . first ] ;
2018-03-17 10:46:16 +02:00
double raisedFromCasualty = std : : min ( c - > MaxHealth ( ) / raisedUnitHealth , 1.0 ) * casualty . second * necromancySkill ;
if ( c - > level < requiredCasualtyLevel )
raisedFromCasualty * = 0.5 ;
raisedUnits + = raisedFromCasualty ;
}
return CStackBasicDescriptor ( creatureTypeRaised , std : : max ( static_cast < int > ( raisedUnits ) , 1 ) ) ;
2014-06-05 19:52:14 +03:00
}
return CStackBasicDescriptor ( ) ;
}
/**
* Show the necromancy dialog with information about units raised .
* @ param raisedStack Pair where the first element represents ID of the raised creature
* and the second element the amount .
*/
2016-09-09 19:30:36 +02:00
void CGHeroInstance : : showNecromancyDialog ( const CStackBasicDescriptor & raisedStack , CRandomGenerator & rand ) const
2014-06-05 19:52:14 +03:00
{
InfoWindow iw ;
2023-03-07 03:09:19 +02:00
iw . type = EInfoWindowMode : : AUTO ;
2016-09-09 19:30:36 +02:00
iw . soundID = soundBase : : pickup01 + rand . nextInt ( 6 ) ;
2014-06-05 19:52:14 +03:00
iw . player = tempOwner ;
2023-02-12 22:39:17 +02:00
iw . components . emplace_back ( raisedStack ) ;
2014-06-05 19:52:14 +03:00
if ( raisedStack . count > 1 ) // Practicing the dark arts of necromancy, ... (plural)
{
iw . text . addTxt ( MetaString : : GENERAL_TXT , 145 ) ;
iw . text . addReplacement ( raisedStack . count ) ;
}
else // Practicing the dark arts of necromancy, ... (singular)
{
iw . text . addTxt ( MetaString : : GENERAL_TXT , 146 ) ;
}
iw . text . addReplacement ( raisedStack ) ;
cb - > showInfoDialog ( & iw ) ;
}
2014-06-24 02:26:36 +03:00
/*
2014-06-05 19:52:14 +03:00
int3 CGHeroInstance : : getSightCenter ( ) const
{
return getPosition ( false ) ;
2014-06-24 02:26:36 +03:00
} */
2014-06-05 19:52:14 +03:00
2016-01-31 17:01:58 +02:00
int CGHeroInstance : : getSightRadius ( ) const
2014-06-05 19:52:14 +03:00
{
2023-02-19 13:40:28 +02:00
return 5 + valOfBonuses ( Bonus : : SIGHT_RADIUS ) ; // scouting gives SIGHT_RADIUS bonus
2014-06-05 19:52:14 +03:00
}
si32 CGHeroInstance : : manaRegain ( ) const
{
if ( hasBonusOfType ( Bonus : : FULL_MANA_REGENERATION ) )
return manaLimit ( ) ;
2017-08-26 11:16:05 +02:00
return 1 + valOfBonuses ( Bonus : : SECONDARY_SKILL_PREMY , SecondarySkill : : MYSTICISM ) + valOfBonuses ( Bonus : : MANA_REGENERATION ) ; //1 + Mysticism level
2014-06-05 19:52:14 +03:00
}
2016-01-30 09:20:49 +02:00
si32 CGHeroInstance : : getManaNewTurn ( ) const
{
if ( visitedTown & & visitedTown - > hasBuilt ( BuildingID : : MAGES_GUILD_1 ) )
{
//if hero starts turn in town with mage guild - restore all mana
return std : : max ( mana , manaLimit ( ) ) ;
}
si32 res = mana + manaRegain ( ) ;
res = std : : min ( res , manaLimit ( ) ) ;
res = std : : max ( res , mana ) ;
res = std : : max ( res , 0 ) ;
return res ;
}
2014-06-05 19:52:14 +03:00
// /**
// * Places an artifact in hero's backpack. If it's a big artifact equips it
// * or discards it if it cannot be equipped.
// */
// void CGHeroInstance::giveArtifact (ui32 aid) //use only for fixed artifacts
// {
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
// CArtifact * const artifact = VLC->arth->objects[aid]; //pointer to constant object
2014-06-05 19:52:14 +03:00
// CArtifactInstance *ai = CArtifactInstance::createNewArtifactInstance(artifact);
// ai->putAt(this, ai->firstAvailableSlot(this));
// }
int CGHeroInstance : : getBoatType ( ) const
{
switch ( type - > heroClass - > getAlignment ( ) )
{
case EAlignment : : GOOD :
return 1 ;
case EAlignment : : EVIL :
return 0 ;
case EAlignment : : NEUTRAL :
return 2 ;
default :
throw std : : runtime_error ( " Wrong alignment! " ) ;
}
}
void CGHeroInstance : : getOutOffsets ( std : : vector < int3 > & offsets ) const
{
2015-11-26 09:55:02 +02:00
// FIXME: Offsets need to be fixed once we get rid of convertPosition
// Check issue 515 for details
2016-01-24 01:27:14 +02:00
offsets =
{
2015-11-26 09:55:02 +02:00
int3 ( - 1 , 1 , 0 ) , int3 ( - 1 , - 1 , 0 ) , int3 ( - 2 , 0 , 0 ) , int3 ( 0 , 0 , 0 ) , int3 ( 0 , 1 , 0 ) , int3 ( - 2 , 1 , 0 ) , int3 ( 0 , - 1 , 0 ) , int3 ( - 2 , - 1 , 0 )
2014-10-03 23:34:13 +03:00
} ;
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
int32_t CGHeroInstance : : getSpellCost ( const spells : : Spell * sp ) const
2014-06-05 19:52:14 +03:00
{
return sp - > getCost ( getSpellSchoolLevel ( sp ) ) ;
}
void CGHeroInstance : : pushPrimSkill ( PrimarySkill : : PrimarySkill which , int val )
{
assert ( ! hasBonus ( Selector : : typeSubtype ( Bonus : : PRIMARY_SKILL , which )
2020-11-11 21:43:40 +02:00
. And ( Selector : : sourceType ( ) ( Bonus : : HERO_BASE_SKILL ) ) ) ) ;
2016-09-19 23:36:35 +02:00
addNewBonus ( std : : make_shared < Bonus > ( Bonus : : PERMANENT , Bonus : : PRIMARY_SKILL , Bonus : : HERO_BASE_SKILL , val , id . getNum ( ) , which ) ) ;
2014-06-05 19:52:14 +03:00
}
EAlignment : : EAlignment CGHeroInstance : : getAlignment ( ) const
{
return type - > heroClass - > getAlignment ( ) ;
}
2016-09-09 19:30:36 +02:00
void CGHeroInstance : : initExp ( CRandomGenerator & rand )
2014-06-05 19:52:14 +03:00
{
2016-09-09 19:30:36 +02:00
exp = rand . nextInt ( 40 , 89 ) ;
2014-06-05 19:52:14 +03:00
}
std : : string CGHeroInstance : : nodeName ( ) const
{
2023-03-14 19:55:08 +02:00
return " Hero " + getNameTextID ( ) ;
2023-01-02 13:27:03 +02:00
}
std : : string CGHeroInstance : : getNameTranslated ( ) const
{
2023-02-24 13:40:06 +02:00
if ( ! nameCustom . empty ( ) )
return nameCustom ;
2023-01-02 13:27:03 +02:00
return VLC - > generaltexth - > translate ( getNameTextID ( ) ) ;
}
std : : string CGHeroInstance : : getNameTextID ( ) const
{
if ( ! nameCustom . empty ( ) )
return nameCustom ;
if ( type )
return type - > getNameTextID ( ) ;
2023-01-21 15:57:25 +02:00
// FIXME: called by logging from some specialties (mods?) before type is set on deserialization
// assert(0);
2023-01-02 13:27:03 +02:00
return " " ;
}
std : : string CGHeroInstance : : getBiographyTranslated ( ) const
{
2023-02-24 13:40:06 +02:00
if ( ! biographyCustom . empty ( ) )
return biographyCustom ;
2023-01-02 13:27:03 +02:00
return VLC - > generaltexth - > translate ( getBiographyTextID ( ) ) ;
}
std : : string CGHeroInstance : : getBiographyTextID ( ) const
{
if ( ! biographyCustom . empty ( ) )
return biographyCustom ;
if ( type )
return type - > getBiographyTextID ( ) ;
assert ( 0 ) ;
return " " ;
2014-06-05 19:52:14 +03:00
}
void CGHeroInstance : : putArtifact ( ArtifactPosition pos , CArtifactInstance * art )
{
assert ( ! getArt ( pos ) ) ;
art - > putAt ( ArtifactLocation ( this , pos ) ) ;
}
void CGHeroInstance : : putInBackpack ( CArtifactInstance * art )
{
putArtifact ( art - > firstBackpackSlot ( this ) , art ) ;
}
bool CGHeroInstance : : hasSpellbook ( ) const
{
return getArt ( ArtifactPosition : : SPELLBOOK ) ;
}
2023-02-12 22:39:17 +02:00
void CGHeroInstance : : addSpellToSpellbook ( const SpellID & spell )
2018-12-20 23:42:31 +02:00
{
spells . insert ( spell ) ;
}
2023-02-12 22:39:17 +02:00
void CGHeroInstance : : removeSpellFromSpellbook ( const SpellID & spell )
2018-12-20 23:42:31 +02:00
{
spells . erase ( spell ) ;
}
2023-02-12 22:39:17 +02:00
bool CGHeroInstance : : spellbookContainsSpell ( const SpellID & spell ) const
2018-12-20 23:42:31 +02:00
{
return vstd : : contains ( spells , spell ) ;
}
void CGHeroInstance : : removeSpellbook ( )
{
spells . clear ( ) ;
if ( hasSpellbook ( ) )
{
ArtifactLocation ( this , ArtifactPosition ( ArtifactPosition : : SPELLBOOK ) ) . removeArtifact ( ) ;
}
}
const std : : set < SpellID > & CGHeroInstance : : getSpellsInSpellbook ( ) const
{
return spells ;
}
2017-08-26 10:49:29 +02:00
int CGHeroInstance : : maxSpellLevel ( ) const
{
return std : : min ( GameConstants : : SPELL_LEVELS , 2 + valOfBonuses ( Selector : : typeSubtype ( Bonus : : SECONDARY_SKILL_PREMY , SecondarySkill : : WISDOM ) ) ) ;
}
2014-06-05 19:52:14 +03:00
void CGHeroInstance : : deserializationFix ( )
{
artDeserializationFix ( this ) ;
}
2021-03-23 16:47:07 +02:00
CBonusSystemNode * CGHeroInstance : : whereShouldBeAttachedOnSiege ( const bool isBattleOutsideTown ) const
{
if ( ! visitedTown )
return nullptr ;
return isBattleOutsideTown ? ( CBonusSystemNode * ) ( & visitedTown - > townAndVis )
: ( CBonusSystemNode * ) ( visitedTown . get ( ) ) ;
}
CBonusSystemNode * CGHeroInstance : : whereShouldBeAttachedOnSiege ( CGameState * gs )
2014-06-05 19:52:14 +03:00
{
if ( visitedTown )
2021-03-23 16:47:07 +02:00
return whereShouldBeAttachedOnSiege ( visitedTown - > isBattleOutsideTown ( this ) ) ;
2022-11-06 01:26:13 +02:00
return & CArmedInstance : : whereShouldBeAttached ( gs ) ;
2021-03-23 16:47:07 +02:00
}
2022-11-06 01:26:13 +02:00
CBonusSystemNode & CGHeroInstance : : whereShouldBeAttached ( CGameState * gs )
2021-03-23 16:47:07 +02:00
{
2021-08-19 00:45:28 +02:00
if ( visitedTown )
2014-06-05 19:52:14 +03:00
{
2021-08-19 00:45:28 +02:00
if ( inTownGarrison )
2022-11-06 01:26:13 +02:00
return * visitedTown ;
2014-06-05 19:52:14 +03:00
else
2022-11-06 01:26:13 +02:00
return visitedTown - > townAndVis ;
2014-06-05 19:52:14 +03:00
}
else
return CArmedInstance : : whereShouldBeAttached ( gs ) ;
}
2017-07-15 13:08:20 +02:00
int CGHeroInstance : : movementPointsAfterEmbark ( int MPsBefore , int basicCost , bool disembark , const TurnInfo * ti ) const
2014-06-05 19:52:14 +03:00
{
2016-11-25 14:34:38 +02:00
int ret = 0 ; //take all MPs by default
2016-08-16 14:47:21 +02:00
bool localTi = false ;
2015-11-12 13:04:33 +02:00
if ( ! ti )
2016-08-16 14:47:21 +02:00
{
localTi = true ;
2015-11-12 13:04:33 +02:00
ti = new TurnInfo ( this ) ;
2016-08-16 14:47:21 +02:00
}
2015-11-12 13:04:33 +02:00
2015-11-21 11:46:59 +02:00
int mp1 = ti - > getMaxMovePoints ( disembark ? EPathfindingLayer : : LAND : EPathfindingLayer : : SAIL ) ;
int mp2 = ti - > getMaxMovePoints ( disembark ? EPathfindingLayer : : SAIL : EPathfindingLayer : : LAND ) ;
2015-11-12 13:04:33 +02:00
if ( ti - > hasBonusOfType ( Bonus : : FREE_SHIP_BOARDING ) )
2020-10-01 10:38:06 +02:00
ret = static_cast < int > ( ( MPsBefore - basicCost ) * static_cast < double > ( mp1 ) / mp2 ) ;
2014-06-05 19:52:14 +03:00
2016-08-16 14:47:21 +02:00
if ( localTi )
delete ti ;
2016-11-25 14:34:38 +02:00
return ret ;
2014-06-05 19:52:14 +03:00
}
2015-11-29 11:32:06 +02:00
EDiggingStatus CGHeroInstance : : diggingStatus ( ) const
2014-06-05 19:52:14 +03:00
{
2023-02-12 22:39:17 +02:00
if ( static_cast < int > ( movement ) < maxMovePoints ( true ) )
2015-11-29 11:32:06 +02:00
return EDiggingStatus : : LACK_OF_MOVEMENT ;
2023-02-22 17:24:42 +02:00
return cb - > getTileDigStatus ( visitablePos ( ) ) ;
2014-06-05 19:52:14 +03:00
}
ArtBearer : : ArtBearer CGHeroInstance : : bearerType ( ) const
{
return ArtBearer : : HERO ;
}
std : : vector < SecondarySkill > CGHeroInstance : : getLevelUpProposedSecondarySkills ( ) const
{
std : : vector < SecondarySkill > obligatorySkills ; //hero is offered magic school or wisdom if possible
2023-02-27 11:42:57 +02:00
auto getObligatorySkills = [ ] ( CSkill : : Obligatory obl ) {
std : : vector < SecondarySkill > obligatory = { } ;
for ( int i = 0 ; i < VLC - > skillh - > size ( ) ; i + + )
if ( ( * VLC - > skillh ) [ SecondarySkill ( i ) ] - > obligatory ( obl ) )
{
obligatory . emplace_back ( i ) ;
break ;
}
return obligatory ;
} ;
auto selectObligatorySkill = [ & ] ( std : : vector < SecondarySkill > & ss ) - > void
{
2014-06-05 19:52:14 +03:00
std : : shuffle ( ss . begin ( ) , ss . end ( ) , skillsInfo . rand . getStdGenerator ( ) ) ;
2023-02-12 22:39:17 +02:00
for ( const auto & skill : ss )
2014-06-05 19:52:14 +03:00
{
2023-02-27 11:42:57 +02:00
if ( canLearnSkill ( skill ) ) //only skills hero doesn't know yet
2014-06-05 19:52:14 +03:00
{
obligatorySkills . push_back ( skill ) ;
break ; //only one
}
}
2023-02-27 11:42:57 +02:00
} ;
if ( ! skillsInfo . wisdomCounter )
{
auto obligatory = getObligatorySkills ( CSkill : : Obligatory : : MAJOR ) ;
selectObligatorySkill ( obligatory ) ;
}
if ( ! skillsInfo . magicSchoolCounter )
{
auto obligatory = getObligatorySkills ( CSkill : : Obligatory : : MINOR ) ;
selectObligatorySkill ( obligatory ) ;
2014-06-05 19:52:14 +03:00
}
std : : vector < SecondarySkill > skills ;
//picking sec. skills for choice
2023-02-12 22:39:17 +02:00
std : : set < SecondarySkill > basicAndAdv ;
std : : set < SecondarySkill > expert ;
std : : set < SecondarySkill > none ;
2018-03-31 07:56:40 +02:00
for ( int i = 0 ; i < VLC - > skillh - > size ( ) ; i + + )
2022-11-14 19:08:49 +02:00
if ( canLearnSkill ( SecondarySkill ( i ) ) )
2014-06-05 19:52:14 +03:00
none . insert ( SecondarySkill ( i ) ) ;
2023-02-12 22:39:17 +02:00
for ( const auto & elem : secSkills )
2014-06-05 19:52:14 +03:00
{
if ( elem . second < SecSkillLevel : : EXPERT )
basicAndAdv . insert ( elem . first ) ;
else
expert . insert ( elem . first ) ;
none . erase ( elem . first ) ;
}
2023-02-12 22:39:17 +02:00
for ( const auto & s : obligatorySkills ) //don't duplicate them
2014-06-05 19:52:14 +03:00
{
none . erase ( s ) ;
basicAndAdv . erase ( s ) ;
expert . erase ( s ) ;
}
//first offered skill:
// 1) give obligatory skill
// 2) give any other new skill
// 3) upgrade existing
2023-02-12 22:39:17 +02:00
if ( canLearnSkill ( ) & & ! obligatorySkills . empty ( ) )
2014-06-05 19:52:14 +03:00
{
skills . push_back ( obligatorySkills [ 0 ] ) ;
}
2023-02-12 22:39:17 +02:00
else if ( ! none . empty ( ) & & canLearnSkill ( ) ) //hero have free skill slot
2014-06-05 19:52:14 +03:00
{
skills . push_back ( type - > heroClass - > chooseSecSkill ( none , skillsInfo . rand ) ) ; //new skill
none . erase ( skills . back ( ) ) ;
}
else if ( ! basicAndAdv . empty ( ) )
{
skills . push_back ( type - > heroClass - > chooseSecSkill ( basicAndAdv , skillsInfo . rand ) ) ; //upgrade existing
basicAndAdv . erase ( skills . back ( ) ) ;
}
//second offered skill:
//1) upgrade existing
//2) give obligatory skill
//3) give any other new skill
if ( ! basicAndAdv . empty ( ) )
{
SecondarySkill s = type - > heroClass - > chooseSecSkill ( basicAndAdv , skillsInfo . rand ) ; //upgrade existing
skills . push_back ( s ) ;
basicAndAdv . erase ( s ) ;
}
else if ( canLearnSkill ( ) & & obligatorySkills . size ( ) > 1 )
{
skills . push_back ( obligatorySkills [ 1 ] ) ;
}
2023-02-12 22:39:17 +02:00
else if ( ! none . empty ( ) & & canLearnSkill ( ) )
2014-06-05 19:52:14 +03:00
{
skills . push_back ( type - > heroClass - > chooseSecSkill ( none , skillsInfo . rand ) ) ; //give new skill
none . erase ( skills . back ( ) ) ;
}
2014-08-19 13:15:40 +03:00
if ( skills . size ( ) = = 2 ) // Fix for #1868 to avoid changing logic (possibly causing bugs in process)
std : : swap ( skills [ 0 ] , skills [ 1 ] ) ;
2014-06-05 19:52:14 +03:00
return skills ;
}
2016-08-23 18:12:10 +02:00
PrimarySkill : : PrimarySkill CGHeroInstance : : nextPrimarySkill ( CRandomGenerator & rand ) const
2014-06-05 19:52:14 +03:00
{
assert ( gainsLevel ( ) ) ;
2023-02-12 22:39:17 +02:00
int randomValue = rand . nextInt ( 99 ) ;
int pom = 0 ;
int primarySkill = 0 ;
2021-07-28 17:54:32 +02:00
const auto isLowLevelHero = level < GameConstants : : HERO_HIGH_LEVEL ;
const auto & skillChances = isLowLevelHero ? type - > heroClass - > primarySkillLowLevel : type - > heroClass - > primarySkillHighLevel ;
2014-06-05 19:52:14 +03:00
for ( ; primarySkill < GameConstants : : PRIMARY_SKILLS ; + + primarySkill )
{
pom + = skillChances [ primarySkill ] ;
if ( randomValue < pom )
{
break ;
}
}
2021-07-28 17:54:32 +02:00
if ( primarySkill > = GameConstants : : PRIMARY_SKILLS )
{
primarySkill = rand . nextInt ( GameConstants : : PRIMARY_SKILLS - 1 ) ;
2023-01-02 13:27:03 +02:00
logGlobal - > error ( " Wrong values in primarySkill%sLevel for hero class %s " , isLowLevelHero ? " Low " : " High " , type - > heroClass - > getNameTranslated ( ) ) ;
2021-07-28 17:54:32 +02:00
randomValue = 100 / GameConstants : : PRIMARY_SKILLS ;
}
2017-08-11 19:03:05 +02:00
logGlobal - > trace ( " The hero gets the primary skill %d with a probability of %d %%. " , primarySkill , randomValue ) ;
2014-06-05 19:52:14 +03:00
return static_cast < PrimarySkill : : PrimarySkill > ( primarySkill ) ;
}
2016-09-09 19:30:36 +02:00
boost : : optional < SecondarySkill > CGHeroInstance : : nextSecondarySkill ( CRandomGenerator & rand ) const
2014-06-05 19:52:14 +03:00
{
assert ( gainsLevel ( ) ) ;
boost : : optional < SecondarySkill > chosenSecondarySkill ;
const auto proposedSecondarySkills = getLevelUpProposedSecondarySkills ( ) ;
if ( ! proposedSecondarySkills . empty ( ) )
{
std : : vector < SecondarySkill > learnedSecondarySkills ;
2023-02-12 22:39:17 +02:00
for ( const auto & secondarySkill : proposedSecondarySkills )
2014-06-05 19:52:14 +03:00
{
if ( getSecSkillLevel ( secondarySkill ) > 0 )
{
learnedSecondarySkills . push_back ( secondarySkill ) ;
}
}
if ( learnedSecondarySkills . empty ( ) )
{
// there are only new skills to learn, so choose anyone of them
2017-08-05 15:09:29 +02:00
chosenSecondarySkill = boost : : make_optional ( * RandomGeneratorUtil : : nextItem ( proposedSecondarySkills , rand ) ) ;
2014-06-05 19:52:14 +03:00
}
else
{
// preferably upgrade a already learned secondary skill
2017-08-05 15:09:29 +02:00
chosenSecondarySkill = boost : : make_optional ( * RandomGeneratorUtil : : nextItem ( learnedSecondarySkills , rand ) ) ;
2014-06-05 19:52:14 +03:00
}
}
return chosenSecondarySkill ;
}
void CGHeroInstance : : setPrimarySkill ( PrimarySkill : : PrimarySkill primarySkill , si64 value , ui8 abs )
{
if ( primarySkill < PrimarySkill : : EXPERIENCE )
{
2020-11-11 21:43:40 +02:00
auto skill = getBonusLocalFirst ( Selector : : type ( ) ( Bonus : : PRIMARY_SKILL )
. And ( Selector : : subtype ( ) ( primarySkill ) )
. And ( Selector : : sourceType ( ) ( Bonus : : HERO_BASE_SKILL ) ) ) ;
2014-06-05 19:52:14 +03:00
assert ( skill ) ;
if ( abs )
{
2020-10-01 10:38:06 +02:00
skill - > val = static_cast < si32 > ( value ) ;
2014-06-05 19:52:14 +03:00
}
else
{
2020-10-01 10:38:06 +02:00
skill - > val + = static_cast < si32 > ( value ) ;
2014-06-05 19:52:14 +03:00
}
2015-12-11 15:13:18 +02:00
CBonusSystemNode : : treeHasChanged ( ) ;
2014-06-05 19:52:14 +03:00
}
else if ( primarySkill = = PrimarySkill : : EXPERIENCE )
{
if ( abs )
{
exp = value ;
}
else
{
exp + = value ;
}
}
}
bool CGHeroInstance : : gainsLevel ( ) const
{
2020-10-01 10:38:06 +02:00
return exp > = static_cast < TExpType > ( VLC - > heroh - > reqExp ( level + 1 ) ) ;
2014-06-05 19:52:14 +03:00
}
2023-02-12 22:39:17 +02:00
void CGHeroInstance : : levelUp ( const std : : vector < SecondarySkill > & skills )
2014-06-05 19:52:14 +03:00
{
+ + level ;
//deterministic secondary skills
skillsInfo . magicSchoolCounter = ( skillsInfo . magicSchoolCounter + 1 ) % maxlevelsToMagicSchool ( ) ;
skillsInfo . wisdomCounter = ( skillsInfo . wisdomCounter + 1 ) % maxlevelsToWisdom ( ) ;
2023-02-27 11:42:57 +02:00
for ( const auto & skill : skills )
2014-06-05 19:52:14 +03:00
{
2023-02-27 11:42:57 +02:00
if ( ( * VLC - > skillh ) [ skill ] - > obligatory ( CSkill : : Obligatory : : MAJOR ) )
skillsInfo . resetWisdomCounter ( ) ;
if ( ( * VLC - > skillh ) [ skill ] - > obligatory ( CSkill : : Obligatory : : MINOR ) )
2014-06-05 19:52:14 +03:00
skillsInfo . resetMagicSchoolCounter ( ) ;
}
2017-09-16 02:26:48 +02:00
//update specialty and other bonuses that scale with level
treeHasChanged ( ) ;
2014-06-05 19:52:14 +03:00
}
2016-09-09 19:30:36 +02:00
void CGHeroInstance : : levelUpAutomatically ( CRandomGenerator & rand )
2014-06-05 19:52:14 +03:00
{
while ( gainsLevel ( ) )
{
2016-09-09 19:30:36 +02:00
const auto primarySkill = nextPrimarySkill ( rand ) ;
2014-06-05 19:52:14 +03:00
setPrimarySkill ( primarySkill , 1 , false ) ;
auto proposedSecondarySkills = getLevelUpProposedSecondarySkills ( ) ;
2016-09-09 19:30:36 +02:00
const auto secondarySkill = nextSecondarySkill ( rand ) ;
2014-06-05 19:52:14 +03:00
if ( secondarySkill )
{
setSecSkillLevel ( * secondarySkill , 1 , false ) ;
}
//TODO why has the secondary skills to be passed to the method?
levelUp ( proposedSecondarySkills ) ;
}
}
2015-02-06 16:36:09 +02:00
bool CGHeroInstance : : hasVisions ( const CGObjectInstance * target , const int subtype ) const
{
//VISIONS spell support
2016-01-24 01:27:14 +02:00
const std : : string cached = boost : : to_string ( ( boost : : format ( " type_%d__subtype_%d " ) % Bonus : : VISIONS % subtype ) ) ;
2015-02-06 16:36:09 +02:00
const int visionsMultiplier = valOfBonuses ( Selector : : typeSubtype ( Bonus : : VISIONS , subtype ) , cached ) ;
2016-01-24 01:27:14 +02:00
2015-02-06 16:36:09 +02:00
int visionsRange = visionsMultiplier * getPrimSkillLevel ( PrimarySkill : : SPELL_POWER ) ;
2016-01-24 01:27:14 +02:00
if ( visionsMultiplier > 0 )
2015-02-06 16:36:09 +02:00
vstd : : amax ( visionsRange , 3 ) ; //minimum range is 3 tiles, but only if VISIONS bonus present
2016-01-24 01:27:14 +02:00
2022-12-07 22:10:08 +02:00
const int distance = static_cast < int > ( target - > pos . dist2d ( visitablePos ( ) ) ) ;
2016-01-24 01:27:14 +02:00
2015-03-28 23:17:45 +02:00
//logGlobal->debug(boost::to_string(boost::format("Visions: dist %d, mult %d, range %d") % distance % visionsMultiplier % visionsRange));
2016-01-24 01:27:14 +02:00
return ( distance < visionsRange ) & & ( target - > pos . z = = pos . z ) ;
}
2016-11-13 12:38:42 +02:00
std : : string CGHeroInstance : : getHeroTypeName ( ) const
2016-01-24 01:27:14 +02:00
{
2016-11-13 12:38:42 +02:00
if ( ID = = Obj : : HERO | | ID = = Obj : : PRISON )
2016-02-14 10:25:01 +02:00
{
2016-02-22 01:37:19 +02:00
if ( type )
{
2023-01-02 13:27:03 +02:00
return type - > getJsonKey ( ) ;
2016-02-22 01:37:19 +02:00
}
else
{
2023-01-02 13:27:03 +02:00
return VLC - > heroh - > objects [ subID ] - > getJsonKey ( ) ;
2016-02-22 01:37:19 +02:00
}
2016-02-14 10:25:01 +02:00
}
2016-11-13 12:38:42 +02:00
return " " ;
}
2017-05-28 15:23:42 +02:00
void CGHeroInstance : : afterAddToMap ( CMap * map )
{
2017-05-28 17:42:36 +02:00
if ( ID = = Obj : : HERO )
2023-02-12 22:39:17 +02:00
map - > heroesOnMap . emplace_back ( this ) ;
2017-05-28 15:23:42 +02:00
}
2022-09-17 13:04:01 +02:00
void CGHeroInstance : : afterRemoveFromMap ( CMap * map )
{
if ( ID = = Obj : : HERO )
vstd : : erase_if_present ( map - > heroesOnMap , this ) ;
}
2017-05-28 15:23:42 +02:00
2016-11-13 12:38:42 +02:00
void CGHeroInstance : : setHeroTypeName ( const std : : string & identifier )
{
if ( ID = = Obj : : HERO | | ID = = Obj : : PRISON )
2016-02-14 10:25:01 +02:00
{
2022-12-21 12:58:40 +02:00
auto rawId = VLC - > modh - > identifiers . getIdentifier ( CModHandler : : scopeMap ( ) , " hero " , identifier ) ;
2016-11-13 12:38:42 +02:00
if ( rawId )
subID = rawId . get ( ) ;
else
2022-09-09 21:02:16 +02:00
{
throw std : : runtime_error ( " Couldn't resolve hero identifier " + identifier ) ;
}
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
void CGHeroInstance : : updateFrom ( const JsonNode & data )
{
CGObjectInstance : : updateFrom ( data ) ;
}
2016-11-13 12:38:42 +02:00
void CGHeroInstance : : serializeCommonOptions ( JsonSerializeFormat & handler )
{
2023-01-02 13:27:03 +02:00
handler . serializeString ( " biography " , biographyCustom ) ;
2016-11-13 12:38:42 +02:00
handler . serializeInt ( " experience " , exp , 0 ) ;
2022-03-11 21:15:44 +02:00
2023-02-15 12:10:39 +02:00
if ( ! handler . saving & & exp ! = UNINITIALIZED_EXPERIENCE ) //do not gain levels if experience is not initialized
2022-03-11 21:15:44 +02:00
{
while ( gainsLevel ( ) )
{
+ + level ;
}
}
2023-01-02 13:27:03 +02:00
handler . serializeString ( " name " , nameCustom ) ;
2016-11-13 12:38:42 +02:00
handler . serializeBool < ui8 > ( " female " , sex , 1 , 0 , 0xFF ) ;
2016-02-14 11:13:30 +02:00
2016-11-13 12:38:42 +02:00
{
2020-10-01 10:38:06 +02:00
const int legacyHeroes = static_cast < int > ( VLC - > modh - > settings . data [ " textData " ] [ " hero " ] . Integer ( ) ) ;
2016-11-13 12:38:42 +02:00
const int moddedStart = legacyHeroes + GameConstants : : HERO_PORTRAIT_SHIFT ;
2016-02-14 10:25:01 +02:00
2016-11-13 12:38:42 +02:00
if ( handler . saving )
{
if ( portrait > = 0 )
{
if ( portrait < legacyHeroes | | portrait > = moddedStart )
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
handler . serializeId < si32 , si32 , HeroTypeID > ( " portrait " , portrait , - 1 ) ;
2016-11-13 12:38:42 +02:00
else
handler . serializeInt ( " portrait " , portrait , - 1 ) ;
}
}
else
{
const JsonNode & portraitNode = handler . getCurrent ( ) [ " portrait " ] ;
2017-11-26 23:18:18 +02:00
if ( portraitNode . getType ( ) = = JsonNode : : JsonType : : DATA_STRING )
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
handler . serializeId < si32 , si32 , HeroTypeID > ( " portrait " , portrait , - 1 ) ;
2016-02-22 01:37:19 +02:00
else
2016-11-13 12:38:42 +02:00
handler . serializeInt ( " portrait " , portrait , - 1 ) ;
2016-02-22 01:37:19 +02:00
}
}
2015-02-06 16:36:09 +02:00
2017-06-06 01:37:07 +02:00
//primary skills
if ( handler . saving )
2016-02-14 10:25:01 +02:00
{
2020-11-11 21:43:40 +02:00
const bool haveSkills = hasBonus ( Selector : : type ( ) ( Bonus : : PRIMARY_SKILL ) . And ( Selector : : sourceType ( ) ( Bonus : : HERO_BASE_SKILL ) ) ) ;
2017-06-06 01:37:07 +02:00
if ( haveSkills )
2016-11-13 12:38:42 +02:00
{
2017-06-06 01:37:07 +02:00
auto primarySkills = handler . enterStruct ( " primarySkills " ) ;
2016-11-13 12:38:42 +02:00
for ( int i = 0 ; i < GameConstants : : PRIMARY_SKILLS ; + + i )
{
2020-11-11 21:43:40 +02:00
int value = valOfBonuses ( Selector : : typeSubtype ( Bonus : : PRIMARY_SKILL , i ) . And ( Selector : : sourceType ( ) ( Bonus : : HERO_BASE_SKILL ) ) ) ;
2016-11-13 12:38:42 +02:00
2017-06-06 01:37:07 +02:00
handler . serializeInt ( PrimarySkill : : names [ i ] , value , 0 ) ;
2016-11-13 12:38:42 +02:00
}
}
2017-06-06 01:37:07 +02:00
}
else
{
auto primarySkills = handler . enterStruct ( " primarySkills " ) ;
2016-11-13 12:38:42 +02:00
2017-07-20 06:08:49 +02:00
if ( handler . getCurrent ( ) . getType ( ) = = JsonNode : : JsonType : : DATA_STRUCT )
2017-06-06 01:37:07 +02:00
{
2016-11-13 12:38:42 +02:00
for ( int i = 0 ; i < GameConstants : : PRIMARY_SKILLS ; + + i )
{
int value = 0 ;
2017-07-20 06:08:49 +02:00
handler . serializeInt ( PrimarySkill : : names [ i ] , value , 0 ) ;
2017-06-06 01:01:24 +02:00
pushPrimSkill ( static_cast < PrimarySkill : : PrimarySkill > ( i ) , value ) ;
2016-11-13 12:38:42 +02:00
}
}
2016-02-14 10:25:01 +02:00
}
2016-11-13 12:38:42 +02:00
//secondary skills
if ( handler . saving )
{
//does hero have default skills?
bool defaultSkills = false ;
bool normalSkills = false ;
for ( const auto & p : secSkills )
{
if ( p . first = = SecondarySkill ( SecondarySkill : : DEFAULT ) )
defaultSkills = true ;
else
normalSkills = true ;
}
if ( defaultSkills & & normalSkills )
logGlobal - > error ( " Mixed default and normal secondary skills " ) ;
//in json default skills means no field/null
if ( ! defaultSkills )
{
//enter structure here as handler initialize it
auto secondarySkills = handler . enterStruct ( " secondarySkills " ) ;
for ( auto & p : secSkills )
{
const si32 rawId = p . first . num ;
2018-03-31 07:56:40 +02:00
if ( rawId < 0 | | rawId > = VLC - > skillh - > size ( ) )
2017-08-11 19:03:05 +02:00
logGlobal - > error ( " Invalid secondary skill %d " , rawId ) ;
2016-11-13 12:38:42 +02:00
2023-01-01 22:20:41 +02:00
handler . serializeEnum ( ( * VLC - > skillh ) [ SecondarySkill ( rawId ) ] - > getJsonKey ( ) , p . second , 0 , NSecondarySkill : : levels ) ;
2016-11-13 12:38:42 +02:00
}
}
}
else
{
auto secondarySkills = handler . enterStruct ( " secondarySkills " ) ;
const JsonNode & skillMap = handler . getCurrent ( ) ;
secSkills . clear ( ) ;
2017-11-26 23:18:18 +02:00
if ( skillMap . getType ( ) = = JsonNode : : JsonType : : DATA_NULL )
2016-11-13 12:38:42 +02:00
{
2023-02-12 22:39:17 +02:00
secSkills . emplace_back ( SecondarySkill : : DEFAULT , - 1 ) ;
2016-11-13 12:38:42 +02:00
}
else
{
for ( const auto & p : skillMap . Struct ( ) )
{
2017-08-11 19:03:05 +02:00
const std : : string skillId = p . first ;
2016-11-13 12:38:42 +02:00
const std : : string levelId = p . second . String ( ) ;
2018-03-31 07:56:40 +02:00
const int rawId = CSkillHandler : : decodeSkill ( skillId ) ;
2016-11-13 12:38:42 +02:00
if ( rawId < 0 )
{
2017-08-11 19:03:05 +02:00
logGlobal - > error ( " Invalid secondary skill %s " , skillId ) ;
2016-11-13 12:38:42 +02:00
continue ;
}
const int level = vstd : : find_pos ( NSecondarySkill : : levels , levelId ) ;
if ( level < 0 )
{
2017-08-11 19:03:05 +02:00
logGlobal - > error ( " Invalid secondary skill level%s " , levelId ) ;
2016-11-13 12:38:42 +02:00
continue ;
}
2023-02-12 22:39:17 +02:00
secSkills . emplace_back ( SecondarySkill ( rawId ) , level ) ;
2016-11-13 12:38:42 +02:00
}
}
}
2017-07-20 06:08:49 +02:00
handler . serializeIdArray ( " spellBook " , spells ) ;
2016-11-13 12:38:42 +02:00
if ( handler . saving )
CArtifactSet : : serializeJsonArtifacts ( handler , " artifacts " , nullptr ) ;
}
void CGHeroInstance : : serializeJsonOptions ( JsonSerializeFormat & handler )
{
serializeCommonOptions ( handler ) ;
serializeJsonOwner ( handler ) ;
if ( ID = = Obj : : HERO | | ID = = Obj : : PRISON )
{
std : : string typeName ;
if ( handler . saving )
typeName = getHeroTypeName ( ) ;
handler . serializeString ( " type " , typeName ) ;
if ( ! handler . saving )
setHeroTypeName ( typeName ) ;
}
CCreatureSet : : serializeJson ( handler , " army " , 7 ) ;
handler . serializeBool < ui8 > ( " tightFormation " , formation , 1 , 0 , 0 ) ;
{
2023-02-12 22:39:17 +02:00
static constexpr int NO_PATROLING = - 1 ;
2016-11-13 12:38:42 +02:00
int rawPatrolRadius = NO_PATROLING ;
if ( handler . saving )
{
rawPatrolRadius = patrol . patrolling ? patrol . patrolRadius : NO_PATROLING ;
}
handler . serializeInt ( " patrolRadius " , rawPatrolRadius , NO_PATROLING ) ;
if ( ! handler . saving )
{
2023-02-13 21:10:11 +02:00
if ( ! appearance )
{
// crossoverDeserialize
type = VLC - > heroh - > objects [ subID ] ;
appearance = VLC - > objtypeh - > getHandlerFor ( Obj : : HERO , type - > heroClass - > getIndex ( ) ) - > getTemplates ( ) . front ( ) ;
}
2016-11-13 12:38:42 +02:00
patrol . patrolling = ( rawPatrolRadius > NO_PATROLING ) ;
2022-12-09 14:42:47 +02:00
patrol . initialPos = visitablePos ( ) ;
2016-11-13 12:38:42 +02:00
patrol . patrolRadius = ( rawPatrolRadius > NO_PATROLING ) ? rawPatrolRadius : 0 ;
}
}
}
void CGHeroInstance : : serializeJsonDefinition ( JsonSerializeFormat & handler )
{
serializeCommonOptions ( handler ) ;
2016-01-24 01:27:14 +02:00
}
2016-02-14 11:13:30 +02:00
2016-01-27 12:47:42 +02:00
bool CGHeroInstance : : isMissionCritical ( ) const
{
for ( const TriggeredEvent & event : IObjectInterface : : cb - > getMapHeader ( ) - > triggeredEvents )
{
if ( event . trigger . test ( [ & ] ( const EventCondition & condition )
{
2016-11-13 12:38:42 +02:00
if ( ( condition . condition = = EventCondition : : CONTROL | | condition . condition = = EventCondition : : HAVE_0 ) & & condition . object )
2016-01-27 12:47:42 +02:00
{
2023-02-12 22:39:17 +02:00
const auto * hero = dynamic_cast < const CGHeroInstance * > ( condition . object ) ;
2016-01-27 12:47:42 +02:00
return ( hero ! = this ) ;
}
else if ( condition . condition = = EventCondition : : IS_HUMAN )
{
return true ;
}
return false ;
} ) )
{
return true ;
}
}
return false ;
}
2022-07-26 15:07:42 +02:00
VCMI_LIB_NAMESPACE_END