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 17:58:30 +03:00
# include <vcmi/ServerCallback.h>
# include <vcmi/spells/Spell.h>
2024-01-31 00:18:10 +02:00
# include <vstd/RNG.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 17:58:30 +03:00
2024-07-20 12:55:17 +00:00
# include "../texts/CGeneralTextHandler.h"
2023-05-17 16:52:16 +03:00
# include "../ArtifactUtils.h"
2023-01-09 01:17:37 +02:00
# include "../TerrainHandler.h"
2023-01-11 15:17:24 +02:00
# include "../RoadHandler.h"
2024-08-31 11:00:36 +00:00
# include "../IGameSettings.h"
2014-06-05 23:51:24 +03:00
# include "../CSoundBase.h"
2015-02-02 11:25:26 +03:00
# include "../spells/CSpellHandler.h"
2017-08-22 00:35:46 +12:00
# include "../CSkillHandler.h"
2014-06-25 17:11:07 +03:00
# include "../IGameCallback.h"
2023-06-23 18:02:48 +03:00
# include "../gameState/CGameState.h"
2024-12-19 11:29:57 +01:00
# include "../gameState/UpgradeInfo.h"
2014-06-25 17:11:07 +03:00
# include "../CCreatureHandler.h"
2016-01-27 13:47:42 +03:00
# include "../mapping/CMap.h"
2024-01-30 23:34:27 +02:00
# include "../StartInfo.h"
2015-12-02 21:05:10 +02:00
# include "CGTownInstance.h"
2024-07-21 10:49:40 +00:00
# include "../entities/faction/CTownHandler.h"
2024-10-11 16:30:16 +00:00
# include "../entities/hero/CHeroHandler.h"
# include "../entities/hero/CHeroClass.h"
2024-08-11 20:22:35 +00:00
# include "../battle/CBattleInfoEssentials.h"
2024-01-30 23:34:27 +02:00
# include "../campaign/CampaignState.h"
2024-02-14 15:48:06 +02:00
# include "../json/JsonBonus.h"
2023-06-21 13:46:09 +03:00
# include "../pathfinder/TurnInfo.h"
2016-02-22 02:37:19 +03:00
# include "../serializer/JsonSerializeFormat.h"
2023-06-02 21:47:37 +03:00
# include "../mapObjectConstructors/AObjectTypeHandler.h"
# include "../mapObjectConstructors/CObjectClassesHandler.h"
2024-01-09 16:43:36 +02:00
# include "../mapObjects/MiscObjects.h"
2023-07-30 20:12:25 +03:00
# include "../modding/ModScope.h"
2023-10-23 13:59:15 +03:00
# include "../networkPacks/PacksForClient.h"
# include "../networkPacks/PacksForClientBattle.h"
2023-08-20 00:22:31 +03:00
# include "../constants/StringConstants.h"
2017-07-20 07:08:49 +03:00
# include "../battle/Unit.h"
2023-09-16 19:34:50 +03:00
# include "CConfigHandler.h"
2017-07-20 07:08:49 +03:00
2022-07-26 16:07:42 +03:00
VCMI_LIB_NAMESPACE_BEGIN
2024-10-29 21:41:42 +01:00
const ui32 CGHeroInstance : : NO_PATROLLING = std : : numeric_limits < ui32 > : : max ( ) ;
2023-10-16 22:24:12 +02:00
void CGHeroPlaceholder : : serializeJsonOptions ( JsonSerializeFormat & handler )
{
2023-10-24 23:58:26 +02:00
serializeJsonOwner ( handler ) ;
2023-10-16 22:24:12 +02:00
bool isHeroType = heroType . has_value ( ) ;
handler . serializeBool ( " placeholderType " , isHeroType , false ) ;
if ( ! handler . saving )
{
if ( isHeroType )
heroType = HeroTypeID : : NONE ;
else
powerRank = 0 ;
}
if ( isHeroType )
handler . serializeId ( " heroType " , heroType . value ( ) , HeroTypeID : : NONE ) ;
else
handler . serializeInt ( " powerRank " , powerRank . value ( ) ) ;
}
2023-06-21 20:38:26 +03:00
ui32 CGHeroInstance : : getTileMovementCost ( const TerrainTile & dest , const TerrainTile & from , const TurnInfo * ti ) const
2014-06-05 19:52:14 +03:00
{
2021-08-21 12:05:28 +03:00
int64_t ret = GameConstants : : BASE_MOVEMENT_COST ;
2014-06-05 19:52:14 +03:00
2023-03-12 19:01:54 +03:00
//if there is road both on dest and src tiles - use src road movement cost
2024-07-13 18:37:13 +00:00
if ( dest . hasRoad ( ) & & from . hasRoad ( ) )
2014-06-05 19:52:14 +03:00
{
2024-07-13 18:37:13 +00:00
ret = from . getRoad ( ) - > movementCost ;
2014-06-05 19:52:14 +03:00
}
2025-01-05 15:41:42 +00:00
else if ( ! ti - > hasNoTerrainPenalty ( from . getTerrainID ( ) ) ) //no special movement bonus
2014-06-05 19:52:14 +03:00
{
2024-07-13 18:37:13 +00:00
ret = VLC - > terrainTypeHandler - > getById ( from . getTerrainID ( ) ) - > moveCost ;
2024-12-28 12:02:27 +00:00
ret - = ti - > getRoughTerrainDiscountValue ( ) ;
2015-11-21 13:30:39 +03:00
if ( ret < GameConstants : : BASE_MOVEMENT_COST )
ret = GameConstants : : BASE_MOVEMENT_COST ;
}
2023-02-12 23:39:17 +03:00
return static_cast < ui32 > ( ret ) ;
2015-11-21 13:30:39 +03:00
}
2014-06-05 19:52:14 +03:00
2024-10-05 19:37:52 +00:00
FactionID CGHeroInstance : : getFactionID ( ) const
2023-04-05 18:56:28 +03:00
{
2024-10-05 19:37:52 +00:00
return getHeroClass ( ) - > faction ;
2023-04-05 18:56:28 +03:00
}
const IBonusBearer * CGHeroInstance : : getBonusBearer ( ) const
{
return this ;
}
2022-09-29 11:44:46 +02:00
TerrainId CGHeroInstance : : getNativeTerrain ( ) const
2015-11-21 13:30:39 +03: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 03:03:40 +03:00
2023-01-10 20:09:09 +02:00
TerrainId nativeTerrain = ETerrainId : : ANY_TERRAIN ;
2020-12-10 04:05:37 +03:00
2023-02-12 23:39:17 +03:00
for ( const auto & stack : stacks )
2015-11-21 13:30:39 +03:00
{
2023-04-05 18:56:28 +03:00
TerrainId stackNativeTerrain = stack . second - > getNativeTerrain ( ) ; //consider terrain bonuses e.g. Lodestar.
2015-11-21 13:30:39 +03:00
2023-01-10 20:09:09 +02:00
if ( stackNativeTerrain = = ETerrainId : : NONE )
2020-12-10 04:05:37 +03:00
continue ;
2023-01-10 20:09:09 +02:00
if ( nativeTerrain = = ETerrainId : : ANY_TERRAIN )
2015-11-21 13:30:39 +03:00
nativeTerrain = stackNativeTerrain ;
else if ( nativeTerrain ! = stackNativeTerrain )
2023-01-10 20:09:09 +02:00
return ETerrainId : : NONE ;
2015-10-15 15:17:21 +03:00
}
2015-11-21 13:30:39 +03:00
return nativeTerrain ;
2014-06-05 19:52:14 +03:00
}
2023-06-21 16:49:44 +03:00
bool CGHeroInstance : : isCoastVisitable ( ) const
{
return true ;
}
2023-12-09 14:59:09 +01:00
bool CGHeroInstance : : isBlockedVisitable ( ) const
{
return true ;
}
2022-07-09 19:00:03 +03:00
BattleField CGHeroInstance : : getBattlefield ( ) const
{
return BattleField : : NONE ;
}
2023-02-12 23:39:17 +03:00
ui8 CGHeroInstance : : getSecSkillLevel ( const SecondarySkill & skill ) const
2014-06-05 19:52:14 +03:00
{
2023-02-12 23:39:17 +03: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 23:39:17 +03: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 23:39:17 +03:00
secSkills . emplace_back ( which , val ) ;
2018-03-05 20:02:23 +13: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 15:43:41 +03: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 20:02:23 +13: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 23:39:17 +03:00
bool CGHeroInstance : : canLearnSkill ( const SecondarySkill & which ) const
2022-11-14 19:08:49 +02:00
{
if ( ! canLearnSkill ( ) )
return false ;
2023-11-05 15:24:26 +02:00
if ( ! cb - > isAllowed ( which ) )
2022-11-14 19:08:49 +02:00
return false ;
if ( getSecSkillLevel ( which ) > 0 )
return false ;
2024-10-05 19:37:52 +00:00
if ( getHeroClass ( ) - > secSkillProbability . count ( which ) = = 0 )
2023-11-02 18:45:46 +02:00
return false ;
2024-10-05 19:37:52 +00:00
if ( getHeroClass ( ) - > secSkillProbability . at ( which ) = = 0 )
2022-11-14 19:08:49 +02:00
return false ;
return true ;
}
2023-06-21 20:38:26 +03:00
int CGHeroInstance : : movementPointsRemaining ( ) const
{
return movement ;
}
void CGHeroInstance : : setMovementPoints ( int points )
{
2023-12-09 01:37:11 +01:00
if ( getBonusBearer ( ) - > hasBonusOfType ( BonusType : : UNLIMITED_MOVEMENT ) )
movement = 1000000 ;
else
movement = std : : max ( 0 , points ) ;
2023-06-21 20:38:26 +03:00
}
int CGHeroInstance : : movementPointsLimit ( bool onLand ) const
2014-06-05 19:52:14 +03:00
{
2024-12-28 12:02:27 +00:00
auto ti = getTurnInfo ( 0 ) ;
return onLand ? ti - > getMovePointsLimitLand ( ) : ti - > getMovePointsLimitWater ( ) ;
2019-01-15 08:52:55 +03:00
}
2015-11-12 14:04:33 +03:00
2023-03-11 23:09:16 +03:00
int CGHeroInstance : : getLowestCreatureSpeed ( ) const
2019-01-15 08:52:55 +03:00
{
2024-12-28 12:02:27 +00:00
if ( stacksCount ( ) ! = 0 )
2014-06-05 19:52:14 +03:00
{
2024-12-28 12:02:27 +00:00
int minimalSpeed = std : : numeric_limits < int > : : max ( ) ;
//TODO? should speed modifiers (eg from artifacts) affect hero movement?
for ( const auto & slot : Slots ( ) )
2025-01-05 15:41:42 +00:00
minimalSpeed = std : : min ( minimalSpeed , slot . second - > getInitiative ( ) ) ;
2024-12-28 12:02:27 +00:00
return minimalSpeed ;
2014-06-05 19:52:14 +03:00
}
2024-12-28 12:02:27 +00:00
else
{
if ( commander & & commander - > alive )
2025-01-05 15:41:42 +00:00
return commander - > getInitiative ( ) ;
2024-12-28 12:02:27 +00:00
}
return 10 ;
}
std : : unique_ptr < TurnInfo > CGHeroInstance : : getTurnInfo ( int days ) const
{
return std : : make_unique < TurnInfo > ( turnInfoCache . get ( ) , this , days ) ;
2023-02-18 21:01:32 +03:00
}
2014-06-05 19:52:14 +03:00
2023-06-21 20:38:26 +03:00
int CGHeroInstance : : movementPointsLimitCached ( bool onLand , const TurnInfo * ti ) const
2023-02-18 21:01:32 +03:00
{
2024-12-28 12:02:27 +00:00
if ( onLand )
return ti - > getMovePointsLimitLand ( ) ;
else
return ti - > getMovePointsLimitWater ( ) ;
2014-06-05 19:52:14 +03:00
}
2024-01-01 16:37:48 +02:00
CGHeroInstance : : CGHeroInstance ( IGameCallback * cb )
: CArmedInstance ( cb ) ,
2023-02-12 23:39:17 +03:00
tacticFormationEnabled ( false ) ,
inTownGarrison ( false ) ,
moveDir ( 4 ) ,
mana ( UNINITIALIZED_MANA ) ,
movement ( UNINITIALIZED_MOVEMENT ) ,
level ( 1 ) ,
2023-02-15 12:10:39 +02:00
exp ( UNINITIALIZED_EXPERIENCE ) ,
2023-04-02 19:56:10 +03:00
gender ( EHeroGender : : DEFAULT ) ,
2024-12-24 23:11:20 +00:00
primarySkills ( this ) ,
2024-12-25 23:04:15 +00:00
magicSchoolMastery ( this ) ,
2024-12-28 12:02:27 +00:00
turnInfoCache ( std : : make_unique < TurnInfoCache > ( this ) ) ,
manaPerKnowledgeCached ( this , Selector : : type ( ) ( BonusType : : MANA_PER_KNOWLEDGE_PERCENTAGE ) )
2014-06-05 19:52:14 +03:00
{
setNodeType ( HERO ) ;
ID = Obj : : HERO ;
2023-09-30 18:47:47 +03:00
secSkills . emplace_back ( SecondarySkill : : NONE , - 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 17:58:30 +03:00
PlayerColor CGHeroInstance : : getOwner ( ) const
{
return tempOwner ;
}
2024-10-05 19:37:52 +00:00
const CHeroClass * CGHeroInstance : : getHeroClass ( ) const
2023-10-24 17:11:25 +03:00
{
2024-10-05 19:37:52 +00:00
return getHeroType ( ) - > heroClass ;
}
HeroClassID CGHeroInstance : : getHeroClassID ( ) const
{
2024-10-11 15:01:43 +00:00
auto heroType = getHeroTypeID ( ) ;
if ( heroType . hasValue ( ) )
return getHeroType ( ) - > heroClass - > getId ( ) ;
else
return HeroClassID ( ) ;
2024-10-05 19:37:52 +00:00
}
const CHero * CGHeroInstance : : getHeroType ( ) const
{
return getHeroTypeID ( ) . toHeroType ( ) ;
}
HeroTypeID CGHeroInstance : : getHeroTypeID ( ) const
{
if ( ID = = Obj : : RANDOM_HERO )
return HeroTypeID : : NONE ;
2023-10-24 17:11:25 +03:00
return HeroTypeID ( getObjTypeIndex ( ) . getNum ( ) ) ;
}
2023-10-28 12:27:10 +03:00
void CGHeroInstance : : setHeroType ( HeroTypeID heroType )
{
subID = heroType ;
}
2024-11-03 18:50:47 +00:00
void CGHeroInstance : : initObj ( vstd : : RNG & rand )
{
2024-11-17 18:07:28 +00:00
if ( ID = = Obj : : HERO )
updateAppearance ( ) ;
2024-11-03 18:50:47 +00:00
}
2024-06-01 15:28:17 +00:00
void CGHeroInstance : : initHero ( vstd : : RNG & rand , const HeroTypeID & SUBID )
2014-06-05 19:52:14 +03:00
{
subID = SUBID . getNum ( ) ;
2016-09-09 20:30:36 +03:00
initHero ( rand ) ;
2014-06-05 19:52:14 +03:00
}
2024-10-13 13:06:31 +00:00
TObjectTypeHandler CGHeroInstance : : getObjectHandler ( ) const
{
if ( ID = = Obj : : HERO )
return VLC - > objtypeh - > getHandlerFor ( ID , getHeroClass ( ) - > getIndex ( ) ) ;
else // prison or random hero
return VLC - > objtypeh - > getHandlerFor ( ID , 0 ) ;
}
2024-11-03 18:50:47 +00:00
void CGHeroInstance : : updateAppearance ( )
{
auto handler = VLC - > objtypeh - > getHandlerFor ( Obj : : HERO , getHeroClass ( ) - > getIndex ( ) ) ; ;
2024-11-07 12:07:45 +00:00
auto terrain = cb - > gameState ( ) - > getTile ( visitablePos ( ) ) - > getTerrainID ( ) ;
2024-11-03 18:50:47 +00:00
auto app = handler - > getOverride ( terrain , this ) ;
if ( app )
appearance = app ;
}
2024-06-01 15:28:17 +00:00
void CGHeroInstance : : initHero ( vstd : : RNG & rand )
2014-06-05 19:52:14 +03:00
{
assert ( validTypes ( true ) ) ;
2024-10-05 19:37:52 +00:00
2024-11-03 18:50:47 +00:00
if ( gender = = EHeroGender : : DEFAULT )
gender = getHeroType ( ) - > gender ;
2014-06-05 19:52:14 +03:00
if ( ID = = Obj : : HERO )
2024-11-03 18:50:47 +00:00
{
auto handler = VLC - > objtypeh - > getHandlerFor ( Obj : : HERO , getHeroClass ( ) - > getIndex ( ) ) ; ;
appearance = handler - > getTemplates ( ) . front ( ) ;
}
2014-06-05 19:52:14 +03:00
2023-06-27 13:58:45 +03:00
if ( ! vstd : : contains ( spells , SpellID : : PRESET ) )
2014-06-05 19:52:14 +03:00
{
2023-06-27 13:58:45 +03:00
// hero starts with default spells
2024-10-05 19:37:52 +00:00
for ( const auto & spellID : getHeroType ( ) - > spells )
2014-06-05 19:52:14 +03:00
spells . insert ( spellID ) ;
}
else //remove placeholder
spells - = SpellID : : PRESET ;
2023-06-27 13:58:45 +03:00
if ( ! vstd : : contains ( spells , SpellID : : SPELLBOOK_PRESET ) )
{
// hero starts with default spellbook presence status
2024-10-05 19:37:52 +00:00
if ( ! getArt ( ArtifactPosition : : SPELLBOOK ) & & getHeroType ( ) - > haveSpellBook )
2023-11-13 12:09:55 +02:00
{
2024-09-04 16:25:30 +03:00
auto artifact = ArtifactUtils : : createArtifact ( ArtifactID : : SPELLBOOK ) ;
2023-11-13 12:09:55 +02:00
putArtifact ( ArtifactPosition : : SPELLBOOK , artifact ) ;
}
2023-06-27 13:58:45 +03:00
}
else
spells - = SpellID : : SPELLBOOK_PRESET ;
2014-06-05 19:52:14 +03:00
if ( ! getArt ( ArtifactPosition : : MACH4 ) )
2023-11-13 12:09:55 +02:00
{
2024-09-04 16:25:30 +03:00
auto artifact = ArtifactUtils : : createArtifact ( ArtifactID : : CATAPULT ) ;
2023-11-13 12:09:55 +02:00
putArtifact ( ArtifactPosition : : MACH4 , artifact ) ; //everyone has a catapult
}
2014-06-05 19:52:14 +03:00
2024-12-21 18:47:11 +00:00
if ( ! hasBonusFrom ( BonusSource : : HERO_BASE_SKILL ) )
2014-06-05 19:52:14 +03:00
{
for ( int g = 0 ; g < GameConstants : : PRIMARY_SKILLS ; + + g )
{
2024-10-05 19:37:52 +00:00
pushPrimSkill ( static_cast < PrimarySkill > ( g ) , getHeroClass ( ) - > primarySkillInitial [ g ] ) ;
2014-06-05 19:52:14 +03:00
}
}
2023-09-30 18:47:47 +03:00
if ( secSkills . size ( ) = = 1 & & secSkills [ 0 ] = = std : : pair < SecondarySkill , ui8 > ( SecondarySkill : : NONE , - 1 ) ) //set secondary skills to default
2024-10-05 19:37:52 +00:00
secSkills = getHeroType ( ) - > secSkillsInit ;
2014-06-05 19:52:14 +03:00
2023-11-06 18:27:16 +02:00
setFormation ( EArmyFormation : : LOOSE ) ;
2014-06-05 19:52:14 +03:00
if ( ! stacksCount ( ) ) //standard army//initial army
{
2016-09-09 20:30:36 +03: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 20:30:36 +03:00
initExp ( rand ) ;
2014-06-05 19:52:14 +03:00
}
else
{
2016-09-09 20:30:36 +03:00
levelUpAutomatically ( rand ) ;
2014-06-05 19:52:14 +03:00
}
2023-02-19 21:42:51 +03: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
2024-03-23 19:57:56 +01:00
// e.g. MANA_PER_KNOWLEDGE_PERCENTAGE for correct preview and initial state after recruit for(const auto & ob : VLC->modh->heroBaseBonuses)
2023-02-19 21:42:51 +03:00
// or MOVEMENT to compute initial movement before recruiting is finished
2024-08-31 11:00:36 +00:00
const JsonNode & baseBonuses = cb - > getSettings ( ) . getValue ( EGameSettings : : BONUSES_PER_HERO ) ;
2023-03-15 21:34:29 +02:00
for ( const auto & b : baseBonuses . Struct ( ) )
2023-02-19 21:42:51 +03:00
{
2023-03-15 21:34:29 +02:00
auto bonus = JsonUtils : : parseBonus ( b . second ) ;
2023-05-01 01:20:01 +03:00
bonus - > source = BonusSource : : HERO_BASE_SKILL ;
2023-10-21 14:50:42 +03:00
bonus - > sid = BonusSourceID ( id ) ;
2023-05-01 01:20:01 +03:00
bonus - > duration = BonusDuration : : PERMANENT ;
2023-02-19 21:42:51 +03:00
addNewBonus ( bonus ) ;
}
2024-10-05 19:37:52 +00:00
if ( cb - > getSettings ( ) . getBoolean ( EGameSettings : : MODULE_COMMANDERS ) & & ! commander & & getHeroClass ( ) - > commander . hasValue ( ) )
2014-06-05 19:52:14 +03:00
{
2024-10-05 19:37:52 +00:00
commander = new CCommanderInstance ( getHeroClass ( ) - > commander ) ;
2014-06-05 19:52:14 +03:00
commander - > setArmyObj ( castToArmyObj ( ) ) ; //TODO: separate function for setting commanders
commander - > giveStackExp ( exp ) ; //after our exp is set
}
2023-11-20 18:44:27 +02:00
skillsInfo = SecondarySkillsInfo ( ) ;
2023-07-22 22:04:32 +03:00
//copy active (probably growing) bonuses from hero prototype to hero object
2024-10-05 19:37:52 +00:00
for ( const std : : shared_ptr < Bonus > & b : getHeroType ( ) - > specialty )
2023-07-22 22:04:32 +03:00
addNewBonus ( b ) ;
//initialize bonuses
recreateSecondarySkillsBonuses ( ) ;
movement = movementPointsLimit ( true ) ;
mana = manaLimit ( ) ; //after all bonuses are taken into account, make sure this line is the last one
2014-06-05 19:52:14 +03:00
}
2024-06-01 15:28:17 +00:00
void CGHeroInstance : : initArmy ( vstd : : RNG & rand , IArmyDescriptor * dst )
2014-06-05 19:52:14 +03:00
{
if ( ! dst )
dst = this ;
int warMachinesGiven = 0 ;
2024-08-31 11:00:36 +00:00
auto stacksCountChances = cb - > getSettings ( ) . getVector ( EGameSettings : : HEROES_STARTING_STACKS_CHANCES ) ;
2023-02-04 23:40:02 +01:00
int stacksCountInitRandomNumber = rand . nextInt ( 1 , 100 ) ;
2024-10-05 19:37:52 +00:00
size_t maxStacksCount = std : : min ( stacksCountChances . size ( ) , getHeroType ( ) - > initialArmy . size ( ) ) ;
2014-06-05 19:52:14 +03:00
2023-03-15 23:47:26 +02:00
for ( int stackNo = 0 ; stackNo < maxStacksCount ; stackNo + + )
2014-06-05 19:52:14 +03:00
{
2023-03-15 23:47:26 +02:00
if ( stacksCountInitRandomNumber > stacksCountChances [ stackNo ] )
continue ;
2024-10-05 19:37:52 +00:00
auto & stack = getHeroType ( ) - > initialArmy [ stackNo ] ;
2014-06-05 19:52:14 +03:00
2016-08-23 08:13:52 +03:00
int count = rand . nextInt ( stack . minAmount , stack . maxAmount ) ;
2014-06-05 19:52:14 +03:00
2024-01-15 18:19:21 +02:00
if ( stack . creature = = CreatureID : : NONE )
2017-05-26 19:51:45 +03:00
{
2024-01-15 18:19:21 +02:00
logGlobal - > error ( " Hero %s has invalid creature in initial army " , getNameTranslated ( ) ) ;
2017-05-26 19:51:45 +03:00
continue ;
}
2024-01-15 18:19:21 +02:00
const CCreature * creature = stack . creature . toCreature ( ) ;
2017-05-26 19:51:45 +03:00
if ( creature - > warMachine ! = ArtifactID : : NONE ) //war machine
2014-06-05 19:52:14 +03:00
{
warMachinesGiven + + ;
if ( dst ! = this )
continue ;
2017-05-26 19:51:45 +03:00
ArtifactID aid = creature - > warMachine ;
const CArtifact * art = aid . toArtifact ( ) ;
2023-07-03 23:11:56 +03:00
if ( art ! = nullptr & & ! art - > getPossibleSlots ( ) . at ( ArtBearer : : HERO ) . empty ( ) )
2014-06-05 19:52:14 +03:00
{
2017-05-26 19:51:45 +03:00
//TODO: should we try another possible slots?
2023-07-03 23:11:56 +03:00
ArtifactPosition slot = art - > getPossibleSlots ( ) . at ( ArtBearer : : HERO ) . front ( ) ;
2017-05-26 19:51:45 +03:00
if ( ! getArt ( slot ) )
2023-11-13 12:09:55 +02:00
{
2024-09-04 16:25:30 +03:00
auto artifact = ArtifactUtils : : createArtifact ( aid ) ;
2023-11-13 12:09:55 +02:00
putArtifact ( slot , artifact ) ;
}
2017-05-26 19:51:45 +03:00
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 19:51:45 +03:00
{
2023-01-02 13:27:03 +02:00
logGlobal - > error ( " Hero %s has invalid war machine in initial army " , getNameTranslated ( ) ) ;
2017-05-26 19:51:45 +03:00
}
2014-06-05 19:52:14 +03:00
}
else
2017-05-26 19:51:45 +03:00
{
2014-06-05 19:52:14 +03:00
dst - > setCreature ( SlotID ( stackNo - warMachinesGiven ) , stack . creature , count ) ;
2017-05-26 19:51:45 +03: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 )
{
2023-08-19 21:43:50 +03:00
if ( cb - > gameState ( ) - > getPlayerRelations ( tempOwner , h - > tempOwner ) ! = PlayerRelations : : ENEMIES )
2014-06-05 19:52:14 +03:00
{
//exchange
cb - > heroExchange ( h - > id , id ) ;
}
else //battle
{
if ( visitedTown ) //we're in town
visitedTown - > onHeroVisit ( h ) ; //town will handle attacking
else
2024-08-31 21:04:32 +00:00
cb - > startBattle ( h , this ) ;
2014-06-05 19:52:14 +03:00
}
}
else if ( ID = = Obj : : PRISON )
{
2024-08-31 11:00:36 +00:00
if ( cb - > getHeroCount ( h - > tempOwner , false ) < cb - > getSettings ( ) . getInteger ( EGameSettings : : HEROES_PER_PLAYER_ON_MAP_CAP ) ) //free hero slot
2014-06-05 19:52:14 +03:00
{
//update hero parameters
SetMovePoints smp ;
smp . hid = id ;
2023-06-08 09:17:08 +02:00
2023-11-06 18:27:16 +02:00
cb - > setManaPoints ( id , manaLimit ( ) ) ;
2023-06-08 09:17:08 +02:00
ObjectInstanceID boatId ;
2023-06-15 17:53:18 +02:00
const auto boatPos = visitablePos ( ) ;
if ( cb - > gameState ( ) - > map - > getTile ( boatPos ) . isWater ( ) )
2023-06-08 09:17:08 +02:00
{
2023-06-21 20:38:26 +03:00
smp . val = movementPointsLimit ( false ) ;
2023-08-01 18:51:33 +02:00
if ( ! boat )
{
//Create a new boat for hero
2024-07-12 16:51:27 +00:00
cb - > createBoat ( boatPos , getBoatType ( ) , h - > getOwner ( ) ) ;
2023-08-01 18:51:33 +02:00
boatId = cb - > getTopObj ( boatPos ) - > id ;
}
2023-06-08 09:17:08 +02:00
}
else
{
2023-06-21 20:38:26 +03:00
smp . val = movementPointsLimit ( true ) ;
2023-06-08 09:17:08 +02:00
}
cb - > giveHero ( id , h - > tempOwner , boatId ) ; //recreates def and adds hero to player
2023-11-06 18:27:16 +02:00
cb - > setObjPropertyID ( id , ObjProperty : : ID , Obj ( Obj : : HERO ) ) ; //set ID to 34 AFTER hero gets correct flag color
2014-06-05 19:52:14 +03:00
cb - > setMovePoints ( & smp ) ;
2023-06-08 09:17:08 +02:00
h - > showInfoDialog ( 102 ) ;
2014-06-05 19:52:14 +03:00
}
else //already 8 wandering heroes
{
2023-06-08 09:17:08 +02:00
h - > showInfoDialog ( 103 ) ;
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 ( ) ) ;
2024-01-30 23:34:27 +02:00
boost : : algorithm : : replace_first ( hoverName , " %s " , getClassNameTranslated ( ) ) ;
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
}
2024-09-17 21:38:28 +02:00
std : : string CGHeroInstance : : getHoverText ( PlayerColor player ) const
{
std : : string hoverText = CArmedInstance : : getHoverText ( player ) + getMovementPointsTextIfOwner ( player ) ;
return hoverText ;
}
std : : string CGHeroInstance : : getMovementPointsTextIfOwner ( PlayerColor player ) const
{
std : : string output = " " ;
if ( player = = getOwner ( ) )
{
output + = " " + VLC - > generaltexth - > translate ( " vcmi.adventureMap.movementPointsHeroInfo " ) ;
boost : : replace_first ( output , " %POINTS " , std : : to_string ( movementPointsLimit ( ! boat ) ) ) ;
boost : : replace_first ( output , " %REMAINING " , std : : to_string ( movementPointsRemaining ( ) ) ) ;
}
return output ;
}
2014-06-05 19:52:14 +03:00
ui8 CGHeroInstance : : maxlevelsToMagicSchool ( ) const
{
2024-10-05 19:37:52 +00:00
return getHeroClass ( ) - > isMagicHero ( ) ? 3 : 4 ;
2014-06-05 19:52:14 +03:00
}
ui8 CGHeroInstance : : maxlevelsToWisdom ( ) const
{
2024-10-05 19:37:52 +00:00
return getHeroClass ( ) - > isMagicHero ( ) ? 3 : 6 ;
2014-06-05 19:52:14 +03:00
}
2023-02-12 23:39:17 +03:00
CGHeroInstance : : SecondarySkillsInfo : : SecondarySkillsInfo ( ) :
magicSchoolCounter ( 1 ) ,
wisdomCounter ( 1 )
2023-11-20 18:44:27 +02:00
{ }
2016-08-18 18:53:28 +03:00
2014-06-05 19:52:14 +03:00
void CGHeroInstance : : SecondarySkillsInfo : : resetMagicSchoolCounter ( )
{
2023-11-20 18:44:27 +02:00
magicSchoolCounter = 0 ;
2014-06-05 19:52:14 +03:00
}
void CGHeroInstance : : SecondarySkillsInfo : : resetWisdomCounter ( )
{
2023-11-20 18:44:27 +02:00
wisdomCounter = 0 ;
2014-06-05 19:52:14 +03:00
}
2024-06-01 15:28:17 +00:00
void CGHeroInstance : : pickRandomObject ( vstd : : RNG & rand )
2023-10-25 13:50:11 +03:00
{
assert ( ID = = Obj : : HERO | | ID = = Obj : : PRISON | | ID = = Obj : : RANDOM_HERO ) ;
if ( ID = = Obj : : RANDOM_HERO )
{
2024-11-24 20:05:34 +00:00
auto selectedHero = cb - > gameState ( ) - > pickNextHeroType ( getOwner ( ) ) ;
2023-10-25 13:50:11 +03:00
ID = Obj : : HERO ;
2024-11-24 20:05:34 +00:00
subID = selectedHero ;
2024-10-05 19:37:52 +00:00
randomizeArmy ( getHeroClass ( ) - > faction ) ;
2023-10-25 13:50:11 +03:00
}
2023-10-26 19:11:18 +03:00
auto oldSubID = subID ;
2023-10-25 21:56:00 +03:00
2023-10-26 19:11:18 +03:00
// to find object handler we must use heroClass->id
// after setType subID used to store unique hero identify id. Check issue 2277 for details
2024-04-18 19:13:32 +03:00
// exclude prisons which should use appearance as set in map, via map editor or RMG
2023-10-26 16:50:29 +03:00
if ( ID ! = Obj : : PRISON )
2024-10-05 19:37:52 +00:00
setType ( ID , getHeroClass ( ) - > getIndex ( ) ) ;
2023-10-26 19:11:18 +03:00
this - > subID = oldSubID ;
2023-10-25 13:50:11 +03:00
}
2014-06-05 19:52:14 +03:00
void CGHeroInstance : : recreateSecondarySkillsBonuses ( )
{
2024-12-21 18:47:11 +00:00
auto secondarySkillsBonuses = getBonusesFrom ( BonusSource : : SECONDARY_SKILL ) ;
2023-02-12 23:39:17 +03:00
for ( const auto & bonus : * secondarySkillsBonuses )
2014-06-05 19:52:14 +03:00
removeBonus ( bonus ) ;
2023-02-12 23:39:17 +03:00
for ( const auto & skill_info : secSkills )
2018-03-01 08:44:05 +13:00
if ( skill_info . second > 0 )
2018-03-05 20:02:23 +13:00
updateSkillBonus ( SecondarySkill ( skill_info . first ) , skill_info . second ) ;
2014-06-05 19:52:14 +03:00
}
2023-02-12 23:39:17 +03:00
void CGHeroInstance : : updateSkillBonus ( const SecondarySkill & which , int val )
2014-06-05 19:52:14 +03:00
{
2023-10-21 14:50:42 +03:00
removeBonuses ( Selector : : source ( BonusSource : : SECONDARY_SKILL , BonusSourceID ( which ) ) ) ;
2018-03-31 18:56:40 +13:00
auto skillBonus = ( * VLC - > skillh ) [ which ] - > at ( val ) . effects ;
2023-02-12 23:39:17 +03:00
for ( const auto & b : skillBonus )
2018-03-01 08:44:05 +13:00
addNewBonus ( std : : make_shared < Bonus > ( * b ) ) ;
2014-06-05 19:52:14 +03:00
}
2018-03-05 20:02:23 +13:00
2023-11-06 18:27:16 +02:00
void CGHeroInstance : : setPropertyDer ( ObjProperty what , ObjPropertyID identifier )
2014-06-05 19:52:14 +03:00
{
if ( what = = ObjProperty : : PRIMARY_STACK_COUNT )
2023-11-06 18:27:16 +02:00
setStackCount ( SlotID ( 0 ) , identifier . getNum ( ) ) ;
2014-06-05 19:52:14 +03:00
}
2024-12-24 23:11:20 +00:00
int CGHeroInstance : : getPrimSkillLevel ( PrimarySkill id ) const
2024-12-22 14:49:35 +00:00
{
2024-12-24 23:11:20 +00:00
return primarySkills . getSkills ( ) [ id ] ;
2024-12-22 14:49:35 +00:00
}
2014-06-05 19:52:14 +03:00
double CGHeroInstance : : getFightingStrength ( ) const
{
2024-12-24 23:11:20 +00:00
const auto & skillValues = primarySkills . getSkills ( ) ;
return sqrt ( ( 1.0 + 0.05 * skillValues [ PrimarySkill : : ATTACK ] ) * ( 1.0 + 0.05 * skillValues [ PrimarySkill : : DEFENSE ] ) ) ;
2014-06-05 19:52:14 +03:00
}
double CGHeroInstance : : getMagicStrength ( ) const
2024-12-22 14:49:35 +00:00
{
2024-12-24 23:11:20 +00:00
const auto & skillValues = primarySkills . getSkills ( ) ;
2024-07-15 17:42:02 +02:00
if ( ! hasSpellbook ( ) )
return 1 ;
bool atLeastOneCombatSpell = false ;
for ( auto spell : spells )
{
2024-12-22 14:49:35 +00:00
if ( spell . toSpell ( ) - > isCombat ( ) )
2024-07-15 17:42:02 +02:00
{
atLeastOneCombatSpell = true ;
break ;
}
}
if ( ! atLeastOneCombatSpell )
return 1 ;
2024-12-24 23:11:20 +00:00
return sqrt ( ( 1.0 + 0.05 * skillValues [ PrimarySkill : : KNOWLEDGE ] * mana / manaLimit ( ) ) * ( 1.0 + 0.05 * skillValues [ PrimarySkill : : SPELL_POWER ] * mana / manaLimit ( ) ) ) ;
2014-06-05 19:52:14 +03:00
}
2024-12-22 14:49:35 +00:00
double CGHeroInstance : : getHeroStrength ( ) const
2024-09-05 17:38:27 +02:00
{
2024-12-24 23:11:20 +00:00
return getFightingStrength ( ) * getMagicStrength ( ) ;
2024-09-05 17:38:27 +02:00
}
2024-12-22 14:49:35 +00:00
uint64_t CGHeroInstance : : getValueForDiplomacy ( ) const
2014-06-05 19:52:14 +03:00
{
2024-12-22 14:49:35 +00:00
// H3 formula for hero strength when considering diplomacy skill
uint64_t armyStrength = getArmyStrength ( ) ;
double heroStrength = sqrt (
( 1.0 + 0.05 * getPrimSkillLevel ( PrimarySkill : : ATTACK ) ) *
( 1.0 + 0.05 * getPrimSkillLevel ( PrimarySkill : : DEFENSE ) )
) ;
return heroStrength * armyStrength ;
2014-06-05 19:52:14 +03:00
}
2024-12-22 14:49:35 +00:00
uint32_t CGHeroInstance : : getValueForCampaign ( ) const
2024-09-05 17:38:27 +02:00
{
2024-12-22 14:49:35 +00:00
/// Determined by testing H3: hero is preferred for transfer in campaigns if total sum of his primary skills and his secondary skill levels is greatest
uint32_t score = 0 ;
score + = getPrimSkillLevel ( PrimarySkill : : ATTACK ) ;
score + = getPrimSkillLevel ( PrimarySkill : : DEFENSE ) ;
score + = getPrimSkillLevel ( PrimarySkill : : SPELL_POWER ) ;
score + = getPrimSkillLevel ( PrimarySkill : : DEFENSE ) ;
for ( const auto & secondary : secSkills )
score + = secondary . second ;
return score ;
2024-09-05 17:38:27 +02:00
}
2014-06-05 19:52:14 +03:00
ui64 CGHeroInstance : : getTotalStrength ( ) const
{
2024-07-15 17:42:02 +02:00
double ret = getHeroStrength ( ) * getArmyStrength ( ) ;
2023-02-12 23:39:17 +03:00
return static_cast < ui64 > ( ret ) ;
2014-06-05 19:52:14 +03:00
}
TExpType CGHeroInstance : : calculateXp ( TExpType exp ) const
{
2023-05-01 01:20:01 +03:00
return static_cast < TExpType > ( exp * ( valOfBonuses ( BonusType : : HERO_EXPERIENCE_GAIN_PERCENT ) ) / 100.0 ) ;
2014-06-05 19:52:14 +03:00
}
2018-03-04 11:13:07 +03:00
int32_t CGHeroInstance : : getCasterUnitId ( ) const
{
2023-04-10 01:06:02 +04:00
return id . getNum ( ) ;
2018-03-04 11:13:07 +03:00
}
2023-11-05 18:58:07 +02:00
int32_t CGHeroInstance : : getSpellSchoolLevel ( const spells : : Spell * spell , SpellSchool * 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 17:58:30 +03:00
int32_t skill = - 1 ; //skill level
2016-01-24 02:27:14 +03:00
2023-08-21 22:10:32 +03:00
spell - > forEachSchool ( [ & , this ] ( const SpellSchool & cnf , bool & stop )
2014-11-13 17:24:30 +03:00
{
2024-12-25 23:04:15 +00:00
int32_t thisSchool = magicSchoolMastery . getMastery ( cnf ) ; //FIXME: Bonus shouldn't be additive (Witchking Artifacts : Crown of Skies)
2016-01-24 02:27:14 +03:00
if ( thisSchool > skill )
{
skill = thisSchool ;
if ( outSelectedSchool )
2023-08-21 22:10:32 +03:00
* outSelectedSchool = cnf ;
2016-01-24 02:27:14 +03:00
}
2014-11-24 19:14:10 +03:00
} ) ;
2016-01-24 02:27:14 +03:00
2024-12-25 23:04:15 +00:00
vstd : : amax ( skill , magicSchoolMastery . getMastery ( SpellSchool : : ANY ) ) ; //any school bonus
2023-10-21 14:50:42 +03:00
vstd : : amax ( skill , valOfBonuses ( BonusType : : SPELL , BonusSubtypeID ( spell - > getId ( ) ) ) ) ; //given by artifact or other effect
2015-09-15 05:53:10 +03:00
2015-10-31 21:15:40 +01: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 17:58:30 +03:00
return skill ;
2014-06-05 19:52:14 +03:00
}
2017-07-20 07:08:49 +03:00
int64_t CGHeroInstance : : getSpellBonus ( const spells : : Spell * spell , int64_t base , const battle : : Unit * affectedStack ) const
2015-03-18 22:22:52 +03:00
{
//applying sorcery secondary skill
2023-03-21 12:24:48 +03:00
if ( spell - > isMagical ( ) )
2023-10-21 14:50:42 +03:00
base = static_cast < int64_t > ( base * ( valOfBonuses ( BonusType : : SPELL_DAMAGE , BonusSubtypeID ( SpellSchool : : ANY ) ) ) / 100.0 ) ;
2023-03-21 12:24:48 +03:00
2023-10-21 14:50:42 +03:00
base = static_cast < int64_t > ( base * ( 100 + valOfBonuses ( BonusType : : SPECIFIC_SPELL_DAMAGE , BonusSubtypeID ( spell - > getId ( ) ) ) ) / 100.0 ) ;
2015-03-18 22:22:52 +03:00
2018-02-20 14:29:06 +03:00
int maxSchoolBonus = 0 ;
2023-08-21 22:10:32 +03:00
spell - > forEachSchool ( [ & maxSchoolBonus , this ] ( const SpellSchool & cnf , bool & stop )
2015-03-18 22:22:52 +03:00
{
2023-10-21 14:50:42 +03:00
vstd : : amax ( maxSchoolBonus , valOfBonuses ( BonusType : : SPELL_DAMAGE , BonusSubtypeID ( cnf ) ) ) ;
2015-03-18 22:22:52 +03:00
} ) ;
2023-02-12 23:39:17 +03:00
base = static_cast < int64_t > ( base * ( 100 + maxSchoolBonus ) / 100.0 ) ;
2018-02-20 14:29:06 +03:00
2017-07-20 07:08:49 +03:00
if ( affectedStack & & affectedStack - > creatureLevel ( ) > 0 ) //Hero specials like Solmyr, Deemer
2023-10-21 14:50:42 +03:00
base = static_cast < int64_t > ( base * static_cast < double > ( 100 + valOfBonuses ( BonusType : : SPECIAL_SPELL_LEV , BonusSubtypeID ( spell - > getId ( ) ) ) / affectedStack - > creatureLevel ( ) ) / 100.0 ) ;
2017-07-20 07:08:49 +03:00
return base ;
}
2015-03-18 22:22:52 +03:00
2017-07-20 07:08:49 +03:00
int64_t CGHeroInstance : : getSpecificSpellBonus ( const spells : : Spell * spell , int64_t base ) const
{
2023-10-21 14:50:42 +03:00
base = static_cast < int64_t > ( base * ( 100 + valOfBonuses ( BonusType : : SPECIFIC_SPELL_DAMAGE , BonusSubtypeID ( spell - > getId ( ) ) ) ) / 100.0 ) ;
2016-01-24 02:27:14 +03:00
return base ;
2015-03-18 22:22:52 +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 17:58:30 +03:00
int32_t CGHeroInstance : : getEffectLevel ( const spells : : Spell * spell ) const
2015-09-17 08:42:30 +03:00
{
2023-01-31 11:30:41 +03:00
return getSpellSchoolLevel ( spell ) ;
2015-09-17 08:42:30 +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 17:58:30 +03:00
int32_t CGHeroInstance : : getEffectPower ( const spells : : Spell * spell ) const
2015-09-17 08:42:30 +03: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 17:58:30 +03:00
int32_t CGHeroInstance : : getEnchantPower ( const spells : : Spell * spell ) const
2015-09-17 08:42:30 +03:00
{
2023-10-28 17:57:56 +03:00
int32_t spellpower = getPrimSkillLevel ( PrimarySkill : : SPELL_POWER ) ;
int32_t durationCommon = valOfBonuses ( BonusType : : SPELL_DURATION , BonusSubtypeID ( ) ) ;
int32_t durationSpecific = valOfBonuses ( BonusType : : SPELL_DURATION , BonusSubtypeID ( spell - > getId ( ) ) ) ;
return spellpower + durationCommon + durationSpecific ;
2015-09-17 08:42:30 +03:00
}
2017-07-20 07:08:49 +03:00
int64_t CGHeroInstance : : getEffectValue ( const spells : : Spell * spell ) const
2015-09-17 08:42:30 +03:00
{
return 0 ;
}
2015-03-18 22:22:52 +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 17:58:30 +03:00
PlayerColor CGHeroInstance : : getCasterOwner ( ) const
2015-09-17 09:29:57 +03:00
{
return tempOwner ;
}
2016-09-10 18:23:55 +03: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-06-18 12:18:25 +03:00
text . replaceRawString ( getNameTranslated ( ) ) ;
2017-07-20 07:08:49 +03:00
}
2016-09-10 18:23:55 +03:00
2025-01-10 18:58:46 +00:00
void CGHeroInstance : : getCastDescription ( const spells : : Spell * spell , const battle : : Units & attacked , MetaString & text ) const
2016-09-17 23:04:23 +03:00
{
const bool singleTarget = attacked . size ( ) = = 1 ;
const int textIndex = singleTarget ? 195 : 196 ;
2023-06-18 12:18:25 +03:00
text . appendLocalString ( EMetaText : : GENERAL_TXT , textIndex ) ;
2016-09-17 23:04:23 +03:00
getCasterName ( text ) ;
2023-11-02 22:01:49 +02:00
text . replaceName ( spell - > getId ( ) ) ;
2016-09-17 23:04:23 +03:00
if ( singleTarget )
2017-07-20 07:08:49 +03:00
attacked . at ( 0 ) - > addNameReplacement ( text , true ) ;
}
2023-04-10 01:31:41 +04:00
const CGHeroInstance * CGHeroInstance : : getHeroCaster ( ) const
{
return this ;
}
Entities redesign and a few ERM features
* Made most Handlers derived from CHandlerBase and moved service API there.
* Declared existing Entity APIs.
* Added basic script context caching
* Started Lua script module
* Started Lua spell effect API
* Started script state persistence
* Started battle info callback binding
* CommitPackage removed
* Extracted spells::Caster to own header; Expanded Spell API.
* implemented !!MC:S, !!FU:E, !!FU:P, !!MA, !!VR:H, !!VR:C
* !!BU:C, !!BU:E, !!BU:G, !!BU:M implemented
* Allow use of "MC:S@varName@" to declare normal variable (technically v-variable with string key)
* Re-enabled VERM macros.
* !?GM0 added
* !?TM implemented
* Added !!MF:N
* Started !?OB, !!BM, !!HE, !!OW, !!UN
* Added basic support of w-variables
* Added support for ERM indirect variables
* Made !?FU regular trigger
* !!re (ERA loop receiver) implemented
* Fixed ERM receivers with zero args.
2018-03-17 17:58:30 +03:00
void CGHeroInstance : : spendMana ( ServerCallback * server , const int spellCost ) const
2017-07-20 07:08:49 +03:00
{
if ( spellCost ! = 0 )
{
SetMana sm ;
sm . absolute = false ;
sm . hid = id ;
sm . val = - spellCost ;
2024-10-04 18:59:51 +00:00
server - > apply ( sm ) ;
2017-07-20 07:08:49 +03:00
}
2016-09-17 23:04:23 +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 17:58:30 +03:00
bool CGHeroInstance : : canCastThisSpell ( const spells : : Spell * spell ) const
2014-06-05 19:52:14 +03:00
{
2024-01-01 16:37:48 +02:00
const bool isAllowed = cb - > isAllowed ( spell - > getId ( ) ) ;
2015-09-16 04:39:44 +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 17:58:30 +03:00
const bool inSpellBook = vstd : : contains ( spells , spell - > getId ( ) ) & & hasSpellbook ( ) ;
2023-10-21 14:50:42 +03:00
const bool specificBonus = hasBonusOfType ( BonusType : : SPELL , BonusSubtypeID ( spell - > getId ( ) ) ) ;
2015-09-16 04:39:44 +03:00
bool schoolBonus = false ;
2023-08-21 22:10:32 +03:00
spell - > forEachSchool ( [ this , & schoolBonus ] ( const SpellSchool & cnf , bool & stop )
2015-09-16 04:39:44 +03:00
{
2023-10-21 14:50:42 +03:00
if ( hasBonusOfType ( BonusType : : SPELLS_OF_SCHOOL , BonusSubtypeID ( cnf ) ) )
2015-09-16 04:39:44 +03:00
{
schoolBonus = stop = true ;
}
} ) ;
2023-10-21 14:50:42 +03:00
const bool levelBonus = hasBonusOfType ( BonusType : : SPELLS_OF_LEVEL , BonusCustomSubtype : : spellLevel ( spell - > getLevel ( ) ) ) ;
2015-09-16 04:39:44 +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 17:58:30 +03:00
if ( spell - > isSpecial ( ) )
2016-03-12 04:41:27 +03: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 04:41:27 +03:00
}
return specificBonus ;
}
else if ( ! isAllowed )
{
if ( inSpellBook )
2016-11-02 14:52:02 +03: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 04:41:27 +03:00
}
2016-11-02 14:52:02 +03:00
return inSpellBook | | specificBonus | | schoolBonus | | levelBonus ;
2016-03-12 04:41:27 +03:00
}
else
{
2015-09-16 04:39:44 +03:00
return inSpellBook | | schoolBonus | | specificBonus | | levelBonus ;
2016-03-12 04:41:27 +03:00
}
2014-06-05 19:52:14 +03:00
}
2023-10-18 17:38:40 +03:00
bool CGHeroInstance : : canLearnSpell ( const spells : : Spell * spell , bool allowBanned ) const
2016-10-09 13:38:54 +03:00
{
2023-10-04 16:49:17 +03:00
if ( ! hasSpellbook ( ) )
2016-10-09 13:38:54 +03: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 17:58:30 +03:00
if ( spell - > getLevel ( ) > maxSpellLevel ( ) ) //not enough wisdom
2016-10-09 13:38:54 +03: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 17:58:30 +03:00
if ( vstd : : contains ( spells , spell - > getId ( ) ) ) //already known
2016-10-09 13:38:54 +03: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 17:58:30 +03:00
if ( spell - > isSpecial ( ) )
2016-10-09 13:38:54 +03: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 13:38:54 +03: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 13:38:54 +03:00
return false ; //creature abilities can not be learned
}
2024-01-01 16:37:48 +02:00
if ( ! allowBanned & & ! cb - > isAllowed ( spell - > getId ( ) ) )
2016-10-09 13:38:54 +03: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 13:38:54 +03: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
{
2023-05-01 01:20:01 +03:00
bool hasImprovedNecromancy = hasBonusOfType ( BonusType : : IMPROVED_NECROMANCY ) ;
2023-02-19 23:55:18 +03:00
2018-03-17 21:46:16 +13:00
// need skill or cloak of undead king - lesser artifacts don't work without skill
2023-02-19 23:55:18 +03:00
if ( hasImprovedNecromancy )
2014-06-05 19:52:14 +03:00
{
2023-05-01 01:20:01 +03:00
double necromancySkill = valOfBonuses ( BonusType : : UNDEAD_RAISE_PERCENTAGE ) / 100.0 ;
const ui8 necromancyLevel = valOfBonuses ( BonusType : : IMPROVED_NECROMANCY ) ;
2014-06-05 19:52:14 +03:00
vstd : : amin ( necromancySkill , 1.0 ) ; //it's impossible to raise more creatures than all...
2024-08-11 20:22:35 +00:00
const std : : map < CreatureID , si32 > & casualties = battleResult . casualties [ CBattleInfoEssentials : : otherSide ( battleResult . winner ) ] ;
2018-03-17 21:46:16 +13:00
// figure out what to raise - pick strongest creature meeting requirements
2023-08-19 20:48:28 +03:00
CreatureID creatureTypeRaised = CreatureID : : NONE ; //now we always have IMPROVED_NECROMANCY, no need for hardcode
2018-03-17 21:46:16 +13:00
int requiredCasualtyLevel = 1 ;
2024-12-21 18:47:11 +00:00
TConstBonusListPtr improvedNecromancy = getBonusesOfType ( BonusType : : IMPROVED_NECROMANCY ) ;
2018-03-17 21:46:16 +13:00
if ( ! improvedNecromancy - > empty ( ) )
2014-06-05 19:52:14 +03:00
{
2018-03-17 21:46:16 +13:00
int maxCasualtyLevel = 1 ;
2023-02-12 23:39:17 +03:00
for ( const auto & casualty : casualties )
2023-12-31 23:43:35 +02:00
vstd : : amax ( maxCasualtyLevel , VLC - > creatures ( ) - > getById ( casualty . first ) - > getLevel ( ) ) ;
2018-03-17 21:46:16 +13:00
// pick best bonus available
std : : shared_ptr < Bonus > topPick ;
2023-02-12 23:39:17 +03:00
for ( const std : : shared_ptr < Bonus > & newPick : * improvedNecromancy )
2018-03-17 21:46:16 +13: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-10-05 16:13:52 +03:00
auto quality = [ ] ( const std : : shared_ptr < Bonus > & pick ) - > std : : tuple < int , int , int >
2018-03-17 21:46:16 +13:00
{
2023-10-05 16:13:52 +03:00
const auto * c = pick - > subtype . as < CreatureID > ( ) . toCreature ( ) ;
2023-04-05 03:26:29 +03:00
return std : : tuple < int , int , int > { c - > getLevel ( ) , static_cast < int > ( c - > getFullRecruitCost ( ) . marketValue ( ) ) , - pick - > additionalInfo [ 1 ] } ;
2018-03-17 21:46:16 +13:00
} ;
if ( quality ( topPick ) < quality ( newPick ) )
topPick = newPick ;
}
}
if ( topPick )
{
2023-10-05 16:13:52 +03:00
creatureTypeRaised = topPick - > subtype . as < CreatureID > ( ) ;
2018-03-17 21:46:16 +13:00
requiredCasualtyLevel = std : : max ( topPick - > additionalInfo [ 1 ] , 1 ) ;
}
2014-06-05 19:52:14 +03:00
}
2023-02-19 23:55:18 +03:00
assert ( creatureTypeRaised ! = CreatureID : : NONE ) ;
2018-03-17 21:46:16 +13: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-11-05 18:58:07 +02:00
for ( const CreatureID & upgraded : creatureTypeRaised . toCreature ( ) - > upgrades )
2018-03-17 21:46:16 +13:00
{
if ( getSlotFor ( upgraded ) ! = SlotID ( ) )
{
creatureTypeRaised = upgraded ;
necromancySkill * = 2 / 3.0 ;
break ;
}
}
2014-06-05 19:52:14 +03:00
}
2018-03-17 21:46:16 +13:00
// calculate number of creatures raised - low level units contribute at 50% rate
2023-11-05 18:58:07 +02:00
const double raisedUnitHealth = creatureTypeRaised . toCreature ( ) - > getMaxHealth ( ) ;
2018-03-17 21:46:16 +13:00
double raisedUnits = 0 ;
2023-02-12 23:39:17 +03:00
for ( const auto & casualty : casualties )
2018-03-17 21:46:16 +13:00
{
2023-12-31 23:43:35 +02:00
const CCreature * c = casualty . first . toCreature ( ) ;
2023-05-01 20:29:53 +03:00
double raisedFromCasualty = std : : min ( c - > getMaxHealth ( ) / raisedUnitHealth , 1.0 ) * casualty . second * necromancySkill ;
2023-04-05 03:26:29 +03:00
if ( c - > getLevel ( ) < requiredCasualtyLevel )
2018-03-17 21:46:16 +13:00
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 .
*/
2024-06-01 15:28:17 +00:00
void CGHeroInstance : : showNecromancyDialog ( const CStackBasicDescriptor & raisedStack , vstd : : RNG & rand ) const
2014-06-05 19:52:14 +03:00
{
InfoWindow iw ;
2023-03-07 04:09:19 +03:00
iw . type = EInfoWindowMode : : AUTO ;
2016-09-09 20:30:36 +03:00
iw . soundID = soundBase : : pickup01 + rand . nextInt ( 6 ) ;
2014-06-05 19:52:14 +03:00
iw . player = tempOwner ;
2023-10-31 11:09:56 +02:00
iw . components . emplace_back ( ComponentType : : CREATURE , raisedStack . getId ( ) , raisedStack . count ) ;
2014-06-05 19:52:14 +03:00
if ( raisedStack . count > 1 ) // Practicing the dark arts of necromancy, ... (plural)
{
2023-06-18 12:18:25 +03:00
iw . text . appendLocalString ( EMetaText : : GENERAL_TXT , 145 ) ;
iw . text . replaceNumber ( raisedStack . count ) ;
2014-06-05 19:52:14 +03:00
}
else // Practicing the dark arts of necromancy, ... (singular)
{
2023-06-18 12:18:25 +03:00
iw . text . appendLocalString ( EMetaText : : GENERAL_TXT , 146 ) ;
2014-06-05 19:52:14 +03:00
}
2023-11-02 22:01:49 +02:00
iw . text . replaceName ( raisedStack ) ;
2014-06-05 19:52:14 +03:00
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 18:01:58 +03:00
int CGHeroInstance : : getSightRadius ( ) const
2014-06-05 19:52:14 +03:00
{
2023-05-01 01:20:01 +03:00
return valOfBonuses ( BonusType : : SIGHT_RADIUS ) ; // scouting gives SIGHT_RADIUS bonus
2014-06-05 19:52:14 +03:00
}
si32 CGHeroInstance : : manaRegain ( ) const
{
2023-05-01 01:20:01 +03:00
if ( hasBonusOfType ( BonusType : : FULL_MANA_REGENERATION ) )
2014-06-05 19:52:14 +03:00
return manaLimit ( ) ;
2023-05-01 01:20:01 +03:00
return valOfBonuses ( BonusType : : MANA_REGENERATION ) ;
2014-06-05 19:52:14 +03:00
}
2016-01-30 10:20:49 +03: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 17:58:30 +03: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));
// }
2023-04-20 21:20:51 +04:00
BoatId CGHeroInstance : : getBoatType ( ) const
2014-06-05 19:52:14 +03:00
{
2024-10-05 19:37:52 +00:00
return BoatId ( VLC - > townh - > getById ( getHeroClass ( ) - > faction ) - > getBoatType ( ) ) ;
2014-06-05 19:52:14 +03:00
}
void CGHeroInstance : : getOutOffsets ( std : : vector < int3 > & offsets ) const
{
2023-06-20 22:38:47 +03:00
offsets = {
{ 0 , - 1 , 0 } ,
{ + 1 , - 1 , 0 } ,
{ + 1 , 0 , 0 } ,
{ + 1 , + 1 , 0 } ,
{ 0 , + 1 , 0 } ,
{ - 1 , + 1 , 0 } ,
{ - 1 , 0 , 0 } ,
{ - 1 , - 1 , 0 } ,
2014-10-04 00:34:13 +04:00
} ;
2014-06-05 19:52:14 +03:00
}
2023-06-07 01:55:21 +03:00
const IObjectInterface * CGHeroInstance : : getObject ( ) const
{
return this ;
}
Entities redesign and a few ERM features
* Made most Handlers derived from CHandlerBase and moved service API there.
* Declared existing Entity APIs.
* Added basic script context caching
* Started Lua script module
* Started Lua spell effect API
* Started script state persistence
* Started battle info callback binding
* CommitPackage removed
* Extracted spells::Caster to own header; Expanded Spell API.
* implemented !!MC:S, !!FU:E, !!FU:P, !!MA, !!VR:H, !!VR:C
* !!BU:C, !!BU:E, !!BU:G, !!BU:M implemented
* Allow use of "MC:S@varName@" to declare normal variable (technically v-variable with string key)
* Re-enabled VERM macros.
* !?GM0 added
* !?TM implemented
* Added !!MF:N
* Started !?OB, !!BM, !!HE, !!OW, !!UN
* Added basic support of w-variables
* Added support for ERM indirect variables
* Made !?FU regular trigger
* !!re (ERA loop receiver) implemented
* Fixed ERM receivers with zero args.
2018-03-17 17:58:30 +03:00
int32_t CGHeroInstance : : getSpellCost ( const spells : : Spell * sp ) const
2014-06-05 19:52:14 +03:00
{
return sp - > getCost ( getSpellSchoolLevel ( sp ) ) ;
}
2023-08-19 21:43:50 +03:00
void CGHeroInstance : : pushPrimSkill ( PrimarySkill which , int val )
2014-06-05 19:52:14 +03:00
{
2023-10-21 14:50:42 +03:00
auto sel = Selector : : typeSubtype ( BonusType : : PRIMARY_SKILL , BonusSubtypeID ( which ) )
2023-09-10 23:39:54 +02:00
. And ( Selector : : sourceType ( ) ( BonusSource : : HERO_BASE_SKILL ) ) ;
2024-12-21 18:47:11 +00:00
removeBonuses ( sel ) ;
2023-10-21 14:50:42 +03:00
addNewBonus ( std : : make_shared < Bonus > ( BonusDuration : : PERMANENT , BonusType : : PRIMARY_SKILL , BonusSource : : HERO_BASE_SKILL , val , BonusSourceID ( id ) , BonusSubtypeID ( which ) ) ) ;
2014-06-05 19:52:14 +03:00
}
2023-04-04 13:36:42 +03:00
EAlignment CGHeroInstance : : getAlignment ( ) const
2014-06-05 19:52:14 +03:00
{
2024-10-05 19:37:52 +00:00
return getHeroClass ( ) - > getAlignment ( ) ;
2014-06-05 19:52:14 +03:00
}
2024-06-01 15:28:17 +00:00
void CGHeroInstance : : initExp ( vstd : : RNG & rand )
2014-06-05 19:52:14 +03:00
{
2016-09-09 20:30:36 +03: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
}
2023-02-20 00:09:06 +03:00
si32 CGHeroInstance : : manaLimit ( ) const
{
2024-12-25 23:04:15 +00:00
return getPrimSkillLevel ( PrimarySkill : : KNOWLEDGE ) * manaPerKnowledgeCached . getValue ( ) / 100 ;
2023-02-20 00:09:06 +03:00
}
2023-09-28 19:43:04 +03:00
HeroTypeID CGHeroInstance : : getPortraitSource ( ) const
{
if ( customPortraitSource . isValid ( ) )
return customPortraitSource ;
else
2024-10-05 19:37:52 +00:00
return getHeroTypeID ( ) ;
2023-09-28 19:43:04 +03:00
}
int32_t CGHeroInstance : : getIconIndex ( ) const
{
2024-10-05 19:37:52 +00:00
return getPortraitSource ( ) . toEntity ( VLC ) - > getIconIndex ( ) ;
2023-09-28 19:43:04 +03:00
}
2023-01-02 13:27:03 +02:00
std : : string CGHeroInstance : : getNameTranslated ( ) const
{
return VLC - > generaltexth - > translate ( getNameTextID ( ) ) ;
}
2024-01-30 23:34:27 +02:00
std : : string CGHeroInstance : : getClassNameTranslated ( ) const
{
return VLC - > generaltexth - > translate ( getClassNameTextID ( ) ) ;
}
std : : string CGHeroInstance : : getClassNameTextID ( ) const
{
if ( isCampaignGem ( ) )
return " core.genrltxt.735 " ;
2024-10-05 19:37:52 +00:00
return getHeroClass ( ) - > getNameTextID ( ) ;
2024-01-30 23:34:27 +02:00
}
2023-01-02 13:27:03 +02:00
std : : string CGHeroInstance : : getNameTextID ( ) const
{
2023-09-28 00:00:32 +02:00
if ( ! nameCustomTextId . empty ( ) )
return nameCustomTextId ;
2024-10-05 19:37:52 +00:00
if ( getHeroTypeID ( ) . hasValue ( ) )
return getHeroType ( ) - > getNameTextID ( ) ;
2023-01-02 13:27:03 +02:00
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
{
return VLC - > generaltexth - > translate ( getBiographyTextID ( ) ) ;
}
std : : string CGHeroInstance : : getBiographyTextID ( ) const
{
2023-09-28 00:00:32 +02:00
if ( ! biographyCustomTextId . empty ( ) )
return biographyCustomTextId ;
2024-10-05 19:37:52 +00:00
if ( getHeroTypeID ( ) . hasValue ( ) )
return getHeroType ( ) - > getBiographyTextID ( ) ;
2023-10-13 20:15:11 +02:00
return " " ; //for random hero
2014-06-05 19:52:14 +03:00
}
2024-09-19 16:51:59 +03:00
CGHeroInstance : : ArtPlacementMap CGHeroInstance : : putArtifact ( const ArtifactPosition & pos , CArtifactInstance * art )
2014-06-05 19:52:14 +03:00
{
2023-10-14 22:00:39 +03:00
assert ( art - > canBePutAt ( this , pos ) ) ;
2023-05-23 20:24:55 +03:00
if ( ArtifactUtils : : isSlotEquipment ( pos ) )
attachTo ( * art ) ;
2023-07-24 19:09:17 +03:00
return CArtifactSet : : putArtifact ( pos , art ) ;
2023-05-23 20:24:55 +03:00
}
2024-09-19 16:51:59 +03:00
void CGHeroInstance : : removeArtifact ( const ArtifactPosition & pos )
2023-05-23 20:24:55 +03:00
{
auto art = getArt ( pos ) ;
assert ( art ) ;
CArtifactSet : : removeArtifact ( pos ) ;
if ( ArtifactUtils : : isSlotEquipment ( pos ) )
detachFrom ( * art ) ;
2014-06-05 19:52:14 +03:00
}
bool CGHeroInstance : : hasSpellbook ( ) const
{
return getArt ( ArtifactPosition : : SPELLBOOK ) ;
}
2023-02-12 23:39:17 +03:00
void CGHeroInstance : : addSpellToSpellbook ( const SpellID & spell )
2018-12-20 23:42:31 +02:00
{
spells . insert ( spell ) ;
}
2023-02-12 23:39:17 +03:00
void CGHeroInstance : : removeSpellFromSpellbook ( const SpellID & spell )
2018-12-20 23:42:31 +02:00
{
spells . erase ( spell ) ;
}
2023-02-12 23:39:17 +03: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 ( ) )
{
2024-11-16 16:22:43 +00:00
cb - > gameState ( ) - > map - > removeArtifactInstance ( * this , ArtifactPosition : : SPELLBOOK ) ;
2018-12-20 23:42:31 +02:00
}
}
const std : : set < SpellID > & CGHeroInstance : : getSpellsInSpellbook ( ) const
{
return spells ;
}
2017-08-26 20:49:29 +12:00
int CGHeroInstance : : maxSpellLevel ( ) const
{
2024-12-21 18:47:11 +00:00
return std : : min ( GameConstants : : SPELL_LEVELS , valOfBonuses ( BonusType : : MAX_LEARNABLE_SPELL_LEVEL ) ) ;
2017-08-26 20:49:29 +12:00
}
2023-08-01 18:51:33 +02:00
void CGHeroInstance : : attachToBoat ( CGBoat * newBoat )
{
assert ( newBoat ) ;
boat = newBoat ;
attachTo ( const_cast < CGBoat & > ( * boat ) ) ;
const_cast < CGBoat * > ( boat ) - > hero = this ;
}
2014-06-05 19:52:14 +03:00
void CGHeroInstance : : deserializationFix ( )
{
artDeserializationFix ( this ) ;
2023-06-19 22:01:18 +03:00
boatDeserializationFix ( ) ;
}
void CGHeroInstance : : boatDeserializationFix ( )
{
if ( boat )
attachTo ( const_cast < CGBoat & > ( * boat ) ) ;
2014-06-05 19:52:14 +03:00
}
2021-03-23 17:47:07 +03: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 17:47:07 +03:00
return whereShouldBeAttachedOnSiege ( visitedTown - > isBattleOutsideTown ( this ) ) ;
2022-11-06 03:26:13 +04:00
return & CArmedInstance : : whereShouldBeAttached ( gs ) ;
2021-03-23 17:47:07 +03:00
}
2022-11-06 03:26:13 +04:00
CBonusSystemNode & CGHeroInstance : : whereShouldBeAttached ( CGameState * gs )
2021-03-23 17:47:07 +03:00
{
2021-08-19 01:45:28 +03:00
if ( visitedTown )
2014-06-05 19:52:14 +03:00
{
2021-08-19 01:45:28 +03:00
if ( inTownGarrison )
2022-11-06 03:26:13 +04:00
return * visitedTown ;
2014-06-05 19:52:14 +03:00
else
2022-11-06 03:26:13 +04:00
return visitedTown - > townAndVis ;
2014-06-05 19:52:14 +03:00
}
else
return CArmedInstance : : whereShouldBeAttached ( gs ) ;
}
2017-07-15 14:08:20 +03:00
int CGHeroInstance : : movementPointsAfterEmbark ( int MPsBefore , int basicCost , bool disembark , const TurnInfo * ti ) const
2014-06-05 19:52:14 +03:00
{
2024-12-28 12:02:27 +00:00
if ( ! ti - > hasFreeShipBoarding ( ) )
2023-03-19 12:45:49 +03:00
return 0 ; // take all MPs by default
2023-04-18 17:36:42 +04:00
auto boatLayer = boat ? boat - > layer : EPathfindingLayer : : SAIL ;
2023-03-19 12:45:49 +03:00
2023-04-18 17:36:42 +04:00
int mp1 = ti - > getMaxMovePoints ( disembark ? EPathfindingLayer : : LAND : boatLayer ) ;
int mp2 = ti - > getMaxMovePoints ( disembark ? boatLayer : EPathfindingLayer : : LAND ) ;
2023-03-19 12:45:49 +03:00
int ret = static_cast < int > ( ( MPsBefore - basicCost ) * static_cast < double > ( mp1 ) / mp2 ) ;
2016-11-25 15:34:38 +03:00
return ret ;
2014-06-05 19:52:14 +03:00
}
2015-11-29 12:32:06 +03:00
EDiggingStatus CGHeroInstance : : diggingStatus ( ) const
2014-06-05 19:52:14 +03:00
{
2023-06-21 20:38:26 +03:00
if ( static_cast < int > ( movement ) < movementPointsLimit ( true ) )
2015-11-29 12:32:06 +03:00
return EDiggingStatus : : LACK_OF_MOVEMENT ;
2024-02-01 18:21:54 +02:00
if ( ! ArtifactID ( ArtifactID : : GRAIL ) . toArtifact ( ) - > canBePutAt ( this ) )
2023-03-21 20:02:58 +02:00
return EDiggingStatus : : BACKPACK_IS_FULL ;
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 ;
}
2024-06-01 15:28:17 +00:00
std : : vector < SecondarySkill > CGHeroInstance : : getLevelUpProposedSecondarySkills ( vstd : : RNG & rand ) const
2014-06-05 19:52:14 +03:00
{
2023-02-27 12:42:57 +03:00
auto getObligatorySkills = [ ] ( CSkill : : Obligatory obl ) {
2023-11-20 18:44:27 +02:00
std : : set < SecondarySkill > obligatory ;
2023-05-31 20:00:30 +03:00
for ( auto i = 0 ; i < VLC - > skillh - > size ( ) ; i + + )
2023-02-27 12:42:57 +03:00
if ( ( * VLC - > skillh ) [ SecondarySkill ( i ) ] - > obligatory ( obl ) )
2023-11-20 18:44:27 +02:00
obligatory . insert ( i ) ; //Always return all obligatory skills
2023-05-31 20:00:30 +03:00
2023-02-27 12:42:57 +03:00
return obligatory ;
} ;
2023-11-20 18:44:27 +02:00
auto intersect = [ ] ( const std : : set < SecondarySkill > & left , const std : : set < SecondarySkill > & right )
2023-02-27 12:42:57 +03:00
{
2023-11-20 18:44:27 +02:00
std : : set < SecondarySkill > intersect ;
std : : set_intersection ( left . begin ( ) , left . end ( ) , right . begin ( ) , right . end ( ) ,
std : : inserter ( intersect , intersect . begin ( ) ) ) ;
return intersect ;
2023-02-27 12:42:57 +03:00
} ;
2023-11-20 18:44:27 +02:00
std : : set < SecondarySkill > wisdomList = getObligatorySkills ( CSkill : : Obligatory : : MAJOR ) ;
std : : set < SecondarySkill > schoolList = getObligatorySkills ( CSkill : : Obligatory : : MINOR ) ;
2014-06-05 19:52:14 +03:00
2023-02-12 23:39:17 +03:00
std : : set < SecondarySkill > basicAndAdv ;
std : : set < SecondarySkill > none ;
2023-11-20 18:44:27 +02:00
2018-03-31 18:56:40 +13: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 23:39:17 +03:00
for ( const auto & elem : secSkills )
2014-06-05 19:52:14 +03:00
{
2023-10-05 16:13:52 +03:00
if ( elem . second < MasteryLevel : : EXPERT )
2014-06-05 19:52:14 +03:00
basicAndAdv . insert ( elem . first ) ;
none . erase ( elem . first ) ;
}
2023-11-20 18:44:27 +02:00
bool wantsWisdom = skillsInfo . wisdomCounter + 1 > = maxlevelsToWisdom ( ) ;
bool wantsSchool = skillsInfo . magicSchoolCounter + 1 > = maxlevelsToMagicSchool ( ) ;
2014-06-05 19:52:14 +03:00
2023-11-20 18:44:27 +02:00
std : : vector < SecondarySkill > skills ;
auto chooseSkill = [ & ] ( std : : set < SecondarySkill > & options )
2014-06-05 19:52:14 +03:00
{
2023-11-20 18:44:27 +02:00
bool selectWisdom = wantsWisdom & & ! intersect ( options , wisdomList ) . empty ( ) ;
bool selectSchool = wantsSchool & & ! intersect ( options , schoolList ) . empty ( ) ;
SecondarySkill selection ;
if ( selectWisdom )
2024-10-05 19:37:52 +00:00
selection = getHeroClass ( ) - > chooseSecSkill ( intersect ( options , wisdomList ) , rand ) ;
2023-11-20 18:44:27 +02:00
else if ( selectSchool )
2024-10-05 19:37:52 +00:00
selection = getHeroClass ( ) - > chooseSecSkill ( intersect ( options , schoolList ) , rand ) ;
2023-11-20 18:44:27 +02:00
else
2024-10-05 19:37:52 +00:00
selection = getHeroClass ( ) - > chooseSecSkill ( options , rand ) ;
2023-11-20 18:44:27 +02:00
skills . push_back ( selection ) ;
options . erase ( selection ) ;
if ( wisdomList . count ( selection ) )
wisdomList . clear ( ) ;
if ( schoolList . count ( selection ) )
schoolList . clear ( ) ;
} ;
if ( ! basicAndAdv . empty ( ) )
chooseSkill ( basicAndAdv ) ;
if ( canLearnSkill ( ) & & ! none . empty ( ) )
chooseSkill ( none ) ;
if ( ! basicAndAdv . empty ( ) & & skills . size ( ) < 2 )
chooseSkill ( basicAndAdv ) ;
if ( canLearnSkill ( ) & & ! none . empty ( ) & & skills . size ( ) < 2 )
chooseSkill ( none ) ;
2014-06-05 19:52:14 +03:00
return skills ;
}
2024-06-01 15:28:17 +00:00
PrimarySkill CGHeroInstance : : nextPrimarySkill ( vstd : : RNG & rand ) const
2014-06-05 19:52:14 +03:00
{
assert ( gainsLevel ( ) ) ;
2021-07-28 18:54:32 +03:00
const auto isLowLevelHero = level < GameConstants : : HERO_HIGH_LEVEL ;
2024-10-05 19:37:52 +00:00
const auto & skillChances = isLowLevelHero ? getHeroClass ( ) - > primarySkillLowLevel : getHeroClass ( ) - > primarySkillHighLevel ;
2014-06-05 19:52:14 +03:00
2024-01-31 00:18:10 +02:00
if ( isCampaignYog ( ) )
2014-06-05 19:52:14 +03:00
{
2024-01-31 00:18:10 +02:00
// Yog can only receive Attack or Defence on level-up
std : : vector < int > yogChances = { skillChances [ 0 ] , skillChances [ 1 ] } ;
return static_cast < PrimarySkill > ( RandomGeneratorUtil : : nextItemWeighted ( yogChances , rand ) ) ;
2014-06-05 19:52:14 +03:00
}
2024-01-31 00:18:10 +02:00
return static_cast < PrimarySkill > ( RandomGeneratorUtil : : nextItemWeighted ( skillChances , rand ) ) ;
2014-06-05 19:52:14 +03:00
}
2024-06-01 15:28:17 +00:00
std : : optional < SecondarySkill > CGHeroInstance : : nextSecondarySkill ( vstd : : RNG & rand ) const
2014-06-05 19:52:14 +03:00
{
assert ( gainsLevel ( ) ) ;
2023-04-16 20:42:56 +03:00
std : : optional < SecondarySkill > chosenSecondarySkill ;
2023-11-20 18:44:27 +02:00
const auto proposedSecondarySkills = getLevelUpProposedSecondarySkills ( rand ) ;
2014-06-05 19:52:14 +03:00
if ( ! proposedSecondarySkills . empty ( ) )
{
std : : vector < SecondarySkill > learnedSecondarySkills ;
2023-02-12 23:39:17 +03: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
2023-04-16 20:42:56 +03:00
chosenSecondarySkill = std : : make_optional ( * RandomGeneratorUtil : : nextItem ( proposedSecondarySkills , rand ) ) ;
2014-06-05 19:52:14 +03:00
}
else
{
// preferably upgrade a already learned secondary skill
2023-04-16 20:42:56 +03:00
chosenSecondarySkill = std : : make_optional ( * RandomGeneratorUtil : : nextItem ( learnedSecondarySkills , rand ) ) ;
2014-06-05 19:52:14 +03:00
}
}
return chosenSecondarySkill ;
}
2023-08-19 21:43:50 +03:00
void CGHeroInstance : : setPrimarySkill ( PrimarySkill primarySkill , si64 value , ui8 abs )
2014-06-05 19:52:14 +03:00
{
if ( primarySkill < PrimarySkill : : EXPERIENCE )
{
2024-01-16 18:14:40 +02:00
auto skill = getLocalBonus ( Selector : : type ( ) ( BonusType : : PRIMARY_SKILL )
2023-10-21 14:50:42 +03:00
. And ( Selector : : subtype ( ) ( BonusSubtypeID ( primarySkill ) ) )
2023-05-01 01:20:01 +03:00
. And ( Selector : : sourceType ( ) ( BonusSource : : HERO_BASE_SKILL ) ) ) ;
2014-06-05 19:52:14 +03:00
assert ( skill ) ;
if ( abs )
{
2020-10-01 01:38:06 -07:00
skill - > val = static_cast < si32 > ( value ) ;
2014-06-05 19:52:14 +03:00
}
else
{
2020-10-01 01:38:06 -07:00
skill - > val + = static_cast < si32 > ( value ) ;
2014-06-05 19:52:14 +03:00
}
2025-01-10 23:45:02 +00:00
nodeHasChanged ( ) ;
2014-06-05 19:52:14 +03:00
}
else if ( primarySkill = = PrimarySkill : : EXPERIENCE )
{
if ( abs )
{
exp = value ;
}
else
{
exp + = value ;
}
}
}
bool CGHeroInstance : : gainsLevel ( ) const
{
2024-01-04 23:57:36 +02:00
return level < VLC - > heroh - > maxSupportedLevel ( ) & & exp > = static_cast < TExpType > ( VLC - > heroh - > reqExp ( level + 1 ) ) ;
2014-06-05 19:52:14 +03:00
}
2023-02-12 23:39:17 +03:00
void CGHeroInstance : : levelUp ( const std : : vector < SecondarySkill > & skills )
2014-06-05 19:52:14 +03:00
{
+ + level ;
//deterministic secondary skills
2023-11-20 18:44:27 +02:00
+ + skillsInfo . magicSchoolCounter ;
+ + skillsInfo . wisdomCounter ;
2023-02-27 12:42:57 +03:00
for ( const auto & skill : skills )
2014-06-05 19:52:14 +03:00
{
2023-02-27 12:42:57 +03: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 12:26:48 +12:00
//update specialty and other bonuses that scale with level
2025-01-10 23:45:02 +00:00
nodeHasChanged ( ) ;
2014-06-05 19:52:14 +03:00
}
2024-06-01 15:28:17 +00:00
void CGHeroInstance : : levelUpAutomatically ( vstd : : RNG & rand )
2014-06-05 19:52:14 +03:00
{
while ( gainsLevel ( ) )
{
2016-09-09 20:30:36 +03:00
const auto primarySkill = nextPrimarySkill ( rand ) ;
2014-06-05 19:52:14 +03:00
setPrimarySkill ( primarySkill , 1 , false ) ;
2023-11-20 18:44:27 +02:00
auto proposedSecondarySkills = getLevelUpProposedSecondarySkills ( rand ) ;
2014-06-05 19:52:14 +03:00
2016-09-09 20:30:36 +03: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 17:36:09 +03:00
2023-10-21 14:50:42 +03:00
bool CGHeroInstance : : hasVisions ( const CGObjectInstance * target , BonusSubtypeID subtype ) const
2015-02-06 17:36:09 +03:00
{
//VISIONS spell support
2023-10-05 16:13:52 +03:00
const int visionsMultiplier = valOfBonuses ( BonusType : : VISIONS , subtype ) ;
2016-01-24 02:27:14 +03:00
2015-02-06 17:36:09 +03:00
int visionsRange = visionsMultiplier * getPrimSkillLevel ( PrimarySkill : : SPELL_POWER ) ;
2016-01-24 02:27:14 +03:00
if ( visionsMultiplier > 0 )
2015-02-06 17:36:09 +03:00
vstd : : amax ( visionsRange , 3 ) ; //minimum range is 3 tiles, but only if VISIONS bonus present
2016-01-24 02:27:14 +03:00
2024-10-02 16:40:06 +00:00
const int distance = static_cast < int > ( target - > anchorPos ( ) . dist2d ( visitablePos ( ) ) ) ;
2016-01-24 02:27:14 +03:00
2023-08-20 23:45:41 +03:00
//logGlobal->debug(boost::str(boost::format("Visions: dist %d, mult %d, range %d") % distance % visionsMultiplier % visionsRange));
2016-01-24 02:27:14 +03:00
2024-10-02 16:40:06 +00:00
return ( distance < visionsRange ) & & ( target - > anchorPos ( ) . z = = anchorPos ( ) . z ) ;
2016-01-24 02:27:14 +03:00
}
2016-11-13 13:38:42 +03:00
std : : string CGHeroInstance : : getHeroTypeName ( ) const
2016-01-24 02:27:14 +03:00
{
2016-11-13 13:38:42 +03:00
if ( ID = = Obj : : HERO | | ID = = Obj : : PRISON )
2024-10-05 19:37:52 +00:00
return getHeroType ( ) - > getJsonKey ( ) ;
2016-11-13 13:38:42 +03:00
return " " ;
}
2017-05-28 16:23:42 +03:00
void CGHeroInstance : : afterAddToMap ( CMap * map )
{
2023-11-01 16:10:33 +02:00
if ( ID ! = Obj : : PRISON )
2023-02-12 23:39:17 +03:00
map - > heroesOnMap . emplace_back ( this ) ;
2017-05-28 16:23:42 +03:00
}
2022-09-17 15:04:01 +04:00
void CGHeroInstance : : afterRemoveFromMap ( CMap * map )
{
2023-11-01 16:10:33 +02:00
if ( ID = = Obj : : PRISON )
2022-09-17 15:04:01 +04:00
vstd : : erase_if_present ( map - > heroesOnMap , this ) ;
}
2017-05-28 16:23:42 +03:00
2016-11-13 13:38:42 +03:00
void CGHeroInstance : : setHeroTypeName ( const std : : string & identifier )
{
if ( ID = = Obj : : HERO | | ID = = Obj : : PRISON )
2016-02-14 11:25:01 +03:00
{
2023-07-30 20:12:25 +03:00
auto rawId = VLC - > identifiers ( ) - > getIdentifier ( ModScope : : scopeMap ( ) , " hero " , identifier ) ;
2016-11-13 13:38:42 +03:00
if ( rawId )
2023-04-16 20:42:56 +03:00
subID = rawId . value ( ) ;
2016-11-13 13:38:42 +03:00
else
2022-09-09 21:02:16 +02:00
{
throw std : : runtime_error ( " Couldn't resolve hero identifier " + identifier ) ;
}
2016-11-13 13:38:42 +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 17:58:30 +03:00
void CGHeroInstance : : updateFrom ( const JsonNode & data )
{
CGObjectInstance : : updateFrom ( data ) ;
}
2016-11-13 13:38:42 +03:00
void CGHeroInstance : : serializeCommonOptions ( JsonSerializeFormat & handler )
{
2023-09-28 00:00:32 +02:00
handler . serializeString ( " biography " , biographyCustomTextId ) ;
2016-11-13 13:38:42 +03:00
handler . serializeInt ( " experience " , exp , 0 ) ;
2022-03-11 11:15:44 -08: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 11:15:44 -08:00
{
while ( gainsLevel ( ) )
{
+ + level ;
}
}
2023-09-28 00:00:32 +02:00
handler . serializeString ( " name " , nameCustomTextId ) ;
2023-04-02 19:56:10 +03:00
handler . serializeInt ( " gender " , gender , 0 ) ;
2023-09-28 19:43:04 +03:00
handler . serializeId ( " portrait " , customPortraitSource , HeroTypeID : : NONE ) ;
2015-02-06 17:36:09 +03:00
2017-06-06 02:37:07 +03:00
//primary skills
if ( handler . saving )
2016-02-14 11:25:01 +03:00
{
2023-05-01 01:20:01 +03:00
const bool haveSkills = hasBonus ( Selector : : type ( ) ( BonusType : : PRIMARY_SKILL ) . And ( Selector : : sourceType ( ) ( BonusSource : : HERO_BASE_SKILL ) ) ) ;
2017-06-06 02:37:07 +03:00
if ( haveSkills )
2016-11-13 13:38:42 +03:00
{
2017-06-06 02:37:07 +03:00
auto primarySkills = handler . enterStruct ( " primarySkills " ) ;
2016-11-13 13:38:42 +03:00
2024-12-22 14:49:35 +00:00
for ( auto skill : PrimarySkill : : ALL_SKILLS ( ) )
2016-11-13 13:38:42 +03:00
{
2024-12-22 14:49:35 +00:00
int value = valOfBonuses ( Selector : : typeSubtype ( BonusType : : PRIMARY_SKILL , BonusSubtypeID ( skill ) ) . And ( Selector : : sourceType ( ) ( BonusSource : : HERO_BASE_SKILL ) ) ) ;
2016-11-13 13:38:42 +03:00
2024-12-22 14:49:35 +00:00
handler . serializeInt ( NPrimarySkill : : names [ skill . getNum ( ) ] , value , 0 ) ;
2016-11-13 13:38:42 +03:00
}
}
2017-06-06 02:37:07 +03:00
}
else
{
auto primarySkills = handler . enterStruct ( " primarySkills " ) ;
2016-11-13 13:38:42 +03:00
2017-07-20 07:08:49 +03:00
if ( handler . getCurrent ( ) . getType ( ) = = JsonNode : : JsonType : : DATA_STRUCT )
2017-06-06 02:37:07 +03:00
{
2016-11-13 13:38:42 +03:00
for ( int i = 0 ; i < GameConstants : : PRIMARY_SKILLS ; + + i )
{
int value = 0 ;
2023-08-19 21:43:50 +03:00
handler . serializeInt ( NPrimarySkill : : names [ i ] , value , 0 ) ;
pushPrimSkill ( static_cast < PrimarySkill > ( i ) , value ) ;
2016-11-13 13:38:42 +03:00
}
}
2016-02-14 11:25:01 +03:00
}
2016-11-13 13:38:42 +03:00
//secondary skills
if ( handler . saving )
{
//does hero have default skills?
bool defaultSkills = false ;
bool normalSkills = false ;
for ( const auto & p : secSkills )
{
2023-09-30 18:47:47 +03:00
if ( p . first = = SecondarySkill ( SecondarySkill : : NONE ) )
2016-11-13 13:38:42 +03:00
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 )
{
2023-08-14 00:06:22 +03:00
//enter array here as handler initialize it
auto secondarySkills = handler . enterArray ( " secondarySkills " ) ;
secondarySkills . syncSize ( secSkills , JsonNode : : JsonType : : DATA_VECTOR ) ;
2016-11-13 13:38:42 +03:00
2023-08-14 00:06:22 +03:00
for ( size_t skillIndex = 0 ; skillIndex < secondarySkills . size ( ) ; + + skillIndex )
2016-11-13 13:38:42 +03:00
{
2023-08-14 00:06:22 +03:00
JsonArraySerializer inner = secondarySkills . enterArray ( skillIndex ) ;
2023-11-05 18:58:07 +02:00
SecondarySkill skillId = secSkills . at ( skillIndex ) . first ;
2016-11-13 13:38:42 +03:00
2023-11-05 18:58:07 +02:00
handler . serializeId ( " skill " , skillId ) ;
std : : string skillLevel = NSecondarySkill : : levels . at ( secSkills . at ( skillIndex ) . second ) ;
handler . serializeString ( " level " , skillLevel ) ;
2016-11-13 13:38:42 +03:00
}
}
}
else
{
2023-08-14 00:06:22 +03:00
auto secondarySkills = handler . getCurrent ( ) [ " secondarySkills " ] ;
2016-11-13 13:38:42 +03:00
secSkills . clear ( ) ;
2023-08-14 00:06:22 +03:00
if ( secondarySkills . getType ( ) = = JsonNode : : JsonType : : DATA_NULL )
2016-11-13 13:38:42 +03:00
{
2023-09-30 18:47:47 +03:00
secSkills . emplace_back ( SecondarySkill : : NONE , - 1 ) ;
2016-11-13 13:38:42 +03:00
}
else
{
2023-08-14 00:06:22 +03:00
auto addSkill = [ this ] ( const std : : string & skillId , const std : : string & levelId )
2016-11-13 13:38:42 +03:00
{
2023-11-02 17:48:48 +02:00
const int rawId = SecondarySkill : : decode ( skillId ) ;
2016-11-13 13:38:42 +03:00
if ( rawId < 0 )
{
2017-08-11 20:03:05 +03:00
logGlobal - > error ( " Invalid secondary skill %s " , skillId ) ;
2023-08-14 00:06:22 +03:00
return ;
2016-11-13 13:38:42 +03:00
}
const int level = vstd : : find_pos ( NSecondarySkill : : levels , levelId ) ;
if ( level < 0 )
{
2017-08-11 20:03:05 +03:00
logGlobal - > error ( " Invalid secondary skill level%s " , levelId ) ;
2023-08-14 00:06:22 +03:00
return ;
2016-11-13 13:38:42 +03:00
}
2023-02-12 23:39:17 +03:00
secSkills . emplace_back ( SecondarySkill ( rawId ) , level ) ;
2023-08-14 00:06:22 +03:00
} ;
if ( secondarySkills . getType ( ) = = JsonNode : : JsonType : : DATA_VECTOR )
{
for ( const auto & p : secondarySkills . Vector ( ) )
{
auto skillMap = p . Struct ( ) ;
addSkill ( skillMap [ " skill " ] . String ( ) , skillMap [ " level " ] . String ( ) ) ;
}
}
else if ( secondarySkills . getType ( ) = = JsonNode : : JsonType : : DATA_STRUCT )
{
for ( const auto & p : secondarySkills . Struct ( ) )
{
addSkill ( p . first , p . second . String ( ) ) ;
} ;
2016-11-13 13:38:42 +03:00
}
}
}
2017-07-20 07:08:49 +03:00
handler . serializeIdArray ( " spellBook " , spells ) ;
2016-11-13 13:38:42 +03:00
if ( handler . saving )
2024-09-04 14:32:47 +03:00
CArtifactSet : : serializeJsonArtifacts ( handler , " artifacts " ) ;
2016-11-13 13:38:42 +03:00
}
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 ) ;
}
2024-08-02 15:56:34 +00:00
if ( ! handler . saving )
{
if ( ! appearance )
{
// crossoverDeserialize
2024-10-05 19:37:52 +00:00
appearance = VLC - > objtypeh - > getHandlerFor ( Obj : : HERO , getHeroClassID ( ) ) - > getTemplates ( ) . front ( ) ;
2024-08-02 15:56:34 +00:00
}
}
2023-09-17 22:19:45 +02:00
CArmedInstance : : serializeJsonOptions ( handler ) ;
2016-11-13 13:38:42 +03:00
{
2024-10-29 18:34:02 +01:00
ui32 rawPatrolRadius = NO_PATROLLING ;
2016-11-13 13:38:42 +03:00
if ( handler . saving )
{
2024-10-28 18:30:30 +01:00
rawPatrolRadius = patrol . patrolling ? patrol . patrolRadius : NO_PATROLLING ;
2016-11-13 13:38:42 +03:00
}
2024-10-28 18:30:30 +01:00
handler . serializeInt ( " patrolRadius " , rawPatrolRadius , NO_PATROLLING ) ;
2016-11-13 13:38:42 +03:00
if ( ! handler . saving )
{
2024-10-29 18:34:02 +01:00
patrol . patrolling = ( rawPatrolRadius ! = NO_PATROLLING ) ;
2022-12-09 14:42:47 +02:00
patrol . initialPos = visitablePos ( ) ;
2024-10-29 18:34:02 +01:00
patrol . patrolRadius = patrol . patrolling ? rawPatrolRadius : 0 ;
2016-11-13 13:38:42 +03:00
}
}
}
void CGHeroInstance : : serializeJsonDefinition ( JsonSerializeFormat & handler )
{
serializeCommonOptions ( handler ) ;
2016-01-24 02:27:14 +03:00
}
2016-02-14 12:13:30 +03:00
2016-01-27 13:47:42 +03:00
bool CGHeroInstance : : isMissionCritical ( ) const
{
2024-01-01 16:37:48 +02:00
for ( const TriggeredEvent & event : cb - > getMapHeader ( ) - > triggeredEvents )
2016-01-27 13:47:42 +03:00
{
2023-07-30 15:28:56 +03:00
if ( event . effect . type ! = EventEffect : : DEFEAT )
continue ;
auto const & testFunctor = [ & ] ( const EventCondition & condition )
2016-01-27 13:47:42 +03:00
{
2023-11-17 16:39:15 +02:00
if ( ( condition . condition = = EventCondition : : CONTROL ) & & condition . objectID ! = ObjectInstanceID : : NONE )
return ( id ! = condition . objectID ) ;
2023-07-30 15:28:56 +03:00
2024-01-31 12:52:16 +02:00
if ( condition . condition = = EventCondition : : HAVE_ARTIFACT )
{
if ( hasArt ( condition . objectType . as < ArtifactID > ( ) ) )
return true ;
}
2023-07-30 15:28:56 +03:00
if ( condition . condition = = EventCondition : : IS_HUMAN )
2016-01-27 13:47:42 +03:00
return true ;
2023-07-30 15:28:56 +03:00
2016-01-27 13:47:42 +03:00
return false ;
2023-07-30 15:28:56 +03:00
} ;
if ( event . trigger . test ( testFunctor ) )
2016-01-27 13:47:42 +03:00
return true ;
}
return false ;
}
2022-07-26 16:07:42 +03:00
2024-12-16 16:05:46 +01:00
void CGHeroInstance : : fillUpgradeInfo ( UpgradeInfo & info , const CStackInstance & stack ) const
2023-06-06 19:19:30 +03:00
{
2024-12-21 18:47:11 +00:00
TConstBonusListPtr lista = getBonusesOfType ( BonusType : : SPECIAL_UPGRADE , BonusSubtypeID ( stack . getId ( ) ) ) ;
2023-06-06 19:19:30 +03:00
for ( const auto & it : * lista )
{
auto nid = CreatureID ( it - > additionalInfo [ 0 ] ) ;
2024-10-12 16:02:35 +00:00
if ( nid ! = stack . getId ( ) ) //in very specific case the upgrade is available by default (?)
2023-06-06 19:19:30 +03:00
{
2024-12-16 16:05:46 +01:00
info . addUpgrade ( nid , stack . getType ( ) ) ;
2023-06-06 19:19:30 +03:00
}
}
}
2024-01-30 23:33:58 +02:00
bool CGHeroInstance : : isCampaignYog ( ) const
{
const StartInfo * si = cb - > getStartInfo ( ) ;
// it would be nice to find a way to move this hack to config/mapOverrides.json
if ( ! si | | ! si - > campState )
return false ;
std : : string campaign = si - > campState - > getFilename ( ) ;
if ( ! boost : : starts_with ( campaign , " DATA/YOG " ) ) // "Birth of a Barbarian"
return false ;
2024-10-05 19:37:52 +00:00
if ( getHeroTypeID ( ) ! = HeroTypeID : : SOLMYR ) // Yog (based on Solmyr)
2024-01-30 23:33:58 +02:00
return false ;
return true ;
}
bool CGHeroInstance : : isCampaignGem ( ) const
{
const StartInfo * si = cb - > getStartInfo ( ) ;
// it would be nice to find a way to move this hack to config/mapOverrides.json
if ( ! si | | ! si - > campState )
return false ;
std : : string campaign = si - > campState - > getFilename ( ) ;
if ( ! boost : : starts_with ( campaign , " DATA/GEM " ) & & ! boost : : starts_with ( campaign , " DATA/FINAL " ) ) // "New Beginning" and "Unholy Alliance"
return false ;
2024-10-05 19:37:52 +00:00
if ( getHeroTypeID ( ) ! = HeroTypeID : : GEM ) // Yog (based on Solmyr)
2024-01-30 23:33:58 +02:00
return false ;
return true ;
}
2024-08-24 20:42:19 +00:00
ResourceSet CGHeroInstance : : dailyIncome ( ) const
{
ResourceSet income ;
for ( GameResID k : GameResID : : ALL_RESOURCES ( ) )
income [ k ] + = valOfBonuses ( BonusType : : GENERATE_RESOURCE , BonusSubtypeID ( k ) ) ;
const auto & playerSettings = cb - > getPlayerSettings ( getOwner ( ) ) ;
income . applyHandicap ( playerSettings - > handicap . percentIncome ) ;
return income ;
}
2024-08-25 15:04:44 +00:00
std : : vector < CreatureID > CGHeroInstance : : providedCreatures ( ) const
{
return { } ;
}
2024-08-24 20:42:19 +00:00
const IOwnableObject * CGHeroInstance : : asOwnable ( ) const
{
return this ;
}
2024-10-21 09:37:44 +02:00
int CGHeroInstance : : getBasePrimarySkillValue ( PrimarySkill which ) const
{
2024-12-21 18:47:11 +00:00
std : : string cachingStr = " CGHeroInstance::getBasePrimarySkillValue " + std : : to_string ( static_cast < int > ( which ) ) ;
2024-10-21 09:37:44 +02:00
auto selector = Selector : : typeSubtype ( BonusType : : PRIMARY_SKILL , BonusSubtypeID ( which ) ) . And ( Selector : : sourceType ( ) ( BonusSource : : HERO_BASE_SKILL ) ) ;
2024-12-18 13:38:38 +00:00
auto minSkillValue = VLC - > engineSettings ( ) - > getVectorValue ( EGameSettings : : HEROES_MINIMAL_PRIMARY_SKILLS , which . getNum ( ) ) ;
2024-12-08 17:49:41 +01:00
return std : : max ( valOfBonuses ( selector , cachingStr ) , minSkillValue ) ;
2024-10-21 09:37:44 +02:00
}
2024-08-24 20:42:19 +00:00
2022-07-26 16:07:42 +03:00
VCMI_LIB_NAMESPACE_END