1
0
mirror of https://github.com/vcmi/vcmi.git synced 2024-12-24 22:14:36 +02:00

* minor improvements in spell immunity handling

This commit is contained in:
mateuszb 2011-02-22 13:44:34 +00:00
parent b4edd10e6c
commit 6db7f1d610
6 changed files with 94 additions and 105 deletions

View File

@ -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()

View File

@ -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

View File

@ -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)

View File

@ -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

View File

@ -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);
}
}
}

View File

@ -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 );