mirror of
				https://github.com/vcmi/vcmi.git
				synced 2025-10-31 00:07:39 +02:00 
			
		
		
		
	Refactored player-specific data into single struct BattleState.
This commit is contained in:
		
							
								
								
									
										13
									
								
								Global.h
									
									
									
									
									
								
							
							
						
						
									
										13
									
								
								Global.h
									
									
									
									
									
								
							| @@ -329,13 +329,14 @@ namespace vstd | ||||
| 		return -1; | ||||
| 	} | ||||
|  | ||||
| 	//Func(T1,T2) must say if these elements matches | ||||
| 	template <typename T1, typename T2, typename Func> | ||||
| 	int find_pos(const std::vector<T1> & c, const T2 &s, const Func &f) | ||||
| 	//Func f tells if element matches | ||||
| 	template <typename Container, typename Func> | ||||
| 	int find_pos_if(const Container & c, const Func &f) | ||||
| 	{ | ||||
| 		for(size_t i=0; i < c.size(); ++i) | ||||
| 			if(f(c[i],s)) | ||||
| 				return i; | ||||
| 		auto ret = boost::range::find_if(c, f); | ||||
| 		if(ret != std::end(c)) | ||||
| 			return std::distance(std::begin(c), ret); | ||||
|  | ||||
| 		return -1; | ||||
| 	} | ||||
|  | ||||
|   | ||||
| @@ -570,51 +570,58 @@ void CClient::battleStarted(const BattleInfo * info) | ||||
| { | ||||
| 	for(auto &battleCb : battleCallbacks) | ||||
| 	{ | ||||
| 		if(vstd::contains(info->sides, battleCb.first)  ||  battleCb.first >= PlayerColor::PLAYER_LIMIT) | ||||
| 		if(vstd::contains_if(info->sides, [&](const SideInBattle& side) {return side.color == battleCb.first; })   | ||||
| 			||  battleCb.first >= PlayerColor::PLAYER_LIMIT) | ||||
| 		{ | ||||
| 			battleCb.second->setBattle(info); | ||||
| 		} | ||||
| 	} | ||||
| // 	for(ui8 side : info->sides) | ||||
| // 		if(battleCallbacks.count(side)) | ||||
| // 			battleCallbacks[side]->setBattle(info); | ||||
|  | ||||
| 	shared_ptr<CPlayerInterface> att, def; | ||||
| 	auto &leftSide = info->sides[0], &rightSide = info->sides[1]; | ||||
|  | ||||
|  | ||||
| 	//If quick combat is not, do not prepare interfaces for battleint | ||||
| 	if(!settings["adventure"]["quickCombat"].Bool()) | ||||
| 	{ | ||||
| 		if(vstd::contains(playerint, info->sides[0]) && playerint[info->sides[0]]->human) | ||||
| 			att = std::dynamic_pointer_cast<CPlayerInterface>( playerint[info->sides[0]] ); | ||||
| 		if(vstd::contains(playerint, leftSide.color) && playerint[leftSide.color]->human) | ||||
| 			att = std::dynamic_pointer_cast<CPlayerInterface>( playerint[leftSide.color] ); | ||||
|  | ||||
| 		if(vstd::contains(playerint, info->sides[1]) && playerint[info->sides[1]]->human) | ||||
| 			def = std::dynamic_pointer_cast<CPlayerInterface>( playerint[info->sides[1]] ); | ||||
| 		if(vstd::contains(playerint, rightSide.color) && playerint[rightSide.color]->human) | ||||
| 			def = std::dynamic_pointer_cast<CPlayerInterface>( playerint[rightSide.color] ); | ||||
| 	} | ||||
|  | ||||
| 	if(!gNoGUI && (!!att || !!def || gs->scenarioOps->mode == StartInfo::DUEL)) | ||||
| 	{ | ||||
| 		boost::unique_lock<boost::recursive_mutex> un(*LOCPLINT->pim); | ||||
| 		new CBattleInterface(info->belligerents[0], info->belligerents[1], info->heroes[0], info->heroes[1], | ||||
| 		new CBattleInterface(leftSide.armyObject, rightSide.armyObject, leftSide.hero, rightSide.hero, | ||||
| 			Rect((screen->w - 800)/2,  | ||||
| 			     (screen->h - 600)/2, 800, 600), att, def); | ||||
| 	} | ||||
|  | ||||
| 	if(vstd::contains(battleints,info->sides[0])) | ||||
| 		battleints[info->sides[0]]->battleStart(info->belligerents[0], info->belligerents[1], info->tile, info->heroes[0], info->heroes[1], 0); | ||||
| 	if(vstd::contains(battleints,info->sides[1])) | ||||
| 		battleints[info->sides[1]]->battleStart(info->belligerents[0], info->belligerents[1], info->tile, info->heroes[0], info->heroes[1], 1); | ||||
| 	if(vstd::contains(battleints,PlayerColor::UNFLAGGABLE)) | ||||
| 		battleints[PlayerColor::UNFLAGGABLE]->battleStart(info->belligerents[0], info->belligerents[1], info->tile, info->heroes[0], info->heroes[1], 1); | ||||
| 	auto callBattleStart = [&](PlayerColor color, ui8 side){ | ||||
| 		if(vstd::contains(battleints, color)) | ||||
| 			battleints[color]->battleStart(leftSide.armyObject, rightSide.armyObject, info->tile, leftSide.hero, rightSide.hero, side); | ||||
| 	}; | ||||
|  | ||||
| 	if(info->tacticDistance && vstd::contains(battleints,info->sides[info->tacticsSide])) | ||||
| 	callBattleStart(leftSide.color, 0); | ||||
| 	callBattleStart(leftSide.color, 1); | ||||
| 	callBattleStart(PlayerColor::UNFLAGGABLE, 1); | ||||
|  | ||||
| 	if(info->tacticDistance && vstd::contains(battleints,info->sides[info->tacticsSide].color)) | ||||
| 	{ | ||||
| 		boost::thread(&CClient::commenceTacticPhaseForInt, this, battleints[info->sides[info->tacticsSide]]); | ||||
| 		boost::thread(&CClient::commenceTacticPhaseForInt, this, battleints[info->sides[info->tacticsSide].color]); | ||||
| 	} | ||||
| } | ||||
|  | ||||
| void CClient::battleFinished() | ||||
| { | ||||
| 	for(PlayerColor side : gs->curB->sides) | ||||
| 		if(battleCallbacks.count(side)) | ||||
| 			battleCallbacks[side]->setBattle(nullptr); | ||||
| 	for(auto & side : gs->curB->sides) | ||||
| 		if(battleCallbacks.count(side.color)) | ||||
| 			battleCallbacks[side.color]->setBattle(nullptr); | ||||
| } | ||||
|  | ||||
| void CClient::loadNeutralBattleAI() | ||||
|   | ||||
| @@ -90,8 +90,8 @@ | ||||
|  | ||||
|  | ||||
| #define BATTLE_INTERFACE_CALL_IF_PRESENT_FOR_BOTH_SIDES(function,...) 				\ | ||||
| 	CALL_ONLY_THAT_BATTLE_INTERFACE(GS(cl)->curB->sides[0], function, __VA_ARGS__)	\ | ||||
| 	CALL_ONLY_THAT_BATTLE_INTERFACE(GS(cl)->curB->sides[1], function, __VA_ARGS__)	\ | ||||
| 	CALL_ONLY_THAT_BATTLE_INTERFACE(GS(cl)->curB->sides[0].color, function, __VA_ARGS__)	\ | ||||
| 	CALL_ONLY_THAT_BATTLE_INTERFACE(GS(cl)->curB->sides[1].color, function, __VA_ARGS__)	\ | ||||
| 	BATTLE_INTERFACE_CALL_RECEIVERS(function, __VA_ARGS__) | ||||
| /* | ||||
|  * NetPacksClient.cpp, part of VCMI engine | ||||
| @@ -624,7 +624,9 @@ void BattleSetActiveStack::applyCl( CClient *cl ) | ||||
| 	PlayerColor playerToCall; //player that will move activated stack | ||||
| 	if( activated->hasBonusOfType(Bonus::HYPNOTIZED) ) | ||||
| 	{ | ||||
| 		playerToCall = ( GS(cl)->curB->sides[0] == activated->owner ? GS(cl)->curB->sides[1] : GS(cl)->curB->sides[0] ); | ||||
| 		playerToCall = ( GS(cl)->curB->sides[0].color == activated->owner  | ||||
| 			? GS(cl)->curB->sides[1].color  | ||||
| 			: GS(cl)->curB->sides[0].color ); | ||||
| 	} | ||||
| 	else | ||||
| 	{ | ||||
|   | ||||
| @@ -101,7 +101,7 @@ void CBattleInterface::addNewAnim(CBattleAnimation * anim) | ||||
| } | ||||
|  | ||||
| CBattleInterface::CBattleInterface(const CCreatureSet * army1, const CCreatureSet * army2,  | ||||
| 								   CGHeroInstance *hero1, CGHeroInstance *hero2,  | ||||
| 								   const CGHeroInstance *hero1, const CGHeroInstance *hero2,  | ||||
| 								   const SDL_Rect & myRect,  | ||||
| 								   shared_ptr<CPlayerInterface> att, shared_ptr<CPlayerInterface> defen) | ||||
| 	: background(nullptr), queue(nullptr), attackingHeroInstance(hero1), defendingHeroInstance(hero2), animCount(0), | ||||
|   | ||||
| @@ -243,7 +243,7 @@ public: | ||||
| 	ui32 animIDhelper; //for giving IDs for animations | ||||
| 	static CondSh<bool> animsAreDisplayed; //for waiting with the end of battle for end of anims | ||||
|  | ||||
| 	CBattleInterface(const CCreatureSet * army1, const CCreatureSet * army2, CGHeroInstance *hero1, CGHeroInstance *hero2, const SDL_Rect & myRect, shared_ptr<CPlayerInterface> att, shared_ptr<CPlayerInterface> defen); //c-tor | ||||
| 	CBattleInterface(const CCreatureSet * army1, const CCreatureSet * army2, const CGHeroInstance *hero1, const CGHeroInstance *hero2, const SDL_Rect & myRect, shared_ptr<CPlayerInterface> att, shared_ptr<CPlayerInterface> defen); //c-tor | ||||
| 	~CBattleInterface(); //d-tor | ||||
|  | ||||
| 	//std::vector<TimeInterested*> timeinterested; //animation handling | ||||
|   | ||||
| @@ -140,7 +140,7 @@ int BattleInfo::calculateSpellDuration( const CSpell * spell, const CGHeroInstan | ||||
| CStack * BattleInfo::generateNewStack(const CStackInstance &base, bool attackerOwned, SlotID slot, BattleHex position) const | ||||
| { | ||||
| 	int stackID = getIdForNewStack(); | ||||
| 	PlayerColor owner = attackerOwned ? sides[0] : sides[1]; | ||||
| 	PlayerColor owner = sides[attackerOwned ? 0 : 1].color; | ||||
| 	assert((owner >= PlayerColor::PLAYER_LIMIT)  || | ||||
| 		   (base.armyObj && base.armyObj->tempOwner == owner)); | ||||
|  | ||||
| @@ -153,7 +153,7 @@ CStack * BattleInfo::generateNewStack(const CStackInstance &base, bool attackerO | ||||
| CStack * BattleInfo::generateNewStack(const CStackBasicDescriptor &base, bool attackerOwned, SlotID slot, BattleHex position) const | ||||
| { | ||||
| 	int stackID = getIdForNewStack(); | ||||
| 	PlayerColor owner = attackerOwned ? sides[0] : sides[1]; | ||||
| 	PlayerColor owner = sides[attackerOwned ? 0 : 1].color; | ||||
| 	auto  ret = new CStack(&base, owner, stackID, attackerOwned, slot); | ||||
| 	ret->position = position; | ||||
| 	ret->state.insert(EBattleStackState::ALIVE);  //alive state indication | ||||
| @@ -202,7 +202,7 @@ const CStack * BattleInfo::battleGetStack(BattleHex pos, bool onlyAlive) | ||||
| 			) ) | ||||
| 		{ | ||||
| 			if (elem->alive()) | ||||
| 				return elem; //we prefer living stacks - there cna be only one stack on te tile, so return it imediately | ||||
| 				return elem; //we prefer living stacks - there can be only one stack on the tile, so return it immediately | ||||
| 			else if (!onlyAlive) | ||||
| 				stack = elem; //dead stacks are only accessible when there's no alive stack on this tile | ||||
| 		} | ||||
| @@ -212,15 +212,17 @@ const CStack * BattleInfo::battleGetStack(BattleHex pos, bool onlyAlive) | ||||
|  | ||||
| const CGHeroInstance * BattleInfo::battleGetOwner(const CStack * stack) const | ||||
| { | ||||
| 	return heroes[!stack->attackerOwned]; | ||||
| 	return sides[!stack->attackerOwned].hero; | ||||
| } | ||||
|  | ||||
| void BattleInfo::localInit() | ||||
| { | ||||
| 	belligerents[0]->battle = belligerents[1]->battle = this; | ||||
|  | ||||
| 	for(CArmedInstance *b : belligerents) | ||||
| 		b->attachTo(this); | ||||
| 	for(int i = 0; i < 2; i++) | ||||
| 	{ | ||||
| 		auto armyObj = battleGetArmyObject(i); | ||||
| 		armyObj->battle = this; | ||||
| 		armyObj->attachTo(this); | ||||
| 	} | ||||
|  | ||||
| 	for(CStack *s : stacks) | ||||
| 		localInitStack(s); | ||||
| @@ -237,7 +239,7 @@ void BattleInfo::localInitStack(CStack * s) | ||||
| 	} | ||||
| 	else //attach directly to obj to which stack belongs and creature type | ||||
| 	{ | ||||
| 		CArmedInstance *army = belligerents[!s->attackerOwned]; | ||||
| 		CArmedInstance *army = battleGetArmyObject(!s->attackerOwned); | ||||
| 		s->attachTo(army); | ||||
| 		assert(s->type); | ||||
| 		s->attachTo(const_cast<CCreature*>(s->type)); | ||||
| @@ -349,23 +351,17 @@ BattleInfo * BattleInfo::setupBattle( int3 tile, ETerrainType terrain, BFieldTyp | ||||
| { | ||||
| 	CMP_stack cmpst; | ||||
| 	auto curB = new BattleInfo; | ||||
| 	curB->castSpells[0] = curB->castSpells[1] = 0; | ||||
| 	curB->sides[0] = armies[0]->tempOwner; | ||||
| 	curB->sides[1] = armies[1]->tempOwner; | ||||
| 	if(curB->sides[1] == PlayerColor::UNFLAGGABLE) | ||||
| 		curB->sides[1] = PlayerColor::NEUTRAL; | ||||
|  | ||||
| 	for(auto i = 0u; i < curB->sides.size(); i++) | ||||
| 		curB->sides[i].init(heroes[i], armies[i]); | ||||
|  | ||||
|  | ||||
| 	std::vector<CStack*> & stacks = (curB->stacks); | ||||
|  | ||||
| 	curB->tile = tile; | ||||
| 	curB->battlefieldType = battlefieldType; | ||||
| 	curB->belligerents[0] = const_cast<CArmedInstance*>(armies[0]); | ||||
| 	curB->belligerents[1] = const_cast<CArmedInstance*>(armies[1]); | ||||
| 	curB->heroes[0] = const_cast<CGHeroInstance*>(heroes[0]); | ||||
| 	curB->heroes[1] = const_cast<CGHeroInstance*>(heroes[1]); | ||||
| 	curB->round = -2; | ||||
| 	curB->activeStack = -1; | ||||
| 	curB->enchanterCounter[0] = curB->enchanterCounter[1] = 0; //ready to cast | ||||
|  | ||||
| 	if(town) | ||||
| 	{ | ||||
| @@ -664,7 +660,7 @@ BattleInfo * BattleInfo::setupBattle( int3 tile, ETerrainType terrain, BFieldTyp | ||||
| 	////////////////////////////////////////////////////////////////////////// | ||||
|  | ||||
| 	//tactics | ||||
| 	bool isTacticsAllowed = !creatureBank; //no tactics in crebanks | ||||
| 	bool isTacticsAllowed = !creatureBank; //no tactics in creature banks | ||||
|  | ||||
| 	int tacticLvls[2] = {0}; | ||||
| 	for(int i = 0; i < ARRAY_COUNT(tacticLvls); i++) | ||||
| @@ -687,7 +683,7 @@ BattleInfo * BattleInfo::setupBattle( int3 tile, ETerrainType terrain, BFieldTyp | ||||
| 	for(int i = 0; i < 2; i++) | ||||
| 	{ | ||||
| 		TNodes nodes; | ||||
| 		curB->belligerents[i]->getRedAncestors(nodes); | ||||
| 		curB->battleGetArmyObject(i)->getRedAncestors(nodes); | ||||
| 		for(CBonusSystemNode *n : nodes) | ||||
| 		{ | ||||
| 			for(Bonus *b : n->getExportedBonusList()) | ||||
| @@ -697,7 +693,7 @@ BattleInfo * BattleInfo::setupBattle( int3 tile, ETerrainType terrain, BFieldTyp | ||||
| 					auto bCopy = new Bonus(*b); | ||||
| 					bCopy->effectRange = Bonus::NO_LIMIT; | ||||
| 					bCopy->propagator.reset(); | ||||
| 					bCopy->limiter.reset(new StackOwnerLimiter(curB->sides[!i])); | ||||
| 					bCopy->limiter.reset(new StackOwnerLimiter(curB->sides[!i].color)); | ||||
| 					curB->addNewBonus(bCopy); | ||||
| 				} | ||||
| 			} | ||||
| @@ -709,11 +705,12 @@ BattleInfo * BattleInfo::setupBattle( int3 tile, ETerrainType terrain, BFieldTyp | ||||
|  | ||||
| const CGHeroInstance * BattleInfo::getHero( PlayerColor player ) const | ||||
| { | ||||
| 	assert(sides[0] == player || sides[1] == player); | ||||
| 	if(heroes[0] && heroes[0]->getOwner() == player) | ||||
| 		return heroes[0]; | ||||
| 	for(int i = 0; i < sides.size(); i++) | ||||
| 		if(sides[i].color == player) | ||||
| 			return sides[i].hero; | ||||
|  | ||||
| 	return heroes[1]; | ||||
| 	logGlobal->errorStream() << "Player " << player << " is not in battle!"; | ||||
| 	return nullptr; | ||||
| } | ||||
|  | ||||
| std::vector<ui32> BattleInfo::calculateResistedStacks(const CSpell * sp, const CGHeroInstance * caster, const CGHeroInstance * hero2, const std::vector<const CStack*> affectedCreatures, PlayerColor casterSideOwner, ECastingMode::ECastingMode mode, int usedSpellPower, int spellLevel) const | ||||
| @@ -767,13 +764,13 @@ std::vector<ui32> BattleInfo::calculateResistedStacks(const CSpell * sp, const C | ||||
|  | ||||
| PlayerColor BattleInfo::theOtherPlayer(PlayerColor player) const | ||||
| { | ||||
| 	return sides[!whatSide(player)]; | ||||
| 	return sides[!whatSide(player)].color; | ||||
| } | ||||
|  | ||||
| ui8 BattleInfo::whatSide(PlayerColor player) const | ||||
| { | ||||
| 	for(int i = 0; i < ARRAY_COUNT(sides); i++) | ||||
| 		if(sides[i] == player) | ||||
| 	for(int i = 0; i < sides.size(); i++) | ||||
| 		if(sides[i].color == player) | ||||
| 			return i; | ||||
|  | ||||
|     logGlobal->warnStream() << "BattleInfo::whatSide: Player " << player << " is not in battle!"; | ||||
| @@ -841,6 +838,16 @@ BattleInfo::BattleInfo() | ||||
| 	setNodeType(BATTLE); | ||||
| } | ||||
|  | ||||
| CArmedInstance * BattleInfo::battleGetArmyObject(ui8 side) const | ||||
| { | ||||
| 	return const_cast<CArmedInstance*>(CBattleInfoEssentials::battleGetArmyObject(side)); | ||||
| } | ||||
|  | ||||
| CGHeroInstance * BattleInfo::battleGetFightingHero(ui8 side) const | ||||
| { | ||||
| 	return const_cast<CGHeroInstance*>(CBattleInfoEssentials::battleGetFightingHero(side)); | ||||
| } | ||||
|  | ||||
| CStack::CStack(const CStackInstance *Base, PlayerColor O, int I, bool AO, SlotID S) | ||||
| 	: base(Base), ID(I), owner(O), slot(S), attackerOwned(AO), | ||||
| 	counterAttacks(1) | ||||
| @@ -1287,3 +1294,21 @@ CMP_stack::CMP_stack( int Phase /*= 1*/, int Turn ) | ||||
| 	turn = Turn; | ||||
| } | ||||
|  | ||||
|  | ||||
| SideInBattle::SideInBattle() | ||||
| { | ||||
| 	hero = nullptr; | ||||
| 	armyObject = nullptr; | ||||
| 	castSpellsCount = 0; | ||||
| 	enchanterCounter = 0; | ||||
| } | ||||
|  | ||||
| void SideInBattle::init(const CGHeroInstance *Hero, const CArmedInstance *Army) | ||||
| { | ||||
| 	hero = Hero; | ||||
| 	armyObject = Army; | ||||
| 	color = armyObject->getOwner(); | ||||
|  | ||||
| 	if(color == PlayerColor::UNFLAGGABLE) | ||||
| 		color = PlayerColor::NEUTRAL; | ||||
| } | ||||
|   | ||||
| @@ -41,22 +41,36 @@ struct DLL_LINKAGE SiegeInfo | ||||
| 	} | ||||
| }; | ||||
|  | ||||
| struct DLL_LINKAGE SideInBattle | ||||
| { | ||||
| 	PlayerColor color; | ||||
| 	const CGHeroInstance *hero; //may be NULL if army is not commanded by hero | ||||
| 	const CArmedInstance *armyObject; //adv. map object with army that participates in battle; may be same as hero | ||||
|  | ||||
| 	ui8 castSpellsCount; //how many spells each side has cast this turn | ||||
| 	std::vector<const CSpell *> usedSpellsHistory; //every time hero casts spell, it's inserted here -> eagle eye skill | ||||
| 	si16 enchanterCounter; //tends to pass through 0, so sign is needed | ||||
|  | ||||
| 	SideInBattle(); | ||||
| 	void init(const CGHeroInstance *Hero, const CArmedInstance *Army); | ||||
|  | ||||
|  | ||||
| 	template <typename Handler> void serialize(Handler &h, const int version) | ||||
| 	{ | ||||
| 		h & color & hero & armyObject; | ||||
| 		h & castSpellsCount & usedSpellsHistory & enchanterCounter; | ||||
| 	} | ||||
| }; | ||||
|  | ||||
| struct DLL_LINKAGE BattleInfo : public CBonusSystemNode, public CBattleInfoCallback | ||||
| { | ||||
| 	PlayerColor sides[2]; //sides[0] - attacker, sides[1] - defender | ||||
| 	std::array<SideInBattle, 2> sides; //sides[0] - attacker, sides[1] - defender | ||||
| 	si32 round, activeStack, selectedStack; | ||||
| 	CGTownInstance::EFortLevel siege; | ||||
| 	const CGTownInstance * town; //used during town siege - id of attacked town; -1 if not town defence | ||||
| 	int3 tile; //for background and bonuses | ||||
| 	CGHeroInstance* heroes[2]; | ||||
| 	CArmedInstance *belligerents[2]; //may be same as heroes | ||||
| 	std::vector<CStack*> stacks; | ||||
| 	std::vector<shared_ptr<CObstacleInstance> > obstacles; | ||||
| 	ui8 castSpells[2]; //how many spells each side has cast this turn [0] - attacker, [1] - defender | ||||
| 	std::vector<const CSpell *> usedSpellsHistory[2]; //each time hero casts spell, it's inserted here -> eagle eye skill | ||||
| 	si16 enchanterCounter[2]; //tends to pass through 0, so sign is needed | ||||
| 	SiegeInfo si; | ||||
|  | ||||
| 	BFieldType battlefieldType; //like !!BA:B | ||||
| @@ -67,10 +81,9 @@ struct DLL_LINKAGE BattleInfo : public CBonusSystemNode, public CBattleInfoCallb | ||||
|  | ||||
| 	template <typename Handler> void serialize(Handler &h, const int version) | ||||
| 	{ | ||||
| 		h & sides & round & activeStack & selectedStack & siege & town & tile & stacks & belligerents & obstacles | ||||
| 			& castSpells & si & battlefieldType & terrainType; | ||||
| 		h & heroes; | ||||
| 		h & usedSpellsHistory & enchanterCounter; | ||||
| 		h & sides; | ||||
| 		h & round & activeStack & selectedStack & siege & town & tile & stacks & obstacles | ||||
| 			& si & battlefieldType & terrainType; | ||||
| 		h & tacticsSide & tacticDistance; | ||||
| 		h & static_cast<CBonusSystemNode&>(*this); | ||||
| 	} | ||||
| @@ -82,6 +95,10 @@ struct DLL_LINKAGE BattleInfo : public CBonusSystemNode, public CBattleInfoCallb | ||||
| 	////////////////////////////////////////////////////////////////////////// | ||||
| 	CStack * getStackT(BattleHex tileID, bool onlyAlive = true); | ||||
| 	CStack * getStack(int stackID, bool onlyAlive = true); | ||||
| 	using CBattleInfoEssentials::battleGetArmyObject; | ||||
| 	CArmedInstance * battleGetArmyObject(ui8 side) const;  | ||||
| 	using CBattleInfoEssentials::battleGetFightingHero; | ||||
| 	CGHeroInstance * battleGetFightingHero(ui8 side) const;  | ||||
|  | ||||
| 	const CStack * getNextStack() const; //which stack will have turn after current one | ||||
| 	//void getStackQueue(std::vector<const CStack *> &out, int howMany, int turn = 0, int lastMoved = -1) const; //returns stack in order of their movement action | ||||
| @@ -170,7 +187,7 @@ public: | ||||
| 	bool canBeHealed() const; //for first aid tent - only harmed stacks that are not war machines | ||||
| 	ui32 Speed(int turn = 0, bool useBind = false) const; //get speed of creature with all modificators | ||||
| 	ui32 level() const; | ||||
| 	si32 magicResistance() const; //include aura of resistance | ||||
| 	si32 magicResistance() const override; //include aura of resistance | ||||
| 	static void stackEffectToFeature(std::vector<Bonus> & sf, const Bonus & sse); | ||||
| 	std::vector<si32> activeSpells() const; //returns vector of active spell IDs sorted by time of cast | ||||
| 	const CGHeroInstance *getMyHero() const; //if stack belongs to hero (directly or was by him summoned) returns hero, nullptr otherwise | ||||
|   | ||||
| @@ -221,9 +221,9 @@ BattlePerspective::BattlePerspective CBattleInfoEssentials::battleGetMySide() co | ||||
| 	RETURN_IF_NOT_BATTLE(BattlePerspective::INVALID); | ||||
| 	if(!player) | ||||
| 		return BattlePerspective::ALL_KNOWING; | ||||
| 	if(*player == getBattle()->sides[0]) | ||||
| 	if(*player == getBattle()->sides[0].color) | ||||
| 		return BattlePerspective::LEFT_SIDE; | ||||
| 	if(*player == getBattle()->sides[1]) | ||||
| 	if(*player == getBattle()->sides[1].color) | ||||
| 		return BattlePerspective::RIGHT_SIDE; | ||||
|  | ||||
|     logGlobal->errorStream() << "Cannot find player " << *player << " in battle!"; | ||||
| @@ -281,12 +281,30 @@ const CGHeroInstance * CBattleInfoEssentials::battleGetFightingHero(ui8 side) co | ||||
| 		return nullptr; | ||||
| 	} | ||||
|  | ||||
| 	return getBattle()->heroes[side]; | ||||
| 	return getBattle()->sides[side].hero; | ||||
| } | ||||
|  | ||||
| const CArmedInstance * CBattleInfoEssentials::battleGetArmyObject(ui8 side) const | ||||
| { | ||||
| 	RETURN_IF_NOT_BATTLE(nullptr); | ||||
| 	if(side > 1) | ||||
| 	{ | ||||
| 		logGlobal->errorStream() << "FIXME: " <<  __FUNCTION__ << " wrong argument!"; | ||||
| 		return nullptr; | ||||
| 	} | ||||
|  | ||||
| 	if(!battleDoWeKnowAbout(side)) | ||||
| 	{ | ||||
| 		logGlobal->errorStream() << "FIXME: " <<  __FUNCTION__ << " access check "; | ||||
| 		return nullptr; | ||||
| 	} | ||||
|  | ||||
| 	return getBattle()->sides[side].armyObject; | ||||
| } | ||||
|  | ||||
| InfoAboutHero CBattleInfoEssentials::battleGetHeroInfo( ui8 side ) const | ||||
| { | ||||
| 	auto hero = getBattle()->heroes[side]; | ||||
| 	auto hero = getBattle()->sides[side].hero; | ||||
| 	if(!hero) | ||||
| 	{ | ||||
|         logGlobal->warnStream() << __FUNCTION__ << ": side " << (int)side << " does not have hero!"; | ||||
| @@ -299,7 +317,7 @@ InfoAboutHero CBattleInfoEssentials::battleGetHeroInfo( ui8 side ) const | ||||
| int CBattleInfoEssentials::battleCastSpells(ui8 side) const | ||||
| { | ||||
| 	RETURN_IF_NOT_BATTLE(-1); | ||||
| 	return getBattle()->castSpells[side]; | ||||
| 	return getBattle()->sides[side].castSpellsCount; | ||||
| } | ||||
|  | ||||
| ESpellCastProblem::ESpellCastProblem CBattleInfoCallback::battleCanCastSpell(PlayerColor player, ECastingMode::ECastingMode mode) const | ||||
| @@ -366,7 +384,7 @@ bool CBattleInfoEssentials::battleCanFlee(PlayerColor player) const | ||||
| ui8 CBattleInfoEssentials::playerToSide(PlayerColor player) const | ||||
| { | ||||
| 	RETURN_IF_NOT_BATTLE(-1); | ||||
| 	int ret = vstd::find_pos(getBattle()->sides, player); | ||||
| 	int ret = vstd::find_pos_if(getBattle()->sides, [=](const SideInBattle &side){ return side.color == player; }); | ||||
| 	if(ret < 0) | ||||
|         logGlobal->warnStream() << "Cannot find side for player " << player; | ||||
|  | ||||
| @@ -390,7 +408,7 @@ bool CBattleInfoEssentials::battleHasHero(ui8 side) const | ||||
| { | ||||
| 	RETURN_IF_NOT_BATTLE(false); | ||||
| 	assert(side < 2); | ||||
| 	return getBattle()->heroes[side]; | ||||
| 	return getBattle()->sides[side].hero; | ||||
| } | ||||
|  | ||||
| ui8 CBattleInfoEssentials::battleGetWallState(int partOfWall) const | ||||
|   | ||||
| @@ -20,6 +20,7 @@ struct BattleInfo; | ||||
| struct CObstacleInstance; | ||||
| class IBonusBearer; | ||||
| struct InfoAboutHero; | ||||
| class CArmedInstance; | ||||
|  | ||||
| namespace boost | ||||
| {class shared_mutex;} | ||||
| @@ -182,6 +183,7 @@ public: | ||||
| 	bool battleHasHero(ui8 side) const; | ||||
| 	int battleCastSpells(ui8 side) const; //how many spells has given side casted | ||||
| 	const CGHeroInstance * battleGetFightingHero(ui8 side) const; //depracated for players callback, easy to get wrong | ||||
| 	const CArmedInstance * battleGetArmyObject(ui8 side) const;  | ||||
| 	InfoAboutHero battleGetHeroInfo(ui8 side) const; | ||||
|  | ||||
| 	//helpers | ||||
|   | ||||
| @@ -1015,10 +1015,10 @@ DLL_LINKAGE void BattleStart::applyGs( CGameState *gs ) | ||||
|  | ||||
| DLL_LINKAGE void BattleNextRound::applyGs( CGameState *gs ) | ||||
| { | ||||
| 	gs->curB->castSpells[0] = gs->curB->castSpells[1] = 0; | ||||
| 	for (int i = 0; i < 2; ++i) | ||||
| 	{ | ||||
| 		vstd::amax(--gs->curB->enchanterCounter[i], 0); | ||||
| 		gs->curB->sides[i].castSpellsCount = 0; | ||||
| 		vstd::amax(--gs->curB->sides[i].enchanterCounter, 0); | ||||
| 	} | ||||
|  | ||||
| 	gs->curB->round = round; | ||||
| @@ -1106,11 +1106,10 @@ void BattleResult::applyGs( CGameState *gs ) | ||||
| 	for (auto & elem : gs->curB->stacks) | ||||
| 		delete elem; | ||||
|  | ||||
| 	CGHeroInstance *h; | ||||
| 	for (int i = 0; i < 2; ++i) | ||||
|  | ||||
| 	for(int i = 0; i < 2; ++i) | ||||
| 	{ | ||||
| 		h = gs->curB->heroes[i]; | ||||
| 		if (h) | ||||
| 		if(auto h = gs->curB->battleGetFightingHero(i)) | ||||
| 		{ | ||||
| 			h->getBonusList().remove_if(Bonus::OneBattle); 	//remove any "until next battle" bonuses | ||||
| 			if (h->commander && h->commander->alive) | ||||
| @@ -1123,17 +1122,18 @@ void BattleResult::applyGs( CGameState *gs ) | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	if (VLC->modh->modules.STACK_EXP) | ||||
| 	if(VLC->modh->modules.STACK_EXP) | ||||
| 	{ | ||||
| 		if (exp[0]) //checking local array is easier than dereferencing this crap twice | ||||
| 			gs->curB->belligerents[0]->giveStackExp(exp[0]); | ||||
| 		if (exp[1]) | ||||
| 			gs->curB->belligerents[1]->giveStackExp(exp[1]); | ||||
|  | ||||
| 		for(int i = 0; i < 2; i++) | ||||
| 			if(exp[i]) | ||||
| 				gs->curB->battleGetArmyObject(i)->giveStackExp(exp[i]); | ||||
| 		 | ||||
| 		CBonusSystemNode::treeHasChanged(); | ||||
| 	} | ||||
|  | ||||
| 	gs->curB->belligerents[0]->battle = gs->curB->belligerents[1]->battle = nullptr; | ||||
| 	for(int i = 0; i < 2; i++) | ||||
| 		gs->curB->battleGetArmyObject(i)->battle = nullptr; | ||||
|  | ||||
| 	gs->curB.dellNull(); | ||||
| } | ||||
|  | ||||
| @@ -1246,7 +1246,7 @@ DLL_LINKAGE void StartAction::applyGs( CGameState *gs ) | ||||
| 	} | ||||
| 	else | ||||
| 	{ | ||||
| 		gs->curB->usedSpellsHistory[ba.side].push_back(SpellID(ba.additionalInfo).toSpell()); | ||||
| 		gs->curB->sides[ba.side].usedSpellsHistory.push_back(SpellID(ba.additionalInfo).toSpell()); | ||||
| 	} | ||||
|  | ||||
| 	switch(ba.actionType) | ||||
| @@ -1273,8 +1273,8 @@ DLL_LINKAGE void BattleSpellCast::applyGs( CGameState *gs ) | ||||
| 	assert(gs->curB); | ||||
| 	if (castedByHero) | ||||
| 	{ | ||||
| 		CGHeroInstance * h = gs->curB->heroes[side]; | ||||
| 		CGHeroInstance * enemy = gs->curB->heroes[1-side]; | ||||
| 		CGHeroInstance * h = gs->curB->battleGetFightingHero(side); | ||||
| 		CGHeroInstance * enemy = gs->curB->battleGetFightingHero(!side); | ||||
|  | ||||
| 		h->mana -= spellCost; | ||||
| 			vstd::amax(h->mana, 0); | ||||
| @@ -1282,7 +1282,7 @@ DLL_LINKAGE void BattleSpellCast::applyGs( CGameState *gs ) | ||||
| 			enemy->mana += manaGained; | ||||
| 		if (side < 2) | ||||
| 		{ | ||||
| 			gs->curB->castSpells[side]++; | ||||
| 			gs->curB->sides[side].castSpellsCount++; | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| @@ -1532,12 +1532,12 @@ DLL_LINKAGE void BattleSetStackProperty::applyGs(CGameState *gs) | ||||
| 		} | ||||
| 		case ENCHANTER_COUNTER: | ||||
| 		{ | ||||
| 			int side = gs->curB->whatSide(stack->owner); | ||||
| 			auto & counter = gs->curB->sides[gs->curB->whatSide(stack->owner)].enchanterCounter; | ||||
| 			if (absolute) | ||||
| 				gs->curB->enchanterCounter[side] = val; | ||||
| 				counter = val; | ||||
| 			else | ||||
| 				gs->curB->enchanterCounter[side] += val; | ||||
| 			vstd::amax(gs->curB->enchanterCounter[side], 0); | ||||
| 				counter += val; | ||||
| 			vstd::amax(counter, 0); | ||||
| 			break; | ||||
| 		} | ||||
| 		case UNBIND: | ||||
|   | ||||
| @@ -428,8 +428,8 @@ void CGameHandler::endBattle(int3 tile, const CGHeroInstance *hero1, const CGHer | ||||
| 	if (hero2) | ||||
| 		battleResult.data->exp[1] = hero2->calculateXp(battleResult.data->exp[1]); | ||||
|  | ||||
| 	const CArmedInstance *bEndArmy1 = gs->curB->belligerents[0]; | ||||
| 	const CArmedInstance *bEndArmy2 = gs->curB->belligerents[1]; | ||||
| 	const CArmedInstance *bEndArmy1 = gs->curB->sides[0].armyObject; | ||||
| 	const CArmedInstance *bEndArmy2 = gs->curB->sides[1].armyObject; | ||||
| 	const BattleResult::EResult result = battleResult.get()->result; | ||||
|  | ||||
| 	auto findBattleQuery = [this] () -> shared_ptr<CBattleQuery> | ||||
| @@ -452,8 +452,8 @@ void CGameHandler::endBattle(int3 tile, const CGHeroInstance *hero1, const CGHer | ||||
| 			battleQuery = make_shared<CBattleQuery>(gs->curB); | ||||
| 		} | ||||
| 	} | ||||
| 	if(battleQuery != queries.topQuery(gs->curB->sides[0])) | ||||
| 		complain("Player " + boost::lexical_cast<std::string>(gs->curB->sides[0]) + " although in battle has no battle query at the top!"); | ||||
| 	if(battleQuery != queries.topQuery(gs->curB->sides[0].color)) | ||||
| 		complain("Player " + boost::lexical_cast<std::string>(gs->curB->sides[0].color) + " although in battle has no battle query at the top!"); | ||||
| 		 | ||||
| 	battleQuery->result = *battleResult.data; | ||||
|  | ||||
| @@ -480,7 +480,7 @@ void CGameHandler::endBattle(int3 tile, const CGHeroInstance *hero1, const CGHer | ||||
| 		{ | ||||
| 			int maxLevel = eagleEyeLevel + 1; | ||||
| 			double eagleEyeChance = finishingBattle->winnerHero->valOfBonuses(Bonus::SECONDARY_SKILL_PREMY, SecondarySkill::EAGLE_EYE); | ||||
| 			for(const CSpell *sp : gs->curB->usedSpellsHistory[!battleResult.data->winner]) | ||||
| 			for(const CSpell *sp : gs->curB->sides[!battleResult.data->winner].usedSpellsHistory) | ||||
| 				if(sp->level <= maxLevel && !vstd::contains(finishingBattle->winnerHero->spells, sp->id) && rand() % 100 < eagleEyeChance) | ||||
| 					cs.spells.insert(sp->id); | ||||
| 		} | ||||
| @@ -534,7 +534,7 @@ void CGameHandler::endBattle(int3 tile, const CGHeroInstance *hero1, const CGHer | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
| 		for (auto armySlot : gs->curB->belligerents[!battleResult.data->winner]->stacks) | ||||
| 		for (auto armySlot : gs->curB->battleGetArmyObject(!battleResult.data->winner)->stacks) | ||||
| 		{ | ||||
| 			auto artifactsWorn = armySlot.second->artifactsWorn; | ||||
| 			for (auto artSlot : artifactsWorn) | ||||
| @@ -647,7 +647,7 @@ void CGameHandler::battleAfterLevelUp( const BattleResult &result ) | ||||
| 	logGlobal->traceStream() << "Decremented queries count to " << finishingBattle->remainingBattleQueriesCount; | ||||
|  | ||||
| 	if(finishingBattle->remainingBattleQueriesCount > 0) | ||||
| 		//Battle results will be hndled when all battle queries are closed | ||||
| 		//Battle results will be handled when all battle queries are closed | ||||
| 		return; | ||||
|  | ||||
| 	//TODO consider if we really want it to work like above. ATM each player as unblocked as soon as possible | ||||
| @@ -657,7 +657,7 @@ void CGameHandler::battleAfterLevelUp( const BattleResult &result ) | ||||
| 	// Necromancy if applicable. | ||||
| 	const CStackBasicDescriptor raisedStack = finishingBattle->winnerHero ? finishingBattle->winnerHero->calculateNecromancy(*battleResult.data) : CStackBasicDescriptor(); | ||||
| 	// Give raised units to winner and show dialog, if any were raised, | ||||
| 	// units will be given after casualities are taken | ||||
| 	// units will be given after casualties are taken | ||||
| 	const SlotID necroSlot = raisedStack.type ? finishingBattle->winnerHero->getSlotFor(raisedStack.type) : SlotID(); | ||||
|  | ||||
| 	if (necroSlot != SlotID()) | ||||
| @@ -706,12 +706,11 @@ void CGameHandler::prepareAttack(BattleAttack &bat, const CStack *att, const CSt | ||||
| { | ||||
| 	bat.bsa.clear(); | ||||
| 	bat.stackAttacking = att->ID; | ||||
| 	int attackerLuck = att->LuckVal(); | ||||
| 	const CGHeroInstance * h0 = gs->curB->heroes[0], | ||||
| 		* h1 = gs->curB->heroes[1]; | ||||
| 	const int attackerLuck = att->LuckVal(); | ||||
| 	 | ||||
| 	auto sideHeroBlocksLuck = [](const SideInBattle &side){ return NBonus::hasOfType(side.hero, Bonus::BLOCK_LUCK); }; | ||||
|  | ||||
| 	if(!(h0 && NBonus::hasOfType(h0, Bonus::BLOCK_LUCK)) && | ||||
| 	   !(h1 && NBonus::hasOfType(h1, Bonus::BLOCK_LUCK))) | ||||
| 	if(!vstd::contains_if(gs->curB->sides, sideHeroBlocksLuck)) | ||||
| 	{ | ||||
| 		if(attackerLuck > 0  && rand()%24 < attackerLuck) | ||||
| 		{ | ||||
| @@ -3339,7 +3338,7 @@ bool CGameHandler::makeBattleAction( BattleAction &ba ) | ||||
| 		} | ||||
| 	case Battle::RETREAT: //retreat/flee | ||||
| 		{ | ||||
| 			if(!gs->curB->battleCanFlee(gs->curB->sides[ba.side])) | ||||
| 			if(!gs->curB->battleCanFlee(gs->curB->sides[ba.side].color)) | ||||
| 				complain("Cannot retreat!"); | ||||
| 			else | ||||
| 				setBattleResult(BattleResult::ESCAPE, !ba.side); //surrendering side loses | ||||
| @@ -3347,7 +3346,7 @@ bool CGameHandler::makeBattleAction( BattleAction &ba ) | ||||
| 		} | ||||
| 	case Battle::SURRENDER: | ||||
| 		{ | ||||
| 			PlayerColor player = gs->curB->sides[ba.side]; | ||||
| 			PlayerColor player = gs->curB->sides[ba.side].color; | ||||
| 			int cost = gs->curB->battleGetSurrenderCost(player); | ||||
| 			if(cost < 0) | ||||
| 				complain("Cannot surrender!"); | ||||
| @@ -3477,7 +3476,7 @@ bool CGameHandler::makeBattleAction( BattleAction &ba ) | ||||
|  | ||||
| 			//second shot for ballista, only if hero has advanced artillery | ||||
|  | ||||
| 			const CGHeroInstance * attackingHero = gs->curB->heroes[ba.side]; | ||||
| 			const CGHeroInstance * attackingHero = gs->curB->battleGetFightingHero(ba.side); | ||||
|  | ||||
| 			if( destStack->alive() | ||||
| 			    && (stack->getCreature()->idNumber == CreatureID::BALLISTA) | ||||
| @@ -3516,7 +3515,7 @@ bool CGameHandler::makeBattleAction( BattleAction &ba ) | ||||
| 		{ | ||||
| 			StartAction start_action(ba); | ||||
| 			sendAndApply(&start_action); | ||||
| 			const CGHeroInstance * attackingHero = gs->curB->heroes[ba.side]; | ||||
| 			const CGHeroInstance * attackingHero = gs->curB->battleGetFightingHero(ba.side); | ||||
| 			CHeroHandler::SBallisticsLevelInfo sbi = VLC->heroh->ballistics[attackingHero->getSecSkillLevel(SecondarySkill::BALLISTICS)]; | ||||
|  | ||||
| 			EWallParts::EWallParts attackedPart = gs->curB->battleHexToWallPart(ba.destinationTile); | ||||
| @@ -3620,7 +3619,7 @@ bool CGameHandler::makeBattleAction( BattleAction &ba ) | ||||
| 		{ | ||||
| 			StartAction start_action(ba); | ||||
| 			sendAndApply(&start_action); | ||||
| 			const CGHeroInstance * attackingHero = gs->curB->heroes[ba.side]; | ||||
| 			const CGHeroInstance * attackingHero = gs->curB->battleGetFightingHero(ba.side); | ||||
| 			const CStack *healer = gs->curB->battleGetStackByID(ba.stackNumber), | ||||
| 				*destStack = gs->curB->battleGetStackByPos(ba.destinationTile); | ||||
|  | ||||
| @@ -4385,7 +4384,7 @@ void CGameHandler::handleSpellCasting( SpellID spellID, int spellLvl, BattleHex | ||||
| 				std::vector<CStack *> & battleStacks = gs->curB->stacks; | ||||
| 				for (auto & battleStack : battleStacks) | ||||
| 				{ | ||||
| 					if(battleStack->owner == gs->curB->sides[casterSide]) //get enemy stacks which can be affected by this spell | ||||
| 					if(battleStack->owner == gs->curB->sides[casterSide].color) //get enemy stacks which can be affected by this spell | ||||
| 					{ | ||||
| 						if (!gs->curB->battleIsImmune(nullptr, spell, ECastingMode::MAGIC_MIRROR, battleStack->position)) | ||||
| 							mirrorTargets.push_back(battleStack); | ||||
| @@ -4410,8 +4409,8 @@ bool CGameHandler::makeCustomAction( BattleAction &ba ) | ||||
| 			COMPLAIN_RET_FALSE_IF(ba.side > 1, "Side must be 0 or 1!"); | ||||
| 				 | ||||
|  | ||||
| 			const CGHeroInstance *h = gs->curB->heroes[ba.side]; | ||||
| 			const CGHeroInstance *secondHero = gs->curB->heroes[!ba.side]; | ||||
| 			const CGHeroInstance *h = gs->curB->battleGetFightingHero(ba.side); | ||||
| 			const CGHeroInstance *secondHero = gs->curB->battleGetFightingHero(!ba.side); | ||||
| 			if(!h) | ||||
| 			{ | ||||
|                 logGlobal->warnStream() << "Wrong caster!"; | ||||
| @@ -4534,10 +4533,11 @@ void CGameHandler::stackTurnTrigger(const CStack * st) | ||||
| 		if (st->hasBonusOfType(Bonus::MANA_DRAIN) && !vstd::contains(st->state, EBattleStackState::DRAINED_MANA)) | ||||
| 		{ | ||||
| 			const CGHeroInstance * enemy = gs->curB->getHero(gs->curB->theOtherPlayer(st->owner)); | ||||
| 			const CGHeroInstance * owner = gs->curB->getHero(st->owner); | ||||
| 			if (enemy) | ||||
| 			{ | ||||
| 				ui32 manaDrained = st->valOfBonuses(Bonus::MANA_DRAIN); | ||||
| 				vstd::amin (manaDrained, gs->curB->heroes[0]->mana); | ||||
| 				vstd::amin(manaDrained, gs->curB->battleGetFightingHero(0)->mana); | ||||
| 				if (manaDrained) | ||||
| 				{ | ||||
| 					bte.effect = Bonus::MANA_DRAIN; | ||||
| @@ -4569,7 +4569,7 @@ void CGameHandler::stackTurnTrigger(const CStack * st) | ||||
| 		} | ||||
| 		BonusList bl = *(st->getBonuses(Selector::type(Bonus::ENCHANTER))); | ||||
| 		int side = gs->curB->whatSide(st->owner); | ||||
| 		if (bl.size() && st->casts && !gs->curB->enchanterCounter[side]) | ||||
| 		if (bl.size() && st->casts && !gs->curB->sides[side].enchanterCounter) | ||||
| 		{ | ||||
| 			int index = rand() % bl.size(); | ||||
| 			SpellID spellID = SpellID(bl[index]->subtype); | ||||
| @@ -4624,7 +4624,7 @@ void CGameHandler::handleDamageFromObstacle(const CObstacleInstance &obstacle, c | ||||
| 	//helper info | ||||
| 	const SpellCreatedObstacle *spellObstacle = dynamic_cast<const SpellCreatedObstacle*>(&obstacle); //not nice but we may need spell params | ||||
| 	const ui8 side = curStack->attackerOwned; //if enemy is defending (false = 0), side of our hero is also 0 (attacker) | ||||
| 	const CGHeroInstance *hero = gs->curB->heroes[side]; | ||||
| 	const CGHeroInstance *hero = gs->curB->battleGetFightingHero(side); | ||||
|  | ||||
| 	if(obstacle.obstacleType == CObstacleInstance::MOAT) | ||||
| 	{ | ||||
| @@ -5819,14 +5819,15 @@ void CGameHandler::runBattle() | ||||
| 	} | ||||
|  | ||||
| 	//spells opening battle | ||||
| 	for(int i=0; i<ARRAY_COUNT(gs->curB->heroes); ++i) | ||||
| 	for(int i = 0; i < 2; ++i) | ||||
| 	{ | ||||
| 		if(gs->curB->heroes[i] && gs->curB->heroes[i]->hasBonusOfType(Bonus::OPENING_BATTLE_SPELL)) | ||||
| 		auto h = gs->curB->battleGetFightingHero(i); | ||||
| 		if(h && h->hasBonusOfType(Bonus::OPENING_BATTLE_SPELL)) | ||||
| 		{ | ||||
| 			TBonusListPtr bl = gs->curB->heroes[i]->getBonuses(Selector::type(Bonus::OPENING_BATTLE_SPELL)); | ||||
| 			TBonusListPtr bl = h->getBonuses(Selector::type(Bonus::OPENING_BATTLE_SPELL)); | ||||
| 			for (Bonus *b : *bl) | ||||
| 			{ | ||||
| 				handleSpellCasting(SpellID(b->subtype), 3, -1, 0, gs->curB->heroes[i]->tempOwner, nullptr, gs->curB->heroes[1-i], b->val, ECastingMode::HERO_CASTING, nullptr); | ||||
| 				handleSpellCasting(SpellID(b->subtype), 3, -1, 0, h->tempOwner, nullptr, gs->curB->battleGetFightingHero(1-i), b->val, ECastingMode::HERO_CASTING, nullptr); | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| @@ -5853,7 +5854,8 @@ void CGameHandler::runBattle() | ||||
| 			//check for bad morale => freeze | ||||
| 			int nextStackMorale = next->MoraleVal(); | ||||
| 			if( nextStackMorale < 0 && | ||||
| 				!(NBonus::hasOfType(gs->curB->heroes[0], Bonus::BLOCK_MORALE) || NBonus::hasOfType(gs->curB->heroes[1], Bonus::BLOCK_MORALE)) //checking if gs->curB->heroes have (or don't have) morale blocking bonuses) | ||||
| 				!(NBonus::hasOfType(gs->curB->battleGetFightingHero(0), Bonus::BLOCK_MORALE) | ||||
| 				   || NBonus::hasOfType(gs->curB->battleGetFightingHero(1), Bonus::BLOCK_MORALE)) //checking if gs->curB->heroes have (or don't have) morale blocking bonuses) | ||||
| 				) | ||||
| 			{ | ||||
| 				if( rand()%24   <   -2 * nextStackMorale) | ||||
| @@ -6005,7 +6007,8 @@ void CGameHandler::runBattle() | ||||
| 					&& !vstd::contains(next->state, EBattleStackState::FEAR) | ||||
| 					&&  next->alive() | ||||
| 					&&  nextStackMorale > 0 | ||||
| 					&& !(NBonus::hasOfType(gs->curB->heroes[0], Bonus::BLOCK_MORALE) || NBonus::hasOfType(gs->curB->heroes[1], Bonus::BLOCK_MORALE)) //checking if gs->curB->heroes have (or don't have) morale blocking bonuses | ||||
| 					&& !(NBonus::hasOfType(gs->curB->battleGetFightingHero(0), Bonus::BLOCK_MORALE) | ||||
| 						|| NBonus::hasOfType(gs->curB->battleGetFightingHero(1), Bonus::BLOCK_MORALE)) //checking if gs->curB->heroes have (or don't have) morale blocking bonuses | ||||
| 					) | ||||
| 				{ | ||||
| 					if(rand()%24 < nextStackMorale) //this stack hasn't got morale this turn | ||||
| @@ -6033,7 +6036,7 @@ void CGameHandler::runBattle() | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	endBattle(gs->curB->tile, gs->curB->heroes[0], gs->curB->heroes[1]); | ||||
| 	endBattle(gs->curB->tile, gs->curB->battleGetFightingHero(0), gs->curB->battleGetFightingHero(1)); | ||||
| } | ||||
|  | ||||
| bool CGameHandler::makeAutomaticAction(const CStack *stack, BattleAction &ba) | ||||
| @@ -6210,7 +6213,7 @@ bool CGameHandler::isVisitCoveredByAnotherQuery(const CGObjectInstance *obj, con | ||||
| void CGameHandler::duelFinished() | ||||
| { | ||||
| 	auto si = getStartInfo(); | ||||
| 	auto getName = [&](int i){ return si->getIthPlayersSettings(gs->curB->sides[i]).name; }; | ||||
| 	auto getName = [&](int i){ return si->getIthPlayersSettings(gs->curB->sides[i].color).name; }; | ||||
|  | ||||
| 	int casualtiesPoints = 0; | ||||
| 	logGlobal->debugStream() << boost::format("Winner side %d\nWinner casualties:")  | ||||
| @@ -6310,10 +6313,10 @@ CGameHandler::FinishingBattleHelper::FinishingBattleHelper(shared_ptr<const CBat | ||||
| 	auto &result = *Query->result; | ||||
| 	auto &info = *Query->bi; | ||||
|  | ||||
| 	winnerHero = result.winner != 0 ? info.heroes[1] : info.heroes[0]; | ||||
| 	loserHero = result.winner != 0 ? info.heroes[0] : info.heroes[1]; | ||||
| 	victor = info.sides[result.winner]; | ||||
| 	loser = info.sides[!result.winner]; | ||||
| 	winnerHero = result.winner != 0 ? info.sides[1].hero : info.sides[0].hero; | ||||
| 	loserHero = result.winner != 0 ? info.sides[0].hero : info.sides[1].hero; | ||||
| 	victor = info.sides[result.winner].color; | ||||
| 	loser = info.sides[!result.winner].color; | ||||
| 	duel = Duel; | ||||
| 	remainingBattleQueriesCount = RemainingBattleQueriesCount; | ||||
| } | ||||
|   | ||||
| @@ -222,13 +222,13 @@ void CBattleQuery::notifyObjectAboutRemoval(const CObjectVisitQuery &objectVisit | ||||
|  | ||||
| CBattleQuery::CBattleQuery(const BattleInfo *Bi) | ||||
| { | ||||
| 	belligerents[0] = Bi->belligerents[0]; | ||||
| 	belligerents[1] = Bi->belligerents[1]; | ||||
| 	belligerents[0] = Bi->sides[0].armyObject; | ||||
| 	belligerents[1] = Bi->sides[1].armyObject; | ||||
|  | ||||
| 	bi = Bi; | ||||
|  | ||||
| 	for(PlayerColor side : bi->sides) | ||||
| 		addPlayer(side); | ||||
| 	for(auto &side : bi->sides) | ||||
| 		addPlayer(side.color); | ||||
| } | ||||
|  | ||||
| CBattleQuery::CBattleQuery() | ||||
|   | ||||
| @@ -249,7 +249,7 @@ bool MakeAction::applyGh( CGameHandler *gh ) | ||||
| 		if(ba.actionType != Battle::WALK  &&  ba.actionType != Battle::END_TACTIC_PHASE   | ||||
| 			&& ba.actionType != Battle::RETREAT && ba.actionType != Battle::SURRENDER) | ||||
| 			ERROR_AND_RETURN; | ||||
| 		if(gh->connections[b->sides[b->tacticsSide]] != c)  | ||||
| 		if(gh->connections[b->sides[b->tacticsSide].color] != c)  | ||||
| 			ERROR_AND_RETURN; | ||||
| 	} | ||||
| 	else if(gh->connections[b->battleGetStackByID(b->activeStack)->owner] != c)  | ||||
|   | ||||
		Reference in New Issue
	
	Block a user