diff --git a/CCallback.cpp b/CCallback.cpp index 0a5f4c1e1..6e0b35f80 100644 --- a/CCallback.cpp +++ b/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() diff --git a/global.h b/global.h index dea0ba034..2316589fa 100644 --- a/global.h +++ b/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 diff --git a/lib/BattleState.cpp b/lib/BattleState.cpp index c722b2fd5..86a425d30 100644 --- a/lib/BattleState.cpp +++ b/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 BattleInfo::calculateResistedStacks( const CSpell * sp, const CGHeroInstance * caster, const CGHeroInstance * hero2, const std::set affectedCreatures, int casterSideOwner, SpellCasting::ECastingMode mode ) const +{ + std::vector ret; + for(std::set::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::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) diff --git a/lib/BattleState.h b/lib/BattleState.h index 50b51d813..008008b95 100644 --- a/lib/BattleState.h +++ b/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 calculateResistedStacks(const CSpell * sp, const CGHeroInstance * caster, const CGHeroInstance * hero2, const std::set 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 diff --git a/server/CGameHandler.cpp b/server/CGameHandler.cpp index 992b83544..0d3d25ce3 100644 --- a/server/CGameHandler.cpp +++ b/server/CGameHandler.cpp @@ -3552,89 +3552,7 @@ void CGameHandler::playerMessage( ui8 player, const std::string &message ) } } -static std::vector calculateResistedStacks(const CSpell * sp, const CGHeroInstance * caster, const CGHeroInstance * hero2, const std::set affectedCreatures, int casterSideOwner) -{ - std::vector ret; - for(std::set::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::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::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); } } } diff --git a/server/CGameHandler.h b/server/CGameHandler.h index c2c5505f3..42392aec3 100644 --- a/server/CGameHandler.h +++ b/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 );