Browse Source

* minor improvements in spell immunity handling

mateuszb 14 years ago
parent
commit
6db7f1d610
6 changed files with 94 additions and 105 deletions
  1. 2 2
      CCallback.cpp
  2. 2 0
      global.h
  3. 76 8
      lib/BattleState.cpp
  4. 6 5
      lib/BattleState.h
  5. 7 89
      server/CGameHandler.cpp
  6. 1 1
      server/CGameHandler.h

+ 2 - 2
CCallback.cpp

@@ -596,7 +596,7 @@ bool CBattleCallback::battleCanCastSpell()
 	if(!gs->curB) //there is no battle
 		return false;
 
-	return gs->curB->battleCanCastSpell(player, BattleInfo::HERO_CASTING) == SpellCasting::OK;
+	return gs->curB->battleCanCastSpell(player, SpellCasting::HERO_CASTING) == SpellCasting::OK;
 }
 
 bool CBattleCallback::battleCanFlee()
@@ -1097,7 +1097,7 @@ SpellCasting::ESpellCastProblem CBattleCallback::battleCanCastThisSpell( const C
 		return SpellCasting::NO_HERO_TO_CAST_SPELL;
 	}
 
-	return gs->curB->battleCanCastThisSpell(player, spell, BattleInfo::HERO_CASTING);
+	return gs->curB->battleCanCastThisSpell(player, spell, SpellCasting::HERO_CASTING);
 }
 
 si8 CBattleCallback::battleGetTacticDist()

+ 2 - 0
global.h

@@ -316,6 +316,8 @@ namespace SpellCasting
 		SECOND_HEROS_SPELL_IMMUNITY, SPELL_LEVEL_LIMIT_EXCEEDED, NO_SPELLS_TO_DISPEL,
 		NO_APPROPRIATE_TARGET, STACK_IMMUNE_TO_SPELL, WRONG_SPELL_TARGET
 	};
+
+	enum ECastingMode {HERO_CASTING, AFTER_ATTACK_CASTING};
 }
 
 namespace Res

+ 76 - 8
lib/BattleState.cpp

@@ -1580,13 +1580,13 @@ bool BattleInfo::isInTacticRange( THex dest ) const
 		|| (tacticsSide && dest.getX() < BFIELD_WIDTH - 1 && dest.getX() >= BFIELD_WIDTH - tacticDistance - 1));
 }
 
-SpellCasting::ESpellCastProblem BattleInfo::battleCanCastSpell(int player, ECastingMode mode) const
+SpellCasting::ESpellCastProblem BattleInfo::battleCanCastSpell(int player, SpellCasting::ECastingMode mode) const
 {
 	int side = sides[0] == player ? 0 : 1;
 
 	switch (mode)
 	{
-	case HERO_CASTING:
+	case SpellCasting::HERO_CASTING:
 		{
 			if(castSpells[side] > 0)
 				return SpellCasting::ALREADY_CASTED_THIS_TURN;
@@ -1601,7 +1601,7 @@ SpellCasting::ESpellCastProblem BattleInfo::battleCanCastSpell(int player, ECast
 	return SpellCasting::OK;
 }
 
-SpellCasting::ESpellCastProblem BattleInfo::battleCanCastThisSpell( int player, const CSpell * spell, ECastingMode mode ) const
+SpellCasting::ESpellCastProblem BattleInfo::battleCanCastThisSpell( int player, const CSpell * spell, SpellCasting::ECastingMode mode ) const
 {
 	SpellCasting::ESpellCastProblem genProblem = battleCanCastSpell(player, mode);
 	if(genProblem != SpellCasting::OK)
@@ -1654,7 +1654,7 @@ SpellCasting::ESpellCastProblem BattleInfo::battleCanCastThisSpell( int player,
 	{
 	case CSpell::CREATURE:
 	case CSpell::CREATURE_EXPERT_MASSIVE:
-		if(mode == HERO_CASTING)
+		if(mode == SpellCasting::HERO_CASTING)
 		{
 			const CGHeroInstance * caster = getHero(player);
 			bool targetExists = false;
@@ -1704,7 +1704,7 @@ SpellCasting::ESpellCastProblem BattleInfo::battleCanCastThisSpell( int player,
 	return SpellCasting::OK;
 }
 
-SpellCasting::ESpellCastProblem BattleInfo::battleCanCastThisSpellHere( int player, const CSpell * spell, ECastingMode mode, THex dest )
+SpellCasting::ESpellCastProblem BattleInfo::battleCanCastThisSpellHere( int player, const CSpell * spell, SpellCasting::ECastingMode mode, THex dest )
 {
 	SpellCasting::ESpellCastProblem moreGeneralProblem = battleCanCastThisSpell(player, spell, mode);
 	if(moreGeneralProblem != SpellCasting::OK)
@@ -1721,14 +1721,27 @@ const CGHeroInstance * BattleInfo::getHero( int player ) const
 	return heroes[1];
 }
 
-SpellCasting::ESpellCastProblem BattleInfo::battleIsImmune( int player, const CSpell * spell, ECastingMode mode, THex dest ) const
+SpellCasting::ESpellCastProblem BattleInfo::battleIsImmune( int player, const CSpell * spell, SpellCasting::ECastingMode mode, THex dest ) const
 {
+	struct NegateRemover
+	{
+		bool operator()(const Bonus* b)
+		{
+			return b->source == Bonus::CREATURE_ABILITY;
+		}
+	};
 	const CStack * subject = getStackT(dest, false);
-	const CGHeroInstance * caster = mode == HERO_CASTING ? getHero(player) : NULL;
+	const CGHeroInstance * caster = mode == SpellCasting::HERO_CASTING ? getHero(player) : NULL;
 	if(subject)
 	{
+		BonusList immunities = subject->getBonuses(Selector::type(Bonus::LEVEL_SPELL_IMMUNITY));
+		if(subject->hasBonusOfType(Bonus::NEGATE_ALL_NATURAL_IMMUNITIES))
+		{
+			std::remove_if(immunities.begin(), immunities.end(), NegateRemover());
+		}
+
 		if(subject->hasBonusOfType(Bonus::SPELL_IMMUNITY, spell->id)
-			|| ( subject->hasBonusOfType(Bonus::LEVEL_SPELL_IMMUNITY) && subject->valOfBonuses(Bonus::LEVEL_SPELL_IMMUNITY) >= spell->level))
+			|| ( immunities.size() > 0 && immunities.totalValue() >= spell->level))
 		{
 			return SpellCasting::STACK_IMMUNE_TO_SPELL;
 		}
@@ -1763,6 +1776,61 @@ SpellCasting::ESpellCastProblem BattleInfo::battleIsImmune( int player, const CS
 	return SpellCasting::OK;
 }
 
+std::vector<ui32> BattleInfo::calculateResistedStacks( const CSpell * sp, const CGHeroInstance * caster, const CGHeroInstance * hero2, const std::set<CStack*> affectedCreatures, int casterSideOwner, SpellCasting::ECastingMode mode ) const
+{
+	std::vector<ui32> ret;
+	for(std::set<CStack*>::const_iterator it = affectedCreatures.begin(); it != affectedCreatures.end(); ++it)
+	{
+		if(battleIsImmune(caster->getOwner(), sp, mode, (*it)->position) != SpellCasting::OK)
+		{
+			ret.push_back((*it)->ID);
+			continue;
+		}
+
+		//non-negative spells on friendly stacks should always succeed, unless immune
+		if(sp->positiveness >= 0 && (*it)->owner == casterSideOwner)
+			continue;
+
+		const CGHeroInstance * bonusHero; //hero we should take bonuses from
+		if((*it)->owner == casterSideOwner)
+			bonusHero = caster;
+		else
+			bonusHero = hero2;
+
+		int prob = (*it)->valOfBonuses(Bonus::MAGIC_RESISTANCE); //probability of resistance in %
+		if(bonusHero)
+		{
+			//bonusHero's resistance support (secondary skils and artifacts)
+			prob += bonusHero->valOfBonuses(Bonus::MAGIC_RESISTANCE);
+			//resistance skill
+			prob += bonusHero->valOfBonuses(Bonus::SECONDARY_SKILL_PREMY, 26) / 100.0f;
+		}
+
+		if(prob > 100) prob = 100;
+
+		if(rand()%100 < prob) //immunity from resistance
+			ret.push_back((*it)->ID);
+
+	}
+
+	if(sp->id == 60) //hypnotize
+	{
+		for(std::set<CStack*>::const_iterator it = affectedCreatures.begin(); it != affectedCreatures.end(); ++it)
+		{
+			if( (*it)->hasBonusOfType(Bonus::SPELL_IMMUNITY, sp->id) //100% sure spell immunity
+				|| ( (*it)->count - 1 ) * (*it)->MaxHealth() + (*it)->firstHPleft 
+		> 
+		caster->getPrimSkillLevel(2) * 25 + sp->powers[caster->getSpellSchoolLevel(sp)]
+			)
+			{
+				ret.push_back((*it)->ID);
+			}
+		}
+	}
+
+	return ret;
+}
+
 CStack::CStack(const CStackInstance *Base, int O, int I, bool AO, int S)
 	: base(Base), ID(I), owner(O), slot(S), attackerOwned(AO),   
 	counterAttacks(1)

+ 6 - 5
lib/BattleState.h

@@ -116,12 +116,13 @@ struct DLL_EXPORT BattleInfo : public CBonusSystemNode
 	bool battleCanShoot(const CStack * stack, THex dest) const; //determines if stack with given ID shoot at the selected destination
 	const CGHeroInstance * getHero(int player) const; //returns fighting hero that belongs to given player
 
-	enum ECastingMode {HERO_CASTING, AFTER_ATTACK_CASTING};
+	SpellCasting::ESpellCastProblem battleCanCastSpell(int player, SpellCasting::ECastingMode mode) const; //returns true if there are no general issues preventing from casting a spell
+	SpellCasting::ESpellCastProblem battleCanCastThisSpell(int player, const CSpell * spell, SpellCasting::ECastingMode mode) const; //checks if given player can cast given spell
+	SpellCasting::ESpellCastProblem battleIsImmune(int player, const CSpell * spell, SpellCasting::ECastingMode mode, THex dest) const; //checks for creature immunity / anything that prevent casting *at given hex* - doesn't take into acount general problems such as not having spellbook or mana points etc.
+	SpellCasting::ESpellCastProblem battleCanCastThisSpellHere(int player, const CSpell * spell, SpellCasting::ECastingMode mode, THex dest); //checks if given player can cast given spell at given tile in given mode
+
+	std::vector<ui32> calculateResistedStacks(const CSpell * sp, const CGHeroInstance * caster, const CGHeroInstance * hero2, const std::set<CStack*> affectedCreatures, int casterSideOwner, SpellCasting::ECastingMode mode) const;
 
-	SpellCasting::ESpellCastProblem battleCanCastSpell(int player, ECastingMode mode) const; //returns true if there are no general issues preventing from casting a spell
-	SpellCasting::ESpellCastProblem battleCanCastThisSpell(int player, const CSpell * spell, ECastingMode mode) const; //checks if given player can cast given spell
-	SpellCasting::ESpellCastProblem battleIsImmune(int player, const CSpell * spell, ECastingMode mode, THex dest) const; //checks for creature immunity / anything that prevent casting *at given hex* - doesn't take into acount general problems such as not having spellbook or mana points etc.
-	SpellCasting::ESpellCastProblem battleCanCastThisSpellHere(int player, const CSpell * spell, ECastingMode mode, THex dest); //checks if given player can cast given spell at given tile in given mode
 
 	bool battleCanFlee(int player) const; //returns true if player can flee from the battle
 	const CStack * battleGetStack(THex pos, bool onlyAlive); //returns stack at given tile

+ 7 - 89
server/CGameHandler.cpp

@@ -3552,89 +3552,7 @@ void CGameHandler::playerMessage( ui8 player, const std::string &message )
 	}
 }
 
-static std::vector<ui32> calculateResistedStacks(const CSpell * sp, const CGHeroInstance * caster, const CGHeroInstance * hero2, const std::set<CStack*> affectedCreatures, int casterSideOwner)
-{
-	std::vector<ui32> ret;
-	for(std::set<CStack*>::const_iterator it = affectedCreatures.begin(); it != affectedCreatures.end(); ++it)
-	{
-		if (NBonus::hasOfType(caster, Bonus::NEGATE_ALL_NATURAL_IMMUNITIES) ||
-			NBonus::hasOfType(hero2, Bonus::NEGATE_ALL_NATURAL_IMMUNITIES))
-		{
-			//don't use natural immunities when one of heroes has this bonus
- 			BonusList bl = (*it)->getBonuses(Selector::type(Bonus::SPELL_IMMUNITY)),
-				b2 = (*it)->getBonuses(Selector::type(Bonus::LEVEL_SPELL_IMMUNITY));
-
-			bl.insert(bl.end(), b2.begin(), b2.end());
- 
- 			BOOST_FOREACH(Bonus *bb, bl)
- 			{
- 				if( (bb->type == Bonus::SPELL_IMMUNITY && bb->subtype == sp->id || //100% sure spell immunity
- 					bb->type == Bonus::LEVEL_SPELL_IMMUNITY && bb->val >= sp->level) //some creature abilities have level 0
-					&& bb->source != Bonus::CREATURE_ABILITY)
- 				{
- 					ret.push_back((*it)->ID);
- 					continue;
- 				}
- 			}
-		}
-		else
-		{
-			if ((*it)->hasBonusOfType(Bonus::SPELL_IMMUNITY, sp->id) //100% sure spell immunity
-				|| ( (*it)->hasBonusOfType(Bonus::LEVEL_SPELL_IMMUNITY) &&
-				(*it)->valOfBonuses(Bonus::LEVEL_SPELL_IMMUNITY) >= sp->level) ) //some creature abilities have level 0
-			{
-				ret.push_back((*it)->ID);
-				continue;
-			}
-		}
-		
-
-		//non-negative spells on friendly stacks should always succeed, unless immune
-		if(sp->positiveness >= 0 && (*it)->owner == casterSideOwner)
-			continue;
-
-		const CGHeroInstance * bonusHero; //hero we should take bonuses from
-		if((*it)->owner == casterSideOwner)
-			bonusHero = caster;
-		else
-			bonusHero = hero2;
-
-		int prob = (*it)->valOfBonuses(Bonus::MAGIC_RESISTANCE); //probability of resistance in %
-		if(bonusHero)
-		{
-			//bonusHero's resistance support (secondary skils and artifacts)
-			prob += bonusHero->valOfBonuses(Bonus::MAGIC_RESISTANCE);
-			//resistance skill
-			prob += bonusHero->valOfBonuses(Bonus::SECONDARY_SKILL_PREMY, 26) / 100.0f;
-		}
-
-		if(prob > 100) prob = 100;
-
-		if(rand()%100 < prob) //immunity from resistance
-			ret.push_back((*it)->ID);
-
-	}
-
-	if(sp->id == 60) //hypnotize
-	{
-		for(std::set<CStack*>::const_iterator it = affectedCreatures.begin(); it != affectedCreatures.end(); ++it)
-		{
-			if( (*it)->hasBonusOfType(Bonus::SPELL_IMMUNITY, sp->id) //100% sure spell immunity
-				|| ( (*it)->count - 1 ) * (*it)->MaxHealth() + (*it)->firstHPleft 
-				> 
-				caster->getPrimSkillLevel(2) * 25 + sp->powers[caster->getSpellSchoolLevel(sp)]
-			)
-			{
-				ret.push_back((*it)->ID);
-			}
-		}
-	}
-
-	return ret;
-}
-
-void CGameHandler::handleSpellCasting( int spellID, int spellLvl, int destination, ui8 casterSide, ui8 casterColor,
-	const CGHeroInstance * caster, const CGHeroInstance * secHero, int usedSpellPower )
+void CGameHandler::handleSpellCasting( int spellID, int spellLvl, int destination, ui8 casterSide, ui8 casterColor, const CGHeroInstance * caster, const CGHeroInstance * secHero, int usedSpellPower, SpellCasting::ECastingMode mode )
 {
 	const CSpell *spell = VLC->spellh->spells[spellID];
 
@@ -3654,7 +3572,7 @@ void CGameHandler::handleSpellCasting( int spellID, int spellLvl, int destinatio
 	}
 
 	//checking if creatures resist
-	sc.resisted = calculateResistedStacks(spell, caster, secHero, attackedCres, casterColor);
+	sc.resisted = gs->curB->calculateResistedStacks(spell, caster, secHero, attackedCres, casterColor, mode);
 
 	//calculating dmg to display
 	for(std::set<CStack*>::iterator it = attackedCres.begin(); it != attackedCres.end(); ++it)
@@ -3824,7 +3742,7 @@ bool CGameHandler::makeCustomAction( BattleAction &ba )
 			const CSpell *s = VLC->spellh->spells[ba.additionalInfo];
 			ui8 skill = h->getSpellSchoolLevel(s); //skill level
 
-			SpellCasting::ESpellCastProblem escp = gs->curB->battleCanCastThisSpell(h->tempOwner, s, BattleInfo::HERO_CASTING);
+			SpellCasting::ESpellCastProblem escp = gs->curB->battleCanCastThisSpell(h->tempOwner, s, SpellCasting::HERO_CASTING);
 			if(escp != SpellCasting::OK)
 			{
 				tlog2 << "Spell cannot be cast!\n";
@@ -3834,7 +3752,7 @@ bool CGameHandler::makeCustomAction( BattleAction &ba )
 
 			sendAndApply(&StartAction(ba)); //start spell casting
 
-			handleSpellCasting (ba.additionalInfo, skill, ba.destinationTile, ba.side, h->tempOwner, h, secondHero, h->getPrimSkillLevel(2));
+			handleSpellCasting (ba.additionalInfo, skill, ba.destinationTile, ba.side, h->tempOwner, h, secondHero, h->getPrimSkillLevel(2), SpellCasting::HERO_CASTING);
 
 			sendAndApply(&EndAction());
 			if( !gs->curB->getStack(gs->curB->activeStack, false)->alive() )
@@ -4410,7 +4328,7 @@ void CGameHandler::handleAfterAttackCasting( const BattleAttack & bat )
 				int destination = oneOfAttacked->position;
 
 				const CSpell * spell = VLC->spellh->spells[spellID];
-				if(gs->curB->battleCanCastThisSpellHere(attacker->owner, spell, BattleInfo::AFTER_ATTACK_CASTING, oneOfAttacked->position)
+				if(gs->curB->battleCanCastThisSpellHere(attacker->owner, spell, SpellCasting::AFTER_ATTACK_CASTING, oneOfAttacked->position)
 					!= SpellCasting::OK)
 					continue;
 
@@ -4419,7 +4337,7 @@ void CGameHandler::handleAfterAttackCasting( const BattleAttack & bat )
 					continue;
 
 				//casting
-				handleSpellCasting(spellID, spellLevel, destination, !attacker->attackerOwned, attacker->owner, NULL, NULL, attacker->count);
+				handleSpellCasting(spellID, spellLevel, destination, !attacker->attackerOwned, attacker->owner, NULL, NULL, attacker->count, SpellCasting::AFTER_ATTACK_CASTING);
 			}
 		}
 	}
@@ -4878,7 +4796,7 @@ void CGameHandler::runBattle()
 			gs->curB->heroes[i]->getBonuses(bl, Selector::type(Bonus::OPENING_BATTLE_SPELL));
 			BOOST_FOREACH (Bonus *b, bl)
 			{
-				handleSpellCasting(b->subtype, 3, -1, 0, gs->curB->heroes[i]->tempOwner, NULL, gs->curB->heroes[1-i], b->val);
+				handleSpellCasting(b->subtype, 3, -1, 0, gs->curB->heroes[i]->tempOwner, NULL, gs->curB->heroes[1-i], b->val, SpellCasting::HERO_CASTING);
 			}
 		}
 	}

+ 1 - 1
server/CGameHandler.h

@@ -196,7 +196,7 @@ public:
 
 	void playerMessage( ui8 player, const std::string &message);
 	bool makeBattleAction(BattleAction &ba);
-	void handleSpellCasting(int spellID, int spellLvl, int destination, ui8 casterSide, ui8 casterColor, const CGHeroInstance * caster, const CGHeroInstance * secHero, int usedSpellPower);
+	void handleSpellCasting(int spellID, int spellLvl, int destination, ui8 casterSide, ui8 casterColor, const CGHeroInstance * caster, const CGHeroInstance * secHero, int usedSpellPower, SpellCasting::ECastingMode mode);
 	bool makeCustomAction(BattleAction &ba);
 	bool queryReply( ui32 qid, ui32 answer );
 	bool hireHero( const CGObjectInstance *obj, ui8 hid, ui8 player );