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:
parent
b4edd10e6c
commit
6db7f1d610
@ -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
global.h
2
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
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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 );
|
||||
|
Loading…
Reference in New Issue
Block a user