| 
									
										
										
										
											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>
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-06-05 20:26:50 +03:00
										 |  |  | #include "../NetPacks.h"
 | 
					
						
							|  |  |  | #include "../CGeneralTextHandler.h"
 | 
					
						
							| 
									
										
										
										
											2023-05-17 16:52:16 +03:00
										 |  |  | #include "../ArtifactUtils.h"
 | 
					
						
							| 
									
										
										
										
											2014-06-05 20:26:50 +03:00
										 |  |  | #include "../CHeroHandler.h"
 | 
					
						
							| 
									
										
										
										
											2023-01-09 01:17:37 +02:00
										 |  |  | #include "../TerrainHandler.h"
 | 
					
						
							| 
									
										
										
										
											2023-01-11 15:17:24 +02:00
										 |  |  | #include "../RoadHandler.h"
 | 
					
						
							| 
									
										
										
										
											2023-03-15 21:34:29 +02:00
										 |  |  | #include "../GameSettings.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"
 | 
					
						
							| 
									
										
										
										
											2014-06-25 17:11:07 +03:00
										 |  |  | #include "../CCreatureHandler.h"
 | 
					
						
							| 
									
										
										
										
											2015-12-02 21:05:10 +02:00
										 |  |  | #include "../CTownHandler.h"
 | 
					
						
							| 
									
										
										
										
											2016-01-27 13:47:42 +03:00
										 |  |  | #include "../mapping/CMap.h"
 | 
					
						
							| 
									
										
										
										
											2015-12-02 21:05:10 +02:00
										 |  |  | #include "CGTownInstance.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"
 | 
					
						
							| 
									
										
										
										
											2023-07-30 20:12:25 +03:00
										 |  |  | #include "../modding/ModScope.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"
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-07-26 16:07:42 +03:00
										 |  |  | VCMI_LIB_NAMESPACE_BEGIN | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-06-05 19:52:14 +03:00
										 |  |  | static int lowestSpeed(const CGHeroInstance * chi) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2023-05-01 01:20:01 +03:00
										 |  |  | 	static const CSelector selectorSTACKS_SPEED = Selector::type()(BonusType::STACKS_SPEED); | 
					
						
							|  |  |  | 	static const std::string keySTACKS_SPEED = "type_" + std::to_string(static_cast<si32>(BonusType::STACKS_SPEED)); | 
					
						
							| 
									
										
										
										
											2022-04-23 16:45:38 +03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-12-24 21:30:57 +03:00
										 |  |  | 	if(!chi->stacksCount()) | 
					
						
							| 
									
										
										
										
											2014-06-05 19:52:14 +03:00
										 |  |  | 	{ | 
					
						
							| 
									
										
										
										
											2022-04-23 16:45:38 +03:00
										 |  |  | 		if(chi->commander && chi->commander->alive) | 
					
						
							|  |  |  | 		{ | 
					
						
							|  |  |  | 			return chi->commander->valOfBonuses(selectorSTACKS_SPEED, keySTACKS_SPEED); | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-01-02 13:27:03 +02:00
										 |  |  | 		logGlobal->error("Hero %d (%s) has no army!", chi->id.getNum(), chi->getNameTranslated()); | 
					
						
							| 
									
										
										
										
											2014-06-05 19:52:14 +03:00
										 |  |  | 		return 20; | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2022-04-23 16:45:38 +03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-06-05 19:52:14 +03:00
										 |  |  | 	auto i = chi->Slots().begin(); | 
					
						
							|  |  |  | 	//TODO? should speed modifiers (eg from artifacts) affect hero movement?
 | 
					
						
							| 
									
										
										
										
											2019-01-15 06:00:00 +03:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	int ret = (i++)->second->valOfBonuses(selectorSTACKS_SPEED, keySTACKS_SPEED); | 
					
						
							| 
									
										
										
										
											2017-08-11 20:03:05 +03:00
										 |  |  | 	for(; i != chi->Slots().end(); i++) | 
					
						
							| 
									
										
										
										
											2019-01-15 06:00:00 +03:00
										 |  |  | 		ret = std::min(ret, i->second->valOfBonuses(selectorSTACKS_SPEED, keySTACKS_SPEED)); | 
					
						
							| 
									
										
										
										
											2014-06-05 19:52:14 +03:00
										 |  |  | 	return ret; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											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
 | 
					
						
							| 
									
										
										
										
											2023-01-01 17:10:47 +02:00
										 |  |  | 	if(dest.roadType->getId() != Road::NO_ROAD && from.roadType->getId() != Road::NO_ROAD) | 
					
						
							| 
									
										
										
										
											2014-06-05 19:52:14 +03:00
										 |  |  | 	{ | 
					
						
							| 
									
										
										
										
											2023-03-12 19:01:54 +03:00
										 |  |  | 		ret = from.roadType->movementCost; | 
					
						
							| 
									
										
										
										
											2014-06-05 19:52:14 +03:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2023-01-01 17:10:47 +02:00
										 |  |  | 	else if(ti->nativeTerrain != from.terType->getId() &&//the terrain is not native
 | 
					
						
							| 
									
										
										
										
											2022-12-20 18:35:40 +02:00
										 |  |  | 			ti->nativeTerrain != ETerrainId::ANY_TERRAIN && //no special creature bonus
 | 
					
						
							| 
									
										
										
										
											2023-05-01 01:20:01 +03:00
										 |  |  | 			!ti->hasBonusOfType(BonusType::NO_TERRAIN_PENALTY, from.terType->getIndex())) //no special movement bonus
 | 
					
						
							| 
									
										
										
										
											2014-06-05 19:52:14 +03:00
										 |  |  | 	{ | 
					
						
							| 
									
										
										
										
											2019-01-15 06:00:00 +03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-09-13 18:54:08 +02:00
										 |  |  | 		ret = VLC->terrainTypeHandler->getById(from.terType->getId())->moveCost; | 
					
						
							| 
									
										
										
										
											2023-05-01 01:20:01 +03:00
										 |  |  | 		ret -= ti->valOfBonuses(BonusType::ROUGH_TERRAIN_DISCOUNT); | 
					
						
							| 
									
										
										
										
											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
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-04-05 18:56:28 +03:00
										 |  |  | FactionID CGHeroInstance::getFaction() const | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	return FactionID(type->heroClass->faction); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 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; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											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; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (!cb->isAllowed(2, which)) | 
					
						
							|  |  |  | 		return false; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (getSecSkillLevel(which) > 0) | 
					
						
							|  |  |  | 		return false; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (type->heroClass->secSkillProbability[which] == 0) | 
					
						
							|  |  |  | 		return false; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return true; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-06-21 20:38:26 +03:00
										 |  |  | int CGHeroInstance::movementPointsRemaining() const | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	return movement; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void CGHeroInstance::setMovementPoints(int points) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	movement = std::max(0, points); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | int CGHeroInstance::movementPointsLimit(bool onLand) const | 
					
						
							| 
									
										
										
										
											2014-06-05 19:52:14 +03:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2019-01-15 08:52:55 +03:00
										 |  |  | 	TurnInfo ti(this); | 
					
						
							| 
									
										
										
										
											2023-06-21 20:38:26 +03:00
										 |  |  | 	return movementPointsLimitCached(onLand, &ti); | 
					
						
							| 
									
										
										
										
											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
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2023-03-11 23:09:16 +03:00
										 |  |  | 	return lowestCreatureSpeed; | 
					
						
							| 
									
										
										
										
											2023-02-18 21:01:32 +03:00
										 |  |  | } | 
					
						
							| 
									
										
										
										
											2014-06-05 19:52:14 +03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-02-18 21:01:32 +03:00
										 |  |  | void CGHeroInstance::updateArmyMovementBonus(bool onLand, const TurnInfo * ti) const | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2023-03-11 23:09:16 +03:00
										 |  |  | 	auto realLowestSpeed = lowestSpeed(this); | 
					
						
							|  |  |  | 	if(lowestCreatureSpeed != realLowestSpeed) | 
					
						
							| 
									
										
										
										
											2014-06-05 19:52:14 +03:00
										 |  |  | 	{ | 
					
						
							| 
									
										
										
										
											2023-03-11 23:09:16 +03:00
										 |  |  | 		lowestCreatureSpeed = realLowestSpeed; | 
					
						
							| 
									
										
										
										
											2023-03-12 14:39:36 +03:00
										 |  |  | 		//Let updaters run again
 | 
					
						
							|  |  |  | 		treeHasChanged(); | 
					
						
							| 
									
										
										
										
											2023-05-01 01:20:01 +03:00
										 |  |  | 		ti->updateHeroBonuses(BonusType::MOVEMENT, Selector::subtype()(!!onLand)); | 
					
						
							| 
									
										
										
										
											2014-06-05 19:52:14 +03:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											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
										 |  |  | { | 
					
						
							|  |  |  | 	updateArmyMovementBonus(onLand, ti); | 
					
						
							| 
									
										
										
										
											2023-05-01 01:20:01 +03:00
										 |  |  | 	return ti->valOfBonuses(BonusType::MOVEMENT, !!onLand); | 
					
						
							| 
									
										
										
										
											2014-06-05 19:52:14 +03:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-02-12 23:39:17 +03:00
										 |  |  | CGHeroInstance::CGHeroInstance(): | 
					
						
							|  |  |  | 	tacticFormationEnabled(false), | 
					
						
							|  |  |  | 	inTownGarrison(false), | 
					
						
							|  |  |  | 	moveDir(4), | 
					
						
							|  |  |  | 	mana(UNINITIALIZED_MANA), | 
					
						
							|  |  |  | 	movement(UNINITIALIZED_MOVEMENT), | 
					
						
							|  |  |  | 	portrait(UNINITIALIZED_PORTRAIT), | 
					
						
							|  |  |  | 	level(1), | 
					
						
							| 
									
										
										
										
											2023-02-15 12:10:39 +02:00
										 |  |  | 	exp(UNINITIALIZED_EXPERIENCE), | 
					
						
							| 
									
										
										
										
											2023-04-02 19:56:10 +03:00
										 |  |  | 	gender(EHeroGender::DEFAULT), | 
					
						
							| 
									
										
										
										
											2023-03-11 23:09:16 +03:00
										 |  |  | 	lowestCreatureSpeed(0) | 
					
						
							| 
									
										
										
										
											2014-06-05 19:52:14 +03:00
										 |  |  | { | 
					
						
							|  |  |  | 	setNodeType(HERO); | 
					
						
							|  |  |  | 	ID = Obj::HERO; | 
					
						
							| 
									
										
										
										
											2023-02-12 23:39:17 +03:00
										 |  |  | 	secSkills.emplace_back(SecondarySkill::DEFAULT, -1); | 
					
						
							| 
									
										
										
										
											2023-07-22 22:04:32 +03:00
										 |  |  | 	blockVisit = true; | 
					
						
							| 
									
										
										
										
											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; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-02-12 23:39:17 +03:00
										 |  |  | void CGHeroInstance::initHero(CRandomGenerator & 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
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void CGHeroInstance::setType(si32 ID, si32 subID) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	assert(ID == Obj::HERO); // just in case
 | 
					
						
							| 
									
										
										
											
												Entities redesign and a few ERM features
* Made most Handlers derived from CHandlerBase and moved service API there.
* Declared existing Entity APIs.
* Added basic script context caching
* Started Lua script module
* Started Lua spell effect API
* Started script state persistence
* Started battle info callback binding
* CommitPackage removed
* Extracted spells::Caster to own header; Expanded Spell API.
* implemented !!MC:S, !!FU:E, !!FU:P, !!MA, !!VR:H, !!VR:C
* !!BU:C, !!BU:E, !!BU:G, !!BU:M implemented
* Allow use of "MC:S@varName@" to declare normal variable (technically v-variable with string key)
* Re-enabled VERM macros.
* !?GM0 added
* !?TM implemented
* Added !!MF:N
* Started !?OB, !!BM, !!HE, !!OW, !!UN
* Added basic support of w-variables
* Added support for ERM indirect variables
* Made !?FU regular trigger
* !!re (ERA loop receiver) implemented
* Fixed ERM receivers with zero args.
											
										 
											2018-03-17 17:58:30 +03:00
										 |  |  | 	type = VLC->heroh->objects[subID]; | 
					
						
							| 
									
										
										
										
											2014-06-05 19:52:14 +03:00
										 |  |  | 	portrait = type->imageIndex; | 
					
						
							| 
									
										
										
											
												Entities redesign and a few ERM features
* Made most Handlers derived from CHandlerBase and moved service API there.
* Declared existing Entity APIs.
* Added basic script context caching
* Started Lua script module
* Started Lua spell effect API
* Started script state persistence
* Started battle info callback binding
* CommitPackage removed
* Extracted spells::Caster to own header; Expanded Spell API.
* implemented !!MC:S, !!FU:E, !!FU:P, !!MA, !!VR:H, !!VR:C
* !!BU:C, !!BU:E, !!BU:G, !!BU:M implemented
* Allow use of "MC:S@varName@" to declare normal variable (technically v-variable with string key)
* Re-enabled VERM macros.
* !?GM0 added
* !?TM implemented
* Added !!MF:N
* Started !?OB, !!BM, !!HE, !!OW, !!UN
* Added basic support of w-variables
* Added support for ERM indirect variables
* Made !?FU regular trigger
* !!re (ERA loop receiver) implemented
* Fixed ERM receivers with zero args.
											
										 
											2018-03-17 17:58:30 +03:00
										 |  |  | 	CGObjectInstance::setType(ID, type->heroClass->getIndex()); // to find object handler we must use heroClass->id
 | 
					
						
							| 
									
										
										
										
											2016-09-18 10:01:09 +03:00
										 |  |  | 	this->subID = subID; // after setType subID used to store unique hero identify id. Check issue 2277 for details
 | 
					
						
							| 
									
										
										
										
											2014-06-05 19:52:14 +03:00
										 |  |  | 	randomizeArmy(type->heroClass->faction); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-09-09 20:30:36 +03:00
										 |  |  | void CGHeroInstance::initHero(CRandomGenerator & rand) | 
					
						
							| 
									
										
										
										
											2014-06-05 19:52:14 +03:00
										 |  |  | { | 
					
						
							|  |  |  | 	assert(validTypes(true)); | 
					
						
							|  |  |  | 	if(!type) | 
					
						
							| 
									
										
										
											
												Entities redesign and a few ERM features
* Made most Handlers derived from CHandlerBase and moved service API there.
* Declared existing Entity APIs.
* Added basic script context caching
* Started Lua script module
* Started Lua spell effect API
* Started script state persistence
* Started battle info callback binding
* CommitPackage removed
* Extracted spells::Caster to own header; Expanded Spell API.
* implemented !!MC:S, !!FU:E, !!FU:P, !!MA, !!VR:H, !!VR:C
* !!BU:C, !!BU:E, !!BU:G, !!BU:M implemented
* Allow use of "MC:S@varName@" to declare normal variable (technically v-variable with string key)
* Re-enabled VERM macros.
* !?GM0 added
* !?TM implemented
* Added !!MF:N
* Started !?OB, !!BM, !!HE, !!OW, !!UN
* Added basic support of w-variables
* Added support for ERM indirect variables
* Made !?FU regular trigger
* !!re (ERA loop receiver) implemented
* Fixed ERM receivers with zero args.
											
										 
											2018-03-17 17:58:30 +03:00
										 |  |  | 		type = VLC->heroh->objects[subID]; | 
					
						
							| 
									
										
										
										
											2014-06-05 19:52:14 +03:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	if (ID == Obj::HERO) | 
					
						
							| 
									
										
										
											
												Entities redesign and a few ERM features
* Made most Handlers derived from CHandlerBase and moved service API there.
* Declared existing Entity APIs.
* Added basic script context caching
* Started Lua script module
* Started Lua spell effect API
* Started script state persistence
* Started battle info callback binding
* CommitPackage removed
* Extracted spells::Caster to own header; Expanded Spell API.
* implemented !!MC:S, !!FU:E, !!FU:P, !!MA, !!VR:H, !!VR:C
* !!BU:C, !!BU:E, !!BU:G, !!BU:M implemented
* Allow use of "MC:S@varName@" to declare normal variable (technically v-variable with string key)
* Re-enabled VERM macros.
* !?GM0 added
* !?TM implemented
* Added !!MF:N
* Started !?OB, !!BM, !!HE, !!OW, !!UN
* Added basic support of w-variables
* Added support for ERM indirect variables
* Made !?FU regular trigger
* !!re (ERA loop receiver) implemented
* Fixed ERM receivers with zero args.
											
										 
											2018-03-17 17:58:30 +03:00
										 |  |  | 		appearance = VLC->objtypeh->getHandlerFor(Obj::HERO, type->heroClass->getIndex())->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
 | 
					
						
							| 
									
										
										
										
											2023-02-12 23:39:17 +03:00
										 |  |  | 		for(const auto & spellID : type->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
 | 
					
						
							|  |  |  | 		if(!getArt(ArtifactPosition::SPELLBOOK) && type->haveSpellBook) | 
					
						
							|  |  |  | 			putArtifact(ArtifactPosition::SPELLBOOK, ArtifactUtils::createNewArtifactInstance(ArtifactID::SPELLBOOK)); | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	else | 
					
						
							|  |  |  | 		spells -= SpellID::SPELLBOOK_PRESET; | 
					
						
							| 
									
										
										
										
											2014-06-05 19:52:14 +03:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	if(!getArt(ArtifactPosition::MACH4)) | 
					
						
							| 
									
										
										
										
											2023-05-17 16:52:16 +03:00
										 |  |  | 		putArtifact(ArtifactPosition::MACH4, ArtifactUtils::createNewArtifactInstance(ArtifactID::CATAPULT)); //everyone has a catapult
 | 
					
						
							| 
									
										
										
										
											2014-06-05 19:52:14 +03:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	if(portrait < 0 || portrait == 255) | 
					
						
							|  |  |  | 		portrait = type->imageIndex; | 
					
						
							| 
									
										
										
										
											2023-05-01 01:20:01 +03:00
										 |  |  | 	if(!hasBonus(Selector::sourceType()(BonusSource::HERO_BASE_SKILL))) | 
					
						
							| 
									
										
										
										
											2014-06-05 19:52:14 +03:00
										 |  |  | 	{ | 
					
						
							|  |  |  | 		for(int g=0; g<GameConstants::PRIMARY_SKILLS; ++g) | 
					
						
							|  |  |  | 		{ | 
					
						
							| 
									
										
										
										
											2023-08-19 21:43:50 +03:00
										 |  |  | 			pushPrimSkill(static_cast<PrimarySkill>(g), type->heroClass->primarySkillInitial[g]); | 
					
						
							| 
									
										
										
										
											2014-06-05 19:52:14 +03:00
										 |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	if(secSkills.size() == 1 && secSkills[0] == std::pair<SecondarySkill,ui8>(SecondarySkill::DEFAULT, -1)) //set secondary skills to default
 | 
					
						
							|  |  |  | 		secSkills = type->secSkillsInit; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-04-02 19:56:10 +03:00
										 |  |  | 	if (gender == EHeroGender::DEFAULT) | 
					
						
							|  |  |  | 		gender = type->gender; | 
					
						
							| 
									
										
										
										
											2014-06-05 19:52:14 +03:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	setFormation(false); | 
					
						
							|  |  |  | 	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
 | 
					
						
							|  |  |  | 	// e.g. MANA_PER_KNOWLEDGE for correct preview and initial state after recruit	for(const auto & ob : VLC->modh->heroBaseBonuses)
 | 
					
						
							|  |  |  | 	// or MOVEMENT to compute initial movement before recruiting is finished
 | 
					
						
							| 
									
										
										
										
											2023-03-15 23:47:26 +02:00
										 |  |  | 	const JsonNode & baseBonuses = VLC->settings()->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-02-19 21:42:51 +03:00
										 |  |  | 		bonus->sid = id.getNum(); | 
					
						
							| 
									
										
										
										
											2023-05-01 01:20:01 +03:00
										 |  |  | 		bonus->duration = BonusDuration::PERMANENT; | 
					
						
							| 
									
										
										
										
											2023-02-19 21:42:51 +03:00
										 |  |  | 		addNewBonus(bonus); | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-03-15 21:34:29 +02:00
										 |  |  | 	if (VLC->settings()->getBoolean(EGameSettings::MODULE_COMMANDERS) && !commander) | 
					
						
							| 
									
										
										
										
											2014-06-05 19:52:14 +03:00
										 |  |  | 	{ | 
					
						
							| 
									
										
										
										
											2023-04-05 03:26:29 +03:00
										 |  |  | 		commander = new CCommanderInstance(type->heroClass->commander->getId()); | 
					
						
							| 
									
										
										
										
											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-07-22 22:04:32 +03:00
										 |  |  | 	skillsInfo.rand.setSeed(rand.nextInt()); | 
					
						
							|  |  |  | 	skillsInfo.resetMagicSchoolCounter(); | 
					
						
							|  |  |  | 	skillsInfo.resetWisdomCounter(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	//copy active (probably growing) bonuses from hero prototype to hero object
 | 
					
						
							|  |  |  | 	for(const std::shared_ptr<Bonus> & b : type->specialty) | 
					
						
							|  |  |  | 		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
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-07-15 14:08:20 +03:00
										 |  |  | void CGHeroInstance::initArmy(CRandomGenerator & rand, IArmyDescriptor * dst) | 
					
						
							| 
									
										
										
										
											2014-06-05 19:52:14 +03:00
										 |  |  | { | 
					
						
							|  |  |  | 	if(!dst) | 
					
						
							|  |  |  | 		dst = this; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	int warMachinesGiven = 0; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-03-15 23:47:26 +02:00
										 |  |  | 	auto stacksCountChances = VLC->settings()->getVector(EGameSettings::HEROES_STARTING_STACKS_CHANCES); | 
					
						
							| 
									
										
										
										
											2023-02-04 23:40:02 +01:00
										 |  |  | 	int stacksCountInitRandomNumber = rand.nextInt(1, 100); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-03-15 23:47:26 +02:00
										 |  |  | 	size_t maxStacksCount = std::min(stacksCountChances.size(), type->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; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-06-05 19:52:14 +03:00
										 |  |  | 		auto & stack = type->initialArmy[stackNo]; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-23 08:13:52 +03:00
										 |  |  | 		int count = rand.nextInt(stack.minAmount, stack.maxAmount); | 
					
						
							| 
									
										
										
										
											2014-06-05 19:52:14 +03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-05-26 19:51:45 +03:00
										 |  |  | 		const CCreature * creature = stack.creature.toCreature(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		if(creature == nullptr) | 
					
						
							|  |  |  | 		{ | 
					
						
							| 
									
										
										
										
											2023-01-02 13:27:03 +02:00
										 |  |  | 			logGlobal->error("Hero %s has invalid creature with id %d in initial army", getNameTranslated(), stack.creature.toEnum()); | 
					
						
							| 
									
										
										
										
											2017-05-26 19:51:45 +03:00
										 |  |  | 			continue; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		if(creature->warMachine != ArtifactID::NONE) //war machine
 | 
					
						
							| 
									
										
										
										
											2014-06-05 19:52:14 +03:00
										 |  |  | 		{ | 
					
						
							|  |  |  | 			warMachinesGiven++; | 
					
						
							|  |  |  | 			if(dst != this) | 
					
						
							|  |  |  | 				continue; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-05-26 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-05-17 16:52:16 +03:00
										 |  |  | 					putArtifact(slot, ArtifactUtils::createNewArtifactInstance(aid)); | 
					
						
							| 
									
										
										
										
											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 | 
					
						
							|  |  |  | 				cb->startBattleI(h,	this); | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	else if(ID == Obj::PRISON) | 
					
						
							|  |  |  | 	{ | 
					
						
							| 
									
										
										
										
											2023-03-15 23:47:26 +02:00
										 |  |  | 		if (cb->getHeroCount(h->tempOwner, false) < VLC->settings()->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
										 |  |  | 			 | 
					
						
							|  |  |  | 			cb->setManaPoints (id, manaLimit());		 | 
					
						
							|  |  |  | 			 | 
					
						
							|  |  |  | 			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
 | 
					
						
							|  |  |  | 					cb->createObject(boatPos, Obj::BOAT, getBoatType().getNum()); | 
					
						
							|  |  |  | 					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
 | 
					
						
							|  |  |  | 			cb->setObjProperty(id, ObjProperty::ID, 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()); | 
					
						
							|  |  |  | 		boost::algorithm::replace_first(hoverName,"%s", type->heroClass->getNameTranslated()); | 
					
						
							| 
									
										
										
										
											2014-06-05 19:52:14 +03:00
										 |  |  | 		return hoverName; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	else | 
					
						
							| 
									
										
										
										
											2023-01-30 19:00:51 +02:00
										 |  |  | 		return VLC->objtypeh->getObjectName(ID, 0); | 
					
						
							| 
									
										
										
										
											2014-06-05 19:52:14 +03:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | ui8 CGHeroInstance::maxlevelsToMagicSchool() const | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	return type->heroClass->isMagicHero() ? 3 : 4; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | ui8 CGHeroInstance::maxlevelsToWisdom() const | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	return type->heroClass->isMagicHero() ? 3 : 6; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-02-12 23:39:17 +03:00
										 |  |  | CGHeroInstance::SecondarySkillsInfo::SecondarySkillsInfo(): | 
					
						
							|  |  |  | 	magicSchoolCounter(1), | 
					
						
							|  |  |  | 	wisdomCounter(1) | 
					
						
							| 
									
										
										
										
											2016-08-18 18:53:28 +03:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2016-08-19 23:31:54 +03:00
										 |  |  | 	rand.setSeed(0); | 
					
						
							| 
									
										
										
										
											2016-08-18 18:53:28 +03:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-06-05 19:52:14 +03:00
										 |  |  | void CGHeroInstance::SecondarySkillsInfo::resetMagicSchoolCounter() | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	magicSchoolCounter = 1; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | void CGHeroInstance::SecondarySkillsInfo::resetWisdomCounter() | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	wisdomCounter = 1; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-09-09 20:30:36 +03:00
										 |  |  | void CGHeroInstance::initObj(CRandomGenerator & rand) | 
					
						
							| 
									
										
										
										
											2014-06-05 19:52:14 +03:00
										 |  |  | { | 
					
						
							|  |  |  | 	if(!type) | 
					
						
							| 
									
										
										
										
											2016-09-09 20:30:36 +03:00
										 |  |  | 		initHero(rand); //TODO: set up everything for prison before specialties are configured
 | 
					
						
							| 
									
										
										
										
											2014-06-05 19:52:14 +03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-06-28 17:19:53 +03:00
										 |  |  | 	if (ID != Obj::PRISON) | 
					
						
							|  |  |  | 	{ | 
					
						
							| 
									
										
										
										
											2023-01-01 17:10:47 +02:00
										 |  |  | 		auto terrain = cb->gameState()->getTile(visitablePos())->terType->getId(); | 
					
						
							| 
									
										
										
										
											2022-09-21 11:34:23 +02:00
										 |  |  | 		auto customApp = VLC->objtypeh->getHandlerFor(ID, type->heroClass->getIndex())->getOverride(terrain, this); | 
					
						
							| 
									
										
										
										
											2014-06-28 17:19:53 +03:00
										 |  |  | 		if (customApp) | 
					
						
							| 
									
										
										
										
											2022-09-11 15:12:35 +02:00
										 |  |  | 			appearance = customApp; | 
					
						
							| 
									
										
										
										
											2014-06-28 17:19:53 +03:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2014-06-05 19:52:14 +03:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void CGHeroInstance::recreateSecondarySkillsBonuses() | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2023-05-01 01:20:01 +03:00
										 |  |  | 	auto secondarySkillsBonuses = getBonuses(Selector::sourceType()(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-05-01 01:20:01 +03:00
										 |  |  | 	removeBonuses(Selector::source(BonusSource::SECONDARY_SKILL, 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
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-06-05 19:52:14 +03:00
										 |  |  | void CGHeroInstance::setPropertyDer( ui8 what, ui32 val ) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	if(what == ObjProperty::PRIMARY_STACK_COUNT) | 
					
						
							|  |  |  | 		setStackCount(SlotID(0), val); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | double CGHeroInstance::getFightingStrength() const | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	return sqrt((1.0 + 0.05*getPrimSkillLevel(PrimarySkill::ATTACK)) * (1.0 + 0.05*getPrimSkillLevel(PrimarySkill::DEFENSE))); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | double CGHeroInstance::getMagicStrength() const | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	return sqrt((1.0 + 0.05*getPrimSkillLevel(PrimarySkill::KNOWLEDGE)) * (1.0 + 0.05*getPrimSkillLevel(PrimarySkill::SPELL_POWER))); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | double CGHeroInstance::getHeroStrength() const | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	return sqrt(pow(getFightingStrength(), 2.0) * pow(getMagicStrength(), 2.0)); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | ui64 CGHeroInstance::getTotalStrength() const | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	double ret = getFightingStrength() * getArmyStrength(); | 
					
						
							| 
									
										
										
										
											2023-02-12 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
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
											
												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::getSpellSchoolLevel(const spells::Spell * spell, int32_t * outSelectedSchool) const | 
					
						
							| 
									
										
										
										
											2014-06-05 19:52:14 +03:00
										 |  |  | { | 
					
						
							| 
									
										
										
											
												Entities redesign and a few ERM features
* Made most Handlers derived from CHandlerBase and moved service API there.
* Declared existing Entity APIs.
* Added basic script context caching
* Started Lua script module
* Started Lua spell effect API
* Started script state persistence
* Started battle info callback binding
* CommitPackage removed
* Extracted spells::Caster to own header; Expanded Spell API.
* implemented !!MC:S, !!FU:E, !!FU:P, !!MA, !!VR:H, !!VR:C
* !!BU:C, !!BU:E, !!BU:G, !!BU:M implemented
* Allow use of "MC:S@varName@" to declare normal variable (technically v-variable with string key)
* Re-enabled VERM macros.
* !?GM0 added
* !?TM implemented
* Added !!MF:N
* Started !?OB, !!BM, !!HE, !!OW, !!UN
* Added basic support of w-variables
* Added support for ERM indirect variables
* Made !?FU regular trigger
* !!re (ERA loop receiver) implemented
* Fixed ERM receivers with zero args.
											
										 
											2018-03-17 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
										 |  |  | 	{ | 
					
						
							| 
									
										
										
										
											2023-08-21 22:10:32 +03:00
										 |  |  | 		int32_t thisSchool = valOfBonuses(BonusType::MAGIC_SCHOOL_SKILL, 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
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-05-05 21:28:07 +03:00
										 |  |  | 	vstd::amax(skill, valOfBonuses(BonusType::MAGIC_SCHOOL_SKILL, SpellSchool(ESpellSchool::ANY))); //any school bonus
 | 
					
						
							| 
									
										
										
										
											2023-05-01 01:20:01 +03:00
										 |  |  | 	vstd::amax(skill, valOfBonuses(BonusType::SPELL, spell->getIndex())); //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-05-05 21:28:07 +03:00
										 |  |  | 		base = static_cast<int64_t>(base * (valOfBonuses(BonusType::SPELL_DAMAGE, SpellSchool(ESpellSchool::ANY))) / 100.0); | 
					
						
							| 
									
										
										
										
											2023-03-21 12:24:48 +03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-05-01 01:20:01 +03:00
										 |  |  | 	base = static_cast<int64_t>(base * (100 + valOfBonuses(BonusType::SPECIFIC_SPELL_DAMAGE, spell->getIndex())) / 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-08-21 22:10:32 +03:00
										 |  |  | 		vstd::amax(maxSchoolBonus, valOfBonuses(BonusType::SPELL_DAMAGE, 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-05-01 01:20:01 +03:00
										 |  |  | 		base = static_cast<int64_t>(base * static_cast<double>(100 + valOfBonuses(BonusType::SPECIAL_SPELL_LEV, spell->getIndex()) / 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-05-01 01:20:01 +03:00
										 |  |  | 	base = static_cast<int64_t>(base * (100 + valOfBonuses(BonusType::SPECIFIC_SPELL_DAMAGE, spell->getIndex())) / 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-05-01 01:20:01 +03:00
										 |  |  | 	return getPrimSkillLevel(PrimarySkill::SPELL_POWER) + valOfBonuses(BonusType::SPELL_DURATION); | 
					
						
							| 
									
										
										
										
											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
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-07-20 07:08:49 +03:00
										 |  |  | void CGHeroInstance::getCastDescription(const spells::Spell * spell, const std::vector<const battle::Unit *> & 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-06-18 12:18:25 +03:00
										 |  |  | 	text.replaceLocalString(EMetaText::SPELL_NAME, spell->getIndex()); | 
					
						
							| 
									
										
										
										
											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; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
											
												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
										 |  |  | 		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
										 |  |  | { | 
					
						
							| 
									
										
										
											
												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 isAllowed = IObjectInterface::cb->isAllowed(0, spell->getIndex()); | 
					
						
							| 
									
										
										
										
											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-05-01 01:20:01 +03:00
										 |  |  | 	const bool specificBonus = hasBonusOfType(BonusType::SPELL, spell->getIndex()); | 
					
						
							| 
									
										
										
										
											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-08-21 22:10:32 +03:00
										 |  |  | 		if(hasBonusOfType(BonusType::SPELLS_OF_SCHOOL, cnf)) | 
					
						
							| 
									
										
										
										
											2015-09-16 04:39:44 +03:00
										 |  |  | 		{ | 
					
						
							|  |  |  | 			schoolBonus = stop = true; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	}); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-05-01 01:20:01 +03:00
										 |  |  | 	const bool levelBonus = hasBonusOfType(BonusType::SPELLS_OF_LEVEL, 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
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
											
												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::canLearnSpell(const spells::Spell * spell) const | 
					
						
							| 
									
										
										
										
											2016-10-09 13:38:54 +03:00
										 |  |  | { | 
					
						
							|  |  |  |     if(!hasSpellbook()) | 
					
						
							|  |  |  | 		return false; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
											
												Entities redesign and a few ERM features
* Made most Handlers derived from CHandlerBase and moved service API there.
* Declared existing Entity APIs.
* Added basic script context caching
* Started Lua script module
* Started Lua spell effect API
* Started script state persistence
* Started battle info callback binding
* CommitPackage removed
* Extracted spells::Caster to own header; Expanded Spell API.
* implemented !!MC:S, !!FU:E, !!FU:P, !!MA, !!VR:H, !!VR:C
* !!BU:C, !!BU:E, !!BU:G, !!BU:M implemented
* Allow use of "MC:S@varName@" to declare normal variable (technically v-variable with string key)
* Re-enabled VERM macros.
* !?GM0 added
* !?TM implemented
* Added !!MF:N
* Started !?OB, !!BM, !!HE, !!OW, !!UN
* Added basic support of w-variables
* Added support for ERM indirect variables
* Made !?FU regular trigger
* !!re (ERA loop receiver) implemented
* Fixed ERM receivers with zero args.
											
										 
											2018-03-17 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
 | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
											
												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(!IObjectInterface::cb->isAllowed(0, spell->getIndex())) | 
					
						
							| 
									
										
										
										
											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...
 | 
					
						
							|  |  |  | 		const std::map<ui32,si32> &casualties = battleResult.casualties[!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; | 
					
						
							| 
									
										
										
										
											2023-05-01 01:20:01 +03:00
										 |  |  | 		TConstBonusListPtr improvedNecromancy = getBonuses(Selector::type()(BonusType::IMPROVED_NECROMANCY)); | 
					
						
							| 
									
										
										
										
											2018-03-17 21:46:16 +13:00
										 |  |  | 		if(!improvedNecromancy->empty()) | 
					
						
							| 
									
										
										
										
											2014-06-05 19:52:14 +03:00
										 |  |  | 		{ | 
					
						
							| 
									
										
										
										
											2023-02-19 23:55:18 +03:00
										 |  |  | 			auto getCreatureID = [](const std::shared_ptr<Bonus> & bonus) -> CreatureID | 
					
						
							| 
									
										
										
										
											2018-03-17 21:46:16 +13:00
										 |  |  | 			{ | 
					
						
							| 
									
										
										
										
											2023-02-19 23:55:18 +03:00
										 |  |  | 				assert(bonus->subtype >=0); | 
					
						
							|  |  |  | 				if(bonus->subtype >= 0) | 
					
						
							|  |  |  | 					return CreatureID(bonus->subtype); | 
					
						
							|  |  |  | 				return CreatureID::NONE; | 
					
						
							| 
									
										
										
										
											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-04-05 03:26:29 +03:00
										 |  |  | 				vstd::amax(maxCasualtyLevel, VLC->creatures()->getByIndex(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-02-12 23:39:17 +03:00
										 |  |  | 					auto quality = [getCreatureID](const std::shared_ptr<Bonus> & pick) -> std::tuple<int, int, int> | 
					
						
							| 
									
										
										
										
											2018-03-17 21:46:16 +13:00
										 |  |  | 					{ | 
					
						
							| 
									
										
										
										
											2023-04-05 03:26:29 +03:00
										 |  |  | 						const auto * c = getCreatureID(pick).toCreature(); | 
					
						
							|  |  |  | 						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) | 
					
						
							|  |  |  | 			{ | 
					
						
							|  |  |  | 				creatureTypeRaised = getCreatureID(topPick); | 
					
						
							|  |  |  | 				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-02-12 23:39:17 +03:00
										 |  |  | 			for(const CreatureID & upgraded : VLC->creh->objects[creatureTypeRaised]->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-05-01 20:29:53 +03:00
										 |  |  | 		const double raisedUnitHealth = VLC->creh->objects[creatureTypeRaised]->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
										 |  |  | 		{ | 
					
						
							| 
									
										
										
											
												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 CCreature * c = VLC->creh->objects[casualty.first]; | 
					
						
							| 
									
										
										
										
											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. | 
					
						
							|  |  |  |  */ | 
					
						
							| 
									
										
										
										
											2016-09-09 20:30:36 +03:00
										 |  |  | void CGHeroInstance::showNecromancyDialog(const CStackBasicDescriptor &raisedStack, CRandomGenerator & 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-02-12 23:39:17 +03:00
										 |  |  | 	iw.components.emplace_back(raisedStack); | 
					
						
							| 
									
										
										
										
											2014-06-05 19:52:14 +03:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	if (raisedStack.count > 1) // Practicing the dark arts of necromancy, ... (plural)
 | 
					
						
							|  |  |  | 	{ | 
					
						
							| 
									
										
										
										
											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-06-18 12:18:25 +03:00
										 |  |  | 	iw.text.replaceCreatureName(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
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2023-06-19 22:01:18 +03:00
										 |  |  | 	return BoatId(VLC->townh->getById(type->heroClass->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-09-10 23:39:54 +02:00
										 |  |  | 	auto sel = Selector::typeSubtype(BonusType::PRIMARY_SKILL, static_cast<int>(which)) | 
					
						
							|  |  |  | 		.And(Selector::sourceType()(BonusSource::HERO_BASE_SKILL)); | 
					
						
							|  |  |  | 	if(hasBonus(sel)) | 
					
						
							|  |  |  | 		removeBonuses(sel); | 
					
						
							|  |  |  | 		 | 
					
						
							| 
									
										
										
										
											2023-08-19 21:43:50 +03:00
										 |  |  | 	addNewBonus(std::make_shared<Bonus>(BonusDuration::PERMANENT, BonusType::PRIMARY_SKILL, BonusSource::HERO_BASE_SKILL, val, id.getNum(), static_cast<int>(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
										 |  |  | { | 
					
						
							|  |  |  | 	return type->heroClass->getAlignment(); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-09-09 20:30:36 +03:00
										 |  |  | void CGHeroInstance::initExp(CRandomGenerator & 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 | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	return si32(getPrimSkillLevel(PrimarySkill::KNOWLEDGE) | 
					
						
							| 
									
										
										
										
											2023-05-01 01:20:01 +03:00
										 |  |  | 		* (valOfBonuses(BonusType::MANA_PER_KNOWLEDGE))); | 
					
						
							| 
									
										
										
										
											2023-02-20 00:09:06 +03:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-01-02 13:27:03 +02:00
										 |  |  | std::string CGHeroInstance::getNameTranslated() const | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2023-02-24 13:40:06 +02:00
										 |  |  | 	if (!nameCustom.empty()) | 
					
						
							|  |  |  | 		return nameCustom; | 
					
						
							| 
									
										
										
										
											2023-01-02 13:27:03 +02:00
										 |  |  | 	return VLC->generaltexth->translate(getNameTextID()); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | std::string CGHeroInstance::getNameTextID() const | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	if (!nameCustom.empty()) | 
					
						
							|  |  |  | 		return nameCustom; | 
					
						
							|  |  |  | 	if (type) | 
					
						
							|  |  |  | 		return type->getNameTextID(); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-01-21 15:57:25 +02:00
										 |  |  | 	// FIXME: called by logging from some specialties (mods?) before type is set on deserialization
 | 
					
						
							|  |  |  | 	// assert(0);
 | 
					
						
							| 
									
										
										
										
											2023-01-02 13:27:03 +02:00
										 |  |  | 	return ""; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | std::string CGHeroInstance::getBiographyTranslated() const | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2023-02-24 13:40:06 +02:00
										 |  |  | 	if (!biographyCustom.empty()) | 
					
						
							|  |  |  | 		return biographyCustom; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-01-02 13:27:03 +02:00
										 |  |  | 	return VLC->generaltexth->translate(getBiographyTextID()); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | std::string CGHeroInstance::getBiographyTextID() const | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	if (!biographyCustom.empty()) | 
					
						
							|  |  |  | 		return biographyCustom; | 
					
						
							|  |  |  | 	if (type) | 
					
						
							|  |  |  | 		return type->getBiographyTextID(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	assert(0); | 
					
						
							|  |  |  | 	return ""; | 
					
						
							| 
									
										
										
										
											2014-06-05 19:52:14 +03:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void CGHeroInstance::putArtifact(ArtifactPosition pos, CArtifactInstance *art) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2023-05-23 20:24:55 +03:00
										 |  |  | 	assert(art->artType->canBePutAt(this, pos)); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	CArtifactSet::putArtifact(pos, art); | 
					
						
							|  |  |  | 	if(ArtifactUtils::isSlotEquipment(pos)) | 
					
						
							|  |  |  | 		attachTo(*art); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void CGHeroInstance::removeArtifact(ArtifactPosition pos) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	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()) | 
					
						
							|  |  |  | 	{ | 
					
						
							|  |  |  | 		ArtifactLocation(this, ArtifactPosition(ArtifactPosition::SPELLBOOK)).removeArtifact(); | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | const std::set<SpellID> & CGHeroInstance::getSpellsInSpellbook() const | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	return spells; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-08-26 20:49:29 +12:00
										 |  |  | int CGHeroInstance::maxSpellLevel() const | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2023-05-01 01:20:01 +03:00
										 |  |  | 	return std::min(GameConstants::SPELL_LEVELS, valOfBonuses(Selector::type()(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
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2023-03-19 12:45:49 +03:00
										 |  |  | 	std::unique_ptr<TurnInfo> turnInfoLocal; | 
					
						
							| 
									
										
										
										
											2015-11-12 14:04:33 +03:00
										 |  |  | 	if(!ti) | 
					
						
							| 
									
										
										
										
											2016-08-16 15:47:21 +03:00
										 |  |  | 	{ | 
					
						
							| 
									
										
										
										
											2023-03-19 12:45:49 +03:00
										 |  |  | 		turnInfoLocal = std::make_unique<TurnInfo>(this); | 
					
						
							|  |  |  | 		ti = turnInfoLocal.get(); | 
					
						
							| 
									
										
										
										
											2016-08-16 15:47:21 +03:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2015-11-12 14:04:33 +03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-05-01 01:20:01 +03:00
										 |  |  | 	if(!ti->hasBonusOfType(BonusType::FREE_SHIP_BOARDING)) | 
					
						
							| 
									
										
										
										
											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; | 
					
						
							| 
									
										
										
										
											2023-03-21 20:02:58 +02:00
										 |  |  | 	if(!VLC->arth->objects[ArtifactID::GRAIL]->canBePutAt(this)) | 
					
						
							|  |  |  | 		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; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | std::vector<SecondarySkill> CGHeroInstance::getLevelUpProposedSecondarySkills() const | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	std::vector<SecondarySkill> obligatorySkills; //hero is offered magic school or wisdom if possible
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-02-27 12:42:57 +03:00
										 |  |  | 	auto getObligatorySkills = [](CSkill::Obligatory obl){ | 
					
						
							|  |  |  | 		std::vector<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-05-31 20:00:30 +03:00
										 |  |  | 				obligatory.emplace_back(i); //Always return all obligatory skills
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-02-27 12:42:57 +03:00
										 |  |  | 		return obligatory; | 
					
						
							|  |  |  | 	}; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	auto selectObligatorySkill = [&](std::vector<SecondarySkill>& ss) -> void | 
					
						
							|  |  |  | 	{ | 
					
						
							| 
									
										
										
										
											2014-06-05 19:52:14 +03:00
										 |  |  | 		std::shuffle(ss.begin(), ss.end(), skillsInfo.rand.getStdGenerator()); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-02-12 23:39:17 +03:00
										 |  |  | 		for(const auto & skill : ss) | 
					
						
							| 
									
										
										
										
											2014-06-05 19:52:14 +03:00
										 |  |  | 		{ | 
					
						
							| 
									
										
										
										
											2023-02-27 12:42:57 +03:00
										 |  |  | 			if (canLearnSkill(skill)) //only skills hero doesn't know yet
 | 
					
						
							| 
									
										
										
										
											2014-06-05 19:52:14 +03:00
										 |  |  | 			{ | 
					
						
							|  |  |  | 				obligatorySkills.push_back(skill); | 
					
						
							|  |  |  | 				break; //only one
 | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2023-02-27 12:42:57 +03:00
										 |  |  | 	}; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (!skillsInfo.wisdomCounter) | 
					
						
							|  |  |  | 	{ | 
					
						
							|  |  |  | 		auto obligatory = getObligatorySkills(CSkill::Obligatory::MAJOR); | 
					
						
							|  |  |  | 		selectObligatorySkill(obligatory); | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	if (!skillsInfo.magicSchoolCounter) | 
					
						
							|  |  |  | 	{ | 
					
						
							|  |  |  | 		auto obligatory = getObligatorySkills(CSkill::Obligatory::MINOR); | 
					
						
							|  |  |  | 		selectObligatorySkill(obligatory); | 
					
						
							| 
									
										
										
										
											2014-06-05 19:52:14 +03:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	std::vector<SecondarySkill> skills; | 
					
						
							|  |  |  | 	//picking sec. skills for choice
 | 
					
						
							| 
									
										
										
										
											2023-02-12 23:39:17 +03:00
										 |  |  | 	std::set<SecondarySkill> basicAndAdv; | 
					
						
							|  |  |  | 	std::set<SecondarySkill> expert; | 
					
						
							|  |  |  | 	std::set<SecondarySkill> none; | 
					
						
							| 
									
										
										
										
											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
										 |  |  | 	{ | 
					
						
							|  |  |  | 		if(elem.second < SecSkillLevel::EXPERT) | 
					
						
							|  |  |  | 			basicAndAdv.insert(elem.first); | 
					
						
							|  |  |  | 		else | 
					
						
							|  |  |  | 			expert.insert(elem.first); | 
					
						
							|  |  |  | 		none.erase(elem.first); | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2023-02-12 23:39:17 +03:00
										 |  |  | 	for(const auto & s : obligatorySkills) //don't duplicate them
 | 
					
						
							| 
									
										
										
										
											2014-06-05 19:52:14 +03:00
										 |  |  | 	{ | 
					
						
							|  |  |  | 		none.erase (s); | 
					
						
							|  |  |  | 		basicAndAdv.erase (s); | 
					
						
							|  |  |  | 		expert.erase (s); | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	//first offered skill:
 | 
					
						
							|  |  |  | 	// 1) give obligatory skill
 | 
					
						
							|  |  |  | 	// 2) give any other new skill
 | 
					
						
							|  |  |  | 	// 3) upgrade existing
 | 
					
						
							| 
									
										
										
										
											2023-02-12 23:39:17 +03:00
										 |  |  | 	if(canLearnSkill() && !obligatorySkills.empty()) | 
					
						
							| 
									
										
										
										
											2014-06-05 19:52:14 +03:00
										 |  |  | 	{ | 
					
						
							|  |  |  | 		skills.push_back (obligatorySkills[0]); | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2023-02-12 23:39:17 +03:00
										 |  |  | 	else if(!none.empty() && canLearnSkill()) //hero have free skill slot
 | 
					
						
							| 
									
										
										
										
											2014-06-05 19:52:14 +03:00
										 |  |  | 	{ | 
					
						
							|  |  |  | 		skills.push_back(type->heroClass->chooseSecSkill(none, skillsInfo.rand)); //new skill
 | 
					
						
							|  |  |  | 		none.erase(skills.back()); | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	else if(!basicAndAdv.empty()) | 
					
						
							|  |  |  | 	{ | 
					
						
							|  |  |  | 		skills.push_back(type->heroClass->chooseSecSkill(basicAndAdv, skillsInfo.rand)); //upgrade existing
 | 
					
						
							|  |  |  | 		basicAndAdv.erase(skills.back()); | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	//second offered skill:
 | 
					
						
							|  |  |  | 	//1) upgrade existing
 | 
					
						
							|  |  |  | 	//2) give obligatory skill
 | 
					
						
							|  |  |  | 	//3) give any other new skill
 | 
					
						
							|  |  |  | 	if(!basicAndAdv.empty()) | 
					
						
							|  |  |  | 	{ | 
					
						
							|  |  |  | 		SecondarySkill s = type->heroClass->chooseSecSkill(basicAndAdv, skillsInfo.rand);//upgrade existing
 | 
					
						
							|  |  |  | 		skills.push_back(s); | 
					
						
							|  |  |  | 		basicAndAdv.erase(s); | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	else if (canLearnSkill() && obligatorySkills.size() > 1) | 
					
						
							|  |  |  | 	{ | 
					
						
							|  |  |  | 		skills.push_back (obligatorySkills[1]); | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2023-02-12 23:39:17 +03:00
										 |  |  | 	else if(!none.empty() && canLearnSkill()) | 
					
						
							| 
									
										
										
										
											2014-06-05 19:52:14 +03:00
										 |  |  | 	{ | 
					
						
							|  |  |  | 		skills.push_back(type->heroClass->chooseSecSkill(none, skillsInfo.rand)); //give new skill
 | 
					
						
							|  |  |  | 		none.erase(skills.back()); | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-08-19 13:15:40 +03:00
										 |  |  | 	if (skills.size() == 2) // Fix for #1868 to avoid changing logic (possibly causing bugs in process)
 | 
					
						
							|  |  |  | 		std::swap(skills[0], skills[1]); | 
					
						
							| 
									
										
										
										
											2014-06-05 19:52:14 +03:00
										 |  |  | 	return skills; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-08-19 21:43:50 +03:00
										 |  |  | PrimarySkill CGHeroInstance::nextPrimarySkill(CRandomGenerator & rand) const | 
					
						
							| 
									
										
										
										
											2014-06-05 19:52:14 +03:00
										 |  |  | { | 
					
						
							|  |  |  | 	assert(gainsLevel()); | 
					
						
							| 
									
										
										
										
											2023-02-12 23:39:17 +03:00
										 |  |  | 	int randomValue = rand.nextInt(99); | 
					
						
							|  |  |  | 	int pom = 0; | 
					
						
							|  |  |  | 	int primarySkill = 0; | 
					
						
							| 
									
										
										
										
											2021-07-28 18:54:32 +03:00
										 |  |  | 	const auto isLowLevelHero = level < GameConstants::HERO_HIGH_LEVEL; | 
					
						
							|  |  |  | 	const auto & skillChances = isLowLevelHero ? type->heroClass->primarySkillLowLevel : type->heroClass->primarySkillHighLevel; | 
					
						
							| 
									
										
										
										
											2014-06-05 19:52:14 +03:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	for(; primarySkill < GameConstants::PRIMARY_SKILLS; ++primarySkill) | 
					
						
							|  |  |  | 	{ | 
					
						
							|  |  |  | 		pom += skillChances[primarySkill]; | 
					
						
							|  |  |  | 		if(randomValue < pom) | 
					
						
							|  |  |  | 		{ | 
					
						
							|  |  |  | 			break; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2021-07-28 18:54:32 +03:00
										 |  |  | 	if(primarySkill >= GameConstants::PRIMARY_SKILLS) | 
					
						
							|  |  |  | 	{ | 
					
						
							|  |  |  | 		primarySkill = rand.nextInt(GameConstants::PRIMARY_SKILLS - 1); | 
					
						
							| 
									
										
										
										
											2023-01-02 13:27:03 +02:00
										 |  |  | 		logGlobal->error("Wrong values in primarySkill%sLevel for hero class %s", isLowLevelHero ? "Low" : "High", type->heroClass->getNameTranslated()); | 
					
						
							| 
									
										
										
										
											2021-07-28 18:54:32 +03:00
										 |  |  | 		randomValue = 100 / GameConstants::PRIMARY_SKILLS; | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2017-08-11 20:03:05 +03:00
										 |  |  | 	logGlobal->trace("The hero gets the primary skill %d with a probability of %d %%.", primarySkill, randomValue); | 
					
						
							| 
									
										
										
										
											2023-08-19 21:43:50 +03:00
										 |  |  | 	return static_cast<PrimarySkill>(primarySkill); | 
					
						
							| 
									
										
										
										
											2014-06-05 19:52:14 +03:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-04-16 20:42:56 +03:00
										 |  |  | std::optional<SecondarySkill> CGHeroInstance::nextSecondarySkill(CRandomGenerator & rand) const | 
					
						
							| 
									
										
										
										
											2014-06-05 19:52:14 +03:00
										 |  |  | { | 
					
						
							|  |  |  | 	assert(gainsLevel()); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-04-16 20:42:56 +03:00
										 |  |  | 	std::optional<SecondarySkill> chosenSecondarySkill; | 
					
						
							| 
									
										
										
										
											2014-06-05 19:52:14 +03:00
										 |  |  | 	const auto proposedSecondarySkills = getLevelUpProposedSecondarySkills(); | 
					
						
							|  |  |  | 	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) | 
					
						
							|  |  |  | 	{ | 
					
						
							| 
									
										
										
										
											2023-05-01 01:20:01 +03:00
										 |  |  | 		auto skill = getBonusLocalFirst(Selector::type()(BonusType::PRIMARY_SKILL) | 
					
						
							| 
									
										
										
										
											2023-08-19 21:43:50 +03:00
										 |  |  | 			.And(Selector::subtype()(static_cast<int>(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
										 |  |  | 		} | 
					
						
							| 
									
										
										
										
											2015-12-11 16:13:18 +03:00
										 |  |  | 		CBonusSystemNode::treeHasChanged(); | 
					
						
							| 
									
										
										
										
											2014-06-05 19:52:14 +03:00
										 |  |  | 	} | 
					
						
							|  |  |  | 	else if(primarySkill == PrimarySkill::EXPERIENCE) | 
					
						
							|  |  |  | 	{ | 
					
						
							|  |  |  | 		if(abs) | 
					
						
							|  |  |  | 		{ | 
					
						
							|  |  |  | 			exp = value; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		else | 
					
						
							|  |  |  | 		{ | 
					
						
							|  |  |  | 			exp += value; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | bool CGHeroInstance::gainsLevel() const | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2020-10-01 01:38:06 -07:00
										 |  |  | 	return 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
 | 
					
						
							|  |  |  | 	skillsInfo.magicSchoolCounter = (skillsInfo.magicSchoolCounter + 1) % maxlevelsToMagicSchool(); | 
					
						
							|  |  |  | 	skillsInfo.wisdomCounter = (skillsInfo.wisdomCounter + 1) % maxlevelsToWisdom(); | 
					
						
							| 
									
										
										
										
											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
 | 
					
						
							|  |  |  | 	treeHasChanged(); | 
					
						
							| 
									
										
										
										
											2014-06-05 19:52:14 +03:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-09-09 20:30:36 +03:00
										 |  |  | void CGHeroInstance::levelUpAutomatically(CRandomGenerator & 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); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		auto proposedSecondarySkills = getLevelUpProposedSecondarySkills(); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											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
										 |  |  | 
 | 
					
						
							|  |  |  | bool CGHeroInstance::hasVisions(const CGObjectInstance * target, const int subtype) const | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	//VISIONS spell support
 | 
					
						
							| 
									
										
										
										
											2016-01-24 02:27:14 +03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-05-01 01:20:01 +03:00
										 |  |  | 	const auto cached = "type_" + std::to_string(vstd::to_underlying(BonusType::VISIONS)) + "__subtype_" + std::to_string(subtype); | 
					
						
							| 
									
										
										
										
											2016-01-24 02:27:14 +03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-05-01 01:20:01 +03:00
										 |  |  | 	const int visionsMultiplier = valOfBonuses(Selector::typeSubtype(BonusType::VISIONS,subtype), cached); | 
					
						
							| 
									
										
										
										
											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
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-12-07 22:10:08 +02:00
										 |  |  | 	const int distance = static_cast<int>(target->pos.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
										 |  |  | 
 | 
					
						
							|  |  |  | 	return (distance < visionsRange) && (target->pos.z == pos.z); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											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) | 
					
						
							| 
									
										
										
										
											2016-02-14 11:25:01 +03:00
										 |  |  | 	{ | 
					
						
							| 
									
										
										
										
											2016-02-22 02:37:19 +03:00
										 |  |  | 		if(type) | 
					
						
							|  |  |  | 		{ | 
					
						
							| 
									
										
										
										
											2023-01-02 13:27:03 +02:00
										 |  |  | 			return type->getJsonKey(); | 
					
						
							| 
									
										
										
										
											2016-02-22 02:37:19 +03:00
										 |  |  | 		} | 
					
						
							|  |  |  | 		else | 
					
						
							|  |  |  | 		{ | 
					
						
							| 
									
										
										
										
											2023-01-02 13:27:03 +02:00
										 |  |  | 			return VLC->heroh->objects[subID]->getJsonKey(); | 
					
						
							| 
									
										
										
										
											2016-02-22 02:37:19 +03:00
										 |  |  | 		} | 
					
						
							| 
									
										
										
										
											2016-02-14 11:25:01 +03:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2016-11-13 13:38:42 +03:00
										 |  |  | 	return ""; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-05-28 16:23:42 +03:00
										 |  |  | void CGHeroInstance::afterAddToMap(CMap * map) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2017-05-28 18:42:36 +03:00
										 |  |  | 	if(ID == Obj::HERO) | 
					
						
							| 
									
										
										
										
											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) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	if (ID == Obj::HERO) | 
					
						
							|  |  |  | 		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-01-02 13:27:03 +02:00
										 |  |  | 	handler.serializeString("biography", biographyCustom); | 
					
						
							| 
									
										
										
										
											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-01-02 13:27:03 +02:00
										 |  |  | 	handler.serializeString("name", nameCustom); | 
					
						
							| 
									
										
										
										
											2023-04-02 19:56:10 +03:00
										 |  |  | 	handler.serializeInt("gender", gender, 0); | 
					
						
							| 
									
										
										
										
											2016-02-14 12:13:30 +03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-11-13 13:38:42 +03:00
										 |  |  | 	{ | 
					
						
							| 
									
										
										
										
											2023-03-15 21:34:29 +02:00
										 |  |  | 		const int legacyHeroes = VLC->settings()->getInteger(EGameSettings::TEXTS_HERO); | 
					
						
							| 
									
										
										
										
											2016-11-13 13:38:42 +03:00
										 |  |  | 		const int moddedStart = legacyHeroes + GameConstants::HERO_PORTRAIT_SHIFT; | 
					
						
							| 
									
										
										
										
											2016-02-14 11:25:01 +03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-11-13 13:38:42 +03:00
										 |  |  | 		if(handler.saving) | 
					
						
							|  |  |  | 		{ | 
					
						
							|  |  |  | 			if(portrait >= 0) | 
					
						
							|  |  |  | 			{ | 
					
						
							|  |  |  | 				if(portrait < legacyHeroes || portrait >= moddedStart) | 
					
						
							| 
									
										
										
										
											2023-04-20 03:20:00 +04:00
										 |  |  | 				{ | 
					
						
							| 
									
										
										
										
											2023-04-28 05:34:59 +04:00
										 |  |  | 					int tempPortrait = portrait >= moddedStart | 
					
						
							|  |  |  | 						? portrait - GameConstants::HERO_PORTRAIT_SHIFT | 
					
						
							|  |  |  | 						: portrait; | 
					
						
							| 
									
										
										
										
											2023-04-20 03:20:00 +04:00
										 |  |  | 					handler.serializeId<si32, si32, HeroTypeID>("portrait", tempPortrait, -1); | 
					
						
							|  |  |  | 				} | 
					
						
							| 
									
										
										
										
											2016-11-13 13:38:42 +03:00
										 |  |  | 				else | 
					
						
							|  |  |  | 					handler.serializeInt("portrait", portrait, -1); | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		else | 
					
						
							|  |  |  | 		{ | 
					
						
							|  |  |  | 			const JsonNode & portraitNode = handler.getCurrent()["portrait"]; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-11-26 22:18:18 +01:00
										 |  |  | 			if(portraitNode.getType() == JsonNode::JsonType::DATA_STRING) | 
					
						
							| 
									
										
										
										
											2023-04-20 03:20:00 +04: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
										 |  |  | 				handler.serializeId<si32, si32, HeroTypeID>("portrait", portrait, -1); | 
					
						
							| 
									
										
										
										
											2023-04-28 05:34:59 +04:00
										 |  |  | 				if(portrait >= legacyHeroes) | 
					
						
							|  |  |  | 					portrait += GameConstants::HERO_PORTRAIT_SHIFT; | 
					
						
							| 
									
										
										
										
											2023-04-20 03:20:00 +04:00
										 |  |  | 			} | 
					
						
							| 
									
										
										
										
											2016-02-22 02:37:19 +03:00
										 |  |  | 			else | 
					
						
							| 
									
										
										
										
											2016-11-13 13:38:42 +03:00
										 |  |  | 				handler.serializeInt("portrait", portrait, -1); | 
					
						
							| 
									
										
										
										
											2016-02-22 02:37:19 +03:00
										 |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											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
										 |  |  | 
 | 
					
						
							|  |  |  | 			for(int i = 0; i < GameConstants::PRIMARY_SKILLS; ++i) | 
					
						
							|  |  |  | 			{ | 
					
						
							| 
									
										
										
										
											2023-05-01 01:20:01 +03:00
										 |  |  | 				int value = valOfBonuses(Selector::typeSubtype(BonusType::PRIMARY_SKILL, i).And(Selector::sourceType()(BonusSource::HERO_BASE_SKILL))); | 
					
						
							| 
									
										
										
										
											2016-11-13 13:38:42 +03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-08-19 21:43:50 +03:00
										 |  |  | 				handler.serializeInt(NPrimarySkill::names[i], 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) | 
					
						
							|  |  |  | 		{ | 
					
						
							|  |  |  | 			if(p.first == SecondarySkill(SecondarySkill::DEFAULT)) | 
					
						
							|  |  |  | 				defaultSkills = true; | 
					
						
							|  |  |  | 			else | 
					
						
							|  |  |  | 				normalSkills = true; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		if(defaultSkills && normalSkills) | 
					
						
							|  |  |  | 			logGlobal->error("Mixed default and normal secondary skills"); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		//in json default skills means no field/null
 | 
					
						
							|  |  |  | 		if(!defaultSkills) | 
					
						
							|  |  |  | 		{ | 
					
						
							| 
									
										
										
										
											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); | 
					
						
							|  |  |  | 				const si32 rawId = secSkills.at(skillIndex).first; | 
					
						
							| 
									
										
										
										
											2016-11-13 13:38:42 +03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-03-31 18:56:40 +13:00
										 |  |  | 				if(rawId < 0 || rawId >= VLC->skillh->size()) | 
					
						
							| 
									
										
										
										
											2017-08-11 20:03:05 +03:00
										 |  |  | 					logGlobal->error("Invalid secondary skill %d", rawId); | 
					
						
							| 
									
										
										
										
											2016-11-13 13:38:42 +03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-08-14 00:06:22 +03:00
										 |  |  | 				auto value = (*VLC->skillh)[SecondarySkill(rawId)]->getJsonKey(); | 
					
						
							|  |  |  | 				handler.serializeString("skill", value); | 
					
						
							|  |  |  | 				value = NSecondarySkill::levels.at(secSkills.at(skillIndex).second); | 
					
						
							|  |  |  | 				handler.serializeString("level", value); | 
					
						
							| 
									
										
										
										
											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-02-12 23:39:17 +03:00
										 |  |  | 			secSkills.emplace_back(SecondarySkill::DEFAULT, -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
										 |  |  | 			{ | 
					
						
							| 
									
										
										
										
											2018-03-31 18:56:40 +13:00
										 |  |  | 				const int rawId = CSkillHandler::decodeSkill(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) | 
					
						
							|  |  |  | 		CArtifactSet::serializeJsonArtifacts(handler, "artifacts", nullptr); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void CGHeroInstance::serializeJsonOptions(JsonSerializeFormat & handler) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	serializeCommonOptions(handler); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	serializeJsonOwner(handler); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if(ID == Obj::HERO || ID == Obj::PRISON) | 
					
						
							|  |  |  | 	{ | 
					
						
							|  |  |  | 		std::string typeName; | 
					
						
							|  |  |  | 		if(handler.saving) | 
					
						
							|  |  |  | 			typeName = getHeroTypeName(); | 
					
						
							|  |  |  | 		handler.serializeString("type", typeName); | 
					
						
							|  |  |  | 		if(!handler.saving) | 
					
						
							|  |  |  | 			setHeroTypeName(typeName); | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-09-17 22:19:45 +02:00
										 |  |  | 	CArmedInstance::serializeJsonOptions(handler); | 
					
						
							| 
									
										
										
										
											2016-11-13 13:38:42 +03:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	{ | 
					
						
							| 
									
										
										
										
											2023-02-12 23:39:17 +03:00
										 |  |  | 		static constexpr int NO_PATROLING = -1; | 
					
						
							| 
									
										
										
										
											2016-11-13 13:38:42 +03:00
										 |  |  | 		int rawPatrolRadius = NO_PATROLING; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		if(handler.saving) | 
					
						
							|  |  |  | 		{ | 
					
						
							|  |  |  | 			rawPatrolRadius = patrol.patrolling ? patrol.patrolRadius : NO_PATROLING; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		handler.serializeInt("patrolRadius", rawPatrolRadius, NO_PATROLING); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		if(!handler.saving) | 
					
						
							|  |  |  | 		{ | 
					
						
							| 
									
										
										
										
											2023-02-13 21:10:11 +02:00
										 |  |  | 			if(!appearance) | 
					
						
							|  |  |  | 			{ | 
					
						
							|  |  |  | 				// crossoverDeserialize
 | 
					
						
							|  |  |  | 				type = VLC->heroh->objects[subID]; | 
					
						
							|  |  |  | 				appearance = VLC->objtypeh->getHandlerFor(Obj::HERO, type->heroClass->getIndex())->getTemplates().front(); | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-11-13 13:38:42 +03:00
										 |  |  | 			patrol.patrolling = (rawPatrolRadius > NO_PATROLING); | 
					
						
							| 
									
										
										
										
											2022-12-09 14:42:47 +02:00
										 |  |  | 			patrol.initialPos = visitablePos(); | 
					
						
							| 
									
										
										
										
											2016-11-13 13:38:42 +03:00
										 |  |  | 			patrol.patrolRadius = (rawPatrolRadius > NO_PATROLING) ? rawPatrolRadius : 0; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 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 | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	for(const TriggeredEvent & event : IObjectInterface::cb->getMapHeader()->triggeredEvents) | 
					
						
							|  |  |  | 	{ | 
					
						
							| 
									
										
										
										
											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
										 |  |  | 		{ | 
					
						
							| 
									
										
										
										
											2016-11-13 13:38:42 +03:00
										 |  |  | 			if ((condition.condition == EventCondition::CONTROL || condition.condition == EventCondition::HAVE_0) && condition.object) | 
					
						
							| 
									
										
										
										
											2016-01-27 13:47:42 +03:00
										 |  |  | 			{ | 
					
						
							| 
									
										
										
										
											2023-02-12 23:39:17 +03:00
										 |  |  | 				const auto * hero = dynamic_cast<const CGHeroInstance *>(condition.object); | 
					
						
							| 
									
										
										
										
											2016-01-27 13:47:42 +03:00
										 |  |  | 				return (hero != this); | 
					
						
							|  |  |  | 			} | 
					
						
							| 
									
										
										
										
											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
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-06-06 19:19:30 +03:00
										 |  |  | void CGHeroInstance::fillUpgradeInfo(UpgradeInfo & info, const CStackInstance &stack) const | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	TConstBonusListPtr lista = getBonuses(Selector::typeSubtype(BonusType::SPECIAL_UPGRADE, stack.type->getId())); | 
					
						
							|  |  |  | 	for(const auto & it : *lista) | 
					
						
							|  |  |  | 	{ | 
					
						
							|  |  |  | 		auto nid = CreatureID(it->additionalInfo[0]); | 
					
						
							|  |  |  | 		if (nid != stack.type->getId()) //in very specific case the upgrade is available by default (?)
 | 
					
						
							|  |  |  | 		{ | 
					
						
							|  |  |  | 			info.newID.push_back(nid); | 
					
						
							|  |  |  | 			info.cost.push_back(nid.toCreature()->getFullRecruitCost() - stack.type->getFullRecruitCost()); | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-07-26 16:07:42 +03:00
										 |  |  | VCMI_LIB_NAMESPACE_END |